diff --git a/qiskit/algorithms/eigensolvers/vqd.py b/qiskit/algorithms/eigensolvers/vqd.py index 26b215aa760a..6b48a30090ed 100644 --- a/qiskit/algorithms/eigensolvers/vqd.py +++ b/qiskit/algorithms/eigensolvers/vqd.py @@ -259,7 +259,7 @@ def compute_eigenvalues( eval_time = time() - start_time - self._update_vqd_result(result, opt_result, eval_time, self.ansatz) + self._update_vqd_result(result, opt_result, eval_time, self.ansatz.copy()) if aux_operators is not None: aux_value = estimate_observables( @@ -388,6 +388,7 @@ def _build_vqd_result() -> VQDResult: result.optimizer_times = [] result.eigenvalues = [] result.optimizer_results = [] + result.optimal_circuits = [] return result @staticmethod @@ -400,6 +401,7 @@ def _update_vqd_result(result, opt_result, eval_time, ansatz) -> VQDResult: result.optimizer_times.append(eval_time) result.eigenvalues.append(opt_result.fun + 0j) result.optimizer_results.append(opt_result) + result.optimal_circuits.append(ansatz) return result @@ -414,6 +416,7 @@ def __init__(self) -> None: self._optimal_points = None self._optimal_parameters = None self._optimizer_results = None + self._optimal_circuits = None @property def cost_function_evals(self) -> Sequence[int] | None: @@ -474,3 +477,13 @@ def optimizer_results(self) -> Sequence[OptimizerResult] | None: def optimizer_results(self, value: Sequence[OptimizerResult]) -> None: """Sets optimizer results""" self._optimizer_results = value + + @property + def optimal_circuits(self) -> list[QuantumCircuit]: + """The optimal circuits. Along with the optimal parameters, + these can be used to retrieve the different eigenstates.""" + return self._optimal_circuits + + @optimal_circuits.setter + def optimal_circuits(self, optimal_circuits: list[QuantumCircuit]) -> None: + self._optimal_circuits = optimal_circuits diff --git a/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py b/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py index 36d691753f2d..7e84ec35e299 100755 --- a/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py +++ b/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py @@ -234,7 +234,12 @@ def compute_minimum_eigenvalue( aux_operators_evaluated = None return self._build_sampling_vqe_result( - optimizer_result, aux_operators_evaluated, best_measurement, final_state, optimizer_time + self.ansatz.copy(), + optimizer_result, + aux_operators_evaluated, + best_measurement, + final_state, + optimizer_time, ) def _get_evaluate_energy( @@ -306,6 +311,7 @@ def evaluate_energy(parameters): def _build_sampling_vqe_result( self, + ansatz: QuantumCircuit, optimizer_result: OptimizerResult, aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]], best_measurement: dict[str, Any], @@ -323,6 +329,7 @@ def _build_sampling_vqe_result( result.optimizer_result = optimizer_result result.best_measurement = best_measurement["best"] result.eigenstate = final_state + result.optimal_circuit = ansatz return result diff --git a/qiskit/algorithms/minimum_eigensolvers/vqe.py b/qiskit/algorithms/minimum_eigensolvers/vqe.py index ca8a260622d6..4c01ddc26191 100644 --- a/qiskit/algorithms/minimum_eigensolvers/vqe.py +++ b/qiskit/algorithms/minimum_eigensolvers/vqe.py @@ -200,7 +200,9 @@ def compute_minimum_eigenvalue( else: aux_operators_evaluated = None - return self._build_vqe_result(optimizer_result, aux_operators_evaluated, optimizer_time) + return self._build_vqe_result( + self.ansatz, optimizer_result, aux_operators_evaluated, optimizer_time + ) @classmethod def supports_aux_operators(cls) -> bool: @@ -308,11 +310,13 @@ def _check_operator_ansatz(self, operator: BaseOperator | PauliSumOp): def _build_vqe_result( self, + ansatz: QuantumCircuit, optimizer_result: OptimizerResult, aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]], optimizer_time: float, ) -> VQEResult: result = VQEResult() + result.optimal_circuit = ansatz.copy() result.eigenvalue = optimizer_result.fun result.cost_function_evals = optimizer_result.nfev result.optimal_point = optimizer_result.x diff --git a/qiskit/algorithms/variational_algorithm.py b/qiskit/algorithms/variational_algorithm.py index db28193d31f9..fc05d28af090 100644 --- a/qiskit/algorithms/variational_algorithm.py +++ b/qiskit/algorithms/variational_algorithm.py @@ -30,6 +30,8 @@ from abc import ABC, abstractmethod import numpy as np +from qiskit.circuit import QuantumCircuit + from .algorithm_result import AlgorithmResult from .optimizers import OptimizerResult @@ -61,6 +63,7 @@ def __init__(self) -> None: self._optimal_point = None self._optimal_parameters = None self._optimizer_result = None + self._optimal_circuit = None @property def optimizer_evals(self) -> Optional[int]: @@ -121,3 +124,14 @@ def optimizer_result(self) -> Optional[OptimizerResult]: def optimizer_result(self, value: OptimizerResult) -> None: """Sets optimizer result""" self._optimizer_result = value + + @property + def optimal_circuit(self) -> QuantumCircuit: + """The optimal circuits. Along with the optimal parameters, + these can be used to retrieve the minimum eigenstate. + """ + return self._optimal_circuit + + @optimal_circuit.setter + def optimal_circuit(self, optimal_circuit: QuantumCircuit) -> None: + self._optimal_circuit = optimal_circuit diff --git a/test/python/algorithms/eigensolvers/test_vqd.py b/test/python/algorithms/eigensolvers/test_vqd.py index e977461468d0..f51ecb92147a 100644 --- a/test/python/algorithms/eigensolvers/test_vqd.py +++ b/test/python/algorithms/eigensolvers/test_vqd.py @@ -100,6 +100,14 @@ def test_basic_operator(self, op): with self.subTest(msg="assert optimizer_times is set"): self.assertIsNotNone(result.optimizer_times) + with self.subTest(msg="assert return ansatz is set"): + job = self.estimator.run( + result.optimal_circuits, + [op] * len(result.optimal_points), + result.optimal_points, + ) + np.testing.assert_array_almost_equal(job.result().values, result.eigenvalues, 6) + @data(H2_PAULI, H2_OP) def test_mismatching_num_qubits(self, op): """Ensuring circuit and operator mismatch is caught""" diff --git a/test/python/algorithms/minimum_eigensolvers/test_vqe.py b/test/python/algorithms/minimum_eigensolvers/test_vqe.py index 27f58f85ccfe..c3c0b89d9cce 100644 --- a/test/python/algorithms/minimum_eigensolvers/test_vqe.py +++ b/test/python/algorithms/minimum_eigensolvers/test_vqe.py @@ -107,6 +107,11 @@ def test_basic_aer_statevector(self, estimator): with self.subTest(msg="assert optimizer_result."): self.assertAlmostEqual(result.optimizer_result.fun, self.h2_energy, places=5) + with self.subTest(msg="assert return ansatz is set"): + estimator = Estimator() + job = estimator.run(result.optimal_circuit, self.h2_op, result.optimal_point) + np.testing.assert_array_almost_equal(job.result().values, result.eigenvalue, 6) + def test_invalid_initial_point(self): """Test the proper error is raised when the initial point has the wrong size.""" ansatz = self.ryrz_wavefunction