Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f5eecd7
Moved provider session from qiskit_ibm_provider to here
merav-aharoni Dec 11, 2023
07e8e10
black
merav-aharoni Dec 11, 2023
0877423
Added import for Session in IBMBackend
merav-aharoni Dec 12, 2023
3f899f6
Changed use of session from qiskit_ibm_provider to Session here
merav-aharoni Dec 12, 2023
d25e219
Removed warnings about 2 different session types
merav-aharoni Dec 12, 2023
167e05d
Cleaning up
merav-aharoni Dec 12, 2023
2d92d1b
Fixed issues related to opening Session with backend.open_session()
merav-aharoni Dec 12, 2023
b899dfe
Removed session.py that was copied from qiskit_ibm_provider
merav-aharoni Dec 12, 2023
b7c83d2
disable cyclic import message
merav-aharoni Dec 12, 2023
8274905
Merge branch 'main' into unite_sessions
merav-aharoni Dec 12, 2023
610d78d
Release note
merav-aharoni Dec 14, 2023
aa5349f
Merge branch 'unite_sessions' of github.com:merav-aharoni/qiskit-ibm-…
merav-aharoni Dec 14, 2023
3eb50f2
Merge branch 'main' into unite_sessions
merav-aharoni Dec 14, 2023
7beb8e8
Merge branch 'main' into unite_sessions
merav-aharoni Dec 19, 2023
422f7b7
Merge branch 'main' into unite_sessions
kt474 Dec 20, 2023
5b930ef
Merge branch 'main' into unite_sessions
merav-aharoni Dec 20, 2023
eeabf5b
Merge branch 'main' into unite_sessions
kt474 Dec 20, 2023
8f6f69a
Added test
merav-aharoni Dec 27, 2023
40b4501
Merge branch 'main' into unite_sessions
kt474 Jan 30, 2024
3a4af3a
Merge branch 'main' into unite_sessions
kt474 Jan 31, 2024
4e072fe
Merge branch 'main' into unite_sessions
kt474 Jan 31, 2024
592bcdf
update integration tests & allow non session backend.run
kt474 Jan 31, 2024
0924318
Merge branch 'main' into unite_sessions
kt474 Feb 2, 2024
3dd7230
fix lint
kt474 Feb 2, 2024
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: 1 addition & 8 deletions qiskit_ibm_runtime/base_primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@
import copy
import logging
from dataclasses import asdict
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 .runtime_job import RuntimeJob
Expand Down Expand Up @@ -98,6 +95,7 @@ def __init__(
if isinstance(backend, IBMBackend):
self._service = backend.service
self._backend = backend
self._session = self._backend.session
elif isinstance(backend, str):
self._service = (
QiskitRuntimeService()
Expand All @@ -121,11 +119,6 @@ def __init__(
raise ValueError(
"A backend or session must be specified when not using ibm_cloud channel."
)
# Check if initialized within a IBMBackend session. If so, issue a warning.
if get_cm_provider_session():
warnings.warn(
"A Backend.run() session is open but Primitives will not be run within this session"
)

def _run_primitive(self, primitive_inputs: Dict, user_kwargs: Dict) -> RuntimeJob:
"""Run the primitive.
Expand Down
35 changes: 16 additions & 19 deletions qiskit_ibm_runtime/ibm_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,6 @@
MeasureChannel,
)
from qiskit.transpiler.target import Target

# temporary until we unite the 2 Session classes
from qiskit_ibm_provider.session import (
Session as ProviderSession,
) # temporary until we unite the 2 Session classes

from .utils.utils import validate_job_tags
from . import qiskit_runtime_service # pylint: disable=unused-import,cyclic-import
from .runtime_job import RuntimeJob
Expand All @@ -54,7 +48,8 @@
from .utils.backend_converter import (
convert_to_target,
)
from .utils.default_session import get_cm_session as get_cm_primitive_session
from .session import Session # pylint: disable=cyclic-import
from .utils.default_session import get_cm_session
from .utils.backend_decoder import (
defaults_from_server_data,
properties_from_server_data,
Expand Down Expand Up @@ -194,7 +189,8 @@ def __init__(
self._defaults = None
self._target = None
self._max_circuits = configuration.max_experiments
self._session: ProviderSession = None
self._session: Session = None

if (
not self._configuration.simulator
and hasattr(self.options, "noise_model")
Expand Down Expand Up @@ -582,6 +578,7 @@ def __deepcopy__(self, _memo: dict = None) -> "IBMBackend":
def run(
self,
circuits: Union[QuantumCircuit, str, List[Union[QuantumCircuit, str]]],
session: Session = None,
dynamic: bool = None,
job_tags: Optional[List[str]] = None,
init_circuit: Optional[QuantumCircuit] = None,
Expand Down Expand Up @@ -731,6 +728,7 @@ def run(
run_config_dict["circuits"] = circuits

return self._runtime_run(
session=session,
program_id=program_id,
inputs=run_config_dict,
backend_name=self.name,
Expand All @@ -743,6 +741,7 @@ def _runtime_run(
program_id: str,
inputs: Dict,
backend_name: str,
session: Optional[Session] = None,
job_tags: Optional[List[str]] = None,
image: Optional[str] = None,
) -> RuntimeJob:
Expand All @@ -751,14 +750,12 @@ def _runtime_run(
if self._service._channel == "ibm_quantum":
hgp_name = self._instance or self._service._get_hgp().name

# Check if initialized within a Primitive session. If so, issue a warning.
if get_cm_primitive_session():
warnings.warn(
"A Primitive session is open but Backend.run() jobs will not be run within this session"
)
if self._session:
if not self._session.active:
raise RuntimeError(f"The session {self._session.session_id} is closed.")
if session:
self._session = session
elif get_cm_session():
self._session = get_cm_session()
Comment thread
kt474 marked this conversation as resolved.

if self._session and self._session._active:
session_id = self._session.session_id
start_session = session_id is None
max_session_time = self._session._max_time
Expand Down Expand Up @@ -829,13 +826,13 @@ def _get_run_config(self, program_id: str, **kwargs: Any) -> Dict:
run_config_dict[key] = backend_options[key]
return run_config_dict

def open_session(self, max_time: Optional[Union[int, str]] = None) -> ProviderSession:
def open_session(self, max_time: Optional[Union[int, str]] = None) -> Session:
"""Open session"""
self._session = ProviderSession(max_time=max_time)
self._session = Session(backend=self, max_time=max_time)
return self._session

@property
def session(self) -> ProviderSession:
def session(self) -> Session:
"""Return session"""
return self._session

Expand Down
24 changes: 12 additions & 12 deletions qiskit_ibm_runtime/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
from functools import wraps
from threading import Lock

from qiskit_ibm_runtime import QiskitRuntimeService
from . import qiskit_runtime_service # pylint: disable=cyclic-import
from .runtime_job import RuntimeJob
from .utils.result_decoder import ResultDecoder
from .ibm_backend import IBMBackend
from . import ibm_backend
from .utils.default_session import set_cm_session
from .utils.deprecation import deprecate_arguments
from .utils.converters import hms_to_seconds
Expand Down Expand Up @@ -75,8 +75,8 @@ class Session:

def __init__(
self,
service: Optional[QiskitRuntimeService] = None,
backend: Optional[Union[str, IBMBackend]] = None,
service: Optional["qiskit_runtime_service.QiskitRuntimeService"] = None,
backend: Optional[Union[str, "ibm_backend.IBMBackend"]] = None,
max_time: Optional[Union[int, str]] = None,
): # pylint: disable=line-too-long
"""Session constructor.
Expand All @@ -102,13 +102,13 @@ def __init__(
"""

if service is None:
if isinstance(backend, IBMBackend):
if isinstance(backend, ibm_backend.IBMBackend):
self._service = backend.service
else:
self._service = (
QiskitRuntimeService()
if QiskitRuntimeService.global_service is None
else QiskitRuntimeService.global_service
qiskit_runtime_service.QiskitRuntimeService()
if qiskit_runtime_service.QiskitRuntimeService.global_service is None
else qiskit_runtime_service.QiskitRuntimeService.global_service
)

else:
Expand All @@ -118,7 +118,7 @@ def __init__(
raise ValueError('"backend" is required for ``ibm_quantum`` channel.')

self._instance = None
if isinstance(backend, IBMBackend):
if isinstance(backend, ibm_backend.IBMBackend):
self._instance = backend._instance
backend = backend.name
self._backend = backend
Expand Down Expand Up @@ -287,7 +287,7 @@ def session_id(self) -> str:
return self._session_id

@property
def service(self) -> QiskitRuntimeService:
def service(self) -> "qiskit_runtime_service.QiskitRuntimeService":
"""Return service associated with this session.

Returns:
Expand All @@ -299,8 +299,8 @@ def service(self) -> QiskitRuntimeService:
def from_id(
cls,
session_id: str,
service: Optional[QiskitRuntimeService] = None,
backend: Optional[Union[str, IBMBackend]] = None,
service: Optional["qiskit_runtime_service.QiskitRuntimeService"] = None,
backend: Optional[Union[str, "ibm_backend.IBMBackend"]] = None,
) -> "Session":
"""Construct a Session object with a given session_id

Expand Down
18 changes: 18 additions & 0 deletions releasenotes/notes/unite_sessions-bc69cc9d4c37d551.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
features:
- |
Session in `qiskit_ibm_runtime` and in `qiskit_ibm_provider` are now the same.
It is possible now to run a primitive and backend.run() in the same session.
For example:

.. code-block::

backend = self.service.get_backend("ibmq_qasm_simulator")
with Session(backend=backend) as session:
sampler = Sampler(session=session)
job1 = sampler.run(circuits=ReferenceCircuits.bell())
job2 = backend.run(circuits=ReferenceCircuits.bell(), session=session)
# both jobs will be run in the same session
print(job1.session_id)
print(job2.session_id)

104 changes: 77 additions & 27 deletions test/integration/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

"""Integration tests for Session."""

import warnings

from qiskit.circuit.library import RealAmplitudes
from qiskit.quantum_info import SparsePauliOp
Expand Down Expand Up @@ -106,21 +105,23 @@ def test_session_from_id(self, service):
class TestBackendRunInSession(IBMIntegrationTestCase):
"""Integration tests for Backend.run in Session."""

def test_session_id(self):
@run_integration_test
def test_session_id(self, service):
"""Test that session_id is updated correctly and maintained throughout the session"""
backend = self.service.get_backend("ibmq_qasm_simulator")
backend = service.get_backend("ibmq_qasm_simulator")
backend.open_session()
self.assertEqual(backend.session.session_id, None)
self.assertTrue(backend.session.active)
self.assertTrue(backend.session._active)
job1 = backend.run(bell())
self.assertEqual(job1._session_id, job1.job_id())
job2 = backend.run(bell())
self.assertFalse(job2._session_id == job2.job_id())

def test_backend_run_with_session(self):
@run_integration_test
def test_backend_run_with_session(self, service):
"""Test that 'shots' parameter is transferred correctly"""
shots = 1000
backend = self.service.backend("ibmq_qasm_simulator")
backend = service.backend("ibmq_qasm_simulator")
backend.open_session()
result = backend.run(circuits=bell(), shots=shots).result()
backend.cancel_session()
Expand All @@ -130,44 +131,79 @@ def test_backend_run_with_session(self):
result.get_counts()["00"], result.get_counts()["11"], delta=shots / 10
)

def test_backend_and_primitive_in_session(self):
"""Test Sampler.run and backend.run in the same session."""
backend = self.service.get_backend("ibmq_qasm_simulator")
@run_integration_test
def test_backend_run_session_param(self, service):
"""Test Sampler.run and backend.run in the same session.
Session is defined as parameter"""
backend = service.get_backend("ibmq_qasm_simulator")
with Session(backend=backend) as session:
sampler = Sampler(session=session)
job1 = sampler.run(circuits=bell())
with warnings.catch_warnings(record=True):
job2 = backend.run(circuits=bell())
job2 = backend.run(circuits=bell(), session=session)
self.assertEqual(job1.session_id, job1.job_id())
self.assertIsNone(job2.session_id)
self.assertEqual(job2.session_id, job1.job_id())

@run_integration_test
def test_backend_run_session_context_manager(self, service):
"""Test Sampler.run and backend.run in the same session.
Session is defined in context manager"""
backend = service.get_backend("ibmq_qasm_simulator")
with Session(backend=backend) as session:
sampler = Sampler(session=session)
job1 = sampler.run(circuits=bell())
job2 = backend.run(circuits=bell())
self.assertEqual(session.session_id, job1.job_id())
self.assertEqual(job2.session_id, session.session_id)

@run_integration_test
def test_backend_open_session(self, service):
"""Test Sampler.run and backend.run in the same session.
Session is created by open_session."""
backend = service.get_backend("ibmq_qasm_simulator")
with backend.open_session() as session:
sampler = Sampler(backend=backend, session=session)
job1 = backend.run(bell())
job2 = sampler.run(circuits=bell())
session_id = session.session_id
self.assertEqual(session_id, job1.job_id())
self.assertEqual(job2.session_id, session_id)

@run_integration_test
def test_backend_open_session_with_context_manager(self, service):
"""Test Sampler.run and backend.run in the same session.
Session is created by open_session. Sampler gets
Session from backend without parameter"""
backend = service.get_backend("ibmq_qasm_simulator")
with backend.open_session() as session:
with warnings.catch_warnings(record=True):
sampler = Sampler(backend=backend)
sampler = Sampler(backend=backend)
job1 = backend.run(bell())
job2 = sampler.run(circuits=bell())
session_id = session.session_id
self.assertEqual(session_id, job1.job_id())
self.assertIsNone(job2.session_id)
self.assertEqual(job2.session_id, session_id)

def test_session_cancel(self):
@run_integration_test
def test_session_cancel(self, service):
"""Test closing a session"""
backend = self.service.backend("ibmq_qasm_simulator")
backend = service.backend("ibmq_qasm_simulator")
backend.open_session()
self.assertTrue(backend.session.active)
self.assertTrue(backend.session._active)
backend.cancel_session()
self.assertIsNone(backend.session)

def test_session_close(self):
@run_integration_test
def test_session_close(self, service):
"""Test closing a session"""
backend = self.service.backend("ibmq_qasm_simulator")
backend = service.backend("ibmq_qasm_simulator")
backend.open_session()
self.assertTrue(backend.session.active)
self.assertTrue(backend.session._active)
backend.close_session()
self.assertIsNone(backend.session)

def test_run_after_cancel(self):
@run_integration_test
def test_run_after_cancel(self, service):
"""Test running after session is cancelled."""
backend = self.service.backend("ibmq_qasm_simulator")
backend = service.backend("ibmq_qasm_simulator")
job1 = backend.run(circuits=bell())
self.assertIsNone(backend.session)
self.assertIsNone(job1._session_id)
Expand All @@ -181,9 +217,10 @@ def test_run_after_cancel(self):
self.assertIsNone(backend.session)
self.assertIsNone(job3._session_id)

def test_session_as_context_manager(self):
@run_integration_test
def test_session_as_context_manager(self, service):
"""Test session as a context manager"""
backend = self.service.backend("ibmq_qasm_simulator")
backend = service.backend("ibmq_qasm_simulator")

with backend.open_session() as session:
job1 = backend.run(bell())
Expand All @@ -192,13 +229,26 @@ def test_session_as_context_manager(self):
job2 = backend.run(bell())
self.assertFalse(session_id == job2.job_id())

def test_run_after_cancel_as_context_manager(self):
@run_integration_test
def test_run_after_cancel_as_context_manager(self, service):
"""Test run after cancel in context manager"""
backend = self.service.backend("ibmq_qasm_simulator")
backend = service.backend("ibmq_qasm_simulator")
with backend.open_session() as session:
_ = backend.run(bell())
self.assertEqual(backend.session, session)
backend.cancel_session()
job = backend.run(circuits=bell())
self.assertIsNone(backend.session)
self.assertIsNone(job._session_id)

@run_integration_test
def test_run_after_session(self, service):
"""Test run after session was closed"""
backend = service.backend("ibmq_qasm_simulator")
with Session(backend=backend) as session:
job1 = backend.run(bell())
self.assertTrue(job1.result())
self.assertEqual(backend.session, session)

job2 = backend.run(circuits=bell())
self.assertTrue(job2.result())
Loading