-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Add infrastructure for gates, instruction, and operations in Rust #12459
Conversation
This commit adds a native representation of Gates, Instruction, and Operations to rust's circuit module. At a high level this works by either wrapping the Python object in a rust wrapper struct that tracks metadata about the operations (name, num_qubits, etc) and then for other details it calls back to Python to get dynamic details like the definition, matrix, etc. For standard library gates like Swap, CX, H, etc this replaces the on-circuit representation with a new rust enum StandardGate. The enum representation is much more efficient and has a minimal memory footprint (just the enum variant and then any parameters or other mutable state stored in the circuit instruction). All the gate properties such as the matrix, definiton, name, etc are statically defined in rust code based on the enum variant (which represents the gate). The use of an enum to represent standard gates does mean a change in what we store on a CircuitInstruction. To represent a standard gate fully we need to store the mutable properties of the existing Gate class on the circuit instruction as the gate by itself doesn't contain this detail. That means, the parameters, label, unit, duration, and condition are added to the rust side of circuit instrucion. However no Python side access methods are added for these as they're internal only to the Rust code. In Qiskit 2.0 to simplify this storage we'll be able to drop, unit, duration, and condition from the api leaving only label and parameters. But for right now we're tracking all of the fields. To facilitate working with circuits and gates full from rust the setting the `operation` attribute of a `CircuitInstruction` object now transltates the python object to an internal rust representation. For standard gates this translates it to the enum form described earlier, and for other circuit operations 3 new Rust structs: PyGate, PyInstruction, and PyOperation are used to wrap the underlying Python object in a Rust api. These structs cache some commonly accessed static properties of the operation, such as the name, number of qubits, etc. However for dynamic pieces, such as the definition or matrix, callback to python to get a rust representation for those. Similarly whenever the `operation` attribute is accessed from Python it converts it back to the normal Python object representation. For standard gates this involves creating a new instance of a Python object based on it's internal rust representation. For the wrapper structs a reference to the wrapped PyObject is returned. To manage the 4 variants of operation (`StandardGate`, `PyGate`, `PyInstruction`, and `PyOperation`) a new Rust trait `Operation` is created that defines a standard interface for getting the properties of a given circuit operation. This common interface is implemented for the 4 variants as well as the `OperationType` enum which wraps all 4 (and is used as the type for `CircuitInstruction.operation` in the rust code. As everything in the `QuantumCircuit` data model is quite coupled moving the source of truth for the operations to exist in Rust means that more of the underlying `QuantumCircuit`'s responsibility has to move to Rust as well. Primarily this involves the `ParameterTable` which was an internal class for tracking which instructions in the circuit have a `ParameterExpression` parameter so that when we go to bind parameters we can lookup which operations need to be updated with the bind value. Since the representation of those instructions now lives in Rust and Python only recieves a ephemeral copy of the instructions the ParameterTable had to be reimplemented in Rust to track the instructions. This new parameter table maps the Parameter's uuid (as a u128) as a unique identifier for each parameter and maps this to a positional index in the circuit data to the underlying instruction using that parameter. This is a bit different from the Python parameter table which was mapping a parameter object to the id of the operation object using that parmaeter. This also leads to a difference in the binding mechanics as the parameter assignment was done by reference in the old model, but now we need to update the entire instruction more explicitly in rust. Additionally, because the global phase of a circuit can be parameterized the ownership of global phase is moved from Python into Rust in this commit as well. After this commit the only properties of a circuit that are not defined in Rust for the source of truth are the bits (and vars) of the circuit, and when creating circuits from rust this is what causes a Python interaction to still be required. This commit does not translate the full standard library of gates as that would make the pull request huge, instead this adds the basic infrastructure for having a more efficient standard gate representation on circuits. There will be follow up pull requests to add the missing gates and round out support in rust. The goal of this pull request is primarily to add the infrastructure for representing the full circuit model (and dag model in the future) in rust. By itself this is not expected to improve runtime performance (if anything it will probably hurt performance because of extra type conversions) but it is intended to enable writing native circuit manipulations in Rust, including transpiler passes without needing involvement from Python. Longer term this should greatly improve the runtime performance and reduce the memory overhead of Qiskit. But, this is just an early step towards that goal, and is more about unlocking the future capability. The next steps after this commit are to finish migrating the standard gate library and also update the `QuantumCircuit` methods to better leverage the more complete rust representation (which should help offset the performance penalty introduced by this). Fixes: Qiskit#12205
One or more of the following people are relevant to this code:
|
Oh, those neko failures are interesting and unexpected (I was expecting the 6 unit test failures in terra's unit tests). Looking at it I think this is the same case of something I hit in the nlocal parameter tests. The underlying issue is that for the type of the elements of param vector in rust I defined basically the following type: #[derive(FromPyObject)]
enum Param {
Float(f64),
ParameterExpression(PyObject)
} and the issue is the derive p = Parameter('p')
expression = p * 0 and then uses |
This commit adds a custom implementation of the FromPyObject trait for the Param enum. Previously, the Param trait derived it's impl of the trait, but this logic wasn't perfect. In cases whern a ParameterExpression was effectively a constant (such as `0 * x`) the trait's attempt to coerce to a float first would result in those ParameterExpressions being dropped from the circuit at insertion time. This was a change in behavior from before having gates in Rust as the parameters would disappear from the circuit at insertion time instead of at bind time. This commit fixes this by having a custom impl for FromPyObject that first tries to figure out if the parameter is a ParameterExpression (or a QuantumCircuit) by using a Python isinstance() check, then tries to extract it as a float, and finally stores a non-parameter object; which is a new variant in the Param enum. This new variant also lets us simplify the logic around adding gates to the parameter table as we're able to know ahead of time which gate parameters are `ParameterExpression`s and which are other objects (and don't need to be tracked in the parameter table. Additionally this commit tweaks two tests, the first is test.python.circuit.library.test_nlocal.TestNLocal.test_parameters_setter which was adjusted in the previous commit to workaround the bug fixed by this commit. The second is test.python.circuit.test_parameters which was testing that a bound ParameterExpression with a value of 0 defaults to an int which was a side effect of passing an int input to symengine for the bind value and not part of the api and didn't need to be checked. This assertion was removed from the test because the rust representation is only storing f64 values for the numeric parameters and it is never an int after binding from the Python perspective it isn't any different to have float(0) and int(0) unless you explicit isinstance check like the test previously was.
This commit fixes the handling of standard gates in Qiskit when the user specifies excluding the use of the stdgates.inc file from the exported qasm. Previously the object id of the standard gates were used to maintain a lookup table of the global definitions for all the standard gates explicitly in the file. However, the rust refactor means that every time the exporter accesses `circuit.data[x].operation` a new instance is returned. This means that on subsequent lookups for the definition the gate definitions are never found. To correct this issue this commit adds to the lookup table a fallback of the gate name + parameters to do the lookup for. This should be unique for any standard gate and not interfere with the previous logic that's still in place and functional for other custom gate definitions. While this fixes the logic in the exporter the test is still failing because the test is asserting the object ids are the same in the qasm3 file, which isn't the case anymore. The test will be updated in a subsequent commit to validate the qasm3 file is correct without using a hardcoded object id.
When ALAPScheduleAnalysis and ASAPScheduleAnalysis were setting the duration of a gate they were doing `node.op.duration = duration` this wasn't always working because if `node.op` was a standard gate it returned a new Python object created from the underlying rust representation. This commit fixes the passes so that they modify the duration and then explicit set the operation to update it's rust representation.
While the logic for the qasm3 exporter was fixed in commit a6e69ba to handle the edge case of a user specifying that the qasm exporter does not use the stdgates.inc include file in the output, but also has qiskit's standard gates in their circuit being exported. The one unit test to provide coverage for that scenario was not passing because when an id was used for the gate definitions in the qasm3 file it was being referenced against a temporary created by accessing a standard gate from the circuit and the ids weren't the same so the reference string didn't match what the exporter generated. This commit fixes this by changing the test to not do an exact string comparison, but instead a line by line comparison that either does exact equality check or a regex search for the expected line and the ids are checked as being any 15 character integer.
Pull Request Test Coverage Report for Build 9496969108Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
This commit updates the QuantumCircuit gate methods which add a given gate to the circuit to bypass the python gate object creation and directly insert a rust representation of the gate. This avoids a conversion in the rust side of the code. While in practice this is just the Python side object creation and a getattr for the rust code to determine it's a standard gate that we're skipping. This may add up over time if there are a lot of gates being created by the method. To accomplish this the rust code handling the mapping of rust StandardGate variants to the Python classes that represent those gates needed to be updated as well. By bypassing the python object creation we need a fallback to populate the gate class for when a user access the operation object from Python. Previously this mapping was only being populated at insertion time and if we never insert the python object (for a circuit created only via the methods) then we need a way to find what the gate class is. A static lookup table of import paths and class names are added to `qiskit_circuit::imports` module to faciliate this and helper functions are added to facilitate interacting with the class objects that represent each gate.
This commit updates the commutative cancellation and commutation analysis transpiler pass. It builds off of Qiskit#12692 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 on these passes introduced by the extra conversion cost of Qiskit#12459. As part of this the commutation checker is rewritten in rust since all that requires is gates in rust which we've had a representation of since Qiskit#12459 merged.
This commit updates the commutative cancellation and commutation analysis transpiler pass. It builds off of Qiskit#12692 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 on these passes introduced by the extra conversion cost of Qiskit#12459.
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 Qiskit#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 Qiskit#12459. To that end this builds on the thread of work in the two PRs Qiskit#12692 and Qiskit#12701 which changed the access patterns for other passes to minimize eager gate object construction.
This commit updates the BasisTranslator transpiler pass. It builds off of Qiskit#12692 and Qiskit#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 Qiskit#12459 on the BasisTranslator.
* Avoid Python operation creation in transpiler Since #12459 accessing `node.op` in the transpiler eagerly creates a Python object on access. This is because we now are no longer storing a Python object internally and we need to rebuild the object to return the python object as expected by the api. This is causing a significant performance regression because of the extra overhead. The longer term goal is to move as much of the performance critical passes to operate in rust which will eliminate this overhead. But in the meantime we can mitigate the performance overhead by changing the Python access patterns to avoid the operation object creation. This commit adds some new getter methods to DAGOpNode to give access to the inner rust data so that we can avoid the extra overhead. As a proof of concept this updates the unitary synthesis pass in isolation. Doing this fixes the regression caused by #12459 for that pass. We can continue this migration for everything else in follow up PRs. This commit is mostly to establish the pattern and add the python space access methods. * Remove unused import * Add path to avoid StandardGate conversion in circuit_to_dag * Add fast path through dag_to_circuit
This commit updates the commutative cancellation and commutation analysis transpiler pass. It builds off of Qiskit#12692 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 on these passes introduced by the extra conversion cost of Qiskit#12459.
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 Qiskit#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 Qiskit#12459. To that end this builds on the thread of work in the two PRs Qiskit#12692 and Qiskit#12701 which changed the access patterns for other passes to minimize eager gate object construction.
* Avoid Python op creation in commutative cancellation This commit updates the commutative cancellation and commutation analysis transpiler pass. It builds off of #12692 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 on these passes introduced by the extra conversion cost of #12459. * Remove stray print * Don't add __array__ to DAGOpNode or CircuitInstruction
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 Qiskit#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 Qiskit#12459. To that end this builds on the thread of work in the two PRs Qiskit#12692 and Qiskit#12701 which changed the access patterns for other passes to minimize eager gate object construction.
This commit updates the BasisTranslator transpiler pass. It builds off of Qiskit#12692 and Qiskit#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 Qiskit#12459 on the BasisTranslator.
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 Qiskit#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 Qiskit#12459. To that end this builds on the thread of work in the two PRs Qiskit#12692 and Qiskit#12701 which changed the access patterns for other passes to minimize eager gate object construction.
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 Qiskit#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 Qiskit#12459. To that end this builds on the thread of work in the two PRs Qiskit#12692 and Qiskit#12701 which changed the access patterns for other passes to minimize eager gate object construction.
* 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 <[email protected]>
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.
* 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 <[email protected]> Co-authored-by: Elena Peña Tapia <[email protected]> Co-authored-by: Sebastian Brandhofer <[email protected]> Co-authored-by: Jake Lishman <[email protected]> Co-authored-by: John Lapeyre <[email protected]> Co-authored-by: Julien Gacon <[email protected]> Co-authored-by: Eli Arbel <[email protected]> Co-authored-by: Raynel Sanchez <[email protected]> Co-authored-by: Henry Zou <[email protected]> Co-authored-by: Shelly Garion <[email protected]> Co-authored-by: Alexander Ivrii <[email protected]> * 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 <[email protected]> Co-authored-by: Elena Peña Tapia <[email protected]> Co-authored-by: Sebastian Brandhofer <[email protected]> Co-authored-by: Jake Lishman <[email protected]> Co-authored-by: John Lapeyre <[email protected]> Co-authored-by: Julien Gacon <[email protected]> Co-authored-by: Eli Arbel <[email protected]> Co-authored-by: Raynel Sanchez <[email protected]> Co-authored-by: Henry Zou <[email protected]> Co-authored-by: Shelly Garion <[email protected]> Co-authored-by: Alexander Ivrii <[email protected]>
…12292) * Initial: Add `Target` class to `_accelerate` - Add `Target` class to test mobility between Rust and Python. - Add `add_instruction` method to test compatibility with instructions. * Fix: Remove empty property check - Property check caused most cases to panic. - Will be commented out and restored at a later time. * Add: Instructions property - Instructions property returns all added to the target. - Similar behavior to source. * Chore: comments and deprecated methods - Add comments to instruction property. - Use new_bound for new PyDicts. * Chore: Remove redundant code - Remove redundant transformation of PyObject to PyTuple. - Remove debugging print statement. * Add: `InstructionProperties` class and type checkers - Add `InstructionProperties` class to process properties in rust. - Add `is_instance` and `is_class` to identify certain Python objects. - Modify logic of `add_instruction` to use class check. - Other tweaks and fixes. * Add: Setter and Getter for calibration in `InstructionProperty` * Add: `update_instruction_properties` to Target. * Add: Update_from_instruction_schedule_map - Partial addition from Target.py\ - Introduction of hashable qarg data structure. - Other tweaks and fixes. * Add: Complete `update_from_instruction_schedule_map1 - Complete missing procedures in function. - Rename `Qargs` to `HashableVec`. - Make `HashableVec` generic. - Separate `import_from_module_call` into call0 and call1. - Other tweaks and fixes. * Add: instruction_schedule_map property. - Remove stray print statements. - Other tweaks and fixes. * Fix: Key issue in `update_from_instruction_schedule_map` - Remove all unsafe unwraps * Fix: Use PyResult Value for void functon - Update `update_from_instruction_schedule_map to use PyResult and '?' operator. - Use Bound Python objects whenever possible. - Other tweaks and fixes. * Add: Python wrapping for Target - Add temporary _target module for testing. - Remove update_from_instruction_schedule_map function back to python. - Add python properties for all public attributes in rust - Other tweaks and fixes. * Add: `qargs` property - Add identical method `qargs` to obtain the qargs of a target. - Other tweaks and fixes. * Add: `qargs_for_operation_name` function. - Add function with identical behavior to the original in Target. - Other tweaks and fixes. * Add: durations method for Target - Add target module to qiskit init file. - Remove is_instance method. - Modify set_calibration method in InstructionProperty to leave typechecking to Python. - Change rust Target alias to Target2. - Other tweaks and fixes, * Add: InstructionProperties wrapper in python * Fix: InstructionProperties could not receive calibrations - Fix wrong setters/getters for calibration in InstructionProperty object in rust. * Add: more methods to Target in `target.rs` - Add FromPyObject trait to Hashable vec to receive Tuples and transform them directly into this type. - Add operations_for_qargs for Target class in Rust side and Python. - Fix return dict keys for `qargs_for_operation_name`. - Add `timing_constrains` and `operation_from_name` to Python side. - Other tweaks and fixes. * Fix: missing return value in `operations_for_args` - Fix wrong name for function operation_for_qargs. - Fix missing return value in the python side. - Other tweaks and fixes. * Fix: Bad compatibility with InstructionProperties - Make `InstructionProperties` "_calibration" attribute visible. - Removed attribute "calibration", treat as class property. - Other tweaks and fixes * Add: `operation_names_for_qargs` to Target - Port class method to rust and connect to Python wrapper. - Other tweaks and fixes. * Add: instruction_supported method to rust and python: - Other tweaks and fixes. * Add: changes to add_instruction function to increase functionality. - These changes break current functionality of other functions, butemulate intended behavior better. - Fixes coming soon. * Fix: Backwards compatibility with `add_instruction` - Fixed wrong additions to HashMaps in the rust side causing instructions to be missing. - Other tweaks and fixes. * Fix: Gate Map behavior didn't match #11422 - Make GateMap use optional values to match behavior of #11422. - Define GateMapType for complex type in self.gate_map. - Throw Python KeyError exceptions from the rust side in `update_instruction_properties` and other functions. - Modify logic in subsequent functions that use gate_map optional values. - Other tweaks and fixes. * Add: `has_calibration` method to Target * Add: `get_calibraton` method to Target * Add: `instruction_properties` method to Target * Add: `build_coupling_map` and helper methods - `build_coupling_map`will remain in Python for now, along with its helper functions. - Make `gate_name_map` visible to python. - Add `coupling_graph` attribute to Target in Rust. - Other tweaks and fixes. * Add: `get_non_global_operation_names` to Target. - Add attributes `non_global_strict_basis` and `non_global_basis` as Optional. - Other tweaks and fixes. * Add: Missing properties - Add properties: operations, operation_names, and physical_qubits. - Reorganize properties placement. - Other tweaks and fixes. * Add: `from_configuration` classmethod to Target. - Add method that mimics the behavior of the python method. - Change concurrent_measurements to 2d Vec instead of a Vec of sets. - Other tweaks and fixes. * Add: Magic methods to Rust and Python - Add docstring to __init__. - Add __iter__, __getitem__, __len__, __contains__, keys, values, and items methods to rust. - Add equivalen methods to python + the __str__ method. - Make description an optional attribute in rust. - Other tweaks and fixes. * Fix: Bugs when fetching qargs or operations - Fix qarg_for_operation_name logic to account for None and throw correct exceptions. - Stringify description before sending in case of numerical descriptors. - Fix qarg to account for None entry. - Other tweaks and fixes. * Chore: Prepare for Draft PR - Remove _target.py testing file. - Fix incorrect initialization of calibration in InstructionProperties. - Other tweaks and fixes. * Fix: target not being recognized as a module - Add target to the pyext crate. - Change placement of target import for alphabetical ordering. - Other tweaks and fixes. * Fix: Change HashMap to IndexMap - Change from f32 to f64 precision. - Other tweaks and fixes. * Fix: Move InstructionProperties fully to Rust - Move InstructionProperties to rust. - Modify gate_map to accept an InstructionProprties object instead of PyObjecy. - Change update_instruction_properties to use Option InstructionProprtyird. - Remove InstructionProperties from target.py - Other tweaks and fixes. * Fix: Make Target inherit from Rust - Make Target inherit from the rust-side Target by using subclass attribute, then extending its functionality using python. - Switch from __init__ to __new__ to adapt to the Target rust class. - Modify every trait that worked with `target._Target` to use `super()` or `self` instead. - Fix repr in InstructionProperties to not show `Some()` when a value exists. - Fix `__str__` method in `Target` to not display "None" if no description is given. - Assume `num_qubits` is the first argument when an integer is provided as a first argument and nothing else is provided for second (Target initializer). - Return a set in operation_names instead of a Vec. - Other tweaks and fixes. * Fix: Recognize None in `operation_for_qargs`. - Fix module labels for each class in target.rs. - Use py.is_instance instead of passing isinstance to `instruction_supported`. - Modify `operations_for_qargs` to accept optional values less aggressively. Allow it to find instructions with no qargs. (NoneType). - Other tweaks and fixes. * Fix: Make InstructionProperties subclassable. - Fix get_non_global_operation_names to accept optional values and fix search set to use sorted values. - Fix __repr__ method in InstructionProperties to add punctuation. - Fix typo in python durations method. - Modify test to overload __new__ method instead of just __init__ (Possible breaking change). -Other tweaks and fixes. * Fix: errors in `instruction_properties` and others: - Allow `instruction_properties` method to view optional properties. - Allow `operation_names_for_qargs` to select class instructions when None is passed as a qarg. - Modify __str__ method to display error and duration times as int if the value is 0. - Other tweaks and fixes. * Fix: call `isclass` from rust, instead of passing it from Python. * Fix: Move `update_from_instruction_schedule_map` to rust. * Fix: Move `durations` to rust. * Fix: Move `timing_constraints` to rust * Fix: Move operations_from_name fully to rust * Fix: `instruction_supported` method: - Rewrite the logic of instruction_supported due to previous errors in the method. - Move `check_obj_params` to Rust. - Other tweaks and fixes. * Fix: errors in `from_configuration` class method. - Fix some of the logic when retrieving gates from `name_mapping`. - Remove function arguments in favor of implementing counterpart functions in rust. - Add qubit_props_list_from_props function and return rust datatypes. - Fix wrong error handling procedures when retrieving attributes from backend_property. - Other tweaks and fixes. * Fix: Import `InstructionScheduleMap` directly instead of passing. - `instruction_schedule_map()` now imports the classtype directly from rust instead of needing it to be passed from python. - Remove unused imports in `target.py`. - Ignore unused arguments in `test_extra_props_str`. - Other tweaks and fixes. * Docs: Add docstrings to rust functions - Remove redundant redefinitions in python. - Fix text_signatures for some rust functions. - Added lint exceptions to some necessary imports and function arguments. - Other tweaks and fixes. * Add: Make `Target` and `InstructionProperties` pickleable. - Add `__getstate__` and `__setstate__` methods to make both rust subclasses pickleable. * Fix: Wrong calibration assignment in __setstate__ - Use set_calibration to set the correct calibration argument. - Fix wrong signature in get_non_global_operation_names. - Other tweaks and fixes. * Refactor: HashableVec is now Qarg - Use `PhysicalQubit` instead of u32 for qargs. - Use a `SmallVec` of size 4 instead of a dynamic Vec. - Default to using the `Hash()` method embedded in `SmallVec`. - Add a Default method to easily unwrap Qarg objects. - Other tweaks and fixes. * Add: `get` function to target. - Remove some redundant cloning in code. - Other small fixes. * Fix: Remove unnecessary Optional values in gate_map. - Update every gate_map call to use the new format. - Other small tweaks and fixes. * Refactor: `calibration` is for `InstructionProperties` - Use python `None` instead of option to store `calibration` in `InstructionProperties`. - Adapt code to these changes. - Remove redundant implementation of Hash in Qargs. - Other tweaks and fixes. * Fix: Temporary speedup for `gate_map` access - Added temporary speedups to access the gate_map by returning the values as PyObjects. - Convert qargs to rust tuples instead of initializing a `PyTuple`. - Store `InstructionProperties` as a python ref in gate_map. (Will be changed in future updates). - Other tweaks anf fixes. * Fix: Incorrect extractions for `InstructionProperties` - Fix incorrect conversion of `InstructionProperties` to `Py<InstructionProperties>` - Fix incorrect extraction of qargs in `update_from_instruction_schedule_map` * Fix: Hide all private attributes in `Target` - Hide all private attributes of the `Target` to prevent unecessary cloning. - Other small tweaks and fixes. * Add: New representation of gate_map using new pyclasses: - Make Qarg a sequence pyclass. - Make QargPropsMap the new representation of a GateMap value. - Adapt the code to new structure. - TODO: Add missing magic methods for sequence and mapping objects. - Other small tweaks and fixes. * Add: Use custom datatypes to return values to Python. - Add QargSet datatype to return a set of qargs. - Works as return type for `Target.qargs` - Object is has itertype of QargSetIter. - Rename QargPropMap to PropsMap - Use iterator type IterPropsMap - Other small tweaks and fixes. * Fix: Extend `InstructionProperties` to be subclassable using `__init__: - Made a subclass of `InstructionProperties` that can be extended using an `__init__`method. - Revert previous changes to `test_target.py`. - Other tweaks and fixes. * Refactor: Split target into its own module - Reestructure the files to improve readability of code. - `instruction_properties.rs` contaisn the `InstructionProperties` class. - `mod.rs` contains the `Target` class. - `qargs.rs` contains the Qargs struct to store quantum arguments. - `property_map` contains the Qarg: Property Mapping that will be stored in the gate_map. - Add missing methods to PropsMap: - Add `PropsMapKeys` object to store the qargs as a set. - Add methods to compare and access `PropsMapKey`. - Add QargsOrTuple enum in Qargs to parse Qargs instantly. * Fix: Rest of failing tests in Target - Modify the `InstructionProperties` python wrapper. - InstructionProperties was not assigning properties to rust side. - Make duration in `InstructionProperties` setable. - Add `__eq__` method for `PropMap` to compare with other dicts. - `PropMapKeys` can only be compared with a Set. - Remove `qargs_for_operation_name` from `target.py` - Other small tweaks and fixes. * Add: New GateMap Structure - GateMap is now its own mapping object. - Add `__setstate__` and `__getstate__` methods for `PropMap` and `GateMap`. - Other small tweaks and fixes. * Fix: Make new subclasses pickleable - Add module location to `PropsMap`, `GateMap`, and `Qargs`. - Added default method to PropMap. - Made default method part of class initializers. - Other smalls tweaks and fixes. * Fix: Remove redundant lookup in Target (#12373) * Format: `mod.rs` qubit_comparison to one line. * Add: `GateMapKeys` object in GateMap: - Use IndexSet as a base to preserve the insertion order. - Other tweaks and fixes. * Add: __sub__ method to GateMapKeys * Fix: Modify `GateMap` to store values in Python heap. - Fix `GateMap.__iter__` to use an IndexKeys iterator. - Other small tweaks and fixes. * Fix: Remove duplicate import of `IndexSet::into_iter` in `GateMap`. - Make `__iter__` use the keys() method in `GateMap`. * Fix:: Adapt to target changes (#12288) - Fix lint stray imports. * Fix: Incorrect creation of parameters in `update_from_instruction_schedule_map` - Add `tupelize` function to create tuples from non-downcastable items. - Fix creation of Parameters by iterating through members of tuple object and mapping them to parameters in `update_from_instruction_schedule_map`. - Add missing logic for creating a Target with/without `qubit_properties`. - Add tuple conversion of `Qargs` to store items in a dict in `BasisTranslator` and `UnitarySynthesis` passes. - Cast `PropsMap` object to dict when comparing in `test_fake_backends.py`. - Modify logic of helper functions that receive a bound object reference, a second `py` not required as an argument. - Add set operation methods to `GateMapKeys`. - Other tweaks and fixes. * Fix: More failing tests - Fix repeated erroneous calls to `add_instruction` in `update_from_instruction_schedule_map` - Add missing condition in `instruction_supported` - Use `IndexSet` instead of `HashSet` for `QargsSet`. - Other small tweaks and fixes. * Add: Macro rules for qargs and other sequences. - Create `QargSet` and `PropsMap` using the new macros. - Return a `TargetOpNames` ordered set to python in `operation_names`. - Remove the Python side `operation_names.` - Fix faulty docstring in `target.py`. - Other tweaks and fixes. * Docs: Add necessary docstrings to all new rust functions. - Remove duplicate Iterator in GateMap. - Other small tweaks and fixes. * Fix: Use `GILOneCell` and remove `Qargs` - Use `GILOneCell` to import python modules only once at initialization. - Remove the custom data structure `Qargs` to avoid conversion overhead. - `Qargs` does not use `PhysicalQubits`, `u32` is used instead. - Fix `__setstate__ `and `__getstate__` methods for `PropsMap`, `GateMap`, and `key_like_set_iterator` macro_rule. - Update code to use the new structures. - TODO: Fix broken tests. * Fix: Cast `Qargs` to `Tuple` in specific situations - Use tupleize to cast `Qargs` to `Tuple` in `instructions`. - Use downcast to extract string in `add_instruction`. - Other tweaks and fixes. * Add: Make `Target` Representable in Rust - Rename `InstructionProperties` as `BaseInstructionProperties`. - Remove `Calibration` from the rust space. - Restore `gate_map`, `coupling_map`, `instruction_schedule_map`, and `instruction_durations` to rust. - Remove all unnecessary data structures from rust space. - Other tweaks and fixes. * Refactor: Remove previour changes to unrelated files. * Add: rust native functions to target - Added rust native functionality to target such that a `py` would not be needed to use one. - Add Index trait to make `Target` subscriptable. - Other small tweaks and fixes. * Fix: Remove all unnecessary python method calls. - Remove uage of `inspect.isclass`. - Rename `Target` to `BaseTarget` in the rust side. - Rename `err.rs` to `errors.rs`. - Remove rust-native `add_inst` and `update_inst` as Target should not be modified from Rust. - Made `add_instruction` and `update_instruction_properties` private in `BaseTarget`. - Add missing `get` method in `Target`. - Other tweaks and fixes * Format: Fix lint * Fix: Wrong return value for `BaseTarget.qargs` * Add: Temporary Instruction representation in rust. - Add temporary instruction representation to avoid repeated extraction from python. * Add: Native representation of coupling graph * Fix: Wrong attribute extraction for `GateRep` * Remove: `CouplingGraph` rust native representation. - Move to different PR. * Format: Remove stray whitespace * Add: `get_non_global_op_names` as a rust native function * Fix: Use Ahash for Hashing - Use ahash for hashing when possible. - Rename `BaseTarget` to `Target` in rust only. - Rename `BaseInstructionProperties` to `InstructionProperties` in rust only. - Remove optional logic from `generate_non_global_op_names`. - Use dict for `__setstate__` and `__getstate__` in `Target`. - Reduced the docstring for `Target` and `InstructionProperties`. - Other small tweaks and fixes. * Format: new changes to `lib.rs` * Format: Adapt to new lint rules * Fix: Use new gates infrastructure (#12459) - Create custom enum to collect either a `NormalOperation` or a `VariableOperation` depending on what is needed. - Add a rust native `is_instruction_supported` method to check whether a Target supports a certain instruction. - Make conversion methods from `circuit_instruction.rs` public. - Add comparison methods for `Param` in `operations.rs` - Remove need for isclass method in rustwise `add_instruction` - Other tweaks and fixes. * Format: Fix rust formatting * Add: rust-native method to obtain Operstion objects. * Add: Comparison methods for `Param` * FIx: Add display methods for `Params` * Format: Fix lint test * Format: Wrong merge conflict solve * Fix: Improve rust methods to use iterators. - Adapt the Python methods to leverage the rust native improvements. - Use python native structures for the Python methods. * Format: Remove extra blankspace * Fix: Remove `text_signature`, use `signature` instead. * Fix: Rectify the behavior of `qargs` - Keep insertion order by inserting all qargs into a `PySet`. - Perform conversion to `PyTuple` at insertion time leveraging the iterator architecture. - Remove python side counterpart to avoid double iteration. - Make rust-native `qargs` return an iterator. * Fix: Corrections from Matthew's review - Use `format!` for repr method in `InstructionProperties` - Rename `Variable` variant of `TargetInstruction` to `Variadic`. - Remove `internal_name` attribute from `TargetOperation`. - Remove `VariableOperation` class. - Use `u32` for `granularity`, `pulse_alignment`, and `acquire_alignment`. - Use `Option` to store nullable `concurrent_measurements. - Use `&str` instead of `String` for most function arguments. - Use `swap_remove` to deallocate items from the provided `properties` map in `add_instruction`. - Avoid cloning instructions, use `to_object()` instead. - Avoid using `.to_owned()`, use `.clone()` instead. - Remove mention of `RandomState`, use `ahash::HashSet` instead. - Move parameter check to python in `instruction_supported`. - Avoid exposing private attributes, use the available ones instead. - Filter out `Varidadic` Instructions as they're not supported in rust. - Use peekable iterator to peak at the next qargs in `generate_non_global_op_names`. - Rename `qarg_set` to `deduplicated_qargs` in `generate_non_global_op_names`. - Return iterator instances instead of allocated `Vec`. - Add `python_compare` and `python_is_instance` to perform object comparison with objects that satisfy the `ToPyObject` trait. - Other small tweaks and fixes. * Implement a nullable dict-like structure for IndexMap (#2) * Initial: Implement a nullable dict-like structure for IndexMap * FIx: Erroneous item extraction from Python - Fix error that caused `None` values to be ignored from `None` keys. - Removed mutability from rust function argument in `add_instruction`. - Object is mutably referenced after option unwrapping. - Add missing header in `nullable_index_map.rs`. - Add Clone as a `K` and/or `V` constraint in some of the iterators. - Remove `IntoPy` constraint from `NullableIndexMap<K, V>`. - Add `ToPyObject` trait to `NullableIndexMap<K, V>`. * Fix: inplace modification of Python dict. - Perform `None` extraction from rust. - Revert changes to `Target.py` * Fix: Avoid double iteration by using filter_map. * Docs: Add inline comments. * Fix: More specific error message in `NullableIndexMap` * Fix: Use `Mapping` as the metaclass for `Target` - Minor corrections from Matthew's review. * Fix: Make `Target` crate-private. - Due to the private nature of `NullableIndexMap`, the `Target` has to be made crate private. - Add temporary`allow(dead_code)` flag for the unused `Target` and `NullableIndexMap` methods. - Fix docstring of `Target` struct. - Fix docstring of `add_instruction`. - Make several python-only operations public so they can be used with other `PyClass` instances as long as they own the gil. - Modify `py_instruction_supported` to accept bound objects. - Use rust-native functions for some of the instance properties. - Rewrite `instruction` to return parameters as slice. - `operation_names` returns an `ExactSizeIterator`. - All rust-native methods that return an `OperationType` object, will return a `NormalOperation` instance which includes the `OperationType` and the parameters. * Fix: Comments from Matthew's review - Mention duplication in docstring for rust Target. - Use f"{*:g}" to avoid printing the floating point for 0 in `Target`'s repr method. - Add note mentioning future unit-tests in rust. * Fix: Adapt to #12730
…skit#12459) * Add infrastructure for gates, instruction, and operations in Rust This commit adds a native representation of Gates, Instruction, and Operations to rust's circuit module. At a high level this works by either wrapping the Python object in a rust wrapper struct that tracks metadata about the operations (name, num_qubits, etc) and then for other details it calls back to Python to get dynamic details like the definition, matrix, etc. For standard library gates like Swap, CX, H, etc this replaces the on-circuit representation with a new rust enum StandardGate. The enum representation is much more efficient and has a minimal memory footprint (just the enum variant and then any parameters or other mutable state stored in the circuit instruction). All the gate properties such as the matrix, definiton, name, etc are statically defined in rust code based on the enum variant (which represents the gate). The use of an enum to represent standard gates does mean a change in what we store on a CircuitInstruction. To represent a standard gate fully we need to store the mutable properties of the existing Gate class on the circuit instruction as the gate by itself doesn't contain this detail. That means, the parameters, label, unit, duration, and condition are added to the rust side of circuit instrucion. However no Python side access methods are added for these as they're internal only to the Rust code. In Qiskit 2.0 to simplify this storage we'll be able to drop, unit, duration, and condition from the api leaving only label and parameters. But for right now we're tracking all of the fields. To facilitate working with circuits and gates full from rust the setting the `operation` attribute of a `CircuitInstruction` object now transltates the python object to an internal rust representation. For standard gates this translates it to the enum form described earlier, and for other circuit operations 3 new Rust structs: PyGate, PyInstruction, and PyOperation are used to wrap the underlying Python object in a Rust api. These structs cache some commonly accessed static properties of the operation, such as the name, number of qubits, etc. However for dynamic pieces, such as the definition or matrix, callback to python to get a rust representation for those. Similarly whenever the `operation` attribute is accessed from Python it converts it back to the normal Python object representation. For standard gates this involves creating a new instance of a Python object based on it's internal rust representation. For the wrapper structs a reference to the wrapped PyObject is returned. To manage the 4 variants of operation (`StandardGate`, `PyGate`, `PyInstruction`, and `PyOperation`) a new Rust trait `Operation` is created that defines a standard interface for getting the properties of a given circuit operation. This common interface is implemented for the 4 variants as well as the `OperationType` enum which wraps all 4 (and is used as the type for `CircuitInstruction.operation` in the rust code. As everything in the `QuantumCircuit` data model is quite coupled moving the source of truth for the operations to exist in Rust means that more of the underlying `QuantumCircuit`'s responsibility has to move to Rust as well. Primarily this involves the `ParameterTable` which was an internal class for tracking which instructions in the circuit have a `ParameterExpression` parameter so that when we go to bind parameters we can lookup which operations need to be updated with the bind value. Since the representation of those instructions now lives in Rust and Python only recieves a ephemeral copy of the instructions the ParameterTable had to be reimplemented in Rust to track the instructions. This new parameter table maps the Parameter's uuid (as a u128) as a unique identifier for each parameter and maps this to a positional index in the circuit data to the underlying instruction using that parameter. This is a bit different from the Python parameter table which was mapping a parameter object to the id of the operation object using that parmaeter. This also leads to a difference in the binding mechanics as the parameter assignment was done by reference in the old model, but now we need to update the entire instruction more explicitly in rust. Additionally, because the global phase of a circuit can be parameterized the ownership of global phase is moved from Python into Rust in this commit as well. After this commit the only properties of a circuit that are not defined in Rust for the source of truth are the bits (and vars) of the circuit, and when creating circuits from rust this is what causes a Python interaction to still be required. This commit does not translate the full standard library of gates as that would make the pull request huge, instead this adds the basic infrastructure for having a more efficient standard gate representation on circuits. There will be follow up pull requests to add the missing gates and round out support in rust. The goal of this pull request is primarily to add the infrastructure for representing the full circuit model (and dag model in the future) in rust. By itself this is not expected to improve runtime performance (if anything it will probably hurt performance because of extra type conversions) but it is intended to enable writing native circuit manipulations in Rust, including transpiler passes without needing involvement from Python. Longer term this should greatly improve the runtime performance and reduce the memory overhead of Qiskit. But, this is just an early step towards that goal, and is more about unlocking the future capability. The next steps after this commit are to finish migrating the standard gate library and also update the `QuantumCircuit` methods to better leverage the more complete rust representation (which should help offset the performance penalty introduced by this). Fixes: Qiskit#12205 * Fix Python->Rust Param conversion This commit adds a custom implementation of the FromPyObject trait for the Param enum. Previously, the Param trait derived it's impl of the trait, but this logic wasn't perfect. In cases whern a ParameterExpression was effectively a constant (such as `0 * x`) the trait's attempt to coerce to a float first would result in those ParameterExpressions being dropped from the circuit at insertion time. This was a change in behavior from before having gates in Rust as the parameters would disappear from the circuit at insertion time instead of at bind time. This commit fixes this by having a custom impl for FromPyObject that first tries to figure out if the parameter is a ParameterExpression (or a QuantumCircuit) by using a Python isinstance() check, then tries to extract it as a float, and finally stores a non-parameter object; which is a new variant in the Param enum. This new variant also lets us simplify the logic around adding gates to the parameter table as we're able to know ahead of time which gate parameters are `ParameterExpression`s and which are other objects (and don't need to be tracked in the parameter table. Additionally this commit tweaks two tests, the first is test.python.circuit.library.test_nlocal.TestNLocal.test_parameters_setter which was adjusted in the previous commit to workaround the bug fixed by this commit. The second is test.python.circuit.test_parameters which was testing that a bound ParameterExpression with a value of 0 defaults to an int which was a side effect of passing an int input to symengine for the bind value and not part of the api and didn't need to be checked. This assertion was removed from the test because the rust representation is only storing f64 values for the numeric parameters and it is never an int after binding from the Python perspective it isn't any different to have float(0) and int(0) unless you explicit isinstance check like the test previously was. * Fix qasm3 exporter for std gates without stdgates.inc This commit fixes the handling of standard gates in Qiskit when the user specifies excluding the use of the stdgates.inc file from the exported qasm. Previously the object id of the standard gates were used to maintain a lookup table of the global definitions for all the standard gates explicitly in the file. However, the rust refactor means that every time the exporter accesses `circuit.data[x].operation` a new instance is returned. This means that on subsequent lookups for the definition the gate definitions are never found. To correct this issue this commit adds to the lookup table a fallback of the gate name + parameters to do the lookup for. This should be unique for any standard gate and not interfere with the previous logic that's still in place and functional for other custom gate definitions. While this fixes the logic in the exporter the test is still failing because the test is asserting the object ids are the same in the qasm3 file, which isn't the case anymore. The test will be updated in a subsequent commit to validate the qasm3 file is correct without using a hardcoded object id. * Fix base scheduler analysis pass duration setting When ALAPScheduleAnalysis and ASAPScheduleAnalysis were setting the duration of a gate they were doing `node.op.duration = duration` this wasn't always working because if `node.op` was a standard gate it returned a new Python object created from the underlying rust representation. This commit fixes the passes so that they modify the duration and then explicit set the operation to update it's rust representation. * Fix python lint * Fix last failing qasm3 test for std gates without stdgates.inc While the logic for the qasm3 exporter was fixed in commit a6e69ba to handle the edge case of a user specifying that the qasm exporter does not use the stdgates.inc include file in the output, but also has qiskit's standard gates in their circuit being exported. The one unit test to provide coverage for that scenario was not passing because when an id was used for the gate definitions in the qasm3 file it was being referenced against a temporary created by accessing a standard gate from the circuit and the ids weren't the same so the reference string didn't match what the exporter generated. This commit fixes this by changing the test to not do an exact string comparison, but instead a line by line comparison that either does exact equality check or a regex search for the expected line and the ids are checked as being any 15 character integer. * Remove superfluous comment * Cache imported classes with GILOnceCell * Remove unused python variables * Add missing file * Update QuantumCircuit gate methods to bypass Python object This commit updates the QuantumCircuit gate methods which add a given gate to the circuit to bypass the python gate object creation and directly insert a rust representation of the gate. This avoids a conversion in the rust side of the code. While in practice this is just the Python side object creation and a getattr for the rust code to determine it's a standard gate that we're skipping. This may add up over time if there are a lot of gates being created by the method. To accomplish this the rust code handling the mapping of rust StandardGate variants to the Python classes that represent those gates needed to be updated as well. By bypassing the python object creation we need a fallback to populate the gate class for when a user access the operation object from Python. Previously this mapping was only being populated at insertion time and if we never insert the python object (for a circuit created only via the methods) then we need a way to find what the gate class is. A static lookup table of import paths and class names are added to `qiskit_circuit::imports` module to faciliate this and helper functions are added to facilitate interacting with the class objects that represent each gate. * Deduplicate gate matrix definitions * Fix lint * Attempt to fix qasm3 test failure * Add compile time option to cache py gate returns for rust std gates This commit adds a new rust crate feature flag for the qiskit-circuits and qiskit-pyext that enables caching the output from CircuitInstruction.operation to python space. Previously, for memory efficiency we were reconstructing the python object on demand for every access. This was to avoid carrying around an extra pointer and keeping the ephemeral python object around longer term if it's only needed once. But right now nothing is directly using the rust representation yet and everything is accessing via the python interface, so recreating gate objects on the fly has a huge performance penalty. To avoid that this adds caching by default as a temporary solution to avoid this until we have more usage of the rust representation of gates. There is an inherent tension between an optimal rust representation and something that is performant for Python access and there isn't a clear cut answer on which one is better to optimize for. A build time feature lets the user pick, if what we settle on for the default doesn't agree with their priorities or use case. Personally I'd like to see us disable the caching longer term (hopefully before releasing this functionality), but that's dependent on a sufficent level of usage from rust superseding the current Python space usage in the core of Qiskit. * Add num_nonlocal_gates implementation in rust This commit adds a native rust implementation to rust for the num_nonlocal_gates method on QuantumCircuit. Now that we have a rust representation of gates it is potentially faster to do the count because the iteration and filtering is done rust side. * Performance tuning circuit construction This commit fixes some performance issues with the addition of standard gates to a circuit. To workaround potential reference cycles in Python when calling rust we need to check the parameters of the operation. This was causing our fast path for standard gates to access the `operation` attribute to get the parameters. This causes the gate to be eagerly constructed on the getter. However, the reference cycle case can only happen in situations without a standard gate, and the fast path for adding standard gates directly won't need to run this so a skip is added if we're adding a standard gate. * Add back validation of parameters on gate methods In the previous commit a side effect of the accidental eager operation creation was that the parameter input for gates were being validated by that. By fixing that in the previous commit the validation of input parameters on the circuit methods was broken. This commit fixes that oversight and adds back the validation. * Skip validation on gate creation from rust * Offload operation copying to rust This commit fixes a performance regression in the `QuantumCircuit.copy()` method which was previously using Python to copy the operations which had extra overhead to go from rust to python and vice versa. This moves that logic to exist in rust and improve the copy performance. * Fix lint * Perform deepcopy in rust This commit moves the deepcopy handling to occur solely in Rust. Previously each instruction would be directly deepcopied by iterating over the circuit data. However, we can do this rust side now and doing this is more efficient because while we need to rely on Python to run a deepcopy we can skip it for the Rust standard gates and rely on Rust to copy those gates. * Fix QuantumCircuit.compose() performance regression This commit fixes a performance regression in the compose() method. This was caused by the checking for classical conditions in the method requiring eagerly converting all standard gates to a Python object. This changes the logic to do this only if we know we have a condition (which we can determine Python side now). * Fix map_ops test case with no caching case * Fix typos in docs This commit fixes several docs typos that were caught during code review. Co-authored-by: Eli Arbel <[email protected]> * Shrink memory usage for extra mutable instruction state This commit changes how we store the extra mutable instruction state (condition, duration, unit, and label) for each `CircuitInstruction` and `PackedInstruction` in the circuit. Previously it was all stored as separate `Option<T>` fields on the struct, which required at least a pointer's width for each field which was wasted space the majority of the time as using these fields are not common. To optimize the memory layout of the struct this moves these attributes to a new struct which is put in an `Option<Box<_>>` which reduces it from 4 pointer widths down to 1 per object. This comes from extra runtime cost from the extra layer of pointer indirection but as this is the uncommon path this tradeoff is fine. * Remove Option<> from params field in CircuitInstruction This commit removes the Option<> from the params field in CircuitInstruction. There is no real distinction between an empty vec and None in this case, so the option just added another layer in the API that we didn't need to deal with. Also depending on the memory alignment using an Option<T> might have ended up in a little extra memory usage too, so removing it removes that potential source of overhead. * Eagerly construct rust python wrappers in .append() This commit updates the Python code in QuantumCircuit.append() method to eagerly construct the rust wrapper objects for python defined circuit operations. * Simplify code around handling python errors in rust * Revert "Skip validation on gate creation from rust" This reverts commit 2f81bde. The validation skipping was unsound in some cases and could lead to invalid circuit being generated. If we end up needing this as an optimization we can remove this in the future in a follow-up PR that explores this in isolation. * Temporarily use git for qasm3 import In Qiskit/qiskit-qasm3-import#34 the issue we're hitting caused by qiskit-qasm3-import using the private circuit attributes removed in this PR was fixed. This commit temporarily moves to installing it from git so we can fully run CI. When qiskit-qasm3-import is released we should revert this commit. * Fix lint * Fix lint for real (we really need to use a py312 compatible version of pylint) * Fix test failure caused by incorrect lint fix * Relax trait-method typing requirements * Encapsulate `GILOnceCell` initialisers to local logic * Simplify Interface for building circuit of standard gates in rust * Simplify complex64 creation in gate_matrix.rs This just switches Complex64::new(re, im) to be c64(re, im) to reduce the amount of typing. c64 needs to be defined inplace so it can be a const fn. * Simplify initialization of array of elements that are not Copy (Qiskit#28) * Simplify initialization of array of elements that are not Copy * Only generate array when necessary * Fix doc typos Co-authored-by: Kevin Hartman <[email protected]> * Add conversion trait for OperationType -> OperationInput and simplify CircuitInstruction::replace() * Use destructuring for operation_type_to_py extra attr handling * Simplify trait bounds for map_indices() The map_indices() method previously specified both Iterator and ExactSizeIterator for it's trait bounds, but Iterator is a supertrait of ExactSizeIterator and we don't need to explicitly list both. This commit removes the duplicate trait bound. * Make Qubit and Clbit newtype member public As we start to use Qubit and Clbit for creating circuits from accelerate and other crates in the Qiskit workspace we need to be able to create instances of them. However, the newtype member BitType was not public which prevented creating new Qubits. This commit fixes this by making it public. * Use snakecase for gate matrix names * Remove pointless underscore prefix * Use downcast instead of bound * Rwork _append reference cycle handling This commit reworks the multiple borrow handling in the _append() method to leveraging `Bound.try_borrow()` to return a consistent error message if we're unable to borrow a CircuitInstruction in the rust code meaning there is a cyclical reference in the code. Previously we tried to detect this cycle up-front which added significant overhead for a corner case. * Make CircuitData.global_phase_param_index a class attr * Use &[Param] instead of &SmallVec<..> for operation_type_and_data_to_py * Have get_params_unsorted return a set * Use lookup table for static property methods of StandardGate * Use PyTuple::empty_bound() * Fix lint * Add missing test method docstring * Reuse allocations in parameter table update * Remove unnecessary global phase zeroing * Move manually set params to a separate function * Fix release note typo * Use constant for global-phase index * Switch requirement to release version --------- Co-authored-by: Eli Arbel <[email protected]> Co-authored-by: Jake Lishman <[email protected]> Co-authored-by: John Lapeyre <[email protected]> Co-authored-by: Kevin Hartman <[email protected]>
This PR introduces some abbreviations for repetitive Rust code. Motivations are reducing clutter, improving readability, and perhaps modest support for rapid development. * Use the definition of `const fn 64` that was introduced in Qiskit#12459 uniformly in all crates. * Define some complex constants `C_ONE`, `C_ZERO`, `IM`, etc. * Introduce type definitions for arrays representing gates. For example: `GateArray1Q = [[Complex64; 2]; 2];`
* Avoid Python operation creation in transpiler Since Qiskit#12459 accessing `node.op` in the transpiler eagerly creates a Python object on access. This is because we now are no longer storing a Python object internally and we need to rebuild the object to return the python object as expected by the api. This is causing a significant performance regression because of the extra overhead. The longer term goal is to move as much of the performance critical passes to operate in rust which will eliminate this overhead. But in the meantime we can mitigate the performance overhead by changing the Python access patterns to avoid the operation object creation. This commit adds some new getter methods to DAGOpNode to give access to the inner rust data so that we can avoid the extra overhead. As a proof of concept this updates the unitary synthesis pass in isolation. Doing this fixes the regression caused by Qiskit#12459 for that pass. We can continue this migration for everything else in follow up PRs. This commit is mostly to establish the pattern and add the python space access methods. * Remove unused import * Add path to avoid StandardGate conversion in circuit_to_dag * Add fast path through dag_to_circuit
* Avoid Python op creation in commutative cancellation This commit updates the commutative cancellation and commutation analysis transpiler pass. It builds off of Qiskit#12692 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 on these passes introduced by the extra conversion cost of Qiskit#12459. * Remove stray print * Don't add __array__ to DAGOpNode or CircuitInstruction
* 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 Qiskit#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 Qiskit#12459. To that end this builds on the thread of work in the two PRs Qiskit#12692 and Qiskit#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 <[email protected]>
This commit updates the BasisTranslator transpiler pass. It builds off of Qiskit#12692 and Qiskit#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 Qiskit#12459 on the BasisTranslator.
* 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 <[email protected]> Co-authored-by: Elena Peña Tapia <[email protected]> Co-authored-by: Sebastian Brandhofer <[email protected]> Co-authored-by: Jake Lishman <[email protected]> Co-authored-by: John Lapeyre <[email protected]> Co-authored-by: Julien Gacon <[email protected]> Co-authored-by: Eli Arbel <[email protected]> Co-authored-by: Raynel Sanchez <[email protected]> Co-authored-by: Henry Zou <[email protected]> Co-authored-by: Shelly Garion <[email protected]> Co-authored-by: Alexander Ivrii <[email protected]> * Build circuit from rust Since Qiskit#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 <[email protected]> Co-authored-by: Elena Peña Tapia <[email protected]> Co-authored-by: Sebastian Brandhofer <[email protected]> Co-authored-by: Jake Lishman <[email protected]> Co-authored-by: John Lapeyre <[email protected]> Co-authored-by: Julien Gacon <[email protected]> Co-authored-by: Eli Arbel <[email protected]> Co-authored-by: Raynel Sanchez <[email protected]> Co-authored-by: Henry Zou <[email protected]> Co-authored-by: Shelly Garion <[email protected]> Co-authored-by: Alexander Ivrii <[email protected]>
…iskit#12292) * Initial: Add `Target` class to `_accelerate` - Add `Target` class to test mobility between Rust and Python. - Add `add_instruction` method to test compatibility with instructions. * Fix: Remove empty property check - Property check caused most cases to panic. - Will be commented out and restored at a later time. * Add: Instructions property - Instructions property returns all added to the target. - Similar behavior to source. * Chore: comments and deprecated methods - Add comments to instruction property. - Use new_bound for new PyDicts. * Chore: Remove redundant code - Remove redundant transformation of PyObject to PyTuple. - Remove debugging print statement. * Add: `InstructionProperties` class and type checkers - Add `InstructionProperties` class to process properties in rust. - Add `is_instance` and `is_class` to identify certain Python objects. - Modify logic of `add_instruction` to use class check. - Other tweaks and fixes. * Add: Setter and Getter for calibration in `InstructionProperty` * Add: `update_instruction_properties` to Target. * Add: Update_from_instruction_schedule_map - Partial addition from Target.py\ - Introduction of hashable qarg data structure. - Other tweaks and fixes. * Add: Complete `update_from_instruction_schedule_map1 - Complete missing procedures in function. - Rename `Qargs` to `HashableVec`. - Make `HashableVec` generic. - Separate `import_from_module_call` into call0 and call1. - Other tweaks and fixes. * Add: instruction_schedule_map property. - Remove stray print statements. - Other tweaks and fixes. * Fix: Key issue in `update_from_instruction_schedule_map` - Remove all unsafe unwraps * Fix: Use PyResult Value for void functon - Update `update_from_instruction_schedule_map to use PyResult and '?' operator. - Use Bound Python objects whenever possible. - Other tweaks and fixes. * Add: Python wrapping for Target - Add temporary _target module for testing. - Remove update_from_instruction_schedule_map function back to python. - Add python properties for all public attributes in rust - Other tweaks and fixes. * Add: `qargs` property - Add identical method `qargs` to obtain the qargs of a target. - Other tweaks and fixes. * Add: `qargs_for_operation_name` function. - Add function with identical behavior to the original in Target. - Other tweaks and fixes. * Add: durations method for Target - Add target module to qiskit init file. - Remove is_instance method. - Modify set_calibration method in InstructionProperty to leave typechecking to Python. - Change rust Target alias to Target2. - Other tweaks and fixes, * Add: InstructionProperties wrapper in python * Fix: InstructionProperties could not receive calibrations - Fix wrong setters/getters for calibration in InstructionProperty object in rust. * Add: more methods to Target in `target.rs` - Add FromPyObject trait to Hashable vec to receive Tuples and transform them directly into this type. - Add operations_for_qargs for Target class in Rust side and Python. - Fix return dict keys for `qargs_for_operation_name`. - Add `timing_constrains` and `operation_from_name` to Python side. - Other tweaks and fixes. * Fix: missing return value in `operations_for_args` - Fix wrong name for function operation_for_qargs. - Fix missing return value in the python side. - Other tweaks and fixes. * Fix: Bad compatibility with InstructionProperties - Make `InstructionProperties` "_calibration" attribute visible. - Removed attribute "calibration", treat as class property. - Other tweaks and fixes * Add: `operation_names_for_qargs` to Target - Port class method to rust and connect to Python wrapper. - Other tweaks and fixes. * Add: instruction_supported method to rust and python: - Other tweaks and fixes. * Add: changes to add_instruction function to increase functionality. - These changes break current functionality of other functions, butemulate intended behavior better. - Fixes coming soon. * Fix: Backwards compatibility with `add_instruction` - Fixed wrong additions to HashMaps in the rust side causing instructions to be missing. - Other tweaks and fixes. * Fix: Gate Map behavior didn't match Qiskit#11422 - Make GateMap use optional values to match behavior of Qiskit#11422. - Define GateMapType for complex type in self.gate_map. - Throw Python KeyError exceptions from the rust side in `update_instruction_properties` and other functions. - Modify logic in subsequent functions that use gate_map optional values. - Other tweaks and fixes. * Add: `has_calibration` method to Target * Add: `get_calibraton` method to Target * Add: `instruction_properties` method to Target * Add: `build_coupling_map` and helper methods - `build_coupling_map`will remain in Python for now, along with its helper functions. - Make `gate_name_map` visible to python. - Add `coupling_graph` attribute to Target in Rust. - Other tweaks and fixes. * Add: `get_non_global_operation_names` to Target. - Add attributes `non_global_strict_basis` and `non_global_basis` as Optional. - Other tweaks and fixes. * Add: Missing properties - Add properties: operations, operation_names, and physical_qubits. - Reorganize properties placement. - Other tweaks and fixes. * Add: `from_configuration` classmethod to Target. - Add method that mimics the behavior of the python method. - Change concurrent_measurements to 2d Vec instead of a Vec of sets. - Other tweaks and fixes. * Add: Magic methods to Rust and Python - Add docstring to __init__. - Add __iter__, __getitem__, __len__, __contains__, keys, values, and items methods to rust. - Add equivalen methods to python + the __str__ method. - Make description an optional attribute in rust. - Other tweaks and fixes. * Fix: Bugs when fetching qargs or operations - Fix qarg_for_operation_name logic to account for None and throw correct exceptions. - Stringify description before sending in case of numerical descriptors. - Fix qarg to account for None entry. - Other tweaks and fixes. * Chore: Prepare for Draft PR - Remove _target.py testing file. - Fix incorrect initialization of calibration in InstructionProperties. - Other tweaks and fixes. * Fix: target not being recognized as a module - Add target to the pyext crate. - Change placement of target import for alphabetical ordering. - Other tweaks and fixes. * Fix: Change HashMap to IndexMap - Change from f32 to f64 precision. - Other tweaks and fixes. * Fix: Move InstructionProperties fully to Rust - Move InstructionProperties to rust. - Modify gate_map to accept an InstructionProprties object instead of PyObjecy. - Change update_instruction_properties to use Option InstructionProprtyird. - Remove InstructionProperties from target.py - Other tweaks and fixes. * Fix: Make Target inherit from Rust - Make Target inherit from the rust-side Target by using subclass attribute, then extending its functionality using python. - Switch from __init__ to __new__ to adapt to the Target rust class. - Modify every trait that worked with `target._Target` to use `super()` or `self` instead. - Fix repr in InstructionProperties to not show `Some()` when a value exists. - Fix `__str__` method in `Target` to not display "None" if no description is given. - Assume `num_qubits` is the first argument when an integer is provided as a first argument and nothing else is provided for second (Target initializer). - Return a set in operation_names instead of a Vec. - Other tweaks and fixes. * Fix: Recognize None in `operation_for_qargs`. - Fix module labels for each class in target.rs. - Use py.is_instance instead of passing isinstance to `instruction_supported`. - Modify `operations_for_qargs` to accept optional values less aggressively. Allow it to find instructions with no qargs. (NoneType). - Other tweaks and fixes. * Fix: Make InstructionProperties subclassable. - Fix get_non_global_operation_names to accept optional values and fix search set to use sorted values. - Fix __repr__ method in InstructionProperties to add punctuation. - Fix typo in python durations method. - Modify test to overload __new__ method instead of just __init__ (Possible breaking change). -Other tweaks and fixes. * Fix: errors in `instruction_properties` and others: - Allow `instruction_properties` method to view optional properties. - Allow `operation_names_for_qargs` to select class instructions when None is passed as a qarg. - Modify __str__ method to display error and duration times as int if the value is 0. - Other tweaks and fixes. * Fix: call `isclass` from rust, instead of passing it from Python. * Fix: Move `update_from_instruction_schedule_map` to rust. * Fix: Move `durations` to rust. * Fix: Move `timing_constraints` to rust * Fix: Move operations_from_name fully to rust * Fix: `instruction_supported` method: - Rewrite the logic of instruction_supported due to previous errors in the method. - Move `check_obj_params` to Rust. - Other tweaks and fixes. * Fix: errors in `from_configuration` class method. - Fix some of the logic when retrieving gates from `name_mapping`. - Remove function arguments in favor of implementing counterpart functions in rust. - Add qubit_props_list_from_props function and return rust datatypes. - Fix wrong error handling procedures when retrieving attributes from backend_property. - Other tweaks and fixes. * Fix: Import `InstructionScheduleMap` directly instead of passing. - `instruction_schedule_map()` now imports the classtype directly from rust instead of needing it to be passed from python. - Remove unused imports in `target.py`. - Ignore unused arguments in `test_extra_props_str`. - Other tweaks and fixes. * Docs: Add docstrings to rust functions - Remove redundant redefinitions in python. - Fix text_signatures for some rust functions. - Added lint exceptions to some necessary imports and function arguments. - Other tweaks and fixes. * Add: Make `Target` and `InstructionProperties` pickleable. - Add `__getstate__` and `__setstate__` methods to make both rust subclasses pickleable. * Fix: Wrong calibration assignment in __setstate__ - Use set_calibration to set the correct calibration argument. - Fix wrong signature in get_non_global_operation_names. - Other tweaks and fixes. * Refactor: HashableVec is now Qarg - Use `PhysicalQubit` instead of u32 for qargs. - Use a `SmallVec` of size 4 instead of a dynamic Vec. - Default to using the `Hash()` method embedded in `SmallVec`. - Add a Default method to easily unwrap Qarg objects. - Other tweaks and fixes. * Add: `get` function to target. - Remove some redundant cloning in code. - Other small fixes. * Fix: Remove unnecessary Optional values in gate_map. - Update every gate_map call to use the new format. - Other small tweaks and fixes. * Refactor: `calibration` is for `InstructionProperties` - Use python `None` instead of option to store `calibration` in `InstructionProperties`. - Adapt code to these changes. - Remove redundant implementation of Hash in Qargs. - Other tweaks and fixes. * Fix: Temporary speedup for `gate_map` access - Added temporary speedups to access the gate_map by returning the values as PyObjects. - Convert qargs to rust tuples instead of initializing a `PyTuple`. - Store `InstructionProperties` as a python ref in gate_map. (Will be changed in future updates). - Other tweaks anf fixes. * Fix: Incorrect extractions for `InstructionProperties` - Fix incorrect conversion of `InstructionProperties` to `Py<InstructionProperties>` - Fix incorrect extraction of qargs in `update_from_instruction_schedule_map` * Fix: Hide all private attributes in `Target` - Hide all private attributes of the `Target` to prevent unecessary cloning. - Other small tweaks and fixes. * Add: New representation of gate_map using new pyclasses: - Make Qarg a sequence pyclass. - Make QargPropsMap the new representation of a GateMap value. - Adapt the code to new structure. - TODO: Add missing magic methods for sequence and mapping objects. - Other small tweaks and fixes. * Add: Use custom datatypes to return values to Python. - Add QargSet datatype to return a set of qargs. - Works as return type for `Target.qargs` - Object is has itertype of QargSetIter. - Rename QargPropMap to PropsMap - Use iterator type IterPropsMap - Other small tweaks and fixes. * Fix: Extend `InstructionProperties` to be subclassable using `__init__: - Made a subclass of `InstructionProperties` that can be extended using an `__init__`method. - Revert previous changes to `test_target.py`. - Other tweaks and fixes. * Refactor: Split target into its own module - Reestructure the files to improve readability of code. - `instruction_properties.rs` contaisn the `InstructionProperties` class. - `mod.rs` contains the `Target` class. - `qargs.rs` contains the Qargs struct to store quantum arguments. - `property_map` contains the Qarg: Property Mapping that will be stored in the gate_map. - Add missing methods to PropsMap: - Add `PropsMapKeys` object to store the qargs as a set. - Add methods to compare and access `PropsMapKey`. - Add QargsOrTuple enum in Qargs to parse Qargs instantly. * Fix: Rest of failing tests in Target - Modify the `InstructionProperties` python wrapper. - InstructionProperties was not assigning properties to rust side. - Make duration in `InstructionProperties` setable. - Add `__eq__` method for `PropMap` to compare with other dicts. - `PropMapKeys` can only be compared with a Set. - Remove `qargs_for_operation_name` from `target.py` - Other small tweaks and fixes. * Add: New GateMap Structure - GateMap is now its own mapping object. - Add `__setstate__` and `__getstate__` methods for `PropMap` and `GateMap`. - Other small tweaks and fixes. * Fix: Make new subclasses pickleable - Add module location to `PropsMap`, `GateMap`, and `Qargs`. - Added default method to PropMap. - Made default method part of class initializers. - Other smalls tweaks and fixes. * Fix: Remove redundant lookup in Target (Qiskit#12373) * Format: `mod.rs` qubit_comparison to one line. * Add: `GateMapKeys` object in GateMap: - Use IndexSet as a base to preserve the insertion order. - Other tweaks and fixes. * Add: __sub__ method to GateMapKeys * Fix: Modify `GateMap` to store values in Python heap. - Fix `GateMap.__iter__` to use an IndexKeys iterator. - Other small tweaks and fixes. * Fix: Remove duplicate import of `IndexSet::into_iter` in `GateMap`. - Make `__iter__` use the keys() method in `GateMap`. * Fix:: Adapt to target changes (Qiskit#12288) - Fix lint stray imports. * Fix: Incorrect creation of parameters in `update_from_instruction_schedule_map` - Add `tupelize` function to create tuples from non-downcastable items. - Fix creation of Parameters by iterating through members of tuple object and mapping them to parameters in `update_from_instruction_schedule_map`. - Add missing logic for creating a Target with/without `qubit_properties`. - Add tuple conversion of `Qargs` to store items in a dict in `BasisTranslator` and `UnitarySynthesis` passes. - Cast `PropsMap` object to dict when comparing in `test_fake_backends.py`. - Modify logic of helper functions that receive a bound object reference, a second `py` not required as an argument. - Add set operation methods to `GateMapKeys`. - Other tweaks and fixes. * Fix: More failing tests - Fix repeated erroneous calls to `add_instruction` in `update_from_instruction_schedule_map` - Add missing condition in `instruction_supported` - Use `IndexSet` instead of `HashSet` for `QargsSet`. - Other small tweaks and fixes. * Add: Macro rules for qargs and other sequences. - Create `QargSet` and `PropsMap` using the new macros. - Return a `TargetOpNames` ordered set to python in `operation_names`. - Remove the Python side `operation_names.` - Fix faulty docstring in `target.py`. - Other tweaks and fixes. * Docs: Add necessary docstrings to all new rust functions. - Remove duplicate Iterator in GateMap. - Other small tweaks and fixes. * Fix: Use `GILOneCell` and remove `Qargs` - Use `GILOneCell` to import python modules only once at initialization. - Remove the custom data structure `Qargs` to avoid conversion overhead. - `Qargs` does not use `PhysicalQubits`, `u32` is used instead. - Fix `__setstate__ `and `__getstate__` methods for `PropsMap`, `GateMap`, and `key_like_set_iterator` macro_rule. - Update code to use the new structures. - TODO: Fix broken tests. * Fix: Cast `Qargs` to `Tuple` in specific situations - Use tupleize to cast `Qargs` to `Tuple` in `instructions`. - Use downcast to extract string in `add_instruction`. - Other tweaks and fixes. * Add: Make `Target` Representable in Rust - Rename `InstructionProperties` as `BaseInstructionProperties`. - Remove `Calibration` from the rust space. - Restore `gate_map`, `coupling_map`, `instruction_schedule_map`, and `instruction_durations` to rust. - Remove all unnecessary data structures from rust space. - Other tweaks and fixes. * Refactor: Remove previour changes to unrelated files. * Add: rust native functions to target - Added rust native functionality to target such that a `py` would not be needed to use one. - Add Index trait to make `Target` subscriptable. - Other small tweaks and fixes. * Fix: Remove all unnecessary python method calls. - Remove uage of `inspect.isclass`. - Rename `Target` to `BaseTarget` in the rust side. - Rename `err.rs` to `errors.rs`. - Remove rust-native `add_inst` and `update_inst` as Target should not be modified from Rust. - Made `add_instruction` and `update_instruction_properties` private in `BaseTarget`. - Add missing `get` method in `Target`. - Other tweaks and fixes * Format: Fix lint * Fix: Wrong return value for `BaseTarget.qargs` * Add: Temporary Instruction representation in rust. - Add temporary instruction representation to avoid repeated extraction from python. * Add: Native representation of coupling graph * Fix: Wrong attribute extraction for `GateRep` * Remove: `CouplingGraph` rust native representation. - Move to different PR. * Format: Remove stray whitespace * Add: `get_non_global_op_names` as a rust native function * Fix: Use Ahash for Hashing - Use ahash for hashing when possible. - Rename `BaseTarget` to `Target` in rust only. - Rename `BaseInstructionProperties` to `InstructionProperties` in rust only. - Remove optional logic from `generate_non_global_op_names`. - Use dict for `__setstate__` and `__getstate__` in `Target`. - Reduced the docstring for `Target` and `InstructionProperties`. - Other small tweaks and fixes. * Format: new changes to `lib.rs` * Format: Adapt to new lint rules * Fix: Use new gates infrastructure (Qiskit#12459) - Create custom enum to collect either a `NormalOperation` or a `VariableOperation` depending on what is needed. - Add a rust native `is_instruction_supported` method to check whether a Target supports a certain instruction. - Make conversion methods from `circuit_instruction.rs` public. - Add comparison methods for `Param` in `operations.rs` - Remove need for isclass method in rustwise `add_instruction` - Other tweaks and fixes. * Format: Fix rust formatting * Add: rust-native method to obtain Operstion objects. * Add: Comparison methods for `Param` * FIx: Add display methods for `Params` * Format: Fix lint test * Format: Wrong merge conflict solve * Fix: Improve rust methods to use iterators. - Adapt the Python methods to leverage the rust native improvements. - Use python native structures for the Python methods. * Format: Remove extra blankspace * Fix: Remove `text_signature`, use `signature` instead. * Fix: Rectify the behavior of `qargs` - Keep insertion order by inserting all qargs into a `PySet`. - Perform conversion to `PyTuple` at insertion time leveraging the iterator architecture. - Remove python side counterpart to avoid double iteration. - Make rust-native `qargs` return an iterator. * Fix: Corrections from Matthew's review - Use `format!` for repr method in `InstructionProperties` - Rename `Variable` variant of `TargetInstruction` to `Variadic`. - Remove `internal_name` attribute from `TargetOperation`. - Remove `VariableOperation` class. - Use `u32` for `granularity`, `pulse_alignment`, and `acquire_alignment`. - Use `Option` to store nullable `concurrent_measurements. - Use `&str` instead of `String` for most function arguments. - Use `swap_remove` to deallocate items from the provided `properties` map in `add_instruction`. - Avoid cloning instructions, use `to_object()` instead. - Avoid using `.to_owned()`, use `.clone()` instead. - Remove mention of `RandomState`, use `ahash::HashSet` instead. - Move parameter check to python in `instruction_supported`. - Avoid exposing private attributes, use the available ones instead. - Filter out `Varidadic` Instructions as they're not supported in rust. - Use peekable iterator to peak at the next qargs in `generate_non_global_op_names`. - Rename `qarg_set` to `deduplicated_qargs` in `generate_non_global_op_names`. - Return iterator instances instead of allocated `Vec`. - Add `python_compare` and `python_is_instance` to perform object comparison with objects that satisfy the `ToPyObject` trait. - Other small tweaks and fixes. * Implement a nullable dict-like structure for IndexMap (Qiskit#2) * Initial: Implement a nullable dict-like structure for IndexMap * FIx: Erroneous item extraction from Python - Fix error that caused `None` values to be ignored from `None` keys. - Removed mutability from rust function argument in `add_instruction`. - Object is mutably referenced after option unwrapping. - Add missing header in `nullable_index_map.rs`. - Add Clone as a `K` and/or `V` constraint in some of the iterators. - Remove `IntoPy` constraint from `NullableIndexMap<K, V>`. - Add `ToPyObject` trait to `NullableIndexMap<K, V>`. * Fix: inplace modification of Python dict. - Perform `None` extraction from rust. - Revert changes to `Target.py` * Fix: Avoid double iteration by using filter_map. * Docs: Add inline comments. * Fix: More specific error message in `NullableIndexMap` * Fix: Use `Mapping` as the metaclass for `Target` - Minor corrections from Matthew's review. * Fix: Make `Target` crate-private. - Due to the private nature of `NullableIndexMap`, the `Target` has to be made crate private. - Add temporary`allow(dead_code)` flag for the unused `Target` and `NullableIndexMap` methods. - Fix docstring of `Target` struct. - Fix docstring of `add_instruction`. - Make several python-only operations public so they can be used with other `PyClass` instances as long as they own the gil. - Modify `py_instruction_supported` to accept bound objects. - Use rust-native functions for some of the instance properties. - Rewrite `instruction` to return parameters as slice. - `operation_names` returns an `ExactSizeIterator`. - All rust-native methods that return an `OperationType` object, will return a `NormalOperation` instance which includes the `OperationType` and the parameters. * Fix: Comments from Matthew's review - Mention duplication in docstring for rust Target. - Use f"{*:g}" to avoid printing the floating point for 0 in `Target`'s repr method. - Add note mentioning future unit-tests in rust. * Fix: Adapt to Qiskit#12730
Summary
This commit adds a native representation of Gates, Instruction, and Operations to rust's circuit module. At a high level this works by either wrapping the Python object in a rust wrapper struct that tracks metadata about the operations (name, num_qubits, etc) and then for other details it calls back to Python to get dynamic details like the definition, matrix, etc. For standard library gates like Swap, CX, H, etc this replaces the on-circuit representation with a new rust enum StandardGate. The enum representation is much more efficient and has a minimal memory footprint (just the enum variant and then any parameters or other mutable state stored in the circuit instruction). All the gate properties such as the matrix, definiton, name, etc are statically defined in rust code based on the enum variant (which represents the gate).
The use of an enum to represent standard gates does mean a change in what we store on a CircuitInstruction. To represent a standard gate fully we need to store the mutable properties of the existing Gate class on the circuit instruction as the gate by itself doesn't contain this detail. That means, the parameters, label, unit, duration, and condition are added to the rust side of circuit instrucion. However no Python side access methods are added for these as they're internal only to the Rust code. In Qiskit 2.0 to simplify this storage we'll be able to drop, unit, duration, and condition from the api leaving only label and parameters. But for right now we're tracking all of the fields.
To facilitate working with circuits and gates full from rust the setting the
operation
attribute of aCircuitInstruction
object now transltates the python object to an internal rust representation. For standard gates this translates it to the enum form described earlier, and for other circuit operations 3 new Rust structs: PyGate, PyInstruction, and PyOperation are used to wrap the underlying Python object in a Rust api. These structs cache some commonly accessed static properties of the operation, such as the name, number of qubits, etc. However for dynamic pieces, such as the definition or matrix, callback to python to get a rust representation for those.Similarly whenever the
operation
attribute is accessed from Python it converts it back to the normal Python object representation. For standard gates this involves creating a new instance of a Python object based on it's internal rust representation. For the wrapper structs a reference to the wrapped PyObject is returned.To manage the 4 variants of operation (
StandardGate
,PyGate
,PyInstruction
, andPyOperation
) a new Rust traitOperation
is created that defines a standard interface for getting the properties of a given circuit operation. This common interface is implemented for the 4 variants as well as theOperationType
enum which wraps all 4 (and is used as the type forCircuitInstruction.operation
in the rust code.As everything in the
QuantumCircuit
data model is quite coupled moving the source of truth for the operations to exist in Rust means that more of the underlyingQuantumCircuit
's responsibility has to move to Rust as well. Primarily this involves theParameterTable
which was an internal class for tracking which instructions in the circuit have aParameterExpression
parameter so that when we go to bind parameters we can lookup which operations need to be updated with the bind value. Since the representation of those instructions now lives in Rust and Python only recieves a ephemeral copy of the instructions the ParameterTable had to be reimplemented in Rust to track the instructions. This new parameter table maps the Parameter's uuid (as a u128) as a unique identifier for each parameter and maps this to a positional index in the circuit data to the underlying instruction using that parameter. This is a bit different from the Python parameter table which was mapping a parameter object to the id of the operation object using that parmaeter. This also leads to a difference in the binding mechanics as the parameter assignment was done by reference in the old model, but now we need to update the entire instruction more explicitly in rust. Additionally, because the global phase of a circuit can be parameterized the ownership of global phase is moved from Python into Rust in this commit as well.After this commit the only properties of a circuit that are not defined in Rust for the source of truth are the bits (and vars) of the circuit, and when creating circuits from rust this is what causes a Python interaction to still be required.
This commit does not translate the full standard library of gates as that would make the pull request huge, instead this adds the basic infrastructure for having a more efficient standard gate representation on circuits. There will be follow up pull requests to add the missing gates and round out support in rust.
The goal of this pull request is primarily to add the infrastructure for representing the full circuit model (and dag model in the future) in rust. By itself this is not expected to improve runtime performance (if anything it will probably hurt performance because of extra type conversions) but it is intended to enable writing native circuit manipulations in Rust, including transpiler passes without needing involvement from Python. Longer term this should greatly improve the runtime performance and reduce the memory overhead of Qiskit. But, this is just an early step towards that goal, and is more about unlocking the future capability. The next steps after this commit are to finish migrating the standard gate library and also update the
QuantumCircuit
methods to better leverage the more complete rust representation (which should help offset the performance penalty introduced by this).Details and comments
Fixes: #12205
TODO: