LCOV - code coverage report
Current view: top level - src/operations - pragma_operations.rs (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 283 312 90.7 %
Date: 2021-11-09 13:25:48 Functions: 270 285 94.7 %

          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             : //! Collection of roqoqo PRAGMA operations.
      14             : //!
      15             : 
      16             : use crate::operations::{
      17             :     InvolveQubits, InvolvedQubits, Operate, OperateMultiQubit, OperatePragma, OperatePragmaNoise,
      18             :     OperatePragmaNoiseProba, OperateSingleQubit, RoqoqoError, Substitute,
      19             : };
      20             : use crate::Circuit;
      21             : #[cfg(feature = "serialize")]
      22             : use bincode::serialize;
      23             : use nalgebra::Matrix4;
      24             : use ndarray::{array, Array, Array1, Array2};
      25             : use num_complex::Complex64;
      26             : use qoqo_calculator::{Calculator, CalculatorFloat};
      27             : #[cfg(feature = "serialize")]
      28             : use serde::{Deserialize, Serialize};
      29             : use std::collections::HashMap;
      30             : use std::convert::TryFrom;
      31             : 
      32             : /// This PRAGMA Operation sets the number of measurements of the circuit.
      33             : ///
      34             : /// This is used for backends that allow setting the number of tries. However, setting the number of
      35             : /// measurements does not allow access to the underlying wavefunction or density matrix.
      36             : ///
      37             : #[derive(
      38           1 :     Debug,
      39           1 :     Clone,
      40           7 :     PartialEq,
      41          14 :     roqoqo_derive::Operate,
      42           2 :     roqoqo_derive::Substitute,
      43             :     roqoqo_derive::OperatePragma,
      44             : )]
      45             : #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
      46             : pub struct PragmaSetNumberOfMeasurements {
      47             :     /// The number of measurements.
      48             :     number_measurements: usize,
      49             :     /// The register for the readout.
      50             :     readout: String,
      51             : }
      52             : 
      53             : #[allow(non_upper_case_globals)]
      54             : const TAGS_PragmaSetNumberOfMeasurements: &[&str; 3] = &[
      55             :     "Operation",
      56             :     "PragmaOperation",
      57             :     "PragmaSetNumberOfMeasurements",
      58             : ];
      59             : 
      60             : // Implementing the InvolveQubits trait for PragmaSetNumberOfMeasurements.
      61             : impl InvolveQubits for PragmaSetNumberOfMeasurements {
      62             :     /// Lists all involved qubits (here, none).
      63           1 :     fn involved_qubits(&self) -> InvolvedQubits {
      64           1 :         InvolvedQubits::None
      65           1 :     }
      66             : }
      67             : 
      68             : /// This PRAGMA Operation sets the statevector of a quantum register.
      69             : ///
      70             : /// The Circuit() module automatically initializes the qubits in the |0> state, so this PRAGMA
      71             : /// operation allows you to set the state of the qubits to a state of your choosing.
      72             : ///
      73             : /// # Example
      74             : ///
      75             : /// For instance, to initialize the $|\Psi^->$ Bell state, we pass the following `statevec` to
      76             : /// the PragmaSetStateVector operation.
      77             : ///
      78             : /// ```
      79             : /// use ndarray::{array, Array1};
      80             : /// use num_complex::Complex64;
      81             : /// use roqoqo::operations::PragmaSetStateVector;
      82             : ///
      83             : /// let statevec: Array1<Complex64> = array![
      84             : ///     Complex64::new(0.0, 0.0),
      85             : ///     Complex64::new(1.0 / (2.0_f64).sqrt(), 0.0),
      86             : ///     Complex64::new(-1.0 / (2.0_f64).sqrt(), 0.0),
      87             : ///     Complex64::new(0.0, 0.0)
      88             : /// ];
      89             : ///
      90             : /// let pragma = PragmaSetStateVector::new(statevec.clone());
      91             : /// ```
      92             : ///
      93             : #[derive(
      94           1 :     Debug,
      95           1 :     Clone,
      96           7 :     PartialEq,
      97          13 :     roqoqo_derive::Operate,
      98           2 :     roqoqo_derive::Substitute,
      99             :     roqoqo_derive::OperatePragma,
     100             : )]
     101             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     102             : pub struct PragmaSetStateVector {
     103             :     /// The statevector that is initialized.
     104             :     statevector: Array1<Complex64>,
     105             : }
     106             : 
     107             : #[allow(non_upper_case_globals)]
     108             : const TAGS_PragmaSetStateVector: &[&str; 3] =
     109             :     &["Operation", "PragmaOperation", "PragmaSetStateVector"];
     110             : 
     111             : // Implementing the InvolveQubits trait for PragmaSetStateVector.
     112             : impl InvolveQubits for PragmaSetStateVector {
     113             :     /// Lists all involved qubits (here, all).
     114           1 :     fn involved_qubits(&self) -> InvolvedQubits {
     115           1 :         InvolvedQubits::All
     116           1 :     }
     117             : }
     118             : 
     119             : /// This PRAGMA Operation sets the density matrix of a quantum register.
     120             : ///
     121             : /// The Circuit() module automatically initializes the qubits in the |0> state, so this PRAGMA
     122             : /// operation allows you to set the state of the qubits by setting a density matrix of your choosing.
     123             : ///
     124             : /// # Example
     125             : ///
     126             : /// ```
     127             : /// use ndarray::{array, Array2};
     128             : /// use num_complex::Complex64;
     129             : /// use roqoqo::operations::PragmaSetDensityMatrix;
     130             : ///
     131             : /// let matrix: Array2<Complex64> = array![
     132             : ///    [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
     133             : ///    [Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0)],
     134             : /// ];
     135             : ///
     136             : /// let pragma = PragmaSetDensityMatrix::new(matrix.clone());
     137             : /// ```
     138             : ///
     139             : #[derive(
     140           1 :     Debug,
     141           1 :     Clone,
     142           7 :     PartialEq,
     143          13 :     roqoqo_derive::Operate,
     144           2 :     roqoqo_derive::Substitute,
     145             :     roqoqo_derive::OperatePragma,
     146             : )]
     147             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     148             : pub struct PragmaSetDensityMatrix {
     149             :     /// The density matrix that is initialized.
     150             :     density_matrix: Array2<Complex64>,
     151             : }
     152             : 
     153             : #[allow(non_upper_case_globals)]
     154             : const TAGS_PragmaSetDensityMatrix: &[&str; 3] =
     155             :     &["Operation", "PragmaOperation", "PragmaSetDensityMatrix"];
     156             : 
     157             : // Implementing the InvolveQubits trait for PragmaSetDensityMatrix.
     158             : impl InvolveQubits for PragmaSetDensityMatrix {
     159             :     /// Lists all involved qubits (here, all).
     160           1 :     fn involved_qubits(&self) -> InvolvedQubits {
     161           1 :         InvolvedQubits::All
     162           1 :     }
     163             : }
     164             : 
     165             : /// The repeated gate PRAGMA operation.
     166             : ///
     167             : /// This PRAGMA Operation repeats the next gate in the circuit the given number of times to increase the rate for error mitigation.
     168             : ///
     169             : #[derive(
     170           1 :     Debug,
     171           1 :     Clone,
     172           7 :     PartialEq,
     173          13 :     roqoqo_derive::Operate,
     174           2 :     roqoqo_derive::Substitute,
     175             :     roqoqo_derive::OperatePragma,
     176             : )]
     177             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     178             : pub struct PragmaRepeatGate {
     179             :     /// The number of times the following gate is repeated.
     180             :     repetition_coefficient: usize,
     181             : }
     182             : 
     183             : #[allow(non_upper_case_globals)]
     184             : const TAGS_PragmaRepeatGate: &[&str; 3] = &["Operation", "PragmaOperation", "PragmaRepeatGate"];
     185             : 
     186             : // Implementing the InvolveQubits trait for PragmaRepeatGate.
     187             : impl InvolveQubits for PragmaRepeatGate {
     188             :     /// Lists all involved qubits (here, all).
     189           1 :     fn involved_qubits(&self) -> InvolvedQubits {
     190           1 :         InvolvedQubits::All
     191           1 :     }
     192             : }
     193             : 
     194             : /// The statistical overrotation PRAGMA operation.
     195             : ///
     196             : /// This PRAGMA applies a statistical overrotation to the next rotation gate in the circuit, which
     197             : /// matches the hqslang name in the `gate` parameter of PragmaOverrotation and the involved qubits in `qubits`.
     198             : ///
     199             : /// The applied overrotation corresponds to adding a random number to the rotation angle.
     200             : /// The random number is drawn from a normal distribution with mean `0`
     201             : /// and standard deviation `variance` and is multiplied by the `amplitude`.
     202             : ///
     203             : #[derive(
     204           1 :     Debug,
     205           1 :     Clone,
     206           7 :     PartialEq,
     207          16 :     roqoqo_derive::Operate,
     208           2 :     roqoqo_derive::Substitute,
     209           1 :     roqoqo_derive::InvolveQubits,
     210             :     roqoqo_derive::OperatePragma,
     211           1 :     roqoqo_derive::OperateMultiQubit,
     212             : )]
     213             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     214             : // #[cfg_attr(feature = "overrotate")]
     215             : pub struct PragmaOverrotation {
     216             :     /// The unique hqslang name of the gate to overrotate.
     217             :     gate_hqslang: String,
     218             :     /// The qubits of the gate to overrotate.
     219             :     qubits: Vec<usize>,
     220             :     /// The amplitude the random number is multiplied by.
     221             :     amplitude: f64,
     222             :     /// The standard deviation of the normal distribution the random number is drawn from.
     223             :     variance: f64,
     224             : }
     225             : 
     226             : #[allow(non_upper_case_globals)]
     227             : const TAGS_PragmaOverrotation: &[&str; 4] = &[
     228             :     "Operation",
     229             :     "MultiQubitOperation",
     230             :     "PragmaOperation",
     231             :     "PragmaOverrotation",
     232             : ];
     233             : 
     234             : /// This PRAGMA Operation boosts noise and overrotations in the circuit.
     235             : ///
     236             : #[derive(
     237           1 :     Debug,
     238           1 :     Clone,
     239           7 :     PartialEq,
     240          14 :     roqoqo_derive::Operate,
     241           1 :     roqoqo_derive::Substitute,
     242             :     roqoqo_derive::OperatePragma,
     243             : )]
     244             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     245             : pub struct PragmaBoostNoise {
     246             :     /// The coefficient by which the noise is boosted, i.e. the number by which the gate time is multiplied.
     247             :     noise_coefficient: CalculatorFloat,
     248             : }
     249             : 
     250             : #[allow(non_upper_case_globals)]
     251             : const TAGS_PragmaBoostNoise: &[&str; 3] = &["Operation", "PragmaOperation", "PragmaBoostNoise"];
     252             : 
     253             : // Implementing the InvolveQubits trait for PragmaBoostNoise.
     254             : impl InvolveQubits for PragmaBoostNoise {
     255             :     /// Lists all involved qubits (here, none).
     256           2 :     fn involved_qubits(&self) -> InvolvedQubits {
     257           2 :         InvolvedQubits::None
     258           2 :     }
     259             : }
     260             : 
     261             : /// This PRAGMA Operation signals the STOP of a parallel execution block.
     262             : ///
     263             : #[derive(
     264           1 :     Debug,
     265           1 :     Clone,
     266           7 :     PartialEq,
     267           1 :     roqoqo_derive::InvolveQubits,
     268          14 :     roqoqo_derive::Operate,
     269           4 :     roqoqo_derive::Substitute,
     270           1 :     roqoqo_derive::OperateMultiQubit,
     271             :     roqoqo_derive::OperatePragma,
     272             : )]
     273             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     274             : pub struct PragmaStopParallelBlock {
     275             :     /// The qubits involved in parallel execution block.
     276             :     qubits: Vec<usize>,
     277             :     /// The time for the execution of the block in seconds.
     278             :     execution_time: CalculatorFloat,
     279             : }
     280             : 
     281             : #[allow(non_upper_case_globals)]
     282             : const TAGS_PragmaStopParallelBlock: &[&str; 4] = &[
     283             :     "Operation",
     284             :     "MultiQubitOperation",
     285             :     "PragmaOperation",
     286             :     "PragmaStopParallelBlock",
     287             : ];
     288             : 
     289             : /// The global phase PRAGMA operation.
     290             : ///
     291             : /// This PRAGMA Operation signals that the quantum register picks up a global phase,
     292             : /// i.e. it provides information that there is a global phase to be considered.
     293             : ///
     294             : #[derive(
     295           1 :     Debug,
     296           1 :     Clone,
     297           7 :     PartialEq,
     298          13 :     roqoqo_derive::Operate,
     299           1 :     roqoqo_derive::Substitute,
     300             :     roqoqo_derive::OperatePragma,
     301             : )]
     302             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     303             : pub struct PragmaGlobalPhase {
     304             :     /// The picked up global phase.
     305             :     phase: CalculatorFloat,
     306             : }
     307             : 
     308             : #[allow(non_upper_case_globals)]
     309             : const TAGS_PragmaGlobalPhase: &[&str; 3] = &["Operation", "PragmaOperation", "PragmaGlobalPhase"];
     310             : 
     311             : // Implementing the InvolveQubits trait for PragmaGlobalPhase.
     312             : impl InvolveQubits for PragmaGlobalPhase {
     313             :     /// Lists all involved qubits (here, none).
     314           1 :     fn involved_qubits(&self) -> InvolvedQubits {
     315           1 :         InvolvedQubits::None
     316           1 :     }
     317             : }
     318             : 
     319             : /// This PRAGMA Operation makes the quantum hardware wait a given amount of time.
     320             : ///
     321             : /// This PRAGMA Operation is used for error mitigation reasons, for instance.
     322             : /// It can be used to boost the noise on the qubits since it gets worse with time.
     323             : ///
     324             : #[derive(
     325           1 :     Debug,
     326           1 :     Clone,
     327           7 :     PartialEq,
     328           1 :     roqoqo_derive::InvolveQubits,
     329          14 :     roqoqo_derive::Operate,
     330           4 :     roqoqo_derive::Substitute,
     331           1 :     roqoqo_derive::OperateMultiQubit,
     332             :     roqoqo_derive::OperatePragma,
     333             : )]
     334             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     335             : pub struct PragmaSleep {
     336             :     /// The qubits involved in the sleep block.
     337             :     qubits: Vec<usize>,
     338             :     /// Time for the execution of the operation in seconds.
     339             :     sleep_time: CalculatorFloat,
     340             : }
     341             : 
     342             : #[allow(non_upper_case_globals)]
     343             : const TAGS_PragmaSleep: &[&str; 4] = &[
     344             :     "Operation",
     345             :     "MultiQubitOperation",
     346             :     "PragmaOperation",
     347             :     "PragmaSleep",
     348             : ];
     349             : 
     350             : /// This PRAGMA Operation resets the chosen qubit to the zero state.
     351             : ///
     352             : #[derive(
     353           1 :     Debug,
     354           1 :     Clone,
     355           7 :     PartialEq,
     356           1 :     roqoqo_derive::InvolveQubits,
     357          13 :     roqoqo_derive::Operate,
     358           2 :     roqoqo_derive::Substitute,
     359           1 :     roqoqo_derive::OperateSingleQubit,
     360             :     roqoqo_derive::OperatePragma,
     361             : )]
     362             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     363             : pub struct PragmaActiveReset {
     364             :     /// The qubit to be reset.
     365             :     qubit: usize,
     366             : }
     367             : 
     368             : #[allow(non_upper_case_globals)]
     369             : const TAGS_PragmaActiveReset: &[&str; 4] = &[
     370             :     "Operation",
     371             :     "SingleQubitOperation",
     372             :     "PragmaOperation",
     373             :     "PragmaActiveReset",
     374             : ];
     375             : 
     376             : /// This PRAGMA Operation signals the START of a decomposition block.
     377             : ///
     378             : #[derive(
     379           1 :     Debug,
     380           2 :     Clone,
     381           7 :     PartialEq,
     382           1 :     roqoqo_derive::InvolveQubits,
     383          13 :     roqoqo_derive::Operate,
     384           1 :     roqoqo_derive::OperateMultiQubit,
     385             :     roqoqo_derive::OperatePragma,
     386             : )]
     387             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     388             : pub struct PragmaStartDecompositionBlock {
     389             :     /// The qubits involved in the decomposition block.
     390             :     qubits: Vec<usize>,
     391             :     /// The reordering dictionary of the block.
     392             :     reordering_dictionary: HashMap<usize, usize>,
     393             : }
     394             : 
     395             : #[allow(non_upper_case_globals)]
     396             : const TAGS_PragmaStartDecompositionBlock: &[&str; 4] = &[
     397             :     "Operation",
     398             :     "MultiQubitOperation",
     399             :     "PragmaOperation",
     400             :     "PragmaStartDecompositionBlock",
     401             : ];
     402             : 
     403             : /// Substitute trait allowing to replace symbolic parameters and to perform qubit mappings.
     404             : impl Substitute for PragmaStartDecompositionBlock {
     405             :     /// Remaps qubits in clone of the operation.
     406           1 :     fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
     407           1 :         let mut new_qubits: Vec<usize> = Vec::new();
     408           2 :         for q in &self.qubits {
     409           2 :             new_qubits.push(*mapping.get(q).ok_or(Err("")).map_err(
     410           2 :                 |_x: std::result::Result<&usize, &str>| RoqoqoError::QubitMappingError {
     411           0 :                     qubit: *q,
     412           2 :                 },
     413           2 :             )?)
     414             :         }
     415             : 
     416           1 :         let mut mutable_reordering: HashMap<usize, usize> = HashMap::new();
     417           3 :         for (old_qubit, new_qubit) in self.reordering_dictionary.clone() {
     418           2 :             let old_remapped = *mapping.get(&old_qubit).ok_or(Err("")).map_err(
     419           2 :                 |_x: std::result::Result<&usize, &str>| RoqoqoError::QubitMappingError {
     420           0 :                     qubit: old_qubit,
     421           2 :                 },
     422           2 :             )?;
     423           2 :             let new_remapped = *mapping.get(&new_qubit).ok_or(Err("")).map_err(
     424           2 :                 |_x: std::result::Result<&usize, &str>| RoqoqoError::QubitMappingError {
     425           0 :                     qubit: new_qubit,
     426           2 :                 },
     427           2 :             )?;
     428           2 :             mutable_reordering.insert(old_remapped, new_remapped);
     429             :         }
     430             : 
     431           1 :         Ok(PragmaStartDecompositionBlock::new(
     432           1 :             new_qubits,
     433           1 :             mutable_reordering,
     434           1 :         ))
     435           1 :     }
     436             : 
     437             :     /// Substitutes symbolic parameters in clone of the operation.
     438           1 :     fn substitute_parameters(&self, _calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
     439           1 :         Ok(self.clone())
     440           1 :     }
     441             : }
     442             : 
     443             : /// This PRAGMA Operation signals the STOP of a decomposition block.
     444             : ///
     445             : #[derive(
     446           1 :     Debug,
     447           1 :     Clone,
     448           7 :     PartialEq,
     449           1 :     roqoqo_derive::InvolveQubits,
     450          13 :     roqoqo_derive::Operate,
     451           3 :     roqoqo_derive::Substitute,
     452           1 :     roqoqo_derive::OperateMultiQubit,
     453             :     roqoqo_derive::OperatePragma,
     454             : )]
     455             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     456             : pub struct PragmaStopDecompositionBlock {
     457             :     /// The qubits involved in the decomposition block.
     458             :     qubits: Vec<usize>,
     459             : }
     460             : 
     461             : #[allow(non_upper_case_globals)]
     462             : const TAGS_PragmaStopDecompositionBlock: &[&str; 4] = &[
     463             :     "Operation",
     464             :     "MultiQubitOperation",
     465             :     "PragmaOperation",
     466             :     "PragmaStopDecompositionBlock",
     467             : ];
     468             : 
     469             : /// The damping PRAGMA noise Operation.
     470             : ///
     471             : /// This PRAGMA Operation applies a pure damping error corresponding to zero temperature environments.
     472             : ///
     473             : #[derive(
     474           1 :     Debug,
     475           2 :     Clone,
     476           8 :     PartialEq,
     477           1 :     roqoqo_derive::InvolveQubits,
     478           1 :     roqoqo_derive::Operate,
     479           3 :     roqoqo_derive::Substitute,
     480           1 :     roqoqo_derive::OperateSingleQubit,
     481             :     roqoqo_derive::OperatePragma,
     482             : )]
     483             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     484             : pub struct PragmaDamping {
     485             :     /// The qubit on which to apply the damping.
     486             :     qubit: usize,
     487             :     /// The time (in seconds) the gate takes to be applied to the qubit on the (simulated) hardware
     488             :     gate_time: CalculatorFloat,
     489             :     /// The error rate of the damping (in 1/second).
     490             :     rate: CalculatorFloat,
     491             : }
     492             : 
     493             : #[allow(non_upper_case_globals)]
     494             : const TAGS_PragmaDamping: &[&str; 6] = &[
     495             :     "Operation",
     496             :     "SingleQubitOperation",
     497             :     "PragmaOperation",
     498             :     "PragmaNoiseOperation",
     499             :     "PragmaNoiseProbaOperation",
     500             :     "PragmaDamping",
     501             : ];
     502             : 
     503             : /// OperatePragmaNoise trait creating necessary functions for a PRAGMA noise Operation.
     504             : impl OperatePragmaNoise for PragmaDamping {
     505             :     /// Returns the superoperator matrix of the operation.
     506           1 :     fn superoperator(&self) -> Result<Array2<f64>, RoqoqoError> {
     507           1 :         let prob: f64 = f64::try_from(self.probability())?;
     508           1 :         let sqrt: f64 = (1.0 - prob).sqrt();
     509           1 : 
     510           1 :         Ok(array![
     511           1 :             [1.0, 0.0, 0.0, prob],
     512           1 :             [0.0, sqrt, 0.0, 0.0],
     513           1 :             [0.0, 0.0, sqrt, 0.0],
     514           1 :             [0.0, 0.0, 0.0, 1.0 - prob],
     515           1 :         ])
     516           1 :     }
     517             : 
     518             :     /// Returns the gate to the power of `power`.
     519           1 :     fn powercf(&self, power: CalculatorFloat) -> Self {
     520           1 :         let mut new = self.clone();
     521           1 :         new.gate_time = power * self.gate_time.clone();
     522           1 :         new
     523           1 :     }
     524             : }
     525             : 
     526             : /// OperatePragmaNoiseProba trait creating necessary functions for a PRAGMA noise Operation.
     527             : impl OperatePragmaNoiseProba for PragmaDamping {
     528             :     /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`.
     529           3 :     fn probability(&self) -> CalculatorFloat {
     530           3 :         let prob: CalculatorFloat =
     531           3 :             ((self.gate_time.clone() * self.rate.clone() * (-2.0)).exp() * (-1.0) + 1.0) * 0.5;
     532           3 :         prob
     533           3 :     }
     534             : }
     535             : 
     536             : /// The depolarising PRAGMA noise Operation.
     537             : ///
     538             : /// This PRAGMA Operation applies a depolarising error corresponding to infinite temperature environments.
     539             : ///
     540             : #[derive(
     541           1 :     Debug,
     542           2 :     Clone,
     543           8 :     PartialEq,
     544           1 :     roqoqo_derive::InvolveQubits,
     545           1 :     roqoqo_derive::Operate,
     546           3 :     roqoqo_derive::Substitute,
     547           1 :     roqoqo_derive::OperateSingleQubit,
     548             :     roqoqo_derive::OperatePragma,
     549             : )]
     550             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     551             : pub struct PragmaDepolarising {
     552             :     /// The qubit on which to apply the depolarising.
     553             :     qubit: usize,
     554             :     /// The time (in seconds) the gate takes to be applied to the qubit on the (simulated) hardware
     555             :     gate_time: CalculatorFloat,
     556             :     /// The error rate of the depolarisation (in 1/second).
     557             :     rate: CalculatorFloat,
     558             : }
     559             : 
     560             : #[allow(non_upper_case_globals)]
     561             : const TAGS_PragmaDepolarising: &[&str; 6] = &[
     562             :     "Operation",
     563             :     "SingleQubitOperation",
     564             :     "PragmaOperation",
     565             :     "PragmaNoiseOperation",
     566             :     "PragmaNoiseProbaOperation",
     567             :     "PragmaDepolarising",
     568             : ];
     569             : 
     570             : /// OperatePragmaNoise trait creating necessary functions for a PRAGMA noise Operation.
     571             : impl OperatePragmaNoise for PragmaDepolarising {
     572             :     /// Returns the superoperator matrix of the operation.
     573           1 :     fn superoperator(&self) -> Result<Array2<f64>, RoqoqoError> {
     574           1 :         let gate_time: f64 = f64::try_from(self.gate_time.clone())?;
     575           1 :         let rate: f64 = f64::try_from(self.rate.clone())?;
     576             : 
     577           1 :         let pre_exp: f64 = -1.0 * gate_time * rate;
     578           1 :         let prob: f64 = (3.0 / 4.0) * (1.0 - pre_exp.exp());
     579           1 :         let proba1: f64 = 1.0 - (2.0 / 3.0) * prob;
     580           1 :         let proba2: f64 = 1.0 - (4.0 / 3.0) * prob;
     581           1 :         let proba3: f64 = (2.0 / 3.0) * prob;
     582           1 : 
     583           1 :         Ok(array![
     584           1 :             [proba1, 0.0, 0.0, proba3],
     585           1 :             [0.0, proba2, 0.0, 0.0],
     586           1 :             [0.0, 0.0, proba2, 0.0],
     587           1 :             [proba3, 0.0, 0.0, proba1],
     588           1 :         ])
     589           1 :     }
     590             : 
     591             :     /// Returns the gate to the power of `power`.
     592           1 :     fn powercf(&self, power: CalculatorFloat) -> Self {
     593           1 :         let mut new = self.clone();
     594           1 :         new.gate_time = power * self.gate_time.clone();
     595           1 :         new
     596           1 :     }
     597             : }
     598             : 
     599             : /// OperatePragmaNoiseProba trait creating necessary functions for a PRAGMA noise Operation.
     600             : impl OperatePragmaNoiseProba for PragmaDepolarising {
     601             :     /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`.
     602           1 :     fn probability(&self) -> CalculatorFloat {
     603           1 :         let prob: CalculatorFloat =
     604           1 :             ((self.gate_time.clone() * self.rate.clone() * (-1.0)).exp() * (-1.0) + 1.0) * 0.75;
     605           1 :         prob
     606           1 :     }
     607             : }
     608             : 
     609             : /// The dephasing PRAGMA noise Operation.
     610             : ///
     611             : /// This PRAGMA Operation applies a pure dephasing error.
     612             : ///
     613             : #[derive(
     614           1 :     Debug,
     615           2 :     Clone,
     616           8 :     PartialEq,
     617           1 :     roqoqo_derive::InvolveQubits,
     618           1 :     roqoqo_derive::Operate,
     619           3 :     roqoqo_derive::Substitute,
     620           1 :     roqoqo_derive::OperateSingleQubit,
     621             :     roqoqo_derive::OperatePragma,
     622             : )]
     623             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     624             : pub struct PragmaDephasing {
     625             :     /// The qubit on which to apply the dephasing.
     626             :     qubit: usize,
     627             :     /// The time (in seconds) the gate takes to be applied to the qubit on the (simulated) hardware
     628             :     gate_time: CalculatorFloat,
     629             :     /// The error rate of the dephasing (in 1/second).
     630             :     rate: CalculatorFloat,
     631             : }
     632             : 
     633             : #[allow(non_upper_case_globals)]
     634             : const TAGS_PragmaDephasing: &[&str; 6] = &[
     635             :     "Operation",
     636             :     "SingleQubitOperation",
     637             :     "PragmaOperation",
     638             :     "PragmaNoiseOperation",
     639             :     "PragmaNoiseProbaOperation",
     640             :     "PragmaDephasing",
     641             : ];
     642             : 
     643             : /// OperatePragmaNoise trait creating necessary functions for a PRAGMA noise Operation.
     644             : impl OperatePragmaNoise for PragmaDephasing {
     645             :     /// Returns the superoperator matrix of the operation.
     646           1 :     fn superoperator(&self) -> Result<Array2<f64>, RoqoqoError> {
     647           1 :         let gate_time: f64 = f64::try_from(self.gate_time.clone())?;
     648           1 :         let rate: f64 = f64::try_from(self.rate.clone())?;
     649             : 
     650           1 :         let pre_exp: f64 = -2.0 * gate_time * rate;
     651           1 :         let prob: f64 = (1.0 / 2.0) * (1.0 - pre_exp.exp());
     652           1 : 
     653           1 :         Ok(array![
     654           1 :             [1.0, 0.0, 0.0, 0.0],
     655           1 :             [0.0, 1.0 - 2.0 * prob, 0.0, 0.0],
     656           1 :             [0.0, 0.0, 1.0 - 2.0 * prob, 0.0],
     657           1 :             [0.0, 0.0, 0.0, 1.0],
     658           1 :         ])
     659           1 :     }
     660             : 
     661             :     /// Returns the gate to the power of `power`.
     662           1 :     fn powercf(&self, power: CalculatorFloat) -> Self {
     663           1 :         let mut new = self.clone();
     664           1 :         new.gate_time = power * self.gate_time.clone();
     665           1 :         new
     666           1 :     }
     667             : }
     668             : 
     669             : /// OperatePragmaNoiseProba trait creating necessary functions for a PRAGMA noise Operation.
     670             : impl OperatePragmaNoiseProba for PragmaDephasing {
     671             :     /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time` and `rate`.
     672           1 :     fn probability(&self) -> CalculatorFloat {
     673           1 :         let prob: CalculatorFloat =
     674           1 :             ((self.gate_time.clone() * self.rate.clone() * (-2.0)).exp() * (-1.0) + 1.0) * 0.5;
     675           1 :         prob
     676           1 :     }
     677             : }
     678             : 
     679             : /// The random noise PRAGMA operation.
     680             : ///
     681             : /// This PRAGMA Operation applies a stochastically unravelled combination of dephasing and depolarising.
     682             : ///
     683             : #[derive(
     684           1 :     Debug,
     685           2 :     Clone,
     686           8 :     PartialEq,
     687           1 :     roqoqo_derive::InvolveQubits,
     688           1 :     roqoqo_derive::Operate,
     689           3 :     roqoqo_derive::Substitute,
     690           1 :     roqoqo_derive::OperateSingleQubit,
     691             :     roqoqo_derive::OperatePragma,
     692             : )]
     693             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     694             : pub struct PragmaRandomNoise {
     695             :     /// The qubit the PRAGMA Operation is applied to.
     696             :     qubit: usize,
     697             :     /// The time (in seconds) the gate takes to be applied to the qubit on the (simulated) hardware
     698             :     gate_time: CalculatorFloat,
     699             :     /// The error rate of the depolarisation (in 1/second).
     700             :     depolarising_rate: CalculatorFloat,
     701             :     /// The error rate of the dephasing (in 1/second).
     702             :     dephasing_rate: CalculatorFloat,
     703             : }
     704             : 
     705             : #[allow(non_upper_case_globals)]
     706             : const TAGS_PragmaRandomNoise: &[&str; 6] = &[
     707             :     "Operation",
     708             :     "SingleQubitOperation",
     709             :     "PragmaOperation",
     710             :     "PragmaNoiseOperation",
     711             :     "PragmaNoiseProbaOperation",
     712             :     "PragmaRandomNoise",
     713             : ];
     714             : 
     715             : /// OperatePragmaNoise trait creating necessary functions for a PRAGMA noise Operation.
     716             : impl OperatePragmaNoise for PragmaRandomNoise {
     717             :     /// Returns the superoperator matrix of the operation. For the RandomNoise pragma, the superoperator
     718             :     /// is the effective superoperator after averaging over many trajectories: the dephasing superoperator.
     719           1 :     fn superoperator(&self) -> Result<Array2<f64>, RoqoqoError> {
     720           1 :         let gate_time: f64 = f64::try_from(self.gate_time.clone())?;
     721           1 :         let rate: f64 = f64::try_from(self.dephasing_rate.clone())?;
     722             : 
     723           1 :         let pre_exp: f64 = -2.0 * gate_time * rate;
     724           1 :         let prob: f64 = (1.0 / 2.0) * (1.0 - pre_exp.exp());
     725           1 : 
     726           1 :         Ok(array![
     727           1 :             [1.0, 0.0, 0.0, 0.0],
     728           1 :             [0.0, 1.0 - 2.0 * prob, 0.0, 0.0],
     729           1 :             [0.0, 0.0, 1.0 - 2.0 * prob, 0.0],
     730           1 :             [0.0, 0.0, 0.0, 1.0],
     731           1 :         ])
     732           1 :     }
     733             : 
     734             :     /// Returns the gate to the power of `power`.
     735           1 :     fn powercf(&self, power: CalculatorFloat) -> Self {
     736           1 :         let mut new = self.clone();
     737           1 :         new.gate_time = power * self.gate_time.clone();
     738           1 :         new
     739           1 :     }
     740             : }
     741             : 
     742             : /// OperatePragmaNoiseProba trait creating necessary functions for a PRAGMA noise Operation.
     743             : impl OperatePragmaNoiseProba for PragmaRandomNoise {
     744             :     /// Returns the probability of the noise gate affecting the qubit, based on its `gate_time`, `depolarising_rate` and `dephasing_rate`.
     745           1 :     fn probability(&self) -> CalculatorFloat {
     746           1 :         let rates = [
     747           1 :             self.depolarising_rate.clone() / 4.0,
     748           1 :             self.depolarising_rate.clone() / 4.0,
     749           1 :             (self.depolarising_rate.clone() / 4.0) + self.dephasing_rate.clone(),
     750           1 :         ];
     751           1 :         (rates[0].clone() + &rates[1] + &rates[2]) * &self.gate_time
     752           1 :     }
     753             : }
     754             : 
     755             : /// The general noise PRAGMA operation.
     756             : ///
     757             : /// This PRAGMA operation applies a noise term according to the given rates.
     758             : /// The rates are represented by a 3x3 matrix:
     759             : /// $$ M = \begin{pmatrix}
     760             : /// a & b & c \\\\
     761             : /// d & e & f \\\\
     762             : /// g & h & j \\\\
     763             : /// \end{pmatrix} $$
     764             : /// where the coefficients correspond to the following summands
     765             : /// expanded from the first term of the non-coherent part of the Lindblad equation:
     766             : ///     $$ \frac{d}{dt}\rho = \sum_{i,j=0}^{2} M_{i,j} L_{i} \rho L_{j}^{\dagger} - \frac{1}{2} \{ L_{j}^{\dagger} L_i, \rho \} \\\\
     767             : ///         L_0 = \sigma^{+} \\\\
     768             : ///         L_1 = \sigma^{-} \\\\
     769             : ///         L_3 = \sigma^{z}
     770             : ///     $$
     771             : /// result{sigma_z, sigma_minus} = sigma_z (x) sigma_minus.T - 1/2 * (sigma_minus.T * sigma_z) (x) 1 - 1/2 * 1 (x) (sigma_minus.T * sigma_z).T
     772             : ///
     773             : /// Applying the Pragma with a given `gate_time` corresponds to applying the full time-evolution under the Lindblad equation for `gate_time` time.
     774             : ///
     775             : /// # Example
     776             : ///
     777             : /// ```
     778             : /// use ndarray::{array, Array2};
     779             : /// use roqoqo::operations::PragmaGeneralNoise;
     780             : /// use qoqo_calculator::CalculatorFloat;
     781             : ///
     782             : /// let rates: Array2<f64> = array![
     783             : ///    [
     784             : ///         1.0,
     785             : ///         0.0,
     786             : ///         0.0
     787             : ///     ],
     788             : ///     [
     789             : ///         0.0,
     790             : ///         1.0,
     791             : ///         0.0
     792             : ///     ],
     793             : ///     [
     794             : ///         0.0,
     795             : ///         0.0,
     796             : ///         1.0
     797             : ///     ],
     798             : /// ];
     799             : /// let pragma = PragmaGeneralNoise::new(
     800             : ///     0,
     801             : ///     CalculatorFloat::from(0.005),
     802             : ///     rates.clone(),
     803             : /// );
     804             : /// ```
     805             : /// That will result into $.
     806             : ///
     807             : #[derive(
     808           1 :     Debug,
     809           1 :     Clone,
     810           7 :     PartialEq,
     811           1 :     roqoqo_derive::InvolveQubits,
     812          16 :     roqoqo_derive::Operate,
     813           3 :     roqoqo_derive::Substitute,
     814           1 :     roqoqo_derive::OperateSingleQubit,
     815             :     roqoqo_derive::OperatePragma,
     816             : )]
     817             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     818             : pub struct PragmaGeneralNoise {
     819             :     /// The qubit the PRAGMA Operation is applied to.
     820             :     qubit: usize,
     821             :     /// The time (in seconds) the gate takes to be applied to the qubit on the (simulated) hardware
     822             :     gate_time: CalculatorFloat,
     823             :     /// The rates representing the general noise matrix M (a 3x3 matrix).
     824             :     rates: Array2<f64>,
     825             : }
     826             : 
     827             : #[allow(non_upper_case_globals)]
     828             : const TAGS_PragmaGeneralNoise: &[&str; 5] = &[
     829             :     "Operation",
     830             :     "SingleQubitOperation",
     831             :     "PragmaOperation",
     832             :     "PragmaNoiseOperation",
     833             :     "PragmaGeneralNoise",
     834             : ];
     835             : 
     836             : // Collection of superoperators that appear in the Lindblad equation for a single qubit/spin with
     837             : // a basis of the form 0: sigma+ 1:sigma- 2: sigmaz
     838             : const PGN_SUPEROP: [[[[f64; 4]; 4]; 3]; 3] = [
     839             :     [
     840             :         // sigma+ sigma+
     841             :         [
     842             :             [0., 0., 0., 4.],
     843             :             [0., -2., 0., 0.],
     844             :             [0., 0., -2., 0.],
     845             :             [0., 0., 0., -4.],
     846             :         ],
     847             :         // sigma+ sigma-
     848             :         [
     849             :             [0., 0., 0., 0.],
     850             :             [0., 0., 4., 0.],
     851             :             [0., 0., 0., 0.],
     852             :             [0., 0., 0., 0.],
     853             :         ],
     854             :         // sigma+ sigmaz
     855             :         [
     856             :             [0., 0., 1., 0.],
     857             :             [-1., 0., 0., -3.],
     858             :             [0., 0., 0., 0.],
     859             :             [0., 0., 0., -1.],
     860             :         ],
     861             :     ],
     862             :     [
     863             :         // sigma- sigma+
     864             :         [
     865             :             [0., 0., 0., 0.],
     866             :             [0., 0., 0., 0.],
     867             :             [0., 4., 0., 0.],
     868             :             [0., 0., 0., 0.],
     869             :         ],
     870             :         // sigma- sigma-
     871             :         [
     872             :             [-4., 0., 0., 0.],
     873             :             [0., -2., 0., 0.],
     874             :             [0., 0., -2., 0.],
     875             :             [4., 0., 0., 0.],
     876             :         ],
     877             :         // sigma- sigmaz
     878             :         [
     879             :             [0., 1., 0., 0.],
     880             :             [0., 0., 0., 0.],
     881             :             [3., 0., 0., 1.],
     882             :             [0., -1., 0., 0.],
     883             :         ],
     884             :     ],
     885             :     [
     886             :         //  sigmaz sigma+
     887             :         [
     888             :             [0., 1., 0., 0.],
     889             :             [0., 0., 0., 0.],
     890             :             [-1., 0., 0., -3.],
     891             :             [0., -1., 0., 0.],
     892             :         ],
     893             :         // sigmaz sigma-
     894             :         [
     895             :             [0., 0., 1., 0.],
     896             :             [3., 0., 0., 1.],
     897             :             [0., 0., 0., 0.],
     898             :             [0., 0., -1., 0.],
     899             :         ],
     900             :         // sigmaz sigmaz
     901             :         [
     902             :             [0., 0., 0., 0.],
     903             :             [0., -2., 0., 0.],
     904             :             [0., 0., 0., 0.],
     905             :             [0., 0., 0., -2.],
     906             :         ],
     907             :     ],
     908             : ];
     909             : 
     910             : /// OperatePragmaNoise trait creating necessary functions for a PRAGMA noise Operation.
     911             : impl OperatePragmaNoise for PragmaGeneralNoise {
     912           1 :     fn superoperator(&self) -> Result<Array2<f64>, RoqoqoError> {
     913           1 :         let gate_time: f64 = f64::try_from(self.gate_time.clone())?;
     914             :         // Creating the superoperator that propagates the density matrix in vector form scaled by rate and time
     915           1 :         let mut superop = Matrix4::<f64>::default();
     916           3 :         for (i, row) in PGN_SUPEROP.iter().enumerate() {
     917           9 :             for (j, op) in row.iter().clone().enumerate() {
     918           9 :                 let tmp_superop: Matrix4<f64> = (*op).into();
     919           9 :                 superop += gate_time * self.rates[(i, j)] * tmp_superop;
     920           9 :             }
     921             :         }
     922             :         // Integrate superoperator for infinitesimal time to get superoperator for given rate and gate-time
     923             :         // Use exponential
     924           1 :         let mut exp_superop: Matrix4<f64> = superop.exp();
     925           1 :         // transpose because NAlgebra matrix iter is column major
     926           1 :         exp_superop.transpose_mut();
     927           1 :         let mut tmp_iter = exp_superop.iter();
     928           1 :         // convert to ndarray.
     929          16 :         let array: Array2<f64> = Array::from_shape_simple_fn((4, 4), || *tmp_iter.next().unwrap());
     930           1 : 
     931           1 :         Ok(array)
     932           1 :     }
     933             : 
     934             :     /// Returns the gate to the power of `power`.
     935           0 :     fn powercf(&self, power: CalculatorFloat) -> Self {
     936           0 :         let mut new = self.clone();
     937           0 :         new.gate_time = power * self.gate_time.clone();
     938           0 :         new
     939           0 :     }
     940             : }
     941             : 
     942             : /// The conditional PRAGMA operation.
     943             : ///
     944             : /// This PRAGMA executes a circuit when the condition bit/bool stored in a [crate::registers::BitRegister] is true.
     945             : ///
     946          18 : #[derive(Debug, Clone, PartialEq, roqoqo_derive::Operate, roqoqo_derive::OperatePragma)]
     947             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
     948             : pub struct PragmaConditional {
     949             :     /// The name of the [crate::registers::BitRegister] containting the condition bool value.
     950             :     condition_register: String,
     951             :     /// The index in the [crate::registers::BitRegister] containting the condition bool value.
     952             :     condition_index: usize,
     953             :     /// The circuit executed if the condition is met.
     954             :     circuit: Circuit,
     955             : }
     956             : 
     957             : #[allow(non_upper_case_globals)]
     958             : const TAGS_PragmaConditional: &[&str; 3] = &["Operation", "PragmaOperation", "PragmaConditional"];
     959             : 
     960             : // Implementing the InvolveQubits trait for PragmaConditional.
     961             : impl InvolveQubits for PragmaConditional {
     962             :     /// Lists all involved qubits.
     963           1 :     fn involved_qubits(&self) -> InvolvedQubits {
     964           1 :         self.circuit.involved_qubits()
     965           1 :     }
     966             : }
     967             : 
     968             : /// Substitute trait allowing to replace symbolic parameters and to perform qubit mappings.
     969             : impl Substitute for PragmaConditional {
     970             :     /// Remaps qubits in clone of the operation.
     971           2 :     fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
     972           2 :         let new_circuit = self.circuit.remap_qubits(mapping).unwrap();
     973           2 :         Ok(PragmaConditional::new(
     974           2 :             self.condition_register.clone(),
     975           2 :             self.condition_index,
     976           2 :             new_circuit,
     977           2 :         ))
     978           2 :     }
     979             : 
     980             :     /// Substitutes symbolic parameters in clone of the operation.
     981           1 :     fn substitute_parameters(&self, calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
     982           1 :         let new_circuit = self.circuit.substitute_parameters(calculator).unwrap();
     983           1 :         Ok(PragmaConditional::new(
     984           1 :             self.condition_register.clone(),
     985           1 :             self.condition_index,
     986           1 :             new_circuit,
     987           1 :         ))
     988           1 :     }
     989             : }
     990             : 
     991             : /// A wrapper around backend specific PRAGMA operations capable of changing a device.
     992             : ///
     993             : /// This PRAGMA is a thin wrapper around device specific operations that can change
     994             : /// device properties.
     995             : ///
     996             : /// # NOTE
     997             : ///
     998             : /// Since this PRAGMA uses serde and bincode to store a representation of the wrapped
     999             : /// operation internally it is only available when roqoqo is built with the `serialize` feature
    1000           0 : #[derive(Debug, Clone, PartialEq, roqoqo_derive::OperatePragma)]
    1001             : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
    1002             : pub struct PragmaChangeDevice {
    1003             :     /// The tags of the wrapped operation.
    1004             :     pub wrapped_tags: Vec<String>,
    1005             :     /// The hqslang name of the wrapped operation.
    1006             :     pub wrapped_hqslang: String,
    1007             :     /// Binary representation of the wrapped operation using serde and bincode.
    1008             :     pub wrapped_operation: Vec<u8>,
    1009             : }
    1010             : #[cfg_attr(feature = "dynamic", typetag::serde)]
    1011             : impl Operate for PragmaChangeDevice {
    1012           0 :     fn tags(&self) -> &'static [&'static str] {
    1013           0 :         TAGS_PragmaChangeDevice
    1014           0 :     }
    1015           0 :     fn hqslang(&self) -> &'static str {
    1016           0 :         "PragmaChangeDevice"
    1017           0 :     }
    1018           0 :     fn is_parametrized(&self) -> bool {
    1019           0 :         false
    1020           0 :     }
    1021             : }
    1022             : impl PragmaChangeDevice {
    1023             :     #[cfg(feature = "serialize")]
    1024             :     pub fn new<T>(wrapped_pragma: &T) -> Result<Self, RoqoqoError>
    1025             :     where
    1026             :         T: Operate,
    1027             :         T: Serialize,
    1028             :     {
    1029             :         Ok(Self {
    1030             :             wrapped_tags: wrapped_pragma
    1031             :                 .tags()
    1032             :                 .iter()
    1033             :                 .map(|x| x.to_string())
    1034             :                 .collect(),
    1035             :             wrapped_hqslang: wrapped_pragma.hqslang().to_string(),
    1036             :             wrapped_operation: serialize(wrapped_pragma).map_err(|err| {
    1037             :                 RoqoqoError::SerializationError {
    1038             :                     msg: format!("{:?}", err),
    1039             :                 }
    1040             :             })?,
    1041             :         })
    1042             :     }
    1043             : }
    1044             : #[allow(non_upper_case_globals)]
    1045             : const TAGS_PragmaChangeDevice: &[&str; 3] = &["Operation", "PragmaOperation", "PragmaChangeDevice"];
    1046             : 
    1047             : // Implementing the InvolveQubits trait for PragmaConditional.
    1048             : impl InvolveQubits for PragmaChangeDevice {
    1049             :     /// Lists all involved qubits.
    1050           0 :     fn involved_qubits(&self) -> InvolvedQubits {
    1051           0 :         InvolvedQubits::All
    1052           0 :     }
    1053             : }
    1054             : 
    1055             : /// Substitute trait allowing to replace symbolic parameters and to perform qubit mappings.
    1056             : impl Substitute for PragmaChangeDevice {
    1057             :     /// Remaps qubits in clone of the operation.
    1058             :     /// This is not supported  for PragmaChangeDevice and should throw and error when a non-trivial remapping
    1059             :     /// is used
    1060           0 :     fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
    1061           0 :         match mapping.iter().find(|(x, y)| x != y) {
    1062           0 :             Some((x, _)) => Err(RoqoqoError::QubitMappingError { qubit: *x }),
    1063           0 :             None => Ok(self.clone()),
    1064             :         }
    1065           0 :     }
    1066             : 
    1067             :     #[allow(unused_variables)]
    1068             :     /// Substitutes symbolic parameters in clone of the operation.
    1069           0 :     fn substitute_parameters(&self, calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
    1070           0 :         Ok(self.clone())
    1071           0 :     }
    1072             : }

Generated by: LCOV version 1.13