Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

VQE change backends Failure #1154

Merged
merged 1 commit into from
Aug 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def __init__(self,
self._max_evals_grouped = max_evals_grouped
self._circuit_sampler = None # type: Optional[CircuitSampler]
self._expectation = expectation
self._user_valid_expectation = self._expectation is not None
self._include_custom = include_custom
self._expect_op = None
self._operator = None
Expand Down Expand Up @@ -184,26 +185,29 @@ def operator(self, operator: Union[OperatorBase, LegacyBaseOperator]) -> None:
self._operator = operator
self._expect_op = None
self._check_operator_varform()
if self._expectation is None:
# Expectation was not passed by user, try to create one
if not self._user_valid_expectation:
self._try_set_expectation_value_from_factory()

def _try_set_expectation_value_from_factory(self):
if self.operator and self.quantum_instance:
self.expectation = ExpectationFactory.build(operator=self.operator,
backend=self.quantum_instance,
include_custom=self._include_custom)
def _try_set_expectation_value_from_factory(self) -> None:
if self.operator is not None and self.quantum_instance is not None:
self._set_expectation(ExpectationFactory.build(operator=self.operator,
backend=self.quantum_instance,
include_custom=self._include_custom))

def _set_expectation(self, exp: ExpectationBase) -> None:
self._expectation = exp
self._user_valid_expectation = False
self._expect_op = None

@QuantumAlgorithm.quantum_instance.setter
def quantum_instance(self, quantum_instance: Union[QuantumInstance, BaseBackend]) -> None:
""" set quantum_instance """
super(VQE, self.__class__).quantum_instance.__set__(self, quantum_instance)

if self._circuit_sampler is None:
self._circuit_sampler = CircuitSampler(self._quantum_instance)
else:
self._circuit_sampler.quantum_instance = self._quantum_instance

if self._expectation is None:
self._circuit_sampler = CircuitSampler(self._quantum_instance)
# Expectation was not passed by user, try to create one
if not self._user_valid_expectation:
self._try_set_expectation_value_from_factory()

@property
Expand All @@ -214,8 +218,8 @@ def expectation(self) -> ExpectationBase:

@expectation.setter
def expectation(self, exp: ExpectationBase) -> None:
self._expectation = exp
self._expect_op = None
self._set_expectation(exp)
self._user_valid_expectation = self._expectation is not None

@property
def aux_operators(self) -> Optional[List[Optional[OperatorBase]]]:
Expand Down Expand Up @@ -328,12 +332,12 @@ def construct_expectation(self,
else:
wave_function = self.var_form.construct_circuit(parameter)

# If ExpectationValue was never created, create one now.
if self.expectation is None:
# Expectation was never created, try to create one
if self._expectation is None:
self._try_set_expectation_value_from_factory()

# If setting the expectation failed, raise an Error:
if self.expectation is None:
if self._expectation is None:
raise AquaError('No expectation set and could not automatically set one, please '
'try explicitly setting an expectation or specify a backend so it '
'can be chosen automatically.')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
fixes:
- |
Changing backends in VQE from statevector to qasm_simulator or real device
was causing an error due to CircuitSampler incompatible reuse. VQE was changed
to always create a new CircuitSampler and create a new expectation in case not
entered by user.
Refer to
`#1153 <https://github.com/Qiskit/qiskit-aqua/issues/1153>` for more details.
34 changes: 33 additions & 1 deletion test/aqua/test_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from qiskit.aqua import QuantumInstance, aqua_globals, AquaError
from qiskit.aqua.operators import (WeightedPauliOperator, PrimitiveOp, X, Z, I,
AerPauliExpectation, PauliExpectation,
MatrixExpectation)
MatrixExpectation, ExpectationBase)
from qiskit.aqua.components.variational_forms import RYRZ
from qiskit.aqua.components.optimizers import L_BFGS_B, COBYLA, SPSA, SLSQP
from qiskit.aqua.algorithms import VQE
Expand Down Expand Up @@ -315,6 +315,38 @@ def test_ibmq(self):
self.assertIsNotNone(result.cost_function_evals)
self.assertIsNotNone(result.optimizer_time)

@data(MatrixExpectation(), None)
def test_backend_change(self, user_expectation):
"""Test that VQE works when backend changes."""
vqe = VQE(operator=self.h2_op,
var_form=TwoLocal(rotation_blocks=['ry', 'rz'], entanglement_blocks='cz'),
optimizer=SLSQP(maxiter=2),
expectation=user_expectation,
quantum_instance=BasicAer.get_backend('statevector_simulator'))
result0 = vqe.run()
if user_expectation is not None:
with self.subTest('User expectation kept.'):
self.assertEqual(vqe.expectation, user_expectation)
else:
with self.subTest('Expectation created.'):
self.assertIsInstance(vqe.expectation, ExpectationBase)
try:
vqe.set_backend(BasicAer.get_backend('qasm_simulator'))
except Exception as ex: # pylint: disable=broad-except
self.fail("Failed to change backend. Error: '{}'".format(str(ex)))
return

result1 = vqe.run()
if user_expectation is not None:
with self.subTest('Change backend with user expectation, it is kept.'):
self.assertEqual(vqe.expectation, user_expectation)
else:
with self.subTest('Change backend without user expectation, one created.'):
self.assertIsInstance(vqe.expectation, ExpectationBase)

with self.subTest('Check results.'):
self.assertEqual(len(result0.optimal_point), len(result1.optimal_point))


if __name__ == '__main__':
unittest.main()
55 changes: 41 additions & 14 deletions test/optimization/test_qaoa.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from qiskit.aqua.components.optimizers import COBYLA
from qiskit.aqua.algorithms import QAOA
from qiskit.aqua import QuantumInstance, aqua_globals
from qiskit.aqua.operators import X, Z, I
from qiskit.aqua.operators import X, I

W1 = np.array([
[0, 1, 0, 1],
Expand Down Expand Up @@ -87,19 +87,46 @@ def test_qaoa(self, w, prob, m, solutions, convert_to_matrix_op):
self.assertIn(''.join([str(int(i)) for i in graph_solution]), solutions)

def test_change_operator_size(self):
""" QAOA test """
backend = BasicAer.get_backend('statevector_simulator')
optimizer = COBYLA(maxiter=2)
qubit_op, _ = max_cut.get_operator(W1)
qubit_op = qubit_op.to_opflow().to_matrix_op()

seed = 0
aqua_globals.random_seed = seed
qaoa = QAOA(qubit_op, optimizer, P1)
quantum_instance = QuantumInstance(backend, seed_simulator=seed, seed_transpiler=seed)
qaoa.run(quantum_instance)
qaoa.operator = (X ^ qubit_op ^ Z)
qaoa.run()
""" QAOA change operator size test """

aqua_globals.random_seed = 0
qubit_op, _ = max_cut.get_operator(
np.array([
[0, 1, 0, 1],
[1, 0, 1, 0],
[0, 1, 0, 1],
[1, 0, 1, 0]
]))
qaoa = QAOA(qubit_op.to_opflow(), COBYLA(), 1)
quantum_instance = QuantumInstance(BasicAer.get_backend('statevector_simulator'),
seed_simulator=aqua_globals.random_seed,
seed_transpiler=aqua_globals.random_seed)
result = qaoa.run(quantum_instance)
x = sample_most_likely(result.eigenstate)
graph_solution = max_cut.get_graph_solution(x)
with self.subTest(msg='QAOA 4x4'):
self.assertIn(''.join([str(int(i)) for i in graph_solution]), {'0101', '1010'})

try:
qubit_op, _ = max_cut.get_operator(
np.array([
[0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0],
]))
qaoa.operator = qubit_op.to_opflow()
except Exception as ex: # pylint: disable=broad-except
self.fail("Failed to change operator. Error: '{}'".format(str(ex)))
return

result = qaoa.run()
x = sample_most_likely(result.eigenstate)
graph_solution = max_cut.get_graph_solution(x)
with self.subTest(msg='QAOA 6x6'):
self.assertIn(''.join([str(int(i)) for i in graph_solution]), {'010101', '101010'})

@idata([
[W2, S2, None],
Expand Down