1use crate::circuit::circuit_param::{CircuitParam, ParameterValue};
22use crate::circuit::gate::{ClassicalDataOp, Directive, Instruction};
23use crate::circuit::value_instruction::storage_operation_to_value;
24use crate::circuit::{
25 Circuit, CircuitError, ClassicalControlOp, ClassicalExpr, ClassicalType, ClassicalValue,
26 ClassicalVar, ControlBody, ForOp, IfOp, Operation, Parameter, Qubit, SwitchCase, SwitchOp,
27 ValueClassicalControlOp, ValueControlBody, ValueInstruction, ValueOperation, WhileOp,
28};
29use indexmap::{IndexMap, IndexSet};
30use rustworkx_core::dag_algo::lexicographical_topological_sort;
31use rustworkx_core::petgraph::Direction;
32use rustworkx_core::petgraph::algo::toposort;
33use rustworkx_core::petgraph::prelude::{NodeIndex, StableDiGraph};
34use rustworkx_core::petgraph::visit::{EdgeRef, IntoEdgeReferences};
35use std::collections::{HashMap, HashSet};
36use std::convert::Infallible;
37use std::sync::OnceLock;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41pub enum DagWire {
42 Qubit(Qubit),
44 ClassicalVar(ClassicalVar),
46 ClassicalValue(ClassicalValue),
48 GlobalOrder,
50}
51
52#[derive(Debug, Clone)]
54pub enum DagNode {
55 WireIn(DagWire),
57 WireOut(DagWire),
59 Operation {
61 operation: Operation,
63 order: usize,
65 },
66}
67
68#[derive(Debug, Clone)]
70pub enum DagControlFlow {
71 If {
73 then_body: Box<CircuitDag>,
74 else_body: Option<Box<CircuitDag>>,
75 },
76 While { body: Box<CircuitDag> },
78 For { body: Box<CircuitDag> },
80 Switch {
82 cases: Vec<DagSwitchCase>,
83 default: Option<Box<CircuitDag>>,
84 },
85 Break,
87 Continue,
89}
90
91#[derive(Debug, Clone)]
93pub struct DagSwitchCase {
94 pub value: u128,
95 pub body: Box<CircuitDag>,
96}
97
98#[derive(Debug, Clone)]
100pub struct CircuitDag {
101 qubits: IndexSet<Qubit>,
102 symbols: IndexSet<String>,
103 parameters: IndexSet<Parameter>,
104 classical_vars: Vec<ClassicalType>,
105 classical_values: Vec<ClassicalType>,
106 global_phase: CircuitParam,
107 graph: StableDiGraph<DagNode, DagWire>,
108 wire_io: IndexMap<DagWire, [NodeIndex; 2]>,
109 control_flow: HashMap<NodeIndex, DagControlFlow>,
110 order_to_node: Vec<NodeIndex>,
111 topological_op_nodes_cache: OnceLock<Vec<NodeIndex>>,
112 node_layers_cache: OnceLock<IndexMap<NodeIndex, usize>>,
113}
114
115impl CircuitDag {
116 pub fn from_circuit(circuit: &Circuit) -> Result<Self, CircuitError> {
118 let mut dag = Self {
119 qubits: circuit.qubits().into_iter().collect(),
120 symbols: circuit.symbols().clone(),
121 parameters: circuit.parameters().clone(),
122 classical_vars: circuit.classical_vars().to_vec(),
123 classical_values: circuit.classical_values().to_vec(),
124 global_phase: circuit.global_phase_param().clone(),
125 graph: StableDiGraph::new(),
126 wire_io: IndexMap::new(),
127 control_flow: HashMap::new(),
128 order_to_node: Vec::with_capacity(circuit.operations().len()),
129 topological_op_nodes_cache: OnceLock::new(),
130 node_layers_cache: OnceLock::new(),
131 };
132 dag.initialize_wires();
133 dag.add_operations(circuit.operations())?;
134 dag.validate()?;
135 Ok(dag)
136 }
137
138 pub fn from_operations(
160 qubits: impl IntoIterator<Item = Qubit>,
161 operations: &[Operation],
162 ) -> Result<Self, CircuitError> {
163 let mut qubit_set = IndexSet::new();
164 for qubit in qubits {
165 if !qubit_set.insert(qubit) {
166 return Err(CircuitError::DuplicateQubits);
167 }
168 }
169 let mut dag = Self {
170 qubits: qubit_set,
171 symbols: IndexSet::new(),
172 parameters: IndexSet::new(),
173 classical_vars: Vec::new(),
174 classical_values: Vec::new(),
175 global_phase: CircuitParam::Fixed(0.0),
176 graph: StableDiGraph::new(),
177 wire_io: IndexMap::new(),
178 control_flow: HashMap::new(),
179 order_to_node: Vec::with_capacity(operations.len()),
180 topological_op_nodes_cache: OnceLock::new(),
181 node_layers_cache: OnceLock::new(),
182 };
183 dag.initialize_wires();
184 dag.add_operations(operations)?;
185 dag.validate()?;
186 Ok(dag)
187 }
188
189 pub fn to_circuit(&self) -> Result<Circuit, CircuitError> {
197 let value_ops = self
198 .topological_operations()?
199 .into_iter()
200 .map(|operation| {
201 storage_operation_to_value(operation, &|param| self.parameter_value(param))
202 })
203 .collect::<Result<Vec<_>, _>>()?;
204 let mut circuit = Circuit::from_operations(
205 self.qubits.iter().copied().collect(),
206 value_ops,
207 Some(self.classical_vars.clone()),
208 Some(self.classical_values.clone()),
209 )?;
210 circuit.set_global_phase(self.global_phase_parameter()?);
211 Ok(circuit)
212 }
213
214 pub fn validate(&self) -> Result<(), CircuitError> {
222 toposort(&self.graph, None).map_err(|cycle| {
223 CircuitError::InvalidDag(format!(
224 "DAG contains a cycle at node {:?}",
225 cycle.node_id()
226 ))
227 })?;
228 self.validate_wire_io()?;
229
230 let mut produced_values = HashSet::new();
231 for node in self.graph.node_indices() {
232 match &self.graph[node] {
233 DagNode::WireIn(wire) | DagNode::WireOut(wire) => self.validate_wire(*wire)?,
234 DagNode::Operation { operation, order } => {
235 if self.node_for_order(*order) != Some(node) {
236 return Err(CircuitError::InvalidDag(format!(
237 "operation order {order} is not unique"
238 )));
239 }
240 if self.order_to_node.get(*order).copied() != Some(node) {
241 return Err(CircuitError::InvalidDag(format!(
242 "operation order {order} is missing from the order index"
243 )));
244 }
245 self.validate_operation(operation)?;
246 if let Instruction::ClassicalData(op) = &operation.instruction
247 && let Some(value) = op.result()
248 && !produced_values.insert(value)
249 {
250 return Err(CircuitError::DuplicateClassicalValueDefinition {
251 index: value.index(),
252 first: "CircuitDag".to_string(),
253 second: "CircuitDag".to_string(),
254 });
255 }
256 if let Some(control) = self.control_flow.get(&node) {
257 self.validate_control_flow_payload(operation, control)?;
258 }
259 }
260 }
261 }
262
263 for edge in self.graph.edge_references() {
264 self.validate_wire(*edge.weight())?;
265 }
266 Ok(())
267 }
268
269 pub fn num_qubits(&self) -> usize {
271 self.qubits.len()
272 }
273
274 pub fn num_ops(&self) -> usize {
276 self.op_nodes().count()
277 }
278
279 pub fn is_empty(&self) -> bool {
281 self.num_ops() == 0
282 }
283
284 pub fn qubits(&self) -> Vec<Qubit> {
286 self.qubits.iter().copied().collect()
287 }
288
289 pub fn parameters(&self) -> &IndexSet<Parameter> {
291 &self.parameters
292 }
293
294 pub fn add_parameter(&mut self, param: Parameter) -> (usize, bool) {
302 let (index, is_new) = self.parameters.insert_full(param.clone());
303 if is_new {
304 for sym in param.get_symbols() {
305 self.symbols.insert(sym);
306 }
307 }
308 (index, is_new)
309 }
310
311 pub fn symbols(&self) -> &IndexSet<String> {
313 &self.symbols
314 }
315
316 pub fn classical_vars(&self) -> &[ClassicalType] {
318 &self.classical_vars
319 }
320
321 pub fn classical_values(&self) -> &[ClassicalType] {
323 &self.classical_values
324 }
325
326 pub fn wires(&self) -> impl Iterator<Item = DagWire> + '_ {
328 self.wire_io.keys().copied()
329 }
330
331 pub fn has_wire(&self, wire: DagWire) -> bool {
333 self.wire_io.contains_key(&wire)
334 }
335
336 pub fn is_wire_idle(&self, wire: DagWire) -> Result<bool, CircuitError> {
343 Ok(self.nodes_on_wire(wire)?.is_empty())
344 }
345
346 pub fn wire_in(&self, wire: DagWire) -> Option<NodeIndex> {
348 self.wire_io.get(&wire).map(|pair| pair[0])
349 }
350
351 pub fn wire_out(&self, wire: DagWire) -> Option<NodeIndex> {
353 self.wire_io.get(&wire).map(|pair| pair[1])
354 }
355
356 pub fn operation(&self, node: NodeIndex) -> Option<&Operation> {
358 match self.graph.node_weight(node) {
359 Some(DagNode::Operation { operation, .. }) => Some(operation),
360 _ => None,
361 }
362 }
363
364 pub fn is_operation(&self, node: NodeIndex) -> bool {
366 self.operation(node).is_some()
367 }
368
369 pub fn node_kind(&self, node: NodeIndex) -> Option<&'static str> {
371 match self.graph.node_weight(node) {
372 Some(DagNode::WireIn(_)) => Some("wire_in"),
373 Some(DagNode::WireOut(_)) => Some("wire_out"),
374 Some(DagNode::Operation { .. }) => Some("operation"),
375 None => None,
376 }
377 }
378
379 pub fn control_flow(&self, node: NodeIndex) -> Option<&DagControlFlow> {
381 self.control_flow.get(&node)
382 }
383
384 pub fn op_nodes(&self) -> impl Iterator<Item = NodeIndex> + '_ {
386 let mut nodes = self
387 .graph
388 .node_indices()
389 .filter(|node| matches!(self.graph[*node], DagNode::Operation { .. }))
390 .collect::<Vec<_>>();
391 nodes.sort_by_key(|node| self.operation_order(*node).unwrap_or(usize::MAX));
392 nodes.into_iter()
393 }
394
395 pub fn topological_op_nodes(&self) -> Result<Vec<NodeIndex>, CircuitError> {
401 Ok(self.topological_op_nodes_ref()?.clone())
402 }
403
404 pub fn predecessors(&self, node: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
406 self.graph
407 .edges_directed(node, rustworkx_core::petgraph::Incoming)
408 .map(|edge| edge.source())
409 }
410
411 pub fn successors(&self, node: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
413 self.graph
414 .edges_directed(node, rustworkx_core::petgraph::Outgoing)
415 .map(|edge| edge.target())
416 }
417
418 pub fn predecessors_on_wire(
425 &self,
426 node: NodeIndex,
427 wire: DagWire,
428 ) -> Result<Vec<NodeIndex>, CircuitError> {
429 self.operation_neighbors_on_wire(node, wire, Direction::Incoming)
430 }
431
432 pub fn successors_on_wire(
439 &self,
440 node: NodeIndex,
441 wire: DagWire,
442 ) -> Result<Vec<NodeIndex>, CircuitError> {
443 self.operation_neighbors_on_wire(node, wire, Direction::Outgoing)
444 }
445
446 pub fn quantum_predecessors(&self, node: NodeIndex) -> Result<Vec<NodeIndex>, CircuitError> {
452 self.operation_neighbors_matching(node, Direction::Incoming, |wire| {
453 matches!(wire, DagWire::Qubit(_))
454 })
455 }
456
457 pub fn quantum_successors(&self, node: NodeIndex) -> Result<Vec<NodeIndex>, CircuitError> {
463 self.operation_neighbors_matching(node, Direction::Outgoing, |wire| {
464 matches!(wire, DagWire::Qubit(_))
465 })
466 }
467
468 pub fn classical_predecessors(&self, node: NodeIndex) -> Result<Vec<NodeIndex>, CircuitError> {
474 self.operation_neighbors_matching(node, Direction::Incoming, |wire| {
475 matches!(wire, DagWire::ClassicalVar(_) | DagWire::ClassicalValue(_))
476 })
477 }
478
479 pub fn classical_successors(&self, node: NodeIndex) -> Result<Vec<NodeIndex>, CircuitError> {
485 self.operation_neighbors_matching(node, Direction::Outgoing, |wire| {
486 matches!(wire, DagWire::ClassicalVar(_) | DagWire::ClassicalValue(_))
487 })
488 }
489
490 pub fn nodes_on_wire(&self, wire: DagWire) -> Result<Vec<NodeIndex>, CircuitError> {
497 self.validate_wire(wire)?;
498 let Some(input) = self.wire_in(wire) else {
499 return Ok(Vec::new());
500 };
501 let output = self.wire_out(wire).ok_or_else(|| {
502 CircuitError::InvalidDag(format!("wire {:?} has no output endpoint", wire))
503 })?;
504
505 let mut nodes = Vec::new();
506 let mut seen = HashSet::new();
507 let mut current = input;
508 while current != output {
509 if !seen.insert(current) {
510 return Err(CircuitError::InvalidDag(format!(
511 "wire {:?} contains a cycle",
512 wire
513 )));
514 }
515 let next = self.next_node_on_wire(current, wire)?.ok_or_else(|| {
516 CircuitError::InvalidDag(format!("wire {:?} is not connected to its output", wire))
517 })?;
518 match self.graph.node_weight(next) {
519 Some(DagNode::Operation { .. }) => nodes.push(next),
520 Some(DagNode::WireOut(found)) if *found == wire => {}
521 Some(_) => {
522 return Err(CircuitError::InvalidDag(format!(
523 "wire {:?} reaches an invalid node {:?}",
524 wire, next
525 )));
526 }
527 None => {
528 return Err(CircuitError::InvalidDag(format!(
529 "wire {:?} reaches missing node {:?}",
530 wire, next
531 )));
532 }
533 }
534 current = next;
535 }
536 Ok(nodes)
537 }
538
539 pub fn front_layer(&self) -> Result<Vec<NodeIndex>, CircuitError> {
545 Ok(self
546 .topological_op_nodes_ref()?
547 .iter()
548 .copied()
549 .filter(|node| {
550 !self
551 .predecessors(*node)
552 .any(|pred| matches!(self.graph[pred], DagNode::Operation { .. }))
553 })
554 .collect())
555 }
556
557 pub fn layers(&self) -> Result<Vec<Vec<NodeIndex>>, CircuitError> {
564 let mut layers: Vec<Vec<NodeIndex>> = Vec::new();
565 for (node, layer) in self.node_layers_ref()? {
566 if layers.len() <= *layer {
567 layers.resize_with(layer + 1, Vec::new);
568 }
569 layers[*layer].push(*node);
570 }
571 Ok(layers)
572 }
573
574 pub fn node_layers(&self) -> Result<IndexMap<NodeIndex, usize>, CircuitError> {
581 Ok(self.node_layers_ref()?.clone())
582 }
583
584 pub fn depth(&self) -> Result<usize, CircuitError> {
586 Ok(self
587 .node_layers_ref()?
588 .values()
589 .copied()
590 .max()
591 .map_or(0, |layer| layer + 1))
592 }
593
594 pub fn has_control_flow(&self) -> bool {
596 self.op_nodes().any(|node| {
597 matches!(
598 self.operation(node).map(|op| &op.instruction),
599 Some(Instruction::ClassicalControl(_))
600 )
601 })
602 }
603
604 pub fn has_nested_control_flow(&self) -> bool {
606 self.control_flow.values().any(|control| {
607 control
608 .body_dags()
609 .any(|body| body.has_control_flow() || body.has_nested_control_flow())
610 })
611 }
612
613 pub fn has_measurement(&self) -> bool {
615 self.op_nodes().any(|node| {
616 self.operation(node)
617 .is_some_and(|operation| operation.instruction.has_measurement())
618 })
619 }
620
621 pub fn operation_count_by_name(&self) -> IndexMap<String, usize> {
623 let mut counts = IndexMap::new();
624 for node in self.op_nodes() {
625 if let Some(operation) = self.operation(node) {
626 let name = operation.instruction.name();
627 *counts.entry(name).or_insert(0) += 1;
628 }
629 }
630 counts
631 }
632
633 pub fn operation_count_by_name_recursive(&self) -> IndexMap<String, usize> {
635 let mut counts = IndexMap::new();
636 self.add_operation_counts_recursive(&mut counts);
637 counts
638 }
639
640 pub fn collect_runs_on_wire<F>(
653 &self,
654 wire: DagWire,
655 mut predicate: F,
656 ) -> Result<Vec<Vec<NodeIndex>>, CircuitError>
657 where
658 F: FnMut(&Operation) -> bool,
659 {
660 let mut runs = Vec::new();
661 let mut current = Vec::new();
662 for node in self.nodes_on_wire(wire)? {
663 let operation = self.operation(node).ok_or_else(|| {
664 CircuitError::InvalidDag(format!("node {:?} is not an operation", node))
665 })?;
666 if predicate(operation) {
667 current.push(node);
668 } else if !current.is_empty() {
669 runs.push(std::mem::take(&mut current));
670 }
671 }
672 if !current.is_empty() {
673 runs.push(current);
674 }
675 Ok(runs)
676 }
677
678 pub fn collect_1q_runs(&self) -> Result<Vec<Vec<NodeIndex>>, CircuitError> {
687 let mut runs = Vec::new();
688 for qubit in &self.qubits {
689 runs.extend(self.collect_runs_on_wire(DagWire::Qubit(*qubit), is_one_qubit_gate)?);
690 }
691 Ok(runs)
692 }
693
694 pub fn collect_2q_runs(&self) -> Result<Vec<Vec<NodeIndex>>, CircuitError> {
703 let mut runs = Vec::new();
704 for qubit in &self.qubits {
705 runs.extend(self.collect_runs_on_wire(DagWire::Qubit(*qubit), is_two_qubit_gate)?);
706 }
707 Ok(runs)
708 }
709
710 pub fn apply_operation_back(
712 &mut self,
713 operation: Operation,
714 ) -> Result<NodeIndex, CircuitError> {
715 let mut operations = self.topological_operations()?;
716 let order = operations.len();
717 operations.push(operation);
718 self.rebuild_from_operations(&operations)?;
719 self.node_for_order(order)
720 .ok_or_else(|| CircuitError::InvalidDag("appended operation node missing".to_string()))
721 }
722
723 pub fn apply_value_operation_back(
729 &mut self,
730 operation: ValueOperation,
731 ) -> Result<NodeIndex, CircuitError> {
732 let operation = self.lower_value_operation(operation)?;
733 self.apply_operation_back(operation)
734 }
735
736 pub fn apply_operation_front(
738 &mut self,
739 operation: Operation,
740 ) -> Result<NodeIndex, CircuitError> {
741 let mut operations = Vec::with_capacity(self.num_ops() + 1);
742 operations.push(operation);
743 operations.extend(self.topological_operations()?);
744 self.rebuild_from_operations(&operations)?;
745 self.node_for_order(0)
746 .ok_or_else(|| CircuitError::InvalidDag("prepended operation node missing".to_string()))
747 }
748
749 pub fn apply_value_operation_front(
755 &mut self,
756 operation: ValueOperation,
757 ) -> Result<NodeIndex, CircuitError> {
758 let operation = self.lower_value_operation(operation)?;
759 self.apply_operation_front(operation)
760 }
761
762 pub fn remove_op_node(&mut self, node: NodeIndex) -> Result<Operation, CircuitError> {
764 let order = self.require_operation_order(node)?;
765 let mut operations = self.topological_operations()?;
766 let removed = operations.remove(order);
767 self.rebuild_from_operations_unchecked(&operations)?;
768 Ok(removed)
769 }
770
771 pub fn substitute_node(
773 &mut self,
774 node: NodeIndex,
775 operation: Operation,
776 ) -> Result<(), CircuitError> {
777 let order = self.require_operation_order(node)?;
778 let mut operations = self.topological_operations()?;
779 operations[order] = operation;
780 self.rebuild_from_operations(&operations)
781 }
782
783 pub fn substitute_value_node(
789 &mut self,
790 node: NodeIndex,
791 operation: ValueOperation,
792 ) -> Result<(), CircuitError> {
793 let operation = self.lower_value_operation(operation)?;
794 self.substitute_node(node, operation)
795 }
796
797 pub fn substitute_node_with_dag(
799 &mut self,
800 node: NodeIndex,
801 replacement: CircuitDag,
802 ) -> Result<(), CircuitError> {
803 replacement.validate()?;
804 let order = self.require_operation_order(node)?;
805 let old = self.operation(node).ok_or_else(|| {
806 CircuitError::InvalidDag(format!("node {:?} is not an operation", node))
807 })?;
808 let replacement_operations = self.validate_replacement(old, &replacement)?;
809 let mut operations = self.topological_operations()?;
810 operations.splice(order..=order, replacement_operations);
811 self.rebuild_from_operations(&operations)
812 }
813
814 fn initialize_wires(&mut self) {
815 let mut wires = Vec::new();
816 wires.push(DagWire::GlobalOrder);
817 wires.extend(self.qubits.iter().copied().map(DagWire::Qubit));
818 for wire in wires {
819 let input = self.graph.add_node(DagNode::WireIn(wire));
820 let output = self.graph.add_node(DagNode::WireOut(wire));
821 self.wire_io.insert(wire, [input, output]);
822 }
823 }
824
825 fn add_operations(&mut self, operations: &[Operation]) -> Result<(), CircuitError> {
826 let mut frontier = self
827 .wire_io
828 .iter()
829 .map(|(wire, endpoints)| (*wire, endpoints[0]))
830 .collect::<HashMap<_, _>>();
831
832 for (order, operation) in operations.iter().cloned().enumerate() {
833 self.validate_operation(&operation)?;
834 let node = self.graph.add_node(DagNode::Operation {
835 operation: operation.clone(),
836 order,
837 });
838 self.order_to_node.push(node);
839 if let Some(control) = self.build_control_flow_payload(&operation)? {
840 self.control_flow.insert(node, control);
841 }
842
843 let resources = self.operation_resources(&operation);
844 let mut deps = resources.reads.clone();
845 deps.extend(resources.writes.iter().copied());
846 if deps.is_empty() {
847 deps.insert(DagWire::GlobalOrder);
848 }
849
850 for wire in deps {
851 self.ensure_wire(wire)?;
852 if let std::collections::hash_map::Entry::Vacant(entry) = frontier.entry(wire) {
853 let endpoints = self.wire_io.get(&wire).ok_or_else(|| {
854 CircuitError::InvalidDag(format!("missing endpoints for wire {:?}", wire))
855 })?;
856 entry.insert(endpoints[0]);
857 }
858 let source = frontier.get(&wire).copied().ok_or_else(|| {
859 CircuitError::InvalidDag(format!("missing frontier for wire {:?}", wire))
860 })?;
861 self.add_edge_once(source, node, wire);
862 frontier.insert(wire, node);
863 }
864 }
865
866 let output_edges = self
867 .wire_io
868 .iter()
869 .map(|(wire, endpoints)| {
870 (
871 frontier.get(wire).copied().unwrap_or(endpoints[0]),
872 endpoints[1],
873 *wire,
874 )
875 })
876 .collect::<Vec<_>>();
877 for (source, target, wire) in output_edges {
878 self.add_edge_once(source, target, wire);
879 }
880 Ok(())
881 }
882
883 fn rebuild_from_operations(&mut self, operations: &[Operation]) -> Result<(), CircuitError> {
884 self.rebuild_from_operations_with_validation(operations, true)
885 }
886
887 fn rebuild_from_operations_unchecked(
888 &mut self,
889 operations: &[Operation],
890 ) -> Result<(), CircuitError> {
891 self.rebuild_from_operations_with_validation(operations, false)
892 }
893
894 fn rebuild_from_operations_with_validation(
895 &mut self,
896 operations: &[Operation],
897 validate: bool,
898 ) -> Result<(), CircuitError> {
899 let mut rebuilt = Self {
900 qubits: self.qubits.clone(),
901 symbols: self.symbols.clone(),
902 parameters: self.parameters.clone(),
903 classical_vars: self.classical_vars.clone(),
904 classical_values: self.classical_values.clone(),
905 global_phase: self.global_phase.clone(),
906 graph: StableDiGraph::new(),
907 wire_io: IndexMap::new(),
908 control_flow: HashMap::new(),
909 order_to_node: Vec::with_capacity(operations.len()),
910 topological_op_nodes_cache: OnceLock::new(),
911 node_layers_cache: OnceLock::new(),
912 };
913 rebuilt.initialize_wires();
914 rebuilt.add_operations(operations)?;
915 if validate {
916 rebuilt.validate()?;
917 }
918 *self = rebuilt;
919 Ok(())
920 }
921
922 fn build_control_flow_payload(
923 &self,
924 operation: &Operation,
925 ) -> Result<Option<DagControlFlow>, CircuitError> {
926 let Instruction::ClassicalControl(control) = &operation.instruction else {
927 return Ok(None);
928 };
929 let build_body = |ops: &[Operation]| -> Result<Box<CircuitDag>, CircuitError> {
930 let mut dag = Self {
931 qubits: self.qubits.clone(),
932 symbols: self.symbols.clone(),
933 parameters: self.parameters.clone(),
934 classical_vars: self.classical_vars.clone(),
935 classical_values: self.classical_values.clone(),
936 global_phase: self.global_phase.clone(),
937 graph: StableDiGraph::new(),
938 wire_io: IndexMap::new(),
939 control_flow: HashMap::new(),
940 order_to_node: Vec::with_capacity(ops.len()),
941 topological_op_nodes_cache: OnceLock::new(),
942 node_layers_cache: OnceLock::new(),
943 };
944 dag.initialize_wires();
945 dag.add_operations(ops)?;
946 dag.validate()?;
947 Ok(Box::new(dag))
948 };
949
950 Ok(Some(match control {
951 ClassicalControlOp::If(op) => DagControlFlow::If {
952 then_body: build_body(op.then_body().operations())?,
953 else_body: op
954 .else_body()
955 .map(|body| build_body(body.operations()))
956 .transpose()?,
957 },
958 ClassicalControlOp::While(op) => DagControlFlow::While {
959 body: build_body(op.body().operations())?,
960 },
961 ClassicalControlOp::For(op) => DagControlFlow::For {
962 body: build_body(op.body().operations())?,
963 },
964 ClassicalControlOp::Switch(op) => DagControlFlow::Switch {
965 cases: op
966 .cases()
967 .iter()
968 .map(|case| {
969 Ok(DagSwitchCase {
970 value: case.value(),
971 body: build_body(case.body().operations())?,
972 })
973 })
974 .collect::<Result<_, CircuitError>>()?,
975 default: op
976 .default()
977 .map(|body| build_body(body.operations()))
978 .transpose()?,
979 },
980 ClassicalControlOp::Break => DagControlFlow::Break,
981 ClassicalControlOp::Continue => DagControlFlow::Continue,
982 }))
983 }
984
985 fn add_edge_once(&mut self, source: NodeIndex, target: NodeIndex, wire: DagWire) {
986 if self
987 .graph
988 .edges_connecting(source, target)
989 .any(|edge| *edge.weight() == wire)
990 {
991 return;
992 }
993 self.graph.add_edge(source, target, wire);
994 }
995
996 fn operation_neighbors_on_wire(
997 &self,
998 node: NodeIndex,
999 wire: DagWire,
1000 direction: Direction,
1001 ) -> Result<Vec<NodeIndex>, CircuitError> {
1002 if self.graph.node_weight(node).is_none() {
1003 return Err(CircuitError::InvalidDag(format!(
1004 "node {:?} is not in the DAG",
1005 node
1006 )));
1007 }
1008 self.validate_wire(wire)?;
1009 Ok(self.operation_neighbors_matching_unchecked(node, direction, |found| found == wire))
1010 }
1011
1012 fn operation_neighbors_matching(
1013 &self,
1014 node: NodeIndex,
1015 direction: Direction,
1016 predicate: impl FnMut(DagWire) -> bool,
1017 ) -> Result<Vec<NodeIndex>, CircuitError> {
1018 if self.graph.node_weight(node).is_none() {
1019 return Err(CircuitError::InvalidDag(format!(
1020 "node {:?} is not in the DAG",
1021 node
1022 )));
1023 }
1024 Ok(self.operation_neighbors_matching_unchecked(node, direction, predicate))
1025 }
1026
1027 fn operation_neighbors_matching_unchecked(
1028 &self,
1029 node: NodeIndex,
1030 direction: Direction,
1031 mut predicate: impl FnMut(DagWire) -> bool,
1032 ) -> Vec<NodeIndex> {
1033 let mut neighbors = IndexSet::new();
1034 for edge in self.graph.edges_directed(node, direction) {
1035 if !predicate(*edge.weight()) {
1036 continue;
1037 }
1038 let neighbor = match direction {
1039 Direction::Incoming => edge.source(),
1040 Direction::Outgoing => edge.target(),
1041 };
1042 if matches!(
1043 self.graph.node_weight(neighbor),
1044 Some(DagNode::Operation { .. })
1045 ) {
1046 neighbors.insert(neighbor);
1047 }
1048 }
1049 let mut neighbors = neighbors.into_iter().collect::<Vec<_>>();
1050 neighbors.sort_by_key(|node| self.topological_sort_key(*node));
1051 neighbors
1052 }
1053
1054 fn next_node_on_wire(
1055 &self,
1056 node: NodeIndex,
1057 wire: DagWire,
1058 ) -> Result<Option<NodeIndex>, CircuitError> {
1059 let mut next = None;
1060 for edge in self
1061 .graph
1062 .edges_directed(node, rustworkx_core::petgraph::Outgoing)
1063 .filter(|edge| *edge.weight() == wire)
1064 {
1065 if next.replace(edge.target()).is_some() {
1066 return Err(CircuitError::InvalidDag(format!(
1067 "wire {:?} branches at node {:?}",
1068 wire, node
1069 )));
1070 }
1071 }
1072 Ok(next)
1073 }
1074
1075 fn ensure_wire(&mut self, wire: DagWire) -> Result<(), CircuitError> {
1076 self.validate_wire(wire)?;
1077 if !self.wire_io.contains_key(&wire) {
1078 let input = self.graph.add_node(DagNode::WireIn(wire));
1079 let output = self.graph.add_node(DagNode::WireOut(wire));
1080 self.wire_io.insert(wire, [input, output]);
1081 }
1082 Ok(())
1083 }
1084
1085 fn validate_wire_io(&self) -> Result<(), CircuitError> {
1086 for (wire, endpoints) in &self.wire_io {
1087 match self.graph.node_weight(endpoints[0]) {
1088 Some(DagNode::WireIn(found)) if found == wire => {}
1089 _ => {
1090 return Err(CircuitError::InvalidDag(format!(
1091 "wire {:?} has invalid input endpoint",
1092 wire
1093 )));
1094 }
1095 }
1096 match self.graph.node_weight(endpoints[1]) {
1097 Some(DagNode::WireOut(found)) if found == wire => {}
1098 _ => {
1099 return Err(CircuitError::InvalidDag(format!(
1100 "wire {:?} has invalid output endpoint",
1101 wire
1102 )));
1103 }
1104 }
1105 }
1106 Ok(())
1107 }
1108
1109 fn validate_wire(&self, wire: DagWire) -> Result<(), CircuitError> {
1110 match wire {
1111 DagWire::Qubit(qubit) => {
1112 if !self.qubits.contains(&qubit) {
1113 return Err(CircuitError::QubitNotFound(qubit.id()));
1114 }
1115 }
1116 DagWire::ClassicalVar(var) => {
1117 self.validate_classical_var(var)?;
1118 }
1119 DagWire::ClassicalValue(value) => {
1120 self.validate_classical_value(value)?;
1121 }
1122 DagWire::GlobalOrder => {}
1123 }
1124 Ok(())
1125 }
1126
1127 fn validate_operation(&self, operation: &Operation) -> Result<(), CircuitError> {
1128 for qubit in &operation.qubits {
1129 if !self.qubits.contains(qubit) {
1130 return Err(CircuitError::QubitNotFound(qubit.id()));
1131 }
1132 }
1133 for param in &operation.params {
1134 if let CircuitParam::Index(index) = param
1135 && self.parameters.get_index(*index as usize).is_none()
1136 {
1137 return Err(CircuitError::InvalidParameterIndex(*index));
1138 }
1139 }
1140 self.validate_operation_classical_resources(operation)?;
1141 Ok(())
1142 }
1143
1144 fn validate_classical_var(&self, var: ClassicalVar) -> Result<(), CircuitError> {
1145 match self.classical_vars.get(var.index() as usize) {
1146 Some(ty) if *ty == var.ty() => Ok(()),
1147 Some(_) => Err(CircuitError::InvalidDag(format!(
1148 "classical var {} has mismatched type",
1149 var.index()
1150 ))),
1151 None => Err(CircuitError::ForeignClassicalHandle {
1152 kind: "classical var",
1153 index: var.index(),
1154 }),
1155 }
1156 }
1157
1158 fn validate_classical_value(&self, value: ClassicalValue) -> Result<(), CircuitError> {
1159 match self.classical_values.get(value.index() as usize) {
1160 Some(ty) if *ty == value.ty() => Ok(()),
1161 Some(_) => Err(CircuitError::InvalidDag(format!(
1162 "classical value {} has mismatched type",
1163 value.index()
1164 ))),
1165 None => Err(CircuitError::ForeignClassicalHandle {
1166 kind: "classical value",
1167 index: value.index(),
1168 }),
1169 }
1170 }
1171
1172 fn validate_control_flow_payload(
1173 &self,
1174 operation: &Operation,
1175 control: &DagControlFlow,
1176 ) -> Result<(), CircuitError> {
1177 if !matches!(operation.instruction, Instruction::ClassicalControl(_)) {
1178 return Err(CircuitError::InvalidDag(
1179 "control-flow payload attached to non-control operation".to_string(),
1180 ));
1181 }
1182 match control {
1183 DagControlFlow::If {
1184 then_body,
1185 else_body,
1186 } => {
1187 then_body.validate()?;
1188 if let Some(body) = else_body {
1189 body.validate()?;
1190 }
1191 }
1192 DagControlFlow::While { body } | DagControlFlow::For { body } => body.validate()?,
1193 DagControlFlow::Switch { cases, default } => {
1194 for case in cases {
1195 case.body.validate()?;
1196 }
1197 if let Some(body) = default {
1198 body.validate()?;
1199 }
1200 }
1201 DagControlFlow::Break | DagControlFlow::Continue => {}
1202 }
1203 Ok(())
1204 }
1205
1206 fn parameter_value(&self, parameter: &CircuitParam) -> Result<ParameterValue, CircuitError> {
1207 match parameter {
1208 CircuitParam::Fixed(value) => Ok(ParameterValue::Fixed(*value)),
1209 CircuitParam::Index(index) => self
1210 .parameters
1211 .get_index(*index as usize)
1212 .cloned()
1213 .map(ParameterValue::Param)
1214 .ok_or(CircuitError::InvalidParameterIndex(*index)),
1215 }
1216 }
1217
1218 fn lower_value_operation(
1219 &mut self,
1220 operation: ValueOperation,
1221 ) -> Result<Operation, CircuitError> {
1222 let instruction = self.lower_value_instruction(operation.instruction)?;
1223 let params = operation
1224 .params
1225 .into_iter()
1226 .enumerate()
1227 .map(
1228 |(param_index, param)| -> Result<CircuitParam, CircuitError> {
1229 match param {
1230 ParameterValue::Param(param) => {
1231 let (index, _) = self.add_parameter(param);
1232 Ok(CircuitParam::Index(index as u32))
1233 }
1234 ParameterValue::Fixed(value) => {
1235 if !value.is_finite() {
1236 return Err(CircuitError::InvalidParameterValue(
1237 param_index,
1238 value,
1239 ));
1240 }
1241 Ok(CircuitParam::Fixed(value))
1242 }
1243 }
1244 },
1245 )
1246 .collect::<Result<_, _>>()?;
1247 Ok(Operation {
1248 instruction,
1249 qubits: operation.qubits,
1250 params,
1251 label: operation.label,
1252 })
1253 }
1254
1255 fn lower_value_instruction(
1256 &mut self,
1257 instruction: ValueInstruction,
1258 ) -> Result<Instruction, CircuitError> {
1259 let op = match instruction {
1260 ValueInstruction::Instruction(Instruction::ClassicalControl(_)) => {
1261 return Err(CircuitError::InvalidOperation(
1262 "ValueInstruction::Instruction cannot wrap Instruction::ClassicalControl"
1263 .to_string(),
1264 ));
1265 }
1266 ValueInstruction::Instruction(instruction) => return Ok(instruction),
1267 ValueInstruction::ClassicalControl(op) => op,
1268 };
1269
1270 let op = match op {
1271 ValueClassicalControlOp::If {
1272 condition,
1273 then_body,
1274 else_body,
1275 } => {
1276 let then_body = self.lower_value_body(then_body)?;
1277 let else_body = else_body
1278 .map(|body| self.lower_value_body(body))
1279 .transpose()?;
1280 IfOp::new(condition, then_body, else_body).map(ClassicalControlOp::If)?
1281 }
1282 ValueClassicalControlOp::While { condition, body } => {
1283 let body = self.lower_value_body(body)?;
1284 WhileOp::new(condition, body).map(ClassicalControlOp::While)?
1285 }
1286 ValueClassicalControlOp::For {
1287 var,
1288 start,
1289 stop,
1290 step,
1291 body,
1292 } => {
1293 let body = self.lower_value_body(body)?;
1294 ForOp::new(var, start, stop, step, body).map(ClassicalControlOp::For)?
1295 }
1296 ValueClassicalControlOp::Switch {
1297 target,
1298 cases,
1299 default,
1300 } => {
1301 let cases = cases
1302 .into_iter()
1303 .map(|case| {
1304 Ok(SwitchCase::new(
1305 case.value,
1306 self.lower_value_body(case.body)?,
1307 ))
1308 })
1309 .collect::<Result<Vec<_>, CircuitError>>()?;
1310 let default = default
1311 .map(|body| self.lower_value_body(body))
1312 .transpose()?;
1313 SwitchOp::new(target, cases, default).map(ClassicalControlOp::Switch)?
1314 }
1315 ValueClassicalControlOp::Break => ClassicalControlOp::Break,
1316 ValueClassicalControlOp::Continue => ClassicalControlOp::Continue,
1317 };
1318 Ok(Instruction::ClassicalControl(op))
1319 }
1320
1321 fn lower_value_body(&mut self, body: ValueControlBody) -> Result<ControlBody, CircuitError> {
1322 body.operations()
1323 .iter()
1324 .cloned()
1325 .map(|operation| self.lower_value_operation(operation))
1326 .collect::<Result<Vec<_>, _>>()
1327 .map(ControlBody::new)
1328 }
1329
1330 fn global_phase_parameter(&self) -> Result<Parameter, CircuitError> {
1331 match self.global_phase {
1332 CircuitParam::Fixed(value) => Ok(Parameter::from(value)),
1333 CircuitParam::Index(index) => self
1334 .parameters
1335 .get_index(index as usize)
1336 .cloned()
1337 .ok_or(CircuitError::InvalidParameterIndex(index)),
1338 }
1339 }
1340
1341 fn topological_operations(&self) -> Result<Vec<Operation>, CircuitError> {
1342 Ok(self
1343 .topological_op_nodes_ref()?
1344 .iter()
1345 .copied()
1346 .map(|node| {
1347 self.operation(node)
1348 .expect("topological_op_nodes returns only operation nodes")
1349 .clone()
1350 })
1351 .collect())
1352 }
1353
1354 fn topological_op_nodes_ref(&self) -> Result<&Vec<NodeIndex>, CircuitError> {
1355 if self.topological_op_nodes_cache.get().is_none() {
1356 let nodes = lexicographical_topological_sort(
1357 &self.graph,
1358 |node| Ok::<_, Infallible>(self.topological_sort_key(node)),
1359 false,
1360 None,
1361 )
1362 .map_err(|error| {
1363 CircuitError::InvalidDag(format!("DAG topological sort failed: {error}"))
1364 })?;
1365 let nodes = nodes
1366 .into_iter()
1367 .filter(|node| matches!(self.graph[*node], DagNode::Operation { .. }))
1368 .collect::<Vec<_>>();
1369 let _ = self.topological_op_nodes_cache.set(nodes);
1370 }
1371 self.topological_op_nodes_cache.get().ok_or_else(|| {
1372 CircuitError::InvalidDag("topological operation cache was not initialized".to_string())
1373 })
1374 }
1375
1376 fn node_layers_ref(&self) -> Result<&IndexMap<NodeIndex, usize>, CircuitError> {
1377 if self.node_layers_cache.get().is_none() {
1378 let mut ordered_layers = IndexMap::new();
1379 for node in self.topological_op_nodes_ref()?.iter().copied() {
1380 let mut layer = 0usize;
1381 for pred in self
1382 .predecessors(node)
1383 .filter(|pred| matches!(self.graph[*pred], DagNode::Operation { .. }))
1384 {
1385 let predecessor_layer =
1386 ordered_layers.get(&pred).copied().ok_or_else(|| {
1387 CircuitError::InvalidDag(format!(
1388 "operation predecessor {:?} was not visited before {:?}",
1389 pred, node
1390 ))
1391 })?;
1392 layer = layer.max(predecessor_layer + 1);
1393 }
1394 ordered_layers.insert(node, layer);
1395 }
1396 let _ = self.node_layers_cache.set(ordered_layers);
1397 }
1398 self.node_layers_cache.get().ok_or_else(|| {
1399 CircuitError::InvalidDag("node layer cache was not initialized".to_string())
1400 })
1401 }
1402
1403 pub(crate) fn operation_order(&self, node: NodeIndex) -> Option<usize> {
1404 match self.graph.node_weight(node) {
1405 Some(DagNode::Operation { order, .. }) => Some(*order),
1406 _ => None,
1407 }
1408 }
1409
1410 fn topological_sort_key(&self, node: NodeIndex) -> (u8, usize, usize) {
1411 match self.graph.node_weight(node) {
1412 Some(DagNode::WireIn(_)) => (0, node.index(), 0),
1413 Some(DagNode::Operation { order, .. }) => (1, *order, node.index()),
1414 Some(DagNode::WireOut(_)) => (2, node.index(), 0),
1415 None => (3, node.index(), 0),
1416 }
1417 }
1418
1419 fn require_operation_order(&self, node: NodeIndex) -> Result<usize, CircuitError> {
1420 self.operation_order(node)
1421 .ok_or_else(|| CircuitError::InvalidDag(format!("node {:?} is not an operation", node)))
1422 }
1423
1424 pub(crate) fn node_for_order(&self, order: usize) -> Option<NodeIndex> {
1425 self.order_to_node.get(order).copied()
1426 }
1427
1428 fn operation_resources(&self, operation: &Operation) -> OperationResources {
1429 let mut resources = OperationResources::from_operation(operation);
1430 if matches!(
1431 operation.instruction,
1432 Instruction::Directive(Directive::Barrier)
1433 ) && operation.qubits.is_empty()
1434 {
1435 resources.writes.clear();
1436 resources
1437 .writes
1438 .extend(self.qubits.iter().copied().map(DagWire::Qubit));
1439 }
1440 resources
1441 }
1442
1443 fn validate_operation_classical_resources(
1444 &self,
1445 operation: &Operation,
1446 ) -> Result<(), CircuitError> {
1447 let resources = self.operation_resources(operation);
1448 for wire in resources.reads.iter().chain(resources.writes.iter()) {
1449 self.validate_wire(*wire)?;
1450 }
1451 Ok(())
1452 }
1453
1454 fn validate_replacement(
1455 &self,
1456 old: &Operation,
1457 replacement: &CircuitDag,
1458 ) -> Result<Vec<Operation>, CircuitError> {
1459 let replacement_operations = replacement.topological_operations()?;
1460 self.validate_replacement_control_flow_shape(old, &replacement_operations)?;
1461 self.validate_replacement_footprint(old, replacement, &replacement_operations)?;
1462 Ok(replacement_operations)
1463 }
1464
1465 fn validate_replacement_control_flow_shape(
1466 &self,
1467 old: &Operation,
1468 replacement_operations: &[Operation],
1469 ) -> Result<(), CircuitError> {
1470 if !matches!(old.instruction, Instruction::ClassicalControl(_)) {
1471 return Ok(());
1472 }
1473
1474 let [replacement_op] = replacement_operations else {
1475 return Err(CircuitError::InvalidDag(
1476 "control-flow operation replacement must contain exactly one operation".to_string(),
1477 ));
1478 };
1479
1480 if !same_control_flow_kind(&old.instruction, &replacement_op.instruction) {
1481 return Err(CircuitError::InvalidDag(
1482 "control-flow operation replacement must preserve the control-flow kind"
1483 .to_string(),
1484 ));
1485 }
1486 Ok(())
1487 }
1488
1489 fn validate_replacement_footprint(
1490 &self,
1491 old: &Operation,
1492 replacement: &CircuitDag,
1493 replacement_operations: &[Operation],
1494 ) -> Result<(), CircuitError> {
1495 let old_resources = self.operation_resources(old);
1496 let readable = old_resources
1497 .reads
1498 .union(&old_resources.writes)
1499 .copied()
1500 .collect::<IndexSet<_>>();
1501
1502 for operation in replacement_operations {
1503 let resources = replacement.operation_resources(operation);
1504 for wire in &resources.writes {
1505 if *wire != DagWire::GlobalOrder && !old_resources.writes.contains(wire) {
1506 return Err(CircuitError::InvalidDag(format!(
1507 "replacement writes wire {:?} outside replaced node write footprint",
1508 wire
1509 )));
1510 }
1511 }
1512 for wire in &resources.reads {
1513 if *wire != DagWire::GlobalOrder && !readable.contains(wire) {
1514 return Err(CircuitError::InvalidDag(format!(
1515 "replacement reads wire {:?} outside replaced node footprint",
1516 wire
1517 )));
1518 }
1519 }
1520 }
1521 Ok(())
1522 }
1523
1524 fn add_operation_counts_recursive(&self, counts: &mut IndexMap<String, usize>) {
1525 for node in self.op_nodes() {
1526 if let Some(operation) = self.operation(node) {
1527 let name = operation.instruction.name();
1528 *counts.entry(name).or_insert(0) += 1;
1529 }
1530 if let Some(control) = self.control_flow(node) {
1531 for body in control.body_dags() {
1532 body.add_operation_counts_recursive(counts);
1533 }
1534 }
1535 }
1536 }
1537}
1538
1539impl DagControlFlow {
1540 fn body_dags(&self) -> Box<dyn Iterator<Item = &CircuitDag> + '_> {
1541 match self {
1542 DagControlFlow::If {
1543 then_body,
1544 else_body,
1545 } => Box::new(std::iter::once(then_body.as_ref()).chain(else_body.as_deref())),
1546 DagControlFlow::While { body } | DagControlFlow::For { body } => {
1547 Box::new(std::iter::once(body.as_ref()))
1548 }
1549 DagControlFlow::Switch { cases, default } => Box::new(
1550 cases
1551 .iter()
1552 .map(|case| case.body.as_ref())
1553 .chain(default.as_deref()),
1554 ),
1555 DagControlFlow::Break | DagControlFlow::Continue => Box::new(std::iter::empty()),
1556 }
1557 }
1558}
1559
1560#[derive(Debug, Clone, Default)]
1561struct OperationResources {
1562 reads: IndexSet<DagWire>,
1563 writes: IndexSet<DagWire>,
1564}
1565
1566impl OperationResources {
1567 fn from_operation(operation: &Operation) -> Self {
1568 let mut resources = Self::default();
1569 resources
1570 .writes
1571 .extend(operation.qubits.iter().copied().map(DagWire::Qubit));
1572
1573 match &operation.instruction {
1574 Instruction::ClassicalData(op) => match op {
1575 ClassicalDataOp::Store { target, value } => {
1576 resources.add_expr_reads(value);
1577 resources.writes.insert(DagWire::ClassicalVar(*target));
1578 }
1579 ClassicalDataOp::MeasureBit { result }
1580 | ClassicalDataOp::MeasureBits { result } => {
1581 resources.writes.insert(DagWire::ClassicalValue(*result));
1582 }
1583 },
1584 Instruction::ClassicalControl(control) => resources.add_control_resources(control),
1585 _ => {}
1586 }
1587 resources
1588 }
1589
1590 fn add_control_resources(&mut self, control: &ClassicalControlOp) {
1591 match control {
1592 ClassicalControlOp::If(op) => {
1593 self.add_expr_reads(op.condition());
1594 self.add_body_resources(op.then_body().operations());
1595 if let Some(body) = op.else_body() {
1596 self.add_body_resources(body.operations());
1597 }
1598 }
1599 ClassicalControlOp::While(op) => {
1600 self.add_expr_reads(op.condition());
1601 self.add_body_resources(op.body().operations());
1602 }
1603 ClassicalControlOp::For(op) => {
1604 self.writes.insert(DagWire::ClassicalVar(op.var()));
1605 self.add_expr_reads(op.start());
1606 self.add_expr_reads(op.stop());
1607 self.add_expr_reads(op.step());
1608 self.add_body_resources(op.body().operations());
1609 }
1610 ClassicalControlOp::Switch(op) => {
1611 self.add_expr_reads(op.target());
1612 for case in op.cases() {
1613 self.add_body_resources(case.body().operations());
1614 }
1615 if let Some(body) = op.default() {
1616 self.add_body_resources(body.operations());
1617 }
1618 }
1619 ClassicalControlOp::Break | ClassicalControlOp::Continue => {}
1620 }
1621 }
1622
1623 fn add_body_resources(&mut self, operations: &[Operation]) {
1624 for operation in operations {
1625 let nested = Self::from_operation(operation);
1626 self.reads.extend(nested.reads);
1627 self.writes.extend(nested.writes);
1628 }
1629 }
1630
1631 fn add_expr_reads(&mut self, expr: &ClassicalExpr) {
1632 self.reads
1633 .extend(expr.vars().into_iter().map(DagWire::ClassicalVar));
1634 self.reads
1635 .extend(expr.values().into_iter().map(DagWire::ClassicalValue));
1636 }
1637}
1638
1639fn is_one_qubit_gate(operation: &Operation) -> bool {
1640 operation.qubits.len() == 1
1641 && matches!(
1642 operation.instruction,
1643 Instruction::Standard(_)
1644 | Instruction::McGate(_)
1645 | Instruction::UnitaryGate(_)
1646 | Instruction::CircuitGate(_)
1647 )
1648}
1649
1650fn is_two_qubit_gate(operation: &Operation) -> bool {
1651 operation.qubits.len() == 2
1652 && matches!(
1653 operation.instruction,
1654 Instruction::Standard(_)
1655 | Instruction::McGate(_)
1656 | Instruction::UnitaryGate(_)
1657 | Instruction::CircuitGate(_)
1658 )
1659}
1660
1661fn same_control_flow_kind(lhs: &Instruction, rhs: &Instruction) -> bool {
1662 matches!((lhs, rhs), (Instruction::ClassicalControl(lhs), Instruction::ClassicalControl(rhs)) if lhs.kind() == rhs.kind())
1663}
1664
1665impl TryFrom<&Circuit> for CircuitDag {
1666 type Error = CircuitError;
1667
1668 fn try_from(circuit: &Circuit) -> Result<Self, Self::Error> {
1669 Self::from_circuit(circuit)
1670 }
1671}
1672
1673impl TryFrom<&CircuitDag> for Circuit {
1674 type Error = CircuitError;
1675
1676 fn try_from(dag: &CircuitDag) -> Result<Self, Self::Error> {
1677 dag.to_circuit()
1678 }
1679}
1680
1681#[cfg(test)]
1682#[path = "./dag_test.rs"]
1683mod dag_test;