Status | Accepted |
---|---|
RFC # | 0018 |
Authors | Jessie Yu ([email protected]) |
Submitted | 2023-10-20 |
This RFC proposes using Python and pydantic
dataclasses as the object type of primitive options. This allows users to discover supported options without having to read the documentation. It also supports auto-complete, nested options in the x.y
format, and field validation.
BasePrimitive
currently accept an options
parameter of type dict
to its constructor. The underlying expectation is that all options would be stored in this dictionary. However, due to the large number of options, both Qiskit Runtime and Qiskit Aer primitive implementations use a more structured approach. Transpiler options, for example, is specified via transpile_options
in Aer and options.transpilation.x
in Qiskit Runtime.
While BasePrimitive
takes a dictionary options
as an input, its options
property actually returns a qiskit.providers.Options
instance, which is another inconsistency. There is little reason to use qiskit.providers.Options
, which has a lot of complicated code designed to maintain backward compatibility with Qiskit Experiments. qiskit.providers.Options
does allow setting of validators, but that feature is not used by any of known primitive implementations.
In addition, there are features users requested around accessing options that this RFC wishes to address:
- auto-complete, this can include nested auto-complete with
x.y
- an easy way to discover the different options for each primitive
- validation on values
- consistent way to specify the same options across different providers
- the ability to specify experimental or undocumented options
- the ability to make a copy of the options
All users of the primitives stand to benefit from this proposal. It will allow users to more easily discover available options, auto-complete field names, and get prompted for invalid values when specifying them.
It will also decouple qiskit.providers.Options
from primitives, if Qiskit wants to retire that class.
-
Use Python dataclass as the input/output type for primitive options. Dataclass allows auto-complete, and users can easily discover what fields are available.
-
The
options
property would return the dataclass, and a user can doestimator.options.x
, to get/set the specific option. The users can also use the builtinasdict
method to convert the dataclass to a dictionary if they wish. -
Instead of a
set_options()
method attached to the primitive, the dataclass will have anupdate()
method for bulk updates. -
Providers that have more complex options, such as Qiskit Runtime, can use more advanced dataclass type such as the one in
pydantic
. -
It is still up to each provider on how they want to support undocumented/experimental options.
Per #51, we will start versioning the primitives. Therefore, no backward compatibility is needed, and the changes will only apply to v2 primitives.
BasePrimitive
would accept either a dictionary or a dataclass foroptions
.- A new
BasePrimitiveOptions
dataclass will serve as the base class, since there is no type hint for dataclasses. _options_class
class attribute is used to identify the dataclass the subclass wishes to use.- The
options
property wll return the dataclass. - The options dataclass will have a
update()
method for bulk update
from dataclasses import dataclass, replace
@dataclass
class BasePrimitiveOptions:
test_attr: int = 1
def update(self, **kwargs):
for key, val in kwargs.items():
setattr(self, key, val)
class BasePrimitiveV2:
"""Primitive abstract base class."""
_options_class = BasePrimitiveOptions
def __init__(self, options: dict | BasePrimitiveOptions | None = None):
if isinstance(options, dict):
self.options = self._options_class(**options)
elif options is None:
self.options = self._options_class()
else:
self.options = options
>>> base = BasePrimitive()
>>> base.options.test_attr
1
>>> base.options
BasePrimitiveOptions(test_attr=1)
>>> base.options.update(test_attr=2)
>>> base.options.test_attr
2
An alternative considered was using pydantic.BaseModel
. This class offers all the features Python dataclasses and pydantic dataclasses have, plus many useful methods, such as determining whether a field was explicitly set or defaulted to.
Unfortunately, these useful methods would also show up when you attempt to do auto-complete and could cause confusion on which ones are the actual options.