diff --git a/crates/accelerate/src/basis/basis_translator/basis_search.rs b/crates/accelerate/src/basis/basis_translator/basis_search.rs index 2810765db741..4686ba9c4c3f 100644 --- a/crates/accelerate/src/basis/basis_translator/basis_search.rs +++ b/crates/accelerate/src/basis/basis_translator/basis_search.rs @@ -13,7 +13,6 @@ use std::cell::RefCell; use hashbrown::{HashMap, HashSet}; -use pyo3::prelude::*; use crate::equivalence::{EdgeData, Equivalence, EquivalenceLibrary, Key, NodeData}; use qiskit_circuit::operations::Operation; @@ -23,28 +22,6 @@ use rustworkx_core::traversal::{dijkstra_search, DijkstraEvent}; use super::compose_transforms::{BasisTransformIn, GateIdentifier}; -/// Search for a set of transformations from source_basis to target_basis. -/// Args: -/// equiv_lib (EquivalenceLibrary): Source of valid translations -/// source_basis (Set[Tuple[gate_name: str, gate_num_qubits: int]]): Starting basis. -/// target_basis (Set[gate_name: str]): Target basis. -/// -/// Returns: -/// Optional[List[Tuple[gate, equiv_params, equiv_circuit]]]: List of (gate, -/// equiv_params, equiv_circuit) tuples tuples which, if applied in order -/// will map from source_basis to target_basis. Returns None if no path -/// was found. -#[pyfunction] -#[pyo3(name = "basis_search")] -pub(crate) fn py_basis_search( - py: Python, - equiv_lib: &mut EquivalenceLibrary, - source_basis: HashSet, - target_basis: HashSet, -) -> PyObject { - basis_search(equiv_lib, &source_basis, &target_basis).into_py(py) -} - type BasisTransforms = Vec<(GateIdentifier, BasisTransformIn)>; /// Search for a set of transformations from source_basis to target_basis. /// diff --git a/crates/accelerate/src/basis/basis_translator/compose_transforms.rs b/crates/accelerate/src/basis/basis_translator/compose_transforms.rs index e4498af0f8a5..b1366c30bf2f 100644 --- a/crates/accelerate/src/basis/basis_translator/compose_transforms.rs +++ b/crates/accelerate/src/basis/basis_translator/compose_transforms.rs @@ -30,16 +30,6 @@ pub type GateIdentifier = (String, u32); pub type BasisTransformIn = (SmallVec<[Param; 3]>, CircuitFromPython); pub type BasisTransformOut = (SmallVec<[Param; 3]>, DAGCircuit); -#[pyfunction(name = "compose_transforms")] -pub(super) fn py_compose_transforms( - py: Python, - basis_transforms: Vec<(GateIdentifier, BasisTransformIn)>, - source_basis: HashSet, - source_dag: &DAGCircuit, -) -> PyResult> { - compose_transforms(py, &basis_transforms, &source_basis, source_dag) -} - pub(super) fn compose_transforms<'a>( py: Python, basis_transforms: &'a [(GateIdentifier, BasisTransformIn)], diff --git a/crates/accelerate/src/basis/basis_translator/mod.rs b/crates/accelerate/src/basis/basis_translator/mod.rs index 18970065267c..c900f80beff4 100644 --- a/crates/accelerate/src/basis/basis_translator/mod.rs +++ b/crates/accelerate/src/basis/basis_translator/mod.rs @@ -10,14 +10,803 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +use compose_transforms::BasisTransformIn; +use compose_transforms::BasisTransformOut; +use compose_transforms::GateIdentifier; + +use basis_search::basis_search; +use compose_transforms::compose_transforms; +use hashbrown::{HashMap, HashSet}; +use itertools::Itertools; +use pyo3::intern; use pyo3::prelude::*; -pub mod basis_search; +mod basis_search; mod compose_transforms; +use pyo3::types::{IntoPyDict, PyComplex, PyDict, PyTuple}; +use pyo3::PyTypeInfo; +use qiskit_circuit::circuit_instruction::OperationFromPython; +use qiskit_circuit::converters::circuit_to_dag; +use qiskit_circuit::imports::DAG_TO_CIRCUIT; +use qiskit_circuit::imports::PARAMETER_EXPRESSION; +use qiskit_circuit::operations::Param; +use qiskit_circuit::packed_instruction::PackedInstruction; +use qiskit_circuit::{ + circuit_data::CircuitData, + dag_circuit::DAGCircuit, + operations::{Operation, OperationRef}, +}; +use qiskit_circuit::{Clbit, Qubit}; +use smallvec::SmallVec; + +use crate::equivalence::EquivalenceLibrary; +use crate::nlayout::PhysicalQubit; +use crate::target_transpiler::exceptions::TranspilerError; +use crate::target_transpiler::{Qargs, Target}; + +type InstMap = HashMap; +type ExtraInstructionMap<'a> = HashMap<&'a Option, InstMap>; + +#[allow(clippy::too_many_arguments)] +#[pyfunction(name = "base_run", signature = (dag, equiv_lib, qargs_with_non_global_operation, min_qubits, target_basis=None, target=None, non_global_operations=None))] +fn run( + py: Python<'_>, + dag: DAGCircuit, + equiv_lib: &mut EquivalenceLibrary, + qargs_with_non_global_operation: HashMap, HashSet>, + min_qubits: usize, + target_basis: Option>, + target: Option<&Target>, + non_global_operations: Option>, +) -> PyResult { + if target_basis.is_none() && target.is_none() { + return Ok(dag); + } + + let basic_instrs: HashSet; + let mut source_basis: HashSet = HashSet::default(); + let mut new_target_basis: HashSet; + let mut qargs_local_source_basis: HashMap, HashSet> = + HashMap::default(); + if let Some(target) = target.as_ref() { + basic_instrs = ["barrier", "snapshot", "store"] + .into_iter() + .map(|x| x.to_string()) + .collect(); + let non_global_str: HashSet<&str> = if let Some(operations) = non_global_operations.as_ref() + { + operations.iter().map(|x| x.as_str()).collect() + } else { + HashSet::default() + }; + let target_keys = target.keys().collect::>(); + new_target_basis = target_keys + .difference(&non_global_str) + .map(|x| x.to_string()) + .collect(); + extract_basis_target( + py, + &dag, + &mut source_basis, + &mut qargs_local_source_basis, + min_qubits, + &qargs_with_non_global_operation, + )?; + } else { + basic_instrs = ["measure", "reset", "barrier", "snapshot", "delay", "store"] + .into_iter() + .map(|x| x.to_string()) + .collect(); + source_basis = extract_basis(py, &dag, min_qubits)?; + new_target_basis = target_basis.unwrap(); + } + new_target_basis = new_target_basis + .union(&basic_instrs) + .map(|x| x.to_string()) + .collect(); + // If the source basis is a subset of the target basis and we have no circuit + // instructions on qargs that have non-global operations there is nothing to + // translate and we can exit early. + let source_basis_names: HashSet = source_basis.iter().map(|x| x.0.clone()).collect(); + if source_basis_names.is_subset(&new_target_basis) && qargs_local_source_basis.is_empty() { + return Ok(dag); + } + let basis_transforms = basis_search(equiv_lib, &source_basis, &new_target_basis); + let mut qarg_local_basis_transforms: HashMap< + Option, + Vec<(GateIdentifier, BasisTransformIn)>, + > = HashMap::default(); + for (qarg, local_source_basis) in qargs_local_source_basis.iter() { + // For any multiqubit operation that contains a subset of qubits that + // has a non-local operation, include that non-local operation in the + // search. This matches with the check we did above to include those + // subset non-local operations in the check here. + let mut expanded_target = new_target_basis.clone(); + if qarg.as_ref().is_some_and(|qarg| qarg.len() > 1) { + let qarg_as_set: HashSet = + HashSet::from_iter(qarg.as_ref().unwrap().iter().copied()); + for (non_local_qarg, local_basis) in qargs_with_non_global_operation.iter() { + if let Some(non_local_qarg) = non_local_qarg { + let non_local_qarg_as_set = HashSet::from_iter(non_local_qarg.iter().copied()); + if qarg_as_set.is_superset(&non_local_qarg_as_set) { + expanded_target = expanded_target.union(local_basis).cloned().collect(); + } + } + } + } else { + expanded_target = expanded_target + .union(&qargs_with_non_global_operation[qarg]) + .cloned() + .collect(); + } + let local_basis_transforms = basis_search(equiv_lib, local_source_basis, &expanded_target); + if let Some(local_basis_transforms) = local_basis_transforms { + qarg_local_basis_transforms.insert(qarg.clone(), local_basis_transforms); + } else { + return Err(TranspilerError::new_err(format!( + "Unable to translate the operations in the circuit: \ + {:?} to the backend's (or manually specified) target \ + basis: {:?}. This likely means the target basis is not universal \ + or there are additional equivalence rules needed in the EquivalenceLibrary being \ + used. For more details on this error see: \ + https://docs.quantum.ibm.com/api/qiskit/qiskit.transpiler.passes.\ + BasisTranslator#translation-errors", + local_source_basis + .iter() + .map(|x| x.0.as_str()) + .collect_vec(), + &expanded_target + ))); + } + } + + let Some(basis_transforms) = basis_transforms else { + return Err(TranspilerError::new_err(format!( + "Unable to translate the operations in the circuit: \ + {:?} to the backend's (or manually specified) target \ + basis: {:?}. This likely means the target basis is not universal \ + or there are additional equivalence rules needed in the EquivalenceLibrary being \ + used. For more details on this error see: \ + https://docs.quantum.ibm.com/api/qiskit/qiskit.transpiler.passes. \ + BasisTranslator#translation-errors", + source_basis.iter().map(|x| x.0.as_str()).collect_vec(), + &new_target_basis + ))); + }; + + let instr_map: InstMap = compose_transforms(py, &basis_transforms, &source_basis, &dag)?; + let extra_inst_map: ExtraInstructionMap = qarg_local_basis_transforms + .iter() + .map(|(qarg, transform)| -> PyResult<_> { + Ok(( + qarg, + compose_transforms(py, transform, &qargs_local_source_basis[qarg], &dag)?, + )) + }) + .collect::>()?; + + let (out_dag, _) = apply_translation( + py, + &dag, + &new_target_basis, + &instr_map, + &extra_inst_map, + min_qubits, + &qargs_with_non_global_operation, + )?; + Ok(out_dag) +} + +/// Method that extracts all non-calibrated gate instances identifiers from a DAGCircuit. +fn extract_basis( + py: Python, + circuit: &DAGCircuit, + min_qubits: usize, +) -> PyResult> { + let mut basis = HashSet::default(); + // Recurse for DAGCircuit + fn recurse_dag( + py: Python, + circuit: &DAGCircuit, + basis: &mut HashSet, + min_qubits: usize, + ) -> PyResult<()> { + for node in circuit.op_nodes(true) { + let operation: &PackedInstruction = circuit.dag()[node].unwrap_operation(); + if !circuit.has_calibration_for_index(py, node)? + && circuit.get_qargs(operation.qubits).len() >= min_qubits + { + basis.insert((operation.op.name().to_string(), operation.op.num_qubits())); + } + if operation.op.control_flow() { + let OperationRef::Instruction(inst) = operation.op.view() else { + unreachable!("Control flow operation is not an instance of PyInstruction.") + }; + let inst_bound = inst.instruction.bind(py); + for block in inst_bound.getattr("blocks")?.iter()? { + recurse_circuit(py, block?, basis, min_qubits)?; + } + } + } + Ok(()) + } + + // Recurse for QuantumCircuit + fn recurse_circuit( + py: Python, + circuit: Bound, + basis: &mut HashSet, + min_qubits: usize, + ) -> PyResult<()> { + let circuit_data: PyRef = circuit + .getattr(intern!(py, "_data"))? + .downcast_into()? + .borrow(); + for (index, inst) in circuit_data.iter().enumerate() { + let instruction_object = circuit.get_item(index)?; + let has_calibration = circuit + .call_method1(intern!(py, "_has_calibration_for"), (&instruction_object,))?; + if !has_calibration.is_truthy()? + && circuit_data.get_qargs(inst.qubits).len() >= min_qubits + { + basis.insert((inst.op.name().to_string(), inst.op.num_qubits())); + } + if inst.op.control_flow() { + let operation_ob = instruction_object.getattr(intern!(py, "operation"))?; + let blocks = operation_ob.getattr("blocks")?; + for block in blocks.iter()? { + recurse_circuit(py, block?, basis, min_qubits)?; + } + } + } + Ok(()) + } + + recurse_dag(py, circuit, &mut basis, min_qubits)?; + Ok(basis) +} + +/// Method that extracts a mapping of all the qargs in the local_source basis +/// obtained from the [Target], to all non-calibrated gate instances identifiers from a DAGCircuit. +/// When dealing with `ControlFlowOp` instances the function will perform a recursion call +/// to a variant design to handle instances of `QuantumCircuit`. +fn extract_basis_target( + py: Python, + dag: &DAGCircuit, + source_basis: &mut HashSet, + qargs_local_source_basis: &mut HashMap, HashSet>, + min_qubits: usize, + qargs_with_non_global_operation: &HashMap, HashSet>, +) -> PyResult<()> { + for node in dag.op_nodes(true) { + let node_obj: &PackedInstruction = dag.dag()[node].unwrap_operation(); + let qargs: &[Qubit] = dag.get_qargs(node_obj.qubits); + if dag.has_calibration_for_index(py, node)? || qargs.len() < min_qubits { + continue; + } + // Treat the instruction as on an incomplete basis if the qargs are in the + // qargs_with_non_global_operation dictionary or if any of the qubits in qargs + // are a superset for a non-local operation. For example, if the qargs + // are (0, 1) and that's a global (ie no non-local operations on (0, 1) + // operation but there is a non-local operation on (1,) we need to + // do an extra non-local search for this op to ensure we include any + // single qubit operation for (1,) as valid. This pattern also holds + // true for > 2q ops too (so for 4q operations we need to check for 3q, 2q, + // and 1q operations in the same manner) + let physical_qargs: SmallVec<[PhysicalQubit; 2]> = + qargs.iter().map(|x| PhysicalQubit(x.0)).collect(); + let physical_qargs_as_set: HashSet = + HashSet::from_iter(physical_qargs.iter().copied()); + if qargs_with_non_global_operation.contains_key(&Some(physical_qargs)) + || qargs_with_non_global_operation + .keys() + .flatten() + .any(|incomplete_qargs| { + let incomplete_qargs = HashSet::from_iter(incomplete_qargs.iter().copied()); + physical_qargs_as_set.is_superset(&incomplete_qargs) + }) + { + qargs_local_source_basis + .entry(Some(physical_qargs_as_set.into_iter().collect())) + .and_modify(|set| { + set.insert((node_obj.op.name().to_string(), node_obj.op.num_qubits())); + }) + .or_insert(HashSet::from_iter([( + node_obj.op.name().to_string(), + node_obj.op.num_qubits(), + )])); + } else { + source_basis.insert((node_obj.op.name().to_string(), node_obj.op.num_qubits())); + } + if node_obj.op.control_flow() { + let OperationRef::Instruction(op) = node_obj.op.view() else { + unreachable!("Control flow op is not a control flow op. But control_flow is `true`") + }; + let bound_inst = op.instruction.bind(py); + // Use python side extraction instead of the Rust method `op.blocks` due to + // required usage of a python-space method `QuantumCircuit.has_calibration_for`. + let blocks = bound_inst.getattr("blocks")?.iter()?; + for block in blocks { + extract_basis_target_circ( + &block?, + source_basis, + qargs_local_source_basis, + min_qubits, + qargs_with_non_global_operation, + )?; + } + } + } + Ok(()) +} + +/// Variant of extract_basis_target that takes an instance of QuantumCircuit. +/// This needs to use a Python instance of `QuantumCircuit` due to it needing +/// to access `has_calibration_for()` which is unavailable through rust. However, +/// this API will be removed with the deprecation of `Pulse`. +fn extract_basis_target_circ( + circuit: &Bound, + source_basis: &mut HashSet, + qargs_local_source_basis: &mut HashMap, HashSet>, + min_qubits: usize, + qargs_with_non_global_operation: &HashMap, HashSet>, +) -> PyResult<()> { + let py = circuit.py(); + let circ_data_bound = circuit.getattr("_data")?.downcast_into::()?; + let circ_data = circ_data_bound.borrow(); + for (index, node_obj) in circ_data.iter().enumerate() { + let qargs = circ_data.get_qargs(node_obj.qubits); + if circuit + .call_method1("_has_calibration_for", (circuit.get_item(index)?,))? + .is_truthy()? + || qargs.len() < min_qubits + { + continue; + } + // Treat the instruction as on an incomplete basis if the qargs are in the + // qargs_with_non_global_operation dictionary or if any of the qubits in qargs + // are a superset for a non-local operation. For example, if the qargs + // are (0, 1) and that's a global (ie no non-local operations on (0, 1) + // operation but there is a non-local operation on (1,) we need to + // do an extra non-local search for this op to ensure we include any + // single qubit operation for (1,) as valid. This pattern also holds + // true for > 2q ops too (so for 4q operations we need to check for 3q, 2q, + // and 1q operations in the same manner) + let physical_qargs: SmallVec<[PhysicalQubit; 2]> = + qargs.iter().map(|x| PhysicalQubit(x.0)).collect(); + let physical_qargs_as_set: HashSet = + HashSet::from_iter(physical_qargs.iter().copied()); + if qargs_with_non_global_operation.contains_key(&Some(physical_qargs)) + || qargs_with_non_global_operation + .keys() + .flatten() + .any(|incomplete_qargs| { + let incomplete_qargs = HashSet::from_iter(incomplete_qargs.iter().copied()); + physical_qargs_as_set.is_superset(&incomplete_qargs) + }) + { + qargs_local_source_basis + .entry(Some(physical_qargs_as_set.into_iter().collect())) + .and_modify(|set| { + set.insert((node_obj.op.name().to_string(), node_obj.op.num_qubits())); + }) + .or_insert(HashSet::from_iter([( + node_obj.op.name().to_string(), + node_obj.op.num_qubits(), + )])); + } else { + source_basis.insert((node_obj.op.name().to_string(), node_obj.op.num_qubits())); + } + if node_obj.op.control_flow() { + let OperationRef::Instruction(op) = node_obj.op.view() else { + unreachable!("Control flow op is not a control flow op. But control_flow is `true`") + }; + let bound_inst = op.instruction.bind(py); + let blocks = bound_inst.getattr("blocks")?.iter()?; + for block in blocks { + extract_basis_target_circ( + &block?, + source_basis, + qargs_local_source_basis, + min_qubits, + qargs_with_non_global_operation, + )?; + } + } + } + Ok(()) +} + +fn apply_translation( + py: Python, + dag: &DAGCircuit, + target_basis: &HashSet, + instr_map: &InstMap, + extra_inst_map: &ExtraInstructionMap, + min_qubits: usize, + qargs_with_non_global_operation: &HashMap, HashSet>, +) -> PyResult<(DAGCircuit, bool)> { + let mut is_updated = false; + let mut out_dag = dag.copy_empty_like(py, "alike")?; + for node in dag.topological_op_nodes()? { + let node_obj = dag.dag()[node].unwrap_operation(); + let node_qarg = dag.get_qargs(node_obj.qubits); + let node_carg = dag.get_cargs(node_obj.clbits); + let qubit_set: HashSet = HashSet::from_iter(node_qarg.iter().copied()); + let mut new_op: Option = None; + if target_basis.contains(node_obj.op.name()) || node_qarg.len() < min_qubits { + if node_obj.op.control_flow() { + let OperationRef::Instruction(control_op) = node_obj.op.view() else { + unreachable!("This instruction {} says it is of control flow type, but is not an Instruction instance", node_obj.op.name()) + }; + let mut flow_blocks = vec![]; + let bound_obj = control_op.instruction.bind(py); + let blocks = bound_obj.getattr("blocks")?; + for block in blocks.iter()? { + let block = block?; + let dag_block: DAGCircuit = + circuit_to_dag(py, block.extract()?, true, None, None)?; + let updated_dag: DAGCircuit; + (updated_dag, is_updated) = apply_translation( + py, + &dag_block, + target_basis, + instr_map, + extra_inst_map, + min_qubits, + qargs_with_non_global_operation, + )?; + let flow_circ_block = if is_updated { + DAG_TO_CIRCUIT + .get_bound(py) + .call1((updated_dag,))? + .extract()? + } else { + block + }; + flow_blocks.push(flow_circ_block); + } + let replaced_blocks = bound_obj.call_method1("replace_blocks", (flow_blocks,))?; + new_op = Some(replaced_blocks.extract()?); + } + if let Some(new_op) = new_op { + out_dag.apply_operation_back( + py, + new_op.operation, + node_qarg, + node_carg, + if new_op.params.is_empty() { + None + } else { + Some(new_op.params) + }, + new_op.extra_attrs, + #[cfg(feature = "cache_pygates")] + None, + )?; + } else { + out_dag.apply_operation_back( + py, + node_obj.op.clone(), + node_qarg, + node_carg, + if node_obj.params_view().is_empty() { + None + } else { + Some( + node_obj + .params_view() + .iter() + .map(|param| param.clone_ref(py)) + .collect(), + ) + }, + node_obj.extra_attrs.clone(), + #[cfg(feature = "cache_pygates")] + None, + )?; + } + continue; + } + let node_qarg_as_physical: Option = + Some(node_qarg.iter().map(|x| PhysicalQubit(x.0)).collect()); + if qargs_with_non_global_operation.contains_key(&node_qarg_as_physical) + && qargs_with_non_global_operation[&node_qarg_as_physical].contains(node_obj.op.name()) + { + out_dag.apply_operation_back( + py, + node_obj.op.clone(), + node_qarg, + node_carg, + if node_obj.params_view().is_empty() { + None + } else { + Some( + node_obj + .params_view() + .iter() + .map(|param| param.clone_ref(py)) + .collect(), + ) + }, + node_obj.extra_attrs.clone(), + #[cfg(feature = "cache_pygates")] + None, + )?; + continue; + } + + if dag.has_calibration_for_index(py, node)? { + out_dag.apply_operation_back( + py, + node_obj.op.clone(), + node_qarg, + node_carg, + if node_obj.params_view().is_empty() { + None + } else { + Some( + node_obj + .params_view() + .iter() + .map(|param| param.clone_ref(py)) + .collect(), + ) + }, + node_obj.extra_attrs.clone(), + #[cfg(feature = "cache_pygates")] + None, + )?; + continue; + } + let unique_qargs: Option = if qubit_set.is_empty() { + None + } else { + Some(qubit_set.iter().map(|x| PhysicalQubit(x.0)).collect()) + }; + if extra_inst_map.contains_key(&unique_qargs) { + replace_node( + py, + &mut out_dag, + node_obj.clone(), + &extra_inst_map[&unique_qargs], + )?; + } else if instr_map + .contains_key(&(node_obj.op.name().to_string(), node_obj.op.num_qubits())) + { + replace_node(py, &mut out_dag, node_obj.clone(), instr_map)?; + } else { + return Err(TranspilerError::new_err(format!( + "BasisTranslator did not map {}", + node_obj.op.name() + ))); + } + is_updated = true; + } + + Ok((out_dag, is_updated)) +} + +fn replace_node( + py: Python, + dag: &mut DAGCircuit, + node: PackedInstruction, + instr_map: &HashMap, DAGCircuit)>, +) -> PyResult<()> { + let (target_params, target_dag) = + &instr_map[&(node.op.name().to_string(), node.op.num_qubits())]; + if node.params_view().len() != target_params.len() { + return Err(TranspilerError::new_err(format!( + "Translation num_params not equal to op num_params. \ + Op: {:?} {} Translation: {:?}\n{:?}", + node.params_view(), + node.op.name(), + &target_params, + &target_dag + ))); + } + if node.params_view().is_empty() { + for inner_index in target_dag.topological_op_nodes()? { + let inner_node = &target_dag.dag()[inner_index].unwrap_operation(); + let old_qargs = dag.get_qargs(node.qubits); + let old_cargs = dag.get_cargs(node.clbits); + let new_qubits: Vec = target_dag + .get_qargs(inner_node.qubits) + .iter() + .map(|qubit| old_qargs[qubit.0 as usize]) + .collect(); + let new_clbits: Vec = target_dag + .get_cargs(inner_node.clbits) + .iter() + .map(|clbit| old_cargs[clbit.0 as usize]) + .collect(); + let new_op = if inner_node.op.try_standard_gate().is_none() { + inner_node.op.py_copy(py)? + } else { + inner_node.op.clone() + }; + if node.condition().is_some() { + match new_op.view() { + OperationRef::Gate(gate) => { + gate.gate.setattr(py, "condition", node.condition())? + } + OperationRef::Instruction(inst) => { + inst.instruction + .setattr(py, "condition", node.condition())? + } + OperationRef::Operation(oper) => { + oper.operation.setattr(py, "condition", node.condition())? + } + _ => (), + } + } + let new_params: SmallVec<[Param; 3]> = inner_node + .params_view() + .iter() + .map(|param| param.clone_ref(py)) + .collect(); + let new_extra_props = node.extra_attrs.clone(); + dag.apply_operation_back( + py, + new_op, + &new_qubits, + &new_clbits, + if new_params.is_empty() { + None + } else { + Some(new_params) + }, + new_extra_props, + #[cfg(feature = "cache_pygates")] + None, + )?; + } + dag.add_global_phase(py, target_dag.global_phase())?; + } else { + let parameter_map = target_params + .iter() + .zip(node.params_view()) + .into_py_dict_bound(py); + for inner_index in target_dag.topological_op_nodes()? { + let inner_node = &target_dag.dag()[inner_index].unwrap_operation(); + let old_qargs = dag.get_qargs(node.qubits); + let old_cargs = dag.get_cargs(node.clbits); + let new_qubits: Vec = target_dag + .get_qargs(inner_node.qubits) + .iter() + .map(|qubit| old_qargs[qubit.0 as usize]) + .collect(); + let new_clbits: Vec = target_dag + .get_cargs(inner_node.clbits) + .iter() + .map(|clbit| old_cargs[clbit.0 as usize]) + .collect(); + let new_op = if inner_node.op.try_standard_gate().is_none() { + inner_node.op.py_copy(py)? + } else { + inner_node.op.clone() + }; + let mut new_params: SmallVec<[Param; 3]> = inner_node + .params_view() + .iter() + .map(|param| param.clone_ref(py)) + .collect(); + if inner_node + .params_view() + .iter() + .any(|param| matches!(param, Param::ParameterExpression(_))) + { + new_params = SmallVec::new(); + for param in inner_node.params_view() { + if let Param::ParameterExpression(param_obj) = param { + let bound_param = param_obj.bind(py); + let exp_params = param.iter_parameters(py)?; + let bind_dict = PyDict::new_bound(py); + for key in exp_params { + let key = key?; + bind_dict.set_item(&key, parameter_map.get_item(&key)?)?; + } + let mut new_value: Bound; + let comparison = bind_dict.values().iter().any(|param| { + param + .is_instance(PARAMETER_EXPRESSION.get_bound(py)) + .is_ok_and(|x| x) + }); + if comparison { + new_value = bound_param.clone(); + for items in bind_dict.items() { + new_value = new_value.call_method1( + intern!(py, "assign"), + items.downcast::()?, + )?; + } + } else { + new_value = + bound_param.call_method1(intern!(py, "bind"), (&bind_dict,))?; + } + let eval = new_value.getattr(intern!(py, "parameters"))?; + if eval.is_empty()? { + new_value = new_value.call_method0(intern!(py, "numeric"))?; + } + new_params.push(new_value.extract()?); + } else { + new_params.push(param.clone_ref(py)); + } + } + if new_op.try_standard_gate().is_none() { + match new_op.view() { + OperationRef::Instruction(inst) => inst + .instruction + .bind(py) + .setattr("params", new_params.clone())?, + OperationRef::Gate(gate) => { + gate.gate.bind(py).setattr("params", new_params.clone())? + } + OperationRef::Operation(oper) => oper + .operation + .bind(py) + .setattr("params", new_params.clone())?, + _ => (), + } + } + } + dag.apply_operation_back( + py, + new_op, + &new_qubits, + &new_clbits, + if new_params.is_empty() { + None + } else { + Some(new_params) + }, + inner_node.extra_attrs.clone(), + #[cfg(feature = "cache_pygates")] + None, + )?; + } + + if let Param::ParameterExpression(old_phase) = target_dag.global_phase() { + let bound_old_phase = old_phase.bind(py); + let bind_dict = PyDict::new_bound(py); + for key in target_dag.global_phase().iter_parameters(py)? { + let key = key?; + bind_dict.set_item(&key, parameter_map.get_item(&key)?)?; + } + let mut new_phase: Bound; + if bind_dict.values().iter().any(|param| { + param + .is_instance(PARAMETER_EXPRESSION.get_bound(py)) + .is_ok_and(|x| x) + }) { + new_phase = bound_old_phase.clone(); + for key_val in bind_dict.items() { + new_phase = + new_phase.call_method1(intern!(py, "assign"), key_val.downcast()?)?; + } + } else { + new_phase = bound_old_phase.call_method1(intern!(py, "bind"), (bind_dict,))?; + } + if !new_phase.getattr(intern!(py, "parameters"))?.is_truthy()? { + new_phase = new_phase.call_method0(intern!(py, "numeric"))?; + if new_phase.is_instance(&PyComplex::type_object_bound(py))? { + return Err(TranspilerError::new_err(format!( + "Global phase must be real, but got {}", + new_phase.repr()? + ))); + } + } + let new_phase: Param = new_phase.extract()?; + dag.add_global_phase(py, &new_phase)?; + } + } + + Ok(()) +} + #[pymodule] pub fn basis_translator(m: &Bound) -> PyResult<()> { - m.add_wrapped(wrap_pyfunction!(basis_search::py_basis_search))?; - m.add_wrapped(wrap_pyfunction!(compose_transforms::py_compose_transforms))?; + m.add_wrapped(wrap_pyfunction!(run))?; Ok(()) } diff --git a/crates/accelerate/src/circuit_library/mod.rs b/crates/accelerate/src/circuit_library/mod.rs index 599d67c3e0b6..2e087b442a9f 100644 --- a/crates/accelerate/src/circuit_library/mod.rs +++ b/crates/accelerate/src/circuit_library/mod.rs @@ -17,10 +17,12 @@ mod entanglement; mod iqp; mod multi_local; mod parameter_ledger; +mod pauli_evolution; mod pauli_feature_map; mod quantum_volume; pub fn circuit_library(m: &Bound) -> PyResult<()> { + m.add_wrapped(wrap_pyfunction!(pauli_evolution::py_pauli_evolution))?; m.add_wrapped(wrap_pyfunction!(pauli_feature_map::pauli_feature_map))?; m.add_wrapped(wrap_pyfunction!(entanglement::get_entangler_map))?; m.add_wrapped(wrap_pyfunction!(iqp::py_iqp))?; diff --git a/crates/accelerate/src/circuit_library/pauli_evolution.rs b/crates/accelerate/src/circuit_library/pauli_evolution.rs new file mode 100644 index 000000000000..be41b51347bf --- /dev/null +++ b/crates/accelerate/src/circuit_library/pauli_evolution.rs @@ -0,0 +1,356 @@ +// 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::*; +use pyo3::types::{PyList, PyString, PyTuple}; +use qiskit_circuit::circuit_data::CircuitData; +use qiskit_circuit::operations::{multiply_param, radd_param, Param, PyInstruction, StandardGate}; +use qiskit_circuit::packed_instruction::PackedOperation; +use qiskit_circuit::{imports, Clbit, Qubit}; +use smallvec::{smallvec, SmallVec}; + +// custom types for a more readable code +type StandardInstruction = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); +type Instruction = ( + PackedOperation, + SmallVec<[Param; 3]>, + Vec, + Vec, +); + +/// Return instructions (using only StandardGate operations) to implement a Pauli evolution +/// of a given Pauli string over a given time (as Param). +/// +/// Args: +/// pauli: The Pauli string, e.g. "IXYZ". +/// indices: The qubit indices the Pauli acts on, e.g. if given as [0, 1, 2, 3] with the +/// Pauli "IXYZ", then the correspondence is I_0 X_1 Y_2 Z_3. +/// time: The rotation angle. Note that this will directly be used as input of the +/// rotation gate and not be multiplied by a factor of 2 (that should be done before so +/// that this function can remain Rust-only). +/// phase_gate: If ``true``, use the ``PhaseGate`` instead of ``RZGate`` as single-qubit rotation. +/// do_fountain: If ``true``, implement the CX propagation as "fountain" shape, where each +/// CX uses the top qubit as target. If ``false``, uses a "chain" shape, where CX in between +/// neighboring qubits are used. +/// +/// Returns: +/// A pointer to an iterator over standard instructions. +pub fn pauli_evolution<'a>( + pauli: &'a str, + indices: Vec, + time: Param, + phase_gate: bool, + do_fountain: bool, +) -> Box + 'a> { + // ensure the Pauli has no identity terms + let binding = pauli.to_lowercase(); // lowercase for convenience + let active = binding + .as_str() + .chars() + .zip(indices) + .filter(|(pauli, _)| *pauli != 'i'); + let (paulis, indices): (Vec, Vec) = active.unzip(); + + match (phase_gate, indices.len()) { + (_, 0) => Box::new(std::iter::empty()), + (false, 1) => Box::new(single_qubit_evolution(paulis[0], indices[0], time)), + (false, 2) => two_qubit_evolution(paulis, indices, time), + _ => Box::new(multi_qubit_evolution( + paulis, + indices, + time, + phase_gate, + do_fountain, + )), + } +} + +/// Implement a single-qubit Pauli evolution of a Pauli given as char, on a given index and +/// for given time. Note that the time here equals the angle of the rotation and is not +/// multiplied by a factor of 2. +fn single_qubit_evolution( + pauli: char, + index: u32, + time: Param, +) -> impl Iterator { + let qubit: SmallVec<[Qubit; 2]> = smallvec![Qubit(index)]; + let param: SmallVec<[Param; 3]> = smallvec![time]; + + std::iter::once(match pauli { + 'x' => (StandardGate::RXGate, param, qubit), + 'y' => (StandardGate::RYGate, param, qubit), + 'z' => (StandardGate::RZGate, param, qubit), + _ => unreachable!("Unsupported Pauli, at this point we expected one of x, y, z."), + }) +} + +/// Implement a 2-qubit Pauli evolution of a Pauli string, on a given indices and +/// for given time. Note that the time here equals the angle of the rotation and is not +/// multiplied by a factor of 2. +/// +/// If possible, Qiskit's native 2-qubit Pauli rotations are used. Otherwise, the general +/// multi-qubit evolution is called. +fn two_qubit_evolution<'a>( + pauli: Vec, + indices: Vec, + time: Param, +) -> Box + 'a> { + let qubits: SmallVec<[Qubit; 2]> = smallvec![Qubit(indices[0]), Qubit(indices[1])]; + let param: SmallVec<[Param; 3]> = smallvec![time.clone()]; + let paulistring: String = pauli.iter().collect(); + + match paulistring.as_str() { + "xx" => Box::new(std::iter::once((StandardGate::RXXGate, param, qubits))), + "zx" => Box::new(std::iter::once((StandardGate::RZXGate, param, qubits))), + "yy" => Box::new(std::iter::once((StandardGate::RYYGate, param, qubits))), + "zz" => Box::new(std::iter::once((StandardGate::RZZGate, param, qubits))), + // Note: the CX modes (do_fountain=true/false) give the same circuit for a 2-qubit + // Pauli, so we just set it to false here + _ => Box::new(multi_qubit_evolution(pauli, indices, time, false, false)), + } +} + +/// Implement a multi-qubit Pauli evolution. See ``pauli_evolution`` detailed docs. +fn multi_qubit_evolution( + pauli: Vec, + indices: Vec, + time: Param, + phase_gate: bool, + do_fountain: bool, +) -> impl Iterator { + let active_paulis: Vec<(char, Qubit)> = pauli + .into_iter() + .zip(indices.into_iter().map(Qubit)) + .collect(); + + // get the basis change: x -> HGate, y -> SXdgGate, z -> nothing + let basis_change: Vec = active_paulis + .iter() + .filter(|(p, _)| *p != 'z') + .map(|(p, q)| match p { + 'x' => (StandardGate::HGate, smallvec![], smallvec![*q]), + 'y' => (StandardGate::SXGate, smallvec![], smallvec![*q]), + _ => unreachable!("Invalid Pauli string."), // "z" and "i" have been filtered out + }) + .collect(); + + // get the inverse basis change + let inverse_basis_change: Vec = basis_change + .iter() + .map(|(gate, _, qubit)| match gate { + StandardGate::HGate => (StandardGate::HGate, smallvec![], qubit.clone()), + StandardGate::SXGate => (StandardGate::SXdgGate, smallvec![], qubit.clone()), + _ => unreachable!("Invalid basis-changing Clifford."), + }) + .collect(); + + // get the CX propagation up to the first qubit, and down + let (chain_up, chain_down) = match do_fountain { + true => ( + cx_fountain(active_paulis.clone()), + cx_fountain(active_paulis.clone()).rev(), + ), + false => ( + cx_chain(active_paulis.clone()), + cx_chain(active_paulis.clone()).rev(), + ), + }; + + // get the RZ gate on the first qubit + let first_qubit = active_paulis.first().unwrap().1; + let z_rotation = std::iter::once(( + if phase_gate { + StandardGate::PhaseGate + } else { + StandardGate::RZGate + }, + smallvec![time], + smallvec![first_qubit], + )); + + // and finally chain everything together + basis_change + .into_iter() + .chain(chain_down) + .chain(z_rotation) + .chain(chain_up) + .chain(inverse_basis_change) +} + +/// Implement a Pauli evolution circuit. +/// +/// The Pauli evolution is implemented as a basis transformation to the Pauli-Z basis, +/// followed by a CX-chain and then a single Pauli-Z rotation on the last qubit. Then the CX-chain +/// is uncomputed and the inverse basis transformation applied. E.g. for the evolution under the +/// Pauli string XIYZ we have the circuit +/// ┌───┐┌───────┐┌───┐ +/// 0: ─────────────┤ X ├┤ Rz(2) ├┤ X ├─────────── +/// ┌──────┐┌───┐└─┬─┘└───────┘└─┬─┘┌───┐┌────┐ +/// 1: ┤ √Xdg ├┤ X ├──■─────────────■──┤ X ├┤ √X ├ +/// └──────┘└─┬─┘ └─┬─┘└────┘ +/// 2: ──────────┼───────────────────────┼──────── +/// ┌───┐ │ │ ┌───┐ +/// 3: ─┤ H ├────■───────────────────────■──┤ H ├─ +/// └───┘ └───┘ +/// +/// Args: +/// num_qubits: The number of qubits in the Hamiltonian. +/// sparse_paulis: The Paulis to implement. Given in a sparse-list format with elements +/// ``(pauli_string, qubit_indices, coefficient)``. An element of the form +/// ``("IXYZ", [0,1,2,3], 0.2)``, for example, is interpreted in terms of qubit indices as +/// I_q0 X_q1 Y_q2 Z_q3 and will use a RZ rotation angle of 0.4. +/// insert_barriers: If ``true``, insert a barrier in between the evolution of individual +/// Pauli terms. +/// do_fountain: If ``true``, implement the CX propagation as "fountain" shape, where each +/// CX uses the top qubit as target. If ``false``, uses a "chain" shape, where CX in between +/// neighboring qubits are used. +/// +/// Returns: +/// Circuit data for to implement the evolution. +#[pyfunction] +#[pyo3(name = "pauli_evolution", signature = (num_qubits, sparse_paulis, insert_barriers=false, do_fountain=false))] +pub fn py_pauli_evolution( + num_qubits: i64, + sparse_paulis: &Bound, + insert_barriers: bool, + do_fountain: bool, +) -> PyResult { + let py = sparse_paulis.py(); + let num_paulis = sparse_paulis.len(); + let mut paulis: Vec = Vec::with_capacity(num_paulis); + let mut indices: Vec> = Vec::with_capacity(num_paulis); + let mut times: Vec = Vec::with_capacity(num_paulis); + let mut global_phase = Param::Float(0.0); + let mut modified_phase = false; // keep track of whether we modified the phase + + for el in sparse_paulis.iter() { + let tuple = el.downcast::()?; + let pauli = tuple.get_item(0)?.downcast::()?.to_string(); + let time = Param::extract_no_coerce(&tuple.get_item(2)?)?; + + if pauli.as_str().chars().all(|p| p == 'i') { + global_phase = radd_param(global_phase, time, py); + modified_phase = true; + continue; + } + + paulis.push(pauli); + times.push(time); // note we do not multiply by 2 here, this is done Python side! + indices.push(tuple.get_item(1)?.extract::>()?) + } + + let barrier = get_barrier(py, num_qubits as u32); + + let evos = paulis.iter().enumerate().zip(indices).zip(times).flat_map( + |(((i, pauli), qubits), time)| { + let as_packed = pauli_evolution(pauli, qubits, time, false, do_fountain).map( + |(gate, params, qubits)| -> PyResult { + Ok(( + gate.into(), + params, + Vec::from_iter(qubits.into_iter()), + Vec::new(), + )) + }, + ); + + // this creates an iterator containing a barrier only if required, otherwise it is empty + let maybe_barrier = (insert_barriers && i < (num_paulis - 1)) + .then_some(Ok(barrier.clone())) + .into_iter(); + as_packed.chain(maybe_barrier) + }, + ); + + // When handling all-identity Paulis above, we added the time as global phase. + // However, the all-identity Paulis should add a negative phase, as they implement + // exp(-i t I). We apply the negative sign here, to only do a single (-1) multiplication, + // instead of doing it every time we find an all-identity Pauli. + if modified_phase { + global_phase = multiply_param(&global_phase, -1.0, py); + } + + CircuitData::from_packed_operations(py, num_qubits as u32, 0, evos, global_phase) +} + +/// Build a CX chain over the active qubits. E.g. with q_1 inactive, this would return +/// +/// ┌───┐ +/// q_0: ──────────┤ X ├ +/// └─┬─┘ +/// q_1: ────────────┼── +/// ┌───┐ │ +/// q_2: ─────┤ X ├──■── +/// ┌───┐└─┬─┘ +/// q_3: ┤ X ├──■─────── +/// └─┬─┘ +/// q_4: ──■──────────── +/// +fn cx_chain( + active_paulis: Vec<(char, Qubit)>, +) -> Box> { + let num_terms = active_paulis.len(); + Box::new( + (0..num_terms - 1) + .map(move |i| (active_paulis[i].1, active_paulis[i + 1].1)) + .map(|(target, ctrl)| (StandardGate::CXGate, smallvec![], smallvec![ctrl, target])), + ) +} + +/// Build a CX fountain over the active qubits. E.g. with q_1 inactive, this would return +/// +//// ┌───┐┌───┐┌───┐ +//// q_0: ┤ X ├┤ X ├┤ X ├ +//// └─┬─┘└─┬─┘└─┬─┘ +//// q_1: ──┼────┼────┼── +//// │ │ │ +//// q_2: ──■────┼────┼── +//// │ │ +//// q_3: ───────■────┼── +//// │ +//// q_4: ────────────■── +/// +fn cx_fountain( + active_paulis: Vec<(char, Qubit)>, +) -> Box> { + let num_terms = active_paulis.len(); + let first_qubit = active_paulis[0].1; + Box::new((1..num_terms).rev().map(move |i| { + let ctrl = active_paulis[i].1; + ( + StandardGate::CXGate, + smallvec![], + smallvec![ctrl, first_qubit], + ) + })) +} + +fn get_barrier(py: Python, num_qubits: u32) -> Instruction { + let barrier_cls = imports::BARRIER.get_bound(py); + let barrier = barrier_cls + .call1((num_qubits,)) + .expect("Could not create Barrier Python-side"); + let barrier_inst = PyInstruction { + qubits: num_qubits, + clbits: 0, + params: 0, + op_name: "barrier".to_string(), + control_flow: false, + instruction: barrier.into(), + }; + ( + barrier_inst.into(), + smallvec![], + (0..num_qubits).map(Qubit).collect(), + vec![], + ) +} diff --git a/crates/accelerate/src/circuit_library/pauli_feature_map.rs b/crates/accelerate/src/circuit_library/pauli_feature_map.rs index 6fa88d187182..bb9f8c25eb24 100644 --- a/crates/accelerate/src/circuit_library/pauli_feature_map.rs +++ b/crates/accelerate/src/circuit_library/pauli_feature_map.rs @@ -10,7 +10,6 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -use itertools::Itertools; use pyo3::prelude::*; use pyo3::types::PySequence; use pyo3::types::PyString; @@ -24,106 +23,15 @@ use smallvec::{smallvec, SmallVec}; use std::f64::consts::PI; use crate::circuit_library::entanglement; +use crate::circuit_library::pauli_evolution; use crate::QiskitError; -// custom math and types for a more readable code -const PI2: f64 = PI / 2.; type Instruction = ( PackedOperation, SmallVec<[Param; 3]>, Vec, Vec, ); -type StandardInstruction = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); - -/// Return instructions (using only StandardGate operations) to implement a Pauli evolution -/// of a given Pauli string over a given time (as Param). -/// -/// The Pauli evolution is implemented as a basis transformation to the Pauli-Z basis, -/// followed by a CX-chain and then a single Pauli-Z rotation on the last qubit. Then the CX-chain -/// is uncomputed and the inverse basis transformation applied. E.g. for the evolution under the -/// Pauli string XIYZ we have the circuit -/// ┌───┐┌───────┐┌───┐ -/// 0: ─────────────────┤ X ├┤ Rz(2) ├┤ X ├────────────────── -/// ┌──────────┐┌───┐└─┬─┘└───────┘└─┬─┘┌───┐┌───────────┐ -/// 1: ┤ Rx(pi/2) ├┤ X ├──■─────────────■──┤ X ├┤ Rx(-pi/2) ├ -/// └──────────┘└─┬─┘ └─┬─┘└───────────┘ -/// 2: ──────────────┼───────────────────────┼─────────────── -/// ┌───┐ │ │ ┌───┐ -/// 3: ─┤ H ├────────■───────────────────────■──┤ H ├──────── -/// └───┘ └───┘ -fn pauli_evolution( - pauli: &str, - indices: Vec, - time: Param, -) -> impl Iterator + '_ { - // Get pairs of (pauli, qubit) that are active, i.e. that are not the identity. Note that - // the rest of the code also works if there are only identities, in which case we will - // effectively return an empty iterator. - let qubits = indices.iter().map(|i| Qubit(*i)).collect_vec(); - let binding = pauli.to_lowercase(); // lowercase for convenience - let active_paulis = binding - .as_str() - .chars() - .rev() // reverse due to Qiskit's bit ordering convention - .zip(qubits) - .filter(|(p, _)| *p != 'i') - .collect_vec(); - - // get the basis change: x -> HGate, y -> RXGate(pi/2), z -> nothing - let basis_change = active_paulis - .clone() - .into_iter() - .filter(|(p, _)| *p != 'z') - .map(|(p, q)| match p { - 'x' => (StandardGate::HGate, smallvec![], smallvec![q]), - 'y' => ( - StandardGate::RXGate, - smallvec![Param::Float(PI2)], - smallvec![q], - ), - _ => unreachable!("Invalid Pauli string."), // "z" and "i" have been filtered out - }); - - // get the inverse basis change - let inverse_basis_change = basis_change.clone().map(|(gate, _, qubit)| match gate { - StandardGate::HGate => (gate, smallvec![], qubit), - StandardGate::RXGate => (gate, smallvec![Param::Float(-PI2)], qubit), - _ => unreachable!(), - }); - - // get the CX chain down to the target rotation qubit - let chain_down = active_paulis - .clone() - .into_iter() - .map(|(_, q)| q) - .tuple_windows() // iterates over (q[i], q[i+1]) windows - .map(|(ctrl, target)| (StandardGate::CXGate, smallvec![], smallvec![ctrl, target])); - - // get the CX chain up (cannot use chain_down.rev since tuple_windows is not double ended) - let chain_up = active_paulis - .clone() - .into_iter() - .rev() - .map(|(_, q)| q) - .tuple_windows() - .map(|(target, ctrl)| (StandardGate::CXGate, smallvec![], smallvec![ctrl, target])); - - // get the RZ gate on the last qubit - let last_qubit = active_paulis.last().unwrap().1; - let z_rotation = std::iter::once(( - StandardGate::PhaseGate, - smallvec![time], - smallvec![last_qubit], - )); - - // and finally chain everything together - basis_change - .chain(chain_down) - .chain(z_rotation) - .chain(chain_up) - .chain(inverse_basis_change) -} /// Build a Pauli feature map circuit. /// @@ -263,11 +171,17 @@ fn _get_evolution_layer<'a>( // to call CircuitData::from_packed_operations. This is needed since we might // have to interject barriers, which are not a standard gate and prevents us // from using CircuitData::from_standard_gates. - let evo = pauli_evolution(pauli, indices.clone(), multiply_param(&angle, alpha, py)) - .map(|(gate, params, qargs)| { - (gate.into(), params, qargs.to_vec(), vec![] as Vec) - }) - .collect::>(); + let evo = pauli_evolution::pauli_evolution( + pauli, + indices.into_iter().rev().collect(), + multiply_param(&angle, alpha, py), + true, + false, + ) + .map(|(gate, params, qargs)| { + (gate.into(), params, qargs.to_vec(), vec![] as Vec) + }) + .collect::>(); insts.extend(evo); } } diff --git a/crates/accelerate/src/gate_direction.rs b/crates/accelerate/src/gate_direction.rs old mode 100644 new mode 100755 index 3143a11a22a2..a20dfea00535 --- a/crates/accelerate/src/gate_direction.rs +++ b/crates/accelerate/src/gate_direction.rs @@ -11,18 +11,35 @@ // that they have been altered from the originals. use crate::nlayout::PhysicalQubit; +use crate::target_transpiler::exceptions::TranspilerError; use crate::target_transpiler::Target; use hashbrown::HashSet; +use pyo3::intern; use pyo3::prelude::*; -use qiskit_circuit::imports; +use pyo3::types::PyTuple; use qiskit_circuit::operations::OperationRef; +use qiskit_circuit::packed_instruction::PackedOperation; use qiskit_circuit::{ + circuit_instruction::CircuitInstruction, + circuit_instruction::ExtraInstructionAttributes, + converters::{circuit_to_dag, QuantumCircuitData}, dag_circuit::{DAGCircuit, NodeType}, + dag_node::{DAGNode, DAGOpNode}, + imports, + imports::get_std_gate_class, operations::Operation, + operations::Param, + operations::StandardGate, packed_instruction::PackedInstruction, Qubit, }; -use smallvec::smallvec; +use rustworkx_core::petgraph::stable_graph::NodeIndex; +use smallvec::{smallvec, SmallVec}; +use std::f64::consts::PI; + +//######################################################################### +// CheckGateDirection analysis pass functions +//######################################################################### /// Check if the two-qubit gates follow the right direction with respect to the coupling map. /// @@ -35,7 +52,7 @@ use smallvec::smallvec; /// true iff all two-qubit gates comply with the coupling constraints #[pyfunction] #[pyo3(name = "check_gate_direction_coupling")] -fn py_check_with_coupling_map( +fn py_check_direction_coupling_map( py: Python, dag: &DAGCircuit, coupling_edges: HashSet<[Qubit; 2]>, @@ -57,7 +74,7 @@ fn py_check_with_coupling_map( /// true iff all two-qubit gates comply with the target's coupling constraints #[pyfunction] #[pyo3(name = "check_gate_direction_target")] -fn py_check_with_target(py: Python, dag: &DAGCircuit, target: &Target) -> PyResult { +fn py_check_direction_target(py: Python, dag: &DAGCircuit, target: &Target) -> PyResult { let target_check = |inst: &PackedInstruction, op_args: &[Qubit]| -> bool { let qargs = smallvec![ PhysicalQubit::new(op_args[0].0), @@ -97,7 +114,7 @@ where if let OperationRef::Instruction(py_inst) = packed_inst.op.view() { if py_inst.control_flow() { - let circuit_to_dag = imports::CIRCUIT_TO_DAG.get_bound(py); // TODO: Take out of the recursion + let circuit_to_dag = imports::CIRCUIT_TO_DAG.get_bound(py); let py_inst = py_inst.instruction.bind(py); for block in py_inst.getattr("blocks")?.iter()? { @@ -142,8 +159,479 @@ where Ok(true) } +//######################################################################### +// GateDirection transformation pass functions +//######################################################################### + +/// Try to swap two-qubit gate directions using pre-defined mapping to follow the right direction with respect to the coupling map. +/// +/// Args: +/// dag: the DAGCircuit to analyze +/// +/// coupling_edges: set of edge pairs representing a directed coupling map, against which gate directionality is checked +/// +/// Returns: +/// the transformed DAGCircuit +#[pyfunction] +#[pyo3(name = "fix_gate_direction_coupling")] +fn py_fix_direction_coupling_map( + py: Python, + dag: &mut DAGCircuit, + coupling_edges: HashSet<[Qubit; 2]>, +) -> PyResult { + if coupling_edges.is_empty() { + return Ok(dag.clone()); + } + + let coupling_map_check = + |_: &PackedInstruction, op_args: &[Qubit]| -> bool { coupling_edges.contains(op_args) }; + + fix_gate_direction(py, dag, &coupling_map_check, None).cloned() +} + +/// Try to swap two-qubit gate directions using pre-defined mapping to follow the right direction with respect to the given target. +/// +/// Args: +/// dag: the DAGCircuit to analyze +/// +/// coupling_edges: set of edge pairs representing a directed coupling map, against which gate directionality is checked +/// +/// Returns: +/// the transformed DAGCircuit +#[pyfunction] +#[pyo3(name = "fix_gate_direction_target")] +fn py_fix_direction_target( + py: Python, + dag: &mut DAGCircuit, + target: &Target, +) -> PyResult { + let target_check = |inst: &PackedInstruction, op_args: &[Qubit]| -> bool { + let qargs = smallvec![ + PhysicalQubit::new(op_args[0].0), + PhysicalQubit::new(op_args[1].0) + ]; + + // Take this path so Target can check for exact match of the parameterized gate's angle + if let OperationRef::Standard(std_gate) = inst.op.view() { + match std_gate { + StandardGate::RXXGate + | StandardGate::RYYGate + | StandardGate::RZZGate + | StandardGate::RZXGate => { + return target + .py_instruction_supported( + py, + None, + Some(qargs), + Some( + get_std_gate_class(py, std_gate) + .expect("These gates should have Python classes") + .bind(py), + ), + Some(inst.params_view().to_vec()), + ) + .unwrap_or(false) + } + _ => {} + } + } + target.instruction_supported(inst.op.name(), Some(&qargs)) + }; + + fix_gate_direction(py, dag, &target_check, None).cloned() +} + +// The main routine for fixing gate direction. Same parameters are check_gate_direction +fn fix_gate_direction<'a, T>( + py: Python, + dag: &'a mut DAGCircuit, + gate_complies: &T, + qubit_mapping: Option<&[Qubit]>, +) -> PyResult<&'a DAGCircuit> +where + T: Fn(&PackedInstruction, &[Qubit]) -> bool, +{ + let mut nodes_to_replace: Vec<(NodeIndex, DAGCircuit)> = Vec::new(); + let mut ops_to_replace: Vec<(NodeIndex, Vec>)> = Vec::new(); + + for node in dag.op_nodes(false) { + let packed_inst = dag.dag()[node].unwrap_operation(); + + let op_args = dag.get_qargs(packed_inst.qubits); + + if let OperationRef::Instruction(py_inst) = packed_inst.op.view() { + if py_inst.control_flow() { + let dag_to_circuit = imports::DAG_TO_CIRCUIT.get_bound(py); + + let blocks = py_inst.instruction.bind(py).getattr("blocks")?; + let blocks = blocks.downcast::()?; + + let mut blocks_to_replace = Vec::with_capacity(blocks.len()); + for block in blocks { + let mut inner_dag = circuit_to_dag( + py, + QuantumCircuitData::extract_bound(&block)?, + false, + None, + None, + )?; + + let inner_dag = if let Some(mapping) = qubit_mapping { + let mapping = op_args // Create a temp mapping for the recursive call + .iter() + .map(|q| mapping[q.index()]) + .collect::>(); + + fix_gate_direction(py, &mut inner_dag, gate_complies, Some(&mapping))? + } else { + fix_gate_direction(py, &mut inner_dag, gate_complies, Some(op_args))? + }; + + let circuit = dag_to_circuit.call1((inner_dag.clone(),))?; + blocks_to_replace.push(circuit); + } + + // Store this for replacement outside the dag.op_nodes loop + ops_to_replace.push((node, blocks_to_replace)); + + continue; + } + } + + if op_args.len() != 2 || dag.has_calibration_for_index(py, node)? { + continue; + }; + + // Take into account qubit index mapping if we're inside a control-flow block + let (op_args0, op_args1) = if let Some(mapping) = qubit_mapping { + (mapping[op_args[0].index()], mapping[op_args[1].index()]) + } else { + (op_args[0], op_args[1]) + }; + + if gate_complies(packed_inst, &[op_args0, op_args1]) { + continue; + } + + // If the op has a pre-defined replacement - replace if the other direction is supported otherwise error + // If no pre-defined replacement for the op - if the other direction is supported error saying no pre-defined rule otherwise error saying op is not supported + if let OperationRef::Standard(std_gate) = packed_inst.op.view() { + match std_gate { + StandardGate::CXGate + | StandardGate::ECRGate + | StandardGate::CZGate + | StandardGate::SwapGate + | StandardGate::RXXGate + | StandardGate::RYYGate + | StandardGate::RZZGate + | StandardGate::RZXGate => { + if gate_complies(packed_inst, &[op_args1, op_args0]) { + // Store this for replacement outside the dag.op_nodes loop + nodes_to_replace.push((node, replace_dag(py, std_gate, packed_inst)?)); + continue; + } else { + return Err(TranspilerError::new_err(format!( + "The circuit requires a connection between physical qubits {:?} for {}", + op_args, + packed_inst.op.name() + ))); + } + } + _ => {} + } + } + // No matching replacement found + if gate_complies(packed_inst, &[op_args1, op_args0]) + || has_calibration_for_op_node(py, dag, packed_inst, &[op_args1, op_args0])? + { + return Err(TranspilerError::new_err(format!("{} would be supported on {:?} if the direction was swapped, but no rules are known to do that. {:?} can be automatically flipped.", packed_inst.op.name(), op_args, vec!["cx", "cz", "ecr", "swap", "rzx", "rxx", "ryy", "rzz"]))); + // NOTE: Make sure to update the list of the supported gates if adding more replacements + } else { + return Err(TranspilerError::new_err(format!( + "{} with parameters {:?} is not supported on qubits {:?} in either direction.", + packed_inst.op.name(), + packed_inst.params_view(), + op_args + ))); + } + } + + for (node, op_blocks) in ops_to_replace { + let packed_inst = dag.dag()[node].unwrap_operation(); + let OperationRef::Instruction(py_inst) = packed_inst.op.view() else { + panic!("PyInstruction is expected"); + }; + let new_op = py_inst + .instruction + .bind(py) + .call_method1("replace_blocks", (op_blocks,))?; + + dag.py_substitute_node(dag.get_node(py, node)?.bind(py), &new_op, false, false)?; + } + + for (node, replacemanet_dag) in nodes_to_replace { + dag.py_substitute_node_with_dag( + py, + dag.get_node(py, node)?.bind(py), + &replacemanet_dag, + None, + true, + )?; + } + + Ok(dag) +} + +// Check whether the dag as calibration for a DAGOpNode +fn has_calibration_for_op_node( + py: Python, + dag: &DAGCircuit, + packed_inst: &PackedInstruction, + qargs: &[Qubit], +) -> PyResult { + let py_args = PyTuple::new_bound(py, dag.qubits().map_indices(qargs)); + + let dag_op_node = Py::new( + py, + ( + DAGOpNode { + instruction: CircuitInstruction { + operation: packed_inst.op.clone(), + qubits: py_args.unbind(), + clbits: PyTuple::empty_bound(py).unbind(), + params: packed_inst.params_view().iter().cloned().collect(), + extra_attrs: packed_inst.extra_attrs.clone(), + #[cfg(feature = "cache_pygates")] + py_op: packed_inst.py_op.clone(), + }, + sort_key: "".into_py(py), + }, + DAGNode { node: None }, + ), + )?; + + dag.has_calibration_for(py, dag_op_node.borrow(py)) +} + +// Return a replacement DAG for the given standard gate in the supported list +// TODO: optimize it by caching the DAGs of the non-parametric gates and caching and +// mutating upon request the DAGs of the parametric gates +fn replace_dag( + py: Python, + std_gate: StandardGate, + inst: &PackedInstruction, +) -> PyResult { + let replacement_dag = match std_gate { + StandardGate::CXGate => cx_replacement_dag(py), + StandardGate::ECRGate => ecr_replacement_dag(py), + StandardGate::CZGate => cz_replacement_dag(py), + StandardGate::SwapGate => swap_replacement_dag(py), + StandardGate::RXXGate => rxx_replacement_dag(py, inst.params_view()), + StandardGate::RYYGate => ryy_replacement_dag(py, inst.params_view()), + StandardGate::RZZGate => rzz_replacement_dag(py, inst.params_view()), + StandardGate::RZXGate => rzx_replacement_dag(py, inst.params_view()), + _ => panic!("Mismatch in supported gates assumption"), + }; + + replacement_dag +} + +//################################################### +// Utility functions to build the replacement dags +// +// TODO: replace this once we have a Rust version of QuantumRegister +#[inline] +fn add_qreg(py: Python, dag: &mut DAGCircuit, num_qubits: u32) -> PyResult> { + let qreg = imports::QUANTUM_REGISTER + .get_bound(py) + .call1((num_qubits,))?; + dag.add_qreg(py, &qreg)?; + let mut qargs = Vec::new(); + + for i in 0..num_qubits { + let qubit = qreg.call_method1(intern!(py, "__getitem__"), (i,))?; + qargs.push( + dag.qubits() + .find(&qubit) + .expect("Qubit should have been stored in the DAGCircuit"), + ); + } + + Ok(qargs) +} + +#[inline] +fn apply_operation_back( + py: Python, + dag: &mut DAGCircuit, + gate: StandardGate, + qargs: &[Qubit], + param: Option>, +) -> PyResult<()> { + dag.apply_operation_back( + py, + PackedOperation::from_standard(gate), + qargs, + &[], + param, + ExtraInstructionAttributes::default(), + #[cfg(feature = "cache_pygates")] + None, + )?; + + Ok(()) +} + +fn cx_replacement_dag(py: Python) -> PyResult { + let new_dag = &mut DAGCircuit::new(py)?; + let qargs = add_qreg(py, new_dag, 2)?; + let qargs = qargs.as_slice(); + + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[0]], None)?; + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[1]], None)?; + apply_operation_back( + py, + new_dag, + StandardGate::CXGate, + &[qargs[1], qargs[0]], + None, + )?; + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[0]], None)?; + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[1]], None)?; + + Ok(new_dag.clone()) +} + +fn ecr_replacement_dag(py: Python) -> PyResult { + let new_dag = &mut DAGCircuit::new(py)?; + new_dag.add_global_phase(py, &Param::Float(-PI / 2.0))?; + let qargs = add_qreg(py, new_dag, 2)?; + let qargs = qargs.as_slice(); + + apply_operation_back(py, new_dag, StandardGate::SGate, &[qargs[0]], None)?; + apply_operation_back(py, new_dag, StandardGate::SXGate, &[qargs[0]], None)?; + apply_operation_back(py, new_dag, StandardGate::SdgGate, &[qargs[0]], None)?; + apply_operation_back(py, new_dag, StandardGate::SdgGate, &[qargs[1]], None)?; + apply_operation_back(py, new_dag, StandardGate::SXGate, &[qargs[1]], None)?; + apply_operation_back(py, new_dag, StandardGate::SGate, &[qargs[1]], None)?; + apply_operation_back( + py, + new_dag, + StandardGate::ECRGate, + &[qargs[1], qargs[0]], + None, + )?; + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[0]], None)?; + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[1]], None)?; + + Ok(new_dag.clone()) +} + +fn cz_replacement_dag(py: Python) -> PyResult { + let new_dag = &mut DAGCircuit::new(py)?; + let qargs = add_qreg(py, new_dag, 2)?; + let qargs = qargs.as_slice(); + + apply_operation_back( + py, + new_dag, + StandardGate::CZGate, + &[qargs[1], qargs[0]], + None, + )?; + + Ok(new_dag.clone()) +} + +fn swap_replacement_dag(py: Python) -> PyResult { + let new_dag = &mut DAGCircuit::new(py)?; + let qargs = add_qreg(py, new_dag, 2)?; + let qargs = qargs.as_slice(); + + apply_operation_back( + py, + new_dag, + StandardGate::SwapGate, + &[qargs[1], qargs[0]], + None, + )?; + + Ok(new_dag.clone()) +} + +fn rxx_replacement_dag(py: Python, param: &[Param]) -> PyResult { + let new_dag = &mut DAGCircuit::new(py)?; + let qargs = add_qreg(py, new_dag, 2)?; + let qargs = qargs.as_slice(); + + apply_operation_back( + py, + new_dag, + StandardGate::RXXGate, + &[qargs[1], qargs[0]], + Some(SmallVec::from(param)), + )?; + + Ok(new_dag.clone()) +} + +fn ryy_replacement_dag(py: Python, param: &[Param]) -> PyResult { + let new_dag = &mut DAGCircuit::new(py)?; + let qargs = add_qreg(py, new_dag, 2)?; + let qargs = qargs.as_slice(); + + apply_operation_back( + py, + new_dag, + StandardGate::RYYGate, + &[qargs[1], qargs[0]], + Some(SmallVec::from(param)), + )?; + + Ok(new_dag.clone()) +} + +fn rzz_replacement_dag(py: Python, param: &[Param]) -> PyResult { + let new_dag = &mut DAGCircuit::new(py)?; + let qargs = add_qreg(py, new_dag, 2)?; + let qargs = qargs.as_slice(); + + apply_operation_back( + py, + new_dag, + StandardGate::RZZGate, + &[qargs[1], qargs[0]], + Some(SmallVec::from(param)), + )?; + + Ok(new_dag.clone()) +} + +fn rzx_replacement_dag(py: Python, param: &[Param]) -> PyResult { + let new_dag = &mut DAGCircuit::new(py)?; + let qargs = add_qreg(py, new_dag, 2)?; + let qargs = qargs.as_slice(); + + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[0]], None)?; + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[1]], None)?; + apply_operation_back( + py, + new_dag, + StandardGate::RZXGate, + &[qargs[1], qargs[0]], + Some(SmallVec::from(param)), + )?; + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[0]], None)?; + apply_operation_back(py, new_dag, StandardGate::HGate, &[qargs[1]], None)?; + + Ok(new_dag.clone()) +} + +#[pymodule] pub fn gate_direction(m: &Bound) -> PyResult<()> { - m.add_wrapped(wrap_pyfunction!(py_check_with_coupling_map))?; - m.add_wrapped(wrap_pyfunction!(py_check_with_target))?; + m.add_wrapped(wrap_pyfunction!(py_check_direction_coupling_map))?; + m.add_wrapped(wrap_pyfunction!(py_check_direction_target))?; + m.add_wrapped(wrap_pyfunction!(py_fix_direction_coupling_map))?; + m.add_wrapped(wrap_pyfunction!(py_fix_direction_target))?; Ok(()) } diff --git a/crates/accelerate/src/sparse_pauli_op.rs b/crates/accelerate/src/sparse_pauli_op.rs index 73c4ab7a73d5..1a85daf036d9 100644 --- a/crates/accelerate/src/sparse_pauli_op.rs +++ b/crates/accelerate/src/sparse_pauli_op.rs @@ -20,11 +20,13 @@ use numpy::prelude::*; use numpy::{PyArray1, PyArray2, PyReadonlyArray1, PyReadonlyArray2, PyUntypedArrayMethods}; use hashbrown::HashMap; -use ndarray::{s, Array1, Array2, ArrayView1, ArrayView2, Axis}; +use ndarray::{s, ArrayView1, ArrayView2, Axis}; use num_complex::Complex64; use num_traits::Zero; -use qiskit_circuit::util::{c64, C_ONE, C_ZERO}; use rayon::prelude::*; +use thiserror::Error; + +use qiskit_circuit::util::{c64, C_ZERO}; use crate::rayon_ext::*; @@ -70,14 +72,6 @@ pub fn unordered_unique(py: Python, array: PyReadonlyArray2) -> (PyObject, ) } -#[derive(Clone, Copy)] -enum Pauli { - I, - X, - Y, - Z, -} - /// Pack a 2D array of Booleans into a given width. Returns an error if the input array is /// too large to be packed into u64. fn pack_bits(bool_arr: ArrayView2) -> Result, ()> { @@ -188,10 +182,9 @@ impl ZXPaulis { } /// Intermediate structure that represents readonly views onto the Python-space sparse Pauli data. -/// This is used in the chained methods so that the syntactical temporary lifetime extension can -/// occur; we can't have the readonly array temporaries only live within a method that returns -/// [ZXPaulisView], because otherwise the lifetimes of the [PyReadonlyArray] elements will be too -/// short. +/// This is used in the chained methods so that the lifetime extension can occur; we can't have the +/// readonly array temporaries only live within a method that returns [ZXPaulisView], because +/// otherwise the lifetimes of the [PyReadonlyArray] elements will be too short. pub struct ZXPaulisReadonly<'a> { x: PyReadonlyArray2<'a, bool>, z: PyReadonlyArray2<'a, bool>, @@ -325,175 +318,609 @@ impl MatrixCompressedPaulis { } } +#[derive(Clone, Debug)] +struct DecomposeOut { + z: Vec, + x: Vec, + phases: Vec, + coeffs: Vec, + scale: f64, + tol: f64, + num_qubits: usize, +} + +#[derive(Error, Debug)] +enum DecomposeError { + #[error("operators must have two dimensions, not {0}")] + BadDimension(usize), + #[error("operators must be square with a power-of-two side length, not {0:?}")] + BadShape([usize; 2]), +} +impl From for PyErr { + fn from(value: DecomposeError) -> PyErr { + PyValueError::new_err(value.to_string()) + } +} + /// Decompose a dense complex operator into the symplectic Pauli representation in the /// ZX-convention. /// /// This is an implementation of the "tensorized Pauli decomposition" presented in /// `Hantzko, Binkowski and Gupta (2023) `__. +/// +/// Implementation +/// -------------- +/// +/// The original algorithm was described recurisvely, allocating new matrices for each of the +/// block-wise sums (e.g. `op[top_left] + op[bottom_right]`). This implementation differs in two +/// major ways: +/// +/// - We do not allocate new matrices recursively, but instead produce a single copy of the input +/// and repeatedly overwrite subblocks of it at each point of the decomposition. +/// - The implementation is rewritten as an iteration rather than a recursion. The current "state" +/// of the iteration is encoded in a single machine word (the `PauliLocation` struct below). +/// +/// We do the decomposition in three "stages", with the stage changing whenever we need to change +/// the input/output types. The first level is mathematically the same as the middle levels, it +/// just gets handled separately because it does the double duty of moving the data out of the +/// Python-space strided array into a Rust-space contiguous array that we can modify in-place. +/// The middle levels all act in-place on this newly created scratch space. Finally, at the last +/// level, we've completed the decomposition and need to be writing the result into the output +/// data structures rather than into the scratch space. +/// +/// Each "level" is handling one qubit in the operator, equivalently to the recursive procedure +/// described in the paper referenced in the docstring. This implementation is iterative +/// stack-based and in place, rather than recursive. +/// +/// We can get away with overwriting our scratch-space matrix at each point, because each +/// element of a given subblock is used exactly twice during each decomposition - once for the `a + +/// b` case, and once for the `a - b` case. The second operand is the same in both cases. +/// Illustratively, at each step we're decomposing a submatrix blockwise, where we label the blocks +/// like this: +/// +/// +---------+---------+ +---------+---------+ +/// | | | | | | +/// | I | X | | I + Z | X + Y | +/// | | | | | | +/// +---------+---------+ =====> +---------+---------+ +/// | | | | | | +/// | Y | Z | | X - Y | I - Z | +/// | | | | | | +/// +---------+---------+ +---------+---------+ +/// +/// Each addition or subtraction is done elementwise, so as long as we iterate through the two pairs +/// of coupled blocks in order in lockstep, we can write out the answers together without +/// overwriting anything we need again. We ignore all factors of 1/2 until the very last step, and +/// apply them all at once. This minimises the number of floating-point operations we have to do. +/// +/// We store the iteration order as a stack of `PauliLocation`s, whose own docstring explains how it +/// tracks the top-left corner and the size of the submatrix it represents. #[pyfunction] pub fn decompose_dense( py: Python, operator: PyReadonlyArray2, tolerance: f64, ) -> PyResult { - let num_qubits = operator.shape()[0].ilog2() as usize; - let size = 1 << num_qubits; - if operator.shape() != [size, size] { - return Err(PyValueError::new_err(format!( - "input with shape {:?} cannot be interpreted as a multiqubit operator", - operator.shape() - ))); - } - let mut paulis = vec![]; - let mut coeffs = vec![]; - if num_qubits > 0 { - decompose_dense_inner( - C_ONE, - num_qubits, - &[], - operator.as_array(), - &mut paulis, - &mut coeffs, - tolerance * tolerance, - ); - } - if coeffs.is_empty() { - Ok(ZXPaulis { - z: PyArray2::zeros_bound(py, [0, num_qubits], false).into(), - x: PyArray2::zeros_bound(py, [0, num_qubits], false).into(), - phases: PyArray1::zeros_bound(py, [0], false).into(), - coeffs: PyArray1::zeros_bound(py, [0], false).into(), - }) - } else { - // Constructing several arrays of different shapes at once is rather awkward in iterator - // logic, so we just loop manually. - let mut z = Array2::::uninit([paulis.len(), num_qubits]); - let mut x = Array2::::uninit([paulis.len(), num_qubits]); - let mut phases = Array1::::uninit(paulis.len()); - for (i, paulis) in paulis.drain(..).enumerate() { - let mut phase = 0u8; - for (j, pauli) in paulis.into_iter().rev().enumerate() { - match pauli { - Pauli::I => { - z[[i, j]].write(false); - x[[i, j]].write(false); - } - Pauli::X => { - z[[i, j]].write(false); - x[[i, j]].write(true); - } - Pauli::Y => { - z[[i, j]].write(true); - x[[i, j]].write(true); - phase = phase.wrapping_add(1); - } - Pauli::Z => { - z[[i, j]].write(true); - x[[i, j]].write(false); - } + let array_view = operator.as_array(); + let out = py.allow_threads(|| decompose_dense_inner(array_view, tolerance))?; + Ok(ZXPaulis { + z: PyArray1::from_vec_bound(py, out.z) + .reshape([out.phases.len(), out.num_qubits])? + .into(), + x: PyArray1::from_vec_bound(py, out.x) + .reshape([out.phases.len(), out.num_qubits])? + .into(), + phases: PyArray1::from_vec_bound(py, out.phases).into(), + coeffs: PyArray1::from_vec_bound(py, out.coeffs).into(), + }) +} + +/// Rust-only inner component of the `SparsePauliOp` decomposition. +/// +/// See the top-level documentation of [decompose_dense] for more information on the internal +/// algorithm at play. +fn decompose_dense_inner( + operator: ArrayView2, + tolerance: f64, +) -> Result { + let op_shape = match operator.shape() { + [a, b] => [*a, *b], + shape => return Err(DecomposeError::BadDimension(shape.len())), + }; + if op_shape[0].is_zero() { + return Err(DecomposeError::BadShape(op_shape)); + } + let num_qubits = op_shape[0].ilog2() as usize; + let side = 1 << num_qubits; + if op_shape != [side, side] { + return Err(DecomposeError::BadShape(op_shape)); + } + if num_qubits.is_zero() { + // We have to special-case the zero-qubit operator because our `decompose_last_level` still + // needs to "consume" a qubit. + return Ok(DecomposeOut { + z: vec![], + x: vec![], + phases: vec![], + coeffs: vec![operator[[0, 0]]], + scale: 1.0, + tol: tolerance, + num_qubits: 0, + }); + } + let (stack, mut out_list, mut scratch) = decompose_first_level(operator, num_qubits); + decompose_middle_levels(stack, &mut out_list, &mut scratch, num_qubits); + Ok(decompose_last_level( + &mut out_list, + &scratch, + num_qubits, + tolerance, + )) +} + +/// Apply the matrix-addition decomposition at the first level. +/// +/// This is split out from the middle levels because it acts on an `ArrayView2`, and is responsible +/// for copying the operator over into the contiguous scratch space. We can't write over the +/// operator the user gave us (it's not ours to do that to), and anyway, we want to drop to a chunk +/// of memory that we can 100% guarantee is contiguous, so we can elide all the stride checking. +/// We split this out so we can do the first decomposition at the same time as scanning over the +/// operator to copy it. +/// +/// # Panics +/// +/// If the number of qubits in the operator is zero. +fn decompose_first_level( + in_op: ArrayView2, + num_qubits: usize, +) -> (Vec, Vec, Vec) { + let side = 1 << num_qubits; + let mut stack = Vec::::with_capacity(4); + let mut out_list = Vec::::new(); + let mut scratch = Vec::::with_capacity(side * side); + match num_qubits { + 0 => panic!("number of qubits must be greater than zero"), + 1 => { + // If we've only got one qubit, we just want to copy the data over in the correct + // continuity and let the base case of the iteration take care of outputting it. + scratch.extend(in_op.iter()); + out_list.push(PauliLocation::begin(num_qubits)); + } + _ => { + // We don't write out the operator in contiguous-index order, but we can easily + // guarantee that we'll write to each index exactly once without reading it - we still + // visit every index, just in 2x2 blockwise order, not row-by-row. + unsafe { scratch.set_len(scratch.capacity()) }; + let mut ptr = 0usize; + + let cur_qubit = num_qubits - 1; + let mid = 1 << cur_qubit; + let loc = PauliLocation::begin(num_qubits); + let mut i_nonzero = false; + let mut x_nonzero = false; + let mut y_nonzero = false; + let mut z_nonzero = false; + + let i_row_0 = loc.row(); + let i_col_0 = loc.col(); + + let x_row_0 = loc.row(); + let x_col_0 = loc.col() + mid; + + let y_row_0 = loc.row() + mid; + let y_col_0 = loc.col(); + + let z_row_0 = loc.row() + mid; + let z_col_0 = loc.col() + mid; + + for off_row in 0..mid { + let i_row = i_row_0 + off_row; + let z_row = z_row_0 + off_row; + for off_col in 0..mid { + let i_col = i_col_0 + off_col; + let z_col = z_col_0 + off_col; + let value = in_op[[i_row, i_col]] + in_op[[z_row, z_col]]; + scratch[ptr] = value; + ptr += 1; + i_nonzero = i_nonzero || (value != C_ZERO); + } + + let x_row = x_row_0 + off_row; + let y_row = y_row_0 + off_row; + for off_col in 0..mid { + let x_col = x_col_0 + off_col; + let y_col = y_col_0 + off_col; + let value = in_op[[x_row, x_col]] + in_op[[y_row, y_col]]; + scratch[ptr] = value; + ptr += 1; + x_nonzero = x_nonzero || (value != C_ZERO); } } - phases[i].write(phase % 4); + for off_row in 0..mid { + let x_row = x_row_0 + off_row; + let y_row = y_row_0 + off_row; + for off_col in 0..mid { + let x_col = x_col_0 + off_col; + let y_col = y_col_0 + off_col; + let value = in_op[[x_row, x_col]] - in_op[[y_row, y_col]]; + scratch[ptr] = value; + ptr += 1; + y_nonzero = y_nonzero || (value != C_ZERO); + } + let i_row = i_row_0 + off_row; + let z_row = z_row_0 + off_row; + for off_col in 0..mid { + let i_col = i_col_0 + off_col; + let z_col = z_col_0 + off_col; + let value = in_op[[i_row, i_col]] - in_op[[z_row, z_col]]; + scratch[ptr] = value; + ptr += 1; + z_nonzero = z_nonzero || (value != C_ZERO); + } + } + // The middle-levels `stack` is a LIFO, so if we push in this order, we'll consider the + // Pauli terms in lexicographical order, which is the canonical order from + // `SparsePauliOp.sort`. Populating the `out_list` (an initially empty `Vec`) + // effectively reverses the stack, so we want to push its elements in the IXYZ order. + if loc.qubit() == 1 { + i_nonzero.then(|| out_list.push(loc.push_i())); + x_nonzero.then(|| out_list.push(loc.push_x())); + y_nonzero.then(|| out_list.push(loc.push_y())); + z_nonzero.then(|| out_list.push(loc.push_z())); + } else { + z_nonzero.then(|| stack.push(loc.push_z())); + y_nonzero.then(|| stack.push(loc.push_y())); + x_nonzero.then(|| stack.push(loc.push_x())); + i_nonzero.then(|| stack.push(loc.push_i())); + } } - // These are safe because the above loops write into every element. It's guaranteed that - // each of the elements of the `paulis` vec will have `num_qubits` because they're all - // reading from the same base array. - let z = unsafe { z.assume_init() }; - let x = unsafe { x.assume_init() }; - let phases = unsafe { phases.assume_init() }; - Ok(ZXPaulis { - z: z.into_pyarray_bound(py).into(), - x: x.into_pyarray_bound(py).into(), - phases: phases.into_pyarray_bound(py).into(), - coeffs: PyArray1::from_vec_bound(py, coeffs).into(), - }) } + (stack, out_list, scratch) } -/// Recurse worker routine of `decompose_dense`. Should be called with at least one qubit. -fn decompose_dense_inner( - factor: Complex64, +/// Iteratively decompose the matrix at all levels other than the first and last. +/// +/// This populates the `out_list` with locations. This is mathematically the same as the first +/// level of the decomposition, except now we're acting in-place on our Rust-space contiguous +/// scratch space, rather than the strided Python-space array we were originally given. +fn decompose_middle_levels( + mut stack: Vec, + out_list: &mut Vec, + scratch: &mut [Complex64], num_qubits: usize, - paulis: &[Pauli], - block: ArrayView2, - out_paulis: &mut Vec>, - out_coeffs: &mut Vec, - square_tolerance: f64, ) { - if num_qubits == 0 { - // It would be safe to `return` here, but if it's unreachable then LLVM is allowed to - // optimize out this branch entirely in release mode, which is good for a ~2% speedup. - unreachable!("should not call this with an empty operator") - } - // Base recursion case. - if num_qubits == 1 { - let mut push_if_nonzero = |extra: Pauli, value: Complex64| { - if value.norm_sqr() <= square_tolerance { - return; + let side = 1 << num_qubits; + // The stack is a LIFO, which is how we implement the depth-first iteration. Depth-first + // means `stack` never grows very large; it reaches at most `3*num_qubits - 2` elements (if all + // terms are zero all the way through the first subblock decomposition). `out_list`, on the + // other hand, can be `4 ** (num_qubits - 1)` entries in the worst-case scenario of a + // completely dense (in Pauli terms) operator. + while let Some(loc) = stack.pop() { + // Here we work pairwise, writing out the new values into both I and Z simultaneously (etc + // for X and Y) so we can re-use their scratch space and avoid re-allocating. We're doing + // the multiple assignment `(I, Z) = (I + Z, I - Z)`. + // + // See the documentation of `decompose_dense` for more information on how this works. + let mid = 1 << loc.qubit(); + let mut i_nonzero = false; + let mut z_nonzero = false; + let i_row_0 = loc.row(); + let i_col_0 = loc.col(); + let z_row_0 = loc.row() + mid; + let z_col_0 = loc.col() + mid; + for off_row in 0..mid { + let i_loc_0 = (i_row_0 + off_row) * side + i_col_0; + let z_loc_0 = (z_row_0 + off_row) * side + z_col_0; + for off_col in 0..mid { + let i_loc = i_loc_0 + off_col; + let z_loc = z_loc_0 + off_col; + let add = scratch[i_loc] + scratch[z_loc]; + let sub = scratch[i_loc] - scratch[z_loc]; + scratch[i_loc] = add; + scratch[z_loc] = sub; + i_nonzero = i_nonzero || (add != C_ZERO); + z_nonzero = z_nonzero || (sub != C_ZERO); } - let paulis = { - let mut vec = Vec::with_capacity(paulis.len() + 1); - vec.extend_from_slice(paulis); - vec.push(extra); - vec - }; - out_paulis.push(paulis); - out_coeffs.push(value); - }; - push_if_nonzero(Pauli::I, 0.5 * factor * (block[[0, 0]] + block[[1, 1]])); - push_if_nonzero(Pauli::X, 0.5 * factor * (block[[0, 1]] + block[[1, 0]])); - push_if_nonzero( - Pauli::Y, - 0.5 * Complex64::i() * factor * (block[[0, 1]] - block[[1, 0]]), - ); - push_if_nonzero(Pauli::Z, 0.5 * factor * (block[[0, 0]] - block[[1, 1]])); - return; - } - let mut recurse_if_nonzero = |extra: Pauli, factor: Complex64, values: Array2| { - let mut is_zero = true; - for value in values.iter() { - if !value.is_zero() { - is_zero = false; - break; + } + + let mut x_nonzero = false; + let mut y_nonzero = false; + let x_row_0 = loc.row(); + let x_col_0 = loc.col() + mid; + let y_row_0 = loc.row() + mid; + let y_col_0 = loc.col(); + for off_row in 0..mid { + let x_loc_0 = (x_row_0 + off_row) * side + x_col_0; + let y_loc_0 = (y_row_0 + off_row) * side + y_col_0; + for off_col in 0..mid { + let x_loc = x_loc_0 + off_col; + let y_loc = y_loc_0 + off_col; + let add = scratch[x_loc] + scratch[y_loc]; + let sub = scratch[x_loc] - scratch[y_loc]; + scratch[x_loc] = add; + scratch[y_loc] = sub; + x_nonzero = x_nonzero || (add != C_ZERO); + y_nonzero = y_nonzero || (sub != C_ZERO); } } - if is_zero { - return; + // The middle-levels `stack` is a LIFO, so if we push in this order, we'll consider the + // Pauli terms in lexicographical order, which is the canonical order from + // `SparsePauliOp.sort`. Populating the `out_list` (an initially empty `Vec`) effectively + // reverses the stack, so we want to push its elements in the IXYZ order. + if loc.qubit() == 1 { + i_nonzero.then(|| out_list.push(loc.push_i())); + x_nonzero.then(|| out_list.push(loc.push_x())); + y_nonzero.then(|| out_list.push(loc.push_y())); + z_nonzero.then(|| out_list.push(loc.push_z())); + } else { + z_nonzero.then(|| stack.push(loc.push_z())); + y_nonzero.then(|| stack.push(loc.push_y())); + x_nonzero.then(|| stack.push(loc.push_x())); + i_nonzero.then(|| stack.push(loc.push_i())); } - let mut new_paulis = Vec::with_capacity(paulis.len() + 1); - new_paulis.extend_from_slice(paulis); - new_paulis.push(extra); - decompose_dense_inner( - factor, - num_qubits - 1, - &new_paulis, - values.view(), - out_paulis, - out_coeffs, - square_tolerance, - ); + } +} + +/// Write out the results of the final decomposition into the Pauli ZX form. +/// +/// The calculation here is the same as the previous two sets of decomposers, but we don't want to +/// write the result out into the scratch space to iterate needlessly once more; we want to +/// associate each non-zero coefficient with the final Pauli in the ZX format. +/// +/// This function applies all the factors of 1/2 that we've been skipping during the intermediate +/// decompositions. This means that the factors are applied to the output with `2 * output_len` +/// floating-point operations (real and imaginary), which is a huge reduction compared to repeatedly +/// doing it during the decomposition. +fn decompose_last_level( + out_list: &mut Vec, + scratch: &[Complex64], + num_qubits: usize, + tolerance: f64, +) -> DecomposeOut { + let side = 1 << num_qubits; + let scale = 0.5f64.powi(num_qubits as i32); + // Pessimistically allocate assuming that there will be no zero terms in the out list. We + // don't really pay much cost if we overallocate, but underallocating means that all four + // outputs have to copy their data across to a new allocation. + let mut out = DecomposeOut { + z: Vec::with_capacity(4 * num_qubits * out_list.len()), + x: Vec::with_capacity(4 * num_qubits * out_list.len()), + phases: Vec::with_capacity(4 * out_list.len()), + coeffs: Vec::with_capacity(4 * out_list.len()), + scale, + tol: (tolerance * tolerance) / (scale * scale), + num_qubits, }; - let mid = 1usize << (num_qubits - 1); - recurse_if_nonzero( - Pauli::I, - 0.5 * factor, - &block.slice(s![..mid, ..mid]) + &block.slice(s![mid.., mid..]), - ); - recurse_if_nonzero( - Pauli::X, - 0.5 * factor, - &block.slice(s![..mid, mid..]) + &block.slice(s![mid.., ..mid]), - ); - recurse_if_nonzero( - Pauli::Y, - 0.5 * Complex64::i() * factor, - &block.slice(s![..mid, mid..]) - &block.slice(s![mid.., ..mid]), - ); - recurse_if_nonzero( - Pauli::Z, - 0.5 * factor, - &block.slice(s![..mid, ..mid]) - &block.slice(s![mid.., mid..]), - ); + + for loc in out_list.drain(..) { + let row = loc.row(); + let col = loc.col(); + let base = row * side + col; + let i_value = scratch[base] + scratch[base + side + 1]; + let z_value = scratch[base] - scratch[base + side + 1]; + let x_value = scratch[base + 1] + scratch[base + side]; + let y_value = scratch[base + 1] - scratch[base + side]; + + let x = row ^ col; + let z = row; + let phase = (x & z).count_ones() as u8; + // Pushing the last Pauli onto the `loc` happens "forwards" to maintain lexicographical + // ordering in `out`, since this is the construction of the final object. + push_pauli_if_nonzero(x, z, phase, i_value, &mut out); + push_pauli_if_nonzero(x | 1, z, phase, x_value, &mut out); + push_pauli_if_nonzero(x | 1, z | 1, phase + 1, y_value, &mut out); + push_pauli_if_nonzero(x, z | 1, phase, z_value, &mut out); + } + // If we _wildly_ overallocated, then shrink back to a sensible size to avoid tying up too much + // memory as we return to Python space. + if out.z.capacity() / 4 > out.z.len() { + out.z.shrink_to_fit(); + out.x.shrink_to_fit(); + out.phases.shrink_to_fit(); + out.coeffs.shrink_to_fit(); + } + out +} + +// This generates lookup tables of the form +// const LOOKUP: [[bool; 2] 4] = [[false, false], [true, false], [false, true], [true, true]]; +// when called `pauli_lookup!(LOOKUP, 2, [_, _])`. The last argument is like a dummy version of +// an individual lookup rule, which is consumed to make an inner "loop" with a declarative macro. +macro_rules! pauli_lookup { + ($name:ident, $n:literal, [$head:expr$ (, $($tail:expr),*)?]) => { + static $name: [[bool; $n]; 1<<$n] = pauli_lookup!(@acc, [$($($tail),*)?], [[false], [true]]); + }; + (@acc, [$head:expr $(, $($tail:expr),*)?], [$([$($bools:tt),*]),+]) => { + pauli_lookup!(@acc, [$($($tail),*)?], [$([$($bools),*, false]),+, $([$($bools),*, true]),+]) + }; + (@acc, [], $init:expr) => { $init }; +} +pauli_lookup!(PAULI_LOOKUP_2, 2, [(), ()]); +pauli_lookup!(PAULI_LOOKUP_4, 4, [(), (), (), ()]); +pauli_lookup!(PAULI_LOOKUP_8, 8, [(), (), (), (), (), (), (), ()]); + +/// Push a complete Pauli chain into the output (`out`), if the corresponding entry is non-zero. +/// +/// `x` and `z` represent the symplectic X and Z bitvectors, packed into `usize`, where LSb n +/// corresponds to qubit `n`. +fn push_pauli_if_nonzero( + mut x: usize, + mut z: usize, + phase: u8, + value: Complex64, + out: &mut DecomposeOut, +) { + if value.norm_sqr() <= out.tol { + return; + } + + // This set of `extend` calls is effectively an 8-fold unrolling of the "natural" loop through + // each bit, where the initial `if` statements are handling the remainder (the up-to 7 + // least-significant bits). In practice, it's probably unlikely that people are decomposing + // 16q+ operators, since that's a pretty huge matrix already. + // + // The 8-fold loop unrolling is because going bit-by-bit all the way would be dominated by loop + // and bitwise-operation overhead. + + if out.num_qubits & 1 == 1 { + out.x.push(x & 1 == 1); + out.z.push(z & 1 == 1); + x >>= 1; + z >>= 1; + } + if out.num_qubits & 2 == 2 { + out.x.extend(&PAULI_LOOKUP_2[x & 0b11]); + out.z.extend(&PAULI_LOOKUP_2[z & 0b11]); + x >>= 2; + z >>= 2; + } + if out.num_qubits & 4 == 4 { + out.x.extend(&PAULI_LOOKUP_4[x & 0b1111]); + out.z.extend(&PAULI_LOOKUP_4[z & 0b1111]); + x >>= 4; + z >>= 4; + } + for _ in 0..(out.num_qubits / 8) { + out.x.extend(&PAULI_LOOKUP_8[x & 0b1111_1111]); + out.z.extend(&PAULI_LOOKUP_8[z & 0b1111_1111]); + x >>= 8; + z >>= 8; + } + + let phase = phase % 4; + let value = match phase { + 0 => Complex64::new(out.scale, 0.0) * value, + 1 => Complex64::new(0.0, out.scale) * value, + 2 => Complex64::new(-out.scale, 0.0) * value, + 3 => Complex64::new(0.0, -out.scale) * value, + _ => unreachable!("'x % 4' has only four possible values"), + }; + out.phases.push(phase); + out.coeffs.push(value); +} + +/// The "state" of an iteration step of the dense-operator decomposition routine. +/// +/// Pack the information about which row, column and qubit we're considering into a single `usize`. +/// Complex64 data is 16 bytes long and the operators are square and must be addressable in memory, +/// so the row and column are hardware limited to be of width `usize::BITS / 2 - 2` each. However, +/// we don't need to store at a granularity of 1, because the last 2x2 block we handle manually, so +/// we can remove an extra least significant bit from the row and column. Regardless of the width +/// of `usize`, we can therefore track the state for up to 30 qubits losslessly, which is greater +/// than the maximum addressable memory on a 64-bit system. +/// +/// For a 64-bit usize, the bit pattern is stored like this: +/// +/// 0b__000101__11111111111111111111111110000__11111111111111111111111110000 +/// <-6--> <------------29-------------> <------------29-------------> +/// | | | +/// | uint of the input row uint of the input column +/// | (once a 0 is appended) (once a 0 is appended) +/// | +/// current qubit under consideration +/// +/// The `qubit` field encodes the depth in the call stack that the user of the `PauliLocation` +/// should consider. When the stack is initialised (before any calculation is done), it starts at +/// the highest qubit index (`num_qubits - 1`) and decreases from there until 0. +/// +/// The `row` and `col` methods form the top-left corner of a `(2**(qubit + 1), 2**(qubit + 1))` +/// submatrix (where the top row and leftmost column are 0). The least significant `qubit + 1` +/// bits of the of row and column are therefore always zero; the 0-indexed qubit still corresponds +/// to a 2x2 block. This is why we needn't store it. +#[derive(Debug, Clone, Copy)] +struct PauliLocation(usize); + +impl PauliLocation { + // These shifts and masks are used to access the three components of the bit-packed state. + const QUBIT_SHIFT: u32 = usize::BITS - 6; + const QUBIT_MASK: usize = (usize::MAX >> Self::QUBIT_SHIFT) << Self::QUBIT_SHIFT; + const ROW_SHIFT: u32 = usize::BITS / 2 - 3; + const ROW_MASK: usize = + ((usize::MAX >> Self::ROW_SHIFT) << Self::ROW_SHIFT) & !Self::QUBIT_MASK; + const COL_SHIFT: u32 = 0; // Just for consistency. + const COL_MASK: usize = usize::MAX & !Self::ROW_MASK & !Self::QUBIT_MASK; + + /// Create the base `PauliLocation` for an entire matrix with `num_qubits` qubits. The initial + /// Pauli chain is empty. + #[inline(always)] + fn begin(num_qubits: usize) -> Self { + Self::new(0, 0, num_qubits - 1) + } + + /// Manually create a new `PauliLocation` with the given information. The logic in the rest of + /// the class assumes that `row` and `col` will end with at least `qubit + 1` zeros, since + /// these are the only valid locations. + #[inline(always)] + fn new(row: usize, col: usize, qubit: usize) -> Self { + debug_assert!(row & 1 == 0); + debug_assert!(col & 1 == 0); + debug_assert!(row < 2 * Self::ROW_SHIFT as usize); + debug_assert!(col < 2 * Self::ROW_SHIFT as usize); + debug_assert!(qubit < 64); + Self( + (qubit << Self::QUBIT_SHIFT) + | (row << Self::ROW_SHIFT >> 1) + | (col << Self::COL_SHIFT >> 1), + ) + } + + /// The row in the dense matrix that this location corresponds to. + #[inline(always)] + fn row(&self) -> usize { + ((self.0 & Self::ROW_MASK) >> Self::ROW_SHIFT) << 1 + } + + /// The column in the dense matrix that this location corresponds to. + #[inline(always)] + fn col(&self) -> usize { + ((self.0 & Self::COL_MASK) >> Self::COL_SHIFT) << 1 + } + + /// Which qubit in the Pauli chain we're currently considering. + #[inline(always)] + fn qubit(&self) -> usize { + (self.0 & Self::QUBIT_MASK) >> Self::QUBIT_SHIFT + } + + /// Create a new location corresponding to the Pauli chain so far, plus an identity on the + /// currently considered qubit. + #[inline(always)] + fn push_i(&self) -> Self { + Self::new(self.row(), self.col(), self.qubit() - 1) + } + + /// Create a new location corresponding to the Pauli chain so far, plus an X on the currently + /// considered qubit. + #[inline(always)] + fn push_x(&self) -> Self { + Self::new( + self.row(), + self.col() | (1 << self.qubit()), + self.qubit() - 1, + ) + } + + /// Create a new location corresponding to the Pauli chain so far, plus a Y on the currently + /// considered qubit. + #[inline(always)] + fn push_y(&self) -> Self { + Self::new( + self.row() | (1 << self.qubit()), + self.col(), + self.qubit() - 1, + ) + } + + /// Create a new location corresponding to the Pauli chain so far, plus a Z on the currently + /// considered qubit. + #[inline(always)] + fn push_z(&self) -> Self { + Self::new( + self.row() | (1 << self.qubit()), + self.col() | (1 << self.qubit()), + self.qubit() - 1, + ) + } } /// Convert the given [ZXPaulis] object to a dense 2D Numpy matrix. @@ -830,11 +1257,13 @@ pub fn sparse_pauli_op(m: &Bound) -> PyResult<()> { #[cfg(test)] mod tests { + use ndarray::{aview2, Array1}; + use super::*; use crate::test::*; - // The purpose of these tests is more about exercising the `unsafe` code; we test for full - // correctness from Python space. + // The purpose of these tests is more about exercising the `unsafe` code under Miri; we test for + // full numerical correctness from Python space. fn example_paulis() -> MatrixCompressedPaulis { MatrixCompressedPaulis { @@ -853,6 +1282,166 @@ mod tests { } } + /// Helper struct for the decomposition testing. This is a subset of the `DecomposeOut` + /// struct, skipping the unnecessary algorithm-state components of it. + /// + /// If we add a more Rust-friendly interface to `SparsePauliOp` in the future, hopefully this + /// can be removed. + #[derive(Clone, PartialEq, Debug)] + struct DecomposeMinimal { + z: Vec, + x: Vec, + phases: Vec, + coeffs: Vec, + num_qubits: usize, + } + impl From for DecomposeMinimal { + fn from(value: DecomposeOut) -> Self { + Self { + z: value.z, + x: value.x, + phases: value.phases, + coeffs: value.coeffs, + num_qubits: value.num_qubits, + } + } + } + impl From for DecomposeMinimal { + fn from(value: MatrixCompressedPaulis) -> Self { + let phases = value + .z_like + .iter() + .zip(value.x_like.iter()) + .map(|(z, x)| ((z & x).count_ones() % 4) as u8) + .collect::>(); + let coeffs = value + .coeffs + .iter() + .zip(phases.iter()) + .map(|(c, phase)| match phase { + 0 => *c, + 1 => Complex64::new(-c.im, c.re), + 2 => Complex64::new(-c.re, -c.im), + 3 => Complex64::new(c.im, -c.re), + _ => panic!("phase should only in [0, 4)"), + }) + .collect(); + let z = value + .z_like + .iter() + .flat_map(|digit| (0..value.num_qubits).map(move |i| (digit & (1 << i)) != 0)) + .collect(); + let x = value + .x_like + .iter() + .flat_map(|digit| (0..value.num_qubits).map(move |i| (digit & (1 << i)) != 0)) + .collect(); + Self { + z, + x, + phases, + coeffs, + num_qubits: value.num_qubits as usize, + } + } + } + + #[test] + fn decompose_empty_operator_fails() { + assert!(matches!( + decompose_dense_inner(aview2::(&[]), 0.0), + Err(DecomposeError::BadShape(_)), + )); + } + + #[test] + fn decompose_0q_operator() { + let coeff = Complex64::new(1.5, -0.5); + let arr = [[coeff]]; + let out = decompose_dense_inner(aview2(&arr), 0.0).unwrap(); + let expected = DecomposeMinimal { + z: vec![], + x: vec![], + phases: vec![], + coeffs: vec![coeff], + num_qubits: 0, + }; + assert_eq!(DecomposeMinimal::from(out), expected); + } + + #[test] + fn decompose_1q_operator() { + // Be sure that any sums are given in canonical order of the output, or there will be + // spurious test failures. + let paulis = [ + (vec![0], vec![0]), // I + (vec![1], vec![0]), // X + (vec![1], vec![1]), // Y + (vec![0], vec![1]), // Z + (vec![0, 1], vec![0, 0]), // I, X + (vec![0, 1], vec![0, 1]), // I, Y + (vec![0, 0], vec![0, 1]), // I, Z + (vec![1, 1], vec![0, 1]), // X, Y + (vec![1, 0], vec![1, 1]), // X, Z + (vec![1, 0], vec![1, 1]), // Y, Z + (vec![1, 1, 0], vec![0, 1, 1]), // X, Y, Z + ]; + let coeffs = [ + Complex64::new(1.5, -0.5), + Complex64::new(-0.25, 2.0), + Complex64::new(0.75, 0.75), + ]; + for (x_like, z_like) in paulis { + let paulis = MatrixCompressedPaulis { + num_qubits: 1, + coeffs: coeffs[0..x_like.len()].to_owned(), + x_like, + z_like, + }; + let arr = Array1::from_vec(to_matrix_dense_inner(&paulis, false)) + .into_shape((2, 2)) + .unwrap(); + let expected: DecomposeMinimal = paulis.into(); + let actual: DecomposeMinimal = decompose_dense_inner(arr.view(), 0.0).unwrap().into(); + assert_eq!(actual, expected); + } + } + + #[test] + fn decompose_3q_operator() { + // Be sure that any sums are given in canonical order of the output, or there will be + // spurious test failures. + let paulis = [ + (vec![0], vec![0]), // III + (vec![1], vec![0]), // IIX + (vec![2], vec![2]), // IYI + (vec![0], vec![4]), // ZII + (vec![6], vec![6]), // YYI + (vec![7], vec![7]), // YYY + (vec![1, 6, 7], vec![1, 6, 7]), // IIY, YYI, YYY + (vec![1, 2, 0], vec![0, 2, 4]), // IIX, IYI, ZII + ]; + let coeffs = [ + Complex64::new(1.5, -0.5), + Complex64::new(-0.25, 2.0), + Complex64::new(0.75, 0.75), + ]; + for (x_like, z_like) in paulis { + let paulis = MatrixCompressedPaulis { + num_qubits: 3, + coeffs: coeffs[0..x_like.len()].to_owned(), + x_like, + z_like, + }; + let arr = Array1::from_vec(to_matrix_dense_inner(&paulis, false)) + .into_shape((8, 8)) + .unwrap(); + let expected: DecomposeMinimal = paulis.into(); + let actual: DecomposeMinimal = decompose_dense_inner(arr.view(), 0.0).unwrap().into(); + assert_eq!(actual, expected); + } + } + #[test] fn dense_threaded_and_serial_equal() { let paulis = example_paulis(); diff --git a/crates/accelerate/src/split_2q_unitaries.rs b/crates/accelerate/src/split_2q_unitaries.rs index 6950ea590212..ac2577c2fc2c 100644 --- a/crates/accelerate/src/split_2q_unitaries.rs +++ b/crates/accelerate/src/split_2q_unitaries.rs @@ -28,6 +28,9 @@ pub fn split_2q_unitaries( dag: &mut DAGCircuit, requested_fidelity: f64, ) -> PyResult<()> { + if !dag.get_op_counts().contains_key("unitary") { + return Ok(()); + } let nodes: Vec = dag.op_nodes(false).collect(); for node in nodes { diff --git a/crates/accelerate/src/target_transpiler/mod.rs b/crates/accelerate/src/target_transpiler/mod.rs index 9e6c220818a3..72cde7735b93 100644 --- a/crates/accelerate/src/target_transpiler/mod.rs +++ b/crates/accelerate/src/target_transpiler/mod.rs @@ -43,7 +43,7 @@ use instruction_properties::InstructionProperties; use self::exceptions::TranspilerError; -mod exceptions { +pub(crate) mod exceptions { use pyo3::import_exception_bound; import_exception_bound! {qiskit.exceptions, QiskitError} import_exception_bound! {qiskit.transpiler.exceptions, TranspilerError} diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index 7d2d99181e02..f680d96e47a8 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -154,6 +154,7 @@ impl CircuitData { self_.clbits.cached().clone_ref(py), None::<()>, self_.data.len(), + self_.global_phase.clone(), ) }; Ok((ty, args, None::<()>, self_.iter()?).into_py(py)) @@ -732,13 +733,19 @@ impl CircuitData { } /// Assign all uses of the circuit parameters as keys `mapping` to their corresponding values. + /// + /// Any items in the mapping that are not present in the circuit are skipped; it's up to Python + /// space to turn extra bindings into an error, if they choose to do it. fn assign_parameters_mapping(&mut self, mapping: Bound) -> PyResult<()> { let py = mapping.py(); let mut items = Vec::new(); for item in mapping.call_method0("items")?.iter()? { let (param_ob, value) = item?.extract::<(Py, AssignParam)>()?; let uuid = ParameterUuid::from_parameter(param_ob.bind(py))?; - items.push((param_ob, value.0, self.param_table.pop(uuid)?)); + // It's fine if the mapping contains parameters that we don't have - just skip those. + if let Ok(uses) = self.param_table.pop(uuid) { + items.push((param_ob, value.0, uses)); + } } self.assign_parameters_inner(py, items) } @@ -776,6 +783,16 @@ impl CircuitData { if slf.len()? != other.len()? { return Ok(false); } + + if let Ok(other_dc) = other.downcast::() { + if !slf + .getattr("global_phase")? + .eq(other_dc.getattr("global_phase")?)? + { + return Ok(false); + } + } + // Implemented using generic iterators on both sides // for simplicity. let mut ours_itr = slf.iter()?; diff --git a/crates/circuit/src/circuit_instruction.rs b/crates/circuit/src/circuit_instruction.rs index 255343ac186c..e1bf3cfc2e55 100644 --- a/crates/circuit/src/circuit_instruction.rs +++ b/crates/circuit/src/circuit_instruction.rs @@ -656,7 +656,7 @@ impl<'py> FromPyObject<'py> for OperationFromPython { ob.getattr(intern!(py, "label"))?.extract()?, duration, unit, - ob.getattr(intern!(py, "condition"))?.extract()?, + ob.getattr(intern!(py, "_condition"))?.extract()?, )) }; diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index e631b6971580..135ac289fff1 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -888,6 +888,8 @@ impl DAGCircuit { /// /// Raises: /// Exception: if the gate is of type string and params is None. + /// + /// DEPRECATED since Qiskit 1.3.0 and will be removed in Qiskit 2.0.0 #[pyo3(signature=(gate, qubits, schedule, params=None))] fn add_calibration<'py>( &mut self, @@ -961,7 +963,7 @@ def _format(operand): /// case, the operation does not need to be translated to the device basis. /// /// DEPRECATED since Qiskit 1.3.0 and will be removed in Qiskit 2.0.0 - fn has_calibration_for(&self, py: Python, node: PyRef) -> PyResult { + pub fn has_calibration_for(&self, py: Python, node: PyRef) -> PyResult { emit_pulse_dependency_deprecation( py, "method ``qiskit.dagcircuit.dagcircuit.DAGCircuit.has_calibration_for``", diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 8705d52d1aa7..a4064d44b917 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -17,7 +17,7 @@ pub mod converters; pub mod dag_circuit; pub mod dag_node; mod dot_utils; -mod error; +pub mod error; pub mod gate_matrix; pub mod imports; pub mod interner; diff --git a/crates/circuit/src/operations.rs b/crates/circuit/src/operations.rs index f8c1e68c0da9..a7b147c7e371 100644 --- a/crates/circuit/src/operations.rs +++ b/crates/circuit/src/operations.rs @@ -2305,7 +2305,7 @@ pub fn multiply_param(param: &Param, mult: f64, py: Python) -> Param { .call_method1(py, intern!(py, "__rmul__"), (mult,)) .expect("Multiplication of Parameter expression by float failed."), ), - Param::Obj(_) => unreachable!(), + Param::Obj(_) => unreachable!("Unsupported multiplication of a Param::Obj."), } } @@ -2339,7 +2339,7 @@ pub fn add_param(param: &Param, summand: f64, py: Python) -> Param { } } -fn radd_param(param1: Param, param2: Param, py: Python) -> Param { +pub fn radd_param(param1: Param, param2: Param, py: Python) -> Param { match [param1, param2] { [Param::Float(theta), Param::Float(lambda)] => Param::Float(theta + lambda), [Param::ParameterExpression(theta), Param::ParameterExpression(lambda)] => { diff --git a/docs/release_notes.rst b/docs/release_notes.rst index fddd907195fc..37e1127c5d8b 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -10,4 +10,4 @@ Qiskit |version| release notes `:earliest-version:` should be set to the rc1 release for the current minor release series. For example, the stable/1.1 branch should set it to 1.1.0rc1. If on `main`, set to the prior minor version's rc1, like `1.0.0rc1`. .. release-notes:: - :earliest-version: 1.0.0rc1 + :earliest-version: 1.1.0rc1 diff --git a/pyproject.toml b/pyproject.toml index 326d28f2a273..91ef14f2e84d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,6 +101,7 @@ sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevS "qft.line" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisLine" "qft.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisFull" "permutation.token_swapper" = "qiskit.transpiler.passes.synthesis.hls_plugins:TokenSwapperSynthesisPermutation" +"PauliEvolution.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:PauliEvolutionSynthesisDefault" [project.entry-points."qiskit.transpiler.init"] default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager" diff --git a/qiskit/circuit/barrier.py b/qiskit/circuit/barrier.py index c339066b4dce..d04769b68d39 100644 --- a/qiskit/circuit/barrier.py +++ b/qiskit/circuit/barrier.py @@ -19,6 +19,7 @@ from __future__ import annotations from qiskit.exceptions import QiskitError +from qiskit.utils import deprecate_func from .instruction import Instruction @@ -44,5 +45,6 @@ def inverse(self, annotated: bool = False): """Special case. Return self.""" return Barrier(self.num_qubits) + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): raise QiskitError("Barriers are compiler directives and cannot be conditional.") diff --git a/qiskit/circuit/controlflow/builder.py b/qiskit/circuit/controlflow/builder.py index ab464a50ca67..458fabec49ef 100644 --- a/qiskit/circuit/controlflow/builder.py +++ b/qiskit/circuit/controlflow/builder.py @@ -284,7 +284,7 @@ def _copy_mutable_properties(self, instruction: Instruction) -> Instruction: The same instruction instance that was passed, but mutated to propagate the tracked changes to this class. """ - instruction.condition = self.condition + instruction._condition = self._condition return instruction # Provide some better error messages, just in case something goes wrong during development and @@ -639,8 +639,8 @@ def update_registers(index, op): # a register is already present, so we use our own tracking. self.add_register(register) out.add_register(register) - if getattr(op, "condition", None) is not None: - for register in condition_resources(op.condition).cregs: + if getattr(op, "_condition", None) is not None: + for register in condition_resources(op._condition).cregs: if register not in self.registers: self.add_register(register) out.add_register(register) diff --git a/qiskit/circuit/controlflow/if_else.py b/qiskit/circuit/controlflow/if_else.py index dd639c65f4b5..f5cdc9a2e1ae 100644 --- a/qiskit/circuit/controlflow/if_else.py +++ b/qiskit/circuit/controlflow/if_else.py @@ -87,12 +87,20 @@ def __init__( super().__init__("if_else", num_qubits, num_clbits, [true_body, false_body], label=label) - self.condition = validate_condition(condition) + self._condition = validate_condition(condition) @property def params(self): return self._params + @property + def condition(self): + return self._condition + + @condition.setter + def condition(self, value): + self._condition = value + @params.setter def params(self, parameters): # pylint: disable=cyclic-import @@ -152,7 +160,7 @@ def replace_blocks(self, blocks: Iterable[QuantumCircuit]) -> "IfElseOp": true_body, false_body = ( ablock for ablock, _ in itertools.zip_longest(blocks, range(2), fillvalue=None) ) - return IfElseOp(self.condition, true_body, false_body=false_body, label=self.label) + return IfElseOp(self._condition, true_body, false_body=false_body, label=self.label) def c_if(self, classical, val): raise NotImplementedError( @@ -200,7 +208,7 @@ def __init__( "if_else", len(self.__resources.qubits), len(self.__resources.clbits), [], label=label ) # Set the condition after super().__init__() has initialized it to None. - self.condition = validate_condition(condition) + self._condition = validate_condition(condition) def with_false_block(self, false_block: ControlFlowBuilderBlock) -> "IfElsePlaceholder": """Return a new placeholder instruction, with the false block set to the given value, @@ -225,7 +233,7 @@ def with_false_block(self, false_block: ControlFlowBuilderBlock) -> "IfElsePlace false_bits = false_block.qubits() | false_block.clbits() true_block.add_bits(false_bits - true_bits) false_block.add_bits(true_bits - false_bits) - return type(self)(self.condition, true_block, false_block, label=self.label) + return type(self)(self._condition, true_block, false_block, label=self.label) def registers(self): """Get the registers used by the interior blocks.""" @@ -288,7 +296,7 @@ def concrete_instruction(self, qubits, clbits): ) return ( self._copy_mutable_properties( - IfElseOp(self.condition, true_body, false_body, label=self.label) + IfElseOp(self._condition, true_body, false_body, label=self.label) ), InstructionResources( qubits=tuple(true_body.qubits), diff --git a/qiskit/circuit/controlflow/while_loop.py b/qiskit/circuit/controlflow/while_loop.py index 488a7a8b7025..7dcc2c22f6ee 100644 --- a/qiskit/circuit/controlflow/while_loop.py +++ b/qiskit/circuit/controlflow/while_loop.py @@ -53,12 +53,20 @@ def __init__( num_clbits = body.num_clbits super().__init__("while_loop", num_qubits, num_clbits, [body], label=label) - self.condition = validate_condition(condition) + self._condition = validate_condition(condition) @property def params(self): return self._params + @property + def condition(self): + return self._condition + + @condition.setter + def condition(self, value): + self._condition = value + @params.setter def params(self, parameters): # pylint: disable=cyclic-import @@ -88,7 +96,7 @@ def blocks(self): def replace_blocks(self, blocks): (body,) = blocks - return WhileLoopOp(self.condition, body, label=self.label) + return WhileLoopOp(self._condition, body, label=self.label) def c_if(self, classical, val): raise NotImplementedError( diff --git a/qiskit/circuit/delay.py b/qiskit/circuit/delay.py index 7dab3a6839f9..2f0613acb932 100644 --- a/qiskit/circuit/delay.py +++ b/qiskit/circuit/delay.py @@ -19,6 +19,7 @@ from qiskit.circuit.gate import Gate from qiskit.circuit import _utils from qiskit.circuit.parameterexpression import ParameterExpression +from qiskit.utils import deprecate_func @_utils.with_gate_array(np.eye(2, dtype=complex)) @@ -46,6 +47,7 @@ def inverse(self, annotated: bool = False): """Special case. Return self.""" return self + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): raise CircuitError("Conditional Delay is not yet implemented.") diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index f2879e2be5cd..e1df54c2cb99 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -176,6 +176,7 @@ def to_mutable(self): return self.copy() @property + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0", is_property=True) def condition(self): """The classical condition on the instruction.""" return self._condition @@ -410,8 +411,8 @@ def _assemble(self): # Add condition parameters for assembler. This is needed to convert # to a qobj conditional instruction at assemble time and after # conversion will be deleted by the assembler. - if self.condition: - instruction._condition = self.condition + if self._condition: + instruction._condition = self._condition return instruction @property @@ -517,6 +518,7 @@ def inverse(self, annotated: bool = False): inverse_gate.definition = inverse_definition return inverse_gate + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): """Set a classical equality condition on this instruction between the register or cbit ``classical`` and value ``val``. @@ -632,7 +634,7 @@ def repeat(self, n): qargs = tuple(qc.qubits) cargs = tuple(qc.clbits) base = self.copy() - if self.condition: + if self._condition: # Condition is handled on the outer instruction. base = base.to_mutable() base.condition = None @@ -640,18 +642,19 @@ def repeat(self, n): qc._append(CircuitInstruction(base, qargs, cargs)) instruction.definition = qc - if self.condition: - instruction = instruction.c_if(*self.condition) + if self._condition: + instruction = instruction.c_if(*self._condition) return instruction @property + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0", is_property=True) def condition_bits(self) -> List[Clbit]: """Get Clbits in condition.""" from qiskit.circuit.controlflow import condition_resources # pylint: disable=cyclic-import - if self.condition is None: + if self._condition is None: return [] - return list(condition_resources(self.condition).clbits) + return list(condition_resources(self._condition).clbits) @property def name(self): diff --git a/qiskit/circuit/instructionset.py b/qiskit/circuit/instructionset.py index cc8a050fd2b0..a3f06a90040c 100644 --- a/qiskit/circuit/instructionset.py +++ b/qiskit/circuit/instructionset.py @@ -20,6 +20,7 @@ from typing import Callable from qiskit.circuit.exceptions import CircuitError +from qiskit.utils import deprecate_func from .classicalregister import Clbit, ClassicalRegister from .operation import Operation from .quantumcircuitdata import CircuitInstruction @@ -105,6 +106,7 @@ def inverse(self, annotated: bool = False): ) return self + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "InstructionSet": """Set a classical equality condition on all the instructions in this set between the :obj:`.ClassicalRegister` or :obj:`.Clbit` ``classical`` and value ``val``. diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 7adb8a09e8a4..34efa90cdb3e 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -321,20 +321,29 @@ :template: autosummary/class_no_inherited_members.rst FourierChecking + fourier_checking GraphState + GraphStateGate HiddenLinearFunction + hidden_linear_function IQP QuantumVolume quantum_volume PhaseEstimation + phase_estimation GroverOperator PhaseOracle PauliEvolutionGate HamiltonianGate UnitaryOverlap + unitary_overlap .. autofunction:: iqp .. autofunction:: random_iqp +.. autofunction:: fourier_checking +.. autofunction:: hidden_linear_function +.. autofunction:: unitary_overlap +.. autofunction:: phase_estimation N-local circuits @@ -592,11 +601,11 @@ Initialize, ) from .quantum_volume import QuantumVolume, quantum_volume -from .fourier_checking import FourierChecking -from .graph_state import GraphState -from .hidden_linear_function import HiddenLinearFunction +from .fourier_checking import FourierChecking, fourier_checking +from .graph_state import GraphState, GraphStateGate +from .hidden_linear_function import HiddenLinearFunction, hidden_linear_function from .iqp import IQP, iqp, random_iqp -from .phase_estimation import PhaseEstimation +from .phase_estimation import PhaseEstimation, phase_estimation from .grover_operator import GroverOperator from .phase_oracle import PhaseOracle -from .overlap import UnitaryOverlap +from .overlap import UnitaryOverlap, unitary_overlap diff --git a/qiskit/circuit/library/data_preparation/pauli_feature_map.py b/qiskit/circuit/library/data_preparation/pauli_feature_map.py index 9b08a408ec24..b511edd9513e 100644 --- a/qiskit/circuit/library/data_preparation/pauli_feature_map.py +++ b/qiskit/circuit/library/data_preparation/pauli_feature_map.py @@ -585,7 +585,10 @@ def basis_change(circuit, inverse=False): if pauli == "X": circuit.h(i) elif pauli == "Y": - circuit.rx(-np.pi / 2 if inverse else np.pi / 2, i) + if inverse: + circuit.sxdg(i) + else: + circuit.sx(i) def cx_chain(circuit, inverse=False): num_cx = len(indices) - 1 diff --git a/qiskit/circuit/library/fourier_checking.py b/qiskit/circuit/library/fourier_checking.py index 9035fbc360d4..b5012b4beeef 100644 --- a/qiskit/circuit/library/fourier_checking.py +++ b/qiskit/circuit/library/fourier_checking.py @@ -12,13 +12,14 @@ """Fourier checking circuit.""" -from typing import List - +from collections.abc import Sequence import math + from qiskit.circuit import QuantumCircuit from qiskit.circuit.exceptions import CircuitError +from qiskit.utils.deprecation import deprecate_func -from .generalized_gates.diagonal import Diagonal +from .generalized_gates.diagonal import Diagonal, DiagonalGate class FourierChecking(QuantumCircuit): @@ -52,7 +53,12 @@ class FourierChecking(QuantumCircuit): `arXiv:1411.5729 `_ """ - def __init__(self, f: List[int], g: List[int]) -> None: + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.fourier_checking instead.", + pending=True, + ) + def __init__(self, f: Sequence[int], g: Sequence[int]) -> None: """Create Fourier checking circuit. Args: @@ -81,17 +87,72 @@ def __init__(self, f: List[int], g: List[int]) -> None: "{1, -1}." ) - circuit = QuantumCircuit(num_qubits, name=f"fc: {f}, {g}") - + # This definition circuit is not replaced by the circuit produced by fourier_checking, + # as the latter produces a slightly different circuit, with DiagonalGates instead + # of Diagonal circuits. + circuit = QuantumCircuit(int(num_qubits), name=f"fc: {f}, {g}") circuit.h(circuit.qubits) - circuit.compose(Diagonal(f), inplace=True) - circuit.h(circuit.qubits) - circuit.compose(Diagonal(g), inplace=True) - circuit.h(circuit.qubits) - super().__init__(*circuit.qregs, name=circuit.name) self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + + +def fourier_checking(f: Sequence[int], g: Sequence[int]) -> QuantumCircuit: + """Fourier checking circuit. + + The circuit for the Fourier checking algorithm, introduced in [1], + involves a layer of Hadamards, the function :math:`f`, another layer of + Hadamards, the function :math:`g`, followed by a final layer of Hadamards. + The functions :math:`f` and :math:`g` are classical functions realized + as phase oracles (diagonal operators with {-1, 1} on the diagonal). + + The probability of observing the all-zeros string is :math:`p(f,g)`. + The algorithm solves the promise Fourier checking problem, + which decides if f is correlated with the Fourier transform + of g, by testing if :math:`p(f,g) <= 0.01` or :math:`p(f,g) >= 0.05`, + promised that one or the other of these is true. + + The functions :math:`f` and :math:`g` are currently implemented + from their truth tables but could be represented concisely and + implemented efficiently for special classes of functions. + + Fourier checking is a special case of :math:`k`-fold forrelation [2]. + + **Reference Circuit:** + + .. plot:: + :include-source: + + from qiskit.circuit.library import fourier_checking + circuit = fourier_checking([1, -1, -1, -1], [1, 1, -1, -1]) + circuit.draw('mpl') + + **Reference:** + + [1] S. Aaronson, BQP and the Polynomial Hierarchy, 2009 (Section 3.2). + `arXiv:0910.4698 `_ + + [2] S. Aaronson, A. Ambainis, Forrelation: a problem that + optimally separates quantum from classical computing, 2014. + `arXiv:1411.5729 `_ + """ + num_qubits = math.log2(len(f)) + + if len(f) != len(g) or num_qubits == 0 or not num_qubits.is_integer(): + raise CircuitError( + "The functions f and g must be given as truth " + "tables, each as a list of 2**n entries of " + "{1, -1}." + ) + num_qubits = int(num_qubits) + + circuit = QuantumCircuit(num_qubits, name=f"fc: {f}, {g}") + circuit.h(circuit.qubits) + circuit.append(DiagonalGate(f), range(num_qubits)) + circuit.h(circuit.qubits) + circuit.append(DiagonalGate(g), range(num_qubits)) + circuit.h(circuit.qubits) + return circuit diff --git a/qiskit/circuit/library/graph_state.py b/qiskit/circuit/library/graph_state.py index 89d1edb035ff..79c0dfbc864d 100644 --- a/qiskit/circuit/library/graph_state.py +++ b/qiskit/circuit/library/graph_state.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2020. +# (C) Copyright IBM 2017, 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 @@ -10,13 +10,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Graph State circuit.""" +"""Graph State circuit and gate.""" from __future__ import annotations import numpy as np -from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.circuit.quantumcircuit import QuantumCircuit, Gate from qiskit.circuit.exceptions import CircuitError +from qiskit.utils.deprecation import deprecate_func class GraphState(QuantumCircuit): @@ -56,6 +57,11 @@ class GraphState(QuantumCircuit): `arXiv:1512.07892 `_ """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.GraphStateGate instead.", + pending=True, + ) def __init__(self, adjacency_matrix: list | np.ndarray) -> None: """Create graph state preparation circuit. @@ -73,14 +79,91 @@ def __init__(self, adjacency_matrix: list | np.ndarray) -> None: if not np.allclose(adjacency_matrix, adjacency_matrix.transpose()): raise CircuitError("The adjacency matrix must be symmetric.") + graph_state_gate = GraphStateGate(adjacency_matrix) + super().__init__(graph_state_gate.num_qubits, name=f"graph: {adjacency_matrix}") + self.compose(graph_state_gate, qubits=self.qubits, inplace=True) + + +class GraphStateGate(Gate): + r"""A gate representing a graph state. + + Given a graph G = (V, E), with the set of vertices V and the set of edges E, + the corresponding graph state is defined as + + .. math:: + + |G\rangle = \prod_{(a,b) \in E} CZ_{(a,b)} {|+\rangle}^{\otimes V} + + Such a state can be prepared by first preparing all qubits in the :math:`+` + state, then applying a :math:`CZ` gate for each corresponding graph edge. + + Graph state preparation circuits are Clifford circuits, and thus + easy to simulate classically. However, by adding a layer of measurements + in a product basis at the end, there is evidence that the circuit becomes + hard to simulate [2]. + + **Reference Circuit:** + + .. plot:: + :include-source: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import GraphStateGate + import rustworkx as rx + + G = rx.generators.cycle_graph(5) + circuit = QuantumCircuit(5) + circuit.append(GraphStateGate(rx.adjacency_matrix(G)), [0, 1, 2, 3, 4]) + circuit.decompose().draw('mpl') + + **References:** + + [1] M. Hein, J. Eisert, H.J. Briegel, Multi-party Entanglement in Graph States, + `arXiv:0307130 `_ + [2] D. Koh, Further Extensions of Clifford Circuits & their Classical Simulation Complexities. + `arXiv:1512.07892 `_ + """ + + def __init__(self, adjacency_matrix: list | np.ndarray) -> None: + """ + Args: + adjacency_matrix: input graph as n-by-n list of 0-1 lists + + Raises: + CircuitError: If adjacency_matrix is not symmetric. + + The gate represents a graph state with the given adjacency matrix. + """ + + adjacency_matrix = np.asarray(adjacency_matrix) + if not np.allclose(adjacency_matrix, adjacency_matrix.transpose()): + raise CircuitError("The adjacency matrix must be symmetric.") num_qubits = len(adjacency_matrix) - circuit = QuantumCircuit(num_qubits, name=f"graph: {adjacency_matrix}") - circuit.h(range(num_qubits)) - for i in range(num_qubits): - for j in range(i + 1, num_qubits): + super().__init__(name="graph_state", num_qubits=num_qubits, params=[adjacency_matrix]) + + def _define(self): + adjacency_matrix = self.adjacency_matrix + circuit = QuantumCircuit(self.num_qubits, name=self.name) + circuit.h(range(self.num_qubits)) + for i in range(self.num_qubits): + for j in range(i + 1, self.num_qubits): if adjacency_matrix[i][j] == 1: circuit.cz(i, j) - - super().__init__(*circuit.qregs, name=circuit.name) - self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + self.definition = circuit + + def validate_parameter(self, parameter): + """Parameter validation""" + return parameter + + @property + def adjacency_matrix(self): + """Returns the adjacency matrix.""" + return self.params[0] + + def __eq__(self, other): + return ( + isinstance(other, GraphStateGate) + and self.num_qubits == other.num_qubits + and np.all(self.adjacency_matrix == other.adjacency_matrix) + ) diff --git a/qiskit/circuit/library/hidden_linear_function.py b/qiskit/circuit/library/hidden_linear_function.py index b68fda7f8fc5..7acaaa7aa72e 100644 --- a/qiskit/circuit/library/hidden_linear_function.py +++ b/qiskit/circuit/library/hidden_linear_function.py @@ -12,11 +12,12 @@ """Hidden Linear Function circuit.""" -from typing import Union, List +from __future__ import annotations import numpy as np from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.exceptions import CircuitError +from qiskit.utils.deprecation import deprecate_func class HiddenLinearFunction(QuantumCircuit): @@ -67,7 +68,12 @@ class HiddenLinearFunction(QuantumCircuit): `arXiv:1704.00690 `_ """ - def __init__(self, adjacency_matrix: Union[List[List[int]], np.ndarray]) -> None: + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.hidden_linear_function instead.", + pending=True, + ) + def __init__(self, adjacency_matrix: list | np.ndarray) -> None: """Create new HLF circuit. Args: @@ -77,22 +83,79 @@ def __init__(self, adjacency_matrix: Union[List[List[int]], np.ndarray]) -> None Raises: CircuitError: If A is not symmetric. """ - adjacency_matrix = np.asarray(adjacency_matrix) - if not np.allclose(adjacency_matrix, adjacency_matrix.transpose()): - raise CircuitError("The adjacency matrix must be symmetric.") - - num_qubits = len(adjacency_matrix) - circuit = QuantumCircuit(num_qubits, name=f"hlf: {adjacency_matrix}") - - circuit.h(range(num_qubits)) - for i in range(num_qubits): - for j in range(i + 1, num_qubits): - if adjacency_matrix[i][j]: - circuit.cz(i, j) - for i in range(num_qubits): - if adjacency_matrix[i][i]: - circuit.s(i) - circuit.h(range(num_qubits)) - + circuit = hidden_linear_function(adjacency_matrix) super().__init__(*circuit.qregs, name=circuit.name) - self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + self.append(circuit.to_gate(), self.qubits) + + +def hidden_linear_function(adjacency_matrix: list | np.ndarray) -> QuantumCircuit: + r"""Circuit to solve the hidden linear function problem. + + The 2D Hidden Linear Function problem is determined by a 2D adjacency + matrix A, where only elements that are nearest-neighbor on a grid have + non-zero entries. Each row/column corresponds to one binary variable + :math:`x_i`. + + The hidden linear function problem is as follows: + + Consider the quadratic form + + .. math:: + + q(x) = \sum_{i,j=1}^{n}{x_i x_j} ~(\mathrm{mod}~ 4) + + and restrict :math:`q(x)` onto the nullspace of A. This results in a linear + function. + + .. math:: + + 2 \sum_{i=1}^{n}{z_i x_i} ~(\mathrm{mod}~ 4) \forall x \in \mathrm{Ker}(A) + + and the goal is to recover this linear function (equivalently a vector + :math:`[z_0, ..., z_{n-1}]`). There can be multiple solutions. + + In [1] it is shown that the present circuit solves this problem + on a quantum computer in constant depth, whereas any corresponding + solution on a classical computer would require circuits that grow + logarithmically with :math:`n`. Thus this circuit is an example + of quantum advantage with shallow circuits. + + **Reference Circuit:** + + .. plot:: + :include-source: + + from qiskit.circuit.library import hidden_linear_function + A = [[1, 1, 0], [1, 0, 1], [0, 1, 1]] + circuit = hidden_linear_function(A) + circuit.draw('mpl') + + Args: + adjacency_matrix: a symmetric n-by-n list of 0-1 lists. + n will be the number of qubits. + + Raises: + CircuitError: If A is not symmetric. + + **Reference:** + + [1] S. Bravyi, D. Gosset, R. Koenig, Quantum Advantage with Shallow Circuits, 2017. + `arXiv:1704.00690 `_ + """ + adjacency_matrix = np.asarray(adjacency_matrix) + if not np.allclose(adjacency_matrix, adjacency_matrix.transpose()): + raise CircuitError("The adjacency matrix must be symmetric.") + + num_qubits = len(adjacency_matrix) + circuit = QuantumCircuit(num_qubits, name=f"hlf: {adjacency_matrix}") + + circuit.h(range(num_qubits)) + for i in range(num_qubits): + for j in range(i + 1, num_qubits): + if adjacency_matrix[i][j]: + circuit.cz(i, j) + for i in range(num_qubits): + if adjacency_matrix[i][i]: + circuit.s(i) + circuit.h(range(num_qubits)) + return circuit diff --git a/qiskit/circuit/library/overlap.py b/qiskit/circuit/library/overlap.py index f6ae5fd6ebdd..6f444bebf0fb 100644 --- a/qiskit/circuit/library/overlap.py +++ b/qiskit/circuit/library/overlap.py @@ -16,6 +16,7 @@ from qiskit.circuit.parametervector import ParameterVector from qiskit.circuit.exceptions import CircuitError from qiskit.circuit import Barrier +from qiskit.utils.deprecation import deprecate_func class UnitaryOverlap(QuantumCircuit): @@ -41,13 +42,13 @@ class UnitaryOverlap(QuantumCircuit): from qiskit.circuit.library import EfficientSU2, UnitaryOverlap from qiskit.primitives import Sampler - # get two circuit to prepare states of which we comput the overlap + # get two circuit to prepare states of which we compute the overlap circuit = EfficientSU2(2, reps=1) unitary1 = circuit.assign_parameters(np.random.random(circuit.num_parameters)) unitary2 = circuit.assign_parameters(np.random.random(circuit.num_parameters)) # create the overlap circuit - overlap = UnitaryOverap(unitary1, unitary2) + overlap = UnitaryOverlap(unitary1, unitary2) # sample from the overlap sampler = Sampler(options={"shots": 100}) @@ -58,6 +59,11 @@ class UnitaryOverlap(QuantumCircuit): """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.unitary_overlap instead.", + pending=True, + ) def __init__( self, unitary1: QuantumCircuit, @@ -80,30 +86,9 @@ def __init__( CircuitError: Number of qubits in ``unitary1`` and ``unitary2`` does not match. CircuitError: Inputs contain measurements and/or resets. """ - # check inputs are valid - if unitary1.num_qubits != unitary2.num_qubits: - raise CircuitError( - f"Number of qubits in unitaries does " - f"not match: {unitary1.num_qubits} != {unitary2.num_qubits}." - ) - - unitaries = [unitary1, unitary2] - for unitary in unitaries: - _check_unitary(unitary) - - # Vectors of new parameters, if any. Need the unitaries in a list here to ensure - # we can overwrite them. - for i, prefix in enumerate([prefix1, prefix2]): - if unitaries[i].num_parameters > 0: - new_params = ParameterVector(prefix, unitaries[i].num_parameters) - unitaries[i] = unitaries[i].assign_parameters(new_params) - - # Generate the actual overlap circuit - super().__init__(unitaries[0].num_qubits, name="UnitaryOverlap") - self.compose(unitaries[0], inplace=True) - if insert_barrier: - self.barrier() - self.compose(unitaries[1].inverse(), inplace=True) + circuit = unitary_overlap(unitary1, unitary2, prefix1, prefix2, insert_barrier) + super().__init__(*circuit.qregs, name=circuit.name) + self.compose(circuit, qubits=self.qubits, inplace=True) def _check_unitary(circuit): @@ -115,3 +100,83 @@ def _check_unitary(circuit): "One or more instructions cannot be converted to" f' a gate. "{instruction.operation.name}" is not a gate instruction' ) + + +def unitary_overlap( + unitary1: QuantumCircuit, + unitary2: QuantumCircuit, + prefix1: str = "p1", + prefix2: str = "p2", + insert_barrier: bool = False, +) -> QuantumCircuit: + r"""Circuit that returns the overlap between two unitaries :math:`U_2^{\dag} U_1`. + + The input quantum circuits must represent unitary operations, since they must be invertible. + If the inputs will have parameters, they are replaced by :class:`.ParameterVector`\s with + names `"p1"` (for circuit ``unitary1``) and `"p2"` (for circuit ``unitary_2``) in the output + circuit. + + This circuit is usually employed in computing the fidelity: + + .. math:: + + \left|\langle 0| U_2^{\dag} U_1|0\rangle\right|^{2} + + by computing the probability of being in the all-zeros bit-string, or equivalently, + the expectation value of projector :math:`|0\rangle\langle 0|`. + + **Reference Circuit:** + + .. plot:: + :include-source: + + import numpy as np + from qiskit.circuit.library import EfficientSU2, unitary_overlap + + # get two circuit to prepare states of which we compute the overlap + circuit = EfficientSU2(2, reps=1) + unitary1 = circuit.assign_parameters(np.random.random(circuit.num_parameters)) + unitary2 = circuit.assign_parameters(np.random.random(circuit.num_parameters)) + + # create the overlap circuit + overlap = unitary_overlap(unitary1, unitary2) + overlap.draw('mpl') + + Args: + unitary1: Unitary acting on the ket vector. + unitary2: Unitary whose inverse operates on the bra vector. + prefix1: The name of the parameter vector associated to ``unitary1``, + if it is parameterized. Defaults to ``"p1"``. + prefix2: The name of the parameter vector associated to ``unitary2``, + if it is parameterized. Defaults to ``"p2"``. + insert_barrier: Whether to insert a barrier between the two unitaries. + + Raises: + CircuitError: Number of qubits in ``unitary1`` and ``unitary2`` does not match. + CircuitError: Inputs contain measurements and/or resets. + """ + # check inputs are valid + if unitary1.num_qubits != unitary2.num_qubits: + raise CircuitError( + f"Number of qubits in unitaries does " + f"not match: {unitary1.num_qubits} != {unitary2.num_qubits}." + ) + + unitaries = [unitary1, unitary2] + for unitary in unitaries: + _check_unitary(unitary) + + # Vectors of new parameters, if any. Need the unitaries in a list here to ensure + # we can overwrite them. + for i, prefix in enumerate([prefix1, prefix2]): + if unitaries[i].num_parameters > 0: + new_params = ParameterVector(prefix, unitaries[i].num_parameters) + unitaries[i] = unitaries[i].assign_parameters(new_params) + + # Generate the actual overlap circuit + circuit = QuantumCircuit(unitaries[0].num_qubits, name="UnitaryOverlap") + circuit.compose(unitaries[0], inplace=True) + if insert_barrier: + circuit.barrier() + circuit.compose(unitaries[1].inverse(), inplace=True) + return circuit diff --git a/qiskit/circuit/library/pauli_evolution.py b/qiskit/circuit/library/pauli_evolution.py index 23ccd065aad7..f7ab0393f32c 100644 --- a/qiskit/circuit/library/pauli_evolution.py +++ b/qiskit/circuit/library/pauli_evolution.py @@ -14,10 +14,11 @@ from __future__ import annotations -from typing import Union, Optional, TYPE_CHECKING +from typing import TYPE_CHECKING import numpy as np from qiskit.circuit.gate import Gate +from qiskit.circuit.quantumcircuit import ParameterValueType from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.quantum_info import Pauli, SparsePauliOp @@ -85,10 +86,10 @@ class PauliEvolutionGate(Gate): def __init__( self, - operator, - time: Union[int, float, ParameterExpression] = 1.0, - label: Optional[str] = None, - synthesis: Optional[EvolutionSynthesis] = None, + operator: Pauli | SparsePauliOp | list[Pauli | SparsePauliOp], + time: ParameterValueType = 1.0, + label: str | None = None, + synthesis: EvolutionSynthesis | None = None, ) -> None: """ Args: @@ -123,7 +124,7 @@ class docstring for an example. self.synthesis = synthesis @property - def time(self) -> Union[float, ParameterExpression]: + def time(self) -> ParameterValueType: """Return the evolution time as stored in the gate parameters. Returns: @@ -132,7 +133,7 @@ def time(self) -> Union[float, ParameterExpression]: return self.params[0] @time.setter - def time(self, time: Union[float, ParameterExpression]) -> None: + def time(self, time: ParameterValueType) -> None: """Set the evolution time. Args: @@ -144,9 +145,7 @@ def _define(self): """Unroll, where the default synthesis is matrix based.""" self.definition = self.synthesis.synthesize(self) - def validate_parameter( - self, parameter: Union[int, float, ParameterExpression] - ) -> Union[float, ParameterExpression]: + def validate_parameter(self, parameter: ParameterValueType) -> ParameterValueType: """Gate parameters should be int, float, or ParameterExpression""" if isinstance(parameter, int): parameter = float(parameter) diff --git a/qiskit/circuit/library/phase_estimation.py b/qiskit/circuit/library/phase_estimation.py index 3311adf46324..8ff441eb705e 100644 --- a/qiskit/circuit/library/phase_estimation.py +++ b/qiskit/circuit/library/phase_estimation.py @@ -12,11 +12,11 @@ """Phase estimation circuit.""" -from typing import Optional +from __future__ import annotations from qiskit.circuit import QuantumCircuit, QuantumRegister - -from .basis_change import QFT +from qiskit.utils.deprecation import deprecate_func +from qiskit.circuit.library import QFT class PhaseEstimation(QuantumCircuit): @@ -49,11 +49,16 @@ class PhaseEstimation(QuantumCircuit): """ + @deprecate_func( + since="1.3", + additional_msg="Use qiskit.circuit.library.phase_estimation instead.", + pending=True, + ) def __init__( self, num_evaluation_qubits: int, unitary: QuantumCircuit, - iqft: Optional[QuantumCircuit] = None, + iqft: QuantumCircuit | None = None, name: str = "QPE", ) -> None: """ @@ -97,3 +102,74 @@ def __init__( super().__init__(*circuit.qregs, name=circuit.name) self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) + + +def phase_estimation( + num_evaluation_qubits: int, + unitary: QuantumCircuit, + name: str = "QPE", +) -> QuantumCircuit: + r"""Phase Estimation circuit. + + In the Quantum Phase Estimation (QPE) algorithm [1, 2, 3], the Phase Estimation circuit is used + to estimate the phase :math:`\phi` of an eigenvalue :math:`e^{2\pi i\phi}` of a unitary operator + :math:`U`, provided with the corresponding eigenstate :math:`|\psi\rangle`. + That is + + .. math:: + + U|\psi\rangle = e^{2\pi i\phi} |\psi\rangle + + This estimation (and thereby this circuit) is a central routine to several well-known + algorithms, such as Shor's algorithm or Quantum Amplitude Estimation. + + Args: + num_evaluation_qubits: The number of evaluation qubits. + unitary: The unitary operation :math:`U` which will be repeated and controlled. + name: The name of the circuit. + + **Reference Circuit:** + + .. plot:: + :include-source: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import phase_estimation + unitary = QuantumCircuit(2) + unitary.x(0) + unitary.y(1) + circuit = phase_estimation(3, unitary) + circuit.draw('mpl') + + **References:** + + [1]: Kitaev, A. Y. (1995). Quantum measurements and the Abelian Stabilizer Problem. 1–22. + `quant-ph/9511026 `_ + + [2]: Michael A. Nielsen and Isaac L. Chuang. 2011. + Quantum Computation and Quantum Information: 10th Anniversary Edition (10th ed.). + Cambridge University Press, New York, NY, USA. + + [3]: Qiskit + `textbook `_ + + """ + # pylint: disable=cyclic-import + from qiskit.circuit.library import PermutationGate, QFTGate + + qr_eval = QuantumRegister(num_evaluation_qubits, "eval") + qr_state = QuantumRegister(unitary.num_qubits, "q") + circuit = QuantumCircuit(qr_eval, qr_state, name=name) + + circuit.h(qr_eval) # hadamards on evaluation qubits + + for j in range(num_evaluation_qubits): # controlled powers + circuit.compose(unitary.power(2**j).control(), qubits=[j] + qr_state[:], inplace=True) + + circuit.append(QFTGate(num_evaluation_qubits).inverse(), qr_eval[:]) + + reversal_pattern = list(reversed(range(num_evaluation_qubits))) + circuit.append(PermutationGate(reversal_pattern), qr_eval[:]) + + return circuit diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index fd3c0b693cef..b3901efefebe 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2104,14 +2104,14 @@ def map_vars(op): is_control_flow = isinstance(n_op, ControlFlowOp) if ( not is_control_flow - and (condition := getattr(n_op, "condition", None)) is not None + and (condition := getattr(n_op, "_condition", None)) is not None ): n_op = n_op.copy() if n_op is op and copy else n_op n_op.condition = variable_mapper.map_condition(condition) elif is_control_flow: n_op = n_op.replace_blocks(recurse_block(block) for block in n_op.blocks) if isinstance(n_op, (IfElseOp, WhileLoopOp)): - n_op.condition = variable_mapper.map_condition(n_op.condition) + n_op.condition = variable_mapper.map_condition(n_op._condition) elif isinstance(n_op, SwitchCaseOp): n_op.target = variable_mapper.map_target(n_op.target) elif isinstance(n_op, Store): @@ -2735,7 +2735,10 @@ def has_parameter(self, name_or_param: str | Parameter, /) -> bool: """ if isinstance(name_or_param, str): return self.get_parameter(name_or_param, None) is not None - return self.get_parameter(name_or_param.name) == name_or_param + return ( + isinstance(name_or_param, Parameter) + and self.get_parameter(name_or_param.name, None) == name_or_param + ) @typing.overload def get_var(self, name: str, default: T) -> Union[expr.Var, T]: ... @@ -3522,7 +3525,7 @@ def update_from_expr(objects, node): for instruction in self._data: objects = set(itertools.chain(instruction.qubits, instruction.clbits)) - if (condition := getattr(instruction.operation, "condition", None)) is not None: + if (condition := getattr(instruction.operation, "_condition", None)) is not None: objects.update(_builder_utils.condition_resources(condition).clbits) if isinstance(condition, expr.Expr): update_from_expr(objects, condition) @@ -3625,7 +3628,7 @@ def num_connected_components(self, unitary_only: bool = False) -> int: else: args = instruction.qubits + instruction.clbits num_qargs = len(args) + ( - 1 if getattr(instruction.operation, "condition", None) else 0 + 1 if getattr(instruction.operation, "_condition", None) else 0 ) if num_qargs >= 2 and not getattr(instruction.operation, "_directive", False): @@ -4373,30 +4376,57 @@ def assign_parameters( # pylint: disable=missing-raises-doc if isinstance(parameters, collections.abc.Mapping): raw_mapping = parameters if flat_input else self._unroll_param_dict(parameters) - our_parameters = self._data.unsorted_parameters() - if strict and (extras := raw_mapping.keys() - our_parameters): + if strict and ( + extras := [ + parameter for parameter in raw_mapping if not self.has_parameter(parameter) + ] + ): raise CircuitError( f"Cannot bind parameters ({', '.join(str(x) for x in extras)}) not present in" " the circuit." ) - parameter_binds = _ParameterBindsDict(raw_mapping, our_parameters) - target._data.assign_parameters_mapping(parameter_binds) + + def create_mapping_view(): + return raw_mapping + + target._data.assign_parameters_mapping(raw_mapping) else: - parameter_binds = _ParameterBindsSequence(target._data.parameters, parameters) + # This should be a cache retrieval, since we warmed the cache. We need to keep hold of + # what the parameters were before anything is assigned, because we assign parameters in + # the calibrations (which aren't tracked in the internal parameter table) after, which + # would change what we create. We don't make the full Python-space mapping object of + # parameters to values eagerly because 99.9% of the time we don't need it, and it's + # relatively expensive to do for large numbers of parameters. + initial_parameters = target._data.parameters + + def create_mapping_view(): + return dict(zip(initial_parameters, parameters)) + target._data.assign_parameters_iterable(parameters) # Finally, assign the parameters inside any of the calibrations. We don't track these in - # the `ParameterTable`, so we manually reconstruct things. + # the `ParameterTable`, so we manually reconstruct things. We lazily construct the mapping + # `{parameter: bound_value}` the first time we encounter a binding (we have to scan for + # this, because calibrations don't use a parameter-table lookup), rather than always paying + # the cost - most circuits don't have parametric calibrations, and it's expensive. + mapping_view = None + def map_calibration(qubits, parameters, schedule): + # All calls to this function should share the same `{Parameter: bound_value}` mapping, + # which we only want to lazily construct a single time. + nonlocal mapping_view + if mapping_view is None: + mapping_view = create_mapping_view() + modified = False new_parameters = list(parameters) for i, parameter in enumerate(new_parameters): if not isinstance(parameter, ParameterExpression): continue - if not (contained := parameter.parameters & parameter_binds.mapping.keys()): + if not (contained := parameter.parameters & mapping_view.keys()): continue for to_bind in contained: - parameter = parameter.assign(to_bind, parameter_binds.mapping[to_bind]) + parameter = parameter.assign(to_bind, mapping_view[to_bind]) if not parameter.parameters: parameter = parameter.numeric() if isinstance(parameter, complex): @@ -4404,7 +4434,7 @@ def map_calibration(qubits, parameters, schedule): new_parameters[i] = parameter modified = True if modified: - schedule.assign_parameters(parameter_binds.mapping) + schedule.assign_parameters(mapping_view) return (qubits, tuple(new_parameters)), schedule target._calibrations = defaultdict( @@ -6728,42 +6758,6 @@ def _validate_expr(circuit_scope: CircuitScopeInterface, node: expr.Expr) -> exp return node -class _ParameterBindsDict: - __slots__ = ("mapping", "allowed_keys") - - def __init__(self, mapping, allowed_keys): - self.mapping = mapping - self.allowed_keys = allowed_keys - - def items(self): - """Iterator through all the keys in the mapping that we care about. Wrapping the main - mapping allows us to avoid reconstructing a new 'dict', but just use the given 'mapping' - without any copy / reconstruction.""" - for parameter, value in self.mapping.items(): - if parameter in self.allowed_keys: - yield parameter, value - - -class _ParameterBindsSequence: - __slots__ = ("parameters", "values", "mapping_cache") - - def __init__(self, parameters, values): - self.parameters = parameters - self.values = values - self.mapping_cache = None - - def items(self): - """Iterator through all the keys in the mapping that we care about.""" - return zip(self.parameters, self.values) - - @property - def mapping(self): - """Cached version of a mapping. This is only generated on demand.""" - if self.mapping_cache is None: - self.mapping_cache = dict(zip(self.parameters, self.values)) - return self.mapping_cache - - def _bit_argument_conversion(specifier, bit_sequence, bit_set, type_) -> list[Bit]: """Get the list of bits referred to by the specifier ``specifier``. diff --git a/qiskit/circuit/singleton.py b/qiskit/circuit/singleton.py index 874b979ff588..a315f8aaa619 100644 --- a/qiskit/circuit/singleton.py +++ b/qiskit/circuit/singleton.py @@ -251,6 +251,7 @@ class XGate(Gate, metaclass=_SingletonMeta, overrides=_SingletonGateOverrides): import functools +from qiskit.utils import deprecate_func from .instruction import Instruction from .gate import Gate from .controlledgate import ControlledGate, _ctrl_state_to_int @@ -489,6 +490,7 @@ class they are providing overrides for has more lazy attributes or user-exposed instruction._params = _frozenlist(instruction._params) return instruction + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): return self.to_mutable().c_if(classical, val) diff --git a/qiskit/circuit/store.py b/qiskit/circuit/store.py index 6bbc5439332d..43e81ce61056 100644 --- a/qiskit/circuit/store.py +++ b/qiskit/circuit/store.py @@ -16,6 +16,7 @@ import typing +from qiskit.utils import deprecate_func from .exceptions import CircuitError from .classical import expr, types from .instruction import Instruction @@ -88,6 +89,7 @@ def rvalue(self): """Get the r-value :class:`~.expr.Expr` node that is being written into the l-value.""" return self.params[1] + @deprecate_func(since="1.3.0", removal_timeline="in 2.0.0") def c_if(self, classical, val): """:meta hidden:""" raise NotImplementedError( diff --git a/qiskit/converters/circuit_to_instruction.py b/qiskit/converters/circuit_to_instruction.py index 4487a65e08fd..db314eee267e 100644 --- a/qiskit/converters/circuit_to_instruction.py +++ b/qiskit/converters/circuit_to_instruction.py @@ -125,7 +125,7 @@ def fix_condition(op): if (out := operation_map.get(original_id)) is not None: return out - condition = getattr(op, "condition", None) + condition = getattr(op, "_condition", None) if condition: reg, val = condition if isinstance(reg, Clbit): diff --git a/qiskit/dagcircuit/collect_blocks.py b/qiskit/dagcircuit/collect_blocks.py index c5c7b49144f7..99c51d2e3600 100644 --- a/qiskit/dagcircuit/collect_blocks.py +++ b/qiskit/dagcircuit/collect_blocks.py @@ -306,7 +306,7 @@ def split_block_into_layers(block: list[DAGOpNode | DAGDepNode]): cur_bits = set(node.qargs) cur_bits.update(node.cargs) - cond = getattr(node.op, "condition", None) + cond = getattr(node.op, "_condition", None) if cond is not None: cur_bits.update(condition_resources(cond).clbits) @@ -356,7 +356,7 @@ def collapse_to_operation(self, blocks, collapse_fn): for node in block: cur_qubits.update(node.qargs) cur_clbits.update(node.cargs) - cond = getattr(node.op, "condition", None) + cond = getattr(node.op, "_condition", None) if cond is not None: cur_clbits.update(condition_resources(cond).clbits) if isinstance(cond[0], ClassicalRegister): @@ -378,7 +378,7 @@ def collapse_to_operation(self, blocks, collapse_fn): for node in block: instructions = qc.append(CircuitInstruction(node.op, node.qargs, node.cargs)) - cond = getattr(node.op, "condition", None) + cond = getattr(node.op, "_condition", None) if cond is not None: instructions.c_if(*cond) diff --git a/qiskit/dagcircuit/dagdependency.py b/qiskit/dagcircuit/dagdependency.py index 2658cd731d69..63e9114a6ca1 100644 --- a/qiskit/dagcircuit/dagdependency.py +++ b/qiskit/dagcircuit/dagdependency.py @@ -409,13 +409,13 @@ def _create_op_node(self, operation, qargs, cargs): for elem in qargs: qindices_list.append(self.qubits.index(elem)) - if getattr(operation, "condition", None): + if getattr(operation, "_condition", None): # The change to handling operation.condition follows code patterns in quantum_circuit.py. # However: # (1) cindices_list are specific to template optimization and should not be computed # in this place. # (2) Template optimization pass needs currently does not handle general conditions. - cond_bits = condition_resources(operation.condition).clbits + cond_bits = condition_resources(operation._condition).clbits cindices_list = [self.clbits.index(clbit) for clbit in cond_bits] else: cindices_list = [] @@ -609,7 +609,7 @@ def replace_block_with_op(self, node_block, op, wire_pos_map, cycle_check=True): for nd in node_block: block_qargs |= set(nd.qargs) block_cargs |= set(nd.cargs) - cond = getattr(nd.op, "condition", None) + cond = getattr(nd.op, "_condition", None) if cond is not None: block_cargs.update(condition_resources(cond).clbits) diff --git a/qiskit/dagcircuit/dagnode.py b/qiskit/dagcircuit/dagnode.py index 60e2a7465707..151861d8028c 100644 --- a/qiskit/dagcircuit/dagnode.py +++ b/qiskit/dagcircuit/dagnode.py @@ -92,8 +92,8 @@ def key(var): def _condition_op_eq(node1, node2, bit_indices1, bit_indices2): - cond1 = node1.op.condition - cond2 = node2.op.condition + cond1 = node1.condition + cond2 = node2.condition if isinstance(cond1, expr.Expr) and isinstance(cond2, expr.Expr): if not expr.structurally_equivalent( cond1, cond2, _make_expr_key(bit_indices1), _make_expr_key(bit_indices2) diff --git a/qiskit/primitives/backend_estimator.py b/qiskit/primitives/backend_estimator.py index 722ee900ceea..5f9f8d20c75e 100644 --- a/qiskit/primitives/backend_estimator.py +++ b/qiskit/primitives/backend_estimator.py @@ -44,13 +44,15 @@ def _run_circuits( circuits: QuantumCircuit | list[QuantumCircuit], backend: BackendV1 | BackendV2, + clear_metadata: bool = True, **run_options, ) -> tuple[list[Result], list[dict]]: """Remove metadata of circuits and run the circuits on a backend. Args: circuits: The circuits backend: The backend - monitor: Enable job minotor if True + clear_metadata: Clear circuit metadata before passing to backend.run if + True. **run_options: run_options Returns: The result and the metadata of the circuits @@ -60,7 +62,8 @@ def _run_circuits( metadata = [] for circ in circuits: metadata.append(circ.metadata) - circ.metadata = {} + if clear_metadata: + circ.metadata = {} if isinstance(backend, BackendV1): max_circuits = getattr(backend.configuration(), "max_experiments", None) elif isinstance(backend, BackendV2): diff --git a/qiskit/primitives/backend_sampler_v2.py b/qiskit/primitives/backend_sampler_v2.py index bac0bec5eaed..40a6d8560e07 100644 --- a/qiskit/primitives/backend_sampler_v2.py +++ b/qiskit/primitives/backend_sampler_v2.py @@ -17,12 +17,13 @@ import warnings from collections import defaultdict from dataclasses import dataclass -from typing import Iterable +from typing import Any, Iterable, Union import numpy as np from numpy.typing import NDArray from qiskit.circuit import QuantumCircuit +from qiskit.exceptions import QiskitError from qiskit.primitives.backend_estimator import _run_circuits from qiskit.primitives.base import BaseSamplerV2 from qiskit.primitives.containers import ( @@ -53,6 +54,11 @@ class Options: Default: None. """ + run_options: dict[str, Any] | None = None + """A dictionary of options to pass to the backend's ``run()`` method. + Default: None (no option passed to backend's ``run`` method) + """ + @dataclass class _MeasureInfo: @@ -62,6 +68,16 @@ class _MeasureInfo: start: int +ResultMemory = Union[list[str], list[list[float]], list[list[list[float]]]] +"""Type alias for possible level 2 and level 1 result memory formats. For level +2, the format is a list of bit strings. For level 1, format can be either a +list of I/Q pairs (list with two floats) for each memory slot if using +``meas_return=avg`` or a list of of lists of I/Q pairs if using +``meas_return=single`` with the outer list indexing shot number and the inner +list indexing memory slot. +""" + + class BackendSamplerV2(BaseSamplerV2): """Evaluates bitstrings for provided quantum circuits @@ -91,6 +107,9 @@ class BackendSamplerV2(BaseSamplerV2): * ``seed_simulator``: The seed to use in the simulator. If None, a random seed will be used. Default: None. + * ``run_options``: A dictionary of options to pass through to the ``run()`` + method of the wrapped :class:`~.BackendV2` instance. + .. note:: This class requires a backend that supports ``memory`` option. @@ -165,19 +184,27 @@ def _run_pubs(self, pubs: list[SamplerPub], shots: int) -> list[SamplerPubResult for circuits in bound_circuits: flatten_circuits.extend(np.ravel(circuits).tolist()) + run_opts = self._options.run_options or {} # run circuits results, _ = _run_circuits( flatten_circuits, self._backend, + clear_metadata=False, memory=True, shots=shots, seed_simulator=self._options.seed_simulator, + **run_opts, ) result_memory = _prepare_memory(results) # pack memory to an ndarray of uint8 results = [] start = 0 + meas_level = ( + None + if self._options.run_options is None + else self._options.run_options.get("meas_level") + ) for pub, bound in zip(pubs, bound_circuits): meas_info, max_num_bytes = _analyze_circuit(pub.circuit) end = start + bound.size @@ -189,6 +216,7 @@ def _run_pubs(self, pubs: list[SamplerPub], shots: int) -> list[SamplerPubResult meas_info, max_num_bytes, pub.circuit.metadata, + meas_level, ) ) start = end @@ -197,28 +225,43 @@ def _run_pubs(self, pubs: list[SamplerPub], shots: int) -> list[SamplerPubResult def _postprocess_pub( self, - result_memory: list[list[str]], + result_memory: list[ResultMemory], shots: int, shape: tuple[int, ...], meas_info: list[_MeasureInfo], max_num_bytes: int, circuit_metadata: dict, + meas_level: int | None, ) -> SamplerPubResult: - """Converts the memory data into an array of bit arrays with the shape of the pub.""" - arrays = { - item.creg_name: np.zeros(shape + (shots, item.num_bytes), dtype=np.uint8) - for item in meas_info - } - memory_array = _memory_array(result_memory, max_num_bytes) - - for samples, index in zip(memory_array, np.ndindex(*shape)): - for item in meas_info: - ary = _samples_to_packed_array(samples, item.num_bits, item.start) - arrays[item.creg_name][index] = ary - - meas = { - item.creg_name: BitArray(arrays[item.creg_name], item.num_bits) for item in meas_info - } + """Converts the memory data into a sampler pub result + + For level 2 data, the memory data are stored in an array of bit arrays + with the shape of the pub. For level 1 data, the data are stored in a + complex numpy array. + """ + if meas_level == 2 or meas_level is None: + arrays = { + item.creg_name: np.zeros(shape + (shots, item.num_bytes), dtype=np.uint8) + for item in meas_info + } + memory_array = _memory_array(result_memory, max_num_bytes) + + for samples, index in zip(memory_array, np.ndindex(*shape)): + for item in meas_info: + ary = _samples_to_packed_array(samples, item.num_bits, item.start) + arrays[item.creg_name][index] = ary + + meas = { + item.creg_name: BitArray(arrays[item.creg_name], item.num_bits) + for item in meas_info + } + elif meas_level == 1: + raw = np.array(result_memory) + cplx = raw[..., 0] + 1j * raw[..., 1] + cplx = np.reshape(cplx, (*shape, *cplx.shape[1:])) + meas = {item.creg_name: cplx for item in meas_info} + else: + raise QiskitError(f"Unsupported meas_level: {meas_level}") return SamplerPubResult( DataBin(**meas, shape=shape), metadata={"shots": shots, "circuit_metadata": circuit_metadata}, @@ -248,7 +291,7 @@ def _analyze_circuit(circuit: QuantumCircuit) -> tuple[list[_MeasureInfo], int]: return meas_info, _min_num_bytes(max_num_bits) -def _prepare_memory(results: list[Result]) -> list[list[str]]: +def _prepare_memory(results: list[Result]) -> list[ResultMemory]: """Joins splitted results if exceeding max_experiments""" lst = [] for res in results: diff --git a/qiskit/primitives/statevector_sampler.py b/qiskit/primitives/statevector_sampler.py index 78672e441dc4..486f35bc9364 100644 --- a/qiskit/primitives/statevector_sampler.py +++ b/qiskit/primitives/statevector_sampler.py @@ -287,6 +287,6 @@ def _final_measurement_mapping(circuit: QuantumCircuit) -> dict[tuple[ClassicalR def _has_control_flow(circuit: QuantumCircuit) -> bool: return any( - isinstance((op := instruction.operation), ControlFlowOp) or op.condition + isinstance((op := instruction.operation), ControlFlowOp) or op._condition for instruction in circuit ) diff --git a/qiskit/providers/basic_provider/basic_simulator.py b/qiskit/providers/basic_provider/basic_simulator.py index 9ac4760b214e..e412867a0e19 100644 --- a/qiskit/providers/basic_provider/basic_simulator.py +++ b/qiskit/providers/basic_provider/basic_simulator.py @@ -257,7 +257,7 @@ def configuration(self) -> BackendConfiguration: backend_name=self.name, backend_version=self.backend_version, n_qubits=self.num_qubits, - basis_gates=self.target.operation_names, + basis_gates=list(self.target.operation_names), gates=gates, local=True, simulator=True, diff --git a/qiskit/qasm2/export.py b/qiskit/qasm2/export.py index 3cf0d8942553..7c655dfd432b 100644 --- a/qiskit/qasm2/export.py +++ b/qiskit/qasm2/export.py @@ -246,12 +246,14 @@ def _instruction_call_site(operation): if operation.params: params = ",".join([pi_check(i, output="qasm", eps=1e-12) for i in operation.params]) qasm2_call = f"{qasm2_call}({params})" - if operation.condition is not None: - if not isinstance(operation.condition[0], ClassicalRegister): + if operation._condition is not None: + if not isinstance(operation._condition[0], ClassicalRegister): raise QASM2ExportError( "OpenQASM 2 can only condition on registers, but got '{operation.condition[0]}'" ) - qasm2_call = f"if({operation.condition[0].name}=={operation.condition[1]:d}) " + qasm2_call + qasm2_call = ( + f"if({operation._condition[0].name}=={operation._condition[1]:d}) " + qasm2_call + ) return qasm2_call diff --git a/qiskit/qasm2/parse.py b/qiskit/qasm2/parse.py index 6cdb0f70bba0..39ff388b6b45 100644 --- a/qiskit/qasm2/parse.py +++ b/qiskit/qasm2/parse.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """Python-space bytecode interpreter for the output of the main Rust parser logic.""" - +import warnings import dataclasses import math from typing import Iterable, Callable @@ -255,6 +255,11 @@ def from_bytecode(bytecode, custom_instructions: Iterable[CustomInstruction]): CircuitInstruction(gates[gate_id](*parameters), [qubits[q] for q in op_qubits]) ) elif opcode == OpCode.ConditionedGate: + warnings.warn( + "Conditioned gates in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) gate_id, parameters, op_qubits, creg, value = op.operands gate = gates[gate_id](*parameters).c_if(qc.cregs[creg], value) qc._append(CircuitInstruction(gate, [qubits[q] for q in op_qubits])) @@ -262,12 +267,22 @@ def from_bytecode(bytecode, custom_instructions: Iterable[CustomInstruction]): qubit, clbit = op.operands qc._append(CircuitInstruction(Measure(), (qubits[qubit],), (clbits[clbit],))) elif opcode == OpCode.ConditionedMeasure: + warnings.warn( + "Conditioned measurements in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) qubit, clbit, creg, value = op.operands measure = Measure().c_if(qc.cregs[creg], value) qc._append(CircuitInstruction(measure, (qubits[qubit],), (clbits[clbit],))) elif opcode == OpCode.Reset: qc._append(CircuitInstruction(Reset(), (qubits[op.operands[0]],))) elif opcode == OpCode.ConditionedReset: + warnings.warn( + "Conditioned resets in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) qubit, creg, value = op.operands reset = Reset().c_if(qc.cregs[creg], value) qc._append(CircuitInstruction(reset, (qubits[qubit],))) @@ -356,7 +371,7 @@ def __array__(self, dtype=None, copy=None): # to pickle ourselves, we just eagerly create the definition and pickle that. def __getstate__(self): - return (self.name, self.num_qubits, self.params, self.definition, self.condition) + return (self.name, self.num_qubits, self.params, self.definition, self._condition) def __setstate__(self, state): name, num_qubits, params, definition, condition = state diff --git a/qiskit/qasm3/ast.py b/qiskit/qasm3/ast.py index eaac830edf65..c723c443fef2 100644 --- a/qiskit/qasm3/ast.py +++ b/qiskit/qasm3/ast.py @@ -15,12 +15,14 @@ """QASM3 AST Nodes""" import enum -from typing import Optional, List, Union, Iterable, Tuple +from typing import Optional, List, Union, Iterable, Tuple, Sequence class ASTNode: """Base abstract class for AST nodes""" + __slots__ = () + class Statement(ASTNode): """ @@ -35,6 +37,8 @@ class Statement(ASTNode): | quantumStatement """ + __slots__ = () + class Pragma(ASTNode): """ @@ -42,6 +46,8 @@ class Pragma(ASTNode): : '#pragma' LBRACE statement* RBRACE // match any valid openqasm statement """ + __slots__ = ("content",) + def __init__(self, content): self.content = content @@ -52,6 +58,8 @@ class CalibrationGrammarDeclaration(Statement): : 'defcalgrammar' calibrationGrammar SEMICOLON """ + __slots__ = ("name",) + def __init__(self, name): self.name = name @@ -62,9 +70,11 @@ class Program(ASTNode): : header (globalStatement | statement)* """ - def __init__(self, header, statements=None): + __slots__ = ("header", "statements") + + def __init__(self, header, statements=()): self.header = header - self.statements = statements or [] + self.statements = statements class Header(ASTNode): @@ -73,6 +83,8 @@ class Header(ASTNode): : version? include* """ + __slots__ = ("version", "includes") + def __init__(self, version, includes): self.version = version self.includes = includes @@ -84,6 +96,8 @@ class Include(ASTNode): : 'include' StringLiteral SEMICOLON """ + __slots__ = ("filename",) + def __init__(self, filename): self.filename = filename @@ -94,6 +108,8 @@ class Version(ASTNode): : 'OPENQASM'(Integer | RealNumber) SEMICOLON """ + __slots__ = ("version_number",) + def __init__(self, version_number): self.version_number = version_number @@ -108,10 +124,14 @@ class QuantumInstruction(ASTNode): | quantumBarrier """ + __slots__ = () + class ClassicalType(ASTNode): """Information about a classical type. This is just an abstract base for inheritance tests.""" + __slots__ = () + class FloatType(ClassicalType, enum.Enum): """Allowed values for the width of floating-point types.""" @@ -126,10 +146,14 @@ class FloatType(ClassicalType, enum.Enum): class BoolType(ClassicalType): """Type information for a Boolean.""" + __slots__ = () + class IntType(ClassicalType): """Type information for a signed integer.""" + __slots__ = ("size",) + def __init__(self, size: Optional[int] = None): self.size = size @@ -137,6 +161,8 @@ def __init__(self, size: Optional[int] = None): class UintType(ClassicalType): """Type information for an unsigned integer.""" + __slots__ = ("size",) + def __init__(self, size: Optional[int] = None): self.size = size @@ -144,19 +170,25 @@ def __init__(self, size: Optional[int] = None): class BitType(ClassicalType): """Type information for a single bit.""" + __slots__ = () + class BitArrayType(ClassicalType): """Type information for a sized number of classical bits.""" + __slots__ = ("size",) + def __init__(self, size: int): self.size = size class Expression(ASTNode): - pass + __slots__ = () class StringifyAndPray(Expression): + __slots__ = ("obj",) + # This is not a real AST node, yet is somehow very common. It's used when there are # `ParameterExpression` instances; instead of actually visiting the Sympy expression tree into # an OQ3 AST, we just convert it to a string, cross our fingers, and hope. @@ -165,6 +197,8 @@ def __init__(self, obj): class Range(Expression): + __slots__ = ("start", "step", "end") + def __init__( self, start: Optional[Expression] = None, @@ -177,6 +211,8 @@ def __init__( class Identifier(Expression): + __slots__ = ("string",) + def __init__(self, string): self.string = string @@ -184,6 +220,8 @@ def __init__(self, string): class SubscriptedIdentifier(Identifier): """An identifier with subscripted access.""" + __slots__ = ("subscript",) + def __init__(self, string: str, subscript: Union[Range, Expression]): super().__init__(string) self.subscript = subscript @@ -198,16 +236,22 @@ class Constant(Expression, enum.Enum): class IntegerLiteral(Expression): + __slots__ = ("value",) + def __init__(self, value): self.value = value class BooleanLiteral(Expression): + __slots__ = ("value",) + def __init__(self, value): self.value = value class BitstringLiteral(Expression): + __slots__ = ("value", "width") + def __init__(self, value, width): self.value = value self.width = width @@ -224,12 +268,16 @@ class DurationUnit(enum.Enum): class DurationLiteral(Expression): + __slots__ = ("value", "unit") + def __init__(self, value: float, unit: DurationUnit): self.value = value self.unit = unit class Unary(Expression): + __slots__ = ("op", "operand") + class Op(enum.Enum): LOGIC_NOT = "!" BIT_NOT = "~" @@ -240,6 +288,8 @@ def __init__(self, op: Op, operand: Expression): class Binary(Expression): + __slots__ = ("op", "left", "right") + class Op(enum.Enum): BIT_AND = "&" BIT_OR = "|" @@ -262,12 +312,16 @@ def __init__(self, op: Op, left: Expression, right: Expression): class Cast(Expression): + __slots__ = ("type", "operand") + def __init__(self, type: ClassicalType, operand: Expression): self.type = type self.operand = operand class Index(Expression): + __slots__ = ("target", "index") + def __init__(self, target: Expression, index: Expression): self.target = target self.index = index @@ -280,6 +334,8 @@ class IndexSet(ASTNode): { Expression (, Expression)* } """ + __slots__ = ("values",) + def __init__(self, values: List[Expression]): self.values = values @@ -290,6 +346,8 @@ class QuantumMeasurement(ASTNode): : 'measure' indexIdentifierList """ + __slots__ = ("identifierList",) + def __init__(self, identifierList: List[Identifier]): self.identifierList = identifierList @@ -301,6 +359,8 @@ class QuantumMeasurementAssignment(Statement): | indexIdentifier EQUALS quantumMeasurement # eg: bits = measure qubits; """ + __slots__ = ("identifier", "quantumMeasurement") + def __init__(self, identifier: Identifier, quantumMeasurement: QuantumMeasurement): self.identifier = identifier self.quantumMeasurement = quantumMeasurement @@ -312,6 +372,8 @@ class Designator(ASTNode): : LBRACKET expression RBRACKET """ + __slots__ = ("expression",) + def __init__(self, expression: Expression): self.expression = expression @@ -319,6 +381,8 @@ def __init__(self, expression: Expression): class ClassicalDeclaration(Statement): """Declaration of a classical type, optionally initializing it to a value.""" + __slots__ = ("type", "identifier", "initializer") + def __init__(self, type_: ClassicalType, identifier: Identifier, initializer=None): self.type = type_ self.identifier = identifier @@ -328,6 +392,8 @@ def __init__(self, type_: ClassicalType, identifier: Identifier, initializer=Non class AssignmentStatement(Statement): """Assignment of an expression to an l-value.""" + __slots__ = ("lvalue", "rvalue") + def __init__(self, lvalue: SubscriptedIdentifier, rvalue: Expression): self.lvalue = lvalue self.rvalue = rvalue @@ -340,6 +406,8 @@ class QuantumDeclaration(ASTNode): 'qubit' designator? Identifier """ + __slots__ = ("identifier", "designator") + def __init__(self, identifier: Identifier, designator=None): self.identifier = identifier self.designator = designator @@ -351,6 +419,8 @@ class AliasStatement(ASTNode): : 'let' Identifier EQUALS indexIdentifier SEMICOLON """ + __slots__ = ("identifier", "value") + def __init__(self, identifier: Identifier, value: Expression): self.identifier = identifier self.value = value @@ -368,6 +438,8 @@ class QuantumGateModifierName(enum.Enum): class QuantumGateModifier(ASTNode): """A modifier of a gate. For example, in ``ctrl @ x $0``, the ``ctrl @`` is the modifier.""" + __slots__ = ("modifier", "argument") + def __init__(self, modifier: QuantumGateModifierName, argument: Optional[Expression] = None): self.modifier = modifier self.argument = argument @@ -379,17 +451,19 @@ class QuantumGateCall(QuantumInstruction): : quantumGateModifier* quantumGateName ( LPAREN expressionList? RPAREN )? indexIdentifierList """ + __slots__ = ("quantumGateName", "indexIdentifierList", "parameters", "modifiers") + def __init__( self, quantumGateName: Identifier, indexIdentifierList: List[Identifier], - parameters: List[Expression] = None, - modifiers: Optional[List[QuantumGateModifier]] = None, + parameters: Sequence[Expression] = (), + modifiers: Sequence[QuantumGateModifier] = (), ): self.quantumGateName = quantumGateName self.indexIdentifierList = indexIdentifierList - self.parameters = parameters or [] - self.modifiers = modifiers or [] + self.parameters = parameters + self.modifiers = modifiers class QuantumBarrier(QuantumInstruction): @@ -398,6 +472,8 @@ class QuantumBarrier(QuantumInstruction): : 'barrier' indexIdentifierList """ + __slots__ = ("indexIdentifierList",) + def __init__(self, indexIdentifierList: List[Identifier]): self.indexIdentifierList = indexIdentifierList @@ -405,6 +481,8 @@ def __init__(self, indexIdentifierList: List[Identifier]): class QuantumReset(QuantumInstruction): """A built-in ``reset q0;`` statement.""" + __slots__ = ("identifier",) + def __init__(self, identifier: Identifier): self.identifier = identifier @@ -412,6 +490,8 @@ def __init__(self, identifier: Identifier): class QuantumDelay(QuantumInstruction): """A built-in ``delay[duration] q0;`` statement.""" + __slots__ = ("duration", "qubits") + def __init__(self, duration: Expression, qubits: List[Identifier]): self.duration = duration self.qubits = qubits @@ -424,6 +504,8 @@ class ProgramBlock(ASTNode): | LBRACE(statement | controlDirective) * RBRACE """ + __slots__ = ("statements",) + def __init__(self, statements: List[Statement]): self.statements = statements @@ -434,6 +516,8 @@ class ReturnStatement(ASTNode): # TODO probably should be a subclass of Control : 'return' ( expression | quantumMeasurement )? SEMICOLON; """ + __slots__ = ("expression",) + def __init__(self, expression=None): self.expression = expression @@ -444,7 +528,7 @@ class QuantumBlock(ProgramBlock): : LBRACE ( quantumStatement | quantumLoop )* RBRACE """ - pass + __slots__ = () class SubroutineBlock(ProgramBlock): @@ -453,7 +537,7 @@ class SubroutineBlock(ProgramBlock): : LBRACE statement* returnStatement? RBRACE """ - pass + __slots__ = () class QuantumGateDefinition(Statement): @@ -462,6 +546,8 @@ class QuantumGateDefinition(Statement): : 'gate' quantumGateSignature quantumBlock """ + __slots__ = ("name", "params", "qubits", "body") + def __init__( self, name: Identifier, @@ -482,14 +568,16 @@ class SubroutineDefinition(Statement): returnSignature? subroutineBlock """ + __slots__ = ("identifier", "arguments", "subroutineBlock") + def __init__( self, identifier: Identifier, subroutineBlock: SubroutineBlock, - arguments=None, # [ClassicalArgument] + arguments=(), # [ClassicalArgument] ): self.identifier = identifier - self.arguments = arguments or [] + self.arguments = arguments self.subroutineBlock = subroutineBlock @@ -499,7 +587,7 @@ class CalibrationArgument(ASTNode): : classicalArgumentList | expressionList """ - pass + __slots__ = () class CalibrationDefinition(Statement): @@ -511,15 +599,17 @@ class CalibrationDefinition(Statement): ; """ + __slots__ = ("name", "identifierList", "calibrationArgumentList") + def __init__( self, name: Identifier, identifierList: List[Identifier], - calibrationArgumentList: Optional[List[CalibrationArgument]] = None, + calibrationArgumentList: Sequence[CalibrationArgument] = (), ): self.name = name self.identifierList = identifierList - self.calibrationArgumentList = calibrationArgumentList or [] + self.calibrationArgumentList = calibrationArgumentList class BranchingStatement(Statement): @@ -528,6 +618,8 @@ class BranchingStatement(Statement): : 'if' LPAREN booleanExpression RPAREN programBlock ( 'else' programBlock )? """ + __slots__ = ("condition", "true_body", "false_body") + def __init__(self, condition: Expression, true_body: ProgramBlock, false_body=None): self.condition = condition self.true_body = true_body @@ -547,6 +639,8 @@ class ForLoopStatement(Statement): | "[" Range "]" """ + __slots__ = ("indexset", "parameter", "body") + def __init__( self, indexset: Union[Identifier, IndexSet, Range], @@ -567,6 +661,8 @@ class WhileLoopStatement(Statement): WhileLoop: "while" "(" Expression ")" ProgramBlock """ + __slots__ = ("condition", "body") + def __init__(self, condition: Expression, body: ProgramBlock): self.condition = condition self.body = body @@ -575,10 +671,14 @@ def __init__(self, condition: Expression, body: ProgramBlock): class BreakStatement(Statement): """AST node for ``break`` statements. Has no associated information.""" + __slots__ = () + class ContinueStatement(Statement): """AST node for ``continue`` statements. Has no associated information.""" + __slots__ = () + class IOModifier(enum.Enum): """IO Modifier object""" @@ -590,6 +690,8 @@ class IOModifier(enum.Enum): class IODeclaration(ClassicalDeclaration): """A declaration of an IO variable.""" + __slots__ = ("modifier",) + def __init__(self, modifier: IOModifier, type_: ClassicalType, identifier: Identifier): super().__init__(type_, identifier) self.modifier = modifier @@ -598,6 +700,8 @@ def __init__(self, modifier: IOModifier, type_: ClassicalType, identifier: Ident class DefaultCase(Expression): """An object representing the `default` special label in switch statements.""" + __slots__ = () + class SwitchStatementPreview(Statement): """AST node for the proposed 'switch-case' extension to OpenQASM 3, before the syntax was @@ -605,6 +709,8 @@ class SwitchStatementPreview(Statement): The stabilized form of the syntax instead uses :class:`.SwitchStatement`.""" + __slots__ = ("target", "cases") + def __init__( self, target: Expression, cases: Iterable[Tuple[Iterable[Expression], ProgramBlock]] ): @@ -619,6 +725,8 @@ class SwitchStatement(Statement): cannot be joined with other cases (even though that's meaningless, the V1 syntax permitted it). """ + __slots__ = ("target", "cases", "default") + def __init__( self, target: Expression, diff --git a/qiskit/qasm3/exporter.py b/qiskit/qasm3/exporter.py index 0b50e31f2cff..8997770c14e7 100644 --- a/qiskit/qasm3/exporter.py +++ b/qiskit/qasm3/exporter.py @@ -618,12 +618,10 @@ def new_context(self, body: QuantumCircuit): self.scope = old_scope self.symbols = old_symbols - def _lookup_variable(self, variable) -> ast.Identifier: - """Lookup a Qiskit object within the current context, and return the name that should be + def _lookup_bit(self, bit) -> ast.Identifier: + """Lookup a Qiskit bit within the current context, and return the name that should be used to represent it in OpenQASM 3 programmes.""" - if isinstance(variable, Bit): - variable = self.scope.bit_map[variable] - return self.symbols.get_variable(variable) + return self.symbols.get_variable(self.scope.bit_map[bit]) def build_program(self): """Builds a Program""" @@ -930,7 +928,7 @@ def build_aliases(self, registers: Iterable[Register]) -> List[ast.AliasStatemen out = [] for register in registers: name = self.symbols.register_variable(register.name, register, allow_rename=True) - elements = [self._lookup_variable(bit) for bit in register] + elements = [self._lookup_bit(bit) for bit in register] for i, bit in enumerate(register): # This might shadow previous definitions, but that's not a problem. self.symbols.set_object_ident( @@ -977,18 +975,17 @@ def build_current_scope(self) -> List[ast.Statement]: if isinstance(instruction.operation, Gate): nodes = [self.build_gate_call(instruction)] elif isinstance(instruction.operation, Barrier): - operands = [self._lookup_variable(operand) for operand in instruction.qubits] + operands = [self._lookup_bit(operand) for operand in instruction.qubits] nodes = [ast.QuantumBarrier(operands)] elif isinstance(instruction.operation, Measure): measurement = ast.QuantumMeasurement( - [self._lookup_variable(operand) for operand in instruction.qubits] + [self._lookup_bit(operand) for operand in instruction.qubits] ) - qubit = self._lookup_variable(instruction.clbits[0]) + qubit = self._lookup_bit(instruction.clbits[0]) nodes = [ast.QuantumMeasurementAssignment(qubit, measurement)] elif isinstance(instruction.operation, Reset): nodes = [ - ast.QuantumReset(self._lookup_variable(operand)) - for operand in instruction.qubits + ast.QuantumReset(self._lookup_bit(operand)) for operand in instruction.qubits ] elif isinstance(instruction.operation, Delay): nodes = [self.build_delay(instruction)] @@ -1009,13 +1006,13 @@ def build_current_scope(self) -> List[ast.Statement]: f" but received '{instruction.operation}'" ) - if instruction.operation.condition is None: + if instruction.operation._condition is None: statements.extend(nodes) else: body = ast.ProgramBlock(nodes) statements.append( ast.BranchingStatement( - self.build_expression(_lift_condition(instruction.operation.condition)), + self.build_expression(_lift_condition(instruction.operation._condition)), body, ) ) @@ -1122,9 +1119,14 @@ def build_for_loop(self, instruction: CircuitInstruction) -> ast.ForLoopStatemen body_ast = ast.ProgramBlock(self.build_current_scope()) return ast.ForLoopStatement(indexset_ast, loop_parameter_ast, body_ast) + def _lookup_variable_for_expression(self, var): + if isinstance(var, Bit): + return self._lookup_bit(var) + return self.symbols.get_variable(var) + def build_expression(self, node: expr.Expr) -> ast.Expression: """Build an expression.""" - return node.accept(_ExprBuilder(self._lookup_variable)) + return node.accept(_ExprBuilder(self._lookup_variable_for_expression)) def build_delay(self, instruction: CircuitInstruction) -> ast.QuantumDelay: """Build a built-in delay statement.""" @@ -1144,9 +1146,7 @@ def build_delay(self, instruction: CircuitInstruction) -> ast.QuantumDelay: "dt": ast.DurationUnit.SAMPLE, } duration = ast.DurationLiteral(duration_value, unit_map[unit]) - return ast.QuantumDelay( - duration, [self._lookup_variable(qubit) for qubit in instruction.qubits] - ) + return ast.QuantumDelay(duration, [self._lookup_bit(qubit) for qubit in instruction.qubits]) def build_integer(self, value) -> ast.IntegerLiteral: """Build an integer literal, raising a :obj:`.QASM3ExporterError` if the input is not @@ -1166,9 +1166,11 @@ def _rebind_scoped_parameters(self, expression): # missing, pending a new system in Terra to replace it (2022-03-07). if not isinstance(expression, ParameterExpression): return expression + if isinstance(expression, Parameter): + return self.symbols.get_variable(expression).string return expression.subs( { - param: Parameter(self._lookup_variable(param).string) + param: Parameter(self.symbols.get_variable(param).string, uuid=param.uuid) for param in expression.parameters } ) @@ -1181,18 +1183,14 @@ def build_gate_call(self, instruction: CircuitInstruction): ident = self.symbols.get_gate(instruction.operation) if ident is None: ident = self.define_gate(instruction.operation) - qubits = [self._lookup_variable(qubit) for qubit in instruction.qubits] - if self.disable_constants: - parameters = [ - ast.StringifyAndPray(self._rebind_scoped_parameters(param)) - for param in instruction.operation.params - ] - else: - parameters = [ - ast.StringifyAndPray(pi_check(self._rebind_scoped_parameters(param), output="qasm")) - for param in instruction.operation.params - ] - + qubits = [self._lookup_bit(qubit) for qubit in instruction.qubits] + parameters = [ + ast.StringifyAndPray(self._rebind_scoped_parameters(param)) + for param in instruction.operation.params + ] + if not self.disable_constants: + for parameter in parameters: + parameter.obj = pi_check(parameter.obj, output="qasm") return ast.QuantumGateCall(ident, qubits, parameters=parameters) diff --git a/qiskit/qpy/binary_io/circuits.py b/qiskit/qpy/binary_io/circuits.py index 3fe1834db6ef..174acceb59e4 100644 --- a/qiskit/qpy/binary_io/circuits.py +++ b/qiskit/qpy/binary_io/circuits.py @@ -318,6 +318,13 @@ def _read_instruction( use_symengine, standalone_vars, ) + if condition is not None: + warnings.warn( + f"The .condition attribute on {gate_name} will be loaded as an IfElseOp " + "starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) inst_obj.condition = condition if instruction.label_size > 0: inst_obj.label = label @@ -414,6 +421,12 @@ def _read_instruction( gate = gate_class(*params) if condition: if not isinstance(gate, ControlFlowOp): + warnings.warn( + f"The .condition attribute on {gate} will be loaded as an " + "IfElseOp starting in Qiskit 2.0", + FutureWarning, + stacklevel=3, + ) gate = gate.c_if(*condition) else: gate.condition = condition @@ -761,13 +774,13 @@ def _write_instruction( condition_type = type_keys.Condition.NONE condition_register = b"" condition_value = 0 - if (op_condition := getattr(instruction.operation, "condition", None)) is not None: + if (op_condition := getattr(instruction.operation, "_condition", None)) is not None: if isinstance(op_condition, expr.Expr): condition_type = type_keys.Condition.EXPRESSION else: condition_type = type_keys.Condition.TWO_TUPLE - condition_register = _dumps_register(instruction.operation.condition[0], index_map) - condition_value = int(instruction.operation.condition[1]) + condition_register = _dumps_register(instruction.operation._condition[0], index_map) + condition_value = int(instruction.operation._condition[1]) gate_class_name = gate_class_name.encode(common.ENCODE) label = getattr(instruction.operation, "label", None) diff --git a/qiskit/quantum_info/operators/operator.py b/qiskit/quantum_info/operators/operator.py index fc3dd9ee187a..3b6ec8abff89 100644 --- a/qiskit/quantum_info/operators/operator.py +++ b/qiskit/quantum_info/operators/operator.py @@ -547,7 +547,7 @@ def power(self, n: float, branch_cut_rotation=cmath.pi * 1e-12) -> Operator: Non-integer powers of operators with an eigenvalue whose complex phase is :math:`\\pi` have a branch cut in the complex plane, which makes the calculation of the principal root around this cut subject to precision / differences in BLAS implementation. For example, the square - root of Pauli Y can return the :math:`\\pi/2` or :math:`\\-pi/2` Y rotation depending on + root of Pauli Y can return the :math:`\\pi/2` or :math:`-\\pi/2` Y rotation depending on whether the -1 eigenvalue is found as ``complex(-1, tiny)`` or ``complex(-1, -tiny)``. Such eigenvalues are really common in quantum information, so this function first phase-rotates the input matrix to shift the branch cut to a far less common point. The underlying diff --git a/qiskit/quantum_info/operators/symplectic/clifford_circuits.py b/qiskit/quantum_info/operators/symplectic/clifford_circuits.py index 283893e59f58..44bcd31e2da5 100644 --- a/qiskit/quantum_info/operators/symplectic/clifford_circuits.py +++ b/qiskit/quantum_info/operators/symplectic/clifford_circuits.py @@ -81,7 +81,7 @@ def _append_operation(clifford, operation, qargs=None): else: # assert isinstance(gate, Instruction) name = gate.name - if getattr(gate, "condition", None) is not None: + if getattr(gate, "_condition", None) is not None: raise QiskitError("Conditional gate is not a valid Clifford operation.") # Apply gate if it is a Clifford basis gate diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index 8bf7b3848c46..e8e19bd7af56 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -935,6 +935,14 @@ def to_list(self, array: bool = False): return labels return labels.tolist() + def to_sparse_list(self): + """Convert to a sparse Pauli list format with elements (pauli, qubits, coefficient).""" + pauli_labels = self.paulis.to_labels() + sparse_list = [ + (*sparsify_label(label), coeff) for label, coeff in zip(pauli_labels, self.coeffs) + ] + return sparse_list + def to_matrix(self, sparse: bool = False, force_serial: bool = False) -> np.ndarray: """Convert to a dense or sparse matrix. @@ -1176,5 +1184,13 @@ def apply_layout( return new_op.compose(self, qargs=layout) +def sparsify_label(pauli_string): + """Return a sparse format of a Pauli string, e.g. "XIIIZ" -> ("XZ", [0, 4]).""" + qubits = [i for i, label in enumerate(reversed(pauli_string)) if label != "I"] + sparse_label = "".join(pauli_string[~i] for i in qubits) + + return sparse_label, qubits + + # Update docstrings for API docs generate_apidocs(SparsePauliOp) diff --git a/qiskit/synthesis/evolution/lie_trotter.py b/qiskit/synthesis/evolution/lie_trotter.py index 1a01675a6782..5b4e91721a6e 100644 --- a/qiskit/synthesis/evolution/lie_trotter.py +++ b/qiskit/synthesis/evolution/lie_trotter.py @@ -14,18 +14,15 @@ from __future__ import annotations -import inspect from collections.abc import Callable from typing import Any -import numpy as np from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.quantum_info.operators import SparsePauliOp, Pauli -from qiskit.utils.deprecation import deprecate_arg -from .product_formula import ProductFormula +from .suzuki_trotter import SuzukiTrotter -class LieTrotter(ProductFormula): +class LieTrotter(SuzukiTrotter): r"""The Lie-Trotter product formula. The Lie-Trotter formula approximates the exponential of two non-commuting operators @@ -40,7 +37,7 @@ class LieTrotter(ProductFormula): .. math:: - e^{-it(XX + ZZ)} = e^{-it XX}e^{-it ZZ} + \mathcal{O}(t^2). + e^{-it(XI + ZZ)} = e^{-it XI}e^{-it ZZ} + \mathcal{O}(t^2). References: @@ -52,21 +49,6 @@ class LieTrotter(ProductFormula): `arXiv:math-ph/0506007 `_ """ - @deprecate_arg( - name="atomic_evolution", - since="1.2", - predicate=lambda callable: callable is not None - and len(inspect.signature(callable).parameters) == 2, - deprecation_description=( - "The 'Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]' signature of the " - "'atomic_evolution' argument" - ), - additional_msg=( - "Instead you should update your 'atomic_evolution' function to be of the following " - "type: 'Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]'." - ), - pending=True, - ) def __init__( self, reps: int = 1, @@ -100,26 +82,6 @@ def __init__( """ super().__init__(1, reps, insert_barriers, cx_structure, atomic_evolution, wrap) - def synthesize(self, evolution): - # get operators and time to evolve - operators = evolution.operator - time = evolution.time - - # construct the evolution circuit - single_rep = QuantumCircuit(operators[0].num_qubits) - - if not isinstance(operators, list): - pauli_list = [(Pauli(op), np.real(coeff)) for op, coeff in operators.to_list()] - else: - pauli_list = [(op, 1) for op in operators] - - for i, (op, coeff) in enumerate(pauli_list): - self.atomic_evolution(single_rep, op, coeff * time / self.reps) - if self.insert_barriers and i != len(pauli_list) - 1: - single_rep.barrier() - - return single_rep.repeat(self.reps, insert_barriers=self.insert_barriers).decompose() - @property def settings(self) -> dict[str, Any]: """Return the settings in a dictionary, which can be used to reconstruct the object. diff --git a/qiskit/synthesis/evolution/product_formula.py b/qiskit/synthesis/evolution/product_formula.py index 28bbc5711bf8..4de838215ff1 100644 --- a/qiskit/synthesis/evolution/product_formula.py +++ b/qiskit/synthesis/evolution/product_formula.py @@ -16,16 +16,19 @@ import inspect from collections.abc import Callable -from typing import Any -from functools import partial +import typing import numpy as np from qiskit.circuit.parameterexpression import ParameterExpression -from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.circuit.quantumcircuit import QuantumCircuit, ParameterValueType from qiskit.quantum_info import SparsePauliOp, Pauli from qiskit.utils.deprecation import deprecate_arg +from qiskit._accelerate.circuit_library import pauli_evolution from .evolution_synthesis import EvolutionSynthesis +if typing.TYPE_CHECKING: + from qiskit.circuit.library import PauliEvolutionGate + class ProductFormula(EvolutionSynthesis): """Product formula base class for the decomposition of non-commuting operator exponentials. @@ -78,8 +81,9 @@ def __init__( Alternatively, the function can also take Pauli operator and evolution time as inputs and returns the circuit that will be appended to the overall circuit being built. - wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes - effect when ``atomic_evolution is None``. + wrap: Whether to wrap the atomic evolutions into custom gate objects. Note that setting + this to ``True`` is slower than ``False``. This only takes effect when + ``atomic_evolution is None``. """ super().__init__() self.order = order @@ -88,14 +92,16 @@ def __init__( # user-provided atomic evolution, stored for serialization self._atomic_evolution = atomic_evolution + + if cx_structure not in ["chain", "fountain"]: + raise ValueError(f"Unsupported CX structure: {cx_structure}") + self._cx_structure = cx_structure self._wrap = wrap # if atomic evolution is not provided, set a default if atomic_evolution is None: - self.atomic_evolution = partial( - _default_atomic_evolution, cx_structure=cx_structure, wrap=wrap - ) + self.atomic_evolution = None elif len(inspect.signature(atomic_evolution).parameters) == 2: @@ -108,8 +114,50 @@ def wrap_atomic_evolution(output, operator, time): else: self.atomic_evolution = atomic_evolution + def expand( + self, evolution: PauliEvolutionGate + ) -> list[tuple[str, tuple[int], ParameterValueType]]: + """Apply the product formula to expand the Hamiltonian in the evolution gate. + + Args: + evolution: The :class:`.PauliEvolutionGate`, whose Hamiltonian we expand. + + Returns: + A list of Pauli rotations in a sparse format, where each element is + ``(paulistring, qubits, coefficient)``. For example, the Lie-Trotter expansion + of ``H = XI + ZZ`` would return ``[("X", [1], 1), ("ZZ", [0, 1], 1)]``. + """ + raise NotImplementedError( + f"The method ``expand`` is not implemented for {self.__class__}. Implement it to " + f"automatically enable the call to {self.__class__}.synthesize." + ) + + def synthesize(self, evolution: PauliEvolutionGate) -> QuantumCircuit: + """Synthesize a :class:`.PauliEvolutionGate`. + + Args: + evolution: The evolution gate to synthesize. + + Returns: + QuantumCircuit: A circuit implementing the evolution. + """ + pauli_rotations = self.expand(evolution) + num_qubits = evolution.num_qubits + + if self._wrap or self._atomic_evolution is not None: + # this is the slow path, where each Pauli evolution is constructed in Rust + # separately and then wrapped into a gate object + circuit = self._custom_evolution(num_qubits, pauli_rotations) + else: + # this is the fast path, where the whole evolution is constructed Rust-side + cx_fountain = self._cx_structure == "fountain" + data = pauli_evolution(num_qubits, pauli_rotations, self.insert_barriers, cx_fountain) + circuit = QuantumCircuit._from_circuit_data(data, add_regs=True) + + return circuit + @property - def settings(self) -> dict[str, Any]: + def settings(self) -> dict[str, typing.Any]: """Return the settings in a dictionary, which can be used to reconstruct the object. Returns: @@ -131,254 +179,63 @@ def settings(self) -> dict[str, Any]: "wrap": self._wrap, } + def _normalize_coefficients( + self, paulis: list[str | list[int], float | complex | ParameterExpression] + ) -> list[str | list[int] | ParameterValueType]: + """Ensure the coefficients are real (or parameter expressions).""" + return [[(op, qubits, real_or_fail(coeff)) for op, qubits, coeff in ops] for ops in paulis] -def evolve_pauli( - output: QuantumCircuit, - pauli: Pauli, - time: float | ParameterExpression = 1.0, - cx_structure: str = "chain", - wrap: bool = False, - label: str | None = None, -) -> None: - r"""Construct a circuit implementing the time evolution of a single Pauli string. - - For a Pauli string :math:`P = \{I, X, Y, Z\}^{\otimes n}` on :math:`n` qubits and an - evolution time :math:`t`, the returned circuit implements the unitary operation + def _custom_evolution(self, num_qubits, pauli_rotations): + """Implement the evolution for the non-standard path. - .. math:: + This is either because a user-defined atomic evolution is given, or because the evolution + of individual Paulis needs to be wrapped in gates. + """ + circuit = QuantumCircuit(num_qubits) + cx_fountain = self._cx_structure == "fountain" - U(t) = e^{-itP}. + num_paulis = len(pauli_rotations) + for i, pauli_rotation in enumerate(pauli_rotations): + if self._atomic_evolution is not None: + # use the user-provided evolution with a global operator + operator = SparsePauliOp.from_sparse_list([pauli_rotation], num_qubits) + self.atomic_evolution(circuit, operator, time=1) # time is inside the Pauli coeff - Since only a single Pauli string is evolved the circuit decomposition is exact. + else: # this means self._wrap is True + # we create a local sparse Pauli representation such that the operator + # does not span over all qubits of the circuit + pauli_string, qubits, coeff = pauli_rotation + local_pauli = (pauli_string, list(range(len(qubits))), coeff) - Args: - output: The circuit object to which to append the evolved Pauli. - pauli: The Pauli to evolve. - time: The evolution time. - cx_structure: Determine the structure of CX gates, can be either ``"chain"`` for - next-neighbor connections or ``"fountain"`` to connect directly to the top qubit. - wrap: Whether to wrap the single Pauli evolutions into custom gate objects. - label: A label for the gate. - """ - num_non_identity = len([label for label in pauli.to_label() if label != "I"]) - - # first check, if the Pauli is only the identity, in which case the evolution only - # adds a global phase - if num_non_identity == 0: - output.global_phase -= time - # if we evolve on a single qubit, if yes use the corresponding qubit rotation - elif num_non_identity == 1: - _single_qubit_evolution(output, pauli, time, wrap) - # same for two qubits, use Qiskit's native rotations - elif num_non_identity == 2: - _two_qubit_evolution(output, pauli, time, cx_structure, wrap) - # otherwise do basis transformation and CX chains - else: - _multi_qubit_evolution(output, pauli, time, cx_structure, wrap) - - -def _single_qubit_evolution(output, pauli, time, wrap): - dest = QuantumCircuit(1) if wrap else output - # Note that all phases are removed from the pauli label and are only in the coefficients. - # That's because the operators we evolved have all been translated to a SparsePauliOp. - qubits = [] - label = "" - for i, pauli_i in enumerate(reversed(pauli.to_label())): - idx = 0 if wrap else i - if pauli_i == "X": - dest.rx(2 * time, idx) - qubits.append(i) - label += "X" - elif pauli_i == "Y": - dest.ry(2 * time, idx) - qubits.append(i) - label += "Y" - elif pauli_i == "Z": - dest.rz(2 * time, idx) - qubits.append(i) - label += "Z" - - if wrap: - gate = dest.to_gate(label=f"exp(it {label})") - qubits = [output.qubits[q] for q in qubits] - output.append(gate, qargs=qubits, copy=False) - - -def _two_qubit_evolution(output, pauli, time, cx_structure, wrap): - # Get the Paulis and the qubits they act on. - # Note that all phases are removed from the pauli label and are only in the coefficients. - # That's because the operators we evolved have all been translated to a SparsePauliOp. - labels_as_array = np.array(list(reversed(pauli.to_label()))) - qubits = np.where(labels_as_array != "I")[0] - indices = [0, 1] if wrap else qubits - labels = np.array([labels_as_array[idx] for idx in qubits]) - - dest = QuantumCircuit(2) if wrap else output - - # go through all cases we have implemented in Qiskit - if all(labels == "X"): # RXX - dest.rxx(2 * time, indices[0], indices[1]) - elif all(labels == "Y"): # RYY - dest.ryy(2 * time, indices[0], indices[1]) - elif all(labels == "Z"): # RZZ - dest.rzz(2 * time, indices[0], indices[1]) - elif labels[0] == "Z" and labels[1] == "X": # RZX - dest.rzx(2 * time, indices[0], indices[1]) - elif labels[0] == "X" and labels[1] == "Z": # RXZ - dest.rzx(2 * time, indices[1], indices[0]) - else: # all the others are not native in Qiskit, so use default the decomposition - _multi_qubit_evolution(output, pauli, time, cx_structure, wrap) - return - - if wrap: - gate = dest.to_gate(label=f"exp(it {''.join(labels)})") - qubits = [output.qubits[q] for q in qubits] - output.append(gate, qargs=qubits, copy=False) - - -def _multi_qubit_evolution(output, pauli, time, cx_structure, wrap): - # get diagonalizing clifford - cliff = diagonalizing_clifford(pauli) - - # get CX chain to reduce the evolution to the top qubit - if cx_structure == "chain": - chain = cnot_chain(pauli) - else: - chain = cnot_fountain(pauli) - - # determine qubit to do the rotation on - target = None - # Note that all phases are removed from the pauli label and are only in the coefficients. - # That's because the operators we evolved have all been translated to a SparsePauliOp. - for i, pauli_i in enumerate(reversed(pauli.to_label())): - if pauli_i != "I": - target = i - break - - # build the evolution as: diagonalization, reduction, 1q evolution, followed by inverses - dest = QuantumCircuit(pauli.num_qubits) if wrap else output - dest.compose(cliff, inplace=True) - dest.compose(chain, inplace=True) - dest.rz(2 * time, target) - dest.compose(chain.inverse(), inplace=True) - dest.compose(cliff.inverse(), inplace=True) - - if wrap: - gate = dest.to_gate(label=f"exp(it {pauli.to_label()})") - output.append(gate, qargs=output.qubits, copy=False) - - -def diagonalizing_clifford(pauli: Pauli) -> QuantumCircuit: - """Get the clifford circuit to diagonalize the Pauli operator. - - Args: - pauli: The Pauli to diagonalize. - - Returns: - A circuit to diagonalize. - """ - cliff = QuantumCircuit(pauli.num_qubits) - for i, pauli_i in enumerate(reversed(pauli.to_label())): - if pauli_i == "Y": - cliff.sdg(i) - if pauli_i in ["X", "Y"]: - cliff.h(i) + # build the circuit Rust-side + data = pauli_evolution( + len(qubits), + [local_pauli], + False, + cx_fountain, + ) + evo = QuantumCircuit._from_circuit_data(data) - return cliff + # and append it to the circuit with the correct label + gate = evo.to_gate(label=f"exp(it {pauli_string})") + circuit.append(gate, qubits) + if self.insert_barriers and i < num_paulis - 1: + circuit.barrier() -def cnot_chain(pauli: Pauli) -> QuantumCircuit: - """CX chain. + return circuit - For example, for the Pauli with the label 'XYZIX'. - .. code-block:: text +def real_or_fail(value, tol=100): + """Return real if close, otherwise fail. Unbound parameters are left unchanged. - ┌───┐ - q_0: ──────────┤ X ├ - └─┬─┘ - q_1: ────────────┼── - ┌───┐ │ - q_2: ─────┤ X ├──■── - ┌───┐└─┬─┘ - q_3: ┤ X ├──■─────── - └─┬─┘ - q_4: ──■──────────── - - Args: - pauli: The Pauli for which to construct the CX chain. - - Returns: - A circuit implementing the CX chain. + Based on NumPy's ``real_if_close``, i.e. ``tol`` is in terms of machine precision for float. """ + if isinstance(value, ParameterExpression): + return value - chain = QuantumCircuit(pauli.num_qubits) - control, target = None, None - - # iterate over the Pauli's and add CNOTs - for i, pauli_i in enumerate(pauli.to_label()): - i = pauli.num_qubits - i - 1 - if pauli_i != "I": - if control is None: - control = i - else: - target = i - - if control is not None and target is not None: - chain.cx(control, target) - control = i - target = None - - return chain - - -def cnot_fountain(pauli: Pauli) -> QuantumCircuit: - """CX chain in the fountain shape. - - For example, for the Pauli with the label 'XYZIX'. - - .. code-block:: text - - ┌───┐┌───┐┌───┐ - q_0: ┤ X ├┤ X ├┤ X ├ - └─┬─┘└─┬─┘└─┬─┘ - q_1: ──┼────┼────┼── - │ │ │ - q_2: ──■────┼────┼── - │ │ - q_3: ───────■────┼── - │ - q_4: ────────────■── - - Args: - pauli: The Pauli for which to construct the CX chain. - - Returns: - A circuit implementing the CX chain. - """ + abstol = tol * np.finfo(float).eps + if abs(np.imag(value)) < abstol: + return np.real(value) - chain = QuantumCircuit(pauli.num_qubits) - control, target = None, None - for i, pauli_i in enumerate(reversed(pauli.to_label())): - if pauli_i != "I": - if target is None: - target = i - else: - control = i - - if control is not None and target is not None: - chain.cx(control, target) - control = None - - return chain - - -def _default_atomic_evolution(output, operator, time, cx_structure, wrap): - if isinstance(operator, Pauli): - # single Pauli operator: just exponentiate it - evolve_pauli(output, operator, time, cx_structure, wrap) - else: - # sum of Pauli operators: exponentiate each term (this assumes they commute) - pauli_list = [(Pauli(op), np.real(coeff)) for op, coeff in operator.to_list()] - for pauli, coeff in pauli_list: - evolve_pauli(output, pauli, coeff * time, cx_structure, wrap) + raise ValueError(f"Encountered complex value {value}, but expected real.") diff --git a/qiskit/synthesis/evolution/qdrift.py b/qiskit/synthesis/evolution/qdrift.py index 7c68f66dc8ea..64ae5fba637b 100644 --- a/qiskit/synthesis/evolution/qdrift.py +++ b/qiskit/synthesis/evolution/qdrift.py @@ -16,14 +16,19 @@ import inspect import math +import typing +from itertools import chain from collections.abc import Callable import numpy as np from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.quantum_info.operators import SparsePauliOp, Pauli from qiskit.utils.deprecation import deprecate_arg +from qiskit.exceptions import QiskitError from .product_formula import ProductFormula -from .lie_trotter import LieTrotter + +if typing.TYPE_CHECKING: + from qiskit.circuit.library import PauliEvolutionGate class QDrift(ProductFormula): @@ -88,44 +93,36 @@ def __init__( self.sampled_ops = None self.rng = np.random.default_rng(seed) - def synthesize(self, evolution): - # get operators and time to evolve + def expand(self, evolution: PauliEvolutionGate) -> list[tuple[str, tuple[int], float]]: operators = evolution.operator - time = evolution.time + time = evolution.time # used to determine the number of gates - if not isinstance(operators, list): - pauli_list = [(Pauli(op), coeff) for op, coeff in operators.to_list()] - coeffs = [np.real(coeff) for op, coeff in operators.to_list()] + # QDrift is based on first-order Lie-Trotter, hence we can just concatenate all + # Pauli terms and ignore commutations + if isinstance(operators, list): + paulis = list(chain.from_iterable([op.to_sparse_list() for op in operators])) else: - pauli_list = [(op, 1) for op in operators] - coeffs = [1 for op in operators] + paulis = operators.to_sparse_list() + + try: + coeffs = [float(np.real_if_close(coeff)) for _, _, coeff in paulis] + except TypeError as exc: + raise QiskitError("QDrift requires bound, real coefficients.") from exc # We artificially make the weights positive weights = np.abs(coeffs) lambd = np.sum(weights) num_gates = math.ceil(2 * (lambd**2) * (time**2) * self.reps) + # The protocol calls for the removal of the individual coefficients, # and multiplication by a constant evolution time. - evolution_time = lambd * time / num_gates - - self.sampled_ops = self.rng.choice( - np.array(pauli_list, dtype=object), - size=(num_gates,), - p=weights / lambd, - ) - - # pylint: disable=cyclic-import - from qiskit.circuit.library.pauli_evolution import PauliEvolutionGate - - # Build the evolution circuit using the LieTrotter synthesis with the sampled operators - lie_trotter = LieTrotter( - insert_barriers=self.insert_barriers, atomic_evolution=self.atomic_evolution + sampled = self.rng.choice( + np.array(paulis, dtype=object), size=(num_gates,), p=weights / lambd ) - evolution_circuit = PauliEvolutionGate( - sum(SparsePauliOp(np.sign(coeff) * op) for op, coeff in self.sampled_ops), - time=evolution_time, - synthesis=lie_trotter, - ).definition - return evolution_circuit + rescaled_time = 2 * lambd / num_gates * time + sampled_paulis = [ + (pauli[0], pauli[1], np.real(np.sign(pauli[2])) * rescaled_time) for pauli in sampled + ] + return sampled_paulis diff --git a/qiskit/synthesis/evolution/suzuki_trotter.py b/qiskit/synthesis/evolution/suzuki_trotter.py index e03fd27e26d4..4dfa92579bcb 100644 --- a/qiskit/synthesis/evolution/suzuki_trotter.py +++ b/qiskit/synthesis/evolution/suzuki_trotter.py @@ -15,17 +15,20 @@ from __future__ import annotations import inspect +import typing from collections.abc import Callable - -import numpy as np +from itertools import chain from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.quantum_info.operators import SparsePauliOp, Pauli from qiskit.utils.deprecation import deprecate_arg - from .product_formula import ProductFormula +if typing.TYPE_CHECKING: + from qiskit.circuit.quantumcircuit import ParameterValueType + from qiskit.circuit.library.pauli_evolution import PauliEvolutionGate + class SuzukiTrotter(ProductFormula): r"""The (higher order) Suzuki-Trotter product formula. @@ -44,7 +47,7 @@ class SuzukiTrotter(ProductFormula): .. math:: - e^{-it(XX + ZZ)} = e^{-it/2 ZZ}e^{-it XX}e^{-it/2 ZZ} + \mathcal{O}(t^3). + e^{-it(XI + ZZ)} = e^{-it/2 XI}e^{-it ZZ}e^{-it/2 XI} + \mathcal{O}(t^3). References: [1]: D. Berry, G. Ahokas, R. Cleve and B. Sanders, @@ -105,51 +108,91 @@ def __init__( ValueError: If order is not even """ - if order % 2 == 1: + if order > 1 and order % 2 == 1: raise ValueError( "Suzuki product formulae are symmetric and therefore only defined " - "for even orders." + f"for when the order is 1 or even, not {order}." ) super().__init__(order, reps, insert_barriers, cx_structure, atomic_evolution, wrap) - def synthesize(self, evolution): - # get operators and time to evolve - operators = evolution.operator - time = evolution.time + def expand( + self, evolution: PauliEvolutionGate + ) -> list[tuple[str, list[int], ParameterValueType]]: + """Expand the Hamiltonian into a Suzuki-Trotter sequence of sparse gates. - if not isinstance(operators, list): - pauli_list = [(Pauli(op), np.real(coeff)) for op, coeff in operators.to_list()] - else: - pauli_list = [(op, 1) for op in operators] + For example, the Hamiltonian ``H = IX + ZZ`` for an evolution time ``t`` and + 1 repetition for an order 2 formula would get decomposed into a list of 3-tuples + containing ``(pauli, indices, rz_rotation_angle)``, that is: + + .. code-block:: text + + ("X", [0], t), ("ZZ", [0, 1], 2t), ("X", [0], 2) + + Note that the rotation angle contains a factor of 2, such that that evolution + of a Pauli :math:`P` over time :math:`t`, which is :math:`e^{itP}`, is represented + by ``(P, indices, 2 * t)``. - ops_to_evolve = self._recurse(self.order, time / self.reps, pauli_list) + For ``N`` repetitions, this sequence would be repeated ``N`` times and the coefficients + divided by ``N``. + + Args: + evolution: The evolution gate to expand. + + Returns: + The Pauli network implementing the Trotter expansion. + """ + operators = evolution.operator # type: SparsePauliOp | list[SparsePauliOp] + time = evolution.time # construct the evolution circuit - single_rep = QuantumCircuit(operators[0].num_qubits) + if isinstance(operators, list): # already sorted into commuting bits + non_commuting = [ + (2 / self.reps * time * operator).to_sparse_list() for operator in operators + ] + else: + # Assume no commutativity here. If we were to group commuting Paulis, + # here would be the location to do so. + non_commuting = [[op] for op in (2 / self.reps * time * operators).to_sparse_list()] + + # normalize coefficients, i.e. ensure they are float or ParameterExpression + non_commuting = self._normalize_coefficients(non_commuting) - for i, (op, coeff) in enumerate(ops_to_evolve): - self.atomic_evolution(single_rep, op, coeff) - if self.insert_barriers and i != len(ops_to_evolve) - 1: - single_rep.barrier() + # we're already done here since Lie Trotter does not do any operator repetition + product_formula = self._recurse(self.order, non_commuting) + flattened = self.reps * list(chain.from_iterable(product_formula)) - return single_rep.repeat(self.reps, insert_barriers=self.insert_barriers).decompose() + return flattened @staticmethod - def _recurse(order, time, pauli_list): + def _recurse(order, grouped_paulis): if order == 1: - return pauli_list + return grouped_paulis elif order == 2: - halves = [(op, coeff * time / 2) for op, coeff in pauli_list[:-1]] - full = [(pauli_list[-1][0], time * pauli_list[-1][1])] + halves = [ + [(label, qubits, coeff / 2) for label, qubits, coeff in paulis] + for paulis in grouped_paulis[:-1] + ] + full = [grouped_paulis[-1]] return halves + full + list(reversed(halves)) else: reduction = 1 / (4 - 4 ** (1 / (order - 1))) outer = 2 * SuzukiTrotter._recurse( - order - 2, time=reduction * time, pauli_list=pauli_list + order - 2, + [ + [(label, qubits, coeff * reduction) for label, qubits, coeff in paulis] + for paulis in grouped_paulis + ], ) inner = SuzukiTrotter._recurse( - order - 2, time=(1 - 4 * reduction) * time, pauli_list=pauli_list + order - 2, + [ + [ + (label, qubits, coeff * (1 - 4 * reduction)) + for label, qubits, coeff in paulis + ] + for paulis in grouped_paulis + ], ) return outer + inner + outer diff --git a/qiskit/transpiler/passes/basis/basis_translator.py b/qiskit/transpiler/passes/basis/basis_translator.py index c75bff1a6ebc..c6a61b8ad49a 100644 --- a/qiskit/transpiler/passes/basis/basis_translator.py +++ b/qiskit/transpiler/passes/basis/basis_translator.py @@ -13,23 +13,12 @@ """Translates gates to a target basis using a given equivalence library.""" -import time import logging -from functools import singledispatchmethod from collections import defaultdict -from qiskit.circuit import ( - ControlFlowOp, - QuantumCircuit, - ParameterExpression, -) -from qiskit.dagcircuit import DAGCircuit, DAGOpNode -from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.transpiler.basepasses import TransformationPass -from qiskit.transpiler.exceptions import TranspilerError -from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES -from qiskit._accelerate.basis.basis_translator import basis_search, compose_transforms +from qiskit._accelerate.basis.basis_translator import base_run logger = logging.getLogger(__name__) @@ -136,309 +125,13 @@ def run(self, dag): Returns: DAGCircuit: translated circuit. """ - if self._target_basis is None and self._target is None: - return dag - qarg_indices = {qubit: index for index, qubit in enumerate(dag.qubits)} - - # Names of instructions assumed to supported by any backend. - if self._target is None: - basic_instrs = ["measure", "reset", "barrier", "snapshot", "delay", "store"] - target_basis = set(self._target_basis) - source_basis = set(self._extract_basis(dag)) - qargs_local_source_basis = {} - else: - basic_instrs = ["barrier", "snapshot", "store"] - target_basis = self._target.keys() - set(self._non_global_operations) - source_basis, qargs_local_source_basis = self._extract_basis_target(dag, qarg_indices) - - target_basis = set(target_basis).union(basic_instrs) - # If the source basis is a subset of the target basis and we have no circuit - # instructions on qargs that have non-global operations there is nothing to - # translate and we can exit early. - source_basis_names = {x[0] for x in source_basis} - if source_basis_names.issubset(target_basis) and not qargs_local_source_basis: - return dag - - logger.info( - "Begin BasisTranslator from source basis %s to target basis %s.", - source_basis, - target_basis, - ) - - # Search for a path from source to target basis. - search_start_time = time.time() - basis_transforms = basis_search(self._equiv_lib, source_basis, target_basis) - - qarg_local_basis_transforms = {} - for qarg, local_source_basis in qargs_local_source_basis.items(): - expanded_target = set(target_basis) - # For any multiqubit operation that contains a subset of qubits that - # has a non-local operation, include that non-local operation in the - # search. This matches with the check we did above to include those - # subset non-local operations in the check here. - if len(qarg) > 1: - for non_local_qarg, local_basis in self._qargs_with_non_global_operation.items(): - if qarg.issuperset(non_local_qarg): - expanded_target |= local_basis - else: - expanded_target |= self._qargs_with_non_global_operation[tuple(qarg)] - - logger.info( - "Performing BasisTranslator search from source basis %s to target " - "basis %s on qarg %s.", - local_source_basis, - expanded_target, - qarg, - ) - local_basis_transforms = basis_search( - self._equiv_lib, local_source_basis, expanded_target - ) - - if local_basis_transforms is None: - raise TranspilerError( - "Unable to translate the operations in the circuit: " - f"{[x[0] for x in local_source_basis]} to the backend's (or manually " - f"specified) target basis: {list(expanded_target)}. This likely means the " - "target basis is not universal or there are additional equivalence rules " - "needed in the EquivalenceLibrary being used. For more details on this " - "error see: " - "https://docs.quantum.ibm.com/api/qiskit/qiskit.transpiler.passes." - "BasisTranslator#translation-errors" - ) - - qarg_local_basis_transforms[qarg] = local_basis_transforms - - search_end_time = time.time() - logger.info( - "Basis translation path search completed in %.3fs.", search_end_time - search_start_time - ) - - if basis_transforms is None: - raise TranspilerError( - "Unable to translate the operations in the circuit: " - f"{[x[0] for x in source_basis]} to the backend's (or manually specified) target " - f"basis: {list(target_basis)}. This likely means the target basis is not universal " - "or there are additional equivalence rules needed in the EquivalenceLibrary being " - "used. For more details on this error see: " - "https://docs.quantum.ibm.com/api/qiskit/qiskit.transpiler.passes." - "BasisTranslator#translation-errors" - ) - - # Compose found path into a set of instruction substitution rules. - - compose_start_time = time.time() - instr_map = compose_transforms(basis_transforms, source_basis, dag) - extra_instr_map = { - qarg: compose_transforms(transforms, qargs_local_source_basis[qarg], dag) - for qarg, transforms in qarg_local_basis_transforms.items() - } - - compose_end_time = time.time() - logger.info( - "Basis translation paths composed in %.3fs.", compose_end_time - compose_start_time + return base_run( + dag, + self._equiv_lib, + self._qargs_with_non_global_operation, + self._min_qubits, + None if self._target_basis is None else set(self._target_basis), + self._target, + None if self._non_global_operations is None else set(self._non_global_operations), ) - - # Replace source instructions with target translations. - - replace_start_time = time.time() - - def apply_translation(dag, wire_map): - is_updated = False - out_dag = dag.copy_empty_like() - for node in dag.topological_op_nodes(): - node_qargs = tuple(wire_map[bit] for bit in node.qargs) - qubit_set = frozenset(node_qargs) - if node.name in target_basis or len(node.qargs) < self._min_qubits: - if node.name in CONTROL_FLOW_OP_NAMES: - flow_blocks = [] - for block in node.op.blocks: - dag_block = circuit_to_dag(block) - updated_dag, is_updated = apply_translation( - dag_block, - { - inner: wire_map[outer] - for inner, outer in zip(block.qubits, node.qargs) - }, - ) - if is_updated: - flow_circ_block = dag_to_circuit(updated_dag) - else: - flow_circ_block = block - flow_blocks.append(flow_circ_block) - node.op = node.op.replace_blocks(flow_blocks) - out_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False) - continue - if ( - node_qargs in self._qargs_with_non_global_operation - and node.name in self._qargs_with_non_global_operation[node_qargs] - ): - out_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False) - continue - - if dag._has_calibration_for(node): - out_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False) - continue - if qubit_set in extra_instr_map: - self._replace_node(out_dag, node, extra_instr_map[qubit_set]) - elif (node.name, node.num_qubits) in instr_map: - self._replace_node(out_dag, node, instr_map) - else: - raise TranspilerError(f"BasisTranslator did not map {node.name}.") - is_updated = True - return out_dag, is_updated - - out_dag, _ = apply_translation(dag, qarg_indices) - replace_end_time = time.time() - logger.info( - "Basis translation instructions replaced in %.3fs.", - replace_end_time - replace_start_time, - ) - - return out_dag - - def _replace_node(self, dag, node, instr_map): - target_params, target_dag = instr_map[node.name, node.num_qubits] - if len(node.params) != len(target_params): - raise TranspilerError( - "Translation num_params not equal to op num_params." - f"Op: {node.params} {node.name} Translation: {target_params}\n{target_dag}" - ) - if node.params: - parameter_map = dict(zip(target_params, node.params)) - for inner_node in target_dag.topological_op_nodes(): - new_node = DAGOpNode.from_instruction(inner_node._to_circuit_instruction()) - new_node.qargs = tuple( - node.qargs[target_dag.find_bit(x).index] for x in inner_node.qargs - ) - new_node.cargs = tuple( - node.cargs[target_dag.find_bit(x).index] for x in inner_node.cargs - ) - - if not new_node.is_standard_gate(): - new_node.op = new_node.op.copy() - if any(isinstance(x, ParameterExpression) for x in inner_node.params): - new_params = [] - for param in new_node.params: - if not isinstance(param, ParameterExpression): - new_params.append(param) - else: - bind_dict = {x: parameter_map[x] for x in param.parameters} - if any(isinstance(x, ParameterExpression) for x in bind_dict.values()): - new_value = param - for x in bind_dict.items(): - new_value = new_value.assign(*x) - else: - new_value = param.bind(bind_dict) - if not new_value.parameters: - new_value = new_value.numeric() - new_params.append(new_value) - new_node.params = new_params - if not new_node.is_standard_gate(): - new_node.op.params = new_params - dag._apply_op_node_back(new_node) - - if isinstance(target_dag.global_phase, ParameterExpression): - old_phase = target_dag.global_phase - bind_dict = {x: parameter_map[x] for x in old_phase.parameters} - if any(isinstance(x, ParameterExpression) for x in bind_dict.values()): - new_phase = old_phase - for x in bind_dict.items(): - new_phase = new_phase.assign(*x) - else: - new_phase = old_phase.bind(bind_dict) - if not new_phase.parameters: - new_phase = new_phase.numeric() - if isinstance(new_phase, complex): - raise TranspilerError(f"Global phase must be real, but got '{new_phase}'") - dag.global_phase += new_phase - - else: - for inner_node in target_dag.topological_op_nodes(): - new_node = DAGOpNode.from_instruction( - inner_node._to_circuit_instruction(), - ) - new_node.qargs = tuple( - node.qargs[target_dag.find_bit(x).index] for x in inner_node.qargs - ) - new_node.cargs = tuple( - node.cargs[target_dag.find_bit(x).index] for x in inner_node.cargs - ) - if not new_node.is_standard_gate: - new_node.op = new_node.op.copy() - # dag_op may be the same instance as other ops in the dag, - # so if there is a condition, need to copy - if getattr(node.op, "condition", None): - new_node_op = new_node.op.to_mutable() - new_node_op.condition = node.op.condition - new_node.op = new_node_op - dag._apply_op_node_back(new_node) - if target_dag.global_phase: - dag.global_phase += target_dag.global_phase - - @singledispatchmethod - def _extract_basis(self, circuit): - return circuit - - @_extract_basis.register - def _(self, dag: DAGCircuit): - for node in dag.op_nodes(): - if not dag._has_calibration_for(node) and len(node.qargs) >= self._min_qubits: - yield (node.name, node.num_qubits) - if node.name in CONTROL_FLOW_OP_NAMES: - for block in node.op.blocks: - yield from self._extract_basis(block) - - @_extract_basis.register - def _(self, circ: QuantumCircuit): - for instruction in circ.data: - operation = instruction.operation - if ( - not circ._has_calibration_for(instruction) - and len(instruction.qubits) >= self._min_qubits - ): - yield (operation.name, operation.num_qubits) - if isinstance(operation, ControlFlowOp): - for block in operation.blocks: - yield from self._extract_basis(block) - - def _extract_basis_target( - self, dag, qarg_indices, source_basis=None, qargs_local_source_basis=None - ): - if source_basis is None: - source_basis = set() - if qargs_local_source_basis is None: - qargs_local_source_basis = defaultdict(set) - for node in dag.op_nodes(): - qargs = tuple(qarg_indices[bit] for bit in node.qargs) - if dag._has_calibration_for(node) or len(node.qargs) < self._min_qubits: - continue - # Treat the instruction as on an incomplete basis if the qargs are in the - # qargs_with_non_global_operation dictionary or if any of the qubits in qargs - # are a superset for a non-local operation. For example, if the qargs - # are (0, 1) and that's a global (ie no non-local operations on (0, 1) - # operation but there is a non-local operation on (1,) we need to - # do an extra non-local search for this op to ensure we include any - # single qubit operation for (1,) as valid. This pattern also holds - # true for > 2q ops too (so for 4q operations we need to check for 3q, 2q, - # and 1q operations in the same manner) - if qargs in self._qargs_with_non_global_operation or any( - frozenset(qargs).issuperset(incomplete_qargs) - for incomplete_qargs in self._qargs_with_non_global_operation - ): - qargs_local_source_basis[frozenset(qargs)].add((node.name, node.num_qubits)) - else: - source_basis.add((node.name, node.num_qubits)) - if node.name in CONTROL_FLOW_OP_NAMES: - for block in node.op.blocks: - block_dag = circuit_to_dag(block) - source_basis, qargs_local_source_basis = self._extract_basis_target( - block_dag, - { - inner: qarg_indices[outer] - for inner, outer in zip(block.qubits, node.qargs) - }, - source_basis=source_basis, - qargs_local_source_basis=qargs_local_source_basis, - ) - return source_basis, qargs_local_source_basis diff --git a/qiskit/transpiler/passes/optimization/collect_cliffords.py b/qiskit/transpiler/passes/optimization/collect_cliffords.py index 5ee75af98000..8b26d04045c1 100644 --- a/qiskit/transpiler/passes/optimization/collect_cliffords.py +++ b/qiskit/transpiler/passes/optimization/collect_cliffords.py @@ -83,7 +83,7 @@ def __init__( def _is_clifford_gate(node, matrix_based=False): """Specifies whether a node holds a clifford gate.""" - if getattr(node.op, "condition", None) is not None: + if getattr(node.op, "_condition", None) is not None: return False if node.op.name in clifford_gate_names: return True diff --git a/qiskit/transpiler/passes/optimization/collect_linear_functions.py b/qiskit/transpiler/passes/optimization/collect_linear_functions.py index 158440e6a8a1..25a66e2bf9dc 100644 --- a/qiskit/transpiler/passes/optimization/collect_linear_functions.py +++ b/qiskit/transpiler/passes/optimization/collect_linear_functions.py @@ -71,7 +71,7 @@ def __init__( def _is_linear_gate(node): """Specifies whether a node holds a linear gate.""" - return node.op.name in ("cx", "swap") and getattr(node.op, "condition", None) is None + return node.op.name in ("cx", "swap") and getattr(node, "condition", None) is None def _collapse_to_linear_function(circuit): diff --git a/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py b/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py index e0dd61ff6cf4..34d51a17fe4a 100644 --- a/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py +++ b/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py @@ -120,7 +120,7 @@ def collect_key(x): if not isinstance(x, DAGOpNode): return "d" if isinstance(x.op, Gate): - if x.op.is_parameterized() or getattr(x.op, "condition", None) is not None: + if x.op.is_parameterized() or getattr(x.op, "_condition", None) is not None: return "c" return "b" + chr(ord("a") + len(x.qargs)) return "d" @@ -133,7 +133,7 @@ def collect_key(x): # check if the node is a gate and if it is parameterized if ( - getattr(nd.op, "condition", None) is not None + getattr(nd.op, "_condition", None) is not None or nd.op.is_parameterized() or not isinstance(nd.op, Gate) ): diff --git a/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py b/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py index 97324e2376cd..8c1b67b595a0 100644 --- a/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py +++ b/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py @@ -49,7 +49,7 @@ def _skip_node(self, node): # checking can be extended to cover additional cases. if getattr(node.op, "_directive", False) or node.name in {"measure", "reset", "delay"}: return True - if getattr(node.op, "condition", None): + if getattr(node, "condition", None): return True if node.op.is_parameterized(): return True diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_gates.py b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py index f8302b9232c0..466bcc4d5fcc 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_gates.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py @@ -86,7 +86,7 @@ def run(self, dag): for current_node in run: left_name = current_node.name if ( - getattr(current_node.op, "condition", None) is not None + getattr(current_node, "condition", None) is not None or len(current_node.qargs) != 1 or left_name not in ["p", "u1", "u2", "u3", "u", "id"] ): diff --git a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py index 6682e7ebbdba..7345a5bc40fd 100644 --- a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py +++ b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py @@ -40,7 +40,7 @@ def run(self, dag): swaps = dag.op_nodes(SwapGate) for swap in swaps[::-1]: - if getattr(swap.op, "condition", None) is not None: + if getattr(swap.op, "_condition", None) is not None: continue final_successor = [] for successor in dag.descendants(swap): diff --git a/qiskit/transpiler/passes/optimization/remove_identity_equiv.py b/qiskit/transpiler/passes/optimization/remove_identity_equiv.py index 4f8551f12442..fbf132d958a2 100644 --- a/qiskit/transpiler/passes/optimization/remove_identity_equiv.py +++ b/qiskit/transpiler/passes/optimization/remove_identity_equiv.py @@ -33,7 +33,7 @@ class RemoveIdentityEquivalent(TransformationPass): .. math:: - \bar{F} = \frac{1 + F_{\text{process}}{1 + d} + \bar{F} = \frac{1 + F_{\text{process}}}{1 + d},\ F_{\text{process}} = \frac{|\mathrm{Tr}(G)|^2}{d^2} diff --git a/qiskit/transpiler/passes/optimization/template_matching/backward_match.py b/qiskit/transpiler/passes/optimization/template_matching/backward_match.py index 3869a078250a..dc1ff7b3447f 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/backward_match.py +++ b/qiskit/transpiler/passes/optimization/template_matching/backward_match.py @@ -304,15 +304,15 @@ def _is_same_c_conf(self, node_circuit, node_template, carg_circuit): """ if ( node_circuit.type == "op" - and getattr(node_circuit.op, "condition", None) + and getattr(node_circuit.op, "_condition", None) and node_template.type == "op" - and getattr(node_template.op, "condition", None) + and getattr(node_template.op, "_condition", None) ): if set(carg_circuit) != set(node_template.cindices): return False if ( - getattr(node_circuit.op, "condition", None)[1] - != getattr(node_template.op, "condition", None)[1] + getattr(node_circuit.op, "_condition", None)[1] + != getattr(node_template.op, "_condition", None)[1] ): return False return True diff --git a/qiskit/transpiler/passes/optimization/template_matching/forward_match.py b/qiskit/transpiler/passes/optimization/template_matching/forward_match.py index d8dd5bb2b9a2..b4232d8ff6d2 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/forward_match.py +++ b/qiskit/transpiler/passes/optimization/template_matching/forward_match.py @@ -311,15 +311,15 @@ def _is_same_c_conf(self, node_circuit, node_template): """ if ( node_circuit.type == "op" - and getattr(node_circuit.op, "condition", None) + and getattr(node_circuit.op, "_condition", None) and node_template.type == "op" - and getattr(node_template.op, "condition", None) + and getattr(node_template.op, "_condition", None) ): if set(self.carg_indices) != set(node_template.cindices): return False if ( - getattr(node_circuit.op, "condition", None)[1] - != getattr(node_template.op, "condition", None)[1] + getattr(node_circuit.op, "_condition", None)[1] + != getattr(node_template.op, "_condition", None)[1] ): return False return True diff --git a/qiskit/transpiler/passes/routing/star_prerouting.py b/qiskit/transpiler/passes/routing/star_prerouting.py index 53bc971a268b..259d79ba636b 100644 --- a/qiskit/transpiler/passes/routing/star_prerouting.py +++ b/qiskit/transpiler/passes/routing/star_prerouting.py @@ -223,7 +223,7 @@ def filter_fn(node): return ( len(node.qargs) <= 2 and len(node.cargs) == 0 - and getattr(node.op, "condition", None) is None + and getattr(node, "condition", None) is None and not isinstance(node.op, Barrier) ) @@ -372,7 +372,7 @@ def _extract_nodes(nodes, dag): qubit_indices = [dag.find_bit(qubit).index for qubit in node.qargs] classical_bit_indices = set() - if node.op.condition is not None: + if node.condition is not None: classical_bit_indices.update(condition_resources(node.op.condition).clbits) if isinstance(node.op, SwitchCaseOp): diff --git a/qiskit/transpiler/passes/synthesis/hls_plugins.py b/qiskit/transpiler/passes/synthesis/hls_plugins.py index fa827a20ed48..3e26809a55f7 100644 --- a/qiskit/transpiler/passes/synthesis/hls_plugins.py +++ b/qiskit/transpiler/passes/synthesis/hls_plugins.py @@ -92,6 +92,19 @@ PMHSynthesisLinearFunction DefaultSynthesisLinearFunction + +Pauli Evolution Synthesis +''''''''''''''''''''''''' + +.. list-table:: Plugins for :class:`.PauliEvolutionGate` (key = ``"PauliEvolution"``) + :header-rows: 1 + + * - Plugin name + - Plugin class + - Description + * - ``"default"`` + - :class:`.PauliEvolutionSynthesisDefault` + - use a diagonalizing Clifford per Pauli term Permutation Synthesis ''''''''''''''''''''' @@ -251,6 +264,8 @@ """ +from __future__ import annotations + import numpy as np import rustworkx as rx @@ -288,8 +303,8 @@ synth_mcx_1_clean_b95, synth_mcx_gray_code, synth_mcx_noaux_v24, + synth_mcmt_vchain, ) -from qiskit.synthesis.multi_controlled import synth_mcmt_vchain from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper from .plugin import HighLevelSynthesisPlugin @@ -1029,3 +1044,11 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** high_level_object.num_target_qubits, ctrl_state, ) + + +class PauliEvolutionSynthesisDefault(HighLevelSynthesisPlugin): + """The default implementation calling the attached synthesis algorithm.""" + + def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options): + algo = high_level_object.synthesis + return algo.synthesize(high_level_object) diff --git a/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py b/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py index a73f9690ae0e..52f56aebbace 100644 --- a/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py +++ b/qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py @@ -22,6 +22,7 @@ ) from qiskit.dagcircuit import DAGCircuit from qiskit.transpiler.basepasses import TransformationPass +from qiskit.utils import deprecate_func class ConvertConditionsToIfOps(TransformationPass): @@ -31,6 +32,10 @@ class ConvertConditionsToIfOps(TransformationPass): This is a simple pass aimed at easing the conversion from the old style of using :meth:`.InstructionSet.c_if` into the new style of using more complex conditional logic.""" + @deprecate_func(since="1.3.0", removal_timeline="in Qiskit 2.0.0") + def __init__(self): + super().__init__() + def _run_inner(self, dag): """Run the pass on one :class:`.DAGCircuit`, mutating it. Returns ``True`` if the circuit was modified and ``False`` if not.""" diff --git a/qiskit/transpiler/passes/utils/gate_direction.py b/qiskit/transpiler/passes/utils/gate_direction.py index 198eb34c501f..fbdcc9321cbb 100644 --- a/qiskit/transpiler/passes/utils/gate_direction.py +++ b/qiskit/transpiler/passes/utils/gate_direction.py @@ -10,40 +10,17 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Rearrange the direction of the cx nodes to match the directed coupling map.""" - -from math import pi +"""Rearrange the direction of the 2-qubit gate nodes to match the directed coupling map.""" from qiskit.transpiler.basepasses import TransformationPass -from qiskit.transpiler.exceptions import TranspilerError - -from qiskit.converters import dag_to_circuit, circuit_to_dag -from qiskit.circuit import QuantumRegister, ControlFlowOp -from qiskit.dagcircuit import DAGCircuit, DAGOpNode -from qiskit.circuit.library.standard_gates import ( - SGate, - SdgGate, - SXGate, - HGate, - CXGate, - CZGate, - ECRGate, - RXXGate, - RYYGate, - RZZGate, - RZXGate, - SwapGate, -) - - -def _swap_node_qargs(node): - return DAGOpNode(node.op, node.qargs[::-1], node.cargs) +from qiskit._accelerate.gate_direction import fix_gate_direction_coupling, fix_gate_direction_target class GateDirection(TransformationPass): """Modify asymmetric gates to match the hardware coupling direction. - This pass makes use of the following identities:: + This pass supports replacements for the `cx`, `cz`, `ecr`, `swap`, `rzx`, `rxx`, `ryy` and + `rzz` gates, using the following identities:: ┌───┐┌───┐┌───┐ q_0: ──■── q_0: ┤ H ├┤ X ├┤ H ├ @@ -58,6 +35,9 @@ class GateDirection(TransformationPass): │ ECR │ = ┌┴───┴┐├────┤└┬───┬┘│ Ecr │├───┤ q_1: ┤1 ├ q_1: ┤ Sdg ├┤ √X ├─┤ S ├─┤0 ├┤ H ├ └──────┘ └─────┘└────┘ └───┘ └──────┘└───┘ + Note: This is done in terms of less-efficient S/SX/Sdg gates instead of the more natural + `RY(pi /2)` so we have a chance for basis translation to keep things in a discrete basis + during resynthesis, if that's what's being asked for. ┌──────┐ ┌───┐┌──────┐┌───┐ @@ -66,18 +46,18 @@ class GateDirection(TransformationPass): q_1: ┤1 ├ q_1: ┤ H ├┤0 ├┤ H ├ └──────┘ └───┘└──────┘└───┘ + cz, swap, rxx, ryy and rzz directions are fixed by reversing their qargs order. + This pass assumes that the positions of the qubits in the :attr:`.DAGCircuit.qubits` attribute are the physical qubit indices. For example if ``dag.qubits[0]`` is qubit 0 in the :class:`.CouplingMap` or :class:`.Target`. """ - _KNOWN_REPLACEMENTS = frozenset(["cx", "cz", "ecr", "swap", "rzx", "rxx", "ryy", "rzz"]) - def __init__(self, coupling_map, target=None): """GateDirection pass. Args: - coupling_map (CouplingMap): Directed graph represented a coupling map. + coupling_map (CouplingMap): Directed graph representing a coupling map. target (Target): The backend target to use for this pass. If this is specified it will be used instead of the coupling map """ @@ -85,248 +65,6 @@ def __init__(self, coupling_map, target=None): self.coupling_map = coupling_map self.target = target - # Create the replacement dag and associated register. - self._cx_dag = DAGCircuit() - qr = QuantumRegister(2) - self._cx_dag.add_qreg(qr) - self._cx_dag.apply_operation_back(HGate(), [qr[0]], []) - self._cx_dag.apply_operation_back(HGate(), [qr[1]], []) - self._cx_dag.apply_operation_back(CXGate(), [qr[1], qr[0]], []) - self._cx_dag.apply_operation_back(HGate(), [qr[0]], []) - self._cx_dag.apply_operation_back(HGate(), [qr[1]], []) - - # This is done in terms of less-efficient S/SX/Sdg gates instead of the more natural - # `RY(pi /2)` so we have a chance for basis translation to keep things in a discrete basis - # during resynthesis, if that's what's being asked for. - self._ecr_dag = DAGCircuit() - qr = QuantumRegister(2) - self._ecr_dag.global_phase = -pi / 2 - self._ecr_dag.add_qreg(qr) - self._ecr_dag.apply_operation_back(SGate(), [qr[0]], []) - self._ecr_dag.apply_operation_back(SXGate(), [qr[0]], []) - self._ecr_dag.apply_operation_back(SdgGate(), [qr[0]], []) - self._ecr_dag.apply_operation_back(SdgGate(), [qr[1]], []) - self._ecr_dag.apply_operation_back(SXGate(), [qr[1]], []) - self._ecr_dag.apply_operation_back(SGate(), [qr[1]], []) - self._ecr_dag.apply_operation_back(ECRGate(), [qr[1], qr[0]], []) - self._ecr_dag.apply_operation_back(HGate(), [qr[0]], []) - self._ecr_dag.apply_operation_back(HGate(), [qr[1]], []) - - self._cz_dag = DAGCircuit() - qr = QuantumRegister(2) - self._cz_dag.add_qreg(qr) - self._cz_dag.apply_operation_back(CZGate(), [qr[1], qr[0]], []) - - self._swap_dag = DAGCircuit() - qr = QuantumRegister(2) - self._swap_dag.add_qreg(qr) - self._swap_dag.apply_operation_back(SwapGate(), [qr[1], qr[0]], []) - - # If adding more replacements (either static or dynamic), also update the class variable - # `_KNOWN_REPLACMENTS` to include them in the error messages. - self._static_replacements = { - "cx": self._cx_dag, - "cz": self._cz_dag, - "ecr": self._ecr_dag, - "swap": self._swap_dag, - } - - @staticmethod - def _rzx_dag(parameter): - _rzx_dag = DAGCircuit() - qr = QuantumRegister(2) - _rzx_dag.add_qreg(qr) - _rzx_dag.apply_operation_back(HGate(), [qr[0]], []) - _rzx_dag.apply_operation_back(HGate(), [qr[1]], []) - _rzx_dag.apply_operation_back(RZXGate(parameter), [qr[1], qr[0]], []) - _rzx_dag.apply_operation_back(HGate(), [qr[0]], []) - _rzx_dag.apply_operation_back(HGate(), [qr[1]], []) - return _rzx_dag - - @staticmethod - def _rxx_dag(parameter): - _rxx_dag = DAGCircuit() - qr = QuantumRegister(2) - _rxx_dag.add_qreg(qr) - _rxx_dag.apply_operation_back(RXXGate(parameter), [qr[1], qr[0]], []) - return _rxx_dag - - @staticmethod - def _ryy_dag(parameter): - _ryy_dag = DAGCircuit() - qr = QuantumRegister(2) - _ryy_dag.add_qreg(qr) - _ryy_dag.apply_operation_back(RYYGate(parameter), [qr[1], qr[0]], []) - return _ryy_dag - - @staticmethod - def _rzz_dag(parameter): - _rzz_dag = DAGCircuit() - qr = QuantumRegister(2) - _rzz_dag.add_qreg(qr) - _rzz_dag.apply_operation_back(RZZGate(parameter), [qr[1], qr[0]], []) - return _rzz_dag - - def _run_coupling_map(self, dag, wire_map, edges=None): - if edges is None: - edges = set(self.coupling_map.get_edges()) - if not edges: - return dag - # Don't include directives to avoid things like barrier, which are assumed always supported. - for node in dag.op_nodes(include_directives=False): - if isinstance(node.op, ControlFlowOp): - new_op = node.op.replace_blocks( - dag_to_circuit( - self._run_coupling_map( - circuit_to_dag(block), - { - inner: wire_map[outer] - for outer, inner in zip(node.qargs, block.qubits) - }, - edges, - ) - ) - for block in node.op.blocks - ) - dag.substitute_node(node, new_op, propagate_condition=False) - continue - if len(node.qargs) != 2: - continue - if dag._has_calibration_for(node): - continue - qargs = (wire_map[node.qargs[0]], wire_map[node.qargs[1]]) - if qargs not in edges and (qargs[1], qargs[0]) not in edges: - raise TranspilerError( - f"The circuit requires a connection between physical qubits {qargs}" - ) - if qargs not in edges: - replacement = self._static_replacements.get(node.name) - if replacement is not None: - dag.substitute_node_with_dag(node, replacement) - elif node.name == "rzx": - dag.substitute_node_with_dag(node, self._rzx_dag(*node.op.params)) - elif node.name == "rxx": - dag.substitute_node_with_dag(node, self._rxx_dag(*node.op.params)) - elif node.name == "ryy": - dag.substitute_node_with_dag(node, self._ryy_dag(*node.op.params)) - elif node.name == "rzz": - dag.substitute_node_with_dag(node, self._rzz_dag(*node.op.params)) - else: - raise TranspilerError( - f"'{node.name}' would be supported on '{qargs}' if the direction were" - f" swapped, but no rules are known to do that." - f" {list(self._KNOWN_REPLACEMENTS)} can be automatically flipped." - ) - return dag - - def _run_target(self, dag, wire_map): - # Don't include directives to avoid things like barrier, which are assumed always supported. - for node in dag.op_nodes(include_directives=False): - if isinstance(node.op, ControlFlowOp): - new_op = node.op.replace_blocks( - dag_to_circuit( - self._run_target( - circuit_to_dag(block), - { - inner: wire_map[outer] - for outer, inner in zip(node.qargs, block.qubits) - }, - ) - ) - for block in node.op.blocks - ) - dag.substitute_node(node, new_op, propagate_condition=False) - continue - if len(node.qargs) != 2: - continue - if dag._has_calibration_for(node): - continue - qargs = (wire_map[node.qargs[0]], wire_map[node.qargs[1]]) - swapped = (qargs[1], qargs[0]) - if node.name in self._static_replacements: - if self.target.instruction_supported(node.name, qargs): - continue - if self.target.instruction_supported(node.name, swapped): - dag.substitute_node_with_dag(node, self._static_replacements[node.name]) - else: - raise TranspilerError( - f"The circuit requires a connection between physical qubits {qargs}" - f" for {node.name}" - ) - elif node.name == "rzx": - if self.target.instruction_supported( - qargs=qargs, operation_class=RZXGate, parameters=node.op.params - ): - continue - if self.target.instruction_supported( - qargs=swapped, operation_class=RZXGate, parameters=node.op.params - ): - dag.substitute_node_with_dag(node, self._rzx_dag(*node.op.params)) - else: - raise TranspilerError( - f"The circuit requires a connection between physical qubits {qargs}" - f" for {node.name}" - ) - elif node.name == "rxx": - if self.target.instruction_supported( - qargs=qargs, operation_class=RXXGate, parameters=node.op.params - ): - continue - if self.target.instruction_supported( - qargs=swapped, operation_class=RXXGate, parameters=node.op.params - ): - dag.substitute_node_with_dag(node, self._rxx_dag(*node.op.params)) - else: - raise TranspilerError( - f"The circuit requires a connection between physical qubits {qargs}" - f" for {node.name}" - ) - elif node.name == "ryy": - if self.target.instruction_supported( - qargs=qargs, operation_class=RYYGate, parameters=node.op.params - ): - continue - if self.target.instruction_supported( - qargs=swapped, operation_class=RYYGate, parameters=node.op.params - ): - dag.substitute_node_with_dag(node, self._ryy_dag(*node.op.params)) - else: - raise TranspilerError( - f"The circuit requires a connection between physical qubits {qargs}" - f" for {node.name}" - ) - elif node.name == "rzz": - if self.target.instruction_supported( - qargs=qargs, operation_class=RZZGate, parameters=node.op.params - ): - continue - if self.target.instruction_supported( - qargs=swapped, operation_class=RZZGate, parameters=node.op.params - ): - dag.substitute_node_with_dag(node, self._rzz_dag(*node.op.params)) - else: - raise TranspilerError( - f"The circuit requires a connection between physical qubits {qargs}" - f" for {node.name}" - ) - elif self.target.instruction_supported(node.name, qargs): - continue - elif self.target.instruction_supported(node.name, swapped) or dag._has_calibration_for( - _swap_node_qargs(node) - ): - raise TranspilerError( - f"'{node.name}' would be supported on '{qargs}' if the direction were" - f" swapped, but no rules are known to do that." - f" {list(self._KNOWN_REPLACEMENTS)} can be automatically flipped." - ) - else: - raise TranspilerError( - f"'{node.name}' with parameters '{node.op.params}' is not supported on qubits" - f" '{qargs}' in either direction." - ) - - return dag - def run(self, dag): """Run the GateDirection pass on `dag`. @@ -343,7 +81,6 @@ def run(self, dag): TranspilerError: If the circuit cannot be mapped just by flipping the cx nodes. """ - layout_map = {bit: i for i, bit in enumerate(dag.qubits)} if self.target is None: - return self._run_coupling_map(dag, layout_map) - return self._run_target(dag, layout_map) + return fix_gate_direction_coupling(dag, set(self.coupling_map.get_edges())) + return fix_gate_direction_target(dag, self.target) diff --git a/qiskit/transpiler/passmanager_config.py b/qiskit/transpiler/passmanager_config.py index baf4482a3b05..52f3d65449e5 100644 --- a/qiskit/transpiler/passmanager_config.py +++ b/qiskit/transpiler/passmanager_config.py @@ -17,11 +17,13 @@ from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.instruction_durations import InstructionDurations +from qiskit.utils.deprecate_pulse import deprecate_pulse_arg class PassManagerConfig: """Pass Manager Configuration.""" + @deprecate_pulse_arg("inst_map", predicate=lambda inst_map: inst_map is not None) def __init__( self, initial_layout=None, diff --git a/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py b/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py index 04516f4b0c6a..00b4b1418258 100644 --- a/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +++ b/qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py @@ -412,21 +412,28 @@ def generate_preset_pass_manager( "qubits_initially_zero": qubits_initially_zero, } - 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}") + with warnings.catch_warnings(): + # inst_map is deprecated in the PassManagerConfig initializer + warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + message=".*argument ``inst_map`` is deprecated as of Qiskit 1.3", + ) + 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 diff --git a/qiskit/visualization/circuit/_utils.py b/qiskit/visualization/circuit/_utils.py index d933d38b5c4a..0aa6e7c10188 100644 --- a/qiskit/visualization/circuit/_utils.py +++ b/qiskit/visualization/circuit/_utils.py @@ -503,7 +503,7 @@ def _get_gate_span(qubits, node): # type of op must be the only op in the layer if isinstance(node.op, ControlFlowOp): span = qubits - elif node.cargs or getattr(node.op, "condition", None): + elif node.cargs or getattr(node, "condition", None): span = qubits[min_index : len(qubits)] else: span = qubits[min_index : max_index + 1] @@ -582,7 +582,7 @@ def slide_from_left(self, node, index): curr_index = index last_insertable_index = -1 index_stop = -1 - if (condition := getattr(node.op, "condition", None)) is not None: + if (condition := getattr(node, "condition", None)) is not None: index_stop = max( (self.measure_map[bit] for bit in condition_resources(condition).clbits), default=index_stop, diff --git a/releasenotes/notes/add-generate-preset-pm-global-import-efb12f185f3f738b.yaml b/releasenotes/notes/1.2/add-generate-preset-pm-global-import-efb12f185f3f738b.yaml similarity index 100% rename from releasenotes/notes/add-generate-preset-pm-global-import-efb12f185f3f738b.yaml rename to releasenotes/notes/1.2/add-generate-preset-pm-global-import-efb12f185f3f738b.yaml diff --git a/releasenotes/notes/add-qft-gate-fd4e08f6721a9da4.yaml b/releasenotes/notes/1.2/add-qft-gate-fd4e08f6721a9da4.yaml similarity index 100% rename from releasenotes/notes/add-qft-gate-fd4e08f6721a9da4.yaml rename to releasenotes/notes/1.2/add-qft-gate-fd4e08f6721a9da4.yaml diff --git a/releasenotes/notes/add-sabre-all-threads-option-ad4ff7a4d045cb2b.yaml b/releasenotes/notes/1.2/add-sabre-all-threads-option-ad4ff7a4d045cb2b.yaml similarity index 100% rename from releasenotes/notes/add-sabre-all-threads-option-ad4ff7a4d045cb2b.yaml rename to releasenotes/notes/1.2/add-sabre-all-threads-option-ad4ff7a4d045cb2b.yaml diff --git a/releasenotes/notes/adjust-neato-settings-3adcc0ae9e245ce9.yaml b/releasenotes/notes/1.2/adjust-neato-settings-3adcc0ae9e245ce9.yaml similarity index 100% rename from releasenotes/notes/adjust-neato-settings-3adcc0ae9e245ce9.yaml rename to releasenotes/notes/1.2/adjust-neato-settings-3adcc0ae9e245ce9.yaml diff --git a/releasenotes/notes/annotated-params-116288d5628f7ee8.yaml b/releasenotes/notes/1.2/annotated-params-116288d5628f7ee8.yaml similarity index 100% rename from releasenotes/notes/annotated-params-116288d5628f7ee8.yaml rename to releasenotes/notes/1.2/annotated-params-116288d5628f7ee8.yaml diff --git a/releasenotes/notes/avoid-op-creation-804c0bed6c408911.yaml b/releasenotes/notes/1.2/avoid-op-creation-804c0bed6c408911.yaml similarity index 100% rename from releasenotes/notes/avoid-op-creation-804c0bed6c408911.yaml rename to releasenotes/notes/1.2/avoid-op-creation-804c0bed6c408911.yaml diff --git a/releasenotes/notes/backendv1-d0d0642ed38fed3c.yaml b/releasenotes/notes/1.2/backendv1-d0d0642ed38fed3c.yaml similarity index 100% rename from releasenotes/notes/backendv1-d0d0642ed38fed3c.yaml rename to releasenotes/notes/1.2/backendv1-d0d0642ed38fed3c.yaml diff --git a/releasenotes/notes/bitarray-postselect-659b8f7801ccaa60.yaml b/releasenotes/notes/1.2/bitarray-postselect-659b8f7801ccaa60.yaml similarity index 100% rename from releasenotes/notes/bitarray-postselect-659b8f7801ccaa60.yaml rename to releasenotes/notes/1.2/bitarray-postselect-659b8f7801ccaa60.yaml diff --git a/releasenotes/notes/circuit-gates-rust-5c6ab6c58f7fd2c9.yaml b/releasenotes/notes/1.2/circuit-gates-rust-5c6ab6c58f7fd2c9.yaml similarity index 100% rename from releasenotes/notes/circuit-gates-rust-5c6ab6c58f7fd2c9.yaml rename to releasenotes/notes/1.2/circuit-gates-rust-5c6ab6c58f7fd2c9.yaml diff --git a/releasenotes/notes/default-level-2-generate-preset-passmanager-ec758ddc896ae2d6.yaml b/releasenotes/notes/1.2/default-level-2-generate-preset-passmanager-ec758ddc896ae2d6.yaml similarity index 100% rename from releasenotes/notes/default-level-2-generate-preset-passmanager-ec758ddc896ae2d6.yaml rename to releasenotes/notes/1.2/default-level-2-generate-preset-passmanager-ec758ddc896ae2d6.yaml diff --git a/releasenotes/notes/deprecate-circuit-internal-helpers-ee65fbac455de47c.yaml b/releasenotes/notes/1.2/deprecate-circuit-internal-helpers-ee65fbac455de47c.yaml similarity index 100% rename from releasenotes/notes/deprecate-circuit-internal-helpers-ee65fbac455de47c.yaml rename to releasenotes/notes/1.2/deprecate-circuit-internal-helpers-ee65fbac455de47c.yaml diff --git a/releasenotes/notes/deprecate-legacy-circuit-instruction-8a332ab09de73766.yaml b/releasenotes/notes/1.2/deprecate-legacy-circuit-instruction-8a332ab09de73766.yaml similarity index 100% rename from releasenotes/notes/deprecate-legacy-circuit-instruction-8a332ab09de73766.yaml rename to releasenotes/notes/1.2/deprecate-legacy-circuit-instruction-8a332ab09de73766.yaml diff --git a/releasenotes/notes/deprecate-primitives-v1.yaml b/releasenotes/notes/1.2/deprecate-primitives-v1.yaml similarity index 100% rename from releasenotes/notes/deprecate-primitives-v1.yaml rename to releasenotes/notes/1.2/deprecate-primitives-v1.yaml diff --git a/releasenotes/notes/deprecate-visualize_transition-8c1d257b7f37aa58.yaml b/releasenotes/notes/1.2/deprecate-visualize_transition-8c1d257b7f37aa58.yaml similarity index 100% rename from releasenotes/notes/deprecate-visualize_transition-8c1d257b7f37aa58.yaml rename to releasenotes/notes/1.2/deprecate-visualize_transition-8c1d257b7f37aa58.yaml diff --git a/releasenotes/notes/deprecate_assemble-67486b4d0a8d4f96.yaml b/releasenotes/notes/1.2/deprecate_assemble-67486b4d0a8d4f96.yaml similarity index 100% rename from releasenotes/notes/deprecate_assemble-67486b4d0a8d4f96.yaml rename to releasenotes/notes/1.2/deprecate_assemble-67486b4d0a8d4f96.yaml diff --git a/releasenotes/notes/extract-standard-parametric-controlled-1a495f6f7ce89397.yaml b/releasenotes/notes/1.2/extract-standard-parametric-controlled-1a495f6f7ce89397.yaml similarity index 100% rename from releasenotes/notes/extract-standard-parametric-controlled-1a495f6f7ce89397.yaml rename to releasenotes/notes/1.2/extract-standard-parametric-controlled-1a495f6f7ce89397.yaml diff --git a/releasenotes/notes/fix-2q-basis-decomposer-non-std-kak-gate-edc69ffb5d9ef302.yaml b/releasenotes/notes/1.2/fix-2q-basis-decomposer-non-std-kak-gate-edc69ffb5d9ef302.yaml similarity index 100% rename from releasenotes/notes/fix-2q-basis-decomposer-non-std-kak-gate-edc69ffb5d9ef302.yaml rename to releasenotes/notes/1.2/fix-2q-basis-decomposer-non-std-kak-gate-edc69ffb5d9ef302.yaml diff --git a/releasenotes/notes/fix-InstructionDurations-b47a9770b424d7a0.yaml b/releasenotes/notes/1.2/fix-InstructionDurations-b47a9770b424d7a0.yaml similarity index 100% rename from releasenotes/notes/fix-InstructionDurations-b47a9770b424d7a0.yaml rename to releasenotes/notes/1.2/fix-InstructionDurations-b47a9770b424d7a0.yaml diff --git a/releasenotes/notes/fix-bitarray-fromcounts-nobits-82958a596b3489ec.yaml b/releasenotes/notes/1.2/fix-bitarray-fromcounts-nobits-82958a596b3489ec.yaml similarity index 100% rename from releasenotes/notes/fix-bitarray-fromcounts-nobits-82958a596b3489ec.yaml rename to releasenotes/notes/1.2/fix-bitarray-fromcounts-nobits-82958a596b3489ec.yaml diff --git a/releasenotes/notes/fix-bitarray-slice-bits-shots-c9cb7e5d907722f5.yaml b/releasenotes/notes/1.2/fix-bitarray-slice-bits-shots-c9cb7e5d907722f5.yaml similarity index 100% rename from releasenotes/notes/fix-bitarray-slice-bits-shots-c9cb7e5d907722f5.yaml rename to releasenotes/notes/1.2/fix-bitarray-slice-bits-shots-c9cb7e5d907722f5.yaml diff --git a/releasenotes/notes/fix-consolidate-blocks-custom-gate-no-target-e2d1e0b0ee7ace11.yaml b/releasenotes/notes/1.2/fix-consolidate-blocks-custom-gate-no-target-e2d1e0b0ee7ace11.yaml similarity index 100% rename from releasenotes/notes/fix-consolidate-blocks-custom-gate-no-target-e2d1e0b0ee7ace11.yaml rename to releasenotes/notes/1.2/fix-consolidate-blocks-custom-gate-no-target-e2d1e0b0ee7ace11.yaml diff --git a/releasenotes/notes/fix-cu-rust-6464b6893ecca1b3.yaml b/releasenotes/notes/1.2/fix-cu-rust-6464b6893ecca1b3.yaml similarity index 100% rename from releasenotes/notes/fix-cu-rust-6464b6893ecca1b3.yaml rename to releasenotes/notes/1.2/fix-cu-rust-6464b6893ecca1b3.yaml diff --git a/releasenotes/notes/fix-dd-misalignment-msg-76fe16e5eb4ae670.yaml b/releasenotes/notes/1.2/fix-dd-misalignment-msg-76fe16e5eb4ae670.yaml similarity index 100% rename from releasenotes/notes/fix-dd-misalignment-msg-76fe16e5eb4ae670.yaml rename to releasenotes/notes/1.2/fix-dd-misalignment-msg-76fe16e5eb4ae670.yaml diff --git a/releasenotes/notes/fix-elide-permutations-1b9e1d10c3abb2a4.yaml b/releasenotes/notes/1.2/fix-elide-permutations-1b9e1d10c3abb2a4.yaml similarity index 100% rename from releasenotes/notes/fix-elide-permutations-1b9e1d10c3abb2a4.yaml rename to releasenotes/notes/1.2/fix-elide-permutations-1b9e1d10c3abb2a4.yaml diff --git a/releasenotes/notes/fix-hoare-opt-56d1ca6a07f07a2d.yaml b/releasenotes/notes/1.2/fix-hoare-opt-56d1ca6a07f07a2d.yaml similarity index 100% rename from releasenotes/notes/fix-hoare-opt-56d1ca6a07f07a2d.yaml rename to releasenotes/notes/1.2/fix-hoare-opt-56d1ca6a07f07a2d.yaml diff --git a/releasenotes/notes/fix-kwarg-validation-BitArray-1bf542a1fb5c15c6.yaml b/releasenotes/notes/1.2/fix-kwarg-validation-BitArray-1bf542a1fb5c15c6.yaml similarity index 100% rename from releasenotes/notes/fix-kwarg-validation-BitArray-1bf542a1fb5c15c6.yaml rename to releasenotes/notes/1.2/fix-kwarg-validation-BitArray-1bf542a1fb5c15c6.yaml diff --git a/releasenotes/notes/fix-mcx-performance-de86bcc9f969b81e.yaml b/releasenotes/notes/1.2/fix-mcx-performance-de86bcc9f969b81e.yaml similarity index 100% rename from releasenotes/notes/fix-mcx-performance-de86bcc9f969b81e.yaml rename to releasenotes/notes/1.2/fix-mcx-performance-de86bcc9f969b81e.yaml diff --git a/releasenotes/notes/fix-negative-seed-pm-2813a62a020da115.yaml b/releasenotes/notes/1.2/fix-negative-seed-pm-2813a62a020da115.yaml similarity index 100% rename from releasenotes/notes/fix-negative-seed-pm-2813a62a020da115.yaml rename to releasenotes/notes/1.2/fix-negative-seed-pm-2813a62a020da115.yaml diff --git a/releasenotes/notes/fix-potential-non-determinism-dense-layout-da66de0217121146.yaml b/releasenotes/notes/1.2/fix-potential-non-determinism-dense-layout-da66de0217121146.yaml similarity index 100% rename from releasenotes/notes/fix-potential-non-determinism-dense-layout-da66de0217121146.yaml rename to releasenotes/notes/1.2/fix-potential-non-determinism-dense-layout-da66de0217121146.yaml diff --git a/releasenotes/notes/fix-qft-plugins-7106029d33c44b96.yaml b/releasenotes/notes/1.2/fix-qft-plugins-7106029d33c44b96.yaml similarity index 100% rename from releasenotes/notes/fix-qft-plugins-7106029d33c44b96.yaml rename to releasenotes/notes/1.2/fix-qft-plugins-7106029d33c44b96.yaml diff --git a/releasenotes/notes/fix-qpy-parsing-123-75357c3709e35963.yaml b/releasenotes/notes/1.2/fix-qpy-parsing-123-75357c3709e35963.yaml similarity index 100% rename from releasenotes/notes/fix-qpy-parsing-123-75357c3709e35963.yaml rename to releasenotes/notes/1.2/fix-qpy-parsing-123-75357c3709e35963.yaml diff --git a/releasenotes/notes/fix-qpy-symengine-compat-858970a9a1d6bc14.yaml b/releasenotes/notes/1.2/fix-qpy-symengine-compat-858970a9a1d6bc14.yaml similarity index 100% rename from releasenotes/notes/fix-qpy-symengine-compat-858970a9a1d6bc14.yaml rename to releasenotes/notes/1.2/fix-qpy-symengine-compat-858970a9a1d6bc14.yaml diff --git a/releasenotes/notes/fix-sabre-releasevalve-7f9af9bfc0482e04.yaml b/releasenotes/notes/1.2/fix-sabre-releasevalve-7f9af9bfc0482e04.yaml similarity index 100% rename from releasenotes/notes/fix-sabre-releasevalve-7f9af9bfc0482e04.yaml rename to releasenotes/notes/1.2/fix-sabre-releasevalve-7f9af9bfc0482e04.yaml diff --git a/releasenotes/notes/fix-split-2q-unitaries-custom-gate-d10f7670a35548f4.yaml b/releasenotes/notes/1.2/fix-split-2q-unitaries-custom-gate-d10f7670a35548f4.yaml similarity index 100% rename from releasenotes/notes/fix-split-2q-unitaries-custom-gate-d10f7670a35548f4.yaml rename to releasenotes/notes/1.2/fix-split-2q-unitaries-custom-gate-d10f7670a35548f4.yaml diff --git a/releasenotes/notes/fix-stateprep-normalize-a8057c339ba619bd.yaml b/releasenotes/notes/1.2/fix-stateprep-normalize-a8057c339ba619bd.yaml similarity index 100% rename from releasenotes/notes/fix-stateprep-normalize-a8057c339ba619bd.yaml rename to releasenotes/notes/1.2/fix-stateprep-normalize-a8057c339ba619bd.yaml diff --git a/releasenotes/notes/fix-statevector-sampler-c_if-9753f8f97a3d0ff5.yaml b/releasenotes/notes/1.2/fix-statevector-sampler-c_if-9753f8f97a3d0ff5.yaml similarity index 100% rename from releasenotes/notes/fix-statevector-sampler-c_if-9753f8f97a3d0ff5.yaml rename to releasenotes/notes/1.2/fix-statevector-sampler-c_if-9753f8f97a3d0ff5.yaml diff --git a/releasenotes/notes/fix-synth-qregs-7662681c0ff02511.yaml b/releasenotes/notes/1.2/fix-synth-qregs-7662681c0ff02511.yaml similarity index 100% rename from releasenotes/notes/fix-synth-qregs-7662681c0ff02511.yaml rename to releasenotes/notes/1.2/fix-synth-qregs-7662681c0ff02511.yaml diff --git a/releasenotes/notes/fix_initialize_gates_to_uncompute-d0dba6a642d07f30.yaml b/releasenotes/notes/1.2/fix_initialize_gates_to_uncompute-d0dba6a642d07f30.yaml similarity index 100% rename from releasenotes/notes/fix_initialize_gates_to_uncompute-d0dba6a642d07f30.yaml rename to releasenotes/notes/1.2/fix_initialize_gates_to_uncompute-d0dba6a642d07f30.yaml diff --git a/releasenotes/notes/improve-quantum-causal-cone-f63eaaa9ab658811.yaml b/releasenotes/notes/1.2/improve-quantum-causal-cone-f63eaaa9ab658811.yaml similarity index 100% rename from releasenotes/notes/improve-quantum-causal-cone-f63eaaa9ab658811.yaml rename to releasenotes/notes/1.2/improve-quantum-causal-cone-f63eaaa9ab658811.yaml diff --git a/releasenotes/notes/linear-binary-matrix-utils-rust-c48b5577749c34ab.yaml b/releasenotes/notes/1.2/linear-binary-matrix-utils-rust-c48b5577749c34ab.yaml similarity index 100% rename from releasenotes/notes/linear-binary-matrix-utils-rust-c48b5577749c34ab.yaml rename to releasenotes/notes/1.2/linear-binary-matrix-utils-rust-c48b5577749c34ab.yaml diff --git a/releasenotes/notes/mcx_recursive_clean_ancilla-2b0f6956e0f4cbbd.yaml b/releasenotes/notes/1.2/mcx_recursive_clean_ancilla-2b0f6956e0f4cbbd.yaml similarity index 100% rename from releasenotes/notes/mcx_recursive_clean_ancilla-2b0f6956e0f4cbbd.yaml rename to releasenotes/notes/1.2/mcx_recursive_clean_ancilla-2b0f6956e0f4cbbd.yaml diff --git a/releasenotes/notes/mcxvchain_dirty_auxiliary-5ea4037557209f6e.yaml b/releasenotes/notes/1.2/mcxvchain_dirty_auxiliary-5ea4037557209f6e.yaml similarity index 100% rename from releasenotes/notes/mcxvchain_dirty_auxiliary-5ea4037557209f6e.yaml rename to releasenotes/notes/1.2/mcxvchain_dirty_auxiliary-5ea4037557209f6e.yaml diff --git a/releasenotes/notes/no-elide-routing-none-7c1bebf1283d48c0.yaml b/releasenotes/notes/1.2/no-elide-routing-none-7c1bebf1283d48c0.yaml similarity index 100% rename from releasenotes/notes/no-elide-routing-none-7c1bebf1283d48c0.yaml rename to releasenotes/notes/1.2/no-elide-routing-none-7c1bebf1283d48c0.yaml diff --git a/releasenotes/notes/oxidize-acg-0294a87c0d5974fa.yaml b/releasenotes/notes/1.2/oxidize-acg-0294a87c0d5974fa.yaml similarity index 100% rename from releasenotes/notes/oxidize-acg-0294a87c0d5974fa.yaml rename to releasenotes/notes/1.2/oxidize-acg-0294a87c0d5974fa.yaml diff --git a/releasenotes/notes/oxidize-permbasic-be27578187ac472f.yaml b/releasenotes/notes/1.2/oxidize-permbasic-be27578187ac472f.yaml similarity index 100% rename from releasenotes/notes/oxidize-permbasic-be27578187ac472f.yaml rename to releasenotes/notes/1.2/oxidize-permbasic-be27578187ac472f.yaml diff --git a/releasenotes/notes/oxidize-pmh-ec74e4002510eaad.yaml b/releasenotes/notes/1.2/oxidize-pmh-ec74e4002510eaad.yaml similarity index 100% rename from releasenotes/notes/oxidize-pmh-ec74e4002510eaad.yaml rename to releasenotes/notes/1.2/oxidize-pmh-ec74e4002510eaad.yaml diff --git a/releasenotes/notes/oxidize-synth-clifford-bm-91d8b974ca0522b7.yaml b/releasenotes/notes/1.2/oxidize-synth-clifford-bm-91d8b974ca0522b7.yaml similarity index 100% rename from releasenotes/notes/oxidize-synth-clifford-bm-91d8b974ca0522b7.yaml rename to releasenotes/notes/1.2/oxidize-synth-clifford-bm-91d8b974ca0522b7.yaml diff --git a/releasenotes/notes/oxidize-synth-clifford-greedy-0739e9688bc4eedd.yaml b/releasenotes/notes/1.2/oxidize-synth-clifford-greedy-0739e9688bc4eedd.yaml similarity index 100% rename from releasenotes/notes/oxidize-synth-clifford-greedy-0739e9688bc4eedd.yaml rename to releasenotes/notes/1.2/oxidize-synth-clifford-greedy-0739e9688bc4eedd.yaml diff --git a/releasenotes/notes/peephole-before-routing-c3d184b740bb7a8b.yaml b/releasenotes/notes/1.2/peephole-before-routing-c3d184b740bb7a8b.yaml similarity index 100% rename from releasenotes/notes/peephole-before-routing-c3d184b740bb7a8b.yaml rename to releasenotes/notes/1.2/peephole-before-routing-c3d184b740bb7a8b.yaml diff --git a/releasenotes/notes/port_star_prerouting-13fae3ff78feb5e3.yaml b/releasenotes/notes/1.2/port_star_prerouting-13fae3ff78feb5e3.yaml similarity index 100% rename from releasenotes/notes/port_star_prerouting-13fae3ff78feb5e3.yaml rename to releasenotes/notes/1.2/port_star_prerouting-13fae3ff78feb5e3.yaml diff --git a/releasenotes/notes/qasm2-builtin-gate-d80c2868cdf5f958.yaml b/releasenotes/notes/1.2/qasm2-builtin-gate-d80c2868cdf5f958.yaml similarity index 100% rename from releasenotes/notes/qasm2-builtin-gate-d80c2868cdf5f958.yaml rename to releasenotes/notes/1.2/qasm2-builtin-gate-d80c2868cdf5f958.yaml diff --git a/releasenotes/notes/qasm3-basis-gates-keyword-c5998bff1e178715.yaml b/releasenotes/notes/1.2/qasm3-basis-gates-keyword-c5998bff1e178715.yaml similarity index 100% rename from releasenotes/notes/qasm3-basis-gates-keyword-c5998bff1e178715.yaml rename to releasenotes/notes/1.2/qasm3-basis-gates-keyword-c5998bff1e178715.yaml diff --git a/releasenotes/notes/qasm3-includes-ceb56f49b8c190ff.yaml b/releasenotes/notes/1.2/qasm3-includes-ceb56f49b8c190ff.yaml similarity index 100% rename from releasenotes/notes/qasm3-includes-ceb56f49b8c190ff.yaml rename to releasenotes/notes/1.2/qasm3-includes-ceb56f49b8c190ff.yaml diff --git a/releasenotes/notes/qasm3-symbol-table-efad35629639c77d.yaml b/releasenotes/notes/1.2/qasm3-symbol-table-efad35629639c77d.yaml similarity index 100% rename from releasenotes/notes/qasm3-symbol-table-efad35629639c77d.yaml rename to releasenotes/notes/1.2/qasm3-symbol-table-efad35629639c77d.yaml diff --git a/releasenotes/notes/replace-initialization-algorithm-by-isometry-41f9ffa58f72ece5.yaml b/releasenotes/notes/1.2/replace-initialization-algorithm-by-isometry-41f9ffa58f72ece5.yaml similarity index 100% rename from releasenotes/notes/replace-initialization-algorithm-by-isometry-41f9ffa58f72ece5.yaml rename to releasenotes/notes/1.2/replace-initialization-algorithm-by-isometry-41f9ffa58f72ece5.yaml diff --git a/releasenotes/notes/restrict-split2q-d51d840cc7a7a482.yaml b/releasenotes/notes/1.2/restrict-split2q-d51d840cc7a7a482.yaml similarity index 100% rename from releasenotes/notes/restrict-split2q-d51d840cc7a7a482.yaml rename to releasenotes/notes/1.2/restrict-split2q-d51d840cc7a7a482.yaml diff --git a/releasenotes/notes/sabre_level0-1524f01965257f3f.yaml b/releasenotes/notes/1.2/sabre_level0-1524f01965257f3f.yaml similarity index 100% rename from releasenotes/notes/sabre_level0-1524f01965257f3f.yaml rename to releasenotes/notes/1.2/sabre_level0-1524f01965257f3f.yaml diff --git a/releasenotes/notes/synth_permutation_depth_lnn_kms-c444f3a363f3a903.yaml b/releasenotes/notes/1.2/synth_permutation_depth_lnn_kms-c444f3a363f3a903.yaml similarity index 100% rename from releasenotes/notes/synth_permutation_depth_lnn_kms-c444f3a363f3a903.yaml rename to releasenotes/notes/1.2/synth_permutation_depth_lnn_kms-c444f3a363f3a903.yaml diff --git a/releasenotes/notes/unary_pos_for_parameterexpression-6421421b6dc20fbb.yaml b/releasenotes/notes/1.2/unary_pos_for_parameterexpression-6421421b6dc20fbb.yaml similarity index 100% rename from releasenotes/notes/unary_pos_for_parameterexpression-6421421b6dc20fbb.yaml rename to releasenotes/notes/1.2/unary_pos_for_parameterexpression-6421421b6dc20fbb.yaml diff --git a/releasenotes/notes/update-primitive-v2-metadata-cf1226e2d6477688.yaml b/releasenotes/notes/1.2/update-primitive-v2-metadata-cf1226e2d6477688.yaml similarity index 100% rename from releasenotes/notes/update-primitive-v2-metadata-cf1226e2d6477688.yaml rename to releasenotes/notes/1.2/update-primitive-v2-metadata-cf1226e2d6477688.yaml diff --git a/releasenotes/notes/update-qasm3-lib-40e15bc24234970d.yaml b/releasenotes/notes/1.2/update-qasm3-lib-40e15bc24234970d.yaml similarity index 100% rename from releasenotes/notes/update-qasm3-lib-40e15bc24234970d.yaml rename to releasenotes/notes/1.2/update-qasm3-lib-40e15bc24234970d.yaml diff --git a/releasenotes/notes/update-rustworkx-min-version-4f07aacfebccae80.yaml b/releasenotes/notes/1.2/update-rustworkx-min-version-4f07aacfebccae80.yaml similarity index 100% rename from releasenotes/notes/update-rustworkx-min-version-4f07aacfebccae80.yaml rename to releasenotes/notes/1.2/update-rustworkx-min-version-4f07aacfebccae80.yaml diff --git a/releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml b/releasenotes/notes/1.2/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml similarity index 100% rename from releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml rename to releasenotes/notes/1.2/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml diff --git a/releasenotes/notes/workaroud_12361-994d0ac2d2a6ed41.yaml b/releasenotes/notes/1.2/workaroud_12361-994d0ac2d2a6ed41.yaml similarity index 100% rename from releasenotes/notes/workaroud_12361-994d0ac2d2a6ed41.yaml rename to releasenotes/notes/1.2/workaroud_12361-994d0ac2d2a6ed41.yaml diff --git a/releasenotes/notes/assign-parameters-perf-regression-fc8c9db134b1763d.yaml b/releasenotes/notes/assign-parameters-perf-regression-fc8c9db134b1763d.yaml new file mode 100644 index 000000000000..28f29b63ea2a --- /dev/null +++ b/releasenotes/notes/assign-parameters-perf-regression-fc8c9db134b1763d.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixed a performance regression in :meth:`.QuantumCircuit.assign_parameters` introduced in Qiskit + 1.2.0 when calling the method in a tight loop, binding only a small number of parameters out of + a heavily parametric circuit on each iteration. If possible, it is still more performant to + call :meth:`~.QuantumCircuit.assign_parameters` only once, with all assignments at the same + time, as this reduces the proportion of time spent on input normalization and error-checking + overhead. diff --git a/releasenotes/notes/backend-sampler-v2-level1-dc13af460cd38454.yaml b/releasenotes/notes/backend-sampler-v2-level1-dc13af460cd38454.yaml new file mode 100644 index 000000000000..594e610939cf --- /dev/null +++ b/releasenotes/notes/backend-sampler-v2-level1-dc13af460cd38454.yaml @@ -0,0 +1,17 @@ +--- +features: + - | + Support for level 1 data was added to :class:`~.BackendSamplerV2` as was + support for passing options through to the ``run()`` method of the wrapped + :class:`~.BackendV2`. The run options can be specified using a + ``"run_options"`` entry inside of the ``options`` dicitonary passed to + :class:`~.BackendSamplerV2`. The ``"run_options"`` entry should be a + dictionary mapping argument names to values for passing to the backend's + ``run()`` method. When a ``"meas_level"`` option with a value of 1 is set + in the run options, the results from the backend will be treated as level 1 + results rather as bit arrays (the level 2 format). +upgrade: + - | + When using :class:`~.BackendSamplerV2`, circuit metadata is no longer + cleared before passing circuits to the ``run()`` method of the wrapped + :class:`~.BackendV2` instance. diff --git a/releasenotes/notes/basicsimulator-config-copy-5ecdfdf161e488f2.yaml b/releasenotes/notes/basicsimulator-config-copy-5ecdfdf161e488f2.yaml new file mode 100644 index 000000000000..b5ec0112004e --- /dev/null +++ b/releasenotes/notes/basicsimulator-config-copy-5ecdfdf161e488f2.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + For :class:`~.BasicSimulator`, the ``basis_gates`` entry in the + configuration instance returned by the ``configuration()`` is now a list + rather than a ``dict_keys`` instance, matching the expected type and + allowing for configuration instance to be deep copied. diff --git a/releasenotes/notes/deprecate-condition_c_if-9548e5522814fe9c.yaml b/releasenotes/notes/deprecate-condition_c_if-9548e5522814fe9c.yaml new file mode 100644 index 000000000000..e096273f3c8e --- /dev/null +++ b/releasenotes/notes/deprecate-condition_c_if-9548e5522814fe9c.yaml @@ -0,0 +1,38 @@ +--- +deprecations_circuits: + - | + Deprecated the :attr:`.Instruction.condition` attribute and the + :meth:`.Instruction.c_if` method. They will be removed + in Qiskit 2.0, along with any uses in the Qiskit data + model. This functionality has been superseded by the :class:`.IfElseOp` class + which can be used to describe a classical condition in a circuit. For + example, a circuit using :meth:`.Instruction.c_if` like:: + + from qiskit.circuit import QuantumCircuit + + qc = QuantumCircuit(2, 2) + qc.h(0) + qc.x(0).c_if(0, 1) + qc.z(1.c_if(1, 0) + qc.measure(0, 0) + qc.measure(1, 1) + + can be rewritten as:: + + qc = QuantumCircuit(2, 2) + qc.h(0) + with expected.if_test((expected.clbits[0], True)): + qc.x(0) + with expected.if_test((expected.clbits[1], False)): + qc.z(1) + qc.measure(0, 0) + qc.measure(1, 1) + + The now deprecated :class:`.ConvertConditionsToIfOps` transpiler pass can + be used to automate this conversion for existing circuits. +deprecations_transpiler: + - | + The transpiler pass :class:`.ConvertConditionsToIfOps` has been deprecated + and will be removed in Qiskit 2.0.0. This class is now deprecated because + the underlying data model for :attr:`.Instruction.condition` which this + pass is converting from has been deprecated and will be removed in 2.0.0. diff --git a/releasenotes/notes/deprecate-pulse-package-07a621be1db7fa30.yaml b/releasenotes/notes/deprecate-pulse-package-07a621be1db7fa30.yaml index 5b4941f9d847..479c31556eb5 100644 --- a/releasenotes/notes/deprecate-pulse-package-07a621be1db7fa30.yaml +++ b/releasenotes/notes/deprecate-pulse-package-07a621be1db7fa30.yaml @@ -43,8 +43,8 @@ deprecations_transpiler: * :class:`~qiskit.transpiler.passes.EchoRZXWeylDecomposition` - | The `inst_map` argument in :func:`~qiskit.transpiler.generate_preset_pass_manager`, - :meth:`~transpiler.target.Target.from_configuration` and :func:`~qiskit.transpiler.preset_passmanagers.common.generate_scheduling` - is being deprecated. + :meth:`~transpiler.target.Target.from_configuration`, :class:`~qiskit.transpiler.PassManagerConfig` initializer + and :func:`~qiskit.transpiler.preset_passmanagers.common.generate_scheduling` is being deprecated. - | The `calibration` argument in :func:`~qiskit.transpiler.target.InstructionProperties` initializer methods is being deprecated. diff --git a/releasenotes/notes/faster-pauli-decomposition-faf2be01a6e75fff.yaml b/releasenotes/notes/faster-pauli-decomposition-faf2be01a6e75fff.yaml new file mode 100644 index 000000000000..56ad1a725f9a --- /dev/null +++ b/releasenotes/notes/faster-pauli-decomposition-faf2be01a6e75fff.yaml @@ -0,0 +1,7 @@ +--- +features_quantum_info: + - | + The performance of :meth:`.SparsePauliOp.from_operator` has been optimized on top of the + algorithm improvements methods introduced in Qiskit 1.0. It is now approximately five times + faster than before for fully dense matrices, taking approximately 40ms to decompose a 10q + operator involving all Pauli terms. diff --git a/releasenotes/notes/modernize-circuit-library-6e0be83421fd480b.yaml b/releasenotes/notes/modernize-circuit-library-6e0be83421fd480b.yaml new file mode 100644 index 000000000000..a1a4bb591860 --- /dev/null +++ b/releasenotes/notes/modernize-circuit-library-6e0be83421fd480b.yaml @@ -0,0 +1,12 @@ +--- +features_circuits: + - | + As a part of circuit library modernization, each of the following quantum + circuits is either also represented as a :class:`.Gate` object or can be + constructed using a synthesis method: + + * :class:`.GraphState` is represented by :class:`.GraphStateGate`, + * :class:`.FourierChecking` can be constructed using :meth:`.fourier_checking`, + * :class:`.UnitaryOverlap` can be constructed using :meth:`.unitary_overlap`, + * :class:`.HiddenLinearFunction` can be constructed using :meth:`.hidden_linear_function`, + * :class:`.PhaseEstimation` can be constructed using :meth:`.phase_estimation`. diff --git a/releasenotes/notes/pauli-evo-plugins-612850146c3f7d49.yaml b/releasenotes/notes/pauli-evo-plugins-612850146c3f7d49.yaml new file mode 100644 index 000000000000..e4693aa4f3f0 --- /dev/null +++ b/releasenotes/notes/pauli-evo-plugins-612850146c3f7d49.yaml @@ -0,0 +1,35 @@ +--- +features_quantum_info: + - | + Added :meth:`.SparsePauliOperator.to_sparse_list` to convert an operator into + a sparse list format. This works inversely to :meth:`.SparsePauliOperator.from_sparse_list`. + For example:: + + from qiskit.quantum_info import SparsePauliOp + + op = SparsePauliOp(["XIII", "IZZI"], coeffs=[1, 2]) + sparse = op.to_sparse_list() # [("X", [3], 1), ("ZZ", [1, 2], 2)] + + other = SparsePauliOp.from_sparse_list(sparse, op.num_qubits) + print(other == op) # True + +features_synthesis: + - | + Added :meth:`.ProductFormula.expand` which allows to view the expansion of a product formula + in a sparse Pauli format. + - | + Added the plugin structure for the :class:`.PauliEvolutionGate`. The default plugin, + :class:`.PauliEvolutionSynthesisDefault`, constructs circuit as before, but faster as it + internally uses Rust. The larger the circuit (e.g. by the Hamiltonian size, the number + of timesteps, or the Suzuki-Trotter order), the higher the speedup. For example, + a 100-qubit Heisenberg Hamiltonian with 10 timesteps and a 4th-order Trotter formula is + now constructed ~9.4x faster. + +upgrade: + - | + The following classes now use the :math:`\sqrt{X}` operation to diagonalize the Pauli-Y + operator: :class:`.PauliEvolutionGate`, :class:`.EvolvedOperatorAnsatz`, + :class:`.PauliFeatureMap`. Previously, these classes used either :math:`H S` or + :math:`R_X(-\pi/2)` as basis transformation. Using the :math:`\sqrt{X}` operation, + represented by the :class:`.SXGate` is more efficient as it uses only a single gate + implemented as singleton. diff --git a/test/python/circuit/classical/test_expr_constructors.py b/test/python/circuit/classical/test_expr_constructors.py index 012697a17dd0..6500e5593ac2 100644 --- a/test/python/circuit/classical/test_expr_constructors.py +++ b/test/python/circuit/classical/test_expr_constructors.py @@ -26,46 +26,54 @@ def test_lift_legacy_condition(self): clbit = Clbit() inst = Instruction("custom", 1, 0, []) - inst.c_if(cr, 7) - self.assertEqual( - expr.lift_legacy_condition(inst.condition), - expr.Binary( - expr.Binary.Op.EQUAL, - expr.Var(cr, types.Uint(cr.size)), - expr.Value(7, types.Uint(cr.size)), - types.Bool(), - ), - ) + with self.assertWarns(DeprecationWarning): + inst.c_if(cr, 7) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + expr.lift_legacy_condition(inst.condition), + expr.Binary( + expr.Binary.Op.EQUAL, + expr.Var(cr, types.Uint(cr.size)), + expr.Value(7, types.Uint(cr.size)), + types.Bool(), + ), + ) inst = Instruction("custom", 1, 0, []) - inst.c_if(cr, 255) - self.assertEqual( - expr.lift_legacy_condition(inst.condition), - expr.Binary( - expr.Binary.Op.EQUAL, - expr.Cast(expr.Var(cr, types.Uint(cr.size)), types.Uint(8), implicit=True), - expr.Value(255, types.Uint(8)), - types.Bool(), - ), - ) + with self.assertWarns(DeprecationWarning): + inst.c_if(cr, 255) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + expr.lift_legacy_condition(inst.condition), + expr.Binary( + expr.Binary.Op.EQUAL, + expr.Cast(expr.Var(cr, types.Uint(cr.size)), types.Uint(8), implicit=True), + expr.Value(255, types.Uint(8)), + types.Bool(), + ), + ) inst = Instruction("custom", 1, 0, []) - inst.c_if(clbit, False) - self.assertEqual( - expr.lift_legacy_condition(inst.condition), - expr.Unary( - expr.Unary.Op.LOGIC_NOT, - expr.Var(clbit, types.Bool()), - types.Bool(), - ), - ) + with self.assertWarns(DeprecationWarning): + inst.c_if(clbit, False) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + expr.lift_legacy_condition(inst.condition), + expr.Unary( + expr.Unary.Op.LOGIC_NOT, + expr.Var(clbit, types.Bool()), + types.Bool(), + ), + ) inst = Instruction("custom", 1, 0, []) - inst.c_if(clbit, True) - self.assertEqual( - expr.lift_legacy_condition(inst.condition), - expr.Var(clbit, types.Bool()), - ) + with self.assertWarns(DeprecationWarning): + inst.c_if(clbit, True) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + expr.lift_legacy_condition(inst.condition), + expr.Var(clbit, types.Bool()), + ) def test_value_lifts_qiskit_scalars(self): cr = ClassicalRegister(3, "c") diff --git a/test/python/circuit/library/test_evolution_gate.py b/test/python/circuit/library/test_evolution_gate.py index a7c9a73abb5f..7d1740b67f30 100644 --- a/test/python/circuit/library/test_evolution_gate.py +++ b/test/python/circuit/library/test_evolution_gate.py @@ -18,9 +18,8 @@ from ddt import ddt, data, unpack from qiskit.circuit import QuantumCircuit, Parameter -from qiskit.circuit.library import PauliEvolutionGate +from qiskit.circuit.library import PauliEvolutionGate, HamiltonianGate from qiskit.synthesis import LieTrotter, SuzukiTrotter, MatrixExponential, QDrift -from qiskit.synthesis.evolution.product_formula import cnot_chain, diagonalizing_clifford from qiskit.converters import circuit_to_dag from qiskit.quantum_info import Operator, SparsePauliOp, Pauli, Statevector from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -40,6 +39,19 @@ def setUp(self): # fix random seed for reproducibility (used in QDrift) self.seed = 2 + def assertSuzukiTrotterIsCorrect(self, gate): + """Assert the Suzuki Trotter evolution is correct.""" + op = gate.operator + time = gate.time + synthesis = gate.synthesis + + exact_suzuki = SuzukiTrotter( + reps=synthesis.reps, order=synthesis.order, atomic_evolution=exact_atomic_evolution + ) + exact_gate = PauliEvolutionGate(op, time, synthesis=exact_suzuki) + + self.assertTrue(Operator(gate).equiv(exact_gate)) + def test_matrix_decomposition(self): """Test the default decomposition.""" op = (X ^ X ^ X) + (Y ^ Y ^ Y) + (Z ^ Z ^ Z) @@ -59,7 +71,16 @@ def test_lie_trotter(self): reps = 4 evo_gate = PauliEvolutionGate(op, time, synthesis=LieTrotter(reps=reps)) decomposed = evo_gate.definition.decompose() + self.assertEqual(decomposed.count_ops()["cx"], reps * 3 * 4) + self.assertSuzukiTrotterIsCorrect(evo_gate) + + def test_basis_change(self): + """Test the basis change is correctly implemented.""" + op = I ^ Y # use a string for which we do not have a basis gate + time = 0.321 + evo_gate = PauliEvolutionGate(op, time) + self.assertSuzukiTrotterIsCorrect(evo_gate) def test_rzx_order(self): """Test ZX and XZ is mapped onto the correct qubits.""" @@ -105,6 +126,7 @@ def test_suzuki_trotter(self): ) decomposed = evo_gate.definition.decompose() self.assertEqual(decomposed.count_ops()["cx"], expected_cx) + self.assertSuzukiTrotterIsCorrect(evo_gate) def test_suzuki_trotter_manual(self): """Test the evolution circuit of Suzuki Trotter against a manually constructed circuit.""" @@ -132,6 +154,7 @@ def test_suzuki_trotter_manual(self): expected.rx(p_4 * time, 0) self.assertEqual(evo_gate.definition, expected) + self.assertSuzukiTrotterIsCorrect(evo_gate) @data( (X + Y, 0.5, 1, [(Pauli("X"), 0.5), (Pauli("X"), 0.5)]), @@ -185,6 +208,7 @@ def test_passing_grouped_paulis(self, wrap): decomposed = evo_gate.definition.decompose() else: decomposed = evo_gate.definition + self.assertEqual(decomposed.count_ops()["rz"], 4) self.assertEqual(decomposed.count_ops()["rzz"], 1) self.assertEqual(decomposed.count_ops()["rxx"], 1) @@ -251,6 +275,7 @@ def test_cnot_chain_options(self, option): expected.cx(1, 0) self.assertEqual(expected, evo.definition) + self.assertSuzukiTrotterIsCorrect(evo) @data( Pauli("XI"), @@ -275,6 +300,7 @@ def test_pauliop_coefficients_respected(self): circuit = evo.definition.decompose() rz_angle = circuit.data[0].operation.params[0] self.assertEqual(rz_angle, 10) + self.assertSuzukiTrotterIsCorrect(evo) def test_paulisumop_coefficients_respected(self): """Test that global ``PauliSumOp`` coefficients are being taken care of.""" @@ -286,6 +312,7 @@ def test_paulisumop_coefficients_respected(self): circuit.data[2].operation.params[0], # Z ] self.assertListEqual(rz_angles, [20, 30, -10]) + self.assertSuzukiTrotterIsCorrect(evo) def test_lie_trotter_two_qubit_correct_order(self): """Test that evolutions on two qubit operators are in the right order. @@ -294,10 +321,9 @@ def test_lie_trotter_two_qubit_correct_order(self): """ operator = I ^ Z ^ Z time = 0.5 - exact = scipy.linalg.expm(-1j * time * operator.to_matrix()) lie_trotter = PauliEvolutionGate(operator, time, synthesis=LieTrotter()) - self.assertTrue(Operator(lie_trotter).equiv(exact)) + self.assertSuzukiTrotterIsCorrect(lie_trotter) def test_complex_op_raises(self): """Test an operator with complex coefficient raises an error.""" @@ -336,6 +362,12 @@ def test_atomic_evolution(self): """Test a custom atomic_evolution.""" def atomic_evolution(pauli, time): + if isinstance(pauli, SparsePauliOp): + if len(pauli.paulis) != 1: + raise ValueError("Unsupported input.") + time *= np.real(pauli.coeffs[0]) + pauli = pauli.paulis[0] + cliff = diagonalizing_clifford(pauli) chain = cnot_chain(pauli) @@ -365,5 +397,65 @@ def atomic_evolution(pauli, time): self.assertEqual(decomposed.count_ops()["cx"], reps * 3 * 4) +def exact_atomic_evolution(circuit, pauli, time): + """An exact atomic evolution for Suzuki-Trotter. + + Note that the Pauli has a x2 coefficient already, hence we evolve for time/2. + """ + circuit.append(HamiltonianGate(pauli.to_matrix(), time / 2), circuit.qubits) + + +def diagonalizing_clifford(pauli: Pauli) -> QuantumCircuit: + """Get the clifford circuit to diagonalize the Pauli operator.""" + cliff = QuantumCircuit(pauli.num_qubits) + for i, pauli_i in enumerate(reversed(pauli.to_label())): + if pauli_i == "Y": + cliff.sx(i) + elif pauli_i == "X": + cliff.h(i) + + return cliff + + +def cnot_chain(pauli: Pauli) -> QuantumCircuit: + """CX chain. + + For example, for the Pauli with the label 'XYZIX'. + + .. parsed-literal:: + + ┌───┐ + q_0: ──────────┤ X ├ + └─┬─┘ + q_1: ────────────┼── + ┌───┐ │ + q_2: ─────┤ X ├──■── + ┌───┐└─┬─┘ + q_3: ┤ X ├──■─────── + └─┬─┘ + q_4: ──■──────────── + + """ + + chain = QuantumCircuit(pauli.num_qubits) + control, target = None, None + + # iterate over the Pauli's and add CNOTs + for i, pauli_i in enumerate(pauli.to_label()): + i = pauli.num_qubits - i - 1 + if pauli_i != "I": + if control is None: + control = i + else: + target = i + + if control is not None and target is not None: + chain.cx(control, target) + control = i + target = None + + return chain + + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/library/test_evolved_op_ansatz.py b/test/python/circuit/library/test_evolved_op_ansatz.py index 8ea66b7012fe..2dbcec86a120 100644 --- a/test/python/circuit/library/test_evolved_op_ansatz.py +++ b/test/python/circuit/library/test_evolved_op_ansatz.py @@ -119,8 +119,7 @@ def evolve(pauli_string, time): if pauli == "x": forward.h(i) elif pauli == "y": - forward.sdg(i) - forward.h(i) + forward.sx(i) for i in range(1, num_qubits): forward.cx(num_qubits - i, num_qubits - i - 1) diff --git a/test/python/circuit/library/test_fourier_checking.py b/test/python/circuit/library/test_fourier_checking.py index 23595d70d95c..31490584b100 100644 --- a/test/python/circuit/library/test_fourier_checking.py +++ b/test/python/circuit/library/test_fourier_checking.py @@ -16,7 +16,7 @@ from ddt import ddt, data, unpack import numpy as np -from qiskit.circuit.library import FourierChecking +from qiskit.circuit.library import FourierChecking, fourier_checking from qiskit.circuit.exceptions import CircuitError from qiskit.quantum_info import Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -61,6 +61,20 @@ def test_invalid_input_raises(self, f_truth_table, g_truth_table): with self.assertRaises(CircuitError): FourierChecking(f_truth_table, g_truth_table) + @data(([1, -1, -1, -1], [1, 1, -1, -1]), ([1, 1, 1, 1], [1, 1, 1, 1])) + @unpack + def test_fourier_checking_function(self, f_truth_table, g_truth_table): + """Test if the Fourier Checking circuit produces the correct matrix.""" + fc_circuit = fourier_checking(f_truth_table, g_truth_table) + self.assertFourierCheckingIsCorrect(f_truth_table, g_truth_table, fc_circuit) + + @data(([1, -1, -1, -1], [1, 1, -1]), ([1], [-1]), ([1, -1, -1, -1, 1], [1, 1, -1, -1, 1])) + @unpack + def test_invalid_input_raises_function(self, f_truth_table, g_truth_table): + """Test that invalid input truth tables raise an error.""" + with self.assertRaises(CircuitError): + fourier_checking(f_truth_table, g_truth_table) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/library/test_graph_state.py b/test/python/circuit/library/test_graph_state.py index dbefa2cebd3d..3e1e3a02967b 100644 --- a/test/python/circuit/library/test_graph_state.py +++ b/test/python/circuit/library/test_graph_state.py @@ -13,10 +13,12 @@ """Test library of graph state circuits.""" import unittest +import numpy as np +from qiskit.circuit import Gate from qiskit.circuit.exceptions import CircuitError -from qiskit.circuit.library import GraphState -from qiskit.quantum_info import Clifford +from qiskit.circuit.library import GraphState, GraphStateGate +from qiskit.quantum_info import Clifford, Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -24,11 +26,15 @@ class TestGraphStateLibrary(QiskitTestCase): """Test the graph state circuit.""" def assertGraphStateIsCorrect(self, adjacency_matrix, graph_state): - """Check the stabilizers of the graph state against the expected stabilizers. + """Check the stabilizers of the graph state circuit/gate against the expected stabilizers. Based on https://arxiv.org/pdf/quant-ph/0307130.pdf, Eq. (6). """ + if isinstance(graph_state, Gate): + cliff = Clifford(graph_state.definition) + else: + cliff = Clifford(graph_state) - stabilizers = [stabilizer[1:] for stabilizer in Clifford(graph_state).to_labels(mode="S")] + stabilizers = [stabilizer[1:] for stabilizer in cliff.to_labels(mode="S")] expected_stabilizers = [] # keep track of all expected stabilizers num_vertices = len(adjacency_matrix) @@ -47,7 +53,7 @@ def assertGraphStateIsCorrect(self, adjacency_matrix, graph_state): self.assertListEqual(expected_stabilizers, stabilizers) - def test_graph_state(self): + def test_graph_state_circuit(self): """Verify the GraphState by checking if the circuit has the expected stabilizers.""" adjacency_matrix = [ [0, 1, 0, 0, 1], @@ -59,12 +65,82 @@ def test_graph_state(self): graph_state = GraphState(adjacency_matrix) self.assertGraphStateIsCorrect(adjacency_matrix, graph_state) - def test_non_symmetric_raises(self): + def test_non_symmetric_circuit_raises(self): """Test that adjacency matrix is required to be symmetric.""" adjacency_matrix = [[1, 1, 0], [1, 0, 1], [1, 1, 1]] with self.assertRaises(CircuitError): GraphState(adjacency_matrix) + def test_graph_state_gate(self): + """Verify correctness of GraphStatGate by checking that the gate's definition circuit + has the expected stabilizers. + """ + adjacency_matrix = [ + [0, 1, 0, 0, 1], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [1, 0, 0, 1, 0], + ] + graph_state = GraphStateGate(adjacency_matrix) + self.assertGraphStateIsCorrect(adjacency_matrix, graph_state) + + def test_non_symmetric_gate_raises(self): + """Test that adjacency matrix is required to be symmetric.""" + adjacency_matrix = [[1, 1, 0], [1, 0, 1], [1, 1, 1]] + with self.assertRaises(CircuitError): + GraphStateGate(adjacency_matrix) + + def test_circuit_and_gate_equivalence(self): + """Test that GraphState-circuit and GraphStateGate yield equal operators.""" + adjacency_matrix = [ + [0, 1, 0, 0, 1], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [1, 0, 0, 1, 0], + ] + graph_state_gate = GraphStateGate(adjacency_matrix) + graph_state_circuit = GraphState(adjacency_matrix) + self.assertEqual(Operator(graph_state_gate), Operator(graph_state_circuit)) + + def test_adjacency_matrix(self): + """Test GraphStateGate's adjacency_matrix method.""" + adjacency_matrix = [ + [0, 1, 0, 0, 1], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [1, 0, 0, 1, 0], + ] + graph_state_gate = GraphStateGate(adjacency_matrix) + self.assertTrue(np.all(graph_state_gate.adjacency_matrix == adjacency_matrix)) + + def test_equality(self): + """Test GraphStateGate's equality method.""" + mat1 = [ + [0, 1, 0, 0, 1], + [1, 0, 1, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 1], + [1, 0, 0, 1, 0], + ] + mat2 = [ + [0, 1, 0, 0, 1], + [1, 0, 0, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 1, 0, 1], + [1, 0, 0, 1, 0], + ] + with self.subTest("equal"): + gate1 = GraphStateGate(mat1) + gate2 = GraphStateGate(mat1) + self.assertEqual(gate1, gate2) + with self.subTest("not equal"): + gate1 = GraphStateGate(mat1) + gate2 = GraphStateGate(mat2) + self.assertNotEqual(gate1, gate2) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/library/test_hidden_linear_function.py b/test/python/circuit/library/test_hidden_linear_function.py index bc7d9c7545b6..fce0ffb04cd9 100644 --- a/test/python/circuit/library/test_hidden_linear_function.py +++ b/test/python/circuit/library/test_hidden_linear_function.py @@ -17,7 +17,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit.exceptions import CircuitError -from qiskit.circuit.library import HiddenLinearFunction +from qiskit.circuit.library import HiddenLinearFunction, hidden_linear_function from qiskit.quantum_info import Operator from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -60,6 +60,17 @@ def test_non_symmetric_raises(self): with self.assertRaises(CircuitError): HiddenLinearFunction([[1, 1, 0], [1, 0, 1], [1, 1, 1]]) + def test_hlf_function(self): + """Test if the HLF matrix produces the right matrix.""" + hidden_function = [[1, 1, 0], [1, 0, 1], [0, 1, 1]] + hlf = hidden_linear_function(hidden_function) + self.assertHLFIsCorrect(hidden_function, hlf) + + def test_non_symmetric_raises_function(self): + """Test that adjacency matrix is required to be symmetric.""" + with self.assertRaises(CircuitError): + hidden_linear_function([[1, 1, 0], [1, 0, 1], [1, 1, 1]]) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/library/test_overlap.py b/test/python/circuit/library/test_overlap.py index 1a95e3ba9155..c103e60fba91 100644 --- a/test/python/circuit/library/test_overlap.py +++ b/test/python/circuit/library/test_overlap.py @@ -12,50 +12,69 @@ """Test unitary overlap function""" import unittest +from ddt import ddt, data import numpy as np from qiskit.circuit import QuantumCircuit, Qubit, Clbit -from qiskit.circuit.library import EfficientSU2, UnitaryOverlap +from qiskit.circuit.library import EfficientSU2, UnitaryOverlap, unitary_overlap from qiskit.quantum_info import Statevector from qiskit.circuit.exceptions import CircuitError from test import QiskitTestCase # pylint: disable=wrong-import-order +@ddt class TestUnitaryOverlap(QiskitTestCase): """Test the unitary overlap circuit class.""" - def test_identity(self): + @data(True, False) + def test_identity(self, use_function): """Test identity is returned""" unitary = EfficientSU2(2) unitary.assign_parameters(np.random.random(size=unitary.num_parameters), inplace=True) - - overlap = UnitaryOverlap(unitary, unitary) + if use_function: + overlap = unitary_overlap(unitary, unitary) + else: + overlap = UnitaryOverlap(unitary, unitary) self.assertLess(abs(Statevector.from_instruction(overlap)[0] - 1), 1e-12) - def test_parameterized_identity(self): + @data(True, False) + def test_parameterized_identity(self, use_function): """Test identity is returned""" unitary = EfficientSU2(2) - overlap = UnitaryOverlap(unitary, unitary) + if use_function: + overlap = unitary_overlap(unitary, unitary) + else: + overlap = UnitaryOverlap(unitary, unitary) + rands = np.random.random(size=unitary.num_parameters) double_rands = np.hstack((rands, rands)) overlap.assign_parameters(double_rands, inplace=True) self.assertLess(abs(Statevector.from_instruction(overlap)[0] - 1), 1e-12) - def test_two_parameterized_inputs(self): + @data(True, False) + def test_two_parameterized_inputs(self, use_function): """Test two parameterized inputs""" unitary1 = EfficientSU2(2) unitary2 = EfficientSU2(2) - overlap = UnitaryOverlap(unitary1, unitary2) + if use_function: + overlap = unitary_overlap(unitary1, unitary2) + else: + overlap = UnitaryOverlap(unitary1, unitary2) self.assertEqual(overlap.num_parameters, unitary1.num_parameters + unitary2.num_parameters) - def test_parameter_prefixes(self): + @data(True, False) + def test_parameter_prefixes(self, use_function): """Test two parameterized inputs""" unitary1 = EfficientSU2(2) unitary2 = EfficientSU2(2) - overlap = UnitaryOverlap(unitary1, unitary2, prefix1="a", prefix2="b") + if use_function: + overlap = unitary_overlap(unitary1, unitary2, prefix1="a", prefix2="b") + else: + overlap = UnitaryOverlap(unitary1, unitary2, prefix1="a", prefix2="b") + self.assertEqual(overlap.num_parameters, unitary1.num_parameters + unitary2.num_parameters) expected_names = [f"a[{i}]" for i in range(unitary1.num_parameters)] @@ -63,53 +82,76 @@ def test_parameter_prefixes(self): self.assertListEqual([p.name for p in overlap.parameters], expected_names) - def test_partial_parameterized_inputs(self): + @data(True, False) + def test_partial_parameterized_inputs(self, use_function): """Test one parameterized inputs (1)""" unitary1 = EfficientSU2(2) unitary1.assign_parameters(np.random.random(size=unitary1.num_parameters), inplace=True) unitary2 = EfficientSU2(2, reps=5) - overlap = UnitaryOverlap(unitary1, unitary2) + if use_function: + overlap = unitary_overlap(unitary1, unitary2) + else: + overlap = UnitaryOverlap(unitary1, unitary2) + self.assertEqual(overlap.num_parameters, unitary2.num_parameters) - def test_partial_parameterized_inputs2(self): + @data(True, False) + def test_partial_parameterized_inputs2(self, use_function): """Test one parameterized inputs (2)""" unitary1 = EfficientSU2(2) unitary2 = EfficientSU2(2, reps=5) unitary2.assign_parameters(np.random.random(size=unitary2.num_parameters), inplace=True) - overlap = UnitaryOverlap(unitary1, unitary2) + if use_function: + overlap = unitary_overlap(unitary1, unitary2) + else: + overlap = UnitaryOverlap(unitary1, unitary2) + self.assertEqual(overlap.num_parameters, unitary1.num_parameters) - def test_barrier(self): + @data(True, False) + def test_barrier(self, use_function): """Test that barriers on input circuits are well handled""" unitary1 = EfficientSU2(1, reps=0) unitary1.barrier() unitary2 = EfficientSU2(1, reps=1) unitary2.barrier() - overlap = UnitaryOverlap(unitary1, unitary2) + if use_function: + overlap = unitary_overlap(unitary1, unitary2) + else: + overlap = UnitaryOverlap(unitary1, unitary2) self.assertEqual(overlap.num_parameters, unitary1.num_parameters + unitary2.num_parameters) - def test_measurements(self): + @data(True, False) + def test_measurements(self, use_function): """Test that exception is thrown for measurements""" unitary1 = EfficientSU2(2) unitary1.measure_all() unitary2 = EfficientSU2(2) with self.assertRaises(CircuitError): - _ = UnitaryOverlap(unitary1, unitary2) + if use_function: + _ = unitary_overlap(unitary1, unitary2) + else: + _ = UnitaryOverlap(unitary1, unitary2) - def test_rest(self): + @data(True, False) + def test_rest(self, use_function): """Test that exception is thrown for rest""" unitary1 = EfficientSU2(1, reps=0) unitary1.reset(0) unitary2 = EfficientSU2(1, reps=1) with self.assertRaises(CircuitError): - _ = UnitaryOverlap(unitary1, unitary2) + if use_function: + _ = unitary_overlap(unitary1, unitary2) + else: + _ = UnitaryOverlap(unitary1, unitary2) - def test_controlflow(self): + @data(True, False) + def test_controlflow(self, use_function): """Test that exception is thrown for controlflow""" bit = Clbit() unitary1 = QuantumCircuit([Qubit(), bit]) @@ -121,21 +163,34 @@ def test_controlflow(self): unitary2.rx(0.2, 0) with self.assertRaises(CircuitError): - _ = UnitaryOverlap(unitary1, unitary2) + if use_function: + _ = unitary_overlap(unitary1, unitary2) + else: + _ = UnitaryOverlap(unitary1, unitary2) - def test_mismatching_qubits(self): + @data(True, False) + def test_mismatching_qubits(self, use_function): """Test that exception is thrown for mismatching circuit""" unitary1 = EfficientSU2(2) unitary2 = EfficientSU2(1) with self.assertRaises(CircuitError): - _ = UnitaryOverlap(unitary1, unitary2) + if use_function: + _ = unitary_overlap(unitary1, unitary2) + else: + _ = UnitaryOverlap(unitary1, unitary2) - def test_insert_barrier(self): + @data(True, False) + def test_insert_barrier(self, use_function): """Test inserting barrier between circuits""" unitary1 = EfficientSU2(1, reps=1) unitary2 = EfficientSU2(1, reps=1) - overlap = UnitaryOverlap(unitary1, unitary2, insert_barrier=True) + + if use_function: + overlap = unitary_overlap(unitary1, unitary2, insert_barrier=True) + else: + overlap = UnitaryOverlap(unitary1, unitary2, insert_barrier=True) + self.assertEqual(overlap.count_ops()["barrier"], 1) self.assertEqual( str(overlap.draw(fold=-1, output="text")).strip(), diff --git a/test/python/circuit/library/test_pauli_feature_map.py b/test/python/circuit/library/test_pauli_feature_map.py index 5d61944d4032..d12e89fc556f 100644 --- a/test/python/circuit/library/test_pauli_feature_map.py +++ b/test/python/circuit/library/test_pauli_feature_map.py @@ -70,22 +70,22 @@ def test_pauli_evolution(self): self.assertTrue(Operator(pauli).equiv(evo)) with self.subTest(pauli_string="XYZ"): - # q_0: ─────────────■────────────────────────■────────────── - # ┌─────────┐┌─┴─┐ ┌─┴─┐┌──────────┐ - # q_1: ┤ Rx(π/2) ├┤ X ├──■──────────────■──┤ X ├┤ Rx(-π/2) ├ - # └──┬───┬──┘└───┘┌─┴─┐┌────────┐┌─┴─┐├───┤└──────────┘ - # q_2: ───┤ H ├────────┤ X ├┤ P(2.8) ├┤ X ├┤ H ├──────────── - # └───┘ └───┘└────────┘└───┘└───┘ + # q_0: ────────■────────────────────────■────────── + # ┌────┐┌─┴─┐ ┌─┴─┐┌──────┐ + # q_1: ┤ √X ├┤ X ├──■──────────────■──┤ X ├┤ √Xdg ├ + # └┬───┤└───┘┌─┴─┐┌────────┐┌─┴─┐├───┤└──────┘ + # q_2: ─┤ H ├─────┤ X ├┤ P(2.8) ├┤ X ├┤ H ├──────── + # └───┘ └───┘└────────┘└───┘└───┘ evo = QuantumCircuit(3) # X on the most-significant, bottom qubit, Z on the top evo.h(2) - evo.rx(np.pi / 2, 1) + evo.sx(1) evo.cx(0, 1) evo.cx(1, 2) evo.p(2 * time, 2) evo.cx(1, 2) evo.cx(0, 1) - evo.rx(-np.pi / 2, 1) + evo.sxdg(1) evo.h(2) pauli = encoding.pauli_evolution("XYZ", time) @@ -347,23 +347,23 @@ def test_pauli_xyz(self): params = encoding.parameters - # q_0: ─────────────■────────────────────────■────────────── - # ┌─────────┐┌─┴─┐ ┌─┴─┐┌──────────┐ - # q_1: ┤ Rx(π/2) ├┤ X ├──■──────────────■──┤ X ├┤ Rx(-π/2) ├ - # └──┬───┬──┘└───┘┌─┴─┐┌────────┐┌─┴─┐├───┤└──────────┘ - # q_2: ───┤ H ├────────┤ X ├┤ P(2.8) ├┤ X ├┤ H ├──────────── - # └───┘ └───┘└────────┘└───┘└───┘ + # q_0: ────────■────────────────────────■────────── + # ┌────┐┌─┴─┐ ┌─┴─┐┌──────┐ + # q_1: ┤ √X ├┤ X ├──■──────────────■──┤ X ├┤ √Xdg ├ + # └┬───┤└───┘┌─┴─┐┌────────┐┌─┴─┐├───┤└──────┘ + # q_2: ─┤ H ├─────┤ X ├┤ P(2.8) ├┤ X ├┤ H ├──────── + # └───┘ └───┘└────────┘└───┘└───┘ # X on the most-significant, bottom qubit, Z on the top ref = QuantumCircuit(3) ref.h(range(3)) ref.h(2) - ref.rx(np.pi / 2, 1) + ref.sx(1) ref.cx(0, 1) ref.cx(1, 2) ref.p(2 * np.prod([np.pi - p for p in params]), 2) ref.cx(1, 2) ref.cx(0, 1) - ref.rx(-np.pi / 2, 1) + ref.sxdg(1) ref.h(2) self.assertEqual(ref, encoding) @@ -483,13 +483,13 @@ def test_dict_entanglement(self): ref.cx(1, 2) ref.h([1, 2]) - ref.rx(np.pi / 2, range(3)) + ref.sx(range(3)) ref.cx(0, 1) ref.cx(1, 2) ref.p(2 * np.prod([np.pi - xi for xi in x]), 2) ref.cx(1, 2) ref.cx(0, 1) - ref.rx(-np.pi / 2, range(3)) + ref.sxdg(range(3)) self.assertEqual(ref, circuit) diff --git a/test/python/circuit/library/test_permutation.py b/test/python/circuit/library/test_permutation.py index a2ec59431fe3..380a5f6a0358 100644 --- a/test/python/circuit/library/test_permutation.py +++ b/test/python/circuit/library/test_permutation.py @@ -149,8 +149,10 @@ def test_reverse_ops(self): def test_conditional(self): """Test adding conditional permutations.""" qc = QuantumCircuit(5, 1) - qc.append(PermutationGate([1, 2, 0]), [2, 3, 4]).c_if(0, 1) - self.assertIsNotNone(qc.data[0].operation.condition) + with self.assertWarns(DeprecationWarning): + qc.append(PermutationGate([1, 2, 0]), [2, 3, 4]).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + self.assertIsNotNone(qc.data[0].operation.condition) def test_qasm(self): """Test qasm for circuits with permutations.""" diff --git a/test/python/circuit/library/test_phase_estimation.py b/test/python/circuit/library/test_phase_estimation.py index 28b388a15b2e..5b1da4a0bc7c 100644 --- a/test/python/circuit/library/test_phase_estimation.py +++ b/test/python/circuit/library/test_phase_estimation.py @@ -16,8 +16,9 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library import PhaseEstimation, QFT +from qiskit.circuit.library import PhaseEstimation, QFT, phase_estimation from qiskit.quantum_info import Statevector +from qiskit.compiler import transpile from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -149,6 +150,93 @@ def test_phase_estimation_iqft_setting(self): pec = PhaseEstimation(3, unitary, iqft=iqft) self.assertEqual(pec.decompose().data[-1].operation.definition, iqft.decompose()) + def test_phase_estimation_function(self): + """Test the phase estimation function.""" + with self.subTest("U=S, psi=|1>"): + unitary = QuantumCircuit(1) + unitary.s(0) + + eigenstate = QuantumCircuit(1) + eigenstate.x(0) + + # eigenvalue is 1j = exp(2j pi 0.25) thus phi = 0.25 = 0.010 = '010' + # using three digits as 3 evaluation qubits are used + phase_as_binary = "0100" + + pec = phase_estimation(4, unitary) + + self.assertPhaseEstimationIsCorrect(pec, eigenstate, phase_as_binary) + + with self.subTest("U=SZ, psi=|11>"): + unitary = QuantumCircuit(2) + unitary.z(0) + unitary.s(1) + + eigenstate = QuantumCircuit(2) + eigenstate.x([0, 1]) + + # eigenvalue is -1j = exp(2j pi 0.75) thus phi = 0.75 = 0.110 = '110' + # using three digits as 3 evaluation qubits are used + phase_as_binary = "110" + + pec = phase_estimation(3, unitary) + + self.assertPhaseEstimationIsCorrect(pec, eigenstate, phase_as_binary) + + with self.subTest("a 3-q unitary"): + # ┌───┐ + # q_0: ┤ X ├──■────■─────── + # ├───┤ │ │ + # q_1: ┤ X ├──■────■─────── + # ├───┤┌───┐┌─┴─┐┌───┐ + # q_2: ┤ X ├┤ H ├┤ X ├┤ H ├ + # └───┘└───┘└───┘└───┘ + unitary = QuantumCircuit(3) + unitary.x([0, 1, 2]) + unitary.cz(0, 1) + unitary.h(2) + unitary.ccx(0, 1, 2) + unitary.h(2) + + # ┌───┐ + # q_0: ┤ H ├──■────■── + # └───┘┌─┴─┐ │ + # q_1: ─────┤ X ├──┼── + # └───┘┌─┴─┐ + # q_2: ──────────┤ X ├ + # └───┘ + eigenstate = QuantumCircuit(3) + eigenstate.h(0) + eigenstate.cx(0, 1) + eigenstate.cx(0, 2) + + # the unitary acts as identity on the eigenstate, thus the phase is 0 + phase_as_binary = "00" + + pec = phase_estimation(2, unitary) + + self.assertPhaseEstimationIsCorrect(pec, eigenstate, phase_as_binary) + + def test_phase_estimation_function_swaps_get_removed(self): + """Test that transpiling the circuit correctly removes swaps and permutations.""" + unitary = QuantumCircuit(1) + unitary.s(0) + + eigenstate = QuantumCircuit(1) + eigenstate.x(0) + + pec = phase_estimation(4, unitary) + + # transpilation (or more precisely HighLevelSynthesis + ElidePermutations) should + # remove all swap gates (possibly added when synthesizing the QFTGate in the circuit) + # and the final permutation gate + transpiled = transpile(pec) + transpiled_ops = transpiled.count_ops() + + self.assertNotIn("permutation", transpiled_ops) + self.assertNotIn("swap", transpiled_ops) + self.assertNotIn("cx", transpiled_ops) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/library/test_qft.py b/test/python/circuit/library/test_qft.py index 85837f0ac80c..6250b656a772 100644 --- a/test/python/circuit/library/test_qft.py +++ b/test/python/circuit/library/test_qft.py @@ -272,8 +272,10 @@ def test_reverse_ops(self): def test_conditional(self): """Test adding conditional to a QFTGate.""" qc = QuantumCircuit(5, 1) - qc.append(QFTGate(4), [1, 2, 0, 4]).c_if(0, 1) - self.assertIsNotNone(qc.data[0].operation.condition) + with self.assertWarns(DeprecationWarning): + qc.append(QFTGate(4), [1, 2, 0, 4]).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + self.assertIsNotNone(qc.data[0].operation.condition) def test_qasm(self): """Test qasm for circuits with QFTGates.""" diff --git a/test/python/circuit/test_circuit_data.py b/test/python/circuit/test_circuit_data.py index ff241baeb61c..9568c1c42084 100644 --- a/test/python/circuit/test_circuit_data.py +++ b/test/python/circuit/test_circuit_data.py @@ -11,6 +11,7 @@ # that they have been altered from the originals. """Test operations on circuit.data.""" +import pickle import ddt from qiskit._accelerate.circuit import CircuitData @@ -110,6 +111,7 @@ def test_copy(self): CircuitInstruction(Measure(), [qr[0]], [cr[1]]), CircuitInstruction(Measure(), [qr[1]], [cr[0]]), ], + global_phase=1, ) qubits = data.qubits clbits = data.clbits @@ -126,6 +128,25 @@ def test_copy(self): self.assertIsNot(data_copy.clbits, clbits) self.assertEqual(data_copy.clbits, clbits) + with self.subTest("global_phase is equal"): + self.assertEqual(data.global_phase, data_copy.global_phase) + + def test_pickle_roundtrip(self): + """Test pickle roundtrip coverage""" + qr = QuantumRegister(1) + cr = ClassicalRegister(1) + data = CircuitData( + qubits=qr, + clbits=cr, + data=[ + CircuitInstruction(XGate(), [qr[0]], []), + CircuitInstruction(Measure(), [qr[0]], [cr[0]]), + ], + global_phase=1, + ) + + self.assertEqual(data, pickle.loads(pickle.dumps(data))) + @ddt.data( (QuantumRegister(5), ClassicalRegister(5)), ([Qubit() for _ in range(5)], [Clbit() for _ in range(5)]), diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 5e45d0671021..962dd22ac79b 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -138,11 +138,13 @@ def test_qpy_full_path(self): def test_circuit_with_conditional(self): """Test that instructions with conditions are correctly serialized.""" qc = QuantumCircuit(1, 1) - qc.x(0).c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(qc.cregs[0], 1) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circ = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertDeprecatedBitProperties(qc, new_circ) @@ -413,13 +415,15 @@ def test_multiple_circuits(self): """Test multiple circuits can be serialized together.""" circuits = [] for i in range(10): - circuits.append( - random_circuit(10, 10, measure=True, conditional=True, reset=True, seed=42 + i) - ) + with self.assertWarns(DeprecationWarning): + circuits.append( + random_circuit(10, 10, measure=True, conditional=True, reset=True, seed=42 + i) + ) qpy_file = io.BytesIO() dump(circuits, qpy_file) qpy_file.seek(0) - new_circs = load(qpy_file) + with self.assertWarns(DeprecationWarning): + new_circs = load(qpy_file) self.assertEqual(circuits, new_circs) for old, new in zip(circuits, new_circs): self.assertDeprecatedBitProperties(old, new) @@ -679,12 +683,14 @@ def test_circuit_with_conditional_with_label(self): """Test that instructions with conditions are correctly serialized.""" qc = QuantumCircuit(1, 1) gate = XGate(label="My conditional x gate") - gate.c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + gate.c_if(qc.cregs[0], 1) qc.append(gate, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circ = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual( [x.operation.label for x in qc.data], [x.operation.label for x in new_circ.data] @@ -760,12 +766,14 @@ def test_single_bit_teleportation(self): qc = QuantumCircuit(qr, cr, name="Reset Test") qc.x(0) qc.measure(0, cr[0]) - qc.x(0).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(cr[0], 1) qc.measure(0, cr[1]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circ = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual( [x.operation.label for x in qc.data], [x.operation.label for x in new_circ.data] @@ -1143,11 +1151,13 @@ def test_qpy_with_for_loop(self): qc.h(0) qc.cx(0, 1) qc.measure(0, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circuit = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circuit = load(qpy_file)[0] self.assertEqual(qc, new_circuit) self.assertDeprecatedBitProperties(qc, new_circuit) @@ -1159,11 +1169,13 @@ def test_qpy_with_for_loop_iterator(self): qc.h(0) qc.cx(0, 1) qc.measure(0, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) - new_circuit = load(qpy_file)[0] + with self.assertWarns(DeprecationWarning): + new_circuit = load(qpy_file)[0] self.assertEqual(qc, new_circuit) self.assertDeprecatedBitProperties(qc, new_circuit) diff --git a/test/python/circuit/test_circuit_operations.py b/test/python/circuit/test_circuit_operations.py index 2d1e136d4580..bd35c8d920a5 100644 --- a/test/python/circuit/test_circuit_operations.py +++ b/test/python/circuit/test_circuit_operations.py @@ -1038,18 +1038,21 @@ def test_repeat(self): qc.h(0) qc.cx(0, 1) qc.barrier() - qc.h(0).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(cr, 1) with self.subTest("repeat 0 times"): rep = qc.repeat(0) self.assertEqual(rep, QuantumCircuit(qr, cr)) with self.subTest("repeat 3 times"): - inst = qc.to_instruction() + with self.assertWarns(DeprecationWarning): + inst = qc.to_instruction() ref = QuantumCircuit(qr, cr) for _ in range(3): ref.append(inst, ref.qubits, ref.clbits) - rep = qc.repeat(3) + with self.assertWarns(DeprecationWarning): + rep = qc.repeat(3) self.assertEqual(rep, ref) @data(0, 1, 4) @@ -1295,13 +1298,15 @@ def test_reverse_bits_with_registerless_bits(self): qc = QuantumCircuit([q0, q1], [c0, c1]) qc.h(0) qc.cx(0, 1) - qc.x(0).c_if(1, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(1, True) qc.measure(0, 0) expected = QuantumCircuit([c1, c0], [q1, q0]) expected.h(1) expected.cx(1, 0) - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) expected.measure(1, 1) self.assertEqual(qc.reverse_bits(), expected) @@ -1392,29 +1397,41 @@ def test_compare_circuits_with_single_bit_conditions(self): qreg = QuantumRegister(1, name="q") creg = ClassicalRegister(1, name="c") qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.cregs[0], 1) - qc2.x(0).c_if(qc2.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], True) self.assertEqual(qc1, qc2) # Order of operations transposed. qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.clbits[-1], True) - qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) self.assertNotEqual(qc1, qc2) # Single-bit condition values not the same. qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.cregs[0], 1) - qc2.x(0).c_if(qc2.clbits[-1], False) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], False) self.assertNotEqual(qc1, qc2) def test_compare_a_circuit_with_none(self): diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index d87487639672..14b5364b2168 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -226,8 +226,10 @@ def test_circuit_depth_conditionals1(self): qc.cx(q[2], q[3]) qc.measure(q[0], c[0]) qc.measure(q[1], c[1]) - qc.h(q[2]).c_if(c, 2) - qc.h(q[3]).c_if(c, 4) + with self.assertWarns(DeprecationWarning): + qc.h(q[2]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.h(q[3]).c_if(c, 4) self.assertEqual(qc.depth(), 5) def test_circuit_depth_conditionals2(self): @@ -258,8 +260,10 @@ def test_circuit_depth_conditionals2(self): qc.cx(q[2], q[3]) qc.measure(q[0], c[0]) qc.measure(q[0], c[0]) - qc.h(q[2]).c_if(c, 2) - qc.h(q[3]).c_if(c, 4) + with self.assertWarns(DeprecationWarning): + qc.h(q[2]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.h(q[3]).c_if(c, 4) self.assertEqual(qc.depth(), 6) def test_circuit_depth_conditionals3(self): @@ -287,7 +291,8 @@ def test_circuit_depth_conditionals3(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.cx(q[0], q[3]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.cx(q[0], q[3]).c_if(c, 2) qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) @@ -320,8 +325,10 @@ def test_circuit_depth_bit_conditionals1(self): qc.h(q[3]) qc.measure(q[0], c[0]) qc.measure(q[2], c[2]) - qc.h(q[1]).c_if(c[0], True) - qc.h(q[3]).c_if(c[2], False) + with self.assertWarns(DeprecationWarning): + qc.h(q[1]).c_if(c[0], True) + with self.assertWarns(DeprecationWarning): + qc.h(q[3]).c_if(c[2], False) self.assertEqual(qc.depth(), 3) def test_circuit_depth_bit_conditionals2(self): @@ -362,12 +369,18 @@ def test_circuit_depth_bit_conditionals2(self): qc.h(q[3]) qc.measure(q[0], c[0]) qc.measure(q[2], c[2]) - qc.h(q[1]).c_if(c[1], True) - qc.h(q[3]).c_if(c[3], True) - qc.cx(0, 1).c_if(c[0], False) - qc.cx(2, 3).c_if(c[2], False) - qc.ch(0, 2).c_if(c[1], True) - qc.ch(1, 3).c_if(c[3], True) + with self.assertWarns(DeprecationWarning): + qc.h(q[1]).c_if(c[1], True) + with self.assertWarns(DeprecationWarning): + qc.h(q[3]).c_if(c[3], True) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(c[0], False) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(c[2], False) + with self.assertWarns(DeprecationWarning): + qc.ch(0, 2).c_if(c[1], True) + with self.assertWarns(DeprecationWarning): + qc.ch(1, 3).c_if(c[3], True) self.assertEqual(qc.depth(), 4) def test_circuit_depth_bit_conditionals3(self): @@ -395,9 +408,12 @@ def test_circuit_depth_bit_conditionals3(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.h(1).c_if(c[0], True) - qc.h(q[2]).c_if(c, 2) - qc.h(3).c_if(c[3], True) + with self.assertWarns(DeprecationWarning): + qc.h(1).c_if(c[0], True) + with self.assertWarns(DeprecationWarning): + qc.h(q[2]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.h(3).c_if(c[3], True) qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) qc.measure(q[3], c[3]) @@ -608,11 +624,15 @@ def test_circuit_depth_multiqubit_or_conditional(self): circ.rz(0.1, 1) circ.cz(1, 3) circ.measure(1, 0) - circ.x(0).c_if(0, 1) - self.assertEqual( - circ.depth(lambda x: x.operation.num_qubits >= 2 or x.operation.condition is not None), - 4, - ) + with self.assertWarns(DeprecationWarning): + circ.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + circ.depth( + lambda x: x.operation.num_qubits >= 2 or x.operation.condition is not None + ), + 4, + ) def test_circuit_depth_first_qubit(self): """Test finding depth of gates touching q0 only.""" @@ -765,7 +785,8 @@ def test_circuit_nonlocal_gates(self): qc.cry(0.1, q[2], q[4]) qc.z(q[3:]) qc.cswap(q[1], q[2], q[3]) - qc.iswap(q[0], q[4]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.iswap(q[0], q[4]).c_if(c, 2) result = qc.num_nonlocal_gates() expected = 3 self.assertEqual(expected, result) @@ -813,7 +834,9 @@ def test_circuit_connected_components_multi_reg(self): qc.cx(q1[1], q2[1]) qc.cx(q2[1], q1[2]) qc.cx(q1[2], q2[0]) - self.assertEqual(qc.num_connected_components(), 1) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 1) def test_circuit_connected_components_multi_reg2(self): """Test tensor factors works over multi registers #2.""" @@ -834,7 +857,9 @@ def test_circuit_connected_components_multi_reg2(self): qc.cx(q1[0], q2[1]) qc.cx(q2[0], q1[2]) qc.cx(q1[1], q2[0]) - self.assertEqual(qc.num_connected_components(), 2) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 2) def test_circuit_connected_components_disconnected(self): """Test tensor factors works with 2q subspaces.""" @@ -867,7 +892,9 @@ def test_circuit_connected_components_disconnected(self): qc.cx(q1[2], q2[2]) qc.cx(q1[3], q2[1]) qc.cx(q1[4], q2[0]) - self.assertEqual(qc.num_connected_components(), 5) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 5) def test_circuit_connected_components_with_clbits(self): """Test tensor components with classical register.""" @@ -895,7 +922,9 @@ def test_circuit_connected_components_with_clbits(self): qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) qc.measure(q[3], c[3]) - self.assertEqual(qc.num_connected_components(), 4) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 4) def test_circuit_connected_components_with_cond(self): """Test tensor components with one conditional gate.""" @@ -921,11 +950,14 @@ def test_circuit_connected_components_with_cond(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.cx(q[0], q[3]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.cx(q[0], q[3]).c_if(c, 2) qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) qc.measure(q[3], c[3]) - self.assertEqual(qc.num_connected_components(), 1) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 1) def test_circuit_connected_components_with_cond2(self): """Test tensor components with two conditional gates.""" @@ -949,9 +981,13 @@ def test_circuit_connected_components_with_cond2(self): qc.h(q[1]) qc.h(q[2]) qc.h(q[3]) - qc.h(0).c_if(c, 0) - qc.cx(1, 2).c_if(c, 4) - self.assertEqual(qc.num_connected_components(), 2) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(c, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 2).c_if(c, 4) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 2) def test_circuit_connected_components_with_cond3(self): """Test tensor components with three conditional gates and measurements.""" @@ -977,11 +1013,16 @@ def test_circuit_connected_components_with_cond3(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.h(q[0]).c_if(c, 0) - qc.cx(q[1], q[2]).c_if(c, 1) + with self.assertWarns(DeprecationWarning): + qc.h(q[0]).c_if(c, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(q[1], q[2]).c_if(c, 1) qc.measure(q[2], c[2]) - qc.x(q[3]).c_if(c, 2) - self.assertEqual(qc.num_connected_components(), 1) + with self.assertWarns(DeprecationWarning): + qc.x(q[3]).c_if(c, 2) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 1) def test_circuit_connected_components_with_bit_cond(self): """Test tensor components with one single bit conditional gate.""" @@ -1007,11 +1048,14 @@ def test_circuit_connected_components_with_bit_cond(self): qc.h(q[2]) qc.h(q[3]) qc.measure(q[0], c[0]) - qc.cx(q[0], q[3]).c_if(c[0], True) + with self.assertWarns(DeprecationWarning): + qc.cx(q[0], q[3]).c_if(c[0], True) qc.measure(q[1], c[1]) qc.measure(q[2], c[2]) qc.measure(q[3], c[3]) - self.assertEqual(qc.num_connected_components(), 3) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 3) def test_circuit_connected_components_with_bit_cond2(self): """Test tensor components with two bit conditional gates.""" @@ -1035,10 +1079,15 @@ def test_circuit_connected_components_with_bit_cond2(self): qc.h(q[1]) qc.h(q[2]) qc.h(q[3]) - qc.h(0).c_if(c[1], True) - qc.cx(1, 0).c_if(c[4], False) - qc.cz(2, 3).c_if(c[0], True) - self.assertEqual(qc.num_connected_components(), 5) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(c[1], True) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 0).c_if(c[4], False) + with self.assertWarns(DeprecationWarning): + qc.cz(2, 3).c_if(c[0], True) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 5) def test_circuit_connected_components_with_bit_cond3(self): """Test tensor components with register and bit conditional gates.""" @@ -1063,10 +1112,15 @@ def test_circuit_connected_components_with_bit_cond3(self): qc.h(q[1]) qc.h(q[2]) qc.h(q[3]) - qc.h(q[0]).c_if(c[0], True) - qc.cx(q[1], q[2]).c_if(c, 1) - qc.x(q[3]).c_if(c[2], True) - self.assertEqual(qc.num_connected_components(), 1) + with self.assertWarns(DeprecationWarning): + qc.h(q[0]).c_if(c[0], True) + with self.assertWarns(DeprecationWarning): + qc.cx(q[1], q[2]).c_if(c, 1) + with self.assertWarns(DeprecationWarning): + qc.x(q[3]).c_if(c[2], True) + # Internally calls op.condition_bits + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.num_connected_components(), 1) def test_circuit_unitary_factors1(self): """Test unitary factors empty circuit.""" @@ -1109,7 +1163,8 @@ def test_circuit_unitary_factors3(self): qc.h(q[3]) qc.cx(q[1], q[2]) qc.cx(q[1], q[2]) - qc.cx(q[0], q[3]).c_if(c, 2) + with self.assertWarns(DeprecationWarning): + qc.cx(q[0], q[3]).c_if(c, 2) qc.cx(q[0], q[3]) qc.cx(q[0], q[3]) qc.cx(q[0], q[3]) diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index 984851e00388..bbbb494877f6 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -44,9 +44,12 @@ def test_circuit_qasm(self): qc.barrier(qr2) qc.cx(qr2[1], qr1[0]) qc.h(qr2[1]) - qc.x(qr2[1]).c_if(cr, 0) - qc.y(qr1[0]).c_if(cr, 1) - qc.z(qr1[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + qc.x(qr2[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.y(qr1[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.z(qr1[0]).c_if(cr, 2) qc.barrier(qr1, qr2) qc.measure(qr1[0], cr[0]) qc.measure(qr2[0], cr[1]) @@ -639,7 +642,8 @@ def test_circuit_raises_on_single_bit_condition(self): """OpenQASM 2 can't represent single-bit conditions, so test that a suitable error is printed if this is attempted.""" qc = QuantumCircuit(1, 1) - qc.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, True) with self.assertRaisesRegex(QasmError, "OpenQASM 2 can only condition on registers"): dumps(qc) diff --git a/test/python/circuit/test_commutation_checker.py b/test/python/circuit/test_commutation_checker.py index e261389a3272..8bb3f6939add 100644 --- a/test/python/circuit/test_commutation_checker.py +++ b/test/python/circuit/test_commutation_checker.py @@ -264,26 +264,33 @@ def test_conditional_gates(self): # Currently, in all cases commutativity checker should returns False. # This is definitely suboptimal. - self.assertFalse( - scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[2]], []) - ) - self.assertFalse( - scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[1]], []) - ) - self.assertFalse( - scc.commute( - CXGate().c_if(cr[0], 0), - [qr[0], qr[1]], - [], - CXGate().c_if(cr[0], 0), - [qr[0], qr[1]], - [], + with self.assertWarns(DeprecationWarning): + self.assertFalse( + scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[2]], []) ) - ) - self.assertFalse( - scc.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate().c_if(cr[0], 1), [qr[0]], []) - ) - self.assertFalse(scc.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate(), [qr[0]], [])) + with self.assertWarns(DeprecationWarning): + self.assertFalse( + scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[1]], []) + ) + with self.assertWarns(DeprecationWarning): + self.assertFalse( + scc.commute( + CXGate().c_if(cr[0], 0), + [qr[0], qr[1]], + [], + CXGate().c_if(cr[0], 0), + [qr[0], qr[1]], + [], + ) + ) + with self.assertWarns(DeprecationWarning): + self.assertFalse( + scc.commute( + XGate().c_if(cr[0], 0), [qr[0]], [], XGate().c_if(cr[0], 1), [qr[0]], [] + ) + ) + with self.assertWarns(DeprecationWarning): + self.assertFalse(scc.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate(), [qr[0]], [])) def test_complex_gates(self): """Check commutativity involving more complex gates.""" diff --git a/test/python/circuit/test_compose.py b/test/python/circuit/test_compose.py index 0981b5f3ed79..8221f81ad15e 100644 --- a/test/python/circuit/test_compose.py +++ b/test/python/circuit/test_compose.py @@ -367,10 +367,12 @@ def __init__(self): super().__init__("mygate", 1, []) conditional = QuantumCircuit(1, 1) - conditional.append(Custom(), [0], []).c_if(conditional.clbits[0], True) + with self.assertWarns(DeprecationWarning): + conditional.append(Custom(), [0], []).c_if(conditional.clbits[0], True) test = base.compose(conditional, qubits=[0], clbits=[0], copy=False) self.assertIs(test.data[-1].operation, conditional.data[-1].operation) - self.assertEqual(test.data[-1].operation.condition, (test.clbits[0], True)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(test.data[-1].operation.condition, (test.clbits[0], True)) def test_compose_classical(self): """Composing on classical bits. @@ -463,16 +465,20 @@ def test_compose_conditional(self): creg = ClassicalRegister(2, "rcr") circuit_right = QuantumCircuit(qreg, creg) - circuit_right.x(qreg[1]).c_if(creg, 3) - circuit_right.h(qreg[0]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit_right.x(qreg[1]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit_right.h(qreg[0]).c_if(creg, 3) circuit_right.measure(qreg, creg) # permuted subset of qubits and clbits circuit_composed = self.circuit_left.compose(circuit_right, qubits=[1, 4], clbits=[0, 1]) circuit_expected = self.circuit_left.copy() - circuit_expected.x(self.left_qubit4).c_if(*self.condition) - circuit_expected.h(self.left_qubit1).c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + circuit_expected.x(self.left_qubit4).c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + circuit_expected.h(self.left_qubit1).c_if(*self.condition) circuit_expected.measure(self.left_qubit1, self.left_clbit0) circuit_expected.measure(self.left_qubit4, self.left_clbit1) @@ -489,24 +495,36 @@ def test_compose_conditional_no_match(self): right.cx(0, 1) right.h(0) right.measure([0, 1], [0, 1]) - right.z(2).c_if(right.cregs[0], 1) - right.x(2).c_if(right.cregs[1], 1) + with self.assertWarns(DeprecationWarning): + right.z(2).c_if(right.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + right.x(2).c_if(right.cregs[1], 1) test = QuantumCircuit(3, 3).compose(right, range(3), range(2)) z = next(ins.operation for ins in test.data[::-1] if ins.operation.name == "z") x = next(ins.operation for ins in test.data[::-1] if ins.operation.name == "x") # The registers should have been mapped, including the bits inside them. Unlike the # previous test, there are no matching registers in the destination circuit, so the # composition needs to add new registers (bit groupings) over the existing mapped bits. - self.assertIsNot(z.condition, None) - self.assertIsInstance(z.condition[0], ClassicalRegister) - self.assertEqual(len(z.condition[0]), len(right.cregs[0])) - self.assertIs(z.condition[0][0], test.clbits[0]) - self.assertEqual(z.condition[1], 1) - self.assertIsNot(x.condition, None) - self.assertIsInstance(x.condition[0], ClassicalRegister) - self.assertEqual(len(x.condition[0]), len(right.cregs[1])) - self.assertEqual(z.condition[1], 1) - self.assertIs(x.condition[0][0], test.clbits[1]) + with self.assertWarns(DeprecationWarning): + self.assertIsNot(z.condition, None) + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(z.condition[0], ClassicalRegister) + with self.assertWarns(DeprecationWarning): + self.assertEqual(len(z.condition[0]), len(right.cregs[0])) + with self.assertWarns(DeprecationWarning): + self.assertIs(z.condition[0][0], test.clbits[0]) + with self.assertWarns(DeprecationWarning): + self.assertEqual(z.condition[1], 1) + with self.assertWarns(DeprecationWarning): + self.assertIsNot(x.condition, None) + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(x.condition[0], ClassicalRegister) + with self.assertWarns(DeprecationWarning): + self.assertEqual(len(x.condition[0]), len(right.cregs[1])) + with self.assertWarns(DeprecationWarning): + self.assertEqual(z.condition[1], 1) + with self.assertWarns(DeprecationWarning): + self.assertIs(x.condition[0][0], test.clbits[1]) def test_compose_switch_match(self): """Test that composition containing a `switch` with a register that matches proceeds @@ -729,11 +747,13 @@ def test_single_bit_condition(self): """Test that compose can correctly handle circuits that contain conditions on single bits. This is a regression test of the bug that broke qiskit-experiments in gh-7653.""" base = QuantumCircuit(1, 1) - base.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(0, True) test = QuantumCircuit(1, 1).compose(base) self.assertIsNot(base.clbits[0], test.clbits[0]) self.assertEqual(base, test) - self.assertIs(test.data[0].operation.condition[0], test.clbits[0]) + with self.assertWarns(DeprecationWarning): + self.assertIs(test.data[0].operation.condition[0], test.clbits[0]) def test_condition_mapping_ifelseop(self): """Test that the condition in an `IfElseOp` is correctly mapped to a new set of bits and diff --git a/test/python/circuit/test_control_flow.py b/test/python/circuit/test_control_flow.py index 35c57839d753..51733a37ffc9 100644 --- a/test/python/circuit/test_control_flow.py +++ b/test/python/circuit/test_control_flow.py @@ -587,7 +587,8 @@ def test_appending_switch_case_op(self, target, labels): self.assertEqual(qc.data[0].operation.name, "switch_case") self.assertEqual(qc.data[0].operation.params, bodies[: len(labels)]) - self.assertEqual(qc.data[0].operation.condition, None) + with self.assertWarns(DeprecationWarning): + self.assertEqual(qc.data[0].operation.condition, None) self.assertEqual(qc.data[0].qubits, tuple(qc.qubits[1:4])) self.assertEqual(qc.data[0].clbits, (qc.clbits[1],)) @@ -618,7 +619,7 @@ def test_quantumcircuit_switch(self, target, labels): self.assertEqual(qc.data[0].operation.name, "switch_case") self.assertEqual(qc.data[0].operation.params, bodies[: len(labels)]) - self.assertEqual(qc.data[0].operation.condition, None) + self.assertEqual(qc.data[0].operation._condition, None) self.assertEqual(qc.data[0].qubits, tuple(qc.qubits[1:4])) self.assertEqual(qc.data[0].clbits, (qc.clbits[1],)) @@ -808,15 +809,20 @@ def test_no_c_if_for_while_loop_if_else(self): body = QuantumCircuit(1) with self.assertRaisesRegex(NotImplementedError, r"cannot be classically controlled"): - qc.while_loop((qc.clbits[0], False), body, [qc.qubits[0]], []).c_if(qc.clbits[0], True) + with self.assertWarns(DeprecationWarning): + qc.while_loop((qc.clbits[0], False), body, [qc.qubits[0]], []).c_if( + qc.clbits[0], True + ) with self.assertRaisesRegex(NotImplementedError, r"cannot be classically controlled"): - qc.if_test((qc.clbits[0], False), body, [qc.qubits[0]], []).c_if(qc.clbits[0], True) + with self.assertWarns(DeprecationWarning): + qc.if_test((qc.clbits[0], False), body, [qc.qubits[0]], []).c_if(qc.clbits[0], True) with self.assertRaisesRegex(NotImplementedError, r"cannot be classically controlled"): - qc.if_else((qc.clbits[0], False), body, body, [qc.qubits[0]], []).c_if( - qc.clbits[0], True - ) + with self.assertWarns(DeprecationWarning): + qc.if_else((qc.clbits[0], False), body, body, [qc.qubits[0]], []).c_if( + qc.clbits[0], True + ) def test_nested_parameters_are_recognised(self): """Verify that parameters added inside a control-flow operator get added to the outer diff --git a/test/python/circuit/test_control_flow_builders.py b/test/python/circuit/test_control_flow_builders.py index 7a3a0873ae77..419cfb406d19 100644 --- a/test/python/circuit/test_control_flow_builders.py +++ b/test/python/circuit/test_control_flow_builders.py @@ -193,12 +193,16 @@ def test_register_condition_in_nested_block(self): with self.subTest("if/c_if"): test = QuantumCircuit(qr, clbits, cr1, cr2, cr3, cr4) with test.if_test((cr1, 0)): - test.x(0).c_if(cr2, 0) - test.z(0).c_if(cr3, 0) + with self.assertWarns(DeprecationWarning): + test.x(0).c_if(cr2, 0) + with self.assertWarns(DeprecationWarning): + test.z(0).c_if(cr3, 0) true_body = QuantumCircuit([qr[0]], clbits, cr1, cr2, cr3) - true_body.x(0).c_if(cr2, 0) - true_body.z(0).c_if(cr3, 0) + with self.assertWarns(DeprecationWarning): + true_body.x(0).c_if(cr2, 0) + with self.assertWarns(DeprecationWarning): + true_body.z(0).c_if(cr3, 0) expected = QuantumCircuit(qr, clbits, cr1, cr2, cr3, cr4) expected.if_test((cr1, 0), true_body, [qr[0]], clbits + list(cr1)) @@ -209,14 +213,18 @@ def test_register_condition_in_nested_block(self): test = QuantumCircuit(qr, clbits, cr1, cr2, cr3, cr4) with test.while_loop((cr1, 0)): with test.if_test((cr2, 0)) as else_: - test.x(0).c_if(cr3, 0) + with self.assertWarns(DeprecationWarning): + test.x(0).c_if(cr3, 0) with else_: - test.z(0).c_if(cr4, 0) + with self.assertWarns(DeprecationWarning): + test.z(0).c_if(cr4, 0) true_body = QuantumCircuit([qr[0]], cr2, cr3, cr4) - true_body.x(0).c_if(cr3, 0) + with self.assertWarns(DeprecationWarning): + true_body.x(0).c_if(cr3, 0) false_body = QuantumCircuit([qr[0]], cr2, cr3, cr4) - false_body.z(0).c_if(cr4, 0) + with self.assertWarns(DeprecationWarning): + false_body.z(0).c_if(cr4, 0) while_body = QuantumCircuit([qr[0]], clbits, cr1, cr2, cr3, cr4) while_body.if_else((cr2, 0), true_body, false_body, [qr[0]], clbits) @@ -647,17 +655,23 @@ def test_if_else_tracks_registers(self): test = QuantumCircuit(qr, *cr) with test.if_test((cr[0], 0)) as else_: - test.h(0).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(cr[1], 0) # Test repetition. - test.h(0).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(cr[1], 0) with else_: - test.h(0).c_if(cr[2], 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(cr[2], 0) true_body = QuantumCircuit([qr[0]], cr[0], cr[1], cr[2]) - true_body.h(qr[0]).c_if(cr[1], 0) - true_body.h(qr[0]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + true_body.h(qr[0]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + true_body.h(qr[0]).c_if(cr[1], 0) false_body = QuantumCircuit([qr[0]], cr[0], cr[1], cr[2]) - false_body.h(qr[0]).c_if(cr[2], 0) + with self.assertWarns(DeprecationWarning): + false_body.h(qr[0]).c_if(cr[2], 0) expected = QuantumCircuit(qr, *cr) expected.if_else( @@ -1036,11 +1050,13 @@ def test_break_continue_accept_c_if(self, loop_operation): test = QuantumCircuit(qubits, clbits) with test.for_loop(range(2)): test.h(0) - loop_operation(test).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + loop_operation(test).c_if(1, 0) body = QuantumCircuit([qubits[0]], [clbits[1]]) body.h(qubits[0]) - loop_operation(body).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + loop_operation(body).c_if(clbits[1], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, body, [qubits[0]], [clbits[1]]) @@ -1052,11 +1068,13 @@ def test_break_continue_accept_c_if(self, loop_operation): test = QuantumCircuit(qubits, clbits) with test.while_loop(cond): test.h(0) - loop_operation(test).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + loop_operation(test).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) body.h(qubits[0]) - loop_operation(body).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + loop_operation(body).c_if(clbits[1], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop(cond, body, [qubits[0]], clbits) @@ -1230,7 +1248,8 @@ def test_break_continue_nested_in_if(self, loop_operation): # full width of the loop do so. with test.if_test(cond_inner): pass - test.h(0).c_if(2, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(2, 0) true_body1 = QuantumCircuit([qubits[0], clbits[0], clbits[2]]) loop_operation(true_body1) @@ -1240,7 +1259,8 @@ def test_break_continue_nested_in_if(self, loop_operation): loop_body = QuantumCircuit([qubits[0], clbits[0], clbits[2]]) loop_body.if_test(cond_inner, true_body1, [qubits[0]], [clbits[0], clbits[2]]) loop_body.if_test(cond_inner, true_body2, [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, [qubits[0]], [clbits[0], clbits[2]]) @@ -1258,7 +1278,8 @@ def test_break_continue_nested_in_if(self, loop_operation): pass with else_: pass - test.h(0).c_if(2, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(2, 0) true_body1 = QuantumCircuit([qubits[0], qubits[1], clbits[0], clbits[2]]) true_body1.h(qubits[1]) @@ -1273,7 +1294,8 @@ def test_break_continue_nested_in_if(self, loop_operation): cond_inner, true_body1, false_body1, [qubits[0], qubits[1]], [clbits[0], clbits[2]] ) loop_body.if_else(cond_inner, true_body2, false_body2, [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop( @@ -1289,7 +1311,8 @@ def test_break_continue_nested_in_if(self, loop_operation): loop_operation(test) with test.if_test(cond_inner): pass - test.h(0).c_if(2, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(2, 0) true_body1 = QuantumCircuit([qubits[0], clbits[0], clbits[1], clbits[2]]) loop_operation(true_body1) @@ -1301,7 +1324,8 @@ def test_break_continue_nested_in_if(self, loop_operation): cond_inner, true_body1, [qubits[0]], [clbits[0], clbits[1], clbits[2]] ) loop_body.if_test(cond_inner, true_body2, [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop( @@ -1321,7 +1345,8 @@ def test_break_continue_nested_in_if(self, loop_operation): pass with else_: pass - test.h(0).c_if(2, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(2, 0) true_body1 = QuantumCircuit([qubits[0], qubits[1], clbits[0], clbits[1], clbits[2]]) true_body1.h(qubits[1]) @@ -1340,7 +1365,8 @@ def test_break_continue_nested_in_if(self, loop_operation): [clbits[0], clbits[1], clbits[2]], ) loop_body.if_else(cond_inner, true_body2, false_body2, [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop( @@ -1368,7 +1394,8 @@ def test_break_continue_nested_in_switch(self, loop_operation): with test.switch(clbits[0]) as case: with case(case.DEFAULT): pass - test.h(0).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(clbits[2], 0) body0 = QuantumCircuit([qubits[0], clbits[0], clbits[2]]) loop_operation(body0) @@ -1379,7 +1406,8 @@ def test_break_continue_nested_in_switch(self, loop_operation): loop_body = QuantumCircuit([qubits[0], clbits[0], clbits[2]]) loop_body.switch(clbits[0], [(0, body0), (1, body1)], [qubits[0]], [clbits[0], clbits[2]]) loop_body.switch(clbits[0], [(CASE_DEFAULT, body2)], [], [clbits[0]]) - loop_body.h(qubits[0]).c_if(clbits[2], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[2], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, [qubits[0]], [clbits[0], clbits[2]]) @@ -1465,18 +1493,23 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_operation(test) # inner true 2 with test.if_test(cond_inner): - test.h(0).c_if(3, 0) - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) # outer true 2 with test.if_test(cond_outer): - test.h(2).c_if(5, 0) - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) inner_true_body1 = QuantumCircuit(qubits[:4], clbits[:2], clbits[3:7]) loop_operation(inner_true_body1) inner_true_body2 = QuantumCircuit([qubits[0], clbits[0], clbits[3]]) - inner_true_body2.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + inner_true_body2.h(qubits[0]).c_if(clbits[3], 0) outer_true_body1 = QuantumCircuit(qubits[:4], clbits[:2], clbits[3:7]) outer_true_body1.if_test( @@ -1485,15 +1518,18 @@ def test_break_continue_deeply_nested(self, loop_operation): outer_true_body1.if_test( cond_inner, inner_true_body2, [qubits[0]], [clbits[0], clbits[3]] ) - outer_true_body1.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + outer_true_body1.h(qubits[1]).c_if(clbits[4], 0) outer_true_body2 = QuantumCircuit([qubits[2], clbits[1], clbits[5]]) - outer_true_body2.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + outer_true_body2.h(qubits[2]).c_if(clbits[5], 0) loop_body = QuantumCircuit(qubits[:4], clbits[:2] + clbits[3:7]) loop_body.if_test(cond_outer, outer_true_body1, qubits[:4], clbits[:2] + clbits[3:7]) loop_body.if_test(cond_outer, outer_true_body2, [qubits[2]], [clbits[1], clbits[5]]) - loop_body.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[3]).c_if(clbits[6], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, qubits[:4], clbits[:2] + clbits[3:7]) @@ -1507,31 +1543,43 @@ def test_break_continue_deeply_nested(self, loop_operation): with test.if_test(cond_outer): # inner 1 with test.if_test(cond_inner) as inner1_else: - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) with inner1_else: - loop_operation(test).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + loop_operation(test).c_if(4, 0) # inner 2 with test.if_test(cond_inner) as inner2_else: - test.h(1).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(5, 0) with inner2_else: - test.h(2).c_if(6, 0) - test.h(3).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(7, 0) # outer 2 with test.if_test(cond_outer) as outer2_else: - test.h(4).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(8, 0) with outer2_else: - test.h(5).c_if(9, 0) - test.h(6).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(10, 0) inner1_true = QuantumCircuit(qubits[:7], clbits[:2], clbits[3:11]) - inner1_true.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + inner1_true.h(qubits[0]).c_if(clbits[3], 0) inner1_false = QuantumCircuit(qubits[:7], clbits[:2], clbits[3:11]) - loop_operation(inner1_false).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + loop_operation(inner1_false).c_if(clbits[4], 0) inner2_true = QuantumCircuit([qubits[1], qubits[2], clbits[0], clbits[5], clbits[6]]) - inner2_true.h(qubits[1]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + inner2_true.h(qubits[1]).c_if(clbits[5], 0) inner2_false = QuantumCircuit([qubits[1], qubits[2], clbits[0], clbits[5], clbits[6]]) - inner2_false.h(qubits[2]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + inner2_false.h(qubits[2]).c_if(clbits[6], 0) outer1_true = QuantumCircuit(qubits[:7], clbits[:2], clbits[3:11]) outer1_true.if_else( @@ -1544,12 +1592,15 @@ def test_break_continue_deeply_nested(self, loop_operation): qubits[1:3], [clbits[0], clbits[5], clbits[6]], ) - outer1_true.h(qubits[3]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + outer1_true.h(qubits[3]).c_if(clbits[7], 0) outer2_true = QuantumCircuit([qubits[4], qubits[5], clbits[1], clbits[8], clbits[9]]) - outer2_true.h(qubits[4]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[4]).c_if(clbits[8], 0) outer2_false = QuantumCircuit([qubits[4], qubits[5], clbits[1], clbits[8], clbits[9]]) - outer2_false.h(qubits[5]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer2_false.h(qubits[5]).c_if(clbits[9], 0) loop_body = QuantumCircuit(qubits[:7], clbits[:2], clbits[3:11]) loop_body.if_test(cond_outer, outer1_true, qubits[:7], clbits[:2] + clbits[3:11]) @@ -1560,7 +1611,8 @@ def test_break_continue_deeply_nested(self, loop_operation): qubits[4:6], [clbits[1], clbits[8], clbits[9]], ) - loop_body.h(qubits[6]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[6]).c_if(clbits[10], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, qubits[:7], clbits[:2] + clbits[3:11]) @@ -1574,72 +1626,90 @@ def test_break_continue_deeply_nested(self, loop_operation): test = QuantumCircuit(qubits, clbits) with test.for_loop(range(2)): - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) # outer 1 with test.if_test(cond_outer) as outer1_else: - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) with outer1_else: - test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) # outer 2 (nesting the inner condition in the 'if') with test.if_test(cond_outer) as outer2_else: - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) # inner 21 with test.if_test(cond_inner) as inner21_else: loop_operation(test) with inner21_else: - test.h(4).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(7, 0) # inner 22 with test.if_test(cond_inner) as inner22_else: - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) with inner22_else: loop_operation(test) # inner 23 with test.switch(cond_inner[0]) as inner23_case: with inner23_case(True): - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) with inner23_case(False): loop_operation(test) - test.h(6).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(9, 0) with outer2_else: - test.h(7).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(7).c_if(10, 0) # inner 24 with test.if_test(cond_inner) as inner24_else: - test.h(8).c_if(11, 0) + with self.assertWarns(DeprecationWarning): + test.h(8).c_if(11, 0) with inner24_else: - test.h(9).c_if(12, 0) + with self.assertWarns(DeprecationWarning): + test.h(9).c_if(12, 0) # outer 3 (nesting the inner condition in an 'else' branch) with test.if_test(cond_outer) as outer3_else: - test.h(10).c_if(13, 0) + with self.assertWarns(DeprecationWarning): + test.h(10).c_if(13, 0) with outer3_else: - test.h(11).c_if(14, 0) + with self.assertWarns(DeprecationWarning): + test.h(11).c_if(14, 0) # inner 31 with test.if_test(cond_inner) as inner31_else: loop_operation(test) with inner31_else: - test.h(12).c_if(15, 0) + with self.assertWarns(DeprecationWarning): + test.h(12).c_if(15, 0) # inner 32 with test.if_test(cond_inner) as inner32_else: - test.h(13).c_if(16, 0) + with self.assertWarns(DeprecationWarning): + test.h(13).c_if(16, 0) with inner32_else: loop_operation(test) # inner 33 with test.if_test(cond_inner) as inner33_else: - test.h(14).c_if(17, 0) + with self.assertWarns(DeprecationWarning): + test.h(14).c_if(17, 0) with inner33_else: - test.h(15).c_if(18, 0) + with self.assertWarns(DeprecationWarning): + test.h(15).c_if(18, 0) - test.h(16).c_if(19, 0) + with self.assertWarns(DeprecationWarning): + test.h(16).c_if(19, 0) # End of test "for" loop. # No `clbits[2]` here because that's only used in `cond_loop`, for while loops. @@ -1648,32 +1718,40 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_bits = loop_qubits + loop_clbits outer1_true = QuantumCircuit([qubits[1], qubits[2], clbits[1], clbits[4], clbits[5]]) - outer1_true.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + outer1_true.h(qubits[1]).c_if(clbits[4], 0) outer1_false = QuantumCircuit([qubits[1], qubits[2], clbits[1], clbits[4], clbits[5]]) - outer1_false.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + outer1_false.h(qubits[2]).c_if(clbits[5], 0) inner21_true = QuantumCircuit(loop_bits) loop_operation(inner21_true) inner21_false = QuantumCircuit(loop_bits) - inner21_false.h(qubits[4]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + inner21_false.h(qubits[4]).c_if(clbits[7], 0) inner22_true = QuantumCircuit(loop_bits) - inner22_true.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + inner22_true.h(qubits[5]).c_if(clbits[8], 0) inner22_false = QuantumCircuit(loop_bits) loop_operation(inner22_false) inner23_true = QuantumCircuit(loop_bits) - inner23_true.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + inner23_true.h(qubits[5]).c_if(clbits[8], 0) inner23_false = QuantumCircuit(loop_bits) loop_operation(inner23_false) inner24_true = QuantumCircuit(qubits[8:10], [clbits[0], clbits[11], clbits[12]]) - inner24_true.h(qubits[8]).c_if(clbits[11], 0) + with self.assertWarns(DeprecationWarning): + inner24_true.h(qubits[8]).c_if(clbits[11], 0) inner24_false = QuantumCircuit(qubits[8:10], [clbits[0], clbits[11], clbits[12]]) - inner24_false.h(qubits[9]).c_if(clbits[12], 0) + with self.assertWarns(DeprecationWarning): + inner24_false.h(qubits[9]).c_if(clbits[12], 0) outer2_true = QuantumCircuit(loop_bits) - outer2_true.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[3]).c_if(clbits[6], 0) outer2_true.if_else(cond_inner, inner21_true, inner21_false, loop_qubits, loop_clbits) outer2_true.if_else(cond_inner, inner22_true, inner22_false, loop_qubits, loop_clbits) outer2_true.switch( @@ -1682,9 +1760,11 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_qubits, loop_clbits, ) - outer2_true.h(qubits[6]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[6]).c_if(clbits[9], 0) outer2_false = QuantumCircuit(loop_bits) - outer2_false.h(qubits[7]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + outer2_false.h(qubits[7]).c_if(clbits[10], 0) outer2_false.if_else( cond_inner, inner24_true, @@ -1696,22 +1776,28 @@ def test_break_continue_deeply_nested(self, loop_operation): inner31_true = QuantumCircuit(loop_bits) loop_operation(inner31_true) inner31_false = QuantumCircuit(loop_bits) - inner31_false.h(qubits[12]).c_if(clbits[15], 0) + with self.assertWarns(DeprecationWarning): + inner31_false.h(qubits[12]).c_if(clbits[15], 0) inner32_true = QuantumCircuit(loop_bits) - inner32_true.h(qubits[13]).c_if(clbits[16], 0) + with self.assertWarns(DeprecationWarning): + inner32_true.h(qubits[13]).c_if(clbits[16], 0) inner32_false = QuantumCircuit(loop_bits) loop_operation(inner32_false) inner33_true = QuantumCircuit(qubits[14:16], [clbits[0], clbits[17], clbits[18]]) - inner33_true.h(qubits[14]).c_if(clbits[17], 0) + with self.assertWarns(DeprecationWarning): + inner33_true.h(qubits[14]).c_if(clbits[17], 0) inner33_false = QuantumCircuit(qubits[14:16], [clbits[0], clbits[17], clbits[18]]) - inner33_false.h(qubits[15]).c_if(clbits[18], 0) + with self.assertWarns(DeprecationWarning): + inner33_false.h(qubits[15]).c_if(clbits[18], 0) outer3_true = QuantumCircuit(loop_bits) - outer3_true.h(qubits[10]).c_if(clbits[13], 0) + with self.assertWarns(DeprecationWarning): + outer3_true.h(qubits[10]).c_if(clbits[13], 0) outer3_false = QuantumCircuit(loop_bits) - outer3_false.h(qubits[11]).c_if(clbits[14], 0) + with self.assertWarns(DeprecationWarning): + outer3_false.h(qubits[11]).c_if(clbits[14], 0) outer3_false.if_else(cond_inner, inner31_true, inner31_false, loop_qubits, loop_clbits) outer3_false.if_else(cond_inner, inner32_true, inner32_false, loop_qubits, loop_clbits) outer3_false.if_else( @@ -1723,7 +1809,8 @@ def test_break_continue_deeply_nested(self, loop_operation): ) loop_body = QuantumCircuit(loop_bits) - loop_body.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[3], 0) loop_body.if_else( cond_outer, outer1_true, @@ -1733,7 +1820,8 @@ def test_break_continue_deeply_nested(self, loop_operation): ) loop_body.if_else(cond_outer, outer2_true, outer2_false, loop_qubits, loop_clbits) loop_body.if_else(cond_outer, outer3_true, outer3_false, loop_qubits, loop_clbits) - loop_body.h(qubits[16]).c_if(clbits[19], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[16]).c_if(clbits[19], 0) expected = QuantumCircuit(qubits, clbits) expected.for_loop(range(2), None, loop_body, loop_qubits, loop_clbits) @@ -1756,33 +1844,41 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_operation(test) # inner true 2 with test.if_test(cond_inner): - test.h(0).c_if(3, 0) - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) # outer true 2 with test.if_test(cond_outer): - test.h(2).c_if(5, 0) - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) inner_true_body1 = QuantumCircuit(qubits[:4], clbits[:7]) loop_operation(inner_true_body1) inner_true_body2 = QuantumCircuit([qubits[0], clbits[0], clbits[3]]) - inner_true_body2.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + inner_true_body2.h(qubits[0]).c_if(clbits[3], 0) outer_true_body1 = QuantumCircuit(qubits[:4], clbits[:7]) outer_true_body1.if_test(cond_inner, inner_true_body1, qubits[:4], clbits[:7]) outer_true_body1.if_test( cond_inner, inner_true_body2, [qubits[0]], [clbits[0], clbits[3]] ) - outer_true_body1.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + outer_true_body1.h(qubits[1]).c_if(clbits[4], 0) outer_true_body2 = QuantumCircuit([qubits[2], clbits[1], clbits[5]]) - outer_true_body2.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + outer_true_body2.h(qubits[2]).c_if(clbits[5], 0) loop_body = QuantumCircuit(qubits[:4], clbits[:7]) loop_body.if_test(cond_outer, outer_true_body1, qubits[:4], clbits[:7]) loop_body.if_test(cond_outer, outer_true_body2, [qubits[2]], [clbits[1], clbits[5]]) - loop_body.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[3]).c_if(clbits[6], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop(cond_loop, loop_body, qubits[:4], clbits[:7]) @@ -1796,31 +1892,43 @@ def test_break_continue_deeply_nested(self, loop_operation): with test.if_test(cond_outer): # inner 1 with test.if_test(cond_inner) as inner1_else: - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) with inner1_else: - loop_operation(test).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + loop_operation(test).c_if(4, 0) # inner 2 with test.if_test(cond_inner) as inner2_else: - test.h(1).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(5, 0) with inner2_else: - test.h(2).c_if(6, 0) - test.h(3).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(7, 0) # outer 2 with test.if_test(cond_outer) as outer2_else: - test.h(4).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(8, 0) with outer2_else: - test.h(5).c_if(9, 0) - test.h(6).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(10, 0) inner1_true = QuantumCircuit(qubits[:7], clbits[:11]) - inner1_true.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + inner1_true.h(qubits[0]).c_if(clbits[3], 0) inner1_false = QuantumCircuit(qubits[:7], clbits[:11]) - loop_operation(inner1_false).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + loop_operation(inner1_false).c_if(clbits[4], 0) inner2_true = QuantumCircuit([qubits[1], qubits[2], clbits[0], clbits[5], clbits[6]]) - inner2_true.h(qubits[1]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + inner2_true.h(qubits[1]).c_if(clbits[5], 0) inner2_false = QuantumCircuit([qubits[1], qubits[2], clbits[0], clbits[5], clbits[6]]) - inner2_false.h(qubits[2]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + inner2_false.h(qubits[2]).c_if(clbits[6], 0) outer1_true = QuantumCircuit(qubits[:7], clbits[:11]) outer1_true.if_else(cond_inner, inner1_true, inner1_false, qubits[:7], clbits[:11]) @@ -1831,12 +1939,15 @@ def test_break_continue_deeply_nested(self, loop_operation): qubits[1:3], [clbits[0], clbits[5], clbits[6]], ) - outer1_true.h(qubits[3]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + outer1_true.h(qubits[3]).c_if(clbits[7], 0) outer2_true = QuantumCircuit([qubits[4], qubits[5], clbits[1], clbits[8], clbits[9]]) - outer2_true.h(qubits[4]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[4]).c_if(clbits[8], 0) outer2_false = QuantumCircuit([qubits[4], qubits[5], clbits[1], clbits[8], clbits[9]]) - outer2_false.h(qubits[5]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer2_false.h(qubits[5]).c_if(clbits[9], 0) loop_body = QuantumCircuit(qubits[:7], clbits[:11]) loop_body.if_test(cond_outer, outer1_true, qubits[:7], clbits[:11]) @@ -1847,7 +1958,8 @@ def test_break_continue_deeply_nested(self, loop_operation): qubits[4:6], [clbits[1], clbits[8], clbits[9]], ) - loop_body.h(qubits[6]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[6]).c_if(clbits[10], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop(cond_loop, loop_body, qubits[:7], clbits[:11]) @@ -1857,65 +1969,81 @@ def test_break_continue_deeply_nested(self, loop_operation): with self.subTest("while/else/else"): test = QuantumCircuit(qubits, clbits) with test.while_loop(cond_loop): - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) # outer 1 with test.if_test(cond_outer) as outer1_else: - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) with outer1_else: - test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) # outer 2 (nesting the inner condition in the 'if') with test.if_test(cond_outer) as outer2_else: - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) # inner 21 with test.if_test(cond_inner) as inner21_else: loop_operation(test) with inner21_else: - test.h(4).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(7, 0) # inner 22 with test.if_test(cond_inner) as inner22_else: - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) with inner22_else: loop_operation(test) - test.h(6).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(9, 0) with outer2_else: - test.h(7).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(7).c_if(10, 0) # inner 23 with test.if_test(cond_inner) as inner23_else: - test.h(8).c_if(11, 0) + with self.assertWarns(DeprecationWarning): + test.h(8).c_if(11, 0) with inner23_else: - test.h(9).c_if(12, 0) + with self.assertWarns(DeprecationWarning): + test.h(9).c_if(12, 0) # outer 3 (nesting the inner condition in an 'else' branch) with test.if_test(cond_outer) as outer3_else: - test.h(10).c_if(13, 0) + with self.assertWarns(DeprecationWarning): + test.h(10).c_if(13, 0) with outer3_else: - test.h(11).c_if(14, 0) + with self.assertWarns(DeprecationWarning): + test.h(11).c_if(14, 0) # inner 31 with test.if_test(cond_inner) as inner31_else: loop_operation(test) with inner31_else: - test.h(12).c_if(15, 0) + with self.assertWarns(DeprecationWarning): + test.h(12).c_if(15, 0) # inner 32 with test.if_test(cond_inner) as inner32_else: - test.h(13).c_if(16, 0) + with self.assertWarns(DeprecationWarning): + test.h(13).c_if(16, 0) with inner32_else: loop_operation(test) # inner 33 with test.if_test(cond_inner) as inner33_else: - test.h(14).c_if(17, 0) + with self.assertWarns(DeprecationWarning): + test.h(14).c_if(17, 0) with inner33_else: - test.h(15).c_if(18, 0) - - test.h(16).c_if(19, 0) + with self.assertWarns(DeprecationWarning): + test.h(15).c_if(18, 0) + with self.assertWarns(DeprecationWarning): + test.h(16).c_if(19, 0) # End of test "for" loop. # No `clbits[2]` here because that's only used in `cond_loop`, for while loops. @@ -1924,32 +2052,41 @@ def test_break_continue_deeply_nested(self, loop_operation): loop_bits = loop_qubits + loop_clbits outer1_true = QuantumCircuit([qubits[1], qubits[2], clbits[1], clbits[4], clbits[5]]) - outer1_true.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + outer1_true.h(qubits[1]).c_if(clbits[4], 0) outer1_false = QuantumCircuit([qubits[1], qubits[2], clbits[1], clbits[4], clbits[5]]) - outer1_false.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + outer1_false.h(qubits[2]).c_if(clbits[5], 0) inner21_true = QuantumCircuit(loop_bits) loop_operation(inner21_true) inner21_false = QuantumCircuit(loop_bits) - inner21_false.h(qubits[4]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + inner21_false.h(qubits[4]).c_if(clbits[7], 0) inner22_true = QuantumCircuit(loop_bits) - inner22_true.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + inner22_true.h(qubits[5]).c_if(clbits[8], 0) inner22_false = QuantumCircuit(loop_bits) loop_operation(inner22_false) inner23_true = QuantumCircuit(qubits[8:10], [clbits[0], clbits[11], clbits[12]]) - inner23_true.h(qubits[8]).c_if(clbits[11], 0) + with self.assertWarns(DeprecationWarning): + inner23_true.h(qubits[8]).c_if(clbits[11], 0) inner23_false = QuantumCircuit(qubits[8:10], [clbits[0], clbits[11], clbits[12]]) - inner23_false.h(qubits[9]).c_if(clbits[12], 0) + with self.assertWarns(DeprecationWarning): + inner23_false.h(qubits[9]).c_if(clbits[12], 0) outer2_true = QuantumCircuit(loop_bits) - outer2_true.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[3]).c_if(clbits[6], 0) outer2_true.if_else(cond_inner, inner21_true, inner21_false, loop_qubits, loop_clbits) outer2_true.if_else(cond_inner, inner22_true, inner22_false, loop_qubits, loop_clbits) - outer2_true.h(qubits[6]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer2_true.h(qubits[6]).c_if(clbits[9], 0) outer2_false = QuantumCircuit(loop_bits) - outer2_false.h(qubits[7]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + outer2_false.h(qubits[7]).c_if(clbits[10], 0) outer2_false.if_else( cond_inner, inner23_true, @@ -1961,22 +2098,28 @@ def test_break_continue_deeply_nested(self, loop_operation): inner31_true = QuantumCircuit(loop_bits) loop_operation(inner31_true) inner31_false = QuantumCircuit(loop_bits) - inner31_false.h(qubits[12]).c_if(clbits[15], 0) + with self.assertWarns(DeprecationWarning): + inner31_false.h(qubits[12]).c_if(clbits[15], 0) inner32_true = QuantumCircuit(loop_bits) - inner32_true.h(qubits[13]).c_if(clbits[16], 0) + with self.assertWarns(DeprecationWarning): + inner32_true.h(qubits[13]).c_if(clbits[16], 0) inner32_false = QuantumCircuit(loop_bits) loop_operation(inner32_false) inner33_true = QuantumCircuit(qubits[14:16], [clbits[0], clbits[17], clbits[18]]) - inner33_true.h(qubits[14]).c_if(clbits[17], 0) + with self.assertWarns(DeprecationWarning): + inner33_true.h(qubits[14]).c_if(clbits[17], 0) inner33_false = QuantumCircuit(qubits[14:16], [clbits[0], clbits[17], clbits[18]]) - inner33_false.h(qubits[15]).c_if(clbits[18], 0) + with self.assertWarns(DeprecationWarning): + inner33_false.h(qubits[15]).c_if(clbits[18], 0) outer3_true = QuantumCircuit(loop_bits) - outer3_true.h(qubits[10]).c_if(clbits[13], 0) + with self.assertWarns(DeprecationWarning): + outer3_true.h(qubits[10]).c_if(clbits[13], 0) outer3_false = QuantumCircuit(loop_bits) - outer3_false.h(qubits[11]).c_if(clbits[14], 0) + with self.assertWarns(DeprecationWarning): + outer3_false.h(qubits[11]).c_if(clbits[14], 0) outer3_false.if_else(cond_inner, inner31_true, inner31_false, loop_qubits, loop_clbits) outer3_false.if_else(cond_inner, inner32_true, inner32_false, loop_qubits, loop_clbits) outer3_false.if_else( @@ -1988,7 +2131,8 @@ def test_break_continue_deeply_nested(self, loop_operation): ) loop_body = QuantumCircuit(loop_bits) - loop_body.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[0]).c_if(clbits[3], 0) loop_body.if_else( cond_outer, outer1_true, @@ -1998,7 +2142,8 @@ def test_break_continue_deeply_nested(self, loop_operation): ) loop_body.if_else(cond_outer, outer2_true, outer2_false, loop_qubits, loop_clbits) loop_body.if_else(cond_outer, outer3_true, outer3_false, loop_qubits, loop_clbits) - loop_body.h(qubits[16]).c_if(clbits[19], 0) + with self.assertWarns(DeprecationWarning): + loop_body.h(qubits[16]).c_if(clbits[19], 0) expected = QuantumCircuit(qubits, clbits) expected.while_loop(cond_loop, loop_body, loop_qubits, loop_clbits) @@ -2008,52 +2153,68 @@ def test_break_continue_deeply_nested(self, loop_operation): with self.subTest("if/while/if/switch"): test = QuantumCircuit(qubits, clbits) with test.if_test(cond_outer): # outer_t - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) with test.while_loop(cond_loop): # loop - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) with test.if_test(cond_inner): # inner_t - test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) with test.switch(5) as case_: with case_(False): # case_f - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) with case_(True): # case_t loop_operation(test) - test.h(4).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(7, 0) # exit inner_t - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) # exit loop - test.h(6).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(9, 0) # exit outer_t - test.h(7).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(7).c_if(10, 0) case_f = QuantumCircuit(qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - case_f.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + case_f.h(qubits[3]).c_if(clbits[6], 0) case_t = QuantumCircuit(qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) loop_operation(case_t) inner_t = QuantumCircuit(qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - inner_t.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + inner_t.h(qubits[2]).c_if(clbits[5], 0) inner_t.switch( clbits[5], [(False, case_f), (True, case_t)], qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9], ) - inner_t.h(qubits[4]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + inner_t.h(qubits[4]).c_if(clbits[7], 0) loop = QuantumCircuit(qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - loop.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + loop.h(qubits[1]).c_if(clbits[4], 0) loop.if_test(cond_inner, inner_t, qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - loop.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + loop.h(qubits[5]).c_if(clbits[8], 0) outer_t = QuantumCircuit(qubits[:7], clbits[:10]) - outer_t.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + outer_t.h(qubits[0]).c_if(clbits[3], 0) outer_t.while_loop(cond_loop, loop, qubits[1:6], [clbits[0], clbits[2]] + clbits[4:9]) - outer_t.h(qubits[6]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer_t.h(qubits[6]).c_if(clbits[9], 0) expected = QuantumCircuit(qubits, clbits) expected.if_test(cond_outer, outer_t, qubits[:7], clbits[:10]) - expected.h(qubits[7]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + expected.h(qubits[7]).c_if(clbits[10], 0) self.assertEqual(canonicalize_control_flow(test), canonicalize_control_flow(expected)) @@ -2061,64 +2222,82 @@ def test_break_continue_deeply_nested(self, loop_operation): test = QuantumCircuit(qubits, clbits) with test.switch(0) as case_outer: with case_outer(False): # outer_case_f - test.h(0).c_if(3, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(3, 0) with test.for_loop(range(2)): # loop - test.h(1).c_if(4, 0) + with self.assertWarns(DeprecationWarning): + test.h(1).c_if(4, 0) with test.switch(1) as case_inner: with case_inner(False): # inner_case_f - test.h(2).c_if(5, 0) + with self.assertWarns(DeprecationWarning): + test.h(2).c_if(5, 0) with test.if_test((2, True)) as else_: # if_t - test.h(3).c_if(6, 0) + with self.assertWarns(DeprecationWarning): + test.h(3).c_if(6, 0) with else_: # if_f loop_operation(test) - test.h(4).c_if(7, 0) + with self.assertWarns(DeprecationWarning): + test.h(4).c_if(7, 0) with case_inner(True): # inner_case_t loop_operation(test) - test.h(5).c_if(8, 0) + with self.assertWarns(DeprecationWarning): + test.h(5).c_if(8, 0) # exit loop1 - test.h(6).c_if(9, 0) + with self.assertWarns(DeprecationWarning): + test.h(6).c_if(9, 0) with case_outer(True): # outer_case_t - test.h(7).c_if(10, 0) - test.h(8).c_if(11, 0) + with self.assertWarns(DeprecationWarning): + test.h(7).c_if(10, 0) + with self.assertWarns(DeprecationWarning): + test.h(8).c_if(11, 0) if_t = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) - if_t.h(qubits[3]).c_if(clbits[6], 0) + with self.assertWarns(DeprecationWarning): + if_t.h(qubits[3]).c_if(clbits[6], 0) if_f = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) loop_operation(if_f) inner_case_f = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) - inner_case_f.h(qubits[2]).c_if(clbits[5], 0) + with self.assertWarns(DeprecationWarning): + inner_case_f.h(qubits[2]).c_if(clbits[5], 0) inner_case_f.if_else( (clbits[2], True), if_t, if_f, qubits[1:6], clbits[1:3] + clbits[4:9] ) - inner_case_f.h(qubits[4]).c_if(clbits[7], 0) + with self.assertWarns(DeprecationWarning): + inner_case_f.h(qubits[4]).c_if(clbits[7], 0) inner_case_t = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) loop_operation(inner_case_t) loop = QuantumCircuit(qubits[1:6], clbits[1:3] + clbits[4:9]) - loop.h(qubits[1]).c_if(clbits[4], 0) + with self.assertWarns(DeprecationWarning): + loop.h(qubits[1]).c_if(clbits[4], 0) loop.switch( clbits[1], [(False, inner_case_f), (True, inner_case_t)], qubits[1:6], clbits[1:3] + clbits[4:9], ) - loop.h(qubits[5]).c_if(clbits[8], 0) + with self.assertWarns(DeprecationWarning): + loop.h(qubits[5]).c_if(clbits[8], 0) outer_case_f = QuantumCircuit(qubits[:8], clbits[:11]) - outer_case_f.h(qubits[0]).c_if(clbits[3], 0) + with self.assertWarns(DeprecationWarning): + outer_case_f.h(qubits[0]).c_if(clbits[3], 0) outer_case_f.for_loop(range(2), None, loop, qubits[1:6], clbits[1:3] + clbits[4:9]) - outer_case_f.h(qubits[6]).c_if(clbits[9], 0) + with self.assertWarns(DeprecationWarning): + outer_case_f.h(qubits[6]).c_if(clbits[9], 0) outer_case_t = QuantumCircuit(qubits[:8], clbits[:11]) - outer_case_t.h(qubits[7]).c_if(clbits[10], 0) + with self.assertWarns(DeprecationWarning): + outer_case_t.h(qubits[7]).c_if(clbits[10], 0) expected = QuantumCircuit(qubits, clbits) expected.switch( clbits[0], [(False, outer_case_f), (True, outer_case_t)], qubits[:8], clbits[:11] ) - expected.h(qubits[8]).c_if(clbits[11], 0) + with self.assertWarns(DeprecationWarning): + expected.h(qubits[8]).c_if(clbits[11], 0) self.assertEqual(canonicalize_control_flow(test), canonicalize_control_flow(expected)) @@ -2351,10 +2530,12 @@ def test_access_of_clbit_from_c_if(self): with self.subTest("if"): test = QuantumCircuit(bits) with test.if_test(cond): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.if_test(cond, body, [qubits[0]], clbits) @@ -2363,41 +2544,49 @@ def test_access_of_clbit_from_c_if(self): with test.if_test(cond) as else_: pass with else_: - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) true_body = QuantumCircuit([qubits[0]], clbits) false_body = QuantumCircuit([qubits[0]], clbits) - false_body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + false_body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.if_else(cond, true_body, false_body, [qubits[0]], clbits) with self.subTest("for"): test = QuantumCircuit(bits) with test.for_loop(range(2)): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.for_loop(range(2), None, body, [qubits[0]], clbits) with self.subTest("while"): test = QuantumCircuit(bits) with test.while_loop(cond): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.while_loop(cond, body, [qubits[0]], clbits) with self.subTest("switch"): test = QuantumCircuit(bits) with test.switch(cond[0]) as case, case(False): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) expected = QuantumCircuit(bits) expected.switch(cond[0], [(False, body)], [qubits[0]], clbits) @@ -2405,10 +2594,12 @@ def test_access_of_clbit_from_c_if(self): test = QuantumCircuit(bits) with test.for_loop(range(2)): with test.if_test(cond): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) true_body = QuantumCircuit([qubits[0]], clbits) - true_body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + true_body.h(qubits[0]).c_if(clbits[1], 0) body = QuantumCircuit([qubits[0]], clbits) body.if_test(cond, body, [qubits[0]], clbits) expected = QuantumCircuit(bits) @@ -2417,10 +2608,12 @@ def test_access_of_clbit_from_c_if(self): with self.subTest("switch inside for"): test = QuantumCircuit(bits) with test.for_loop(range(2)), test.switch(cond[0]) as case, case(False): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits) - body.h(qubits[0]).c_if(clbits[1], 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(clbits[1], 0) body = QuantumCircuit([qubits[0]], clbits) body.switch(cond[0], [(False, body)], [qubits[0]], clbits) expected = QuantumCircuit(bits) @@ -2438,10 +2631,12 @@ def test_access_of_classicalregister_from_c_if(self): with self.subTest("if"): test = QuantumCircuit(qubits, clbits, creg) with test.if_test(cond): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) - body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.if_test(cond, body, [qubits[0]], all_clbits) @@ -2450,41 +2645,49 @@ def test_access_of_classicalregister_from_c_if(self): with test.if_test(cond) as else_: pass with else_: - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) true_body = QuantumCircuit([qubits[0]], clbits, creg) false_body = QuantumCircuit([qubits[0]], clbits, creg) - false_body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + false_body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.if_else(cond, true_body, false_body, [qubits[0]], all_clbits) with self.subTest("for"): test = QuantumCircuit(qubits, clbits, creg) with test.for_loop(range(2)): - test.h(0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(1, 0) body = QuantumCircuit([qubits[0]], clbits, creg) - body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.for_loop(range(2), None, body, [qubits[0]], all_clbits) with self.subTest("while"): test = QuantumCircuit(qubits, clbits, creg) with test.while_loop(cond): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) - body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.while_loop(cond, body, [qubits[0]], all_clbits) with self.subTest("switch"): test = QuantumCircuit(qubits, clbits, creg) with test.switch(cond[0]) as case, case(False): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) - body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + body.h(qubits[0]).c_if(creg, 0) expected = QuantumCircuit(qubits, clbits, creg) expected.switch(cond[0], [(False, body)], [qubits[0]], all_clbits) @@ -2492,10 +2695,12 @@ def test_access_of_classicalregister_from_c_if(self): test = QuantumCircuit(qubits, clbits, creg) with test.for_loop(range(2)): with test.if_test(cond): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) true_body = QuantumCircuit([qubits[0]], clbits, creg) - true_body.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + true_body.h(qubits[0]).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) body.if_test(cond, body, [qubits[0]], all_clbits) expected = QuantumCircuit(qubits, clbits, creg) @@ -2505,10 +2710,12 @@ def test_access_of_classicalregister_from_c_if(self): test = QuantumCircuit(qubits, clbits, creg) with test.for_loop(range(2)): with test.switch(cond[0]) as case, case(False): - test.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + test.h(0).c_if(creg, 0) case = QuantumCircuit([qubits[0]], clbits, creg) - case.h(qubits[0]).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + case.h(qubits[0]).c_if(creg, 0) body = QuantumCircuit([qubits[0]], clbits, creg) body.switch(cond[0], [(False, case)], [qubits[0]], all_clbits) expected = QuantumCircuit(qubits, clbits, creg) @@ -3424,7 +3631,8 @@ def test_if_placeholder_rejects_c_if(self): NotImplementedError, r"IfElseOp cannot be classically controlled through Instruction\.c_if", ): - placeholder.c_if(bits[1], 0) + with self.assertWarns(DeprecationWarning): + placeholder.c_if(bits[1], 0) with self.subTest("else"): test = QuantumCircuit(bits) @@ -3441,7 +3649,8 @@ def test_if_placeholder_rejects_c_if(self): NotImplementedError, r"IfElseOp cannot be classically controlled through Instruction\.c_if", ): - placeholder.c_if(bits[1], 0) + with self.assertWarns(DeprecationWarning): + placeholder.c_if(bits[1], 0) def test_switch_rejects_operations_outside_cases(self): """It shouldn't be permissible to try and put instructions inside a switch but outside a @@ -3543,7 +3752,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("else"): test = QuantumCircuit(bits) @@ -3554,7 +3764,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("for"): test = QuantumCircuit(bits) @@ -3563,7 +3774,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("while"): test = QuantumCircuit(bits) @@ -3572,7 +3784,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("switch"): test = QuantumCircuit(bits) @@ -3581,7 +3794,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("if inside for"): # As a side-effect of how the lazy building of 'if' statements works, we actually @@ -3595,7 +3809,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) with self.subTest("switch inside for"): # `switch` has the same lazy building as `if`, so is subject to the same considerations @@ -3608,7 +3823,8 @@ def test_reject_c_if_from_outside_scope(self): with self.assertRaisesRegex( CircuitError, r"Cannot add resources after the scope has been built\." ): - instructions.c_if(*cond) + with self.assertWarns(DeprecationWarning): + instructions.c_if(*cond) def test_raising_inside_context_manager_leave_circuit_usable(self): """Test that if we leave a builder by raising some sort of exception, the circuit is left in diff --git a/test/python/circuit/test_extensions_standard.py b/test/python/circuit/test_extensions_standard.py index 73e00fa84c90..d9bdbc3fd13f 100644 --- a/test/python/circuit/test_extensions_standard.py +++ b/test/python/circuit/test_extensions_standard.py @@ -76,7 +76,8 @@ def test_barrier_invalid(self): def test_conditional_barrier_invalid(self): qc = self.circuit barrier = qc.barrier(self.qr) - self.assertRaises(QiskitError, barrier.c_if, self.cr, 0) + with self.assertWarns(DeprecationWarning): + self.assertRaises(QiskitError, barrier.c_if, self.cr, 0) def test_barrier_reg(self): self.circuit.barrier(self.qr) @@ -131,16 +132,20 @@ def test_ch_invalid(self): self.assertRaises(CircuitError, qc.ch, "a", self.qr[1]) def test_cif_reg(self): - self.circuit.h(self.qr[0]).c_if(self.cr, 7) + with self.assertWarns(DeprecationWarning): + self.circuit.h(self.qr[0]).c_if(self.cr, 7) self.assertEqual(self.circuit[0].operation.name, "h") self.assertEqual(self.circuit[0].qubits, (self.qr[0],)) - self.assertEqual(self.circuit[0].operation.condition, (self.cr, 7)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.circuit[0].operation.condition, (self.cr, 7)) def test_cif_single_bit(self): - self.circuit.h(self.qr[0]).c_if(self.cr[0], True) + with self.assertWarns(DeprecationWarning): + self.circuit.h(self.qr[0]).c_if(self.cr[0], True) self.assertEqual(self.circuit[0].operation.name, "h") self.assertEqual(self.circuit[0].qubits, (self.qr[0],)) - self.assertEqual(self.circuit[0].operation.condition, (self.cr[0], True)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.circuit[0].operation.condition, (self.cr[0], True)) def test_crz(self): self.circuit.crz(1, self.qr[0], self.qr[1]) diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py index ba2546b2e8a5..976c66d266ad 100644 --- a/test/python/circuit/test_gate_definitions.py +++ b/test/python/circuit/test_gate_definitions.py @@ -311,6 +311,7 @@ class TestGateEquivalenceEqual(QiskitTestCase): "_SingletonGateOverrides", "_SingletonControlledGateOverrides", "QFTGate", + "GraphStateGate", "AndGate", "OrGate", "BitwiseXorGate", diff --git a/test/python/circuit/test_instruction_repeat.py b/test/python/circuit/test_instruction_repeat.py index 778e6aad64c2..b2fcda1522e6 100644 --- a/test/python/circuit/test_instruction_repeat.py +++ b/test/python/circuit/test_instruction_repeat.py @@ -55,8 +55,10 @@ def test_standard_1Q_one(self): def test_conditional(self): """Test that repetition works with a condition.""" cr = ClassicalRegister(3, "cr") - gate = SGate().c_if(cr, 7).repeat(5) - self.assertEqual(gate.condition, (cr, 7)) + with self.assertWarns(DeprecationWarning): + gate = SGate().c_if(cr, 7).repeat(5) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate.condition, (cr, 7)) defn = QuantumCircuit(1) for _ in range(5): @@ -98,8 +100,10 @@ def test_standard_2Q_one(self): def test_conditional(self): """Test that repetition works with a condition.""" cr = ClassicalRegister(3, "cr") - gate = CXGate().c_if(cr, 7).repeat(5) - self.assertEqual(gate.condition, (cr, 7)) + with self.assertWarns(DeprecationWarning): + gate = CXGate().c_if(cr, 7).repeat(5) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate.condition, (cr, 7)) defn = QuantumCircuit(2) for _ in range(5): @@ -145,8 +149,10 @@ def test_measure_one(self): def test_measure_conditional(self): """Test conditional measure moves condition to the outside.""" cr = ClassicalRegister(3, "cr") - measure = Measure().c_if(cr, 7).repeat(5) - self.assertEqual(measure.condition, (cr, 7)) + with self.assertWarns(DeprecationWarning): + measure = Measure().c_if(cr, 7).repeat(5) + with self.assertWarns(DeprecationWarning): + self.assertEqual(measure.condition, (cr, 7)) defn = QuantumCircuit(1, 1) for _ in range(5): diff --git a/test/python/circuit/test_instructions.py b/test/python/circuit/test_instructions.py index 170b47632c4d..da1d0797870e 100644 --- a/test/python/circuit/test_instructions.py +++ b/test/python/circuit/test_instructions.py @@ -176,7 +176,8 @@ def circuit_instruction_circuit_roundtrip(self): circ1.u(0.1, 0.2, -0.2, q[0]) circ1.barrier() circ1.measure(q, c) - circ1.rz(0.8, q[0]).c_if(c, 6) + with self.assertWarns(DeprecationWarning): + circ1.rz(0.8, q[0]).c_if(c, 6) inst = circ1.to_instruction() circ2 = QuantumCircuit(q, c, name="circ2") @@ -238,16 +239,20 @@ def test_reverse_instruction(self): circ.u(0.1, 0.2, -0.2, q[0]) circ.barrier() circ.measure(q[0], c[0]) - circ.rz(0.8, q[0]).c_if(c, 6) - inst = circ.to_instruction() + with self.assertWarns(DeprecationWarning): + circ.rz(0.8, q[0]).c_if(c, 6) + with self.assertWarns(DeprecationWarning): + inst = circ.to_instruction() circ = QuantumCircuit(q, c, name="circ") - circ.rz(0.8, q[0]).c_if(c, 6) + with self.assertWarns(DeprecationWarning): + circ.rz(0.8, q[0]).c_if(c, 6) circ.measure(q[0], c[0]) circ.barrier() circ.u(0.1, 0.2, -0.2, q[0]) circ.t(q[1]) - inst_reverse = circ.to_instruction() + with self.assertWarns(DeprecationWarning): + inst_reverse = circ.to_instruction() self.assertEqual(inst.reverse_ops().definition, inst_reverse.definition) @@ -336,8 +341,10 @@ def test_inverse_instruction_with_conditional(self): circ.u(0.1, 0.2, -0.2, q[0]) circ.barrier() circ.measure(q[0], c[0]) - circ.rz(0.8, q[0]).c_if(c, 6) - inst = circ.to_instruction() + with self.assertWarns(DeprecationWarning): + circ.rz(0.8, q[0]).c_if(c, 6) + with self.assertWarns(DeprecationWarning): + inst = circ.to_instruction() self.assertRaises(CircuitError, inst.inverse) def test_inverse_opaque(self): @@ -446,13 +453,18 @@ def key(bit): return body.find_bit(bit).index op = IfElseOp((bits[0], False), body) - self.assertEqual(op.condition_bits, [bits[0]]) + with self.assertWarns(DeprecationWarning): + self.assertEqual(op.condition_bits, [bits[0]]) op = IfElseOp((cr1, 3), body) - self.assertEqual(op.condition_bits, list(cr1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(op.condition_bits, list(cr1)) op = IfElseOp(expr.logic_and(bits[1], expr.equal(cr2, 3)), body) - self.assertEqual(sorted(op.condition_bits, key=key), sorted([bits[1]] + list(cr2), key=key)) + with self.assertWarns(DeprecationWarning): + self.assertEqual( + sorted(op.condition_bits, key=key), sorted([bits[1]] + list(cr2), key=key) + ) def test_instructionset_c_if_direct_resource(self): """Test that using :meth:`.InstructionSet.c_if` with an exact classical resource always @@ -467,8 +479,10 @@ def test_instructionset_c_if_direct_resource(self): def case(resource): qc = QuantumCircuit(cr1, qubits, loose_clbits, cr2, cr3) - qc.x(0).c_if(resource, 0) - c_if_resource = qc.data[0].operation.condition[0] + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(resource, 0) + with self.assertWarns(DeprecationWarning): + c_if_resource = qc.data[0].operation.condition[0] self.assertIs(c_if_resource, resource) with self.subTest("classical register"): @@ -500,9 +514,11 @@ def test_instructionset_c_if_indexing(self): qc = QuantumCircuit(cr1, qubits, loose_clbits, cr2, cr3) for index, clbit in enumerate(qc.clbits): with self.subTest(index=index): - qc.x(0).c_if(index, 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(index, 0) qc.measure(0, index) - from_c_if = qc.data[-2].operation.condition[0] + with self.assertWarns(DeprecationWarning): + from_c_if = qc.data[-2].operation.condition[0] from_measure = qc.data[-1].clbits[0] self.assertIs(from_c_if, from_measure) # Sanity check that the bit is also the one we expected. @@ -516,14 +532,20 @@ def test_instructionset_c_if_size_1_classical_register(self): qc = QuantumCircuit(qr, cr) with self.subTest("classical register"): - qc.x(0).c_if(cr, 0) - self.assertIs(qc.data[-1].operation.condition[0], cr) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], cr) with self.subTest("classical bit by value"): - qc.x(0).c_if(cr[0], 0) - self.assertIs(qc.data[-1].operation.condition[0], cr[0]) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(cr[0], 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], cr[0]) with self.subTest("classical bit by index"): - qc.x(0).c_if(0, 0) - self.assertIs(qc.data[-1].operation.condition[0], cr[0]) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], cr[0]) def test_instructionset_c_if_no_classical_registers(self): """Test that using :meth:`.InstructionSet.c_if` works if there are no classical registers @@ -533,11 +555,15 @@ def test_instructionset_c_if_no_classical_registers(self): bits = [Qubit(), Clbit()] qc = QuantumCircuit(bits) with self.subTest("by value"): - qc.x(0).c_if(bits[1], 0) - self.assertIs(qc.data[-1].operation.condition[0], bits[1]) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(bits[1], 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], bits[1]) with self.subTest("by index"): - qc.x(0).c_if(0, 0) - self.assertIs(qc.data[-1].operation.condition[0], bits[1]) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(qc.data[-1].operation.condition[0], bits[1]) def test_instructionset_c_if_rejects_invalid_specifiers(self): """Test that calling the :meth:`.InstructionSet.c_if` method on instructions added to a @@ -550,7 +576,8 @@ def case(specifier, message): qc = QuantumCircuit(qreg, creg) instruction = qc.x(0) with self.assertRaisesRegex(CircuitError, message): - instruction.c_if(specifier, 0) + with self.assertWarns(DeprecationWarning): + instruction.c_if(specifier, 0) with self.subTest("absent bit"): case(Clbit(), r"Clbit .* is not present in this circuit\.") @@ -574,21 +601,26 @@ def test_instructionset_c_if_with_no_requester(self): instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) register = ClassicalRegister(2) - instructions.c_if(register, 0) - self.assertIs(instructions[0].operation.condition[0], register) + with self.assertWarns(DeprecationWarning): + instructions.c_if(register, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], register) with self.subTest("accepts arbitrary bit"): instruction = RZGate(0) instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) bit = Clbit() - instructions.c_if(bit, 0) - self.assertIs(instructions[0].operation.condition[0], bit) + with self.assertWarns(DeprecationWarning): + instructions.c_if(bit, 0) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], bit) with self.subTest("rejects index"): instruction = RZGate(0) instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) with self.assertRaisesRegex(CircuitError, r"Cannot pass an index as a condition .*"): - instructions.c_if(0, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(0, 0) def test_instructionset_c_if_calls_custom_requester(self): """Test that :meth:`.InstructionSet.c_if` calls a custom requester, and uses its output.""" @@ -613,27 +645,33 @@ def dummy_requester(specifier): instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) bit = Clbit() - instructions.c_if(bit, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(bit, 0) dummy_requester.assert_called_once_with(bit) - self.assertIs(instructions[0].operation.condition[0], sentinel_bit) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], sentinel_bit) with self.subTest("calls requester with index"): dummy_requester.reset_mock() instruction = RZGate(0) instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) index = 0 - instructions.c_if(index, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(index, 0) dummy_requester.assert_called_once_with(index) - self.assertIs(instructions[0].operation.condition[0], sentinel_bit) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], sentinel_bit) with self.subTest("calls requester with register"): dummy_requester.reset_mock() instruction = RZGate(0) instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) register = ClassicalRegister(2) - instructions.c_if(register, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(register, 0) dummy_requester.assert_called_once_with(register) - self.assertIs(instructions[0].operation.condition[0], sentinel_register) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], sentinel_register) with self.subTest("calls requester only once when broadcast"): dummy_requester.reset_mock() instruction_list = [RZGate(0), RZGate(0), RZGate(0)] @@ -641,10 +679,12 @@ def dummy_requester(specifier): for instruction in instruction_list: instructions.add(instruction, [Qubit()], []) register = ClassicalRegister(2) - instructions.c_if(register, 0) + with self.assertWarns(DeprecationWarning): + instructions.c_if(register, 0) dummy_requester.assert_called_once_with(register) for instruction in instruction_list: - self.assertIs(instructions[0].operation.condition[0], sentinel_register) + with self.assertWarns(DeprecationWarning): + self.assertIs(instructions[0].operation.condition[0], sentinel_register) def test_label_type_enforcement(self): """Test instruction label type enforcement.""" diff --git a/test/python/circuit/test_random_circuit.py b/test/python/circuit/test_random_circuit.py index ebbdfd28d648..0845fdbe97d6 100644 --- a/test/python/circuit/test_random_circuit.py +++ b/test/python/circuit/test_random_circuit.py @@ -49,18 +49,20 @@ def test_random_circuit_conditional_reset(self): """Test generating random circuits with conditional and reset.""" num_qubits = 1 depth = 100 - circ = random_circuit(num_qubits, depth, conditional=True, reset=True, seed=5) + with self.assertWarns(DeprecationWarning): + circ = random_circuit(num_qubits, depth, conditional=True, reset=True, seed=5) self.assertEqual(circ.width(), 2 * num_qubits) self.assertIn("reset", circ.count_ops()) def test_large_conditional(self): """Test that conditions do not fail with large conditionals. Regression test of gh-6994.""" # The main test is that this call actually returns without raising an exception. - circ = random_circuit(64, 2, conditional=True, seed=0) + with self.assertWarns(DeprecationWarning): + circ = random_circuit(64, 2, conditional=True, seed=0) # Test that at least one instruction had a condition generated. It's possible that this # fails due to very bad luck with the random seed - if so, change the seed to ensure that a # condition _is_ generated, because we need to test that generation doesn't error. - conditions = (getattr(instruction.operation, "condition", None) for instruction in circ) + conditions = (getattr(instruction.operation, "_condition", None) for instruction in circ) conditions = [x for x in conditions if x is not None] self.assertNotEqual(conditions, []) for register, value in conditions: @@ -72,14 +74,15 @@ def test_large_conditional(self): def test_random_mid_circuit_measure_conditional(self): """Test random circuit with mid-circuit measurements for conditionals.""" num_qubits = depth = 2 - circ = random_circuit(num_qubits, depth, conditional=True, seed=16) + with self.assertWarns(DeprecationWarning): + circ = random_circuit(num_qubits, depth, conditional=True, seed=16) self.assertEqual(circ.width(), 2 * num_qubits) op_names = [instruction.operation.name for instruction in circ] # Before a condition, there needs to be measurement in all the qubits. self.assertEqual(4, len(op_names)) self.assertEqual(["measure"] * num_qubits, op_names[1 : 1 + num_qubits]) conditions = [ - bool(getattr(instruction.operation, "condition", None)) for instruction in circ + bool(getattr(instruction.operation, "_condition", None)) for instruction in circ ] self.assertEqual([False, False, False, True], conditions) diff --git a/test/python/circuit/test_registerless_circuit.py b/test/python/circuit/test_registerless_circuit.py index 3f77919957da..c3da073e5234 100644 --- a/test/python/circuit/test_registerless_circuit.py +++ b/test/python/circuit/test_registerless_circuit.py @@ -195,10 +195,12 @@ def test_circuit_conditional(self): qreg = QuantumRegister(2) creg = ClassicalRegister(4) circuit = QuantumCircuit(qreg, creg) - circuit.h(0).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(creg, 3) expected = QuantumCircuit(qreg, creg) - expected.h(qreg[0]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg[0]).c_if(creg, 3) self.assertEqual(circuit, expected) @@ -333,11 +335,14 @@ def test_circuit_conditional(self): qreg1 = QuantumRegister(2) creg = ClassicalRegister(2) circuit = QuantumCircuit(qreg0, qreg1, creg) - circuit.h(range(1, 3)).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit.h(range(1, 3)).c_if(creg, 3) expected = QuantumCircuit(qreg0, qreg1, creg) - expected.h(qreg0[1]).c_if(creg, 3) - expected.h(qreg1[0]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg0[1]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg1[0]).c_if(creg, 3) self.assertEqual(circuit, expected) @@ -466,11 +471,14 @@ def test_circuit_conditional(self): qreg1 = QuantumRegister(2) creg = ClassicalRegister(2) circuit = QuantumCircuit(qreg0, qreg1, creg) - circuit.h(slice(1, 3)).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + circuit.h(slice(1, 3)).c_if(creg, 3) expected = QuantumCircuit(qreg0, qreg1, creg) - expected.h(qreg0[1]).c_if(creg, 3) - expected.h(qreg1[0]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg0[1]).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + expected.h(qreg1[0]).c_if(creg, 3) self.assertEqual(circuit, expected) @@ -504,10 +512,12 @@ def test_bit_conditional_single_gate(self): qreg = QuantumRegister(1) creg = ClassicalRegister(2) circuit = QuantumCircuit(qreg, creg) - circuit.h(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(0, True) expected = QuantumCircuit(qreg, creg) - expected.h(qreg[0]).c_if(creg[0], True) + with self.assertWarns(DeprecationWarning): + expected.h(qreg[0]).c_if(creg[0], True) self.assertEqual(circuit, expected) def test_bit_conditional_multiple_gates(self): @@ -516,12 +526,18 @@ def test_bit_conditional_multiple_gates(self): creg = ClassicalRegister(2) creg1 = ClassicalRegister(1) circuit = QuantumCircuit(qreg, creg, creg1) - circuit.h(0).c_if(0, True) - circuit.h(1).c_if(1, False) - circuit.cx(1, 0).c_if(2, True) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + circuit.h(1).c_if(1, False) + with self.assertWarns(DeprecationWarning): + circuit.cx(1, 0).c_if(2, True) expected = QuantumCircuit(qreg, creg, creg1) - expected.h(qreg[0]).c_if(creg[0], True) - expected.h(qreg[1]).c_if(creg[1], False) - expected.cx(qreg[1], qreg[0]).c_if(creg1[0], True) + with self.assertWarns(DeprecationWarning): + expected.h(qreg[0]).c_if(creg[0], True) + with self.assertWarns(DeprecationWarning): + expected.h(qreg[1]).c_if(creg[1], False) + with self.assertWarns(DeprecationWarning): + expected.cx(qreg[1], qreg[0]).c_if(creg1[0], True) self.assertEqual(circuit, expected) diff --git a/test/python/circuit/test_singleton.py b/test/python/circuit/test_singleton.py index 40549dd0ad15..0274242eec8e 100644 --- a/test/python/circuit/test_singleton.py +++ b/test/python/circuit/test_singleton.py @@ -63,7 +63,8 @@ def test_label_not_singleton(self): def test_condition_not_singleton(self): gate = HGate() - condition_gate = HGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + condition_gate = HGate().c_if(Clbit(), 0) self.assertIsNot(gate, condition_gate) def test_raise_on_state_mutation(self): @@ -76,10 +77,12 @@ def test_raise_on_state_mutation(self): def test_labeled_condition(self): singleton_gate = HGate() clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = HGate(label="conditionally special").c_if(clbit, 0) self.assertIsNot(singleton_gate, gate) self.assertEqual(gate.label, "conditionally special") - self.assertEqual(gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate.condition, (clbit, 0)) def test_default_singleton_copy(self): gate = HGate() @@ -109,19 +112,22 @@ def test_label_copy_new(self): self.assertEqual(copied_label.label, "special") def test_condition_copy(self): - gate = HGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + gate = HGate().c_if(Clbit(), 0) copied = gate.copy() self.assertIsNot(gate, copied) self.assertEqual(gate, copied) def test_condition_label_copy(self): clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = HGate(label="conditionally special").c_if(clbit, 0) copied = gate.copy() self.assertIsNot(gate, copied) self.assertEqual(gate, copied) self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied.condition, (clbit, 0)) def test_deepcopy(self): gate = HGate() @@ -136,19 +142,22 @@ def test_deepcopy_with_label(self): self.assertEqual(copied.label, "special") def test_deepcopy_with_condition(self): - gate = HGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + gate = HGate().c_if(Clbit(), 0) copied = copy.deepcopy(gate) self.assertIsNot(gate, copied) self.assertEqual(gate, copied) def test_condition_label_deepcopy(self): clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = HGate(label="conditionally special").c_if(clbit, 0) copied = copy.deepcopy(gate) self.assertIsNot(gate, copied) self.assertEqual(gate, copied) self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied.condition, (clbit, 0)) def test_label_deepcopy_new(self): gate = HGate() @@ -193,23 +202,27 @@ def test_round_trip_dag_conversion_with_label(self): def test_round_trip_dag_conversion_with_condition(self): qc = QuantumCircuit(1, 1) - gate = HGate().c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + gate = HGate().c_if(qc.cregs[0], 0) qc.append(gate, [0]) dag = circuit_to_dag(qc) out = dag_to_circuit(dag) self.assertIsNot(qc.data[0].operation, out.data[0].operation) self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) def test_round_trip_dag_conversion_condition_label(self): qc = QuantumCircuit(1, 1) - gate = HGate(label="conditionally special").c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + gate = HGate(label="conditionally special").c_if(qc.cregs[0], 0) qc.append(gate, [0]) dag = circuit_to_dag(qc) out = dag_to_circuit(dag) self.assertIsNot(qc.data[0].operation, out.data[0].operation) self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) self.assertEqual(out.data[0].operation.label, "conditionally special") def test_condition_via_instructionset(self): @@ -217,9 +230,11 @@ def test_condition_via_instructionset(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 1) self.assertIsNot(gate, circuit.data[0].operation) - self.assertEqual(circuit.data[0].operation.condition, (cr, 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(circuit.data[0].operation.condition, (cr, 1)) def test_is_mutable(self): gate = HGate() @@ -249,7 +264,8 @@ def test_to_mutable_setter(self): self.assertEqual(mutable_gate.label, "foo") self.assertEqual(mutable_gate.duration, 3) self.assertEqual(mutable_gate.unit, "s") - self.assertEqual(mutable_gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(mutable_gate.condition, (clbit, 0)) def test_to_mutable_of_mutable_instance(self): gate = HGate(label="foo") @@ -287,9 +303,11 @@ def test_immutable_pickle(self): def test_mutable_pickle(self): gate = SXGate() clbit = Clbit() - condition_gate = gate.c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + condition_gate = gate.c_if(clbit, 0) self.assertIsNot(gate, condition_gate) - self.assertEqual(condition_gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(condition_gate.condition, (clbit, 0)) self.assertTrue(condition_gate.mutable) with io.BytesIO() as fd: pickle.dump(condition_gate, fd) @@ -505,7 +523,8 @@ def test_label_not_singleton(self): def test_condition_not_singleton(self): gate = CZGate() - condition_gate = CZGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + condition_gate = CZGate().c_if(Clbit(), 0) self.assertIsNot(gate, condition_gate) def test_raise_on_state_mutation(self): @@ -518,10 +537,12 @@ def test_raise_on_state_mutation(self): def test_labeled_condition(self): singleton_gate = CSwapGate() clbit = Clbit() - gate = CSwapGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = CSwapGate(label="conditionally special").c_if(clbit, 0) self.assertIsNot(singleton_gate, gate) self.assertEqual(gate.label, "conditionally special") - self.assertEqual(gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate.condition, (clbit, 0)) def test_default_singleton_copy(self): gate = CXGate() @@ -551,19 +572,22 @@ def test_label_copy_new(self): self.assertEqual(copied_label.label, "special") def test_condition_copy(self): - gate = CZGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + gate = CZGate().c_if(Clbit(), 0) copied = gate.copy() self.assertIsNot(gate, copied) self.assertEqual(gate, copied) def test_condition_label_copy(self): clbit = Clbit() - gate = CZGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = CZGate(label="conditionally special").c_if(clbit, 0) copied = gate.copy() self.assertIsNot(gate, copied) self.assertEqual(gate, copied) self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied.condition, (clbit, 0)) def test_deepcopy(self): gate = CXGate() @@ -583,19 +607,22 @@ def test_deepcopy_with_label(self): self.assertNotEqual(singleton_gate.label, copied.label) def test_deepcopy_with_condition(self): - gate = CCXGate().c_if(Clbit(), 0) + with self.assertWarns(DeprecationWarning): + gate = CCXGate().c_if(Clbit(), 0) copied = copy.deepcopy(gate) self.assertIsNot(gate, copied) self.assertEqual(gate, copied) def test_condition_label_deepcopy(self): clbit = Clbit() - gate = CHGate(label="conditionally special").c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + gate = CHGate(label="conditionally special").c_if(clbit, 0) copied = copy.deepcopy(gate) self.assertIsNot(gate, copied) self.assertEqual(gate, copied) self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied.condition, (clbit, 0)) def test_label_deepcopy_new(self): gate = CHGate() @@ -640,23 +667,27 @@ def test_round_trip_dag_conversion_with_label(self): def test_round_trip_dag_conversion_with_condition(self): qc = QuantumCircuit(2, 1) - gate = CHGate().c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + gate = CHGate().c_if(qc.cregs[0], 0) qc.append(gate, [0, 1]) dag = circuit_to_dag(qc) out = dag_to_circuit(dag) self.assertIsNot(qc.data[0].operation, out.data[0].operation) self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) def test_round_trip_dag_conversion_condition_label(self): qc = QuantumCircuit(2, 1) - gate = CHGate(label="conditionally special").c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + gate = CHGate(label="conditionally special").c_if(qc.cregs[0], 0) qc.append(gate, [0, 1]) dag = circuit_to_dag(qc) out = dag_to_circuit(dag) self.assertIsNot(qc.data[0].operation, out.data[0].operation) self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) self.assertEqual(out.data[0].operation.label, "conditionally special") def test_condition_via_instructionset(self): @@ -664,9 +695,10 @@ def test_condition_via_instructionset(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 1) self.assertIsNot(gate, circuit.data[0].operation) - self.assertEqual(circuit.data[0].operation.condition, (cr, 1)) + self.assertEqual(circuit.data[0].operation._condition, (cr, 1)) def test_is_mutable(self): gate = CXGate() @@ -696,7 +728,8 @@ def test_to_mutable_setter(self): self.assertEqual(mutable_gate.label, "foo") self.assertEqual(mutable_gate.duration, 3) self.assertEqual(mutable_gate.unit, "s") - self.assertEqual(mutable_gate.condition, (clbit, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(mutable_gate.condition, (clbit, 0)) def test_to_mutable_of_mutable_instance(self): gate = CZGate(label="foo") @@ -723,27 +756,32 @@ def test_inner_outer_label_with_c_if(self): inner_gate = HGate(label="my h gate") controlled_gate = inner_gate.control(label="foo") clbit = Clbit() - conditonal_controlled_gate = controlled_gate.c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + conditonal_controlled_gate = controlled_gate.c_if(clbit, 0) self.assertTrue(conditonal_controlled_gate.mutable) self.assertEqual("my h gate", conditonal_controlled_gate.base_gate.label) self.assertEqual("foo", conditonal_controlled_gate.label) - self.assertEqual((clbit, 0), conditonal_controlled_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual((clbit, 0), conditonal_controlled_gate.condition) def test_inner_outer_label_with_c_if_deepcopy(self): inner_gate = XGate(label="my h gate") controlled_gate = inner_gate.control(label="foo") clbit = Clbit() - conditonal_controlled_gate = controlled_gate.c_if(clbit, 0) + with self.assertWarns(DeprecationWarning): + conditonal_controlled_gate = controlled_gate.c_if(clbit, 0) self.assertTrue(conditonal_controlled_gate.mutable) self.assertEqual("my h gate", conditonal_controlled_gate.base_gate.label) self.assertEqual("foo", conditonal_controlled_gate.label) - self.assertEqual((clbit, 0), conditonal_controlled_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual((clbit, 0), conditonal_controlled_gate.condition) copied = copy.deepcopy(conditonal_controlled_gate) self.assertIsNot(conditonal_controlled_gate, copied) self.assertTrue(copied.mutable) self.assertEqual("my h gate", copied.base_gate.label) self.assertEqual("foo", copied.label) - self.assertEqual((clbit, 0), copied.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual((clbit, 0), copied.condition) def test_inner_outer_label_pickle(self): inner_gate = XGate(label="my h gate") diff --git a/test/python/circuit/test_store.py b/test/python/circuit/test_store.py index 139192745d2e..ecb98681bbd2 100644 --- a/test/python/circuit/test_store.py +++ b/test/python/circuit/test_store.py @@ -73,7 +73,8 @@ def test_rejects_dangerous_cast(self): def test_rejects_c_if(self): instruction = Store(expr.Var.new("a", types.Bool()), expr.Var.new("b", types.Bool())) with self.assertRaises(NotImplementedError): - instruction.c_if(Clbit(), False) + with self.assertWarns(DeprecationWarning): + instruction.c_if(Clbit(), False) class TestStoreCircuit(QiskitTestCase): @@ -241,4 +242,5 @@ def test_rejects_c_if(self): qc = QuantumCircuit([Clbit()], inputs=[a]) instruction_set = qc.store(a, True) with self.assertRaises(NotImplementedError): - instruction_set.c_if(qc.clbits[0], False) + with self.assertWarns(DeprecationWarning): + instruction_set.c_if(qc.clbits[0], False) diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py index b384d8b9267f..6a954d871cc9 100644 --- a/test/python/compiler/test_assembler.py +++ b/test/python/compiler/test_assembler.py @@ -285,7 +285,8 @@ def test_measure_to_registers_when_conditionals(self): qc.measure(qr[0], cr1) # Measure not required for a later conditional qc.measure(qr[1], cr2[1]) # Measure required for a later conditional - qc.h(qr[1]).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + qc.h(qr[1]).c_if(cr2, 3) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) @@ -305,7 +306,8 @@ def test_convert_to_bfunc_plus_conditional(self): cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr, 1) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) @@ -326,7 +328,8 @@ def test_convert_to_bfunc_plus_conditional_onebit(self): cr = ClassicalRegister(3) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr[2], 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr[2], 1) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) @@ -352,7 +355,8 @@ def test_resize_value_to_register(self): cr3 = ClassicalRegister(1) qc = QuantumCircuit(qr, cr1, cr2, cr3) - qc.h(qr[0]).c_if(cr2, 2) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr2, 2) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) diff --git a/test/python/compiler/test_disassembler.py b/test/python/compiler/test_disassembler.py index 805fac1178a1..4556550422b2 100644 --- a/test/python/compiler/test_disassembler.py +++ b/test/python/compiler/test_disassembler.py @@ -191,7 +191,8 @@ def test_circuit_with_conditionals(self): qc = QuantumCircuit(qr, cr1, cr2) qc.measure(qr[0], cr1) # Measure not required for a later conditional qc.measure(qr[1], cr2[1]) # Measure required for a later conditional - qc.h(qr[1]).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + qc.h(qr[1]).c_if(cr2, 3) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) @@ -207,7 +208,8 @@ def test_circuit_with_simple_conditional(self): qr = QuantumRegister(1) cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr, 1) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) @@ -228,7 +230,8 @@ def test_circuit_with_single_bit_conditions(self): qr = QuantumRegister(1) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr[0], 1) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) @@ -267,9 +270,12 @@ def test_multiple_conditionals_multiple_registers(self): qc = QuantumCircuit(qr, cr1, cr2, cr3, cr4) qc.x(qr[1]) qc.h(qr) - qc.cx(qr[1], qr[0]).c_if(cr3, 14) - qc.ccx(qr[0], qr[2], qr[1]).c_if(cr4, 1) - qc.h(qr).c_if(cr1, 3) + with self.assertWarns(DeprecationWarning): + qc.cx(qr[1], qr[0]).c_if(cr3, 14) + with self.assertWarns(DeprecationWarning): + qc.ccx(qr[0], qr[2], qr[1]).c_if(cr4, 1) + with self.assertWarns(DeprecationWarning): + qc.h(qr).c_if(cr1, 3) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) @@ -285,7 +291,8 @@ def test_circuit_with_bit_conditional_1(self): qr = QuantumRegister(2) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) - qc.h(qr[0]).c_if(cr[1], True) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr[1], True) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) @@ -302,9 +309,12 @@ def test_circuit_with_bit_conditional_2(self): cr = ClassicalRegister(2) cr1 = ClassicalRegister(2) qc = QuantumCircuit(qr, cr, cr1) - qc.h(qr[0]).c_if(cr1[1], False) - qc.h(qr[1]).c_if(cr[0], True) - qc.cx(qr[0], qr[1]).c_if(cr1[0], False) + with self.assertWarns(DeprecationWarning): + qc.h(qr[0]).c_if(cr1[1], False) + with self.assertWarns(DeprecationWarning): + qc.h(qr[1]).c_if(cr[0], True) + with self.assertWarns(DeprecationWarning): + qc.cx(qr[0], qr[1]).c_if(cr1[0], False) with self.assertWarns(DeprecationWarning): qobj = assemble(qc) circuits, run_config_out, header = disassemble(qobj) diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 17239d338a2d..5241304a0db4 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -2268,7 +2268,8 @@ def _regular_circuit(self): base.append(CustomCX(), [3, 6]) base.append(CustomCX(), [5, 4]) base.append(CustomCX(), [5, 3]) - base.append(CustomCX(), [2, 4]).c_if(base.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + base.append(CustomCX(), [2, 4]).c_if(base.cregs[0], 3) base.ry(a, 4) base.measure(4, 2) return base @@ -2882,12 +2883,14 @@ def test_parallel_singleton_conditional_gate(self, opt_level): circ = QuantumCircuit(2, 1) circ.h(0) circ.measure(0, circ.clbits[0]) - circ.z(1).c_if(circ.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + circ.z(1).c_if(circ.clbits[0], 1) res = transpile( [circ, circ], backend, optimization_level=opt_level, seed_transpiler=123456769 ) self.assertTrue(res[0].data[-1].operation.mutable) - self.assertEqual(res[0].data[-1].operation.condition, (res[0].clbits[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(res[0].data[-1].operation.condition, (res[0].clbits[0], 1)) @data(0, 1, 2, 3) def test_backendv2_and_basis_gates(self, opt_level): @@ -3332,7 +3335,8 @@ def test_shared_classical_between_components_condition(self, opt_level): for i in range(18): qc.measure(i, creg[i]) - qc.ecr(20, 21).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.ecr(20, 21).c_if(creg, 0) tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=42) def _visit_block(circuit, qubit_mapping=None): @@ -3368,9 +3372,11 @@ def test_shared_classical_between_components_condition_large_to_small(self, opt_ qc.measure(24, creg[0]) qc.measure(23, creg[1]) # Component 1 - qc.h(0).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(creg, 0) for i in range(18): - qc.ecr(0, i + 1).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.ecr(0, i + 1).c_if(creg, 0) tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=123456789) def _visit_block(circuit, qubit_mapping=None): @@ -3442,9 +3448,11 @@ def test_shared_classical_between_components_condition_large_to_small_reverse_in qc.measure(0, creg[0]) qc.measure(1, creg[1]) # Component 1 - qc.h(24).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.h(24).c_if(creg, 0) for i in range(23, 5, -1): - qc.ecr(24, i).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.ecr(24, i).c_if(creg, 0) tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=2023) def _visit_block(circuit, qubit_mapping=None): @@ -3515,15 +3523,19 @@ def test_chained_data_dependency(self, opt_level): measure_op = Measure() qc.append(measure_op, [9], [creg[0]]) # Component 1 - qc.h(10).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.h(10).c_if(creg, 0) for i in range(11, 20): - qc.ecr(10, i).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.ecr(10, i).c_if(creg, 0) measure_op = Measure() qc.append(measure_op, [19], [creg[0]]) # Component 2 - qc.h(20).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.h(20).c_if(creg, 0) for i in range(21, 30): - qc.cz(20, i).c_if(creg, 0) + with self.assertWarns(DeprecationWarning): + qc.cz(20, i).c_if(creg, 0) measure_op = Measure() qc.append(measure_op, [29], [creg[0]]) tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=2023) diff --git a/test/python/converters/test_circuit_to_dag.py b/test/python/converters/test_circuit_to_dag.py index 852cb324aa79..06d3dec654e4 100644 --- a/test/python/converters/test_circuit_to_dag.py +++ b/test/python/converters/test_circuit_to_dag.py @@ -34,7 +34,8 @@ def test_circuit_and_dag(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/converters/test_circuit_to_dagdependency.py b/test/python/converters/test_circuit_to_dagdependency.py index 65221e9cf2c1..7d3bbfd2c49c 100644 --- a/test/python/converters/test_circuit_to_dagdependency.py +++ b/test/python/converters/test_circuit_to_dagdependency.py @@ -33,7 +33,8 @@ def test_circuit_and_dag_canonical(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) @@ -51,7 +52,8 @@ def test_circuit_and_dag_canonical2(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/converters/test_circuit_to_dagdependency_v2.py b/test/python/converters/test_circuit_to_dagdependency_v2.py index 3323ca0e6768..c7a31dc5767c 100644 --- a/test/python/converters/test_circuit_to_dagdependency_v2.py +++ b/test/python/converters/test_circuit_to_dagdependency_v2.py @@ -33,7 +33,8 @@ def test_circuit_and_dag_canonical(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/converters/test_circuit_to_instruction.py b/test/python/converters/test_circuit_to_instruction.py index e3239d4b5ff4..1b225831bb23 100644 --- a/test/python/converters/test_circuit_to_instruction.py +++ b/test/python/converters/test_circuit_to_instruction.py @@ -56,22 +56,28 @@ def test_flatten_registers_of_circuit_single_bit_cond(self): cr1 = ClassicalRegister(3, "cr1") cr2 = ClassicalRegister(3, "cr2") circ = QuantumCircuit(qr1, qr2, cr1, cr2) - circ.h(qr1[0]).c_if(cr1[1], True) - circ.h(qr2[1]).c_if(cr2[0], False) - circ.cx(qr1[1], qr2[2]).c_if(cr2[2], True) + with self.assertWarns(DeprecationWarning): + circ.h(qr1[0]).c_if(cr1[1], True) + with self.assertWarns(DeprecationWarning): + circ.h(qr2[1]).c_if(cr2[0], False) + with self.assertWarns(DeprecationWarning): + circ.cx(qr1[1], qr2[2]).c_if(cr2[2], True) circ.measure(qr2[2], cr2[0]) - inst = circuit_to_instruction(circ) + with self.assertWarns(DeprecationWarning): + inst = circuit_to_instruction(circ) q = QuantumRegister(5, "q") c = ClassicalRegister(6, "c") self.assertEqual(inst.definition[0].qubits, (q[0],)) self.assertEqual(inst.definition[1].qubits, (q[3],)) self.assertEqual(inst.definition[2].qubits, (q[1], q[4])) - - self.assertEqual(inst.definition[0].operation.condition, (c[1], True)) - self.assertEqual(inst.definition[1].operation.condition, (c[3], False)) - self.assertEqual(inst.definition[2].operation.condition, (c[5], True)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(inst.definition[0].operation.condition, (c[1], True)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(inst.definition[1].operation.condition, (c[3], False)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(inst.definition[2].operation.condition, (c[5], True)) def test_flatten_circuit_registerless(self): """Test that the conversion works when the given circuit has bits that are not contained in @@ -196,8 +202,10 @@ def test_registerless_classical_bits(self): Regression test of gh-7394.""" expected = QuantumCircuit([Qubit(), Clbit()]) - expected.h(0).c_if(expected.clbits[0], 0) - test = circuit_to_instruction(expected) + with self.assertWarns(DeprecationWarning): + expected.h(0).c_if(expected.clbits[0], 0) + with self.assertWarns(DeprecationWarning): + test = circuit_to_instruction(expected) self.assertIsInstance(test, Instruction) self.assertIsInstance(test.definition, QuantumCircuit) @@ -206,7 +214,8 @@ def test_registerless_classical_bits(self): test_instruction = test.definition.data[0] expected_instruction = expected.data[0] self.assertIs(type(test_instruction.operation), type(expected_instruction.operation)) - self.assertEqual(test_instruction.operation.condition, (test.definition.clbits[0], 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(test_instruction.operation.condition, (test.definition.clbits[0], 0)) def test_zero_operands(self): """Test that an instruction can be created, even if it has zero operands.""" diff --git a/test/python/converters/test_dag_to_dagdependency.py b/test/python/converters/test_dag_to_dagdependency.py index 6b52652e6ace..b4a66659245b 100644 --- a/test/python/converters/test_dag_to_dagdependency.py +++ b/test/python/converters/test_dag_to_dagdependency.py @@ -34,7 +34,8 @@ def test_circuit_and_dag_dependency(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) @@ -55,7 +56,8 @@ def test_circuit_and_dag_dependency2(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/converters/test_dag_to_dagdependency_v2.py b/test/python/converters/test_dag_to_dagdependency_v2.py index 925bf442f477..951e31835fe9 100644 --- a/test/python/converters/test_dag_to_dagdependency_v2.py +++ b/test/python/converters/test_dag_to_dagdependency_v2.py @@ -34,7 +34,8 @@ def test_circuit_and_dag_dependency(self): circuit_in.h(qr[1]) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) - circuit_in.x(qr[0]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_in.x(qr[0]).c_if(cr, 0x3) circuit_in.measure(qr[0], cr[0]) circuit_in.measure(qr[1], cr[1]) circuit_in.measure(qr[2], cr[2]) diff --git a/test/python/dagcircuit/test_collect_blocks.py b/test/python/dagcircuit/test_collect_blocks.py index b2715078d7f5..d8178fdb3a54 100644 --- a/test/python/dagcircuit/test_collect_blocks.py +++ b/test/python/dagcircuit/test_collect_blocks.py @@ -243,7 +243,8 @@ def test_circuit_has_conditional_gates(self): qc.x(0) qc.x(1) qc.cx(1, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.x(0) qc.x(1) qc.cx(0, 1) @@ -263,11 +264,13 @@ def test_circuit_has_conditional_gates(self): # conditional gate (note that x(1) following the measure is collected into the first # block). block_collector = BlockCollector(circuit_to_dag(qc)) - blocks = block_collector.collect_all_matching_blocks( - lambda node: node.op.name in ["x", "cx"] and not getattr(node.op, "condition", None), - split_blocks=False, - min_block_size=1, - ) + with self.assertWarns(DeprecationWarning): + blocks = block_collector.collect_all_matching_blocks( + lambda node: node.op.name in ["x", "cx"] + and not getattr(node.op, "condition", None), + split_blocks=False, + min_block_size=1, + ) self.assertEqual(len(blocks), 2) self.assertEqual(len(blocks[0]), 4) self.assertEqual(len(blocks[1]), 2) @@ -280,7 +283,8 @@ def test_circuit_has_conditional_gates_dagdependency(self): qc.x(0) qc.x(1) qc.cx(1, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.x(0) qc.x(1) qc.cx(0, 1) @@ -300,11 +304,13 @@ def test_circuit_has_conditional_gates_dagdependency(self): # conditional gate (note that x(1) following the measure is collected into the first # block). block_collector = BlockCollector(circuit_to_dag(qc)) - blocks = block_collector.collect_all_matching_blocks( - lambda node: node.op.name in ["x", "cx"] and not getattr(node.op, "condition", None), - split_blocks=False, - min_block_size=1, - ) + with self.assertWarns(DeprecationWarning): + blocks = block_collector.collect_all_matching_blocks( + lambda node: node.op.name in ["x", "cx"] + and not getattr(node.op, "condition", None), + split_blocks=False, + min_block_size=1, + ) self.assertEqual(len(blocks), 2) self.assertEqual(len(blocks[0]), 4) self.assertEqual(len(blocks[1]), 2) @@ -544,11 +550,13 @@ def test_collect_blocks_with_clbits(self): condition.""" qc = QuantumCircuit(4, 3) - qc.cx(0, 1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 1) qc.cx(2, 3) qc.cx(1, 2) qc.cx(0, 1) - qc.cx(2, 3).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(1, 0) dag = circuit_to_dag(qc) @@ -567,7 +575,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -580,11 +589,13 @@ def test_collect_blocks_with_clbits_dagdependency(self): under conditions, using DAGDependency.""" qc = QuantumCircuit(4, 3) - qc.cx(0, 1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 1) qc.cx(2, 3) qc.cx(1, 2) qc.cx(0, 1) - qc.cx(2, 3).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(1, 0) dag = circuit_to_dagdependency(qc) @@ -603,7 +614,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dagdependency_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -620,10 +632,13 @@ def test_collect_blocks_with_clbits2(self): cbit = Clbit() qc = QuantumCircuit(qreg, creg, [cbit]) - qc.cx(0, 1).c_if(creg[1], 1) - qc.cx(2, 3).c_if(cbit, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[1], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(cbit, 0) qc.cx(1, 2) - qc.cx(0, 1).c_if(creg[2], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[2], 1) dag = circuit_to_dag(qc) @@ -642,7 +657,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -659,10 +675,13 @@ def test_collect_blocks_with_clbits2_dagdependency(self): cbit = Clbit() qc = QuantumCircuit(qreg, creg, [cbit]) - qc.cx(0, 1).c_if(creg[1], 1) - qc.cx(2, 3).c_if(cbit, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[1], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(2, 3).c_if(cbit, 0) qc.cx(1, 2) - qc.cx(0, 1).c_if(creg[2], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[2], 1) dag = circuit_to_dag(qc) @@ -681,7 +700,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -698,9 +718,11 @@ def test_collect_blocks_with_cregs(self): creg2 = ClassicalRegister(2, "cr2") qc = QuantumCircuit(qreg, creg, creg2) - qc.cx(0, 1).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg, 3) qc.cx(1, 2) - qc.cx(0, 1).c_if(creg[2], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[2], 1) dag = circuit_to_dag(qc) @@ -719,7 +741,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -737,9 +760,11 @@ def test_collect_blocks_with_cregs_dagdependency(self): creg2 = ClassicalRegister(2, "cr2") qc = QuantumCircuit(qreg, creg, creg2) - qc.cx(0, 1).c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg, 3) qc.cx(1, 2) - qc.cx(0, 1).c_if(creg[2], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(creg[2], 1) dag = circuit_to_dagdependency(qc) @@ -758,7 +783,8 @@ def _collapse_fn(circuit): return op # Collapse block with measures into a single "COLLAPSED" block - dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation(blocks, _collapse_fn) collapsed_qc = dagdependency_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) @@ -917,14 +943,19 @@ def test_split_layers_dagdependency(self): def test_block_collapser_register_condition(self): """Test that BlockCollapser can handle a register being used more than once.""" qc = QuantumCircuit(1, 2) - qc.x(0).c_if(qc.cregs[0], 0) - qc.y(0).c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + qc.y(0).c_if(qc.cregs[0], 1) dag = circuit_to_dag(qc) blocks = BlockCollector(dag).collect_all_matching_blocks( lambda _: True, split_blocks=False, min_block_size=1 ) - dag = BlockCollapser(dag).collapse_to_operation(blocks, lambda circ: circ.to_instruction()) + with self.assertWarns(DeprecationWarning): + dag = BlockCollapser(dag).collapse_to_operation( + blocks, lambda circ: circ.to_instruction() + ) collapsed_qc = dag_to_circuit(dag) self.assertEqual(len(collapsed_qc.data), 1) diff --git a/test/python/dagcircuit/test_compose.py b/test/python/dagcircuit/test_compose.py index 27404cec05c2..1bbd3031d310 100644 --- a/test/python/dagcircuit/test_compose.py +++ b/test/python/dagcircuit/test_compose.py @@ -321,8 +321,10 @@ def test_compose_conditional(self): creg = ClassicalRegister(2, "rcr") circuit_right = QuantumCircuit(qreg, creg) - circuit_right.x(qreg[1]).c_if(creg, 2) - circuit_right.h(qreg[0]).c_if(creg, 1) + with self.assertWarns(DeprecationWarning): + circuit_right.x(qreg[1]).c_if(creg, 2) + with self.assertWarns(DeprecationWarning): + circuit_right.h(qreg[0]).c_if(creg, 1) circuit_right.measure(qreg, creg) # permuted subset of qubits and clbits @@ -330,16 +332,19 @@ def test_compose_conditional(self): dag_right = circuit_to_dag(circuit_right) # permuted subset of qubits and clbits - dag_left.compose( - dag_right, - qubits=[self.left_qubit1, self.left_qubit4], - clbits=[self.left_clbit1, self.left_clbit0], - ) + with self.assertWarns(DeprecationWarning): + dag_left.compose( + dag_right, + qubits=[self.left_qubit1, self.left_qubit4], + clbits=[self.left_clbit1, self.left_clbit0], + ) circuit_composed = dag_to_circuit(dag_left) circuit_expected = self.circuit_left.copy() - circuit_expected.x(self.left_qubit4).c_if(*self.condition1) - circuit_expected.h(self.left_qubit1).c_if(*self.condition2) + with self.assertWarns(DeprecationWarning): + circuit_expected.x(self.left_qubit4).c_if(*self.condition1) + with self.assertWarns(DeprecationWarning): + circuit_expected.h(self.left_qubit1).c_if(*self.condition2) circuit_expected.measure(self.left_qubit4, self.left_clbit0) circuit_expected.measure(self.left_qubit1, self.left_clbit1) @@ -423,12 +428,13 @@ def test_compose_condition_multiple_classical(self): circuit_left = QuantumCircuit(qreg, creg1, creg2) circuit_right = QuantumCircuit(qreg, creg1, creg2) - circuit_right.h(0).c_if(creg1, 1) + with self.assertWarns(DeprecationWarning): + circuit_right.h(0).c_if(creg1, 1) dag_left = circuit_to_dag(circuit_left) dag_right = circuit_to_dag(circuit_right) - - dag_composed = dag_left.compose(dag_right, qubits=[0], clbits=[0, 1], inplace=False) + with self.assertWarns(DeprecationWarning): + dag_composed = dag_left.compose(dag_right, qubits=[0], clbits=[0, 1], inplace=False) dag_expected = circuit_to_dag(circuit_right.copy()) diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index e2881cf4a3d9..96f307ea8548 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -118,7 +118,7 @@ def raise_if_dagcircuit_invalid(dag): out_wires = set(dag._out_wires(node._node_id)) node_cond_bits = set( - node.op.condition[0][:] if getattr(node.op, "condition", None) is not None else [] + node.condition[0][:] if getattr(node, "condition", None) is not None else [] ) node_qubits = set(node.qargs) node_clbits = set(node.cargs) @@ -561,7 +561,8 @@ def setUp(self): def test_apply_operation_back(self): """The apply_operation_back() method.""" - x_gate = XGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + x_gate = XGate().c_if(*self.condition) self.dag.apply_operation_back(HGate(), [self.qubit0], []) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1], [self.clbit1]) @@ -573,7 +574,8 @@ def test_apply_operation_back(self): def test_edges(self): """Test that DAGCircuit.edges() behaves as expected with ops.""" - x_gate = XGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + x_gate = XGate().c_if(*self.condition) self.dag.apply_operation_back(HGate(), [self.qubit0], []) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1], [self.clbit1]) @@ -590,13 +592,14 @@ def test_apply_operation_back_conditional(self): """Test consistency of apply_operation_back with condition set.""" # Single qubit gate conditional: qc.h(qr[2]).c_if(cr, 3) - - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) h_node = self.dag.apply_operation_back(h_gate, [self.qubit2], []) self.assertEqual(h_node.qargs, (self.qubit2,)) self.assertEqual(h_node.cargs, ()) - self.assertEqual(h_node.op.condition, h_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual(h_node.op.condition, h_gate.condition) self.assertEqual( sorted(self.dag._in_edges(h_node._node_id)), @@ -630,13 +633,14 @@ def test_apply_operation_back_conditional_measure(self): new_creg = ClassicalRegister(1, "cr2") self.dag.add_creg(new_creg) - - meas_gate = Measure().c_if(new_creg, 0) + with self.assertWarns(DeprecationWarning): + meas_gate = Measure().c_if(new_creg, 0) meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit0], [self.clbit0]) self.assertEqual(meas_node.qargs, (self.qubit0,)) self.assertEqual(meas_node.cargs, (self.clbit0,)) - self.assertEqual(meas_node.op.condition, meas_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual(meas_node.op.condition, meas_gate.condition) self.assertEqual( sorted(self.dag._in_edges(meas_node._node_id)), @@ -675,13 +679,14 @@ def test_apply_operation_back_conditional_measure_to_self(self): # Measure targeting a clbit which _is_ a member of the conditional # register. qc.measure(qr[0], cr[0]).c_if(cr, 3) - - meas_gate = Measure().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + meas_gate = Measure().c_if(*self.condition) meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit1], [self.clbit1]) self.assertEqual(meas_node.qargs, (self.qubit1,)) self.assertEqual(meas_node.cargs, (self.clbit1,)) - self.assertEqual(meas_node.op.condition, meas_gate.condition) + with self.assertWarns(DeprecationWarning): + self.assertEqual(meas_node.op.condition, meas_gate.condition) self.assertEqual( sorted(self.dag._in_edges(meas_node._node_id)), @@ -1239,7 +1244,8 @@ def test_dag_collect_runs(self): def test_dag_collect_runs_start_with_conditional(self): """Test collect runs with a conditional at the start of the run.""" - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1252,7 +1258,8 @@ def test_dag_collect_runs_start_with_conditional(self): def test_dag_collect_runs_conditional_in_middle(self): """Test collect_runs with a conditional in the middle of a run.""" - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1294,7 +1301,8 @@ def test_dag_collect_1q_runs_start_with_conditional(self): """Test collect 1q runs with a conditional at the start of the run.""" self.dag.apply_operation_back(Reset(), [self.qubit0]) self.dag.apply_operation_back(Delay(100), [self.qubit0]) - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1309,7 +1317,8 @@ def test_dag_collect_1q_runs_conditional_in_middle(self): """Test collect_1q_runs with a conditional in the middle of a run.""" self.dag.apply_operation_back(Reset(), [self.qubit0]) self.dag.apply_operation_back(Delay(100), [self.qubit0]) - h_gate = HGate().c_if(*self.condition) + with self.assertWarns(DeprecationWarning): + h_gate = HGate().c_if(*self.condition) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1387,7 +1396,8 @@ def test_layers_basic(self): qubit1 = qreg[1] clbit0 = creg[0] clbit1 = creg[1] - x_gate = XGate().c_if(creg, 3) + with self.assertWarns(DeprecationWarning): + x_gate = XGate().c_if(creg, 3) dag = DAGCircuit() dag.add_qreg(qreg) dag.add_creg(creg) @@ -1856,29 +1866,41 @@ def test_semantic_conditions(self): qreg = QuantumRegister(1, name="q") creg = ClassicalRegister(1, name="c") qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.cregs[0], 1) - qc2.x(0).c_if(qc2.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], True) self.assertEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) # Order of operations transposed. qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.clbits[-1], True) - qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) self.assertNotEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) # Single-bit condition values not the same. qc1 = QuantumCircuit(qreg, creg, [Clbit()]) - qc1.x(0).c_if(qc1.cregs[0], 1) - qc1.x(0).c_if(qc1.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc1.x(0).c_if(qc1.clbits[-1], True) qc2 = QuantumCircuit(qreg, creg, [Clbit()]) - qc2.x(0).c_if(qc2.cregs[0], 1) - qc2.x(0).c_if(qc2.clbits[-1], False) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0).c_if(qc2.clbits[-1], False) self.assertNotEqual(circuit_to_dag(qc1), circuit_to_dag(qc2)) def test_semantic_expr(self): @@ -2489,7 +2511,8 @@ def test_substitute_without_propagating_bit_conditional(self): sub = QuantumCircuit(2, 1) sub.h(0) - sub.cx(0, 1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + sub.cx(0, 1).c_if(0, True) sub.h(0) expected = DAGCircuit() @@ -2522,7 +2545,8 @@ def test_substitute_without_propagating_register_conditional(self): sub = QuantumCircuit(QuantumRegister(2), ClassicalRegister(2)) sub.h(0) - sub.cx(0, 1).c_if(sub.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + sub.cx(0, 1).c_if(sub.cregs[0], 3) sub.h(0) expected = DAGCircuit() @@ -2559,8 +2583,10 @@ def test_substitute_with_provided_wire_map_propagate_condition(self): sub.cx(0, 1) sub.h(0) - conditioned_h = HGate().c_if(*conditioned_cz.condition) - conditioned_cx = CXGate().c_if(*conditioned_cz.condition) + with self.assertWarns(DeprecationWarning): + conditioned_h = HGate().c_if(*conditioned_cz.condition) + with self.assertWarns(DeprecationWarning): + conditioned_cx = CXGate().c_if(*conditioned_cz.condition) expected = DAGCircuit() expected.add_qubits(base_qubits) @@ -2593,11 +2619,13 @@ def test_substitute_with_provided_wire_map_no_propagate_condition(self): sub = QuantumCircuit(2, 1) sub.h(0) - sub.cx(0, 1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + sub.cx(0, 1).c_if(0, True) sub.h(0) conditioned_cx = CXGate().to_mutable() - conditioned_cx.condition = conditioned_cz.condition + with self.assertWarns(DeprecationWarning): + conditioned_cx.condition = conditioned_cz.condition expected = DAGCircuit() expected.add_qubits(base_qubits) @@ -2626,7 +2654,8 @@ def test_creates_additional_alias_register(self): target = base.apply_operation_back(Instruction("dummy", 2, 2, []), base_qreg, base_creg[:2]) sub = QuantumCircuit(QuantumRegister(2), ClassicalRegister(2)) - sub.cx(0, 1).c_if(sub.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + sub.cx(0, 1).c_if(sub.cregs[0], 3) base.substitute_node_with_dag(target, circuit_to_dag(sub)) @@ -2694,7 +2723,8 @@ def test_substituting_node_preserves_args_condition(self, inplace): self.assertEqual(replacement_node.op.name, "cz") self.assertEqual(replacement_node.qargs, (qr[1], qr[0])) self.assertEqual(replacement_node.cargs, ()) - self.assertEqual(replacement_node.op.condition, (cr, 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(replacement_node.op.condition, (cr, 1)) self.assertEqual(replacement_node is node_to_be_replaced, inplace) @data(True, False) @@ -2731,12 +2761,14 @@ def test_refuses_to_overwrite_condition(self, inplace): dag = DAGCircuit() dag.add_qreg(qr) dag.add_creg(cr) - node = dag.apply_operation_back(XGate().c_if(cr, 2), qr, []) + with self.assertWarns(DeprecationWarning): + node = dag.apply_operation_back(XGate().c_if(cr, 2), qr, []) with self.assertRaisesRegex(DAGCircuitError, "Cannot propagate a condition"): - dag.substitute_node( - node, XGate().c_if(cr, 1), inplace=inplace, propagate_condition=True - ) + with self.assertWarns(DeprecationWarning): + dag.substitute_node( + node, XGate().c_if(cr, 1), inplace=inplace, propagate_condition=True + ) @data(True, False) def test_replace_if_else_op_with_another(self, inplace): @@ -3194,13 +3226,15 @@ def setUp(self): def test_creg_conditional(self): """Test consistency of conditional on classical register.""" - self.circuit.h(self.qreg[0]).c_if(self.creg, 1) + with self.assertWarns(DeprecationWarning): + self.circuit.h(self.qreg[0]).c_if(self.creg, 1) self.dag = circuit_to_dag(self.circuit) gate_node = self.dag.gate_nodes()[0] self.assertEqual(gate_node.op, HGate()) self.assertEqual(gate_node.qargs, (self.qreg[0],)) self.assertEqual(gate_node.cargs, ()) - self.assertEqual(gate_node.op.condition, (self.creg, 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate_node.op.condition, (self.creg, 1)) gate_node_preds = list(self.dag.predecessors(gate_node)) gate_node_in_edges = [ @@ -3235,14 +3269,15 @@ def test_creg_conditional(self): def test_clbit_conditional(self): """Test consistency of conditional on single classical bit.""" - - self.circuit.h(self.qreg[0]).c_if(self.creg[0], 1) + with self.assertWarns(DeprecationWarning): + self.circuit.h(self.qreg[0]).c_if(self.creg[0], 1) self.dag = circuit_to_dag(self.circuit) gate_node = self.dag.gate_nodes()[0] self.assertEqual(gate_node.op, HGate()) self.assertEqual(gate_node.qargs, (self.qreg[0],)) self.assertEqual(gate_node.cargs, ()) - self.assertEqual(gate_node.op.condition, (self.creg[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(gate_node.op.condition, (self.creg[0], 1)) gate_node_preds = list(self.dag.predecessors(gate_node)) gate_node_in_edges = [ diff --git a/test/python/primitives/test_backend_estimator.py b/test/python/primitives/test_backend_estimator.py index 80b471b66063..62845461dacf 100644 --- a/test/python/primitives/test_backend_estimator.py +++ b/test/python/primitives/test_backend_estimator.py @@ -493,7 +493,8 @@ def test_dynamic_circuit(self): qc.h(0) qc.cx(0, 1) qc.measure(1, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) observable = SparsePauliOp("IZ") diff --git a/test/python/primitives/test_backend_sampler.py b/test/python/primitives/test_backend_sampler.py index eb3b79f1b911..dc77a3c47ce6 100644 --- a/test/python/primitives/test_backend_sampler.py +++ b/test/python/primitives/test_backend_sampler.py @@ -397,7 +397,8 @@ def test_circuit_with_dynamic_circuit(self): qc.h(0) qc.cx(0, 1) qc.measure(0, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) with self.assertWarns(DeprecationWarning): backend = Aer.get_backend("aer_simulator") diff --git a/test/python/primitives/test_backend_sampler_v2.py b/test/python/primitives/test_backend_sampler_v2.py index e66b594cc738..77830d5c9224 100644 --- a/test/python/primitives/test_backend_sampler_v2.py +++ b/test/python/primitives/test_backend_sampler_v2.py @@ -31,8 +31,10 @@ from qiskit.primitives.containers.sampler_pub import SamplerPub from qiskit.providers import JobStatus from qiskit.providers.backend_compat import BackendV2Converter -from qiskit.providers.basic_provider import BasicSimulator +from qiskit.providers.basic_provider import BasicProviderJob, BasicSimulator from qiskit.providers.fake_provider import Fake7QPulseV1, GenericBackendV2 +from qiskit.result import Result +from qiskit.qobj.utils import MeasReturnType, MeasLevel from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from ..legacy_cmaps import LAGOS_CMAP @@ -50,6 +52,64 @@ BACKENDS = BACKENDS_V1 + BACKENDS_V2 +class Level1BackendV2(GenericBackendV2): + """Wrapper around GenericBackendV2 adding level 1 data support for testing + + GenericBackendV2 is used to run the simulation. Then level 1 data (a + complex number per classical register per shot) is generated by mapping 0 + to -1 and 1 to 1 with a random number added to each shot drawn from a + normal distribution with a standard deviation of ``level1_sigma``. Each + data point has ``1j * idx`` added to it where ``idx`` is the index of the + classical register. For ``meas_return="avg"``, the individual shot results + are still calculated and then averaged. + """ + + level1_sigma = 0.1 + + def run(self, run_input, **options): + # Validate level 1 options + if "meas_level" not in options or "meas_return" not in options: + raise ValueError(f"{type(self)} requires 'meas_level' and 'meas_return' run options!") + meas_level = options.pop("meas_level") + if meas_level != 1: + raise ValueError(f"'meas_level' must be 1, not {meas_level}") + meas_return = options.pop("meas_return") + if meas_return not in ("single", "avg"): + raise ValueError(f"Unexpected value for 'meas_return': {meas_return}") + + options["memory"] = True + + rng = np.random.default_rng(seed=options.get("seed_simulator")) + + inner_job = super().run(run_input, **options) + result_dict = inner_job.result().to_dict() + for circ, exp_result in zip(run_input, result_dict["results"]): + num_clbits = sum(cr.size for cr in circ.cregs) + bitstrings = [ + format(int(x, 16), f"0{num_clbits}b") for x in exp_result["data"]["memory"] + ] + new_data = [ + [ + [2 * int(d) - 1 + rng.normal(scale=self.level1_sigma), i] + for i, d in enumerate(reversed(bs)) + ] + for bs in bitstrings + ] + if meas_return == "avg": + new_data = [ + [sum(shot[idx][0] for shot in new_data) / len(new_data), idx] + for idx in range(num_clbits) + ] + exp_result["meas_return"] = MeasReturnType.AVERAGE + else: + exp_result["meas_return"] = MeasReturnType.SINGLE + exp_result["data"] = {"memory": new_data} + exp_result["meas_level"] = MeasLevel.KERNELED + + result = Result.from_dict(result_dict) + return BasicProviderJob(self, inner_job.job_id(), result) + + @ddt class TestBackendSamplerV2(QiskitTestCase): """Test for BackendSamplerV2""" @@ -942,6 +1002,55 @@ def test_run_shots_result_size_v1(self, backend): self.assertLessEqual(result[0].data.meas.num_shots, self._shots) self.assertEqual(sum(result[0].data.meas.get_counts().values()), self._shots) + def test_run_level1(self): + """Test running with meas_level=1""" + nq = 2 + shots = 100 + + backend = Level1BackendV2(nq) + qc = QuantumCircuit(nq) + qc.x(1) + qc.measure_all() + pm = generate_preset_pass_manager(optimization_level=0, backend=backend) + qc = pm.run(qc) + options = { + "default_shots": shots, + "seed_simulator": self._seed, + "run_options": { + "meas_level": 1, + "meas_return": "single", + }, + } + sampler = BackendSamplerV2(backend=backend, options=options) + result_single = sampler.run([qc]).result() + + options = { + "default_shots": shots, + "seed_simulator": self._seed, + "run_options": { + "meas_level": 1, + "meas_return": "avg", + }, + } + sampler = BackendSamplerV2(backend=backend, options=options) + result_avg = sampler.run([qc]).result() + + # Check that averaging the meas_return="single" data matches the + # meas_return="avg" data. + averaged_singles = np.average(result_single[0].join_data(), axis=0) + average_data = result_avg[0].join_data() + self.assertLessEqual( + max(abs(averaged_singles - average_data)), + backend.level1_sigma / np.sqrt(shots) * 6, + ) + + # Check that average data matches expected form for the circuit + expected_average = np.array([-1, 1 + 1j]) + self.assertLessEqual( + max(abs(expected_average - average_data)), + backend.level1_sigma / np.sqrt(shots) * 6, + ) + @combine(backend=BACKENDS_V2) def test_primitive_job_status_done(self, backend): """test primitive job's status""" @@ -1215,8 +1324,10 @@ def test_circuit_with_aliased_cregs(self, backend): qc.h(0) qc.measure(0, c1) qc.measure(1, c2) - qc.z(2).c_if(c1, 1) - qc.x(2).c_if(c2, 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(c1, 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(c2, 1) qc2 = QuantumCircuit(5, 5) qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True) cregs = [creg.name for creg in qc2.cregs] @@ -1251,8 +1362,10 @@ def test_circuit_with_aliased_cregs_v1(self, backend): qc.h(0) qc.measure(0, c1) qc.measure(1, c2) - qc.z(2).c_if(c1, 1) - qc.x(2).c_if(c2, 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(c1, 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(c2, 1) qc2 = QuantumCircuit(5, 5) qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True) cregs = [creg.name for creg in qc2.cregs] diff --git a/test/python/primitives/test_primitive.py b/test/python/primitives/test_primitive.py index fc0118564f3d..78bcff5ff405 100644 --- a/test/python/primitives/test_primitive.py +++ b/test/python/primitives/test_primitive.py @@ -163,7 +163,8 @@ def test_circuit_key_controlflow(self): qc.h(0) qc.cx(0, 1) qc.measure(0, 0) - qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) self.assertIsInstance(hash(_circuit_key(qc)), int) self.assertIsInstance(json.dumps(_circuit_key(qc)), str) diff --git a/test/python/primitives/test_statevector_sampler.py b/test/python/primitives/test_statevector_sampler.py index a782aafaeaf5..3af4e8d9f688 100644 --- a/test/python/primitives/test_statevector_sampler.py +++ b/test/python/primitives/test_statevector_sampler.py @@ -283,7 +283,8 @@ def test_run_errors(self): qc4 = QuantumCircuit(2, 2) qc4.h(0) qc4.measure(1, 1) - qc4.x(0).c_if(1, 1) + with self.assertWarns(DeprecationWarning): + qc4.x(0).c_if(1, 1) qc4.measure(0, 0) sampler = StatevectorSampler() @@ -592,8 +593,10 @@ def test_circuit_with_aliased_cregs(self): c2 = ClassicalRegister(1, "c2") qc = QuantumCircuit(q, c1, c2) - qc.z(2).c_if(c1, 1) - qc.x(2).c_if(c2, 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(c1, 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(c2, 1) qc2 = QuantumCircuit(5, 5) qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True) # Note: qc2 has aliased cregs, c0 -> c[2] and c1 -> c[4]. diff --git a/test/python/providers/basic_provider/test_basic_simulator.py b/test/python/providers/basic_provider/test_basic_simulator.py index 57dd67dfd3c3..925823b49300 100644 --- a/test/python/providers/basic_provider/test_basic_simulator.py +++ b/test/python/providers/basic_provider/test_basic_simulator.py @@ -174,7 +174,8 @@ def test_if_statement(self): circuit_if_true.x(qr[1]) circuit_if_true.measure(qr[0], cr[0]) circuit_if_true.measure(qr[1], cr[1]) - circuit_if_true.x(qr[2]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_if_true.x(qr[2]).c_if(cr, 0x3) circuit_if_true.measure(qr[0], cr[0]) circuit_if_true.measure(qr[1], cr[1]) circuit_if_true.measure(qr[2], cr[2]) @@ -193,7 +194,8 @@ def test_if_statement(self): circuit_if_false.x(qr[0]) circuit_if_false.measure(qr[0], cr[0]) circuit_if_false.measure(qr[1], cr[1]) - circuit_if_false.x(qr[2]).c_if(cr, 0x3) + with self.assertWarns(DeprecationWarning): + circuit_if_false.x(qr[2]).c_if(cr, 0x3) circuit_if_false.measure(qr[0], cr[0]) circuit_if_false.measure(qr[1], cr[1]) circuit_if_false.measure(qr[2], cr[2]) @@ -230,7 +232,8 @@ def test_bit_cif_crossaffect(self): circuit.x([qr[1], qr[2]]) circuit.measure(qr[1], cr[1]) circuit.measure(qr[2], cr[2]) - circuit.h(qr[0]).c_if(cr[0], True) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], True) circuit.measure(qr[0], cr1[0]) job = self.backend.run(circuit, shots=shots, seed_simulator=self.seed) result = job.result().get_counts() @@ -269,8 +272,10 @@ def test_teleport(self): circuit.barrier(qr) circuit.measure(qr[0], cr0[0]) circuit.measure(qr[1], cr1[0]) - circuit.z(qr[2]).c_if(cr0, 1) - circuit.x(qr[2]).c_if(cr1, 1) + with self.assertWarns(DeprecationWarning): + circuit.z(qr[2]).c_if(cr0, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[2]).c_if(cr1, 1) circuit.measure(qr[2], cr2[0]) job = self.backend.run( transpile(circuit, self.backend), shots=shots, seed_simulator=self.seed diff --git a/test/python/qasm2/test_arxiv_examples.py b/test/python/qasm2/test_arxiv_examples.py index 197b842697e1..85ed23e0ed19 100644 --- a/test/python/qasm2/test_arxiv_examples.py +++ b/test/python/qasm2/test_arxiv_examples.py @@ -72,7 +72,8 @@ def test_teleportation(self, parser): if(c1==1) x q[2]; post q[2]; measure q[2] -> c2[0];""" - parsed = parser(example) + with self.assertWarns(DeprecationWarning): + parsed = parser(example) post = gate_builder("post", [], QuantumCircuit([Qubit()])) @@ -90,8 +91,10 @@ def test_teleportation(self, parser): qc.h(q[0]) qc.measure(q[0], c0[0]) qc.measure(q[1], c1[0]) - qc.z(q[2]).c_if(c0, 1) - qc.x(q[2]).c_if(c1, 1) + with self.assertWarns(DeprecationWarning): + qc.z(q[2]).c_if(c0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(q[2]).c_if(c1, 1) qc.append(post(), [q[2]], []) qc.measure(q[2], c2[0]) @@ -168,7 +171,8 @@ def test_inverse_qft_1(self, parser): if(c==7) u1(pi/2+pi/4+pi/8) q[3]; h q[3]; measure q[3] -> c[3];""" - parsed = parser(example) + with self.assertWarns(DeprecationWarning): + parsed = parser(example) q = QuantumRegister(4, "q") c = ClassicalRegister(4, "c") @@ -177,21 +181,32 @@ def test_inverse_qft_1(self, parser): qc.barrier(q) qc.h(q[0]) qc.measure(q[0], c[0]) - qc.append(U1Gate(math.pi / 2).c_if(c, 1), [q[1]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c, 1), [q[1]]) qc.h(q[1]) qc.measure(q[1], c[1]) - qc.append(U1Gate(math.pi / 4).c_if(c, 1), [q[2]]) - qc.append(U1Gate(math.pi / 2).c_if(c, 2), [q[2]]) - qc.append(U1Gate(math.pi / 4 + math.pi / 2).c_if(c, 3), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4).c_if(c, 1), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c, 2), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4 + math.pi / 2).c_if(c, 3), [q[2]]) qc.h(q[2]) qc.measure(q[2], c[2]) - qc.append(U1Gate(math.pi / 8).c_if(c, 1), [q[3]]) - qc.append(U1Gate(math.pi / 4).c_if(c, 2), [q[3]]) - qc.append(U1Gate(math.pi / 8 + math.pi / 4).c_if(c, 3), [q[3]]) - qc.append(U1Gate(math.pi / 2).c_if(c, 4), [q[3]]) - qc.append(U1Gate(math.pi / 8 + math.pi / 2).c_if(c, 5), [q[3]]) - qc.append(U1Gate(math.pi / 4 + math.pi / 2).c_if(c, 6), [q[3]]) - qc.append(U1Gate(math.pi / 8 + math.pi / 4 + math.pi / 2).c_if(c, 7), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8).c_if(c, 1), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4).c_if(c, 2), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8 + math.pi / 4).c_if(c, 3), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c, 4), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8 + math.pi / 2).c_if(c, 5), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4 + math.pi / 2).c_if(c, 6), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8 + math.pi / 4 + math.pi / 2).c_if(c, 7), [q[3]]) qc.h(q[3]) qc.measure(q[3], c[3]) @@ -224,7 +239,8 @@ def test_inverse_qft_2(self, parser): if(c2==1) u1(pi/2) q[3]; h q[3]; measure q[3] -> c3[0];""" - parsed = parser(example) + with self.assertWarns(DeprecationWarning): + parsed = parser(example) q = QuantumRegister(4, "q") c0 = ClassicalRegister(1, "c0") @@ -236,16 +252,22 @@ def test_inverse_qft_2(self, parser): qc.barrier(q) qc.h(q[0]) qc.measure(q[0], c0[0]) - qc.append(U1Gate(math.pi / 2).c_if(c0, 1), [q[1]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c0, 1), [q[1]]) qc.h(q[1]) qc.measure(q[1], c1[0]) - qc.append(U1Gate(math.pi / 4).c_if(c0, 1), [q[2]]) - qc.append(U1Gate(math.pi / 2).c_if(c1, 1), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4).c_if(c0, 1), [q[2]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c1, 1), [q[2]]) qc.h(q[2]) qc.measure(q[2], c2[0]) - qc.append(U1Gate(math.pi / 8).c_if(c0, 1), [q[3]]) - qc.append(U1Gate(math.pi / 4).c_if(c1, 1), [q[3]]) - qc.append(U1Gate(math.pi / 2).c_if(c2, 1), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 8).c_if(c0, 1), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 4).c_if(c1, 1), [q[3]]) + with self.assertWarns(DeprecationWarning): + qc.append(U1Gate(math.pi / 2).c_if(c2, 1), [q[3]]) qc.h(q[3]) qc.measure(q[3], c3[0]) @@ -423,7 +445,8 @@ def test_error_correction(self, parser): if(syn==2) x q[2]; if(syn==3) x q[1]; measure q -> c;""" - parsed = parser(example) + with self.assertWarns(DeprecationWarning): + parsed = parser(example) syndrome_definition = QuantumCircuit([Qubit() for _ in [None] * 5]) syndrome_definition.cx(0, 3) @@ -442,9 +465,12 @@ def test_error_correction(self, parser): qc.barrier(q) qc.append(syndrome(), [q[0], q[1], q[2], a[0], a[1]]) qc.measure(a, syn) - qc.x(q[0]).c_if(syn, 1) - qc.x(q[2]).c_if(syn, 2) - qc.x(q[1]).c_if(syn, 3) + with self.assertWarns(DeprecationWarning): + qc.x(q[0]).c_if(syn, 1) + with self.assertWarns(DeprecationWarning): + qc.x(q[2]).c_if(syn, 2) + with self.assertWarns(DeprecationWarning): + qc.x(q[1]).c_if(syn, 3) qc.measure(q, c) self.assertEqual(parsed, qc) diff --git a/test/python/qasm2/test_circuit_methods.py b/test/python/qasm2/test_circuit_methods.py index 1ba9689ab3ef..d5fa814f7c52 100644 --- a/test/python/qasm2/test_circuit_methods.py +++ b/test/python/qasm2/test_circuit_methods.py @@ -180,14 +180,16 @@ def test_qasm_text_conditional(self): ) + "\n" ) - q_circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + q_circuit = QuantumCircuit.from_qasm_str(qasm_string) qr = QuantumRegister(1, "q") cr0 = ClassicalRegister(4, "c0") cr1 = ClassicalRegister(4, "c1") ref = QuantumCircuit(qr, cr0, cr1) ref.x(qr[0]) - ref.x(qr[0]).c_if(cr1, 4) + with self.assertWarns(DeprecationWarning): + ref.x(qr[0]).c_if(cr1, 4) self.assertEqual(len(q_circuit.cregs), 2) self.assertEqual(len(q_circuit.qregs), 1) diff --git a/test/python/qasm2/test_export.py b/test/python/qasm2/test_export.py index 8de4bb8eb34f..ef6ab8076ef6 100644 --- a/test/python/qasm2/test_export.py +++ b/test/python/qasm2/test_export.py @@ -44,9 +44,12 @@ def test_basic_output(self): qc.barrier(qr2) qc.cx(qr2[1], qr1[0]) qc.h(qr2[1]) - qc.x(qr2[1]).c_if(cr, 0) - qc.y(qr1[0]).c_if(cr, 1) - qc.z(qr1[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + qc.x(qr2[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.y(qr1[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.z(qr1[0]).c_if(cr, 2) qc.barrier(qr1, qr2) qc.measure(qr1[0], cr[0]) qc.measure(qr2[0], cr[1]) @@ -616,7 +619,8 @@ def test_rotation_angles_close_to_pi(self): def test_raises_on_single_bit_condition(self): qc = QuantumCircuit(1, 1) - qc.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, True) with self.assertRaisesRegex( qasm2.QASM2ExportError, "OpenQASM 2 can only condition on registers" diff --git a/test/python/qasm2/test_structure.py b/test/python/qasm2/test_structure.py index a5e5bbd77329..8f3c6aaabd75 100644 --- a/test/python/qasm2/test_structure.py +++ b/test/python/qasm2/test_structure.py @@ -256,11 +256,14 @@ def test_conditioned(self): if (cond == 0) U(0, 0, 0) q[0]; if (cond == 1) CX q[1], q[0]; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) cond = ClassicalRegister(1, "cond") qc = QuantumCircuit(QuantumRegister(2, "q"), cond) - qc.u(0, 0, 0, 0).c_if(cond, 0) - qc.cx(1, 0).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, 0).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 0).c_if(cond, 1) self.assertEqual(parsed, qc) def test_conditioned_broadcast(self): @@ -271,15 +274,20 @@ def test_conditioned_broadcast(self): if (cond == 0) U(0, 0, 0) q1; if (cond == 1) CX q1[0], q2; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) cond = ClassicalRegister(1, "cond") q1 = QuantumRegister(2, "q1") q2 = QuantumRegister(2, "q2") qc = QuantumCircuit(q1, q2, cond) - qc.u(0, 0, 0, q1[0]).c_if(cond, 0) - qc.u(0, 0, 0, q1[1]).c_if(cond, 0) - qc.cx(q1[0], q2[0]).c_if(cond, 1) - qc.cx(q1[0], q2[1]).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, q1[0]).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, q1[1]).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(q1[0], q2[0]).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(q1[0], q2[1]).c_if(cond, 1) self.assertEqual(parsed, qc) def test_constant_folding(self): @@ -338,19 +346,29 @@ def test_huge_conditions(self): if (cond=={bigint}) measure qr[0] -> cr[0]; if (cond=={bigint}) measure qr -> cr; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) qr, cr = QuantumRegister(2, "qr"), ClassicalRegister(2, "cr") cond = ClassicalRegister(500, "cond") qc = QuantumCircuit(qr, cr, cond) - qc.u(0, 0, 0, qr[0]).c_if(cond, bigint) - qc.u(0, 0, 0, qr[0]).c_if(cond, bigint) - qc.u(0, 0, 0, qr[1]).c_if(cond, bigint) - qc.reset(qr[0]).c_if(cond, bigint) - qc.reset(qr[0]).c_if(cond, bigint) - qc.reset(qr[1]).c_if(cond, bigint) - qc.measure(qr[0], cr[0]).c_if(cond, bigint) - qc.measure(qr[0], cr[0]).c_if(cond, bigint) - qc.measure(qr[1], cr[1]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, qr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, qr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.u(0, 0, 0, qr[1]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.reset(qr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.reset(qr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.reset(qr[1]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.measure(qr[0], cr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.measure(qr[0], cr[0]).c_if(cond, bigint) + with self.assertWarns(DeprecationWarning): + qc.measure(qr[1], cr[1]).c_if(cond, bigint) self.assertEqual(parsed, qc) @@ -383,14 +401,16 @@ def test_conditioned(self): creg cond[1]; if (cond == 0) not_bell q[0], q[1]; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) not_bell_def = QuantumCircuit([Qubit(), Qubit()]) not_bell_def.u(0, 0, 0, 0) not_bell_def.cx(0, 1) not_bell = gate_builder("not_bell", [], not_bell_def) cond = ClassicalRegister(1, "cond") qc = QuantumCircuit(QuantumRegister(2, "q"), cond) - qc.append(not_bell().c_if(cond, 0), [0, 1]) + with self.assertWarns(DeprecationWarning): + qc.append(not_bell().c_if(cond, 0), [0, 1]) self.assertEqual(parsed, qc) def test_constant_folding_in_definition(self): @@ -735,21 +755,25 @@ def test_deepcopy_conditioned_defined_gate(self): creg c[1]; if (c == 1) my_gate q[0]; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) my_gate = parsed.data[0].operation self.assertEqual(my_gate.name, "my_gate") - self.assertEqual(my_gate.condition, (parsed.cregs[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(my_gate.condition, (parsed.cregs[0], 1)) copied = copy.deepcopy(parsed) copied_gate = copied.data[0].operation self.assertEqual(copied_gate.name, "my_gate") - self.assertEqual(copied_gate.condition, (copied.cregs[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(copied_gate.condition, (copied.cregs[0], 1)) pickled = pickle.loads(pickle.dumps(parsed)) pickled_gate = pickled.data[0].operation self.assertEqual(pickled_gate.name, "my_gate") - self.assertEqual(pickled_gate.condition, (pickled.cregs[0], 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(pickled_gate.condition, (pickled.cregs[0], 1)) class TestOpaque(QiskitTestCase): @@ -904,12 +928,16 @@ def test_conditioned(self): if (cond == 0) measure q[0] -> c[0]; if (cond == 1) measure q -> c; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) cond = ClassicalRegister(1, "cond") qc = QuantumCircuit(QuantumRegister(2, "q"), ClassicalRegister(2, "c"), cond) - qc.measure(0, 0).c_if(cond, 0) - qc.measure(0, 0).c_if(cond, 1) - qc.measure(1, 1).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.measure(0, 0).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.measure(0, 0).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.measure(1, 1).c_if(cond, 1) self.assertEqual(parsed, qc) def test_broadcast_against_empty_register(self): @@ -991,12 +1019,16 @@ def test_conditioned(self): if (cond == 0) reset q[0]; if (cond == 1) reset q; """ - parsed = qiskit.qasm2.loads(program) + with self.assertWarns(DeprecationWarning): + parsed = qiskit.qasm2.loads(program) cond = ClassicalRegister(1, "cond") qc = QuantumCircuit(QuantumRegister(2, "q"), cond) - qc.reset(0).c_if(cond, 0) - qc.reset(0).c_if(cond, 1) - qc.reset(1).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.reset(0).c_if(cond, 0) + with self.assertWarns(DeprecationWarning): + qc.reset(0).c_if(cond, 1) + with self.assertWarns(DeprecationWarning): + qc.reset(1).c_if(cond, 1) self.assertEqual(parsed, qc) def test_broadcast_against_empty_register(self): diff --git a/test/python/qasm3/test_export.py b/test/python/qasm3/test_export.py index eefc0a2cc53c..acae1d7d3cd7 100644 --- a/test/python/qasm3/test_export.py +++ b/test/python/qasm3/test_export.py @@ -104,9 +104,12 @@ def test_regs_conds_qasm(self): qc.measure(qr1[0], cr[0]) qc.measure(qr2[0], cr[1]) qc.measure(qr2[1], cr[2]) - qc.x(qr2[1]).c_if(cr, 0) - qc.y(qr1[0]).c_if(cr, 1) - qc.z(qr1[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + qc.x(qr2[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.y(qr1[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.z(qr1[0]).c_if(cr, 2) expected_qasm = "\n".join( [ "OPENQASM 3.0;", @@ -723,8 +726,10 @@ def test_teleportation(self): qc.barrier() qc.measure([0, 1], [0, 1]) qc.barrier() - qc.x(2).c_if(qc.clbits[1], 1) - qc.z(2).c_if(qc.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(qc.clbits[1], 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(qc.clbits[0], 1) transpiled = transpile(qc, initial_layout=[0, 1, 2]) expected_qasm = """\ @@ -780,8 +785,10 @@ def test_basis_gates(self): qc.barrier() qc.measure([0, 1], [0, 1]) qc.barrier() - qc.x(2).c_if(qc.clbits[1], 1) - qc.z(2).c_if(qc.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(qc.clbits[1], 1) + with self.assertWarns(DeprecationWarning): + qc.z(2).c_if(qc.clbits[0], 1) transpiled = transpile(qc, initial_layout=[0, 1, 2]) expected_qasm = """\ @@ -2046,11 +2053,15 @@ def test_unusual_conditions(self): qc = QuantumCircuit(3, 2) qc.h(0) qc.measure(0, 0) - qc.measure(1, 1).c_if(0, True) - qc.reset([0, 1]).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.measure(1, 1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.reset([0, 1]).c_if(0, True) with qc.while_loop((qc.clbits[0], True)): - qc.break_loop().c_if(0, True) - qc.continue_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.continue_loop().c_if(0, True) # Terra forbids delay and barrier from being conditioned through `c_if`, but in theory they # should work fine in a dynamic-circuits sense (although what a conditional barrier _means_ # is a whole other kettle of fish). diff --git a/test/python/quantum_info/operators/symplectic/test_clifford.py b/test/python/quantum_info/operators/symplectic/test_clifford.py index e78feaf3b78f..253bc15852b6 100644 --- a/test/python/quantum_info/operators/symplectic/test_clifford.py +++ b/test/python/quantum_info/operators/symplectic/test_clifford.py @@ -387,7 +387,8 @@ def test_barrier_delay_sim(self): def test_from_circuit_with_conditional_gate(self): """Test initialization from circuit with conditional gate.""" qc = QuantumCircuit(2, 1) - qc.h(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(0, 0) qc.cx(0, 1) with self.assertRaises(QiskitError): diff --git a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py index 65f19eb8e44c..3f96cd32e15f 100644 --- a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py +++ b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py @@ -19,7 +19,7 @@ import numpy as np import rustworkx as rx import scipy.sparse -from ddt import ddt +import ddt from qiskit import QiskitError from qiskit.circuit import Parameter, ParameterExpression, ParameterVector @@ -141,19 +141,49 @@ def test_sparse_pauli_op_init(self): self.assertEqual(spp_op, ref_op) -@ddt +@ddt.ddt class TestSparsePauliOpConversions(QiskitTestCase): """Tests SparsePauliOp representation conversions.""" - def test_from_operator(self): + @ddt.data(1, 2, 4) + def test_from_operator_single(self, num_qubits): """Test from_operator methods.""" - for tup in it.product(["I", "X", "Y", "Z"], repeat=2): + for tup in it.product(["I", "X", "Y", "Z"], repeat=num_qubits): label = "".join(tup) with self.subTest(msg=label): spp_op = SparsePauliOp.from_operator(Operator(pauli_mat(label))) np.testing.assert_array_equal(spp_op.coeffs, [1]) self.assertEqual(spp_op.paulis, PauliList(label)) + @ddt.data( + SparsePauliOp.from_sparse_list([("", (), 1.0), ("X", (0,), -2.0j)], num_qubits=1), + SparsePauliOp.from_sparse_list([("", (), 1.0), ("Y", (0,), -2.0j)], num_qubits=1), + SparsePauliOp.from_sparse_list([("Y", (0,), 1.0), ("Z", (0,), -2.0j)], num_qubits=1), + SparsePauliOp.from_sparse_list( + [("Y", (0,), 1.0), ("YY", (1, 0), -0.5), ("YYY", (2, 1, 0), 1j)], num_qubits=3 + ), + SparsePauliOp.from_sparse_list( + [("XZ", (2, 0), 1.0), ("YZ", (1, 0), -0.5), ("ZZ", (2, 1), 1j)], num_qubits=3 + ), + ) + def test_from_operator_roundtrip(self, op): + """Test `SparsePauliOp.from_operator` roundtrips things correctly.""" + # Ensure canonical order of the input. Part of this test is ensuring that the output is + # given in canonical order too. The coefficients in the inputs are chosen to be simple + # multiples of powers of two, so there are no floating-point rounding or associativity + # concerns. + op = op.simplify().sort() + roundtrip = SparsePauliOp.from_operator(op.to_matrix()) + self.assertEqual(roundtrip, op) + + def test_from_operator_tolerance(self): + """Test that terms whose coefficient falls below the tolerance are removed.""" + operator = SparsePauliOp.from_list( + [("IIXI", 0.25), ("IIZI", -0.25j), ("IXYI", 0.5j)] + ).to_matrix() + expected = SparsePauliOp.from_list([("IXYI", 0.5j)]) + self.assertEqual(SparsePauliOp.from_operator(operator, 0.26), expected) + def test_from_list(self): """Test from_list method.""" labels = ["XXZ", "IXI", "YZZ", "III"] @@ -416,7 +446,7 @@ def bind_one(a): return np.vectorize(bind_one, otypes=[complex])(array) -@ddt +@ddt.ddt class TestSparsePauliOpMethods(QiskitTestCase): """Tests for SparsePauliOp operator methods.""" diff --git a/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py b/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py index 3f093d18c517..38f84492ee86 100644 --- a/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py +++ b/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py @@ -304,7 +304,8 @@ def test_circuit_using_clbit(self): circuit.x(0) circuit.delay(100, 0, unit="dt") circuit.measure(0, 0) - circuit.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(1).c_if(0, 1) circuit.measure(2, 0) timed_circuit = self.time_conversion_pass(circuit) @@ -320,7 +321,8 @@ def test_circuit_using_clbit(self): ref_circuit.delay(1872, 1, unit="dt") # 2032 - 160 ref_circuit.delay(432, 2, unit="dt") # 2032 - 1600 ref_circuit.measure(0, 0) - ref_circuit.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.x(1).c_if(0, 1) ref_circuit.delay(160, 0, unit="dt") ref_circuit.measure(2, 0) diff --git a/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py b/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py index 417ff9b42212..a13ca2e12de1 100644 --- a/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py +++ b/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py @@ -82,7 +82,8 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass): """ qc = QuantumCircuit(2, 1) qc.measure(0, 0) - qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) with self.assertWarns(DeprecationWarning): @@ -92,7 +93,8 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass): expected = QuantumCircuit(2, 1) expected.measure(0, 0) expected.delay(1000, 1) # x.c_if starts after measure - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) expected.delay(200, 0) self.assertEqual(expected, scheduled) @@ -170,8 +172,10 @@ def test_c_if_on_different_qubits(self, schedule_pass): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, True) - qc.x(2).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(0, True) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) with self.assertWarns(DeprecationWarning): @@ -182,8 +186,10 @@ def test_c_if_on_different_qubits(self, schedule_pass): expected.measure(0, 0) expected.delay(1000, 1) expected.delay(1000, 2) - expected.x(1).c_if(0, True) - expected.x(2).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(2).c_if(0, True) expected.delay(200, 0) self.assertEqual(expected, scheduled) @@ -255,7 +261,8 @@ def test_measure_after_c_if(self, schedule_pass): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.measure(2, 0) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) @@ -267,7 +274,8 @@ def test_measure_after_c_if(self, schedule_pass): expected.delay(1000, 1) expected.delay(1000, 2) expected.measure(0, 0) - expected.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, 1) expected.measure(2, 0) expected.delay(1000, 0) expected.delay(800, 1) @@ -451,7 +459,8 @@ def test_measure_after_c_if_on_edge_locking(self): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.measure(2, 0) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) @@ -465,7 +474,8 @@ def test_measure_after_c_if_on_edge_locking(self): expected_asap = QuantumCircuit(3, 1) expected_asap.measure(0, 0) expected_asap.delay(1000, 1) - expected_asap.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_asap.x(1).c_if(0, 1) expected_asap.measure(2, 0) expected_asap.delay(200, 0) expected_asap.delay(200, 2) @@ -474,7 +484,8 @@ def test_measure_after_c_if_on_edge_locking(self): expected_alap = QuantumCircuit(3, 1) expected_alap.measure(0, 0) expected_alap.delay(1000, 1) - expected_alap.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_alap.x(1).c_if(0, 1) expected_alap.delay(200, 2) expected_alap.measure(2, 0) expected_alap.delay(200, 0) @@ -500,11 +511,14 @@ def test_active_reset_circuit(self, write_lat, cond_lat): """ qc = QuantumCircuit(1, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) durations = InstructionDurations([("x", None, 100), ("measure", None, 1000)]) with self.assertWarns(DeprecationWarning): @@ -519,15 +533,18 @@ def test_active_reset_circuit(self, write_lat, cond_lat): expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) self.assertEqual(expected, actual_asap) self.assertEqual(expected, actual_alap) @@ -616,15 +633,19 @@ def test_random_complicated_circuit(self): """ qc = QuantumCircuit(3, 1) qc.delay(100, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.barrier() qc.measure(2, 0) - qc.x(1).c_if(0, 0) - qc.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 0) qc.delay(300, 0) qc.cx(1, 2) qc.x(0) - qc.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 0) qc.measure(2, 0) durations = InstructionDurations( @@ -644,19 +665,23 @@ def test_random_complicated_circuit(self): expected_asap.delay(100, 0) # due to conditional latency of 200dt expected_asap.delay(300, 1) expected_asap.delay(300, 2) - expected_asap.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_asap.x(0).c_if(0, 1) expected_asap.barrier() expected_asap.delay(1400, 0) expected_asap.delay(1200, 1) expected_asap.measure(2, 0) - expected_asap.x(1).c_if(0, 0) - expected_asap.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.x(0).c_if(0, 0) expected_asap.delay(300, 0) expected_asap.x(0) expected_asap.delay(300, 2) expected_asap.cx(1, 2) expected_asap.delay(400, 1) - expected_asap.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.cx(0, 1).c_if(0, 0) expected_asap.delay(700, 0) # creg is released at t0 of cx(0,1).c_if(0,0) expected_asap.delay( 700, 1 @@ -671,20 +696,24 @@ def test_random_complicated_circuit(self): expected_alap.delay(100, 0) # due to conditional latency of 200dt expected_alap.delay(300, 1) expected_alap.delay(300, 2) - expected_alap.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_alap.x(0).c_if(0, 1) expected_alap.barrier() expected_alap.delay(1400, 0) expected_alap.delay(1200, 1) expected_alap.measure(2, 0) - expected_alap.x(1).c_if(0, 0) - expected_alap.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.x(0).c_if(0, 0) expected_alap.delay(300, 0) expected_alap.x(0) expected_alap.delay(300, 1) expected_alap.delay(600, 2) expected_alap.cx(1, 2) expected_alap.delay(100, 1) - expected_alap.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.cx(0, 1).c_if(0, 0) expected_alap.measure(2, 0) expected_alap.delay(700, 0) expected_alap.delay(700, 1) @@ -722,8 +751,10 @@ def test_dag_introduces_extra_dependency_between_conditionals(self): """ qc = QuantumCircuit(2, 1) qc.delay(100, 0) - qc.x(0).c_if(0, True) - qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) durations = InstructionDurations([("x", None, 160)]) with self.assertWarns(DeprecationWarning): @@ -733,8 +764,10 @@ def test_dag_introduces_extra_dependency_between_conditionals(self): expected = QuantumCircuit(2, 1) expected.delay(100, 0) expected.delay(100, 1) # due to extra dependency on clbits - expected.x(0).c_if(0, True) - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) self.assertEqual(expected, scheduled) diff --git a/test/python/transpiler/test_barrier_before_final_measurements.py b/test/python/transpiler/test_barrier_before_final_measurements.py index eb4f509e5301..3aaa5c7895cd 100644 --- a/test/python/transpiler/test_barrier_before_final_measurements.py +++ b/test/python/transpiler/test_barrier_before_final_measurements.py @@ -175,13 +175,15 @@ def test_preserve_measure_for_conditional(self): circuit.h(qr0) circuit.measure(qr0, cr0) - circuit.z(qr1).c_if(cr0, 1) + with self.assertWarns(DeprecationWarning): + circuit.z(qr1).c_if(cr0, 1) circuit.measure(qr1, cr1) expected = QuantumCircuit(qr0, qr1, cr0, cr1) expected.h(qr0) expected.measure(qr0, cr0) - expected.z(qr1).c_if(cr0, 1) + with self.assertWarns(DeprecationWarning): + expected.z(qr1).c_if(cr0, 1) expected.barrier(qr0, qr1) expected.measure(qr1, cr1) @@ -377,17 +379,23 @@ def test_conditioned_on_single_bit(self): circuit = QuantumCircuit(QuantumRegister(3), ClassicalRegister(2), [Clbit()]) circuit.h(range(3)) circuit.measure(range(3), range(3)) - circuit.h(0).c_if(circuit.cregs[0], 3) - circuit.h(1).c_if(circuit.clbits[-1], True) - circuit.h(2).c_if(circuit.clbits[-1], False) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(circuit.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + circuit.h(1).c_if(circuit.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + circuit.h(2).c_if(circuit.clbits[-1], False) circuit.measure(range(3), range(3)) expected = circuit.copy_empty_like() expected.h(range(3)) expected.measure(range(3), range(3)) - expected.h(0).c_if(expected.cregs[0], 3) - expected.h(1).c_if(expected.clbits[-1], True) - expected.h(2).c_if(expected.clbits[-1], False) + with self.assertWarns(DeprecationWarning): + expected.h(0).c_if(expected.cregs[0], 3) + with self.assertWarns(DeprecationWarning): + expected.h(1).c_if(expected.clbits[-1], True) + with self.assertWarns(DeprecationWarning): + expected.h(2).c_if(expected.clbits[-1], False) expected.barrier(range(3)) expected.measure(range(3), range(3)) diff --git a/test/python/transpiler/test_basis_translator.py b/test/python/transpiler/test_basis_translator.py index 9dae6c3f283a..820c4f241a1e 100644 --- a/test/python/transpiler/test_basis_translator.py +++ b/test/python/transpiler/test_basis_translator.py @@ -576,9 +576,12 @@ def test_unroll_1q_chain_conditional(self): circuit.rz(0.3, qr) circuit.rx(0.1, qr) circuit.measure(qr, cr) - circuit.x(qr).c_if(cr, 1) - circuit.y(qr).c_if(cr, 1) - circuit.z(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.y(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.z(qr).c_if(cr, 1) dag = circuit_to_dag(circuit) pass_ = UnrollCustomDefinitions(std_eqlib, ["u1", "u2", "u3"]) dag = pass_.run(dag) @@ -609,9 +612,12 @@ def test_unroll_1q_chain_conditional(self): ref_circuit.append(U1Gate(0.3), [qr[0]]) ref_circuit.append(U3Gate(0.1, -pi / 2, pi / 2), [qr[0]]) ref_circuit.measure(qr[0], cr[0]) - ref_circuit.append(U3Gate(pi, 0, pi), [qr[0]]).c_if(cr, 1) - ref_circuit.append(U3Gate(pi, pi / 2, pi / 2), [qr[0]]).c_if(cr, 1) - ref_circuit.append(U1Gate(pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U3Gate(pi, 0, pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U3Gate(pi, pi / 2, pi / 2), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U1Gate(pi), [qr[0]]).c_if(cr, 1) ref_dag = circuit_to_dag(ref_circuit) self.assertEqual(unrolled_dag, ref_dag) @@ -1073,7 +1079,8 @@ def test_condition_set_substitute_node(self): circ.h(0) circ.cx(0, 1) circ.measure(1, 1) - circ.h(0).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circ.h(0).c_if(cr, 1) circ_transpiled = transpile(circ, optimization_level=3, basis_gates=["cx", "id", "u"]) # ┌────────────┐ ┌────────────┐ @@ -1089,7 +1096,8 @@ def test_condition_set_substitute_node(self): expected.u(pi / 2, 0, pi, 0) expected.cx(0, 1) expected.measure(1, 1) - expected.u(pi / 2, 0, pi, 0).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + expected.u(pi / 2, 0, pi, 0).c_if(cr, 1) self.assertEqual(circ_transpiled, expected) diff --git a/test/python/transpiler/test_check_map.py b/test/python/transpiler/test_check_map.py index 2a88b272c74d..866eb9f4dfae 100644 --- a/test/python/transpiler/test_check_map.py +++ b/test/python/transpiler/test_check_map.py @@ -268,7 +268,8 @@ def test_nested_conditional_unusual_bit_order(self): # should all be fine. This kind of thing is a staple of the control-flow builders. inner_order = [cr2[0], cr1[0], cr2[1], cr1[1]] inner = QuantumCircuit(qr, inner_order, cr1, cr2) - inner.cx(0, 1).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + inner.cx(0, 1).c_if(cr2, 3) outer = QuantumCircuit(qr, cr1, cr2) outer.if_test((cr1, 3), inner, outer.qubits, inner_order) diff --git a/test/python/transpiler/test_clifford_passes.py b/test/python/transpiler/test_clifford_passes.py index 66a7fd15ad10..2a39c45bc48a 100644 --- a/test/python/transpiler/test_clifford_passes.py +++ b/test/python/transpiler/test_clifford_passes.py @@ -653,7 +653,8 @@ def test_do_not_merge_conditional_gates(self): qc.cx(1, 0) qc.x(0) qc.x(1) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.x(0) qc.x(1) qc.cx(0, 1) @@ -664,7 +665,8 @@ def test_do_not_merge_conditional_gates(self): self.assertEqual(qct.count_ops()["clifford"], 2) # Make sure that the condition on the middle gate is not lost - self.assertIsNotNone(qct.data[1].operation.condition) + with self.assertWarns(DeprecationWarning): + self.assertIsNotNone(qct.data[1].operation.condition) def test_collect_with_cliffords(self): """Make sure that collecting Clifford gates and replacing them by Clifford diff --git a/test/python/transpiler/test_collect_2q_blocks.py b/test/python/transpiler/test_collect_2q_blocks.py index 69efc210a9f6..362765101f08 100644 --- a/test/python/transpiler/test_collect_2q_blocks.py +++ b/test/python/transpiler/test_collect_2q_blocks.py @@ -129,11 +129,15 @@ def test_do_not_merge_conditioned_gates(self): qc = QuantumCircuit(qr, cr) qc.p(0.1, 0) - qc.p(0.2, 0).c_if(cr, 0) - qc.p(0.3, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.p(0.2, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.p(0.3, 0).c_if(cr, 0) qc.cx(0, 1) - qc.cx(1, 0).c_if(cr, 0) - qc.cx(0, 1).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(cr, 1) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) diff --git a/test/python/transpiler/test_collect_multiq_blocks.py b/test/python/transpiler/test_collect_multiq_blocks.py index e2446d03da2a..2d4bc8783764 100644 --- a/test/python/transpiler/test_collect_multiq_blocks.py +++ b/test/python/transpiler/test_collect_multiq_blocks.py @@ -127,7 +127,8 @@ def test_block_with_classical_register(self): if(c0==0) u1(0.25*pi) q[1]; if(c0==0) u2(0.25*pi, 0.25*pi) q[0]; """ - qc = QuantumCircuit.from_qasm_str(qasmstr) + with self.assertWarns(DeprecationWarning): + qc = QuantumCircuit.from_qasm_str(qasmstr) pass_manager = PassManager() pass_manager.append(CollectMultiQBlocks()) @@ -166,11 +167,15 @@ def test_do_not_merge_conditioned_gates(self): qc = QuantumCircuit(qr, cr) qc.p(0.1, 0) - qc.p(0.2, 0).c_if(cr, 0) - qc.p(0.3, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.p(0.2, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.p(0.3, 0).c_if(cr, 0) qc.cx(0, 1) - qc.cx(1, 0).c_if(cr, 0) - qc.cx(0, 1).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(1, 0).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(cr, 1) pass_manager = PassManager() pass_manager.append(CollectMultiQBlocks()) diff --git a/test/python/transpiler/test_commutative_cancellation.py b/test/python/transpiler/test_commutative_cancellation.py index 88f1d99bef31..15b7f6705787 100644 --- a/test/python/transpiler/test_commutative_cancellation.py +++ b/test/python/transpiler/test_commutative_cancellation.py @@ -564,7 +564,8 @@ def test_conditional_gates_dont_commute(self): circuit.h(0) circuit.measure(0, 0) circuit.cx(1, 2) - circuit.cx(1, 2).c_if(circuit.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + circuit.cx(1, 2).c_if(circuit.cregs[0], 0) circuit.measure([1, 2], [0, 1]) new_pm = PassManager(CommutativeCancellation()) @@ -677,8 +678,10 @@ def test_basic_classical_wires(self): """Test that transpile runs without internal errors when dealing with commutable operations with classical controls. Regression test for gh-8553.""" original = QuantumCircuit(2, 1) - original.x(0).c_if(original.cregs[0], 0) - original.x(1).c_if(original.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + original.x(0).c_if(original.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + original.x(1).c_if(original.cregs[0], 0) # This transpilation shouldn't change anything, but it should succeed. At one point it was # triggering an internal logic error and crashing. transpiled = PassManager([CommutativeCancellation()]).run(original) diff --git a/test/python/transpiler/test_commutative_inverse_cancellation.py b/test/python/transpiler/test_commutative_inverse_cancellation.py index c84dfaea3071..e9e85c5505f3 100644 --- a/test/python/transpiler/test_commutative_inverse_cancellation.py +++ b/test/python/transpiler/test_commutative_inverse_cancellation.py @@ -415,7 +415,8 @@ def test_conditional_gates_dont_commute(self, matrix_based): circuit.h(0) circuit.measure(0, 0) circuit.cx(1, 2) - circuit.cx(1, 2).c_if(circuit.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + circuit.cx(1, 2).c_if(circuit.cregs[0], 0) circuit.measure([1, 2], [0, 1]) passmanager = PassManager(CommutativeInverseCancellation(matrix_based=matrix_based)) diff --git a/test/python/transpiler/test_consolidate_blocks.py b/test/python/transpiler/test_consolidate_blocks.py index 1984ad1a3dc4..3b3aff1db228 100644 --- a/test/python/transpiler/test_consolidate_blocks.py +++ b/test/python/transpiler/test_consolidate_blocks.py @@ -328,7 +328,8 @@ def test_classical_conditions_maintained(self): This issue was raised in #2752 """ qc = QuantumCircuit(1, 1) - qc.h(0).c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(qc.cregs[0], 1) qc.measure(0, 0) pass_manager = PassManager() diff --git a/test/python/transpiler/test_convert_conditions_to_if_ops.py b/test/python/transpiler/test_convert_conditions_to_if_ops.py index 799a71163590..d1eadda0d92d 100644 --- a/test/python/transpiler/test_convert_conditions_to_if_ops.py +++ b/test/python/transpiler/test_convert_conditions_to_if_ops.py @@ -26,13 +26,17 @@ def test_simple_loose_bits(self): base = QuantumCircuit(bits) base.h(0) - base.x(0).c_if(0, 1) - base.z(1).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + base.z(1).c_if(1, 0) base.measure(0, 0) base.measure(1, 1) base.h(0) - base.x(0).c_if(0, 1) - base.cx(0, 1).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + base.cx(0, 1).c_if(1, 0) expected = QuantumCircuit(bits) expected.h(0) @@ -48,8 +52,8 @@ def test_simple_loose_bits(self): with expected.if_test((expected.clbits[1], False)): expected.cx(0, 1) expected = canonicalize_control_flow(expected) - - output = PassManager([ConvertConditionsToIfOps()]).run(base) + with self.assertWarns(DeprecationWarning): + output = PassManager([ConvertConditionsToIfOps()]).run(base) self.assertEqual(output, expected) def test_simple_registers(self): @@ -58,13 +62,17 @@ def test_simple_registers(self): base = QuantumCircuit(*registers) base.h(0) - base.x(0).c_if(base.cregs[0], 1) - base.z(1).c_if(base.cregs[1], 0) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(base.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + base.z(1).c_if(base.cregs[1], 0) base.measure(0, 0) base.measure(1, 2) base.h(0) - base.x(0).c_if(base.cregs[0], 1) - base.cx(0, 1).c_if(base.cregs[1], 0) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(base.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + base.cx(0, 1).c_if(base.cregs[1], 0) expected = QuantumCircuit(*registers) expected.h(0) @@ -81,7 +89,8 @@ def test_simple_registers(self): expected.cx(0, 1) expected = canonicalize_control_flow(expected) - output = PassManager([ConvertConditionsToIfOps()]).run(base) + with self.assertWarns(DeprecationWarning): + output = PassManager([ConvertConditionsToIfOps()]).run(base) self.assertEqual(output, expected) def test_nested_control_flow(self): @@ -91,14 +100,18 @@ def test_nested_control_flow(self): registers = [QuantumRegister(3), ClassicalRegister(2)] base = QuantumCircuit(*registers, bits) - base.x(0).c_if(bits[0], False) + with self.assertWarns(DeprecationWarning): + base.x(0).c_if(bits[0], False) with base.if_test((base.cregs[0], 0)) as else_: - base.z(1).c_if(bits[0], False) + with self.assertWarns(DeprecationWarning): + base.z(1).c_if(bits[0], False) with else_: - base.z(1).c_if(base.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + base.z(1).c_if(base.cregs[0], 1) with base.for_loop(range(2)): with base.while_loop((base.cregs[0], 1)): - base.cx(1, 2).c_if(base.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + base.cx(1, 2).c_if(base.cregs[0], 1) base = canonicalize_control_flow(base) expected = QuantumCircuit(*registers, bits) @@ -115,8 +128,8 @@ def test_nested_control_flow(self): with expected.if_test((expected.cregs[0], 1)): expected.cx(1, 2) expected = canonicalize_control_flow(expected) - - output = PassManager([ConvertConditionsToIfOps()]).run(base) + with self.assertWarns(DeprecationWarning): + output = PassManager([ConvertConditionsToIfOps()]).run(base) self.assertEqual(output, expected) def test_no_op(self): @@ -135,5 +148,6 @@ def test_no_op(self): with base.while_loop((base.cregs[0], 1)): base.cx(1, 2) base = canonicalize_control_flow(base) - output = PassManager([ConvertConditionsToIfOps()]).run(base) + with self.assertWarns(DeprecationWarning): + output = PassManager([ConvertConditionsToIfOps()]).run(base) self.assertEqual(output, base) diff --git a/test/python/transpiler/test_decompose.py b/test/python/transpiler/test_decompose.py index 64f08ec52682..64a9b97440ba 100644 --- a/test/python/transpiler/test_decompose.py +++ b/test/python/transpiler/test_decompose.py @@ -117,15 +117,19 @@ def test_decompose_conditional(self): qr = QuantumRegister(1, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr).c_if(cr, 1) - circuit.x(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr).c_if(cr, 1) dag = circuit_to_dag(circuit) pass_ = Decompose(HGate) after_dag = pass_.run(dag) ref_circuit = QuantumCircuit(qr, cr) - ref_circuit.append(U2Gate(0, pi), [qr[0]]).c_if(cr, 1) - ref_circuit.x(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U2Gate(0, pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.x(qr).c_if(cr, 1) ref_dag = circuit_to_dag(ref_circuit) self.assertEqual(after_dag, ref_dag) diff --git a/test/python/transpiler/test_elide_permutations.py b/test/python/transpiler/test_elide_permutations.py index 6f3051cec0d6..edef139af2ee 100644 --- a/test/python/transpiler/test_elide_permutations.py +++ b/test/python/transpiler/test_elide_permutations.py @@ -180,7 +180,8 @@ def test_swap_condition(self): """Test swap elision doesn't touch conditioned swap.""" qc = QuantumCircuit(3, 3) qc.h(0) - qc.swap(0, 1).c_if(qc.clbits[0], 0) + with self.assertWarns(DeprecationWarning): + qc.swap(0, 1).c_if(qc.clbits[0], 0) qc.cx(0, 1) res = self.swap_pass(qc) self.assertEqual(res, qc) diff --git a/test/python/transpiler/test_gate_direction.py b/test/python/transpiler/test_gate_direction.py index c22ad4d58b41..41734d1ca8ee 100644 --- a/test/python/transpiler/test_gate_direction.py +++ b/test/python/transpiler/test_gate_direction.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Test the CX Direction pass""" +"""Test the Gate Direction pass""" import unittest from math import pi @@ -65,9 +65,9 @@ def test_direction_error(self): """The mapping cannot be fixed by direction mapper qr0:--------- - qr1:---(+)--- + qr1:----.----- | - qr2:----.---- + qr2:---(+)---- CouplingMap map: [2] <- [0] -> [1] """ @@ -84,9 +84,9 @@ def test_direction_error(self): def test_direction_correct(self): """The CX is in the right direction - qr0:---(+)--- + qr0:----.---- | - qr1:----.---- + qr1:---(+)---- CouplingMap map: [0] -> [1] """ @@ -103,9 +103,9 @@ def test_direction_correct(self): def test_multi_register(self): """The CX is in the right direction - qr0:---(+)--- + qr0:----.----- | - qr1:----.---- + qr1:---(+)---- CouplingMap map: [0] -> [1] """ @@ -123,15 +123,15 @@ def test_multi_register(self): def test_direction_flip(self): """Flip a CX - qr0:----.---- + qr0:---(+)--- | - qr1:---(+)--- + qr1:----.---- CouplingMap map: [0] -> [1] - qr0:-[H]-(+)-[H]-- + qr0:-[H]--.--[H]-- | - qr1:-[H]--.--[H]-- + qr1:-[H]-(+)--[H]-- """ qr = QuantumRegister(2, "qr") circuit = QuantumCircuit(qr) @@ -243,8 +243,10 @@ def test_preserves_conditions(self): cr = ClassicalRegister(1, "c") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[0], qr[1]).c_if(cr, 0) - circuit.cx(qr[1], qr[0]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[0], qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[1], qr[0]).c_if(cr, 0) circuit.cx(qr[0], qr[1]) circuit.cx(qr[1], qr[0]) @@ -261,16 +263,22 @@ def test_preserves_conditions(self): # c: 1/╡ 0x0 ╞╡ 0x0 ╞╡ 0x0 ╞╡ 0x0 ╞╡ 0x0 ╞╡ 0x0 ╞════════════════════ # └─────┘└─────┘└─────┘└─────┘└─────┘└─────┘ expected = QuantumCircuit(qr, cr) - expected.cx(qr[0], qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(qr[0], qr[1]).c_if(cr, 0) # Order of H gates is important because DAG comparison will consider # different conditional order on a creg to be a different circuit. # See https://github.com/Qiskit/qiskit-terra/issues/3164 - expected.h(qr[1]).c_if(cr, 0) - expected.h(qr[0]).c_if(cr, 0) - expected.cx(qr[0], qr[1]).c_if(cr, 0) - expected.h(qr[1]).c_if(cr, 0) - expected.h(qr[0]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.h(qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.h(qr[0]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(qr[0], qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.h(qr[1]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + expected.h(qr[0]).c_if(cr, 0) expected.cx(qr[0], qr[1]) expected.h(qr[1]) @@ -484,7 +492,7 @@ def test_target_cannot_flip_message(self): circuit.append(gate, (1, 0)) pass_ = GateDirection(None, target) - with self.assertRaisesRegex(TranspilerError, "'my_2q_gate' would be supported.*"): + with self.assertRaisesRegex(TranspilerError, "my_2q_gate would be supported.*"): pass_(circuit) def test_target_cannot_flip_message_calibrated(self): @@ -500,7 +508,7 @@ def test_target_cannot_flip_message_calibrated(self): circuit.add_calibration(gate, (0, 1), pulse.ScheduleBlock()) pass_ = GateDirection(None, target) - with self.assertRaisesRegex(TranspilerError, "'my_2q_gate' would be supported.*"): + with self.assertRaisesRegex(TranspilerError, "my_2q_gate would be supported.*"): pass_(circuit) def test_target_unknown_gate_message(self): @@ -514,7 +522,7 @@ def test_target_unknown_gate_message(self): circuit.append(gate, (0, 1)) pass_ = GateDirection(None, target) - with self.assertRaisesRegex(TranspilerError, "'my_2q_gate'.*not supported on qubits .*"): + with self.assertRaisesRegex(TranspilerError, "my_2q_gate.*not supported on qubits .*"): pass_(circuit) def test_allows_calibrated_gates_coupling_map(self): diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index 73061854b689..6bac509a90ec 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -1720,9 +1720,12 @@ def test_unroll_1q_chain_conditional(self): circuit.rz(0.3, qr) circuit.rx(0.1, qr) circuit.measure(qr, cr) - circuit.x(qr).c_if(cr, 1) - circuit.y(qr).c_if(cr, 1) - circuit.z(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.y(qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.z(qr).c_if(cr, 1) dag = circuit_to_dag(circuit) pass_ = HighLevelSynthesis(equivalence_library=std_eqlib, basis_gates=["u1", "u2", "u3"]) dag = pass_.run(dag) @@ -1753,9 +1756,12 @@ def test_unroll_1q_chain_conditional(self): ref_circuit.append(U1Gate(0.3), [qr[0]]) ref_circuit.append(U3Gate(0.1, -np.pi / 2, np.pi / 2), [qr[0]]) ref_circuit.measure(qr[0], cr[0]) - ref_circuit.append(U3Gate(np.pi, 0, np.pi), [qr[0]]).c_if(cr, 1) - ref_circuit.append(U3Gate(np.pi, np.pi / 2, np.pi / 2), [qr[0]]).c_if(cr, 1) - ref_circuit.append(U1Gate(np.pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U3Gate(np.pi, 0, np.pi), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U3Gate(np.pi, np.pi / 2, np.pi / 2), [qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + ref_circuit.append(U1Gate(np.pi), [qr[0]]).c_if(cr, 1) ref_dag = circuit_to_dag(ref_circuit) self.assertEqual(unrolled_dag, ref_dag) diff --git a/test/python/transpiler/test_linear_functions_passes.py b/test/python/transpiler/test_linear_functions_passes.py index 1d157c437155..39997ad816ef 100644 --- a/test/python/transpiler/test_linear_functions_passes.py +++ b/test/python/transpiler/test_linear_functions_passes.py @@ -615,7 +615,8 @@ def test_do_not_merge_conditional_gates(self): qc = QuantumCircuit(2, 1) qc.cx(1, 0) qc.swap(1, 0) - qc.cx(0, 1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 1) qc.cx(0, 1) qc.cx(1, 0) @@ -625,7 +626,8 @@ def test_do_not_merge_conditional_gates(self): self.assertEqual(qct.count_ops()["linear_function"], 2) # Make sure that the condition on the middle gate is not lost - self.assertIsNotNone(qct.data[1].operation.condition) + with self.assertWarns(DeprecationWarning): + self.assertIsNotNone(qct.data[1].operation.condition) @combine(do_commutative_analysis=[False, True]) def test_split_layers(self, do_commutative_analysis): diff --git a/test/python/transpiler/test_optimize_1q_decomposition.py b/test/python/transpiler/test_optimize_1q_decomposition.py index 06aab474d611..fb043ff9d950 100644 --- a/test/python/transpiler/test_optimize_1q_decomposition.py +++ b/test/python/transpiler/test_optimize_1q_decomposition.py @@ -212,16 +212,18 @@ def test_ignores_conditional_rotations(self, basis): qr = QuantumRegister(1, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.p(0.1, qr).c_if(cr, 1) - circuit.p(0.2, qr).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.p(0.1, qr).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.p(0.2, qr).c_if(cr, 3) circuit.p(0.3, qr) circuit.p(0.4, qr) passmanager = PassManager() passmanager.append(Optimize1qGatesDecomposition(basis)) result = passmanager.run(circuit) - - self.assertTrue(Operator(circuit).equiv(Operator(result))) + with self.assertWarns(DeprecationWarning): + self.assertTrue(Operator(circuit).equiv(Operator(result))) @ddt.data( ["cx", "u3"], diff --git a/test/python/transpiler/test_optimize_1q_gates.py b/test/python/transpiler/test_optimize_1q_gates.py index e5483dd47499..bfa578826510 100644 --- a/test/python/transpiler/test_optimize_1q_gates.py +++ b/test/python/transpiler/test_optimize_1q_gates.py @@ -162,15 +162,19 @@ def test_ignores_conditional_rotations(self): qr = QuantumRegister(1, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(U1Gate(0.1), [qr]).c_if(cr, 1) - circuit.append(U1Gate(0.2), [qr]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0.1), [qr]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0.2), [qr]).c_if(cr, 3) circuit.append(U1Gate(0.3), [qr]) circuit.append(U1Gate(0.4), [qr]) dag = circuit_to_dag(circuit) expected = QuantumCircuit(qr, cr) - expected.append(U1Gate(0.1), [qr]).c_if(cr, 1) - expected.append(U1Gate(0.2), [qr]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + expected.append(U1Gate(0.1), [qr]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + expected.append(U1Gate(0.2), [qr]).c_if(cr, 3) expected.append(U1Gate(0.7), [qr]) pass_ = Optimize1qGates() @@ -190,15 +194,19 @@ def test_ignores_conditional_rotations_phase_gates(self): qr = QuantumRegister(1, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(PhaseGate(0.1), [qr]).c_if(cr, 1) - circuit.append(PhaseGate(0.2), [qr]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.append(PhaseGate(0.1), [qr]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(PhaseGate(0.2), [qr]).c_if(cr, 3) circuit.append(PhaseGate(0.3), [qr]) circuit.append(PhaseGate(0.4), [qr]) dag = circuit_to_dag(circuit) expected = QuantumCircuit(qr, cr) - expected.append(PhaseGate(0.1), [qr]).c_if(cr, 1) - expected.append(PhaseGate(0.2), [qr]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + expected.append(PhaseGate(0.1), [qr]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + expected.append(PhaseGate(0.2), [qr]).c_if(cr, 3) expected.append(PhaseGate(0.7), [qr]) pass_ = Optimize1qGates(["p", "u2", "u", "cx", "id"]) diff --git a/test/python/transpiler/test_optimize_swap_before_measure.py b/test/python/transpiler/test_optimize_swap_before_measure.py index 6d7c06f410a7..10455318bd09 100644 --- a/test/python/transpiler/test_optimize_swap_before_measure.py +++ b/test/python/transpiler/test_optimize_swap_before_measure.py @@ -363,12 +363,14 @@ def test_no_optimize_swap_with_condition(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.swap(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.swap(qr[0], qr[1]).c_if(cr, 1) circuit.measure(qr[0], cr[0]) dag = circuit_to_dag(circuit) expected = QuantumCircuit(qr, cr) - expected.swap(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + expected.swap(qr[0], qr[1]).c_if(cr, 1) expected.measure(qr[0], cr[0]) pass_ = OptimizeSwapBeforeMeasure() diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 37b34f7fae10..7bc23b01e841 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -1214,7 +1214,8 @@ def test_optimization_condition(self, level): qr = QuantumRegister(2) cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr) - qc.cx(0, 1).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(cr, 1) backend = GenericBackendV2( num_qubits=20, coupling_map=TOKYO_CMAP, @@ -1227,7 +1228,8 @@ def test_optimization_condition(self, level): def test_input_dag_copy(self): """Test substitute_node_with_dag input_dag copy on condition""" qc = QuantumCircuit(2, 1) - qc.cx(0, 1).c_if(qc.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(qc.cregs[0], 1) qc.cx(1, 0) circ = transpile(qc, basis_gates=["u3", "cz"]) self.assertIsInstance(circ, QuantumCircuit) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index 474a586448b8..f560aa2c4855 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -550,7 +550,8 @@ def test_do_not_optimize_with_conditional(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.rz(0.1, qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.rz(0.1, qr[1]).c_if(cr, 1) circuit.barrier() circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) diff --git a/test/python/transpiler/test_remove_final_measurements.py b/test/python/transpiler/test_remove_final_measurements.py index 4bc0e107109b..a91687bd8f3e 100644 --- a/test/python/transpiler/test_remove_final_measurements.py +++ b/test/python/transpiler/test_remove_final_measurements.py @@ -57,7 +57,8 @@ def expected_dag(): q0 = QuantumRegister(1, "q0") c0 = ClassicalRegister(1, "c0") qc = QuantumCircuit(q0, c0) - qc.x(0).c_if(c0[0], 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(c0[0], 0) return circuit_to_dag(qc) q0 = QuantumRegister(1, "q0") @@ -65,7 +66,8 @@ def expected_dag(): qc = QuantumCircuit(q0, c0) # make c0 busy - qc.x(0).c_if(c0[0], 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(c0[0], 0) # measure into c0 qc.measure(0, c0[0]) @@ -87,7 +89,8 @@ def expected_dag(): q0 = QuantumRegister(1, "q0") c0 = ClassicalRegister(2, "c0") qc = QuantumCircuit(q0, c0) - qc.x(q0[0]).c_if(c0[0], 0) + with self.assertWarns(DeprecationWarning): + qc.x(q0[0]).c_if(c0[0], 0) return circuit_to_dag(qc) q0 = QuantumRegister(1, "q0") @@ -95,7 +98,8 @@ def expected_dag(): qc = QuantumCircuit(q0, c0) # make c0[0] busy - qc.x(q0[0]).c_if(c0[0], 0) + with self.assertWarns(DeprecationWarning): + qc.x(q0[0]).c_if(c0[0], 0) # measure into not busy c0[1] qc.measure(0, c0[1]) diff --git a/test/python/transpiler/test_reset_after_measure_simplification.py b/test/python/transpiler/test_reset_after_measure_simplification.py index 38f602443c06..976801e71353 100644 --- a/test/python/transpiler/test_reset_after_measure_simplification.py +++ b/test/python/transpiler/test_reset_after_measure_simplification.py @@ -26,12 +26,13 @@ def test_simple(self): qc = QuantumCircuit(1, 1) qc.measure(0, 0) qc.reset(0) - - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) ans_qc = QuantumCircuit(1, 1) ans_qc.measure(0, 0) - ans_qc.x(0).c_if(ans_qc.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + ans_qc.x(0).c_if(ans_qc.clbits[0], 1) self.assertEqual(new_qc, ans_qc) def test_simple_null(self): @@ -52,12 +53,13 @@ def test_simple_multi_reg(self): qc = QuantumCircuit(qr, cr1, cr2) qc.measure(0, 1) qc.reset(0) - - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) ans_qc = QuantumCircuit(qr, cr1, cr2) ans_qc.measure(0, 1) - ans_qc.x(0).c_if(cr2[0], 1) + with self.assertWarns(DeprecationWarning): + ans_qc.x(0).c_if(cr2[0], 1) self.assertEqual(new_qc, ans_qc) @@ -69,7 +71,6 @@ def test_simple_multi_reg_null(self): qc = QuantumCircuit(qr, cr1, cr2) qc.measure(0, 1) qc.reset(1) # reset not on same qubit as meas - new_qc = ResetAfterMeasureSimplification()(qc) self.assertEqual(new_qc, qc) @@ -79,12 +80,13 @@ def test_simple_multi_resets(self): qc.measure(0, 0) qc.reset(0) qc.reset(0) - - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) ans_qc = QuantumCircuit(1, 2) ans_qc.measure(0, 0) - ans_qc.x(0).c_if(ans_qc.clbits[0], 1) + with self.assertWarns(DeprecationWarning): + ans_qc.x(0).c_if(ans_qc.clbits[0], 1) ans_qc.reset(0) self.assertEqual(new_qc, ans_qc) @@ -96,11 +98,13 @@ def test_simple_multi_resets_with_resets_before_measure(self): qc.reset(1) qc.measure(1, 1) - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) ans_qc = QuantumCircuit(2, 2) ans_qc.measure(0, 0) - ans_qc.x(0).c_if(Clbit(ClassicalRegister(2, "c"), 0), 1) + with self.assertWarns(DeprecationWarning): + ans_qc.x(0).c_if(Clbit(ClassicalRegister(2, "c"), 0), 1) ans_qc.reset(1) ans_qc.measure(1, 1) @@ -134,7 +138,8 @@ def test_bv_circuit(self): qc.reset(1) qc.x(1) qc.h(1) - new_qc = ResetAfterMeasureSimplification()(qc) + with self.assertWarns(DeprecationWarning): + new_qc = ResetAfterMeasureSimplification()(qc) for op in new_qc.data: if op.operation.name == "reset": self.assertEqual(op.qubits[0], new_qc.qubits[1]) @@ -149,7 +154,8 @@ def test_simple_if_else(self): base_expected = QuantumCircuit(1, 1) base_expected.measure(0, 0) - base_expected.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + base_expected.x(0).c_if(0, True) test = QuantumCircuit(1, 1) test.if_else( @@ -165,7 +171,8 @@ def test_simple_if_else(self): expected.clbits, ) - self.assertEqual(pass_(test), expected) + with self.assertWarns(DeprecationWarning): + self.assertEqual(pass_(test), expected) def test_nested_control_flow(self): """Test that the pass recurses into nested control flow.""" @@ -177,7 +184,8 @@ def test_nested_control_flow(self): base_expected = QuantumCircuit(1, 1) base_expected.measure(0, 0) - base_expected.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + base_expected.x(0).c_if(0, True) body_test = QuantumCircuit(1, 1) body_test.for_loop((0,), None, base_expected.copy(), body_test.qubits, body_test.clbits) @@ -194,5 +202,5 @@ def test_nested_control_flow(self): expected.while_loop( (expected.clbits[0], True), body_expected, expected.qubits, expected.clbits ) - - self.assertEqual(pass_(test), expected) + with self.assertWarns(DeprecationWarning): + self.assertEqual(pass_(test), expected) diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index d196e4982ea9..7cf86356ab1f 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -293,7 +293,8 @@ def test_classical_condition(self): with self.subTest("1 bit in register"): qc = QuantumCircuit(2, 1) qc.z(0) - qc.z(0).c_if(qc.cregs[0], 0) + with self.assertWarns(DeprecationWarning): + qc.z(0).c_if(qc.cregs[0], 0) cm = CouplingMap([(0, 1), (1, 0)]) expected = PassManager([TrivialLayout(cm)]).run(qc) actual = PassManager([TrivialLayout(cm), SabreSwap(cm)]).run(qc) @@ -302,8 +303,10 @@ def test_classical_condition(self): cregs = [ClassicalRegister(3), ClassicalRegister(4)] qc = QuantumCircuit(QuantumRegister(2, name="q"), *cregs) qc.z(0) - qc.z(0).c_if(cregs[0], 0) - qc.z(0).c_if(cregs[1], 0) + with self.assertWarns(DeprecationWarning): + qc.z(0).c_if(cregs[0], 0) + with self.assertWarns(DeprecationWarning): + qc.z(0).c_if(cregs[1], 0) cm = CouplingMap([(0, 1), (1, 0)]) expected = PassManager([TrivialLayout(cm)]).run(qc) actual = PassManager([TrivialLayout(cm), SabreSwap(cm)]).run(qc) @@ -316,40 +319,54 @@ def test_classical_condition_cargs(self): """ with self.subTest("missing measurement"): qc = QuantumCircuit(3, 1) - qc.cx(0, 2).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 2).c_if(0, 0) qc.measure(1, 0) - qc.h(2).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.h(2).c_if(0, 0) expected = QuantumCircuit(3, 1) expected.swap(1, 2) - expected.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(0, 1).c_if(0, 0) expected.measure(2, 0) - expected.h(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.h(1).c_if(0, 0) result = SabreSwap(CouplingMap.from_line(3), seed=12345)(qc) self.assertEqual(result, expected) with self.subTest("reordered measurement"): qc = QuantumCircuit(3, 1) - qc.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 0) qc.measure(1, 0) - qc.h(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.h(0).c_if(0, 0) expected = QuantumCircuit(3, 1) - expected.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(0, 1).c_if(0, 0) expected.measure(1, 0) - expected.h(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.h(0).c_if(0, 0) result = SabreSwap(CouplingMap.from_line(3), seed=12345)(qc) self.assertEqual(result, expected) def test_conditional_measurement(self): """Test that instructions with cargs and conditions are handled correctly.""" qc = QuantumCircuit(3, 2) - qc.cx(0, 2).c_if(0, 0) - qc.measure(2, 0).c_if(1, 0) - qc.h(2).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 2).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.measure(2, 0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + qc.h(2).c_if(0, 0) qc.measure(1, 1) expected = QuantumCircuit(3, 2) expected.swap(1, 2) - expected.cx(0, 1).c_if(0, 0) - expected.measure(1, 0).c_if(1, 0) - expected.h(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected.measure(1, 0).c_if(1, 0) + with self.assertWarns(DeprecationWarning): + expected.h(1).c_if(0, 0) expected.measure(2, 1) result = SabreSwap(CouplingMap.from_line(3), seed=12345)(qc) self.assertEqual(result, expected) diff --git a/test/python/transpiler/test_scheduling_padding_pass.py b/test/python/transpiler/test_scheduling_padding_pass.py index a1ae04d5e68e..f5ebc349d814 100644 --- a/test/python/transpiler/test_scheduling_padding_pass.py +++ b/test/python/transpiler/test_scheduling_padding_pass.py @@ -114,7 +114,8 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass): """ qc = QuantumCircuit(2, 1) qc.measure(0, 0) - qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) pm = PassManager([schedule_pass(durations), PadDelay()]) @@ -123,7 +124,8 @@ def test_classically_controlled_gate_after_measure(self, schedule_pass): expected = QuantumCircuit(2, 1) expected.measure(0, 0) expected.delay(1000, 1) # x.c_if starts after measure - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) expected.delay(200, 0) self.assertEqual(expected, scheduled) @@ -200,8 +202,10 @@ def test_c_if_on_different_qubits(self, schedule_pass): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, True) - qc.x(2).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(2).c_if(0, True) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) pm = PassManager([schedule_pass(durations), PadDelay()]) @@ -211,8 +215,10 @@ def test_c_if_on_different_qubits(self, schedule_pass): expected.measure(0, 0) expected.delay(1000, 1) expected.delay(1000, 2) - expected.x(1).c_if(0, True) - expected.x(2).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(2).c_if(0, True) expected.delay(200, 0) self.assertEqual(expected, scheduled) @@ -283,7 +289,8 @@ def test_measure_after_c_if(self, schedule_pass): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.measure(2, 0) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) @@ -294,7 +301,8 @@ def test_measure_after_c_if(self, schedule_pass): expected.delay(1000, 1) expected.delay(1000, 2) expected.measure(0, 0) - expected.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, 1) expected.measure(2, 0) expected.delay(1000, 0) expected.delay(800, 1) @@ -474,7 +482,8 @@ def test_measure_after_c_if_on_edge_locking(self): """ qc = QuantumCircuit(3, 1) qc.measure(0, 0) - qc.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 1) qc.measure(2, 0) durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) @@ -499,7 +508,8 @@ def test_measure_after_c_if_on_edge_locking(self): expected_asap = QuantumCircuit(3, 1) expected_asap.measure(0, 0) expected_asap.delay(1000, 1) - expected_asap.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_asap.x(1).c_if(0, 1) expected_asap.measure(2, 0) expected_asap.delay(200, 0) expected_asap.delay(200, 2) @@ -508,7 +518,8 @@ def test_measure_after_c_if_on_edge_locking(self): expected_alap = QuantumCircuit(3, 1) expected_alap.measure(0, 0) expected_alap.delay(1000, 1) - expected_alap.x(1).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_alap.x(1).c_if(0, 1) expected_alap.delay(200, 2) expected_alap.measure(2, 0) expected_alap.delay(200, 0) @@ -534,11 +545,14 @@ def test_active_reset_circuit(self, write_lat, cond_lat): """ qc = QuantumCircuit(1, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.measure(0, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) durations = InstructionDurations([("x", None, 100), ("measure", None, 1000)]) @@ -562,15 +576,18 @@ def test_active_reset_circuit(self, write_lat, cond_lat): expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) expected.measure(0, 0) if cond_lat > 0: expected.delay(cond_lat, 0) - expected.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, 1) self.assertEqual(expected, actual_asap) self.assertEqual(expected, actual_alap) @@ -659,15 +676,19 @@ def test_random_complicated_circuit(self): """ qc = QuantumCircuit(3, 1) qc.delay(100, 0) - qc.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 1) qc.barrier() qc.measure(2, 0) - qc.x(1).c_if(0, 0) - qc.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, 0) qc.delay(300, 0) qc.cx(1, 2) qc.x(0) - qc.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + qc.cx(0, 1).c_if(0, 0) qc.measure(2, 0) durations = InstructionDurations( @@ -694,19 +715,23 @@ def test_random_complicated_circuit(self): expected_asap.delay(200, 0) # due to conditional latency of 200dt expected_asap.delay(300, 1) expected_asap.delay(300, 2) - expected_asap.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_asap.x(0).c_if(0, 1) expected_asap.barrier() expected_asap.delay(1400, 0) expected_asap.delay(1200, 1) expected_asap.measure(2, 0) - expected_asap.x(1).c_if(0, 0) - expected_asap.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.x(0).c_if(0, 0) expected_asap.delay(300, 0) expected_asap.x(0) expected_asap.delay(300, 2) expected_asap.cx(1, 2) expected_asap.delay(400, 1) - expected_asap.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_asap.cx(0, 1).c_if(0, 0) expected_asap.delay(700, 0) # creg is released at t0 of cx(0,1).c_if(0,0) expected_asap.delay( 700, 1 @@ -720,20 +745,24 @@ def test_random_complicated_circuit(self): expected_alap.delay(200, 0) # due to conditional latency of 200dt expected_alap.delay(300, 1) expected_alap.delay(300, 2) - expected_alap.x(0).c_if(0, 1) + with self.assertWarns(DeprecationWarning): + expected_alap.x(0).c_if(0, 1) expected_alap.barrier() expected_alap.delay(1400, 0) expected_alap.delay(1200, 1) expected_alap.measure(2, 0) - expected_alap.x(1).c_if(0, 0) - expected_alap.x(0).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.x(1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.x(0).c_if(0, 0) expected_alap.delay(300, 0) expected_alap.x(0) expected_alap.delay(300, 1) expected_alap.delay(600, 2) expected_alap.cx(1, 2) expected_alap.delay(100, 1) - expected_alap.cx(0, 1).c_if(0, 0) + with self.assertWarns(DeprecationWarning): + expected_alap.cx(0, 1).c_if(0, 0) expected_alap.measure(2, 0) expected_alap.delay(700, 0) expected_alap.delay(700, 1) @@ -771,8 +800,10 @@ def test_dag_introduces_extra_dependency_between_conditionals(self): """ qc = QuantumCircuit(2, 1) qc.delay(100, 0) - qc.x(0).c_if(0, True) - qc.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + qc.x(1).c_if(0, True) durations = InstructionDurations([("x", None, 160)]) pm = PassManager([ASAPScheduleAnalysis(durations), PadDelay()]) @@ -781,8 +812,10 @@ def test_dag_introduces_extra_dependency_between_conditionals(self): expected = QuantumCircuit(2, 1) expected.delay(100, 0) expected.delay(100, 1) # due to extra dependency on clbits - expected.x(0).c_if(0, True) - expected.x(1).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(0).c_if(0, True) + with self.assertWarns(DeprecationWarning): + expected.x(1).c_if(0, True) self.assertEqual(expected, scheduled) diff --git a/test/python/transpiler/test_unroll_3q_or_more.py b/test/python/transpiler/test_unroll_3q_or_more.py index 27927bbbd4ad..15a90cdf7759 100644 --- a/test/python/transpiler/test_unroll_3q_or_more.py +++ b/test/python/transpiler/test_unroll_3q_or_more.py @@ -62,7 +62,8 @@ def test_decompose_conditional(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 0) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 0) dag = circuit_to_dag(circuit) pass_ = Unroll3qOrMore() after_dag = pass_.run(dag) @@ -70,7 +71,8 @@ def test_decompose_conditional(self): self.assertEqual(len(op_nodes), 15) for node in op_nodes: self.assertIn(node.name, ["h", "t", "tdg", "cx"]) - self.assertEqual(node.op.condition, (cr, 0)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(node.op.condition, (cr, 0)) def test_decompose_unitary(self): """Test unrolling of unitary gate over 4qubits.""" diff --git a/test/python/transpiler/test_unroll_forloops.py b/test/python/transpiler/test_unroll_forloops.py index cbf8f70ef767..a44655903370 100644 --- a/test/python/transpiler/test_unroll_forloops.py +++ b/test/python/transpiler/test_unroll_forloops.py @@ -135,7 +135,8 @@ def test_skip_continue_c_if(self): circuit.h(0) circuit.cx(0, 1) circuit.measure(0, 0) - circuit.break_loop().c_if(0, True) + with self.assertWarns(DeprecationWarning): + circuit.break_loop().c_if(0, True) passmanager = PassManager() passmanager.append(UnrollForLoops()) diff --git a/test/python/visualization/test_circuit_drawer.py b/test/python/visualization/test_circuit_drawer.py index dd69faac02cb..9eb4e3ad2c88 100644 --- a/test/python/visualization/test_circuit_drawer.py +++ b/test/python/visualization/test_circuit_drawer.py @@ -151,7 +151,8 @@ def test_wire_order(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 10) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 10) expected = "\n".join( [ @@ -183,7 +184,8 @@ def test_wire_order_cregbundle(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 10) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 10) expected = "\n".join( [ diff --git a/test/python/visualization/test_circuit_latex.py b/test/python/visualization/test_circuit_latex.py index bcf5b77d51bd..8f33f1aa3ce8 100644 --- a/test/python/visualization/test_circuit_latex.py +++ b/test/python/visualization/test_circuit_latex.py @@ -99,7 +99,8 @@ def test_4597(self): qr = QuantumRegister(3, "q") cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) - circuit.x(qr[2]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[2]).c_if(cr, 2) circuit.draw(output="latex_source", cregbundle=True) circuit_drawer(circuit, filename=filename, output="latex_source") @@ -148,8 +149,10 @@ def test_teleport(self): circuit.measure(qr[0], cr[0]) circuit.measure(qr[1], cr[1]) # Apply a correction - circuit.z(qr[2]).c_if(cr, 1) - circuit.x(qr[2]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.z(qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[2]).c_if(cr, 2) circuit.measure(qr[2], cr[2]) circuit_drawer(circuit, filename=filename, output="latex_source") @@ -206,7 +209,8 @@ def test_conditional(self): # check gates are shifted over accordingly circuit.h(qr) circuit.measure(qr, cr) - circuit.h(qr[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 2) circuit_drawer(circuit, filename=filename, output="latex_source") @@ -570,7 +574,8 @@ def test_meas_condition(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) circuit_drawer(circuit, filename=filename, output="latex_source") self.assertEqualToReference(filename) @@ -598,8 +603,10 @@ def test_cif_single_bit(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr[1], 0) - circuit.x(qr[1]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[1]).c_if(cr[0], 1) circuit_drawer(circuit, cregbundle=False, filename=filename, output="latex_source") self.assertEqualToReference(filename) @@ -611,8 +618,10 @@ def test_cif_single_bit_cregbundle(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr[1], 0) - circuit.x(qr[1]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[1]).c_if(cr[0], 1) circuit_drawer(circuit, cregbundle=True, filename=filename, output="latex_source") self.assertEqualToReference(filename) @@ -639,8 +648,10 @@ def test_measures_with_conditions(self): circuit.h(0) circuit.h(1) circuit.measure(0, cr1[1]) - circuit.measure(1, cr2[0]).c_if(cr1, 1) - circuit.h(0).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + circuit.measure(1, cr2[0]).c_if(cr1, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(cr2, 3) circuit_drawer(circuit, cregbundle=False, filename=filename1, output="latex_source") circuit_drawer(circuit, cregbundle=True, filename=filename2, output="latex_source") self.assertEqualToReference(filename1) @@ -654,7 +665,8 @@ def test_measures_with_conditions_with_bits(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(crx[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(crx[1], 0) circuit.measure(0, bits[3]) circuit_drawer(circuit, cregbundle=False, filename=filename1, output="latex_source") circuit_drawer(circuit, cregbundle=True, filename=filename2, output="latex_source") @@ -668,7 +680,8 @@ def test_conditions_with_bits_reverse(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(bits[3], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(bits[3], 0) circuit_drawer( circuit, cregbundle=False, reverse_bits=True, filename=filename, output="latex_source" ) @@ -680,7 +693,8 @@ def test_sidetext_with_condition(self): qr = QuantumRegister(2, "q") cr = ClassicalRegister(2, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) circuit_drawer(circuit, cregbundle=False, filename=filename, output="latex_source") self.assertEqualToReference(filename) @@ -703,7 +717,8 @@ def test_wire_order(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 12) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 12) circuit_drawer( circuit, cregbundle=False, diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py index e7d28aac8a9e..666255c44f23 100644 --- a/test/python/visualization/test_circuit_text_drawer.py +++ b/test/python/visualization/test_circuit_text_drawer.py @@ -388,7 +388,8 @@ def test_wire_order(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 10) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 10) self.assertEqual( str( circuit_drawer( @@ -845,7 +846,8 @@ def test_text_cu1_condition(self): qr = QuantumRegister(3, "q") cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(CU1Gate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU1Gate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_rzz_condition(self): @@ -866,7 +868,8 @@ def test_text_rzz_condition(self): qr = QuantumRegister(3, "q") cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(RZZGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(RZZGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_cp_condition(self): @@ -887,7 +890,8 @@ def test_text_cp_condition(self): qr = QuantumRegister(3, "q") cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_cu1_reverse_bits(self): @@ -1758,7 +1762,8 @@ def test_control_gate_label_with_cond_1_low(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1789,7 +1794,8 @@ def test_control_gate_label_with_cond_1_low_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1824,7 +1830,8 @@ def test_control_gate_label_with_cond_1_med(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1860,7 +1867,8 @@ def test_control_gate_label_with_cond_1_med_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1895,7 +1903,8 @@ def test_control_gate_label_with_cond_1_high(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1930,7 +1939,8 @@ def test_control_gate_label_with_cond_1_high_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) self.assertEqual( @@ -1966,7 +1976,8 @@ def test_control_gate_label_with_cond_2_med_space(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -1998,7 +2009,8 @@ def test_control_gate_label_with_cond_2_med(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ctrl-h").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ctrl-h").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -2034,7 +2046,8 @@ def test_control_gate_label_with_cond_2_med_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -2071,7 +2084,8 @@ def test_control_gate_label_with_cond_2_low(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -2108,7 +2122,8 @@ def test_control_gate_label_with_cond_2_low_cregbundle(self): cr = ClassicalRegister(1, "c") circ = QuantumCircuit(qr, cr) hgate = HGate(label="my h") - controlh = hgate.control(label="my ch").c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) self.assertEqual( @@ -2269,8 +2284,8 @@ def test_text_conditional_1(self): " 0x1 ", ] ) - - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2309,7 +2324,8 @@ def test_text_conditional_1_bundle(self): ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2335,7 +2351,8 @@ def test_text_conditional_reverse_bits_true(self): circuit.x(0) circuit.x(0) circuit.measure(2, 1) - circuit.x(2).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2).c_if(cr, 2) expected = "\n".join( [ @@ -2386,7 +2403,8 @@ def test_text_conditional_reverse_bits_false(self): circuit.x(0) circuit.x(0) circuit.measure(2, 1) - circuit.x(2).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2).c_if(cr, 2) expected = "\n".join( [ @@ -2486,7 +2504,8 @@ def test_text_conditional_1(self): " 0x1 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2523,8 +2542,8 @@ def test_text_conditional_1_bundle(self): " └─────┘", ] ) - - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2565,7 +2584,8 @@ def test_text_measure_with_spaces(self): " 0x1 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2603,7 +2623,8 @@ def test_text_measure_with_spaces_bundle(self): " 1 └─────┘", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2694,8 +2715,10 @@ def test_text_barrier_med_compress_3(self): qc1 = ClassicalRegister(3, "cr") qc2 = ClassicalRegister(1, "cr2") circuit = QuantumCircuit(qr, qc1, qc2) - circuit.x(0).c_if(qc1, 3) - circuit.x(0).c_if(qc2[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(qc1, 3) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(qc2[0], 1) expected = "\n".join( [ @@ -2753,8 +2776,8 @@ def test_text_conditional_1_cregbundle(self): " └─────┘", ] ) - - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2790,8 +2813,8 @@ def test_text_conditional_1(self): " 0x1 ", ] ) - - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), expected, @@ -2819,7 +2842,8 @@ def test_text_conditional_2_cregbundle(self): " └─────┘", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2859,7 +2883,8 @@ def test_text_conditional_2(self): " 0x2 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), expected, @@ -2887,7 +2912,8 @@ def test_text_conditional_3_cregbundle(self): " └─────┘", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -2931,7 +2957,8 @@ def test_text_conditional_3(self): " 0x3 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), expected, @@ -2959,7 +2986,8 @@ def test_text_conditional_4(self): " └─────┘", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str( circuit_drawer( @@ -3007,7 +3035,8 @@ def test_text_conditional_5(self): " 0x5 ", ] ) - circuit = QuantumCircuit.from_qasm_str(qasm_string) + with self.assertWarns(DeprecationWarning): + circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), expected, @@ -3018,7 +3047,8 @@ def test_text_conditional_cz_no_space_cregbundle(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cz(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3042,7 +3072,8 @@ def test_text_conditional_cz_no_space(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cz(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3066,7 +3097,8 @@ def test_text_conditional_cz_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cz(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3092,7 +3124,8 @@ def test_text_conditional_cz(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cz(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3118,7 +3151,8 @@ def test_text_conditional_cx_ct_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3144,7 +3178,8 @@ def test_text_conditional_cx_ct(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3170,7 +3205,8 @@ def test_text_conditional_cx_tc_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[1], qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[1], qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3196,7 +3232,8 @@ def test_text_conditional_cx_tc(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[1], qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cx(qr[1], qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3222,7 +3259,8 @@ def test_text_conditional_cu3_ct_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]).c_if(cr, 1) expected = "\n".join( [ @@ -3248,7 +3286,8 @@ def test_text_conditional_cu3_ct(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]).c_if(cr, 1) expected = "\n".join( [ @@ -3274,7 +3313,8 @@ def test_text_conditional_cu3_tc_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[1], qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[1], qr[0]]).c_if(cr, 1) expected = "\n".join( [ @@ -3300,7 +3340,8 @@ def test_text_conditional_cu3_tc(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[1], qr[0]]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[1], qr[0]]).c_if(cr, 1) expected = "\n".join( [ @@ -3326,7 +3367,8 @@ def test_text_conditional_ccx_cregbundle(self): qr = QuantumRegister(4, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3354,7 +3396,8 @@ def test_text_conditional_ccx(self): qr = QuantumRegister(4, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3382,7 +3425,8 @@ def test_text_conditional_ccx_no_space_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3416,7 +3460,8 @@ def test_text_conditional_ccx_no_space(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3442,7 +3487,8 @@ def test_text_conditional_h_cregbundle(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3466,7 +3512,8 @@ def test_text_conditional_h(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3490,7 +3537,8 @@ def test_text_conditional_swap_cregbundle(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.swap(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.swap(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3516,7 +3564,8 @@ def test_text_conditional_swap(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.swap(qr[0], qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.swap(qr[0], qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3542,7 +3591,8 @@ def test_text_conditional_cswap_cregbundle(self): qr = QuantumRegister(4, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cswap(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cswap(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3570,7 +3620,8 @@ def test_text_conditional_cswap(self): qr = QuantumRegister(4, "qr") cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.cswap(qr[0], qr[1], qr[2]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.cswap(qr[0], qr[1], qr[2]).c_if(cr, 1) expected = "\n".join( [ @@ -3599,7 +3650,8 @@ def test_conditional_reset_cregbundle(self): cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.reset(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.reset(qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3624,7 +3676,8 @@ def test_conditional_reset(self): cr = ClassicalRegister(1, "cr") circuit = QuantumCircuit(qr, cr) - circuit.reset(qr[0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.reset(qr[0]).c_if(cr, 1) expected = "\n".join( [ @@ -3649,7 +3702,8 @@ def test_conditional_multiplexer_cregbundle(self): qr = QuantumRegister(3, name="qr") cr = ClassicalRegister(1, "cr") qc = QuantumCircuit(qr, cr) - qc.append(cx_multiplexer.c_if(cr, 1), [qr[0], qr[1]]) + with self.assertWarns(DeprecationWarning): + qc.append(cx_multiplexer.c_if(cr, 1), [qr[0], qr[1]]) expected = "\n".join( [ @@ -3675,7 +3729,8 @@ def test_conditional_multiplexer(self): qr = QuantumRegister(3, name="qr") cr = ClassicalRegister(1, "cr") qc = QuantumCircuit(qr, cr) - qc.append(cx_multiplexer.c_if(cr, 1), [qr[0], qr[1]]) + with self.assertWarns(DeprecationWarning): + qc.append(cx_multiplexer.c_if(cr, 1), [qr[0], qr[1]]) expected = "\n".join( [ @@ -3702,7 +3757,8 @@ def test_text_conditional_measure_cregbundle(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3736,7 +3792,8 @@ def test_text_conditional_measure(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3762,8 +3819,10 @@ def test_text_bit_conditional(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr[0], 1) - circuit.h(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr[1], 0) expected = "\n".join( [ @@ -3790,8 +3849,10 @@ def test_text_bit_conditional_cregbundle(self): qr = QuantumRegister(2, "qr") cr = ClassicalRegister(2, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr[0], 1) - circuit.h(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr[1], 0) expected = "\n".join( [ @@ -3826,7 +3887,8 @@ def test_text_condition_measure_bits_true(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(crx[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(crx[1], 0) circuit.measure(0, bits[3]) expected = "\n".join( @@ -3860,7 +3922,8 @@ def test_text_condition_measure_bits_false(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(crx[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(crx[1], 0) circuit.measure(0, bits[3]) expected = "\n".join( @@ -3900,7 +3963,8 @@ def test_text_conditional_reverse_bits_1(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) expected = "\n".join( [ @@ -3930,10 +3994,14 @@ def test_text_conditional_reverse_bits_2(self): qr = QuantumRegister(3, "qr") cr = ClassicalRegister(3, "cr") circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 6) - circuit.h(qr[1]).c_if(cr, 1) - circuit.h(qr[2]).c_if(cr, 2) - circuit.cx(0, 1).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 6) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[2]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.cx(0, 1).c_if(cr, 3) expected = "\n".join( [ @@ -3969,7 +4037,8 @@ def test_text_condition_bits_reverse(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(bits[3], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(bits[3], 0) expected = "\n".join( [ @@ -4847,9 +4916,10 @@ def test_cccz_conditional(self): qr = QuantumRegister(4, "q") cr = ClassicalRegister(1, "c") circuit = QuantumCircuit(qr, cr) - circuit.append( - ZGate().control(3, ctrl_state="101").c_if(cr, 1), [qr[0], qr[1], qr[2], qr[3]] - ) + with self.assertWarns(DeprecationWarning): + circuit.append( + ZGate().control(3, ctrl_state="101").c_if(cr, 1), [qr[0], qr[1], qr[2], qr[3]] + ) self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cch_bot(self): @@ -5908,14 +5978,16 @@ def test_if_else_with_body_specified(self): circuit.measure(0, 1) circuit.measure(1, 2) circuit.x(2) - circuit.x(2, label="XLabel").c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2, label="XLabel").c_if(cr, 2) qr2 = QuantumRegister(3, "qr2") circuit2 = QuantumCircuit(qr2, cr) circuit2.x(1) circuit2.y(1) circuit2.z(0) - circuit2.x(0, label="X1i").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit2.x(0, label="X1i").c_if(cr, 4) circuit.if_else((cr[1], 1), circuit2, None, [0, 1, 2], [0, 1, 2]) circuit.x(0, label="X1i") @@ -5981,7 +6053,8 @@ def test_if_op_nested_wire_order(self): circuit.h(0) with circuit.if_test((cr[1], 1)) as _else: - circuit.x(0, label="X c_if").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit.x(0, label="X c_if").c_if(cr, 4) with circuit.if_test((cr[2], 1)): circuit.z(0) circuit.y(1) @@ -6227,12 +6300,15 @@ def test_if_else_op_from_circuit_with_conditions(self): cr = ClassicalRegister(3, "cr") circuit = QuantumCircuit(qr, cr) circuit.h(0) - circuit.x(2).c_if(cr[1], 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2).c_if(cr[1], 2) qr2 = QuantumRegister(3, "qr2") qc2 = QuantumCircuit(qr2, cr) - qc2.x(0, label="X1").c_if(cr, 4) - qc2.x(1, label="X2").c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + qc2.x(0, label="X1").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + qc2.x(1, label="X2").c_if(cr[1], 1) circuit.if_else((cr[1], 1), qc2, None, [0, 1, 2], [0, 1, 2]) self.assertEqual( diff --git a/test/python/visualization/test_dag_drawer.py b/test/python/visualization/test_dag_drawer.py index 56138c12cf00..9c9b11e42a68 100644 --- a/test/python/visualization/test_dag_drawer.py +++ b/test/python/visualization/test_dag_drawer.py @@ -98,7 +98,8 @@ def test_dag_drawer_with_dag_dep(self): qc.cx(0, 1) qc.cx(0, 2) qc.cx(0, 3) - qc.x(3).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + qc.x(3).c_if(cr[1], 1) qc.h(3) qc.x(4) qc.barrier(0, 1) diff --git a/test/python/visualization/test_utils.py b/test/python/visualization/test_utils.py index 1ef87994d001..7e0f91f3f993 100644 --- a/test/python/visualization/test_utils.py +++ b/test/python/visualization/test_utils.py @@ -328,9 +328,13 @@ def test_get_layered_instructions_op_with_cargs(self): qc.h(0) qc.measure(0, 0) qc_2 = QuantumCircuit(1, 1, name="add_circ") - qc_2.h(0).c_if(qc_2.cregs[0], 1) + with self.assertWarns(DeprecationWarning): + qc_2.h(0).c_if(qc_2.cregs[0], 1) qc_2.measure(0, 0) - qc.append(qc_2, [1], [0]) + # This append calls ends up calling .to_instruction() which calls + # .c_if() internally + with self.assertWarns(DeprecationWarning): + qc.append(qc_2, [1], [0]) (_, _, layered_ops) = _utils._get_layered_instructions(qc) diff --git a/test/utils/base.py b/test/utils/base.py index dded6bbda2b2..133666cfc7ad 100644 --- a/test/utils/base.py +++ b/test/utils/base.py @@ -176,6 +176,27 @@ def setUpClass(cls): module="qiskit.providers.fake_provider.fake_backend", ) + warnings.filterwarnings( + "default", + category=DeprecationWarning, + message=r".*The property.*condition.*is deprecated.*", + module="qiskit_aer", + ) + + # Remove with the condition attribute in 2.0: + warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + message=r".*The property.*condition.*is deprecated.*", + module="qiskit.visualization", + ) + warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + message=r".*The property.*condition_bits.*is deprecated.*", + module="qiskit.transpiler.passes.scheduling", + ) + allow_DeprecationWarning_message = [ r"The property ``qiskit\.circuit\.bit\.Bit\.(register|index)`` is deprecated.*", ] diff --git a/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py b/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py index 4da725c4b5d6..b51b61cb7a90 100644 --- a/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py +++ b/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py @@ -343,7 +343,8 @@ def test_conditional(self): # check gates are shifted over accordingly circuit.h(qr) circuit.measure(qr, cr) - circuit.h(qr[0]).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr, 2) fname = "reg_conditional.png" self.circuit_drawer(circuit, output="mpl", filename=fname) @@ -366,8 +367,10 @@ def test_bit_conditional_with_cregbundle(self): circuit.x(qr[0]) circuit.measure(qr, cr) - circuit.h(qr[0]).c_if(cr[0], 1) - circuit.x(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[1]).c_if(cr[1], 0) fname = "bit_conditional_bundle.png" self.circuit_drawer(circuit, output="mpl", filename=fname) @@ -390,8 +393,10 @@ def test_bit_conditional_no_cregbundle(self): circuit.x(qr[0]) circuit.measure(qr, cr) - circuit.h(qr[0]).c_if(cr[0], 1) - circuit.x(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[0]).c_if(cr[0], 1) + with self.assertWarns(DeprecationWarning): + circuit.x(qr[1]).c_if(cr[1], 0) fname = "bit_conditional_no_bundle.png" self.circuit_drawer(circuit, output="mpl", filename=fname, cregbundle=False) @@ -1077,7 +1082,8 @@ def test_meas_condition(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[0]) - circuit.h(qr[1]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr, 1) fname = "meas_condition.png" self.circuit_drawer(circuit, output="mpl", filename=fname) @@ -1103,7 +1109,8 @@ def test_reverse_bits_condition(self): circuit.x(0) circuit.x(0) circuit.measure(2, 1) - circuit.x(2).c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2).c_if(cr, 2) fname = "reverse_bits_cond_true.png" self.circuit_drawer( @@ -1398,8 +1405,10 @@ def test_measures_with_conditions(self): circuit.h(0) circuit.h(1) circuit.measure(0, cr1[1]) - circuit.measure(1, cr2[0]).c_if(cr1, 1) - circuit.h(0).c_if(cr2, 3) + with self.assertWarns(DeprecationWarning): + circuit.measure(1, cr2[0]).c_if(cr1, 1) + with self.assertWarns(DeprecationWarning): + circuit.h(0).c_if(cr2, 3) fname = "measure_cond_false.png" self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) @@ -1431,7 +1440,8 @@ def test_conditions_measures_with_bits(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(3, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(crx[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(crx[1], 0) circuit.measure(0, bits[3]) fname = "measure_cond_bits_false.png" @@ -1465,8 +1475,10 @@ def test_conditional_gates_right_of_measures_with_bits(self): circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.measure(qr[0], cr[1]) - circuit.h(qr[1]).c_if(cr[1], 0) - circuit.h(qr[2]).c_if(cr[0], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[1]).c_if(cr[1], 0) + with self.assertWarns(DeprecationWarning): + circuit.h(qr[2]).c_if(cr[0], 0) fname = "measure_cond_bits_right.png" self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) @@ -1486,7 +1498,8 @@ def test_conditions_with_bits_reverse(self): cr = ClassicalRegister(2, "cr") crx = ClassicalRegister(2, "cs") circuit = QuantumCircuit(bits, cr, [Clbit()], crx) - circuit.x(0).c_if(bits[3], 0) + with self.assertWarns(DeprecationWarning): + circuit.x(0).c_if(bits[3], 0) fname = "cond_bits_reverse.png" self.circuit_drawer( @@ -1507,7 +1520,8 @@ def test_sidetext_with_condition(self): qr = QuantumRegister(2, "q") cr = ClassicalRegister(2, "c") circuit = QuantumCircuit(qr, cr) - circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + with self.assertWarns(DeprecationWarning): + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) fname = "sidetext_condition.png" self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) @@ -1527,22 +1541,38 @@ def test_fold_with_conditions(self): cr = ClassicalRegister(5, "cr") circuit = QuantumCircuit(qr, cr) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 1) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 3) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 5) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 7) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 9) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 11) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 13) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 15) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 17) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 19) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 21) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 23) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 25) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 27) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 29) - circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 31) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 1) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 3) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 5) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 7) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 9) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 11) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 13) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 15) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 17) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 19) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 21) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 23) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 25) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 27) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 29) + with self.assertWarns(DeprecationWarning): + circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 31) fname = "fold_with_conditions.png" self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) @@ -1583,7 +1613,8 @@ def test_wire_order(self): circuit.h(0) circuit.h(3) circuit.x(1) - circuit.x(3).c_if(cr, 10) + with self.assertWarns(DeprecationWarning): + circuit.x(3).c_if(cr, 10) fname = "wire_order.png" self.circuit_drawer( @@ -1732,14 +1763,16 @@ def test_if_else_with_body(self): circuit.measure(0, 1) circuit.measure(1, 2) circuit.x(2) - circuit.x(2, label="XLabel").c_if(cr, 2) + with self.assertWarns(DeprecationWarning): + circuit.x(2, label="XLabel").c_if(cr, 2) qr2 = QuantumRegister(3, "qr2") qc2 = QuantumCircuit(qr2, cr) qc2.x(1) qc2.y(1) qc2.z(0) - qc2.x(0, label="X1i").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + qc2.x(0, label="X1i").c_if(cr, 4) circuit.if_else((cr[1], 1), qc2, None, [0, 1, 2], [0, 1, 2]) circuit.x(0, label="X1i") @@ -1764,7 +1797,8 @@ def test_if_else_op_nested(self): circuit.h(0) with circuit.if_test((cr[1], 1)) as _else: - circuit.x(0, label="X c_if").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit.x(0, label="X c_if").c_if(cr, 4) with circuit.if_test((cr[2], 1)): circuit.z(0) circuit.y(1) @@ -1805,7 +1839,8 @@ def test_if_else_op_wire_order(self): circuit.h(0) with circuit.if_test((cr[1], 1)) as _else: - circuit.x(0, label="X c_if").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit.x(0, label="X c_if").c_if(cr, 4) with circuit.if_test((cr[2], 1)): circuit.z(0) circuit.y(1) @@ -1852,7 +1887,8 @@ def test_if_else_op_fold(self): circuit.h(0) with circuit.if_test((cr[1], 1)) as _else: - circuit.x(0, label="X c_if").c_if(cr, 4) + with self.assertWarns(DeprecationWarning): + circuit.x(0, label="X c_if").c_if(cr, 4) with circuit.if_test((cr[2], 1)): circuit.z(0) circuit.y(1)