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

Some gates in rust #5

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fb70814
Add infrastructure for gates, instruction, and operations in Rust
mtreinish May 3, 2024
ad3e3c5
Merge branch 'main' into gates-in-rust
mtreinish May 25, 2024
37c0780
Fix Python->Rust Param conversion
mtreinish May 25, 2024
a6e69ba
Fix qasm3 exporter for std gates without stdgates.inc
mtreinish May 25, 2024
4e34642
Fix base scheduler analysis pass duration setting
mtreinish May 25, 2024
0edcfb0
Fix python lint
mtreinish May 25, 2024
f896512
Fix last failing qasm3 test for std gates without stdgates.inc
mtreinish May 25, 2024
046737f
Remove superfluous comment
mtreinish May 25, 2024
5c5b90f
Cache imported classes with GILOnceCell
mtreinish May 26, 2024
7329399
Remove unused python variables
mtreinish May 26, 2024
ae64fd7
Add missing file
mtreinish May 26, 2024
76599b2
Update QuantumCircuit gate methods to bypass Python object
mtreinish May 26, 2024
14b7133
Deduplicate gate matrix definitions
mtreinish May 26, 2024
0863830
Fix lint
mtreinish May 26, 2024
c4cda8d
Attempt to fix qasm3 test failure
mtreinish May 26, 2024
ffe04e5
Merge branch 'main' into gates-in-rust
mtreinish May 28, 2024
e9bb053
Add compile time option to cache py gate returns for rust std gates
mtreinish May 28, 2024
dfb02de
Merge branch 'main' into gates-in-rust
mtreinish May 28, 2024
0980d8d
Add num_nonlocal_gates implementation in rust
mtreinish May 29, 2024
dc9e8f0
Merge remote-tracking branch 'origin/main' into gates-in-rust
mtreinish May 29, 2024
b35bdbd
Performance tuning circuit construction
mtreinish May 30, 2024
3ea95de
Add back validation of parameters on gate methods
mtreinish May 31, 2024
2f81bde
Skip validation on gate creation from rust
mtreinish May 31, 2024
5f6453f
Implement RGate in Rust according to established pattern
jlapeyre Jun 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ jobs:
python -m pip install -U -r requirements.txt -c constraints.txt
python -m pip install -U -r requirements-dev.txt -c constraints.txt
python -m pip install -c constraints.txt -e .
if: matrix.python-version == '3.10'
env:
QISKIT_NO_CACHE_GATES: 1
- name: 'Install dependencies'
run: |
python -m pip install -U -r requirements.txt -c constraints.txt
python -m pip install -U -r requirements-dev.txt -c constraints.txt
python -m pip install -c constraints.txt -e .
if: matrix.python-version == '3.12'
- name: 'Install optionals'
run: |
python -m pip install -r requirements-optional.txt -c constraints.txt
Expand Down
12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ Note that in order to run `python setup.py ...` commands you need have build
dependency packages installed in your environment, which are listed in the
`pyproject.toml` file under the `[build-system]` section.

### Compile time options

When building qiskit from source there are options available to control how
Qiskit is build. Right now the only option is if you set the environment
variable `QISKIT_NO_CACHE_GATES=1` this will disable runtime caching of
Python gate objects when accessing them from a `QuantumCircuit` or `DAGCircuit`.
This makes a tradeoff between runtime performance for Python access and memory
overhead. Caching gates will result in better runtime for users of Python at
the cost of increased memory consumption. If you're working with any custom
transpiler passes written in python or are otherwise using a workflow that
repeatedly accesses the `operation` attribute of a `CircuitInstruction` or `op`
attribute of `DAGOpNode` enabling caching is recommended.

## Issues and pull requests

Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ license = "Apache-2.0"
[workspace.dependencies]
indexmap.version = "2.2.6"
hashbrown.version = "0.14.0"
num-complex = "0.4"
ndarray = "^0.15.6"
numpy = "0.21.0"
smallvec = "1.13"

# Most of the crates don't need the feature `extension-module`, since only `qiskit-pyext` builds an
# actual C extension (the feature disables linking in `libpython`, which is forbidden in Python
# distributions). We only activate that feature when building the C extension module; we still need
Expand Down
8 changes: 4 additions & 4 deletions crates/accelerate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,29 @@ doctest = false

[dependencies]
rayon = "1.10"
numpy = "0.21.0"
numpy.workspace = true
rand = "0.8"
rand_pcg = "0.3"
rand_distr = "0.4.3"
ahash = "0.8.11"
num-traits = "0.2"
num-complex = "0.4"
num-complex.workspace = true
num-bigint = "0.4"
rustworkx-core = "0.14"
faer = "0.19.0"
itertools = "0.13.0"
qiskit-circuit.workspace = true

[dependencies.smallvec]
version = "1.13"
workspace = true
features = ["union"]

[dependencies.pyo3]
workspace = true
features = ["hashbrown", "indexmap", "num-complex", "num-bigint", "smallvec"]

[dependencies.ndarray]
version = "^0.15.6"
workspace = true
features = ["rayon", "approx-0_5"]

[dependencies.approx]
Expand Down
2 changes: 1 addition & 1 deletion crates/accelerate/src/isometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use itertools::Itertools;
use ndarray::prelude::*;
use numpy::{IntoPyArray, PyReadonlyArray1, PyReadonlyArray2};

use crate::two_qubit_decompose::ONE_QUBIT_IDENTITY;
use qiskit_circuit::gate_matrix::ONE_QUBIT_IDENTITY;

/// Find special unitary matrix that maps [c0,c1] to [r,0] or [0,r] if basis_state=0 or
/// basis_state=1 respectively
Expand Down
54 changes: 1 addition & 53 deletions crates/accelerate/src/two_qubit_decompose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ use rand::prelude::*;
use rand_distr::StandardNormal;
use rand_pcg::Pcg64Mcg;

use qiskit_circuit::gate_matrix::{CXGATE, HGATE, ONE_QUBIT_IDENTITY, SXGATE, XGATE};
use qiskit_circuit::SliceOrInt;

const PI2: f64 = PI / 2.0;
Expand All @@ -60,11 +61,6 @@ const TWO_PI: f64 = 2.0 * PI;

const C1: c64 = c64 { re: 1.0, im: 0.0 };

pub static ONE_QUBIT_IDENTITY: [[Complex64; 2]; 2] = [
[Complex64::new(1., 0.), Complex64::new(0., 0.)],
[Complex64::new(0., 0.), Complex64::new(1., 0.)],
];

static B_NON_NORMALIZED: [[Complex64; 4]; 4] = [
[
Complex64::new(1.0, 0.),
Expand Down Expand Up @@ -342,54 +338,6 @@ fn rz_matrix(theta: f64) -> Array2<Complex64> {
]
}

static HGATE: [[Complex64; 2]; 2] = [
[
Complex64::new(FRAC_1_SQRT_2, 0.),
Complex64::new(FRAC_1_SQRT_2, 0.),
],
[
Complex64::new(FRAC_1_SQRT_2, 0.),
Complex64::new(-FRAC_1_SQRT_2, 0.),
],
];

static CXGATE: [[Complex64; 4]; 4] = [
[
Complex64::new(1., 0.),
Complex64::new(0., 0.),
Complex64::new(0., 0.),
Complex64::new(0., 0.),
],
[
Complex64::new(0., 0.),
Complex64::new(0., 0.),
Complex64::new(0., 0.),
Complex64::new(1., 0.),
],
[
Complex64::new(0., 0.),
Complex64::new(0., 0.),
Complex64::new(1., 0.),
Complex64::new(0., 0.),
],
[
Complex64::new(0., 0.),
Complex64::new(1., 0.),
Complex64::new(0., 0.),
Complex64::new(0., 0.),
],
];

static SXGATE: [[Complex64; 2]; 2] = [
[Complex64::new(0.5, 0.5), Complex64::new(0.5, -0.5)],
[Complex64::new(0.5, -0.5), Complex64::new(0.5, 0.5)],
];

static XGATE: [[Complex64; 2]; 2] = [
[Complex64::new(0., 0.), Complex64::new(1., 0.)],
[Complex64::new(1., 0.), Complex64::new(0., 0.)],
];

fn compute_unitary(sequence: &TwoQubitSequenceVec, global_phase: f64) -> Array2<Complex64> {
let identity = aview2(&ONE_QUBIT_IDENTITY);
let phase = Complex64::new(0., global_phase).exp();
Expand Down
15 changes: 14 additions & 1 deletion crates/circuit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,17 @@ doctest = false

[dependencies]
hashbrown.workspace = true
pyo3.workspace = true
num-complex.workspace = true
ndarray.workspace = true
numpy.workspace = true

[dependencies.pyo3]
workspace = true
features = ["hashbrown", "indexmap", "num-complex", "num-bigint", "smallvec"]

[dependencies.smallvec]
workspace = true
features = ["union"]

[features]
cache_pygates = []
63 changes: 63 additions & 0 deletions crates/circuit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,66 @@ The Rust-based data structures for circuits.
This currently defines the core data collections for `QuantumCircuit`, but may expand in the future to back `DAGCircuit` as well.

This crate is a very low part of the Rust stack, if not the very lowest.

The data model exposed by this crate is as follows.

## CircuitData

The core representation of a quantum circuit in Rust is the `CircuitData` struct. This containts the list
of instructions that are comprising the circuit. Each element in this list is modeled by a
`CircuitInstruction` struct. The `CircuitInstruction` contains the operation object and it's operands.
This includes the parameters and bits. It also contains the potential mutable state of the Operation representation from the legacy Python data model; namely `duration`, `unit`, `condition`, and `label`.
In the future we'll be able to remove all of that except for label.

At rest a `CircuitInstruction` is compacted into a `PackedInstruction` which caches reused qargs
in the instructions to reduce the memory overhead of `CircuitData`. The `PackedInstruction` objects
get unpacked back to `CircuitInstruction` when accessed for a more convienent working form.

Additionally the `CircuitData` contains a `param_table` field which is used to track parameterized
instructions that are using python defined `ParmaeterExpression` objects for any parameters and also
a global phase field which is used to track the global phase of the circuit.

## Operation Model

In the circuit crate all the operations used in a `CircuitInstruction` are part of the `OperationType`
enum. The `OperationType` enum has four variants which are used to define the different types of
operation objects that can be on a circuit:

- `StandardGate`: a rust native representation of a member of the Qiskit standard gate library. This is
an `enum` that enuerates all the gates in the library and statically defines all the gate properties
except for gates that take parameters,
- `PyGate`: A struct that wraps a gate outside the standard library defined in Python. This struct wraps
a `Gate` instance (or subclass) as a `PyObject`. The static properties of this object (such as name,
number of qubits, etc) are stored in Rust for performance but the dynamic properties such as
the matrix or definition are accessed by calling back into Python to get them from the stored
`PyObject`
- `PyInstruction`: A struct that wraps an instruction defined in Python. This struct wraps an
`Instruction` instance (or subclass) as a `PyObject`. The static properties of this object (such as
name, number of qubits, etc) are stored in Rust for performance but the dynamic properties such as
the definition are accessed by calling back into Python to get them from the stored `PyObject`. As
the primary difference between `Gate` and `Instruction` in the python data model are that `Gate` is a
specialized `Instruction` subclass that represents unitary operations the primary difference between
this and `PyGate` are that `PyInstruction` will always return `None` when it's matrix is accessed.
- `PyOperation`: A struct that wraps an operation defined in Python. This struct wraps an `Operation`
instance (or subclass)` as a `PyObject`. The static properties of this object (such as name, number
of qubits, etc) are stored in Rust for performance. As `Operation` is the base abstract interface
definition of what can be put on a circuit this is mostly just a container for custom Python objects.
Anything that's operating on a bare operation will likely need to access it via the `PyObject`
manually because the interface doesn't define many standard properties outside of what's cached in
the struct.

There is also an `Operation` trait defined which defines the common access pattern interface to these
4 types along with the `OperationType` parent. This trait defined methods to access the standard data
model attributes of operations in Qiskit. This includes things like the name, number of qubits, the matrix, the definition, etc.

## ParameterTable

The `ParameterTable` struct is used to track which circuit instructions are using `ParameterExpression`
objects for any of their parameters. The Python space `ParameterExpression` is comprised of a symengine
symbolic expression that defines operations using `Parameter` objects. Each `Parameter` is modeled by
a uuid and a name to uniquely identify it. The parameter table maps the `Parameter` objects to the
`CircuitInstruction` in the `CircuitData` that are using them. The `Parameter` comprised of 3 `HashMaps` internally that map the uuid (as `u128`, which is accesible in Python by using `uuid.int`) to the `ParameterEntry`, the `name` to the uuid, and the uuid to the PyObject for the actual `Parameter`.

The `ParameterEntry` is just a `HashSet` of 2-tuples with usize elements. The two usizes represent the instruction index in the `CircuitData` and the index of the for the `CircuitInstruction.params` field of
a give instruction where the given `Parameter` is used in the circuit. If the instruction index is
`usize::MAX` that points to the global phase property of the circuit instead of a `CircuitInstruction`.
Loading