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 {