Coverage for qml_essentials/ansaetze.py: 65%

444 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-15 15:48 +0000

1from abc import ABC, abstractmethod 

2from typing import Any, Optional 

3import pennylane.numpy as np 

4import pennylane as qml 

5 

6from typing import List, Union, Dict 

7 

8import logging 

9 

10log = logging.getLogger(__name__) 

11 

12 

13class Circuit(ABC): 

14 def __init__(self): 

15 pass 

16 

17 @abstractmethod 

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

19 return 

20 

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

27 

28 Parameters 

29 ---------- 

30 n_qubits : int 

31 Number of qubits in the circuit 

32 

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 

40 

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. 

45 

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 

52 

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 

62 

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

64 

65 @abstractmethod 

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

67 return 

68 

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

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

71 

72 

73class Gates: 

74 rng = np.random.default_rng() 

75 

76 @staticmethod 

77 def init_rng(seed: int): 

78 """ 

79 Initializes the random number generator with the given seed. 

80 

81 Parameters 

82 ---------- 

83 seed : int 

84 The seed for the random number generator. 

85 """ 

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

87 

88 @staticmethod 

89 def Noise( 

90 wires: Union[int, List[int]], noise_params: Optional[Dict[str, float]] = None 

91 ) -> None: 

92 """ 

93 Applies noise to the given wires. 

94 

95 Parameters 

96 ---------- 

97 wires : Union[int, List[int]] 

98 The wire(s) to apply the noise to. 

99 noise_params : Optional[Dict[str, float]] 

100 A dictionary of noise parameters. The following noise gates are 

101 supported: 

102 -BitFlip: Applies a bit flip error to the given wires. 

103 -PhaseFlip: Applies a phase flip error to the given wires. 

104 -Depolarizing: Applies a depolarizing channel error to the 

105 given wires. 

106 

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

108 """ 

109 if noise_params is not None: 

110 if isinstance(wires, int): 

111 wires = [wires] # single qubit gate 

112 # iterate for multi qubit gates 

113 for wire in wires: 

114 qml.BitFlip(noise_params.get("BitFlip", 0.0), wires=wire) 

115 qml.PhaseFlip(noise_params.get("PhaseFlip", 0.0), wires=wire) 

116 qml.DepolarizingChannel( 

117 noise_params.get("Depolarizing", 0.0), wires=wire 

118 ) 

119 

120 @staticmethod 

121 def GateError( 

122 w: np.ndarray, noise_params: Optional[Dict[str, float]] = None 

123 ) -> np.ndarray: 

124 """ 

125 Applies a gate error to the given rotation angle(s). 

126 

127 Parameters 

128 ---------- 

129 w : np.ndarray 

130 The rotation angle(s) in radians. 

131 noise_params : Optional[Dict[str, float]] 

132 A dictionary of noise parameters. The following noise gates are 

133 supported: 

134 -GateError: Applies a normal distribution error to the rotation 

135 angle(s). The standard deviation of the noise is specified by 

136 the "GateError" key in the dictionary. 

137 

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

139 

140 Returns 

141 ------- 

142 np.ndarray 

143 The modified rotation angle(s) after applying the gate error. 

144 """ 

145 if noise_params is not None: 

146 w += Gates.rng.normal(0, noise_params["GateError"], w.shape) 

147 return w 

148 

149 @staticmethod 

150 def Rot(phi, theta, omega, wires, noise_params=None): 

151 """ 

152 Applies a rotation gate to the given wires and adds `Noise` 

153 

154 Parameters 

155 ---------- 

156 phi : float 

157 The first rotation angle in radians. 

158 theta : float 

159 The second rotation angle in radians. 

160 omega : float 

161 The third rotation angle in radians. 

162 wires : Union[int, List[int]] 

163 The wire(s) to apply the rotation gate to. 

164 noise_params : Optional[Dict[str, float]] 

165 A dictionary of noise parameters. The following noise gates are 

166 supported: 

167 -BitFlip: Applies a bit flip error to the given wires. 

168 -PhaseFlip: Applies a phase flip error to the given wires. 

169 -Depolarizing: Applies a depolarizing channel error to the 

170 given wires. 

171 

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

173 """ 

174 if noise_params is not None and "GateError" in noise_params: 

175 phi += Gates.rng.normal(0, noise_params["GateError"]) 

176 theta += Gates.rng.normal(0, noise_params["GateError"]) 

177 omega += Gates.rng.normal(0, noise_params["GateError"]) 

178 qml.Rot(phi, theta, omega, wires=wires) 

179 Gates.Noise(wires, noise_params) 

180 

181 @staticmethod 

182 def RX(w, wires, noise_params=None): 

183 """ 

184 Applies a rotation around the X axis to the given wires and adds `Noise` 

185 

186 Parameters 

187 ---------- 

188 w : float 

189 The rotation angle in radians. 

190 wires : Union[int, List[int]] 

191 The wire(s) to apply the rotation gate to. 

192 noise_params : Optional[Dict[str, float]] 

193 A dictionary of noise parameters. The following noise gates are 

194 supported: 

195 -BitFlip: Applies a bit flip error to the given wires. 

196 -PhaseFlip: Applies a phase flip error to the given wires. 

197 -Depolarizing: Applies a depolarizing channel error to the 

198 given wires. 

199 

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

201 """ 

202 qml.RX(w, wires=wires) 

203 Gates.Noise(wires, noise_params) 

204 

205 @staticmethod 

206 def RY(w, wires, noise_params=None): 

207 """ 

208 Applies a rotation around the Y axis to the given wires and adds `Noise` 

209 

210 Parameters 

211 ---------- 

212 w : float 

213 The rotation angle in radians. 

214 wires : Union[int, List[int]] 

215 The wire(s) to apply the rotation gate to. 

216 noise_params : Optional[Dict[str, float]] 

217 A dictionary of noise parameters. The following noise gates are 

218 supported: 

219 -BitFlip: Applies a bit flip error to the given wires. 

220 -PhaseFlip: Applies a phase flip error to the given wires. 

221 -Depolarizing: Applies a depolarizing channel error to the 

222 given wires. 

223 

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

225 """ 

226 w = Gates.GateError(w, noise_params) 

227 qml.RY(w, wires=wires) 

228 Gates.Noise(wires, noise_params) 

229 

230 @staticmethod 

231 def RZ(w, wires, noise_params=None): 

232 """ 

233 Applies a rotation around the Z axis to the given wires and adds `Noise` 

234 

235 Parameters 

236 ---------- 

237 w : float 

238 The rotation angle in radians. 

239 wires : Union[int, List[int]] 

240 The wire(s) to apply the rotation gate to. 

241 noise_params : Optional[Dict[str, float]] 

242 A dictionary of noise parameters. The following noise gates are 

243 supported: 

244 -BitFlip: Applies a bit flip error to the given wires. 

245 -PhaseFlip: Applies a phase flip error to the given wires. 

246 -Depolarizing: Applies a depolarizing channel error to the 

247 given wires. 

248 

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

250 """ 

251 qml.RZ(w, wires=wires) 

252 Gates.Noise(wires, noise_params) 

253 

254 @staticmethod 

255 def CRX(w, wires, noise_params=None): 

256 """ 

257 Applies a controlled rotation around the X axis to the given wires 

258 and adds `Noise` 

259 

260 Parameters 

261 ---------- 

262 w : float 

263 The rotation angle in radians. 

264 wires : Union[int, List[int]] 

265 The wire(s) to apply the controlled rotation gate to. 

266 noise_params : Optional[Dict[str, float]] 

267 A dictionary of noise parameters. The following noise gates are 

268 supported: 

269 -BitFlip: Applies a bit flip error to the given wires. 

270 -PhaseFlip: Applies a phase flip error to the given wires. 

271 -Depolarizing: Applies a depolarizing channel error to the 

272 given wires. 

273 

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

275 """ 

276 w = Gates.GateError(w, noise_params) 

277 qml.CRX(w, wires=wires) 

278 Gates.Noise(wires, noise_params) 

279 

280 @staticmethod 

281 def CRY(w, wires, noise_params=None): 

282 """ 

283 Applies a controlled rotation around the Y axis to the given wires 

284 and adds `Noise` 

285 

286 Parameters 

287 ---------- 

288 w : float 

289 The rotation angle in radians. 

290 wires : Union[int, List[int]] 

291 The wire(s) to apply the controlled rotation gate to. 

292 noise_params : Optional[Dict[str, float]] 

293 A dictionary of noise parameters. The following noise gates are 

294 supported: 

295 -BitFlip: Applies a bit flip error to the given wires. 

296 -PhaseFlip: Applies a phase flip error to the given wires. 

297 -Depolarizing: Applies a depolarizing channel error to the 

298 given wires. 

299 

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

301 """ 

302 w = Gates.GateError(w, noise_params) 

303 qml.CRY(w, wires=wires) 

304 Gates.Noise(wires, noise_params) 

305 

306 @staticmethod 

307 def CRZ(w, wires, noise_params=None): 

308 """ 

309 Applies a controlled rotation around the Z axis to the given wires 

310 and adds `Noise` 

311 

312 Parameters 

313 ---------- 

314 w : float 

315 The rotation angle in radians. 

316 wires : Union[int, List[int]] 

317 The wire(s) to apply the controlled rotation gate to. 

318 noise_params : Optional[Dict[str, float]] 

319 A dictionary of noise parameters. The following noise gates are 

320 supported: 

321 -BitFlip: Applies a bit flip error to the given wires. 

322 -PhaseFlip: Applies a phase flip error to the given wires. 

323 -Depolarizing: Applies a depolarizing channel error to the 

324 given wires. 

325 

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

327 """ 

328 w = Gates.GateError(w, noise_params) 

329 qml.CRZ(w, wires=wires) 

330 Gates.Noise(wires, noise_params) 

331 

332 @staticmethod 

333 def CX(wires, noise_params=None): 

334 """ 

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

336 

337 Parameters 

338 ---------- 

339 wires : Union[int, List[int]] 

340 The wire(s) to apply the controlled NOT gate to. 

341 noise_params : Optional[Dict[str, float]] 

342 A dictionary of noise parameters. The following noise gates are 

343 supported: 

344 -BitFlip: Applies a bit flip error to the given wires. 

345 -PhaseFlip: Applies a phase flip error to the given wires. 

346 -Depolarizing: Applies a depolarizing channel error to the 

347 given wires. 

348 

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

350 """ 

351 qml.CNOT(wires=wires) 

352 Gates.Noise(wires, noise_params) 

353 

354 @staticmethod 

355 def CY(wires, noise_params=None): 

356 """ 

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

358 

359 Parameters 

360 ---------- 

361 wires : Union[int, List[int]] 

362 The wire(s) to apply the controlled Y gate to. 

363 noise_params : Optional[Dict[str, float]] 

364 A dictionary of noise parameters. The following noise gates are 

365 supported: 

366 -BitFlip: Applies a bit flip error to the given wires. 

367 -PhaseFlip: Applies a phase flip error to the given wires. 

368 -Depolarizing: Applies a depolarizing channel error to the 

369 given wires. 

370 

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

372 """ 

373 qml.CY(wires=wires) 

374 Gates.Noise(wires, noise_params) 

375 

376 @staticmethod 

377 def CZ(wires, noise_params=None): 

378 """ 

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

380 

381 Parameters 

382 ---------- 

383 wires : Union[int, List[int]] 

384 The wire(s) to apply the controlled Z gate to. 

385 noise_params : Optional[Dict[str, float]] 

386 A dictionary of noise parameters. The following noise gates are 

387 supported: 

388 -BitFlip: Applies a bit flip error to the given wires. 

389 -PhaseFlip: Applies a phase flip error to the given wires. 

390 -Depolarizing: Applies a depolarizing channel error to the 

391 given wires. 

392 

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

394 """ 

395 qml.CZ(wires=wires) 

396 Gates.Noise(wires, noise_params) 

397 

398 @staticmethod 

399 def H(wires, noise_params=None): 

400 """ 

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

402 

403 Parameters 

404 ---------- 

405 wires : Union[int, List[int]] 

406 The wire(s) to apply the Hadamard gate to. 

407 noise_params : Optional[Dict[str, float]] 

408 A dictionary of noise parameters. The following noise gates are 

409 supported: 

410 -BitFlip: Applies a bit flip error to the given wires. 

411 -PhaseFlip: Applies a phase flip error to the given wires. 

412 -Depolarizing: Applies a depolarizing channel error to the 

413 given wires. 

414 

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

416 """ 

417 qml.Hadamard(wires=wires) 

418 Gates.Noise(wires, noise_params) 

419 

420 

421class Ansaetze: 

422 

423 def get_available(): 

424 return [ 

425 Ansaetze.No_Ansatz, 

426 Ansaetze.Circuit_1, 

427 Ansaetze.Circuit_2, 

428 Ansaetze.Circuit_3, 

429 Ansaetze.Circuit_4, 

430 Ansaetze.Circuit_6, 

431 Ansaetze.Circuit_9, 

432 Ansaetze.Circuit_10, 

433 Ansaetze.Circuit_15, 

434 Ansaetze.Circuit_16, 

435 Ansaetze.Circuit_17, 

436 Ansaetze.Circuit_18, 

437 Ansaetze.Circuit_19, 

438 Ansaetze.No_Entangling, 

439 Ansaetze.Strongly_Entangling, 

440 Ansaetze.Hardware_Efficient, 

441 Ansaetze.GHZ, 

442 ] 

443 

444 class No_Ansatz(Circuit): 

445 @staticmethod 

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

447 return 0 

448 

449 @staticmethod 

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

451 return None 

452 

453 @staticmethod 

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

455 pass 

456 

457 class GHZ(Circuit): 

458 @staticmethod 

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

460 return 0 

461 

462 @staticmethod 

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

464 return None 

465 

466 @staticmethod 

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

468 Gates.H(0, noise_params=noise_params) 

469 

470 for q in range(n_qubits - 1): 

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

472 

473 class Hardware_Efficient(Circuit): 

474 @staticmethod 

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

476 """ 

477 Returns the number of parameters per layer for the 

478 Hardware Efficient Ansatz. 

479 

480 The number of parameters is 3 times the number of qubits when there 

481 is more than one qubit, as each qubit contributes 3 parameters. 

482 If the number of qubits is less than 2, a warning is logged since 

483 no entanglement is possible, and a fixed number of 2 parameters is used. 

484 

485 Parameters 

486 ---------- 

487 n_qubits : int 

488 Number of qubits in the circuit 

489 

490 Returns 

491 ------- 

492 int 

493 Number of parameters required for one layer of the circuit 

494 """ 

495 if n_qubits < 2: 

496 log.warning("Number of Qubits < 2, no entanglement available") 

497 return n_qubits * 3 

498 

499 @staticmethod 

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

501 """ 

502 No controlled rotation gates available. Always None. 

503 

504 Parameters 

505 ---------- 

506 n_qubits : int 

507 Number of qubits in the circuit 

508 

509 Returns 

510 ------- 

511 Optional[np.ndarray] 

512 List of all controlled indices, or None if the circuit does not 

513 contain controlled rotation gates. 

514 """ 

515 return None 

516 

517 @staticmethod 

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

519 """ 

520 Creates a Hardware-Efficient ansatz, as proposed in 

521 https://arxiv.org/pdf/2309.03279 

522 

523 Parameters 

524 ---------- 

525 w : np.ndarray 

526 Weight vector of size n_qubits*3 

527 n_qubits : int 

528 Number of qubits 

529 noise_params : Optional[Dict[str, float]], optional 

530 Dictionary of noise parameters to apply to the gates 

531 """ 

532 w_idx = 0 

533 for q in range(n_qubits): 

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

535 w_idx += 1 

536 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

537 w_idx += 1 

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

539 w_idx += 1 

540 

541 if n_qubits > 1: 

542 for q in range(n_qubits // 2): 

543 Gates.CX(wires=[(2 * q), (2 * q + 1)], noise_params=noise_params) 

544 for q in range((n_qubits - 1) // 2): 

545 Gates.CX( 

546 wires=[(2 * q + 1), (2 * q + 2)], noise_params=noise_params 

547 ) 

548 if n_qubits > 2: 

549 Gates.CX(wires=[(n_qubits - 1), 0], noise_params=noise_params) 

550 

551 class Circuit_19(Circuit): 

552 @staticmethod 

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

554 """ 

555 Returns the number of parameters per layer for Circuit_19. 

556 

557 The number of parameters is 3 times the number of qubits when there 

558 is more than one qubit, as each qubit contributes 3 parameters. 

559 If the number of qubits is less than 2, a warning is logged since 

560 no entanglement is possible, and a fixed number of 2 parameters is used. 

561 

562 Parameters 

563 ---------- 

564 n_qubits : int 

565 Number of qubits in the circuit 

566 

567 Returns 

568 ------- 

569 int 

570 Number of parameters required for one layer of the circuit 

571 """ 

572 

573 if n_qubits > 1: 

574 return n_qubits * 3 

575 else: 

576 log.warning("Number of Qubits < 2, no entanglement available") 

577 return 2 

578 

579 @staticmethod 

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

581 """ 

582 Returns the indices for the controlled rotation gates for one layer. 

583 Indices should slice the list of all parameters for one layer as follows: 

584 [indices[0]:indices[1]:indices[2]] 

585 

586 Parameters 

587 ---------- 

588 n_qubits : int 

589 Number of qubits in the circuit 

590 

591 Returns 

592 ------- 

593 Optional[np.ndarray] 

594 List of all controlled indices, or None if the circuit does not 

595 contain controlled rotation gates. 

596 """ 

597 if n_qubits > 1: 

598 return [-n_qubits, None, None] 

599 else: 

600 return None 

601 

602 @staticmethod 

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

604 """ 

605 Creates a Circuit19 ansatz. 

606 

607 Length of flattened vector must be n_qubits*3 

608 because for >1 qubits there are three gates 

609 

610 Parameters 

611 ---------- 

612 w : np.ndarray 

613 Weight vector of size n_qubits*3 

614 n_qubits : int 

615 Number of qubits 

616 noise_params : Optional[Dict[str, float]], optional 

617 Dictionary of noise parameters to apply to the gates 

618 """ 

619 w_idx = 0 

620 for q in range(n_qubits): 

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

622 w_idx += 1 

623 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

624 w_idx += 1 

625 

626 if n_qubits > 1: 

627 for q in range(n_qubits): 

628 Gates.CRX( 

629 w[w_idx], 

630 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits], 

631 noise_params=noise_params, 

632 ) 

633 w_idx += 1 

634 

635 class Circuit_18(Circuit): 

636 @staticmethod 

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

638 """ 

639 Returns the number of parameters per layer for Circuit_18. 

640 

641 The number of parameters is 3 times the number of qubits when there 

642 is more than one qubit, as each qubit contributes 3 parameters. 

643 If the number of qubits is less than 2, a warning is logged since 

644 no entanglement is possible, and a fixed number of 2 parameters is used. 

645 

646 Parameters 

647 ---------- 

648 n_qubits : int 

649 Number of qubits in the circuit 

650 

651 Returns 

652 ------- 

653 int 

654 Number of parameters required for one layer of the circuit 

655 """ 

656 if n_qubits > 1: 

657 return n_qubits * 3 

658 else: 

659 log.warning("Number of Qubits < 2, no entanglement available") 

660 return 2 

661 

662 @staticmethod 

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

664 """ 

665 Returns the indices for the controlled rotation gates for one layer. 

666 Indices should slice the list of all parameters for one layer as follows: 

667 [indices[0]:indices[1]:indices[2]] 

668 

669 Parameters 

670 ---------- 

671 n_qubits : int 

672 Number of qubits in the circuit 

673 

674 Returns 

675 ------- 

676 Optional[np.ndarray] 

677 List of all controlled indices, or None if the circuit does not 

678 contain controlled rotation gates. 

679 """ 

680 if n_qubits > 1: 

681 return [-n_qubits, None, None] 

682 else: 

683 return None 

684 

685 @staticmethod 

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

687 """ 

688 Creates a Circuit18 ansatz. 

689 

690 Length of flattened vector must be n_qubits*3 

691 

692 Parameters 

693 ---------- 

694 w : np.ndarray 

695 Weight vector of size n_qubits*3 

696 n_qubits : int 

697 Number of qubits 

698 noise_params : Optional[Dict[str, float]], optional 

699 Dictionary of noise parameters to apply to the gates 

700 """ 

701 w_idx = 0 

702 for q in range(n_qubits): 

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

704 w_idx += 1 

705 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

706 w_idx += 1 

707 

708 if n_qubits > 1: 

709 for q in range(n_qubits): 

710 Gates.CRZ( 

711 w[w_idx], 

712 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits], 

713 noise_params=noise_params, 

714 ) 

715 w_idx += 1 

716 

717 class Circuit_15(Circuit): 

718 @staticmethod 

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

720 """ 

721 Returns the number of parameters per layer for Circuit_15. 

722 

723 The number of parameters is 2 times the number of qubits. 

724 A warning is logged if the number of qubits is less than 2. 

725 

726 Parameters 

727 ---------- 

728 n_qubits : int 

729 Number of qubits in the circuit 

730 

731 Returns 

732 ------- 

733 int 

734 Number of parameters required for one layer of the circuit 

735 """ 

736 if n_qubits > 1: 

737 return n_qubits * 2 

738 else: 

739 log.warning("Number of Qubits < 2, no entanglement available") 

740 return 2 

741 

742 @staticmethod 

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

744 """ 

745 No controlled rotation gates available. Always None. 

746 

747 Parameters 

748 ---------- 

749 n_qubits : int 

750 Number of qubits in the circuit 

751 

752 Returns 

753 ------- 

754 Optional[np.ndarray] 

755 List of all controlled indices, or None if the circuit does not 

756 contain controlled rotation gates. 

757 """ 

758 return None 

759 

760 @staticmethod 

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

762 """ 

763 Creates a Circuit15 ansatz. 

764 

765 Length of flattened vector must be n_qubits*2 

766 because for >1 qubits there are three gates 

767 

768 Parameters 

769 ---------- 

770 w : np.ndarray 

771 Weight vector of size n_qubits*2 

772 n_qubits : int 

773 Number of qubits 

774 noise_params : Optional[Dict[str, float]], optional 

775 Dictionary of noise parameters to apply to the gates 

776 """ 

777 w_idx = 0 

778 for q in range(n_qubits): 

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

780 w_idx += 1 

781 

782 if n_qubits > 1: 

783 for q in range(n_qubits): 

784 Gates.CX( 

785 wires=[n_qubits - q - 1, (n_qubits - q) % n_qubits], 

786 noise_params=noise_params, 

787 ) 

788 

789 for q in range(n_qubits): 

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

791 w_idx += 1 

792 

793 if n_qubits > 1: 

794 for q in range(n_qubits): 

795 Gates.CX( 

796 wires=[(q - 1) % n_qubits, (q - 2) % n_qubits], 

797 noise_params=noise_params, 

798 ) 

799 

800 class Circuit_9(Circuit): 

801 @staticmethod 

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

803 """ 

804 Returns the number of parameters per layer for Circuit_9. 

805 

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

807 

808 Parameters 

809 ---------- 

810 n_qubits : int 

811 Number of qubits in the circuit 

812 

813 Returns 

814 ------- 

815 int 

816 Number of parameters required for one layer of the circuit 

817 """ 

818 return n_qubits 

819 

820 @staticmethod 

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

822 """ 

823 No controlled rotation gates available. Always None. 

824 

825 Parameters 

826 ---------- 

827 n_qubits : int 

828 Number of qubits in the circuit 

829 

830 Returns 

831 ------- 

832 Optional[np.ndarray] 

833 List of all controlled indices, or None if the circuit does not 

834 contain controlled rotation gates. 

835 """ 

836 return None 

837 

838 @staticmethod 

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

840 """ 

841 Creates a Circuit9 ansatz. 

842 

843 Length of flattened vector must be n_qubits 

844 

845 Parameters 

846 ---------- 

847 w : np.ndarray 

848 Weight vector of size n_qubits 

849 n_qubits : int 

850 Number of qubits 

851 noise_params : Optional[Dict[str, float]], optional 

852 Dictionary of noise parameters to apply to the gates 

853 """ 

854 w_idx = 0 

855 for q in range(n_qubits): 

856 Gates.H(wires=q, noise_params=noise_params) 

857 

858 if n_qubits > 1: 

859 for q in range(n_qubits - 1): 

860 Gates.CZ( 

861 wires=[n_qubits - q - 2, n_qubits - q - 1], 

862 noise_params=noise_params, 

863 ) 

864 

865 for q in range(n_qubits): 

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

867 w_idx += 1 

868 

869 class Circuit_6(Circuit): 

870 @staticmethod 

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

872 """ 

873 Returns the number of parameters per layer for Circuit_6. 

874 

875 The total number of parameters is n_qubits*3+n_qubits**2, which is 

876 the number of rotations n_qubits*3 plus the number of entangling gates 

877 n_qubits**2. 

878 

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

880 since no entanglement is possible. 

881 

882 Parameters 

883 ---------- 

884 n_qubits : int 

885 Number of qubits 

886 

887 Returns 

888 ------- 

889 int 

890 Number of parameters per layer 

891 """ 

892 if n_qubits > 1: 

893 return n_qubits * 3 + n_qubits**2 

894 else: 

895 log.warning("Number of Qubits < 2, no entanglement available") 

896 return 4 

897 

898 @staticmethod 

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

900 """ 

901 Returns the indices for the controlled rotation gates for one layer. 

902 Indices should slice the list of all parameters for one layer as follows: 

903 [indices[0]:indices[1]:indices[2]] 

904 

905 Parameters 

906 ---------- 

907 n_qubits : int 

908 Number of qubits in the circuit 

909 

910 Returns 

911 ------- 

912 Optional[np.ndarray] 

913 List of all controlled indices, or None if the circuit does not 

914 contain controlled rotation gates. 

915 """ 

916 if n_qubits > 1: 

917 return [-n_qubits, None, None] 

918 else: 

919 return None 

920 

921 @staticmethod 

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

923 """ 

924 Creates a Circuit6 ansatz. 

925 

926 Length of flattened vector must be 

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

928 n_qubits*3+n_qubits**2 

929 

930 Parameters 

931 ---------- 

932 w : np.ndarray 

933 Weight vector of size 

934 n_layers*(n_qubits*3+n_qubits**2) 

935 n_qubits : int 

936 Number of qubits 

937 noise_params : Optional[Dict[str, float]], optional 

938 Dictionary of noise parameters to apply to the gates 

939 """ 

940 w_idx = 0 

941 for q in range(n_qubits): 

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

943 w_idx += 1 

944 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

945 w_idx += 1 

946 

947 if n_qubits > 1: 

948 for ql in range(n_qubits): 

949 for q in range(n_qubits): 

950 if q == ql: 

951 continue 

952 Gates.CRX( 

953 w[w_idx], 

954 wires=[n_qubits - ql - 1, (n_qubits - q - 1) % n_qubits], 

955 noise_params=noise_params, 

956 ) 

957 w_idx += 1 

958 

959 for q in range(n_qubits): 

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

961 w_idx += 1 

962 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

963 w_idx += 1 

964 

965 class Circuit_1(Circuit): 

966 @staticmethod 

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

968 """ 

969 Returns the number of parameters per layer for Circuit_1. 

970 

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

972 each qubit contributing 2 parameters. 

973 

974 Parameters 

975 ---------- 

976 n_qubits : int 

977 Number of qubits in the circuit 

978 

979 Returns 

980 ------- 

981 int 

982 Number of parameters per layer 

983 """ 

984 return n_qubits * 2 

985 

986 @staticmethod 

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

988 """ 

989 No controlled rotation gates available. Always None. 

990 

991 Parameters 

992 ---------- 

993 n_qubits : int 

994 Number of qubits in the circuit 

995 

996 Returns 

997 ------- 

998 Optional[np.ndarray] 

999 List of all controlled indices, or None if the circuit does not 

1000 contain controlled rotation gates. 

1001 """ 

1002 return None 

1003 

1004 @staticmethod 

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

1006 """ 

1007 Creates a Circuit1 ansatz. 

1008 

1009 Length of flattened vector must be n_qubits*2 

1010 

1011 Parameters 

1012 ---------- 

1013 w : np.ndarray 

1014 Weight vector of size n_qubits*2 

1015 n_qubits : int 

1016 Number of qubits 

1017 noise_params : Optional[Dict[str, float]], optional 

1018 Dictionary of noise parameters to apply to the gates 

1019 """ 

1020 w_idx = 0 

1021 for q in range(n_qubits): 

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

1023 w_idx += 1 

1024 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

1025 w_idx += 1 

1026 

1027 class Circuit_2(Circuit): 

1028 @staticmethod 

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

1030 """ 

1031 Returns the number of parameters per layer for Circuit_2. 

1032 

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

1034 each qubit contributing 2 parameters. 

1035 

1036 Parameters 

1037 ---------- 

1038 n_qubits : int 

1039 Number of qubits in the circuit 

1040 

1041 Returns 

1042 ------- 

1043 int 

1044 Number of parameters per layer 

1045 """ 

1046 return n_qubits * 2 

1047 

1048 @staticmethod 

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

1050 """ 

1051 No controlled rotation gates available. Always None. 

1052 

1053 Parameters 

1054 ---------- 

1055 n_qubits : int 

1056 Number of qubits in the circuit 

1057 

1058 Returns 

1059 ------- 

1060 Optional[np.ndarray] 

1061 List of all controlled indices, or None if the circuit does not 

1062 contain controlled rotation gates. 

1063 """ 

1064 return None 

1065 

1066 @staticmethod 

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

1068 """ 

1069 Creates a Circuit2 ansatz. 

1070 

1071 Length of flattened vector must be n_qubits*2 

1072 

1073 Parameters 

1074 ---------- 

1075 w : np.ndarray 

1076 Weight vector of size n_qubits*2 

1077 n_qubits : int 

1078 Number of qubits 

1079 noise_params : Optional[Dict[str, float]], optional 

1080 Dictionary of noise parameters to apply to the gates 

1081 """ 

1082 w_idx = 0 

1083 for q in range(n_qubits): 

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

1085 w_idx += 1 

1086 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

1087 w_idx += 1 

1088 

1089 if n_qubits > 1: 

1090 for q in range(n_qubits - 1): 

1091 Gates.CX( 

1092 wires=[n_qubits - q - 1, n_qubits - q - 2], 

1093 noise_params=noise_params, 

1094 ) 

1095 

1096 class Circuit_3(Circuit): 

1097 @staticmethod 

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

1099 """ 

1100 Calculates the number of parameters per layer for Circuit3. 

1101 

1102 The number of parameters per layer is given by the number of qubits, with 

1103 each qubit contributing 3 parameters. The last qubit only contributes 2 

1104 parameters because it is the target qubit for the controlled gates. 

1105 

1106 Parameters 

1107 ---------- 

1108 n_qubits : int 

1109 Number of qubits in the circuit 

1110 

1111 Returns 

1112 ------- 

1113 int 

1114 Number of parameters per layer 

1115 """ 

1116 return n_qubits * 3 - 1 

1117 

1118 @staticmethod 

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

1120 """ 

1121 No controlled rotation gates available. Always None. 

1122 

1123 Parameters 

1124 ---------- 

1125 n_qubits : int 

1126 Number of qubits in the circuit 

1127 

1128 Returns 

1129 ------- 

1130 Optional[np.ndarray] 

1131 List of all controlled indices, or None if the circuit does not 

1132 contain controlled rotation gates. 

1133 """ 

1134 return None 

1135 

1136 @staticmethod 

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

1138 """ 

1139 Creates a Circuit3 ansatz. 

1140 

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

1142 

1143 Parameters 

1144 ---------- 

1145 w : np.ndarray 

1146 Weight vector of size n_qubits*3-1 

1147 n_qubits : int 

1148 Number of qubits 

1149 noise_params : Optional[Dict[str, float]], optional 

1150 Dictionary of noise parameters to apply to the gates 

1151 """ 

1152 w_idx = 0 

1153 for q in range(n_qubits): 

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

1155 w_idx += 1 

1156 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

1157 w_idx += 1 

1158 

1159 if n_qubits > 1: 

1160 for q in range(n_qubits - 1): 

1161 Gates.CRZ( 

1162 w[w_idx], 

1163 wires=[n_qubits - q - 1, n_qubits - q - 2], 

1164 noise_params=noise_params, 

1165 ) 

1166 w_idx += 1 

1167 

1168 class Circuit_4(Circuit): 

1169 @staticmethod 

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

1171 """ 

1172 Returns the number of parameters per layer for the Circuit_4 ansatz. 

1173 

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

1175 

1176 Parameters 

1177 ---------- 

1178 n_qubits : int 

1179 Number of qubits in the circuit 

1180 

1181 Returns 

1182 ------- 

1183 int 

1184 Number of parameters per layer 

1185 """ 

1186 return n_qubits * 3 - 1 

1187 

1188 @staticmethod 

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

1190 """ 

1191 No controlled rotation gates available. Always None. 

1192 

1193 Parameters 

1194 ---------- 

1195 n_qubits : int 

1196 Number of qubits in the circuit 

1197 

1198 Returns 

1199 ------- 

1200 Optional[np.ndarray] 

1201 List of all controlled indices, or None if the circuit does not 

1202 contain controlled rotation gates. 

1203 """ 

1204 return None 

1205 

1206 @staticmethod 

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

1208 """ 

1209 Creates a Circuit4 ansatz. 

1210 

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

1212 

1213 Parameters 

1214 ---------- 

1215 w : np.ndarray 

1216 Weight vector of size n_qubits*3-1 

1217 n_qubits : int 

1218 Number of qubits 

1219 noise_params : Optional[Dict[str, float]], optional 

1220 Dictionary of noise parameters to apply to the gates 

1221 """ 

1222 w_idx = 0 

1223 for q in range(n_qubits): 

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

1225 w_idx += 1 

1226 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

1227 w_idx += 1 

1228 

1229 if n_qubits > 1: 

1230 for q in range(n_qubits - 1): 

1231 Gates.CRX( 

1232 w[w_idx], 

1233 wires=[n_qubits - q - 1, n_qubits - q - 2], 

1234 noise_params=noise_params, 

1235 ) 

1236 w_idx += 1 

1237 

1238 class Circuit_10(Circuit): 

1239 @staticmethod 

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

1241 """ 

1242 Returns the number of parameters per layer for the Circuit_10 ansatz. 

1243 

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

1245 

1246 Parameters 

1247 ---------- 

1248 n_qubits : int 

1249 Number of qubits in the circuit 

1250 

1251 Returns 

1252 ------- 

1253 int 

1254 Number of parameters per layer 

1255 """ 

1256 return n_qubits * 2 # constant gates not considered yet. has to be fixed 

1257 

1258 @staticmethod 

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

1260 """ 

1261 No controlled rotation gates available. Always None. 

1262 

1263 Parameters 

1264 ---------- 

1265 n_qubits : int 

1266 Number of qubits in the circuit 

1267 

1268 Returns 

1269 ------- 

1270 Optional[np.ndarray] 

1271 List of all controlled indices, or None if the circuit does not 

1272 contain controlled rotation gates. 

1273 """ 

1274 return None 

1275 

1276 @staticmethod 

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

1278 """ 

1279 Creates a Circuit10 ansatz. 

1280 

1281 Length of flattened vector must be n_qubits*2 

1282 

1283 Parameters 

1284 ---------- 

1285 w : np.ndarray 

1286 Weight vector of size n_qubits*2 

1287 n_qubits : int 

1288 Number of qubits 

1289 noise_params : Optional[Dict[str, float]], optional 

1290 Dictionary of noise parameters to apply to the gates 

1291 """ 

1292 w_idx = 0 

1293 # constant gates, independent of layers. has to be fixed 

1294 for q in range(n_qubits): 

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

1296 w_idx += 1 

1297 

1298 if n_qubits > 1: 

1299 for q in range(n_qubits - 1): 

1300 Gates.CZ( 

1301 wires=[ 

1302 (n_qubits - q - 2) % n_qubits, 

1303 (n_qubits - q - 1) % n_qubits, 

1304 ], 

1305 noise_params=noise_params, 

1306 ) 

1307 if n_qubits > 2: 

1308 Gates.CZ(wires=[n_qubits - 1, 0], noise_params=noise_params) 

1309 

1310 for q in range(n_qubits): 

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

1312 w_idx += 1 

1313 

1314 class Circuit_16(Circuit): 

1315 @staticmethod 

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

1317 """ 

1318 Returns the number of parameters per layer for the Circuit_16 ansatz. 

1319 

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

1321 

1322 Parameters 

1323 ---------- 

1324 n_qubits : int 

1325 Number of qubits in the circuit 

1326 

1327 Returns 

1328 ------- 

1329 int 

1330 Number of parameters per layer 

1331 """ 

1332 

1333 return n_qubits * 3 - 1 

1334 

1335 @staticmethod 

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

1337 """ 

1338 No controlled rotation gates available. Always None. 

1339 

1340 Parameters 

1341 ---------- 

1342 n_qubits : int 

1343 Number of qubits in the circuit 

1344 

1345 Returns 

1346 ------- 

1347 Optional[np.ndarray] 

1348 List of all controlled indices, or None if the circuit does not 

1349 contain controlled rotation gates. 

1350 """ 

1351 return None 

1352 

1353 @staticmethod 

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

1355 """ 

1356 Creates a Circuit16 ansatz. 

1357 

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

1359 

1360 Parameters 

1361 ---------- 

1362 w : np.ndarray 

1363 Weight vector of size n_qubits*3-1 

1364 n_qubits : int 

1365 Number of qubits 

1366 noise_params : Optional[Dict[str, float]], optional 

1367 Dictionary of noise parameters to apply to the gates 

1368 """ 

1369 w_idx = 0 

1370 for q in range(n_qubits): 

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

1372 w_idx += 1 

1373 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

1374 w_idx += 1 

1375 

1376 if n_qubits > 1: 

1377 for q in range(n_qubits // 2): 

1378 Gates.CRZ( 

1379 w[w_idx], 

1380 wires=[(2 * q + 1), (2 * q)], 

1381 noise_params=noise_params, 

1382 ) 

1383 w_idx += 1 

1384 

1385 for q in range((n_qubits - 1) // 2): 

1386 Gates.CRZ( 

1387 w[w_idx], 

1388 wires=[(2 * q + 2), (2 * q + 1)], 

1389 noise_params=noise_params, 

1390 ) 

1391 w_idx += 1 

1392 

1393 class Circuit_17(Circuit): 

1394 @staticmethod 

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

1396 """ 

1397 Returns the number of parameters per layer for the Circuit_17 ansatz. 

1398 

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

1400 

1401 Parameters 

1402 ---------- 

1403 n_qubits : int 

1404 Number of qubits in the circuit 

1405 

1406 Returns 

1407 ------- 

1408 int 

1409 Number of parameters per layer 

1410 """ 

1411 

1412 return n_qubits * 3 - 1 

1413 

1414 @staticmethod 

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

1416 """ 

1417 No controlled rotation gates available. Always None. 

1418 

1419 Parameters 

1420 ---------- 

1421 n_qubits : int 

1422 Number of qubits in the circuit 

1423 

1424 Returns 

1425 ------- 

1426 Optional[np.ndarray] 

1427 List of all controlled indices, or None if the circuit does not 

1428 contain controlled rotation gates. 

1429 """ 

1430 return None 

1431 

1432 @staticmethod 

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

1434 """ 

1435 Creates a Circuit17 ansatz. 

1436 

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

1438 

1439 Parameters 

1440 ---------- 

1441 w : np.ndarray 

1442 Weight vector of size n_qubits*3-1 

1443 n_qubits : int 

1444 Number of qubits 

1445 noise_params : Optional[Dict[str, float]], optional 

1446 Dictionary of noise parameters to apply to the gates 

1447 """ 

1448 w_idx = 0 

1449 for q in range(n_qubits): 

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

1451 w_idx += 1 

1452 Gates.RZ(w[w_idx], wires=q, noise_params=noise_params) 

1453 w_idx += 1 

1454 

1455 if n_qubits > 1: 

1456 for q in range(n_qubits // 2): 

1457 Gates.CRX( 

1458 w[w_idx], 

1459 wires=[(2 * q + 1), (2 * q)], 

1460 noise_params=noise_params, 

1461 ) 

1462 w_idx += 1 

1463 

1464 for q in range((n_qubits - 1) // 2): 

1465 Gates.CRX( 

1466 w[w_idx], 

1467 wires=[(2 * q + 2), (2 * q + 1)], 

1468 noise_params=noise_params, 

1469 ) 

1470 w_idx += 1 

1471 

1472 class Strongly_Entangling(Circuit): 

1473 @staticmethod 

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

1475 """ 

1476 Returns the number of parameters per layer for the 

1477 Strongly Entangling ansatz. 

1478 

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

1480 

1481 Parameters 

1482 ---------- 

1483 n_qubits : int 

1484 Number of qubits in the circuit 

1485 

1486 Returns 

1487 ------- 

1488 int 

1489 Number of parameters per layer 

1490 """ 

1491 if n_qubits < 2: 

1492 log.warning("Number of Qubits < 2, no entanglement available") 

1493 return n_qubits * 6 

1494 

1495 @staticmethod 

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

1497 """ 

1498 No controlled rotation gates available. Always None. 

1499 

1500 Parameters 

1501 ---------- 

1502 n_qubits : int 

1503 Number of qubits in the circuit 

1504 

1505 Returns 

1506 ------- 

1507 Optional[np.ndarray] 

1508 List of all controlled indices, or None if the circuit does not 

1509 contain controlled rotation gates. 

1510 """ 

1511 return None 

1512 

1513 @staticmethod 

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

1515 """ 

1516 Creates a Strongly Entangling ansatz. 

1517 

1518 Length of flattened vector must be n_qubits*6 

1519 

1520 Parameters 

1521 ---------- 

1522 w : np.ndarray 

1523 Weight vector of size n_qubits*6 

1524 n_qubits : int 

1525 Number of qubits 

1526 noise_params : Optional[Dict[str, float]], optional 

1527 Dictionary of noise parameters to apply to the gates 

1528 """ 

1529 w_idx = 0 

1530 for q in range(n_qubits): 

1531 Gates.Rot( 

1532 w[w_idx], 

1533 w[w_idx + 1], 

1534 w[w_idx + 2], 

1535 wires=q, 

1536 noise_params=noise_params, 

1537 ) 

1538 w_idx += 3 

1539 

1540 if n_qubits > 1: 

1541 for q in range(n_qubits): 

1542 Gates.CX(wires=[q, (q + 1) % n_qubits], noise_params=noise_params) 

1543 

1544 for q in range(n_qubits): 

1545 Gates.Rot( 

1546 w[w_idx], 

1547 w[w_idx + 1], 

1548 w[w_idx + 2], 

1549 wires=q, 

1550 noise_params=noise_params, 

1551 ) 

1552 w_idx += 3 

1553 

1554 if n_qubits > 1: 

1555 for q in range(n_qubits): 

1556 Gates.CX( 

1557 wires=[q, (q + n_qubits // 2) % n_qubits], 

1558 noise_params=noise_params, 

1559 ) 

1560 

1561 class No_Entangling(Circuit): 

1562 @staticmethod 

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

1564 """ 

1565 Returns the number of parameters per layer for the NoEntangling ansatz. 

1566 

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

1568 

1569 Parameters 

1570 ---------- 

1571 n_qubits : int 

1572 Number of qubits in the circuit 

1573 

1574 Returns 

1575 ------- 

1576 int 

1577 Number of parameters per layer 

1578 """ 

1579 return n_qubits * 3 

1580 

1581 @staticmethod 

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

1583 """ 

1584 No controlled rotation gates available. Always None. 

1585 

1586 Parameters 

1587 ---------- 

1588 n_qubits : int 

1589 Number of qubits in the circuit 

1590 

1591 Returns 

1592 ------- 

1593 Optional[np.ndarray] 

1594 List of all controlled indices, or None if the circuit does not 

1595 contain controlled rotation gates. 

1596 """ 

1597 return None 

1598 

1599 @staticmethod 

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

1601 """ 

1602 Creates a circuit without entangling, but with U3 gates on all qubits 

1603 

1604 Length of flattened vector must be n_qubits*3 

1605 

1606 Parameters 

1607 ---------- 

1608 w : np.ndarray 

1609 Weight vector of size n_qubits*3 

1610 n_qubits : int 

1611 Number of qubits 

1612 noise_params : Optional[Dict[str, float]], optional 

1613 Dictionary of noise parameters to apply to the gates 

1614 """ 

1615 w_idx = 0 

1616 for q in range(n_qubits): 

1617 Gates.Rot( 

1618 w[w_idx], 

1619 w[w_idx + 1], 

1620 w[w_idx + 2], 

1621 wires=q, 

1622 noise_params=noise_params, 

1623 ) 

1624 w_idx += 3