Skip to content

Commit

Permalink
Merge branch 'Qiskit:main' into port-synth_cnot_phase_aam-to-rust
Browse files Browse the repository at this point in the history
  • Loading branch information
MozammilQ authored Jan 13, 2025
2 parents cb2170c + 5757fa0 commit 2bdaaae
Show file tree
Hide file tree
Showing 234 changed files with 3,473 additions and 1,655 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.19"
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
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ we use `measure_all(inplace=False)` to get a copy of the circuit in which all th
qc_measured = qc_example.measure_all(inplace=False)

# 3. Execute using the Sampler primitive
from qiskit.primitives.sampler import Sampler
sampler = Sampler()
job = sampler.run(qc_measured, shots=1000)
from qiskit.primitives import StatevectorSampler
sampler = StatevectorSampler()
job = sampler.run([qc_measured], shots=1000)
result = job.result()
print(f" > Quasi probability distribution: {result.quasi_dists}")
print(f" > Counts: {result[0].data["meas"].get_counts()}")
```
Running this will give an outcome similar to `{0: 0.497, 7: 0.503}` which is `000` 50% of the time and `111` 50% of the time up to statistical fluctuations.
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.

```python
Expand All @@ -81,17 +81,17 @@ from qiskit.quantum_info import SparsePauliOp
operator = SparsePauliOp.from_list([("XXY", 1), ("XYX", 1), ("YXX", 1), ("YYY", -1)])

# 3. Execute using the Estimator primitive
from qiskit.primitives import Estimator
estimator = Estimator()
job = estimator.run(qc_example, operator, shots=1000)
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
job = estimator.run([(qc_example, operator)], precision=1e-3)
result = job.result()
print(f" > Expectation values: {result.values}")
print(f" > Expectation values: {result[0].data.evs}")
```

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.
Using the Qiskit-provided `qiskit.primitives.StatevectorSampler` and `qiskit.primitives.StatevectorEstimator` 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.
Expand All @@ -106,7 +106,7 @@ qc_transpiled = transpile(qc_example, basis_gates = ['cz', 'sx', 'rz'], coupling
### Executing your code on real quantum hardware

Qiskit provides an abstraction layer that lets users run quantum circuits on hardware from any vendor that provides a compatible interface.
The best way to use Qiskit is with a runtime environment that provides optimized implementations of `sampler` and `estimator` for a given hardware platform. This runtime may involve using pre- and post-processing, such as optimized transpiler passes with error suppression, error mitigation, and, eventually, error correction built in. A runtime implements `qiskit.primitives.BaseSampler` and `qiskit.primitives.BaseEstimator` interfaces. For example,
The best way to use Qiskit is with a runtime environment that provides optimized implementations of `sampler` and `estimator` for a given hardware platform. This runtime may involve using pre- and post-processing, such as optimized transpiler passes with error suppression, error mitigation, and, eventually, error correction built in. A runtime implements `qiskit.primitives.BaseSamplerV2` and `qiskit.primitives.BaseEstimatorV2` interfaces. For example,
some packages that provide implementations of a runtime primitive implementation are:

* https://github.com/Qiskit/qiskit-ibm-runtime
Expand Down Expand Up @@ -165,4 +165,4 @@ We acknowledge partial support for Qiskit development from the DOE Office of Sci

## License

[Apache License 2.0](LICENSE.txt)
[Apache License 2.0](LICENSE.txt)
9 changes: 4 additions & 5 deletions crates/accelerate/src/circuit_library/quantum_volume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use rayon::prelude::*;
use qiskit_circuit::circuit_data::CircuitData;
use qiskit_circuit::imports::UNITARY_GATE;
use qiskit_circuit::operations::Param;
use qiskit_circuit::operations::PyInstruction;
use qiskit_circuit::operations::PyGate;
use qiskit_circuit::packed_instruction::PackedOperation;
use qiskit_circuit::{Clbit, Qubit};
use smallvec::{smallvec, SmallVec};
Expand Down Expand Up @@ -127,17 +127,16 @@ pub fn quantum_volume(
let unitary_gate = UNITARY_GATE
.get_bound(py)
.call((unitary.clone(), py.None(), false), Some(&kwargs))?;
let instruction = PyInstruction {
let instruction = PyGate {
qubits: 2,
clbits: 0,
params: 1,
op_name: "unitary".to_string(),
control_flow: false,
instruction: unitary_gate.unbind(),
gate: unitary_gate.unbind(),
};
let qubit = layer_index * 2;
Ok((
PackedOperation::from_instruction(Box::new(instruction)),
PackedOperation::from_gate(Box::new(instruction)),
smallvec![Param::Obj(unitary.unbind().into())],
vec![permutation[qubit], permutation[qubit + 1]],
vec![],
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
5 changes: 3 additions & 2 deletions crates/accelerate/src/consolidate_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ const MAX_2Q_DEPTH: usize = 20;

#[allow(clippy::too_many_arguments)]
#[pyfunction]
#[pyo3(signature = (dag, decomposer, force_consolidate, target=None, basis_gates=None, blocks=None, runs=None))]
#[pyo3(signature = (dag, decomposer, basis_gate_name, force_consolidate, target=None, basis_gates=None, blocks=None, runs=None))]
pub(crate) fn consolidate_blocks(
py: Python,
dag: &mut DAGCircuit,
decomposer: &TwoQubitBasisDecomposer,
basis_gate_name: &str,
force_consolidate: bool,
target: Option<&Target>,
basis_gates: Option<HashSet<String>>,
Expand Down Expand Up @@ -125,7 +126,7 @@ pub(crate) fn consolidate_blocks(
let inst = dag.dag()[*node].unwrap_operation();
block_qargs.extend(dag.get_qargs(inst.qubits));
all_block_gates.insert(*node);
if inst.op.name() == decomposer.gate_name() {
if inst.op.name() == basis_gate_name {
basis_count += 1;
}
if !is_supported(
Expand Down
10 changes: 5 additions & 5 deletions crates/accelerate/src/rayon_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub struct ParUnevenChunksMut<'len, 'data, T> {
data: &'data mut [T],
}

impl<'len, 'data, T: Send + 'data> ParallelIterator for ParUnevenChunksMut<'len, 'data, T> {
impl<'data, T: Send + 'data> ParallelIterator for ParUnevenChunksMut<'_, 'data, T> {
type Item = &'data mut [T];

#[track_caller]
Expand All @@ -61,7 +61,7 @@ impl<'len, 'data, T: Send + 'data> ParallelIterator for ParUnevenChunksMut<'len,
}
}

impl<'len, 'data, T: Send + 'data> IndexedParallelIterator for ParUnevenChunksMut<'len, 'data, T> {
impl<'data, T: Send + 'data> IndexedParallelIterator for ParUnevenChunksMut<'_, 'data, T> {
#[track_caller]
fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
bridge(self, consumer)
Expand Down Expand Up @@ -132,7 +132,7 @@ impl<'len, 'data, T> UnevenChunksMutIter<'len, 'data, T> {
}
}

impl<'len, 'data, T> Iterator for UnevenChunksMutIter<'len, 'data, T> {
impl<'data, T> Iterator for UnevenChunksMutIter<'_, 'data, T> {
type Item = &'data mut [T];

#[track_caller]
Expand All @@ -154,8 +154,8 @@ impl<'len, 'data, T> Iterator for UnevenChunksMutIter<'len, 'data, T> {
(self.chunk_lengths.len(), Some(self.chunk_lengths.len()))
}
}
impl<'len, 'data, T> ExactSizeIterator for UnevenChunksMutIter<'len, 'data, T> {}
impl<'len, 'data, T> DoubleEndedIterator for UnevenChunksMutIter<'len, 'data, T> {
impl<T> ExactSizeIterator for UnevenChunksMutIter<'_, '_, T> {}
impl<T> DoubleEndedIterator for UnevenChunksMutIter<'_, '_, T> {
#[track_caller]
fn next_back(&mut self) -> Option<Self::Item> {
if self.chunk_lengths.is_empty() {
Expand Down
2 changes: 1 addition & 1 deletion crates/accelerate/src/sabre/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ struct RoutingState<'a, 'b> {
seed: u64,
}

impl<'a, 'b> RoutingState<'a, 'b> {
impl RoutingState<'_, '_> {
/// Apply a swap to the program-state structures (front layer, extended set and current
/// layout).
#[inline]
Expand Down
Loading

0 comments on commit 2bdaaae

Please sign in to comment.