diff --git a/.mergify.yml b/.mergify.yml index 10b6d2022256..50a638989a6c 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -6,4 +6,4 @@ pull_request_rules: actions: backport: branches: - - stable/1.1 + - stable/1.2 diff --git a/Cargo.lock b/Cargo.lock index c397b7999557..e3416426b4d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1157,7 +1157,7 @@ dependencies = [ [[package]] name = "qiskit-accelerate" -version = "1.2.0" +version = "1.3.0" dependencies = [ "ahash 0.8.11", "approx", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "qiskit-circuit" -version = "1.2.0" +version = "1.3.0" dependencies = [ "bytemuck", "hashbrown 0.14.5", @@ -1199,7 +1199,7 @@ dependencies = [ [[package]] name = "qiskit-pyext" -version = "1.2.0" +version = "1.3.0" dependencies = [ "pyo3", "qiskit-accelerate", @@ -1210,7 +1210,7 @@ dependencies = [ [[package]] name = "qiskit-qasm2" -version = "1.2.0" +version = "1.3.0" dependencies = [ "hashbrown 0.14.5", "pyo3", @@ -1219,7 +1219,7 @@ dependencies = [ [[package]] name = "qiskit-qasm3" -version = "1.2.0" +version = "1.3.0" dependencies = [ "hashbrown 0.14.5", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index aa6d3d82570a..a10792b9ad0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/*"] resolver = "2" [workspace.package] -version = "1.2.0" +version = "1.3.0" edition = "2021" rust-version = "1.70" # Keep in sync with README.md and rust-toolchain.toml. license = "Apache-2.0" diff --git a/crates/accelerate/src/convert_2q_block_matrix.rs b/crates/accelerate/src/convert_2q_block_matrix.rs index dd61137c54f4..12146dd9d080 100644 --- a/crates/accelerate/src/convert_2q_block_matrix.rs +++ b/crates/accelerate/src/convert_2q_block_matrix.rs @@ -126,7 +126,9 @@ pub fn change_basis(matrix: ArrayView2) -> Array2 { #[pyfunction] pub fn collect_2q_blocks_filter(node: &Bound) -> Option { - let Ok(node) = node.downcast::() else { return None }; + let Ok(node) = node.downcast::() else { + return None; + }; let node = node.borrow(); match node.instruction.op() { gate @ (OperationRef::Standard(_) | OperationRef::Gate(_)) => Some( diff --git a/crates/accelerate/src/euler_one_qubit_decomposer.rs b/crates/accelerate/src/euler_one_qubit_decomposer.rs index f42cb7f705ee..7bbb6871db0a 100644 --- a/crates/accelerate/src/euler_one_qubit_decomposer.rs +++ b/crates/accelerate/src/euler_one_qubit_decomposer.rs @@ -1045,7 +1045,9 @@ fn matmul_1q(operator: &mut [[Complex64; 2]; 2], other: Array2) { #[pyfunction] pub fn collect_1q_runs_filter(node: &Bound) -> bool { - let Ok(node) = node.downcast::() else { return false }; + let Ok(node) = node.downcast::() else { + return false; + }; let node = node.borrow(); let op = node.instruction.op(); op.num_qubits() == 1 diff --git a/crates/accelerate/src/lib.rs b/crates/accelerate/src/lib.rs index dcfbdc9f1878..4e079ea84b57 100644 --- a/crates/accelerate/src/lib.rs +++ b/crates/accelerate/src/lib.rs @@ -27,8 +27,10 @@ pub mod results; pub mod sabre; pub mod sampled_exp_val; pub mod sparse_pauli_op; +pub mod star_prerouting; pub mod stochastic_swap; pub mod synthesis; +pub mod target_transpiler; pub mod two_qubit_decompose; pub mod uc_gate; pub mod utils; diff --git a/crates/accelerate/src/sabre/mod.rs b/crates/accelerate/src/sabre/mod.rs index 3eb8ebb3a219..1229be16b723 100644 --- a/crates/accelerate/src/sabre/mod.rs +++ b/crates/accelerate/src/sabre/mod.rs @@ -14,8 +14,8 @@ mod layer; mod layout; mod neighbor_table; mod route; -mod sabre_dag; -mod swap_map; +pub mod sabre_dag; +pub mod swap_map; use hashbrown::HashMap; use numpy::{IntoPyArray, ToPyArray}; diff --git a/crates/accelerate/src/star_prerouting.rs b/crates/accelerate/src/star_prerouting.rs new file mode 100644 index 000000000000..fd2156ad2011 --- /dev/null +++ b/crates/accelerate/src/star_prerouting.rs @@ -0,0 +1,214 @@ +// 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. + +/// Type alias for a node representation. +/// Each node is represented as a tuple containing: +/// - Node id (usize) +/// - List of involved qubit indices (Vec) +/// - Set of involved classical bit indices (HashSet) +/// - Directive flag (bool) +type Nodes = (usize, Vec, HashSet, bool); + +/// Type alias for a block representation. +/// Each block is represented by a tuple containing: +/// - A boolean indicating the presence of a center (bool) +/// - A list of nodes (Vec) +type Block = (bool, Vec); + +use crate::nlayout::PhysicalQubit; +use crate::nlayout::VirtualQubit; +use crate::sabre::sabre_dag::SabreDAG; +use crate::sabre::swap_map::SwapMap; +use crate::sabre::BlockResult; +use crate::sabre::NodeBlockResults; +use crate::sabre::SabreResult; +use hashbrown::HashMap; +use hashbrown::HashSet; +use numpy::IntoPyArray; +use pyo3::prelude::*; + +/// Python function to perform star prerouting on a SabreDAG. +/// This function processes star blocks and updates the DAG and qubit mapping. +#[pyfunction] +#[pyo3(text_signature = "(dag, blocks, processing_order, /)")] +fn star_preroute( + py: Python, + dag: &mut SabreDAG, + blocks: Vec, + processing_order: Vec, +) -> (SwapMap, PyObject, NodeBlockResults, PyObject) { + let mut qubit_mapping: Vec = (0..dag.num_qubits).collect(); + let mut processed_block_ids: HashSet = HashSet::with_capacity(blocks.len()); + let last_2q_gate = processing_order.iter().rev().find(|node| node.1.len() == 2); + let mut is_first_star = true; + + // Structures for SabreResult + let mut out_map: HashMap> = + HashMap::with_capacity(dag.dag.node_count()); + let mut gate_order: Vec = Vec::with_capacity(dag.dag.node_count()); + let node_block_results: HashMap> = HashMap::new(); + + // Create a HashMap to store the node-to-block mapping + let mut node_to_block: HashMap = HashMap::with_capacity(processing_order.len()); + for (block_id, block) in blocks.iter().enumerate() { + for node in &block.1 { + node_to_block.insert(node.0, block_id); + } + } + // Store nodes where swaps will be placed. + let mut swap_locations: Vec<&Nodes> = Vec::with_capacity(processing_order.len()); + + // Process blocks, gathering swap locations and updating the gate order + for node in &processing_order { + if let Some(&block_id) = node_to_block.get(&node.0) { + // Skip if the block has already been processed + if !processed_block_ids.insert(block_id) { + continue; + } + process_block( + &blocks[block_id], + last_2q_gate, + &mut is_first_star, + &mut gate_order, + &mut swap_locations, + ); + } else { + // Apply operation for nodes not part of any block + gate_order.push(node.0); + } + } + + // Apply the swaps based on the gathered swap locations and gate order + for (index, node_id) in gate_order.iter().enumerate() { + for swap_location in &swap_locations { + if *node_id == swap_location.0 { + if let Some(next_node_id) = gate_order.get(index + 1) { + apply_swap( + &mut qubit_mapping, + &swap_location.1, + *next_node_id, + &mut out_map, + ); + } + } + } + } + + let res = SabreResult { + map: SwapMap { map: out_map }, + node_order: gate_order, + node_block_results: NodeBlockResults { + results: node_block_results, + }, + }; + + let final_res = ( + res.map, + res.node_order.into_pyarray_bound(py).into(), + res.node_block_results, + qubit_mapping.into_pyarray_bound(py).into(), + ); + + final_res +} + +/// Processes a star block, applying operations and handling swaps. +/// +/// Args: +/// +/// * `block` - A tuple containing a boolean indicating the presence of a center and a vector of nodes representing the star block. +/// * `last_2q_gate` - The last two-qubit gate in the processing order. +/// * `is_first_star` - A mutable reference to a boolean indicating if this is the first star block being processed. +/// * `gate_order` - A mutable reference to the gate order vector. +/// * `swap_locations` - A mutable reference to the nodes where swaps will be placed after +fn process_block<'a>( + block: &'a Block, + last_2q_gate: Option<&'a Nodes>, + is_first_star: &mut bool, + gate_order: &mut Vec, + swap_locations: &mut Vec<&'a Nodes>, +) { + let (has_center, sequence) = block; + + // If the block contains exactly 2 nodes, apply them directly + if sequence.len() == 2 { + for inner_node in sequence { + gate_order.push(inner_node.0); + } + return; + } + + let mut prev_qargs = None; + let mut swap_source = false; + + // Process each node in the block + for inner_node in sequence.iter() { + // Apply operation directly if it's a single-qubit operation or the same as previous qargs + if inner_node.1.len() == 1 || prev_qargs == Some(&inner_node.1) { + gate_order.push(inner_node.0); + continue; + } + + // If this is the first star and no swap source has been identified, set swap_source + if *is_first_star && !swap_source { + swap_source = *has_center; + gate_order.push(inner_node.0); + prev_qargs = Some(&inner_node.1); + continue; + } + + // Place 2q-gate and subsequent swap gate + gate_order.push(inner_node.0); + + if inner_node != last_2q_gate.unwrap() && inner_node.1.len() == 2 { + swap_locations.push(inner_node); + } + prev_qargs = Some(&inner_node.1); + } + *is_first_star = false; +} + +/// Applies a swap operation to the DAG and updates the qubit mapping. +/// +/// # Args: +/// +/// * `qubit_mapping` - A mutable reference to the qubit mapping vector. +/// * `qargs` - Qubit indices for the swap operation (node before the swap) +/// * `next_node_id` - ID of the next node in the gate order (node after the swap) +/// * `out_map` - A mutable reference to the output map. +fn apply_swap( + qubit_mapping: &mut [usize], + qargs: &[VirtualQubit], + next_node_id: usize, + out_map: &mut HashMap>, +) { + if qargs.len() == 2 { + let idx0 = qargs[0].index(); + let idx1 = qargs[1].index(); + + // Update the `qubit_mapping` and `out_map` to reflect the swap operation + qubit_mapping.swap(idx0, idx1); + out_map.insert( + next_node_id, + vec![[ + PhysicalQubit::new(qubit_mapping[idx0].try_into().unwrap()), + PhysicalQubit::new(qubit_mapping[idx1].try_into().unwrap()), + ]], + ); + } +} + +#[pymodule] +pub fn star_prerouting(m: &Bound) -> PyResult<()> { + m.add_wrapped(wrap_pyfunction!(star_preroute))?; + Ok(()) +} diff --git a/crates/accelerate/src/target_transpiler/errors.rs b/crates/accelerate/src/target_transpiler/errors.rs new file mode 100644 index 000000000000..0201e4c1fabe --- /dev/null +++ b/crates/accelerate/src/target_transpiler/errors.rs @@ -0,0 +1,34 @@ +// 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::{error::Error, fmt::Display}; + +/// Error thrown when operation key is not present in the Target +#[derive(Debug)] +pub struct TargetKeyError { + pub message: String, +} + +impl TargetKeyError { + /// Initializes the new error + pub fn new_err(message: String) -> Self { + Self { message } + } +} + +impl Display for TargetKeyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.message) + } +} + +impl Error for TargetKeyError {} diff --git a/crates/accelerate/src/target_transpiler/instruction_properties.rs b/crates/accelerate/src/target_transpiler/instruction_properties.rs new file mode 100644 index 000000000000..a7a31c87924c --- /dev/null +++ b/crates/accelerate/src/target_transpiler/instruction_properties.rs @@ -0,0 +1,72 @@ +// 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 pyo3::{prelude::*, pyclass}; + +/** + A representation of an ``InstructionProperties`` object. +*/ +#[pyclass( + subclass, + name = "BaseInstructionProperties", + module = "qiskit._accelerate.target" +)] +#[derive(Clone, Debug)] +pub struct InstructionProperties { + #[pyo3(get, set)] + pub duration: Option, + #[pyo3(get, set)] + pub error: Option, +} + +#[pymethods] +impl InstructionProperties { + /// Create a new ``BaseInstructionProperties`` object + /// + /// Args: + /// duration (Option): The duration, in seconds, of the instruction on the + /// specified set of qubits + /// error (Option): The average error rate for the instruction on the specified + /// set of qubits. + /// calibration (Option): The pulse representation of the instruction. + #[new] + #[pyo3(signature = (duration=None, error=None))] + pub fn new(_py: Python<'_>, duration: Option, error: Option) -> Self { + Self { error, duration } + } + + fn __getstate__(&self) -> PyResult<(Option, Option)> { + Ok((self.duration, self.error)) + } + + fn __setstate__(&mut self, _py: Python<'_>, state: (Option, Option)) -> PyResult<()> { + self.duration = state.0; + self.error = state.1; + Ok(()) + } + + fn __repr__(&self, _py: Python<'_>) -> String { + format!( + "InstructionProperties(duration={}, error={})", + if let Some(duration) = self.duration { + duration.to_string() + } else { + "None".to_string() + }, + if let Some(error) = self.error { + error.to_string() + } else { + "None".to_string() + } + ) + } +} diff --git a/crates/accelerate/src/target_transpiler/mod.rs b/crates/accelerate/src/target_transpiler/mod.rs new file mode 100644 index 000000000000..b5c56dc6d091 --- /dev/null +++ b/crates/accelerate/src/target_transpiler/mod.rs @@ -0,0 +1,1264 @@ +// 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. + +#![allow(clippy::too_many_arguments)] + +mod errors; +mod instruction_properties; +mod nullable_index_map; + +use std::ops::Index; + +use ahash::RandomState; + +use ahash::HashSet; +use indexmap::{IndexMap, IndexSet}; +use itertools::Itertools; +use nullable_index_map::NullableIndexMap; +use pyo3::{ + exceptions::{PyAttributeError, PyIndexError, PyKeyError, PyValueError}, + prelude::*, + pyclass, + types::{PyDict, PyList, PySet, PyTuple}, +}; + +use qiskit_circuit::circuit_instruction::OperationFromPython; +use qiskit_circuit::operations::{Operation, Param}; +use qiskit_circuit::packed_instruction::PackedOperation; +use smallvec::SmallVec; + +use crate::nlayout::PhysicalQubit; + +use errors::TargetKeyError; +use instruction_properties::InstructionProperties; + +use self::exceptions::TranspilerError; + +mod exceptions { + use pyo3::import_exception_bound; + import_exception_bound! {qiskit.exceptions, QiskitError} + import_exception_bound! {qiskit.transpiler.exceptions, TranspilerError} +} + +// Custom types +type Qargs = SmallVec<[PhysicalQubit; 2]>; +type GateMap = IndexMap; +type PropsMap = NullableIndexMap>; +type GateMapState = Vec<(String, Vec<(Option, Option)>)>; + +/// Represents a Qiskit `Gate` object or a Variadic instruction. +/// Keeps a reference to its Python instance for caching purposes. +#[derive(Debug, Clone, FromPyObject)] +pub(crate) enum TargetOperation { + Normal(NormalOperation), + Variadic(PyObject), +} + +impl IntoPy for TargetOperation { + fn into_py(self, py: Python<'_>) -> PyObject { + match self { + Self::Normal(normal) => normal.into_py(py), + Self::Variadic(variable) => variable, + } + } +} + +impl ToPyObject for TargetOperation { + fn to_object(&self, py: Python<'_>) -> PyObject { + match self { + Self::Normal(normal) => normal.to_object(py), + Self::Variadic(variable) => variable.clone_ref(py), + } + } +} + +impl TargetOperation { + fn num_qubits(&self) -> u32 { + match &self { + Self::Normal(normal) => normal.operation.view().num_qubits(), + Self::Variadic(_) => { + unreachable!("'num_qubits' property is reserved for normal operations only.") + } + } + } + + fn params(&self) -> &[Param] { + match &self { + TargetOperation::Normal(normal) => normal.params.as_slice(), + TargetOperation::Variadic(_) => &[], + } + } +} + +/// Represents a Qiskit `Gate` object, keeps a reference to its Python +/// instance for caching purposes. +#[derive(Debug, Clone)] +pub(crate) struct NormalOperation { + pub operation: PackedOperation, + pub params: SmallVec<[Param; 3]>, + op_object: PyObject, +} + +impl<'py> FromPyObject<'py> for NormalOperation { + fn extract(ob: &'py PyAny) -> PyResult { + let operation: OperationFromPython = ob.extract()?; + Ok(Self { + operation: operation.operation, + params: operation.params, + op_object: ob.into(), + }) + } +} + +impl IntoPy for NormalOperation { + fn into_py(self, py: Python<'_>) -> PyObject { + self.to_object(py) + } +} + +impl ToPyObject for NormalOperation { + fn to_object(&self, py: Python<'_>) -> PyObject { + self.op_object.clone_ref(py) + } +} + +/** +The base class for a Python ``Target`` object. Contains data representing the +constraints of a particular backend. + +The intent of this struct is to contain data that can be representable and +accessible through both Rust and Python, so it can be used for rust-based +transpiler processes. + +This structure contains duplicates of every element in the Python counterpart of +`gate_map`. Which improves access for Python while sacrificing a small amount of +memory. + */ +#[pyclass( + mapping, + subclass, + name = "BaseTarget", + module = "qiskit._accelerate.target" +)] +#[derive(Clone, Debug)] +pub(crate) struct Target { + #[pyo3(get, set)] + pub description: Option, + #[pyo3(get)] + pub num_qubits: Option, + #[pyo3(get, set)] + pub dt: Option, + #[pyo3(get, set)] + pub granularity: u32, + #[pyo3(get, set)] + pub min_length: usize, + #[pyo3(get, set)] + pub pulse_alignment: u32, + #[pyo3(get, set)] + pub acquire_alignment: u32, + #[pyo3(get, set)] + pub qubit_properties: Option>, + #[pyo3(get, set)] + pub concurrent_measurements: Option>>, + gate_map: GateMap, + #[pyo3(get)] + _gate_name_map: IndexMap, + global_operations: IndexMap, RandomState>, + variable_class_operations: IndexSet, + qarg_gate_map: NullableIndexMap>>, + non_global_strict_basis: Option>, + non_global_basis: Option>, +} + +#[pymethods] +impl Target { + /// Create a new ``Target`` object + /// + ///Args: + /// description (str): An optional string to describe the Target. + /// num_qubits (int): An optional int to specify the number of qubits + /// the backend target has. If not set it will be implicitly set + /// based on the qargs when :meth:`~qiskit.Target.add_instruction` + /// is called. Note this must be set if the backend target is for a + /// noiseless simulator that doesn't have constraints on the + /// instructions so the transpiler knows how many qubits are + /// available. + /// dt (float): The system time resolution of input signals in seconds + /// granularity (int): An integer value representing minimum pulse gate + /// resolution in units of ``dt``. A user-defined pulse gate should + /// have duration of a multiple of this granularity value. + /// min_length (int): An integer value representing minimum pulse gate + /// length in units of ``dt``. A user-defined pulse gate should be + /// longer than this length. + /// pulse_alignment (int): An integer value representing a time + /// resolution of gate instruction starting time. Gate instruction + /// should start at time which is a multiple of the alignment + /// value. + /// acquire_alignment (int): An integer value representing a time + /// resolution of measure instruction starting time. Measure + /// instruction should start at time which is a multiple of the + /// alignment value. + /// qubit_properties (list): A list of :class:`~.QubitProperties` + /// objects defining the characteristics of each qubit on the + /// target device. If specified the length of this list must match + /// the number of qubits in the target, where the index in the list + /// matches the qubit number the properties are defined for. If some + /// qubits don't have properties available you can set that entry to + /// ``None`` + /// concurrent_measurements(list): A list of sets of qubits that must be + /// measured together. This must be provided + /// as a nested list like ``[[0, 1], [2, 3, 4]]``. + ///Raises: + /// ValueError: If both ``num_qubits`` and ``qubit_properties`` are both + /// defined and the value of ``num_qubits`` differs from the length of + /// ``qubit_properties``. + #[new] + #[pyo3(signature = ( + description = None, + num_qubits = None, + dt = None, + granularity = None, + min_length = None, + pulse_alignment = None, + acquire_alignment = None, + qubit_properties = None, + concurrent_measurements = None, + ))] + fn new( + description: Option, + num_qubits: Option, + dt: Option, + granularity: Option, + min_length: Option, + pulse_alignment: Option, + acquire_alignment: Option, + qubit_properties: Option>, + concurrent_measurements: Option>>, + ) -> PyResult { + let mut num_qubits = num_qubits; + if let Some(qubit_properties) = qubit_properties.as_ref() { + if let Some(num_qubits) = num_qubits { + if num_qubits != qubit_properties.len() { + return Err(PyValueError::new_err( + "The value of num_qubits specified does not match the \ + length of the input qubit_properties list", + )); + } + } else { + num_qubits = Some(qubit_properties.len()) + } + } + Ok(Target { + description, + num_qubits, + dt, + granularity: granularity.unwrap_or(1), + min_length: min_length.unwrap_or(1), + pulse_alignment: pulse_alignment.unwrap_or(1), + acquire_alignment: acquire_alignment.unwrap_or(0), + qubit_properties, + concurrent_measurements, + gate_map: GateMap::default(), + _gate_name_map: IndexMap::default(), + variable_class_operations: IndexSet::default(), + global_operations: IndexMap::default(), + qarg_gate_map: NullableIndexMap::default(), + non_global_basis: None, + non_global_strict_basis: None, + }) + } + + /// Add a new instruction to the `Target` after it has been processed in python. + /// + /// Args: + /// instruction: An instance of `Instruction` or the class representing said instructionm + /// if representing a variadic. + /// properties: A mapping of qargs and ``InstructionProperties``. + /// name: A name assigned to the provided gate. + /// Raises: + /// AttributeError: If gate is already in map + /// TranspilerError: If an operation class is passed in for ``instruction`` and no name + /// is specified or ``properties`` is set. + #[pyo3(signature = (instruction, name, properties=None))] + fn add_instruction( + &mut self, + instruction: TargetOperation, + name: &str, + properties: Option, + ) -> PyResult<()> { + if self.gate_map.contains_key(name) { + return Err(PyAttributeError::new_err(format!( + "Instruction {:?} is already in the target", + name + ))); + } + let mut qargs_val: PropsMap; + match instruction { + TargetOperation::Variadic(_) => { + qargs_val = PropsMap::with_capacity(1); + qargs_val.extend([(None, None)].into_iter()); + self.variable_class_operations.insert(name.to_string()); + } + TargetOperation::Normal(_) => { + if let Some(mut properties) = properties { + qargs_val = PropsMap::with_capacity(properties.len()); + let inst_num_qubits = instruction.num_qubits(); + if properties.contains_key(None) { + self.global_operations + .entry(inst_num_qubits) + .and_modify(|e| { + e.insert(name.to_string()); + }) + .or_insert(HashSet::from_iter([name.to_string()])); + } + let property_keys: Vec> = + properties.keys().map(|qargs| qargs.cloned()).collect(); + for qarg in property_keys { + if let Some(qarg) = qarg.as_ref() { + if qarg.len() != inst_num_qubits as usize { + return Err(TranspilerError::new_err(format!( + "The number of qubits for {name} does not match\ + the number of qubits in the properties dictionary: {:?}", + qarg + ))); + } + self.num_qubits = + Some(self.num_qubits.unwrap_or_default().max( + qarg.iter().fold(0, |acc, x| { + if acc > x.index() { + acc + } else { + x.index() + } + }) + 1, + )); + } + let inst_properties = properties.swap_remove(qarg.as_ref()).unwrap(); + qargs_val.insert(qarg.clone(), inst_properties); + if let Some(Some(value)) = self.qarg_gate_map.get_mut(qarg.as_ref()) { + value.insert(name.to_string()); + } else { + self.qarg_gate_map + .insert(qarg, Some(HashSet::from_iter([name.to_string()]))); + } + } + } else { + qargs_val = PropsMap::with_capacity(0); + } + } + } + self._gate_name_map.insert(name.to_string(), instruction); + self.gate_map.insert(name.to_string(), qargs_val); + self.non_global_basis = None; + self.non_global_strict_basis = None; + Ok(()) + } + + /// Update the property object for an instruction qarg pair already in the `Target` + /// + /// Args: + /// instruction (str): The instruction name to update + /// qargs (tuple): The qargs to update the properties of + /// properties (InstructionProperties): The properties to set for this instruction + /// Raises: + /// KeyError: If ``instruction`` or ``qarg`` are not in the target + #[pyo3(text_signature = "(instruction, qargs, properties, /,)")] + fn update_instruction_properties( + &mut self, + instruction: String, + qargs: Option, + properties: Option, + ) -> PyResult<()> { + if !self.contains_key(&instruction) { + return Err(PyKeyError::new_err(format!( + "Provided instruction: '{:?}' not in this Target.", + &instruction + ))); + }; + let mut prop_map = self[&instruction].clone(); + if !(prop_map.contains_key(qargs.as_ref())) { + return Err(PyKeyError::new_err(format!( + "Provided qarg {:?} not in this Target for {:?}.", + &qargs.unwrap_or_default(), + &instruction + ))); + } + if let Some(e) = prop_map.get_mut(qargs.as_ref()) { + *e = properties; + } + self.gate_map + .entry(instruction) + .and_modify(|e| *e = prop_map); + Ok(()) + } + + /// Get the qargs for a given operation name + /// + /// Args: + /// operation (str): The operation name to get qargs for + /// Returns: + /// set: The set of qargs the gate instance applies to. + #[pyo3(name = "qargs_for_operation_name")] + pub fn py_qargs_for_operation_name( + &self, + py: Python, + operation: &str, + ) -> PyResult>> { + match self.qargs_for_operation_name(operation) { + Ok(option_set) => { + Ok(option_set.map(|qargs| qargs.map(|qargs| qargs.to_object(py)).collect())) + } + Err(e) => Err(PyKeyError::new_err(e.message)), + } + } + + /// Get the operation class object for a given name + /// + /// Args: + /// instruction (str): The instruction name to get the + /// :class:`~qiskit.circuit.Instruction` instance for + /// Returns: + /// qiskit.circuit.Instruction: The Instruction instance corresponding to the + /// name. This also can also be the class for globally defined variable with + /// operations. + #[pyo3(name = "operation_from_name")] + pub fn py_operation_from_name(&self, py: Python, instruction: &str) -> PyResult { + match self._operation_from_name(instruction) { + Ok(instruction) => Ok(instruction.to_object(py)), + Err(e) => Err(PyKeyError::new_err(e.message)), + } + } + + /// Get the operation class object for a specified qargs tuple + /// + /// Args: + /// qargs (tuple): A qargs tuple of the qubits to get the gates that apply + /// to it. For example, ``(0,)`` will return the set of all + /// instructions that apply to qubit 0. If set to ``None`` this will + /// return any globally defined operations in the target. + /// Returns: + /// list: The list of :class:`~qiskit.circuit.Instruction` instances + /// that apply to the specified qarg. This may also be a class if + /// a variable width operation is globally defined. + /// + /// Raises: + /// KeyError: If qargs is not in target + #[pyo3(name = "operations_for_qargs", signature=(qargs=None, /))] + pub fn py_operations_for_qargs( + &self, + py: Python, + qargs: Option, + ) -> PyResult> { + // Move to rust native once Gates are in rust + Ok(self + .py_operation_names_for_qargs(qargs)? + .into_iter() + .map(|x| self._gate_name_map[x].to_object(py)) + .collect()) + } + + /// Get the operation names for a specified qargs tuple + /// + /// Args: + /// qargs (tuple): A ``qargs`` tuple of the qubits to get the gates that apply + /// to it. For example, ``(0,)`` will return the set of all + /// instructions that apply to qubit 0. If set to ``None`` this will + /// return the names for any globally defined operations in the target. + /// Returns: + /// set: The set of operation names that apply to the specified ``qargs``. + /// + /// Raises: + /// KeyError: If ``qargs`` is not in target + #[pyo3(name = "operation_names_for_qargs", signature=(qargs=None, /))] + pub fn py_operation_names_for_qargs(&self, qargs: Option) -> PyResult> { + match self.operation_names_for_qargs(qargs.as_ref()) { + Ok(set) => Ok(set), + Err(e) => Err(PyKeyError::new_err(e.message)), + } + } + + /// Return whether the instruction (operation + qubits) is supported by the target + /// + /// Args: + /// operation_name (str): The name of the operation for the instruction. Either + /// this or ``operation_class`` must be specified, if both are specified + /// ``operation_class`` will take priority and this argument will be ignored. + /// qargs (tuple): The tuple of qubit indices for the instruction. If this is + /// not specified then this method will return ``True`` if the specified + /// operation is supported on any qubits. The typical application will + /// always have this set (otherwise it's the same as just checking if the + /// target contains the operation). Normally you would not set this argument + /// if you wanted to check more generally that the target supports an operation + /// with the ``parameters`` on any qubits. + /// operation_class (Type[qiskit.circuit.Instruction]): The operation class to check whether + /// the target supports a particular operation by class rather + /// than by name. This lookup is more expensive as it needs to + /// iterate over all operations in the target instead of just a + /// single lookup. If this is specified it will supersede the + /// ``operation_name`` argument. The typical use case for this + /// operation is to check whether a specific variant of an operation + /// is supported on the backend. For example, if you wanted to + /// check whether a :class:`~.RXGate` was supported on a specific + /// qubit with a fixed angle. That fixed angle variant will + /// typically have a name different from the object's + /// :attr:`~.Instruction.name` attribute (``"rx"``) in the target. + /// This can be used to check if any instances of the class are + /// available in such a case. + /// parameters (list): A list of parameters to check if the target + /// supports them on the specified qubits. If the instruction + /// supports the parameter values specified in the list on the + /// operation and qargs specified this will return ``True`` but + /// if the parameters are not supported on the specified + /// instruction it will return ``False``. If this argument is not + /// specified this method will return ``True`` if the instruction + /// is supported independent of the instruction parameters. If + /// specified with any :class:`~.Parameter` objects in the list, + /// that entry will be treated as supporting any value, however parameter names + /// will not be checked (for example if an operation in the target + /// is listed as parameterized with ``"theta"`` and ``"phi"`` is + /// passed into this function that will return ``True``). For + /// example, if called with:: + /// + /// parameters = [Parameter("theta")] + /// target.instruction_supported("rx", (0,), parameters=parameters) + /// + /// will return ``True`` if an :class:`~.RXGate` is supported on qubit 0 + /// that will accept any parameter. If you need to check for a fixed numeric + /// value parameter this argument is typically paired with the ``operation_class`` + /// argument. For example:: + /// + /// target.instruction_supported("rx", (0,), RXGate, parameters=[pi / 4]) + /// + /// will return ``True`` if an RXGate(pi/4) exists on qubit 0. + /// + /// Returns: + /// bool: Returns ``True`` if the instruction is supported and ``False`` if it isn't. + #[pyo3( + name = "instruction_supported", + signature = (operation_name=None, qargs=None, operation_class=None, parameters=None) + )] + pub fn py_instruction_supported( + &self, + py: Python, + operation_name: Option, + qargs: Option, + operation_class: Option<&Bound>, + parameters: Option>, + ) -> PyResult { + let mut qargs = qargs; + if self.num_qubits.is_none() { + qargs = None; + } + if let Some(_operation_class) = operation_class { + for (op_name, obj) in self._gate_name_map.iter() { + match obj { + TargetOperation::Variadic(variable) => { + if !_operation_class.eq(variable)? { + continue; + } + // If no qargs operation class is supported + if let Some(_qargs) = &qargs { + let qarg_set: HashSet = _qargs.iter().cloned().collect(); + // If qargs set then validate no duplicates and all indices are valid on device + return Ok(_qargs + .iter() + .all(|qarg| qarg.index() <= self.num_qubits.unwrap_or_default()) + && qarg_set.len() == _qargs.len()); + } else { + return Ok(true); + } + } + TargetOperation::Normal(normal) => { + if python_is_instance(py, normal, _operation_class)? { + if let Some(parameters) = ¶meters { + if parameters.len() != normal.params.len() { + continue; + } + if !check_obj_params(parameters, normal) { + continue; + } + } + if let Some(_qargs) = &qargs { + if self.gate_map.contains_key(op_name) { + let gate_map_name = &self.gate_map[op_name]; + if gate_map_name.contains_key(qargs.as_ref()) { + return Ok(true); + } + if gate_map_name.contains_key(None) { + let qubit_comparison = + self._gate_name_map[op_name].num_qubits(); + return Ok(qubit_comparison == _qargs.len() as u32 + && _qargs.iter().all(|x| { + x.index() < self.num_qubits.unwrap_or_default() + })); + } + } else { + let qubit_comparison = obj.num_qubits(); + return Ok(qubit_comparison == _qargs.len() as u32 + && _qargs.iter().all(|x| { + x.index() < self.num_qubits.unwrap_or_default() + })); + } + } else { + return Ok(true); + } + } + } + } + } + Ok(false) + } else if let Some(operation_name) = operation_name { + if let Some(parameters) = parameters { + if let Some(obj) = self._gate_name_map.get(&operation_name) { + if self.variable_class_operations.contains(&operation_name) { + if let Some(_qargs) = qargs { + let qarg_set: HashSet = _qargs.iter().cloned().collect(); + return Ok(_qargs + .iter() + .all(|qarg| qarg.index() <= self.num_qubits.unwrap_or_default()) + && qarg_set.len() == _qargs.len()); + } else { + return Ok(true); + } + } + + let obj_params = obj.params(); + if parameters.len() != obj_params.len() { + return Ok(false); + } + for (index, params) in parameters.iter().enumerate() { + let mut matching_params = false; + let obj_at_index = &obj_params[index]; + if matches!(obj_at_index, Param::ParameterExpression(_)) + || python_compare(py, ¶ms, &obj_params[index])? + { + matching_params = true; + } + if !matching_params { + return Ok(false); + } + } + return Ok(true); + } + } + Ok(self.instruction_supported(&operation_name, qargs.as_ref())) + } else { + Ok(false) + } + } + + /// Get the instruction properties for a specific instruction tuple + /// + /// This method is to be used in conjunction with the + /// :attr:`~qiskit.transpiler.Target.instructions` attribute of a + /// :class:`~qiskit.transpiler.Target` object. You can use this method to quickly + /// get the instruction properties for an element of + /// :attr:`~qiskit.transpiler.Target.instructions` by using the index in that list. + /// However, if you're not working with :attr:`~qiskit.transpiler.Target.instructions` + /// directly it is likely more efficient to access the target directly via the name + /// and qubits to get the instruction properties. For example, if + /// :attr:`~qiskit.transpiler.Target.instructions` returned:: + /// + /// [(XGate(), (0,)), (XGate(), (1,))] + /// + /// you could get the properties of the ``XGate`` on qubit 1 with:: + /// + /// props = target.instruction_properties(1) + /// + /// but just accessing it directly via the name would be more efficient:: + /// + /// props = target['x'][(1,)] + /// + /// (assuming the ``XGate``'s canonical name in the target is ``'x'``) + /// This is especially true for larger targets as this will scale worse with the number + /// of instruction tuples in a target. + /// + /// Args: + /// index (int): The index of the instruction tuple from the + /// :attr:`~qiskit.transpiler.Target.instructions` attribute. For, example + /// if you want the properties from the third element in + /// :attr:`~qiskit.transpiler.Target.instructions` you would set this to be ``2``. + /// Returns: + /// InstructionProperties: The instruction properties for the specified instruction tuple + pub fn instruction_properties(&self, index: usize) -> PyResult> { + let mut index_counter = 0; + for (_operation, props_map) in self.gate_map.iter() { + let gate_map_oper = props_map.values(); + for inst_props in gate_map_oper { + if index_counter == index { + return Ok(inst_props.clone()); + } + index_counter += 1; + } + } + Err(PyIndexError::new_err(format!( + "Index: {:?} is out of range.", + index + ))) + } + + /// Return the non-global operation names for the target + /// + /// The non-global operations are those in the target which don't apply + /// on all qubits (for single qubit operations) or all multi-qubit qargs + /// (for multi-qubit operations). + /// + /// Args: + /// strict_direction (bool): If set to ``True`` the multi-qubit + /// operations considered as non-global respect the strict + /// direction (or order of qubits in the qargs is significant). For + /// example, if ``cx`` is defined on ``(0, 1)`` and ``ecr`` is + /// defined over ``(1, 0)`` by default neither would be considered + /// non-global, but if ``strict_direction`` is set ``True`` both + /// ``cx`` and ``ecr`` would be returned. + /// + /// Returns: + /// List[str]: A list of operation names for operations that aren't global in this target + #[pyo3(name = "get_non_global_operation_names", signature = (/, strict_direction=false,))] + fn py_get_non_global_operation_names( + &mut self, + py: Python<'_>, + strict_direction: bool, + ) -> PyObject { + self.get_non_global_operation_names(strict_direction) + .to_object(py) + } + + // Instance attributes + + /// The set of qargs in the target. + #[getter] + #[pyo3(name = "qargs")] + fn py_qargs(&self, py: Python) -> PyResult { + if let Some(qargs) = self.qargs() { + let qargs = qargs.map(|qargs| qargs.map(|q| PyTuple::new_bound(py, q))); + let set = PySet::empty_bound(py)?; + for qargs in qargs { + set.add(qargs)?; + } + Ok(set.into_any().unbind()) + } else { + Ok(py.None()) + } + } + + /// Get the list of tuples ``(:class:`~qiskit.circuit.Instruction`, (qargs))`` + /// for the target + /// + /// For globally defined variable width operations the tuple will be of the form + /// ``(class, None)`` where class is the actual operation class that + /// is globally defined. + #[getter] + #[pyo3(name = "instructions")] + pub fn py_instructions(&self, py: Python<'_>) -> PyResult> { + let list = PyList::empty_bound(py); + for (inst, qargs) in self._instructions() { + let qargs = qargs.map(|q| PyTuple::new_bound(py, q).unbind()); + list.append((inst, qargs))?; + } + Ok(list.unbind()) + } + /// Get the operation names in the target. + #[getter] + #[pyo3(name = "operation_names")] + fn py_operation_names(&self, py: Python<'_>) -> Py { + PyList::new_bound(py, self.operation_names()).unbind() + } + + /// Get the operation objects in the target. + #[getter] + #[pyo3(name = "operations")] + fn py_operations(&self, py: Python<'_>) -> Py { + PyList::new_bound(py, self._gate_name_map.values()).unbind() + } + + /// Returns a sorted list of physical qubits. + #[getter] + #[pyo3(name = "physical_qubits")] + fn py_physical_qubits(&self, py: Python<'_>) -> Py { + PyList::new_bound(py, self.physical_qubits()).unbind() + } + + // Magic methods: + + fn __len__(&self) -> PyResult { + Ok(self.gate_map.len()) + } + + fn __getstate__(&self, py: Python<'_>) -> PyResult> { + let result_list = PyDict::new_bound(py); + result_list.set_item("description", self.description.clone())?; + result_list.set_item("num_qubits", self.num_qubits)?; + result_list.set_item("dt", self.dt)?; + result_list.set_item("granularity", self.granularity)?; + result_list.set_item("min_length", self.min_length)?; + result_list.set_item("pulse_alignment", self.pulse_alignment)?; + result_list.set_item("acquire_alignment", self.acquire_alignment)?; + result_list.set_item("qubit_properties", self.qubit_properties.clone())?; + result_list.set_item( + "concurrent_measurements", + self.concurrent_measurements.clone(), + )?; + result_list.set_item( + "gate_map", + self.gate_map + .clone() + .into_iter() + .map(|(key, value)| { + ( + key, + value + .into_iter() + .collect::, Option)>>(), + ) + }) + .collect::() + .into_py(py), + )?; + result_list.set_item("gate_name_map", self._gate_name_map.to_object(py))?; + result_list.set_item("global_operations", self.global_operations.clone())?; + result_list.set_item( + "qarg_gate_map", + self.qarg_gate_map.clone().into_iter().collect_vec(), + )?; + result_list.set_item("non_global_basis", self.non_global_basis.clone())?; + result_list.set_item( + "non_global_strict_basis", + self.non_global_strict_basis.clone(), + )?; + Ok(result_list.unbind()) + } + + fn __setstate__(&mut self, state: Bound) -> PyResult<()> { + self.description = state + .get_item("description")? + .unwrap() + .extract::>()?; + self.num_qubits = state + .get_item("num_qubits")? + .unwrap() + .extract::>()?; + self.dt = state.get_item("dt")?.unwrap().extract::>()?; + self.granularity = state.get_item("granularity")?.unwrap().extract::()?; + self.min_length = state.get_item("min_length")?.unwrap().extract::()?; + self.pulse_alignment = state + .get_item("pulse_alignment")? + .unwrap() + .extract::()?; + self.acquire_alignment = state + .get_item("acquire_alignment")? + .unwrap() + .extract::()?; + self.qubit_properties = state + .get_item("qubit_properties")? + .unwrap() + .extract::>>()?; + self.concurrent_measurements = state + .get_item("concurrent_measurements")? + .unwrap() + .extract::>>>()?; + self.gate_map = IndexMap::from_iter( + state + .get_item("gate_map")? + .unwrap() + .extract::()? + .into_iter() + .map(|(name, prop_map)| (name, PropsMap::from_iter(prop_map.into_iter()))), + ); + self._gate_name_map = state + .get_item("gate_name_map")? + .unwrap() + .extract::>()?; + self.global_operations = state + .get_item("global_operations")? + .unwrap() + .extract::, RandomState>>()?; + self.qarg_gate_map = NullableIndexMap::from_iter( + state + .get_item("qarg_gate_map")? + .unwrap() + .extract::, Option>)>>()?, + ); + self.non_global_basis = state + .get_item("non_global_basis")? + .unwrap() + .extract::>>()?; + self.non_global_strict_basis = state + .get_item("non_global_strict_basis")? + .unwrap() + .extract::>>()?; + Ok(()) + } +} + +// Rust native methods +impl Target { + /// Returns an iterator over all the instructions present in the `Target` + /// as pair of `&OperationType`, `&SmallVec<[Param; 3]>` and `Option<&Qargs>`. + // TODO: Remove once `Target` is being consumed. + #[allow(dead_code)] + pub fn instructions(&self) -> impl Iterator)> { + self._instructions() + .filter_map(|(operation, qargs)| match &operation { + TargetOperation::Normal(oper) => Some((oper, qargs)), + _ => None, + }) + } + + /// Returns an iterator over all the instructions present in the `Target` + /// as pair of `&TargetOperation` and `Option<&Qargs>`. + fn _instructions(&self) -> impl Iterator)> { + self.gate_map.iter().flat_map(move |(op, props_map)| { + props_map + .keys() + .map(move |qargs| (&self._gate_name_map[op], qargs)) + }) + } + /// Returns an iterator over the operation names in the target. + // TODO: Remove once `Target` is being consumed. + #[allow(dead_code)] + pub fn operation_names(&self) -> impl ExactSizeIterator { + self.gate_map.keys().map(|x| x.as_str()) + } + + /// Get the `OperationType` objects present in the target. + // TODO: Remove once `Target` is being consumed. + #[allow(dead_code)] + pub fn operations(&self) -> impl Iterator { + return self._gate_name_map.values().filter_map(|oper| match oper { + TargetOperation::Normal(oper) => Some(oper), + _ => None, + }); + } + + /// Get an iterator over the indices of all physical qubits of the target + pub fn physical_qubits(&self) -> impl ExactSizeIterator { + 0..self.num_qubits.unwrap_or_default() + } + + /// Generate non global operations if missing + fn generate_non_global_op_names(&mut self, strict_direction: bool) -> &[String] { + let mut search_set: HashSet = HashSet::default(); + if strict_direction { + // Build search set + search_set = self.qarg_gate_map.keys().flatten().cloned().collect(); + } else { + for qarg_key in self.qarg_gate_map.keys().flatten() { + if qarg_key.len() != 1 { + let mut vec = qarg_key.clone(); + vec.sort_unstable(); + search_set.insert(vec); + } + } + } + let mut incomplete_basis_gates: Vec = vec![]; + let mut size_dict: IndexMap = IndexMap::default(); + *size_dict + .entry(1) + .or_insert(self.num_qubits.unwrap_or_default()) = self.num_qubits.unwrap_or_default(); + for qarg in &search_set { + if qarg.len() == 1 { + continue; + } + *size_dict.entry(qarg.len()).or_insert(0) += 1; + } + for (inst, qargs_props) in self.gate_map.iter() { + let mut qarg_len = qargs_props.len(); + let mut qargs_keys = qargs_props.keys().peekable(); + let qarg_sample = qargs_keys.peek().cloned(); + if let Some(qarg_sample) = qarg_sample { + if qarg_sample.is_none() { + continue; + } + if !strict_direction { + let mut deduplicated_qargs: HashSet> = + HashSet::default(); + for qarg in qargs_keys.flatten() { + let mut ordered_qargs = qarg.clone(); + ordered_qargs.sort_unstable(); + deduplicated_qargs.insert(ordered_qargs); + } + qarg_len = deduplicated_qargs.len(); + } + if let Some(qarg_sample) = qarg_sample { + if qarg_len != *size_dict.entry(qarg_sample.len()).or_insert(0) { + incomplete_basis_gates.push(inst.clone()); + } + } + } + } + if strict_direction { + self.non_global_strict_basis = Some(incomplete_basis_gates); + self.non_global_strict_basis.as_ref().unwrap() + } else { + self.non_global_basis = Some(incomplete_basis_gates.clone()); + self.non_global_basis.as_ref().unwrap() + } + } + + /// Get all non_global operation names. + pub fn get_non_global_operation_names(&mut self, strict_direction: bool) -> Option<&[String]> { + if strict_direction { + if self.non_global_strict_basis.is_some() { + return self.non_global_strict_basis.as_deref(); + } + } else if self.non_global_basis.is_some() { + return self.non_global_basis.as_deref(); + } + return Some(self.generate_non_global_op_names(strict_direction)); + } + + /// Gets all the operation names that use these qargs. Rust native equivalent of ``BaseTarget.operation_names_for_qargs()`` + pub fn operation_names_for_qargs( + &self, + qargs: Option<&Qargs>, + ) -> Result, TargetKeyError> { + // When num_qubits == 0 we return globally defined operators + let mut res: HashSet<&str> = HashSet::default(); + let mut qargs = qargs; + if self.num_qubits.unwrap_or_default() == 0 || self.num_qubits.is_none() { + qargs = None; + } + if let Some(qargs) = qargs.as_ref() { + if qargs + .iter() + .any(|x| !(0..self.num_qubits.unwrap_or_default()).contains(&x.index())) + { + return Err(TargetKeyError::new_err(format!( + "{:?} not in Target", + qargs + ))); + } + } + if let Some(Some(qarg_gate_map_arg)) = self.qarg_gate_map.get(qargs).as_ref() { + res.extend(qarg_gate_map_arg.iter().map(|key| key.as_str())); + } + for name in self._gate_name_map.keys() { + if self.variable_class_operations.contains(name) { + res.insert(name); + } + } + if let Some(qargs) = qargs.as_ref() { + if let Some(global_gates) = self.global_operations.get(&(qargs.len() as u32)) { + res.extend(global_gates.iter().map(|key| key.as_str())) + } + } + if res.is_empty() { + return Err(TargetKeyError::new_err(format!( + "{:?} not in target", + qargs + ))); + } + Ok(res) + } + + /// Returns an iterator of `OperationType` instances and parameters present in the Target that affect the provided qargs. + // TODO: Remove once `Target` is being consumed. + #[allow(dead_code)] + pub fn operations_for_qargs( + &self, + qargs: Option<&Qargs>, + ) -> Result, TargetKeyError> { + self.operation_names_for_qargs(qargs).map(|operations| { + operations + .into_iter() + .filter_map(|oper| match &self._gate_name_map[oper] { + TargetOperation::Normal(normal) => Some(normal), + _ => None, + }) + }) + } + + /// Gets an iterator with all the qargs used by the specified operation name. + /// + /// Rust native equivalent of ``BaseTarget.qargs_for_operation_name()`` + pub fn qargs_for_operation_name( + &self, + operation: &str, + ) -> Result>, TargetKeyError> { + if let Some(gate_map_oper) = self.gate_map.get(operation) { + if gate_map_oper.contains_key(None) { + return Ok(None); + } + let qargs = gate_map_oper.keys().flatten(); + Ok(Some(qargs)) + } else { + Err(TargetKeyError::new_err(format!( + "Operation: {operation} not in Target." + ))) + } + } + + /// Gets a tuple of Operation object and Parameters based on the operation name if present in the Target. + // TODO: Remove once `Target` is being consumed. + #[allow(dead_code)] + pub fn operation_from_name( + &self, + instruction: &str, + ) -> Result<&NormalOperation, TargetKeyError> { + match self._operation_from_name(instruction) { + Ok(TargetOperation::Normal(operation)) => Ok(operation), + Ok(TargetOperation::Variadic(_)) => Err(TargetKeyError::new_err(format!( + "Instruction {:?} was found in the target, but the instruction is Varidic.", + instruction + ))), + Err(e) => Err(e), + } + } + + /// Gets the instruction object based on the operation name + fn _operation_from_name(&self, instruction: &str) -> Result<&TargetOperation, TargetKeyError> { + if let Some(gate_obj) = self._gate_name_map.get(instruction) { + Ok(gate_obj) + } else { + Err(TargetKeyError::new_err(format!( + "Instruction {:?} not in target", + instruction + ))) + } + } + + /// Returns an iterator over all the qargs of a specific Target object + pub fn qargs(&self) -> Option>> { + let mut qargs = self.qarg_gate_map.keys().peekable(); + let next_entry = qargs.peek(); + let is_none = next_entry.is_none() || next_entry.unwrap().is_none(); + if qargs.len() == 1 && is_none { + return None; + } + Some(qargs) + } + + /// Checks whether an instruction is supported by the Target based on instruction name and qargs. + pub fn instruction_supported(&self, operation_name: &str, qargs: Option<&Qargs>) -> bool { + if self.gate_map.contains_key(operation_name) { + if let Some(_qargs) = qargs { + let qarg_set: HashSet<&PhysicalQubit> = _qargs.iter().collect(); + if let Some(gate_prop_name) = self.gate_map.get(operation_name) { + if gate_prop_name.contains_key(qargs) { + return true; + } + if gate_prop_name.contains_key(None) { + let obj = &self._gate_name_map[operation_name]; + if self.variable_class_operations.contains(operation_name) { + return qargs.is_none() + || _qargs.iter().all(|qarg| { + qarg.index() <= self.num_qubits.unwrap_or_default() + }) && qarg_set.len() == _qargs.len(); + } else { + let qubit_comparison = obj.num_qubits(); + return qubit_comparison == _qargs.len() as u32 + && _qargs.iter().all(|qarg| { + qarg.index() < self.num_qubits.unwrap_or_default() + }); + } + } + } else { + // Duplicate case is if it contains none + if self.variable_class_operations.contains(operation_name) { + return qargs.is_none() + || _qargs + .iter() + .all(|qarg| qarg.index() <= self.num_qubits.unwrap_or_default()) + && qarg_set.len() == _qargs.len(); + } else { + let qubit_comparison = self._gate_name_map[operation_name].num_qubits(); + return qubit_comparison == _qargs.len() as u32 + && _qargs + .iter() + .all(|qarg| qarg.index() < self.num_qubits.unwrap_or_default()); + } + } + } else { + return true; + } + } + false + } + + // IndexMap methods + + /// Retreive all the gate names in the Target + // TODO: Remove once `Target` is being consumed. + #[allow(dead_code)] + pub fn keys(&self) -> impl Iterator { + self.gate_map.keys().map(|x| x.as_str()) + } + + /// Retrieves an iterator over the property maps stored within the Target + // TODO: Remove once `Target` is being consumed. + #[allow(dead_code)] + pub fn values(&self) -> impl Iterator { + self.gate_map.values() + } + + /// Checks if a key exists in the Target + pub fn contains_key(&self, key: &str) -> bool { + self.gate_map.contains_key(key) + } +} + +// To access the Target's gate map by gate name. +impl Index<&str> for Target { + type Output = PropsMap; + fn index(&self, index: &str) -> &Self::Output { + self.gate_map.index(index) + } +} + +// For instruction_supported +fn check_obj_params(parameters: &[Param], obj: &NormalOperation) -> bool { + for (index, param) in parameters.iter().enumerate() { + let param_at_index = &obj.params[index]; + match (param, param_at_index) { + (Param::Float(p1), Param::Float(p2)) => { + if p1 != p2 { + return false; + } + } + (&Param::ParameterExpression(_), Param::Float(_)) => return false, + (&Param::ParameterExpression(_), Param::Obj(_)) => return false, + _ => continue, + } + } + true +} + +pub fn python_compare(py: Python, obj: &T, other: &U) -> PyResult +where + T: ToPyObject, + U: ToPyObject, +{ + let obj = obj.to_object(py); + let obj_bound = obj.bind(py); + obj_bound.eq(other) +} + +pub fn python_is_instance(py: Python, obj: &T, other: &U) -> PyResult +where + T: ToPyObject, + U: ToPyObject, +{ + let obj = obj.to_object(py); + let other_obj = other.to_object(py); + let obj_bound = obj.bind(py); + obj_bound.is_instance(other_obj.bind(py)) +} + +#[pymodule] +pub fn target(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + Ok(()) +} + +// TODO: Add rust-based unit testing. diff --git a/crates/accelerate/src/target_transpiler/nullable_index_map.rs b/crates/accelerate/src/target_transpiler/nullable_index_map.rs new file mode 100644 index 000000000000..d3056c9edd8a --- /dev/null +++ b/crates/accelerate/src/target_transpiler/nullable_index_map.rs @@ -0,0 +1,457 @@ +// 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 ahash::RandomState; +use indexmap::{ + map::{IntoIter as BaseIntoIter, Iter as BaseIter, Keys as BaseKeys, Values as BaseValues}, + IndexMap, +}; +use pyo3::prelude::*; +use pyo3::types::PyDict; +use pyo3::IntoPy; +use rustworkx_core::dictmap::InitWithHasher; +use std::ops::Index; +use std::{hash::Hash, mem::swap}; + +type BaseMap = IndexMap; + +/// +/// An `IndexMap`-like structure thet can be used when one of the keys can have a `None` value. +/// +/// This structure is essentially a wrapper around the `IndexMap` struct that allows the +/// storage of `Option` key values as `K`` and keep an extra slot reserved only for the +/// `None` instance. There are some upsides to this including: +/// +/// The ability to index using Option<&K> to index a specific key. +/// Store keys as non option wrapped to obtain references to K instead of reference to Option. +/// +/// **Warning:** This is an experimental feature and should be used with care as it does not +/// fully implement all the methods present in `IndexMap` due to API limitations. +#[derive(Debug, Clone)] +pub(crate) struct NullableIndexMap +where + K: Eq + Hash + Clone, + V: Clone, +{ + map: BaseMap, + null_val: Option, +} + +impl NullableIndexMap +where + K: Eq + Hash + Clone, + V: Clone, +{ + /// Returns a reference to the value stored at `key`, if it does not exist + /// `None` is returned instead. + pub fn get(&self, key: Option<&K>) -> Option<&V> { + match key { + Some(key) => self.map.get(key), + None => self.null_val.as_ref(), + } + } + + /// Returns a mutable reference to the value stored at `key`, if it does not + /// exist `None` is returned instead. + pub fn get_mut(&mut self, key: Option<&K>) -> Option<&mut V> { + match key { + Some(key) => self.map.get_mut(key), + None => self.null_val.as_mut(), + } + } + + /// Inserts a `value` in the slot alotted to `key`. + /// + /// If a previous value existed there previously it will be returned, otherwise + /// `None` will be returned. + pub fn insert(&mut self, key: Option, value: V) -> Option { + match key { + Some(key) => self.map.insert(key, value), + None => { + let mut old_val = Some(value); + swap(&mut old_val, &mut self.null_val); + old_val + } + } + } + + /// Creates an instance of `NullableIndexMap` with capacity to hold `n`+1 key-value + /// pairs. + /// + /// Notice that an extra space needs to be alotted to store the instance of `None` a + /// key. + pub fn with_capacity(n: usize) -> Self { + Self { + map: BaseMap::with_capacity(n), + null_val: None, + } + } + + /// Creates an instance of `NullableIndexMap` from an iterator over instances of + /// `(Option, V)`. + pub fn from_iter<'a, I>(iter: I) -> Self + where + I: IntoIterator, V)> + 'a, + { + let mut null_val = None; + let filtered = iter.into_iter().filter_map(|item| match item { + (Some(key), value) => Some((key, value)), + (None, value) => { + null_val = Some(value); + None + } + }); + Self { + map: IndexMap::from_iter(filtered), + null_val, + } + } + + /// Returns `true` if the map contains a slot indexed by `key`, otherwise `false`. + pub fn contains_key(&self, key: Option<&K>) -> bool { + match key { + Some(key) => self.map.contains_key(key), + None => self.null_val.is_some(), + } + } + + /// Extends the key-value pairs in the map with the contents of an iterator over + /// `(Option, V)`. + /// + /// If an already existent key is provided, it will be replaced by the entry provided + /// in the iterator. + pub fn extend<'a, I>(&mut self, iter: I) + where + I: IntoIterator, V)> + 'a, + { + let filtered = iter.into_iter().filter_map(|item| match item { + (Some(key), value) => Some((key, value)), + (None, value) => { + self.null_val = Some(value); + None + } + }); + self.map.extend(filtered) + } + + /// Removes the entry allotted to `key` from the map and returns it. The index of + /// this entry is then replaced by the entry located at the last index. + /// + /// `None` will be returned if the `key` is not present in the map. + pub fn swap_remove(&mut self, key: Option<&K>) -> Option { + match key { + Some(key) => self.map.swap_remove(key), + None => { + let mut ret_val = None; + swap(&mut ret_val, &mut self.null_val); + ret_val + } + } + } + + /// Returns an iterator over references of the key-value pairs of the map. + // TODO: Remove once `NullableIndexMap` is being consumed. + #[allow(dead_code)] + pub fn iter(&self) -> Iter { + Iter { + map: self.map.iter(), + null_value: &self.null_val, + } + } + + /// Returns an iterator over references of the keys present in the map. + pub fn keys(&self) -> Keys { + Keys { + map_keys: self.map.keys(), + null_value: self.null_val.is_some(), + } + } + + /// Returns an iterator over references of all the values present in the map. + pub fn values(&self) -> Values { + Values { + map_values: self.map.values(), + null_value: &self.null_val, + } + } + + /// Returns the number of key-value pairs present in the map. + pub fn len(&self) -> usize { + self.map.len() + self.null_val.is_some() as usize + } +} + +impl IntoIterator for NullableIndexMap +where + K: Eq + Hash + Clone, + V: Clone, +{ + type Item = (Option, V); + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter { + map: self.map.into_iter(), + null_value: self.null_val, + } + } +} + +/// Iterator for the key-value pairs in `NullableIndexMap`. +pub struct Iter<'a, K, V> { + map: BaseIter<'a, K, V>, + null_value: &'a Option, +} + +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (Option<&'a K>, &'a V); + + fn next(&mut self) -> Option { + if let Some((key, val)) = self.map.next() { + Some((Some(key), val)) + } else if let Some(value) = self.null_value { + let value = value; + self.null_value = &None; + Some((None, value)) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + ( + self.map.size_hint().0 + self.null_value.is_some() as usize, + self.map + .size_hint() + .1 + .map(|hint| hint + self.null_value.is_some() as usize), + ) + } +} + +impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { + fn len(&self) -> usize { + self.map.len() + self.null_value.is_some() as usize + } +} + +/// Owned iterator over the key-value pairs in `NullableIndexMap`. +pub struct IntoIter +where + V: Clone, +{ + map: BaseIntoIter, + null_value: Option, +} + +impl Iterator for IntoIter +where + V: Clone, +{ + type Item = (Option, V); + + fn next(&mut self) -> Option { + if let Some((key, val)) = self.map.next() { + Some((Some(key), val)) + } else if self.null_value.is_some() { + let mut value = None; + swap(&mut value, &mut self.null_value); + Some((None, value.unwrap())) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + ( + self.map.size_hint().0 + self.null_value.is_some() as usize, + self.map + .size_hint() + .1 + .map(|hint| hint + self.null_value.is_some() as usize), + ) + } +} + +impl ExactSizeIterator for IntoIter +where + V: Clone, +{ + fn len(&self) -> usize { + self.map.len() + self.null_value.is_some() as usize + } +} + +/// Iterator over the keys of a `NullableIndexMap`. +pub struct Keys<'a, K, V> { + map_keys: BaseKeys<'a, K, V>, + null_value: bool, +} + +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = Option<&'a K>; + + fn next(&mut self) -> Option { + if let Some(key) = self.map_keys.next() { + Some(Some(key)) + } else if self.null_value { + self.null_value = false; + Some(None) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + ( + self.map_keys.size_hint().0 + self.null_value as usize, + self.map_keys + .size_hint() + .1 + .map(|hint| hint + self.null_value as usize), + ) + } +} + +impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { + fn len(&self) -> usize { + self.map_keys.len() + self.null_value as usize + } +} + +/// Iterator over the values of a `NullableIndexMap`. +pub struct Values<'a, K, V> { + map_values: BaseValues<'a, K, V>, + null_value: &'a Option, +} + +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + fn next(&mut self) -> Option { + if let Some(value) = self.map_values.next() { + Some(value) + } else if self.null_value.is_some() { + let return_value = self.null_value; + self.null_value = &None; + return_value.as_ref() + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + ( + self.map_values.size_hint().0 + self.null_value.is_some() as usize, + self.map_values + .size_hint() + .1 + .map(|hint| hint + self.null_value.is_some() as usize), + ) + } +} + +impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { + fn len(&self) -> usize { + self.map_values.len() + self.null_value.is_some() as usize + } +} + +impl Index> for NullableIndexMap +where + K: Eq + Hash + Clone, + V: Clone, +{ + type Output = V; + fn index(&self, index: Option<&K>) -> &Self::Output { + match index { + Some(k) => self.map.index(k), + None => match &self.null_val { + Some(val) => val, + None => panic!("The provided key is not present in map: None"), + }, + } + } +} + +impl Default for NullableIndexMap +where + K: Eq + Hash + Clone, + V: Clone, +{ + fn default() -> Self { + Self { + map: IndexMap::default(), + null_val: None, + } + } +} + +impl<'py, K, V> FromPyObject<'py> for NullableIndexMap +where + K: IntoPy + FromPyObject<'py> + Eq + Hash + Clone, + V: IntoPy + FromPyObject<'py> + Clone, +{ + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + let map: IndexMap, V, RandomState> = ob.extract()?; + let mut null_val: Option = None; + let filtered = map + .into_iter() + .filter_map(|(key, value)| match (key, value) { + (Some(key), value) => Some((key, value)), + (None, value) => { + null_val = Some(value); + None + } + }); + Ok(Self { + map: filtered.collect(), + null_val, + }) + } +} + +impl IntoPy for NullableIndexMap +where + K: IntoPy + Eq + Hash + Clone, + V: IntoPy + Clone, +{ + fn into_py(self, py: Python<'_>) -> PyObject { + let map_object = self.map.into_py(py); + let bound_map_obj = map_object.bind(py); + let downcast_dict: &Bound = bound_map_obj.downcast().unwrap(); + if let Some(null_val) = self.null_val { + downcast_dict + .set_item(py.None(), null_val.into_py(py)) + .unwrap(); + } + map_object + } +} + +impl ToPyObject for NullableIndexMap +where + K: ToPyObject + Eq + Hash + Clone, + V: ToPyObject + Clone, +{ + fn to_object(&self, py: Python<'_>) -> PyObject { + let map_object = self.map.to_object(py); + let bound_map_obj = map_object.bind(py); + let downcast_dict: &Bound = bound_map_obj.downcast().unwrap(); + if let Some(null_val) = &self.null_val { + downcast_dict + .set_item(py.None(), null_val.to_object(py)) + .unwrap(); + } + map_object + } +} diff --git a/crates/accelerate/src/two_qubit_decompose.rs b/crates/accelerate/src/two_qubit_decompose.rs index ac2cc1d2e50e..dfee4c5b0bff 100644 --- a/crates/accelerate/src/two_qubit_decompose.rs +++ b/crates/accelerate/src/two_qubit_decompose.rs @@ -51,10 +51,13 @@ use rand::prelude::*; use rand_distr::StandardNormal; use rand_pcg::Pcg64Mcg; +use qiskit_circuit::circuit_data::CircuitData; +use qiskit_circuit::circuit_instruction::OperationFromPython; use qiskit_circuit::gate_matrix::{CX_GATE, H_GATE, ONE_QUBIT_IDENTITY, SX_GATE, X_GATE}; -use qiskit_circuit::operations::Operation; +use qiskit_circuit::operations::{Param, StandardGate}; use qiskit_circuit::slice::{PySequenceIndex, SequenceIndex}; use qiskit_circuit::util::{c64, GateArray1Q, GateArray2Q, C_M_ONE, C_ONE, C_ZERO, IM, M_IM}; +use qiskit_circuit::Qubit; const PI2: f64 = PI / 2.; const PI4: f64 = PI / 4.; @@ -309,10 +312,10 @@ fn compute_unitary(sequence: &TwoQubitSequenceVec, global_phase: f64) -> Array2< // sequence. If we get a different gate this is getting called // by something else and is invalid. let gate_matrix = match inst.0.as_ref() { - "sx" => aview2(&SX_GATE).to_owned(), - "rz" => rz_matrix(inst.1[0]), - "cx" => aview2(&CX_GATE).to_owned(), - "x" => aview2(&X_GATE).to_owned(), + Some(StandardGate::SXGate) => aview2(&SX_GATE).to_owned(), + Some(StandardGate::RZGate) => rz_matrix(inst.1[0]), + Some(StandardGate::CXGate) => aview2(&CX_GATE).to_owned(), + Some(StandardGate::XGate) => aview2(&X_GATE).to_owned(), _ => unreachable!("Undefined gate"), }; (gate_matrix, &inst.2) @@ -395,6 +398,8 @@ impl Specialization { } } +type WeylCircuitSequence = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>; + #[derive(Clone, Debug)] #[allow(non_snake_case)] #[pyclass(module = "qiskit._accelerate.two_qubit_decompose", subclass)] @@ -425,97 +430,63 @@ impl TwoQubitWeylDecomposition { fn weyl_gate( &self, simplify: bool, - sequence: &mut TwoQubitSequenceVec, + sequence: &mut WeylCircuitSequence, atol: f64, global_phase: &mut f64, ) { match self.specialization { Specialization::MirrorControlledEquiv => { - sequence.push(("swap".to_string(), SmallVec::new(), smallvec![0, 1])); sequence.push(( - "rzz".to_string(), - smallvec![(PI4 - self.c) * 2.], - smallvec![0, 1], + StandardGate::SwapGate, + SmallVec::new(), + smallvec![Qubit(0), Qubit(1)], + )); + sequence.push(( + StandardGate::RZZGate, + smallvec![Param::Float((PI4 - self.c) * 2.)], + smallvec![Qubit(0), Qubit(1)], )); *global_phase += PI4 } Specialization::SWAPEquiv => { - sequence.push(("swap".to_string(), SmallVec::new(), smallvec![0, 1])); + sequence.push(( + StandardGate::SwapGate, + SmallVec::new(), + smallvec![Qubit(0), Qubit(1)], + )); *global_phase -= 3. * PI / 4. } _ => { if !simplify || self.a.abs() > atol { - sequence.push(("rxx".to_string(), smallvec![-self.a * 2.], smallvec![0, 1])); + sequence.push(( + StandardGate::RXXGate, + smallvec![Param::Float(-self.a * 2.)], + smallvec![Qubit(0), Qubit(1)], + )); } if !simplify || self.b.abs() > atol { - sequence.push(("ryy".to_string(), smallvec![-self.b * 2.], smallvec![0, 1])); + sequence.push(( + StandardGate::RYYGate, + smallvec![Param::Float(-self.b * 2.)], + smallvec![Qubit(0), Qubit(1)], + )); } if !simplify || self.c.abs() > atol { - sequence.push(("rzz".to_string(), smallvec![-self.c * 2.], smallvec![0, 1])); + sequence.push(( + StandardGate::RZZGate, + smallvec![Param::Float(-self.c * 2.)], + smallvec![Qubit(0), Qubit(1)], + )); } } } } -} -static IPZ: GateArray1Q = [[IM, C_ZERO], [C_ZERO, M_IM]]; -static IPY: GateArray1Q = [[C_ZERO, C_ONE], [C_M_ONE, C_ZERO]]; -static IPX: GateArray1Q = [[C_ZERO, IM], [IM, C_ZERO]]; + /// Instantiate a new TwoQubitWeylDecomposition with rust native + /// data structures + fn new_inner( + unitary_matrix: ArrayView2, -#[pymethods] -impl TwoQubitWeylDecomposition { - #[staticmethod] - fn _from_state( - angles: [f64; 4], - matrices: [PyReadonlyArray2; 5], - specialization: Specialization, - default_euler_basis: EulerBasis, - calculated_fidelity: f64, - requested_fidelity: Option, - ) -> Self { - let [a, b, c, global_phase] = angles; - Self { - a, - b, - c, - global_phase, - K1l: matrices[0].as_array().to_owned(), - K1r: matrices[1].as_array().to_owned(), - K2l: matrices[2].as_array().to_owned(), - K2r: matrices[3].as_array().to_owned(), - specialization, - default_euler_basis, - calculated_fidelity, - requested_fidelity, - unitary_matrix: matrices[4].as_array().to_owned(), - } - } - - fn __reduce__(&self, py: Python) -> PyResult> { - Ok(( - py.get_type_bound::().getattr("_from_state")?, - ( - [self.a, self.b, self.c, self.global_phase], - [ - self.K1l.to_pyarray_bound(py), - self.K1r.to_pyarray_bound(py), - self.K2l.to_pyarray_bound(py), - self.K2r.to_pyarray_bound(py), - self.unitary_matrix.to_pyarray_bound(py), - ], - self.specialization, - self.default_euler_basis, - self.calculated_fidelity, - self.requested_fidelity, - ), - ) - .into_py(py)) - } - - #[new] - #[pyo3(signature=(unitary_matrix, fidelity=DEFAULT_FIDELITY, _specialization=None))] - fn new( - unitary_matrix: PyReadonlyArray2, fidelity: Option, _specialization: Option, ) -> PyResult { @@ -523,8 +494,8 @@ impl TwoQubitWeylDecomposition { let ipy: ArrayView2 = aview2(&IPY); let ipx: ArrayView2 = aview2(&IPX); - let mut u = unitary_matrix.as_array().to_owned(); - let unitary_matrix = unitary_matrix.as_array().to_owned(); + let mut u = unitary_matrix.to_owned(); + let unitary_matrix = unitary_matrix.to_owned(); let det_u = u.view().into_faer_complex().determinant().to_num_complex(); let det_pow = det_u.powf(-0.25); u.mapv_inplace(|x| x * det_pow); @@ -990,6 +961,71 @@ impl TwoQubitWeylDecomposition { specialized.global_phase += tr.arg(); Ok(specialized) } +} + +static IPZ: GateArray1Q = [[IM, C_ZERO], [C_ZERO, M_IM]]; +static IPY: GateArray1Q = [[C_ZERO, C_ONE], [C_M_ONE, C_ZERO]]; +static IPX: GateArray1Q = [[C_ZERO, IM], [IM, C_ZERO]]; + +#[pymethods] +impl TwoQubitWeylDecomposition { + #[staticmethod] + fn _from_state( + angles: [f64; 4], + matrices: [PyReadonlyArray2; 5], + specialization: Specialization, + default_euler_basis: EulerBasis, + calculated_fidelity: f64, + requested_fidelity: Option, + ) -> Self { + let [a, b, c, global_phase] = angles; + Self { + a, + b, + c, + global_phase, + K1l: matrices[0].as_array().to_owned(), + K1r: matrices[1].as_array().to_owned(), + K2l: matrices[2].as_array().to_owned(), + K2r: matrices[3].as_array().to_owned(), + specialization, + default_euler_basis, + calculated_fidelity, + requested_fidelity, + unitary_matrix: matrices[4].as_array().to_owned(), + } + } + + fn __reduce__(&self, py: Python) -> PyResult> { + Ok(( + py.get_type_bound::().getattr("_from_state")?, + ( + [self.a, self.b, self.c, self.global_phase], + [ + self.K1l.to_pyarray_bound(py), + self.K1r.to_pyarray_bound(py), + self.K2l.to_pyarray_bound(py), + self.K2r.to_pyarray_bound(py), + self.unitary_matrix.to_pyarray_bound(py), + ], + self.specialization, + self.default_euler_basis, + self.calculated_fidelity, + self.requested_fidelity, + ), + ) + .into_py(py)) + } + + #[new] + #[pyo3(signature=(unitary_matrix, fidelity=DEFAULT_FIDELITY, _specialization=None))] + fn new( + unitary_matrix: PyReadonlyArray2, + fidelity: Option, + _specialization: Option, + ) -> PyResult { + TwoQubitWeylDecomposition::new_inner(unitary_matrix.as_array(), fidelity, _specialization) + } #[allow(non_snake_case)] #[getter] @@ -1023,17 +1059,18 @@ impl TwoQubitWeylDecomposition { #[pyo3(signature = (euler_basis=None, simplify=false, atol=None))] fn circuit( &self, + py: Python, euler_basis: Option, simplify: bool, atol: Option, - ) -> PyResult { + ) -> PyResult { let euler_basis: EulerBasis = match euler_basis { Some(basis) => EulerBasis::__new__(basis.deref())?, None => self.default_euler_basis, }; let target_1q_basis_list: Vec = vec![euler_basis]; - let mut gate_sequence = Vec::new(); + let mut gate_sequence: WeylCircuitSequence = Vec::with_capacity(21); let mut global_phase: f64 = self.global_phase; let c2r = unitary_to_gate_sequence_inner( @@ -1046,7 +1083,11 @@ impl TwoQubitWeylDecomposition { ) .unwrap(); for gate in c2r.gates { - gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![0])) + gate_sequence.push(( + gate.0, + gate.1.into_iter().map(Param::Float).collect(), + smallvec![Qubit(0)], + )) } global_phase += c2r.global_phase; let c2l = unitary_to_gate_sequence_inner( @@ -1059,7 +1100,11 @@ impl TwoQubitWeylDecomposition { ) .unwrap(); for gate in c2l.gates { - gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![1])) + gate_sequence.push(( + gate.0, + gate.1.into_iter().map(Param::Float).collect(), + smallvec![Qubit(1)], + )) } global_phase += c2l.global_phase; self.weyl_gate( @@ -1078,7 +1123,11 @@ impl TwoQubitWeylDecomposition { ) .unwrap(); for gate in c1r.gates { - gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![0])) + gate_sequence.push(( + gate.0, + gate.1.into_iter().map(Param::Float).collect(), + smallvec![Qubit(0)], + )) } global_phase += c2r.global_phase; let c1l = unitary_to_gate_sequence_inner( @@ -1091,16 +1140,17 @@ impl TwoQubitWeylDecomposition { ) .unwrap(); for gate in c1l.gates { - gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![1])) + gate_sequence.push(( + gate.0, + gate.1.into_iter().map(Param::Float).collect(), + smallvec![Qubit(1)], + )) } - Ok(TwoQubitGateSequence { - gates: gate_sequence, - global_phase, - }) + CircuitData::from_standard_gates(py, 2, gate_sequence, Param::Float(global_phase)) } } -type TwoQubitSequenceVec = Vec<(String, SmallVec<[f64; 3]>, SmallVec<[u8; 2]>)>; +type TwoQubitSequenceVec = Vec<(Option, SmallVec<[f64; 3]>, SmallVec<[u8; 2]>)>; #[pyclass(sequence)] pub struct TwoQubitGateSequence { @@ -1263,17 +1313,21 @@ impl TwoQubitBasisDecomposer { let mut euler_matrix_q1 = rz_matrix(euler_q1[0][1]).dot(&rx_matrix(euler_q1[0][0])); euler_matrix_q1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]).dot(&euler_matrix_q1); self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q1.view(), 1); - gates.push(("cx".to_string(), smallvec![], smallvec![0, 1])); - gates.push(("sx".to_string(), smallvec![], smallvec![0])); + gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![0, 1])); + gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![0])); gates.push(( - "rz".to_string(), + Some(StandardGate::RZGate), smallvec![euler_q0[1][1] - PI], smallvec![0], )); - gates.push(("sx".to_string(), smallvec![], smallvec![0])); - gates.push(("rz".to_string(), smallvec![euler_q1[1][1]], smallvec![1])); + gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![0])); + gates.push(( + Some(StandardGate::RZGate), + smallvec![euler_q1[1][1]], + smallvec![1], + )); global_phase += PI2; - gates.push(("cx".to_string(), smallvec![], smallvec![0, 1])); + gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![0, 1])); let mut euler_matrix_q0 = rx_matrix(euler_q0[2][1]).dot(&rz_matrix(euler_q0[1][2] + euler_q0[2][0] + PI2)); euler_matrix_q0 = rz_matrix(euler_q0[2][2]).dot(&euler_matrix_q0); @@ -1358,7 +1412,7 @@ impl TwoQubitBasisDecomposer { euler_matrix_q1 = aview2(&H_GATE).dot(&euler_matrix_q1); self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q1.view(), 1); - gates.push(("cx".to_string(), smallvec![], smallvec![1, 0])); + gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![1, 0])); if x12_is_pi_mult { // even or odd multiple @@ -1366,14 +1420,22 @@ impl TwoQubitBasisDecomposer { global_phase += x12_phase; } if x12_is_non_zero && x12_is_old_mult.unwrap() { - gates.push(("rz".to_string(), smallvec![-euler_q0[1][1]], smallvec![0])); + gates.push(( + Some(StandardGate::RZGate), + smallvec![-euler_q0[1][1]], + smallvec![0], + )); } else { - gates.push(("rz".to_string(), smallvec![euler_q0[1][1]], smallvec![0])); + gates.push(( + Some(StandardGate::RZGate), + smallvec![euler_q0[1][1]], + smallvec![0], + )); global_phase += PI; } } if x12_is_half_pi { - gates.push(("sx".to_string(), smallvec![], smallvec![0])); + gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![0])); global_phase -= PI4; } else if x12_is_non_zero && !x12_is_pi_mult { if self.pulse_optimize.is_none() { @@ -1383,7 +1445,7 @@ impl TwoQubitBasisDecomposer { } } if abs_diff_eq!(euler_q1[1][1], PI2, epsilon = atol) { - gates.push(("sx".to_string(), smallvec![], smallvec![1])); + gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![1])); global_phase -= PI4 } else if self.pulse_optimize.is_none() { self.append_1q_sequence( @@ -1396,14 +1458,18 @@ impl TwoQubitBasisDecomposer { return None; } gates.push(( - "rz".to_string(), + Some(StandardGate::RZGate), smallvec![euler_q1[1][2] + euler_q1[2][0]], smallvec![1], )); - gates.push(("cx".to_string(), smallvec![], smallvec![1, 0])); - gates.push(("rz".to_string(), smallvec![euler_q0[2][1]], smallvec![0])); + gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![1, 0])); + gates.push(( + Some(StandardGate::RZGate), + smallvec![euler_q0[2][1]], + smallvec![0], + )); if abs_diff_eq!(euler_q1[2][1], PI2, epsilon = atol) { - gates.push(("sx".to_string(), smallvec![], smallvec![1])); + gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![1])); global_phase -= PI4; } else if self.pulse_optimize.is_none() { self.append_1q_sequence( @@ -1415,7 +1481,7 @@ impl TwoQubitBasisDecomposer { } else { return None; } - gates.push(("cx".to_string(), smallvec![], smallvec![1, 0])); + gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![1, 0])); let mut euler_matrix = rz_matrix(euler_q0[2][2] + euler_q0[3][0]).dot(&aview2(&H_GATE)); euler_matrix = rx_matrix(euler_q0[3][1]).dot(&euler_matrix); euler_matrix = rz_matrix(euler_q0[3][2]).dot(&euler_matrix); @@ -1460,7 +1526,7 @@ impl TwoQubitBasisDecomposer { if let Some(sequence) = sequence { *global_phase += sequence.global_phase; for gate in sequence.gates { - gates.push((gate.0.name().to_string(), gate.1, smallvec![qubit])); + gates.push((Some(gate.0), gate.1, smallvec![qubit])); } } } @@ -1513,49 +1579,17 @@ impl TwoQubitBasisDecomposer { } Ok(res) } -} - -static K12R_ARR: GateArray1Q = [ - [c64(0., FRAC_1_SQRT_2), c64(FRAC_1_SQRT_2, 0.)], - [c64(-FRAC_1_SQRT_2, 0.), c64(0., -FRAC_1_SQRT_2)], -]; - -static K12L_ARR: GateArray1Q = [ - [c64(0.5, 0.5), c64(0.5, 0.5)], - [c64(-0.5, 0.5), c64(0.5, -0.5)], -]; - -fn decomp0_inner(target: &TwoQubitWeylDecomposition) -> SmallVec<[Array2; 8]> { - smallvec![target.K1r.dot(&target.K2r), target.K1l.dot(&target.K2l),] -} - -#[pymethods] -impl TwoQubitBasisDecomposer { - fn __getnewargs__(&self, py: Python) -> (String, PyObject, f64, &str, Option) { - ( - self.gate.clone(), - self.basis_decomposer - .unitary_matrix - .to_pyarray_bound(py) - .into(), - self.basis_fidelity, - self.euler_basis.as_str(), - self.pulse_optimize, - ) - } - #[new] - #[pyo3(signature=(gate, gate_matrix, basis_fidelity=1.0, euler_basis="U", pulse_optimize=None))] - fn new( + fn new_inner( gate: String, - gate_matrix: PyReadonlyArray2, + gate_matrix: ArrayView2, basis_fidelity: f64, euler_basis: &str, pulse_optimize: Option, ) -> PyResult { let ipz: ArrayView2 = aview2(&IPZ); let basis_decomposer = - TwoQubitWeylDecomposition::new(gate_matrix, Some(DEFAULT_FIDELITY), None)?; + TwoQubitWeylDecomposition::new_inner(gate_matrix, Some(DEFAULT_FIDELITY), None)?; let super_controlled = relative_eq!(basis_decomposer.a, PI4, max_relative = 1e-09) && relative_eq!(basis_decomposer.c, 0.0, max_relative = 1e-09); @@ -1684,6 +1718,153 @@ impl TwoQubitBasisDecomposer { }) } + fn call_inner( + &self, + unitary: ArrayView2, + basis_fidelity: Option, + approximate: bool, + _num_basis_uses: Option, + ) -> PyResult { + let basis_fidelity = if !approximate { + 1.0 + } else { + basis_fidelity.unwrap_or(self.basis_fidelity) + }; + let target_decomposed = + TwoQubitWeylDecomposition::new_inner(unitary, Some(DEFAULT_FIDELITY), None)?; + let traces = self.traces(&target_decomposed); + let best_nbasis = _num_basis_uses.unwrap_or_else(|| { + traces + .into_iter() + .enumerate() + .map(|(idx, trace)| (idx, trace.trace_to_fid() * basis_fidelity.powi(idx as i32))) + .min_by(|(_idx1, fid1), (_idx2, fid2)| fid2.partial_cmp(fid1).unwrap()) + .unwrap() + .0 as u8 + }); + let decomposition = match best_nbasis { + 0 => decomp0_inner(&target_decomposed), + 1 => self.decomp1_inner(&target_decomposed), + 2 => self.decomp2_supercontrolled_inner(&target_decomposed), + 3 => self.decomp3_supercontrolled_inner(&target_decomposed), + _ => unreachable!("Invalid basis to use"), + }; + let pulse_optimize = self.pulse_optimize.unwrap_or(true); + let sequence = if pulse_optimize { + self.pulse_optimal_chooser(best_nbasis, &decomposition, &target_decomposed)? + } else { + None + }; + if let Some(seq) = sequence { + return Ok(seq); + } + let target_1q_basis_list = vec![self.euler_basis]; + let euler_decompositions: SmallVec<[Option; 8]> = decomposition + .iter() + .map(|decomp| { + unitary_to_gate_sequence_inner( + decomp.view(), + &target_1q_basis_list, + 0, + None, + true, + None, + ) + }) + .collect(); + // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q gate + // We might overallocate a bit if the euler basis is different but + // the worst case is just 16 extra elements with just a String and 2 smallvecs + // each. This is only transient though as the circuit sequences aren't long lived + // and are just used to create a QuantumCircuit or DAGCircuit when we return to + // Python space. + let mut gates = Vec::with_capacity(21); + let mut global_phase = target_decomposed.global_phase; + global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; + if best_nbasis == 2 { + global_phase += PI; + } + for i in 0..best_nbasis as usize { + if let Some(euler_decomp) = &euler_decompositions[2 * i] { + for gate in &euler_decomp.gates { + gates.push((Some(gate.0), gate.1.clone(), smallvec![0])); + } + global_phase += euler_decomp.global_phase + } + if let Some(euler_decomp) = &euler_decompositions[2 * i + 1] { + for gate in &euler_decomp.gates { + gates.push((Some(gate.0), gate.1.clone(), smallvec![1])); + } + global_phase += euler_decomp.global_phase + } + gates.push((None, smallvec![], smallvec![0, 1])); + } + if let Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize] { + for gate in &euler_decomp.gates { + gates.push((Some(gate.0), gate.1.clone(), smallvec![0])); + } + global_phase += euler_decomp.global_phase + } + if let Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize + 1] { + for gate in &euler_decomp.gates { + gates.push((Some(gate.0), gate.1.clone(), smallvec![1])); + } + global_phase += euler_decomp.global_phase + } + Ok(TwoQubitGateSequence { + gates, + global_phase, + }) + } +} + +static K12R_ARR: GateArray1Q = [ + [c64(0., FRAC_1_SQRT_2), c64(FRAC_1_SQRT_2, 0.)], + [c64(-FRAC_1_SQRT_2, 0.), c64(0., -FRAC_1_SQRT_2)], +]; + +static K12L_ARR: GateArray1Q = [ + [c64(0.5, 0.5), c64(0.5, 0.5)], + [c64(-0.5, 0.5), c64(0.5, -0.5)], +]; + +fn decomp0_inner(target: &TwoQubitWeylDecomposition) -> SmallVec<[Array2; 8]> { + smallvec![target.K1r.dot(&target.K2r), target.K1l.dot(&target.K2l),] +} + +#[pymethods] +impl TwoQubitBasisDecomposer { + fn __getnewargs__(&self, py: Python) -> (String, PyObject, f64, &str, Option) { + ( + self.gate.clone(), + self.basis_decomposer + .unitary_matrix + .to_pyarray_bound(py) + .into(), + self.basis_fidelity, + self.euler_basis.as_str(), + self.pulse_optimize, + ) + } + + #[new] + #[pyo3(signature=(gate, gate_matrix, basis_fidelity=1.0, euler_basis="U", pulse_optimize=None))] + fn new( + gate: String, + gate_matrix: PyReadonlyArray2, + basis_fidelity: f64, + euler_basis: &str, + pulse_optimize: Option, + ) -> PyResult { + TwoQubitBasisDecomposer::new_inner( + gate, + gate_matrix.as_array(), + basis_fidelity, + euler_basis, + pulse_optimize, + ) + } + fn traces(&self, target: &TwoQubitWeylDecomposition) -> [Complex64; 4] { [ 4. * c64( @@ -1848,27 +2029,27 @@ impl TwoQubitBasisDecomposer { for i in 0..best_nbasis as usize { if let Some(euler_decomp) = &euler_decompositions[2 * i] { for gate in &euler_decomp.gates { - gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![0])); + gates.push((Some(gate.0), gate.1.clone(), smallvec![0])); } global_phase += euler_decomp.global_phase } if let Some(euler_decomp) = &euler_decompositions[2 * i + 1] { for gate in &euler_decomp.gates { - gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![1])); + gates.push((Some(gate.0), gate.1.clone(), smallvec![1])); } global_phase += euler_decomp.global_phase } - gates.push((self.gate.clone(), smallvec![], smallvec![0, 1])); + gates.push((None, smallvec![], smallvec![0, 1])); } if let Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize] { for gate in &euler_decomp.gates { - gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![0])); + gates.push((Some(gate.0), gate.1.clone(), smallvec![0])); } global_phase += euler_decomp.global_phase } if let Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize + 1] { for gate in &euler_decomp.gates { - gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![1])); + gates.push((Some(gate.0), gate.1.clone(), smallvec![1])); } global_phase += euler_decomp.global_phase } @@ -1878,11 +2059,102 @@ impl TwoQubitBasisDecomposer { }) } + #[pyo3(signature = (unitary, kak_gate, basis_fidelity=None, approximate=true, _num_basis_uses=None))] + fn to_circuit( + &self, + py: Python, + unitary: PyReadonlyArray2, + kak_gate: PyObject, + basis_fidelity: Option, + approximate: bool, + _num_basis_uses: Option, + ) -> PyResult { + let kak_gate = kak_gate.extract::(py)?; + let sequence = self.__call__(unitary, basis_fidelity, approximate, _num_basis_uses)?; + CircuitData::from_standard_gates( + py, + 2, + sequence + .gates + .into_iter() + .map(|(gate, params, qubits)| match gate { + Some(gate) => ( + gate, + params.into_iter().map(Param::Float).collect(), + qubits.into_iter().map(|x| Qubit(x.into())).collect(), + ), + None => ( + kak_gate.operation.standard_gate(), + kak_gate.params.clone(), + qubits.into_iter().map(|x| Qubit(x.into())).collect(), + ), + }), + Param::Float(sequence.global_phase), + ) + } + fn num_basis_gates(&self, unitary: PyReadonlyArray2) -> usize { _num_basis_gates(self.basis_decomposer.b, self.basis_fidelity, unitary) } } +fn u4_to_su4(u4: ArrayView2) -> (Array2, f64) { + let det_u = u4.into_faer_complex().determinant().to_num_complex(); + let phase_factor = det_u.powf(-0.25).conj(); + let su4 = u4.mapv(|x| x / phase_factor); + (su4, phase_factor.arg()) +} + +fn real_trace_transform(mat: ArrayView2) -> Array2 { + let a1 = -mat[[1, 3]] * mat[[2, 0]] + mat[[1, 2]] * mat[[2, 1]] + mat[[1, 1]] * mat[[2, 2]] + - mat[[1, 0]] * mat[[2, 3]]; + let a2 = mat[[0, 3]] * mat[[3, 0]] - mat[[0, 2]] * mat[[3, 1]] - mat[[0, 1]] * mat[[3, 2]] + + mat[[0, 0]] * mat[[3, 3]]; + let theta = 0.; // Arbitrary! + let phi = 0.; // This is extra arbitrary! + let psi = f64::atan2(a1.im + a2.im, a1.re - a2.re) - phi; + let im = Complex64::new(0., -1.); + let temp = [ + (theta * im).exp(), + (phi * im).exp(), + (psi * im).exp(), + (-(theta + phi + psi) * im).exp(), + ]; + Array2::from_diag(&arr1(&temp)) +} + +#[pyfunction] +fn two_qubit_decompose_up_to_diagonal( + py: Python, + mat: PyReadonlyArray2, +) -> PyResult<(PyObject, CircuitData)> { + let mat_arr: ArrayView2 = mat.as_array(); + let (su4, phase) = u4_to_su4(mat_arr); + let mut real_map = real_trace_transform(su4.view()); + let mapped_su4 = real_map.dot(&su4.view()); + let decomp = + TwoQubitBasisDecomposer::new_inner("cx".to_string(), aview2(&CX_GATE), 1.0, "U", None)?; + + let circ_seq = decomp.call_inner(mapped_su4.view(), None, true, None)?; + let circ = CircuitData::from_standard_gates( + py, + 2, + circ_seq + .gates + .into_iter() + .map(|(gate, param_floats, qubit_index)| { + let params: SmallVec<[Param; 3]> = + param_floats.into_iter().map(Param::Float).collect(); + let qubits: SmallVec<[Qubit; 2]> = + qubit_index.into_iter().map(|x| Qubit(x as u32)).collect(); + (gate.unwrap_or(StandardGate::CXGate), params, qubits) + }), + Param::Float(circ_seq.global_phase + phase), + )?; + real_map.mapv_inplace(|x| x.conj()); + Ok((real_map.into_pyarray_bound(py).into(), circ)) +} + static MAGIC: GateArray2Q = [ [ c64(FRAC_1_SQRT_2, 0.), @@ -1985,6 +2257,7 @@ pub fn local_equivalence(weyl: PyReadonlyArray1) -> PyResult<[f64; 3]> { #[pymodule] pub fn two_qubit_decompose(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(_num_basis_gates))?; + m.add_wrapped(wrap_pyfunction!(two_qubit_decompose_up_to_diagonal))?; m.add_wrapped(wrap_pyfunction!(two_qubit_local_invariants))?; m.add_wrapped(wrap_pyfunction!(local_equivalence))?; m.add_class::()?; diff --git a/crates/circuit/src/circuit_instruction.rs b/crates/circuit/src/circuit_instruction.rs index 7fc35269d1f0..3ab0fe6279f7 100644 --- a/crates/circuit/src/circuit_instruction.rs +++ b/crates/circuit/src/circuit_instruction.rs @@ -429,7 +429,9 @@ impl CircuitInstruction { if other.is_instance_of::() { return Ok(Some(self_._legacy_format(py)?.eq(other)?)); } - let Ok(other) = other.downcast::() else { return Ok(None) }; + let Ok(other) = other.downcast::() else { + return Ok(None); + }; let other = other.try_borrow()?; Ok(Some( @@ -471,7 +473,7 @@ impl CircuitInstruction { /// though you can also accept `ob: OperationFromPython` directly, if you don't also need a handle /// to the Python object that it came from. The handle is useful for the Python-operation caching. #[derive(Debug)] -pub(crate) struct OperationFromPython { +pub struct OperationFromPython { pub operation: PackedOperation, pub params: SmallVec<[Param; 3]>, pub extra_attrs: Option>, @@ -508,7 +510,10 @@ impl<'py> FromPyObject<'py> for OperationFromPython { let Some(standard) = ob_type .getattr(intern!(py, "_standard_gate")) .and_then(|standard| standard.extract::()) - .ok() else { break 'standard }; + .ok() + else { + break 'standard; + }; // If the instruction is a controlled gate with a not-all-ones control state, it doesn't // fit our definition of standard. We abuse the fact that we know our standard-gate @@ -581,7 +586,9 @@ impl<'py> FromPyObject<'py> for OperationFromPython { /// Convert a sequence-like Python object to a tuple. fn as_tuple<'py>(py: Python<'py>, seq: Option>) -> PyResult> { - let Some(seq) = seq else { return Ok(PyTuple::empty_bound(py)) }; + let Some(seq) = seq else { + return Ok(PyTuple::empty_bound(py)); + }; if seq.is_instance_of::() { Ok(seq.downcast_into_exact::()?) } else if seq.is_instance_of::() { diff --git a/crates/circuit/src/operations.rs b/crates/circuit/src/operations.rs index 493889c07d87..25eb9b1e29e7 100644 --- a/crates/circuit/src/operations.rs +++ b/crates/circuit/src/operations.rs @@ -454,7 +454,9 @@ impl StandardGate { pub fn __eq__(&self, other: &Bound) -> Py { let py = other.py(); - let Ok(other) = other.extract::() else { return py.NotImplemented() }; + let Ok(other) = other.extract::() else { + return py.NotImplemented(); + }; (*self == other).into_py(py) } diff --git a/crates/circuit/src/packed_instruction.rs b/crates/circuit/src/packed_instruction.rs index 9f7cf9c0135d..c909ca3d1b56 100644 --- a/crates/circuit/src/packed_instruction.rs +++ b/crates/circuit/src/packed_instruction.rs @@ -378,7 +378,9 @@ impl Drop for PackedOperation { fn drop_pointer_as(slf: &mut PackedOperation) { // This should only ever be called when the pointer is valid, but this is defensive just // to 100% ensure that our `Drop` implementation doesn't panic. - let Some(pointer) = slf.try_pointer() else { return }; + let Some(pointer) = slf.try_pointer() else { + return; + }; // SAFETY: `PackedOperation` asserts ownership over its contents, and the contained // pointer can only be null if we were already dropped. We set our discriminant to mark // ourselves as plain old data immediately just as a defensive measure. diff --git a/crates/pyext/src/lib.rs b/crates/pyext/src/lib.rs index 72f0d759099a..f9711641a938 100644 --- a/crates/pyext/src/lib.rs +++ b/crates/pyext/src/lib.rs @@ -18,7 +18,8 @@ use qiskit_accelerate::{ error_map::error_map, euler_one_qubit_decomposer::euler_one_qubit_decomposer, isometry::isometry, nlayout::nlayout, optimize_1q_gates::optimize_1q_gates, pauli_exp_val::pauli_expval, results::results, sabre::sabre, sampled_exp_val::sampled_exp_val, - sparse_pauli_op::sparse_pauli_op, stochastic_swap::stochastic_swap, synthesis::synthesis, + sparse_pauli_op::sparse_pauli_op, star_prerouting::star_prerouting, + stochastic_swap::stochastic_swap, synthesis::synthesis, target_transpiler::target, two_qubit_decompose::two_qubit_decompose, uc_gate::uc_gate, utils::utils, vf2_layout::vf2_layout, }; @@ -41,7 +42,9 @@ fn _accelerate(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(sabre))?; m.add_wrapped(wrap_pymodule!(sampled_exp_val))?; m.add_wrapped(wrap_pymodule!(sparse_pauli_op))?; + m.add_wrapped(wrap_pymodule!(star_prerouting))?; m.add_wrapped(wrap_pymodule!(stochastic_swap))?; + m.add_wrapped(wrap_pymodule!(target))?; m.add_wrapped(wrap_pymodule!(two_qubit_decompose))?; m.add_wrapped(wrap_pymodule!(uc_gate))?; m.add_wrapped(wrap_pymodule!(utils))?; diff --git a/docs/conf.py b/docs/conf.py index f6bf2faa9a18..1ebd77749b41 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,9 +30,9 @@ author = "Qiskit Development Team" # The short X.Y version -version = "1.2" +version = "1.3" # The full version, including alpha/beta/rc tags -release = "1.2.0" +release = "1.3.0" language = "en" diff --git a/qiskit/VERSION.txt b/qiskit/VERSION.txt index 26aaba0e8663..f0bb29e76388 100644 --- a/qiskit/VERSION.txt +++ b/qiskit/VERSION.txt @@ -1 +1 @@ -1.2.0 +1.3.0 diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 9aaa7a76a68e..6091bfa90346 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -77,7 +77,9 @@ sys.modules["qiskit._accelerate.sabre"] = _accelerate.sabre sys.modules["qiskit._accelerate.sampled_exp_val"] = _accelerate.sampled_exp_val sys.modules["qiskit._accelerate.sparse_pauli_op"] = _accelerate.sparse_pauli_op +sys.modules["qiskit._accelerate.star_prerouting"] = _accelerate.star_prerouting sys.modules["qiskit._accelerate.stochastic_swap"] = _accelerate.stochastic_swap +sys.modules["qiskit._accelerate.target"] = _accelerate.target sys.modules["qiskit._accelerate.two_qubit_decompose"] = _accelerate.two_qubit_decompose sys.modules["qiskit._accelerate.vf2_layout"] = _accelerate.vf2_layout sys.modules["qiskit._accelerate.synthesis.permutation"] = _accelerate.synthesis.permutation @@ -101,7 +103,7 @@ _config = _user_config.get_config() from qiskit.compiler import transpile, assemble, schedule, sequence - +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from .version import __version__ __all__ = [ @@ -115,4 +117,5 @@ "schedule", "sequence", "transpile", + "generate_preset_pass_manager", ] diff --git a/qiskit/assembler/assemble_circuits.py b/qiskit/assembler/assemble_circuits.py index a3d9b6bbb549..62f3df180718 100644 --- a/qiskit/assembler/assemble_circuits.py +++ b/qiskit/assembler/assemble_circuits.py @@ -12,6 +12,7 @@ """Assemble function for converting a list of circuits into a qobj.""" import copy +import warnings from collections import defaultdict from typing import Dict, List, Optional, Tuple @@ -35,6 +36,7 @@ QobjHeader, ) from qiskit.utils.parallel import parallel_map +from qiskit.utils import deprecate_func PulseLibrary = Dict[str, List[complex]] @@ -87,20 +89,26 @@ def _assemble_circuit( metadata = circuit.metadata if metadata is None: metadata = {} - header = QobjExperimentHeader( - qubit_labels=qubit_labels, - n_qubits=num_qubits, - qreg_sizes=qreg_sizes, - clbit_labels=clbit_labels, - memory_slots=memory_slots, - creg_sizes=creg_sizes, - name=circuit.name, - global_phase=float(circuit.global_phase), - metadata=metadata, - ) + with warnings.catch_warnings(): + # The class QobjExperimentHeader is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + header = QobjExperimentHeader( + qubit_labels=qubit_labels, + n_qubits=num_qubits, + qreg_sizes=qreg_sizes, + clbit_labels=clbit_labels, + memory_slots=memory_slots, + creg_sizes=creg_sizes, + name=circuit.name, + global_phase=float(circuit.global_phase), + metadata=metadata, + ) # TODO: why do we need n_qubits and memory_slots in both the header and the config - config = QasmQobjExperimentConfig(n_qubits=num_qubits, memory_slots=memory_slots) + with warnings.catch_warnings(): + # The class QasmQobjExperimentConfig is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + config = QasmQobjExperimentConfig(n_qubits=num_qubits, memory_slots=memory_slots) calibrations, pulse_library = _assemble_pulse_gates(circuit, run_config) if calibrations: config.calibrations = calibrations @@ -118,7 +126,7 @@ def _assemble_circuit( instructions = [] for op_context in circuit.data: - instruction = op_context.operation.assemble() + instruction = op_context.operation._assemble() # Add register attributes to the instruction qargs = op_context.qubits @@ -151,13 +159,16 @@ def _assemble_circuit( ] conditional_reg_idx = memory_slots + max_conditional_idx - conversion_bfunc = QasmQobjInstruction( - name="bfunc", - mask="0x%X" % mask, # pylint: disable=consider-using-f-string - relation="==", - val="0x%X" % val, # pylint: disable=consider-using-f-string - register=conditional_reg_idx, - ) + with warnings.catch_warnings(): + # The class QasmQobjInstruction is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + conversion_bfunc = QasmQobjInstruction( + name="bfunc", + mask="0x%X" % mask, # pylint: disable=consider-using-f-string + relation="==", + val="0x%X" % val, # pylint: disable=consider-using-f-string + register=conditional_reg_idx, + ) instructions.append(conversion_bfunc) instruction.conditional = conditional_reg_idx max_conditional_idx += 1 @@ -166,10 +177,13 @@ def _assemble_circuit( del instruction._condition instructions.append(instruction) - return ( - QasmQobjExperiment(instructions=instructions, header=header, config=config), - pulse_library, - ) + with warnings.catch_warnings(): + # The class QasmQobjExperiment is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + return ( + QasmQobjExperiment(instructions=instructions, header=header, config=config), + pulse_library, + ) def _assemble_pulse_gates( @@ -299,42 +313,14 @@ def _configure_experiment_los( return experiments -def assemble_circuits( +def _assemble_circuits( circuits: List[QuantumCircuit], run_config: RunConfig, qobj_id: int, qobj_header: QobjHeader ) -> QasmQobj: - """Assembles a list of circuits into a qobj that can be run on the backend. - - Args: - circuits: circuit(s) to assemble - run_config: configuration of the runtime environment - qobj_id: identifier for the generated qobj - qobj_header: header to pass to the results - - Returns: - The qobj to be run on the backends - - Examples: - - .. code-block:: python - - from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit - from qiskit.assembler import assemble_circuits - from qiskit.assembler.run_config import RunConfig - # Build a circuit to convert into a Qobj - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - qc.h(q[0]) - qc.cx(q[0], q[1]) - qc.measure(q, c) - # Assemble a Qobj from the input circuit - qobj = assemble_circuits(circuits=[qc], - qobj_id="custom-id", - qobj_header=[], - run_config=RunConfig(shots=2000, memory=True, init_qubits=True)) - """ - # assemble the circuit experiments - experiments_and_pulse_libs = parallel_map(_assemble_circuit, circuits, [run_config]) + with warnings.catch_warnings(): + # Still constructs Qobj, that is deprecated. The message is hard to trace to a module, + # because concurrency is hard. + warnings.filterwarnings("ignore", category=DeprecationWarning) + experiments_and_pulse_libs = parallel_map(_assemble_circuit, circuits, [run_config]) experiments = [] pulse_library = {} for exp, lib in experiments_and_pulse_libs: @@ -346,10 +332,16 @@ def assemble_circuits( experiments, calibrations = _extract_common_calibrations(experiments) # configure LO freqs per circuit - lo_converter = converters.LoConfigConverter(QasmQobjExperimentConfig, **run_config.to_dict()) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + lo_converter = converters.LoConfigConverter( + QasmQobjExperimentConfig, **run_config.to_dict() + ) experiments = _configure_experiment_los(experiments, lo_converter, run_config) - qobj_config = QasmQobjConfig() + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + qobj_config = QasmQobjConfig() if run_config: qobj_config_dict = run_config.to_dict() @@ -379,7 +371,10 @@ def assemble_circuits( if m_los: qobj_config_dict["meas_lo_freq"] = [freq / 1e9 for freq in m_los] - qobj_config = QasmQobjConfig(**qobj_config_dict) + with warnings.catch_warnings(): + # The class QasmQobjConfig is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + qobj_config = QasmQobjConfig(**qobj_config_dict) qubit_sizes = [] memory_slot_sizes = [] @@ -402,7 +397,55 @@ def assemble_circuits( if calibrations and calibrations.gates: qobj_config.calibrations = calibrations + with warnings.catch_warnings(): + # The class QasmQobj is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + return QasmQobj( + qobj_id=qobj_id, config=qobj_config, experiments=experiments, header=qobj_header + ) - return QasmQobj( - qobj_id=qobj_id, config=qobj_config, experiments=experiments, header=qobj_header - ) + +@deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated `BackendV1` " + "workflow, and no longer necessary for `BackendV2`. If a user workflow requires " + "`Qobj` it likely relies on deprecated functionality and should be updated to " + "use `BackendV2`.", +) +def assemble_circuits( + circuits: List[QuantumCircuit], run_config: RunConfig, qobj_id: int, qobj_header: QobjHeader +) -> QasmQobj: + """Assembles a list of circuits into a qobj that can be run on the backend. + + Args: + circuits: circuit(s) to assemble + run_config: configuration of the runtime environment + qobj_id: identifier for the generated qobj + qobj_header: header to pass to the results + + Returns: + The qobj to be run on the backends + + Examples: + + .. code-block:: python + + from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit + from qiskit.assembler import assemble_circuits + from qiskit.assembler.run_config import RunConfig + # Build a circuit to convert into a Qobj + q = QuantumRegister(2) + c = ClassicalRegister(2) + qc = QuantumCircuit(q, c) + qc.h(q[0]) + qc.cx(q[0], q[1]) + qc.measure(q, c) + # Assemble a Qobj from the input circuit + qobj = assemble_circuits(circuits=[qc], + qobj_id="custom-id", + qobj_header=[], + run_config=RunConfig(shots=2000, memory=True, init_qubits=True)) + """ + # assemble the circuit experiments + return _assemble_circuits(circuits, run_config, qobj_id, qobj_header) diff --git a/qiskit/assembler/disassemble.py b/qiskit/assembler/disassemble.py index 127bbd35eb26..74905a9a6d20 100644 --- a/qiskit/assembler/disassemble.py +++ b/qiskit/assembler/disassemble.py @@ -23,6 +23,7 @@ from qiskit.qobj import PulseQobjInstruction from qiskit.qobj.converters import QobjToInstructionConverter +from qiskit.utils import deprecate_func # A ``CircuitModule`` is a representation of a circuit execution on the backend. # It is currently a list of quantum circuits to execute, a run Qobj dictionary @@ -37,6 +38,14 @@ PulseModule = NewType("PulseModule", Tuple[List[pulse.Schedule], Dict[str, Any], Dict[str, Any]]) +@deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", +) def disassemble(qobj) -> Union[CircuitModule, PulseModule]: """Disassemble a qobj and return the circuits or pulse schedules, run_config, and user header. diff --git a/qiskit/circuit/annotated_operation.py b/qiskit/circuit/annotated_operation.py index 6780cc2e330f..6006e68f58df 100644 --- a/qiskit/circuit/annotated_operation.py +++ b/qiskit/circuit/annotated_operation.py @@ -18,6 +18,7 @@ from typing import Union, List from qiskit.circuit.operation import Operation +from qiskit.circuit.parameterexpression import ParameterValueType from qiskit.circuit._utils import _compute_control_matrix, _ctrl_state_to_int from qiskit.circuit.exceptions import CircuitError @@ -219,6 +220,27 @@ def power(self, exponent: float, annotated: bool = False): extended_modifiers.append(PowerModifier(exponent)) return AnnotatedOperation(self.base_op, extended_modifiers) + @property + def params(self) -> list[ParameterValueType]: + """The params of the underlying base operation.""" + return getattr(self.base_op, "params", []) + + @params.setter + def params(self, value: list[ParameterValueType]): + if hasattr(self.base_op, "params"): + self.base_op.params = value + else: + raise AttributeError( + f"Cannot set attribute ``params`` on the base operation {self.base_op}." + ) + + def validate_parameter(self, parameter: ParameterValueType) -> ParameterValueType: + """Validate a parameter for the underlying base operation.""" + if hasattr(self.base_op, "validate_parameter"): + return self.base_op.validate_parameter(parameter) + + raise AttributeError(f"Cannot validate parameters on the base operation {self.base_op}.") + def _canonicalize_modifiers(modifiers): """ diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index d2c88f40bdb6..37fd19e2022a 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -104,10 +104,9 @@ def control( num_ctrl_qubits: int = 1, label: str | None = None, ctrl_state: int | str | None = None, - annotated: bool = False, + annotated: bool | None = None, ): - """ - Return the controlled version of itself. + """Return the controlled version of itself. Implemented either as a controlled gate (ref. :class:`.ControlledGate`) or as an annotated operation (ref. :class:`.AnnotatedOperation`). @@ -118,8 +117,12 @@ def control( operation. ctrl_state: the control state in decimal or as a bitstring (e.g. ``'111'``). If ``None``, use ``2**num_ctrl_qubits-1``. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate is implemented + as an annotated gate. If ``None``, this is set to ``False`` + if the controlled gate can directly be constructed, and otherwise + set to ``True``. This allows defering the construction process in case the + synthesis of the controlled gate requires more information (e.g. + values of unbound parameters). Returns: Controlled version of the given operation. @@ -127,7 +130,7 @@ def control( Raises: QiskitError: unrecognized mode or invalid ctrl_state """ - if not annotated: + if not annotated: # captures both None and False # pylint: disable=cyclic-import from .add_control import add_control diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index 4bebf812e185..d67fe4b5f12f 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -34,6 +34,7 @@ from __future__ import annotations import copy +import warnings from itertools import zip_longest import math from typing import List, Type @@ -47,7 +48,7 @@ from qiskit.circuit.operation import Operation from qiskit.circuit.annotated_operation import AnnotatedOperation, InverseModifier - +from qiskit.utils import deprecate_func _CUTOFF_PRECISION = 1e-10 @@ -359,9 +360,23 @@ def unit(self, unit): """Set the time unit of duration.""" self._unit = unit + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def assemble(self): """Assemble a QasmQobjInstruction""" - instruction = QasmQobjInstruction(name=self.name) + return self._assemble() + + def _assemble(self): + with warnings.catch_warnings(): + # The class QasmQobjInstruction is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + instruction = QasmQobjInstruction(name=self.name) # Evaluate parameters if self.params: params = [x.evalf(x) if hasattr(x, "evalf") else x for x in self.params] diff --git a/qiskit/circuit/library/generalized_gates/unitary.py b/qiskit/circuit/library/generalized_gates/unitary.py index 6a6623ffce5d..9aa03be86318 100644 --- a/qiskit/circuit/library/generalized_gates/unitary.py +++ b/qiskit/circuit/library/generalized_gates/unitary.py @@ -165,7 +165,7 @@ def control( num_ctrl_qubits: int = 1, label: str | None = None, ctrl_state: int | str | None = None, - annotated: bool = False, + annotated: bool | None = None, ) -> ControlledGate | AnnotatedOperation: """Return controlled version of gate. @@ -174,8 +174,8 @@ def control( label: Optional gate label. ctrl_state: The control state in decimal or as a bit string (e.g. ``"1011"``). If ``None``, use ``2**num_ctrl_qubits - 1``. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. Returns: Controlled version of gate. diff --git a/qiskit/circuit/library/standard_gates/h.py b/qiskit/circuit/library/standard_gates/h.py index c07895ebbeaa..462ede2c93ae 100644 --- a/qiskit/circuit/library/standard_gates/h.py +++ b/qiskit/circuit/library/standard_gates/h.py @@ -11,6 +11,9 @@ # that they have been altered from the originals. """Hadamard gate.""" + +from __future__ import annotations + from math import sqrt, pi from typing import Optional, Union import numpy @@ -79,9 +82,9 @@ def _define(self): def control( self, num_ctrl_qubits: int = 1, - label: Optional[str] = None, - ctrl_state: Optional[Union[int, str]] = None, - annotated: bool = False, + label: str | None = None, + ctrl_state: int | str | None = None, + annotated: bool | None = None, ): """Return a (multi-)controlled-H gate. @@ -92,8 +95,8 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. Returns: ControlledGate: controlled version of this gate. diff --git a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py index 16907949456a..6e31c99005b3 100644 --- a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +++ b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py @@ -18,7 +18,7 @@ from typing import Optional, Union, Tuple, List import numpy as np -from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit +from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit, ParameterExpression from qiskit.circuit.library.standard_gates.x import MCXGate from qiskit.circuit.library.standard_gates.u3 import _generate_gray_code from qiskit.circuit.parameterexpression import ParameterValueType @@ -227,8 +227,8 @@ def mcrx( """ from .rx import RXGate - control_qubits = self.qbit_argument_conversion(q_controls) - target_qubit = self.qbit_argument_conversion(q_target) + control_qubits = self._qbit_argument_conversion(q_controls) + target_qubit = self._qbit_argument_conversion(q_target) if len(target_qubit) != 1: raise QiskitError("The mcrz gate needs a single qubit as target.") all_qubits = control_qubits + target_qubit @@ -258,6 +258,9 @@ def mcrx( use_basis_gates=use_basis_gates, ) else: + if isinstance(theta, ParameterExpression): + raise QiskitError(f"Cannot synthesize MCRX with unbound parameter: {theta}.") + cgate = _mcsu2_real_diagonal( RXGate(theta).to_matrix(), num_controls=len(control_qubits), @@ -272,8 +275,8 @@ def mcry( q_controls: Union[QuantumRegister, List[Qubit]], q_target: Qubit, q_ancillae: Optional[Union[QuantumRegister, Tuple[QuantumRegister, int]]] = None, - mode: str = None, - use_basis_gates=False, + mode: Optional[str] = None, + use_basis_gates: bool = False, ): """ Apply Multiple-Controlled Y rotation gate @@ -292,11 +295,11 @@ def mcry( """ from .ry import RYGate - control_qubits = self.qbit_argument_conversion(q_controls) - target_qubit = self.qbit_argument_conversion(q_target) + control_qubits = self._qbit_argument_conversion(q_controls) + target_qubit = self._qbit_argument_conversion(q_target) if len(target_qubit) != 1: raise QiskitError("The mcrz gate needs a single qubit as target.") - ancillary_qubits = [] if q_ancillae is None else self.qbit_argument_conversion(q_ancillae) + ancillary_qubits = [] if q_ancillae is None else self._qbit_argument_conversion(q_ancillae) all_qubits = control_qubits + target_qubit + ancillary_qubits target_qubit = target_qubit[0] self._check_dups(all_qubits) @@ -333,6 +336,9 @@ def mcry( use_basis_gates=use_basis_gates, ) else: + if isinstance(theta, ParameterExpression): + raise QiskitError(f"Cannot synthesize MCRY with unbound parameter: {theta}.") + cgate = _mcsu2_real_diagonal( RYGate(theta).to_matrix(), num_controls=len(control_qubits), @@ -365,8 +371,8 @@ def mcrz( """ from .rz import CRZGate, RZGate - control_qubits = self.qbit_argument_conversion(q_controls) - target_qubit = self.qbit_argument_conversion(q_target) + control_qubits = self._qbit_argument_conversion(q_controls) + target_qubit = self._qbit_argument_conversion(q_target) if len(target_qubit) != 1: raise QiskitError("The mcrz gate needs a single qubit as target.") all_qubits = control_qubits + target_qubit @@ -383,6 +389,9 @@ def mcrz( else: self.append(CRZGate(lam), control_qubits + [target_qubit]) else: + if isinstance(lam, ParameterExpression): + raise QiskitError(f"Cannot synthesize MCRZ with unbound parameter: {lam}.") + cgate = _mcsu2_real_diagonal( RZGate(lam).to_matrix(), num_controls=len(control_qubits), diff --git a/qiskit/circuit/library/standard_gates/p.py b/qiskit/circuit/library/standard_gates/p.py index 8c83aa464027..cb2c19bf51e9 100644 --- a/qiskit/circuit/library/standard_gates/p.py +++ b/qiskit/circuit/library/standard_gates/p.py @@ -99,7 +99,7 @@ def control( num_ctrl_qubits: int = 1, label: str | None = None, ctrl_state: str | int | None = None, - annotated: bool = False, + annotated: bool | None = None, ): """Return a (multi-)controlled-Phase gate. @@ -108,8 +108,8 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g. ``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. Returns: ControlledGate: controlled version of this gate. @@ -255,7 +255,7 @@ def control( num_ctrl_qubits: int = 1, label: str | None = None, ctrl_state: str | int | None = None, - annotated: bool = False, + annotated: bool | None = None, ): """Controlled version of this gate. @@ -264,8 +264,8 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. Returns: ControlledGate: controlled version of this gate. @@ -396,7 +396,7 @@ def control( num_ctrl_qubits: int = 1, label: str | None = None, ctrl_state: str | int | None = None, - annotated: bool = False, + annotated: bool | None = None, ): """Controlled version of this gate. @@ -405,8 +405,8 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. Returns: ControlledGate: controlled version of this gate. diff --git a/qiskit/circuit/library/standard_gates/rx.py b/qiskit/circuit/library/standard_gates/rx.py index cb851a740d28..4b8c9e6b446a 100644 --- a/qiskit/circuit/library/standard_gates/rx.py +++ b/qiskit/circuit/library/standard_gates/rx.py @@ -12,6 +12,8 @@ """Rotation around the X axis.""" +from __future__ import annotations + import math from math import pi from typing import Optional, Union @@ -20,7 +22,7 @@ from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit._accelerate.circuit import StandardGate @@ -78,9 +80,9 @@ def _define(self): def control( self, num_ctrl_qubits: int = 1, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - annotated: bool = False, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, ): """Return a (multi-)controlled-RX gate. @@ -89,16 +91,24 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters and more than one control qubit, in which + case it cannot yet be synthesized. Otherwise it is set to ``False``. Returns: ControlledGate: controlled version of this gate. """ + # deliberately capture annotated in [None, False] here if not annotated and num_ctrl_qubits == 1: gate = CRXGate(self.params[0], label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label else: + # If the gate parameters contain free parameters, we cannot eagerly synthesize + # the controlled gate decomposition. In this case, we annotate the gate per default. + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + gate = super().control( num_ctrl_qubits=num_ctrl_qubits, label=label, diff --git a/qiskit/circuit/library/standard_gates/rxx.py b/qiskit/circuit/library/standard_gates/rxx.py index 1c06ae05a85b..3b069aa933bb 100644 --- a/qiskit/circuit/library/standard_gates/rxx.py +++ b/qiskit/circuit/library/standard_gates/rxx.py @@ -11,12 +11,15 @@ # that they have been altered from the originals. """Two-qubit XX-rotation gate.""" + +from __future__ import annotations + import math from typing import Optional import numpy from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit._accelerate.circuit import StandardGate @@ -111,6 +114,39 @@ def _define(self): self.definition = qc + def control( + self, + num_ctrl_qubits: int = 1, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, + ): + """Return a (multi-)controlled-RXX gate. + + Args: + num_ctrl_qubits: number of control qubits. + label: An optional label for the gate [Default: ``None``] + ctrl_state: control state expressed as integer, + string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters, in which case it cannot + yet be synthesized. + + Returns: + ControlledGate: controlled version of this gate. + """ + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + + gate = super().control( + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + annotated=annotated, + ) + return gate + def inverse(self, annotated: bool = False): """Return inverse RXX gate (i.e. with the negative rotation angle). diff --git a/qiskit/circuit/library/standard_gates/ry.py b/qiskit/circuit/library/standard_gates/ry.py index b60b34ffde6f..614d4ef13a0f 100644 --- a/qiskit/circuit/library/standard_gates/ry.py +++ b/qiskit/circuit/library/standard_gates/ry.py @@ -12,6 +12,8 @@ """Rotation around the Y axis.""" +from __future__ import annotations + import math from math import pi from typing import Optional, Union @@ -19,7 +21,7 @@ from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit._accelerate.circuit import StandardGate @@ -77,9 +79,9 @@ def _define(self): def control( self, num_ctrl_qubits: int = 1, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - annotated: bool = False, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, ): """Return a (multi-)controlled-RY gate. @@ -88,16 +90,24 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters and more than one control qubit, in which + case it cannot yet be synthesized. Otherwise it is set to ``False``. Returns: ControlledGate: controlled version of this gate. """ + # deliberately capture annotated in [None, False] here if not annotated and num_ctrl_qubits == 1: gate = CRYGate(self.params[0], label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label else: + # If the gate parameters contain free parameters, we cannot eagerly synthesize + # the controlled gate decomposition. In this case, we annotate the gate per default. + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + gate = super().control( num_ctrl_qubits=num_ctrl_qubits, label=label, diff --git a/qiskit/circuit/library/standard_gates/ryy.py b/qiskit/circuit/library/standard_gates/ryy.py index 91d7d8096cf9..ad185e88d04b 100644 --- a/qiskit/circuit/library/standard_gates/ryy.py +++ b/qiskit/circuit/library/standard_gates/ryy.py @@ -11,12 +11,15 @@ # that they have been altered from the originals. """Two-qubit YY-rotation gate.""" + +from __future__ import annotations + import math from typing import Optional import numpy as np from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit._accelerate.circuit import StandardGate @@ -111,6 +114,39 @@ def _define(self): self.definition = qc + def control( + self, + num_ctrl_qubits: int = 1, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, + ): + """Return a (multi-)controlled-YY gate. + + Args: + num_ctrl_qubits: number of control qubits. + label: An optional label for the gate [Default: ``None``] + ctrl_state: control state expressed as integer, + string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters, in which case it cannot + yet be synthesized. + + Returns: + ControlledGate: controlled version of this gate. + """ + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + + gate = super().control( + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + annotated=annotated, + ) + return gate + def inverse(self, annotated: bool = False): """Return inverse RYY gate (i.e. with the negative rotation angle). diff --git a/qiskit/circuit/library/standard_gates/rz.py b/qiskit/circuit/library/standard_gates/rz.py index 78cf20efa5c6..3abef37b7534 100644 --- a/qiskit/circuit/library/standard_gates/rz.py +++ b/qiskit/circuit/library/standard_gates/rz.py @@ -11,12 +11,15 @@ # that they have been altered from the originals. """Rotation around the Z axis.""" + +from __future__ import annotations + from cmath import exp from typing import Optional, Union from qiskit.circuit.gate import Gate from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit._accelerate.circuit import StandardGate @@ -88,9 +91,9 @@ def _define(self): def control( self, num_ctrl_qubits: int = 1, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - annotated: bool = False, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, ): """Return a (multi-)controlled-RZ gate. @@ -99,16 +102,24 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters and more than one control qubit, in which + case it cannot yet be synthesized. Otherwise it is set to ``False``. Returns: ControlledGate: controlled version of this gate. """ + # deliberately capture annotated in [None, False] here if not annotated and num_ctrl_qubits == 1: gate = CRZGate(self.params[0], label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label else: + # If the gate parameters contain free parameters, we cannot eagerly synthesize + # the controlled gate decomposition. In this case, we annotate the gate per default. + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + gate = super().control( num_ctrl_qubits=num_ctrl_qubits, label=label, diff --git a/qiskit/circuit/library/standard_gates/rzx.py b/qiskit/circuit/library/standard_gates/rzx.py index 90e7b71c0a33..003805cc6b55 100644 --- a/qiskit/circuit/library/standard_gates/rzx.py +++ b/qiskit/circuit/library/standard_gates/rzx.py @@ -11,11 +11,14 @@ # that they have been altered from the originals. """Two-qubit ZX-rotation gate.""" + +from __future__ import annotations + import math from typing import Optional from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit._accelerate.circuit import StandardGate @@ -155,6 +158,39 @@ def _define(self): self.definition = qc + def control( + self, + num_ctrl_qubits: int = 1, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, + ): + """Return a (multi-)controlled-RZX gate. + + Args: + num_ctrl_qubits: number of control qubits. + label: An optional label for the gate [Default: ``None``] + ctrl_state: control state expressed as integer, + string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters, in which case it cannot + yet be synthesized. + + Returns: + ControlledGate: controlled version of this gate. + """ + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + + gate = super().control( + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + annotated=annotated, + ) + return gate + def inverse(self, annotated: bool = False): """Return inverse RZX gate (i.e. with the negative rotation angle). diff --git a/qiskit/circuit/library/standard_gates/rzz.py b/qiskit/circuit/library/standard_gates/rzz.py index 554ad4954a31..ca3e6d2db2da 100644 --- a/qiskit/circuit/library/standard_gates/rzz.py +++ b/qiskit/circuit/library/standard_gates/rzz.py @@ -11,11 +11,14 @@ # that they have been altered from the originals. """Two-qubit ZZ-rotation gate.""" + +from __future__ import annotations + from cmath import exp from typing import Optional from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit._accelerate.circuit import StandardGate @@ -119,6 +122,39 @@ def _define(self): self.definition = qc + def control( + self, + num_ctrl_qubits: int = 1, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, + ): + """Return a (multi-)controlled-RZZ gate. + + Args: + num_ctrl_qubits: number of control qubits. + label: An optional label for the gate [Default: ``None``] + ctrl_state: control state expressed as integer, + string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters, in which case it cannot + yet be synthesized. + + Returns: + ControlledGate: controlled version of this gate. + """ + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + + gate = super().control( + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + annotated=annotated, + ) + return gate + def inverse(self, annotated: bool = False): """Return inverse RZZ gate (i.e. with the negative rotation angle). diff --git a/qiskit/circuit/library/standard_gates/s.py b/qiskit/circuit/library/standard_gates/s.py index 975d1cb3be8c..e859de4b5013 100644 --- a/qiskit/circuit/library/standard_gates/s.py +++ b/qiskit/circuit/library/standard_gates/s.py @@ -12,6 +12,8 @@ """The S, Sdg, CS and CSdg gates.""" +from __future__ import annotations + from math import pi from typing import Optional, Union @@ -83,6 +85,39 @@ def _define(self): self.definition = qc + def control( + self, + num_ctrl_qubits: int = 1, + label: str | None = None, + ctrl_state: int | str | None = None, + annotated: bool | None = None, + ): + """Return a (multi-)controlled-S gate. + + One control qubit returns a :class:`.CSGate`. + + Args: + num_ctrl_qubits: number of control qubits. + label: An optional label for the gate [Default: ``None``] + ctrl_state: control state expressed as integer, + string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. + + Returns: + ControlledGate: controlled version of this gate. + """ + if not annotated and num_ctrl_qubits == 1: + gate = CSGate(label=label, ctrl_state=ctrl_state, _base_label=self.label) + else: + gate = super().control( + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + annotated=annotated, + ) + return gate + def inverse(self, annotated: bool = False): """Return inverse of S (SdgGate). @@ -162,6 +197,39 @@ def _define(self): self.definition = qc + def control( + self, + num_ctrl_qubits: int = 1, + label: str | None = None, + ctrl_state: int | str | None = None, + annotated: bool | None = None, + ): + """Return a (multi-)controlled-Sdg gate. + + One control qubit returns a :class:`.CSdgGate`. + + Args: + num_ctrl_qubits: number of control qubits. + label: An optional label for the gate [Default: ``None``] + ctrl_state: control state expressed as integer, + string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. + + Returns: + ControlledGate: controlled version of this gate. + """ + if not annotated and num_ctrl_qubits == 1: + gate = CSdgGate(label=label, ctrl_state=ctrl_state, _base_label=self.label) + else: + gate = super().control( + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + annotated=annotated, + ) + return gate + def inverse(self, annotated: bool = False): """Return inverse of Sdg (SGate). diff --git a/qiskit/circuit/library/standard_gates/swap.py b/qiskit/circuit/library/standard_gates/swap.py index 5d33bc74b8d0..84ef3046746d 100644 --- a/qiskit/circuit/library/standard_gates/swap.py +++ b/qiskit/circuit/library/standard_gates/swap.py @@ -12,6 +12,8 @@ """Swap gate.""" +from __future__ import annotations + from typing import Optional, Union import numpy from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key @@ -90,9 +92,9 @@ def _define(self): def control( self, num_ctrl_qubits: int = 1, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - annotated: bool = False, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, ): """Return a (multi-)controlled-SWAP gate. @@ -103,8 +105,8 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. Returns: ControlledGate: controlled version of this gate. diff --git a/qiskit/circuit/library/standard_gates/sx.py b/qiskit/circuit/library/standard_gates/sx.py index ec3c87653148..ec1f57a83bd3 100644 --- a/qiskit/circuit/library/standard_gates/sx.py +++ b/qiskit/circuit/library/standard_gates/sx.py @@ -12,6 +12,8 @@ """Sqrt(X) and C-Sqrt(X) gates.""" +from __future__ import annotations + from math import pi from typing import Optional, Union from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key @@ -104,9 +106,9 @@ def inverse(self, annotated: bool = False): def control( self, num_ctrl_qubits: int = 1, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - annotated: bool = False, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, ): """Return a (multi-)controlled-SX gate. @@ -117,8 +119,8 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is handled as ``False``. Returns: SingletonControlledGate: controlled version of this gate. diff --git a/qiskit/circuit/library/standard_gates/u.py b/qiskit/circuit/library/standard_gates/u.py index 7f1d32eb914c..bed454897929 100644 --- a/qiskit/circuit/library/standard_gates/u.py +++ b/qiskit/circuit/library/standard_gates/u.py @@ -11,6 +11,9 @@ # that they have been altered from the originals. """Two-pulse single-qubit gate.""" + +from __future__ import annotations + import cmath import copy as _copy import math @@ -19,7 +22,7 @@ import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit.circuit.quantumregister import QuantumRegister from qiskit._accelerate.circuit import StandardGate @@ -103,9 +106,9 @@ def inverse(self, annotated: bool = False): def control( self, num_ctrl_qubits: int = 1, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - annotated: bool = False, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, ): """Return a (multi-)controlled-U gate. @@ -114,8 +117,10 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters and more than one control qubit, in which + case it cannot yet be synthesized. Otherwise it is set to ``False``. Returns: ControlledGate: controlled version of this gate. @@ -131,6 +136,11 @@ def control( ) gate.base_gate.label = self.label else: + # If the gate parameters contain free parameters, we cannot eagerly synthesize + # the controlled gate decomposition. In this case, we annotate the gate per default. + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + gate = super().control( num_ctrl_qubits=num_ctrl_qubits, label=label, diff --git a/qiskit/circuit/library/standard_gates/u1.py b/qiskit/circuit/library/standard_gates/u1.py index e62a132670ff..e9bbed871d1f 100644 --- a/qiskit/circuit/library/standard_gates/u1.py +++ b/qiskit/circuit/library/standard_gates/u1.py @@ -128,7 +128,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: @@ -278,7 +278,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: @@ -410,7 +410,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: diff --git a/qiskit/circuit/library/standard_gates/u3.py b/qiskit/circuit/library/standard_gates/u3.py index 80581bf55a5d..df229af7d819 100644 --- a/qiskit/circuit/library/standard_gates/u3.py +++ b/qiskit/circuit/library/standard_gates/u3.py @@ -11,13 +11,16 @@ # that they have been altered from the originals. """Two-pulse single-qubit gate.""" + +from __future__ import annotations + import math from cmath import exp from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit.circuit.quantumregister import QuantumRegister from qiskit._accelerate.circuit import StandardGate @@ -115,9 +118,9 @@ def inverse(self, annotated: bool = False): def control( self, num_ctrl_qubits: int = 1, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - annotated: bool = False, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, ): """Return a (multi-)controlled-U3 gate. @@ -126,8 +129,10 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented - as an annotated gate. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters and more than one control qubit, in which + case it cannot yet be synthesized. Otherwise it is set to ``False``. Returns: ControlledGate: controlled version of this gate. @@ -136,6 +141,11 @@ def control( gate = CU3Gate(*self.params, label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label else: + # If the gate parameters contain free parameters, we cannot eagerly synthesize + # the controlled gate decomposition. In this case, we annotate the gate per default. + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + gate = super().control( num_ctrl_qubits=num_ctrl_qubits, label=label, diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py index 3688d376538a..f3f7b5ebdb72 100644 --- a/qiskit/circuit/library/standard_gates/x.py +++ b/qiskit/circuit/library/standard_gates/x.py @@ -112,7 +112,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g. ``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: @@ -257,7 +257,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g. ``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: @@ -453,7 +453,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g. ``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: @@ -801,7 +801,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g. ``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: @@ -1047,7 +1047,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g. ``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: @@ -1222,7 +1222,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g. ``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: diff --git a/qiskit/circuit/library/standard_gates/xx_minus_yy.py b/qiskit/circuit/library/standard_gates/xx_minus_yy.py index db3c3dc89153..2fac02fd154d 100644 --- a/qiskit/circuit/library/standard_gates/xx_minus_yy.py +++ b/qiskit/circuit/library/standard_gates/xx_minus_yy.py @@ -11,6 +11,9 @@ # that they have been altered from the originals. """Two-qubit XX-YY gate.""" + +from __future__ import annotations + import math from cmath import exp from math import pi @@ -24,7 +27,7 @@ from qiskit.circuit.library.standard_gates.s import SdgGate, SGate from qiskit.circuit.library.standard_gates.sx import SXdgGate, SXGate from qiskit.circuit.library.standard_gates.x import CXGate -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.quantumregister import QuantumRegister from qiskit._accelerate.circuit import StandardGate @@ -156,6 +159,39 @@ def _define(self): self.definition = circuit + def control( + self, + num_ctrl_qubits: int = 1, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, + ): + """Return a (multi-)controlled-(XX-YY) gate. + + Args: + num_ctrl_qubits: number of control qubits. + label: An optional label for the gate [Default: ``None``] + ctrl_state: control state expressed as integer, + string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters, in which case it cannot + yet be synthesized. + + Returns: + ControlledGate: controlled version of this gate. + """ + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + + gate = super().control( + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + annotated=annotated, + ) + return gate + def inverse(self, annotated: bool = False): """Inverse gate. diff --git a/qiskit/circuit/library/standard_gates/xx_plus_yy.py b/qiskit/circuit/library/standard_gates/xx_plus_yy.py index 7920454d0b98..e0528a1f1792 100644 --- a/qiskit/circuit/library/standard_gates/xx_plus_yy.py +++ b/qiskit/circuit/library/standard_gates/xx_plus_yy.py @@ -11,6 +11,9 @@ # that they have been altered from the originals. """Two-qubit XX+YY gate.""" + +from __future__ import annotations + import math from cmath import exp from math import pi @@ -20,7 +23,7 @@ from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.parameterexpression import ParameterValueType +from qiskit.circuit.parameterexpression import ParameterValueType, ParameterExpression from qiskit._accelerate.circuit import StandardGate @@ -160,6 +163,39 @@ def _define(self): self.definition = qc + def control( + self, + num_ctrl_qubits: int = 1, + label: str | None = None, + ctrl_state: str | int | None = None, + annotated: bool | None = None, + ): + """Return a (multi-)controlled-(XX+YY) gate. + + Args: + num_ctrl_qubits: number of control qubits. + label: An optional label for the gate [Default: ``None``] + ctrl_state: control state expressed as integer, + string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. + annotated: indicates whether the controlled gate should be implemented + as an annotated gate. If ``None``, this is set to ``True`` if + the gate contains free parameters, in which case it cannot + yet be synthesized. + + Returns: + ControlledGate: controlled version of this gate. + """ + if annotated is None: + annotated = any(isinstance(p, ParameterExpression) for p in self.params) + + gate = super().control( + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + annotated=annotated, + ) + return gate + def inverse(self, annotated: bool = False): """Return inverse XX+YY gate (i.e. with the negative rotation angle and same phase angle). diff --git a/qiskit/circuit/library/standard_gates/y.py b/qiskit/circuit/library/standard_gates/y.py index d62586aa2b9b..99d37ee08bd3 100644 --- a/qiskit/circuit/library/standard_gates/y.py +++ b/qiskit/circuit/library/standard_gates/y.py @@ -108,7 +108,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: diff --git a/qiskit/circuit/library/standard_gates/z.py b/qiskit/circuit/library/standard_gates/z.py index 4b2364178a94..dd83c3833d62 100644 --- a/qiskit/circuit/library/standard_gates/z.py +++ b/qiskit/circuit/library/standard_gates/z.py @@ -112,7 +112,7 @@ def control( label: An optional label for the gate [Default: ``None``] ctrl_state: control state expressed as integer, string (e.g.``'110'``), or ``None``. If ``None``, use all 1s. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index cfb04f910934..5211eb47a175 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -46,6 +46,7 @@ from qiskit.circuit.gate import Gate from qiskit.circuit.parameter import Parameter from qiskit.circuit.exceptions import CircuitError +from qiskit.utils import deprecate_func from . import _classical_resource_map from .controlflow import ControlFlowOp, _builder_utils from .controlflow.builder import CircuitScopeInterface, ControlFlowBuilderBlock @@ -1078,7 +1079,7 @@ def __init__( self.name: str """A human-readable name for the circuit.""" if name is None: - self._base_name = self.cls_prefix() + self._base_name = self._cls_prefix() self._name_update() elif not isinstance(name, str): raise CircuitError( @@ -1397,16 +1398,39 @@ def _increment_instances(cls): cls.instances += 1 @classmethod + @deprecate_func( + since=1.2, + removal_timeline="in the 2.0 release", + additional_msg="This method is only used as an internal helper " + "and will be removed with no replacement.", + ) def cls_instances(cls) -> int: """Return the current number of instances of this class, useful for auto naming.""" return cls.instances @classmethod + def _cls_instances(cls) -> int: + """Return the current number of instances of this class, + useful for auto naming.""" + return cls.instances + + @classmethod + @deprecate_func( + since=1.2, + removal_timeline="in the 2.0 release", + additional_msg="This method is only used as an internal helper " + "and will be removed with no replacement.", + ) def cls_prefix(cls) -> str: """Return the prefix to use for auto naming.""" return cls.prefix + @classmethod + def _cls_prefix(cls) -> str: + """Return the prefix to use for auto naming.""" + return cls.prefix + def _name_update(self) -> None: """update name of instance using instance number""" if not is_main_process(): @@ -1414,7 +1438,7 @@ def _name_update(self) -> None: else: pid_name = "" - self.name = f"{self._base_name}-{self.cls_instances()}{pid_name}" + self.name = f"{self._base_name}-{self._cls_instances()}{pid_name}" def has_register(self, register: Register) -> bool: """ @@ -1690,7 +1714,7 @@ def control( label (str): An optional label to give the controlled operation for visualization. ctrl_state (str or int): The control state in decimal or as a bitstring (e.g. '111'). If None, use ``2**num_ctrl_qubits - 1``. - annotated: indicates whether the controlled gate can be implemented + annotated: indicates whether the controlled gate should be implemented as an annotated gate. Returns: @@ -1923,7 +1947,7 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var: mapped_qubits = dest.qubits edge_map.update(zip(other.qubits, dest.qubits)) else: - mapped_qubits = dest.qbit_argument_conversion(qubits) + mapped_qubits = dest._qbit_argument_conversion(qubits) if len(mapped_qubits) != other.num_qubits: raise CircuitError( f"Number of items in qubits parameter ({len(mapped_qubits)}) does not" @@ -1939,7 +1963,7 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var: mapped_clbits = dest.clbits edge_map.update(zip(other.clbits, dest.clbits)) else: - mapped_clbits = dest.cbit_argument_conversion(clbits) + mapped_clbits = dest._cbit_argument_conversion(clbits) if len(mapped_clbits) != other.num_clbits: raise CircuitError( f"Number of items in clbits parameter ({len(mapped_clbits)}) does not" @@ -1949,7 +1973,7 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var: raise CircuitError( f"Duplicate clbits referenced in 'clbits' parameter: '{mapped_clbits}'" ) - edge_map.update(zip(other.clbits, dest.cbit_argument_conversion(clbits))) + edge_map.update(zip(other.clbits, dest._cbit_argument_conversion(clbits))) for gate, cals in other.calibrations.items(): dest._calibrations[gate].update(cals) @@ -2264,6 +2288,12 @@ def __getitem__(self, item): return self._data[item] @staticmethod + @deprecate_func( + since=1.2, + removal_timeline="in the 2.0 release", + additional_msg="This method is only used as an internal helper " + "and will be removed with no replacement.", + ) def cast(value: S, type_: Callable[..., T]) -> Union[S, T]: """Best effort to cast value to type. Otherwise, returns the value.""" try: @@ -2271,31 +2301,78 @@ def cast(value: S, type_: Callable[..., T]) -> Union[S, T]: except (ValueError, TypeError): return value + @staticmethod + def _cast(value: S, type_: Callable[..., T]) -> Union[S, T]: + """Best effort to cast value to type. Otherwise, returns the value.""" + try: + return type_(value) + except (ValueError, TypeError): + return value + + @deprecate_func( + since=1.2, + removal_timeline="in the 2.0 release", + additional_msg="This method is only used as an internal helper " + "and will be removed with no replacement.", + ) def qbit_argument_conversion(self, qubit_representation: QubitSpecifier) -> list[Qubit]: """ Converts several qubit representations (such as indexes, range, etc.) into a list of qubits. Args: - qubit_representation (Object): representation to expand + qubit_representation: Representation to expand. Returns: - List(Qubit): the resolved instances of the qubits. + The resolved instances of the qubits. + """ + + return self._qbit_argument_conversion(qubit_representation) + + def _qbit_argument_conversion(self, qubit_representation: QubitSpecifier) -> list[Qubit]: + """ + Converts several qubit representations (such as indexes, range, etc.) + into a list of qubits. + + Args: + qubit_representation: Representation to expand. + + Returns: + The resolved instances of the qubits. """ return _bit_argument_conversion( qubit_representation, self.qubits, self._qubit_indices, Qubit ) + @deprecate_func( + since=1.2, + removal_timeline="in the 2.0 release", + additional_msg="This method is only used as an internal helper " + "and will be removed with no replacement.", + ) def cbit_argument_conversion(self, clbit_representation: ClbitSpecifier) -> list[Clbit]: """ Converts several classical bit representations (such as indexes, range, etc.) into a list of classical bits. Args: - clbit_representation (Object): representation to expand + clbit_representation : Representation to expand. Returns: - List(tuple): Where each tuple is a classical bit. + A list of tuples where each tuple is a classical bit. + """ + return self._cbit_argument_conversion(clbit_representation) + + def _cbit_argument_conversion(self, clbit_representation: ClbitSpecifier) -> list[Clbit]: + """ + Converts several classical bit representations (such as indexes, range, etc.) + into a list of classical bits. + + Args: + clbit_representation: Representation to expand. + + Returns: + A list of tuples where each tuple is a classical bit. """ return _bit_argument_conversion( clbit_representation, self.clbits, self._clbit_indices, Clbit @@ -2314,7 +2391,7 @@ def _append_standard_gate( if params is None: params = [] - expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []] + expanded_qargs = [self._qbit_argument_conversion(qarg) for qarg in qargs or []] for param in params: Gate.validate_parameter(op, param) @@ -2413,8 +2490,8 @@ def append( " which are not in this circuit" ) - expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []] - expanded_cargs = [self.cbit_argument_conversion(carg) for carg in cargs or []] + expanded_qargs = [self._qbit_argument_conversion(qarg) for qarg in qargs or []] + expanded_cargs = [self._cbit_argument_conversion(carg) for carg in cargs or []] instructions = InstructionSet(resource_requester=circuit_scope.resolve_classical_resource) # For Operations that are non-Instructions, we use the Instruction's default method @@ -4312,7 +4389,9 @@ def barrier(self, *qargs: QubitSpecifier, label=None) -> InstructionSet: if qargs: # This uses a `dict` not a `set` to guarantee a deterministic order to the arguments. - qubits = tuple({q: None for qarg in qargs for q in self.qbit_argument_conversion(qarg)}) + qubits = tuple( + {q: None for qarg in qargs for q in self._qbit_argument_conversion(qarg)} + ) return self.append( CircuitInstruction(Barrier(len(qubits), label=label), qubits, ()), copy=False ) @@ -5344,7 +5423,7 @@ def mcx( # check ancilla input if ancilla_qubits: - _ = self.qbit_argument_conversion(ancilla_qubits) + _ = self._qbit_argument_conversion(ancilla_qubits) try: gate = available_implementations[mode] diff --git a/qiskit/circuit/quantumcircuitdata.py b/qiskit/circuit/quantumcircuitdata.py index 9ecc8e6a6cac..d4baafce12c7 100644 --- a/qiskit/circuit/quantumcircuitdata.py +++ b/qiskit/circuit/quantumcircuitdata.py @@ -53,8 +53,8 @@ def _resolve_legacy_value(self, operation, qargs, cargs) -> CircuitInstruction: if not isinstance(operation, Operation): raise CircuitError("object is not an Operation.") - expanded_qargs = [self._circuit.qbit_argument_conversion(qarg) for qarg in qargs or []] - expanded_cargs = [self._circuit.cbit_argument_conversion(carg) for carg in cargs or []] + expanded_qargs = [self._circuit._qbit_argument_conversion(qarg) for qarg in qargs or []] + expanded_cargs = [self._circuit._cbit_argument_conversion(carg) for carg in cargs or []] if isinstance(operation, Instruction): broadcast_args = list(operation.broadcast_arguments(expanded_qargs, expanded_cargs)) diff --git a/qiskit/compiler/assembler.py b/qiskit/compiler/assembler.py index 522e1c503ddf..2802169b4afb 100644 --- a/qiskit/compiler/assembler.py +++ b/qiskit/compiler/assembler.py @@ -20,7 +20,7 @@ import numpy as np -from qiskit.assembler import assemble_circuits, assemble_schedules +from qiskit.assembler import assemble_schedules from qiskit.assembler.run_config import RunConfig from qiskit.circuit import Parameter, QuantumCircuit, Qubit from qiskit.exceptions import QiskitError @@ -29,6 +29,8 @@ from qiskit.pulse.channels import PulseChannel from qiskit.qobj import QasmQobj, PulseQobj, QobjHeader from qiskit.qobj.utils import MeasLevel, MeasReturnType +from qiskit.utils import deprecate_func +from qiskit.assembler.assemble_circuits import _assemble_circuits logger = logging.getLogger(__name__) @@ -39,6 +41,14 @@ def _log_assembly_time(start_time, end_time): # TODO: parallelize over the experiments (serialize each separately, then add global header/config) +@deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", +) def assemble( experiments: Union[ QuantumCircuit, @@ -81,7 +91,7 @@ def assemble( to create ``Qobj`` "experiments". It further annotates the experiment payload with header and configurations. - NOTE: Backend.options is not used within assemble. The required values + NOTE: ``Backend.options`` is not used within assemble. The required values (previously given by backend.set_options) should be manually extracted from options and supplied directly when calling. @@ -153,27 +163,92 @@ def assemble( Raises: QiskitError: if the input cannot be interpreted as either circuits or schedules """ - start_time = time() - experiments = experiments if isinstance(experiments, list) else [experiments] - pulse_qobj = any(isinstance(exp, (ScheduleBlock, Schedule, Instruction)) for exp in experiments) - qobj_id, qobj_header, run_config_common_dict = _parse_common_args( + return _assemble( + experiments, backend, qobj_id, qobj_header, shots, memory, seed_simulator, - init_qubits, - rep_delay, qubit_lo_freq, meas_lo_freq, qubit_lo_range, meas_lo_range, schedule_los, - pulse_qobj=pulse_qobj, + meas_level, + meas_return, + meas_map, + memory_slot_size, + rep_time, + rep_delay, + parameter_binds, + parametric_pulses, + init_qubits, **run_config, ) + +def _assemble( + experiments: Union[ + QuantumCircuit, + List[QuantumCircuit], + Schedule, + List[Schedule], + ScheduleBlock, + List[ScheduleBlock], + ], + backend: Optional[Backend] = None, + qobj_id: Optional[str] = None, + qobj_header: Optional[Union[QobjHeader, Dict]] = None, + shots: Optional[int] = None, + memory: Optional[bool] = False, + seed_simulator: Optional[int] = None, + qubit_lo_freq: Optional[List[float]] = None, + meas_lo_freq: Optional[List[float]] = None, + qubit_lo_range: Optional[List[float]] = None, + meas_lo_range: Optional[List[float]] = None, + schedule_los: Optional[ + Union[ + List[Union[Dict[PulseChannel, float], LoConfig]], + Union[Dict[PulseChannel, float], LoConfig], + ] + ] = None, + meas_level: Union[int, MeasLevel] = MeasLevel.CLASSIFIED, + meas_return: Union[str, MeasReturnType] = MeasReturnType.AVERAGE, + meas_map: Optional[List[List[Qubit]]] = None, + memory_slot_size: int = 100, + rep_time: Optional[int] = None, + rep_delay: Optional[float] = None, + parameter_binds: Optional[List[Dict[Parameter, float]]] = None, + parametric_pulses: Optional[List[str]] = None, + init_qubits: bool = True, + **run_config: Dict, +) -> Union[QasmQobj, PulseQobj]: + start_time = time() + 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 + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + qobj_id, qobj_header, run_config_common_dict = _parse_common_args( + backend, + qobj_id, + qobj_header, + shots, + memory, + seed_simulator, + init_qubits, + rep_delay, + qubit_lo_freq, + meas_lo_freq, + qubit_lo_range, + meas_lo_range, + schedule_los, + pulse_qobj=pulse_qobj, + **run_config, + ) + # assemble either circuits or schedules if all(isinstance(exp, QuantumCircuit) for exp in experiments): run_config = _parse_circuit_args( @@ -191,7 +266,7 @@ def assemble( ) end_time = time() _log_assembly_time(start_time, end_time) - return assemble_circuits( + return _assemble_circuits( circuits=bound_experiments, qobj_id=qobj_id, qobj_header=qobj_header, diff --git a/qiskit/compiler/scheduler.py b/qiskit/compiler/scheduler.py index f141902b7062..e75e9a388f04 100644 --- a/qiskit/compiler/scheduler.py +++ b/qiskit/compiler/scheduler.py @@ -23,7 +23,7 @@ from qiskit.exceptions import QiskitError from qiskit.pulse import InstructionScheduleMap, Schedule from qiskit.providers.backend import Backend -from qiskit.scheduler import ScheduleConfig +from qiskit.scheduler.config import ScheduleConfig from qiskit.scheduler.schedule_circuit import schedule_circuit from qiskit.utils.parallel import parallel_map diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index 183e260739ba..adf60ca91e56 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -20,7 +20,6 @@ from qiskit import user_config from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.circuit.quantumregister import Qubit from qiskit.dagcircuit import DAGCircuit from qiskit.providers.backend import Backend from qiskit.providers.backend_compat import BackendV2Converter @@ -219,7 +218,7 @@ def transpile( # pylint: disable=too-many-return-statements * 2: heavy optimization * 3: even heavier optimization - If ``None``, level 1 will be chosen as default. + If ``None``, level 2 will be chosen as default. callback: A callback function that will be called after each pass execution. The function will be called with 5 keyword arguments, @@ -313,7 +312,7 @@ def callback_func(**kwargs): if optimization_level is None: # Take optimization level from the configuration or 1 as default. config = user_config.get_config() - optimization_level = config.get("transpile_optimization_level", 1) + optimization_level = config.get("transpile_optimization_level", 2) if backend is not None and getattr(backend, "version", 0) <= 1: # This is a temporary conversion step to allow for a smoother transition @@ -339,10 +338,7 @@ def callback_func(**kwargs): if translation_method is None and hasattr(backend, "get_translation_stage_plugin"): translation_method = backend.get_translation_stage_plugin() - initial_layout = _parse_initial_layout(initial_layout) - approximation_degree = _parse_approximation_degree(approximation_degree) output_name = _parse_output_name(output_name, circuits) - coupling_map = _parse_coupling_map(coupling_map) _check_circuits_coupling_map(circuits, coupling_map, backend) @@ -425,27 +421,6 @@ def _parse_coupling_map(coupling_map): return coupling_map -def _parse_initial_layout(initial_layout): - # initial_layout could be None, or a list of ints, e.g. [0, 5, 14] - # or a list of tuples/None e.g. [qr[0], None, qr[1]] or a dict e.g. {qr[0]: 0} - if initial_layout is None or isinstance(initial_layout, Layout): - return initial_layout - if isinstance(initial_layout, dict): - return Layout(initial_layout) - initial_layout = list(initial_layout) - if all(phys is None or isinstance(phys, Qubit) for phys in initial_layout): - return Layout.from_qubit_list(initial_layout) - return initial_layout - - -def _parse_approximation_degree(approximation_degree): - if approximation_degree is None: - return None - if approximation_degree < 0.0 or approximation_degree > 1.0: - raise TranspilerError("Approximation degree must be in [0.0, 1.0]") - return approximation_degree - - def _parse_output_name(output_name, circuits): # naming and returning circuits # output_name could be either a string or a list diff --git a/qiskit/primitives/backend_estimator.py b/qiskit/primitives/backend_estimator.py index 8446c870b1fd..f6a53c02ea9e 100644 --- a/qiskit/primitives/backend_estimator.py +++ b/qiskit/primitives/backend_estimator.py @@ -9,9 +9,8 @@ # 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. -""" -Expectation value class -""" + +"""Estimator V1 implementation for an arbitrary Backend object.""" from __future__ import annotations @@ -35,8 +34,9 @@ Optimize1qGatesDecomposition, SetLayout, ) +from qiskit.utils.deprecation import deprecate_func -from .base import BaseEstimator, EstimatorResult +from .base import BaseEstimatorV1, EstimatorResult from .primitive_job import PrimitiveJob from .utils import _circuit_key, _observable_key, init_observable @@ -88,22 +88,29 @@ def _prepare_counts(results: list[Result]): return counts -class BackendEstimator(BaseEstimator[PrimitiveJob[EstimatorResult]]): +class BackendEstimator(BaseEstimatorV1[PrimitiveJob[EstimatorResult]]): """Evaluates expectation value using Pauli rotation gates. The :class:`~.BackendEstimator` class is a generic implementation of the - :class:`~.BaseEstimator` interface that is used to wrap a :class:`~.BackendV2` - (or :class:`~.BackendV1`) object in the :class:`~.BaseEstimator` API. It + :class:`~.BaseEstimatorV1` interface that is used to wrap a :class:`~.BackendV2` + (or :class:`~.BackendV1`) object in the :class:`~.BaseEstimatorV1` API. It facilitates using backends that do not provide a native - :class:`~.BaseEstimator` implementation in places that work with - :class:`~.BaseEstimator`. However, - if you're using a provider that has a native implementation of - :class:`~.BaseEstimator`, it is a better choice to leverage that native - implementation as it will likely include additional optimizations and be - a more efficient implementation. The generic nature of this class - precludes doing any provider- or backend-specific optimizations. + :class:`~.BaseEstimatorV1` implementation in places that work with + :class:`~.BaseEstimatorV1`. + However, if you're using a provider that has a native implementation of + :class:`~.BaseEstimatorV1` or :class:`~.BaseEstimatorV2`, it is a better + choice to leverage that native implementation as it will likely include + additional optimizations and be a more efficient implementation. + The generic nature of this class precludes doing any provider- or + backend-specific optimizations. """ + @deprecate_func( + since="1.2", + additional_msg="All implementations of the `BaseEstimatorV1` interface " + "have been deprecated in favor of their V2 counterparts. " + "The V2 alternative for the `BackendEstimator` class is `BackendEstimatorV2`.", + ) def __init__( self, backend: BackendV1 | BackendV2, @@ -112,10 +119,10 @@ def __init__( bound_pass_manager: PassManager | None = None, skip_transpilation: bool = False, ): - """Initialize a new BackendEstimator instance + """Initialize a new BackendEstimator (V1) instance Args: - backend: Required: the backend to run the primitive on + backend: (required) the backend to run the primitive on options: Default options. abelian_grouping: Whether the observable should be grouped into commuting diff --git a/qiskit/primitives/backend_sampler.py b/qiskit/primitives/backend_sampler.py index f1399a548939..213b9701a556 100644 --- a/qiskit/primitives/backend_sampler.py +++ b/qiskit/primitives/backend_sampler.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Sampler implementation for an arbitrary Backend object.""" +"""Sampler V1 implementation for an arbitrary Backend object.""" from __future__ import annotations @@ -23,29 +23,37 @@ from qiskit.providers.options import Options from qiskit.result import QuasiDistribution, Result from qiskit.transpiler.passmanager import PassManager +from qiskit.utils.deprecation import deprecate_func from .backend_estimator import _prepare_counts, _run_circuits -from .base import BaseSampler, SamplerResult +from .base import BaseSamplerV1, SamplerResult from .primitive_job import PrimitiveJob from .utils import _circuit_key -class BackendSampler(BaseSampler[PrimitiveJob[SamplerResult]]): - """A :class:`~.BaseSampler` implementation that provides an interface for - leveraging the sampler interface from any backend. +class BackendSampler(BaseSamplerV1[PrimitiveJob[SamplerResult]]): + """A :class:`~.BaseSamplerV1` implementation that provides a wrapper for + leveraging the Sampler V1 interface from any backend. This class provides a sampler interface from any backend and doesn't do any measurement mitigation, it just computes the probability distribution from the counts. It facilitates using backends that do not provide a - native :class:`~.BaseSampler` implementation in places that work with - :class:`~.BaseSampler`. + native :class:`~.BaseSamplerV1` implementation in places that work with + :class:`~.BaseSamplerV1`. However, if you're using a provider that has a native implementation of - :class:`~.BaseSampler`, it is a better choice to leverage that native - implementation as it will likely include additional optimizations and be - a more efficient implementation. The generic nature of this class - precludes doing any provider- or backend-specific optimizations. + :class:`~.BaseSamplerV1` or :class:`~.BaseESamplerV2`, it is a better + choice to leverage that native implementation as it will likely include + additional optimizations and be a more efficient implementation. + The generic nature of this class precludes doing any provider- or + backend-specific optimizations. """ + @deprecate_func( + since="1.2", + additional_msg="All implementations of the `BaseSamplerV1` interface " + "have been deprecated in favor of their V2 counterparts. " + "The V2 alternative for the `BackendSampler` class is `BackendSamplerV2`.", + ) def __init__( self, backend: BackendV1 | BackendV2, @@ -53,10 +61,10 @@ def __init__( bound_pass_manager: PassManager | None = None, skip_transpilation: bool = False, ): - """Initialize a new BackendSampler + """Initialize a new BackendSampler (V1) instance Args: - backend: Required: the backend to run the sampler primitive on + backend: (required) the backend to run the sampler primitive on options: Default options. bound_pass_manager: An optional pass manager to run after parameter binding. diff --git a/qiskit/primitives/base/base_estimator.py b/qiskit/primitives/base/base_estimator.py index 0a7c0ec86289..d3d364f9951a 100644 --- a/qiskit/primitives/base/base_estimator.py +++ b/qiskit/primitives/base/base_estimator.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -r"""Base Estimator Classes""" +"""Base Estimator V1 and V2 classes""" from __future__ import annotations @@ -23,6 +23,7 @@ from qiskit.providers import JobV1 as Job from qiskit.quantum_info.operators import SparsePauliOp from qiskit.quantum_info.operators.base_operator import BaseOperator +from qiskit.utils.deprecation import deprecate_func from ..containers import ( DataBin, @@ -109,7 +110,7 @@ def __init__( options: dict | None = None, ): """ - Creating an instance of an Estimator, or using one in a ``with`` context opens a session that + Creating an instance of an Estimator V1, or using one in a ``with`` context opens a session that holds resources until the instance is ``close()`` ed or the context is exited. Args: @@ -187,7 +188,33 @@ def _run( raise NotImplementedError("The subclass of BaseEstimator must implement `_run` method.") -BaseEstimator = BaseEstimatorV1 +class BaseEstimator(BaseEstimatorV1[T]): + """DEPRECATED. Type alias for Estimator V1 base class. + + See :class:`.BaseEstimatorV1` for details. + """ + + @deprecate_func( + since="1.2", + additional_msg="The `BaseEstimator` class is a type alias for the `BaseEstimatorV1` " + "interface that has been deprecated in favor of explicitly versioned interface classes. " + "It is recommended to migrate all implementations to use `BaseEstimatorV2`. " + "However, for implementations incompatible with `BaseEstimatorV2`, `BaseEstimator` can " + "be replaced with the explicitly versioned `BaseEstimatorV1` class.", + ) + def __init__( + self, + *, + options: dict | None = None, + ): + """ + Creating an instance of an Estimator, or using one in a ``with`` context opens a session that + holds resources until the instance is ``close()`` ed or the context is exited. + + Args: + options: Default options. + """ + super().__init__(options=options) class BaseEstimatorV2(ABC): diff --git a/qiskit/primitives/base/base_primitive.py b/qiskit/primitives/base/base_primitive.py index 4519f6eb24e3..dff019aceeba 100644 --- a/qiskit/primitives/base/base_primitive.py +++ b/qiskit/primitives/base/base_primitive.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Primitive abstract base class.""" +"""Primitive V1 abstract base class.""" from __future__ import annotations @@ -20,7 +20,7 @@ class BasePrimitive(ABC): - """Primitive abstract base class.""" + """Primitive V1 abstract base class.""" def __init__(self, options: dict | None = None): self._run_options = Options() diff --git a/qiskit/primitives/base/base_result.py b/qiskit/primitives/base/base_result.py index b14c682a83f0..6a0d25c83151 100644 --- a/qiskit/primitives/base/base_result.py +++ b/qiskit/primitives/base/base_result.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ -Primitive result abstract base class +Primitive V1 result abstract base class """ from __future__ import annotations @@ -27,7 +27,7 @@ class _BasePrimitiveResult(ABC): """ - Base class for deprecated Primitive result methods. + Base class for deprecated Primitive V1 result methods. """ def __post_init__(self) -> None: diff --git a/qiskit/primitives/base/base_sampler.py b/qiskit/primitives/base/base_sampler.py index 94c9a7681d06..65d87d86b077 100644 --- a/qiskit/primitives/base/base_sampler.py +++ b/qiskit/primitives/base/base_sampler.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Base Sampler Classes""" +"""Base Sampler V1 and V2 classes""" from __future__ import annotations @@ -21,6 +21,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.providers import JobV1 as Job +from qiskit.utils.deprecation import deprecate_func from ..containers.primitive_result import PrimitiveResult from ..containers.sampler_pub import SamplerPubLike @@ -150,7 +151,30 @@ def _run( raise NotImplementedError("The subclass of BaseSampler must implement `_run` method.") -BaseSampler = BaseSamplerV1 +class BaseSampler(BaseSamplerV1[T]): + """DEPRECATED. Type alias for Sampler V1 base class + + See :class:`.BaseSamplerV1` for details. + """ + + @deprecate_func( + since="1.2", + additional_msg="The `BaseSampler` class is a type alias for the `BaseSamplerV1` " + "interface that has been deprecated in favor of explicitly versioned interface classes. " + "It is recommended to migrate all implementations to use `BaseSamplerV2`. " + "However, for implementations incompatible with `BaseSamplerV2`, `BaseSampler` can " + "be replaced with the explicitly versioned `BaseSamplerV1` class.", + ) + def __init__( + self, + *, + options: dict | None = None, + ): + """ + Args: + options: Default options. + """ + super().__init__(options=options) class BaseSamplerV2(ABC): diff --git a/qiskit/primitives/base/estimator_result.py b/qiskit/primitives/base/estimator_result.py index 88dbf1862d49..dea72484d7df 100644 --- a/qiskit/primitives/base/estimator_result.py +++ b/qiskit/primitives/base/estimator_result.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ -Estimator result class +Estimator V1 result class """ from __future__ import annotations @@ -26,7 +26,7 @@ @dataclass(frozen=True) class EstimatorResult(_BasePrimitiveResult): - """Result of Estimator. + """Result of Estimator V1. .. code-block:: python diff --git a/qiskit/primitives/base/sampler_result.py b/qiskit/primitives/base/sampler_result.py index 4a472f0e2df9..d27a0f6ba70d 100644 --- a/qiskit/primitives/base/sampler_result.py +++ b/qiskit/primitives/base/sampler_result.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ -Sampler result class +Sampler V1 result class """ from __future__ import annotations @@ -25,7 +25,7 @@ @dataclass(frozen=True) class SamplerResult(_BasePrimitiveResult): - """Result of Sampler. + """Result of Sampler V1. .. code-block:: python diff --git a/qiskit/primitives/containers/bit_array.py b/qiskit/primitives/containers/bit_array.py index 5c68893a4da2..b458b3d49232 100644 --- a/qiskit/primitives/containers/bit_array.py +++ b/qiskit/primitives/containers/bit_array.py @@ -130,10 +130,15 @@ def __repr__(self): return f"BitArray({desc})" def __getitem__(self, indices): - if isinstance(indices, tuple) and len(indices) >= self.ndim + 2: - raise ValueError( - "BitArrays cannot be sliced along the bits axis, see slice_bits() instead." - ) + if isinstance(indices, tuple): + if len(indices) == self.ndim + 1: + raise IndexError( + "BitArray cannot be sliced along the shots axis, use slice_shots() instead." + ) + if len(indices) >= self.ndim + 2: + raise IndexError( + "BitArray cannot be sliced along the bits axis, use slice_bits() instead." + ) return BitArray(self._array[indices], self.num_bits) @property @@ -233,7 +238,7 @@ def from_counts( Args: counts: One or more counts-like mappings with the same number of shots. num_bits: The desired number of bits per shot. If unset, the biggest value found sets - this value. + this value, with a minimum of one bit. Returns: A new bit array with shape ``()`` for single input counts, or ``(N,)`` for an iterable @@ -277,7 +282,7 @@ def from_samples( Args: samples: A list of bitstrings, a list of integers, or a list of hexstrings. num_bits: The desired number of bits per sample. If unset, the biggest sample provided - is used to determine this value. + is used to determine this value, with a minimum of one bit. Returns: A new bit array. @@ -300,6 +305,9 @@ def from_samples( # we are forced to prematurely look at every iterand in this case ints = list(ints) num_bits = max(map(int.bit_length, ints)) + # convention: if the only value is 0, represent with one bit: + if num_bits == 0: + num_bits = 1 num_bytes = _min_num_bytes(num_bits) data = b"".join(val.to_bytes(num_bytes, "big") for val in ints) @@ -427,13 +435,13 @@ def slice_bits(self, indices: int | Sequence[int]) -> "BitArray": A bit array sliced along the bit axis. Raises: - ValueError: If there are any invalid indices of the bit axis. + IndexError: If there are any invalid indices of the bit axis. """ if isinstance(indices, int): indices = (indices,) for index in indices: if index < 0 or index >= self.num_bits: - raise ValueError( + raise IndexError( f"index {index} is out of bounds for the number of bits {self.num_bits}." ) # This implementation introduces a temporary 8x memory overhead due to bit @@ -454,19 +462,110 @@ def slice_shots(self, indices: int | Sequence[int]) -> "BitArray": A bit array sliced along the shots axis. Raises: - ValueError: If there are any invalid indices of the shots axis. + IndexError: If there are any invalid indices of the shots axis. """ if isinstance(indices, int): indices = (indices,) for index in indices: if index < 0 or index >= self.num_shots: - raise ValueError( + raise IndexError( f"index {index} is out of bounds for the number of shots {self.num_shots}." ) arr = self._array arr = arr[..., indices, :] return BitArray(arr, self.num_bits) + def postselect( + self, + indices: Sequence[int] | int, + selection: Sequence[bool | int] | bool | int, + ) -> BitArray: + """Post-select this bit array based on sliced equality with a given bitstring. + + .. note:: + If this bit array contains any shape axes, it is first flattened into a long list of shots + before applying post-selection. This is done because :class:`~BitArray` cannot handle + ragged numbers of shots across axes. + + Args: + indices: A list of the indices of the cbits on which to postselect. + If this bit array was produced by a sampler, then an index ``i`` corresponds to the + :class:`~.ClassicalRegister` location ``creg[i]`` (as in :meth:`~slice_bits`). + Negative indices are allowed. + + selection: A list of binary values (will be cast to ``bool``) of length matching + ``indices``, with ``indices[i]`` corresponding to ``selection[i]``. Shots will be + discarded unless all cbits specified by ``indices`` have the values given by + ``selection``. + + Returns: + A new bit array with ``shape=(), num_bits=data.num_bits, num_shots<=data.num_shots``. + + Raises: + IndexError: If ``max(indices)`` is greater than or equal to :attr:`num_bits`. + IndexError: If ``min(indices)`` is less than negative :attr:`num_bits`. + ValueError: If the lengths of ``selection`` and ``indices`` do not match. + """ + if isinstance(indices, int): + indices = (indices,) + if isinstance(selection, (bool, int)): + selection = (selection,) + selection = np.asarray(selection, dtype=bool) + + num_indices = len(indices) + + if len(selection) != num_indices: + raise ValueError("Lengths of indices and selection do not match.") + + num_bytes = self._array.shape[-1] + indices = np.asarray(indices) + + if num_indices > 0: + if indices.max() >= self.num_bits: + raise IndexError( + f"index {int(indices.max())} out of bounds for the number of bits {self.num_bits}." + ) + if indices.min() < -self.num_bits: + raise IndexError( + f"index {int(indices.min())} out of bounds for the number of bits {self.num_bits}." + ) + + flattened = self.reshape((), self.size * self.num_shots) + + # If no conditions, keep all data, but flatten as promised: + if num_indices == 0: + return flattened + + # Make negative bit indices positive: + indices %= self.num_bits + + # Handle special-case of contradictory conditions: + if np.intersect1d(indices[selection], indices[np.logical_not(selection)]).size > 0: + return BitArray(np.empty((0, num_bytes), dtype=np.uint8), num_bits=self.num_bits) + + # Recall that creg[0] is the LSb: + byte_significance, bit_significance = np.divmod(indices, 8) + # least-significant byte is at last position: + byte_idx = (num_bytes - 1) - byte_significance + # least-significant bit is at position 0: + bit_offset = bit_significance.astype(np.uint8) + + # Get bitpacked representation of `indices` (bitmask): + bitmask = np.zeros(num_bytes, dtype=np.uint8) + np.bitwise_or.at(bitmask, byte_idx, np.uint8(1) << bit_offset) + + # Get bitpacked representation of `selection` (desired bitstring): + selection_bytes = np.zeros(num_bytes, dtype=np.uint8) + ## This assumes no contradictions present, since those were already checked for: + np.bitwise_or.at( + selection_bytes, byte_idx, np.asarray(selection, dtype=np.uint8) << bit_offset + ) + + return BitArray( + flattened._array[((flattened._array & bitmask) == selection_bytes).all(axis=-1)], + num_bits=self.num_bits, + ) + def expectation_values(self, observables: ObservablesArrayLike) -> NDArray[np.float64]: """Compute the expectation values of the provided observables, broadcasted against this bit array. diff --git a/qiskit/primitives/estimator.py b/qiskit/primitives/estimator.py index f300d377874d..1ca1529852c3 100644 --- a/qiskit/primitives/estimator.py +++ b/qiskit/primitives/estimator.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ -Estimator class +Estimator V1 reference implementation """ from __future__ import annotations @@ -24,8 +24,9 @@ from qiskit.exceptions import QiskitError from qiskit.quantum_info import Statevector from qiskit.quantum_info.operators.base_operator import BaseOperator +from qiskit.utils.deprecation import deprecate_func -from .base import BaseEstimator, EstimatorResult +from .base import BaseEstimatorV1, EstimatorResult from .primitive_job import PrimitiveJob from .utils import ( _circuit_key, @@ -35,9 +36,9 @@ ) -class Estimator(BaseEstimator[PrimitiveJob[EstimatorResult]]): +class Estimator(BaseEstimatorV1[PrimitiveJob[EstimatorResult]]): """ - Reference implementation of :class:`BaseEstimator`. + Reference implementation of :class:`BaseEstimatorV1`. :Run Options: @@ -51,6 +52,12 @@ class Estimator(BaseEstimator[PrimitiveJob[EstimatorResult]]): this option is ignored. """ + @deprecate_func( + since="1.2", + additional_msg="All implementations of the `BaseEstimatorV1` interface " + "have been deprecated in favor of their V2 counterparts. " + "The V2 alternative for the `Estimator` class is `StatevectorEstimator`.", + ) def __init__(self, *, options: dict | None = None): """ Args: diff --git a/qiskit/primitives/primitive_job.py b/qiskit/primitives/primitive_job.py index 5dddda27d98b..64ab8d016095 100644 --- a/qiskit/primitives/primitive_job.py +++ b/qiskit/primitives/primitive_job.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ -Job implementation for the reference implementations of Primitives. +Job for the reference implementations of Primitives V1 and V2. """ import uuid diff --git a/qiskit/primitives/sampler.py b/qiskit/primitives/sampler.py index 23a901603bef..d93db4f04116 100644 --- a/qiskit/primitives/sampler.py +++ b/qiskit/primitives/sampler.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ -Sampler class +Sampler V1 reference implementation """ from __future__ import annotations @@ -24,8 +24,9 @@ from qiskit.exceptions import QiskitError from qiskit.quantum_info import Statevector from qiskit.result import QuasiDistribution +from qiskit.utils.deprecation import deprecate_func -from .base import BaseSampler, SamplerResult +from .base import BaseSamplerV1, SamplerResult from .primitive_job import PrimitiveJob from .utils import ( _circuit_key, @@ -35,11 +36,11 @@ ) -class Sampler(BaseSampler[PrimitiveJob[SamplerResult]]): +class Sampler(BaseSamplerV1[PrimitiveJob[SamplerResult]]): """ - Sampler class. + Sampler V1 class. - :class:`~Sampler` is a reference implementation of :class:`~BaseSampler`. + :class:`~Sampler` is a reference implementation of :class:`~BaseSamplerV1`. :Run Options: @@ -52,6 +53,12 @@ class Sampler(BaseSampler[PrimitiveJob[SamplerResult]]): option is ignored. """ + @deprecate_func( + since="1.2", + additional_msg="All implementations of the `BaseSamplerV1` interface " + "have been deprecated in favor of their V2 counterparts. " + "The V2 alternative for the `Sampler` class is `StatevectorSampler`.", + ) def __init__(self, *, options: dict | None = None): """ Args: diff --git a/qiskit/primitives/statevector_estimator.py b/qiskit/primitives/statevector_estimator.py index a5dc029edf73..722291bcf42f 100644 --- a/qiskit/primitives/statevector_estimator.py +++ b/qiskit/primitives/statevector_estimator.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ -Estimator class +Statevector Estimator V2 class """ from __future__ import annotations diff --git a/qiskit/primitives/statevector_sampler.py b/qiskit/primitives/statevector_sampler.py index 90fe452ad124..58980cb79f6e 100644 --- a/qiskit/primitives/statevector_sampler.py +++ b/qiskit/primitives/statevector_sampler.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ -Statevector Sampler class +Statevector Sampler V2 class """ from __future__ import annotations @@ -207,7 +207,7 @@ def _preprocess_circuit(circuit: QuantumCircuit): qargs_index = {v: k for k, v in enumerate(qargs)} circuit = circuit.remove_final_measurements(inplace=False) if _has_control_flow(circuit): - raise QiskitError("StatevectorSampler cannot handle ControlFlowOp") + raise QiskitError("StatevectorSampler cannot handle ControlFlowOp and c_if") if _has_measure(circuit): raise QiskitError("StatevectorSampler cannot handle mid-circuit measurements") # num_qubits is used as sentinel to fill 0 in _samples_to_packed_array @@ -283,4 +283,7 @@ def _final_measurement_mapping(circuit: QuantumCircuit) -> dict[tuple[ClassicalR def _has_control_flow(circuit: QuantumCircuit) -> bool: - return any(isinstance(instruction.operation, ControlFlowOp) for instruction in circuit) + return any( + isinstance((op := instruction.operation), ControlFlowOp) or op.condition + for instruction in circuit + ) diff --git a/qiskit/primitives/utils.py b/qiskit/primitives/utils.py index d94e3355d201..db3fcbd132dc 100644 --- a/qiskit/primitives/utils.py +++ b/qiskit/primitives/utils.py @@ -25,8 +25,14 @@ from qiskit.quantum_info import PauliList, SparsePauliOp, Statevector from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.quantum_info.operators.symplectic.base_pauli import BasePauli +from qiskit.utils.deprecation import deprecate_func +@deprecate_func( + since="1.2", + additional_msg="To initialize a circuit from a ``Statevector`` instance, " + + "use ``QuantumCircuit.initialize`` instead.", +) def init_circuit(state: QuantumCircuit | Statevector) -> QuantumCircuit: """Initialize state by converting the input to a quantum circuit. @@ -45,6 +51,10 @@ def init_circuit(state: QuantumCircuit | Statevector) -> QuantumCircuit: return qc +@deprecate_func( + since="1.2", + additional_msg="Use the constructor of ``SparsePauliOp`` instead.", +) def init_observable(observable: BaseOperator | str) -> SparsePauliOp: """Initialize observable by converting the input to a :class:`~qiskit.quantum_info.SparsePauliOp`. @@ -68,6 +78,12 @@ def init_observable(observable: BaseOperator | str) -> SparsePauliOp: return SparsePauliOp(observable) +@deprecate_func( + since="1.2", + additional_msg="Use ``QuantumCircuit.layout`` and ``SparsePauliOp.apply_layout`` " + + "to adjust an operator for a layout. Otherwise, use ``mthree.utils.final_measurement_mapping``. " + + "See https://qiskit-extensions.github.io/mthree/apidocs/utils.html for details.", +) def final_measurement_mapping(circuit: QuantumCircuit) -> dict[int, int]: """Return the final measurement mapping for the circuit. diff --git a/qiskit/providers/basic_provider/basic_simulator.py b/qiskit/providers/basic_provider/basic_simulator.py index 9971bf36725c..1d1205294677 100644 --- a/qiskit/providers/basic_provider/basic_simulator.py +++ b/qiskit/providers/basic_provider/basic_simulator.py @@ -525,7 +525,7 @@ def run( } """ # TODO: replace assemble with new run flow - from qiskit.compiler import assemble + from qiskit.compiler.assembler import _assemble out_options = {} for key, value in backend_options.items(): @@ -535,7 +535,7 @@ def run( ) else: out_options[key] = value - qobj = assemble(run_input, self, **out_options) + qobj = _assemble(run_input, self, **out_options) qobj_options = qobj.config self._set_options(qobj_config=qobj_options, backend_options=backend_options) job_id = str(uuid.uuid4()) diff --git a/qiskit/providers/fake_provider/fake_backend.py b/qiskit/providers/fake_provider/fake_backend.py index 4a638f315574..21d221b68c04 100644 --- a/qiskit/providers/fake_provider/fake_backend.py +++ b/qiskit/providers/fake_provider/fake_backend.py @@ -23,7 +23,7 @@ from qiskit.providers import BackendV1 from qiskit import pulse from qiskit.exceptions import QiskitError -from qiskit.utils import optionals as _optionals +from qiskit.utils import optionals as _optionals, deprecate_func from qiskit.providers import basic_provider @@ -39,6 +39,13 @@ def __init__(self, token="123456", url="https://"): class FakeBackend(BackendV1): """This is a dummy backend just for testing purposes.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="Fake backends using BackendV1 are deprecated in favor of " + ":class:`.GenericBackendV2`. You can convert BackendV1 to " + ":class:`.BackendV2` with :class:`.BackendV2Converter`.", + ) def __init__(self, configuration, time_alive=10): """FakeBackend initializer. diff --git a/qiskit/providers/fake_provider/fake_openpulse_2q.py b/qiskit/providers/fake_provider/fake_openpulse_2q.py index caa0f53e7a38..c76f4e048828 100644 --- a/qiskit/providers/fake_provider/fake_openpulse_2q.py +++ b/qiskit/providers/fake_provider/fake_openpulse_2q.py @@ -14,6 +14,7 @@ Fake backend supporting OpenPulse. """ import datetime +import warnings from qiskit.providers.models import ( GateConfig, @@ -126,152 +127,159 @@ def __init__(self): description="A fake test backend with pulse defaults", ) - self._defaults = PulseDefaults.from_dict( - { - "qubit_freq_est": [4.9, 5.0], - "meas_freq_est": [6.5, 6.6], - "buffer": 10, - "pulse_library": [ - {"name": "x90p_d0", "samples": 2 * [0.1 + 0j]}, - {"name": "x90p_d1", "samples": 2 * [0.1 + 0j]}, - {"name": "x90m_d0", "samples": 2 * [-0.1 + 0j]}, - {"name": "x90m_d1", "samples": 2 * [-0.1 + 0j]}, - {"name": "y90p_d0", "samples": 2 * [0.1j]}, - {"name": "y90p_d1", "samples": 2 * [0.1j]}, - {"name": "xp_d0", "samples": 2 * [0.2 + 0j]}, - {"name": "ym_d0", "samples": 2 * [-0.2j]}, - {"name": "cr90p_u0", "samples": 9 * [0.1 + 0j]}, - {"name": "cr90m_u0", "samples": 9 * [-0.1 + 0j]}, - {"name": "measure_m0", "samples": 10 * [0.1 + 0j]}, - {"name": "measure_m1", "samples": 10 * [0.1 + 0j]}, - ], - "cmd_def": [ - Command.from_dict( - { - "name": "u1", - "qubits": [0], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d0", t0=0, phase="-P0" - ).to_dict() - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u1", - "qubits": [1], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d1", t0=0, phase="-P0" - ).to_dict() - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u2", - "qubits": [0], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d0", t0=0, phase="-P1" - ).to_dict(), - PulseQobjInstruction(name="y90p_d0", ch="d0", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d0", t0=2, phase="-P0" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u2", - "qubits": [1], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d1", t0=0, phase="-P1" - ).to_dict(), - PulseQobjInstruction(name="y90p_d1", ch="d1", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d1", t0=2, phase="-P0" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u3", - "qubits": [0], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d0", t0=0, phase="-P2" - ).to_dict(), - PulseQobjInstruction(name="x90p_d0", ch="d0", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d0", t0=2, phase="-P0" - ).to_dict(), - PulseQobjInstruction(name="x90m_d0", ch="d0", t0=2).to_dict(), - PulseQobjInstruction( - name="fc", ch="d0", t0=4, phase="-P1" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u3", - "qubits": [1], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d1", t0=0, phase="-P2" - ).to_dict(), - PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d1", t0=2, phase="-P0" - ).to_dict(), - PulseQobjInstruction(name="x90m_d1", ch="d1", t0=2).to_dict(), - PulseQobjInstruction( - name="fc", ch="d1", t0=4, phase="-P1" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "cx", - "qubits": [0, 1], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d0", t0=0, phase=1.57 - ).to_dict(), - PulseQobjInstruction(name="ym_d0", ch="d0", t0=0).to_dict(), - PulseQobjInstruction(name="xp_d0", ch="d0", t0=11).to_dict(), - PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), - PulseQobjInstruction(name="cr90p_u0", ch="u0", t0=2).to_dict(), - PulseQobjInstruction(name="cr90m_u0", ch="u0", t0=13).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "measure", - "qubits": [0, 1], - "sequence": [ - PulseQobjInstruction(name="measure_m0", ch="m0", t0=0).to_dict(), - PulseQobjInstruction(name="measure_m1", ch="m1", t0=0).to_dict(), - PulseQobjInstruction( - name="acquire", - duration=10, - t0=0, - qubits=[0, 1], - memory_slot=[0, 1], - ).to_dict(), - ], - } - ).to_dict(), - ], - } - ) + with warnings.catch_warnings(): + # The class PulseQobjInstruction is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + self._defaults = PulseDefaults.from_dict( + { + "qubit_freq_est": [4.9, 5.0], + "meas_freq_est": [6.5, 6.6], + "buffer": 10, + "pulse_library": [ + {"name": "x90p_d0", "samples": 2 * [0.1 + 0j]}, + {"name": "x90p_d1", "samples": 2 * [0.1 + 0j]}, + {"name": "x90m_d0", "samples": 2 * [-0.1 + 0j]}, + {"name": "x90m_d1", "samples": 2 * [-0.1 + 0j]}, + {"name": "y90p_d0", "samples": 2 * [0.1j]}, + {"name": "y90p_d1", "samples": 2 * [0.1j]}, + {"name": "xp_d0", "samples": 2 * [0.2 + 0j]}, + {"name": "ym_d0", "samples": 2 * [-0.2j]}, + {"name": "cr90p_u0", "samples": 9 * [0.1 + 0j]}, + {"name": "cr90m_u0", "samples": 9 * [-0.1 + 0j]}, + {"name": "measure_m0", "samples": 10 * [0.1 + 0j]}, + {"name": "measure_m1", "samples": 10 * [0.1 + 0j]}, + ], + "cmd_def": [ + Command.from_dict( + { + "name": "u1", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u1", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d0", ch="d0", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d1", ch="d1", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "cx", + "qubits": [0, 1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase=1.57 + ).to_dict(), + PulseQobjInstruction(name="ym_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction(name="xp_d0", ch="d0", t0=11).to_dict(), + PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction(name="cr90p_u0", ch="u0", t0=2).to_dict(), + PulseQobjInstruction(name="cr90m_u0", ch="u0", t0=13).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "measure", + "qubits": [0, 1], + "sequence": [ + PulseQobjInstruction( + name="measure_m0", ch="m0", t0=0 + ).to_dict(), + PulseQobjInstruction( + name="measure_m1", ch="m1", t0=0 + ).to_dict(), + PulseQobjInstruction( + name="acquire", + duration=10, + t0=0, + qubits=[0, 1], + memory_slot=[0, 1], + ).to_dict(), + ], + } + ).to_dict(), + ], + } + ) mock_time = datetime.datetime.now() dt = 1.3333 diff --git a/qiskit/providers/fake_provider/fake_openpulse_3q.py b/qiskit/providers/fake_provider/fake_openpulse_3q.py index 8d2529a68a2a..36b66847dad4 100644 --- a/qiskit/providers/fake_provider/fake_openpulse_3q.py +++ b/qiskit/providers/fake_provider/fake_openpulse_3q.py @@ -13,6 +13,7 @@ """ Fake backend supporting OpenPulse. """ +import warnings from qiskit.providers.models import ( GateConfig, @@ -109,223 +110,231 @@ def __init__(self): }, }, ) - - self._defaults = PulseDefaults.from_dict( - { - "qubit_freq_est": [4.9, 5.0, 4.8], - "meas_freq_est": [6.5, 6.6, 6.4], - "buffer": 10, - "pulse_library": [ - {"name": "x90p_d0", "samples": 2 * [0.1 + 0j]}, - {"name": "x90p_d1", "samples": 2 * [0.1 + 0j]}, - {"name": "x90p_d2", "samples": 2 * [0.1 + 0j]}, - {"name": "x90m_d0", "samples": 2 * [-0.1 + 0j]}, - {"name": "x90m_d1", "samples": 2 * [-0.1 + 0j]}, - {"name": "x90m_d2", "samples": 2 * [-0.1 + 0j]}, - {"name": "y90p_d0", "samples": 2 * [0.1j]}, - {"name": "y90p_d1", "samples": 2 * [0.1j]}, - {"name": "y90p_d2", "samples": 2 * [0.1j]}, - {"name": "xp_d0", "samples": 2 * [0.2 + 0j]}, - {"name": "ym_d0", "samples": 2 * [-0.2j]}, - {"name": "xp_d1", "samples": 2 * [0.2 + 0j]}, - {"name": "ym_d1", "samples": 2 * [-0.2j]}, - {"name": "cr90p_u0", "samples": 9 * [0.1 + 0j]}, - {"name": "cr90m_u0", "samples": 9 * [-0.1 + 0j]}, - {"name": "cr90p_u1", "samples": 9 * [0.1 + 0j]}, - {"name": "cr90m_u1", "samples": 9 * [-0.1 + 0j]}, - {"name": "measure_m0", "samples": 10 * [0.1 + 0j]}, - {"name": "measure_m1", "samples": 10 * [0.1 + 0j]}, - {"name": "measure_m2", "samples": 10 * [0.1 + 0j]}, - ], - "cmd_def": [ - Command.from_dict( - { - "name": "u1", - "qubits": [0], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d0", t0=0, phase="-P0" - ).to_dict() - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u1", - "qubits": [1], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d1", t0=0, phase="-P0" - ).to_dict() - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u1", - "qubits": [2], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d2", t0=0, phase="-P0" - ).to_dict() - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u2", - "qubits": [0], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d0", t0=0, phase="-P1" - ).to_dict(), - PulseQobjInstruction(name="y90p_d0", ch="d0", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d0", t0=2, phase="-P0" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u2", - "qubits": [1], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d1", t0=0, phase="-P1" - ).to_dict(), - PulseQobjInstruction(name="y90p_d1", ch="d1", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d1", t0=2, phase="-P0" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u2", - "qubits": [2], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d2", t0=0, phase="-P1" - ).to_dict(), - PulseQobjInstruction(name="y90p_d2", ch="d2", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d2", t0=2, phase="-P0" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u3", - "qubits": [0], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d0", t0=0, phase="-P2" - ).to_dict(), - PulseQobjInstruction(name="x90p_d0", ch="d0", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d0", t0=2, phase="-P0" - ).to_dict(), - PulseQobjInstruction(name="x90m_d0", ch="d0", t0=2).to_dict(), - PulseQobjInstruction( - name="fc", ch="d0", t0=4, phase="-P1" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u3", - "qubits": [1], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d1", t0=0, phase="-P2" - ).to_dict(), - PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d1", t0=2, phase="-P0" - ).to_dict(), - PulseQobjInstruction(name="x90m_d1", ch="d1", t0=2).to_dict(), - PulseQobjInstruction( - name="fc", ch="d1", t0=4, phase="-P1" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "u3", - "qubits": [2], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d2", t0=0, phase="-P2" - ).to_dict(), - PulseQobjInstruction(name="x90p_d2", ch="d2", t0=0).to_dict(), - PulseQobjInstruction( - name="fc", ch="d2", t0=2, phase="-P0" - ).to_dict(), - PulseQobjInstruction(name="x90m_d2", ch="d2", t0=2).to_dict(), - PulseQobjInstruction( - name="fc", ch="d2", t0=4, phase="-P1" - ).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "cx", - "qubits": [0, 1], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d0", t0=0, phase=1.57 - ).to_dict(), - PulseQobjInstruction(name="ym_d0", ch="d0", t0=0).to_dict(), - PulseQobjInstruction(name="xp_d0", ch="d0", t0=11).to_dict(), - PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), - PulseQobjInstruction(name="cr90p_u0", ch="u0", t0=2).to_dict(), - PulseQobjInstruction(name="cr90m_u0", ch="u0", t0=13).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "cx", - "qubits": [1, 2], - "sequence": [ - PulseQobjInstruction( - name="fc", ch="d1", t0=0, phase=1.57 - ).to_dict(), - PulseQobjInstruction(name="ym_d1", ch="d1", t0=0).to_dict(), - PulseQobjInstruction(name="xp_d1", ch="d1", t0=11).to_dict(), - PulseQobjInstruction(name="x90p_d2", ch="d2", t0=0).to_dict(), - PulseQobjInstruction(name="cr90p_u1", ch="u1", t0=2).to_dict(), - PulseQobjInstruction(name="cr90m_u1", ch="u1", t0=13).to_dict(), - ], - } - ).to_dict(), - Command.from_dict( - { - "name": "measure", - "qubits": [0, 1, 2], - "sequence": [ - PulseQobjInstruction(name="measure_m0", ch="m0", t0=0).to_dict(), - PulseQobjInstruction(name="measure_m1", ch="m1", t0=0).to_dict(), - PulseQobjInstruction(name="measure_m2", ch="m2", t0=0).to_dict(), - PulseQobjInstruction( - name="acquire", - duration=10, - t0=0, - qubits=[0, 1, 2], - memory_slot=[0, 1, 2], - ).to_dict(), - ], - } - ).to_dict(), - ], - } - ) + with warnings.catch_warnings(): + # The class PulseQobjInstruction is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + self._defaults = PulseDefaults.from_dict( + { + "qubit_freq_est": [4.9, 5.0, 4.8], + "meas_freq_est": [6.5, 6.6, 6.4], + "buffer": 10, + "pulse_library": [ + {"name": "x90p_d0", "samples": 2 * [0.1 + 0j]}, + {"name": "x90p_d1", "samples": 2 * [0.1 + 0j]}, + {"name": "x90p_d2", "samples": 2 * [0.1 + 0j]}, + {"name": "x90m_d0", "samples": 2 * [-0.1 + 0j]}, + {"name": "x90m_d1", "samples": 2 * [-0.1 + 0j]}, + {"name": "x90m_d2", "samples": 2 * [-0.1 + 0j]}, + {"name": "y90p_d0", "samples": 2 * [0.1j]}, + {"name": "y90p_d1", "samples": 2 * [0.1j]}, + {"name": "y90p_d2", "samples": 2 * [0.1j]}, + {"name": "xp_d0", "samples": 2 * [0.2 + 0j]}, + {"name": "ym_d0", "samples": 2 * [-0.2j]}, + {"name": "xp_d1", "samples": 2 * [0.2 + 0j]}, + {"name": "ym_d1", "samples": 2 * [-0.2j]}, + {"name": "cr90p_u0", "samples": 9 * [0.1 + 0j]}, + {"name": "cr90m_u0", "samples": 9 * [-0.1 + 0j]}, + {"name": "cr90p_u1", "samples": 9 * [0.1 + 0j]}, + {"name": "cr90m_u1", "samples": 9 * [-0.1 + 0j]}, + {"name": "measure_m0", "samples": 10 * [0.1 + 0j]}, + {"name": "measure_m1", "samples": 10 * [0.1 + 0j]}, + {"name": "measure_m2", "samples": 10 * [0.1 + 0j]}, + ], + "cmd_def": [ + Command.from_dict( + { + "name": "u1", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u1", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u1", + "qubits": [2], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d2", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [2], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d2", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d2", ch="d2", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d2", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d0", ch="d0", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d1", ch="d1", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [2], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d2", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d2", ch="d2", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d2", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d2", ch="d2", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d2", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "cx", + "qubits": [0, 1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase=1.57 + ).to_dict(), + PulseQobjInstruction(name="ym_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction(name="xp_d0", ch="d0", t0=11).to_dict(), + PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction(name="cr90p_u0", ch="u0", t0=2).to_dict(), + PulseQobjInstruction(name="cr90m_u0", ch="u0", t0=13).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "cx", + "qubits": [1, 2], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase=1.57 + ).to_dict(), + PulseQobjInstruction(name="ym_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction(name="xp_d1", ch="d1", t0=11).to_dict(), + PulseQobjInstruction(name="x90p_d2", ch="d2", t0=0).to_dict(), + PulseQobjInstruction(name="cr90p_u1", ch="u1", t0=2).to_dict(), + PulseQobjInstruction(name="cr90m_u1", ch="u1", t0=13).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "measure", + "qubits": [0, 1, 2], + "sequence": [ + PulseQobjInstruction( + name="measure_m0", ch="m0", t0=0 + ).to_dict(), + PulseQobjInstruction( + name="measure_m1", ch="m1", t0=0 + ).to_dict(), + PulseQobjInstruction( + name="measure_m2", ch="m2", t0=0 + ).to_dict(), + PulseQobjInstruction( + name="acquire", + duration=10, + t0=0, + qubits=[0, 1, 2], + memory_slot=[0, 1, 2], + ).to_dict(), + ], + } + ).to_dict(), + ], + } + ) super().__init__(configuration) def defaults(self): # pylint: disable=missing-function-docstring diff --git a/qiskit/providers/fake_provider/generic_backend_v2.py b/qiskit/providers/fake_provider/generic_backend_v2.py index 0da1df7eab65..6374a0b0b60a 100644 --- a/qiskit/providers/fake_provider/generic_backend_v2.py +++ b/qiskit/providers/fake_provider/generic_backend_v2.py @@ -266,27 +266,36 @@ def _get_calibration_sequence( # Note that the calibration pulses are different for # 1q gates vs 2q gates vs measurement instructions. if inst == "measure": - sequence = [ - PulseQobjInstruction( - name="acquire", - duration=1792, - t0=0, - qubits=qargs, - memory_slot=qargs, - ) - ] + [PulseQobjInstruction(name=pulse_library[1].name, ch=f"m{i}", t0=0) for i in qargs] + with warnings.catch_warnings(): + # The class PulseQobjInstruction is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + sequence = [ + PulseQobjInstruction( + name="acquire", + duration=1792, + t0=0, + qubits=qargs, + memory_slot=qargs, + ) + ] + [ + PulseQobjInstruction(name=pulse_library[1].name, ch=f"m{i}", t0=0) + for i in qargs + ] return sequence - if num_qubits == 1: + with warnings.catch_warnings(): + # The class PulseQobjInstruction is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + if num_qubits == 1: + return [ + PulseQobjInstruction(name="fc", ch=f"u{qargs[0]}", t0=0, phase="-P0"), + PulseQobjInstruction(name=pulse_library[0].name, ch=f"d{qargs[0]}", t0=0), + ] return [ - PulseQobjInstruction(name="fc", ch=f"u{qargs[0]}", t0=0, phase="-P0"), - PulseQobjInstruction(name=pulse_library[0].name, ch=f"d{qargs[0]}", t0=0), + PulseQobjInstruction(name=pulse_library[1].name, ch=f"d{qargs[0]}", t0=0), + PulseQobjInstruction(name=pulse_library[2].name, ch=f"u{qargs[0]}", t0=0), + PulseQobjInstruction(name=pulse_library[1].name, ch=f"d{qargs[1]}", t0=0), + PulseQobjInstruction(name="fc", ch=f"d{qargs[1]}", t0=0, phase=2.1), ] - return [ - PulseQobjInstruction(name=pulse_library[1].name, ch=f"d{qargs[0]}", t0=0), - PulseQobjInstruction(name=pulse_library[2].name, ch=f"u{qargs[0]}", t0=0), - PulseQobjInstruction(name=pulse_library[1].name, ch=f"d{qargs[1]}", t0=0), - PulseQobjInstruction(name="fc", ch=f"d{qargs[1]}", t0=0, phase=2.1), - ] def _generate_calibration_defaults(self) -> PulseDefaults: """Generate pulse calibration defaults as specified with `self._calibrate_instructions`. @@ -406,7 +415,9 @@ def _build_generic_target(self): noise_params = self._get_noise_defaults(name, gate.num_qubits) self._add_noisy_instruction_to_target(gate, noise_params, calibration_inst_map) else: - self._target.add_instruction(gate, properties=None, name=name) + qarg_set = self._coupling_map if gate.num_qubits > 1 else range(self.num_qubits) + props = {(qarg,) if isinstance(qarg, int) else qarg: None for qarg in qarg_set} + self._target.add_instruction(gate, properties=props, name=name) if self._control_flow: self._target.add_instruction(IfElseOp, name="if_else") @@ -556,18 +567,12 @@ def _setup_sim(self) -> None: @classmethod def _default_options(cls) -> Options: - with warnings.catch_warnings(): # TODO remove catch once aer release without Provider ABC - warnings.filterwarnings( - "ignore", - category=DeprecationWarning, - message=".+abstract Provider and ProviderV1.+", - ) - if _optionals.HAS_AER: - from qiskit_aer import AerSimulator + if _optionals.HAS_AER: + from qiskit_aer import AerSimulator - return AerSimulator._default_options() - else: - return BasicSimulator._default_options() + return AerSimulator._default_options() + else: + return BasicSimulator._default_options() def drive_channel(self, qubit: int): drive_channels_map = getattr(self, "channels_map", {}).get("drive", {}) diff --git a/qiskit/providers/models/pulsedefaults.py b/qiskit/providers/models/pulsedefaults.py index 7c1864bad9ee..1302994d40e6 100644 --- a/qiskit/providers/models/pulsedefaults.py +++ b/qiskit/providers/models/pulsedefaults.py @@ -12,6 +12,7 @@ """Model and schema for pulse defaults.""" +import warnings from typing import Any, Dict, List from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap, PulseQobjDef @@ -271,7 +272,7 @@ def from_dict(cls, data): PulseDefaults: The PulseDefaults from the input dictionary. """ schema = { - "pulse_library": PulseLibraryItem, + "pulse_library": PulseLibraryItem, # The class PulseLibraryItem is deprecated "cmd_def": Command, "meas_kernel": MeasurementKernel, "discriminator": Discriminator, @@ -282,10 +283,13 @@ def from_dict(cls, data): in_data = {} for key, value in data.items(): if key in schema: - if isinstance(value, list): - in_data[key] = list(map(schema[key].from_dict, value)) - else: - in_data[key] = schema[key].from_dict(value) + with warnings.catch_warnings(): + # The class PulseLibraryItem is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + if isinstance(value, list): + in_data[key] = list(map(schema[key].from_dict, value)) + else: + in_data[key] = schema[key].from_dict(value) else: in_data[key] = value diff --git a/qiskit/qobj/common.py b/qiskit/qobj/common.py index 9b219e5ba8d0..0f1e2372fd9a 100644 --- a/qiskit/qobj/common.py +++ b/qiskit/qobj/common.py @@ -13,6 +13,8 @@ """Module providing definitions of common Qobj classes.""" from types import SimpleNamespace +from qiskit.utils import deprecate_func + class QobjDictField(SimpleNamespace): """A class used to represent a dictionary field in Qobj @@ -21,6 +23,14 @@ class QobjDictField(SimpleNamespace): previously constructed using marshmallow. """ + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, **kwargs): """Instantiate a new Qobj dict field object. diff --git a/qiskit/qobj/converters/lo_config.py b/qiskit/qobj/converters/lo_config.py index e06ff11a8c93..a5b5beb80df2 100644 --- a/qiskit/qobj/converters/lo_config.py +++ b/qiskit/qobj/converters/lo_config.py @@ -15,6 +15,7 @@ from qiskit.pulse.channels import DriveChannel, MeasureChannel from qiskit.pulse.configuration import LoConfig from qiskit.exceptions import QiskitError +from qiskit.utils import deprecate_func class LoConfigConverter: @@ -23,6 +24,14 @@ class LoConfigConverter: ``get_qubit_los`` and ``get_meas_los`` to align with your backend. """ + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__( self, qobj_model, diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py index 8f34ee0855ac..e17036210d57 100644 --- a/qiskit/qobj/converters/pulse_instruction.py +++ b/qiskit/qobj/converters/pulse_instruction.py @@ -30,6 +30,7 @@ from qiskit.pulse.schedule import Schedule from qiskit.qobj import QobjMeasurementOption, PulseLibraryItem, PulseQobjInstruction from qiskit.qobj.utils import MeasLevel +from qiskit.utils import deprecate_func class ParametricPulseShapes(Enum): @@ -107,6 +108,14 @@ def _convert_NewInstruction(self, instruction, time_offset): where ``NewInstruction`` must be a class name of Qiskit Pulse instruction. """ + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__( self, qobj_model: PulseQobjInstruction, diff --git a/qiskit/qobj/pulse_qobj.py b/qiskit/qobj/pulse_qobj.py index 3552d83ada84..7a5ce06c8f3f 100644 --- a/qiskit/qobj/pulse_qobj.py +++ b/qiskit/qobj/pulse_qobj.py @@ -23,11 +23,20 @@ from qiskit.qobj.common import QobjDictField from qiskit.qobj.common import QobjHeader from qiskit.qobj.common import QobjExperimentHeader +from qiskit.utils import deprecate_func class QobjMeasurementOption: """An individual measurement option.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, name, params=None): """Instantiate a new QobjMeasurementOption object. @@ -89,6 +98,14 @@ class PulseQobjInstruction: "parameters", ] + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__( self, name, @@ -282,6 +299,14 @@ def _to_complex(value: Union[List[float], complex]) -> complex: class PulseQobjConfig(QobjDictField): """A configuration for a Pulse Qobj.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__( self, meas_level, @@ -383,6 +408,14 @@ class PulseQobjExperiment: experiment as part of a larger Pulse Qobj. """ + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, instructions, config=None, header=None): """Instantiate a PulseQobjExperiment. @@ -473,6 +506,14 @@ def __eq__(self, other): class PulseQobjExperimentConfig(QobjDictField): """A config for a single Pulse experiment in the qobj.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, qubit_lo_freq=None, meas_lo_freq=None, **kwargs): """Instantiate a PulseQobjExperimentConfig object. @@ -492,6 +533,14 @@ def __init__(self, qubit_lo_freq=None, meas_lo_freq=None, **kwargs): class PulseLibraryItem: """An item in a pulse library.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, name, samples): """Instantiate a pulse library item. @@ -542,6 +591,14 @@ def __eq__(self, other): class PulseQobj: """A Pulse Qobj.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, qobj_id, config, experiments, header=None): """Instantiate a new Pulse Qobj Object. diff --git a/qiskit/qobj/qasm_qobj.py b/qiskit/qobj/qasm_qobj.py index 88d775b3b773..8858913fb47c 100644 --- a/qiskit/qobj/qasm_qobj.py +++ b/qiskit/qobj/qasm_qobj.py @@ -18,11 +18,20 @@ from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.qobj.pulse_qobj import PulseQobjInstruction, PulseLibraryItem from qiskit.qobj.common import QobjDictField, QobjHeader +from qiskit.utils import deprecate_func class QasmQobjInstruction: """A class representing a single instruction in an QasmQobj Experiment.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__( self, name, @@ -200,6 +209,14 @@ class QasmQobjExperiment: part of a larger OpenQASM 2 qobj. """ + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, config=None, header=None, instructions=None): """Instantiate a QasmQobjExperiment. @@ -276,6 +293,14 @@ def __eq__(self, other): class QasmQobjConfig(SimpleNamespace): """A configuration for an OpenQASM 2 Qobj.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__( self, shots=None, @@ -410,6 +435,14 @@ class QasmQobjExperimentHeader(QobjDictField): class QasmQobjExperimentConfig(QobjDictField): """Configuration for a single OpenQASM 2 experiment in the qobj.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, calibrations=None, qubit_lo_freq=None, meas_lo_freq=None, **kwargs): """ Args: @@ -446,6 +479,14 @@ class QasmExperimentCalibrations: GateCalibrations. """ + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, gates): """ Initialize a container for calibrations. @@ -486,6 +527,14 @@ class GateCalibration: """Each calibration specifies a unique gate by name, qubits and params, and contains the Pulse instructions to implement it.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, name, qubits, params, instructions): """ Initialize a single gate calibration. Instructions may reference waveforms which should be @@ -541,6 +590,14 @@ def from_dict(cls, data): class QasmQobj: """An OpenQASM 2 Qobj.""" + @deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated " + "`BackendV1` workflow, and no longer necessary for `BackendV2`. If a user " + "workflow requires `Qobj` it likely relies on deprecated functionality and " + "should be updated to use `BackendV2`.", + ) def __init__(self, qobj_id=None, config=None, experiments=None, header=None): """Instantiate a new OpenQASM 2 Qobj Object. diff --git a/qiskit/qobj/utils.py b/qiskit/qobj/utils.py index a689fc01e7e7..1e691d6324bf 100644 --- a/qiskit/qobj/utils.py +++ b/qiskit/qobj/utils.py @@ -14,7 +14,16 @@ from enum import Enum, IntEnum +from qiskit.utils import deprecate_func + +@deprecate_func( + since="1.2", + removal_timeline="in the 2.0 release", + additional_msg="The `Qobj` class and related functionality are part of the deprecated `BackendV1` " + "workflow, and no longer necessary for `BackendV2`. If a user workflow requires `Qobj` it likely " + "relies on deprecated functionality and should be updated to use `BackendV2`.", +) class QobjType(str, Enum): """Qobj.type allowed values.""" diff --git a/qiskit/result/models.py b/qiskit/result/models.py index 83d9e4e78d5f..93a1954ac7f4 100644 --- a/qiskit/result/models.py +++ b/qiskit/result/models.py @@ -13,6 +13,7 @@ """Schema and helper models for schema-conformant Results.""" import copy +import warnings from qiskit.qobj.utils import MeasReturnType, MeasLevel from qiskit.qobj import QobjExperimentHeader @@ -223,7 +224,10 @@ def from_dict(cls, data): in_data = copy.copy(data) data_obj = ExperimentResultData.from_dict(in_data.pop("data")) if "header" in in_data: - in_data["header"] = QobjExperimentHeader.from_dict(in_data.pop("header")) + with warnings.catch_warnings(): + # The class QobjExperimentHeader is deprecated + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + in_data["header"] = QobjExperimentHeader.from_dict(in_data.pop("header")) shots = in_data.pop("shots") success = in_data.pop("success") diff --git a/qiskit/result/result.py b/qiskit/result/result.py index 7df365785166..4a5a928f06d5 100644 --- a/qiskit/result/result.py +++ b/qiskit/result/result.py @@ -125,7 +125,9 @@ def from_dict(cls, data): in_data = copy.copy(data) in_data["results"] = [ExperimentResult.from_dict(x) for x in in_data.pop("results")] if in_data.get("header") is not None: - in_data["header"] = QobjHeader.from_dict(in_data.pop("header")) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + in_data["header"] = QobjHeader.from_dict(in_data.pop("header")) return cls(**in_data) def data(self, experiment=None): diff --git a/qiskit/synthesis/two_qubit/two_qubit_decompose.py b/qiskit/synthesis/two_qubit/two_qubit_decompose.py index 3269797827e2..86c5cba8295f 100644 --- a/qiskit/synthesis/two_qubit/two_qubit_decompose.py +++ b/qiskit/synthesis/two_qubit/two_qubit_decompose.py @@ -35,7 +35,7 @@ import numpy as np -from qiskit.circuit import QuantumRegister, QuantumCircuit, Gate +from qiskit.circuit import QuantumRegister, QuantumCircuit, Gate, CircuitInstruction from qiskit.circuit.library.standard_gates import ( CXGate, U3Gate, @@ -60,7 +60,7 @@ from qiskit._accelerate import two_qubit_decompose if TYPE_CHECKING: - from qiskit.dagcircuit.dagcircuit import DAGCircuit + from qiskit.dagcircuit.dagcircuit import DAGCircuit, DAGOpNode logger = logging.getLogger(__name__) @@ -230,13 +230,10 @@ def circuit( self, *, euler_basis: str | None = None, simplify: bool = False, atol: float = DEFAULT_ATOL ) -> QuantumCircuit: """Returns Weyl decomposition in circuit form.""" - circuit_sequence = self._inner_decomposition.circuit( + circuit_data = self._inner_decomposition.circuit( euler_basis=euler_basis, simplify=simplify, atol=atol ) - circ = QuantumCircuit(2, global_phase=circuit_sequence.global_phase) - for name, params, qubits in circuit_sequence: - getattr(circ, name)(*params, *qubits) - return circ + return QuantumCircuit._from_circuit_data(circuit_data) def actual_fidelity(self, **kwargs) -> float: """Calculates the actual fidelity of the decomposed circuit to the input unitary.""" @@ -641,47 +638,58 @@ def __call__( QiskitError: if ``pulse_optimize`` is True but we don't know how to do it. """ - sequence = self._inner_decomposer( - np.asarray(unitary, dtype=complex), - basis_fidelity, - approximate, - _num_basis_uses=_num_basis_uses, - ) - q = QuantumRegister(2) if use_dag: - from qiskit.dagcircuit.dagcircuit import DAGCircuit + from qiskit.dagcircuit.dagcircuit import DAGCircuit, DAGOpNode + + sequence = self._inner_decomposer( + np.asarray(unitary, dtype=complex), + basis_fidelity, + approximate, + _num_basis_uses=_num_basis_uses, + ) + q = QuantumRegister(2) dag = DAGCircuit() dag.global_phase = sequence.global_phase dag.add_qreg(q) - for name, params, qubits in sequence: - if name == "USER_GATE": + for gate, params, qubits in sequence: + if gate is None: dag.apply_operation_back(self.gate, tuple(q[x] for x in qubits), check=False) else: - gate = GATE_NAME_MAP[name](*params) - dag.apply_operation_back(gate, tuple(q[x] for x in qubits), check=False) + op = CircuitInstruction.from_standard( + gate, qubits=tuple(q[x] for x in qubits), params=params + ) + node = DAGOpNode.from_instruction(op, dag=dag) + dag._apply_op_node_back(node) return dag else: - circ = QuantumCircuit(q, global_phase=sequence.global_phase) - for name, params, qubits in sequence: - try: - getattr(circ, name)(*params, *qubits) - except AttributeError as exc: - if name == "USER_GATE": - circ.append(self.gate, qubits) - elif name == "u3": - gate = U3Gate(*params) - circ.append(gate, qubits) - elif name == "u2": - gate = U2Gate(*params) - circ.append(gate, qubits) - elif name == "u1": - gate = U1Gate(*params) - circ.append(gate, qubits) + if getattr(self.gate, "_standard_gate", None): + circ_data = self._inner_decomposer.to_circuit( + np.asarray(unitary, dtype=complex), + self.gate, + basis_fidelity, + approximate, + _num_basis_uses=_num_basis_uses, + ) + return QuantumCircuit._from_circuit_data(circ_data) + else: + sequence = self._inner_decomposer( + np.asarray(unitary, dtype=complex), + basis_fidelity, + approximate, + _num_basis_uses=_num_basis_uses, + ) + q = QuantumRegister(2) + circ = QuantumCircuit(q, global_phase=sequence.global_phase) + for gate, params, qubits in sequence: + if gate is None: + circ._append(self.gate, qargs=tuple(q[x] for x in qubits)) else: - raise QiskitError(f"Unknown gate {name}") from exc - - return circ + inst = CircuitInstruction.from_standard( + gate, qubits=tuple(q[x] for x in qubits), params=params + ) + circ._append(inst) + return circ def traces(self, target): r""" @@ -691,95 +699,6 @@ def traces(self, target): return self._inner_decomposer.traces(target._inner_decomposition) -class TwoQubitDecomposeUpToDiagonal: - """ - Class to decompose two qubit unitaries into the product of a diagonal gate - and another unitary gate which can be represented by two CX gates instead of the - usual three. This can be used when neighboring gates commute with the diagonal to - potentially reduce overall CX count. - """ - - def __init__(self): - sy = np.array([[0, -1j], [1j, 0]]) - self.sysy = np.kron(sy, sy) - - def _u4_to_su4(self, u4): - phase_factor = np.conj(np.linalg.det(u4) ** (-1 / u4.shape[0])) - su4 = u4 / phase_factor - return su4, cmath.phase(phase_factor) - - def _gamma(self, mat): - """ - proposition II.1: this invariant characterizes when two operators in U(4), - say u, v, are equivalent up to single qubit gates: - - u ≡ v -> Det(γ(u)) = Det(±(γ(v))) - """ - sumat, _ = self._u4_to_su4(mat) - sysy = self.sysy - return sumat @ sysy @ sumat.T @ sysy - - def _cx0_test(self, mat): - # proposition III.1: zero cx sufficient - gamma = self._gamma(mat) - evals = np.linalg.eigvals(gamma) - return np.all(np.isclose(evals, np.ones(4))) - - def _cx1_test(self, mat): - # proposition III.2: one cx sufficient - gamma = self._gamma(mat) - evals = np.linalg.eigvals(gamma) - uvals, ucnts = np.unique(np.round(evals, 10), return_counts=True) - return ( - len(uvals) == 2 - and all(ucnts == 2) - and all((np.isclose(x, 1j)) or np.isclose(x, -1j) for x in uvals) - ) - - def _cx2_test(self, mat): - # proposition III.3: two cx sufficient - gamma = self._gamma(mat) - return np.isclose(np.trace(gamma).imag, 0) - - def _real_trace_transform(self, mat): - """ - Determine diagonal gate such that - - U3 = D U2 - - Where U3 is a general two-qubit gate which takes 3 cnots, D is a - diagonal gate, and U2 is a gate which takes 2 cnots. - """ - a1 = ( - -mat[1, 3] * mat[2, 0] - + mat[1, 2] * mat[2, 1] - + mat[1, 1] * mat[2, 2] - - mat[1, 0] * mat[2, 3] - ) - a2 = ( - mat[0, 3] * mat[3, 0] - - mat[0, 2] * mat[3, 1] - - mat[0, 1] * mat[3, 2] - + mat[0, 0] * mat[3, 3] - ) - theta = 0 # arbitrary - phi = 0 # arbitrary - psi = np.arctan2(a1.imag + a2.imag, a1.real - a2.real) - phi - diag = np.diag(np.exp(-1j * np.array([theta, phi, psi, -(theta + phi + psi)]))) - return diag - - def __call__(self, mat): - """do the decomposition""" - su4, phase = self._u4_to_su4(mat) - real_map = self._real_trace_transform(su4) - mapped_su4 = real_map @ su4 - if not self._cx2_test(mapped_su4): - warnings.warn("Unitary decomposition up to diagonal may use an additionl CX gate.") - circ = two_qubit_cnot_decompose(mapped_su4) - circ.global_phase += phase - return real_map.conj(), circ - - # This weird duplicated lazy structure is for backwards compatibility; Qiskit has historically # always made ``two_qubit_cnot_decompose`` available publicly immediately on import, but it's quite # expensive to construct, and we want to defer the object's creation until it's actually used. We diff --git a/qiskit/synthesis/unitary/qsd.py b/qiskit/synthesis/unitary/qsd.py index 525daa3caf15..b6b31aaa4fec 100644 --- a/qiskit/synthesis/unitary/qsd.py +++ b/qiskit/synthesis/unitary/qsd.py @@ -30,6 +30,7 @@ from qiskit.circuit.library.generalized_gates.uc_pauli_rot import UCPauliRotGate, _EPS from qiskit.circuit.library.generalized_gates.ucry import UCRYGate from qiskit.circuit.library.generalized_gates.ucrz import UCRZGate +from qiskit._accelerate.two_qubit_decompose import two_qubit_decompose_up_to_diagonal def qs_decomposition( @@ -253,7 +254,7 @@ def _apply_a2(circ): from qiskit.quantum_info import Operator from qiskit.circuit.library.generalized_gates.unitary import UnitaryGate - decomposer = two_qubit_decompose.TwoQubitDecomposeUpToDiagonal() + decomposer = two_qubit_decompose_up_to_diagonal ccirc = transpile(circ, basis_gates=["u", "cx", "qsd2q"], optimization_level=0) ind2q = [] # collect 2q instrs @@ -275,7 +276,8 @@ def _apply_a2(circ): instr2 = ccirc.data[ind2] mat2 = Operator(instr2.operation).data # rollover - dmat, qc2cx = decomposer(mat1) + dmat, qc2cx_data = decomposer(mat1) + qc2cx = QuantumCircuit._from_circuit_data(qc2cx_data) ccirc.data[ind1] = instr1.replace(operation=qc2cx.to_gate()) mat2 = mat2 @ dmat ccirc.data[ind2] = instr2.replace(UnitaryGate(mat2)) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index d4aad32cf7e4..9ea0707272b8 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -1277,6 +1277,7 @@ from .coupling import CouplingMap from .layout import Layout, TranspileLayout from .instruction_durations import InstructionDurations +from .preset_passmanagers import generate_preset_pass_manager from .target import Target from .target import InstructionProperties from .target import QubitProperties diff --git a/qiskit/transpiler/instruction_durations.py b/qiskit/transpiler/instruction_durations.py index a34b7e787f32..85d89bb16a18 100644 --- a/qiskit/transpiler/instruction_durations.py +++ b/qiskit/transpiler/instruction_durations.py @@ -15,8 +15,7 @@ from typing import Optional, List, Tuple, Union, Iterable import qiskit.circuit -from qiskit.circuit import Barrier, Delay -from qiskit.circuit import Instruction, ParameterExpression +from qiskit.circuit import Barrier, Delay, Instruction, ParameterExpression from qiskit.circuit.duration import duration_in_dt from qiskit.providers import Backend from qiskit.transpiler.exceptions import TranspilerError diff --git a/qiskit/transpiler/passes/basis/translate_parameterized.py b/qiskit/transpiler/passes/basis/translate_parameterized.py index 9629132866dd..fd9699916f16 100644 --- a/qiskit/transpiler/passes/basis/translate_parameterized.py +++ b/qiskit/transpiler/passes/basis/translate_parameterized.py @@ -15,15 +15,13 @@ from __future__ import annotations from qiskit.circuit import Instruction, ParameterExpression, Qubit, Clbit +from qiskit.circuit.equivalence_library import EquivalenceLibrary from qiskit.converters import circuit_to_dag from qiskit.dagcircuit import DAGCircuit, DAGOpNode -from qiskit.circuit.equivalence_library import EquivalenceLibrary from qiskit.exceptions import QiskitError -from qiskit.transpiler import Target - from qiskit.transpiler.basepasses import TransformationPass - -from .basis_translator import BasisTranslator +from qiskit.transpiler.passes.basis.basis_translator import BasisTranslator +from qiskit.transpiler.target import Target class TranslateParameterizedGates(TransformationPass): diff --git a/qiskit/transpiler/passes/calibration/rx_builder.py b/qiskit/transpiler/passes/calibration/rx_builder.py index 7cf9c8a1b633..45789e4cd109 100644 --- a/qiskit/transpiler/passes/calibration/rx_builder.py +++ b/qiskit/transpiler/passes/calibration/rx_builder.py @@ -17,13 +17,13 @@ import numpy as np from qiskit.circuit import Instruction +from qiskit.circuit.library.standard_gates import RXGate +from qiskit.exceptions import QiskitError from qiskit.pulse import Schedule, ScheduleBlock, builder, ScalableSymbolicPulse from qiskit.pulse.channels import Channel from qiskit.pulse.library.symbolic_pulses import Drag from qiskit.transpiler.passes.calibration.base_builder import CalibrationBuilder -from qiskit.transpiler import Target -from qiskit.circuit.library.standard_gates import RXGate -from qiskit.exceptions import QiskitError +from qiskit.transpiler.target import Target class RXCalibrationBuilder(CalibrationBuilder): diff --git a/qiskit/transpiler/passes/layout/sabre_pre_layout.py b/qiskit/transpiler/passes/layout/sabre_pre_layout.py index 9e68fd77b019..4a7ea35e3f2a 100644 --- a/qiskit/transpiler/passes/layout/sabre_pre_layout.py +++ b/qiskit/transpiler/passes/layout/sabre_pre_layout.py @@ -14,8 +14,11 @@ import itertools -from qiskit.transpiler import CouplingMap, Target, AnalysisPass, TranspilerError +from qiskit.transpiler.basepasses import AnalysisPass +from qiskit.transpiler.coupling import CouplingMap +from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.passes.layout.vf2_layout import VF2Layout +from qiskit.transpiler.target import Target from qiskit._accelerate.error_map import ErrorMap diff --git a/qiskit/transpiler/passes/layout/set_layout.py b/qiskit/transpiler/passes/layout/set_layout.py index c4e5faa91fb5..2dd717085ff3 100644 --- a/qiskit/transpiler/passes/layout/set_layout.py +++ b/qiskit/transpiler/passes/layout/set_layout.py @@ -11,9 +11,9 @@ # that they have been altered from the originals. """Set the ``layout`` property to the given layout.""" -from qiskit.transpiler import Layout -from qiskit.transpiler.exceptions import InvalidLayoutError from qiskit.transpiler.basepasses import AnalysisPass +from qiskit.transpiler.exceptions import InvalidLayoutError +from qiskit.transpiler.layout import Layout class SetLayout(AnalysisPass): diff --git a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py index 501400f70ced..ce47981ad2a6 100644 --- a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +++ b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py @@ -17,8 +17,9 @@ from qiskit.circuit import Gate, QuantumCircuit, Qubit from qiskit.converters import circuit_to_dag from qiskit.dagcircuit import DAGCircuit, DAGOpNode -from qiskit.transpiler import TransformationPass, Layout, TranspilerError - +from qiskit.transpiler.basepasses import TransformationPass +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.layout import Layout from qiskit.transpiler.passes.routing.commuting_2q_gate_routing.swap_strategy import SwapStrategy from qiskit.transpiler.passes.routing.commuting_2q_gate_routing.commuting_2q_block import ( Commuting2qBlock, diff --git a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py index 72b62dc49a81..825c298dd304 100644 --- a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py +++ b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py @@ -17,7 +17,7 @@ import numpy as np from qiskit.exceptions import QiskitError -from qiskit.transpiler import CouplingMap +from qiskit.transpiler.coupling import CouplingMap class SwapStrategy: diff --git a/qiskit/transpiler/passes/routing/layout_transformation.py b/qiskit/transpiler/passes/routing/layout_transformation.py index 42bf44e13f2a..01be9a3c1261 100644 --- a/qiskit/transpiler/passes/routing/layout_transformation.py +++ b/qiskit/transpiler/passes/routing/layout_transformation.py @@ -15,9 +15,10 @@ import numpy as np -from qiskit.transpiler import Layout, CouplingMap from qiskit.transpiler.basepasses import TransformationPass +from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.layout import Layout from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper from qiskit.transpiler.target import Target diff --git a/qiskit/transpiler/passes/routing/star_prerouting.py b/qiskit/transpiler/passes/routing/star_prerouting.py index 3679e8bfb8e3..53bc971a268b 100644 --- a/qiskit/transpiler/passes/routing/star_prerouting.py +++ b/qiskit/transpiler/passes/routing/star_prerouting.py @@ -14,11 +14,15 @@ from typing import Iterable, Union, Optional, List, Tuple from math import floor, log10 -from qiskit.circuit import Barrier +from qiskit.circuit import SwitchCaseOp, Clbit, ClassicalRegister, Barrier +from qiskit.circuit.controlflow import condition_resources, node_resources from qiskit.dagcircuit import DAGOpNode, DAGDepNode, DAGDependency, DAGCircuit -from qiskit.transpiler import Layout from qiskit.transpiler.basepasses import TransformationPass -from qiskit.circuit.library import SwapGate +from qiskit.transpiler.layout import Layout +from qiskit.transpiler.passes.routing.sabre_swap import _build_sabre_dag, _apply_sabre_result + +from qiskit._accelerate import star_prerouting +from qiskit._accelerate.nlayout import NLayout class StarBlock: @@ -305,113 +309,84 @@ def star_preroute(self, dag, blocks, processing_order): new_dag: a dag specifying the pre-routed circuit qubit_mapping: the final qubit mapping after pre-routing """ - node_to_block_id = {} - for i, block in enumerate(blocks): - for node in block.get_nodes(): - node_to_block_id[node] = i - - new_dag = dag.copy_empty_like() - processed_block_ids = set() - qubit_mapping = list(range(len(dag.qubits))) - - def _apply_mapping(qargs, qubit_mapping, qubits): - return tuple(qubits[qubit_mapping[dag.find_bit(qubit).index]] for qubit in qargs) - - is_first_star = True - last_2q_gate = [ - op - for op in reversed(processing_order) - if ((len(op.qargs) > 1) and (op.name != "barrier")) + # Convert the DAG to a SabreDAG + num_qubits = len(dag.qubits) + canonical_register = dag.qregs["q"] + current_layout = Layout.generate_trivial_layout(canonical_register) + qubit_indices = {bit: idx for idx, bit in enumerate(canonical_register)} + layout_mapping = {qubit_indices[k]: v for k, v in current_layout.get_virtual_bits().items()} + initial_layout = NLayout(layout_mapping, num_qubits, num_qubits) + sabre_dag, circuit_to_dag_dict = _build_sabre_dag(dag, num_qubits, qubit_indices) + + # Extract the nodes from the blocks for the Rust representation + rust_blocks = [ + (block.center is not None, _extract_nodes(block.get_nodes(), dag)) for block in blocks ] - if len(last_2q_gate) > 0: - last_2q_gate = last_2q_gate[0] - else: - last_2q_gate = None + # Determine the processing order of the nodes in the DAG for the Rust representation int_digits = floor(log10(len(processing_order))) + 1 processing_order_index_map = { - node: f"a{str(index).zfill(int(int_digits))}" - for index, node in enumerate(processing_order) + node: f"a{index:0{int_digits}}" for index, node in enumerate(processing_order) } def tie_breaker_key(node): return processing_order_index_map.get(node, node.sort_key) - for node in dag.topological_op_nodes(key=tie_breaker_key): - block_id = node_to_block_id.get(node, None) - if block_id is not None: - if block_id in processed_block_ids: - continue - - processed_block_ids.add(block_id) - - # process the whole block - block = blocks[block_id] - sequence = block.nodes - center_node = block.center - - if len(sequence) == 2: - for inner_node in sequence: - new_dag.apply_operation_back( - inner_node.op, - _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits), - inner_node.cargs, - check=False, - ) - continue - swap_source = None - prev = None - for inner_node in sequence: - if (len(inner_node.qargs) == 1) or (inner_node.qargs == prev): - new_dag.apply_operation_back( - inner_node.op, - _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits), - inner_node.cargs, - check=False, - ) - continue - if is_first_star and swap_source is None: - swap_source = center_node - new_dag.apply_operation_back( - inner_node.op, - _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits), - inner_node.cargs, - check=False, - ) - - prev = inner_node.qargs - continue - # place 2q-gate and subsequent swap gate - new_dag.apply_operation_back( - inner_node.op, - _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits), - inner_node.cargs, - check=False, - ) - - if not inner_node is last_2q_gate and not isinstance(inner_node.op, Barrier): - new_dag.apply_operation_back( - SwapGate(), - _apply_mapping(inner_node.qargs, qubit_mapping, dag.qubits), - inner_node.cargs, - check=False, - ) - # Swap mapping - index_0 = dag.find_bit(inner_node.qargs[0]).index - index_1 = dag.find_bit(inner_node.qargs[1]).index - qubit_mapping[index_1], qubit_mapping[index_0] = ( - qubit_mapping[index_0], - qubit_mapping[index_1], - ) - - prev = inner_node.qargs - is_first_star = False - else: - # the node is not part of a block - new_dag.apply_operation_back( - node.op, - _apply_mapping(node.qargs, qubit_mapping, dag.qubits), - node.cargs, - check=False, - ) - return new_dag, qubit_mapping + rust_processing_order = _extract_nodes(dag.topological_op_nodes(key=tie_breaker_key), dag) + + # Run the star prerouting algorithm to obtain the new DAG and qubit mapping + *sabre_result, qubit_mapping = star_prerouting.star_preroute( + sabre_dag, rust_blocks, rust_processing_order + ) + + res_dag = _apply_sabre_result( + dag.copy_empty_like(), + dag, + sabre_result, + initial_layout, + dag.qubits, + circuit_to_dag_dict, + ) + + return res_dag, qubit_mapping + + +def _extract_nodes(nodes, dag): + """Extract and format node information for Rust representation used in SabreDAG. + + Each node is represented as a tuple containing: + - Node ID (int): The unique identifier of the node in the DAG. + - Qubit indices (list of int): Indices of qubits involved in the node's operation. + - Classical bit indices (set of int): Indices of classical bits involved in the node's operation. + - Directive flag (bool): Indicates whether the operation is a directive (True) or not (False). + + Args: + nodes (list[DAGOpNode]): List of DAGOpNode objects to extract information from. + dag (DAGCircuit): DAGCircuit object containing the circuit structure. + + Returns: + list of tuples: Each tuple contains information about a node in the format described above. + """ + extracted_node_info = [] + for node in nodes: + qubit_indices = [dag.find_bit(qubit).index for qubit in node.qargs] + classical_bit_indices = set() + + if node.op.condition is not None: + classical_bit_indices.update(condition_resources(node.op.condition).clbits) + + if isinstance(node.op, SwitchCaseOp): + switch_case_target = node.op.target + if isinstance(switch_case_target, Clbit): + classical_bit_indices.add(switch_case_target) + elif isinstance(switch_case_target, ClassicalRegister): + classical_bit_indices.update(switch_case_target) + else: # Assume target is an expression involving classical bits + classical_bit_indices.update(node_resources(switch_case_target).clbits) + + is_directive = getattr(node.op, "_directive", False) + extracted_node_info.append( + (node._node_id, qubit_indices, classical_bit_indices, is_directive) + ) + + return extracted_node_info diff --git a/qiskit/transpiler/passes/scheduling/alap.py b/qiskit/transpiler/passes/scheduling/alap.py index ee2c9160c197..10ae623c4659 100644 --- a/qiskit/transpiler/passes/scheduling/alap.py +++ b/qiskit/transpiler/passes/scheduling/alap.py @@ -15,10 +15,9 @@ from qiskit.circuit import Delay, Qubit, Measure from qiskit.dagcircuit import DAGCircuit from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.passes.scheduling.base_scheduler import BaseSchedulerTransform from qiskit.utils.deprecation import deprecate_func -from .base_scheduler import BaseSchedulerTransform - class ALAPSchedule(BaseSchedulerTransform): """ALAP Scheduling pass, which schedules the **stop** time of instructions as late as possible. diff --git a/qiskit/transpiler/passes/scheduling/alignments/check_durations.py b/qiskit/transpiler/passes/scheduling/alignments/check_durations.py index 5c4e2b331eb8..46fdb1f5160b 100644 --- a/qiskit/transpiler/passes/scheduling/alignments/check_durations.py +++ b/qiskit/transpiler/passes/scheduling/alignments/check_durations.py @@ -14,7 +14,7 @@ from qiskit.circuit.delay import Delay from qiskit.dagcircuit import DAGCircuit from qiskit.transpiler.basepasses import AnalysisPass -from qiskit.transpiler import Target +from qiskit.transpiler.target import Target class InstructionDurationCheck(AnalysisPass): diff --git a/qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py b/qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py index a4c23a645e17..955f712214a3 100644 --- a/qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +++ b/qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py @@ -16,7 +16,7 @@ from qiskit.pulse import Play from qiskit.transpiler.basepasses import AnalysisPass from qiskit.transpiler.exceptions import TranspilerError -from qiskit.transpiler import Target +from qiskit.transpiler.target import Target class ValidatePulseGates(AnalysisPass): diff --git a/qiskit/transpiler/passes/scheduling/alignments/reschedule.py b/qiskit/transpiler/passes/scheduling/alignments/reschedule.py index 5cab7028745b..416a92e10390 100644 --- a/qiskit/transpiler/passes/scheduling/alignments/reschedule.py +++ b/qiskit/transpiler/passes/scheduling/alignments/reschedule.py @@ -21,7 +21,7 @@ from qiskit.dagcircuit import DAGCircuit, DAGOpNode, DAGOutNode from qiskit.transpiler.basepasses import AnalysisPass from qiskit.transpiler.exceptions import TranspilerError -from qiskit.transpiler import Target +from qiskit.transpiler.target import Target class ConstrainedReschedule(AnalysisPass): diff --git a/qiskit/transpiler/passes/scheduling/asap.py b/qiskit/transpiler/passes/scheduling/asap.py index 0ea56bb67468..b1e559718143 100644 --- a/qiskit/transpiler/passes/scheduling/asap.py +++ b/qiskit/transpiler/passes/scheduling/asap.py @@ -15,10 +15,9 @@ from qiskit.circuit import Delay, Qubit, Measure from qiskit.dagcircuit import DAGCircuit from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.passes.scheduling.base_scheduler import BaseSchedulerTransform from qiskit.utils.deprecation import deprecate_func -from .base_scheduler import BaseSchedulerTransform - class ASAPSchedule(BaseSchedulerTransform): """ASAP Scheduling pass, which schedules the start time of instructions as early as possible.. diff --git a/qiskit/transpiler/passes/scheduling/base_scheduler.py b/qiskit/transpiler/passes/scheduling/base_scheduler.py index e9076c5c637b..fe6d0e16cb76 100644 --- a/qiskit/transpiler/passes/scheduling/base_scheduler.py +++ b/qiskit/transpiler/passes/scheduling/base_scheduler.py @@ -13,13 +13,13 @@ """Base circuit scheduling pass.""" import warnings -from qiskit.transpiler import InstructionDurations -from qiskit.transpiler.basepasses import TransformationPass -from qiskit.transpiler.passes.scheduling.time_unit_conversion import TimeUnitConversion -from qiskit.dagcircuit import DAGOpNode, DAGCircuit, DAGOutNode from qiskit.circuit import Delay, Gate, Measure, Reset from qiskit.circuit.parameterexpression import ParameterExpression +from qiskit.dagcircuit import DAGOpNode, DAGCircuit, DAGOutNode +from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.instruction_durations import InstructionDurations +from qiskit.transpiler.passes.scheduling.time_unit_conversion import TimeUnitConversion from qiskit.transpiler.target import Target diff --git a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py index 7be0e838ebff..a4695c24439c 100644 --- a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +++ b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py @@ -20,10 +20,10 @@ from qiskit.dagcircuit import DAGOpNode, DAGInNode from qiskit.quantum_info.operators.predicates import matrix_equal from qiskit.synthesis.one_qubit import OneQubitEulerDecomposer -from qiskit.transpiler import InstructionDurations -from qiskit.transpiler.passes.optimization import Optimize1qGates from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.instruction_durations import InstructionDurations +from qiskit.transpiler.passes.optimization import Optimize1qGates from qiskit.utils.deprecation import deprecate_func diff --git a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py index 45333de009bd..8c3ea87c8578 100644 --- a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +++ b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py @@ -26,9 +26,9 @@ from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.instruction_durations import InstructionDurations from qiskit.transpiler.passes.optimization import Optimize1qGates +from qiskit.transpiler.passes.scheduling.padding.base_padding import BasePadding from qiskit.transpiler.target import Target -from .base_padding import BasePadding logger = logging.getLogger(__name__) diff --git a/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py b/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py index 69bea32acca7..2c7c97ec856a 100644 --- a/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py +++ b/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py @@ -14,13 +14,13 @@ import warnings -from qiskit.transpiler import InstructionDurations -from qiskit.transpiler.basepasses import AnalysisPass -from qiskit.transpiler.passes.scheduling.time_unit_conversion import TimeUnitConversion -from qiskit.dagcircuit import DAGOpNode, DAGCircuit from qiskit.circuit import Delay, Gate from qiskit.circuit.parameterexpression import ParameterExpression +from qiskit.dagcircuit import DAGOpNode, DAGCircuit +from qiskit.transpiler.basepasses import AnalysisPass from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.instruction_durations import InstructionDurations +from qiskit.transpiler.passes.scheduling.time_unit_conversion import TimeUnitConversion from qiskit.transpiler.target import Target diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py index 08b6a15fd03d..1ce2f2800c09 100644 --- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py @@ -28,27 +28,16 @@ from functools import partial import numpy as np -from qiskit.converters import circuit_to_dag, dag_to_circuit -from qiskit.transpiler import CouplingMap, Target -from qiskit.transpiler.basepasses import TransformationPass -from qiskit.transpiler.exceptions import TranspilerError -from qiskit.dagcircuit.dagcircuit import DAGCircuit, DAGOpNode -from qiskit.synthesis.one_qubit import one_qubit_decompose -from qiskit.transpiler.passes.optimization.optimize_1q_decomposition import _possible_decomposers -from qiskit.synthesis.two_qubit.xx_decompose import XXDecomposer, XXEmbodiments -from qiskit.synthesis.two_qubit.two_qubit_decompose import ( - TwoQubitBasisDecomposer, - TwoQubitWeylDecomposition, -) -from qiskit.quantum_info import Operator from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES from qiskit.circuit import Gate, Parameter, CircuitInstruction +from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping from qiskit.circuit.library.standard_gates import ( iSwapGate, CXGate, CZGate, RXXGate, RZXGate, + RZZGate, ECRGate, RXGate, SXGate, @@ -62,13 +51,26 @@ RYGate, RGate, ) -from qiskit.transpiler.passes.synthesis import plugin +from qiskit.converters import circuit_to_dag, dag_to_circuit +from qiskit.dagcircuit.dagcircuit import DAGCircuit, DAGOpNode +from qiskit.exceptions import QiskitError +from qiskit.providers.models import BackendProperties +from qiskit.quantum_info import Operator +from qiskit.synthesis.one_qubit import one_qubit_decompose +from qiskit.synthesis.two_qubit.xx_decompose import XXDecomposer, XXEmbodiments +from qiskit.synthesis.two_qubit.two_qubit_decompose import ( + TwoQubitBasisDecomposer, + TwoQubitWeylDecomposition, +) +from qiskit.transpiler.basepasses import TransformationPass +from qiskit.transpiler.coupling import CouplingMap +from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.passes.optimization.optimize_1q_decomposition import ( Optimize1qGatesDecomposition, + _possible_decomposers, ) -from qiskit.providers.models import BackendProperties -from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping -from qiskit.exceptions import QiskitError +from qiskit.transpiler.passes.synthesis import plugin +from qiskit.transpiler.target import Target GATE_NAME_MAP = { @@ -561,11 +563,11 @@ def _run_main_loop( qubits = node.qargs user_gate_node = DAGOpNode(gate) for ( - op_name, + gate, params, qargs, ) in node_list: - if op_name == "USER_GATE": + if gate is None: node = DAGOpNode.from_instruction( user_gate_node._to_circuit_instruction().replace( params=user_gate_node.params, @@ -576,7 +578,7 @@ def _run_main_loop( else: node = DAGOpNode.from_instruction( CircuitInstruction.from_standard( - GATE_NAME_MAP[op_name], tuple(qubits[x] for x in qargs), params + gate, tuple(qubits[x] for x in qargs), params ), dag=out_dag, ) @@ -780,6 +782,8 @@ def _replace_parameterized_gate(op): op = RXXGate(pi / 2) elif isinstance(op, RZXGate) and isinstance(op.params[0], Parameter): op = RZXGate(pi / 4) + elif isinstance(op, RZZGate) and isinstance(op.params[0], Parameter): + op = RZZGate(pi / 2) return op try: @@ -1008,8 +1012,8 @@ def _synth_su4_no_dag(self, unitary, decomposer2q, preferred_direction, approxim # if the gates in synthesis are in the opposite direction of the preferred direction # resynthesize a new operator which is the original conjugated by swaps. # this new operator is doubly mirrored from the original and is locally equivalent. - for op_name, _params, qubits in synth_circ: - if op_name in {"USER_GATE", "cx"}: + for gate, _params, qubits in synth_circ: + if gate is None or gate == CXGate._standard_gate: synth_direction = qubits if synth_direction is not None and synth_direction != preferred_direction: # TODO: Avoid using a dag to correct the synthesis direction 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 bf00aea87d40..46d91f7d6ce0 100644 --- a/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py +++ b/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py @@ -22,7 +22,7 @@ QuantumCircuit, ) from qiskit.dagcircuit import DAGCircuit -from qiskit.transpiler import TransformationPass +from qiskit.transpiler.basepasses import TransformationPass class ConvertConditionsToIfOps(TransformationPass): diff --git a/qiskit/transpiler/preset_passmanagers/__init__.py b/qiskit/transpiler/preset_passmanagers/__init__.py index f2f011e486c4..6092573876f1 100644 --- a/qiskit/transpiler/preset_passmanagers/__init__.py +++ b/qiskit/transpiler/preset_passmanagers/__init__.py @@ -57,466 +57,12 @@ .. autofunction:: generate_scheduling .. currentmodule:: qiskit.transpiler.preset_passmanagers """ -import copy - -from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES -from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping -from qiskit.providers.backend_compat import BackendV2Converter - -from qiskit.transpiler.instruction_durations import InstructionDurations -from qiskit.transpiler.timing_constraints import TimingConstraints - -from qiskit.transpiler.passmanager_config import PassManagerConfig -from qiskit.transpiler.target import Target, target_to_backend_properties -from qiskit.transpiler import CouplingMap - -from qiskit.transpiler.exceptions import TranspilerError from .level0 import level_0_pass_manager from .level1 import level_1_pass_manager from .level2 import level_2_pass_manager from .level3 import level_3_pass_manager - - -def generate_preset_pass_manager( - optimization_level, - backend=None, - target=None, - basis_gates=None, - inst_map=None, - coupling_map=None, - instruction_durations=None, - backend_properties=None, - timing_constraints=None, - initial_layout=None, - layout_method=None, - routing_method=None, - translation_method=None, - scheduling_method=None, - approximation_degree=1.0, - seed_transpiler=None, - unitary_synthesis_method="default", - unitary_synthesis_plugin_config=None, - hls_config=None, - init_method=None, - optimization_method=None, - dt=None, - *, - _skip_target=False, -): - """Generate a preset :class:`~.PassManager` - - This function is used to quickly generate a preset pass manager. Preset pass - managers are the default pass managers used by the :func:`~.transpile` - function. This function provides a convenient and simple method to construct - a standalone :class:`~.PassManager` object that mirrors what the :func:`~.transpile` - function internally builds and uses. - - The target constraints for the pass manager construction can be specified through a :class:`.Target` - instance, a :class:`.BackendV1` or :class:`.BackendV2` instance, or via loose constraints - (``basis_gates``, ``inst_map``, ``coupling_map``, ``backend_properties``, ``instruction_durations``, - ``dt`` or ``timing_constraints``). - The order of priorities for target constraints works as follows: if a ``target`` - input is provided, it will take priority over any ``backend`` input or loose constraints. - If a ``backend`` is provided together with any loose constraint - from the list above, the loose constraint will take priority over the corresponding backend - constraint. This behavior is independent of whether the ``backend`` instance is of type - :class:`.BackendV1` or :class:`.BackendV2`, as summarized in the table below. The first column - in the table summarizes the potential user-provided constraints, and each cell shows whether - the priority is assigned to that specific constraint input or another input - (`target`/`backend(V1)`/`backend(V2)`). - - ============================ ========= ======================== ======================= - User Provided target backend(V1) backend(V2) - ============================ ========= ======================== ======================= - **basis_gates** target basis_gates basis_gates - **coupling_map** target coupling_map coupling_map - **instruction_durations** target instruction_durations instruction_durations - **inst_map** target inst_map inst_map - **dt** target dt dt - **timing_constraints** target timing_constraints timing_constraints - **backend_properties** target backend_properties backend_properties - ============================ ========= ======================== ======================= - - Args: - optimization_level (int): The optimization level to generate a - :class:`~.PassManager` for. This can be 0, 1, 2, or 3. Higher - levels generate more optimized circuits, at the expense of - longer transpilation time: - - * 0: no optimization - * 1: light optimization - * 2: heavy optimization - * 3: even heavier optimization - - backend (Backend): An optional backend object which can be used as the - source of the default values for the ``basis_gates``, ``inst_map``, - ``coupling_map``, ``backend_properties``, ``instruction_durations``, - ``timing_constraints``, and ``target``. If any of those other arguments - are specified in addition to ``backend`` they will take precedence - over the value contained in the backend. - target (Target): The :class:`~.Target` representing a backend compilation - target. The following attributes will be inferred from this - argument if they are not set: ``coupling_map``, ``basis_gates``, - ``instruction_durations``, ``inst_map``, ``timing_constraints`` - and ``backend_properties``. - basis_gates (list): List of basis gate names to unroll to - (e.g: ``['u1', 'u2', 'u3', 'cx']``). - inst_map (InstructionScheduleMap): Mapping object that maps gates to schedules. - If any user defined calibration is found in the map and this is used in a - circuit, transpiler attaches the custom gate definition to the circuit. - This enables one to flexibly override the low-level instruction - implementation. - coupling_map (CouplingMap or list): Directed graph represented a coupling - map. Multiple formats are supported: - - #. ``CouplingMap`` instance - #. List, must be given as an adjacency matrix, where each entry - specifies all directed two-qubit interactions supported by backend, - e.g: ``[[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]]`` - - instruction_durations (InstructionDurations or list): Dictionary of duration - (in dt) for each instruction. If specified, these durations overwrite the - gate lengths in ``backend.properties``. Applicable only if ``scheduling_method`` - is specified. - The format of ``instruction_durations`` must be as follows: - They must be given as an :class:`.InstructionDurations` instance or a list of tuples - - ``` - [(instruction_name, qubits, duration, unit), ...]. - | [('cx', [0, 1], 12.3, 'ns'), ('u3', [0], 4.56, 'ns')] - | [('cx', [0, 1], 1000), ('u3', [0], 300)] - ``` - - If ``unit`` is omitted, the default is ``'dt'``, which is a sample time depending on backend. - If the time unit is ``'dt'``, the duration must be an integer. - dt (float): Backend sample time (resolution) in seconds. - If provided, this value will overwrite the ``dt`` value in ``instruction_durations``. - If ``None`` (default) and a backend is provided, ``backend.dt`` is used. - timing_constraints (TimingConstraints): Hardware time alignment restrictions. - A quantum computer backend may report a set of restrictions, namely: - - - granularity: An integer value representing minimum pulse gate - resolution in units of ``dt``. A user-defined pulse gate should have - duration of a multiple of this granularity value. - - min_length: An integer value representing minimum pulse gate - length in units of ``dt``. A user-defined pulse gate should be longer - than this length. - - pulse_alignment: An integer value representing a time resolution of gate - instruction starting time. Gate instruction should start at time which - is a multiple of the alignment value. - - acquire_alignment: An integer value representing a time resolution of measure - instruction starting time. Measure instruction should start at time which - is a multiple of the alignment value. - - This information will be provided by the backend configuration. - If the backend doesn't have any restriction on the instruction time allocation, - then ``timing_constraints`` is None and no adjustment will be performed. - - initial_layout (Layout | List[int]): Initial position of virtual qubits on - physical qubits. - layout_method (str): The :class:`~.Pass` to use for choosing initial qubit - placement. Valid choices are ``'trivial'``, ``'dense'``, - and ``'sabre'``, representing :class:`~.TrivialLayout`, :class:`~.DenseLayout` and - :class:`~.SabreLayout` respectively. This can also - be the external plugin name to use for the ``layout`` stage of the output - :class:`~.StagedPassManager`. You can see a list of installed plugins by using - :func:`~.list_stage_plugins` with ``"layout"`` for the ``stage_name`` argument. - routing_method (str): The pass to use for routing qubits on the - architecture. Valid choices are ``'basic'``, ``'lookahead'``, ``'stochastic'``, - ``'sabre'``, and ``'none'`` representing :class:`~.BasicSwap`, - :class:`~.LookaheadSwap`, :class:`~.StochasticSwap`, :class:`~.SabreSwap`, and - erroring if routing is required respectively. This can also be the external plugin - name to use for the ``routing`` stage of the output :class:`~.StagedPassManager`. - You can see a list of installed plugins by using :func:`~.list_stage_plugins` with - ``"routing"`` for the ``stage_name`` argument. - translation_method (str): The method to use for translating gates to - basis gates. Valid choices ``'translator'``, ``'synthesis'`` representing - :class:`~.BasisTranslator`, and :class:`~.UnitarySynthesis` respectively. This can - also be the external plugin name to use for the ``translation`` stage of the output - :class:`~.StagedPassManager`. You can see a list of installed plugins by using - :func:`~.list_stage_plugins` with ``"translation"`` for the ``stage_name`` argument. - scheduling_method (str): The pass to use for scheduling instructions. Valid choices - are ``'alap'`` and ``'asap'``. This can also be the external plugin name to use - for the ``scheduling`` stage of the output :class:`~.StagedPassManager`. You can - see a list of installed plugins by using :func:`~.list_stage_plugins` with - ``"scheduling"`` for the ``stage_name`` argument. - backend_properties (BackendProperties): Properties returned by a - backend, including information on gate errors, readout errors, - qubit coherence times, etc. - approximation_degree (float): Heuristic dial used for circuit approximation - (1.0=no approximation, 0.0=maximal approximation). - seed_transpiler (int): Sets random seed for the stochastic parts of - the transpiler. - unitary_synthesis_method (str): The name of the unitary synthesis - method to use. By default ``'default'`` is used. You can see a list of - installed plugins with :func:`.unitary_synthesis_plugin_names`. - unitary_synthesis_plugin_config (dict): An optional configuration dictionary - that will be passed directly to the unitary synthesis plugin. By - default this setting will have no effect as the default unitary - synthesis method does not take custom configuration. This should - only be necessary when a unitary synthesis plugin is specified with - the ``unitary_synthesis_method`` argument. As this is custom for each - unitary synthesis plugin refer to the plugin documentation for how - to use this option. - hls_config (HLSConfig): An optional configuration class :class:`~.HLSConfig` - that will be passed directly to :class:`~.HighLevelSynthesis` transformation pass. - This configuration class allows to specify for various high-level objects - the lists of synthesis algorithms and their parameters. - init_method (str): The plugin name to use for the ``init`` stage of - the output :class:`~.StagedPassManager`. By default an external - plugin is not used. You can see a list of installed plugins by - using :func:`~.list_stage_plugins` with ``"init"`` for the stage - name argument. - optimization_method (str): The plugin name to use for the - ``optimization`` stage of the output - :class:`~.StagedPassManager`. By default an external - plugin is not used. You can see a list of installed plugins by - using :func:`~.list_stage_plugins` with ``"optimization"`` for the - ``stage_name`` argument. - - Returns: - StagedPassManager: The preset pass manager for the given options - - Raises: - ValueError: if an invalid value for ``optimization_level`` is passed in. - """ - - if backend is not None and getattr(backend, "version", 0) <= 1: - # This is a temporary conversion step to allow for a smoother transition - # to a fully target-based transpiler pipeline while maintaining the behavior - # of `transpile` with BackendV1 inputs. - backend = BackendV2Converter(backend) - - # Check if a custom inst_map was specified before overwriting inst_map - _given_inst_map = bool(inst_map) - # If there are no loose constraints => use backend target if available - _no_loose_constraints = ( - basis_gates is None - and coupling_map is None - and dt is None - and instruction_durations is None - and backend_properties is None - and timing_constraints is None - ) - # If it's an edge case => do not build target - _skip_target = ( - target is None - and backend is None - and (basis_gates is None or coupling_map is None or instruction_durations is not None) - ) - - # Resolve loose constraints case-by-case against backend constraints. - # The order of priority is loose constraints > backend. - dt = _parse_dt(dt, backend) - instruction_durations = _parse_instruction_durations(backend, instruction_durations, dt) - timing_constraints = _parse_timing_constraints(backend, timing_constraints) - inst_map = _parse_inst_map(inst_map, backend) - # The basis gates parser will set _skip_target to True if a custom basis gate is found - # (known edge case). - basis_gates, name_mapping, _skip_target = _parse_basis_gates( - basis_gates, backend, inst_map, _skip_target - ) - coupling_map = _parse_coupling_map(coupling_map, backend) - - if target is None: - if backend is not None and _no_loose_constraints: - # If a backend is specified without loose constraints, use its target directly. - target = backend.target - elif not _skip_target: - # Only parse backend properties when the target isn't skipped to - # preserve the former behavior of transpile. - backend_properties = _parse_backend_properties(backend_properties, backend) - # Build target from constraints. - target = Target.from_configuration( - basis_gates=basis_gates, - num_qubits=backend.num_qubits if backend is not None else None, - coupling_map=coupling_map, - # If the instruction map has custom gates, do not give as config, the information - # will be added to the target with update_from_instruction_schedule_map - inst_map=inst_map if inst_map and not inst_map.has_custom_gate() else None, - backend_properties=backend_properties, - instruction_durations=instruction_durations, - concurrent_measurements=( - backend.target.concurrent_measurements if backend is not None else None - ), - dt=dt, - timing_constraints=timing_constraints, - custom_name_mapping=name_mapping, - ) - - # Update target with custom gate information. Note that this is an exception to the priority - # order (target > loose constraints), added to handle custom gates for scheduling passes. - if target is not None and _given_inst_map and inst_map.has_custom_gate(): - target = copy.deepcopy(target) - target.update_from_instruction_schedule_map(inst_map) - - if target is not None: - if coupling_map is None: - coupling_map = target.build_coupling_map() - if basis_gates is None: - basis_gates = target.operation_names - if instruction_durations is None: - instruction_durations = target.durations() - if inst_map is None: - inst_map = target.instruction_schedule_map() - if timing_constraints is None: - timing_constraints = target.timing_constraints() - if backend_properties is None: - backend_properties = target_to_backend_properties(target) - - pm_options = { - "target": target, - "basis_gates": basis_gates, - "inst_map": inst_map, - "coupling_map": coupling_map, - "instruction_durations": instruction_durations, - "backend_properties": backend_properties, - "timing_constraints": timing_constraints, - "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, - "initial_layout": initial_layout, - "hls_config": hls_config, - "init_method": init_method, - "optimization_method": optimization_method, - } - - if backend is not None: - pm_options["_skip_target"] = _skip_target - pm_config = PassManagerConfig.from_backend(backend, **pm_options) - else: - pm_config = PassManagerConfig(**pm_options) - if optimization_level == 0: - pm = level_0_pass_manager(pm_config) - elif optimization_level == 1: - pm = level_1_pass_manager(pm_config) - elif optimization_level == 2: - pm = level_2_pass_manager(pm_config) - elif optimization_level == 3: - pm = level_3_pass_manager(pm_config) - else: - raise ValueError(f"Invalid optimization level {optimization_level}") - return pm - - -def _parse_basis_gates(basis_gates, backend, inst_map, skip_target): - name_mapping = {} - standard_gates = get_standard_gate_name_mapping() - # Add control flow gates by default to basis set - default_gates = {"measure", "delay", "reset"}.union(CONTROL_FLOW_OP_NAMES) - - try: - instructions = set(basis_gates) - for name in default_gates: - if name not in instructions: - instructions.add(name) - except TypeError: - instructions = None - - if backend is None: - # Check for custom instructions - if instructions is None: - return None, name_mapping, skip_target - - for inst in instructions: - if inst not in standard_gates or inst not in default_gates: - skip_target = True - break - - return list(instructions), name_mapping, skip_target - - instructions = instructions or backend.operation_names - name_mapping.update( - {name: backend.target.operation_from_name(name) for name in backend.operation_names} - ) - - # Check for custom instructions before removing calibrations - for inst in instructions: - if inst not in standard_gates or inst not in default_gates: - skip_target = True - break - - # Remove calibrated instructions, as they will be added later from the instruction schedule map - if inst_map is not None and not skip_target: - for inst in inst_map.instructions: - for qubit in inst_map.qubits_with_instruction(inst): - entry = inst_map._get_calibration_entry(inst, qubit) - if entry.user_provided and inst in instructions: - instructions.remove(inst) - - return list(instructions) if instructions else None, name_mapping, skip_target - - -def _parse_inst_map(inst_map, backend): - # try getting inst_map from user, else backend - if inst_map is None and backend is not None: - inst_map = backend.target.instruction_schedule_map() - return inst_map - - -def _parse_backend_properties(backend_properties, backend): - # try getting backend_props from user, else backend - if backend_properties is None and backend is not None: - backend_properties = target_to_backend_properties(backend.target) - return backend_properties - - -def _parse_dt(dt, backend): - # try getting dt from user, else backend - if dt is None and backend is not None: - dt = backend.target.dt - return dt - - -def _parse_coupling_map(coupling_map, backend): - # try getting coupling_map from user, else backend - if coupling_map is None and backend is not None: - coupling_map = backend.coupling_map - - # coupling_map could be None, or a list of lists, e.g. [[0, 1], [2, 1]] - if coupling_map is None or isinstance(coupling_map, CouplingMap): - return coupling_map - if isinstance(coupling_map, list) and all( - isinstance(i, list) and len(i) == 2 for i in coupling_map - ): - return CouplingMap(coupling_map) - else: - raise TranspilerError( - "Only a single input coupling map can be used with generate_preset_pass_manager()." - ) - - -def _parse_instruction_durations(backend, inst_durations, dt): - """Create a list of ``InstructionDuration``s. If ``inst_durations`` is provided, - the backend will be ignored, otherwise, the durations will be populated from the - backend. - """ - final_durations = InstructionDurations() - if not inst_durations: - backend_durations = InstructionDurations() - if backend is not None: - backend_durations = backend.instruction_durations - final_durations.update(backend_durations, dt or backend_durations.dt) - else: - final_durations.update(inst_durations, dt or getattr(inst_durations, "dt", None)) - return final_durations - - -def _parse_timing_constraints(backend, timing_constraints): - if isinstance(timing_constraints, TimingConstraints): - return timing_constraints - if backend is None and timing_constraints is None: - timing_constraints = TimingConstraints() - elif backend is not None: - timing_constraints = backend.target.timing_constraints() - return timing_constraints - +from .generate_preset_pass_manager import generate_preset_pass_manager __all__ = [ "level_0_pass_manager", diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index f85b4d113c17..5e42c7ba3e3f 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -12,6 +12,8 @@ """Built-in transpiler stage plugins for preset pass managers.""" +import os + from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.passes import BasicSwap @@ -63,6 +65,10 @@ SXGate, SXdgGate, ) +from qiskit.utils.parallel import CPU_COUNT +from qiskit import user_config + +CONFIG = user_config.get_config() class DefaultInitPassManager(PassManagerStagePlugin): @@ -397,11 +403,12 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana pass_manager_config.initial_layout, ) if optimization_level == 0: + trial_count = _get_trial_count(5) routing_pass = SabreSwap( coupling_map_routing, heuristic="basic", seed=seed_transpiler, - trials=5, + trials=trial_count, ) return common.generate_routing_passmanager( routing_pass, @@ -411,11 +418,12 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana use_barrier_before_measurement=True, ) if optimization_level == 1: + trial_count = _get_trial_count(5) routing_pass = SabreSwap( coupling_map_routing, heuristic="decay", seed=seed_transpiler, - trials=5, + trials=trial_count, ) return common.generate_routing_passmanager( routing_pass, @@ -429,11 +437,13 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana use_barrier_before_measurement=True, ) if optimization_level == 2: + trial_count = _get_trial_count(20) + routing_pass = SabreSwap( coupling_map_routing, heuristic="decay", seed=seed_transpiler, - trials=10, + trials=trial_count, ) return common.generate_routing_passmanager( routing_pass, @@ -446,11 +456,12 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana use_barrier_before_measurement=True, ) if optimization_level == 3: + trial_count = _get_trial_count(20) routing_pass = SabreSwap( coupling_map_routing, heuristic="decay", seed=seed_transpiler, - trials=20, + trials=trial_count, ) return common.generate_routing_passmanager( routing_pass, @@ -737,12 +748,15 @@ def _swap_mapped(property_set): max_trials=2500, # Limits layout scoring to < 600ms on ~400 qubit devices ) layout.append(ConditionalController(choose_layout_1, condition=_layout_not_perfect)) + + trial_count = _get_trial_count(5) + choose_layout_2 = SabreLayout( coupling_map, max_iterations=2, seed=pass_manager_config.seed_transpiler, - swap_trials=5, - layout_trials=5, + swap_trials=trial_count, + layout_trials=trial_count, skip_routing=pass_manager_config.routing_method is not None and pass_manager_config.routing_method != "sabre", ) @@ -769,12 +783,15 @@ def _swap_mapped(property_set): layout.append( ConditionalController(choose_layout_0, condition=_choose_layout_condition) ) + + trial_count = _get_trial_count(20) + choose_layout_1 = SabreLayout( coupling_map, max_iterations=2, seed=pass_manager_config.seed_transpiler, - swap_trials=20, - layout_trials=20, + swap_trials=trial_count, + layout_trials=trial_count, skip_routing=pass_manager_config.routing_method is not None and pass_manager_config.routing_method != "sabre", ) @@ -801,12 +818,15 @@ def _swap_mapped(property_set): layout.append( ConditionalController(choose_layout_0, condition=_choose_layout_condition) ) + + trial_count = _get_trial_count(20) + choose_layout_1 = SabreLayout( coupling_map, max_iterations=4, seed=pass_manager_config.seed_transpiler, - swap_trials=20, - layout_trials=20, + swap_trials=trial_count, + layout_trials=trial_count, skip_routing=pass_manager_config.routing_method is not None and pass_manager_config.routing_method != "sabre", ) @@ -902,42 +922,50 @@ def _swap_mapped(property_set): layout = PassManager() layout.append(_given_layout) if optimization_level == 0: + trial_count = _get_trial_count(5) + layout_pass = SabreLayout( coupling_map, max_iterations=1, seed=pass_manager_config.seed_transpiler, - swap_trials=5, - layout_trials=5, + swap_trials=trial_count, + layout_trials=trial_count, skip_routing=pass_manager_config.routing_method is not None and pass_manager_config.routing_method != "sabre", ) elif optimization_level == 1: + trial_count = _get_trial_count(5) + layout_pass = SabreLayout( coupling_map, max_iterations=2, seed=pass_manager_config.seed_transpiler, - swap_trials=5, - layout_trials=5, + swap_trials=trial_count, + layout_trials=trial_count, skip_routing=pass_manager_config.routing_method is not None and pass_manager_config.routing_method != "sabre", ) elif optimization_level == 2: + trial_count = _get_trial_count(20) + layout_pass = SabreLayout( coupling_map, max_iterations=2, seed=pass_manager_config.seed_transpiler, - swap_trials=20, - layout_trials=20, + swap_trials=trial_count, + layout_trials=trial_count, skip_routing=pass_manager_config.routing_method is not None and pass_manager_config.routing_method != "sabre", ) elif optimization_level == 3: + trial_count = _get_trial_count(20) + layout_pass = SabreLayout( coupling_map, max_iterations=4, seed=pass_manager_config.seed_transpiler, - swap_trials=20, - layout_trials=20, + swap_trials=trial_count, + layout_trials=trial_count, skip_routing=pass_manager_config.routing_method is not None and pass_manager_config.routing_method != "sabre", ) @@ -957,3 +985,9 @@ def _swap_mapped(property_set): embed = common.generate_embed_passmanager(coupling_map) layout.append(ConditionalController(embed.to_flow_controller(), condition=_swap_mapped)) return layout + + +def _get_trial_count(default_trials=5): + if CONFIG.get("sabre_all_threads", None) or os.getenv("QISKIT_SABRE_ALL_THREADS"): + return max(CPU_COUNT, default_trials) + return default_trials diff --git a/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py b/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py new file mode 100644 index 000000000000..bdbac42c8055 --- /dev/null +++ b/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py @@ -0,0 +1,511 @@ +# 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. + +""" +Preset pass manager generation function +""" + +import copy + +from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES +from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping +from qiskit.circuit.quantumregister import Qubit +from qiskit.providers.backend import Backend +from qiskit.providers.backend_compat import BackendV2Converter +from qiskit.transpiler.coupling import CouplingMap +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.instruction_durations import InstructionDurations +from qiskit.transpiler.layout import Layout +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 .level0 import level_0_pass_manager +from .level1 import level_1_pass_manager +from .level2 import level_2_pass_manager +from .level3 import level_3_pass_manager + + +def generate_preset_pass_manager( + optimization_level=2, + backend=None, + target=None, + basis_gates=None, + inst_map=None, + coupling_map=None, + instruction_durations=None, + backend_properties=None, + timing_constraints=None, + initial_layout=None, + layout_method=None, + routing_method=None, + translation_method=None, + scheduling_method=None, + approximation_degree=1.0, + seed_transpiler=None, + unitary_synthesis_method="default", + unitary_synthesis_plugin_config=None, + hls_config=None, + init_method=None, + optimization_method=None, + dt=None, + *, + _skip_target=False, +): + """Generate a preset :class:`~.PassManager` + + This function is used to quickly generate a preset pass manager. Preset pass + managers are the default pass managers used by the :func:`~.transpile` + function. This function provides a convenient and simple method to construct + a standalone :class:`~.PassManager` object that mirrors what the :func:`~.transpile` + function internally builds and uses. + + The target constraints for the pass manager construction can be specified through a :class:`.Target` + instance, a :class:`.BackendV1` or :class:`.BackendV2` instance, or via loose constraints + (``basis_gates``, ``inst_map``, ``coupling_map``, ``backend_properties``, ``instruction_durations``, + ``dt`` or ``timing_constraints``). + The order of priorities for target constraints works as follows: if a ``target`` + input is provided, it will take priority over any ``backend`` input or loose constraints. + If a ``backend`` is provided together with any loose constraint + from the list above, the loose constraint will take priority over the corresponding backend + constraint. This behavior is independent of whether the ``backend`` instance is of type + :class:`.BackendV1` or :class:`.BackendV2`, as summarized in the table below. The first column + in the table summarizes the potential user-provided constraints, and each cell shows whether + the priority is assigned to that specific constraint input or another input + (`target`/`backend(V1)`/`backend(V2)`). + + ============================ ========= ======================== ======================= + User Provided target backend(V1) backend(V2) + ============================ ========= ======================== ======================= + **basis_gates** target basis_gates basis_gates + **coupling_map** target coupling_map coupling_map + **instruction_durations** target instruction_durations instruction_durations + **inst_map** target inst_map inst_map + **dt** target dt dt + **timing_constraints** target timing_constraints timing_constraints + **backend_properties** target backend_properties backend_properties + ============================ ========= ======================== ======================= + + Args: + optimization_level (int): The optimization level to generate a + :class:`~.StagedPassManager` for. By default optimization level 2 + is used if this is not specified. This can be 0, 1, 2, or 3. Higher + levels generate potentially more optimized circuits, at the expense + of longer transpilation time: + + * 0: no optimization + * 1: light optimization + * 2: heavy optimization + * 3: even heavier optimization + + backend (Backend): An optional backend object which can be used as the + source of the default values for the ``basis_gates``, ``inst_map``, + ``coupling_map``, ``backend_properties``, ``instruction_durations``, + ``timing_constraints``, and ``target``. If any of those other arguments + are specified in addition to ``backend`` they will take precedence + over the value contained in the backend. + target (Target): The :class:`~.Target` representing a backend compilation + target. The following attributes will be inferred from this + argument if they are not set: ``coupling_map``, ``basis_gates``, + ``instruction_durations``, ``inst_map``, ``timing_constraints`` + and ``backend_properties``. + basis_gates (list): List of basis gate names to unroll to + (e.g: ``['u1', 'u2', 'u3', 'cx']``). + inst_map (InstructionScheduleMap): Mapping object that maps gates to schedules. + If any user defined calibration is found in the map and this is used in a + circuit, transpiler attaches the custom gate definition to the circuit. + This enables one to flexibly override the low-level instruction + implementation. + coupling_map (CouplingMap or list): Directed graph represented a coupling + map. Multiple formats are supported: + + #. ``CouplingMap`` instance + #. List, must be given as an adjacency matrix, where each entry + specifies all directed two-qubit interactions supported by backend, + e.g: ``[[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]]`` + + instruction_durations (InstructionDurations or list): Dictionary of duration + (in dt) for each instruction. If specified, these durations overwrite the + gate lengths in ``backend.properties``. Applicable only if ``scheduling_method`` + is specified. + The format of ``instruction_durations`` must be as follows: + They must be given as an :class:`.InstructionDurations` instance or a list of tuples + + ``` + [(instruction_name, qubits, duration, unit), ...]. + | [('cx', [0, 1], 12.3, 'ns'), ('u3', [0], 4.56, 'ns')] + | [('cx', [0, 1], 1000), ('u3', [0], 300)] + ``` + + If ``unit`` is omitted, the default is ``'dt'``, which is a sample time depending on backend. + If the time unit is ``'dt'``, the duration must be an integer. + dt (float): Backend sample time (resolution) in seconds. + If provided, this value will overwrite the ``dt`` value in ``instruction_durations``. + If ``None`` (default) and a backend is provided, ``backend.dt`` is used. + timing_constraints (TimingConstraints): Hardware time alignment restrictions. + A quantum computer backend may report a set of restrictions, namely: + + - granularity: An integer value representing minimum pulse gate + resolution in units of ``dt``. A user-defined pulse gate should have + duration of a multiple of this granularity value. + - min_length: An integer value representing minimum pulse gate + length in units of ``dt``. A user-defined pulse gate should be longer + than this length. + - pulse_alignment: An integer value representing a time resolution of gate + instruction starting time. Gate instruction should start at time which + is a multiple of the alignment value. + - acquire_alignment: An integer value representing a time resolution of measure + instruction starting time. Measure instruction should start at time which + is a multiple of the alignment value. + + This information will be provided by the backend configuration. + If the backend doesn't have any restriction on the instruction time allocation, + then ``timing_constraints`` is None and no adjustment will be performed. + + initial_layout (Layout | List[int]): Initial position of virtual qubits on + physical qubits. + layout_method (str): The :class:`~.Pass` to use for choosing initial qubit + placement. Valid choices are ``'trivial'``, ``'dense'``, + and ``'sabre'``, representing :class:`~.TrivialLayout`, :class:`~.DenseLayout` and + :class:`~.SabreLayout` respectively. This can also + be the external plugin name to use for the ``layout`` stage of the output + :class:`~.StagedPassManager`. You can see a list of installed plugins by using + :func:`~.list_stage_plugins` with ``"layout"`` for the ``stage_name`` argument. + routing_method (str): The pass to use for routing qubits on the + architecture. Valid choices are ``'basic'``, ``'lookahead'``, ``'stochastic'``, + ``'sabre'``, and ``'none'`` representing :class:`~.BasicSwap`, + :class:`~.LookaheadSwap`, :class:`~.StochasticSwap`, :class:`~.SabreSwap`, and + erroring if routing is required respectively. This can also be the external plugin + name to use for the ``routing`` stage of the output :class:`~.StagedPassManager`. + You can see a list of installed plugins by using :func:`~.list_stage_plugins` with + ``"routing"`` for the ``stage_name`` argument. + translation_method (str): The method to use for translating gates to + basis gates. Valid choices ``'translator'``, ``'synthesis'`` representing + :class:`~.BasisTranslator`, and :class:`~.UnitarySynthesis` respectively. This can + also be the external plugin name to use for the ``translation`` stage of the output + :class:`~.StagedPassManager`. You can see a list of installed plugins by using + :func:`~.list_stage_plugins` with ``"translation"`` for the ``stage_name`` argument. + scheduling_method (str): The pass to use for scheduling instructions. Valid choices + are ``'alap'`` and ``'asap'``. This can also be the external plugin name to use + for the ``scheduling`` stage of the output :class:`~.StagedPassManager`. You can + see a list of installed plugins by using :func:`~.list_stage_plugins` with + ``"scheduling"`` for the ``stage_name`` argument. + backend_properties (BackendProperties): Properties returned by a + backend, including information on gate errors, readout errors, + qubit coherence times, etc. + approximation_degree (float): Heuristic dial used for circuit approximation + (1.0=no approximation, 0.0=maximal approximation). + seed_transpiler (int): Sets random seed for the stochastic parts of + the transpiler. + unitary_synthesis_method (str): The name of the unitary synthesis + method to use. By default ``'default'`` is used. You can see a list of + installed plugins with :func:`.unitary_synthesis_plugin_names`. + unitary_synthesis_plugin_config (dict): An optional configuration dictionary + that will be passed directly to the unitary synthesis plugin. By + default this setting will have no effect as the default unitary + synthesis method does not take custom configuration. This should + only be necessary when a unitary synthesis plugin is specified with + the ``unitary_synthesis_method`` argument. As this is custom for each + unitary synthesis plugin refer to the plugin documentation for how + to use this option. + hls_config (HLSConfig): An optional configuration class :class:`~.HLSConfig` + that will be passed directly to :class:`~.HighLevelSynthesis` transformation pass. + This configuration class allows to specify for various high-level objects + the lists of synthesis algorithms and their parameters. + init_method (str): The plugin name to use for the ``init`` stage of + the output :class:`~.StagedPassManager`. By default an external + plugin is not used. You can see a list of installed plugins by + using :func:`~.list_stage_plugins` with ``"init"`` for the stage + name argument. + optimization_method (str): The plugin name to use for the + ``optimization`` stage of the output + :class:`~.StagedPassManager`. By default an external + plugin is not used. You can see a list of installed plugins by + using :func:`~.list_stage_plugins` with ``"optimization"`` for the + ``stage_name`` argument. + + Returns: + StagedPassManager: The preset pass manager for the given options + + Raises: + ValueError: if an invalid value for ``optimization_level`` is passed in. + """ + + # Handle positional arguments for target and backend. This enables the usage + # pattern `generate_preset_pass_manager(backend.target)` to generate a default + # pass manager for a given target. + if isinstance(optimization_level, Target): + target = optimization_level + optimization_level = 2 + elif isinstance(optimization_level, Backend): + backend = optimization_level + optimization_level = 2 + + if backend is not None and getattr(backend, "version", 0) <= 1: + # This is a temporary conversion step to allow for a smoother transition + # to a fully target-based transpiler pipeline while maintaining the behavior + # of `transpile` with BackendV1 inputs. + backend = BackendV2Converter(backend) + + # Check if a custom inst_map was specified before overwriting inst_map + _given_inst_map = bool(inst_map) + # If there are no loose constraints => use backend target if available + _no_loose_constraints = ( + basis_gates is None + and coupling_map is None + and dt is None + and instruction_durations is None + and backend_properties is None + and timing_constraints is None + ) + # If it's an edge case => do not build target + _skip_target = ( + target is None + and backend is None + and (basis_gates is None or coupling_map is None or instruction_durations is not None) + ) + + # Resolve loose constraints case-by-case against backend constraints. + # The order of priority is loose constraints > backend. + dt = _parse_dt(dt, backend) + instruction_durations = _parse_instruction_durations(backend, instruction_durations, dt) + timing_constraints = _parse_timing_constraints(backend, timing_constraints) + inst_map = _parse_inst_map(inst_map, backend) + # The basis gates parser will set _skip_target to True if a custom basis gate is found + # (known edge case). + basis_gates, name_mapping, _skip_target = _parse_basis_gates( + basis_gates, backend, inst_map, _skip_target + ) + coupling_map = _parse_coupling_map(coupling_map, backend) + + if target is None: + if backend is not None and _no_loose_constraints: + # If a backend is specified without loose constraints, use its target directly. + target = backend.target + elif not _skip_target: + # Only parse backend properties when the target isn't skipped to + # preserve the former behavior of transpile. + backend_properties = _parse_backend_properties(backend_properties, backend) + # Build target from constraints. + target = Target.from_configuration( + basis_gates=basis_gates, + num_qubits=backend.num_qubits if backend is not None else None, + coupling_map=coupling_map, + # If the instruction map has custom gates, do not give as config, the information + # will be added to the target with update_from_instruction_schedule_map + inst_map=inst_map if inst_map and not inst_map.has_custom_gate() else None, + backend_properties=backend_properties, + instruction_durations=instruction_durations, + concurrent_measurements=( + backend.target.concurrent_measurements if backend is not None else None + ), + dt=dt, + timing_constraints=timing_constraints, + custom_name_mapping=name_mapping, + ) + + # Update target with custom gate information. Note that this is an exception to the priority + # order (target > loose constraints), added to handle custom gates for scheduling passes. + if target is not None and _given_inst_map and inst_map.has_custom_gate(): + target = copy.deepcopy(target) + target.update_from_instruction_schedule_map(inst_map) + + if target is not None: + if coupling_map is None: + coupling_map = target.build_coupling_map() + if basis_gates is None: + basis_gates = target.operation_names + if instruction_durations is None: + instruction_durations = target.durations() + if inst_map is None: + inst_map = target.instruction_schedule_map() + if timing_constraints is None: + timing_constraints = target.timing_constraints() + if backend_properties is None: + backend_properties = target_to_backend_properties(target) + + # Parse non-target dependent pm options + initial_layout = _parse_initial_layout(initial_layout) + approximation_degree = _parse_approximation_degree(approximation_degree) + + pm_options = { + "target": target, + "basis_gates": basis_gates, + "inst_map": inst_map, + "coupling_map": coupling_map, + "instruction_durations": instruction_durations, + "backend_properties": backend_properties, + "timing_constraints": timing_constraints, + "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, + "initial_layout": initial_layout, + "hls_config": hls_config, + "init_method": init_method, + "optimization_method": optimization_method, + } + + if backend is not None: + pm_options["_skip_target"] = _skip_target + pm_config = PassManagerConfig.from_backend(backend, **pm_options) + else: + pm_config = PassManagerConfig(**pm_options) + if optimization_level == 0: + pm = level_0_pass_manager(pm_config) + elif optimization_level == 1: + pm = level_1_pass_manager(pm_config) + elif optimization_level == 2: + pm = level_2_pass_manager(pm_config) + elif optimization_level == 3: + pm = level_3_pass_manager(pm_config) + else: + raise ValueError(f"Invalid optimization level {optimization_level}") + return pm + + +def _parse_basis_gates(basis_gates, backend, inst_map, skip_target): + name_mapping = {} + standard_gates = get_standard_gate_name_mapping() + # Add control flow gates by default to basis set + default_gates = {"measure", "delay", "reset"}.union(CONTROL_FLOW_OP_NAMES) + + try: + instructions = set(basis_gates) + for name in default_gates: + if name not in instructions: + instructions.add(name) + except TypeError: + instructions = None + + if backend is None: + # Check for custom instructions + if instructions is None: + return None, name_mapping, skip_target + + for inst in instructions: + if inst not in standard_gates or inst not in default_gates: + skip_target = True + break + + return list(instructions), name_mapping, skip_target + + instructions = instructions or backend.operation_names + name_mapping.update( + {name: backend.target.operation_from_name(name) for name in backend.operation_names} + ) + + # Check for custom instructions before removing calibrations + for inst in instructions: + if inst not in standard_gates or inst not in default_gates: + skip_target = True + break + + # Remove calibrated instructions, as they will be added later from the instruction schedule map + if inst_map is not None and not skip_target: + for inst in inst_map.instructions: + for qubit in inst_map.qubits_with_instruction(inst): + entry = inst_map._get_calibration_entry(inst, qubit) + if entry.user_provided and inst in instructions: + instructions.remove(inst) + + return list(instructions) if instructions else None, name_mapping, skip_target + + +def _parse_inst_map(inst_map, backend): + # try getting inst_map from user, else backend + if inst_map is None and backend is not None: + inst_map = backend.target.instruction_schedule_map() + return inst_map + + +def _parse_backend_properties(backend_properties, backend): + # try getting backend_props from user, else backend + if backend_properties is None and backend is not None: + backend_properties = target_to_backend_properties(backend.target) + return backend_properties + + +def _parse_dt(dt, backend): + # try getting dt from user, else backend + if dt is None and backend is not None: + dt = backend.target.dt + return dt + + +def _parse_coupling_map(coupling_map, backend): + # try getting coupling_map from user, else backend + if coupling_map is None and backend is not None: + coupling_map = backend.coupling_map + + # coupling_map could be None, or a list of lists, e.g. [[0, 1], [2, 1]] + if coupling_map is None or isinstance(coupling_map, CouplingMap): + return coupling_map + if isinstance(coupling_map, list) and all( + isinstance(i, list) and len(i) == 2 for i in coupling_map + ): + return CouplingMap(coupling_map) + else: + raise TranspilerError( + "Only a single input coupling map can be used with generate_preset_pass_manager()." + ) + + +def _parse_instruction_durations(backend, inst_durations, dt): + """Create a list of ``InstructionDuration``s. If ``inst_durations`` is provided, + the backend will be ignored, otherwise, the durations will be populated from the + backend. + """ + final_durations = InstructionDurations() + if not inst_durations: + backend_durations = InstructionDurations() + if backend is not None: + backend_durations = backend.instruction_durations + final_durations.update(backend_durations, dt or backend_durations.dt) + else: + final_durations.update(inst_durations, dt or getattr(inst_durations, "dt", None)) + return final_durations + + +def _parse_timing_constraints(backend, timing_constraints): + if isinstance(timing_constraints, TimingConstraints): + return timing_constraints + if backend is None and timing_constraints is None: + timing_constraints = TimingConstraints() + elif backend is not None: + timing_constraints = backend.target.timing_constraints() + return timing_constraints + + +def _parse_initial_layout(initial_layout): + # initial_layout could be None, or a list of ints, e.g. [0, 5, 14] + # or a list of tuples/None e.g. [qr[0], None, qr[1]] or a dict e.g. {qr[0]: 0} + if initial_layout is None or isinstance(initial_layout, Layout): + return initial_layout + if isinstance(initial_layout, dict): + return Layout(initial_layout) + initial_layout = list(initial_layout) + if all(phys is None or isinstance(phys, Qubit) for phys in initial_layout): + return Layout.from_qubit_list(initial_layout) + return initial_layout + + +def _parse_approximation_degree(approximation_degree): + if approximation_degree is None: + return None + if approximation_degree < 0.0 or approximation_degree > 1.0: + raise TranspilerError("Approximation degree must be in [0.0, 1.0]") + return approximation_degree diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 7289a8635c55..58381b3db7a8 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -48,7 +48,7 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa initial_layout = pass_manager_config.initial_layout init_method = pass_manager_config.init_method or "default" layout_method = pass_manager_config.layout_method or "default" - routing_method = pass_manager_config.routing_method or "stochastic" + routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" optimization_method = pass_manager_config.optimization_method or "default" scheduling_method = pass_manager_config.scheduling_method or "default" diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 8805deece50e..001e8020962b 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -23,7 +23,6 @@ from typing import Optional, List, Any from collections.abc import Mapping -from collections import defaultdict import datetime import io import logging @@ -31,6 +30,12 @@ import rustworkx as rx +# import target class from the rust side +from qiskit._accelerate.target import ( + BaseTarget, + BaseInstructionProperties, +) + from qiskit.circuit.parameter import Parameter from qiskit.circuit.parameterexpression import ParameterValueType from qiskit.circuit.gate import Gate @@ -51,11 +56,10 @@ from qiskit.providers.backend import QubitProperties # pylint: disable=unused-import from qiskit.providers.models.backendproperties import BackendProperties - logger = logging.getLogger(__name__) -class InstructionProperties: +class InstructionProperties(BaseInstructionProperties): """A representation of the properties of a gate implementation. This class provides the optional properties that a backend can provide @@ -65,12 +69,25 @@ class InstructionProperties: custom attributes for those custom/additional properties by the backend. """ - __slots__ = ("duration", "error", "_calibration") + __slots__ = [ + "_calibration", + ] + + def __new__( # pylint: disable=keyword-arg-before-vararg + cls, + duration=None, # pylint: disable=keyword-arg-before-vararg + error=None, # pylint: disable=keyword-arg-before-vararg + *args, # pylint: disable=unused-argument + **kwargs, # pylint: disable=unused-argument + ): + return super(InstructionProperties, cls).__new__( # pylint: disable=too-many-function-args + cls, duration, error + ) def __init__( self, - duration: float | None = None, - error: float | None = None, + duration: float | None = None, # pylint: disable=unused-argument + error: float | None = None, # pylint: disable=unused-argument calibration: Schedule | ScheduleBlock | CalibrationEntry | None = None, ): """Create a new ``InstructionProperties`` object @@ -82,10 +99,8 @@ def __init__( set of qubits. calibration: The pulse representation of the instruction. """ + super().__init__() self._calibration: CalibrationEntry | None = None - - self.duration = duration - self.error = error self.calibration = calibration @property @@ -135,8 +150,16 @@ def __repr__(self): f", calibration={self._calibration})" ) + def __getstate__(self) -> tuple: + return (super().__getstate__(), self.calibration, self._calibration) + + def __setstate__(self, state: tuple): + super().__setstate__(state[0]) + self.calibration = state[1] + self._calibration = state[2] + -class Target(Mapping): +class Target(BaseTarget): """ The intent of the ``Target`` object is to inform Qiskit's compiler about the constraints of a particular backend so the compiler can compile an @@ -221,37 +244,25 @@ class Target(Mapping): """ __slots__ = ( - "num_qubits", "_gate_map", - "_gate_name_map", - "_qarg_gate_map", - "description", "_coupling_graph", "_instruction_durations", "_instruction_schedule_map", - "dt", - "granularity", - "min_length", - "pulse_alignment", - "acquire_alignment", - "_non_global_basis", - "_non_global_strict_basis", - "qubit_properties", - "_global_operations", - "concurrent_measurements", ) - def __init__( - self, - description=None, - num_qubits=0, - dt=None, - granularity=1, - min_length=1, - pulse_alignment=1, - acquire_alignment=1, - qubit_properties=None, - concurrent_measurements=None, + def __new__( # pylint: disable=keyword-arg-before-vararg + cls, + description: str | None = None, + num_qubits: int = 0, + dt: float | None = None, + granularity: int = 1, + min_length: int = 1, + pulse_alignment: int = 1, + acquire_alignment: int = 1, + qubit_properties: list | None = None, + concurrent_measurements: list | None = None, + *args, # pylint: disable=unused-argument disable=keyword-arg-before-vararg + **kwargs, # pylint: disable=unused-argument ): """ Create a new ``Target`` object @@ -295,37 +306,38 @@ def __init__( defined and the value of ``num_qubits`` differs from the length of ``qubit_properties``. """ - self.num_qubits = num_qubits - # A mapping of gate name -> gate instance - self._gate_name_map = {} + if description is not None: + description = str(description) + return super(Target, cls).__new__( # pylint: disable=too-many-function-args + cls, + description, + num_qubits, + dt, + granularity, + min_length, + pulse_alignment, + acquire_alignment, + qubit_properties, + concurrent_measurements, + ) + + def __init__( + self, + description=None, # pylint: disable=unused-argument + num_qubits=0, # pylint: disable=unused-argument + dt=None, # pylint: disable=unused-argument + granularity=1, # pylint: disable=unused-argument + min_length=1, # pylint: disable=unused-argument + pulse_alignment=1, # pylint: disable=unused-argument + acquire_alignment=1, # pylint: disable=unused-argument + qubit_properties=None, # pylint: disable=unused-argument + concurrent_measurements=None, # pylint: disable=unused-argument + ): # A nested mapping of gate name -> qargs -> properties self._gate_map = {} - # A mapping of number of qubits to set of op names which are global - self._global_operations = defaultdict(set) - # A mapping of qarg -> set(gate name) - self._qarg_gate_map = defaultdict(set) - self.dt = dt - self.description = description self._coupling_graph = None self._instruction_durations = None self._instruction_schedule_map = None - self.granularity = granularity - self.min_length = min_length - self.pulse_alignment = pulse_alignment - self.acquire_alignment = acquire_alignment - self._non_global_basis = None - self._non_global_strict_basis = None - if qubit_properties is not None: - if not self.num_qubits: - self.num_qubits = len(qubit_properties) - else: - if self.num_qubits != len(qubit_properties): - raise ValueError( - "The value of num_qubits specified does not match the " - "length of the input qubit_properties list" - ) - self.qubit_properties = qubit_properties - self.concurrent_measurements = concurrent_measurements def add_instruction(self, instruction, properties=None, name=None): """Add a new instruction to the :class:`~qiskit.transpiler.Target` @@ -408,35 +420,15 @@ def add_instruction(self, instruction, properties=None, name=None): "An instruction added globally by class can't have properties set." ) instruction_name = name - if properties is None: + if properties is None or is_class: properties = {None: None} if instruction_name in self._gate_map: raise AttributeError(f"Instruction {instruction_name} is already in the target") - self._gate_name_map[instruction_name] = instruction - if is_class: - qargs_val = {None: None} - else: - if None in properties: - self._global_operations[instruction.num_qubits].add(instruction_name) - qargs_val = {} - for qarg in properties: - if qarg is not None and len(qarg) != instruction.num_qubits: - raise TranspilerError( - f"The number of qubits for {instruction} does not match the number " - f"of qubits in the properties dictionary: {qarg}" - ) - if qarg is not None: - self.num_qubits = max( - self.num_qubits if self.num_qubits is not None else 0, max(qarg) + 1 - ) - qargs_val[qarg] = properties[qarg] - self._qarg_gate_map[qarg].add(instruction_name) - self._gate_map[instruction_name] = qargs_val + super().add_instruction(instruction, instruction_name, properties) + self._gate_map[instruction_name] = properties self._coupling_graph = None self._instruction_durations = None self._instruction_schedule_map = None - self._non_global_basis = None - self._non_global_strict_basis = None def update_instruction_properties(self, instruction, qargs, properties): """Update the property object for an instruction qarg pair already in the Target @@ -448,10 +440,7 @@ def update_instruction_properties(self, instruction, qargs, properties): Raises: KeyError: If ``instruction`` or ``qarg`` are not in the target """ - if instruction not in self._gate_map: - raise KeyError(f"Provided instruction: '{instruction}' not in this Target") - if qargs not in self._gate_map[instruction]: - raise KeyError(f"Provided qarg: '{qargs}' not in this Target for {instruction}") + super().update_instruction_properties(instruction, qargs, properties) self._gate_map[instruction][qargs] = properties self._instruction_durations = None self._instruction_schedule_map = None @@ -574,14 +563,6 @@ def update_from_instruction_schedule_map(self, inst_map, inst_name_map=None, err continue self.update_instruction_properties(inst_name, qargs, prop) - @property - def qargs(self): - """The set of qargs in the target.""" - qargs = set(self._qarg_gate_map) - if len(qargs) == 1 and next(iter(qargs)) is None: - return None - return qargs - def qargs_for_operation_name(self, operation): """Get the qargs for a given operation name @@ -643,236 +624,6 @@ def instruction_schedule_map(self): self._instruction_schedule_map = out_inst_schedule_map return out_inst_schedule_map - def operation_from_name(self, instruction): - """Get the operation class object for a given name - - Args: - instruction (str): The instruction name to get the - :class:`~qiskit.circuit.Instruction` instance for - Returns: - qiskit.circuit.Instruction: The Instruction instance corresponding to the - name. This also can also be the class for globally defined variable with - operations. - """ - return self._gate_name_map[instruction] - - def operations_for_qargs(self, qargs): - """Get the operation class object for a specified qargs tuple - - Args: - qargs (tuple): A qargs tuple of the qubits to get the gates that apply - to it. For example, ``(0,)`` will return the set of all - instructions that apply to qubit 0. If set to ``None`` this will - return any globally defined operations in the target. - Returns: - list: The list of :class:`~qiskit.circuit.Instruction` instances - that apply to the specified qarg. This may also be a class if - a variable width operation is globally defined. - - Raises: - KeyError: If qargs is not in target - """ - if qargs is not None and any(x not in range(0, self.num_qubits) for x in qargs): - raise KeyError(f"{qargs} not in target.") - res = [self._gate_name_map[x] for x in self._qarg_gate_map[qargs]] - if qargs is not None: - res += self._global_operations.get(len(qargs), []) - for op in self._gate_name_map.values(): - if inspect.isclass(op): - res.append(op) - if not res: - raise KeyError(f"{qargs} not in target.") - return list(res) - - def operation_names_for_qargs(self, qargs): - """Get the operation names for a specified qargs tuple - - Args: - qargs (tuple): A ``qargs`` tuple of the qubits to get the gates that apply - to it. For example, ``(0,)`` will return the set of all - instructions that apply to qubit 0. If set to ``None`` this will - return the names for any globally defined operations in the target. - Returns: - set: The set of operation names that apply to the specified ``qargs``. - - Raises: - KeyError: If ``qargs`` is not in target - """ - # if num_qubits == 0, we will return globally defined operations - if self.num_qubits == 0 or self.num_qubits is None: - qargs = None - if qargs is not None and any(x not in range(0, self.num_qubits) for x in qargs): - raise KeyError(f"{qargs} not in target.") - res = self._qarg_gate_map.get(qargs, set()) - if qargs is not None: - res.update(self._global_operations.get(len(qargs), set())) - for name, op in self._gate_name_map.items(): - if inspect.isclass(op): - res.add(name) - if not res: - raise KeyError(f"{qargs} not in target.") - return res - - def instruction_supported( - self, operation_name=None, qargs=None, operation_class=None, parameters=None - ): - """Return whether the instruction (operation + qubits) is supported by the target - - Args: - operation_name (str): The name of the operation for the instruction. Either - this or ``operation_class`` must be specified, if both are specified - ``operation_class`` will take priority and this argument will be ignored. - qargs (tuple): The tuple of qubit indices for the instruction. If this is - not specified then this method will return ``True`` if the specified - operation is supported on any qubits. The typical application will - always have this set (otherwise it's the same as just checking if the - target contains the operation). Normally you would not set this argument - if you wanted to check more generally that the target supports an operation - with the ``parameters`` on any qubits. - operation_class (Type[qiskit.circuit.Instruction]): The operation class to check whether - the target supports a particular operation by class rather - than by name. This lookup is more expensive as it needs to - iterate over all operations in the target instead of just a - single lookup. If this is specified it will supersede the - ``operation_name`` argument. The typical use case for this - operation is to check whether a specific variant of an operation - is supported on the backend. For example, if you wanted to - check whether a :class:`~.RXGate` was supported on a specific - qubit with a fixed angle. That fixed angle variant will - typically have a name different from the object's - :attr:`~.Instruction.name` attribute (``"rx"``) in the target. - This can be used to check if any instances of the class are - available in such a case. - parameters (list): A list of parameters to check if the target - supports them on the specified qubits. If the instruction - supports the parameter values specified in the list on the - operation and qargs specified this will return ``True`` but - if the parameters are not supported on the specified - instruction it will return ``False``. If this argument is not - specified this method will return ``True`` if the instruction - is supported independent of the instruction parameters. If - specified with any :class:`~.Parameter` objects in the list, - that entry will be treated as supporting any value, however parameter names - will not be checked (for example if an operation in the target - is listed as parameterized with ``"theta"`` and ``"phi"`` is - passed into this function that will return ``True``). For - example, if called with:: - - parameters = [Parameter("theta")] - target.instruction_supported("rx", (0,), parameters=parameters) - - will return ``True`` if an :class:`~.RXGate` is supported on qubit 0 - that will accept any parameter. If you need to check for a fixed numeric - value parameter this argument is typically paired with the ``operation_class`` - argument. For example:: - - target.instruction_supported("rx", (0,), RXGate, parameters=[pi / 4]) - - will return ``True`` if an RXGate(pi/4) exists on qubit 0. - - Returns: - bool: Returns ``True`` if the instruction is supported and ``False`` if it isn't. - - """ - - def check_obj_params(parameters, obj): - for index, param in enumerate(parameters): - if isinstance(param, Parameter) and not isinstance(obj.params[index], Parameter): - return False - if param != obj.params[index] and not isinstance(obj.params[index], Parameter): - return False - return True - - # Handle case where num_qubits is None by always checking globally supported operations - if self.num_qubits is None: - qargs = None - # Case a list if passed in by mistake - if qargs is not None: - qargs = tuple(qargs) - if operation_class is not None: - for op_name, obj in self._gate_name_map.items(): - if inspect.isclass(obj): - if obj != operation_class: - continue - # If no qargs operation class is supported - if qargs is None: - return True - # If qargs set then validate no duplicates and all indices are valid on device - elif all(qarg <= self.num_qubits for qarg in qargs) and len(set(qargs)) == len( - qargs - ): - return True - else: - return False - - if isinstance(obj, operation_class): - if parameters is not None: - if len(parameters) != len(obj.params): - continue - if not check_obj_params(parameters, obj): - continue - if qargs is None: - return True - if qargs in self._gate_map[op_name]: - return True - if self._gate_map[op_name] is None or None in self._gate_map[op_name]: - return obj.num_qubits == len(qargs) and all( - x < self.num_qubits for x in qargs - ) - return False - if operation_name in self._gate_map: - if parameters is not None: - obj = self._gate_name_map[operation_name] - if inspect.isclass(obj): - # The parameters argument was set and the operation_name specified is - # defined as a globally supported class in the target. This means - # there is no available validation (including whether the specified - # operation supports parameters), the returned value will not factor - # in the argument `parameters`, - - # If no qargs a operation class is supported - if qargs is None: - return True - # If qargs set then validate no duplicates and all indices are valid on device - elif all(qarg <= self.num_qubits for qarg in qargs) and len(set(qargs)) == len( - qargs - ): - return True - else: - return False - if len(parameters) != len(obj.params): - return False - for index, param in enumerate(parameters): - matching_param = False - if isinstance(obj.params[index], Parameter): - matching_param = True - elif param == obj.params[index]: - matching_param = True - if not matching_param: - return False - return True - if qargs is None: - return True - if qargs in self._gate_map[operation_name]: - return True - if self._gate_map[operation_name] is None or None in self._gate_map[operation_name]: - obj = self._gate_name_map[operation_name] - if inspect.isclass(obj): - if qargs is None: - return True - # If qargs set then validate no duplicates and all indices are valid on device - elif all(qarg <= self.num_qubits for qarg in qargs) and len(set(qargs)) == len( - qargs - ): - return True - else: - return False - else: - return self._gate_name_map[operation_name].num_qubits == len(qargs) and all( - x < self.num_qubits for x in qargs - ) - return False - def has_calibration( self, operation_name: str, @@ -927,11 +678,6 @@ def operation_names(self): """Get the operation names in the target.""" return self._gate_map.keys() - @property - def operations(self): - """Get the operation class objects in the target.""" - return list(self._gate_name_map.values()) - @property def instructions(self): """Get the list of tuples ``(:class:`~qiskit.circuit.Instruction`, (qargs))`` @@ -994,25 +740,30 @@ def _build_coupling_graph(self): for gate, qarg_map in self._gate_map.items(): if qarg_map is None: if self._gate_name_map[gate].num_qubits == 2: - self._coupling_graph = None + self._coupling_graph = None # pylint: disable=attribute-defined-outside-init return continue for qarg, properties in qarg_map.items(): if qarg is None: - if self._gate_name_map[gate].num_qubits == 2: + if self.operation_from_name(gate).num_qubits == 2: self._coupling_graph = None return continue if len(qarg) == 1: - self._coupling_graph[qarg[0]] = properties + self._coupling_graph[qarg[0]] = ( + properties # pylint: disable=attribute-defined-outside-init + ) elif len(qarg) == 2: try: edge_data = self._coupling_graph.get_edge_data(*qarg) edge_data[gate] = properties except rx.NoEdgeBetweenNodes: self._coupling_graph.add_edge(*qarg, {gate: properties}) - if self._coupling_graph.num_edges() == 0 and any(x is None for x in self._qarg_gate_map): - self._coupling_graph = None + qargs = self.qargs + if self._coupling_graph.num_edges() == 0 and ( + qargs is None or any(x is None for x in qargs) + ): + self._coupling_graph = None # pylint: disable=attribute-defined-outside-init def build_coupling_map(self, two_q_gate=None, filter_idle_qubits=False): """Get a :class:`~qiskit.transpiler.CouplingMap` from this target. @@ -1059,7 +810,7 @@ def build_coupling_map(self, two_q_gate=None, filter_idle_qubits=False): if two_q_gate is not None: coupling_graph = rx.PyDiGraph(multigraph=False) coupling_graph.add_nodes_from([None] * self.num_qubits) - for qargs, properties in self._gate_map[two_q_gate].items(): + for qargs, properties in self[two_q_gate].items(): if len(qargs) != 2: raise ValueError( f"Specified two_q_gate: {two_q_gate} is not a 2 qubit instruction" @@ -1090,70 +841,19 @@ def _filter_coupling_graph(self): graph.remove_nodes_from(list(to_remove)) return graph - @property - def physical_qubits(self): - """Returns a sorted list of physical_qubits""" - return list(range(self.num_qubits)) - - def get_non_global_operation_names(self, strict_direction=False): - """Return the non-global operation names for the target - - The non-global operations are those in the target which don't apply - on all qubits (for single qubit operations) or all multi-qubit qargs - (for multi-qubit operations). - - Args: - strict_direction (bool): If set to ``True`` the multi-qubit - operations considered as non-global respect the strict - direction (or order of qubits in the qargs is significant). For - example, if ``cx`` is defined on ``(0, 1)`` and ``ecr`` is - defined over ``(1, 0)`` by default neither would be considered - non-global, but if ``strict_direction`` is set ``True`` both - ``cx`` and ``ecr`` would be returned. - - Returns: - List[str]: A list of operation names for operations that aren't global in this target - """ - if strict_direction: - if self._non_global_strict_basis is not None: - return self._non_global_strict_basis - search_set = self._qarg_gate_map.keys() - else: - if self._non_global_basis is not None: - return self._non_global_basis - - search_set = { - frozenset(qarg) - for qarg in self._qarg_gate_map - if qarg is not None and len(qarg) != 1 - } - incomplete_basis_gates = [] - size_dict = defaultdict(int) - size_dict[1] = self.num_qubits - for qarg in search_set: - if qarg is None or len(qarg) == 1: - continue - size_dict[len(qarg)] += 1 - for inst, qargs in self._gate_map.items(): - qarg_sample = next(iter(qargs)) - if qarg_sample is None: - continue - if not strict_direction: - qargs = {frozenset(qarg) for qarg in qargs} - if len(qargs) != size_dict[len(qarg_sample)]: - incomplete_basis_gates.append(inst) - if strict_direction: - self._non_global_strict_basis = incomplete_basis_gates - else: - self._non_global_basis = incomplete_basis_gates - return incomplete_basis_gates - def __iter__(self): return iter(self._gate_map) def __getitem__(self, key): return self._gate_map[key] + def get(self, key, default=None): + """Gets an item from the Target. If not found return a provided default or `None`.""" + try: + return self[key] + except KeyError: + return default + def __len__(self): return len(self._gate_map) @@ -1161,12 +861,15 @@ def __contains__(self, item): return item in self._gate_map def keys(self): + """Return the keys (operation_names) of the Target""" return self._gate_map.keys() def values(self): + """Return the Property Map (qargs -> InstructionProperties) of every instruction in the Target""" return self._gate_map.values() def items(self): + """Returns pairs of Gate names and its property map (str, dict[tuple, InstructionProperties])""" return self._gate_map.items() def __str__(self): @@ -1188,10 +891,10 @@ def __str__(self): prop_str_pieces = [f"\t\t{qarg}:\n"] duration = getattr(props, "duration", None) if duration is not None: - prop_str_pieces.append(f"\t\t\tDuration: {duration} sec.\n") + prop_str_pieces.append(f"\t\t\tDuration: {duration:g} sec.\n") error = getattr(props, "error", None) if error is not None: - prop_str_pieces.append(f"\t\t\tError Rate: {error}\n") + prop_str_pieces.append(f"\t\t\tError Rate: {error:g}\n") schedule = getattr(props, "_calibration", None) if schedule is not None: prop_str_pieces.append("\t\t\tWith pulse schedule calibration\n") @@ -1205,6 +908,22 @@ def __str__(self): output.write("".join(prop_str_pieces)) return output.getvalue() + def __getstate__(self) -> dict: + return { + "_gate_map": self._gate_map, + "coupling_graph": self._coupling_graph, + "instruction_durations": self._instruction_durations, + "instruction_schedule_map": self._instruction_schedule_map, + "base": super().__getstate__(), + } + + def __setstate__(self, state: tuple): + self._gate_map = state["_gate_map"] + self._coupling_graph = state["coupling_graph"] + self._instruction_durations = state["instruction_durations"] + self._instruction_schedule_map = state["instruction_schedule_map"] + super().__setstate__(state["base"]) + @classmethod def from_configuration( cls, @@ -1442,6 +1161,9 @@ def from_configuration( return target +Mapping.register(Target) + + def target_to_backend_properties(target: Target): """Convert a :class:`~.Target` object into a legacy :class:`~.BackendProperties`""" diff --git a/qiskit/user_config.py b/qiskit/user_config.py index 0ca52fc5c8c0..22d12406b348 100644 --- a/qiskit/user_config.py +++ b/qiskit/user_config.py @@ -35,6 +35,7 @@ class UserConfig: transpile_optimization_level = 1 parallel = False num_processes = 4 + sabre_all_threads = true """ @@ -168,6 +169,13 @@ def read_config_file(self): ) self.settings["num_processes"] = num_processes + # Parse sabre_all_threads + sabre_all_threads = self.config_parser.getboolean( + "default", "sabre_all_threads", fallback=None + ) + if sabre_all_threads is not None: + self.settings["sabre_all_threads"] = sabre_all_threads + def set_config(key, value, section=None, file_path=None): """Adds or modifies a user configuration @@ -208,6 +216,7 @@ def set_config(key, value, section=None, file_path=None): "transpile_optimization_level", "parallel", "num_processes", + "sabre_all_threads", } if section in [None, "default"]: diff --git a/qiskit/visualization/circuit/_utils.py b/qiskit/visualization/circuit/_utils.py index 2077a3891542..e6ee03905d27 100644 --- a/qiskit/visualization/circuit/_utils.py +++ b/qiskit/visualization/circuit/_utils.py @@ -387,7 +387,7 @@ def _get_valid_justify_arg(justify): warn( f"Setting QuantumCircuit.draw()’s or circuit_drawer()'s justify argument: {justify}, to a " "value other than 'left', 'right', 'none' or None (='left'). Default 'left' will be used. " - "Support for invalid justify arguments is deprecated as of qiskit 1.2.0. Starting no " + "Support for invalid justify arguments is deprecated as of Qiskit 1.2.0. Starting no " "earlier than 3 months after the release date, invalid arguments will error.", DeprecationWarning, 2, diff --git a/qiskit/visualization/transition_visualization.py b/qiskit/visualization/transition_visualization.py index a2ff74799999..0d1623831883 100644 --- a/qiskit/visualization/transition_visualization.py +++ b/qiskit/visualization/transition_visualization.py @@ -18,6 +18,7 @@ import numpy as np from qiskit.exceptions import MissingOptionalLibraryError +from qiskit.utils.deprecation import deprecate_func def _normalize(v, tolerance=0.00001): @@ -121,6 +122,10 @@ def vector_norm(self): return np.linalg.norm(v) +@deprecate_func( + since="1.2.0", + removal_timeline="in the 2.0 release", +) def visualize_transition(circuit, trace=False, saveas=None, fpg=100, spg=2): """ Creates animation showing transitions between states of a single diff --git a/releasenotes/notes/add-generate-preset-pm-global-import-efb12f185f3f738b.yaml b/releasenotes/notes/add-generate-preset-pm-global-import-efb12f185f3f738b.yaml new file mode 100644 index 000000000000..98c9f64cf2ae --- /dev/null +++ b/releasenotes/notes/add-generate-preset-pm-global-import-efb12f185f3f738b.yaml @@ -0,0 +1,14 @@ +--- +features_transpiler: + - | + Added a new import path option for :func:`.generate_preset_pass_manager`, so that it can now be imported as:: + + from qiskit import generate_preset_pass_manager + + instead of having to type the full path:: + + from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager + + The function is also importable from the :mod:`qiskit.transpiler` module as:: + + from qiskit.transpiler import generate_preset_pass_manager \ No newline at end of file diff --git a/releasenotes/notes/add-sabre-all-threads-option-ad4ff7a4d045cb2b.yaml b/releasenotes/notes/add-sabre-all-threads-option-ad4ff7a4d045cb2b.yaml new file mode 100644 index 000000000000..24b64a28703e --- /dev/null +++ b/releasenotes/notes/add-sabre-all-threads-option-ad4ff7a4d045cb2b.yaml @@ -0,0 +1,26 @@ +--- +features_transpiler: + - | + Added a new user config file option ``sabre_all_threads`` and a + corresponding environment variable ``QISKIT_SABRE_ALL_THREADS``. When this + flag is set the preset pass managers will run the :class:`.SabreLayout` + and :class:`.SabreSwap` transpiler passes using all the available + CPUs on the local system. Using this option is a tradeoff between + determinism of output between different computers and potentially better + output with fewer :class:`.SwapGate`\s. + + These transpiler passes run multiple random trials in parallel and pick + the output which results in the fewest :class:`.SwapGate`\s. As a rule of + thumb, if you run more trials, this provides the algorithm more opportunities + to find a better result. By default, the preset pass managers use a fixed + number of trials, in this release 5 trials for levels 0 and 1, and 20 + trials for levels 2 and 3, but these numbers may change in future releases + (and were different in historical releases). Using a fixed number of + trials results in deterministic results regardless of the local system, + because even with a fixed seed if you were to default to the number of + local CPUs available the results would different when running between + different computers. + + If the default number of trials for a given optimization level is higher + than the number of local CPUs it will use the optimization level default + which is higher. diff --git a/releasenotes/notes/annotated-params-116288d5628f7ee8.yaml b/releasenotes/notes/annotated-params-116288d5628f7ee8.yaml new file mode 100644 index 000000000000..2f316a06ae8d --- /dev/null +++ b/releasenotes/notes/annotated-params-116288d5628f7ee8.yaml @@ -0,0 +1,30 @@ +--- +features_circuits: + - | + Added support for :meth:`.AnnotatedOperation.params` and + :meth:`.AnnotatedOperation.validate_parameter`, which enable + circuit-level parameter handling (such as binding parameters) for + annotated operations. +fixes: + - | + Fixed a series of issues when controlling parameterized standard gates. + The controlled version of some gates (e.g. :class:`.RXXGate` or + :class:`.RYGate` for more than 1 control) cannot be synthesized if + they contain unbound parameters. Previously, calling ``.control()`` but + now we create an :class:`.AnnotatedOperation` as placeholder. This + allows to insert the controlled gate into a circuit, bind the parameters + at a later stage, and then synthesize the operation. + Fixes `#10311 `_, + `#10697 `_, + and `#12135 `_. + - | + The :class:`.SGate` and :class:`.SdgGate` now correctly return a + :class:`.CSGate`, resp. :class:`.CSdgGate`, if they are controlled on + a single control qubit. +upgrade_circuits: + - | + The ``annotated`` argument of the :meth:`.Gate.control` method is now + ``None``, which allows Qiskit to choose whether to annotate a controlled operation. + If the concrete implementation (``annotated=False``) is available, it will be returned by + default. Otherwise, the annotated implementation will be returned (``annotated=True``). + This allows, for example, to defer the synthesis of controlled, parameterized gates. diff --git a/releasenotes/notes/bitarray-postselect-659b8f7801ccaa60.yaml b/releasenotes/notes/bitarray-postselect-659b8f7801ccaa60.yaml new file mode 100644 index 000000000000..33ce17bafa8d --- /dev/null +++ b/releasenotes/notes/bitarray-postselect-659b8f7801ccaa60.yaml @@ -0,0 +1,11 @@ +--- +features_primitives: + - | + Added a new method :meth:`.BitArray.postselect` that returns all shots containing specified bit values. + Example usage:: + + from qiskit.primitives.containers import BitArray + + ba = BitArray.from_counts({'110': 2, '100': 4, '000': 3}) + print(ba.postselect([0,2], [0,1]).get_counts()) + # {'110': 2, '100': 4} diff --git a/releasenotes/notes/default-level-2-generate-preset-passmanager-ec758ddc896ae2d6.yaml b/releasenotes/notes/default-level-2-generate-preset-passmanager-ec758ddc896ae2d6.yaml new file mode 100644 index 000000000000..ff5d57797860 --- /dev/null +++ b/releasenotes/notes/default-level-2-generate-preset-passmanager-ec758ddc896ae2d6.yaml @@ -0,0 +1,44 @@ +--- +features_transpiler: + - | + The ``optimization_level`` argument for the :func:`.generate_preset_pass_manager` function is + now optional. If it's not specified it will default to using optimization level 2. As the argument + is now optional, the first positional argument has been expanded to enable passing a :class:`.Target` + or a :class:`.BackendV2` as the first argument for more convenient construction. For example:: + + from qiskit.transpiler.preset_passmanager import generate_preset_pass_manager + from qiskit.providers.fake_provider import GenericBackendV2 + + backend = GenericBackendV2(100) + + generate_preset_pass_manager(backend.Target) + + will construct a default pass manager for the 100 qubit :class`.GenericBackendV2` instance. +upgrade_transpiler: + - | + The default ``optimization_level`` used by the :func:`.transpile` function when one is not + specified has been changed to level 2. This makes it consistent with the default used + by :func:`.generate_preset_pass_manager` which is used internally by :func:`.transpile`. Optimization + level 2 provides a much better balance between the run time of the function and the optimizations it + performs, it's a better tradeoff to use by default. + + The API of :func:`.transpile` remains unchanged because, fundamentally, level 2 and level 1 + have the same semantics. If you were previously relying on the implicit default of level 1, + you can simply set the argument ``optimization_level=1`` when you call :func:`.transpile`. + Similarly you can change the default back in your local environment by using a user config + file and setting the ``transpile_optimization_level`` field to 1. + + The only potential issue is that your transpilation workflow may be relying on an implicit trivial layout (where qubit 0 + in the circuit passed to :func:`.transpile` is mapped to qubit 0 on the target backend/coupling, + 1->1, 2->2, etc.) without specifying ``optimization_level=1``, ``layout_method="trivial"``, or + explicitly setting ``initial_layout`` when calling :func:`.transpile`. This behavior was a side + effect of the preset pass manager construction in optimization level 1 and is not mirrored in + level 2. If you need this behavior you can use any of the three options listed previously to make + this behavior explicit. + + Similarly, if you were targeting a discrete basis gate set you may encounter an issue using the + new default with optimization level 2 (or running explicitly optimization level 3), as the additional optimization passes that run in + level 2 and 3 don't work in all cases with a discrete basis. You can explicitly set + ``optimization_level=1`` manually in this case. In general the transpiler does not currently + fully support discrete basis sets and if you're relying on this you should likely construct a + pass manager manually to build a compilation pipeline that will work with your target. diff --git a/releasenotes/notes/deprecate-circuit-internal-helpers-ee65fbac455de47c.yaml b/releasenotes/notes/deprecate-circuit-internal-helpers-ee65fbac455de47c.yaml new file mode 100644 index 000000000000..809aa11adc9c --- /dev/null +++ b/releasenotes/notes/deprecate-circuit-internal-helpers-ee65fbac455de47c.yaml @@ -0,0 +1,11 @@ +--- +deprecations_circuits: + - | + The following circuit methods were not intended for public use, but were accidentally left documented in the + public API during the 1.0 release. They are now deprecated from Qiskit 1.2 and will be removed in Qiskit 2.0: + + * ``QuantumCircuit.cast`` + * ``QuantumCircuit.cls_instances`` + * ``QuantumCircuit.cls_prefix`` + * ``QuantumCircuit.cbit_argument_conversion`` + * ``QuantumCircuit.qbit_argument_conversion`` \ No newline at end of file diff --git a/releasenotes/notes/deprecate-primitives-v1.yaml b/releasenotes/notes/deprecate-primitives-v1.yaml new file mode 100644 index 000000000000..3d0335d358cb --- /dev/null +++ b/releasenotes/notes/deprecate-primitives-v1.yaml @@ -0,0 +1,32 @@ +--- +deprecations_primitives: + - | + Primitive V1 implementations and V1-exclusive non-versioned type aliases are now + deprecated in favor of their V2 counterparts. The deprecation is extended to the + following classes implementing V1 interfaces: + + * :class:`.Estimator`, in favor of the V2 equivalent, :class:`.StatevectorEstimator` + * :class:`.Sampler`, in favor of the V2 equivalent, :class:`.StatevectorSampler` + * :class:`.BackendEstimator`, in favor of the V2 equivalent, :class:`.BackendEstimatorV2` + * :class:`.BackendSampler`, in favor of the V2 equivalent, :class:`.BackendSamplerV2` + + As well as the following non-versioned type aliases: + + * :class:`.BaseEstimator`, alias for :class:`.BaseEstimatorV1` + * :class:`.BaseSampler`, alias for :class:`.BaseSamplerV1` + + This deprecation does NOT affect the explicitly-versioned :class:`BaseEstimatorV1` + and :class:`BaseSamplerV1` abstract + interface definitions or related result and job classes. + + In addition, the following utility functions are deprecated: + + * :func:`.init_circuit`, to initialize a circuit from a :class:`.Statevector`, + use :meth:`.QuantumCircuit.initialize` instead, + * :func:`.init_observable`, use the constructor of :class:`.SparsePauliOp` instead, + * :func:`.final_measurement_mapping`, use :meth:`.QuantumCircuit.layout` and + :meth:`.SparsePauliOp.apply_layout` to adjust an operator for a layout. + Otherwise, use ``mthree.utils.final_measurement_mapping``. + See `Mthree Utility functions `__ + for details. + diff --git a/releasenotes/notes/deprecate-visualize_transition-8c1d257b7f37aa58.yaml b/releasenotes/notes/deprecate-visualize_transition-8c1d257b7f37aa58.yaml new file mode 100644 index 000000000000..6900dcbd7676 --- /dev/null +++ b/releasenotes/notes/deprecate-visualize_transition-8c1d257b7f37aa58.yaml @@ -0,0 +1,7 @@ +--- +deprecations_visualization: + - | + The :func:`.transition_visualization` function has been deprecated and will + be removed in the 2.0.0 release. This function had a number of limitations + which limited it's utility to only very specific use cases and didn't fit in with + the rest of the Qiskit visualization module. diff --git a/releasenotes/notes/deprecate_assemble-67486b4d0a8d4f96.yaml b/releasenotes/notes/deprecate_assemble-67486b4d0a8d4f96.yaml new file mode 100644 index 000000000000..b7b50d2eb2b2 --- /dev/null +++ b/releasenotes/notes/deprecate_assemble-67486b4d0a8d4f96.yaml @@ -0,0 +1,7 @@ +--- +deprecations_providers: + - | + The `Qobj` structure and related classes are now deprecated, they were introduced as part of the `BackendV1` workflow and are no longer necessary for interacting with :class:`.BackendV2` backends. Remote backend interaction should be done via `QPY` or `OpenQASM` instead. +deprecations_transpiler: + - | + The `assemble` function is now deprecated and will be removed in the 2.0 release. The function was primarily used to create a `Qobj`, which is no longer necessary in :class:`.BackendV2`-based workflows. It was also used for binding parameters, a functionality fully covered by :meth:`~.QuantumCircuit.assign_parameters`. diff --git a/releasenotes/notes/fix-bitarray-fromcounts-nobits-82958a596b3489ec.yaml b/releasenotes/notes/fix-bitarray-fromcounts-nobits-82958a596b3489ec.yaml new file mode 100644 index 000000000000..6be5ea21bdf5 --- /dev/null +++ b/releasenotes/notes/fix-bitarray-fromcounts-nobits-82958a596b3489ec.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixed a bug in :meth:`.BitArray.from_counts` and :meth:`.BitArray.from_samples`. + Previously these would raise an error if given data containing only zeros, and no + value for the optional argument ``num_bits``. Now they produce a :class:`.BitArray` + with :attr:`.BitArray.num_bits` set to 1. diff --git a/releasenotes/notes/fix-bitarray-slice-bits-shots-c9cb7e5d907722f5.yaml b/releasenotes/notes/fix-bitarray-slice-bits-shots-c9cb7e5d907722f5.yaml new file mode 100644 index 000000000000..56fd81012428 --- /dev/null +++ b/releasenotes/notes/fix-bitarray-slice-bits-shots-c9cb7e5d907722f5.yaml @@ -0,0 +1,11 @@ +--- +upgrade_primitives: + - | + :meth:`.BitArray.slice_bits` and :meth:`.BitArray.slice_shots` + will now raise ``IndexError`` when indices are out of bounds. + They used to raise ``ValueError`` in the case. + - | + :meth:`.BitArray.__getitem__` will now raise ``IndexError`` + when indices are out of bounds or the number of dimensions + of indices does not match that of BitArray. + They used to raise ``ValueError`` in the case. diff --git a/releasenotes/notes/fix-statevector-sampler-c_if-9753f8f97a3d0ff5.yaml b/releasenotes/notes/fix-statevector-sampler-c_if-9753f8f97a3d0ff5.yaml new file mode 100644 index 000000000000..d743b52ee900 --- /dev/null +++ b/releasenotes/notes/fix-statevector-sampler-c_if-9753f8f97a3d0ff5.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed a bug of :class:`.StatevectorSampler` that ignored gates with ``c_if``. + It will raise an error because :class:`.Statevector` cannot handle ``c_if``. diff --git a/releasenotes/notes/port_star_prerouting-13fae3ff78feb5e3.yaml b/releasenotes/notes/port_star_prerouting-13fae3ff78feb5e3.yaml new file mode 100644 index 000000000000..f8eca807bec6 --- /dev/null +++ b/releasenotes/notes/port_star_prerouting-13fae3ff78feb5e3.yaml @@ -0,0 +1,11 @@ +--- +features_transpiler: + - | + Port part of the logic from the :class:`StarPrerouting`, used to + find a star graph connectivity subcircuit and replaces it with a + linear routing equivalent. + - | + The function :func:`star_preroute` now performs the heavily lifting + to transform the dag by in the rust space by taking advantage + of the functions :func:`_build_sabre_dag` and + :func:`_apply_sabre_result`. diff --git a/releasenotes/notes/sabre_level0-1524f01965257f3f.yaml b/releasenotes/notes/sabre_level0-1524f01965257f3f.yaml new file mode 100644 index 000000000000..c24775d6d096 --- /dev/null +++ b/releasenotes/notes/sabre_level0-1524f01965257f3f.yaml @@ -0,0 +1,13 @@ +--- +upgrade_transpiler: + - | + The default routing pass used by optimization level 0 for :func:`.generate_preset_pass_manager` + and :func:`.transpile` has been changed from :class:`.StochasticSwap` to :class:`.SabreSwap`. + The :class:`.SabreSwap` pass performs exactly the same function but performs better in both + runtime and output quality (in number of swap gates and depth) compared to + :class:`.StochasticSwap`. For ``optimization_level=0`` this shouldn't matter because it's not + expected to run routing for the typical use case of level 0. + + If you were relying on the previous default routing algorithm for any reason you can use the + ``routing_method`` argument for :func:`.transpile` and :func:`.generate_preset_pass_manager` + to ``"stochastic"`` to use the :class:`.StochasticSwap` pass. diff --git a/releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml b/releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml index 4857bb1bda12..0dabc3b48253 100644 --- a/releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml +++ b/releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml @@ -12,3 +12,9 @@ upgrade_transpiler: If a `backend` input of type :class:`.BackendV1` is provided, it will be converted to :class:`.BackendV2` to expose its :class:`.Target`. This change does not require any user action. + +fixes: + - | + A series of input-handling inconsistencies between :func:`.transpile` and :func:`.generate_preset_pass_manager` + have been fixed. These inconsistencies would lead to different transpilation outputs for the same inputs, + or :func:`.generate_preset_pass_manager` failing for certain input combinations accepted by :func:`.transpile`. \ No newline at end of file diff --git a/test/benchmarks/assembler.py b/test/benchmarks/assembler.py index 3899c05e92bb..03d349088c86 100644 --- a/test/benchmarks/assembler.py +++ b/test/benchmarks/assembler.py @@ -46,4 +46,6 @@ def setup(self, n_qubits, depth, number_of_circuits): self.qobj = assemble(self.circuits) def time_disassemble_circuit(self, _, __, ___): + # TODO: QObj is getting deprecated. Remove once that happens + # https://github.com/Qiskit/qiskit/pull/12649 disassemble(self.qobj) diff --git a/test/python/circuit/library/test_qft.py b/test/python/circuit/library/test_qft.py index 1f5c9715dd7a..85837f0ac80c 100644 --- a/test/python/circuit/library/test_qft.py +++ b/test/python/circuit/library/test_qft.py @@ -139,7 +139,7 @@ def test_qft_num_gates(self, num_qubits, approximation_degree, insert_barriers): qft = QFT( num_qubits, approximation_degree=approximation_degree, insert_barriers=insert_barriers ) - ops = transpile(qft, basis_gates=basis_gates).count_ops() + ops = transpile(qft, basis_gates=basis_gates, optimization_level=1).count_ops() with self.subTest(msg="assert H count"): self.assertEqual(ops["h"], num_qubits) diff --git a/test/python/circuit/test_annotated_operation.py b/test/python/circuit/test_annotated_operation.py index f4228fc0485f..e2ca9f4af9a4 100644 --- a/test/python/circuit/test_annotated_operation.py +++ b/test/python/circuit/test_annotated_operation.py @@ -14,6 +14,7 @@ import unittest +from qiskit.circuit import Parameter, QuantumCircuit from qiskit.circuit._utils import _compute_control_matrix from qiskit.circuit.annotated_operation import ( AnnotatedOperation, @@ -22,7 +23,7 @@ PowerModifier, _canonicalize_modifiers, ) -from qiskit.circuit.library import SGate, SdgGate +from qiskit.circuit.library import SGate, SdgGate, UGate, RXGate from qiskit.quantum_info import Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -157,6 +158,52 @@ def test_canonicalize_inverse(self): expected_list = [] self.assertEqual(canonical_list, expected_list) + def test_params_access(self): + """Test access to the params field.""" + p, q = Parameter("p"), Parameter("q") + params = [0.2, -1, p] + gate = UGate(*params) + annotated = gate.control(10, annotated=True) + + with self.subTest(msg="reading params"): + self.assertListEqual(annotated.params, params) + + new_params = [q, 131, -1.2] + with self.subTest(msg="setting params"): + annotated.params = new_params + self.assertListEqual(annotated.params, new_params) + + def test_binding_annotated_gate(self): + """Test binding an annotated gate in a circuit.""" + p = Parameter("p") + annotated = RXGate(p).control(2, annotated=True) + circuit = QuantumCircuit(annotated.num_qubits) + circuit.h(circuit.qubits) + circuit.append(annotated, circuit.qubits) + + with self.subTest(msg="test parameter is reported"): + self.assertEqual(circuit.num_parameters, 1) + + with self.subTest(msg="test binding parameters worked"): + bound = circuit.assign_parameters([0.321]) + self.assertEqual(bound.num_parameters, 0) + + def test_invalid_params_access(self): + """Test params access to a operation not providing params.""" + op = Operator(SGate()) + annotated = AnnotatedOperation(op, InverseModifier()) + + with self.subTest(msg="accessing params returns an empty list"): + self.assertEqual(len(annotated.params), 0) + + with self.subTest(msg="setting params fails"): + with self.assertRaises(AttributeError): + annotated.params = [1.2] + + with self.subTest(msg="validating params fails"): + with self.assertRaises(AttributeError): + _ = annotated.validate_parameter(1.2) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index 6d7b237915fa..707f9d32cb94 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -19,7 +19,7 @@ from numpy import pi from ddt import ddt, data, unpack -from qiskit import QuantumRegister, QuantumCircuit, QiskitError +from qiskit import QuantumRegister, QuantumCircuit, QiskitError, transpile from qiskit.circuit import ControlledGate, Parameter, Gate from qiskit.circuit.annotated_operation import AnnotatedOperation from qiskit.circuit.singleton import SingletonControlledGate, _SingletonControlledGateOverrides @@ -46,9 +46,13 @@ CCXGate, HGate, RZGate, + RYGate, RXGate, + RZZGate, + RZXGate, + RYYGate, + RXXGate, CPhaseGate, - RYGate, CRYGate, CRXGate, CSwapGate, @@ -73,6 +77,8 @@ C3SXGate, C4XGate, MCPhaseGate, + XXMinusYYGate, + XXPlusYYGate, GlobalPhaseGate, UnitaryGate, ) @@ -761,7 +767,6 @@ def test_small_mcx_gates_yield_cx_count(self, num_ctrl_qubits): yields the expected number of cx gates.""" qc = QuantumCircuit(num_ctrl_qubits + 1) qc.append(MCXGate(num_ctrl_qubits), range(num_ctrl_qubits + 1)) - from qiskit import transpile cqc = transpile(qc, basis_gates=["u", "cx"]) cx_count = cqc.count_ops()["cx"] @@ -808,8 +813,6 @@ def test_mcx_gates(self, num_ctrl_qubits): def test_mcxvchain_dirty_ancilla_cx_count(self, num_ctrl_qubits): """Test if cx count of the v-chain mcx with dirty ancilla is less than upper bound.""" - from qiskit import transpile - mcx_vchain = MCXVChain(num_ctrl_qubits, dirty_ancillas=True) qc = QuantumCircuit(mcx_vchain.num_qubits) @@ -824,8 +827,6 @@ def test_mcxvchain_dirty_ancilla_cx_count(self, num_ctrl_qubits): def test_mcxrecursive_clean_ancilla_cx_count(self, num_ctrl_qubits): """Test if cx count of the mcx with one clean ancilla is less than upper bound.""" - from qiskit import transpile - mcx_recursive = MCXRecursive(num_ctrl_qubits) qc = QuantumCircuit(mcx_recursive.num_qubits) @@ -1491,6 +1492,45 @@ def test_control_zero_operand_gate(self, num_ctrl_qubits): target.flat[-1] = -1 self.assertEqual(Operator(controlled), Operator(target)) + @data( + RXGate, + RYGate, + RZGate, + RXXGate, + RYYGate, + RZXGate, + RZZGate, + UGate, + U3Gate, + XXMinusYYGate, + XXPlusYYGate, + ) + def test_mc_failure_without_annotation(self, gate_cls): + """Test error for gates that cannot be multi-controlled without annotation.""" + theta = Parameter("theta") + num_params = len(_get_free_params(gate_cls.__init__, ignore=["self"])) + params = [theta] + (num_params - 1) * [1.234] + + for annotated in [False, None]: + with self.subTest(annotated=annotated): + # if annotated is False, check that a sensible error is raised + if annotated is False: + with self.assertRaisesRegex(QiskitError, "unbound parameter"): + _ = gate_cls(*params).control(5, annotated=False) + + # else, check that the gate can be synthesized after all parameters + # have been bound + else: + mc_gate = gate_cls(*params).control(5) + + circuit = QuantumCircuit(mc_gate.num_qubits) + circuit.append(mc_gate, circuit.qubits) + + bound = circuit.assign_parameters([0.5123]) + unrolled = transpile(bound, basis_gates=["u", "cx"], optimization_level=0) + + self.assertEqual(unrolled.num_parameters, 0) + def assertEqualTranslated(self, circuit, unrolled_reference, basis): """Assert that the circuit is equal to the unrolled reference circuit.""" unroller = UnrollCustomDefinitions(std_eqlib, basis) diff --git a/test/python/circuit/test_diagonal_gate.py b/test/python/circuit/test_diagonal_gate.py index 57a5d5966c7e..de8d53e12d3b 100644 --- a/test/python/circuit/test_diagonal_gate.py +++ b/test/python/circuit/test_diagonal_gate.py @@ -75,11 +75,13 @@ def test_npcomplex_params_conversion(self): all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) ) - qobj = assemble(qc) - params = qobj.experiments[0].instructions[0].params - self.assertTrue( - all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) - ) + with self.assertWarns(DeprecationWarning): + # REMOVE this assertion (not the full test) once ASSEMBLE is removed. + qobj = assemble(qc) + params = qobj.experiments[0].instructions[0].params + self.assertTrue( + all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) + ) def _get_diag_gate_matrix(diag): diff --git a/test/python/circuit/test_hamiltonian_gate.py b/test/python/circuit/test_hamiltonian_gate.py index 46aee007b158..60fb1e9a90a9 100644 --- a/test/python/circuit/test_hamiltonian_gate.py +++ b/test/python/circuit/test_hamiltonian_gate.py @@ -131,7 +131,8 @@ def test_3q_hamiltonian(self): np.testing.assert_almost_equal(dnode.op.to_matrix(), 1j * matrix.data) def test_qobj_with_hamiltonian(self): - """test qobj output with hamiltonian""" + """test qobj output with hamiltonian + REMOVE once Qobj gets removed""" qr = QuantumRegister(4) qc = QuantumCircuit(qr) qc.rx(np.pi / 4, qr[0]) @@ -141,7 +142,8 @@ def test_qobj_with_hamiltonian(self): qc.append(uni, [qr[0], qr[1], qr[3]]) qc.cx(qr[3], qr[2]) qc = qc.assign_parameters({theta: np.pi / 2}) - qobj = qiskit.compiler.assemble(qc) + with self.assertWarns(DeprecationWarning): + qobj = qiskit.compiler.assemble(qc) instr = qobj.experiments[0].instructions[1] self.assertEqual(instr.name, "hamiltonian") # Also test label diff --git a/test/python/circuit/test_initializer.py b/test/python/circuit/test_initializer.py index 122a4e4afe9a..a37c51f48184 100644 --- a/test/python/circuit/test_initializer.py +++ b/test/python/circuit/test_initializer.py @@ -481,7 +481,8 @@ def test_init(self): all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) ) - qobj = assemble(qc) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) params = qobj.experiments[0].instructions[0].params self.assertTrue( all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index bfe63b7955c0..aca58d8f7b27 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -31,10 +31,11 @@ from qiskit.compiler import assemble, transpile from qiskit import pulse from qiskit.quantum_info import Operator -from qiskit.providers.fake_provider import Fake5QV1 +from qiskit.providers.fake_provider import Fake5QV1, GenericBackendV2 from qiskit.providers.basic_provider import BasicSimulator from qiskit.utils import parallel_map from test import QiskitTestCase, combine # pylint: disable=wrong-import-order +from ..legacy_cmaps import BOGOTA_CMAP def raise_if_parameter_table_invalid(circuit): @@ -794,7 +795,8 @@ def test_circuit_generation(self): theta_list = numpy.linspace(0, numpy.pi, 20) for theta_i in theta_list: circs.append(qc_aer.assign_parameters({theta: theta_i})) - qobj = assemble(circs) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circs) for index, theta_i in enumerate(theta_list): res = float(qobj.experiments[index].instructions[0].params[0]) self.assertTrue(math.isclose(res, theta_i), f"{res} != {theta_i}") @@ -981,8 +983,9 @@ def test_parameter_equality_to_expression(self, ptype): self.assertEqual(hash(x1), hash(x1_expr)) self.assertEqual(hash(x2), hash(x2_expr)) - def test_binding_parameterized_circuits_built_in_multiproc(self): - """Verify subcircuits built in a subprocess can still be bound.""" + def test_binding_parameterized_circuits_built_in_multiproc_(self): + """Verify subcircuits built in a subprocess can still be bound. + REMOVE this test once assemble is REMOVED""" # ref: https://github.com/Qiskit/qiskit-terra/issues/2429 num_processes = 4 @@ -1002,11 +1005,12 @@ def test_binding_parameterized_circuits_built_in_multiproc(self): parameter_values = [{x: 1.0 for x in parameters}] - qobj = assemble( - circuit, - backend=BasicSimulator(), - parameter_binds=parameter_values, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + circuit, + backend=BasicSimulator(), + parameter_binds=parameter_values, + ) self.assertEqual(len(qobj.experiments), 1) self.assertEqual(len(qobj.experiments[0].instructions), 4) @@ -1019,6 +1023,39 @@ def test_binding_parameterized_circuits_built_in_multiproc(self): ) ) + def test_binding_parameterized_circuits_built_in_multiproc(self): + """Verify subcircuits built in a subprocess can still be bound.""" + # ref: https://github.com/Qiskit/qiskit-terra/issues/2429 + + num_processes = 4 + + qr = QuantumRegister(3) + cr = ClassicalRegister(3) + + circuit = QuantumCircuit(qr, cr) + parameters = [Parameter(f"x{i}") for i in range(num_processes)] + + results = parallel_map( + _construct_circuit, parameters, task_args=(qr,), num_processes=num_processes + ) + + for qc in results: + circuit.compose(qc, inplace=True) + + parameter_values = {x: 1.0 for x in parameters} + + bind_circuit = circuit.assign_parameters(parameter_values) + + self.assertEqual(len(bind_circuit.data), 4) + self.assertTrue( + all( + len(inst.operation.params) == 1 + and isinstance(inst.operation.params[0], float) + and float(inst.operation.params[0]) == 1 + for inst in bind_circuit.data + ) + ) + def test_transpiling_multiple_parameterized_circuits(self): """Verify several parameterized circuits can be transpiled at once.""" # ref: https://github.com/Qiskit/qiskit-terra/issues/2864 @@ -1039,6 +1076,26 @@ def test_transpiling_multiple_parameterized_circuits(self): self.assertTrue(len(job.result().results), 2) + @data(0, 1, 2, 3) + def test_transpile_across_optimization_levelsV1(self, opt_level): + """Verify parameterized circuits can be transpiled with all default pass managers. + To remove once Fake5QV1 gets removed""" + + qc = QuantumCircuit(5, 5) + + theta = Parameter("theta") + phi = Parameter("phi") + + qc.rx(theta, 0) + qc.x(0) + for i in range(5 - 1): + qc.rxx(phi, i, i + 1) + + qc.measure(range(5 - 1), range(5 - 1)) + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() + transpile(qc, backend, optimization_level=opt_level) + @data(0, 1, 2, 3) def test_transpile_across_optimization_levels(self, opt_level): """Verify parameterized circuits can be transpiled with all default pass managers.""" @@ -1055,7 +1112,15 @@ def test_transpile_across_optimization_levels(self, opt_level): qc.measure(range(5 - 1), range(5 - 1)) - transpile(qc, Fake5QV1(), optimization_level=opt_level) + transpile( + qc, + GenericBackendV2( + num_qubits=5, + coupling_map=BOGOTA_CMAP, + seed=42, + ), + optimization_level=opt_level, + ) def test_repeated_gates_to_dag_and_back(self): """Verify circuits with repeated parameterized gates can be converted diff --git a/test/python/circuit/test_scheduled_circuit.py b/test/python/circuit/test_scheduled_circuit.py index ec4bc72ffd06..c934b5b5e04c 100644 --- a/test/python/circuit/test_scheduled_circuit.py +++ b/test/python/circuit/test_scheduled_circuit.py @@ -33,8 +33,9 @@ class TestScheduledCircuit(QiskitTestCase): def setUp(self): super().setUp() - self.backend_with_dt = Fake27QPulseV1() - self.backend_without_dt = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + self.backend_with_dt = Fake27QPulseV1() + self.backend_without_dt = Fake27QPulseV1() delattr(self.backend_without_dt.configuration(), "dt") # Remove timing constraints from the backends (alignment values, # granularity and min_length), so that these values will default @@ -50,27 +51,25 @@ def setUp(self): def test_schedule_circuit_when_backend_tells_dt(self): """dt is known to transpiler by backend""" qc = QuantumCircuit(2) - qc.delay(0.1, 0, unit="ms") # 450000[dt] + qc.delay(0.1, 0, unit="ms") # 450450[dt] qc.delay(100, 0, unit="ns") # 450[dt] - qc.h(0) # 160[dt] - qc.h(1) # 160[dt] - sc = transpile(qc, self.backend_with_dt, scheduling_method="alap", layout_method="trivial") - self.assertEqual(sc.duration, 450546) + qc.h(0) # 195[dt] + qc.h(1) # 210[dt] + + backend = GenericBackendV2(2, calibrate_instructions=True, seed=42) + + sc = transpile(qc, backend, scheduling_method="alap", layout_method="trivial") + self.assertEqual(sc.duration, 451095) self.assertEqual(sc.unit, "dt") self.assertEqual(sc.data[0].operation.name, "delay") - self.assertEqual(sc.data[0].operation.duration, 450450) + self.assertEqual(sc.data[0].operation.duration, 450900) self.assertEqual(sc.data[0].operation.unit, "dt") self.assertEqual(sc.data[1].operation.name, "rz") self.assertEqual(sc.data[1].operation.duration, 0) self.assertEqual(sc.data[1].operation.unit, "dt") self.assertEqual(sc.data[4].operation.name, "delay") - self.assertEqual(sc.data[4].operation.duration, 450450) + self.assertEqual(sc.data[4].operation.duration, 450885) self.assertEqual(sc.data[4].operation.unit, "dt") - qobj = assemble(sc, self.backend_with_dt) - self.assertEqual(qobj.experiments[0].instructions[0].name, "delay") - self.assertEqual(qobj.experiments[0].instructions[0].params[0], 450450) - self.assertEqual(qobj.experiments[0].instructions[4].name, "delay") - self.assertEqual(qobj.experiments[0].instructions[4].params[0], 450450) def test_schedule_circuit_when_transpile_option_tells_dt(self): """dt is known to transpiler by transpile option""" @@ -119,7 +118,7 @@ def test_schedule_circuit_in_sec_when_no_one_tells_dt(self): self.assertEqual(sc.data[4].operation.name, "delay") self.assertAlmostEqual(sc.data[4].operation.duration, 1.0e-4 + 1.0e-7) self.assertEqual(sc.data[4].operation.unit, "s") - with self.assertRaises(QiskitError): + with self.assertRaises(DeprecationWarning): assemble(sc, self.backend_without_dt) def test_cannot_schedule_circuit_with_mixed_SI_and_dt_when_no_one_tells_dt(self): @@ -306,7 +305,7 @@ 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.""" - backend = GenericBackendV2(num_qubits=3, calibrate_instructions=True, seed=10) + backend = GenericBackendV2(num_qubits=3, calibrate_instructions=True, seed=42) schedule_config = ScheduleConfig( inst_map=backend.target.instruction_schedule_map(), meas_map=backend.meas_map, @@ -377,7 +376,8 @@ def test_transpile_and_assemble_delay_circuit_for_simulator(self): qc.delay(100, 0, "ns") circ = transpile(qc, self.simulator_backend) self.assertEqual(circ.duration, None) # not scheduled - qobj = assemble(circ, self.simulator_backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ, self.simulator_backend) self.assertEqual(qobj.experiments[0].instructions[0].name, "delay") self.assertEqual(qobj.experiments[0].instructions[0].params[0], 1e-7) @@ -389,7 +389,8 @@ def test_transpile_and_assemble_t1_circuit_for_simulator(self): qc.measure(0, 0) circ = transpile(qc, self.simulator_backend) self.assertEqual(circ.duration, None) # not scheduled - qobj = assemble(circ, self.simulator_backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ, self.simulator_backend) self.assertEqual(qobj.experiments[0].instructions[1].name, "delay") self.assertAlmostEqual(qobj.experiments[0].instructions[1].params[0], 1e-7) @@ -415,7 +416,8 @@ def test_can_transpile_and_assemble_circuits_with_assigning_parameters_inbetween qc.measure(0, 0) circ = transpile(qc, self.backend_with_dt) circ = circ.assign_parameters({idle_dur: 0.1}) - qobj = assemble(circ, self.backend_with_dt) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ, self.backend_with_dt) self.assertEqual(qobj.experiments[0].instructions[1].name, "delay") self.assertEqual(qobj.experiments[0].instructions[1].params[0], 450) @@ -440,7 +442,7 @@ def test_fail_to_assemble_circuits_with_unbounded_parameters(self): qc.delay(idle_dur, 0, "us") qc.measure(0, 0) qc = transpile(qc, self.backend_with_dt) - with self.assertRaises(QiskitError): + with self.assertRaises(DeprecationWarning): assemble(qc, self.backend_with_dt) @data("asap", "alap") diff --git a/test/python/circuit/test_unitary.py b/test/python/circuit/test_unitary.py index c5c9344ad7ed..63edbdaffbf9 100644 --- a/test/python/circuit/test_unitary.py +++ b/test/python/circuit/test_unitary.py @@ -168,7 +168,8 @@ def test_qobj_with_unitary_matrix(self): uni = UnitaryGate(matrix) qc.append(uni, [qr[0], qr[1], qr[3]]) qc.cx(qr[3], qr[2]) - qobj = qiskit.compiler.assemble(qc) + with self.assertWarns(DeprecationWarning): + qobj = qiskit.compiler.assemble(qc) instr = qobj.experiments[0].instructions[1] self.assertEqual(instr.name, "unitary") assert_allclose(numpy.array(instr.params[0]).astype(numpy.complex64), matrix) @@ -197,7 +198,8 @@ def test_labeled_unitary(self): matrix = numpy.kron(sigmax, sigmay) uni = UnitaryGate(matrix, label="xy") qc.append(uni, [qr[0], qr[1]]) - qobj = qiskit.compiler.assemble(qc) + with self.assertWarns(DeprecationWarning): + qobj = qiskit.compiler.assemble(qc) instr = qobj.experiments[0].instructions[0] self.assertEqual(instr.name, "unitary") self.assertEqual(instr.label, "xy") diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py index 1a6e5b6b8fe5..c333ce9ace22 100644 --- a/test/python/compiler/test_assembler.py +++ b/test/python/compiler/test_assembler.py @@ -63,7 +63,8 @@ def setUp(self): self.circ.cx(qr[0], qr[1]) self.circ.measure(qr, cr) - self.backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + self.backend = Fake5QV1() self.backend_config = self.backend.configuration() self.num_qubits = self.backend_config.n_qubits @@ -80,7 +81,8 @@ def setUp(self): def test_assemble_single_circuit(self): """Test assembling a single circuit.""" - qobj = assemble(self.circ, shots=2000, memory=True) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, shots=2000, memory=True) self.assertIsInstance(qobj, QasmQobj) self.assertEqual(qobj.config.shots, 2000) @@ -105,7 +107,8 @@ def test_assemble_multiple_circuits(self): circ1.cx(qr1[0], qr1[2]) circ1.measure(qr1, qc1) - qobj = assemble([circ0, circ1], shots=100, memory=False, seed_simulator=6) + with self.assertWarns(DeprecationWarning): + qobj = assemble([circ0, circ1], shots=100, memory=False, seed_simulator=6) self.assertIsInstance(qobj, QasmQobj) self.assertEqual(qobj.config.seed_simulator, 6) @@ -116,29 +119,34 @@ def test_assemble_multiple_circuits(self): def test_assemble_no_run_config(self): """Test assembling with no run_config, relying on default.""" - qobj = assemble(self.circ) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ) self.assertIsInstance(qobj, QasmQobj) self.assertEqual(qobj.config.shots, 1024) def test_shots_greater_than_max_shots(self): """Test assembling with shots greater than max shots""" - self.assertRaises(QiskitError, assemble, self.backend, shots=1024000) + with self.assertWarns(DeprecationWarning): + self.assertRaises(QiskitError, assemble, self.backend, shots=1024000) def test_shots_not_of_type_int(self): """Test assembling with shots having type other than int""" - self.assertRaises(QiskitError, assemble, self.backend, shots="1024") + with self.assertWarns(DeprecationWarning): + self.assertRaises(QiskitError, assemble, self.backend, shots="1024") def test_shots_of_type_numpy_int64(self): """Test assembling with shots having type numpy.int64""" - qobj = assemble(self.circ, shots=np.int64(2048)) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, shots=np.int64(2048)) self.assertEqual(qobj.config.shots, 2048) def test_default_shots_greater_than_max_shots(self): """Test assembling with default shots greater than max shots""" self.backend_config.max_shots = 5 - qobj = assemble(self.circ, self.backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, self.backend) self.assertIsInstance(qobj, QasmQobj) self.assertEqual(qobj.config.shots, 5) @@ -149,7 +157,8 @@ def test_assemble_initialize(self): circ = QuantumCircuit(q, name="circ") circ.initialize([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)], q[:]) - qobj = assemble(circ) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ) self.assertIsInstance(qobj, QasmQobj) self.assertEqual(qobj.experiments[0].instructions[0].name, "initialize") @@ -159,14 +168,16 @@ def test_assemble_initialize(self): def test_assemble_meas_level_meas_return(self): """Test assembling a circuit schedule with `meas_level`.""" - qobj = assemble(self.circ, meas_level=1, meas_return="single") + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, meas_level=1, meas_return="single") self.assertIsInstance(qobj, QasmQobj) self.assertEqual(qobj.config.meas_level, 1) self.assertEqual(qobj.config.meas_return, "single") # no meas_level set - qobj = assemble(self.circ) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ) self.assertIsInstance(qobj, QasmQobj) self.assertEqual(qobj.config.meas_level, 2) @@ -181,12 +192,14 @@ def test_assemble_backend_rep_delays(self): # dynamic rep rates off setattr(self.backend_config, "dynamic_reprate_enabled", False) - qobj = assemble(self.circ, self.backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, self.backend) self.assertEqual(hasattr(qobj.config, "rep_delay"), False) # dynamic rep rates on setattr(self.backend_config, "dynamic_reprate_enabled", True) - qobj = assemble(self.circ, self.backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, self.backend) self.assertEqual(qobj.config.rep_delay, default_rep_delay * 1e6) def test_assemble_user_rep_time_delay(self): @@ -198,22 +211,26 @@ def test_assemble_user_rep_time_delay(self): # dynamic rep rates off (no default so shouldn't be in qobj config) setattr(self.backend_config, "dynamic_reprate_enabled", False) - qobj = assemble(self.circ, self.backend, rep_delay=rep_delay) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, self.backend, rep_delay=rep_delay) self.assertEqual(hasattr(qobj.config, "rep_delay"), False) # turn on dynamic rep rates, rep_delay should be set setattr(self.backend_config, "dynamic_reprate_enabled", True) - qobj = assemble(self.circ, self.backend, rep_delay=rep_delay) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, self.backend, rep_delay=rep_delay) self.assertEqual(qobj.config.rep_delay, 2.2) # test ``rep_delay=0`` - qobj = assemble(self.circ, self.backend, rep_delay=0) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, self.backend, rep_delay=0) self.assertEqual(qobj.config.rep_delay, 0) # use ``rep_delay`` outside of ``rep_delay_range``` rep_delay_large = 5.0e-6 - with self.assertRaises(QiskitError): - assemble(self.circ, self.backend, rep_delay=rep_delay_large) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + assemble(self.circ, self.backend, rep_delay=rep_delay_large) def test_assemble_opaque_inst(self): """Test opaque instruction is assembled as-is""" @@ -223,7 +240,8 @@ def test_assemble_opaque_inst(self): circ = QuantumCircuit(q, c, name="circ") circ.append(opaque_inst, [q[0], q[2], q[5], q[3]], [c[3], c[0]]) - qobj = assemble(circ) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ) self.assertIsInstance(qobj, QasmQobj) self.assertEqual(len(qobj.experiments[0].instructions), 1) @@ -245,7 +263,8 @@ def test_assemble_unroll_parametervector(self): qc.assign_parameters({pv1: [0.1, 0.2, 0.3], pv2: [0.4, 0.5, 0.6]}) - qobj = assemble(qc, parameter_binds=[{pv1: [0.1, 0.2, 0.3], pv2: [0.4, 0.5, 0.6]}]) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc, parameter_binds=[{pv1: [0.1, 0.2, 0.3], pv2: [0.4, 0.5, 0.6]}]) self.assertIsInstance(qobj, QasmQobj) self.assertEqual(qobj.experiments[0].instructions[0].params[0], 0.100000000000000) @@ -267,7 +286,8 @@ def test_measure_to_registers_when_conditionals(self): qc.measure(qr[1], cr2[1]) # Measure required for a later conditional qc.h(qr[1]).c_if(cr2, 3) - qobj = assemble(qc) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) first_measure, second_measure = ( op for op in qobj.experiments[0].instructions if op.name == "measure" @@ -286,7 +306,8 @@ def test_convert_to_bfunc_plus_conditional(self): qc.h(qr[0]).c_if(cr, 1) - qobj = assemble(qc) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) bfunc_op, h_op = qobj.experiments[0].instructions @@ -306,7 +327,8 @@ def test_convert_to_bfunc_plus_conditional_onebit(self): qc.h(qr[0]).c_if(cr[2], 1) - qobj = assemble(qc) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) inst_set = qobj.experiments[0].instructions [bfunc_op, h_op] = inst_set @@ -331,7 +353,8 @@ def test_resize_value_to_register(self): qc.h(qr[0]).c_if(cr2, 2) - qobj = assemble(qc) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) bfunc_op, h_op = qobj.experiments[0].instructions @@ -363,23 +386,24 @@ def test_assemble_circuits_raises_for_bind_circuit_mismatch(self): full_bind_args = {"parameter_binds": [{x: 1, y: 1}, {x: 0, y: 0}]} inconsistent_bind_args = {"parameter_binds": [{x: 1}, {x: 0, y: 0}]} - # Raise when parameters passed for non-parametric circuit - self.assertRaises(QiskitError, assemble, full_bound_circ, **partial_bind_args) + with self.assertWarns(DeprecationWarning): + # Raise when parameters passed for non-parametric circuit + self.assertRaises(QiskitError, assemble, full_bound_circ, **partial_bind_args) - # Raise when no parameters passed for parametric circuit - self.assertRaises(QiskitError, assemble, partial_param_circ) - self.assertRaises(QiskitError, assemble, full_param_circ) + # Raise when no parameters passed for parametric circuit + self.assertRaises(QiskitError, assemble, partial_param_circ) + self.assertRaises(QiskitError, assemble, full_param_circ) - # Raise when circuit has more parameters than run_config - self.assertRaises(QiskitError, assemble, full_param_circ, **partial_bind_args) + # Raise when circuit has more parameters than run_config + self.assertRaises(QiskitError, assemble, full_param_circ, **partial_bind_args) - # Raise when not all circuits have all parameters - self.assertRaises( - QiskitError, assemble, [full_param_circ, partial_param_circ], **full_bind_args - ) + # Raise when not all circuits have all parameters + self.assertRaises( + QiskitError, assemble, [full_param_circ, partial_param_circ], **full_bind_args + ) - # Raise when not all binds have all circuit params - self.assertRaises(QiskitError, assemble, full_param_circ, **inconsistent_bind_args) + # Raise when not all binds have all circuit params + self.assertRaises(QiskitError, assemble, full_param_circ, **inconsistent_bind_args) def test_assemble_circuits_rases_for_bind_mismatch_over_expressions(self): """Verify assemble_circuits raises for invalid binds for circuit including @@ -395,11 +419,13 @@ def test_assemble_circuits_rases_for_bind_mismatch_over_expressions(self): partial_bind_args = {"parameter_binds": [{x: 1}, {x: 0}]} - # Raise when no parameters passed for parametric circuit - self.assertRaises(QiskitError, assemble, expr_circ) + with self.assertWarns(DeprecationWarning): + # Raise when no parameters passed for parametric circuit + self.assertRaises(QiskitError, assemble, expr_circ) - # Raise when circuit has more parameters than run_config - self.assertRaises(QiskitError, assemble, expr_circ, **partial_bind_args) + with self.assertWarns(DeprecationWarning): + # Raise when circuit has more parameters than run_config + self.assertRaises(QiskitError, assemble, expr_circ, **partial_bind_args) def test_assemble_circuits_binds_parameters(self): """Verify assemble_circuits applies parameter bindings and output circuits are bound.""" @@ -422,7 +448,8 @@ def test_assemble_circuits_binds_parameters(self): bind_args = {"parameter_binds": [{x: 0, y: 0}, {x: 1, y: 0}, {x: 1, y: 1}]} - qobj = assemble([qc1, qc2, qc3], **bind_args) + with self.assertWarns(DeprecationWarning): + qobj = assemble([qc1, qc2, qc3], **bind_args) self.assertEqual(len(qobj.experiments), 9) self.assertEqual( @@ -451,17 +478,20 @@ def _qobj_inst_params(expt_no, inst_no): def test_init_qubits_default(self): """Check that the init_qubits=None assemble option is passed on to the qobj.""" - qobj = assemble(self.circ) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ) self.assertEqual(qobj.config.init_qubits, True) def test_init_qubits_true(self): """Check that the init_qubits=True assemble option is passed on to the qobj.""" - qobj = assemble(self.circ, init_qubits=True) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, init_qubits=True) self.assertEqual(qobj.config.init_qubits, True) def test_init_qubits_false(self): """Check that the init_qubits=False assemble option is passed on to the qobj.""" - qobj = assemble(self.circ, init_qubits=False) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, init_qubits=False) self.assertEqual(qobj.config.init_qubits, False) def test_circuit_with_global_phase(self): @@ -471,7 +501,8 @@ def test_circuit_with_global_phase(self): circ.cx(0, 1) circ.measure_all() circ.global_phase = 0.3 * np.pi - qobj = assemble([circ, self.circ]) + with self.assertWarns(DeprecationWarning): + qobj = assemble([circ, self.circ]) self.assertEqual(getattr(qobj.experiments[1].header, "global_phase"), 0) self.assertEqual(getattr(qobj.experiments[0].header, "global_phase"), 0.3 * np.pi) @@ -493,10 +524,12 @@ def _define(self): gate = TestGate() circ = QuantumCircuit(1) circ.append(gate, [0]) - qobj = assemble([circ]) + with self.assertWarns(DeprecationWarning): + qobj = assemble([circ]) self.assertEqual(getattr(qobj.experiments[0].header, "global_phase"), 0) circ.global_phase = np.pi / 2 - qobj = assemble([circ]) + with self.assertWarns(DeprecationWarning): + qobj = assemble([circ]) self.assertEqual(getattr(qobj.experiments[0].header, "global_phase"), np.pi / 2) def test_pulse_gates_single_circ(self): @@ -518,7 +551,8 @@ def test_pulse_gates_single_circ(self): circ.add_calibration(RxGate(3.14), [0], x180) circ.add_calibration(RxGate(3.14), [1], x180) - qobj = assemble(circ, FakeOpenPulse2Q()) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ, FakeOpenPulse2Q()) # Only one circuit, so everything is stored at the job level cals = qobj.config.calibrations lib = qobj.config.pulse_library @@ -539,7 +573,8 @@ def test_custom_pulse_gates_single_circ(self): circ.add_calibration("h", [0], custom_h_schedule) - qobj = assemble(circ, FakeOpenPulse2Q()) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ, FakeOpenPulse2Q()) lib = qobj.config.pulse_library self.assertEqual(len(lib), 1) np.testing.assert_almost_equal( @@ -557,9 +592,11 @@ def test_pulse_gates_with_parameteric_pulses(self): circ.h(0) circ.add_calibration("h", [0], custom_h_schedule) - backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() backend.configuration().parametric_pulses = ["drag"] - qobj = assemble(circ, backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ, backend) self.assertFalse(hasattr(qobj.config, "pulse_library")) self.assertTrue(hasattr(qobj.config, "calibrations")) @@ -577,7 +614,8 @@ def test_pulse_gates_multiple_circuits(self): circ2 = QuantumCircuit(2) circ2.h(0) - qobj = assemble([circ, circ2], FakeOpenPulse2Q()) + with self.assertWarns(DeprecationWarning): + qobj = assemble([circ, circ2], FakeOpenPulse2Q()) self.assertEqual(len(qobj.config.pulse_library), 1) self.assertEqual(len(qobj.experiments[0].config.calibrations.gates), 2) self.assertFalse(hasattr(qobj.config, "calibrations")) @@ -598,7 +636,8 @@ def test_pulse_gates_common_cals(self): circ2.h(0) circ2.add_calibration(RxGate(3.14), [1], dummy_sched) - qobj = assemble([circ, circ2], FakeOpenPulse2Q()) + with self.assertWarns(DeprecationWarning): + qobj = assemble([circ, circ2], FakeOpenPulse2Q()) # Identical pulses are only added once self.assertEqual(len(qobj.config.pulse_library), 1) # Identical calibrations are only added once @@ -611,7 +650,8 @@ def test_pulse_gates_common_cals(self): def test_assemble_adds_circuit_metadata_to_experiment_header(self): """Verify that any circuit metadata is added to the experiment header.""" circ = QuantumCircuit(2, metadata={"experiment_type": "gst", "execution_number": "1234"}) - qobj = assemble(circ, shots=100, memory=False, seed_simulator=6) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ, shots=100, memory=False, seed_simulator=6) self.assertEqual( qobj.experiments[0].header.metadata, {"experiment_type": "gst", "execution_number": "1234"}, @@ -623,7 +663,8 @@ def test_pulse_gates_delay_only(self): circ.append(Gate("test", 1, []), [0]) test_sched = pulse.Delay(64, DriveChannel(0)) + pulse.Delay(160, DriveChannel(0)) circ.add_calibration("test", [0], test_sched) - qobj = assemble(circ, FakeOpenPulse2Q()) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ, FakeOpenPulse2Q()) self.assertEqual(len(qobj.config.calibrations.gates[0].instructions), 2) self.assertEqual( qobj.config.calibrations.gates[0].instructions[1].to_dict(), @@ -633,12 +674,13 @@ def test_pulse_gates_delay_only(self): def test_job_qubit_meas_los_no_range(self): """Test that adding job qubit/meas lo freq lists are assembled into the qobj.config, w/ out any lo range.""" - qobj = assemble( - self.circ, - backend=self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.circ, + backend=self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + ) # convert to ghz qubit_lo_freq_ghz = [freq / 1e9 for freq in self.default_qubit_lo_freq] @@ -653,73 +695,79 @@ def test_job_lo_errors(self): meas_lo_range = [[freq - 5e6, freq + 5e6] for freq in self.default_meas_lo_freq] # lo range not a nested list - with self.assertRaises(QiskitError): - assemble( - self.circ, - backend=self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - qubit_lo_range=[4.995e9 for i in range(self.num_qubits)], - meas_lo_range=meas_lo_range, - ) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + assemble( + self.circ, + backend=self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + qubit_lo_range=[4.995e9 for i in range(self.num_qubits)], + meas_lo_range=meas_lo_range, + ) # qubit lo range inner list not 2d - with self.assertRaises(QiskitError): - assemble( - self.circ, - backend=self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - qubit_lo_range=qubit_lo_range, - meas_lo_range=[[6.695e9] for i in range(self.num_qubits)], - ) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + assemble( + self.circ, + backend=self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + qubit_lo_range=qubit_lo_range, + meas_lo_range=[[6.695e9] for i in range(self.num_qubits)], + ) # meas lo range inner list not 2d - with self.assertRaises(QiskitError): - assemble( - self.circ, - backend=self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - qubit_lo_range=qubit_lo_range, - meas_lo_range=[[6.695e9] for i in range(self.num_qubits)], - ) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + assemble( + self.circ, + backend=self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + qubit_lo_range=qubit_lo_range, + meas_lo_range=[[6.695e9] for i in range(self.num_qubits)], + ) # qubit lo out of range - with self.assertRaises(QiskitError): - assemble( - self.circ, - backend=self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - qubit_lo_range=[[5.005e9, 5.010e9] for i in range(self.num_qubits)], - meas_lo_range=meas_lo_range, - ) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + assemble( + self.circ, + backend=self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + qubit_lo_range=[[5.005e9, 5.010e9] for i in range(self.num_qubits)], + meas_lo_range=meas_lo_range, + ) # meas lo out of range - with self.assertRaises(QiskitError): - assemble( - self.circ, - backend=self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - qubit_lo_range=qubit_lo_range, - meas_lo_range=[[6.705e9, 6.710e9] for i in range(self.num_qubits)], - ) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + assemble( + self.circ, + backend=self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + qubit_lo_range=qubit_lo_range, + meas_lo_range=[[6.705e9, 6.710e9] for i in range(self.num_qubits)], + ) def test_job_qubit_meas_los_w_range(self): """Test that adding job qubit/meas lo freq lists are assembled into the qobj.config, w/ lo ranges input. Verify that lo ranges do not enter into the config.""" qubit_lo_range = [[freq - 5e6, freq + 5e6] for freq in self.default_qubit_lo_freq] meas_lo_range = [[freq - 5e6, freq + 5e6] for freq in self.default_meas_lo_freq] - qobj = assemble( - self.circ, - backend=self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - qubit_lo_range=qubit_lo_range, - meas_lo_range=meas_lo_range, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.circ, + backend=self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + qubit_lo_range=qubit_lo_range, + meas_lo_range=meas_lo_range, + ) # convert to ghz qubit_lo_freq_ghz = [freq / 1e9 for freq in self.default_qubit_lo_freq] @@ -731,13 +779,14 @@ def test_job_qubit_meas_los_w_range(self): def test_assemble_single_circ_single_lo_config(self): """Test assembling a single circuit, with a single experiment level lo config.""" - qobj = assemble( - self.circ, - self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=self.user_lo_config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.circ, + self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=self.user_lo_config, + ) self.assertListEqual(qobj.config.qubit_lo_freq, [5.55, 5, 5, 4.91, 5]) self.assertListEqual(qobj.config.meas_lo_freq, [6.64, 6.7, 6.7, 6.7, 6.1]) @@ -746,13 +795,14 @@ def test_assemble_single_circ_single_lo_config(self): def test_assemble_single_circ_single_lo_config_dict(self): """Test assembling a single circuit, with a single experiment level lo config supplied as dictionary.""" - qobj = assemble( - self.circ, - self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=self.user_lo_config_dict, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.circ, + self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=self.user_lo_config_dict, + ) self.assertListEqual(qobj.config.qubit_lo_freq, [5.55, 5, 5, 4.91, 5]) self.assertListEqual(qobj.config.meas_lo_freq, [6.64, 6.7, 6.7, 6.7, 6.1]) @@ -769,13 +819,14 @@ def test_assemble_single_circ_multi_lo_config(self): pulse.MeasureChannel(3): 6.1e9, } user_lo_config2 = pulse.LoConfig(user_lo_config_dict2) - qobj = assemble( - self.circ, - self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[self.user_lo_config, user_lo_config2], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.circ, + self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[self.user_lo_config, user_lo_config2], + ) qubit_lo_freq_ghz = [freq / 1e9 for freq in self.default_qubit_lo_freq] meas_lo_freq_ghz = [freq / 1e9 for freq in self.default_meas_lo_freq] @@ -800,13 +851,14 @@ def test_assemble_multi_circ_multi_lo_config(self): pulse.MeasureChannel(3): 6.1e9, } user_lo_config2 = pulse.LoConfig(user_lo_config_dict2) - qobj = assemble( - [self.circ, self.circ], - self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[self.user_lo_config, user_lo_config2], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + [self.circ, self.circ], + self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[self.user_lo_config, user_lo_config2], + ) qubit_lo_freq_ghz = [freq / 1e9 for freq in self.default_qubit_lo_freq] meas_lo_freq_ghz = [freq / 1e9 for freq in self.default_meas_lo_freq] @@ -824,13 +876,14 @@ def test_assemble_multi_circ_multi_lo_config(self): def test_assemble_multi_circ_single_lo_config(self): """Test assembling multiple circuits, with a single experiment level lo config (should override job level).""" - qobj = assemble( - [self.circ, self.circ], - self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=self.user_lo_config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + [self.circ, self.circ], + self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=self.user_lo_config, + ) self.assertListEqual(qobj.config.qubit_lo_freq, [5.55, 5, 5, 4.91, 5]) self.assertListEqual(qobj.config.meas_lo_freq, [6.64, 6.7, 6.7, 6.7, 6.1]) @@ -840,14 +893,15 @@ def test_assemble_multi_circ_wrong_number_of_multi_lo_configs(self): """Test assembling circuits, with a different number of experiment level lo configs (n:m setup). """ - with self.assertRaises(QiskitError): - assemble( - [self.circ, self.circ, self.circ], - self.backend, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[self.user_lo_config, self.user_lo_config], - ) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + assemble( + [self.circ, self.circ, self.circ], + self.backend, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[self.user_lo_config, self.user_lo_config], + ) def test_assemble_circ_lo_config_errors(self): """Test that lo config errors are raised properly if experiment level los are provided and @@ -867,7 +921,8 @@ def test_assemble_circ_lo_config_errors(self): pulse.MeasureChannel(4): 7e9, } - qobj = assemble(self.circ, self.backend, schedule_los=full_lo_config_dict) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circ, self.backend, schedule_los=full_lo_config_dict) self.assertListEqual(qobj.config.qubit_lo_freq, [4.85, 4.9, 4.95, 5, 5.05]) self.assertListEqual(qobj.config.meas_lo_freq, [6.8, 6.85, 6.9, 6.95, 7]) @@ -876,14 +931,16 @@ def test_assemble_circ_lo_config_errors(self): # no defaults and missing experiment level drive lo raises missing_drive_lo_config_dict = copy.deepcopy(full_lo_config_dict) missing_drive_lo_config_dict.pop(pulse.DriveChannel(0)) - with self.assertRaises(QiskitError): - qobj = assemble(self.circ, self.backend, schedule_los=missing_drive_lo_config_dict) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + qobj = assemble(self.circ, self.backend, schedule_los=missing_drive_lo_config_dict) # no defaults and missing experiment level meas lo raises missing_meas_lo_config_dict = copy.deepcopy(full_lo_config_dict) missing_meas_lo_config_dict.pop(pulse.MeasureChannel(0)) - with self.assertRaises(QiskitError): - qobj = assemble(self.circ, self.backend, schedule_los=missing_meas_lo_config_dict) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + qobj = assemble(self.circ, self.backend, schedule_los=missing_meas_lo_config_dict) # verify lo ranges are checked at experiment level lo_values = list(full_lo_config_dict.values()) @@ -892,24 +949,26 @@ def test_assemble_circ_lo_config_errors(self): # out of range drive lo full_lo_config_dict[pulse.DriveChannel(0)] -= 5.5e6 - with self.assertRaises(QiskitError): - qobj = assemble( - self.circ, - self.backend, - qubit_lo_range=qubit_lo_range, - schedule_los=full_lo_config_dict, - ) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + qobj = assemble( + self.circ, + self.backend, + qubit_lo_range=qubit_lo_range, + schedule_los=full_lo_config_dict, + ) full_lo_config_dict[pulse.DriveChannel(0)] += 5.5e6 # reset drive value # out of range meas lo full_lo_config_dict[pulse.MeasureChannel(0)] += 5.5e6 - with self.assertRaises(QiskitError): - qobj = assemble( - self.circ, - self.backend, - meas_lo_range=meas_lo_range, - schedule_los=full_lo_config_dict, - ) + with self.assertWarns(DeprecationWarning): + with self.assertRaises(QiskitError): + qobj = assemble( + self.circ, + self.backend, + meas_lo_range=meas_lo_range, + schedule_los=full_lo_config_dict, + ) class TestPulseAssembler(QiskitTestCase): @@ -917,7 +976,8 @@ class TestPulseAssembler(QiskitTestCase): def setUp(self): super().setUp() - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() self.backend_config = self.backend.configuration() test_pulse = pulse.Waveform( @@ -945,13 +1005,14 @@ def setUp(self): def test_assemble_adds_schedule_metadata_to_experiment_header(self): """Verify that any circuit metadata is added to the experiment header.""" self.schedule.metadata = {"experiment_type": "gst", "execution_number": "1234"} - qobj = assemble( - self.schedule, - shots=100, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + shots=100, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[], + ) self.assertEqual( qobj.experiments[0].header.metadata, {"experiment_type": "gst", "execution_number": "1234"}, @@ -969,14 +1030,15 @@ def test_assemble_sample_pulse(self): schedule += pulse.Play( pulse.Waveform([0.5] * 16, name="test0"), pulse.DriveChannel(0), name="test1" ) - qobj = assemble( - schedule, - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[], - **self.config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[], + **self.config, + ) test_dict = qobj.to_dict() experiment = test_dict["experiments"][0] @@ -994,14 +1056,15 @@ def test_assemble_sample_pulse(self): def test_assemble_single_schedule_without_lo_config(self): """Test assembling a single schedule, no lo config.""" - qobj = assemble( - self.schedule, - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[], - **self.config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[], + **self.config, + ) test_dict = qobj.to_dict() self.assertListEqual(test_dict["config"]["qubit_lo_freq"], [4.9, 5.0]) @@ -1010,13 +1073,14 @@ def test_assemble_single_schedule_without_lo_config(self): def test_assemble_multi_schedules_without_lo_config(self): """Test assembling schedules, no lo config.""" - qobj = assemble( - [self.schedule, self.schedule], - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - **self.config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + [self.schedule, self.schedule], + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + **self.config, + ) test_dict = qobj.to_dict() self.assertListEqual(test_dict["config"]["qubit_lo_freq"], [4.9, 5.0]) @@ -1025,14 +1089,15 @@ def test_assemble_multi_schedules_without_lo_config(self): def test_assemble_single_schedule_with_lo_config(self): """Test assembling a single schedule, with a single lo config.""" - qobj = assemble( - self.schedule, - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=self.user_lo_config, - **self.config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=self.user_lo_config, + **self.config, + ) test_dict = qobj.to_dict() self.assertListEqual(test_dict["config"]["qubit_lo_freq"], [4.91, 5.0]) @@ -1041,14 +1106,15 @@ def test_assemble_single_schedule_with_lo_config(self): def test_assemble_single_schedule_with_lo_config_dict(self): """Test assembling a single schedule, with a single lo config supplied as dictionary.""" - qobj = assemble( - self.schedule, - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=self.user_lo_config_dict, - **self.config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=self.user_lo_config_dict, + **self.config, + ) test_dict = qobj.to_dict() self.assertListEqual(test_dict["config"]["qubit_lo_freq"], [4.91, 5.0]) @@ -1057,14 +1123,15 @@ def test_assemble_single_schedule_with_lo_config_dict(self): def test_assemble_single_schedule_with_multi_lo_configs(self): """Test assembling a single schedule, with multiple lo configs (frequency sweep).""" - qobj = assemble( - self.schedule, - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[self.user_lo_config, self.user_lo_config], - **self.config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[self.user_lo_config, self.user_lo_config], + **self.config, + ) test_dict = qobj.to_dict() self.assertListEqual(test_dict["config"]["qubit_lo_freq"], [4.9, 5.0]) @@ -1074,14 +1141,15 @@ def test_assemble_single_schedule_with_multi_lo_configs(self): def test_assemble_multi_schedules_with_multi_lo_configs(self): """Test assembling schedules, with the same number of lo configs (n:n setup).""" - qobj = assemble( - [self.schedule, self.schedule], - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[self.user_lo_config, self.user_lo_config], - **self.config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + [self.schedule, self.schedule], + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[self.user_lo_config, self.user_lo_config], + **self.config, + ) test_dict = qobj.to_dict() self.assertListEqual(test_dict["config"]["qubit_lo_freq"], [4.9, 5.0]) @@ -1092,14 +1160,15 @@ def test_assemble_multi_schedules_with_multi_lo_configs(self): def test_assemble_multi_schedules_with_wrong_number_of_multi_lo_configs(self): """Test assembling schedules, with a different number of lo configs (n:m setup).""" with self.assertRaises(QiskitError): - assemble( - [self.schedule, self.schedule, self.schedule], - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[self.user_lo_config, self.user_lo_config], - **self.config, - ) + with self.assertWarns(DeprecationWarning): + assemble( + [self.schedule, self.schedule, self.schedule], + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[self.user_lo_config, self.user_lo_config], + **self.config, + ) def test_assemble_meas_map(self): """Test assembling a single schedule, no lo config.""" @@ -1107,20 +1176,22 @@ def test_assemble_meas_map(self): schedule = schedule.insert(5, Acquire(5, AcquireChannel(0), MemorySlot(0))) schedule = schedule.insert(5, Acquire(5, AcquireChannel(1), MemorySlot(1))) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0], [1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0], [1]], + ) self.assertIsInstance(qobj, PulseQobj) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 1, 2]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 1, 2]], + ) self.assertIsInstance(qobj, PulseQobj) def test_assemble_memory_slots(self): @@ -1132,12 +1203,13 @@ def test_assemble_memory_slots(self): 5, self.backend_config.acquire(0), mem_slot=pulse.MemorySlot(n_memoryslots - 1) ) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0], [1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0], [1]], + ) self.assertEqual(qobj.config.memory_slots, n_memoryslots) # this should be in experimental header as well @@ -1153,13 +1225,13 @@ def test_assemble_memory_slots(self): 5, self.backend_config.acquire(0), mem_slot=pulse.MemorySlot(n_memoryslots - 1) ), ) - - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0], [1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0], [1]], + ) self.assertEqual(qobj.config.memory_slots, n_memoryslots) # this should be in experimental header as well @@ -1176,12 +1248,13 @@ def test_assemble_memory_slots_for_schedules(self): ) schedules.append(schedule) - qobj = assemble( - schedules, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0], [1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedules, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0], [1]], + ) self.assertEqual(qobj.config.memory_slots, max(n_memoryslots)) self.assertEqual(qobj.experiments[0].header.memory_slots, n_memoryslots[0]) @@ -1198,20 +1271,22 @@ def test_pulse_name_conflicts(self): 1, Play(name_conflict_pulse, self.backend_config.drive(1)) ) - qobj = assemble( - self.schedule, - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[], - **self.config, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[], + **self.config, + ) self.assertNotEqual(qobj.config.pulse_library[0].name, qobj.config.pulse_library[1].name) def test_pulse_name_conflicts_in_other_schedule(self): """Test two pulses with the same name in different schedule can be resolved.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() defaults = backend.defaults() schedules = [] @@ -1222,9 +1297,12 @@ def test_pulse_name_conflicts_in_other_schedule(self): sched += measure(qubits=[0], backend=backend) << 100 schedules.append(sched) - qobj = assemble( - schedules, qubit_lo_freq=defaults.qubit_freq_est, meas_lo_freq=defaults.meas_freq_est - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedules, + qubit_lo_freq=defaults.qubit_freq_est, + meas_lo_freq=defaults.meas_freq_est, + ) # two user pulses and one measurement pulse should be contained self.assertEqual(len(qobj.config.pulse_library), 3) @@ -1233,7 +1311,9 @@ def test_assemble_with_delay(self): """Test that delay instruction is not ignored in assembly.""" delay_schedule = pulse.Delay(10, self.backend_config.drive(0)) delay_schedule += self.schedule - delay_qobj = assemble(delay_schedule, self.backend) + + with self.assertWarns(DeprecationWarning): + delay_qobj = assemble(delay_schedule, self.backend) self.assertEqual(delay_qobj.experiments[0].instructions[0].name, "delay") self.assertEqual(delay_qobj.experiments[0].instructions[0].duration, 10) @@ -1258,7 +1338,8 @@ def test_delay_removed_on_acq_ch(self): sched2 += delay1 sched2 += self.schedule # includes ``Acquire`` instr - delay_qobj = assemble([sched0, sched1, sched2], self.backend) + with self.assertWarns(DeprecationWarning): + delay_qobj = assemble([sched0, sched1, sched2], self.backend) # check that no delay instrs occur on acquire channels is_acq_delay = False @@ -1279,15 +1360,16 @@ def test_delay_removed_on_acq_ch(self): def test_assemble_schedule_enum(self): """Test assembling a schedule with enum input values to assemble.""" - qobj = assemble( - self.schedule, - qobj_header=self.header, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - schedule_los=[], - meas_level=MeasLevel.CLASSIFIED, - meas_return=MeasReturnType.AVERAGE, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qobj_header=self.header, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + schedule_los=[], + meas_level=MeasLevel.CLASSIFIED, + meas_return=MeasReturnType.AVERAGE, + ) test_dict = qobj.to_dict() self.assertEqual(test_dict["config"]["meas_return"], "avg") @@ -1312,14 +1394,16 @@ def test_assemble_parametric(self): ) << sched.duration ) - backend = FakeOpenPulse3Q() + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse3Q() backend.configuration().parametric_pulses = [ "gaussian", "drag", "gaussian_square", "constant", ] - qobj = assemble(sched, backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(sched, backend) self.assertEqual(qobj.config.pulse_library, []) qobj_insts = qobj.experiments[0].instructions @@ -1358,10 +1442,12 @@ def test_assemble_parametric_unsupported(self): ) sched += Play(pulse.Constant(duration=25, amp=1), DriveChannel(2)) - backend = FakeOpenPulse3Q() + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse3Q() backend.configuration().parametric_pulses = ["something_extra"] - qobj = assemble(sched, backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(sched, backend) self.assertNotEqual(qobj.config.pulse_library, []) qobj_insts = qobj.experiments[0].instructions @@ -1369,7 +1455,8 @@ def test_assemble_parametric_unsupported(self): def test_assemble_parametric_pulse_kwarg_with_backend_setting(self): """Test that parametric pulses respect the kwarg over backend""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() qc = QuantumCircuit(1, 1) qc.x(0) @@ -1379,12 +1466,14 @@ def test_assemble_parametric_pulse_kwarg_with_backend_setting(self): qc.add_calibration("x", (0,), x_q0) - qobj = assemble(qc, backend, parametric_pulses=["gaussian"]) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc, backend, parametric_pulses=["gaussian"]) self.assertEqual(qobj.config.parametric_pulses, ["gaussian"]) def test_assemble_parametric_pulse_kwarg_empty_list_with_backend_setting(self): """Test that parametric pulses respect the kwarg as empty list over backend""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() qc = QuantumCircuit(1, 1) qc.x(0) @@ -1394,22 +1483,26 @@ def test_assemble_parametric_pulse_kwarg_empty_list_with_backend_setting(self): qc.add_calibration("x", (0,), x_q0) - qobj = assemble(qc, backend, parametric_pulses=[]) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc, backend, parametric_pulses=[]) self.assertEqual(qobj.config.parametric_pulses, []) def test_init_qubits_default(self): """Check that the init_qubits=None assemble option is passed on to the qobj.""" - qobj = assemble(self.schedule, self.backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.schedule, self.backend) self.assertEqual(qobj.config.init_qubits, True) def test_init_qubits_true(self): """Check that the init_qubits=True assemble option is passed on to the qobj.""" - qobj = assemble(self.schedule, self.backend, init_qubits=True) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.schedule, self.backend, init_qubits=True) self.assertEqual(qobj.config.init_qubits, True) def test_init_qubits_false(self): """Check that the init_qubits=False assemble option is passed on to the qobj.""" - qobj = assemble(self.schedule, self.backend, init_qubits=False) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.schedule, self.backend, init_qubits=False) self.assertEqual(qobj.config.init_qubits, False) def test_assemble_backend_rep_times_delays(self): @@ -1423,14 +1516,15 @@ def test_assemble_backend_rep_times_delays(self): setattr(self.backend_config, "default_rep_delay", default_rep_delay) # dynamic rep rates off - qobj = assemble(self.schedule, self.backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.schedule, self.backend) self.assertEqual(qobj.config.rep_time, int(rep_times[0] * 1e6)) self.assertEqual(hasattr(qobj.config, "rep_delay"), False) # dynamic rep rates on setattr(self.backend_config, "dynamic_reprate_enabled", True) # RuntimeWarning bc ``rep_time`` is specified`` when dynamic rep rates not enabled - with self.assertWarns(RuntimeWarning): + with self.assertWarns(DeprecationWarning): qobj = assemble(self.schedule, self.backend) self.assertEqual(qobj.config.rep_time, int(rep_times[0] * 1e6)) self.assertEqual(qobj.config.rep_delay, default_rep_delay * 1e6) @@ -1445,7 +1539,7 @@ def test_assemble_user_rep_time_delay(self): # dynamic rep rates off # RuntimeWarning bc using ``rep_delay`` when dynamic rep rates off - with self.assertWarns(RuntimeWarning): + with self.assertWarns(DeprecationWarning): qobj = assemble(self.schedule, self.backend, **self.config) self.assertEqual(qobj.config.rep_time, int(rep_time * 1e6)) self.assertEqual(hasattr(qobj.config, "rep_delay"), False) @@ -1454,7 +1548,7 @@ def test_assemble_user_rep_time_delay(self): # RuntimeWarning bc using ``rep_time`` when dynamic rep rates are enabled del self.config["rep_delay"] setattr(self.backend_config, "dynamic_reprate_enabled", True) - with self.assertWarns(RuntimeWarning): + with self.assertWarns(DeprecationWarning): qobj = assemble(self.schedule, self.backend, **self.config) self.assertEqual(qobj.config.rep_time, int(rep_time * 1e6)) self.assertEqual(hasattr(qobj.config, "rep_delay"), False) @@ -1466,19 +1560,21 @@ def test_assemble_user_rep_time_delay(self): setattr(self.backend_config, "rep_delay_range", [0, 3.0e-6]) setattr(self.backend_config, "default_rep_delay", 2.2e-6) del self.config["rep_time"] - qobj = assemble(self.schedule, self.backend, **self.config) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.schedule, self.backend, **self.config) self.assertEqual(qobj.config.rep_time, int(rep_times[0] * 1e6)) self.assertEqual(qobj.config.rep_delay, 2.2) # use qobj ``default_rep_delay`` self.config["rep_delay"] = 1.5e-6 - qobj = assemble(self.schedule, self.backend, **self.config) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.schedule, self.backend, **self.config) self.assertEqual(qobj.config.rep_time, int(rep_times[0] * 1e6)) self.assertEqual(qobj.config.rep_delay, 1.5) # use ``rep_delay`` outside of ``rep_delay_range self.config["rep_delay"] = 5.0e-6 - with self.assertRaises(QiskitError): + with self.assertRaises(DeprecationWarning): assemble(self.schedule, self.backend, **self.config) def test_assemble_with_individual_discriminators(self): @@ -1494,12 +1590,13 @@ def test_assemble_with_individual_discriminators(self): Acquire(5, AcquireChannel(1), MemorySlot(1), discriminator=disc_two), ) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 1]], + ) qobj_discriminators = qobj.experiments[0].instructions[0].discriminators self.assertEqual(len(qobj_discriminators), 2) @@ -1520,12 +1617,13 @@ def test_assemble_with_single_discriminators(self): Acquire(5, AcquireChannel(1), MemorySlot(1)), ) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 1]], + ) qobj_discriminators = qobj.experiments[0].instructions[0].discriminators self.assertEqual(len(qobj_discriminators), 1) @@ -1543,7 +1641,7 @@ def test_assemble_with_unequal_discriminators(self): schedule += Acquire(5, AcquireChannel(1), MemorySlot(1), discriminator=disc_two) schedule += Acquire(5, AcquireChannel(2), MemorySlot(2)) - with self.assertRaises(QiskitError): + with self.assertRaises(QiskitError), self.assertWarns(DeprecationWarning): assemble( schedule, qubit_lo_freq=self.default_qubit_lo_freq, @@ -1564,12 +1662,13 @@ def test_assemble_with_individual_kernels(self): Acquire(5, AcquireChannel(1), MemorySlot(1), kernel=disc_two), ) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 1]], + ) qobj_kernels = qobj.experiments[0].instructions[0].kernels self.assertEqual(len(qobj_kernels), 2) @@ -1590,12 +1689,13 @@ def test_assemble_with_single_kernels(self): Acquire(5, AcquireChannel(1), MemorySlot(1)), ) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 1]], + ) qobj_kernels = qobj.experiments[0].instructions[0].kernels self.assertEqual(len(qobj_kernels), 1) @@ -1613,7 +1713,7 @@ def test_assemble_with_unequal_kernels(self): schedule += Acquire(5, AcquireChannel(1), MemorySlot(1), kernel=disc_two) schedule += Acquire(5, AcquireChannel(2), MemorySlot(2)) - with self.assertRaises(QiskitError): + with self.assertRaises(QiskitError), self.assertWarns(DeprecationWarning): assemble( schedule, qubit_lo_freq=self.default_qubit_lo_freq, @@ -1624,7 +1724,8 @@ def test_assemble_with_unequal_kernels(self): def test_assemble_single_instruction(self): """Test assembling schedules, no lo config.""" inst = pulse.Play(pulse.Constant(100, 1.0), pulse.DriveChannel(0)) - self.assertIsInstance(assemble(inst, self.backend), PulseQobj) + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(assemble(inst, self.backend), PulseQobj) def test_assemble_overlapping_time(self): """Test that assembly errors when qubits are measured in overlapping time.""" @@ -1636,12 +1737,13 @@ def test_assemble_overlapping_time(self): Acquire(5, AcquireChannel(1), MemorySlot(1)) << 1, ) with self.assertRaises(QiskitError): - assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 1]], - ) + with self.assertWarns(DeprecationWarning): + assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 1]], + ) def test_assemble_meas_map_vs_insts(self): """Test that assembly errors when the qubits are measured in overlapping time @@ -1653,12 +1755,13 @@ def test_assemble_meas_map_vs_insts(self): schedule += Acquire(5, AcquireChannel(3), MemorySlot(3)) << 2 with self.assertRaises(QiskitError): - assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0], [1, 2], [3]], - ) + with self.assertWarns(DeprecationWarning): + assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0], [1, 2], [3]], + ) def test_assemble_non_overlapping_time_single_meas_map(self): """Test that assembly works when qubits are measured in non-overlapping @@ -1670,12 +1773,13 @@ def test_assemble_non_overlapping_time_single_meas_map(self): schedule = schedule.append( Acquire(5, AcquireChannel(1), MemorySlot(1)) << 5, ) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 1]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 1]], + ) self.assertIsInstance(qobj, PulseQobj) def test_assemble_disjoint_time(self): @@ -1687,12 +1791,13 @@ def test_assemble_disjoint_time(self): schedule = schedule.append( Acquire(5, AcquireChannel(1), MemorySlot(1)) << 1, ) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 2], [1, 3]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 2], [1, 3]], + ) self.assertIsInstance(qobj, PulseQobj) def test_assemble_valid_qubits(self): @@ -1708,12 +1813,13 @@ def test_assemble_valid_qubits(self): schedule = schedule.append( Acquire(5, AcquireChannel(3), MemorySlot(3)), ) - qobj = assemble( - schedule, - qubit_lo_freq=self.default_qubit_lo_freq, - meas_lo_freq=self.default_meas_lo_freq, - meas_map=[[0, 1, 2], [3]], - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + schedule, + qubit_lo_freq=self.default_qubit_lo_freq, + meas_lo_freq=self.default_meas_lo_freq, + meas_map=[[0, 1, 2], [3]], + ) self.assertIsInstance(qobj, PulseQobj) @@ -1724,7 +1830,8 @@ def setUp(self): super().setUp() self.schedule = pulse.Schedule(name="fake_experiment") - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() self.config = self.backend.configuration() self.defaults = self.backend.defaults() self.qubit_lo_freq = list(self.defaults.qubit_freq_est) @@ -1746,24 +1853,26 @@ def setUp(self): def test_defaults(self): """Test defaults work.""" - qobj = assemble( - self.schedule, - qubit_lo_freq=self.qubit_lo_freq, - meas_lo_freq=self.meas_lo_freq, - qubit_lo_range=self.qubit_lo_range, - meas_lo_range=self.meas_lo_range, - schedule_los=self.schedule_los, - meas_map=self.meas_map, - memory_slots=self.memory_slots, - rep_time=self.rep_time, - rep_delay=self.rep_delay, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qubit_lo_freq=self.qubit_lo_freq, + meas_lo_freq=self.meas_lo_freq, + qubit_lo_range=self.qubit_lo_range, + meas_lo_range=self.meas_lo_range, + schedule_los=self.schedule_los, + meas_map=self.meas_map, + memory_slots=self.memory_slots, + rep_time=self.rep_time, + rep_delay=self.rep_delay, + ) self.assertIsInstance(qobj, PulseQobj) def test_missing_qubit_lo_freq(self): """Test error raised if qubit_lo_freq missing.""" - with self.assertRaises(QiskitError): + with self.assertRaises(QiskitError), self.assertWarns(DeprecationWarning): + assemble( self.schedule, qubit_lo_freq=None, @@ -1779,7 +1888,7 @@ def test_missing_qubit_lo_freq(self): def test_missing_meas_lo_freq(self): """Test error raised if meas_lo_freq missing.""" - with self.assertRaises(QiskitError): + with self.assertRaises(QiskitError), self.assertWarns(DeprecationWarning): assemble( self.schedule, qubit_lo_freq=self.qubit_lo_freq, @@ -1794,74 +1903,79 @@ def test_missing_meas_lo_freq(self): def test_missing_memory_slots(self): """Test error is not raised if memory_slots are missing.""" - qobj = assemble( - self.schedule, - qubit_lo_freq=self.qubit_lo_freq, - meas_lo_freq=self.meas_lo_freq, - qubit_lo_range=self.qubit_lo_range, - meas_lo_range=self.meas_lo_range, - schedule_los=self.schedule_los, - meas_map=self.meas_map, - memory_slots=None, - rep_time=self.rep_time, - rep_delay=self.rep_delay, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qubit_lo_freq=self.qubit_lo_freq, + meas_lo_freq=self.meas_lo_freq, + qubit_lo_range=self.qubit_lo_range, + meas_lo_range=self.meas_lo_range, + schedule_los=self.schedule_los, + meas_map=self.meas_map, + memory_slots=None, + rep_time=self.rep_time, + rep_delay=self.rep_delay, + ) self.assertIsInstance(qobj, PulseQobj) def test_missing_rep_time_and_delay(self): """Test qobj is valid if rep_time and rep_delay are missing.""" - qobj = assemble( - self.schedule, - qubit_lo_freq=self.qubit_lo_freq, - meas_lo_freq=self.meas_lo_freq, - qubit_lo_range=self.qubit_lo_range, - meas_lo_range=self.meas_lo_range, - schedule_los=self.schedule_los, - meas_map=self.meas_map, - memory_slots=None, - rep_time=None, - rep_delay=None, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qubit_lo_freq=self.qubit_lo_freq, + meas_lo_freq=self.meas_lo_freq, + qubit_lo_range=self.qubit_lo_range, + meas_lo_range=self.meas_lo_range, + schedule_los=self.schedule_los, + meas_map=self.meas_map, + memory_slots=None, + rep_time=None, + rep_delay=None, + ) self.assertEqual(hasattr(qobj, "rep_time"), False) self.assertEqual(hasattr(qobj, "rep_delay"), False) def test_missing_meas_map(self): """Test that assembly still works if meas_map is missing.""" - qobj = assemble( - self.schedule, - qubit_lo_freq=self.qubit_lo_freq, - meas_lo_freq=self.meas_lo_freq, - qubit_lo_range=self.qubit_lo_range, - meas_lo_range=self.meas_lo_range, - schedule_los=self.schedule_los, - meas_map=None, - memory_slots=self.memory_slots, - rep_time=self.rep_time, - rep_delay=self.rep_delay, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qubit_lo_freq=self.qubit_lo_freq, + meas_lo_freq=self.meas_lo_freq, + qubit_lo_range=self.qubit_lo_range, + meas_lo_range=self.meas_lo_range, + schedule_los=self.schedule_los, + meas_map=None, + memory_slots=self.memory_slots, + rep_time=self.rep_time, + rep_delay=self.rep_delay, + ) self.assertIsInstance(qobj, PulseQobj) def test_missing_lo_ranges(self): """Test that assembly still works if lo_ranges are missing.""" - qobj = assemble( - self.schedule, - qubit_lo_freq=self.qubit_lo_freq, - meas_lo_freq=self.meas_lo_freq, - qubit_lo_range=None, - meas_lo_range=None, - schedule_los=self.schedule_los, - meas_map=self.meas_map, - memory_slots=self.memory_slots, - rep_time=self.rep_time, - rep_delay=self.rep_delay, - ) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + self.schedule, + qubit_lo_freq=self.qubit_lo_freq, + meas_lo_freq=self.meas_lo_freq, + qubit_lo_range=None, + meas_lo_range=None, + schedule_los=self.schedule_los, + meas_map=self.meas_map, + memory_slots=self.memory_slots, + rep_time=self.rep_time, + rep_delay=self.rep_delay, + ) self.assertIsInstance(qobj, PulseQobj) def test_unsupported_meas_level(self): """Test that assembly raises an error if meas_level is not supported""" - backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() backend.configuration().meas_levels = [1, 2] - with self.assertRaises(QiskitError): + with self.assertRaises(QiskitError), self.assertWarns(DeprecationWarning): assemble( self.schedule, backend, @@ -1879,7 +1993,8 @@ def test_unsupported_meas_level(self): def test_single_and_deprecated_acquire_styles(self): """Test that acquires are identically combined with Acquires that take a single channel.""" - backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() new_style_schedule = Schedule() acq_dur = 1200 for i in range(2): @@ -1890,10 +2005,12 @@ def test_single_and_deprecated_acquire_styles(self): deprecated_style_schedule += Acquire(1200, AcquireChannel(i), MemorySlot(i)) # The Qobj IDs will be different - n_qobj = assemble(new_style_schedule, backend) + with self.assertWarns(DeprecationWarning): + n_qobj = assemble(new_style_schedule, backend) n_qobj.qobj_id = None n_qobj.experiments[0].header.name = None - d_qobj = assemble(deprecated_style_schedule, backend) + with self.assertWarns(DeprecationWarning): + d_qobj = assemble(deprecated_style_schedule, backend) d_qobj.qobj_id = None d_qobj.experiments[0].header.name = None self.assertEqual(n_qobj, d_qobj) @@ -1924,7 +2041,8 @@ def setUp(self): def assertAssembleLog(self, log_msg): """Runs assemble and checks for logs containing specified message""" - assemble(self.circuit, shots=2000, memory=True) + with self.assertWarns(DeprecationWarning): + assemble(self.circuit, shots=2000, memory=True) self.output.seek(0) # Filter unrelated log lines output_lines = self.output.readlines() diff --git a/test/python/compiler/test_compiler.py b/test/python/compiler/test_compiler.py index 7ebfe1f13343..32d33ba3e56c 100644 --- a/test/python/compiler/test_compiler.py +++ b/test/python/compiler/test_compiler.py @@ -188,7 +188,6 @@ def test_example_swap_bits(self): def test_parallel_compile(self): """Trigger parallel routines in compile.""" - backend = Fake20QV1() qr = QuantumRegister(16) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) @@ -197,7 +196,9 @@ def test_parallel_compile(self): qc.cx(qr[0], qr[k]) qc.measure(qr[5], cr[0]) qlist = [qc for k in range(10)] - qobj = assemble(transpile(qlist, backend=backend)) + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() + qobj = assemble(transpile(qlist, backend=backend)) self.assertEqual(len(qobj.experiments), 10) def test_no_conflict_backend_passmanager(self): @@ -240,7 +241,8 @@ def test_compile_single_qubit(self): circuit2 = transpile( circuit, backend=None, coupling_map=cmap, basis_gates=["u2"], initial_layout=layout ) - qobj = assemble(circuit2) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circuit2) compiled_instruction = qobj.experiments[0].instructions[0] @@ -498,13 +500,15 @@ def test_yzy_zyz_cases(self): See: https://github.com/Qiskit/qiskit-terra/issues/607 """ - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr = QuantumRegister(2) circ1 = QuantumCircuit(qr) circ1.cx(qr[0], qr[1]) circ1.rz(0.7, qr[1]) circ1.rx(1.570796, qr[1]) - qobj1 = assemble(transpile(circ1, backend)) + with self.assertWarns(DeprecationWarning): + qobj1 = assemble(transpile(circ1, backend)) self.assertIsInstance(qobj1, QasmQobj) circ2 = QuantumCircuit(qr) @@ -512,7 +516,8 @@ def test_yzy_zyz_cases(self): circ2.h(qr[0]) circ2.s(qr[0]) circ2.h(qr[0]) - qobj2 = assemble(transpile(circ2, backend)) + with self.assertWarns(DeprecationWarning): + qobj2 = assemble(transpile(circ2, backend)) self.assertIsInstance(qobj2, QasmQobj) diff --git a/test/python/compiler/test_disassembler.py b/test/python/compiler/test_disassembler.py index e575c021adca..0525b54e2107 100644 --- a/test/python/compiler/test_disassembler.py +++ b/test/python/compiler/test_disassembler.py @@ -10,7 +10,9 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Assembler Test.""" +"""Assembler Test. +FULLY REMOVE ONCE Qobj, assemble AND disassemble ARE REMOVED. +""" import unittest @@ -55,14 +57,15 @@ def test_disassemble_single_circuit(self): qubit_lo_freq = [5e9, 5e9] meas_lo_freq = [6.7e9, 6.7e9] - qobj = assemble( - circ, - shots=2000, - memory=True, - qubit_lo_freq=qubit_lo_freq, - meas_lo_freq=meas_lo_freq, - ) - circuits, run_config_out, headers = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble( + circ, + shots=2000, + memory=True, + qubit_lo_freq=qubit_lo_freq, + meas_lo_freq=meas_lo_freq, + ) + circuits, run_config_out, headers = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 2) self.assertEqual(run_config_out.memory_slots, 2) @@ -91,8 +94,9 @@ def test_disassemble_multiple_circuits(self): circ1.cx(qr1[0], qr1[2]) circ1.measure(qr1, qc1) - qobj = assemble([circ0, circ1], shots=100, memory=False, seed=6) - circuits, run_config_out, headers = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble([circ0, circ1], shots=100, memory=False, seed=6) + circuits, run_config_out, headers = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 3) self.assertEqual(run_config_out.memory_slots, 3) @@ -113,8 +117,9 @@ def test_disassemble_no_run_config(self): circ.cx(qr[0], qr[1]) circ.measure(qr, qc) - qobj = assemble(circ) - circuits, run_config_out, headers = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ) + circuits, run_config_out, headers = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 2) self.assertEqual(run_config_out.memory_slots, 2) @@ -128,8 +133,9 @@ def test_disassemble_initialize(self): circ = QuantumCircuit(q, name="circ") circ.initialize([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)], q[:]) - qobj = assemble(circ) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 2) self.assertEqual(run_config_out.memory_slots, 0) @@ -142,8 +148,9 @@ def test_disassemble_isometry(self): q = QuantumRegister(2, name="q") circ = QuantumCircuit(q, name="circ") circ.append(Isometry(qi.random_unitary(4).data, 0, 0), circ.qubits) - qobj = assemble(circ) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 2) self.assertEqual(run_config_out.memory_slots, 0) @@ -166,8 +173,9 @@ def test_opaque_instruction(self): c = ClassicalRegister(4, name="c") circ = QuantumCircuit(q, c, name="circ") circ.append(opaque_inst, [q[0], q[2], q[5], q[3]], [c[3], c[0]]) - qobj = assemble(circ) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circ) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 6) self.assertEqual(run_config_out.memory_slots, 4) @@ -184,8 +192,9 @@ def test_circuit_with_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) - qobj = assemble(qc) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 2) self.assertEqual(run_config_out.memory_slots, 3) @@ -199,8 +208,9 @@ def test_circuit_with_simple_conditional(self): cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr) qc.h(qr[0]).c_if(cr, 1) - qobj = assemble(qc) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 1) self.assertEqual(run_config_out.memory_slots, 1) @@ -219,8 +229,10 @@ def test_circuit_with_single_bit_conditions(self): cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) qc.h(qr[0]).c_if(cr[0], 1) - qobj = assemble(qc) - circuits, run_config_out, header = disassemble(qobj) + + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, len(qr)) self.assertEqual(run_config_out.memory_slots, len(cr)) @@ -234,8 +246,9 @@ def test_circuit_with_mcx(self): cr = ClassicalRegister(5) qc = QuantumCircuit(qr, cr) qc.mcx([0, 1, 2], 4) - qobj = assemble(qc) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 5) self.assertEqual(run_config_out.memory_slots, 5) @@ -257,8 +270,9 @@ def test_multiple_conditionals_multiple_registers(self): 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) - qobj = assemble(qc) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 3) self.assertEqual(run_config_out.memory_slots, 15) @@ -272,8 +286,9 @@ def test_circuit_with_bit_conditional_1(self): cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) qc.h(qr[0]).c_if(cr[1], True) - qobj = assemble(qc) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 2) self.assertEqual(run_config_out.memory_slots, 2) @@ -290,8 +305,9 @@ def test_circuit_with_bit_conditional_2(self): 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) - qobj = assemble(qc) - circuits, run_config_out, header = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc) + circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.n_qubits, 2) self.assertEqual(run_config_out.memory_slots, 4) @@ -330,8 +346,9 @@ def test_single_circuit_calibrations(self): qc.add_calibration("h", [0], h_sched) qc.add_calibration(RXGate(np.pi), [0], x180) - qobj = assemble(qc, FakeOpenPulse2Q()) - output_circuits, _, _ = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc, FakeOpenPulse2Q()) + output_circuits, _, _ = disassemble(qobj) self.assertCircuitCalibrationsEqual([qc], output_circuits) @@ -344,11 +361,12 @@ def test_parametric_pulse_circuit_calibrations(self): qc.h(0) qc.add_calibration("h", [0], h_sched) - backend = FakeOpenPulse2Q() - backend.configuration().parametric_pulses = ["drag"] + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() + backend.configuration().parametric_pulses = ["drag"] - qobj = assemble(qc, backend) - output_circuits, _, _ = disassemble(qobj) + qobj = assemble(qc, backend) + output_circuits, _, _ = disassemble(qobj) out_qc = output_circuits[0] self.assertCircuitCalibrationsEqual([qc], output_circuits) @@ -377,8 +395,9 @@ def test_multi_circuit_uncommon_calibrations(self): qc_1.h(0) circuits = [qc_0, qc_1] - qobj = assemble(circuits, FakeOpenPulse2Q()) - output_circuits, _, _ = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circuits, FakeOpenPulse2Q()) + output_circuits, _, _ = disassemble(qobj) self.assertCircuitCalibrationsEqual(circuits, output_circuits) @@ -398,8 +417,9 @@ def test_multi_circuit_common_calibrations(self): qc_1.add_calibration(RXGate(np.pi), [1], sched) circuits = [qc_0, qc_1] - qobj = assemble(circuits, FakeOpenPulse2Q()) - output_circuits, _, _ = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(circuits, FakeOpenPulse2Q()) + output_circuits, _, _ = disassemble(qobj) self.assertCircuitCalibrationsEqual(circuits, output_circuits) @@ -413,8 +433,9 @@ def test_single_circuit_delay_calibrations(self): qc.add_calibration("test", [0], test_sched) - qobj = assemble(qc, FakeOpenPulse2Q()) - output_circuits, _, _ = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(qc, FakeOpenPulse2Q()) + output_circuits, _, _ = disassemble(qobj) self.assertEqual(len(qc.calibrations), len(output_circuits[0].calibrations)) self.assertEqual(qc.calibrations.keys(), output_circuits[0].calibrations.keys()) @@ -436,7 +457,8 @@ class TestPulseScheduleDisassembler(QiskitTestCase): def setUp(self): super().setUp() - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() self.backend_config = self.backend.configuration() self.backend_config.parametric_pulses = ["constant", "gaussian", "gaussian_square", "drag"] @@ -456,8 +478,9 @@ def test_disassemble_single_schedule(self): pulse.play(pulse.library.Constant(8, 0.1), d1) pulse.measure_all() - qobj = assemble(sched, backend=self.backend, shots=2000) - scheds, run_config_out, _ = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(sched, backend=self.backend, shots=2000) + scheds, run_config_out, _ = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.memory_slots, 2) self.assertEqual(run_config_out.shots, 2000) @@ -498,8 +521,9 @@ def test_disassemble_multiple_schedules(self): pulse.play(pulse.library.Constant(8, 0.4), d1) pulse.measure_all() - qobj = assemble([sched0, sched1], backend=self.backend, shots=2000) - scheds, run_config_out, _ = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble([sched0, sched1], backend=self.backend, shots=2000) + scheds, run_config_out, _ = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.memory_slots, 2) self.assertEqual(run_config_out.shots, 2000) @@ -518,8 +542,9 @@ def test_disassemble_parametric_pulses(self): pulse.play(pulse.library.GaussianSquare(10, 1.0, 2.0, 3), d0) pulse.play(pulse.library.Drag(10, 1.0, 2.0, 0.1), d0) - qobj = assemble(sched, backend=self.backend, shots=2000) - scheds, _, _ = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble(sched, backend=self.backend, shots=2000) + scheds, _, _ = disassemble(qobj) self.assertEqual(scheds[0], target_qobj_transform(sched)) def test_disassemble_schedule_los(self): @@ -536,8 +561,9 @@ def test_disassemble_schedule_los(self): {d0: 4.5e9, d1: 5e9, m0: 6e9, m1: 7e9}, {d0: 5e9, d1: 4.5e9, m0: 7e9, m1: 6e9}, ] - qobj = assemble([sched0, sched1], backend=self.backend, schedule_los=schedule_los) - _, run_config_out, _ = disassemble(qobj) + with self.assertWarns(DeprecationWarning): + qobj = assemble([sched0, sched1], backend=self.backend, schedule_los=schedule_los) + _, run_config_out, _ = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.schedule_los, schedule_los) diff --git a/test/python/compiler/test_scheduler.py b/test/python/compiler/test_scheduler.py index 62e0af596ba4..ad9b14b24c4a 100644 --- a/test/python/compiler/test_scheduler.py +++ b/test/python/compiler/test_scheduler.py @@ -15,7 +15,7 @@ from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.exceptions import QiskitError from qiskit.pulse import InstructionScheduleMap, Schedule -from qiskit.providers.fake_provider import FakeOpenPulse3Q +from qiskit.providers.fake_provider import FakeOpenPulse3Q, GenericBackendV2 from qiskit.compiler.scheduler import schedule from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -37,9 +37,9 @@ def setUp(self): self.circ2.cx(qr2[0], qr2[1]) self.circ2.measure(qr2, cr2) - self.backend = FakeOpenPulse3Q() - self.backend_config = self.backend.configuration() - self.num_qubits = self.backend_config.n_qubits + self.backend = GenericBackendV2( + 3, calibrate_instructions=True, basis_gates=["cx", "u1", "u2", "u3"], seed=42 + ) def test_instruction_map_and_backend_not_supplied(self): """Test instruction map and backend not supplied.""" @@ -51,6 +51,8 @@ def test_instruction_map_and_backend_not_supplied(self): def test_instruction_map_and_backend_defaults_unavailable(self): """Test backend defaults unavailable when backend is provided, but instruction map is not.""" + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse3Q() self.backend._defaults = None with self.assertRaisesRegex( QiskitError, r"The backend defaults are unavailable. The backend may not support pulse." diff --git a/test/python/compiler/test_sequencer.py b/test/python/compiler/test_sequencer.py index 771e854effab..e32d6370367b 100644 --- a/test/python/compiler/test_sequencer.py +++ b/test/python/compiler/test_sequencer.py @@ -13,6 +13,8 @@ # pylint: disable=missing-function-docstring """Tests basic functionality of the sequence function""" +# TODO with the removal of pulses, this file can be removed too. + import unittest from qiskit import QuantumCircuit, pulse @@ -27,7 +29,8 @@ class TestSequence(QiskitTestCase): def setUp(self): super().setUp() - self.backend = Fake127QPulseV1() + with self.assertWarns(DeprecationWarning): + self.backend = Fake127QPulseV1() self.backend.configuration().timing_constraints = {} def test_sequence_empty(self): diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 77b63a3098bc..46acff9d40d6 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -96,7 +96,7 @@ from test import QiskitTestCase, combine, slow_test # pylint: disable=wrong-import-order -from ..legacy_cmaps import MELBOURNE_CMAP, RUESCHLIKON_CMAP +from ..legacy_cmaps import MELBOURNE_CMAP, RUESCHLIKON_CMAP, MUMBAI_CMAP, TOKYO_CMAP class CustomCX(Gate): @@ -170,7 +170,7 @@ def test_num_processes_kwarg_concurrent_default(self, num_processes): qc.h(0) qc.cx(0, 1) qc.measure_all() - target = GenericBackendV2(num_qubits=27).target + target = GenericBackendV2(num_qubits=27, seed=42).target res = transpile([qc] * 3, target=target, num_processes=num_processes) self.assertIsInstance(res, list) for circ in res: @@ -269,7 +269,7 @@ def test_transpile_non_adjacent_layout(self): circuit.cx(qr[2], qr[3]) backend = GenericBackendV2( - num_qubits=15, basis_gates=["ecr", "id", "rz", "sx", "x"], coupling_map=cmap + num_qubits=15, basis_gates=["ecr", "id", "rz", "sx", "x"], coupling_map=cmap, seed=42 ) initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]] @@ -313,7 +313,7 @@ def test_already_mapped_1(self): See: https://github.com/Qiskit/qiskit-terra/issues/342 """ - backend = GenericBackendV2(num_qubits=16, coupling_map=RUESCHLIKON_CMAP) + backend = GenericBackendV2(num_qubits=16, coupling_map=RUESCHLIKON_CMAP, seed=42) coupling_map = backend.coupling_map basis_gates = backend.operation_names @@ -517,11 +517,21 @@ def test_transpile_bell_discrete_basis(self): # Try with the initial layout in both directions to ensure we're dealing with the basis # having only a single direction. + + # Use optimization level=1 because the synthesis that runs as part of optimization at + # higher optimization levels will create intermediate gates that the transpiler currently + # lacks logic to translate to a discrete basis. self.assertIsInstance( - transpile(qc, target=target, initial_layout=[0, 1], seed_transpiler=42), QuantumCircuit + transpile( + qc, target=target, initial_layout=[0, 1], seed_transpiler=42, optimization_level=1 + ), + QuantumCircuit, ) self.assertIsInstance( - transpile(qc, target=target, initial_layout=[1, 0], seed_transpiler=42), QuantumCircuit + transpile( + qc, target=target, initial_layout=[1, 0], seed_transpiler=42, optimization_level=1 + ), + QuantumCircuit, ) def test_transpile_one(self): @@ -588,7 +598,7 @@ def test_transpile_singleton(self): def test_mapping_correction(self): """Test mapping works in previous failed case.""" - backend = GenericBackendV2(num_qubits=12) + backend = GenericBackendV2(num_qubits=12, seed=42) qr = QuantumRegister(name="qr", size=11) cr = ClassicalRegister(name="qc", size=11) circuit = QuantumCircuit(qr, cr) @@ -708,7 +718,7 @@ def test_transpiler_layout_from_intlist(self): def test_mapping_multi_qreg(self): """Test mapping works for multiple qregs.""" - backend = GenericBackendV2(num_qubits=8) + backend = GenericBackendV2(num_qubits=8, seed=42) qr = QuantumRegister(3, name="qr") qr2 = QuantumRegister(1, name="qr2") qr3 = QuantumRegister(4, name="qr3") @@ -725,7 +735,7 @@ def test_mapping_multi_qreg(self): def test_transpile_circuits_diff_registers(self): """Transpile list of circuits with different qreg names.""" - backend = GenericBackendV2(num_qubits=4) + backend = GenericBackendV2(num_qubits=4, seed=42) circuits = [] for _ in range(2): qr = QuantumRegister(2) @@ -741,7 +751,7 @@ def test_transpile_circuits_diff_registers(self): def test_wrong_initial_layout(self): """Test transpile with a bad initial layout.""" - backend = GenericBackendV2(num_qubits=4) + backend = GenericBackendV2(num_qubits=4, seed=42) qubit_reg = QuantumRegister(2, name="q") clbit_reg = ClassicalRegister(2, name="c") @@ -780,7 +790,7 @@ def test_parameterized_circuit_for_device(self): theta = Parameter("theta") qc.p(theta, qr[0]) - backend = GenericBackendV2(num_qubits=4) + backend = GenericBackendV2(num_qubits=4, seed=42) transpiled_qc = transpile( qc, @@ -820,7 +830,7 @@ def test_parameter_expression_circuit_for_device(self): square = theta * theta qc.rz(square, qr[0]) - backend = GenericBackendV2(num_qubits=4) + backend = GenericBackendV2(num_qubits=4, seed=42) transpiled_qc = transpile( qc, backend=backend, @@ -853,7 +863,7 @@ def test_do_not_run_gatedirection_with_symmetric_cm(self): circ = QuantumCircuit.from_qasm_file(os.path.join(qasm_dir, "example.qasm")) layout = Layout.generate_trivial_layout(*circ.qregs) coupling_map = [] - for node1, node2 in GenericBackendV2(num_qubits=16).coupling_map: + for node1, node2 in GenericBackendV2(num_qubits=16, seed=42).coupling_map: coupling_map.append([node1, node2]) coupling_map.append([node2, node1]) @@ -912,7 +922,7 @@ def test_pass_manager_empty(self): def test_move_measurements(self): """Measurements applied AFTER swap mapping.""" - cmap = GenericBackendV2(num_qubits=16).coupling_map + cmap = GenericBackendV2(num_qubits=16, seed=42).coupling_map qasm_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "qasm") circ = QuantumCircuit.from_qasm_file(os.path.join(qasm_dir, "move_measurements.qasm")) @@ -956,7 +966,7 @@ def test_initialize_FakeMelbourne(self): qc = QuantumCircuit(qr) qc.initialize(desired_vector, [qr[0], qr[1], qr[2]]) - out = transpile(qc, backend=GenericBackendV2(num_qubits=4)) + out = transpile(qc, backend=GenericBackendV2(num_qubits=4, seed=42)) out_dag = circuit_to_dag(out) reset_nodes = out_dag.named_nodes("reset") @@ -1275,7 +1285,7 @@ def test_transpiled_custom_gates_calibration(self): transpiled_circuit = transpile( circ, - backend=GenericBackendV2(num_qubits=4), + backend=GenericBackendV2(num_qubits=4, seed=42), layout_method="trivial", seed_transpiler=42, ) @@ -1318,6 +1328,7 @@ def test_transpile_calibrated_custom_gate_on_diff_qubit(self): backend=GenericBackendV2(num_qubits=4), layout_method="trivial", seed_transpiler=42, + optimization_level=1, ) def test_transpile_calibrated_nonbasis_gate_on_diff_qubit(self): @@ -1334,7 +1345,7 @@ def test_transpile_calibrated_nonbasis_gate_on_diff_qubit(self): circ.add_calibration("h", [1], q0_x180) transpiled_circuit = transpile( - circ, backend=GenericBackendV2(num_qubits=4), seed_transpiler=42 + circ, backend=GenericBackendV2(num_qubits=4), seed_transpiler=42, optimization_level=1 ) self.assertEqual(transpiled_circuit.calibrations, circ.calibrations) self.assertEqual(set(transpiled_circuit.count_ops().keys()), {"rz", "sx", "h"}) @@ -1511,8 +1522,15 @@ def test_scheduling_timing_constraints(self): """Test that scheduling-related loose transpile constraints work with both BackendV1 and BackendV2.""" - backend_v1 = Fake27QPulseV1() - backend_v2 = BackendV2Converter(backend_v1) + with self.assertWarns(DeprecationWarning): + backend_v1 = Fake27QPulseV1() + backend_v2 = GenericBackendV2( + num_qubits=27, + calibrate_instructions=True, + control_flow=True, + coupling_map=MUMBAI_CMAP, + seed=42, + ) # the original timing constraints are granularity = min_length = 16 timing_constraints = TimingConstraints(granularity=32, min_length=64) error_msgs = { @@ -1546,7 +1564,8 @@ def test_scheduling_instruction_constraints(self): """Test that scheduling-related loose transpile constraints work with both BackendV1 and BackendV2.""" - backend_v1 = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend_v1 = Fake27QPulseV1() backend_v2 = BackendV2Converter(backend_v1) qc = QuantumCircuit(2) qc.h(0) @@ -1571,7 +1590,8 @@ def test_scheduling_dt_constraints(self): """Test that scheduling-related loose transpile constraints work with both BackendV1 and BackendV2.""" - backend_v1 = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend_v1 = Fake27QPulseV1() backend_v2 = BackendV2Converter(backend_v1) qc = QuantumCircuit(1, 1) qc.x(0) @@ -1591,7 +1611,8 @@ def test_backend_props_constraints(self): """Test that loose transpile constraints work with both BackendV1 and BackendV2.""" - backend_v1 = Fake20QV1() + with self.assertWarns(DeprecationWarning): + backend_v1 = Fake20QV1() backend_v2 = BackendV2Converter(backend_v1) qr1 = QuantumRegister(3, "qr1") qr2 = QuantumRegister(2, "qr2") @@ -1781,7 +1802,7 @@ def test_approximation_degree_invalid(self): ) def test_approximation_degree(self): - """Test more approximation gives lower-cost circuit.""" + """Test more approximation can give lower-cost circuit.""" circuit = QuantumCircuit(2) circuit.swap(0, 1) circuit.h(0) @@ -1791,6 +1812,7 @@ def test_approximation_degree(self): translation_method="synthesis", approximation_degree=0.1, seed_transpiler=42, + optimization_level=1, ) circ_90 = transpile( circuit, @@ -1798,6 +1820,7 @@ def test_approximation_degree(self): translation_method="synthesis", approximation_degree=0.9, seed_transpiler=42, + optimization_level=1, ) self.assertLess(circ_10.depth(), circ_90.depth()) @@ -2068,6 +2091,21 @@ def test_transpile_annotated_ops(self, opt_level): self.assertEqual(Operator(qc), Operator(transpiled)) self.assertEqual(Operator(qc), Operator(expected)) + @combine(opt_level=[0, 1, 2, 3]) + def test_transpile_annotated_ops_with_backend_v1(self, opt_level): + """Test transpilation of circuits with annotated operations given a backend. + Remove once Fake20QV1 is removed.""" + qc = QuantumCircuit(3) + qc.append(AnnotatedOperation(SGate(), InverseModifier()), [0]) + qc.append(AnnotatedOperation(XGate(), ControlModifier(1)), [1, 2]) + qc.append(AnnotatedOperation(HGate(), PowerModifier(3)), [2]) + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() + transpiled = transpile( + qc, optimization_level=opt_level, backend=backend, seed_transpiler=42 + ) + self.assertLessEqual(set(transpiled.count_ops().keys()), {"u1", "u2", "u3", "cx"}) + @combine(opt_level=[0, 1, 2, 3]) def test_transpile_annotated_ops_with_backend(self, opt_level): """Test transpilation of circuits with annotated operations given a backend.""" @@ -2075,7 +2113,12 @@ def test_transpile_annotated_ops_with_backend(self, opt_level): qc.append(AnnotatedOperation(SGate(), InverseModifier()), [0]) qc.append(AnnotatedOperation(XGate(), ControlModifier(1)), [1, 2]) qc.append(AnnotatedOperation(HGate(), PowerModifier(3)), [2]) - backend = Fake20QV1() + + backend = GenericBackendV2( + num_qubits=20, + coupling_map=TOKYO_CMAP, + basis_gates=["id", "u1", "u2", "u3", "cx"], + ) transpiled = transpile( qc, optimization_level=opt_level, backend=backend, seed_transpiler=42 ) @@ -2383,9 +2426,32 @@ def test_qpy_roundtrip_standalone_var_target(self, optimization_level): @data(0, 1, 2, 3) def test_qasm3_output(self, optimization_level): """Test that the output of a transpiled circuit can be dumped into OpenQASM 3.""" + backend = GenericBackendV2( + num_qubits=20, + coupling_map=TOKYO_CMAP, + basis_gates=["id", "u1", "u2", "u3", "cx"], + ) + transpiled = transpile( self._regular_circuit(), - backend=Fake20QV1(), + backend=backend, + optimization_level=optimization_level, + seed_transpiler=2022_10_17, + ) + # TODO: There's not a huge amount we can sensibly test for the output here until we can + # round-trip the OpenQASM 3 back into a Terra circuit. Mostly we're concerned that the dump + # itself doesn't throw an error, though. + self.assertIsInstance(qasm3.dumps(transpiled).strip(), str) + + @data(0, 1, 2, 3) + def test_qasm3_output_v1(self, optimization_level): + """Test that the output of a transpiled circuit can be dumped into OpenQASM 3 (backend V1).""" + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() + + transpiled = transpile( + self._regular_circuit(), + backend=backend, optimization_level=optimization_level, seed_transpiler=2022_10_17, ) diff --git a/test/python/legacy_cmaps.py b/test/python/legacy_cmaps.py index 08d2f043ca4e..84f4be45128b 100644 --- a/test/python/legacy_cmaps.py +++ b/test/python/legacy_cmaps.py @@ -426,3 +426,151 @@ [64, 54], [64, 63], ] + +# 127 qubits +KYOTO_CMAP = [ + [0, 14], + [1, 0], + [1, 2], + [3, 2], + [4, 3], + [4, 5], + [6, 5], + [7, 6], + [8, 7], + [8, 9], + [8, 16], + [9, 10], + [11, 10], + [11, 12], + [12, 13], + [15, 4], + [16, 26], + [17, 12], + [17, 30], + [18, 14], + [18, 19], + [19, 20], + [21, 20], + [22, 15], + [22, 21], + [22, 23], + [23, 24], + [25, 24], + [25, 26], + [27, 26], + [27, 28], + [28, 29], + [28, 35], + [30, 29], + [30, 31], + [31, 32], + [32, 36], + [33, 20], + [33, 39], + [34, 24], + [34, 43], + [37, 38], + [38, 39], + [39, 40], + [40, 41], + [42, 41], + [43, 42], + [44, 43], + [44, 45], + [46, 45], + [47, 35], + [47, 46], + [48, 47], + [49, 48], + [49, 55], + [50, 49], + [50, 51], + [51, 36], + [52, 37], + [53, 41], + [53, 60], + [54, 45], + [54, 64], + [55, 68], + [56, 52], + [57, 56], + [57, 58], + [59, 58], + [59, 60], + [61, 60], + [62, 61], + [62, 63], + [63, 64], + [64, 65], + [65, 66], + [67, 66], + [67, 68], + [68, 69], + [70, 69], + [71, 58], + [72, 62], + [73, 66], + [73, 85], + [74, 70], + [75, 90], + [76, 75], + [76, 77], + [77, 71], + [77, 78], + [79, 78], + [79, 91], + [80, 79], + [81, 72], + [81, 80], + [82, 81], + [82, 83], + [83, 92], + [84, 83], + [84, 85], + [86, 85], + [87, 86], + [87, 93], + [88, 87], + [89, 74], + [89, 88], + [93, 106], + [94, 90], + [94, 95], + [96, 95], + [96, 97], + [96, 109], + [97, 98], + [98, 91], + [98, 99], + [99, 100], + [101, 100], + [101, 102], + [102, 92], + [102, 103], + [104, 103], + [104, 111], + [105, 104], + [105, 106], + [106, 107], + [107, 108], + [109, 114], + [110, 100], + [112, 108], + [113, 114], + [115, 114], + [116, 115], + [117, 116], + [118, 110], + [118, 117], + [119, 118], + [120, 119], + [121, 120], + [122, 111], + [122, 121], + [122, 123], + [124, 123], + [125, 124], + [125, 126], + [126, 112], +] diff --git a/test/python/primitives/containers/test_bit_array.py b/test/python/primitives/containers/test_bit_array.py index e4963d046f51..fb99ab0332e4 100644 --- a/test/python/primitives/containers/test_bit_array.py +++ b/test/python/primitives/containers/test_bit_array.py @@ -232,6 +232,7 @@ def convert(counts: Counts): counts1 = convert(Counts({"0b101010": 2, "0b1": 3, "0x010203": 4})) counts2 = convert(Counts({1: 3, 2: 6})) + counts3 = convert(Counts({0: 2})) bit_array = BitArray.from_counts(counts1) expected = BitArray(u_8([[0, 0, 42]] * 2 + [[0, 0, 1]] * 3 + [[1, 2, 3]] * 4), 17) @@ -248,6 +249,10 @@ def convert(counts: Counts): ] self.assertEqual(bit_array, BitArray(u_8(expected), 17)) + bit_array = BitArray.from_counts(counts3) + expected = BitArray(u_8([[0], [0]]), 1) + self.assertEqual(bit_array, expected) + def test_from_samples_bitstring(self): """Test the from_samples static constructor.""" bit_array = BitArray.from_samples(["110", "1", "1111111111"]) @@ -256,6 +261,9 @@ def test_from_samples_bitstring(self): bit_array = BitArray.from_samples(["110", "1", "1111111111"], 20) self.assertEqual(bit_array, BitArray(u_8([[0, 0, 6], [0, 0, 1], [0, 3, 255]]), 20)) + bit_array = BitArray.from_samples(["000", "0"]) + self.assertEqual(bit_array, BitArray(u_8([[0], [0]]), 1)) + def test_from_samples_hex(self): """Test the from_samples static constructor.""" bit_array = BitArray.from_samples(["0x01", "0x0a12", "0x0105"]) @@ -264,6 +272,9 @@ def test_from_samples_hex(self): bit_array = BitArray.from_samples(["0x01", "0x0a12", "0x0105"], 20) self.assertEqual(bit_array, BitArray(u_8([[0, 0, 1], [0, 10, 18], [0, 1, 5]]), 20)) + bit_array = BitArray.from_samples(["0x0", "0x0"]) + self.assertEqual(bit_array, BitArray(u_8([[0], [0]]), 1)) + def test_from_samples_int(self): """Test the from_samples static constructor.""" bit_array = BitArray.from_samples([1, 2578, 261]) @@ -272,6 +283,9 @@ def test_from_samples_int(self): bit_array = BitArray.from_samples([1, 2578, 261], 20) self.assertEqual(bit_array, BitArray(u_8([[0, 0, 1], [0, 10, 18], [0, 1, 5]]), 20)) + bit_array = BitArray.from_samples([0, 0, 0]) + self.assertEqual(bit_array, BitArray(u_8([[0], [0], [0]]), 1)) + def test_reshape(self): """Test the reshape method.""" # this creates incrementing bitstrings from 0 to 360 * 32 - 1 @@ -523,6 +537,20 @@ def test_getitem(self): for j in range(2): self.assertEqual(ba.get_counts((0, j, 2)), ba2.get_counts(j)) + with self.subTest("errors"): + with self.assertRaisesRegex(IndexError, "index 2 is out of bounds"): + _ = ba[0, 2, 2] + with self.assertRaisesRegex(IndexError, "index -3 is out of bounds"): + _ = ba[0, -3, 2] + with self.assertRaisesRegex( + IndexError, "BitArray cannot be sliced along the shots axis" + ): + _ = ba[0, 1, 2, 3] + with self.assertRaisesRegex( + IndexError, "BitArray cannot be sliced along the bits axis" + ): + _ = ba[0, 1, 2, 3, 4] + def test_slice_bits(self): """Test the slice_bits method.""" # this creates incrementing bitstrings from 0 to 59 @@ -567,9 +595,9 @@ def test_slice_bits(self): self.assertEqual(ba2.get_counts((i, j, k)), expect) with self.subTest("errors"): - with self.assertRaisesRegex(ValueError, "index -1 is out of bounds"): + with self.assertRaisesRegex(IndexError, "index -1 is out of bounds"): _ = ba.slice_bits(-1) - with self.assertRaisesRegex(ValueError, "index 9 is out of bounds"): + with self.assertRaisesRegex(IndexError, "index 9 is out of bounds"): _ = ba.slice_bits(9) def test_slice_shots(self): @@ -617,9 +645,9 @@ def test_slice_shots(self): self.assertEqual(ba2.get_bitstrings((i, j, k)), expected) with self.subTest("errors"): - with self.assertRaisesRegex(ValueError, "index -1 is out of bounds"): + with self.assertRaisesRegex(IndexError, "index -1 is out of bounds"): _ = ba.slice_shots(-1) - with self.assertRaisesRegex(ValueError, "index 10 is out of bounds"): + with self.assertRaisesRegex(IndexError, "index 10 is out of bounds"): _ = ba.slice_shots(10) def test_expectation_values(self): @@ -705,3 +733,82 @@ def test_expectation_values(self): _ = ba.expectation_values("Z") with self.assertRaisesRegex(ValueError, "is not diagonal"): _ = ba.expectation_values("X" * ba.num_bits) + + def test_postselection(self): + """Test the postselection method.""" + + flat_data = np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + ], + dtype=bool, + ) + + shaped_data = np.array( + [ + [ + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + ], + [ + [1, 0, 1, 0, 1, 0, 1, 0, 1, 0], + [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + ], + ] + ], + dtype=bool, + ) + + for dataname, bool_array in zip(["flat", "shaped"], [flat_data, shaped_data]): + + bit_array = BitArray.from_bool_array(bool_array, order="little") + # indices value of i <-> creg[i] <-> bool_array[..., i] + + num_bits = bool_array.shape[-1] + bool_array = bool_array.reshape(-1, num_bits) + + test_cases = [ + ("basic", [0, 1], [0, 0]), + ("multibyte", [0, 9], [0, 1]), + ("repeated", [5, 5, 5], [0, 0, 0]), + ("contradict", [5, 5, 5], [1, 0, 0]), + ("unsorted", [5, 0, 9, 3], [1, 0, 1, 0]), + ("negative", [-5, 1, -2, -10], [1, 0, 1, 0]), + ("negcontradict", [4, -6], [1, 0]), + ("trivial", [], []), + ("bareindex", 6, 0), + ] + + for name, indices, selection in test_cases: + with self.subTest("_".join([dataname, name])): + postselected_bools = np.unpackbits( + bit_array.postselect(indices, selection).array[:, ::-1], + count=num_bits, + axis=-1, + bitorder="little", + ).astype(bool) + if isinstance(indices, int): + indices = (indices,) + if isinstance(selection, bool): + selection = (selection,) + answer = bool_array[np.all(bool_array[:, indices] == selection, axis=-1)] + if name in ["contradict", "negcontradict"]: + self.assertEqual(len(answer), 0) + else: + self.assertGreater(len(answer), 0) + np.testing.assert_equal(postselected_bools, answer) + + error_cases = [ + ("aboverange", [0, 6, 10], [True, True, False], IndexError), + ("belowrange", [0, 6, -11], [True, True, False], IndexError), + ("mismatch", [0, 1, 2], [False, False], ValueError), + ] + for name, indices, selection, error in error_cases: + with self.subTest(dataname + "_" + name): + with self.assertRaises(error): + bit_array.postselect(indices, selection) diff --git a/test/python/primitives/test_backend_estimator.py b/test/python/primitives/test_backend_estimator.py index 9725b865b4c4..80b471b66063 100644 --- a/test/python/primitives/test_backend_estimator.py +++ b/test/python/primitives/test_backend_estimator.py @@ -88,14 +88,16 @@ def test_estimator_run(self, backend): psi1, psi2 = self.psi hamiltonian1, hamiltonian2, hamiltonian3 = self.hamiltonian theta1, theta2, theta3 = self.theta - estimator = BackendEstimator(backend=backend) + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend=backend) estimator.set_options(seed_simulator=123) # Specify the circuit and observable by indices. # calculate [ ] - job = estimator.run([psi1], [hamiltonian1], [theta1]) - result = job.result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + job = estimator.run([psi1], [hamiltonian1], [theta1]) + result = job.result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1.5555572817900956], rtol=0.5, atol=0.2) # Objects can be passed instead of indices. @@ -103,19 +105,26 @@ def test_estimator_run(self, backend): # since the corresponding indices need to be searched. # User can append a circuit and observable. # calculate [ ] - result2 = estimator.run([psi2], [hamiltonian1], [theta2]).result() + with self.assertWarns(DeprecationWarning): + result2 = estimator.run([psi2], [hamiltonian1], [theta2]).result() np.testing.assert_allclose(result2.values, [2.97797666], rtol=0.5, atol=0.2) # calculate [ , ] - result3 = estimator.run([psi1, psi1], [hamiltonian2, hamiltonian3], [theta1] * 2).result() + with self.assertWarns(DeprecationWarning): + result3 = estimator.run( + [psi1, psi1], [hamiltonian2, hamiltonian3], [theta1] * 2 + ).result() np.testing.assert_allclose(result3.values, [-0.551653, 0.07535239], rtol=0.5, atol=0.2) # calculate [ , # , # ] - result4 = estimator.run( - [psi1, psi2, psi1], [hamiltonian1, hamiltonian2, hamiltonian3], [theta1, theta2, theta3] - ).result() + with self.assertWarns(DeprecationWarning): + result4 = estimator.run( + [psi1, psi2, psi1], + [hamiltonian1, hamiltonian2, hamiltonian3], + [theta1, theta2, theta3], + ).result() np.testing.assert_allclose( result4.values, [1.55555728, 0.17849238, -1.08766318], rtol=0.5, atol=0.2 ) @@ -124,10 +133,11 @@ def test_estimator_run(self, backend): def test_estimator_run_no_params(self, backend): """test for estimator without parameters""" circuit = self.ansatz.assign_parameters([0, 1, 1, 2, 3, 5]) - est = BackendEstimator(backend=backend) - est.set_options(seed_simulator=123) - result = est.run([circuit], [self.observable]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + est = BackendEstimator(backend=backend) + est.set_options(seed_simulator=123) + result = est.run([circuit], [self.observable]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1.284366511861733], rtol=0.05) @combine(backend=BACKENDS, creg=[True, False]) @@ -140,22 +150,26 @@ def test_run_1qubit(self, backend, creg): op = SparsePauliOp.from_list([("I", 1)]) op2 = SparsePauliOp.from_list([("Z", 1)]) - est = BackendEstimator(backend=backend) - est.set_options(seed_simulator=123) - result = est.run([qc], [op], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + est = BackendEstimator(backend=backend) + est.set_options(seed_simulator=123) + result = est.run([qc], [op], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) - result = est.run([qc], [op2], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc], [op2], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) - result = est.run([qc2], [op], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) - result = est.run([qc2], [op2], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op2], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1], rtol=0.1) @combine(backend=BACKENDS, creg=[True, False]) @@ -170,29 +184,35 @@ def test_run_2qubits(self, backend, creg): op2 = SparsePauliOp.from_list([("ZI", 1)]) op3 = SparsePauliOp.from_list([("IZ", 1)]) - est = BackendEstimator(backend=backend) - result = est.run([qc], [op], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + est = BackendEstimator(backend=backend) + result = est.run([qc], [op], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) - result = est.run([qc2], [op], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) - result = est.run([qc], [op2], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc], [op2], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) - result = est.run([qc2], [op2], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op2], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) - result = est.run([qc], [op3], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc], [op3], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1], rtol=0.1) - result = est.run([qc2], [op3], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op3], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1], rtol=0.1) @combine(backend=BACKENDS) @@ -204,18 +224,19 @@ def test_run_errors(self, backend): op = SparsePauliOp.from_list([("I", 1)]) op2 = SparsePauliOp.from_list([("II", 1)]) - est = BackendEstimator(backend=backend) - est.set_options(seed_simulator=123) - with self.assertRaises(ValueError): - est.run([qc], [op2], [[]]).result() - with self.assertRaises(ValueError): - est.run([qc], [op], [[1e4]]).result() - with self.assertRaises(ValueError): - est.run([qc2], [op2], [[1, 2]]).result() - with self.assertRaises(ValueError): - est.run([qc, qc2], [op2], [[1]]).result() - with self.assertRaises(ValueError): - est.run([qc], [op, op2], [[1]]).result() + with self.assertWarns(DeprecationWarning): + est = BackendEstimator(backend=backend) + est.set_options(seed_simulator=123) + with self.assertRaises(ValueError): + est.run([qc], [op2], [[]]).result() + with self.assertRaises(ValueError): + est.run([qc], [op], [[1e4]]).result() + with self.assertRaises(ValueError): + est.run([qc2], [op2], [[1, 2]]).result() + with self.assertRaises(ValueError): + est.run([qc, qc2], [op2], [[1]]).result() + with self.assertRaises(ValueError): + est.run([qc], [op, op2], [[1]]).result() @combine(backend=BACKENDS) def test_run_numpy_params(self, backend): @@ -226,52 +247,58 @@ def test_run_numpy_params(self, backend): params_array = self._rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) - estimator = BackendEstimator(backend=backend) - estimator.set_options(seed_simulator=123) + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend=backend) + estimator.set_options(seed_simulator=123) - target = estimator.run([qc] * k, [op] * k, params_list).result() + target = estimator.run([qc] * k, [op] * k, params_list).result() with self.subTest("ndarrary"): - result = estimator.run([qc] * k, [op] * k, params_array).result() + with self.assertWarns(DeprecationWarning): + result = estimator.run([qc] * k, [op] * k, params_array).result() self.assertEqual(len(result.metadata), k) np.testing.assert_allclose(result.values, target.values, rtol=0.2, atol=0.2) with self.subTest("list of ndarray"): - result = estimator.run([qc] * k, [op] * k, params_list_array).result() + with self.assertWarns(DeprecationWarning): + result = estimator.run([qc] * k, [op] * k, params_list_array).result() self.assertEqual(len(result.metadata), k) np.testing.assert_allclose(result.values, target.values, rtol=0.2, atol=0.2) @combine(backend=BACKENDS) def test_run_with_shots_option(self, backend): """test with shots option.""" - est = BackendEstimator(backend=backend) - result = est.run( - [self.ansatz], - [self.observable], - parameter_values=[[0, 1, 1, 2, 3, 5]], - shots=1024, - seed_simulator=15, - ).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + est = BackendEstimator(backend=backend) + result = est.run( + [self.ansatz], + [self.observable], + parameter_values=[[0, 1, 1, 2, 3, 5]], + shots=1024, + seed_simulator=15, + ).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1.307397243478641], rtol=0.1) @combine(backend=BACKENDS) def test_options(self, backend): """Test for options""" with self.subTest("init"): - estimator = BackendEstimator(backend=backend, options={"shots": 3000}) + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend=backend, options={"shots": 3000}) self.assertEqual(estimator.options.get("shots"), 3000) with self.subTest("set_options"): estimator.set_options(shots=1024, seed_simulator=15) self.assertEqual(estimator.options.get("shots"), 1024) self.assertEqual(estimator.options.get("seed_simulator"), 15) with self.subTest("run"): - result = estimator.run( - [self.ansatz], - [self.observable], - parameter_values=[[0, 1, 1, 2, 3, 5]], - ).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = estimator.run( + [self.ansatz], + [self.observable], + parameter_values=[[0, 1, 1, 2, 3, 5]], + ).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1.307397243478641], rtol=0.1) def test_job_size_limit_v2(self): @@ -284,21 +311,25 @@ class FakeBackendLimitedCircuits(GenericBackendV2): def max_circuits(self): return 1 - backend = FakeBackendLimitedCircuits(num_qubits=5) + backend = FakeBackendLimitedCircuits(num_qubits=5, seed=42) backend.set_options(seed_simulator=123) qc = RealAmplitudes(num_qubits=2, reps=2) op = SparsePauliOp.from_list([("IZ", 1), ("XI", 2), ("ZY", -1)]) k = 5 params_array = self._rng.random((k, qc.num_parameters)) params_list = params_array.tolist() - estimator = BackendEstimator(backend=backend) + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend=backend) with patch.object(backend, "run") as run_mock: - estimator.run([qc] * k, [op] * k, params_list).result() + with self.assertWarns(DeprecationWarning): + estimator.run([qc] * k, [op] * k, params_list).result() self.assertEqual(run_mock.call_count, 10) def test_job_size_limit_v1(self): - """Test BackendEstimator respects job size limit""" - backend = Fake7QPulseV1() + """Test BackendEstimator respects job size limit + REMOVE ONCE Fake7QPulseV1 GETS REMOVED""" + with self.assertWarns(DeprecationWarning): + backend = Fake7QPulseV1() config = backend.configuration() config.max_experiments = 1 backend._configuration = config @@ -307,15 +338,19 @@ def test_job_size_limit_v1(self): k = 5 params_array = self._rng.random((k, qc.num_parameters)) params_list = params_array.tolist() - estimator = BackendEstimator(backend=backend) + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend=backend) estimator.set_options(seed_simulator=123) with patch.object(backend, "run") as run_mock: - estimator.run([qc] * k, [op] * k, params_list).result() + with self.assertWarns(DeprecationWarning): + estimator.run([qc] * k, [op] * k, params_list).result() self.assertEqual(run_mock.call_count, 10) def test_no_max_circuits(self): - """Test BackendEstimator works with BackendV1 and no max_experiments set.""" - backend = Fake7QPulseV1() + """Test BackendEstimator works with BackendV1 and no max_experiments set. + REMOVE ONCE Fake7QPulseV1 GETS REMOVED""" + with self.assertWarns(DeprecationWarning): + backend = Fake7QPulseV1() config = backend.configuration() del config.max_experiments backend._configuration = config @@ -325,16 +360,19 @@ def test_no_max_circuits(self): params_array = self._rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) - estimator = BackendEstimator(backend=backend) - estimator.set_options(seed_simulator=123) - target = estimator.run([qc] * k, [op] * k, params_list).result() + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend=backend) + estimator.set_options(seed_simulator=123) + target = estimator.run([qc] * k, [op] * k, params_list).result() with self.subTest("ndarrary"): - result = estimator.run([qc] * k, [op] * k, params_array).result() + with self.assertWarns(DeprecationWarning): + result = estimator.run([qc] * k, [op] * k, params_array).result() self.assertEqual(len(result.metadata), k) np.testing.assert_allclose(result.values, target.values, rtol=0.2, atol=0.2) with self.subTest("list of ndarray"): - result = estimator.run([qc] * k, [op] * k, params_list_array).result() + with self.assertWarns(DeprecationWarning): + result = estimator.run([qc] * k, [op] * k, params_list_array).result() self.assertEqual(len(result.metadata), k) np.testing.assert_allclose(result.values, target.values, rtol=0.2, atol=0.2) @@ -352,8 +390,11 @@ def callback(msg): bound_counter = CallbackPass("bound_pass_manager", callback) bound_pass = PassManager(bound_counter) - estimator = BackendEstimator(backend=Fake7QPulseV1(), bound_pass_manager=bound_pass) - _ = estimator.run(qc, op).result() + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator( + backend=GenericBackendV2(num_qubits=5, seed=42), bound_pass_manager=bound_pass + ) + _ = estimator.run(qc, op).result() expected = [ "bound_pass_manager", ] @@ -372,8 +413,12 @@ def callback(msg): # pylint: disable=function-redefined bound_counter = CallbackPass("bound_pass_manager", callback) bound_pass = PassManager(bound_counter) - estimator = BackendEstimator(backend=Fake7QPulseV1(), bound_pass_manager=bound_pass) - _ = estimator.run([qc, qc], [op, op]).result() + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator( + backend=GenericBackendV2(num_qubits=5, seed=42), + bound_pass_manager=bound_pass, + ) + _ = estimator.run([qc, qc], [op, op]).result() expected = [ "bound_pass_manager", "bound_pass_manager", @@ -390,9 +435,10 @@ def test_layout(self, backend): qc.cx(0, 2) op = SparsePauliOp("IZI") backend.set_options(seed_simulator=15) - estimator = BackendEstimator(backend) - estimator.set_transpile_options(seed_transpiler=15) - value = estimator.run(qc, op, shots=10000).result().values[0] + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend) + estimator.set_transpile_options(seed_transpiler=15, optimization_level=1) + value = estimator.run(qc, op, shots=10000).result().values[0] if optionals.HAS_AER: ref_value = -0.9954 if isinstance(backend, GenericBackendV2) else -0.916 else: @@ -405,10 +451,13 @@ def test_layout(self, backend): qc.cx(0, 1) qc.cx(0, 2) op = SparsePauliOp("IZI") - estimator = BackendEstimator(backend) - estimator.set_transpile_options(initial_layout=[0, 1, 2], seed_transpiler=15) - estimator.set_options(seed_simulator=15) - value = estimator.run(qc, op, shots=10000).result().values[0] + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend) + estimator.set_transpile_options( + initial_layout=[0, 1, 2], seed_transpiler=15, optimization_level=1 + ) + estimator.set_options(seed_simulator=15) + value = estimator.run(qc, op, shots=10000).result().values[0] if optionals.HAS_AER: ref_value = -0.9954 if isinstance(backend, GenericBackendV2) else -0.8902 else: @@ -428,9 +477,10 @@ def test_circuit_with_measurement(self): backend = AerSimulator() backend.set_options(seed_simulator=15) - estimator = BackendEstimator(backend, skip_transpilation=True) - estimator.set_transpile_options(seed_transpiler=15) - result = estimator.run(bell, observable).result() + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend, skip_transpilation=True) + estimator.set_transpile_options(seed_transpiler=15) + result = estimator.run(bell, observable).result() self.assertAlmostEqual(result.values[0], 1, places=1) @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required to run this test") @@ -449,9 +499,10 @@ def test_dynamic_circuit(self): backend = AerSimulator() backend.set_options(seed_simulator=15) - estimator = BackendEstimator(backend, skip_transpilation=True) - estimator.set_transpile_options(seed_transpiler=15) - result = estimator.run(qc, observable).result() + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend, skip_transpilation=True) + estimator.set_transpile_options(seed_transpiler=15) + result = estimator.run(qc, observable).result() self.assertAlmostEqual(result.values[0], 0, places=1) diff --git a/test/python/primitives/test_backend_estimator_v2.py b/test/python/primitives/test_backend_estimator_v2.py index 6728d57e3fdd..319fd846ee95 100644 --- a/test/python/primitives/test_backend_estimator_v2.py +++ b/test/python/primitives/test_backend_estimator_v2.py @@ -446,8 +446,9 @@ def max_circuits(self): self.assertEqual(run_mock.call_count, 10) def test_job_size_limit_backend_v1(self): - """Test BackendEstimatorV2 respects job size limit""" - backend = Fake7QPulseV1() + """Test BackendEstimatorV2 respects job size limit from BackendV1""" + with self.assertWarns(DeprecationWarning): + backend = Fake7QPulseV1() config = backend.configuration() config.max_experiments = 1 backend._configuration = config diff --git a/test/python/primitives/test_backend_sampler.py b/test/python/primitives/test_backend_sampler.py index 8d1a3e2aab2b..f0fdb4f07f8c 100644 --- a/test/python/primitives/test_backend_sampler.py +++ b/test/python/primitives/test_backend_sampler.py @@ -113,10 +113,11 @@ def _compare_probs(self, prob, target): def test_sampler_run(self, backend): """Test Sampler.run().""" bell = self._circuit[1] - sampler = BackendSampler(backend=backend) - job = sampler.run(circuits=[bell], shots=1000) - result = job.result() - self.assertIsInstance(result, SamplerResult) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + job = sampler.run(circuits=[bell], shots=1000) + result = job.result() + self.assertIsInstance(result, SamplerResult) self.assertEqual(result.quasi_dists[0].shots, 1000) self.assertEqual(result.quasi_dists[0].stddev_upper_bound, math.sqrt(1 / 1000)) self._compare_probs(result.quasi_dists, self._target[1]) @@ -127,8 +128,9 @@ def test_sample_run_multiple_circuits(self, backend): # executes three Bell circuits # Argument `parameters` is optional. bell = self._circuit[1] - sampler = BackendSampler(backend=backend) - result = sampler.run([bell, bell, bell]).result() + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + result = sampler.run([bell, bell, bell]).result() self._compare_probs(result.quasi_dists[0], self._target[1]) self._compare_probs(result.quasi_dists[1], self._target[1]) self._compare_probs(result.quasi_dists[2], self._target[1]) @@ -142,8 +144,9 @@ def test_sampler_run_with_parameterized_circuits(self, backend): pqc2 = self._pqc2 theta1, theta2, theta3 = self._theta - sampler = BackendSampler(backend=backend) - result = sampler.run([pqc, pqc, pqc2], [theta1, theta2, theta3]).result() + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + result = sampler.run([pqc, pqc, pqc2], [theta1, theta2, theta3]).result() # result of pqc(theta1) prob1 = { @@ -181,9 +184,10 @@ def test_run_1qubit(self, backend): qc2.x(0) qc2.measure_all() - sampler = BackendSampler(backend=backend) - result = sampler.run([qc, qc2]).result() - self.assertIsInstance(result, SamplerResult) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + result = sampler.run([qc, qc2]).result() + self.assertIsInstance(result, SamplerResult) self.assertEqual(len(result.quasi_dists), 2) self.assertDictAlmostEqual(result.quasi_dists[0], {0: 1}, 0.1) @@ -204,9 +208,10 @@ def test_run_2qubit(self, backend): qc3.x([0, 1]) qc3.measure_all() - sampler = BackendSampler(backend=backend) - result = sampler.run([qc0, qc1, qc2, qc3]).result() - self.assertIsInstance(result, SamplerResult) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + result = sampler.run([qc0, qc1, qc2, qc3]).result() + self.assertIsInstance(result, SamplerResult) self.assertEqual(len(result.quasi_dists), 4) self.assertDictAlmostEqual(result.quasi_dists[0], {0: 1}, 0.1) @@ -222,13 +227,14 @@ def test_run_errors(self, backend): qc2 = RealAmplitudes(num_qubits=1, reps=1) qc2.measure_all() - sampler = BackendSampler(backend=backend) - with self.assertRaises(ValueError): - sampler.run([qc1], [[1e2]]).result() - with self.assertRaises(ValueError): - sampler.run([qc2], [[]]).result() - with self.assertRaises(ValueError): - sampler.run([qc2], [[1e2]]).result() + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + with self.assertRaises(ValueError): + sampler.run([qc1], [[1e2]]).result() + with self.assertRaises(ValueError): + sampler.run([qc2], [[]]).result() + with self.assertRaises(ValueError): + sampler.run([qc2], [[1e2]]).result() @combine(backend=BACKENDS) def test_run_empty_parameter(self, backend): @@ -236,7 +242,8 @@ def test_run_empty_parameter(self, backend): n = 5 qc = QuantumCircuit(n, n - 1) qc.measure(range(n - 1), range(n - 1)) - sampler = BackendSampler(backend=backend) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) with self.subTest("one circuit"): result = sampler.run([qc], shots=1000).result() self.assertEqual(len(result.quasi_dists), 1) @@ -263,8 +270,9 @@ def test_run_numpy_params(self, backend): params_array = rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) - sampler = BackendSampler(backend=backend) - target = sampler.run([qc] * k, params_list).result() + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + target = sampler.run([qc] * k, params_list).result() with self.subTest("ndarrary"): result = sampler.run([qc] * k, params_array).result() @@ -282,19 +290,21 @@ def test_run_numpy_params(self, backend): def test_run_with_shots_option(self, backend): """test with shots option.""" params, target = self._generate_params_target([1]) - sampler = BackendSampler(backend=backend) - result = sampler.run( - circuits=[self._pqc], parameter_values=params, shots=1024, seed=15 - ).result() + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + result = sampler.run( + circuits=[self._pqc], parameter_values=params, shots=1024, seed=15 + ).result() self._compare_probs(result.quasi_dists, target) @combine(backend=BACKENDS) def test_primitive_job_status_done(self, backend): """test primitive job's status""" bell = self._circuit[1] - sampler = BackendSampler(backend=backend) - job = sampler.run(circuits=[bell]) - _ = job.result() + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + job = sampler.run(circuits=[bell]) + _ = job.result() self.assertEqual(job.status(), JobStatus.DONE) def test_primitive_job_size_limit_backend_v2(self): @@ -312,7 +322,8 @@ def max_circuits(self): qc2 = QuantumCircuit(1) qc2.x(0) qc2.measure_all() - sampler = BackendSampler(backend=FakeBackendLimitedCircuits(num_qubits=5)) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=FakeBackendLimitedCircuits(num_qubits=5, seed=42)) result = sampler.run([qc, qc2]).result() self.assertIsInstance(result, SamplerResult) self.assertEqual(len(result.quasi_dists), 2) @@ -322,18 +333,18 @@ def max_circuits(self): def test_primitive_job_size_limit_backend_v1(self): """Test primitive respects backend's job size limit.""" - backend = Fake7QPulseV1() - config = backend.configuration() - config.max_experiments = 1 - backend._configuration = config + backend = GenericBackendV2( + 7, calibrate_instructions=True, basis_gates=["cx", "u1", "u2", "u3"], seed=42 + ) qc = QuantumCircuit(1) qc.measure_all() qc2 = QuantumCircuit(1) qc2.x(0) qc2.measure_all() - sampler = BackendSampler(backend=backend) - result = sampler.run([qc, qc2]).result() - self.assertIsInstance(result, SamplerResult) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) + result = sampler.run([qc, qc2]).result() + self.assertIsInstance(result, SamplerResult) self.assertEqual(len(result.quasi_dists), 2) self.assertDictAlmostEqual(result.quasi_dists[0], {0: 1}, 0.1) @@ -354,20 +365,24 @@ def test_circuit_with_dynamic_circuit(self): with self.assertWarns(DeprecationWarning): backend = Aer.get_backend("aer_simulator") - sampler = BackendSampler(backend, skip_transpilation=True) - sampler.set_options(seed_simulator=15) - sampler.set_transpile_options(seed_transpiler=15) - result = sampler.run(qc).result() + sampler = BackendSampler(backend, skip_transpilation=True) + sampler.set_options(seed_simulator=15) + sampler.set_transpile_options(seed_transpiler=15) + result = sampler.run(qc).result() self.assertDictAlmostEqual(result.quasi_dists[0], {0: 0.5029296875, 1: 0.4970703125}) def test_sequential_run(self): """Test sequential run.""" + backend = GenericBackendV2( + 7, calibrate_instructions=True, basis_gates=["cx", "u1", "u2", "u3"], seed=42 + ) qc = QuantumCircuit(1) qc.measure_all() qc2 = QuantumCircuit(1) qc2.x(0) qc2.measure_all() - sampler = BackendSampler(backend=Fake7QPulseV1()) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend) result = sampler.run([qc]).result() self.assertDictAlmostEqual(result.quasi_dists[0], {0: 1}, 0.1) result2 = sampler.run([qc2]).result() @@ -388,9 +403,10 @@ def test_outcome_bitstring_size(self): # We need a noise-free backend here (shot noise is fine) to ensure that # the only bit string measured is "0001". With device noise, it could happen that # strings with a leading 1 are measured and then the truncation cannot be tested. - sampler = BackendSampler(backend=BasicSimulator()) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=BasicSimulator()) - result = sampler.run(qc).result() + result = sampler.run(qc).result() probs = result.quasi_dists[0].binary_probabilities() self.assertIn("0001", probs.keys()) @@ -407,7 +423,11 @@ def callback(msg): bound_counter = CallbackPass("bound_pass_manager", callback) bound_pass = PassManager(bound_counter) - sampler = BackendSampler(backend=Fake7QPulseV1(), bound_pass_manager=bound_pass) + backend = GenericBackendV2( + 7, calibrate_instructions=True, basis_gates=["cx", "u1", "u2", "u3"], seed=42 + ) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend, bound_pass_manager=bound_pass) _ = sampler.run([self._circuit[0]]).result() expected = [ "bound_pass_manager", @@ -427,7 +447,11 @@ def callback(msg): # pylint: disable=function-redefined bound_counter = CallbackPass("bound_pass_manager", callback) bound_pass = PassManager(bound_counter) - sampler = BackendSampler(backend=Fake7QPulseV1(), bound_pass_manager=bound_pass) + backend = GenericBackendV2( + 7, calibrate_instructions=True, basis_gates=["cx", "u1", "u2", "u3"], seed=42 + ) + with self.assertWarns(DeprecationWarning): + sampler = BackendSampler(backend=backend, bound_pass_manager=bound_pass) _ = sampler.run([self._circuit[0], self._circuit[0]]).result() expected = [ "bound_pass_manager", diff --git a/test/python/primitives/test_backend_sampler_v2.py b/test/python/primitives/test_backend_sampler_v2.py index b03818846c82..632ed1984d2d 100644 --- a/test/python/primitives/test_backend_sampler_v2.py +++ b/test/python/primitives/test_backend_sampler_v2.py @@ -714,10 +714,9 @@ def max_circuits(self): def test_job_size_limit_backend_v1(self): """Test BackendSamplerV2 respects backend's job size limit.""" - backend = Fake7QPulseV1() - config = backend.configuration() - config.max_experiments = 1 - backend._configuration = config + backend = GenericBackendV2( + 2, calibrate_instructions=True, basis_gates=["cx", "u1", "u2", "u3"], seed=42 + ) qc = QuantumCircuit(1) qc.measure_all() qc2 = QuantumCircuit(1) diff --git a/test/python/primitives/test_estimator.py b/test/python/primitives/test_estimator.py index 535841cc90f7..783461c7e4ad 100644 --- a/test/python/primitives/test_estimator.py +++ b/test/python/primitives/test_estimator.py @@ -62,13 +62,14 @@ def test_estimator_run(self): psi1, psi2 = self.psi hamiltonian1, hamiltonian2, hamiltonian3 = self.hamiltonian theta1, theta2, theta3 = self.theta - estimator = Estimator() + with self.assertWarns(DeprecationWarning): + estimator = Estimator() - # Specify the circuit and observable by indices. - # calculate [ ] - job = estimator.run([psi1], [hamiltonian1], [theta1]) - result = job.result() - self.assertIsInstance(result, EstimatorResult) + # Specify the circuit and observable by indices. + # calculate [ ] + job = estimator.run([psi1], [hamiltonian1], [theta1]) + result = job.result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1.5555572817900956]) # Objects can be passed instead of indices. @@ -76,32 +77,41 @@ def test_estimator_run(self): # since the corresponding indices need to be searched. # User can append a circuit and observable. # calculate [ ] - result2 = estimator.run([psi2], [hamiltonian1], [theta2]).result() + with self.assertWarns(DeprecationWarning): + result2 = estimator.run([psi2], [hamiltonian1], [theta2]).result() np.testing.assert_allclose(result2.values, [2.97797666]) # calculate [ , ] - result3 = estimator.run([psi1, psi1], [hamiltonian2, hamiltonian3], [theta1] * 2).result() + with self.assertWarns(DeprecationWarning): + result3 = estimator.run( + [psi1, psi1], [hamiltonian2, hamiltonian3], [theta1] * 2 + ).result() np.testing.assert_allclose(result3.values, [-0.551653, 0.07535239]) # calculate [ , # , # ] - result4 = estimator.run( - [psi1, psi2, psi1], [hamiltonian1, hamiltonian2, hamiltonian3], [theta1, theta2, theta3] - ).result() + with self.assertWarns(DeprecationWarning): + result4 = estimator.run( + [psi1, psi2, psi1], + [hamiltonian1, hamiltonian2, hamiltonian3], + [theta1, theta2, theta3], + ).result() np.testing.assert_allclose(result4.values, [1.55555728, 0.17849238, -1.08766318]) def test_estiamtor_run_no_params(self): """test for estimator without parameters""" circuit = self.ansatz.assign_parameters([0, 1, 1, 2, 3, 5]) - est = Estimator() - result = est.run([circuit], [self.observable]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + est = Estimator() + result = est.run([circuit], [self.observable]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1.284366511861733]) def test_run_single_circuit_observable(self): """Test for single circuit and single observable case.""" - est = Estimator() + with self.assertWarns(DeprecationWarning): + est = Estimator() with self.subTest("No parameter"): qc = QuantumCircuit(1) @@ -111,7 +121,8 @@ def test_run_single_circuit_observable(self): target = [-1] for val in param_vals: self.subTest(f"{val}") - result = est.run(qc, op, val).result() + with self.assertWarns(DeprecationWarning): + result = est.run(qc, op, val).result() np.testing.assert_allclose(result.values, target) self.assertEqual(len(result.metadata), 1) @@ -130,7 +141,8 @@ def test_run_single_circuit_observable(self): target = [-1] for val in param_vals: self.subTest(f"{val}") - result = est.run(qc, op, val).result() + with self.assertWarns(DeprecationWarning): + result = est.run(qc, op, val).result() np.testing.assert_allclose(result.values, target) self.assertEqual(len(result.metadata), 1) @@ -147,7 +159,8 @@ def test_run_single_circuit_observable(self): target = [1.5555572817900956] for val in param_vals: self.subTest(f"{val}") - result = est.run(qc, op, val).result() + with self.assertWarns(DeprecationWarning): + result = est.run(qc, op, val).result() np.testing.assert_allclose(result.values, target) self.assertEqual(len(result.metadata), 1) @@ -160,21 +173,25 @@ def test_run_1qubit(self): op = SparsePauliOp.from_list([("I", 1)]) op2 = SparsePauliOp.from_list([("Z", 1)]) - est = Estimator() - result = est.run([qc], [op], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + est = Estimator() + result = est.run([qc], [op], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1]) - result = est.run([qc], [op2], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc], [op2], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1]) - result = est.run([qc2], [op], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1]) - result = est.run([qc2], [op2], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op2], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1]) def test_run_2qubits(self): @@ -187,29 +204,35 @@ def test_run_2qubits(self): op2 = SparsePauliOp.from_list([("ZI", 1)]) op3 = SparsePauliOp.from_list([("IZ", 1)]) - est = Estimator() - result = est.run([qc], [op], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + est = Estimator() + result = est.run([qc], [op], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1]) - result = est.run([qc2], [op], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1]) - result = est.run([qc], [op2], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc], [op2], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1]) - result = est.run([qc2], [op2], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op2], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1]) - result = est.run([qc], [op3], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc], [op3], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [1]) - result = est.run([qc2], [op3], [[]]).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = est.run([qc2], [op3], [[]]).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1]) def test_run_errors(self): @@ -220,17 +243,18 @@ def test_run_errors(self): op = SparsePauliOp.from_list([("I", 1)]) op2 = SparsePauliOp.from_list([("II", 1)]) - est = Estimator() - with self.assertRaises(ValueError): - est.run([qc], [op2], [[]]).result() - with self.assertRaises(ValueError): - est.run([qc], [op], [[1e4]]).result() - with self.assertRaises(ValueError): - est.run([qc2], [op2], [[1, 2]]).result() - with self.assertRaises(ValueError): - est.run([qc, qc2], [op2], [[1]]).result() - with self.assertRaises(ValueError): - est.run([qc], [op, op2], [[1]]).result() + with self.assertWarns(DeprecationWarning): + est = Estimator() + with self.assertRaises(ValueError): + est.run([qc], [op2], [[]]).result() + with self.assertRaises(ValueError): + est.run([qc], [op], [[1e4]]).result() + with self.assertRaises(ValueError): + est.run([qc2], [op2], [[1, 2]]).result() + with self.assertRaises(ValueError): + est.run([qc, qc2], [op2], [[1]]).result() + with self.assertRaises(ValueError): + est.run([qc], [op, op2], [[1]]).result() def test_run_numpy_params(self): """Test for numpy array as parameter values""" @@ -241,75 +265,83 @@ def test_run_numpy_params(self): params_array = rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) - estimator = Estimator() - target = estimator.run([qc] * k, [op] * k, params_list).result() + with self.assertWarns(DeprecationWarning): + estimator = Estimator() + target = estimator.run([qc] * k, [op] * k, params_list).result() with self.subTest("ndarrary"): - result = estimator.run([qc] * k, [op] * k, params_array).result() + with self.assertWarns(DeprecationWarning): + result = estimator.run([qc] * k, [op] * k, params_array).result() self.assertEqual(len(result.metadata), k) np.testing.assert_allclose(result.values, target.values) with self.subTest("list of ndarray"): - result = estimator.run([qc] * k, [op] * k, params_list_array).result() + with self.assertWarns(DeprecationWarning): + result = estimator.run([qc] * k, [op] * k, params_list_array).result() self.assertEqual(len(result.metadata), k) np.testing.assert_allclose(result.values, target.values) def test_run_with_shots_option(self): """test with shots option.""" - est = Estimator() - result = est.run( - [self.ansatz], - [self.observable], - parameter_values=[[0, 1, 1, 2, 3, 5]], - shots=1024, - seed=15, - ).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + est = Estimator() + result = est.run( + [self.ansatz], + [self.observable], + parameter_values=[[0, 1, 1, 2, 3, 5]], + shots=1024, + seed=15, + ).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1.307397243478641]) def test_run_with_shots_option_none(self): """test with shots=None option. Seed is ignored then.""" - est = Estimator() - result_42 = est.run( - [self.ansatz], - [self.observable], - parameter_values=[[0, 1, 1, 2, 3, 5]], - shots=None, - seed=42, - ).result() - result_15 = est.run( - [self.ansatz], - [self.observable], - parameter_values=[[0, 1, 1, 2, 3, 5]], - shots=None, - seed=15, - ).result() + with self.assertWarns(DeprecationWarning): + est = Estimator() + result_42 = est.run( + [self.ansatz], + [self.observable], + parameter_values=[[0, 1, 1, 2, 3, 5]], + shots=None, + seed=42, + ).result() + result_15 = est.run( + [self.ansatz], + [self.observable], + parameter_values=[[0, 1, 1, 2, 3, 5]], + shots=None, + seed=15, + ).result() np.testing.assert_allclose(result_42.values, result_15.values) def test_options(self): """Test for options""" with self.subTest("init"): - estimator = Estimator(options={"shots": 3000}) + with self.assertWarns(DeprecationWarning): + estimator = Estimator(options={"shots": 3000}) self.assertEqual(estimator.options.get("shots"), 3000) with self.subTest("set_options"): estimator.set_options(shots=1024, seed=15) self.assertEqual(estimator.options.get("shots"), 1024) self.assertEqual(estimator.options.get("seed"), 15) with self.subTest("run"): - result = estimator.run( - [self.ansatz], - [self.observable], - parameter_values=[[0, 1, 1, 2, 3, 5]], - ).result() - self.assertIsInstance(result, EstimatorResult) + with self.assertWarns(DeprecationWarning): + result = estimator.run( + [self.ansatz], + [self.observable], + parameter_values=[[0, 1, 1, 2, 3, 5]], + ).result() + self.assertIsInstance(result, EstimatorResult) np.testing.assert_allclose(result.values, [-1.307397243478641]) def test_negative_variance(self): """Test for negative variance caused by numerical error.""" qc = QuantumCircuit(1) - estimator = Estimator() - result = estimator.run(qc, 1e-4 * SparsePauliOp("I"), shots=1024).result() + with self.assertWarns(DeprecationWarning): + estimator = Estimator() + result = estimator.run(qc, 1e-4 * SparsePauliOp("I"), shots=1024).result() self.assertEqual(result.values[0], 1e-4) self.assertEqual(result.metadata[0]["variance"], 0.0) @@ -348,19 +380,22 @@ class TestObservableValidation(QiskitTestCase): @unpack def test_validate_observables(self, observables, expected): """Test observables standardization.""" - self.assertEqual(validation._validate_observables(observables), expected) + with self.assertWarns(DeprecationWarning): + self.assertEqual(validation._validate_observables(observables), expected) @data(None, "ERROR") def test_qiskit_error(self, observables): """Test qiskit error if invalid input.""" with self.assertRaises(QiskitError): - validation._validate_observables(observables) + with self.assertWarns(DeprecationWarning): + validation._validate_observables(observables) @data((), []) def test_value_error(self, observables): """Test value error if no observables are provided.""" with self.assertRaises(ValueError): - validation._validate_observables(observables) + with self.assertWarns(DeprecationWarning): + validation._validate_observables(observables) if __name__ == "__main__": diff --git a/test/python/primitives/test_primitive.py b/test/python/primitives/test_primitive.py index f0401e771448..fb96081fa001 100644 --- a/test/python/primitives/test_primitive.py +++ b/test/python/primitives/test_primitive.py @@ -21,7 +21,7 @@ from qiskit.circuit.random import random_circuit from qiskit.primitives.base import validation from qiskit.primitives.utils import _circuit_key -from qiskit.providers.fake_provider import Fake20QV1 +from qiskit.providers.fake_provider import GenericBackendV2 from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -142,7 +142,11 @@ def test_with_scheduling(n): qc = QuantumCircuit(1) qc.x(0) qc.add_calibration("x", qubits=(0,), schedule=custom_gate) - return transpile(qc, Fake20QV1(), scheduling_method="alap") + + backend = GenericBackendV2( + num_qubits=2, basis_gates=["id", "u1", "u2", "u3", "cx"], seed=42 + ) + return transpile(qc, backend, scheduling_method="alap", optimization_level=1) keys = [_circuit_key(test_with_scheduling(i)) for i in range(1, 5)] self.assertEqual(len(keys), len(set(keys))) diff --git a/test/python/primitives/test_sampler.py b/test/python/primitives/test_sampler.py index eac0886b30b1..58ab97689748 100644 --- a/test/python/primitives/test_sampler.py +++ b/test/python/primitives/test_sampler.py @@ -87,10 +87,11 @@ def _compare_probs(self, prob, target): def test_sampler_run(self): """Test Sampler.run().""" bell = self._circuit[1] - sampler = Sampler() - job = sampler.run(circuits=[bell]) - result = job.result() - self.assertIsInstance(result, SamplerResult) + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + job = sampler.run(circuits=[bell]) + result = job.result() + self.assertIsInstance(result, SamplerResult) self._compare_probs(result.quasi_dists, self._target[1]) def test_sample_run_multiple_circuits(self): @@ -98,8 +99,9 @@ def test_sample_run_multiple_circuits(self): # executes three Bell circuits # Argument `parameters` is optional. bell = self._circuit[1] - sampler = Sampler() - result = sampler.run([bell, bell, bell]).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + result = sampler.run([bell, bell, bell]).result() self._compare_probs(result.quasi_dists[0], self._target[1]) self._compare_probs(result.quasi_dists[1], self._target[1]) self._compare_probs(result.quasi_dists[2], self._target[1]) @@ -112,8 +114,9 @@ def test_sampler_run_with_parameterized_circuits(self): pqc2 = self._pqc2 theta1, theta2, theta3 = self._theta - sampler = Sampler() - result = sampler.run([pqc, pqc, pqc2], [theta1, theta2, theta3]).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + result = sampler.run([pqc, pqc, pqc2], [theta1, theta2, theta3]).result() # result of pqc(theta1) prob1 = { @@ -150,9 +153,10 @@ def test_run_1qubit(self): qc2.x(0) qc2.measure_all() - sampler = Sampler() - result = sampler.run([qc, qc2]).result() - self.assertIsInstance(result, SamplerResult) + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + result = sampler.run([qc, qc2]).result() + self.assertIsInstance(result, SamplerResult) self.assertEqual(len(result.quasi_dists), 2) for i in range(2): @@ -174,9 +178,10 @@ def test_run_2qubit(self): qc3.x([0, 1]) qc3.measure_all() - sampler = Sampler() - result = sampler.run([qc0, qc1, qc2, qc3]).result() - self.assertIsInstance(result, SamplerResult) + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + result = sampler.run([qc0, qc1, qc2, qc3]).result() + self.assertIsInstance(result, SamplerResult) self.assertEqual(len(result.quasi_dists), 4) for i in range(4): @@ -187,15 +192,16 @@ def test_run_2qubit(self): def test_run_single_circuit(self): """Test for single circuit case.""" - sampler = Sampler() - with self.subTest("No parameter"): circuit = self._circuit[1] target = self._target[1] param_vals = [None, [], [[]], np.array([]), np.array([[]])] for val in param_vals: with self.subTest(f"{circuit.name} w/ {val}"): - result = sampler.run(circuit, val).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + with self.assertWarns(DeprecationWarning): + result = sampler.run(circuit, val).result() self._compare_probs(result.quasi_dists, target) self.assertEqual(len(result.metadata), 1) @@ -214,7 +220,10 @@ def test_run_single_circuit(self): ] for val in param_vals: with self.subTest(f"{circuit.name} w/ {val}"): - result = sampler.run(circuit, val).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + with self.assertWarns(DeprecationWarning): + result = sampler.run(circuit, val).result() self._compare_probs(result.quasi_dists, target) self.assertEqual(len(result.metadata), 1) @@ -230,7 +239,10 @@ def test_run_single_circuit(self): ] for val in param_vals: with self.subTest(f"{circuit.name} w/ {val}"): - result = sampler.run(circuit, val).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + with self.assertWarns(DeprecationWarning): + result = sampler.run(circuit, val).result() self._compare_probs(result.quasi_dists, target) self.assertEqual(len(result.metadata), 1) @@ -247,9 +259,10 @@ def test_run_reverse_meas_order(self): qc.measure(1, 1) qc.measure(2, 0) - sampler = Sampler() - result = sampler.run([qc] * 2, [[0, 0], [np.pi / 2, 0]]).result() - self.assertIsInstance(result, SamplerResult) + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + result = sampler.run([qc] * 2, [[0, 0], [np.pi / 2, 0]]).result() + self.assertIsInstance(result, SamplerResult) self.assertEqual(len(result.quasi_dists), 2) # qc({x: 0, y: 0}) @@ -274,37 +287,40 @@ def test_run_errors(self): with qc5.for_loop(range(5)): qc5.h(0) - sampler = Sampler() - with self.subTest("set parameter values to a non-parameterized circuit"): - with self.assertRaises(ValueError): - _ = sampler.run([qc1], [[1e2]]) - with self.subTest("missing all parameter values for a parameterized circuit"): - with self.assertRaises(ValueError): - _ = sampler.run([qc2], [[]]) - with self.subTest("missing some parameter values for a parameterized circuit"): - with self.assertRaises(ValueError): - _ = sampler.run([qc2], [[1e2]]) - with self.subTest("too many parameter values for a parameterized circuit"): - with self.assertRaises(ValueError): - _ = sampler.run([qc2], [[1e2]] * 100) - with self.subTest("no classical bits"): - with self.assertRaises(ValueError): - _ = sampler.run([qc3], [[]]) - with self.subTest("no measurement"): - with self.assertRaises(ValueError): - _ = sampler.run([qc4], [[]]) - with self.subTest("no measurement in control flow"): - with self.assertRaises(ValueError): - _ = sampler.run([qc5], [[]]) + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + with self.subTest("set parameter values to a non-parameterized circuit"): + with self.assertRaises(ValueError): + _ = sampler.run([qc1], [[1e2]]) + with self.subTest("missing all parameter values for a parameterized circuit"): + with self.assertRaises(ValueError): + _ = sampler.run([qc2], [[]]) + with self.subTest("missing some parameter values for a parameterized circuit"): + with self.assertRaises(ValueError): + _ = sampler.run([qc2], [[1e2]]) + with self.subTest("too many parameter values for a parameterized circuit"): + with self.assertRaises(ValueError): + _ = sampler.run([qc2], [[1e2]] * 100) + with self.subTest("no classical bits"): + with self.assertRaises(ValueError): + _ = sampler.run([qc3], [[]]) + with self.subTest("no measurement"): + with self.assertRaises(ValueError): + _ = sampler.run([qc4], [[]]) + with self.subTest("no measurement in control flow"): + with self.assertRaises(ValueError): + _ = sampler.run([qc5], [[]]) def test_run_empty_parameter(self): """Test for empty parameter""" n = 5 qc = QuantumCircuit(n, n - 1) qc.measure(range(n - 1), range(n - 1)) - sampler = Sampler() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() with self.subTest("one circuit"): - result = sampler.run([qc], shots=1000).result() + with self.assertWarns(DeprecationWarning): + result = sampler.run([qc], shots=1000).result() self.assertEqual(len(result.quasi_dists), 1) for q_d in result.quasi_dists: quasi_dist = {k: v for k, v in q_d.items() if v != 0.0} @@ -328,8 +344,9 @@ def test_run_numpy_params(self): params_array = rng.random((k, qc.num_parameters)) params_list = params_array.tolist() params_list_array = list(params_array) - sampler = Sampler() - target = sampler.run([qc] * k, params_list).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + target = sampler.run([qc] * k, params_list).result() with self.subTest("ndarrary"): result = sampler.run([qc] * k, params_array).result() @@ -346,21 +363,23 @@ def test_run_numpy_params(self): def test_run_with_shots_option(self): """test with shots option.""" params, target = self._generate_params_target([1]) - sampler = Sampler() - result = sampler.run( - circuits=[self._pqc], parameter_values=params, shots=1024, seed=15 - ).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + result = sampler.run( + circuits=[self._pqc], parameter_values=params, shots=1024, seed=15 + ).result() self._compare_probs(result.quasi_dists, target) def test_run_with_shots_option_none(self): """test with shots=None option. Seed is ignored then.""" - sampler = Sampler() - result_42 = sampler.run( - [self._pqc], parameter_values=[[0, 1, 1, 2, 3, 5]], shots=None, seed=42 - ).result() - result_15 = sampler.run( - [self._pqc], parameter_values=[[0, 1, 1, 2, 3, 5]], shots=None, seed=15 - ).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + result_42 = sampler.run( + [self._pqc], parameter_values=[[0, 1, 1, 2, 3, 5]], shots=None, seed=42 + ).result() + result_15 = sampler.run( + [self._pqc], parameter_values=[[0, 1, 1, 2, 3, 5]], shots=None, seed=15 + ).result() self.assertDictAlmostEqual(result_42.quasi_dists, result_15.quasi_dists) def test_run_shots_result_size(self): @@ -370,8 +389,9 @@ def test_run_shots_result_size(self): qc = QuantumCircuit(n) qc.h(range(n)) qc.measure_all() - sampler = Sampler() - result = sampler.run(qc, [], shots=shots, seed=42).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + result = sampler.run(qc, [], shots=shots, seed=42).result() self.assertEqual(len(result.quasi_dists), 1) self.assertLessEqual(len(result.quasi_dists[0]), shots) self.assertAlmostEqual(sum(result.quasi_dists[0].values()), 1.0) @@ -379,15 +399,17 @@ def test_run_shots_result_size(self): def test_primitive_job_status_done(self): """test primitive job's status""" bell = self._circuit[1] - sampler = Sampler() - job = sampler.run(circuits=[bell]) - _ = job.result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + job = sampler.run(circuits=[bell]) + _ = job.result() self.assertEqual(job.status(), JobStatus.DONE) def test_options(self): """Test for options""" with self.subTest("init"): - sampler = Sampler(options={"shots": 3000}) + with self.assertWarns(DeprecationWarning): + sampler = Sampler(options={"shots": 3000}) self.assertEqual(sampler.options.get("shots"), 3000) with self.subTest("set_options"): sampler.set_options(shots=1024, seed=15) @@ -395,7 +417,8 @@ def test_options(self): self.assertEqual(sampler.options.get("seed"), 15) with self.subTest("run"): params, target = self._generate_params_target([1]) - result = sampler.run([self._pqc], parameter_values=params).result() + with self.assertWarns(DeprecationWarning): + result = sampler.run([self._pqc], parameter_values=params).result() self._compare_probs(result.quasi_dists, target) self.assertEqual(result.quasi_dists[0].shots, 1024) @@ -407,8 +430,9 @@ def test_circuit_with_unitary(self): circuit.append(gate, [0]) circuit.measure_all() - sampler = Sampler() - sampler_result = sampler.run([circuit]).result() + with self.assertWarns(DeprecationWarning): + sampler = Sampler() + sampler_result = sampler.run([circuit]).result() self.assertDictAlmostEqual(sampler_result.quasi_dists[0], {0: 1, 1: 0}) diff --git a/test/python/primitives/test_statevector_sampler.py b/test/python/primitives/test_statevector_sampler.py index c065871025d7..1f84c072564d 100644 --- a/test/python/primitives/test_statevector_sampler.py +++ b/test/python/primitives/test_statevector_sampler.py @@ -280,6 +280,11 @@ def test_run_errors(self): qc3 = QuantumCircuit(1, 1) with qc3.for_loop(range(5)): qc3.h(0) + qc4 = QuantumCircuit(2, 2) + qc4.h(0) + qc4.measure(1, 1) + qc4.x(0).c_if(1, 1) + qc4.measure(0, 0) sampler = StatevectorSampler() with self.subTest("set parameter values to a non-parameterized circuit"): @@ -301,6 +306,9 @@ def test_run_errors(self): with self.subTest("with control flow"): with self.assertRaises(QiskitError): _ = sampler.run([qc3]).result() + with self.subTest("with c_if"): + with self.assertRaises(QiskitError): + _ = sampler.run([qc4]).result() with self.subTest("negative shots, run arg"): with self.assertRaises(ValueError): _ = sampler.run([qc1], shots=-1).result() @@ -584,17 +592,21 @@ def test_circuit_with_aliased_cregs(self): c2 = ClassicalRegister(1, "c2") qc = QuantumCircuit(q, c1, c2) - qc.ry(np.pi / 4, 2) - qc.cx(2, 1) - qc.cx(0, 1) - 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) qc2 = QuantumCircuit(5, 5) qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True) - cregs = [creg.name for creg in qc2.cregs] + # Note: qc2 has aliased cregs, c0 -> c[2] and c1 -> c[4]. + # copy_empty_like copies the aliased cregs of qc2 to qc3. + qc3 = QuantumCircuit.copy_empty_like(qc2) + qc3.ry(np.pi / 4, 2) + qc3.cx(2, 1) + qc3.cx(0, 1) + qc3.h(0) + qc3.measure(0, 2) + qc3.measure(1, 4) + self.assertEqual(len(qc3.cregs), 3) + cregs = [creg.name for creg in qc3.cregs] target = { cregs[0]: {0: 4255, 4: 4297, 16: 720, 20: 726}, cregs[1]: {0: 5000, 1: 5000}, @@ -602,7 +614,7 @@ def test_circuit_with_aliased_cregs(self): } sampler = StatevectorSampler(seed=self._seed) - result = sampler.run([qc2], shots=self._shots).result() + result = sampler.run([qc3], shots=self._shots).result() self.assertEqual(len(result), 1) data = result[0].data self.assertEqual(len(data), 3) diff --git a/test/python/primitives/test_utils.py b/test/python/primitives/test_utils.py index 9f1c53091f2f..9a9d27a62bf1 100644 --- a/test/python/primitives/test_utils.py +++ b/test/python/primitives/test_utils.py @@ -27,20 +27,23 @@ class TestMapping(QiskitTestCase): def test_empty_circ(self): """Empty circuit has no mapping""" qc = QuantumCircuit() - self.assertDictEqual(final_measurement_mapping(qc), {}) + with self.assertWarns(DeprecationWarning): + self.assertDictEqual(final_measurement_mapping(qc), {}) def test_sime_circ(self): """Just measures""" qc = QuantumCircuit(5) qc.measure_all() - self.assertDictEqual(final_measurement_mapping(qc), {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}) + with self.assertWarns(DeprecationWarning): + self.assertDictEqual(final_measurement_mapping(qc), {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}) def test_simple2_circ(self): """Meas followed by Hadamards""" qc = QuantumCircuit(5) qc.measure_all() qc.h(range(5)) - self.assertDictEqual(final_measurement_mapping(qc), {}) + with self.assertWarns(DeprecationWarning): + self.assertDictEqual(final_measurement_mapping(qc), {}) def test_multi_qreg(self): """Test multiple qregs""" @@ -55,7 +58,8 @@ def test_multi_qreg(self): qc.measure(range(2, 4), range(2, 4)) qc.barrier(range(5)) qc.measure(1, 4) - self.assertDictEqual(final_measurement_mapping(qc), {2: 2, 3: 3, 1: 4}) + with self.assertWarns(DeprecationWarning): + self.assertDictEqual(final_measurement_mapping(qc), {2: 2, 3: 3, 1: 4}) def test_multi_creg(self): """Test multiple qregs""" @@ -71,7 +75,8 @@ def test_multi_creg(self): qc.measure(range(2, 4), range(2, 4)) qc.barrier(range(5)) qc.measure(1, 4) - self.assertDictEqual(final_measurement_mapping(qc), {2: 2, 3: 3, 1: 4}) + with self.assertWarns(DeprecationWarning): + self.assertDictEqual(final_measurement_mapping(qc), {2: 2, 3: 3, 1: 4}) def test_mapping_w_delays(self): """Check that measurements followed by delays get in the mapping""" @@ -81,5 +86,6 @@ def test_mapping_w_delays(self): qc.measure(1, 0) qc.barrier() - maps = final_measurement_mapping(qc) + with self.assertWarns(DeprecationWarning): + maps = final_measurement_mapping(qc) self.assertDictEqual(maps, {1: 0, 0: 1}) diff --git a/test/python/providers/basic_provider/test_basic_simulator.py b/test/python/providers/basic_provider/test_basic_simulator.py index 282b23c1b2a5..57dd67dfd3c3 100644 --- a/test/python/providers/basic_provider/test_basic_simulator.py +++ b/test/python/providers/basic_provider/test_basic_simulator.py @@ -17,7 +17,7 @@ import numpy as np from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister -from qiskit.compiler import transpile, assemble +from qiskit.compiler import transpile from qiskit.providers.basic_provider import BasicSimulator from qiskit.qasm2 import dumps from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -47,7 +47,6 @@ def setUp(self): qcirc = QuantumCircuit.from_qasm_file(qasm_filename) qcirc.name = "test" self.transpiled_circuit = transpile(qcirc, backend=self.backend) - self.qobj = assemble(self.transpiled_circuit, shots=1000, seed_simulator=self.seed) def test_basic_simulator_single_shot(self): """Test single shot run.""" diff --git a/test/python/providers/fake_provider/test_fake_backends.py b/test/python/providers/fake_provider/test_fake_backends.py index c12be3584fc2..72c9675e1ce7 100644 --- a/test/python/providers/fake_provider/test_fake_backends.py +++ b/test/python/providers/fake_provider/test_fake_backends.py @@ -15,7 +15,7 @@ import unittest from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, transpile -from qiskit.providers.fake_provider import Fake5QV1, GenericBackendV2 +from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.utils import optionals from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -36,9 +36,9 @@ class FakeBackendsTest(QiskitTestCase): """fake backends test.""" @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required to run this test") - def test_fake_backends_get_kwargs(self): + def test_fake_backends_get_kwargs_v1(self): """Fake backends honor kwargs passed.""" - backend = Fake5QV1() + backend = GenericBackendV2(num_qubits=5, seed=42) qc = QuantumCircuit(2) qc.x(range(0, 2)) @@ -57,6 +57,7 @@ def test_fake_backend_v2_noise_model_always_present(self): qc = QuantumCircuit(1) qc.x(0) qc.measure_all() + res = backend.run(qc, shots=1000).result().get_counts() # Assert noise was present and result wasn't ideal self.assertNotEqual(res, {"1": 1000}) diff --git a/test/python/providers/fake_provider/test_generic_backend_v2.py b/test/python/providers/fake_provider/test_generic_backend_v2.py index 33bf57cf3903..42f46b7d7851 100644 --- a/test/python/providers/fake_provider/test_generic_backend_v2.py +++ b/test/python/providers/fake_provider/test_generic_backend_v2.py @@ -16,8 +16,10 @@ from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister, transpile from qiskit.providers.fake_provider import GenericBackendV2 +from qiskit.quantum_info import Operator from qiskit.transpiler import CouplingMap from qiskit.exceptions import QiskitError +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -33,17 +35,17 @@ def setUp(self): def test_supported_basis_gates(self): """Test that target raises error if basis_gate not in ``supported_names``.""" with self.assertRaises(QiskitError): - GenericBackendV2(num_qubits=8, basis_gates=["cx", "id", "rz", "sx", "zz"]) + GenericBackendV2(num_qubits=8, basis_gates=["cx", "id", "rz", "sx", "zz"], seed=42) def test_cx_1Q(self): """Test failing with a backend with single qubit but with a two-qubit basis gate""" with self.assertRaises(QiskitError): - GenericBackendV2(num_qubits=1, basis_gates=["cx", "id"]) + GenericBackendV2(num_qubits=1, basis_gates=["cx", "id"], seed=42) def test_ccx_2Q(self): """Test failing with a backend with two qubits but with a three-qubit basis gate""" with self.assertRaises(QiskitError): - GenericBackendV2(num_qubits=2, basis_gates=["ccx", "id"]) + GenericBackendV2(num_qubits=2, basis_gates=["ccx", "id"], seed=42) def test_calibration_no_noise_info(self): """Test failing with a backend with calibration and no noise info""" @@ -53,27 +55,84 @@ def test_calibration_no_noise_info(self): basis_gates=["ccx", "id"], calibrate_instructions=True, noise_info=False, + seed=42, ) def test_no_noise(self): """Test no noise info when parameter is false""" - backend = GenericBackendV2(num_qubits=2, noise_info=False) + backend = GenericBackendV2( + num_qubits=5, coupling_map=CouplingMap.from_line(5), noise_info=False, seed=42 + ) + qc = QuantumCircuit(5) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + qc.cx(1, 4) + qc.cx(3, 0) + qc.cx(2, 4) + qc_res = generate_preset_pass_manager(optimization_level=2, backend=backend).run(qc) + self.assertTrue(Operator.from_circuit(qc_res).equiv(qc)) + self.assertEqual(backend.target.qubit_properties, None) + + def test_no_noise_fully_connected(self): + """Test no noise info when parameter is false""" + backend = GenericBackendV2(num_qubits=5, noise_info=False, seed=42) + qc = QuantumCircuit(5) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + qc.cx(1, 4) + qc.cx(3, 0) + qc.cx(2, 4) + qc_res = generate_preset_pass_manager(optimization_level=2, backend=backend).run(qc) + self.assertTrue(Operator.from_circuit(qc_res).equiv(qc)) + self.assertEqual(backend.target.qubit_properties, None) + + def test_no_info(self): + """Test no noise info when parameter is false""" + backend = GenericBackendV2( + num_qubits=5, + coupling_map=CouplingMap.from_line(5), + noise_info=False, + pulse_channels=False, + seed=42, + ) + qc = QuantumCircuit(5) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + qc.cx(1, 4) + qc.cx(3, 0) + qc.cx(2, 4) + qc_res = generate_preset_pass_manager(optimization_level=2, backend=backend).run(qc) + self.assertTrue(Operator.from_circuit(qc_res).equiv(qc)) self.assertEqual(backend.target.qubit_properties, None) def test_no_pulse_channels(self): """Test no/empty pulse channels when parameter is false""" - backend = GenericBackendV2(num_qubits=2, pulse_channels=False) + backend = GenericBackendV2( + num_qubits=5, coupling_map=CouplingMap.from_line(5), pulse_channels=False, seed=42 + ) + qc = QuantumCircuit(5) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + qc.cx(1, 4) + qc.cx(3, 0) + qc.cx(2, 4) + qc_res = generate_preset_pass_manager(optimization_level=2, backend=backend).run(qc) + self.assertTrue(Operator.from_circuit(qc_res).equiv(qc)) self.assertTrue(len(backend.channels_map) == 0) def test_operation_names(self): """Test that target basis gates include "delay", "measure" and "reset" even if not provided by user.""" - target = GenericBackendV2(num_qubits=8) + target = GenericBackendV2(num_qubits=8, seed=42) op_names = list(target.operation_names) op_names.sort() self.assertEqual(op_names, ["cx", "delay", "id", "measure", "reset", "rz", "sx", "x"]) - target = GenericBackendV2(num_qubits=8, basis_gates=["ecr", "id", "rz", "sx", "x"]) + target = GenericBackendV2(num_qubits=8, basis_gates=["ecr", "id", "rz", "sx", "x"], seed=42) op_names = list(target.operation_names) op_names.sort() self.assertEqual(op_names, ["delay", "ecr", "id", "measure", "reset", "rz", "sx", "x"]) @@ -81,7 +140,7 @@ def test_operation_names(self): def test_incompatible_coupling_map(self): """Test that the size of the coupling map must match num_qubits.""" with self.assertRaises(QiskitError): - GenericBackendV2(num_qubits=5, coupling_map=self.cmap) + GenericBackendV2(num_qubits=5, coupling_map=self.cmap, seed=42) def test_control_flow_operation_names(self): """Test that control flow instructions are added to the target if control_flow is True.""" @@ -90,6 +149,7 @@ def test_control_flow_operation_names(self): basis_gates=["ecr", "id", "rz", "sx", "x"], coupling_map=self.cmap, control_flow=True, + seed=42, ).target op_names = list(target.operation_names) op_names.sort() @@ -119,7 +179,7 @@ def test_default_coupling_map(self): (1, 3), (3, 1), (1, 4), (4, 1), (2, 3), (3, 2), (2, 4), (4, 2), (3, 4), (4, 3)] # fmt: on self.assertEqual( - list(GenericBackendV2(num_qubits=5).coupling_map.get_edges()), + list(GenericBackendV2(num_qubits=5, seed=42).coupling_map.get_edges()), reference_cmap, ) @@ -134,7 +194,7 @@ def test_run(self): qc.cx(qr[0], qr[k]) qc.measure(qr, cr) - backend = GenericBackendV2(num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"]) + backend = GenericBackendV2(num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], seed=42) tqc = transpile(qc, backend=backend, optimization_level=3, seed_transpiler=42) result = backend.run(tqc, seed_simulator=42, shots=1000).result() counts = result.get_counts() @@ -157,7 +217,7 @@ def test_duration_defaults(self): "rxx": (7.992e-08, 8.99988e-07), } for _ in range(20): - target = GenericBackendV2(num_qubits=2, basis_gates=basis_gates).target + target = GenericBackendV2(num_qubits=2, basis_gates=basis_gates, seed=42).target for inst in target: for qargs in target.qargs_for_operation_name(inst): duration = target[inst][qargs].duration diff --git a/test/python/providers/test_backend_v2.py b/test/python/providers/test_backend_v2.py index 70330085b1ab..df5f7b9abdea 100644 --- a/test/python/providers/test_backend_v2.py +++ b/test/python/providers/test_backend_v2.py @@ -41,7 +41,7 @@ class TestBackendV2(QiskitTestCase): def setUp(self): super().setUp() - self.backend = GenericBackendV2(num_qubits=2, seed=42, basis_gates=["rx", "u"]) + self.backend = GenericBackendV2(num_qubits=2, basis_gates=["rx", "u"], seed=42) cx_props = { (0, 1): InstructionProperties(duration=5.23e-7, error=0.00098115), } @@ -127,9 +127,9 @@ def test_transpile(self, opt_level): ) def test_5q_ghz(self, opt_level, gate, bidirectional): if bidirectional: - backend = GenericBackendV2(num_qubits=5) + backend = GenericBackendV2(num_qubits=5, seed=42) else: - backend = GenericBackendV2(num_qubits=5, coupling_map=TENERIFE_CMAP) + backend = GenericBackendV2(num_qubits=5, coupling_map=TENERIFE_CMAP, seed=42) qc = QuantumCircuit(5) qc.h(0) getattr(qc, gate)(0, 1) @@ -147,7 +147,7 @@ def test_transpile_respects_arg_constraints(self): qc = QuantumCircuit(2) qc.h(0) qc.cx(1, 0) - tqc = transpile(qc, self.backend) + tqc = transpile(qc, self.backend, optimization_level=1) self.assertTrue(Operator.from_circuit(tqc).equiv(qc)) # Below is done to check we're decomposing cx(1, 0) with extra # rotations to correct for direction. However because of fp @@ -163,7 +163,7 @@ def test_transpile_respects_arg_constraints(self): qc = QuantumCircuit(2) qc.h(0) qc.ecr(0, 1) - tqc = transpile(qc, self.backend) + tqc = transpile(qc, self.backend, optimization_level=1) self.assertTrue(Operator.from_circuit(tqc).equiv(qc)) self.assertEqual(tqc.count_ops(), {"ecr": 1, "u": 4}) self.assertMatchesTargetConstraints(tqc, self.backend.target) @@ -173,7 +173,7 @@ def test_transpile_relies_on_gate_direction(self): qc = QuantumCircuit(2) qc.h(0) qc.ecr(0, 1) - tqc = transpile(qc, self.backend) + tqc = transpile(qc, self.backend, optimization_level=1) expected = QuantumCircuit(2) expected.u(0, 0, -math.pi, 0) expected.u(math.pi / 2, 0, 0, 1) @@ -191,7 +191,7 @@ def test_transpile_mumbai_target(self): qc.h(0) qc.cx(1, 0) qc.measure_all() - tqc = transpile(qc, backend) + tqc = transpile(qc, backend, optimization_level=1) qr = QuantumRegister(27, "q") cr = ClassicalRegister(2, "meas") expected = QuantumCircuit(qr, cr, global_phase=math.pi / 4) @@ -207,7 +207,7 @@ def test_transpile_mumbai_target(self): @data(0, 1, 2, 3, 4) def test_drive_channel(self, qubit): """Test getting drive channel with qubit index.""" - backend = GenericBackendV2(num_qubits=5) + backend = GenericBackendV2(num_qubits=5, seed=42) chan = backend.drive_channel(qubit) ref = channels.DriveChannel(qubit) self.assertEqual(chan, ref) @@ -215,7 +215,7 @@ def test_drive_channel(self, qubit): @data(0, 1, 2, 3, 4) def test_measure_channel(self, qubit): """Test getting measure channel with qubit index.""" - backend = GenericBackendV2(num_qubits=5) + backend = GenericBackendV2(num_qubits=5, seed=42) chan = backend.measure_channel(qubit) ref = channels.MeasureChannel(qubit) self.assertEqual(chan, ref) @@ -223,7 +223,7 @@ def test_measure_channel(self, qubit): @data(0, 1, 2, 3, 4) def test_acquire_channel(self, qubit): """Test getting acquire channel with qubit index.""" - backend = GenericBackendV2(num_qubits=5) + backend = GenericBackendV2(num_qubits=5, seed=42) chan = backend.acquire_channel(qubit) ref = channels.AcquireChannel(qubit) self.assertEqual(chan, ref) @@ -241,7 +241,7 @@ def test_control_channel(self, qubits): (1, 0): 1, (0, 1): 0, } - backend = GenericBackendV2(num_qubits=5, coupling_map=BOGOTA_CMAP) + backend = GenericBackendV2(num_qubits=5, coupling_map=BOGOTA_CMAP, seed=42) chan = backend.control_channel(qubits)[0] ref = channels.ControlChannel(bogota_cr_channels_map[qubits]) self.assertEqual(chan, ref) diff --git a/test/python/providers/test_backendconfiguration.py b/test/python/providers/test_backendconfiguration.py index 5ba727fd660d..82bbd1c6847f 100644 --- a/test/python/providers/test_backendconfiguration.py +++ b/test/python/providers/test_backendconfiguration.py @@ -12,6 +12,9 @@ """ Test that the PulseBackendConfiguration methods work as expected with a mocked Pulse backend. """ +# TODO the full file can be removed once BackendV1 is removed, since it is the +# only one with backend.configuration() + import collections import copy @@ -26,7 +29,8 @@ class TestBackendConfiguration(QiskitTestCase): def setUp(self): super().setUp() - backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() self.config = backend.configuration() def test_simple_config(self): @@ -60,7 +64,8 @@ def test_hamiltonian(self): {k: var * 1e-9 for k, var in ref_vars.items()}, ) # 3Q doesn't offer a hamiltonian -- test that we get a reasonable response - backend_3q = FakeOpenPulse3Q() + with self.assertWarns(DeprecationWarning): + backend_3q = FakeOpenPulse3Q() self.assertEqual(backend_3q.configuration().hamiltonian, None) def test_get_channels(self): @@ -80,7 +85,8 @@ def test_get_channel_qubits(self): """Test to get all qubits operated on a given channel.""" self.assertEqual(self.config.get_channel_qubits(channel=DriveChannel(0)), [0]) self.assertEqual(self.config.get_channel_qubits(channel=ControlChannel(0)), [0, 1]) - backend_3q = FakeOpenPulse3Q() + with self.assertWarns(DeprecationWarning): + backend_3q = FakeOpenPulse3Q() self.assertEqual(backend_3q.configuration().get_channel_qubits(ControlChannel(2)), [2, 1]) self.assertEqual(backend_3q.configuration().get_channel_qubits(ControlChannel(1)), [1, 0]) with self.assertRaises(BackendConfigurationError): @@ -107,7 +113,8 @@ def test_get_qubit_channels(self): ], ) ) - backend_3q = FakeOpenPulse3Q() + with self.assertWarns(DeprecationWarning): + backend_3q = FakeOpenPulse3Q() self.assertTrue( self._test_lists_equal( actual=backend_3q.configuration().get_qubit_channels(1), @@ -178,7 +185,8 @@ def test_deepcopy(self): def test_u_channel_lo_scale(self): """Ensure that u_channel_lo scale is a complex number""" - valencia_conf = Fake27QPulseV1().configuration() + with self.assertWarns(DeprecationWarning): + valencia_conf = Fake27QPulseV1().configuration() self.assertTrue(isinstance(valencia_conf.u_channel_lo[0][0].scale, complex)) def test_processor_type(self): diff --git a/test/python/providers/test_backendproperties.py b/test/python/providers/test_backendproperties.py index b1455bd9a320..ac024973133e 100644 --- a/test/python/providers/test_backendproperties.py +++ b/test/python/providers/test_backendproperties.py @@ -22,12 +22,13 @@ class BackendpropertiesTestCase(QiskitTestCase): """Test usability methods of backend.properties().""" - backend = Fake5QV1() - backend_name = "fake_ourense" + # TODO the full file can be removed once BackendV1 is removed, since it is the + # only one with backend.properties() def setUp(self): super().setUp() - self.backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + self.backend = Fake5QV1() self.properties = self.backend.properties() self.ref_gate = next( g for g in self.backend.configuration().basis_gates if g not in ["id", "rz"] diff --git a/test/python/providers/test_backendstatus.py b/test/python/providers/test_backendstatus.py index 8372108408ac..2cfa31791de3 100644 --- a/test/python/providers/test_backendstatus.py +++ b/test/python/providers/test_backendstatus.py @@ -35,7 +35,8 @@ def test_repr(self): def test_fake_backend_status(self): """Test backend status for one of the fake backends""" - fake_backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + fake_backend = Fake5QV1() backend_status = fake_backend.status() self.assertIsInstance(backend_status, BackendStatus) diff --git a/test/python/providers/test_fake_backends.py b/test/python/providers/test_fake_backends.py index d5c5507b3b8e..d743c4be5c53 100644 --- a/test/python/providers/test_fake_backends.py +++ b/test/python/providers/test_fake_backends.py @@ -17,6 +17,7 @@ import itertools import operator import unittest +import warnings from test import combine from ddt import ddt, data @@ -77,12 +78,13 @@ from qiskit.transpiler.coupling import CouplingMap from test.utils.base import QiskitTestCase # pylint: disable=wrong-import-order -BACKENDS = [Fake5QV1(), Fake20QV1(), Fake7QPulseV1(), Fake27QPulseV1(), Fake127QPulseV1()] +with warnings.catch_warnings(): + BACKENDS = [Fake5QV1(), Fake20QV1(), Fake7QPulseV1(), Fake27QPulseV1(), Fake127QPulseV1()] BACKENDS_V2 = [] for n in [5, 7, 16, 20, 27, 65, 127]: cmap = CouplingMap.from_ring(n) - BACKENDS_V2.append(GenericBackendV2(num_qubits=n, coupling_map=cmap)) + BACKENDS_V2.append(GenericBackendV2(num_qubits=n, coupling_map=cmap, seed=42)) @ddt @@ -142,7 +144,8 @@ def test_circuit_on_fake_backend(self, backend, optimization_level): def test_qobj_failure(self): backend = BACKENDS[-1] tqc = transpile(self.circuit, backend) - qobj = assemble(tqc, backend) + with self.assertWarns(DeprecationWarning): + qobj = assemble(tqc, backend) with self.assertRaises(QiskitError): backend.run(qobj) @@ -187,7 +190,7 @@ def test_to_dict_configuration(self, backend): self.assertIsInstance(configuration.to_dict(), dict) - @data(BACKENDS) + @data(*BACKENDS) def test_defaults_to_dict(self, backend): if hasattr(backend, "defaults"): defaults = backend.defaults() @@ -204,7 +207,8 @@ def test_defaults_to_dict(self, backend): self.skipTest(f"Backend {backend} does not have defaults") def test_delay_circuit(self): - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.configuration().timing_constraints = { "acquire_alignment": 1, "granularity": 1, @@ -221,18 +225,23 @@ def test_delay_circuit(self): @data(0, 1, 2, 3) def test_converter(self, opt_level): - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() backend_v2 = BackendV2Converter(backend) self.assertIsInstance(backend_v2, BackendV2) res = transpile(self.circuit, backend_v2, optimization_level=opt_level) job = backend_v2.run(res) - result = job.result() + with warnings.catch_warnings(): + # TODO remove this catch once Aer stops using QobjDictField + warnings.filterwarnings("ignore", category=DeprecationWarning, module="qiskit") + result = job.result() counts = result.get_counts() max_count = max(counts.items(), key=operator.itemgetter(1))[0] self.assertEqual(max_count, "11") def test_converter_delay_circuit(self): - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.configuration().timing_constraints = { "acquire_alignment": 1, "granularity": 1, @@ -251,8 +260,8 @@ def test_converter_delay_circuit(self): def test_converter_with_missing_gate_property(self): """Test converting to V2 model with irregular backend data.""" - backend = FakeOpenPulse2Q() - + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() # The backend includes pulse calibration definition for U2, but its property is gone. # Note that u2 is a basis gate of this device. # Since gate property is not provided, the gate broadcasts to all qubits as ideal instruction. @@ -263,9 +272,11 @@ def test_converter_with_missing_gate_property(self): self.assertDictEqual(backend_v2.target["u2"], {None: None}) def test_non_cx_tests(self): - backend = GenericBackendV2(num_qubits=5, basis_gates=["cz", "x", "sx", "id", "rz"]) + backend = GenericBackendV2(num_qubits=5, basis_gates=["cz", "x", "sx", "id", "rz"], seed=42) self.assertIsInstance(backend.target.operation_from_name("cz"), CZGate) - backend = GenericBackendV2(num_qubits=5, basis_gates=["ecr", "x", "sx", "id", "rz"]) + backend = GenericBackendV2( + num_qubits=5, basis_gates=["ecr", "x", "sx", "id", "rz"], seed=42 + ) self.assertIsInstance(backend.target.operation_from_name("ecr"), ECRGate) @unittest.skipUnless(optionals.HAS_AER, "Aer required for this test") @@ -516,7 +527,8 @@ def __init__(self, num_ctrl_qubits, ctrl_state=None): def test_filter_faulty_qubits_backend_v2_converter(self): """Test faulty qubits in v2 conversion.""" - backend = Fake127QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake127QPulseV1() # Get properties dict to make it easier to work with the properties API # is difficult to edit because of the multiple layers of nesting and # different object types @@ -537,7 +549,8 @@ def test_filter_faulty_qubits_backend_v2_converter(self): def test_filter_faulty_qubits_backend_v2_converter_with_delay(self): """Test faulty qubits in v2 conversion.""" - backend = Fake127QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake127QPulseV1() # Get properties dict to make it easier to work with the properties API # is difficult to edit because of the multiple layers of nesting and # different object types @@ -579,14 +592,16 @@ def test_backend_v2_converter_without_delay(self): (4, 2), (4, 3), } - - backend = BackendV2Converter(backend=Fake5QV1(), filter_faulty=True, add_delay=False) + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() + backend = BackendV2Converter(backend=backend, filter_faulty=True, add_delay=False) self.assertEqual(backend.target.qargs, expected) def test_backend_v2_converter_with_meaningless_gate_config(self): """Test backend with broken gate config can be converted only with properties data.""" - backend_v1 = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend_v1 = Fake5QV1() backend_v1.configuration().gates = [ GateConfig(name="NotValidGate", parameters=[], qasm_def="not_valid_gate") ] @@ -603,7 +618,8 @@ def test_backend_v2_converter_with_meaningless_gate_config(self): def test_filter_faulty_qubits_and_gates_backend_v2_converter(self): """Test faulty gates and qubits.""" - backend = Fake127QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake127QPulseV1() # Get properties dict to make it easier to work with the properties API # is difficult to edit because of the multiple layers of nesting and # different object types @@ -646,7 +662,8 @@ def test_filter_faulty_qubits_and_gates_backend_v2_converter(self): def test_filter_faulty_gates_v2_converter(self): """Test just faulty gates in conversion.""" - backend = Fake127QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake127QPulseV1() # Get properties dict to make it easier to work with the properties API # is difficult to edit because of the multiple layers of nesting and # different object types @@ -680,14 +697,16 @@ def test_filter_faulty_gates_v2_converter(self): def test_filter_faulty_no_faults_v2_converter(self): """Test that faulty qubit filtering does nothing with all operational qubits and gates.""" - backend = Fake127QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake127QPulseV1() v2_backend = BackendV2Converter(backend, filter_faulty=True) for i in range(v2_backend.num_qubits): self.assertIn((i,), v2_backend.target.qargs) @data(0, 1, 2, 3) def test_faulty_full_path_transpile_connected_cmap(self, opt_level): - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() non_operational_gate = { "date": datetime.datetime.now(datetime.timezone.utc), "name": "operational", @@ -710,7 +729,8 @@ def test_faulty_full_path_transpile_connected_cmap(self, opt_level): self.assertNotIn((0, 1), connections) def test_convert_to_target_control_flow(self): - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() properties = backend.properties() configuration = backend.configuration() configuration.supported_instructions = [ @@ -734,7 +754,8 @@ def test_convert_to_target_control_flow(self): self.assertTrue(target.instruction_supported("switch_case", ())) def test_convert_unrelated_supported_instructions(self): - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() properties = backend.properties() configuration = backend.configuration() configuration.supported_instructions = [ diff --git a/test/python/providers/test_pulse_defaults.py b/test/python/providers/test_pulse_defaults.py index 33fe9ecbad65..18f849255917 100644 --- a/test/python/providers/test_pulse_defaults.py +++ b/test/python/providers/test_pulse_defaults.py @@ -17,7 +17,7 @@ import numpy as np -from qiskit.providers.fake_provider import FakeOpenPulse2Q +from qiskit.providers.fake_provider import FakeOpenPulse2Q, GenericBackendV2 from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -26,8 +26,13 @@ class TestPulseDefaults(QiskitTestCase): def setUp(self): super().setUp() - self.defs = FakeOpenPulse2Q().defaults() - self.inst_map = self.defs.instruction_schedule_map + with self.assertWarns(DeprecationWarning): + # BackendV2 does not have defaults + self.defs = FakeOpenPulse2Q().defaults() + backend = GenericBackendV2( + 2, calibrate_instructions=True, basis_gates=["cx", "u1", "u2", "u3"], seed=42 + ) + self.inst_map = backend.instruction_schedule_map def test_buffer(self): """Test getting the buffer value.""" diff --git a/test/python/pulse/test_block.py b/test/python/pulse/test_block.py index 3af2d0b510d1..c6a2c2384f64 100644 --- a/test/python/pulse/test_block.py +++ b/test/python/pulse/test_block.py @@ -28,7 +28,8 @@ class BaseTestBlock(QiskitTestCase): def setUp(self): super().setUp() - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() self.test_waveform0 = pulse.Constant(100, 0.1) self.test_waveform1 = pulse.Constant(200, 0.1) diff --git a/test/python/pulse/test_builder.py b/test/python/pulse/test_builder.py index cf029d5d98c2..563b83345496 100644 --- a/test/python/pulse/test_builder.py +++ b/test/python/pulse/test_builder.py @@ -30,7 +30,8 @@ class TestBuilder(QiskitTestCase): def setUp(self): super().setUp() - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() self.configuration = self.backend.configuration() self.defaults = self.backend.defaults() self.inst_map = self.defaults.instruction_schedule_map @@ -689,7 +690,8 @@ def test_measure_all(self): self.assertScheduleEqual(schedule, reference) - backend = Fake127QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake127QPulseV1() num_qubits = backend.configuration().num_qubits with pulse.build(backend) as schedule: regs = pulse.measure_all() @@ -764,7 +766,9 @@ def get_sched(qubit_idx: [int], backend): qc = circuit.QuantumCircuit(2) for idx in qubit_idx: qc.append(circuit.library.U2Gate(0, pi / 2), [idx]) - return compiler.schedule(compiler.transpile(qc, backend=backend), backend) + return compiler.schedule( + compiler.transpile(qc, backend=backend, optimization_level=1), backend + ) with pulse.build(self.backend) as schedule: with pulse.align_sequential(): @@ -784,7 +788,7 @@ def get_sched(qubit_idx: [int], backend): # prepare and schedule circuits that will be used. single_u2_qc = circuit.QuantumCircuit(2) single_u2_qc.append(circuit.library.U2Gate(0, pi / 2), [1]) - single_u2_qc = compiler.transpile(single_u2_qc, self.backend) + single_u2_qc = compiler.transpile(single_u2_qc, self.backend, optimization_level=1) single_u2_sched = compiler.schedule(single_u2_qc, self.backend) # sequential context @@ -809,7 +813,7 @@ def get_sched(qubit_idx: [int], backend): triple_u2_qc.append(circuit.library.U2Gate(0, pi / 2), [0]) triple_u2_qc.append(circuit.library.U2Gate(0, pi / 2), [1]) triple_u2_qc.append(circuit.library.U2Gate(0, pi / 2), [0]) - triple_u2_qc = compiler.transpile(triple_u2_qc, self.backend) + triple_u2_qc = compiler.transpile(triple_u2_qc, self.backend, optimization_level=1) align_left_reference = compiler.schedule(triple_u2_qc, self.backend, method="alap") # measurement diff --git a/test/python/pulse/test_builder_v2.py b/test/python/pulse/test_builder_v2.py index 843c8a8447fe..79d3ac5b6020 100644 --- a/test/python/pulse/test_builder_v2.py +++ b/test/python/pulse/test_builder_v2.py @@ -32,9 +32,7 @@ class TestBuilderV2(QiskitTestCase): def setUp(self): super().setUp() self.backend = GenericBackendV2( - num_qubits=27, - coupling_map=MUMBAI_CMAP, - calibrate_instructions=True, + num_qubits=27, coupling_map=MUMBAI_CMAP, calibrate_instructions=True, seed=42 ) def assertScheduleEqual(self, program, target): diff --git a/test/python/pulse/test_calibration_entries.py b/test/python/pulse/test_calibration_entries.py index 20e6773d2fd2..cc31789ef683 100644 --- a/test/python/pulse/test_calibration_entries.py +++ b/test/python/pulse/test_calibration_entries.py @@ -281,29 +281,31 @@ class TestPulseQobj(QiskitTestCase): def setUp(self): super().setUp() - self.converter = QobjToInstructionConverter( - pulse_library=[ - PulseLibraryItem(name="waveform", samples=[0.3, 0.1, 0.2, 0.2, 0.3]), - ] - ) + with self.assertWarns(DeprecationWarning): + self.converter = QobjToInstructionConverter( + pulse_library=[ + PulseLibraryItem(name="waveform", samples=[0.3, 0.1, 0.2, 0.2, 0.3]), + ] + ) def test_add_qobj(self): """Basic test PulseQobj format.""" - serialized_program = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.1 + 0j, "duration": 10}, - ), - PulseQobjInstruction( - name="waveform", - t0=20, - ch="d0", - ), - ] + with self.assertWarns(DeprecationWarning): + serialized_program = [ + PulseQobjInstruction( + name="parametric_pulse", + t0=0, + ch="d0", + label="TestPulse", + pulse_shape="constant", + parameters={"amp": 0.1 + 0j, "duration": 10}, + ), + PulseQobjInstruction( + name="waveform", + t0=20, + ch="d0", + ), + ] entry = PulseQobjDef(converter=self.converter, name="my_gate") entry.define(serialized_program) @@ -328,13 +330,14 @@ def test_add_qobj(self): def test_missing_waveform(self): """Test incomplete Qobj should raise warning and calibration returns None.""" - serialized_program = [ - PulseQobjInstruction( - name="waveform_123456", - t0=20, - ch="d0", - ), - ] + with self.assertWarns(DeprecationWarning): + serialized_program = [ + PulseQobjInstruction( + name="waveform_123456", + t0=20, + ch="d0", + ), + ] entry = PulseQobjDef(converter=self.converter, name="my_gate") entry.define(serialized_program) @@ -355,22 +358,23 @@ def test_parameterized_qobj(self): Note that pulse parameter cannot be parameterized by convention. """ - serialized_program = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.1, "duration": 10}, - ), - PulseQobjInstruction( - name="fc", - t0=0, - ch="d0", - phase="P1", - ), - ] + with self.assertWarns(DeprecationWarning): + serialized_program = [ + PulseQobjInstruction( + name="parametric_pulse", + t0=0, + ch="d0", + label="TestPulse", + pulse_shape="constant", + parameters={"amp": 0.1, "duration": 10}, + ), + PulseQobjInstruction( + name="fc", + t0=0, + ch="d0", + phase="P1", + ), + ] entry = PulseQobjDef(converter=self.converter, name="my_gate") entry.define(serialized_program) @@ -395,27 +399,27 @@ def test_parameterized_qobj(self): def test_equality(self): """Test equality evaluation between the pulse qobj entries.""" - serialized_program1 = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.1, "duration": 10}, - ) - ] - - serialized_program2 = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.2, "duration": 10}, - ) - ] + with self.assertWarns(DeprecationWarning): + serialized_program1 = [ + PulseQobjInstruction( + name="parametric_pulse", + t0=0, + ch="d0", + label="TestPulse", + pulse_shape="constant", + parameters={"amp": 0.1, "duration": 10}, + ) + ] + serialized_program2 = [ + PulseQobjInstruction( + name="parametric_pulse", + t0=0, + ch="d0", + label="TestPulse", + pulse_shape="constant", + parameters={"amp": 0.2, "duration": 10}, + ) + ] entry1 = PulseQobjDef(name="my_gate1") entry1.define(serialized_program1) @@ -435,16 +439,17 @@ def test_equality_with_schedule(self): Because the pulse qobj entry is a subclass of the schedule entry, these instances can be compared by the generated definition, i.e. Schedule. """ - serialized_program = [ - PulseQobjInstruction( - name="parametric_pulse", - t0=0, - ch="d0", - label="TestPulse", - pulse_shape="constant", - parameters={"amp": 0.1, "duration": 10}, - ) - ] + with self.assertWarns(DeprecationWarning): + serialized_program = [ + PulseQobjInstruction( + name="parametric_pulse", + t0=0, + ch="d0", + label="TestPulse", + pulse_shape="constant", + parameters={"amp": 0.1, "duration": 10}, + ) + ] entry1 = PulseQobjDef(name="qobj_entry") entry1.define(serialized_program) @@ -470,13 +475,14 @@ def test_calibration_missing_waveform(self): In this situation, parsed calibration data must become None, instead of raising an error. """ - serialized_program = [ - PulseQobjInstruction( - name="SomeMissingPulse", - t0=0, - ch="d0", - ) - ] + with self.assertWarns(DeprecationWarning): + serialized_program = [ + PulseQobjInstruction( + name="SomeMissingPulse", + t0=0, + ch="d0", + ) + ] entry = PulseQobjDef(name="qobj_entry") entry.define(serialized_program) diff --git a/test/python/pulse/test_instruction_schedule_map.py b/test/python/pulse/test_instruction_schedule_map.py index 67628ba845ae..d1610d7ebcc0 100644 --- a/test/python/pulse/test_instruction_schedule_map.py +++ b/test/python/pulse/test_instruction_schedule_map.py @@ -100,7 +100,9 @@ def test_has(self): def test_has_from_mock(self): """Test `has` and `assert_has` from mock data.""" - inst_map = FakeOpenPulse2Q().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() + inst_map = backend.defaults().instruction_schedule_map self.assertTrue(inst_map.has("u1", [0])) self.assertTrue(inst_map.has("cx", (0, 1))) self.assertTrue(inst_map.has("u3", 0)) @@ -229,7 +231,9 @@ def test_has_gate(self): def test_has_from_mock_gate(self): """Test `has` and `assert_has` from mock data.""" - inst_map = FakeOpenPulse2Q().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() + inst_map = backend.defaults().instruction_schedule_map self.assertTrue(inst_map.has(U1Gate(0), [0])) self.assertTrue(inst_map.has(CXGate(), (0, 1))) self.assertTrue(inst_map.has(U3Gate(0, 0, 0), 0)) @@ -305,12 +309,13 @@ def test_pop_gate(self): def test_sequenced_parameterized_schedule(self): """Test parameterized schedule consists of multiple instruction.""" - converter = QobjToInstructionConverter([], buffer=0) - qobjs = [ - PulseQobjInstruction(name="fc", ch="d0", t0=10, phase="P1"), - PulseQobjInstruction(name="fc", ch="d0", t0=20, phase="P2"), - PulseQobjInstruction(name="fc", ch="d0", t0=30, phase="P3"), - ] + with self.assertWarns(DeprecationWarning): + converter = QobjToInstructionConverter([], buffer=0) + qobjs = [ + PulseQobjInstruction(name="fc", ch="d0", t0=10, phase="P1"), + PulseQobjInstruction(name="fc", ch="d0", t0=20, phase="P2"), + PulseQobjInstruction(name="fc", ch="d0", t0=30, phase="P3"), + ] converted_instruction = [converter(qobj) for qobj in qobjs] inst_map = InstructionScheduleMap() @@ -530,14 +535,18 @@ def test_callable_sched1(par_b): def test_two_instmaps_equal(self): """Test eq method when two instmaps are identical.""" - instmap1 = Fake7QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = Fake7QPulseV1() + instmap1 = backend.defaults().instruction_schedule_map instmap2 = copy.deepcopy(instmap1) self.assertEqual(instmap1, instmap2) def test_two_instmaps_different(self): """Test eq method when two instmaps are not identical.""" - instmap1 = Fake7QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = Fake7QPulseV1() + instmap1 = backend.defaults().instruction_schedule_map instmap2 = copy.deepcopy(instmap1) # override one of instruction @@ -547,7 +556,9 @@ def test_two_instmaps_different(self): def test_instmap_picklable(self): """Test if instmap can be pickled.""" - instmap = Fake7QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = Fake7QPulseV1() + instmap = backend.defaults().instruction_schedule_map ser_obj = pickle.dumps(instmap) deser_instmap = pickle.loads(ser_obj) @@ -561,7 +572,9 @@ def test_instmap_picklable_with_arguments(self): in which arguments are provided by users in the form of python dict key object that is not picklable. """ - instmap = Fake7QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = Fake7QPulseV1() + instmap = backend.defaults().instruction_schedule_map param1 = Parameter("P1") param2 = Parameter("P2") @@ -581,14 +594,18 @@ def test_instmap_picklable_with_arguments(self): def test_check_backend_provider_cals(self): """Test if schedules provided by backend provider is distinguishable.""" - instmap = FakeOpenPulse2Q().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() + instmap = backend.defaults().instruction_schedule_map publisher = instmap.get("u1", (0,), P0=0).metadata["publisher"] self.assertEqual(publisher, CalibrationPublisher.BACKEND_PROVIDER) def test_check_user_cals(self): """Test if schedules provided by user is distinguishable.""" - instmap = FakeOpenPulse2Q().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() + instmap = backend.defaults().instruction_schedule_map test_u1 = Schedule() test_u1 += ShiftPhase(Parameter("P0"), DriveChannel(0)) @@ -600,7 +617,8 @@ def test_check_user_cals(self): def test_has_custom_gate(self): """Test method to check custom gate.""" - backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse2Q() instmap = backend.defaults().instruction_schedule_map self.assertFalse(instmap.has_custom_gate()) diff --git a/test/python/pulse/test_macros.py b/test/python/pulse/test_macros.py index 05937c88982d..6d87320cf480 100644 --- a/test/python/pulse/test_macros.py +++ b/test/python/pulse/test_macros.py @@ -33,12 +33,15 @@ class TestMeasure(QiskitTestCase): def setUp(self): super().setUp() - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() + self.backend_v1 = Fake27QPulseV1() + self.inst_map = self.backend.defaults().instruction_schedule_map - self.backend_v1 = Fake27QPulseV1() self.backend_v2 = GenericBackendV2( num_qubits=27, calibrate_instructions=self.backend_v1.defaults().instruction_schedule_map, + seed=42, ) def test_measure(self): @@ -171,7 +174,9 @@ def test_output_with_measure_v1_and_measure_v2_sched_with_qubit_mem_slots(self): def test_output_with_measure_v1_and_measure_v2_sched_with_meas_map(self): """Test make outputs of measure_v1 and measure_v2 with custom meas_map as list and dict consistent.""" - num_qubits_list_measure_v1 = list(range(Fake27QPulseV1().configuration().num_qubits)) + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() + num_qubits_list_measure_v1 = list(range(backend.configuration().num_qubits)) num_qubits_list_measure_v2 = list(range(self.backend_v2.num_qubits)) sched_with_meas_map_list_v1 = macros.measure( qubits=[0], backend=self.backend_v1, meas_map=[num_qubits_list_measure_v1] @@ -210,11 +215,13 @@ class TestMeasureAll(QiskitTestCase): def setUp(self): super().setUp() - self.backend_v1 = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend_v1 = FakeOpenPulse2Q() self.inst_map = self.backend_v1.defaults().instruction_schedule_map self.backend_v2 = GenericBackendV2( num_qubits=2, calibrate_instructions=self.backend_v1.defaults().instruction_schedule_map, + seed=42, ) def test_measure_all(self): diff --git a/test/python/pulse/test_schedule.py b/test/python/pulse/test_schedule.py index 40d637887ed0..5e5676e7c2d9 100644 --- a/test/python/pulse/test_schedule.py +++ b/test/python/pulse/test_schedule.py @@ -61,7 +61,8 @@ def linear(duration, slope, intercept): return slope * x + intercept self.linear = linear - self.config = FakeOpenPulse2Q().configuration() + with self.assertWarns(DeprecationWarning): + self.config = FakeOpenPulse2Q().configuration() class TestScheduleBuilding(BaseTestSchedule): diff --git a/test/python/pulse/test_transforms.py b/test/python/pulse/test_transforms.py index 5918a6e6993d..c16405cff4d1 100644 --- a/test/python/pulse/test_transforms.py +++ b/test/python/pulse/test_transforms.py @@ -46,7 +46,8 @@ class TestAlignMeasures(QiskitTestCase): def setUp(self): super().setUp() - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() self.config = self.backend.configuration() self.inst_map = self.backend.defaults().instruction_schedule_map self.short_pulse = pulse.Waveform( @@ -202,7 +203,8 @@ class TestAddImplicitAcquires(QiskitTestCase): def setUp(self): super().setUp() - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() self.config = self.backend.configuration() self.short_pulse = pulse.Waveform( samples=np.array([0.02739068], dtype=np.complex128), name="p0" diff --git a/test/python/qobj/test_pulse_converter.py b/test/python/qobj/test_pulse_converter.py index cacbde2cd8b5..528d87244fe8 100644 --- a/test/python/qobj/test_pulse_converter.py +++ b/test/python/qobj/test_pulse_converter.py @@ -56,29 +56,33 @@ class TestInstructionToQobjConverter(QiskitTestCase): def test_drive_instruction(self): """Test converted qobj from Play.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = Play(Waveform(np.arange(0, 0.01), name="linear"), DriveChannel(0)) - valid_qobj = PulseQobjInstruction(name="linear", ch="d0", t0=0) - self.assertEqual(converter(0, instruction), valid_qobj) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + instruction = Play(Waveform(np.arange(0, 0.01), name="linear"), DriveChannel(0)) + valid_qobj = PulseQobjInstruction(name="linear", ch="d0", t0=0) + self.assertEqual(converter(0, instruction), valid_qobj) def test_gaussian_pulse_instruction(self): """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" amp = 0.3 angle = -0.7 - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) instruction = Play(Gaussian(duration=25, sigma=15, amp=amp, angle=angle), DriveChannel(0)) - valid_qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="gaussian", - ch="d0", - t0=0, - parameters={"duration": 25, "sigma": 15, "amp": amp * np.exp(1j * angle)}, - ) - self.assertEqual(converter(0, instruction), valid_qobj) + with self.assertWarns(DeprecationWarning): + valid_qobj = PulseQobjInstruction( + name="parametric_pulse", + pulse_shape="gaussian", + ch="d0", + t0=0, + parameters={"duration": 25, "sigma": 15, "amp": amp * np.exp(1j * angle)}, + ) + self.assertEqual(converter(0, instruction), valid_qobj) def test_gaussian_square_pulse_instruction(self): """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) amp = 0.7 angle = -0.6 instruction = Play( @@ -86,110 +90,129 @@ def test_gaussian_square_pulse_instruction(self): MeasureChannel(1), ) - valid_qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="gaussian_square", - ch="m1", - t0=10, - parameters={ - "duration": 1500, - "sigma": 15, - "amp": amp * np.exp(1j * angle), - "width": 1300, - }, - ) - self.assertEqual(converter(10, instruction), valid_qobj) + with self.assertWarns(DeprecationWarning): + valid_qobj = PulseQobjInstruction( + name="parametric_pulse", + pulse_shape="gaussian_square", + ch="m1", + t0=10, + parameters={ + "duration": 1500, + "sigma": 15, + "amp": amp * np.exp(1j * angle), + "width": 1300, + }, + ) + self.assertEqual(converter(10, instruction), valid_qobj) def test_constant_pulse_instruction(self): """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) instruction = Play(Constant(duration=25, amp=1, angle=np.pi), ControlChannel(2)) - valid_qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="constant", - ch="u2", - t0=20, - parameters={"duration": 25, "amp": 1 * np.exp(1j * np.pi)}, - ) - self.assertEqual(converter(20, instruction), valid_qobj) + with self.assertWarns(DeprecationWarning): + valid_qobj = PulseQobjInstruction( + name="parametric_pulse", + pulse_shape="constant", + ch="u2", + t0=20, + parameters={"duration": 25, "amp": 1 * np.exp(1j * np.pi)}, + ) + self.assertEqual(converter(20, instruction), valid_qobj) def test_drag_pulse_instruction(self): """Test that parametric pulses are correctly converted to PulseQobjInstructions.""" amp = 0.7 angle = -0.6 - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) instruction = Play( Drag(duration=25, sigma=15, amp=amp, angle=angle, beta=0.5), DriveChannel(0) ) - valid_qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="drag", - ch="d0", - t0=30, - parameters={"duration": 25, "sigma": 15, "amp": amp * np.exp(1j * angle), "beta": 0.5}, - ) - self.assertEqual(converter(30, instruction), valid_qobj) + with self.assertWarns(DeprecationWarning): + valid_qobj = PulseQobjInstruction( + name="parametric_pulse", + pulse_shape="drag", + ch="d0", + t0=30, + parameters={ + "duration": 25, + "sigma": 15, + "amp": amp * np.exp(1j * angle), + "beta": 0.5, + }, + ) + self.assertEqual(converter(30, instruction), valid_qobj) def test_frame_change(self): """Test converted qobj from ShiftPhase.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - valid_qobj = PulseQobjInstruction(name="fc", ch="d0", t0=0, phase=0.1) - instruction = ShiftPhase(0.1, DriveChannel(0)) - self.assertEqual(converter(0, instruction), valid_qobj) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + valid_qobj = PulseQobjInstruction(name="fc", ch="d0", t0=0, phase=0.1) + instruction = ShiftPhase(0.1, DriveChannel(0)) + self.assertEqual(converter(0, instruction), valid_qobj) def test_set_phase(self): """Test converted qobj from ShiftPhase.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = SetPhase(3.14, DriveChannel(0)) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + instruction = SetPhase(3.14, DriveChannel(0)) - valid_qobj = PulseQobjInstruction(name="setp", ch="d0", t0=0, phase=3.14) + valid_qobj = PulseQobjInstruction(name="setp", ch="d0", t0=0, phase=3.14) - self.assertEqual(converter(0, instruction), valid_qobj) + self.assertEqual(converter(0, instruction), valid_qobj) def test_set_frequency(self): """Test converted qobj from SetFrequency.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = SetFrequency(8.0e9, DriveChannel(0)) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + instruction = SetFrequency(8.0e9, DriveChannel(0)) - valid_qobj = PulseQobjInstruction(name="setf", ch="d0", t0=0, frequency=8.0) + valid_qobj = PulseQobjInstruction(name="setf", ch="d0", t0=0, frequency=8.0) - self.assertEqual(converter(0, instruction), valid_qobj) + self.assertEqual(converter(0, instruction), valid_qobj) def test_shift_frequency(self): """Test converted qobj from ShiftFrequency.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) - instruction = ShiftFrequency(8.0e9, DriveChannel(0)) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + instruction = ShiftFrequency(8.0e9, DriveChannel(0)) - valid_qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=0, frequency=8.0) + valid_qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=0, frequency=8.0) - self.assertEqual(converter(0, instruction), valid_qobj) + self.assertEqual(converter(0, instruction), valid_qobj) def test_acquire(self): """Test converted qobj from AcquireInstruction.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) instruction = Acquire(10, AcquireChannel(0), MemorySlot(0), RegisterSlot(0)) - valid_qobj = PulseQobjInstruction( - name="acquire", t0=0, duration=10, qubits=[0], memory_slot=[0], register_slot=[0] - ) - self.assertEqual(converter(0, instruction), valid_qobj) + with self.assertWarns(DeprecationWarning): + valid_qobj = PulseQobjInstruction( + name="acquire", t0=0, duration=10, qubits=[0], memory_slot=[0], register_slot=[0] + ) + self.assertEqual(converter(0, instruction), valid_qobj) # without register instruction = Acquire(10, AcquireChannel(0), MemorySlot(0)) - valid_qobj = PulseQobjInstruction( - name="acquire", t0=0, duration=10, qubits=[0], memory_slot=[0] - ) - self.assertEqual(converter(0, instruction), valid_qobj) + with self.assertWarns(DeprecationWarning): + valid_qobj = PulseQobjInstruction( + name="acquire", t0=0, duration=10, qubits=[0], memory_slot=[0] + ) + self.assertEqual(converter(0, instruction), valid_qobj) def test_snapshot(self): """Test converted qobj from Snapshot.""" - converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) + with self.assertWarns(DeprecationWarning): + converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) instruction = Snapshot(label="label", snapshot_type="type") - valid_qobj = PulseQobjInstruction(name="snapshot", t0=0, label="label", type="type") + with self.assertWarns(DeprecationWarning): + valid_qobj = PulseQobjInstruction(name="snapshot", t0=0, label="label", type="type") - self.assertEqual(converter(0, instruction), valid_qobj) + self.assertEqual(converter(0, instruction), valid_qobj) class TestQobjToInstructionConverter(QiskitTestCase): @@ -198,9 +221,10 @@ class TestQobjToInstructionConverter(QiskitTestCase): def setUp(self): super().setUp() self.linear = Waveform(np.arange(0, 0.01), name="linear") - self.pulse_library = [ - PulseLibraryItem(name=self.linear.name, samples=self.linear.samples.tolist()) - ] + with self.assertWarns(DeprecationWarning): + self.pulse_library = [ + PulseLibraryItem(name=self.linear.name, samples=self.linear.samples.tolist()) + ] self.converter = QobjToInstructionConverter(self.pulse_library, buffer=0) self.num_qubits = 2 @@ -208,7 +232,8 @@ def setUp(self): def test_drive_instruction(self): """Test converted qobj from PulseInstruction.""" instruction = Play(self.linear, DriveChannel(0)) - qobj = PulseQobjInstruction(name="linear", ch="d0", t0=10) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="linear", ch="d0", t0=10) converted_instruction = self.converter(qobj) self.assertEqual(converted_instruction.instructions[0][-1], instruction) @@ -218,14 +243,15 @@ def test_parametric_pulses(self): Gaussian(duration=25, sigma=15, amp=0.5, angle=np.pi / 2, name="pulse1"), DriveChannel(0), ) - qobj = PulseQobjInstruction( - name="parametric_pulse", - label="pulse1", - pulse_shape="gaussian", - ch="d0", - t0=0, - parameters={"duration": 25, "sigma": 15, "amp": 0.5j}, - ) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction( + name="parametric_pulse", + label="pulse1", + pulse_shape="gaussian", + ch="d0", + t0=0, + parameters={"duration": 25, "sigma": 15, "amp": 0.5j}, + ) converted_instruction = self.converter(qobj) self.assertEqual(converted_instruction.start_time, 0) self.assertEqual(converted_instruction.duration, 25) @@ -238,19 +264,21 @@ def test_parametric_pulses_no_label(self): short_pulse_id = hashlib.md5(base_str.encode("utf-8")).hexdigest()[:4] pulse_name = f"gaussian_{short_pulse_id}" - qobj = PulseQobjInstruction( - name="parametric_pulse", - pulse_shape="gaussian", - ch="d0", - t0=0, - parameters={"duration": 25, "sigma": 15, "amp": -0.5 + 0.2j}, - ) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction( + name="parametric_pulse", + pulse_shape="gaussian", + ch="d0", + t0=0, + parameters={"duration": 25, "sigma": 15, "amp": -0.5 + 0.2j}, + ) converted_instruction = self.converter(qobj) self.assertEqual(converted_instruction.instructions[0][-1].pulse.name, pulse_name) def test_frame_change(self): """Test converted qobj from ShiftPhase.""" - qobj = PulseQobjInstruction(name="fc", ch="m0", t0=0, phase=0.1) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="fc", ch="m0", t0=0, phase=0.1) converted_instruction = self.converter(qobj) instruction = ShiftPhase(0.1, MeasureChannel(0)) @@ -263,7 +291,8 @@ def test_parameterized_frame_change(self): instruction = ShiftPhase(4.0, MeasureChannel(0)) shifted = instruction << 10 - qobj = PulseQobjInstruction(name="fc", ch="m0", t0=10, phase="P1*2") + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="fc", ch="m0", t0=10, phase="P1*2") converted_instruction = self.converter(qobj) self.assertIsInstance(converted_instruction, Schedule) @@ -277,7 +306,8 @@ def test_parameterized_frame_change(self): def test_set_phase(self): """Test converted qobj from SetPhase.""" - qobj = PulseQobjInstruction(name="setp", ch="m0", t0=0, phase=3.14) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="setp", ch="m0", t0=0, phase=3.14) converted_instruction = self.converter(qobj) instruction = SetPhase(3.14, MeasureChannel(0)) @@ -287,7 +317,8 @@ def test_set_phase(self): def test_parameterized_set_phase(self): """Test converted qobj from SetPhase, with parameterized phase.""" - qobj = PulseQobjInstruction(name="setp", ch="m0", t0=0, phase="p/2") + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="setp", ch="m0", t0=0, phase="p/2") converted_instruction = self.converter(qobj) self.assertIsInstance(converted_instruction, Schedule) @@ -303,7 +334,8 @@ def test_set_frequency(self): """Test converted qobj from SetFrequency.""" instruction = SetFrequency(8.0e9, DriveChannel(0)) - qobj = PulseQobjInstruction(name="setf", ch="d0", t0=0, frequency=8.0) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="setf", ch="d0", t0=0, frequency=8.0) converted_instruction = self.converter(qobj) self.assertEqual(converted_instruction.start_time, 0) @@ -313,7 +345,8 @@ def test_set_frequency(self): def test_parameterized_set_frequency(self): """Test converted qobj from SetFrequency, when passing a parameterized frequency.""" - qobj = PulseQobjInstruction(name="setf", ch="d0", t0=2, frequency="f") + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="setf", ch="d0", t0=2, frequency="f") self.assertTrue("frequency" in qobj.to_dict()) converted_instruction = self.converter(qobj) @@ -332,7 +365,8 @@ def test_shift_frequency(self): """Test converted qobj from ShiftFrequency.""" instruction = ShiftFrequency(8.0e9, DriveChannel(0)) - qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=0, frequency=8.0) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=0, frequency=8.0) converted_instruction = self.converter(qobj) self.assertEqual(converted_instruction.start_time, 0) @@ -342,9 +376,8 @@ def test_shift_frequency(self): def test_parameterized_shift_frequency(self): """Test converted qobj from ShiftFrequency, with a parameterized frequency.""" - instruction = ShiftFrequency(8.0e9, DriveChannel(0)) - - qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=1, frequency="f / 1000") + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="shiftf", ch="d0", t0=1, frequency="f / 1000") self.assertTrue("frequency" in qobj.to_dict()) converted_instruction = self.converter(qobj) @@ -363,7 +396,8 @@ def test_delay(self): """Test converted qobj from Delay.""" instruction = Delay(10, DriveChannel(0)) - qobj = PulseQobjInstruction(name="delay", ch="d0", t0=0, duration=10) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="delay", ch="d0", t0=0, duration=10) converted_instruction = self.converter(qobj) self.assertTrue("delay" in qobj.to_dict().values()) @@ -382,17 +416,19 @@ def test_acquire(self): kernel=Kernel(name="test_kern", test_params="test"), discriminator=Discriminator(name="test_disc", test_params=1.0), ) - - qobj = PulseQobjInstruction( - name="acquire", - t0=0, - duration=10, - qubits=[0, 1], - memory_slot=[0, 1], - register_slot=[0, 1], - kernels=[QobjMeasurementOption(name="test_kern", params={"test_params": "test"})], - discriminators=[QobjMeasurementOption(name="test_disc", params={"test_params": 1.0})], - ) + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction( + name="acquire", + t0=0, + duration=10, + qubits=[0, 1], + memory_slot=[0, 1], + register_slot=[0, 1], + kernels=[QobjMeasurementOption(name="test_kern", params={"test_params": "test"})], + discriminators=[ + QobjMeasurementOption(name="test_disc", params={"test_params": 1.0}) + ], + ) converted_instruction = self.converter(qobj) self.assertEqual(converted_instruction.start_time, 0) @@ -408,7 +444,8 @@ def test_snapshot(self): instruction = Snapshot(label="label", snapshot_type="type") shifted = instruction << 10 - qobj = PulseQobjInstruction(name="snapshot", t0=10, label="label", type="type") + with self.assertWarns(DeprecationWarning): + qobj = PulseQobjInstruction(name="snapshot", t0=10, label="label", type="type") converted_instruction = self.converter(qobj) self.assertEqual(converted_instruction.start_time, shifted.start_time) @@ -417,17 +454,22 @@ def test_snapshot(self): def test_instruction_name_collision(self): """Avoid command name collision of pulse library items.""" - pulse_library_from_backend_x = [ - PulseLibraryItem(name="pulse123", samples=[0.1, 0.1, 0.1]), - PulseLibraryItem(name="pulse456", samples=[0.3, 0.3, 0.3]), - ] + with self.assertWarns(DeprecationWarning): + pulse_library_from_backend_x = [ + PulseLibraryItem(name="pulse123", samples=[0.1, 0.1, 0.1]), + PulseLibraryItem(name="pulse456", samples=[0.3, 0.3, 0.3]), + ] converter_of_backend_x = QobjToInstructionConverter(pulse_library_from_backend_x, buffer=0) - pulse_library_from_backend_y = [PulseLibraryItem(name="pulse123", samples=[0.2, 0.2, 0.2])] + with self.assertWarns(DeprecationWarning): + pulse_library_from_backend_y = [ + PulseLibraryItem(name="pulse123", samples=[0.2, 0.2, 0.2]) + ] converter_of_backend_y = QobjToInstructionConverter(pulse_library_from_backend_y, buffer=0) - qobj1 = PulseQobjInstruction(name="pulse123", qubits=[0], t0=0, ch="d0") - qobj2 = PulseQobjInstruction(name="pulse456", qubits=[0], t0=0, ch="d0") + with self.assertWarns(DeprecationWarning): + qobj1 = PulseQobjInstruction(name="pulse123", qubits=[0], t0=0, ch="d0") + qobj2 = PulseQobjInstruction(name="pulse456", qubits=[0], t0=0, ch="d0") sched_out_x = converter_of_backend_x(qobj1) sched_out_y = converter_of_backend_y(qobj1) @@ -446,21 +488,23 @@ class TestLoConverter(QiskitTestCase): def test_qubit_los(self): """Test qubit channel configuration.""" user_lo_config = LoConfig({DriveChannel(0): 1.3e9}) - converter = LoConfigConverter( - PulseQobjExperimentConfig, [1.2e9], [3.4e9], [(0.0, 5e9)], [(0.0, 5e9)] - ) - - valid_qobj = PulseQobjExperimentConfig(qubit_lo_freq=[1.3]) + with self.assertWarns(DeprecationWarning): + converter = LoConfigConverter( + PulseQobjExperimentConfig, [1.2e9], [3.4e9], [(0.0, 5e9)], [(0.0, 5e9)] + ) + valid_qobj = PulseQobjExperimentConfig(qubit_lo_freq=[1.3]) - self.assertEqual(converter(user_lo_config), valid_qobj) + with self.assertWarns(DeprecationWarning): + self.assertEqual(converter(user_lo_config), valid_qobj) def test_meas_los(self): """Test measurement channel configuration.""" user_lo_config = LoConfig({MeasureChannel(0): 3.5e9}) - converter = LoConfigConverter( - PulseQobjExperimentConfig, [1.2e9], [3.4e9], [(0.0, 5e9)], [(0.0, 5e9)] - ) + with self.assertWarns(DeprecationWarning): + converter = LoConfigConverter( + PulseQobjExperimentConfig, [1.2e9], [3.4e9], [(0.0, 5e9)], [(0.0, 5e9)] + ) - valid_qobj = PulseQobjExperimentConfig(meas_lo_freq=[3.5]) + valid_qobj = PulseQobjExperimentConfig(meas_lo_freq=[3.5]) - self.assertEqual(converter(user_lo_config), valid_qobj) + self.assertEqual(converter(user_lo_config), valid_qobj) diff --git a/test/python/qobj/test_qobj.py b/test/python/qobj/test_qobj.py index c0b96a1bda5e..ae59d6de00a5 100644 --- a/test/python/qobj/test_qobj.py +++ b/test/python/qobj/test_qobj.py @@ -41,19 +41,20 @@ class TestQASMQobj(QiskitTestCase): def setUp(self): super().setUp() - self.valid_qobj = QasmQobj( - qobj_id="12345", - header=QobjHeader(), - config=QasmQobjConfig(shots=1024, memory_slots=2), - experiments=[ - QasmQobjExperiment( - instructions=[ - QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), - QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), - ] - ) - ], - ) + with self.assertWarns(DeprecationWarning): + self.valid_qobj = QasmQobj( + qobj_id="12345", + header=QobjHeader(), + config=QasmQobjConfig(shots=1024, memory_slots=2), + experiments=[ + QasmQobjExperiment( + instructions=[ + QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), + QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), + ] + ) + ], + ) self.valid_dict = { "qobj_id": "12345", @@ -70,55 +71,59 @@ def setUp(self): } ], } - - self.bad_qobj = copy.deepcopy(self.valid_qobj) + with self.assertWarns(DeprecationWarning): + self.bad_qobj = copy.deepcopy(self.valid_qobj) self.bad_qobj.experiments = [] def test_from_dict_per_class(self): """Test Qobj and its subclass representations given a dictionary.""" - test_parameters = { - QasmQobj: (self.valid_qobj, self.valid_dict), - QasmQobjConfig: ( - QasmQobjConfig(shots=1, memory_slots=2), - {"shots": 1, "memory_slots": 2}, - ), - QasmQobjExperiment: ( - QasmQobjExperiment( - instructions=[QasmQobjInstruction(name="u1", qubits=[1], params=[0.4])] + with self.assertWarns(DeprecationWarning): + test_parameters = { + QasmQobj: (self.valid_qobj, self.valid_dict), + QasmQobjConfig: ( + QasmQobjConfig(shots=1, memory_slots=2), + {"shots": 1, "memory_slots": 2}, ), - {"instructions": [{"name": "u1", "qubits": [1], "params": [0.4]}]}, - ), - QasmQobjInstruction: ( - QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), - {"name": "u1", "qubits": [1], "params": [0.4]}, - ), - } + QasmQobjExperiment: ( + QasmQobjExperiment( + instructions=[QasmQobjInstruction(name="u1", qubits=[1], params=[0.4])] + ), + {"instructions": [{"name": "u1", "qubits": [1], "params": [0.4]}]}, + ), + QasmQobjInstruction: ( + QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), + {"name": "u1", "qubits": [1], "params": [0.4]}, + ), + } for qobj_class, (qobj_item, expected_dict) in test_parameters.items(): with self.subTest(msg=str(qobj_class)): - self.assertEqual(qobj_item, qobj_class.from_dict(expected_dict)) + with self.assertWarns(DeprecationWarning): + qobj = qobj_class.from_dict(expected_dict) + self.assertEqual(qobj_item, qobj) def test_snapshot_instruction_to_dict(self): """Test snapshot instruction to dict.""" - valid_qobj = QasmQobj( - qobj_id="12345", - header=QobjHeader(), - config=QasmQobjConfig(shots=1024, memory_slots=2), - experiments=[ - QasmQobjExperiment( - instructions=[ - QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), - QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), - QasmQobjInstruction( - name="snapshot", - qubits=[1], - snapshot_type="statevector", - label="my_snap", - ), - ] - ) - ], - ) + with self.assertWarns(DeprecationWarning): + valid_qobj = QasmQobj( + qobj_id="12345", + header=QobjHeader(), + config=QasmQobjConfig(shots=1024, memory_slots=2), + experiments=[ + QasmQobjExperiment( + instructions=[ + QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), + QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), + QasmQobjInstruction( + name="snapshot", + qubits=[1], + snapshot_type="statevector", + label="my_snap", + ), + ] + ) + ], + ) res = valid_qobj.to_dict() expected_dict = { "qobj_id": "12345", @@ -147,25 +152,26 @@ def test_snapshot_instruction_to_dict(self): def test_snapshot_instruction_from_dict(self): """Test snapshot instruction from dict.""" - expected_qobj = QasmQobj( - qobj_id="12345", - header=QobjHeader(), - config=QasmQobjConfig(shots=1024, memory_slots=2), - experiments=[ - QasmQobjExperiment( - instructions=[ - QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), - QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), - QasmQobjInstruction( - name="snapshot", - qubits=[1], - snapshot_type="statevector", - label="my_snap", - ), - ] - ) - ], - ) + with self.assertWarns(DeprecationWarning): + expected_qobj = QasmQobj( + qobj_id="12345", + header=QobjHeader(), + config=QasmQobjConfig(shots=1024, memory_slots=2), + experiments=[ + QasmQobjExperiment( + instructions=[ + QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]), + QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]), + QasmQobjInstruction( + name="snapshot", + qubits=[1], + snapshot_type="statevector", + label="my_snap", + ), + ] + ) + ], + ) qobj_dict = { "qobj_id": "12345", "type": "QASM", @@ -187,7 +193,9 @@ def test_snapshot_instruction_from_dict(self): } ], } - self.assertEqual(expected_qobj, QasmQobj.from_dict(qobj_dict)) + with self.assertWarns(DeprecationWarning): + qobj = QasmQobj.from_dict(qobj_dict) + self.assertEqual(expected_qobj, qobj) def test_change_qobj_after_compile(self): """Test modifying Qobj parameters after compile.""" @@ -202,7 +210,8 @@ def test_change_qobj_after_compile(self): qc1.measure(qr, cr) qc2.measure(qr, cr) circuits = [qc1, qc2] - qobj1 = assemble(circuits, shots=1024, seed=88) + with self.assertWarns(DeprecationWarning): + qobj1 = assemble(circuits, shots=1024, seed=88) qobj1.experiments[0].config.shots = 50 qobj1.experiments[1].config.shots = 1 self.assertTrue(qobj1.experiments[0].config.shots == 50) @@ -211,27 +220,28 @@ def test_change_qobj_after_compile(self): def test_gate_calibrations_to_dict(self): """Test gate calibrations to dict.""" - - pulse_library = [PulseLibraryItem(name="test", samples=[1j, 1j])] - valid_qobj = QasmQobj( - qobj_id="12345", - header=QobjHeader(), - config=QasmQobjConfig(shots=1024, memory_slots=2, pulse_library=pulse_library), - experiments=[ - QasmQobjExperiment( - instructions=[QasmQobjInstruction(name="u1", qubits=[1], params=[0.4])], - config=QasmQobjConfig( - calibrations=QasmExperimentCalibrations( - gates=[ - GateCalibration( - name="u1", qubits=[1], params=[0.4], instructions=[] - ) - ] - ) - ), - ) - ], - ) + with self.assertWarns(DeprecationWarning): + pulse_library = [PulseLibraryItem(name="test", samples=[1j, 1j])] + with self.assertWarns(DeprecationWarning): + valid_qobj = QasmQobj( + qobj_id="12345", + header=QobjHeader(), + config=QasmQobjConfig(shots=1024, memory_slots=2, pulse_library=pulse_library), + experiments=[ + QasmQobjExperiment( + instructions=[QasmQobjInstruction(name="u1", qubits=[1], params=[0.4])], + config=QasmQobjConfig( + calibrations=QasmExperimentCalibrations( + gates=[ + GateCalibration( + name="u1", qubits=[1], params=[0.4], instructions=[] + ) + ] + ) + ), + ) + ], + ) res = valid_qobj.to_dict() expected_dict = { "qobj_id": "12345", @@ -265,48 +275,51 @@ class TestPulseQobj(QiskitTestCase): def setUp(self): super().setUp() - self.valid_qobj = PulseQobj( - qobj_id="12345", - header=QobjHeader(), - config=PulseQobjConfig( - shots=1024, - memory_slots=2, - meas_level=1, - memory_slot_size=8192, - meas_return="avg", - pulse_library=[ - PulseLibraryItem(name="pulse0", samples=[0.0 + 0.0j, 0.5 + 0.0j, 0.0 + 0.0j]) + with self.assertWarns(DeprecationWarning): + self.valid_qobj = PulseQobj( + qobj_id="12345", + header=QobjHeader(), + config=PulseQobjConfig( + shots=1024, + memory_slots=2, + meas_level=1, + memory_slot_size=8192, + meas_return="avg", + pulse_library=[ + PulseLibraryItem( + name="pulse0", samples=[0.0 + 0.0j, 0.5 + 0.0j, 0.0 + 0.0j] + ) + ], + qubit_lo_freq=[4.9], + meas_lo_freq=[6.9], + rep_time=1000, + ), + experiments=[ + PulseQobjExperiment( + instructions=[ + PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), + PulseQobjInstruction(name="fc", t0=5, ch="d0", phase=1.57), + PulseQobjInstruction(name="fc", t0=5, ch="d0", phase=0.0), + PulseQobjInstruction(name="fc", t0=5, ch="d0", phase="P1"), + PulseQobjInstruction(name="setp", t0=10, ch="d0", phase=3.14), + PulseQobjInstruction(name="setf", t0=10, ch="d0", frequency=8.0), + PulseQobjInstruction(name="shiftf", t0=10, ch="d0", frequency=4.0), + PulseQobjInstruction( + name="acquire", + t0=15, + duration=5, + qubits=[0], + memory_slot=[0], + kernels=[ + QobjMeasurementOption( + name="boxcar", params={"start_window": 0, "stop_window": 5} + ) + ], + ), + ] + ) ], - qubit_lo_freq=[4.9], - meas_lo_freq=[6.9], - rep_time=1000, - ), - experiments=[ - PulseQobjExperiment( - instructions=[ - PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), - PulseQobjInstruction(name="fc", t0=5, ch="d0", phase=1.57), - PulseQobjInstruction(name="fc", t0=5, ch="d0", phase=0.0), - PulseQobjInstruction(name="fc", t0=5, ch="d0", phase="P1"), - PulseQobjInstruction(name="setp", t0=10, ch="d0", phase=3.14), - PulseQobjInstruction(name="setf", t0=10, ch="d0", frequency=8.0), - PulseQobjInstruction(name="shiftf", t0=10, ch="d0", frequency=4.0), - PulseQobjInstruction( - name="acquire", - t0=15, - duration=5, - qubits=[0], - memory_slot=[0], - kernels=[ - QobjMeasurementOption( - name="boxcar", params={"start_window": 0, "stop_window": 5} - ) - ], - ), - ] - ) - ], - ) + ) self.valid_dict = { "qobj_id": "12345", "type": "PULSE", @@ -350,87 +363,91 @@ def setUp(self): def test_from_dict_per_class(self): """Test converting to Qobj and its subclass representations given a dictionary.""" - test_parameters = { - PulseQobj: (self.valid_qobj, self.valid_dict), - PulseQobjConfig: ( - PulseQobjConfig( - meas_level=1, - memory_slot_size=8192, - meas_return="avg", - pulse_library=[PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j])], - qubit_lo_freq=[4.9], - meas_lo_freq=[6.9], - rep_time=1000, + with self.assertWarns(DeprecationWarning): + test_parameters = { + PulseQobj: (self.valid_qobj, self.valid_dict), + PulseQobjConfig: ( + PulseQobjConfig( + meas_level=1, + memory_slot_size=8192, + meas_return="avg", + pulse_library=[PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j])], + qubit_lo_freq=[4.9], + meas_lo_freq=[6.9], + rep_time=1000, + ), + { + "meas_level": 1, + "memory_slot_size": 8192, + "meas_return": "avg", + "pulse_library": [{"name": "pulse0", "samples": [0.1 + 0j]}], + "qubit_lo_freq": [4.9], + "meas_lo_freq": [6.9], + "rep_time": 1000, + }, ), - { - "meas_level": 1, - "memory_slot_size": 8192, - "meas_return": "avg", - "pulse_library": [{"name": "pulse0", "samples": [0.1 + 0j]}], - "qubit_lo_freq": [4.9], - "meas_lo_freq": [6.9], - "rep_time": 1000, - }, - ), - PulseLibraryItem: ( - PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j]), - {"name": "pulse0", "samples": [0.1 + 0j]}, - ), - PulseQobjExperiment: ( - PulseQobjExperiment( - instructions=[PulseQobjInstruction(name="pulse0", t0=0, ch="d0")] + PulseLibraryItem: ( + PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j]), + {"name": "pulse0", "samples": [0.1 + 0j]}, ), - {"instructions": [{"name": "pulse0", "t0": 0, "ch": "d0"}]}, - ), - PulseQobjInstruction: ( - PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), - {"name": "pulse0", "t0": 0, "ch": "d0"}, - ), - } + PulseQobjExperiment: ( + PulseQobjExperiment( + instructions=[PulseQobjInstruction(name="pulse0", t0=0, ch="d0")] + ), + {"instructions": [{"name": "pulse0", "t0": 0, "ch": "d0"}]}, + ), + PulseQobjInstruction: ( + PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), + {"name": "pulse0", "t0": 0, "ch": "d0"}, + ), + } for qobj_class, (qobj_item, expected_dict) in test_parameters.items(): with self.subTest(msg=str(qobj_class)): - self.assertEqual(qobj_item, qobj_class.from_dict(expected_dict)) + with self.assertWarns(DeprecationWarning): + qobj = qobj_class.from_dict(expected_dict) + self.assertEqual(qobj_item, qobj) def test_to_dict_per_class(self): """Test converting from Qobj and its subclass representations given a dictionary.""" - test_parameters = { - PulseQobj: (self.valid_qobj, self.valid_dict), - PulseQobjConfig: ( - PulseQobjConfig( - meas_level=1, - memory_slot_size=8192, - meas_return="avg", - pulse_library=[PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j])], - qubit_lo_freq=[4.9], - meas_lo_freq=[6.9], - rep_time=1000, + with self.assertWarns(DeprecationWarning): + test_parameters = { + PulseQobj: (self.valid_qobj, self.valid_dict), + PulseQobjConfig: ( + PulseQobjConfig( + meas_level=1, + memory_slot_size=8192, + meas_return="avg", + pulse_library=[PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j])], + qubit_lo_freq=[4.9], + meas_lo_freq=[6.9], + rep_time=1000, + ), + { + "meas_level": 1, + "memory_slot_size": 8192, + "meas_return": "avg", + "pulse_library": [{"name": "pulse0", "samples": [0.1 + 0j]}], + "qubit_lo_freq": [4.9], + "meas_lo_freq": [6.9], + "rep_time": 1000, + }, ), - { - "meas_level": 1, - "memory_slot_size": 8192, - "meas_return": "avg", - "pulse_library": [{"name": "pulse0", "samples": [0.1 + 0j]}], - "qubit_lo_freq": [4.9], - "meas_lo_freq": [6.9], - "rep_time": 1000, - }, - ), - PulseLibraryItem: ( - PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j]), - {"name": "pulse0", "samples": [0.1 + 0j]}, - ), - PulseQobjExperiment: ( - PulseQobjExperiment( - instructions=[PulseQobjInstruction(name="pulse0", t0=0, ch="d0")] + PulseLibraryItem: ( + PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j]), + {"name": "pulse0", "samples": [0.1 + 0j]}, ), - {"instructions": [{"name": "pulse0", "t0": 0, "ch": "d0"}]}, - ), - PulseQobjInstruction: ( - PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), - {"name": "pulse0", "t0": 0, "ch": "d0"}, - ), - } + PulseQobjExperiment: ( + PulseQobjExperiment( + instructions=[PulseQobjInstruction(name="pulse0", t0=0, ch="d0")] + ), + {"instructions": [{"name": "pulse0", "t0": 0, "ch": "d0"}]}, + ), + PulseQobjInstruction: ( + PulseQobjInstruction(name="pulse0", t0=0, ch="d0"), + {"name": "pulse0", "t0": 0, "ch": "d0"}, + ), + } for qobj_class, (qobj_item, expected_dict) in test_parameters.items(): with self.subTest(msg=str(qobj_class)): diff --git a/test/python/qobj/test_qobj_identifiers.py b/test/python/qobj/test_qobj_identifiers.py index d319aa4611de..84eb58a1d782 100644 --- a/test/python/qobj/test_qobj_identifiers.py +++ b/test/python/qobj/test_qobj_identifiers.py @@ -36,7 +36,8 @@ def setUp(self): self.circuits = [qc] def test_qobj_identifiers(self): - qobj = assemble(self.circuits) + with self.assertWarns(DeprecationWarning): + qobj = assemble(self.circuits) exp = qobj.experiments[0] self.assertIn(self.qr_name, (x[0] for x in exp.header.qubit_labels)) self.assertIn(self.cr_name, (x[0] for x in exp.header.clbit_labels)) diff --git a/test/python/qpy/test_circuit_load_from_qpy.py b/test/python/qpy/test_circuit_load_from_qpy.py index a050a70cac5f..8f0cffb36a5f 100644 --- a/test/python/qpy/test_circuit_load_from_qpy.py +++ b/test/python/qpy/test_circuit_load_from_qpy.py @@ -61,8 +61,10 @@ class TestCalibrationPasses(QpyCircuitTestCase): def setUp(self): super().setUp() - # This backend provides CX(0,1) with native ECR direction. - self.inst_map = Fake27QPulseV1().defaults().instruction_schedule_map + # TODO remove context once https://github.com/Qiskit/qiskit/issues/12759 is fixed + with self.assertWarns(DeprecationWarning): + # This backend provides CX(0,1) with native ECR direction. + self.inst_map = Fake27QPulseV1().defaults().instruction_schedule_map @data(0.1, 0.7, 1.5) def test_rzx_calibration(self, angle): @@ -112,7 +114,7 @@ def test_transpile_layout(self, opt_level): qc.h(0) qc.cx(0, 1) qc.measure_all() - backend = GenericBackendV2(num_qubits=127) + backend = GenericBackendV2(num_qubits=127, seed=42) tqc = transpile(qc, backend, optimization_level=opt_level) self.assert_roundtrip_equal(tqc) @@ -126,7 +128,7 @@ def test_transpile_with_routing(self, opt_level): qc.cx(0, 3) qc.cx(0, 4) qc.measure_all() - backend = GenericBackendV2(num_qubits=127) + backend = GenericBackendV2(num_qubits=127, seed=42) tqc = transpile(qc, backend, optimization_level=opt_level) self.assert_roundtrip_equal(tqc) @@ -137,7 +139,7 @@ def test_transpile_layout_explicit_None_final_layout(self, opt_level): qc.h(0) qc.cx(0, 1) qc.measure_all() - backend = GenericBackendV2(num_qubits=127) + backend = GenericBackendV2(num_qubits=127, seed=42) tqc = transpile(qc, backend, optimization_level=opt_level) tqc.layout.final_layout = None self.assert_roundtrip_equal(tqc) @@ -201,7 +203,7 @@ def test_custom_register_name(self, opt_level): qc.cx(0, 3) qc.cx(0, 4) qc.measure_all() - backend = GenericBackendV2(num_qubits=127) + backend = GenericBackendV2(num_qubits=127, seed=42) tqc = transpile(qc, backend, optimization_level=opt_level) self.assert_roundtrip_equal(tqc) @@ -213,7 +215,7 @@ def test_no_register(self, opt_level): qc.h(0) qc.cx(0, 1) qc.measure_all() - backend = GenericBackendV2(num_qubits=127) + backend = GenericBackendV2(num_qubits=127, seed=42) tqc = transpile(qc, backend, optimization_level=opt_level) # Manually validate to deal with qubit equality needing exact objects qpy_file = io.BytesIO() diff --git a/test/python/quantum_info/operators/symplectic/test_pauli.py b/test/python/quantum_info/operators/symplectic/test_pauli.py index 35acd46a4d02..91568ac32a4b 100644 --- a/test/python/quantum_info/operators/symplectic/test_pauli.py +++ b/test/python/quantum_info/operators/symplectic/test_pauli.py @@ -545,12 +545,14 @@ def test_permute_pauli_estimator_example(self): op = Pauli("XXXI") backend = GenericBackendV2(num_qubits=7, seed=0) backend.set_options(seed_simulator=123) - estimator = BackendEstimator(backend=backend, skip_transpilation=True) + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend=backend, skip_transpilation=True) thetas = list(range(len(psi.parameters))) transpiled_psi = transpile(psi, backend, optimization_level=3) permuted_op = op.apply_layout(transpiled_psi.layout) - job = estimator.run(transpiled_psi, permuted_op, thetas) - res = job.result().values + with self.assertWarns(DeprecationWarning): + job = estimator.run(transpiled_psi, permuted_op, thetas) + res = job.result().values if optionals.HAS_AER: np.testing.assert_allclose(res, [0.20898438], rtol=0.5, atol=0.2) else: 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 c4f09ec2d795..4964b0c6a609 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 @@ -1118,12 +1118,14 @@ def test_permute_sparse_pauli_op_estimator_example(self): op = SparsePauliOp.from_list([("IIII", 1), ("IZZZ", 2), ("XXXI", 3)]) backend = GenericBackendV2(num_qubits=7, seed=0) backend.set_options(seed_simulator=123) - estimator = BackendEstimator(backend=backend, skip_transpilation=True) + with self.assertWarns(DeprecationWarning): + estimator = BackendEstimator(backend=backend, skip_transpilation=True) thetas = list(range(len(psi.parameters))) transpiled_psi = transpile(psi, backend, optimization_level=3) permuted_op = op.apply_layout(transpiled_psi.layout) - job = estimator.run(transpiled_psi, permuted_op, thetas) - res = job.result().values + with self.assertWarns(DeprecationWarning): + job = estimator.run(transpiled_psi, permuted_op, thetas) + res = job.result().values if optionals.HAS_AER: np.testing.assert_allclose(res, [1.419922], rtol=0.5, atol=0.2) else: diff --git a/test/python/result/test_mitigators.py b/test/python/result/test_mitigators.py index 3b3e83bce009..8dcdedc433fe 100644 --- a/test/python/result/test_mitigators.py +++ b/test/python/result/test_mitigators.py @@ -119,8 +119,11 @@ def counts_data(circuit, assignment_matrices, shots=1024): def test_mitigation_improvement(self): """Test whether readout mitigation led to more accurate results""" shots = 1024 - assignment_matrices = self.assignment_matrices() - num_qubits = len(assignment_matrices) + with self.assertWarns(DeprecationWarning): + # TODO self.assignment_matrices calls LocalReadoutMitigator, + # which only supports BackendV1 at the moment: + # https://github.com/Qiskit/qiskit/issues/12832 + assignment_matrices = self.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( @@ -156,7 +159,8 @@ def test_expectation_improvement(self): """Test whether readout mitigation led to more accurate results and that its standard deviation is increased""" shots = 1024 - assignment_matrices = self.assignment_matrices() + with self.assertWarns(DeprecationWarning): + assignment_matrices = self.assignment_matrices() mitigators = self.mitigators(assignment_matrices) num_qubits = len(assignment_matrices) diagonals = [] @@ -198,7 +202,8 @@ def test_expectation_improvement(self): def test_clbits_parameter(self): """Test whether the clbits parameter is handled correctly""" shots = 10000 - assignment_matrices = self.assignment_matrices() + with self.assertWarns(DeprecationWarning): + assignment_matrices = self.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) @@ -233,7 +238,8 @@ def test_clbits_parameter(self): def test_qubits_parameter(self): """Test whether the qubits parameter is handled correctly""" shots = 10000 - assignment_matrices = self.assignment_matrices() + with self.assertWarns(DeprecationWarning): + assignment_matrices = self.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) @@ -281,7 +287,8 @@ def test_qubits_parameter(self): def test_repeated_qubits_parameter(self): """Tests the order of mitigated qubits.""" shots = 10000 - assignment_matrices = self.assignment_matrices() + with self.assertWarns(DeprecationWarning): + assignment_matrices = self.assignment_matrices() 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) @@ -319,7 +326,8 @@ def test_qubits_subset_parameter(self): """Tests mitigation on a subset of the initial set of qubits.""" shots = 10000 - assignment_matrices = self.assignment_matrices() + with self.assertWarns(DeprecationWarning): + assignment_matrices = self.assignment_matrices() 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) @@ -364,7 +372,8 @@ def test_qubits_subset_parameter(self): def test_from_backend(self): """Test whether a local mitigator can be created directly from backend properties""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() num_qubits = len(backend.properties().qubits) probs = TestReadoutMitigation.rng.random((num_qubits, 2)) for qubit_idx, qubit_prop in enumerate(backend.properties().qubits): @@ -422,7 +431,9 @@ def test_error_handling(self): def test_expectation_value_endian(self): """Test that endian for expval is little.""" - mitigators = self.mitigators(self.assignment_matrices()) + with self.assertWarns(DeprecationWarning): + assignment_matrices = self.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]) @@ -449,7 +460,9 @@ class TestLocalReadoutMitigation(QiskitTestCase): def test_assignment_matrix(self): """Tests that the local mitigator generates the full assignment matrix correctly""" qubits = [7, 2, 3] - assignment_matrices = LocalReadoutMitigator(backend=Fake5QV1())._assignment_mats[0:3] + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() + 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] ) diff --git a/test/python/result/test_result.py b/test/python/result/test_result.py index ff1f4cbf29a0..89539487158c 100644 --- a/test/python/result/test_result.py +++ b/test/python/result/test_result.py @@ -42,7 +42,8 @@ def generate_qiskit_result(self): memory = [hex(ii) for ii in range(8)] counts = {m: 1 for m in memory} data_1 = models.ExperimentResultData(counts=counts, memory=memory) - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result_1 = models.ExperimentResult( shots=8, success=True, data=data_1, header=exp_result_header_1 ) @@ -67,9 +68,10 @@ def test_counts_header(self): raw_counts = {"0x0": 4, "0x2": 10} processed_counts = {"0 0 00": 4, "0 0 10": 10} data = models.ExperimentResultData(counts=raw_counts) - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 - ) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader( + creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 + ) exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data, header=exp_result_header ) @@ -82,9 +84,10 @@ def test_counts_by_name(self): raw_counts = {"0x0": 4, "0x2": 10} processed_counts = {"0 0 00": 4, "0 0 10": 10} data = models.ExperimentResultData(counts=raw_counts) - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4, name="a_name" - ) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader( + creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4, name="a_name" + ) exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data, header=exp_result_header ) @@ -95,7 +98,8 @@ def test_counts_by_name(self): def test_counts_duplicate_name(self): """Test results containing multiple entries of a single name will warn.""" data = models.ExperimentResultData(counts={}) - exp_result_header = QobjExperimentHeader(name="foo") + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader(name="foo") exp_result = models.ExperimentResult( shots=14, success=True, data=data, header=exp_result_header ) @@ -108,9 +112,10 @@ def test_result_repr(self): """Test that repr is constructed correctly for a results object.""" raw_counts = {"0x0": 4, "0x2": 10} data = models.ExperimentResultData(counts=raw_counts) - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 - ) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader( + creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 + ) exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data, header=exp_result_header ) @@ -137,7 +142,8 @@ def test_multiple_circuits_counts(self): raw_counts_1 = {"0x0": 5, "0x3": 12, "0x5": 9, "0xD": 6, "0xE": 2} processed_counts_1 = {"0000": 5, "0011": 12, "0101": 9, "1101": 6, "1110": 2} data_1 = models.ExperimentResultData(counts=raw_counts_1) - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result_1 = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data_1, header=exp_result_header_1 ) @@ -145,7 +151,8 @@ def test_multiple_circuits_counts(self): raw_counts_2 = {"0x1": 0, "0x4": 3, "0x6": 6, "0xA": 1, "0xB": 2} processed_counts_2 = {"0001": 0, "0100": 3, "0110": 6, "1010": 1, "1011": 2} data_2 = models.ExperimentResultData(counts=raw_counts_2) - exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result_2 = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data_2, header=exp_result_header_2 ) @@ -153,7 +160,8 @@ def test_multiple_circuits_counts(self): raw_counts_3 = {"0xC": 27, "0xF": 20} processed_counts_3 = {"1100": 27, "1111": 20} data_3 = models.ExperimentResultData(counts=raw_counts_3) - exp_result_header_3 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header_3 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result_3 = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data_3, header=exp_result_header_3 ) @@ -172,7 +180,8 @@ def test_marginal_counts(self): """Test that counts are marginalized correctly.""" raw_counts = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data = models.ExperimentResultData(counts=raw_counts) - exp_result_header = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result = models.ExperimentResult( shots=54, success=True, data=data, header=exp_result_header ) @@ -186,7 +195,8 @@ def test_marginal_distribution(self): """Test that counts are marginalized correctly.""" raw_counts = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data = models.ExperimentResultData(counts=raw_counts) - exp_result_header = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result = models.ExperimentResult( shots=54, success=True, data=data, header=exp_result_header ) @@ -200,7 +210,10 @@ def test_marginal_distribution(self): self.assertEqual(marginal_distribution(result.get_counts(), [1, 0]), expected_reverse) # test with register spacing, bitstrings are in form of "00 00" for register split data = models.ExperimentResultData(counts=raw_counts) - exp_result_header = QobjExperimentHeader(creg_sizes=[["c0", 2], ["c1", 2]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader( + creg_sizes=[["c0", 2], ["c1", 2]], memory_slots=4 + ) exp_result = models.ExperimentResult( shots=54, success=True, data=data, header=exp_result_header ) @@ -214,14 +227,16 @@ def test_marginal_counts_result(self): """Test that a Result object containing counts marginalizes correctly.""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) raw_counts_2 = {"0x2": 5, "0x3": 8} data_2 = models.ExperimentResultData(counts=raw_counts_2) - exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) + with self.assertWarns(DeprecationWarning): + exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) exp_result_2 = models.ExperimentResult( shots=13, success=True, data=data_2, header=exp_result_header_2 ) @@ -240,14 +255,20 @@ def test_marginal_counts_result(self): "1110": 8, } - self.assertEqual(marginal_counts(result, [0, 1]).get_counts(0), expected_marginal_counts_1) - self.assertEqual(marginal_counts(result, [0]).get_counts(1), expected_marginal_counts_2) - self.assertEqual(marginal_counts(result, None).get_counts(0), expected_marginal_counts_none) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + marginal_counts(result, [0, 1]).get_counts(0), expected_marginal_counts_1 + ) + self.assertEqual(marginal_counts(result, [0]).get_counts(1), expected_marginal_counts_2) + self.assertEqual( + marginal_counts(result, None).get_counts(0), expected_marginal_counts_none + ) def test_marginal_counts_result_memory(self): """Test that a Result object containing memory marginalizes correctly.""" result = self.generate_qiskit_result() - marginal_result = marginal_counts(result, indices=[0]) + with self.assertWarns(DeprecationWarning): + marginal_result = marginal_counts(result, indices=[0]) marginal_memory = marginal_result.results[0].data.memory self.assertEqual(marginal_memory, [hex(ii % 2) for ii in range(8)]) @@ -255,7 +276,8 @@ def test_marginal_counts_result_memory_nonzero_indices(self): """Test that a Result object containing memory marginalizes correctly.""" result = self.generate_qiskit_result() index = 2 - marginal_result = marginal_counts(result, indices=[index]) + with self.assertWarns(DeprecationWarning): + marginal_result = marginal_counts(result, indices=[index]) marginal_memory = marginal_result.results[0].data.memory mask = 1 << index expected = [hex((ii & mask) >> index) for ii in range(8)] @@ -266,7 +288,8 @@ def test_marginal_counts_result_memory_indices_None(self): result = self.generate_qiskit_result() memory = "should not be touched" result.results[0].data.memory = memory - marginal_result = marginal_counts(result, indices=None) + with self.assertWarns(DeprecationWarning): + marginal_result = marginal_counts(result, indices=None) marginal_memory = marginal_result.results[0].data.memory self.assertEqual(marginal_memory, memory) @@ -297,17 +320,20 @@ def test_marginal_counts_result_marginalize_memory(self): self.assertTrue(hasattr(marginal_result.results[0].data, "memory")) result = self.generate_qiskit_result() - marginal_result = marginal_counts( - result, indices=[0], inplace=False, marginalize_memory=False - ) + with self.assertWarns(DeprecationWarning): + marginal_result = marginal_counts( + result, indices=[0], inplace=False, marginalize_memory=False + ) self.assertFalse(hasattr(marginal_result.results[0].data, "memory")) - marginal_result = marginal_counts( - result, indices=[0], inplace=False, marginalize_memory=None - ) + with self.assertWarns(DeprecationWarning): + marginal_result = marginal_counts( + result, indices=[0], inplace=False, marginalize_memory=None + ) self.assertTrue(hasattr(marginal_result.results[0].data, "memory")) - marginal_result = marginal_counts( - result, indices=[0], inplace=False, marginalize_memory=True - ) + with self.assertWarns(DeprecationWarning): + marginal_result = marginal_counts( + result, indices=[0], inplace=False, marginalize_memory=True + ) self.assertTrue(hasattr(marginal_result.results[0].data, "memory")) def test_marginal_counts_result_inplace(self): @@ -323,7 +349,10 @@ def test_marginal_counts_result_creg_sizes(self): """Test that marginal_counts with Result input properly changes creg_sizes.""" raw_counts = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data = models.ExperimentResultData(counts=raw_counts) - exp_result_header = QobjExperimentHeader(creg_sizes=[["c0", 1], ["c1", 3]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader( + creg_sizes=[["c0", 1], ["c1", 3]], memory_slots=4 + ) exp_result = models.ExperimentResult( shots=54, success=True, data=data, header=exp_result_header ) @@ -333,7 +362,8 @@ def test_marginal_counts_result_creg_sizes(self): expected_marginal_counts = {"0 0": 14, "0 1": 18, "1 0": 13, "1 1": 9} expected_creg_sizes = [["c0", 1], ["c1", 1]] expected_memory_slots = 2 - marginal_counts_result = marginal_counts(result, [0, 2]) + with self.assertWarns(DeprecationWarning): + marginal_counts_result = marginal_counts(result, [0, 2]) self.assertEqual(marginal_counts_result.results[0].header.creg_sizes, expected_creg_sizes) self.assertEqual( marginal_counts_result.results[0].header.memory_slots, expected_memory_slots @@ -344,9 +374,10 @@ def test_marginal_counts_result_format(self): """Test that marginal_counts with format_marginal true properly formats output.""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0x12": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - exp_result_header_1 = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c1", 3]], memory_slots=5 - ) + with self.assertWarns(DeprecationWarning): + exp_result_header_1 = QobjExperimentHeader( + creg_sizes=[["c0", 2], ["c1", 3]], memory_slots=5 + ) exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) @@ -369,14 +400,16 @@ def test_marginal_counts_inplace_true(self): """Test marginal_counts(Result, inplace = True)""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) raw_counts_2 = {"0x2": 5, "0x3": 8} data_2 = models.ExperimentResultData(counts=raw_counts_2) - exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) + with self.assertWarns(DeprecationWarning): + exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) exp_result_2 = models.ExperimentResult( shots=13, success=True, data=data_2, header=exp_result_header_2 ) @@ -394,14 +427,16 @@ def test_marginal_counts_inplace_false(self): """Test marginal_counts(Result, inplace=False)""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0xE": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) + with self.assertWarns(DeprecationWarning): + exp_result_header_1 = QobjExperimentHeader(creg_sizes=[["c0", 4]], memory_slots=4) exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) raw_counts_2 = {"0x2": 5, "0x3": 8} data_2 = models.ExperimentResultData(counts=raw_counts_2) - exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) + with self.assertWarns(DeprecationWarning): + exp_result_header_2 = QobjExperimentHeader(creg_sizes=[["c0", 2]], memory_slots=2) exp_result_2 = models.ExperimentResult( shots=13, success=True, data=data_2, header=exp_result_header_2 ) @@ -410,9 +445,10 @@ def test_marginal_counts_inplace_false(self): expected_marginal_counts = {"0": 27, "1": 27} - self.assertEqual( - marginal_counts(result, [0], inplace=False).get_counts(0), expected_marginal_counts - ) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + marginal_counts(result, [0], inplace=False).get_counts(0), expected_marginal_counts + ) self.assertNotEqual(result.get_counts(0), expected_marginal_counts) def test_marginal_counts_with_dict(self): @@ -465,9 +501,10 @@ def test_memory_counts_header(self): "0 0 10", ] data = models.ExperimentResultData(memory=raw_memory) - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 - ) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader( + creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4 + ) exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, memory=True, data=data, header=exp_result_header ) @@ -703,9 +740,10 @@ def test_counts_name_out(self): """Test that fails when get_count is called with a nonexistent name.""" raw_counts = {"0x0": 4, "0x2": 10} data = models.ExperimentResultData(counts=raw_counts) - exp_result_header = QobjExperimentHeader( - creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4, name="a_name" - ) + with self.assertWarns(DeprecationWarning): + exp_result_header = QobjExperimentHeader( + creg_sizes=[["c0", 2], ["c0", 1], ["c1", 1]], memory_slots=4, name="a_name" + ) exp_result = models.ExperimentResult( shots=14, success=True, meas_level=2, data=data, header=exp_result_header ) @@ -736,13 +774,15 @@ def test_marginal_counts_no_cregs(self): """Test that marginal_counts without cregs See qiskit-terra/6430.""" raw_counts_1 = {"0x0": 4, "0x1": 7, "0x2": 10, "0x6": 5, "0x9": 11, "0xD": 9, "0x12": 8} data_1 = models.ExperimentResultData(counts=raw_counts_1) - exp_result_header_1 = QobjExperimentHeader(memory_slots=5) + with self.assertWarns(DeprecationWarning): + exp_result_header_1 = QobjExperimentHeader(memory_slots=5) exp_result_1 = models.ExperimentResult( shots=54, success=True, data=data_1, header=exp_result_header_1 ) result = Result(results=[exp_result_1], **self.base_result_args) - _ = marginal_counts(result, indices=[0]) - marginal_counts_result = marginal_counts(result, indices=[0]) + with self.assertWarns(DeprecationWarning): + _ = marginal_counts(result, indices=[0]) + marginal_counts_result = marginal_counts(result, indices=[0]) self.assertEqual(marginal_counts_result.get_counts(), {"0": 27, "1": 27}) diff --git a/test/python/scheduler/test_basic_scheduler.py b/test/python/scheduler/test_basic_scheduler.py index 16ec00927c67..947a255ddf21 100644 --- a/test/python/scheduler/test_basic_scheduler.py +++ b/test/python/scheduler/test_basic_scheduler.py @@ -46,14 +46,16 @@ class TestBasicSchedule(QiskitTestCase): def setUp(self): super().setUp() - self.backend = FakeOpenPulse2Q() + with self.assertWarns(DeprecationWarning): + self.backend = FakeOpenPulse2Q() self.inst_map = self.backend.defaults().instruction_schedule_map def test_unavailable_defaults(self): """Test backend with unavailable defaults.""" qr = QuantumRegister(1) qc = QuantumCircuit(qr) - backend = FakeBackend(None) + with self.assertWarns(DeprecationWarning): + backend = FakeBackend(None) backend.defaults = backend.configuration self.assertRaises(QiskitError, lambda: schedule(qc, backend)) @@ -252,7 +254,8 @@ def test_3q_schedule(self): # ┌──────┴───┴──────┐ └───────────────┘ ┌─┴─┐┌─────────────────┐ # q0_2: ┤ U2(0.778,0.122) ├───────────────────┤ X ├┤ U2(0.778,0.122) ├ # └─────────────────┘ └───┘└─────────────────┘ - backend = FakeOpenPulse3Q() + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse3Q() inst_map = backend.defaults().instruction_schedule_map q = QuantumRegister(3) c = ClassicalRegister(3) @@ -416,9 +419,13 @@ def test_subset_calibrated_measurements(self): meas_scheds.append(meas) qc.add_calibration("measure", [qubit], meas) - meas = macros.measure([1], FakeOpenPulse3Q()) + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse3Q() + meas = macros.measure([1], backend) meas = meas.exclude(channels=[AcquireChannel(0), AcquireChannel(2)]) - sched = schedule(qc, FakeOpenPulse3Q()) + with self.assertWarns(DeprecationWarning): + backend = FakeOpenPulse3Q() + sched = schedule(qc, backend) expected = Schedule(meas_scheds[0], meas_scheds[1], meas) self.assertEqual(sched.instructions, expected.instructions) @@ -509,7 +516,7 @@ class TestBasicScheduleV2(QiskitTestCase): def setUp(self): super().setUp() - self.backend = GenericBackendV2(num_qubits=3, calibrate_instructions=True) + self.backend = GenericBackendV2(num_qubits=3, calibrate_instructions=True, seed=42) self.inst_map = self.backend.instruction_schedule_map # self.pulse_2_samples is the pulse sequence used to calibrate "measure" in # GenericBackendV2. See class construction for more details. diff --git a/test/python/synthesis/test_synthesis.py b/test/python/synthesis/test_synthesis.py index 025b9accf227..05739db249b7 100644 --- a/test/python/synthesis/test_synthesis.py +++ b/test/python/synthesis/test_synthesis.py @@ -16,7 +16,6 @@ import unittest import contextlib import logging -import math import numpy as np import scipy import scipy.stats @@ -62,8 +61,8 @@ TwoQubitControlledUDecomposer, Ud, decompose_two_qubit_product_gate, - TwoQubitDecomposeUpToDiagonal, ) +from qiskit._accelerate.two_qubit_decompose import two_qubit_decompose_up_to_diagonal from qiskit._accelerate.two_qubit_decompose import Specialization from qiskit.synthesis.unitary import qsd from test import combine # pylint: disable=wrong-import-order @@ -1673,149 +1672,23 @@ def test_a2_opt_single_2q(self): class TestTwoQubitDecomposeUpToDiagonal(QiskitTestCase): """test TwoQubitDecomposeUpToDiagonal class""" - def test_prop31(self): - """test proposition III.1: no CNOTs needed""" - dec = TwoQubitDecomposeUpToDiagonal() - # test identity - mat = np.identity(4) - self.assertTrue(dec._cx0_test(mat)) - - sz = np.array([[1, 0], [0, -1]]) - zz = np.kron(sz, sz) - self.assertTrue(dec._cx0_test(zz)) - - had = np.matrix([[1, 1], [1, -1]]) / np.sqrt(2) - hh = np.kron(had, had) - self.assertTrue(dec._cx0_test(hh)) - - sy = np.array([[0, -1j], [1j, 0]]) - hy = np.kron(had, sy) - self.assertTrue(dec._cx0_test(hy)) - - qc = QuantumCircuit(2) - qc.cx(0, 1) - self.assertFalse(dec._cx0_test(Operator(qc).data)) - - def test_prop32_true(self): - """test proposition III.2: 1 CNOT sufficient""" - dec = TwoQubitDecomposeUpToDiagonal() - qc = QuantumCircuit(2) - qc.ry(np.pi / 4, 0) - qc.ry(np.pi / 3, 1) - qc.cx(0, 1) - qc.ry(np.pi / 4, 0) - qc.y(1) - mat = Operator(qc).data - self.assertTrue(dec._cx1_test(mat)) - - qc = QuantumCircuit(2) - qc.ry(np.pi / 5, 0) - qc.ry(np.pi / 3, 1) - qc.cx(1, 0) - qc.ry(np.pi / 2, 0) - qc.y(1) - mat = Operator(qc).data - self.assertTrue(dec._cx1_test(mat)) - - # this SU4 is non-local - mat = scipy.stats.unitary_group.rvs(4, random_state=84) - self.assertFalse(dec._cx1_test(mat)) - - def test_prop32_false(self): - """test proposition III.2: 1 CNOT not sufficient""" - dec = TwoQubitDecomposeUpToDiagonal() - qc = QuantumCircuit(2) - qc.ry(np.pi / 4, 0) - qc.ry(np.pi / 3, 1) - qc.cx(0, 1) - qc.ry(np.pi / 4, 0) - qc.y(1) - qc.cx(0, 1) - qc.ry(np.pi / 3, 0) - qc.rx(np.pi / 2, 1) - mat = Operator(qc).data - self.assertFalse(dec._cx1_test(mat)) - - def test_prop33_true(self): - """test proposition III.3: 2 CNOT sufficient""" - dec = TwoQubitDecomposeUpToDiagonal() - qc = QuantumCircuit(2) - qc.rx(np.pi / 4, 0) - qc.ry(np.pi / 2, 1) - qc.cx(0, 1) - qc.rx(np.pi / 4, 0) - qc.ry(np.pi / 2, 1) - qc.cx(0, 1) - qc.rx(np.pi / 4, 0) - qc.y(1) - mat = Operator(qc).data - self.assertTrue(dec._cx2_test(mat)) - - def test_prop33_false(self): - """test whether circuit which requires 3 cx fails 2 cx test""" - dec = TwoQubitDecomposeUpToDiagonal() - qc = QuantumCircuit(2) - qc.u(0.1, 0.2, 0.3, 0) - qc.u(0.4, 0.5, 0.6, 1) - qc.cx(0, 1) - qc.u(0.1, 0.2, 0.3, 0) - qc.u(0.4, 0.5, 0.6, 1) - qc.cx(0, 1) - qc.u(0.5, 0.2, 0.3, 0) - qc.u(0.2, 0.4, 0.1, 1) - qc.cx(1, 0) - qc.u(0.1, 0.2, 0.3, 0) - qc.u(0.4, 0.5, 0.6, 1) - mat = Operator(qc).data - self.assertFalse(dec._cx2_test(mat)) - - def test_ortho_local_map(self): - """test map of SO4 to SU2⊗SU2""" - dec = TwoQubitDecomposeUpToDiagonal() - emap = np.array([[1, 1j, 0, 0], [0, 0, 1j, 1], [0, 0, 1j, -1], [1, -1j, 0, 0]]) / math.sqrt( - 2 - ) - so4 = scipy.stats.ortho_group.rvs(4, random_state=284) - sy = np.array([[0, -1j], [1j, 0]]) - self.assertTrue(np.allclose(-np.kron(sy, sy), emap @ emap.T)) - self.assertFalse(dec._cx0_test(so4)) - self.assertTrue(dec._cx0_test(emap @ so4 @ emap.T.conj())) - - def test_ortho_local_map2(self): - """test map of SO4 to SU2⊗SU2""" - dec = TwoQubitDecomposeUpToDiagonal() - emap = np.array([[1, 0, 0, 1j], [0, 1j, 1, 0], [0, 1j, -1, 0], [1, 0, 0, -1j]]) / math.sqrt( - 2 - ) - so4 = scipy.stats.ortho_group.rvs(4, random_state=284) - sy = np.array([[0, -1j], [1j, 0]]) - self.assertTrue(np.allclose(-np.kron(sy, sy), emap @ emap.T)) - self.assertFalse(dec._cx0_test(so4)) - self.assertTrue(dec._cx0_test(emap @ so4 @ emap.T.conj())) - - def test_real_trace_transform(self): - """test finding diagonal factor of unitary""" - dec = TwoQubitDecomposeUpToDiagonal() - u4 = scipy.stats.unitary_group.rvs(4, random_state=83) - su4, _ = dec._u4_to_su4(u4) - real_map = dec._real_trace_transform(su4) - self.assertTrue(dec._cx2_test(real_map @ su4)) - def test_call_decompose(self): """ test __call__ method to decompose """ - dec = TwoQubitDecomposeUpToDiagonal() + dec = two_qubit_decompose_up_to_diagonal u4 = scipy.stats.unitary_group.rvs(4, random_state=47) - dmat, circ2cx = dec(u4) + dmat, circ2cx_data = dec(u4) + circ2cx = QuantumCircuit._from_circuit_data(circ2cx_data) dec_diag = dmat @ Operator(circ2cx).data self.assertTrue(Operator(u4) == Operator(dec_diag)) def test_circuit_decompose(self): """test applying decomposed gates as circuit elements""" - dec = TwoQubitDecomposeUpToDiagonal() + dec = two_qubit_decompose_up_to_diagonal u4 = scipy.stats.unitary_group.rvs(4, random_state=47) - dmat, circ2cx = dec(u4) + dmat, circ2cx_data = dec(u4) + circ2cx = QuantumCircuit._from_circuit_data(circ2cx_data) qc1 = QuantumCircuit(2) qc1.append(UnitaryGate(u4), range(2)) diff --git a/test/python/transpiler/test_1q.py b/test/python/transpiler/test_1q.py index 50bdc7b24643..13379f410c34 100644 --- a/test/python/transpiler/test_1q.py +++ b/test/python/transpiler/test_1q.py @@ -16,12 +16,16 @@ from qiskit import QuantumCircuit from qiskit.compiler import transpile -from qiskit.providers.fake_provider import Fake1Q +from qiskit.providers.fake_provider import Fake1Q, GenericBackendV2 from qiskit.providers.basic_provider import BasicSimulator from qiskit.transpiler import TranspilerError from test import combine # pylint: disable=wrong-import-order from test import QiskitTestCase # pylint: disable=wrong-import-order +Fake1QV2 = GenericBackendV2( + num_qubits=1, basis_gates=["u1", "u2", "u3"], coupling_map=None, dtm=1.3333, seed=42 +) + def emptycircuit(): """Empty circuit""" @@ -45,31 +49,54 @@ class Test1QFailing(QiskitTestCase): circuit=[circuit_3516], level=[0, 1, 2, 3], dsc="Transpiling {circuit.__name__} at level {level} should fail", - name="{circuit.__name__}_level{level}_fail", + name="{circuit.__name__}_level{level}_fail_v1", + ) + def test(self, circuit, level): + """All the levels with all the 1Q backendV1""" + with self.assertRaises(TranspilerError): + with self.assertWarns(DeprecationWarning): + transpile(circuit(), backend=Fake1Q(), optimization_level=level, seed_transpiler=42) + + +@ddt +class Test1QV2Failing(QiskitTestCase): + """1QV2 tests that should fail.""" + + @combine( + circuit=[circuit_3516], + level=[0, 1, 2, 3], + dsc="Transpiling {circuit.__name__} at level {level} should fail", + name="{circuit.__name__}_level{level}_fail_v2", ) def test(self, circuit, level): - """All the levels with all the 1Q backend""" + """All the levels with all the 1Q backendV2""" with self.assertRaises(TranspilerError): - transpile(circuit(), backend=Fake1Q(), optimization_level=level, seed_transpiler=42) + transpile(circuit(), backend=Fake1QV2, optimization_level=level, seed_transpiler=42) @ddt class Test1QWorking(QiskitTestCase): - """1Q tests that should work.""" + """1QV1 tests that should work.""" @combine( circuit=[emptycircuit], level=[0, 1, 2, 3], dsc="Transpiling {circuit.__name__} at level {level} should work", - name="{circuit.__name__}_level{level}_valid", + name="{circuit.__name__}_level{level}_valid_v1", ) def test_device(self, circuit, level): - """All the levels with all the 1Q backend""" - result = transpile( - circuit(), backend=Fake1Q(), optimization_level=level, seed_transpiler=42 - ) + """All the levels with all the 1Q backendV1""" + with self.assertWarns(DeprecationWarning): + result = transpile( + circuit(), backend=Fake1Q(), optimization_level=level, seed_transpiler=42 + ) self.assertIsInstance(result, QuantumCircuit) + +@ddt +class TestBasicSimulatorWorking(QiskitTestCase): + """All the levels with a simulator backend""" + @combine( circuit=[circuit_3516], level=[0, 1, 2, 3], @@ -81,3 +108,21 @@ def test_simulator(self, circuit, level): backend = BasicSimulator() result = transpile(circuit(), backend=backend, optimization_level=level, seed_transpiler=42) self.assertIsInstance(result, QuantumCircuit) + + +@ddt +class Test1QV2Working(QiskitTestCase): + """1QV2 tests that should work.""" + + @combine( + circuit=[emptycircuit], + level=[0, 1, 2, 3], + dsc="Transpiling {circuit.__name__} at level {level} should work", + name="{circuit.__name__}_level{level}_valid_v2", + ) + def test_device(self, circuit, level): + """All the levels with all the 1Q backendV2""" + result = transpile( + circuit(), backend=Fake1QV2, optimization_level=level, seed_transpiler=42 + ) + self.assertIsInstance(result, QuantumCircuit) diff --git a/test/python/transpiler/test_basis_translator.py b/test/python/transpiler/test_basis_translator.py index fc933cd8f666..9dae6c3f283a 100644 --- a/test/python/transpiler/test_basis_translator.py +++ b/test/python/transpiler/test_basis_translator.py @@ -16,6 +16,7 @@ import os from numpy import pi +import scipy from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit import transpile @@ -33,13 +34,17 @@ XGate, SXGate, CXGate, + RXGate, + RZZGate, ) from qiskit.converters import circuit_to_dag, dag_to_circuit, circuit_to_instruction from qiskit.exceptions import QiskitError +from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.quantum_info import Operator from qiskit.transpiler.target import Target, InstructionProperties from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit.circuit.library.standard_gates.equivalence_library import ( StandardEquivalenceLibrary as std_eqlib, ) @@ -1101,6 +1106,7 @@ def test_skip_target_basis_equivalences_1(self): circ, basis_gates=["id", "rz", "sx", "x", "cx"], seed_transpiler=42, + optimization_level=1, ) self.assertEqual(circ_transpiled.count_ops(), {"cx": 91, "rz": 66, "sx": 22}) @@ -1223,3 +1229,48 @@ def __init__(self): out = BasisTranslator(eq_lib, {"my_h", "my_cx"}, target)(qc) self.assertEqual(out, expected) + + def test_fractional_gate_in_basis_from_string(self): + """Test transpiling with RZZ in basis with only basis_gates option.""" + num_qubits = 2 + seed = 9169 + basis_gates = ["rz", "rx", "rzz"] + qc = QuantumCircuit(num_qubits) + mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed) + qc.unitary(mat, range(num_qubits)) + pm = generate_preset_pass_manager( + optimization_level=1, basis_gates=basis_gates, seed_transpiler=134 + ) + cqc = pm.run(qc) + self.assertEqual(Operator(qc), Operator(cqc)) + + def test_fractional_gate_in_basis_from_backendv2(self): + """Test transpiling with RZZ in basis of backendv2.""" + num_qubits = 2 + seed = 9169 + basis_gates = ["rz", "rx", "rzz"] + qc = QuantumCircuit(num_qubits) + mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed) + qc.unitary(mat, range(num_qubits)) + backend = GenericBackendV2(num_qubits, basis_gates=basis_gates) + target = backend.target + pm = generate_preset_pass_manager(optimization_level=1, target=target, seed_transpiler=134) + cqc = pm.run(qc) + self.assertEqual(Operator(qc), Operator.from_circuit(cqc)) + + def test_fractional_gate_in_basis_from_custom_target(self): + """Test transpiling with RZZ in basis of custom target.""" + num_qubits = 2 + seed = 9169 + qc = QuantumCircuit(num_qubits) + mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed) + qc.unitary(mat, range(num_qubits)) + target = Target() + target.add_instruction(RZGate(self.theta), {(i,): None for i in range(qc.num_qubits)}) + target.add_instruction(RXGate(self.phi), {(i,): None for i in range(qc.num_qubits)}) + target.add_instruction( + RZZGate(self.lam), {(i, i + 1): None for i in range(qc.num_qubits - 1)} + ) + pm = generate_preset_pass_manager(optimization_level=1, target=target, seed_transpiler=134) + cqc = pm.run(qc) + self.assertEqual(Operator(qc), Operator.from_circuit(cqc)) diff --git a/test/python/transpiler/test_calibrationbuilder.py b/test/python/transpiler/test_calibrationbuilder.py index bc64d954a429..cb7293049ef5 100644 --- a/test/python/transpiler/test_calibrationbuilder.py +++ b/test/python/transpiler/test_calibrationbuilder.py @@ -266,7 +266,8 @@ def build_reverse( @data(-np.pi / 4, 0.1, np.pi / 4, np.pi / 2, np.pi) def test_rzx_calibration_cr_pulse_stretch(self, theta: float): """Test that cross resonance pulse durations are computed correctly.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() inst_map = backend.defaults().instruction_schedule_map cr_schedule = inst_map.get("cx", (0, 1)) with builder.build() as test_sched: @@ -279,7 +280,8 @@ def test_rzx_calibration_cr_pulse_stretch(self, theta: float): @data(-np.pi / 4, 0.1, np.pi / 4, np.pi / 2, np.pi) def test_rzx_calibration_rotary_pulse_stretch(self, theta: float): """Test that rotary pulse durations are computed correctly.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() inst_map = backend.defaults().instruction_schedule_map cr_schedule = inst_map.get("cx", (0, 1)) with builder.build() as test_sched: @@ -296,8 +298,8 @@ def test_raise(self): qc = circuit.QuantumCircuit(2) qc.rzx(theta, 0, 1) dag = circuit_to_dag(qc) - - backend = Fake7QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake7QPulseV1() # The error is raised when calibrations in multi-qubit # gates are not detected. # We force this by removing the 'cx' entries from the @@ -322,7 +324,8 @@ def test_ecr_cx_forward(self): qc = circuit.QuantumCircuit(2) qc.rzx(theta, 0, 1) - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() inst_map = backend.defaults().instruction_schedule_map _pass = RZXCalibrationBuilder(inst_map) test_qc = PassManager(_pass).run(qc) @@ -347,7 +350,8 @@ def test_ecr_cx_reverse(self): qc = circuit.QuantumCircuit(2) qc.rzx(theta, 1, 0) - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() inst_map = backend.defaults().instruction_schedule_map _pass = RZXCalibrationBuilder(inst_map) test_qc = PassManager(_pass).run(qc) @@ -434,7 +438,8 @@ def test_ecr_cx_forward(self): qc = circuit.QuantumCircuit(2) qc.rzx(theta, 0, 1) - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() inst_map = backend.defaults().instruction_schedule_map _pass = RZXCalibrationBuilderNoEcho(inst_map) diff --git a/test/python/transpiler/test_echo_rzx_weyl_decomposition.py b/test/python/transpiler/test_echo_rzx_weyl_decomposition.py index c4efffc977f4..8f876dd261d7 100644 --- a/test/python/transpiler/test_echo_rzx_weyl_decomposition.py +++ b/test/python/transpiler/test_echo_rzx_weyl_decomposition.py @@ -32,7 +32,12 @@ class TestEchoRZXWeylDecomposition(QiskitTestCase): def setUp(self): super().setUp() - self.backend = Fake27QPulseV1() + # TODO once https://github.com/Qiskit/qiskit/issues/12759 is fixed, replace with + # backend = GenericBackendV2(num_qubits=27, calibrate_instructions=True, + # control_flow=True, seed=42) + # self.inst_map = backend.instruction_schedule_map + with self.assertWarns(DeprecationWarning): + self.backend = Fake27QPulseV1() self.inst_map = self.backend.defaults().instruction_schedule_map def assertRZXgates(self, unitary_circuit, after): diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index fd6ae6a01cda..d1ea21cde544 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -2118,11 +2118,9 @@ def test_qft_plugins_qft(self, qft_plugin_name): qc.cx(1, 3) qc.append(QFTGate(3).inverse(), [0, 1, 2]) hls_config = HLSConfig(qft=[qft_plugin_name]) - basis_gates = ["cx", "u"] - qct = transpile(qc, hls_config=hls_config, basis_gates=basis_gates) + hls_pass = HighLevelSynthesis(hls_config=hls_config) + qct = hls_pass(qc) self.assertEqual(Operator(qc), Operator(qct)) - ops = set(qct.count_ops().keys()) - self.assertEqual(ops, {"u", "cx"}) @data("line", "full") def test_qft_line_plugin_annotated_qft(self, qft_plugin_name): @@ -2130,11 +2128,9 @@ def test_qft_line_plugin_annotated_qft(self, qft_plugin_name): qc = QuantumCircuit(4) qc.append(QFTGate(3).inverse(annotated=True).control(annotated=True), [0, 1, 2, 3]) hls_config = HLSConfig(qft=[qft_plugin_name]) - basis_gates = ["cx", "u"] - qct = transpile(qc, hls_config=hls_config, basis_gates=basis_gates) + hls_pass = HighLevelSynthesis(hls_config=hls_config) + qct = hls_pass(qc) self.assertEqual(Operator(qc), Operator(qct)) - ops = set(qct.count_ops().keys()) - self.assertEqual(ops, {"u", "cx"}) if __name__ == "__main__": diff --git a/test/python/transpiler/test_instruction_durations.py b/test/python/transpiler/test_instruction_durations.py index ba37f6cd65e4..de68fbadf86a 100644 --- a/test/python/transpiler/test_instruction_durations.py +++ b/test/python/transpiler/test_instruction_durations.py @@ -36,14 +36,18 @@ def test_fail_if_invalid_dict_is_supplied_when_construction(self): InstructionDurations(invalid_dic) def test_from_backend_for_backend_with_dt(self): - backend = Fake27QPulseV1() + # Remove context once https://github.com/Qiskit/qiskit/issues/12760 is fixed + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() gate = self._find_gate_with_length(backend) durations = InstructionDurations.from_backend(backend) self.assertGreater(durations.dt, 0) self.assertGreater(durations.get(gate, 0), 0) def test_from_backend_for_backend_without_dt(self): - backend = Fake27QPulseV1() + # Remove context once https://github.com/Qiskit/qiskit/issues/12760 is fixed + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() delattr(backend.configuration(), "dt") gate = self._find_gate_with_length(backend) durations = InstructionDurations.from_backend(backend) diff --git a/test/python/transpiler/test_passmanager_config.py b/test/python/transpiler/test_passmanager_config.py index 01ec7ebf133a..ebac6a410b7f 100644 --- a/test/python/transpiler/test_passmanager_config.py +++ b/test/python/transpiler/test_passmanager_config.py @@ -19,6 +19,7 @@ from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.passmanager_config import PassManagerConfig from test import QiskitTestCase # pylint: disable=wrong-import-order +from ..legacy_cmaps import ALMADEN_CMAP class TestPassManagerConfig(QiskitTestCase): @@ -30,7 +31,8 @@ def test_config_from_backend(self): `Fake27QPulseV1` is used in this testcase. This backend has `defaults` attribute that contains an instruction schedule map. """ - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() config = PassManagerConfig.from_backend(backend) self.assertEqual(config.basis_gates, backend.configuration().basis_gates) self.assertEqual(config.inst_map, backend.defaults().instruction_schedule_map) @@ -40,7 +42,7 @@ def test_config_from_backend(self): def test_config_from_backend_v2(self): """Test from_backend() with a BackendV2 instance.""" - backend = GenericBackendV2(num_qubits=27) + backend = GenericBackendV2(num_qubits=27, seed=42) config = PassManagerConfig.from_backend(backend) self.assertEqual(config.basis_gates, backend.operation_names) self.assertEqual(config.inst_map, backend.instruction_schedule_map) @@ -51,16 +53,19 @@ def test_invalid_backend(self): with self.assertRaises(AttributeError): PassManagerConfig.from_backend(Backend()) - def test_from_backend_and_user(self): + def test_from_backend_and_user_v1(self): """Test from_backend() with a backend and user options. `FakeMelbourne` is used in this testcase. This backend does not have `defaults` attribute and thus not provide an instruction schedule map. + + REMOVE AFTER Fake20QV1 GETS REMOVED. """ qr = QuantumRegister(4, "qr") initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]] - backend = Fake20QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() config = PassManagerConfig.from_backend( backend, basis_gates=["user_gate"], initial_layout=initial_layout ) @@ -72,9 +77,35 @@ def test_from_backend_and_user(self): ) self.assertEqual(config.initial_layout, initial_layout) + def test_from_backend_and_user(self): + """Test from_backend() with a backend and user options. + + `FakeMelbourne` is used in this testcase. This backend does not have + `defaults` attribute and thus not provide an instruction schedule map. + """ + qr = QuantumRegister(4, "qr") + initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]] + + backend = GenericBackendV2( + num_qubits=20, + coupling_map=ALMADEN_CMAP, + basis_gates=["id", "u1", "u2", "u3", "cx"], + calibrate_instructions=None, + seed=42, + ) + config = PassManagerConfig.from_backend( + backend, basis_gates=["user_gate"], initial_layout=initial_layout + ) + self.assertEqual(config.basis_gates, ["user_gate"]) + self.assertNotEqual(config.basis_gates, backend.operation_names) + self.assertEqual(config.inst_map.instructions, []) + self.assertEqual(str(config.coupling_map), str(CouplingMap(backend.coupling_map))) + self.assertEqual(config.initial_layout, initial_layout) + def test_from_backendv1_inst_map_is_none(self): """Test that from_backend() works with backend that has defaults defined as None.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.defaults = lambda: None config = PassManagerConfig.from_backend(backend) self.assertIsInstance(config, PassManagerConfig) @@ -82,8 +113,9 @@ def test_from_backendv1_inst_map_is_none(self): def test_invalid_user_option(self): """Test from_backend() with an invalid user option.""" + backend = GenericBackendV2(num_qubits=20, seed=42) with self.assertRaises(TypeError): - PassManagerConfig.from_backend(Fake20QV1(), invalid_option=None) + PassManagerConfig.from_backend(backend, invalid_option=None) def test_str(self): """Test string output.""" diff --git a/test/python/transpiler/test_passmanager_run.py b/test/python/transpiler/test_passmanager_run.py index caf6af2aaf67..3da6a042564c 100644 --- a/test/python/transpiler/test_passmanager_run.py +++ b/test/python/transpiler/test_passmanager_run.py @@ -15,9 +15,10 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.circuit.library import CXGate from qiskit.transpiler.preset_passmanagers import level_1_pass_manager -from qiskit.providers.fake_provider import Fake20QV1 +from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.transpiler import Layout, PassManager from qiskit.transpiler.passmanager_config import PassManagerConfig +from ..legacy_cmaps import ALMADEN_CMAP from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -83,12 +84,14 @@ def test_default_pass_manager_single(self): circuit.cx(qr[1], qr[2]) circuit.cx(qr[2], qr[3]) - coupling_map = Fake20QV1().configuration().coupling_map + backend = GenericBackendV2( + num_qubits=20, coupling_map=ALMADEN_CMAP, basis_gates=["id", "u1", "u2", "u3", "cx"] + ) initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]] pass_manager = level_1_pass_manager( PassManagerConfig.from_backend( - Fake20QV1(), + backend, initial_layout=Layout.from_qubit_list(initial_layout), seed_transpiler=42, ) @@ -100,7 +103,7 @@ def test_default_pass_manager_single(self): for instruction in new_circuit.data: if isinstance(instruction.operation, CXGate): - self.assertIn([bit_indices[x] for x in instruction.qubits], coupling_map) + self.assertIn([bit_indices[x] for x in instruction.qubits], ALMADEN_CMAP) def test_default_pass_manager_two(self): """Test default_pass_manager.run(circuitS). @@ -133,12 +136,63 @@ def test_default_pass_manager_two(self): circuit2.cx(qr[0], qr[1]) circuit2.cx(qr[2], qr[3]) - coupling_map = Fake20QV1().configuration().coupling_map + coupling_map = [ + [0, 1], + [1, 0], + [1, 2], + [1, 6], + [2, 1], + [2, 3], + [3, 2], + [3, 4], + [3, 8], + [4, 3], + [5, 6], + [5, 10], + [6, 1], + [6, 5], + [6, 7], + [7, 6], + [7, 8], + [7, 12], + [8, 3], + [8, 7], + [8, 9], + [9, 8], + [9, 14], + [10, 5], + [10, 11], + [11, 10], + [11, 12], + [11, 16], + [12, 7], + [12, 11], + [12, 13], + [13, 12], + [13, 14], + [13, 18], + [14, 9], + [14, 13], + [15, 16], + [16, 11], + [16, 15], + [16, 17], + [17, 16], + [17, 18], + [18, 13], + [18, 17], + [18, 19], + [19, 18], + ] initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]] + backend = GenericBackendV2( + num_qubits=20, coupling_map=coupling_map, basis_gates=["id", "u1", "u2", "u3", "cx"] + ) + pass_manager = level_1_pass_manager( PassManagerConfig.from_backend( - Fake20QV1(), + backend=backend, initial_layout=Layout.from_qubit_list(initial_layout), seed_transpiler=42, ) diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index ee85dc34ffd6..3d11a5d55e93 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -21,7 +21,7 @@ from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister from qiskit.circuit import Qubit, Gate, ControlFlowOp, ForLoopOp -from qiskit.compiler import transpile, assemble +from qiskit.compiler import transpile from qiskit.transpiler import CouplingMap, Layout, PassManager, TranspilerError, Target from qiskit.circuit.library import U2Gate, U3Gate, QuantumVolume, CXGate, CZGate, XGate from qiskit.transpiler.passes import ( @@ -222,7 +222,9 @@ def test_alignment_constraints_called_with_by_default(self, level): circuit.h(q[0]) circuit.cz(q[0], q[1]) with unittest.mock.patch("qiskit.transpiler.passes.TimeUnitConversion.run") as mock: - transpile(circuit, backend=Fake20QV1(), optimization_level=level) + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() + transpile(circuit, backend=backend, optimization_level=level) mock.assert_not_called() @combine(level=[0, 1, 2, 3], name="level{level}") @@ -236,12 +238,15 @@ def test_alignment_constraints_called_with_delay_in_circuit(self, level): with unittest.mock.patch( "qiskit.transpiler.passes.TimeUnitConversion.run", return_value=circuit_to_dag(circuit) ) as mock: - transpile(circuit, backend=Fake20QV1(), optimization_level=level) + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() + transpile(circuit, backend=backend, optimization_level=level) mock.assert_called_once() def test_unroll_only_if_not_gates_in_basis(self): """Test that the list of passes _unroll only runs if a gate is not in the basis.""" - qcomp = Fake5QV1() + with self.assertWarns(DeprecationWarning): + qcomp = Fake5QV1() qv_circuit = QuantumVolume(3) gates_in_basis_true_count = 0 collect_2q_blocks_count = 0 @@ -684,15 +689,15 @@ def test_layout_1711(self, level): } backend = GenericBackendV2(num_qubits=16, coupling_map=RUESCHLIKON_CMAP, seed=42) qc_b = transpile(qc, backend, initial_layout=initial_layout, optimization_level=level) - qobj = assemble(qc_b) self.assertEqual(qc_b._layout.initial_layout._p2v, final_layout) - compiled_ops = qobj.experiments[0].instructions - for operation in compiled_ops: - if operation.name == "cx": - self.assertIn(tuple(operation.qubits), backend.coupling_map) - self.assertIn(operation.qubits, [[15, 0], [15, 2]]) + for inst in qc_b.data: + if inst.operation.name == "cx": + self.assertIn( + tuple(qc_b.find_bit(bit).index for bit in inst.qubits), backend.coupling_map + ) + self.assertIn([qc_b.find_bit(bit).index for bit in inst.qubits], [[15, 0], [15, 2]]) @data(0, 1, 2, 3) def test_layout_2532(self, level): @@ -782,7 +787,8 @@ def test_layout_2503(self, level): 19: ancilla[16], } - backend = Fake20QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() qc_b = transpile(qc, backend, initial_layout=initial_layout, optimization_level=level) self.assertEqual(qc_b._layout.initial_layout._p2v, final_layout) @@ -1038,7 +1044,8 @@ def test_trivial_layout(self, level): expected_layouts = [trivial_layout, trivial_layout] - backend = Fake20QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() result = transpile(qc, backend, optimization_level=level, seed_transpiler=42) self.assertEqual(result._layout.initial_layout._p2v, expected_layouts[level]) @@ -1071,7 +1078,8 @@ def test_initial_layout(self, level): 18: qr[9], } - backend = Fake20QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() result = transpile( qc, backend, optimization_level=level, initial_layout=initial_layout, seed_transpiler=42 ) @@ -1151,7 +1159,8 @@ def test_optimization_condition(self, level): cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr) qc.cx(0, 1).c_if(cr, 1) - backend = Fake20QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() circ = transpile(qc, backend, optimization_level=level) self.assertIsInstance(circ, QuantumCircuit) @@ -1215,8 +1224,27 @@ 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.""" - target = Fake20QV1() - pm = generate_preset_pass_manager(optimization_level, target) + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() + pm = generate_preset_pass_manager(optimization_level, backend) + self.assertIsInstance(pm, PassManager) + + def test_default_optimization_level(self): + """Test a pass manager is constructed with no optimization level.""" + backend = GenericBackendV2(num_qubits=14, coupling_map=MELBOURNE_CMAP) + pm = generate_preset_pass_manager(backend=backend) + self.assertIsInstance(pm, PassManager) + + def test_default_optimization_level_backend_first_pos_arg(self): + """Test a pass manager is constructed with only a positional backend.""" + backend = GenericBackendV2(num_qubits=14, coupling_map=MELBOURNE_CMAP) + pm = generate_preset_pass_manager(backend) + self.assertIsInstance(pm, PassManager) + + def test_default_optimization_level_target_first_pos_arg(self): + """Test a pass manager is constructed with only a positional target.""" + backend = GenericBackendV2(num_qubits=14, coupling_map=MELBOURNE_CMAP) + pm = generate_preset_pass_manager(backend.target) self.assertIsInstance(pm, PassManager) @data(0, 1, 2, 3) @@ -1569,7 +1597,8 @@ def test_invalid_methods_raise_on_control_flow(self, optimization_level): def test_unsupported_basis_gates_raise(self, optimization_level): """Test that trying to transpile a control-flow circuit for a backend that doesn't support the necessary operations in its `basis_gates` will raise a sensible error.""" - backend = Fake20QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake20QV1() qc = QuantumCircuit(1, 1) with qc.for_loop((0,)): diff --git a/test/python/transpiler/test_pulse_gate_pass.py b/test/python/transpiler/test_pulse_gate_pass.py index a11d4c4a6b53..539674609c26 100644 --- a/test/python/transpiler/test_pulse_gate_pass.py +++ b/test/python/transpiler/test_pulse_gate_pass.py @@ -56,7 +56,8 @@ def setUp(self): def test_transpile_with_bare_backend(self): """Test transpile without custom calibrations.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() # Remove timing constraints to avoid triggering # scheduling passes. backend.configuration().timing_constraints = {} @@ -96,7 +97,8 @@ def test_transpile_with_backend_target(self): def test_transpile_with_custom_basis_gate(self): """Test transpile with custom calibrations.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.defaults().instruction_schedule_map.add("sx", (0,), self.custom_sx_q0) backend.defaults().instruction_schedule_map.add("sx", (1,), self.custom_sx_q1) # Remove timing constraints to avoid triggering @@ -122,10 +124,13 @@ def test_transpile_with_custom_basis_gate(self): def test_transpile_with_custom_basis_gate_in_target(self): """Test transpile with custom calibrations.""" + with self.assertWarns(DeprecationWarning): + backend_pulse = Fake27QPulseV1() target = GenericBackendV2( num_qubits=5, coupling_map=BOGOTA_CMAP, - calibrate_instructions=Fake27QPulseV1().defaults().instruction_schedule_map, + calibrate_instructions=backend_pulse.defaults().instruction_schedule_map, + seed=42, ).target target["sx"][(0,)].calibration = self.custom_sx_q0 @@ -150,12 +155,15 @@ def test_transpile_with_custom_basis_gate_in_target(self): def test_transpile_with_instmap(self): """Test providing instruction schedule map.""" - instmap = Fake27QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() + instmap = backend.defaults().instruction_schedule_map instmap.add("sx", (0,), self.custom_sx_q0) instmap.add("sx", (1,), self.custom_sx_q1) # Inst map is renewed - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() # Remove timing constraints to avoid triggering # scheduling passes. backend.configuration().timing_constraints = {} @@ -179,7 +187,8 @@ def test_transpile_with_instmap(self): def test_transpile_with_custom_gate(self): """Test providing non-basis gate.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.defaults().instruction_schedule_map.add( "my_gate", (0,), self.my_gate_q0, arguments=["P0"] ) @@ -215,7 +224,8 @@ def test_transpile_with_custom_gate(self): def test_transpile_with_parameterized_custom_gate(self): """Test providing non-basis gate, which is kept parameterized throughout transpile.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.defaults().instruction_schedule_map.add( "my_gate", (0,), self.my_gate_q0, arguments=["P0"] ) @@ -244,7 +254,8 @@ def test_transpile_with_parameterized_custom_gate(self): def test_transpile_with_multiple_circuits(self): """Test transpile with multiple circuits with custom gate.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.defaults().instruction_schedule_map.add( "my_gate", (0,), self.my_gate_q0, arguments=["P0"] ) @@ -274,7 +285,8 @@ def test_transpile_with_multiple_circuits(self): def test_multiple_instructions_with_different_parameters(self): """Test adding many instruction with different parameter binding.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.defaults().instruction_schedule_map.add( "my_gate", (0,), self.my_gate_q0, arguments=["P0"] ) @@ -308,7 +320,8 @@ def test_multiple_instructions_with_different_parameters(self): def test_transpile_with_different_qubit(self): """Test transpile with qubit without custom gate.""" - backend = Fake27QPulseV1() + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() backend.defaults().instruction_schedule_map.add("sx", (0,), self.custom_sx_q0) # Remove timing constraints to avoid triggering # scheduling passes. @@ -329,16 +342,20 @@ def test_transpile_with_both_instmap_and_empty_target(self, opt_level): Test case from Qiskit/qiskit-terra/#9489 """ - instmap = Fake27QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() + instmap = backend.defaults().instruction_schedule_map instmap.add("sx", (0,), self.custom_sx_q0) instmap.add("sx", (1,), self.custom_sx_q1) instmap.add("cx", (0, 1), self.custom_cx_q01) + with self.assertWarns(DeprecationWarning): + backend_pulse = Fake27QPulseV1() # This doesn't have custom schedule definition target = GenericBackendV2( num_qubits=5, coupling_map=BOGOTA_CMAP, - calibrate_instructions=Fake27QPulseV1().defaults().instruction_schedule_map, + calibrate_instructions=backend_pulse.defaults().instruction_schedule_map, seed=42, ).target @@ -371,7 +388,10 @@ def test_transpile_with_instmap_with_v2backend(self, opt_level): Test case from Qiskit/qiskit-terra/#9489 """ - instmap = Fake27QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() + + instmap = backend.defaults().instruction_schedule_map instmap.add("sx", (0,), self.custom_sx_q0) instmap.add("sx", (1,), self.custom_sx_q1) instmap.add("cx", (0, 1), self.custom_cx_q01) @@ -380,9 +400,12 @@ def test_transpile_with_instmap_with_v2backend(self, opt_level): qc.append(random_unitary(4, seed=123), [0, 1]) qc.measure_all() + with self.assertWarns(DeprecationWarning): + backend_pulse = Fake27QPulseV1() + backend = GenericBackendV2( num_qubits=5, - calibrate_instructions=Fake27QPulseV1().defaults().instruction_schedule_map, + calibrate_instructions=backend_pulse.defaults().instruction_schedule_map, seed=42, ) @@ -416,8 +439,9 @@ def test_transpile_with_instmap_with_v2backend_with_custom_gate(self, opt_level) """ with pulse.build(name="custom") as rabi12: pulse.play(pulse.Constant(100, 0.4), pulse.DriveChannel(0)) - - instmap = Fake27QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() + instmap = backend.defaults().instruction_schedule_map instmap.add("rabi12", (0,), rabi12) gate = circuit.Gate("rabi12", 1, []) @@ -457,7 +481,10 @@ def test_transpile_with_instmap_not_mutate_backend(self): ) original_sx0 = backend.target["sx"][(0,)].calibration - instmap = Fake27QPulseV1().defaults().instruction_schedule_map + with self.assertWarns(DeprecationWarning): + backend_pulse = Fake27QPulseV1() + + instmap = backend_pulse.defaults().instruction_schedule_map instmap.add("sx", (0,), self.custom_sx_q0) qc = circuit.QuantumCircuit(1) diff --git a/test/python/transpiler/test_sabre_layout.py b/test/python/transpiler/test_sabre_layout.py index 0a7b977162a3..42ae3691c3ca 100644 --- a/test/python/transpiler/test_sabre_layout.py +++ b/test/python/transpiler/test_sabre_layout.py @@ -195,7 +195,11 @@ def test_layout_with_classical_bits(self): rz(0) q4835[1]; """ ) - res = transpile(qc, Fake27QPulseV1(), layout_method="sabre", seed_transpiler=1234) + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() + res = transpile( + qc, backend, layout_method="sabre", seed_transpiler=1234, optimization_level=1 + ) self.assertIsInstance(res, QuantumCircuit) layout = res._layout.initial_layout self.assertEqual( @@ -245,12 +249,15 @@ def test_layout_many_search_trials(self): barrier q18585[5],q18585[2],q18585[8],q18585[3],q18585[6]; """ ) + with self.assertWarns(DeprecationWarning): + backend = Fake27QPulseV1() res = transpile( qc, - Fake27QPulseV1(), + backend, layout_method="sabre", routing_method="stochastic", seed_transpiler=12345, + optimization_level=1, ) self.assertIsInstance(res, QuantumCircuit) layout = res._layout.initial_layout diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index b1effdae7d8b..6650ca27c6f4 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -15,6 +15,7 @@ import unittest import itertools + import ddt import numpy.random @@ -24,7 +25,7 @@ from qiskit.circuit.random import random_circuit from qiskit.compiler.transpiler import transpile from qiskit.converters import circuit_to_dag, dag_to_circuit -from qiskit.providers.fake_provider import Fake27QPulseV1, GenericBackendV2 +from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.transpiler.passes import SabreSwap, TrivialLayout, CheckMap from qiskit.transpiler import CouplingMap, Layout, PassManager, Target, TranspilerError from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit @@ -278,8 +279,7 @@ def test_no_infinite_loop(self, method): from qiskit_aer import Aer - with self.assertWarns(DeprecationWarning): - sim = Aer.get_backend("aer_simulator") + sim = Aer.get_backend("aer_simulator") in_results = sim.run(qc, shots=4096).result().get_counts() out_results = sim.run(routed, shots=4096).result().get_counts() self.assertEqual(set(in_results), set(out_results)) @@ -1327,11 +1327,15 @@ class TestSabreSwapRandomCircuitValidOutput(QiskitTestCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.backend = Fake27QPulseV1() - cls.backend.configuration().coupling_map = MUMBAI_CMAP - cls.backend.configuration().basis_gates += ["for_loop", "while_loop", "if_else"] - cls.coupling_edge_set = {tuple(x) for x in cls.backend.configuration().coupling_map} - cls.basis_gates = set(cls.backend.configuration().basis_gates) + cls.backend = GenericBackendV2( + num_qubits=27, + calibrate_instructions=True, + control_flow=True, + coupling_map=MUMBAI_CMAP, + seed=42, + ) + cls.coupling_edge_set = {tuple(x) for x in cls.backend.coupling_map} + cls.basis_gates = set(cls.backend.operation_names) def assert_valid_circuit(self, transpiled): """Assert circuit complies with constraints of backend.""" diff --git a/test/python/transpiler/test_star_prerouting.py b/test/python/transpiler/test_star_prerouting.py index ddc8096eefd7..2744113f13cb 100644 --- a/test/python/transpiler/test_star_prerouting.py +++ b/test/python/transpiler/test_star_prerouting.py @@ -15,17 +15,16 @@ """Test the StarPreRouting pass""" import unittest + from test import QiskitTestCase import ddt from qiskit.circuit.library import QFT from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.converters import ( - circuit_to_dag, - dag_to_circuit, -) +from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.quantum_info import Operator from qiskit.transpiler.passes import VF2Layout, ApplyLayout, SabreSwap, SabreLayout +from qiskit.transpiler.passes.layout.vf2_utils import build_interaction_graph from qiskit.transpiler.passes.routing.star_prerouting import StarPreRouting from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.passmanager import PassManager @@ -482,3 +481,23 @@ def test_routing_after_star_prerouting(self): self.assertTrue(Operator.from_circuit(res_sabre), qc) self.assertTrue(Operator.from_circuit(res_star), qc) self.assertTrue(Operator.from_circuit(res_star), Operator.from_circuit(res_sabre)) + + @ddt.data(4, 8, 16, 32) + def test_qft_linearization(self, num_qubits): + """Test the QFT circuit to verify if it is linearized and requires n-2 swaps.""" + + qc = QFT(num_qubits, do_swaps=False, insert_barriers=True).decompose() + dag = circuit_to_dag(qc) + new_dag = StarPreRouting().run(dag) + new_qc = dag_to_circuit(new_dag) + + # Check that resulting result has n-2 swaps, where n is the number of cp gates + swap_count = new_qc.count_ops().get("swap", 0) + cp_count = new_qc.count_ops().get("cp", 0) + self.assertEqual(swap_count, cp_count - 2) + + # Confirm linearization by checking that the number of edges is equal to the number of nodes + interaction_graph = build_interaction_graph(new_dag, strict_direction=False)[0] + num_edges = interaction_graph.num_edges() + num_nodes = interaction_graph.num_nodes() + self.assertEqual(num_edges, num_nodes - 1) diff --git a/test/python/transpiler/test_stochastic_swap.py b/test/python/transpiler/test_stochastic_swap.py index 8c96150ae8ff..4843bac8baf5 100644 --- a/test/python/transpiler/test_stochastic_swap.py +++ b/test/python/transpiler/test_stochastic_swap.py @@ -24,7 +24,7 @@ from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.transpiler.passes.utils import CheckMap from qiskit.circuit.random import random_circuit -from qiskit.providers.fake_provider import Fake27QPulseV1, GenericBackendV2 +from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.compiler.transpiler import transpile from qiskit.circuit import ControlFlowOp, Clbit, CASE_DEFAULT from qiskit.circuit.classical import expr, types @@ -1488,10 +1488,11 @@ class TestStochasticSwapRandomCircuitValidOutput(QiskitTestCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.backend = Fake27QPulseV1() - cls.backend.configuration().basis_gates += ["for_loop", "while_loop", "if_else"] - cls.coupling_edge_set = {tuple(x) for x in cls.backend.configuration().coupling_map} - cls.basis_gates = set(cls.backend.configuration().basis_gates) + cls.backend = GenericBackendV2( + num_qubits=27, calibrate_instructions=True, control_flow=True, seed=42 + ) + cls.coupling_edge_set = {tuple(x) for x in cls.backend.coupling_map} + cls.basis_gates = set(cls.backend.operation_names) def assert_valid_circuit(self, transpiled): """Assert circuit complies with constraints of backend.""" diff --git a/test/python/transpiler/test_target.py b/test/python/transpiler/test_target.py index f63ed5061cc7..928cd09c8dd1 100644 --- a/test/python/transpiler/test_target.py +++ b/test/python/transpiler/test_target.py @@ -1937,7 +1937,8 @@ def test_basis_gates_coupling_map(self): self.assertEqual({(0, 1), (1, 2), (2, 0)}, target["cx"].keys()) def test_properties(self): - fake_backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + fake_backend = Fake5QV1() config = fake_backend.configuration() properties = fake_backend.properties() target = Target.from_configuration( @@ -1950,7 +1951,8 @@ def test_properties(self): self.assertEqual(0, target["rz"][(0,)].duration) def test_properties_with_durations(self): - fake_backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + fake_backend = Fake5QV1() config = fake_backend.configuration() properties = fake_backend.properties() durations = InstructionDurations([("rz", 0, 0.5)], dt=1.0) @@ -1965,7 +1967,8 @@ def test_properties_with_durations(self): self.assertEqual(0.5, target["rz"][(0,)].duration) def test_inst_map(self): - fake_backend = Fake7QPulseV1() + with self.assertWarns(DeprecationWarning): + fake_backend = Fake7QPulseV1() config = fake_backend.configuration() properties = fake_backend.properties() defaults = fake_backend.defaults() @@ -1986,7 +1989,8 @@ def test_inst_map(self): self.assertEqual(target.acquire_alignment, constraints.acquire_alignment) def test_concurrent_measurements(self): - fake_backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + fake_backend = Fake5QV1() config = fake_backend.configuration() target = Target.from_configuration( basis_gates=config.basis_gates, diff --git a/test/python/transpiler/test_unitary_synthesis.py b/test/python/transpiler/test_unitary_synthesis.py index 0f4423426a8a..43acd4ef67af 100644 --- a/test/python/transpiler/test_unitary_synthesis.py +++ b/test/python/transpiler/test_unitary_synthesis.py @@ -148,7 +148,8 @@ def test_two_qubit_synthesis_to_directional_cx_from_gate_errors(self): """Verify two qubit unitaries are synthesized to match basis gates.""" # TODO: should make check more explicit e.g. explicitly set gate # direction in test instead of using specific fake backend - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() qr = QuantumRegister(2) coupling_map = CouplingMap(conf.coupling_map) @@ -182,7 +183,8 @@ def test_swap_synthesis_to_directional_cx(self): """Verify two qubit unitaries are synthesized to match basis gates.""" # TODO: should make check more explicit e.g. explicitly set gate # direction in test instead of using specific fake backend - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() qr = QuantumRegister(2) coupling_map = CouplingMap(conf.coupling_map) @@ -218,7 +220,8 @@ def test_two_qubit_synthesis_to_directional_cx_multiple_registers(self): across multiple registers.""" # TODO: should make check more explicit e.g. explicitly set gate # direction in test instead of using specific fake backend - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() qr0 = QuantumRegister(1) qr1 = QuantumRegister(1) @@ -253,7 +256,8 @@ def test_two_qubit_synthesis_to_directional_cx_from_coupling_map(self): """Verify natural cx direction is used when specified in coupling map.""" # TODO: should make check more explicit e.g. explicitly set gate # direction in test instead of using specific fake backend - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() qr = QuantumRegister(2) coupling_map = CouplingMap([[0, 1], [1, 2], [1, 3], [3, 4]]) @@ -296,7 +300,8 @@ def test_two_qubit_synthesis_to_directional_cx_from_coupling_map_natural_none(se when natural_direction is None.""" # TODO: should make check more explicit e.g. explicitly set gate # direction in test instead of using specific fake backend - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() qr = QuantumRegister(2) coupling_map = CouplingMap([[0, 1], [1, 2], [1, 3], [3, 4]]) @@ -339,7 +344,8 @@ def test_two_qubit_synthesis_to_directional_cx_from_coupling_map_natural_false(s when natural_direction is None.""" # TODO: should make check more explicit e.g. explicitly set gate # direction in test instead of using specific fake backend - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() qr = QuantumRegister(2) coupling_map = CouplingMap([[0, 1], [1, 2], [1, 3], [3, 4]]) @@ -379,7 +385,8 @@ def test_two_qubit_synthesis_to_directional_cx_from_coupling_map_natural_false(s def test_two_qubit_synthesis_not_pulse_optimal(self): """Verify not attempting pulse optimal decomposition when pulse_optimize==False.""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() qr = QuantumRegister(2) qc = QuantumCircuit(qr) @@ -415,7 +422,8 @@ def test_two_qubit_synthesis_not_pulse_optimal(self): def test_two_qubit_pulse_optimal_true_raises(self): """Verify raises if pulse optimal==True but cx is not in the backend basis.""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() # this assumes iswawp pulse optimal decomposition doesn't exist conf.basis_gates = [gate if gate != "cx" else "iswap" for gate in conf.basis_gates] @@ -438,7 +446,8 @@ def test_two_qubit_pulse_optimal_true_raises(self): def test_two_qubit_natural_direction_true_duration_fallback(self): """Verify not attempting pulse optimal decomposition when pulse_optimize==False.""" # this assumes iswawp pulse optimal decomposition doesn't exist - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() # conf.basis_gates = [gate if gate != "cx" else "iswap" for gate in conf.basis_gates] qr = QuantumRegister(2) @@ -462,7 +471,8 @@ def test_two_qubit_natural_direction_true_duration_fallback(self): def test_two_qubit_natural_direction_true_gate_length_raises(self): """Verify not attempting pulse optimal decomposition when pulse_optimize==False.""" # this assumes iswawp pulse optimal decomposition doesn't exist - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() for _, nduv in backend.properties()._gates["cx"].items(): nduv["gate_length"] = (4e-7, nduv["gate_length"][1]) @@ -485,7 +495,8 @@ def test_two_qubit_natural_direction_true_gate_length_raises(self): def test_two_qubit_pulse_optimal_none_optimal(self): """Verify pulse optimal decomposition when pulse_optimize==None.""" # this assumes iswawp pulse optimal decomposition doesn't exist - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() qr = QuantumRegister(2) coupling_map = CouplingMap([[0, 1], [1, 2], [1, 3], [3, 4]]) @@ -512,7 +523,8 @@ def test_two_qubit_pulse_optimal_none_no_raise(self): """Verify pulse optimal decomposition when pulse_optimize==None doesn't raise when pulse optimal decomposition unknown.""" # this assumes iswawp pulse optimal decomposition doesn't exist - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() conf = backend.configuration() conf.basis_gates = [gate if gate != "cx" else "iswap" for gate in conf.basis_gates] qr = QuantumRegister(2) @@ -662,7 +674,8 @@ def test_coupling_map_unequal_durations(self, opt): qr = QuantumRegister(2) circ = QuantumCircuit(qr) circ.append(random_unitary(4, seed=1), [1, 0]) - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() tqc = transpile( circ, backend=backend, diff --git a/test/python/transpiler/test_vf2_layout.py b/test/python/transpiler/test_vf2_layout.py index 716e49d35009..4e4844f5f701 100644 --- a/test/python/transpiler/test_vf2_layout.py +++ b/test/python/transpiler/test_vf2_layout.py @@ -629,7 +629,8 @@ def test_no_properties(self): def test_with_properties(self): """Test it finds the least noise perfect layout with no properties.""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.x(qr) @@ -643,7 +644,8 @@ def test_with_properties(self): def test_max_trials_exceeded(self): """Test it exits when max_trials is reached.""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.x(qr) @@ -663,7 +665,8 @@ def test_max_trials_exceeded(self): def test_time_limit_exceeded(self): """Test the pass stops after time_limit is reached.""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr = QuantumRegister(2) qc = QuantumCircuit(qr) qc.x(qr) @@ -685,9 +688,11 @@ def test_time_limit_exceeded(self): self.assertEqual(set(property_set["layout"].get_physical_bits()), {2, 0}) - def test_reasonable_limits_for_simple_layouts(self): - """Test that the default trials is set to a reasonable number.""" - backend = Fake127QPulseV1() + def test_reasonable_limits_for_simple_layouts_v1(self): + """Test that the default trials is set to a reasonable number. + REMOVE ONCE Fake127QPulseV1 IS GONE""" + with self.assertWarns(DeprecationWarning): + backend = Fake127QPulseV1() qc = QuantumCircuit(5) qc.cx(2, 3) qc.cx(0, 1) @@ -704,9 +709,28 @@ def test_reasonable_limits_for_simple_layouts(self): ) self.assertEqual(set(property_set["layout"].get_physical_bits()), {57, 58, 61, 62, 0}) + def test_reasonable_limits_for_simple_layouts(self): + """Test that the default trials is set to a reasonable number.""" + backend = GenericBackendV2(27, calibrate_instructions=True, seed=42) + qc = QuantumCircuit(5) + qc.cx(2, 3) + qc.cx(0, 1) + + # Run without any limits set + vf2_pass = VF2Layout(target=backend.target, seed=42) + property_set = {} + with self.assertLogs("qiskit.transpiler.passes.layout.vf2_layout", level="DEBUG") as cm: + vf2_pass(qc, property_set) + self.assertIn( + "DEBUG:qiskit.transpiler.passes.layout.vf2_layout:Trial 717 is >= configured max trials 717", + cm.output, + ) + self.assertEqual(set(property_set["layout"].get_physical_bits()), {16, 24, 6, 7, 0}) + def test_no_limits_with_negative(self): """Test that we're not enforcing a trial limit if set to negative.""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qc = QuantumCircuit(3) qc.h(0) cmap = CouplingMap(backend.configuration().coupling_map) diff --git a/test/python/transpiler/test_vf2_post_layout.py b/test/python/transpiler/test_vf2_post_layout.py index 3df2fefcc73b..e97ed279a8d8 100644 --- a/test/python/transpiler/test_vf2_post_layout.py +++ b/test/python/transpiler/test_vf2_post_layout.py @@ -102,8 +102,9 @@ def test_no_backend_properties(self): def test_empty_circuit(self): """Test no solution found for empty circuit""" + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qc = QuantumCircuit(2, 2) - backend = Fake5QV1() cmap = CouplingMap(backend.configuration().coupling_map) props = backend.properties() vf2_pass = VF2PostLayout(coupling_map=cmap, properties=props) @@ -128,9 +129,10 @@ def test_empty_circuit_v2(self): def test_skip_3q_circuit(self): """Test that the pass is a no-op on circuits with >2q gates.""" + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qc = QuantumCircuit(3) qc.ccx(0, 1, 2) - backend = Fake5QV1() cmap = CouplingMap(backend.configuration().coupling_map) props = backend.properties() vf2_pass = VF2PostLayout(coupling_map=cmap, properties=props) @@ -141,10 +143,11 @@ def test_skip_3q_circuit(self): def test_skip_3q_circuit_control_flow(self): """Test that the pass is a no-op on circuits with >2q gates.""" + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qc = QuantumCircuit(3) with qc.for_loop((1,)): qc.ccx(0, 1, 2) - backend = Fake5QV1() cmap = CouplingMap(backend.configuration().coupling_map) props = backend.properties() vf2_pass = VF2PostLayout(coupling_map=cmap, properties=props) @@ -182,7 +185,8 @@ def test_skip_3q_circuit_control_flow_v2(self): def test_best_mapping_ghz_state_full_device_multiple_qregs(self): """Test best mappings with multiple registers""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr_a = QuantumRegister(2) qr_b = QuantumRegister(3) qc = QuantumCircuit(qr_a, qr_b) @@ -207,7 +211,8 @@ def test_2q_circuit_5q_backend(self): 0 - 1 qr1 - qr0 """ - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr = QuantumRegister(2, "qr") circuit = QuantumCircuit(qr) @@ -227,7 +232,8 @@ def test_2q_circuit_5q_backend_controlflow(self): 0 - 1 qr1 - qr0 """ - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() circuit = QuantumCircuit(2, 1) with circuit.for_loop((1,)): @@ -253,7 +259,8 @@ def test_2q_circuit_5q_backend_max_trials(self): qr1 - qr0 """ max_trials = 11 - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr = QuantumRegister(2, "qr") circuit = QuantumCircuit(qr) @@ -564,8 +571,10 @@ def test_no_backend_properties(self): def test_empty_circuit(self): """Test no solution found for empty circuit""" + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() + qc = QuantumCircuit(2, 2) - backend = Fake5QV1() cmap = CouplingMap(backend.configuration().coupling_map) props = backend.properties() vf2_pass = VF2PostLayout(coupling_map=cmap, properties=props, strict_direction=False) @@ -593,9 +602,11 @@ def test_empty_circuit_v2(self): def test_skip_3q_circuit(self): """Test that the pass is a no-op on circuits with >2q gates.""" + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() + qc = QuantumCircuit(3) qc.ccx(0, 1, 2) - backend = Fake5QV1() cmap = CouplingMap(backend.configuration().coupling_map) props = backend.properties() vf2_pass = VF2PostLayout(coupling_map=cmap, properties=props, strict_direction=False) @@ -624,7 +635,8 @@ def test_skip_3q_circuit_v2(self): def test_best_mapping_ghz_state_full_device_multiple_qregs(self): """Test best mappings with multiple registers""" - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr_a = QuantumRegister(2) qr_b = QuantumRegister(3) qc = QuantumCircuit(qr_a, qr_b) @@ -651,7 +663,8 @@ def test_2q_circuit_5q_backend(self): 0 - 1 qr1 - qr0 """ - backend = Fake5QV1() + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() qr = QuantumRegister(2, "qr") circuit = QuantumCircuit(qr) diff --git a/test/python/visualization/test_circuit_drawer.py b/test/python/visualization/test_circuit_drawer.py index d459bd945046..dd69faac02cb 100644 --- a/test/python/visualization/test_circuit_drawer.py +++ b/test/python/visualization/test_circuit_drawer.py @@ -249,7 +249,7 @@ def test_warning_for_bad_justify_argument(self): error_message = re.escape( f"Setting QuantumCircuit.draw()’s or circuit_drawer()'s justify argument: {bad_arg}, to a " "value other than 'left', 'right', 'none' or None (='left'). Default 'left' will be used. " - "Support for invalid justify arguments is deprecated as of qiskit 1.2.0. Starting no " + "Support for invalid justify arguments is deprecated as of Qiskit 1.2.0. Starting no " "earlier than 3 months after the release date, invalid arguments will error.", ) diff --git a/test/python/visualization/test_circuit_latex.py b/test/python/visualization/test_circuit_latex.py index 5490648fd6bf..1be7ad7ce051 100644 --- a/test/python/visualization/test_circuit_latex.py +++ b/test/python/visualization/test_circuit_latex.py @@ -480,11 +480,15 @@ def test_partial_layout(self): """Tests partial_layout See: https://github.com/Qiskit/qiskit-terra/issues/4757""" filename = self._get_resource_path("test_latex_partial_layout.tex") + + with self.assertWarns(DeprecationWarning): + backend = Fake5QV1() + circuit = QuantumCircuit(3) circuit.h(1) transpiled = transpile( circuit, - backend=Fake5QV1(), + backend=backend, optimization_level=0, initial_layout=[1, 2, 0], seed_transpiler=0, diff --git a/test/python/visualization/test_gate_map.py b/test/python/visualization/test_gate_map.py index bf9b1ca80d79..fcac1e71c400 100644 --- a/test/python/visualization/test_gate_map.py +++ b/test/python/visualization/test_gate_map.py @@ -19,8 +19,6 @@ Fake5QV1, Fake20QV1, Fake7QPulseV1, - Fake27QPulseV1, - Fake127QPulseV1, GenericBackendV2, ) from qiskit.visualization import ( @@ -33,6 +31,7 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.transpiler.layout import Layout, TranspileLayout from .visualization import path_to_diagram_reference, QiskitVisualizationTestCase +from ..legacy_cmaps import KYOTO_CMAP, MUMBAI_CMAP if optionals.HAS_MATPLOTLIB: import matplotlib.pyplot as plt @@ -108,7 +107,11 @@ def test_plot_gate_map_no_backend(self): @unittest.skipUnless(optionals.HAS_SEABORN, "Seaborn not installed") def test_plot_error_map_backend_v1(self): """Test plotting error map with fake backend v1.""" - backend = Fake27QPulseV1() + backend = GenericBackendV2( + num_qubits=27, + pulse_channels=True, + coupling_map=MUMBAI_CMAP, + ) img_ref = path_to_diagram_reference("fake_27_q_error.png") fig = plot_error_map(backend) with BytesIO() as img_buffer: @@ -122,9 +125,11 @@ def test_plot_error_map_backend_v1(self): @unittest.skipUnless(optionals.HAS_SEABORN, "Seaborn not installed") def test_plot_error_map_backend_v2(self): """Test plotting error map with fake backend v2.""" + coupling_map = MUMBAI_CMAP backend = GenericBackendV2( num_qubits=27, - coupling_map=Fake27QPulseV1().configuration().coupling_map, + pulse_channels=True, + coupling_map=coupling_map, ) img_ref = path_to_diagram_reference("fake_27_q_v2_error.png") fig = plot_error_map(backend) @@ -139,7 +144,10 @@ def test_plot_error_map_backend_v2(self): @unittest.skipUnless(optionals.HAS_SEABORN, "Seaborn not installed") def test_plot_error_map_over_100_qubit(self): """Test plotting error map with large fake backend.""" - backend = Fake127QPulseV1() + coupling_map = KYOTO_CMAP + backend = GenericBackendV2( + num_qubits=127, coupling_map=coupling_map, pulse_channels=True, seed=42 + ) img_ref = path_to_diagram_reference("fake_127_q_error.png") fig = plot_error_map(backend) with BytesIO() as img_buffer: @@ -153,9 +161,294 @@ def test_plot_error_map_over_100_qubit(self): @unittest.skipUnless(optionals.HAS_SEABORN, "Seaborn not installed") def test_plot_error_map_over_100_qubit_backend_v2(self): """Test plotting error map with large fake backendv2.""" + coupling_map = [ + [0, 1], + [0, 14], + [1, 0], + [1, 2], + [2, 1], + [2, 3], + [3, 2], + [3, 4], + [4, 3], + [4, 5], + [4, 15], + [5, 4], + [5, 6], + [6, 5], + [6, 7], + [7, 6], + [7, 8], + [8, 7], + [8, 16], + [9, 10], + [10, 9], + [10, 11], + [11, 10], + [11, 12], + [12, 11], + [12, 13], + [12, 17], + [13, 12], + [14, 0], + [14, 18], + [15, 4], + [15, 22], + [16, 8], + [16, 26], + [17, 12], + [17, 30], + [18, 14], + [18, 19], + [19, 18], + [19, 20], + [20, 19], + [20, 21], + [20, 33], + [21, 20], + [21, 22], + [22, 15], + [22, 21], + [22, 23], + [23, 22], + [23, 24], + [24, 23], + [24, 25], + [24, 34], + [25, 24], + [25, 26], + [26, 16], + [26, 25], + [26, 27], + [27, 26], + [27, 28], + [28, 27], + [28, 29], + [28, 35], + [29, 28], + [29, 30], + [30, 17], + [30, 29], + [30, 31], + [31, 30], + [31, 32], + [32, 31], + [32, 36], + [33, 20], + [33, 39], + [34, 24], + [34, 43], + [35, 28], + [35, 47], + [36, 32], + [36, 51], + [37, 38], + [37, 52], + [38, 37], + [38, 39], + [39, 33], + [39, 38], + [39, 40], + [40, 39], + [40, 41], + [41, 40], + [41, 42], + [41, 53], + [42, 41], + [42, 43], + [43, 34], + [43, 42], + [43, 44], + [44, 43], + [44, 45], + [45, 44], + [45, 46], + [45, 54], + [46, 45], + [46, 47], + [47, 35], + [47, 46], + [47, 48], + [48, 47], + [48, 49], + [49, 48], + [49, 50], + [49, 55], + [50, 49], + [50, 51], + [51, 36], + [51, 50], + [52, 37], + [52, 56], + [53, 41], + [53, 60], + [54, 45], + [54, 64], + [55, 49], + [55, 68], + [56, 52], + [56, 57], + [57, 56], + [57, 58], + [58, 57], + [58, 59], + [58, 71], + [59, 58], + [59, 60], + [60, 53], + [60, 59], + [60, 61], + [61, 60], + [61, 62], + [62, 61], + [62, 63], + [62, 72], + [63, 62], + [63, 64], + [64, 54], + [64, 63], + [64, 65], + [65, 64], + [65, 66], + [66, 65], + [66, 67], + [66, 73], + [67, 66], + [67, 68], + [68, 55], + [68, 67], + [68, 69], + [69, 68], + [69, 70], + [70, 69], + [70, 74], + [71, 58], + [71, 77], + [72, 62], + [72, 81], + [73, 66], + [73, 85], + [74, 70], + [74, 89], + [75, 76], + [75, 90], + [76, 75], + [76, 77], + [77, 71], + [77, 76], + [77, 78], + [78, 77], + [78, 79], + [79, 78], + [79, 80], + [79, 91], + [80, 79], + [80, 81], + [81, 72], + [81, 80], + [81, 82], + [82, 81], + [82, 83], + [83, 82], + [83, 84], + [83, 92], + [84, 83], + [84, 85], + [85, 73], + [85, 84], + [85, 86], + [86, 85], + [86, 87], + [87, 86], + [87, 88], + [87, 93], + [88, 87], + [88, 89], + [89, 74], + [89, 88], + [90, 75], + [90, 94], + [91, 79], + [91, 98], + [92, 83], + [92, 102], + [93, 87], + [93, 106], + [94, 90], + [94, 95], + [95, 94], + [95, 96], + [96, 95], + [96, 97], + [96, 109], + [97, 96], + [97, 98], + [98, 91], + [98, 97], + [98, 99], + [99, 98], + [99, 100], + [100, 99], + [100, 101], + [100, 110], + [101, 100], + [101, 102], + [102, 92], + [102, 101], + [102, 103], + [103, 102], + [103, 104], + [104, 103], + [104, 105], + [104, 111], + [105, 104], + [105, 106], + [106, 93], + [106, 105], + [106, 107], + [107, 106], + [107, 108], + [108, 107], + [108, 112], + [109, 96], + [110, 100], + [110, 118], + [111, 104], + [111, 122], + [112, 108], + [112, 126], + [113, 114], + [114, 113], + [114, 115], + [115, 114], + [115, 116], + [116, 115], + [116, 117], + [117, 116], + [117, 118], + [118, 110], + [118, 117], + [118, 119], + [119, 118], + [119, 120], + [120, 119], + [120, 121], + [121, 120], + [121, 122], + [122, 111], + [122, 121], + [122, 123], + [123, 122], + [123, 124], + [124, 123], + [124, 125], + [125, 124], + [125, 126], + [126, 112], + [126, 125], + ] backend = GenericBackendV2( - num_qubits=127, - coupling_map=Fake127QPulseV1().configuration().coupling_map, + num_qubits=127, coupling_map=coupling_map, pulse_channels=True, seed=42 ) img_ref = path_to_diagram_reference("fake_127_q_v2_error.png") fig = plot_error_map(backend) diff --git a/test/utils/base.py b/test/utils/base.py index 0b02f3e58488..6d0c917ace30 100644 --- a/test/utils/base.py +++ b/test/utils/base.py @@ -123,12 +123,30 @@ def setUpClass(cls): # Safe to remove once https://github.com/Qiskit/qiskit-aer/pull/2179 is in a release version # of Aer. warnings.filterwarnings( - "default", + "ignore", # If "default", it floods the CI output category=DeprecationWarning, message="Treating CircuitInstruction as an iterable is deprecated", module=r"qiskit_aer(\.[a-zA-Z0-9_]+)*", ) + # Safe to remove once https://github.com/Qiskit/qiskit-aer/issues/2065 is in a release version + # of Aer. + warnings.filterwarnings( + "ignore", # If "default", it floods the CI output + category=DeprecationWarning, + message=r".*The `Qobj` class and related functionality.*", + module=r"qiskit_aer", + ) + + # Safe to remove once https://github.com/Qiskit/qiskit-aer/pull/2184 is in a release version + # of Aer. + warnings.filterwarnings( + "ignore", # If "default", it floods the CI output + category=DeprecationWarning, + message=r".*The abstract Provider and ProviderV1 classes are deprecated.*", + module="qiskit_aer", + ) + allow_DeprecationWarning_message = [ r"The property ``qiskit\.circuit\.bit\.Bit\.(register|index)`` is deprecated.*", ] diff --git a/tools/pgo_scripts/test_utility_scale.py b/tools/pgo_scripts/test_utility_scale.py index 30d3d7a7b32f..0e9e94655692 100755 --- a/tools/pgo_scripts/test_utility_scale.py +++ b/tools/pgo_scripts/test_utility_scale.py @@ -15,11 +15,10 @@ """Script to generate 'utility scale' load for profiling in a PGO context""" import os - -from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager +from qiskit import qasm2 from qiskit.providers.fake_provider import GenericBackendV2 from qiskit.transpiler import CouplingMap -from qiskit import qasm2 +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager QASM_DIR = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), diff --git a/tox.ini b/tox.ini index 3ee544538a09..89dc84d1758a 100644 --- a/tox.ini +++ b/tox.ini @@ -16,6 +16,7 @@ setenv = QISKIT_TEST_CAPTURE_STREAMS=1 QISKIT_PARALLEL=FALSE passenv = + RUSTUP_TOOLCHAIN RAYON_NUM_THREADS OMP_NUM_THREADS QISKIT_PARALLEL