Skip to content

Commit e1d28b1

Browse files
ElePTCryorismergify[bot]
authored
Fix VQE result, add ansatz (Qiskit/qiskit#8816)
* Add ansatz to result * Add ansatz to eigen. results * Update qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Copy ansatz * Apply comments * Change name to optimal_circuit(s) * add blank line * move optimal circuit to variational result * remove import Co-authored-by: Julien Gacon <gaconju@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 8575820 commit e1d28b1

File tree

6 files changed

+54
-3
lines changed

6 files changed

+54
-3
lines changed

qiskit_algorithms/eigensolvers/vqd.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ def compute_eigenvalues(
259259

260260
eval_time = time() - start_time
261261

262-
self._update_vqd_result(result, opt_result, eval_time, self.ansatz)
262+
self._update_vqd_result(result, opt_result, eval_time, self.ansatz.copy())
263263

264264
if aux_operators is not None:
265265
aux_value = estimate_observables(
@@ -388,6 +388,7 @@ def _build_vqd_result() -> VQDResult:
388388
result.optimizer_times = []
389389
result.eigenvalues = []
390390
result.optimizer_results = []
391+
result.optimal_circuits = []
391392
return result
392393

393394
@staticmethod
@@ -400,6 +401,7 @@ def _update_vqd_result(result, opt_result, eval_time, ansatz) -> VQDResult:
400401
result.optimizer_times.append(eval_time)
401402
result.eigenvalues.append(opt_result.fun + 0j)
402403
result.optimizer_results.append(opt_result)
404+
result.optimal_circuits.append(ansatz)
403405
return result
404406

405407

@@ -414,6 +416,7 @@ def __init__(self) -> None:
414416
self._optimal_points = None
415417
self._optimal_parameters = None
416418
self._optimizer_results = None
419+
self._optimal_circuits = None
417420

418421
@property
419422
def cost_function_evals(self) -> Sequence[int] | None:
@@ -474,3 +477,13 @@ def optimizer_results(self) -> Sequence[OptimizerResult] | None:
474477
def optimizer_results(self, value: Sequence[OptimizerResult]) -> None:
475478
"""Sets optimizer results"""
476479
self._optimizer_results = value
480+
481+
@property
482+
def optimal_circuits(self) -> list[QuantumCircuit]:
483+
"""The optimal circuits. Along with the optimal parameters,
484+
these can be used to retrieve the different eigenstates."""
485+
return self._optimal_circuits
486+
487+
@optimal_circuits.setter
488+
def optimal_circuits(self, optimal_circuits: list[QuantumCircuit]) -> None:
489+
self._optimal_circuits = optimal_circuits

qiskit_algorithms/minimum_eigensolvers/sampling_vqe.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,12 @@ def compute_minimum_eigenvalue(
234234
aux_operators_evaluated = None
235235

236236
return self._build_sampling_vqe_result(
237-
optimizer_result, aux_operators_evaluated, best_measurement, final_state, optimizer_time
237+
self.ansatz.copy(),
238+
optimizer_result,
239+
aux_operators_evaluated,
240+
best_measurement,
241+
final_state,
242+
optimizer_time,
238243
)
239244

240245
def _get_evaluate_energy(
@@ -306,6 +311,7 @@ def evaluate_energy(parameters):
306311

307312
def _build_sampling_vqe_result(
308313
self,
314+
ansatz: QuantumCircuit,
309315
optimizer_result: OptimizerResult,
310316
aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]],
311317
best_measurement: dict[str, Any],
@@ -323,6 +329,7 @@ def _build_sampling_vqe_result(
323329
result.optimizer_result = optimizer_result
324330
result.best_measurement = best_measurement["best"]
325331
result.eigenstate = final_state
332+
result.optimal_circuit = ansatz
326333
return result
327334

328335

qiskit_algorithms/minimum_eigensolvers/vqe.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ def compute_minimum_eigenvalue(
200200
else:
201201
aux_operators_evaluated = None
202202

203-
return self._build_vqe_result(optimizer_result, aux_operators_evaluated, optimizer_time)
203+
return self._build_vqe_result(
204+
self.ansatz, optimizer_result, aux_operators_evaluated, optimizer_time
205+
)
204206

205207
@classmethod
206208
def supports_aux_operators(cls) -> bool:
@@ -308,11 +310,13 @@ def _check_operator_ansatz(self, operator: BaseOperator | PauliSumOp):
308310

309311
def _build_vqe_result(
310312
self,
313+
ansatz: QuantumCircuit,
311314
optimizer_result: OptimizerResult,
312315
aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]],
313316
optimizer_time: float,
314317
) -> VQEResult:
315318
result = VQEResult()
319+
result.optimal_circuit = ansatz.copy()
316320
result.eigenvalue = optimizer_result.fun
317321
result.cost_function_evals = optimizer_result.nfev
318322
result.optimal_point = optimizer_result.x

qiskit_algorithms/variational_algorithm.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
from abc import ABC, abstractmethod
3131
import numpy as np
3232

33+
from qiskit.circuit import QuantumCircuit
34+
3335
from .algorithm_result import AlgorithmResult
3436
from .optimizers import OptimizerResult
3537

@@ -61,6 +63,7 @@ def __init__(self) -> None:
6163
self._optimal_point = None
6264
self._optimal_parameters = None
6365
self._optimizer_result = None
66+
self._optimal_circuit = None
6467

6568
@property
6669
def optimizer_evals(self) -> Optional[int]:
@@ -121,3 +124,14 @@ def optimizer_result(self) -> Optional[OptimizerResult]:
121124
def optimizer_result(self, value: OptimizerResult) -> None:
122125
"""Sets optimizer result"""
123126
self._optimizer_result = value
127+
128+
@property
129+
def optimal_circuit(self) -> QuantumCircuit:
130+
"""The optimal circuits. Along with the optimal parameters,
131+
these can be used to retrieve the minimum eigenstate.
132+
"""
133+
return self._optimal_circuit
134+
135+
@optimal_circuit.setter
136+
def optimal_circuit(self, optimal_circuit: QuantumCircuit) -> None:
137+
self._optimal_circuit = optimal_circuit

test/eigensolvers/test_vqd.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ def test_basic_operator(self, op):
100100
with self.subTest(msg="assert optimizer_times is set"):
101101
self.assertIsNotNone(result.optimizer_times)
102102

103+
with self.subTest(msg="assert return ansatz is set"):
104+
job = self.estimator.run(
105+
result.optimal_circuits,
106+
[op] * len(result.optimal_points),
107+
result.optimal_points,
108+
)
109+
np.testing.assert_array_almost_equal(job.result().values, result.eigenvalues, 6)
110+
103111
@data(H2_PAULI, H2_OP)
104112
def test_mismatching_num_qubits(self, op):
105113
"""Ensuring circuit and operator mismatch is caught"""

test/minimum_eigensolvers/test_vqe.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ def test_basic_aer_statevector(self, estimator):
107107
with self.subTest(msg="assert optimizer_result."):
108108
self.assertAlmostEqual(result.optimizer_result.fun, self.h2_energy, places=5)
109109

110+
with self.subTest(msg="assert return ansatz is set"):
111+
estimator = Estimator()
112+
job = estimator.run(result.optimal_circuit, self.h2_op, result.optimal_point)
113+
np.testing.assert_array_almost_equal(job.result().values, result.eigenvalue, 6)
114+
110115
def test_invalid_initial_point(self):
111116
"""Test the proper error is raised when the initial point has the wrong size."""
112117
ansatz = self.ryrz_wavefunction

0 commit comments

Comments
 (0)