Coverage for qml_essentials/ansaetze.py: 91%
484 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-29 14:55 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-29 14:55 +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 raise NotImplementedError("n_params_per_layer method is not implemented")
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 raise NotImplementedError("get_control_indices method is not implemented")
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 np.array([])
64 return w[indices[0] : indices[1] : indices[2]]
66 @abstractmethod
67 def build(self, n_qubits: int, n_layers: int):
68 raise NotImplementedError("build method is not implemented")
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 """
122 def n_qubit_depolarizing_kraus(p: float, n: int) -> List[np.ndarray]:
123 if not (0.0 <= p <= 1.0):
124 raise ValueError(f"Probability p must be between 0 and 1, got {p}")
125 if n < 2:
126 raise ValueError(f"Number of qubits must be >= 2, got {n}")
128 Id = np.eye(2)
129 X = qml.matrix(qml.PauliX(0))
130 Y = qml.matrix(qml.PauliY(0))
131 Z = qml.matrix(qml.PauliZ(0))
132 paulis = [Id, X, Y, Z]
134 dim = 2**n
135 all_ops = []
137 # Generate all n-qubit Pauli tensor products:
138 for indices in itertools.product(range(4), repeat=n):
139 P = np.eye(1)
140 for idx in indices:
141 P = np.kron(P, paulis[idx])
142 all_ops.append(P)
144 # Identity operator corresponds to all zeros indices (Id^n)
145 K0 = np.sqrt(1 - p * (4**n - 1) / (4**n)) * np.eye(dim)
147 kraus_ops = []
148 for i, P in enumerate(all_ops):
149 if i == 0:
150 # Skip the identity, already handled as K0
151 continue
152 kraus_ops.append(np.sqrt(p / (4**n)) * P)
154 return [K0] + kraus_ops
156 return qml.QubitChannel(n_qubit_depolarizing_kraus(p, len(wires)), wires=wires)
158 @staticmethod
159 def Noise(
160 wires: Union[int, List[int]], noise_params: Optional[Dict[str, float]] = None
161 ) -> None:
162 """
163 Applies noise to the given wires.
165 Parameters
166 ----------
167 wires : Union[int, List[int]]
168 The wire(s) to apply the noise to.
169 noise_params : Optional[Dict[str, float]]
170 A dictionary of noise parameters. The following noise gates are
171 supported:
172 -BitFlip: Applies a bit flip error to the given wires.
173 -PhaseFlip: Applies a phase flip error to the given wires.
174 -Depolarizing: Applies a depolarizing channel error to the
175 given wires.
176 -MultiQubitDepolarizing: Applies a two-qubit depolarizing channel
177 error to the given wires.
179 All parameters are optional and default to 0.0 if not provided.
180 """
181 if noise_params is not None:
182 if isinstance(wires, int):
183 wires = [wires] # single qubit gate
185 # noise on single qubits
186 for wire in wires:
187 bf = noise_params.get("BitFlip", 0.0)
188 if bf > 0:
189 qml.BitFlip(bf, wires=wire)
191 pf = noise_params.get("PhaseFlip", 0.0)
192 if pf > 0:
193 qml.PhaseFlip(pf, wires=wire)
195 dp = noise_params.get("Depolarizing", 0.0)
196 if dp > 0:
197 qml.DepolarizingChannel(dp, wires=wire)
199 # noise on two-qubits
200 if len(wires) > 1:
201 p = noise_params.get("MultiQubitDepolarizing", 0.0)
202 if p > 0:
203 Gates.NQubitDepolarizingChannel(p, wires)
205 @staticmethod
206 def GateError(
207 w: float, noise_params: Optional[Dict[str, float]] = None
208 ) -> np.ndarray:
209 """
210 Applies a gate error to the given rotation angle(s).
212 Parameters
213 ----------
214 w : Union[float, np.ndarray, List[float]]
215 The rotation angle in radians.
216 noise_params : Optional[Dict[str, float]]
217 A dictionary of noise parameters. The following noise gates are
218 supported:
219 -GateError: Applies a normal distribution error to the rotation
220 angle. The standard deviation of the noise is specified by
221 the "GateError" key in the dictionary.
223 All parameters are optional and default to 0.0 if not provided.
225 Returns
226 -------
227 float
228 The modified rotation angle after applying the gate error.
229 """
230 if noise_params is not None and noise_params.get("GateError", None) is not None:
231 w += Gates.rng.normal(
232 0,
233 noise_params["GateError"],
234 w.shape if isinstance(w, np.ndarray) else 1,
235 )
236 return w
238 @staticmethod
239 def Rot(phi, theta, omega, wires, noise_params=None):
240 """
241 Applies a rotation gate to the given wires and adds `Noise`
243 Parameters
244 ----------
245 phi : Union[float, np.ndarray, List[float]]
246 The first rotation angle in radians.
247 theta : Union[float, np.ndarray, List[float]]
248 The second rotation angle in radians.
249 omega : Union[float, np.ndarray, List[float]]
250 The third rotation angle in radians.
251 wires : Union[int, List[int]]
252 The wire(s) to apply the rotation gate to.
253 noise_params : Optional[Dict[str, float]]
254 A dictionary of noise parameters. The following noise gates are
255 supported:
256 -BitFlip: Applies a bit flip error to the given wires.
257 -PhaseFlip: Applies a phase flip error to the given wires.
258 -Depolarizing: Applies a depolarizing channel error to the
259 given wires.
261 All parameters are optional and default to 0.0 if not provided.
262 """
263 if noise_params is not None and "GateError" in noise_params:
264 phi = Gates.GateError(phi, noise_params)
265 theta = Gates.GateError(theta, noise_params)
266 omega = Gates.GateError(omega, noise_params)
267 # phi += Gates.rng.normal(0, noise_params["GateError"])
268 # theta += Gates.rng.normal(0, noise_params["GateError"])
269 # omega += Gates.rng.normal(0, noise_params["GateError"])
270 qml.Rot(phi, theta, omega, wires=wires)
271 Gates.Noise(wires, noise_params)
273 @staticmethod
274 def RX(w, wires, noise_params=None):
275 """
276 Applies a rotation around the X axis to the given wires and adds `Noise`
278 Parameters
279 ----------
280 w : Union[float, np.ndarray, List[float]]
281 The rotation angle in radians.
282 wires : Union[int, List[int]]
283 The wire(s) to apply the rotation gate to.
284 noise_params : Optional[Dict[str, float]]
285 A dictionary of noise parameters. The following noise gates are
286 supported:
287 -BitFlip: Applies a bit flip error to the given wires.
288 -PhaseFlip: Applies a phase flip error to the given wires.
289 -Depolarizing: Applies a depolarizing channel error to the
290 given wires.
292 All parameters are optional and default to 0.0 if not provided.
293 """
294 w = Gates.GateError(w, noise_params)
295 qml.RX(w, wires=wires)
296 Gates.Noise(wires, noise_params)
298 @staticmethod
299 def RY(w, wires, noise_params=None):
300 """
301 Applies a rotation around the Y axis to the given wires and adds `Noise`
303 Parameters
304 ----------
305 w : Union[float, np.ndarray, List[float]]
306 The rotation angle in radians.
307 wires : Union[int, List[int]]
308 The wire(s) to apply the rotation gate to.
309 noise_params : Optional[Dict[str, float]]
310 A dictionary of noise parameters. The following noise gates are
311 supported:
312 -BitFlip: Applies a bit flip error to the given wires.
313 -PhaseFlip: Applies a phase flip error to the given wires.
314 -Depolarizing: Applies a depolarizing channel error to the
315 given wires.
317 All parameters are optional and default to 0.0 if not provided.
318 """
319 w = Gates.GateError(w, noise_params)
320 qml.RY(w, wires=wires)
321 Gates.Noise(wires, noise_params)
323 @staticmethod
324 def RZ(w, wires, noise_params=None):
325 """
326 Applies a rotation around the Z axis to the given wires and adds `Noise`
328 Parameters
329 ----------
330 w : Union[float, np.ndarray, List[float]]
331 The rotation angle in radians.
332 wires : Union[int, List[int]]
333 The wire(s) to apply the rotation gate to.
334 noise_params : Optional[Dict[str, float]]
335 A dictionary of noise parameters. The following noise gates are
336 supported:
337 -BitFlip: Applies a bit flip error to the given wires.
338 -PhaseFlip: Applies a phase flip error to the given wires.
339 -Depolarizing: Applies a depolarizing channel error to the
340 given wires.
342 All parameters are optional and default to 0.0 if not provided.
343 """
344 w = Gates.GateError(w, noise_params)
345 qml.RZ(w, wires=wires)
346 Gates.Noise(wires, noise_params)
348 @staticmethod
349 def CRX(w, wires, noise_params=None):
350 """
351 Applies a controlled rotation around the X axis to the given wires
352 and adds `Noise`
354 Parameters
355 ----------
356 w : Union[float, np.ndarray, List[float]]
357 The rotation angle in radians.
358 wires : Union[int, List[int]]
359 The wire(s) to apply the controlled rotation gate to.
360 noise_params : Optional[Dict[str, float]]
361 A dictionary of noise parameters. The following noise gates are
362 supported:
363 -BitFlip: Applies a bit flip error to the given wires.
364 -PhaseFlip: Applies a phase flip error to the given wires.
365 -Depolarizing: Applies a depolarizing channel error to the
366 given wires.
368 All parameters are optional and default to 0.0 if not provided.
369 """
370 w = Gates.GateError(w, noise_params)
371 qml.CRX(w, wires=wires)
372 Gates.Noise(wires, noise_params)
374 @staticmethod
375 def CRY(w, wires, noise_params=None):
376 """
377 Applies a controlled rotation around the Y axis to the given wires
378 and adds `Noise`
380 Parameters
381 ----------
382 w : Union[float, np.ndarray, List[float]]
383 The rotation angle in radians.
384 wires : Union[int, List[int]]
385 The wire(s) to apply the controlled rotation gate to.
386 noise_params : Optional[Dict[str, float]]
387 A dictionary of noise parameters. The following noise gates are
388 supported:
389 -BitFlip: Applies a bit flip error to the given wires.
390 -PhaseFlip: Applies a phase flip error to the given wires.
391 -Depolarizing: Applies a depolarizing channel error to the
392 given wires.
394 All parameters are optional and default to 0.0 if not provided.
395 """
396 w = Gates.GateError(w, noise_params)
397 qml.CRY(w, wires=wires)
398 Gates.Noise(wires, noise_params)
400 @staticmethod
401 def CRZ(w, wires, noise_params=None):
402 """
403 Applies a controlled rotation around the Z axis to the given wires
404 and adds `Noise`
406 Parameters
407 ----------
408 w : Union[float, np.ndarray, List[float]]
409 The rotation angle in radians.
410 wires : Union[int, List[int]]
411 The wire(s) to apply the controlled rotation gate to.
412 noise_params : Optional[Dict[str, float]]
413 A dictionary of noise parameters. The following noise gates are
414 supported:
415 -BitFlip: Applies a bit flip error to the given wires.
416 -PhaseFlip: Applies a phase flip error to the given wires.
417 -Depolarizing: Applies a depolarizing channel error to the
418 given wires.
420 All parameters are optional and default to 0.0 if not provided.
421 """
422 w = Gates.GateError(w, noise_params)
423 qml.CRZ(w, wires=wires)
424 Gates.Noise(wires, noise_params)
426 @staticmethod
427 def CX(wires, noise_params=None):
428 """
429 Applies a controlled NOT gate to the given wires and adds `Noise`
431 Parameters
432 ----------
433 wires : Union[int, List[int]]
434 The wire(s) to apply the controlled NOT gate to.
435 noise_params : Optional[Dict[str, float]]
436 A dictionary of noise parameters. The following noise gates are
437 supported:
438 -BitFlip: Applies a bit flip error to the given wires.
439 -PhaseFlip: Applies a phase flip error to the given wires.
440 -Depolarizing: Applies a depolarizing channel error to the
441 given wires.
443 All parameters are optional and default to 0.0 if not provided.
444 """
445 qml.CNOT(wires=wires)
446 Gates.Noise(wires, noise_params)
448 @staticmethod
449 def CY(wires, noise_params=None):
450 """
451 Applies a controlled Y gate to the given wires and adds `Noise`
453 Parameters
454 ----------
455 wires : Union[int, List[int]]
456 The wire(s) to apply the controlled Y gate to.
457 noise_params : Optional[Dict[str, float]]
458 A dictionary of noise parameters. The following noise gates are
459 supported:
460 -BitFlip: Applies a bit flip error to the given wires.
461 -PhaseFlip: Applies a phase flip error to the given wires.
462 -Depolarizing: Applies a depolarizing channel error to the
463 given wires.
465 All parameters are optional and default to 0.0 if not provided.
466 """
467 qml.CY(wires=wires)
468 Gates.Noise(wires, noise_params)
470 @staticmethod
471 def CZ(wires, noise_params=None):
472 """
473 Applies a controlled Z gate to the given wires and adds `Noise`
475 Parameters
476 ----------
477 wires : Union[int, List[int]]
478 The wire(s) to apply the controlled Z gate to.
479 noise_params : Optional[Dict[str, float]]
480 A dictionary of noise parameters. The following noise gates are
481 supported:
482 -BitFlip: Applies a bit flip error to the given wires.
483 -PhaseFlip: Applies a phase flip error to the given wires.
484 -Depolarizing: Applies a depolarizing channel error to the
485 given wires.
487 All parameters are optional and default to 0.0 if not provided.
488 """
489 qml.CZ(wires=wires)
490 Gates.Noise(wires, noise_params)
492 @staticmethod
493 def H(wires, noise_params=None):
494 """
495 Applies a Hadamard gate to the given wires and adds `Noise`
497 Parameters
498 ----------
499 wires : Union[int, List[int]]
500 The wire(s) to apply the Hadamard gate to.
501 noise_params : Optional[Dict[str, float]]
502 A dictionary of noise parameters. The following noise gates are
503 supported:
504 -BitFlip: Applies a bit flip error to the given wires.
505 -PhaseFlip: Applies a phase flip error to the given wires.
506 -Depolarizing: Applies a depolarizing channel error to the
507 given wires.
509 All parameters are optional and default to 0.0 if not provided.
510 """
511 qml.Hadamard(wires=wires)
512 Gates.Noise(wires, noise_params)
515class Ansaetze:
517 def get_available():
518 return [
519 Ansaetze.No_Ansatz,
520 Ansaetze.Circuit_1,
521 Ansaetze.Circuit_2,
522 Ansaetze.Circuit_3,
523 Ansaetze.Circuit_4,
524 Ansaetze.Circuit_6,
525 Ansaetze.Circuit_9,
526 Ansaetze.Circuit_10,
527 Ansaetze.Circuit_15,
528 Ansaetze.Circuit_16,
529 Ansaetze.Circuit_17,
530 Ansaetze.Circuit_18,
531 Ansaetze.Circuit_19,
532 Ansaetze.No_Entangling,
533 Ansaetze.Strongly_Entangling,
534 Ansaetze.Hardware_Efficient,
535 Ansaetze.GHZ,
536 ]
538 class No_Ansatz(Circuit):
539 @staticmethod
540 def n_params_per_layer(n_qubits: int) -> int:
541 return 0
543 @staticmethod
544 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
545 return None
547 @staticmethod
548 def build(w: np.ndarray, n_qubits: int, noise_params=None):
549 pass
551 class GHZ(Circuit):
552 @staticmethod
553 def n_params_per_layer(n_qubits: int) -> int:
554 return 0
556 @staticmethod
557 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
558 return None
560 @staticmethod
561 def build(w: np.ndarray, n_qubits: int, noise_params=None):
562 Gates.H(0, noise_params=noise_params)
564 for q in range(n_qubits - 1):
565 Gates.CX([q, q + 1], noise_params=noise_params)
567 class Hardware_Efficient(Circuit):
568 @staticmethod
569 def n_params_per_layer(n_qubits: int) -> int:
570 """
571 Returns the number of parameters per layer for the
572 Hardware Efficient Ansatz.
574 The number of parameters is 3 times the number of qubits when there
575 is more than one qubit, as each qubit contributes 3 parameters.
576 If the number of qubits is less than 2, a warning is logged since
577 no entanglement is possible, and a fixed number of 2 parameters is used.
579 Parameters
580 ----------
581 n_qubits : int
582 Number of qubits in the circuit
584 Returns
585 -------
586 int
587 Number of parameters required for one layer of the circuit
588 """
589 if n_qubits < 2:
590 log.warning("Number of Qubits < 2, no entanglement available")
591 return n_qubits * 3
593 @staticmethod
594 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
595 """
596 No controlled rotation gates available. Always None.
598 Parameters
599 ----------
600 n_qubits : int
601 Number of qubits in the circuit
603 Returns
604 -------
605 Optional[np.ndarray]
606 List of all controlled indices, or None if the circuit does not
607 contain controlled rotation gates.
608 """
609 return None
611 @staticmethod
612 def build(w: np.ndarray, n_qubits: int, noise_params=None):
613 """
614 Creates a Hardware-Efficient ansatz, as proposed in
615 https://arxiv.org/pdf/2309.03279
617 Parameters
618 ----------
619 w : np.ndarray
620 Weight vector of size n_qubits*3
621 n_qubits : int
622 Number of qubits
623 noise_params : Optional[Dict[str, float]], optional
624 Dictionary of noise parameters to apply to the gates
625 """
626 w_idx = 0
627 for q in range(n_qubits):
628 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
629 w_idx += 1
630 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
631 w_idx += 1
632 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
633 w_idx += 1
635 if n_qubits > 1:
636 for q in range(n_qubits // 2):
637 Gates.CX(wires=[(2 * q), (2 * q + 1)], noise_params=noise_params)
638 for q in range((n_qubits - 1) // 2):
639 Gates.CX(
640 wires=[(2 * q + 1), (2 * q + 2)], noise_params=noise_params
641 )
642 if n_qubits > 2:
643 Gates.CX(wires=[(n_qubits - 1), 0], noise_params=noise_params)
645 class Circuit_19(Circuit):
646 @staticmethod
647 def n_params_per_layer(n_qubits: int) -> int:
648 """
649 Returns the number of parameters per layer for Circuit_19.
651 The number of parameters is 3 times the number of qubits when there
652 is more than one qubit, as each qubit contributes 3 parameters.
653 If the number of qubits is less than 2, a warning is logged since
654 no entanglement is possible, and a fixed number of 2 parameters is used.
656 Parameters
657 ----------
658 n_qubits : int
659 Number of qubits in the circuit
661 Returns
662 -------
663 int
664 Number of parameters required for one layer of the circuit
665 """
667 if n_qubits > 1:
668 return n_qubits * 3
669 else:
670 log.warning("Number of Qubits < 2, no entanglement available")
671 return 2
673 @staticmethod
674 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
675 """
676 Returns the indices for the controlled rotation gates for one layer.
677 Indices should slice the list of all parameters for one layer as follows:
678 [indices[0]:indices[1]:indices[2]]
680 Parameters
681 ----------
682 n_qubits : int
683 Number of qubits in the circuit
685 Returns
686 -------
687 Optional[np.ndarray]
688 List of all controlled indices, or None if the circuit does not
689 contain controlled rotation gates.
690 """
691 if n_qubits > 1:
692 return [-n_qubits, None, None]
693 else:
694 return None
696 @staticmethod
697 def build(w: np.ndarray, n_qubits: int, noise_params=None):
698 """
699 Creates a Circuit19 ansatz.
701 Length of flattened vector must be n_qubits*3
702 because for >1 qubits there are three gates
704 Parameters
705 ----------
706 w : np.ndarray
707 Weight vector of size n_qubits*3
708 n_qubits : int
709 Number of qubits
710 noise_params : Optional[Dict[str, float]], optional
711 Dictionary of noise parameters to apply to the gates
712 """
713 w_idx = 0
714 for q in range(n_qubits):
715 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
716 w_idx += 1
717 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
718 w_idx += 1
720 if n_qubits > 1:
721 for q in range(n_qubits):
722 Gates.CRX(
723 w[w_idx],
724 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
725 noise_params=noise_params,
726 )
727 w_idx += 1
729 class Circuit_18(Circuit):
730 @staticmethod
731 def n_params_per_layer(n_qubits: int) -> int:
732 """
733 Returns the number of parameters per layer for Circuit_18.
735 The number of parameters is 3 times the number of qubits when there
736 is more than one qubit, as each qubit contributes 3 parameters.
737 If the number of qubits is less than 2, a warning is logged since
738 no entanglement is possible, and a fixed number of 2 parameters is used.
740 Parameters
741 ----------
742 n_qubits : int
743 Number of qubits in the circuit
745 Returns
746 -------
747 int
748 Number of parameters required for one layer of the circuit
749 """
750 if n_qubits > 1:
751 return n_qubits * 3
752 else:
753 log.warning("Number of Qubits < 2, no entanglement available")
754 return 2
756 @staticmethod
757 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
758 """
759 Returns the indices for the controlled rotation gates for one layer.
760 Indices should slice the list of all parameters for one layer as follows:
761 [indices[0]:indices[1]:indices[2]]
763 Parameters
764 ----------
765 n_qubits : int
766 Number of qubits in the circuit
768 Returns
769 -------
770 Optional[np.ndarray]
771 List of all controlled indices, or None if the circuit does not
772 contain controlled rotation gates.
773 """
774 if n_qubits > 1:
775 return [-n_qubits, None, None]
776 else:
777 return None
779 @staticmethod
780 def build(w: np.ndarray, n_qubits: int, noise_params=None):
781 """
782 Creates a Circuit18 ansatz.
784 Length of flattened vector must be n_qubits*3
786 Parameters
787 ----------
788 w : np.ndarray
789 Weight vector of size n_qubits*3
790 n_qubits : int
791 Number of qubits
792 noise_params : Optional[Dict[str, float]], optional
793 Dictionary of noise parameters to apply to the gates
794 """
795 w_idx = 0
796 for q in range(n_qubits):
797 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
798 w_idx += 1
799 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
800 w_idx += 1
802 if n_qubits > 1:
803 for q in range(n_qubits):
804 Gates.CRZ(
805 w[w_idx],
806 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
807 noise_params=noise_params,
808 )
809 w_idx += 1
811 class Circuit_15(Circuit):
812 @staticmethod
813 def n_params_per_layer(n_qubits: int) -> int:
814 """
815 Returns the number of parameters per layer for Circuit_15.
817 The number of parameters is 2 times the number of qubits.
818 A warning is logged if the number of qubits is less than 2.
820 Parameters
821 ----------
822 n_qubits : int
823 Number of qubits in the circuit
825 Returns
826 -------
827 int
828 Number of parameters required for one layer of the circuit
829 """
830 if n_qubits > 1:
831 return n_qubits * 2
832 else:
833 log.warning("Number of Qubits < 2, no entanglement available")
834 return 2
836 @staticmethod
837 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
838 """
839 No controlled rotation gates available. Always None.
841 Parameters
842 ----------
843 n_qubits : int
844 Number of qubits in the circuit
846 Returns
847 -------
848 Optional[np.ndarray]
849 List of all controlled indices, or None if the circuit does not
850 contain controlled rotation gates.
851 """
852 return None
854 @staticmethod
855 def build(w: np.ndarray, n_qubits: int, noise_params=None):
856 """
857 Creates a Circuit15 ansatz.
859 Length of flattened vector must be n_qubits*2
860 because for >1 qubits there are three gates
862 Parameters
863 ----------
864 w : np.ndarray
865 Weight vector of size n_qubits*2
866 n_qubits : int
867 Number of qubits
868 noise_params : Optional[Dict[str, float]], optional
869 Dictionary of noise parameters to apply to the gates
870 """
871 w_idx = 0
872 for q in range(n_qubits):
873 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
874 w_idx += 1
876 if n_qubits > 1:
877 for q in range(n_qubits):
878 Gates.CX(
879 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
880 noise_params=noise_params,
881 )
883 for q in range(n_qubits):
884 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
885 w_idx += 1
887 if n_qubits > 1:
888 for q in range(n_qubits):
889 Gates.CX(
890 wires=[(q - 1) % n_qubits, (q - 2) % n_qubits],
891 noise_params=noise_params,
892 )
894 class Circuit_9(Circuit):
895 @staticmethod
896 def n_params_per_layer(n_qubits: int) -> int:
897 """
898 Returns the number of parameters per layer for Circuit_9.
900 The number of parameters is equal to the number of qubits.
902 Parameters
903 ----------
904 n_qubits : int
905 Number of qubits in the circuit
907 Returns
908 -------
909 int
910 Number of parameters required for one layer of the circuit
911 """
912 return n_qubits
914 @staticmethod
915 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
916 """
917 No controlled rotation gates available. Always None.
919 Parameters
920 ----------
921 n_qubits : int
922 Number of qubits in the circuit
924 Returns
925 -------
926 Optional[np.ndarray]
927 List of all controlled indices, or None if the circuit does not
928 contain controlled rotation gates.
929 """
930 return None
932 @staticmethod
933 def build(w: np.ndarray, n_qubits: int, noise_params=None):
934 """
935 Creates a Circuit9 ansatz.
937 Length of flattened vector must be n_qubits
939 Parameters
940 ----------
941 w : np.ndarray
942 Weight vector of size n_qubits
943 n_qubits : int
944 Number of qubits
945 noise_params : Optional[Dict[str, float]], optional
946 Dictionary of noise parameters to apply to the gates
947 """
948 w_idx = 0
949 for q in range(n_qubits):
950 Gates.H(wires=q, noise_params=noise_params)
952 if n_qubits > 1:
953 for q in range(n_qubits - 1):
954 Gates.CZ(
955 wires=[n_qubits - q - 2, n_qubits - q - 1],
956 noise_params=noise_params,
957 )
959 for q in range(n_qubits):
960 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
961 w_idx += 1
963 class Circuit_6(Circuit):
964 @staticmethod
965 def n_params_per_layer(n_qubits: int) -> int:
966 """
967 Returns the number of parameters per layer for Circuit_6.
969 The total number of parameters is n_qubits*3+n_qubits**2, which is
970 the number of rotations n_qubits*3 plus the number of entangling gates
971 n_qubits**2.
973 If n_qubits is 1, the number of parameters is 4, and a warning is logged
974 since no entanglement is possible.
976 Parameters
977 ----------
978 n_qubits : int
979 Number of qubits
981 Returns
982 -------
983 int
984 Number of parameters per layer
985 """
986 if n_qubits > 1:
987 return n_qubits * 3 + n_qubits**2
988 else:
989 log.warning("Number of Qubits < 2, no entanglement available")
990 return 4
992 @staticmethod
993 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
994 """
995 Returns the indices for the controlled rotation gates for one layer.
996 Indices should slice the list of all parameters for one layer as follows:
997 [indices[0]:indices[1]:indices[2]]
999 Parameters
1000 ----------
1001 n_qubits : int
1002 Number of qubits in the circuit
1004 Returns
1005 -------
1006 Optional[np.ndarray]
1007 List of all controlled indices, or None if the circuit does not
1008 contain controlled rotation gates.
1009 """
1010 if n_qubits > 1:
1011 return [-n_qubits, None, None]
1012 else:
1013 return None
1015 @staticmethod
1016 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1017 """
1018 Creates a Circuit6 ansatz.
1020 Length of flattened vector must be
1021 n_qubits*4+n_qubits*(n_qubits-1) =
1022 n_qubits*3+n_qubits**2
1024 Parameters
1025 ----------
1026 w : np.ndarray
1027 Weight vector of size
1028 n_layers*(n_qubits*3+n_qubits**2)
1029 n_qubits : int
1030 Number of qubits
1031 noise_params : Optional[Dict[str, float]], optional
1032 Dictionary of noise parameters to apply to the gates
1033 """
1034 w_idx = 0
1035 for q in range(n_qubits):
1036 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1037 w_idx += 1
1038 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1039 w_idx += 1
1041 if n_qubits > 1:
1042 for ql in range(n_qubits):
1043 for q in range(n_qubits):
1044 if q == ql:
1045 continue
1046 Gates.CRX(
1047 w[w_idx],
1048 wires=[n_qubits - ql - 1, (n_qubits - q - 1) % n_qubits],
1049 noise_params=noise_params,
1050 )
1051 w_idx += 1
1053 for q in range(n_qubits):
1054 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1055 w_idx += 1
1056 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1057 w_idx += 1
1059 class Circuit_1(Circuit):
1060 @staticmethod
1061 def n_params_per_layer(n_qubits: int) -> int:
1062 """
1063 Returns the number of parameters per layer for Circuit_1.
1065 The total number of parameters is determined by the number of qubits, with
1066 each qubit contributing 2 parameters.
1068 Parameters
1069 ----------
1070 n_qubits : int
1071 Number of qubits in the circuit
1073 Returns
1074 -------
1075 int
1076 Number of parameters per layer
1077 """
1078 return n_qubits * 2
1080 @staticmethod
1081 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1082 """
1083 No controlled rotation gates available. Always None.
1085 Parameters
1086 ----------
1087 n_qubits : int
1088 Number of qubits in the circuit
1090 Returns
1091 -------
1092 Optional[np.ndarray]
1093 List of all controlled indices, or None if the circuit does not
1094 contain controlled rotation gates.
1095 """
1096 return None
1098 @staticmethod
1099 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1100 """
1101 Creates a Circuit1 ansatz.
1103 Length of flattened vector must be n_qubits*2
1105 Parameters
1106 ----------
1107 w : np.ndarray
1108 Weight vector of size n_qubits*2
1109 n_qubits : int
1110 Number of qubits
1111 noise_params : Optional[Dict[str, float]], optional
1112 Dictionary of noise parameters to apply to the gates
1113 """
1114 w_idx = 0
1115 for q in range(n_qubits):
1116 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1117 w_idx += 1
1118 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1119 w_idx += 1
1121 class Circuit_2(Circuit):
1122 @staticmethod
1123 def n_params_per_layer(n_qubits: int) -> int:
1124 """
1125 Returns the number of parameters per layer for Circuit_2.
1127 The total number of parameters is determined by the number of qubits, with
1128 each qubit contributing 2 parameters.
1130 Parameters
1131 ----------
1132 n_qubits : int
1133 Number of qubits in the circuit
1135 Returns
1136 -------
1137 int
1138 Number of parameters per layer
1139 """
1140 return n_qubits * 2
1142 @staticmethod
1143 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1144 """
1145 No controlled rotation gates available. Always None.
1147 Parameters
1148 ----------
1149 n_qubits : int
1150 Number of qubits in the circuit
1152 Returns
1153 -------
1154 Optional[np.ndarray]
1155 List of all controlled indices, or None if the circuit does not
1156 contain controlled rotation gates.
1157 """
1158 return None
1160 @staticmethod
1161 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1162 """
1163 Creates a Circuit2 ansatz.
1165 Length of flattened vector must be n_qubits*2
1167 Parameters
1168 ----------
1169 w : np.ndarray
1170 Weight vector of size n_qubits*2
1171 n_qubits : int
1172 Number of qubits
1173 noise_params : Optional[Dict[str, float]], optional
1174 Dictionary of noise parameters to apply to the gates
1175 """
1176 w_idx = 0
1177 for q in range(n_qubits):
1178 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1179 w_idx += 1
1180 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1181 w_idx += 1
1183 if n_qubits > 1:
1184 for q in range(n_qubits - 1):
1185 Gates.CX(
1186 wires=[n_qubits - q - 1, n_qubits - q - 2],
1187 noise_params=noise_params,
1188 )
1190 class Circuit_3(Circuit):
1191 @staticmethod
1192 def n_params_per_layer(n_qubits: int) -> int:
1193 """
1194 Calculates the number of parameters per layer for Circuit3.
1196 The number of parameters per layer is given by the number of qubits, with
1197 each qubit contributing 3 parameters. The last qubit only contributes 2
1198 parameters because it is the target qubit for the controlled gates.
1200 Parameters
1201 ----------
1202 n_qubits : int
1203 Number of qubits in the circuit
1205 Returns
1206 -------
1207 int
1208 Number of parameters per layer
1209 """
1210 return n_qubits * 3 - 1
1212 @staticmethod
1213 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1214 """
1215 No controlled rotation gates available. Always None.
1217 Parameters
1218 ----------
1219 n_qubits : int
1220 Number of qubits in the circuit
1222 Returns
1223 -------
1224 Optional[np.ndarray]
1225 List of all controlled indices, or None if the circuit does not
1226 contain controlled rotation gates.
1227 """
1228 return None
1230 @staticmethod
1231 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1232 """
1233 Creates a Circuit3 ansatz.
1235 Length of flattened vector must be n_qubits*3-1
1237 Parameters
1238 ----------
1239 w : np.ndarray
1240 Weight vector of size n_qubits*3-1
1241 n_qubits : int
1242 Number of qubits
1243 noise_params : Optional[Dict[str, float]], optional
1244 Dictionary of noise parameters to apply to the gates
1245 """
1246 w_idx = 0
1247 for q in range(n_qubits):
1248 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1249 w_idx += 1
1250 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1251 w_idx += 1
1253 if n_qubits > 1:
1254 for q in range(n_qubits - 1):
1255 Gates.CRZ(
1256 w[w_idx],
1257 wires=[n_qubits - q - 1, n_qubits - q - 2],
1258 noise_params=noise_params,
1259 )
1260 w_idx += 1
1262 class Circuit_4(Circuit):
1263 @staticmethod
1264 def n_params_per_layer(n_qubits: int) -> int:
1265 """
1266 Returns the number of parameters per layer for the Circuit_4 ansatz.
1268 The number of parameters is calculated as n_qubits*3-1.
1270 Parameters
1271 ----------
1272 n_qubits : int
1273 Number of qubits in the circuit
1275 Returns
1276 -------
1277 int
1278 Number of parameters per layer
1279 """
1280 return n_qubits * 3 - 1
1282 @staticmethod
1283 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1284 """
1285 No controlled rotation gates available. Always None.
1287 Parameters
1288 ----------
1289 n_qubits : int
1290 Number of qubits in the circuit
1292 Returns
1293 -------
1294 Optional[np.ndarray]
1295 List of all controlled indices, or None if the circuit does not
1296 contain controlled rotation gates.
1297 """
1298 return None
1300 @staticmethod
1301 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1302 """
1303 Creates a Circuit4 ansatz.
1305 Length of flattened vector must be n_qubits*3-1
1307 Parameters
1308 ----------
1309 w : np.ndarray
1310 Weight vector of size n_qubits*3-1
1311 n_qubits : int
1312 Number of qubits
1313 noise_params : Optional[Dict[str, float]], optional
1314 Dictionary of noise parameters to apply to the gates
1315 """
1316 w_idx = 0
1317 for q in range(n_qubits):
1318 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1319 w_idx += 1
1320 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1321 w_idx += 1
1323 if n_qubits > 1:
1324 for q in range(n_qubits - 1):
1325 Gates.CRX(
1326 w[w_idx],
1327 wires=[n_qubits - q - 1, n_qubits - q - 2],
1328 noise_params=noise_params,
1329 )
1330 w_idx += 1
1332 class Circuit_10(Circuit):
1333 @staticmethod
1334 def n_params_per_layer(n_qubits: int) -> int:
1335 """
1336 Returns the number of parameters per layer for the Circuit_10 ansatz.
1338 The number of parameters is calculated as n_qubits*2.
1340 Parameters
1341 ----------
1342 n_qubits : int
1343 Number of qubits in the circuit
1345 Returns
1346 -------
1347 int
1348 Number of parameters per layer
1349 """
1350 return n_qubits * 2 # constant gates not considered yet. has to be fixed
1352 @staticmethod
1353 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1354 """
1355 No controlled rotation gates available. Always None.
1357 Parameters
1358 ----------
1359 n_qubits : int
1360 Number of qubits in the circuit
1362 Returns
1363 -------
1364 Optional[np.ndarray]
1365 List of all controlled indices, or None if the circuit does not
1366 contain controlled rotation gates.
1367 """
1368 return None
1370 @staticmethod
1371 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1372 """
1373 Creates a Circuit10 ansatz.
1375 Length of flattened vector must be n_qubits*2
1377 Parameters
1378 ----------
1379 w : np.ndarray
1380 Weight vector of size n_qubits*2
1381 n_qubits : int
1382 Number of qubits
1383 noise_params : Optional[Dict[str, float]], optional
1384 Dictionary of noise parameters to apply to the gates
1385 """
1386 w_idx = 0
1387 # constant gates, independent of layers. has to be fixed
1388 for q in range(n_qubits):
1389 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1390 w_idx += 1
1392 if n_qubits > 1:
1393 for q in range(n_qubits - 1):
1394 Gates.CZ(
1395 wires=[
1396 (n_qubits - q - 2) % n_qubits,
1397 (n_qubits - q - 1) % n_qubits,
1398 ],
1399 noise_params=noise_params,
1400 )
1401 if n_qubits > 2:
1402 Gates.CZ(wires=[n_qubits - 1, 0], noise_params=noise_params)
1404 for q in range(n_qubits):
1405 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1406 w_idx += 1
1408 class Circuit_16(Circuit):
1409 @staticmethod
1410 def n_params_per_layer(n_qubits: int) -> int:
1411 """
1412 Returns the number of parameters per layer for the Circuit_16 ansatz.
1414 The number of parameters is calculated as n_qubits*3-1.
1416 Parameters
1417 ----------
1418 n_qubits : int
1419 Number of qubits in the circuit
1421 Returns
1422 -------
1423 int
1424 Number of parameters per layer
1425 """
1427 return n_qubits * 3 - 1
1429 @staticmethod
1430 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1431 """
1432 No controlled rotation gates available. Always None.
1434 Parameters
1435 ----------
1436 n_qubits : int
1437 Number of qubits in the circuit
1439 Returns
1440 -------
1441 Optional[np.ndarray]
1442 List of all controlled indices, or None if the circuit does not
1443 contain controlled rotation gates.
1444 """
1445 return None
1447 @staticmethod
1448 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1449 """
1450 Creates a Circuit16 ansatz.
1452 Length of flattened vector must be n_qubits*3-1
1454 Parameters
1455 ----------
1456 w : np.ndarray
1457 Weight vector of size n_qubits*3-1
1458 n_qubits : int
1459 Number of qubits
1460 noise_params : Optional[Dict[str, float]], optional
1461 Dictionary of noise parameters to apply to the gates
1462 """
1463 w_idx = 0
1464 for q in range(n_qubits):
1465 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1466 w_idx += 1
1467 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1468 w_idx += 1
1470 if n_qubits > 1:
1471 for q in range(n_qubits // 2):
1472 Gates.CRZ(
1473 w[w_idx],
1474 wires=[(2 * q + 1), (2 * q)],
1475 noise_params=noise_params,
1476 )
1477 w_idx += 1
1479 for q in range((n_qubits - 1) // 2):
1480 Gates.CRZ(
1481 w[w_idx],
1482 wires=[(2 * q + 2), (2 * q + 1)],
1483 noise_params=noise_params,
1484 )
1485 w_idx += 1
1487 class Circuit_17(Circuit):
1488 @staticmethod
1489 def n_params_per_layer(n_qubits: int) -> int:
1490 """
1491 Returns the number of parameters per layer for the Circuit_17 ansatz.
1493 The number of parameters is calculated as n_qubits*3-1.
1495 Parameters
1496 ----------
1497 n_qubits : int
1498 Number of qubits in the circuit
1500 Returns
1501 -------
1502 int
1503 Number of parameters per layer
1504 """
1506 return n_qubits * 3 - 1
1508 @staticmethod
1509 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1510 """
1511 No controlled rotation gates available. Always None.
1513 Parameters
1514 ----------
1515 n_qubits : int
1516 Number of qubits in the circuit
1518 Returns
1519 -------
1520 Optional[np.ndarray]
1521 List of all controlled indices, or None if the circuit does not
1522 contain controlled rotation gates.
1523 """
1524 return None
1526 @staticmethod
1527 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1528 """
1529 Creates a Circuit17 ansatz.
1531 Length of flattened vector must be n_qubits*3-1
1533 Parameters
1534 ----------
1535 w : np.ndarray
1536 Weight vector of size n_qubits*3-1
1537 n_qubits : int
1538 Number of qubits
1539 noise_params : Optional[Dict[str, float]], optional
1540 Dictionary of noise parameters to apply to the gates
1541 """
1542 w_idx = 0
1543 for q in range(n_qubits):
1544 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1545 w_idx += 1
1546 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1547 w_idx += 1
1549 if n_qubits > 1:
1550 for q in range(n_qubits // 2):
1551 Gates.CRX(
1552 w[w_idx],
1553 wires=[(2 * q + 1), (2 * q)],
1554 noise_params=noise_params,
1555 )
1556 w_idx += 1
1558 for q in range((n_qubits - 1) // 2):
1559 Gates.CRX(
1560 w[w_idx],
1561 wires=[(2 * q + 2), (2 * q + 1)],
1562 noise_params=noise_params,
1563 )
1564 w_idx += 1
1566 class Strongly_Entangling(Circuit):
1567 @staticmethod
1568 def n_params_per_layer(n_qubits: int) -> int:
1569 """
1570 Returns the number of parameters per layer for the
1571 Strongly Entangling ansatz.
1573 The number of parameters is calculated as n_qubits*6.
1575 Parameters
1576 ----------
1577 n_qubits : int
1578 Number of qubits in the circuit
1580 Returns
1581 -------
1582 int
1583 Number of parameters per layer
1584 """
1585 if n_qubits < 2:
1586 log.warning("Number of Qubits < 2, no entanglement available")
1587 return n_qubits * 6
1589 @staticmethod
1590 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1591 """
1592 No controlled rotation gates available. Always None.
1594 Parameters
1595 ----------
1596 n_qubits : int
1597 Number of qubits in the circuit
1599 Returns
1600 -------
1601 Optional[np.ndarray]
1602 List of all controlled indices, or None if the circuit does not
1603 contain controlled rotation gates.
1604 """
1605 return None
1607 @staticmethod
1608 def build(w: np.ndarray, n_qubits: int, noise_params=None) -> None:
1609 """
1610 Creates a Strongly Entangling ansatz.
1612 Length of flattened vector must be n_qubits*6
1614 Parameters
1615 ----------
1616 w : np.ndarray
1617 Weight vector of size n_qubits*6
1618 n_qubits : int
1619 Number of qubits
1620 noise_params : Optional[Dict[str, float]], optional
1621 Dictionary of noise parameters to apply to the gates
1622 """
1623 w_idx = 0
1624 for q in range(n_qubits):
1625 Gates.Rot(
1626 w[w_idx],
1627 w[w_idx + 1],
1628 w[w_idx + 2],
1629 wires=q,
1630 noise_params=noise_params,
1631 )
1632 w_idx += 3
1634 if n_qubits > 1:
1635 for q in range(n_qubits):
1636 Gates.CX(wires=[q, (q + 1) % n_qubits], noise_params=noise_params)
1638 for q in range(n_qubits):
1639 Gates.Rot(
1640 w[w_idx],
1641 w[w_idx + 1],
1642 w[w_idx + 2],
1643 wires=q,
1644 noise_params=noise_params,
1645 )
1646 w_idx += 3
1648 if n_qubits > 1:
1649 for q in range(n_qubits):
1650 Gates.CX(
1651 wires=[q, (q + n_qubits // 2) % n_qubits],
1652 noise_params=noise_params,
1653 )
1655 class No_Entangling(Circuit):
1656 @staticmethod
1657 def n_params_per_layer(n_qubits: int) -> int:
1658 """
1659 Returns the number of parameters per layer for the NoEntangling ansatz.
1661 The number of parameters is calculated as n_qubits*3.
1663 Parameters
1664 ----------
1665 n_qubits : int
1666 Number of qubits in the circuit
1668 Returns
1669 -------
1670 int
1671 Number of parameters per layer
1672 """
1673 return n_qubits * 3
1675 @staticmethod
1676 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1677 """
1678 No controlled rotation gates available. Always None.
1680 Parameters
1681 ----------
1682 n_qubits : int
1683 Number of qubits in the circuit
1685 Returns
1686 -------
1687 Optional[np.ndarray]
1688 List of all controlled indices, or None if the circuit does not
1689 contain controlled rotation gates.
1690 """
1691 return None
1693 @staticmethod
1694 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1695 """
1696 Creates a circuit without entangling, but with U3 gates on all qubits
1698 Length of flattened vector must be n_qubits*3
1700 Parameters
1701 ----------
1702 w : np.ndarray
1703 Weight vector of size n_qubits*3
1704 n_qubits : int
1705 Number of qubits
1706 noise_params : Optional[Dict[str, float]], optional
1707 Dictionary of noise parameters to apply to the gates
1708 """
1709 w_idx = 0
1710 for q in range(n_qubits):
1711 Gates.Rot(
1712 w[w_idx],
1713 w[w_idx + 1],
1714 w[w_idx + 2],
1715 wires=q,
1716 noise_params=noise_params,
1717 )
1718 w_idx += 3