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

Commit

Permalink
VQE change backends (#1154)
Browse files Browse the repository at this point in the history
  • Loading branch information
manoelmarques authored Aug 1, 2020
1 parent 7ac0fbd commit a133bc6
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 32 deletions.
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

0 comments on commit a133bc6

Please sign in to comment.