Skip to content

Commit

Permalink
Merge branch 'main' into two-qubit-peephole-parallel-pass
Browse files Browse the repository at this point in the history
  • Loading branch information
mtreinish authored Jan 13, 2025
2 parents f06a070 + 8d8da66 commit 90b16e8
Show file tree
Hide file tree
Showing 213 changed files with 3,174 additions and 1,853 deletions.
15 changes: 8 additions & 7 deletions .github/workflows/wheels-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ on:
python-version:
description: "The Python version to use to host the build runner."
type: string
default: "3.10"
default: "3.13"
required: false

pgo:
Expand All @@ -90,7 +90,7 @@ jobs:
os:
- ubuntu-latest
# Used for the x86_64 builds.
- macos-12
- macos-13
# Used for the ARM builds.
- macos-14
- windows-latest
Expand Down Expand Up @@ -121,12 +121,13 @@ jobs:
cat >>"$GITHUB_ENV" <<EOF
CIBW_BEFORE_BUILD=bash ./tools/build_pgo.sh $PGO_WORK_DIR $PGO_OUT_PATH
CIBW_ENVIRONMENT=RUSTUP_TOOLCHAIN=stable RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function'
CIBW_ENVIRONMENT_MACOS=MACOSX_DEPLOYMENT_TARGET='10.12' RUSTUP_TOOLCHAIN=stable RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function'
CIBW_ENVIRONMENT_LINUX=RUSTUP_TOOLCHAIN=stable RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function' PATH="\$PATH:\$HOME/.cargo/bin" CARGO_NET_GIT_FETCH_WITH_CLI="true"
EOF
env:
PGO_WORK_DIR: ${{ github.workspace }}/pgo-data
PGO_OUT_PATH: ${{ github.workspace }}/merged.profdata
- uses: pypa/cibuildwheel@v2.21.3
- uses: pypa/cibuildwheel@v2.22.0
- uses: actions/upload-artifact@v4
with:
path: ./wheelhouse/*.whl
Expand All @@ -151,7 +152,7 @@ jobs:
with:
components: llvm-tools-preview
- name: Build wheels
uses: pypa/cibuildwheel@v2.21.3
uses: pypa/cibuildwheel@v2.22.0
env:
CIBW_SKIP: 'pp* cp36-* cp37-* cp38-* *musllinux* *amd64 *x86_64'
- uses: actions/upload-artifact@v4
Expand All @@ -173,7 +174,7 @@ jobs:
- uses: docker/setup-qemu-action@v3
with:
platforms: all
- uses: pypa/cibuildwheel@v2.21.3
- uses: pypa/cibuildwheel@v2.22.0
env:
CIBW_ARCHS_LINUX: s390x
CIBW_TEST_SKIP: "cp*"
Expand All @@ -196,7 +197,7 @@ jobs:
- uses: docker/setup-qemu-action@v3
with:
platforms: all
- uses: pypa/cibuildwheel@v2.21.3
- uses: pypa/cibuildwheel@v2.22.0
env:
CIBW_ARCHS_LINUX: ppc64le
CIBW_TEST_SKIP: "cp*"
Expand All @@ -218,7 +219,7 @@ jobs:
- uses: docker/setup-qemu-action@v3
with:
platforms: all
- uses: pypa/cibuildwheel@v2.21.3
- uses: pypa/cibuildwheel@v2.22.0
env:
CIBW_ARCHS_LINUX: aarch64
CIBW_TEST_COMMAND: cp -r {project}/test . && QISKIT_PARALLEL=FALSE stestr --test-path test/python run --abbreviate -n test.python.compiler.test_transpiler
Expand Down
14 changes: 7 additions & 7 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ license = "Apache-2.0"
#
# Each crate can add on specific features freely as it inherits.
[workspace.dependencies]
bytemuck = "1.20"
indexmap.version = "2.6.0"
bytemuck = "1.21"
indexmap.version = "2.7.0"
hashbrown.version = "0.14.5"
num-bigint = "0.4"
num-complex = "0.4"
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ lint:
tools/verify_headers.py qiskit test tools
tools/find_optional_imports.py
tools/find_stray_release_notes.py
tools/verify_images.py

# Only pylint on files that have changed from origin/main. Also parallelize (disables cyclic-import check)
lint-incr:
-git fetch -q https://github.com/Qiskit/qiskit-terra.git :lint_incr_latest
tools/pylint_incr.py -j4 -rn -sn --paths :/qiskit/*.py :/test/*.py :/tools/*.py
tools/verify_headers.py qiskit test tools
tools/find_optional_imports.py
tools/verify_images.py

ruff:
ruff qiskit test tools setup.py
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ from qiskit.primitives import StatevectorSampler
sampler = StatevectorSampler()
job = sampler.run([qc_measured], shots=1000)
result = job.result()
print(f" > Counts: {result[0].meas.get_counts()}")
print(f" > Counts: {result[0].data["meas"].get_counts()}")
```
Running this will give an outcome similar to `{'000': 497, '111': 503}` which is `000` 50% of the time and `111` 50% of the time up to statistical fluctuations.
To illustrate the power of Estimator, we now use the quantum information toolbox to create the operator $XXY+XYX+YXX-YYY$ and pass it to the `run()` function, along with our quantum circuit. Note the Estimator requires a circuit _**without**_ measurement, so we use the `qc_example` circuit we created earlier.
Expand Down
48 changes: 29 additions & 19 deletions crates/accelerate/src/commutation_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,29 @@ use qiskit_circuit::circuit_instruction::{ExtraInstructionAttributes, OperationF
use qiskit_circuit::dag_node::DAGOpNode;
use qiskit_circuit::imports::QI_OPERATOR;
use qiskit_circuit::operations::OperationRef::{Gate as PyGateType, Operation as PyOperationType};
use qiskit_circuit::operations::{Operation, OperationRef, Param, StandardGate};
use qiskit_circuit::operations::{
get_standard_gate_names, Operation, OperationRef, Param, StandardGate,
};
use qiskit_circuit::{BitType, Clbit, Qubit};

use crate::unitary_compose;
use crate::QiskitError;

const TWOPI: f64 = 2.0 * std::f64::consts::PI;

// These gates do not commute with other gates, we do not check them.
static SKIPPED_NAMES: [&str; 4] = ["measure", "reset", "delay", "initialize"];
static NO_CACHE_NAMES: [&str; 2] = ["annotated", "linear_function"];

// We keep a hash-set of operations eligible for commutation checking. This is because checking
// eligibility is not for free.
static SUPPORTED_OP: Lazy<HashSet<&str>> = Lazy::new(|| {
HashSet::from([
"rxx", "ryy", "rzz", "rzx", "h", "x", "y", "z", "sx", "sxdg", "t", "tdg", "s", "sdg", "cx",
"cy", "cz", "swap", "iswap", "ecr", "ccx", "cswap",
])
});

const TWOPI: f64 = 2.0 * std::f64::consts::PI;

// map rotation gates to their generators, or to ``None`` if we cannot currently efficiently
// Map rotation gates to their generators, or to ``None`` if we cannot currently efficiently
// represent the generator in Rust and store the commutation relation in the commutation dictionary
static SUPPORTED_ROTATIONS: Lazy<HashMap<&str, Option<OperationRef>>> = Lazy::new(|| {
HashMap::from([
Expand Down Expand Up @@ -322,15 +327,17 @@ impl CommutationChecker {
(qargs1, qargs2)
};

let skip_cache: bool = NO_CACHE_NAMES.contains(&first_op.name()) ||
NO_CACHE_NAMES.contains(&second_op.name()) ||
// Skip params that do not evaluate to floats for caching and commutation library
first_params.iter().any(|p| !matches!(p, Param::Float(_))) ||
second_params.iter().any(|p| !matches!(p, Param::Float(_)))
&& !SUPPORTED_OP.contains(op1.name())
&& !SUPPORTED_OP.contains(op2.name());

if skip_cache {
// For our cache to work correctly, we require the gate's definition to only depend on the
// ``params`` attribute. This cannot be guaranteed for custom gates, so we only check
// the cache for our standard gates, which we know are defined by the ``params`` AND
// that the ``params`` are float-only at this point.
let whitelist = get_standard_gate_names();
let check_cache = whitelist.contains(&first_op.name())
&& whitelist.contains(&second_op.name())
&& first_params.iter().all(|p| matches!(p, Param::Float(_)))
&& second_params.iter().all(|p| matches!(p, Param::Float(_)));

if !check_cache {
return self.commute_matmul(
py,
first_op,
Expand Down Expand Up @@ -630,21 +637,24 @@ fn map_rotation<'a>(
) -> (&'a OperationRef<'a>, &'a [Param], bool) {
let name = op.name();
if let Some(generator) = SUPPORTED_ROTATIONS.get(name) {
// if the rotation angle is below the tolerance, the gate is assumed to
// If the rotation angle is below the tolerance, the gate is assumed to
// commute with everything, and we simply return the operation with the flag that
// it commutes trivially
// it commutes trivially.
if let Param::Float(angle) = params[0] {
if (angle % TWOPI).abs() < tol {
return (op, params, true);
};
};

// otherwise, we check if a generator is given -- if not, we'll just return the operation
// itself (e.g. RXX does not have a generator and is just stored in the commutations
// dictionary)
// Otherwise we need to cover two cases -- either a generator is given, in which case
// we return it, or we don't have a generator yet, but we know we have the operation
// stored in the commutation library. For example, RXX does not have a generator in Rust
// yet (PauliGate is not in Rust currently), but it is stored in the library, so we
// can strip the parameters and just return the gate.
if let Some(gate) = generator {
return (gate, &[], false);
};
return (op, &[], false);
}
(op, params, false)
}
Expand Down
8 changes: 3 additions & 5 deletions crates/accelerate/src/synthesis/clifford/random_clifford.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,15 @@ pub fn random_clifford_tableau_inner(num_qubits: usize, seed: Option<u64>) -> Ar

// Compute the full stabilizer tableau

// The code below is identical to the Python implementation, but is based on the original
// code in the paper.

// The code below is based on the original code in the referenced paper.
let mut table = Array2::from_elem((2 * num_qubits, 2 * num_qubits), false);

// Apply qubit permutation
for i in 0..num_qubits {
replace_row_inner(table.view_mut(), i, table2.slice(s![i, ..]));
replace_row_inner(table.view_mut(), i, table2.slice(s![perm[i], ..]));
replace_row_inner(
table.view_mut(),
perm[i] + num_qubits,
i + num_qubits,
table2.slice(s![perm[i] + num_qubits, ..]),
);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/accelerate/src/twirling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ fn generate_twirled_circuit(
custom_gate_map: Option<&CustomGateTwirlingMap>,
optimizer_target: Option<&Target>,
) -> PyResult<CircuitData> {
let mut out_circ = CircuitData::clone_empty_like(circ, None);
let mut out_circ = CircuitData::clone_empty_like(py, circ, None)?;

for inst in circ.data() {
if let Some(custom_gate_map) = custom_gate_map {
Expand Down
3 changes: 2 additions & 1 deletion crates/accelerate/src/unitary_synthesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ fn py_run_main_loop(
None,
None,
)?;
out_dag = synth_dag;
let out_qargs = dag.get_qargs(packed_instr.qubits);
apply_synth_dag(py, &mut out_dag, out_qargs, &synth_dag)?;
}
}
}
Expand Down
44 changes: 28 additions & 16 deletions crates/circuit/src/circuit_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ import_exception!(qiskit.circuit.exceptions, CircuitError);
///
/// For example,
///
/// .. code-block::
/// .. plot::
/// :include-source:
/// :no-figs:
///
/// qubits = [Qubit()]
/// data = CircuitData(qubits)
Expand Down Expand Up @@ -928,6 +930,7 @@ impl CircuitData {
instruction_iter.size_hint().0,
global_phase,
)?;

for item in instruction_iter {
let (operation, params, qargs, cargs) = item?;
let qubits = res.qargs_interner.insert_owned(qargs);
Expand Down Expand Up @@ -994,9 +997,13 @@ impl CircuitData {
qubits,
clbits,
param_table: ParameterTable::new(),
global_phase,
global_phase: Param::Float(0.0),
};

// use the global phase setter to ensure parameters are registered
// in the parameter table
res.set_global_phase(py, global_phase)?;

for inst in instruction_iter {
res.data.push(inst?);
res.track_instruction_parameters(py, res.data.len() - 1)?;
Expand Down Expand Up @@ -1038,6 +1045,7 @@ impl CircuitData {
instruction_iter.size_hint().0,
global_phase,
)?;

let no_clbit_index = res.cargs_interner.get_default();
for (operation, params, qargs) in instruction_iter {
let qubits = res.qargs_interner.insert(&qargs);
Expand Down Expand Up @@ -1071,8 +1079,13 @@ impl CircuitData {
qubits: BitData::new(py, "qubits".to_string()),
clbits: BitData::new(py, "clbits".to_string()),
param_table: ParameterTable::new(),
global_phase,
global_phase: Param::Float(0.0),
};

// use the global phase setter to ensure parameters are registered
// in the parameter table
res.set_global_phase(py, global_phase)?;

if num_qubits > 0 {
let qubit_cls = QUBIT.get_bound(py);
for _i in 0..num_qubits {
Expand Down Expand Up @@ -1379,15 +1392,12 @@ impl CircuitData {
#[cfg(feature = "cache_pygates")]
{
// Standard gates can all rebuild their definitions, so if the
// cached py_op exists, update the `params` attribute and clear out
// any existing cache.
if let Some(borrowed) = previous.py_op.get() {
borrowed
.bind(py)
.getattr(params_attr)?
.set_item(parameter, new_param)?;
borrowed.bind(py).setattr("_definition", py.None())?
}
// cached py_op exists, discard it to prompt the instruction
// to rebuild its cached python gate upon request later on. This is
// done to avoid an unintentional duplicated reference to the same gate
// instance in python. For more information, see
// https://github.com/Qiskit/qiskit/issues/13504
previous.py_op.take();
}
} else {
// Track user operations we've seen so we can rebind their definitions.
Expand Down Expand Up @@ -1526,16 +1536,18 @@ impl CircuitData {
/// * capacity - The capacity for instructions to use in the output `CircuitData`
/// If `None` the length of `other` will be used, if `Some` the integer
/// value will be used as the capacity.
pub fn clone_empty_like(other: &Self, capacity: Option<usize>) -> Self {
CircuitData {
pub fn clone_empty_like(py: Python, other: &Self, capacity: Option<usize>) -> PyResult<Self> {
let mut empty = CircuitData {
data: Vec::with_capacity(capacity.unwrap_or(other.data.len())),
qargs_interner: other.qargs_interner.clone(),
cargs_interner: other.cargs_interner.clone(),
qubits: other.qubits.clone(),
clbits: other.clbits.clone(),
param_table: ParameterTable::new(),
global_phase: other.global_phase.clone(),
}
global_phase: Param::Float(0.0),
};
empty.set_global_phase(py, other.global_phase.clone())?;
Ok(empty)
}

/// Append a PackedInstruction to the circuit data.
Expand Down
Loading

0 comments on commit 90b16e8

Please sign in to comment.