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
1 change: 1 addition & 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
ElePT marked this conversation as resolved.
Show resolved Hide resolved
from qiskit.opflow import PauliSumOp
from qiskit.quantum_info.operators.base_operator import BaseOperator

Expand Down
15 changes: 14 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 All @@ -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:
Expand Down Expand Up @@ -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
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
14 changes: 14 additions & 0 deletions qiskit/algorithms/variational_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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]:
Expand Down Expand Up @@ -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
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