roqoqo/operations/
two_qubit_gate_operations.rs

1// Copyright © 2021-2024 HQS Quantum Simulations GmbH. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4// in compliance with the License. You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the
9// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
10// express or implied. See the License for the specific language governing permissions and
11// limitations under the License.
12
13use crate::operations::single_qubit_gate_operations::*;
14use crate::prelude::*;
15use crate::Circuit;
16use crate::RoqoqoError;
17use ndarray::{array, Array2};
18use num_complex::Complex64;
19use qoqo_calculator::{CalculatorComplex, CalculatorFloat};
20#[cfg(feature = "overrotate")]
21use rand_distr::{Distribution, Normal};
22use std::convert::TryFrom;
23use std::f64::consts::PI;
24
25use super::SupportedVersion;
26
27/// The KAK decomposition of a two-qubit gate.
28///
29/// Each two-qubit gate can be described by a KAK decomposition (<http://arxiv.org/abs/quant-ph/0507171>).
30///
31/// A two qubit gate is decomposed into four single qubit gates, one for each qubit acting before and after applying the
32/// entangling operation based on the k_vector:  
33///
34/// U(k_vector) = exp(i (k_vector(0) XX + k_vector(1) YY + k_vector(2) ZZ))
35///
36/// This struct contains all information on the KAK decomposition of a two qubit gate.
37#[derive(Debug, Clone, PartialEq)]
38pub struct KakDecomposition {
39    /// Global phase of KAK decomposition
40    pub global_phase: CalculatorFloat,
41    /// Three component vector of the KAK decomposition
42    pub k_vector: [CalculatorFloat; 3],
43    /// Circuit including operations acting on control and target qubits before two-qubit entangling
44    pub circuit_before: Option<Circuit>,
45    /// Circuit including operations acting on control and target qubits after two-qubit entangling
46    pub circuit_after: Option<Circuit>,
47}
48
49/// The CNOT controlled not gate.
50///
51/// Flips the state of a `target` qubit based on the `control` qubit.
52#[allow(clippy::upper_case_acronyms)]
53#[derive(
54    Debug,
55    Clone,
56    PartialEq,
57    Eq,
58    roqoqo_derive::InvolveQubits,
59    roqoqo_derive::SupportedVersion,
60    roqoqo_derive::Operate,
61    roqoqo_derive::Substitute,
62    roqoqo_derive::OperateTwoQubit,
63)]
64#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
65#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
66pub struct CNOT {
67    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of NOT on the target qubit.
68    control: usize,
69    /// The index of the least significant qubit in the unitary representation. Here, the qubit NOT is applied to.
70    target: usize,
71}
72
73#[allow(non_upper_case_globals)]
74const TAGS_CNOT: &[&str; 4] = &[
75    "Operation",
76    "GateOperation",
77    "TwoQubitGateOperation",
78    "CNOT",
79];
80
81/// Trait for all Operations acting with a unitary gate on a set of qubits.
82impl OperateGate for CNOT {
83    /// Returns unitary matrix of the gate.
84    ///
85    /// # Returns
86    ///
87    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
88    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
89    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
90        Ok(array![
91            [
92                Complex64::new(1.0, 0.0),
93                Complex64::new(0.0, 0.0),
94                Complex64::new(0.0, 0.0),
95                Complex64::new(0.0, 0.0)
96            ],
97            [
98                Complex64::new(0.0, 0.0),
99                Complex64::new(1.0, 0.0),
100                Complex64::new(0.0, 0.0),
101                Complex64::new(0.0, 0.0)
102            ],
103            [
104                Complex64::new(0.0, 0.0),
105                Complex64::new(0.0, 0.0),
106                Complex64::new(0.0, 0.0),
107                Complex64::new(1.0, 0.0)
108            ],
109            [
110                Complex64::new(0.0, 0.0),
111                Complex64::new(0.0, 0.0),
112                Complex64::new(1.0, 0.0),
113                Complex64::new(0.0, 0.0)
114            ],
115        ])
116    }
117}
118
119/// Trait for all gate operations acting on exactly two qubits.
120impl OperateTwoQubitGate for CNOT {
121    /// Returns [KakDecomposition] of the  gate.
122    ///
123    /// # Returns
124    ///
125    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
126    fn kak_decomposition(&self) -> KakDecomposition {
127        let mut circuit_b = Circuit::new();
128        circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
129        circuit_b += RotateY::new(self.control, CalculatorFloat::FRAC_PI_2);
130        circuit_b += RotateX::new(self.target, CalculatorFloat::FRAC_PI_2);
131
132        let mut circuit_a = Circuit::new();
133        circuit_a += RotateY::new(self.control, CalculatorFloat::FRAC_PI_2 * (-1.0));
134
135        KakDecomposition {
136            global_phase: CalculatorFloat::FRAC_PI_4,
137            k_vector: [
138                CalculatorFloat::FRAC_PI_4,
139                CalculatorFloat::ZERO,
140                CalculatorFloat::ZERO,
141            ],
142            circuit_before: Some(circuit_b),
143            circuit_after: Some(circuit_a),
144        }
145    }
146}
147
148/// The SWAP gate.
149///
150/// Swaps the states of two qubits `target` and `control`.
151#[allow(clippy::upper_case_acronyms)]
152#[derive(
153    Debug,
154    Clone,
155    PartialEq,
156    Eq,
157    roqoqo_derive::InvolveQubits,
158    roqoqo_derive::SupportedVersion,
159    roqoqo_derive::Operate,
160    roqoqo_derive::Substitute,
161    roqoqo_derive::OperateTwoQubit,
162)]
163#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
164#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
165pub struct SWAP {
166    /// The index of the most significant qubit in the unitary representation.
167    control: usize,
168    /// The index of the least significant qubit in the unitary representation.
169    target: usize,
170}
171
172#[allow(non_upper_case_globals)]
173const TAGS_SWAP: &[&str; 4] = &[
174    "Operation",
175    "GateOperation",
176    "TwoQubitGateOperation",
177    "SWAP",
178];
179
180/// Trait for all Operations acting with a unitary gate on a set of qubits.
181impl OperateGate for SWAP {
182    /// Returns unitary matrix of the gate.
183    ///
184    /// # Returns
185    ///
186    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
187    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
188    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
189        Ok(array![
190            [
191                Complex64::new(1.0, 0.0),
192                Complex64::new(0.0, 0.0),
193                Complex64::new(0.0, 0.0),
194                Complex64::new(0.0, 0.0)
195            ],
196            [
197                Complex64::new(0.0, 0.0),
198                Complex64::new(0.0, 0.0),
199                Complex64::new(1.0, 0.0),
200                Complex64::new(0.0, 0.0)
201            ],
202            [
203                Complex64::new(0.0, 0.0),
204                Complex64::new(1.0, 0.0),
205                Complex64::new(0.0, 0.0),
206                Complex64::new(0.0, 0.0)
207            ],
208            [
209                Complex64::new(0.0, 0.0),
210                Complex64::new(0.0, 0.0),
211                Complex64::new(0.0, 0.0),
212                Complex64::new(1.0, 0.0)
213            ],
214        ])
215    }
216}
217
218/// Trait for all gate operations acting on exactly two qubits.
219impl OperateTwoQubitGate for SWAP {
220    /// Returns [KakDecomposition] of the gate.
221    ///
222    /// # Returns
223    ///
224    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
225    fn kak_decomposition(&self) -> KakDecomposition {
226        KakDecomposition {
227            global_phase: CalculatorFloat::from(-PI / 4.0),
228            k_vector: [
229                CalculatorFloat::FRAC_PI_4,
230                CalculatorFloat::FRAC_PI_4,
231                CalculatorFloat::FRAC_PI_4,
232            ],
233            circuit_before: None,
234            circuit_after: None,
235        }
236    }
237}
238
239/// The ISwap gate.
240///
241/// Swaps the states of two qubits `target` and `control`
242/// and applies a complex phase `i` to states |01> and |10>.
243#[allow(clippy::upper_case_acronyms)]
244#[derive(
245    Debug,
246    Clone,
247    PartialEq,
248    Eq,
249    roqoqo_derive::InvolveQubits,
250    roqoqo_derive::SupportedVersion,
251    roqoqo_derive::Operate,
252    roqoqo_derive::Substitute,
253    roqoqo_derive::OperateTwoQubit,
254)]
255#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
256#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
257pub struct ISwap {
258    /// The index of the most significant qubit in the unitary representation.
259    control: usize,
260    /// The index of the least significant qubit in the unitary representation.
261    target: usize,
262}
263
264#[allow(non_upper_case_globals)]
265const TAGS_ISwap: &[&str; 4] = &[
266    "Operation",
267    "GateOperation",
268    "TwoQubitGateOperation",
269    "ISwap",
270];
271
272/// Trait for all Operations acting with a unitary gate on a set of qubits.
273impl OperateGate for ISwap {
274    /// Returns unitary matrix of the gate.
275    ///
276    /// # Returns
277    ///
278    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
279    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
280    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
281        Ok(array![
282            [
283                Complex64::new(1.0, 0.0),
284                Complex64::new(0.0, 0.0),
285                Complex64::new(0.0, 0.0),
286                Complex64::new(0.0, 0.0)
287            ],
288            [
289                Complex64::new(0.0, 0.0),
290                Complex64::new(0.0, 0.0),
291                Complex64::new(0.0, 1.0),
292                Complex64::new(0.0, 0.0)
293            ],
294            [
295                Complex64::new(0.0, 0.0),
296                Complex64::new(0.0, 1.0),
297                Complex64::new(0.0, 0.0),
298                Complex64::new(0.0, 0.0)
299            ],
300            [
301                Complex64::new(0.0, 0.0),
302                Complex64::new(0.0, 0.0),
303                Complex64::new(0.0, 0.0),
304                Complex64::new(1.0, 0.0)
305            ],
306        ])
307    }
308}
309
310/// Trait for all gate operations acting on exactly two qubits.
311impl OperateTwoQubitGate for ISwap {
312    /// Returns [KakDecomposition] of the gate.
313    ///
314    /// # Returns
315    ///
316    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
317    fn kak_decomposition(&self) -> KakDecomposition {
318        KakDecomposition {
319            global_phase: CalculatorFloat::ZERO,
320            k_vector: [
321                CalculatorFloat::FRAC_PI_4,
322                CalculatorFloat::FRAC_PI_4,
323                CalculatorFloat::ZERO,
324            ],
325            circuit_before: None,
326            circuit_after: None,
327        }
328    }
329}
330
331/// The Fermionic SWAP gate.
332///
333/// Swaps the states of two qubits `target` and `control`
334/// and applies a sign `-1` to states |01> and |10>.
335/// Conserves the correct sign when the qubits represent Fermionic degrees of freedom.
336#[allow(clippy::upper_case_acronyms)]
337#[derive(
338    Debug,
339    Clone,
340    PartialEq,
341    Eq,
342    roqoqo_derive::InvolveQubits,
343    roqoqo_derive::SupportedVersion,
344    roqoqo_derive::Operate,
345    roqoqo_derive::Substitute,
346    roqoqo_derive::OperateTwoQubit,
347)]
348#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
349#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
350pub struct FSwap {
351    /// The index of the most significant qubit in the unitary representation.
352    control: usize,
353    /// The index of the least significant qubit in the unitary representation.
354    target: usize,
355}
356
357#[allow(non_upper_case_globals)]
358const TAGS_FSwap: &[&str; 4] = &[
359    "Operation",
360    "GateOperation",
361    "TwoQubitGateOperation",
362    "FSwap",
363];
364
365/// Trait for all Operations acting with a unitary gate on a set of qubits.
366impl OperateGate for FSwap {
367    /// Returns unitary matrix of the gate.
368    ///
369    /// # Returns
370    ///
371    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
372    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
373    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
374        Ok(array![
375            [
376                Complex64::new(1.0, 0.0),
377                Complex64::new(0.0, 0.0),
378                Complex64::new(0.0, 0.0),
379                Complex64::new(0.0, 0.0)
380            ],
381            [
382                Complex64::new(0.0, 0.0),
383                Complex64::new(0.0, 0.0),
384                Complex64::new(1.0, 0.0),
385                Complex64::new(0.0, 0.0)
386            ],
387            [
388                Complex64::new(0.0, 0.0),
389                Complex64::new(1.0, 0.0),
390                Complex64::new(0.0, 0.0),
391                Complex64::new(0.0, 0.0)
392            ],
393            [
394                Complex64::new(0.0, 0.0),
395                Complex64::new(0.0, 0.0),
396                Complex64::new(0.0, 0.0),
397                Complex64::new(-1.0, 0.0)
398            ],
399        ])
400    }
401}
402
403/// Trait for all gate operations acting on exactly two qubits.
404impl OperateTwoQubitGate for FSwap {
405    /// Returns [KakDecomposition] of the gate.
406    ///
407    /// # Returns
408    ///
409    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
410    fn kak_decomposition(&self) -> KakDecomposition {
411        let mut circuit_b = Circuit::new();
412        circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2 * (-1.0));
413        circuit_b += RotateZ::new(self.target, CalculatorFloat::FRAC_PI_2 * (-1.0));
414
415        KakDecomposition {
416            global_phase: CalculatorFloat::FRAC_PI_2 * (-1.0),
417            k_vector: [
418                CalculatorFloat::FRAC_PI_4,
419                CalculatorFloat::FRAC_PI_4,
420                CalculatorFloat::ZERO,
421            ],
422            circuit_before: Some(circuit_b),
423            circuit_after: None,
424        }
425    }
426}
427
428/// The square root ISwap gate.
429///
430/// Square root version of the ISwap gate so that
431/// SqrtISwap * SqrtISwap = ISwap
432#[allow(clippy::upper_case_acronyms)]
433#[derive(
434    Debug,
435    Clone,
436    PartialEq,
437    Eq,
438    roqoqo_derive::InvolveQubits,
439    roqoqo_derive::SupportedVersion,
440    roqoqo_derive::Operate,
441    roqoqo_derive::Substitute,
442    roqoqo_derive::OperateTwoQubit,
443)]
444#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
445#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
446pub struct SqrtISwap {
447    /// The index of the most significant qubit in the unitary representation.
448    control: usize,
449    /// The index of the least significant qubit in the unitary representation.
450    target: usize,
451}
452
453#[allow(non_upper_case_globals)]
454const TAGS_SqrtISwap: &[&str; 4] = &[
455    "Operation",
456    "GateOperation",
457    "TwoQubitGateOperation",
458    "SqrtISwap",
459];
460
461/// Trait for all Operations acting with a unitary gate on a set of qubits.
462impl OperateGate for SqrtISwap {
463    /// Returns unitary matrix of the gate.
464    ///
465    /// # Returns
466    ///
467    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
468    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
469    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
470        let f: f64 = 1.0 / ((2.0_f64).sqrt());
471        Ok(array![
472            [
473                Complex64::new(1.0, 0.0),
474                Complex64::new(0.0, 0.0),
475                Complex64::new(0.0, 0.0),
476                Complex64::new(0.0, 0.0)
477            ],
478            [
479                Complex64::new(0.0, 0.0),
480                Complex64::new(f, 0.0),
481                Complex64::new(0.0, f),
482                Complex64::new(0.0, 0.0)
483            ],
484            [
485                Complex64::new(0.0, 0.0),
486                Complex64::new(0.0, f),
487                Complex64::new(f, 0.0),
488                Complex64::new(0.0, 0.0)
489            ],
490            [
491                Complex64::new(0.0, 0.0),
492                Complex64::new(0.0, 0.0),
493                Complex64::new(0.0, 0.0),
494                Complex64::new(1.0, 0.0)
495            ],
496        ])
497    }
498}
499
500/// Trait for all gate operations acting on exactly two qubits.
501impl OperateTwoQubitGate for SqrtISwap {
502    /// Returns [KakDecomposition] of the gate.
503    ///
504    /// # Returns
505    ///
506    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
507    fn kak_decomposition(&self) -> KakDecomposition {
508        KakDecomposition {
509            global_phase: CalculatorFloat::ZERO,
510            k_vector: [
511                CalculatorFloat::from(PI / 8.0),
512                CalculatorFloat::from(PI / 8.0),
513                CalculatorFloat::ZERO,
514            ],
515            circuit_before: None,
516            circuit_after: None,
517        }
518    }
519}
520
521/// The inverse square root ISwap gate.
522///
523/// InvSqrtISwap * SqrtISwap = Identity
524#[allow(clippy::upper_case_acronyms)]
525#[derive(
526    Debug,
527    Clone,
528    PartialEq,
529    Eq,
530    roqoqo_derive::InvolveQubits,
531    roqoqo_derive::SupportedVersion,
532    roqoqo_derive::Operate,
533    roqoqo_derive::Substitute,
534    roqoqo_derive::OperateTwoQubit,
535)]
536#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
537#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
538pub struct InvSqrtISwap {
539    /// The index of the most significant qubit in the unitary representation.
540    control: usize,
541    /// The index of the least significant qubit in the unitary representation.
542    target: usize,
543}
544
545#[allow(non_upper_case_globals)]
546const TAGS_InvSqrtISwap: &[&str; 4] = &[
547    "Operation",
548    "GateOperation",
549    "TwoQubitGateOperation",
550    "InvSqrtISwap",
551];
552
553/// Trait for all Operations acting with a unitary gate on a set of qubits.
554impl OperateGate for InvSqrtISwap {
555    /// Returns unitary matrix of the gate.
556    ///
557    /// # Returns
558    ///
559    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
560    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
561    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
562        let f: f64 = 1.0 / ((2.0_f64).sqrt());
563        Ok(array![
564            [
565                Complex64::new(1.0, 0.0),
566                Complex64::new(0.0, 0.0),
567                Complex64::new(0.0, 0.0),
568                Complex64::new(0.0, 0.0)
569            ],
570            [
571                Complex64::new(0.0, 0.0),
572                Complex64::new(f, 0.0),
573                Complex64::new(0.0, -f),
574                Complex64::new(0.0, 0.0)
575            ],
576            [
577                Complex64::new(0.0, 0.0),
578                Complex64::new(0.0, -f),
579                Complex64::new(f, 0.0),
580                Complex64::new(0.0, 0.0)
581            ],
582            [
583                Complex64::new(0.0, 0.0),
584                Complex64::new(0.0, 0.0),
585                Complex64::new(0.0, 0.0),
586                Complex64::new(1.0, 0.0)
587            ],
588        ])
589    }
590}
591
592/// Trait for all gate operations acting on exactly two qubits.
593impl OperateTwoQubitGate for InvSqrtISwap {
594    /// Returns [KakDecomposition] of the gate.
595    ///
596    /// # Returns
597    ///
598    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
599    fn kak_decomposition(&self) -> KakDecomposition {
600        KakDecomposition {
601            global_phase: CalculatorFloat::ZERO,
602            k_vector: [
603                CalculatorFloat::from(-PI / 8.0),
604                CalculatorFloat::from(-PI / 8.0),
605                CalculatorFloat::ZERO,
606            ],
607            circuit_before: None,
608            circuit_after: None,
609        }
610    }
611}
612
613/// The XY gate.
614///
615/// The XY gate applies a unitary rotation to the two qubit gates `control` and `target`.
616///
617/// XY = exp(i * (X_target * X_control + Y_target * Y_control) * theta / 2)
618#[allow(clippy::upper_case_acronyms)]
619#[derive(
620    Debug,
621    Clone,
622    PartialEq,
623    roqoqo_derive::InvolveQubits,
624    roqoqo_derive::SupportedVersion,
625    roqoqo_derive::Operate,
626    roqoqo_derive::Substitute,
627    roqoqo_derive::OperateTwoQubit,
628    roqoqo_derive::Rotate,
629)]
630#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
631#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
632pub struct XY {
633    /// The index of the most significant qubit in the unitary representation.
634    control: usize,
635    /// The index of the least significant qubit in the unitary representation.
636    target: usize,
637    /// The rotation angle θ.
638    theta: CalculatorFloat,
639}
640
641#[allow(non_upper_case_globals)]
642const TAGS_XY: &[&str; 5] = &[
643    "Operation",
644    "GateOperation",
645    "TwoQubitGateOperation",
646    "Rotation",
647    "XY",
648];
649
650/// Trait for all Operations acting with a unitary gate on a set of qubits.
651impl OperateGate for XY {
652    /// Returns unitary matrix of the gate.
653    ///
654    /// # Returns
655    ///
656    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
657    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
658    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
659        let c: f64 = (f64::try_from(self.theta.clone())? / 2.0).cos();
660        let s: f64 = (f64::try_from(self.theta.clone())? / 2.0).sin();
661        Ok(array![
662            [
663                Complex64::new(1.0, 0.0),
664                Complex64::new(0.0, 0.0),
665                Complex64::new(0.0, 0.0),
666                Complex64::new(0.0, 0.0)
667            ],
668            [
669                Complex64::new(0.0, 0.0),
670                Complex64::new(c, 0.0),
671                Complex64::new(0.0, s),
672                Complex64::new(0.0, 0.0)
673            ],
674            [
675                Complex64::new(0.0, 0.0),
676                Complex64::new(0.0, s),
677                Complex64::new(c, 0.0),
678                Complex64::new(0.0, 0.0)
679            ],
680            [
681                Complex64::new(0.0, 0.0),
682                Complex64::new(0.0, 0.0),
683                Complex64::new(0.0, 0.0),
684                Complex64::new(1.0, 0.0)
685            ],
686        ])
687    }
688}
689
690/// Trait for all gate operations acting on exactly two qubits.
691impl OperateTwoQubitGate for XY {
692    /// Returns [KakDecomposition] of the gate.
693    ///
694    /// # Returns
695    ///
696    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
697    fn kak_decomposition(&self) -> KakDecomposition {
698        KakDecomposition {
699            global_phase: CalculatorFloat::ZERO,
700            k_vector: [
701                self.theta.clone() / 4.0,
702                self.theta.clone() / 4.0,
703                CalculatorFloat::ZERO,
704            ],
705            circuit_before: None,
706            circuit_after: None,
707        }
708    }
709}
710
711/// Implements the controlled PhaseShift gate.
712///
713/// The controlled PhaseShift applies a phase shift to the `target` qubit
714/// depending on the state of the `control` qubit.
715///
716#[allow(clippy::upper_case_acronyms)]
717#[derive(
718    Debug,
719    Clone,
720    PartialEq,
721    roqoqo_derive::InvolveQubits,
722    roqoqo_derive::SupportedVersion,
723    roqoqo_derive::Operate,
724    roqoqo_derive::Substitute,
725    roqoqo_derive::OperateTwoQubit,
726    roqoqo_derive::Rotate,
727)]
728#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
729#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
730pub struct ControlledPhaseShift {
731    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of the phase-shift on the target qubit.
732    control: usize,
733    /// The index of the least significant qubit in the unitary representation. Here, the qubit phase-shift is applied to.
734    target: usize,
735    /// The rotation angle θ.
736    theta: CalculatorFloat,
737}
738
739#[allow(non_upper_case_globals)]
740const TAGS_ControlledPhaseShift: &[&str; 5] = &[
741    "Operation",
742    "GateOperation",
743    "TwoQubitGateOperation",
744    "Rotation",
745    "ControlledPhaseShift",
746];
747
748/// Trait for all Operations acting with a unitary gate on a set of qubits.
749impl OperateGate for ControlledPhaseShift {
750    /// Returns unitary matrix of the gate.
751    ///
752    /// # Returns
753    ///
754    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
755    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
756    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
757        // exp(i*x) = cos(x)+i*sin(x)
758        let c: f64 = (f64::try_from(self.theta.clone())?).cos();
759        let s: f64 = (f64::try_from(self.theta.clone())?).sin();
760        Ok(array![
761            [
762                Complex64::new(1.0, 0.0),
763                Complex64::new(0.0, 0.0),
764                Complex64::new(0.0, 0.0),
765                Complex64::new(0.0, 0.0)
766            ],
767            [
768                Complex64::new(0.0, 0.0),
769                Complex64::new(1.0, 0.0),
770                Complex64::new(0.0, 0.0),
771                Complex64::new(0.0, 0.0)
772            ],
773            [
774                Complex64::new(0.0, 0.0),
775                Complex64::new(0.0, 0.0),
776                Complex64::new(1.0, 0.0),
777                Complex64::new(0.0, 0.0)
778            ],
779            [
780                Complex64::new(0.0, 0.0),
781                Complex64::new(0.0, 0.0),
782                Complex64::new(0.0, 0.0),
783                Complex64::new(c, s)
784            ],
785        ])
786    }
787}
788
789/// Trait for all gate operations acting on exactly two qubits.
790impl OperateTwoQubitGate for ControlledPhaseShift {
791    /// Returns [KakDecomposition] of the gate.
792    ///
793    /// # Returns
794    ///
795    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
796    fn kak_decomposition(&self) -> KakDecomposition {
797        let mut circuit_b = Circuit::new();
798        circuit_b += RotateZ::new(self.control, self.theta.clone() / 2.0);
799        circuit_b += RotateZ::new(self.target, self.theta.clone() / 2.0);
800
801        KakDecomposition {
802            global_phase: self.theta.clone() / 4.0,
803            k_vector: [
804                CalculatorFloat::ZERO,
805                CalculatorFloat::ZERO,
806                self.theta.clone() / 4.0,
807            ],
808            circuit_before: Some(circuit_b),
809            circuit_after: None,
810        }
811    }
812}
813
814/// The controlled-PauliY gate.
815///
816/// Applies a PauliY unitary to the `target` qubit depending on the state of the `control`
817#[allow(clippy::upper_case_acronyms)]
818#[derive(
819    Debug,
820    Clone,
821    PartialEq,
822    Eq,
823    roqoqo_derive::InvolveQubits,
824    roqoqo_derive::SupportedVersion,
825    roqoqo_derive::Operate,
826    roqoqo_derive::Substitute,
827    roqoqo_derive::OperateTwoQubit,
828)]
829#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
830#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
831pub struct ControlledPauliY {
832    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of PauliY gate on the target qubit.
833    control: usize,
834    /// The index of the least significant qubit in the unitary representation. Here, the qubit PauliY is applied to.
835    target: usize,
836}
837
838#[allow(non_upper_case_globals)]
839const TAGS_ControlledPauliY: &[&str; 4] = &[
840    "Operation",
841    "GateOperation",
842    "TwoQubitGateOperation",
843    "ControlledPauliY",
844];
845
846/// Trait for all Operations acting with a unitary gate on a set of qubits.
847impl OperateGate for ControlledPauliY {
848    /// Returns unitary matrix of the gate.
849    ///
850    /// # Returns
851    ///
852    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
853    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
854    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
855        Ok(array![
856            [
857                Complex64::new(1.0, 0.0),
858                Complex64::new(0.0, 0.0),
859                Complex64::new(0.0, 0.0),
860                Complex64::new(0.0, 0.0)
861            ],
862            [
863                Complex64::new(0.0, 0.0),
864                Complex64::new(1.0, 0.0),
865                Complex64::new(0.0, 0.0),
866                Complex64::new(0.0, 0.0)
867            ],
868            [
869                Complex64::new(0.0, 0.0),
870                Complex64::new(0.0, 0.0),
871                Complex64::new(0.0, 0.0),
872                Complex64::new(0.0, -1.0)
873            ],
874            [
875                Complex64::new(0.0, 0.0),
876                Complex64::new(0.0, 0.0),
877                Complex64::new(0.0, 1.0),
878                Complex64::new(0.0, 0.0)
879            ],
880        ])
881    }
882}
883
884/// Trait for all gate operations acting on exactly two qubits.
885impl OperateTwoQubitGate for ControlledPauliY {
886    /// Returns [KakDecomposition] of the gate.
887    ///
888    /// # Returns
889    ///
890    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
891    fn kak_decomposition(&self) -> KakDecomposition {
892        let mut circuit_b = Circuit::new();
893        circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
894        circuit_b += RotateY::new(self.target, CalculatorFloat::FRAC_PI_2);
895        circuit_b += RotateX::new(self.target, CalculatorFloat::FRAC_PI_2);
896
897        let mut circuit_a = Circuit::new();
898        circuit_a += RotateX::new(self.target, CalculatorFloat::FRAC_PI_2 * (-1.0));
899
900        KakDecomposition {
901            global_phase: CalculatorFloat::FRAC_PI_4,
902            k_vector: [
903                CalculatorFloat::ZERO,
904                CalculatorFloat::ZERO,
905                CalculatorFloat::FRAC_PI_4,
906            ],
907            circuit_before: Some(circuit_b),
908            circuit_after: Some(circuit_a),
909        }
910    }
911}
912
913/// The controlled-PauliZ gate.
914///
915/// Applies a PauliZ unitary to the `target` qubit depending on the state of the `control` qubit.
916#[allow(clippy::upper_case_acronyms)]
917#[derive(
918    Debug,
919    Clone,
920    PartialEq,
921    Eq,
922    roqoqo_derive::InvolveQubits,
923    roqoqo_derive::SupportedVersion,
924    roqoqo_derive::Operate,
925    roqoqo_derive::Substitute,
926    roqoqo_derive::OperateTwoQubit,
927)]
928#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
929#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
930pub struct ControlledPauliZ {
931    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of PauliZ gate on the target qubit.
932    control: usize,
933    /// The index of the least significant qubit in the unitary representation. Here, the qubit PauliZ is applied to.
934    target: usize,
935}
936
937#[allow(non_upper_case_globals)]
938const TAGS_ControlledPauliZ: &[&str; 4] = &[
939    "Operation",
940    "GateOperation",
941    "TwoQubitGateOperation",
942    "ControlledPauliZ",
943];
944
945/// Trait for all Operations acting with a unitary gate on a set of qubits.
946impl OperateGate for ControlledPauliZ {
947    /// Returns unitary matrix of the gate.
948    ///
949    /// # Returns
950    ///
951    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
952    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
953    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
954        Ok(array![
955            [
956                Complex64::new(1.0, 0.0),
957                Complex64::new(0.0, 0.0),
958                Complex64::new(0.0, 0.0),
959                Complex64::new(0.0, 0.0)
960            ],
961            [
962                Complex64::new(0.0, 0.0),
963                Complex64::new(1.0, 0.0),
964                Complex64::new(0.0, 0.0),
965                Complex64::new(0.0, 0.0)
966            ],
967            [
968                Complex64::new(0.0, 0.0),
969                Complex64::new(0.0, 0.0),
970                Complex64::new(1.0, 0.0),
971                Complex64::new(0.0, 0.0)
972            ],
973            [
974                Complex64::new(0.0, 0.0),
975                Complex64::new(0.0, 0.0),
976                Complex64::new(0.0, 0.0),
977                Complex64::new(-1.0, 0.0)
978            ],
979        ])
980    }
981}
982
983/// Trait for all gate operations acting on exactly two qubits.
984impl OperateTwoQubitGate for ControlledPauliZ {
985    /// Returns [KakDecomposition] of the gate.
986    ///
987    /// # Returns
988    ///
989    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
990    fn kak_decomposition(&self) -> KakDecomposition {
991        let mut circuit_b = Circuit::new();
992        circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
993        circuit_b += RotateZ::new(self.target, CalculatorFloat::FRAC_PI_2);
994
995        KakDecomposition {
996            global_phase: CalculatorFloat::FRAC_PI_4,
997            k_vector: [
998                CalculatorFloat::ZERO,
999                CalculatorFloat::ZERO,
1000                CalculatorFloat::FRAC_PI_4,
1001            ],
1002            circuit_before: Some(circuit_b),
1003            circuit_after: None,
1004        }
1005    }
1006}
1007
1008/// The fixed phase MolmerSorensen XX gate.
1009///
1010/// Applies the unitary exp(-1 X_control X_target * pi/4) to two qubits `control` and `target`
1011#[allow(clippy::upper_case_acronyms)]
1012#[derive(
1013    Debug,
1014    Clone,
1015    PartialEq,
1016    Eq,
1017    roqoqo_derive::InvolveQubits,
1018    roqoqo_derive::SupportedVersion,
1019    roqoqo_derive::Operate,
1020    roqoqo_derive::Substitute,
1021    roqoqo_derive::OperateTwoQubit,
1022)]
1023#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1024#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1025pub struct MolmerSorensenXX {
1026    /// The index of the most significant qubit in the unitary representation. The gate is symmetric under the exchange of qubits.
1027    control: usize,
1028    /// The index of the least significant qubit in the unitary representation. The gate is symmetric under the exchange of qubits.
1029    target: usize,
1030}
1031
1032#[allow(non_upper_case_globals)]
1033const TAGS_MolmerSorensenXX: &[&str; 4] = &[
1034    "Operation",
1035    "GateOperation",
1036    "TwoQubitGateOperation",
1037    "MolmerSorensenXX",
1038];
1039
1040/// Trait for all Operations acting with a unitary gate on a set of qubits.
1041impl OperateGate for MolmerSorensenXX {
1042    /// Returns unitary matrix of the gate.
1043    ///
1044    /// # Returns
1045    ///
1046    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1047    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
1048    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1049        let f: f64 = 1.0 / ((2.0_f64).sqrt());
1050        Ok(array![
1051            [
1052                Complex64::new(f, 0.0),
1053                Complex64::new(0.0, 0.0),
1054                Complex64::new(0.0, 0.0),
1055                Complex64::new(0.0, -f)
1056            ],
1057            [
1058                Complex64::new(0.0, 0.0),
1059                Complex64::new(f, 0.0),
1060                Complex64::new(0.0, -f),
1061                Complex64::new(0.0, 0.0)
1062            ],
1063            [
1064                Complex64::new(0.0, 0.0),
1065                Complex64::new(0.0, -f),
1066                Complex64::new(f, 0.0),
1067                Complex64::new(0.0, 0.0)
1068            ],
1069            [
1070                Complex64::new(0.0, -f),
1071                Complex64::new(0.0, 0.0),
1072                Complex64::new(0.0, 0.0),
1073                Complex64::new(f, 0.0)
1074            ],
1075        ])
1076    }
1077}
1078
1079/// Trait for all gate operations acting on exactly two qubits.
1080impl OperateTwoQubitGate for MolmerSorensenXX {
1081    /// Returns [KakDecomposition] of the gate.
1082    ///
1083    /// # Returns
1084    ///
1085    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1086    fn kak_decomposition(&self) -> KakDecomposition {
1087        KakDecomposition {
1088            global_phase: CalculatorFloat::ZERO,
1089            k_vector: [
1090                CalculatorFloat::from(-PI / 4.0),
1091                CalculatorFloat::ZERO,
1092                CalculatorFloat::ZERO,
1093            ],
1094            circuit_before: None,
1095            circuit_after: None,
1096        }
1097    }
1098}
1099
1100/// The variable-angle MolmerSorensen XX gate.
1101///
1102/// Applies the unitary exp(-1 X_control X_target * theta/2) to two qubits `control` and `target`
1103#[allow(clippy::upper_case_acronyms)]
1104#[derive(
1105    Debug,
1106    Clone,
1107    PartialEq,
1108    roqoqo_derive::InvolveQubits,
1109    roqoqo_derive::SupportedVersion,
1110    roqoqo_derive::Operate,
1111    roqoqo_derive::Substitute,
1112    roqoqo_derive::OperateTwoQubit,
1113    roqoqo_derive::Rotate,
1114)]
1115#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1116#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1117pub struct VariableMSXX {
1118    /// The index of the most significant qubit in the unitary representation. The gate is symmetric under the exchange of qubits.
1119    control: usize,
1120    /// The index of the least significant qubit in the unitary representation. The gate is symmetric under the exchange of qubits.
1121    target: usize,
1122    /// The rotation angle θ.
1123    theta: CalculatorFloat,
1124}
1125
1126#[allow(non_upper_case_globals)]
1127const TAGS_VariableMSXX: &[&str; 5] = &[
1128    "Operation",
1129    "GateOperation",
1130    "TwoQubitGateOperation",
1131    "Rotation",
1132    "VariableMSXX",
1133];
1134
1135/// Trait for all Operations acting with a unitary gate on a set of qubits.
1136impl OperateGate for VariableMSXX {
1137    /// Returns unitary matrix of the gate.
1138    ///
1139    /// # Returns
1140    ///
1141    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1142    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1143    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1144        let c: f64 = (f64::try_from(self.theta.clone())? / 2.0).cos();
1145        let s: f64 = (f64::try_from(self.theta.clone())? / 2.0).sin();
1146        Ok(array![
1147            [
1148                Complex64::new(c, 0.0),
1149                Complex64::new(0.0, 0.0),
1150                Complex64::new(0.0, 0.0),
1151                Complex64::new(0.0, -s)
1152            ],
1153            [
1154                Complex64::new(0.0, 0.0),
1155                Complex64::new(c, 0.0),
1156                Complex64::new(0.0, -s),
1157                Complex64::new(0.0, 0.0)
1158            ],
1159            [
1160                Complex64::new(0.0, 0.0),
1161                Complex64::new(0.0, -s),
1162                Complex64::new(c, 0.0),
1163                Complex64::new(0.0, 0.0)
1164            ],
1165            [
1166                Complex64::new(0.0, -s),
1167                Complex64::new(0.0, 0.0),
1168                Complex64::new(0.0, 0.0),
1169                Complex64::new(c, 0.0)
1170            ],
1171        ])
1172    }
1173}
1174
1175/// Trait for all gate operations acting on exactly two qubits.
1176impl OperateTwoQubitGate for VariableMSXX {
1177    /// Returns [KakDecomposition] of the gate.
1178    ///
1179    /// # Returns
1180    ///
1181    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1182    fn kak_decomposition(&self) -> KakDecomposition {
1183        KakDecomposition {
1184            global_phase: CalculatorFloat::ZERO,
1185            k_vector: [
1186                self.theta.clone() * (-1.0 / 2.0),
1187                CalculatorFloat::ZERO,
1188                CalculatorFloat::ZERO,
1189            ],
1190            circuit_before: None,
1191            circuit_after: None,
1192        }
1193    }
1194}
1195
1196/// The Givens rotation interaction gate in big endian notation: exp(-i * θ * [X_c Y_t - Y_c X_t]) * exp(-i * φ * Z_t/2).
1197///
1198/// Where X_c is the Pauli matrix σ^x acting on the control qubit, Y_t is the Pauli matrix σ^y acting on the target qubit,
1199/// and Z_t is the Pauli matrix σ^z acting on the target qubit.
1200///
1201#[allow(clippy::upper_case_acronyms)]
1202#[derive(
1203    Debug,
1204    Clone,
1205    PartialEq,
1206    roqoqo_derive::InvolveQubits,
1207    roqoqo_derive::SupportedVersion,
1208    roqoqo_derive::Operate,
1209    roqoqo_derive::Substitute,
1210    roqoqo_derive::OperateTwoQubit,
1211    roqoqo_derive::Rotate,
1212)]
1213#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1214#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1215pub struct GivensRotation {
1216    /// The index of the most significant qubit in the unitary representation.
1217    control: usize,
1218    /// The index of the least significant qubit in the unitary representation.
1219    target: usize,
1220    /// The rotation angle θ.
1221    theta: CalculatorFloat,
1222    /// The phase φ of the rotation.
1223    phi: CalculatorFloat,
1224}
1225
1226#[allow(non_upper_case_globals)]
1227const TAGS_GivensRotation: &[&str; 5] = &[
1228    "Operation",
1229    "GateOperation",
1230    "TwoQubitGateOperation",
1231    "Rotation",
1232    "GivensRotation",
1233];
1234
1235/// Trait for all Operations acting with a unitary gate on a set of qubits.
1236impl OperateGate for GivensRotation {
1237    /// Returns unitary matrix of the gate.
1238    ///
1239    /// # Returns
1240    ///
1241    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1242    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1243    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1244        let ct: f64 = (f64::try_from(self.theta.clone())?).cos();
1245        let st: f64 = (f64::try_from(self.theta.clone())?).sin();
1246        // exp(i*phi) = cos(phi)+i*sin(phi)
1247        let cp: f64 = (f64::try_from(self.phi.clone())?).cos();
1248        let sp: f64 = (f64::try_from(self.phi.clone())?).sin();
1249        Ok(array![
1250            [
1251                Complex64::new(1.0, 0.0),
1252                Complex64::new(0.0, 0.0),
1253                Complex64::new(0.0, 0.0),
1254                Complex64::new(0.0, 0.0)
1255            ],
1256            [
1257                Complex64::new(0.0, 0.0),
1258                Complex64::new(ct * cp, ct * sp),
1259                Complex64::new(st, 0.0),
1260                Complex64::new(0.0, 0.0)
1261            ],
1262            [
1263                Complex64::new(0.0, 0.0),
1264                Complex64::new(-st * cp, -st * sp),
1265                Complex64::new(ct, 0.0),
1266                Complex64::new(0.0, 0.0)
1267            ],
1268            [
1269                Complex64::new(0.0, 0.0),
1270                Complex64::new(0.0, 0.0),
1271                Complex64::new(0.0, 0.0),
1272                Complex64::new(cp, sp)
1273            ],
1274        ])
1275    }
1276}
1277
1278/// Trait for all gate operations acting on exactly two qubits.
1279impl OperateTwoQubitGate for GivensRotation {
1280    /// Returns [KakDecomposition] of the gate.
1281    ///
1282    /// # Returns
1283    ///
1284    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1285    fn kak_decomposition(&self) -> KakDecomposition {
1286        let mut circuit_b = Circuit::new();
1287        circuit_b += RotateZ::new(self.target, self.phi.clone() + (PI / 2.0));
1288
1289        let mut circuit_a = Circuit::new();
1290        circuit_a += RotateZ::new(self.target, CalculatorFloat::FRAC_PI_2 * (-1.0));
1291
1292        KakDecomposition {
1293            global_phase: self.phi.clone() / 2.0,
1294            k_vector: [
1295                self.theta.clone() / 2.0,
1296                self.theta.clone() / 2.0,
1297                CalculatorFloat::ZERO,
1298            ],
1299            circuit_before: Some(circuit_b),
1300            circuit_after: Some(circuit_a),
1301        }
1302    }
1303}
1304
1305/// The Givens rotation interaction gate in little endian notation: exp(-i * θ * [X_c Y_t -Y_c  X_t]) * exp(-i * φ * Z_c/2).
1306///
1307/// Where X_c is the Pauli matrix σ^x acting on the control qubit, Y_t is the Pauli matrix σ^y acting on the target qubit,
1308/// and Z_c is the Pauli matrix σ^z acting on the control qubit.
1309///
1310#[allow(clippy::upper_case_acronyms)]
1311#[derive(
1312    Debug,
1313    Clone,
1314    PartialEq,
1315    roqoqo_derive::InvolveQubits,
1316    roqoqo_derive::SupportedVersion,
1317    roqoqo_derive::Operate,
1318    roqoqo_derive::Substitute,
1319    roqoqo_derive::OperateTwoQubit,
1320    roqoqo_derive::Rotate,
1321)]
1322#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1323#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1324pub struct GivensRotationLittleEndian {
1325    /// The index of the most significant qubit in the unitary representation.
1326    control: usize,
1327    /// The index of the least significant qubit in the unitary representation.
1328    target: usize,
1329    /// The rotation angle θ.
1330    theta: CalculatorFloat,
1331    /// The phase φ of the rotation.
1332    phi: CalculatorFloat,
1333}
1334
1335#[allow(non_upper_case_globals)]
1336const TAGS_GivensRotationLittleEndian: &[&str; 5] = &[
1337    "Operation",
1338    "GateOperation",
1339    "TwoQubitGateOperation",
1340    "Rotation",
1341    "GivensRotationLittleEndian",
1342];
1343
1344/// Trait for all Operations acting with a unitary gate on a set of qubits.
1345impl OperateGate for GivensRotationLittleEndian {
1346    /// Returns unitary matrix of the gate.
1347    ///
1348    /// # Returns
1349    ///
1350    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1351    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1352    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1353        let ct: f64 = (f64::try_from(self.theta.clone())?).cos();
1354        let st: f64 = (f64::try_from(self.theta.clone())?).sin();
1355        // exp(i*phi) = cos(phi)+i*sin(phi)
1356        let cp: f64 = (f64::try_from(self.phi.clone())?).cos();
1357        let sp: f64 = (f64::try_from(self.phi.clone())?).sin();
1358        Ok(array![
1359            [
1360                Complex64::new(1.0, 0.0),
1361                Complex64::new(0.0, 0.0),
1362                Complex64::new(0.0, 0.0),
1363                Complex64::new(0.0, 0.0)
1364            ],
1365            [
1366                Complex64::new(0.0, 0.0),
1367                Complex64::new(ct, 0.0),
1368                Complex64::new(st, 0.0),
1369                Complex64::new(0.0, 0.0)
1370            ],
1371            [
1372                Complex64::new(0.0, 0.0),
1373                Complex64::new(-st * cp, -st * sp),
1374                Complex64::new(ct * cp, ct * sp),
1375                Complex64::new(0.0, 0.0)
1376            ],
1377            [
1378                Complex64::new(0.0, 0.0),
1379                Complex64::new(0.0, 0.0),
1380                Complex64::new(0.0, 0.0),
1381                Complex64::new(cp, sp)
1382            ],
1383        ])
1384    }
1385}
1386
1387/// Trait for all gate operations acting on exactly two qubits.
1388impl OperateTwoQubitGate for GivensRotationLittleEndian {
1389    /// Returns [KakDecomposition] of the gate.
1390    ///
1391    /// # Returns
1392    ///
1393    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1394    fn kak_decomposition(&self) -> KakDecomposition {
1395        let mut circuit_b = Circuit::new();
1396        circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2 * (-1.0));
1397
1398        let mut circuit_a = Circuit::new();
1399        circuit_a += RotateZ::new(self.control, self.phi.clone() + (PI / 2.0));
1400
1401        KakDecomposition {
1402            global_phase: self.phi.clone() / 2.0,
1403            k_vector: [
1404                self.theta.clone() / 2.0,
1405                self.theta.clone() / 2.0,
1406                CalculatorFloat::ZERO,
1407            ],
1408            circuit_before: Some(circuit_b),
1409            circuit_after: Some(circuit_a),
1410        }
1411    }
1412}
1413
1414/// The qubit simulation gate.
1415///
1416/// Swaps the state of two qubits `control` and `target` and
1417/// at the same time applies the unitary
1418///
1419/// exp(-i (x * X_c X_t + y * Y_c Y_t + z * Z_c Z_t))
1420#[allow(clippy::upper_case_acronyms)]
1421#[derive(
1422    Debug,
1423    Clone,
1424    PartialEq,
1425    roqoqo_derive::InvolveQubits,
1426    roqoqo_derive::SupportedVersion,
1427    roqoqo_derive::Operate,
1428    roqoqo_derive::Substitute,
1429    roqoqo_derive::OperateTwoQubit,
1430)]
1431#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1432#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1433pub struct Qsim {
1434    /// The index of the most significant qubit in the unitary representation.
1435    control: usize,
1436    /// The index of the least significant qubit in the unitary representation.
1437    target: usize,
1438    /// The prefactor of the XX interaction.
1439    x: CalculatorFloat,
1440    /// The prefactor of the YY interaction.
1441    y: CalculatorFloat,
1442    /// The prefactor of the ZZ interaction.
1443    z: CalculatorFloat,
1444}
1445
1446#[allow(non_upper_case_globals)]
1447const TAGS_Qsim: &[&str; 4] = &[
1448    "Operation",
1449    "GateOperation",
1450    "TwoQubitGateOperation",
1451    "Qsim",
1452];
1453
1454/// Trait for all Operations acting with a unitary gate on a set of qubits.
1455impl OperateGate for Qsim {
1456    /// Returns unitary matrix of the gate.
1457    ///
1458    /// # Returns
1459    ///
1460    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1461    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1462    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1463        let x: f64 = f64::try_from(self.x.clone())?;
1464        let y: f64 = f64::try_from(self.y.clone())?;
1465        let z: f64 = f64::try_from(self.z.clone())?;
1466
1467        let cm: f64 = (x - y).cos();
1468        let cp: f64 = (x + y).cos();
1469        let sm: f64 = (x - y).sin();
1470        let sp: f64 = (x + y).sin();
1471
1472        // exp(i*z) = cos(z) + i*sin(z)
1473        // exp(-i*z) = cos(z) - i*sin(z)
1474        let cz: f64 = z.cos();
1475        let sz: f64 = z.sin();
1476
1477        Ok(array![
1478            [
1479                Complex64::new(cm * cz, -cm * sz),
1480                Complex64::new(0.0, 0.0),
1481                Complex64::new(0.0, 0.0),
1482                Complex64::new(-sm * sz, -sm * cz)
1483            ],
1484            [
1485                Complex64::new(0.0, 0.0),
1486                Complex64::new(sp * sz, -sp * cz),
1487                Complex64::new(cp * cz, cp * sz),
1488                Complex64::new(0.0, 0.0)
1489            ],
1490            [
1491                Complex64::new(0.0, 0.0),
1492                Complex64::new(cp * cz, cp * sz),
1493                Complex64::new(sp * sz, -sp * cz),
1494                Complex64::new(0.0, 0.0)
1495            ],
1496            [
1497                Complex64::new(-sm * sz, -sm * cz),
1498                Complex64::new(0.0, 0.0),
1499                Complex64::new(0.0, 0.0),
1500                Complex64::new(cm * cz, -cm * sz)
1501            ],
1502        ])
1503    }
1504}
1505
1506/// Trait for all gate operations acting on exactly two qubits.
1507impl OperateTwoQubitGate for Qsim {
1508    /// Returns [KakDecomposition] of the gate.
1509    ///
1510    /// # Returns
1511    ///
1512    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1513    fn kak_decomposition(&self) -> KakDecomposition {
1514        KakDecomposition {
1515            global_phase: CalculatorFloat::from(-PI / 4.0),
1516            k_vector: [
1517                self.x.clone() * (-1.0) + PI / 4.0,
1518                self.y.clone() * (-1.0) + PI / 4.0,
1519                self.z.clone() * (-1.0) + PI / 4.0,
1520            ],
1521            circuit_before: None,
1522            circuit_after: None,
1523        }
1524    }
1525}
1526
1527/// The fermionic qubit simulation gate.
1528///
1529/// Applies a Fermionic SWAP between two qubits `target` and `control`
1530/// and applies the unitary evolution with a hopping t, a density-density interaction u and
1531/// a Bogoliubov interaction delta.
1532///
1533/// # Note
1534/// The qubits have to be adjacent, i.e., :math:`|i-j|=1` has to hold. This is the only case
1535/// in which the gate is valid as a two-qubit gate (due to the Jordan-Wigner transformation).
1536///
1537#[allow(clippy::upper_case_acronyms)]
1538#[derive(
1539    Debug,
1540    Clone,
1541    PartialEq,
1542    roqoqo_derive::InvolveQubits,
1543    roqoqo_derive::SupportedVersion,
1544    roqoqo_derive::Operate,
1545    roqoqo_derive::Substitute,
1546    roqoqo_derive::OperateTwoQubit,
1547)]
1548#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1549#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1550pub struct Fsim {
1551    /// The index of the most significant qubit in the unitary representation.
1552    control: usize,
1553    /// The index of the least significant qubit in the unitary representation.
1554    target: usize,
1555    /// The hopping strength.
1556    t: CalculatorFloat,
1557    /// The interaction strength.
1558    u: CalculatorFloat,
1559    /// The Bogoliubov interaction strength Δ.
1560    delta: CalculatorFloat,
1561}
1562
1563#[allow(non_upper_case_globals)]
1564const TAGS_Fsim: &[&str; 4] = &[
1565    "Operation",
1566    "GateOperation",
1567    "TwoQubitGateOperation",
1568    "Fsim",
1569];
1570
1571/// Trait for all Operations acting with a unitary gate on a set of qubits.
1572impl OperateGate for Fsim {
1573    /// Returns unitary matrix of the gate.
1574    ///
1575    /// # Returns
1576    ///
1577    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1578    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1579    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1580        let t: f64 = f64::try_from(self.t.clone())?;
1581        let u: f64 = f64::try_from(self.u.clone())?;
1582        let d: f64 = f64::try_from(self.delta.clone())?;
1583
1584        Ok(array![
1585            [
1586                Complex64::new(d.cos(), 0.0),
1587                Complex64::new(0.0, 0.0),
1588                Complex64::new(0.0, 0.0),
1589                Complex64::new(0.0, d.sin())
1590            ],
1591            [
1592                Complex64::new(0.0, 0.0),
1593                Complex64::new(0.0, -t.sin()),
1594                Complex64::new(t.cos(), 0.0),
1595                Complex64::new(0.0, 0.0)
1596            ],
1597            [
1598                Complex64::new(0.0, 0.0),
1599                Complex64::new(t.cos(), 0.0),
1600                Complex64::new(0.0, -t.sin()),
1601                Complex64::new(0.0, 0.0)
1602            ],
1603            [
1604                Complex64::new(-d.sin() * u.sin(), -d.sin() * u.cos()),
1605                Complex64::new(0.0, 0.0),
1606                Complex64::new(0.0, 0.0),
1607                Complex64::new(-d.cos() * u.cos(), d.cos() * u.sin())
1608            ],
1609        ])
1610    }
1611}
1612
1613/// Trait for all gate operations acting on exactly two qubits.
1614impl OperateTwoQubitGate for Fsim {
1615    /// Returns [KakDecomposition] of the gate.
1616    ///
1617    /// # Returns
1618    ///
1619    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1620    fn kak_decomposition(&self) -> KakDecomposition {
1621        let theta = self.u.clone() / (-2.0) - PI / 2.0;
1622        let mut circuit_a = Circuit::new();
1623        circuit_a += RotateZ::new(self.control, theta.clone());
1624        circuit_a += RotateZ::new(self.target, theta);
1625
1626        KakDecomposition {
1627            global_phase: self.u.clone() / (-4.0) - PI / 2.0,
1628            k_vector: [
1629                (self.t.clone() / (-2.0) + self.delta.clone() / 2.0 + PI / 4.0),
1630                (self.t.clone() / (-2.0) - self.delta.clone() / 2.0 + PI / 4.0),
1631                self.u.clone() / (-4.0),
1632            ],
1633            circuit_before: None,
1634            circuit_after: Some(circuit_a),
1635        }
1636    }
1637}
1638
1639/// The generalized, anisotropic XYZ Heisenberg interaction between spins.
1640///
1641/// Applies a unitary to two qubits `control` and `target`  
1642/// exp(-i (x * X_t X_c + y * Y_t Y_c + z * Z_t Z_c))
1643#[allow(clippy::upper_case_acronyms)]
1644#[derive(
1645    Debug,
1646    Clone,
1647    PartialEq,
1648    roqoqo_derive::InvolveQubits,
1649    roqoqo_derive::SupportedVersion,
1650    roqoqo_derive::Operate,
1651    roqoqo_derive::Substitute,
1652    roqoqo_derive::OperateTwoQubit,
1653)]
1654#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1655#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1656pub struct SpinInteraction {
1657    /// The index of the most significant qubit in the unitary representation.
1658    control: usize,
1659    /// The index of the least significant qubit in the unitary representation.
1660    target: usize,
1661    /// The prefactor of the XX interaction.
1662    x: CalculatorFloat,
1663    /// The prefactor of the YY interaction.
1664    y: CalculatorFloat,
1665    /// The prefactor of the ZZ interaction.
1666    z: CalculatorFloat,
1667}
1668
1669#[allow(non_upper_case_globals)]
1670const TAGS_SpinInteraction: &[&str; 4] = &[
1671    "Operation",
1672    "GateOperation",
1673    "TwoQubitGateOperation",
1674    "SpinInteraction",
1675];
1676
1677/// Trait for all Operations acting with a unitary gate on a set of qubits.
1678impl OperateGate for SpinInteraction {
1679    /// Returns unitary matrix of the gate.
1680    ///
1681    /// # Returns
1682    ///
1683    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1684    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1685    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1686        let x: f64 = f64::try_from(self.x.clone())?;
1687        let y: f64 = f64::try_from(self.y.clone())?;
1688        let z: f64 = f64::try_from(self.z.clone())?;
1689
1690        let cm: f64 = (x - y).cos();
1691        let cp: f64 = (x + y).cos();
1692        let sm: f64 = (x - y).sin();
1693        let sp: f64 = (x + y).sin();
1694
1695        // exp(i*z) = cos(z) + i*sin(z)
1696        // exp(-i*z) = cos(z) - i*sin(z)
1697        let cz: f64 = z.cos();
1698        let sz: f64 = z.sin();
1699
1700        Ok(array![
1701            [
1702                Complex64::new(cm * cz, -cm * sz),
1703                Complex64::new(0.0, 0.0),
1704                Complex64::new(0.0, 0.0),
1705                Complex64::new(-sm * sz, -sm * cz)
1706            ],
1707            [
1708                Complex64::new(0.0, 0.0),
1709                Complex64::new(cp * cz, cp * sz),
1710                Complex64::new(sp * sz, -sp * cz),
1711                Complex64::new(0.0, 0.0)
1712            ],
1713            [
1714                Complex64::new(0.0, 0.0),
1715                Complex64::new(sp * sz, -sp * cz),
1716                Complex64::new(cp * cz, cp * sz),
1717                Complex64::new(0.0, 0.0)
1718            ],
1719            [
1720                Complex64::new(-sm * sz, -sm * cz),
1721                Complex64::new(0.0, 0.0),
1722                Complex64::new(0.0, 0.0),
1723                Complex64::new(cm * cz, -cm * sz)
1724            ],
1725        ])
1726    }
1727}
1728
1729/// Trait for all gate operations acting on exactly two qubits.
1730impl OperateTwoQubitGate for SpinInteraction {
1731    /// Returns [KakDecomposition] of the gate.
1732    ///
1733    /// # Returns
1734    ///
1735    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1736    fn kak_decomposition(&self) -> KakDecomposition {
1737        KakDecomposition {
1738            global_phase: CalculatorFloat::ZERO,
1739            k_vector: [
1740                self.x.clone() * (-1.0),
1741                self.y.clone() * (-1.0),
1742                self.z.clone() * (-1.0),
1743            ],
1744            circuit_before: None,
1745            circuit_after: None,
1746        }
1747    }
1748}
1749
1750/// The Bogoliubov DeGennes interaction gate.
1751///
1752/// exp(-i * Re(Δ) * [X_c X_t - Y_c Y_t]/2 + Im(Δ) * [X_c Y_t+Y_c X_t]/2)
1753///
1754/// Where X_c is the Pauli matrix σ^x acting on the control qubit, and Y_t is the Pauli matrix σ^y acting on the target qubit.
1755///
1756#[allow(clippy::upper_case_acronyms)]
1757#[derive(
1758    Debug,
1759    Clone,
1760    PartialEq,
1761    roqoqo_derive::InvolveQubits,
1762    roqoqo_derive::SupportedVersion,
1763    roqoqo_derive::Operate,
1764    roqoqo_derive::Substitute,
1765    roqoqo_derive::OperateTwoQubit,
1766)]
1767#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1768#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1769pub struct Bogoliubov {
1770    /// The index of the most significant qubit in the unitary representation.
1771    control: usize,
1772    /// The index of the least significant qubit in the unitary representation.
1773    target: usize,
1774    /// The real part of the complex Bogoliubov interaction strength Re(Δ).
1775    delta_real: CalculatorFloat,
1776    /// The imaginary part of the complex Bogoliubov interaction strength Im(Δ).
1777    delta_imag: CalculatorFloat,
1778}
1779
1780#[allow(non_upper_case_globals)]
1781const TAGS_Bogoliubov: &[&str; 4] = &[
1782    "Operation",
1783    "GateOperation",
1784    "TwoQubitGateOperation",
1785    "Bogoliubov",
1786];
1787
1788/// Trait for all Operations acting with a unitary gate on a set of qubits.
1789impl OperateGate for Bogoliubov {
1790    /// Returns unitary matrix of the gate.
1791    ///
1792    /// # Returns
1793    ///
1794    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1795    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1796    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1797        let dr: f64 = f64::try_from(self.delta_real.clone())?;
1798        let di: f64 = f64::try_from(self.delta_imag.clone())?;
1799        let delta: Complex64 = Complex64::new(dr, di);
1800        let da: f64 = delta.norm(); //absolute value of delta
1801        let dp: f64 = delta.arg(); // phase of delta
1802        Ok(array![
1803            [
1804                Complex64::new(da.cos(), 0.0),
1805                Complex64::new(0.0, 0.0),
1806                Complex64::new(0.0, 0.0),
1807                Complex64::new(-da.sin() * dp.sin(), da.sin() * dp.cos())
1808            ],
1809            [
1810                Complex64::new(0.0, 0.0),
1811                Complex64::new(1.0, 0.0),
1812                Complex64::new(0.0, 0.0),
1813                Complex64::new(0.0, 0.0)
1814            ],
1815            [
1816                Complex64::new(0.0, 0.0),
1817                Complex64::new(0.0, 0.0),
1818                Complex64::new(1.0, 0.0),
1819                Complex64::new(0.0, 0.0)
1820            ],
1821            [
1822                Complex64::new(da.sin() * dp.sin(), da.sin() * dp.cos()),
1823                Complex64::new(0.0, 0.0),
1824                Complex64::new(0.0, 0.0),
1825                Complex64::new(da.cos(), 0.0)
1826            ],
1827        ])
1828    }
1829}
1830
1831/// Trait for all gate operations acting on exactly two qubits.
1832impl OperateTwoQubitGate for Bogoliubov {
1833    /// Returns [KakDecomposition] of the gate.
1834    ///
1835    /// # Returns
1836    ///
1837    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1838    fn kak_decomposition(&self) -> KakDecomposition {
1839        let dr = self.delta_real.clone();
1840        let di = self.delta_imag.clone();
1841        let delta: CalculatorComplex = CalculatorComplex::new(dr, di);
1842
1843        let mut circuit_b = Circuit::new();
1844        circuit_b += RotateZ::new(self.target, delta.arg());
1845
1846        let mut circuit_a = Circuit::new();
1847        circuit_a += RotateZ::new(self.target, delta.arg() * (-1.0));
1848
1849        KakDecomposition {
1850            global_phase: CalculatorFloat::ZERO,
1851            k_vector: [
1852                delta.norm() / (2.0),
1853                delta.norm() / (-2.0),
1854                CalculatorFloat::ZERO,
1855            ],
1856            circuit_before: Some(circuit_b),
1857            circuit_after: Some(circuit_a),
1858        }
1859    }
1860}
1861
1862/// The transversal interaction gate.
1863///
1864/// exp(-i * θ *[X_c X_t + Y_c Y_t]) = exp( -i * θ * [σ^+_c * σ^-_t + σ^-_c σ^+_t])
1865///
1866/// Where X_c is the Pauli matrix σ^x acting on the control qubit, and Y_t is the Pauli matrix σ^y acting on the target qubit.
1867///
1868#[allow(clippy::upper_case_acronyms)]
1869#[derive(
1870    Debug,
1871    Clone,
1872    PartialEq,
1873    roqoqo_derive::InvolveQubits,
1874    roqoqo_derive::SupportedVersion,
1875    roqoqo_derive::Operate,
1876    roqoqo_derive::Substitute,
1877    roqoqo_derive::OperateTwoQubit,
1878)]
1879#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1880#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1881pub struct PMInteraction {
1882    /// The index of the most significant qubit in the unitary representation.
1883    control: usize,
1884    /// The index of the least significant qubit in the unitary representation.
1885    target: usize,
1886    /// The strength of the rotation θ.
1887    t: CalculatorFloat,
1888}
1889
1890#[allow(non_upper_case_globals)]
1891const TAGS_PMInteraction: &[&str; 4] = &[
1892    "Operation",
1893    "GateOperation",
1894    "TwoQubitGateOperation",
1895    "PMInteraction",
1896];
1897
1898/// Trait for all Operations acting with a unitary gate on a set of qubits.
1899impl OperateGate for PMInteraction {
1900    /// Returns unitary matrix of the gate.
1901    ///
1902    /// # Returns
1903    ///
1904    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1905    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1906    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1907        let c: f64 = (f64::try_from(self.t.clone())?).cos();
1908        let s: f64 = (f64::try_from(self.t.clone())?).sin();
1909        Ok(array![
1910            [
1911                Complex64::new(1.0, 0.0),
1912                Complex64::new(0.0, 0.0),
1913                Complex64::new(0.0, 0.0),
1914                Complex64::new(0.0, 0.0)
1915            ],
1916            [
1917                Complex64::new(0.0, 0.0),
1918                Complex64::new(c, 0.0),
1919                Complex64::new(0.0, -s),
1920                Complex64::new(0.0, 0.0)
1921            ],
1922            [
1923                Complex64::new(0.0, 0.0),
1924                Complex64::new(0.0, -s),
1925                Complex64::new(c, 0.0),
1926                Complex64::new(0.0, 0.0)
1927            ],
1928            [
1929                Complex64::new(0.0, 0.0),
1930                Complex64::new(0.0, 0.0),
1931                Complex64::new(0.0, 0.0),
1932                Complex64::new(1.0, 0.0)
1933            ],
1934        ])
1935    }
1936}
1937
1938/// Trait for all gate operations acting on exactly two qubits.
1939impl OperateTwoQubitGate for PMInteraction {
1940    /// Returns [KakDecomposition] of the gate.
1941    ///
1942    /// # Returns
1943    ///
1944    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1945    fn kak_decomposition(&self) -> KakDecomposition {
1946        KakDecomposition {
1947            global_phase: CalculatorFloat::ZERO,
1948            k_vector: [
1949                self.t.clone() / (-2.0),
1950                self.t.clone() / (-2.0),
1951                CalculatorFloat::ZERO,
1952            ],
1953            circuit_before: None,
1954            circuit_after: None,
1955        }
1956    }
1957}
1958
1959/// The complex hopping gate.
1960///
1961/// exp(-i * [ Re(θ) * (X_c X_t + Y_c Y_t) - Im(θ) * (X_c Y_t - Y_c X_t) ] )
1962///
1963/// Where X_c is the Pauli matrix σ^x acting on the control qubit, and Y_t is the Pauli matrix σ^y acting on the target qubit.
1964///
1965#[allow(clippy::upper_case_acronyms)]
1966#[derive(
1967    Debug,
1968    Clone,
1969    PartialEq,
1970    roqoqo_derive::InvolveQubits,
1971    roqoqo_derive::SupportedVersion,
1972    roqoqo_derive::Operate,
1973    roqoqo_derive::Substitute,
1974    roqoqo_derive::OperateTwoQubit,
1975)]
1976#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1977#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1978pub struct ComplexPMInteraction {
1979    /// The index of the most significant qubit in the unitary representation.
1980    control: usize,
1981    /// The index of the least significant qubit in the unitary representation.
1982    target: usize,
1983    /// The real part of the strength of the rotation Re(θ).
1984    t_real: CalculatorFloat,
1985    /// The imaginary part of the strength of the rotation Im(θ).
1986    t_imag: CalculatorFloat,
1987}
1988
1989#[allow(non_upper_case_globals)]
1990const TAGS_ComplexPMInteraction: &[&str; 4] = &[
1991    "Operation",
1992    "GateOperation",
1993    "TwoQubitGateOperation",
1994    "ComplexPMInteraction",
1995];
1996
1997/// Trait for all Operations acting with a unitary gate on a set of qubits.
1998impl OperateGate for ComplexPMInteraction {
1999    /// Returns unitary matrix of the gate.
2000    ///
2001    /// # Returns
2002    ///
2003    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
2004    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
2005    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
2006        let tr: f64 = f64::try_from(self.t_real.clone())?;
2007        let ti: f64 = f64::try_from(self.t_imag.clone())?;
2008        let t: Complex64 = Complex64::new(tr, ti);
2009        let tn: f64 = t.norm(); //absolute value of delta
2010        let ta: f64 = t.arg(); // phase of delta
2011        Ok(array![
2012            [
2013                Complex64::new(1.0, 0.0),
2014                Complex64::new(0.0, 0.0),
2015                Complex64::new(0.0, 0.0),
2016                Complex64::new(0.0, 0.0)
2017            ],
2018            [
2019                Complex64::new(0.0, 0.0),
2020                Complex64::new(tn.cos(), 0.0),
2021                Complex64::new(-tn.sin() * ta.sin(), -tn.sin() * ta.cos()),
2022                Complex64::new(0.0, 0.0)
2023            ],
2024            [
2025                Complex64::new(0.0, 0.0),
2026                Complex64::new(tn.sin() * ta.sin(), -tn.sin() * ta.cos()),
2027                Complex64::new(tn.cos(), 0.0),
2028                Complex64::new(0.0, 0.0)
2029            ],
2030            [
2031                Complex64::new(0.0, 0.0),
2032                Complex64::new(0.0, 0.0),
2033                Complex64::new(0.0, 0.0),
2034                Complex64::new(1.0, 0.0)
2035            ],
2036        ])
2037    }
2038}
2039
2040/// Trait for all gate operations acting on exactly two qubits.
2041impl OperateTwoQubitGate for ComplexPMInteraction {
2042    /// Returns [KakDecomposition] of the gate.
2043    ///
2044    /// # Returns
2045    ///
2046    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2047    fn kak_decomposition(&self) -> KakDecomposition {
2048        let tr = self.t_real.clone();
2049        let ti = self.t_imag.clone();
2050        let t: CalculatorComplex = CalculatorComplex::new(tr, ti);
2051
2052        let mut circuit_b = Circuit::new();
2053        circuit_b += RotateZ::new(self.target, t.arg());
2054
2055        let mut circuit_a = Circuit::new();
2056        circuit_a += RotateZ::new(self.target, t.arg() * (-1.0));
2057
2058        KakDecomposition {
2059            global_phase: CalculatorFloat::ZERO,
2060            k_vector: [t.norm() / (-2.0), t.norm() / (-2.0), CalculatorFloat::ZERO],
2061            circuit_before: Some(circuit_b),
2062            circuit_after: Some(circuit_a),
2063        }
2064    }
2065}
2066
2067/// Implements the phased-shifted controlled-Z gate.
2068///
2069/// Modified, i.e. phase-shifted ControlledPauliZ two-qubit gate (`<https://arxiv.org/pdf/1908.06101.pdf eq.(1)>`).
2070///
2071#[allow(clippy::upper_case_acronyms)]
2072#[derive(
2073    Debug,
2074    Clone,
2075    PartialEq,
2076    roqoqo_derive::InvolveQubits,
2077    roqoqo_derive::SupportedVersion,
2078    roqoqo_derive::Operate,
2079    roqoqo_derive::Substitute,
2080    roqoqo_derive::OperateTwoQubit,
2081)]
2082#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2083#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
2084pub struct PhaseShiftedControlledZ {
2085    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of the phase-shift on the target qubit.
2086    control: usize,
2087    /// The index of the least significant qubit in the unitary representation. Here, the qubit phase-shift is applied to.
2088    target: usize,
2089    /// The single qubit phase φ.
2090    phi: CalculatorFloat,
2091}
2092
2093#[allow(non_upper_case_globals)]
2094const TAGS_PhaseShiftedControlledZ: &[&str; 4] = &[
2095    "Operation",
2096    "GateOperation",
2097    "TwoQubitGateOperation",
2098    "PhaseShiftedControlledZ",
2099];
2100
2101/// Trait for all Operations acting with a unitary gate on a set of qubits.
2102impl OperateGate for PhaseShiftedControlledZ {
2103    /// Returns unitary matrix of the gate.
2104    ///
2105    /// # Returns
2106    ///
2107    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
2108    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
2109    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
2110        // exp(i*x) = cos(x)+i*sin(x)
2111        let phi: f64 = f64::try_from(self.phi.clone())?;
2112        let cos: f64 = phi.cos();
2113        let sin: f64 = phi.sin();
2114        let cos2: f64 = (2.0 * phi + PI).cos();
2115        let sin2: f64 = (2.0 * phi + PI).sin();
2116        Ok(array![
2117            [
2118                Complex64::new(1.0, 0.0),
2119                Complex64::new(0.0, 0.0),
2120                Complex64::new(0.0, 0.0),
2121                Complex64::new(0.0, 0.0)
2122            ],
2123            [
2124                Complex64::new(0.0, 0.0),
2125                Complex64::new(cos, sin),
2126                Complex64::new(0.0, 0.0),
2127                Complex64::new(0.0, 0.0)
2128            ],
2129            [
2130                Complex64::new(0.0, 0.0),
2131                Complex64::new(0.0, 0.0),
2132                Complex64::new(cos, sin),
2133                Complex64::new(0.0, 0.0)
2134            ],
2135            [
2136                Complex64::new(0.0, 0.0),
2137                Complex64::new(0.0, 0.0),
2138                Complex64::new(0.0, 0.0),
2139                Complex64::new(cos2, sin2)
2140            ],
2141        ])
2142    }
2143}
2144
2145/// Trait for all gate operations acting on exactly two qubits.
2146impl OperateTwoQubitGate for PhaseShiftedControlledZ {
2147    /// Returns [KakDecomposition] of the gate.
2148    ///
2149    /// # Returns
2150    ///
2151    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2152    fn kak_decomposition(&self) -> KakDecomposition {
2153        let mut circuit_b = Circuit::new();
2154        circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
2155        circuit_b += RotateZ::new(self.target, CalculatorFloat::FRAC_PI_2);
2156
2157        let mut circuit_a = Circuit::new();
2158        circuit_a += RotateZ::new(self.control, self.phi.clone());
2159        circuit_a += RotateZ::new(self.target, self.phi.clone());
2160
2161        let g: CalculatorFloat = CalculatorFloat::FRAC_PI_4 + self.phi.clone();
2162        KakDecomposition {
2163            global_phase: g,
2164            k_vector: [
2165                CalculatorFloat::ZERO,
2166                CalculatorFloat::ZERO,
2167                CalculatorFloat::FRAC_PI_4,
2168            ],
2169            circuit_before: Some(circuit_b),
2170            circuit_after: Some(circuit_a),
2171        }
2172    }
2173}
2174
2175/// Implements the phase-shifted controlled PhaseShift gate.
2176///
2177#[allow(clippy::upper_case_acronyms)]
2178#[derive(
2179    Debug,
2180    Clone,
2181    PartialEq,
2182    roqoqo_derive::InvolveQubits,
2183    roqoqo_derive::Operate,
2184    roqoqo_derive::Substitute,
2185    roqoqo_derive::OperateTwoQubit,
2186    roqoqo_derive::Rotate,
2187)]
2188#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2189#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
2190pub struct PhaseShiftedControlledPhase {
2191    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of the phase-shift on the target qubit.
2192    control: usize,
2193    /// The index of the least significant qubit in the unitary representation. Here, the qubit phase-shift is applied to.
2194    target: usize,
2195    /// The phase rotation θ.
2196    theta: CalculatorFloat,
2197    /// The single qubit phase φ.
2198    phi: CalculatorFloat,
2199}
2200
2201impl SupportedVersion for PhaseShiftedControlledPhase {
2202    fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
2203        (1, 2, 0)
2204    }
2205}
2206
2207impl super::ImplementedIn1point2 for PhaseShiftedControlledPhase {}
2208
2209#[allow(non_upper_case_globals)]
2210const TAGS_PhaseShiftedControlledPhase: &[&str; 4] = &[
2211    "Operation",
2212    "GateOperation",
2213    "TwoQubitGateOperation",
2214    "PhaseShiftedControlledPhase",
2215];
2216
2217/// Trait for all Operations acting with a unitary gate on a set of qubits.
2218impl OperateGate for PhaseShiftedControlledPhase {
2219    /// Returns unitary matrix of the gate.
2220    ///
2221    /// # Returns
2222    ///
2223    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
2224    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
2225    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
2226        // exp(i*x) = cos(x)+i*sin(x)
2227        let phi: f64 = f64::try_from(self.phi.clone())?;
2228        let theta: f64 = f64::try_from(self.theta.clone())?;
2229        let cos: f64 = phi.cos();
2230        let sin: f64 = phi.sin();
2231        let cos2: f64 = (2.0 * phi + theta).cos();
2232        let sin2: f64 = (2.0 * phi + theta).sin();
2233        Ok(array![
2234            [
2235                Complex64::new(1.0, 0.0),
2236                Complex64::new(0.0, 0.0),
2237                Complex64::new(0.0, 0.0),
2238                Complex64::new(0.0, 0.0)
2239            ],
2240            [
2241                Complex64::new(0.0, 0.0),
2242                Complex64::new(cos, sin),
2243                Complex64::new(0.0, 0.0),
2244                Complex64::new(0.0, 0.0)
2245            ],
2246            [
2247                Complex64::new(0.0, 0.0),
2248                Complex64::new(0.0, 0.0),
2249                Complex64::new(cos, sin),
2250                Complex64::new(0.0, 0.0)
2251            ],
2252            [
2253                Complex64::new(0.0, 0.0),
2254                Complex64::new(0.0, 0.0),
2255                Complex64::new(0.0, 0.0),
2256                Complex64::new(cos2, sin2)
2257            ],
2258        ])
2259    }
2260}
2261
2262/// Trait for all gate operations acting on exactly two qubits.
2263impl OperateTwoQubitGate for PhaseShiftedControlledPhase {
2264    /// Returns [KakDecomposition] of the gate.
2265    ///
2266    /// # Returns
2267    ///
2268    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2269    fn kak_decomposition(&self) -> KakDecomposition {
2270        let mut circuit_b = Circuit::new();
2271        circuit_b += RotateZ::new(self.control, self.theta.clone() / 2.0);
2272        circuit_b += RotateZ::new(self.target, self.theta.clone() / 2.0);
2273
2274        let mut circuit_a = Circuit::new();
2275        circuit_a += RotateZ::new(self.control, self.phi.clone());
2276        circuit_a += RotateZ::new(self.target, self.phi.clone());
2277
2278        let g: CalculatorFloat = self.theta.clone() / 4.0 + self.phi.clone();
2279        KakDecomposition {
2280            global_phase: g,
2281            k_vector: [
2282                CalculatorFloat::ZERO,
2283                CalculatorFloat::ZERO,
2284                self.theta.clone() / 4.0,
2285            ],
2286            circuit_before: Some(circuit_b),
2287            circuit_after: Some(circuit_a),
2288        }
2289    }
2290}
2291
2292/// Implements the controlled RotateX operation.
2293#[allow(clippy::upper_case_acronyms)]
2294#[derive(
2295    Debug,
2296    Clone,
2297    PartialEq,
2298    roqoqo_derive::InvolveQubits,
2299    roqoqo_derive::Operate,
2300    roqoqo_derive::Substitute,
2301    roqoqo_derive::OperateTwoQubit,
2302    roqoqo_derive::Rotate,
2303)]
2304#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2305#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
2306pub struct ControlledRotateX {
2307    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of the phase-shift on the target qubit.
2308    control: usize,
2309    /// The index of the least significant qubit in the unitary representation. Here, the qubit phase-shift is applied to.
2310    target: usize,
2311    /// The angle θ of the rotation, in the interval from 0 to 2 * 2π.
2312    theta: CalculatorFloat,
2313}
2314
2315impl SupportedVersion for ControlledRotateX {
2316    fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
2317        (1, 3, 0)
2318    }
2319}
2320
2321impl super::ImplementedIn1point3 for ControlledRotateX {}
2322
2323#[allow(non_upper_case_globals)]
2324const TAGS_ControlledRotateX: &[&str; 5] = &[
2325    "Operation",
2326    "GateOperation",
2327    "TwoQubitGateOperation",
2328    "Rotation",
2329    "ControlledRotateX",
2330];
2331
2332/// Trait for all Operations acting with a unitary gate on a set of qubits.
2333impl OperateGate for ControlledRotateX {
2334    /// Returns unitary matrix of the gate.
2335    ///
2336    /// # Returns
2337    ///
2338    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
2339    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
2340    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
2341        let c: f64 = (f64::try_from(self.theta.clone())? / 2.0).cos();
2342        let s: f64 = (f64::try_from(self.theta.clone())? / 2.0).sin();
2343        Ok(array![
2344            [
2345                Complex64::new(1.0, 0.0),
2346                Complex64::new(0.0, 0.0),
2347                Complex64::new(0.0, 0.0),
2348                Complex64::new(0.0, 0.0)
2349            ],
2350            [
2351                Complex64::new(0.0, 0.0),
2352                Complex64::new(1.0, 0.0),
2353                Complex64::new(0.0, 0.0),
2354                Complex64::new(0.0, 0.0)
2355            ],
2356            [
2357                Complex64::new(0.0, 0.0),
2358                Complex64::new(0.0, 0.0),
2359                Complex64::new(c, 0.0),
2360                Complex64::new(0.0, -s)
2361            ],
2362            [
2363                Complex64::new(0.0, 0.0),
2364                Complex64::new(0.0, 0.0),
2365                Complex64::new(0.0, -s),
2366                Complex64::new(c, 0.0)
2367            ]
2368        ])
2369    }
2370}
2371
2372/// Trait for all gate operations acting on exactly two qubits.
2373impl OperateTwoQubitGate for ControlledRotateX {
2374    /// Returns [KakDecomposition] of the gate.
2375    ///
2376    /// # Returns
2377    ///
2378    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2379    fn kak_decomposition(&self) -> KakDecomposition {
2380        let mut circuit_b = Circuit::new();
2381        circuit_b += Hadamard::new(self.target);
2382        circuit_b += RotateZ::new(self.target, self.theta.clone() / 2.0);
2383
2384        let mut circuit_a = Circuit::new();
2385        circuit_a += Hadamard::new(self.target);
2386
2387        KakDecomposition {
2388            global_phase: CalculatorFloat::ZERO,
2389            k_vector: [
2390                CalculatorFloat::ZERO,
2391                CalculatorFloat::ZERO,
2392                self.theta.clone() / 4.0,
2393            ],
2394            circuit_before: Some(circuit_b),
2395            circuit_after: Some(circuit_a),
2396        }
2397    }
2398}
2399
2400/// Implements the controlled RotateX operation.
2401#[allow(clippy::upper_case_acronyms)]
2402#[derive(
2403    Debug,
2404    Clone,
2405    PartialEq,
2406    roqoqo_derive::InvolveQubits,
2407    roqoqo_derive::Operate,
2408    roqoqo_derive::Substitute,
2409    roqoqo_derive::OperateTwoQubit,
2410    roqoqo_derive::Rotate,
2411)]
2412#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2413#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
2414pub struct ControlledRotateXY {
2415    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of the phase-shift on the target qubit.
2416    control: usize,
2417    /// The index of the least significant qubit in the unitary representation. Here, the qubit phase-shift is applied to.
2418    target: usize,
2419    /// The angle θ of the rotation, in the interval from 0 to 2 * 2π.
2420    theta: CalculatorFloat,
2421    /// The rotation axis, in spherical coordinates φ gives the angle in the x-y plane.
2422    phi: CalculatorFloat,
2423}
2424impl SupportedVersion for ControlledRotateXY {
2425    fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
2426        (1, 3, 0)
2427    }
2428}
2429
2430impl super::ImplementedIn1point3 for ControlledRotateXY {}
2431
2432#[allow(non_upper_case_globals)]
2433const TAGS_ControlledRotateXY: &[&str; 5] = &[
2434    "Operation",
2435    "GateOperation",
2436    "TwoQubitGateOperation",
2437    "Rotation",
2438    "ControlledRotateXY",
2439];
2440
2441/// Trait for all Operations acting with a unitary gate on a set of qubits.
2442impl OperateGate for ControlledRotateXY {
2443    /// Returns unitary matrix of the gate.
2444    ///
2445    /// # Returns
2446    ///
2447    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
2448    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
2449    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
2450        let c: f64 = (f64::try_from(self.theta.clone())? / 2.0).cos();
2451        let s: f64 = (f64::try_from(self.theta.clone())? / 2.0).sin();
2452        let vx: f64 = (f64::try_from(self.phi.clone())?).cos();
2453        let vy: f64 = (f64::try_from(self.phi.clone())?).sin();
2454        Ok(array![
2455            [
2456                Complex64::new(1.0, 0.0),
2457                Complex64::new(0.0, 0.0),
2458                Complex64::new(0.0, 0.0),
2459                Complex64::new(0.0, 0.0)
2460            ],
2461            [
2462                Complex64::new(0.0, 0.0),
2463                Complex64::new(1.0, 0.0),
2464                Complex64::new(0.0, 0.0),
2465                Complex64::new(0.0, 0.0)
2466            ],
2467            [
2468                Complex64::new(0.0, 0.0),
2469                Complex64::new(0.0, 0.0),
2470                Complex64::new(c, 0.0),
2471                Complex64::new(-s * vy, -s * vx)
2472            ],
2473            [
2474                Complex64::new(0.0, 0.0),
2475                Complex64::new(0.0, 0.0),
2476                Complex64::new(s * vy, -s * vx),
2477                Complex64::new(c, 0.0)
2478            ]
2479        ])
2480    }
2481}
2482
2483/// Trait for all gate operations acting on exactly two qubits.
2484impl OperateTwoQubitGate for ControlledRotateXY {
2485    /// Returns [KakDecomposition] of the gate.
2486    ///
2487    /// # Returns
2488    ///
2489    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2490    fn kak_decomposition(&self) -> KakDecomposition {
2491        let mut circuit_b = Circuit::new();
2492        circuit_b += RotateZ::new(self.target, -self.phi.clone());
2493        circuit_b += Hadamard::new(self.target);
2494        circuit_b += RotateZ::new(self.target, self.theta.clone() / 2.0);
2495
2496        let mut circuit_a = Circuit::new();
2497        circuit_a += Hadamard::new(self.target);
2498        circuit_a += RotateZ::new(self.target, self.phi.clone());
2499
2500        KakDecomposition {
2501            global_phase: CalculatorFloat::ZERO,
2502            k_vector: [
2503                CalculatorFloat::ZERO,
2504                CalculatorFloat::ZERO,
2505                self.theta.clone() / 4.0,
2506            ],
2507            circuit_before: Some(circuit_b),
2508            circuit_after: Some(circuit_a),
2509        }
2510    }
2511}
2512
2513/// Implements the controlled RotateX operation.
2514#[allow(clippy::upper_case_acronyms)]
2515#[derive(
2516    Debug,
2517    Clone,
2518    PartialEq,
2519    roqoqo_derive::InvolveQubits,
2520    roqoqo_derive::Operate,
2521    roqoqo_derive::Substitute,
2522    roqoqo_derive::OperateTwoQubit,
2523)]
2524#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2525#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
2526pub struct EchoCrossResonance {
2527    /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of the phase-shift on the target qubit.
2528    control: usize,
2529    /// The index of the least significant qubit in the unitary representation. Here, the qubit phase-shift is applied to.
2530    target: usize,
2531}
2532impl SupportedVersion for EchoCrossResonance {
2533    fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
2534        (1, 8, 0)
2535    }
2536}
2537
2538impl super::ImplementedIn1point8 for EchoCrossResonance {}
2539
2540#[allow(non_upper_case_globals)]
2541const TAGS_EchoCrossResonance: &[&str; 4] = &[
2542    "Operation",
2543    "GateOperation",
2544    "TwoQubitGateOperation",
2545    "EchoCrossResonance",
2546];
2547
2548/// Trait for all Operations acting with a unitary gate on a set of qubits.
2549impl OperateGate for EchoCrossResonance {
2550    /// Returns unitary matrix of the gate.
2551    ///
2552    /// # Returns
2553    ///
2554    /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
2555    /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
2556    fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
2557        let matrix: Array2<Complex64> = array![
2558            [
2559                Complex64::new(0.0, 0.0),
2560                Complex64::new(0.0, 0.0),
2561                Complex64::new(1.0, 0.0),
2562                Complex64::new(0.0, 1.0)
2563            ],
2564            [
2565                Complex64::new(0.0, 0.0),
2566                Complex64::new(0.0, 0.0),
2567                Complex64::new(0.0, 1.0),
2568                Complex64::new(1.0, 0.0)
2569            ],
2570            [
2571                Complex64::new(1.0, 0.0),
2572                Complex64::new(0.0, -1.0),
2573                Complex64::new(0.0, 0.0),
2574                Complex64::new(0.0, 0.0)
2575            ],
2576            [
2577                Complex64::new(0.0, -1.0),
2578                Complex64::new(1.0, 0.0),
2579                Complex64::new(0.0, 0.0),
2580                Complex64::new(0.0, 0.0)
2581            ]
2582        ];
2583        Ok(matrix / 2.0_f64.sqrt())
2584    }
2585}
2586
2587/// Trait for all gate operations acting on exactly two qubits.
2588impl OperateTwoQubitGate for EchoCrossResonance {
2589    /// Returns [KakDecomposition] of the gate.
2590    ///
2591    /// # Returns
2592    ///
2593    /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2594    fn kak_decomposition(&self) -> KakDecomposition {
2595        let mut circuit_b = Circuit::new();
2596        circuit_b += SGate::new(self.control);
2597        circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
2598        circuit_b += RotateY::new(self.control, CalculatorFloat::FRAC_PI_2);
2599        circuit_b += RotateX::new(self.target, CalculatorFloat::PI);
2600
2601        let mut circuit_a = Circuit::new();
2602        circuit_a += RotateY::new(self.control, CalculatorFloat::FRAC_PI_2 * (-1.0));
2603        circuit_a += PauliX::new(self.control);
2604
2605        KakDecomposition {
2606            global_phase: CalculatorFloat::FRAC_PI_4,
2607            k_vector: [
2608                CalculatorFloat::FRAC_PI_4,
2609                CalculatorFloat::ZERO,
2610                CalculatorFloat::ZERO,
2611            ],
2612            circuit_before: Some(circuit_b),
2613            circuit_after: Some(circuit_a),
2614        }
2615    }
2616}