Skip to content

Commit

Permalink
Fix VQE result, add ansatz (Qiskit/qiskit#8816)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Sep 30, 2022
1 parent 8575820 commit e1d28b1
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 3 deletions.
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/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/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

0 comments on commit e1d28b1

Please sign in to comment.