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 measurement operations.
14 :
15 : use qoqo_calculator::Calculator;
16 : use std::collections::{HashMap, HashSet};
17 :
18 : use crate::operations::{
19 : InvolveQubits, InvolvedQubits, Operate, OperatePragma, OperateSingleQubit, RoqoqoError,
20 : Substitute,
21 : };
22 : use crate::Circuit;
23 :
24 : /// Measurement gate operation.
25 : ///
26 : /// This Operation acts on one qubit writing the result of the measurement into a readout.
27 : /// The classical register for the readout needs to be defined in advance by using a Definition operation.
28 : ///
29 : /// # Note
30 : ///
31 : /// Here, it is a measurement in terms of quantum mechanics. The obtained result of a $\textit{single}$ measurement will be either a `0` or a `1`.
32 : /// In order to be able to derive probabilities in the $\textit{post-processing}$, the actual measurement needs to be repeated lots of times.
33 : ///
34 : #[derive(
35 1 : Debug,
36 1 : Clone,
37 7 : PartialEq,
38 1 : roqoqo_derive::InvolveQubits,
39 15 : roqoqo_derive::Operate,
40 2 : roqoqo_derive::Substitute,
41 2 : roqoqo_derive::OperateSingleQubit,
42 : )]
43 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
44 : pub struct MeasureQubit {
45 : /// The measured qubit.
46 : qubit: usize,
47 : /// The register for the readout.
48 : readout: String,
49 : /// The index in the readout the result is saved to.
50 : readout_index: usize,
51 : }
52 :
53 : #[allow(non_upper_case_globals)]
54 : const TAGS_MeasureQubit: &[&str; 3] = &["Operation", "Measurement", "MeasureQubit"];
55 :
56 : /// This PRAGMA measurement operation returns the statevector of a quantum register.
57 : ///
58 8 : #[derive(Debug, Clone, PartialEq, roqoqo_derive::Operate, roqoqo_derive::OperatePragma)]
59 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
60 : pub struct PragmaGetStateVector {
61 : /// The name of the classical readout register.
62 : readout: String,
63 : /// The measurement preparation Circuit, applied on a copy of the register before measurement (None if not defined, Some(Circuit) otherwise).
64 : circuit: Option<Circuit>,
65 : }
66 :
67 : #[allow(non_upper_case_globals)]
68 : const TAGS_PragmaGetStateVector: &[&str; 4] = &[
69 : "Operation",
70 : "Measurement",
71 : "PragmaOperation",
72 : "PragmaGetStateVector",
73 : ];
74 :
75 : /// Implements [Substitute] trait allowing to replace symbolic parameters and to perform qubit mappings.
76 : impl Substitute for PragmaGetStateVector {
77 : /// Remaps qubits in operations in clone of the operation.
78 2 : fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
79 2 : let new_circuit = match self.circuit.as_ref() {
80 1 : Some(x) => Some(x.remap_qubits(mapping)?),
81 1 : _ => None,
82 : };
83 2 : Ok(PragmaGetStateVector::new(self.readout.clone(), new_circuit))
84 2 : }
85 :
86 : /// Substitutes symbolic parameters in clone of the operation.
87 1 : fn substitute_parameters(&self, calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
88 1 : let new_circuit = match self.circuit.as_ref() {
89 0 : Some(x) => Some(x.substitute_parameters(calculator)?),
90 1 : _ => None,
91 : };
92 1 : Ok(PragmaGetStateVector::new(self.readout.clone(), new_circuit))
93 1 : }
94 : }
95 :
96 : // Implements the InvolveQubits trait for PragmaGetStateVector.
97 : impl InvolveQubits for PragmaGetStateVector {
98 : /// Lists all involved qubits (here: All).
99 1 : fn involved_qubits(&self) -> InvolvedQubits {
100 1 : InvolvedQubits::All
101 1 : }
102 : }
103 :
104 : /// This PRAGMA measurement operation returns the density matrix of a quantum register.
105 : ///
106 8 : #[derive(Debug, Clone, PartialEq, roqoqo_derive::Operate, roqoqo_derive::OperatePragma)]
107 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
108 : pub struct PragmaGetDensityMatrix {
109 : /// The name of the classical readout register.
110 : readout: String,
111 : /// The measurement preparation Circuit, applied on a copy of the register before measurement (None if not defined, Some(Circuit) otherwise).
112 : circuit: Option<Circuit>,
113 : }
114 :
115 : #[allow(non_upper_case_globals)]
116 : const TAGS_PragmaGetDensityMatrix: &[&str; 4] = &[
117 : "Operation",
118 : "Measurement",
119 : "PragmaOperation",
120 : "PragmaGetDensityMatrix",
121 : ];
122 :
123 : /// Implements [Substitute] trait allowing to replace symbolic parameters and to perform qubit mappings.
124 : impl Substitute for PragmaGetDensityMatrix {
125 : /// Remaps qubits in operations in clone of the operation.
126 2 : fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
127 2 : let new_circuit = match self.circuit.as_ref() {
128 1 : Some(x) => Some(x.remap_qubits(mapping)?),
129 1 : _ => None,
130 : };
131 2 : Ok(PragmaGetDensityMatrix::new(
132 2 : self.readout.clone(),
133 2 : new_circuit,
134 2 : ))
135 2 : }
136 :
137 : /// Substitutes symbolic parameters in clone of the operation.
138 1 : fn substitute_parameters(&self, calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
139 1 : let new_circuit = match self.circuit.as_ref() {
140 0 : Some(x) => Some(x.substitute_parameters(calculator)?),
141 1 : _ => None,
142 : };
143 1 : Ok(PragmaGetDensityMatrix::new(
144 1 : self.readout.clone(),
145 1 : new_circuit,
146 1 : ))
147 1 : }
148 : }
149 :
150 : // Implements the InvolveQubits trait for PragmaGetDensityMatrix.
151 : impl InvolveQubits for PragmaGetDensityMatrix {
152 : /// Lists all involved qubits (here, all).
153 1 : fn involved_qubits(&self) -> InvolvedQubits {
154 1 : InvolvedQubits::All
155 1 : }
156 : }
157 :
158 : /// This PRAGMA measurement operation returns the vector of the occupation probabilities.
159 : ///
160 : /// Occupation probabilities in the context of this PRAGMA Operation are probabilities of finding the quantum
161 : /// register in each $\sigma_z$ basis state. The quantum register remains unchanged by this PRAGMA measurement operation.
162 : ///
163 8 : #[derive(Debug, Clone, PartialEq, roqoqo_derive::Operate, roqoqo_derive::OperatePragma)]
164 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
165 : pub struct PragmaGetOccupationProbability {
166 : /// The name of the classical readout register.
167 : readout: String,
168 : /// The measurement preparation Circuit, applied on a copy of the register before measurement (None if not defined, Some(Circuit) otherwise).
169 : circuit: Option<Circuit>,
170 : }
171 :
172 : #[allow(non_upper_case_globals)]
173 : const TAGS_PragmaGetOccupationProbability: &[&str; 4] = &[
174 : "Operation",
175 : "Measurement",
176 : "PragmaOperation",
177 : "PragmaGetOccupationProbability",
178 : ];
179 :
180 : /// Implements [Substitute] trait allowing to replace symbolic parameters and to perform qubit mappings.
181 : impl Substitute for PragmaGetOccupationProbability {
182 : /// Remaps qubits in operations in clone of the operation.
183 2 : fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
184 2 : let new_circuit = match self.circuit.as_ref() {
185 1 : Some(x) => Some(x.remap_qubits(mapping)?),
186 1 : _ => None,
187 : };
188 2 : Ok(PragmaGetOccupationProbability::new(
189 2 : self.readout.clone(),
190 2 : new_circuit,
191 2 : ))
192 2 : }
193 :
194 : /// Substitutes symbolic parameters in clone of the operation.
195 1 : fn substitute_parameters(&self, calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
196 1 : let new_circuit = match self.circuit.as_ref() {
197 0 : Some(x) => Some(x.substitute_parameters(calculator)?),
198 1 : _ => None,
199 : };
200 1 : Ok(PragmaGetOccupationProbability::new(
201 1 : self.readout.clone(),
202 1 : new_circuit,
203 1 : ))
204 1 : }
205 : }
206 :
207 : // Implements the InvolveQubits trait for PragmaGetOccupationProbability.
208 : impl InvolveQubits for PragmaGetOccupationProbability {
209 : /// Lists all involved qubits (here, all).
210 2 : fn involved_qubits(&self) -> InvolvedQubits {
211 2 : InvolvedQubits::All
212 2 : }
213 : }
214 :
215 : /// This PRAGMA measurement operation returns a Pauli product expectation value.
216 : ///
217 : /// This PRAGMA Operation returns a Pauli product expectation value after applying
218 : /// a Rotate to another basis. It performs all of the operation on a clone of the quantum register,
219 : /// so that the actual quantum register remains unchanged.
220 : ///
221 19 : #[derive(Debug, Clone, PartialEq, roqoqo_derive::Operate, roqoqo_derive::OperatePragma)]
222 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
223 : pub struct PragmaGetPauliProduct {
224 : /// The HashMap of the pauli matrix to apply to each qubit in the form {qubit: pauli}. Allowed values to be provided for 'pauli' are: `0` = identity, `1` = PauliX, `2` = PauliY, `3` = PauliZ.
225 : qubit_paulis: HashMap<usize, usize>,
226 : /// The name of the classical readout register.
227 : readout: String,
228 : /// The measurement preparation Circuit, applied on a copy of the register before measurement.
229 : circuit: Circuit,
230 : }
231 :
232 : #[allow(non_upper_case_globals)]
233 : const TAGS_PragmaGetPauliProduct: &[&str; 4] = &[
234 : "Operation",
235 : "Measurement",
236 : "PragmaOperation",
237 : "PragmaGetPauliProduct",
238 : ];
239 :
240 : /// Implements [Substitute] trait allowing to replace symbolic parameters and to perform qubit mappings.
241 : impl Substitute for PragmaGetPauliProduct {
242 : /// Remaps qubits in operations in clone of the operation.
243 2 : fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
244 2 : let mut mutable_mapping: HashMap<usize, usize> = self.qubit_paulis.clone();
245 4 : for (old_qubit, new_qubit) in mapping {
246 2 : if let Some(v) = mutable_mapping.remove(old_qubit) {
247 2 : mutable_mapping.insert(*new_qubit, v);
248 2 : }
249 : }
250 2 : let new_circuit = self.circuit.remap_qubits(mapping).unwrap();
251 2 : Ok(PragmaGetPauliProduct::new(
252 2 : mutable_mapping,
253 2 : self.readout.clone(),
254 2 : new_circuit,
255 2 : ))
256 2 : }
257 :
258 : /// Substitutes symbolic parameters in clone of the operation.
259 1 : fn substitute_parameters(&self, calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
260 1 : let new_circuit = self.circuit.substitute_parameters(calculator).unwrap();
261 1 : Ok(PragmaGetPauliProduct::new(
262 1 : self.qubit_paulis.clone(),
263 1 : self.readout.clone(),
264 1 : new_circuit,
265 1 : ))
266 1 : }
267 : }
268 :
269 : // Implements the InvolveQubits trait for PragmaGetPauliProduct.
270 : impl InvolveQubits for PragmaGetPauliProduct {
271 : /// Lists all involved qubits.
272 1 : fn involved_qubits(&self) -> InvolvedQubits {
273 1 : let mut new_hash_set: HashSet<usize> = HashSet::new();
274 1 : for qubit in self.qubit_paulis.keys() {
275 1 : new_hash_set.insert(*qubit);
276 1 : }
277 1 : if let InvolvedQubits::Set(tmp_set) = &self.circuit.involved_qubits() {
278 1 : for qubit in tmp_set {
279 1 : new_hash_set.insert(*qubit);
280 1 : }
281 0 : }
282 1 : InvolvedQubits::Set(new_hash_set)
283 1 : }
284 : }
285 :
286 : /// This PRAGMA measurement operation returns a measurement record for $N$ repeated measurements.
287 : ///
288 15 : #[derive(Debug, Clone, PartialEq, roqoqo_derive::Operate, roqoqo_derive::OperatePragma)]
289 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
290 : pub struct PragmaRepeatedMeasurement {
291 : /// The name of the classical readout register.
292 : readout: String,
293 : /// The number of times $N$ to repeat the measurement.
294 : number_measurements: usize,
295 : /// The mapping of qubits to indices in the readout register.
296 : qubit_mapping: Option<HashMap<usize, usize>>,
297 : }
298 :
299 : #[allow(non_upper_case_globals)]
300 : const TAGS_PragmaRepeatedMeasurement: &[&str; 4] = &[
301 : "Operation",
302 : "Measurement",
303 : "PragmaOperation",
304 : "PragmaRepeatedMeasurement",
305 : ];
306 :
307 : /// Implements [Substitute] trait allowing to replace symbolic parameters and to perform qubit mappings.
308 : impl Substitute for PragmaRepeatedMeasurement {
309 : /// Remaps qubits in operations in clone of the operation.
310 1 : fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
311 1 : let new_mapping = (self.qubit_mapping.clone()).map(|hm| {
312 1 : let mut mutable_mapping: HashMap<usize, usize> = hm;
313 2 : for (old_qubit, new_qubit) in mapping {
314 1 : if let Some(v) = mutable_mapping.remove(old_qubit) {
315 1 : mutable_mapping.insert(*new_qubit, v);
316 1 : }
317 : }
318 1 : mutable_mapping
319 1 : });
320 1 : Ok(PragmaRepeatedMeasurement::new(
321 1 : self.readout.clone(),
322 1 : self.number_measurements,
323 1 : new_mapping,
324 1 : ))
325 1 : }
326 :
327 : /// Substitutes symbolic parameters in clone of the operation.
328 1 : fn substitute_parameters(&self, _calculator: &mut Calculator) -> Result<Self, RoqoqoError> {
329 1 : Ok(self.clone())
330 1 : }
331 : }
332 :
333 : // Implements the InvolveQubits trait for PragmaRepeatedMeasurement.
334 : impl InvolveQubits for PragmaRepeatedMeasurement {
335 : /// Lists all involved qubits (here, all).
336 1 : fn involved_qubits(&self) -> InvolvedQubits {
337 1 : InvolvedQubits::All
338 1 : }
339 : }
|