qoro-divi 0.3.4__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.
- divi/backends/_circuit_runner.py +21 -0
- divi/backends/_parallel_simulator.py +132 -50
- divi/backends/_qoro_service.py +239 -132
- divi/circuits/_core.py +101 -0
- divi/circuits/qasm.py +19 -0
- divi/qprog/algorithms/_ansatze.py +96 -0
- divi/qprog/algorithms/_qaoa.py +68 -40
- divi/qprog/algorithms/_vqe.py +51 -8
- divi/qprog/batch.py +237 -51
- divi/qprog/exceptions.py +9 -0
- divi/qprog/optimizers.py +218 -16
- divi/qprog/quantum_program.py +375 -50
- divi/qprog/workflows/_graph_partitioning.py +1 -32
- divi/qprog/workflows/_qubo_partitioning.py +40 -23
- divi/qprog/workflows/_vqe_sweep.py +30 -9
- divi/reporting/_pbar.py +51 -9
- divi/reporting/_qlogger.py +35 -1
- divi/reporting/_reporter.py +8 -14
- divi/utils.py +35 -4
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.3.5.dist-info}/METADATA +2 -1
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.3.5.dist-info}/RECORD +25 -24
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.3.5.dist-info}/LICENSE +0 -0
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.3.5.dist-info}/LICENSES/.license-header +0 -0
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.3.5.dist-info}/LICENSES/Apache-2.0.txt +0 -0
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.3.5.dist-info}/WHEEL +0 -0
|
@@ -143,11 +143,37 @@ class GenericLayerAnsatz(Ansatz):
|
|
|
143
143
|
|
|
144
144
|
|
|
145
145
|
class QAOAAnsatz(Ansatz):
|
|
146
|
+
"""
|
|
147
|
+
QAOA-style ansatz using PennyLane's QAOAEmbedding.
|
|
148
|
+
|
|
149
|
+
Implements a parameterized ansatz based on the Quantum Approximate Optimization
|
|
150
|
+
Algorithm structure, alternating between problem and mixer Hamiltonians.
|
|
151
|
+
"""
|
|
152
|
+
|
|
146
153
|
@staticmethod
|
|
147
154
|
def n_params_per_layer(n_qubits: int, **kwargs) -> int:
|
|
155
|
+
"""
|
|
156
|
+
Calculate the number of parameters per layer for QAOA ansatz.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
n_qubits (int): Number of qubits in the circuit.
|
|
160
|
+
**kwargs: Additional unused arguments.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
int: Number of parameters needed per layer.
|
|
164
|
+
"""
|
|
148
165
|
return qml.QAOAEmbedding.shape(n_layers=1, n_wires=n_qubits)[1]
|
|
149
166
|
|
|
150
167
|
def build(self, params, n_qubits: int, n_layers: int, **kwargs):
|
|
168
|
+
"""
|
|
169
|
+
Build the QAOA ansatz circuit.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
params: Parameter array to use for the ansatz.
|
|
173
|
+
n_qubits (int): Number of qubits.
|
|
174
|
+
n_layers (int): Number of QAOA layers.
|
|
175
|
+
**kwargs: Additional unused arguments.
|
|
176
|
+
"""
|
|
151
177
|
qml.QAOAEmbedding(
|
|
152
178
|
features=[],
|
|
153
179
|
weights=params.reshape(n_layers, -1),
|
|
@@ -156,11 +182,23 @@ class QAOAAnsatz(Ansatz):
|
|
|
156
182
|
|
|
157
183
|
|
|
158
184
|
class HardwareEfficientAnsatz(Ansatz):
|
|
185
|
+
"""
|
|
186
|
+
Hardware-efficient ansatz (not yet implemented).
|
|
187
|
+
|
|
188
|
+
This ansatz is designed to be easily implementable on near-term quantum hardware,
|
|
189
|
+
typically using native gate sets and connectivity patterns.
|
|
190
|
+
|
|
191
|
+
Note:
|
|
192
|
+
This class is a placeholder for future implementation.
|
|
193
|
+
"""
|
|
194
|
+
|
|
159
195
|
@staticmethod
|
|
160
196
|
def n_params_per_layer(n_qubits: int, **kwargs) -> int:
|
|
197
|
+
"""Not yet implemented."""
|
|
161
198
|
raise NotImplementedError("HardwareEfficientAnsatz is not yet implemented.")
|
|
162
199
|
|
|
163
200
|
def build(self, params, n_qubits: int, n_layers: int, **kwargs) -> None:
|
|
201
|
+
"""Not yet implemented."""
|
|
164
202
|
raise NotImplementedError("HardwareEfficientAnsatz is not yet implemented.")
|
|
165
203
|
|
|
166
204
|
|
|
@@ -168,13 +206,42 @@ class HardwareEfficientAnsatz(Ansatz):
|
|
|
168
206
|
|
|
169
207
|
|
|
170
208
|
class UCCSDAnsatz(Ansatz):
|
|
209
|
+
"""
|
|
210
|
+
Unitary Coupled Cluster Singles and Doubles (UCCSD) ansatz.
|
|
211
|
+
|
|
212
|
+
This ansatz is specifically designed for quantum chemistry calculations,
|
|
213
|
+
implementing the UCCSD approximation which includes all single and double
|
|
214
|
+
electron excitations from a reference state.
|
|
215
|
+
"""
|
|
216
|
+
|
|
171
217
|
@staticmethod
|
|
172
218
|
def n_params_per_layer(n_qubits: int, n_electrons: int, **kwargs) -> int:
|
|
219
|
+
"""
|
|
220
|
+
Calculate the number of parameters per layer for UCCSD ansatz.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
n_qubits (int): Number of qubits in the circuit.
|
|
224
|
+
n_electrons (int): Number of electrons in the system.
|
|
225
|
+
**kwargs: Additional unused arguments.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
int: Number of parameters (number of single + double excitations).
|
|
229
|
+
"""
|
|
173
230
|
singles, doubles = qml.qchem.excitations(n_electrons, n_qubits)
|
|
174
231
|
s_wires, d_wires = qml.qchem.excitations_to_wires(singles, doubles)
|
|
175
232
|
return len(s_wires) + len(d_wires)
|
|
176
233
|
|
|
177
234
|
def build(self, params, n_qubits: int, n_layers: int, n_electrons: int, **kwargs):
|
|
235
|
+
"""
|
|
236
|
+
Build the UCCSD ansatz circuit.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
params: Parameter array for excitation amplitudes.
|
|
240
|
+
n_qubits (int): Number of qubits.
|
|
241
|
+
n_layers (int): Number of UCCSD layers (repeats).
|
|
242
|
+
n_electrons (int): Number of electrons in the system.
|
|
243
|
+
**kwargs: Additional unused arguments.
|
|
244
|
+
"""
|
|
178
245
|
singles, doubles = qml.qchem.excitations(n_electrons, n_qubits)
|
|
179
246
|
s_wires, d_wires = qml.qchem.excitations_to_wires(singles, doubles)
|
|
180
247
|
hf_state = qml.qchem.hf_state(n_electrons, n_qubits)
|
|
@@ -190,12 +257,41 @@ class UCCSDAnsatz(Ansatz):
|
|
|
190
257
|
|
|
191
258
|
|
|
192
259
|
class HartreeFockAnsatz(Ansatz):
|
|
260
|
+
"""
|
|
261
|
+
Hartree-Fock-based ansatz for quantum chemistry.
|
|
262
|
+
|
|
263
|
+
This ansatz prepares the Hartree-Fock reference state and applies
|
|
264
|
+
parameterized single and double excitation gates. It's a simplified
|
|
265
|
+
alternative to UCCSD, often used as a starting point for VQE calculations.
|
|
266
|
+
"""
|
|
267
|
+
|
|
193
268
|
@staticmethod
|
|
194
269
|
def n_params_per_layer(n_qubits: int, n_electrons: int, **kwargs) -> int:
|
|
270
|
+
"""
|
|
271
|
+
Calculate the number of parameters per layer for Hartree-Fock ansatz.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
n_qubits (int): Number of qubits in the circuit.
|
|
275
|
+
n_electrons (int): Number of electrons in the system.
|
|
276
|
+
**kwargs: Additional unused arguments.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
int: Number of parameters (number of single + double excitations).
|
|
280
|
+
"""
|
|
195
281
|
singles, doubles = qml.qchem.excitations(n_electrons, n_qubits)
|
|
196
282
|
return len(singles) + len(doubles)
|
|
197
283
|
|
|
198
284
|
def build(self, params, n_qubits: int, n_layers: int, n_electrons: int, **kwargs):
|
|
285
|
+
"""
|
|
286
|
+
Build the Hartree-Fock ansatz circuit.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
params: Parameter array for excitation amplitudes.
|
|
290
|
+
n_qubits (int): Number of qubits.
|
|
291
|
+
n_layers (int): Number of ansatz layers.
|
|
292
|
+
n_electrons (int): Number of electrons in the system.
|
|
293
|
+
**kwargs: Additional unused arguments.
|
|
294
|
+
"""
|
|
199
295
|
singles, doubles = qml.qchem.excitations(n_electrons, n_qubits)
|
|
200
296
|
hf_state = qml.qchem.hf_state(n_electrons, n_qubits)
|
|
201
297
|
|
divi/qprog/algorithms/_qaoa.py
CHANGED
|
@@ -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()
|
|
@@ -228,10 +238,9 @@ class QAOA(QuantumProgram):
|
|
|
228
238
|
self._is_compute_probabilites = False
|
|
229
239
|
self.optimizer = optimizer if optimizer is not None else MonteCarloOptimizer()
|
|
230
240
|
|
|
231
|
-
|
|
232
|
-
self.
|
|
233
|
-
self.
|
|
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__(
|
|
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.
|
|
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.
|
|
409
|
+
self._curr_params = np.array(self._final_params)
|
|
371
410
|
|
|
372
|
-
self.
|
|
411
|
+
self._circuits[:] = []
|
|
373
412
|
|
|
374
413
|
self._generate_circuits()
|
|
375
414
|
|
|
376
|
-
self.
|
|
415
|
+
self._probs.update(self._dispatch_circuits_and_process_results())
|
|
377
416
|
|
|
378
417
|
self._is_compute_probabilites = False
|
|
379
418
|
|
|
380
|
-
def
|
|
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
|
-
|
|
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(
|
|
429
|
-
|
|
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.
|
|
481
|
+
self._perform_final_computation()
|
|
454
482
|
|
|
455
483
|
draw_graph_solution_nodes(self.problem, self._solution_nodes)
|
divi/qprog/algorithms/_vqe.py
CHANGED
|
@@ -29,13 +29,20 @@ class VQE(QuantumProgram):
|
|
|
29
29
|
Initialize the VQE problem.
|
|
30
30
|
|
|
31
31
|
Args:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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.
|
|
39
46
|
"""
|
|
40
47
|
|
|
41
48
|
# Local Variables
|
|
@@ -57,12 +64,33 @@ class VQE(QuantumProgram):
|
|
|
57
64
|
|
|
58
65
|
@property
|
|
59
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
|
+
"""
|
|
60
73
|
return (
|
|
61
74
|
self.ansatz.n_params_per_layer(self.n_qubits, n_electrons=self.n_electrons)
|
|
62
75
|
* self.n_layers
|
|
63
76
|
)
|
|
64
77
|
|
|
65
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
|
+
"""
|
|
66
94
|
if hamiltonian is None and molecule is None:
|
|
67
95
|
raise ValueError(
|
|
68
96
|
"Either one of `molecule` and `hamiltonian` must be provided."
|
|
@@ -175,9 +203,24 @@ class VQE(QuantumProgram):
|
|
|
175
203
|
"cost_circuit"
|
|
176
204
|
].initialize_circuit_from_params(params_group, tag_prefix=f"{p}")
|
|
177
205
|
|
|
178
|
-
self.
|
|
206
|
+
self._circuits.append(circuit)
|
|
179
207
|
|
|
180
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
|
+
"""
|
|
181
224
|
if self.cost_hamiltonian is None or len(self.cost_hamiltonian) == 0:
|
|
182
225
|
raise RuntimeError(
|
|
183
226
|
"Hamiltonian operators must be generated before running the VQE"
|