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

1from abc import ABC, abstractmethod 

2from typing import Any, Optional 

3import pennylane.numpy as np 

4import pennylane as qml 

5import itertools 

6 

7from typing import List, Union, Dict 

8 

9import logging 

10 

11log = logging.getLogger(__name__) 

12 

13 

14class Circuit(ABC): 

15 def __init__(self): 

16 pass 

17 

18 @abstractmethod 

19 def n_params_per_layer(n_qubits: int) -> int: 

20 raise NotImplementedError("n_params_per_layer method is not implemented") 

21 

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]] 

28 

29 Parameters 

30 ---------- 

31 n_qubits : int 

32 Number of qubits in the circuit 

33 

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") 

41 

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. 

46 

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 

53 

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([]) 

63 

64 return w[indices[0] : indices[1] : indices[2]] 

65 

66 @abstractmethod 

67 def build(self, n_qubits: int, n_layers: int): 

68 raise NotImplementedError("build method is not implemented") 

69 

70 def __call__(self, *args: Any, **kwds: Any) -> Any: 

71 self.build(*args, **kwds) 

72 

73 

74class Gates: 

75 rng = np.random.default_rng() 

76 batch_gate_error = True 

77 

78 @staticmethod 

79 def init_rng(seed: int): 

80 """ 

81 Initializes the random number generator with the given seed. 

82 

83 Parameters 

84 ---------- 

85 seed : int 

86 The seed for the random number generator. 

87 """ 

88 Gates.rng = np.random.default_rng(seed) 

89 

90 @staticmethod 

91 def NQubitDepolarizingChannel(p, wires): 

92 """ 

93 Generates the Kraus operators for an n-qubit depolarizing channel. 

94 

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. 

101 

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. 

105 

106 Parameters 

107 ---------- 

108 p : float 

109 The total probability of an n-qubit depolarizing error occurring. 

110 Must satisfy 0 ≤ p ≤ 1. 

111 

112 wires : Sequence[int] 

113 The list of qubit indices (wires) on which the channel acts. 

114 Must contain at least 2 qubits. 

115 

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 """ 

122 

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}") 

128 

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] 

134 

135 dim = 2**n 

136 all_ops = [] 

137 

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) 

144 

145 # Identity operator corresponds to all zeros indices (Id^n) 

146 K0 = np.sqrt(1 - p * (4**n - 1) / (4**n)) * np.eye(dim) 

147 

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) 

154 

155 return [K0] + kraus_ops 

156 

157 return qml.QubitChannel(n_qubit_depolarizing_kraus(p, len(wires)), wires=wires) 

158 

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. 

165 

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. 

179 

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 

185 

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) 

191 

192 pf = noise_params.get("PhaseFlip", 0.0) 

193 if pf > 0: 

194 qml.PhaseFlip(pf, wires=wire) 

195 

196 dp = noise_params.get("Depolarizing", 0.0) 

197 if dp > 0: 

198 qml.DepolarizingChannel(dp, wires=wire) 

199 

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) 

205 

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). 

212 

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. 

223 

224 All parameters are optional and default to 0.0 if not provided. 

225 

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 

242 

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` 

247 

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. 

265 

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) 

277 

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` 

282 

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. 

296 

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) 

302 

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` 

307 

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. 

321 

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) 

327 

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` 

332 

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. 

346 

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) 

352 

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` 

358 

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. 

372 

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) 

378 

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` 

384 

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. 

398 

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) 

404 

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` 

410 

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. 

424 

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) 

430 

431 @staticmethod 

432 def CX(wires, noise_params=None): 

433 """ 

434 Applies a controlled NOT gate to the given wires and adds `Noise` 

435 

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. 

447 

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) 

452 

453 @staticmethod 

454 def CY(wires, noise_params=None): 

455 """ 

456 Applies a controlled Y gate to the given wires and adds `Noise` 

457 

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. 

469 

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) 

474 

475 @staticmethod 

476 def CZ(wires, noise_params=None): 

477 """ 

478 Applies a controlled Z gate to the given wires and adds `Noise` 

479 

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. 

491 

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) 

496 

497 @staticmethod 

498 def H(wires, noise_params=None): 

499 """ 

500 Applies a Hadamard gate to the given wires and adds `Noise` 

501 

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. 

513 

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) 

518 

519 

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 ] 

541 

542 class No_Ansatz(Circuit): 

543 @staticmethod 

544 def n_params_per_layer(n_qubits: int) -> int: 

545 return 0 

546 

547 @staticmethod 

548 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

549 return None 

550 

551 @staticmethod 

552 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

553 pass 

554 

555 class GHZ(Circuit): 

556 @staticmethod 

557 def n_params_per_layer(n_qubits: int) -> int: 

558 return 0 

559 

560 @staticmethod 

561 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

562 return None 

563 

564 @staticmethod 

565 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

566 Gates.H(0, noise_params=noise_params) 

567 

568 for q in range(n_qubits - 1): 

569 Gates.CX([q, q + 1], noise_params=noise_params) 

570 

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. 

577 

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. 

582 

583 Parameters 

584 ---------- 

585 n_qubits : int 

586 Number of qubits in the circuit 

587 

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 

596 

597 @staticmethod 

598 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

599 """ 

600 No controlled rotation gates available. Always None. 

601 

602 Parameters 

603 ---------- 

604 n_qubits : int 

605 Number of qubits in the circuit 

606 

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 

614 

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 

620 

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 

638 

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) 

648 

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. 

654 

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. 

659 

660 Parameters 

661 ---------- 

662 n_qubits : int 

663 Number of qubits in the circuit 

664 

665 Returns 

666 ------- 

667 int 

668 Number of parameters required for one layer of the circuit 

669 """ 

670 

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 

676 

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]] 

683 

684 Parameters 

685 ---------- 

686 n_qubits : int 

687 Number of qubits in the circuit 

688 

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 

699 

700 @staticmethod 

701 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

702 """ 

703 Creates a Circuit19 ansatz. 

704 

705 Length of flattened vector must be n_qubits*3 

706 because for >1 qubits there are three gates 

707 

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 

723 

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 

732 

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. 

738 

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. 

743 

744 Parameters 

745 ---------- 

746 n_qubits : int 

747 Number of qubits in the circuit 

748 

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 

759 

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]] 

766 

767 Parameters 

768 ---------- 

769 n_qubits : int 

770 Number of qubits in the circuit 

771 

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 

782 

783 @staticmethod 

784 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

785 """ 

786 Creates a Circuit18 ansatz. 

787 

788 Length of flattened vector must be n_qubits*3 

789 

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 

805 

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 

814 

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. 

820 

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. 

823 

824 Parameters 

825 ---------- 

826 n_qubits : int 

827 Number of qubits in the circuit 

828 

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 

839 

840 @staticmethod 

841 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

842 """ 

843 No controlled rotation gates available. Always None. 

844 

845 Parameters 

846 ---------- 

847 n_qubits : int 

848 Number of qubits in the circuit 

849 

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 

857 

858 @staticmethod 

859 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

860 """ 

861 Creates a Circuit15 ansatz. 

862 

863 Length of flattened vector must be n_qubits*2 

864 because for >1 qubits there are three gates 

865 

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 

879 

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 ) 

886 

887 for q in range(n_qubits): 

888 Gates.RY(w[w_idx], wires=q, noise_params=noise_params) 

889 w_idx += 1 

890 

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 ) 

897 

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. 

903 

904 The number of parameters is equal to the number of qubits. 

905 

906 Parameters 

907 ---------- 

908 n_qubits : int 

909 Number of qubits in the circuit 

910 

911 Returns 

912 ------- 

913 int 

914 Number of parameters required for one layer of the circuit 

915 """ 

916 return n_qubits 

917 

918 @staticmethod 

919 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

920 """ 

921 No controlled rotation gates available. Always None. 

922 

923 Parameters 

924 ---------- 

925 n_qubits : int 

926 Number of qubits in the circuit 

927 

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 

935 

936 @staticmethod 

937 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

938 """ 

939 Creates a Circuit9 ansatz. 

940 

941 Length of flattened vector must be n_qubits 

942 

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) 

955 

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 ) 

962 

963 for q in range(n_qubits): 

964 Gates.RX(w[w_idx], wires=q, noise_params=noise_params) 

965 w_idx += 1 

966 

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. 

972 

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. 

976 

977 If n_qubits is 1, the number of parameters is 4, and a warning is logged 

978 since no entanglement is possible. 

979 

980 Parameters 

981 ---------- 

982 n_qubits : int 

983 Number of qubits 

984 

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 

995 

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]] 

1002 

1003 Parameters 

1004 ---------- 

1005 n_qubits : int 

1006 Number of qubits in the circuit 

1007 

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 

1016 

1017 @staticmethod 

1018 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

1019 """ 

1020 Creates a Circuit6 ansatz. 

1021 

1022 Length of flattened vector must be 

1023 n_qubits*4+n_qubits*(n_qubits-1) = 

1024 n_qubits*3+n_qubits**2 

1025 

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 

1042 

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 

1054 

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 

1060 

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. 

1066 

1067 The total number of parameters is determined by the number of qubits, with 

1068 each qubit contributing 2 parameters. 

1069 

1070 Parameters 

1071 ---------- 

1072 n_qubits : int 

1073 Number of qubits in the circuit 

1074 

1075 Returns 

1076 ------- 

1077 int 

1078 Number of parameters per layer 

1079 """ 

1080 return n_qubits * 2 

1081 

1082 @staticmethod 

1083 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1084 """ 

1085 No controlled rotation gates available. Always None. 

1086 

1087 Parameters 

1088 ---------- 

1089 n_qubits : int 

1090 Number of qubits in the circuit 

1091 

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 

1099 

1100 @staticmethod 

1101 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

1102 """ 

1103 Creates a Circuit1 ansatz. 

1104 

1105 Length of flattened vector must be n_qubits*2 

1106 

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 

1122 

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. 

1128 

1129 The total number of parameters is determined by the number of qubits, with 

1130 each qubit contributing 2 parameters. 

1131 

1132 Parameters 

1133 ---------- 

1134 n_qubits : int 

1135 Number of qubits in the circuit 

1136 

1137 Returns 

1138 ------- 

1139 int 

1140 Number of parameters per layer 

1141 """ 

1142 return n_qubits * 2 

1143 

1144 @staticmethod 

1145 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1146 """ 

1147 No controlled rotation gates available. Always None. 

1148 

1149 Parameters 

1150 ---------- 

1151 n_qubits : int 

1152 Number of qubits in the circuit 

1153 

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 

1161 

1162 @staticmethod 

1163 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

1164 """ 

1165 Creates a Circuit2 ansatz. 

1166 

1167 Length of flattened vector must be n_qubits*2 

1168 

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 

1184 

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 ) 

1191 

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. 

1197 

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. 

1201 

1202 Parameters 

1203 ---------- 

1204 n_qubits : int 

1205 Number of qubits in the circuit 

1206 

1207 Returns 

1208 ------- 

1209 int 

1210 Number of parameters per layer 

1211 """ 

1212 return n_qubits * 3 - 1 

1213 

1214 @staticmethod 

1215 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1216 """ 

1217 No controlled rotation gates available. Always None. 

1218 

1219 Parameters 

1220 ---------- 

1221 n_qubits : int 

1222 Number of qubits in the circuit 

1223 

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 

1234 

1235 @staticmethod 

1236 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

1237 """ 

1238 Creates a Circuit3 ansatz. 

1239 

1240 Length of flattened vector must be n_qubits*3-1 

1241 

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 

1257 

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 

1266 

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. 

1272 

1273 The number of parameters is calculated as n_qubits*3-1. 

1274 

1275 Parameters 

1276 ---------- 

1277 n_qubits : int 

1278 Number of qubits in the circuit 

1279 

1280 Returns 

1281 ------- 

1282 int 

1283 Number of parameters per layer 

1284 """ 

1285 return n_qubits * 3 - 1 

1286 

1287 @staticmethod 

1288 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1289 """ 

1290 No controlled rotation gates available. Always None. 

1291 

1292 Parameters 

1293 ---------- 

1294 n_qubits : int 

1295 Number of qubits in the circuit 

1296 

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 

1307 

1308 @staticmethod 

1309 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

1310 """ 

1311 Creates a Circuit4 ansatz. 

1312 

1313 Length of flattened vector must be n_qubits*3-1 

1314 

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 

1330 

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 

1339 

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. 

1345 

1346 The number of parameters is calculated as n_qubits*2. 

1347 

1348 Parameters 

1349 ---------- 

1350 n_qubits : int 

1351 Number of qubits in the circuit 

1352 

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 

1359 

1360 @staticmethod 

1361 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1362 """ 

1363 No controlled rotation gates available. Always None. 

1364 

1365 Parameters 

1366 ---------- 

1367 n_qubits : int 

1368 Number of qubits in the circuit 

1369 

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 

1377 

1378 @staticmethod 

1379 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

1380 """ 

1381 Creates a Circuit10 ansatz. 

1382 

1383 Length of flattened vector must be n_qubits*2 

1384 

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 

1399 

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) 

1411 

1412 for q in range(n_qubits): 

1413 Gates.RY(w[w_idx], wires=q, noise_params=noise_params) 

1414 w_idx += 1 

1415 

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. 

1421 

1422 The number of parameters is calculated as n_qubits*3-1. 

1423 

1424 Parameters 

1425 ---------- 

1426 n_qubits : int 

1427 Number of qubits in the circuit 

1428 

1429 Returns 

1430 ------- 

1431 int 

1432 Number of parameters per layer 

1433 """ 

1434 

1435 return n_qubits * 3 - 1 

1436 

1437 @staticmethod 

1438 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1439 """ 

1440 No controlled rotation gates available. Always None. 

1441 

1442 Parameters 

1443 ---------- 

1444 n_qubits : int 

1445 Number of qubits in the circuit 

1446 

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 

1457 

1458 @staticmethod 

1459 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

1460 """ 

1461 Creates a Circuit16 ansatz. 

1462 

1463 Length of flattened vector must be n_qubits*3-1 

1464 

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 

1480 

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 

1489 

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 

1497 

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. 

1503 

1504 The number of parameters is calculated as n_qubits*3-1. 

1505 

1506 Parameters 

1507 ---------- 

1508 n_qubits : int 

1509 Number of qubits in the circuit 

1510 

1511 Returns 

1512 ------- 

1513 int 

1514 Number of parameters per layer 

1515 """ 

1516 

1517 return n_qubits * 3 - 1 

1518 

1519 @staticmethod 

1520 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1521 """ 

1522 No controlled rotation gates available. Always None. 

1523 

1524 Parameters 

1525 ---------- 

1526 n_qubits : int 

1527 Number of qubits in the circuit 

1528 

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 

1539 

1540 @staticmethod 

1541 def build(w: np.ndarray, n_qubits: int, noise_params=None): 

1542 """ 

1543 Creates a Circuit17 ansatz. 

1544 

1545 Length of flattened vector must be n_qubits*3-1 

1546 

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 

1562 

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 

1571 

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 

1579 

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. 

1586 

1587 The number of parameters is calculated as n_qubits*6. 

1588 

1589 Parameters 

1590 ---------- 

1591 n_qubits : int 

1592 Number of qubits in the circuit 

1593 

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 

1602 

1603 @staticmethod 

1604 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1605 """ 

1606 No controlled rotation gates available. Always None. 

1607 

1608 Parameters 

1609 ---------- 

1610 n_qubits : int 

1611 Number of qubits in the circuit 

1612 

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 

1620 

1621 @staticmethod 

1622 def build(w: np.ndarray, n_qubits: int, noise_params=None) -> None: 

1623 """ 

1624 Creates a Strongly Entangling ansatz. 

1625 

1626 Length of flattened vector must be n_qubits*6 

1627 

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 

1647 

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) 

1651 

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 

1661 

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 ) 

1668 

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. 

1674 

1675 The number of parameters is calculated as n_qubits*3. 

1676 

1677 Parameters 

1678 ---------- 

1679 n_qubits : int 

1680 Number of qubits in the circuit 

1681 

1682 Returns 

1683 ------- 

1684 int 

1685 Number of parameters per layer 

1686 """ 

1687 return n_qubits * 3 

1688 

1689 @staticmethod 

1690 def get_control_indices(n_qubits: int) -> Optional[np.ndarray]: 

1691 """ 

1692 No controlled rotation gates available. Always None. 

1693 

1694 Parameters 

1695 ---------- 

1696 n_qubits : int 

1697 Number of qubits in the circuit 

1698 

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 

1706 

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 

1711 

1712 Length of flattened vector must be n_qubits*3 

1713 

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