diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs
index 8e0e73d72ca2..6d1aaf3af987 100644
--- a/crates/circuit/src/dag_circuit.rs
+++ b/crates/circuit/src/dag_circuit.rs
@@ -10,6 +10,7 @@
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.
+use std::cell::RefCell;
use std::hash::{Hash, Hasher};
use ahash::RandomState;
@@ -6933,6 +6934,223 @@ impl DAGCircuit {
}
}
+pub struct DAGCircuitConcat<'a> {
+ dag: RefCell<&'a mut DAGCircuit>,
+ last_clbits: Vec>,
+ last_qubits: Vec >,
+ // TODO: Keep track of vars.
+}
+
+impl<'a> DAGCircuitConcat<'a> {
+ pub fn new(dag: &'a mut DAGCircuit) -> Self {
+ let num_qubits = dag.num_qubits();
+ let num_clbits = dag.num_clbits();
+ Self {
+ dag: dag.into(),
+ last_qubits: Vec::with_capacity(num_qubits),
+ last_clbits: Vec::with_capacity(num_clbits),
+ }
+ }
+
+ pub fn end(self) {
+ // Re-connects all of the output nodes with their respective last nodes.
+ todo!()
+ }
+
+ pub fn apply_operation_back(
+ &self,
+ op: PackedOperation,
+ qubits: &[Qubit],
+ clbits: &[Clbit],
+ params: Option>,
+ extra_attrs: Option,
+ #[cfg(feature = "cache_pygates")] py_op: PyObject,
+ ) {
+ let instruction = self.pack_instruction(
+ op,
+ qubits,
+ clbits,
+ params,
+ extra_attrs,
+ #[cfg(feature = "cache_pygates")]
+ py_op,
+ );
+ todo!()
+ }
+
+ pub fn apply_operation_owned(
+ &self,
+ op: PackedOperation,
+ qubits: Vec,
+ clbits: Vec,
+ params: Option>,
+ extra_attrs: Option,
+ #[cfg(feature = "cache_pygates")] py_op: PyObject,
+ ) {
+ let instruction = self.pack_instruction_owned(op, qubits, clbits, params, extra_attrs);
+ todo!()
+ }
+
+ fn push_back(&self, inst: PackedInstruction) {
+ // let mut dag_mut = self.dag.borrow_mut();
+ // let op_name = inst.op.name();
+ // let (all_cbits, vars): (Vec, Option>) = {
+ // if dag_mut.may_have_additional_wires(py, &inst) {
+ // let mut clbits: HashSet =
+ // HashSet::from_iter(dag_mut.cargs_interner.get(inst.clbits).iter().copied());
+ // let (additional_clbits, additional_vars) =
+ // dag_mut.additional_wires(py, inst.op.view(), inst.condition())?;
+ // for clbit in additional_clbits {
+ // clbits.insert(clbit);
+ // }
+ // (clbits.into_iter().collect(), Some(additional_vars))
+ // } else {
+ // (dag_mut.cargs_interner.get(inst.clbits).to_vec(), None)
+ // }
+ // };
+
+ // // Increment the operation count
+ // dag_mut.increment_op(op_name);
+
+ // // Get the correct qubit indices
+ // let qubits_id = inst.qubits;
+
+ // // Insert op-node to graph.
+ // let new_node = dag_mut.dag.add_node(NodeType::Operation(inst));
+
+ // // Check all the qubits in this instruction.
+ // for qubit in dag_mut.qargs_interner.get(qubits_id) {
+ // // Retrieve each qubit's last node
+ // let qubit_last_node = *qubit_last_nodes.entry(*qubit).or_insert_with(|| {
+ // // If the qubit is not in the last nodes collection, the edge between the output node and its predecessor.
+ // // Then, store the predecessor's NodeIndex in the last nodes collection.
+ // let output_node = dag_mut.qubit_io_map[qubit.0 as usize][1];
+ // let (edge_id, predecessor_node) = dag_mut
+ // .dag
+ // .edges_directed(output_node, Incoming)
+ // .next()
+ // .map(|edge| (edge.id(), edge.source()))
+ // .unwrap();
+ // dag_mut.dag.remove_edge(edge_id);
+ // predecessor_node
+ // });
+ // qubit_last_nodes
+ // .entry(*qubit)
+ // .and_modify(|val| *val = new_node);
+ // dag_mut.dag
+ // .add_edge(qubit_last_node, new_node, Wire::Qubit(*qubit));
+ // }
+
+ // // Check all the clbits in this instruction.
+ // for clbit in all_cbits {
+ // let clbit_last_node = *clbit_last_nodes.entry(clbit).or_insert_with(|| {
+ // // If the qubit is not in the last nodes collection, the edge between the output node and its predecessor.
+ // // Then, store the predecessor's NodeIndex in the last nodes collection.
+ // let output_node = dag_mut.clbit_io_map[clbit.0 as usize][1];
+ // let (edge_id, predecessor_node) = self
+ // .dag
+ // .edges_directed(output_node, Incoming)
+ // .next()
+ // .map(|edge| (edge.id(), edge.source()))
+ // .unwrap();
+ // dag_mut.dag.remove_edge(edge_id);
+ // predecessor_node
+ // });
+ // clbit_last_nodes
+ // .entry(clbit)
+ // .and_modify(|val| *val = new_node);
+ // dag_mut.dag
+ // .add_edge(clbit_last_node, new_node, Wire::Clbit(clbit));
+ // }
+
+ // // If available, check all the vars in this instruction
+ // for var in vars.iter().flatten() {
+ // let var_last_node = if let Some(result) = vars_last_nodes.get_item(var)? {
+ // let node: usize = result.extract()?;
+ // vars_last_nodes.del_item(var)?;
+ // NodeIndex::new(node)
+ // } else {
+ // // If the var is not in the last nodes collection, the edge between the output node and its predecessor.
+ // // Then, store the predecessor's NodeIndex in the last nodes collection.
+ // let output_node = dag_mut.var_output_map.get(py, var).unwrap();
+ // let (edge_id, predecessor_node) = self
+ // .dag
+ // .edges_directed(output_node, Incoming)
+ // .next()
+ // .map(|edge| (edge.id(), edge.source()))
+ // .unwrap();
+ // dag_mut.dag.remove_edge(edge_id);
+ // predecessor_node
+ // };
+
+ // // Because `DAGCircuit::additional_wires` can return repeated instances of vars,
+ // // we need to make sure to skip those to avoid cycles.
+ // vars_last_nodes.set_item(var, new_node.index())?;
+ // if var_last_node == new_node {
+ // continue;
+ // }
+ // dag_mut.dag
+ // .add_edge(var_last_node, new_node, Wire::Var(var.clone_ref(py)));
+ // }
+ }
+
+ #[inline]
+ fn pack_instruction(
+ &self,
+ op: PackedOperation,
+ qubits: &[Qubit],
+ clbits: &[Clbit],
+ params: Option>,
+ extra_attrs: Option,
+ #[cfg(feature = "cache_pygates")] py_op: PyObject,
+ ) -> PackedInstruction {
+ let extra_attrs = extra_attrs.unwrap_or_default();
+ #[cfg(feature = "cache_pygates")]
+ let py_op = if let Some(py_op) = py_op {
+ py_op.into()
+ } else {
+ OnceCell::new()
+ };
+ PackedInstruction {
+ op,
+ qubits: self.dag.borrow_mut().qargs_interner.insert(qubits),
+ clbits: self.dag.borrow_mut().cargs_interner.insert(clbits),
+ params: params.map(|x| x.into()),
+ extra_attrs,
+ #[cfg(feature = "cache_pygates")]
+ py_op,
+ }
+ }
+
+ #[inline]
+ fn pack_instruction_owned(
+ &self,
+ op: PackedOperation,
+ qubits: Vec,
+ clbits: Vec,
+ params: Option>,
+ extra_attrs: Option,
+ #[cfg(feature = "cache_pygates")] py_op: PyObject,
+ ) -> PackedInstruction {
+ let extra_attrs = extra_attrs.unwrap_or_default();
+ #[cfg(feature = "cache_pygates")]
+ let py_op = if let Some(py_op) = py_op {
+ py_op.into()
+ } else {
+ OnceCell::new()
+ };
+ PackedInstruction {
+ op,
+ qubits: self.dag.borrow_mut().qargs_interner.insert_owned(qubits),
+ clbits: self.dag.borrow_mut().cargs_interner.insert_owned(clbits),
+ params: params.map(|x| x.into()),
+ extra_attrs,
+ #[cfg(feature = "cache_pygates")]
+ py_op,
+ }
+ }
+}
+
/// Add to global phase. Global phase can only be Float or ParameterExpression so this
/// does not handle the full possibility of parameter values.
fn add_global_phase(py: Python, phase: &Param, other: &Param) -> PyResult {