Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Qiskit Optimizers in the runtime programs #214

Merged
merged 6 commits into from
Jul 29, 2021
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
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
Cryoris marked this conversation as resolved.
Show resolved Hide resolved
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