1use super::qubit::LogicalQubit;
6use crate::{
7 decompose::{
8 self,
9 network::network,
10 u2::su2_rewrite_hadamard,
11 x::{single_aux, v_chain::v_chain, CXMode},
12 Algorithm, AuxMode, DepthMode, Schema,
13 },
14 execution::U4Gate,
15};
16use num::Complex;
17use serde::{Deserialize, Serialize};
18use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4, FRAC_PI_8, PI};
19
20#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
21pub enum Param {
22 Value(f64),
23 Ref {
24 index: usize,
25 value: f64,
26 multiplier: f64,
27 },
28}
29
30impl From<f64> for Param {
31 fn from(value: f64) -> Self {
32 Self::Value(value)
33 }
34}
35
36impl Param {
37 pub fn new_ref(index: usize, multiplier: f64) -> Self {
38 Self::Ref {
39 index,
40 value: 0.0,
41 multiplier,
42 }
43 }
44
45 pub fn new_value(value: f64) -> Self {
46 value.into()
47 }
48
49 pub(crate) fn update_ref(&mut self, new_value: f64) {
50 if let Param::Ref { value, .. } = self {
51 *value = new_value;
52 } else {
53 panic!()
54 }
55 }
56
57 pub(crate) fn index(&self) -> usize {
58 if let Param::Ref { index, .. } = self {
59 *index
60 } else {
61 panic!()
62 }
63 }
64
65 pub(crate) fn value(&self) -> f64 {
66 match self {
67 Param::Value(value) => *value,
68 Param::Ref {
69 value, multiplier, ..
70 } => value * multiplier,
71 }
72 }
73
74 fn inverse(&self) -> Self {
75 match self {
76 Param::Value(value) => Param::Value(-value),
77 Param::Ref {
78 index,
79 value,
80 multiplier,
81 } => Param::Ref {
82 index: *index,
83 value: *value,
84 multiplier: -multiplier,
85 },
86 }
87 }
88}
89
90#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
91pub enum QuantumGate {
92 PauliX,
93 PauliY,
94 PauliZ,
95 RotationX(Param),
96 RotationY(Param),
97 RotationZ(Param),
98 Phase(Param),
99 Hadamard,
100}
101
102pub type Cf64 = Complex<f64>;
103pub type Matrix = [[Cf64; 2]; 2];
104
105impl QuantumGate {
106 pub fn s() -> Self {
107 Self::Phase(FRAC_PI_2.into())
108 }
109
110 pub fn sd() -> Self {
111 Self::Phase((-FRAC_PI_2).into())
112 }
113
114 pub fn t() -> Self {
115 Self::Phase(FRAC_PI_4.into())
116 }
117
118 pub fn td() -> Self {
119 Self::Phase((-FRAC_PI_4).into())
120 }
121
122 pub fn sqrt_t() -> Self {
123 Self::Phase(FRAC_PI_8.into())
124 }
125
126 pub fn sqrt_td() -> Self {
127 Self::Phase((-FRAC_PI_8).into())
128 }
129
130 pub fn is_permutation(&self) -> bool {
131 match self {
132 QuantumGate::RotationX(param) | QuantumGate::RotationY(param) => {
133 if let Param::Value(value) = param {
134 (value.abs() - PI).abs() <= 1e-10 || (value.abs() - 2.0 * PI).abs() <= 1e-10
135 } else {
136 false
137 }
138 }
139 QuantumGate::Hadamard => false,
140 _ => true,
141 }
142 }
143
144 pub fn is_diagonal(&self) -> bool {
145 matches!(
146 self,
147 QuantumGate::PauliZ | QuantumGate::RotationZ(_) | QuantumGate::Phase(_)
148 )
149 }
150
151 pub(crate) fn is_identity(&self) -> bool {
152 let (angle, n) = match self {
153 QuantumGate::RotationX(angle) => (angle, 4.0),
154 QuantumGate::RotationY(angle) => (angle, 4.0),
155 QuantumGate::RotationZ(angle) => (angle, 4.0),
156 QuantumGate::Phase(angle) => (angle, 2.0),
157 _ => return false,
158 };
159 if let Param::Value(angle) = angle {
160 (angle % (n * PI)).abs() < 1e-14
161 } else {
162 false
163 }
164 }
165
166 pub(crate) fn is_inverse(&self, other: &Self) -> bool {
167 match self {
168 QuantumGate::PauliX => matches!(other, QuantumGate::PauliX),
169 QuantumGate::PauliY => matches!(other, QuantumGate::PauliY),
170 QuantumGate::PauliZ => matches!(other, QuantumGate::PauliZ),
171 QuantumGate::RotationX(angle) => {
172 if let QuantumGate::RotationX(Param::Value(other)) = other {
173 if let Param::Value(angle) = angle {
174 QuantumGate::RotationX((angle + other).into()).is_identity()
175 } else {
176 false
177 }
178 } else {
179 false
180 }
181 }
182 QuantumGate::RotationY(angle) => {
183 if let QuantumGate::RotationY(Param::Value(other)) = other {
184 if let Param::Value(angle) = angle {
185 QuantumGate::RotationY((angle + other).into()).is_identity()
186 } else {
187 false
188 }
189 } else {
190 false
191 }
192 }
193 QuantumGate::RotationZ(angle) => {
194 if let QuantumGate::RotationZ(Param::Value(other)) = other {
195 if let Param::Value(angle) = angle {
196 QuantumGate::RotationZ((angle + other).into()).is_identity()
197 } else {
198 false
199 }
200 } else {
201 false
202 }
203 }
204 QuantumGate::Phase(angle) => {
205 if let QuantumGate::Phase(Param::Value(other)) = other {
206 if let Param::Value(angle) = angle {
207 QuantumGate::Phase((angle + other).into()).is_identity()
208 } else {
209 false
210 }
211 } else {
212 false
213 }
214 }
215 QuantumGate::Hadamard => matches!(other, QuantumGate::Hadamard),
216 }
217 }
218
219 pub(crate) fn matrix(&self) -> Matrix {
220 match self {
221 QuantumGate::PauliX => [[0.0.into(), 1.0.into()], [1.0.into(), 0.0.into()]],
222 QuantumGate::PauliY => [[0.0.into(), -Cf64::i()], [Cf64::i(), 0.0.into()]],
223 QuantumGate::PauliZ => [[1.0.into(), 0.0.into()], [0.0.into(), (-1.0).into()]],
224 QuantumGate::RotationX(angle) => {
225 let angle = angle.value();
226 [
227 [(angle / 2.0).cos().into(), -Cf64::i() * (angle / 2.0).sin()],
228 [-Cf64::i() * (angle / 2.0).sin(), (angle / 2.0).cos().into()],
229 ]
230 }
231 QuantumGate::RotationY(angle) => {
232 let angle = angle.value();
233 [
234 [(angle / 2.0).cos().into(), (-(angle / 2.0).sin()).into()],
235 [(angle / 2.0).sin().into(), (angle / 2.0).cos().into()],
236 ]
237 }
238 QuantumGate::RotationZ(angle) => {
239 let angle = angle.value();
240 [
241 [(-Cf64::i() * (angle / 2.0)).exp(), 0.0.into()],
242 [0.0.into(), (Cf64::i() * (angle / 2.0)).exp()],
243 ]
244 }
245 QuantumGate::Phase(angle) => [
246 [1.0.into(), 0.0.into()],
247 [0.0.into(), (Cf64::i() * angle.value()).exp()],
248 ],
249 QuantumGate::Hadamard => [
250 [(1.0 / 2.0f64.sqrt()).into(), (1.0 / 2.0f64.sqrt()).into()],
251 [(1.0 / 2.0f64.sqrt()).into(), (-1.0 / 2.0f64.sqrt()).into()],
252 ],
253 }
254 }
255
256 pub(crate) fn su2_matrix(&self) -> Matrix {
257 match self {
258 QuantumGate::PauliX => QuantumGate::RotationX(PI.into()).matrix(),
259 QuantumGate::PauliY => QuantumGate::RotationY(PI.into()).matrix(),
260 QuantumGate::PauliZ => QuantumGate::RotationZ(PI.into()).matrix(),
261 QuantumGate::Phase(angle) => QuantumGate::RotationZ(*angle).matrix(),
262 QuantumGate::Hadamard => [
263 [-Cf64::i() * FRAC_1_SQRT_2, -Cf64::i() * FRAC_1_SQRT_2],
264 [-Cf64::i() * FRAC_1_SQRT_2, Cf64::i() * FRAC_1_SQRT_2],
265 ],
266 _ => self.matrix(),
267 }
268 }
269
270 pub(crate) fn inverse(&self) -> Self {
271 match self {
272 QuantumGate::PauliX => QuantumGate::PauliX,
273 QuantumGate::PauliY => QuantumGate::PauliY,
274 QuantumGate::PauliZ => QuantumGate::PauliZ,
275 QuantumGate::RotationX(angle) => QuantumGate::RotationX(angle.inverse()),
276 QuantumGate::RotationY(angle) => QuantumGate::RotationY(angle.inverse()),
277 QuantumGate::RotationZ(angle) => QuantumGate::RotationZ(angle.inverse()),
278 QuantumGate::Phase(angle) => QuantumGate::Phase(angle.inverse()),
279 QuantumGate::Hadamard => QuantumGate::Hadamard,
280 }
281 }
282
283 pub(crate) fn decomposition_list(&self, control_size: usize) -> Vec<Algorithm> {
284 if std::env::var("KET_FORCE_DECOMPOSE_ALGORITHM").unwrap_or("0".to_string()) == "1" {
285 let algorithm = match std::env::var("KET_DECOMPOSE_ALGORITHM")
286 .unwrap_or_default()
287 .as_str()
288 {
289 "VChainC2XClean" => Algorithm::VChain(CXMode::C2X, AuxMode::Clean),
290 "VChainC3XClean" => Algorithm::VChain(CXMode::C3X, AuxMode::Clean),
291 "VChainC2XDirty" => Algorithm::VChain(CXMode::C2X, AuxMode::Dirty),
292 "VChainC3XDirty" => Algorithm::VChain(CXMode::C3X, AuxMode::Dirty),
293 "NetworkU2C2X" => Algorithm::NetworkU2(CXMode::C2X),
294 "NetworkU2C3X" => Algorithm::NetworkU2(CXMode::C3X),
295 "NetworkPauliC2X" => Algorithm::NetworkPauli(CXMode::C2X),
296 "NetworkPauliC3X" => Algorithm::NetworkPauli(CXMode::C3X),
297 "SingleAuxLinearClean" => Algorithm::SingleAux(DepthMode::Linear, AuxMode::Clean),
298 "SingleAuxLinearDirty" => Algorithm::SingleAux(DepthMode::Linear, AuxMode::Dirty),
299 "SingleAuxLogClean" => Algorithm::SingleAux(DepthMode::Log, AuxMode::Clean),
300 "SingleAuxLogDirty" => Algorithm::SingleAux(DepthMode::Log, AuxMode::Dirty),
301 "SingleAuxU2" => Algorithm::SingleAuxU2,
302 "LinearDepth" => Algorithm::LinearDepth,
303 "SU2Linear" => Algorithm::SU2(DepthMode::Linear),
304 "SU2Log" => Algorithm::SU2(DepthMode::Log),
305 "SU2Rewrite" => Algorithm::SU2Rewrite,
306 "AdjustableDepth" => Algorithm::AdjustableDepth,
307 other => panic!("undefined decomposition algorithm: {other}. Are you sure that you want to use this configuration?"),
308 };
309 return match self {
310 QuantumGate::PauliX | QuantumGate::PauliY | QuantumGate::PauliZ => {
311 assert!(
312 control_size <= 3
313 || matches!(
314 algorithm,
315 Algorithm::NetworkPauli(_)
316 | Algorithm::VChain(_, _)
317 | Algorithm::AdjustableDepth
318 | Algorithm::SingleAux(_, _)
319 | Algorithm::LinearDepth
320 )
321 );
322 if control_size <= 3 {
323 vec![Algorithm::NoAuxCX]
324 } else if control_size == 4 {
325 vec![algorithm, Algorithm::NoAuxCX]
326 } else {
327 vec![algorithm, Algorithm::LinearDepth]
328 }
329 }
330 QuantumGate::RotationX(_)
331 | QuantumGate::RotationY(_)
332 | QuantumGate::RotationZ(_) => {
333 assert!(
334 control_size <= 1
335 || matches!(
336 algorithm,
337 Algorithm::NetworkU2(_)
338 | Algorithm::SU2(_)
339 | Algorithm::LinearDepth
340 )
341 );
342 if control_size > 1 {
343 vec![algorithm, Algorithm::LinearDepth]
344 } else {
345 vec![Algorithm::CU2]
346 }
347 }
348 QuantumGate::Phase(_) | QuantumGate::Hadamard => {
349 assert!(
350 control_size <= 1
351 || matches!(
352 algorithm,
353 Algorithm::NetworkU2(_)
354 | Algorithm::SingleAuxU2
355 | Algorithm::SU2Rewrite
356 | Algorithm::LinearDepth
357 )
358 );
359 if control_size > 1 {
360 vec![algorithm, Algorithm::LinearDepth]
361 } else {
362 vec![Algorithm::CU2]
363 }
364 }
365 };
366 }
367
368 match self {
369 QuantumGate::PauliX | QuantumGate::PauliY | QuantumGate::PauliZ => {
370 if control_size <= 3 {
371 vec![Algorithm::NoAuxCX]
372 } else if control_size == 4 {
373 vec![
374 Algorithm::NetworkPauli(CXMode::C2X),
375 Algorithm::NetworkPauli(CXMode::C3X),
376 Algorithm::VChain(CXMode::C3X, AuxMode::Clean),
377 Algorithm::VChain(CXMode::C2X, AuxMode::Dirty),
378 Algorithm::NoAuxCX,
379 ]
380 } else {
381 vec![
382 Algorithm::NetworkPauli(CXMode::C2X),
383 Algorithm::NetworkPauli(CXMode::C3X),
384 Algorithm::VChain(CXMode::C3X, AuxMode::Clean),
385 Algorithm::VChain(CXMode::C2X, AuxMode::Dirty),
386 Algorithm::SingleAux(DepthMode::Linear, AuxMode::Dirty),
387 Algorithm::LinearDepth,
388 ]
389 }
390 }
391 QuantumGate::RotationX(_) | QuantumGate::RotationY(_) | QuantumGate::RotationZ(_) => {
392 if control_size > 1 {
393 vec![
394 Algorithm::NetworkU2(CXMode::C3X),
395 Algorithm::SU2(DepthMode::Linear),
396 ]
397 } else {
398 vec![Algorithm::CU2]
399 }
400 }
401 QuantumGate::Phase(_) | QuantumGate::Hadamard => {
402 if control_size > 1 {
403 vec![
404 Algorithm::NetworkU2(CXMode::C3X),
405 Algorithm::SU2Rewrite,
406 Algorithm::LinearDepth,
407 ]
408 } else {
409 vec![Algorithm::CU2]
410 }
411 }
412 }
413 }
414
415 pub(crate) fn decompose(
416 &self,
417 target: LogicalQubit,
418 control: &[LogicalQubit],
419 schema: Schema,
420 u4_gate: U4Gate,
421 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
422 match self {
423 QuantumGate::PauliX => Self::decompose_x(target, control, schema, u4_gate),
424 QuantumGate::PauliY => Self::decompose_y(target, control, schema, u4_gate),
425 QuantumGate::PauliZ => Self::decompose_z(target, control, schema, u4_gate),
426 QuantumGate::RotationX(_) | QuantumGate::RotationY(_) | QuantumGate::RotationZ(_) => {
427 self.decompose_r(target, control, schema, u4_gate)
428 }
429 QuantumGate::Phase(angle) => {
430 Self::decompose_phase(angle.value(), target, control, schema, u4_gate)
431 }
432 QuantumGate::Hadamard => Self::decompose_hadamard(target, control, schema, u4_gate),
433 }
434 }
435
436 fn decompose_r(
437 &self,
438 target: LogicalQubit,
439 control: &[LogicalQubit],
440 schema: Schema,
441 u4_gate: U4Gate,
442 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
443 match &schema.algorithm {
444 Algorithm::LinearDepth => decompose::u2::linear_depth(*self, control, target, u4_gate),
445
446 Algorithm::NetworkU2(cx_mode) => decompose::network::network(
447 *self,
448 control,
449 &schema.aux_qubits.unwrap().1,
450 target,
451 u4_gate,
452 *cx_mode,
453 schema.approximated,
454 ),
455 Algorithm::SU2(depth_mode) => decompose::su2::decompose(
456 *self,
457 control,
458 target,
459 u4_gate,
460 *depth_mode,
461 schema.approximated,
462 ),
463 Algorithm::CU2 => decompose::u2::cu2(self.matrix(), control[0], target, u4_gate),
464 _ => panic!("Invalid Decomposition for Rotation Gate"),
465 }
466 }
467
468 fn decompose_x(
469 target: LogicalQubit,
470 control: &[LogicalQubit],
471 schema: Schema,
472 u4_gate: U4Gate,
473 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
474 match &schema.algorithm {
475 Algorithm::VChain(cx_mode, aux_mode) => v_chain(
476 control,
477 &schema.aux_qubits.unwrap().1,
478 target,
479 u4_gate,
480 *aux_mode,
481 *cx_mode,
482 schema.approximated,
483 ),
484 Algorithm::SingleAux(depth_mode, aux_mode) => single_aux::decompose(
485 control,
486 schema.aux_qubits.unwrap().1[0],
487 target,
488 u4_gate,
489 *aux_mode,
490 *depth_mode,
491 schema.approximated,
492 ),
493 Algorithm::LinearDepth => {
494 decompose::u2::linear_depth(QuantumGate::PauliX, control, target, u4_gate)
495 }
496 Algorithm::NoAuxCX => {
497 decompose::x::c2to4x::c1to4x(control, target, u4_gate, schema.approximated)
498 }
499 Algorithm::AdjustableDepth => decompose::x::adjustable_depth(
500 control,
501 &schema.aux_qubits.unwrap().1,
502 target,
503 u4_gate,
504 schema.approximated,
505 ),
506 Algorithm::NetworkPauli(cx_mode) => network(
507 QuantumGate::PauliX,
508 control,
509 &schema.aux_qubits.unwrap().1,
510 target,
511 u4_gate,
512 *cx_mode,
513 schema.approximated,
514 ),
515 _ => panic!("Invalid Decomposition {schema:?} for Pauli Gate"),
516 }
517 }
518
519 fn decompose_y(
520 target: LogicalQubit,
521 control: &[LogicalQubit],
522 schema: Schema,
523 u4_gate: U4Gate,
524 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
525 [(QuantumGate::sd(), target, None)]
526 .into_iter()
527 .chain(Self::decompose_x(target, control, schema, u4_gate))
528 .chain([(QuantumGate::s(), target, None)])
529 .collect()
530 }
531
532 fn decompose_z(
533 target: LogicalQubit,
534 control: &[LogicalQubit],
535 schema: Schema,
536 u4_gate: U4Gate,
537 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
538 [(QuantumGate::Hadamard, target, None)]
539 .into_iter()
540 .chain(Self::decompose_x(target, control, schema, u4_gate))
541 .chain([(QuantumGate::Hadamard, target, None)])
542 .collect()
543 }
544
545 fn decompose_phase(
546 angle: f64,
547 target: LogicalQubit,
548 control: &[LogicalQubit],
549 schema: Schema,
550 u4_gate: U4Gate,
551 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
552 match &schema.algorithm {
553 Algorithm::LinearDepth => {
554 decompose::u2::linear_depth(Self::Phase(angle.into()), control, target, u4_gate)
555 }
556 Algorithm::NetworkU2(cx_mode) => network(
557 Self::Phase(angle.into()),
558 control,
559 &schema.aux_qubits.unwrap().1,
560 target,
561 u4_gate,
562 *cx_mode,
563 schema.approximated,
564 ),
565 Algorithm::SU2Rewrite => {
566 let control: Vec<_> = control.iter().cloned().chain([target]).collect();
567 decompose::su2::decompose(
568 QuantumGate::RotationZ((-2.0 * angle).into()),
569 &control,
570 schema.aux_qubits.unwrap().1[0],
571 u4_gate,
572 DepthMode::Linear,
573 schema.approximated,
574 )
575 }
576 Algorithm::CU2 => decompose::u2::cu2(
577 QuantumGate::Phase(angle.into()).matrix(),
578 control[0],
579 target,
580 u4_gate,
581 ),
582 Algorithm::SingleAuxU2 => decompose::u2::single_aux(
583 Self::Phase(angle.into()),
584 control,
585 schema.aux_qubits.unwrap().1[0],
586 target,
587 u4_gate,
588 schema.approximated,
589 ),
590 _ => panic!("Invalid Decomposition for Phase Gate"),
591 }
592 }
593
594 fn decompose_hadamard(
595 target: LogicalQubit,
596 control: &[LogicalQubit],
597 schema: Schema,
598 u4_gate: U4Gate,
599 ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
600 match &schema.algorithm {
601 Algorithm::LinearDepth => {
602 decompose::u2::linear_depth(Self::Hadamard, control, target, u4_gate)
603 }
604 Algorithm::NetworkU2(cx_mode) => network(
605 Self::Hadamard,
606 control,
607 &schema.aux_qubits.unwrap().1,
608 target,
609 u4_gate,
610 *cx_mode,
611 schema.approximated,
612 ),
613 Algorithm::SU2Rewrite => {
614 su2_rewrite_hadamard(control, schema.aux_qubits.unwrap().1[0], target, u4_gate)
615 }
616 Algorithm::CU2 => {
617 decompose::u2::cu2(QuantumGate::Hadamard.matrix(), control[0], target, u4_gate)
618 }
619 Algorithm::SingleAuxU2 => decompose::u2::single_aux(
620 QuantumGate::Hadamard,
621 control,
622 schema.aux_qubits.unwrap().1[0],
623 target,
624 u4_gate,
625 schema.approximated,
626 ),
627 _ => panic!("Invalid Decomposition for Hadamard Gate"),
628 }
629 }
630}
631
632pub(crate) fn matrix_dot(matrix_a: &Matrix, matrix_b: &Matrix) -> Matrix {
633 [
634 [
635 matrix_a[0][0] * matrix_b[0][0] + matrix_a[0][1] * matrix_b[1][0],
636 matrix_a[0][0] * matrix_b[0][1] + matrix_a[0][1] * matrix_b[1][1],
637 ],
638 [
639 matrix_a[1][0] * matrix_b[0][0] + matrix_a[1][1] * matrix_b[1][0],
640 matrix_a[1][0] * matrix_b[0][1] + matrix_a[1][1] * matrix_b[1][1],
641 ],
642 ]
643}