Skip to content

Commit

Permalink
Allow Qiskit Optimizers in the runtime programs (#214) (#215)
Browse files Browse the repository at this point in the history
* make opt.VQEProgram a copy of nature.VQEProgram

* allow Qiskit Optimizers in QAOAProgram

* add reno

* Update releasenotes/notes/runtime-optimizers-58f30311f969f54f.yaml

Co-authored-by: Manoel Marques <Manoel.Marques@ibm.com>
Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com>
(cherry picked from commit 1d71c13)

Co-authored-by: Julien Gacon <gaconju@gmail.com>
  • Loading branch information
mergify[bot] and Cryoris authored Jul 29, 2021
1 parent 22631f8 commit bbaf0e6
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 37 deletions.
13 changes: 7 additions & 6 deletions qiskit_optimization/runtime/qaoa_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from qiskit import QuantumCircuit
from qiskit.algorithms import MinimumEigensolverResult
from qiskit.algorithms.optimizers import Optimizer
from qiskit.circuit.library import QAOAAnsatz
from qiskit.opflow import OperatorBase
from qiskit.providers import Provider
Expand All @@ -32,7 +33,7 @@ class QAOAProgram(VQEProgram):

def __init__(
self,
optimizer: Optional[Dict[str, Any]] = None,
optimizer: Optional[Union[Optimizer, Dict[str, Any]]] = None,
reps: int = 1,
initial_state: Optional[QuantumCircuit] = None,
mixer: Union[QuantumCircuit, OperatorBase] = None,
Expand All @@ -46,11 +47,11 @@ def __init__(
) -> None:
"""
Args:
optimizer: A dictionary specifying a classical optimizer.
Currently only SPSA and QN-SPSA are supported. Per default, SPSA is used.
The dictionary must contain a key ``name`` for the name of the optimizer and may
contain additional keys for the settings.
E.g. ``{'name': 'SPSA', 'maxiter': 100}``.
optimizer: An optimizer or dictionary specifying a classical optimizer.
If a dictionary, only SPSA and QN-SPSA are supported. The dictionary must contain a
key ``name`` for the name of the optimizer and may contain additional keys for the
settings. E.g. ``{'name': 'SPSA', 'maxiter': 100}``.
Per default, SPSA is used.
reps: the integer parameter :math:`p` as specified in https://arxiv.org/abs/1411.4028,
Has a minimum valid value of 1.
initial_state: An optional initial state to prepend the QAOA circuit with
Expand Down
51 changes: 25 additions & 26 deletions qiskit_optimization/runtime/vqe_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
"""The Qiskit Optimization VQE Quantum Program."""


from typing import List, Callable, Optional, Any, Dict
from typing import List, Callable, Optional, Any, Dict, Union
import numpy as np

from qiskit import QuantumCircuit
from qiskit.exceptions import QiskitError
from qiskit.providers import Provider
from qiskit.providers.backend import Backend
from qiskit.algorithms import MinimumEigensolver, MinimumEigensolverResult, VQEResult
from qiskit.algorithms.optimizers import Optimizer, SPSA
from qiskit.opflow import OperatorBase, PauliSumOp
from qiskit.quantum_info import SparsePauliOp

Expand All @@ -35,7 +36,7 @@ class VQEProgram(MinimumEigensolver):
def __init__(
self,
ansatz: QuantumCircuit,
optimizer: Optional[Dict[str, Any]] = None,
optimizer: Optional[Union[Optimizer, Dict[str, Any]]] = None,
initial_point: Optional[np.ndarray] = None,
provider: Optional[Provider] = None,
backend: Optional[Backend] = None,
Expand All @@ -47,11 +48,11 @@ def __init__(
"""
Args:
ansatz: A parameterized circuit used as Ansatz for the wave function.
optimizer: A dictionary specifying a classical optimizer.
Currently only SPSA and QN-SPSA are supported. Per default, SPSA is used.
The dictionary must contain a key ``name`` for the name of the optimizer and may
contain additional keys for the settings.
E.g. ``{'name': 'SPSA', 'maxiter': 100}``.
optimizer: An optimizer or dictionary specifying a classical optimizer.
If a dictionary, only SPSA and QN-SPSA are supported. The dictionary must contain a
key ``name`` for the name of the optimizer and may contain additional keys for the
settings. E.g. ``{'name': 'SPSA', 'maxiter': 100}``.
Per default, SPSA is used.
backend: The backend to run the circuits on.
initial_point: An optional initial point (i.e. initial parameter values)
for the optimizer. If ``None`` a random vector is used.
Expand All @@ -66,7 +67,7 @@ def __init__(
steps. Per default False.
"""
if optimizer is None:
optimizer = {"name": "SPSA"}
optimizer = SPSA(maxiter=300)

# define program name
self._program_id = "vqe"
Expand Down Expand Up @@ -120,21 +121,25 @@ def ansatz(self, ansatz: QuantumCircuit) -> None:
self._ansatz = ansatz

@property
def optimizer(self) -> Dict[str, Any]:
def optimizer(self) -> Union[Optimizer, Dict[str, Any]]:
"""Return the dictionary describing the optimizer."""
return self._optimizer

@optimizer.setter
def optimizer(self, settings: Dict[str, Any]) -> None:
def optimizer(self, optimizer: Union[Optimizer, Dict[str, Any]]) -> None:
"""Set the optimizer."""
if "name" not in settings.keys():
raise ValueError(
"The settings must contain a ``name`` key specifying the type of " "the optimizer."
)
if isinstance(optimizer, Optimizer):
self._optimizer = optimizer
else:
if "name" not in optimizer.keys():
raise ValueError(
"The optimizer dictionary must contain a ``name`` key specifying the type "
"of the optimizer."
)

_validate_optimizer_settings(settings)
_validate_optimizer_settings(optimizer)

self._optimizer = settings
self._optimizer = optimizer

@property
def backend(self) -> Optional[Backend]:
Expand Down Expand Up @@ -218,9 +223,7 @@ def wrapped_callback(*args):
return None

def compute_minimum_eigenvalue(
self,
operator: OperatorBase,
aux_operators: Optional[List[Optional[OperatorBase]]] = None,
self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None
) -> MinimumEigensolverResult:
"""Calls the VQE Runtime to approximate the ground state of the given operator.
Expand Down Expand Up @@ -280,11 +283,6 @@ def compute_minimum_eigenvalue(
raise RuntimeError(f"The job {job.job_id()} failed unexpectedly.") from exc

# re-build result from serialized return value
vqe_result = self._parse_job_result(job, result)
return vqe_result

def _parse_job_result(self, job, result):
"""Build the VQE result object from the job result."""
vqe_result = VQEProgramResult()
vqe_result.job_id = job.job_id()
vqe_result.cost_function_evals = result.get("cost_function_evals", None)
Expand All @@ -297,6 +295,7 @@ def _parse_job_result(self, job, result):
vqe_result.optimizer_evals = result.get("optimizer_evals", None)
vqe_result.optimizer_time = result.get("optimizer_time", None)
vqe_result.optimizer_history = result.get("optimizer_history", None)

return vqe_result


Expand All @@ -309,8 +308,8 @@ class VQEProgramResult(VQEResult):

def __init__(self) -> None:
super().__init__()
self._job_id: Optional[str] = None
self._optimizer_history: Optional[Dict[str, Any]] = None
self._job_id = None # type: str
self._optimizer_history = None # type: Dict[str, Any]

@property
def job_id(self) -> str:
Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/runtime-optimizers-58f30311f969f54f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Allow Qiskit's :class:`~qiskit.algorithms.optimizers.Optimizer` classes as input for
the ``optimizer`` in the :class:`~qiskit_optimization.runtime.VQEProgram` and
:class:`~qiskit_optimization.runtime.QAOAProgram` instead of only
dictionaries.
3 changes: 2 additions & 1 deletion test/runtime/fake_vqeruntime.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from typing import Dict, Any
import numpy as np
from qiskit.algorithms.optimizers import Optimizer
from qiskit.circuit import QuantumCircuit
from qiskit.opflow import PauliSumOp
from qiskit.providers import Provider
Expand Down Expand Up @@ -59,7 +60,7 @@ def run(self, program_id, inputs, options, callback=None):
"aux_operators": (list, type(None)),
"ansatz": QuantumCircuit,
"initial_point": (np.ndarray, str),
"optimizer": dict,
"optimizer": (Optimizer, dict),
"shots": int,
"measurement_error_mitigation": bool,
"store_intermediate": bool,
Expand Down
10 changes: 8 additions & 2 deletions test/runtime/test_qaoaprogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
from test import QiskitOptimizationTestCase

import unittest
from ddt import ddt, data
import numpy as np
from qiskit.algorithms.optimizers import COBYLA
from qiskit.providers.basicaer import QasmSimulatorPy
from qiskit.opflow import I, Z

Expand All @@ -24,19 +26,23 @@
from .fake_vqeruntime import FakeRuntimeProvider


@ddt
class TestQAOAProgram(QiskitOptimizationTestCase):
"""Test the QAOA program."""

def setUp(self):
super().setUp()
self.provider = FakeRuntimeProvider()

def test_standard_case(self):
@data(
{"name": "SPSA", "maxiter": 100},
COBYLA(),
)
def test_standard_case(self, optimizer):
"""Test a standard use case."""
operator = Z ^ I ^ Z
reps = 2
initial_point = np.random.RandomState(42).random(2 * reps)
optimizer = {"name": "SPSA", "maxiter": 100}
backend = QasmSimulatorPy()

qaoa = QAOAProgram(
Expand Down
10 changes: 8 additions & 2 deletions test/runtime/test_vqeprogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
from test import QiskitOptimizationTestCase

import unittest
from ddt import ddt, data
import numpy as np
from qiskit.algorithms.optimizers import COBYLA
from qiskit.providers.basicaer import QasmSimulatorPy
from qiskit.circuit.library import RealAmplitudes
from qiskit.opflow import I, Z
Expand All @@ -25,19 +27,23 @@
from .fake_vqeruntime import FakeRuntimeProvider


@ddt
class TestVQEProgram(QiskitOptimizationTestCase):
"""Test the VQE program."""

def setUp(self):
super().setUp()
self.provider = FakeRuntimeProvider()

def test_standard_case(self):
@data(
{"name": "SPSA", "maxiter": 100},
COBYLA(),
)
def test_standard_case(self, optimizer):
"""Test a standard use case."""
circuit = RealAmplitudes(3)
operator = Z ^ I ^ Z
initial_point = np.random.RandomState(42).random(circuit.num_parameters)
optimizer = {"name": "SPSA", "maxiter": 100}
backend = QasmSimulatorPy()

vqe = VQEProgram(
Expand Down

0 comments on commit bbaf0e6

Please sign in to comment.