From 358874b825eec135a4c5316009e312c5129f01f6 Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Fri, 31 Jul 2020 13:05:49 +0200 Subject: [PATCH] VQE construct circuit returns circuits (#1121) * VQE construct circuit to return circuits * add reno * add test * fix spell * return all circuits from expression (also duplicates) Co-authored-by: Manoel Marques Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- .../algorithms/minimum_eigen_solvers/vqe.py | 43 ++++++++++++++++--- ...qe-construct-circuit-cc30fbcc7a563474.yaml | 8 ++++ test/aqua/test_vqe.py | 20 ++++++++- 3 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/vqe-construct-circuit-cc30fbcc7a563474.yaml diff --git a/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py index 205a8ddd72..2df96408a7 100755 --- a/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py @@ -299,9 +299,9 @@ def print_settings(self): ret += "===============================================================\n" return ret - def construct_circuit(self, - parameter: Union[List[float], List[Parameter], np.ndarray] - ) -> OperatorBase: + def construct_expectation(self, + parameter: Union[List[float], List[Parameter], np.ndarray] + ) -> OperatorBase: r""" Generate the ansatz circuit and expectation value measurement, and return their runnable composition. @@ -329,13 +329,46 @@ def construct_circuit(self, wave_function = self.var_form.construct_circuit(parameter) # If ExpectationValue was never created, create one now. - if not self.expectation: + 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: + 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.') + observable_meas = self.expectation.convert(StateFn(self.operator, is_measurement=True)) ansatz_circuit_op = CircuitStateFn(wave_function) return observable_meas.compose(ansatz_circuit_op).reduce() + def construct_circuit(self, + parameter: Union[List[float], List[Parameter], np.ndarray] + ) -> List[QuantumCircuit]: + """Return the circuits used to compute the expectation value. + + Args: + parameter: Parameters for the ansatz circuit. + + Returns: + A list of the circuits used to compute the expectation value. + """ + expect_op = self.construct_expectation(parameter).to_circuit_op() + + circuits = [] + + # recursively extract circuits + def extract_circuits(op): + if isinstance(op, CircuitStateFn): + circuits.append(op.primitive) + elif isinstance(op, ListOp): + for op_i in op.oplist: + extract_circuits(op_i) + + extract_circuits(expect_op) + + return circuits + def supports_aux_operators(self) -> bool: return True @@ -436,7 +469,7 @@ def _energy_evaluation(self, parameters: Union[List[float], np.ndarray] RuntimeError: If the variational form has no parameters. """ if not self._expect_op: - self._expect_op = self.construct_circuit(self._var_form_params) + self._expect_op = self.construct_expectation(self._var_form_params) num_parameters = self.var_form.num_parameters if self._var_form.num_parameters == 0: diff --git a/releasenotes/notes/vqe-construct-circuit-cc30fbcc7a563474.yaml b/releasenotes/notes/vqe-construct-circuit-cc30fbcc7a563474.yaml new file mode 100644 index 0000000000..b196ae3921 --- /dev/null +++ b/releasenotes/notes/vqe-construct-circuit-cc30fbcc7a563474.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + The ``construct_circuit`` method of ``VQE`` previously returned the + expectation value to be evaluated as type ``OperatorBase``. + This functionality has been moved into ``construct_expectation`` and + ``construct_circuit`` returns a list of the circuits that are evaluated + to compute the expectation value. diff --git a/test/aqua/test_vqe.py b/test/aqua/test_vqe.py index bec76c1bf5..88e014bcb0 100644 --- a/test/aqua/test_vqe.py +++ b/test/aqua/test_vqe.py @@ -24,7 +24,8 @@ from qiskit.aqua import QuantumInstance, aqua_globals, AquaError from qiskit.aqua.operators import (WeightedPauliOperator, PrimitiveOp, X, Z, I, - AerPauliExpectation, PauliExpectation) + AerPauliExpectation, PauliExpectation, + MatrixExpectation) 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 @@ -97,6 +98,23 @@ def test_circuit_input(self): result = vqe.run(self.statevector_simulator) self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) + @data( + (MatrixExpectation(), 1), + (AerPauliExpectation(), 1), + (PauliExpectation(), 2), + ) + @unpack + def test_construct_circuit(self, expectation, num_circuits): + """Test construct circuits returns QuantumCircuits and the right number of them.""" + wavefunction = EfficientSU2(2, reps=1) + vqe = VQE(self.h2_op, wavefunction, expectation=expectation) + params = [0] * wavefunction.num_parameters + circuits = vqe.construct_circuit(params) + + self.assertEqual(len(circuits), num_circuits) + for circuit in circuits: + self.assertIsInstance(circuit, QuantumCircuit) + def test_legacy_operator(self): """Test the VQE accepts and converts the legacy WeightedPauliOperator.""" pauli_dict = {