1use super::qubit::LogicalQubit;
6use crate::{
7 decompose::{self, Algorithm, AuxMode, Schema},
8 execution::U4Gate,
9};
10use num::Complex;
11use serde::{Deserialize, Serialize};
12use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4, FRAC_PI_8, PI};
13
14#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
15pub enum QuantumGate {
16 PauliX,
17 PauliY,
18 PauliZ,
19 RotationX(f64),
20 RotationY(f64),
21 RotationZ(f64),
22 Phase(f64),
23 Hadamard,
24}
25
26pub type Cf64 = Complex<f64>;
27pub type Matrix = [[Cf64; 2]; 2];
28
29impl QuantumGate {
30 pub fn s() -> Self {
31 Self::Phase(FRAC_PI_2)
32 }
33
34 pub fn sd() -> Self {
35 Self::Phase(-FRAC_PI_2)
36 }
37
38 pub fn t() -> Self {
39 Self::Phase(FRAC_PI_4)
40 }
41
42 pub fn td() -> Self {
43 Self::Phase(-FRAC_PI_4)
44 }
45
46 pub fn sqrt_t() -> Self {
47 Self::Phase(FRAC_PI_8)
48 }
49
50 pub fn sqrt_td() -> Self {
51 Self::Phase(-FRAC_PI_8)
52 }
53
54 pub(crate) fn is_identity(&self) -> bool {
55 let (angle, n) = match self {
56 QuantumGate::RotationX(angle) => (angle, 4.0),
57 QuantumGate::RotationY(angle) => (angle, 4.0),
58 QuantumGate::RotationZ(angle) => (angle, 4.0),
59 QuantumGate::Phase(angle) => (angle, 2.0),
60 _ => return false,
61 };
62
63 (angle % (n * PI)).abs() < 1e-14
64 }
65
66 pub(crate) fn is_minus_identity(&self) -> bool {
67 let angle = match self {
68 QuantumGate::RotationX(angle) => angle,
69 QuantumGate::RotationY(angle) => angle,
70 QuantumGate::RotationZ(angle) => angle,
71 _ => return false,
72 };
73
74 (angle % (2.0 * PI)).abs() < 1e-14
75 }
76
77 pub(crate) fn is_inverse(&self, other: &Self) -> bool {
78 match self {
79 QuantumGate::PauliX => matches!(other, QuantumGate::PauliX),
80 QuantumGate::PauliY => matches!(other, QuantumGate::PauliY),
81 QuantumGate::PauliZ => matches!(other, QuantumGate::PauliZ),
82 QuantumGate::RotationX(angle) => {
83 if let QuantumGate::RotationX(other) = other {
84 QuantumGate::RotationX(angle + other).is_identity()
85 } else {
86 false
87 }
88 }
89 QuantumGate::RotationY(angle) => {
90 if let QuantumGate::RotationY(other) = other {
91 QuantumGate::RotationY(angle + other).is_identity()
92 } else {
93 false
94 }
95 }
96 QuantumGate::RotationZ(angle) => {
97 if let QuantumGate::RotationZ(other) = other {
98 QuantumGate::RotationZ(angle + other).is_identity()
99 } else {
100 false
101 }
102 }
103 QuantumGate::Phase(angle) => {
104 if let QuantumGate::Phase(other) = other {
105 QuantumGate::Phase(angle + other).is_identity()
106 } else {
107 false
108 }
109 }
110 QuantumGate::Hadamard => matches!(other, QuantumGate::Hadamard),
111 }
112 }
113
114 pub(crate) fn matrix(&self) -> Matrix {
115 match self {
116 QuantumGate::PauliX => [[0.0.into(), 1.0.into()], [1.0.into(), 0.0.into()]],
117 QuantumGate::PauliY => [[0.0.into(), -Cf64::i()], [Cf64::i(), 0.0.into()]],
118 QuantumGate::PauliZ => [[1.0.into(), 0.0.into()], [0.0.into(), (-1.0).into()]],
119 QuantumGate::RotationX(angle) => [
120 [(angle / 2.0).cos().into(), -Cf64::i() * (angle / 2.0).sin()],
121 [-Cf64::i() * (angle / 2.0).sin(), (angle / 2.0).cos().into()],
122 ],
123 QuantumGate::RotationY(angle) => [
124 [(angle / 2.0).cos().into(), (-(angle / 2.0).sin()).into()],
125 [(angle / 2.0).sin().into(), (angle / 2.0).cos().into()],
126 ],
127 QuantumGate::RotationZ(angle) => [
128 [(-Cf64::i() * (angle / 2.0)).exp(), 0.0.into()],
129 [0.0.into(), (Cf64::i() * (angle / 2.0)).exp()],
130 ],
131 QuantumGate::Phase(angle) => [
132 [1.0.into(), 0.0.into()],
133 [0.0.into(), (Cf64::i() * angle).exp()],
134 ],
135 QuantumGate::Hadamard => [
136 [(1.0 / 2.0f64.sqrt()).into(), (1.0 / 2.0f64.sqrt()).into()],
137 [(1.0 / 2.0f64.sqrt()).into(), (-1.0 / 2.0f64.sqrt()).into()],
138 ],
139 }
140 }
141
142 pub(crate) fn su2_matrix(&self) -> Matrix {
143 match self {
144 QuantumGate::PauliX => QuantumGate::RotationX(PI).matrix(),
145 QuantumGate::PauliY => QuantumGate::RotationY(PI).matrix(),
146 QuantumGate::PauliZ => QuantumGate::RotationZ(PI).matrix(),
147 QuantumGate::Phase(angle) => QuantumGate::RotationZ(*angle).matrix(),
148 QuantumGate::Hadamard => [
149 [-Cf64::i() * FRAC_1_SQRT_2, -Cf64::i() * FRAC_1_SQRT_2],
150 [-Cf64::i() * FRAC_1_SQRT_2, Cf64::i() * FRAC_1_SQRT_2],
151 ],
152 _ => self.matrix(),
153 }
154 }
155
156 pub(crate) fn su2_phase(&self) -> f64 {
157 match self {
158 QuantumGate::PauliX => FRAC_PI_2,
159 QuantumGate::PauliY => FRAC_PI_2,
160 QuantumGate::PauliZ => FRAC_PI_2,
161 QuantumGate::Phase(angle) => angle / 2.0,
162 QuantumGate::Hadamard => FRAC_PI_2,
163 _ => 0.0,
164 }
165 }
166
167 pub(crate) fn inverse(&self) -> Self {
168 match self {
169 QuantumGate::PauliX => QuantumGate::PauliX,
170 QuantumGate::PauliY => QuantumGate::PauliY,
171 QuantumGate::PauliZ => QuantumGate::PauliZ,
172 QuantumGate::RotationX(angle) => QuantumGate::RotationX(-angle),
173 QuantumGate::RotationY(angle) => QuantumGate::RotationY(-angle),
174 QuantumGate::RotationZ(angle) => QuantumGate::RotationZ(-angle),
175 QuantumGate::Phase(angle) => QuantumGate::Phase(-angle),
176 QuantumGate::Hadamard => QuantumGate::Hadamard,
177 }
178 }
179
180 pub(crate) fn decomposition_list(&self, control_size: usize) -> Vec<Algorithm> {
181 match self {
182 QuantumGate::PauliX | QuantumGate::PauliY | QuantumGate::PauliZ => {
183 if control_size <= 3 {
184 vec![Algorithm::Optimal]
185 } else if control_size == 4 {
186 vec![
187 Algorithm::VChain(AuxMode::Clean),
188 Algorithm::VChain(AuxMode::Dirty),
189 Algorithm::Optimal,
190 ]
191 } else {
192 vec![
193 Algorithm::VChain(AuxMode::Clean),
194 Algorithm::VChain(AuxMode::Dirty),
195 Algorithm::SingleAux(AuxMode::Clean),
196 Algorithm::SingleAux(AuxMode::Dirty),
197 Algorithm::LinearDepth,
198 ]
199 }
200 }
201 QuantumGate::RotationX(_) | QuantumGate::RotationY(_) | QuantumGate::RotationZ(_) => {
202 if control_size > 1 {
203 vec![Algorithm::Network, Algorithm::SU2]
204 } else {
205 vec![Algorithm::CU2]
206 }
207 }
208 QuantumGate::Phase(_) | QuantumGate::Hadamard => {
209 if control_size > 1 {
210 vec![
211 Algorithm::Network,
212 Algorithm::SU2Rewrite,
213 Algorithm::LinearDepth,
214 ]
215 } else {
216 vec![Algorithm::CU2]
217 }
218 }
219 }
220 }
221
222 pub(crate) fn decompose(
223 &self,
224 target: LogicalQubit,
225 control: &[LogicalQubit],
226 schema: Schema,
227 u4_gate: U4Gate,
228 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
229 match self {
230 QuantumGate::PauliX => Self::decompose_x(target, control, schema, u4_gate),
231 QuantumGate::PauliY => Self::decompose_y(target, control, schema, u4_gate),
232 QuantumGate::PauliZ => Self::decompose_z(target, control, schema, u4_gate),
233 QuantumGate::RotationX(_) | QuantumGate::RotationY(_) | QuantumGate::RotationZ(_) => {
234 self.decompose_r(target, control, schema, u4_gate)
235 }
236 QuantumGate::Phase(angle) => {
237 Self::decompose_phase(*angle, target, control, schema, u4_gate)
238 }
239 QuantumGate::Hadamard => Self::decompose_hadamard(target, control, schema, u4_gate),
240 }
241 }
242
243 fn decompose_r(
244 &self,
245 target: LogicalQubit,
246 control: &[LogicalQubit],
247 schema: Schema,
248 u4_gate: U4Gate,
249 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
250 match &schema.algorithm {
251 Algorithm::Network => {
252 decompose::u2::network(*self, control, &schema.aux_qubits.unwrap(), target, u4_gate)
253 }
254 Algorithm::SU2 => decompose::su2::decompose(*self, control, target, u4_gate),
255 Algorithm::CU2 => decompose::u2::cu2(self.matrix(), control[0], target, u4_gate),
256 _ => panic!("Invalid Decomposition for Rotation Gate"),
257 }
258 }
259
260 fn decompose_x(
261 target: LogicalQubit,
262 control: &[LogicalQubit],
263 schema: Schema,
264 u4_gate: U4Gate,
265 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
266 match &schema.algorithm {
267 Algorithm::VChain(aux_mode) => match aux_mode {
268 AuxMode::Clean => decompose::x::v_chain_clean(
269 control,
270 &schema.aux_qubits.unwrap(),
271 target,
272 u4_gate,
273 ),
274 AuxMode::Dirty => decompose::x::v_chain_dirty(
275 control,
276 &schema.aux_qubits.unwrap(),
277 target,
278 u4_gate,
279 ),
280 },
281 Algorithm::SingleAux(aux_mode) => decompose::x::mxc_1_aux(
282 control,
283 schema.aux_qubits.unwrap()[0],
284 target,
285 u4_gate,
286 *aux_mode,
287 ),
288 Algorithm::LinearDepth => {
289 decompose::u2::decompose(QuantumGate::PauliX, control, target, u4_gate)
290 }
291 Algorithm::Optimal => match control.len() {
292 1 => u4_gate.cnot(control[0], target),
293 2 => decompose::x::c2x(control[0], control[1], target, u4_gate),
294 3 => decompose::x::c3x(control[0], control[1], control[2], target, u4_gate),
295 _ => decompose::x::c4x(
296 control[0], control[1], control[2], control[3], target, u4_gate,
297 ),
298 },
299 _ => panic!("Invalid Decomposition for Pauli Gate"),
300 }
301 }
302
303 fn decompose_y(
304 target: LogicalQubit,
305 control: &[LogicalQubit],
306 schema: Schema,
307 u4_gate: U4Gate,
308 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
309 [
310 (QuantumGate::sd(), target, None),
311 (QuantumGate::Hadamard, target, None),
312 ]
313 .into_iter()
314 .chain(Self::decompose_x(target, control, schema, u4_gate))
315 .chain([
316 (QuantumGate::Hadamard, target, None),
317 (QuantumGate::s(), target, None),
318 ])
319 .collect()
320 }
321
322 fn decompose_z(
323 target: LogicalQubit,
324 control: &[LogicalQubit],
325 schema: Schema,
326 u4_gate: U4Gate,
327 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
328 [(QuantumGate::Hadamard, target, None)]
329 .into_iter()
330 .chain(Self::decompose_x(target, control, schema, u4_gate))
331 .chain([(QuantumGate::Hadamard, target, None)])
332 .collect()
333 }
334
335 fn decompose_phase(
336 angle: f64,
337 target: LogicalQubit,
338 control: &[LogicalQubit],
339 schema: Schema,
340 u4_gate: U4Gate,
341 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
342 match &schema.algorithm {
343 Algorithm::LinearDepth => {
344 decompose::u2::decompose(Self::Phase(angle), control, target, u4_gate)
345 }
346 Algorithm::Network => decompose::u2::network(
347 Self::Phase(angle),
348 control,
349 &schema.aux_qubits.unwrap(),
350 target,
351 u4_gate,
352 ),
353 Algorithm::SU2Rewrite => {
354 let control: Vec<_> = control.iter().cloned().chain([target]).collect();
355 decompose::su2::decompose(
356 QuantumGate::RotationZ(-2.0 * angle),
357 &control,
358 schema.aux_qubits.unwrap()[0],
359 u4_gate,
360 )
361 }
362 Algorithm::CU2 => decompose::u2::cu2(
363 QuantumGate::Phase(angle).matrix(),
364 control[0],
365 target,
366 u4_gate,
367 ),
368 _ => panic!("Invalid Decomposition for Phase Gate"),
369 }
370 }
371
372 fn decompose_hadamard(
373 target: LogicalQubit,
374 control: &[LogicalQubit],
375 schema: Schema,
376 u4_gate: U4Gate,
377 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
378 match &schema.algorithm {
379 Algorithm::LinearDepth => {
380 decompose::u2::decompose(Self::Hadamard, control, target, u4_gate)
381 }
382 Algorithm::Network => decompose::u2::network(
383 Self::Hadamard,
384 control,
385 &schema.aux_qubits.unwrap(),
386 target,
387 u4_gate,
388 ),
389 Algorithm::SU2Rewrite => {
390 let phase = QuantumGate::Hadamard.su2_phase();
391
392 decompose::su2::decompose(QuantumGate::Hadamard, control, target, u4_gate)
393 .into_iter()
394 .chain(decompose::su2::decompose(
395 QuantumGate::RotationZ(-2.0 * phase),
396 control,
397 schema.aux_qubits.unwrap()[0],
398 u4_gate,
399 ))
400 .collect()
401 }
402 Algorithm::CU2 => {
403 decompose::u2::cu2(QuantumGate::Hadamard.matrix(), control[0], target, u4_gate)
404 }
405 _ => panic!("Invalid Decomposition for Hadamard Gate"),
406 }
407 }
408}
409
410pub(crate) fn matrix_dot(matrix_a: &Matrix, matrix_b: &Matrix) -> Matrix {
411 [
412 [
413 matrix_a[0][0] * matrix_b[0][0] + matrix_a[0][1] * matrix_b[1][0],
414 matrix_a[0][0] * matrix_b[0][1] + matrix_a[0][1] * matrix_b[1][1],
415 ],
416 [
417 matrix_a[1][0] * matrix_b[0][0] + matrix_a[1][1] * matrix_b[1][0],
418 matrix_a[1][0] * matrix_b[0][1] + matrix_a[1][1] * matrix_b[1][1],
419 ],
420 ]
421}