Coverage for qml_essentials/ansaetze.py: 62%
478 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-04 21:13 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-04 21:13 +0000
1from abc import ABC, abstractmethod
2from typing import Any, Optional
3import pennylane.numpy as np
4import pennylane as qml
5import itertools
7from typing import List, Union, Dict
9import logging
11log = logging.getLogger(__name__)
14class Circuit(ABC):
15 def __init__(self):
16 pass
18 @abstractmethod
19 def n_params_per_layer(n_qubits: int) -> int:
20 return
22 @abstractmethod
23 def get_control_indices(self, n_qubits: int) -> List[int]:
24 """
25 Returns the indices for the controlled rotation gates for one layer.
26 Indices should slice the list of all parameters for one layer as follows:
27 [indices[0]:indices[1]:indices[2]]
29 Parameters
30 ----------
31 n_qubits : int
32 Number of qubits in the circuit
34 Returns
35 -------
36 Optional[np.ndarray]
37 List of all controlled indices, or None if the circuit does not
38 contain controlled rotation gates.
39 """
40 return
42 def get_control_angles(self, w: np.ndarray, n_qubits: int) -> Optional[np.ndarray]:
43 """
44 Returns the angles for the controlled rotation gates from the list of
45 all parameters for one layer.
47 Parameters
48 ----------
49 w : np.ndarray
50 List of parameters for one layer
51 n_qubits : int
52 Number of qubits in the circuit
54 Returns
55 -------
56 Optional[np.ndarray]
57 List of all controlled parameters, or None if the circuit does not
58 contain controlled rotation gates.
59 """
60 indices = self.get_control_indices(n_qubits)
61 if indices is None:
62 return None
64 return w[indices[0] : indices[1] : indices[2]]
66 @abstractmethod
67 def build(self, n_qubits: int, n_layers: int):
68 return
70 def __call__(self, *args: Any, **kwds: Any) -> Any:
71 self.build(*args, **kwds)
74class Gates:
75 rng = np.random.default_rng()
77 @staticmethod
78 def init_rng(seed: int):
79 """
80 Initializes the random number generator with the given seed.
82 Parameters
83 ----------
84 seed : int
85 The seed for the random number generator.
86 """
87 Gates.rng = np.random.default_rng(seed)
89 @staticmethod
90 def NQubitDepolarizingChannel(p, wires):
91 """
92 Generates the Kraus operators for an n-qubit depolarizing channel.
94 The n-qubit depolarizing channel is defined as:
95 E(rho) = sqrt(1 - p * (4^n - 1) / 4^n) * rho
96 + sqrt(p / 4^n) * ∑_{P ≠ I^{⊗n}} P rho P†
97 where the sum is over all non-identity n-qubit Pauli operators
98 (i.e., tensor products of {I, X, Y, Z} excluding the identity operator I^{⊗n}).
99 Each Pauli error operator is weighted equally by p / 4^n.
101 This operator-sum (Kraus) representation models uniform depolarizing noise
102 acting on n qubits simultaneously. It is useful for simulating realistic
103 multi-qubit noise affecting entangling gates in noisy quantum circuits.
105 Parameters
106 ----------
107 p : float
108 The total probability of an n-qubit depolarizing error occurring.
109 Must satisfy 0 ≤ p ≤ 1.
111 wires : Sequence[int]
112 The list of qubit indices (wires) on which the channel acts.
113 Must contain at least 2 qubits.
115 Returns
116 -------
117 qml.QubitChannel
118 A PennyLane QubitChannel constructed from the Kraus operators representing
119 the n-qubit depolarizing noise channel acting on the specified wires.
120 """
121 def n_qubit_depolarizing_kraus(p: float, n: int) -> List[np.ndarray]:
122 if not (0.0 <= p <= 1.0):
123 raise ValueError(f"Probability p must be between 0 and 1, got {p}")
124 if n < 2:
125 raise ValueError(f"Number of qubits must be >= 2, got {n}")
127 Id = np.eye(2)
128 X = qml.matrix(qml.PauliX(0))
129 Y = qml.matrix(qml.PauliY(0))
130 Z = qml.matrix(qml.PauliZ(0))
131 paulis = [Id, X, Y, Z]
133 dim = 2 ** n
134 all_ops = []
136 # Generate all n-qubit Pauli tensor products:
137 for indices in itertools.product(range(4), repeat=n):
138 P = np.eye(1)
139 for idx in indices:
140 P = np.kron(P, paulis[idx])
141 all_ops.append(P)
143 # Identity operator corresponds to all zeros indices (Id^n)
144 K0 = np.sqrt(1 - p * (4 ** n - 1) / (4 ** n)) * np.eye(dim)
146 kraus_ops = []
147 for i, P in enumerate(all_ops):
148 if i == 0:
149 # Skip the identity, already handled as K0
150 continue
151 kraus_ops.append(np.sqrt(p / (4 ** n)) * P)
153 return [K0] + kraus_ops
155 return qml.QubitChannel(n_qubit_depolarizing_kraus(p, len(wires)), wires=wires)
157 @staticmethod
158 def Noise(
159 wires: Union[int, List[int]], noise_params: Optional[Dict[str, float]] = None
160 ) -> None:
161 """
162 Applies noise to the given wires.
164 Parameters
165 ----------
166 wires : Union[int, List[int]]
167 The wire(s) to apply the noise to.
168 noise_params : Optional[Dict[str, float]]
169 A dictionary of noise parameters. The following noise gates are
170 supported:
171 -BitFlip: Applies a bit flip error to the given wires.
172 -PhaseFlip: Applies a phase flip error to the given wires.
173 -Depolarizing: Applies a depolarizing channel error to the
174 given wires.
175 -MultiQubitDepolarizing: Applies a two-qubit depolarizing channel
176 error to the given wires.
178 All parameters are optional and default to 0.0 if not provided.
179 """
180 if noise_params is not None:
181 if isinstance(wires, int):
182 wires = [wires] # single qubit gate
184 # noise on single qubits
185 for wire in wires:
186 qml.BitFlip(noise_params.get("BitFlip", 0.0), wires=wire)
187 qml.PhaseFlip(noise_params.get("PhaseFlip", 0.0), wires=wire)
188 qml.DepolarizingChannel(
189 noise_params.get("Depolarizing", 0.0), wires=wire
190 )
192 # noise on two-qubits
193 if len(wires) > 1:
194 p = noise_params.get("MultiQubitDepolarizing", 0.0)
195 if p > 0:
196 Gates.NQubitDepolarizingChannel(p, wires)
198 @staticmethod
199 def GateError(
200 w: float, noise_params: Optional[Dict[str, float]] = None
201 ) -> np.ndarray:
202 """
203 Applies a gate error to the given rotation angle(s).
205 Parameters
206 ----------
207 w : float
208 The rotation angle in radians.
209 noise_params : Optional[Dict[str, float]]
210 A dictionary of noise parameters. The following noise gates are
211 supported:
212 -GateError: Applies a normal distribution error to the rotation
213 angle. The standard deviation of the noise is specified by
214 the "GateError" key in the dictionary.
216 All parameters are optional and default to 0.0 if not provided.
218 Returns
219 -------
220 float
221 The modified rotation angle after applying the gate error.
222 """
223 if (
224 noise_params is not None
225 and noise_params.get("GateError", None) is not None
226 ):
227 w += Gates.rng.normal(0, noise_params["GateError"])
228 return w
230 @staticmethod
231 def Rot(phi, theta, omega, wires, noise_params=None):
232 """
233 Applies a rotation gate to the given wires and adds `Noise`
235 Parameters
236 ----------
237 phi : float
238 The first rotation angle in radians.
239 theta : float
240 The second rotation angle in radians.
241 omega : float
242 The third rotation angle in radians.
243 wires : Union[int, List[int]]
244 The wire(s) to apply the rotation gate to.
245 noise_params : Optional[Dict[str, float]]
246 A dictionary of noise parameters. The following noise gates are
247 supported:
248 -BitFlip: Applies a bit flip error to the given wires.
249 -PhaseFlip: Applies a phase flip error to the given wires.
250 -Depolarizing: Applies a depolarizing channel error to the
251 given wires.
253 All parameters are optional and default to 0.0 if not provided.
254 """
255 if noise_params is not None and "GateError" in noise_params:
256 phi = Gates.GateError(phi, noise_params)
257 theta = Gates.GateError(theta, noise_params)
258 omega = Gates.GateError(omega, noise_params)
259 # phi += Gates.rng.normal(0, noise_params["GateError"])
260 # theta += Gates.rng.normal(0, noise_params["GateError"])
261 # omega += Gates.rng.normal(0, noise_params["GateError"])
262 qml.Rot(phi, theta, omega, wires=wires)
263 Gates.Noise(wires, noise_params)
265 @staticmethod
266 def RX(w, wires, noise_params=None):
267 """
268 Applies a rotation around the X axis to the given wires and adds `Noise`
270 Parameters
271 ----------
272 w : float
273 The rotation angle in radians.
274 wires : Union[int, List[int]]
275 The wire(s) to apply the rotation gate to.
276 noise_params : Optional[Dict[str, float]]
277 A dictionary of noise parameters. The following noise gates are
278 supported:
279 -BitFlip: Applies a bit flip error to the given wires.
280 -PhaseFlip: Applies a phase flip error to the given wires.
281 -Depolarizing: Applies a depolarizing channel error to the
282 given wires.
284 All parameters are optional and default to 0.0 if not provided.
285 """
286 w = Gates.GateError(w, noise_params)
287 qml.RX(w, wires=wires)
288 Gates.Noise(wires, noise_params)
290 @staticmethod
291 def RY(w, wires, noise_params=None):
292 """
293 Applies a rotation around the Y axis to the given wires and adds `Noise`
295 Parameters
296 ----------
297 w : float
298 The rotation angle in radians.
299 wires : Union[int, List[int]]
300 The wire(s) to apply the rotation gate to.
301 noise_params : Optional[Dict[str, float]]
302 A dictionary of noise parameters. The following noise gates are
303 supported:
304 -BitFlip: Applies a bit flip error to the given wires.
305 -PhaseFlip: Applies a phase flip error to the given wires.
306 -Depolarizing: Applies a depolarizing channel error to the
307 given wires.
309 All parameters are optional and default to 0.0 if not provided.
310 """
311 w = Gates.GateError(w, noise_params)
312 qml.RY(w, wires=wires)
313 Gates.Noise(wires, noise_params)
315 @staticmethod
316 def RZ(w, wires, noise_params=None):
317 """
318 Applies a rotation around the Z axis to the given wires and adds `Noise`
320 Parameters
321 ----------
322 w : float
323 The rotation angle in radians.
324 wires : Union[int, List[int]]
325 The wire(s) to apply the rotation gate to.
326 noise_params : Optional[Dict[str, float]]
327 A dictionary of noise parameters. The following noise gates are
328 supported:
329 -BitFlip: Applies a bit flip error to the given wires.
330 -PhaseFlip: Applies a phase flip error to the given wires.
331 -Depolarizing: Applies a depolarizing channel error to the
332 given wires.
334 All parameters are optional and default to 0.0 if not provided.
335 """
336 w = Gates.GateError(w, noise_params)
337 qml.RZ(w, wires=wires)
338 Gates.Noise(wires, noise_params)
340 @staticmethod
341 def CRX(w, wires, noise_params=None):
342 """
343 Applies a controlled rotation around the X axis to the given wires
344 and adds `Noise`
346 Parameters
347 ----------
348 w : float
349 The rotation angle in radians.
350 wires : Union[int, List[int]]
351 The wire(s) to apply the controlled rotation gate to.
352 noise_params : Optional[Dict[str, float]]
353 A dictionary of noise parameters. The following noise gates are
354 supported:
355 -BitFlip: Applies a bit flip error to the given wires.
356 -PhaseFlip: Applies a phase flip error to the given wires.
357 -Depolarizing: Applies a depolarizing channel error to the
358 given wires.
360 All parameters are optional and default to 0.0 if not provided.
361 """
362 w = Gates.GateError(w, noise_params)
363 qml.CRX(w, wires=wires)
364 Gates.Noise(wires, noise_params)
366 @staticmethod
367 def CRY(w, wires, noise_params=None):
368 """
369 Applies a controlled rotation around the Y axis to the given wires
370 and adds `Noise`
372 Parameters
373 ----------
374 w : float
375 The rotation angle in radians.
376 wires : Union[int, List[int]]
377 The wire(s) to apply the controlled rotation gate to.
378 noise_params : Optional[Dict[str, float]]
379 A dictionary of noise parameters. The following noise gates are
380 supported:
381 -BitFlip: Applies a bit flip error to the given wires.
382 -PhaseFlip: Applies a phase flip error to the given wires.
383 -Depolarizing: Applies a depolarizing channel error to the
384 given wires.
386 All parameters are optional and default to 0.0 if not provided.
387 """
388 w = Gates.GateError(w, noise_params)
389 qml.CRY(w, wires=wires)
390 Gates.Noise(wires, noise_params)
392 @staticmethod
393 def CRZ(w, wires, noise_params=None):
394 """
395 Applies a controlled rotation around the Z axis to the given wires
396 and adds `Noise`
398 Parameters
399 ----------
400 w : float
401 The rotation angle in radians.
402 wires : Union[int, List[int]]
403 The wire(s) to apply the controlled rotation gate to.
404 noise_params : Optional[Dict[str, float]]
405 A dictionary of noise parameters. The following noise gates are
406 supported:
407 -BitFlip: Applies a bit flip error to the given wires.
408 -PhaseFlip: Applies a phase flip error to the given wires.
409 -Depolarizing: Applies a depolarizing channel error to the
410 given wires.
412 All parameters are optional and default to 0.0 if not provided.
413 """
414 w = Gates.GateError(w, noise_params)
415 qml.CRZ(w, wires=wires)
416 Gates.Noise(wires, noise_params)
418 @staticmethod
419 def CX(wires, noise_params=None):
420 """
421 Applies a controlled NOT gate to the given wires and adds `Noise`
423 Parameters
424 ----------
425 wires : Union[int, List[int]]
426 The wire(s) to apply the controlled NOT gate to.
427 noise_params : Optional[Dict[str, float]]
428 A dictionary of noise parameters. The following noise gates are
429 supported:
430 -BitFlip: Applies a bit flip error to the given wires.
431 -PhaseFlip: Applies a phase flip error to the given wires.
432 -Depolarizing: Applies a depolarizing channel error to the
433 given wires.
435 All parameters are optional and default to 0.0 if not provided.
436 """
437 qml.CNOT(wires=wires)
438 Gates.Noise(wires, noise_params)
440 @staticmethod
441 def CY(wires, noise_params=None):
442 """
443 Applies a controlled Y gate to the given wires and adds `Noise`
445 Parameters
446 ----------
447 wires : Union[int, List[int]]
448 The wire(s) to apply the controlled Y gate to.
449 noise_params : Optional[Dict[str, float]]
450 A dictionary of noise parameters. The following noise gates are
451 supported:
452 -BitFlip: Applies a bit flip error to the given wires.
453 -PhaseFlip: Applies a phase flip error to the given wires.
454 -Depolarizing: Applies a depolarizing channel error to the
455 given wires.
457 All parameters are optional and default to 0.0 if not provided.
458 """
459 qml.CY(wires=wires)
460 Gates.Noise(wires, noise_params)
462 @staticmethod
463 def CZ(wires, noise_params=None):
464 """
465 Applies a controlled Z gate to the given wires and adds `Noise`
467 Parameters
468 ----------
469 wires : Union[int, List[int]]
470 The wire(s) to apply the controlled Z gate to.
471 noise_params : Optional[Dict[str, float]]
472 A dictionary of noise parameters. The following noise gates are
473 supported:
474 -BitFlip: Applies a bit flip error to the given wires.
475 -PhaseFlip: Applies a phase flip error to the given wires.
476 -Depolarizing: Applies a depolarizing channel error to the
477 given wires.
479 All parameters are optional and default to 0.0 if not provided.
480 """
481 qml.CZ(wires=wires)
482 Gates.Noise(wires, noise_params)
484 @staticmethod
485 def H(wires, noise_params=None):
486 """
487 Applies a Hadamard gate to the given wires and adds `Noise`
489 Parameters
490 ----------
491 wires : Union[int, List[int]]
492 The wire(s) to apply the Hadamard gate to.
493 noise_params : Optional[Dict[str, float]]
494 A dictionary of noise parameters. The following noise gates are
495 supported:
496 -BitFlip: Applies a bit flip error to the given wires.
497 -PhaseFlip: Applies a phase flip error to the given wires.
498 -Depolarizing: Applies a depolarizing channel error to the
499 given wires.
501 All parameters are optional and default to 0.0 if not provided.
502 """
503 qml.Hadamard(wires=wires)
504 Gates.Noise(wires, noise_params)
507class Ansaetze:
509 def get_available():
510 return [
511 Ansaetze.No_Ansatz,
512 Ansaetze.Circuit_1,
513 Ansaetze.Circuit_2,
514 Ansaetze.Circuit_3,
515 Ansaetze.Circuit_4,
516 Ansaetze.Circuit_6,
517 Ansaetze.Circuit_9,
518 Ansaetze.Circuit_10,
519 Ansaetze.Circuit_15,
520 Ansaetze.Circuit_16,
521 Ansaetze.Circuit_17,
522 Ansaetze.Circuit_18,
523 Ansaetze.Circuit_19,
524 Ansaetze.No_Entangling,
525 Ansaetze.Strongly_Entangling,
526 Ansaetze.Hardware_Efficient,
527 Ansaetze.GHZ,
528 ]
530 class No_Ansatz(Circuit):
531 @staticmethod
532 def n_params_per_layer(n_qubits: int) -> int:
533 return 0
535 @staticmethod
536 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
537 return None
539 @staticmethod
540 def build(w: np.ndarray, n_qubits: int, noise_params=None):
541 pass
543 class GHZ(Circuit):
544 @staticmethod
545 def n_params_per_layer(n_qubits: int) -> int:
546 return 0
548 @staticmethod
549 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
550 return None
552 @staticmethod
553 def build(w: np.ndarray, n_qubits: int, noise_params=None):
554 Gates.H(0, noise_params=noise_params)
556 for q in range(n_qubits - 1):
557 Gates.CX([q, q + 1], noise_params=noise_params)
559 class Hardware_Efficient(Circuit):
560 @staticmethod
561 def n_params_per_layer(n_qubits: int) -> int:
562 """
563 Returns the number of parameters per layer for the
564 Hardware Efficient Ansatz.
566 The number of parameters is 3 times the number of qubits when there
567 is more than one qubit, as each qubit contributes 3 parameters.
568 If the number of qubits is less than 2, a warning is logged since
569 no entanglement is possible, and a fixed number of 2 parameters is used.
571 Parameters
572 ----------
573 n_qubits : int
574 Number of qubits in the circuit
576 Returns
577 -------
578 int
579 Number of parameters required for one layer of the circuit
580 """
581 if n_qubits < 2:
582 log.warning("Number of Qubits < 2, no entanglement available")
583 return n_qubits * 3
585 @staticmethod
586 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
587 """
588 No controlled rotation gates available. Always None.
590 Parameters
591 ----------
592 n_qubits : int
593 Number of qubits in the circuit
595 Returns
596 -------
597 Optional[np.ndarray]
598 List of all controlled indices, or None if the circuit does not
599 contain controlled rotation gates.
600 """
601 return None
603 @staticmethod
604 def build(w: np.ndarray, n_qubits: int, noise_params=None):
605 """
606 Creates a Hardware-Efficient ansatz, as proposed in
607 https://arxiv.org/pdf/2309.03279
609 Parameters
610 ----------
611 w : np.ndarray
612 Weight vector of size n_qubits*3
613 n_qubits : int
614 Number of qubits
615 noise_params : Optional[Dict[str, float]], optional
616 Dictionary of noise parameters to apply to the gates
617 """
618 w_idx = 0
619 for q in range(n_qubits):
620 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
621 w_idx += 1
622 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
623 w_idx += 1
624 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
625 w_idx += 1
627 if n_qubits > 1:
628 for q in range(n_qubits // 2):
629 Gates.CX(wires=[(2 * q), (2 * q + 1)], noise_params=noise_params)
630 for q in range((n_qubits - 1) // 2):
631 Gates.CX(
632 wires=[(2 * q + 1), (2 * q + 2)], noise_params=noise_params
633 )
634 if n_qubits > 2:
635 Gates.CX(wires=[(n_qubits - 1), 0], noise_params=noise_params)
637 class Circuit_19(Circuit):
638 @staticmethod
639 def n_params_per_layer(n_qubits: int) -> int:
640 """
641 Returns the number of parameters per layer for Circuit_19.
643 The number of parameters is 3 times the number of qubits when there
644 is more than one qubit, as each qubit contributes 3 parameters.
645 If the number of qubits is less than 2, a warning is logged since
646 no entanglement is possible, and a fixed number of 2 parameters is used.
648 Parameters
649 ----------
650 n_qubits : int
651 Number of qubits in the circuit
653 Returns
654 -------
655 int
656 Number of parameters required for one layer of the circuit
657 """
659 if n_qubits > 1:
660 return n_qubits * 3
661 else:
662 log.warning("Number of Qubits < 2, no entanglement available")
663 return 2
665 @staticmethod
666 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
667 """
668 Returns the indices for the controlled rotation gates for one layer.
669 Indices should slice the list of all parameters for one layer as follows:
670 [indices[0]:indices[1]:indices[2]]
672 Parameters
673 ----------
674 n_qubits : int
675 Number of qubits in the circuit
677 Returns
678 -------
679 Optional[np.ndarray]
680 List of all controlled indices, or None if the circuit does not
681 contain controlled rotation gates.
682 """
683 if n_qubits > 1:
684 return [-n_qubits, None, None]
685 else:
686 return None
688 @staticmethod
689 def build(w: np.ndarray, n_qubits: int, noise_params=None):
690 """
691 Creates a Circuit19 ansatz.
693 Length of flattened vector must be n_qubits*3
694 because for >1 qubits there are three gates
696 Parameters
697 ----------
698 w : np.ndarray
699 Weight vector of size n_qubits*3
700 n_qubits : int
701 Number of qubits
702 noise_params : Optional[Dict[str, float]], optional
703 Dictionary of noise parameters to apply to the gates
704 """
705 w_idx = 0
706 for q in range(n_qubits):
707 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
708 w_idx += 1
709 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
710 w_idx += 1
712 if n_qubits > 1:
713 for q in range(n_qubits):
714 Gates.CRX(
715 w[w_idx],
716 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
717 noise_params=noise_params,
718 )
719 w_idx += 1
721 class Circuit_18(Circuit):
722 @staticmethod
723 def n_params_per_layer(n_qubits: int) -> int:
724 """
725 Returns the number of parameters per layer for Circuit_18.
727 The number of parameters is 3 times the number of qubits when there
728 is more than one qubit, as each qubit contributes 3 parameters.
729 If the number of qubits is less than 2, a warning is logged since
730 no entanglement is possible, and a fixed number of 2 parameters is used.
732 Parameters
733 ----------
734 n_qubits : int
735 Number of qubits in the circuit
737 Returns
738 -------
739 int
740 Number of parameters required for one layer of the circuit
741 """
742 if n_qubits > 1:
743 return n_qubits * 3
744 else:
745 log.warning("Number of Qubits < 2, no entanglement available")
746 return 2
748 @staticmethod
749 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
750 """
751 Returns the indices for the controlled rotation gates for one layer.
752 Indices should slice the list of all parameters for one layer as follows:
753 [indices[0]:indices[1]:indices[2]]
755 Parameters
756 ----------
757 n_qubits : int
758 Number of qubits in the circuit
760 Returns
761 -------
762 Optional[np.ndarray]
763 List of all controlled indices, or None if the circuit does not
764 contain controlled rotation gates.
765 """
766 if n_qubits > 1:
767 return [-n_qubits, None, None]
768 else:
769 return None
771 @staticmethod
772 def build(w: np.ndarray, n_qubits: int, noise_params=None):
773 """
774 Creates a Circuit18 ansatz.
776 Length of flattened vector must be n_qubits*3
778 Parameters
779 ----------
780 w : np.ndarray
781 Weight vector of size n_qubits*3
782 n_qubits : int
783 Number of qubits
784 noise_params : Optional[Dict[str, float]], optional
785 Dictionary of noise parameters to apply to the gates
786 """
787 w_idx = 0
788 for q in range(n_qubits):
789 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
790 w_idx += 1
791 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
792 w_idx += 1
794 if n_qubits > 1:
795 for q in range(n_qubits):
796 Gates.CRZ(
797 w[w_idx],
798 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
799 noise_params=noise_params,
800 )
801 w_idx += 1
803 class Circuit_15(Circuit):
804 @staticmethod
805 def n_params_per_layer(n_qubits: int) -> int:
806 """
807 Returns the number of parameters per layer for Circuit_15.
809 The number of parameters is 2 times the number of qubits.
810 A warning is logged if the number of qubits is less than 2.
812 Parameters
813 ----------
814 n_qubits : int
815 Number of qubits in the circuit
817 Returns
818 -------
819 int
820 Number of parameters required for one layer of the circuit
821 """
822 if n_qubits > 1:
823 return n_qubits * 2
824 else:
825 log.warning("Number of Qubits < 2, no entanglement available")
826 return 2
828 @staticmethod
829 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
830 """
831 No controlled rotation gates available. Always None.
833 Parameters
834 ----------
835 n_qubits : int
836 Number of qubits in the circuit
838 Returns
839 -------
840 Optional[np.ndarray]
841 List of all controlled indices, or None if the circuit does not
842 contain controlled rotation gates.
843 """
844 return None
846 @staticmethod
847 def build(w: np.ndarray, n_qubits: int, noise_params=None):
848 """
849 Creates a Circuit15 ansatz.
851 Length of flattened vector must be n_qubits*2
852 because for >1 qubits there are three gates
854 Parameters
855 ----------
856 w : np.ndarray
857 Weight vector of size n_qubits*2
858 n_qubits : int
859 Number of qubits
860 noise_params : Optional[Dict[str, float]], optional
861 Dictionary of noise parameters to apply to the gates
862 """
863 w_idx = 0
864 for q in range(n_qubits):
865 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
866 w_idx += 1
868 if n_qubits > 1:
869 for q in range(n_qubits):
870 Gates.CX(
871 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
872 noise_params=noise_params,
873 )
875 for q in range(n_qubits):
876 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
877 w_idx += 1
879 if n_qubits > 1:
880 for q in range(n_qubits):
881 Gates.CX(
882 wires=[(q - 1) % n_qubits, (q - 2) % n_qubits],
883 noise_params=noise_params,
884 )
886 class Circuit_9(Circuit):
887 @staticmethod
888 def n_params_per_layer(n_qubits: int) -> int:
889 """
890 Returns the number of parameters per layer for Circuit_9.
892 The number of parameters is equal to the number of qubits.
894 Parameters
895 ----------
896 n_qubits : int
897 Number of qubits in the circuit
899 Returns
900 -------
901 int
902 Number of parameters required for one layer of the circuit
903 """
904 return n_qubits
906 @staticmethod
907 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
908 """
909 No controlled rotation gates available. Always None.
911 Parameters
912 ----------
913 n_qubits : int
914 Number of qubits in the circuit
916 Returns
917 -------
918 Optional[np.ndarray]
919 List of all controlled indices, or None if the circuit does not
920 contain controlled rotation gates.
921 """
922 return None
924 @staticmethod
925 def build(w: np.ndarray, n_qubits: int, noise_params=None):
926 """
927 Creates a Circuit9 ansatz.
929 Length of flattened vector must be n_qubits
931 Parameters
932 ----------
933 w : np.ndarray
934 Weight vector of size n_qubits
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.H(wires=q, noise_params=noise_params)
944 if n_qubits > 1:
945 for q in range(n_qubits - 1):
946 Gates.CZ(
947 wires=[n_qubits - q - 2, n_qubits - q - 1],
948 noise_params=noise_params,
949 )
951 for q in range(n_qubits):
952 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
953 w_idx += 1
955 class Circuit_6(Circuit):
956 @staticmethod
957 def n_params_per_layer(n_qubits: int) -> int:
958 """
959 Returns the number of parameters per layer for Circuit_6.
961 The total number of parameters is n_qubits*3+n_qubits**2, which is
962 the number of rotations n_qubits*3 plus the number of entangling gates
963 n_qubits**2.
965 If n_qubits is 1, the number of parameters is 4, and a warning is logged
966 since no entanglement is possible.
968 Parameters
969 ----------
970 n_qubits : int
971 Number of qubits
973 Returns
974 -------
975 int
976 Number of parameters per layer
977 """
978 if n_qubits > 1:
979 return n_qubits * 3 + n_qubits**2
980 else:
981 log.warning("Number of Qubits < 2, no entanglement available")
982 return 4
984 @staticmethod
985 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
986 """
987 Returns the indices for the controlled rotation gates for one layer.
988 Indices should slice the list of all parameters for one layer as follows:
989 [indices[0]:indices[1]:indices[2]]
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 if n_qubits > 1:
1003 return [-n_qubits, None, None]
1004 else:
1005 return None
1007 @staticmethod
1008 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1009 """
1010 Creates a Circuit6 ansatz.
1012 Length of flattened vector must be
1013 n_qubits*4+n_qubits*(n_qubits-1) =
1014 n_qubits*3+n_qubits**2
1016 Parameters
1017 ----------
1018 w : np.ndarray
1019 Weight vector of size
1020 n_layers*(n_qubits*3+n_qubits**2)
1021 n_qubits : int
1022 Number of qubits
1023 noise_params : Optional[Dict[str, float]], optional
1024 Dictionary of noise parameters to apply to the gates
1025 """
1026 w_idx = 0
1027 for q in range(n_qubits):
1028 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1029 w_idx += 1
1030 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1031 w_idx += 1
1033 if n_qubits > 1:
1034 for ql in range(n_qubits):
1035 for q in range(n_qubits):
1036 if q == ql:
1037 continue
1038 Gates.CRX(
1039 w[w_idx],
1040 wires=[n_qubits - ql - 1, (n_qubits - q - 1) % n_qubits],
1041 noise_params=noise_params,
1042 )
1043 w_idx += 1
1045 for q in range(n_qubits):
1046 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1047 w_idx += 1
1048 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1049 w_idx += 1
1051 class Circuit_1(Circuit):
1052 @staticmethod
1053 def n_params_per_layer(n_qubits: int) -> int:
1054 """
1055 Returns the number of parameters per layer for Circuit_1.
1057 The total number of parameters is determined by the number of qubits, with
1058 each qubit contributing 2 parameters.
1060 Parameters
1061 ----------
1062 n_qubits : int
1063 Number of qubits in the circuit
1065 Returns
1066 -------
1067 int
1068 Number of parameters per layer
1069 """
1070 return n_qubits * 2
1072 @staticmethod
1073 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1074 """
1075 No controlled rotation gates available. Always None.
1077 Parameters
1078 ----------
1079 n_qubits : int
1080 Number of qubits in the circuit
1082 Returns
1083 -------
1084 Optional[np.ndarray]
1085 List of all controlled indices, or None if the circuit does not
1086 contain controlled rotation gates.
1087 """
1088 return None
1090 @staticmethod
1091 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1092 """
1093 Creates a Circuit1 ansatz.
1095 Length of flattened vector must be n_qubits*2
1097 Parameters
1098 ----------
1099 w : np.ndarray
1100 Weight vector of size n_qubits*2
1101 n_qubits : int
1102 Number of qubits
1103 noise_params : Optional[Dict[str, float]], optional
1104 Dictionary of noise parameters to apply to the gates
1105 """
1106 w_idx = 0
1107 for q in range(n_qubits):
1108 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1109 w_idx += 1
1110 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1111 w_idx += 1
1113 class Circuit_2(Circuit):
1114 @staticmethod
1115 def n_params_per_layer(n_qubits: int) -> int:
1116 """
1117 Returns the number of parameters per layer for Circuit_2.
1119 The total number of parameters is determined by the number of qubits, with
1120 each qubit contributing 2 parameters.
1122 Parameters
1123 ----------
1124 n_qubits : int
1125 Number of qubits in the circuit
1127 Returns
1128 -------
1129 int
1130 Number of parameters per layer
1131 """
1132 return n_qubits * 2
1134 @staticmethod
1135 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1136 """
1137 No controlled rotation gates available. Always None.
1139 Parameters
1140 ----------
1141 n_qubits : int
1142 Number of qubits in the circuit
1144 Returns
1145 -------
1146 Optional[np.ndarray]
1147 List of all controlled indices, or None if the circuit does not
1148 contain controlled rotation gates.
1149 """
1150 return None
1152 @staticmethod
1153 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1154 """
1155 Creates a Circuit2 ansatz.
1157 Length of flattened vector must be n_qubits*2
1159 Parameters
1160 ----------
1161 w : np.ndarray
1162 Weight vector of size n_qubits*2
1163 n_qubits : int
1164 Number of qubits
1165 noise_params : Optional[Dict[str, float]], optional
1166 Dictionary of noise parameters to apply to the gates
1167 """
1168 w_idx = 0
1169 for q in range(n_qubits):
1170 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1171 w_idx += 1
1172 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1173 w_idx += 1
1175 if n_qubits > 1:
1176 for q in range(n_qubits - 1):
1177 Gates.CX(
1178 wires=[n_qubits - q - 1, n_qubits - q - 2],
1179 noise_params=noise_params,
1180 )
1182 class Circuit_3(Circuit):
1183 @staticmethod
1184 def n_params_per_layer(n_qubits: int) -> int:
1185 """
1186 Calculates the number of parameters per layer for Circuit3.
1188 The number of parameters per layer is given by the number of qubits, with
1189 each qubit contributing 3 parameters. The last qubit only contributes 2
1190 parameters because it is the target qubit for the controlled gates.
1192 Parameters
1193 ----------
1194 n_qubits : int
1195 Number of qubits in the circuit
1197 Returns
1198 -------
1199 int
1200 Number of parameters per layer
1201 """
1202 return n_qubits * 3 - 1
1204 @staticmethod
1205 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1206 """
1207 No controlled rotation gates available. Always None.
1209 Parameters
1210 ----------
1211 n_qubits : int
1212 Number of qubits in the circuit
1214 Returns
1215 -------
1216 Optional[np.ndarray]
1217 List of all controlled indices, or None if the circuit does not
1218 contain controlled rotation gates.
1219 """
1220 return None
1222 @staticmethod
1223 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1224 """
1225 Creates a Circuit3 ansatz.
1227 Length of flattened vector must be n_qubits*3-1
1229 Parameters
1230 ----------
1231 w : np.ndarray
1232 Weight vector of size n_qubits*3-1
1233 n_qubits : int
1234 Number of qubits
1235 noise_params : Optional[Dict[str, float]], optional
1236 Dictionary of noise parameters to apply to the gates
1237 """
1238 w_idx = 0
1239 for q in range(n_qubits):
1240 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1241 w_idx += 1
1242 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1243 w_idx += 1
1245 if n_qubits > 1:
1246 for q in range(n_qubits - 1):
1247 Gates.CRZ(
1248 w[w_idx],
1249 wires=[n_qubits - q - 1, n_qubits - q - 2],
1250 noise_params=noise_params,
1251 )
1252 w_idx += 1
1254 class Circuit_4(Circuit):
1255 @staticmethod
1256 def n_params_per_layer(n_qubits: int) -> int:
1257 """
1258 Returns the number of parameters per layer for the Circuit_4 ansatz.
1260 The number of parameters is calculated as n_qubits*3-1.
1262 Parameters
1263 ----------
1264 n_qubits : int
1265 Number of qubits in the circuit
1267 Returns
1268 -------
1269 int
1270 Number of parameters per layer
1271 """
1272 return n_qubits * 3 - 1
1274 @staticmethod
1275 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1276 """
1277 No controlled rotation gates available. Always None.
1279 Parameters
1280 ----------
1281 n_qubits : int
1282 Number of qubits in the circuit
1284 Returns
1285 -------
1286 Optional[np.ndarray]
1287 List of all controlled indices, or None if the circuit does not
1288 contain controlled rotation gates.
1289 """
1290 return None
1292 @staticmethod
1293 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1294 """
1295 Creates a Circuit4 ansatz.
1297 Length of flattened vector must be n_qubits*3-1
1299 Parameters
1300 ----------
1301 w : np.ndarray
1302 Weight vector of size n_qubits*3-1
1303 n_qubits : int
1304 Number of qubits
1305 noise_params : Optional[Dict[str, float]], optional
1306 Dictionary of noise parameters to apply to the gates
1307 """
1308 w_idx = 0
1309 for q in range(n_qubits):
1310 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1311 w_idx += 1
1312 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1313 w_idx += 1
1315 if n_qubits > 1:
1316 for q in range(n_qubits - 1):
1317 Gates.CRX(
1318 w[w_idx],
1319 wires=[n_qubits - q - 1, n_qubits - q - 2],
1320 noise_params=noise_params,
1321 )
1322 w_idx += 1
1324 class Circuit_10(Circuit):
1325 @staticmethod
1326 def n_params_per_layer(n_qubits: int) -> int:
1327 """
1328 Returns the number of parameters per layer for the Circuit_10 ansatz.
1330 The number of parameters is calculated as n_qubits*2.
1332 Parameters
1333 ----------
1334 n_qubits : int
1335 Number of qubits in the circuit
1337 Returns
1338 -------
1339 int
1340 Number of parameters per layer
1341 """
1342 return n_qubits * 2 # constant gates not considered yet. has to be fixed
1344 @staticmethod
1345 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1346 """
1347 No controlled rotation gates available. Always None.
1349 Parameters
1350 ----------
1351 n_qubits : int
1352 Number of qubits in the circuit
1354 Returns
1355 -------
1356 Optional[np.ndarray]
1357 List of all controlled indices, or None if the circuit does not
1358 contain controlled rotation gates.
1359 """
1360 return None
1362 @staticmethod
1363 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1364 """
1365 Creates a Circuit10 ansatz.
1367 Length of flattened vector must be n_qubits*2
1369 Parameters
1370 ----------
1371 w : np.ndarray
1372 Weight vector of size n_qubits*2
1373 n_qubits : int
1374 Number of qubits
1375 noise_params : Optional[Dict[str, float]], optional
1376 Dictionary of noise parameters to apply to the gates
1377 """
1378 w_idx = 0
1379 # constant gates, independent of layers. has to be fixed
1380 for q in range(n_qubits):
1381 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1382 w_idx += 1
1384 if n_qubits > 1:
1385 for q in range(n_qubits - 1):
1386 Gates.CZ(
1387 wires=[
1388 (n_qubits - q - 2) % n_qubits,
1389 (n_qubits - q - 1) % n_qubits,
1390 ],
1391 noise_params=noise_params,
1392 )
1393 if n_qubits > 2:
1394 Gates.CZ(wires=[n_qubits - 1, 0], noise_params=noise_params)
1396 for q in range(n_qubits):
1397 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1398 w_idx += 1
1400 class Circuit_16(Circuit):
1401 @staticmethod
1402 def n_params_per_layer(n_qubits: int) -> int:
1403 """
1404 Returns the number of parameters per layer for the Circuit_16 ansatz.
1406 The number of parameters is calculated as n_qubits*3-1.
1408 Parameters
1409 ----------
1410 n_qubits : int
1411 Number of qubits in the circuit
1413 Returns
1414 -------
1415 int
1416 Number of parameters per layer
1417 """
1419 return n_qubits * 3 - 1
1421 @staticmethod
1422 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1423 """
1424 No controlled rotation gates available. Always None.
1426 Parameters
1427 ----------
1428 n_qubits : int
1429 Number of qubits in the circuit
1431 Returns
1432 -------
1433 Optional[np.ndarray]
1434 List of all controlled indices, or None if the circuit does not
1435 contain controlled rotation gates.
1436 """
1437 return None
1439 @staticmethod
1440 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1441 """
1442 Creates a Circuit16 ansatz.
1444 Length of flattened vector must be n_qubits*3-1
1446 Parameters
1447 ----------
1448 w : np.ndarray
1449 Weight vector of size n_qubits*3-1
1450 n_qubits : int
1451 Number of qubits
1452 noise_params : Optional[Dict[str, float]], optional
1453 Dictionary of noise parameters to apply to the gates
1454 """
1455 w_idx = 0
1456 for q in range(n_qubits):
1457 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1458 w_idx += 1
1459 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1460 w_idx += 1
1462 if n_qubits > 1:
1463 for q in range(n_qubits // 2):
1464 Gates.CRZ(
1465 w[w_idx],
1466 wires=[(2 * q + 1), (2 * q)],
1467 noise_params=noise_params,
1468 )
1469 w_idx += 1
1471 for q in range((n_qubits - 1) // 2):
1472 Gates.CRZ(
1473 w[w_idx],
1474 wires=[(2 * q + 2), (2 * q + 1)],
1475 noise_params=noise_params,
1476 )
1477 w_idx += 1
1479 class Circuit_17(Circuit):
1480 @staticmethod
1481 def n_params_per_layer(n_qubits: int) -> int:
1482 """
1483 Returns the number of parameters per layer for the Circuit_17 ansatz.
1485 The number of parameters is calculated as n_qubits*3-1.
1487 Parameters
1488 ----------
1489 n_qubits : int
1490 Number of qubits in the circuit
1492 Returns
1493 -------
1494 int
1495 Number of parameters per layer
1496 """
1498 return n_qubits * 3 - 1
1500 @staticmethod
1501 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1502 """
1503 No controlled rotation gates available. Always None.
1505 Parameters
1506 ----------
1507 n_qubits : int
1508 Number of qubits in the circuit
1510 Returns
1511 -------
1512 Optional[np.ndarray]
1513 List of all controlled indices, or None if the circuit does not
1514 contain controlled rotation gates.
1515 """
1516 return None
1518 @staticmethod
1519 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1520 """
1521 Creates a Circuit17 ansatz.
1523 Length of flattened vector must be n_qubits*3-1
1525 Parameters
1526 ----------
1527 w : np.ndarray
1528 Weight vector of size n_qubits*3-1
1529 n_qubits : int
1530 Number of qubits
1531 noise_params : Optional[Dict[str, float]], optional
1532 Dictionary of noise parameters to apply to the gates
1533 """
1534 w_idx = 0
1535 for q in range(n_qubits):
1536 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1537 w_idx += 1
1538 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1539 w_idx += 1
1541 if n_qubits > 1:
1542 for q in range(n_qubits // 2):
1543 Gates.CRX(
1544 w[w_idx],
1545 wires=[(2 * q + 1), (2 * q)],
1546 noise_params=noise_params,
1547 )
1548 w_idx += 1
1550 for q in range((n_qubits - 1) // 2):
1551 Gates.CRX(
1552 w[w_idx],
1553 wires=[(2 * q + 2), (2 * q + 1)],
1554 noise_params=noise_params,
1555 )
1556 w_idx += 1
1558 class Strongly_Entangling(Circuit):
1559 @staticmethod
1560 def n_params_per_layer(n_qubits: int) -> int:
1561 """
1562 Returns the number of parameters per layer for the
1563 Strongly Entangling ansatz.
1565 The number of parameters is calculated as n_qubits*6.
1567 Parameters
1568 ----------
1569 n_qubits : int
1570 Number of qubits in the circuit
1572 Returns
1573 -------
1574 int
1575 Number of parameters per layer
1576 """
1577 if n_qubits < 2:
1578 log.warning("Number of Qubits < 2, no entanglement available")
1579 return n_qubits * 6
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) -> None:
1601 """
1602 Creates a Strongly Entangling ansatz.
1604 Length of flattened vector must be n_qubits*6
1606 Parameters
1607 ----------
1608 w : np.ndarray
1609 Weight vector of size n_qubits*6
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
1626 if n_qubits > 1:
1627 for q in range(n_qubits):
1628 Gates.CX(wires=[q, (q + 1) % n_qubits], noise_params=noise_params)
1630 for q in range(n_qubits):
1631 Gates.Rot(
1632 w[w_idx],
1633 w[w_idx + 1],
1634 w[w_idx + 2],
1635 wires=q,
1636 noise_params=noise_params,
1637 )
1638 w_idx += 3
1640 if n_qubits > 1:
1641 for q in range(n_qubits):
1642 Gates.CX(
1643 wires=[q, (q + n_qubits // 2) % n_qubits],
1644 noise_params=noise_params,
1645 )
1647 class No_Entangling(Circuit):
1648 @staticmethod
1649 def n_params_per_layer(n_qubits: int) -> int:
1650 """
1651 Returns the number of parameters per layer for the NoEntangling ansatz.
1653 The number of parameters is calculated as n_qubits*3.
1655 Parameters
1656 ----------
1657 n_qubits : int
1658 Number of qubits in the circuit
1660 Returns
1661 -------
1662 int
1663 Number of parameters per layer
1664 """
1665 return n_qubits * 3
1667 @staticmethod
1668 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1669 """
1670 No controlled rotation gates available. Always None.
1672 Parameters
1673 ----------
1674 n_qubits : int
1675 Number of qubits in the circuit
1677 Returns
1678 -------
1679 Optional[np.ndarray]
1680 List of all controlled indices, or None if the circuit does not
1681 contain controlled rotation gates.
1682 """
1683 return None
1685 @staticmethod
1686 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1687 """
1688 Creates a circuit without entangling, but with U3 gates on all qubits
1690 Length of flattened vector must be n_qubits*3
1692 Parameters
1693 ----------
1694 w : np.ndarray
1695 Weight vector of size n_qubits*3
1696 n_qubits : int
1697 Number of qubits
1698 noise_params : Optional[Dict[str, float]], optional
1699 Dictionary of noise parameters to apply to the gates
1700 """
1701 w_idx = 0
1702 for q in range(n_qubits):
1703 Gates.Rot(
1704 w[w_idx],
1705 w[w_idx + 1],
1706 w[w_idx + 2],
1707 wires=q,
1708 noise_params=noise_params,
1709 )
1710 w_idx += 3