cqlib_core/compile/transform/
analysis.rs1use crate::circuit::{
20 Circuit, ClassicalControlOp, Directive, Instruction, Operation, StandardGate,
21};
22
23#[derive(Debug, Clone, Default, PartialEq, Eq)]
25pub struct CircuitAnalysis {
26 pub has_classical_data: bool,
27 pub has_classical_control: bool,
28 pub has_measurement: bool,
29 pub has_classical_values: bool,
30 pub has_classical_vars: bool,
31 pub has_runtime_classical: bool,
32 pub needs_classical_handle_preservation: bool,
33 pub has_circuit_gate_definitions: bool,
34 pub has_unitary_circuit_definitions: bool,
35 pub has_unitary_gates: bool,
36 pub has_mc_gates: bool,
37}
38
39impl CircuitAnalysis {
40 pub fn analyze(circuit: &Circuit) -> Self {
42 WorkflowCircuitAnalysis::analyze(circuit).public
43 }
44}
45
46#[derive(Debug, Clone, Default, PartialEq, Eq)]
53pub(crate) struct WorkflowCircuitAnalysis {
54 public: CircuitAnalysis,
55 standard_gates: StandardGateSet,
56 instruction_kinds: InstructionKindSet,
57 has_gate_like_operation_over_two_qubits: bool,
58 has_direction_sensitive_two_qubit_operation: bool,
61 has_reset: bool,
62 has_control_flow: bool,
63}
64
65impl WorkflowCircuitAnalysis {
66 pub(crate) fn analyze(circuit: &Circuit) -> Self {
67 let mut analysis = Self {
68 public: CircuitAnalysis {
69 has_classical_values: !circuit.classical_values().is_empty(),
70 has_classical_vars: !circuit.classical_vars().is_empty(),
71 ..CircuitAnalysis::default()
72 },
73 ..Self::default()
74 };
75 analysis.scan_operations(circuit.operations());
76 analysis.public.has_runtime_classical = analysis.public.has_classical_data
77 || analysis.public.has_classical_control
78 || analysis.public.has_classical_values
79 || analysis.public.has_classical_vars;
80 analysis.public.needs_classical_handle_preservation = analysis.public.has_runtime_classical;
81 analysis
82 }
83
84 pub(crate) const fn public(&self) -> &CircuitAnalysis {
85 &self.public
86 }
87
88 pub(crate) const fn has_unexpanded_definitions(&self) -> bool {
89 self.public.has_circuit_gate_definitions || self.public.has_unitary_circuit_definitions
90 }
91
92 pub(crate) const fn has_unitary_gates(&self) -> bool {
93 self.public.has_unitary_gates
94 }
95
96 pub(crate) const fn has_mc_gates(&self) -> bool {
97 self.public.has_mc_gates
98 }
99
100 pub(crate) const fn has_gate_like_operation_over_two_qubits(&self) -> bool {
101 self.has_gate_like_operation_over_two_qubits
102 }
103
104 pub(crate) fn standard_gates(&self) -> impl Iterator<Item = StandardGate> + '_ {
105 StandardGate::all()
106 .iter()
107 .copied()
108 .filter(|gate| self.standard_gates.contains(*gate))
109 }
110
111 pub(crate) const fn has_extended_gate_like_operations(&self) -> bool {
112 self.instruction_kinds.contains(InstructionKind::McGate)
113 || self
114 .instruction_kinds
115 .contains(InstructionKind::UnitaryGate)
116 || self
117 .instruction_kinds
118 .contains(InstructionKind::CircuitGate)
119 }
120
121 fn scan_operations(&mut self, operations: &[Operation]) {
122 for operation in operations {
123 self.scan_operation(operation);
124 }
125 }
126
127 fn scan_operation(&mut self, operation: &Operation) {
128 match &operation.instruction {
129 Instruction::Standard(gate) => {
130 self.instruction_kinds.insert(InstructionKind::Standard);
131 self.standard_gates.insert(*gate);
132 self.has_gate_like_operation_over_two_qubits |= operation.qubits.len() > 2;
133 self.has_direction_sensitive_two_qubit_operation |=
134 gate.num_qubits() == 2 && !gate.is_invariant_under_operand_swap();
135 }
136 Instruction::ClassicalData(op) => {
137 self.instruction_kinds
138 .insert(InstructionKind::ClassicalData);
139 self.public.has_classical_data = true;
140 if op.result().is_some() {
141 self.public.has_measurement = true;
142 }
143 }
144 Instruction::ClassicalControl(op) => {
145 self.instruction_kinds
146 .insert(InstructionKind::ClassicalControl);
147 self.public.has_classical_control = true;
148 self.has_control_flow = true;
149 self.scan_control_flow(op);
150 }
151 Instruction::CircuitGate(_) => {
152 self.instruction_kinds.insert(InstructionKind::CircuitGate);
153 self.public.has_circuit_gate_definitions = true;
154 self.has_gate_like_operation_over_two_qubits |= operation.qubits.len() > 2;
155 }
156 Instruction::UnitaryGate(gate) => {
157 self.instruction_kinds.insert(InstructionKind::UnitaryGate);
158 self.public.has_unitary_gates = true;
159 if gate.circuit().is_some() {
160 self.public.has_unitary_circuit_definitions = true;
161 }
162 self.has_gate_like_operation_over_two_qubits |= operation.qubits.len() > 2;
163 }
164 Instruction::McGate(_) => {
165 self.instruction_kinds.insert(InstructionKind::McGate);
166 self.public.has_mc_gates = true;
167 self.has_gate_like_operation_over_two_qubits |= operation.qubits.len() > 2;
168 }
169 Instruction::Directive(directive) => {
170 self.instruction_kinds.insert(InstructionKind::Directive);
171 match directive {
172 Directive::Measure => self.public.has_measurement = true,
173 Directive::Reset => self.has_reset = true,
174 Directive::Barrier => {}
175 }
176 }
177 Instruction::Delay => self.instruction_kinds.insert(InstructionKind::Delay),
178 }
179 }
180
181 fn scan_control_flow(&mut self, op: &ClassicalControlOp) {
182 for body in op.bodies() {
183 self.scan_operations(body.operations());
184 }
185 }
186}
187
188#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
189struct StandardGateSet(u64);
190
191impl StandardGateSet {
192 fn insert(&mut self, gate: StandardGate) {
193 self.0 |= 1_u64 << gate as u8;
194 }
195
196 const fn contains(self, gate: StandardGate) -> bool {
197 self.0 & (1_u64 << gate as u8) != 0
198 }
199}
200
201#[repr(u8)]
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203enum InstructionKind {
204 Standard,
205 McGate,
206 UnitaryGate,
207 CircuitGate,
208 Directive,
209 ClassicalData,
210 ClassicalControl,
211 Delay,
212}
213
214#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
215struct InstructionKindSet(u16);
216
217impl InstructionKindSet {
218 fn insert(&mut self, kind: InstructionKind) {
219 self.0 |= 1_u16 << kind as u8;
220 }
221
222 const fn contains(self, kind: InstructionKind) -> bool {
223 self.0 & (1_u16 << kind as u8) != 0
224 }
225}
226
227#[cfg(test)]
228#[path = "./analysis_test.rs"]
229mod analysis_test;