LCOV - code coverage report
Current view: top level - src/operations - two_qubit_gate_operations.rs (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 1126 1126 100.0 %
Date: 2021-11-09 13:25:48 Functions: 430 430 100.0 %

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

Generated by: LCOV version 1.13