Skip to content
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

Open
mtreinish opened this issue Apr 18, 2024 · 1 comment · May be fixed by #12459
Open

Add a rust representation of operations, instructions, and gates #12205

mtreinish opened this issue Apr 18, 2024 · 1 comment · May be fixed by #12459
Assignees
Labels
mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library performance priority: high Rust This PR or issue is related to Rust code in the repository type: feature request New feature or request
Milestone

Comments

@mtreinish
Copy link
Member

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.

@mtreinish mtreinish added the type: feature request New feature or request label Apr 18, 2024
@mtreinish mtreinish added this to the 1.2.0 milestone Apr 18, 2024
@mtreinish
Copy link
Member Author

In my ideal model for the standard library our internal representation would be an enum that has a variant for each standard library gate and trait implementations for all of the data access. This would minimize what we store in the circuit structure to a minimal size. Something like:

#[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)

@mtreinish mtreinish added priority: high mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library Rust This PR or issue is related to Rust code in the repository labels Apr 18, 2024
mtreinish added a commit to mtreinish/qiskit-core that referenced this issue May 5, 2024
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
mtreinish added a commit to mtreinish/qiskit-core that referenced this issue May 5, 2024
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
mtreinish added a commit to mtreinish/qiskit-core that referenced this issue May 5, 2024
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
mtreinish added a commit to mtreinish/qiskit-core that referenced this issue May 6, 2024
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
mtreinish added a commit to mtreinish/qiskit-core that referenced this issue May 6, 2024
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
mtreinish added a commit to mtreinish/qiskit-core that referenced this issue May 7, 2024
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
mtreinish added a commit to mtreinish/qiskit-core that referenced this issue May 19, 2024
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
mtreinish added a commit to mtreinish/qiskit-core that referenced this issue May 25, 2024
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library performance priority: high Rust This PR or issue is related to Rust code in the repository type: feature request New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants