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