-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Performance boost for CircuitData
oxidation
#11214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Pull Request Test Coverage Report for Build 7412912864Warning: This coverage report may be inaccurate.We've detected an issue with your CI configuration that might affect the accuracy of this pull request's coverage report.
💛 - Coveralls |
4027857
to
43fd555
Compare
Here are some updated benchmarks for circuit building comparing memory pressure and runtime between The graphs show a sweep from 2Local 127Q,
|
To demonstrate the impact of these changes on transpilation time relative to We wouldn't expect much difference in transpilation time anyway since we convert to a import sys
from qiskit import QuantumCircuit, transpile
from qiskit.circuit.library import QuantumVolume
from qiskit.providers.fake_provider import FakeWashington
backend = FakeWashington()
qv = QuantumVolume(127, int(sys.argv[1]), 1337)
qv.measure_all()
tqc = transpile(
qv,
backend,
seed_transpiler=1334,
layout_method="sabre",
routing_method="sabre",
optimization_level=0
) |
Use CircuitData in builder.py. Add fastpath for CircuitData::extend(CircuitData). Fix merge conflicts. Add docstrings and reorder functions for an improved diff. Optimize QuantumCircuit.copy for CircuitData. Run formatting after merge. Update docstrings. Improve doc comment for replace_bits. Fix lint. Optimize copy. Fix merge. Refactor replace_bits. Run formatter. Use Python form docstrings in API. Fix lint.
21db863
to
10bb926
Compare
One or more of the the following people are requested to review this:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The python part LGTM. But probably another pair of eyes would be better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally this looks sensible to me. If we're bikeshedding names, I'd perhaps suggest map_ops
instead of replace_ops
, just to match more with the standard Python and Rust terminology for that operation, but it's not very important.
Could you show the performance characteristics of the Pauli.to_instruction
and QuantumCircuit.copy
asv benchmarks between the three reference points as well, just to check everything? The changeset here looks strongly like you've directly targetted those benchmarks, so I'm assuming you already have that data somewhere?
This simplifies caller logic when appending an instruction listing to a scope by leaving it up to the scope implementation to determine what is required (e.g. updating the parameter table only for QuantumCircuit scopes vs just tracking bits and instructions for a control flow block.
|
I've also run benchmarks for
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is very close to being ready to land - the performance improvements certainly look great!
Aside from two fairly minor comments here, I guess there's a little bit of merge conflict that'll need fixing, and there's a couple of not-yet-resolved comment chains I replied to in the previous review:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the changes on this! All the comments I had are resolved, so I think we should merge this and start getting tested in general usage (at least on the main branch!).
* Optimize QC compose and circuit_to_instruction. Use CircuitData in builder.py. Add fastpath for CircuitData::extend(CircuitData). Fix merge conflicts. Add docstrings and reorder functions for an improved diff. Optimize QuantumCircuit.copy for CircuitData. Run formatting after merge. Update docstrings. Improve doc comment for replace_bits. Fix lint. Optimize copy. Fix merge. Refactor replace_bits. Run formatter. Use Python form docstrings in API. Fix lint. * Update comment. * Add unit tests. * Make active_bits stable and preallocate. * Improve readability of add_{qu,cl}bit methods. * Clarify replace_bits docstring. * Remove unnecessary type hint. * Make ControlFlowBuilderBlock qubits and clbits methods. * Remove TODO. * Use single replace_ops in QuantumCircuit.copy. * Use memoization in circuit_to_instruction. Style fixes. * Add CircuitScopeInterface.extend. This simplifies caller logic when appending an instruction listing to a scope by leaving it up to the scope implementation to determine what is required (e.g. updating the parameter table only for QuantumCircuit scopes vs just tracking bits and instructions for a control flow block. * Simplify logic for front compose. * Add enumerate_ops, optimize block builder. * Rename replace_ops to map_ops. * Fix benchmark random_circuit. * Add default strict mode for bits. * Return tuple of sets from active_bits. * Rename 'enumerate_ops' to 'foreach_op_indexed'. * Add _track_operation method. * Fix merge for global phase.
Summary
This PR includes:
replace_bits
on the Rust typeCircuitData
(introduced in OxidizeQuantumCircuit._data
and internCircuitInstruction
args #10827) that takes advantage ofCircuitData
's interned representation ofqubits
andclbits
as vectors of indices to provide circuit remapping in constant time with respect to the number of existing instructions.CircuitData::map_ops
,CircuitData::foreach_op
andCircuitData::enumerate_ops
that are used to invoke a Python callable for each operation in the circuit without constructing ephemeralCircuitInstruction
instances. These methods provide a considerable performance boost for client code that was previously iterating overCircuitData
but ignoring thequbits
andclbits
of yieldedCircuitInstruction
s.ControlFlowBuilderBlock
's instruction storage, which now usesCircuitData
instead oflist
. This also includes a new methodCircuitScopeInterface.extend
which is used byQuantumCircuit.compose
to update the currently scopedControlFlowBuilderBlock
all in one go.Details and comments
CircuitData::replace_bits
In its current form,
CircuitData
owns thequbits
andclbits
of aQuantumCircuit
. As instructions are added to it, thesequbits
andclbits
sequences are used to convert the instruction's bits fromQubit
andClbit
instances to the corresponding indices, which are then stored with theInternContext
. On access (e.g. via lookup or iteration),CircuitData
retrieves indices from theInternContext
and performs a lookup in the opposite direction to get back toQubit
andClbit
instances. Because we do this lookup anyway,replace_bits
gives us a way to perform circuit remapping in constant time by simply swapping-out aCircuitData
'sclbits
andqubits
.This is used to accelerate
QuantumCircuit.compose
andcircuit_to_instruction
.CircuitData::map_ops,
CircuitData::foreach_opand
CircuitData::enumerate_ops`In several cases, we don't actually need to access the
qubits
andclbits
ofCircuitInstruction
instances while iterating over aCircuitData
. For example, when updating aParameterTable
. And now with the introduction ofreplace_bits
, we also have a use case for these functions to update theoperation
on conditional instructions during bit remapping.CircuitScopeInterface.extend
This new method takes a
CircuitData
instance and uses it to extend the block's instruction listing all in one go. Because blocks now store their instructions in aCircuitData
, a fast path has been added toCircuitData::extend
that avoids construction of intermediateCircuitInstruction
instances which would simply be thrown away anyway.Todo
0.45
,oxidize-qc-data
, and this branch,oxidize-qc-data-perf
.CircuitData::replace_bits
cleaner.pyclass
methods.