quant_iron/compiler/
compilable.rs

1use crate::{
2    circuit::Circuit,
3    compiler::ir::InstructionIR,
4    components::gate::Gate,
5    components::{
6        measurement::MeasurementOperation,
7        operator::{
8            CNOT, Hadamard, Identity, Pauli, PhaseS, PhaseSdag, PhaseShift, PhaseT, PhaseTdag,
9            RotateX, RotateY, RotateZ, SWAP, Toffoli, Unitary2,
10        },
11    },
12    errors::CompilerError,
13};
14use dyn_clone::DynClone;
15use rayon::prelude::*;
16use std::convert::TryFrom;
17
18/// Trait for operators or measurements that can be compiled into an IR representation
19///
20/// This trait defines the `to_ir` method, which converts an operator into an
21/// IR representation. The IR representation is a structured format that can be
22/// used for further processing, such as optimisation or execution on a quantum
23/// circuit simulator or hardware.
24pub trait Compilable: DynClone + Send + Sync + 'static {
25    /// Converts the operation into an IR representation
26    ///
27    /// # Returns
28    /// A vector of `InstructionIR` representing the operation in IR format.
29    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR>;
30}
31
32dyn_clone::clone_trait_object!(Compilable);
33
34/// Represents a quantum circuit that can be compiled into an IR representation
35///
36/// This is an internal struct used to convert a quantum circuit into an IR representation
37pub(crate) struct CompilableCircuit {
38    /// The number of qubits in the circuit
39    pub num_qubits: usize,
40
41    /// The list of gates in the circuit
42    ///
43    /// All gates in the circuit must implement the `Compilable` trait.
44    pub gates: Vec<CompilableGate>,
45}
46
47impl CompilableCircuit {
48    /// Converts the `CompilableCircuit` into an IR representation
49    ///
50    /// # Returns
51    /// A vector of `InstructionIR` representing the circuit in IR format.
52    pub(crate) fn to_ir(&self) -> Vec<InstructionIR> {
53        self.gates
54            .par_iter()
55            .flat_map(|gate| {
56                gate.operator
57                    .to_ir(gate.targets.clone(), gate.controls.clone())
58            })
59            .collect()
60    }
61}
62
63/// Internal struct representing a gate that can be compiled into an IR representation
64///
65/// This struct is used to hold the operator, target qubits, and control qubits of a gate.
66pub(crate) struct CompilableGate {
67    /// The operator of the gate
68    pub operator: Box<dyn Compilable>,
69    /// The target qubits of the gate
70    pub targets: Vec<usize>,
71    /// The control qubits of the gate
72    pub controls: Vec<usize>,
73}
74
75impl TryFrom<&Circuit> for CompilableCircuit {
76    type Error = CompilerError;
77
78    /// Converts a `Circuit` into a `CompilableCircuit`
79    ///
80    /// # Arguments
81    /// * `circuit` - The circuit to convert
82    ///
83    /// # Returns
84    /// A `Result` containing the `CompilableCircuit` or an error message if conversion fails.
85    fn try_from(circuit: &Circuit) -> Result<Self, CompilerError> {
86        let num_qubits: usize = circuit.get_num_qubits();
87        let mut compilable_gates: Vec<CompilableGate> = Vec::with_capacity(circuit.gates.len() * 2); // Preallocate space for operators
88
89        for gate in &circuit.gates {
90            match gate {
91                Gate::Operator(op, _targets, _controls) => {
92                    if let Some(compilable_op) = op.to_compilable() {
93                        // Convert the operator to a compilable form
94                        let targets = _targets.clone();
95                        let controls = _controls.clone();
96                        let operator = dyn_clone::clone_box(compilable_op);
97
98                        // Create a CompilableGate for the operator
99                        let gate: CompilableGate = CompilableGate {
100                            operator,
101                            targets,
102                            controls,
103                        };
104                        compilable_gates.push(gate);
105                    } else {
106                        return Err(CompilerError::UnsupportedOperator(
107                            "Operator does not implement Compilable trait".to_string(),
108                        ));
109                    }
110                }
111                Gate::Measurement(measurement_basis, targets) => {
112                    // All measurement operations are compilable, no trait check needed
113                    let measurement_op = MeasurementOperation {
114                        basis: *measurement_basis,
115                    };
116
117                    // Create a CompilableGate for the measurement operation
118                    let gate = CompilableGate {
119                        operator: Box::new(measurement_op),
120                        targets: targets.clone(),
121                        controls: vec![],
122                    };
123
124                    compilable_gates.push(gate);
125                }
126            }
127        }
128        Ok(CompilableCircuit {
129            num_qubits,
130            gates: compilable_gates,
131        })
132    }
133}
134
135impl Compilable for Hadamard {
136    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
137        targets
138            .par_iter()
139            .map(|&target| InstructionIR::Hadamard(target, controls.clone()))
140            .collect()
141    }
142}
143
144impl Compilable for Pauli {
145    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
146        targets
147            .par_iter()
148            .map(|&target| match self {
149                Pauli::X => InstructionIR::PauliX(target, controls.clone()),
150                Pauli::Y => InstructionIR::PauliY(target, controls.clone()),
151                Pauli::Z => InstructionIR::PauliZ(target, controls.clone()),
152            })
153            .collect()
154    }
155}
156
157impl Compilable for CNOT {
158    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
159        targets
160            .par_iter()
161            .map(|&target| {
162                if controls.is_empty() {
163                    // If no controls specified, treat as unconditional X gate
164                    InstructionIR::PauliX(target, vec![])
165                } else {
166                    // For CNOT, only use the first control
167                    InstructionIR::PauliX(target, vec![controls[0]])
168                }
169            })
170            .collect()
171    }
172}
173
174impl Compilable for SWAP {
175    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
176        // SWAP requires exactly 2 target qubits
177        if targets.len() >= 2 {
178            // Create SWAP instructions for pairs of qubits
179            targets
180                .par_chunks(2)
181                .filter_map(|chunk| {
182                    if chunk.len() == 2 {
183                        Some(InstructionIR::Swap(chunk[0], chunk[1], controls.clone()))
184                    } else {
185                        None
186                    }
187                })
188                .collect()
189        } else {
190            vec![]
191        }
192    }
193}
194
195impl Compilable for Toffoli {
196    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
197        targets
198            .par_iter()
199            .map(|&target| {
200                // Toffoli is a controlled X gate with additional controls
201                InstructionIR::PauliX(target, controls.clone())
202            })
203            .collect()
204    }
205}
206
207impl Compilable for Identity {
208    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
209        targets
210            .par_iter()
211            .map(|&target| InstructionIR::Id(target, controls.clone()))
212            .collect()
213    }
214}
215
216impl Compilable for PhaseS {
217    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
218        targets
219            .par_iter()
220            .map(|&target| InstructionIR::S(target, controls.clone()))
221            .collect()
222    }
223}
224
225impl Compilable for PhaseT {
226    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
227        targets
228            .par_iter()
229            .map(|&target| InstructionIR::T(target, controls.clone()))
230            .collect()
231    }
232}
233
234impl Compilable for PhaseSdag {
235    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
236        targets
237            .par_iter()
238            .map(|&target| InstructionIR::Sdg(target, controls.clone()))
239            .collect()
240    }
241}
242
243impl Compilable for PhaseTdag {
244    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
245        targets
246            .par_iter()
247            .map(|&target| InstructionIR::Tdg(target, controls.clone()))
248            .collect()
249    }
250}
251
252impl Compilable for PhaseShift {
253    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
254        targets
255            .par_iter()
256            .map(|&target| InstructionIR::Phase(self.angle, target, controls.clone()))
257            .collect()
258    }
259}
260
261impl Compilable for RotateX {
262    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
263        targets
264            .par_iter()
265            .map(|&target| InstructionIR::Rx(self.angle, target, controls.clone()))
266            .collect()
267    }
268}
269
270impl Compilable for RotateY {
271    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
272        targets
273            .par_iter()
274            .map(|&target| InstructionIR::Ry(self.angle, target, controls.clone()))
275            .collect()
276    }
277}
278
279impl Compilable for RotateZ {
280    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
281        targets
282            .par_iter()
283            .map(|&target| InstructionIR::Rz(self.angle, target, controls.clone()))
284            .collect()
285    }
286}
287
288impl Compilable for Unitary2 {
289    fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
290        targets
291            .par_iter()
292            .map(|&target| InstructionIR::Unitary(self.matrix, target, controls.clone()))
293            .collect()
294    }
295}