LCOV - code coverage report
Current view: top level - src/operations - mod.rs (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 26 26 100.0 %
Date: 2021-11-09 13:25:48 Functions: 6 8 75.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             : //! Operations are the atomic instructions in any quantum program that can be represented by roqoqo.
      14             : //!
      15             : //! Operations can be of various kinds: Definitions, GateOperations, PRAGMAs or measurement Operations.
      16             : //! * Definition operations define the classical registers and variables in the Circuit.
      17             : //! * GateOperations are single-, two- or multi-qubit gate operations that act on a set of qubits
      18             : //! and can be executed on a quantum computing device.
      19             : //! * PRAGMAs are operations that can be used when running a simulation of a quantum computing program.
      20             : //! * Measurement Operations are operations that perform a measurement either on a quantum computing device (MeasuareQubit)
      21             : //! or on a simulation of a quantum computing program (PRAGMA measurement operations).
      22             : 
      23             : use crate::RoqoqoError;
      24             : #[cfg(feature = "dynamic")]
      25             : use dyn_clone::DynClone;
      26             : use ndarray::Array2;
      27             : use num_complex::Complex64;
      28             : use qoqo_calculator::{Calculator, CalculatorComplex, CalculatorFloat};
      29             : use roqoqo_derive::*;
      30             : use std::collections::{HashMap, HashSet};
      31             : /// Collection of roqoqo definition operations.
      32             : #[doc(hidden)]
      33             : mod define_operations;
      34             : pub use define_operations::*;
      35             : /// Collection of roqoqo measurement operations.
      36             : #[doc(hidden)]
      37             : mod measurement_operations;
      38             : pub use measurement_operations::*;
      39             : /// Collection of roqoqo multi qubit gate operations.
      40             : #[doc(hidden)]
      41             : mod multi_qubit_gate_operations;
      42             : pub use multi_qubit_gate_operations::*;
      43             : /// Collection of roqoqo PRAGMA operation structs.\
      44             : #[doc(hidden)]
      45             : mod pragma_operations;
      46             : pub use pragma_operations::*;
      47             : /// Collection of roqoqo single qubit gate operations.
      48             : #[doc(hidden)]
      49             : mod single_qubit_gate_operations;
      50             : pub use single_qubit_gate_operations::*;
      51             : /// Collection of roqoqo two qubit gate operations.
      52             : #[doc(hidden)]
      53             : mod two_qubit_gate_operations;
      54             : pub use two_qubit_gate_operations::*;
      55             : 
      56             : include!(concat!(env!("OUT_DIR"), "/_auto_generated_operations.rs"));
      57             : 
      58             : /// Represents qubits involved in a roqoqo Operation.
      59         101 : #[derive(Debug, PartialEq, Clone, Eq)]
      60             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
      61             : pub enum InvolvedQubits {
      62             :     /// Operation affects all qubits no matter how many there are.
      63             :     All,
      64             :     /// Operation affects no qubits (annotations etc.).
      65             :     None,
      66             :     /// Operation affects a specific set of qubits.
      67             :     Set(HashSet<usize>),
      68             : }
      69             : #[cfg(feature = "dynamic")]
      70             : /// Universal basic trait for all operations of roqoqo.
      71             : #[cfg_attr(feature = "dynamic", typetag::serde(tag = "Operate"))]
      72             : pub trait Operate: InvolveQubits + SubstituteDyn + DynClone + std::fmt::Debug + Send {
      73             :     /// Returns tags classifying the type of operation.
      74             :     ///
      75             :     /// Used for type based dispatch in ffi interfaces.
      76             :     fn tags(&self) -> &'static [&'static str];
      77             :     /// Returns hqslang name of operation.
      78             :     ///
      79             :     /// As a general rule that should correspond to the roqoqo name of the operation.
      80             :     fn hqslang(&self) -> &'static str;
      81             :     /// Returns true when operation has symbolic parameters.
      82             :     fn is_parametrized(&self) -> bool;
      83             : }
      84             : 
      85             : #[cfg(not(feature = "dynamic"))]
      86             : /// Universal basic trait for all operations of roqoqo.
      87             : ///
      88             : /// # Example
      89             : /// ```
      90             : /// use roqoqo::operations::{Operate, PauliX, RotateZ};
      91             : /// use qoqo_calculator::CalculatorFloat;
      92             : ///
      93             : /// let paulix = PauliX::new(0);
      94             : /// let gate_tags: &[&str; 4] = &[
      95             : ///     "Operation",
      96             : ///     "GateOperation",
      97             : ///     "SingleQubitGateOperation",
      98             : ///     "PauliX",
      99             : /// ];
     100             : ///
     101             : /// // 1) The tags of the operation tell us what kind of operation it is, and what traits it implements
     102             : /// assert_eq!(paulix.tags(), gate_tags);
     103             : /// // 2) The name of the operation is given by hqslang
     104             : /// assert_eq!(paulix.hqslang(), "PauliX");
     105             : /// // 3) Whether a gate is parametrized is determined by whether any of its inputs are symbolic (CalculatorFloat with a string).
     106             : /// // As the PauliX gate only takes an integer input (qubit), it can never be parametrized.
     107             : /// assert!(!paulix.is_parametrized());
     108             : /// // However, a RotateZ gate can be parametrized:
     109             : /// let rotatez_param = RotateZ::new(0, CalculatorFloat::from("parametrized"));
     110             : /// assert!(rotatez_param.is_parametrized());
     111             : /// // But it can also not be parametrized:
     112             : /// let rotatez_not_param = RotateZ::new(0, CalculatorFloat::from(2.0));
     113             : /// assert!(!rotatez_not_param.is_parametrized());
     114             : /// ```
     115             : ///
     116             : pub trait Operate: InvolveQubits + Substitute + Clone + std::fmt::Debug + Send {
     117             :     /// Returns tags classifying the type of the operation.
     118             :     ///
     119             :     /// Used for type based dispatch in ffi interfaces.
     120             :     fn tags(&self) -> &'static [&'static str];
     121             :     /// Returns hqslang name of the operation.
     122             :     ///
     123             :     /// As a general rule that should correspond to the roqoqo name of the operation.
     124             :     fn hqslang(&self) -> &'static str;
     125             :     /// Returns `true` when operation has symbolic parameters.
     126             :     fn is_parametrized(&self) -> bool;
     127             : }
     128             : 
     129             : #[cfg(feature = "dynamic")]
     130             : dyn_clone::clone_trait_object!(Operate);
     131             : 
     132             : /// Trait for the qubits involved in each Operation.
     133             : ///
     134             : /// # Example
     135             : /// ```
     136             : /// use roqoqo::operations::{CNOT, DefinitionFloat, InvolveQubits, InvolvedQubits, PragmaRepeatedMeasurement};
     137             : /// use std::collections::{HashMap, HashSet};
     138             : ///
     139             : /// // The involved qubits of the operation tell us which qubits are affected by the Operation.
     140             : /// // There are three possibilities:
     141             : /// // 1) The involved qubits are a set of integers (usize): these are the qubits affected by the Operation
     142             : /// let cnot = CNOT::new(0, 1);
     143             : /// let mut qubits: HashSet<usize> = HashSet::new();
     144             : /// qubits.insert(0);
     145             : /// qubits.insert(1);
     146             : /// assert_eq!(cnot.involved_qubits(), InvolvedQubits::Set(qubits));
     147             : /// // 2) The involved qubits are None: there are no qubits affected by this Operation
     148             : /// let def_float = DefinitionFloat::new("ro".to_string(), 1, true);
     149             : /// assert_eq!(def_float.involved_qubits(), InvolvedQubits::None);
     150             : /// // 3) The involved qubits are All: all of the qubits in the Circuit are affected by the Operation
     151             : /// let mut qubit_mapping: HashMap<usize, usize> = HashMap::new();
     152             : /// qubit_mapping.insert(0, 1);
     153             : /// let pragma = PragmaRepeatedMeasurement::new("ro".to_string(), 2, Some(qubit_mapping.clone()));
     154             : /// assert_eq!(pragma.involved_qubits(), InvolvedQubits::All);
     155             : /// ```
     156             : pub trait InvolveQubits {
     157             :     /// Returns a list of all involved qubits.
     158             :     fn involved_qubits(&self) -> InvolvedQubits;
     159             : }
     160             : 
     161             : /// Substitute trait allowing to replace symbolic parameters and to perform qubit mappings.
     162             : ///
     163             : /// # Example
     164             : /// ```
     165             : /// use roqoqo::operations::{RotateZ, Substitute};
     166             : /// use qoqo_calculator::{Calculator, CalculatorFloat};
     167             : /// use std::collections::HashMap;
     168             : ///
     169             : /// // 1) The substitute_parameters function substitutes all symbolic parameters in the Operation and its inputs
     170             : /// let rotatez = RotateZ::new(0, CalculatorFloat::from("sub"));
     171             : /// let mut substitution_dict: Calculator = Calculator::new();
     172             : /// substitution_dict.set_variable("sub", 0.0);
     173             : /// let result = rotatez
     174             : ///     .substitute_parameters(&mut substitution_dict)
     175             : ///     .unwrap();
     176             : /// assert_eq!(result, RotateZ::new(0, CalculatorFloat::from(0.0)));
     177             : /// // 2) The remap_qubits function remaps all qubits in the Operation and its inputs
     178             : /// let rotatez = RotateZ::new(0, CalculatorFloat::from(0.0));
     179             : /// let mut qubit_mapping_test: HashMap<usize, usize> = HashMap::new();
     180             : /// qubit_mapping_test.insert(0, 2);
     181             : /// let result = rotatez.remap_qubits(&qubit_mapping_test).unwrap();
     182             : /// assert_eq!(result, RotateZ::new(2, CalculatorFloat::from(0.0)));
     183             : /// ```
     184             : ///
     185             : pub trait Substitute
     186             : where
     187             :     Self: Sized,
     188             : {
     189             :     /// Substitutes symbolic parameters in clone of the operation.
     190             :     fn substitute_parameters(&self, calculator: &mut Calculator) -> Result<Self, RoqoqoError>;
     191             :     /// Remaps the qubits in clone of the operation.
     192             :     fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError>;
     193             : }
     194             : 
     195             : #[cfg(feature = "dynamic")]
     196             : /// Helper trait for implementing substitute for Box<dyn> operation.
     197             : pub trait SubstituteDyn {
     198             :     /// Substitute parameters in symbolic expression in clone of operation.
     199             :     fn substitute_parameters_dyn(
     200             :         &self,
     201             :         calculator: &mut Calculator,
     202             :     ) -> Result<Box<dyn Operate>, RoqoqoError>;
     203             :     /// Remap qubits in operations in clone of operation.
     204             :     fn remap_qubits_dyn(
     205             :         &self,
     206             :         mapping: &HashMap<usize, usize>,
     207             :     ) -> Result<Box<dyn Operate>, RoqoqoError>;
     208             : }
     209             : 
     210             : #[cfg(feature = "dynamic")]
     211             : impl<T> SubstituteDyn for T
     212             : where
     213             :     T: 'static + Operate + Substitute,
     214             : {
     215             :     /// Substitute symbolic parameters in boxed clone of operation.
     216             :     fn substitute_parameters_dyn(
     217             :         &self,
     218             :         calculator: &mut Calculator,
     219             :     ) -> Result<Box<dyn Operate>, RoqoqoError> {
     220             :         Ok(Box::new(Substitute::substitute_parameters(
     221             :             self, calculator,
     222             :         )?))
     223             :     }
     224             :     /// Remap qubits in operations in boxed clone of operation.
     225             :     fn remap_qubits_dyn(
     226             :         &self,
     227             :         mapping: &HashMap<usize, usize>,
     228             :     ) -> Result<Box<dyn Operate>, RoqoqoError> {
     229             :         Ok(Box::new(Substitute::remap_qubits(self, mapping)?))
     230             :     }
     231             : }
     232             : 
     233             : /// Trait for operations acting on exactly one qubit.
     234             : ///
     235             : /// # Example
     236             : /// ```
     237             : /// use roqoqo::operations::{OperateSingleQubit, PauliX};
     238             : /// let paulix = PauliX::new(0);
     239             : /// assert_eq!(paulix.qubit(), &0_usize);
     240             : /// ```
     241             : ///
     242             : pub trait OperateSingleQubit: Operate + InvolveQubits + Substitute + Clone + PartialEq {
     243             :     /// Returns `qubit` the Operation acts on.
     244             :     fn qubit(&self) -> &usize;
     245             : }
     246             : 
     247             : /// Trait for Operations acting on exactly two qubits.
     248             : ///
     249             : /// # Example
     250             : /// ```
     251             : /// use roqoqo::operations::{CNOT, OperateTwoQubit};
     252             : /// let cnot = CNOT::new(0, 1);
     253             : /// assert_eq!(cnot.control(), &0_usize);
     254             : /// assert_eq!(cnot.target(), &1_usize);
     255             : /// ```
     256             : ///
     257             : pub trait OperateTwoQubit: Operate + InvolveQubits + Substitute + Clone + PartialEq {
     258             :     /// Returns `target` qubit of two qubit Operation.
     259             :     fn target(&self) -> &usize;
     260             :     /// Returns `control` qubit of two qubit Operation.
     261             :     fn control(&self) -> &usize;
     262             : }
     263             : 
     264             : /// Trait for operations acting on multiple (more than two) qubits.
     265             : ///
     266             : /// # Example
     267             : /// ```
     268             : /// use roqoqo::operations::{MultiQubitMS, OperateMultiQubit};
     269             : /// use qoqo_calculator::CalculatorFloat;
     270             : /// let multi_ms = MultiQubitMS::new(vec![0, 1, 3], CalculatorFloat::from(0.0));
     271             : /// assert_eq!(multi_ms.qubits(), &vec![0, 1, 3]);
     272             : /// ```
     273             : ///
     274             : pub trait OperateMultiQubit: Operate + InvolveQubits + Substitute + Clone + PartialEq {
     275             :     /// Returns vector of qubits operation is acting on in descending order of significance
     276             :     fn qubits(&self) -> &Vec<usize>;
     277             : }
     278             : 
     279             : /// Trait for PRAGMA Operations that are not necessary available on all universal quantum hardware.
     280             : ///
     281             : /// PRAGMA Operations are unphysical in terms of quantum mechanics and are meant to be used for simulation purposes only, i.e. to run on simulation backends.
     282             : ///
     283             : pub trait OperatePragma: Operate + InvolveQubits + Substitute + Clone + PartialEq {}
     284             : 
     285             : /// Trait for PRAGMA Operations that are not necessary available on all universal quantum hardware, that indicate noise.
     286             : ///
     287             : /// # Example
     288             : /// ```
     289             : /// use ndarray::{array, Array2};
     290             : /// use roqoqo::operations::{OperatePragmaNoise, OperatePragmaNoiseProba, PragmaDamping};
     291             : /// use qoqo_calculator::CalculatorFloat;
     292             : ///
     293             : /// let pragma = PragmaDamping::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02));
     294             : ///
     295             : /// // 1) The superoperator representation of the noise Pragma
     296             : /// let superop_prob: f64 = *pragma.probability().float().unwrap();
     297             : /// let superop_sqrt: f64 = (1.0 - superop_prob.clone()).sqrt();
     298             : /// let superop: Array2<f64> = array![
     299             : ///     [1.0, 0.0, 0.0, superop_prob.clone()],
     300             : ///     [0.0, superop_sqrt, 0.0, 0.0],
     301             : ///     [0.0, 0.0, superop_sqrt, 0.0],
     302             : ///     [0.0, 0.0, 0.0, 1.0 - superop_prob.clone()],
     303             : /// ];
     304             : /// assert_eq!(superop, pragma.superoperator().unwrap());
     305             : /// // 2) The power function applied to the noise Pragma
     306             : /// let pragma_test = PragmaDamping::new(
     307             : ///     0,
     308             : ///     CalculatorFloat::from(0.005 * 1.5),
     309             : ///     CalculatorFloat::from(0.02),
     310             : /// );
     311             : /// assert_eq!(pragma_test, pragma.powercf(CalculatorFloat::from(1.5)));
     312             : /// ```
     313             : ///
     314             : pub trait OperatePragmaNoise:
     315             :     Operate + InvolveQubits + Substitute + Clone + PartialEq + OperatePragma
     316             : {
     317             :     /// Returns superoperator matrix of the Operation.
     318             :     fn superoperator(&self) -> Result<Array2<f64>, RoqoqoError>;
     319             :     /// Returns the gate to the power of `power`.
     320             :     fn powercf(&self, power: CalculatorFloat) -> Self;
     321             : }
     322             : 
     323             : /// Trait for PRAGMA Operations that are not necessary available on all universal quantum hardware, that indicate noise.
     324             : ///
     325             : /// # Example
     326             : /// ```
     327             : /// use ndarray::{array, Array2};
     328             : /// use roqoqo::operations::{OperatePragmaNoiseProba, PragmaDamping};
     329             : /// use qoqo_calculator::CalculatorFloat;
     330             : ///
     331             : /// let pragma = PragmaDamping::new(0, CalculatorFloat::from(0.005), CalculatorFloat::from(0.02));
     332             : ///
     333             : /// // The probability of the noise Pragma
     334             : /// let proba_pre_exp: f64 = -2.0 * 0.005 * 0.02;
     335             : /// let proba = CalculatorFloat::from(0.5 * (1.0 - proba_pre_exp.exp()));
     336             : /// assert_eq!(proba, pragma.probability());
     337             : /// ```
     338             : ///
     339             : pub trait OperatePragmaNoiseProba:
     340             :     Operate + InvolveQubits + Substitute + Clone + PartialEq + OperatePragma + OperatePragmaNoise
     341             : {
     342             :     /// Returns the probability of the gate, based on its gate_time and rate.
     343             :     fn probability(&self) -> CalculatorFloat;
     344             : }
     345             : 
     346             : /// Trait for Operations acting with a unitary gate on a set of qubits.
     347             : ///
     348             : /// # Example
     349             : /// ```
     350             : /// use ndarray::array;
     351             : /// use num_complex::Complex64;
     352             : /// use roqoqo::operations::{OperateGate, PauliX};
     353             : ///
     354             : /// let paulix = PauliX::new(0);
     355             : /// let matrix = array![
     356             : ///     [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)],
     357             : ///     [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]
     358             : /// ];
     359             : /// assert_eq!(paulix.unitary_matrix().unwrap(), matrix);
     360             : /// ```
     361             : ///
     362             : pub trait OperateGate: Operate + InvolveQubits + Substitute + Clone + PartialEq {
     363             :     /// Returns unitary matrix of the gate.
     364             :     fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError>;
     365             : }
     366             : 
     367             : /// Trait for unitary operations corresponding to rotations that can be characteriszed by a single rotation parameter theta.
     368             : ///
     369             : /// # Example
     370             : /// ```
     371             : /// use qoqo_calculator::CalculatorFloat;
     372             : /// use roqoqo::operations::{Rotate, RotateX};
     373             : /// let rotatex = RotateX::new(0, 2.0.into());
     374             : ///
     375             : /// // 1) The angle of rotation of the Rotate Operation
     376             : /// assert_eq!(rotatex.theta(), &CalculatorFloat::from(2.0));
     377             : /// // 2) The power function applied to the Rotate Operation
     378             : /// assert_eq!(rotatex.powercf(CalculatorFloat::from(1.5)), RotateX::new(0, 3.0.into()));
     379             : /// ```
     380             : ///
     381             : pub trait Rotate: OperateGate + Operate + InvolveQubits + Substitute + Clone + PartialEq {
     382             :     /// Returns rotation parameter theta.
     383             :     fn theta(&self) -> &CalculatorFloat;
     384             :     /// Returns the gate to the power of `power`.`
     385             :     fn powercf(&self, power: CalculatorFloat) -> Self;
     386             : 
     387             :     #[cfg(feature = "overrotate")]
     388             :     /// Returns clone of the gate with one parameter statistically overrotated.
     389             :     ///
     390             :     /// A random number drawn from a normal distribution N(0, variance^2)
     391             :     /// and multiplied by the amplitue  is added to the overrotated parameter.  
     392             :     /// gate_overrotated.parameter() = gate.parameter + amplitude * rand(N(0, variance^2))
     393             :     ///
     394             :     /// This functionc is specifically designed for statistical overrotations that change the angle
     395             :     /// of an applied rotation gate randomly during the execution of a quantum program.  
     396             :     /// For static overrotations that represent a drift in the callibration of gates and are constant
     397             :     /// during the execution of a quantum programm use symbolic parameters and the substitute_parameters
     398             :     /// function.
     399             :     ///
     400             :     /// # Arguments
     401             :     ///
     402             :     /// *`amplitude` - The amplitude the random number is multiplied with.
     403             :     /// *`variance` - The standard deviation of the normal distribution the random number is drawn from.
     404             :     ///
     405             :     /// # Example
     406             :     /// ```
     407             :     /// use roqoqo::prelude::*;
     408             :     /// use roqoqo::operations::RotateZ;
     409             :     ///
     410             :     /// let gate = RotateZ::new(0, 1.0.into());
     411             :     /// let overrotated_gate = gate.overrotate(&1.0, &0.5);
     412             :     /// println!("{:?}", gate);
     413             :     /// println!("{:?}", overrotated_gate);
     414             :     /// let gate_symbolic = RotateZ::new(0, "theta_var".into());
     415             :     /// let overrotated_symbolic = gate_symbolic.overrotate(&1.0, &0.5);
     416             :     /// println!("{:?}", gate_symbolic);
     417             :     /// println!("{:?}", overrotated_symbolic);
     418             :     /// ```
     419             :     fn overrotate(&self, amplitude: &f64, variance: &f64) -> Self;
     420             : }
     421             : 
     422             : /// Trait for definition operations.
     423             : ///
     424             : /// # Example
     425             : /// ```
     426             : /// use roqoqo::operations::{Define, DefinitionFloat};
     427             : /// let definition = DefinitionFloat::new("ro".to_string(), 1, false);
     428             : /// assert_eq!(definition.name(), &"ro".to_string());
     429             : /// ```
     430             : ///
     431             : pub trait Define: Operate + InvolveQubits + Substitute + Clone + PartialEq {
     432             :     /// Returns name of definition operation.
     433             :     fn name(&self) -> &String;
     434             : }
     435             : 
     436             : /// Trait for unitary operations without any free parameters.
     437             : ///
     438             : /// # Example
     439             : /// ```
     440             : /// use roqoqo::operations::{OperateConstantGate, PauliX};
     441             : /// let paulix = PauliX::new(0);
     442             : /// ```
     443             : ///
     444             : pub trait OperateConstantGate:
     445             :     OperateGate + Operate + InvolveQubits + Substitute + Clone + PartialEq
     446             : {
     447             :     /// Returns true when unitary operation U is self inverse U*U = I.
     448             :     fn inverse(&self) -> GateOperation;
     449             : }
     450             : 
     451             : /// Trait for unitary operations acting on exactly one qubit.
     452             : ///
     453             : /// Implements the general single qubit unitary gates  that can be brought into the form:
     454             : ///
     455             : /// $$ U =e^{i \phi}\begin{pmatrix}
     456             : /// \alpha_r+i \alpha_i & -\beta_r+i \beta_i \\\\
     457             : /// \beta_r+i \beta_i & \alpha_r-i\alpha_i
     458             : /// \end{pmatrix} $$
     459             : ///
     460             : /// These gates can be parametrized by five real parameters:
     461             : ///
     462             : /// * `alpha_r` - The real part $ \alpha_r $ of the on-diagonal elements of the single-qubit unitary.
     463             : /// * `alpha_i` - The imaginary part $ \alpha_i $ of the on-diagonal elements of the single-qubit unitary.
     464             : /// * `beta_r` - The real part $ \beta_r $ of the off-diagonal elements of the single-qubit unitary.
     465             : /// * `beta_i` - The imaginary part $ \beta_i $ of the off-diagonal elements of the single-qubit unitary.
     466             : /// * `global_phase` - The global phase $ \phi $ of the single-qubit unitary.
     467             : ///
     468             : /// These are the single qubit gates that are performed in the Circuit(), and are then translated
     469             : /// to quantum hardware through the relevant backend. Two-qubit gates are also available
     470             : /// (see roqoqo/src/operations/two_qubit_gate_operations.rs).
     471             : ///
     472             : /// # Example
     473             : /// ```
     474             : /// use qoqo_calculator::CalculatorFloat;
     475             : /// use roqoqo::operations::{OperateSingleQubitGate, PauliX};
     476             : /// use std::f64::consts::PI;
     477             : ///
     478             : /// let paulix = PauliX::new(0);
     479             : ///
     480             : /// assert_eq!(paulix.alpha_r(), 0.0.into());
     481             : /// assert_eq!(paulix.alpha_i(), 0.0.into());
     482             : /// assert_eq!(paulix.beta_r(), 0.0.into());
     483             : /// assert_eq!(paulix.beta_i(), CalculatorFloat::from(-1.0));
     484             : /// assert_eq!(paulix.global_phase(), ((PI) / 2.0).into());
     485             : /// ```
     486             : ///
     487             : pub trait OperateSingleQubitGate:
     488             :     Operate
     489             :     + OperateGate
     490             :     + InvolveQubits
     491             :     + Substitute
     492             :     + OperateSingleQubit
     493             :     + Clone
     494             :     + PartialEq
     495             :     + OperateSingleQubit
     496             :     + std::fmt::Debug
     497             : {
     498             :     /// Returns alpha_r parameter of operation.
     499             :     ///
     500             :     /// # Returns
     501             :     ///
     502             :     /// * `alpha_r` - The real part $\alpha_r$ of the on-diagonal elements of the single-qubit unitary matrix.
     503             :     fn alpha_r(&self) -> CalculatorFloat;
     504             : 
     505             :     /// Returns alpha_i parameter of operation.
     506             :     ///
     507             :     /// # Returns
     508             :     ///
     509             :     /// * `alpha_i` - The imaginary part $ \alpha_i $ of the on-diagonal elements of the single-qubit unitary matrix.
     510             :     fn alpha_i(&self) -> CalculatorFloat;
     511             : 
     512             :     /// Returns beta_r parameter of operation.
     513             :     ///
     514             :     /// # Returns
     515             :     ///
     516             :     /// * `beta_r` - The real part $ \beta_r $ of the off-diagonal elements of the single-qubit unitary matrix.
     517             :     fn beta_r(&self) -> CalculatorFloat;
     518             : 
     519             :     /// Returns beta_i parameter of operation.
     520             :     ///
     521             :     /// # Returns
     522             :     ///
     523             :     /// * `beta_i` -  imaginary part $ \beta_i $ of the off-diagonal elements of the single-qubit unitary matrix.
     524             :     fn beta_i(&self) -> CalculatorFloat;
     525             : 
     526             :     /// Returns global_phase parameter of operation.
     527             :     ///
     528             :     /// # Returns
     529             :     ///
     530             :     /// * `global_phase` - The global phase phi $ \phi $ of the single-qubit unitary.
     531             :     fn global_phase(&self) -> CalculatorFloat;
     532             : 
     533             :     /// Multiplies two compatible operations implementing OperateSingleQubitGate.
     534             :     ///
     535             :     /// Does not consume the two operations being multiplied.
     536             :     /// Only Operations
     537             :     ///
     538             :     /// # Arguments:
     539             :     ///
     540             :     /// * `other` - An Operation implementing [OperateSingleQubitGate].
     541             :     ///
     542             :     /// # Example
     543             :     /// ```
     544             :     /// use roqoqo::operations::{RotateZ, RotateX};
     545             :     /// use roqoqo::prelude::*;
     546             :     /// use qoqo_calculator::CalculatorFloat;
     547             :     ///
     548             :     /// let gate1 =  RotateZ::new(0, CalculatorFloat::from(1));
     549             :     /// let gate2 =  RotateX::new(0, CalculatorFloat::from(1));
     550             :     /// let multiplied = gate1.mul(&gate2).unwrap();
     551             :     /// ```
     552          47 :     fn mul<T>(&self, other: &T) -> Result<SingleQubitGate, RoqoqoError>
     553          47 :     where
     554          47 :         T: OperateSingleQubitGate,
     555          47 :     {
     556          47 :         if self.qubit() != other.qubit() {
     557           2 :             return Err(RoqoqoError::MultiplicationIncompatibleQubits {
     558           2 :                 squbit: *self.qubit(),
     559           2 :                 oqubit: *other.qubit(),
     560           2 :             });
     561          45 :         }
     562          45 :         let alpha = CalculatorComplex::new(self.alpha_r(), self.alpha_i());
     563          45 :         let beta = CalculatorComplex::new(self.beta_r(), self.beta_i());
     564          45 :         let oalpha = CalculatorComplex::new(other.alpha_r(), other.alpha_i());
     565          45 :         let obeta = CalculatorComplex::new(other.beta_r(), other.beta_i());
     566          45 :         let new_alpha = alpha.clone() * &oalpha - beta.conj() * &obeta;
     567          45 :         let new_beta = beta * oalpha + obeta * alpha.conj();
     568          45 :         Ok(SingleQubitGate::new(
     569          45 :             *other.qubit(),
     570          45 :             new_alpha.re,
     571          45 :             new_alpha.im,
     572          45 :             new_beta.re,
     573          45 :             new_beta.im,
     574          45 :             self.global_phase() + other.global_phase(),
     575          45 :         ))
     576          47 :     }
     577             : }
     578             : 
     579             : /// Trait for all Operations operating on or affecting exactly two qubits.
     580             : ///
     581             : /// # Example
     582             : /// ```
     583             : /// use roqoqo::operations::{ISwap, KakDecomposition, OperateTwoQubitGate};
     584             : /// use qoqo_calculator::CalculatorFloat;
     585             : /// let iswap = ISwap::new(0, 1);
     586             : ///
     587             : /// assert_eq!(iswap.kak_decomposition().circuit_before, None);
     588             : /// assert_eq!(iswap.kak_decomposition().circuit_after, None);
     589             : /// assert_eq!(iswap.kak_decomposition().global_phase, CalculatorFloat::ZERO);
     590             : /// assert_eq!(iswap.kak_decomposition().k_vector, [CalculatorFloat::FRAC_PI_4, CalculatorFloat::FRAC_PI_4, CalculatorFloat::ZERO]);
     591             : /// ```
     592             : ///
     593             : pub trait OperateTwoQubitGate:
     594             :     Operate + OperateGate + OperateTwoQubit + InvolveQubits + Substitute + Clone + PartialEq
     595             : {
     596             :     /// Returns [KakDecomposition] of two qubit gate.
     597             :     fn kak_decomposition(&self) -> KakDecomposition;
     598             : }
     599             : 
     600             : /// Trait for all Operations operating on or affecting more than two qubits.
     601             : ///
     602             : /// # Example
     603             : /// ```
     604             : /// use roqoqo::operations::{CNOT, Hadamard, MultiQubitMS, OperateMultiQubitGate, RotateZ};
     605             : /// use roqoqo::Circuit;
     606             : /// use qoqo_calculator::CalculatorFloat;
     607             : ///
     608             : /// let multi_ms = MultiQubitMS::new(vec![0, 1, 2], CalculatorFloat::from(1.0));
     609             : /// let mut circuit = Circuit::new();
     610             : /// circuit += Hadamard::new(0);
     611             : /// circuit += Hadamard::new(1);
     612             : /// circuit += Hadamard::new(2);
     613             : /// circuit += CNOT::new(0, 1);
     614             : /// circuit += CNOT::new(1, 2);
     615             : /// circuit += RotateZ::new(2, CalculatorFloat::from(0.5));
     616             : /// circuit += CNOT::new(1, 2);
     617             : /// circuit += CNOT::new(0, 1);
     618             : /// circuit += Hadamard::new(0);
     619             : /// circuit += Hadamard::new(1);
     620             : /// circuit += Hadamard::new(2);
     621             : ///
     622             : /// assert_eq!(multi_ms.circuit(), circuit);
     623             : /// ```
     624             : ///
     625             : pub trait OperateMultiQubitGate:
     626             :     Operate + OperateGate + OperateMultiQubit + InvolveQubits + Substitute + Clone + PartialEq
     627             : {
     628             :     /// Returns a decomposition of the multi-qubit operation using a circuit with two-qubit-operations.
     629             :     fn circuit(&self) -> crate::Circuit;
     630             : }
     631             : 
     632             : // Implementing DynOperation for storing dynamic operations from extern crates in trait object
     633             : 
     634             : #[cfg(feature = "dynamic")]
     635             : /// A wrapper for Operate trait objects.
     636             : ///
     637             : /// This wrapper struct can be used to insert Operate trait objects in a circuit.
     638             : /// The intended use case is to store structs from an external crate that implement Operate,
     639             : /// in a circuit.
     640             : #[derive(Debug, Clone)]
     641             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     642             : pub struct DynOperation(Box<dyn Operate>);
     643             : 
     644             : #[cfg(feature = "dynamic")]
     645             : #[cfg_attr(feature = "dynamic", typetag::serde)]
     646             : impl Operate for DynOperation {
     647             :     fn tags(&self) -> &'static [&'static str] {
     648             :         self.0.tags()
     649             :     }
     650             :     fn hqslang(&self) -> &'static str {
     651             :         self.0.hqslang()
     652             :     }
     653             :     fn is_parametrized(&self) -> bool {
     654             :         self.0.is_parametrized()
     655             :     }
     656             : }
     657             : #[cfg(feature = "dynamic")]
     658             : impl InvolveQubits for DynOperation {
     659             :     fn involved_qubits(&self) -> InvolvedQubits {
     660             :         self.0.involved_qubits()
     661             :     }
     662             : }
     663             : #[cfg(feature = "dynamic")]
     664             : /// Implements [Substitute] trait allowing to replace symbolic parameters and to perform qubit mappings.
     665             : impl Substitute for DynOperation {
     666             :     fn substitute_parameters(&self, calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
     667             :         Ok(DynOperation(self.0.substitute_parameters_dyn(calculator)?))
     668             :     }
     669             :     fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
     670             :         Ok(DynOperation(self.0.remap_qubits_dyn(mapping)?))
     671             :     }
     672             : }
     673             : #[cfg(feature = "dynamic")]
     674             : impl PartialEq for DynOperation {
     675             :     fn eq(&self, other: &Self) -> bool {
     676             :         self.0.hqslang() == other.0.hqslang()
     677             :     }
     678             : }

Generated by: LCOV version 1.13