diff --git a/qiskit_ibm_runtime/__init__.py b/qiskit_ibm_runtime/__init__.py index 22d808464..18769d6a6 100644 --- a/qiskit_ibm_runtime/__init__.py +++ b/qiskit_ibm_runtime/__init__.py @@ -206,7 +206,6 @@ from .ibm_backend import IBMBackend from .runtime_job import RuntimeJob from .runtime_job_v2 import RuntimeJobV2 -from .runtime_options import RuntimeOptions from .utils.json import RuntimeEncoder, RuntimeDecoder from .session import Session # pylint: disable=cyclic-import from .batch import Batch # pylint: disable=cyclic-import diff --git a/qiskit_ibm_runtime/fake_provider/local_service.py b/qiskit_ibm_runtime/fake_provider/local_service.py index 0e78dcccc..bcb562af7 100644 --- a/qiskit_ibm_runtime/fake_provider/local_service.py +++ b/qiskit_ibm_runtime/fake_provider/local_service.py @@ -18,8 +18,7 @@ import copy import logging import warnings -from dataclasses import asdict -from typing import Callable, Dict, List, Literal, Optional, Union +from typing import Callable, Dict, List, Literal, Optional from qiskit.primitives import ( BackendEstimatorV2, @@ -34,7 +33,6 @@ from .fake_provider import FakeProviderForBackendV2 # pylint: disable=unused-import, cyclic-import from .local_runtime_job import LocalRuntimeJob from ..ibm_backend import IBMBackend -from ..runtime_options import RuntimeOptions logger = logging.getLogger(__name__) @@ -148,7 +146,7 @@ def _run( self, program_id: Literal["sampler", "estimator"], inputs: Dict, - options: Union[RuntimeOptions, Dict], + options: Dict, ) -> PrimitiveJob: """Execute the runtime program. @@ -165,10 +163,7 @@ def _run( ValueError: If input is invalid. NotImplementedError: If using V2 primitives. """ - if isinstance(options, Dict): - qrt_options = copy.deepcopy(options) - else: - qrt_options = asdict(options) + qrt_options = copy.deepcopy(options) backend = qrt_options.pop("backend", None) diff --git a/qiskit_ibm_runtime/options/options.py b/qiskit_ibm_runtime/options/options.py index 1fba169fd..bfd068a14 100644 --- a/qiskit_ibm_runtime/options/options.py +++ b/qiskit_ibm_runtime/options/options.py @@ -14,7 +14,7 @@ from abc import abstractmethod from typing import Iterable, Tuple, Union, Any -from dataclasses import dataclass, fields, asdict, is_dataclass +from dataclasses import dataclass, asdict, is_dataclass import copy from qiskit.transpiler import CouplingMap @@ -31,7 +31,6 @@ ) from .environment_options import EnvironmentOptions from .simulator_options import SimulatorOptions -from ..runtime_options import RuntimeOptions def _make_data_row(indent: int, name: str, value: Any, is_section: bool) -> Iterable[str]: @@ -93,9 +92,8 @@ def _get_runtime_options(options: dict) -> dict: environment = options_copy.get("environment") or {} out = {"max_execution_time": options_copy.get("max_execution_time", None)} - for fld in fields(RuntimeOptions): - if fld.name in environment: - out[fld.name] = environment[fld.name] + for fld in environment: + out[fld] = environment[fld] if "image" in options_copy: out["image"] = options_copy["image"] diff --git a/qiskit_ibm_runtime/qiskit_runtime_service.py b/qiskit_ibm_runtime/qiskit_runtime_service.py index cb61242c9..47599856b 100644 --- a/qiskit_ibm_runtime/qiskit_runtime_service.py +++ b/qiskit_ibm_runtime/qiskit_runtime_service.py @@ -37,7 +37,6 @@ from .runtime_job_v2 import RuntimeJobV2 from .utils import validate_job_tags from .api.client_parameters import ClientParameters -from .runtime_options import RuntimeOptions from .ibm_backend import IBMBackend from .models import QasmBackendConfiguration @@ -842,7 +841,7 @@ def _run( self, program_id: str, inputs: Dict, - options: Optional[Union[RuntimeOptions, Dict]] = None, + options: Optional[Dict] = None, callback: Optional[Callable] = None, result_decoder: Optional[Union[Type[ResultDecoder], Sequence[Type[ResultDecoder]]]] = None, session_id: Optional[str] = None, @@ -877,18 +876,9 @@ def _run( RuntimeProgramNotFound: If the program cannot be found. IBMRuntimeError: An error occurred running the program. """ - - qrt_options: RuntimeOptions = options - if options is None: - qrt_options = RuntimeOptions() - elif isinstance(options, Dict): - qrt_options = RuntimeOptions(**options) - - qrt_options.validate(channel=self.channel) - - backend = qrt_options.backend + backend = options["backend"] if isinstance(backend, str): - backend = self.backend(name=qrt_options.get_backend_name()) + backend = self.backend(name=backend) status = backend.status() if status.operational is True and status.status_msg != "active": @@ -900,16 +890,16 @@ def _run( try: response = self._active_api_client.program_run( program_id=program_id, - backend_name=qrt_options.get_backend_name(), + backend_name=backend.name, params=inputs, - image=qrt_options.image, - log_level=qrt_options.log_level, + image=options.get("image"), + log_level=options.get("log_level"), session_id=session_id, - job_tags=qrt_options.job_tags, - max_execution_time=qrt_options.max_execution_time, + job_tags=options.get("job_tags"), + max_execution_time=options.get("max_execution_time"), start_session=start_session, - session_time=qrt_options.session_time, - private=qrt_options.private, + session_time=options.get("session_time"), + private=options.get("private"), ) except RequestsApiError as ex: @@ -917,7 +907,7 @@ def _run( raise RuntimeProgramNotFound(f"Program not found: {ex.message}") from None raise IBMRuntimeError(f"Failed to run program: {ex}") from None - if response["backend"] and response["backend"] != qrt_options.get_backend_name(): + if response["backend"] and response["backend"] != backend.name: backend = self.backend(name=response["backend"]) return RuntimeJobV2( @@ -927,10 +917,11 @@ def _run( program_id=program_id, user_callback=callback, result_decoder=result_decoder, - image=qrt_options.image, + image=options.get("image"), + tags=options.get("job_tags"), service=self, version=version, - private=qrt_options.private, + private=options.get("private"), ) def job(self, job_id: str) -> Union[RuntimeJob, RuntimeJobV2]: diff --git a/qiskit_ibm_runtime/runtime_options.py b/qiskit_ibm_runtime/runtime_options.py deleted file mode 100644 index 8ad856d58..000000000 --- a/qiskit_ibm_runtime/runtime_options.py +++ /dev/null @@ -1,118 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Runtime options that control the execution environment.""" - -from __future__ import annotations - -import re -import logging -from dataclasses import dataclass -from typing import Optional, List - -from qiskit.providers.backend import Backend - -from .exceptions import IBMInputValueError -from .utils import validate_job_tags - - -@dataclass(init=False) -class RuntimeOptions: - """Class for representing generic runtime execution options.""" - - backend: Optional[str | Backend] = None - image: Optional[str] = None - log_level: Optional[str] = None - instance: Optional[str] = None - job_tags: Optional[List[str]] = None - max_execution_time: Optional[int] = None - session_time: Optional[int] = None - private: Optional[bool] = False - - def __init__( - self, - backend: Optional[str | Backend] = None, - image: Optional[str] = None, - log_level: Optional[str] = None, - instance: Optional[str] = None, - job_tags: Optional[List[str]] = None, - max_execution_time: Optional[int] = None, - session_time: Optional[int] = None, - private: Optional[bool] = False, - ) -> None: - """RuntimeOptions constructor. - - Args: - backend: target backend to run on. - image: the runtime image used to execute the primitive, specified in - the form of ``image_name:tag``. Not all accounts are - authorized to select a different image. - log_level: logging level to set in the execution environment. The valid - log levels are: ``DEBUG``, ``INFO``, ``WARNING``, ``ERROR``, and ``CRITICAL``. - The default level is ``WARNING``. - instance: This is only supported on the new IBM Quantum Platform. - job_tags: Tags to be assigned to the job. The tags can subsequently be used - as a filter in the :meth:`jobs()` function call. - max_execution_time: Maximum execution time in seconds, which is based - on system execution time (not wall clock time). System execution time is the - amount of time that the system is dedicated to processing your job. If a job exceeds - this time limit, it is forcibly cancelled. Simulator jobs continue to use wall - clock time. - session_time: Length of session in seconds. - private: Boolean that indicates whether the job is marked as private. When set to true, - input parameters are not returned, and the results can only be read once. - After the job is completed, input parameters are deleted from the service. - After the results are read, these are also deleted from the service. - When set to false, the input parameters and results follow the - standard retention behavior of the API. - """ - self.backend = backend - self.image = image - self.log_level = log_level - self.instance = instance - self.job_tags = job_tags - self.max_execution_time = max_execution_time - self.session_time = session_time - self.private = private - - def validate(self, channel: str) -> None: # pylint: disable=unused-argument - """Validate options. - - Args: - channel: channel type. - - Raises: - IBMInputValueError: If one or more option is invalid. - """ - - if self.image and not re.match( - "[a-zA-Z0-9]+([/.\\-_][a-zA-Z0-9]+)*:[a-zA-Z0-9]+([.\\-_][a-zA-Z0-9]+)*$", - self.image, - ): - raise IBMInputValueError('"image" needs to be in form of image_name:tag') - - if self.log_level and not isinstance(logging.getLevelName(self.log_level.upper()), int): - raise IBMInputValueError( - f"{self.log_level} is not a valid log level. The valid log levels are: `DEBUG`, " - f"`INFO`, `WARNING`, `ERROR`, and `CRITICAL`." - ) - - if self.job_tags: - validate_job_tags(self.job_tags) - - def get_backend_name(self) -> str: - """Get backend name.""" - if isinstance(self.backend, str): - return self.backend - if self.backend: - return self.backend.name if self.backend.version == 2 else self.backend.name() - return None diff --git a/test/unit/test_options.py b/test/unit/test_options.py index f4cec3e92..9c9a5dad2 100644 --- a/test/unit/test_options.py +++ b/test/unit/test_options.py @@ -20,7 +20,6 @@ from qiskit.transpiler import CouplingMap from qiskit_aer.noise import NoiseModel -from qiskit_ibm_runtime import RuntimeOptions from qiskit_ibm_runtime.options import EstimatorOptions, SamplerOptions from qiskit_ibm_runtime.fake_provider import FakeManilaV2, FakeNairobiV2 @@ -32,26 +31,6 @@ class TestOptionsV2(IBMTestCase): """Class for testing the v2 Options class.""" - @data(EstimatorOptions, SamplerOptions) - def test_runtime_options(self, opt_cls): - """Test converting runtime options.""" - full_options = RuntimeOptions( - backend="ibm_gotham", - image="foo:bar", - log_level="DEBUG", - instance="crn", - job_tags=["foo", "bar"], - max_execution_time=600, - ) - partial_options = RuntimeOptions(backend="foo", log_level="DEBUG") - - for rt_options in [full_options, partial_options]: - with self.subTest(rt_options=rt_options): - self.assertGreaterEqual( - vars(rt_options).items(), - opt_cls._get_runtime_options(vars(rt_options)).items(), - ) - @data(EstimatorOptions, SamplerOptions) def test_kwargs_options(self, opt_cls): """Test specifying arbitrary options."""