Skip to content

Commit f77504a

Browse files
authored
[executor-preview] Test noise_learner_v3.py (#2498)
* Test noise_learner_v3.py * reset mock * init with session * fix nlv3 constructor * a bit more tests * backticks * test the run method * black * reverting a change by mistake * lint * lint * wrote properties * no need to check if simulator * set session._run.return_value instead of session._run * removed newly added properties * convert _get_mode_service_backend into public * fixed tests of the selected run method * black * lint * typo * 2026 * lint
1 parent f24d674 commit f77504a

File tree

5 files changed

+105
-50
lines changed

5 files changed

+105
-50
lines changed

qiskit_ibm_runtime/base_primitive.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
OptionsT = TypeVar("OptionsT", bound=BaseOptions)
4747

4848

49-
def _get_mode_service_backend(mode: BackendV2 | Session | Batch | None = None) -> tuple[
49+
def get_mode_service_backend(mode: BackendV2 | Session | Batch | None = None) -> tuple[
5050
Session | Batch | None,
5151
QiskitRuntimeService | QiskitRuntimeLocalService | None,
5252
BackendV2 | None,
@@ -120,7 +120,7 @@ def __init__(
120120
Raises:
121121
ValueError: Invalid arguments are given.
122122
"""
123-
self._mode, self._service, self._backend = _get_mode_service_backend(mode)
123+
self._mode, self._service, self._backend = get_mode_service_backend(mode)
124124
self._set_options(options)
125125

126126
def _run(self, pubs: list[EstimatorPub] | list[SamplerPub]) -> RuntimeJobV2:

qiskit_ibm_runtime/noise_learner/noise_learner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from qiskit.primitives.containers import EstimatorPubLike
2626
from qiskit.primitives.containers.estimator_pub import EstimatorPub
2727

28-
from ..base_primitive import _get_mode_service_backend
28+
from ..base_primitive import get_mode_service_backend
2929
from ..constants import DEFAULT_DECODERS
3030
from ..runtime_job_v2 import RuntimeJobV2
3131
from ..ibm_backend import IBMBackend
@@ -127,7 +127,7 @@ def __init__(
127127
mode: BackendV2 | Session | Batch | None = None,
128128
options: dict | NoiseLearnerOptions | EstimatorOptions | None = None,
129129
):
130-
self._mode, self._service, self._backend = _get_mode_service_backend(mode)
130+
self._mode, self._service, self._backend = get_mode_service_backend(mode)
131131
if isinstance(self._service, QiskitRuntimeLocalService):
132132
raise ValueError("``NoiseLearner`` not currently supported in local mode.")
133133

qiskit_ibm_runtime/noise_learner_v3/noise_learner_v3.py

Lines changed: 10 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,15 @@
2222

2323
from qiskit_ibm_runtime.options.utils import UnsetType
2424

25-
from ..base_primitive import _get_mode_service_backend
25+
from ..base_primitive import get_mode_service_backend
2626
from ..batch import Batch
2727
from ..fake_provider.local_service import QiskitRuntimeLocalService
2828
from ..options.noise_learner_v3_options import NoiseLearnerV3Options
29-
from ..qiskit_runtime_service import QiskitRuntimeService
3029
from ..runtime_job_v2 import RuntimeJobV2
3130

3231
# pylint: disable=unused-import,cyclic-import
3332
from ..session import Session
3433
from ..utils.default_session import get_cm_session
35-
from ..utils.utils import is_simulator
3634
from .converters.version_0_1 import noise_learner_v3_inputs_to_0_1
3735
from .noise_learner_v3_decoders import NoiseLearnerV3ResultDecoder
3836
from .validation import validate_instruction, validate_options
@@ -77,50 +75,19 @@ def __init__(
7775
mode: BackendV2 | Session | Batch | None = None,
7876
options: NoiseLearnerV3Options | None = None,
7977
):
80-
self._session: BackendV2 | None = None
81-
self._backend: BackendV2
82-
self._service: QiskitRuntimeService
83-
8478
self._options = options or NoiseLearnerV3Options()
8579
if (
8680
isinstance(self._options.experimental, UnsetType)
8781
or self._options.experimental.get("image") is None
8882
):
8983
self._options.experimental = {}
9084

91-
if isinstance(mode, (Session, Batch)):
92-
self._session = mode
93-
self._backend = self._session._backend
94-
self._service = self._session.service
95-
elif open_session := get_cm_session():
96-
if open_session != mode:
97-
if open_session._backend != mode:
98-
raise ValueError(
99-
"The backend passed in to the primitive is different from the session "
100-
"backend. Please check which backend you intend to use or leave the mode "
101-
"parameter empty to use the session backend."
102-
)
103-
logger.warning(
104-
"A backend was passed in as the mode but a session context manager "
105-
"is open so this job will run inside this session/batch "
106-
"instead of in job mode."
107-
)
108-
self._session = open_session
109-
self._backend = self._session._backend
110-
self._service = self._session.service
111-
elif isinstance(mode, BackendV2):
112-
self._backend = mode
113-
self._service = self._backend.service
114-
else:
115-
raise ValueError(
116-
"A backend or session/batch must be specified, or a session/batch must be open."
117-
)
118-
self._mode, self._service, self._backend = _get_mode_service_backend( # type: ignore[assignment]
85+
self._session, self._service, self._backend = get_mode_service_backend(
11986
mode
120-
)
87+
) # type: ignore[assignment]
12188

12289
if isinstance(self._service, QiskitRuntimeLocalService): # type: ignore[unreachable]
123-
raise ValueError("``NoiseLearner`` not currently supported in local mode.")
90+
raise ValueError("``NoiseLearnerV3`` is currently not supported in local mode.")
12491

12592
@property
12693
def options(self) -> NoiseLearnerV3Options:
@@ -146,15 +113,12 @@ def run(self, instructions: Iterable[CircuitInstruction]) -> RuntimeJobV2:
146113
IBMInputValueError: If an instruction cannot be learned by any of the supported learning
147114
protocols.
148115
"""
149-
if self._backend:
150-
target = getattr(self._backend, "target", None)
151-
if target and not is_simulator(self._backend):
152-
for instruction in instructions:
153-
validate_instruction(instruction, target)
154-
155-
configuration = getattr(self._backend, "configuration", None)
156-
if configuration and not is_simulator(self._backend):
157-
validate_options(self.options, configuration())
116+
if target := getattr(self._backend, "target", None):
117+
for instruction in instructions:
118+
validate_instruction(instruction, target)
119+
120+
if configuration := getattr(self._backend, "configuration", None):
121+
validate_options(self.options, configuration())
158122

159123
inputs = noise_learner_v3_inputs_to_0_1(instructions, self.options).model_dump()
160124
inputs["version"] = 3 # TODO: this is a work-around for the dispatch
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2026.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
13+
"""Tests the `NoiseLearnerV3` class."""
14+
15+
from unittest.mock import patch
16+
17+
from test.utils import get_mocked_backend, get_mocked_session
18+
19+
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
20+
21+
from ...ibm_test_case import IBMTestCase
22+
23+
24+
class TestNoiseLearnerV3(IBMTestCase):
25+
"""Tests the ``NoiseLearnerV3`` class."""
26+
27+
def test_run_of_session_is_selected(self):
28+
"""Test that ``NoiseLearnerV3.run`` selects the ``run`` method
29+
of the session, if a session is specified."""
30+
backend_name = "ibm_hello"
31+
session = get_mocked_session(get_mocked_backend(backend_name))
32+
with (
33+
patch.object(session, "_run", return_value="session"),
34+
patch.object(session.service, "_run", return_value="service"),
35+
):
36+
noise_learner = NoiseLearnerV3(mode=session)
37+
selected_run = noise_learner.run([])
38+
self.assertEqual(selected_run, "session")
39+
40+
def test_run_of_service_is_selected(self):
41+
"""Test that ``NoiseLearnerV3.run`` selects the ``run`` method
42+
of the service, if a session is not specified."""
43+
backend = get_mocked_backend()
44+
with patch.object(backend.service, "_run", return_value="service"):
45+
noise_learner = NoiseLearnerV3(mode=backend)
46+
selected_run = noise_learner.run([])
47+
self.assertEqual(selected_run, "service")

test/unit/test_ibm_primitives_v2.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from qiskit_ibm_runtime import Session, Batch
2727
from qiskit_ibm_runtime.utils.default_session import _DEFAULT_SESSION
2828
from qiskit_ibm_runtime import EstimatorV2, SamplerV2
29+
from qiskit_ibm_runtime.base_primitive import get_mode_service_backend
2930
from qiskit_ibm_runtime.estimator import Estimator as IBMBaseEstimator
3031
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
3132
from qiskit_ibm_runtime.exceptions import IBMInputValueError
@@ -650,3 +651,46 @@ def _assert_dict_partially_equal(self, dict1, dict2):
650651
dict_paritally_equal(dict1, dict2),
651652
f"{dict1} and {dict2} not partially equal.",
652653
)
654+
655+
656+
class TestGetModeServiceBackend(IBMTestCase):
657+
"""Test the function ``get_mode_service_backend``."""
658+
659+
def test_mode_is_backend(self):
660+
"""Test ``get_mode_service_backend`` when the input mode is an ``IBMBackend``."""
661+
backend = get_mocked_backend()
662+
service = backend.service
663+
result = get_mode_service_backend(mode=backend)
664+
self.assertEqual(result[0], None)
665+
self.assertEqual(result[1], service)
666+
self.assertEqual(result[2], backend)
667+
668+
def test_mode_is_session(self):
669+
"""Test ``get_mode_service_backend`` when the input mode is a session."""
670+
backend_name = "ibm_hello"
671+
session = get_mocked_session(get_mocked_backend(backend_name))
672+
result = get_mode_service_backend(mode=session)
673+
self.assertEqual(result[0], session)
674+
self.assertEqual(result[1], session.service)
675+
self.assertEqual(result[2].name, backend_name)
676+
677+
def test_session_context_manager(self):
678+
"""Test ``get_mode_service_backend`` inside a session context manager."""
679+
backend = get_mocked_backend()
680+
service = backend.service
681+
with Session(backend=backend) as session:
682+
result = get_mode_service_backend()
683+
self.assertEqual(result[0], session)
684+
self.assertEqual(result[1], service)
685+
self.assertEqual(result[2], backend)
686+
687+
def test_mode_is_backend_inside_session_context_manager(self):
688+
"""Test ``get_mode_service_backend`` inside a session context manager,
689+
when the input mode is an ``IBMBackend``."""
690+
backend = get_mocked_backend()
691+
service = backend.service
692+
with Session(backend=backend) as session:
693+
result = get_mode_service_backend(mode=backend)
694+
self.assertEqual(result[0], session)
695+
self.assertEqual(result[1], service)
696+
self.assertEqual(result[2], backend)

0 commit comments

Comments
 (0)