Qiskit / qiskit-ibm-runtime

IBM Client for Qiskit Runtime
https://docs.quantum.ibm.com/api/qiskit-ibm-runtime
Apache License 2.0
140 stars 150 forks source link

Initializing with options dict using resilience level and resilience options fails #1654

Closed chriseclectic closed 2 weeks ago

chriseclectic commented 2 months ago

Describe the bug

When passing in a dict for Options when initializing an estimator, trying to modify options related to the resilience levels raises an exception.

Steps to reproduce

The following works to initialize a level 2 Estimator and set a custom ZNE extrapolator:

options = {"resilience_level": 2}
estimator = EstimatorV2(backend, options=options)
estimator.options.resilience.zne.extrapolator = "linear"

The following does not:

options = {
    "resilience_level": 2,
    "resilience": {"zne": {"extrapolator": "linear"}},
}
estimator = EstimatorV2(backend, options=options)
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Cell In[39], line 5
      1 options = {
      2     "resilience_level": 2,
      3     "resilience": {"zne": {"extrapolator": "linear"}},
      4 }
----> 5 estimator = EstimatorV2(backend, options=options)

File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit_ibm_runtime/estimator.py:132, in EstimatorV2.__init__(self, backend, session, options)
    130 BaseEstimatorV2.__init__(self)
    131 Estimator.__init__(self)
--> 132 BasePrimitiveV2.__init__(self, backend=backend, session=session, options=options)

File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit_ibm_runtime/base_primitive.py:89, in BasePrimitiveV2.__init__(self, backend, session, options)
     86 self._service: QiskitRuntimeService | QiskitRuntimeLocalService = None
     87 self._backend: Optional[BackendV1 | BackendV2] = None
---> 89 self._set_options(options)
     91 if isinstance(session, Session):
     92     self._session = session

File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit_ibm_runtime/base_primitive.py:211, in BasePrimitiveV2._set_options(self, options)
    209 elif isinstance(options, dict):
    210     default_options = self._options_class()
--> 211     self._options = self._options_class(**merge_options(default_options, options))
    212 elif isinstance(options, self._options_class):
    213     self._options = replace(options)

File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/pydantic/_internal/_dataclasses.py:135, in complete_dataclass.<locals>.__init__(__dataclass_self__, *args, **kwargs)
    133 __tracebackhide__ = True
    134 s = __dataclass_self__
--> 135 s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s)

ValidationError: 2 validation errors for EstimatorOptions
resilience.function-after[_validate_options(), ResilienceOptionsV2]
  Value error, 'zne' options are set, but 'zne_mitigation' is not set to True. [type=value_error, input_value={'measure_mitigation': Un...er_pair_depths': Unset}}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/value_error
resilience.is-instance[Dict]
  Input should be an instance of Dict [type=is_instance_of, input_value={'measure_mitigation': Un...er_pair_depths': Unset}}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/is_instance_of

Expected behavior

Passing in a dict of options should be equivalent to modifying them after initialization.

Suggested solutions

This looks like a Pydantic validation issue.

Additional Information

jyu00 commented 1 month ago

We don't map resilience_level=2 to zne_mitigation=True in qiskit-ibm-runtime, since it's up to the service to decide what resilience_level=2 means. We do, however, check if zne_mitigation=True is set if one is to set other zne options.

I think the best solution is to disable the zne_mitigation=True check in the client and let the service decide if that's compatible with the resilience level. Note that we do the same for pec.