-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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 a rust representation of operations, instructions, and gates #12205
Comments
In my ideal model for the standard library our internal representation would be an #[derive(Clone)]
#[pyclass]
pub enum StandardGate {
ZGate,
YGate,
XGate,
CZGate,
CYGate,
CXGate,
CCXGate,
RXGate,
RYGate,
RZGate,
ECRGate,
SwapGate,
SXGate,
GlobalPhaseGate,
IGate,
HGate,
....
} The trait impls for this enum aren't the most ergonomic to work with, but the efficiency of the structure seems worth it (especially as we don't modify the standard gate library too often) |
This compiles but has circular import errors because we need to use the standard gate classes in Python to map from python to rust. Fixes: Qiskit#12205
This compiles but has circular import errors because we need to use the standard gate classes in Python to map from python to rust. Fixes: Qiskit#12205
This compiles but has circular import errors because we need to use the standard gate classes in Python to map from python to rust. Fixes: Qiskit#12205
This compiles but has circular import errors because we need to use the standard gate classes in Python to map from python to rust. Fixes: Qiskit#12205
This compiles but has circular import errors because we need to use the standard gate classes in Python to map from python to rust. Fixes: Qiskit#12205
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 enum StandardGate. The enum representation is much more efficient and has a minimal memory footprint as all the gate properties are defined in 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 and hopefully in Qiskit 2.0 we'll be able to drop, unit, duration, and condition from the api leaving only label and parameters. When an object is added to a CircuitInstruction the operation field is translated to it's internal representation automatically. For standard gates this translates it to the enum form, and for Python defined gates this involves just wrapping them. Then whenever the operation field of a circuit instruction object is read from python it converts it back to the normal Python object representation. 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. TODO: - [ ] Fix parameter handling on copy (might involve moving parameter table to rust) - [ ] Handle global phase in definitions (might mean moving a QuantumCircuit struct to rust instead of just CircuitData) - [ ] Add definitions for gates migrated so far Fixes: Qiskit#12205
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 enum StandardGate. The enum representation is much more efficient and has a minimal memory footprint as all the gate properties are defined in 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 and hopefully in Qiskit 2.0 we'll be able to drop, unit, duration, and condition from the api leaving only label and parameters. When an object is added to a CircuitInstruction the operation field is translated to it's internal representation automatically. For standard gates this translates it to the enum form, and for Python defined gates this involves just wrapping them. Then whenever the operation field of a circuit instruction object is read from python it converts it back to the normal Python object representation. 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. TODO: - [ ] Fix parameter handling on copy (might involve moving parameter table to rust) - [ ] Handle global phase in definitions (might mean moving a QuantumCircuit struct to rust instead of just CircuitData) - [ ] Add definitions for gates migrated so far Fixes: Qiskit#12205
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
What should we add?
As part of the ongoing effort to be able to more of Qiskit's operations in rust for performance and efficiency the last major piece (besides the
DAGCircuit
in #11721) are the operations object. This issue is to track the development of rust data structures that represent the the circuit's operations.The main complexity here is that the python side of Qiskit exposes a very flexible and dynamic interface for defining operations. This includes fully custom operation objects that are defined in Python and generate their contents dynamically. We want to find a model where we retain this functionality and capability from Python as it's quite widely used, but also ensure we're storing an efficient representation in rust as the source of truth. The key goal of this work though is to enable working with gates from rust without any involvement from Python.
The text was updated successfully, but these errors were encountered: