Skip to content

Commit 3d4bab2

Browse files
raynelfssElePT
andauthored
Add method to add instructions to a DAGCircuit from an iterator of PackedInstruction (#13032)
* Initial: Add add_from_iter method to DAGCircuit - Introduce a method that adds a chain of `PackedInstruction` continuously avoiding the re-linking of each bit's output-node until the very end of the iterator. - TODO: Add handling of vars - Add header for a `from_iter` function that will create a `DAGCircuit` based on a chain of `PackedInstruction`. * Fix: leverage new methods in layers - Fix incorrect re-insertion of last_node. * Fix: Keep track of Vars for add_from_iter - Remove `from_iter` * Fix: Incorrect modification of last nodes in `add_from_iter`. - Use `entry` api to either modify or insert a value if missing. * Fix: Cycling edges in when adding vars. - A bug that adds duplicate edges to vars has been temporarily fixed. However, the root of this problem hasn't been found yet. A proper fix is pending. For now skip those instances. * Fix: Remove set collecting all nodes to be connected. - A set collecting all the new nodes to connect with a new node was preventing additional wires to connect to subsequent nodes. * Fix: Adapt to #13033 * Refactor: `add_from_iter` is now called `extend` to stick with `Rust` nomenclature. * Fix docstring - Caught by @ElePT Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> * Fix: Remove duplicate vars check * Fix: Corrections from code review. - Use Entry API to modify last nodes in the var. - Build new_nodes with an allocated vec. - Add comment explaining the removal of the edge between the output node and its predecessor. * Fix: Improper use of `Entry API`. - Use `or_insert_with` instead of `or_insert` to perform actions before inserting a value. --------- Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
1 parent 7c40912 commit 3d4bab2

File tree

1 file changed

+138
-3
lines changed

1 file changed

+138
-3
lines changed

crates/circuit/src/dag_circuit.rs

+138-3
Original file line numberDiff line numberDiff line change
@@ -4350,9 +4350,7 @@ def _format(operand):
43504350

43514351
let mut new_layer = self.copy_empty_like(py, vars_mode)?;
43524352

4353-
for (node, _) in op_nodes {
4354-
new_layer.push_back(py, node.clone())?;
4355-
}
4353+
new_layer.extend(py, op_nodes.iter().map(|(inst, _)| (*inst).clone()))?;
43564354

43574355
let new_layer_op_nodes = new_layer.op_nodes(false).filter_map(|node_index| {
43584356
match new_layer.dag.node_weight(node_index) {
@@ -6366,6 +6364,143 @@ impl DAGCircuit {
63666364
Err(DAGCircuitError::new_err("Specified node is not an op node"))
63676365
}
63686366
}
6367+
6368+
/// Extends the DAG with valid instances of [PackedInstruction]
6369+
pub fn extend<I>(&mut self, py: Python, iter: I) -> PyResult<Vec<NodeIndex>>
6370+
where
6371+
I: IntoIterator<Item = PackedInstruction>,
6372+
{
6373+
// Create HashSets to keep track of each bit/var's last node
6374+
let mut qubit_last_nodes: HashMap<Qubit, NodeIndex> = HashMap::default();
6375+
let mut clbit_last_nodes: HashMap<Clbit, NodeIndex> = HashMap::default();
6376+
// TODO: Refactor once Vars are in rust
6377+
// Dict [ Var: (int, VarWeight)]
6378+
let vars_last_nodes: Bound<PyDict> = PyDict::new_bound(py);
6379+
6380+
// Consume into iterator to obtain size hint
6381+
let iter = iter.into_iter();
6382+
// Store new nodes to return
6383+
let mut new_nodes = Vec::with_capacity(iter.size_hint().1.unwrap_or_default());
6384+
for instr in iter {
6385+
let op_name = instr.op.name();
6386+
let (all_cbits, vars): (Vec<Clbit>, Option<Vec<PyObject>>) = {
6387+
if self.may_have_additional_wires(py, &instr) {
6388+
let mut clbits: HashSet<Clbit> =
6389+
HashSet::from_iter(self.cargs_interner.get(instr.clbits).iter().copied());
6390+
let (additional_clbits, additional_vars) =
6391+
self.additional_wires(py, instr.op.view(), instr.condition())?;
6392+
for clbit in additional_clbits {
6393+
clbits.insert(clbit);
6394+
}
6395+
(clbits.into_iter().collect(), Some(additional_vars))
6396+
} else {
6397+
(self.cargs_interner.get(instr.clbits).to_vec(), None)
6398+
}
6399+
};
6400+
6401+
// Increment the operation count
6402+
self.increment_op(op_name);
6403+
6404+
// Get the correct qubit indices
6405+
let qubits_id = instr.qubits;
6406+
6407+
// Insert op-node to graph.
6408+
let new_node = self.dag.add_node(NodeType::Operation(instr));
6409+
new_nodes.push(new_node);
6410+
6411+
// Check all the qubits in this instruction.
6412+
for qubit in self.qargs_interner.get(qubits_id) {
6413+
// Retrieve each qubit's last node
6414+
let qubit_last_node = *qubit_last_nodes.entry(*qubit).or_insert_with(|| {
6415+
// If the qubit is not in the last nodes collection, the edge between the output node and its predecessor.
6416+
// Then, store the predecessor's NodeIndex in the last nodes collection.
6417+
let output_node = self.qubit_io_map[qubit.0 as usize][1];
6418+
let (edge_id, predecessor_node) = self
6419+
.dag
6420+
.edges_directed(output_node, Incoming)
6421+
.next()
6422+
.map(|edge| (edge.id(), edge.source()))
6423+
.unwrap();
6424+
self.dag.remove_edge(edge_id);
6425+
predecessor_node
6426+
});
6427+
qubit_last_nodes
6428+
.entry(*qubit)
6429+
.and_modify(|val| *val = new_node);
6430+
self.dag
6431+
.add_edge(qubit_last_node, new_node, Wire::Qubit(*qubit));
6432+
}
6433+
6434+
// Check all the clbits in this instruction.
6435+
for clbit in all_cbits {
6436+
let clbit_last_node = *clbit_last_nodes.entry(clbit).or_insert_with(|| {
6437+
// If the qubit is not in the last nodes collection, the edge between the output node and its predecessor.
6438+
// Then, store the predecessor's NodeIndex in the last nodes collection.
6439+
let output_node = self.clbit_io_map[clbit.0 as usize][1];
6440+
let (edge_id, predecessor_node) = self
6441+
.dag
6442+
.edges_directed(output_node, Incoming)
6443+
.next()
6444+
.map(|edge| (edge.id(), edge.source()))
6445+
.unwrap();
6446+
self.dag.remove_edge(edge_id);
6447+
predecessor_node
6448+
});
6449+
clbit_last_nodes
6450+
.entry(clbit)
6451+
.and_modify(|val| *val = new_node);
6452+
self.dag
6453+
.add_edge(clbit_last_node, new_node, Wire::Clbit(clbit));
6454+
}
6455+
6456+
// If available, check all the vars in this instruction
6457+
for var in vars.iter().flatten() {
6458+
let var_last_node = if let Some(result) = vars_last_nodes.get_item(var)? {
6459+
let node: usize = result.extract()?;
6460+
vars_last_nodes.del_item(var)?;
6461+
NodeIndex::new(node)
6462+
} else {
6463+
// If the var is not in the last nodes collection, the edge between the output node and its predecessor.
6464+
// Then, store the predecessor's NodeIndex in the last nodes collection.
6465+
let output_node = self.var_output_map.get(py, var).unwrap();
6466+
let (edge_id, predecessor_node) = self
6467+
.dag
6468+
.edges_directed(output_node, Incoming)
6469+
.next()
6470+
.map(|edge| (edge.id(), edge.source()))
6471+
.unwrap();
6472+
self.dag.remove_edge(edge_id);
6473+
predecessor_node
6474+
};
6475+
6476+
vars_last_nodes.set_item(var, new_node.index())?;
6477+
self.dag
6478+
.add_edge(var_last_node, new_node, Wire::Var(var.clone_ref(py)));
6479+
}
6480+
}
6481+
6482+
// Add the output_nodes back to qargs
6483+
for (qubit, node) in qubit_last_nodes {
6484+
let output_node = self.qubit_io_map[qubit.0 as usize][1];
6485+
self.dag.add_edge(node, output_node, Wire::Qubit(qubit));
6486+
}
6487+
6488+
// Add the output_nodes back to cargs
6489+
for (clbit, node) in clbit_last_nodes {
6490+
let output_node = self.clbit_io_map[clbit.0 as usize][1];
6491+
self.dag.add_edge(node, output_node, Wire::Clbit(clbit));
6492+
}
6493+
6494+
// Add the output_nodes back to vars
6495+
for item in vars_last_nodes.items() {
6496+
let (var, node): (PyObject, usize) = item.extract()?;
6497+
let output_node = self.var_output_map.get(py, &var).unwrap();
6498+
self.dag
6499+
.add_edge(NodeIndex::new(node), output_node, Wire::Var(var));
6500+
}
6501+
6502+
Ok(new_nodes)
6503+
}
63696504
}
63706505

63716506
/// Add to global phase. Global phase can only be Float or ParameterExpression so this

0 commit comments

Comments
 (0)