Coverage for qml_essentials/ansaetze.py: 96%
490 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-08-18 11:46 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-08-18 11:46 +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 # TODO: implement
1011 return None
1013 @staticmethod
1014 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1015 """
1016 Creates a Circuit6 ansatz.
1018 Length of flattened vector must be
1019 n_qubits*4+n_qubits*(n_qubits-1) =
1020 n_qubits*3+n_qubits**2
1022 Parameters
1023 ----------
1024 w : np.ndarray
1025 Weight vector of size
1026 n_layers*(n_qubits*3+n_qubits**2)
1027 n_qubits : int
1028 Number of qubits
1029 noise_params : Optional[Dict[str, float]], optional
1030 Dictionary of noise parameters to apply to the gates
1031 """
1032 w_idx = 0
1033 for q in range(n_qubits):
1034 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1035 w_idx += 1
1036 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1037 w_idx += 1
1039 if n_qubits > 1:
1040 for ql in range(n_qubits):
1041 for q in range(n_qubits):
1042 if q == ql:
1043 continue
1044 Gates.CRX(
1045 w[w_idx],
1046 wires=[n_qubits - ql - 1, (n_qubits - q - 1) % n_qubits],
1047 noise_params=noise_params,
1048 )
1049 w_idx += 1
1051 for q in range(n_qubits):
1052 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1053 w_idx += 1
1054 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1055 w_idx += 1
1057 class Circuit_1(Circuit):
1058 @staticmethod
1059 def n_params_per_layer(n_qubits: int) -> int:
1060 """
1061 Returns the number of parameters per layer for Circuit_1.
1063 The total number of parameters is determined by the number of qubits, with
1064 each qubit contributing 2 parameters.
1066 Parameters
1067 ----------
1068 n_qubits : int
1069 Number of qubits in the circuit
1071 Returns
1072 -------
1073 int
1074 Number of parameters per layer
1075 """
1076 return n_qubits * 2
1078 @staticmethod
1079 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1080 """
1081 No controlled rotation gates available. Always None.
1083 Parameters
1084 ----------
1085 n_qubits : int
1086 Number of qubits in the circuit
1088 Returns
1089 -------
1090 Optional[np.ndarray]
1091 List of all controlled indices, or None if the circuit does not
1092 contain controlled rotation gates.
1093 """
1094 return None
1096 @staticmethod
1097 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1098 """
1099 Creates a Circuit1 ansatz.
1101 Length of flattened vector must be n_qubits*2
1103 Parameters
1104 ----------
1105 w : np.ndarray
1106 Weight vector of size n_qubits*2
1107 n_qubits : int
1108 Number of qubits
1109 noise_params : Optional[Dict[str, float]], optional
1110 Dictionary of noise parameters to apply to the gates
1111 """
1112 w_idx = 0
1113 for q in range(n_qubits):
1114 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1115 w_idx += 1
1116 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1117 w_idx += 1
1119 class Circuit_2(Circuit):
1120 @staticmethod
1121 def n_params_per_layer(n_qubits: int) -> int:
1122 """
1123 Returns the number of parameters per layer for Circuit_2.
1125 The total number of parameters is determined by the number of qubits, with
1126 each qubit contributing 2 parameters.
1128 Parameters
1129 ----------
1130 n_qubits : int
1131 Number of qubits in the circuit
1133 Returns
1134 -------
1135 int
1136 Number of parameters per layer
1137 """
1138 return n_qubits * 2
1140 @staticmethod
1141 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1142 """
1143 No controlled rotation gates available. Always None.
1145 Parameters
1146 ----------
1147 n_qubits : int
1148 Number of qubits in the circuit
1150 Returns
1151 -------
1152 Optional[np.ndarray]
1153 List of all controlled indices, or None if the circuit does not
1154 contain controlled rotation gates.
1155 """
1156 return None
1158 @staticmethod
1159 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1160 """
1161 Creates a Circuit2 ansatz.
1163 Length of flattened vector must be n_qubits*2
1165 Parameters
1166 ----------
1167 w : np.ndarray
1168 Weight vector of size n_qubits*2
1169 n_qubits : int
1170 Number of qubits
1171 noise_params : Optional[Dict[str, float]], optional
1172 Dictionary of noise parameters to apply to the gates
1173 """
1174 w_idx = 0
1175 for q in range(n_qubits):
1176 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1177 w_idx += 1
1178 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1179 w_idx += 1
1181 if n_qubits > 1:
1182 for q in range(n_qubits - 1):
1183 Gates.CX(
1184 wires=[n_qubits - q - 1, n_qubits - q - 2],
1185 noise_params=noise_params,
1186 )
1188 class Circuit_3(Circuit):
1189 @staticmethod
1190 def n_params_per_layer(n_qubits: int) -> int:
1191 """
1192 Calculates the number of parameters per layer for Circuit3.
1194 The number of parameters per layer is given by the number of qubits, with
1195 each qubit contributing 3 parameters. The last qubit only contributes 2
1196 parameters because it is the target qubit for the controlled gates.
1198 Parameters
1199 ----------
1200 n_qubits : int
1201 Number of qubits in the circuit
1203 Returns
1204 -------
1205 int
1206 Number of parameters per layer
1207 """
1208 return n_qubits * 3 - 1
1210 @staticmethod
1211 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1212 """
1213 No controlled rotation gates available. Always None.
1215 Parameters
1216 ----------
1217 n_qubits : int
1218 Number of qubits in the circuit
1220 Returns
1221 -------
1222 Optional[np.ndarray]
1223 List of all controlled indices, or None if the circuit does not
1224 contain controlled rotation gates.
1225 """
1226 if n_qubits > 1:
1227 return [-(n_qubits - 1), None, None]
1228 else:
1229 return None
1231 @staticmethod
1232 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1233 """
1234 Creates a Circuit3 ansatz.
1236 Length of flattened vector must be n_qubits*3-1
1238 Parameters
1239 ----------
1240 w : np.ndarray
1241 Weight vector of size n_qubits*3-1
1242 n_qubits : int
1243 Number of qubits
1244 noise_params : Optional[Dict[str, float]], optional
1245 Dictionary of noise parameters to apply to the gates
1246 """
1247 w_idx = 0
1248 for q in range(n_qubits):
1249 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1250 w_idx += 1
1251 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1252 w_idx += 1
1254 if n_qubits > 1:
1255 for q in range(n_qubits - 1):
1256 Gates.CRZ(
1257 w[w_idx],
1258 wires=[n_qubits - q - 1, n_qubits - q - 2],
1259 noise_params=noise_params,
1260 )
1261 w_idx += 1
1263 class Circuit_4(Circuit):
1264 @staticmethod
1265 def n_params_per_layer(n_qubits: int) -> int:
1266 """
1267 Returns the number of parameters per layer for the Circuit_4 ansatz.
1269 The number of parameters is calculated as n_qubits*3-1.
1271 Parameters
1272 ----------
1273 n_qubits : int
1274 Number of qubits in the circuit
1276 Returns
1277 -------
1278 int
1279 Number of parameters per layer
1280 """
1281 return n_qubits * 3 - 1
1283 @staticmethod
1284 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1285 """
1286 No controlled rotation gates available. Always None.
1288 Parameters
1289 ----------
1290 n_qubits : int
1291 Number of qubits in the circuit
1293 Returns
1294 -------
1295 Optional[np.ndarray]
1296 List of all controlled indices, or None if the circuit does not
1297 contain controlled rotation gates.
1298 """
1299 if n_qubits > 1:
1300 return [-(n_qubits - 1), None, None]
1301 else:
1302 return None
1304 @staticmethod
1305 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1306 """
1307 Creates a Circuit4 ansatz.
1309 Length of flattened vector must be n_qubits*3-1
1311 Parameters
1312 ----------
1313 w : np.ndarray
1314 Weight vector of size n_qubits*3-1
1315 n_qubits : int
1316 Number of qubits
1317 noise_params : Optional[Dict[str, float]], optional
1318 Dictionary of noise parameters to apply to the gates
1319 """
1320 w_idx = 0
1321 for q in range(n_qubits):
1322 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1323 w_idx += 1
1324 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1325 w_idx += 1
1327 if n_qubits > 1:
1328 for q in range(n_qubits - 1):
1329 Gates.CRX(
1330 w[w_idx],
1331 wires=[n_qubits - q - 1, n_qubits - q - 2],
1332 noise_params=noise_params,
1333 )
1334 w_idx += 1
1336 class Circuit_10(Circuit):
1337 @staticmethod
1338 def n_params_per_layer(n_qubits: int) -> int:
1339 """
1340 Returns the number of parameters per layer for the Circuit_10 ansatz.
1342 The number of parameters is calculated as n_qubits*2.
1344 Parameters
1345 ----------
1346 n_qubits : int
1347 Number of qubits in the circuit
1349 Returns
1350 -------
1351 int
1352 Number of parameters per layer
1353 """
1354 return n_qubits * 2 # constant gates not considered yet. has to be fixed
1356 @staticmethod
1357 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1358 """
1359 No controlled rotation gates available. Always None.
1361 Parameters
1362 ----------
1363 n_qubits : int
1364 Number of qubits in the circuit
1366 Returns
1367 -------
1368 Optional[np.ndarray]
1369 List of all controlled indices, or None if the circuit does not
1370 contain controlled rotation gates.
1371 """
1372 return None
1374 @staticmethod
1375 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1376 """
1377 Creates a Circuit10 ansatz.
1379 Length of flattened vector must be n_qubits*2
1381 Parameters
1382 ----------
1383 w : np.ndarray
1384 Weight vector of size n_qubits*2
1385 n_qubits : int
1386 Number of qubits
1387 noise_params : Optional[Dict[str, float]], optional
1388 Dictionary of noise parameters to apply to the gates
1389 """
1390 w_idx = 0
1391 # constant gates, independent of layers. has to be fixed
1392 for q in range(n_qubits):
1393 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1394 w_idx += 1
1396 if n_qubits > 1:
1397 for q in range(n_qubits - 1):
1398 Gates.CZ(
1399 wires=[
1400 (n_qubits - q - 2) % n_qubits,
1401 (n_qubits - q - 1) % n_qubits,
1402 ],
1403 noise_params=noise_params,
1404 )
1405 if n_qubits > 2:
1406 Gates.CZ(wires=[n_qubits - 1, 0], noise_params=noise_params)
1408 for q in range(n_qubits):
1409 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1410 w_idx += 1
1412 class Circuit_16(Circuit):
1413 @staticmethod
1414 def n_params_per_layer(n_qubits: int) -> int:
1415 """
1416 Returns the number of parameters per layer for the Circuit_16 ansatz.
1418 The number of parameters is calculated as n_qubits*3-1.
1420 Parameters
1421 ----------
1422 n_qubits : int
1423 Number of qubits in the circuit
1425 Returns
1426 -------
1427 int
1428 Number of parameters per layer
1429 """
1431 return n_qubits * 3 - 1
1433 @staticmethod
1434 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1435 """
1436 No controlled rotation gates available. Always None.
1438 Parameters
1439 ----------
1440 n_qubits : int
1441 Number of qubits in the circuit
1443 Returns
1444 -------
1445 Optional[np.ndarray]
1446 List of all controlled indices, or None if the circuit does not
1447 contain controlled rotation gates.
1448 """
1449 if n_qubits > 1:
1450 return [-(n_qubits - 1), None, None]
1451 else:
1452 return None
1454 @staticmethod
1455 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1456 """
1457 Creates a Circuit16 ansatz.
1459 Length of flattened vector must be n_qubits*3-1
1461 Parameters
1462 ----------
1463 w : np.ndarray
1464 Weight vector of size n_qubits*3-1
1465 n_qubits : int
1466 Number of qubits
1467 noise_params : Optional[Dict[str, float]], optional
1468 Dictionary of noise parameters to apply to the gates
1469 """
1470 w_idx = 0
1471 for q in range(n_qubits):
1472 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1473 w_idx += 1
1474 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1475 w_idx += 1
1477 if n_qubits > 1:
1478 for q in range(n_qubits // 2):
1479 Gates.CRZ(
1480 w[w_idx],
1481 wires=[(2 * q + 1), (2 * q)],
1482 noise_params=noise_params,
1483 )
1484 w_idx += 1
1486 for q in range((n_qubits - 1) // 2):
1487 Gates.CRZ(
1488 w[w_idx],
1489 wires=[(2 * q + 2), (2 * q + 1)],
1490 noise_params=noise_params,
1491 )
1492 w_idx += 1
1494 class Circuit_17(Circuit):
1495 @staticmethod
1496 def n_params_per_layer(n_qubits: int) -> int:
1497 """
1498 Returns the number of parameters per layer for the Circuit_17 ansatz.
1500 The number of parameters is calculated as n_qubits*3-1.
1502 Parameters
1503 ----------
1504 n_qubits : int
1505 Number of qubits in the circuit
1507 Returns
1508 -------
1509 int
1510 Number of parameters per layer
1511 """
1513 return n_qubits * 3 - 1
1515 @staticmethod
1516 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1517 """
1518 No controlled rotation gates available. Always None.
1520 Parameters
1521 ----------
1522 n_qubits : int
1523 Number of qubits in the circuit
1525 Returns
1526 -------
1527 Optional[np.ndarray]
1528 List of all controlled indices, or None if the circuit does not
1529 contain controlled rotation gates.
1530 """
1531 if n_qubits > 1:
1532 return [-(n_qubits - 1), None, None]
1533 else:
1534 return None
1536 @staticmethod
1537 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1538 """
1539 Creates a Circuit17 ansatz.
1541 Length of flattened vector must be n_qubits*3-1
1543 Parameters
1544 ----------
1545 w : np.ndarray
1546 Weight vector of size n_qubits*3-1
1547 n_qubits : int
1548 Number of qubits
1549 noise_params : Optional[Dict[str, float]], optional
1550 Dictionary of noise parameters to apply to the gates
1551 """
1552 w_idx = 0
1553 for q in range(n_qubits):
1554 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1555 w_idx += 1
1556 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1557 w_idx += 1
1559 if n_qubits > 1:
1560 for q in range(n_qubits // 2):
1561 Gates.CRX(
1562 w[w_idx],
1563 wires=[(2 * q + 1), (2 * q)],
1564 noise_params=noise_params,
1565 )
1566 w_idx += 1
1568 for q in range((n_qubits - 1) // 2):
1569 Gates.CRX(
1570 w[w_idx],
1571 wires=[(2 * q + 2), (2 * q + 1)],
1572 noise_params=noise_params,
1573 )
1574 w_idx += 1
1576 class Strongly_Entangling(Circuit):
1577 @staticmethod
1578 def n_params_per_layer(n_qubits: int) -> int:
1579 """
1580 Returns the number of parameters per layer for the
1581 Strongly Entangling ansatz.
1583 The number of parameters is calculated as n_qubits*6.
1585 Parameters
1586 ----------
1587 n_qubits : int
1588 Number of qubits in the circuit
1590 Returns
1591 -------
1592 int
1593 Number of parameters per layer
1594 """
1595 if n_qubits < 2:
1596 log.warning("Number of Qubits < 2, no entanglement available")
1597 return n_qubits * 6
1599 @staticmethod
1600 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1601 """
1602 No controlled rotation gates available. Always None.
1604 Parameters
1605 ----------
1606 n_qubits : int
1607 Number of qubits in the circuit
1609 Returns
1610 -------
1611 Optional[np.ndarray]
1612 List of all controlled indices, or None if the circuit does not
1613 contain controlled rotation gates.
1614 """
1615 return None
1617 @staticmethod
1618 def build(w: np.ndarray, n_qubits: int, noise_params=None) -> None:
1619 """
1620 Creates a Strongly Entangling ansatz.
1622 Length of flattened vector must be n_qubits*6
1624 Parameters
1625 ----------
1626 w : np.ndarray
1627 Weight vector of size n_qubits*6
1628 n_qubits : int
1629 Number of qubits
1630 noise_params : Optional[Dict[str, float]], optional
1631 Dictionary of noise parameters to apply to the gates
1632 """
1633 w_idx = 0
1634 for q in range(n_qubits):
1635 Gates.Rot(
1636 w[w_idx],
1637 w[w_idx + 1],
1638 w[w_idx + 2],
1639 wires=q,
1640 noise_params=noise_params,
1641 )
1642 w_idx += 3
1644 if n_qubits > 1:
1645 for q in range(n_qubits):
1646 Gates.CX(wires=[q, (q + 1) % n_qubits], noise_params=noise_params)
1648 for q in range(n_qubits):
1649 Gates.Rot(
1650 w[w_idx],
1651 w[w_idx + 1],
1652 w[w_idx + 2],
1653 wires=q,
1654 noise_params=noise_params,
1655 )
1656 w_idx += 3
1658 if n_qubits > 1:
1659 for q in range(n_qubits):
1660 Gates.CX(
1661 wires=[q, (q + n_qubits // 2) % n_qubits],
1662 noise_params=noise_params,
1663 )
1665 class No_Entangling(Circuit):
1666 @staticmethod
1667 def n_params_per_layer(n_qubits: int) -> int:
1668 """
1669 Returns the number of parameters per layer for the NoEntangling ansatz.
1671 The number of parameters is calculated as n_qubits*3.
1673 Parameters
1674 ----------
1675 n_qubits : int
1676 Number of qubits in the circuit
1678 Returns
1679 -------
1680 int
1681 Number of parameters per layer
1682 """
1683 return n_qubits * 3
1685 @staticmethod
1686 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1687 """
1688 No controlled rotation gates available. Always None.
1690 Parameters
1691 ----------
1692 n_qubits : int
1693 Number of qubits in the circuit
1695 Returns
1696 -------
1697 Optional[np.ndarray]
1698 List of all controlled indices, or None if the circuit does not
1699 contain controlled rotation gates.
1700 """
1701 return None
1703 @staticmethod
1704 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1705 """
1706 Creates a circuit without entangling, but with U3 gates on all qubits
1708 Length of flattened vector must be n_qubits*3
1710 Parameters
1711 ----------
1712 w : np.ndarray
1713 Weight vector of size n_qubits*3
1714 n_qubits : int
1715 Number of qubits
1716 noise_params : Optional[Dict[str, float]], optional
1717 Dictionary of noise parameters to apply to the gates
1718 """
1719 w_idx = 0
1720 for q in range(n_qubits):
1721 Gates.Rot(
1722 w[w_idx],
1723 w[w_idx + 1],
1724 w[w_idx + 2],
1725 wires=q,
1726 noise_params=noise_params,
1727 )
1728 w_idx += 3