Coverage for qml_essentials/ansaetze.py: 84%
228 statements
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-15 11:13 +0000
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-15 11:13 +0000
1from abc import ABC, abstractmethod
2from typing import Any, Optional
3import pennylane.numpy as np
4import pennylane as qml
6from typing import List
8import logging
10log = logging.getLogger(__name__)
13class Circuit(ABC):
14 def __init__(self):
15 pass
17 @abstractmethod
18 def n_params_per_layer(n_qubits: int) -> int:
19 return
21 @abstractmethod
22 def get_control_indices(self, n_qubits: int) -> List[int]:
23 """
24 Returns the indices for the controlled rotation gates for one layer.
25 Indices should slice the list of all parameters for one layer as follows:
26 [indices[0]:indices[1]:indices[2]]
28 Parameters
29 ----------
30 n_qubits : int
31 Number of qubits in the circuit
33 Returns
34 -------
35 Optional[np.ndarray]
36 List of all controlled indices, or None if the circuit does not
37 contain controlled rotation gates.
38 """
39 return
41 def get_control_angles(self, w: np.ndarray, n_qubits: int) -> Optional[np.ndarray]:
42 """
43 Returns the angles for the controlled rotation gates from the list of
44 all parameters for one layer.
46 Parameters
47 ----------
48 w : np.ndarray
49 List of parameters for one layer
50 n_qubits : int
51 Number of qubits in the circuit
53 Returns
54 -------
55 Optional[np.ndarray]
56 List of all controlled parameters, or None if the circuit does not
57 contain controlled rotation gates.
58 """
59 indices = self.get_control_indices(n_qubits)
60 if indices is None:
61 return None
63 return w[indices[0] : indices[1] : indices[2]]
65 @abstractmethod
66 def build(self, n_qubits: int, n_layers: int):
67 return
69 def __call__(self, *args: Any, **kwds: Any) -> Any:
70 self.build(*args, **kwds)
73class Ansaetze:
74 def get_available():
75 return [
76 Ansaetze.No_Ansatz,
77 Ansaetze.Circuit_1,
78 Ansaetze.Circuit_6,
79 Ansaetze.Circuit_9,
80 Ansaetze.Circuit_15,
81 Ansaetze.Circuit_18,
82 Ansaetze.Circuit_19,
83 Ansaetze.No_Entangling,
84 Ansaetze.Strongly_Entangling,
85 Ansaetze.Hardware_Efficient,
86 ]
88 class No_Ansatz(Circuit):
89 @staticmethod
90 def n_params_per_layer(n_qubits: int) -> int:
91 return 0
93 @staticmethod
94 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
95 return None
97 @staticmethod
98 def build(w: np.ndarray, n_qubits: int):
99 pass
101 class Hardware_Efficient(Circuit):
102 @staticmethod
103 def n_params_per_layer(n_qubits: int) -> int:
104 if n_qubits > 1:
105 return n_qubits * 3
106 else:
107 log.warning("Number of Qubits < 2, no entanglement available")
108 return 3
110 @staticmethod
111 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
112 return None
114 @staticmethod
115 def build(w: np.ndarray, n_qubits: int):
116 """
117 Creates a Hardware-Efficient ansatz, as proposed in
118 https://arxiv.org/pdf/2309.03279
120 Length of flattened vector must be n_qubits*3
122 Args:
123 w (np.ndarray): weight vector of size n_layers*(n_qubits*3)
124 n_qubits (int): number of qubits
125 """
126 w_idx = 0
127 for q in range(n_qubits):
128 qml.RX(w[w_idx], wires=q)
129 w_idx += 1
130 qml.RY(w[w_idx], wires=q)
131 w_idx += 1
132 qml.RX(w[w_idx], wires=q)
133 w_idx += 1
135 if n_qubits > 1:
136 for q in range(n_qubits // 2):
137 qml.CZ(wires=[(2 * q), (2 * q + 1)])
138 for q in range((n_qubits - 1) // 2):
139 qml.CZ(wires=[(2 * q + 1), (2 * q + 2)])
141 class Circuit_19(Circuit):
142 @staticmethod
143 def n_params_per_layer(n_qubits: int) -> int:
144 if n_qubits > 1:
145 return n_qubits * 3
146 else:
147 log.warning("Number of Qubits < 2, no entanglement available")
148 return 2
150 @staticmethod
151 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
152 if n_qubits > 1:
153 return [-n_qubits, None, None]
154 else:
155 return None
157 @staticmethod
158 def build(w: np.ndarray, n_qubits: int):
159 """
160 Creates a Circuit19 ansatz.
162 Length of flattened vector must be n_qubits*3-1
163 because for >1 qubits there are three gates
165 Args:
166 w (np.ndarray): weight vector of size n_layers*(n_qubits*3-1)
167 n_qubits (int): number of qubits
168 """
169 w_idx = 0
170 for q in range(n_qubits):
171 qml.RX(w[w_idx], wires=q)
172 w_idx += 1
173 qml.RZ(w[w_idx], wires=q)
174 w_idx += 1
176 if n_qubits > 1:
177 for q in range(n_qubits):
178 qml.CRX(
179 w[w_idx],
180 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
181 )
182 w_idx += 1
184 class Circuit_18(Circuit):
185 @staticmethod
186 def n_params_per_layer(n_qubits: int) -> int:
187 if n_qubits > 1:
188 return n_qubits * 3
189 else:
190 log.warning("Number of Qubits < 2, no entanglement available")
191 return 2
193 @staticmethod
194 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
195 if n_qubits > 1:
196 return [-n_qubits, None, None]
197 else:
198 return None
200 @staticmethod
201 def build(w: np.ndarray, n_qubits: int):
202 """
203 Creates a Circuit18 ansatz.
205 Length of flattened vector must be n_qubits*3
207 Args:
208 w (np.ndarray): weight vector of size n_layers*(n_qubits*3)
209 n_qubits (int): number of qubits
210 """
211 w_idx = 0
212 for q in range(n_qubits):
213 qml.RX(w[w_idx], wires=q)
214 w_idx += 1
215 qml.RZ(w[w_idx], wires=q)
216 w_idx += 1
218 if n_qubits > 1:
219 for q in range(n_qubits):
220 qml.CRZ(
221 w[w_idx],
222 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits],
223 )
224 w_idx += 1
226 class Circuit_15(Circuit):
227 @staticmethod
228 def n_params_per_layer(n_qubits: int) -> int:
229 if n_qubits > 1:
230 return n_qubits * 2
231 else:
232 log.warning("Number of Qubits < 2, no entanglement available")
233 return 2
235 @staticmethod
236 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
237 return None
239 @staticmethod
240 def build(w: np.ndarray, n_qubits: int):
241 """
242 Creates a Circuit15 ansatz.
244 Length of flattened vector must be n_qubits*2
245 because for >1 qubits there are three gates
247 Args:
248 w (np.ndarray): weight vector of size n_layers*(n_qubits*2)
249 n_qubits (int): number of qubits
250 """
251 raise NotImplementedError # Did not figured out the entangling sequence yet
253 w_idx = 0
254 for q in range(n_qubits):
255 qml.RX(w[w_idx], wires=q)
256 w_idx += 1
258 if n_qubits > 1:
259 for q in range(n_qubits):
260 qml.CNOT(wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits])
262 for q in range(n_qubits):
263 qml.RZ(w[w_idx], wires=q)
264 w_idx += 1
266 class Circuit_9(Circuit):
267 @staticmethod
268 def n_params_per_layer(n_qubits: int) -> int:
269 return n_qubits
271 @staticmethod
272 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
273 return None
275 @staticmethod
276 def build(w: np.ndarray, n_qubits: int):
277 """
278 Creates a Circuit9 ansatz.
280 Length of flattened vector must be n_qubits
282 Args:
283 w (np.ndarray): weight vector of size n_layers*n_qubits
284 n_qubits (int): number of qubits
285 """
286 w_idx = 0
287 for q in range(n_qubits):
288 qml.Hadamard(wires=q)
290 if n_qubits > 1:
291 for q in range(n_qubits - 1):
292 qml.CZ(wires=[n_qubits - q - 2, n_qubits - q - 1])
294 for q in range(n_qubits):
295 qml.RX(w[w_idx], wires=q)
296 w_idx += 1
298 class Circuit_6(Circuit):
299 @staticmethod
300 def n_params_per_layer(n_qubits: int) -> int:
301 if n_qubits > 1:
302 return n_qubits * 3 + n_qubits**2
303 else:
304 log.warning("Number of Qubits < 2, no entanglement available")
305 return 4
307 @staticmethod
308 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
309 if n_qubits > 1:
310 return [-n_qubits, None, None]
311 else:
312 return None
314 @staticmethod
315 def build(w: np.ndarray, n_qubits: int):
316 """
317 Creates a Circuit6 ansatz.
319 Length of flattened vector must be
320 n_qubits * 4 + n_qubits * (n_qubits - 1) =
321 n_qubits * 3 + n_qubits**2
323 Args:
324 w (np.ndarray): weight vector of size
325 n_layers * (n_qubits * 3 + n_qubits**2)
326 n_qubits (int): number of qubits
327 """
328 w_idx = 0
329 for q in range(n_qubits):
330 qml.RX(w[w_idx], wires=q)
331 w_idx += 1
332 qml.RZ(w[w_idx], wires=q)
333 w_idx += 1
335 if n_qubits > 1:
336 for ql in range(n_qubits):
337 for q in range(n_qubits):
338 if q == ql:
339 continue
340 qml.CRX(
341 w[w_idx],
342 wires=[n_qubits - ql - 1, (n_qubits - q - 1) % n_qubits],
343 )
344 w_idx += 1
346 for q in range(n_qubits):
347 qml.RX(w[w_idx], wires=q)
348 w_idx += 1
349 qml.RZ(w[w_idx], wires=q)
350 w_idx += 1
352 class Circuit_1(Circuit):
353 @staticmethod
354 def n_params_per_layer(n_qubits: int) -> int:
355 return n_qubits * 2
357 @staticmethod
358 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
359 return None
361 @staticmethod
362 def build(w: np.ndarray, n_qubits: int):
363 """
364 Creates a Circuit1 ansatz.
366 Length of flattened vector must be n_qubits*2
368 Args:
369 w (np.ndarray): weight vector of size n_layers*(n_qubits*2)
370 n_qubits (int): number of qubits
371 """
372 w_idx = 0
373 for q in range(n_qubits):
374 qml.RX(w[w_idx], wires=q)
375 w_idx += 1
376 qml.RZ(w[w_idx], wires=q)
377 w_idx += 1
379 class Strongly_Entangling(Circuit):
380 @staticmethod
381 def n_params_per_layer(n_qubits: int) -> int:
382 if n_qubits > 1:
383 return n_qubits * 6
384 else:
385 log.warning("Number of Qubits < 2, no entanglement available")
386 return 2
388 @staticmethod
389 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
390 return None
392 @staticmethod
393 def build(w: np.ndarray, n_qubits: int) -> None:
394 """
395 Creates a StronglyEntanglingLayers ansatz.
397 Args:
398 w (np.ndarray): weight vector of size n_layers*(n_qubits*6)
399 n_qubits (int): number of qubits
400 """
401 w_idx = 0
402 for q in range(n_qubits):
403 qml.Rot(w[w_idx], w[w_idx + 1], w[w_idx + 2], wires=q)
404 w_idx += 3
406 if n_qubits > 1:
407 for q in range(n_qubits):
408 qml.CNOT(wires=[q, (q + 1) % n_qubits])
410 for q in range(n_qubits):
411 qml.Rot(w[w_idx], w[w_idx + 1], w[w_idx + 2], wires=q)
412 w_idx += 3
414 if n_qubits > 1:
415 for q in range(n_qubits):
416 qml.CNOT(wires=[q, (q + n_qubits // 2) % n_qubits])
418 class No_Entangling(Circuit):
419 @staticmethod
420 def n_params_per_layer(n_qubits: int) -> int:
421 return n_qubits * 3
423 @staticmethod
424 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]:
425 return None
427 @staticmethod
428 def build(w: np.ndarray, n_qubits: int):
429 """
430 Creates a circuit without entangling, but with U3 gates on all qubits
432 Length of flattened vector must be n_qubits*3
434 Args:
435 w (np.ndarray): weight vector of size n_layers*(n_qubits*3)
436 n_qubits (int): number of qubits
437 """
438 w_idx = 0
439 for q in range(n_qubits):
440 qml.Rot(w[w_idx], w[w_idx + 1], w[w_idx + 2], wires=q)
441 w_idx += 3