Skip to content

Commit

Permalink
Encapsulate Python sequence-like indexers
Browse files Browse the repository at this point in the history
This encapsulates a lot of the common logic around Python sequence-like
indexers (`SliceOrInt`) into iterators that handle adapting negative
indices and slices in `usize` for containers of a given size.

These indexers now all implement `ExactSizeIterator` and
`DoubleEndedIterator`, so they can be used with all `Iterator` methods,
and can be used (with `Iterator::map` and friends) as inputs to
`PyList::new_bound`, which makes code simpler at all points of use.

The special-cased uses of this kind of thing from `CircuitData` are
replaced with the new forms.  This had no measurable impact on
performance on my machine, and removes a lot noise from error-handling
and highly specialised functions.
  • Loading branch information
jakelishman committed Jun 28, 2024
1 parent 3adcd5d commit dc20c20
Show file tree
Hide file tree
Showing 9 changed files with 517 additions and 296 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions crates/accelerate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ rustworkx-core = "0.14"
faer = "0.19.1"
itertools = "0.13.0"
qiskit-circuit.workspace = true
thiserror.workspace = true

[dependencies.smallvec]
workspace = true
Expand Down
55 changes: 12 additions & 43 deletions crates/accelerate/src/euler_one_qubit_decomposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ 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;

use ndarray::prelude::*;
use numpy::PyReadonlyArray2;
use pyo3::pybacked::PyBackedStr;

use qiskit_circuit::slice::{PySequenceIndex, SequenceIndex};
use qiskit_circuit::util::c64;
use qiskit_circuit::SliceOrInt;

pub const ANGLE_ZERO_EPSILON: f64 = 1e-12;

Expand Down Expand Up @@ -97,46 +97,15 @@ impl OneQubitGateSequence {
Ok(self.gates.len())
}

fn __getitem__(&self, py: Python, idx: SliceOrInt) -> PyResult<PyObject> {
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<PyObject> {
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()),
}
}
}
Expand Down
59 changes: 14 additions & 45 deletions crates/accelerate/src/two_qubit_decompose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@
use approx::{abs_diff_eq, relative_eq};
use num_complex::{Complex, Complex64, ComplexFloat};
use num_traits::Zero;
use pyo3::exceptions::{PyIndexError, PyValueError};
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use pyo3::Python;
use smallvec::{smallvec, SmallVec};
use std::f64::consts::{FRAC_1_SQRT_2, PI};
use std::ops::Deref;
Expand All @@ -37,7 +33,11 @@ use ndarray::prelude::*;
use ndarray::Zip;
use numpy::PyReadonlyArray2;
use numpy::{IntoPyArray, ToPyArray};

use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::pybacked::PyBackedStr;
use pyo3::types::PyList;

use crate::convert_2q_block_matrix::change_basis;
use crate::euler_one_qubit_decomposer::{
Expand All @@ -52,8 +52,8 @@ use rand_distr::StandardNormal;
use rand_pcg::Pcg64Mcg;

use qiskit_circuit::gate_matrix::{CX_GATE, H_GATE, ONE_QUBIT_IDENTITY, SX_GATE, X_GATE};
use qiskit_circuit::slice::{PySequenceIndex, SequenceIndex};
use qiskit_circuit::util::{c64, GateArray1Q, GateArray2Q, C_M_ONE, C_ONE, C_ZERO, IM, M_IM};
use qiskit_circuit::SliceOrInt;

const PI2: f64 = PI / 2.;
const PI4: f64 = PI / 4.;
Expand Down Expand Up @@ -1131,46 +1131,15 @@ impl TwoQubitGateSequence {
Ok(self.gates.len())
}

fn __getitem__(&self, py: Python, idx: SliceOrInt) -> PyResult<PyObject> {
match idx {
SliceOrInt::Slice(slc) => {
let len = self.gates.len().try_into().unwrap();
let indices = slc.indices(len)?;
let mut out_vec: TwoQubitSequenceVec = 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=-
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<PyObject> {
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()),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/circuit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ hashbrown.workspace = true
num-complex.workspace = true
ndarray.workspace = true
numpy.workspace = true
thiserror.workspace = true

[dependencies.pyo3]
workspace = true
Expand Down
Loading

0 comments on commit dc20c20

Please sign in to comment.