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 : }
|