diff --git a/.binder/postBuild b/.binder/postBuild
index 9517953258f2..cb06527f280e 100644
--- a/.binder/postBuild
+++ b/.binder/postBuild
@@ -7,7 +7,7 @@
# - pylatexenc: for MPL drawer
# - pillow: for image comparison
# - appmode: jupyter extension for executing the notebook
-# - seaborn: visualisation pacakge required for some graphs
+# - seaborn: visualization pacakge required for some graphs
pip install matplotlib pylatexenc pillow appmode seaborn
pip install .
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index bcc86d63fcf5..88fd919e8ad6 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -1,6 +1,6 @@
name: Backport metadata
-# Mergify manages the opening of the backport PR, this workflow is just to extend its behaviour to
+# Mergify manages the opening of the backport PR, this workflow is just to extend its behavior to
# do useful things like copying across the tagged labels and milestone from the base PR.
on:
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
index 7c29f1376e46..a33d025affc7 100644
--- a/.github/workflows/wheels.yml
+++ b/.github/workflows/wheels.yml
@@ -30,7 +30,7 @@ jobs:
with:
components: llvm-tools-preview
- name: Build wheels
- uses: pypa/cibuildwheel@v2.19.1
+ uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_BEFORE_BUILD: 'bash ./tools/build_pgo.sh /tmp/pgo-data/merged.profdata'
CIBW_BEFORE_BUILD_WINDOWS: 'bash ./tools/build_pgo.sh /tmp/pgo-data/merged.profdata && cp /tmp/pgo-data/merged.profdata ~/.'
@@ -58,7 +58,7 @@ jobs:
with:
components: llvm-tools-preview
- name: Build wheels
- uses: pypa/cibuildwheel@v2.19.1
+ uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_BEFORE_ALL: rustup target add aarch64-apple-darwin
CIBW_BUILD: cp38-macosx_universal2 cp38-macosx_arm64
@@ -87,7 +87,7 @@ jobs:
with:
components: llvm-tools-preview
- name: Build wheels
- uses: pypa/cibuildwheel@v2.19.1
+ uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_SKIP: 'pp* cp36-* cp37-* *musllinux* *amd64 *x86_64'
- uses: actions/upload-artifact@v4
@@ -133,7 +133,7 @@ jobs:
with:
platforms: all
- name: Build wheels
- uses: pypa/cibuildwheel@v2.19.1
+ uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_ARCHS_LINUX: s390x
CIBW_TEST_SKIP: "cp*"
@@ -167,7 +167,7 @@ jobs:
with:
platforms: all
- name: Build wheels
- uses: pypa/cibuildwheel@v2.19.1
+ uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_ARCHS_LINUX: ppc64le
CIBW_TEST_SKIP: "cp*"
@@ -201,7 +201,7 @@ jobs:
with:
platforms: all
- name: Build wheels
- uses: pypa/cibuildwheel@v2.19.1
+ uses: pypa/cibuildwheel@v2.19.2
env:
CIBW_ARCHS_LINUX: aarch64
- uses: actions/upload-artifact@v4
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4641c7878fc1..7076c1571b18 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -532,7 +532,7 @@ we used in our CI systems more closely.
### Snapshot Testing for Visualizations
-If you are working on code that makes changes to any matplotlib visualisations
+If you are working on code that makes changes to any matplotlib visualizations
you will need to check that your changes don't break any snapshot tests, and add
new tests where necessary. You can do this as follows:
@@ -543,7 +543,7 @@ the snapshot tests (note this may take some time to finish loading).
3. Each test result provides a set of 3 images (left: reference image, middle: your test result, right: differences). In the list of tests the passed tests are collapsed and failed tests are expanded. If a test fails, you will see a situation like this:
-4. Fix any broken tests. Working on code for one aspect of the visualisations
+4. Fix any broken tests. Working on code for one aspect of the visualizations
can sometimes result in minor changes elsewhere to spacing etc. In these cases
you just need to update the reference images as follows:
- download the mismatched images (link at top of Jupyter Notebook output)
diff --git a/Cargo.lock b/Cargo.lock
index 13a66e1b25e7..31ebaf2e9a6c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -307,9 +307,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "faer"
-version = "0.19.0"
+version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ef9e1a4098a9e3a03c47bc5061406c04820552d869fd0fcd92587d07b271f0"
+checksum = "41543c4de4bfb32efdffdd75cbcca5ef41b800e8a811ea4a41fb9393c6ef3bc0"
dependencies = [
"bytemuck",
"coe-rs",
@@ -785,9 +785,9 @@ dependencies = [
[[package]]
name = "num-bigint"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@@ -1180,6 +1180,7 @@ dependencies = [
"rayon",
"rustworkx-core",
"smallvec",
+ "thiserror",
]
[[package]]
@@ -1195,6 +1196,7 @@ dependencies = [
"pyo3",
"rustworkx-core",
"smallvec",
+ "thiserror",
]
[[package]]
@@ -1527,18 +1529,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
[[package]]
name = "thiserror"
-version = "1.0.59"
+version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
+checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.59"
+version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
+checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index 10b3bcf4a254..63851a44d10b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ num-complex = "0.4"
ndarray = "^0.15.6"
numpy = "0.21.0"
smallvec = "1.13"
+thiserror = "1.0"
# 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
diff --git a/README.md b/README.md
index b84e8107f762..babf5756f5de 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,8 @@
**Qiskit** is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
-This library is the core component of Qiskit, which contains the building blocks for creating and working with quantum circuits, quantum operators, and primitive functions (sampler and estimator).
-It also contains a transpiler that supports optimizing quantum circuits and a quantum information toolbox for creating advanced quantum operators.
+This library is the core component of Qiskit, which contains the building blocks for creating and working with quantum circuits, quantum operators, and primitive functions (Sampler and Estimator).
+It also contains a transpiler that supports optimizing quantum circuits, and a quantum information toolbox for creating advanced operators.
For more details on how to use Qiskit, refer to the documentation located here:
@@ -91,12 +91,12 @@ print(f" > Expectation values: {result.values}")
Running this will give the outcome `4`. For fun, try to assign a value of +/- 1 to each single-qubit operator X and Y
and see if you can achieve this outcome. (Spoiler alert: this is not possible!)
-Using the Qiskit-provided `qiskit.primitives.Sampler` and `qiskit.primitives.Estimator` will not take you very far. The power of quantum computing cannot be simulated
-on classical computers and you need to use real quantum hardware to scale to larger quantum circuits. However, running a quantum
-circuit on hardware requires rewriting them to the basis gates and connectivity of the quantum hardware.
-The tool that does this is the [transpiler](https://docs.quantum.ibm.com/api/qiskit/transpiler)
-and Qiskit includes transpiler passes for synthesis, optimization, mapping, and scheduling. However, it also includes a
-default compiler which works very well in most examples. The following code will map the example circuit to the `basis_gates = ['cz', 'sx', 'rz']` and a linear chain of qubits $0 \rightarrow 1 \rightarrow 2$ with the `coupling_map =[[0, 1], [1, 2]]`.
+Using the Qiskit-provided `qiskit.primitives.Sampler` and `qiskit.primitives.Estimator` will not take you very far.
+The power of quantum computing cannot be simulated on classical computers and you need to use real quantum hardware to scale to larger quantum circuits.
+However, running a quantum circuit on hardware requires rewriting to the basis gates and connectivity of the quantum hardware.
+The tool that does this is the [transpiler](https://docs.quantum.ibm.com/api/qiskit/transpiler), and Qiskit includes transpiler passes for synthesis, optimization, mapping, and scheduling.
+However, it also includes a default compiler, which works very well in most examples.
+The following code will map the example circuit to the `basis_gates = ['cz', 'sx', 'rz']` and a linear chain of qubits $0 \rightarrow 1 \rightarrow 2$ with the `coupling_map =[[0, 1], [1, 2]]`.
```python
from qiskit import transpile
diff --git a/constraints.txt b/constraints.txt
index d3985581d362..6681de226d93 100644
--- a/constraints.txt
+++ b/constraints.txt
@@ -3,6 +3,10 @@
# https://github.com/Qiskit/qiskit-terra/issues/10345 for current details.
scipy<1.11; python_version<'3.12'
+# Temporary pin to avoid CI issues caused by scipy 1.14.0
+# See https://github.com/Qiskit/qiskit/issues/12655 for current details.
+scipy==1.13.1; python_version=='3.12'
+
# z3-solver from 4.12.3 onwards upped the minimum macOS API version for its
# wheels to 11.7. The Azure VM images contain pre-built CPythons, of which at
# least CPython 3.8 was compiled for an older macOS, so does not match a
diff --git a/crates/README.md b/crates/README.md
index cbe58afa07d1..d72247bc61de 100644
--- a/crates/README.md
+++ b/crates/README.md
@@ -29,11 +29,11 @@ This would be a particular problem for defining the circuit object and using it
## Developer notes
-### Beware of initialisation order
+### Beware of initialization order
-The Qiskit C extension `qiskit._accelerate` needs to be initialised in a single go.
-It is the lowest part of the Python package stack, so it cannot rely on importing other parts of the Python library at initialisation time (except for exceptions through PyO3's `import_exception!` mechanism).
-This is because, unlike pure-Python modules, the initialisation of `_accelerate` cannot be done partially, and many components of Qiskit import their accelerators from `_accelerate`.
+The Qiskit C extension `qiskit._accelerate` needs to be initialized in a single go.
+It is the lowest part of the Python package stack, so it cannot rely on importing other parts of the Python library at initialization time (except for exceptions through PyO3's `import_exception!` mechanism).
+This is because, unlike pure-Python modules, the initialization of `_accelerate` cannot be done partially, and many components of Qiskit import their accelerators from `_accelerate`.
In general, this should not be too onerous a requirement, but if you violate it, you might see Rust panics on import, and PyO3 should wrap that up into an exception.
You might be able to track down the Rust source of the import cycle by running the import with the environment variable `RUST_BACKTRACE=full`.
diff --git a/crates/accelerate/Cargo.toml b/crates/accelerate/Cargo.toml
index 578dc1eaa6c7..6d84978c0d04 100644
--- a/crates/accelerate/Cargo.toml
+++ b/crates/accelerate/Cargo.toml
@@ -20,9 +20,10 @@ num-traits = "0.2"
num-complex.workspace = true
num-bigint = "0.4"
rustworkx-core = { git = "https://github.com/Qiskit/rustworkx.git" }
-faer = "0.19.0"
+faer = "0.19.1"
itertools = "0.13.0"
qiskit-circuit.workspace = true
+thiserror.workspace = true
[dependencies.smallvec]
workspace = true
diff --git a/crates/accelerate/src/convert_2q_block_matrix.rs b/crates/accelerate/src/convert_2q_block_matrix.rs
index e311c129b11b..7a9165777dc9 100644
--- a/crates/accelerate/src/convert_2q_block_matrix.rs
+++ b/crates/accelerate/src/convert_2q_block_matrix.rs
@@ -10,7 +10,9 @@
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.
+use pyo3::intern;
use pyo3::prelude::*;
+use pyo3::types::PyDict;
use pyo3::wrap_pyfunction;
use pyo3::Python;
@@ -20,35 +22,84 @@ use numpy::ndarray::{aview2, Array2, ArrayView2};
use numpy::{IntoPyArray, PyArray2, PyReadonlyArray2};
use smallvec::SmallVec;
-static ONE_QUBIT_IDENTITY: [[Complex64; 2]; 2] = [
- [Complex64::new(1., 0.), Complex64::new(0., 0.)],
- [Complex64::new(0., 0.), Complex64::new(1., 0.)],
-];
+use qiskit_circuit::bit_data::BitData;
+use qiskit_circuit::circuit_instruction::{operation_type_to_py, CircuitInstruction};
+use qiskit_circuit::dag_node::DAGOpNode;
+use qiskit_circuit::gate_matrix::ONE_QUBIT_IDENTITY;
+use qiskit_circuit::imports::QI_OPERATOR;
+use qiskit_circuit::operations::{Operation, OperationType};
+
+use crate::QiskitError;
+
+fn get_matrix_from_inst<'py>(
+ py: Python<'py>,
+ inst: &'py CircuitInstruction,
+) -> PyResult> {
+ match inst.operation.matrix(&inst.params) {
+ Some(mat) => Ok(mat),
+ None => match inst.operation {
+ OperationType::Standard(_) => Err(QiskitError::new_err(
+ "Parameterized gates can't be consolidated",
+ )),
+ OperationType::Gate(_) => Ok(QI_OPERATOR
+ .get_bound(py)
+ .call1((operation_type_to_py(py, inst)?,))?
+ .getattr(intern!(py, "data"))?
+ .extract::>()?
+ .as_array()
+ .to_owned()),
+ _ => unreachable!("Only called for unitary ops"),
+ },
+ }
+}
/// Return the matrix Operator resulting from a block of Instructions.
#[pyfunction]
#[pyo3(text_signature = "(op_list, /")]
pub fn blocks_to_matrix(
py: Python,
- op_list: Vec<(PyReadonlyArray2, SmallVec<[u8; 2]>)>,
+ op_list: Vec>,
+ block_index_map_dict: &Bound,
) -> PyResult>> {
+ // Build a BitData in block_index_map_dict order. block_index_map_dict is a dict of bits to
+ // indices mapping the order of the qargs in the block. There should only be 2 entries since
+ // there are only 2 qargs here (e.g. `{Qubit(): 0, Qubit(): 1}`) so we need to ensure that
+ // we added the qubits to bit data in the correct index order.
+ let mut index_map: Vec = (0..block_index_map_dict.len()).map(|_| py.None()).collect();
+ for bit_tuple in block_index_map_dict.items() {
+ let (bit, index): (PyObject, usize) = bit_tuple.extract()?;
+ index_map[index] = bit;
+ }
+ let mut bit_map: BitData = BitData::new(py, "qargs".to_string());
+ for bit in index_map {
+ bit_map.add(py, bit.bind(py), true)?;
+ }
let identity = aview2(&ONE_QUBIT_IDENTITY);
- let input_matrix = op_list[0].0.as_array();
- let mut matrix: Array2 = match op_list[0].1.as_slice() {
+ let first_node = &op_list[0];
+ let input_matrix = get_matrix_from_inst(py, &first_node.instruction)?;
+ let mut matrix: Array2 = match bit_map
+ .map_bits(first_node.instruction.qubits.bind(py).iter())?
+ .collect::>()
+ .as_slice()
+ {
[0] => kron(&identity, &input_matrix),
[1] => kron(&input_matrix, &identity),
- [0, 1] => input_matrix.to_owned(),
- [1, 0] => change_basis(input_matrix),
+ [0, 1] => input_matrix,
+ [1, 0] => change_basis(input_matrix.view()),
[] => Array2::eye(4),
_ => unreachable!(),
};
- for (op_matrix, q_list) in op_list.into_iter().skip(1) {
- let op_matrix = op_matrix.as_array();
+ for node in op_list.into_iter().skip(1) {
+ let op_matrix = get_matrix_from_inst(py, &node.instruction)?;
+ let q_list = bit_map
+ .map_bits(node.instruction.qubits.bind(py).iter())?
+ .map(|x| x as u8)
+ .collect::>();
let result = match q_list.as_slice() {
[0] => Some(kron(&identity, &op_matrix)),
[1] => Some(kron(&op_matrix, &identity)),
- [1, 0] => Some(change_basis(op_matrix)),
+ [1, 0] => Some(change_basis(op_matrix.view())),
[] => Some(Array2::eye(4)),
_ => None,
};
@@ -74,8 +125,42 @@ pub fn change_basis(matrix: ArrayView2) -> Array2 {
trans_matrix
}
+#[pyfunction]
+pub fn collect_2q_blocks_filter(node: &Bound) -> Option {
+ match node.downcast::() {
+ Ok(bound_node) => {
+ let node = bound_node.borrow();
+ match &node.instruction.operation {
+ OperationType::Standard(gate) => Some(
+ gate.num_qubits() <= 2
+ && node
+ .instruction
+ .extra_attrs
+ .as_ref()
+ .and_then(|attrs| attrs.condition.as_ref())
+ .is_none()
+ && !node.is_parameterized(),
+ ),
+ OperationType::Gate(gate) => Some(
+ gate.num_qubits() <= 2
+ && node
+ .instruction
+ .extra_attrs
+ .as_ref()
+ .and_then(|attrs| attrs.condition.as_ref())
+ .is_none()
+ && !node.is_parameterized(),
+ ),
+ _ => Some(false),
+ }
+ }
+ Err(_) => None,
+ }
+}
+
#[pymodule]
pub fn convert_2q_block_matrix(m: &Bound) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(blocks_to_matrix))?;
+ m.add_wrapped(wrap_pyfunction!(collect_2q_blocks_filter))?;
Ok(())
}
diff --git a/crates/accelerate/src/dense_layout.rs b/crates/accelerate/src/dense_layout.rs
index 901a906d9c81..9529742d7e62 100644
--- a/crates/accelerate/src/dense_layout.rs
+++ b/crates/accelerate/src/dense_layout.rs
@@ -197,7 +197,7 @@ pub fn best_subset_inner(
SubsetResult {
count: 0,
map: Vec::new(),
- error: std::f64::INFINITY,
+ error: f64::INFINITY,
subgraph: Vec::new(),
}
};
diff --git a/crates/accelerate/src/euler_one_qubit_decomposer.rs b/crates/accelerate/src/euler_one_qubit_decomposer.rs
index 1fd5fd7834ff..24c4f6e87c2a 100644
--- a/crates/accelerate/src/euler_one_qubit_decomposer.rs
+++ b/crates/accelerate/src/euler_one_qubit_decomposer.rs
@@ -18,12 +18,11 @@ use num_complex::{Complex64, ComplexFloat};
use smallvec::{smallvec, SmallVec};
use std::cmp::Ordering;
use std::f64::consts::PI;
-use std::ops::Deref;
use std::str::FromStr;
-use pyo3::exceptions::{PyIndexError, PyValueError};
+use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
-use pyo3::types::PyString;
+use pyo3::types::{PyList, PyString};
use pyo3::wrap_pyfunction;
use pyo3::Python;
@@ -31,7 +30,12 @@ use ndarray::prelude::*;
use numpy::PyReadonlyArray2;
use pyo3::pybacked::PyBackedStr;
-use qiskit_circuit::SliceOrInt;
+use qiskit_circuit::circuit_data::CircuitData;
+use qiskit_circuit::dag_node::DAGOpNode;
+use qiskit_circuit::operations::{Operation, Param, StandardGate};
+use qiskit_circuit::slice::{PySequenceIndex, SequenceIndex};
+use qiskit_circuit::util::c64;
+use qiskit_circuit::Qubit;
pub const ANGLE_ZERO_EPSILON: f64 = 1e-12;
@@ -67,12 +71,12 @@ impl OneQubitGateErrorMap {
#[pyclass(sequence)]
pub struct OneQubitGateSequence {
- pub gates: Vec<(String, SmallVec<[f64; 3]>)>,
+ pub gates: Vec<(StandardGate, SmallVec<[f64; 3]>)>,
#[pyo3(get)]
pub global_phase: f64,
}
-type OneQubitGateSequenceState = (Vec<(String, SmallVec<[f64; 3]>)>, f64);
+type OneQubitGateSequenceState = (Vec<(StandardGate, SmallVec<[f64; 3]>)>, f64);
#[pymethods]
impl OneQubitGateSequence {
@@ -96,46 +100,15 @@ impl OneQubitGateSequence {
Ok(self.gates.len())
}
- fn __getitem__(&self, py: Python, idx: SliceOrInt) -> PyResult {
- match idx {
- SliceOrInt::Slice(slc) => {
- let len = self.gates.len().try_into().unwrap();
- let indices = slc.indices(len)?;
- let mut out_vec: Vec<(String, SmallVec<[f64; 3]>)> = Vec::new();
- // Start and stop will always be positive the slice api converts
- // negatives to the index for example:
- // list(range(5))[-1:-3:-1]
- // will return start=4, stop=2, and step=-1
- let mut pos: isize = indices.start;
- let mut cond = if indices.step < 0 {
- pos > indices.stop
- } else {
- pos < indices.stop
- };
- while cond {
- if pos < len as isize {
- out_vec.push(self.gates[pos as usize].clone());
- }
- pos += indices.step;
- if indices.step < 0 {
- cond = pos > indices.stop;
- } else {
- cond = pos < indices.stop;
- }
- }
- Ok(out_vec.into_py(py))
- }
- SliceOrInt::Int(idx) => {
- let len = self.gates.len() as isize;
- if idx >= len || idx < -len {
- Err(PyIndexError::new_err(format!("Invalid index, {idx}")))
- } else if idx < 0 {
- let len = self.gates.len();
- Ok(self.gates[len - idx.unsigned_abs()].to_object(py))
- } else {
- Ok(self.gates[idx as usize].to_object(py))
- }
- }
+ fn __getitem__(&self, py: Python, idx: PySequenceIndex) -> PyResult {
+ match idx.with_len(self.gates.len())? {
+ SequenceIndex::Int(idx) => Ok(self.gates[idx].to_object(py)),
+ indices => Ok(PyList::new_bound(
+ py,
+ indices.iter().map(|pos| self.gates[pos].to_object(py)),
+ )
+ .into_any()
+ .unbind()),
}
}
}
@@ -145,15 +118,15 @@ fn circuit_kak(
phi: f64,
lam: f64,
phase: f64,
- k_gate: &str,
- a_gate: &str,
+ k_gate: StandardGate,
+ a_gate: StandardGate,
simplify: bool,
atol: Option,
) -> OneQubitGateSequence {
let mut lam = lam;
let mut theta = theta;
let mut phi = phi;
- let mut circuit: Vec<(String, SmallVec<[f64; 3]>)> = Vec::with_capacity(3);
+ let mut circuit: Vec<(StandardGate, SmallVec<[f64; 3]>)> = Vec::with_capacity(3);
let mut atol = match atol {
Some(atol) => atol,
None => ANGLE_ZERO_EPSILON,
@@ -169,7 +142,7 @@ fn circuit_kak(
// slippage coming from _mod_2pi injecting multiples of 2pi.
lam = mod_2pi(lam, atol);
if lam.abs() > atol {
- circuit.push((String::from(k_gate), smallvec![lam]));
+ circuit.push((k_gate, smallvec![lam]));
global_phase += lam / 2.;
}
return OneQubitGateSequence {
@@ -190,13 +163,13 @@ fn circuit_kak(
lam = mod_2pi(lam, atol);
if lam.abs() > atol {
global_phase += lam / 2.;
- circuit.push((String::from(k_gate), smallvec![lam]));
+ circuit.push((k_gate, smallvec![lam]));
}
- circuit.push((String::from(a_gate), smallvec![theta]));
+ circuit.push((a_gate, smallvec![theta]));
phi = mod_2pi(phi, atol);
if phi.abs() > atol {
global_phase += phi / 2.;
- circuit.push((String::from(k_gate), smallvec![phi]));
+ circuit.push((k_gate, smallvec![phi]));
}
OneQubitGateSequence {
gates: circuit,
@@ -220,7 +193,7 @@ fn circuit_u3(
let phi = mod_2pi(phi, atol);
let lam = mod_2pi(lam, atol);
if !simplify || theta.abs() > atol || phi.abs() > atol || lam.abs() > atol {
- circuit.push((String::from("u3"), smallvec![theta, phi, lam]));
+ circuit.push((StandardGate::U3Gate, smallvec![theta, phi, lam]));
}
OneQubitGateSequence {
gates: circuit,
@@ -247,16 +220,16 @@ fn circuit_u321(
if theta.abs() < atol {
let tot = mod_2pi(phi + lam, atol);
if tot.abs() > atol {
- circuit.push((String::from("u1"), smallvec![tot]));
+ circuit.push((StandardGate::U1Gate, smallvec![tot]));
}
} else if (theta - PI / 2.).abs() < atol {
circuit.push((
- String::from("u2"),
+ StandardGate::U2Gate,
smallvec![mod_2pi(phi, atol), mod_2pi(lam, atol)],
));
} else {
circuit.push((
- String::from("u3"),
+ StandardGate::U3Gate,
smallvec![theta, mod_2pi(phi, atol), mod_2pi(lam, atol)],
));
}
@@ -285,7 +258,7 @@ fn circuit_u(
let phi = mod_2pi(phi, atol);
let lam = mod_2pi(lam, atol);
if theta.abs() > atol || phi.abs() > atol || lam.abs() > atol {
- circuit.push((String::from("u"), smallvec![theta, phi, lam]));
+ circuit.push((StandardGate::UGate, smallvec![theta, phi, lam]));
}
OneQubitGateSequence {
gates: circuit,
@@ -388,7 +361,7 @@ fn circuit_rr(
// This can be expressed as a single R gate
if theta.abs() > atol {
circuit.push((
- String::from("r"),
+ StandardGate::RGate,
smallvec![theta, mod_2pi(PI / 2. + phi, atol)],
));
}
@@ -396,12 +369,12 @@ fn circuit_rr(
// General case: use two R gates
if (theta - PI).abs() > atol {
circuit.push((
- String::from("r"),
+ StandardGate::RGate,
smallvec![theta - PI, mod_2pi(PI / 2. - lam, atol)],
));
}
circuit.push((
- String::from("r"),
+ StandardGate::RGate,
smallvec![PI, mod_2pi(0.5 * (phi - lam + PI), atol)],
));
}
@@ -423,10 +396,46 @@ pub fn generate_circuit(
atol: Option,
) -> PyResult {
let res = match target_basis {
- EulerBasis::ZYZ => circuit_kak(theta, phi, lam, phase, "rz", "ry", simplify, atol),
- EulerBasis::ZXZ => circuit_kak(theta, phi, lam, phase, "rz", "rx", simplify, atol),
- EulerBasis::XZX => circuit_kak(theta, phi, lam, phase, "rx", "rz", simplify, atol),
- EulerBasis::XYX => circuit_kak(theta, phi, lam, phase, "rx", "ry", simplify, atol),
+ EulerBasis::ZYZ => circuit_kak(
+ theta,
+ phi,
+ lam,
+ phase,
+ StandardGate::RZGate,
+ StandardGate::RYGate,
+ simplify,
+ atol,
+ ),
+ EulerBasis::ZXZ => circuit_kak(
+ theta,
+ phi,
+ lam,
+ phase,
+ StandardGate::RZGate,
+ StandardGate::RXGate,
+ simplify,
+ atol,
+ ),
+ EulerBasis::XZX => circuit_kak(
+ theta,
+ phi,
+ lam,
+ phase,
+ StandardGate::RXGate,
+ StandardGate::RZGate,
+ simplify,
+ atol,
+ ),
+ EulerBasis::XYX => circuit_kak(
+ theta,
+ phi,
+ lam,
+ phase,
+ StandardGate::RXGate,
+ StandardGate::RYGate,
+ simplify,
+ atol,
+ ),
EulerBasis::U3 => circuit_u3(theta, phi, lam, phase, simplify, atol),
EulerBasis::U321 => circuit_u321(theta, phi, lam, phase, simplify, atol),
EulerBasis::U => circuit_u(theta, phi, lam, phase, simplify, atol),
@@ -441,11 +450,13 @@ pub fn generate_circuit(
let fnz = |circuit: &mut OneQubitGateSequence, phi: f64| {
let phi = mod_2pi(phi, inner_atol);
if phi.abs() > inner_atol {
- circuit.gates.push((String::from("p"), smallvec![phi]));
+ circuit
+ .gates
+ .push((StandardGate::PhaseGate, smallvec![phi]));
}
};
let fnx = |circuit: &mut OneQubitGateSequence| {
- circuit.gates.push((String::from("sx"), SmallVec::new()));
+ circuit.gates.push((StandardGate::SXGate, SmallVec::new()));
};
circuit_psx_gen(
@@ -471,12 +482,12 @@ pub fn generate_circuit(
let fnz = |circuit: &mut OneQubitGateSequence, phi: f64| {
let phi = mod_2pi(phi, inner_atol);
if phi.abs() > inner_atol {
- circuit.gates.push((String::from("rz"), smallvec![phi]));
+ circuit.gates.push((StandardGate::RZGate, smallvec![phi]));
circuit.global_phase += phi / 2.;
}
};
let fnx = |circuit: &mut OneQubitGateSequence| {
- circuit.gates.push((String::from("sx"), SmallVec::new()));
+ circuit.gates.push((StandardGate::SXGate, SmallVec::new()));
};
circuit_psx_gen(
theta,
@@ -501,12 +512,14 @@ pub fn generate_circuit(
let fnz = |circuit: &mut OneQubitGateSequence, phi: f64| {
let phi = mod_2pi(phi, inner_atol);
if phi.abs() > inner_atol {
- circuit.gates.push((String::from("u1"), smallvec![phi]));
+ circuit.gates.push((StandardGate::U1Gate, smallvec![phi]));
}
};
let fnx = |circuit: &mut OneQubitGateSequence| {
circuit.global_phase += PI / 4.;
- circuit.gates.push((String::from("rx"), smallvec![PI / 2.]));
+ circuit
+ .gates
+ .push((StandardGate::RXGate, smallvec![PI / 2.]));
};
circuit_psx_gen(
theta,
@@ -531,15 +544,15 @@ pub fn generate_circuit(
let fnz = |circuit: &mut OneQubitGateSequence, phi: f64| {
let phi = mod_2pi(phi, inner_atol);
if phi.abs() > inner_atol {
- circuit.gates.push((String::from("rz"), smallvec![phi]));
+ circuit.gates.push((StandardGate::RZGate, smallvec![phi]));
circuit.global_phase += phi / 2.;
}
};
let fnx = |circuit: &mut OneQubitGateSequence| {
- circuit.gates.push((String::from("sx"), SmallVec::new()));
+ circuit.gates.push((StandardGate::SXGate, SmallVec::new()));
};
let fnxpi = |circuit: &mut OneQubitGateSequence| {
- circuit.gates.push((String::from("x"), SmallVec::new()));
+ circuit.gates.push((StandardGate::XGate, SmallVec::new()));
};
circuit_psx_gen(
theta,
@@ -663,7 +676,7 @@ fn compare_error_fn(
let fidelity_product: f64 = circuit
.gates
.iter()
- .map(|x| 1. - err_map.get(&x.0).unwrap_or(&0.))
+ .map(|gate| 1. - err_map.get(gate.0.name()).unwrap_or(&0.))
.product();
(1. - fidelity_product, circuit.gates.len())
}
@@ -672,6 +685,28 @@ fn compare_error_fn(
}
fn compute_error(
+ gates: &[(StandardGate, SmallVec<[f64; 3]>)],
+ error_map: Option<&OneQubitGateErrorMap>,
+ qubit: usize,
+) -> (f64, usize) {
+ match error_map {
+ Some(err_map) => {
+ let num_gates = gates.len();
+ let gate_fidelities: f64 = gates
+ .iter()
+ .map(|gate| 1. - err_map.error_map[qubit].get(gate.0.name()).unwrap_or(&0.))
+ .product();
+ (1. - gate_fidelities, num_gates)
+ }
+ None => (gates.len() as f64, gates.len()),
+ }
+}
+
+fn compute_error_term(gate: &str, error_map: &OneQubitGateErrorMap, qubit: usize) -> f64 {
+ 1. - error_map.error_map[qubit].get(gate).unwrap_or(&0.)
+}
+
+fn compute_error_str(
gates: &[(String, SmallVec<[f64; 3]>)],
error_map: Option<&OneQubitGateErrorMap>,
qubit: usize,
@@ -681,7 +716,7 @@ fn compute_error(
let num_gates = gates.len();
let gate_fidelities: f64 = gates
.iter()
- .map(|x| 1. - err_map.error_map[qubit].get(&x.0).unwrap_or(&0.))
+ .map(|gate| compute_error_term(gate.0.as_str(), err_map, qubit))
.product();
(1. - gate_fidelities, num_gates)
}
@@ -700,11 +735,20 @@ pub fn compute_error_one_qubit_sequence(
#[pyfunction]
pub fn compute_error_list(
- circuit: Vec<(String, SmallVec<[f64; 3]>)>,
+ circuit: Vec>,
qubit: usize,
error_map: Option<&OneQubitGateErrorMap>,
) -> (f64, usize) {
- compute_error(&circuit, error_map, qubit)
+ let circuit_list: Vec<(String, SmallVec<[f64; 3]>)> = circuit
+ .iter()
+ .map(|node| {
+ (
+ node.instruction.operation.name().to_string(),
+ smallvec![], // Params not needed in this path
+ )
+ })
+ .collect();
+ compute_error_str(&circuit_list, error_map, qubit)
}
#[pyfunction]
@@ -717,15 +761,13 @@ pub fn unitary_to_gate_sequence(
simplify: bool,
atol: Option,
) -> PyResult