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

Update from_id() #1311

Merged
merged 14 commits into from
Apr 9, 2024
6 changes: 1 addition & 5 deletions qiskit_ibm_runtime/api/rest/runtime_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,4 @@ def close(self) -> None:

def details(self) -> Dict[str, Any]:
"""Return the details of this session."""
try:
return self.session.get(self.get_url("self")).json()
# return None if API is not supported
except: # pylint: disable=bare-except
return None
return self.session.get(self.get_url("self")).json()
33 changes: 27 additions & 6 deletions qiskit_ibm_runtime/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
from qiskit.providers.backend import BackendV1, BackendV2

from qiskit_ibm_runtime import QiskitRuntimeService
from .exceptions import IBMInputValueError
from .runtime_job import RuntimeJob
from .runtime_job_v2 import RuntimeJobV2
from .utils.result_decoder import ResultDecoder
from .ibm_backend import IBMBackend
from .utils.default_session import set_cm_session
from .utils.deprecation import deprecate_arguments, issue_deprecation_msg
from .utils.deprecation import issue_deprecation_msg
from .utils.converters import hms_to_seconds
from .fake_provider.local_service import QiskitRuntimeLocalService

Expand Down Expand Up @@ -286,6 +287,7 @@ def details(self) -> Optional[Dict[str, Any]]:
started_at: Timestamp of when the session was started.
closed_at: Timestamp of when the session was closed.
activated_at: Timestamp of when the session state was changed to active.
mode: Execution mode of the session.
usage_time: The usage time, in seconds, of this Session or Batch.
Usage is defined as the time a quantum system is committed to complete a job.
"""
Expand All @@ -305,6 +307,7 @@ def details(self) -> Optional[Dict[str, Any]]:
"started_at": response.get("started_at"),
"closed_at": response.get("closed_at"),
"activated_at": response.get("activated_at"),
"mode": response.get("mode"),
"usage_time": response.get("elapsed_time"),
}
return None
Expand Down Expand Up @@ -332,23 +335,41 @@ def from_id(
cls,
session_id: str,
service: Optional[QiskitRuntimeService] = None,
backend: Optional[Union[str, IBMBackend]] = None,
) -> "Session":
"""Construct a Session object with a given session_id

Args:
session_id: the id of the session to be created. This must be an already
existing session id.
service: instance of the ``QiskitRuntimeService`` class.
kt474 marked this conversation as resolved.
Show resolved Hide resolved
backend: instance of :class:`qiskit_ibm_runtime.IBMBackend` class or
string name of backend.
If ``None``, ``QiskitRuntimeService()`` is used to initialize your default saved account.

Raises:
IBMInputValueError: If given `session_id` does not exist.

Returns:
A new Session with the given ``session_id``

"""
if backend:
deprecate_arguments("backend", "0.15.0", "Sessions do not support multiple backends.")
if not service:
warnings.warn(
(
"The `service` parameter will be required in a future release no sooner than "
"3 months after the release of qiskit-ibm-runtime 0.23.0 ."
),
DeprecationWarning,
stacklevel=2,
)
service = QiskitRuntimeService()

response = service._api_client.session_details(session_id)
backend = response.get("backend_name")
mode = response.get("mode")
class_name = "dedicated" if cls.__name__.lower() == "session" else cls.__name__.lower()
if mode != class_name:
raise IBMInputValueError(
f"Input ID {session_id} has execution mode {mode} instead of {class_name}."
)

session = cls(service, backend)
session._session_id = session_id
Expand Down
24 changes: 16 additions & 8 deletions test/integration/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"""Integration tests for Session."""

import warnings
from unittest import SkipTest

from qiskit.circuit.library import RealAmplitudes
from qiskit.quantum_info import SparsePauliOp
Expand All @@ -21,7 +22,8 @@
from qiskit.result import Result
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_ibm_runtime import Estimator, Session, Sampler, Options
from qiskit_ibm_runtime import Estimator, Session, Sampler, Options, Batch
from qiskit_ibm_runtime.exceptions import IBMInputValueError

from ..utils import bell
from ..decorators import run_integration_test, quantum_only
Expand Down Expand Up @@ -95,15 +97,21 @@ def test_using_correct_instance(self, service):
@run_integration_test
def test_session_from_id(self, service):
"""Test creating a session from a given id"""
backend = service.backend("ibmq_qasm_simulator")
try:
backend = service.backend("fake_backend1")
except:
raise SkipTest("No proper backends available")
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(bell())
with Session(service, backend=backend) as session:
sampler = Sampler(session=session)
job = sampler.run(bell(), shots=400)
session_id = job.session_id
new_session = Session.from_id(backend=backend, session_id=session_id)
sampler = Sampler(session=new_session)
job = sampler.run(bell(), shots=400)
self.assertEqual(session_id, job.session_id)
sampler.run(isa_circuit)

new_session = Session.from_id(session_id=session._session_id, service=service)
self.assertEqual(session._session_id, new_session._session_id)

with self.assertRaises(IBMInputValueError):
Batch.from_id(session_id=session._session_id, service=service)


class TestBackendRunInSession(IBMIntegrationTestCase):
Expand Down
4 changes: 4 additions & 0 deletions test/unit/mock/fake_runtime_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,10 @@ def close_session(self, session_id: str) -> None:
raise ValueError(f"Session {session_id} not found.")
self._sessions.remove(session_id)

def session_details(self, session_id: str) -> Dict[str, Any]:
"""Return the details of the session."""
return {"id": session_id, "mode": "dedicated", "backend_name": "common_backend"}

def _find_backend(self, backend_name):
for back in self._backends:
if back.name == backend_name:
Expand Down
2 changes: 1 addition & 1 deletion test/unit/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def test_global_service(self):

def test_session_from_id(self):
"""Create session with given session_id"""
service = MagicMock()
service = FakeRuntimeService(channel="ibm_quantum", token="abc")
session_id = "123"
session = Session.from_id(session_id=session_id, service=service)
session.run(program_id="foo", inputs={})
Expand Down