Skip to content

Commit

Permalink
Accept all hierarchical options if given flatly (#764)
Browse files Browse the repository at this point in the history
* Recognize first level options properly

* Cleaned up Sampler code. Copied same to Estimator

* Added test. Fixed error in skip_transpilation

* Release notes

* Cleaning code: Changed '_options' to 'self._options' in primitive constructors.

* Black

---------

Co-authored-by: Kevin Tian <kevin.tian@ibm.com>
  • Loading branch information
merav-aharoni and kt474 authored Mar 30, 2023
1 parent cadaaa0 commit 5c4cd4d
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 39 deletions.
29 changes: 10 additions & 19 deletions qiskit_ibm_runtime/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def __init__(
skip_transpilation: (DEPRECATED) Transpilation is skipped if set to True. False by default.
Ignored ``skip_transpilation`` is also specified in ``options``.
"""
# `_options` in this class is an instance of qiskit_ibm_runtime.Options class.
# `self._options` in this class is a Dict.
# The base class, however, uses a `_run_options` which is an instance of
# qiskit.providers.Options. We largely ignore this _run_options because we use
# a nested dictionary to categorize options.
Expand All @@ -166,12 +166,12 @@ def __init__(
self._session: Session = None

if options is None:
_options = Options()
self._options = asdict(Options())
elif isinstance(options, Options):
_options = copy.deepcopy(options)
skip_transpilation = (
_options.transpilation.skip_transpilation # type: ignore[union-attr]
options.transpilation.skip_transpilation # type: ignore[union-attr]
)
self._options = asdict(copy.deepcopy(options))
else:
options_copy = copy.deepcopy(options)
backend = options_copy.pop("backend", None)
Expand All @@ -181,24 +181,15 @@ def __init__(
version="0.7",
remedy="Please pass the backend when opening a session.",
)
skip_transpilation = options.get("transpilation", {}).get(
default_options = asdict(Options())
self._options = Options._merge_options(default_options, options_copy)
skip_transpilation = self._options.get("transpilation", {}).get(
"skip_transpilation", False
)
log_level = options_copy.pop("log_level", None)
_options = Options(**options_copy)
if log_level:
issue_deprecation_msg(
msg="The 'log_level' option has been moved to the 'environment' category",
version="0.7",
remedy="Please specify 'environment':{'log_level': log_level} instead.",
)
_options.environment.log_level = log_level # type: ignore[union-attr]

_options.transpilation.skip_transpilation = ( # type: ignore[union-attr]
skip_transpilation
)

self._options: dict = asdict(_options)
self._options["transpilation"][
"skip_transpilation"
] = skip_transpilation # type: ignore[union-attr]

self._initial_inputs = {
"circuits": circuits,
Expand Down
30 changes: 10 additions & 20 deletions qiskit_ibm_runtime/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def __init__(
skip_transpilation (DEPRECATED): Transpilation is skipped if set to True. False by default.
Ignored if ``skip_transpilation`` is also specified in ``options``.
"""
# `_options` in this class is an instance of qiskit_ibm_runtime.Options class.
# `self._options` in this class is a Dict.
# The base class, however, uses a `_run_options` which is an instance of
# qiskit.providers.Options. We largely ignore this _run_options because we use
# a nested dictionary to categorize options.
Expand All @@ -141,12 +141,12 @@ def __init__(
self._session: Session = None

if options is None:
_options = Options()
self._options = asdict(Options())
elif isinstance(options, Options):
_options = copy.deepcopy(options)
skip_transpilation = (
_options.transpilation.skip_transpilation # type: ignore[union-attr]
options.transpilation.skip_transpilation # type: ignore[union-attr]
)
self._options = asdict(copy.deepcopy(options))
else:
options_copy = copy.deepcopy(options)
backend = options_copy.pop("backend", None)
Expand All @@ -156,24 +156,14 @@ def __init__(
version="0.7",
remedy="Please pass the backend when opening a session.",
)
skip_transpilation = options.get("transpilation", {}).get(
default_options = asdict(Options())
self._options = Options._merge_options(default_options, options_copy)
skip_transpilation = self._options.get("transpilation", {}).get(
"skip_transpilation", False
)
log_level = options_copy.pop("log_level", None)
_options = Options(**options_copy)
if log_level:
issue_deprecation_msg(
msg="The 'log_level' option has been moved to the 'environment' category",
version="0.7",
remedy="Please specify 'environment':{'log_level': log_level} instead.",
)
_options.environment.log_level = log_level # type: ignore[union-attr]

_options.transpilation.skip_transpilation = ( # type: ignore[union-attr]
skip_transpilation
)

self._options: dict = asdict(_options)
self._options["transpilation"][
"skip_transpilation"
] = skip_transpilation # type: ignore[union-attr]

self._initial_inputs = {"circuits": circuits, "parameters": parameters}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
upgrade:
- |
Accept all options on given on level 1 and assign them to the appropriate hierarchical option type.
For example, if the user provides ``options = {"shots": 10}`` as input to Sampler/Estimator, this will
be interpreted as ``options = {"execution: {"shots": 10}}``.
40 changes: 40 additions & 0 deletions test/unit/test_ibm_primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,46 @@ def test_set_options(self):
f"inst_options={inst_options}, new_str={new_str}",
)

def test_accept_level_1_options(self):
"""Test initializing options properly when given on level 1."""

options_dicts = [
{},
{"shots": 10},
{"seed_simulator": 123},
{"skip_transpilation": True, "log_level": "ERROR"},
{"initial_layout": [1, 2], "shots": 100, "noise_amplifier": "CxAmplifier"},
]

expected_list = [Options(), Options(), Options(), Options(), Options()]
expected_list[1].execution.shots = 10
expected_list[2].simulator.seed_simulator = 123
expected_list[3].transpilation.skip_transpilation = True
expected_list[3].environment.log_level = "ERROR"
expected_list[4].transpilation.initial_layout = [1, 2]
expected_list[4].execution.shots = 100
expected_list[4].resilience.noise_amplifier = "CxAmplifier"

session = MagicMock(spec=MockSession)
primitives = [Sampler, Estimator]
for cls in primitives:
for opts, expected in zip(options_dicts, expected_list):
with self.subTest(primitive=cls, options=opts):
inst1 = cls(session=session, options=opts)
inst2 = cls(session=session, options=expected)
# Make sure the values are equal.
inst1_options = inst1.options.__dict__
expected_dict = inst2.options.__dict__
self.assertTrue(
dict_paritally_equal(inst1_options, expected_dict),
f"inst_options={inst1_options}, options={opts}",
)
# Make sure the structure didn't change.
self.assertTrue(
dict_keys_equal(inst1_options, expected_dict),
f"inst_options={inst1_options}, expected={expected_dict}",
)

def test_default_error_levels(self):
"""Test the correct default error levels are used."""

Expand Down

0 comments on commit 5c4cd4d

Please sign in to comment.