diff --git a/crates/accelerate/src/two_qubit_decompose.rs b/crates/accelerate/src/two_qubit_decompose.rs index b423b1d65267..2072c5034ff7 100644 --- a/crates/accelerate/src/two_qubit_decompose.rs +++ b/crates/accelerate/src/two_qubit_decompose.rs @@ -398,8 +398,6 @@ impl Specialization { } } -type WeylCircuitSequence = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>; - #[derive(Clone, Debug)] #[allow(non_snake_case)] #[pyclass(module = "qiskit._accelerate.two_qubit_decompose", subclass)] @@ -430,56 +428,49 @@ impl TwoQubitWeylDecomposition { fn weyl_gate( &self, simplify: bool, - sequence: &mut WeylCircuitSequence, + sequence: &mut CircuitData, atol: f64, global_phase: &mut f64, - ) { + ) -> PyResult<()> { match self.specialization { Specialization::MirrorControlledEquiv => { - sequence.push(( - StandardGate::SwapGate, - SmallVec::new(), - smallvec![Qubit(0), Qubit(1)], - )); - sequence.push(( + sequence.push_standard_gate(StandardGate::SwapGate, &[], &[Qubit(0), Qubit(1)])?; + sequence.push_standard_gate( StandardGate::RZZGate, - smallvec![Param::Float((PI4 - self.c) * 2.)], - smallvec![Qubit(0), Qubit(1)], - )); + &[Param::Float((PI4 - self.c) * 2.)], + &[Qubit(0), Qubit(1)], + )?; *global_phase += PI4 } Specialization::SWAPEquiv => { - sequence.push(( - StandardGate::SwapGate, - SmallVec::new(), - smallvec![Qubit(0), Qubit(1)], - )); + sequence.push_standard_gate(StandardGate::SwapGate, &[], &[Qubit(0), Qubit(1)])?; *global_phase -= 3. * PI / 4. } _ => { if !simplify || self.a.abs() > atol { - sequence.push(( + sequence.push_standard_gate( StandardGate::RXXGate, - smallvec![Param::Float(-self.a * 2.)], - smallvec![Qubit(0), Qubit(1)], - )); + &[Param::Float(-self.a * 2.)], + &[Qubit(0), Qubit(1)], + )?; } if !simplify || self.b.abs() > atol { - sequence.push(( + sequence.push_standard_gate( StandardGate::RYYGate, - smallvec![Param::Float(-self.b * 2.)], - smallvec![Qubit(0), Qubit(1)], - )); + &[Param::Float(-self.b * 2.)], + &[Qubit(0), Qubit(1)], + )?; } if !simplify || self.c.abs() > atol { - sequence.push(( + sequence.push_standard_gate( StandardGate::RZZGate, - smallvec![Param::Float(-self.c * 2.)], - smallvec![Qubit(0), Qubit(1)], - )); + &[Param::Float(-self.c * 2.)], + &[Qubit(0), Qubit(1)], + )?; } } } + Ok(()) } /// Instantiate a new TwoQubitWeylDecomposition with rust native @@ -1070,7 +1061,7 @@ impl TwoQubitWeylDecomposition { }; let target_1q_basis_list: Vec = vec![euler_basis]; - let mut gate_sequence: WeylCircuitSequence = Vec::with_capacity(21); + let mut gate_sequence = CircuitData::with_capacity(py, 2, 0, 21, Param::Float(0.))?; let mut global_phase: f64 = self.global_phase; let c2r = unitary_to_gate_sequence_inner( @@ -1083,11 +1074,11 @@ impl TwoQubitWeylDecomposition { ) .unwrap(); for gate in c2r.gates { - gate_sequence.push(( + gate_sequence.push_standard_gate( gate.0, - gate.1.into_iter().map(Param::Float).collect(), - smallvec![Qubit(0)], - )) + &gate.1.into_iter().map(Param::Float).collect::>(), + &[Qubit(0)], + )? } global_phase += c2r.global_phase; let c2l = unitary_to_gate_sequence_inner( @@ -1100,11 +1091,11 @@ impl TwoQubitWeylDecomposition { ) .unwrap(); for gate in c2l.gates { - gate_sequence.push(( + gate_sequence.push_standard_gate( gate.0, - gate.1.into_iter().map(Param::Float).collect(), - smallvec![Qubit(1)], - )) + &gate.1.into_iter().map(Param::Float).collect::>(), + &[Qubit(1)], + )? } global_phase += c2l.global_phase; self.weyl_gate( @@ -1112,7 +1103,7 @@ impl TwoQubitWeylDecomposition { &mut gate_sequence, atol.unwrap_or(ANGLE_ZERO_EPSILON), &mut global_phase, - ); + )?; let c1r = unitary_to_gate_sequence_inner( self.K1r.view(), &target_1q_basis_list, @@ -1123,11 +1114,11 @@ impl TwoQubitWeylDecomposition { ) .unwrap(); for gate in c1r.gates { - gate_sequence.push(( + gate_sequence.push_standard_gate( gate.0, - gate.1.into_iter().map(Param::Float).collect(), - smallvec![Qubit(0)], - )) + &gate.1.into_iter().map(Param::Float).collect::>(), + &[Qubit(0)], + )? } global_phase += c2r.global_phase; let c1l = unitary_to_gate_sequence_inner( @@ -1140,13 +1131,14 @@ impl TwoQubitWeylDecomposition { ) .unwrap(); for gate in c1l.gates { - gate_sequence.push(( + gate_sequence.push_standard_gate( gate.0, - gate.1.into_iter().map(Param::Float).collect(), - smallvec![Qubit(1)], - )) + &gate.1.into_iter().map(Param::Float).collect::>(), + &[Qubit(1)], + )? } - CircuitData::from_standard_gates(py, 2, gate_sequence, Param::Float(global_phase)) + gate_sequence.set_global_phase(py, Param::Float(global_phase))?; + Ok(gate_sequence) } } diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index dd2618f03f9a..d87efd03ac5d 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -15,7 +15,7 @@ use std::cell::RefCell; use crate::bit_data::BitData; use crate::circuit_instruction::{CircuitInstruction, OperationFromPython}; -use crate::imports::{ANNOTATED_OPERATION, QUANTUM_CIRCUIT, QUBIT}; +use crate::imports::{ANNOTATED_OPERATION, CLBIT, QUANTUM_CIRCUIT, QUBIT}; use crate::interner::{IndexedInterner, Interner, InternerKey}; use crate::operations::{Operation, OperationRef, Param, StandardGate}; use crate::packed_instruction::PackedInstruction; @@ -131,22 +131,13 @@ impl CircuitData { I: IntoIterator, SmallVec<[Qubit; 2]>)>, { let instruction_iter = instructions.into_iter(); - let mut res = CircuitData { - data: Vec::with_capacity(instruction_iter.size_hint().0), - qargs_interner: IndexedInterner::new(), - cargs_interner: IndexedInterner::new(), - qubits: BitData::new(py, "qubits".to_string()), - clbits: BitData::new(py, "clbits".to_string()), - param_table: ParameterTable::new(), + let mut res = Self::with_capacity( + py, + num_qubits, + 0, + instruction_iter.size_hint().0, global_phase, - }; - if num_qubits > 0 { - let qubit_cls = QUBIT.get_bound(py); - for _i in 0..num_qubits { - let bit = qubit_cls.call0()?; - res.add_qubit(py, &bit, true)?; - } - } + )?; let no_clbit_index = (&mut res.cargs_interner) .intern(InternerKey::Value(Vec::new()))? .index; @@ -169,6 +160,66 @@ impl CircuitData { Ok(res) } + /// Build an empty CircuitData object with an initially allocated instruction capacity + pub fn with_capacity( + py: Python, + num_qubits: u32, + num_clbits: u32, + instruction_capacity: usize, + global_phase: Param, + ) -> PyResult { + let mut res = CircuitData { + data: Vec::with_capacity(instruction_capacity), + qargs_interner: IndexedInterner::new(), + cargs_interner: IndexedInterner::new(), + qubits: BitData::new(py, "qubits".to_string()), + clbits: BitData::new(py, "clbits".to_string()), + param_table: ParameterTable::new(), + global_phase, + }; + if num_qubits > 0 { + let qubit_cls = QUBIT.get_bound(py); + for _i in 0..num_qubits { + let bit = qubit_cls.call0()?; + res.add_qubit(py, &bit, true)?; + } + } + if num_clbits > 0 { + let clbit_cls = CLBIT.get_bound(py); + for _i in 0..num_clbits { + let bit = clbit_cls.call0()?; + res.add_clbit(py, &bit, true)?; + } + } + Ok(res) + } + + /// Append a standard gate to this CircuitData + pub fn push_standard_gate( + &mut self, + operation: StandardGate, + params: &[Param], + qargs: &[Qubit], + ) -> PyResult<()> { + let no_clbit_index = (&mut self.cargs_interner) + .intern(InternerKey::Value(Vec::new()))? + .index; + let params = (!params.is_empty()).then(|| Box::new(params.iter().cloned().collect())); + let qubits = (&mut self.qargs_interner) + .intern(InternerKey::Value(qargs.to_vec()))? + .index; + self.data.push(PackedInstruction { + op: operation.into(), + qubits, + clbits: no_clbit_index, + params, + extra_attrs: None, + #[cfg(feature = "cache_pygates")] + py_op: RefCell::new(None), + }); + Ok(()) + } + /// Add the entries from the `PackedInstruction` at the given index to the internal parameter /// table. fn track_instruction_parameters(