Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix VQE result, add ansatz #8816

Merged
merged 12 commits into from
Sep 30, 2022
12 changes: 12 additions & 0 deletions qiskit/algorithms/eigensolvers/eigensolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from typing import Any
import numpy as np

from qiskit.circuit import QuantumCircuit
from qiskit.opflow import PauliSumOp
from qiskit.quantum_info.operators.base_operator import BaseOperator

Expand Down Expand Up @@ -73,9 +74,20 @@ class EigensolverResult(AlgorithmResult):

def __init__(self) -> None:
super().__init__()
self._optimal_circuits = None
self._eigenvalues = None
self._aux_operators_evaluated = None

@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

@property
def eigenvalues(self) -> np.ndarray | None:
"""Return the eigenvalues."""
Expand Down
4 changes: 3 additions & 1 deletion qiskit/algorithms/eigensolvers/vqd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -388,6 +388,7 @@ def _build_vqd_result() -> VQDResult:
result.optimizer_times = []
result.eigenvalues = []
result.optimizer_results = []
result.optimal_circuits = []
return result

@staticmethod
Expand All @@ -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


Expand Down
1 change: 0 additions & 1 deletion qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ def compute_minimum_eigenvalue(
result.num_iterations = iteration
result.final_max_gradient = max_grad[0]
result.termination_criterion = termination_criterion

# once finished evaluate auxiliary operators if any
if aux_operators is not None:
aux_values = estimate_observables(
Expand Down
13 changes: 13 additions & 0 deletions qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from abc import ABC, abstractmethod
from typing import Any

from qiskit.circuit import QuantumCircuit
from qiskit.opflow import PauliSumOp
from qiskit.quantum_info.operators.base_operator import BaseOperator

Expand Down Expand Up @@ -72,9 +73,21 @@ class MinimumEigensolverResult(AlgorithmResult):

def __init__(self) -> None:
super().__init__()
self._optimal_circuit = None
self._eigenvalue = None
self._aux_operators_evaluated = None

@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

@property
def eigenvalue(self) -> complex | None:
"""The computed minimum eigenvalue."""
Expand Down
9 changes: 8 additions & 1 deletion qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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],
Expand All @@ -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


Expand Down
6 changes: 5 additions & 1 deletion qiskit/algorithms/minimum_eigensolvers/vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions test/python/algorithms/eigensolvers/test_vqd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down
5 changes: 5 additions & 0 deletions test/python/algorithms/minimum_eigensolvers/test_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down