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 crate::operations::single_qubit_gate_operations::*;
14 : use crate::prelude::*;
15 : use crate::Circuit;
16 : use crate::RoqoqoError;
17 : use ndarray::{array, Array2};
18 : use num_complex::Complex64;
19 : use qoqo_calculator::{CalculatorComplex, CalculatorFloat};
20 : #[cfg(feature = "overrotate")]
21 : use rand_distr::{Distribution, Normal};
22 : use std::convert::TryFrom;
23 : use std::f64::consts::PI;
24 :
25 : /// The KAK decomposition of a two-qubit gate.
26 : ///
27 : /// Each two-qubit gate can be described by a KAK decomposition (<http://arxiv.org/abs/quant-ph/0507171>).
28 : ///
29 : /// A two qubit gate is decomposed into four single qubit gates, one for each qubit acting before and after applying the
30 : /// entangling operation based on the k_vector:
31 : ///
32 : /// U(k_vector) = exp(i (k_vector(0) XX + k_vector(1) YY + k_vector(2) ZZ))
33 : ///
34 : /// This struct contains all information on the KAK decomposition of a two qubit gate.
35 4 : #[derive(Debug, Clone, PartialEq)]
36 : pub struct KakDecomposition {
37 : /// Global phase of KAK decomposition
38 : pub global_phase: CalculatorFloat,
39 : /// Three component vector of the KAK decomposition
40 : pub k_vector: [CalculatorFloat; 3],
41 : /// Circuit including operations acting on control and target qubits before two-qubit entangling
42 : pub circuit_before: Option<Circuit>,
43 : /// Circuit including operations acting on control and target qubits after two-qubit entangling
44 : pub circuit_after: Option<Circuit>,
45 : }
46 :
47 : /// Implements the CNOT controlled not gate.
48 : ///
49 : /// $$
50 : /// U = \begin{pmatrix}
51 : /// 1 & 0 & 0 & 0 \\\\
52 : /// 0 & 1 & 0 & 0 \\\\
53 : /// 0 & 0 & 0 & 1 \\\\
54 : /// 0 & 0 & 1 & 0
55 : /// \end{pmatrix}
56 : /// $$
57 : ///
58 : #[allow(clippy::upper_case_acronyms)]
59 : #[derive(
60 1 : Debug,
61 4 : Clone,
62 13 : PartialEq,
63 1 : roqoqo_derive::InvolveQubits,
64 36 : roqoqo_derive::Operate,
65 3 : roqoqo_derive::Substitute,
66 10 : roqoqo_derive::OperateTwoQubit,
67 : )]
68 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
69 : pub struct CNOT {
70 : /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of NOT on the target qubit.
71 : control: usize,
72 : /// The index of the least significant qubit in the unitary representation. Here, the qubit NOT is applied to.
73 : target: usize,
74 : }
75 :
76 : #[allow(non_upper_case_globals)]
77 : const TAGS_CNOT: &[&str; 4] = &[
78 : "Operation",
79 : "GateOperation",
80 : "TwoQubitGateOperation",
81 : "CNOT",
82 : ];
83 :
84 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
85 : impl OperateGate for CNOT {
86 : /// Returns unitary matrix of the gate.
87 : ///
88 : /// # Returns
89 : ///
90 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
91 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
92 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
93 2 : Ok(array![
94 2 : [
95 2 : Complex64::new(1.0, 0.0),
96 2 : Complex64::new(0.0, 0.0),
97 2 : Complex64::new(0.0, 0.0),
98 2 : Complex64::new(0.0, 0.0)
99 2 : ],
100 2 : [
101 2 : Complex64::new(0.0, 0.0),
102 2 : Complex64::new(1.0, 0.0),
103 2 : Complex64::new(0.0, 0.0),
104 2 : Complex64::new(0.0, 0.0)
105 2 : ],
106 2 : [
107 2 : Complex64::new(0.0, 0.0),
108 2 : Complex64::new(0.0, 0.0),
109 2 : Complex64::new(0.0, 0.0),
110 2 : Complex64::new(1.0, 0.0)
111 2 : ],
112 2 : [
113 2 : Complex64::new(0.0, 0.0),
114 2 : Complex64::new(0.0, 0.0),
115 2 : Complex64::new(1.0, 0.0),
116 2 : Complex64::new(0.0, 0.0)
117 2 : ],
118 2 : ])
119 2 : }
120 : }
121 :
122 : /// Trait for all gate operations acting on exactly two qubits.
123 : impl OperateTwoQubitGate for CNOT {
124 : /// Returns [KakDecomposition] of the gate.
125 : ///
126 : /// # Returns
127 : ///
128 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
129 4 : fn kak_decomposition(&self) -> KakDecomposition {
130 4 : let mut circuit_b = Circuit::new();
131 4 : circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
132 4 : circuit_b += RotateY::new(self.control, CalculatorFloat::FRAC_PI_2);
133 4 : circuit_b += RotateX::new(self.target, CalculatorFloat::FRAC_PI_2);
134 4 :
135 4 : let mut circuit_a = Circuit::new();
136 4 : circuit_a += RotateY::new(self.control, CalculatorFloat::FRAC_PI_2 * (-1.0));
137 4 :
138 4 : KakDecomposition {
139 4 : global_phase: CalculatorFloat::FRAC_PI_4,
140 4 : k_vector: [
141 4 : CalculatorFloat::FRAC_PI_4,
142 4 : CalculatorFloat::ZERO,
143 4 : CalculatorFloat::ZERO,
144 4 : ],
145 4 : circuit_before: Some(circuit_b),
146 4 : circuit_after: Some(circuit_a),
147 4 : }
148 4 : }
149 : }
150 :
151 : /// Implements the SWAP gate.
152 : ///
153 : /// $$
154 : /// U = \begin{pmatrix}
155 : /// 1 & 0 & 0 & 0 \\\\
156 : /// 0 & 0 & 1 & 0 \\\\
157 : /// 0 & 1 & 0 & 0 \\\\
158 : /// 0 & 0 & 0 & 1
159 : /// \end{pmatrix}
160 : /// $$
161 : ///
162 : #[allow(clippy::upper_case_acronyms)]
163 : #[derive(
164 1 : Debug,
165 4 : Clone,
166 7 : PartialEq,
167 1 : roqoqo_derive::InvolveQubits,
168 24 : roqoqo_derive::Operate,
169 3 : roqoqo_derive::Substitute,
170 6 : roqoqo_derive::OperateTwoQubit,
171 : )]
172 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
173 : pub struct SWAP {
174 : /// The index of the most significant qubit in the unitary representation.
175 : control: usize,
176 : /// The index of the least significant qubit in the unitary representation.
177 : target: usize,
178 : }
179 :
180 : #[allow(non_upper_case_globals)]
181 : const TAGS_SWAP: &[&str; 4] = &[
182 : "Operation",
183 : "GateOperation",
184 : "TwoQubitGateOperation",
185 : "SWAP",
186 : ];
187 :
188 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
189 : impl OperateGate for SWAP {
190 : /// Returns unitary matrix of the gate.
191 : ///
192 : /// # Returns
193 : ///
194 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
195 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
196 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
197 2 : Ok(array![
198 2 : [
199 2 : Complex64::new(1.0, 0.0),
200 2 : Complex64::new(0.0, 0.0),
201 2 : Complex64::new(0.0, 0.0),
202 2 : Complex64::new(0.0, 0.0)
203 2 : ],
204 2 : [
205 2 : Complex64::new(0.0, 0.0),
206 2 : Complex64::new(0.0, 0.0),
207 2 : Complex64::new(1.0, 0.0),
208 2 : Complex64::new(0.0, 0.0)
209 2 : ],
210 2 : [
211 2 : Complex64::new(0.0, 0.0),
212 2 : Complex64::new(1.0, 0.0),
213 2 : Complex64::new(0.0, 0.0),
214 2 : Complex64::new(0.0, 0.0)
215 2 : ],
216 2 : [
217 2 : Complex64::new(0.0, 0.0),
218 2 : Complex64::new(0.0, 0.0),
219 2 : Complex64::new(0.0, 0.0),
220 2 : Complex64::new(1.0, 0.0)
221 2 : ],
222 2 : ])
223 2 : }
224 : }
225 :
226 : /// Trait for all gate operations acting on exactly two qubits.
227 : impl OperateTwoQubitGate for SWAP {
228 : /// Returns [KakDecomposition] of the gate.
229 : ///
230 : /// # Returns
231 : ///
232 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
233 4 : fn kak_decomposition(&self) -> KakDecomposition {
234 4 : KakDecomposition {
235 4 : global_phase: CalculatorFloat::from((-1.0) * PI / 4.0),
236 4 : k_vector: [
237 4 : CalculatorFloat::FRAC_PI_4,
238 4 : CalculatorFloat::FRAC_PI_4,
239 4 : CalculatorFloat::FRAC_PI_4,
240 4 : ],
241 4 : circuit_before: None,
242 4 : circuit_after: None,
243 4 : }
244 4 : }
245 : }
246 :
247 : /// The ISwap gate.
248 : ///
249 : /// $$
250 : /// U = \begin{pmatrix}
251 : /// 1 & 0 & 0 & 0 \\\\
252 : /// 0 & 0 & i & 0 \\\\
253 : /// 0 & i & 0 & 0 \\\\
254 : /// 0 & 0 & 0 & 1
255 : /// \end{pmatrix}
256 : /// $$
257 : ///
258 : #[allow(clippy::upper_case_acronyms)]
259 : #[derive(
260 1 : Debug,
261 4 : Clone,
262 7 : PartialEq,
263 1 : roqoqo_derive::InvolveQubits,
264 24 : roqoqo_derive::Operate,
265 3 : roqoqo_derive::Substitute,
266 6 : roqoqo_derive::OperateTwoQubit,
267 : )]
268 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
269 : pub struct ISwap {
270 : /// The index of the most significant qubit in the unitary representation.
271 : control: usize,
272 : /// The index of the least significant qubit in the unitary representation.
273 : target: usize,
274 : }
275 :
276 : #[allow(non_upper_case_globals)]
277 : const TAGS_ISwap: &[&str; 4] = &[
278 : "Operation",
279 : "GateOperation",
280 : "TwoQubitGateOperation",
281 : "ISwap",
282 : ];
283 :
284 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
285 : impl OperateGate for ISwap {
286 : /// Returns unitary matrix of the gate.
287 : ///
288 : /// # Returns
289 : ///
290 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
291 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
292 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
293 2 : Ok(array![
294 2 : [
295 2 : Complex64::new(1.0, 0.0),
296 2 : Complex64::new(0.0, 0.0),
297 2 : Complex64::new(0.0, 0.0),
298 2 : Complex64::new(0.0, 0.0)
299 2 : ],
300 2 : [
301 2 : Complex64::new(0.0, 0.0),
302 2 : Complex64::new(0.0, 0.0),
303 2 : Complex64::new(0.0, 1.0),
304 2 : Complex64::new(0.0, 0.0)
305 2 : ],
306 2 : [
307 2 : Complex64::new(0.0, 0.0),
308 2 : Complex64::new(0.0, 1.0),
309 2 : Complex64::new(0.0, 0.0),
310 2 : Complex64::new(0.0, 0.0)
311 2 : ],
312 2 : [
313 2 : Complex64::new(0.0, 0.0),
314 2 : Complex64::new(0.0, 0.0),
315 2 : Complex64::new(0.0, 0.0),
316 2 : Complex64::new(1.0, 0.0)
317 2 : ],
318 2 : ])
319 2 : }
320 : }
321 :
322 : /// Trait for all gate operations acting on exactly two qubits.
323 : impl OperateTwoQubitGate for ISwap {
324 : /// Returns [KakDecomposition] of the gate.
325 : ///
326 : /// # Returns
327 : ///
328 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
329 4 : fn kak_decomposition(&self) -> KakDecomposition {
330 4 : KakDecomposition {
331 4 : global_phase: CalculatorFloat::ZERO,
332 4 : k_vector: [
333 4 : CalculatorFloat::FRAC_PI_4,
334 4 : CalculatorFloat::FRAC_PI_4,
335 4 : CalculatorFloat::ZERO,
336 4 : ],
337 4 : circuit_before: None,
338 4 : circuit_after: None,
339 4 : }
340 4 : }
341 : }
342 :
343 : /// The fermionic SWAP gate.
344 : ///
345 : /// $$
346 : /// U = \begin{pmatrix}
347 : /// 1 & 0 & 0 & 0 \\\\
348 : /// 0 & 0 & 1 & 0 \\\\
349 : /// 0 & 1 & 0 & 0 \\\\
350 : /// 0 & 0 & 0 & -1
351 : /// \end{pmatrix}
352 : /// $$
353 : ///
354 : #[allow(clippy::upper_case_acronyms)]
355 : #[derive(
356 1 : Debug,
357 4 : Clone,
358 7 : PartialEq,
359 1 : roqoqo_derive::InvolveQubits,
360 24 : roqoqo_derive::Operate,
361 3 : roqoqo_derive::Substitute,
362 8 : roqoqo_derive::OperateTwoQubit,
363 : )]
364 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
365 : pub struct FSwap {
366 : /// The index of the most significant qubit in the unitary representation.
367 : control: usize,
368 : /// The index of the least significant qubit in the unitary representation.
369 : target: usize,
370 : }
371 :
372 : #[allow(non_upper_case_globals)]
373 : const TAGS_FSwap: &[&str; 4] = &[
374 : "Operation",
375 : "GateOperation",
376 : "TwoQubitGateOperation",
377 : "FSwap",
378 : ];
379 :
380 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
381 : impl OperateGate for FSwap {
382 : /// Returns unitary matrix of the gate.
383 : ///
384 : /// # Returns
385 : ///
386 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
387 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
388 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
389 2 : Ok(array![
390 2 : [
391 2 : Complex64::new(1.0, 0.0),
392 2 : Complex64::new(0.0, 0.0),
393 2 : Complex64::new(0.0, 0.0),
394 2 : Complex64::new(0.0, 0.0)
395 2 : ],
396 2 : [
397 2 : Complex64::new(0.0, 0.0),
398 2 : Complex64::new(0.0, 0.0),
399 2 : Complex64::new(1.0, 0.0),
400 2 : Complex64::new(0.0, 0.0)
401 2 : ],
402 2 : [
403 2 : Complex64::new(0.0, 0.0),
404 2 : Complex64::new(1.0, 0.0),
405 2 : Complex64::new(0.0, 0.0),
406 2 : Complex64::new(0.0, 0.0)
407 2 : ],
408 2 : [
409 2 : Complex64::new(0.0, 0.0),
410 2 : Complex64::new(0.0, 0.0),
411 2 : Complex64::new(0.0, 0.0),
412 2 : Complex64::new(-1.0, 0.0)
413 2 : ],
414 2 : ])
415 2 : }
416 : }
417 :
418 : /// Trait for all gate operations acting on exactly two qubits.
419 : impl OperateTwoQubitGate for FSwap {
420 : /// Returns [KakDecomposition] of the gate.
421 : ///
422 : /// # Returns
423 : ///
424 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
425 4 : fn kak_decomposition(&self) -> KakDecomposition {
426 4 : let mut circuit_b = Circuit::new();
427 4 : circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2 * (-1.0));
428 4 : circuit_b += RotateZ::new(self.target, CalculatorFloat::FRAC_PI_2 * (-1.0));
429 4 :
430 4 : KakDecomposition {
431 4 : global_phase: CalculatorFloat::FRAC_PI_2 * (-1.0),
432 4 : k_vector: [
433 4 : CalculatorFloat::FRAC_PI_4,
434 4 : CalculatorFloat::FRAC_PI_4,
435 4 : CalculatorFloat::ZERO,
436 4 : ],
437 4 : circuit_before: Some(circuit_b),
438 4 : circuit_after: None,
439 4 : }
440 4 : }
441 : }
442 :
443 : /// The square root ISwap gate.
444 : ///
445 : /// $$
446 : /// U = \begin{pmatrix}
447 : /// 1 & 0 & 0 & 0 \\\\
448 : /// 0 & \frac{1}{\sqrt{2}} & \frac{i}{\sqrt{2}} & 0 \\\\
449 : /// 0 & \frac{i}{\sqrt{2}} & \frac{1}{\sqrt{2}} & 0 \\\\
450 : /// 0 & 0 & 0 & 1
451 : /// \end{pmatrix}
452 : /// $$
453 : ///
454 : #[allow(clippy::upper_case_acronyms)]
455 : #[derive(
456 1 : Debug,
457 4 : Clone,
458 7 : PartialEq,
459 1 : roqoqo_derive::InvolveQubits,
460 24 : roqoqo_derive::Operate,
461 3 : roqoqo_derive::Substitute,
462 6 : roqoqo_derive::OperateTwoQubit,
463 : )]
464 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
465 : pub struct SqrtISwap {
466 : /// The index of the most significant qubit in the unitary representation.
467 : control: usize,
468 : /// The index of the least significant qubit in the unitary representation.
469 : target: usize,
470 : }
471 :
472 : #[allow(non_upper_case_globals)]
473 : const TAGS_SqrtISwap: &[&str; 4] = &[
474 : "Operation",
475 : "GateOperation",
476 : "TwoQubitGateOperation",
477 : "SqrtISwap",
478 : ];
479 :
480 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
481 : impl OperateGate for SqrtISwap {
482 : /// Returns unitary matrix of the gate.
483 : ///
484 : /// # Returns
485 : ///
486 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
487 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
488 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
489 2 : let f: f64 = 1.0 / ((2.0_f64).sqrt());
490 2 : Ok(array![
491 2 : [
492 2 : Complex64::new(1.0, 0.0),
493 2 : Complex64::new(0.0, 0.0),
494 2 : Complex64::new(0.0, 0.0),
495 2 : Complex64::new(0.0, 0.0)
496 2 : ],
497 2 : [
498 2 : Complex64::new(0.0, 0.0),
499 2 : Complex64::new(f, 0.0),
500 2 : Complex64::new(0.0, f),
501 2 : Complex64::new(0.0, 0.0)
502 2 : ],
503 2 : [
504 2 : Complex64::new(0.0, 0.0),
505 2 : Complex64::new(0.0, f),
506 2 : Complex64::new(f, 0.0),
507 2 : Complex64::new(0.0, 0.0)
508 2 : ],
509 2 : [
510 2 : Complex64::new(0.0, 0.0),
511 2 : Complex64::new(0.0, 0.0),
512 2 : Complex64::new(0.0, 0.0),
513 2 : Complex64::new(1.0, 0.0)
514 2 : ],
515 2 : ])
516 2 : }
517 : }
518 :
519 : /// Trait for all gate operations acting on exactly two qubits.
520 : impl OperateTwoQubitGate for SqrtISwap {
521 : /// Returns [KakDecomposition] of the gate.
522 : ///
523 : /// # Returns
524 : ///
525 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
526 4 : fn kak_decomposition(&self) -> KakDecomposition {
527 4 : KakDecomposition {
528 4 : global_phase: CalculatorFloat::ZERO,
529 4 : k_vector: [
530 4 : CalculatorFloat::from(PI / 8.0),
531 4 : CalculatorFloat::from(PI / 8.0),
532 4 : CalculatorFloat::ZERO,
533 4 : ],
534 4 : circuit_before: None,
535 4 : circuit_after: None,
536 4 : }
537 4 : }
538 : }
539 :
540 : /// The inverse square root ISwap gate.
541 : ///
542 : /// $$
543 : /// U = \begin{pmatrix}
544 : /// 1 & 0 & 0 & 0 \\\\
545 : /// 0 & \frac{1}{\sqrt{2}} & \frac{-i}{\sqrt{2}} & 0 \\\\
546 : /// 0 & \frac{-i}{\sqrt{2}} & \frac{1}{\sqrt{2}} & 0 \\\\
547 : /// 0 & 0 & 0 & 1
548 : /// \end{pmatrix}
549 : /// $$
550 : ///
551 : #[allow(clippy::upper_case_acronyms)]
552 : #[derive(
553 1 : Debug,
554 4 : Clone,
555 7 : PartialEq,
556 1 : roqoqo_derive::InvolveQubits,
557 24 : roqoqo_derive::Operate,
558 3 : roqoqo_derive::Substitute,
559 6 : roqoqo_derive::OperateTwoQubit,
560 : )]
561 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
562 : pub struct InvSqrtISwap {
563 : /// The index of the most significant qubit in the unitary representation.
564 : control: usize,
565 : /// The index of the least significant qubit in the unitary representation.
566 : target: usize,
567 : }
568 :
569 : #[allow(non_upper_case_globals)]
570 : const TAGS_InvSqrtISwap: &[&str; 4] = &[
571 : "Operation",
572 : "GateOperation",
573 : "TwoQubitGateOperation",
574 : "InvSqrtISwap",
575 : ];
576 :
577 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
578 : impl OperateGate for InvSqrtISwap {
579 : /// Returns unitary matrix of the gate.
580 : ///
581 : /// # Returns
582 : ///
583 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
584 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
585 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
586 2 : let f: f64 = 1.0 / ((2.0_f64).sqrt());
587 2 : Ok(array![
588 2 : [
589 2 : Complex64::new(1.0, 0.0),
590 2 : Complex64::new(0.0, 0.0),
591 2 : Complex64::new(0.0, 0.0),
592 2 : Complex64::new(0.0, 0.0)
593 2 : ],
594 2 : [
595 2 : Complex64::new(0.0, 0.0),
596 2 : Complex64::new(f, 0.0),
597 2 : Complex64::new(0.0, (-1.0) * f),
598 2 : Complex64::new(0.0, 0.0)
599 2 : ],
600 2 : [
601 2 : Complex64::new(0.0, 0.0),
602 2 : Complex64::new(0.0, (-1.0) * f),
603 2 : Complex64::new(f, 0.0),
604 2 : Complex64::new(0.0, 0.0)
605 2 : ],
606 2 : [
607 2 : Complex64::new(0.0, 0.0),
608 2 : Complex64::new(0.0, 0.0),
609 2 : Complex64::new(0.0, 0.0),
610 2 : Complex64::new(1.0, 0.0)
611 2 : ],
612 2 : ])
613 2 : }
614 : }
615 :
616 : /// Trait for all gate operations acting on exactly two qubits.
617 : impl OperateTwoQubitGate for InvSqrtISwap {
618 : /// Returns [KakDecomposition] of the gate.
619 : ///
620 : /// # Returns
621 : ///
622 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
623 4 : fn kak_decomposition(&self) -> KakDecomposition {
624 4 : KakDecomposition {
625 4 : global_phase: CalculatorFloat::ZERO,
626 4 : k_vector: [
627 4 : CalculatorFloat::from((-1.0) * PI / 8.0),
628 4 : CalculatorFloat::from((-1.0) * PI / 8.0),
629 4 : CalculatorFloat::ZERO,
630 4 : ],
631 4 : circuit_before: None,
632 4 : circuit_after: None,
633 4 : }
634 4 : }
635 : }
636 :
637 : /// The XY gate.
638 : ///
639 : /// $$
640 : /// U = \begin{pmatrix}
641 : /// 1 & 0 & 0 & 0 \\\\
642 : /// 0 & \cos(\theta/2) & i \sin(\theta/2) & 0 \\\\
643 : /// 0 & i \sin(\theta/2) & \cos(\theta/2) & 0 \\\\
644 : /// 0 & 0 & 0 & 1
645 : /// \end{pmatrix}
646 : /// $$
647 : ///
648 : #[allow(clippy::upper_case_acronyms)]
649 : #[derive(
650 1 : Debug,
651 5 : Clone,
652 9 : PartialEq,
653 1 : roqoqo_derive::InvolveQubits,
654 33 : roqoqo_derive::Operate,
655 6 : roqoqo_derive::Substitute,
656 6 : roqoqo_derive::OperateTwoQubit,
657 3 : roqoqo_derive::Rotate,
658 : )]
659 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
660 : pub struct XY {
661 : /// The index of the most significant qubit in the unitary representation.
662 : control: usize,
663 : /// The index of the least significant qubit in the unitary representation.
664 : target: usize,
665 : /// The rotation angle $\theta$.
666 : theta: CalculatorFloat,
667 : }
668 :
669 : #[allow(non_upper_case_globals)]
670 : const TAGS_XY: &[&str; 5] = &[
671 : "Operation",
672 : "GateOperation",
673 : "TwoQubitGateOperation",
674 : "Rotation",
675 : "XY",
676 : ];
677 :
678 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
679 : impl OperateGate for XY {
680 : /// Returns unitary matrix of the gate.
681 : ///
682 : /// # Returns
683 : ///
684 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
685 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
686 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
687 2 : let c: f64 = (f64::try_from(self.theta.clone())? / 2.0).cos();
688 2 : let s: f64 = (f64::try_from(self.theta.clone())? / 2.0).sin();
689 2 : Ok(array![
690 2 : [
691 2 : Complex64::new(1.0, 0.0),
692 2 : Complex64::new(0.0, 0.0),
693 2 : Complex64::new(0.0, 0.0),
694 2 : Complex64::new(0.0, 0.0)
695 2 : ],
696 2 : [
697 2 : Complex64::new(0.0, 0.0),
698 2 : Complex64::new(c, 0.0),
699 2 : Complex64::new(0.0, s),
700 2 : Complex64::new(0.0, 0.0)
701 2 : ],
702 2 : [
703 2 : Complex64::new(0.0, 0.0),
704 2 : Complex64::new(0.0, s),
705 2 : Complex64::new(c, 0.0),
706 2 : Complex64::new(0.0, 0.0)
707 2 : ],
708 2 : [
709 2 : Complex64::new(0.0, 0.0),
710 2 : Complex64::new(0.0, 0.0),
711 2 : Complex64::new(0.0, 0.0),
712 2 : Complex64::new(1.0, 0.0)
713 2 : ],
714 2 : ])
715 2 : }
716 : }
717 :
718 : /// Trait for all gate operations acting on exactly two qubits.
719 : impl OperateTwoQubitGate for XY {
720 : /// Returns [KakDecomposition] of the gate.
721 : ///
722 : /// # Returns
723 : ///
724 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
725 4 : fn kak_decomposition(&self) -> KakDecomposition {
726 4 : KakDecomposition {
727 4 : global_phase: CalculatorFloat::ZERO,
728 4 : k_vector: [
729 4 : self.theta.clone() / 4.0,
730 4 : self.theta.clone() / 4.0,
731 4 : CalculatorFloat::ZERO,
732 4 : ],
733 4 : circuit_before: None,
734 4 : circuit_after: None,
735 4 : }
736 4 : }
737 : }
738 :
739 : /// Implements the controlled-PhaseShift gate.
740 : ///
741 : /// $$
742 : /// U = \begin{pmatrix}
743 : /// 1 & 0 & 0 & 0 \\\\
744 : /// 0 & 1 & 0 & 0 \\\\
745 : /// 0 & 0 & 1 & 0 \\\\
746 : /// 0 & 0 & 0 & e^{i \theta}
747 : /// \end{pmatrix}
748 : /// $$
749 : ///
750 : #[allow(clippy::upper_case_acronyms)]
751 : #[derive(
752 1 : Debug,
753 5 : Clone,
754 9 : PartialEq,
755 1 : roqoqo_derive::InvolveQubits,
756 33 : roqoqo_derive::Operate,
757 6 : roqoqo_derive::Substitute,
758 8 : roqoqo_derive::OperateTwoQubit,
759 3 : roqoqo_derive::Rotate,
760 : )]
761 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
762 : pub struct ControlledPhaseShift {
763 : /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of the phase-shift on the target qubit.
764 : control: usize,
765 : /// The index of the least significant qubit in the unitary representation. Here, the qubit phase-shift is applied to.
766 : target: usize,
767 : /// The rotation angle $\theta$.
768 : theta: CalculatorFloat,
769 : }
770 :
771 : #[allow(non_upper_case_globals)]
772 : const TAGS_ControlledPhaseShift: &[&str; 5] = &[
773 : "Operation",
774 : "GateOperation",
775 : "TwoQubitGateOperation",
776 : "Rotation",
777 : "ControlledPhaseShift",
778 : ];
779 :
780 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
781 : impl OperateGate for ControlledPhaseShift {
782 : /// Returns unitary matrix of the gate.
783 : ///
784 : /// # Returns
785 : ///
786 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
787 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
788 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
789 : // exp(i*x) = cos(x)+i*sin(x)
790 2 : let c: f64 = (f64::try_from(self.theta.clone())?).cos();
791 2 : let s: f64 = (f64::try_from(self.theta.clone())?).sin();
792 2 : Ok(array![
793 2 : [
794 2 : Complex64::new(1.0, 0.0),
795 2 : Complex64::new(0.0, 0.0),
796 2 : Complex64::new(0.0, 0.0),
797 2 : Complex64::new(0.0, 0.0)
798 2 : ],
799 2 : [
800 2 : Complex64::new(0.0, 0.0),
801 2 : Complex64::new(1.0, 0.0),
802 2 : Complex64::new(0.0, 0.0),
803 2 : Complex64::new(0.0, 0.0)
804 2 : ],
805 2 : [
806 2 : Complex64::new(0.0, 0.0),
807 2 : Complex64::new(0.0, 0.0),
808 2 : Complex64::new(1.0, 0.0),
809 2 : Complex64::new(0.0, 0.0)
810 2 : ],
811 2 : [
812 2 : Complex64::new(0.0, 0.0),
813 2 : Complex64::new(0.0, 0.0),
814 2 : Complex64::new(0.0, 0.0),
815 2 : Complex64::new(c, s)
816 2 : ],
817 2 : ])
818 2 : }
819 : }
820 :
821 : /// Trait for all gate operations acting on exactly two qubits.
822 : impl OperateTwoQubitGate for ControlledPhaseShift {
823 : /// Returns [KakDecomposition] of the gate.
824 : ///
825 : /// # Returns
826 : ///
827 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
828 4 : fn kak_decomposition(&self) -> KakDecomposition {
829 4 : let mut circuit_b = Circuit::new();
830 4 : circuit_b += RotateZ::new(self.control, self.theta.clone() / 2.0);
831 4 : circuit_b += RotateZ::new(self.target, self.theta.clone() / 2.0);
832 4 :
833 4 : KakDecomposition {
834 4 : global_phase: self.theta.clone() / 4.0,
835 4 : k_vector: [
836 4 : CalculatorFloat::ZERO,
837 4 : CalculatorFloat::ZERO,
838 4 : self.theta.clone() / 4.0,
839 4 : ],
840 4 : circuit_before: Some(circuit_b),
841 4 : circuit_after: None,
842 4 : }
843 4 : }
844 : }
845 :
846 : /// The controlled-PauliY gate.
847 : ///
848 : /// $$
849 : /// U = \begin{pmatrix}
850 : /// 1 & 0 & 0 & 0 \\\\
851 : /// 0 & 1 & 0 & 0 \\\\
852 : /// 0 & 0 & 0 & -i \\\\
853 : /// 0 & 0 & i & 0
854 : /// \end{pmatrix}
855 : /// $$
856 : ///
857 : #[allow(clippy::upper_case_acronyms)]
858 : #[derive(
859 1 : Debug,
860 4 : Clone,
861 7 : PartialEq,
862 1 : roqoqo_derive::InvolveQubits,
863 24 : roqoqo_derive::Operate,
864 3 : roqoqo_derive::Substitute,
865 10 : roqoqo_derive::OperateTwoQubit,
866 : )]
867 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
868 : pub struct ControlledPauliY {
869 : /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of PauliY gate on the target qubit.
870 : control: usize,
871 : /// The index of the least significant qubit in the unitary representation. Here, the qubit PauliY is applied to.
872 : target: usize,
873 : }
874 :
875 : #[allow(non_upper_case_globals)]
876 : const TAGS_ControlledPauliY: &[&str; 4] = &[
877 : "Operation",
878 : "GateOperation",
879 : "TwoQubitGateOperation",
880 : "ControlledPauliY",
881 : ];
882 :
883 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
884 : impl OperateGate for ControlledPauliY {
885 : /// Returns unitary matrix of the gate.
886 : ///
887 : /// # Returns
888 : ///
889 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
890 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
891 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
892 2 : Ok(array![
893 2 : [
894 2 : Complex64::new(1.0, 0.0),
895 2 : Complex64::new(0.0, 0.0),
896 2 : Complex64::new(0.0, 0.0),
897 2 : Complex64::new(0.0, 0.0)
898 2 : ],
899 2 : [
900 2 : Complex64::new(0.0, 0.0),
901 2 : Complex64::new(1.0, 0.0),
902 2 : Complex64::new(0.0, 0.0),
903 2 : Complex64::new(0.0, 0.0)
904 2 : ],
905 2 : [
906 2 : Complex64::new(0.0, 0.0),
907 2 : Complex64::new(0.0, 0.0),
908 2 : Complex64::new(0.0, 0.0),
909 2 : Complex64::new(0.0, -1.0)
910 2 : ],
911 2 : [
912 2 : Complex64::new(0.0, 0.0),
913 2 : Complex64::new(0.0, 0.0),
914 2 : Complex64::new(0.0, 1.0),
915 2 : Complex64::new(0.0, 0.0)
916 2 : ],
917 2 : ])
918 2 : }
919 : }
920 :
921 : /// Trait for all gate operations acting on exactly two qubits.
922 : impl OperateTwoQubitGate for ControlledPauliY {
923 : /// Returns [KakDecomposition] of the gate.
924 : ///
925 : /// # Returns
926 : ///
927 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
928 4 : fn kak_decomposition(&self) -> KakDecomposition {
929 4 : let mut circuit_b = Circuit::new();
930 4 : circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
931 4 : circuit_b += RotateY::new(self.target, CalculatorFloat::FRAC_PI_2);
932 4 : circuit_b += RotateX::new(self.target, CalculatorFloat::FRAC_PI_2);
933 4 :
934 4 : let mut circuit_a = Circuit::new();
935 4 : circuit_a += RotateX::new(self.target, CalculatorFloat::FRAC_PI_2 * (-1.0));
936 4 :
937 4 : KakDecomposition {
938 4 : global_phase: CalculatorFloat::FRAC_PI_4,
939 4 : k_vector: [
940 4 : CalculatorFloat::ZERO,
941 4 : CalculatorFloat::ZERO,
942 4 : CalculatorFloat::FRAC_PI_4,
943 4 : ],
944 4 : circuit_before: Some(circuit_b),
945 4 : circuit_after: Some(circuit_a),
946 4 : }
947 4 : }
948 : }
949 :
950 : /// The controlled-PauliZ gate.
951 : ///
952 : /// $$
953 : /// U = \begin{pmatrix}
954 : /// 1 & 0 & 0 & 0 \\\\
955 : /// 0 & 1 & 0 & 0 \\\\
956 : /// 0 & 0 & 1 & 0 \\\\
957 : /// 0 & 0 & 0 & -1
958 : /// \end{pmatrix}
959 : /// $$
960 : ///
961 : #[allow(clippy::upper_case_acronyms)]
962 : #[derive(
963 1 : Debug,
964 4 : Clone,
965 7 : PartialEq,
966 1 : roqoqo_derive::InvolveQubits,
967 24 : roqoqo_derive::Operate,
968 3 : roqoqo_derive::Substitute,
969 8 : roqoqo_derive::OperateTwoQubit,
970 : )]
971 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
972 : pub struct ControlledPauliZ {
973 : /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of PauliZ gate on the target qubit.
974 : control: usize,
975 : /// The index of the least significant qubit in the unitary representation. Here, the qubit PauliZ is applied to.
976 : target: usize,
977 : }
978 :
979 : #[allow(non_upper_case_globals)]
980 : const TAGS_ControlledPauliZ: &[&str; 4] = &[
981 : "Operation",
982 : "GateOperation",
983 : "TwoQubitGateOperation",
984 : "ControlledPauliZ",
985 : ];
986 :
987 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
988 : impl OperateGate for ControlledPauliZ {
989 : /// Returns unitary matrix of the gate.
990 : ///
991 : /// # Returns
992 : ///
993 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
994 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
995 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
996 2 : Ok(array![
997 2 : [
998 2 : Complex64::new(1.0, 0.0),
999 2 : Complex64::new(0.0, 0.0),
1000 2 : Complex64::new(0.0, 0.0),
1001 2 : Complex64::new(0.0, 0.0)
1002 2 : ],
1003 2 : [
1004 2 : Complex64::new(0.0, 0.0),
1005 2 : Complex64::new(1.0, 0.0),
1006 2 : Complex64::new(0.0, 0.0),
1007 2 : Complex64::new(0.0, 0.0)
1008 2 : ],
1009 2 : [
1010 2 : Complex64::new(0.0, 0.0),
1011 2 : Complex64::new(0.0, 0.0),
1012 2 : Complex64::new(1.0, 0.0),
1013 2 : Complex64::new(0.0, 0.0)
1014 2 : ],
1015 2 : [
1016 2 : Complex64::new(0.0, 0.0),
1017 2 : Complex64::new(0.0, 0.0),
1018 2 : Complex64::new(0.0, 0.0),
1019 2 : Complex64::new(-1.0, 0.0)
1020 2 : ],
1021 2 : ])
1022 2 : }
1023 : }
1024 :
1025 : /// Trait for all gate operations acting on exactly two qubits.
1026 : impl OperateTwoQubitGate for ControlledPauliZ {
1027 : /// Returns [KakDecomposition] of the gate.
1028 : ///
1029 : /// # Returns
1030 : ///
1031 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1032 4 : fn kak_decomposition(&self) -> KakDecomposition {
1033 4 : let mut circuit_b = Circuit::new();
1034 4 : circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
1035 4 : circuit_b += RotateZ::new(self.target, CalculatorFloat::FRAC_PI_2);
1036 4 :
1037 4 : KakDecomposition {
1038 4 : global_phase: CalculatorFloat::FRAC_PI_4,
1039 4 : k_vector: [
1040 4 : CalculatorFloat::ZERO,
1041 4 : CalculatorFloat::ZERO,
1042 4 : CalculatorFloat::FRAC_PI_4,
1043 4 : ],
1044 4 : circuit_before: Some(circuit_b),
1045 4 : circuit_after: None,
1046 4 : }
1047 4 : }
1048 : }
1049 :
1050 : /// The fixed phase MolmerSorensen XX gate.
1051 : ///
1052 : /// <http://arxiv.org/abs/1705.02771>
1053 : ///
1054 : /// $$
1055 : /// U = \frac{1}{\sqrt{2}} \begin{pmatrix}
1056 : /// 1 & 0 & 0 & -i \\\\
1057 : /// 0 &1 & -i & 0 \\\\
1058 : /// 0 & -i & 1 & 0 \\\\
1059 : /// -i & 0 & 0 & 1
1060 : /// \end{pmatrix}
1061 : /// $$
1062 : ///
1063 : #[allow(clippy::upper_case_acronyms)]
1064 : #[derive(
1065 1 : Debug,
1066 4 : Clone,
1067 7 : PartialEq,
1068 1 : roqoqo_derive::InvolveQubits,
1069 24 : roqoqo_derive::Operate,
1070 3 : roqoqo_derive::Substitute,
1071 6 : roqoqo_derive::OperateTwoQubit,
1072 : )]
1073 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1074 : pub struct MolmerSorensenXX {
1075 : /// The index of the most significant qubit in the unitary representation. The gate is symmetric under the exchange of qubits.
1076 : control: usize,
1077 : /// The index of the least significant qubit in the unitary representation. The gate is symmetric under the exchange of qubits.
1078 : target: usize,
1079 : }
1080 :
1081 : #[allow(non_upper_case_globals)]
1082 : const TAGS_MolmerSorensenXX: &[&str; 4] = &[
1083 : "Operation",
1084 : "GateOperation",
1085 : "TwoQubitGateOperation",
1086 : "MolmerSorensenXX",
1087 : ];
1088 :
1089 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1090 : impl OperateGate for MolmerSorensenXX {
1091 : /// Returns unitary matrix of the gate.
1092 : ///
1093 : /// # Returns
1094 : ///
1095 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1096 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed (here, not possible).
1097 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1098 2 : let f: f64 = 1.0 / ((2.0_f64).sqrt());
1099 2 : Ok(array![
1100 2 : [
1101 2 : Complex64::new(f, 0.0),
1102 2 : Complex64::new(0.0, 0.0),
1103 2 : Complex64::new(0.0, 0.0),
1104 2 : Complex64::new(0.0, (-1.0) * f)
1105 2 : ],
1106 2 : [
1107 2 : Complex64::new(0.0, 0.0),
1108 2 : Complex64::new(f, 0.0),
1109 2 : Complex64::new(0.0, (-1.0) * f),
1110 2 : Complex64::new(0.0, 0.0)
1111 2 : ],
1112 2 : [
1113 2 : Complex64::new(0.0, 0.0),
1114 2 : Complex64::new(0.0, (-1.0) * f),
1115 2 : Complex64::new(f, 0.0),
1116 2 : Complex64::new(0.0, 0.0)
1117 2 : ],
1118 2 : [
1119 2 : Complex64::new(0.0, (-1.0) * f),
1120 2 : Complex64::new(0.0, 0.0),
1121 2 : Complex64::new(0.0, 0.0),
1122 2 : Complex64::new(f, 0.0)
1123 2 : ],
1124 2 : ])
1125 2 : }
1126 : }
1127 :
1128 : /// Trait for all gate operations acting on exactly two qubits.
1129 : impl OperateTwoQubitGate for MolmerSorensenXX {
1130 : /// Returns [KakDecomposition] of the gate.
1131 : ///
1132 : /// # Returns
1133 : ///
1134 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1135 4 : fn kak_decomposition(&self) -> KakDecomposition {
1136 4 : KakDecomposition {
1137 4 : global_phase: CalculatorFloat::ZERO,
1138 4 : k_vector: [
1139 4 : CalculatorFloat::from((-1.0) * PI / 4.0),
1140 4 : CalculatorFloat::ZERO,
1141 4 : CalculatorFloat::ZERO,
1142 4 : ],
1143 4 : circuit_before: None,
1144 4 : circuit_after: None,
1145 4 : }
1146 4 : }
1147 : }
1148 :
1149 : /// The variable-angle MolmerSorensen XX gate.
1150 : ///
1151 : /// $$
1152 : /// U = \begin{pmatrix}
1153 : /// \cos(\theta/2) & 0 & 0 & -i \sin(\theta/2) \\\\
1154 : /// 0 & \cos(\theta/2) & -i \sin(\theta/2) & 0 \\\\
1155 : /// 0 & -i \sin(\theta/2) & \cos(\theta/2) & 0 \\\\
1156 : /// -i \sin(\theta/2) & 0 & 0 & \cos(\theta/2)
1157 : /// \end{pmatrix}
1158 : /// $$
1159 : ///
1160 : #[allow(clippy::upper_case_acronyms)]
1161 : #[derive(
1162 1 : Debug,
1163 6 : Clone,
1164 10 : PartialEq,
1165 1 : roqoqo_derive::InvolveQubits,
1166 37 : roqoqo_derive::Operate,
1167 8 : roqoqo_derive::Substitute,
1168 10 : roqoqo_derive::OperateTwoQubit,
1169 3 : roqoqo_derive::Rotate,
1170 : )]
1171 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1172 : pub struct VariableMSXX {
1173 : /// The index of the most significant qubit in the unitary representation. The gate is symmetric under the exchange of qubits.
1174 : control: usize,
1175 : /// The index of the least significant qubit in the unitary representation. The gate is symmetric under the exchange of qubits.
1176 : target: usize,
1177 : /// The rotation angle $\theta$.
1178 : theta: CalculatorFloat,
1179 : }
1180 :
1181 : #[allow(non_upper_case_globals)]
1182 : const TAGS_VariableMSXX: &[&str; 5] = &[
1183 : "Operation",
1184 : "GateOperation",
1185 : "TwoQubitGateOperation",
1186 : "Rotation",
1187 : "VariableMSXX",
1188 : ];
1189 :
1190 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1191 : impl OperateGate for VariableMSXX {
1192 : /// Returns unitary matrix of the gate.
1193 : ///
1194 : /// # Returns
1195 : ///
1196 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1197 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1198 3 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1199 3 : let c: f64 = (f64::try_from(self.theta.clone())? / 2.0).cos();
1200 3 : let s: f64 = (f64::try_from(self.theta.clone())? / 2.0).sin();
1201 3 : Ok(array![
1202 3 : [
1203 3 : Complex64::new(c, 0.0),
1204 3 : Complex64::new(0.0, 0.0),
1205 3 : Complex64::new(0.0, 0.0),
1206 3 : Complex64::new(0.0, (-1.0) * s)
1207 3 : ],
1208 3 : [
1209 3 : Complex64::new(0.0, 0.0),
1210 3 : Complex64::new(c, 0.0),
1211 3 : Complex64::new(0.0, (-1.0) * s),
1212 3 : Complex64::new(0.0, 0.0)
1213 3 : ],
1214 3 : [
1215 3 : Complex64::new(0.0, 0.0),
1216 3 : Complex64::new(0.0, (-1.0) * s),
1217 3 : Complex64::new(c, 0.0),
1218 3 : Complex64::new(0.0, 0.0)
1219 3 : ],
1220 3 : [
1221 3 : Complex64::new(0.0, (-1.0) * s),
1222 3 : Complex64::new(0.0, 0.0),
1223 3 : Complex64::new(0.0, 0.0),
1224 3 : Complex64::new(c, 0.0)
1225 3 : ],
1226 3 : ])
1227 3 : }
1228 : }
1229 :
1230 : /// Trait for all gate operations acting on exactly two qubits.
1231 : impl OperateTwoQubitGate for VariableMSXX {
1232 : /// Returns [KakDecomposition] of the gate.
1233 : ///
1234 : /// # Returns
1235 : ///
1236 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1237 8 : fn kak_decomposition(&self) -> KakDecomposition {
1238 8 : KakDecomposition {
1239 8 : global_phase: CalculatorFloat::ZERO,
1240 8 : k_vector: [
1241 8 : self.theta.clone() * (-1.0 / 2.0),
1242 8 : CalculatorFloat::ZERO,
1243 8 : CalculatorFloat::ZERO,
1244 8 : ],
1245 8 : circuit_before: None,
1246 8 : circuit_after: None,
1247 8 : }
1248 8 : }
1249 : }
1250 :
1251 : /// The Givens rotation interaction gate in big endian notation: $e^{-\mathrm{i} \theta (X_c Y_t - Y_c X_t)}\cdot e^{-i \phi Z_t/2}$.
1252 : ///
1253 : /// Where $X_c$ is the Pauli matrix $\sigma^x$ acting on the control qubit, $Y_t$ is the Pauli matrix $\sigma^y$ acting on the target qubit,
1254 : /// and $Z_t$ is the Pauli matrix $\sigma^z$ acting on the target qubit.
1255 : ///
1256 : /// The unitary matrix representation is:
1257 : /// $$
1258 : /// U = \begin{pmatrix}
1259 : /// 1 & 0 & 0 & 0 \\\\
1260 : /// 0 & \cos(\theta) \cdot e^{i \phi} & \sin(\theta) & 0 \\\\
1261 : /// 0 & -\sin(\theta) \cdot e^{i \phi} & \cos(\theta) & 0 \\\\
1262 : /// 0 & 0 & 0 & e^{i \phi}
1263 : /// \end{pmatrix}
1264 : /// $$
1265 : ///
1266 : #[allow(clippy::upper_case_acronyms)]
1267 : #[derive(
1268 1 : Debug,
1269 5 : Clone,
1270 9 : PartialEq,
1271 1 : roqoqo_derive::InvolveQubits,
1272 2 : roqoqo_derive::Operate,
1273 6 : roqoqo_derive::Substitute,
1274 8 : roqoqo_derive::OperateTwoQubit,
1275 3 : roqoqo_derive::Rotate,
1276 : )]
1277 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1278 : pub struct GivensRotation {
1279 : /// The index of the most significant qubit in the unitary representation.
1280 : control: usize,
1281 : /// The index of the least significant qubit in the unitary representation.
1282 : target: usize,
1283 : /// The rotation angle $\theta$.
1284 : theta: CalculatorFloat,
1285 : /// The phase $\phi$ of the rotation.
1286 : phi: CalculatorFloat,
1287 : }
1288 :
1289 : #[allow(non_upper_case_globals)]
1290 : const TAGS_GivensRotation: &[&str; 5] = &[
1291 : "Operation",
1292 : "GateOperation",
1293 : "TwoQubitGateOperation",
1294 : "Rotation",
1295 : "GivensRotation",
1296 : ];
1297 :
1298 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1299 : impl OperateGate for GivensRotation {
1300 : /// Returns unitary matrix of the gate.
1301 : ///
1302 : /// # Returns
1303 : ///
1304 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1305 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1306 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1307 2 : let ct: f64 = (f64::try_from(self.theta.clone())?).cos();
1308 2 : let st: f64 = (f64::try_from(self.theta.clone())?).sin();
1309 : // exp(i*phi) = cos(phi)+i*sin(phi)
1310 2 : let cp: f64 = (f64::try_from(self.phi.clone())?).cos();
1311 2 : let sp: f64 = (f64::try_from(self.phi.clone())?).sin();
1312 2 : Ok(array![
1313 2 : [
1314 2 : Complex64::new(1.0, 0.0),
1315 2 : Complex64::new(0.0, 0.0),
1316 2 : Complex64::new(0.0, 0.0),
1317 2 : Complex64::new(0.0, 0.0)
1318 2 : ],
1319 2 : [
1320 2 : Complex64::new(0.0, 0.0),
1321 2 : Complex64::new(ct * cp, ct * sp),
1322 2 : Complex64::new(st, 0.0),
1323 2 : Complex64::new(0.0, 0.0)
1324 2 : ],
1325 2 : [
1326 2 : Complex64::new(0.0, 0.0),
1327 2 : Complex64::new((-1.0) * st * cp, (-1.0) * st * sp),
1328 2 : Complex64::new(ct, 0.0),
1329 2 : Complex64::new(0.0, 0.0)
1330 2 : ],
1331 2 : [
1332 2 : Complex64::new(0.0, 0.0),
1333 2 : Complex64::new(0.0, 0.0),
1334 2 : Complex64::new(0.0, 0.0),
1335 2 : Complex64::new(cp, sp)
1336 2 : ],
1337 2 : ])
1338 2 : }
1339 : }
1340 :
1341 : /// Trait for all gate operations acting on exactly two qubits.
1342 : impl OperateTwoQubitGate for GivensRotation {
1343 : /// Returns [KakDecomposition] of the gate.
1344 : ///
1345 : /// # Returns
1346 : ///
1347 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1348 4 : fn kak_decomposition(&self) -> KakDecomposition {
1349 4 : let mut circuit_b = Circuit::new();
1350 4 : circuit_b += RotateZ::new(self.target, self.phi.clone() + (PI / 2.0));
1351 4 :
1352 4 : let mut circuit_a = Circuit::new();
1353 4 : circuit_a += RotateZ::new(self.target, CalculatorFloat::FRAC_PI_2 * (-1.0));
1354 4 :
1355 4 : KakDecomposition {
1356 4 : global_phase: self.phi.clone() / 2.0,
1357 4 : k_vector: [
1358 4 : self.theta.clone() / 2.0,
1359 4 : self.theta.clone() / 2.0,
1360 4 : CalculatorFloat::ZERO,
1361 4 : ],
1362 4 : circuit_before: Some(circuit_b),
1363 4 : circuit_after: Some(circuit_a),
1364 4 : }
1365 4 : }
1366 : }
1367 :
1368 : /// The Givens rotation interaction gate in little endian notation: $e^{-\mathrm{i} \theta (X_c Y_t -Y_c X_t)}\cdot e^{-i \phi Z_c/2}$.
1369 : ///
1370 : /// Where $X_c$ is the Pauli matrix $\sigma^x$ acting on the control qubit, $Y_t$ is the Pauli matrix $\sigma^y$ acting on the target qubit,
1371 : /// and $Z_c$ is the Pauli matrix $\sigma^z$ acting on the control qubit.
1372 : ///
1373 : /// The unitary matrix representation is:
1374 : /// $$
1375 : /// U = \begin{pmatrix}
1376 : /// 1 & 0 & 0 & 0 \\\\
1377 : /// 0 & \cos(\theta) & \sin(\theta) & 0 \\\\
1378 : /// 0 & -\sin(\theta) \cdot e^{i \phi} & \cos(\theta) \cdot e^{i \phi} & 0 \\\\
1379 : /// 0 & 0 & 0 & e^{i \phi}
1380 : /// \end{pmatrix}
1381 : /// $$
1382 : ///
1383 : #[allow(clippy::upper_case_acronyms)]
1384 : #[derive(
1385 1 : Debug,
1386 5 : Clone,
1387 9 : PartialEq,
1388 1 : roqoqo_derive::InvolveQubits,
1389 2 : roqoqo_derive::Operate,
1390 6 : roqoqo_derive::Substitute,
1391 8 : roqoqo_derive::OperateTwoQubit,
1392 3 : roqoqo_derive::Rotate,
1393 : )]
1394 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1395 : pub struct GivensRotationLittleEndian {
1396 : /// The index of the most significant qubit in the unitary representation.
1397 : control: usize,
1398 : /// The index of the least significant qubit in the unitary representation.
1399 : target: usize,
1400 : /// The rotation angle $\theta$.
1401 : theta: CalculatorFloat,
1402 : /// The phase $\phi$ of the rotation.
1403 : phi: CalculatorFloat,
1404 : }
1405 :
1406 : #[allow(non_upper_case_globals)]
1407 : const TAGS_GivensRotationLittleEndian: &[&str; 5] = &[
1408 : "Operation",
1409 : "GateOperation",
1410 : "TwoQubitGateOperation",
1411 : "Rotation",
1412 : "GivensRotationLittleEndian",
1413 : ];
1414 :
1415 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1416 : impl OperateGate for GivensRotationLittleEndian {
1417 : /// Returns unitary matrix of the gate.
1418 : ///
1419 : /// # Returns
1420 : ///
1421 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1422 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1423 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1424 2 : let ct: f64 = (f64::try_from(self.theta.clone())?).cos();
1425 2 : let st: f64 = (f64::try_from(self.theta.clone())?).sin();
1426 : // exp(i*phi) = cos(phi)+i*sin(phi)
1427 2 : let cp: f64 = (f64::try_from(self.phi.clone())?).cos();
1428 2 : let sp: f64 = (f64::try_from(self.phi.clone())?).sin();
1429 2 : Ok(array![
1430 2 : [
1431 2 : Complex64::new(1.0, 0.0),
1432 2 : Complex64::new(0.0, 0.0),
1433 2 : Complex64::new(0.0, 0.0),
1434 2 : Complex64::new(0.0, 0.0)
1435 2 : ],
1436 2 : [
1437 2 : Complex64::new(0.0, 0.0),
1438 2 : Complex64::new(ct, 0.0),
1439 2 : Complex64::new(st, 0.0),
1440 2 : Complex64::new(0.0, 0.0)
1441 2 : ],
1442 2 : [
1443 2 : Complex64::new(0.0, 0.0),
1444 2 : Complex64::new((-1.0) * st * cp, (-1.0) * st * sp),
1445 2 : Complex64::new(ct * cp, ct * sp),
1446 2 : Complex64::new(0.0, 0.0)
1447 2 : ],
1448 2 : [
1449 2 : Complex64::new(0.0, 0.0),
1450 2 : Complex64::new(0.0, 0.0),
1451 2 : Complex64::new(0.0, 0.0),
1452 2 : Complex64::new(cp, sp)
1453 2 : ],
1454 2 : ])
1455 2 : }
1456 : }
1457 :
1458 : /// Trait for all gate operations acting on exactly two qubits.
1459 : impl OperateTwoQubitGate for GivensRotationLittleEndian {
1460 : /// Returns [KakDecomposition] of the gate.
1461 : ///
1462 : /// # Returns
1463 : ///
1464 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1465 4 : fn kak_decomposition(&self) -> KakDecomposition {
1466 4 : let mut circuit_b = Circuit::new();
1467 4 : circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2 * (-1.0));
1468 4 :
1469 4 : let mut circuit_a = Circuit::new();
1470 4 : circuit_a += RotateZ::new(self.control, self.phi.clone() + (PI / 2.0));
1471 4 :
1472 4 : KakDecomposition {
1473 4 : global_phase: self.phi.clone() / 2.0,
1474 4 : k_vector: [
1475 4 : self.theta.clone() / 2.0,
1476 4 : self.theta.clone() / 2.0,
1477 4 : CalculatorFloat::ZERO,
1478 4 : ],
1479 4 : circuit_before: Some(circuit_b),
1480 4 : circuit_after: Some(circuit_a),
1481 4 : }
1482 4 : }
1483 : }
1484 :
1485 : /// The qubit simulation (Qsim) gate.
1486 : ///
1487 : /// $$
1488 : /// U = \begin{pmatrix}
1489 : /// \cos(x-y) \cdot e^{-i z} & 0 & 0 & -i\sin(x-y)\cdot e^{-i z}\\\\
1490 : /// 0 & -i \sin(x+y)\cdot e^{i z} & \cos(x+y)\cdot e^{i z} & 0 \\\\
1491 : /// 0 & \cos(x+y)\cdot e^{i z}& -i \sin(x+y)\cdot e^{i z} & 0 \\\\
1492 : /// -\sin(x-y)\cdot e^{-i z} & 0 & 0 & \cos(x-y)\cdot e^{-i z}
1493 : /// \end{pmatrix}
1494 : /// $$
1495 : ///
1496 : #[allow(clippy::upper_case_acronyms)]
1497 : #[derive(
1498 1 : Debug,
1499 4 : Clone,
1500 8 : PartialEq,
1501 1 : roqoqo_derive::InvolveQubits,
1502 2 : roqoqo_derive::Operate,
1503 6 : roqoqo_derive::Substitute,
1504 6 : roqoqo_derive::OperateTwoQubit,
1505 : )]
1506 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1507 : pub struct Qsim {
1508 : /// The index of the most significant qubit in the unitary representation.
1509 : control: usize,
1510 : /// The index of the least significant qubit in the unitary representation.
1511 : target: usize,
1512 : /// The prefactor of the XX interaction.
1513 : x: CalculatorFloat,
1514 : /// The prefactor of the YY interaction.
1515 : y: CalculatorFloat,
1516 : /// The prefactor of the ZZ interaction.
1517 : z: CalculatorFloat,
1518 : }
1519 :
1520 : #[allow(non_upper_case_globals)]
1521 : const TAGS_Qsim: &[&str; 4] = &[
1522 : "Operation",
1523 : "GateOperation",
1524 : "TwoQubitGateOperation",
1525 : "Qsim",
1526 : ];
1527 :
1528 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1529 : impl OperateGate for Qsim {
1530 : /// Returns unitary matrix of the gate.
1531 : ///
1532 : /// # Returns
1533 : ///
1534 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1535 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1536 3 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1537 3 : let x: f64 = f64::try_from(self.x.clone())?;
1538 3 : let y: f64 = f64::try_from(self.y.clone())?;
1539 3 : let z: f64 = f64::try_from(self.z.clone())?;
1540 :
1541 3 : let cm: f64 = (x - y).cos();
1542 3 : let cp: f64 = (x + y).cos();
1543 3 : let sm: f64 = (x - y).sin();
1544 3 : let sp: f64 = (x + y).sin();
1545 3 :
1546 3 : // exp(i*z) = cos(z) + i*sin(z)
1547 3 : // exp(-i*z) = cos(z) - i*sin(z)
1548 3 : let cz: f64 = z.cos();
1549 3 : let sz: f64 = z.sin();
1550 3 :
1551 3 : Ok(array![
1552 3 : [
1553 3 : Complex64::new(cm * cz, (-1.0) * cm * sz),
1554 3 : Complex64::new(0.0, 0.0),
1555 3 : Complex64::new(0.0, 0.0),
1556 3 : Complex64::new((-1.0) * sm * sz, (-1.0) * sm * cz)
1557 3 : ],
1558 3 : [
1559 3 : Complex64::new(0.0, 0.0),
1560 3 : Complex64::new(sp * sz, (-1.0) * sp * cz),
1561 3 : Complex64::new(cp * cz, cp * sz),
1562 3 : Complex64::new(0.0, 0.0)
1563 3 : ],
1564 3 : [
1565 3 : Complex64::new(0.0, 0.0),
1566 3 : Complex64::new(cp * cz, cp * sz),
1567 3 : Complex64::new(sp * sz, (-1.0) * sp * cz),
1568 3 : Complex64::new(0.0, 0.0)
1569 3 : ],
1570 3 : [
1571 3 : Complex64::new((-1.0) * sm * sz, (-1.0) * sm * cz),
1572 3 : Complex64::new(0.0, 0.0),
1573 3 : Complex64::new(0.0, 0.0),
1574 3 : Complex64::new(cm * cz, (-1.0) * cm * sz)
1575 3 : ],
1576 3 : ])
1577 3 : }
1578 : }
1579 :
1580 : /// Trait for all gate operations acting on exactly two qubits.
1581 : impl OperateTwoQubitGate for Qsim {
1582 : /// Returns [KakDecomposition] of the gate.
1583 : ///
1584 : /// # Returns
1585 : ///
1586 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1587 4 : fn kak_decomposition(&self) -> KakDecomposition {
1588 4 : KakDecomposition {
1589 4 : global_phase: CalculatorFloat::from(-1.0 * PI / 4.0),
1590 4 : k_vector: [
1591 4 : self.x.clone() * (-1.0) + PI / 4.0,
1592 4 : self.y.clone() * (-1.0) + PI / 4.0,
1593 4 : self.z.clone() * (-1.0) + PI / 4.0,
1594 4 : ],
1595 4 : circuit_before: None,
1596 4 : circuit_after: None,
1597 4 : }
1598 4 : }
1599 : }
1600 :
1601 : /// The fermionic qubit simulation (Fsim) gate.
1602 : ///
1603 : /// $$
1604 : /// U = \begin{pmatrix}
1605 : /// \cos(\Delta) & 0 & 0 & i \sin(\Delta) \\\\
1606 : /// 0 & -i \sin(t) & \cos(t) & 0 \\\\
1607 : /// 0 & \cos(t) & -i \sin(t) & 0 \\\\
1608 : /// -\sin(\Delta) \cdot e^{-i U} & 0 & 0 & -\cos(\Delta) \cdot e^{-i U}
1609 : /// \end{pmatrix}
1610 : /// $$
1611 : ///
1612 : /// # Note
1613 : /// The qubits have to be adjacent, i.e., :math:`|i-j|=1` has to hold. This is the only case
1614 : /// in which the gate is valid as a two-qubit gate (due to the Jordan-Wigner transformation).
1615 : ///
1616 : #[allow(clippy::upper_case_acronyms)]
1617 : #[derive(
1618 1 : Debug,
1619 5 : Clone,
1620 9 : PartialEq,
1621 1 : roqoqo_derive::InvolveQubits,
1622 2 : roqoqo_derive::Operate,
1623 8 : roqoqo_derive::Substitute,
1624 14 : roqoqo_derive::OperateTwoQubit,
1625 : )]
1626 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1627 : pub struct Fsim {
1628 : /// The index of the most significant qubit in the unitary representation.
1629 : control: usize,
1630 : /// The index of the least significant qubit in the unitary representation.
1631 : target: usize,
1632 : /// The hopping strength.
1633 : t: CalculatorFloat,
1634 : /// The interaction strength.
1635 : u: CalculatorFloat,
1636 : /// The Bogoliubov interaction strength $\Delta$.
1637 : delta: CalculatorFloat,
1638 : }
1639 :
1640 : #[allow(non_upper_case_globals)]
1641 : const TAGS_Fsim: &[&str; 4] = &[
1642 : "Operation",
1643 : "GateOperation",
1644 : "TwoQubitGateOperation",
1645 : "Fsim",
1646 : ];
1647 :
1648 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1649 : impl OperateGate for Fsim {
1650 : /// Returns unitary matrix of the gate.
1651 : ///
1652 : /// # Returns
1653 : ///
1654 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1655 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1656 3 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1657 3 : let t: f64 = f64::try_from(self.t.clone())?;
1658 3 : let u: f64 = f64::try_from(self.u.clone())?;
1659 3 : let d: f64 = f64::try_from(self.delta.clone())?;
1660 :
1661 3 : Ok(array![
1662 3 : [
1663 3 : Complex64::new(d.cos(), 0.0),
1664 3 : Complex64::new(0.0, 0.0),
1665 3 : Complex64::new(0.0, 0.0),
1666 3 : Complex64::new(0.0, d.sin())
1667 3 : ],
1668 3 : [
1669 3 : Complex64::new(0.0, 0.0),
1670 3 : Complex64::new(0.0, (-1.0) * t.sin()),
1671 3 : Complex64::new(t.cos(), 0.0),
1672 3 : Complex64::new(0.0, 0.0)
1673 3 : ],
1674 3 : [
1675 3 : Complex64::new(0.0, 0.0),
1676 3 : Complex64::new(t.cos(), 0.0),
1677 3 : Complex64::new(0.0, (-1.0) * t.sin()),
1678 3 : Complex64::new(0.0, 0.0)
1679 3 : ],
1680 3 : [
1681 3 : Complex64::new((-1.0) * d.sin() * u.sin(), (-1.0) * d.sin() * u.cos()),
1682 3 : Complex64::new(0.0, 0.0),
1683 3 : Complex64::new(0.0, 0.0),
1684 3 : Complex64::new((-1.0) * d.cos() * u.cos(), d.cos() * u.sin())
1685 3 : ],
1686 3 : ])
1687 3 : }
1688 : }
1689 :
1690 : /// Trait for all gate operations acting on exactly two qubits.
1691 : impl OperateTwoQubitGate for Fsim {
1692 : /// Returns [KakDecomposition] of the gate.
1693 : ///
1694 : /// # Returns
1695 : ///
1696 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1697 8 : fn kak_decomposition(&self) -> KakDecomposition {
1698 8 : let theta = self.u.clone() / (-2.0) - PI / 2.0;
1699 8 : let mut circuit_a = Circuit::new();
1700 8 : circuit_a += RotateZ::new(self.control, theta.clone());
1701 8 : circuit_a += RotateZ::new(self.target, theta);
1702 8 :
1703 8 : KakDecomposition {
1704 8 : global_phase: self.u.clone() / (-4.0) - PI / 2.0,
1705 8 : k_vector: [
1706 8 : (self.t.clone() / (-2.0) + self.delta.clone() / 2.0 + PI / 4.0),
1707 8 : (self.t.clone() / (-2.0) - self.delta.clone() / 2.0 + PI / 4.0),
1708 8 : self.u.clone() / (-4.0),
1709 8 : ],
1710 8 : circuit_before: None,
1711 8 : circuit_after: Some(circuit_a),
1712 8 : }
1713 8 : }
1714 : }
1715 :
1716 : /// The generalized, anisotropic XYZ Heisenberg interaction between spins.
1717 : ///
1718 : /// $$
1719 : /// e^{-\mathrm{i} (x \cdot X_c X_t + y \cdot Y_c Y_t + z \cdot Z_c Z_t)}
1720 : /// $$
1721 : ///
1722 : /// Where x, y, z are prefactors of the $X_c X_t$, $Y_c Y_t$, $Z_c Z_t$ Pauliproducts acting on control and target qubit,
1723 : /// with $XX \equiv \sigma_x \sigma_x$, $YY \equiv \sigma_y \sigma_y$ and $ZZ \equiv \sigma_z \sigma_z$.
1724 : ///
1725 : #[allow(clippy::upper_case_acronyms)]
1726 : #[derive(
1727 1 : Debug,
1728 4 : Clone,
1729 8 : PartialEq,
1730 1 : roqoqo_derive::InvolveQubits,
1731 2 : roqoqo_derive::Operate,
1732 6 : roqoqo_derive::Substitute,
1733 6 : roqoqo_derive::OperateTwoQubit,
1734 : )]
1735 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1736 : pub struct SpinInteraction {
1737 : /// The index of the most significant qubit in the unitary representation.
1738 : control: usize,
1739 : /// The index of the least significant qubit in the unitary representation.
1740 : target: usize,
1741 : /// The prefactor of the XX interaction.
1742 : x: CalculatorFloat,
1743 : /// The prefactor of the YY interaction.
1744 : y: CalculatorFloat,
1745 : /// The prefactor of the ZZ interaction.
1746 : z: CalculatorFloat,
1747 : }
1748 :
1749 : #[allow(non_upper_case_globals)]
1750 : const TAGS_SpinInteraction: &[&str; 4] = &[
1751 : "Operation",
1752 : "GateOperation",
1753 : "TwoQubitGateOperation",
1754 : "SpinInteraction",
1755 : ];
1756 :
1757 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1758 : impl OperateGate for SpinInteraction {
1759 : /// Returns unitary matrix of the gate.
1760 : ///
1761 : /// # Returns
1762 : ///
1763 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1764 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1765 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1766 2 : let x: f64 = f64::try_from(self.x.clone())?;
1767 2 : let y: f64 = f64::try_from(self.y.clone())?;
1768 2 : let z: f64 = f64::try_from(self.z.clone())?;
1769 :
1770 2 : let cm: f64 = (x - y).cos();
1771 2 : let cp: f64 = (x + y).cos();
1772 2 : let sm: f64 = (x - y).sin();
1773 2 : let sp: f64 = (x + y).sin();
1774 2 :
1775 2 : // exp(i*z) = cos(z) + i*sin(z)
1776 2 : // exp(-i*z) = cos(z) - i*sin(z)
1777 2 : let cz: f64 = z.cos();
1778 2 : let sz: f64 = z.sin();
1779 2 :
1780 2 : Ok(array![
1781 2 : [
1782 2 : Complex64::new(cm * cz, (-1.0) * cm * sz),
1783 2 : Complex64::new(0.0, 0.0),
1784 2 : Complex64::new(0.0, 0.0),
1785 2 : Complex64::new((-1.0) * sm * sz, (-1.0) * sm * cz)
1786 2 : ],
1787 2 : [
1788 2 : Complex64::new(0.0, 0.0),
1789 2 : Complex64::new(cp * cz, cp * sz),
1790 2 : Complex64::new(sp * sz, (-1.0) * sp * cz),
1791 2 : Complex64::new(0.0, 0.0)
1792 2 : ],
1793 2 : [
1794 2 : Complex64::new(0.0, 0.0),
1795 2 : Complex64::new(sp * sz, (-1.0) * sp * cz),
1796 2 : Complex64::new(cp * cz, cp * sz),
1797 2 : Complex64::new(0.0, 0.0)
1798 2 : ],
1799 2 : [
1800 2 : Complex64::new((-1.0) * sm * sz, (-1.0) * sm * cz),
1801 2 : Complex64::new(0.0, 0.0),
1802 2 : Complex64::new(0.0, 0.0),
1803 2 : Complex64::new(cm * cz, (-1.0) * cm * sz)
1804 2 : ],
1805 2 : ])
1806 2 : }
1807 : }
1808 :
1809 : /// Trait for all gate operations acting on exactly two qubits.
1810 : impl OperateTwoQubitGate for SpinInteraction {
1811 : /// Returns [KakDecomposition] of the gate.
1812 : ///
1813 : /// # Returns
1814 : ///
1815 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1816 4 : fn kak_decomposition(&self) -> KakDecomposition {
1817 4 : KakDecomposition {
1818 4 : global_phase: CalculatorFloat::ZERO,
1819 4 : k_vector: [
1820 4 : self.x.clone() * (-1.0),
1821 4 : self.y.clone() * (-1.0),
1822 4 : self.z.clone() * (-1.0),
1823 4 : ],
1824 4 : circuit_before: None,
1825 4 : circuit_after: None,
1826 4 : }
1827 4 : }
1828 : }
1829 :
1830 : /// The Bogoliubov DeGennes interaction gate.
1831 : ///
1832 : /// $$
1833 : /// e^{-\mathrm{i} Re(\Delta) (X_c X_t - Y_c Y_t)/2 + Im(\Delta) (X_c Y_t+Y_c X_t)/2}
1834 : /// $$
1835 : ///
1836 : /// Where $X_c$ is the Pauli matrix $\sigma^x$ acting on the control qubit, and $Y_t$ is the Pauli matrix $\sigma^y$ acting on the target qubit.
1837 : ///
1838 : /// The unitary matrix representation is:
1839 : /// $$
1840 : /// U = \begin{pmatrix}
1841 : /// \cos(|\Delta|) & 0 & 0 & \mathrm{i} \sin(|\Delta|) e^{\mathrm{i} \cdot \mathrm{angle}(\Delta)} \\\\
1842 : /// 0 & 1 & 0 & 0 \\\\
1843 : /// 0 & 0 & 1 & 0 \\\\
1844 : /// \mathrm{i} \sin(|\Delta|) e^{-\mathrm{i} \cdot \mathrm{angle}(\Delta)} & 0 & 0 & \cos(|\Delta|)
1845 : /// \end{pmatrix}
1846 : /// $$
1847 : ///
1848 : #[allow(clippy::upper_case_acronyms)]
1849 : #[derive(
1850 1 : Debug,
1851 4 : Clone,
1852 8 : PartialEq,
1853 1 : roqoqo_derive::InvolveQubits,
1854 2 : roqoqo_derive::Operate,
1855 6 : roqoqo_derive::Substitute,
1856 8 : roqoqo_derive::OperateTwoQubit,
1857 : )]
1858 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1859 : pub struct Bogoliubov {
1860 : /// The index of the most significant qubit in the unitary representation.
1861 : control: usize,
1862 : /// The index of the least significant qubit in the unitary representation.
1863 : target: usize,
1864 : /// The real part of the complex Bogoliubov interaction strength $Re(\Delta)$
1865 : delta_real: CalculatorFloat,
1866 : /// The imaginary part of the complex Bogoliubov interaction strength $Im(\Delta)$
1867 : delta_imag: CalculatorFloat,
1868 : }
1869 :
1870 : #[allow(non_upper_case_globals)]
1871 : const TAGS_Bogoliubov: &[&str; 4] = &[
1872 : "Operation",
1873 : "GateOperation",
1874 : "TwoQubitGateOperation",
1875 : "Bogoliubov",
1876 : ];
1877 :
1878 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1879 : impl OperateGate for Bogoliubov {
1880 : /// Returns unitary matrix of the gate.
1881 : ///
1882 : /// # Returns
1883 : ///
1884 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1885 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1886 4 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1887 4 : let dr: f64 = f64::try_from(self.delta_real.clone())?;
1888 4 : let di: f64 = f64::try_from(self.delta_imag.clone())?;
1889 4 : let delta: Complex64 = Complex64::new(dr, di);
1890 4 : let da: f64 = delta.norm(); //absolute value of delta
1891 4 : let dp: f64 = delta.arg(); // phase of delta
1892 4 : Ok(array![
1893 4 : [
1894 4 : Complex64::new(da.cos(), 0.0),
1895 4 : Complex64::new(0.0, 0.0),
1896 4 : Complex64::new(0.0, 0.0),
1897 4 : Complex64::new((-1.0) * da.sin() * dp.sin(), da.sin() * dp.cos())
1898 4 : ],
1899 4 : [
1900 4 : Complex64::new(0.0, 0.0),
1901 4 : Complex64::new(1.0, 0.0),
1902 4 : Complex64::new(0.0, 0.0),
1903 4 : Complex64::new(0.0, 0.0)
1904 4 : ],
1905 4 : [
1906 4 : Complex64::new(0.0, 0.0),
1907 4 : Complex64::new(0.0, 0.0),
1908 4 : Complex64::new(1.0, 0.0),
1909 4 : Complex64::new(0.0, 0.0)
1910 4 : ],
1911 4 : [
1912 4 : Complex64::new(da.sin() * dp.sin(), da.sin() * dp.cos()),
1913 4 : Complex64::new(0.0, 0.0),
1914 4 : Complex64::new(0.0, 0.0),
1915 4 : Complex64::new(da.cos(), 0.0)
1916 4 : ],
1917 4 : ])
1918 4 : }
1919 : }
1920 :
1921 : /// Trait for all gate operations acting on exactly two qubits.
1922 : impl OperateTwoQubitGate for Bogoliubov {
1923 : /// Returns [KakDecomposition] of the gate.
1924 : ///
1925 : /// # Returns
1926 : ///
1927 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
1928 4 : fn kak_decomposition(&self) -> KakDecomposition {
1929 4 : let dr = self.delta_real.clone();
1930 4 : let di = self.delta_imag.clone();
1931 4 : let delta: CalculatorComplex = CalculatorComplex::new(dr, di);
1932 4 :
1933 4 : let mut circuit_b = Circuit::new();
1934 4 : circuit_b += RotateZ::new(self.target, delta.arg());
1935 4 :
1936 4 : let mut circuit_a = Circuit::new();
1937 4 : circuit_a += RotateZ::new(self.target, delta.arg() * (-1.0));
1938 4 :
1939 4 : KakDecomposition {
1940 4 : global_phase: CalculatorFloat::ZERO,
1941 4 : k_vector: [
1942 4 : delta.norm() / (2.0),
1943 4 : delta.norm() / (-2.0),
1944 4 : CalculatorFloat::ZERO,
1945 4 : ],
1946 4 : circuit_before: Some(circuit_b),
1947 4 : circuit_after: Some(circuit_a),
1948 4 : }
1949 4 : }
1950 : }
1951 :
1952 : /// The transversal interaction gate.
1953 : ///
1954 : /// $$
1955 : /// e^{-\mathrm{i} \theta (X_c X_t + Y_c Y_t)} = e^{-\mathrm{i} \theta (\sigma^+_c \sigma^-_t + \sigma^-_c \sigma^+_t)}
1956 : /// $$
1957 : /// Where $X_c$ is the Pauli matrix $\sigma^x$ acting on the control qubit, and $Y_t$ is the Pauli matrix $\sigma^y$ acting on the target qubit.
1958 : ///
1959 : #[allow(clippy::upper_case_acronyms)]
1960 : #[derive(
1961 1 : Debug,
1962 4 : Clone,
1963 8 : PartialEq,
1964 1 : roqoqo_derive::InvolveQubits,
1965 32 : roqoqo_derive::Operate,
1966 6 : roqoqo_derive::Substitute,
1967 6 : roqoqo_derive::OperateTwoQubit,
1968 : )]
1969 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1970 : pub struct PMInteraction {
1971 : /// The index of the most significant qubit in the unitary representation.
1972 : control: usize,
1973 : /// The index of the least significant qubit in the unitary representation.
1974 : target: usize,
1975 : /// The strength of the rotation $\theta$.
1976 : t: CalculatorFloat,
1977 : }
1978 :
1979 : #[allow(non_upper_case_globals)]
1980 : const TAGS_PMInteraction: &[&str; 4] = &[
1981 : "Operation",
1982 : "GateOperation",
1983 : "TwoQubitGateOperation",
1984 : "PMInteraction",
1985 : ];
1986 :
1987 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
1988 : impl OperateGate for PMInteraction {
1989 : /// Returns unitary matrix of the gate.
1990 : ///
1991 : /// # Returns
1992 : ///
1993 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
1994 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
1995 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
1996 2 : let c: f64 = (f64::try_from(self.t.clone())?).cos();
1997 2 : let s: f64 = (f64::try_from(self.t.clone())?).sin();
1998 2 : Ok(array![
1999 2 : [
2000 2 : Complex64::new(1.0, 0.0),
2001 2 : Complex64::new(0.0, 0.0),
2002 2 : Complex64::new(0.0, 0.0),
2003 2 : Complex64::new(0.0, 0.0)
2004 2 : ],
2005 2 : [
2006 2 : Complex64::new(0.0, 0.0),
2007 2 : Complex64::new(c, 0.0),
2008 2 : Complex64::new(0.0, (-1.0) * s),
2009 2 : Complex64::new(0.0, 0.0)
2010 2 : ],
2011 2 : [
2012 2 : Complex64::new(0.0, 0.0),
2013 2 : Complex64::new(0.0, (-1.0) * s),
2014 2 : Complex64::new(c, 0.0),
2015 2 : Complex64::new(0.0, 0.0)
2016 2 : ],
2017 2 : [
2018 2 : Complex64::new(0.0, 0.0),
2019 2 : Complex64::new(0.0, 0.0),
2020 2 : Complex64::new(0.0, 0.0),
2021 2 : Complex64::new(1.0, 0.0)
2022 2 : ],
2023 2 : ])
2024 2 : }
2025 : }
2026 :
2027 : /// Trait for all gate operations acting on exactly two qubits.
2028 : impl OperateTwoQubitGate for PMInteraction {
2029 : /// Returns [KakDecomposition] of the gate.
2030 : ///
2031 : /// # Returns
2032 : ///
2033 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2034 4 : fn kak_decomposition(&self) -> KakDecomposition {
2035 4 : KakDecomposition {
2036 4 : global_phase: CalculatorFloat::ZERO,
2037 4 : k_vector: [
2038 4 : self.t.clone() / (-2.0),
2039 4 : self.t.clone() / (-2.0),
2040 4 : CalculatorFloat::ZERO,
2041 4 : ],
2042 4 : circuit_before: None,
2043 4 : circuit_after: None,
2044 4 : }
2045 4 : }
2046 : }
2047 :
2048 : /// The complex hopping gate.
2049 : ///
2050 : /// $$
2051 : /// e^{-\mathrm{i} \left[ Re(\theta) \cdot (X_c X_t + Y_c Y_t) - Im(\theta) \cdot (X_c Y_t - Y_c X_t) \right] }
2052 : /// $$
2053 : /// Where $X_c$ is the Pauli matrix $\sigma^x$ acting on the control qubit, and $Y_t$ is the Pauli matrix $\sigma^y$ acting on the target qubit.
2054 : ///
2055 : #[allow(clippy::upper_case_acronyms)]
2056 : #[derive(
2057 1 : Debug,
2058 4 : Clone,
2059 8 : PartialEq,
2060 1 : roqoqo_derive::InvolveQubits,
2061 2 : roqoqo_derive::Operate,
2062 6 : roqoqo_derive::Substitute,
2063 8 : roqoqo_derive::OperateTwoQubit,
2064 : )]
2065 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2066 : pub struct ComplexPMInteraction {
2067 : /// The index of the most significant qubit in the unitary representation.
2068 : control: usize,
2069 : /// The index of the least significant qubit in the unitary representation.
2070 : target: usize,
2071 : /// The real part of the strength of the rotation $Re(\theta)$.
2072 : t_real: CalculatorFloat,
2073 : /// The imaginary part of the strength of the rotation $Im(\theta)$.
2074 : t_imag: CalculatorFloat,
2075 : }
2076 :
2077 : #[allow(non_upper_case_globals)]
2078 : const TAGS_ComplexPMInteraction: &[&str; 4] = &[
2079 : "Operation",
2080 : "GateOperation",
2081 : "TwoQubitGateOperation",
2082 : "ComplexPMInteraction",
2083 : ];
2084 :
2085 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
2086 : impl OperateGate for ComplexPMInteraction {
2087 : /// Returns unitary matrix of the gate.
2088 : ///
2089 : /// # Returns
2090 : ///
2091 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
2092 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
2093 2 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
2094 2 : let tr: f64 = f64::try_from(self.t_real.clone())?;
2095 2 : let ti: f64 = f64::try_from(self.t_imag.clone())?;
2096 2 : let t: Complex64 = Complex64::new(tr, ti);
2097 2 : let tn: f64 = t.norm(); //absolute value of delta
2098 2 : let ta: f64 = t.arg(); // phase of delta
2099 2 : Ok(array![
2100 2 : [
2101 2 : Complex64::new(1.0, 0.0),
2102 2 : Complex64::new(0.0, 0.0),
2103 2 : Complex64::new(0.0, 0.0),
2104 2 : Complex64::new(0.0, 0.0)
2105 2 : ],
2106 2 : [
2107 2 : Complex64::new(0.0, 0.0),
2108 2 : Complex64::new(tn.cos(), 0.0),
2109 2 : Complex64::new((-1.0) * tn.sin() * ta.sin(), (-1.0) * tn.sin() * ta.cos()),
2110 2 : Complex64::new(0.0, 0.0)
2111 2 : ],
2112 2 : [
2113 2 : Complex64::new(0.0, 0.0),
2114 2 : Complex64::new(tn.sin() * ta.sin(), (-1.0) * tn.sin() * ta.cos()),
2115 2 : Complex64::new(tn.cos(), 0.0),
2116 2 : Complex64::new(0.0, 0.0)
2117 2 : ],
2118 2 : [
2119 2 : Complex64::new(0.0, 0.0),
2120 2 : Complex64::new(0.0, 0.0),
2121 2 : Complex64::new(0.0, 0.0),
2122 2 : Complex64::new(1.0, 0.0)
2123 2 : ],
2124 2 : ])
2125 2 : }
2126 : }
2127 :
2128 : /// Trait for all gate operations acting on exactly two qubits.
2129 : impl OperateTwoQubitGate for ComplexPMInteraction {
2130 : /// Returns [KakDecomposition] of the gate.
2131 : ///
2132 : /// # Returns
2133 : ///
2134 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2135 4 : fn kak_decomposition(&self) -> KakDecomposition {
2136 4 : let tr = self.t_real.clone();
2137 4 : let ti = self.t_imag.clone();
2138 4 : let t: CalculatorComplex = CalculatorComplex::new(tr, ti);
2139 4 :
2140 4 : let mut circuit_b = Circuit::new();
2141 4 : circuit_b += RotateZ::new(self.target, t.arg());
2142 4 :
2143 4 : let mut circuit_a = Circuit::new();
2144 4 : circuit_a += RotateZ::new(self.target, t.arg() * (-1.0));
2145 4 :
2146 4 : KakDecomposition {
2147 4 : global_phase: CalculatorFloat::ZERO,
2148 4 : k_vector: [t.norm() / (-2.0), t.norm() / (-2.0), CalculatorFloat::ZERO],
2149 4 : circuit_before: Some(circuit_b),
2150 4 : circuit_after: Some(circuit_a),
2151 4 : }
2152 4 : }
2153 : }
2154 :
2155 : /// Implements the phased-shifted controlled-Z gate.
2156 : ///
2157 : /// Modified, i.e. phase-shifted ControlledPauliZ two-qubit gate (https://arxiv.org/pdf/1908.06101.pdf eq.(1)).
2158 : /// The unitary matrix representation is:
2159 : ///
2160 : /// $$
2161 : /// U = \begin{pmatrix}
2162 : /// 1 & 0 & 0 & 0 \\\\
2163 : /// 0 & e^{i \phi} & 0 & 0 \\\\
2164 : /// 0 & 0 & e^{i \phi} & 0 \\\\
2165 : /// 0 & 0 & 0 & e^{i (2\cdot\phi - \pi)}
2166 : /// \end{pmatrix}
2167 : /// $$
2168 : ///
2169 : #[allow(clippy::upper_case_acronyms)]
2170 : #[derive(
2171 1 : Debug,
2172 4 : Clone,
2173 8 : PartialEq,
2174 1 : roqoqo_derive::InvolveQubits,
2175 36 : roqoqo_derive::Operate,
2176 6 : roqoqo_derive::Substitute,
2177 26 : roqoqo_derive::OperateTwoQubit,
2178 : )]
2179 : #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2180 : pub struct PhaseShiftedControlledZ {
2181 : /// The index of the most significant qubit in the unitary representation. Here, the qubit that controls the application of the phase-shift on the target qubit.
2182 : control: usize,
2183 : /// The index of the least significant qubit in the unitary representation. Here, the qubit phase-shift is applied to.
2184 : target: usize,
2185 : /// The single qubit phase $\phi$.
2186 : phi: CalculatorFloat,
2187 : }
2188 :
2189 : #[allow(non_upper_case_globals)]
2190 : const TAGS_PhaseShiftedControlledZ: &[&str; 4] = &[
2191 : "Operation",
2192 : "GateOperation",
2193 : "TwoQubitGateOperation",
2194 : "PhaseShiftedControlledZ",
2195 : ];
2196 :
2197 : /// Trait for all Operations acting with a unitary gate on a set of qubits.
2198 : impl OperateGate for PhaseShiftedControlledZ {
2199 : /// Returns unitary matrix of the gate.
2200 : ///
2201 : /// # Returns
2202 : ///
2203 : /// * `Ok(Array2<Complex64>)` - The unitary matrix representation of the gate.
2204 : /// * `Err(RoqoqoError)` - The conversion of parameters to f64 failed.
2205 6 : fn unitary_matrix(&self) -> Result<Array2<Complex64>, RoqoqoError> {
2206 : // exp(i*x) = cos(x)+i*sin(x)
2207 6 : let phi: f64 = f64::try_from(self.phi.clone())?;
2208 6 : let cos: f64 = phi.cos();
2209 6 : let sin: f64 = phi.sin();
2210 6 : let cos2: f64 = (2.0 * phi - PI).cos();
2211 6 : let sin2: f64 = (2.0 * phi - PI).sin();
2212 6 : Ok(array![
2213 6 : [
2214 6 : Complex64::new(1.0, 0.0),
2215 6 : Complex64::new(0.0, 0.0),
2216 6 : Complex64::new(0.0, 0.0),
2217 6 : Complex64::new(0.0, 0.0)
2218 6 : ],
2219 6 : [
2220 6 : Complex64::new(0.0, 0.0),
2221 6 : Complex64::new(cos, sin),
2222 6 : Complex64::new(0.0, 0.0),
2223 6 : Complex64::new(0.0, 0.0)
2224 6 : ],
2225 6 : [
2226 6 : Complex64::new(0.0, 0.0),
2227 6 : Complex64::new(0.0, 0.0),
2228 6 : Complex64::new(cos, sin),
2229 6 : Complex64::new(0.0, 0.0)
2230 6 : ],
2231 6 : [
2232 6 : Complex64::new(0.0, 0.0),
2233 6 : Complex64::new(0.0, 0.0),
2234 6 : Complex64::new(0.0, 0.0),
2235 6 : Complex64::new(cos2, sin2)
2236 6 : ],
2237 6 : ])
2238 6 : }
2239 : }
2240 :
2241 : /// Trait for all gate operations acting on exactly two qubits.
2242 : impl OperateTwoQubitGate for PhaseShiftedControlledZ {
2243 : /// Returns [KakDecomposition] of the gate.
2244 : ///
2245 : /// # Returns
2246 : ///
2247 : /// * struct `KakDecomposition { global_phase, k_vector, circuit_before, circuit_after }`
2248 12 : fn kak_decomposition(&self) -> KakDecomposition {
2249 12 : let mut circuit_b = Circuit::new();
2250 12 : circuit_b += RotateZ::new(self.control, CalculatorFloat::FRAC_PI_2);
2251 12 : circuit_b += RotateZ::new(self.target, CalculatorFloat::FRAC_PI_2);
2252 12 :
2253 12 : let mut circuit_a = Circuit::new();
2254 12 : circuit_a += RotateZ::new(self.control, self.phi.clone());
2255 12 : circuit_a += RotateZ::new(self.target, self.phi.clone());
2256 12 :
2257 12 : let g: CalculatorFloat = CalculatorFloat::FRAC_PI_4 + self.phi.clone();
2258 12 : KakDecomposition {
2259 12 : global_phase: g,
2260 12 : k_vector: [
2261 12 : CalculatorFloat::ZERO,
2262 12 : CalculatorFloat::ZERO,
2263 12 : CalculatorFloat::FRAC_PI_4,
2264 12 : ],
2265 12 : circuit_before: Some(circuit_b),
2266 12 : circuit_after: Some(circuit_a),
2267 12 : }
2268 12 : }
2269 : }
|