Coverage for qml_essentials/ansaetze.py: 96%
491 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-09-08 14:29 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-09-08 14:29 +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()
76 batch_gate_error = True
78 @staticmethod
79 def init_rng(seed: int):
80 """
81 Initializes the random number generator with the given seed.
83 Parameters
84 ----------
85 seed : int
86 The seed for the random number generator.
87 """
88 Gates.rng = np.random.default_rng(seed)
90 @staticmethod
91 def NQubitDepolarizingChannel(p, wires):
92 """
93 Generates the Kraus operators for an n-qubit depolarizing channel.
95 The n-qubit depolarizing channel is defined as:
96 E(rho) = sqrt(1 - p * (4^n - 1) / 4^n) * rho
97 + sqrt(p / 4^n) * ∑_{P ≠ I^{⊗n}} P rho P†
98 where the sum is over all non-identity n-qubit Pauli operators
99 (i.e., tensor products of {I, X, Y, Z} excluding the identity operator I^{⊗n}).
100 Each Pauli error operator is weighted equally by p / 4^n.
102 This operator-sum (Kraus) representation models uniform depolarizing noise
103 acting on n qubits simultaneously. It is useful for simulating realistic
104 multi-qubit noise affecting entangling gates in noisy quantum circuits.
106 Parameters
107 ----------
108 p : float
109 The total probability of an n-qubit depolarizing error occurring.
110 Must satisfy 0 ≤ p ≤ 1.
112 wires : Sequence[int]
113 The list of qubit indices (wires) on which the channel acts.
114 Must contain at least 2 qubits.
116 Returns
117 -------
118 qml.QubitChannel
119 A PennyLane QubitChannel constructed from the Kraus operators representing
120 the n-qubit depolarizing noise channel acting on the specified wires.
121 """
123 def n_qubit_depolarizing_kraus(p: float, n: int) -> List[np.ndarray]:
124 if not (0.0 <= p <= 1.0):
125 raise ValueError(f"Probability p must be between 0 and 1, got {p}")
126 if n < 2:
127 raise ValueError(f"Number of qubits must be >= 2, got {n}")
129 Id = np.eye(2)
130 X = qml.matrix(qml.PauliX(0))
131 Y = qml.matrix(qml.PauliY(0))
132 Z = qml.matrix(qml.PauliZ(0))
133 paulis = [Id, X, Y, Z]
135 dim = 2**n
136 all_ops = []
138 # Generate all n-qubit Pauli tensor products:
139 for indices in itertools.product(range(4), repeat=n):
140 P = np.eye(1)
141 for idx in indices:
142 P = np.kron(P, paulis[idx])
143 all_ops.append(P)
145 # Identity operator corresponds to all zeros indices (Id^n)
146 K0 = np.sqrt(1 - p * (4**n - 1) / (4**n)) * np.eye(dim)
148 kraus_ops = []
149 for i, P in enumerate(all_ops):
150 if i == 0:
151 # Skip the identity, already handled as K0
152 continue
153 kraus_ops.append(np.sqrt(p / (4**n)) * P)
155 return [K0] + kraus_ops
157 return qml.QubitChannel(n_qubit_depolarizing_kraus(p, len(wires)), wires=wires)
159 @staticmethod
160 def Noise(
161 wires: Union[int, List[int]], noise_params: Optional[Dict[str, float]] = None
162 ) -> None:
163 """
164 Applies noise to the given wires.
166 Parameters
167 ----------
168 wires : Union[int, List[int]]
169 The wire(s) to apply the noise to.
170 noise_params : Optional[Dict[str, float]]
171 A dictionary of noise parameters. The following noise gates are
172 supported:
173 -BitFlip: Applies a bit flip error to the given wires.
174 -PhaseFlip: Applies a phase flip error to the given wires.
175 -Depolarizing: Applies a depolarizing channel error to the
176 given wires.
177 -MultiQubitDepolarizing: Applies a two-qubit depolarizing channel
178 error to the given wires.
180 All parameters are optional and default to 0.0 if not provided.
181 """
182 if noise_params is not None:
183 if isinstance(wires, int):
184 wires = [wires] # single qubit gate
186 # noise on single qubits
187 for wire in wires:
188 bf = noise_params.get("BitFlip", 0.0)
189 if bf > 0:
190 qml.BitFlip(bf, wires=wire)
192 pf = noise_params.get("PhaseFlip", 0.0)
193 if pf > 0:
194 qml.PhaseFlip(pf, wires=wire)
196 dp = noise_params.get("Depolarizing", 0.0)
197 if dp > 0:
198 qml.DepolarizingChannel(dp, wires=wire)
200 # noise on two-qubits
201 if len(wires) > 1:
202 p = noise_params.get("MultiQubitDepolarizing", 0.0)
203 if p > 0:
204 Gates.NQubitDepolarizingChannel(p, wires)
206 @staticmethod
207 def GateError(
208 w: float, noise_params: Optional[Dict[str, float]] = None
209 ) -> np.ndarray:
210 """
211 Applies a gate error to the given rotation angle(s).
213 Parameters
214 ----------
215 w : Union[float, np.ndarray, List[float]]
216 The rotation angle in radians.
217 noise_params : Optional[Dict[str, float]]
218 A dictionary of noise parameters. The following noise gates are
219 supported:
220 -GateError: Applies a normal distribution error to the rotation
221 angle. The standard deviation of the noise is specified by
222 the "GateError" key in the dictionary.
224 All parameters are optional and default to 0.0 if not provided.
226 Returns
227 -------
228 float
229 The modified rotation angle after applying the gate error.
230 """
231 if noise_params is not None and noise_params.get("GateError", None) is not None:
232 w += Gates.rng.normal(
233 0,
234 noise_params["GateError"],
235 (
236 w.shape
237 if isinstance(w, np.ndarray) and Gates.batch_gate_error
238 else None
239 ),
240 )
241 return w
243 @staticmethod
244 def Rot(phi, theta, omega, wires, noise_params=None):
245 """
246 Applies a rotation gate to the given wires and adds `Noise`
248 Parameters
249 ----------
250 phi : Union[float, np.ndarray, List[float]]
251 The first rotation angle in radians.
252 theta : Union[float, np.ndarray, List[float]]
253 The second rotation angle in radians.
254 omega : Union[float, np.ndarray, List[float]]
255 The third rotation angle in radians.
256 wires : Union[int, List[int]]
257 The wire(s) to apply the rotation gate to.
258 noise_params : Optional[Dict[str, float]]
259 A dictionary of noise parameters. The following noise gates are
260 supported:
261 -BitFlip: Applies a bit flip error to the given wires.
262 -PhaseFlip: Applies a phase flip error to the given wires.
263 -Depolarizing: Applies a depolarizing channel error to the
264 given wires.
266 All parameters are optional and default to 0.0 if not provided.
267 """
268 if noise_params is not None and "GateError" in noise_params:
269 phi = Gates.GateError(phi, noise_params)
270 theta = Gates.GateError(theta, noise_params)
271 omega = Gates.GateError(omega, noise_params)
272 # phi += Gates.rng.normal(0, noise_params["GateError"])
273 # theta += Gates.rng.normal(0, noise_params["GateError"])
274 # omega += Gates.rng.normal(0, noise_params["GateError"])
275 qml.Rot(phi, theta, omega, wires=wires)
276 Gates.Noise(wires, noise_params)
278 @staticmethod
279 def RX(w, wires, noise_params=None):
280 """
281 Applies a rotation around the X axis to the given wires and adds `Noise`
283 Parameters
284 ----------
285 w : Union[float, np.ndarray, List[float]]
286 The rotation angle in radians.
287 wires : Union[int, List[int]]
288 The wire(s) to apply the rotation gate to.
289 noise_params : Optional[Dict[str, float]]
290 A dictionary of noise parameters. The following noise gates are
291 supported:
292 -BitFlip: Applies a bit flip error to the given wires.
293 -PhaseFlip: Applies a phase flip error to the given wires.
294 -Depolarizing: Applies a depolarizing channel error to the
295 given wires.
297 All parameters are optional and default to 0.0 if not provided.
298 """
299 w = Gates.GateError(w, noise_params)
300 qml.RX(w, wires=wires)
301 Gates.Noise(wires, noise_params)
303 @staticmethod
304 def RY(w, wires, noise_params=None):
305 """
306 Applies a rotation around the Y axis to the given wires and adds `Noise`
308 Parameters
309 ----------
310 w : Union[float, np.ndarray, List[float]]
311 The rotation angle in radians.
312 wires : Union[int, List[int]]
313 The wire(s) to apply the rotation gate to.
314 noise_params : Optional[Dict[str, float]]
315 A dictionary of noise parameters. The following noise gates are
316 supported:
317 -BitFlip: Applies a bit flip error to the given wires.
318 -PhaseFlip: Applies a phase flip error to the given wires.
319 -Depolarizing: Applies a depolarizing channel error to the
320 given wires.
322 All parameters are optional and default to 0.0 if not provided.
323 """
324 w = Gates.GateError(w, noise_params)
325 qml.RY(w, wires=wires)
326 Gates.Noise(wires, noise_params)
328 @staticmethod
329 def RZ(w, wires, noise_params=None):
330 """
331 Applies a rotation around the Z axis to the given wires and adds `Noise`
333 Parameters
334 ----------
335 w : Union[float, np.ndarray, List[float]]
336 The rotation angle in radians.
337 wires : Union[int, List[int]]
338 The wire(s) to apply the rotation gate to.
339 noise_params : Optional[Dict[str, float]]
340 A dictionary of noise parameters. The following noise gates are
341 supported:
342 -BitFlip: Applies a bit flip error to the given wires.
343 -PhaseFlip: Applies a phase flip error to the given wires.
344 -Depolarizing: Applies a depolarizing channel error to the
345 given wires.
347 All parameters are optional and default to 0.0 if not provided.
348 """
349 w = Gates.GateError(w, noise_params)
350 qml.RZ(w, wires=wires)
351 Gates.Noise(wires, noise_params)
353 @staticmethod
354 def CRX(w, wires, noise_params=None):
355 """
356 Applies a controlled rotation around the X axis to the given wires
357 and adds `Noise`
359 Parameters
360 ----------
361 w : Union[float, np.ndarray, List[float]]
362 The rotation angle in radians.
363 wires : Union[int, List[int]]
364 The wire(s) to apply the controlled rotation gate to.
365 noise_params : Optional[Dict[str, float]]
366 A dictionary of noise parameters. The following noise gates are
367 supported:
368 -BitFlip: Applies a bit flip error to the given wires.
369 -PhaseFlip: Applies a phase flip error to the given wires.
370 -Depolarizing: Applies a depolarizing channel error to the
371 given wires.
373 All parameters are optional and default to 0.0 if not provided.
374 """
375 w = Gates.GateError(w, noise_params)
376 qml.CRX(w, wires=wires)
377 Gates.Noise(wires, noise_params)
379 @staticmethod
380 def CRY(w, wires, noise_params=None):
381 """
382 Applies a controlled rotation around the Y axis to the given wires
383 and adds `Noise`
385 Parameters
386 ----------
387 w : Union[float, np.ndarray, List[float]]
388 The rotation angle in radians.
389 wires : Union[int, List[int]]
390 The wire(s) to apply the controlled rotation gate to.
391 noise_params : Optional[Dict[str, float]]
392 A dictionary of noise parameters. The following noise gates are
393 supported:
394 -BitFlip: Applies a bit flip error to the given wires.
395 -PhaseFlip: Applies a phase flip error to the given wires.
396 -Depolarizing: Applies a depolarizing channel error to the
397 given wires.
399 All parameters are optional and default to 0.0 if not provided.
400 """
401 w = Gates.GateError(w, noise_params)
402 qml.CRY(w, wires=wires)
403 Gates.Noise(wires, noise_params)
405 @staticmethod
406 def CRZ(w, wires, noise_params=None):
407 """
408 Applies a controlled rotation around the Z axis to the given wires
409 and adds `Noise`
411 Parameters
412 ----------
413 w : Union[float, np.ndarray, List[float]]
414 The rotation angle in radians.
415 wires : Union[int, List[int]]
416 The wire(s) to apply the controlled rotation gate to.
417 noise_params : Optional[Dict[str, float]]
418 A dictionary of noise parameters. The following noise gates are
419 supported:
420 -BitFlip: Applies a bit flip error to the given wires.
421 -PhaseFlip: Applies a phase flip error to the given wires.
422 -Depolarizing: Applies a depolarizing channel error to the
423 given wires.
425 All parameters are optional and default to 0.0 if not provided.
426 """
427 w = Gates.GateError(w, noise_params)
428 qml.CRZ(w, wires=wires)
429 Gates.Noise(wires, noise_params)
431 @staticmethod
432 def CX(wires, noise_params=None):
433 """
434 Applies a controlled NOT gate to the given wires and adds `Noise`
436 Parameters
437 ----------
438 wires : Union[int, List[int]]
439 The wire(s) to apply the controlled NOT gate to.
440 noise_params : Optional[Dict[str, float]]
441 A dictionary of noise parameters. The following noise gates are
442 supported:
443 -BitFlip: Applies a bit flip error to the given wires.
444 -PhaseFlip: Applies a phase flip error to the given wires.
445 -Depolarizing: Applies a depolarizing channel error to the
446 given wires.
448 All parameters are optional and default to 0.0 if not provided.
449 """
450 qml.CNOT(wires=wires)
451 Gates.Noise(wires, noise_params)
453 @staticmethod
454 def CY(wires, noise_params=None):
455 """
456 Applies a controlled Y gate to the given wires and adds `Noise`
458 Parameters
459 ----------
460 wires : Union[int, List[int]]
461 The wire(s) to apply the controlled Y gate to.
462 noise_params : Optional[Dict[str, float]]
463 A dictionary of noise parameters. The following noise gates are
464 supported:
465 -BitFlip: Applies a bit flip error to the given wires.
466 -PhaseFlip: Applies a phase flip error to the given wires.
467 -Depolarizing: Applies a depolarizing channel error to the
468 given wires.
470 All parameters are optional and default to 0.0 if not provided.
471 """
472 qml.CY(wires=wires)
473 Gates.Noise(wires, noise_params)
475 @staticmethod
476 def CZ(wires, noise_params=None):
477 """
478 Applies a controlled Z gate to the given wires and adds `Noise`
480 Parameters
481 ----------
482 wires : Union[int, List[int]]
483 The wire(s) to apply the controlled Z gate to.
484 noise_params : Optional[Dict[str, float]]
485 A dictionary of noise parameters. The following noise gates are
486 supported:
487 -BitFlip: Applies a bit flip error to the given wires.
488 -PhaseFlip: Applies a phase flip error to the given wires.
489 -Depolarizing: Applies a depolarizing channel error to the
490 given wires.
492 All parameters are optional and default to 0.0 if not provided.
493 """
494 qml.CZ(wires=wires)
495 Gates.Noise(wires, noise_params)
497 @staticmethod
498 def H(wires, noise_params=None):
499 """
500 Applies a Hadamard gate to the given wires and adds `Noise`
502 Parameters
503 ----------
504 wires : Union[int, List[int]]
505 The wire(s) to apply the Hadamard gate to.
506 noise_params : Optional[Dict[str, float]]
507 A dictionary of noise parameters. The following noise gates are
508 supported:
509 -BitFlip: Applies a bit flip error to the given wires.
510 -PhaseFlip: Applies a phase flip error to the given wires.
511 -Depolarizing: Applies a depolarizing channel error to the
512 given wires.
514 All parameters are optional and default to 0.0 if not provided.
515 """
516 qml.Hadamard(wires=wires)
517 Gates.Noise(wires, noise_params)
520class Ansaetze:
521 def get_available():
522 return [
523 Ansaetze.No_Ansatz,
524 Ansaetze.Circuit_1,
525 Ansaetze.Circuit_2,
526 Ansaetze.Circuit_3,
527 Ansaetze.Circuit_4,
528 Ansaetze.Circuit_6,
529 Ansaetze.Circuit_9,
530 Ansaetze.Circuit_10,
531 Ansaetze.Circuit_15,
532 Ansaetze.Circuit_16,
533 Ansaetze.Circuit_17,
534 Ansaetze.Circuit_18,
535 Ansaetze.Circuit_19,
536 Ansaetze.No_Entangling,
537 Ansaetze.Strongly_Entangling,
538 Ansaetze.Hardware_Efficient,
539 Ansaetze.GHZ,
540 ]
542 class No_Ansatz(Circuit):
543 @staticmethod
544 def n_params_per_layer(n_qubits: int) -> int:
545 return 0
547 @staticmethod
548 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
549 return None
551 @staticmethod
552 def build(w: np.ndarray, n_qubits: int, noise_params=None):
553 pass
555 class GHZ(Circuit):
556 @staticmethod
557 def n_params_per_layer(n_qubits: int) -> int:
558 return 0
560 @staticmethod
561 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
562 return None
564 @staticmethod
565 def build(w: np.ndarray, n_qubits: int, noise_params=None):
566 Gates.H(0, noise_params=noise_params)
568 for q in range(n_qubits - 1):
569 Gates.CX([q, q + 1], noise_params=noise_params)
571 class Hardware_Efficient(Circuit):
572 @staticmethod
573 def n_params_per_layer(n_qubits: int) -> int:
574 """
575 Returns the number of parameters per layer for the
576 Hardware Efficient Ansatz.
578 The number of parameters is 3 times the number of qubits when there
579 is more than one qubit, as each qubit contributes 3 parameters.
580 If the number of qubits is less than 2, a warning is logged since
581 no entanglement is possible, and a fixed number of 2 parameters is used.
583 Parameters
584 ----------
585 n_qubits : int
586 Number of qubits in the circuit
588 Returns
589 -------
590 int
591 Number of parameters required for one layer of the circuit
592 """
593 if n_qubits < 2:
594 log.warning("Number of Qubits < 2, no entanglement available")
595 return n_qubits * 3
597 @staticmethod
598 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
599 """
600 No controlled rotation gates available. Always None.
602 Parameters
603 ----------
604 n_qubits : int
605 Number of qubits in the circuit
607 Returns
608 -------
609 Optional[np.ndarray]
610 List of all controlled indices, or None if the circuit does not
611 contain controlled rotation gates.
612 """
613 return None
615 @staticmethod
616 def build(w: np.ndarray, n_qubits: int, noise_params=None):
617 """
618 Creates a Hardware-Efficient ansatz, as proposed in
619 https://arxiv.org/pdf/2309.03279
621 Parameters
622 ----------
623 w : np.ndarray
624 Weight vector of size n_qubits*3
625 n_qubits : int
626 Number of qubits
627 noise_params : Optional[Dict[str, float]], optional
628 Dictionary of noise parameters to apply to the gates
629 """
630 w_idx = 0
631 for q in range(n_qubits):
632 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
633 w_idx += 1
634 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
635 w_idx += 1
636 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
637 w_idx += 1
639 if n_qubits > 1:
640 for q in range(n_qubits // 2):
641 Gates.CX(wires=[(2 * q), (2 * q + 1)], noise_params=noise_params)
642 for q in range((n_qubits - 1) // 2):
643 Gates.CX(
644 wires=[(2 * q + 1), (2 * q + 2)], noise_params=noise_params
645 )
646 if n_qubits > 2:
647 Gates.CX(wires=[(n_qubits - 1), 0], noise_params=noise_params)
649 class Circuit_19(Circuit):
650 @staticmethod
651 def n_params_per_layer(n_qubits: int) -> int:
652 """
653 Returns the number of parameters per layer for Circuit_19.
655 The number of parameters is 3 times the number of qubits when there
656 is more than one qubit, as each qubit contributes 3 parameters.
657 If the number of qubits is less than 2, a warning is logged since
658 no entanglement is possible, and a fixed number of 2 parameters is used.
660 Parameters
661 ----------
662 n_qubits : int
663 Number of qubits in the circuit
665 Returns
666 -------
667 int
668 Number of parameters required for one layer of the circuit
669 """
671 if n_qubits > 1:
672 return n_qubits * 3
673 else:
674 log.warning("Number of Qubits < 2, no entanglement available")
675 return 2
677 @staticmethod
678 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
679 """
680 Returns the indices for the controlled rotation gates for one layer.
681 Indices should slice the list of all parameters for one layer as follows:
682 [indices[0]:indices[1]:indices[2]]
684 Parameters
685 ----------
686 n_qubits : int
687 Number of qubits in the circuit
689 Returns
690 -------
691 Optional[np.ndarray]
692 List of all controlled indices, or None if the circuit does not
693 contain controlled rotation gates.
694 """
695 if n_qubits > 1:
696 return [-n_qubits, None, None]
697 else:
698 return None
700 @staticmethod
701 def build(w: np.ndarray, n_qubits: int, noise_params=None):
702 """
703 Creates a Circuit19 ansatz.
705 Length of flattened vector must be n_qubits*3
706 because for >1 qubits there are three gates
708 Parameters
709 ----------
710 w : np.ndarray
711 Weight vector of size n_qubits*3
712 n_qubits : int
713 Number of qubits
714 noise_params : Optional[Dict[str, float]], optional
715 Dictionary of noise parameters to apply to the gates
716 """
717 w_idx = 0
718 for q in range(n_qubits):
719 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
720 w_idx += 1
721 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
722 w_idx += 1
724 if n_qubits > 1:
725 for q in range(n_qubits):
726 Gates.CRX(
727 w[w_idx],
728 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
729 noise_params=noise_params,
730 )
731 w_idx += 1
733 class Circuit_18(Circuit):
734 @staticmethod
735 def n_params_per_layer(n_qubits: int) -> int:
736 """
737 Returns the number of parameters per layer for Circuit_18.
739 The number of parameters is 3 times the number of qubits when there
740 is more than one qubit, as each qubit contributes 3 parameters.
741 If the number of qubits is less than 2, a warning is logged since
742 no entanglement is possible, and a fixed number of 2 parameters is used.
744 Parameters
745 ----------
746 n_qubits : int
747 Number of qubits in the circuit
749 Returns
750 -------
751 int
752 Number of parameters required for one layer of the circuit
753 """
754 if n_qubits > 1:
755 return n_qubits * 3
756 else:
757 log.warning("Number of Qubits < 2, no entanglement available")
758 return 2
760 @staticmethod
761 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
762 """
763 Returns the indices for the controlled rotation gates for one layer.
764 Indices should slice the list of all parameters for one layer as follows:
765 [indices[0]:indices[1]:indices[2]]
767 Parameters
768 ----------
769 n_qubits : int
770 Number of qubits in the circuit
772 Returns
773 -------
774 Optional[np.ndarray]
775 List of all controlled indices, or None if the circuit does not
776 contain controlled rotation gates.
777 """
778 if n_qubits > 1:
779 return [-n_qubits, None, None]
780 else:
781 return None
783 @staticmethod
784 def build(w: np.ndarray, n_qubits: int, noise_params=None):
785 """
786 Creates a Circuit18 ansatz.
788 Length of flattened vector must be n_qubits*3
790 Parameters
791 ----------
792 w : np.ndarray
793 Weight vector of size n_qubits*3
794 n_qubits : int
795 Number of qubits
796 noise_params : Optional[Dict[str, float]], optional
797 Dictionary of noise parameters to apply to the gates
798 """
799 w_idx = 0
800 for q in range(n_qubits):
801 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
802 w_idx += 1
803 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
804 w_idx += 1
806 if n_qubits > 1:
807 for q in range(n_qubits):
808 Gates.CRZ(
809 w[w_idx],
810 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
811 noise_params=noise_params,
812 )
813 w_idx += 1
815 class Circuit_15(Circuit):
816 @staticmethod
817 def n_params_per_layer(n_qubits: int) -> int:
818 """
819 Returns the number of parameters per layer for Circuit_15.
821 The number of parameters is 2 times the number of qubits.
822 A warning is logged if the number of qubits is less than 2.
824 Parameters
825 ----------
826 n_qubits : int
827 Number of qubits in the circuit
829 Returns
830 -------
831 int
832 Number of parameters required for one layer of the circuit
833 """
834 if n_qubits > 1:
835 return n_qubits * 2
836 else:
837 log.warning("Number of Qubits < 2, no entanglement available")
838 return 2
840 @staticmethod
841 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
842 """
843 No controlled rotation gates available. Always None.
845 Parameters
846 ----------
847 n_qubits : int
848 Number of qubits in the circuit
850 Returns
851 -------
852 Optional[np.ndarray]
853 List of all controlled indices, or None if the circuit does not
854 contain controlled rotation gates.
855 """
856 return None
858 @staticmethod
859 def build(w: np.ndarray, n_qubits: int, noise_params=None):
860 """
861 Creates a Circuit15 ansatz.
863 Length of flattened vector must be n_qubits*2
864 because for >1 qubits there are three gates
866 Parameters
867 ----------
868 w : np.ndarray
869 Weight vector of size n_qubits*2
870 n_qubits : int
871 Number of qubits
872 noise_params : Optional[Dict[str, float]], optional
873 Dictionary of noise parameters to apply to the gates
874 """
875 w_idx = 0
876 for q in range(n_qubits):
877 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
878 w_idx += 1
880 if n_qubits > 1:
881 for q in range(n_qubits):
882 Gates.CX(
883 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
884 noise_params=noise_params,
885 )
887 for q in range(n_qubits):
888 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
889 w_idx += 1
891 if n_qubits > 1:
892 for q in range(n_qubits):
893 Gates.CX(
894 wires=[(q - 1) % n_qubits, (q - 2) % n_qubits],
895 noise_params=noise_params,
896 )
898 class Circuit_9(Circuit):
899 @staticmethod
900 def n_params_per_layer(n_qubits: int) -> int:
901 """
902 Returns the number of parameters per layer for Circuit_9.
904 The number of parameters is equal to the number of qubits.
906 Parameters
907 ----------
908 n_qubits : int
909 Number of qubits in the circuit
911 Returns
912 -------
913 int
914 Number of parameters required for one layer of the circuit
915 """
916 return n_qubits
918 @staticmethod
919 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
920 """
921 No controlled rotation gates available. Always None.
923 Parameters
924 ----------
925 n_qubits : int
926 Number of qubits in the circuit
928 Returns
929 -------
930 Optional[np.ndarray]
931 List of all controlled indices, or None if the circuit does not
932 contain controlled rotation gates.
933 """
934 return None
936 @staticmethod
937 def build(w: np.ndarray, n_qubits: int, noise_params=None):
938 """
939 Creates a Circuit9 ansatz.
941 Length of flattened vector must be n_qubits
943 Parameters
944 ----------
945 w : np.ndarray
946 Weight vector of size n_qubits
947 n_qubits : int
948 Number of qubits
949 noise_params : Optional[Dict[str, float]], optional
950 Dictionary of noise parameters to apply to the gates
951 """
952 w_idx = 0
953 for q in range(n_qubits):
954 Gates.H(wires=q, noise_params=noise_params)
956 if n_qubits > 1:
957 for q in range(n_qubits - 1):
958 Gates.CZ(
959 wires=[n_qubits - q - 2, n_qubits - q - 1],
960 noise_params=noise_params,
961 )
963 for q in range(n_qubits):
964 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
965 w_idx += 1
967 class Circuit_6(Circuit):
968 @staticmethod
969 def n_params_per_layer(n_qubits: int) -> int:
970 """
971 Returns the number of parameters per layer for Circuit_6.
973 The total number of parameters is n_qubits*3+n_qubits**2, which is
974 the number of rotations n_qubits*3 plus the number of entangling gates
975 n_qubits**2.
977 If n_qubits is 1, the number of parameters is 4, and a warning is logged
978 since no entanglement is possible.
980 Parameters
981 ----------
982 n_qubits : int
983 Number of qubits
985 Returns
986 -------
987 int
988 Number of parameters per layer
989 """
990 if n_qubits > 1:
991 return n_qubits * 3 + n_qubits**2
992 else:
993 log.warning("Number of Qubits < 2, no entanglement available")
994 return 4
996 @staticmethod
997 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
998 """
999 Returns the indices for the controlled rotation gates for one layer.
1000 Indices should slice the list of all parameters for one layer as follows:
1001 [indices[0]:indices[1]:indices[2]]
1003 Parameters
1004 ----------
1005 n_qubits : int
1006 Number of qubits in the circuit
1008 Returns
1009 -------
1010 Optional[np.ndarray]
1011 List of all controlled indices, or None if the circuit does not
1012 contain controlled rotation gates.
1013 """
1014 # TODO: implement
1015 return None
1017 @staticmethod
1018 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1019 """
1020 Creates a Circuit6 ansatz.
1022 Length of flattened vector must be
1023 n_qubits*4+n_qubits*(n_qubits-1) =
1024 n_qubits*3+n_qubits**2
1026 Parameters
1027 ----------
1028 w : np.ndarray
1029 Weight vector of size
1030 n_layers*(n_qubits*3+n_qubits**2)
1031 n_qubits : int
1032 Number of qubits
1033 noise_params : Optional[Dict[str, float]], optional
1034 Dictionary of noise parameters to apply to the gates
1035 """
1036 w_idx = 0
1037 for q in range(n_qubits):
1038 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1039 w_idx += 1
1040 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1041 w_idx += 1
1043 if n_qubits > 1:
1044 for ql in range(n_qubits):
1045 for q in range(n_qubits):
1046 if q == ql:
1047 continue
1048 Gates.CRX(
1049 w[w_idx],
1050 wires=[n_qubits - ql - 1, (n_qubits - q - 1) % n_qubits],
1051 noise_params=noise_params,
1052 )
1053 w_idx += 1
1055 for q in range(n_qubits):
1056 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1057 w_idx += 1
1058 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1059 w_idx += 1
1061 class Circuit_1(Circuit):
1062 @staticmethod
1063 def n_params_per_layer(n_qubits: int) -> int:
1064 """
1065 Returns the number of parameters per layer for Circuit_1.
1067 The total number of parameters is determined by the number of qubits, with
1068 each qubit contributing 2 parameters.
1070 Parameters
1071 ----------
1072 n_qubits : int
1073 Number of qubits in the circuit
1075 Returns
1076 -------
1077 int
1078 Number of parameters per layer
1079 """
1080 return n_qubits * 2
1082 @staticmethod
1083 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1084 """
1085 No controlled rotation gates available. Always None.
1087 Parameters
1088 ----------
1089 n_qubits : int
1090 Number of qubits in the circuit
1092 Returns
1093 -------
1094 Optional[np.ndarray]
1095 List of all controlled indices, or None if the circuit does not
1096 contain controlled rotation gates.
1097 """
1098 return None
1100 @staticmethod
1101 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1102 """
1103 Creates a Circuit1 ansatz.
1105 Length of flattened vector must be n_qubits*2
1107 Parameters
1108 ----------
1109 w : np.ndarray
1110 Weight vector of size n_qubits*2
1111 n_qubits : int
1112 Number of qubits
1113 noise_params : Optional[Dict[str, float]], optional
1114 Dictionary of noise parameters to apply to the gates
1115 """
1116 w_idx = 0
1117 for q in range(n_qubits):
1118 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1119 w_idx += 1
1120 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1121 w_idx += 1
1123 class Circuit_2(Circuit):
1124 @staticmethod
1125 def n_params_per_layer(n_qubits: int) -> int:
1126 """
1127 Returns the number of parameters per layer for Circuit_2.
1129 The total number of parameters is determined by the number of qubits, with
1130 each qubit contributing 2 parameters.
1132 Parameters
1133 ----------
1134 n_qubits : int
1135 Number of qubits in the circuit
1137 Returns
1138 -------
1139 int
1140 Number of parameters per layer
1141 """
1142 return n_qubits * 2
1144 @staticmethod
1145 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1146 """
1147 No controlled rotation gates available. Always None.
1149 Parameters
1150 ----------
1151 n_qubits : int
1152 Number of qubits in the circuit
1154 Returns
1155 -------
1156 Optional[np.ndarray]
1157 List of all controlled indices, or None if the circuit does not
1158 contain controlled rotation gates.
1159 """
1160 return None
1162 @staticmethod
1163 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1164 """
1165 Creates a Circuit2 ansatz.
1167 Length of flattened vector must be n_qubits*2
1169 Parameters
1170 ----------
1171 w : np.ndarray
1172 Weight vector of size n_qubits*2
1173 n_qubits : int
1174 Number of qubits
1175 noise_params : Optional[Dict[str, float]], optional
1176 Dictionary of noise parameters to apply to the gates
1177 """
1178 w_idx = 0
1179 for q in range(n_qubits):
1180 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1181 w_idx += 1
1182 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1183 w_idx += 1
1185 if n_qubits > 1:
1186 for q in range(n_qubits - 1):
1187 Gates.CX(
1188 wires=[n_qubits - q - 1, n_qubits - q - 2],
1189 noise_params=noise_params,
1190 )
1192 class Circuit_3(Circuit):
1193 @staticmethod
1194 def n_params_per_layer(n_qubits: int) -> int:
1195 """
1196 Calculates the number of parameters per layer for Circuit3.
1198 The number of parameters per layer is given by the number of qubits, with
1199 each qubit contributing 3 parameters. The last qubit only contributes 2
1200 parameters because it is the target qubit for the controlled gates.
1202 Parameters
1203 ----------
1204 n_qubits : int
1205 Number of qubits in the circuit
1207 Returns
1208 -------
1209 int
1210 Number of parameters per layer
1211 """
1212 return n_qubits * 3 - 1
1214 @staticmethod
1215 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1216 """
1217 No controlled rotation gates available. Always None.
1219 Parameters
1220 ----------
1221 n_qubits : int
1222 Number of qubits in the circuit
1224 Returns
1225 -------
1226 Optional[np.ndarray]
1227 List of all controlled indices, or None if the circuit does not
1228 contain controlled rotation gates.
1229 """
1230 if n_qubits > 1:
1231 return [-(n_qubits - 1), None, None]
1232 else:
1233 return None
1235 @staticmethod
1236 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1237 """
1238 Creates a Circuit3 ansatz.
1240 Length of flattened vector must be n_qubits*3-1
1242 Parameters
1243 ----------
1244 w : np.ndarray
1245 Weight vector of size n_qubits*3-1
1246 n_qubits : int
1247 Number of qubits
1248 noise_params : Optional[Dict[str, float]], optional
1249 Dictionary of noise parameters to apply to the gates
1250 """
1251 w_idx = 0
1252 for q in range(n_qubits):
1253 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1254 w_idx += 1
1255 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1256 w_idx += 1
1258 if n_qubits > 1:
1259 for q in range(n_qubits - 1):
1260 Gates.CRZ(
1261 w[w_idx],
1262 wires=[n_qubits - q - 1, n_qubits - q - 2],
1263 noise_params=noise_params,
1264 )
1265 w_idx += 1
1267 class Circuit_4(Circuit):
1268 @staticmethod
1269 def n_params_per_layer(n_qubits: int) -> int:
1270 """
1271 Returns the number of parameters per layer for the Circuit_4 ansatz.
1273 The number of parameters is calculated as n_qubits*3-1.
1275 Parameters
1276 ----------
1277 n_qubits : int
1278 Number of qubits in the circuit
1280 Returns
1281 -------
1282 int
1283 Number of parameters per layer
1284 """
1285 return n_qubits * 3 - 1
1287 @staticmethod
1288 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1289 """
1290 No controlled rotation gates available. Always None.
1292 Parameters
1293 ----------
1294 n_qubits : int
1295 Number of qubits in the circuit
1297 Returns
1298 -------
1299 Optional[np.ndarray]
1300 List of all controlled indices, or None if the circuit does not
1301 contain controlled rotation gates.
1302 """
1303 if n_qubits > 1:
1304 return [-(n_qubits - 1), None, None]
1305 else:
1306 return None
1308 @staticmethod
1309 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1310 """
1311 Creates a Circuit4 ansatz.
1313 Length of flattened vector must be n_qubits*3-1
1315 Parameters
1316 ----------
1317 w : np.ndarray
1318 Weight vector of size n_qubits*3-1
1319 n_qubits : int
1320 Number of qubits
1321 noise_params : Optional[Dict[str, float]], optional
1322 Dictionary of noise parameters to apply to the gates
1323 """
1324 w_idx = 0
1325 for q in range(n_qubits):
1326 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1327 w_idx += 1
1328 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1329 w_idx += 1
1331 if n_qubits > 1:
1332 for q in range(n_qubits - 1):
1333 Gates.CRX(
1334 w[w_idx],
1335 wires=[n_qubits - q - 1, n_qubits - q - 2],
1336 noise_params=noise_params,
1337 )
1338 w_idx += 1
1340 class Circuit_10(Circuit):
1341 @staticmethod
1342 def n_params_per_layer(n_qubits: int) -> int:
1343 """
1344 Returns the number of parameters per layer for the Circuit_10 ansatz.
1346 The number of parameters is calculated as n_qubits*2.
1348 Parameters
1349 ----------
1350 n_qubits : int
1351 Number of qubits in the circuit
1353 Returns
1354 -------
1355 int
1356 Number of parameters per layer
1357 """
1358 return n_qubits * 2 # constant gates not considered yet. has to be fixed
1360 @staticmethod
1361 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1362 """
1363 No controlled rotation gates available. Always None.
1365 Parameters
1366 ----------
1367 n_qubits : int
1368 Number of qubits in the circuit
1370 Returns
1371 -------
1372 Optional[np.ndarray]
1373 List of all controlled indices, or None if the circuit does not
1374 contain controlled rotation gates.
1375 """
1376 return None
1378 @staticmethod
1379 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1380 """
1381 Creates a Circuit10 ansatz.
1383 Length of flattened vector must be n_qubits*2
1385 Parameters
1386 ----------
1387 w : np.ndarray
1388 Weight vector of size n_qubits*2
1389 n_qubits : int
1390 Number of qubits
1391 noise_params : Optional[Dict[str, float]], optional
1392 Dictionary of noise parameters to apply to the gates
1393 """
1394 w_idx = 0
1395 # constant gates, independent of layers. has to be fixed
1396 for q in range(n_qubits):
1397 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1398 w_idx += 1
1400 if n_qubits > 1:
1401 for q in range(n_qubits - 1):
1402 Gates.CZ(
1403 wires=[
1404 (n_qubits - q - 2) % n_qubits,
1405 (n_qubits - q - 1) % n_qubits,
1406 ],
1407 noise_params=noise_params,
1408 )
1409 if n_qubits > 2:
1410 Gates.CZ(wires=[n_qubits - 1, 0], noise_params=noise_params)
1412 for q in range(n_qubits):
1413 Gates.RY(w[w_idx], wires=q, noise_params=noise_params)
1414 w_idx += 1
1416 class Circuit_16(Circuit):
1417 @staticmethod
1418 def n_params_per_layer(n_qubits: int) -> int:
1419 """
1420 Returns the number of parameters per layer for the Circuit_16 ansatz.
1422 The number of parameters is calculated as n_qubits*3-1.
1424 Parameters
1425 ----------
1426 n_qubits : int
1427 Number of qubits in the circuit
1429 Returns
1430 -------
1431 int
1432 Number of parameters per layer
1433 """
1435 return n_qubits * 3 - 1
1437 @staticmethod
1438 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1439 """
1440 No controlled rotation gates available. Always None.
1442 Parameters
1443 ----------
1444 n_qubits : int
1445 Number of qubits in the circuit
1447 Returns
1448 -------
1449 Optional[np.ndarray]
1450 List of all controlled indices, or None if the circuit does not
1451 contain controlled rotation gates.
1452 """
1453 if n_qubits > 1:
1454 return [-(n_qubits - 1), None, None]
1455 else:
1456 return None
1458 @staticmethod
1459 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1460 """
1461 Creates a Circuit16 ansatz.
1463 Length of flattened vector must be n_qubits*3-1
1465 Parameters
1466 ----------
1467 w : np.ndarray
1468 Weight vector of size n_qubits*3-1
1469 n_qubits : int
1470 Number of qubits
1471 noise_params : Optional[Dict[str, float]], optional
1472 Dictionary of noise parameters to apply to the gates
1473 """
1474 w_idx = 0
1475 for q in range(n_qubits):
1476 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1477 w_idx += 1
1478 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1479 w_idx += 1
1481 if n_qubits > 1:
1482 for q in range(n_qubits // 2):
1483 Gates.CRZ(
1484 w[w_idx],
1485 wires=[(2 * q + 1), (2 * q)],
1486 noise_params=noise_params,
1487 )
1488 w_idx += 1
1490 for q in range((n_qubits - 1) // 2):
1491 Gates.CRZ(
1492 w[w_idx],
1493 wires=[(2 * q + 2), (2 * q + 1)],
1494 noise_params=noise_params,
1495 )
1496 w_idx += 1
1498 class Circuit_17(Circuit):
1499 @staticmethod
1500 def n_params_per_layer(n_qubits: int) -> int:
1501 """
1502 Returns the number of parameters per layer for the Circuit_17 ansatz.
1504 The number of parameters is calculated as n_qubits*3-1.
1506 Parameters
1507 ----------
1508 n_qubits : int
1509 Number of qubits in the circuit
1511 Returns
1512 -------
1513 int
1514 Number of parameters per layer
1515 """
1517 return n_qubits * 3 - 1
1519 @staticmethod
1520 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1521 """
1522 No controlled rotation gates available. Always None.
1524 Parameters
1525 ----------
1526 n_qubits : int
1527 Number of qubits in the circuit
1529 Returns
1530 -------
1531 Optional[np.ndarray]
1532 List of all controlled indices, or None if the circuit does not
1533 contain controlled rotation gates.
1534 """
1535 if n_qubits > 1:
1536 return [-(n_qubits - 1), None, None]
1537 else:
1538 return None
1540 @staticmethod
1541 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1542 """
1543 Creates a Circuit17 ansatz.
1545 Length of flattened vector must be n_qubits*3-1
1547 Parameters
1548 ----------
1549 w : np.ndarray
1550 Weight vector of size n_qubits*3-1
1551 n_qubits : int
1552 Number of qubits
1553 noise_params : Optional[Dict[str, float]], optional
1554 Dictionary of noise parameters to apply to the gates
1555 """
1556 w_idx = 0
1557 for q in range(n_qubits):
1558 Gates.RX(w[w_idx], wires=q, noise_params=noise_params)
1559 w_idx += 1
1560 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params)
1561 w_idx += 1
1563 if n_qubits > 1:
1564 for q in range(n_qubits // 2):
1565 Gates.CRX(
1566 w[w_idx],
1567 wires=[(2 * q + 1), (2 * q)],
1568 noise_params=noise_params,
1569 )
1570 w_idx += 1
1572 for q in range((n_qubits - 1) // 2):
1573 Gates.CRX(
1574 w[w_idx],
1575 wires=[(2 * q + 2), (2 * q + 1)],
1576 noise_params=noise_params,
1577 )
1578 w_idx += 1
1580 class Strongly_Entangling(Circuit):
1581 @staticmethod
1582 def n_params_per_layer(n_qubits: int) -> int:
1583 """
1584 Returns the number of parameters per layer for the
1585 Strongly Entangling ansatz.
1587 The number of parameters is calculated as n_qubits*6.
1589 Parameters
1590 ----------
1591 n_qubits : int
1592 Number of qubits in the circuit
1594 Returns
1595 -------
1596 int
1597 Number of parameters per layer
1598 """
1599 if n_qubits < 2:
1600 log.warning("Number of Qubits < 2, no entanglement available")
1601 return n_qubits * 6
1603 @staticmethod
1604 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1605 """
1606 No controlled rotation gates available. Always None.
1608 Parameters
1609 ----------
1610 n_qubits : int
1611 Number of qubits in the circuit
1613 Returns
1614 -------
1615 Optional[np.ndarray]
1616 List of all controlled indices, or None if the circuit does not
1617 contain controlled rotation gates.
1618 """
1619 return None
1621 @staticmethod
1622 def build(w: np.ndarray, n_qubits: int, noise_params=None) -> None:
1623 """
1624 Creates a Strongly Entangling ansatz.
1626 Length of flattened vector must be n_qubits*6
1628 Parameters
1629 ----------
1630 w : np.ndarray
1631 Weight vector of size n_qubits*6
1632 n_qubits : int
1633 Number of qubits
1634 noise_params : Optional[Dict[str, float]], optional
1635 Dictionary of noise parameters to apply to the gates
1636 """
1637 w_idx = 0
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(wires=[q, (q + 1) % n_qubits], noise_params=noise_params)
1652 for q in range(n_qubits):
1653 Gates.Rot(
1654 w[w_idx],
1655 w[w_idx + 1],
1656 w[w_idx + 2],
1657 wires=q,
1658 noise_params=noise_params,
1659 )
1660 w_idx += 3
1662 if n_qubits > 1:
1663 for q in range(n_qubits):
1664 Gates.CX(
1665 wires=[q, (q + n_qubits // 2) % n_qubits],
1666 noise_params=noise_params,
1667 )
1669 class No_Entangling(Circuit):
1670 @staticmethod
1671 def n_params_per_layer(n_qubits: int) -> int:
1672 """
1673 Returns the number of parameters per layer for the NoEntangling ansatz.
1675 The number of parameters is calculated as n_qubits*3.
1677 Parameters
1678 ----------
1679 n_qubits : int
1680 Number of qubits in the circuit
1682 Returns
1683 -------
1684 int
1685 Number of parameters per layer
1686 """
1687 return n_qubits * 3
1689 @staticmethod
1690 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
1691 """
1692 No controlled rotation gates available. Always None.
1694 Parameters
1695 ----------
1696 n_qubits : int
1697 Number of qubits in the circuit
1699 Returns
1700 -------
1701 Optional[np.ndarray]
1702 List of all controlled indices, or None if the circuit does not
1703 contain controlled rotation gates.
1704 """
1705 return None
1707 @staticmethod
1708 def build(w: np.ndarray, n_qubits: int, noise_params=None):
1709 """
1710 Creates a circuit without entangling, but with U3 gates on all qubits
1712 Length of flattened vector must be n_qubits*3
1714 Parameters
1715 ----------
1716 w : np.ndarray
1717 Weight vector of size n_qubits*3
1718 n_qubits : int
1719 Number of qubits
1720 noise_params : Optional[Dict[str, float]], optional
1721 Dictionary of noise parameters to apply to the gates
1722 """
1723 w_idx = 0
1724 for q in range(n_qubits):
1725 Gates.Rot(
1726 w[w_idx],
1727 w[w_idx + 1],
1728 w[w_idx + 2],
1729 wires=q,
1730 noise_params=noise_params,
1731 )
1732 w_idx += 3