Coverage for qml_essentials/ansaetze.py: 65%
444 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-15 15:48 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-15 15:48 +0000
1from abc import ABC, abstractmethod
2from typing import Any, Optional
3import pennylane.numpy as np
4import pennylane as qml
6from typing import List, Union, Dict
8import logging
10log = logging.getLogger(__name__)
13class Circuit(ABC):
14 def __init__(self):
15 pass
17 @abstractmethod
18 def n_params_per_layer(n_qubits: int) -> int:
19 return
21 @abstractmethod
22 def get_control_indices(self, n_qubits: int) -> List[int]:
23 """
24 Returns the indices for the controlled rotation gates for one layer.
25 Indices should slice the list of all parameters for one layer as follows:
26 [indices[0]:indices[1]:indices[2]]
28 Parameters
29 ----------
30 n_qubits : int
31 Number of qubits in the circuit
33 Returns
34 -------
35 Optional[np.ndarray]
36 List of all controlled indices, or None if the circuit does not
37 contain controlled rotation gates.
38 """
39 return
41 def get_control_angles(self, w: np.ndarray, n_qubits: int) -> Optional[np.ndarray]:
42 """
43 Returns the angles for the controlled rotation gates from the list of
44 all parameters for one layer.
46 Parameters
47 ----------
48 w : np.ndarray
49 List of parameters for one layer
50 n_qubits : int
51 Number of qubits in the circuit
53 Returns
54 -------
55 Optional[np.ndarray]
56 List of all controlled parameters, or None if the circuit does not
57 contain controlled rotation gates.
58 """
59 indices = self.get_control_indices(n_qubits)
60 if indices is None:
61 return None
63 return w[indices[0] : indices[1] : indices[2]]
65 @abstractmethod
66 def build(self, n_qubits: int, n_layers: int):
67 return
69 def __call__(self, *args: Any, **kwds: Any) -> Any:
70 self.build(*args, **kwds)
73class Gates:
74 rng = np.random.default_rng()
76 @staticmethod
77 def init_rng(seed: int):
78 """
79 Initializes the random number generator with the given seed.
81 Parameters
82 ----------
83 seed : int
84 The seed for the random number generator.
85 """
86 Gates.rng = np.random.default_rng(seed)
88 @staticmethod
89 def Noise(
90 wires: Union[int, List[int]], noise_params: Optional[Dict[str, float]] = None
91 ) -> None:
92 """
93 Applies noise to the given wires.
95 Parameters
96 ----------
97 wires : Union[int, List[int]]
98 The wire(s) to apply the noise to.
99 noise_params : Optional[Dict[str, float]]
100 A dictionary of noise parameters. The following noise gates are
101 supported:
102 -BitFlip: Applies a bit flip error to the given wires.
103 -PhaseFlip: Applies a phase flip error to the given wires.
104 -Depolarizing: Applies a depolarizing channel error to the
105 given wires.
107 All parameters are optional and default to 0.0 if not provided.
108 """
109 if noise_params is not None:
110 if isinstance(wires, int):
111 wires = [wires] # single qubit gate
112 # iterate for multi qubit gates
113 for wire in wires:
114 qml.BitFlip(noise_params.get("BitFlip", 0.0), wires=wire)
115 qml.PhaseFlip(noise_params.get("PhaseFlip", 0.0), wires=wire)
116 qml.DepolarizingChannel(
117 noise_params.get("Depolarizing", 0.0), wires=wire
118 )
120 @staticmethod
121 def GateError(
122 w: np.ndarray, noise_params: Optional[Dict[str, float]] = None
123 ) -> np.ndarray:
124 """
125 Applies a gate error to the given rotation angle(s).
127 Parameters
128 ----------
129 w : np.ndarray
130 The rotation angle(s) in radians.
131 noise_params : Optional[Dict[str, float]]
132 A dictionary of noise parameters. The following noise gates are
133 supported:
134 -GateError: Applies a normal distribution error to the rotation
135 angle(s). The standard deviation of the noise is specified by
136 the "GateError" key in the dictionary.
138 All parameters are optional and default to 0.0 if not provided.
140 Returns
141 -------
142 np.ndarray
143 The modified rotation angle(s) after applying the gate error.
144 """
145 if noise_params is not None:
146 w += Gates.rng.normal(0, noise_params["GateError"], w.shape)
147 return w
149 @staticmethod
150 def Rot(phi, theta, omega, wires, noise_params=None):
151 """
152 Applies a rotation gate to the given wires and adds `Noise`
154 Parameters
155 ----------
156 phi : float
157 The first rotation angle in radians.
158 theta : float
159 The second rotation angle in radians.
160 omega : float
161 The third rotation angle in radians.
162 wires : Union[int, List[int]]
163 The wire(s) to apply the rotation gate to.
164 noise_params : Optional[Dict[str, float]]
165 A dictionary of noise parameters. The following noise gates are
166 supported:
167 -BitFlip: Applies a bit flip error to the given wires.
168 -PhaseFlip: Applies a phase flip error to the given wires.
169 -Depolarizing: Applies a depolarizing channel error to the
170 given wires.
172 All parameters are optional and default to 0.0 if not provided.
173 """
174 if noise_params is not None and "GateError" in noise_params:
175 phi += Gates.rng.normal(0, noise_params["GateError"])
176 theta += Gates.rng.normal(0, noise_params["GateError"])
177 omega += Gates.rng.normal(0, noise_params["GateError"])
178 qml.Rot(phi, theta, omega, wires=wires)
179 Gates.Noise(wires, noise_params)
181 @staticmethod
182 def RX(w, wires, noise_params=None):
183 """
184 Applies a rotation around the X axis to the given wires and adds `Noise`
186 Parameters
187 ----------
188 w : float
189 The rotation angle in radians.
190 wires : Union[int, List[int]]
191 The wire(s) to apply the rotation gate to.
192 noise_params : Optional[Dict[str, float]]
193 A dictionary of noise parameters. The following noise gates are
194 supported:
195 -BitFlip: Applies a bit flip error to the given wires.
196 -PhaseFlip: Applies a phase flip error to the given wires.
197 -Depolarizing: Applies a depolarizing channel error to the
198 given wires.
200 All parameters are optional and default to 0.0 if not provided.
201 """
202 qml.RX(w, wires=wires)
203 Gates.Noise(wires, noise_params)
205 @staticmethod
206 def RY(w, wires, noise_params=None):
207 """
208 Applies a rotation around the Y axis to the given wires and adds `Noise`
210 Parameters
211 ----------
212 w : float
213 The rotation angle in radians.
214 wires : Union[int, List[int]]
215 The wire(s) to apply the rotation gate to.
216 noise_params : Optional[Dict[str, float]]
217 A dictionary of noise parameters. The following noise gates are
218 supported:
219 -BitFlip: Applies a bit flip error to the given wires.
220 -PhaseFlip: Applies a phase flip error to the given wires.
221 -Depolarizing: Applies a depolarizing channel error to the
222 given wires.
224 All parameters are optional and default to 0.0 if not provided.
225 """
226 w = Gates.GateError(w, noise_params)
227 qml.RY(w, wires=wires)
228 Gates.Noise(wires, noise_params)
230 @staticmethod
231 def RZ(w, wires, noise_params=None):
232 """
233 Applies a rotation around the Z axis to the given wires and adds `Noise`
235 Parameters
236 ----------
237 w : float
238 The rotation angle in radians.
239 wires : Union[int, List[int]]
240 The wire(s) to apply the rotation gate to.
241 noise_params : Optional[Dict[str, float]]
242 A dictionary of noise parameters. The following noise gates are
243 supported:
244 -BitFlip: Applies a bit flip error to the given wires.
245 -PhaseFlip: Applies a phase flip error to the given wires.
246 -Depolarizing: Applies a depolarizing channel error to the
247 given wires.
249 All parameters are optional and default to 0.0 if not provided.
250 """
251 qml.RZ(w, wires=wires)
252 Gates.Noise(wires, noise_params)
254 @staticmethod
255 def CRX(w, wires, noise_params=None):
256 """
257 Applies a controlled rotation around the X axis to the given wires
258 and adds `Noise`
260 Parameters
261 ----------
262 w : float
263 The rotation angle in radians.
264 wires : Union[int, List[int]]
265 The wire(s) to apply the controlled rotation gate to.
266 noise_params : Optional[Dict[str, float]]
267 A dictionary of noise parameters. The following noise gates are
268 supported:
269 -BitFlip: Applies a bit flip error to the given wires.
270 -PhaseFlip: Applies a phase flip error to the given wires.
271 -Depolarizing: Applies a depolarizing channel error to the
272 given wires.
274 All parameters are optional and default to 0.0 if not provided.
275 """
276 w = Gates.GateError(w, noise_params)
277 qml.CRX(w, wires=wires)
278 Gates.Noise(wires, noise_params)
280 @staticmethod
281 def CRY(w, wires, noise_params=None):
282 """
283 Applies a controlled rotation around the Y axis to the given wires
284 and adds `Noise`
286 Parameters
287 ----------
288 w : float
289 The rotation angle in radians.
290 wires : Union[int, List[int]]
291 The wire(s) to apply the controlled rotation gate to.
292 noise_params : Optional[Dict[str, float]]
293 A dictionary of noise parameters. The following noise gates are
294 supported:
295 -BitFlip: Applies a bit flip error to the given wires.
296 -PhaseFlip: Applies a phase flip error to the given wires.
297 -Depolarizing: Applies a depolarizing channel error to the
298 given wires.
300 All parameters are optional and default to 0.0 if not provided.
301 """
302 w = Gates.GateError(w, noise_params)
303 qml.CRY(w, wires=wires)
304 Gates.Noise(wires, noise_params)
306 @staticmethod
307 def CRZ(w, wires, noise_params=None):
308 """
309 Applies a controlled rotation around the Z axis to the given wires
310 and adds `Noise`
312 Parameters
313 ----------
314 w : float
315 The rotation angle in radians.
316 wires : Union[int, List[int]]
317 The wire(s) to apply the controlled rotation gate to.
318 noise_params : Optional[Dict[str, float]]
319 A dictionary of noise parameters. The following noise gates are
320 supported:
321 -BitFlip: Applies a bit flip error to the given wires.
322 -PhaseFlip: Applies a phase flip error to the given wires.
323 -Depolarizing: Applies a depolarizing channel error to the
324 given wires.
326 All parameters are optional and default to 0.0 if not provided.
327 """
328 w = Gates.GateError(w, noise_params)
329 qml.CRZ(w, wires=wires)
330 Gates.Noise(wires, noise_params)
332 @staticmethod
333 def CX(wires, noise_params=None):
334 """
335 Applies a controlled NOT gate to the given wires and adds `Noise`
337 Parameters
338 ----------
339 wires : Union[int, List[int]]
340 The wire(s) to apply the controlled NOT gate to.
341 noise_params : Optional[Dict[str, float]]
342 A dictionary of noise parameters. The following noise gates are
343 supported:
344 -BitFlip: Applies a bit flip error to the given wires.
345 -PhaseFlip: Applies a phase flip error to the given wires.
346 -Depolarizing: Applies a depolarizing channel error to the
347 given wires.
349 All parameters are optional and default to 0.0 if not provided.
350 """
351 qml.CNOT(wires=wires)
352 Gates.Noise(wires, noise_params)
354 @staticmethod
355 def CY(wires, noise_params=None):
356 """
357 Applies a controlled Y gate to the given wires and adds `Noise`
359 Parameters
360 ----------
361 wires : Union[int, List[int]]
362 The wire(s) to apply the controlled Y gate to.
363 noise_params : Optional[Dict[str, float]]
364 A dictionary of noise parameters. The following noise gates are
365 supported:
366 -BitFlip: Applies a bit flip error to the given wires.
367 -PhaseFlip: Applies a phase flip error to the given wires.
368 -Depolarizing: Applies a depolarizing channel error to the
369 given wires.
371 All parameters are optional and default to 0.0 if not provided.
372 """
373 qml.CY(wires=wires)
374 Gates.Noise(wires, noise_params)
376 @staticmethod
377 def CZ(wires, noise_params=None):
378 """
379 Applies a controlled Z gate to the given wires and adds `Noise`
381 Parameters
382 ----------
383 wires : Union[int, List[int]]
384 The wire(s) to apply the controlled Z gate to.
385 noise_params : Optional[Dict[str, float]]
386 A dictionary of noise parameters. The following noise gates are
387 supported:
388 -BitFlip: Applies a bit flip error to the given wires.
389 -PhaseFlip: Applies a phase flip error to the given wires.
390 -Depolarizing: Applies a depolarizing channel error to the
391 given wires.
393 All parameters are optional and default to 0.0 if not provided.
394 """
395 qml.CZ(wires=wires)
396 Gates.Noise(wires, noise_params)
398 @staticmethod
399 def H(wires, noise_params=None):
400 """
401 Applies a Hadamard gate to the given wires and adds `Noise`
403 Parameters
404 ----------
405 wires : Union[int, List[int]]
406 The wire(s) to apply the Hadamard gate to.
407 noise_params : Optional[Dict[str, float]]
408 A dictionary of noise parameters. The following noise gates are
409 supported:
410 -BitFlip: Applies a bit flip error to the given wires.
411 -PhaseFlip: Applies a phase flip error to the given wires.
412 -Depolarizing: Applies a depolarizing channel error to the
413 given wires.
415 All parameters are optional and default to 0.0 if not provided.
416 """
417 qml.Hadamard(wires=wires)
418 Gates.Noise(wires, noise_params)
421class Ansaetze:
423 def get_available():
424 return [
425 Ansaetze.No_Ansatz,
426 Ansaetze.Circuit_1,
427 Ansaetze.Circuit_2,
428 Ansaetze.Circuit_3,
429 Ansaetze.Circuit_4,
430 Ansaetze.Circuit_6,
431 Ansaetze.Circuit_9,
432 Ansaetze.Circuit_10,
433 Ansaetze.Circuit_15,
434 Ansaetze.Circuit_16,
435 Ansaetze.Circuit_17,
436 Ansaetze.Circuit_18,
437 Ansaetze.Circuit_19,
438 Ansaetze.No_Entangling,
439 Ansaetze.Strongly_Entangling,
440 Ansaetze.Hardware_Efficient,
441 Ansaetze.GHZ,
442 ]
444 class No_Ansatz(Circuit):
445 @staticmethod
446 def n_params_per_layer(n_qubits: int) -> int:
447 return 0
449 @staticmethod
450 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
451 return None
453 @staticmethod
454 def build(w: np.ndarray, n_qubits: int, noise_params=None):
455 pass
457 class GHZ(Circuit):
458 @staticmethod
459 def n_params_per_layer(n_qubits: int) -> int:
460 return 0
462 @staticmethod
463 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
464 return None
466 @staticmethod
467 def build(w: np.ndarray, n_qubits: int, noise_params=None):
468 Gates.H(0, noise_params=noise_params)
470 for q in range(n_qubits - 1):
471 Gates.CX([q, q + 1], noise_params=noise_params)
473 class Hardware_Efficient(Circuit):
474 @staticmethod
475 def n_params_per_layer(n_qubits: int) -> int:
476 """
477 Returns the number of parameters per layer for the
478 Hardware Efficient Ansatz.
480 The number of parameters is 3 times the number of qubits when there
481 is more than one qubit, as each qubit contributes 3 parameters.
482 If the number of qubits is less than 2, a warning is logged since
483 no entanglement is possible, and a fixed number of 2 parameters is used.
485 Parameters
486 ----------
487 n_qubits : int
488 Number of qubits in the circuit
490 Returns
491 -------
492 int
493 Number of parameters required for one layer of the circuit
494 """
495 if n_qubits < 2:
496 log.warning("Number of Qubits < 2, no entanglement available")
497 return n_qubits * 3
499 @staticmethod
500 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
501 """
502 No controlled rotation gates available. Always None.
504 Parameters
505 ----------
506 n_qubits : int
507 Number of qubits in the circuit
509 Returns
510 -------
511 Optional[np.ndarray]
512 List of all controlled indices, or None if the circuit does not
513 contain controlled rotation gates.
514 """
515 return None
517 @staticmethod
518 def build(w: np.ndarray, n_qubits: int, noise_params=None):
519 """
520 Creates a Hardware-Efficient ansatz, as proposed in
521 https://arxiv.org/pdf/2309.03279
523 Parameters
524 ----------
525 w : np.ndarray
526 Weight vector of size n_qubits*3
527 n_qubits : int
528 Number of qubits
529 noise_params : Optional[Dict[str, float]], optional
530 Dictionary of noise parameters to apply to the gates
531 """
532 w_idx = 0
533 for q in range(n_qubits):
534 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
535 w_idx += 1
536 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
537 w_idx += 1
538 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
539 w_idx += 1
541 if n_qubits > 1:
542 for q in range(n_qubits // 2):
543 Gates.CX(wires=[(2 * q), (2 * q + 1)], noise_params=noise_params)
544 for q in range((n_qubits - 1) // 2):
545 Gates.CX(
546 wires=[(2 * q + 1), (2 * q + 2)], noise_params=noise_params
547 )
548 if n_qubits > 2:
549 Gates.CX(wires=[(n_qubits - 1), 0], noise_params=noise_params)
551 class Circuit_19(Circuit):
552 @staticmethod
553 def n_params_per_layer(n_qubits: int) -> int:
554 """
555 Returns the number of parameters per layer for Circuit_19.
557 The number of parameters is 3 times the number of qubits when there
558 is more than one qubit, as each qubit contributes 3 parameters.
559 If the number of qubits is less than 2, a warning is logged since
560 no entanglement is possible, and a fixed number of 2 parameters is used.
562 Parameters
563 ----------
564 n_qubits : int
565 Number of qubits in the circuit
567 Returns
568 -------
569 int
570 Number of parameters required for one layer of the circuit
571 """
573 if n_qubits > 1:
574 return n_qubits * 3
575 else:
576 log.warning("Number of Qubits < 2, no entanglement available")
577 return 2
579 @staticmethod
580 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
581 """
582 Returns the indices for the controlled rotation gates for one layer.
583 Indices should slice the list of all parameters for one layer as follows:
584 [indices[0]:indices[1]:indices[2]]
586 Parameters
587 ----------
588 n_qubits : int
589 Number of qubits in the circuit
591 Returns
592 -------
593 Optional[np.ndarray]
594 List of all controlled indices, or None if the circuit does not
595 contain controlled rotation gates.
596 """
597 if n_qubits > 1:
598 return [-n_qubits, None, None]
599 else:
600 return None
602 @staticmethod
603 def build(w: np.ndarray, n_qubits: int, noise_params=None):
604 """
605 Creates a Circuit19 ansatz.
607 Length of flattened vector must be n_qubits*3
608 because for >1 qubits there are three gates
610 Parameters
611 ----------
612 w : np.ndarray
613 Weight vector of size n_qubits*3
614 n_qubits : int
615 Number of qubits
616 noise_params : Optional[Dict[str, float]], optional
617 Dictionary of noise parameters to apply to the gates
618 """
619 w_idx = 0
620 for q in range(n_qubits):
621 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
622 w_idx += 1
623 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
624 w_idx += 1
626 if n_qubits > 1:
627 for q in range(n_qubits):
628 Gates.CRX(
629 w[w_idx],
630 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
631 noise_params=noise_params,
632 )
633 w_idx += 1
635 class Circuit_18(Circuit):
636 @staticmethod
637 def n_params_per_layer(n_qubits: int) -> int:
638 """
639 Returns the number of parameters per layer for Circuit_18.
641 The number of parameters is 3 times the number of qubits when there
642 is more than one qubit, as each qubit contributes 3 parameters.
643 If the number of qubits is less than 2, a warning is logged since
644 no entanglement is possible, and a fixed number of 2 parameters is used.
646 Parameters
647 ----------
648 n_qubits : int
649 Number of qubits in the circuit
651 Returns
652 -------
653 int
654 Number of parameters required for one layer of the circuit
655 """
656 if n_qubits > 1:
657 return n_qubits * 3
658 else:
659 log.warning("Number of Qubits < 2, no entanglement available")
660 return 2
662 @staticmethod
663 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
664 """
665 Returns the indices for the controlled rotation gates for one layer.
666 Indices should slice the list of all parameters for one layer as follows:
667 [indices[0]:indices[1]:indices[2]]
669 Parameters
670 ----------
671 n_qubits : int
672 Number of qubits in the circuit
674 Returns
675 -------
676 Optional[np.ndarray]
677 List of all controlled indices, or None if the circuit does not
678 contain controlled rotation gates.
679 """
680 if n_qubits > 1:
681 return [-n_qubits, None, None]
682 else:
683 return None
685 @staticmethod
686 def build(w: np.ndarray, n_qubits: int, noise_params=None):
687 """
688 Creates a Circuit18 ansatz.
690 Length of flattened vector must be n_qubits*3
692 Parameters
693 ----------
694 w : np.ndarray
695 Weight vector of size n_qubits*3
696 n_qubits : int
697 Number of qubits
698 noise_params : Optional[Dict[str, float]], optional
699 Dictionary of noise parameters to apply to the gates
700 """
701 w_idx = 0
702 for q in range(n_qubits):
703 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
704 w_idx += 1
705 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
706 w_idx += 1
708 if n_qubits > 1:
709 for q in range(n_qubits):
710 Gates.CRZ(
711 w[w_idx],
712 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
713 noise_params=noise_params,
714 )
715 w_idx += 1
717 class Circuit_15(Circuit):
718 @staticmethod
719 def n_params_per_layer(n_qubits: int) -> int:
720 """
721 Returns the number of parameters per layer for Circuit_15.
723 The number of parameters is 2 times the number of qubits.
724 A warning is logged if the number of qubits is less than 2.
726 Parameters
727 ----------
728 n_qubits : int
729 Number of qubits in the circuit
731 Returns
732 -------
733 int
734 Number of parameters required for one layer of the circuit
735 """
736 if n_qubits > 1:
737 return n_qubits * 2
738 else:
739 log.warning("Number of Qubits < 2, no entanglement available")
740 return 2
742 @staticmethod
743 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
744 """
745 No controlled rotation gates available. Always None.
747 Parameters
748 ----------
749 n_qubits : int
750 Number of qubits in the circuit
752 Returns
753 -------
754 Optional[np.ndarray]
755 List of all controlled indices, or None if the circuit does not
756 contain controlled rotation gates.
757 """
758 return None
760 @staticmethod
761 def build(w: np.ndarray, n_qubits: int, noise_params=None):
762 """
763 Creates a Circuit15 ansatz.
765 Length of flattened vector must be n_qubits*2
766 because for >1 qubits there are three gates
768 Parameters
769 ----------
770 w : np.ndarray
771 Weight vector of size n_qubits*2
772 n_qubits : int
773 Number of qubits
774 noise_params : Optional[Dict[str, float]], optional
775 Dictionary of noise parameters to apply to the gates
776 """
777 w_idx = 0
778 for q in range(n_qubits):
779 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
780 w_idx += 1
782 if n_qubits > 1:
783 for q in range(n_qubits):
784 Gates.CX(
785 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
786 noise_params=noise_params,
787 )
789 for q in range(n_qubits):
790 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
791 w_idx += 1
793 if n_qubits > 1:
794 for q in range(n_qubits):
795 Gates.CX(
796 wires=[(q - 1) % n_qubits, (q - 2) % n_qubits],
797 noise_params=noise_params,
798 )
800 class Circuit_9(Circuit):
801 @staticmethod
802 def n_params_per_layer(n_qubits: int) -> int:
803 """
804 Returns the number of parameters per layer for Circuit_9.
806 The number of parameters is equal to the number of qubits.
808 Parameters
809 ----------
810 n_qubits : int
811 Number of qubits in the circuit
813 Returns
814 -------
815 int
816 Number of parameters required for one layer of the circuit
817 """
818 return n_qubits
820 @staticmethod
821 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
822 """
823 No controlled rotation gates available. Always None.
825 Parameters
826 ----------
827 n_qubits : int
828 Number of qubits in the circuit
830 Returns
831 -------
832 Optional[np.ndarray]
833 List of all controlled indices, or None if the circuit does not
834 contain controlled rotation gates.
835 """
836 return None
838 @staticmethod
839 def build(w: np.ndarray, n_qubits: int, noise_params=None):
840 """
841 Creates a Circuit9 ansatz.
843 Length of flattened vector must be n_qubits
845 Parameters
846 ----------
847 w : np.ndarray
848 Weight vector of size n_qubits
849 n_qubits : int
850 Number of qubits
851 noise_params : Optional[Dict[str, float]], optional
852 Dictionary of noise parameters to apply to the gates
853 """
854 w_idx = 0
855 for q in range(n_qubits):
856 Gates.H(wires=q, noise_params=noise_params)
858 if n_qubits > 1:
859 for q in range(n_qubits - 1):
860 Gates.CZ(
861 wires=[n_qubits - q - 2, n_qubits - q - 1],
862 noise_params=noise_params,
863 )
865 for q in range(n_qubits):
866 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
867 w_idx += 1
869 class Circuit_6(Circuit):
870 @staticmethod
871 def n_params_per_layer(n_qubits: int) -> int:
872 """
873 Returns the number of parameters per layer for Circuit_6.
875 The total number of parameters is n_qubits*3+n_qubits**2, which is
876 the number of rotations n_qubits*3 plus the number of entangling gates
877 n_qubits**2.
879 If n_qubits is 1, the number of parameters is 4, and a warning is logged
880 since no entanglement is possible.
882 Parameters
883 ----------
884 n_qubits : int
885 Number of qubits
887 Returns
888 -------
889 int
890 Number of parameters per layer
891 """
892 if n_qubits > 1:
893 return n_qubits * 3 + n_qubits**2
894 else:
895 log.warning("Number of Qubits < 2, no entanglement available")
896 return 4
898 @staticmethod
899 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
900 """
901 Returns the indices for the controlled rotation gates for one layer.
902 Indices should slice the list of all parameters for one layer as follows:
903 [indices[0]:indices[1]:indices[2]]
905 Parameters
906 ----------
907 n_qubits : int
908 Number of qubits in the circuit
910 Returns
911 -------
912 Optional[np.ndarray]
913 List of all controlled indices, or None if the circuit does not
914 contain controlled rotation gates.
915 """
916 if n_qubits > 1:
917 return [-n_qubits, None, None]
918 else:
919 return None
921 @staticmethod
922 def build(w: np.ndarray, n_qubits: int, noise_params=None):
923 """
924 Creates a Circuit6 ansatz.
926 Length of flattened vector must be
927 n_qubits*4+n_qubits*(n_qubits-1) =
928 n_qubits*3+n_qubits**2
930 Parameters
931 ----------
932 w : np.ndarray
933 Weight vector of size
934 n_layers*(n_qubits*3+n_qubits**2)
935 n_qubits : int
936 Number of qubits
937 noise_params : Optional[Dict[str, float]], optional
938 Dictionary of noise parameters to apply to the gates
939 """
940 w_idx = 0
941 for q in range(n_qubits):
942 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
943 w_idx += 1
944 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
945 w_idx += 1
947 if n_qubits > 1:
948 for ql in range(n_qubits):
949 for q in range(n_qubits):
950 if q == ql:
951 continue
952 Gates.CRX(
953 w[w_idx],
954 wires=[n_qubits - ql - 1, (n_qubits - q - 1) % n_qubits],
955 noise_params=noise_params,
956 )
957 w_idx += 1
959 for q in range(n_qubits):
960 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
961 w_idx += 1
962 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
963 w_idx += 1
965 class Circuit_1(Circuit):
966 @staticmethod
967 def n_params_per_layer(n_qubits: int) -> int:
968 """
969 Returns the number of parameters per layer for Circuit_1.
971 The total number of parameters is determined by the number of qubits, with
972 each qubit contributing 2 parameters.
974 Parameters
975 ----------
976 n_qubits : int
977 Number of qubits in the circuit
979 Returns
980 -------
981 int
982 Number of parameters per layer
983 """
984 return n_qubits * 2
986 @staticmethod
987 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
988 """
989 No controlled rotation gates available. Always None.
991 Parameters
992 ----------
993 n_qubits : int
994 Number of qubits in the circuit
996 Returns
997 -------
998 Optional[np.ndarray]
999 List of all controlled indices, or None if the circuit does not
1000 contain controlled rotation gates.
1001 """
1002 return None
1004 @staticmethod
1005 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1006 """
1007 Creates a Circuit1 ansatz.
1009 Length of flattened vector must be n_qubits*2
1011 Parameters
1012 ----------
1013 w : np.ndarray
1014 Weight vector of size n_qubits*2
1015 n_qubits : int
1016 Number of qubits
1017 noise_params : Optional[Dict[str, float]], optional
1018 Dictionary of noise parameters to apply to the gates
1019 """
1020 w_idx = 0
1021 for q in range(n_qubits):
1022 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1023 w_idx += 1
1024 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1025 w_idx += 1
1027 class Circuit_2(Circuit):
1028 @staticmethod
1029 def n_params_per_layer(n_qubits: int) -> int:
1030 """
1031 Returns the number of parameters per layer for Circuit_2.
1033 The total number of parameters is determined by the number of qubits, with
1034 each qubit contributing 2 parameters.
1036 Parameters
1037 ----------
1038 n_qubits : int
1039 Number of qubits in the circuit
1041 Returns
1042 -------
1043 int
1044 Number of parameters per layer
1045 """
1046 return n_qubits * 2
1048 @staticmethod
1049 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1050 """
1051 No controlled rotation gates available. Always None.
1053 Parameters
1054 ----------
1055 n_qubits : int
1056 Number of qubits in the circuit
1058 Returns
1059 -------
1060 Optional[np.ndarray]
1061 List of all controlled indices, or None if the circuit does not
1062 contain controlled rotation gates.
1063 """
1064 return None
1066 @staticmethod
1067 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1068 """
1069 Creates a Circuit2 ansatz.
1071 Length of flattened vector must be n_qubits*2
1073 Parameters
1074 ----------
1075 w : np.ndarray
1076 Weight vector of size n_qubits*2
1077 n_qubits : int
1078 Number of qubits
1079 noise_params : Optional[Dict[str, float]], optional
1080 Dictionary of noise parameters to apply to the gates
1081 """
1082 w_idx = 0
1083 for q in range(n_qubits):
1084 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1085 w_idx += 1
1086 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1087 w_idx += 1
1089 if n_qubits > 1:
1090 for q in range(n_qubits - 1):
1091 Gates.CX(
1092 wires=[n_qubits - q - 1, n_qubits - q - 2],
1093 noise_params=noise_params,
1094 )
1096 class Circuit_3(Circuit):
1097 @staticmethod
1098 def n_params_per_layer(n_qubits: int) -> int:
1099 """
1100 Calculates the number of parameters per layer for Circuit3.
1102 The number of parameters per layer is given by the number of qubits, with
1103 each qubit contributing 3 parameters. The last qubit only contributes 2
1104 parameters because it is the target qubit for the controlled gates.
1106 Parameters
1107 ----------
1108 n_qubits : int
1109 Number of qubits in the circuit
1111 Returns
1112 -------
1113 int
1114 Number of parameters per layer
1115 """
1116 return n_qubits * 3 - 1
1118 @staticmethod
1119 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1120 """
1121 No controlled rotation gates available. Always None.
1123 Parameters
1124 ----------
1125 n_qubits : int
1126 Number of qubits in the circuit
1128 Returns
1129 -------
1130 Optional[np.ndarray]
1131 List of all controlled indices, or None if the circuit does not
1132 contain controlled rotation gates.
1133 """
1134 return None
1136 @staticmethod
1137 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1138 """
1139 Creates a Circuit3 ansatz.
1141 Length of flattened vector must be n_qubits*3-1
1143 Parameters
1144 ----------
1145 w : np.ndarray
1146 Weight vector of size n_qubits*3-1
1147 n_qubits : int
1148 Number of qubits
1149 noise_params : Optional[Dict[str, float]], optional
1150 Dictionary of noise parameters to apply to the gates
1151 """
1152 w_idx = 0
1153 for q in range(n_qubits):
1154 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1155 w_idx += 1
1156 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1157 w_idx += 1
1159 if n_qubits > 1:
1160 for q in range(n_qubits - 1):
1161 Gates.CRZ(
1162 w[w_idx],
1163 wires=[n_qubits - q - 1, n_qubits - q - 2],
1164 noise_params=noise_params,
1165 )
1166 w_idx += 1
1168 class Circuit_4(Circuit):
1169 @staticmethod
1170 def n_params_per_layer(n_qubits: int) -> int:
1171 """
1172 Returns the number of parameters per layer for the Circuit_4 ansatz.
1174 The number of parameters is calculated as n_qubits*3-1.
1176 Parameters
1177 ----------
1178 n_qubits : int
1179 Number of qubits in the circuit
1181 Returns
1182 -------
1183 int
1184 Number of parameters per layer
1185 """
1186 return n_qubits * 3 - 1
1188 @staticmethod
1189 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1190 """
1191 No controlled rotation gates available. Always None.
1193 Parameters
1194 ----------
1195 n_qubits : int
1196 Number of qubits in the circuit
1198 Returns
1199 -------
1200 Optional[np.ndarray]
1201 List of all controlled indices, or None if the circuit does not
1202 contain controlled rotation gates.
1203 """
1204 return None
1206 @staticmethod
1207 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1208 """
1209 Creates a Circuit4 ansatz.
1211 Length of flattened vector must be n_qubits*3-1
1213 Parameters
1214 ----------
1215 w : np.ndarray
1216 Weight vector of size n_qubits*3-1
1217 n_qubits : int
1218 Number of qubits
1219 noise_params : Optional[Dict[str, float]], optional
1220 Dictionary of noise parameters to apply to the gates
1221 """
1222 w_idx = 0
1223 for q in range(n_qubits):
1224 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1225 w_idx += 1
1226 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1227 w_idx += 1
1229 if n_qubits > 1:
1230 for q in range(n_qubits - 1):
1231 Gates.CRX(
1232 w[w_idx],
1233 wires=[n_qubits - q - 1, n_qubits - q - 2],
1234 noise_params=noise_params,
1235 )
1236 w_idx += 1
1238 class Circuit_10(Circuit):
1239 @staticmethod
1240 def n_params_per_layer(n_qubits: int) -> int:
1241 """
1242 Returns the number of parameters per layer for the Circuit_10 ansatz.
1244 The number of parameters is calculated as n_qubits*2.
1246 Parameters
1247 ----------
1248 n_qubits : int
1249 Number of qubits in the circuit
1251 Returns
1252 -------
1253 int
1254 Number of parameters per layer
1255 """
1256 return n_qubits * 2 # constant gates not considered yet. has to be fixed
1258 @staticmethod
1259 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1260 """
1261 No controlled rotation gates available. Always None.
1263 Parameters
1264 ----------
1265 n_qubits : int
1266 Number of qubits in the circuit
1268 Returns
1269 -------
1270 Optional[np.ndarray]
1271 List of all controlled indices, or None if the circuit does not
1272 contain controlled rotation gates.
1273 """
1274 return None
1276 @staticmethod
1277 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1278 """
1279 Creates a Circuit10 ansatz.
1281 Length of flattened vector must be n_qubits*2
1283 Parameters
1284 ----------
1285 w : np.ndarray
1286 Weight vector of size n_qubits*2
1287 n_qubits : int
1288 Number of qubits
1289 noise_params : Optional[Dict[str, float]], optional
1290 Dictionary of noise parameters to apply to the gates
1291 """
1292 w_idx = 0
1293 # constant gates, independent of layers. has to be fixed
1294 for q in range(n_qubits):
1295 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1296 w_idx += 1
1298 if n_qubits > 1:
1299 for q in range(n_qubits - 1):
1300 Gates.CZ(
1301 wires=[
1302 (n_qubits - q - 2) % n_qubits,
1303 (n_qubits - q - 1) % n_qubits,
1304 ],
1305 noise_params=noise_params,
1306 )
1307 if n_qubits > 2:
1308 Gates.CZ(wires=[n_qubits - 1, 0], noise_params=noise_params)
1310 for q in range(n_qubits):
1311 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1312 w_idx += 1
1314 class Circuit_16(Circuit):
1315 @staticmethod
1316 def n_params_per_layer(n_qubits: int) -> int:
1317 """
1318 Returns the number of parameters per layer for the Circuit_16 ansatz.
1320 The number of parameters is calculated as n_qubits*3-1.
1322 Parameters
1323 ----------
1324 n_qubits : int
1325 Number of qubits in the circuit
1327 Returns
1328 -------
1329 int
1330 Number of parameters per layer
1331 """
1333 return n_qubits * 3 - 1
1335 @staticmethod
1336 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1337 """
1338 No controlled rotation gates available. Always None.
1340 Parameters
1341 ----------
1342 n_qubits : int
1343 Number of qubits in the circuit
1345 Returns
1346 -------
1347 Optional[np.ndarray]
1348 List of all controlled indices, or None if the circuit does not
1349 contain controlled rotation gates.
1350 """
1351 return None
1353 @staticmethod
1354 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1355 """
1356 Creates a Circuit16 ansatz.
1358 Length of flattened vector must be n_qubits*3-1
1360 Parameters
1361 ----------
1362 w : np.ndarray
1363 Weight vector of size n_qubits*3-1
1364 n_qubits : int
1365 Number of qubits
1366 noise_params : Optional[Dict[str, float]], optional
1367 Dictionary of noise parameters to apply to the gates
1368 """
1369 w_idx = 0
1370 for q in range(n_qubits):
1371 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1372 w_idx += 1
1373 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1374 w_idx += 1
1376 if n_qubits > 1:
1377 for q in range(n_qubits // 2):
1378 Gates.CRZ(
1379 w[w_idx],
1380 wires=[(2 * q + 1), (2 * q)],
1381 noise_params=noise_params,
1382 )
1383 w_idx += 1
1385 for q in range((n_qubits - 1) // 2):
1386 Gates.CRZ(
1387 w[w_idx],
1388 wires=[(2 * q + 2), (2 * q + 1)],
1389 noise_params=noise_params,
1390 )
1391 w_idx += 1
1393 class Circuit_17(Circuit):
1394 @staticmethod
1395 def n_params_per_layer(n_qubits: int) -> int:
1396 """
1397 Returns the number of parameters per layer for the Circuit_17 ansatz.
1399 The number of parameters is calculated as n_qubits*3-1.
1401 Parameters
1402 ----------
1403 n_qubits : int
1404 Number of qubits in the circuit
1406 Returns
1407 -------
1408 int
1409 Number of parameters per layer
1410 """
1412 return n_qubits * 3 - 1
1414 @staticmethod
1415 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1416 """
1417 No controlled rotation gates available. Always None.
1419 Parameters
1420 ----------
1421 n_qubits : int
1422 Number of qubits in the circuit
1424 Returns
1425 -------
1426 Optional[np.ndarray]
1427 List of all controlled indices, or None if the circuit does not
1428 contain controlled rotation gates.
1429 """
1430 return None
1432 @staticmethod
1433 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1434 """
1435 Creates a Circuit17 ansatz.
1437 Length of flattened vector must be n_qubits*3-1
1439 Parameters
1440 ----------
1441 w : np.ndarray
1442 Weight vector of size n_qubits*3-1
1443 n_qubits : int
1444 Number of qubits
1445 noise_params : Optional[Dict[str, float]], optional
1446 Dictionary of noise parameters to apply to the gates
1447 """
1448 w_idx = 0
1449 for q in range(n_qubits):
1450 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1451 w_idx += 1
1452 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1453 w_idx += 1
1455 if n_qubits > 1:
1456 for q in range(n_qubits // 2):
1457 Gates.CRX(
1458 w[w_idx],
1459 wires=[(2 * q + 1), (2 * q)],
1460 noise_params=noise_params,
1461 )
1462 w_idx += 1
1464 for q in range((n_qubits - 1) // 2):
1465 Gates.CRX(
1466 w[w_idx],
1467 wires=[(2 * q + 2), (2 * q + 1)],
1468 noise_params=noise_params,
1469 )
1470 w_idx += 1
1472 class Strongly_Entangling(Circuit):
1473 @staticmethod
1474 def n_params_per_layer(n_qubits: int) -> int:
1475 """
1476 Returns the number of parameters per layer for the
1477 Strongly Entangling ansatz.
1479 The number of parameters is calculated as n_qubits*6.
1481 Parameters
1482 ----------
1483 n_qubits : int
1484 Number of qubits in the circuit
1486 Returns
1487 -------
1488 int
1489 Number of parameters per layer
1490 """
1491 if n_qubits < 2:
1492 log.warning("Number of Qubits < 2, no entanglement available")
1493 return n_qubits * 6
1495 @staticmethod
1496 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1497 """
1498 No controlled rotation gates available. Always None.
1500 Parameters
1501 ----------
1502 n_qubits : int
1503 Number of qubits in the circuit
1505 Returns
1506 -------
1507 Optional[np.ndarray]
1508 List of all controlled indices, or None if the circuit does not
1509 contain controlled rotation gates.
1510 """
1511 return None
1513 @staticmethod
1514 def build(w: np.ndarray, n_qubits: int, noise_params=None) -> None:
1515 """
1516 Creates a Strongly Entangling ansatz.
1518 Length of flattened vector must be n_qubits*6
1520 Parameters
1521 ----------
1522 w : np.ndarray
1523 Weight vector of size n_qubits*6
1524 n_qubits : int
1525 Number of qubits
1526 noise_params : Optional[Dict[str, float]], optional
1527 Dictionary of noise parameters to apply to the gates
1528 """
1529 w_idx = 0
1530 for q in range(n_qubits):
1531 Gates.Rot(
1532 w[w_idx],
1533 w[w_idx + 1],
1534 w[w_idx + 2],
1535 wires=q,
1536 noise_params=noise_params,
1537 )
1538 w_idx += 3
1540 if n_qubits > 1:
1541 for q in range(n_qubits):
1542 Gates.CX(wires=[q, (q + 1) % n_qubits], noise_params=noise_params)
1544 for q in range(n_qubits):
1545 Gates.Rot(
1546 w[w_idx],
1547 w[w_idx + 1],
1548 w[w_idx + 2],
1549 wires=q,
1550 noise_params=noise_params,
1551 )
1552 w_idx += 3
1554 if n_qubits > 1:
1555 for q in range(n_qubits):
1556 Gates.CX(
1557 wires=[q, (q + n_qubits // 2) % n_qubits],
1558 noise_params=noise_params,
1559 )
1561 class No_Entangling(Circuit):
1562 @staticmethod
1563 def n_params_per_layer(n_qubits: int) -> int:
1564 """
1565 Returns the number of parameters per layer for the NoEntangling ansatz.
1567 The number of parameters is calculated as n_qubits*3.
1569 Parameters
1570 ----------
1571 n_qubits : int
1572 Number of qubits in the circuit
1574 Returns
1575 -------
1576 int
1577 Number of parameters per layer
1578 """
1579 return n_qubits * 3
1581 @staticmethod
1582 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1583 """
1584 No controlled rotation gates available. Always None.
1586 Parameters
1587 ----------
1588 n_qubits : int
1589 Number of qubits in the circuit
1591 Returns
1592 -------
1593 Optional[np.ndarray]
1594 List of all controlled indices, or None if the circuit does not
1595 contain controlled rotation gates.
1596 """
1597 return None
1599 @staticmethod
1600 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1601 """
1602 Creates a circuit without entangling, but with U3 gates on all qubits
1604 Length of flattened vector must be n_qubits*3
1606 Parameters
1607 ----------
1608 w : np.ndarray
1609 Weight vector of size n_qubits*3
1610 n_qubits : int
1611 Number of qubits
1612 noise_params : Optional[Dict[str, float]], optional
1613 Dictionary of noise parameters to apply to the gates
1614 """
1615 w_idx = 0
1616 for q in range(n_qubits):
1617 Gates.Rot(
1618 w[w_idx],
1619 w[w_idx + 1],
1620 w[w_idx + 2],
1621 wires=q,
1622 noise_params=noise_params,
1623 )
1624 w_idx += 3