qoro-divi 0.3.3__py3-none-any.whl → 0.3.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of qoro-divi might be problematic. Click here for more details.

Files changed (74) hide show
  1. divi/__init__.py +1 -2
  2. divi/backends/__init__.py +7 -0
  3. divi/backends/_circuit_runner.py +46 -0
  4. divi/{parallel_simulator.py → backends/_parallel_simulator.py} +136 -53
  5. divi/backends/_qoro_service.py +531 -0
  6. divi/circuits/__init__.py +5 -0
  7. divi/circuits/_core.py +226 -0
  8. divi/{qasm.py → circuits/qasm.py} +21 -2
  9. divi/{exp → extern}/cirq/_validator.py +9 -7
  10. divi/qprog/__init__.py +18 -5
  11. divi/qprog/algorithms/__init__.py +14 -0
  12. divi/qprog/algorithms/_ansatze.py +311 -0
  13. divi/qprog/{_qaoa.py → algorithms/_qaoa.py} +69 -41
  14. divi/qprog/{_vqe.py → algorithms/_vqe.py} +79 -135
  15. divi/qprog/batch.py +239 -55
  16. divi/qprog/exceptions.py +9 -0
  17. divi/qprog/optimizers.py +219 -18
  18. divi/qprog/quantum_program.py +389 -57
  19. divi/qprog/workflows/__init__.py +10 -0
  20. divi/qprog/{_graph_partitioning.py → workflows/_graph_partitioning.py} +3 -34
  21. divi/qprog/{_qubo_partitioning.py → workflows/_qubo_partitioning.py} +42 -25
  22. divi/qprog/{_vqe_sweep.py → workflows/_vqe_sweep.py} +59 -26
  23. divi/reporting/__init__.py +7 -0
  24. divi/reporting/_pbar.py +112 -0
  25. divi/{qlogger.py → reporting/_qlogger.py} +37 -2
  26. divi/{reporter.py → reporting/_reporter.py} +8 -14
  27. divi/utils.py +49 -10
  28. {qoro_divi-0.3.3.dist-info → qoro_divi-0.3.5.dist-info}/METADATA +2 -1
  29. qoro_divi-0.3.5.dist-info/RECORD +69 -0
  30. divi/_pbar.py +0 -70
  31. divi/circuits.py +0 -139
  32. divi/interfaces.py +0 -25
  33. divi/qoro_service.py +0 -425
  34. qoro_divi-0.3.3.dist-info/RECORD +0 -62
  35. /divi/{qpu_system.py → backends/_qpu_system.py} +0 -0
  36. /divi/{qem.py → circuits/qem.py} +0 -0
  37. /divi/{exp → extern}/cirq/__init__.py +0 -0
  38. /divi/{exp → extern}/cirq/_lexer.py +0 -0
  39. /divi/{exp → extern}/cirq/_parser.py +0 -0
  40. /divi/{exp → extern}/cirq/_qasm_export.py +0 -0
  41. /divi/{exp → extern}/cirq/_qasm_import.py +0 -0
  42. /divi/{exp → extern}/cirq/exception.py +0 -0
  43. /divi/{exp → extern}/scipy/_cobyla.py +0 -0
  44. /divi/{exp → extern}/scipy/pyprima/LICENCE.txt +0 -0
  45. /divi/{exp → extern}/scipy/pyprima/__init__.py +0 -0
  46. /divi/{exp → extern}/scipy/pyprima/cobyla/__init__.py +0 -0
  47. /divi/{exp → extern}/scipy/pyprima/cobyla/cobyla.py +0 -0
  48. /divi/{exp → extern}/scipy/pyprima/cobyla/cobylb.py +0 -0
  49. /divi/{exp → extern}/scipy/pyprima/cobyla/geometry.py +0 -0
  50. /divi/{exp → extern}/scipy/pyprima/cobyla/initialize.py +0 -0
  51. /divi/{exp → extern}/scipy/pyprima/cobyla/trustregion.py +0 -0
  52. /divi/{exp → extern}/scipy/pyprima/cobyla/update.py +0 -0
  53. /divi/{exp → extern}/scipy/pyprima/common/__init__.py +0 -0
  54. /divi/{exp → extern}/scipy/pyprima/common/_bounds.py +0 -0
  55. /divi/{exp → extern}/scipy/pyprima/common/_linear_constraints.py +0 -0
  56. /divi/{exp → extern}/scipy/pyprima/common/_nonlinear_constraints.py +0 -0
  57. /divi/{exp → extern}/scipy/pyprima/common/_project.py +0 -0
  58. /divi/{exp → extern}/scipy/pyprima/common/checkbreak.py +0 -0
  59. /divi/{exp → extern}/scipy/pyprima/common/consts.py +0 -0
  60. /divi/{exp → extern}/scipy/pyprima/common/evaluate.py +0 -0
  61. /divi/{exp → extern}/scipy/pyprima/common/history.py +0 -0
  62. /divi/{exp → extern}/scipy/pyprima/common/infos.py +0 -0
  63. /divi/{exp → extern}/scipy/pyprima/common/linalg.py +0 -0
  64. /divi/{exp → extern}/scipy/pyprima/common/message.py +0 -0
  65. /divi/{exp → extern}/scipy/pyprima/common/powalg.py +0 -0
  66. /divi/{exp → extern}/scipy/pyprima/common/preproc.py +0 -0
  67. /divi/{exp → extern}/scipy/pyprima/common/present.py +0 -0
  68. /divi/{exp → extern}/scipy/pyprima/common/ratio.py +0 -0
  69. /divi/{exp → extern}/scipy/pyprima/common/redrho.py +0 -0
  70. /divi/{exp → extern}/scipy/pyprima/common/selectx.py +0 -0
  71. {qoro_divi-0.3.3.dist-info → qoro_divi-0.3.5.dist-info}/LICENSE +0 -0
  72. {qoro_divi-0.3.3.dist-info → qoro_divi-0.3.5.dist-info}/LICENSES/.license-header +0 -0
  73. {qoro_divi-0.3.3.dist-info → qoro_divi-0.3.5.dist-info}/LICENSES/Apache-2.0.txt +0 -0
  74. {qoro_divi-0.3.3.dist-info → qoro_divi-0.3.5.dist-info}/WHEEL +0 -0
@@ -33,6 +33,16 @@ QUBOProblemTypes = list | np.ndarray | sps.spmatrix | QuadraticProgram
33
33
 
34
34
 
35
35
  def draw_graph_solution_nodes(main_graph, partition_nodes):
36
+ """
37
+ Visualize a graph with solution nodes highlighted.
38
+
39
+ Draws the graph with nodes colored to distinguish solution nodes (red) from
40
+ other nodes (light blue).
41
+
42
+ Args:
43
+ main_graph: NetworkX graph to visualize.
44
+ partition_nodes: Collection of node indices that are part of the solution.
45
+ """
36
46
  # Create a dictionary for node colors
37
47
  node_colors = [
38
48
  "red" if node in partition_nodes else "lightblue" for node in main_graph.nodes()
@@ -224,14 +234,13 @@ class QAOA(QuantumProgram):
224
234
  self.n_layers = n_layers
225
235
  self.max_iterations = max_iterations
226
236
  self.current_iteration = 0
227
- self.n_params = 2
237
+ self._n_params = 2
228
238
  self._is_compute_probabilites = False
229
239
  self.optimizer = optimizer if optimizer is not None else MonteCarloOptimizer()
230
240
 
231
- # Shared Variables
232
- self.probs = kwargs.pop("probs", {})
233
- self._solution_nodes = kwargs.pop("solution_nodes", [])
234
- self._solution_bitstring = kwargs.pop("solution_bitstring", [])
241
+ self._probs = {}
242
+ self._solution_nodes = []
243
+ self._solution_bitstring = []
235
244
 
236
245
  (
237
246
  self.cost_hamiltonian,
@@ -256,18 +265,41 @@ class QAOA(QuantumProgram):
256
265
  self.loss_constant = 0.0
257
266
 
258
267
  kwargs.pop("is_constrained", None)
259
- super().__init__(has_final_computation=True, **kwargs)
268
+ super().__init__(**kwargs)
260
269
 
261
270
  self._meta_circuits = self._create_meta_circuits_dict()
262
271
 
263
272
  @property
264
273
  def solution(self):
274
+ """
275
+ Get the solution found by QAOA optimization.
276
+
277
+ Returns:
278
+ list: For graph problems, returns a list of selected node indices.
279
+ For QUBO problems, returns a list/array of binary values.
280
+ """
265
281
  return (
266
282
  self._solution_nodes
267
283
  if self.graph_problem is not None
268
284
  else self._solution_bitstring
269
285
  )
270
286
 
287
+ @property
288
+ def probs(self) -> dict:
289
+ """
290
+ Get a copy of the probability distributions from final measurements.
291
+
292
+ Returns:
293
+ dict: Copy of the probability distributions dictionary. Keys are
294
+ circuit tags, values are probability distributions.
295
+ """
296
+ return self._probs.copy()
297
+
298
+ @probs.setter
299
+ def probs(self, value: dict):
300
+ """Set the probability distributions."""
301
+ self._probs = value
302
+
271
303
  def _create_meta_circuits_dict(self) -> dict[str, MetaCircuit]:
272
304
  """
273
305
  Generate the meta circuits for the QAOA problem.
@@ -341,7 +373,7 @@ class QAOA(QuantumProgram):
341
373
  params_group, tag_prefix=f"{p}"
342
374
  )
343
375
 
344
- self.circuits.append(circuit)
376
+ self._circuits.append(circuit)
345
377
 
346
378
  def _post_process_results(self, results):
347
379
  """
@@ -365,19 +397,26 @@ class QAOA(QuantumProgram):
365
397
  return losses
366
398
 
367
399
  def _run_final_measurement(self):
400
+ """
401
+ Execute final measurement circuits to obtain probability distributions.
402
+
403
+ Runs the optimized circuit with final parameters to get the full probability
404
+ distribution over all measurement outcomes, which is used to extract the
405
+ solution bitstring.
406
+ """
368
407
  self._is_compute_probabilites = True
369
408
 
370
- self._curr_params = np.array(self.final_params)
409
+ self._curr_params = np.array(self._final_params)
371
410
 
372
- self.circuits[:] = []
411
+ self._circuits[:] = []
373
412
 
374
413
  self._generate_circuits()
375
414
 
376
- self.probs.update(self._dispatch_circuits_and_process_results())
415
+ self._probs.update(self._dispatch_circuits_and_process_results())
377
416
 
378
417
  self._is_compute_probabilites = False
379
418
 
380
- def compute_final_solution(self):
419
+ def _perform_final_computation(self):
381
420
  """
382
421
  Computes and extracts the final solution from the QAOA optimization process.
383
422
  This method performs the following steps:
@@ -397,37 +436,14 @@ class QAOA(QuantumProgram):
397
436
 
398
437
  self.reporter.info(message="🏁 Computing Final Solution 🏁")
399
438
 
400
- # Convert losses dict to list to apply ordinal operations
401
- final_losses_list = list(self.losses[-1].values())
402
-
403
- # Get the index of the smallest loss in the last operation
404
- best_solution_idx = min(
405
- range(len(final_losses_list)),
406
- key=lambda x: final_losses_list.__getitem__(x),
407
- )
408
-
409
- # Insert the measurement circuit here
410
439
  self._run_final_measurement()
411
440
 
412
- # Find the key matching the best_solution_idx with possible metadata in between
413
- pattern = re.compile(rf"^{best_solution_idx}(?:_[^_]*)*_0$")
414
- matching_keys = [k for k in self.probs.keys() if pattern.match(k)]
415
-
416
- # Some minor sanity checks
417
- if len(matching_keys) == 0:
418
- raise RuntimeError("No matching key found.")
419
- if len(matching_keys) > 1:
420
- raise RuntimeError(f"More than one matching key found.")
441
+ final_measurement_probs = next(iter(self._probs.values()))
421
442
 
422
- best_solution_key = matching_keys[0]
423
- # Retrieve the probability distribution dictionary of the best solution
424
- best_solution_probs = self.probs[best_solution_key]
425
-
426
- # Retrieve the bitstring with the actual best solution
427
443
  # Reverse to account for the endianness difference
428
- best_solution_bitstring = max(best_solution_probs, key=best_solution_probs.get)[
429
- ::-1
430
- ]
444
+ best_solution_bitstring = max(
445
+ final_measurement_probs, key=final_measurement_probs.get
446
+ )[::-1]
431
447
 
432
448
  if isinstance(self.problem, QUBOProblemTypes):
433
449
  self._solution_bitstring[:] = np.fromiter(
@@ -439,17 +455,29 @@ class QAOA(QuantumProgram):
439
455
  m.start() for m in re.finditer("1", best_solution_bitstring)
440
456
  ]
441
457
 
442
- self.reporter.info(message="Computed Final Solution!")
443
-
444
458
  return self._total_circuit_count, self._total_run_time
445
459
 
446
460
  def draw_solution(self):
461
+ """
462
+ Visualize the solution found by QAOA for graph problems.
463
+
464
+ Draws the graph with solution nodes highlighted in red and other nodes
465
+ in light blue. If the solution hasn't been computed yet, it will be
466
+ calculated first.
467
+
468
+ Raises:
469
+ RuntimeError: If called on a QUBO problem instead of a graph problem.
470
+
471
+ Note:
472
+ This method only works for graph problems. For QUBO problems, access
473
+ the solution directly via the `solution` property.
474
+ """
447
475
  if self.graph_problem is None:
448
476
  raise RuntimeError(
449
477
  "The problem is not a graph problem. Cannot draw solution."
450
478
  )
451
479
 
452
480
  if not self._solution_nodes:
453
- self.compute_final_solution()
481
+ self._perform_final_computation()
454
482
 
455
483
  draw_graph_solution_nodes(self.problem, self._solution_nodes)
@@ -2,7 +2,6 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- from enum import Enum
6
5
  from warnings import warn
7
6
 
8
7
  import pennylane as qml
@@ -10,38 +9,10 @@ import sympy as sp
10
9
 
11
10
  from divi.circuits import MetaCircuit
12
11
  from divi.qprog import QuantumProgram
12
+ from divi.qprog.algorithms._ansatze import Ansatz, HartreeFockAnsatz
13
13
  from divi.qprog.optimizers import MonteCarloOptimizer, Optimizer
14
14
 
15
15
 
16
- class VQEAnsatz(Enum):
17
- UCCSD = "UCCSD"
18
- RY = "RY"
19
- RYRZ = "RYRZ"
20
- HW_EFFICIENT = "HW_EFFICIENT"
21
- QAOA = "QAOA"
22
- HARTREE_FOCK = "HF"
23
-
24
- def describe(self):
25
- return self.name, self.value
26
-
27
- def n_params(self, n_qubits, **kwargs):
28
- if self in (VQEAnsatz.UCCSD, VQEAnsatz.HARTREE_FOCK):
29
- singles, doubles = qml.qchem.excitations(
30
- kwargs.pop("n_electrons"), n_qubits
31
- )
32
- s_wires, d_wires = qml.qchem.excitations_to_wires(singles, doubles)
33
-
34
- return len(s_wires) + len(d_wires)
35
- elif self == VQEAnsatz.RY:
36
- return n_qubits
37
- elif self == VQEAnsatz.RYRZ:
38
- return 2 * n_qubits
39
- elif self == VQEAnsatz.HW_EFFICIENT:
40
- raise NotImplementedError
41
- elif self == VQEAnsatz.QAOA:
42
- return qml.QAOAEmbedding.shape(n_layers=1, n_wires=n_qubits)[1]
43
-
44
-
45
16
  class VQE(QuantumProgram):
46
17
  def __init__(
47
18
  self,
@@ -49,7 +20,7 @@ class VQE(QuantumProgram):
49
20
  molecule: qml.qchem.Molecule | None = None,
50
21
  n_electrons: int | None = None,
51
22
  n_layers: int = 1,
52
- ansatz=VQEAnsatz.HARTREE_FOCK,
23
+ ansatz: Ansatz | None = None,
53
24
  optimizer: Optimizer | None = None,
54
25
  max_iterations=10,
55
26
  **kwargs,
@@ -58,19 +29,26 @@ class VQE(QuantumProgram):
58
29
  Initialize the VQE problem.
59
30
 
60
31
  Args:
61
- hamiltonain (pennylane.operation.Operator, optional): A Hamiltonian representing the problem.
62
- molecule (pennylane.qchem.Molecule, optional): The molecule representing the problem.
63
- n_electrons (int, optional): Number of electrons associated with the Hamiltonian.
64
- Only needs to be provided when a Hamiltonian is given.
65
- ansatz (VQEAnsatz): The ansatz to use for the VQE problem
66
- optimizer (Optimizers): The optimizer to use.
67
- max_iterations (int): Maximum number of iteration optimizers.
32
+ hamiltonian (pennylane.operation.Operator, optional): A Hamiltonian
33
+ representing the problem.
34
+ molecule (pennylane.qchem.Molecule, optional): The molecule representing
35
+ the problem.
36
+ n_electrons (int, optional): Number of electrons associated with the
37
+ Hamiltonian. Only needs to be provided when a Hamiltonian is given.
38
+ n_layers (int, optional): Number of ansatz layers. Defaults to 1.
39
+ ansatz (Ansatz, optional): The ansatz to use for the VQE problem.
40
+ Defaults to HartreeFockAnsatz.
41
+ optimizer (Optimizer, optional): The optimizer to use. Defaults to
42
+ MonteCarloOptimizer.
43
+ max_iterations (int, optional): Maximum number of optimization iterations.
44
+ Defaults to 10.
45
+ **kwargs: Additional keyword arguments passed to the parent QuantumProgram.
68
46
  """
69
47
 
70
48
  # Local Variables
49
+ self.ansatz = HartreeFockAnsatz() if ansatz is None else ansatz
71
50
  self.n_layers = n_layers
72
51
  self.results = {}
73
- self.ansatz = ansatz
74
52
  self.max_iterations = max_iterations
75
53
  self.current_iteration = 0
76
54
 
@@ -84,20 +62,43 @@ class VQE(QuantumProgram):
84
62
 
85
63
  self._meta_circuits = self._create_meta_circuits_dict()
86
64
 
65
+ @property
66
+ def n_params(self):
67
+ """
68
+ Get the total number of parameters for the VQE ansatz.
69
+
70
+ Returns:
71
+ int: Total number of parameters (n_params_per_layer * n_layers).
72
+ """
73
+ return (
74
+ self.ansatz.n_params_per_layer(self.n_qubits, n_electrons=self.n_electrons)
75
+ * self.n_layers
76
+ )
77
+
87
78
  def _process_problem_input(self, hamiltonian, molecule, n_electrons):
79
+ """
80
+ Process and validate the VQE problem input.
81
+
82
+ Handles both Hamiltonian-based and molecule-based problem specifications,
83
+ extracting the necessary information (n_qubits, n_electrons, hamiltonian).
84
+
85
+ Args:
86
+ hamiltonian: PennyLane Hamiltonian operator or None.
87
+ molecule: PennyLane Molecule object or None.
88
+ n_electrons: Number of electrons or None.
89
+
90
+ Raises:
91
+ ValueError: If neither hamiltonian nor molecule is provided.
92
+ UserWarning: If n_electrons conflicts with the molecule's electron count.
93
+ """
88
94
  if hamiltonian is None and molecule is None:
89
95
  raise ValueError(
90
96
  "Either one of `molecule` and `hamiltonian` must be provided."
91
97
  )
92
98
 
93
99
  if hamiltonian is not None:
94
- if not isinstance(n_electrons, int) or n_electrons < 0:
95
- raise ValueError(
96
- f"`n_electrons` is expected to be a non-negative integer. Got {n_electrons}."
97
- )
98
-
99
- self.n_electrons = n_electrons
100
100
  self.n_qubits = len(hamiltonian.wires)
101
+ self.n_electrons = n_electrons
101
102
 
102
103
  if molecule is not None:
103
104
  self.molecule = molecule
@@ -112,10 +113,6 @@ class VQE(QuantumProgram):
112
113
  UserWarning,
113
114
  )
114
115
 
115
- self.n_params = self.ansatz.n_params(
116
- self.n_qubits, n_electrons=self.n_electrons
117
- )
118
-
119
116
  self.cost_hamiltonian = self._clean_hamiltonian(hamiltonian)
120
117
 
121
118
  def _clean_hamiltonian(
@@ -148,9 +145,17 @@ class VQE(QuantumProgram):
148
145
  return hamiltonian.simplify()
149
146
 
150
147
  def _create_meta_circuits_dict(self) -> dict[str, MetaCircuit]:
151
- weights_syms = sp.symarray("w", (self.n_layers, self.n_params))
148
+ weights_syms = sp.symarray(
149
+ "w",
150
+ (
151
+ self.n_layers,
152
+ self.ansatz.n_params_per_layer(
153
+ self.n_qubits, n_electrons=self.n_electrons
154
+ ),
155
+ ),
156
+ )
152
157
 
153
- def _prepare_circuit(ansatz, hamiltonian, params):
158
+ def _prepare_circuit(hamiltonian, params):
154
159
  """
155
160
  Prepare the circuit for the VQE problem.
156
161
  Args:
@@ -158,7 +163,12 @@ class VQE(QuantumProgram):
158
163
  hamiltonian (qml.Hamiltonian): The Hamiltonian to use
159
164
  params (list): The parameters to use for the ansatz
160
165
  """
161
- self._set_ansatz(ansatz, params)
166
+ self.ansatz.build(
167
+ params,
168
+ n_qubits=self.n_qubits,
169
+ n_layers=self.n_layers,
170
+ n_electrons=self.n_electrons,
171
+ )
162
172
 
163
173
  # Even though in principle we want to sample from a state,
164
174
  # we are applying an `expval` operation here to make it compatible
@@ -169,93 +179,12 @@ class VQE(QuantumProgram):
169
179
  return {
170
180
  "cost_circuit": self._meta_circuit_factory(
171
181
  qml.tape.make_qscript(_prepare_circuit)(
172
- self.ansatz, self.cost_hamiltonian, weights_syms
182
+ self.cost_hamiltonian, weights_syms
173
183
  ),
174
184
  symbols=weights_syms.flatten(),
175
185
  )
176
186
  }
177
187
 
178
- def _set_ansatz(self, ansatz: VQEAnsatz, params):
179
- """
180
- Set the ansatz for the VQE problem.
181
- Args:
182
- ansatz (Ansatze): The ansatz to use
183
- params (list): The parameters to use for the ansatz
184
- n_layers (int): The number of layers to use for the ansatz
185
- """
186
-
187
- def _add_hw_efficient_ansatz(params):
188
- raise NotImplementedError
189
-
190
- def _add_qaoa_ansatz(params):
191
- # This infers layers automatically from the parameters shape
192
- qml.QAOAEmbedding(
193
- features=[],
194
- weights=params.reshape(self.n_layers, -1),
195
- wires=range(self.n_qubits),
196
- )
197
-
198
- def _add_ry_ansatz(params):
199
- qml.layer(
200
- qml.AngleEmbedding,
201
- self.n_layers,
202
- params.reshape(self.n_layers, -1),
203
- wires=range(self.n_qubits),
204
- rotation="Y",
205
- )
206
-
207
- def _add_ryrz_ansatz(params):
208
- def _ryrz(params, wires):
209
- ry_rots, rz_rots = params.reshape(2, -1)
210
- qml.AngleEmbedding(ry_rots, wires=wires, rotation="Y")
211
- qml.AngleEmbedding(rz_rots, wires=wires, rotation="Z")
212
-
213
- qml.layer(
214
- _ryrz,
215
- self.n_layers,
216
- params.reshape(self.n_layers, -1),
217
- wires=range(self.n_qubits),
218
- )
219
-
220
- def _add_uccsd_ansatz(params):
221
- hf_state = qml.qchem.hf_state(self.n_electrons, self.n_qubits)
222
-
223
- singles, doubles = qml.qchem.excitations(self.n_electrons, self.n_qubits)
224
- s_wires, d_wires = qml.qchem.excitations_to_wires(singles, doubles)
225
-
226
- qml.UCCSD(
227
- params.reshape(self.n_layers, -1),
228
- wires=range(self.n_qubits),
229
- s_wires=s_wires,
230
- d_wires=d_wires,
231
- init_state=hf_state,
232
- n_repeats=self.n_layers,
233
- )
234
-
235
- def _add_hartree_fock_ansatz(params):
236
- singles, doubles = qml.qchem.excitations(self.n_electrons, self.n_qubits)
237
- hf_state = qml.qchem.hf_state(self.n_electrons, self.n_qubits)
238
-
239
- qml.layer(
240
- qml.AllSinglesDoubles,
241
- self.n_layers,
242
- params.reshape(self.n_layers, -1),
243
- wires=range(self.n_qubits),
244
- hf_state=hf_state,
245
- singles=singles,
246
- doubles=doubles,
247
- )
248
-
249
- # Reset the BasisState operations after the first layer
250
- # for behaviour similar to UCCSD ansatz
251
- for op in qml.QueuingManager.active_context().queue[1:]:
252
- op._hyperparameters["hf_state"] = 0
253
-
254
- if ansatz in VQEAnsatz:
255
- locals()[f"_add_{ansatz.name.lower()}_ansatz"](params)
256
- else:
257
- raise ValueError(f"Invalid Ansatz Value. Got {ansatz}.")
258
-
259
188
  def _generate_circuits(self):
260
189
  """
261
190
  Generate the circuits for the VQE problem.
@@ -274,9 +203,24 @@ class VQE(QuantumProgram):
274
203
  "cost_circuit"
275
204
  ].initialize_circuit_from_params(params_group, tag_prefix=f"{p}")
276
205
 
277
- self.circuits.append(circuit)
206
+ self._circuits.append(circuit)
278
207
 
279
208
  def _run_optimization_circuits(self, store_data, data_file):
209
+ """
210
+ Execute the circuits for the current optimization iteration.
211
+
212
+ Validates that the Hamiltonian is properly set before running circuits.
213
+
214
+ Args:
215
+ store_data (bool): Whether to save iteration data.
216
+ data_file (str): Path to file for saving data.
217
+
218
+ Returns:
219
+ dict: Loss values for each parameter set.
220
+
221
+ Raises:
222
+ RuntimeError: If the cost Hamiltonian is not set or empty.
223
+ """
280
224
  if self.cost_hamiltonian is None or len(self.cost_hamiltonian) == 0:
281
225
  raise RuntimeError(
282
226
  "Hamiltonian operators must be generated before running the VQE"