LCOV - code coverage report
Current view: top level - src/measurements - basis_rotation_measurement.rs (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 145 147 98.6 %
Date: 2021-11-09 13:25:48 Functions: 9 9 100.0 %

          Line data    Source code
       1             : // Copyright © 2021 HQS Quantum Simulations GmbH. All Rights Reserved.
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
       4             : // in compliance with the License. You may obtain a copy of the License at
       5             : //
       6             : //     http://www.apache.org/licenses/LICENSE-2.0
       7             : //
       8             : // Unless required by applicable law or agreed to in writing, software distributed under the
       9             : // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
      10             : // express or implied. See the License for the specific language governing permissions and
      11             : // limitations under the License.
      12             : 
      13             : use super::*;
      14             : use ndarray::{Array1, Array2};
      15             : #[cfg(feature = "serialize")]
      16             : use serde::{Deserialize, Serialize};
      17             : 
      18             : /// Collected information for executing a basis rotation measurement.
      19           2 : #[derive(Debug, PartialEq, Clone)]
      20             : #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
      21             : pub struct BasisRotation {
      22             :     /// Constant Circuit that is executed before each Circuit in circuits.
      23             :     pub constant_circuit: Option<Circuit>,
      24             :     /// Collection of quantum circuits for the separate basis rotations.
      25             :     pub circuits: Vec<Circuit>,
      26             :     /// Additional input information required for measurement.
      27             :     pub input: BasisRotationInput,
      28             : }
      29             : 
      30             : impl Measure for BasisRotation {
      31             :     /// Returns the constant Circuit that is executed before each Circuit in circuits.
      32             :     ///
      33             :     /// # Returns
      34             :     ///
      35             :     /// * `&Option<Circuit` - The constant Circuit (None if not defined).
      36           3 :     fn constant_circuit(&self) -> &Option<Circuit> {
      37           3 :         &self.constant_circuit
      38           3 :     }
      39             : 
      40             :     /// Returns iterator over circuits for measurement.
      41             :     ///
      42             :     /// # Returns
      43             :     ///
      44             :     /// * `Box<dyn Iterator<Item = &'a Circuit> + 'a>` - The quantum circuits.
      45           3 :     fn circuits<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Circuit> + 'a> {
      46           3 :         Box::new(self.circuits.iter())
      47           3 :     }
      48             : 
      49             :     /// Returns clone of Measurement with symbolic parameters replaced.
      50             :     ///
      51             :     /// # Arguments
      52             :     ///
      53             :     /// * `substituted_parameters` - The HashMap containing the substitutions to use in the Circuit.
      54             :     ///
      55             :     /// # Returns
      56             :     ///
      57             :     /// * `Ok(Self)` -  The Circuits with the parameters substituted.
      58             :     /// * `Err(RoqoqoError)` - The subsitution failed.
      59             :     ///
      60           3 :     fn substitute_parameters(
      61           3 :         &self,
      62           3 :         substituted_parameters: HashMap<String, f64>,
      63           3 :     ) -> Result<Self, RoqoqoError> {
      64           3 :         let mut calculator = qoqo_calculator::Calculator::new();
      65           9 :         for (name, val) in substituted_parameters.iter() {
      66           6 :             calculator.set_variable(name, *val)
      67             :         }
      68           3 :         let new_constant_circuit = match &self.constant_circuit {
      69           0 :             None => None,
      70           3 :             Some(c) => Some(c.substitute_parameters(&mut calculator)?),
      71             :         };
      72           2 :         let mut new_circuits = Vec::new();
      73           2 :         for circ in self.circuits.iter() {
      74           2 :             let mut calculator = qoqo_calculator::Calculator::new();
      75           6 :             for (name, val) in substituted_parameters.iter() {
      76           4 :                 calculator.set_variable(name, *val)
      77             :             }
      78           2 :             new_circuits.push(circ.substitute_parameters(&mut calculator)?)
      79             :         }
      80           2 :         Ok(Self {
      81           2 :             constant_circuit: new_constant_circuit,
      82           2 :             circuits: new_circuits,
      83           2 :             input: self.input.clone(),
      84           2 :         })
      85           3 :     }
      86             : }
      87             : 
      88             : impl MeasureExpectationValues for BasisRotation {
      89             :     // TODO add optional device later for use with flipped measurement
      90             :     #[allow(unused_variables)]
      91             :     /// Executes the basis rotation measurement.
      92             :     ///
      93             :     /// # Arguments
      94             :     ///
      95             :     /// * `bit_registers` - The classical bit registers as a HashMap with the register name as key.
      96             :     /// * `float_registers` - The classical float registers as a HashMap with the register name as key.
      97             :     /// * `complex_registers` - The classical complex registers as a HashMap with the register name as key.
      98             :     ///
      99             :     /// # Returns
     100             :     ///
     101             :     /// * `Ok(Some(HashMap<String, f64>))` - The measurement has been evaluated successfully. The HashMap contains the measured expectation values.
     102             :     /// * `Ok(None)` - The measurement did not fail but is incomplete. A new round of measurements is needed
     103             :     /// * `Err([RoqoqoError::BasisRotationMeasurementError])` - An error occured in basis rotation measurement.
     104             :     ///
     105          10 :     fn evaluate(
     106          10 :         &self,
     107          10 :         bit_registers: HashMap<String, BitOutputRegister>,
     108          10 :         float_registers: HashMap<String, FloatOutputRegister>,
     109          10 :         complex_registers: HashMap<String, ComplexOutputRegister>,
     110          10 :     ) -> Result<Option<HashMap<String, f64>>, RoqoqoError> {
     111          10 :         // todo replace with actual input
     112          10 :         let measurement_fidelities = vec![1.0; self.input.number_qubits];
     113          10 : 
     114          10 :         // Setting up measurement correction factors for flipped measurement
     115          10 :         let mut measurement_correction_factors: HashMap<String, Vec<f64>> = HashMap::new();
     116          10 :         let flipped_and_extension: Vec<(bool, &'static str)>;
     117          10 :         if self.input.use_flipped_measurement {
     118             :             // helper vector to iterate over when evaluating the pauli products
     119           3 :             flipped_and_extension = vec![(false, ""), (true, "_flipped")];
     120           6 :             for (name, pauli_product_mask) in self.input.pauli_product_qubit_masks.iter() {
     121           3 :                 let mut measurement_correction_factor: Vec<f64> =
     122           3 :                     (0..self.input.number_pauli_products)
     123           3 :                         .into_iter()
     124           9 :                         .map(|_| 1.0)
     125           3 :                         .collect();
     126          12 :                 for (pp_index, indices) in pauli_product_mask.iter() {
     127           9 :                     if !indices.is_empty() {
     128           9 :                         for i in indices {
     129           9 :                             measurement_correction_factor[*pp_index] *= measurement_fidelities[*i]
     130             :                         }
     131           3 :                     }
     132             :                 }
     133           3 :                 measurement_correction_factors.insert(name.clone(), measurement_correction_factor);
     134             :             }
     135           7 :         } else {
     136           7 :             flipped_and_extension = vec![(false, "")];
     137           7 :         }
     138          10 :         let mut pauli_product_dict: HashMap<String, Array1<f64>> = HashMap::new();
     139          25 :         for (register_name, mask) in self.input.pauli_product_qubit_masks.iter() {
     140          33 :             for (flip_measurement, extension) in flipped_and_extension.iter() {
     141          18 :                 let register = bit_registers
     142          18 :                     .get(&format!("{}{}", register_name.as_str(), extension))
     143          18 :                     .ok_or(RoqoqoError::BasisRotationMeasurementError {
     144          18 :                         msg: format!(
     145          18 :                             "bit register {}{} not found",
     146          18 :                             register_name.as_str(),
     147          18 :                             extension
     148          18 :                         ),
     149          18 :                     })?;
     150          18 :                 let mut single_shot_pauli_products: Array2<f64> =
     151          18 :                     Array2::zeros((register.len(), self.input.number_pauli_products));
     152          60 :                 for (index, mask_val) in mask.iter() {
     153          42 :                     if mask_val.is_empty() {
     154          12 :                         single_shot_pauli_products.column_mut(*index).fill(1.0);
     155          12 :                     } else {
     156             :                         // Accessing column of single_shot_pauli_products that corresponds to pauli product designated by index
     157          30 :                         let mut column = single_shot_pauli_products.column_mut(*index);
     158             :                         // Iterate over all single shot readouts for all qubits and construct Pauli Product
     159          90 :                         for (row_index, values) in register.iter().enumerate() {
     160             :                             // Determine the value of the pauli product with the parity of the number of 0 and 1 measurements of the qubits
     161             :                             // false is even parity and true is odd parity
     162          90 :                             let mut parity = false;
     163         144 :                             for i in mask_val.iter() {
     164         144 :                                 // For flipped readout a false (0) qubit measurement will flip the parity
     165         144 :                                 // For a not-flipped measurement a true (1) qubit measurement will flip the parity
     166         144 :                                 if values[*i] ^ flip_measurement {
     167          42 :                                     parity = !parity
     168         102 :                                 }
     169             :                             } // Map even parity measurement result to 1 and odd parity result to -1
     170          90 :                             column[row_index] = match parity {
     171          72 :                                 false => 1.0,
     172          18 :                                 true => -1.0,
     173             :                             };
     174             :                         }
     175             :                     }
     176             :                 }
     177          18 :                 let mut pauli_products_tmp: Array1<f64> =
     178          18 :                     Array1::zeros(self.input.number_pauli_products);
     179          66 :                 for i in 0..self.input.number_pauli_products {
     180          66 :                     pauli_products_tmp[i] = single_shot_pauli_products.column(i).mean().ok_or(
     181          66 :                         RoqoqoError::BasisRotationMeasurementError {
     182          66 :                             msg: format!(
     183          66 :                                 "Column {} out of index for sinlge_shot_pauli_products",
     184          66 :                                 i
     185          66 :                             ),
     186          66 :                         },
     187          66 :                     )?;
     188             :                 }
     189          18 :                 pauli_product_dict.insert(
     190          18 :                     format!("{}{}", register_name.as_str(), extension),
     191          18 :                     pauli_products_tmp,
     192          18 :                 );
     193             :             }
     194             :         }
     195             : 
     196          10 :         let mut pauli_products: Array1<f64> = Array1::zeros(self.input.number_pauli_products);
     197          25 :         for (register_name, _) in self.input.pauli_product_qubit_masks.iter() {
     198          15 :             if !register_name.ends_with("flipped") {
     199             :                 // Create temporary averaged vector of pauli_products
     200             :                 // Averaging between normal and flipped readout when flipped measurement is used
     201          15 :                 if self.input.use_flipped_measurement {
     202           3 :                     let tmp_pauli_products = (&pauli_product_dict
     203           3 :                         .get(register_name.as_str())
     204           3 :                         .ok_or(RoqoqoError::BasisRotationMeasurementError {
     205           3 :                             msg: format!("Register name {} not fount", register_name),
     206           3 :                         })?
     207           3 :                         .view()
     208           3 :                         + &pauli_product_dict
     209           3 :                             .get(format!("{}_flipped", register_name).as_str())
     210           3 :                             .ok_or(RoqoqoError::BasisRotationMeasurementError {
     211           3 :                                 msg: format!("Register name {}_flipped not fount", register_name),
     212           3 :                             })?
     213           3 :                             .view())
     214           3 :                         / 2.0;
     215           3 :                     // reinserting in dict of pauli products
     216           3 :                     pauli_products += &tmp_pauli_products.view();
     217             :                 } else {
     218          12 :                     pauli_products += &pauli_product_dict
     219          12 :                         .get(register_name.as_str())
     220          12 :                         .ok_or(RoqoqoError::BasisRotationMeasurementError {
     221          12 :                             msg: format!("Register name {} not fount", register_name),
     222          12 :                         })?
     223          12 :                         .view()
     224             :                 }
     225           0 :             }
     226             :         }
     227             :         // Evaluating expectation values
     228          10 :         let mut results: HashMap<String, f64> = HashMap::new();
     229             : 
     230          40 :         for (name, evaluation) in self.input.measured_exp_vals.iter() {
     231             :             results.insert(
     232          30 :                 name.clone(),
     233          30 :                 match evaluation {
     234          28 :                     PauliProductsToExpVal::Linear(hm) => {
     235          28 :                         let mut value: f64 = 0.0;
     236          63 :                         for (index, coefficient) in hm {
     237          35 :                             value += pauli_products[*index] * coefficient;
     238          35 :                         }
     239          28 :                         value
     240             :                     }
     241           2 :                     PauliProductsToExpVal::Symbolic(x) => {
     242           2 :                         let mut calculator = qoqo_calculator::Calculator::new();
     243           8 :                         for (ind, p) in pauli_products.iter().enumerate() {
     244           8 :                             calculator.set_variable(format!("pauli_product_{}", ind).as_str(), *p);
     245           8 :                         }
     246           2 :                         calculator.parse_get(x.clone())?
     247             :                     }
     248             :                 },
     249             :             );
     250             :         }
     251             : 
     252          10 :         Ok(Some(results))
     253          10 :     }
     254             : }

Generated by: LCOV version 1.13