From 5657756f0377a3e5ff36426ec3b419299f8a1410 Mon Sep 17 00:00:00 2001 From: kevin-tian Date: Mon, 19 Feb 2024 12:52:05 -0500 Subject: [PATCH 1/4] Use POST /sessions --- qiskit_ibm_provider/api/clients/runtime.py | 17 +++++++++++++++ qiskit_ibm_provider/api/rest/runtime.py | 2 +- .../api/rest/runtime_session.py | 21 +++++++++++++++++-- qiskit_ibm_provider/ibm_backend.py | 8 ++++++- qiskit_ibm_provider/session.py | 3 ++- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/qiskit_ibm_provider/api/clients/runtime.py b/qiskit_ibm_provider/api/clients/runtime.py index 8741994f3..295d86985 100644 --- a/qiskit_ibm_provider/api/clients/runtime.py +++ b/qiskit_ibm_provider/api/clients/runtime.py @@ -315,6 +315,23 @@ def update_tags(self, job_id: str, tags: list) -> Response: """ return self._api.program_job(job_id).update_tags(tags) + def create_session( + self, backend: str, instance: str, mode: str = None + ) -> Dict[str, Any]: + """Create a new runtime session. + + Args: + backend: The name of the backend to use. + instance: The instance to use. + mode: The mode to use. + + Returns: + The created session. + """ + return self._api.runtime_session().create( + backend=backend, instance=instance, mode=mode + ) + def close_session(self, session_id: str) -> None: """Close session diff --git a/qiskit_ibm_provider/api/rest/runtime.py b/qiskit_ibm_provider/api/rest/runtime.py index 70481ce5e..17cee7228 100644 --- a/qiskit_ibm_provider/api/rest/runtime.py +++ b/qiskit_ibm_provider/api/rest/runtime.py @@ -57,7 +57,7 @@ def program_job(self, job_id: str) -> "ProgramJob": """ return ProgramJob(self.session, job_id) - def runtime_session(self, session_id: str) -> "RuntimeSession": + def runtime_session(self, session_id: str = None) -> "RuntimeSession": """Return an adapter for the session. Args: diff --git a/qiskit_ibm_provider/api/rest/runtime_session.py b/qiskit_ibm_provider/api/rest/runtime_session.py index fb8aa94c8..454a00402 100644 --- a/qiskit_ibm_provider/api/rest/runtime_session.py +++ b/qiskit_ibm_provider/api/rest/runtime_session.py @@ -12,7 +12,7 @@ """Runtime Session REST adapter.""" - +from typing import Dict, Any from qiskit_ibm_provider.api.rest.base import RestAdapterBase from qiskit_ibm_provider.exceptions import IBMApiError from ..exceptions import RequestsApiError @@ -37,7 +37,24 @@ def __init__( session_id: Job ID of the first job in a runtime session. url_prefix: Prefix to use in the URL. """ - super().__init__(session, "{}/sessions/{}".format(url_prefix, session_id)) + if not session_id: + super().__init__(session, "{}/sessions".format(url_prefix)) + else: + super().__init__(session, "{}/sessions/{}".format(url_prefix, session_id)) + + def create( + self, backend: str = None, instance: str = None, mode: str = None + ) -> Dict[str, Any]: + """Create a session""" + url = self.get_url("self") + payload = {} + if mode: + payload["mode"] = mode + if backend: + payload["backend"] = backend + if instance: + payload["instance"] = instance + return self.session.post(url, json=payload).json() def close(self) -> None: """Set accepting_jobs flag to false, so no more jobs can be submitted.""" diff --git a/qiskit_ibm_provider/ibm_backend.py b/qiskit_ibm_provider/ibm_backend.py index 124bb2d98..e4917391b 100644 --- a/qiskit_ibm_provider/ibm_backend.py +++ b/qiskit_ibm_provider/ibm_backend.py @@ -873,7 +873,13 @@ def _check_faulty(self, circuit: QuantumCircuit) -> None: def open_session(self, max_time: Optional[Union[int, str]] = None) -> Session: """Open session""" - self._session = Session(max_time) + if not self._configuration.simulator: + result = self.provider._runtime_client.create_session( + backend=self.name, instance=self._instance + ) + self._session = Session(max_time, result.get("id")) + else: + self._session = Session(max_time) return self._session @property diff --git a/qiskit_ibm_provider/session.py b/qiskit_ibm_provider/session.py index 9fb534218..b7c9de3ca 100644 --- a/qiskit_ibm_provider/session.py +++ b/qiskit_ibm_provider/session.py @@ -63,6 +63,7 @@ class Session: def __init__( self, max_time: Optional[Union[int, str]] = None, + session_id: Optional[str] = None, ): """Session constructor. @@ -78,7 +79,7 @@ def __init__( ValueError: If an input value is invalid. """ self._instance = None - self._session_id: Optional[str] = None + self._session_id = session_id self._active = True self._max_time = ( From d6dcdb28c97f13e58995bae00b3436e02c42cf64 Mon Sep 17 00:00:00 2001 From: kevin-tian Date: Tue, 20 Feb 2024 15:18:38 -0500 Subject: [PATCH 2/4] Update open_session, get params right --- qiskit_ibm_provider/api/clients/runtime.py | 10 +++--- .../api/rest/runtime_session.py | 10 ++++-- qiskit_ibm_provider/ibm_backend.py | 31 ++++++------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/qiskit_ibm_provider/api/clients/runtime.py b/qiskit_ibm_provider/api/clients/runtime.py index 295d86985..6400c6c77 100644 --- a/qiskit_ibm_provider/api/clients/runtime.py +++ b/qiskit_ibm_provider/api/clients/runtime.py @@ -316,7 +316,11 @@ def update_tags(self, job_id: str, tags: list) -> Response: return self._api.program_job(job_id).update_tags(tags) def create_session( - self, backend: str, instance: str, mode: str = None + self, + backend: Optional[str] = None, + instance: Optional[str] = None, + max_time: Optional[int] = None, + mode: Optional[str] = None, ) -> Dict[str, Any]: """Create a new runtime session. @@ -328,9 +332,7 @@ def create_session( Returns: The created session. """ - return self._api.runtime_session().create( - backend=backend, instance=instance, mode=mode - ) + return self._api.runtime_session().create(backend, instance, max_time, mode) def close_session(self, session_id: str) -> None: """Close session diff --git a/qiskit_ibm_provider/api/rest/runtime_session.py b/qiskit_ibm_provider/api/rest/runtime_session.py index 454a00402..3d48ae211 100644 --- a/qiskit_ibm_provider/api/rest/runtime_session.py +++ b/qiskit_ibm_provider/api/rest/runtime_session.py @@ -12,7 +12,7 @@ """Runtime Session REST adapter.""" -from typing import Dict, Any +from typing import Dict, Any, Optional from qiskit_ibm_provider.api.rest.base import RestAdapterBase from qiskit_ibm_provider.exceptions import IBMApiError from ..exceptions import RequestsApiError @@ -43,7 +43,11 @@ def __init__( super().__init__(session, "{}/sessions/{}".format(url_prefix, session_id)) def create( - self, backend: str = None, instance: str = None, mode: str = None + self, + backend: Optional[str] = None, + instance: Optional[str] = None, + max_time: Optional[int] = None, + mode: Optional[str] = None, ) -> Dict[str, Any]: """Create a session""" url = self.get_url("self") @@ -54,6 +58,8 @@ def create( payload["backend"] = backend if instance: payload["instance"] = instance + if max_time: + payload["max_session_ttl"] = max_time # type: ignore[assignment] return self.session.post(url, json=payload).json() def close(self) -> None: diff --git a/qiskit_ibm_provider/ibm_backend.py b/qiskit_ibm_provider/ibm_backend.py index e4917391b..29a619e09 100644 --- a/qiskit_ibm_provider/ibm_backend.py +++ b/qiskit_ibm_provider/ibm_backend.py @@ -511,18 +511,11 @@ def _runtime_run( """Runs the runtime program and returns the corresponding job object""" hgp_name = self._instance or self.provider._get_hgp().name - session = self._session - - if session: - if not session.active: - raise RuntimeError(f"The session {session.session_id} is closed.") - session_id = session.session_id - session_time = session._max_time - start_session = session_id is None - else: - session_id = None - session_time = None - start_session = False + session_id = None + if self._session: + if not self._session.active: + raise RuntimeError(f"The session {self._session.session_id} is closed.") + session_id = self._session.session_id try: response = self.provider._runtime_client.program_run( @@ -532,15 +525,11 @@ def _runtime_run( hgp=hgp_name, job_tags=job_tags, session_id=session_id, - start_session=start_session, - session_time=session_time, + start_session=False, image=image, ) except RequestsApiError as ex: raise IBMBackendApiError("Error submitting job: {}".format(str(ex))) from ex - session_id = response.get("session_id") - if self._session: - self._session._session_id = session_id try: job = IBMCircuitJob( backend=self, @@ -874,12 +863,12 @@ def _check_faulty(self, circuit: QuantumCircuit) -> None: def open_session(self, max_time: Optional[Union[int, str]] = None) -> Session: """Open session""" if not self._configuration.simulator: - result = self.provider._runtime_client.create_session( - backend=self.name, instance=self._instance + new_session = self.provider._runtime_client.create_session( + self.name, self._instance, max_time ) - self._session = Session(max_time, result.get("id")) + self._session = Session(session_id=new_session.get("id")) else: - self._session = Session(max_time) + self._session = Session() return self._session @property From 4537952c134b94591a33465ff465e0c80f2edb95 Mon Sep 17 00:00:00 2001 From: kevin-tian Date: Tue, 20 Feb 2024 16:38:13 -0500 Subject: [PATCH 3/4] unit tests --- qiskit_ibm_provider/ibm_backend.py | 2 +- test/unit/mock/fake_account_client.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit_ibm_provider/ibm_backend.py b/qiskit_ibm_provider/ibm_backend.py index 29a619e09..ed603ffa5 100644 --- a/qiskit_ibm_provider/ibm_backend.py +++ b/qiskit_ibm_provider/ibm_backend.py @@ -866,7 +866,7 @@ def open_session(self, max_time: Optional[Union[int, str]] = None) -> Session: new_session = self.provider._runtime_client.create_session( self.name, self._instance, max_time ) - self._session = Session(session_id=new_session.get("id")) + self._session = Session(max_time=max_time, session_id=new_session.get("id")) else: self._session = Session() return self._session diff --git a/test/unit/mock/fake_account_client.py b/test/unit/mock/fake_account_client.py index 3a1ca968c..569dd2b69 100644 --- a/test/unit/mock/fake_account_client.py +++ b/test/unit/mock/fake_account_client.py @@ -29,7 +29,8 @@ class FakeApiBackend: def __init__(self, config_update=None, status_update=None): fake_backend = Fake5QV1() self.properties = fake_backend.properties().to_dict() - self.defaults = fake_backend.defaults().to_dict() + if hasattr(fake_backend, "defaults"): + self.defaults = fake_backend.defaults().to_dict() self.configuration = fake_backend.configuration().to_dict() self.configuration["online_date"] = python_datetime.now().isoformat() From ea45b1bf755d26827b419becc82281cf918aa91e Mon Sep 17 00:00:00 2001 From: kevin-tian Date: Tue, 20 Feb 2024 16:56:30 -0500 Subject: [PATCH 4/4] Fix scheduler unit test --- .../passes/scheduling/test_scheduler.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/test/unit/transpiler/passes/scheduling/test_scheduler.py b/test/unit/transpiler/passes/scheduling/test_scheduler.py index c98766e2d..cce7f8ddc 100644 --- a/test/unit/transpiler/passes/scheduling/test_scheduler.py +++ b/test/unit/transpiler/passes/scheduling/test_scheduler.py @@ -20,10 +20,7 @@ from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.exceptions import TranspilerError -try: - from qiskit.providers.fake_provider import Fake7QPulseV1 -except ImportError: - from qiskit.providers.fake_provider import FakeJakarta as Fake7QPulseV1 +from qiskit_ibm_runtime.fake_provider import FakeJakarta from qiskit_ibm_provider.transpiler.passes.scheduling.pad_delay import PadDelay from qiskit_ibm_provider.transpiler.passes.scheduling.scheduler import ( @@ -850,12 +847,12 @@ def test_c_if_plugin_conversion_with_transpile(self): after transpilation with the plugin.""" # Patch the test backend with the plugin with patch.object( - Fake7QPulseV1, + FakeJakarta, "get_translation_stage_plugin", return_value="ibm_dynamic_circuits", create=True, ): - backend = Fake7QPulseV1() + backend = FakeJakarta() # Temporary workaround for mock backends. For real backends this is not required. backend.configuration().basis_gates.append("if_else") @@ -1867,7 +1864,7 @@ def test_for_loop(self): def test_transpile_mock_backend(self): """Test scheduling works with transpilation.""" - backend = Fake7QPulseV1() + backend = FakeJakarta() # Temporary workaround for mock backends. For real backends this is not required. backend.configuration().basis_gates.append("if_else") backend.configuration().basis_gates.append("while_loop") @@ -1915,7 +1912,7 @@ def test_transpile_mock_backend(self): def test_transpile_both_paths(self): """Test scheduling works with both fast- and standard path after transpiling.""" - backend = Fake7QPulseV1() + backend = FakeJakarta() # Temporary workaround for mock backends. For real backends this is not required. backend.configuration().basis_gates.append("if_else") @@ -1957,12 +1954,12 @@ def test_c_if_plugin_conversion_with_transpile(self): transpilation with the plugin.""" # Patch the test backend with the plugin with patch.object( - Fake7QPulseV1, + FakeJakarta, "get_translation_stage_plugin", return_value="ibm_dynamic_circuits", create=True, ): - backend = Fake7QPulseV1() + backend = FakeJakarta() # Temporary workaround for mock backends. For real backends this is not required. backend.configuration().basis_gates.append("if_else")