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

Options v2 #1221

Merged
merged 32 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2785023
Add experimental options (#1067)
jyu00 Sep 20, 2023
cf0d816
Merge branch 'main' of https://github.com/Qiskit/qiskit-ibm-runtime i…
jyu00 Oct 27, 2023
989a321
fix merge issues
jyu00 Oct 27, 2023
5ab8a93
add pydantic
jyu00 Oct 30, 2023
11c30cc
black
jyu00 Oct 30, 2023
e6ef47f
lint
jyu00 Oct 30, 2023
e998b81
Merge branch 'fast_forward' of https://github.com/jyu00/qiskit-ibm-ru…
jyu00 Oct 31, 2023
3e0b4af
Fast forward experimental to latest main (#1178)
jyu00 Oct 31, 2023
b5c7100
v2 options
jyu00 Nov 1, 2023
3671b46
estimator options
jyu00 Nov 3, 2023
bf5a677
update test
jyu00 Nov 3, 2023
1d4b36e
lint
jyu00 Nov 3, 2023
1790d63
Merge branch 'experimental' of https://github.com/Qiskit/qiskit-ibm-r…
jyu00 Nov 3, 2023
4198fad
fix merge issues
jyu00 Nov 3, 2023
9c3e359
black
jyu00 Nov 3, 2023
4ec1b9e
fix noise model type
jyu00 Nov 3, 2023
3c9261c
lint again
jyu00 Nov 3, 2023
6369191
fix header
jyu00 Nov 6, 2023
548005a
fix mypy
jyu00 Nov 6, 2023
d05ce59
use v2 as default
jyu00 Nov 6, 2023
6745067
cleanup terra options
jyu00 Nov 6, 2023
7370b5d
black
jyu00 Nov 6, 2023
a19ccd4
options need not be callable
jyu00 Nov 6, 2023
52febc9
fix doc
jyu00 Nov 6, 2023
b29a4e5
fix tests
jyu00 Nov 6, 2023
1f8bd7c
fix version
jyu00 Nov 6, 2023
a56cdf0
freeze constants
jyu00 Nov 7, 2023
0131789
remove is_simulator
jyu00 Nov 7, 2023
665b881
make image work
jyu00 Nov 8, 2023
9439047
Merge branch 'main' of https://github.com/Qiskit/qiskit-ibm-runtime i…
jyu00 Nov 16, 2023
9dd2c7d
fix tests
jyu00 Nov 16, 2023
d987572
lint
jyu00 Nov 16, 2023
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
9 changes: 6 additions & 3 deletions qiskit_ibm_runtime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ def result_callback(job_id, result):
"""

import logging
import warnings

from .qiskit_runtime_service import QiskitRuntimeService
from .ibm_backend import IBMBackend
Expand All @@ -187,9 +188,9 @@ def result_callback(job_id, result):
from .utils.utils import setup_logger
from .version import __version__

from .estimator import Estimator
from .sampler import Sampler
from .options import Options
from .estimator import EstimatorV2, EstimatorV1 as Estimator
from .sampler import SamplerV1 as Sampler
from .options import Options, EstimatorOptions

# Setup the logger for the IBM Quantum Provider package.
logger = logging.getLogger(__name__)
Expand All @@ -202,3 +203,5 @@ def result_callback(job_id, result):
"""The environment variable name that is used to set the level for the IBM Quantum logger."""
QISKIT_IBM_RUNTIME_LOG_FILE = "QISKIT_IBM_RUNTIME_LOG_FILE"
"""The environment variable name that is used to set the file for the IBM Quantum logger."""

warnings.warn("You are using the experimental branch. Stability is not guaranteed.")
184 changes: 177 additions & 7 deletions qiskit_ibm_runtime/base_primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
from typing import Dict, Optional, Any, Union
import copy
import logging
from dataclasses import asdict
from dataclasses import asdict, replace
import warnings

from qiskit.providers.options import Options as TerraOptions

from qiskit_ibm_provider.session import get_cm_session as get_cm_provider_session

from .options import Options
from .options.utils import set_default_error_levels
from .options import BaseOptions, Options
from .options.utils import merge_options, set_default_error_levels
from .runtime_job import RuntimeJob
from .ibm_backend import IBMBackend
from .utils.default_session import get_cm_session
Expand All @@ -38,9 +38,177 @@
logger = logging.getLogger(__name__)


class BasePrimitive(ABC):
class BasePrimitiveV2(ABC):
"""Base class for Qiskit Runtime primitives."""

_OPTIONS_CLASS: type[BaseOptions] = Options
version = 2

def __init__(
self,
backend: Optional[Union[str, IBMBackend]] = None,
session: Optional[Union[Session, str, IBMBackend]] = None,
options: Optional[Union[Dict, BaseOptions]] = None,
):
"""Initializes the primitive.

Args:

backend: Backend to run the primitive. This can be a backend name or an :class:`IBMBackend`
instance. If a name is specified, the default account (e.g. ``QiskitRuntimeService()``)
is used.

session: Session in which to call the primitive.

If both ``session`` and ``backend`` are specified, ``session`` takes precedence.
If neither is specified, and the primitive is created inside a
:class:`qiskit_ibm_runtime.Session` context manager, then the session is used.
Otherwise if IBM Cloud channel is used, a default backend is selected.

options: Primitive options, see :class:`Options` for detailed description.
The ``backend`` keyword is still supported but is deprecated.

Raises:
ValueError: Invalid arguments are given.
"""
self._session: Optional[Session] = None
self._service: QiskitRuntimeService = None
self._backend: Optional[IBMBackend] = None

opt_cls = self._OPTIONS_CLASS
if options is None:
self.options = opt_cls()
elif isinstance(options, opt_cls):
self.options = replace(options)
elif isinstance(options, dict):
default_options = opt_cls()
self.options = opt_cls(**merge_options(default_options, options))
else:
raise ValueError(f"Invalid 'options' type. It can only be a dictionary of {opt_cls}")

if isinstance(session, Session):
self._session = session
self._service = self._session.service
self._backend = self._service.backend(
name=self._session.backend(), instance=self._session._instance
)
return
elif session is not None:
raise ValueError("session must be of type Session or None")

if isinstance(backend, IBMBackend):
self._service = backend.service
self._backend = backend
elif isinstance(backend, str):
self._service = (
QiskitRuntimeService()
if QiskitRuntimeService.global_service is None
else QiskitRuntimeService.global_service
)
self._backend = self._service.backend(backend)
elif get_cm_session():
self._session = get_cm_session()
self._service = self._session.service
self._backend = self._service.backend(
name=self._session.backend(), instance=self._session._instance
)
else:
self._service = (
QiskitRuntimeService()
if QiskitRuntimeService.global_service is None
else QiskitRuntimeService.global_service
)
if self._service.channel != "ibm_cloud":
raise ValueError(
"A backend or session must be specified when not using ibm_cloud channel."
)

def _run_primitive(self, primitive_inputs: Dict, user_kwargs: Dict) -> RuntimeJob:
"""Run the primitive.

Args:
primitive_inputs: Inputs to pass to the primitive.
user_kwargs: Individual options to overwrite the default primitive options.

Returns:
Submitted job.
"""
logger.debug("Merging current options %s with %s", self.options, user_kwargs)
combined = merge_options(self.options, user_kwargs)

self._validate_options(combined)

primitive_inputs.update(self._OPTIONS_CLASS._get_program_inputs(combined))
runtime_options = self._OPTIONS_CLASS._get_runtime_options(combined)

if self._backend and combined["transpilation"]["skip_transpilation"]:
for circ in primitive_inputs["circuits"]:
self._backend.check_faulty(circ)

logger.info("Submitting job using options %s", combined)

if self._session:
return self._session.run(
program_id=self._program_id(),
inputs=primitive_inputs,
options=runtime_options,
callback=combined.get("environment", {}).get("callback", None),
result_decoder=DEFAULT_DECODERS.get(self._program_id()),
)

if self._backend:
runtime_options["backend"] = self._backend.name
if "instance" not in runtime_options:
runtime_options["instance"] = self._backend._instance

return self._service.run(
program_id=self._program_id(),
options=runtime_options,
inputs=primitive_inputs,
callback=combined.get("environment", {}).get("callback", None),
result_decoder=DEFAULT_DECODERS.get(self._program_id()),
)

@property
def session(self) -> Optional[Session]:
"""Return session used by this primitive.

Returns:
Session used by this primitive, or ``None`` if session is not used.
"""
return self._session

def set_options(self, **fields: Any) -> None:
"""Set options values for the sampler.

Args:
**fields: The fields to update the options
"""
self.options = self._OPTIONS_CLASS( # pylint: disable=attribute-defined-outside-init
**merge_options(self.options, fields)
)

@abstractmethod
def _validate_options(self, options: dict) -> None:
"""Validate that program inputs (options) are valid

Raises:
ValueError: if resilience_level is out of the allowed range.
"""
raise NotImplementedError()

@classmethod
@abstractmethod
def _program_id(cls) -> str:
"""Return the program ID."""
raise NotImplementedError()


class BasePrimitiveV1(ABC):
"""Base class for Qiskit Runtime primitives."""

version = 1

def __init__(
self,
backend: Optional[Union[str, IBMBackend]] = None,
Expand Down Expand Up @@ -83,7 +251,7 @@ def __init__(
else:
options_copy = copy.deepcopy(options)
default_options = asdict(Options())
self._options = Options._merge_options(default_options, options_copy)
self._options = merge_options(default_options, options_copy)

if isinstance(session, Session):
self._session = session
Expand Down Expand Up @@ -137,6 +305,7 @@ def _run_primitive(self, primitive_inputs: Dict, user_kwargs: Dict) -> RuntimeJo
Returns:
Submitted job.
"""
logger.debug("Merging current options %s with %s", self._options, user_kwargs)
combined = Options._merge_options(self._options, user_kwargs)

if self._backend:
Expand Down Expand Up @@ -198,7 +367,6 @@ def session(self) -> Optional[Session]:
@property
def options(self) -> TerraOptions:
"""Return options values for the sampler.

Returns:
options
"""
Expand All @@ -210,7 +378,9 @@ def set_options(self, **fields: Any) -> None:
Args:
**fields: The fields to update the options
"""
self._options = Options._merge_options(self._options, fields)
self._options = merge_options( # pylint: disable=attribute-defined-outside-init
self._options, fields
)

@abstractmethod
def _validate_options(self, options: dict) -> None:
Expand Down
Loading
Loading