diff --git a/.azure/lint_docs_qpy-linux.yml b/.azure/lint_docs-linux.yml similarity index 61% rename from .azure/lint_docs_qpy-linux.yml rename to .azure/lint_docs-linux.yml index c08ea702ede5..b9e0c37892df 100644 --- a/.azure/lint_docs_qpy-linux.yml +++ b/.azure/lint_docs-linux.yml @@ -4,8 +4,8 @@ parameters: displayName: "Version of Python to use" jobs: - - job: 'Lint_Docs_QPY' - displayName: 'Lint, documentation and QPY' + - job: 'Lint_Docs' + displayName: 'Lint and documentation' pool: {vmImage: 'ubuntu-latest'} variables: @@ -43,24 +43,3 @@ jobs: artifactName: 'html_docs' Parallel: true ParallelCount: 8 - - - task: Cache@2 - inputs: - key: 'qpy | test/qpy_compat/test_qpy.py | "$(Build.BuildNumber)"' - restoreKeys: | - qpy | test/qpy_compat/test_qpy.py - path: qpy_files - displayName: Cache old QPY files - - - bash: | - set -e - # Reuse the docs environment to avoid needing to rebuild another - # version of Qiskit. - source .tox/docs/bin/activate - mv qpy_files/* test/qpy_compat || : - pushd test/qpy_compat - ./run_tests.sh - popd - mkdir -p qpy_files - mv test/qpy_compat/qpy_* qpy_files/. - displayName: 'Run QPY backwards compat tests' diff --git a/.github/workflows/qpy.yml b/.github/workflows/qpy.yml new file mode 100644 index 000000000000..2c127add907a --- /dev/null +++ b/.github/workflows/qpy.yml @@ -0,0 +1,39 @@ +name: QPY + +on: + push: + branches: + - 'main' + - 'stable/*' + pull_request: + merge_group: +concurrency: + group: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + backward_compat: + if: github.repository_owner == 'Qiskit' + name: Backwards compatibility + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.9' + + - uses: dtolnay/rust-toolchain@stable + + - uses: actions/cache@v4 + with: + path: test/qpy_compat/qpy_cache + # The hashing is this key can be too eager to invalidate the cache, + # but since we risk the QPY tests failing to update if they're not in + # sync, it's better safe than sorry. + key: qpy-${{ hashFiles('test/qpy_compat/**') }} + + - name: Run QPY backwards compatibility tests + working-directory: test/qpy_compat + run: ./run_tests.sh diff --git a/Cargo.lock b/Cargo.lock index 0638429aaf4b..95b44b4ec364 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,15 +41,6 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" -[[package]] -name = "always-assert" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4436e0292ab1bb631b42973c61205e704475fe8126af845c8d923c0996328127" -dependencies = [ - "log", -] - [[package]] name = "approx" version = "0.4.0" @@ -197,15 +188,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -647,12 +629,6 @@ dependencies = [ "either", ] -[[package]] -name = "jod-thread" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae" - [[package]] name = "lazy_static" version = "1.5.0" @@ -671,12 +647,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - [[package]] name = "matrixcompare" version = "0.3.0" @@ -718,15 +688,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miow" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123" -dependencies = [ - "windows-sys 0.42.0", -] - [[package]] name = "nano-gemm" version = "0.1.2" @@ -878,9 +839,9 @@ dependencies = [ [[package]] name = "numpy" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf314fca279e6e6ac2126a4ff98f26d88aa4ad06bc68fb6ae5cf4bd706758311" +checksum = "edb929bc0da91a4d85ed6c0a84deaa53d411abfb387fc271124f91bf6b89f14e" dependencies = [ "libc", "ndarray", @@ -899,9 +860,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oq3_lexer" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2f0f9d48042c12f82b2550808378718627e108fc3f6adf63e02e5293541a3" +checksum = "a27bbc91e3e9d6193a44aac8f5d62c1507c41669af71a4e7e0ef66fd6470e960" dependencies = [ "unicode-properties", "unicode-xid", @@ -909,9 +870,9 @@ dependencies = [ [[package]] name = "oq3_parser" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69b215426a4a2a023fd62cca4436c633ba0ab39ee260aca875ac60321b3704b" +checksum = "9a72022fcb414e8a0912920a1cf46417b6aa95f19d4b38778df7450f8a3c17fa" dependencies = [ "drop_bomb", "oq3_lexer", @@ -920,9 +881,9 @@ dependencies = [ [[package]] name = "oq3_semantics" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e15e9cee54e92fb1b3aaa42556b0bd76c8c1c10912a7d6798f43dfc3afdcb0d" +checksum = "b72dffd869f3548190c705828d030fbb7fca94e519dcfa6a489227e5c3ffd777" dependencies = [ "boolenum", "hashbrown 0.12.3", @@ -933,9 +894,9 @@ dependencies = [ [[package]] name = "oq3_source_file" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f65243cc4807c600c544a21db6c17544c53aa2bc034b3eccf388251cc6530e7" +checksum = "ff8c03f1f92c7a8f0b5249664b526169ceb8f925cb314ff93d3b27d8a4afb78c" dependencies = [ "ariadne", "oq3_syntax", @@ -943,9 +904,9 @@ dependencies = [ [[package]] name = "oq3_syntax" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c3d637a7db9ddb3811719db8a466bd4960ea668df4b2d14043a1b0038465b0" +checksum = "42c754ce1d9da28d6c0334c212d64b521288fe8c7cf16e9727d45dcf661ff084" dependencies = [ "cov-mark", "either", @@ -954,7 +915,6 @@ dependencies = [ "once_cell", "oq3_lexer", "oq3_parser", - "ra_ap_stdx", "rowan", "rustc-hash", "rustversion", @@ -1286,20 +1246,6 @@ version = "0.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d33758724f997689f84146e5401e28d875a061804f861f113696f44f5232aa" -[[package]] -name = "ra_ap_stdx" -version = "0.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80fb2ff88b31fa35cde89ae13ea7c9ada97c7a2c778dcafef530a267658000" -dependencies = [ - "always-assert", - "crossbeam-channel", - "jod-thread", - "libc", - "miow", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -1674,22 +1620,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.9" @@ -1699,27 +1629,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 512ee095732e..1d5319341bc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ hashbrown.version = "0.14.5" num-bigint = "0.4" num-complex = "0.4" ndarray = "0.15" -numpy = "0.22.0" +numpy = "0.22.1" smallvec = "1.13" thiserror = "1.0" rustworkx-core = "0.15" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d182ba32f1b3..94ed7d1fad7f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -144,7 +144,7 @@ stages: - stage: "Lint_Docs_Prelim_Tests" displayName: "Preliminary tests" jobs: - - template: ".azure/lint_docs_qpy-linux.yml" + - template: ".azure/lint_docs-linux.yml" parameters: pythonVersion: ${{ parameters.minimumPythonVersion }} @@ -208,7 +208,7 @@ stages: - stage: "Merge_Queue" displayName: "Merge queue" jobs: - - template: ".azure/lint_docs_qpy-linux.yml" + - template: ".azure/lint_docs-linux.yml" parameters: pythonVersion: ${{ parameters.minimumPythonVersion }} diff --git a/crates/accelerate/src/circuit_library/iqp.rs b/crates/accelerate/src/circuit_library/iqp.rs new file mode 100644 index 000000000000..4cb931f8c228 --- /dev/null +++ b/crates/accelerate/src/circuit_library/iqp.rs @@ -0,0 +1,165 @@ +// This code is part of Qiskit. +// +// (C) Copyright IBM 2024 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +use std::f64::consts::PI; + +use ndarray::{Array2, ArrayView2}; +use numpy::PyReadonlyArray2; +use pyo3::prelude::*; +use qiskit_circuit::{ + circuit_data::CircuitData, + operations::{Param, StandardGate}, + Qubit, +}; +use rand::{Rng, SeedableRng}; +use rand_pcg::Pcg64Mcg; +use smallvec::{smallvec, SmallVec}; + +use crate::CircuitError; + +const PI2: f64 = PI / 2.0; +const PI8: f64 = PI / 8.0; + +fn iqp( + interactions: ArrayView2, +) -> impl Iterator, SmallVec<[Qubit; 2]>)> + '_ { + let num_qubits = interactions.ncols(); + + // The initial and final Hadamard layer. + let h_layer = + (0..num_qubits).map(|i| (StandardGate::HGate, smallvec![], smallvec![Qubit(i as u32)])); + + // The circuit interactions are powers of the CSGate, which is implemented by calling + // the CPhaseGate with angles of Pi/2 times the power. The gate powers are given by the + // upper triangular part of the symmetric ``interactions`` matrix. + let connections = (0..num_qubits).flat_map(move |i| { + (i + 1..num_qubits) + .map(move |j| (j, interactions[(i, j)])) + .filter(move |(_, value)| value % 4 != 0) + .map(move |(j, value)| { + ( + StandardGate::CPhaseGate, + smallvec![Param::Float(PI2 * value as f64)], + smallvec![Qubit(i as u32), Qubit(j as u32)], + ) + }) + }); + + // The layer of T gates. Again we use the PhaseGate, now with powers of Pi/8. The powers + // are given by the diagonal of the ``interactions`` matrix. + let shifts = (0..num_qubits) + .map(move |i| interactions[(i, i)]) + .enumerate() + .filter(|(_, value)| value % 8 != 0) + .map(|(i, value)| { + ( + StandardGate::PhaseGate, + smallvec![Param::Float(PI8 * value as f64)], + smallvec![Qubit(i as u32)], + ) + }); + + h_layer + .clone() + .chain(connections) + .chain(shifts) + .chain(h_layer) +} + +/// This generates a random symmetric integer matrix with values in [0,7]. +fn generate_random_interactions(num_qubits: u32, seed: Option) -> Array2 { + let num_qubits = num_qubits as usize; + let mut rng = match seed { + Some(seed) => Pcg64Mcg::seed_from_u64(seed), + None => Pcg64Mcg::from_entropy(), + }; + + let mut mat = Array2::zeros((num_qubits, num_qubits)); + for i in 0..num_qubits { + mat[[i, i]] = rng.gen_range(0..8) as i64; + for j in 0..i { + mat[[i, j]] = rng.gen_range(0..8) as i64; + mat[[j, i]] = mat[[i, j]]; + } + } + mat +} + +/// Returns true if the input matrix is symmetric, otherwise false. +fn check_symmetric(matrix: &ArrayView2) -> bool { + let nrows = matrix.nrows(); + + if matrix.ncols() != nrows { + return false; + } + + for i in 0..nrows { + for j in i + 1..nrows { + if matrix[(i, j)] != matrix[(j, i)] { + return false; + } + } + } + + true +} + +/// Implement an Instantaneous Quantum Polynomial time (IQP) circuit. +/// +/// This class of circuits is conjectured to be classically hard to simulate, +/// forming a generalization of the Boson sampling problem. See Ref. [1] for +/// more details. +/// +/// Args: +/// interactions: If provided, this is a symmetric square matrix of width ``num_qubits``, +/// determining the operations in the IQP circuit. The diagonal represents the power +/// of single-qubit T gates and the upper triangular part the power of CS gates +/// in between qubit pairs. If None, a random interactions matrix will be sampled. +/// +/// Returns: +/// The IQP circuit. +/// +/// References: +/// +/// [1] M. J. Bremner et al. Average-case complexity versus approximate simulation of +/// commuting quantum computations, Phys. Rev. Lett. 117, 080501 (2016). +/// `arXiv:1504.07999 `_ +#[pyfunction] +#[pyo3(signature = (interactions))] +pub fn py_iqp(py: Python, interactions: PyReadonlyArray2) -> PyResult { + let array = interactions.as_array(); + let view = array.view(); + if !check_symmetric(&view) { + return Err(CircuitError::new_err("IQP matrix must be symmetric.")); + } + + let num_qubits = view.ncols() as u32; + let instructions = iqp(view); + CircuitData::from_standard_gates(py, num_qubits, instructions, Param::Float(0.0)) +} + +/// Generate a random Instantaneous Quantum Polynomial time (IQP) circuit. +/// +/// Args: +/// num_qubits: The number of qubits. +/// seed: A random seed for generating the interactions matrix. +/// +/// Returns: +/// A random IQP circuit. +#[pyfunction] +#[pyo3(signature = (num_qubits, seed=None))] +pub fn py_random_iqp(py: Python, num_qubits: u32, seed: Option) -> PyResult { + let interactions = generate_random_interactions(num_qubits, seed); + let view = interactions.view(); + let instructions = iqp(view); + CircuitData::from_standard_gates(py, num_qubits, instructions, Param::Float(0.0)) +} diff --git a/crates/accelerate/src/circuit_library/mod.rs b/crates/accelerate/src/circuit_library/mod.rs index bc960bab2aab..5fa711070b48 100644 --- a/crates/accelerate/src/circuit_library/mod.rs +++ b/crates/accelerate/src/circuit_library/mod.rs @@ -13,12 +13,15 @@ use pyo3::prelude::*; mod entanglement; +mod iqp; mod pauli_feature_map; mod quantum_volume; pub fn circuit_library(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(pauli_feature_map::pauli_feature_map))?; m.add_wrapped(wrap_pyfunction!(entanglement::get_entangler_map))?; + m.add_wrapped(wrap_pyfunction!(iqp::py_iqp))?; + m.add_wrapped(wrap_pyfunction!(iqp::py_random_iqp))?; m.add_wrapped(wrap_pyfunction!(quantum_volume::quantum_volume))?; Ok(()) } diff --git a/crates/accelerate/src/commutation_analysis.rs b/crates/accelerate/src/commutation_analysis.rs index a29c648a5f81..07266191fe45 100644 --- a/crates/accelerate/src/commutation_analysis.rs +++ b/crates/accelerate/src/commutation_analysis.rs @@ -61,7 +61,7 @@ pub(crate) fn analyze_commutations_inner( for qubit in 0..dag.num_qubits() { let wire = Wire::Qubit(Qubit(qubit as u32)); - for current_gate_idx in dag.nodes_on_wire(py, &wire, false) { + for current_gate_idx in dag.nodes_on_wire(&wire, false) { // get the commutation set associated with the current wire, or create a new // index set containing the current gate let commutation_entry = commutation_set diff --git a/crates/accelerate/src/commutation_checker.rs b/crates/accelerate/src/commutation_checker.rs index 005e37ecc375..16fcc5eca8fb 100644 --- a/crates/accelerate/src/commutation_checker.rs +++ b/crates/accelerate/src/commutation_checker.rs @@ -28,7 +28,7 @@ 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}; +use qiskit_circuit::operations::{Operation, OperationRef, Param, StandardGate}; use qiskit_circuit::{BitType, Clbit, Qubit}; use crate::unitary_compose; @@ -38,8 +38,28 @@ static SKIPPED_NAMES: [&str; 4] = ["measure", "reset", "delay", "initialize"]; static NO_CACHE_NAMES: [&str; 2] = ["annotated", "linear_function"]; static SUPPORTED_OP: Lazy> = Lazy::new(|| { HashSet::from([ - "h", "x", "y", "z", "sx", "sxdg", "t", "tdg", "s", "sdg", "cx", "cy", "cz", "swap", - "iswap", "ecr", "ccx", "cswap", + "rxx", "ryy", "rzz", "rzx", "h", "x", "y", "z", "sx", "sxdg", "t", "tdg", "s", "sdg", "cx", + "cy", "cz", "swap", "iswap", "ecr", "ccx", "cswap", + ]) +}); + +// 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>> = Lazy::new(|| { + HashMap::from([ + ("rx", Some(OperationRef::Standard(StandardGate::XGate))), + ("ry", Some(OperationRef::Standard(StandardGate::YGate))), + ("rz", Some(OperationRef::Standard(StandardGate::ZGate))), + ("p", Some(OperationRef::Standard(StandardGate::ZGate))), + ("u1", Some(OperationRef::Standard(StandardGate::ZGate))), + ("crx", Some(OperationRef::Standard(StandardGate::CXGate))), + ("cry", Some(OperationRef::Standard(StandardGate::CYGate))), + ("crz", Some(OperationRef::Standard(StandardGate::CZGate))), + ("cp", Some(OperationRef::Standard(StandardGate::CZGate))), + ("rxx", None), // None means the gate is in the commutation dictionary + ("ryy", None), + ("rzx", None), + ("rzz", None), ]) }); @@ -89,6 +109,7 @@ impl CommutationChecker { ) -> Self { // Initialize sets before they are used in the commutation checker Lazy::force(&SUPPORTED_OP); + Lazy::force(&SUPPORTED_ROTATIONS); CommutationChecker { library: CommutationLibrary::new(standard_gate_commutations), cache: HashMap::new(), @@ -242,6 +263,23 @@ impl CommutationChecker { cargs2: &[Clbit], max_num_qubits: u32, ) -> PyResult { + // relative and absolute tolerance used to (1) check whether rotation gates commute + // trivially (i.e. the rotation angle is so small we assume it commutes) and (2) define + // comparison for the matrix-based commutation checks + let rtol = 1e-5; + let atol = 1e-8; + + // if we have rotation gates, we attempt to map them to their generators, for example + // RX -> X or CPhase -> CZ + let (op1, params1, trivial1) = map_rotation(op1, params1, rtol); + if trivial1 { + return Ok(true); + } + let (op2, params2, trivial2) = map_rotation(op2, params2, rtol); + if trivial2 { + return Ok(true); + } + if let Some(gates) = &self.gates { if !gates.is_empty() && (!gates.contains(op1.name()) || !gates.contains(op2.name())) { return Ok(false); @@ -286,7 +324,9 @@ impl CommutationChecker { 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(_))); + second_params.iter().any(|p| !matches!(p, Param::Float(_))) + && !SUPPORTED_OP.contains(op1.name()) + && !SUPPORTED_OP.contains(op2.name()); if skip_cache { return self.commute_matmul( @@ -297,6 +337,8 @@ impl CommutationChecker { second_op, second_params, second_qargs, + rtol, + atol, ); } @@ -331,6 +373,8 @@ impl CommutationChecker { second_op, second_params, second_qargs, + rtol, + atol, )?; // TODO: implement a LRU cache for this @@ -365,6 +409,8 @@ impl CommutationChecker { second_op: &OperationRef, second_params: &[Param], second_qargs: &[Qubit], + rtol: f64, + atol: f64, ) -> PyResult { // Compute relative positioning of qargs of the second gate to the first gate. // Since the qargs come out the same BitData, we already know there are no accidential @@ -405,8 +451,6 @@ impl CommutationChecker { None => return Ok(false), }; - let rtol = 1e-5; - let atol = 1e-8; if first_qarg == second_qarg { match first_qarg.len() { 1 => Ok(unitary_compose::commute_1q( @@ -568,6 +612,41 @@ where .any(|x| matches!(x, Param::ParameterExpression(_))) } +/// Check if a given operation can be mapped onto a generator. +/// +/// If ``op`` is in the ``SUPPORTED_ROTATIONS`` hashmap, it is a rotation and we +/// (1) check whether the rotation is so small (modulo pi) that we assume it is the +/// identity and it commutes trivially with every other operation +/// (2) otherwise, we check whether a generator of the rotation is given (e.g. X for RX) +/// and we return the generator +/// +/// Returns (operation, parameters, commutes_trivially). +fn map_rotation<'a>( + op: &'a OperationRef<'a>, + params: &'a [Param], + tol: f64, +) -> (&'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 + // commute with everything, and we simply return the operation with the flag that + // it commutes trivially + if let Param::Float(angle) = params[0] { + if (angle % std::f64::consts::PI).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) + if let Some(gate) = generator { + return (gate, &[], false); + }; + } + (op, params, false) +} + fn get_relative_placement( first_qargs: &[Qubit], second_qargs: &[Qubit], diff --git a/crates/accelerate/src/lib.rs b/crates/accelerate/src/lib.rs index e04f6d63a936..a0f9a6d72731 100644 --- a/crates/accelerate/src/lib.rs +++ b/crates/accelerate/src/lib.rs @@ -10,6 +10,10 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +// This stylistic lint suppression should be in `Cargo.toml`, but we can't do that until we're at an +// MSRV of 1.74 or greater. +#![allow(clippy::comparison_chain)] + use std::env; use pyo3::import_exception; @@ -73,3 +77,4 @@ pub fn getenv_use_multiple_threads() -> bool { } import_exception!(qiskit.exceptions, QiskitError); +import_exception!(qiskit.circuit.exceptions, CircuitError); diff --git a/crates/accelerate/src/sparse_observable.rs b/crates/accelerate/src/sparse_observable.rs index 14e386f3a2cb..9f410f79d72c 100644 --- a/crates/accelerate/src/sparse_observable.rs +++ b/crates/accelerate/src/sparse_observable.rs @@ -12,6 +12,7 @@ use std::collections::btree_map; +use hashbrown::HashSet; use num_complex::Complex64; use num_traits::Zero; use thiserror::Error; @@ -263,8 +264,11 @@ impl ::std::convert::TryFrom for BitTerm { } } -/// Error cases stemming from data coherence at the point of entry into `SparseObservable` from raw -/// arrays. +/// Error cases stemming from data coherence at the point of entry into `SparseObservable` from +/// user-provided arrays. +/// +/// These most typically appear during [from_raw_parts], but can also be introduced by various +/// remapping arithmetic functions. /// /// These are generally associated with the Python-space `ValueError` because all of the /// `TypeError`-related ones are statically forbidden (within Rust) by the language, and conversion @@ -285,6 +289,10 @@ pub enum CoherenceError { DecreasingBoundaries, #[error("the values in `indices` are not term-wise increasing")] UnsortedIndices, + #[error("the input contains duplicate qubits")] + DuplicateIndices, + #[error("the provided qubit mapping does not account for all contained qubits")] + IndexMapTooSmall, } impl From for PyErr { fn from(value: CoherenceError) -> PyErr { @@ -753,7 +761,9 @@ impl SparseObservable { let indices = &indices[left..right]; if !indices.is_empty() { for (index_left, index_right) in indices[..].iter().zip(&indices[1..]) { - if index_left >= index_right { + if index_left == index_right { + return Err(CoherenceError::DuplicateIndices); + } else if index_left > index_right { return Err(CoherenceError::UnsortedIndices); } } @@ -931,6 +941,42 @@ impl SparseObservable { Ok(()) } + /// Relabel the `indices` in the operator to new values. + /// + /// This fails if any of the new indices are too large, or if any mapping would cause a term to + /// contain duplicates of the same index. It may not detect if multiple qubits are mapped to + /// the same index, if those qubits never appear together in the same term. Such a mapping + /// would not cause data-coherence problems (the output observable will be valid), but is + /// unlikely to be what you intended. + /// + /// *Panics* if `new_qubits` is not long enough to map every index used in the operator. + pub fn relabel_qubits_from_slice(&mut self, new_qubits: &[u32]) -> Result<(), CoherenceError> { + for qubit in new_qubits { + if *qubit >= self.num_qubits { + return Err(CoherenceError::BitIndexTooHigh); + } + } + let mut order = btree_map::BTreeMap::new(); + for i in 0..self.num_terms() { + let start = self.boundaries[i]; + let end = self.boundaries[i + 1]; + for j in start..end { + order.insert(new_qubits[self.indices[j] as usize], self.bit_terms[j]); + } + if order.len() != end - start { + return Err(CoherenceError::DuplicateIndices); + } + for (index, dest) in order.keys().zip(&mut self.indices[start..end]) { + *dest = *index; + } + for (bit_term, dest) in order.values().zip(&mut self.bit_terms[start..end]) { + *dest = *bit_term; + } + order.clear(); + } + Ok(()) + } + /// Return a suitable Python error if two observables do not have equal numbers of qubits. fn check_equal_qubits(&self, other: &SparseObservable) -> PyResult<()> { if self.num_qubits != other.num_qubits { @@ -2020,6 +2066,77 @@ impl SparseObservable { } out } + + /// Apply a transpiler layout to this :class:`SparseObservable`. + /// + /// Typically you will have defined your observable in terms of the virtual qubits of the + /// circuits you will use to prepare states. After transpilation, the virtual qubits are mapped + /// to particular physical qubits on a device, which may be wider than your circuit. That + /// mapping can also change over the course of the circuit. This method transforms the input + /// observable on virtual qubits to an observable that is suitable to apply immediately after + /// the fully transpiled *physical* circuit. + /// + /// Args: + /// layout (TranspileLayout | list[int] | None): The layout to apply. Most uses of this + /// function should pass the :attr:`.QuantumCircuit.layout` field from a circuit that + /// was transpiled for hardware. In addition, you can pass a list of new qubit indices. + /// If given as explicitly ``None``, no remapping is applied (but you can still use + /// ``num_qubits`` to expand the observable). + /// num_qubits (int | None): The number of qubits to expand the observable to. If not + /// supplied, the output will be as wide as the given :class:`.TranspileLayout`, or the + /// same width as the input if the ``layout`` is given in another form. + /// + /// Returns: + /// A new :class:`SparseObservable` with the provided layout applied. + #[pyo3(signature = (/, layout, num_qubits=None), name = "apply_layout")] + fn py_apply_layout(&self, layout: Bound, num_qubits: Option) -> PyResult { + let py = layout.py(); + let check_inferred_qubits = |inferred: u32| -> PyResult { + if inferred < self.num_qubits { + return Err(PyValueError::new_err(format!( + "cannot shrink the qubit count in an observable from {} to {}", + self.num_qubits, inferred + ))); + } + Ok(inferred) + }; + if layout.is_none() { + let mut out = self.clone(); + out.num_qubits = check_inferred_qubits(num_qubits.unwrap_or(self.num_qubits))?; + return Ok(out); + } + let (num_qubits, layout) = if layout.is_instance( + &py.import_bound(intern!(py, "qiskit.transpiler"))? + .getattr(intern!(py, "TranspileLayout"))?, + )? { + ( + check_inferred_qubits( + layout.getattr(intern!(py, "_output_qubit_list"))?.len()? as u32 + )?, + layout + .call_method0(intern!(py, "final_index_layout"))? + .extract::>()?, + ) + } else { + ( + check_inferred_qubits(num_qubits.unwrap_or(self.num_qubits))?, + layout.extract()?, + ) + }; + if layout.len() < self.num_qubits as usize { + return Err(CoherenceError::IndexMapTooSmall.into()); + } + if layout.iter().any(|qubit| *qubit >= num_qubits) { + return Err(CoherenceError::BitIndexTooHigh.into()); + } + if layout.iter().collect::>().len() != layout.len() { + return Err(CoherenceError::DuplicateIndices.into()); + } + let mut out = self.clone(); + out.num_qubits = num_qubits; + out.relabel_qubits_from_slice(&layout)?; + Ok(out) + } } impl ::std::ops::Add<&SparseObservable> for SparseObservable { diff --git a/crates/accelerate/src/sparse_pauli_op.rs b/crates/accelerate/src/sparse_pauli_op.rs index 73c4ab7a73d5..1a85daf036d9 100644 --- a/crates/accelerate/src/sparse_pauli_op.rs +++ b/crates/accelerate/src/sparse_pauli_op.rs @@ -20,11 +20,13 @@ use numpy::prelude::*; use numpy::{PyArray1, PyArray2, PyReadonlyArray1, PyReadonlyArray2, PyUntypedArrayMethods}; use hashbrown::HashMap; -use ndarray::{s, Array1, Array2, ArrayView1, ArrayView2, Axis}; +use ndarray::{s, ArrayView1, ArrayView2, Axis}; use num_complex::Complex64; use num_traits::Zero; -use qiskit_circuit::util::{c64, C_ONE, C_ZERO}; use rayon::prelude::*; +use thiserror::Error; + +use qiskit_circuit::util::{c64, C_ZERO}; use crate::rayon_ext::*; @@ -70,14 +72,6 @@ pub fn unordered_unique(py: Python, array: PyReadonlyArray2) -> (PyObject, ) } -#[derive(Clone, Copy)] -enum Pauli { - I, - X, - Y, - Z, -} - /// Pack a 2D array of Booleans into a given width. Returns an error if the input array is /// too large to be packed into u64. fn pack_bits(bool_arr: ArrayView2) -> Result, ()> { @@ -188,10 +182,9 @@ impl ZXPaulis { } /// Intermediate structure that represents readonly views onto the Python-space sparse Pauli data. -/// This is used in the chained methods so that the syntactical temporary lifetime extension can -/// occur; we can't have the readonly array temporaries only live within a method that returns -/// [ZXPaulisView], because otherwise the lifetimes of the [PyReadonlyArray] elements will be too -/// short. +/// This is used in the chained methods so that the lifetime extension can occur; we can't have the +/// readonly array temporaries only live within a method that returns [ZXPaulisView], because +/// otherwise the lifetimes of the [PyReadonlyArray] elements will be too short. pub struct ZXPaulisReadonly<'a> { x: PyReadonlyArray2<'a, bool>, z: PyReadonlyArray2<'a, bool>, @@ -325,175 +318,609 @@ impl MatrixCompressedPaulis { } } +#[derive(Clone, Debug)] +struct DecomposeOut { + z: Vec, + x: Vec, + phases: Vec, + coeffs: Vec, + scale: f64, + tol: f64, + num_qubits: usize, +} + +#[derive(Error, Debug)] +enum DecomposeError { + #[error("operators must have two dimensions, not {0}")] + BadDimension(usize), + #[error("operators must be square with a power-of-two side length, not {0:?}")] + BadShape([usize; 2]), +} +impl From for PyErr { + fn from(value: DecomposeError) -> PyErr { + PyValueError::new_err(value.to_string()) + } +} + /// Decompose a dense complex operator into the symplectic Pauli representation in the /// ZX-convention. /// /// This is an implementation of the "tensorized Pauli decomposition" presented in /// `Hantzko, Binkowski and Gupta (2023) `__. +/// +/// Implementation +/// -------------- +/// +/// The original algorithm was described recurisvely, allocating new matrices for each of the +/// block-wise sums (e.g. `op[top_left] + op[bottom_right]`). This implementation differs in two +/// major ways: +/// +/// - We do not allocate new matrices recursively, but instead produce a single copy of the input +/// and repeatedly overwrite subblocks of it at each point of the decomposition. +/// - The implementation is rewritten as an iteration rather than a recursion. The current "state" +/// of the iteration is encoded in a single machine word (the `PauliLocation` struct below). +/// +/// We do the decomposition in three "stages", with the stage changing whenever we need to change +/// the input/output types. The first level is mathematically the same as the middle levels, it +/// just gets handled separately because it does the double duty of moving the data out of the +/// Python-space strided array into a Rust-space contiguous array that we can modify in-place. +/// The middle levels all act in-place on this newly created scratch space. Finally, at the last +/// level, we've completed the decomposition and need to be writing the result into the output +/// data structures rather than into the scratch space. +/// +/// Each "level" is handling one qubit in the operator, equivalently to the recursive procedure +/// described in the paper referenced in the docstring. This implementation is iterative +/// stack-based and in place, rather than recursive. +/// +/// We can get away with overwriting our scratch-space matrix at each point, because each +/// element of a given subblock is used exactly twice during each decomposition - once for the `a + +/// b` case, and once for the `a - b` case. The second operand is the same in both cases. +/// Illustratively, at each step we're decomposing a submatrix blockwise, where we label the blocks +/// like this: +/// +/// +---------+---------+ +---------+---------+ +/// | | | | | | +/// | I | X | | I + Z | X + Y | +/// | | | | | | +/// +---------+---------+ =====> +---------+---------+ +/// | | | | | | +/// | Y | Z | | X - Y | I - Z | +/// | | | | | | +/// +---------+---------+ +---------+---------+ +/// +/// Each addition or subtraction is done elementwise, so as long as we iterate through the two pairs +/// of coupled blocks in order in lockstep, we can write out the answers together without +/// overwriting anything we need again. We ignore all factors of 1/2 until the very last step, and +/// apply them all at once. This minimises the number of floating-point operations we have to do. +/// +/// We store the iteration order as a stack of `PauliLocation`s, whose own docstring explains how it +/// tracks the top-left corner and the size of the submatrix it represents. #[pyfunction] pub fn decompose_dense( py: Python, operator: PyReadonlyArray2, tolerance: f64, ) -> PyResult { - let num_qubits = operator.shape()[0].ilog2() as usize; - let size = 1 << num_qubits; - if operator.shape() != [size, size] { - return Err(PyValueError::new_err(format!( - "input with shape {:?} cannot be interpreted as a multiqubit operator", - operator.shape() - ))); - } - let mut paulis = vec![]; - let mut coeffs = vec![]; - if num_qubits > 0 { - decompose_dense_inner( - C_ONE, - num_qubits, - &[], - operator.as_array(), - &mut paulis, - &mut coeffs, - tolerance * tolerance, - ); - } - if coeffs.is_empty() { - Ok(ZXPaulis { - z: PyArray2::zeros_bound(py, [0, num_qubits], false).into(), - x: PyArray2::zeros_bound(py, [0, num_qubits], false).into(), - phases: PyArray1::zeros_bound(py, [0], false).into(), - coeffs: PyArray1::zeros_bound(py, [0], false).into(), - }) - } else { - // Constructing several arrays of different shapes at once is rather awkward in iterator - // logic, so we just loop manually. - let mut z = Array2::::uninit([paulis.len(), num_qubits]); - let mut x = Array2::::uninit([paulis.len(), num_qubits]); - let mut phases = Array1::::uninit(paulis.len()); - for (i, paulis) in paulis.drain(..).enumerate() { - let mut phase = 0u8; - for (j, pauli) in paulis.into_iter().rev().enumerate() { - match pauli { - Pauli::I => { - z[[i, j]].write(false); - x[[i, j]].write(false); - } - Pauli::X => { - z[[i, j]].write(false); - x[[i, j]].write(true); - } - Pauli::Y => { - z[[i, j]].write(true); - x[[i, j]].write(true); - phase = phase.wrapping_add(1); - } - Pauli::Z => { - z[[i, j]].write(true); - x[[i, j]].write(false); - } + let array_view = operator.as_array(); + let out = py.allow_threads(|| decompose_dense_inner(array_view, tolerance))?; + Ok(ZXPaulis { + z: PyArray1::from_vec_bound(py, out.z) + .reshape([out.phases.len(), out.num_qubits])? + .into(), + x: PyArray1::from_vec_bound(py, out.x) + .reshape([out.phases.len(), out.num_qubits])? + .into(), + phases: PyArray1::from_vec_bound(py, out.phases).into(), + coeffs: PyArray1::from_vec_bound(py, out.coeffs).into(), + }) +} + +/// Rust-only inner component of the `SparsePauliOp` decomposition. +/// +/// See the top-level documentation of [decompose_dense] for more information on the internal +/// algorithm at play. +fn decompose_dense_inner( + operator: ArrayView2, + tolerance: f64, +) -> Result { + let op_shape = match operator.shape() { + [a, b] => [*a, *b], + shape => return Err(DecomposeError::BadDimension(shape.len())), + }; + if op_shape[0].is_zero() { + return Err(DecomposeError::BadShape(op_shape)); + } + let num_qubits = op_shape[0].ilog2() as usize; + let side = 1 << num_qubits; + if op_shape != [side, side] { + return Err(DecomposeError::BadShape(op_shape)); + } + if num_qubits.is_zero() { + // We have to special-case the zero-qubit operator because our `decompose_last_level` still + // needs to "consume" a qubit. + return Ok(DecomposeOut { + z: vec![], + x: vec![], + phases: vec![], + coeffs: vec![operator[[0, 0]]], + scale: 1.0, + tol: tolerance, + num_qubits: 0, + }); + } + let (stack, mut out_list, mut scratch) = decompose_first_level(operator, num_qubits); + decompose_middle_levels(stack, &mut out_list, &mut scratch, num_qubits); + Ok(decompose_last_level( + &mut out_list, + &scratch, + num_qubits, + tolerance, + )) +} + +/// Apply the matrix-addition decomposition at the first level. +/// +/// This is split out from the middle levels because it acts on an `ArrayView2`, and is responsible +/// for copying the operator over into the contiguous scratch space. We can't write over the +/// operator the user gave us (it's not ours to do that to), and anyway, we want to drop to a chunk +/// of memory that we can 100% guarantee is contiguous, so we can elide all the stride checking. +/// We split this out so we can do the first decomposition at the same time as scanning over the +/// operator to copy it. +/// +/// # Panics +/// +/// If the number of qubits in the operator is zero. +fn decompose_first_level( + in_op: ArrayView2, + num_qubits: usize, +) -> (Vec, Vec, Vec) { + let side = 1 << num_qubits; + let mut stack = Vec::::with_capacity(4); + let mut out_list = Vec::::new(); + let mut scratch = Vec::::with_capacity(side * side); + match num_qubits { + 0 => panic!("number of qubits must be greater than zero"), + 1 => { + // If we've only got one qubit, we just want to copy the data over in the correct + // continuity and let the base case of the iteration take care of outputting it. + scratch.extend(in_op.iter()); + out_list.push(PauliLocation::begin(num_qubits)); + } + _ => { + // We don't write out the operator in contiguous-index order, but we can easily + // guarantee that we'll write to each index exactly once without reading it - we still + // visit every index, just in 2x2 blockwise order, not row-by-row. + unsafe { scratch.set_len(scratch.capacity()) }; + let mut ptr = 0usize; + + let cur_qubit = num_qubits - 1; + let mid = 1 << cur_qubit; + let loc = PauliLocation::begin(num_qubits); + let mut i_nonzero = false; + let mut x_nonzero = false; + let mut y_nonzero = false; + let mut z_nonzero = false; + + let i_row_0 = loc.row(); + let i_col_0 = loc.col(); + + let x_row_0 = loc.row(); + let x_col_0 = loc.col() + mid; + + let y_row_0 = loc.row() + mid; + let y_col_0 = loc.col(); + + let z_row_0 = loc.row() + mid; + let z_col_0 = loc.col() + mid; + + for off_row in 0..mid { + let i_row = i_row_0 + off_row; + let z_row = z_row_0 + off_row; + for off_col in 0..mid { + let i_col = i_col_0 + off_col; + let z_col = z_col_0 + off_col; + let value = in_op[[i_row, i_col]] + in_op[[z_row, z_col]]; + scratch[ptr] = value; + ptr += 1; + i_nonzero = i_nonzero || (value != C_ZERO); + } + + let x_row = x_row_0 + off_row; + let y_row = y_row_0 + off_row; + for off_col in 0..mid { + let x_col = x_col_0 + off_col; + let y_col = y_col_0 + off_col; + let value = in_op[[x_row, x_col]] + in_op[[y_row, y_col]]; + scratch[ptr] = value; + ptr += 1; + x_nonzero = x_nonzero || (value != C_ZERO); } } - phases[i].write(phase % 4); + for off_row in 0..mid { + let x_row = x_row_0 + off_row; + let y_row = y_row_0 + off_row; + for off_col in 0..mid { + let x_col = x_col_0 + off_col; + let y_col = y_col_0 + off_col; + let value = in_op[[x_row, x_col]] - in_op[[y_row, y_col]]; + scratch[ptr] = value; + ptr += 1; + y_nonzero = y_nonzero || (value != C_ZERO); + } + let i_row = i_row_0 + off_row; + let z_row = z_row_0 + off_row; + for off_col in 0..mid { + let i_col = i_col_0 + off_col; + let z_col = z_col_0 + off_col; + let value = in_op[[i_row, i_col]] - in_op[[z_row, z_col]]; + scratch[ptr] = value; + ptr += 1; + z_nonzero = z_nonzero || (value != C_ZERO); + } + } + // The middle-levels `stack` is a LIFO, so if we push in this order, we'll consider the + // Pauli terms in lexicographical order, which is the canonical order from + // `SparsePauliOp.sort`. Populating the `out_list` (an initially empty `Vec`) + // effectively reverses the stack, so we want to push its elements in the IXYZ order. + if loc.qubit() == 1 { + i_nonzero.then(|| out_list.push(loc.push_i())); + x_nonzero.then(|| out_list.push(loc.push_x())); + y_nonzero.then(|| out_list.push(loc.push_y())); + z_nonzero.then(|| out_list.push(loc.push_z())); + } else { + z_nonzero.then(|| stack.push(loc.push_z())); + y_nonzero.then(|| stack.push(loc.push_y())); + x_nonzero.then(|| stack.push(loc.push_x())); + i_nonzero.then(|| stack.push(loc.push_i())); + } } - // These are safe because the above loops write into every element. It's guaranteed that - // each of the elements of the `paulis` vec will have `num_qubits` because they're all - // reading from the same base array. - let z = unsafe { z.assume_init() }; - let x = unsafe { x.assume_init() }; - let phases = unsafe { phases.assume_init() }; - Ok(ZXPaulis { - z: z.into_pyarray_bound(py).into(), - x: x.into_pyarray_bound(py).into(), - phases: phases.into_pyarray_bound(py).into(), - coeffs: PyArray1::from_vec_bound(py, coeffs).into(), - }) } + (stack, out_list, scratch) } -/// Recurse worker routine of `decompose_dense`. Should be called with at least one qubit. -fn decompose_dense_inner( - factor: Complex64, +/// Iteratively decompose the matrix at all levels other than the first and last. +/// +/// This populates the `out_list` with locations. This is mathematically the same as the first +/// level of the decomposition, except now we're acting in-place on our Rust-space contiguous +/// scratch space, rather than the strided Python-space array we were originally given. +fn decompose_middle_levels( + mut stack: Vec, + out_list: &mut Vec, + scratch: &mut [Complex64], num_qubits: usize, - paulis: &[Pauli], - block: ArrayView2, - out_paulis: &mut Vec>, - out_coeffs: &mut Vec, - square_tolerance: f64, ) { - if num_qubits == 0 { - // It would be safe to `return` here, but if it's unreachable then LLVM is allowed to - // optimize out this branch entirely in release mode, which is good for a ~2% speedup. - unreachable!("should not call this with an empty operator") - } - // Base recursion case. - if num_qubits == 1 { - let mut push_if_nonzero = |extra: Pauli, value: Complex64| { - if value.norm_sqr() <= square_tolerance { - return; + let side = 1 << num_qubits; + // The stack is a LIFO, which is how we implement the depth-first iteration. Depth-first + // means `stack` never grows very large; it reaches at most `3*num_qubits - 2` elements (if all + // terms are zero all the way through the first subblock decomposition). `out_list`, on the + // other hand, can be `4 ** (num_qubits - 1)` entries in the worst-case scenario of a + // completely dense (in Pauli terms) operator. + while let Some(loc) = stack.pop() { + // Here we work pairwise, writing out the new values into both I and Z simultaneously (etc + // for X and Y) so we can re-use their scratch space and avoid re-allocating. We're doing + // the multiple assignment `(I, Z) = (I + Z, I - Z)`. + // + // See the documentation of `decompose_dense` for more information on how this works. + let mid = 1 << loc.qubit(); + let mut i_nonzero = false; + let mut z_nonzero = false; + let i_row_0 = loc.row(); + let i_col_0 = loc.col(); + let z_row_0 = loc.row() + mid; + let z_col_0 = loc.col() + mid; + for off_row in 0..mid { + let i_loc_0 = (i_row_0 + off_row) * side + i_col_0; + let z_loc_0 = (z_row_0 + off_row) * side + z_col_0; + for off_col in 0..mid { + let i_loc = i_loc_0 + off_col; + let z_loc = z_loc_0 + off_col; + let add = scratch[i_loc] + scratch[z_loc]; + let sub = scratch[i_loc] - scratch[z_loc]; + scratch[i_loc] = add; + scratch[z_loc] = sub; + i_nonzero = i_nonzero || (add != C_ZERO); + z_nonzero = z_nonzero || (sub != C_ZERO); } - let paulis = { - let mut vec = Vec::with_capacity(paulis.len() + 1); - vec.extend_from_slice(paulis); - vec.push(extra); - vec - }; - out_paulis.push(paulis); - out_coeffs.push(value); - }; - push_if_nonzero(Pauli::I, 0.5 * factor * (block[[0, 0]] + block[[1, 1]])); - push_if_nonzero(Pauli::X, 0.5 * factor * (block[[0, 1]] + block[[1, 0]])); - push_if_nonzero( - Pauli::Y, - 0.5 * Complex64::i() * factor * (block[[0, 1]] - block[[1, 0]]), - ); - push_if_nonzero(Pauli::Z, 0.5 * factor * (block[[0, 0]] - block[[1, 1]])); - return; - } - let mut recurse_if_nonzero = |extra: Pauli, factor: Complex64, values: Array2| { - let mut is_zero = true; - for value in values.iter() { - if !value.is_zero() { - is_zero = false; - break; + } + + let mut x_nonzero = false; + let mut y_nonzero = false; + let x_row_0 = loc.row(); + let x_col_0 = loc.col() + mid; + let y_row_0 = loc.row() + mid; + let y_col_0 = loc.col(); + for off_row in 0..mid { + let x_loc_0 = (x_row_0 + off_row) * side + x_col_0; + let y_loc_0 = (y_row_0 + off_row) * side + y_col_0; + for off_col in 0..mid { + let x_loc = x_loc_0 + off_col; + let y_loc = y_loc_0 + off_col; + let add = scratch[x_loc] + scratch[y_loc]; + let sub = scratch[x_loc] - scratch[y_loc]; + scratch[x_loc] = add; + scratch[y_loc] = sub; + x_nonzero = x_nonzero || (add != C_ZERO); + y_nonzero = y_nonzero || (sub != C_ZERO); } } - if is_zero { - return; + // The middle-levels `stack` is a LIFO, so if we push in this order, we'll consider the + // Pauli terms in lexicographical order, which is the canonical order from + // `SparsePauliOp.sort`. Populating the `out_list` (an initially empty `Vec`) effectively + // reverses the stack, so we want to push its elements in the IXYZ order. + if loc.qubit() == 1 { + i_nonzero.then(|| out_list.push(loc.push_i())); + x_nonzero.then(|| out_list.push(loc.push_x())); + y_nonzero.then(|| out_list.push(loc.push_y())); + z_nonzero.then(|| out_list.push(loc.push_z())); + } else { + z_nonzero.then(|| stack.push(loc.push_z())); + y_nonzero.then(|| stack.push(loc.push_y())); + x_nonzero.then(|| stack.push(loc.push_x())); + i_nonzero.then(|| stack.push(loc.push_i())); } - let mut new_paulis = Vec::with_capacity(paulis.len() + 1); - new_paulis.extend_from_slice(paulis); - new_paulis.push(extra); - decompose_dense_inner( - factor, - num_qubits - 1, - &new_paulis, - values.view(), - out_paulis, - out_coeffs, - square_tolerance, - ); + } +} + +/// Write out the results of the final decomposition into the Pauli ZX form. +/// +/// The calculation here is the same as the previous two sets of decomposers, but we don't want to +/// write the result out into the scratch space to iterate needlessly once more; we want to +/// associate each non-zero coefficient with the final Pauli in the ZX format. +/// +/// This function applies all the factors of 1/2 that we've been skipping during the intermediate +/// decompositions. This means that the factors are applied to the output with `2 * output_len` +/// floating-point operations (real and imaginary), which is a huge reduction compared to repeatedly +/// doing it during the decomposition. +fn decompose_last_level( + out_list: &mut Vec, + scratch: &[Complex64], + num_qubits: usize, + tolerance: f64, +) -> DecomposeOut { + let side = 1 << num_qubits; + let scale = 0.5f64.powi(num_qubits as i32); + // Pessimistically allocate assuming that there will be no zero terms in the out list. We + // don't really pay much cost if we overallocate, but underallocating means that all four + // outputs have to copy their data across to a new allocation. + let mut out = DecomposeOut { + z: Vec::with_capacity(4 * num_qubits * out_list.len()), + x: Vec::with_capacity(4 * num_qubits * out_list.len()), + phases: Vec::with_capacity(4 * out_list.len()), + coeffs: Vec::with_capacity(4 * out_list.len()), + scale, + tol: (tolerance * tolerance) / (scale * scale), + num_qubits, }; - let mid = 1usize << (num_qubits - 1); - recurse_if_nonzero( - Pauli::I, - 0.5 * factor, - &block.slice(s![..mid, ..mid]) + &block.slice(s![mid.., mid..]), - ); - recurse_if_nonzero( - Pauli::X, - 0.5 * factor, - &block.slice(s![..mid, mid..]) + &block.slice(s![mid.., ..mid]), - ); - recurse_if_nonzero( - Pauli::Y, - 0.5 * Complex64::i() * factor, - &block.slice(s![..mid, mid..]) - &block.slice(s![mid.., ..mid]), - ); - recurse_if_nonzero( - Pauli::Z, - 0.5 * factor, - &block.slice(s![..mid, ..mid]) - &block.slice(s![mid.., mid..]), - ); + + for loc in out_list.drain(..) { + let row = loc.row(); + let col = loc.col(); + let base = row * side + col; + let i_value = scratch[base] + scratch[base + side + 1]; + let z_value = scratch[base] - scratch[base + side + 1]; + let x_value = scratch[base + 1] + scratch[base + side]; + let y_value = scratch[base + 1] - scratch[base + side]; + + let x = row ^ col; + let z = row; + let phase = (x & z).count_ones() as u8; + // Pushing the last Pauli onto the `loc` happens "forwards" to maintain lexicographical + // ordering in `out`, since this is the construction of the final object. + push_pauli_if_nonzero(x, z, phase, i_value, &mut out); + push_pauli_if_nonzero(x | 1, z, phase, x_value, &mut out); + push_pauli_if_nonzero(x | 1, z | 1, phase + 1, y_value, &mut out); + push_pauli_if_nonzero(x, z | 1, phase, z_value, &mut out); + } + // If we _wildly_ overallocated, then shrink back to a sensible size to avoid tying up too much + // memory as we return to Python space. + if out.z.capacity() / 4 > out.z.len() { + out.z.shrink_to_fit(); + out.x.shrink_to_fit(); + out.phases.shrink_to_fit(); + out.coeffs.shrink_to_fit(); + } + out +} + +// This generates lookup tables of the form +// const LOOKUP: [[bool; 2] 4] = [[false, false], [true, false], [false, true], [true, true]]; +// when called `pauli_lookup!(LOOKUP, 2, [_, _])`. The last argument is like a dummy version of +// an individual lookup rule, which is consumed to make an inner "loop" with a declarative macro. +macro_rules! pauli_lookup { + ($name:ident, $n:literal, [$head:expr$ (, $($tail:expr),*)?]) => { + static $name: [[bool; $n]; 1<<$n] = pauli_lookup!(@acc, [$($($tail),*)?], [[false], [true]]); + }; + (@acc, [$head:expr $(, $($tail:expr),*)?], [$([$($bools:tt),*]),+]) => { + pauli_lookup!(@acc, [$($($tail),*)?], [$([$($bools),*, false]),+, $([$($bools),*, true]),+]) + }; + (@acc, [], $init:expr) => { $init }; +} +pauli_lookup!(PAULI_LOOKUP_2, 2, [(), ()]); +pauli_lookup!(PAULI_LOOKUP_4, 4, [(), (), (), ()]); +pauli_lookup!(PAULI_LOOKUP_8, 8, [(), (), (), (), (), (), (), ()]); + +/// Push a complete Pauli chain into the output (`out`), if the corresponding entry is non-zero. +/// +/// `x` and `z` represent the symplectic X and Z bitvectors, packed into `usize`, where LSb n +/// corresponds to qubit `n`. +fn push_pauli_if_nonzero( + mut x: usize, + mut z: usize, + phase: u8, + value: Complex64, + out: &mut DecomposeOut, +) { + if value.norm_sqr() <= out.tol { + return; + } + + // This set of `extend` calls is effectively an 8-fold unrolling of the "natural" loop through + // each bit, where the initial `if` statements are handling the remainder (the up-to 7 + // least-significant bits). In practice, it's probably unlikely that people are decomposing + // 16q+ operators, since that's a pretty huge matrix already. + // + // The 8-fold loop unrolling is because going bit-by-bit all the way would be dominated by loop + // and bitwise-operation overhead. + + if out.num_qubits & 1 == 1 { + out.x.push(x & 1 == 1); + out.z.push(z & 1 == 1); + x >>= 1; + z >>= 1; + } + if out.num_qubits & 2 == 2 { + out.x.extend(&PAULI_LOOKUP_2[x & 0b11]); + out.z.extend(&PAULI_LOOKUP_2[z & 0b11]); + x >>= 2; + z >>= 2; + } + if out.num_qubits & 4 == 4 { + out.x.extend(&PAULI_LOOKUP_4[x & 0b1111]); + out.z.extend(&PAULI_LOOKUP_4[z & 0b1111]); + x >>= 4; + z >>= 4; + } + for _ in 0..(out.num_qubits / 8) { + out.x.extend(&PAULI_LOOKUP_8[x & 0b1111_1111]); + out.z.extend(&PAULI_LOOKUP_8[z & 0b1111_1111]); + x >>= 8; + z >>= 8; + } + + let phase = phase % 4; + let value = match phase { + 0 => Complex64::new(out.scale, 0.0) * value, + 1 => Complex64::new(0.0, out.scale) * value, + 2 => Complex64::new(-out.scale, 0.0) * value, + 3 => Complex64::new(0.0, -out.scale) * value, + _ => unreachable!("'x % 4' has only four possible values"), + }; + out.phases.push(phase); + out.coeffs.push(value); +} + +/// The "state" of an iteration step of the dense-operator decomposition routine. +/// +/// Pack the information about which row, column and qubit we're considering into a single `usize`. +/// Complex64 data is 16 bytes long and the operators are square and must be addressable in memory, +/// so the row and column are hardware limited to be of width `usize::BITS / 2 - 2` each. However, +/// we don't need to store at a granularity of 1, because the last 2x2 block we handle manually, so +/// we can remove an extra least significant bit from the row and column. Regardless of the width +/// of `usize`, we can therefore track the state for up to 30 qubits losslessly, which is greater +/// than the maximum addressable memory on a 64-bit system. +/// +/// For a 64-bit usize, the bit pattern is stored like this: +/// +/// 0b__000101__11111111111111111111111110000__11111111111111111111111110000 +/// <-6--> <------------29-------------> <------------29-------------> +/// | | | +/// | uint of the input row uint of the input column +/// | (once a 0 is appended) (once a 0 is appended) +/// | +/// current qubit under consideration +/// +/// The `qubit` field encodes the depth in the call stack that the user of the `PauliLocation` +/// should consider. When the stack is initialised (before any calculation is done), it starts at +/// the highest qubit index (`num_qubits - 1`) and decreases from there until 0. +/// +/// The `row` and `col` methods form the top-left corner of a `(2**(qubit + 1), 2**(qubit + 1))` +/// submatrix (where the top row and leftmost column are 0). The least significant `qubit + 1` +/// bits of the of row and column are therefore always zero; the 0-indexed qubit still corresponds +/// to a 2x2 block. This is why we needn't store it. +#[derive(Debug, Clone, Copy)] +struct PauliLocation(usize); + +impl PauliLocation { + // These shifts and masks are used to access the three components of the bit-packed state. + const QUBIT_SHIFT: u32 = usize::BITS - 6; + const QUBIT_MASK: usize = (usize::MAX >> Self::QUBIT_SHIFT) << Self::QUBIT_SHIFT; + const ROW_SHIFT: u32 = usize::BITS / 2 - 3; + const ROW_MASK: usize = + ((usize::MAX >> Self::ROW_SHIFT) << Self::ROW_SHIFT) & !Self::QUBIT_MASK; + const COL_SHIFT: u32 = 0; // Just for consistency. + const COL_MASK: usize = usize::MAX & !Self::ROW_MASK & !Self::QUBIT_MASK; + + /// Create the base `PauliLocation` for an entire matrix with `num_qubits` qubits. The initial + /// Pauli chain is empty. + #[inline(always)] + fn begin(num_qubits: usize) -> Self { + Self::new(0, 0, num_qubits - 1) + } + + /// Manually create a new `PauliLocation` with the given information. The logic in the rest of + /// the class assumes that `row` and `col` will end with at least `qubit + 1` zeros, since + /// these are the only valid locations. + #[inline(always)] + fn new(row: usize, col: usize, qubit: usize) -> Self { + debug_assert!(row & 1 == 0); + debug_assert!(col & 1 == 0); + debug_assert!(row < 2 * Self::ROW_SHIFT as usize); + debug_assert!(col < 2 * Self::ROW_SHIFT as usize); + debug_assert!(qubit < 64); + Self( + (qubit << Self::QUBIT_SHIFT) + | (row << Self::ROW_SHIFT >> 1) + | (col << Self::COL_SHIFT >> 1), + ) + } + + /// The row in the dense matrix that this location corresponds to. + #[inline(always)] + fn row(&self) -> usize { + ((self.0 & Self::ROW_MASK) >> Self::ROW_SHIFT) << 1 + } + + /// The column in the dense matrix that this location corresponds to. + #[inline(always)] + fn col(&self) -> usize { + ((self.0 & Self::COL_MASK) >> Self::COL_SHIFT) << 1 + } + + /// Which qubit in the Pauli chain we're currently considering. + #[inline(always)] + fn qubit(&self) -> usize { + (self.0 & Self::QUBIT_MASK) >> Self::QUBIT_SHIFT + } + + /// Create a new location corresponding to the Pauli chain so far, plus an identity on the + /// currently considered qubit. + #[inline(always)] + fn push_i(&self) -> Self { + Self::new(self.row(), self.col(), self.qubit() - 1) + } + + /// Create a new location corresponding to the Pauli chain so far, plus an X on the currently + /// considered qubit. + #[inline(always)] + fn push_x(&self) -> Self { + Self::new( + self.row(), + self.col() | (1 << self.qubit()), + self.qubit() - 1, + ) + } + + /// Create a new location corresponding to the Pauli chain so far, plus a Y on the currently + /// considered qubit. + #[inline(always)] + fn push_y(&self) -> Self { + Self::new( + self.row() | (1 << self.qubit()), + self.col(), + self.qubit() - 1, + ) + } + + /// Create a new location corresponding to the Pauli chain so far, plus a Z on the currently + /// considered qubit. + #[inline(always)] + fn push_z(&self) -> Self { + Self::new( + self.row() | (1 << self.qubit()), + self.col() | (1 << self.qubit()), + self.qubit() - 1, + ) + } } /// Convert the given [ZXPaulis] object to a dense 2D Numpy matrix. @@ -830,11 +1257,13 @@ pub fn sparse_pauli_op(m: &Bound) -> PyResult<()> { #[cfg(test)] mod tests { + use ndarray::{aview2, Array1}; + use super::*; use crate::test::*; - // The purpose of these tests is more about exercising the `unsafe` code; we test for full - // correctness from Python space. + // The purpose of these tests is more about exercising the `unsafe` code under Miri; we test for + // full numerical correctness from Python space. fn example_paulis() -> MatrixCompressedPaulis { MatrixCompressedPaulis { @@ -853,6 +1282,166 @@ mod tests { } } + /// Helper struct for the decomposition testing. This is a subset of the `DecomposeOut` + /// struct, skipping the unnecessary algorithm-state components of it. + /// + /// If we add a more Rust-friendly interface to `SparsePauliOp` in the future, hopefully this + /// can be removed. + #[derive(Clone, PartialEq, Debug)] + struct DecomposeMinimal { + z: Vec, + x: Vec, + phases: Vec, + coeffs: Vec, + num_qubits: usize, + } + impl From for DecomposeMinimal { + fn from(value: DecomposeOut) -> Self { + Self { + z: value.z, + x: value.x, + phases: value.phases, + coeffs: value.coeffs, + num_qubits: value.num_qubits, + } + } + } + impl From for DecomposeMinimal { + fn from(value: MatrixCompressedPaulis) -> Self { + let phases = value + .z_like + .iter() + .zip(value.x_like.iter()) + .map(|(z, x)| ((z & x).count_ones() % 4) as u8) + .collect::>(); + let coeffs = value + .coeffs + .iter() + .zip(phases.iter()) + .map(|(c, phase)| match phase { + 0 => *c, + 1 => Complex64::new(-c.im, c.re), + 2 => Complex64::new(-c.re, -c.im), + 3 => Complex64::new(c.im, -c.re), + _ => panic!("phase should only in [0, 4)"), + }) + .collect(); + let z = value + .z_like + .iter() + .flat_map(|digit| (0..value.num_qubits).map(move |i| (digit & (1 << i)) != 0)) + .collect(); + let x = value + .x_like + .iter() + .flat_map(|digit| (0..value.num_qubits).map(move |i| (digit & (1 << i)) != 0)) + .collect(); + Self { + z, + x, + phases, + coeffs, + num_qubits: value.num_qubits as usize, + } + } + } + + #[test] + fn decompose_empty_operator_fails() { + assert!(matches!( + decompose_dense_inner(aview2::(&[]), 0.0), + Err(DecomposeError::BadShape(_)), + )); + } + + #[test] + fn decompose_0q_operator() { + let coeff = Complex64::new(1.5, -0.5); + let arr = [[coeff]]; + let out = decompose_dense_inner(aview2(&arr), 0.0).unwrap(); + let expected = DecomposeMinimal { + z: vec![], + x: vec![], + phases: vec![], + coeffs: vec![coeff], + num_qubits: 0, + }; + assert_eq!(DecomposeMinimal::from(out), expected); + } + + #[test] + fn decompose_1q_operator() { + // Be sure that any sums are given in canonical order of the output, or there will be + // spurious test failures. + let paulis = [ + (vec![0], vec![0]), // I + (vec![1], vec![0]), // X + (vec![1], vec![1]), // Y + (vec![0], vec![1]), // Z + (vec![0, 1], vec![0, 0]), // I, X + (vec![0, 1], vec![0, 1]), // I, Y + (vec![0, 0], vec![0, 1]), // I, Z + (vec![1, 1], vec![0, 1]), // X, Y + (vec![1, 0], vec![1, 1]), // X, Z + (vec![1, 0], vec![1, 1]), // Y, Z + (vec![1, 1, 0], vec![0, 1, 1]), // X, Y, Z + ]; + let coeffs = [ + Complex64::new(1.5, -0.5), + Complex64::new(-0.25, 2.0), + Complex64::new(0.75, 0.75), + ]; + for (x_like, z_like) in paulis { + let paulis = MatrixCompressedPaulis { + num_qubits: 1, + coeffs: coeffs[0..x_like.len()].to_owned(), + x_like, + z_like, + }; + let arr = Array1::from_vec(to_matrix_dense_inner(&paulis, false)) + .into_shape((2, 2)) + .unwrap(); + let expected: DecomposeMinimal = paulis.into(); + let actual: DecomposeMinimal = decompose_dense_inner(arr.view(), 0.0).unwrap().into(); + assert_eq!(actual, expected); + } + } + + #[test] + fn decompose_3q_operator() { + // Be sure that any sums are given in canonical order of the output, or there will be + // spurious test failures. + let paulis = [ + (vec![0], vec![0]), // III + (vec![1], vec![0]), // IIX + (vec![2], vec![2]), // IYI + (vec![0], vec![4]), // ZII + (vec![6], vec![6]), // YYI + (vec![7], vec![7]), // YYY + (vec![1, 6, 7], vec![1, 6, 7]), // IIY, YYI, YYY + (vec![1, 2, 0], vec![0, 2, 4]), // IIX, IYI, ZII + ]; + let coeffs = [ + Complex64::new(1.5, -0.5), + Complex64::new(-0.25, 2.0), + Complex64::new(0.75, 0.75), + ]; + for (x_like, z_like) in paulis { + let paulis = MatrixCompressedPaulis { + num_qubits: 3, + coeffs: coeffs[0..x_like.len()].to_owned(), + x_like, + z_like, + }; + let arr = Array1::from_vec(to_matrix_dense_inner(&paulis, false)) + .into_shape((8, 8)) + .unwrap(); + let expected: DecomposeMinimal = paulis.into(); + let actual: DecomposeMinimal = decompose_dense_inner(arr.view(), 0.0).unwrap().into(); + assert_eq!(actual, expected); + } + } + #[test] fn dense_threaded_and_serial_equal() { let paulis = example_paulis(); diff --git a/crates/accelerate/src/split_2q_unitaries.rs b/crates/accelerate/src/split_2q_unitaries.rs index 6950ea590212..ac2577c2fc2c 100644 --- a/crates/accelerate/src/split_2q_unitaries.rs +++ b/crates/accelerate/src/split_2q_unitaries.rs @@ -28,6 +28,9 @@ pub fn split_2q_unitaries( dag: &mut DAGCircuit, requested_fidelity: f64, ) -> PyResult<()> { + if !dag.get_op_counts().contains_key("unitary") { + return Ok(()); + } let nodes: Vec = dag.op_nodes(false).collect(); for node in nodes { diff --git a/crates/circuit/src/circuit_instruction.rs b/crates/circuit/src/circuit_instruction.rs index 255343ac186c..e1bf3cfc2e55 100644 --- a/crates/circuit/src/circuit_instruction.rs +++ b/crates/circuit/src/circuit_instruction.rs @@ -656,7 +656,7 @@ impl<'py> FromPyObject<'py> for OperationFromPython { ob.getattr(intern!(py, "label"))?.extract()?, duration, unit, - ob.getattr(intern!(py, "condition"))?.extract()?, + ob.getattr(intern!(py, "_condition"))?.extract()?, )) }; diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index 73345f710083..e631b6971580 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -10,7 +10,7 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use ahash::RandomState; use smallvec::SmallVec; @@ -73,14 +73,55 @@ use std::cell::OnceCell; static CONTROL_FLOW_OP_NAMES: [&str; 4] = ["for_loop", "while_loop", "if_else", "switch_case"]; static SEMANTIC_EQ_SYMMETRIC: [&str; 4] = ["barrier", "swap", "break_loop", "continue_loop"]; +/// An opaque key type that identifies a variable within a [DAGCircuit]. +/// +/// When a new variable is added to the DAG, it is associated internally +/// with one of these keys. When enumerating DAG nodes and edges, you can +/// retrieve the associated variable instance via [DAGCircuit::get_var]. +/// +/// These keys are [Eq], but this is semantically valid only for keys +/// from the same [DAGCircuit] instance. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct Var(BitType); + +impl Var { + /// Construct a new [Var] object from a usize. if you have a u32 you can + /// create a [Var] object directly with `Var(0u32)`. This will panic + /// if the `usize` index exceeds `u32::MAX`. + #[inline(always)] + fn new(index: usize) -> Self { + Var(index + .try_into() + .unwrap_or_else(|_| panic!("Index value '{}' exceeds the maximum bit width!", index))) + } + + /// Get the index of the [Var] + #[inline(always)] + fn index(&self) -> usize { + self.0 as usize + } +} + +impl From for Var { + fn from(value: BitType) -> Self { + Var(value) + } +} + +impl From for BitType { + fn from(value: Var) -> Self { + value.0 + } +} + #[derive(Clone, Debug)] pub enum NodeType { QubitIn(Qubit), QubitOut(Qubit), ClbitIn(Clbit), ClbitOut(Clbit), - VarIn(PyObject), - VarOut(PyObject), + VarIn(Var), + VarOut(Var), Operation(PackedInstruction), } @@ -97,45 +138,21 @@ impl NodeType { } } -#[derive(Clone, Debug)] +#[derive(Hash, Eq, PartialEq, Clone, Debug)] pub enum Wire { Qubit(Qubit), Clbit(Clbit), - Var(PyObject), -} - -impl PartialEq for Wire { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Wire::Qubit(q1), Wire::Qubit(q2)) => q1 == q2, - (Wire::Clbit(c1), Wire::Clbit(c2)) => c1 == c2, - (Wire::Var(v1), Wire::Var(v2)) => { - v1.is(v2) || Python::with_gil(|py| v1.bind(py).eq(v2).unwrap()) - } - _ => false, - } - } -} - -impl Eq for Wire {} - -impl Hash for Wire { - fn hash(&self, state: &mut H) { - match self { - Self::Qubit(qubit) => qubit.hash(state), - Self::Clbit(clbit) => clbit.hash(state), - Self::Var(var) => Python::with_gil(|py| var.bind(py).hash().unwrap().hash(state)), - } - } + Var(Var), } impl Wire { fn to_pickle(&self, py: Python) -> PyObject { match self { - Self::Qubit(bit) => (0, bit.0.into_py(py)).into_py(py), - Self::Clbit(bit) => (1, bit.0.into_py(py)).into_py(py), - Self::Var(var) => (2, var.clone_ref(py)).into_py(py), + Self::Qubit(bit) => (0, bit.0.into_py(py)), + Self::Clbit(bit) => (1, bit.0.into_py(py)), + Self::Var(var) => (2, var.0.into_py(py)), } + .into_py(py) } fn from_pickle(b: &Bound) -> PyResult { @@ -146,84 +163,13 @@ impl Wire { } else if wire_type == 1 { Ok(Self::Clbit(Clbit(tuple.get_item(1)?.extract()?))) } else if wire_type == 2 { - Ok(Self::Var(tuple.get_item(1)?.unbind())) + Ok(Self::Var(Var(tuple.get_item(1)?.extract()?))) } else { Err(PyTypeError::new_err("Invalid wire type")) } } } -// TODO: Remove me. -// This is a temporary map type used to store a mapping of -// Var to NodeIndex to hold us over until Var is ported to -// Rust. Currently, we need this because PyObject cannot be -// used as the key to an IndexMap. -// -// Once we've got Var ported, Wire should also become Hash + Eq -// and we can consider combining input/output nodes maps. -#[derive(Clone, Debug)] -struct _VarIndexMap { - dict: Py, -} - -impl _VarIndexMap { - pub fn new(py: Python) -> Self { - Self { - dict: PyDict::new_bound(py).unbind(), - } - } - - pub fn keys(&self, py: Python) -> impl Iterator { - self.dict - .bind(py) - .keys() - .into_iter() - .map(|k| k.unbind()) - .collect::>() - .into_iter() - } - - pub fn contains_key(&self, py: Python, key: &PyObject) -> bool { - self.dict.bind(py).contains(key).unwrap() - } - - pub fn get(&self, py: Python, key: &PyObject) -> Option { - self.dict - .bind(py) - .get_item(key) - .unwrap() - .map(|v| NodeIndex::new(v.extract().unwrap())) - } - - pub fn insert(&mut self, py: Python, key: PyObject, value: NodeIndex) { - self.dict - .bind(py) - .set_item(key, value.index().into_py(py)) - .unwrap() - } - - pub fn remove(&mut self, py: Python, key: &PyObject) -> Option { - let bound_dict = self.dict.bind(py); - let res = bound_dict - .get_item(key.clone_ref(py)) - .unwrap() - .map(|v| NodeIndex::new(v.extract().unwrap())); - let _del_result = bound_dict.del_item(key); - res - } - pub fn values<'py>(&self, py: Python<'py>) -> impl Iterator + 'py { - let values = self.dict.bind(py).values(); - values.iter().map(|x| NodeIndex::new(x.extract().unwrap())) - } - - pub fn iter<'py>(&self, py: Python<'py>) -> impl Iterator + 'py { - self.dict - .bind(py) - .iter() - .map(|(var, index)| (var.unbind(), NodeIndex::new(index.extract().unwrap()))) - } -} - /// Quantum circuit as a directed acyclic graph. /// /// There are 3 types of nodes in the graph: inputs, outputs, and operations. @@ -257,6 +203,8 @@ pub struct DAGCircuit { qubits: BitData, /// Clbits registered in the circuit. clbits: BitData, + /// Variables registered in the circuit. + vars: BitData, /// Global phase. global_phase: Param, /// Duration. @@ -281,11 +229,8 @@ pub struct DAGCircuit { /// Map from clbit to input and output nodes of the graph. clbit_io_map: Vec<[NodeIndex; 2]>, - // TODO: use IndexMap once Var is ported to Rust - /// Map from var to input nodes of the graph. - var_input_map: _VarIndexMap, - /// Map from var to output nodes of the graph. - var_output_map: _VarIndexMap, + /// Map from var to input and output nodes of the graph. + var_io_map: Vec<[NodeIndex; 2]>, /// Operation kind to count op_names: IndexMap, @@ -438,6 +383,7 @@ impl DAGCircuit { cargs_interner: Interner::new(), qubits: BitData::new(py, "qubits".to_string()), clbits: BitData::new(py, "clbits".to_string()), + vars: BitData::new(py, "vars".to_string()), global_phase: Param::Float(0.), duration: None, unit: "dt".to_string(), @@ -445,8 +391,7 @@ impl DAGCircuit { clbit_locations: PyDict::new_bound(py).unbind(), qubit_io_map: Vec::new(), clbit_io_map: Vec::new(), - var_input_map: _VarIndexMap::new(py), - var_output_map: _VarIndexMap::new(py), + var_io_map: Vec::new(), op_names: IndexMap::default(), control_flow_module: PyControlFlowModule::new(py)?, vars_info: HashMap::new(), @@ -522,10 +467,15 @@ impl DAGCircuit { self.get_node(py, indices[0])?, )?; } - for (var, index) in self.var_input_map.dict.bind(py).iter() { + for (var, indices) in self + .var_io_map + .iter() + .enumerate() + .map(|(idx, indices)| (Var::new(idx), indices)) + { out_dict.set_item( - var, - self.get_node(py, NodeIndex::new(index.extract::()?))?, + self.vars.get(var).unwrap().clone_ref(py), + self.get_node(py, indices[0])?, )?; } Ok(out_dict.unbind()) @@ -556,10 +506,15 @@ impl DAGCircuit { self.get_node(py, indices[1])?, )?; } - for (var, index) in self.var_output_map.dict.bind(py).iter() { + for (var, indices) in self + .var_io_map + .iter() + .enumerate() + .map(|(idx, indices)| (Var::new(idx), indices)) + { out_dict.set_item( - var, - self.get_node(py, NodeIndex::new(index.extract::()?))?, + self.vars.get(var).unwrap().clone_ref(py), + self.get_node(py, indices[1])?, )?; } Ok(out_dict.unbind()) @@ -579,7 +534,7 @@ impl DAGCircuit { .iter() .enumerate() .map(|(k, v)| (k, [v[0].index(), v[1].index()])) - .collect::>(), + .into_py_dict_bound(py), )?; out_dict.set_item( "clbit_io_map", @@ -587,10 +542,16 @@ impl DAGCircuit { .iter() .enumerate() .map(|(k, v)| (k, [v[0].index(), v[1].index()])) - .collect::>(), + .into_py_dict_bound(py), + )?; + out_dict.set_item( + "var_io_map", + self.var_io_map + .iter() + .enumerate() + .map(|(k, v)| (k, [v[0].index(), v[1].index()])) + .into_py_dict_bound(py), )?; - out_dict.set_item("var_input_map", self.var_input_map.dict.clone_ref(py))?; - out_dict.set_item("var_output_map", self.var_output_map.dict.clone_ref(py))?; out_dict.set_item("op_name", self.op_names.clone())?; out_dict.set_item( "vars_info", @@ -607,11 +568,12 @@ impl DAGCircuit { ), ) }) - .collect::>(), + .into_py_dict_bound(py), )?; out_dict.set_item("vars_by_type", self.vars_by_type.clone())?; out_dict.set_item("qubits", self.qubits.bits())?; out_dict.set_item("clbits", self.clbits.bits())?; + out_dict.set_item("vars", self.vars.bits())?; let mut nodes: Vec = Vec::with_capacity(self.dag.node_count()); for node_idx in self.dag.node_indices() { let node_data = self.get_node(py, node_idx)?; @@ -656,12 +618,6 @@ impl DAGCircuit { self.cregs = dict_state.get_item("cregs")?.unwrap().extract()?; self.global_phase = dict_state.get_item("global_phase")?.unwrap().extract()?; self.op_names = dict_state.get_item("op_name")?.unwrap().extract()?; - self.var_input_map = _VarIndexMap { - dict: dict_state.get_item("var_input_map")?.unwrap().extract()?, - }; - self.var_output_map = _VarIndexMap { - dict: dict_state.get_item("var_output_map")?.unwrap().extract()?, - }; self.vars_by_type = dict_state.get_item("vars_by_type")?.unwrap().extract()?; let binding = dict_state.get_item("vars_info")?.unwrap(); let vars_info_raw = binding.downcast::().unwrap(); @@ -692,6 +648,11 @@ impl DAGCircuit { for bit in clbits_raw.iter() { self.clbits.add(py, &bit, false)?; } + let binding = dict_state.get_item("vars")?.unwrap(); + let vars_raw = binding.downcast::().unwrap(); + for bit in vars_raw.iter() { + self.vars.add(py, &bit, false)?; + } let binding = dict_state.get_item("qubit_io_map")?.unwrap(); let qubit_index_map_raw = binding.downcast::().unwrap(); self.qubit_io_map = Vec::with_capacity(qubit_index_map_raw.len()); @@ -703,12 +664,19 @@ impl DAGCircuit { let binding = dict_state.get_item("clbit_io_map")?.unwrap(); let clbit_index_map_raw = binding.downcast::().unwrap(); self.clbit_io_map = Vec::with_capacity(clbit_index_map_raw.len()); - for (_k, v) in clbit_index_map_raw.iter() { let indices: [usize; 2] = v.extract()?; self.clbit_io_map .push([NodeIndex::new(indices[0]), NodeIndex::new(indices[1])]); } + let binding = dict_state.get_item("var_io_map")?.unwrap(); + let var_index_map_raw = binding.downcast::().unwrap(); + self.var_io_map = Vec::with_capacity(var_index_map_raw.len()); + for (_k, v) in var_index_map_raw.iter() { + let indices: [usize; 2] = v.extract()?; + self.var_io_map + .push([NodeIndex::new(indices[0]), NodeIndex::new(indices[1])]); + } // Rebuild Graph preserving index holes: let binding = dict_state.get_item("nodes")?.unwrap(); let nodes_lst = binding.downcast::()?; @@ -1237,7 +1205,7 @@ def _format(operand): let clbits: HashSet = bit_iter.collect(); let mut busy_bits = Vec::new(); for bit in clbits.iter() { - if !self.is_wire_idle(py, &Wire::Clbit(*bit))? { + if !self.is_wire_idle(&Wire::Clbit(*bit))? { busy_bits.push(self.clbits.get(*bit).unwrap()); } } @@ -1264,7 +1232,7 @@ def _format(operand): // Remove DAG in/out nodes etc. for bit in clbits.iter() { - self.remove_idle_wire(py, Wire::Clbit(*bit))?; + self.remove_idle_wire(Wire::Clbit(*bit))?; } // Copy the current clbit mapping so we can use it while remapping @@ -1445,7 +1413,7 @@ def _format(operand): let mut busy_bits = Vec::new(); for bit in qubits.iter() { - if !self.is_wire_idle(py, &Wire::Qubit(*bit))? { + if !self.is_wire_idle(&Wire::Qubit(*bit))? { busy_bits.push(self.qubits.get(*bit).unwrap()); } } @@ -1472,7 +1440,7 @@ def _format(operand): // Remove DAG in/out nodes etc. for bit in qubits.iter() { - self.remove_idle_wire(py, Wire::Qubit(*bit))?; + self.remove_idle_wire(Wire::Qubit(*bit))?; } // Copy the current qubit mapping so we can use it while remapping @@ -2198,7 +2166,7 @@ def _format(operand): let wires = (0..self.qubit_io_map.len()) .map(|idx| Wire::Qubit(Qubit::new(idx))) .chain((0..self.clbit_io_map.len()).map(|idx| Wire::Clbit(Clbit::new(idx)))) - .chain(self.var_input_map.keys(py).map(Wire::Var)); + .chain((0..self.var_io_map.len()).map(|idx| Wire::Var(Var::new(idx)))); match ignore { Some(ignore) => { // Convert the list to a Rust set. @@ -2207,7 +2175,7 @@ def _format(operand): .map(|s| s.extract()) .collect::>>()?; for wire in wires { - let nodes_found = self.nodes_on_wire(py, &wire, true).into_iter().any(|node| { + let nodes_found = self.nodes_on_wire(&wire, true).into_iter().any(|node| { let weight = self.dag.node_weight(node).unwrap(); if let NodeType::Operation(packed) = weight { !ignore_set.contains(packed.op.name()) @@ -2220,18 +2188,18 @@ def _format(operand): result.push(match wire { Wire::Qubit(qubit) => self.qubits.get(qubit).unwrap().clone_ref(py), Wire::Clbit(clbit) => self.clbits.get(clbit).unwrap().clone_ref(py), - Wire::Var(var) => var, + Wire::Var(var) => self.vars.get(var).unwrap().clone_ref(py), }); } } } None => { for wire in wires { - if self.is_wire_idle(py, &wire)? { + if self.is_wire_idle(&wire)? { result.push(match wire { Wire::Qubit(qubit) => self.qubits.get(qubit).unwrap().clone_ref(py), Wire::Clbit(clbit) => self.clbits.get(clbit).unwrap().clone_ref(py), - Wire::Var(var) => var, + Wire::Var(var) => self.vars.get(var).unwrap().clone_ref(py), }); } } @@ -2665,8 +2633,18 @@ def _format(operand): [NodeType::ClbitIn(bit1), NodeType::ClbitIn(bit2)] => Ok(bit1 == bit2), [NodeType::QubitOut(bit1), NodeType::QubitOut(bit2)] => Ok(bit1 == bit2), [NodeType::ClbitOut(bit1), NodeType::ClbitOut(bit2)] => Ok(bit1 == bit2), - [NodeType::VarIn(var1), NodeType::VarIn(var2)] => var1.bind(py).eq(var2), - [NodeType::VarOut(var1), NodeType::VarOut(var2)] => var1.bind(py).eq(var2), + [NodeType::VarIn(var1), NodeType::VarIn(var2)] => self + .vars + .get(*var1) + .unwrap() + .bind(py) + .eq(other.vars.get(*var2).unwrap()), + [NodeType::VarOut(var1), NodeType::VarOut(var2)] => self + .vars + .get(*var1) + .unwrap() + .bind(py) + .eq(other.vars.get(*var2).unwrap()), _ => Ok(false), } }; @@ -3130,7 +3108,7 @@ def _format(operand): .edges_directed(node_index, Incoming) .find(|edge| { if let Wire::Var(var) = edge.weight() { - contracted_var.eq(var).unwrap() + contracted_var.eq(self.vars.get(*var)).unwrap() } else { false } @@ -3141,7 +3119,7 @@ def _format(operand): .edges_directed(node_index, Outgoing) .find(|edge| { if let Wire::Var(var) = edge.weight() { - contracted_var.eq(var).unwrap() + contracted_var.eq(self.vars.get(*var)).unwrap() } else { false } @@ -3150,7 +3128,7 @@ def _format(operand): self.dag.add_edge( pred.source(), succ.target(), - Wire::Var(contracted_var.unbind()), + Wire::Var(self.vars.find(&contracted_var).unwrap()), ); } @@ -3499,7 +3477,11 @@ def _format(operand): let (additional_clbits, additional_vars) = self.additional_wires(py, new_op.operation.view(), new_op.extra_attrs.condition())?; new_wires.extend(additional_clbits.iter().map(|x| Wire::Clbit(*x))); - new_wires.extend(additional_vars.iter().map(|x| Wire::Var(x.clone_ref(py)))); + new_wires.extend( + additional_vars + .iter() + .map(|x| Wire::Var(self.vars.find(x.bind(py)).unwrap())), + ); if old_packed.op.num_qubits() != new_op.operation.num_qubits() || old_packed.op.num_clbits() != new_op.operation.num_clbits() @@ -3659,11 +3641,11 @@ def _format(operand): non_classical = true; } NodeType::VarIn(v) => { - let var_in = new_dag.var_input_map.get(py, v).unwrap(); + let var_in = new_dag.var_io_map[v.index()][0]; node_map.insert(*node, var_in); } NodeType::VarOut(v) => { - let var_out = new_dag.var_output_map.get(py, v).unwrap(); + let var_out = new_dag.var_io_map[v.index()][1]; node_map.insert(*node, var_out); } NodeType::Operation(pi) => { @@ -3717,12 +3699,11 @@ def _format(operand): .add_edge(*in_node, *out_node, Wire::Clbit(clbit)); } } - for (var, in_node) in new_dag.var_input_map.iter(py) { + for (var_index, &[in_node, out_node]) in new_dag.var_io_map.iter().enumerate() { if new_dag.dag.edges(in_node).next().is_none() { - let out_node = new_dag.var_output_map.get(py, &var).unwrap(); new_dag .dag - .add_edge(in_node, out_node, Wire::Var(var.clone_ref(py))); + .add_edge(in_node, out_node, Wire::Var(Var::new(var_index))); } } if remove_idle_qubits { @@ -3892,7 +3873,7 @@ def _format(operand): match edge.weight() { Wire::Qubit(qubit) => self.qubits.get(*qubit).unwrap(), Wire::Clbit(clbit) => self.clbits.get(*clbit).unwrap(), - Wire::Var(var) => var, + Wire::Var(var) => self.vars.get(*var).unwrap(), }, )) } @@ -4326,7 +4307,7 @@ def _format(operand): /// Return a list of op nodes in the first layer of this dag. #[pyo3(name = "front_layer")] fn py_front_layer(&self, py: Python) -> PyResult> { - let native_front_layer = self.front_layer(py); + let native_front_layer = self.front_layer(); let front_layer_list = PyList::empty_bound(py); for node in native_front_layer { front_layer_list.append(self.get_node(py, node)?)?; @@ -4353,7 +4334,7 @@ def _format(operand): #[pyo3(signature = (*, vars_mode="captures"))] fn layers(&self, py: Python, vars_mode: &str) -> PyResult> { let layer_list = PyList::empty_bound(py); - let mut graph_layers = self.multigraph_layers(py); + let mut graph_layers = self.multigraph_layers(); if graph_layers.next().is_none() { return Ok(PyIterator::from_bound_object(&layer_list)?.into()); } @@ -4453,7 +4434,7 @@ def _format(operand): /// Yield layers of the multigraph. #[pyo3(name = "multigraph_layers")] fn py_multigraph_layers(&self, py: Python) -> PyResult> { - let graph_layers = self.multigraph_layers(py).map(|layer| -> Vec { + let graph_layers = self.multigraph_layers().map(|layer| -> Vec { layer .into_iter() .filter_map(|index| self.get_node(py, index).ok()) @@ -4568,10 +4549,8 @@ def _format(operand): self.qubits.find(wire).map(Wire::Qubit) } else if wire.is_instance(imports::CLBIT.get_bound(py))? { self.clbits.find(wire).map(Wire::Clbit) - } else if self.var_input_map.contains_key(py, &wire.clone().unbind()) { - Some(Wire::Var(wire.clone().unbind())) } else { - None + self.vars.find(wire).map(Wire::Var) } .ok_or_else(|| { DAGCircuitError::new_err(format!( @@ -4581,7 +4560,7 @@ def _format(operand): })?; let nodes = self - .nodes_on_wire(py, &wire, only_ops) + .nodes_on_wire(&wire, only_ops) .into_iter() .map(|n| self.get_node(py, n)) .collect::>>()?; @@ -4793,7 +4772,8 @@ def _format(operand): "cannot add inputs to a circuit with captures", )); } - self.add_var(py, var, DAGVarType::Input) + self.add_var(py, var, DAGVarType::Input)?; + Ok(()) } /// Add a captured variable to the circuit. @@ -4809,7 +4789,8 @@ def _format(operand): "cannot add captures to a circuit with inputs", )); } - self.add_var(py, var, DAGVarType::Capture) + self.add_var(py, var, DAGVarType::Capture)?; + Ok(()) } /// Add a declared local variable to the circuit. @@ -4817,7 +4798,8 @@ def _format(operand): /// Args: /// var: the variable to add. fn add_declared_var(&mut self, py: Python, var: &Bound) -> PyResult<()> { - self.add_var(py, var, DAGVarType::Declare) + self.add_var(py, var, DAGVarType::Declare)?; + Ok(()) } /// Total number of classical variables tracked by the circuit. @@ -4926,7 +4908,7 @@ def _format(operand): match wire.weight() { Wire::Qubit(qubit) => self.qubits.get(*qubit).unwrap(), Wire::Clbit(clbit) => self.clbits.get(*clbit).unwrap(), - Wire::Var(var) => var, + Wire::Var(var) => self.vars.get(*var).unwrap(), }, ) .into_py(py) @@ -4944,7 +4926,7 @@ def _format(operand): match wire.weight() { Wire::Qubit(qubit) => self.qubits.get(*qubit).unwrap(), Wire::Clbit(clbit) => self.clbits.get(*clbit).unwrap(), - Wire::Var(var) => var, + Wire::Var(var) => self.vars.get(*var).unwrap(), }, ) .into_py(py) @@ -4958,7 +4940,7 @@ def _format(operand): .map(|wire| match wire.weight() { Wire::Qubit(qubit) => self.qubits.get(*qubit).unwrap(), Wire::Clbit(clbit) => self.clbits.get(*clbit).unwrap(), - Wire::Var(var) => var, + Wire::Var(var) => self.vars.get(*var).unwrap(), }) .collect() } @@ -4969,7 +4951,7 @@ def _format(operand): .map(|wire| match wire.weight() { Wire::Qubit(qubit) => self.qubits.get(*qubit).unwrap(), Wire::Clbit(clbit) => self.clbits.get(*clbit).unwrap(), - Wire::Var(var) => var, + Wire::Var(var) => self.vars.get(*var).unwrap(), }) .collect() } @@ -4989,7 +4971,7 @@ def _format(operand): let weight = match e.weight() { Wire::Qubit(q) => self.qubits.get(*q).unwrap(), Wire::Clbit(c) => self.clbits.get(*c).unwrap(), - Wire::Var(v) => v, + Wire::Var(v) => self.vars.get(*v).unwrap(), }; if edge_checker.call1((weight,))?.extract::()? { result.push(self.get_node(py, e.target())?); @@ -5006,7 +4988,7 @@ def _format(operand): match wire { Wire::Qubit(qubit) => self.qubits.get(*qubit).to_object(py), Wire::Clbit(clbit) => self.clbits.get(*clbit).to_object(py), - Wire::Var(var) => var.clone_ref(py), + Wire::Var(var) => self.vars.get(*var).to_object(py), } }) .collect() @@ -5050,6 +5032,12 @@ impl DAGCircuit { &self.clbits } + /// Returns an immutable view of the Variable wires registered in the circuit + #[inline(always)] + pub fn vars(&self) -> &BitData { + &self.vars + } + /// Return an iterator of gate runs with non-conditional op nodes of given names pub fn collect_runs( &self, @@ -5208,11 +5196,12 @@ impl DAGCircuit { .iter() .map(|c| self.clbit_io_map.get(c.index()).map(|x| x[1]).unwrap()), ) - .chain( - vars.iter() - .flatten() - .map(|v| self.var_output_map.get(py, v).unwrap()), - ) + .chain(vars.iter().flatten().map(|v| { + self.var_io_map + .get(self.vars.find(v.bind(py)).unwrap().index()) + .map(|x| x[1]) + .unwrap() + })) .collect(); for output_node in output_nodes { @@ -5273,7 +5262,7 @@ impl DAGCircuit { .collect(); if let Some(vars) = vars { for var in vars { - input_nodes.push(self.var_input_map.get(py, &var).unwrap()); + input_nodes.push(self.var_io_map[self.vars.find(var.bind(py)).unwrap().index()][0]); } } @@ -5469,7 +5458,7 @@ impl DAGCircuit { .any(|x| self.op_names.contains_key(&x.to_string())) } - fn is_wire_idle(&self, py: Python, wire: &Wire) -> PyResult { + fn is_wire_idle(&self, wire: &Wire) -> PyResult { let (input_node, output_node) = match wire { Wire::Qubit(qubit) => ( self.qubit_io_map[qubit.index()][0], @@ -5480,8 +5469,8 @@ impl DAGCircuit { self.clbit_io_map[clbit.index()][1], ), Wire::Var(var) => ( - self.var_input_map.get(py, var).unwrap(), - self.var_output_map.get(py, var).unwrap(), + self.var_io_map[var.index()][0], + self.var_io_map[var.index()][1], ), }; @@ -5616,9 +5605,12 @@ impl DAGCircuit { /// /// This adds a pair of in and out nodes connected by an edge. /// + /// Returns: + /// The input and output node indices of the added wire, respectively. + /// /// Raises: /// DAGCircuitError: if trying to add duplicate wire - fn add_wire(&mut self, py: Python, wire: Wire) -> PyResult<()> { + fn add_wire(&mut self, wire: Wire) -> PyResult<(NodeIndex, NodeIndex)> { let (in_node, out_node) = match wire { Wire::Qubit(qubit) => { if (qubit.index()) >= self.qubit_io_map.len() { @@ -5640,33 +5632,31 @@ impl DAGCircuit { Err(DAGCircuitError::new_err("classical wire already exists!")) } } - Wire::Var(ref var) => { - if self.var_input_map.contains_key(py, var) - || self.var_output_map.contains_key(py, var) - { + Wire::Var(var) => { + if var.index() >= self.var_io_map.len() { + let in_node = self.dag.add_node(NodeType::VarIn(var)); + let out_node = self.dag.add_node(NodeType::VarOut(var)); + self.var_io_map.push([in_node, out_node]); + Ok((in_node, out_node)) + } else { return Err(DAGCircuitError::new_err("var wire already exists!")); } - let in_node = self.dag.add_node(NodeType::VarIn(var.clone_ref(py))); - let out_node = self.dag.add_node(NodeType::VarOut(var.clone_ref(py))); - self.var_input_map.insert(py, var.clone_ref(py), in_node); - self.var_output_map.insert(py, var.clone_ref(py), out_node); - Ok((in_node, out_node)) } }?; self.dag.add_edge(in_node, out_node, wire); - Ok(()) + Ok((in_node, out_node)) } /// Get the nodes on the given wire. /// /// Note: result is empty if the wire is not in the DAG. - pub fn nodes_on_wire(&self, py: Python, wire: &Wire, only_ops: bool) -> Vec { + pub fn nodes_on_wire(&self, wire: &Wire, only_ops: bool) -> Vec { let mut nodes = Vec::new(); let mut current_node = match wire { Wire::Qubit(qubit) => self.qubit_io_map.get(qubit.index()).map(|x| x[0]), Wire::Clbit(clbit) => self.clbit_io_map.get(clbit.index()).map(|x| x[0]), - Wire::Var(var) => self.var_input_map.get(py, var), + Wire::Var(var) => self.var_io_map.get(var.index()).map(|x| x[0]), }; while let Some(node) = current_node { @@ -5691,14 +5681,11 @@ impl DAGCircuit { nodes } - fn remove_idle_wire(&mut self, py: Python, wire: Wire) -> PyResult<()> { + fn remove_idle_wire(&mut self, wire: Wire) -> PyResult<()> { let [in_node, out_node] = match wire { Wire::Qubit(qubit) => self.qubit_io_map[qubit.index()], Wire::Clbit(clbit) => self.clbit_io_map[clbit.index()], - Wire::Var(var) => [ - self.var_input_map.remove(py, &var).unwrap(), - self.var_output_map.remove(py, &var).unwrap(), - ], + Wire::Var(var) => self.var_io_map[var.index()], }; self.dag.remove_node(in_node); self.dag.remove_node(out_node); @@ -5717,7 +5704,7 @@ impl DAGCircuit { }, )?, )?; - self.add_wire(py, Wire::Qubit(qubit))?; + self.add_wire(Wire::Qubit(qubit))?; Ok(qubit) } @@ -5733,7 +5720,7 @@ impl DAGCircuit { }, )?, )?; - self.add_wire(py, Wire::Clbit(clbit))?; + self.add_wire(Wire::Clbit(clbit))?; Ok(clbit) } @@ -5802,7 +5789,7 @@ impl DAGCircuit { } else if wire.is_instance(imports::CLBIT.get_bound(py))? { NodeType::ClbitIn(self.clbits.find(wire).unwrap()) } else { - NodeType::VarIn(wire.clone().unbind()) + NodeType::VarIn(self.vars.find(wire).unwrap()) } } else if let Ok(out_node) = b.downcast::() { let out_node = out_node.borrow(); @@ -5812,7 +5799,7 @@ impl DAGCircuit { } else if wire.is_instance(imports::CLBIT.get_bound(py))? { NodeType::ClbitOut(self.clbits.find(wire).unwrap()) } else { - NodeType::VarIn(wire.clone().unbind()) + NodeType::VarIn(self.vars.find(wire).unwrap()) } } else if let Ok(op_node) = b.downcast::() { let op_node = op_node.borrow(); @@ -5890,12 +5877,16 @@ impl DAGCircuit { )? .into_any() } - NodeType::VarIn(var) => { - Py::new(py, DAGInNode::new(py, id, var.clone_ref(py)))?.into_any() - } - NodeType::VarOut(var) => { - Py::new(py, DAGOutNode::new(py, id, var.clone_ref(py)))?.into_any() - } + NodeType::VarIn(var) => Py::new( + py, + DAGInNode::new(py, id, self.vars.get(*var).unwrap().clone_ref(py)), + )? + .into_any(), + NodeType::VarOut(var) => Py::new( + py, + DAGOutNode::new(py, id, self.vars.get(*var).unwrap().clone_ref(py)), + )? + .into_any(), }; Ok(dag_node) } @@ -5980,10 +5971,10 @@ impl DAGCircuit { } /// Returns an iterator over a list layers of the `DAGCircuit``. - pub fn multigraph_layers(&self, py: Python) -> impl Iterator> + '_ { + pub fn multigraph_layers(&self) -> impl Iterator> + '_ { let mut first_layer: Vec<_> = self.qubit_io_map.iter().map(|x| x[0]).collect(); first_layer.extend(self.clbit_io_map.iter().map(|x| x[0])); - first_layer.extend(self.var_input_map.values(py)); + first_layer.extend(self.var_io_map.iter().map(|x| x[0])); // A DAG is by definition acyclical, therefore unwrapping the layer should never fail. layers(&self.dag, first_layer).map(|layer| match layer { Ok(layer) => layer, @@ -5992,17 +5983,14 @@ impl DAGCircuit { } /// Returns an iterator over the first layer of the `DAGCircuit``. - pub fn front_layer<'a>(&'a self, py: Python) -> Box + 'a> { - let mut graph_layers = self.multigraph_layers(py); + pub fn front_layer(&self) -> impl Iterator + '_ { + let mut graph_layers = self.multigraph_layers(); graph_layers.next(); - - let next_layer = graph_layers.next(); - match next_layer { - Some(layer) => Box::new(layer.into_iter().filter(|node| { - matches!(self.dag.node_weight(*node).unwrap(), NodeType::Operation(_)) - })), - None => Box::new(vec![].into_iter()), - } + graph_layers + .next() + .into_iter() + .flatten() + .filter(|node| matches!(self.dag.node_weight(*node).unwrap(), NodeType::Operation(_))) } fn substitute_node_with_subgraph( @@ -6090,7 +6078,7 @@ impl DAGCircuit { .any(|edge| match edge.weight() { Wire::Qubit(qubit) => !qubit_map.contains_key(qubit), Wire::Clbit(clbit) => !clbit_map.contains_key(clbit), - Wire::Var(var) => !bound_var_map.contains(var).unwrap(), + Wire::Var(var) => !bound_var_map.contains(other.vars.get(*var)).unwrap(), }), _ => false, } @@ -6154,7 +6142,11 @@ impl DAGCircuit { match edge.weight() { Wire::Qubit(qubit) => Wire::Qubit(qubit_map[qubit]), Wire::Clbit(clbit) => Wire::Clbit(clbit_map[clbit]), - Wire::Var(var) => Wire::Var(bound_var_map.get_item(var)?.unwrap().unbind()), + Wire::Var(var) => Wire::Var( + self.vars + .find(&bound_var_map.get_item(other.vars.get(*var))?.unwrap()) + .unwrap(), + ), }, ); } @@ -6174,9 +6166,13 @@ impl DAGCircuit { .clbit_io_map .get(reverse_clbit_map[&clbit].index()) .map(|x| x[0]), - Wire::Var(ref var) => { - let index = &reverse_var_map.get_item(var)?.unwrap().unbind(); - other.var_input_map.get(py, index) + Wire::Var(var) => { + let index = other + .vars + .find(&reverse_var_map.get_item(self.vars.get(var))?.unwrap()) + .unwrap() + .index(); + other.var_io_map.get(index).map(|x| x[0]) } }; let old_index = @@ -6210,9 +6206,13 @@ impl DAGCircuit { .clbit_io_map .get(reverse_clbit_map[&clbit].index()) .map(|x| x[1]), - Wire::Var(ref var) => { - let index = &reverse_var_map.get_item(var)?.unwrap().unbind(); - other.var_output_map.get(py, index) + Wire::Var(var) => { + let index = other + .vars + .find(&reverse_var_map.get_item(self.vars.get(var))?.unwrap()) + .unwrap() + .index(); + other.var_io_map.get(index).map(|x| x[1]) } }; let old_index = @@ -6239,7 +6239,14 @@ impl DAGCircuit { Ok(out_map) } - fn add_var(&mut self, py: Python, var: &Bound, type_: DAGVarType) -> PyResult<()> { + /// Retrieve a variable given its unique [Var] key within the DAG. + /// + /// The provided [Var] must be from this [DAGCircuit]. + pub fn get_var<'py>(&self, py: Python<'py>, var: Var) -> Option> { + self.vars.get(var).map(|v| v.bind(py).clone()) + } + + fn add_var(&mut self, py: Python, var: &Bound, type_: DAGVarType) -> PyResult { // The setup of the initial graph structure between an "in" and an "out" node is the same as // the bit-related `_add_wire`, but this logically needs to do different bookkeeping around // tracking the properties @@ -6257,16 +6264,9 @@ impl DAGCircuit { "cannot add var as its name shadows an existing var", )); } - let in_node = NodeType::VarIn(var.clone().unbind()); - let out_node = NodeType::VarOut(var.clone().unbind()); - let in_index = self.dag.add_node(in_node); - let out_index = self.dag.add_node(out_node); - self.dag - .add_edge(in_index, out_index, Wire::Var(var.clone().unbind())); - self.var_input_map - .insert(py, var.clone().unbind(), in_index); - self.var_output_map - .insert(py, var.clone().unbind(), out_index); + + let var_idx = self.vars.add(py, var, true)?; + let (in_index, out_index) = self.add_wire(Wire::Var(var_idx))?; self.vars_by_type[type_ as usize] .bind(py) .add(var.clone().unbind())?; @@ -6279,7 +6279,7 @@ impl DAGCircuit { out_node: out_index, }, ); - Ok(()) + Ok(var_idx) } fn check_op_addition(&self, py: Python, inst: &PackedInstruction) -> PyResult<()> { @@ -6316,7 +6316,8 @@ impl DAGCircuit { } } for v in vars { - if !self.var_output_map.contains_key(py, &v) { + let var_idx = self.vars.find(v.bind(py)).unwrap(); + if !self.var_io_map.len() - 1 < var_idx.index() { return Err(DAGCircuitError::new_err(format!( "var {} not found in output map", v @@ -6369,6 +6370,7 @@ impl DAGCircuit { cargs_interner: Interner::with_capacity(num_clbits), qubits: BitData::with_capacity(py, "qubits".to_string(), num_qubits), clbits: BitData::with_capacity(py, "clbits".to_string(), num_clbits), + vars: BitData::with_capacity(py, "vars".to_string(), num_vars), global_phase: Param::Float(0.), duration: None, unit: "dt".to_string(), @@ -6376,8 +6378,7 @@ impl DAGCircuit { clbit_locations: PyDict::new_bound(py).unbind(), qubit_io_map: Vec::with_capacity(num_qubits), clbit_io_map: Vec::with_capacity(num_clbits), - var_input_map: _VarIndexMap::new(py), - var_output_map: _VarIndexMap::new(py), + var_io_map: Vec::with_capacity(num_vars), op_names: IndexMap::default(), control_flow_module: PyControlFlowModule::new(py)?, vars_info: HashMap::with_capacity(num_vars), @@ -6737,7 +6738,8 @@ impl DAGCircuit { } else { // If the var is not in the last nodes collection, the edge between the output node and its predecessor. // Then, store the predecessor's NodeIndex in the last nodes collection. - let output_node = self.var_output_map.get(py, var).unwrap(); + let var_idx = self.vars.find(var.bind(py)).unwrap(); + let output_node = self.var_io_map.get(var_idx.index()).unwrap()[1]; let (edge_id, predecessor_node) = self .dag .edges_directed(output_node, Incoming) @@ -6754,8 +6756,11 @@ impl DAGCircuit { if var_last_node == new_node { continue; } - self.dag - .add_edge(var_last_node, new_node, Wire::Var(var.clone_ref(py))); + self.dag.add_edge( + var_last_node, + new_node, + Wire::Var(self.vars.find(var.bind(py)).unwrap()), + ); } } @@ -6774,7 +6779,8 @@ impl DAGCircuit { // Add the output_nodes back to vars for item in vars_last_nodes.items() { let (var, node): (PyObject, usize) = item.extract()?; - let output_node = self.var_output_map.get(py, &var).unwrap(); + let var = self.vars.find(var.bind(py)).unwrap(); + let output_node = self.var_io_map.get(var.index()).unwrap()[1]; self.dag .add_edge(NodeIndex::new(node), output_node, Wire::Var(var)); } diff --git a/crates/circuit/src/dot_utils.rs b/crates/circuit/src/dot_utils.rs index 8558535788a0..c31488e92c81 100644 --- a/crates/circuit/src/dot_utils.rs +++ b/crates/circuit/src/dot_utils.rs @@ -59,7 +59,7 @@ where let edge_weight = match edge.weight() { Wire::Qubit(qubit) => dag.qubits().get(*qubit).unwrap(), Wire::Clbit(clbit) => dag.clbits().get(*clbit).unwrap(), - Wire::Var(var) => var, + Wire::Var(var) => dag.vars().get(*var).unwrap(), }; writeln!( file, diff --git a/crates/circuit/src/operations.rs b/crates/circuit/src/operations.rs index 67edb3aa0adc..f8c1e68c0da9 100644 --- a/crates/circuit/src/operations.rs +++ b/crates/circuit/src/operations.rs @@ -27,7 +27,7 @@ use smallvec::{smallvec, SmallVec}; use numpy::IntoPyArray; use numpy::PyReadonlyArray2; use pyo3::prelude::*; -use pyo3::types::{IntoPyDict, PyFloat, PyIterator, PyTuple}; +use pyo3::types::{IntoPyDict, PyFloat, PyIterator, PyList, PyTuple}; use pyo3::{intern, IntoPy, Python}; #[derive(Clone, Debug)] @@ -705,6 +705,11 @@ impl StandardGate { self.num_qubits() } + #[getter] + pub fn get_num_ctrl_qubits(&self) -> u32 { + self.num_ctrl_qubits() + } + #[getter] pub fn get_num_clbits(&self) -> u32 { self.num_clbits() @@ -720,6 +725,24 @@ impl StandardGate { self.name() } + #[getter] + pub fn is_controlled_gate(&self) -> bool { + self.num_ctrl_qubits() > 0 + } + + #[getter] + pub fn get_gate_class(&self, py: Python) -> PyResult> { + get_std_gate_class(py, *self) + } + + #[staticmethod] + pub fn all_gates(py: Python) -> Bound { + PyList::new_bound( + py, + (0..STANDARD_GATE_SIZE as u8).map(::bytemuck::checked::cast::<_, Self>), + ) + } + pub fn __hash__(&self) -> isize { *self as isize } diff --git a/crates/qasm3/Cargo.toml b/crates/qasm3/Cargo.toml index 8e42f3f0701b..eb7b7137c7ab 100644 --- a/crates/qasm3/Cargo.toml +++ b/crates/qasm3/Cargo.toml @@ -13,5 +13,5 @@ doctest = false pyo3.workspace = true indexmap.workspace = true hashbrown.workspace = true -oq3_semantics = "0.6.0" +oq3_semantics = "0.7.0" ahash.workspace = true diff --git a/qiskit/circuit/_standard_gates_commutations.py b/qiskit/circuit/_standard_gates_commutations.py index 9dc95b675db7..12899207b7f3 100644 --- a/qiskit/circuit/_standard_gates_commutations.py +++ b/qiskit/circuit/_standard_gates_commutations.py @@ -47,6 +47,7 @@ first cx. """ + standard_gates_commutations = { ("id", "id"): True, ("id", "sx"): True, @@ -70,6 +71,10 @@ ("id", "iswap"): True, ("id", "sxdg"): True, ("id", "tdg"): True, + ("id", "rxx"): True, + ("id", "ryy"): True, + ("id", "rzz"): True, + ("id", "rzx"): True, ("sx", "sx"): True, ("sx", "cx"): { (0,): False, @@ -109,6 +114,13 @@ ("sx", "iswap"): False, ("sx", "sxdg"): True, ("sx", "tdg"): False, + ("sx", "rxx"): True, + ("sx", "ryy"): False, + ("sx", "rzz"): False, + ("sx", "rzx"): { + (0,): False, + (1,): True, + }, ("x", "id"): True, ("x", "sx"): True, ("x", "x"): True, @@ -152,6 +164,13 @@ ("x", "tdg"): False, ("x", "y"): False, ("x", "z"): False, + ("x", "rxx"): True, + ("x", "ryy"): False, + ("x", "rzz"): False, + ("x", "rzx"): { + (0,): False, + (1,): True, + }, ("cx", "cx"): { (0, 1): True, (0, None): True, @@ -303,6 +322,31 @@ }, ("cx", "swap"): False, ("cx", "iswap"): False, + ("cx", "rxx"): { + (0, 1): False, + (0, None): False, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): True, + }, + ("cx", "ryy"): False, + ("cx", "rzz"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("cx", "rzx"): { + (0, 1): True, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, ("c3sx", "c3sx"): { (0, 1, 2, 3): True, (0, 1, 2, None): True, @@ -1029,6 +1073,10 @@ ("dcx", "csdg"): False, ("dcx", "swap"): False, ("dcx", "iswap"): False, + ("dcx", "rxx"): False, + ("dcx", "ryy"): False, + ("dcx", "rzz"): False, + ("dcx", "rzx"): False, ("ch", "cx"): { (0, 1): False, (0, None): True, @@ -1189,6 +1237,24 @@ }, ("ch", "swap"): False, ("ch", "iswap"): False, + ("ch", "rxx"): False, + ("ch", "ryy"): False, + ("ch", "rzz"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("ch", "rzx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, ("cswap", "c3sx"): { (0, 1, 2): True, (0, 1, 3): False, @@ -1499,6 +1565,31 @@ }, ("csx", "swap"): False, ("csx", "iswap"): False, + ("csx", "rxx"): { + (0, 1): False, + (0, None): False, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): True, + }, + ("csx", "ryy"): False, + ("csx", "rzz"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("csx", "rzx"): { + (0, 1): True, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, ("cy", "c3sx"): { (0, 1): False, (0, 2): False, @@ -1635,6 +1726,31 @@ }, ("cy", "swap"): False, ("cy", "iswap"): False, + ("cy", "rxx"): False, + ("cy", "ryy"): { + (0, 1): False, + (0, None): False, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): True, + }, + ("cy", "rzz"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("cy", "rzx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, ("cz", "c3sx"): { (0, 1): True, (0, 2): True, @@ -1750,6 +1866,17 @@ (None, 0): False, (None, 1): False, }, + ("cz", "rxx"): False, + ("cz", "ryy"): False, + ("cz", "rzz"): True, + ("cz", "rzx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): False, + }, ("ccz", "c3sx"): { (0, 1, 2): True, (0, 1, 3): False, @@ -2000,6 +2127,10 @@ ("h", "tdg"): False, ("h", "y"): False, ("h", "z"): False, + ("h", "rxx"): False, + ("h", "ryy"): False, + ("h", "rzz"): False, + ("h", "rzx"): False, ("rccx", "c3sx"): { (0, 1, 2): False, (0, 1, 3): False, @@ -2479,6 +2610,24 @@ ("ecr", "csdg"): False, ("ecr", "swap"): False, ("ecr", "iswap"): False, + ("ecr", "rxx"): { + (0, 1): False, + (0, None): False, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): True, + }, + ("ecr", "ryy"): False, + ("ecr", "rzz"): False, + ("ecr", "rzx"): { + (0, 1): False, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, ("s", "id"): True, ("s", "sx"): False, ("s", "x"): False, @@ -2540,6 +2689,13 @@ ("s", "tdg"): True, ("s", "y"): False, ("s", "z"): True, + ("s", "rxx"): False, + ("s", "ryy"): False, + ("s", "rzz"): True, + ("s", "rzx"): { + (0,): True, + (1,): False, + }, ("sdg", "cx"): { (0,): True, (1,): False, @@ -2594,6 +2750,13 @@ ("sdg", "iswap"): False, ("sdg", "sxdg"): False, ("sdg", "tdg"): True, + ("sdg", "rxx"): False, + ("sdg", "ryy"): False, + ("sdg", "rzz"): True, + ("sdg", "rzx"): { + (0,): True, + (1,): False, + }, ("cs", "cx"): { (0, 1): False, (0, None): True, @@ -2726,6 +2889,17 @@ (None, 0): False, (None, 1): False, }, + ("cs", "rxx"): False, + ("cs", "ryy"): False, + ("cs", "rzz"): True, + ("cs", "rzx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): False, + }, ("csdg", "c3sx"): { (0, 1): True, (0, 2): True, @@ -3064,6 +3238,13 @@ ("sxdg", "swap"): False, ("sxdg", "iswap"): False, ("sxdg", "sxdg"): True, + ("sxdg", "rxx"): True, + ("sxdg", "ryy"): False, + ("sxdg", "rzz"): False, + ("sxdg", "rzx"): { + (0,): False, + (1,): True, + }, ("t", "id"): True, ("t", "sx"): False, ("t", "x"): False, @@ -3124,6 +3305,13 @@ ("t", "tdg"): True, ("t", "y"): False, ("t", "z"): True, + ("t", "rxx"): False, + ("t", "ryy"): False, + ("t", "rzz"): True, + ("t", "rzx"): { + (0,): True, + (1,): False, + }, ("tdg", "cx"): { (0,): True, (1,): False, @@ -3177,6 +3365,13 @@ ("tdg", "iswap"): False, ("tdg", "sxdg"): False, ("tdg", "tdg"): True, + ("tdg", "rxx"): False, + ("tdg", "ryy"): False, + ("tdg", "rzz"): True, + ("tdg", "rzx"): { + (0,): True, + (1,): False, + }, ("y", "id"): True, ("y", "sx"): False, ("y", "cx"): False, @@ -3204,6 +3399,10 @@ ("y", "tdg"): False, ("y", "y"): True, ("y", "z"): False, + ("y", "rxx"): False, + ("y", "ryy"): True, + ("y", "rzz"): False, + ("y", "rzx"): False, ("z", "id"): True, ("z", "sx"): False, ("z", "cx"): { @@ -3261,4 +3460,390 @@ ("z", "sxdg"): False, ("z", "tdg"): True, ("z", "z"): True, + ("z", "rxx"): False, + ("z", "ryy"): False, + ("z", "rzz"): True, + ("z", "rzx"): { + (0,): True, + (1,): False, + }, + ("rxx", "c3sx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): False, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): False, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): True, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): True, + }, + ("rxx", "ccx"): { + (0, 1): False, + (0, 2): False, + (0, None): False, + (1, 0): False, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): True, + (None, 0): False, + (None, 1): False, + (None, 2): True, + }, + ("rxx", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): False, + (1, 0): False, + (1, 2): True, + (1, None): False, + (2, 0): False, + (2, 1): True, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("rxx", "ccz"): False, + ("rxx", "rccx"): False, + ("rxx", "rcccx"): False, + ("rxx", "csdg"): False, + ("rxx", "swap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("rxx", "iswap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("rxx", "rxx"): True, + ("rxx", "ryy"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("rxx", "rzz"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("rxx", "rzx"): { + (0, 1): False, + (0, None): False, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): True, + }, + ("ryy", "c3sx"): False, + ("ryy", "ccx"): False, + ("ryy", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): False, + (1, 0): False, + (1, 2): True, + (1, None): False, + (2, 0): False, + (2, 1): True, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("ryy", "ccz"): False, + ("ryy", "rccx"): False, + ("ryy", "rcccx"): False, + ("ryy", "csdg"): False, + ("ryy", "swap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("ryy", "iswap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("ryy", "ryy"): True, + ("ryy", "rzz"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("ryy", "rzx"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("rzz", "c3sx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): True, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): True, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): True, + (None, 3): False, + }, + ("rzz", "ccx"): { + (0, 1): True, + (0, 2): False, + (0, None): True, + (1, 0): True, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): False, + }, + ("rzz", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): True, + (1, None): False, + (2, 0): False, + (2, 1): True, + (2, None): False, + (None, 0): True, + (None, 1): False, + (None, 2): False, + }, + ("rzz", "ccz"): True, + ("rzz", "rccx"): { + (0, 1): True, + (0, 2): False, + (0, None): True, + (1, 0): True, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): False, + }, + ("rzz", "rcccx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): True, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): True, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): True, + (None, 3): False, + }, + ("rzz", "csdg"): True, + ("rzz", "swap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("rzz", "iswap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("rzz", "rzz"): True, + ("rzx", "c3sx"): { + (0, 1): False, + (0, 2): False, + (0, 3): True, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): True, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): True, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): True, + }, + ("rzx", "ccx"): { + (0, 1): False, + (0, 2): True, + (0, None): True, + (1, 0): False, + (1, 2): True, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): True, + }, + ("rzx", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("rzx", "ccz"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): True, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("rzx", "rccx"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("rzx", "rcccx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("rzx", "csdg"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("rzx", "swap"): False, + ("rzx", "iswap"): False, + ("rzx", "rzz"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("rzx", "rzx"): { + (0, 1): True, + (0, None): True, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, } diff --git a/qiskit/circuit/barrier.py b/qiskit/circuit/barrier.py index c339066b4dce..d04769b68d39 100644 --- a/qiskit/circuit/barrier.py +++ b/qiskit/circuit/barrier.py @@ -19,6 +19,7 @@ from __future__ import annotations from qiskit.exceptions import QiskitError +from qiskit.utils import deprecate_func from .instruction import Instruction @@ -44,5 +45,6 @@ def inverse(self, annotated: bool = False): """Special case. Return self.""" return Barrier(self.num_qubits) + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): raise QiskitError("Barriers are compiler directives and cannot be conditional.") diff --git a/qiskit/circuit/controlflow/builder.py b/qiskit/circuit/controlflow/builder.py index ab464a50ca67..458fabec49ef 100644 --- a/qiskit/circuit/controlflow/builder.py +++ b/qiskit/circuit/controlflow/builder.py @@ -284,7 +284,7 @@ def _copy_mutable_properties(self, instruction: Instruction) -> Instruction: The same instruction instance that was passed, but mutated to propagate the tracked changes to this class. """ - instruction.condition = self.condition + instruction._condition = self._condition return instruction # Provide some better error messages, just in case something goes wrong during development and @@ -639,8 +639,8 @@ def update_registers(index, op): # a register is already present, so we use our own tracking. self.add_register(register) out.add_register(register) - if getattr(op, "condition", None) is not None: - for register in condition_resources(op.condition).cregs: + if getattr(op, "_condition", None) is not None: + for register in condition_resources(op._condition).cregs: if register not in self.registers: self.add_register(register) out.add_register(register) diff --git a/qiskit/circuit/controlflow/if_else.py b/qiskit/circuit/controlflow/if_else.py index dd639c65f4b5..f5cdc9a2e1ae 100644 --- a/qiskit/circuit/controlflow/if_else.py +++ b/qiskit/circuit/controlflow/if_else.py @@ -87,12 +87,20 @@ def __init__( super().__init__("if_else", num_qubits, num_clbits, [true_body, false_body], label=label) - self.condition = validate_condition(condition) + self._condition = validate_condition(condition) @property def params(self): return self._params + @property + def condition(self): + return self._condition + + @condition.setter + def condition(self, value): + self._condition = value + @params.setter def params(self, parameters): # pylint: disable=cyclic-import @@ -152,7 +160,7 @@ def replace_blocks(self, blocks: Iterable[QuantumCircuit]) -> "IfElseOp": true_body, false_body = ( ablock for ablock, _ in itertools.zip_longest(blocks, range(2), fillvalue=None) ) - return IfElseOp(self.condition, true_body, false_body=false_body, label=self.label) + return IfElseOp(self._condition, true_body, false_body=false_body, label=self.label) def c_if(self, classical, val): raise NotImplementedError( @@ -200,7 +208,7 @@ def __init__( "if_else", len(self.__resources.qubits), len(self.__resources.clbits), [], label=label ) # Set the condition after super().__init__() has initialized it to None. - self.condition = validate_condition(condition) + self._condition = validate_condition(condition) def with_false_block(self, false_block: ControlFlowBuilderBlock) -> "IfElsePlaceholder": """Return a new placeholder instruction, with the false block set to the given value, @@ -225,7 +233,7 @@ def with_false_block(self, false_block: ControlFlowBuilderBlock) -> "IfElsePlace false_bits = false_block.qubits() | false_block.clbits() true_block.add_bits(false_bits - true_bits) false_block.add_bits(true_bits - false_bits) - return type(self)(self.condition, true_block, false_block, label=self.label) + return type(self)(self._condition, true_block, false_block, label=self.label) def registers(self): """Get the registers used by the interior blocks.""" @@ -288,7 +296,7 @@ def concrete_instruction(self, qubits, clbits): ) return ( self._copy_mutable_properties( - IfElseOp(self.condition, true_body, false_body, label=self.label) + IfElseOp(self._condition, true_body, false_body, label=self.label) ), InstructionResources( qubits=tuple(true_body.qubits), diff --git a/qiskit/circuit/controlflow/while_loop.py b/qiskit/circuit/controlflow/while_loop.py index 488a7a8b7025..7dcc2c22f6ee 100644 --- a/qiskit/circuit/controlflow/while_loop.py +++ b/qiskit/circuit/controlflow/while_loop.py @@ -53,12 +53,20 @@ def __init__( num_clbits = body.num_clbits super().__init__("while_loop", num_qubits, num_clbits, [body], label=label) - self.condition = validate_condition(condition) + self._condition = validate_condition(condition) @property def params(self): return self._params + @property + def condition(self): + return self._condition + + @condition.setter + def condition(self, value): + self._condition = value + @params.setter def params(self, parameters): # pylint: disable=cyclic-import @@ -88,7 +96,7 @@ def blocks(self): def replace_blocks(self, blocks): (body,) = blocks - return WhileLoopOp(self.condition, body, label=self.label) + return WhileLoopOp(self._condition, body, label=self.label) def c_if(self, classical, val): raise NotImplementedError( diff --git a/qiskit/circuit/delay.py b/qiskit/circuit/delay.py index 7dab3a6839f9..2f0613acb932 100644 --- a/qiskit/circuit/delay.py +++ b/qiskit/circuit/delay.py @@ -19,6 +19,7 @@ from qiskit.circuit.gate import Gate from qiskit.circuit import _utils from qiskit.circuit.parameterexpression import ParameterExpression +from qiskit.utils import deprecate_func @_utils.with_gate_array(np.eye(2, dtype=complex)) @@ -46,6 +47,7 @@ def inverse(self, annotated: bool = False): """Special case. Return self.""" return self + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): raise CircuitError("Conditional Delay is not yet implemented.") diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index f2879e2be5cd..e1df54c2cb99 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -176,6 +176,7 @@ def to_mutable(self): return self.copy() @property + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0", is_property=True) def condition(self): """The classical condition on the instruction.""" return self._condition @@ -410,8 +411,8 @@ def _assemble(self): # Add condition parameters for assembler. This is needed to convert # to a qobj conditional instruction at assemble time and after # conversion will be deleted by the assembler. - if self.condition: - instruction._condition = self.condition + if self._condition: + instruction._condition = self._condition return instruction @property @@ -517,6 +518,7 @@ def inverse(self, annotated: bool = False): inverse_gate.definition = inverse_definition return inverse_gate + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): """Set a classical equality condition on this instruction between the register or cbit ``classical`` and value ``val``. @@ -632,7 +634,7 @@ def repeat(self, n): qargs = tuple(qc.qubits) cargs = tuple(qc.clbits) base = self.copy() - if self.condition: + if self._condition: # Condition is handled on the outer instruction. base = base.to_mutable() base.condition = None @@ -640,18 +642,19 @@ def repeat(self, n): qc._append(CircuitInstruction(base, qargs, cargs)) instruction.definition = qc - if self.condition: - instruction = instruction.c_if(*self.condition) + if self._condition: + instruction = instruction.c_if(*self._condition) return instruction @property + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0", is_property=True) def condition_bits(self) -> List[Clbit]: """Get Clbits in condition.""" from qiskit.circuit.controlflow import condition_resources # pylint: disable=cyclic-import - if self.condition is None: + if self._condition is None: return [] - return list(condition_resources(self.condition).clbits) + return list(condition_resources(self._condition).clbits) @property def name(self): diff --git a/qiskit/circuit/instructionset.py b/qiskit/circuit/instructionset.py index cc8a050fd2b0..a3f06a90040c 100644 --- a/qiskit/circuit/instructionset.py +++ b/qiskit/circuit/instructionset.py @@ -20,6 +20,7 @@ from typing import Callable from qiskit.circuit.exceptions import CircuitError +from qiskit.utils import deprecate_func from .classicalregister import Clbit, ClassicalRegister from .operation import Operation from .quantumcircuitdata import CircuitInstruction @@ -105,6 +106,7 @@ def inverse(self, annotated: bool = False): ) return self + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "InstructionSet": """Set a classical equality condition on all the instructions in this set between the :obj:`.ClassicalRegister` or :obj:`.Clbit` ``classical`` and value ``val``. diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 73588a421ac7..1d13db7327ad 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -211,9 +211,15 @@ :template: autosummary/class_no_inherited_members.rst AND + AndGate OR + OrGate XOR + BitwiseXorGate InnerProduct + InnerProductGate + +.. autofunction:: random_bitwise_xor Basis Change Circuits ===================== @@ -327,6 +333,9 @@ HamiltonianGate UnitaryOverlap +.. autofunction:: iqp +.. autofunction:: random_iqp + N-local circuits ================ @@ -523,9 +532,14 @@ from .hamiltonian_gate import HamiltonianGate from .boolean_logic import ( AND, + AndGate, OR, + OrGate, XOR, + BitwiseXorGate, + random_bitwise_xor, InnerProduct, + InnerProductGate, ) from .basis_change import QFT, QFTGate from .arithmetic import ( @@ -571,7 +585,7 @@ from .fourier_checking import FourierChecking from .graph_state import GraphState from .hidden_linear_function import HiddenLinearFunction -from .iqp import IQP +from .iqp import IQP, iqp, random_iqp from .phase_estimation import PhaseEstimation from .grover_operator import GroverOperator from .phase_oracle import PhaseOracle diff --git a/qiskit/circuit/library/boolean_logic/__init__.py b/qiskit/circuit/library/boolean_logic/__init__.py index b273f0d6a245..736f983b05f9 100644 --- a/qiskit/circuit/library/boolean_logic/__init__.py +++ b/qiskit/circuit/library/boolean_logic/__init__.py @@ -12,7 +12,7 @@ """The Boolean logic circuit library.""" -from .quantum_and import AND -from .quantum_or import OR -from .quantum_xor import XOR -from .inner_product import InnerProduct +from .quantum_and import AND, AndGate +from .quantum_or import OR, OrGate +from .quantum_xor import XOR, BitwiseXorGate, random_bitwise_xor +from .inner_product import InnerProduct, InnerProductGate diff --git a/qiskit/circuit/library/boolean_logic/inner_product.py b/qiskit/circuit/library/boolean_logic/inner_product.py index dcaced069119..84b3807fb56c 100644 --- a/qiskit/circuit/library/boolean_logic/inner_product.py +++ b/qiskit/circuit/library/boolean_logic/inner_product.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,10 +11,11 @@ # that they have been altered from the originals. -"""InnerProduct circuit.""" +"""InnerProduct circuit and gate.""" -from qiskit.circuit import QuantumRegister, QuantumCircuit +from qiskit.circuit import QuantumRegister, QuantumCircuit, Gate +from qiskit.utils.deprecation import deprecate_func class InnerProduct(QuantumCircuit): @@ -61,6 +62,11 @@ class InnerProduct(QuantumCircuit): _generate_circuit_library_visualization(circuit) """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.InnerProductGate instead.", + pending=True, + ) def __init__(self, num_qubits: int) -> None: """Return a circuit to compute the inner product of 2 n-qubit registers. @@ -76,3 +82,74 @@ def __init__(self, num_qubits: int) -> None: super().__init__(*inner.qregs, name="inner_product") self.compose(inner.to_gate(), qubits=self.qubits, inplace=True) + + +class InnerProductGate(Gate): + r"""A 2n-qubit Boolean function that computes the inner product of + two n-qubit vectors over :math:`F_2`. + + This implementation is a phase oracle which computes the following transform. + + .. math:: + + \mathcal{IP}_{2n} : F_2^{2n} \rightarrow {-1, 1} + \mathcal{IP}_{2n}(x_1, \cdots, x_n, y_1, \cdots, y_n) = (-1)^{x.y} + + The corresponding unitary is a diagonal, which induces a -1 phase on any inputs + where the inner product of the top and bottom registers is 1. Otherwise, it keeps + the input intact. + + .. parsed-literal:: + + + q0_0: ─■────────── + │ + q0_1: ─┼──■─────── + │ │ + q0_2: ─┼──┼──■──── + │ │ │ + q0_3: ─┼──┼──┼──■─ + │ │ │ │ + q1_0: ─■──┼──┼──┼─ + │ │ │ + q1_1: ────■──┼──┼─ + │ │ + q1_2: ───────■──┼─ + │ + q1_3: ──────────■─ + + + Reference Circuit: + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import InnerProductGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(8) + circuit.append(InnerProductGate(4), [0, 1, 2, 3, 4, 5, 6, 7]) + _generate_circuit_library_visualization(circuit) + """ + + def __init__( + self, + num_qubits: int, + ) -> None: + """ + Args: + num_qubits: width of top and bottom registers (half total number of qubits). + """ + super().__init__("inner_product", 2 * num_qubits, []) + + def _define(self): + num_qubits = self.num_qubits // 2 + qr_a = QuantumRegister(num_qubits, name="x") + qr_b = QuantumRegister(num_qubits, name="y") + + circuit = QuantumCircuit(qr_a, qr_b, name="inner_product") + for i in range(num_qubits): + circuit.cz(qr_a[i], qr_b[i]) + + self.definition = circuit + + def __eq__(self, other): + return isinstance(other, InnerProductGate) and self.num_qubits == other.num_qubits diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index ce2d253ca85a..53099aa7be84 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,11 +11,13 @@ # that they have been altered from the originals. -"""Implementations of boolean logic quantum circuits.""" +"""Boolean AND circuit and gate.""" + from __future__ import annotations -from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister +from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister, Gate from qiskit.circuit.library.standard_gates import MCXGate +from qiskit.utils.deprecation import deprecate_func class AND(QuantumCircuit): @@ -49,6 +51,11 @@ class AND(QuantumCircuit): """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.AndGate instead.", + pending=True, + ) def __init__( self, num_variable_qubits: int, @@ -58,7 +65,7 @@ def __init__( """Create a new logical AND circuit. Args: - num_variable_qubits: The qubits of which the OR is computed. The result will be written + num_variable_qubits: The qubits of which the AND is computed. The result will be written into an additional result qubit. flags: A list of +1/0/-1 marking negations or omissions of qubits. mcx_mode: The mode to be used to implement the multi-controlled X gate. @@ -95,3 +102,99 @@ def __init__( super().__init__(*circuit.qregs, name="and") self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + + +class AndGate(Gate): + r"""A gate representing the logical AND operation on a number of qubits. + + For the AND operation the state :math:`|1\rangle` is interpreted as ``True``. The result + qubit is flipped, if the state of all variable qubits is ``True``. In this format, the AND + operation equals a multi-controlled X gate, which is controlled on all variable qubits. + Using a list of flags however, qubits can be skipped or negated. Practically, the flags + allow to skip controls or to apply pre- and post-X gates to the negated qubits. + + The AndGate gate without special flags equals the multi-controlled-X gate: + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import AndGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(6) + circuit.append(AndGate(5), [0, 1, 2, 3, 4, 5]) + _generate_circuit_library_visualization(circuit) + + Using flags we can negate qubits or skip them. For instance, if we have 5 qubits and want to + return ``True`` if the first qubit is ``False`` and the last two are ``True`` we use the flags + ``[-1, 0, 0, 1, 1]``. + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import AndGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(6) + circuit.append(AndGate(5, flags=[-1, 0, 0, 1, 1]), [0, 1, 2, 3, 4, 5]) + _generate_circuit_library_visualization(circuit) + + """ + + def __init__( + self, + num_variable_qubits: int, + flags: list[int] | None = None, + ) -> None: + """ + Args: + num_variable_qubits: The qubits of which the AND is computed. The result will be written + into an additional result qubit. + flags: A list of +1/0/-1 marking negations or omissions of qubits. + """ + super().__init__("and", num_variable_qubits + 1, []) + self.num_variable_qubits = num_variable_qubits + self.flags = flags + + def _define(self): + # add registers + qr_variable = QuantumRegister(self.num_variable_qubits, name="variable") + qr_result = QuantumRegister(1, name="result") + + # determine the control qubits: all that have a nonzero flag + flags = self.flags or [1] * self.num_variable_qubits + control_qubits = [q for q, flag in zip(qr_variable, flags) if flag != 0] + + # determine the qubits that need to be flipped (if a flag is < 0) + flip_qubits = [q for q, flag in zip(qr_variable, flags) if flag < 0] + + # create the definition circuit + circuit = QuantumCircuit(qr_variable, qr_result, name="and") + + if len(flip_qubits) > 0: + circuit.x(flip_qubits) + circuit.mcx(control_qubits, qr_result[:]) + if len(flip_qubits) > 0: + circuit.x(flip_qubits) + + self.definition = circuit + + # pylint: disable=unused-argument + def inverse(self, annotated: bool = False): + r"""Return inverted AND gate (itself). + + Args: + annotated: when set to ``True``, this is typically used to return an + :class:`.AnnotatedOperation` with an inverse modifier set instead of a concrete + :class:`.Gate`. However, for this class this argument is ignored as this gate + is self-inverse. + + Returns: + AndGate: inverse gate (self-inverse). + """ + return AndGate(self.num_variable_qubits, self.flags) + + def __eq__(self, other): + return ( + isinstance(other, AndGate) + and self.num_variable_qubits == other.num_variable_qubits + and self.flags == other.flags + ) diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index 91d6c4fbdd52..95b346b34846 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,12 +11,14 @@ # that they have been altered from the originals. -"""Implementations of boolean logic quantum circuits.""" +"""Boolean OR circuit and gate.""" + from __future__ import annotations from typing import List, Optional -from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister +from qiskit.circuit import QuantumRegister, QuantumCircuit, AncillaRegister, Gate from qiskit.circuit.library.standard_gates import MCXGate +from qiskit.utils.deprecation import deprecate_func class OR(QuantumCircuit): @@ -50,6 +52,11 @@ class OR(QuantumCircuit): """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.OrGate instead.", + pending=True, + ) def __init__( self, num_variable_qubits: int, @@ -96,3 +103,100 @@ def __init__( super().__init__(*circuit.qregs, name="or") self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + + +class OrGate(Gate): + r"""A gate representing the logical OR operation on a number of qubits. + + For the OR operation the state :math:`|1\rangle` is interpreted as ``True``. The result + qubit is flipped, if the state of any variable qubit is ``True``. The OR is implemented using + a multi-open-controlled X gate (i.e. flips if the state is :math:`|0\rangle`) and + applying an X gate on the result qubit. + Using a list of flags, qubits can be skipped or negated. + + The OrGate gate without special flags: + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import OrGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(6) + circuit.append(OrGate(5), [0, 1, 2, 3, 4, 5]) + _generate_circuit_library_visualization(circuit) + + Using flags we can negate qubits or skip them. For instance, if we have 5 qubits and want to + return ``True`` if the first qubit is ``False`` or one of the last two are ``True`` we use the + flags ``[-1, 0, 0, 1, 1]``. + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import OrGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(6) + circuit.append(OrGate(5, flags=[-1, 0, 0, 1, 1]), [0, 1, 2, 3, 4, 5]) + _generate_circuit_library_visualization(circuit) + + """ + + def __init__( + self, + num_variable_qubits: int, + flags: list[int] | None = None, + ) -> None: + """ + Args: + num_variable_qubits: The qubits of which the AND is computed. The result will be written + into an additional result qubit. + flags: A list of +1/0/-1 marking negations or omissions of qubits. + """ + super().__init__("and", num_variable_qubits + 1, []) + self.num_variable_qubits = num_variable_qubits + self.flags = flags + + def _define(self): + # add registers + qr_variable = QuantumRegister(self.num_variable_qubits, name="variable") + qr_result = QuantumRegister(1, name="result") + + # determine the control qubits: all that have a nonzero flag + flags = self.flags or [1] * self.num_variable_qubits + control_qubits = [q for q, flag in zip(qr_variable, flags) if flag != 0] + + # determine the qubits that need to be flipped (if a flag is > 0) + flip_qubits = [q for q, flag in zip(qr_variable, flags) if flag > 0] + + # create the definition circuit + circuit = QuantumCircuit(qr_variable, qr_result, name="or") + + circuit.x(qr_result) + if len(flip_qubits) > 0: + circuit.x(flip_qubits) + circuit.mcx(control_qubits, qr_result[:]) + if len(flip_qubits) > 0: + circuit.x(flip_qubits) + + self.definition = circuit + + # pylint: disable=unused-argument + def inverse(self, annotated: bool = False): + r"""Return inverted OR gate (itself). + + Args: + annotated: when set to ``True``, this is typically used to return an + :class:`.AnnotatedOperation` with an inverse modifier set instead of a concrete + :class:`.Gate`. However, for this class this argument is ignored as this gate + is self-inverse. + + Returns: + OrGate: inverse gate (self-inverse). + """ + return OrGate(self.num_variable_qubits, self.flags) + + def __eq__(self, other): + return ( + isinstance(other, OrGate) + and self.num_variable_qubits == other.num_variable_qubits + and self.flags == other.flags + ) diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 5b230345137d..73a2178830bc 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,13 +11,14 @@ # that they have been altered from the originals. -"""XOR circuit.""" +"""Bitwise XOR circuit and gate.""" from typing import Optional import numpy as np -from qiskit.circuit import QuantumCircuit +from qiskit.circuit import QuantumCircuit, Gate from qiskit.circuit.exceptions import CircuitError +from qiskit.utils.deprecation import deprecate_func class XOR(QuantumCircuit): @@ -28,6 +29,12 @@ class XOR(QuantumCircuit): This circuit can also represent addition by ``amount`` over the finite field GF(2). """ + @deprecate_func( + since="1.3", + additional_msg="Instead, for xor-ing with a specified amount, use BitwiseXorGate," + "and for xor-ing with a random amount, use random_bitwise_xor.", + pending=True, + ) def __init__( self, num_qubits: int, @@ -69,3 +76,90 @@ def __init__( super().__init__(*circuit.qregs, name="xor") self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + + +class BitwiseXorGate(Gate): + """An n-qubit gate for bitwise xor-ing the input with some integer ``amount``. + + The ``amount`` is xor-ed in bitstring form with the input. + + This gate can also represent addition by ``amount`` over the finite field GF(2). + + Reference Circuit: + + .. plot:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import BitwiseXorGate + from qiskit.visualization.library import _generate_circuit_library_visualization + circuit = QuantumCircuit(5) + circuit.append(BitwiseXorGate(5, amount=12), [0, 1, 2, 3, 4]) + _generate_circuit_library_visualization(circuit) + + """ + + def __init__( + self, + num_qubits: int, + amount: int, + ) -> None: + """ + Args: + num_qubits: the width of circuit. + amount: the xor amount in decimal form. + + Raises: + CircuitError: if the xor bitstring exceeds available qubits. + """ + if len(bin(amount)[2:]) > num_qubits: + raise CircuitError("Bits in 'amount' exceed circuit width") + + super().__init__("xor", num_qubits, []) + self.amount = amount + + def _define(self): + circuit = QuantumCircuit(self.num_qubits, name="xor") + amount = self.amount + for i in range(self.num_qubits): + bit = amount & 1 + amount = amount >> 1 + if bit == 1: + circuit.x(i) + + self.definition = circuit + + def __eq__(self, other): + return ( + isinstance(other, BitwiseXorGate) + and self.num_qubits == other.num_qubits + and self.amount == other.amount + ) + + # pylint: disable=unused-argument + def inverse(self, annotated: bool = False): + r"""Return inverted BitwiseXorGate gate (itself). + + Args: + annotated: when set to ``True``, this is typically used to return an + :class:`.AnnotatedOperation` with an inverse modifier set instead of a concrete + :class:`.Gate`. However, for this class this argument is ignored as this gate + is self-inverse. + + Returns: + BitwiseXorGate: inverse gate (self-inverse). + """ + return BitwiseXorGate(self.num_qubits, self.amount) + + +def random_bitwise_xor(num_qubits: int, seed: int) -> BitwiseXorGate: + """ + Create a random BitwiseXorGate. + + Args: + num_qubits: the width of circuit. + seed: random seed in case a random xor is requested. + """ + + rng = np.random.default_rng(seed) + amount = rng.integers(0, 2**num_qubits) + return BitwiseXorGate(num_qubits, amount) diff --git a/qiskit/circuit/library/iqp.py b/qiskit/circuit/library/iqp.py index db65fbe294ad..62082fd5c446 100644 --- a/qiskit/circuit/library/iqp.py +++ b/qiskit/circuit/library/iqp.py @@ -13,10 +13,12 @@ """Instantaneous quantum polynomial circuit.""" from __future__ import annotations +from collections.abc import Sequence import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.circuit.exceptions import CircuitError +from qiskit.utils.deprecation import deprecate_func +from qiskit._accelerate.circuit_library import py_iqp, py_random_iqp class IQP(QuantumCircuit): @@ -60,6 +62,11 @@ class IQP(QuantumCircuit): `arXiv:1504.07999 `_ """ + @deprecate_func( + since="1.3", + additional_msg="Use the qiskit.circuit.library.iqp function instead.", + pending=True, + ) def __init__(self, interactions: list | np.ndarray) -> None: """Create IQP circuit. @@ -69,28 +76,100 @@ def __init__(self, interactions: list | np.ndarray) -> None: Raises: CircuitError: if the inputs is not as symmetric matrix. """ - num_qubits = len(interactions) - interactions = np.array(interactions) - if not np.allclose(interactions, interactions.transpose()): - raise CircuitError("The interactions matrix is not symmetric") + circuit = iqp(interactions) + super().__init__(*circuit.qregs, name=circuit.name) + self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) - a_str = np.array_str(interactions) - a_str.replace("\n", ";") - name = "iqp:" + a_str.replace("\n", ";") - circuit = QuantumCircuit(num_qubits, name=name) +def iqp( + interactions: Sequence[Sequence[int]], +) -> QuantumCircuit: + r"""Instantaneous quantum polynomial time (IQP) circuit. - circuit.h(range(num_qubits)) - for i in range(num_qubits): - for j in range(i + 1, num_qubits): - if interactions[i][j] % 4 != 0: - circuit.cp(interactions[i][j] * np.pi / 2, i, j) + The circuit consists of a column of Hadamard gates, a column of powers of T gates, + a sequence of powers of CS gates (up to :math:`\frac{n^2-n}{2}` of them), and a final column of + Hadamard gates, as introduced in [1]. - for i in range(num_qubits): - if interactions[i][i] % 8 != 0: - circuit.p(interactions[i][i] * np.pi / 8, i) + The circuit is parameterized by an :math:`n \times n` interactions matrix. The powers of each + T gate are given by the diagonal elements of the interactions matrix. The powers of the CS gates + are given by the upper triangle of the interactions matrix. - circuit.h(range(num_qubits)) + **Reference Circuit:** - super().__init__(*circuit.qregs, name=circuit.name) - self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + .. plot:: + + from qiskit.circuit.library import iqp + A = [[6, 5, 3], [5, 4, 5], [3, 5, 1]] + circuit = iqp(A) + circuit.draw("mpl") + + **Expanded Circuit:** + + .. plot:: + + from qiskit.circuit.library import iqp + from qiskit.visualization.library import _generate_circuit_library_visualization + A = [[6, 5, 3], [5, 4, 5], [3, 5, 1]] + circuit = iqp(A) + _generate_circuit_library_visualization(circuit) + + **References:** + + [1] M. J. Bremner et al. Average-case complexity versus approximate + simulation of commuting quantum computations, + Phys. Rev. Lett. 117, 080501 (2016). + `arXiv:1504.07999 `_ + + Args: + interactions: The interactions as symmetric square matrix. If ``None``, then the + ``num_qubits`` argument must be set and a random IQP circuit will be generated. + + Returns: + An IQP circuit. + """ + # if no interactions are given, generate them + num_qubits = len(interactions) + interactions = np.asarray(interactions).astype(np.int64) + + # set the label -- if the number of qubits is too large, do not show the interactions matrix + if num_qubits < 5 and interactions is not None: + label = np.array_str(interactions) + name = "iqp:" + label.replace("\n", ";") + else: + name = "iqp" + + circuit = QuantumCircuit._from_circuit_data(py_iqp(interactions), add_regs=True) + circuit.name = name + return circuit + + +def random_iqp( + num_qubits: int, + seed: int | None = None, +) -> QuantumCircuit: + r"""A random instantaneous quantum polynomial time (IQP) circuit. + + See :func:`iqp` for more details on the IQP circuit. + + Example: + + .. plot:: + :include-source: + + from qiskit.circuit.library import random_iqp + + circuit = random_iqp(3) + circuit.draw("mpl") + + Args: + num_qubits: The number of qubits in the circuit. + seed: A seed for the random number generator, in case the interactions matrix is + randomly generated. + + Returns: + An IQP circuit. + """ + # set the label -- if the number of qubits is too large, do not show the interactions matrix + circuit = QuantumCircuit._from_circuit_data(py_random_iqp(num_qubits, seed), add_regs=True) + circuit.name = "iqp" + return circuit diff --git a/qiskit/circuit/library/standard_gates/p.py b/qiskit/circuit/library/standard_gates/p.py index 9f689e46ca2b..a3ea7167a34f 100644 --- a/qiskit/circuit/library/standard_gates/p.py +++ b/qiskit/circuit/library/standard_gates/p.py @@ -36,32 +36,32 @@ class PhaseGate(Gate): .. code-block:: text ┌──────┐ - q_0: ┤ P(λ) ├ + q_0: ┤ P(θ) ├ └──────┘ **Matrix Representation:** .. math:: - P(\lambda) = + P(\theta) = \begin{pmatrix} 1 & 0 \\ - 0 & e^{i\lambda} + 0 & e^{i\theta} \end{pmatrix} **Examples:** .. math:: - P(\lambda = \pi) = Z + P(\theta = \pi) = Z .. math:: - P(\lambda = \pi/2) = S + P(\theta = \pi/2) = S .. math:: - P(\lambda = \pi/4) = T + P(\theta = \pi/4) = T .. seealso:: @@ -70,7 +70,7 @@ class PhaseGate(Gate): .. math:: - P(\lambda) = e^{i{\lambda}/2} RZ(\lambda) + P(\theta) = e^{i{\theta}/2} RZ(\theta) Reference for virtual Z gate implementation: `1612.00858 `_ @@ -175,7 +175,7 @@ class CPhaseGate(ControlledGate): q_0: ─■── - │λ + │θ q_1: ─■── @@ -189,7 +189,7 @@ class CPhaseGate(ControlledGate): 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ - 0 & 0 & 0 & e^{i\lambda} + 0 & 0 & 0 & e^{i\theta} \end{pmatrix} .. seealso:: diff --git a/qiskit/circuit/library/standard_gates/rz.py b/qiskit/circuit/library/standard_gates/rz.py index c1b16e1d99e4..e7efeafd24c5 100644 --- a/qiskit/circuit/library/standard_gates/rz.py +++ b/qiskit/circuit/library/standard_gates/rz.py @@ -178,20 +178,20 @@ class CRZGate(ControlledGate): q_0: ────■──── ┌───┴───┐ - q_1: ┤ Rz(λ) ├ + q_1: ┤ Rz(θ) ├ └───────┘ **Matrix representation:** .. math:: - CRZ(\lambda)\ q_0, q_1 = - I \otimes |0\rangle\langle 0| + RZ(\lambda) \otimes |1\rangle\langle 1| = + CRZ(\theta)\ q_0, q_1 = + I \otimes |0\rangle\langle 0| + RZ(\theta) \otimes |1\rangle\langle 1| = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & e^{-i\frac{\lambda}{2}} & 0 & 0 \\ 0 & 0 & 1 & 0 \\ - 0 & 0 & 0 & e^{i\frac{\lambda}{2}} + 0 & 0 & 0 & e^{i\frac{\theta}{2}} \end{pmatrix} .. note:: @@ -205,19 +205,19 @@ class CRZGate(ControlledGate): .. code-block:: text ┌───────┐ - q_0: ┤ Rz(λ) ├ + q_0: ┤ Rz(θ) ├ └───┬───┘ q_1: ────■──── .. math:: - CRZ(\lambda)\ q_1, q_0 = - |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes RZ(\lambda) = + CRZ(\theta)\ q_1, q_0 = + |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes RZ(\theta) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ - 0 & 0 & e^{-i\frac{\lambda}{2}} & 0 \\ - 0 & 0 & 0 & e^{i\frac{\lambda}{2}} + 0 & 0 & e^{-i\frac{\theta}{2}} & 0 \\ + 0 & 0 & 0 & e^{i\frac{\theta}{2}} \end{pmatrix} .. seealso:: diff --git a/qiskit/circuit/library/standard_gates/u1.py b/qiskit/circuit/library/standard_gates/u1.py index e1657bdee8cc..b9bfa694cfeb 100644 --- a/qiskit/circuit/library/standard_gates/u1.py +++ b/qiskit/circuit/library/standard_gates/u1.py @@ -34,7 +34,7 @@ class U1Gate(Gate): .. math:: - U1(\lambda) = P(\lambda)= U(0,0,\lambda) + U1(\theta) = P(\theta)= U(0,0,\theta) .. code-block:: python @@ -49,32 +49,32 @@ class U1Gate(Gate): .. code-block:: text ┌───────┐ - q_0: ┤ U1(λ) ├ + q_0: ┤ U1(θ) ├ └───────┘ **Matrix Representation:** .. math:: - U1(\lambda) = + U1(\theta) = \begin{pmatrix} 1 & 0 \\ - 0 & e^{i\lambda} + 0 & e^{i\theta} \end{pmatrix} **Examples:** .. math:: - U1(\lambda = \pi) = Z + U1(\theta = \pi) = Z .. math:: - U1(\lambda = \pi/2) = S + U1(\theta = \pi/2) = S .. math:: - U1(\lambda = \pi/4) = T + U1(\theta = \pi/4) = T .. seealso:: @@ -83,7 +83,7 @@ class U1Gate(Gate): .. math:: - U1(\lambda) = e^{i{\lambda}/2} RZ(\lambda) + U1(\theta) = e^{i{\theta}/2} RZ(\theta) :class:`~qiskit.circuit.library.standard_gates.U3Gate`: U3 is a generalization of U2 that covers all single-qubit rotations, @@ -202,7 +202,7 @@ class CU1Gate(ControlledGate): q_0: ─■── - │λ + │θ q_1: ─■── @@ -210,13 +210,13 @@ class CU1Gate(ControlledGate): .. math:: - CU1(\lambda) = + CU1(\theta) = I \otimes |0\rangle\langle 0| + U1 \otimes |1\rangle\langle 1| = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ - 0 & 0 & 0 & e^{i\lambda} + 0 & 0 & 0 & e^{i\theta} \end{pmatrix} .. seealso:: diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index f36e6a304afb..8d81aa09b187 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2102,14 +2102,14 @@ def map_vars(op): is_control_flow = isinstance(n_op, ControlFlowOp) if ( not is_control_flow - and (condition := getattr(n_op, "condition", None)) is not None + and (condition := getattr(n_op, "_condition", None)) is not None ): n_op = n_op.copy() if n_op is op and copy else n_op n_op.condition = variable_mapper.map_condition(condition) elif is_control_flow: n_op = n_op.replace_blocks(recurse_block(block) for block in n_op.blocks) if isinstance(n_op, (IfElseOp, WhileLoopOp)): - n_op.condition = variable_mapper.map_condition(n_op.condition) + n_op.condition = variable_mapper.map_condition(n_op._condition) elif isinstance(n_op, SwitchCaseOp): n_op.target = variable_mapper.map_target(n_op.target) elif isinstance(n_op, Store): @@ -3520,7 +3520,7 @@ def update_from_expr(objects, node): for instruction in self._data: objects = set(itertools.chain(instruction.qubits, instruction.clbits)) - if (condition := getattr(instruction.operation, "condition", None)) is not None: + if (condition := getattr(instruction.operation, "_condition", None)) is not None: objects.update(_builder_utils.condition_resources(condition).clbits) if isinstance(condition, expr.Expr): update_from_expr(objects, condition) @@ -3623,7 +3623,7 @@ def num_connected_components(self, unitary_only: bool = False) -> int: else: args = instruction.qubits + instruction.clbits num_qargs = len(args) + ( - 1 if getattr(instruction.operation, "condition", None) else 0 + 1 if getattr(instruction.operation, "_condition", None) else 0 ) if num_qargs >= 2 and not getattr(instruction.operation, "_directive", False): diff --git a/qiskit/circuit/singleton.py b/qiskit/circuit/singleton.py index 874b979ff588..a315f8aaa619 100644 --- a/qiskit/circuit/singleton.py +++ b/qiskit/circuit/singleton.py @@ -251,6 +251,7 @@ class XGate(Gate, metaclass=_SingletonMeta, overrides=_SingletonGateOverrides): import functools +from qiskit.utils import deprecate_func from .instruction import Instruction from .gate import Gate from .controlledgate import ControlledGate, _ctrl_state_to_int @@ -489,6 +490,7 @@ class they are providing overrides for has more lazy attributes or user-exposed instruction._params = _frozenlist(instruction._params) return instruction + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): return self.to_mutable().c_if(classical, val) diff --git a/qiskit/circuit/store.py b/qiskit/circuit/store.py index 6bbc5439332d..43e81ce61056 100644 --- a/qiskit/circuit/store.py +++ b/qiskit/circuit/store.py @@ -16,6 +16,7 @@ import typing +from qiskit.utils import deprecate_func from .exceptions import CircuitError from .classical import expr, types from .instruction import Instruction @@ -88,6 +89,7 @@ def rvalue(self): """Get the r-value :class:`~.expr.Expr` node that is being written into the l-value.""" return self.params[1] + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): """:meta hidden:""" raise NotImplementedError( diff --git a/qiskit/compiler/assembler.py b/qiskit/compiler/assembler.py index 2802169b4afb..c42ec0663d90 100644 --- a/qiskit/compiler/assembler.py +++ b/qiskit/compiler/assembler.py @@ -189,6 +189,16 @@ def assemble( ) +# Note for future: this method is used in `BasicSimulator` and may need to be kept past the +# `assemble` removal deadline (2.0). If it is kept (potentially in a different location), +# we will need an alternative for the backend.configuration() access that currently takes +# place in L566 (`parse_circuit_args`) and L351 (`parse_common_args`) +# because backend.configuration() is also set for removal in 2.0. +# The ultimate goal will be to move away from relying on any kind of `assemble` implementation +# because of how tightly coupled it is to these legacy data structures. But as a transition step, +# given that we would only have to support the subcase of `BasicSimulator`, we could probably just +# inline the relevant config values that are already hardcoded in the basic simulator configuration +# generator. def _assemble( experiments: Union[ QuantumCircuit, @@ -229,7 +239,7 @@ def _assemble( experiments = experiments if isinstance(experiments, list) else [experiments] pulse_qobj = any(isinstance(exp, (ScheduleBlock, Schedule, Instruction)) for exp in experiments) with warnings.catch_warnings(): - # The Qobj is deprecated + # The Qobj class is deprecated, the backend.configuration() method is too warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") qobj_id, qobj_header, run_config_common_dict = _parse_common_args( backend, @@ -554,9 +564,12 @@ def _parse_circuit_args( run_config_dict = {"parameter_binds": parameter_binds, **run_config} if parametric_pulses is None: if backend: - run_config_dict["parametric_pulses"] = getattr( - backend.configuration(), "parametric_pulses", [] - ) + with warnings.catch_warnings(): + # TODO (2.0): See comment on L192 regarding backend.configuration removal + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + run_config_dict["parametric_pulses"] = getattr( + backend.configuration(), "parametric_pulses", [] + ) else: run_config_dict["parametric_pulses"] = [] else: diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index 2b17d8bbae12..bda22cbbc4b8 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -32,6 +32,7 @@ from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit.transpiler.target import Target +from qiskit.utils import deprecate_arg from qiskit.utils.deprecate_pulse import deprecate_pulse_arg logger = logging.getLogger(__name__) @@ -39,6 +40,32 @@ _CircuitT = TypeVar("_CircuitT", bound=Union[QuantumCircuit, List[QuantumCircuit]]) +@deprecate_arg( + name="instruction_durations", + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `target` parameter should be used instead. You can build a `Target` instance " + "with defined instruction durations with " + "`Target.from_configuration(..., instruction_durations=...)`", +) +@deprecate_arg( + name="timing_constraints", + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `target` parameter should be used instead. You can build a `Target` instance " + "with defined timing constraints with " + "`Target.from_configuration(..., timing_constraints=...)`", +) +@deprecate_arg( + name="backend_properties", + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `target` parameter should be used instead. You can build a `Target` instance " + "with defined properties with Target.from_configuration(..., backend_properties=...)", +) @deprecate_pulse_arg("inst_map", predicate=lambda inst_map: inst_map is not None) def transpile( # pylint: disable=too-many-return-statements circuits: _CircuitT, @@ -367,31 +394,34 @@ def callback_func(**kwargs): # Edge cases require using the old model (loose constraints) instead of building a target, # but we don't populate the passmanager config with loose constraints unless it's one of # the known edge cases to control the execution path. - pm = generate_preset_pass_manager( - optimization_level, - target=target, - backend=backend, - basis_gates=basis_gates, - coupling_map=coupling_map, - instruction_durations=instruction_durations, - backend_properties=backend_properties, - timing_constraints=timing_constraints, - inst_map=inst_map, - initial_layout=initial_layout, - layout_method=layout_method, - routing_method=routing_method, - translation_method=translation_method, - scheduling_method=scheduling_method, - approximation_degree=approximation_degree, - seed_transpiler=seed_transpiler, - unitary_synthesis_method=unitary_synthesis_method, - unitary_synthesis_plugin_config=unitary_synthesis_plugin_config, - hls_config=hls_config, - init_method=init_method, - optimization_method=optimization_method, - dt=dt, - qubits_initially_zero=qubits_initially_zero, - ) + with warnings.catch_warnings(): + warnings.simplefilter(action="ignore", category=DeprecationWarning) + # Filter instruction_durations, timing_constraints and backend_properties deprecation + pm = generate_preset_pass_manager( + optimization_level, + target=target, + backend=backend, + basis_gates=basis_gates, + coupling_map=coupling_map, + instruction_durations=instruction_durations, + backend_properties=backend_properties, + timing_constraints=timing_constraints, + inst_map=inst_map, + initial_layout=initial_layout, + layout_method=layout_method, + routing_method=routing_method, + translation_method=translation_method, + scheduling_method=scheduling_method, + approximation_degree=approximation_degree, + seed_transpiler=seed_transpiler, + unitary_synthesis_method=unitary_synthesis_method, + unitary_synthesis_plugin_config=unitary_synthesis_plugin_config, + hls_config=hls_config, + init_method=init_method, + optimization_method=optimization_method, + dt=dt, + qubits_initially_zero=qubits_initially_zero, + ) out_circuits = pm.run(circuits, callback=callback, num_processes=num_processes) diff --git a/qiskit/converters/circuit_to_instruction.py b/qiskit/converters/circuit_to_instruction.py index 4487a65e08fd..db314eee267e 100644 --- a/qiskit/converters/circuit_to_instruction.py +++ b/qiskit/converters/circuit_to_instruction.py @@ -125,7 +125,7 @@ def fix_condition(op): if (out := operation_map.get(original_id)) is not None: return out - condition = getattr(op, "condition", None) + condition = getattr(op, "_condition", None) if condition: reg, val = condition if isinstance(reg, Clbit): diff --git a/qiskit/dagcircuit/collect_blocks.py b/qiskit/dagcircuit/collect_blocks.py index c5c7b49144f7..99c51d2e3600 100644 --- a/qiskit/dagcircuit/collect_blocks.py +++ b/qiskit/dagcircuit/collect_blocks.py @@ -306,7 +306,7 @@ def split_block_into_layers(block: list[DAGOpNode | DAGDepNode]): cur_bits = set(node.qargs) cur_bits.update(node.cargs) - cond = getattr(node.op, "condition", None) + cond = getattr(node.op, "_condition", None) if cond is not None: cur_bits.update(condition_resources(cond).clbits) @@ -356,7 +356,7 @@ def collapse_to_operation(self, blocks, collapse_fn): for node in block: cur_qubits.update(node.qargs) cur_clbits.update(node.cargs) - cond = getattr(node.op, "condition", None) + cond = getattr(node.op, "_condition", None) if cond is not None: cur_clbits.update(condition_resources(cond).clbits) if isinstance(cond[0], ClassicalRegister): @@ -378,7 +378,7 @@ def collapse_to_operation(self, blocks, collapse_fn): for node in block: instructions = qc.append(CircuitInstruction(node.op, node.qargs, node.cargs)) - cond = getattr(node.op, "condition", None) + cond = getattr(node.op, "_condition", None) if cond is not None: instructions.c_if(*cond) diff --git a/qiskit/dagcircuit/dagdependency.py b/qiskit/dagcircuit/dagdependency.py index 2658cd731d69..63e9114a6ca1 100644 --- a/qiskit/dagcircuit/dagdependency.py +++ b/qiskit/dagcircuit/dagdependency.py @@ -409,13 +409,13 @@ def _create_op_node(self, operation, qargs, cargs): for elem in qargs: qindices_list.append(self.qubits.index(elem)) - if getattr(operation, "condition", None): + if getattr(operation, "_condition", None): # The change to handling operation.condition follows code patterns in quantum_circuit.py. # However: # (1) cindices_list are specific to template optimization and should not be computed # in this place. # (2) Template optimization pass needs currently does not handle general conditions. - cond_bits = condition_resources(operation.condition).clbits + cond_bits = condition_resources(operation._condition).clbits cindices_list = [self.clbits.index(clbit) for clbit in cond_bits] else: cindices_list = [] @@ -609,7 +609,7 @@ def replace_block_with_op(self, node_block, op, wire_pos_map, cycle_check=True): for nd in node_block: block_qargs |= set(nd.qargs) block_cargs |= set(nd.cargs) - cond = getattr(nd.op, "condition", None) + cond = getattr(nd.op, "_condition", None) if cond is not None: block_cargs.update(condition_resources(cond).clbits) diff --git a/qiskit/dagcircuit/dagnode.py b/qiskit/dagcircuit/dagnode.py index 60e2a7465707..151861d8028c 100644 --- a/qiskit/dagcircuit/dagnode.py +++ b/qiskit/dagcircuit/dagnode.py @@ -92,8 +92,8 @@ def key(var): def _condition_op_eq(node1, node2, bit_indices1, bit_indices2): - cond1 = node1.op.condition - cond2 = node2.op.condition + cond1 = node1.condition + cond2 = node2.condition if isinstance(cond1, expr.Expr) and isinstance(cond2, expr.Expr): if not expr.structurally_equivalent( cond1, cond2, _make_expr_key(bit_indices1), _make_expr_key(bit_indices2) diff --git a/qiskit/primitives/statevector_sampler.py b/qiskit/primitives/statevector_sampler.py index 78672e441dc4..486f35bc9364 100644 --- a/qiskit/primitives/statevector_sampler.py +++ b/qiskit/primitives/statevector_sampler.py @@ -287,6 +287,6 @@ def _final_measurement_mapping(circuit: QuantumCircuit) -> dict[tuple[ClassicalR def _has_control_flow(circuit: QuantumCircuit) -> bool: return any( - isinstance((op := instruction.operation), ControlFlowOp) or op.condition + isinstance((op := instruction.operation), ControlFlowOp) or op._condition for instruction in circuit ) diff --git a/qiskit/providers/basic_provider/basic_simulator.py b/qiskit/providers/basic_provider/basic_simulator.py index fd535d9f7278..9ac4760b214e 100644 --- a/qiskit/providers/basic_provider/basic_simulator.py +++ b/qiskit/providers/basic_provider/basic_simulator.py @@ -48,6 +48,7 @@ from qiskit.qobj import QasmQobj, QasmQobjConfig, QasmQobjExperiment from qiskit.result import Result from qiskit.transpiler import Target +from qiskit.utils.deprecation import deprecate_func from .basic_provider_job import BasicProviderJob from .basic_provider_tools import single_gate_matrix @@ -212,6 +213,14 @@ def _build_basic_target(self) -> Target: ) return target + @deprecate_func( + since="1.3.0", + removal_timeline="in Qiskit 2.0.0", + additional_msg="The `BackendConfiguration` class is part of the deprecated `BackendV1` " + "workflow, and no longer necessary for `BackendV2`. The individual configuration elements " + "can be retrieved directly from the backend or from the contained `Target` instance " + "(`backend.target)`).", + ) def configuration(self) -> BackendConfiguration: """Return the simulator backend configuration. @@ -532,7 +541,8 @@ def run( "initial_statevector": np.array([1, 0, 0, 1j]) / math.sqrt(2), } """ - # TODO: replace assemble with new run flow + # TODO: replace assemble with new run flow. If this is not achieved before 2.0, + # see removal note on `def _assemble`, L192 of qiskit/compiler/assembler.py from qiskit.compiler.assembler import _assemble out_options = {} diff --git a/qiskit/qasm2/export.py b/qiskit/qasm2/export.py index 3cf0d8942553..7c655dfd432b 100644 --- a/qiskit/qasm2/export.py +++ b/qiskit/qasm2/export.py @@ -246,12 +246,14 @@ def _instruction_call_site(operation): if operation.params: params = ",".join([pi_check(i, output="qasm", eps=1e-12) for i in operation.params]) qasm2_call = f"{qasm2_call}({params})" - if operation.condition is not None: - if not isinstance(operation.condition[0], ClassicalRegister): + if operation._condition is not None: + if not isinstance(operation._condition[0], ClassicalRegister): raise QASM2ExportError( "OpenQASM 2 can only condition on registers, but got '{operation.condition[0]}'" ) - qasm2_call = f"if({operation.condition[0].name}=={operation.condition[1]:d}) " + qasm2_call + qasm2_call = ( + f"if({operation._condition[0].name}=={operation._condition[1]:d}) " + qasm2_call + ) return qasm2_call diff --git a/qiskit/qasm2/parse.py b/qiskit/qasm2/parse.py index 6cdb0f70bba0..39ff388b6b45 100644 --- a/qiskit/qasm2/parse.py +++ b/qiskit/qasm2/parse.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """Python-space bytecode interpreter for the output of the main Rust parser logic.""" - +import warnings import dataclasses import math from typing import Iterable, Callable @@ -255,6 +255,11 @@ def from_bytecode(bytecode, custom_instructions: Iterable[CustomInstruction]): CircuitInstruction(gates[gate_id](*parameters), [qubits[q] for q in op_qubits]) ) elif opcode == OpCode.ConditionedGate: + warnings.warn( + "Conditioned gates in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) gate_id, parameters, op_qubits, creg, value = op.operands gate = gates[gate_id](*parameters).c_if(qc.cregs[creg], value) qc._append(CircuitInstruction(gate, [qubits[q] for q in op_qubits])) @@ -262,12 +267,22 @@ def from_bytecode(bytecode, custom_instructions: Iterable[CustomInstruction]): qubit, clbit = op.operands qc._append(CircuitInstruction(Measure(), (qubits[qubit],), (clbits[clbit],))) elif opcode == OpCode.ConditionedMeasure: + warnings.warn( + "Conditioned measurements in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) qubit, clbit, creg, value = op.operands measure = Measure().c_if(qc.cregs[creg], value) qc._append(CircuitInstruction(measure, (qubits[qubit],), (clbits[clbit],))) elif opcode == OpCode.Reset: qc._append(CircuitInstruction(Reset(), (qubits[op.operands[0]],))) elif opcode == OpCode.ConditionedReset: + warnings.warn( + "Conditioned resets in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) qubit, creg, value = op.operands reset = Reset().c_if(qc.cregs[creg], value) qc._append(CircuitInstruction(reset, (qubits[qubit],))) @@ -356,7 +371,7 @@ def __array__(self, dtype=None, copy=None): # to pickle ourselves, we just eagerly create the definition and pickle that. def __getstate__(self): - return (self.name, self.num_qubits, self.params, self.definition, self.condition) + return (self.name, self.num_qubits, self.params, self.definition, self._condition) def __setstate__(self, state): name, num_qubits, params, definition, condition = state diff --git a/qiskit/qasm3/exporter.py b/qiskit/qasm3/exporter.py index c76e296d6bab..d101f2cfbaa7 100644 --- a/qiskit/qasm3/exporter.py +++ b/qiskit/qasm3/exporter.py @@ -30,7 +30,6 @@ Barrier, CircuitInstruction, Clbit, - ControlledGate, Gate, Measure, Parameter, @@ -209,46 +208,60 @@ def dump(self, circuit, stream): # comparisons will work. _FIXED_PARAMETERS = (Parameter("p0"), Parameter("p1"), Parameter("p2"), Parameter("p3")) +_CANONICAL_STANDARD_GATES = { + standard: standard.gate_class(*_FIXED_PARAMETERS[: standard.num_params]) + for standard in StandardGate.all_gates() + if not standard.is_controlled_gate +} +_CANONICAL_CONTROLLED_STANDARD_GATES = { + standard: [ + standard.gate_class(*_FIXED_PARAMETERS[: standard.num_params], ctrl_state=ctrl_state) + for ctrl_state in range(1 << standard.num_ctrl_qubits) + ] + for standard in StandardGate.all_gates() + if standard.is_controlled_gate +} + # Mapping of symbols defined by `stdgates.inc` to their gate definition source. _KNOWN_INCLUDES = { "stdgates.inc": { - "p": library.PhaseGate(*_FIXED_PARAMETERS[:1]), - "x": library.XGate(), - "y": library.YGate(), - "z": library.ZGate(), - "h": library.HGate(), - "s": library.SGate(), - "sdg": library.SdgGate(), - "t": library.TGate(), - "tdg": library.TdgGate(), - "sx": library.SXGate(), - "rx": library.RXGate(*_FIXED_PARAMETERS[:1]), - "ry": library.RYGate(*_FIXED_PARAMETERS[:1]), - "rz": library.RZGate(*_FIXED_PARAMETERS[:1]), - "cx": library.CXGate(), - "cy": library.CYGate(), - "cz": library.CZGate(), - "cp": library.CPhaseGate(*_FIXED_PARAMETERS[:1]), - "crx": library.CRXGate(*_FIXED_PARAMETERS[:1]), - "cry": library.CRYGate(*_FIXED_PARAMETERS[:1]), - "crz": library.CRZGate(*_FIXED_PARAMETERS[:1]), - "ch": library.CHGate(), - "swap": library.SwapGate(), - "ccx": library.CCXGate(), - "cswap": library.CSwapGate(), - "cu": library.CUGate(*_FIXED_PARAMETERS[:4]), - "CX": library.CXGate(), - "phase": library.PhaseGate(*_FIXED_PARAMETERS[:1]), - "cphase": library.CPhaseGate(*_FIXED_PARAMETERS[:1]), - "id": library.IGate(), - "u1": library.U1Gate(*_FIXED_PARAMETERS[:1]), - "u2": library.U2Gate(*_FIXED_PARAMETERS[:2]), - "u3": library.U3Gate(*_FIXED_PARAMETERS[:3]), + "p": _CANONICAL_STANDARD_GATES[StandardGate.PhaseGate], + "x": _CANONICAL_STANDARD_GATES[StandardGate.XGate], + "y": _CANONICAL_STANDARD_GATES[StandardGate.YGate], + "z": _CANONICAL_STANDARD_GATES[StandardGate.ZGate], + "h": _CANONICAL_STANDARD_GATES[StandardGate.HGate], + "s": _CANONICAL_STANDARD_GATES[StandardGate.SGate], + "sdg": _CANONICAL_STANDARD_GATES[StandardGate.SdgGate], + "t": _CANONICAL_STANDARD_GATES[StandardGate.TGate], + "tdg": _CANONICAL_STANDARD_GATES[StandardGate.TdgGate], + "sx": _CANONICAL_STANDARD_GATES[StandardGate.SXGate], + "rx": _CANONICAL_STANDARD_GATES[StandardGate.RXGate], + "ry": _CANONICAL_STANDARD_GATES[StandardGate.RYGate], + "rz": _CANONICAL_STANDARD_GATES[StandardGate.RZGate], + "cx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CXGate][1], + "cy": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CYGate][1], + "cz": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CZGate][1], + "cp": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CPhaseGate][1], + "crx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRXGate][1], + "cry": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRYGate][1], + "crz": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CRZGate][1], + "ch": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CHGate][1], + "swap": _CANONICAL_STANDARD_GATES[StandardGate.SwapGate], + "ccx": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CCXGate][3], + "cswap": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CSwapGate][1], + "cu": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CUGate][1], + "CX": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CXGate][1], + "phase": _CANONICAL_STANDARD_GATES[StandardGate.PhaseGate], + "cphase": _CANONICAL_CONTROLLED_STANDARD_GATES[StandardGate.CPhaseGate][1], + "id": _CANONICAL_STANDARD_GATES[StandardGate.IGate], + "u1": _CANONICAL_STANDARD_GATES[StandardGate.U1Gate], + "u2": _CANONICAL_STANDARD_GATES[StandardGate.U2Gate], + "u3": _CANONICAL_STANDARD_GATES[StandardGate.U3Gate], }, } _BUILTIN_GATES = { - "U": library.UGate(*_FIXED_PARAMETERS[:3]), + "U": _CANONICAL_STANDARD_GATES[StandardGate.UGate], } @@ -479,9 +492,14 @@ def register_gate( def get_gate(self, gate: Gate) -> ast.Identifier | None: """Lookup the identifier for a given `Gate`, if it exists.""" canonical = _gate_canonical_form(gate) - # `our_defn.canonical is None` means a basis gate that we should assume is always valid. if (our_defn := self.gates.get(gate.name)) is not None and ( - our_defn.canonical is None or our_defn.canonical == canonical + # We arrange things such that the known definitions for the vast majority of gates we + # will encounter are the exact same canonical instance, so an `is` check saves time. + our_defn.canonical is canonical + # `our_defn.canonical is None` means a basis gate that we should assume is always valid. + or our_defn.canonical is None + # The last catch, if the canonical form is some custom gate that compares equal to this. + or our_defn.canonical == canonical ): return ast.Identifier(gate.name) if canonical._standard_gate is not None: @@ -506,11 +524,14 @@ def _gate_canonical_form(gate: Gate) -> Gate: # If a gate is part of the Qiskit standard-library gates, we know we can safely produce a # reparameterised gate by passing the parameters positionally to the standard-gate constructor # (and control state, if appropriate). - if gate._standard_gate and not isinstance(gate, ControlledGate): - return gate.base_class(*_FIXED_PARAMETERS[: len(gate.params)]) - elif gate._standard_gate: - return gate.base_class(*_FIXED_PARAMETERS[: len(gate.params)], ctrl_state=gate.ctrl_state) - return gate + standard = gate._standard_gate + if standard is None: + return gate + return ( + _CANONICAL_CONTROLLED_STANDARD_GATES[standard][gate.ctrl_state] + if standard.is_controlled_gate + else _CANONICAL_STANDARD_GATES[standard] + ) @dataclasses.dataclass @@ -988,13 +1009,13 @@ def build_current_scope(self) -> List[ast.Statement]: f" but received '{instruction.operation}'" ) - if instruction.operation.condition is None: + if instruction.operation._condition is None: statements.extend(nodes) else: body = ast.ProgramBlock(nodes) statements.append( ast.BranchingStatement( - self.build_expression(_lift_condition(instruction.operation.condition)), + self.build_expression(_lift_condition(instruction.operation._condition)), body, ) ) diff --git a/qiskit/qpy/binary_io/circuits.py b/qiskit/qpy/binary_io/circuits.py index 3fe1834db6ef..174acceb59e4 100644 --- a/qiskit/qpy/binary_io/circuits.py +++ b/qiskit/qpy/binary_io/circuits.py @@ -318,6 +318,13 @@ def _read_instruction( use_symengine, standalone_vars, ) + if condition is not None: + warnings.warn( + f"The .condition attribute on {gate_name} will be loaded as an IfElseOp " + "starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) inst_obj.condition = condition if instruction.label_size > 0: inst_obj.label = label @@ -414,6 +421,12 @@ def _read_instruction( gate = gate_class(*params) if condition: if not isinstance(gate, ControlFlowOp): + warnings.warn( + f"The .condition attribute on {gate} will be loaded as an " + "IfElseOp starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) gate = gate.c_if(*condition) else: gate.condition = condition @@ -761,13 +774,13 @@ def _write_instruction( condition_type = type_keys.Condition.NONE condition_register = b"" condition_value = 0 - if (op_condition := getattr(instruction.operation, "condition", None)) is not None: + if (op_condition := getattr(instruction.operation, "_condition", None)) is not None: if isinstance(op_condition, expr.Expr): condition_type = type_keys.Condition.EXPRESSION else: condition_type = type_keys.Condition.TWO_TUPLE - condition_register = _dumps_register(instruction.operation.condition[0], index_map) - condition_value = int(instruction.operation.condition[1]) + condition_register = _dumps_register(instruction.operation._condition[0], index_map) + condition_value = int(instruction.operation._condition[1]) gate_class_name = gate_class_name.encode(common.ENCODE) label = getattr(instruction.operation, "label", None) diff --git a/qiskit/quantum_info/operators/symplectic/base_pauli.py b/qiskit/quantum_info/operators/symplectic/base_pauli.py index 1d9e88929b2d..f49127bc32d0 100644 --- a/qiskit/quantum_info/operators/symplectic/base_pauli.py +++ b/qiskit/quantum_info/operators/symplectic/base_pauli.py @@ -33,6 +33,8 @@ # utility for _to_matrix _PARITY = np.array([-1 if bin(i).count("1") % 2 else 1 for i in range(256)], dtype=complex) +# Utility for `_to_label` +_TO_LABEL_CHARS = np.array([ord("I"), ord("X"), ord("Z"), ord("Y")], dtype=np.uint8) class BasePauli(BaseOperator, AdjointMixin, MultiplyMixin): @@ -496,25 +498,15 @@ def _to_label(z, x, phase, group_phase=False, full_group=True, return_phase=Fals the phase ``q`` for the coefficient :math:`(-i)^(q + x.z)` for the label from the full Pauli group. """ - num_qubits = z.size - phase = int(phase) - coeff_labels = {0: "", 1: "-i", 2: "-", 3: "i"} - label = "" - for i in range(num_qubits): - if not z[num_qubits - 1 - i]: - if not x[num_qubits - 1 - i]: - label += "I" - else: - label += "X" - elif not x[num_qubits - 1 - i]: - label += "Z" - else: - label += "Y" - if not group_phase: - phase -= 1 - phase %= 4 + # Map each qubit to the {I: 0, X: 1, Z: 2, Y: 3} integer form, then use Numpy advanced + # indexing to get a new data buffer which is compatible with an ASCII string label. + index = z << 1 + index += x + ascii_label = _TO_LABEL_CHARS[index[::-1]].data.tobytes() + phase = (int(phase) if group_phase else int(phase) - ascii_label.count(b"Y")) % 4 + label = ascii_label.decode("ascii") if phase and full_group: - label = coeff_labels[phase] + label + label = ("", "-i", "-", "i")[phase] + label if return_phase: return label, phase return label diff --git a/qiskit/quantum_info/operators/symplectic/clifford_circuits.py b/qiskit/quantum_info/operators/symplectic/clifford_circuits.py index 283893e59f58..44bcd31e2da5 100644 --- a/qiskit/quantum_info/operators/symplectic/clifford_circuits.py +++ b/qiskit/quantum_info/operators/symplectic/clifford_circuits.py @@ -81,7 +81,7 @@ def _append_operation(clifford, operation, qargs=None): else: # assert isinstance(gate, Instruction) name = gate.name - if getattr(gate, "condition", None) is not None: + if getattr(gate, "_condition", None) is not None: raise QiskitError("Conditional gate is not a valid Clifford operation.") # Apply gate if it is a Clifford basis gate diff --git a/qiskit/result/mitigation/base_readout_mitigator.py b/qiskit/result/mitigation/base_readout_mitigator.py index f4ca398b73cf..296565d47d01 100644 --- a/qiskit/result/mitigation/base_readout_mitigator.py +++ b/qiskit/result/mitigation/base_readout_mitigator.py @@ -21,7 +21,7 @@ class BaseReadoutMitigator(ABC): - """Base readout error mitigator class.""" + """This class is DEPRECATED. Base readout error mitigator class.""" @abstractmethod def quasi_probabilities( diff --git a/qiskit/result/mitigation/correlated_readout_mitigator.py b/qiskit/result/mitigation/correlated_readout_mitigator.py index 99e6f9ae4145..190c0509af0e 100644 --- a/qiskit/result/mitigation/correlated_readout_mitigator.py +++ b/qiskit/result/mitigation/correlated_readout_mitigator.py @@ -18,6 +18,7 @@ import numpy as np from qiskit.exceptions import QiskitError +from qiskit.utils.deprecation import deprecate_func from ..distributions.quasi import QuasiDistribution from ..counts import Counts from .base_readout_mitigator import BaseReadoutMitigator @@ -25,7 +26,7 @@ class CorrelatedReadoutMitigator(BaseReadoutMitigator): - """N-qubit readout error mitigator. + """This class is DEPRECATED. N-qubit readout error mitigator. Mitigates :meth:`expectation_value` and :meth:`quasi_probabilities`. The mitigation_matrix should be calibrated using qiskit experiments. @@ -34,6 +35,13 @@ class CorrelatedReadoutMitigator(BaseReadoutMitigator): :math:`2^N x 2^N` so the mitigation complexity is :math:`O(4^N)`. """ + @deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", + ) def __init__(self, assignment_matrix: np.ndarray, qubits: Optional[Iterable[int]] = None): """Initialize a CorrelatedReadoutMitigator diff --git a/qiskit/result/mitigation/local_readout_mitigator.py b/qiskit/result/mitigation/local_readout_mitigator.py index 197c3f00d9be..ee4b970e085c 100644 --- a/qiskit/result/mitigation/local_readout_mitigator.py +++ b/qiskit/result/mitigation/local_readout_mitigator.py @@ -19,6 +19,7 @@ import numpy as np from qiskit.exceptions import QiskitError +from qiskit.utils.deprecation import deprecate_func from ..distributions.quasi import QuasiDistribution from ..counts import Counts from .base_readout_mitigator import BaseReadoutMitigator @@ -26,7 +27,7 @@ class LocalReadoutMitigator(BaseReadoutMitigator): - """1-qubit tensor product readout error mitigator. + """This class is DEPRECATED. 1-qubit tensor product readout error mitigator. Mitigates :meth:`expectation_value` and :meth:`quasi_probabilities`. The mitigator should either be calibrated using qiskit experiments, @@ -37,6 +38,13 @@ class LocalReadoutMitigator(BaseReadoutMitigator): so it is more efficient than the :class:`CorrelatedReadoutMitigator` class. """ + @deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", + ) def __init__( self, assignment_matrices: Optional[List[np.ndarray]] = None, diff --git a/qiskit/result/mitigation/utils.py b/qiskit/result/mitigation/utils.py index 823e2b69a6ca..8e7ed9e2a6af 100644 --- a/qiskit/result/mitigation/utils.py +++ b/qiskit/result/mitigation/utils.py @@ -19,12 +19,20 @@ import numpy as np from qiskit.exceptions import QiskitError +from qiskit.utils.deprecation import deprecate_func from ..utils import marginal_counts from ..counts import Counts logger = logging.getLogger(__name__) +@deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", +) def z_diagonal(dim, dtype=float): r"""Return the diagonal for the operator :math:`Z^\otimes n`""" parity = np.zeros(dim, dtype=dtype) @@ -33,6 +41,13 @@ def z_diagonal(dim, dtype=float): return (-1) ** np.mod(parity, 2) +@deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", +) def expval_with_stddev(coeffs: np.ndarray, probs: np.ndarray, shots: int) -> Tuple[float, float]: """Compute expectation value and standard deviation. Args: @@ -60,6 +75,13 @@ def expval_with_stddev(coeffs: np.ndarray, probs: np.ndarray, shots: int) -> Tup return [expval, calc_stddev] +@deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", +) def stddev(probs, shots): """Calculate stddev dict""" ret = {} @@ -69,6 +91,13 @@ def stddev(probs, shots): return ret +@deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", +) def str2diag(string): """Transform diagonal from a string to a numpy array""" chars = { @@ -85,6 +114,13 @@ def str2diag(string): return ret +@deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", +) def counts_to_vector(counts: Counts, num_qubits: int) -> Tuple[np.ndarray, int]: """Transforms Counts to a probability vector""" vec = np.zeros(2**num_qubits, dtype=float) @@ -96,6 +132,13 @@ def counts_to_vector(counts: Counts, num_qubits: int) -> Tuple[np.ndarray, int]: return vec, shots +@deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", +) def remap_qubits( vec: np.ndarray, num_qubits: int, qubits: Optional[List[int]] = None ) -> np.ndarray: @@ -108,6 +151,13 @@ def remap_qubits( return vec +@deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", +) def marganalize_counts( counts: Counts, qubit_index: Dict[int, int], @@ -129,6 +179,13 @@ def marganalize_counts( return counts +@deprecate_func( + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `qiskit.result.mitigation` module is deprecated in favor of " + "the https://github.com/Qiskit/qiskit-addon-mthree package.", +) def counts_probability_vector( counts: Counts, qubit_index: Dict[int, int], diff --git a/qiskit/transpiler/passes/optimization/collect_cliffords.py b/qiskit/transpiler/passes/optimization/collect_cliffords.py index 5ee75af98000..8b26d04045c1 100644 --- a/qiskit/transpiler/passes/optimization/collect_cliffords.py +++ b/qiskit/transpiler/passes/optimization/collect_cliffords.py @@ -83,7 +83,7 @@ def __init__( def _is_clifford_gate(node, matrix_based=False): """Specifies whether a node holds a clifford gate.""" - if getattr(node.op, "condition", None) is not None: + if getattr(node.op, "_condition", None) is not None: return False if node.op.name in clifford_gate_names: return True diff --git a/qiskit/transpiler/passes/optimization/collect_linear_functions.py b/qiskit/transpiler/passes/optimization/collect_linear_functions.py index 158440e6a8a1..25a66e2bf9dc 100644 --- a/qiskit/transpiler/passes/optimization/collect_linear_functions.py +++ b/qiskit/transpiler/passes/optimization/collect_linear_functions.py @@ -71,7 +71,7 @@ def __init__( def _is_linear_gate(node): """Specifies whether a node holds a linear gate.""" - return node.op.name in ("cx", "swap") and getattr(node.op, "condition", None) is None + return node.op.name in ("cx", "swap") and getattr(node, "condition", None) is None def _collapse_to_linear_function(circuit): diff --git a/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py b/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py index e0dd61ff6cf4..34d51a17fe4a 100644 --- a/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py +++ b/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py @@ -120,7 +120,7 @@ def collect_key(x): if not isinstance(x, DAGOpNode): return "d" if isinstance(x.op, Gate): - if x.op.is_parameterized() or getattr(x.op, "condition", None) is not None: + if x.op.is_parameterized() or getattr(x.op, "_condition", None) is not None: return "c" return "b" + chr(ord("a") + len(x.qargs)) return "d" @@ -133,7 +133,7 @@ def collect_key(x): # check if the node is a gate and if it is parameterized if ( - getattr(nd.op, "condition", None) is not None + getattr(nd.op, "_condition", None) is not None or nd.op.is_parameterized() or not isinstance(nd.op, Gate) ): diff --git a/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py b/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py index 97324e2376cd..8c1b67b595a0 100644 --- a/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py +++ b/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py @@ -49,7 +49,7 @@ def _skip_node(self, node): # checking can be extended to cover additional cases. if getattr(node.op, "_directive", False) or node.name in {"measure", "reset", "delay"}: return True - if getattr(node.op, "condition", None): + if getattr(node, "condition", None): return True if node.op.is_parameterized(): return True diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_gates.py b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py index f8302b9232c0..466bcc4d5fcc 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_gates.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py @@ -86,7 +86,7 @@ def run(self, dag): for current_node in run: left_name = current_node.name if ( - getattr(current_node.op, "condition", None) is not None + getattr(current_node, "condition", None) is not None or len(current_node.qargs) != 1 or left_name not in ["p", "u1", "u2", "u3", "u", "id"] ): diff --git a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py index 6682e7ebbdba..7345a5bc40fd 100644 --- a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py +++ b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py @@ -40,7 +40,7 @@ def run(self, dag): swaps = dag.op_nodes(SwapGate) for swap in swaps[::-1]: - if getattr(swap.op, "condition", None) is not None: + if getattr(swap.op, "_condition", None) is not None: continue final_successor = [] for successor in dag.descendants(swap): diff --git a/qiskit/transpiler/passes/optimization/template_matching/backward_match.py b/qiskit/transpiler/passes/optimization/template_matching/backward_match.py index d194d1cbbddf..dc1ff7b3447f 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/backward_match.py +++ b/qiskit/transpiler/passes/optimization/template_matching/backward_match.py @@ -242,7 +242,7 @@ def _is_same_op(self, node_circuit, node_template): Returns: bool: True if the same, False otherwise. """ - return node_circuit.op == node_template.op + return node_circuit.op.soft_compare(node_template.op) def _is_same_q_conf(self, node_circuit, node_template, qarg_circuit): """ @@ -304,15 +304,15 @@ def _is_same_c_conf(self, node_circuit, node_template, carg_circuit): """ if ( node_circuit.type == "op" - and getattr(node_circuit.op, "condition", None) + and getattr(node_circuit.op, "_condition", None) and node_template.type == "op" - and getattr(node_template.op, "condition", None) + and getattr(node_template.op, "_condition", None) ): if set(carg_circuit) != set(node_template.cindices): return False if ( - getattr(node_circuit.op, "condition", None)[1] - != getattr(node_template.op, "condition", None)[1] + getattr(node_circuit.op, "_condition", None)[1] + != getattr(node_template.op, "_condition", None)[1] ): return False return True diff --git a/qiskit/transpiler/passes/optimization/template_matching/forward_match.py b/qiskit/transpiler/passes/optimization/template_matching/forward_match.py index d8dd5bb2b9a2..b4232d8ff6d2 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/forward_match.py +++ b/qiskit/transpiler/passes/optimization/template_matching/forward_match.py @@ -311,15 +311,15 @@ def _is_same_c_conf(self, node_circuit, node_template): """ if ( node_circuit.type == "op" - and getattr(node_circuit.op, "condition", None) + and getattr(node_circuit.op, "_condition", None) and node_template.type == "op" - and getattr(node_template.op, "condition", None) + and getattr(node_template.op, "_condition", None) ): if set(self.carg_indices) != set(node_template.cindices): return False if ( - getattr(node_circuit.op, "condition", None)[1] - != getattr(node_template.op, "condition", None)[1] + getattr(node_circuit.op, "_condition", None)[1] + != getattr(node_template.op, "_condition", None)[1] ): return False return True diff --git a/qiskit/transpiler/passes/routing/star_prerouting.py b/qiskit/transpiler/passes/routing/star_prerouting.py index 53bc971a268b..259d79ba636b 100644 --- a/qiskit/transpiler/passes/routing/star_prerouting.py +++ b/qiskit/transpiler/passes/routing/star_prerouting.py @@ -223,7 +223,7 @@ def filter_fn(node): return ( len(node.qargs) <= 2 and len(node.cargs) == 0 - and getattr(node.op, "condition", None) is None + and getattr(node, "condition", None) is None and not isinstance(node.op, Barrier) ) @@ -372,7 +372,7 @@ def _extract_nodes(nodes, dag): qubit_indices = [dag.find_bit(qubit).index for qubit in node.qargs] classical_bit_indices = set() - if node.op.condition is not None: + if node.condition is not None: classical_bit_indices.update(condition_resources(node.op.condition).clbits) if isinstance(node.op, SwitchCaseOp): diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 8bdd0a761edf..870217f64754 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -28,7 +28,7 @@ from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.transpiler.basepasses import TransformationPass from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.circuit import ControlledGate, EquivalenceLibrary, equivalence +from qiskit.circuit import ControlledGate, EquivalenceLibrary, equivalence, Qubit from qiskit.transpiler.passes.utils import control_flow from qiskit.transpiler.target import Target from qiskit.transpiler.coupling import CouplingMap @@ -135,6 +135,60 @@ def set_methods(self, hls_name, hls_methods): self.methods[hls_name] = hls_methods +class QubitContext: + """Correspondence between local qubits and global qubits. + + An internal class for handling recursion within HighLevelSynthesis. + Provides correspondence between the qubit indices of an internal DAG, + aka the "local qubits" (for instance, of the definition circuit + of a custom gate), and the qubit indices of the original DAG, aka the + "global qubits". + + Since the local qubits are consecutive integers starting at zero, + i.e. 0, 1, 2, etc., the correspondence is kept using a list, with the + entry in position `k` representing the global qubit that corresponds + to the local qubit `k`. + """ + + def __init__(self, local_to_global: list): + self._local_to_global = local_to_global + + def num_qubits(self) -> int: + """Returns the number of local qubits.""" + return len(self._local_to_global) + + def add_qubit(self, global_qubit) -> int: + """Extends the correspondence by an additional qubit that + maps to the given global qubit. Returns the index of the + new local qubit. + """ + new_local_qubit = len(self._local_to_global) + self._local_to_global.append(global_qubit) + return new_local_qubit + + def to_global_mapping(self) -> list: + """Returns the local-to-global mapping.""" + return self._local_to_global + + def to_local_mapping(self) -> dict: + """Returns the global-to-local mapping .""" + return {j: i for (i, j) in enumerate(self._local_to_global)} + + def restrict(self, qubits: list[int] | tuple[int]) -> "QubitContext": + """Restricts the context to a subset of qubits, remapping the indices + to be consecutive integers starting at zero. + """ + return QubitContext([self._local_to_global[q] for q in qubits]) + + def to_global(self, qubit: int) -> int: + """Returns the global qubits corresponding to the given local qubits.""" + return self._local_to_global[qubit] + + def to_globals(self, qubits: list[int]) -> list[int]: + """Returns the global qubits corresponding to the given local qubits.""" + return [self._local_to_global[q] for q in qubits] + + class HighLevelSynthesis(TransformationPass): r"""Synthesize higher-level objects and unroll custom definitions. @@ -271,96 +325,160 @@ def run(self, dag: DAGCircuit) -> DAGCircuit: (for instance, when the specified synthesis method is not available). """ qubits = tuple(dag.find_bit(q).index for q in dag.qubits) + context = QubitContext(list(range(len(dag.qubits)))) + tracker = QubitTracker(num_qubits=dag.num_qubits()) if self.qubits_initially_zero: - clean, dirty = set(qubits), set() - else: - clean, dirty = set(), set(qubits) + tracker.set_clean(context.to_globals(qubits)) - tracker = QubitTracker(qubits=qubits, clean=clean, dirty=dirty) - return self._run(dag, tracker) + out_dag = self._run(dag, tracker, context, use_ancillas=True, top_level=True) + return out_dag - def _run(self, dag: DAGCircuit, tracker: QubitTracker) -> DAGCircuit: - # Check if HighLevelSynthesis can be skipped. - for node in dag.op_nodes(): - qubits = tuple(dag.find_bit(q).index for q in node.qargs) - if not self._definitely_skip_node(node, qubits, dag): - break - else: - # The for-loop terminates without reaching the break statement - return dag + def _run( + self, + dag: DAGCircuit, + tracker: QubitTracker, + context: QubitContext, + use_ancillas: bool, + top_level: bool, + ) -> DAGCircuit: + """ + The main recursive function that synthesizes a DAGCircuit. + + Input: + dag: the DAG to be synthesized. + tracker: the global tracker, tracking the state of original qubits. + context: the correspondence between the dag's qubits and the global qubits. + use_ancillas: if True, synthesis algorithms are allowed to use ancillas. + top_level: specifies if this is the top-level of the recursion. + + The function returns the synthesized DAG. - # Start by analyzing the nodes in the DAG. This for-loop is a first version of a potentially - # more elaborate approach to find good operation/ancilla allocations. It greedily iterates - # over the nodes, checking whether we can synthesize them, while keeping track of the - # qubit states. It does not trade-off allocations and just gives all available qubits - # to the current operation (a "the-first-takes-all" approach). + Note that by using the auxiliary qubits to synthesize operations present in the input DAG, + the synthesized DAG may be defined over more qubits than the input DAG. In this case, + the function update in-place the global qubits tracker and extends the local-to-global + context. + """ + + if dag.num_qubits() != context.num_qubits(): + raise TranspilerError("HighLevelSynthesis internal error.") + + # STEP 1: Check if HighLevelSynthesis can be skipped altogether. This is only + # done at the top-level since this does not update the global qubits tracker. + if top_level: + for node in dag.op_nodes(): + qubits = tuple(dag.find_bit(q).index for q in node.qargs) + if not self._definitely_skip_node(node, qubits, dag): + break + else: + # The for-loop terminates without reaching the break statement + if dag.num_qubits() != context.num_qubits(): + raise TranspilerError("HighLevelSynthesis internal error.") + return dag + + # STEP 2: Analyze the nodes in the DAG. For each node in the DAG that needs + # to be synthesized, we recursively synthesize it and store the result. For + # instance, the result of synthesizing a custom gate is a DAGCircuit corresponding + # to the (recursively synthesized) gate's definition. When the result is a + # DAG, we also store its context (the mapping of its qubits to global qubits). + # In addition, we keep track of the qubit states using the (global) qubits tracker. + # + # Note: This is a first version of a potentially more elaborate approach to find + # good operation/ancilla allocations. The current approach is greedy and just gives + # all available ancilla qubits to the current operation ("the-first-takes-all" approach). + # It does not distribute ancilla qubits between different operations present in the DAG. synthesized_nodes = {} for node in dag.topological_op_nodes(): qubits = tuple(dag.find_bit(q).index for q in node.qargs) + processed = False synthesized = None - used_qubits = None + synthesized_context = None + + # Start by handling special operations. Other cases can also be + # considered: swaps, automatically simplifying control gate (e.g. if + # a control is 0). + if node.op.name in ["id", "delay", "barrier"]: + # tracker not updated, these are no-ops + processed = True + + elif node.op.name == "reset": + # reset qubits to 0 + tracker.set_clean(context.to_globals(qubits)) + processed = True # check if synthesis for the operation can be skipped - if self._definitely_skip_node(node, qubits, dag): - pass + elif self._definitely_skip_node(node, qubits, dag): + tracker.set_dirty(context.to_globals(qubits)) # next check control flow elif node.is_control_flow(): - dag.substitute_node( - node, - control_flow.map_blocks(partial(self._run, tracker=tracker.copy()), node.op), - propagate_condition=False, + inner_context = context.restrict(qubits) + synthesized = control_flow.map_blocks( + partial( + self._run, + tracker=tracker, + context=inner_context, + use_ancillas=False, + top_level=False, + ), + node.op, ) # now we are free to synthesize else: - # this returns the synthesized operation and the qubits it acts on -- note that this - # may be different from the original qubits, since we may use auxiliary qubits - synthesized, used_qubits = self._synthesize_operation(node.op, qubits, tracker) + # This returns the synthesized operation and its context (when the result is + # a DAG, it's the correspondence between its qubits and the global qubits). + # Also note that the DAG may use auxiliary qubits. The qubits tracker and the + # current DAG's context are updated in-place. + synthesized, synthesized_context = self._synthesize_operation( + node.op, qubits, tracker, context, use_ancillas=use_ancillas + ) - # if the synthesis changed the operation (i.e. it is not None), store the result - # and mark the operation qubits as used + # If the synthesis changed the operation (i.e. it is not None), store the result. if synthesized is not None: - synthesized_nodes[node] = (synthesized, used_qubits) - tracker.used(qubits) # assumes that auxiliary are returned in the same state + synthesized_nodes[node] = (synthesized, synthesized_context) - # if the synthesis did not change anything, just update the qubit tracker - # other cases can be added: swaps, controlled gates (e.g. if control is 0), ... - else: - if node.op.name in ["id", "delay", "barrier"]: - pass # tracker not updated, these are no-ops - elif node.op.name == "reset": - tracker.reset(qubits) # reset qubits to 0 - else: - tracker.used(qubits) # any other op used the clean state up + # If the synthesis did not change anything, just update the qubit tracker. + elif not processed: + tracker.set_dirty(context.to_globals(qubits)) - # we did not change anything just return the input + # We did not change anything just return the input. if len(synthesized_nodes) == 0: + if dag.num_qubits() != context.num_qubits(): + raise TranspilerError("HighLevelSynthesis internal error.") return dag - # Otherwise, we will rebuild with the new operations. Note that we could also + # STEP 3. We rebuild the DAG with new operations. Note that we could also # check if no operation changed in size and substitute in-place, but rebuilding is # generally as fast or faster, unless very few operations are changed. out = dag.copy_empty_like() - index_to_qubit = dict(enumerate(dag.qubits)) + num_additional_qubits = context.num_qubits() - out.num_qubits() + + if num_additional_qubits > 0: + out.add_qubits([Qubit() for _ in range(num_additional_qubits)]) + + index_to_qubit = dict(enumerate(out.qubits)) + outer_to_local = context.to_local_mapping() for node in dag.topological_op_nodes(): if node in synthesized_nodes: - op, qubits = synthesized_nodes[node] - qargs = tuple(index_to_qubit[index] for index in qubits) + op, op_context = synthesized_nodes[node] + if isinstance(op, Operation): - out.apply_operation_back(op, qargs, cargs=[]) + out.apply_operation_back(op, node.qargs, node.cargs) continue if isinstance(op, QuantumCircuit): op = circuit_to_dag(op, copy_operations=False) + inner_to_global = op_context.to_global_mapping() if isinstance(op, DAGCircuit): qubit_map = { - qubit: index_to_qubit[index] for index, qubit in zip(qubits, op.qubits) + q: index_to_qubit[outer_to_local[inner_to_global[i]]] + for (i, q) in enumerate(op.qubits) } clbit_map = dict(zip(op.clbits, node.cargs)) + for sub_node in op.op_nodes(): out.apply_operation_back( sub_node.op, @@ -368,11 +486,15 @@ def _run(self, dag: DAGCircuit, tracker: QubitTracker) -> DAGCircuit: tuple(clbit_map[carg] for carg in sub_node.cargs), ) out.global_phase += op.global_phase + else: - raise RuntimeError(f"Unexpected synthesized type: {type(op)}") + raise TranspilerError(f"Unexpected synthesized type: {type(op)}") else: out.apply_operation_back(node.op, node.qargs, node.cargs, check=False) + if out.num_qubits() != context.num_qubits(): + raise TranspilerError("HighLevelSynthesis internal error.") + return out def _synthesize_operation( @@ -380,7 +502,23 @@ def _synthesize_operation( operation: Operation, qubits: tuple[int], tracker: QubitTracker, - ) -> tuple[QuantumCircuit | Operation | DAGCircuit | None, list[int] | None]: + context: QubitContext, + use_ancillas: bool, + ) -> tuple[QuantumCircuit | Operation | DAGCircuit | None, QubitContext | None]: + """ + Synthesizes an operation. The function receives the qubits on which the operation + is defined in the current DAG, the correspondence between the qubits of the current + DAG and the global qubits and the global qubits tracker. The function returns the + result of synthesizing the operation. The value of `None` means that the operation + should remain as it is. When it's a circuit, we also return the context, i.e. the + correspondence of its local qubits and the global qubits. The function changes + in-place the tracker (state of the global qubits), the qubits (when the synthesized + operation is defined over additional ancilla qubits), and the context (to keep track + of where these ancilla qubits maps to). + """ + + synthesized_context = None + # Try to synthesize the operation. We'll go through the following options: # (1) Annotations: if the operator is annotated, synthesize the base operation # and then apply the modifiers. Returns a circuit (e.g. applying a power) @@ -389,31 +527,62 @@ def _synthesize_operation( # if the operation is a Clifford). Returns a circuit. # (3) Unrolling custom definitions: try defining the operation if it is not yet # in the set of supported instructions. Returns a circuit. + # # If any of the above were triggered, we will recurse and go again through these steps # until no further change occurred. At this point, we convert circuits to DAGs (the final # possible return type). If there was no change, we just return ``None``. + num_original_qubits = len(qubits) + qubits = list(qubits) + synthesized = None # Try synthesizing via AnnotatedOperation. This is faster than an isinstance check # but a bit less safe since someone could create operations with a ``modifiers`` attribute. if len(modifiers := getattr(operation, "modifiers", [])) > 0: - # The base operation must be synthesized without using potential control qubits + # Note: the base operation must be synthesized without using potential control qubits # used in the modifiers. num_ctrl = sum( mod.num_ctrl_qubits for mod in modifiers if isinstance(mod, ControlModifier) ) baseop_qubits = qubits[num_ctrl:] # reminder: control qubits are the first ones - baseop_tracker = tracker.copy(drop=qubits[:num_ctrl]) # no access to control qubits # get qubits of base operation + control_qubits = qubits[0:num_ctrl] + + # Do not allow access to control qubits + tracker.disable(context.to_globals(control_qubits)) synthesized_base_op, _ = self._synthesize_operation( - operation.base_op, baseop_qubits, baseop_tracker + operation.base_op, + baseop_qubits, + tracker, + context, + use_ancillas=use_ancillas, ) + if synthesized_base_op is None: synthesized_base_op = operation.base_op elif isinstance(synthesized_base_op, DAGCircuit): synthesized_base_op = dag_to_circuit(synthesized_base_op) + # Handle the case that synthesizing the base operation introduced + # additional qubits (e.g. the base operation is a circuit that includes + # an MCX gate). + if synthesized_base_op.num_qubits > len(baseop_qubits): + global_aux_qubits = tracker.borrow( + synthesized_base_op.num_qubits - len(baseop_qubits), + context.to_globals(baseop_qubits), + ) + global_to_local = context.to_local_mapping() + for aq in global_aux_qubits: + if aq in global_to_local: + qubits.append(global_to_local[aq]) + else: + new_local_qubit = context.add_qubit(aq) + qubits.append(new_local_qubit) + # Restore access to control qubits. + tracker.enable(context.to_globals(control_qubits)) + + # This step currently does not introduce ancilla qubits. synthesized = self._apply_annotations(synthesized_base_op, operation.modifiers) # If it was no AnnotatedOperation, try synthesizing via HLS or by unrolling. @@ -421,57 +590,106 @@ def _synthesize_operation( # Try synthesis via HLS -- which will return ``None`` if unsuccessful. indices = qubits if self._use_qubit_indices else None if len(hls_methods := self._methods_to_try(operation.name)) > 0: + if use_ancillas: + num_clean_available = tracker.num_clean(context.to_globals(qubits)) + num_dirty_available = tracker.num_dirty(context.to_globals(qubits)) + else: + num_clean_available = 0 + num_dirty_available = 0 synthesized = self._synthesize_op_using_plugins( hls_methods, operation, indices, - tracker.num_clean(qubits), - tracker.num_dirty(qubits), + num_clean_available, + num_dirty_available, ) + # It may happen that the plugin synthesis method uses clean/dirty ancilla qubits + if (synthesized is not None) and (synthesized.num_qubits > len(qubits)): + # need to borrow more qubits from tracker + global_aux_qubits = tracker.borrow( + synthesized.num_qubits - len(qubits), context.to_globals(qubits) + ) + global_to_local = context.to_local_mapping() + + for aq in global_aux_qubits: + if aq in global_to_local: + qubits.append(global_to_local[aq]) + else: + new_local_qubit = context.add_qubit(aq) + qubits.append(new_local_qubit) + # If HLS did not apply, or was unsuccessful, try unrolling custom definitions. if synthesized is None and not self._top_level_only: - synthesized = self._unroll_custom_definition(operation, indices) + synthesized = self._get_custom_definition(operation, indices) if synthesized is None: - # if we didn't synthesize, there was nothing to unroll, so just set the used qubits - used_qubits = qubits + # if we didn't synthesize, there was nothing to unroll + # updating the tracker will be handled upstream + pass + + # if it has been synthesized, recurse and finally store the decomposition + elif isinstance(synthesized, Operation): + resynthesized, resynthesized_context = self._synthesize_operation( + synthesized, qubits, tracker, context, use_ancillas=use_ancillas + ) - else: - # if it has been synthesized, recurse and finally store the decomposition - if isinstance(synthesized, Operation): - re_synthesized, qubits = self._synthesize_operation( - synthesized, qubits, tracker.copy() + if resynthesized is not None: + synthesized = resynthesized + else: + tracker.set_dirty(context.to_globals(qubits)) + if isinstance(resynthesized, DAGCircuit): + synthesized_context = resynthesized_context + + elif isinstance(synthesized, QuantumCircuit): + # Synthesized is a quantum circuit which we want to process recursively. + # For example, it's the definition circuit of a custom gate + # or a circuit obtained by calling a synthesis method on a high-level-object. + # In the second case, synthesized may have more qubits than the original node. + + as_dag = circuit_to_dag(synthesized, copy_operations=False) + inner_context = context.restrict(qubits) + + if as_dag.num_qubits() != inner_context.num_qubits(): + raise TranspilerError("HighLevelSynthesis internal error.") + + # We save the current state of the tracker to be able to return the ancilla + # qubits to the current positions. Note that at this point we do not know + # which ancilla qubits will be allocated. + saved_tracker = tracker.copy() + synthesized = self._run( + as_dag, tracker, inner_context, use_ancillas=use_ancillas, top_level=False + ) + synthesized_context = inner_context + + if (synthesized is not None) and (synthesized.num_qubits() > len(qubits)): + # need to borrow more qubits from tracker + global_aux_qubits = tracker.borrow( + synthesized.num_qubits() - len(qubits), context.to_globals(qubits) + ) + global_to_local = context.to_local_mapping() + + for aq in global_aux_qubits: + if aq in global_to_local: + qubits.append(global_to_local[aq]) + else: + new_local_qubit = context.add_qubit(aq) + qubits.append(new_local_qubit) + + if len(qubits) > num_original_qubits: + tracker.replace_state( + saved_tracker, context.to_globals(qubits[num_original_qubits:]) ) - if re_synthesized is not None: - synthesized = re_synthesized - used_qubits = qubits - - elif isinstance(synthesized, QuantumCircuit): - aux_qubits = tracker.borrow(synthesized.num_qubits - len(qubits), qubits) - used_qubits = qubits + tuple(aux_qubits) - as_dag = circuit_to_dag(synthesized, copy_operations=False) - - # map used qubits to subcircuit - new_qubits = [as_dag.find_bit(q).index for q in as_dag.qubits] - qubit_map = dict(zip(used_qubits, new_qubits)) - - synthesized = self._run(as_dag, tracker.copy(qubit_map)) - if synthesized.num_qubits() != len(used_qubits): - raise RuntimeError( - f"Mismatching number of qubits, using {synthesized.num_qubits()} " - f"but have {len(used_qubits)}." - ) - else: - raise RuntimeError(f"Unexpected synthesized type: {type(synthesized)}") + else: + raise TranspilerError(f"Unexpected synthesized type: {type(synthesized)}") - if synthesized is not None and used_qubits is None: - raise RuntimeError("Failed to find qubit indices on", synthesized) + if isinstance(synthesized, DAGCircuit) and synthesized_context is None: + raise TranspilerError("HighLevelSynthesis internal error.") - return synthesized, used_qubits + return synthesized, synthesized_context - def _unroll_custom_definition( + def _get_custom_definition( self, inst: Instruction, qubits: list[int] | None ) -> QuantumCircuit | None: # check if the operation is already supported natively diff --git a/qiskit/transpiler/passes/synthesis/qubit_tracker.py b/qiskit/transpiler/passes/synthesis/qubit_tracker.py index f3dd34b7df31..162d28bf8e9e 100644 --- a/qiskit/transpiler/passes/synthesis/qubit_tracker.py +++ b/qiskit/transpiler/passes/synthesis/qubit_tracker.py @@ -25,108 +25,98 @@ class QubitTracker: unknown state). """ - # This could in future be extended to track different state types, if necessary. - # However, using sets of integers here is much faster than e.g. storing a dictionary with - # {index: state} entries. - qubits: tuple[int] - clean: set[int] - dirty: set[int] - - def num_clean(self, active_qubits: Iterable[int] | None = None): - """Return the number of clean qubits, not considering the active qubits.""" - # this could be cached if getting the set length becomes a performance bottleneck - return len(self.clean.difference(active_qubits or set())) - - def num_dirty(self, active_qubits: Iterable[int] | None = None): - """Return the number of dirty qubits, not considering the active qubits.""" - return len(self.dirty.difference(active_qubits or set())) - - def borrow(self, num_qubits: int, active_qubits: Iterable[int] | None = None) -> list[int]: - """Get ``num_qubits`` qubits, excluding ``active_qubits``.""" - active_qubits = set(active_qubits or []) - available_qubits = [qubit for qubit in self.qubits if qubit not in active_qubits] - - if num_qubits > (available := len(available_qubits)): - raise RuntimeError(f"Cannot borrow {num_qubits} qubits, only {available} available.") - - # for now, prioritize returning clean qubits - available_clean = [qubit for qubit in available_qubits if qubit in self.clean] - available_dirty = [qubit for qubit in available_qubits if qubit in self.dirty] - - borrowed = available_clean[:num_qubits] - return borrowed + available_dirty[: (num_qubits - len(borrowed))] - - def used(self, qubits: Iterable[int], check: bool = True) -> None: - """Set the state of ``qubits`` to used (i.e. False).""" - qubits = set(qubits) - - if check: - if len(untracked := qubits.difference(self.qubits)) > 0: - raise ValueError(f"Setting state of untracked qubits: {untracked}. Tracker: {self}") - - self.clean -= qubits - self.dirty |= qubits - - def reset(self, qubits: Iterable[int], check: bool = True) -> None: - """Set the state of ``qubits`` to 0 (i.e. True).""" - qubits = set(qubits) - - if check: - if len(untracked := qubits.difference(self.qubits)) > 0: - raise ValueError(f"Setting state of untracked qubits: {untracked}. Tracker: {self}") - - self.clean |= qubits - self.dirty -= qubits - - def drop(self, qubits: Iterable[int], check: bool = True) -> None: - """Drop qubits from the tracker, meaning that they are no longer available.""" - qubits = set(qubits) - - if check: - if len(untracked := qubits.difference(self.qubits)) > 0: - raise ValueError(f"Dropping untracked qubits: {untracked}. Tracker: {self}") - - self.qubits = tuple(qubit for qubit in self.qubits if qubit not in qubits) - self.clean -= qubits - self.dirty -= qubits - - def copy( - self, qubit_map: dict[int, int] | None = None, drop: Iterable[int] | None = None - ) -> "QubitTracker": - """Copy self. - - Args: - qubit_map: If provided, apply the mapping ``{old_qubit: new_qubit}`` to - the qubits in the tracker. Only those old qubits in the mapping will be - part of the new one. - drop: If provided, drop these qubits in the copied tracker. This argument is ignored - if ``qubit_map`` is given, since the qubits can then just be dropped in the map. - """ - if qubit_map is None and drop is not None: - remaining_qubits = [qubit for qubit in self.qubits if qubit not in drop] - qubit_map = dict(zip(remaining_qubits, remaining_qubits)) - - if qubit_map is None: - clean = self.clean.copy() - dirty = self.dirty.copy() - qubits = self.qubits # tuple is immutable, no need to copy - else: - clean, dirty = set(), set() - for old_index, new_index in qubit_map.items(): - if old_index in self.clean: - clean.add(new_index) - elif old_index in self.dirty: - dirty.add(new_index) - else: - raise ValueError(f"Unknown old qubit index: {old_index}. Tracker: {self}") - - qubits = tuple(qubit_map.values()) - - return QubitTracker(qubits, clean=clean, dirty=dirty) + def __init__(self, num_qubits: int): + self.num_qubits = num_qubits + self.state = [False] * num_qubits # True: clean, False: dirty + self.enabled = [True] * num_qubits # True: allowed to use, False: not allowed to use + self.ignored = [False] * num_qubits # Internal scratch space + + def set_dirty(self, qubits): + """Sets state of the given qubits to dirty.""" + for q in qubits: + self.state[q] = False + + def set_clean(self, qubits): + """Sets state of the given qubits to clean.""" + for q in qubits: + self.state[q] = True + + def disable(self, qubits): + """Disables using the given qubits.""" + for q in qubits: + self.enabled[q] = False + + def enable(self, qubits): + """Enables using the given qubits.""" + for q in qubits: + self.enabled[q] = True + + def num_clean(self, ignored_qubits): + """Returns the number of enabled clean qubits, ignoring the given qubits.""" + count = 0 + for q in ignored_qubits: + self.ignored[q] = True + for q in range(self.num_qubits): + if (not self.ignored[q]) and self.enabled[q] and self.state[q]: + count += 1 + for q in ignored_qubits: + self.ignored[q] = False + return count + + def num_dirty(self, ignored_qubits): + """Returns the number of enabled dirty qubits, ignoring the given qubits.""" + count = 0 + for q in ignored_qubits: + self.ignored[q] = True + for q in range(self.num_qubits): + if (not self.ignored[q]) and self.enabled[q] and not self.state[q]: + count += 1 + for q in ignored_qubits: + self.ignored[q] = False + return count + + def borrow(self, num_qubits: int, ignored_qubits: Iterable[int] | None = None) -> list[int]: + """Get ``num_qubits`` enabled qubits, excluding ``ignored_qubits`` and prioritizing + clean qubits.""" + res = [] + for q in ignored_qubits: + self.ignored[q] = True + for q in range(self.num_qubits): + if (not self.ignored[q]) and self.enabled[q] and self.state[q]: + res.append(q) + for q in range(self.num_qubits): + if (not self.ignored[q]) and self.enabled[q] and not self.state[q]: + res.append(q) + for q in ignored_qubits: + self.ignored[q] = False + return res[:num_qubits] + + def copy(self) -> "QubitTracker": + """Copies the qubit tracker.""" + tracker = QubitTracker(self.num_qubits) + tracker.state = self.state.copy() + tracker.enabled = self.enabled.copy() + # no need to copy the scratch space (ignored) + return tracker + + def replace_state(self, other: "QubitTracker", qubits): + """Replaces the state of the given qubits by their state in the ``other`` tracker.""" + for q in qubits: + self.state[q] = other.state[q] def __str__(self) -> str: - return ( - f"QubitTracker({len(self.qubits)}, clean: {self.num_clean()}, dirty: {self.num_dirty()})" - + f"\n\tclean: {self.clean}" - + f"\n\tdirty: {self.dirty}" - ) + """Pretty-prints qubit states.""" + out = "QubitTracker(" + for q in range(self.num_qubits): + out += str(q) + ": " + if not self.enabled[q]: + out += "_" + elif self.state[q]: + out += "0" + else: + out += "*" + if q != self.num_qubits - 1: + out += "; " + else: + out += ")" + return out diff --git a/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py b/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py index a73f9690ae0e..52f56aebbace 100644 --- a/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py +++ b/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py @@ -22,6 +22,7 @@ ) from qiskit.dagcircuit import DAGCircuit from qiskit.transpiler.basepasses import TransformationPass +from qiskit.utils import deprecate_func class ConvertConditionsToIfOps(TransformationPass): @@ -31,6 +32,10 @@ class ConvertConditionsToIfOps(TransformationPass): This is a simple pass aimed at easing the conversion from the old style of using :meth:`.InstructionSet.c_if` into the new style of using more complex conditional logic.""" + @deprecate_func(since="1.3.0", removal_timeline="in Qiskit 2.0.0") + def __init__(self): + super().__init__() + def _run_inner(self, dag): """Run the pass on one :class:`.DAGCircuit`, mutating it. Returns ``True`` if the circuit was modified and ``False`` if not.""" diff --git a/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py b/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py index 779a3512faab..04516f4b0c6a 100644 --- a/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +++ b/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py @@ -29,6 +29,7 @@ from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.target import Target, target_to_backend_properties from qiskit.transpiler.timing_constraints import TimingConstraints +from qiskit.utils import deprecate_arg from qiskit.utils.deprecate_pulse import deprecate_pulse_arg from .level0 import level_0_pass_manager @@ -37,6 +38,32 @@ from .level3 import level_3_pass_manager +@deprecate_arg( + name="instruction_durations", + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `target` parameter should be used instead. You can build a `Target` instance " + "with defined instruction durations with " + "`Target.from_configuration(..., instruction_durations=...)`", +) +@deprecate_arg( + name="timing_constraints", + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `target` parameter should be used instead. You can build a `Target` instance " + "with defined timing constraints with " + "`Target.from_configuration(..., timing_constraints=...)`", +) +@deprecate_arg( + name="backend_properties", + since="1.3", + package_name="Qiskit", + removal_timeline="in Qiskit 2.0", + additional_msg="The `target` parameter should be used instead. You can build a `Target` instance " + "with defined properties with Target.from_configuration(..., backend_properties=...)", +) @deprecate_pulse_arg("inst_map", predicate=lambda inst_map: inst_map is not None) def generate_preset_pass_manager( optimization_level=2, diff --git a/qiskit/visualization/circuit/_utils.py b/qiskit/visualization/circuit/_utils.py index d933d38b5c4a..0aa6e7c10188 100644 --- a/qiskit/visualization/circuit/_utils.py +++ b/qiskit/visualization/circuit/_utils.py @@ -503,7 +503,7 @@ def _get_gate_span(qubits, node): # type of op must be the only op in the layer if isinstance(node.op, ControlFlowOp): span = qubits - elif node.cargs or getattr(node.op, "condition", None): + elif node.cargs or getattr(node, "condition", None): span = qubits[min_index : len(qubits)] else: span = qubits[min_index : max_index + 1] @@ -582,7 +582,7 @@ def slide_from_left(self, node, index): curr_index = index last_insertable_index = -1 index_stop = -1 - if (condition := getattr(node.op, "condition", None)) is not None: + if (condition := getattr(node, "condition", None)) is not None: index_stop = max( (self.measure_map[bit] for bit in condition_resources(condition).clbits), default=index_stop, diff --git a/releasenotes/notes/Parameterized-commutation-checker-8a78a4715bf78b4e.yaml b/releasenotes/notes/Parameterized-commutation-checker-8a78a4715bf78b4e.yaml new file mode 100644 index 000000000000..72bd9aefe726 --- /dev/null +++ b/releasenotes/notes/Parameterized-commutation-checker-8a78a4715bf78b4e.yaml @@ -0,0 +1,14 @@ +--- +features_circuits: + - | + Improved the functionality of :class:`.CommutationChecker` to include + support for the following parameterized gates with free parameters: + :class:`.RXXGate`,:class:`.RYYGate`,:class:`.RZZGate`,:class:`.RZXGate`, + :class:`.RXGate`,:class:`.RYGate`,:class:`.RZGate`,:class:`.PhaseGate`, + :class:`.U1Gate`,:class:`.CRXGate`,:class:`.CRYGate`,:class:`.CRZGate`, + :class:`.CPhaseGate`. + + Before these were only supported with bound parameters. + + + diff --git a/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml b/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml new file mode 100644 index 000000000000..d1de403ab4f7 --- /dev/null +++ b/releasenotes/notes/boolean-logic-gates-40add5cf0b20b5e9.yaml @@ -0,0 +1,14 @@ +--- +features_circuits: + - | + Quantum circuits in :mod:`qiskit.circuit.library.boolean_logic` now have equivalent + representations as :class:`.Gate` objects: + + * :class:`~qiskit.circuit.library.AndGate`, + representing :class:`~qiskit.circuit.library.AND`, + * :class:`~qiskit.circuit.library.OrGate`, + representing :class:`~qiskit.circuit.library.OR`, + * :class:`~qiskit.circuit.library.BitwiseXorGate`, + representing :class:`~qiskit.circuit.library.XOR`, + * :class:`~qiskit.circuit.library.InnerProductGate`, + representing :class:`~qiskit.circuit.library.InnerProduct`. diff --git a/releasenotes/notes/deprecate-basic-simulator-configuration-9d782925196993e9.yaml b/releasenotes/notes/deprecate-basic-simulator-configuration-9d782925196993e9.yaml new file mode 100644 index 000000000000..59b00df0cf5a --- /dev/null +++ b/releasenotes/notes/deprecate-basic-simulator-configuration-9d782925196993e9.yaml @@ -0,0 +1,32 @@ +--- +deprecations_providers: + - | + The :meth:`.BasicSimulator.configuration` method is deprecated and will be removed in 2.0.0. + This method returned a legacy ``providers.models.BackendConfiguration`` instance which is part + of the deprecated ``BackendV1`` model. This model has been replaced with :class:`.BackendV2`, + where the constraints are stored directly in the backend instance or the underlying :class:`.Target` + (``backend.target``). + + Here is a quick guide for accessing the most common ``BackendConfiguration`` attributes in the + :class:`BackendV2` model:"" + + BackendV1 model (deprecated) ------------> BackendV2 model + ---------------------------- --------------- + backend.configuration().backend_name backend.name + backend.configuration().backend_version backend.backend_version + backend.configuration().n_qubits backend.num_qubits + backend.configuration().num_qubits backend.num_qubits + backend.configuration().basis_gates backend.target.operation_names (*) + backend.configuration().coupling_map backend.target.build_coupling_map() + backend.configuration().local No representation + backend.configuration().simulator No representation + backend.configuration().conditional No representation + backend.configuration().open_pulse No representation + backend.configuration().memory No representation + backend.configuration().max_shots No representation + + (*) Note that ``backend.target.operation_names`` includes ``basis_gates`` and additional + non-gate instructions, in some implementations it might be necessary to filter the output. + + See `this guide `__ + for more information on migrating to the ``BackendV2`` model. diff --git a/releasenotes/notes/deprecate-condition_c_if-9548e5522814fe9c.yaml b/releasenotes/notes/deprecate-condition_c_if-9548e5522814fe9c.yaml new file mode 100644 index 000000000000..e096273f3c8e --- /dev/null +++ b/releasenotes/notes/deprecate-condition_c_if-9548e5522814fe9c.yaml @@ -0,0 +1,38 @@ +--- +deprecations_circuits: + - | + Deprecated the :attr:`.Instruction.condition` attribute and the + :meth:`.Instruction.c_if` method. They will be removed + in Qiskit 2.0, along with any uses in the Qiskit data + model. This functionality has been superseded by the :class:`.IfElseOp` class + which can be used to describe a classical condition in a circuit. For + example, a circuit using :meth:`.Instruction.c_if` like:: + + from qiskit.circuit import QuantumCircuit + + qc = QuantumCircuit(2, 2) + qc.h(0) + qc.x(0).c_if(0, 1) + qc.z(1.c_if(1, 0) + qc.measure(0, 0) + qc.measure(1, 1) + + can be rewritten as:: + + qc = QuantumCircuit(2, 2) + qc.h(0) + with expected.if_test((expected.clbits[0], True)): + qc.x(0) + with expected.if_test((expected.clbits[1], False)): + qc.z(1) + qc.measure(0, 0) + qc.measure(1, 1) + + The now deprecated :class:`.ConvertConditionsToIfOps` transpiler pass can + be used to automate this conversion for existing circuits. +deprecations_transpiler: + - | + The transpiler pass :class:`.ConvertConditionsToIfOps` has been deprecated + and will be removed in Qiskit 2.0.0. This class is now deprecated because + the underlying data model for :attr:`.Instruction.condition` which this + pass is converting from has been deprecated and will be removed in 2.0.0. diff --git a/releasenotes/notes/deprecate-mitigation-f5f6ef3233b3d726.yaml b/releasenotes/notes/deprecate-mitigation-f5f6ef3233b3d726.yaml new file mode 100644 index 000000000000..850b84f1fb17 --- /dev/null +++ b/releasenotes/notes/deprecate-mitigation-f5f6ef3233b3d726.yaml @@ -0,0 +1,7 @@ +--- +deprecations_misc: + - | + The ``qiskit.result.mitigation`` module has been deprecated and will be removed in the 2.0 release. + The deprecation includes the ``LocalReadoutMitigator`` and ``CorrelatedReadoutMitigator`` classes + as well as the associated utils. + Their functionality has been superseded by the mthree package, found in https://github.com/Qiskit/qiskit-addon-mthree. diff --git a/releasenotes/notes/faster-pauli-decomposition-faf2be01a6e75fff.yaml b/releasenotes/notes/faster-pauli-decomposition-faf2be01a6e75fff.yaml new file mode 100644 index 000000000000..56ad1a725f9a --- /dev/null +++ b/releasenotes/notes/faster-pauli-decomposition-faf2be01a6e75fff.yaml @@ -0,0 +1,7 @@ +--- +features_quantum_info: + - | + The performance of :meth:`.SparsePauliOp.from_operator` has been optimized on top of the + algorithm improvements methods introduced in Qiskit 1.0. It is now approximately five times + faster than before for fully dense matrices, taking approximately 40ms to decompose a 10q + operator involving all Pauli terms. diff --git a/releasenotes/notes/fixes_13306-f9883a733491a72f.yaml b/releasenotes/notes/fixes_13306-f9883a733491a72f.yaml new file mode 100644 index 000000000000..e46b971b6462 --- /dev/null +++ b/releasenotes/notes/fixes_13306-f9883a733491a72f.yaml @@ -0,0 +1,14 @@ +--- +deprecations_transpiler: + - | + The following :func:`.transpile` and :func:`.generate_preset_pass_manager` arguments are deprecated in favor of + defining a custom :class:`.Target`: ``instruction_durations``, ``timing_constraints``, and ``backend_properties``. + These arguments can be used to build a target with :meth:`.Target.from_configuration`:: + + Target.from_configuration( + ... + backend_properties = backend_properties, + instruction_durations = instruction_durations, + timing_constraints = timing_constraints + ) + diff --git a/releasenotes/notes/improve-hls-qubit-tracking-6b6288d556e3af9d.yaml b/releasenotes/notes/improve-hls-qubit-tracking-6b6288d556e3af9d.yaml new file mode 100644 index 000000000000..f832fed1b72d --- /dev/null +++ b/releasenotes/notes/improve-hls-qubit-tracking-6b6288d556e3af9d.yaml @@ -0,0 +1,8 @@ +--- +features_transpiler: + - | + Improved handling of ancilla qubits in the :class:`.HighLevelSynthesis` + transpiler pass. For example, a circuit may have custom gates whose + definitions include :class:`.MCXGate`\s. Now the synthesis algorithms + for the inner MCX-gates can use the ancilla qubits available on the + global circuit but outside the custom gates' definitions. diff --git a/releasenotes/notes/iqp-function-6594f7cf1521499c.yaml b/releasenotes/notes/iqp-function-6594f7cf1521499c.yaml new file mode 100644 index 000000000000..a33b70c32a33 --- /dev/null +++ b/releasenotes/notes/iqp-function-6594f7cf1521499c.yaml @@ -0,0 +1,12 @@ +--- +features_circuits: + - | + Added the :func:`~qiskit.circuit.library.iqp` function to construct + Instantaneous Quantum Polynomial time (IQP) circuits. In addition to the + existing :class:`.IQP` class, the function also allows construction of random + IQP circuits:: + + from qiskit.circuit.library import iqp + + random_iqp = iqp(num_qubits=4) + print(random_iqp.draw()) \ No newline at end of file diff --git a/releasenotes/notes/pauli-label-perf-b704cbcc5ef92794.yaml b/releasenotes/notes/pauli-label-perf-b704cbcc5ef92794.yaml new file mode 100644 index 000000000000..22035371edc2 --- /dev/null +++ b/releasenotes/notes/pauli-label-perf-b704cbcc5ef92794.yaml @@ -0,0 +1,4 @@ +--- +features_quantum_info: + - | + The performance of :meth:`.Pauli.to_label` has significantly improved for large Paulis. diff --git a/requirements.txt b/requirements.txt index 4c13eb6dc60a..6eb5902ae9fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,7 @@ dill>=0.3 python-dateutil>=2.8.0 stevedore>=3.0.0 typing-extensions + +# If updating the version range here, consider updating 'test/qpy_compat/run_tests.sh' to update the +# list of symengine dependencies used in the cross-version tests. symengine>=0.11,<0.14 diff --git a/test/python/circuit/classical/test_expr_constructors.py b/test/python/circuit/classical/test_expr_constructors.py index 012697a17dd0..6500e5593ac2 100644 --- a/test/python/circuit/classical/test_expr_constructors.py +++ b/test/python/circuit/classical/test_expr_constructors.py @@ -26,46 +26,54 @@ def test_lift_legacy_condition(self): clbit = Clbit() inst = Instruction("custom", 1, 0, []) - inst.c_if(cr, 7) - self.assertEqual( - expr.lift_legacy_condition(inst.condition), - expr.Binary( - expr.Binary.Op.EQUAL, - expr.Var(cr, types.Uint(cr.size)), - expr.Value(7, types.Uint(cr.size)), - types.Bool(), - ), - ) + with self.assertWarns(DeprecationWarning): + inst.c_if(cr, 7) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + expr.lift_legacy_condition(inst.condition), + expr.Binary( + expr.Binary.Op.EQUAL, + expr.Var(cr, types.Uint(cr.size)), + expr.Value(7, types.Uint(cr.size)), + types.Bool(), + ), + ) inst = Instruction("custom", 1, 0, []) - inst.c_if(cr, 255) - self.assertEqual( - expr.lift_legacy_condition(inst.condition), - expr.Binary( - expr.Binary.Op.EQUAL, - expr.Cast(expr.Var(cr, types.Uint(cr.size)), types.Uint(8), implicit=True), - expr.Value(255, types.Uint(8)), - types.Bool(), - ), - ) + with self.assertWarns(DeprecationWarning): + inst.c_if(cr, 255) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + expr.lift_legacy_condition(inst.condition), + expr.Binary( + expr.Binary.Op.EQUAL, + expr.Cast(expr.Var(cr, types.Uint(cr.size)), types.Uint(8), implicit=True), + expr.Value(255, types.Uint(8)), + types.Bool(), + ), + ) inst = Instruction("custom", 1, 0, []) - inst.c_if(clbit, False) - self.assertEqual( - expr.lift_legacy_condition(inst.condition), - expr.Unary( - expr.Unary.Op.LOGIC_NOT, - expr.Var(clbit, types.Bool()), - types.Bool(), - ), - ) + with self.assertWarns(DeprecationWarning): + inst.c_if(clbit, False) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + expr.lift_legacy_condition(inst.condition), + expr.Unary( + expr.Unary.Op.LOGIC_NOT, + expr.Var(clbit, types.Bool()), + types.Bool(), + ), + ) inst = Instruction("custom", 1, 0, []) - inst.c_if(clbit, True) - self.assertEqual( - expr.lift_legacy_condition(inst.condition), - expr.Var(clbit, types.Bool()), - ) + with self.assertWarns(DeprecationWarning): + inst.c_if(clbit, True) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + expr.lift_legacy_condition(inst.condition), + expr.Var(clbit, types.Bool()), + ) def test_value_lifts_qiskit_scalars(self): cr = ClassicalRegister(3, "c") diff --git a/test/python/circuit/library/test_boolean_logic.py b/test/python/circuit/library/test_boolean_logic.py index cdadc9142a97..265f494f4c0c 100644 --- a/test/python/circuit/library/test_boolean_logic.py +++ b/test/python/circuit/library/test_boolean_logic.py @@ -16,9 +16,18 @@ from ddt import ddt, data, unpack import numpy as np -from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library import XOR, InnerProduct, AND, OR -from qiskit.quantum_info import Statevector +from qiskit.circuit import QuantumCircuit, Gate +from qiskit.circuit.library import ( + XOR, + InnerProduct, + AND, + OR, + AndGate, + OrGate, + BitwiseXorGate, + InnerProductGate, +) +from qiskit.quantum_info import Statevector, Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -26,11 +35,15 @@ class TestBooleanLogicLibrary(QiskitTestCase): """Test library of boolean logic quantum circuits.""" - def assertBooleanFunctionIsCorrect(self, boolean_circuit, reference): - """Assert that ``boolean_circuit`` implements the reference boolean function correctly.""" - circuit = QuantumCircuit(boolean_circuit.num_qubits) - circuit.h(list(range(boolean_circuit.num_variable_qubits))) - circuit.append(boolean_circuit.to_instruction(), list(range(boolean_circuit.num_qubits))) + def assertBooleanFunctionIsCorrect(self, boolean_object, reference): + """Assert that ``boolean_object`` implements the reference boolean function correctly.""" + circuit = QuantumCircuit(boolean_object.num_qubits) + circuit.h(list(range(boolean_object.num_variable_qubits))) + + if isinstance(boolean_object, Gate): + circuit.append(boolean_object, list(range(boolean_object.num_qubits))) + else: + circuit.append(boolean_object.to_instruction(), list(range(boolean_object.num_qubits))) # compute the statevector of the circuit statevector = Statevector.from_label("0" * circuit.num_qubits) @@ -38,18 +51,18 @@ def assertBooleanFunctionIsCorrect(self, boolean_circuit, reference): # trace out ancillas probabilities = statevector.probabilities( - qargs=list(range(boolean_circuit.num_variable_qubits + 1)) + qargs=list(range(boolean_object.num_variable_qubits + 1)) ) # compute the expected outcome by computing the entries of the statevector that should # have a 1 / sqrt(2**n) factor expectations = np.zeros_like(probabilities) - for x in range(2**boolean_circuit.num_variable_qubits): - bits = np.array(list(bin(x)[2:].zfill(boolean_circuit.num_variable_qubits)), dtype=int) + for x in range(2**boolean_object.num_variable_qubits): + bits = np.array(list(bin(x)[2:].zfill(boolean_object.num_variable_qubits)), dtype=int) result = reference(bits[::-1]) - entry = int(str(int(result)) + bin(x)[2:].zfill(boolean_circuit.num_variable_qubits), 2) - expectations[entry] = 1 / 2**boolean_circuit.num_variable_qubits + entry = int(str(int(result)) + bin(x)[2:].zfill(boolean_object.num_variable_qubits), 2) + expectations[entry] = 1 / 2**boolean_object.num_variable_qubits np.testing.assert_array_almost_equal(probabilities, expectations) @@ -63,6 +76,39 @@ def test_xor(self): expected.x(2) self.assertEqual(circuit.decompose(), expected) + def test_xor_gate(self): + """Test XOR-gate.""" + xor_gate = BitwiseXorGate(num_qubits=3, amount=4) + expected = QuantumCircuit(3) + expected.x(2) + self.assertEqual(Operator(xor_gate), Operator(expected)) + + @data( + (5, 12), + (6, 21), + ) + @unpack + def test_xor_equivalence(self, num_qubits, amount): + """Test that XOR-circuit and BitwiseXorGate yield equal operators.""" + xor_gate = BitwiseXorGate(num_qubits, amount) + xor_circuit = XOR(num_qubits, amount) + self.assertEqual(Operator(xor_gate), Operator(xor_circuit)) + + def test_xor_eq(self): + """Test BitwiseXorGate's equality method.""" + xor1 = BitwiseXorGate(num_qubits=5, amount=10) + xor2 = BitwiseXorGate(num_qubits=5, amount=10) + xor3 = BitwiseXorGate(num_qubits=5, amount=11) + self.assertEqual(xor1, xor2) + self.assertNotEqual(xor1, xor3) + + def test_xor_inverse(self): + """Test correctness of the BitwiseXorGate's inverse.""" + xor_gate = BitwiseXorGate(num_qubits=5, amount=10) + xor_gate_inverse = xor_gate.inverse() + self.assertEqual(xor_gate, xor_gate_inverse) + self.assertEqual(Operator(xor_gate), Operator(xor_gate_inverse).adjoint()) + def test_inner_product(self): """Test inner product circuit. @@ -75,6 +121,22 @@ def test_inner_product(self): expected.cz(2, 5) self.assertEqual(circuit.decompose(), expected) + def test_inner_product_gate(self): + """Test inner product gate.""" + inner_product = InnerProductGate(num_qubits=3) + expected = QuantumCircuit(6) + expected.cz(0, 3) + expected.cz(1, 4) + expected.cz(2, 5) + self.assertEqual(Operator(inner_product), Operator(expected)) + + @data(4, 5, 6) + def test_inner_product_equivalence(self, num_qubits): + """Test that XOR-circuit and BitwiseXorGate yield equal operators.""" + inner_product_gate = InnerProductGate(num_qubits) + inner_product_circuit = InnerProduct(num_qubits) + self.assertEqual(Operator(inner_product_gate), Operator(inner_product_circuit)) + @data( (2, None, "noancilla"), (5, None, "noancilla"), @@ -100,6 +162,57 @@ def reference(bits): self.assertBooleanFunctionIsCorrect(or_circuit, reference) + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_or_gate(self, num_variables, flags): + """Test correctness of the OrGate.""" + or_gate = OrGate(num_variables, flags) + flags = flags or [1] * num_variables + + def reference(bits): + flagged = [] + for flag, bit in zip(flags, bits): + if flag < 0: + flagged += [1 - bit] + elif flag > 0: + flagged += [bit] + return np.any(flagged) + + self.assertBooleanFunctionIsCorrect(or_gate, reference) + + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_or_gate_inverse(self, num_variables, flags): + """Test correctness of the OrGate's inverse.""" + or_gate = OrGate(num_variables, flags) + or_gate_inverse = or_gate.inverse() + self.assertEqual(Operator(or_gate), Operator(or_gate_inverse).adjoint()) + + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_or_equivalence(self, num_variables, flags): + """Test that OR-circuit and OrGate yield equal operators + (when not using ancilla qubits). + """ + or_gate = OrGate(num_variables, flags) + or_circuit = OR(num_variables, flags) + self.assertEqual(Operator(or_gate), Operator(or_circuit)) + @data( (2, None, "noancilla"), (2, [-1, 1], "v-chain"), @@ -108,7 +221,7 @@ def reference(bits): ) @unpack def test_and(self, num_variables, flags, mcx_mode): - """Test the and circuit.""" + """Test the AND-circuit.""" and_circuit = AND(num_variables, flags, mcx_mode=mcx_mode) flags = flags or [1] * num_variables @@ -123,6 +236,57 @@ def reference(bits): self.assertBooleanFunctionIsCorrect(and_circuit, reference) + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_and_gate(self, num_variables, flags): + """Test correctness of the AndGate.""" + and_gate = AndGate(num_variables, flags) + flags = flags or [1] * num_variables + + def reference(bits): + flagged = [] + for flag, bit in zip(flags, bits): + if flag < 0: + flagged += [1 - bit] + elif flag > 0: + flagged += [bit] + return np.all(flagged) + + self.assertBooleanFunctionIsCorrect(and_gate, reference) + + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_and_gate_inverse(self, num_variables, flags): + """Test correctness of the AND-gate inverse.""" + and_gate = AndGate(num_variables, flags) + and_gate_inverse = and_gate.inverse() + self.assertEqual(Operator(and_gate), Operator(and_gate_inverse).adjoint()) + + @data( + (2, None), + (2, [-1, 1]), + (5, [0, 0, -1, 1, -1]), + (5, [-1, 0, 0, 1, 1]), + ) + @unpack + def test_and_equivalence(self, num_variables, flags): + """Test that AND-circuit and AND-gate yield equal operators + (when not using ancilla qubits). + """ + and_gate = AndGate(num_variables, flags) + and_circuit = AND(num_variables, flags) + self.assertEqual(Operator(and_gate), Operator(and_circuit)) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/library/test_iqp.py b/test/python/circuit/library/test_iqp.py index b371e5546525..ae98b2500120 100644 --- a/test/python/circuit/library/test_iqp.py +++ b/test/python/circuit/library/test_iqp.py @@ -13,29 +13,40 @@ """Test library of IQP circuits.""" import unittest +from ddt import ddt, data import numpy as np from qiskit.circuit import QuantumCircuit from qiskit.circuit.exceptions import CircuitError -from qiskit.circuit.library import IQP +from qiskit.circuit.library import IQP, iqp, random_iqp from qiskit.quantum_info import Operator from test import QiskitTestCase # pylint: disable=wrong-import-order +@ddt class TestIQPLibrary(QiskitTestCase): """Test library of IQP quantum circuits.""" - def test_iqp(self): - """Test iqp circuit.""" - circuit = IQP(interactions=np.array([[6, 5, 1], [5, 4, 3], [1, 3, 2]])) - - # ┌───┐ ┌─────────┐┌───┐ - # q_0: ┤ H ├─■───────────────────■───────┤ P(3π/4) ├┤ H ├ - # ├───┤ │P(5π/2) │ └┬────────┤├───┤ - # q_1: ┤ H ├─■─────────■─────────┼────────┤ P(π/2) ├┤ H ├ - # ├───┤ │P(3π/2) │P(π/2) ├────────┤├───┤ - # q_2: ┤ H ├───────────■─────────■────────┤ P(π/4) ├┤ H ├ - # └───┘ └────────┘└───┘ + @data(True, False) + def test_iqp(self, use_function): + """Test iqp circuit. + + ┌───┐ ┌─────────┐┌───┐ + q_0: ┤ H ├─■───────────────────■───────┤ P(3π/4) ├┤ H ├ + ├───┤ │P(5π/2) │ └┬────────┤├───┤ + q_1: ┤ H ├─■─────────■─────────┼────────┤ P(π/2) ├┤ H ├ + ├───┤ │P(3π/2) │P(π/2) ├────────┤├───┤ + q_2: ┤ H ├───────────■─────────■────────┤ P(π/4) ├┤ H ├ + └───┘ └────────┘└───┘ + """ + + interactions = np.array([[6, 5, 1], [5, 4, 3], [1, 3, 2]]) + + if use_function: + circuit = iqp(interactions) + else: + circuit = IQP(interactions) + expected = QuantumCircuit(3) expected.h([0, 1, 2]) expected.cp(5 * np.pi / 2, 0, 1) @@ -49,9 +60,34 @@ def test_iqp(self): simulated = Operator(circuit) self.assertTrue(expected.equiv(simulated)) - def test_iqp_bad(self): - """Test that [0,..,n-1] permutation is required (no -1 for last element).""" - self.assertRaises(CircuitError, IQP, [[6, 5], [2, 4]]) + @data(True, False) + def test_iqp_bad(self, use_function): + """Test an error is raised if the interactions matrix is not symmetric.""" + self.assertRaises(CircuitError, iqp if use_function else IQP, [[6, 5], [2, 4]]) + + def test_random_iqp(self): + """Test generating a random IQP circuit.""" + + circuit = random_iqp(num_qubits=4, seed=426) + self.assertEqual(circuit.num_qubits, 4) + + ops = circuit.count_ops() + allowed = {"p", "h", "cp"} + + # we pick a seed where neither the diagonal, nor the off-diagonal is completely 0, + # therefore each gate is expected to be present + self.assertEqual(set(ops.keys()), allowed) + + def test_random_iqp_seed(self): + """Test setting the seed.""" + + seed = 236321 + circuit1 = random_iqp(num_qubits=3, seed=seed) + circuit2 = random_iqp(num_qubits=3, seed=seed) + self.assertEqual(circuit1, circuit2) + + circuit3 = random_iqp(num_qubits=3, seed=seed + 1) + self.assertNotEqual(circuit1, circuit3) if __name__ == "__main__": diff --git a/test/python/circuit/library/test_mcmt.py b/test/python/circuit/library/test_mcmt.py index ead6a07d8b4d..73befb19db46 100644 --- a/test/python/circuit/library/test_mcmt.py +++ b/test/python/circuit/library/test_mcmt.py @@ -180,7 +180,9 @@ def test_default_plugin(self): gate = XGate() mcmt = MCMTGate(gate, num_controls, num_target) - hls = HighLevelSynthesis() + # make sure MCX-synthesis does not use ancilla qubits + config = HLSConfig(mcx=["noaux_v24"]) + hls = HighLevelSynthesis(hls_config=config) # test a decomposition without sufficient ancillas for MCMT V-chain with self.subTest(msg="insufficient auxiliaries"): diff --git a/test/python/circuit/library/test_permutation.py b/test/python/circuit/library/test_permutation.py index a2ec59431fe3..380a5f6a0358 100644 --- a/test/python/circuit/library/test_permutation.py +++ b/test/python/circuit/library/test_permutation.py @@ -149,8 +149,10 @@ def test_reverse_ops(self): def test_conditional(self): """Test adding conditional permutations.""" qc = QuantumCircuit(5, 1) - qc.append(PermutationGate([1, 2, 0]), [2, 3, 4]).c_if(0, 1) - self.assertIsNotNone(qc.data[0].operation.condition) + with self.assertWarns(DeprecationWarning): + qc.append(PermutationGate([1, 2, 0]), [2, 3, 4]).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + self.assertIsNotNone(qc.data[0].operation.condition) def test_qasm(self): """Test qasm for circuits with permutations.""" diff --git a/test/python/circuit/library/test_qft.py b/test/python/circuit/library/test_qft.py index 85837f0ac80c..6250b656a772 100644 --- a/test/python/circuit/library/test_qft.py +++ b/test/python/circuit/library/test_qft.py @@ -272,8 +272,10 @@ def test_reverse_ops(self): def test_conditional(self): """Test adding conditional to a QFTGate.""" qc = QuantumCircuit(5, 1) - qc.append(QFTGate(4), [1, 2, 0, 4]).c_if(0, 1) - self.assertIsNotNone(qc.data[0].operation.condition) + with self.assertWarns(DeprecationWarning): + qc.append(QFTGate(4), [1, 2, 0, 4]).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + self.assertIsNotNone(qc.data[0].operation.condition) def test_qasm(self): """Test qasm for circuits with QFTGates.""" diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 5e45d0671021..962dd22ac79b 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -138,11 +138,13 @@ def test_qpy_full_path(self): def test_circuit_with_conditional(self): """Test that instructions with conditions are correctly serialized.""" qc = QuantumCircuit(1, 1) - qc.x(0).c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(qc.cregs[0], 1) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circ = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertDeprecatedBitProperties(qc, new_circ) @@ -413,13 +415,15 @@ def test_multiple_circuits(self): """Test multiple circuits can be serialized together.""" circuits = [] for i in range(10): - circuits.append( - random_circuit(10, 10, measure=True, conditional=True, reset=True, seed=42 + i) - ) + with self.assertWarns(DeprecationWarning): + circuits.append( + random_circuit(10, 10, measure=True, conditional=True, reset=True, seed=42 + i) + ) qpy_file = io.BytesIO() dump(circuits, qpy_file) qpy_file.seek(0) - new_circs = load(qpy_file) + with self.assertWarns(DeprecationWarning): + new_circs = load(qpy_file) self.assertEqual(circuits, new_circs) for old, new in zip(circuits, new_circs): self.assertDeprecatedBitProperties(old, new) @@ -679,12 +683,14 @@ def test_circuit_with_conditional_with_label(self): """Test that instructions with conditions are correctly serialized.""" qc = QuantumCircuit(1, 1) gate = XGate(label="My conditional x gate") - gate.c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + gate.c_if(qc.cregs[0], 1) qc.append(gate, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circ = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual( [x.operation.label for x in qc.data], [x.operation.label for x in new_circ.data] @@ -760,12 +766,14 @@ def test_single_bit_teleportation(self): qc = QuantumCircuit(qr, cr, name="Reset Test") qc.x(0) qc.measure(0, cr[0]) - qc.x(0).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(cr[0], 1) qc.measure(0, cr[1]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circ = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual( [x.operation.label for x in qc.data], [x.operation.label for x in new_circ.data] @@ -1143,11 +1151,13 @@ def test_qpy_with_for_loop(self): qc.h(0) qc.cx(0, 1) qc.measure(0, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circuit = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circuit = load(qpy_file)[0] self.assertEqual(qc, new_circuit) self.assertDeprecatedBitProperties(qc, new_circuit) @@ -1159,11 +1169,13 @@ def test_qpy_with_for_loop_iterator(self): qc.h(0) qc.cx(0, 1) qc.measure(0, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circuit = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circuit = load(qpy_file)[0] self.assertEqual(qc, new_circuit) self.assertDeprecatedBitProperties(qc, new_circuit) diff --git a/test/python/circuit/test_circuit_operations.py b/test/python/circuit/test_circuit_operations.py index 2d1e136d4580..bd35c8d920a5 100644 --- a/test/python/circuit/test_circuit_operations.py +++ b/test/python/circuit/test_circuit_operations.py @@ -1038,18 +1038,21 @@ def test_repeat(self): qc.h(0) qc.cx(0, 1) qc.barrier() - qc.h(0).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(cr, 1) with self.subTest("repeat 0 times"): rep = qc.repeat(0) self.assertEqual(rep, QuantumCircuit(qr, cr)) with self.subTest("repeat 3 times"): - inst = qc.to_instruction() + with self.assertWarns(DeprecationWarning): + inst = qc.to_instruction() ref = QuantumCircuit(qr, cr) for _ in range(3): ref.append(inst, ref.qubits, ref.clbits) - rep = qc.repeat(3) + with self.assertWarns(DeprecationWarning): + rep = qc.repeat(3) self.assertEqual(rep, ref) @data(0, 1, 4) @@ -1295,13 +1298,15 @@ def test_reverse_bits_with_registerless_bits(self): qc = QuantumCircuit([q0, q1], [c0, c1]) qc.h(0) qc.cx(0, 1) - qc.x(0).c_if(1, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(1, True) qc.measure(0, 0) expected = QuantumCircuit([c1, c0], [q1, q0]) expected.h(1) expected.cx(1, 0) - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) expected.measure(1, 1) self.assertEqual(qc.reverse_bits(), expected) @@ -1392,29 +1397,41 @@ def test_compare_circuits_with_single_bit_conditions(self): qreg = QuantumRegister(1, name="q") creg = ClassicalRegister(1, name="c") qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.cregs[0], 1) - qc2.x(0).c_if(qc2.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], True) self.assertEqual(qc1, qc2) # Order of operations transposed. qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.clbits[-1], True) - qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) self.assertNotEqual(qc1, qc2) # Single-bit condition values not the same. qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.cregs[0], 1) - qc2.x(0).c_if(qc2.clbits[-1], False) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], False) self.assertNotEqual(qc1, qc2) def test_compare_a_circuit_with_none(self): diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index d87487639672..14b5364b2168 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -226,8 +226,10 @@ def test_circuit_depth_conditionals1(self): qc.cx(q[2], q[3]) qc.measure(q[0], c[0]) qc.measure(q[1], c[1]) - qc.h(q[2]).c_if(c, 2) - qc.h(q[3]).c_if(c, 4) + with self.assertWarns(DeprecationWarning): + qc.h(q[2]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.h(q[3]).c_if(c, 4) self.assertEqual(qc.depth(), 5) def test_circuit_depth_conditionals2(self): @@ -258,8 +260,10 @@ def test_circuit_depth_conditionals2(self): qc.cx(q[2], q[3]) qc.measure(q[0], c[0]) qc.measure(q[0], c[0]) - qc.h(q[2]).c_if(c, 2) - qc.h(q[3]).c_if(c, 4) + with self.assertWarns(DeprecationWarning): + qc.h(q[2]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.h(q[3]).c_if(c, 4) self.assertEqual(qc.depth(), 6) def test_circuit_depth_conditionals3(self): @@ -287,7 +291,8 @@ def test_circuit_depth_conditionals3(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.cx(q[0], q[3]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.cx(q[0], q[3]).c_if(c, 2) qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) @@ -320,8 +325,10 @@ def test_circuit_depth_bit_conditionals1(self): qc.h(q[3]) qc.measure(q[0], c[0]) qc.measure(q[2], c[2]) - qc.h(q[1]).c_if(c[0], True) - qc.h(q[3]).c_if(c[2], False) + with self.assertWarns(DeprecationWarning): + qc.h(q[1]).c_if(c[0], True) + with self.assertWarns(DeprecationWarning): + qc.h(q[3]).c_if(c[2], False) self.assertEqual(qc.depth(), 3) def test_circuit_depth_bit_conditionals2(self): @@ -362,12 +369,18 @@ def test_circuit_depth_bit_conditionals2(self): qc.h(q[3]) qc.measure(q[0], c[0]) qc.measure(q[2], c[2]) - qc.h(q[1]).c_if(c[1], True) - qc.h(q[3]).c_if(c[3], True) - qc.cx(0, 1).c_if(c[0], False) - qc.cx(2, 3).c_if(c[2], False) - qc.ch(0, 2).c_if(c[1], True) - qc.ch(1, 3).c_if(c[3], True) + with self.assertWarns(DeprecationWarning): + qc.h(q[1]).c_if(c[1], True) + with self.assertWarns(DeprecationWarning): + qc.h(q[3]).c_if(c[3], True) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(c[0], False) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(c[2], False) + with self.assertWarns(DeprecationWarning): + qc.ch(0, 2).c_if(c[1], True) + with self.assertWarns(DeprecationWarning): + qc.ch(1, 3).c_if(c[3], True) self.assertEqual(qc.depth(), 4) def test_circuit_depth_bit_conditionals3(self): @@ -395,9 +408,12 @@ def test_circuit_depth_bit_conditionals3(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.h(1).c_if(c[0], True) - qc.h(q[2]).c_if(c, 2) - qc.h(3).c_if(c[3], True) + with self.assertWarns(DeprecationWarning): + qc.h(1).c_if(c[0], True) + with self.assertWarns(DeprecationWarning): + qc.h(q[2]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.h(3).c_if(c[3], True) qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) qc.measure(q[3], c[3]) @@ -608,11 +624,15 @@ def test_circuit_depth_multiqubit_or_conditional(self): circ.rz(0.1, 1) circ.cz(1, 3) circ.measure(1, 0) - circ.x(0).c_if(0, 1) - self.assertEqual( - circ.depth(lambda x: x.operation.num_qubits >= 2 or x.operation.condition is not None), - 4, - ) + with self.assertWarns(DeprecationWarning): + circ.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + circ.depth( + lambda x: x.operation.num_qubits >= 2 or x.operation.condition is not None + ), + 4, + ) def test_circuit_depth_first_qubit(self): """Test finding depth of gates touching q0 only.""" @@ -765,7 +785,8 @@ def test_circuit_nonlocal_gates(self): qc.cry(0.1, q[2], q[4]) qc.z(q[3:]) qc.cswap(q[1], q[2], q[3]) - qc.iswap(q[0], q[4]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.iswap(q[0], q[4]).c_if(c, 2) result = qc.num_nonlocal_gates() expected = 3 self.assertEqual(expected, result) @@ -813,7 +834,9 @@ def test_circuit_connected_components_multi_reg(self): qc.cx(q1[1], q2[1]) qc.cx(q2[1], q1[2]) qc.cx(q1[2], q2[0]) - self.assertEqual(qc.num_connected_components(), 1) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 1) def test_circuit_connected_components_multi_reg2(self): """Test tensor factors works over multi registers #2.""" @@ -834,7 +857,9 @@ def test_circuit_connected_components_multi_reg2(self): qc.cx(q1[0], q2[1]) qc.cx(q2[0], q1[2]) qc.cx(q1[1], q2[0]) - self.assertEqual(qc.num_connected_components(), 2) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 2) def test_circuit_connected_components_disconnected(self): """Test tensor factors works with 2q subspaces.""" @@ -867,7 +892,9 @@ def test_circuit_connected_components_disconnected(self): qc.cx(q1[2], q2[2]) qc.cx(q1[3], q2[1]) qc.cx(q1[4], q2[0]) - self.assertEqual(qc.num_connected_components(), 5) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 5) def test_circuit_connected_components_with_clbits(self): """Test tensor components with classical register.""" @@ -895,7 +922,9 @@ def test_circuit_connected_components_with_clbits(self): qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) qc.measure(q[3], c[3]) - self.assertEqual(qc.num_connected_components(), 4) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 4) def test_circuit_connected_components_with_cond(self): """Test tensor components with one conditional gate.""" @@ -921,11 +950,14 @@ def test_circuit_connected_components_with_cond(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.cx(q[0], q[3]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.cx(q[0], q[3]).c_if(c, 2) qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) qc.measure(q[3], c[3]) - self.assertEqual(qc.num_connected_components(), 1) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 1) def test_circuit_connected_components_with_cond2(self): """Test tensor components with two conditional gates.""" @@ -949,9 +981,13 @@ def test_circuit_connected_components_with_cond2(self): qc.h(q[1]) qc.h(q[2]) qc.h(q[3]) - qc.h(0).c_if(c, 0) - qc.cx(1, 2).c_if(c, 4) - self.assertEqual(qc.num_connected_components(), 2) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(c, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 2).c_if(c, 4) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 2) def test_circuit_connected_components_with_cond3(self): """Test tensor components with three conditional gates and measurements.""" @@ -977,11 +1013,16 @@ def test_circuit_connected_components_with_cond3(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.h(q[0]).c_if(c, 0) - qc.cx(q[1], q[2]).c_if(c, 1) + with self.assertWarns(DeprecationWarning): + qc.h(q[0]).c_if(c, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(q[1], q[2]).c_if(c, 1) qc.measure(q[2], c[2]) - qc.x(q[3]).c_if(c, 2) - self.assertEqual(qc.num_connected_components(), 1) + with self.assertWarns(DeprecationWarning): + qc.x(q[3]).c_if(c, 2) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 1) def test_circuit_connected_components_with_bit_cond(self): """Test tensor components with one single bit conditional gate.""" @@ -1007,11 +1048,14 @@ def test_circuit_connected_components_with_bit_cond(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.cx(q[0], q[3]).c_if(c[0], True) + with self.assertWarns(DeprecationWarning): + qc.cx(q[0], q[3]).c_if(c[0], True) qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) qc.measure(q[3], c[3]) - self.assertEqual(qc.num_connected_components(), 3) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 3) def test_circuit_connected_components_with_bit_cond2(self): """Test tensor components with two bit conditional gates.""" @@ -1035,10 +1079,15 @@ def test_circuit_connected_components_with_bit_cond2(self): qc.h(q[1]) qc.h(q[2]) qc.h(q[3]) - qc.h(0).c_if(c[1], True) - qc.cx(1, 0).c_if(c[4], False) - qc.cz(2, 3).c_if(c[0], True) - self.assertEqual(qc.num_connected_components(), 5) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(c[1], True) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 0).c_if(c[4], False) + with self.assertWarns(DeprecationWarning): + qc.cz(2, 3).c_if(c[0], True) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 5) def test_circuit_connected_components_with_bit_cond3(self): """Test tensor components with register and bit conditional gates.""" @@ -1063,10 +1112,15 @@ def test_circuit_connected_components_with_bit_cond3(self): qc.h(q[1]) qc.h(q[2]) qc.h(q[3]) - qc.h(q[0]).c_if(c[0], True) - qc.cx(q[1], q[2]).c_if(c, 1) - qc.x(q[3]).c_if(c[2], True) - self.assertEqual(qc.num_connected_components(), 1) + with self.assertWarns(DeprecationWarning): + qc.h(q[0]).c_if(c[0], True) + with self.assertWarns(DeprecationWarning): + qc.cx(q[1], q[2]).c_if(c, 1) + with self.assertWarns(DeprecationWarning): + qc.x(q[3]).c_if(c[2], True) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 1) def test_circuit_unitary_factors1(self): """Test unitary factors empty circuit.""" @@ -1109,7 +1163,8 @@ def test_circuit_unitary_factors3(self): qc.h(q[3]) qc.cx(q[1], q[2]) qc.cx(q[1], q[2]) - qc.cx(q[0], q[3]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.cx(q[0], q[3]).c_if(c, 2) qc.cx(q[0], q[3]) qc.cx(q[0], q[3]) qc.cx(q[0], q[3]) diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index 984851e00388..bbbb494877f6 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -44,9 +44,12 @@ def test_circuit_qasm(self): qc.barrier(qr2) qc.cx(qr2[1], qr1[0]) qc.h(qr2[1]) - qc.x(qr2[1]).c_if(cr, 0) - qc.y(qr1[0]).c_if(cr, 1) - qc.z(qr1[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + qc.x(qr2[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.y(qr1[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.z(qr1[0]).c_if(cr, 2) qc.barrier(qr1, qr2) qc.measure(qr1[0], cr[0]) qc.measure(qr2[0], cr[1]) @@ -639,7 +642,8 @@ def test_circuit_raises_on_single_bit_condition(self): """OpenQASM 2 can't represent single-bit conditions, so test that a suitable error is printed if this is attempted.""" qc = QuantumCircuit(1, 1) - qc.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, True) with self.assertRaisesRegex(QasmError, "OpenQASM 2 can only condition on registers"): dumps(qc) diff --git a/test/python/circuit/test_commutation_checker.py b/test/python/circuit/test_commutation_checker.py index a0aeae5ca2c3..8bb3f6939add 100644 --- a/test/python/circuit/test_commutation_checker.py +++ b/test/python/circuit/test_commutation_checker.py @@ -13,36 +13,47 @@ """Test commutation checker class .""" import unittest +from test import QiskitTestCase # pylint: disable=wrong-import-order import numpy as np +from ddt import data, ddt from qiskit import ClassicalRegister from qiskit.circuit import ( - QuantumRegister, - Parameter, - Qubit, AnnotatedOperation, - InverseModifier, ControlModifier, Gate, + InverseModifier, + Parameter, + QuantumRegister, + Qubit, ) from qiskit.circuit.commutation_library import SessionCommutationChecker as scc -from qiskit.dagcircuit import DAGOpNode from qiskit.circuit.library import ( - ZGate, - XGate, - CXGate, + Barrier, CCXGate, + CPhaseGate, + CRXGate, + CRYGate, + CRZGate, + CXGate, + LinearFunction, MCXGate, - RZGate, Measure, - Barrier, + PhaseGate, Reset, - LinearFunction, - SGate, + RXGate, RXXGate, + RYGate, + RYYGate, + RZGate, + RZXGate, + RZZGate, + SGate, + XGate, + ZGate, ) -from test import QiskitTestCase # pylint: disable=wrong-import-order +from qiskit.dagcircuit import DAGOpNode class NewGateCX(Gate): @@ -55,6 +66,7 @@ def to_matrix(self): return np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex) +@ddt class TestCommutationChecker(QiskitTestCase): """Test CommutationChecker class.""" @@ -145,16 +157,10 @@ def test_caching_different_qubit_sets(self): scc.commute(XGate(), [5], [], NewGateCX(), [5, 7], []) self.assertEqual(scc.num_cached_entries(), 1) - def test_cache_with_param_gates(self): + def test_zero_rotations(self): """Check commutativity between (non-parameterized) gates with parameters.""" - scc.clear_cached_commutations() - self.assertTrue(scc.commute(RZGate(0), [0], [], XGate(), [0], [])) - self.assertFalse(scc.commute(RZGate(np.pi / 2), [0], [], XGate(), [0], [])) - self.assertTrue(scc.commute(RZGate(np.pi / 2), [0], [], RZGate(0), [0], [])) - - self.assertFalse(scc.commute(RZGate(np.pi / 2), [1], [], XGate(), [1], [])) - self.assertEqual(scc.num_cached_entries(), 3) + self.assertTrue(scc.commute(XGate(), [0], [], RZGate(0), [0], [])) def test_gates_with_parameters(self): """Check commutativity between (non-parameterized) gates with parameters.""" @@ -172,6 +178,8 @@ def test_parameterized_gates(self): # gate that has parameters and is considered parameterized rz_gate_theta = RZGate(Parameter("Theta")) + rx_gate_theta = RXGate(Parameter("Theta")) + rxx_gate_theta = RXXGate(Parameter("Theta")) rz_gate_phi = RZGate(Parameter("Phi")) self.assertEqual(len(rz_gate_theta.params), 1) self.assertTrue(rz_gate_theta.is_parameterized()) @@ -193,7 +201,6 @@ def test_parameterized_gates(self): # We should detect that parameterized gates over disjoint qubit subsets commute self.assertTrue(scc.commute(rz_gate_theta, [0], [], rz_gate_phi, [1], [])) - # We should detect that parameterized gates over disjoint qubit subsets commute self.assertTrue(scc.commute(rz_gate_theta, [2], [], cx_gate, [1, 3], [])) # However, for now commutativity checker should return False when checking @@ -201,9 +208,14 @@ def test_parameterized_gates(self): # the two gates are over intersecting qubit subsets. # This check should be changed if commutativity checker is extended to # handle parameterized gates better. - self.assertFalse(scc.commute(rz_gate_theta, [0], [], cx_gate, [0, 1], [])) - - self.assertFalse(scc.commute(rz_gate_theta, [0], [], rz_gate, [0], [])) + self.assertFalse(scc.commute(rz_gate_theta, [1], [], cx_gate, [0, 1], [])) + self.assertTrue(scc.commute(rz_gate_theta, [0], [], rz_gate, [0], [])) + self.assertTrue(scc.commute(rz_gate_theta, [0], [], rz_gate_phi, [0], [])) + self.assertTrue(scc.commute(rxx_gate_theta, [0, 1], [], rx_gate_theta, [0], [])) + self.assertTrue(scc.commute(rxx_gate_theta, [0, 1], [], XGate(), [0], [])) + self.assertTrue(scc.commute(XGate(), [0], [], rxx_gate_theta, [0, 1], [])) + self.assertTrue(scc.commute(rx_gate_theta, [0], [], rxx_gate_theta, [0, 1], [])) + self.assertTrue(scc.commute(rz_gate_theta, [0], [], cx_gate, [0, 1], [])) def test_measure(self): """Check commutativity involving measures.""" @@ -252,26 +264,33 @@ def test_conditional_gates(self): # Currently, in all cases commutativity checker should returns False. # This is definitely suboptimal. - self.assertFalse( - scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[2]], []) - ) - self.assertFalse( - scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[1]], []) - ) - self.assertFalse( - scc.commute( - CXGate().c_if(cr[0], 0), - [qr[0], qr[1]], - [], - CXGate().c_if(cr[0], 0), - [qr[0], qr[1]], - [], + with self.assertWarns(DeprecationWarning): + self.assertFalse( + scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[2]], []) ) - ) - self.assertFalse( - scc.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate().c_if(cr[0], 1), [qr[0]], []) - ) - self.assertFalse(scc.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate(), [qr[0]], [])) + with self.assertWarns(DeprecationWarning): + self.assertFalse( + scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[1]], []) + ) + with self.assertWarns(DeprecationWarning): + self.assertFalse( + scc.commute( + CXGate().c_if(cr[0], 0), + [qr[0], qr[1]], + [], + CXGate().c_if(cr[0], 0), + [qr[0], qr[1]], + [], + ) + ) + with self.assertWarns(DeprecationWarning): + self.assertFalse( + scc.commute( + XGate().c_if(cr[0], 0), [qr[0]], [], XGate().c_if(cr[0], 1), [qr[0]], [] + ) + ) + with self.assertWarns(DeprecationWarning): + self.assertFalse(scc.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate(), [qr[0]], [])) def test_complex_gates(self): """Check commutativity involving more complex gates.""" @@ -354,6 +373,39 @@ def test_serialization(self): cc2.commute_nodes(dop1, dop2) self.assertEqual(cc2.num_cached_entries(), 1) + @data( + RXGate, + RYGate, + RZGate, + PhaseGate, + CRXGate, + CRYGate, + CRZGate, + CPhaseGate, + RXXGate, + RYYGate, + RZZGate, + RZXGate, + ) + def test_cutoff_angles(self, gate_cls): + """Check rotations with a small enough angle are cut off.""" + max_power = 30 + from qiskit.circuit.library import DCXGate + + generic_gate = DCXGate() # gate that does not commute with any rotation gate + + cutoff_angle = 1e-5 # this is the cutoff we use in the CommutationChecker + + for i in range(1, max_power + 1): + angle = 2 ** (-i) + gate = gate_cls(angle) + qargs = list(range(gate.num_qubits)) + + if angle < cutoff_angle: + self.assertTrue(scc.commute(generic_gate, [0, 1], [], gate, qargs, [])) + else: + self.assertFalse(scc.commute(generic_gate, [0, 1], [], gate, qargs, [])) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/test_compose.py b/test/python/circuit/test_compose.py index 0981b5f3ed79..8221f81ad15e 100644 --- a/test/python/circuit/test_compose.py +++ b/test/python/circuit/test_compose.py @@ -367,10 +367,12 @@ def __init__(self): super().__init__("mygate", 1, []) conditional = QuantumCircuit(1, 1) - conditional.append(Custom(), [0], []).c_if(conditional.clbits[0], True) + with self.assertWarns(DeprecationWarning): + conditional.append(Custom(), [0], []).c_if(conditional.clbits[0], True) test = base.compose(conditional, qubits=[0], clbits=[0], copy=False) self.assertIs(test.data[-1].operation, conditional.data[-1].operation) - self.assertEqual(test.data[-1].operation.condition, (test.clbits[0], True)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(test.data[-1].operation.condition, (test.clbits[0], True)) def test_compose_classical(self): """Composing on classical bits. @@ -463,16 +465,20 @@ def test_compose_conditional(self): creg = ClassicalRegister(2, "rcr") circuit_right = QuantumCircuit(qreg, creg) - circuit_right.x(qreg[1]).c_if(creg, 3) - circuit_right.h(qreg[0]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit_right.x(qreg[1]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit_right.h(qreg[0]).c_if(creg, 3) circuit_right.measure(qreg, creg) # permuted subset of qubits and clbits circuit_composed = self.circuit_left.compose(circuit_right, qubits=[1, 4], clbits=[0, 1]) circuit_expected = self.circuit_left.copy() - circuit_expected.x(self.left_qubit4).c_if(*self.condition) - circuit_expected.h(self.left_qubit1).c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + circuit_expected.x(self.left_qubit4).c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + circuit_expected.h(self.left_qubit1).c_if(*self.condition) circuit_expected.measure(self.left_qubit1, self.left_clbit0) circuit_expected.measure(self.left_qubit4, self.left_clbit1) @@ -489,24 +495,36 @@ def test_compose_conditional_no_match(self): right.cx(0, 1) right.h(0) right.measure([0, 1], [0, 1]) - right.z(2).c_if(right.cregs[0], 1) - right.x(2).c_if(right.cregs[1], 1) + with self.assertWarns(DeprecationWarning): + right.z(2).c_if(right.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + right.x(2).c_if(right.cregs[1], 1) test = QuantumCircuit(3, 3).compose(right, range(3), range(2)) z = next(ins.operation for ins in test.data[::-1] if ins.operation.name == "z") x = next(ins.operation for ins in test.data[::-1] if ins.operation.name == "x") # The registers should have been mapped, including the bits inside them. Unlike the # previous test, there are no matching registers in the destination circuit, so the # composition needs to add new registers (bit groupings) over the existing mapped bits. - self.assertIsNot(z.condition, None) - self.assertIsInstance(z.condition[0], ClassicalRegister) - self.assertEqual(len(z.condition[0]), len(right.cregs[0])) - self.assertIs(z.condition[0][0], test.clbits[0]) - self.assertEqual(z.condition[1], 1) - self.assertIsNot(x.condition, None) - self.assertIsInstance(x.condition[0], ClassicalRegister) - self.assertEqual(len(x.condition[0]), len(right.cregs[1])) - self.assertEqual(z.condition[1], 1) - self.assertIs(x.condition[0][0], test.clbits[1]) + with self.assertWarns(DeprecationWarning): + self.assertIsNot(z.condition, None) + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(z.condition[0], ClassicalRegister) + with self.assertWarns(DeprecationWarning): + self.assertEqual(len(z.condition[0]), len(right.cregs[0])) + with self.assertWarns(DeprecationWarning): + self.assertIs(z.condition[0][0], test.clbits[0]) + with self.assertWarns(DeprecationWarning): + self.assertEqual(z.condition[1], 1) + with self.assertWarns(DeprecationWarning): + self.assertIsNot(x.condition, None) + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(x.condition[0], ClassicalRegister) + with self.assertWarns(DeprecationWarning): + self.assertEqual(len(x.condition[0]), len(right.cregs[1])) + with self.assertWarns(DeprecationWarning): + self.assertEqual(z.condition[1], 1) + with self.assertWarns(DeprecationWarning): + self.assertIs(x.condition[0][0], test.clbits[1]) def test_compose_switch_match(self): """Test that composition containing a `switch` with a register that matches proceeds @@ -729,11 +747,13 @@ def test_single_bit_condition(self): """Test that compose can correctly handle circuits that contain conditions on single bits. This is a regression test of the bug that broke qiskit-experiments in gh-7653.""" base = QuantumCircuit(1, 1) - base.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(0, True) test = QuantumCircuit(1, 1).compose(base) self.assertIsNot(base.clbits[0], test.clbits[0]) self.assertEqual(base, test) - self.assertIs(test.data[0].operation.condition[0], test.clbits[0]) + with self.assertWarns(DeprecationWarning): + self.assertIs(test.data[0].operation.condition[0], test.clbits[0]) def test_condition_mapping_ifelseop(self): """Test that the condition in an `IfElseOp` is correctly mapped to a new set of bits and diff --git a/test/python/circuit/test_control_flow.py b/test/python/circuit/test_control_flow.py index 35c57839d753..51733a37ffc9 100644 --- a/test/python/circuit/test_control_flow.py +++ b/test/python/circuit/test_control_flow.py @@ -587,7 +587,8 @@ def test_appending_switch_case_op(self, target, labels): self.assertEqual(qc.data[0].operation.name, "switch_case") self.assertEqual(qc.data[0].operation.params, bodies[: len(labels)]) - self.assertEqual(qc.data[0].operation.condition, None) + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.data[0].operation.condition, None) self.assertEqual(qc.data[0].qubits, tuple(qc.qubits[1:4])) self.assertEqual(qc.data[0].clbits, (qc.clbits[1],)) @@ -618,7 +619,7 @@ def test_quantumcircuit_switch(self, target, labels): self.assertEqual(qc.data[0].operation.name, "switch_case") self.assertEqual(qc.data[0].operation.params, bodies[: len(labels)]) - self.assertEqual(qc.data[0].operation.condition, None) + self.assertEqual(qc.data[0].operation._condition, None) self.assertEqual(qc.data[0].qubits, tuple(qc.qubits[1:4])) self.assertEqual(qc.data[0].clbits, (qc.clbits[1],)) @@ -808,15 +809,20 @@ def test_no_c_if_for_while_loop_if_else(self): body = QuantumCircuit(1) with self.assertRaisesRegex(NotImplementedError, r"cannot be classically controlled"): - qc.while_loop((qc.clbits[0], False), body, [qc.qubits[0]], []).c_if(qc.clbits[0], True) + with self.assertWarns(DeprecationWarning): + qc.while_loop((qc.clbits[0], False), body, [qc.qubits[0]], []).c_if( + qc.clbits[0], True + ) with self.assertRaisesRegex(NotImplementedError, r"cannot be classically controlled"): - qc.if_test((qc.clbits[0], False), body, [qc.qubits[0]], []).c_if(qc.clbits[0], True) + with self.assertWarns(DeprecationWarning): + qc.if_test((qc.clbits[0], False), body, [qc.qubits[0]], []).c_if(qc.clbits[0], True) with self.assertRaisesRegex(NotImplementedError, r"cannot be classically controlled"): - qc.if_else((qc.clbits[0], False), body, body, [qc.qubits[0]], []).c_if( - qc.clbits[0], True - ) + with self.assertWarns(DeprecationWarning): + qc.if_else((qc.clbits[0], False), body, body, [qc.qubits[0]], []).c_if( + qc.clbits[0], True + ) def test_nested_parameters_are_recognised(self): """Verify that parameters added inside a control-flow operator get added to the outer diff --git a/test/python/circuit/test_control_flow_builders.py b/test/python/circuit/test_control_flow_builders.py index 7a3a0873ae77..419cfb406d19 100644 --- a/test/python/circuit/test_control_flow_builders.py +++ b/test/python/circuit/test_control_flow_builders.py @@ -193,12 +193,16 @@ def test_register_condition_in_nested_block(self): with self.subTest("if/c_if"): test = QuantumCircuit(qr, clbits, cr1, cr2, cr3, cr4) with test.if_test((cr1, 0)): - test.x(0).c_if(cr2, 0) - test.z(0).c_if(cr3, 0) + with self.assertWarns(DeprecationWarning): + test.x(0).c_if(cr2, 0) + with self.assertWarns(DeprecationWarning): + test.z(0).c_if(cr3, 0) true_body = QuantumCircuit([qr[0]], clbits, cr1, cr2, cr3) - true_body.x(0).c_if(cr2, 0) - true_body.z(0).c_if(cr3, 0) + with self.assertWarns(DeprecationWarning): + true_body.x(0).c_if(cr2, 0) + with self.assertWarns(DeprecationWarning): + true_body.z(0).c_if(cr3, 0) expected = QuantumCircuit(qr, clbits, cr1, cr2, cr3, cr4) expected.if_test((cr1, 0), true_body, [qr[0]], clbits + list(cr1)) @@ -209,14 +213,18 @@ def test_register_condition_in_nested_block(self): test = QuantumCircuit(qr, clbits, cr1, cr2, cr3, cr4) with test.while_loop((cr1, 0)): with test.if_test((cr2, 0)) as else_: - test.x(0).c_if(cr3, 0) + with self.assertWarns(DeprecationWarning): + test.x(0).c_if(cr3, 0) with else_: - test.z(0).c_if(cr4, 0) + with self.assertWarns(DeprecationWarning): + test.z(0).c_if(cr4, 0) true_body = QuantumCircuit([qr[0]], cr2, cr3, cr4) - true_body.x(0).c_if(cr3, 0) + with self.assertWarns(DeprecationWarning): + true_body.x(0).c_if(cr3, 0) false_body = QuantumCircuit([qr[0]], cr2, cr3, cr4) - false_body.z(0).c_if(cr4, 0) + with self.assertWarns(DeprecationWarning): + false_body.z(0).c_if(cr4, 0) while_body = QuantumCircuit([qr[0]], clbits, cr1, cr2, cr3, cr4) while_body.if_else((cr2, 0), true_body, false_body, [qr[0]], clbits) @@ -647,17 +655,23 @@ def test_if_else_tracks_registers(self): test = QuantumCircuit(qr, *cr) with test.if_test((cr[0], 0)) as else_: - test.h(0).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(cr[1], 0) # Test repetition. - test.h(0).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(cr[1], 0) with else_: - test.h(0).c_if(cr[2], 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(cr[2], 0) true_body = QuantumCircuit([qr[0]], cr[0], cr[1], cr[2]) - true_body.h(qr[0]).c_if(cr[1], 0) - true_body.h(qr[0]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + true_body.h(qr[0]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + true_body.h(qr[0]).c_if(cr[1], 0) false_body = QuantumCircuit([qr[0]], cr[0], cr[1], cr[2]) - false_body.h(qr[0]).c_if(cr[2], 0) + with self.assertWarns(DeprecationWarning): + false_body.h(qr[0]).c_if(cr[2], 0) expected = QuantumCircuit(qr, *cr) expected.if_else( @@ -1036,11 +1050,13 @@ def test_break_continue_accept_c_if(self, loop_operation): test = QuantumCircuit(qubits, clbits) with test.for_loop(range(2)): test.h(0) - loop_operation(test).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + loop_operation(test).c_if(1, 0) body = QuantumCircuit([qubits[0]], [clbits[1]]) body.h(qubits[0]) - loop_operation(body).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + loop_operation(body).c_if(clbits[1], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, body, [qubits[0]], [clbits[1]]) @@ -1052,11 +1068,13 @@ def test_break_continue_accept_c_if(self, loop_operation): test = QuantumCircuit(qubits, clbits) with test.while_loop(cond): test.h(0) - loop_operation(test).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + loop_operation(test).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) body.h(qubits[0]) - loop_operation(body).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + loop_operation(body).c_if(clbits[1], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop(cond, body, [qubits[0]], clbits) @@ -1230,7 +1248,8 @@ def test_break_continue_nested_in_if(self, loop_operation): # full width of the loop do so. with test.if_test(cond_inner): pass - test.h(0).c_if(2, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(2, 0) true_body1 = QuantumCircuit([qubits[0], clbits[0], clbits[2]]) loop_operation(true_body1) @@ -1240,7 +1259,8 @@ def test_break_continue_nested_in_if(self, loop_operation): loop_body = QuantumCircuit([qubits[0], clbits[0], clbits[2]]) loop_body.if_test(cond_inner, true_body1, [qubits[0]], [clbits[0], clbits[2]]) loop_body.if_test(cond_inner, true_body2, [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, [qubits[0]], [clbits[0], clbits[2]]) @@ -1258,7 +1278,8 @@ def test_break_continue_nested_in_if(self, loop_operation): pass with else_: pass - test.h(0).c_if(2, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(2, 0) true_body1 = QuantumCircuit([qubits[0], qubits[1], clbits[0], clbits[2]]) true_body1.h(qubits[1]) @@ -1273,7 +1294,8 @@ def test_break_continue_nested_in_if(self, loop_operation): cond_inner, true_body1, false_body1, [qubits[0], qubits[1]], [clbits[0], clbits[2]] ) loop_body.if_else(cond_inner, true_body2, false_body2, [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop( @@ -1289,7 +1311,8 @@ def test_break_continue_nested_in_if(self, loop_operation): loop_operation(test) with test.if_test(cond_inner): pass - test.h(0).c_if(2, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(2, 0) true_body1 = QuantumCircuit([qubits[0], clbits[0], clbits[1], clbits[2]]) loop_operation(true_body1) @@ -1301,7 +1324,8 @@ def test_break_continue_nested_in_if(self, loop_operation): cond_inner, true_body1, [qubits[0]], [clbits[0], clbits[1], clbits[2]] ) loop_body.if_test(cond_inner, true_body2, [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop( @@ -1321,7 +1345,8 @@ def test_break_continue_nested_in_if(self, loop_operation): pass with else_: pass - test.h(0).c_if(2, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(2, 0) true_body1 = QuantumCircuit([qubits[0], qubits[1], clbits[0], clbits[1], clbits[2]]) true_body1.h(qubits[1]) @@ -1340,7 +1365,8 @@ def test_break_continue_nested_in_if(self, loop_operation): [clbits[0], clbits[1], clbits[2]], ) loop_body.if_else(cond_inner, true_body2, false_body2, [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop( @@ -1368,7 +1394,8 @@ def test_break_continue_nested_in_switch(self, loop_operation): with test.switch(clbits[0]) as case: with case(case.DEFAULT): pass - test.h(0).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(clbits[2], 0) body0 = QuantumCircuit([qubits[0], clbits[0], clbits[2]]) loop_operation(body0) @@ -1379,7 +1406,8 @@ def test_break_continue_nested_in_switch(self, loop_operation): loop_body = QuantumCircuit([qubits[0], clbits[0], clbits[2]]) loop_body.switch(clbits[0], [(0, body0), (1, body1)], [qubits[0]], [clbits[0], clbits[2]]) loop_body.switch(clbits[0], [(CASE_DEFAULT, body2)], [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, [qubits[0]], [clbits[0], clbits[2]]) @@ -1465,18 +1493,23 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_operation(test) # inner true 2 with test.if_test(cond_inner): - test.h(0).c_if(3, 0) - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) # outer true 2 with test.if_test(cond_outer): - test.h(2).c_if(5, 0) - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) inner_true_body1 = QuantumCircuit(qubits[:4], clbits[:2], clbits[3:7]) loop_operation(inner_true_body1) inner_true_body2 = QuantumCircuit([qubits[0], clbits[0], clbits[3]]) - inner_true_body2.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + inner_true_body2.h(qubits[0]).c_if(clbits[3], 0) outer_true_body1 = QuantumCircuit(qubits[:4], clbits[:2], clbits[3:7]) outer_true_body1.if_test( @@ -1485,15 +1518,18 @@ def test_break_continue_deeply_nested(self, loop_operation): outer_true_body1.if_test( cond_inner, inner_true_body2, [qubits[0]], [clbits[0], clbits[3]] ) - outer_true_body1.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + outer_true_body1.h(qubits[1]).c_if(clbits[4], 0) outer_true_body2 = QuantumCircuit([qubits[2], clbits[1], clbits[5]]) - outer_true_body2.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + outer_true_body2.h(qubits[2]).c_if(clbits[5], 0) loop_body = QuantumCircuit(qubits[:4], clbits[:2] + clbits[3:7]) loop_body.if_test(cond_outer, outer_true_body1, qubits[:4], clbits[:2] + clbits[3:7]) loop_body.if_test(cond_outer, outer_true_body2, [qubits[2]], [clbits[1], clbits[5]]) - loop_body.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[3]).c_if(clbits[6], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, qubits[:4], clbits[:2] + clbits[3:7]) @@ -1507,31 +1543,43 @@ def test_break_continue_deeply_nested(self, loop_operation): with test.if_test(cond_outer): # inner 1 with test.if_test(cond_inner) as inner1_else: - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) with inner1_else: - loop_operation(test).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + loop_operation(test).c_if(4, 0) # inner 2 with test.if_test(cond_inner) as inner2_else: - test.h(1).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(5, 0) with inner2_else: - test.h(2).c_if(6, 0) - test.h(3).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(7, 0) # outer 2 with test.if_test(cond_outer) as outer2_else: - test.h(4).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(8, 0) with outer2_else: - test.h(5).c_if(9, 0) - test.h(6).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(10, 0) inner1_true = QuantumCircuit(qubits[:7], clbits[:2], clbits[3:11]) - inner1_true.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + inner1_true.h(qubits[0]).c_if(clbits[3], 0) inner1_false = QuantumCircuit(qubits[:7], clbits[:2], clbits[3:11]) - loop_operation(inner1_false).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + loop_operation(inner1_false).c_if(clbits[4], 0) inner2_true = QuantumCircuit([qubits[1], qubits[2], clbits[0], clbits[5], clbits[6]]) - inner2_true.h(qubits[1]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + inner2_true.h(qubits[1]).c_if(clbits[5], 0) inner2_false = QuantumCircuit([qubits[1], qubits[2], clbits[0], clbits[5], clbits[6]]) - inner2_false.h(qubits[2]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + inner2_false.h(qubits[2]).c_if(clbits[6], 0) outer1_true = QuantumCircuit(qubits[:7], clbits[:2], clbits[3:11]) outer1_true.if_else( @@ -1544,12 +1592,15 @@ def test_break_continue_deeply_nested(self, loop_operation): qubits[1:3], [clbits[0], clbits[5], clbits[6]], ) - outer1_true.h(qubits[3]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + outer1_true.h(qubits[3]).c_if(clbits[7], 0) outer2_true = QuantumCircuit([qubits[4], qubits[5], clbits[1], clbits[8], clbits[9]]) - outer2_true.h(qubits[4]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[4]).c_if(clbits[8], 0) outer2_false = QuantumCircuit([qubits[4], qubits[5], clbits[1], clbits[8], clbits[9]]) - outer2_false.h(qubits[5]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer2_false.h(qubits[5]).c_if(clbits[9], 0) loop_body = QuantumCircuit(qubits[:7], clbits[:2], clbits[3:11]) loop_body.if_test(cond_outer, outer1_true, qubits[:7], clbits[:2] + clbits[3:11]) @@ -1560,7 +1611,8 @@ def test_break_continue_deeply_nested(self, loop_operation): qubits[4:6], [clbits[1], clbits[8], clbits[9]], ) - loop_body.h(qubits[6]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[6]).c_if(clbits[10], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, qubits[:7], clbits[:2] + clbits[3:11]) @@ -1574,72 +1626,90 @@ def test_break_continue_deeply_nested(self, loop_operation): test = QuantumCircuit(qubits, clbits) with test.for_loop(range(2)): - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) # outer 1 with test.if_test(cond_outer) as outer1_else: - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) with outer1_else: - test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) # outer 2 (nesting the inner condition in the 'if') with test.if_test(cond_outer) as outer2_else: - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) # inner 21 with test.if_test(cond_inner) as inner21_else: loop_operation(test) with inner21_else: - test.h(4).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(7, 0) # inner 22 with test.if_test(cond_inner) as inner22_else: - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) with inner22_else: loop_operation(test) # inner 23 with test.switch(cond_inner[0]) as inner23_case: with inner23_case(True): - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) with inner23_case(False): loop_operation(test) - test.h(6).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(9, 0) with outer2_else: - test.h(7).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(7).c_if(10, 0) # inner 24 with test.if_test(cond_inner) as inner24_else: - test.h(8).c_if(11, 0) + with self.assertWarns(DeprecationWarning): + test.h(8).c_if(11, 0) with inner24_else: - test.h(9).c_if(12, 0) + with self.assertWarns(DeprecationWarning): + test.h(9).c_if(12, 0) # outer 3 (nesting the inner condition in an 'else' branch) with test.if_test(cond_outer) as outer3_else: - test.h(10).c_if(13, 0) + with self.assertWarns(DeprecationWarning): + test.h(10).c_if(13, 0) with outer3_else: - test.h(11).c_if(14, 0) + with self.assertWarns(DeprecationWarning): + test.h(11).c_if(14, 0) # inner 31 with test.if_test(cond_inner) as inner31_else: loop_operation(test) with inner31_else: - test.h(12).c_if(15, 0) + with self.assertWarns(DeprecationWarning): + test.h(12).c_if(15, 0) # inner 32 with test.if_test(cond_inner) as inner32_else: - test.h(13).c_if(16, 0) + with self.assertWarns(DeprecationWarning): + test.h(13).c_if(16, 0) with inner32_else: loop_operation(test) # inner 33 with test.if_test(cond_inner) as inner33_else: - test.h(14).c_if(17, 0) + with self.assertWarns(DeprecationWarning): + test.h(14).c_if(17, 0) with inner33_else: - test.h(15).c_if(18, 0) + with self.assertWarns(DeprecationWarning): + test.h(15).c_if(18, 0) - test.h(16).c_if(19, 0) + with self.assertWarns(DeprecationWarning): + test.h(16).c_if(19, 0) # End of test "for" loop. # No `clbits[2]` here because that's only used in `cond_loop`, for while loops. @@ -1648,32 +1718,40 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_bits = loop_qubits + loop_clbits outer1_true = QuantumCircuit([qubits[1], qubits[2], clbits[1], clbits[4], clbits[5]]) - outer1_true.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + outer1_true.h(qubits[1]).c_if(clbits[4], 0) outer1_false = QuantumCircuit([qubits[1], qubits[2], clbits[1], clbits[4], clbits[5]]) - outer1_false.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + outer1_false.h(qubits[2]).c_if(clbits[5], 0) inner21_true = QuantumCircuit(loop_bits) loop_operation(inner21_true) inner21_false = QuantumCircuit(loop_bits) - inner21_false.h(qubits[4]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + inner21_false.h(qubits[4]).c_if(clbits[7], 0) inner22_true = QuantumCircuit(loop_bits) - inner22_true.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + inner22_true.h(qubits[5]).c_if(clbits[8], 0) inner22_false = QuantumCircuit(loop_bits) loop_operation(inner22_false) inner23_true = QuantumCircuit(loop_bits) - inner23_true.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + inner23_true.h(qubits[5]).c_if(clbits[8], 0) inner23_false = QuantumCircuit(loop_bits) loop_operation(inner23_false) inner24_true = QuantumCircuit(qubits[8:10], [clbits[0], clbits[11], clbits[12]]) - inner24_true.h(qubits[8]).c_if(clbits[11], 0) + with self.assertWarns(DeprecationWarning): + inner24_true.h(qubits[8]).c_if(clbits[11], 0) inner24_false = QuantumCircuit(qubits[8:10], [clbits[0], clbits[11], clbits[12]]) - inner24_false.h(qubits[9]).c_if(clbits[12], 0) + with self.assertWarns(DeprecationWarning): + inner24_false.h(qubits[9]).c_if(clbits[12], 0) outer2_true = QuantumCircuit(loop_bits) - outer2_true.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[3]).c_if(clbits[6], 0) outer2_true.if_else(cond_inner, inner21_true, inner21_false, loop_qubits, loop_clbits) outer2_true.if_else(cond_inner, inner22_true, inner22_false, loop_qubits, loop_clbits) outer2_true.switch( @@ -1682,9 +1760,11 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_qubits, loop_clbits, ) - outer2_true.h(qubits[6]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[6]).c_if(clbits[9], 0) outer2_false = QuantumCircuit(loop_bits) - outer2_false.h(qubits[7]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + outer2_false.h(qubits[7]).c_if(clbits[10], 0) outer2_false.if_else( cond_inner, inner24_true, @@ -1696,22 +1776,28 @@ def test_break_continue_deeply_nested(self, loop_operation): inner31_true = QuantumCircuit(loop_bits) loop_operation(inner31_true) inner31_false = QuantumCircuit(loop_bits) - inner31_false.h(qubits[12]).c_if(clbits[15], 0) + with self.assertWarns(DeprecationWarning): + inner31_false.h(qubits[12]).c_if(clbits[15], 0) inner32_true = QuantumCircuit(loop_bits) - inner32_true.h(qubits[13]).c_if(clbits[16], 0) + with self.assertWarns(DeprecationWarning): + inner32_true.h(qubits[13]).c_if(clbits[16], 0) inner32_false = QuantumCircuit(loop_bits) loop_operation(inner32_false) inner33_true = QuantumCircuit(qubits[14:16], [clbits[0], clbits[17], clbits[18]]) - inner33_true.h(qubits[14]).c_if(clbits[17], 0) + with self.assertWarns(DeprecationWarning): + inner33_true.h(qubits[14]).c_if(clbits[17], 0) inner33_false = QuantumCircuit(qubits[14:16], [clbits[0], clbits[17], clbits[18]]) - inner33_false.h(qubits[15]).c_if(clbits[18], 0) + with self.assertWarns(DeprecationWarning): + inner33_false.h(qubits[15]).c_if(clbits[18], 0) outer3_true = QuantumCircuit(loop_bits) - outer3_true.h(qubits[10]).c_if(clbits[13], 0) + with self.assertWarns(DeprecationWarning): + outer3_true.h(qubits[10]).c_if(clbits[13], 0) outer3_false = QuantumCircuit(loop_bits) - outer3_false.h(qubits[11]).c_if(clbits[14], 0) + with self.assertWarns(DeprecationWarning): + outer3_false.h(qubits[11]).c_if(clbits[14], 0) outer3_false.if_else(cond_inner, inner31_true, inner31_false, loop_qubits, loop_clbits) outer3_false.if_else(cond_inner, inner32_true, inner32_false, loop_qubits, loop_clbits) outer3_false.if_else( @@ -1723,7 +1809,8 @@ def test_break_continue_deeply_nested(self, loop_operation): ) loop_body = QuantumCircuit(loop_bits) - loop_body.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[3], 0) loop_body.if_else( cond_outer, outer1_true, @@ -1733,7 +1820,8 @@ def test_break_continue_deeply_nested(self, loop_operation): ) loop_body.if_else(cond_outer, outer2_true, outer2_false, loop_qubits, loop_clbits) loop_body.if_else(cond_outer, outer3_true, outer3_false, loop_qubits, loop_clbits) - loop_body.h(qubits[16]).c_if(clbits[19], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[16]).c_if(clbits[19], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, loop_qubits, loop_clbits) @@ -1756,33 +1844,41 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_operation(test) # inner true 2 with test.if_test(cond_inner): - test.h(0).c_if(3, 0) - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) # outer true 2 with test.if_test(cond_outer): - test.h(2).c_if(5, 0) - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) inner_true_body1 = QuantumCircuit(qubits[:4], clbits[:7]) loop_operation(inner_true_body1) inner_true_body2 = QuantumCircuit([qubits[0], clbits[0], clbits[3]]) - inner_true_body2.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + inner_true_body2.h(qubits[0]).c_if(clbits[3], 0) outer_true_body1 = QuantumCircuit(qubits[:4], clbits[:7]) outer_true_body1.if_test(cond_inner, inner_true_body1, qubits[:4], clbits[:7]) outer_true_body1.if_test( cond_inner, inner_true_body2, [qubits[0]], [clbits[0], clbits[3]] ) - outer_true_body1.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + outer_true_body1.h(qubits[1]).c_if(clbits[4], 0) outer_true_body2 = QuantumCircuit([qubits[2], clbits[1], clbits[5]]) - outer_true_body2.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + outer_true_body2.h(qubits[2]).c_if(clbits[5], 0) loop_body = QuantumCircuit(qubits[:4], clbits[:7]) loop_body.if_test(cond_outer, outer_true_body1, qubits[:4], clbits[:7]) loop_body.if_test(cond_outer, outer_true_body2, [qubits[2]], [clbits[1], clbits[5]]) - loop_body.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[3]).c_if(clbits[6], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop(cond_loop, loop_body, qubits[:4], clbits[:7]) @@ -1796,31 +1892,43 @@ def test_break_continue_deeply_nested(self, loop_operation): with test.if_test(cond_outer): # inner 1 with test.if_test(cond_inner) as inner1_else: - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) with inner1_else: - loop_operation(test).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + loop_operation(test).c_if(4, 0) # inner 2 with test.if_test(cond_inner) as inner2_else: - test.h(1).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(5, 0) with inner2_else: - test.h(2).c_if(6, 0) - test.h(3).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(7, 0) # outer 2 with test.if_test(cond_outer) as outer2_else: - test.h(4).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(8, 0) with outer2_else: - test.h(5).c_if(9, 0) - test.h(6).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(10, 0) inner1_true = QuantumCircuit(qubits[:7], clbits[:11]) - inner1_true.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + inner1_true.h(qubits[0]).c_if(clbits[3], 0) inner1_false = QuantumCircuit(qubits[:7], clbits[:11]) - loop_operation(inner1_false).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + loop_operation(inner1_false).c_if(clbits[4], 0) inner2_true = QuantumCircuit([qubits[1], qubits[2], clbits[0], clbits[5], clbits[6]]) - inner2_true.h(qubits[1]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + inner2_true.h(qubits[1]).c_if(clbits[5], 0) inner2_false = QuantumCircuit([qubits[1], qubits[2], clbits[0], clbits[5], clbits[6]]) - inner2_false.h(qubits[2]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + inner2_false.h(qubits[2]).c_if(clbits[6], 0) outer1_true = QuantumCircuit(qubits[:7], clbits[:11]) outer1_true.if_else(cond_inner, inner1_true, inner1_false, qubits[:7], clbits[:11]) @@ -1831,12 +1939,15 @@ def test_break_continue_deeply_nested(self, loop_operation): qubits[1:3], [clbits[0], clbits[5], clbits[6]], ) - outer1_true.h(qubits[3]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + outer1_true.h(qubits[3]).c_if(clbits[7], 0) outer2_true = QuantumCircuit([qubits[4], qubits[5], clbits[1], clbits[8], clbits[9]]) - outer2_true.h(qubits[4]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[4]).c_if(clbits[8], 0) outer2_false = QuantumCircuit([qubits[4], qubits[5], clbits[1], clbits[8], clbits[9]]) - outer2_false.h(qubits[5]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer2_false.h(qubits[5]).c_if(clbits[9], 0) loop_body = QuantumCircuit(qubits[:7], clbits[:11]) loop_body.if_test(cond_outer, outer1_true, qubits[:7], clbits[:11]) @@ -1847,7 +1958,8 @@ def test_break_continue_deeply_nested(self, loop_operation): qubits[4:6], [clbits[1], clbits[8], clbits[9]], ) - loop_body.h(qubits[6]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[6]).c_if(clbits[10], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop(cond_loop, loop_body, qubits[:7], clbits[:11]) @@ -1857,65 +1969,81 @@ def test_break_continue_deeply_nested(self, loop_operation): with self.subTest("while/else/else"): test = QuantumCircuit(qubits, clbits) with test.while_loop(cond_loop): - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) # outer 1 with test.if_test(cond_outer) as outer1_else: - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) with outer1_else: - test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) # outer 2 (nesting the inner condition in the 'if') with test.if_test(cond_outer) as outer2_else: - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) # inner 21 with test.if_test(cond_inner) as inner21_else: loop_operation(test) with inner21_else: - test.h(4).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(7, 0) # inner 22 with test.if_test(cond_inner) as inner22_else: - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) with inner22_else: loop_operation(test) - test.h(6).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(9, 0) with outer2_else: - test.h(7).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(7).c_if(10, 0) # inner 23 with test.if_test(cond_inner) as inner23_else: - test.h(8).c_if(11, 0) + with self.assertWarns(DeprecationWarning): + test.h(8).c_if(11, 0) with inner23_else: - test.h(9).c_if(12, 0) + with self.assertWarns(DeprecationWarning): + test.h(9).c_if(12, 0) # outer 3 (nesting the inner condition in an 'else' branch) with test.if_test(cond_outer) as outer3_else: - test.h(10).c_if(13, 0) + with self.assertWarns(DeprecationWarning): + test.h(10).c_if(13, 0) with outer3_else: - test.h(11).c_if(14, 0) + with self.assertWarns(DeprecationWarning): + test.h(11).c_if(14, 0) # inner 31 with test.if_test(cond_inner) as inner31_else: loop_operation(test) with inner31_else: - test.h(12).c_if(15, 0) + with self.assertWarns(DeprecationWarning): + test.h(12).c_if(15, 0) # inner 32 with test.if_test(cond_inner) as inner32_else: - test.h(13).c_if(16, 0) + with self.assertWarns(DeprecationWarning): + test.h(13).c_if(16, 0) with inner32_else: loop_operation(test) # inner 33 with test.if_test(cond_inner) as inner33_else: - test.h(14).c_if(17, 0) + with self.assertWarns(DeprecationWarning): + test.h(14).c_if(17, 0) with inner33_else: - test.h(15).c_if(18, 0) - - test.h(16).c_if(19, 0) + with self.assertWarns(DeprecationWarning): + test.h(15).c_if(18, 0) + with self.assertWarns(DeprecationWarning): + test.h(16).c_if(19, 0) # End of test "for" loop. # No `clbits[2]` here because that's only used in `cond_loop`, for while loops. @@ -1924,32 +2052,41 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_bits = loop_qubits + loop_clbits outer1_true = QuantumCircuit([qubits[1], qubits[2], clbits[1], clbits[4], clbits[5]]) - outer1_true.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + outer1_true.h(qubits[1]).c_if(clbits[4], 0) outer1_false = QuantumCircuit([qubits[1], qubits[2], clbits[1], clbits[4], clbits[5]]) - outer1_false.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + outer1_false.h(qubits[2]).c_if(clbits[5], 0) inner21_true = QuantumCircuit(loop_bits) loop_operation(inner21_true) inner21_false = QuantumCircuit(loop_bits) - inner21_false.h(qubits[4]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + inner21_false.h(qubits[4]).c_if(clbits[7], 0) inner22_true = QuantumCircuit(loop_bits) - inner22_true.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + inner22_true.h(qubits[5]).c_if(clbits[8], 0) inner22_false = QuantumCircuit(loop_bits) loop_operation(inner22_false) inner23_true = QuantumCircuit(qubits[8:10], [clbits[0], clbits[11], clbits[12]]) - inner23_true.h(qubits[8]).c_if(clbits[11], 0) + with self.assertWarns(DeprecationWarning): + inner23_true.h(qubits[8]).c_if(clbits[11], 0) inner23_false = QuantumCircuit(qubits[8:10], [clbits[0], clbits[11], clbits[12]]) - inner23_false.h(qubits[9]).c_if(clbits[12], 0) + with self.assertWarns(DeprecationWarning): + inner23_false.h(qubits[9]).c_if(clbits[12], 0) outer2_true = QuantumCircuit(loop_bits) - outer2_true.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[3]).c_if(clbits[6], 0) outer2_true.if_else(cond_inner, inner21_true, inner21_false, loop_qubits, loop_clbits) outer2_true.if_else(cond_inner, inner22_true, inner22_false, loop_qubits, loop_clbits) - outer2_true.h(qubits[6]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[6]).c_if(clbits[9], 0) outer2_false = QuantumCircuit(loop_bits) - outer2_false.h(qubits[7]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + outer2_false.h(qubits[7]).c_if(clbits[10], 0) outer2_false.if_else( cond_inner, inner23_true, @@ -1961,22 +2098,28 @@ def test_break_continue_deeply_nested(self, loop_operation): inner31_true = QuantumCircuit(loop_bits) loop_operation(inner31_true) inner31_false = QuantumCircuit(loop_bits) - inner31_false.h(qubits[12]).c_if(clbits[15], 0) + with self.assertWarns(DeprecationWarning): + inner31_false.h(qubits[12]).c_if(clbits[15], 0) inner32_true = QuantumCircuit(loop_bits) - inner32_true.h(qubits[13]).c_if(clbits[16], 0) + with self.assertWarns(DeprecationWarning): + inner32_true.h(qubits[13]).c_if(clbits[16], 0) inner32_false = QuantumCircuit(loop_bits) loop_operation(inner32_false) inner33_true = QuantumCircuit(qubits[14:16], [clbits[0], clbits[17], clbits[18]]) - inner33_true.h(qubits[14]).c_if(clbits[17], 0) + with self.assertWarns(DeprecationWarning): + inner33_true.h(qubits[14]).c_if(clbits[17], 0) inner33_false = QuantumCircuit(qubits[14:16], [clbits[0], clbits[17], clbits[18]]) - inner33_false.h(qubits[15]).c_if(clbits[18], 0) + with self.assertWarns(DeprecationWarning): + inner33_false.h(qubits[15]).c_if(clbits[18], 0) outer3_true = QuantumCircuit(loop_bits) - outer3_true.h(qubits[10]).c_if(clbits[13], 0) + with self.assertWarns(DeprecationWarning): + outer3_true.h(qubits[10]).c_if(clbits[13], 0) outer3_false = QuantumCircuit(loop_bits) - outer3_false.h(qubits[11]).c_if(clbits[14], 0) + with self.assertWarns(DeprecationWarning): + outer3_false.h(qubits[11]).c_if(clbits[14], 0) outer3_false.if_else(cond_inner, inner31_true, inner31_false, loop_qubits, loop_clbits) outer3_false.if_else(cond_inner, inner32_true, inner32_false, loop_qubits, loop_clbits) outer3_false.if_else( @@ -1988,7 +2131,8 @@ def test_break_continue_deeply_nested(self, loop_operation): ) loop_body = QuantumCircuit(loop_bits) - loop_body.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[3], 0) loop_body.if_else( cond_outer, outer1_true, @@ -1998,7 +2142,8 @@ def test_break_continue_deeply_nested(self, loop_operation): ) loop_body.if_else(cond_outer, outer2_true, outer2_false, loop_qubits, loop_clbits) loop_body.if_else(cond_outer, outer3_true, outer3_false, loop_qubits, loop_clbits) - loop_body.h(qubits[16]).c_if(clbits[19], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[16]).c_if(clbits[19], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop(cond_loop, loop_body, loop_qubits, loop_clbits) @@ -2008,52 +2153,68 @@ def test_break_continue_deeply_nested(self, loop_operation): with self.subTest("if/while/if/switch"): test = QuantumCircuit(qubits, clbits) with test.if_test(cond_outer): # outer_t - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) with test.while_loop(cond_loop): # loop - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) with test.if_test(cond_inner): # inner_t - test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) with test.switch(5) as case_: with case_(False): # case_f - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) with case_(True): # case_t loop_operation(test) - test.h(4).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(7, 0) # exit inner_t - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) # exit loop - test.h(6).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(9, 0) # exit outer_t - test.h(7).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(7).c_if(10, 0) case_f = QuantumCircuit(qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - case_f.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + case_f.h(qubits[3]).c_if(clbits[6], 0) case_t = QuantumCircuit(qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) loop_operation(case_t) inner_t = QuantumCircuit(qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - inner_t.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + inner_t.h(qubits[2]).c_if(clbits[5], 0) inner_t.switch( clbits[5], [(False, case_f), (True, case_t)], qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9], ) - inner_t.h(qubits[4]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + inner_t.h(qubits[4]).c_if(clbits[7], 0) loop = QuantumCircuit(qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - loop.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + loop.h(qubits[1]).c_if(clbits[4], 0) loop.if_test(cond_inner, inner_t, qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - loop.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + loop.h(qubits[5]).c_if(clbits[8], 0) outer_t = QuantumCircuit(qubits[:7], clbits[:10]) - outer_t.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + outer_t.h(qubits[0]).c_if(clbits[3], 0) outer_t.while_loop(cond_loop, loop, qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - outer_t.h(qubits[6]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer_t.h(qubits[6]).c_if(clbits[9], 0) expected = QuantumCircuit(qubits, clbits) expected.if_test(cond_outer, outer_t, qubits[:7], clbits[:10]) - expected.h(qubits[7]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + expected.h(qubits[7]).c_if(clbits[10], 0) self.assertEqual(canonicalize_control_flow(test), canonicalize_control_flow(expected)) @@ -2061,64 +2222,82 @@ def test_break_continue_deeply_nested(self, loop_operation): test = QuantumCircuit(qubits, clbits) with test.switch(0) as case_outer: with case_outer(False): # outer_case_f - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) with test.for_loop(range(2)): # loop - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) with test.switch(1) as case_inner: with case_inner(False): # inner_case_f - test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) with test.if_test((2, True)) as else_: # if_t - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) with else_: # if_f loop_operation(test) - test.h(4).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(7, 0) with case_inner(True): # inner_case_t loop_operation(test) - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) # exit loop1 - test.h(6).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(9, 0) with case_outer(True): # outer_case_t - test.h(7).c_if(10, 0) - test.h(8).c_if(11, 0) + with self.assertWarns(DeprecationWarning): + test.h(7).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(8).c_if(11, 0) if_t = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) - if_t.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + if_t.h(qubits[3]).c_if(clbits[6], 0) if_f = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) loop_operation(if_f) inner_case_f = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) - inner_case_f.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + inner_case_f.h(qubits[2]).c_if(clbits[5], 0) inner_case_f.if_else( (clbits[2], True), if_t, if_f, qubits[1:6], clbits[1:3] + clbits[4:9] ) - inner_case_f.h(qubits[4]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + inner_case_f.h(qubits[4]).c_if(clbits[7], 0) inner_case_t = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) loop_operation(inner_case_t) loop = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) - loop.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + loop.h(qubits[1]).c_if(clbits[4], 0) loop.switch( clbits[1], [(False, inner_case_f), (True, inner_case_t)], qubits[1:6], clbits[1:3] + clbits[4:9], ) - loop.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + loop.h(qubits[5]).c_if(clbits[8], 0) outer_case_f = QuantumCircuit(qubits[:8], clbits[:11]) - outer_case_f.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + outer_case_f.h(qubits[0]).c_if(clbits[3], 0) outer_case_f.for_loop(range(2), None, loop, qubits[1:6], clbits[1:3] + clbits[4:9]) - outer_case_f.h(qubits[6]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer_case_f.h(qubits[6]).c_if(clbits[9], 0) outer_case_t = QuantumCircuit(qubits[:8], clbits[:11]) - outer_case_t.h(qubits[7]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + outer_case_t.h(qubits[7]).c_if(clbits[10], 0) expected = QuantumCircuit(qubits, clbits) expected.switch( clbits[0], [(False, outer_case_f), (True, outer_case_t)], qubits[:8], clbits[:11] ) - expected.h(qubits[8]).c_if(clbits[11], 0) + with self.assertWarns(DeprecationWarning): + expected.h(qubits[8]).c_if(clbits[11], 0) self.assertEqual(canonicalize_control_flow(test), canonicalize_control_flow(expected)) @@ -2351,10 +2530,12 @@ def test_access_of_clbit_from_c_if(self): with self.subTest("if"): test = QuantumCircuit(bits) with test.if_test(cond): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.if_test(cond, body, [qubits[0]], clbits) @@ -2363,41 +2544,49 @@ def test_access_of_clbit_from_c_if(self): with test.if_test(cond) as else_: pass with else_: - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) true_body = QuantumCircuit([qubits[0]], clbits) false_body = QuantumCircuit([qubits[0]], clbits) - false_body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + false_body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.if_else(cond, true_body, false_body, [qubits[0]], clbits) with self.subTest("for"): test = QuantumCircuit(bits) with test.for_loop(range(2)): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.for_loop(range(2), None, body, [qubits[0]], clbits) with self.subTest("while"): test = QuantumCircuit(bits) with test.while_loop(cond): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.while_loop(cond, body, [qubits[0]], clbits) with self.subTest("switch"): test = QuantumCircuit(bits) with test.switch(cond[0]) as case, case(False): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.switch(cond[0], [(False, body)], [qubits[0]], clbits) @@ -2405,10 +2594,12 @@ def test_access_of_clbit_from_c_if(self): test = QuantumCircuit(bits) with test.for_loop(range(2)): with test.if_test(cond): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) true_body = QuantumCircuit([qubits[0]], clbits) - true_body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + true_body.h(qubits[0]).c_if(clbits[1], 0) body = QuantumCircuit([qubits[0]], clbits) body.if_test(cond, body, [qubits[0]], clbits) expected = QuantumCircuit(bits) @@ -2417,10 +2608,12 @@ def test_access_of_clbit_from_c_if(self): with self.subTest("switch inside for"): test = QuantumCircuit(bits) with test.for_loop(range(2)), test.switch(cond[0]) as case, case(False): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) body = QuantumCircuit([qubits[0]], clbits) body.switch(cond[0], [(False, body)], [qubits[0]], clbits) expected = QuantumCircuit(bits) @@ -2438,10 +2631,12 @@ def test_access_of_classicalregister_from_c_if(self): with self.subTest("if"): test = QuantumCircuit(qubits, clbits, creg) with test.if_test(cond): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) - body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.if_test(cond, body, [qubits[0]], all_clbits) @@ -2450,41 +2645,49 @@ def test_access_of_classicalregister_from_c_if(self): with test.if_test(cond) as else_: pass with else_: - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) true_body = QuantumCircuit([qubits[0]], clbits, creg) false_body = QuantumCircuit([qubits[0]], clbits, creg) - false_body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + false_body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.if_else(cond, true_body, false_body, [qubits[0]], all_clbits) with self.subTest("for"): test = QuantumCircuit(qubits, clbits, creg) with test.for_loop(range(2)): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits, creg) - body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.for_loop(range(2), None, body, [qubits[0]], all_clbits) with self.subTest("while"): test = QuantumCircuit(qubits, clbits, creg) with test.while_loop(cond): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) - body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.while_loop(cond, body, [qubits[0]], all_clbits) with self.subTest("switch"): test = QuantumCircuit(qubits, clbits, creg) with test.switch(cond[0]) as case, case(False): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) - body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.switch(cond[0], [(False, body)], [qubits[0]], all_clbits) @@ -2492,10 +2695,12 @@ def test_access_of_classicalregister_from_c_if(self): test = QuantumCircuit(qubits, clbits, creg) with test.for_loop(range(2)): with test.if_test(cond): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) true_body = QuantumCircuit([qubits[0]], clbits, creg) - true_body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + true_body.h(qubits[0]).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) body.if_test(cond, body, [qubits[0]], all_clbits) expected = QuantumCircuit(qubits, clbits, creg) @@ -2505,10 +2710,12 @@ def test_access_of_classicalregister_from_c_if(self): test = QuantumCircuit(qubits, clbits, creg) with test.for_loop(range(2)): with test.switch(cond[0]) as case, case(False): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) case = QuantumCircuit([qubits[0]], clbits, creg) - case.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + case.h(qubits[0]).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) body.switch(cond[0], [(False, case)], [qubits[0]], all_clbits) expected = QuantumCircuit(qubits, clbits, creg) @@ -3424,7 +3631,8 @@ def test_if_placeholder_rejects_c_if(self): NotImplementedError, r"IfElseOp cannot be classically controlled through Instruction\.c_if", ): - placeholder.c_if(bits[1], 0) + with self.assertWarns(DeprecationWarning): + placeholder.c_if(bits[1], 0) with self.subTest("else"): test = QuantumCircuit(bits) @@ -3441,7 +3649,8 @@ def test_if_placeholder_rejects_c_if(self): NotImplementedError, r"IfElseOp cannot be classically controlled through Instruction\.c_if", ): - placeholder.c_if(bits[1], 0) + with self.assertWarns(DeprecationWarning): + placeholder.c_if(bits[1], 0) def test_switch_rejects_operations_outside_cases(self): """It shouldn't be permissible to try and put instructions inside a switch but outside a @@ -3543,7 +3752,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("else"): test = QuantumCircuit(bits) @@ -3554,7 +3764,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("for"): test = QuantumCircuit(bits) @@ -3563,7 +3774,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("while"): test = QuantumCircuit(bits) @@ -3572,7 +3784,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("switch"): test = QuantumCircuit(bits) @@ -3581,7 +3794,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("if inside for"): # As a side-effect of how the lazy building of 'if' statements works, we actually @@ -3595,7 +3809,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("switch inside for"): # `switch` has the same lazy building as `if`, so is subject to the same considerations @@ -3608,7 +3823,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) def test_raising_inside_context_manager_leave_circuit_usable(self): """Test that if we leave a builder by raising some sort of exception, the circuit is left in diff --git a/test/python/circuit/test_extensions_standard.py b/test/python/circuit/test_extensions_standard.py index 73e00fa84c90..d9bdbc3fd13f 100644 --- a/test/python/circuit/test_extensions_standard.py +++ b/test/python/circuit/test_extensions_standard.py @@ -76,7 +76,8 @@ def test_barrier_invalid(self): def test_conditional_barrier_invalid(self): qc = self.circuit barrier = qc.barrier(self.qr) - self.assertRaises(QiskitError, barrier.c_if, self.cr, 0) + with self.assertWarns(DeprecationWarning): + self.assertRaises(QiskitError, barrier.c_if, self.cr, 0) def test_barrier_reg(self): self.circuit.barrier(self.qr) @@ -131,16 +132,20 @@ def test_ch_invalid(self): self.assertRaises(CircuitError, qc.ch, "a", self.qr[1]) def test_cif_reg(self): - self.circuit.h(self.qr[0]).c_if(self.cr, 7) + with self.assertWarns(DeprecationWarning): + self.circuit.h(self.qr[0]).c_if(self.cr, 7) self.assertEqual(self.circuit[0].operation.name, "h") self.assertEqual(self.circuit[0].qubits, (self.qr[0],)) - self.assertEqual(self.circuit[0].operation.condition, (self.cr, 7)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.circuit[0].operation.condition, (self.cr, 7)) def test_cif_single_bit(self): - self.circuit.h(self.qr[0]).c_if(self.cr[0], True) + with self.assertWarns(DeprecationWarning): + self.circuit.h(self.qr[0]).c_if(self.cr[0], True) self.assertEqual(self.circuit[0].operation.name, "h") self.assertEqual(self.circuit[0].qubits, (self.qr[0],)) - self.assertEqual(self.circuit[0].operation.condition, (self.cr[0], True)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.circuit[0].operation.condition, (self.cr[0], True)) def test_crz(self): self.circuit.crz(1, self.qr[0], self.qr[1]) diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py index 220478159ee3..ba2546b2e8a5 100644 --- a/test/python/circuit/test_gate_definitions.py +++ b/test/python/circuit/test_gate_definitions.py @@ -311,6 +311,10 @@ class TestGateEquivalenceEqual(QiskitTestCase): "_SingletonGateOverrides", "_SingletonControlledGateOverrides", "QFTGate", + "AndGate", + "OrGate", + "BitwiseXorGate", + "InnerProductGate", } # Amazingly, Python's scoping rules for class bodies means that this is the closest we can get diff --git a/test/python/circuit/test_instruction_repeat.py b/test/python/circuit/test_instruction_repeat.py index 778e6aad64c2..b2fcda1522e6 100644 --- a/test/python/circuit/test_instruction_repeat.py +++ b/test/python/circuit/test_instruction_repeat.py @@ -55,8 +55,10 @@ def test_standard_1Q_one(self): def test_conditional(self): """Test that repetition works with a condition.""" cr = ClassicalRegister(3, "cr") - gate = SGate().c_if(cr, 7).repeat(5) - self.assertEqual(gate.condition, (cr, 7)) + with self.assertWarns(DeprecationWarning): + gate = SGate().c_if(cr, 7).repeat(5) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate.condition, (cr, 7)) defn = QuantumCircuit(1) for _ in range(5): @@ -98,8 +100,10 @@ def test_standard_2Q_one(self): def test_conditional(self): """Test that repetition works with a condition.""" cr = ClassicalRegister(3, "cr") - gate = CXGate().c_if(cr, 7).repeat(5) - self.assertEqual(gate.condition, (cr, 7)) + with self.assertWarns(DeprecationWarning): + gate = CXGate().c_if(cr, 7).repeat(5) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate.condition, (cr, 7)) defn = QuantumCircuit(2) for _ in range(5): @@ -145,8 +149,10 @@ def test_measure_one(self): def test_measure_conditional(self): """Test conditional measure moves condition to the outside.""" cr = ClassicalRegister(3, "cr") - measure = Measure().c_if(cr, 7).repeat(5) - self.assertEqual(measure.condition, (cr, 7)) + with self.assertWarns(DeprecationWarning): + measure = Measure().c_if(cr, 7).repeat(5) + with self.assertWarns(DeprecationWarning): + self.assertEqual(measure.condition, (cr, 7)) defn = QuantumCircuit(1, 1) for _ in range(5): diff --git a/test/python/circuit/test_instructions.py b/test/python/circuit/test_instructions.py index 170b47632c4d..da1d0797870e 100644 --- a/test/python/circuit/test_instructions.py +++ b/test/python/circuit/test_instructions.py @@ -176,7 +176,8 @@ def circuit_instruction_circuit_roundtrip(self): circ1.u(0.1, 0.2, -0.2, q[0]) circ1.barrier() circ1.measure(q, c) - circ1.rz(0.8, q[0]).c_if(c, 6) + with self.assertWarns(DeprecationWarning): + circ1.rz(0.8, q[0]).c_if(c, 6) inst = circ1.to_instruction() circ2 = QuantumCircuit(q, c, name="circ2") @@ -238,16 +239,20 @@ def test_reverse_instruction(self): circ.u(0.1, 0.2, -0.2, q[0]) circ.barrier() circ.measure(q[0], c[0]) - circ.rz(0.8, q[0]).c_if(c, 6) - inst = circ.to_instruction() + with self.assertWarns(DeprecationWarning): + circ.rz(0.8, q[0]).c_if(c, 6) + with self.assertWarns(DeprecationWarning): + inst = circ.to_instruction() circ = QuantumCircuit(q, c, name="circ") - circ.rz(0.8, q[0]).c_if(c, 6) + with self.assertWarns(DeprecationWarning): + circ.rz(0.8, q[0]).c_if(c, 6) circ.measure(q[0], c[0]) circ.barrier() circ.u(0.1, 0.2, -0.2, q[0]) circ.t(q[1]) - inst_reverse = circ.to_instruction() + with self.assertWarns(DeprecationWarning): + inst_reverse = circ.to_instruction() self.assertEqual(inst.reverse_ops().definition, inst_reverse.definition) @@ -336,8 +341,10 @@ def test_inverse_instruction_with_conditional(self): circ.u(0.1, 0.2, -0.2, q[0]) circ.barrier() circ.measure(q[0], c[0]) - circ.rz(0.8, q[0]).c_if(c, 6) - inst = circ.to_instruction() + with self.assertWarns(DeprecationWarning): + circ.rz(0.8, q[0]).c_if(c, 6) + with self.assertWarns(DeprecationWarning): + inst = circ.to_instruction() self.assertRaises(CircuitError, inst.inverse) def test_inverse_opaque(self): @@ -446,13 +453,18 @@ def key(bit): return body.find_bit(bit).index op = IfElseOp((bits[0], False), body) - self.assertEqual(op.condition_bits, [bits[0]]) + with self.assertWarns(DeprecationWarning): + self.assertEqual(op.condition_bits, [bits[0]]) op = IfElseOp((cr1, 3), body) - self.assertEqual(op.condition_bits, list(cr1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(op.condition_bits, list(cr1)) op = IfElseOp(expr.logic_and(bits[1], expr.equal(cr2, 3)), body) - self.assertEqual(sorted(op.condition_bits, key=key), sorted([bits[1]] + list(cr2), key=key)) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + sorted(op.condition_bits, key=key), sorted([bits[1]] + list(cr2), key=key) + ) def test_instructionset_c_if_direct_resource(self): """Test that using :meth:`.InstructionSet.c_if` with an exact classical resource always @@ -467,8 +479,10 @@ def test_instructionset_c_if_direct_resource(self): def case(resource): qc = QuantumCircuit(cr1, qubits, loose_clbits, cr2, cr3) - qc.x(0).c_if(resource, 0) - c_if_resource = qc.data[0].operation.condition[0] + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(resource, 0) + with self.assertWarns(DeprecationWarning): + c_if_resource = qc.data[0].operation.condition[0] self.assertIs(c_if_resource, resource) with self.subTest("classical register"): @@ -500,9 +514,11 @@ def test_instructionset_c_if_indexing(self): qc = QuantumCircuit(cr1, qubits, loose_clbits, cr2, cr3) for index, clbit in enumerate(qc.clbits): with self.subTest(index=index): - qc.x(0).c_if(index, 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(index, 0) qc.measure(0, index) - from_c_if = qc.data[-2].operation.condition[0] + with self.assertWarns(DeprecationWarning): + from_c_if = qc.data[-2].operation.condition[0] from_measure = qc.data[-1].clbits[0] self.assertIs(from_c_if, from_measure) # Sanity check that the bit is also the one we expected. @@ -516,14 +532,20 @@ def test_instructionset_c_if_size_1_classical_register(self): qc = QuantumCircuit(qr, cr) with self.subTest("classical register"): - qc.x(0).c_if(cr, 0) - self.assertIs(qc.data[-1].operation.condition[0], cr) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], cr) with self.subTest("classical bit by value"): - qc.x(0).c_if(cr[0], 0) - self.assertIs(qc.data[-1].operation.condition[0], cr[0]) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(cr[0], 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], cr[0]) with self.subTest("classical bit by index"): - qc.x(0).c_if(0, 0) - self.assertIs(qc.data[-1].operation.condition[0], cr[0]) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], cr[0]) def test_instructionset_c_if_no_classical_registers(self): """Test that using :meth:`.InstructionSet.c_if` works if there are no classical registers @@ -533,11 +555,15 @@ def test_instructionset_c_if_no_classical_registers(self): bits = [Qubit(), Clbit()] qc = QuantumCircuit(bits) with self.subTest("by value"): - qc.x(0).c_if(bits[1], 0) - self.assertIs(qc.data[-1].operation.condition[0], bits[1]) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(bits[1], 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], bits[1]) with self.subTest("by index"): - qc.x(0).c_if(0, 0) - self.assertIs(qc.data[-1].operation.condition[0], bits[1]) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], bits[1]) def test_instructionset_c_if_rejects_invalid_specifiers(self): """Test that calling the :meth:`.InstructionSet.c_if` method on instructions added to a @@ -550,7 +576,8 @@ def case(specifier, message): qc = QuantumCircuit(qreg, creg) instruction = qc.x(0) with self.assertRaisesRegex(CircuitError, message): - instruction.c_if(specifier, 0) + with self.assertWarns(DeprecationWarning): + instruction.c_if(specifier, 0) with self.subTest("absent bit"): case(Clbit(), r"Clbit .* is not present in this circuit\.") @@ -574,21 +601,26 @@ def test_instructionset_c_if_with_no_requester(self): instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) register = ClassicalRegister(2) - instructions.c_if(register, 0) - self.assertIs(instructions[0].operation.condition[0], register) + with self.assertWarns(DeprecationWarning): + instructions.c_if(register, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], register) with self.subTest("accepts arbitrary bit"): instruction = RZGate(0) instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) bit = Clbit() - instructions.c_if(bit, 0) - self.assertIs(instructions[0].operation.condition[0], bit) + with self.assertWarns(DeprecationWarning): + instructions.c_if(bit, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], bit) with self.subTest("rejects index"): instruction = RZGate(0) instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) with self.assertRaisesRegex(CircuitError, r"Cannot pass an index as a condition .*"): - instructions.c_if(0, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(0, 0) def test_instructionset_c_if_calls_custom_requester(self): """Test that :meth:`.InstructionSet.c_if` calls a custom requester, and uses its output.""" @@ -613,27 +645,33 @@ def dummy_requester(specifier): instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) bit = Clbit() - instructions.c_if(bit, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(bit, 0) dummy_requester.assert_called_once_with(bit) - self.assertIs(instructions[0].operation.condition[0], sentinel_bit) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], sentinel_bit) with self.subTest("calls requester with index"): dummy_requester.reset_mock() instruction = RZGate(0) instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) index = 0 - instructions.c_if(index, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(index, 0) dummy_requester.assert_called_once_with(index) - self.assertIs(instructions[0].operation.condition[0], sentinel_bit) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], sentinel_bit) with self.subTest("calls requester with register"): dummy_requester.reset_mock() instruction = RZGate(0) instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) register = ClassicalRegister(2) - instructions.c_if(register, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(register, 0) dummy_requester.assert_called_once_with(register) - self.assertIs(instructions[0].operation.condition[0], sentinel_register) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], sentinel_register) with self.subTest("calls requester only once when broadcast"): dummy_requester.reset_mock() instruction_list = [RZGate(0), RZGate(0), RZGate(0)] @@ -641,10 +679,12 @@ def dummy_requester(specifier): for instruction in instruction_list: instructions.add(instruction, [Qubit()], []) register = ClassicalRegister(2) - instructions.c_if(register, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(register, 0) dummy_requester.assert_called_once_with(register) for instruction in instruction_list: - self.assertIs(instructions[0].operation.condition[0], sentinel_register) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], sentinel_register) def test_label_type_enforcement(self): """Test instruction label type enforcement.""" diff --git a/test/python/circuit/test_random_circuit.py b/test/python/circuit/test_random_circuit.py index ebbdfd28d648..0845fdbe97d6 100644 --- a/test/python/circuit/test_random_circuit.py +++ b/test/python/circuit/test_random_circuit.py @@ -49,18 +49,20 @@ def test_random_circuit_conditional_reset(self): """Test generating random circuits with conditional and reset.""" num_qubits = 1 depth = 100 - circ = random_circuit(num_qubits, depth, conditional=True, reset=True, seed=5) + with self.assertWarns(DeprecationWarning): + circ = random_circuit(num_qubits, depth, conditional=True, reset=True, seed=5) self.assertEqual(circ.width(), 2 * num_qubits) self.assertIn("reset", circ.count_ops()) def test_large_conditional(self): """Test that conditions do not fail with large conditionals. Regression test of gh-6994.""" # The main test is that this call actually returns without raising an exception. - circ = random_circuit(64, 2, conditional=True, seed=0) + with self.assertWarns(DeprecationWarning): + circ = random_circuit(64, 2, conditional=True, seed=0) # Test that at least one instruction had a condition generated. It's possible that this # fails due to very bad luck with the random seed - if so, change the seed to ensure that a # condition _is_ generated, because we need to test that generation doesn't error. - conditions = (getattr(instruction.operation, "condition", None) for instruction in circ) + conditions = (getattr(instruction.operation, "_condition", None) for instruction in circ) conditions = [x for x in conditions if x is not None] self.assertNotEqual(conditions, []) for register, value in conditions: @@ -72,14 +74,15 @@ def test_large_conditional(self): def test_random_mid_circuit_measure_conditional(self): """Test random circuit with mid-circuit measurements for conditionals.""" num_qubits = depth = 2 - circ = random_circuit(num_qubits, depth, conditional=True, seed=16) + with self.assertWarns(DeprecationWarning): + circ = random_circuit(num_qubits, depth, conditional=True, seed=16) self.assertEqual(circ.width(), 2 * num_qubits) op_names = [instruction.operation.name for instruction in circ] # Before a condition, there needs to be measurement in all the qubits. self.assertEqual(4, len(op_names)) self.assertEqual(["measure"] * num_qubits, op_names[1 : 1 + num_qubits]) conditions = [ - bool(getattr(instruction.operation, "condition", None)) for instruction in circ + bool(getattr(instruction.operation, "_condition", None)) for instruction in circ ] self.assertEqual([False, False, False, True], conditions) diff --git a/test/python/circuit/test_registerless_circuit.py b/test/python/circuit/test_registerless_circuit.py index 3f77919957da..c3da073e5234 100644 --- a/test/python/circuit/test_registerless_circuit.py +++ b/test/python/circuit/test_registerless_circuit.py @@ -195,10 +195,12 @@ def test_circuit_conditional(self): qreg = QuantumRegister(2) creg = ClassicalRegister(4) circuit = QuantumCircuit(qreg, creg) - circuit.h(0).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(creg, 3) expected = QuantumCircuit(qreg, creg) - expected.h(qreg[0]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg[0]).c_if(creg, 3) self.assertEqual(circuit, expected) @@ -333,11 +335,14 @@ def test_circuit_conditional(self): qreg1 = QuantumRegister(2) creg = ClassicalRegister(2) circuit = QuantumCircuit(qreg0, qreg1, creg) - circuit.h(range(1, 3)).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit.h(range(1, 3)).c_if(creg, 3) expected = QuantumCircuit(qreg0, qreg1, creg) - expected.h(qreg0[1]).c_if(creg, 3) - expected.h(qreg1[0]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg0[1]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg1[0]).c_if(creg, 3) self.assertEqual(circuit, expected) @@ -466,11 +471,14 @@ def test_circuit_conditional(self): qreg1 = QuantumRegister(2) creg = ClassicalRegister(2) circuit = QuantumCircuit(qreg0, qreg1, creg) - circuit.h(slice(1, 3)).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit.h(slice(1, 3)).c_if(creg, 3) expected = QuantumCircuit(qreg0, qreg1, creg) - expected.h(qreg0[1]).c_if(creg, 3) - expected.h(qreg1[0]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg0[1]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg1[0]).c_if(creg, 3) self.assertEqual(circuit, expected) @@ -504,10 +512,12 @@ def test_bit_conditional_single_gate(self): qreg = QuantumRegister(1) creg = ClassicalRegister(2) circuit = QuantumCircuit(qreg, creg) - circuit.h(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(0, True) expected = QuantumCircuit(qreg, creg) - expected.h(qreg[0]).c_if(creg[0], True) + with self.assertWarns(DeprecationWarning): + expected.h(qreg[0]).c_if(creg[0], True) self.assertEqual(circuit, expected) def test_bit_conditional_multiple_gates(self): @@ -516,12 +526,18 @@ def test_bit_conditional_multiple_gates(self): creg = ClassicalRegister(2) creg1 = ClassicalRegister(1) circuit = QuantumCircuit(qreg, creg, creg1) - circuit.h(0).c_if(0, True) - circuit.h(1).c_if(1, False) - circuit.cx(1, 0).c_if(2, True) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + circuit.h(1).c_if(1, False) + with self.assertWarns(DeprecationWarning): + circuit.cx(1, 0).c_if(2, True) expected = QuantumCircuit(qreg, creg, creg1) - expected.h(qreg[0]).c_if(creg[0], True) - expected.h(qreg[1]).c_if(creg[1], False) - expected.cx(qreg[1], qreg[0]).c_if(creg1[0], True) + with self.assertWarns(DeprecationWarning): + expected.h(qreg[0]).c_if(creg[0], True) + with self.assertWarns(DeprecationWarning): + expected.h(qreg[1]).c_if(creg[1], False) + with self.assertWarns(DeprecationWarning): + expected.cx(qreg[1], qreg[0]).c_if(creg1[0], True) self.assertEqual(circuit, expected) diff --git a/test/python/circuit/test_scheduled_circuit.py b/test/python/circuit/test_scheduled_circuit.py index 6348d31487bd..7ebed694a8db 100644 --- a/test/python/circuit/test_scheduled_circuit.py +++ b/test/python/circuit/test_scheduled_circuit.py @@ -22,6 +22,7 @@ from qiskit.providers.fake_provider import Fake27QPulseV1, GenericBackendV2 from qiskit.providers.basic_provider import BasicSimulator from qiskit.scheduler import ScheduleConfig +from qiskit.transpiler import InstructionProperties from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.instruction_durations import InstructionDurations from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -194,12 +195,16 @@ def test_transpile_delay_circuit_without_backend(self): qc.h(0) qc.delay(500, 1) qc.cx(0, 1) - scheduled = transpile( - qc, - scheduling_method="alap", - basis_gates=["h", "cx"], - instruction_durations=[("h", 0, 200), ("cx", [0, 1], 700)], - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + scheduled = transpile( + qc, + scheduling_method="alap", + basis_gates=["h", "cx"], + instruction_durations=[("h", 0, 200), ("cx", [0, 1], 700)], + ) self.assertEqual(scheduled.duration, 1200) def test_transpile_circuit_with_custom_instruction(self): @@ -210,9 +215,13 @@ def test_transpile_circuit_with_custom_instruction(self): qc = QuantumCircuit(2) qc.delay(500, 1) qc.append(bell.to_instruction(), [0, 1]) - scheduled = transpile( - qc, scheduling_method="alap", instruction_durations=[("bell", [0, 1], 1000)] - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + scheduled = transpile( + qc, scheduling_method="alap", instruction_durations=[("bell", [0, 1], 1000)] + ) self.assertEqual(scheduled.duration, 1500) def test_transpile_delay_circuit_with_dt_but_without_scheduling_method(self): @@ -263,21 +272,29 @@ def test_default_units_for_my_own_duration_users(self): qc.h(0) qc.delay(500, 1) qc.cx(0, 1) - # accept None for qubits - scheduled = transpile( - qc, - basis_gates=["h", "cx", "delay"], - scheduling_method="alap", - instruction_durations=[("h", 0, 200), ("cx", None, 900)], - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + # accept None for qubits + scheduled = transpile( + qc, + basis_gates=["h", "cx", "delay"], + scheduling_method="alap", + instruction_durations=[("h", 0, 200), ("cx", None, 900)], + ) self.assertEqual(scheduled.duration, 1400) - # prioritize specified qubits over None - scheduled = transpile( - qc, - basis_gates=["h", "cx", "delay"], - scheduling_method="alap", - instruction_durations=[("h", 0, 200), ("cx", None, 900), ("cx", [0, 1], 800)], - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + # prioritize specified qubits over None + scheduled = transpile( + qc, + basis_gates=["h", "cx", "delay"], + scheduling_method="alap", + instruction_durations=[("h", 0, 200), ("cx", None, 900), ("cx", [0, 1], 800)], + ) self.assertEqual(scheduled.duration, 1300) def test_unit_seconds_when_using_backend_durations(self): @@ -313,19 +330,23 @@ def test_unit_seconds_when_using_backend_durations(self): ) self.assertEqual(scheduled.duration, 1500) - def test_per_qubit_durations(self): - """See: https://github.com/Qiskit/qiskit-terra/issues/5109""" + def test_per_qubit_durations_loose_constrain(self): + """See Qiskit/5109 and Qiskit/13306""" qc = QuantumCircuit(3) qc.h(0) qc.delay(500, 1) qc.cx(0, 1) qc.h(1) - sc = transpile( - qc, - scheduling_method="alap", - basis_gates=["h", "cx"], - instruction_durations=[("h", None, 200), ("cx", [0, 1], 700)], - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + sc = transpile( + qc, + scheduling_method="alap", + basis_gates=["h", "cx"], + instruction_durations=[("h", None, 200), ("cx", [0, 1], 700)], + ) self.assertEqual(sc.qubit_start_time(0), 300) self.assertEqual(sc.qubit_stop_time(0), 1200) self.assertEqual(sc.qubit_start_time(1), 500) @@ -336,12 +357,21 @@ def test_per_qubit_durations(self): self.assertEqual(sc.qubit_stop_time(0, 1), 1400) qc.measure_all() - sc = transpile( - qc, - scheduling_method="alap", - basis_gates=["h", "cx", "measure"], - instruction_durations=[("h", None, 200), ("cx", [0, 1], 700), ("measure", None, 1000)], - ) + + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + sc = transpile( + qc, + scheduling_method="alap", + basis_gates=["h", "cx", "measure"], + instruction_durations=[ + ("h", None, 200), + ("cx", [0, 1], 700), + ("measure", None, 1000), + ], + ) q = sc.qubits self.assertEqual(sc.qubit_start_time(q[0]), 300) self.assertEqual(sc.qubit_stop_time(q[0]), 2400) @@ -352,6 +382,57 @@ def test_per_qubit_durations(self): self.assertEqual(sc.qubit_start_time(*q), 300) self.assertEqual(sc.qubit_stop_time(*q), 2400) + def test_per_qubit_durations(self): + """Test target with custom instruction_durations""" + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="argument ``calibrate_instructions`` is deprecated", + ): + target = GenericBackendV2( + 3, + calibrate_instructions=True, + coupling_map=[[0, 1], [1, 2]], + basis_gates=["cx", "h"], + seed=42, + ).target + target.update_instruction_properties("cx", (0, 1), InstructionProperties(0.00001)) + target.update_instruction_properties("cx", (1, 2), InstructionProperties(0.00001)) + target.update_instruction_properties("h", (0,), InstructionProperties(0.000002)) + target.update_instruction_properties("h", (1,), InstructionProperties(0.000002)) + target.update_instruction_properties("h", (2,), InstructionProperties(0.000002)) + + qc = QuantumCircuit(3) + qc.h(0) + qc.delay(500, 1) + qc.cx(0, 1) + qc.h(1) + + sc = transpile(qc, scheduling_method="alap", target=target) + self.assertEqual(sc.qubit_start_time(0), 500) + self.assertEqual(sc.qubit_stop_time(0), 54554) + self.assertEqual(sc.qubit_start_time(1), 9509) + self.assertEqual(sc.qubit_stop_time(1), 63563) + self.assertEqual(sc.qubit_start_time(2), 0) + self.assertEqual(sc.qubit_stop_time(2), 0) + self.assertEqual(sc.qubit_start_time(0, 1), 500) + self.assertEqual(sc.qubit_stop_time(0, 1), 63563) + + qc.measure_all() + + target.update_instruction_properties("measure", (0,), InstructionProperties(0.0001)) + target.update_instruction_properties("measure", (1,), InstructionProperties(0.0001)) + + sc = transpile(qc, scheduling_method="alap", target=target) + q = sc.qubits + self.assertEqual(sc.qubit_start_time(q[0]), 500) + self.assertEqual(sc.qubit_stop_time(q[0]), 514013) + self.assertEqual(sc.qubit_start_time(q[1]), 9509) + self.assertEqual(sc.qubit_stop_time(q[1]), 514013) + self.assertEqual(sc.qubit_start_time(q[2]), 63563) + self.assertEqual(sc.qubit_stop_time(q[2]), 514013) + self.assertEqual(sc.qubit_start_time(*q), 500) + self.assertEqual(sc.qubit_stop_time(*q), 514013) + def test_convert_duration_to_dt(self): """Test that circuit duration unit conversion is applied only when necessary. Tests fix for bug reported in PR #11782.""" diff --git a/test/python/circuit/test_singleton.py b/test/python/circuit/test_singleton.py index 40549dd0ad15..0274242eec8e 100644 --- a/test/python/circuit/test_singleton.py +++ b/test/python/circuit/test_singleton.py @@ -63,7 +63,8 @@ def test_label_not_singleton(self): def test_condition_not_singleton(self): gate = HGate() - condition_gate = HGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + condition_gate = HGate().c_if(Clbit(), 0) self.assertIsNot(gate, condition_gate) def test_raise_on_state_mutation(self): @@ -76,10 +77,12 @@ def test_raise_on_state_mutation(self): def test_labeled_condition(self): singleton_gate = HGate() clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = HGate(label="conditionally special").c_if(clbit, 0) self.assertIsNot(singleton_gate, gate) self.assertEqual(gate.label, "conditionally special") - self.assertEqual(gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate.condition, (clbit, 0)) def test_default_singleton_copy(self): gate = HGate() @@ -109,19 +112,22 @@ def test_label_copy_new(self): self.assertEqual(copied_label.label, "special") def test_condition_copy(self): - gate = HGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + gate = HGate().c_if(Clbit(), 0) copied = gate.copy() self.assertIsNot(gate, copied) self.assertEqual(gate, copied) def test_condition_label_copy(self): clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = HGate(label="conditionally special").c_if(clbit, 0) copied = gate.copy() self.assertIsNot(gate, copied) self.assertEqual(gate, copied) self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied.condition, (clbit, 0)) def test_deepcopy(self): gate = HGate() @@ -136,19 +142,22 @@ def test_deepcopy_with_label(self): self.assertEqual(copied.label, "special") def test_deepcopy_with_condition(self): - gate = HGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + gate = HGate().c_if(Clbit(), 0) copied = copy.deepcopy(gate) self.assertIsNot(gate, copied) self.assertEqual(gate, copied) def test_condition_label_deepcopy(self): clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = HGate(label="conditionally special").c_if(clbit, 0) copied = copy.deepcopy(gate) self.assertIsNot(gate, copied) self.assertEqual(gate, copied) self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied.condition, (clbit, 0)) def test_label_deepcopy_new(self): gate = HGate() @@ -193,23 +202,27 @@ def test_round_trip_dag_conversion_with_label(self): def test_round_trip_dag_conversion_with_condition(self): qc = QuantumCircuit(1, 1) - gate = HGate().c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + gate = HGate().c_if(qc.cregs[0], 0) qc.append(gate, [0]) dag = circuit_to_dag(qc) out = dag_to_circuit(dag) self.assertIsNot(qc.data[0].operation, out.data[0].operation) self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) def test_round_trip_dag_conversion_condition_label(self): qc = QuantumCircuit(1, 1) - gate = HGate(label="conditionally special").c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + gate = HGate(label="conditionally special").c_if(qc.cregs[0], 0) qc.append(gate, [0]) dag = circuit_to_dag(qc) out = dag_to_circuit(dag) self.assertIsNot(qc.data[0].operation, out.data[0].operation) self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) self.assertEqual(out.data[0].operation.label, "conditionally special") def test_condition_via_instructionset(self): @@ -217,9 +230,11 @@ def test_condition_via_instructionset(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 1) self.assertIsNot(gate, circuit.data[0].operation) - self.assertEqual(circuit.data[0].operation.condition, (cr, 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(circuit.data[0].operation.condition, (cr, 1)) def test_is_mutable(self): gate = HGate() @@ -249,7 +264,8 @@ def test_to_mutable_setter(self): self.assertEqual(mutable_gate.label, "foo") self.assertEqual(mutable_gate.duration, 3) self.assertEqual(mutable_gate.unit, "s") - self.assertEqual(mutable_gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(mutable_gate.condition, (clbit, 0)) def test_to_mutable_of_mutable_instance(self): gate = HGate(label="foo") @@ -287,9 +303,11 @@ def test_immutable_pickle(self): def test_mutable_pickle(self): gate = SXGate() clbit = Clbit() - condition_gate = gate.c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + condition_gate = gate.c_if(clbit, 0) self.assertIsNot(gate, condition_gate) - self.assertEqual(condition_gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(condition_gate.condition, (clbit, 0)) self.assertTrue(condition_gate.mutable) with io.BytesIO() as fd: pickle.dump(condition_gate, fd) @@ -505,7 +523,8 @@ def test_label_not_singleton(self): def test_condition_not_singleton(self): gate = CZGate() - condition_gate = CZGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + condition_gate = CZGate().c_if(Clbit(), 0) self.assertIsNot(gate, condition_gate) def test_raise_on_state_mutation(self): @@ -518,10 +537,12 @@ def test_raise_on_state_mutation(self): def test_labeled_condition(self): singleton_gate = CSwapGate() clbit = Clbit() - gate = CSwapGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = CSwapGate(label="conditionally special").c_if(clbit, 0) self.assertIsNot(singleton_gate, gate) self.assertEqual(gate.label, "conditionally special") - self.assertEqual(gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate.condition, (clbit, 0)) def test_default_singleton_copy(self): gate = CXGate() @@ -551,19 +572,22 @@ def test_label_copy_new(self): self.assertEqual(copied_label.label, "special") def test_condition_copy(self): - gate = CZGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + gate = CZGate().c_if(Clbit(), 0) copied = gate.copy() self.assertIsNot(gate, copied) self.assertEqual(gate, copied) def test_condition_label_copy(self): clbit = Clbit() - gate = CZGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = CZGate(label="conditionally special").c_if(clbit, 0) copied = gate.copy() self.assertIsNot(gate, copied) self.assertEqual(gate, copied) self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied.condition, (clbit, 0)) def test_deepcopy(self): gate = CXGate() @@ -583,19 +607,22 @@ def test_deepcopy_with_label(self): self.assertNotEqual(singleton_gate.label, copied.label) def test_deepcopy_with_condition(self): - gate = CCXGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + gate = CCXGate().c_if(Clbit(), 0) copied = copy.deepcopy(gate) self.assertIsNot(gate, copied) self.assertEqual(gate, copied) def test_condition_label_deepcopy(self): clbit = Clbit() - gate = CHGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = CHGate(label="conditionally special").c_if(clbit, 0) copied = copy.deepcopy(gate) self.assertIsNot(gate, copied) self.assertEqual(gate, copied) self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied.condition, (clbit, 0)) def test_label_deepcopy_new(self): gate = CHGate() @@ -640,23 +667,27 @@ def test_round_trip_dag_conversion_with_label(self): def test_round_trip_dag_conversion_with_condition(self): qc = QuantumCircuit(2, 1) - gate = CHGate().c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + gate = CHGate().c_if(qc.cregs[0], 0) qc.append(gate, [0, 1]) dag = circuit_to_dag(qc) out = dag_to_circuit(dag) self.assertIsNot(qc.data[0].operation, out.data[0].operation) self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) def test_round_trip_dag_conversion_condition_label(self): qc = QuantumCircuit(2, 1) - gate = CHGate(label="conditionally special").c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + gate = CHGate(label="conditionally special").c_if(qc.cregs[0], 0) qc.append(gate, [0, 1]) dag = circuit_to_dag(qc) out = dag_to_circuit(dag) self.assertIsNot(qc.data[0].operation, out.data[0].operation) self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) self.assertEqual(out.data[0].operation.label, "conditionally special") def test_condition_via_instructionset(self): @@ -664,9 +695,10 @@ def test_condition_via_instructionset(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 1) self.assertIsNot(gate, circuit.data[0].operation) - self.assertEqual(circuit.data[0].operation.condition, (cr, 1)) + self.assertEqual(circuit.data[0].operation._condition, (cr, 1)) def test_is_mutable(self): gate = CXGate() @@ -696,7 +728,8 @@ def test_to_mutable_setter(self): self.assertEqual(mutable_gate.label, "foo") self.assertEqual(mutable_gate.duration, 3) self.assertEqual(mutable_gate.unit, "s") - self.assertEqual(mutable_gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(mutable_gate.condition, (clbit, 0)) def test_to_mutable_of_mutable_instance(self): gate = CZGate(label="foo") @@ -723,27 +756,32 @@ def test_inner_outer_label_with_c_if(self): inner_gate = HGate(label="my h gate") controlled_gate = inner_gate.control(label="foo") clbit = Clbit() - conditonal_controlled_gate = controlled_gate.c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + conditonal_controlled_gate = controlled_gate.c_if(clbit, 0) self.assertTrue(conditonal_controlled_gate.mutable) self.assertEqual("my h gate", conditonal_controlled_gate.base_gate.label) self.assertEqual("foo", conditonal_controlled_gate.label) - self.assertEqual((clbit, 0), conditonal_controlled_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual((clbit, 0), conditonal_controlled_gate.condition) def test_inner_outer_label_with_c_if_deepcopy(self): inner_gate = XGate(label="my h gate") controlled_gate = inner_gate.control(label="foo") clbit = Clbit() - conditonal_controlled_gate = controlled_gate.c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + conditonal_controlled_gate = controlled_gate.c_if(clbit, 0) self.assertTrue(conditonal_controlled_gate.mutable) self.assertEqual("my h gate", conditonal_controlled_gate.base_gate.label) self.assertEqual("foo", conditonal_controlled_gate.label) - self.assertEqual((clbit, 0), conditonal_controlled_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual((clbit, 0), conditonal_controlled_gate.condition) copied = copy.deepcopy(conditonal_controlled_gate) self.assertIsNot(conditonal_controlled_gate, copied) self.assertTrue(copied.mutable) self.assertEqual("my h gate", copied.base_gate.label) self.assertEqual("foo", copied.label) - self.assertEqual((clbit, 0), copied.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual((clbit, 0), copied.condition) def test_inner_outer_label_pickle(self): inner_gate = XGate(label="my h gate") diff --git a/test/python/circuit/test_store.py b/test/python/circuit/test_store.py index 139192745d2e..ecb98681bbd2 100644 --- a/test/python/circuit/test_store.py +++ b/test/python/circuit/test_store.py @@ -73,7 +73,8 @@ def test_rejects_dangerous_cast(self): def test_rejects_c_if(self): instruction = Store(expr.Var.new("a", types.Bool()), expr.Var.new("b", types.Bool())) with self.assertRaises(NotImplementedError): - instruction.c_if(Clbit(), False) + with self.assertWarns(DeprecationWarning): + instruction.c_if(Clbit(), False) class TestStoreCircuit(QiskitTestCase): @@ -241,4 +242,5 @@ def test_rejects_c_if(self): qc = QuantumCircuit([Clbit()], inputs=[a]) instruction_set = qc.store(a, True) with self.assertRaises(NotImplementedError): - instruction_set.c_if(qc.clbits[0], False) + with self.assertWarns(DeprecationWarning): + instruction_set.c_if(qc.clbits[0], False) diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py index b384d8b9267f..6a954d871cc9 100644 --- a/test/python/compiler/test_assembler.py +++ b/test/python/compiler/test_assembler.py @@ -285,7 +285,8 @@ def test_measure_to_registers_when_conditionals(self): qc.measure(qr[0], cr1) # Measure not required for a later conditional qc.measure(qr[1], cr2[1]) # Measure required for a later conditional - qc.h(qr[1]).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + qc.h(qr[1]).c_if(cr2, 3) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) @@ -305,7 +306,8 @@ def test_convert_to_bfunc_plus_conditional(self): cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr, 1) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) @@ -326,7 +328,8 @@ def test_convert_to_bfunc_plus_conditional_onebit(self): cr = ClassicalRegister(3) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr[2], 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr[2], 1) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) @@ -352,7 +355,8 @@ def test_resize_value_to_register(self): cr3 = ClassicalRegister(1) qc = QuantumCircuit(qr, cr1, cr2, cr3) - qc.h(qr[0]).c_if(cr2, 2) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr2, 2) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) diff --git a/test/python/compiler/test_disassembler.py b/test/python/compiler/test_disassembler.py index 805fac1178a1..4556550422b2 100644 --- a/test/python/compiler/test_disassembler.py +++ b/test/python/compiler/test_disassembler.py @@ -191,7 +191,8 @@ def test_circuit_with_conditionals(self): qc = QuantumCircuit(qr, cr1, cr2) qc.measure(qr[0], cr1) # Measure not required for a later conditional qc.measure(qr[1], cr2[1]) # Measure required for a later conditional - qc.h(qr[1]).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + qc.h(qr[1]).c_if(cr2, 3) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) @@ -207,7 +208,8 @@ def test_circuit_with_simple_conditional(self): qr = QuantumRegister(1) cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr, 1) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) @@ -228,7 +230,8 @@ def test_circuit_with_single_bit_conditions(self): qr = QuantumRegister(1) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr[0], 1) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) @@ -267,9 +270,12 @@ def test_multiple_conditionals_multiple_registers(self): qc = QuantumCircuit(qr, cr1, cr2, cr3, cr4) qc.x(qr[1]) qc.h(qr) - qc.cx(qr[1], qr[0]).c_if(cr3, 14) - qc.ccx(qr[0], qr[2], qr[1]).c_if(cr4, 1) - qc.h(qr).c_if(cr1, 3) + with self.assertWarns(DeprecationWarning): + qc.cx(qr[1], qr[0]).c_if(cr3, 14) + with self.assertWarns(DeprecationWarning): + qc.ccx(qr[0], qr[2], qr[1]).c_if(cr4, 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr).c_if(cr1, 3) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) @@ -285,7 +291,8 @@ def test_circuit_with_bit_conditional_1(self): qr = QuantumRegister(2) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr[1], True) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr[1], True) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) @@ -302,9 +309,12 @@ def test_circuit_with_bit_conditional_2(self): cr = ClassicalRegister(2) cr1 = ClassicalRegister(2) qc = QuantumCircuit(qr, cr, cr1) - qc.h(qr[0]).c_if(cr1[1], False) - qc.h(qr[1]).c_if(cr[0], True) - qc.cx(qr[0], qr[1]).c_if(cr1[0], False) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr1[1], False) + with self.assertWarns(DeprecationWarning): + qc.h(qr[1]).c_if(cr[0], True) + with self.assertWarns(DeprecationWarning): + qc.cx(qr[0], qr[1]).c_if(cr1[0], False) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 38991b63a63e..5241304a0db4 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -1522,14 +1522,18 @@ def test_circuit_with_delay(self, optimization_level): qc.delay(500, 1) qc.cx(0, 1) - out = transpile( - qc, - scheduling_method="alap", - basis_gates=["h", "cx"], - instruction_durations=[("h", 0, 200), ("cx", [0, 1], 700)], - optimization_level=optimization_level, - seed_transpiler=42, - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + out = transpile( + qc, + scheduling_method="alap", + basis_gates=["h", "cx"], + instruction_durations=[("h", 0, 200), ("cx", [0, 1], 700)], + optimization_level=optimization_level, + seed_transpiler=42, + ) self.assertEqual(out.duration, 1200) @@ -1610,7 +1614,7 @@ def test_scheduling_timing_constraints(self): timing_constraints=timing_constraints, ) - def test_scheduling_instruction_constraints(self): + def test_scheduling_instruction_constraints_backend(self): """Test that scheduling-related loose transpile constraints work with both BackendV1 and BackendV2.""" @@ -1638,14 +1642,47 @@ def test_scheduling_instruction_constraints(self): ) self.assertEqual(scheduled.duration, 1500) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + scheduled = transpile( + qc, + backend=backend_v2, + scheduling_method="alap", + instruction_durations=durations, + layout_method="trivial", + ) + self.assertEqual(scheduled.duration, 1500) + + def test_scheduling_instruction_constraints(self): + """Test that scheduling-related loose transpile constraints work with target.""" + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="argument ``calibrate_instructions`` is deprecated", + ): + target = GenericBackendV2( + 2, + calibrate_instructions=True, + coupling_map=[[0, 1]], + basis_gates=["cx", "h"], + seed=42, + ).target + qc = QuantumCircuit(2) + qc.h(0) + qc.delay(0.000001, 1, "s") + qc.cx(0, 1) + + # update cx to 2 seconds + target.update_instruction_properties("cx", (0, 1), InstructionProperties(0.000001)) + scheduled = transpile( qc, - backend=backend_v2, + target=target, scheduling_method="alap", - instruction_durations=durations, layout_method="trivial", ) - self.assertEqual(scheduled.duration, 1500) + self.assertEqual(scheduled.duration, 9010) def test_scheduling_dt_constraints(self): """Test that scheduling-related loose transpile constraints @@ -1675,8 +1712,7 @@ def test_scheduling_dt_constraints(self): self.assertEqual(scheduled.duration, original_duration * 2) def test_backend_props_constraints(self): - """Test that loose transpile constraints - work with both BackendV1 and BackendV2.""" + """Test that loose transpile constraints work with both BackendV1 and BackendV2.""" with self.assertWarns(DeprecationWarning): backend_v1 = Fake20QV1() @@ -1733,13 +1769,17 @@ def test_backend_props_constraints(self): ) self.assertEqual(result._layout.initial_layout._p2v, vf2_layout) - result = transpile( - qc, - backend=backend_v2, - backend_properties=custom_backend_properties, - optimization_level=2, - seed_transpiler=42, - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + result = transpile( + qc, + backend=backend_v2, + backend_properties=custom_backend_properties, + optimization_level=2, + seed_transpiler=42, + ) self.assertEqual(result._layout.initial_layout._p2v, vf2_layout) @@ -2228,7 +2268,8 @@ def _regular_circuit(self): base.append(CustomCX(), [3, 6]) base.append(CustomCX(), [5, 4]) base.append(CustomCX(), [5, 3]) - base.append(CustomCX(), [2, 4]).c_if(base.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + base.append(CustomCX(), [2, 4]).c_if(base.cregs[0], 3) base.ry(a, 4) base.measure(4, 2) return base @@ -2842,12 +2883,14 @@ def test_parallel_singleton_conditional_gate(self, opt_level): circ = QuantumCircuit(2, 1) circ.h(0) circ.measure(0, circ.clbits[0]) - circ.z(1).c_if(circ.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + circ.z(1).c_if(circ.clbits[0], 1) res = transpile( [circ, circ], backend, optimization_level=opt_level, seed_transpiler=123456769 ) self.assertTrue(res[0].data[-1].operation.mutable) - self.assertEqual(res[0].data[-1].operation.condition, (res[0].clbits[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(res[0].data[-1].operation.condition, (res[0].clbits[0], 1)) @data(0, 1, 2, 3) def test_backendv2_and_basis_gates(self, opt_level): @@ -3292,7 +3335,8 @@ def test_shared_classical_between_components_condition(self, opt_level): for i in range(18): qc.measure(i, creg[i]) - qc.ecr(20, 21).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.ecr(20, 21).c_if(creg, 0) tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=42) def _visit_block(circuit, qubit_mapping=None): @@ -3328,9 +3372,11 @@ def test_shared_classical_between_components_condition_large_to_small(self, opt_ qc.measure(24, creg[0]) qc.measure(23, creg[1]) # Component 1 - qc.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(creg, 0) for i in range(18): - qc.ecr(0, i + 1).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.ecr(0, i + 1).c_if(creg, 0) tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=123456789) def _visit_block(circuit, qubit_mapping=None): @@ -3402,9 +3448,11 @@ def test_shared_classical_between_components_condition_large_to_small_reverse_in qc.measure(0, creg[0]) qc.measure(1, creg[1]) # Component 1 - qc.h(24).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.h(24).c_if(creg, 0) for i in range(23, 5, -1): - qc.ecr(24, i).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.ecr(24, i).c_if(creg, 0) tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=2023) def _visit_block(circuit, qubit_mapping=None): @@ -3475,15 +3523,19 @@ def test_chained_data_dependency(self, opt_level): measure_op = Measure() qc.append(measure_op, [9], [creg[0]]) # Component 1 - qc.h(10).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.h(10).c_if(creg, 0) for i in range(11, 20): - qc.ecr(10, i).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.ecr(10, i).c_if(creg, 0) measure_op = Measure() qc.append(measure_op, [19], [creg[0]]) # Component 2 - qc.h(20).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.h(20).c_if(creg, 0) for i in range(21, 30): - qc.cz(20, i).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.cz(20, i).c_if(creg, 0) measure_op = Measure() qc.append(measure_op, [29], [creg[0]]) tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=2023) @@ -3739,14 +3791,19 @@ def test_triple_circuit_invalid_layout(self, routing_method): qc.cy(20, 28) qc.cy(20, 29) qc.measure_all() + with self.assertRaises(TranspilerError): - transpile( - qc, - self.backend, - layout_method="trivial", - routing_method=routing_method, - seed_transpiler=42, - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + transpile( + qc, + self.backend, + layout_method="trivial", + routing_method=routing_method, + seed_transpiler=42, + ) @data("stochastic") def test_triple_circuit_invalid_layout_stochastic(self, routing_method): @@ -3784,15 +3841,14 @@ def test_triple_circuit_invalid_layout_stochastic(self, routing_method): qc.cy(20, 28) qc.cy(20, 29) qc.measure_all() - with self.assertWarns(DeprecationWarning): - with self.assertRaises(TranspilerError): - transpile( - qc, - self.backend, - layout_method="trivial", - routing_method=routing_method, - seed_transpiler=42, - ) + with self.assertRaises(TranspilerError): + transpile( + qc, + self.backend, + layout_method="trivial", + routing_method=routing_method, + seed_transpiler=42, + ) # Lookahead swap skipped for performance reasons, stochastic moved to new test due to deprecation @data("sabre", "basic") diff --git a/test/python/converters/test_circuit_to_dag.py b/test/python/converters/test_circuit_to_dag.py index 852cb324aa79..06d3dec654e4 100644 --- a/test/python/converters/test_circuit_to_dag.py +++ b/test/python/converters/test_circuit_to_dag.py @@ -34,7 +34,8 @@ def test_circuit_and_dag(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/converters/test_circuit_to_dagdependency.py b/test/python/converters/test_circuit_to_dagdependency.py index 65221e9cf2c1..7d3bbfd2c49c 100644 --- a/test/python/converters/test_circuit_to_dagdependency.py +++ b/test/python/converters/test_circuit_to_dagdependency.py @@ -33,7 +33,8 @@ def test_circuit_and_dag_canonical(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) @@ -51,7 +52,8 @@ def test_circuit_and_dag_canonical2(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/converters/test_circuit_to_dagdependency_v2.py b/test/python/converters/test_circuit_to_dagdependency_v2.py index 3323ca0e6768..c7a31dc5767c 100644 --- a/test/python/converters/test_circuit_to_dagdependency_v2.py +++ b/test/python/converters/test_circuit_to_dagdependency_v2.py @@ -33,7 +33,8 @@ def test_circuit_and_dag_canonical(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/converters/test_circuit_to_instruction.py b/test/python/converters/test_circuit_to_instruction.py index e3239d4b5ff4..1b225831bb23 100644 --- a/test/python/converters/test_circuit_to_instruction.py +++ b/test/python/converters/test_circuit_to_instruction.py @@ -56,22 +56,28 @@ def test_flatten_registers_of_circuit_single_bit_cond(self): cr1 = ClassicalRegister(3, "cr1") cr2 = ClassicalRegister(3, "cr2") circ = QuantumCircuit(qr1, qr2, cr1, cr2) - circ.h(qr1[0]).c_if(cr1[1], True) - circ.h(qr2[1]).c_if(cr2[0], False) - circ.cx(qr1[1], qr2[2]).c_if(cr2[2], True) + with self.assertWarns(DeprecationWarning): + circ.h(qr1[0]).c_if(cr1[1], True) + with self.assertWarns(DeprecationWarning): + circ.h(qr2[1]).c_if(cr2[0], False) + with self.assertWarns(DeprecationWarning): + circ.cx(qr1[1], qr2[2]).c_if(cr2[2], True) circ.measure(qr2[2], cr2[0]) - inst = circuit_to_instruction(circ) + with self.assertWarns(DeprecationWarning): + inst = circuit_to_instruction(circ) q = QuantumRegister(5, "q") c = ClassicalRegister(6, "c") self.assertEqual(inst.definition[0].qubits, (q[0],)) self.assertEqual(inst.definition[1].qubits, (q[3],)) self.assertEqual(inst.definition[2].qubits, (q[1], q[4])) - - self.assertEqual(inst.definition[0].operation.condition, (c[1], True)) - self.assertEqual(inst.definition[1].operation.condition, (c[3], False)) - self.assertEqual(inst.definition[2].operation.condition, (c[5], True)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(inst.definition[0].operation.condition, (c[1], True)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(inst.definition[1].operation.condition, (c[3], False)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(inst.definition[2].operation.condition, (c[5], True)) def test_flatten_circuit_registerless(self): """Test that the conversion works when the given circuit has bits that are not contained in @@ -196,8 +202,10 @@ def test_registerless_classical_bits(self): Regression test of gh-7394.""" expected = QuantumCircuit([Qubit(), Clbit()]) - expected.h(0).c_if(expected.clbits[0], 0) - test = circuit_to_instruction(expected) + with self.assertWarns(DeprecationWarning): + expected.h(0).c_if(expected.clbits[0], 0) + with self.assertWarns(DeprecationWarning): + test = circuit_to_instruction(expected) self.assertIsInstance(test, Instruction) self.assertIsInstance(test.definition, QuantumCircuit) @@ -206,7 +214,8 @@ def test_registerless_classical_bits(self): test_instruction = test.definition.data[0] expected_instruction = expected.data[0] self.assertIs(type(test_instruction.operation), type(expected_instruction.operation)) - self.assertEqual(test_instruction.operation.condition, (test.definition.clbits[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(test_instruction.operation.condition, (test.definition.clbits[0], 0)) def test_zero_operands(self): """Test that an instruction can be created, even if it has zero operands.""" diff --git a/test/python/converters/test_dag_to_dagdependency.py b/test/python/converters/test_dag_to_dagdependency.py index 6b52652e6ace..b4a66659245b 100644 --- a/test/python/converters/test_dag_to_dagdependency.py +++ b/test/python/converters/test_dag_to_dagdependency.py @@ -34,7 +34,8 @@ def test_circuit_and_dag_dependency(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) @@ -55,7 +56,8 @@ def test_circuit_and_dag_dependency2(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/converters/test_dag_to_dagdependency_v2.py b/test/python/converters/test_dag_to_dagdependency_v2.py index 925bf442f477..951e31835fe9 100644 --- a/test/python/converters/test_dag_to_dagdependency_v2.py +++ b/test/python/converters/test_dag_to_dagdependency_v2.py @@ -34,7 +34,8 @@ def test_circuit_and_dag_dependency(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/dagcircuit/test_collect_blocks.py b/test/python/dagcircuit/test_collect_blocks.py index b2715078d7f5..d8178fdb3a54 100644 --- a/test/python/dagcircuit/test_collect_blocks.py +++ b/test/python/dagcircuit/test_collect_blocks.py @@ -243,7 +243,8 @@ def test_circuit_has_conditional_gates(self): qc.x(0) qc.x(1) qc.cx(1, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.x(0) qc.x(1) qc.cx(0, 1) @@ -263,11 +264,13 @@ def test_circuit_has_conditional_gates(self): # conditional gate (note that x(1) following the measure is collected into the first # block). block_collector = BlockCollector(circuit_to_dag(qc)) - blocks = block_collector.collect_all_matching_blocks( - lambda node: node.op.name in ["x", "cx"] and not getattr(node.op, "condition", None), - split_blocks=False, - min_block_size=1, - ) + with self.assertWarns(DeprecationWarning): + blocks = block_collector.collect_all_matching_blocks( + lambda node: node.op.name in ["x", "cx"] + and not getattr(node.op, "condition", None), + split_blocks=False, + min_block_size=1, + ) self.assertEqual(len(blocks), 2) self.assertEqual(len(blocks[0]), 4) self.assertEqual(len(blocks[1]), 2) @@ -280,7 +283,8 @@ def test_circuit_has_conditional_gates_dagdependency(self): qc.x(0) qc.x(1) qc.cx(1, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.x(0) qc.x(1) qc.cx(0, 1) @@ -300,11 +304,13 @@ def test_circuit_has_conditional_gates_dagdependency(self): # conditional gate (note that x(1) following the measure is collected into the first # block). block_collector = BlockCollector(circuit_to_dag(qc)) - blocks = block_collector.collect_all_matching_blocks( - lambda node: node.op.name in ["x", "cx"] and not getattr(node.op, "condition", None), - split_blocks=False, - min_block_size=1, - ) + with self.assertWarns(DeprecationWarning): + blocks = block_collector.collect_all_matching_blocks( + lambda node: node.op.name in ["x", "cx"] + and not getattr(node.op, "condition", None), + split_blocks=False, + min_block_size=1, + ) self.assertEqual(len(blocks), 2) self.assertEqual(len(blocks[0]), 4) self.assertEqual(len(blocks[1]), 2) @@ -544,11 +550,13 @@ def test_collect_blocks_with_clbits(self): condition.""" qc = QuantumCircuit(4, 3) - qc.cx(0, 1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 1) qc.cx(2, 3) qc.cx(1, 2) qc.cx(0, 1) - qc.cx(2, 3).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(1, 0) dag = circuit_to_dag(qc) @@ -567,7 +575,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -580,11 +589,13 @@ def test_collect_blocks_with_clbits_dagdependency(self): under conditions, using DAGDependency.""" qc = QuantumCircuit(4, 3) - qc.cx(0, 1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 1) qc.cx(2, 3) qc.cx(1, 2) qc.cx(0, 1) - qc.cx(2, 3).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(1, 0) dag = circuit_to_dagdependency(qc) @@ -603,7 +614,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dagdependency_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -620,10 +632,13 @@ def test_collect_blocks_with_clbits2(self): cbit = Clbit() qc = QuantumCircuit(qreg, creg, [cbit]) - qc.cx(0, 1).c_if(creg[1], 1) - qc.cx(2, 3).c_if(cbit, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[1], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(cbit, 0) qc.cx(1, 2) - qc.cx(0, 1).c_if(creg[2], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[2], 1) dag = circuit_to_dag(qc) @@ -642,7 +657,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -659,10 +675,13 @@ def test_collect_blocks_with_clbits2_dagdependency(self): cbit = Clbit() qc = QuantumCircuit(qreg, creg, [cbit]) - qc.cx(0, 1).c_if(creg[1], 1) - qc.cx(2, 3).c_if(cbit, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[1], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(cbit, 0) qc.cx(1, 2) - qc.cx(0, 1).c_if(creg[2], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[2], 1) dag = circuit_to_dag(qc) @@ -681,7 +700,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -698,9 +718,11 @@ def test_collect_blocks_with_cregs(self): creg2 = ClassicalRegister(2, "cr2") qc = QuantumCircuit(qreg, creg, creg2) - qc.cx(0, 1).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg, 3) qc.cx(1, 2) - qc.cx(0, 1).c_if(creg[2], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[2], 1) dag = circuit_to_dag(qc) @@ -719,7 +741,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -737,9 +760,11 @@ def test_collect_blocks_with_cregs_dagdependency(self): creg2 = ClassicalRegister(2, "cr2") qc = QuantumCircuit(qreg, creg, creg2) - qc.cx(0, 1).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg, 3) qc.cx(1, 2) - qc.cx(0, 1).c_if(creg[2], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[2], 1) dag = circuit_to_dagdependency(qc) @@ -758,7 +783,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dagdependency_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -917,14 +943,19 @@ def test_split_layers_dagdependency(self): def test_block_collapser_register_condition(self): """Test that BlockCollapser can handle a register being used more than once.""" qc = QuantumCircuit(1, 2) - qc.x(0).c_if(qc.cregs[0], 0) - qc.y(0).c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + qc.y(0).c_if(qc.cregs[0], 1) dag = circuit_to_dag(qc) blocks = BlockCollector(dag).collect_all_matching_blocks( lambda _: True, split_blocks=False, min_block_size=1 ) - dag = BlockCollapser(dag).collapse_to_operation(blocks, lambda circ: circ.to_instruction()) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation( + blocks, lambda circ: circ.to_instruction() + ) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) diff --git a/test/python/dagcircuit/test_compose.py b/test/python/dagcircuit/test_compose.py index 27404cec05c2..1bbd3031d310 100644 --- a/test/python/dagcircuit/test_compose.py +++ b/test/python/dagcircuit/test_compose.py @@ -321,8 +321,10 @@ def test_compose_conditional(self): creg = ClassicalRegister(2, "rcr") circuit_right = QuantumCircuit(qreg, creg) - circuit_right.x(qreg[1]).c_if(creg, 2) - circuit_right.h(qreg[0]).c_if(creg, 1) + with self.assertWarns(DeprecationWarning): + circuit_right.x(qreg[1]).c_if(creg, 2) + with self.assertWarns(DeprecationWarning): + circuit_right.h(qreg[0]).c_if(creg, 1) circuit_right.measure(qreg, creg) # permuted subset of qubits and clbits @@ -330,16 +332,19 @@ def test_compose_conditional(self): dag_right = circuit_to_dag(circuit_right) # permuted subset of qubits and clbits - dag_left.compose( - dag_right, - qubits=[self.left_qubit1, self.left_qubit4], - clbits=[self.left_clbit1, self.left_clbit0], - ) + with self.assertWarns(DeprecationWarning): + dag_left.compose( + dag_right, + qubits=[self.left_qubit1, self.left_qubit4], + clbits=[self.left_clbit1, self.left_clbit0], + ) circuit_composed = dag_to_circuit(dag_left) circuit_expected = self.circuit_left.copy() - circuit_expected.x(self.left_qubit4).c_if(*self.condition1) - circuit_expected.h(self.left_qubit1).c_if(*self.condition2) + with self.assertWarns(DeprecationWarning): + circuit_expected.x(self.left_qubit4).c_if(*self.condition1) + with self.assertWarns(DeprecationWarning): + circuit_expected.h(self.left_qubit1).c_if(*self.condition2) circuit_expected.measure(self.left_qubit4, self.left_clbit0) circuit_expected.measure(self.left_qubit1, self.left_clbit1) @@ -423,12 +428,13 @@ def test_compose_condition_multiple_classical(self): circuit_left = QuantumCircuit(qreg, creg1, creg2) circuit_right = QuantumCircuit(qreg, creg1, creg2) - circuit_right.h(0).c_if(creg1, 1) + with self.assertWarns(DeprecationWarning): + circuit_right.h(0).c_if(creg1, 1) dag_left = circuit_to_dag(circuit_left) dag_right = circuit_to_dag(circuit_right) - - dag_composed = dag_left.compose(dag_right, qubits=[0], clbits=[0, 1], inplace=False) + with self.assertWarns(DeprecationWarning): + dag_composed = dag_left.compose(dag_right, qubits=[0], clbits=[0, 1], inplace=False) dag_expected = circuit_to_dag(circuit_right.copy()) diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index e2881cf4a3d9..96f307ea8548 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -118,7 +118,7 @@ def raise_if_dagcircuit_invalid(dag): out_wires = set(dag._out_wires(node._node_id)) node_cond_bits = set( - node.op.condition[0][:] if getattr(node.op, "condition", None) is not None else [] + node.condition[0][:] if getattr(node, "condition", None) is not None else [] ) node_qubits = set(node.qargs) node_clbits = set(node.cargs) @@ -561,7 +561,8 @@ def setUp(self): def test_apply_operation_back(self): """The apply_operation_back() method.""" - x_gate = XGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + x_gate = XGate().c_if(*self.condition) self.dag.apply_operation_back(HGate(), [self.qubit0], []) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1], [self.clbit1]) @@ -573,7 +574,8 @@ def test_apply_operation_back(self): def test_edges(self): """Test that DAGCircuit.edges() behaves as expected with ops.""" - x_gate = XGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + x_gate = XGate().c_if(*self.condition) self.dag.apply_operation_back(HGate(), [self.qubit0], []) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1], [self.clbit1]) @@ -590,13 +592,14 @@ def test_apply_operation_back_conditional(self): """Test consistency of apply_operation_back with condition set.""" # Single qubit gate conditional: qc.h(qr[2]).c_if(cr, 3) - - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) h_node = self.dag.apply_operation_back(h_gate, [self.qubit2], []) self.assertEqual(h_node.qargs, (self.qubit2,)) self.assertEqual(h_node.cargs, ()) - self.assertEqual(h_node.op.condition, h_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual(h_node.op.condition, h_gate.condition) self.assertEqual( sorted(self.dag._in_edges(h_node._node_id)), @@ -630,13 +633,14 @@ def test_apply_operation_back_conditional_measure(self): new_creg = ClassicalRegister(1, "cr2") self.dag.add_creg(new_creg) - - meas_gate = Measure().c_if(new_creg, 0) + with self.assertWarns(DeprecationWarning): + meas_gate = Measure().c_if(new_creg, 0) meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit0], [self.clbit0]) self.assertEqual(meas_node.qargs, (self.qubit0,)) self.assertEqual(meas_node.cargs, (self.clbit0,)) - self.assertEqual(meas_node.op.condition, meas_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual(meas_node.op.condition, meas_gate.condition) self.assertEqual( sorted(self.dag._in_edges(meas_node._node_id)), @@ -675,13 +679,14 @@ def test_apply_operation_back_conditional_measure_to_self(self): # Measure targeting a clbit which _is_ a member of the conditional # register. qc.measure(qr[0], cr[0]).c_if(cr, 3) - - meas_gate = Measure().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + meas_gate = Measure().c_if(*self.condition) meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit1], [self.clbit1]) self.assertEqual(meas_node.qargs, (self.qubit1,)) self.assertEqual(meas_node.cargs, (self.clbit1,)) - self.assertEqual(meas_node.op.condition, meas_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual(meas_node.op.condition, meas_gate.condition) self.assertEqual( sorted(self.dag._in_edges(meas_node._node_id)), @@ -1239,7 +1244,8 @@ def test_dag_collect_runs(self): def test_dag_collect_runs_start_with_conditional(self): """Test collect runs with a conditional at the start of the run.""" - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1252,7 +1258,8 @@ def test_dag_collect_runs_start_with_conditional(self): def test_dag_collect_runs_conditional_in_middle(self): """Test collect_runs with a conditional in the middle of a run.""" - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1294,7 +1301,8 @@ def test_dag_collect_1q_runs_start_with_conditional(self): """Test collect 1q runs with a conditional at the start of the run.""" self.dag.apply_operation_back(Reset(), [self.qubit0]) self.dag.apply_operation_back(Delay(100), [self.qubit0]) - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1309,7 +1317,8 @@ def test_dag_collect_1q_runs_conditional_in_middle(self): """Test collect_1q_runs with a conditional in the middle of a run.""" self.dag.apply_operation_back(Reset(), [self.qubit0]) self.dag.apply_operation_back(Delay(100), [self.qubit0]) - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1387,7 +1396,8 @@ def test_layers_basic(self): qubit1 = qreg[1] clbit0 = creg[0] clbit1 = creg[1] - x_gate = XGate().c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + x_gate = XGate().c_if(creg, 3) dag = DAGCircuit() dag.add_qreg(qreg) dag.add_creg(creg) @@ -1856,29 +1866,41 @@ def test_semantic_conditions(self): qreg = QuantumRegister(1, name="q") creg = ClassicalRegister(1, name="c") qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.cregs[0], 1) - qc2.x(0).c_if(qc2.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], True) self.assertEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) # Order of operations transposed. qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.clbits[-1], True) - qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) self.assertNotEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) # Single-bit condition values not the same. qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.cregs[0], 1) - qc2.x(0).c_if(qc2.clbits[-1], False) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], False) self.assertNotEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) def test_semantic_expr(self): @@ -2489,7 +2511,8 @@ def test_substitute_without_propagating_bit_conditional(self): sub = QuantumCircuit(2, 1) sub.h(0) - sub.cx(0, 1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + sub.cx(0, 1).c_if(0, True) sub.h(0) expected = DAGCircuit() @@ -2522,7 +2545,8 @@ def test_substitute_without_propagating_register_conditional(self): sub = QuantumCircuit(QuantumRegister(2), ClassicalRegister(2)) sub.h(0) - sub.cx(0, 1).c_if(sub.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + sub.cx(0, 1).c_if(sub.cregs[0], 3) sub.h(0) expected = DAGCircuit() @@ -2559,8 +2583,10 @@ def test_substitute_with_provided_wire_map_propagate_condition(self): sub.cx(0, 1) sub.h(0) - conditioned_h = HGate().c_if(*conditioned_cz.condition) - conditioned_cx = CXGate().c_if(*conditioned_cz.condition) + with self.assertWarns(DeprecationWarning): + conditioned_h = HGate().c_if(*conditioned_cz.condition) + with self.assertWarns(DeprecationWarning): + conditioned_cx = CXGate().c_if(*conditioned_cz.condition) expected = DAGCircuit() expected.add_qubits(base_qubits) @@ -2593,11 +2619,13 @@ def test_substitute_with_provided_wire_map_no_propagate_condition(self): sub = QuantumCircuit(2, 1) sub.h(0) - sub.cx(0, 1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + sub.cx(0, 1).c_if(0, True) sub.h(0) conditioned_cx = CXGate().to_mutable() - conditioned_cx.condition = conditioned_cz.condition + with self.assertWarns(DeprecationWarning): + conditioned_cx.condition = conditioned_cz.condition expected = DAGCircuit() expected.add_qubits(base_qubits) @@ -2626,7 +2654,8 @@ def test_creates_additional_alias_register(self): target = base.apply_operation_back(Instruction("dummy", 2, 2, []), base_qreg, base_creg[:2]) sub = QuantumCircuit(QuantumRegister(2), ClassicalRegister(2)) - sub.cx(0, 1).c_if(sub.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + sub.cx(0, 1).c_if(sub.cregs[0], 3) base.substitute_node_with_dag(target, circuit_to_dag(sub)) @@ -2694,7 +2723,8 @@ def test_substituting_node_preserves_args_condition(self, inplace): self.assertEqual(replacement_node.op.name, "cz") self.assertEqual(replacement_node.qargs, (qr[1], qr[0])) self.assertEqual(replacement_node.cargs, ()) - self.assertEqual(replacement_node.op.condition, (cr, 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(replacement_node.op.condition, (cr, 1)) self.assertEqual(replacement_node is node_to_be_replaced, inplace) @data(True, False) @@ -2731,12 +2761,14 @@ def test_refuses_to_overwrite_condition(self, inplace): dag = DAGCircuit() dag.add_qreg(qr) dag.add_creg(cr) - node = dag.apply_operation_back(XGate().c_if(cr, 2), qr, []) + with self.assertWarns(DeprecationWarning): + node = dag.apply_operation_back(XGate().c_if(cr, 2), qr, []) with self.assertRaisesRegex(DAGCircuitError, "Cannot propagate a condition"): - dag.substitute_node( - node, XGate().c_if(cr, 1), inplace=inplace, propagate_condition=True - ) + with self.assertWarns(DeprecationWarning): + dag.substitute_node( + node, XGate().c_if(cr, 1), inplace=inplace, propagate_condition=True + ) @data(True, False) def test_replace_if_else_op_with_another(self, inplace): @@ -3194,13 +3226,15 @@ def setUp(self): def test_creg_conditional(self): """Test consistency of conditional on classical register.""" - self.circuit.h(self.qreg[0]).c_if(self.creg, 1) + with self.assertWarns(DeprecationWarning): + self.circuit.h(self.qreg[0]).c_if(self.creg, 1) self.dag = circuit_to_dag(self.circuit) gate_node = self.dag.gate_nodes()[0] self.assertEqual(gate_node.op, HGate()) self.assertEqual(gate_node.qargs, (self.qreg[0],)) self.assertEqual(gate_node.cargs, ()) - self.assertEqual(gate_node.op.condition, (self.creg, 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate_node.op.condition, (self.creg, 1)) gate_node_preds = list(self.dag.predecessors(gate_node)) gate_node_in_edges = [ @@ -3235,14 +3269,15 @@ def test_creg_conditional(self): def test_clbit_conditional(self): """Test consistency of conditional on single classical bit.""" - - self.circuit.h(self.qreg[0]).c_if(self.creg[0], 1) + with self.assertWarns(DeprecationWarning): + self.circuit.h(self.qreg[0]).c_if(self.creg[0], 1) self.dag = circuit_to_dag(self.circuit) gate_node = self.dag.gate_nodes()[0] self.assertEqual(gate_node.op, HGate()) self.assertEqual(gate_node.qargs, (self.qreg[0],)) self.assertEqual(gate_node.cargs, ()) - self.assertEqual(gate_node.op.condition, (self.creg[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate_node.op.condition, (self.creg[0], 1)) gate_node_preds = list(self.dag.predecessors(gate_node)) gate_node_in_edges = [ diff --git a/test/python/primitives/test_backend_estimator.py b/test/python/primitives/test_backend_estimator.py index 80b471b66063..62845461dacf 100644 --- a/test/python/primitives/test_backend_estimator.py +++ b/test/python/primitives/test_backend_estimator.py @@ -493,7 +493,8 @@ def test_dynamic_circuit(self): qc.h(0) qc.cx(0, 1) qc.measure(1, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) observable = SparsePauliOp("IZ") diff --git a/test/python/primitives/test_backend_sampler.py b/test/python/primitives/test_backend_sampler.py index eb3b79f1b911..dc77a3c47ce6 100644 --- a/test/python/primitives/test_backend_sampler.py +++ b/test/python/primitives/test_backend_sampler.py @@ -397,7 +397,8 @@ def test_circuit_with_dynamic_circuit(self): qc.h(0) qc.cx(0, 1) qc.measure(0, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) with self.assertWarns(DeprecationWarning): backend = Aer.get_backend("aer_simulator") diff --git a/test/python/primitives/test_backend_sampler_v2.py b/test/python/primitives/test_backend_sampler_v2.py index e66b594cc738..b5077cec5016 100644 --- a/test/python/primitives/test_backend_sampler_v2.py +++ b/test/python/primitives/test_backend_sampler_v2.py @@ -1215,8 +1215,10 @@ def test_circuit_with_aliased_cregs(self, backend): qc.h(0) qc.measure(0, c1) qc.measure(1, c2) - qc.z(2).c_if(c1, 1) - qc.x(2).c_if(c2, 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(c1, 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(c2, 1) qc2 = QuantumCircuit(5, 5) qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True) cregs = [creg.name for creg in qc2.cregs] @@ -1251,8 +1253,10 @@ def test_circuit_with_aliased_cregs_v1(self, backend): qc.h(0) qc.measure(0, c1) qc.measure(1, c2) - qc.z(2).c_if(c1, 1) - qc.x(2).c_if(c2, 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(c1, 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(c2, 1) qc2 = QuantumCircuit(5, 5) qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True) cregs = [creg.name for creg in qc2.cregs] diff --git a/test/python/primitives/test_primitive.py b/test/python/primitives/test_primitive.py index fc0118564f3d..78bcff5ff405 100644 --- a/test/python/primitives/test_primitive.py +++ b/test/python/primitives/test_primitive.py @@ -163,7 +163,8 @@ def test_circuit_key_controlflow(self): qc.h(0) qc.cx(0, 1) qc.measure(0, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) self.assertIsInstance(hash(_circuit_key(qc)), int) self.assertIsInstance(json.dumps(_circuit_key(qc)), str) diff --git a/test/python/primitives/test_statevector_sampler.py b/test/python/primitives/test_statevector_sampler.py index a782aafaeaf5..3af4e8d9f688 100644 --- a/test/python/primitives/test_statevector_sampler.py +++ b/test/python/primitives/test_statevector_sampler.py @@ -283,7 +283,8 @@ def test_run_errors(self): qc4 = QuantumCircuit(2, 2) qc4.h(0) qc4.measure(1, 1) - qc4.x(0).c_if(1, 1) + with self.assertWarns(DeprecationWarning): + qc4.x(0).c_if(1, 1) qc4.measure(0, 0) sampler = StatevectorSampler() @@ -592,8 +593,10 @@ def test_circuit_with_aliased_cregs(self): c2 = ClassicalRegister(1, "c2") qc = QuantumCircuit(q, c1, c2) - qc.z(2).c_if(c1, 1) - qc.x(2).c_if(c2, 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(c1, 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(c2, 1) qc2 = QuantumCircuit(5, 5) qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True) # Note: qc2 has aliased cregs, c0 -> c[2] and c1 -> c[4]. diff --git a/test/python/providers/basic_provider/__init__.py b/test/python/providers/basic_provider/__init__.py index 0c8b92822219..c84f6a74b9d4 100644 --- a/test/python/providers/basic_provider/__init__.py +++ b/test/python/providers/basic_provider/__init__.py @@ -20,7 +20,9 @@ class BasicProviderBackendTestMixin: def test_configuration(self): """Test backend.configuration().""" - configuration = self.backend.configuration() + with self.assertWarns(DeprecationWarning): + # The method is deprecated + configuration = self.backend.configuration() return configuration def test_run_circuit(self): diff --git a/test/python/providers/basic_provider/test_basic_simulator.py b/test/python/providers/basic_provider/test_basic_simulator.py index 57dd67dfd3c3..925823b49300 100644 --- a/test/python/providers/basic_provider/test_basic_simulator.py +++ b/test/python/providers/basic_provider/test_basic_simulator.py @@ -174,7 +174,8 @@ def test_if_statement(self): circuit_if_true.x(qr[1]) circuit_if_true.measure(qr[0], cr[0]) circuit_if_true.measure(qr[1], cr[1]) - circuit_if_true.x(qr[2]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_if_true.x(qr[2]).c_if(cr, 0x3) circuit_if_true.measure(qr[0], cr[0]) circuit_if_true.measure(qr[1], cr[1]) circuit_if_true.measure(qr[2], cr[2]) @@ -193,7 +194,8 @@ def test_if_statement(self): circuit_if_false.x(qr[0]) circuit_if_false.measure(qr[0], cr[0]) circuit_if_false.measure(qr[1], cr[1]) - circuit_if_false.x(qr[2]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_if_false.x(qr[2]).c_if(cr, 0x3) circuit_if_false.measure(qr[0], cr[0]) circuit_if_false.measure(qr[1], cr[1]) circuit_if_false.measure(qr[2], cr[2]) @@ -230,7 +232,8 @@ def test_bit_cif_crossaffect(self): circuit.x([qr[1], qr[2]]) circuit.measure(qr[1], cr[1]) circuit.measure(qr[2], cr[2]) - circuit.h(qr[0]).c_if(cr[0], True) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], True) circuit.measure(qr[0], cr1[0]) job = self.backend.run(circuit, shots=shots, seed_simulator=self.seed) result = job.result().get_counts() @@ -269,8 +272,10 @@ def test_teleport(self): circuit.barrier(qr) circuit.measure(qr[0], cr0[0]) circuit.measure(qr[1], cr1[0]) - circuit.z(qr[2]).c_if(cr0, 1) - circuit.x(qr[2]).c_if(cr1, 1) + with self.assertWarns(DeprecationWarning): + circuit.z(qr[2]).c_if(cr0, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[2]).c_if(cr1, 1) circuit.measure(qr[2], cr2[0]) job = self.backend.run( transpile(circuit, self.backend), shots=shots, seed_simulator=self.seed diff --git a/test/python/qasm2/test_arxiv_examples.py b/test/python/qasm2/test_arxiv_examples.py index 197b842697e1..85ed23e0ed19 100644 --- a/test/python/qasm2/test_arxiv_examples.py +++ b/test/python/qasm2/test_arxiv_examples.py @@ -72,7 +72,8 @@ def test_teleportation(self, parser): if(c1==1) x q[2]; post q[2]; measure q[2] -> c2[0];""" - parsed = parser(example) + with self.assertWarns(DeprecationWarning): + parsed = parser(example) post = gate_builder("post", [], QuantumCircuit([Qubit()])) @@ -90,8 +91,10 @@ def test_teleportation(self, parser): qc.h(q[0]) qc.measure(q[0], c0[0]) qc.measure(q[1], c1[0]) - qc.z(q[2]).c_if(c0, 1) - qc.x(q[2]).c_if(c1, 1) + with self.assertWarns(DeprecationWarning): + qc.z(q[2]).c_if(c0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(q[2]).c_if(c1, 1) qc.append(post(), [q[2]], []) qc.measure(q[2], c2[0]) @@ -168,7 +171,8 @@ def test_inverse_qft_1(self, parser): if(c==7) u1(pi/2+pi/4+pi/8) q[3]; h q[3]; measure q[3] -> c[3];""" - parsed = parser(example) + with self.assertWarns(DeprecationWarning): + parsed = parser(example) q = QuantumRegister(4, "q") c = ClassicalRegister(4, "c") @@ -177,21 +181,32 @@ def test_inverse_qft_1(self, parser): qc.barrier(q) qc.h(q[0]) qc.measure(q[0], c[0]) - qc.append(U1Gate(math.pi / 2).c_if(c, 1), [q[1]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c, 1), [q[1]]) qc.h(q[1]) qc.measure(q[1], c[1]) - qc.append(U1Gate(math.pi / 4).c_if(c, 1), [q[2]]) - qc.append(U1Gate(math.pi / 2).c_if(c, 2), [q[2]]) - qc.append(U1Gate(math.pi / 4 + math.pi / 2).c_if(c, 3), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4).c_if(c, 1), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c, 2), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4 + math.pi / 2).c_if(c, 3), [q[2]]) qc.h(q[2]) qc.measure(q[2], c[2]) - qc.append(U1Gate(math.pi / 8).c_if(c, 1), [q[3]]) - qc.append(U1Gate(math.pi / 4).c_if(c, 2), [q[3]]) - qc.append(U1Gate(math.pi / 8 + math.pi / 4).c_if(c, 3), [q[3]]) - qc.append(U1Gate(math.pi / 2).c_if(c, 4), [q[3]]) - qc.append(U1Gate(math.pi / 8 + math.pi / 2).c_if(c, 5), [q[3]]) - qc.append(U1Gate(math.pi / 4 + math.pi / 2).c_if(c, 6), [q[3]]) - qc.append(U1Gate(math.pi / 8 + math.pi / 4 + math.pi / 2).c_if(c, 7), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8).c_if(c, 1), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4).c_if(c, 2), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8 + math.pi / 4).c_if(c, 3), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c, 4), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8 + math.pi / 2).c_if(c, 5), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4 + math.pi / 2).c_if(c, 6), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8 + math.pi / 4 + math.pi / 2).c_if(c, 7), [q[3]]) qc.h(q[3]) qc.measure(q[3], c[3]) @@ -224,7 +239,8 @@ def test_inverse_qft_2(self, parser): if(c2==1) u1(pi/2) q[3]; h q[3]; measure q[3] -> c3[0];""" - parsed = parser(example) + with self.assertWarns(DeprecationWarning): + parsed = parser(example) q = QuantumRegister(4, "q") c0 = ClassicalRegister(1, "c0") @@ -236,16 +252,22 @@ def test_inverse_qft_2(self, parser): qc.barrier(q) qc.h(q[0]) qc.measure(q[0], c0[0]) - qc.append(U1Gate(math.pi / 2).c_if(c0, 1), [q[1]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c0, 1), [q[1]]) qc.h(q[1]) qc.measure(q[1], c1[0]) - qc.append(U1Gate(math.pi / 4).c_if(c0, 1), [q[2]]) - qc.append(U1Gate(math.pi / 2).c_if(c1, 1), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4).c_if(c0, 1), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c1, 1), [q[2]]) qc.h(q[2]) qc.measure(q[2], c2[0]) - qc.append(U1Gate(math.pi / 8).c_if(c0, 1), [q[3]]) - qc.append(U1Gate(math.pi / 4).c_if(c1, 1), [q[3]]) - qc.append(U1Gate(math.pi / 2).c_if(c2, 1), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8).c_if(c0, 1), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4).c_if(c1, 1), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c2, 1), [q[3]]) qc.h(q[3]) qc.measure(q[3], c3[0]) @@ -423,7 +445,8 @@ def test_error_correction(self, parser): if(syn==2) x q[2]; if(syn==3) x q[1]; measure q -> c;""" - parsed = parser(example) + with self.assertWarns(DeprecationWarning): + parsed = parser(example) syndrome_definition = QuantumCircuit([Qubit() for _ in [None] * 5]) syndrome_definition.cx(0, 3) @@ -442,9 +465,12 @@ def test_error_correction(self, parser): qc.barrier(q) qc.append(syndrome(), [q[0], q[1], q[2], a[0], a[1]]) qc.measure(a, syn) - qc.x(q[0]).c_if(syn, 1) - qc.x(q[2]).c_if(syn, 2) - qc.x(q[1]).c_if(syn, 3) + with self.assertWarns(DeprecationWarning): + qc.x(q[0]).c_if(syn, 1) + with self.assertWarns(DeprecationWarning): + qc.x(q[2]).c_if(syn, 2) + with self.assertWarns(DeprecationWarning): + qc.x(q[1]).c_if(syn, 3) qc.measure(q, c) self.assertEqual(parsed, qc) diff --git a/test/python/qasm2/test_circuit_methods.py b/test/python/qasm2/test_circuit_methods.py index 1ba9689ab3ef..d5fa814f7c52 100644 --- a/test/python/qasm2/test_circuit_methods.py +++ b/test/python/qasm2/test_circuit_methods.py @@ -180,14 +180,16 @@ def test_qasm_text_conditional(self): ) + "\n" ) - q_circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + q_circuit = QuantumCircuit.from_qasm_str(qasm_string) qr = QuantumRegister(1, "q") cr0 = ClassicalRegister(4, "c0") cr1 = ClassicalRegister(4, "c1") ref = QuantumCircuit(qr, cr0, cr1) ref.x(qr[0]) - ref.x(qr[0]).c_if(cr1, 4) + with self.assertWarns(DeprecationWarning): + ref.x(qr[0]).c_if(cr1, 4) self.assertEqual(len(q_circuit.cregs), 2) self.assertEqual(len(q_circuit.qregs), 1) diff --git a/test/python/qasm2/test_export.py b/test/python/qasm2/test_export.py index 8de4bb8eb34f..ef6ab8076ef6 100644 --- a/test/python/qasm2/test_export.py +++ b/test/python/qasm2/test_export.py @@ -44,9 +44,12 @@ def test_basic_output(self): qc.barrier(qr2) qc.cx(qr2[1], qr1[0]) qc.h(qr2[1]) - qc.x(qr2[1]).c_if(cr, 0) - qc.y(qr1[0]).c_if(cr, 1) - qc.z(qr1[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + qc.x(qr2[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.y(qr1[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.z(qr1[0]).c_if(cr, 2) qc.barrier(qr1, qr2) qc.measure(qr1[0], cr[0]) qc.measure(qr2[0], cr[1]) @@ -616,7 +619,8 @@ def test_rotation_angles_close_to_pi(self): def test_raises_on_single_bit_condition(self): qc = QuantumCircuit(1, 1) - qc.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, True) with self.assertRaisesRegex( qasm2.QASM2ExportError, "OpenQASM 2 can only condition on registers" diff --git a/test/python/qasm2/test_structure.py b/test/python/qasm2/test_structure.py index a5e5bbd77329..8f3c6aaabd75 100644 --- a/test/python/qasm2/test_structure.py +++ b/test/python/qasm2/test_structure.py @@ -256,11 +256,14 @@ def test_conditioned(self): if (cond == 0) U(0, 0, 0) q[0]; if (cond == 1) CX q[1], q[0]; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) cond = ClassicalRegister(1, "cond") qc = QuantumCircuit(QuantumRegister(2, "q"), cond) - qc.u(0, 0, 0, 0).c_if(cond, 0) - qc.cx(1, 0).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, 0).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 0).c_if(cond, 1) self.assertEqual(parsed, qc) def test_conditioned_broadcast(self): @@ -271,15 +274,20 @@ def test_conditioned_broadcast(self): if (cond == 0) U(0, 0, 0) q1; if (cond == 1) CX q1[0], q2; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) cond = ClassicalRegister(1, "cond") q1 = QuantumRegister(2, "q1") q2 = QuantumRegister(2, "q2") qc = QuantumCircuit(q1, q2, cond) - qc.u(0, 0, 0, q1[0]).c_if(cond, 0) - qc.u(0, 0, 0, q1[1]).c_if(cond, 0) - qc.cx(q1[0], q2[0]).c_if(cond, 1) - qc.cx(q1[0], q2[1]).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, q1[0]).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, q1[1]).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(q1[0], q2[0]).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(q1[0], q2[1]).c_if(cond, 1) self.assertEqual(parsed, qc) def test_constant_folding(self): @@ -338,19 +346,29 @@ def test_huge_conditions(self): if (cond=={bigint}) measure qr[0] -> cr[0]; if (cond=={bigint}) measure qr -> cr; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) qr, cr = QuantumRegister(2, "qr"), ClassicalRegister(2, "cr") cond = ClassicalRegister(500, "cond") qc = QuantumCircuit(qr, cr, cond) - qc.u(0, 0, 0, qr[0]).c_if(cond, bigint) - qc.u(0, 0, 0, qr[0]).c_if(cond, bigint) - qc.u(0, 0, 0, qr[1]).c_if(cond, bigint) - qc.reset(qr[0]).c_if(cond, bigint) - qc.reset(qr[0]).c_if(cond, bigint) - qc.reset(qr[1]).c_if(cond, bigint) - qc.measure(qr[0], cr[0]).c_if(cond, bigint) - qc.measure(qr[0], cr[0]).c_if(cond, bigint) - qc.measure(qr[1], cr[1]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, qr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, qr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, qr[1]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.reset(qr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.reset(qr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.reset(qr[1]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.measure(qr[0], cr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.measure(qr[0], cr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.measure(qr[1], cr[1]).c_if(cond, bigint) self.assertEqual(parsed, qc) @@ -383,14 +401,16 @@ def test_conditioned(self): creg cond[1]; if (cond == 0) not_bell q[0], q[1]; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) not_bell_def = QuantumCircuit([Qubit(), Qubit()]) not_bell_def.u(0, 0, 0, 0) not_bell_def.cx(0, 1) not_bell = gate_builder("not_bell", [], not_bell_def) cond = ClassicalRegister(1, "cond") qc = QuantumCircuit(QuantumRegister(2, "q"), cond) - qc.append(not_bell().c_if(cond, 0), [0, 1]) + with self.assertWarns(DeprecationWarning): + qc.append(not_bell().c_if(cond, 0), [0, 1]) self.assertEqual(parsed, qc) def test_constant_folding_in_definition(self): @@ -735,21 +755,25 @@ def test_deepcopy_conditioned_defined_gate(self): creg c[1]; if (c == 1) my_gate q[0]; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) my_gate = parsed.data[0].operation self.assertEqual(my_gate.name, "my_gate") - self.assertEqual(my_gate.condition, (parsed.cregs[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(my_gate.condition, (parsed.cregs[0], 1)) copied = copy.deepcopy(parsed) copied_gate = copied.data[0].operation self.assertEqual(copied_gate.name, "my_gate") - self.assertEqual(copied_gate.condition, (copied.cregs[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied_gate.condition, (copied.cregs[0], 1)) pickled = pickle.loads(pickle.dumps(parsed)) pickled_gate = pickled.data[0].operation self.assertEqual(pickled_gate.name, "my_gate") - self.assertEqual(pickled_gate.condition, (pickled.cregs[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(pickled_gate.condition, (pickled.cregs[0], 1)) class TestOpaque(QiskitTestCase): @@ -904,12 +928,16 @@ def test_conditioned(self): if (cond == 0) measure q[0] -> c[0]; if (cond == 1) measure q -> c; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) cond = ClassicalRegister(1, "cond") qc = QuantumCircuit(QuantumRegister(2, "q"), ClassicalRegister(2, "c"), cond) - qc.measure(0, 0).c_if(cond, 0) - qc.measure(0, 0).c_if(cond, 1) - qc.measure(1, 1).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.measure(0, 0).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.measure(0, 0).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.measure(1, 1).c_if(cond, 1) self.assertEqual(parsed, qc) def test_broadcast_against_empty_register(self): @@ -991,12 +1019,16 @@ def test_conditioned(self): if (cond == 0) reset q[0]; if (cond == 1) reset q; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) cond = ClassicalRegister(1, "cond") qc = QuantumCircuit(QuantumRegister(2, "q"), cond) - qc.reset(0).c_if(cond, 0) - qc.reset(0).c_if(cond, 1) - qc.reset(1).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.reset(0).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.reset(0).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.reset(1).c_if(cond, 1) self.assertEqual(parsed, qc) def test_broadcast_against_empty_register(self): diff --git a/test/python/qasm3/test_export.py b/test/python/qasm3/test_export.py index eefc0a2cc53c..acae1d7d3cd7 100644 --- a/test/python/qasm3/test_export.py +++ b/test/python/qasm3/test_export.py @@ -104,9 +104,12 @@ def test_regs_conds_qasm(self): qc.measure(qr1[0], cr[0]) qc.measure(qr2[0], cr[1]) qc.measure(qr2[1], cr[2]) - qc.x(qr2[1]).c_if(cr, 0) - qc.y(qr1[0]).c_if(cr, 1) - qc.z(qr1[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + qc.x(qr2[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.y(qr1[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.z(qr1[0]).c_if(cr, 2) expected_qasm = "\n".join( [ "OPENQASM 3.0;", @@ -723,8 +726,10 @@ def test_teleportation(self): qc.barrier() qc.measure([0, 1], [0, 1]) qc.barrier() - qc.x(2).c_if(qc.clbits[1], 1) - qc.z(2).c_if(qc.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(qc.clbits[1], 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(qc.clbits[0], 1) transpiled = transpile(qc, initial_layout=[0, 1, 2]) expected_qasm = """\ @@ -780,8 +785,10 @@ def test_basis_gates(self): qc.barrier() qc.measure([0, 1], [0, 1]) qc.barrier() - qc.x(2).c_if(qc.clbits[1], 1) - qc.z(2).c_if(qc.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(qc.clbits[1], 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(qc.clbits[0], 1) transpiled = transpile(qc, initial_layout=[0, 1, 2]) expected_qasm = """\ @@ -2046,11 +2053,15 @@ def test_unusual_conditions(self): qc = QuantumCircuit(3, 2) qc.h(0) qc.measure(0, 0) - qc.measure(1, 1).c_if(0, True) - qc.reset([0, 1]).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.measure(1, 1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.reset([0, 1]).c_if(0, True) with qc.while_loop((qc.clbits[0], True)): - qc.break_loop().c_if(0, True) - qc.continue_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.continue_loop().c_if(0, True) # Terra forbids delay and barrier from being conditioned through `c_if`, but in theory they # should work fine in a dynamic-circuits sense (although what a conditional barrier _means_ # is a whole other kettle of fish). diff --git a/test/python/quantum_info/operators/symplectic/test_clifford.py b/test/python/quantum_info/operators/symplectic/test_clifford.py index e78feaf3b78f..253bc15852b6 100644 --- a/test/python/quantum_info/operators/symplectic/test_clifford.py +++ b/test/python/quantum_info/operators/symplectic/test_clifford.py @@ -387,7 +387,8 @@ def test_barrier_delay_sim(self): def test_from_circuit_with_conditional_gate(self): """Test initialization from circuit with conditional gate.""" qc = QuantumCircuit(2, 1) - qc.h(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(0, 0) qc.cx(0, 1) with self.assertRaises(QiskitError): diff --git a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py index 65f19eb8e44c..3f96cd32e15f 100644 --- a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py +++ b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py @@ -19,7 +19,7 @@ import numpy as np import rustworkx as rx import scipy.sparse -from ddt import ddt +import ddt from qiskit import QiskitError from qiskit.circuit import Parameter, ParameterExpression, ParameterVector @@ -141,19 +141,49 @@ def test_sparse_pauli_op_init(self): self.assertEqual(spp_op, ref_op) -@ddt +@ddt.ddt class TestSparsePauliOpConversions(QiskitTestCase): """Tests SparsePauliOp representation conversions.""" - def test_from_operator(self): + @ddt.data(1, 2, 4) + def test_from_operator_single(self, num_qubits): """Test from_operator methods.""" - for tup in it.product(["I", "X", "Y", "Z"], repeat=2): + for tup in it.product(["I", "X", "Y", "Z"], repeat=num_qubits): label = "".join(tup) with self.subTest(msg=label): spp_op = SparsePauliOp.from_operator(Operator(pauli_mat(label))) np.testing.assert_array_equal(spp_op.coeffs, [1]) self.assertEqual(spp_op.paulis, PauliList(label)) + @ddt.data( + SparsePauliOp.from_sparse_list([("", (), 1.0), ("X", (0,), -2.0j)], num_qubits=1), + SparsePauliOp.from_sparse_list([("", (), 1.0), ("Y", (0,), -2.0j)], num_qubits=1), + SparsePauliOp.from_sparse_list([("Y", (0,), 1.0), ("Z", (0,), -2.0j)], num_qubits=1), + SparsePauliOp.from_sparse_list( + [("Y", (0,), 1.0), ("YY", (1, 0), -0.5), ("YYY", (2, 1, 0), 1j)], num_qubits=3 + ), + SparsePauliOp.from_sparse_list( + [("XZ", (2, 0), 1.0), ("YZ", (1, 0), -0.5), ("ZZ", (2, 1), 1j)], num_qubits=3 + ), + ) + def test_from_operator_roundtrip(self, op): + """Test `SparsePauliOp.from_operator` roundtrips things correctly.""" + # Ensure canonical order of the input. Part of this test is ensuring that the output is + # given in canonical order too. The coefficients in the inputs are chosen to be simple + # multiples of powers of two, so there are no floating-point rounding or associativity + # concerns. + op = op.simplify().sort() + roundtrip = SparsePauliOp.from_operator(op.to_matrix()) + self.assertEqual(roundtrip, op) + + def test_from_operator_tolerance(self): + """Test that terms whose coefficient falls below the tolerance are removed.""" + operator = SparsePauliOp.from_list( + [("IIXI", 0.25), ("IIZI", -0.25j), ("IXYI", 0.5j)] + ).to_matrix() + expected = SparsePauliOp.from_list([("IXYI", 0.5j)]) + self.assertEqual(SparsePauliOp.from_operator(operator, 0.26), expected) + def test_from_list(self): """Test from_list method.""" labels = ["XXZ", "IXI", "YZZ", "III"] @@ -416,7 +446,7 @@ def bind_one(a): return np.vectorize(bind_one, otypes=[complex])(array) -@ddt +@ddt.ddt class TestSparsePauliOpMethods(QiskitTestCase): """Tests for SparsePauliOp operator methods.""" diff --git a/test/python/quantum_info/test_sparse_observable.py b/test/python/quantum_info/test_sparse_observable.py index 551ea414998e..a36b5889cb62 100644 --- a/test/python/quantum_info/test_sparse_observable.py +++ b/test/python/quantum_info/test_sparse_observable.py @@ -13,15 +13,19 @@ # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring import copy +import itertools import pickle +import random import unittest import ddt import numpy as np -from qiskit.circuit import Parameter +from qiskit import transpile +from qiskit.circuit import Measure, Parameter, library, QuantumCircuit from qiskit.exceptions import QiskitError from qiskit.quantum_info import SparseObservable, SparsePauliOp, Pauli +from qiskit.transpiler import Target from test import QiskitTestCase, combine # pylint: disable=wrong-import-order @@ -39,6 +43,24 @@ def single_cases(): ] +def lnn_target(num_qubits): + """Create a simple `Target` object with an arbitrary basis-gate set, and open-path + connectivity.""" + out = Target() + out.add_instruction(library.RZGate(Parameter("a")), {(q,): None for q in range(num_qubits)}) + out.add_instruction(library.SXGate(), {(q,): None for q in range(num_qubits)}) + out.add_instruction(Measure(), {(q,): None for q in range(num_qubits)}) + out.add_instruction( + library.CXGate(), + { + pair: None + for lower in range(num_qubits - 1) + for pair in [(lower, lower + 1), (lower + 1, lower)] + }, + ) + return out + + class AllowRightArithmetic: """Some type that implements only the right-hand-sided arithmatic operations, and allows `SparseObservable` to pass through them. @@ -1533,3 +1555,158 @@ def test_clear(self, obs): num_qubits = obs.num_qubits obs.clear() self.assertEqual(obs, SparseObservable.zero(num_qubits)) + + def test_apply_layout_list(self): + self.assertEqual( + SparseObservable.zero(5).apply_layout([4, 3, 2, 1, 0]), SparseObservable.zero(5) + ) + self.assertEqual( + SparseObservable.zero(3).apply_layout([0, 2, 1], 8), SparseObservable.zero(8) + ) + self.assertEqual( + SparseObservable.identity(2).apply_layout([1, 0]), SparseObservable.identity(2) + ) + self.assertEqual( + SparseObservable.identity(3).apply_layout([100, 10_000, 3], 100_000_000), + SparseObservable.identity(100_000_000), + ) + + terms = [ + ("ZYX", (4, 2, 1), 1j), + ("", (), -0.5), + ("+-rl01", (10, 8, 6, 4, 2, 0), 2.0), + ] + + def map_indices(terms, layout): + return [ + (terms, tuple(layout[bit] for bit in bits), coeff) for terms, bits, coeff in terms + ] + + identity = list(range(12)) + self.assertEqual( + SparseObservable.from_sparse_list(terms, num_qubits=12).apply_layout(identity), + SparseObservable.from_sparse_list(terms, num_qubits=12), + ) + # We've already tested elsewhere that `SparseObservable.from_sparse_list` produces termwise + # sorted indices, so these tests also ensure `apply_layout` is maintaining that invariant. + backwards = list(range(12))[::-1] + self.assertEqual( + SparseObservable.from_sparse_list(terms, num_qubits=12).apply_layout(backwards), + SparseObservable.from_sparse_list(map_indices(terms, backwards), num_qubits=12), + ) + shuffled = [4, 7, 1, 10, 0, 11, 3, 2, 8, 5, 6, 9] + self.assertEqual( + SparseObservable.from_sparse_list(terms, num_qubits=12).apply_layout(shuffled), + SparseObservable.from_sparse_list(map_indices(terms, shuffled), num_qubits=12), + ) + self.assertEqual( + SparseObservable.from_sparse_list(terms, num_qubits=12).apply_layout(shuffled, 100), + SparseObservable.from_sparse_list(map_indices(terms, shuffled), num_qubits=100), + ) + expanded = [78, 69, 82, 68, 32, 97, 108, 101, 114, 116, 33] + self.assertEqual( + SparseObservable.from_sparse_list(terms, num_qubits=11).apply_layout(expanded, 120), + SparseObservable.from_sparse_list(map_indices(terms, expanded), num_qubits=120), + ) + + def test_apply_layout_transpiled(self): + base = SparseObservable.from_sparse_list( + [ + ("ZYX", (4, 2, 1), 1j), + ("", (), -0.5), + ("+-r", (3, 2, 0), 2.0), + ], + num_qubits=5, + ) + + qc = QuantumCircuit(5) + initial_list = [3, 4, 0, 2, 1] + no_routing = transpile( + qc, target=lnn_target(5), initial_layout=initial_list, seed_transpiler=2024_10_25_0 + ).layout + # It's easiest here to test against the `list` form, which we verify separately and + # explicitly. + self.assertEqual(base.apply_layout(no_routing), base.apply_layout(initial_list)) + + expanded = transpile( + qc, target=lnn_target(100), initial_layout=initial_list, seed_transpiler=2024_10_25_1 + ).layout + self.assertEqual( + base.apply_layout(expanded), base.apply_layout(initial_list, num_qubits=100) + ) + + qc = QuantumCircuit(5) + qargs = list(itertools.permutations(range(5), 2)) + random.Random(2024_10_25_2).shuffle(qargs) + for pair in qargs: + qc.cx(*pair) + + routed = transpile(qc, target=lnn_target(5), seed_transpiler=2024_10_25_3).layout + self.assertEqual( + base.apply_layout(routed), + base.apply_layout(routed.final_index_layout(filter_ancillas=True)), + ) + + routed_expanded = transpile(qc, target=lnn_target(20), seed_transpiler=2024_10_25_3).layout + self.assertEqual( + base.apply_layout(routed_expanded), + base.apply_layout( + routed_expanded.final_index_layout(filter_ancillas=True), num_qubits=20 + ), + ) + + def test_apply_layout_none(self): + self.assertEqual(SparseObservable.zero(0).apply_layout(None), SparseObservable.zero(0)) + self.assertEqual(SparseObservable.zero(0).apply_layout(None, 3), SparseObservable.zero(3)) + self.assertEqual(SparseObservable.zero(5).apply_layout(None), SparseObservable.zero(5)) + self.assertEqual(SparseObservable.zero(3).apply_layout(None, 8), SparseObservable.zero(8)) + self.assertEqual( + SparseObservable.identity(0).apply_layout(None), SparseObservable.identity(0) + ) + self.assertEqual( + SparseObservable.identity(0).apply_layout(None, 8), SparseObservable.identity(8) + ) + self.assertEqual( + SparseObservable.identity(2).apply_layout(None), SparseObservable.identity(2) + ) + self.assertEqual( + SparseObservable.identity(3).apply_layout(None, 100_000_000), + SparseObservable.identity(100_000_000), + ) + + terms = [ + ("ZYX", (2, 1, 0), 1j), + ("", (), -0.5), + ("+-rl01", (10, 8, 6, 4, 2, 0), 2.0), + ] + self.assertEqual( + SparseObservable.from_sparse_list(terms, num_qubits=12).apply_layout(None), + SparseObservable.from_sparse_list(terms, num_qubits=12), + ) + self.assertEqual( + SparseObservable.from_sparse_list(terms, num_qubits=12).apply_layout( + None, num_qubits=200 + ), + SparseObservable.from_sparse_list(terms, num_qubits=200), + ) + + def test_apply_layout_failures(self): + obs = SparseObservable.from_list([("IIYI", 2.0), ("IIIX", -1j)]) + with self.assertRaisesRegex(ValueError, "duplicate"): + obs.apply_layout([0, 0, 1, 2]) + with self.assertRaisesRegex(ValueError, "does not account for all contained qubits"): + obs.apply_layout([0, 1]) + with self.assertRaisesRegex(ValueError, "less than the number of qubits"): + obs.apply_layout([0, 2, 4, 6]) + with self.assertRaisesRegex(ValueError, "cannot shrink"): + obs.apply_layout([0, 1], num_qubits=2) + with self.assertRaisesRegex(ValueError, "cannot shrink"): + obs.apply_layout(None, num_qubits=2) + + qc = QuantumCircuit(3) + qc.cx(0, 1) + qc.cx(1, 2) + qc.cx(2, 0) + layout = transpile(qc, target=lnn_target(3), seed_transpiler=2024_10_25).layout + with self.assertRaisesRegex(ValueError, "cannot shrink"): + obs.apply_layout(layout, num_qubits=2) diff --git a/test/python/result/test_mitigators.py b/test/python/result/test_mitigators.py index 8dcdedc433fe..3d04249d6f78 100644 --- a/test/python/result/test_mitigators.py +++ b/test/python/result/test_mitigators.py @@ -124,16 +124,18 @@ def test_mitigation_improvement(self): # which only supports BackendV1 at the moment: # https://github.com/Qiskit/qiskit/issues/12832 assignment_matrices = self.assignment_matrices() - mitigators = self.mitigators(assignment_matrices) + mitigators = self.mitigators(assignment_matrices) circuit, circuit_name, num_qubits = self.ghz_3_circuit() counts_ideal, counts_noise, probs_noise = self.counts_data( circuit, assignment_matrices, shots ) unmitigated_error = self.compare_results(counts_ideal, counts_noise) - unmitigated_stddev = stddev(probs_noise, shots) + with self.assertWarns(DeprecationWarning): + unmitigated_stddev = stddev(probs_noise, shots) for mitigator in mitigators: - mitigated_quasi_probs = mitigator.quasi_probabilities(counts_noise) + with self.assertWarns(DeprecationWarning): + mitigated_quasi_probs = mitigator.quasi_probabilities(counts_noise) mitigated_probs = ( mitigated_quasi_probs.nearest_probability_distribution().binary_probabilities( num_bits=num_qubits @@ -161,7 +163,7 @@ def test_expectation_improvement(self): shots = 1024 with self.assertWarns(DeprecationWarning): assignment_matrices = self.assignment_matrices() - mitigators = self.mitigators(assignment_matrices) + mitigators = self.mitigators(assignment_matrices) num_qubits = len(assignment_matrices) diagonals = [] diagonals.append("IZ0") @@ -170,20 +172,24 @@ def test_expectation_improvement(self): qubit_index = {i: i for i in range(num_qubits)} circuit, circuit_name, num_qubits = self.ghz_3_circuit() counts_ideal, counts_noise, _ = self.counts_data(circuit, assignment_matrices, shots) - probs_ideal, _ = counts_probability_vector(counts_ideal, qubit_index=qubit_index) - probs_noise, _ = counts_probability_vector(counts_noise, qubit_index=qubit_index) + with self.assertWarns(DeprecationWarning): + probs_ideal, _ = counts_probability_vector(counts_ideal, qubit_index=qubit_index) + probs_noise, _ = counts_probability_vector(counts_noise, qubit_index=qubit_index) for diagonal in diagonals: if isinstance(diagonal, str): - diagonal = str2diag(diagonal) - unmitigated_expectation, unmitigated_stddev = expval_with_stddev( - diagonal, probs_noise, shots=counts_noise.shots() - ) + with self.assertWarns(DeprecationWarning): + diagonal = str2diag(diagonal) + with self.assertWarns(DeprecationWarning): + unmitigated_expectation, unmitigated_stddev = expval_with_stddev( + diagonal, probs_noise, shots=counts_noise.shots() + ) ideal_expectation = np.dot(probs_ideal, diagonal) unmitigated_error = np.abs(ideal_expectation - unmitigated_expectation) for mitigator in mitigators: - mitigated_expectation, mitigated_stddev = mitigator.expectation_value( - counts_noise, diagonal - ) + with self.assertWarns(DeprecationWarning): + mitigated_expectation, mitigated_stddev = mitigator.expectation_value( + counts_noise, diagonal + ) mitigated_error = np.abs(ideal_expectation - mitigated_expectation) self.assertLess( mitigated_error, @@ -204,30 +210,31 @@ def test_clbits_parameter(self): shots = 10000 with self.assertWarns(DeprecationWarning): assignment_matrices = self.assignment_matrices() - mitigators = self.mitigators(assignment_matrices) + mitigators = self.mitigators(assignment_matrices) circuit, _, _ = self.first_qubit_h_3_circuit() counts_ideal, counts_noise, _ = self.counts_data(circuit, assignment_matrices, shots) counts_ideal_12 = marginal_counts(counts_ideal, [1, 2]) counts_ideal_02 = marginal_counts(counts_ideal, [0, 2]) for mitigator in mitigators: - mitigated_probs_12 = ( - mitigator.quasi_probabilities(counts_noise, qubits=[1, 2], clbits=[1, 2]) - .nearest_probability_distribution() - .binary_probabilities(num_bits=2) - ) + with self.assertWarns(DeprecationWarning): + mitigated_probs_12 = ( + mitigator.quasi_probabilities(counts_noise, qubits=[1, 2], clbits=[1, 2]) + .nearest_probability_distribution() + .binary_probabilities(num_bits=2) + ) mitigated_error = self.compare_results(counts_ideal_12, mitigated_probs_12) self.assertLess( mitigated_error, 0.001, f"Mitigator {mitigator} did not correctly marginalize for qubits 1,2", ) - - mitigated_probs_02 = ( - mitigator.quasi_probabilities(counts_noise, qubits=[0, 2], clbits=[0, 2]) - .nearest_probability_distribution() - .binary_probabilities(num_bits=2) - ) + with self.assertWarns(DeprecationWarning): + mitigated_probs_02 = ( + mitigator.quasi_probabilities(counts_noise, qubits=[0, 2], clbits=[0, 2]) + .nearest_probability_distribution() + .binary_probabilities(num_bits=2) + ) mitigated_error = self.compare_results(counts_ideal_02, mitigated_probs_02) self.assertLess( mitigated_error, @@ -240,7 +247,7 @@ def test_qubits_parameter(self): shots = 10000 with self.assertWarns(DeprecationWarning): assignment_matrices = self.assignment_matrices() - mitigators = self.mitigators(assignment_matrices) + mitigators = self.mitigators(assignment_matrices) circuit, _, _ = self.first_qubit_h_3_circuit() counts_ideal, counts_noise, _ = self.counts_data(circuit, assignment_matrices, shots) counts_ideal_012 = counts_ideal @@ -248,35 +255,36 @@ def test_qubits_parameter(self): counts_ideal_102 = Counts({"000": counts_ideal["000"], "010": counts_ideal["001"]}) for mitigator in mitigators: - mitigated_probs_012 = ( - mitigator.quasi_probabilities(counts_noise, qubits=[0, 1, 2]) - .nearest_probability_distribution() - .binary_probabilities(num_bits=3) - ) + with self.assertWarns(DeprecationWarning): + mitigated_probs_012 = ( + mitigator.quasi_probabilities(counts_noise, qubits=[0, 1, 2]) + .nearest_probability_distribution() + .binary_probabilities(num_bits=3) + ) mitigated_error = self.compare_results(counts_ideal_012, mitigated_probs_012) self.assertLess( mitigated_error, 0.001, f"Mitigator {mitigator} did not correctly handle qubit order 0, 1, 2", ) - - mitigated_probs_210 = ( - mitigator.quasi_probabilities(counts_noise, qubits=[2, 1, 0]) - .nearest_probability_distribution() - .binary_probabilities(num_bits=3) - ) + with self.assertWarns(DeprecationWarning): + mitigated_probs_210 = ( + mitigator.quasi_probabilities(counts_noise, qubits=[2, 1, 0]) + .nearest_probability_distribution() + .binary_probabilities(num_bits=3) + ) mitigated_error = self.compare_results(counts_ideal_210, mitigated_probs_210) self.assertLess( mitigated_error, 0.001, f"Mitigator {mitigator} did not correctly handle qubit order 2, 1, 0", ) - - mitigated_probs_102 = ( - mitigator.quasi_probabilities(counts_noise, qubits=[1, 0, 2]) - .nearest_probability_distribution() - .binary_probabilities(num_bits=3) - ) + with self.assertWarns(DeprecationWarning): + mitigated_probs_102 = ( + mitigator.quasi_probabilities(counts_noise, qubits=[1, 0, 2]) + .nearest_probability_distribution() + .binary_probabilities(num_bits=3) + ) mitigated_error = self.compare_results(counts_ideal_102, mitigated_probs_102) self.assertLess( mitigated_error, @@ -289,31 +297,32 @@ def test_repeated_qubits_parameter(self): shots = 10000 with self.assertWarns(DeprecationWarning): assignment_matrices = self.assignment_matrices() - mitigators = self.mitigators(assignment_matrices, qubits=[0, 1, 2]) + mitigators = self.mitigators(assignment_matrices, qubits=[0, 1, 2]) circuit, _, _ = self.first_qubit_h_3_circuit() counts_ideal, counts_noise, _ = self.counts_data(circuit, assignment_matrices, shots) counts_ideal_012 = counts_ideal counts_ideal_210 = Counts({"000": counts_ideal["000"], "100": counts_ideal["001"]}) for mitigator in mitigators: - mitigated_probs_210 = ( - mitigator.quasi_probabilities(counts_noise, qubits=[2, 1, 0]) - .nearest_probability_distribution() - .binary_probabilities(num_bits=3) - ) + with self.assertWarns(DeprecationWarning): + mitigated_probs_210 = ( + mitigator.quasi_probabilities(counts_noise, qubits=[2, 1, 0]) + .nearest_probability_distribution() + .binary_probabilities(num_bits=3) + ) mitigated_error = self.compare_results(counts_ideal_210, mitigated_probs_210) self.assertLess( mitigated_error, 0.001, f"Mitigator {mitigator} did not correctly handle qubit order 2,1,0", ) - - # checking qubit order 2,1,0 should not "overwrite" the default 0,1,2 - mitigated_probs_012 = ( - mitigator.quasi_probabilities(counts_noise) - .nearest_probability_distribution() - .binary_probabilities(num_bits=3) - ) + with self.assertWarns(DeprecationWarning): + # checking qubit order 2,1,0 should not "overwrite" the default 0,1,2 + mitigated_probs_012 = ( + mitigator.quasi_probabilities(counts_noise) + .nearest_probability_distribution() + .binary_probabilities(num_bits=3) + ) mitigated_error = self.compare_results(counts_ideal_012, mitigated_probs_012) self.assertLess( mitigated_error, @@ -328,41 +337,44 @@ def test_qubits_subset_parameter(self): shots = 10000 with self.assertWarns(DeprecationWarning): assignment_matrices = self.assignment_matrices() - mitigators = self.mitigators(assignment_matrices, qubits=[2, 4, 6]) + mitigators = self.mitigators(assignment_matrices, qubits=[2, 4, 6]) circuit, _, _ = self.first_qubit_h_3_circuit() counts_ideal, counts_noise, _ = self.counts_data(circuit, assignment_matrices, shots) counts_ideal_2 = marginal_counts(counts_ideal, [0]) counts_ideal_6 = marginal_counts(counts_ideal, [2]) for mitigator in mitigators: - mitigated_probs_2 = ( - mitigator.quasi_probabilities(counts_noise, qubits=[2]) - .nearest_probability_distribution() - .binary_probabilities(num_bits=1) - ) + with self.assertWarns(DeprecationWarning): + mitigated_probs_2 = ( + mitigator.quasi_probabilities(counts_noise, qubits=[2]) + .nearest_probability_distribution() + .binary_probabilities(num_bits=1) + ) mitigated_error = self.compare_results(counts_ideal_2, mitigated_probs_2) self.assertLess( mitigated_error, 0.001, "Mitigator {mitigator} did not correctly handle qubit subset", ) - - mitigated_probs_6 = ( - mitigator.quasi_probabilities(counts_noise, qubits=[6]) - .nearest_probability_distribution() - .binary_probabilities(num_bits=1) - ) + with self.assertWarns(DeprecationWarning): + mitigated_probs_6 = ( + mitigator.quasi_probabilities(counts_noise, qubits=[6]) + .nearest_probability_distribution() + .binary_probabilities(num_bits=1) + ) mitigated_error = self.compare_results(counts_ideal_6, mitigated_probs_6) self.assertLess( mitigated_error, 0.001, f"Mitigator {mitigator} did not correctly handle qubit subset", ) - diagonal = str2diag("ZZ") + with self.assertWarns(DeprecationWarning): + diagonal = str2diag("ZZ") ideal_expectation = 0 - mitigated_expectation, _ = mitigator.expectation_value( - counts_noise, diagonal, qubits=[2, 6] - ) + with self.assertWarns(DeprecationWarning): + mitigated_expectation, _ = mitigator.expectation_value( + counts_noise, diagonal, qubits=[2, 6] + ) mitigated_error = np.abs(ideal_expectation - mitigated_expectation) self.assertLess( mitigated_error, @@ -382,7 +394,8 @@ def test_from_backend(self): prop.value = probs[qubit_idx][0] if prop.name == "prob_meas0_prep1": prop.value = probs[qubit_idx][1] - LRM_from_backend = LocalReadoutMitigator(backend=backend) + with self.assertWarns(DeprecationWarning): + LRM_from_backend = LocalReadoutMitigator(backend=backend) mats = [] for qubit_idx in range(num_qubits): @@ -393,7 +406,8 @@ def test_from_backend(self): ] ) mats.append(mat) - LRM_from_matrices = LocalReadoutMitigator(assignment_matrices=mats) + with self.assertWarns(DeprecationWarning): + LRM_from_matrices = LocalReadoutMitigator(assignment_matrices=mats) self.assertTrue( matrix_equal( LRM_from_backend.assignment_matrix(), LRM_from_matrices.assignment_matrix() @@ -407,7 +421,8 @@ def test_error_handling(self): good_matrix_A = np.array([[0.2, 1], [0.8, 0]]) for bad_matrix in [bad_matrix_A, bad_matrix_B]: with self.assertRaises(QiskitError) as cm: - CorrelatedReadoutMitigator(bad_matrix) + with self.assertWarns(DeprecationWarning): + CorrelatedReadoutMitigator(bad_matrix) self.assertEqual( cm.exception.message, "Assignment matrix columns must be valid probability distributions", @@ -415,7 +430,8 @@ def test_error_handling(self): with self.assertRaises(QiskitError) as cm: amats = [good_matrix_A, bad_matrix_A] - LocalReadoutMitigator(amats) + with self.assertWarns(DeprecationWarning): + LocalReadoutMitigator(amats) self.assertEqual( cm.exception.message, "Assignment matrix columns must be valid probability distributions", @@ -423,7 +439,8 @@ def test_error_handling(self): with self.assertRaises(QiskitError) as cm: amats = [bad_matrix_B, good_matrix_A] - LocalReadoutMitigator(amats) + with self.assertWarns(DeprecationWarning): + LocalReadoutMitigator(amats) self.assertEqual( cm.exception.message, "Assignment matrix columns must be valid probability distributions", @@ -433,10 +450,11 @@ def test_expectation_value_endian(self): """Test that endian for expval is little.""" with self.assertWarns(DeprecationWarning): assignment_matrices = self.assignment_matrices() - mitigators = self.mitigators(assignment_matrices) + mitigators = self.mitigators(assignment_matrices) counts = Counts({"10": 3, "11": 24, "00": 74, "01": 923}) for mitigator in mitigators: - expval, _ = mitigator.expectation_value(counts, diagonal="IZ", qubits=[0, 1]) + with self.assertWarns(DeprecationWarning): + expval, _ = mitigator.expectation_value(counts, diagonal="IZ", qubits=[0, 1]) self.assertAlmostEqual(expval, -1.0, places=0) def test_quasi_probabilities_shots_passing(self): @@ -444,13 +462,16 @@ def test_quasi_probabilities_shots_passing(self): We require the number of shots to be set in the output. """ - mitigator = LocalReadoutMitigator([np.array([[0.9, 0.1], [0.1, 0.9]])], qubits=[0]) + with self.assertWarns(DeprecationWarning): + mitigator = LocalReadoutMitigator([np.array([[0.9, 0.1], [0.1, 0.9]])], qubits=[0]) counts = Counts({"10": 3, "11": 24, "00": 74, "01": 923}) - quasi_dist = mitigator.quasi_probabilities(counts) + with self.assertWarns(DeprecationWarning): + quasi_dist = mitigator.quasi_probabilities(counts) self.assertEqual(quasi_dist.shots, sum(counts.values())) # custom number of shots - quasi_dist = mitigator.quasi_probabilities(counts, shots=1025) + with self.assertWarns(DeprecationWarning): + quasi_dist = mitigator.quasi_probabilities(counts, shots=1025) self.assertEqual(quasi_dist.shots, 1025) @@ -462,12 +483,13 @@ def test_assignment_matrix(self): qubits = [7, 2, 3] with self.assertWarns(DeprecationWarning): backend = Fake5QV1() - assignment_matrices = LocalReadoutMitigator(backend=backend)._assignment_mats[0:3] + assignment_matrices = LocalReadoutMitigator(backend=backend)._assignment_mats[0:3] expected_assignment_matrix = np.kron( np.kron(assignment_matrices[2], assignment_matrices[1]), assignment_matrices[0] ) expected_mitigation_matrix = np.linalg.inv(expected_assignment_matrix) - LRM = LocalReadoutMitigator(assignment_matrices, qubits) + with self.assertWarns(DeprecationWarning): + LRM = LocalReadoutMitigator(assignment_matrices, qubits) self.assertTrue(matrix_equal(expected_mitigation_matrix, LRM.mitigation_matrix())) self.assertTrue(matrix_equal(expected_assignment_matrix, LRM.assignment_matrix())) diff --git a/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py b/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py index 3f093d18c517..38f84492ee86 100644 --- a/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py +++ b/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py @@ -304,7 +304,8 @@ def test_circuit_using_clbit(self): circuit.x(0) circuit.delay(100, 0, unit="dt") circuit.measure(0, 0) - circuit.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(1).c_if(0, 1) circuit.measure(2, 0) timed_circuit = self.time_conversion_pass(circuit) @@ -320,7 +321,8 @@ def test_circuit_using_clbit(self): ref_circuit.delay(1872, 1, unit="dt") # 2032 - 160 ref_circuit.delay(432, 2, unit="dt") # 2032 - 1600 ref_circuit.measure(0, 0) - ref_circuit.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.x(1).c_if(0, 1) ref_circuit.delay(160, 0, unit="dt") ref_circuit.measure(2, 0) diff --git a/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py b/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py index 417ff9b42212..a13ca2e12de1 100644 --- a/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py +++ b/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py @@ -82,7 +82,8 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass): """ qc = QuantumCircuit(2, 1) qc.measure(0, 0) - qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) with self.assertWarns(DeprecationWarning): @@ -92,7 +93,8 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass): expected = QuantumCircuit(2, 1) expected.measure(0, 0) expected.delay(1000, 1) # x.c_if starts after measure - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) expected.delay(200, 0) self.assertEqual(expected, scheduled) @@ -170,8 +172,10 @@ def test_c_if_on_different_qubits(self, schedule_pass): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, True) - qc.x(2).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(0, True) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) with self.assertWarns(DeprecationWarning): @@ -182,8 +186,10 @@ def test_c_if_on_different_qubits(self, schedule_pass): expected.measure(0, 0) expected.delay(1000, 1) expected.delay(1000, 2) - expected.x(1).c_if(0, True) - expected.x(2).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(2).c_if(0, True) expected.delay(200, 0) self.assertEqual(expected, scheduled) @@ -255,7 +261,8 @@ def test_measure_after_c_if(self, schedule_pass): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.measure(2, 0) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) @@ -267,7 +274,8 @@ def test_measure_after_c_if(self, schedule_pass): expected.delay(1000, 1) expected.delay(1000, 2) expected.measure(0, 0) - expected.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, 1) expected.measure(2, 0) expected.delay(1000, 0) expected.delay(800, 1) @@ -451,7 +459,8 @@ def test_measure_after_c_if_on_edge_locking(self): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.measure(2, 0) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) @@ -465,7 +474,8 @@ def test_measure_after_c_if_on_edge_locking(self): expected_asap = QuantumCircuit(3, 1) expected_asap.measure(0, 0) expected_asap.delay(1000, 1) - expected_asap.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_asap.x(1).c_if(0, 1) expected_asap.measure(2, 0) expected_asap.delay(200, 0) expected_asap.delay(200, 2) @@ -474,7 +484,8 @@ def test_measure_after_c_if_on_edge_locking(self): expected_alap = QuantumCircuit(3, 1) expected_alap.measure(0, 0) expected_alap.delay(1000, 1) - expected_alap.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_alap.x(1).c_if(0, 1) expected_alap.delay(200, 2) expected_alap.measure(2, 0) expected_alap.delay(200, 0) @@ -500,11 +511,14 @@ def test_active_reset_circuit(self, write_lat, cond_lat): """ qc = QuantumCircuit(1, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) durations = InstructionDurations([("x", None, 100), ("measure", None, 1000)]) with self.assertWarns(DeprecationWarning): @@ -519,15 +533,18 @@ def test_active_reset_circuit(self, write_lat, cond_lat): expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) self.assertEqual(expected, actual_asap) self.assertEqual(expected, actual_alap) @@ -616,15 +633,19 @@ def test_random_complicated_circuit(self): """ qc = QuantumCircuit(3, 1) qc.delay(100, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.barrier() qc.measure(2, 0) - qc.x(1).c_if(0, 0) - qc.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 0) qc.delay(300, 0) qc.cx(1, 2) qc.x(0) - qc.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 0) qc.measure(2, 0) durations = InstructionDurations( @@ -644,19 +665,23 @@ def test_random_complicated_circuit(self): expected_asap.delay(100, 0) # due to conditional latency of 200dt expected_asap.delay(300, 1) expected_asap.delay(300, 2) - expected_asap.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_asap.x(0).c_if(0, 1) expected_asap.barrier() expected_asap.delay(1400, 0) expected_asap.delay(1200, 1) expected_asap.measure(2, 0) - expected_asap.x(1).c_if(0, 0) - expected_asap.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.x(0).c_if(0, 0) expected_asap.delay(300, 0) expected_asap.x(0) expected_asap.delay(300, 2) expected_asap.cx(1, 2) expected_asap.delay(400, 1) - expected_asap.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.cx(0, 1).c_if(0, 0) expected_asap.delay(700, 0) # creg is released at t0 of cx(0,1).c_if(0,0) expected_asap.delay( 700, 1 @@ -671,20 +696,24 @@ def test_random_complicated_circuit(self): expected_alap.delay(100, 0) # due to conditional latency of 200dt expected_alap.delay(300, 1) expected_alap.delay(300, 2) - expected_alap.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_alap.x(0).c_if(0, 1) expected_alap.barrier() expected_alap.delay(1400, 0) expected_alap.delay(1200, 1) expected_alap.measure(2, 0) - expected_alap.x(1).c_if(0, 0) - expected_alap.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.x(0).c_if(0, 0) expected_alap.delay(300, 0) expected_alap.x(0) expected_alap.delay(300, 1) expected_alap.delay(600, 2) expected_alap.cx(1, 2) expected_alap.delay(100, 1) - expected_alap.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.cx(0, 1).c_if(0, 0) expected_alap.measure(2, 0) expected_alap.delay(700, 0) expected_alap.delay(700, 1) @@ -722,8 +751,10 @@ def test_dag_introduces_extra_dependency_between_conditionals(self): """ qc = QuantumCircuit(2, 1) qc.delay(100, 0) - qc.x(0).c_if(0, True) - qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) durations = InstructionDurations([("x", None, 160)]) with self.assertWarns(DeprecationWarning): @@ -733,8 +764,10 @@ def test_dag_introduces_extra_dependency_between_conditionals(self): expected = QuantumCircuit(2, 1) expected.delay(100, 0) expected.delay(100, 1) # due to extra dependency on clbits - expected.x(0).c_if(0, True) - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) self.assertEqual(expected, scheduled) diff --git a/test/python/transpiler/test_barrier_before_final_measurements.py b/test/python/transpiler/test_barrier_before_final_measurements.py index eb4f509e5301..3aaa5c7895cd 100644 --- a/test/python/transpiler/test_barrier_before_final_measurements.py +++ b/test/python/transpiler/test_barrier_before_final_measurements.py @@ -175,13 +175,15 @@ def test_preserve_measure_for_conditional(self): circuit.h(qr0) circuit.measure(qr0, cr0) - circuit.z(qr1).c_if(cr0, 1) + with self.assertWarns(DeprecationWarning): + circuit.z(qr1).c_if(cr0, 1) circuit.measure(qr1, cr1) expected = QuantumCircuit(qr0, qr1, cr0, cr1) expected.h(qr0) expected.measure(qr0, cr0) - expected.z(qr1).c_if(cr0, 1) + with self.assertWarns(DeprecationWarning): + expected.z(qr1).c_if(cr0, 1) expected.barrier(qr0, qr1) expected.measure(qr1, cr1) @@ -377,17 +379,23 @@ def test_conditioned_on_single_bit(self): circuit = QuantumCircuit(QuantumRegister(3), ClassicalRegister(2), [Clbit()]) circuit.h(range(3)) circuit.measure(range(3), range(3)) - circuit.h(0).c_if(circuit.cregs[0], 3) - circuit.h(1).c_if(circuit.clbits[-1], True) - circuit.h(2).c_if(circuit.clbits[-1], False) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(circuit.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + circuit.h(1).c_if(circuit.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + circuit.h(2).c_if(circuit.clbits[-1], False) circuit.measure(range(3), range(3)) expected = circuit.copy_empty_like() expected.h(range(3)) expected.measure(range(3), range(3)) - expected.h(0).c_if(expected.cregs[0], 3) - expected.h(1).c_if(expected.clbits[-1], True) - expected.h(2).c_if(expected.clbits[-1], False) + with self.assertWarns(DeprecationWarning): + expected.h(0).c_if(expected.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + expected.h(1).c_if(expected.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + expected.h(2).c_if(expected.clbits[-1], False) expected.barrier(range(3)) expected.measure(range(3), range(3)) diff --git a/test/python/transpiler/test_basis_translator.py b/test/python/transpiler/test_basis_translator.py index 9dae6c3f283a..820c4f241a1e 100644 --- a/test/python/transpiler/test_basis_translator.py +++ b/test/python/transpiler/test_basis_translator.py @@ -576,9 +576,12 @@ def test_unroll_1q_chain_conditional(self): circuit.rz(0.3, qr) circuit.rx(0.1, qr) circuit.measure(qr, cr) - circuit.x(qr).c_if(cr, 1) - circuit.y(qr).c_if(cr, 1) - circuit.z(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.y(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.z(qr).c_if(cr, 1) dag = circuit_to_dag(circuit) pass_ = UnrollCustomDefinitions(std_eqlib, ["u1", "u2", "u3"]) dag = pass_.run(dag) @@ -609,9 +612,12 @@ def test_unroll_1q_chain_conditional(self): ref_circuit.append(U1Gate(0.3), [qr[0]]) ref_circuit.append(U3Gate(0.1, -pi / 2, pi / 2), [qr[0]]) ref_circuit.measure(qr[0], cr[0]) - ref_circuit.append(U3Gate(pi, 0, pi), [qr[0]]).c_if(cr, 1) - ref_circuit.append(U3Gate(pi, pi / 2, pi / 2), [qr[0]]).c_if(cr, 1) - ref_circuit.append(U1Gate(pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U3Gate(pi, 0, pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U3Gate(pi, pi / 2, pi / 2), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U1Gate(pi), [qr[0]]).c_if(cr, 1) ref_dag = circuit_to_dag(ref_circuit) self.assertEqual(unrolled_dag, ref_dag) @@ -1073,7 +1079,8 @@ def test_condition_set_substitute_node(self): circ.h(0) circ.cx(0, 1) circ.measure(1, 1) - circ.h(0).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circ.h(0).c_if(cr, 1) circ_transpiled = transpile(circ, optimization_level=3, basis_gates=["cx", "id", "u"]) # ┌────────────┐ ┌────────────┐ @@ -1089,7 +1096,8 @@ def test_condition_set_substitute_node(self): expected.u(pi / 2, 0, pi, 0) expected.cx(0, 1) expected.measure(1, 1) - expected.u(pi / 2, 0, pi, 0).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + expected.u(pi / 2, 0, pi, 0).c_if(cr, 1) self.assertEqual(circ_transpiled, expected) diff --git a/test/python/transpiler/test_check_map.py b/test/python/transpiler/test_check_map.py index 2a88b272c74d..866eb9f4dfae 100644 --- a/test/python/transpiler/test_check_map.py +++ b/test/python/transpiler/test_check_map.py @@ -268,7 +268,8 @@ def test_nested_conditional_unusual_bit_order(self): # should all be fine. This kind of thing is a staple of the control-flow builders. inner_order = [cr2[0], cr1[0], cr2[1], cr1[1]] inner = QuantumCircuit(qr, inner_order, cr1, cr2) - inner.cx(0, 1).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + inner.cx(0, 1).c_if(cr2, 3) outer = QuantumCircuit(qr, cr1, cr2) outer.if_test((cr1, 3), inner, outer.qubits, inner_order) diff --git a/test/python/transpiler/test_clifford_passes.py b/test/python/transpiler/test_clifford_passes.py index 66a7fd15ad10..2a39c45bc48a 100644 --- a/test/python/transpiler/test_clifford_passes.py +++ b/test/python/transpiler/test_clifford_passes.py @@ -653,7 +653,8 @@ def test_do_not_merge_conditional_gates(self): qc.cx(1, 0) qc.x(0) qc.x(1) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.x(0) qc.x(1) qc.cx(0, 1) @@ -664,7 +665,8 @@ def test_do_not_merge_conditional_gates(self): self.assertEqual(qct.count_ops()["clifford"], 2) # Make sure that the condition on the middle gate is not lost - self.assertIsNotNone(qct.data[1].operation.condition) + with self.assertWarns(DeprecationWarning): + self.assertIsNotNone(qct.data[1].operation.condition) def test_collect_with_cliffords(self): """Make sure that collecting Clifford gates and replacing them by Clifford diff --git a/test/python/transpiler/test_collect_2q_blocks.py b/test/python/transpiler/test_collect_2q_blocks.py index 69efc210a9f6..362765101f08 100644 --- a/test/python/transpiler/test_collect_2q_blocks.py +++ b/test/python/transpiler/test_collect_2q_blocks.py @@ -129,11 +129,15 @@ def test_do_not_merge_conditioned_gates(self): qc = QuantumCircuit(qr, cr) qc.p(0.1, 0) - qc.p(0.2, 0).c_if(cr, 0) - qc.p(0.3, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.p(0.2, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.p(0.3, 0).c_if(cr, 0) qc.cx(0, 1) - qc.cx(1, 0).c_if(cr, 0) - qc.cx(0, 1).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(cr, 1) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) diff --git a/test/python/transpiler/test_collect_multiq_blocks.py b/test/python/transpiler/test_collect_multiq_blocks.py index e2446d03da2a..2d4bc8783764 100644 --- a/test/python/transpiler/test_collect_multiq_blocks.py +++ b/test/python/transpiler/test_collect_multiq_blocks.py @@ -127,7 +127,8 @@ def test_block_with_classical_register(self): if(c0==0) u1(0.25*pi) q[1]; if(c0==0) u2(0.25*pi, 0.25*pi) q[0]; """ - qc = QuantumCircuit.from_qasm_str(qasmstr) + with self.assertWarns(DeprecationWarning): + qc = QuantumCircuit.from_qasm_str(qasmstr) pass_manager = PassManager() pass_manager.append(CollectMultiQBlocks()) @@ -166,11 +167,15 @@ def test_do_not_merge_conditioned_gates(self): qc = QuantumCircuit(qr, cr) qc.p(0.1, 0) - qc.p(0.2, 0).c_if(cr, 0) - qc.p(0.3, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.p(0.2, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.p(0.3, 0).c_if(cr, 0) qc.cx(0, 1) - qc.cx(1, 0).c_if(cr, 0) - qc.cx(0, 1).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(cr, 1) pass_manager = PassManager() pass_manager.append(CollectMultiQBlocks()) diff --git a/test/python/transpiler/test_commutative_cancellation.py b/test/python/transpiler/test_commutative_cancellation.py index 88f1d99bef31..15b7f6705787 100644 --- a/test/python/transpiler/test_commutative_cancellation.py +++ b/test/python/transpiler/test_commutative_cancellation.py @@ -564,7 +564,8 @@ def test_conditional_gates_dont_commute(self): circuit.h(0) circuit.measure(0, 0) circuit.cx(1, 2) - circuit.cx(1, 2).c_if(circuit.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + circuit.cx(1, 2).c_if(circuit.cregs[0], 0) circuit.measure([1, 2], [0, 1]) new_pm = PassManager(CommutativeCancellation()) @@ -677,8 +678,10 @@ def test_basic_classical_wires(self): """Test that transpile runs without internal errors when dealing with commutable operations with classical controls. Regression test for gh-8553.""" original = QuantumCircuit(2, 1) - original.x(0).c_if(original.cregs[0], 0) - original.x(1).c_if(original.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + original.x(0).c_if(original.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + original.x(1).c_if(original.cregs[0], 0) # This transpilation shouldn't change anything, but it should succeed. At one point it was # triggering an internal logic error and crashing. transpiled = PassManager([CommutativeCancellation()]).run(original) diff --git a/test/python/transpiler/test_commutative_inverse_cancellation.py b/test/python/transpiler/test_commutative_inverse_cancellation.py index cd800a3bb46f..e9e85c5505f3 100644 --- a/test/python/transpiler/test_commutative_inverse_cancellation.py +++ b/test/python/transpiler/test_commutative_inverse_cancellation.py @@ -13,15 +13,16 @@ """Test transpiler pass that cancels inverse gates while exploiting the commutation relations.""" import unittest +from test import QiskitTestCase # pylint: disable=wrong-import-order + import numpy as np -from ddt import ddt, data +from ddt import data, ddt from qiskit.circuit import Parameter, QuantumCircuit from qiskit.circuit.library import RZGate, UnitaryGate +from qiskit.quantum_info import Operator from qiskit.transpiler import PassManager from qiskit.transpiler.passes import CommutativeInverseCancellation -from qiskit.quantum_info import Operator -from test import QiskitTestCase # pylint: disable=wrong-import-order @ddt @@ -414,7 +415,8 @@ def test_conditional_gates_dont_commute(self, matrix_based): circuit.h(0) circuit.measure(0, 0) circuit.cx(1, 2) - circuit.cx(1, 2).c_if(circuit.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + circuit.cx(1, 2).c_if(circuit.cregs[0], 0) circuit.measure([1, 2], [0, 1]) passmanager = PassManager(CommutativeInverseCancellation(matrix_based=matrix_based)) @@ -758,25 +760,26 @@ def test_no_cancellation_across_reset(self, matrix_based): self.assertEqual(circuit, new_circuit) @data(False, True) - def test_no_cancellation_across_parameterized_gates(self, matrix_based): - """Test that parameterized gates prevent cancellation. - This test should be modified when inverse and commutativity checking - get improved to handle parameterized gates. - """ + def test_cancellation_across_parameterized_gates(self, matrix_based): + """Test that parameterized gates do not prevent cancellation.""" + theta = Parameter("Theta") circuit = QuantumCircuit(1) circuit.rz(np.pi / 2, 0) - circuit.rz(Parameter("Theta"), 0) + circuit.rz(theta, 0) circuit.rz(-np.pi / 2, 0) + expected_circuit = QuantumCircuit(1) + expected_circuit.rz(theta, 0) + passmanager = PassManager(CommutativeInverseCancellation(matrix_based=matrix_based)) new_circuit = passmanager.run(circuit) - self.assertEqual(circuit, new_circuit) + self.assertEqual(expected_circuit, new_circuit) @data(False, True) def test_parameterized_gates_do_not_cancel(self, matrix_based): """Test that parameterized gates do not cancel. - This test should be modified when inverse and commutativity checking - get improved to handle parameterized gates. + This test should be modified when inverse checking + gets improved to handle parameterized gates. """ gate = RZGate(Parameter("Theta")) diff --git a/test/python/transpiler/test_consolidate_blocks.py b/test/python/transpiler/test_consolidate_blocks.py index 1984ad1a3dc4..3b3aff1db228 100644 --- a/test/python/transpiler/test_consolidate_blocks.py +++ b/test/python/transpiler/test_consolidate_blocks.py @@ -328,7 +328,8 @@ def test_classical_conditions_maintained(self): This issue was raised in #2752 """ qc = QuantumCircuit(1, 1) - qc.h(0).c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(qc.cregs[0], 1) qc.measure(0, 0) pass_manager = PassManager() diff --git a/test/python/transpiler/test_convert_conditions_to_if_ops.py b/test/python/transpiler/test_convert_conditions_to_if_ops.py index 799a71163590..d1eadda0d92d 100644 --- a/test/python/transpiler/test_convert_conditions_to_if_ops.py +++ b/test/python/transpiler/test_convert_conditions_to_if_ops.py @@ -26,13 +26,17 @@ def test_simple_loose_bits(self): base = QuantumCircuit(bits) base.h(0) - base.x(0).c_if(0, 1) - base.z(1).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + base.z(1).c_if(1, 0) base.measure(0, 0) base.measure(1, 1) base.h(0) - base.x(0).c_if(0, 1) - base.cx(0, 1).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + base.cx(0, 1).c_if(1, 0) expected = QuantumCircuit(bits) expected.h(0) @@ -48,8 +52,8 @@ def test_simple_loose_bits(self): with expected.if_test((expected.clbits[1], False)): expected.cx(0, 1) expected = canonicalize_control_flow(expected) - - output = PassManager([ConvertConditionsToIfOps()]).run(base) + with self.assertWarns(DeprecationWarning): + output = PassManager([ConvertConditionsToIfOps()]).run(base) self.assertEqual(output, expected) def test_simple_registers(self): @@ -58,13 +62,17 @@ def test_simple_registers(self): base = QuantumCircuit(*registers) base.h(0) - base.x(0).c_if(base.cregs[0], 1) - base.z(1).c_if(base.cregs[1], 0) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(base.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + base.z(1).c_if(base.cregs[1], 0) base.measure(0, 0) base.measure(1, 2) base.h(0) - base.x(0).c_if(base.cregs[0], 1) - base.cx(0, 1).c_if(base.cregs[1], 0) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(base.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + base.cx(0, 1).c_if(base.cregs[1], 0) expected = QuantumCircuit(*registers) expected.h(0) @@ -81,7 +89,8 @@ def test_simple_registers(self): expected.cx(0, 1) expected = canonicalize_control_flow(expected) - output = PassManager([ConvertConditionsToIfOps()]).run(base) + with self.assertWarns(DeprecationWarning): + output = PassManager([ConvertConditionsToIfOps()]).run(base) self.assertEqual(output, expected) def test_nested_control_flow(self): @@ -91,14 +100,18 @@ def test_nested_control_flow(self): registers = [QuantumRegister(3), ClassicalRegister(2)] base = QuantumCircuit(*registers, bits) - base.x(0).c_if(bits[0], False) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(bits[0], False) with base.if_test((base.cregs[0], 0)) as else_: - base.z(1).c_if(bits[0], False) + with self.assertWarns(DeprecationWarning): + base.z(1).c_if(bits[0], False) with else_: - base.z(1).c_if(base.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + base.z(1).c_if(base.cregs[0], 1) with base.for_loop(range(2)): with base.while_loop((base.cregs[0], 1)): - base.cx(1, 2).c_if(base.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + base.cx(1, 2).c_if(base.cregs[0], 1) base = canonicalize_control_flow(base) expected = QuantumCircuit(*registers, bits) @@ -115,8 +128,8 @@ def test_nested_control_flow(self): with expected.if_test((expected.cregs[0], 1)): expected.cx(1, 2) expected = canonicalize_control_flow(expected) - - output = PassManager([ConvertConditionsToIfOps()]).run(base) + with self.assertWarns(DeprecationWarning): + output = PassManager([ConvertConditionsToIfOps()]).run(base) self.assertEqual(output, expected) def test_no_op(self): @@ -135,5 +148,6 @@ def test_no_op(self): with base.while_loop((base.cregs[0], 1)): base.cx(1, 2) base = canonicalize_control_flow(base) - output = PassManager([ConvertConditionsToIfOps()]).run(base) + with self.assertWarns(DeprecationWarning): + output = PassManager([ConvertConditionsToIfOps()]).run(base) self.assertEqual(output, base) diff --git a/test/python/transpiler/test_decompose.py b/test/python/transpiler/test_decompose.py index 64f08ec52682..64a9b97440ba 100644 --- a/test/python/transpiler/test_decompose.py +++ b/test/python/transpiler/test_decompose.py @@ -117,15 +117,19 @@ def test_decompose_conditional(self): qr = QuantumRegister(1, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr).c_if(cr, 1) - circuit.x(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr).c_if(cr, 1) dag = circuit_to_dag(circuit) pass_ = Decompose(HGate) after_dag = pass_.run(dag) ref_circuit = QuantumCircuit(qr, cr) - ref_circuit.append(U2Gate(0, pi), [qr[0]]).c_if(cr, 1) - ref_circuit.x(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U2Gate(0, pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.x(qr).c_if(cr, 1) ref_dag = circuit_to_dag(ref_circuit) self.assertEqual(after_dag, ref_dag) diff --git a/test/python/transpiler/test_elide_permutations.py b/test/python/transpiler/test_elide_permutations.py index 6f3051cec0d6..edef139af2ee 100644 --- a/test/python/transpiler/test_elide_permutations.py +++ b/test/python/transpiler/test_elide_permutations.py @@ -180,7 +180,8 @@ def test_swap_condition(self): """Test swap elision doesn't touch conditioned swap.""" qc = QuantumCircuit(3, 3) qc.h(0) - qc.swap(0, 1).c_if(qc.clbits[0], 0) + with self.assertWarns(DeprecationWarning): + qc.swap(0, 1).c_if(qc.clbits[0], 0) qc.cx(0, 1) res = self.swap_pass(qc) self.assertEqual(res, qc) diff --git a/test/python/transpiler/test_gate_direction.py b/test/python/transpiler/test_gate_direction.py index c22ad4d58b41..592ce124263c 100644 --- a/test/python/transpiler/test_gate_direction.py +++ b/test/python/transpiler/test_gate_direction.py @@ -243,8 +243,10 @@ def test_preserves_conditions(self): cr = ClassicalRegister(1, "c") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[0], qr[1]).c_if(cr, 0) - circuit.cx(qr[1], qr[0]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[0], qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[1], qr[0]).c_if(cr, 0) circuit.cx(qr[0], qr[1]) circuit.cx(qr[1], qr[0]) @@ -261,16 +263,22 @@ def test_preserves_conditions(self): # c: 1/╡ 0x0 ╞╡ 0x0 ╞╡ 0x0 ╞╡ 0x0 ╞╡ 0x0 ╞╡ 0x0 ╞════════════════════ # └─────┘└─────┘└─────┘└─────┘└─────┘└─────┘ expected = QuantumCircuit(qr, cr) - expected.cx(qr[0], qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(qr[0], qr[1]).c_if(cr, 0) # Order of H gates is important because DAG comparison will consider # different conditional order on a creg to be a different circuit. # See https://github.com/Qiskit/qiskit-terra/issues/3164 - expected.h(qr[1]).c_if(cr, 0) - expected.h(qr[0]).c_if(cr, 0) - expected.cx(qr[0], qr[1]).c_if(cr, 0) - expected.h(qr[1]).c_if(cr, 0) - expected.h(qr[0]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.h(qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.h(qr[0]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(qr[0], qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.h(qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.h(qr[0]).c_if(cr, 0) expected.cx(qr[0], qr[1]) expected.h(qr[1]) diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index a7dd806e63e9..6bac509a90ec 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -45,6 +45,7 @@ QFTGate, IGate, MCXGate, + SGate, ) from qiskit.circuit.library.generalized_gates import LinearFunction from qiskit.quantum_info import Clifford, Operator, Statevector @@ -1489,6 +1490,137 @@ def test_transpile_power_high_level_object(self): for op in ops: self.assertIn(op, ["u", "cx", "ecr", "measure"]) + def test_simple_circuit(self): + """Test HLS on a simple circuit.""" + qc = QuantumCircuit(3) + qc.cz(1, 2) + pass_ = HighLevelSynthesis(basis_gates=["cx", "u"]) + qct = pass_(qc) + self.assertEqual(Operator(qc), Operator(qct)) + + def test_simple_circuit2(self): + """Test HLS on a simple circuit.""" + qc = QuantumCircuit(6) + qc.h(0) + qc.cx(0, 1) + qc.cx(1, 3) + qc.h(5) + pass_ = HighLevelSynthesis(basis_gates=["cx", "u", "h"]) + qct = pass_(qc) + self.assertEqual(Operator(qc), Operator(qct)) + + def test_circuit_with_recursive_def(self): + """Test recursive synthesis of the definition circuit.""" + inner = QuantumCircuit(2) + inner.cz(0, 1) + qc = QuantumCircuit(3) + qc.append(inner.to_gate(), [0, 2]) + pass_ = HighLevelSynthesis(basis_gates=["cx", "u"]) + qct = pass_(qc) + self.assertEqual(Operator(qc), Operator(qct)) + + def test_circuit_with_recursive_def2(self): + """Test recursive synthesis of the definition circuit.""" + inner1 = QuantumCircuit(2) + inner1.cz(0, 1) + qc = QuantumCircuit(4) + qc.append(inner1.to_instruction(), [2, 3]) + pass_ = HighLevelSynthesis(basis_gates=["cz", "cx", "u"]) + qct = pass_(qc) + self.assertEqual(Operator(qc), Operator(qct)) + + def test_circuit_with_recursive_def3(self): + """Test recursive synthesis of the definition circuit.""" + inner2 = QuantumCircuit(2) + inner2.h(0) + inner2.cx(0, 1) + + inner1 = QuantumCircuit(4) + inner1.cz(0, 1) + inner1.append(inner2.to_instruction(), [0, 2]) + + qc = QuantumCircuit(6) + qc.h(1) + qc.h(2) + qc.cz(1, 2) + qc.append(inner1.to_instruction(), [2, 0, 4, 3]) + qc.h(2) + pass_ = HighLevelSynthesis(basis_gates=["h", "z", "cx", "u"]) + qct = pass_(qc) + self.assertEqual(Operator(qc), Operator(qct)) + + def test_circuit_with_mcx(self): + """Test synthesis with plugins.""" + qc = QuantumCircuit(10) + qc.mcx([3, 4, 5, 6, 7], 2) + basis_gates = ["u", "cx"] + qct = HighLevelSynthesis(basis_gates=basis_gates)(qc) + self.assertEqual(Statevector(qc), Statevector(qct)) + + def test_circuit_with_mcx_def(self): + """Test synthesis where the plugin is called within the recursive call + on the definition.""" + circuit = QuantumCircuit(6) + circuit.mcx([0, 1, 2, 3, 4], 5) + custom_gate = circuit.to_gate() + qc = QuantumCircuit(10) + qc.append(custom_gate, [3, 4, 5, 6, 7, 2]) + basis_gates = ["u", "cx"] + qct = HighLevelSynthesis(basis_gates=basis_gates)(qc) + self.assertEqual(Statevector(qc), Statevector(qct)) + + def test_circuit_with_mcx_def_rec(self): + """Test synthesis where the plugin is called within the recursive call + on the definition.""" + inner2 = QuantumCircuit(6) + inner2.mcx([0, 1, 2, 3, 4], 5) + inner1 = QuantumCircuit(7) + inner1.append(inner2.to_gate(), [1, 2, 3, 4, 5, 6]) + qc = QuantumCircuit(10) + qc.append(inner1.to_gate(), [2, 3, 4, 5, 6, 7, 8]) + pass_ = HighLevelSynthesis(basis_gates=["h", "z", "cx", "u"]) + qct = pass_(qc) + self.assertEqual(Statevector(qc), Statevector(qct)) + + def test_annotated_gate(self): + """Test synthesis with annotated gate.""" + qc = QuantumCircuit(10) + qc.x(1) + qc.cz(1, 2) + qc.append(SGate().control(3, annotated=True), [0, 1, 8, 9]) + pass_ = HighLevelSynthesis(basis_gates=["h", "z", "cx", "u"]) + qct = pass_(qc) + self.assertEqual(Operator(qc), Operator(qct)) + + def test_annotated_circuit(self): + """Test synthesis with annotated custom gate.""" + circ = QuantumCircuit(2) + circ.h(0) + circ.cy(0, 1) + qc = QuantumCircuit(10) + qc.x(1) + qc.cz(1, 2) + qc.append(circ.to_gate().control(3, annotated=True), [2, 0, 3, 7, 8]) + pass_ = HighLevelSynthesis(basis_gates=["h", "z", "cx", "u"]) + qct = pass_(qc) + self.assertEqual(Statevector(qc), Statevector(qct)) + + def test_annotated_rec(self): + """Test synthesis with annotated custom gates and recursion.""" + inner2 = QuantumCircuit(2) + inner2.h(0) + inner2.cy(0, 1) + inner1 = QuantumCircuit(5) + inner1.h(1) + inner1.append(inner2.to_gate().control(2, annotated=True), [1, 2, 3, 4]) + qc = QuantumCircuit(10) + qc.x(1) + qc.cz(1, 2) + qc.append(inner1.to_gate().control(3, annotated=True), [9, 8, 7, 6, 5, 4, 3, 2]) + pass_ = HighLevelSynthesis(basis_gates=["h", "z", "cx", "u"]) + qct = pass_(qc) + self.assertEqual(Statevector(qc), Statevector(qct)) + class TestUnrollerCompatability(QiskitTestCase): """Tests backward compatibility with the UnrollCustomDefinitions pass. @@ -1588,9 +1720,12 @@ def test_unroll_1q_chain_conditional(self): circuit.rz(0.3, qr) circuit.rx(0.1, qr) circuit.measure(qr, cr) - circuit.x(qr).c_if(cr, 1) - circuit.y(qr).c_if(cr, 1) - circuit.z(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.y(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.z(qr).c_if(cr, 1) dag = circuit_to_dag(circuit) pass_ = HighLevelSynthesis(equivalence_library=std_eqlib, basis_gates=["u1", "u2", "u3"]) dag = pass_.run(dag) @@ -1621,9 +1756,12 @@ def test_unroll_1q_chain_conditional(self): ref_circuit.append(U1Gate(0.3), [qr[0]]) ref_circuit.append(U3Gate(0.1, -np.pi / 2, np.pi / 2), [qr[0]]) ref_circuit.measure(qr[0], cr[0]) - ref_circuit.append(U3Gate(np.pi, 0, np.pi), [qr[0]]).c_if(cr, 1) - ref_circuit.append(U3Gate(np.pi, np.pi / 2, np.pi / 2), [qr[0]]).c_if(cr, 1) - ref_circuit.append(U1Gate(np.pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U3Gate(np.pi, 0, np.pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U3Gate(np.pi, np.pi / 2, np.pi / 2), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U1Gate(np.pi), [qr[0]]).c_if(cr, 1) ref_dag = circuit_to_dag(ref_circuit) self.assertEqual(unrolled_dag, ref_dag) diff --git a/test/python/transpiler/test_linear_functions_passes.py b/test/python/transpiler/test_linear_functions_passes.py index 1d157c437155..39997ad816ef 100644 --- a/test/python/transpiler/test_linear_functions_passes.py +++ b/test/python/transpiler/test_linear_functions_passes.py @@ -615,7 +615,8 @@ def test_do_not_merge_conditional_gates(self): qc = QuantumCircuit(2, 1) qc.cx(1, 0) qc.swap(1, 0) - qc.cx(0, 1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 1) qc.cx(0, 1) qc.cx(1, 0) @@ -625,7 +626,8 @@ def test_do_not_merge_conditional_gates(self): self.assertEqual(qct.count_ops()["linear_function"], 2) # Make sure that the condition on the middle gate is not lost - self.assertIsNotNone(qct.data[1].operation.condition) + with self.assertWarns(DeprecationWarning): + self.assertIsNotNone(qct.data[1].operation.condition) @combine(do_commutative_analysis=[False, True]) def test_split_layers(self, do_commutative_analysis): diff --git a/test/python/transpiler/test_optimize_1q_decomposition.py b/test/python/transpiler/test_optimize_1q_decomposition.py index 06aab474d611..fb043ff9d950 100644 --- a/test/python/transpiler/test_optimize_1q_decomposition.py +++ b/test/python/transpiler/test_optimize_1q_decomposition.py @@ -212,16 +212,18 @@ def test_ignores_conditional_rotations(self, basis): qr = QuantumRegister(1, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.p(0.1, qr).c_if(cr, 1) - circuit.p(0.2, qr).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.p(0.1, qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.p(0.2, qr).c_if(cr, 3) circuit.p(0.3, qr) circuit.p(0.4, qr) passmanager = PassManager() passmanager.append(Optimize1qGatesDecomposition(basis)) result = passmanager.run(circuit) - - self.assertTrue(Operator(circuit).equiv(Operator(result))) + with self.assertWarns(DeprecationWarning): + self.assertTrue(Operator(circuit).equiv(Operator(result))) @ddt.data( ["cx", "u3"], diff --git a/test/python/transpiler/test_optimize_1q_gates.py b/test/python/transpiler/test_optimize_1q_gates.py index e5483dd47499..bfa578826510 100644 --- a/test/python/transpiler/test_optimize_1q_gates.py +++ b/test/python/transpiler/test_optimize_1q_gates.py @@ -162,15 +162,19 @@ def test_ignores_conditional_rotations(self): qr = QuantumRegister(1, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(U1Gate(0.1), [qr]).c_if(cr, 1) - circuit.append(U1Gate(0.2), [qr]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0.1), [qr]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0.2), [qr]).c_if(cr, 3) circuit.append(U1Gate(0.3), [qr]) circuit.append(U1Gate(0.4), [qr]) dag = circuit_to_dag(circuit) expected = QuantumCircuit(qr, cr) - expected.append(U1Gate(0.1), [qr]).c_if(cr, 1) - expected.append(U1Gate(0.2), [qr]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + expected.append(U1Gate(0.1), [qr]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + expected.append(U1Gate(0.2), [qr]).c_if(cr, 3) expected.append(U1Gate(0.7), [qr]) pass_ = Optimize1qGates() @@ -190,15 +194,19 @@ def test_ignores_conditional_rotations_phase_gates(self): qr = QuantumRegister(1, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(PhaseGate(0.1), [qr]).c_if(cr, 1) - circuit.append(PhaseGate(0.2), [qr]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.append(PhaseGate(0.1), [qr]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(PhaseGate(0.2), [qr]).c_if(cr, 3) circuit.append(PhaseGate(0.3), [qr]) circuit.append(PhaseGate(0.4), [qr]) dag = circuit_to_dag(circuit) expected = QuantumCircuit(qr, cr) - expected.append(PhaseGate(0.1), [qr]).c_if(cr, 1) - expected.append(PhaseGate(0.2), [qr]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + expected.append(PhaseGate(0.1), [qr]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + expected.append(PhaseGate(0.2), [qr]).c_if(cr, 3) expected.append(PhaseGate(0.7), [qr]) pass_ = Optimize1qGates(["p", "u2", "u", "cx", "id"]) diff --git a/test/python/transpiler/test_optimize_swap_before_measure.py b/test/python/transpiler/test_optimize_swap_before_measure.py index 6d7c06f410a7..10455318bd09 100644 --- a/test/python/transpiler/test_optimize_swap_before_measure.py +++ b/test/python/transpiler/test_optimize_swap_before_measure.py @@ -363,12 +363,14 @@ def test_no_optimize_swap_with_condition(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.swap(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.swap(qr[0], qr[1]).c_if(cr, 1) circuit.measure(qr[0], cr[0]) dag = circuit_to_dag(circuit) expected = QuantumCircuit(qr, cr) - expected.swap(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + expected.swap(qr[0], qr[1]).c_if(cr, 1) expected.measure(qr[0], cr[0]) pass_ = OptimizeSwapBeforeMeasure() diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 00fd8944061c..7bc23b01e841 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -1214,7 +1214,8 @@ def test_optimization_condition(self, level): qr = QuantumRegister(2) cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr) - qc.cx(0, 1).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(cr, 1) backend = GenericBackendV2( num_qubits=20, coupling_map=TOKYO_CMAP, @@ -1227,7 +1228,8 @@ def test_optimization_condition(self, level): def test_input_dag_copy(self): """Test substitute_node_with_dag input_dag copy on condition""" qc = QuantumCircuit(2, 1) - qc.cx(0, 1).c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(qc.cregs[0], 1) qc.cx(1, 0) circ = transpile(qc, basis_gates=["u3", "cz"]) self.assertIsInstance(circ, QuantumCircuit) @@ -1284,7 +1286,10 @@ class TestGeneratePresetPassManagers(QiskitTestCase): @data(0, 1, 2, 3) def test_with_backend(self, optimization_level): """Test a passmanager is constructed when only a backend and optimization level.""" - with self.assertWarns(DeprecationWarning): + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex=r"qiskit\.providers\.models\.backendconfiguration\.GateConfig`", + ): backend = Fake20QV1() with self.assertWarnsRegex( DeprecationWarning, @@ -1316,7 +1321,10 @@ def test_default_optimization_level_target_first_pos_arg(self): def test_with_no_backend(self, optimization_level): """Test a passmanager is constructed with no backend and optimization level.""" target = GenericBackendV2(num_qubits=7, coupling_map=LAGOS_CMAP, seed=42) - with self.assertWarns(DeprecationWarning): + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): pm = generate_preset_pass_manager( optimization_level, coupling_map=target.coupling_map, diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index 474a586448b8..f560aa2c4855 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -550,7 +550,8 @@ def test_do_not_optimize_with_conditional(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.rz(0.1, qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.rz(0.1, qr[1]).c_if(cr, 1) circuit.barrier() circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) diff --git a/test/python/transpiler/test_remove_final_measurements.py b/test/python/transpiler/test_remove_final_measurements.py index 4bc0e107109b..a91687bd8f3e 100644 --- a/test/python/transpiler/test_remove_final_measurements.py +++ b/test/python/transpiler/test_remove_final_measurements.py @@ -57,7 +57,8 @@ def expected_dag(): q0 = QuantumRegister(1, "q0") c0 = ClassicalRegister(1, "c0") qc = QuantumCircuit(q0, c0) - qc.x(0).c_if(c0[0], 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(c0[0], 0) return circuit_to_dag(qc) q0 = QuantumRegister(1, "q0") @@ -65,7 +66,8 @@ def expected_dag(): qc = QuantumCircuit(q0, c0) # make c0 busy - qc.x(0).c_if(c0[0], 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(c0[0], 0) # measure into c0 qc.measure(0, c0[0]) @@ -87,7 +89,8 @@ def expected_dag(): q0 = QuantumRegister(1, "q0") c0 = ClassicalRegister(2, "c0") qc = QuantumCircuit(q0, c0) - qc.x(q0[0]).c_if(c0[0], 0) + with self.assertWarns(DeprecationWarning): + qc.x(q0[0]).c_if(c0[0], 0) return circuit_to_dag(qc) q0 = QuantumRegister(1, "q0") @@ -95,7 +98,8 @@ def expected_dag(): qc = QuantumCircuit(q0, c0) # make c0[0] busy - qc.x(q0[0]).c_if(c0[0], 0) + with self.assertWarns(DeprecationWarning): + qc.x(q0[0]).c_if(c0[0], 0) # measure into not busy c0[1] qc.measure(0, c0[1]) diff --git a/test/python/transpiler/test_reset_after_measure_simplification.py b/test/python/transpiler/test_reset_after_measure_simplification.py index 38f602443c06..976801e71353 100644 --- a/test/python/transpiler/test_reset_after_measure_simplification.py +++ b/test/python/transpiler/test_reset_after_measure_simplification.py @@ -26,12 +26,13 @@ def test_simple(self): qc = QuantumCircuit(1, 1) qc.measure(0, 0) qc.reset(0) - - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) ans_qc = QuantumCircuit(1, 1) ans_qc.measure(0, 0) - ans_qc.x(0).c_if(ans_qc.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + ans_qc.x(0).c_if(ans_qc.clbits[0], 1) self.assertEqual(new_qc, ans_qc) def test_simple_null(self): @@ -52,12 +53,13 @@ def test_simple_multi_reg(self): qc = QuantumCircuit(qr, cr1, cr2) qc.measure(0, 1) qc.reset(0) - - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) ans_qc = QuantumCircuit(qr, cr1, cr2) ans_qc.measure(0, 1) - ans_qc.x(0).c_if(cr2[0], 1) + with self.assertWarns(DeprecationWarning): + ans_qc.x(0).c_if(cr2[0], 1) self.assertEqual(new_qc, ans_qc) @@ -69,7 +71,6 @@ def test_simple_multi_reg_null(self): qc = QuantumCircuit(qr, cr1, cr2) qc.measure(0, 1) qc.reset(1) # reset not on same qubit as meas - new_qc = ResetAfterMeasureSimplification()(qc) self.assertEqual(new_qc, qc) @@ -79,12 +80,13 @@ def test_simple_multi_resets(self): qc.measure(0, 0) qc.reset(0) qc.reset(0) - - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) ans_qc = QuantumCircuit(1, 2) ans_qc.measure(0, 0) - ans_qc.x(0).c_if(ans_qc.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + ans_qc.x(0).c_if(ans_qc.clbits[0], 1) ans_qc.reset(0) self.assertEqual(new_qc, ans_qc) @@ -96,11 +98,13 @@ def test_simple_multi_resets_with_resets_before_measure(self): qc.reset(1) qc.measure(1, 1) - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) ans_qc = QuantumCircuit(2, 2) ans_qc.measure(0, 0) - ans_qc.x(0).c_if(Clbit(ClassicalRegister(2, "c"), 0), 1) + with self.assertWarns(DeprecationWarning): + ans_qc.x(0).c_if(Clbit(ClassicalRegister(2, "c"), 0), 1) ans_qc.reset(1) ans_qc.measure(1, 1) @@ -134,7 +138,8 @@ def test_bv_circuit(self): qc.reset(1) qc.x(1) qc.h(1) - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) for op in new_qc.data: if op.operation.name == "reset": self.assertEqual(op.qubits[0], new_qc.qubits[1]) @@ -149,7 +154,8 @@ def test_simple_if_else(self): base_expected = QuantumCircuit(1, 1) base_expected.measure(0, 0) - base_expected.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + base_expected.x(0).c_if(0, True) test = QuantumCircuit(1, 1) test.if_else( @@ -165,7 +171,8 @@ def test_simple_if_else(self): expected.clbits, ) - self.assertEqual(pass_(test), expected) + with self.assertWarns(DeprecationWarning): + self.assertEqual(pass_(test), expected) def test_nested_control_flow(self): """Test that the pass recurses into nested control flow.""" @@ -177,7 +184,8 @@ def test_nested_control_flow(self): base_expected = QuantumCircuit(1, 1) base_expected.measure(0, 0) - base_expected.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + base_expected.x(0).c_if(0, True) body_test = QuantumCircuit(1, 1) body_test.for_loop((0,), None, base_expected.copy(), body_test.qubits, body_test.clbits) @@ -194,5 +202,5 @@ def test_nested_control_flow(self): expected.while_loop( (expected.clbits[0], True), body_expected, expected.qubits, expected.clbits ) - - self.assertEqual(pass_(test), expected) + with self.assertWarns(DeprecationWarning): + self.assertEqual(pass_(test), expected) diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index d196e4982ea9..7cf86356ab1f 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -293,7 +293,8 @@ def test_classical_condition(self): with self.subTest("1 bit in register"): qc = QuantumCircuit(2, 1) qc.z(0) - qc.z(0).c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + qc.z(0).c_if(qc.cregs[0], 0) cm = CouplingMap([(0, 1), (1, 0)]) expected = PassManager([TrivialLayout(cm)]).run(qc) actual = PassManager([TrivialLayout(cm), SabreSwap(cm)]).run(qc) @@ -302,8 +303,10 @@ def test_classical_condition(self): cregs = [ClassicalRegister(3), ClassicalRegister(4)] qc = QuantumCircuit(QuantumRegister(2, name="q"), *cregs) qc.z(0) - qc.z(0).c_if(cregs[0], 0) - qc.z(0).c_if(cregs[1], 0) + with self.assertWarns(DeprecationWarning): + qc.z(0).c_if(cregs[0], 0) + with self.assertWarns(DeprecationWarning): + qc.z(0).c_if(cregs[1], 0) cm = CouplingMap([(0, 1), (1, 0)]) expected = PassManager([TrivialLayout(cm)]).run(qc) actual = PassManager([TrivialLayout(cm), SabreSwap(cm)]).run(qc) @@ -316,40 +319,54 @@ def test_classical_condition_cargs(self): """ with self.subTest("missing measurement"): qc = QuantumCircuit(3, 1) - qc.cx(0, 2).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 2).c_if(0, 0) qc.measure(1, 0) - qc.h(2).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.h(2).c_if(0, 0) expected = QuantumCircuit(3, 1) expected.swap(1, 2) - expected.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(0, 1).c_if(0, 0) expected.measure(2, 0) - expected.h(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.h(1).c_if(0, 0) result = SabreSwap(CouplingMap.from_line(3), seed=12345)(qc) self.assertEqual(result, expected) with self.subTest("reordered measurement"): qc = QuantumCircuit(3, 1) - qc.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 0) qc.measure(1, 0) - qc.h(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(0, 0) expected = QuantumCircuit(3, 1) - expected.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(0, 1).c_if(0, 0) expected.measure(1, 0) - expected.h(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.h(0).c_if(0, 0) result = SabreSwap(CouplingMap.from_line(3), seed=12345)(qc) self.assertEqual(result, expected) def test_conditional_measurement(self): """Test that instructions with cargs and conditions are handled correctly.""" qc = QuantumCircuit(3, 2) - qc.cx(0, 2).c_if(0, 0) - qc.measure(2, 0).c_if(1, 0) - qc.h(2).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 2).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.measure(2, 0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + qc.h(2).c_if(0, 0) qc.measure(1, 1) expected = QuantumCircuit(3, 2) expected.swap(1, 2) - expected.cx(0, 1).c_if(0, 0) - expected.measure(1, 0).c_if(1, 0) - expected.h(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.measure(1, 0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + expected.h(1).c_if(0, 0) expected.measure(2, 1) result = SabreSwap(CouplingMap.from_line(3), seed=12345)(qc) self.assertEqual(result, expected) diff --git a/test/python/transpiler/test_scheduling_padding_pass.py b/test/python/transpiler/test_scheduling_padding_pass.py index a1ae04d5e68e..f5ebc349d814 100644 --- a/test/python/transpiler/test_scheduling_padding_pass.py +++ b/test/python/transpiler/test_scheduling_padding_pass.py @@ -114,7 +114,8 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass): """ qc = QuantumCircuit(2, 1) qc.measure(0, 0) - qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) pm = PassManager([schedule_pass(durations), PadDelay()]) @@ -123,7 +124,8 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass): expected = QuantumCircuit(2, 1) expected.measure(0, 0) expected.delay(1000, 1) # x.c_if starts after measure - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) expected.delay(200, 0) self.assertEqual(expected, scheduled) @@ -200,8 +202,10 @@ def test_c_if_on_different_qubits(self, schedule_pass): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, True) - qc.x(2).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(0, True) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) pm = PassManager([schedule_pass(durations), PadDelay()]) @@ -211,8 +215,10 @@ def test_c_if_on_different_qubits(self, schedule_pass): expected.measure(0, 0) expected.delay(1000, 1) expected.delay(1000, 2) - expected.x(1).c_if(0, True) - expected.x(2).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(2).c_if(0, True) expected.delay(200, 0) self.assertEqual(expected, scheduled) @@ -283,7 +289,8 @@ def test_measure_after_c_if(self, schedule_pass): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.measure(2, 0) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) @@ -294,7 +301,8 @@ def test_measure_after_c_if(self, schedule_pass): expected.delay(1000, 1) expected.delay(1000, 2) expected.measure(0, 0) - expected.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, 1) expected.measure(2, 0) expected.delay(1000, 0) expected.delay(800, 1) @@ -474,7 +482,8 @@ def test_measure_after_c_if_on_edge_locking(self): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.measure(2, 0) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) @@ -499,7 +508,8 @@ def test_measure_after_c_if_on_edge_locking(self): expected_asap = QuantumCircuit(3, 1) expected_asap.measure(0, 0) expected_asap.delay(1000, 1) - expected_asap.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_asap.x(1).c_if(0, 1) expected_asap.measure(2, 0) expected_asap.delay(200, 0) expected_asap.delay(200, 2) @@ -508,7 +518,8 @@ def test_measure_after_c_if_on_edge_locking(self): expected_alap = QuantumCircuit(3, 1) expected_alap.measure(0, 0) expected_alap.delay(1000, 1) - expected_alap.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_alap.x(1).c_if(0, 1) expected_alap.delay(200, 2) expected_alap.measure(2, 0) expected_alap.delay(200, 0) @@ -534,11 +545,14 @@ def test_active_reset_circuit(self, write_lat, cond_lat): """ qc = QuantumCircuit(1, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) durations = InstructionDurations([("x", None, 100), ("measure", None, 1000)]) @@ -562,15 +576,18 @@ def test_active_reset_circuit(self, write_lat, cond_lat): expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) self.assertEqual(expected, actual_asap) self.assertEqual(expected, actual_alap) @@ -659,15 +676,19 @@ def test_random_complicated_circuit(self): """ qc = QuantumCircuit(3, 1) qc.delay(100, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.barrier() qc.measure(2, 0) - qc.x(1).c_if(0, 0) - qc.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 0) qc.delay(300, 0) qc.cx(1, 2) qc.x(0) - qc.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 0) qc.measure(2, 0) durations = InstructionDurations( @@ -694,19 +715,23 @@ def test_random_complicated_circuit(self): expected_asap.delay(200, 0) # due to conditional latency of 200dt expected_asap.delay(300, 1) expected_asap.delay(300, 2) - expected_asap.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_asap.x(0).c_if(0, 1) expected_asap.barrier() expected_asap.delay(1400, 0) expected_asap.delay(1200, 1) expected_asap.measure(2, 0) - expected_asap.x(1).c_if(0, 0) - expected_asap.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.x(0).c_if(0, 0) expected_asap.delay(300, 0) expected_asap.x(0) expected_asap.delay(300, 2) expected_asap.cx(1, 2) expected_asap.delay(400, 1) - expected_asap.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.cx(0, 1).c_if(0, 0) expected_asap.delay(700, 0) # creg is released at t0 of cx(0,1).c_if(0,0) expected_asap.delay( 700, 1 @@ -720,20 +745,24 @@ def test_random_complicated_circuit(self): expected_alap.delay(200, 0) # due to conditional latency of 200dt expected_alap.delay(300, 1) expected_alap.delay(300, 2) - expected_alap.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_alap.x(0).c_if(0, 1) expected_alap.barrier() expected_alap.delay(1400, 0) expected_alap.delay(1200, 1) expected_alap.measure(2, 0) - expected_alap.x(1).c_if(0, 0) - expected_alap.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.x(0).c_if(0, 0) expected_alap.delay(300, 0) expected_alap.x(0) expected_alap.delay(300, 1) expected_alap.delay(600, 2) expected_alap.cx(1, 2) expected_alap.delay(100, 1) - expected_alap.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.cx(0, 1).c_if(0, 0) expected_alap.measure(2, 0) expected_alap.delay(700, 0) expected_alap.delay(700, 1) @@ -771,8 +800,10 @@ def test_dag_introduces_extra_dependency_between_conditionals(self): """ qc = QuantumCircuit(2, 1) qc.delay(100, 0) - qc.x(0).c_if(0, True) - qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) durations = InstructionDurations([("x", None, 160)]) pm = PassManager([ASAPScheduleAnalysis(durations), PadDelay()]) @@ -781,8 +812,10 @@ def test_dag_introduces_extra_dependency_between_conditionals(self): expected = QuantumCircuit(2, 1) expected.delay(100, 0) expected.delay(100, 1) # due to extra dependency on clbits - expected.x(0).c_if(0, True) - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) self.assertEqual(expected, scheduled) diff --git a/test/python/transpiler/test_unroll_3q_or_more.py b/test/python/transpiler/test_unroll_3q_or_more.py index 27927bbbd4ad..15a90cdf7759 100644 --- a/test/python/transpiler/test_unroll_3q_or_more.py +++ b/test/python/transpiler/test_unroll_3q_or_more.py @@ -62,7 +62,8 @@ def test_decompose_conditional(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 0) dag = circuit_to_dag(circuit) pass_ = Unroll3qOrMore() after_dag = pass_.run(dag) @@ -70,7 +71,8 @@ def test_decompose_conditional(self): self.assertEqual(len(op_nodes), 15) for node in op_nodes: self.assertIn(node.name, ["h", "t", "tdg", "cx"]) - self.assertEqual(node.op.condition, (cr, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(node.op.condition, (cr, 0)) def test_decompose_unitary(self): """Test unrolling of unitary gate over 4qubits.""" diff --git a/test/python/transpiler/test_unroll_forloops.py b/test/python/transpiler/test_unroll_forloops.py index cbf8f70ef767..a44655903370 100644 --- a/test/python/transpiler/test_unroll_forloops.py +++ b/test/python/transpiler/test_unroll_forloops.py @@ -135,7 +135,8 @@ def test_skip_continue_c_if(self): circuit.h(0) circuit.cx(0, 1) circuit.measure(0, 0) - circuit.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + circuit.break_loop().c_if(0, True) passmanager = PassManager() passmanager.append(UnrollForLoops()) diff --git a/test/python/visualization/test_circuit_drawer.py b/test/python/visualization/test_circuit_drawer.py index dd69faac02cb..9eb4e3ad2c88 100644 --- a/test/python/visualization/test_circuit_drawer.py +++ b/test/python/visualization/test_circuit_drawer.py @@ -151,7 +151,8 @@ def test_wire_order(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 10) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 10) expected = "\n".join( [ @@ -183,7 +184,8 @@ def test_wire_order_cregbundle(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 10) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 10) expected = "\n".join( [ diff --git a/test/python/visualization/test_circuit_latex.py b/test/python/visualization/test_circuit_latex.py index bcf5b77d51bd..8f33f1aa3ce8 100644 --- a/test/python/visualization/test_circuit_latex.py +++ b/test/python/visualization/test_circuit_latex.py @@ -99,7 +99,8 @@ def test_4597(self): qr = QuantumRegister(3, "q") cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) - circuit.x(qr[2]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[2]).c_if(cr, 2) circuit.draw(output="latex_source", cregbundle=True) circuit_drawer(circuit, filename=filename, output="latex_source") @@ -148,8 +149,10 @@ def test_teleport(self): circuit.measure(qr[0], cr[0]) circuit.measure(qr[1], cr[1]) # Apply a correction - circuit.z(qr[2]).c_if(cr, 1) - circuit.x(qr[2]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.z(qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[2]).c_if(cr, 2) circuit.measure(qr[2], cr[2]) circuit_drawer(circuit, filename=filename, output="latex_source") @@ -206,7 +209,8 @@ def test_conditional(self): # check gates are shifted over accordingly circuit.h(qr) circuit.measure(qr, cr) - circuit.h(qr[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 2) circuit_drawer(circuit, filename=filename, output="latex_source") @@ -570,7 +574,8 @@ def test_meas_condition(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) circuit_drawer(circuit, filename=filename, output="latex_source") self.assertEqualToReference(filename) @@ -598,8 +603,10 @@ def test_cif_single_bit(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr[1], 0) - circuit.x(qr[1]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[1]).c_if(cr[0], 1) circuit_drawer(circuit, cregbundle=False, filename=filename, output="latex_source") self.assertEqualToReference(filename) @@ -611,8 +618,10 @@ def test_cif_single_bit_cregbundle(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr[1], 0) - circuit.x(qr[1]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[1]).c_if(cr[0], 1) circuit_drawer(circuit, cregbundle=True, filename=filename, output="latex_source") self.assertEqualToReference(filename) @@ -639,8 +648,10 @@ def test_measures_with_conditions(self): circuit.h(0) circuit.h(1) circuit.measure(0, cr1[1]) - circuit.measure(1, cr2[0]).c_if(cr1, 1) - circuit.h(0).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + circuit.measure(1, cr2[0]).c_if(cr1, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(cr2, 3) circuit_drawer(circuit, cregbundle=False, filename=filename1, output="latex_source") circuit_drawer(circuit, cregbundle=True, filename=filename2, output="latex_source") self.assertEqualToReference(filename1) @@ -654,7 +665,8 @@ def test_measures_with_conditions_with_bits(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(crx[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(crx[1], 0) circuit.measure(0, bits[3]) circuit_drawer(circuit, cregbundle=False, filename=filename1, output="latex_source") circuit_drawer(circuit, cregbundle=True, filename=filename2, output="latex_source") @@ -668,7 +680,8 @@ def test_conditions_with_bits_reverse(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(bits[3], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(bits[3], 0) circuit_drawer( circuit, cregbundle=False, reverse_bits=True, filename=filename, output="latex_source" ) @@ -680,7 +693,8 @@ def test_sidetext_with_condition(self): qr = QuantumRegister(2, "q") cr = ClassicalRegister(2, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) circuit_drawer(circuit, cregbundle=False, filename=filename, output="latex_source") self.assertEqualToReference(filename) @@ -703,7 +717,8 @@ def test_wire_order(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 12) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 12) circuit_drawer( circuit, cregbundle=False, diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py index e7d28aac8a9e..666255c44f23 100644 --- a/test/python/visualization/test_circuit_text_drawer.py +++ b/test/python/visualization/test_circuit_text_drawer.py @@ -388,7 +388,8 @@ def test_wire_order(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 10) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 10) self.assertEqual( str( circuit_drawer( @@ -845,7 +846,8 @@ def test_text_cu1_condition(self): qr = QuantumRegister(3, "q") cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(CU1Gate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU1Gate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_rzz_condition(self): @@ -866,7 +868,8 @@ def test_text_rzz_condition(self): qr = QuantumRegister(3, "q") cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(RZZGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(RZZGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_cp_condition(self): @@ -887,7 +890,8 @@ def test_text_cp_condition(self): qr = QuantumRegister(3, "q") cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_cu1_reverse_bits(self): @@ -1758,7 +1762,8 @@ def test_control_gate_label_with_cond_1_low(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1789,7 +1794,8 @@ def test_control_gate_label_with_cond_1_low_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1824,7 +1830,8 @@ def test_control_gate_label_with_cond_1_med(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1860,7 +1867,8 @@ def test_control_gate_label_with_cond_1_med_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1895,7 +1903,8 @@ def test_control_gate_label_with_cond_1_high(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1930,7 +1939,8 @@ def test_control_gate_label_with_cond_1_high_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1966,7 +1976,8 @@ def test_control_gate_label_with_cond_2_med_space(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -1998,7 +2009,8 @@ def test_control_gate_label_with_cond_2_med(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ctrl-h").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ctrl-h").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -2034,7 +2046,8 @@ def test_control_gate_label_with_cond_2_med_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -2071,7 +2084,8 @@ def test_control_gate_label_with_cond_2_low(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -2108,7 +2122,8 @@ def test_control_gate_label_with_cond_2_low_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -2269,8 +2284,8 @@ def test_text_conditional_1(self): " 0x1 ", ] ) - - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2309,7 +2324,8 @@ def test_text_conditional_1_bundle(self): ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2335,7 +2351,8 @@ def test_text_conditional_reverse_bits_true(self): circuit.x(0) circuit.x(0) circuit.measure(2, 1) - circuit.x(2).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2).c_if(cr, 2) expected = "\n".join( [ @@ -2386,7 +2403,8 @@ def test_text_conditional_reverse_bits_false(self): circuit.x(0) circuit.x(0) circuit.measure(2, 1) - circuit.x(2).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2).c_if(cr, 2) expected = "\n".join( [ @@ -2486,7 +2504,8 @@ def test_text_conditional_1(self): " 0x1 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2523,8 +2542,8 @@ def test_text_conditional_1_bundle(self): " └─────┘", ] ) - - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2565,7 +2584,8 @@ def test_text_measure_with_spaces(self): " 0x1 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2603,7 +2623,8 @@ def test_text_measure_with_spaces_bundle(self): " 1 └─────┘", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2694,8 +2715,10 @@ def test_text_barrier_med_compress_3(self): qc1 = ClassicalRegister(3, "cr") qc2 = ClassicalRegister(1, "cr2") circuit = QuantumCircuit(qr, qc1, qc2) - circuit.x(0).c_if(qc1, 3) - circuit.x(0).c_if(qc2[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(qc1, 3) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(qc2[0], 1) expected = "\n".join( [ @@ -2753,8 +2776,8 @@ def test_text_conditional_1_cregbundle(self): " └─────┘", ] ) - - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2790,8 +2813,8 @@ def test_text_conditional_1(self): " 0x1 ", ] ) - - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), expected, @@ -2819,7 +2842,8 @@ def test_text_conditional_2_cregbundle(self): " └─────┘", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2859,7 +2883,8 @@ def test_text_conditional_2(self): " 0x2 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), expected, @@ -2887,7 +2912,8 @@ def test_text_conditional_3_cregbundle(self): " └─────┘", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2931,7 +2957,8 @@ def test_text_conditional_3(self): " 0x3 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), expected, @@ -2959,7 +2986,8 @@ def test_text_conditional_4(self): " └─────┘", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -3007,7 +3035,8 @@ def test_text_conditional_5(self): " 0x5 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), expected, @@ -3018,7 +3047,8 @@ def test_text_conditional_cz_no_space_cregbundle(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cz(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3042,7 +3072,8 @@ def test_text_conditional_cz_no_space(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cz(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3066,7 +3097,8 @@ def test_text_conditional_cz_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cz(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3092,7 +3124,8 @@ def test_text_conditional_cz(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cz(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3118,7 +3151,8 @@ def test_text_conditional_cx_ct_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3144,7 +3178,8 @@ def test_text_conditional_cx_ct(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3170,7 +3205,8 @@ def test_text_conditional_cx_tc_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[1], qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[1], qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3196,7 +3232,8 @@ def test_text_conditional_cx_tc(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[1], qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[1], qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3222,7 +3259,8 @@ def test_text_conditional_cu3_ct_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]).c_if(cr, 1) expected = "\n".join( [ @@ -3248,7 +3286,8 @@ def test_text_conditional_cu3_ct(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]).c_if(cr, 1) expected = "\n".join( [ @@ -3274,7 +3313,8 @@ def test_text_conditional_cu3_tc_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[1], qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[1], qr[0]]).c_if(cr, 1) expected = "\n".join( [ @@ -3300,7 +3340,8 @@ def test_text_conditional_cu3_tc(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[1], qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[1], qr[0]]).c_if(cr, 1) expected = "\n".join( [ @@ -3326,7 +3367,8 @@ def test_text_conditional_ccx_cregbundle(self): qr = QuantumRegister(4, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3354,7 +3396,8 @@ def test_text_conditional_ccx(self): qr = QuantumRegister(4, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3382,7 +3425,8 @@ def test_text_conditional_ccx_no_space_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3416,7 +3460,8 @@ def test_text_conditional_ccx_no_space(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3442,7 +3487,8 @@ def test_text_conditional_h_cregbundle(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3466,7 +3512,8 @@ def test_text_conditional_h(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3490,7 +3537,8 @@ def test_text_conditional_swap_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.swap(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.swap(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3516,7 +3564,8 @@ def test_text_conditional_swap(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.swap(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.swap(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3542,7 +3591,8 @@ def test_text_conditional_cswap_cregbundle(self): qr = QuantumRegister(4, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cswap(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cswap(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3570,7 +3620,8 @@ def test_text_conditional_cswap(self): qr = QuantumRegister(4, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cswap(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cswap(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3599,7 +3650,8 @@ def test_conditional_reset_cregbundle(self): cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.reset(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.reset(qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3624,7 +3676,8 @@ def test_conditional_reset(self): cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.reset(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.reset(qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3649,7 +3702,8 @@ def test_conditional_multiplexer_cregbundle(self): qr = QuantumRegister(3, name="qr") cr = ClassicalRegister(1, "cr") qc = QuantumCircuit(qr, cr) - qc.append(cx_multiplexer.c_if(cr, 1), [qr[0], qr[1]]) + with self.assertWarns(DeprecationWarning): + qc.append(cx_multiplexer.c_if(cr, 1), [qr[0], qr[1]]) expected = "\n".join( [ @@ -3675,7 +3729,8 @@ def test_conditional_multiplexer(self): qr = QuantumRegister(3, name="qr") cr = ClassicalRegister(1, "cr") qc = QuantumCircuit(qr, cr) - qc.append(cx_multiplexer.c_if(cr, 1), [qr[0], qr[1]]) + with self.assertWarns(DeprecationWarning): + qc.append(cx_multiplexer.c_if(cr, 1), [qr[0], qr[1]]) expected = "\n".join( [ @@ -3702,7 +3757,8 @@ def test_text_conditional_measure_cregbundle(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3736,7 +3792,8 @@ def test_text_conditional_measure(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3762,8 +3819,10 @@ def test_text_bit_conditional(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr[0], 1) - circuit.h(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr[1], 0) expected = "\n".join( [ @@ -3790,8 +3849,10 @@ def test_text_bit_conditional_cregbundle(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr[0], 1) - circuit.h(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr[1], 0) expected = "\n".join( [ @@ -3826,7 +3887,8 @@ def test_text_condition_measure_bits_true(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(crx[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(crx[1], 0) circuit.measure(0, bits[3]) expected = "\n".join( @@ -3860,7 +3922,8 @@ def test_text_condition_measure_bits_false(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(crx[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(crx[1], 0) circuit.measure(0, bits[3]) expected = "\n".join( @@ -3900,7 +3963,8 @@ def test_text_conditional_reverse_bits_1(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3930,10 +3994,14 @@ def test_text_conditional_reverse_bits_2(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(3, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 6) - circuit.h(qr[1]).c_if(cr, 1) - circuit.h(qr[2]).c_if(cr, 2) - circuit.cx(0, 1).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 6) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[2]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.cx(0, 1).c_if(cr, 3) expected = "\n".join( [ @@ -3969,7 +4037,8 @@ def test_text_condition_bits_reverse(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(bits[3], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(bits[3], 0) expected = "\n".join( [ @@ -4847,9 +4916,10 @@ def test_cccz_conditional(self): qr = QuantumRegister(4, "q") cr = ClassicalRegister(1, "c") circuit = QuantumCircuit(qr, cr) - circuit.append( - ZGate().control(3, ctrl_state="101").c_if(cr, 1), [qr[0], qr[1], qr[2], qr[3]] - ) + with self.assertWarns(DeprecationWarning): + circuit.append( + ZGate().control(3, ctrl_state="101").c_if(cr, 1), [qr[0], qr[1], qr[2], qr[3]] + ) self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cch_bot(self): @@ -5908,14 +5978,16 @@ def test_if_else_with_body_specified(self): circuit.measure(0, 1) circuit.measure(1, 2) circuit.x(2) - circuit.x(2, label="XLabel").c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2, label="XLabel").c_if(cr, 2) qr2 = QuantumRegister(3, "qr2") circuit2 = QuantumCircuit(qr2, cr) circuit2.x(1) circuit2.y(1) circuit2.z(0) - circuit2.x(0, label="X1i").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit2.x(0, label="X1i").c_if(cr, 4) circuit.if_else((cr[1], 1), circuit2, None, [0, 1, 2], [0, 1, 2]) circuit.x(0, label="X1i") @@ -5981,7 +6053,8 @@ def test_if_op_nested_wire_order(self): circuit.h(0) with circuit.if_test((cr[1], 1)) as _else: - circuit.x(0, label="X c_if").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit.x(0, label="X c_if").c_if(cr, 4) with circuit.if_test((cr[2], 1)): circuit.z(0) circuit.y(1) @@ -6227,12 +6300,15 @@ def test_if_else_op_from_circuit_with_conditions(self): cr = ClassicalRegister(3, "cr") circuit = QuantumCircuit(qr, cr) circuit.h(0) - circuit.x(2).c_if(cr[1], 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2).c_if(cr[1], 2) qr2 = QuantumRegister(3, "qr2") qc2 = QuantumCircuit(qr2, cr) - qc2.x(0, label="X1").c_if(cr, 4) - qc2.x(1, label="X2").c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0, label="X1").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + qc2.x(1, label="X2").c_if(cr[1], 1) circuit.if_else((cr[1], 1), qc2, None, [0, 1, 2], [0, 1, 2]) self.assertEqual( diff --git a/test/python/visualization/test_dag_drawer.py b/test/python/visualization/test_dag_drawer.py index 56138c12cf00..9c9b11e42a68 100644 --- a/test/python/visualization/test_dag_drawer.py +++ b/test/python/visualization/test_dag_drawer.py @@ -98,7 +98,8 @@ def test_dag_drawer_with_dag_dep(self): qc.cx(0, 1) qc.cx(0, 2) qc.cx(0, 3) - qc.x(3).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + qc.x(3).c_if(cr[1], 1) qc.h(3) qc.x(4) qc.barrier(0, 1) diff --git a/test/python/visualization/test_utils.py b/test/python/visualization/test_utils.py index 1ef87994d001..7e0f91f3f993 100644 --- a/test/python/visualization/test_utils.py +++ b/test/python/visualization/test_utils.py @@ -328,9 +328,13 @@ def test_get_layered_instructions_op_with_cargs(self): qc.h(0) qc.measure(0, 0) qc_2 = QuantumCircuit(1, 1, name="add_circ") - qc_2.h(0).c_if(qc_2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc_2.h(0).c_if(qc_2.cregs[0], 1) qc_2.measure(0, 0) - qc.append(qc_2, [1], [0]) + # This append calls ends up calling .to_instruction() which calls + # .c_if() internally + with self.assertWarns(DeprecationWarning): + qc.append(qc_2, [1], [0]) (_, _, layered_ops) = _utils._get_layered_instructions(qc) diff --git a/test/python/visualization/timeline/test_core.py b/test/python/visualization/timeline/test_core.py index 16a5457e8d42..895aa48d954e 100644 --- a/test/python/visualization/timeline/test_core.py +++ b/test/python/visualization/timeline/test_core.py @@ -30,13 +30,17 @@ def setUp(self): circ.cx(0, 2) circ.cx(1, 3) - self.circ = transpile( - circ, - scheduling_method="alap", - basis_gates=["h", "cx"], - instruction_durations=[("h", 0, 200), ("cx", [0, 2], 1000), ("cx", [1, 3], 1000)], - optimization_level=0, - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + self.circ = transpile( + circ, + scheduling_method="alap", + basis_gates=["h", "cx"], + instruction_durations=[("h", 0, 200), ("cx", [0, 2], 1000), ("cx", [1, 3], 1000)], + optimization_level=0, + ) def test_time_range(self): """Test calculating time range.""" @@ -153,13 +157,17 @@ def test_multi_measurement_with_clbit_not_shown(self): circ.measure(0, 0) circ.measure(1, 1) - circ = transpile( - circ, - scheduling_method="alap", - basis_gates=[], - instruction_durations=[("measure", 0, 2000), ("measure", 1, 2000)], - optimization_level=0, - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + circ = transpile( + circ, + scheduling_method="alap", + basis_gates=[], + instruction_durations=[("measure", 0, 2000), ("measure", 1, 2000)], + optimization_level=0, + ) canvas = core.DrawerCanvas(stylesheet=self.style) canvas.formatter.update({"control.show_clbits": False}) @@ -184,13 +192,17 @@ def test_multi_measurement_with_clbit_shown(self): circ.measure(0, 0) circ.measure(1, 1) - circ = transpile( - circ, - scheduling_method="alap", - basis_gates=[], - instruction_durations=[("measure", 0, 2000), ("measure", 1, 2000)], - optimization_level=0, - ) + with self.assertWarnsRegex( + DeprecationWarning, + expected_regex="The `target` parameter should be used instead", + ): + circ = transpile( + circ, + scheduling_method="alap", + basis_gates=[], + instruction_durations=[("measure", 0, 2000), ("measure", 1, 2000)], + optimization_level=0, + ) canvas = core.DrawerCanvas(stylesheet=self.style) canvas.formatter.update({"control.show_clbits": True}) diff --git a/test/qpy_compat/process_version.sh b/test/qpy_compat/process_version.sh index c56c44f554bb..01998e01667f 100755 --- a/test/qpy_compat/process_version.sh +++ b/test/qpy_compat/process_version.sh @@ -42,14 +42,14 @@ package="$1" version="$2" our_dir="$(realpath -- "$(dirname -- "${BASH_SOURCE[0]}")")" -cache_dir="$(pwd -P)/qpy_$version" -venv_dir="$(pwd -P)/${version}" +cache_dir="$(pwd -P)/qpy_cache/$version" +venv_dir="$(pwd -P)/venvs/$package-$version" if [[ ! -d $cache_dir ]] ; then echo "Building venv for $package==$version" "$python" -m venv "$venv_dir" "$venv_dir/bin/pip" install -c "${our_dir}/qpy_test_constraints.txt" "${package}==${version}" - mkdir "$cache_dir" + mkdir -p "$cache_dir" pushd "$cache_dir" echo "Generating QPY files with $package==$version" "$venv_dir/bin/python" "${our_dir}/test_qpy.py" generate --version="$version" diff --git a/test/qpy_compat/run_tests.sh b/test/qpy_compat/run_tests.sh index 979306a83784..a6ff67b14a94 100755 --- a/test/qpy_compat/run_tests.sh +++ b/test/qpy_compat/run_tests.sh @@ -14,6 +14,7 @@ set -e set -o pipefail set -x +shopt -s nullglob # Set fixed hash seed to ensure set orders are identical between saving and # loading. @@ -21,35 +22,72 @@ export PYTHONHASHSEED=$(python -S -c "import random; print(random.randint(1, 429 echo "PYTHONHASHSEED=$PYTHONHASHSEED" our_dir="$(realpath -- "$(dirname -- "${BASH_SOURCE[0]}")")" +repo_root="$(realpath -- "$our_dir/../..")" -qiskit_venv="$(pwd -P)/qiskit_venv" +# First, prepare a wheel file for the dev version. We install several venvs with this, and while +# cargo will cache some rust artefacts, it still has to re-link each time, so the wheel build takes +# a little while. +wheel_dir="$(pwd -P)/wheels" +python -m pip wheel --no-deps --wheel-dir "$wheel_dir" "$repo_root" +all_wheels=("$wheel_dir"/*.whl) +qiskit_dev_wheel="${all_wheels[0]}" + +# Now set up a "base" development-version environment, which we'll use for most of the backwards +# compatibility checks. +qiskit_venv="$(pwd -P)/venvs/dev" qiskit_python="$qiskit_venv/bin/python" python -m venv "$qiskit_venv" + # `packaging` is needed for the `get_versions.py` script. -# symengine is pinned to 0.13 to explicitly test the migration path reusing the venv -"$qiskit_venv/bin/pip" install -c "$our_dir/../../constraints.txt" "$our_dir/../.." packaging "symengine~=0.13" +"$qiskit_venv/bin/pip" install -c "$repo_root/constraints.txt" "$qiskit_dev_wheel" packaging +# Run all of the tests of cross-Qiskit-version compatibility. "$qiskit_python" "$our_dir/get_versions.py" | parallel --colsep=" " bash "$our_dir/process_version.sh" -p "$qiskit_python" -# Test dev compatibility +# Test dev compatibility with itself. dev_version="$("$qiskit_python" -c 'import qiskit; print(qiskit.__version__)')" +mkdir -p "dev-files/base" +pushd "dev-files/base" "$qiskit_python" "$our_dir/test_qpy.py" generate --version="$dev_version" "$qiskit_python" "$our_dir/test_qpy.py" load --version="$dev_version" - -# Test dev compatibility with different symengine versions across 0.11 and 0.13 -# -# NOTE: When symengine >= 0.14.0 is released we will need to modify this to build an explicit -# symengine 0.13.0 venv instead of reusing $qiskit_venv. -# -symengine_11_venv="$(pwd -P)/qiskit_symengine_11_venv" -symengine_11_python="$symengine_11_venv/bin/python" -python -m venv "$symengine_11_venv" -"$symengine_11_venv/bin/pip" install -c "$our_dir/../../constraints.txt" "$our_dir/../.." "symengine==0.11.0" -# Load symengine 0.13.0 generated payload with symengine 0.11 -"$symengine_11_python" "$our_dir/test_qpy.py" load --version="$dev_version" -# Load symengine 0.11.0 generated payload with symengine 0.13.0 -mkdir symengine_11_qpy_files -pushd symengine_11_qpy_files -"$symengine_11_python" "$our_dir/test_qpy.py" generate --version="$dev_version" -"$qiskit_python" "$our_dir/test_qpy.py" load --version="$dev_version" popd + + +# Test dev compatibility with all supported combinations of symengine between generator and loader. +# This will likely duplicate the base dev-compatibility test, but the tests are fairly fast, and +# it's better safe than sorry with the serialisation tests. + +symengine_versions=( + '>=0.11,<0.12' + '>=0.13,<0.14' +) +symengine_venv_prefix="$(pwd -P)/venvs/dev-symengine-" +symengine_files_prefix="$(pwd -P)/dev-files/symengine-" + +# Create the venvs and QPY files for each symengine version. +for i in "${!symengine_versions[@]}"; do + specifier="${symengine_versions[$i]}" + symengine_venv="$symengine_venv_prefix$i" + files_dir="$symengine_files_prefix$i" + python -m venv "$symengine_venv" + "$symengine_venv/bin/pip" install -c "$repo_root/constraints.txt" "$qiskit_dev_wheel" "symengine$specifier" + mkdir -p "$files_dir" + pushd "$files_dir" + "$symengine_venv/bin/python" -c 'import symengine; print(symengine.__version__)' > "SYMENGINE_VERSION" + "$symengine_venv/bin/python" "$our_dir/test_qpy.py" generate --version="$dev_version" + popd +done + +# For each symengine version, try loading the QPY files from every other symengine version. +for loader_num in "${!symengine_versions[@]}"; do + loader_venv="$symengine_venv_prefix$loader_num" + loader_version="$(< "$symengine_files_prefix$loader_num/SYMENGINE_VERSION")" + for generator_num in "${!symengine_versions[@]}"; do + generator_files="$symengine_files_prefix$generator_num" + generator_version="$(< "$generator_files/SYMENGINE_VERSION")" + echo "Using symengine==$loader_version to load files generated with symengine==$generator_version" + pushd "$generator_files" + "$loader_venv/bin/python" "$our_dir/test_qpy.py" load --version="$dev_version" + popd + done +done diff --git a/test/utils/base.py b/test/utils/base.py index dded6bbda2b2..133666cfc7ad 100644 --- a/test/utils/base.py +++ b/test/utils/base.py @@ -176,6 +176,27 @@ def setUpClass(cls): module="qiskit.providers.fake_provider.fake_backend", ) + warnings.filterwarnings( + "default", + category=DeprecationWarning, + message=r".*The property.*condition.*is deprecated.*", + module="qiskit_aer", + ) + + # Remove with the condition attribute in 2.0: + warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + message=r".*The property.*condition.*is deprecated.*", + module="qiskit.visualization", + ) + warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + message=r".*The property.*condition_bits.*is deprecated.*", + module="qiskit.transpiler.passes.scheduling", + ) + allow_DeprecationWarning_message = [ r"The property ``qiskit\.circuit\.bit\.Bit\.(register|index)`` is deprecated.*", ] diff --git a/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py b/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py index 4da725c4b5d6..b51b61cb7a90 100644 --- a/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py +++ b/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py @@ -343,7 +343,8 @@ def test_conditional(self): # check gates are shifted over accordingly circuit.h(qr) circuit.measure(qr, cr) - circuit.h(qr[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 2) fname = "reg_conditional.png" self.circuit_drawer(circuit, output="mpl", filename=fname) @@ -366,8 +367,10 @@ def test_bit_conditional_with_cregbundle(self): circuit.x(qr[0]) circuit.measure(qr, cr) - circuit.h(qr[0]).c_if(cr[0], 1) - circuit.x(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[1]).c_if(cr[1], 0) fname = "bit_conditional_bundle.png" self.circuit_drawer(circuit, output="mpl", filename=fname) @@ -390,8 +393,10 @@ def test_bit_conditional_no_cregbundle(self): circuit.x(qr[0]) circuit.measure(qr, cr) - circuit.h(qr[0]).c_if(cr[0], 1) - circuit.x(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[1]).c_if(cr[1], 0) fname = "bit_conditional_no_bundle.png" self.circuit_drawer(circuit, output="mpl", filename=fname, cregbundle=False) @@ -1077,7 +1082,8 @@ def test_meas_condition(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) fname = "meas_condition.png" self.circuit_drawer(circuit, output="mpl", filename=fname) @@ -1103,7 +1109,8 @@ def test_reverse_bits_condition(self): circuit.x(0) circuit.x(0) circuit.measure(2, 1) - circuit.x(2).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2).c_if(cr, 2) fname = "reverse_bits_cond_true.png" self.circuit_drawer( @@ -1398,8 +1405,10 @@ def test_measures_with_conditions(self): circuit.h(0) circuit.h(1) circuit.measure(0, cr1[1]) - circuit.measure(1, cr2[0]).c_if(cr1, 1) - circuit.h(0).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + circuit.measure(1, cr2[0]).c_if(cr1, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(cr2, 3) fname = "measure_cond_false.png" self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) @@ -1431,7 +1440,8 @@ def test_conditions_measures_with_bits(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(crx[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(crx[1], 0) circuit.measure(0, bits[3]) fname = "measure_cond_bits_false.png" @@ -1465,8 +1475,10 @@ def test_conditional_gates_right_of_measures_with_bits(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[1]) - circuit.h(qr[1]).c_if(cr[1], 0) - circuit.h(qr[2]).c_if(cr[0], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[2]).c_if(cr[0], 0) fname = "measure_cond_bits_right.png" self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) @@ -1486,7 +1498,8 @@ def test_conditions_with_bits_reverse(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(2, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(bits[3], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(bits[3], 0) fname = "cond_bits_reverse.png" self.circuit_drawer( @@ -1507,7 +1520,8 @@ def test_sidetext_with_condition(self): qr = QuantumRegister(2, "q") cr = ClassicalRegister(2, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) fname = "sidetext_condition.png" self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) @@ -1527,22 +1541,38 @@ def test_fold_with_conditions(self): cr = ClassicalRegister(5, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 1) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 3) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 5) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 7) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 9) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 11) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 13) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 15) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 17) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 19) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 21) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 23) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 25) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 27) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 29) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 31) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 5) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 7) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 9) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 11) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 13) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 15) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 17) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 19) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 21) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 23) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 25) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 27) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 29) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 31) fname = "fold_with_conditions.png" self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) @@ -1583,7 +1613,8 @@ def test_wire_order(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 10) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 10) fname = "wire_order.png" self.circuit_drawer( @@ -1732,14 +1763,16 @@ def test_if_else_with_body(self): circuit.measure(0, 1) circuit.measure(1, 2) circuit.x(2) - circuit.x(2, label="XLabel").c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2, label="XLabel").c_if(cr, 2) qr2 = QuantumRegister(3, "qr2") qc2 = QuantumCircuit(qr2, cr) qc2.x(1) qc2.y(1) qc2.z(0) - qc2.x(0, label="X1i").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + qc2.x(0, label="X1i").c_if(cr, 4) circuit.if_else((cr[1], 1), qc2, None, [0, 1, 2], [0, 1, 2]) circuit.x(0, label="X1i") @@ -1764,7 +1797,8 @@ def test_if_else_op_nested(self): circuit.h(0) with circuit.if_test((cr[1], 1)) as _else: - circuit.x(0, label="X c_if").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit.x(0, label="X c_if").c_if(cr, 4) with circuit.if_test((cr[2], 1)): circuit.z(0) circuit.y(1) @@ -1805,7 +1839,8 @@ def test_if_else_op_wire_order(self): circuit.h(0) with circuit.if_test((cr[1], 1)) as _else: - circuit.x(0, label="X c_if").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit.x(0, label="X c_if").c_if(cr, 4) with circuit.if_test((cr[2], 1)): circuit.z(0) circuit.y(1) @@ -1852,7 +1887,8 @@ def test_if_else_op_fold(self): circuit.h(0) with circuit.if_test((cr[1], 1)) as _else: - circuit.x(0, label="X c_if").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit.x(0, label="X c_if").c_if(cr, 4) with circuit.if_test((cr[2], 1)): circuit.z(0) circuit.y(1) diff --git a/tools/build_standard_commutations.py b/tools/build_standard_commutations.py index 31f1fe03822b..2e13d741c93b 100644 --- a/tools/build_standard_commutations.py +++ b/tools/build_standard_commutations.py @@ -19,10 +19,19 @@ import itertools from functools import lru_cache from typing import List -from qiskit.circuit import Gate, CommutationChecker + import qiskit.circuit.library.standard_gates as stdg +from qiskit.circuit import CommutationChecker, Gate +from qiskit.circuit.library import PauliGate from qiskit.dagcircuit import DAGOpNode +SUPPORTED_ROTATIONS = { + "rxx": PauliGate("XX"), + "ryy": PauliGate("YY"), + "rzz": PauliGate("ZZ"), + "rzx": PauliGate("XZ"), +} + @lru_cache(maxsize=10**3) def _persistent_id(op_name: str) -> int: @@ -83,7 +92,6 @@ def _get_relative_placement(first_qargs, second_qargs) -> tuple: return tuple(qubits_g2.get(q_g0, None) for q_g0 in first_qargs) -@lru_cache(None) def _get_unparameterizable_gates() -> List[Gate]: """Retrieve a list of non-parmaterized gates with up to 3 qubits, using the python inspection module Return: @@ -95,6 +103,17 @@ def _get_unparameterizable_gates() -> List[Gate]: return [g for g in gates if len(g.params) == 0] +def _get_rotation_gates() -> List[Gate]: + """Retrieve a list of parmaterized gates we know the commutation relations of with up + to 3 qubits, using the python inspection module + Return: + A list of parameterized gates(that we know how to commute) to also be considered + in the commutation library + """ + gates = list(stdg.get_standard_gate_name_mapping().values()) + return [g for g in gates if g.name in SUPPORTED_ROTATIONS] + + def _generate_commutation_dict(considered_gates: List[Gate] = None) -> dict: """Compute the commutation relation of considered gates @@ -110,7 +129,11 @@ def _generate_commutation_dict(considered_gates: List[Gate] = None) -> dict: cc = CommutationChecker() for gate0 in considered_gates: - node0 = DAGOpNode(op=gate0, qargs=list(range(gate0.num_qubits)), cargs=[]) + node0 = DAGOpNode( + op=SUPPORTED_ROTATIONS.get(gate0.name, gate0), + qargs=list(range(gate0.num_qubits)), + cargs=[], + ) for gate1 in considered_gates: # only consider canonical entries @@ -143,12 +166,16 @@ def _generate_commutation_dict(considered_gates: List[Gate] = None) -> dict: gate1_qargs.append(next_non_overlapping_qubit_idx) next_non_overlapping_qubit_idx += 1 - node1 = DAGOpNode(op=gate1, qargs=gate1_qargs, cargs=[]) + node1 = DAGOpNode( + op=SUPPORTED_ROTATIONS.get(gate1.name, gate1), + qargs=gate1_qargs, + cargs=[], + ) # replace non-overlapping qubits with None to act as a key in the commutation library relative_placement = _get_relative_placement(node0.qargs, node1.qargs) - if not gate0.is_parameterized() and not gate1.is_parameterized(): + if not node0.op.is_parameterized() and not node1.op.is_parameterized(): # if no gate includes parameters, compute commutation relation using # matrix multiplication op1 = node0.op @@ -219,6 +246,7 @@ def _dump_commuting_dict_as_python( cgates = [ g for g in _get_unparameterizable_gates() if g.name not in ["reset", "measure", "delay"] ] + cgates += _get_rotation_gates() commutation_dict = _generate_commutation_dict(considered_gates=cgates) commutation_dict = _simplify_commuting_dict(commutation_dict) _dump_commuting_dict_as_python(commutation_dict)