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

Add job_tags and max_execution_time to options #510

Closed
wants to merge 17 commits into from
6 changes: 4 additions & 2 deletions qiskit_ibm_runtime/ibm_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,10 @@ def run(
"""
# pylint: disable=arguments-differ
options = {"backend_name": self.name}
if job_tags:
options["job_tags"] = job_tags
if max_execution_time:
options["max_execution_time"] = max_execution_time
inputs = {"circuits": circuits}
if shots:
inputs["shots"] = shots
Expand Down Expand Up @@ -573,8 +577,6 @@ def run(
result_decoder=result_decoder,
instance=instance,
session_id=session_id,
job_tags=job_tags,
max_execution_time=max_execution_time,
start_session=start_session,
)

Expand Down
11 changes: 10 additions & 1 deletion qiskit_ibm_runtime/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class EnvironmentOptions:
log_level: str = "WARNING"
image: Optional[str] = None
instance: Optional[str] = None
job_tags: Optional[List[str]] = None


@_flexible
Expand All @@ -168,6 +169,9 @@ class Options:
"""Options for the primitive programs.

Args:
max_execution_time: Maximum execution time in seconds. If
a job exceeds this time limit, it is forcibly cancelled.

optimization_level: How much optimization to perform on the circuits.
Higher levels generate more optimized circuits,
at the expense of longer transpilation times.
Expand Down Expand Up @@ -263,8 +267,13 @@ class Options:
* instance: The hub/group/project to use, in that format. This is only supported
for ``ibm_quantum`` channel. If ``None``, a hub/group/project that provides
access to the target backend is randomly selected.

* job_tags: Tags to be assigned to the job. The tags can subsequently be used
as a filter in the :meth:`qiskit_ibm_runtime.qiskit_runtime_service.jobs()`
function call.
"""

max_execution_time: int = None
optimization_level: int = 1
resilience_level: int = 0
transpilation: Union[TranspilationOptions, Dict] = field(
Expand Down Expand Up @@ -344,7 +353,7 @@ def _get_runtime_options(options: Dict) -> Dict:
for fld in fields(RuntimeOptions):
if fld.name in environment:
out[fld.name] = environment[fld.name]

out["max_execution_time"] = options.get("max_execution_time")
return out

def _merge_options(self, new_options: Optional[Dict] = None) -> Dict:
Expand Down
20 changes: 9 additions & 11 deletions qiskit_ibm_runtime/qiskit_runtime_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
from .utils import RuntimeDecoder, to_base64_string, to_python_identifier
from .utils.backend_decoder import configuration_from_server_data
from .utils.hgp import to_instance_format, from_instance_format
from .utils.utils import validate_job_tags
from .api.client_parameters import ClientParameters
from .runtime_options import RuntimeOptions
from .utils.deprecation import (
Expand Down Expand Up @@ -854,8 +853,6 @@ def run(
result_decoder: Optional[Type[ResultDecoder]] = None,
instance: Optional[str] = None,
session_id: Optional[str] = None,
job_tags: Optional[List[str]] = None,
max_execution_time: Optional[int] = None,
start_session: Optional[bool] = False,
) -> RuntimeJob:
"""Execute the runtime program.
Expand All @@ -873,6 +870,10 @@ def run(
* 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``.
* 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. If
a job exceeds this time limit, it is forcibly cancelled.

callback: Callback function to be invoked for any interim results and final result.
The callback function will receive 2 positional parameters:
Expand All @@ -885,10 +886,6 @@ def run(
instance: This is only supported for ``ibm_quantum`` runtime and is in the
hub/group/project format.
session_id: Job ID of the first job in a runtime session.
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. This overrides
the max_execution_time of the program and cannot exceed it.
start_session: Set to True to explicitly start a runtime session. Defaults to False.

Returns:
Expand All @@ -905,7 +902,6 @@ def run(
DeprecationWarning,
stacklevel=2,
)
validate_job_tags(job_tags, IBMInputValueError)

qrt_options: RuntimeOptions = options
if options is None:
Expand All @@ -932,7 +928,9 @@ def run(
hgp_name = None
if self._channel == "ibm_quantum":
# Find the right hgp
hgp = self._get_hgp(instance=instance, backend_name=qrt_options.backend)
hgp = self._get_hgp(
instance=qrt_options.instance, backend_name=qrt_options.backend
)
backend = hgp.backend(qrt_options.backend)
hgp_name = hgp.name

Expand All @@ -946,8 +944,8 @@ def run(
hgp=hgp_name,
log_level=qrt_options.log_level,
session_id=session_id,
job_tags=job_tags,
max_execution_time=max_execution_time,
job_tags=qrt_options.job_tags,
max_execution_time=qrt_options.max_execution_time,
start_session=start_session,
)
except RequestsApiError as ex:
Expand Down
15 changes: 14 additions & 1 deletion qiskit_ibm_runtime/runtime_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
import re
import logging
from dataclasses import dataclass
from typing import Optional
from typing import Optional, List

from qiskit.utils.deprecation import deprecate_arguments

from .exceptions import IBMInputValueError
from .utils.deprecation import issue_deprecation_msg
from .utils.utils import validate_job_tags


@dataclass(init=False)
Expand All @@ -31,6 +32,8 @@ class RuntimeOptions:
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

@deprecate_arguments({"backend_name": "backend"})
def __init__(
Expand All @@ -39,6 +42,8 @@ def __init__(
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,
) -> None:
"""RuntimeOptions constructor.

Expand All @@ -53,11 +58,17 @@ def __init__(
instance: The hub/group/project to use, in that format. This is only supported
for ``ibm_quantum`` channel. If ``None``, a hub/group/project that provides
access to the target backend is randomly selected.
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. If
a job exceeds this time limit, it is forcibly cancelled.
"""
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

def validate(self, channel: str) -> None:
"""Validate options.
Expand Down Expand Up @@ -91,6 +102,8 @@ def validate(self, channel: str) -> None:
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, IBMInputValueError)

@property
def backend_name(self) -> str:
Expand Down
1 change: 0 additions & 1 deletion qiskit_ibm_runtime/runtime_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ def _run(self, inputs: Union[Dict, ParameterNamespace]) -> RuntimeJob:
inputs=inputs,
session_id=self._session_id,
start_session=self._start_session,
max_execution_time=self._max_time,
)

@_active_session
Expand Down
8 changes: 4 additions & 4 deletions qiskit_ibm_runtime/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,21 +137,21 @@ def run(
Returns:
Submitted job.
"""

# TODO: Cache data when server supports it.

# TODO: Do we really need to specify a None max time if session has started?
max_time = self._max_time if not self._session_id else None
options = options or {}
options["backend"] = self._backend
# TODO: Do we really need to specify a None max time if session has started?
max_time = self._max_time if not self._session_id else None
if max_time:
options["max_execution_time"] = max_time

job = self._service.run(
program_id=program_id,
options=options,
inputs=inputs,
session_id=self._session_id,
start_session=self._session_id is None,
max_execution_time=max_time,
callback=callback,
result_decoder=result_decoder,
)
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/add-options-26c127fcd40623a5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
upgrade:
- |
The :class:`qiskit_ibm_runtime.Options` class now accepts
``max_execution_time`` as a first level option and ``job_tags`` as an option under `environment`.
:class:`qiskit_ibm_runtime.RuntimeOptions` has also been updated to include these two parameters.
9 changes: 6 additions & 3 deletions test/ibm_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,16 @@ def _run_program(
backend_name = (
backend if backend is not None else self.sim_backends[service.channel]
)
options = {"backend": backend_name, "log_level": log_level}
options = {
"backend": backend_name,
"log_level": log_level,
"job_tags": job_tags,
"max_execution_time": max_execution_time,
}
job = service.run(
program_id=pid,
inputs=inputs,
options=options,
job_tags=job_tags,
max_execution_time=max_execution_time,
session_id=session_id,
callback=callback,
start_session=start_session,
Expand Down
10 changes: 7 additions & 3 deletions test/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ def run_program(
):
"""Run a program."""
backend_name = backend_name if backend_name is not None else "common_backend"
options = {"backend": backend_name, "image": image, "log_level": log_level}
options = {
"backend": backend_name,
"image": image,
"log_level": log_level,
"job_tags": job_tags,
"max_execution_time": max_execution_time,
}
if final_status is not None:
service._api_client.set_final_status(final_status)
elif job_classes:
Expand All @@ -99,8 +105,6 @@ def run_program(
inputs=inputs,
result_decoder=decoder,
instance=instance,
job_tags=job_tags,
max_execution_time=max_execution_time,
session_id=session_id,
)
job._creation_date = datetime.now(timezone.utc)
Expand Down