quant_iron/
subroutine.rs

1use crate::{
2    circuit::Circuit,
3    components::gate::Gate,
4    errors::Error,
5    circuit::CircuitBuilder,
6};
7
8/// A subroutine for a quantum circuit.
9///
10/// # Fields
11///
12/// * `gates` - A vector of gates in the subroutine.
13///
14/// * `num_qubits` - The number of qubits in the subroutine.
15pub struct Subroutine {
16    /// The gates in the subroutine.
17    pub gates: Vec<Gate>,
18    /// The number of qubits in the subroutine.
19    pub num_qubits: usize,
20}
21
22impl Subroutine {
23    /// Creates a new subroutine with the specified number of qubits.
24    ///
25    /// # Arguments
26    ///
27    /// * `num_qubits` - The number of qubits in the subroutine.
28    pub fn new(num_qubits: usize) -> Self {
29        Subroutine {
30            gates: Vec::new(),
31            num_qubits,
32        }
33    }
34
35    /// Creates a new subroutine with the specified gates and number of qubits.
36    ///
37    /// # Arguments
38    ///
39    /// * `gates` - A vector of gates in the subroutine.
40    /// * `num_qubits` - The number of qubits in the subroutine.
41    pub fn with_gates(gates: Vec<Gate>, num_qubits: usize) -> Subroutine {
42        Subroutine { gates, num_qubits }
43    }
44
45    /// Gets the gates in the subroutine.
46    ///
47    /// # Returns
48    ///
49    /// * `&Vec<Gate>` - A reference to the vector of gates in the subroutine.
50    pub fn get_gates(&self) -> &Vec<Gate> {
51        &self.gates
52    }
53
54    /// Adds a gate to the subroutine.
55    ///
56    /// # Arguments
57    ///
58    /// * `gate` - The gate to be added to the subroutine.
59    pub fn add_gate(&mut self, gate: Gate) {
60        self.gates.push(gate);
61    }
62
63    /// Adds multiple gates to the subroutine.
64    ///
65    /// # Arguments
66    ///
67    /// * `gates` - A vector of gates to be added to the subroutine.
68    pub fn add_gates(&mut self, gates: Vec<Gate>) {
69        self.gates.extend(gates);
70    }
71
72    /// Gets the number of qubits in the subroutine.
73    ///
74    /// # Returns
75    ///
76    /// * `usize` - The number of qubits in the subroutine.
77    pub fn get_num_qubits(&self) -> usize {
78        self.num_qubits
79    }
80
81    // -- COMMON SUBROUTINES --
82
83    /// Creates a quantum fourier transform subroutine for the specified qubits.
84    ///
85    /// # Arguments
86    ///
87    /// * `qubits` - A vector of indices of the qubits to be transformed.
88    ///
89    /// * `num_qubits` - The number of qubits in the subroutine.
90    pub fn qft(qubits: Vec<usize>, num_qubits: usize) -> Subroutine {
91        let mut builder: CircuitBuilder = CircuitBuilder::new(num_qubits);
92        let n: usize = qubits.len();
93        for i in 0..n {
94            // Apply Hadamard gate
95            builder.h_gate(qubits[i]);
96            // Apply controlled phase rotations
97            // (j-i) iterates from 1 up to n-1-i.
98            // Let k_loop_val = j-i.
99            let mut power_of_2_denominator = 2.0; // Initial value for k_loop_val = 1 (i.e., 2^1)
100            for k_loop_val in 1..(n - i) { // k_loop_val goes from 1 to (n-i-1)
101                let original_j_qubit_index = i + k_loop_val; // This is the original 'j'
102                let angle = std::f64::consts::PI / power_of_2_denominator;
103                builder.cp_gates(vec![qubits[i]], vec![qubits[original_j_qubit_index]], angle);
104                power_of_2_denominator *= 2.0; // Update for the next k_loop_val
105            }
106        }
107        // Swap qubits at the end
108        for i in 0..(n / 2) {
109            builder.swap_gate(qubits[i], qubits[n - 1 - i]);
110        }
111        builder.build_subroutine()
112    }
113
114    /// Creates a quantum inverse fourier transform subroutine for the specified qubits.
115    /// 
116    /// # Arguments
117    /// 
118    /// * `qubits` - A vector of indices of the qubits to be transformed.
119    /// 
120    /// * `num_qubits` - The number of qubits in the subroutine.
121    /// 
122    /// # Returns
123    /// 
124    /// * `Subroutine` - A new instance of the Subroutine struct.
125    pub fn iqft(qubits: Vec<usize>, num_qubits: usize) -> Subroutine {
126        let mut builder: CircuitBuilder = CircuitBuilder::new(num_qubits);
127        let n: usize = qubits.len();
128
129        // Apply swaps first
130        for i in 0..(n / 2) {
131            builder.swap_gate(qubits[i], qubits[n - 1 - i]);
132        }
133
134        // Apply inverse controlled rotations and Hadamards
135        for i in (0..n).rev() {
136            // Apply controlled phase rotations (inverse)
137            // (j-i) iterates from (n-1-i) down to 1.
138            // Let k_loop_val = j-i.
139            if n > i + 1 { // Check if there are any rotations to apply for this 'i'
140                let k_initial = (n - 1) - i; // Max value of (j-i)
141                let mut power_of_2_denominator = 2_f64.powi(k_initial as i32);
142
143                // Iterate for k_loop_val from k_initial down to 1
144                for iteration_count in 0..k_initial {
145                    let k_loop_val = k_initial - iteration_count; // Current k_loop_val (j-i)
146                    let original_j_qubit_index = i + k_loop_val; // This is the original 'j'
147
148                    let angle = -std::f64::consts::PI / power_of_2_denominator;
149                    builder.cp_gates(vec![qubits[i]], vec![qubits[original_j_qubit_index]], angle);
150
151                    if k_loop_val > 1 { // If not the last iteration (where k_loop_val would be 1)
152                        power_of_2_denominator /= 2.0; // Update for the next (smaller) k_loop_val
153                    }
154                }
155            }
156            // Apply Hadamard gate
157            builder.h_gate(qubits[i]);
158        }
159        builder.build_subroutine()
160    }
161}
162
163// Allow conversion from Subroutine to Circuit
164impl TryFrom<Subroutine> for Circuit {
165    fn try_from(value: Subroutine) -> Result<Self, Self::Error> {
166        let mut circuit = Circuit::new(value.num_qubits);
167        for gate in value.gates {
168            circuit.add_gate(gate)?;
169        }
170        Ok(circuit)
171    }
172
173    type Error = Error;
174}