* linting
* linting...relax check for 3-qubit circuit
* update test docstrings
* black update
* bind RZZ to pi/2 if Rzz(theta) in basis
* Apply suggestions from code review
---------
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
* Fix oversight from #12185 where two input parsing functions were not migrated from transpile to generate_preset_pm with the others.
* Add fix to reno from #12185
* Oxidize TwoQubitDecomposeUpToDiagonal
This commit ports the TwoQubitDecomposeUpToDiagonal class from Python to
rust. This internal private class is used internally by the quantum
shannon decomposition code, and while not performance critical was
simple to port. One difference is while the original Python
implementation was a class, it acted more like a function in practice.
So the new rust version is exposed as a function.
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
Co-authored-by: Sebastian Brandhofer <148463728+sbrandhsn@users.noreply.github.com>
Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>
Co-authored-by: Julien Gacon <gaconju@gmail.com>
Co-authored-by: Eli Arbel <46826214+eliarbel@users.noreply.github.com>
Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
Co-authored-by: Henry Zou <87874865+henryzou50@users.noreply.github.com>
Co-authored-by: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com>
Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>
* Build circuit from rust
Since #12459 recently merged we now have a mechanism to build a circuit
from rust. This commit updates the synthesis function to build the
circuit directly in rust instead of returning a circuit sequence and
building the circuit from Python. This should speed up the construction
substantially.
* Remove unused private Python class
This commit removes the Python implementation of the function. This is
now unused in Qiskit and was never a public class so nothing external
should be depending on it. Since it's not used we should just remove it.
* Remove unused import
* Calculate best_nbasis in unwrap_or_else()
---------
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
Co-authored-by: Sebastian Brandhofer <148463728+sbrandhsn@users.noreply.github.com>
Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>
Co-authored-by: Julien Gacon <gaconju@gmail.com>
Co-authored-by: Eli Arbel <46826214+eliarbel@users.noreply.github.com>
Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
Co-authored-by: Henry Zou <87874865+henryzou50@users.noreply.github.com>
Co-authored-by: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com>
Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>
* Use Rust gates for 2q unitary synthesis
This commit builds off of what #12650 did for the 1q decomposer and
moves to using rust gates for the 2q decomposer too. This means that the
circuit sequence generation is using rust's StandardGate representation
directly instead of relying on mapping strings. For places where
circuits are generated (calling `TwoQubitWeylDecomposition.circuit()` or
or `TwoQubitBasisDecomposer.__call__` without the `use_dag` flag) the
entire circuit is generated in Rust and returned to Python.
* Run cargo fmt and black post rebase
* Rebalance `CircuitInstruction` and `PackedInstruction`
This is a large overhaul of how circuit instructions are both stored in
Rust (`PackedInstruction`) and how they are presented to Python
(`CircuitInstruction`). In summary:
* The old `OperationType` enum is now collapsed into a manually managed
`PackedOperation`. This is logically equivalent, but stores a
`PyGate`/`PyInstruction`/`PyOperation` indirectly through a boxed
pointer, and stores a `StandardGate` inline. As we expect the vast
majority of gates to be standard, this hugely reduces the memory
usage. The enumeration is manually compressed to a single pointer,
hiding the discriminant in the low, alignment-required bytes of the
pointer.
* `PackedOperation::view()` unpacks the operation into a proper
reference-like enumeration `OperationRef<'a>`, which implements
`Operation` (though there is also a `try_standard_gate` method to get
the gate without unpacking the whole enumeration).
* Both `PackedInstruction` and `CircuitInstruction` use this
`PackedOperation` as the operation storage.
* `PackedInstruction` is now completely the Rust-space format for data,
and `CircuitInstruction` is purely for communication with Python.
On my machine, this commit brings the utility-scale benchmarks to within
10% of the runtime of 1.1.0 (and some to parity), despite all the
additional overhead.
Changes to accepting and building Python objects
------------------------------------------------
* A `PackedInstruction` is created by copy constructor from a
`CircuitInstruction` by `CircuitData::pack`. There is no `pack_owned`
(really, there never was - the previous method didn't take ownership)
because there's never owned `CircuitInstruction`s coming in; they're
Python-space interop, so we never own them (unless we clone them)
other than when we're unpacking them.
* `PackedInstruction` is currently just created manually when not coming
from a `CircuitInstruction`. It's not hard, and makes it easier to
re-use known intern indices than to waste time re-interning them.
There is no need to go via `CircuitInstruction`.
* `CircuitInstruction` now has two separated Python-space constructors:
the old one, which is the default and takes `(operation, qubits,
clbits)` (and extracts the information), and a new fast-path
`from_standard` which asks only for the standard gate, qubits and
params, avoiding operator construction.
* To accept a Python-space operation, extract a Python object to
`OperationFromPython`. This extracts the components that are separate
in Rust space, but joined in Python space (the operation, params and
extra attributes). This replaces `OperationInput` and
`OperationTypeConstruct`, being more efficient at the extraction,
including providing the data in the formats needed for
`PackedInstruction` or `CircuitInstruction`.
* To retrieve the Python-space operation, use
`CircuitInstruction::get_operation` or
`PackedInstruction::unpack_py_op` as appropriate. Both will
cache and reuse the op, if `cache_pygates` is active. (Though note
that if the op is created by `CircuitInstruction`, it will not
propagate back to a `PackedInstruction`.)
Avoiding operation creation
---------------------------
The `_raw_op` field of `CircuitInstruction` is gone, because `PyGate`,
`PyInstruction` and `PyOperation` are no longer pyclasses and no longer
exposed to Python. Instead, we avoid operation creation by:
* having an internal `DAGNode::_to_circuit_instruction`, which returns a
copy of the internal `CircuitInstruction`, which can then be used with
`CircuitInstruction.replace`, etc.
* having `CircuitInstruction::is_standard_gate` to query from Python
space if we should bother to create the operator.
* changing `CircuitData::map_ops` to `map_nonstandard_ops`, and having
it only call the Python callback function if the operation is not an
unconditional standard gate.
Memory usage
------------
Given the very simple example construction script:
```python
from qiskit.circuit import QuantumCircuit
qc = QuantumCircuit(1_000)
for _ in range(3_000):
for q in qc.qubits:
qc.rz(0.0, q)
for q in qc.qubits:
qc.rx(0.0, q)
for q in qc.qubits:
qc.rz(0.0, q)
for a, b in zip(qc.qubits[:-1], qc.qubits[1:]):
qc.cx(a, b)
```
This uses 1.5GB in max resident set size on my Macbook (note that it's
about 12 million gates) on both 1.1.0 and with this commit, so we've
undone our memory losses. The parent of this commit uses 2GB.
However, we're in a strong position to beat 1.1.0 in the future now;
there are two obvious large remaining costs:
* There are 16 bytes per `PackedInstruction` for the Python-operation
caching (worth about 180MB in this benchmark, since no Python
operations are actually created).
* There is also significant memory wastage in the current
`SmallVec<[Param; 3]>` storage of the parameters; for all standard
gates, we know statically how many parameters are / should be stored,
and we never need to increase the capacity. Further, the `Param` enum
is 16 bytes wide per parameter, of which nearly 8 bytes is padding,
but for all our current use cases, we only care if _all_ the
parameters or floats (for everything else, we're going to have to
defer to Python). We could move the discriminant out to the level of
the parameters structure, and save a large amount of padding.
Further work
------------
There's still performance left on the table here:
* We still copy-in and copy-out of `CircuitInstruction` too much right
now; we might want to make all the `CircuitInstruction` fields
nullable and have `CircuitData::append` take them by _move_ rather
than by copy.
* The qubits/clbits interner requires owned arrays going in, but most
interning should return an existing entry. We probably want to switch
to have the interner take references/iterators by default, and clone
when necessary. We could have a small circuit optimisation where the
intern contexts reserve the first n entries to use for an all-to-all
connectivity interning for up to (say) 8 qubits, since the transpiler
will want to create a lot of ephemeral small circuits.
* The `Param` vectors are too heavy at the moment; `SmallVec<[Param;
3]>` is 56 bytes wide, despite the vast majority of gates we care
about having at most one single float (8 bytes). Dead padding is a
large chunk of the memory use currently.
* Fix clippy in no-gate-cache mode
* Fix pylint unused-import complaints
* Fix broken assumptions around the gate model
The `compose` test had a now-broken assumption, because the Python-space
`is` check is no longer expected to return an identical object when a
standard gate is moved from one circuit to another and has its
components remapped as part of the `compose` operation. This doesn't
constitute the unpleasant deep-copy that that test is preventing. A
custom gate still satisfies that, however, so we can just change the
test.
`DAGNode::set_name` could cause problems if it was called for the first
time on a `CircuitInstruction` that was for a standard gate; these would
be created as immutable instances. Given the changes in operator
extraction to Rust space, it can now be the case that a standard gate
that comes in as mutable is unpacked into Rust space, the cache is some
time later invalidated, and then the operation is recreated immutably.
* Fix lint
* Fix minor documentation
* Add troubleshooting section to docs-build dev docs
There are a couple of known failure modes for the documentation build
that require additional setup. This documents these, to consolidate the
knowledge.
* Update CONTRIBUTING.md
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
---------
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
* Update version of OpenQASM 3.0 standard library
This is just a file vendored from the OpenQASM project, but it's been
updated since the last time we vendored it to fix a couple of problems.
This one is vendored from openqasm/openqasm@4ca1d79383.
* Clarify git link
* Rewrite OpenQASM 3 exporter symbol table
This rewrites the symbol handling of the OpenQASM 3 exporter to decouple
object identities from the necessary object identifiers. As part of
this, we use the same trick of standard-gate reparametrisation to
produce gate-definition sources for Qiskit built-ins, which fixes many
cases of bad parametrisation of gates like `rzx`.
This kind of rewrite was necessary to fix the now-bad assumption within
the OQ3 exporter that "gate identity" is always static within a circuit.
Since standard gate `Gate` instances are now only generated on demand,
there is no guarantee of stability of them. The fix to the definition
source for these makes them independent of object identity.
User-defined gates can still use the identity, as these are still
guaranteed static.
This commit fixes almost all of the "bad parametrisation" tests in the
test suite. There are several other changes in the test suite
necessary:
* since the uniqueness of the identifier is now independent of how the
lookup of a Qiskit object works, there is no need to include the
highly non-deterministic `id` in the generated symbols for user gates.
Several tests changed to use the new, simple count-based unique names.
* the escaping and uniqueness rules now apply uniformly to all gate
definitions, fixing several bad test cases that previously were
testing invalid OpenQASM 3.
* the escaping rules changed slightly for naming collisions with
keywords, making them slightly more consistent with how other renaming
rules worked.
* Add test for bug fix for issue #7335
* Rename qiskit gates whose names are OQ3 hardware qubit identifiers
If a custom qiskit gate is given a name that is a valid identifer for a
hardware qubit in OQ3, then, before this commit, the name would not be
escaped when writing the OQ3 gate definition.
This commit fixes this by escaping the leading dollar sign as it would
be in any other position in the name. That is, the dollar sign is
replaced by underscore.
Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
* Reduce overloading of word "definition"
Much of what we're doing with the "definition source" is actually a form
of object canonicalisation for comparison purposes. It's clearer to use
this terminology.
* Remove unnecessary getattr
* Fix isinstance/issubclass naming
---------
Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>
* initial commit
* release notes
* fixing synthesis plugin options
* finalize merge conflicts
* fixing default option values for qft plugins'
* additional tests for qft plugins
* starting to experiment
* porting code
* messy code porting
* printing statements to enable debugging
* fixes
* fixing phase
* removing some of the printing statements
* fixing inaccuracy for cost computation
* Moving some of the functionality to SymplecticMatrix class
* reducing the number of warnings
* formatting
* replacing expensive adjoint and compose operations for symplectic matrices by significantly cheaper in-place prepend operations
* resolving merge conflicts
* cleanup
* code cleanup
* cleanup
* cleanup
* cleanup
* cleanup
* cleanup
* cleanup
* cleanup
* using fast lookup
* cleanup
* clippy
* including params in gate_seq to avoid mapping
* removing unnecessary inner function
* cleanup
* renaming
* changes on the python side
* reno
* adding error handling
* improved error handling
* removing redundant Ok(Some(...))
* using random_clifford in tests
* reorganizing clifford code
* fixes
* formatting
* improved error handling
* do not panic
* formatting
* Applying refactoring suggestions d/utils.rs from code review
* release notes update
* starting to port code
* continuing to port code
* porting + fixing
* polishing
* changes on the python side; modifying tests to include missing testcases and to use random_clifford instead of random_clifford_circuit since the former provides better randomness guarantees
* release notes (last but not least)
* Correct reported speedups
* applying suggestion from code review
* resolving even more merge conflicts
* Optimization for the MCX Recursive Gate
Change the recursive method for the Lemma 9 of arXiv:1501.06911, first shown in Lemma 7.3 of https://link.aps.org/doi/10.1103/PhysRevA.52.3457
Co-Authored-By: Rafaella Vale <26910380+rafaella-vale@users.noreply.github.com>
Co-Authored-By: Jefferson Deyvis <67497412+jeffersondeyvis@users.noreply.github.com>
* Revert "Optimization for the MCX Recursive Gate"
This reverts commit 507d5b3a89.
Co-Authored-By: Rafaella Vale <26910380+rafaella-vale@users.noreply.github.com>
Co-Authored-By: Jefferson Deyvis <67497412+jeffersondeyvis@users.noreply.github.com>
* Revert "Revert "Optimization for the MCX Recursive Gate""
This reverts commit 4671b7e32b.
* Optimization for MCX Recursive
Fixing co-authors
Co-Authored-By: Rafaella Vale <26910380+rafaella-vale@users.noreply.github.com>
Co-Authored-By: Jefferson Deyvis <67497412+jeffersondeyvis@users.noreply.github.com>
Co-Authored-By: Adenilton Silva <7927558+adjs@users.noreply.github.com>
* Optimization of MCX Recursive
Now with fixed co-authors
Co-Authored-By: Rafaella Vale <26910380+rafaella-vale@users.noreply.github.com>
Co-Authored-By: Jefferson Deyvis <67497412+jeffersondeyvis@users.noreply.github.com>
Co-Authored-By: Adenilton Silva <7927558+adjs@users.noreply.github.com>
* refactored MCXRecursive and unused method deleted
* fixed qasm string for mcx test with variants
* remove draw
* fix qasm file in test_circuit_qasm_with_mcx_gate_variants
* update the MCXRecursive class docstring
* add a test of the upper bound limit of the number of CX gates
* fix failing qasm test
* fix qasm file in test_export in qasm2 tests
* fix format lint errors in qasm strings
* add release notes
* update references format
* update release notes after review
---------
Co-authored-by: Thiago Melo <thiagomdazevedo@hotmail.com>
Co-authored-by: Rafaella Vale <26910380+rafaella-vale@users.noreply.github.com>
Co-authored-by: Jefferson Deyvis <67497412+jeffersondeyvis@users.noreply.github.com>
Co-authored-by: Adenilton Silva <7927558+adjs@users.noreply.github.com>
* Fix pass-manager drawing tests for pydot 3.0
Two changes in pydot 3 changed the reference output:
1. previously, attributes were sorted before being output. Pydot 3
stores them in declaration order. Here, we sort our attributes to
maintain the closest behaviour between the two versions.
2. Text fields (like `label`) that contain special characters now have
their values enclosed in quote marks. This is a difference between
the two versions, and we update the reference files to the new
version since that's what we'll be using in CI.
Qiskit is still compatible with pydot 2 in general usage, it's just our
tests that are a little more tied to the current version.
* Add test-only constraint on pydot
* Oxidize two qubit local invariance functions
This commit migrates the two functions in the private module
`qiskit.synthesis.two_qubit.local_invariance` to primarily be
implemented in rust. Since the two_qubit_local_invariants() function is
being used in #12727 premptively porting these functions to rust will
both potentially speed up the new transpiler pass in that PR and also
facilitate a future porting of that pass to rust. The two python space
functions continue to exist as a small wrapper to do input type
checking/conversion and rounding of the result (since the python API for
rounding is simpler). There is no release note included for these
functions as they are internal utilities in Qiskit and not exposed as a
public interface.
* Add docstring to rust function with citation
* Store adjoint magic array statically
* Use arraview1 instead of slice for local_equivalence()
* Fix rustfmt
* Add missing gate definitions
* Reorder gates, following number of qubits and a sort of alphabetical order. Make definitions and matrices consistent with new gate order. Remove C4XGate (second mcx) from list.
* remove some of the entries
* fixing obscure expcetion handleing for comparison
* remove all the modules
* ignore "Treating CircuitInstruction as an iterable is deprecated" in Aer
* remove allow_DeprecationWarning_module and revert ignore/default
* revert
This commit updates the minimum rustworkx version to 0.15.0 to pull in
the new PyDiGraph.remove_node_retain_edges_by_id() method introduced
in that release. This new function is used for the
DAGCircuit.remove_op_node() method instead of the
PyDiGraph.remove_node_retain_edges() function. This new method has much
better scaling characteristics and should improve the performance
characteristics of removing very wide operations from a DAGCircuit.
Fixes#11677
Part of #12156
This commit updates the BasisTranslator transpiler pass. It builds off
of #12692 and #12701 to adjust access patterns in the python transpiler
path to avoid eagerly creating a Python space operation object. The goal
of this PR is to mitigate the performance regression introduced by the
extra conversion cost of #12459 on the BasisTranslator.
* initial commit
* release notes
* fixing synthesis plugin options
* finalize merge conflicts
* fixing default option values for qft plugins'
* additional tests for qft plugins
* renaming QftGate to QFTGate
* Also renaming Qft to QFT in synthesis method names
* appplying Jake's suggestion from code review
* inlining _basic_definition into _define
* docstring improvements
* more docstring improvements
* renaming do_swaps to reverse_qubits in the new code
* typos
* adding synth_qft_full to __init__
* round of suggestions from code review
* another round of code review suggestions
* fixes
* also adding QFTGate plugins to the docs
* Use rust gates for ConsolidateBlocks
This commit moves to use rust gates for the ConsolidateBlocks transpiler
pass. Instead of generating the unitary matrices for the gates in a 2q
block Python side and passing that list to a rust function this commit
switches to passing a list of DAGOpNodes to the rust and then generating
the matrices inside the rust function directly. This is similar to what
was done in #12650 for Optimize1qGatesDecomposition. Besides being faster
to get the matrix for standard gates, it also reduces the eager
construction of Python gate objects which was a significant source of
overhead after #12459. To that end this builds on the thread of work in
the two PRs #12692 and #12701 which changed the access patterns for
other passes to minimize eager gate object construction.
* Add rust filter function for DAGCircuit.collect_2q_runs()
* Update crates/accelerate/src/convert_2q_block_matrix.rs
---------
Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>
* Add C3X (MCX), extend rust tests to multi-controlled gates.
* Add macro to generate multi-controlled gates. Add CU, CU1, CU3, C3SX, C4X, CCZ.
* Kill C4XGate
* Finish adding gates, add circuit construction methods when possible.
* Add import paths, fix drawer test.
* Establish CGates with non-default control states as non-standard in circuit_instruction.rs. Add unit test.
* Fix merge conflicts
* Apply macro on missing gates
* Add RCCX gate and RC3X (RCCCX) gate.
* Make equivalence tests more explicit
* Fix lint
* Modify circuit methods for consistency
* Fix default ctrl state for 3q+ gates, add test for CCZ
* Apply comments from Matt's code review
* Fix ctrl_state logic
* Rename c3x to mcx?
* Brackets didn't match explanation
* Make sure controlled test doesn't use custom ControlledGate instances.
* Rename c4x to mcx in Rust space.
* Return PyResult rather than panic on error
* Add suggestion from Matt's code review
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
---------
Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
* [WIP] adds the output argument to the internal atomic evolution
* meta: modernize type hints
* refactor: change callable structure of atomic evolution
This changes the structure of the `atomic_evolution` callable in the
`ProductFormula` synthesis class. This is motivated by the significant
performance improvements that can be obtained by appending to the
existing circuit directly rather than building out individual evolution
circuits and iteratively composing them.
* refactor: deprecate the legacy atomic_evolution signature
* refactor: add the wrap argument to ProductFormula
This can be used to recover the previous behavior in which the single
individually evolved Pauli terms get wrapped into gate objects.
* fix: insert the missing barriers between LieTrotter repetitions
* refactor: align SuzukiTrotter and LieTrotter
* Propoagate deprecation notice
* fix: the labels of wrapped Pauli evolutions
* fix: respect the insert_barriers setting
* docs: add a release note
* Apply suggestions from code review
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* fix: missing `wrap` forward in SuzukiTrotter
* docs: improve documentation of the `atomic_evolution` argument
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* docs: also document the deprecated form of `atomic_evolution`
* docs: call out ProductFormula docs in release note
* refactor: change to PendingDeprecationWarning
* refactor: explicitly convert to Gate when wrapping
This is slightly faster than the `.compose`-based operation done
previously as it performs fewer checks. Thanks to @jakelishman for the
suggestion offline.
* Update qiskit/synthesis/evolution/lie_trotter.py
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* docs: update after pending deprecation
---------
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* Implement convert_index
This allows to convert a Python-like signed index into an unsigned index suitable in Rust notation.
* Jake's comments
- refactor PySeqIndex.with_len to use convert_idx
- cleanup the tests
* mock QISKIT_SETTINGS to avoid interfering with my own env
* set a dummy QISKIT_SETTINGS to avoid interfering with local env
* apply to all of them
* revert
In the recently merged #12650 a new rust function was added for the
filter function of the collect_1q_runs() method's internal filter
function. As part of this we need to do exclude any non-DAGOpNode dag
nodes passed to the filter function. This was previously implemented
using the pyo3 extract() method so that if the pyobject can be coerced
into a DAGOpNode for later use we continue but if it errors we know the
input object is not a DAGOpNode and return false. However, as we don't
need to return the error to python we should be using downcast instead
of extract (as per the pyo3 performance guide [1]) to avoid the error
conversion cost. This was an oversight in #12650 and I only used
extract() out of habit.
[1] https://pyo3.rs/v0.22.0/performance#extract-versus-downcast
* Accept Option<&str> instead of &Option<String>, etc
In a few places, this removes unnecessary object copies.
Accept a wider range of input types, and more idiomatic input types
in a few functions. This affects code added in the gates-in-rust PR.
The great majority of the changes here were obsoleted by #12594.
The original commit has been cherry picked on top of main.
* Remove unnecessary as_ref()
* Add warning about Tweedledum support in ClassicalFunction
* Update qiskit/circuit/classicalfunction/__init__.py
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
* Update __init__.py
line break to comply to pylint
---------
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>