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}