Skip to content

Commit

Permalink
Merge remote-tracking branch 'ibm/main' into repack-instruction
Browse files Browse the repository at this point in the history
This was a highly nontrivial merge, since bringing in more control-flow
operations and allowing them to be cast down to standard gates found
some problems in the model (both pre-existing and new for this PR).
  • Loading branch information
jakelishman committed Jul 9, 2024
2 parents a6cd6bb + 4867e8a commit c4f299f
Show file tree
Hide file tree
Showing 28 changed files with 1,480 additions and 529 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
with:
components: llvm-tools-preview
- name: Build wheels
uses: pypa/[email protected].1
uses: pypa/[email protected].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 ~/.'
Expand Down Expand Up @@ -58,7 +58,7 @@ jobs:
with:
components: llvm-tools-preview
- name: Build wheels
uses: pypa/[email protected].1
uses: pypa/[email protected].2
env:
CIBW_BEFORE_ALL: rustup target add aarch64-apple-darwin
CIBW_BUILD: cp38-macosx_universal2 cp38-macosx_arm64
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
with:
components: llvm-tools-preview
- name: Build wheels
uses: pypa/[email protected].1
uses: pypa/[email protected].2
env:
CIBW_SKIP: 'pp* cp36-* cp37-* *musllinux* *amd64 *x86_64'
- uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -133,7 +133,7 @@ jobs:
with:
platforms: all
- name: Build wheels
uses: pypa/[email protected].1
uses: pypa/[email protected].2
env:
CIBW_ARCHS_LINUX: s390x
CIBW_TEST_SKIP: "cp*"
Expand Down Expand Up @@ -167,7 +167,7 @@ jobs:
with:
platforms: all
- name: Build wheels
uses: pypa/[email protected].1
uses: pypa/[email protected].2
env:
CIBW_ARCHS_LINUX: ppc64le
CIBW_TEST_SKIP: "cp*"
Expand Down Expand Up @@ -201,7 +201,7 @@ jobs:
with:
platforms: all
- name: Build wheels
uses: pypa/[email protected].1
uses: pypa/[email protected].2
env:
CIBW_ARCHS_LINUX: aarch64
- uses: actions/upload-artifact@v4
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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
Expand Down
19 changes: 5 additions & 14 deletions crates/accelerate/src/synthesis/permutation/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use std::vec::Vec;

use qiskit_circuit::slice::{PySequenceIndex, PySequenceIndexError, SequenceIndex};
use qiskit_circuit::slice::PySequenceIndex;

pub fn validate_permutation(pattern: &ArrayView1<i64>) -> PyResult<()> {
let n = pattern.len();
Expand Down Expand Up @@ -120,11 +120,8 @@ pub fn pattern_to_cycles(pattern: &ArrayView1<usize>) -> Vec<Vec<usize>> {
/// Periodic (or Python-like) access to a vector.
/// Util used below in ``decompose_cycles``.
#[inline]
fn pget(vec: &[usize], index: isize) -> Result<usize, PySequenceIndexError> {
let SequenceIndex::Int(wrapped) = PySequenceIndex::Int(index).with_len(vec.len())? else {
unreachable!()
};
Ok(vec[wrapped])
fn pget(vec: &[usize], index: isize) -> usize {
vec[PySequenceIndex::convert_idx(index, vec.len()).unwrap()]
}

/// Given a disjoint cycle decomposition of a permutation pattern (see the function
Expand All @@ -138,16 +135,10 @@ pub fn decompose_cycles(cycles: &Vec<Vec<usize>>) -> Vec<(usize, usize)> {
let length = cycle.len() as isize;

for idx in 0..(length - 1) / 2 {
swaps.push((
pget(cycle, idx - 1).unwrap(),
pget(cycle, length - 3 - idx).unwrap(),
));
swaps.push((pget(cycle, idx - 1), pget(cycle, length - 3 - idx)));
}
for idx in 0..length / 2 {
swaps.push((
pget(cycle, idx - 1).unwrap(),
pget(cycle, length - 2 - idx).unwrap(),
));
swaps.push((pget(cycle, idx - 1), pget(cycle, length - 2 - idx)));
}
}

Expand Down
32 changes: 10 additions & 22 deletions crates/circuit/src/circuit_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,20 +511,7 @@ impl CircuitData {
});
}
} else {
// Clippy complains in some versions if you attempt to just feature-gate out the
// ref-cell setting line from the middle.
#[cfg(feature = "cache_pygates")]
{
res.data.extend(self.data.iter().map(|inst| {
let out = inst.clone();
*out.py_op.borrow_mut() = None;
out
}));
}
#[cfg(not(feature = "cache_pygates"))]
{
res.data.extend(self.data.iter().cloned());
}
res.data.extend(self.data.iter().cloned());
}
Ok(res)
}
Expand Down Expand Up @@ -715,14 +702,15 @@ impl CircuitData {
let inst = &self.data[index];
let qubits = self.qargs_interner.intern(inst.qubits);
let clbits = self.cargs_interner.intern(inst.clbits);
CircuitInstruction::new(
py,
inst.op.clone(),
self.qubits.map_indices(qubits.value),
self.clbits.map_indices(clbits.value),
inst.params_view().iter().cloned().collect(),
inst.extra_attrs.clone(),
)
CircuitInstruction {
operation: inst.op.clone(),
qubits: PyTuple::new_bound(py, self.qubits.map_indices(qubits.value)).unbind(),
clbits: PyTuple::new_bound(py, self.clbits.map_indices(clbits.value)).unbind(),
params: inst.params_view().iter().cloned().collect(),
extra_attrs: inst.extra_attrs.clone(),
#[cfg(feature = "cache_pygates")]
py_op: inst.py_op.clone(),
}
.into_py(py)
};
match index.with_len(self.data.len())? {
Expand Down
50 changes: 11 additions & 39 deletions crates/circuit/src/circuit_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ use pyo3::{intern, IntoPy, PyObject, PyResult};

use smallvec::SmallVec;

use crate::imports::{
GATE, INSTRUCTION, OPERATION, SINGLETON_CONTROLLED_GATE, SINGLETON_GATE, WARNINGS_WARN,
};
use crate::imports::{GATE, INSTRUCTION, OPERATION, WARNINGS_WARN};
use crate::operations::{
Operation, OperationRef, Param, PyGate, PyInstruction, PyOperation, StandardGate,
};
Expand Down Expand Up @@ -114,31 +112,6 @@ pub struct CircuitInstruction {
}

impl CircuitInstruction {
pub fn new<T1, T2, U1, U2>(
py: Python,
operation: PackedOperation,
qubits: impl IntoIterator<Item = T1, IntoIter = U1>,
clbits: impl IntoIterator<Item = T2, IntoIter = U2>,
params: SmallVec<[Param; 3]>,
extra_attrs: Option<Box<ExtraInstructionAttributes>>,
) -> Self
where
T1: ToPyObject,
T2: ToPyObject,
U1: ExactSizeIterator<Item = T1>,
U2: ExactSizeIterator<Item = T2>,
{
CircuitInstruction {
operation,
qubits: PyTuple::new_bound(py, qubits).unbind(),
clbits: PyTuple::new_bound(py, clbits).unbind(),
params,
extra_attrs,
#[cfg(feature = "cache_pygates")]
py_op: RefCell::new(None),
}
}

/// View the operation in this `CircuitInstruction`.
pub fn op(&self) -> OperationRef {
self.operation.view()
Expand Down Expand Up @@ -521,18 +494,17 @@ impl<'py> FromPyObject<'py> for OperationFromPython {
.and_then(|standard| standard.extract::<StandardGate>())
.ok() else { break 'standard };

// If the input instruction is a standard gate and a singleton instance
// we should check for mutable state. A mutable instance should be treated
// as a custom gate not a standard gate because it has custom properties.
// If the instruction is a controlled gate with a not-all-ones control state, it doesn't
// fit our definition of standard. We abuse the fact that we know our standard-gate
// mapping to avoid an `isinstance` check on `ControlledGate` - a standard gate has
// nonzero `num_ctrl_qubits` iff it is a `ControlledGate`.
//
// In the future we can revisit this when we've dropped `duration`, `unit`,
// and `condition` from the API, as we should own the label in the
// `CircuitInstruction`. The other piece here is for controlled gates there
// is the control state, so for `SingletonControlledGates` we'll still need
// this check.
if ob.getattr(intern!(py, "mutable"))?.is_truthy()?
&& (ob.is_instance(SINGLETON_GATE.get_bound(py))?
|| ob.is_instance(SINGLETON_CONTROLLED_GATE.get_bound(py))?)
// `ControlledGate` also has a `base_gate` attribute, and we don't track enough in Rust
// space to handle the case that that was mutated away from a standard gate.
if standard.num_ctrl_qubits() != 0
&& ((ob.getattr(intern!(py, "ctrl_state"))?.extract::<usize>()?
!= (1 << standard.num_ctrl_qubits()) - 1)
|| ob.getattr(intern!(py, "mutable"))?.extract()?)
{
break 'standard;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/circuit/src/dag_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ impl DAGOpNode {
self.instruction.extra_attrs = res.extra_attrs;
#[cfg(feature = "cache_pygates")]
{
*self.instruction.py_op.borrow_mut() = None;
*self.instruction.py_op.borrow_mut() = Some(op.into_py(op.py()));
}
Ok(())
}
Expand Down
Loading

0 comments on commit c4f299f

Please sign in to comment.