-
Notifications
You must be signed in to change notification settings - Fork 12
QCDL registers: report a register name that is allocated twice #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
c329fb6
588b900
eeed807
57c46de
51c1299
145a0b6
cbb8f2c
e594c9c
b8b8adc
66eb3e5
e8aaa5c
04a4f44
15e1b97
a31a4f9
27bfabd
b555b98
5d6364c
a05bac7
dbd7f52
719ab26
b6058b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -28,7 +28,7 @@ | |||||||||||||||||||||
| import types | ||||||||||||||||||||||
| from collections.abc import Mapping, Sequence, Set | ||||||||||||||||||||||
| from contextlib import contextmanager | ||||||||||||||||||||||
| from typing import TYPE_CHECKING, Any, Callable, Iterator | ||||||||||||||||||||||
| from typing import TYPE_CHECKING, Any, Callable, Iterator, NamedTuple | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import numpy as np | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -76,6 +76,21 @@ def default(self, obj: Any) -> Any: | |||||||||||||||||||||
| return str(obj) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class RegisterAllocation(NamedTuple): | ||||||||||||||||||||||
| """One entry in the register names a circuit has allocated. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Args: | ||||||||||||||||||||||
| dtype: ``"int"`` or ``"float"``. | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This is my guess, the intention is to let the user know what each of the dtypes are meant for.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's also
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure |
||||||||||||||||||||||
| procedure: Procedure that made the allocation. The name it was made | ||||||||||||||||||||||
| under is reported when a later declaration clashes, and the | ||||||||||||||||||||||
| identity tells a re-run of that same procedure apart from a | ||||||||||||||||||||||
| genuine re-declaration. | ||||||||||||||||||||||
|
qci-amos marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||
| """ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| dtype: str | ||||||||||||||||||||||
| procedure: Procedure | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class Procedure(IndexerMixin): | ||||||||||||||||||||||
| """A QCDL procedure. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -241,6 +256,104 @@ def register_module_used(self, module_name: str | None) -> None: | |||||||||||||||||||||
| if module not in self.modules_used: | ||||||||||||||||||||||
| self.modules_used.append(module) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def register_memory_allocation( | ||||||||||||||||||||||
| self, | ||||||||||||||||||||||
| modules: Sequence[QCDLModule], | ||||||||||||||||||||||
| name: str, | ||||||||||||||||||||||
| dtype: str, | ||||||||||||||||||||||
| allow_existing: bool = False, | ||||||||||||||||||||||
| initial_value_specified: bool = False, | ||||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||||
| """Record a register allocation, rejecting a silent re-declaration. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
qci-amos marked this conversation as resolved.
|
||||||||||||||||||||||
| The compiler keeps the *first* allocation of a name, so a second | ||||||||||||||||||||||
| declaration of the same name on the same module is a no-op: its initial | ||||||||||||||||||||||
| value never reaches the qubit. That is almost always a mistake, so it is | ||||||||||||||||||||||
|
qci-amos marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||
| reported here instead. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Register names are global to the circuit rather than local to a | ||||||||||||||||||||||
| procedure, so the record lives on the | ||||||||||||||||||||||
| :attr:`~dwave.gate.qcdl.qcdl_circuit.QCDLCircuit.allocated_registers` | ||||||||||||||||||||||
| attribute of the state, and a name taken in one procedure clashes with | ||||||||||||||||||||||
| the same name in another. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| A procedure body is re-executed on every call while the program is | ||||||||||||||||||||||
| being built, but is emitted once, so a declaration reached through a | ||||||||||||||||||||||
| later run of the *same* procedure is not a re-declaration and is not | ||||||||||||||||||||||
| reported. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Re-declaring the name is allowed when the caller asked for it, but only | ||||||||||||||||||||||
| without an initial value: opting in to the re-declaration says the | ||||||||||||||||||||||
| existing memory is wanted, whereas giving a value says the opposite, | ||||||||||||||||||||||
| and the compiler would ignore it. This applies only once the name is | ||||||||||||||||||||||
| allocated; a first allocation always takes its value, whatever the | ||||||||||||||||||||||
| caller opted in to. | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find it hard to follow the above three paragraphs. Would you try a rewrite from the perspective of a user and I could then take a pass?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| This method is mostly intended for use by developers of QCDL; the | ||||||||||||||||||||||
| :class:`~dwave.gate.qcdl.registers.Register` and | ||||||||||||||||||||||
| :class:`~dwave.gate.qcdl.registers.FixedPointRegister` classes call it | ||||||||||||||||||||||
| for you. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
qci-amos marked this conversation as resolved.
|
||||||||||||||||||||||
| Args: | ||||||||||||||||||||||
| modules: Modules the register is allocated on. | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to keep it abstract as "modules" here because it could be couplers and we want to hide these details in the abstraction.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have been using the phrase with the "typically" for exactly that purpose for this first release. The idea being to help a new user understand what a module might be (we never replace "module" with just "qubit" in such places). |
||||||||||||||||||||||
| name: Name of the register. | ||||||||||||||||||||||
| dtype: ``"int"`` or ``"float"``. | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This is my guess, the intention is to let the user know what each of the dtypes are meant for.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's also used for
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure |
||||||||||||||||||||||
| allow_existing: If True, an existing allocation of ``name`` is | ||||||||||||||||||||||
| accepted as long as no initial value was given. Set by the | ||||||||||||||||||||||
| ``alias`` and ``ignore_reallocation`` arguments of a register. | ||||||||||||||||||||||
| It has no effect when ``name`` is not already allocated. | ||||||||||||||||||||||
|
Comment on lines
+299
to
+302
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||
| initial_value_specified: Whether the caller gave an initial value | ||||||||||||||||||||||
| for this register. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Raises: | ||||||||||||||||||||||
| :exception:`~dwave.gate.qcdl.exceptions.QCDLUserError`: If ``name`` | ||||||||||||||||||||||
| is already allocated on one of ``modules`` and either | ||||||||||||||||||||||
| ``allow_existing`` is False or an initial value was given. | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
| for module in modules: | ||||||||||||||||||||||
| allocated = self.state.allocated_registers.setdefault( | ||||||||||||||||||||||
| module.qcdl_module_name, {} | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| previous = allocated.get(name) | ||||||||||||||||||||||
| if previous is not None and self._is_rerun_of(previous.procedure): | ||||||||||||||||||||||
| previous = None | ||||||||||||||||||||||
| if previous is not None and not ( | ||||||||||||||||||||||
| allow_existing and not initial_value_specified | ||||||||||||||||||||||
| ): | ||||||||||||||||||||||
| if allow_existing: | ||||||||||||||||||||||
| raise QCDLUserError( | ||||||||||||||||||||||
| f"register {name!r} is already allocated on" | ||||||||||||||||||||||
| f" {module.qcdl_module_name} with dtype" | ||||||||||||||||||||||
| f" {previous.dtype} in procedure" | ||||||||||||||||||||||
| f" {previous.procedure.name}, and the compiler keeps" | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The next few lines seem too much implementation for an error message, can we cut this short and skip to "do not specify an initial value"?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||
| f" the first allocation, so the initial value given" | ||||||||||||||||||||||
| f" here would never reach the qubit. Re-declaring the" | ||||||||||||||||||||||
| f" name is allowed, but giving it a value is not: drop" | ||||||||||||||||||||||
| f" the initial value." | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| raise QCDLUserError( | ||||||||||||||||||||||
| f"register {name!r} is already allocated on" | ||||||||||||||||||||||
| f" {module.qcdl_module_name} with dtype {previous.dtype} in" | ||||||||||||||||||||||
| f" procedure {previous.procedure.name}; register names are" | ||||||||||||||||||||||
| f" global to the circuit and the compiler keeps the first" | ||||||||||||||||||||||
| f" allocation, so this one would be discarded. Reuse the" | ||||||||||||||||||||||
| f" existing register, pick another name, or redeclare it" | ||||||||||||||||||||||
| f" deliberately with alias=True or ignore_reallocation=True" | ||||||||||||||||||||||
| f" and no initial value." | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| allocated[name] = RegisterAllocation(dtype, self) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def _is_rerun_of(self, other: Procedure) -> bool: | ||||||||||||||||||||||
| """Whether ``other`` is an earlier run of the procedure ``self`` is. | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Calling a procedure runs its body again, so a register it declares is | ||||||||||||||||||||||
| seen once per call even though the procedure is emitted once. Those | ||||||||||||||||||||||
|
qci-amos marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||
| runs are separate :class:`.Procedure` instances sharing a name, and the | ||||||||||||||||||||||
| name is what the rest of the circuit deduplicates on, so matching on it | ||||||||||||||||||||||
| here agrees with what ends up in the program. | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This sentence is unclear to me
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||
| """ | ||||||||||||||||||||||
| return other is not self and other.proc_name == self.proc_name | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @property | ||||||||||||||||||||||
| def expression_queue(self) -> list | None: | ||||||||||||||||||||||
| """Create an expression queue. | ||||||||||||||||||||||
|
|
@@ -1230,7 +1343,9 @@ def all_to_all_use(q0, q1): | |||||||||||||||||||||
| sc = Scope(q0, q1) | ||||||||||||||||||||||
| r1 = sc.Register(name="r1") | ||||||||||||||||||||||
| h(q0) | ||||||||||||||||||||||
| measure(q0, register=q0.Register(name="r1")) | ||||||||||||||||||||||
| # alias=True reuses the memory r1 already allocated, so the | ||||||||||||||||||||||
| # outcome is stored on q0 only rather than mirrored | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems to suggest that setting
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||
| measure(q0, register=q0.Register(name="r1", alias=True)) | ||||||||||||||||||||||
| sc.all_to_all(send=r1==1, reduce_op="&") | ||||||||||||||||||||||
| with sc.If(None): | ||||||||||||||||||||||
| x(q1) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -26,7 +26,7 @@ | |||||
| import numpy as np | ||||||
|
|
||||||
| from .base import IndexerMixin | ||||||
| from .components import Procedure, QCDLModule | ||||||
| from .components import Procedure, QCDLModule, RegisterAllocation | ||||||
| from .exceptions import QCDLInternalError, QCDLUserError | ||||||
| from .qcdl_models import QCDLProgram, QCDLModuleName, QCDLProcedureDef | ||||||
| from .transformer import print_qcdl | ||||||
|
|
@@ -120,6 +120,11 @@ def __init__( | |||||
| self._main: Procedure | None = None | ||||||
| self._program: QCDLProcedureDef | None = None | ||||||
| self._procedures: dict[str, QCDLProcedureDef] = {} | ||||||
|
|
||||||
| # register names are global to a circuit rather than local to a | ||||||
| # procedure, so they are tracked here rather than on Procedure | ||||||
| self._allocated_registers: dict[str, dict[str, RegisterAllocation]] = {} | ||||||
|
|
||||||
| self._validate_non_deterministic_qubits_mid = ( | ||||||
| validate_non_deterministic_qubits_mid | ||||||
| ) | ||||||
|
|
@@ -485,6 +490,19 @@ def set_or_check_nondeterministic_modules( | |||||
| def procedures(self) -> dict[str, QCDLProcedureDef]: | ||||||
| return self._procedures | ||||||
|
|
||||||
| @property | ||||||
| def allocated_registers(self) -> dict[str, dict[str, RegisterAllocation]]: | ||||||
| """Register names allocated so far, by module name then register name. | ||||||
|
|
||||||
| Register names are global to a circuit: a name allocated in one | ||||||
| procedure is the same memory as that name in another, and only the | ||||||
| first allocation of it takes effect. The | ||||||
| :meth:`~dwave.gate.qcdl.components.Procedure.register_memory_allocation` | ||||||
| method maintains this, and reports a second allocation rather than | ||||||
| letting it be discarded. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Can we say something about how that report is used? It seems to me from the code below, that what is actually meant by "reports a second allocation" is in fact "raises an error", yes?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| """ | ||||||
| return self._allocated_registers | ||||||
|
|
||||||
| def get_procedure(self, procedure_name: str) -> QCDLProcedureDef | None: | ||||||
| return self.procedures.get(procedure_name) | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -244,6 +244,13 @@ def _register_initialization( | |
| if isinstance(initial_value, np.ndarray): | ||
| initial_value = initial_value.tolist() | ||
|
|
||
| # None means the caller gave no initial value. That distinction matters | ||
| # because a value that would never reach the qubit has to be reported | ||
| # rather than silently dropped. | ||
| initial_value_specified = initial_value is not None | ||
| if not initial_value_specified: | ||
| initial_value = 0.0 if str(dtype) == "float" else 0 | ||
|
|
||
| if length is None: | ||
| length = len(initial_value) if isinstance(initial_value, Sequence) else 1 | ||
|
|
||
|
|
@@ -257,6 +264,24 @@ def _register_initialization( | |
| name, initial_value=initial_value, length=length, dtype=dtype, signed=signed | ||
| ) | ||
|
|
||
| if alias is True and initial_value_specified: | ||
| raise QCDLUserError( | ||
| f"register {name!r} is an alias, so no memory is allocated for" | ||
| f" it and the initial value given here would never reach the" | ||
| f" qubit; drop the initial value, or drop alias=True to" | ||
| f" allocate new memory" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A more typical error message just says something like "f"register {name!r} is an alias, so you cannot specify an initial value" |
||
| ) | ||
|
|
||
| # An alias deliberately names memory that already exists, and | ||
| # ignore_reallocation is the documented opt out. | ||
| modules[0].procedure.register_memory_allocation( | ||
| modules, | ||
| name, | ||
| dtype, | ||
| allow_existing=alias is True or ignore_reallocation, | ||
| initial_value_specified=initial_value_specified, | ||
| ) | ||
|
|
||
| if alias is not True: | ||
| # use alias=True if some other code called allocate_memory for this | ||
| # register | ||
|
|
@@ -655,16 +680,24 @@ class Register(IntegerOpsMixin, AssignmentOpsMixin, RegisterInitializerMixin, Ta | |
| one or more qubits. Typically, you create a register from a | ||
| :class:`~dwave.gate.qcdl.Scope` object, which handles this parameter | ||
| for you. | ||
| initial_value: Initial value. Defaults to 0. | ||
| initial_value: Initial value. Defaults to 0. Only the first allocation | ||
| of a name takes effect, so a value given for a name that is already | ||
|
qci-amos marked this conversation as resolved.
Outdated
|
||
| allocated is rejected rather than silently discarded; see | ||
| ``ignore_reallocation``. An ``alias`` never allocates, so it never | ||
| takes a value at all. | ||
| name: Name for this register; useful for troubleshooting. If None, a | ||
| name is generated. See the ``alias`` parameter for type punning. | ||
| master_kwargs: Propagate this to the master instruction. This parameter | ||
| is intended for use by developers of QCDL. | ||
| alias: Set to True if you are aliasing an existing register, and for | ||
| type punning reuse that register's name in the ``name`` parameter. | ||
| Aliased registers are not reinitialized. | ||
| ignore_reallocation: If True, the compiler does not reallocate if | ||
| already allocated (and does not raise an exception). | ||
| Aliased registers are not reinitialized, so you may not give an | ||
| ``initial_value``. | ||
| ignore_reallocation: If True, and the name is already allocated, the | ||
|
qci-amos marked this conversation as resolved.
Outdated
|
||
| compiler does not reallocate it (and does not raise an exception). | ||
| In that case you may not give an ``initial_value``, since it would | ||
| never reach the qubit. A name that is not yet allocated is | ||
| allocated as usual and may carry a value. | ||
|
qci-amos marked this conversation as resolved.
Outdated
|
||
| scope_id: Identity of the :class:`~dwave.gate.qcdl.Scope` this register | ||
| is derived from. The | ||
| :meth:`~dwave.gate.qcdl.QCDLModuleContainer.Register` method sets | ||
|
|
@@ -727,7 +760,7 @@ def direct(q0, q1): | |
| def __init__( | ||
| self, | ||
| modules: Sequence[QCDLModule], | ||
| initial_value: int = 0, | ||
| initial_value: int | None = None, | ||
| name: str | None = None, | ||
| master_kwargs: dict[str, Any] | None = None, | ||
| alias: bool | str = False, | ||
|
|
@@ -772,16 +805,24 @@ class FixedPointRegister( | |
| one or more qubits. Typically, you create a register from a | ||
| :class:`~dwave.gate.qcdl.Scope` object, which handles this parameter | ||
| for you. | ||
| initial_value: Initial value. Defaults to 0.0. | ||
| initial_value: Initial value. Defaults to 0.0. Only the first | ||
| allocation of a name takes effect, so a value given for a name that | ||
|
qci-amos marked this conversation as resolved.
Outdated
|
||
| is already allocated is rejected rather than silently discarded; | ||
| see ``ignore_reallocation``. An ``alias`` never allocates, so it | ||
| never takes a value at all. | ||
|
qci-amos marked this conversation as resolved.
Outdated
|
||
| name: Name for this register; useful for troubleshooting. If None, a | ||
| name is generated. See the ``alias`` parameter for type punning. | ||
| master_kwargs: Propagate this to the master instruction. This parameter | ||
| is intended for use by developers of QCDL. | ||
| alias: Set to True if you are aliasing an existing register, and for | ||
| type punning reuse that register's name in the ``name`` parameter. | ||
| Aliased registers are not reinitialized. | ||
| ignore_reallocation: If True, the compiler does not reallocate if | ||
| already allocated (and does not raise an exception). | ||
| Aliased registers are not reinitialized, so you may not give an | ||
| ``initial_value``. | ||
| ignore_reallocation: If True, and the name is already allocated, the | ||
| compiler does not reallocate it (and does not raise an exception). | ||
| In that case you may not give an ``initial_value``, since it would | ||
| never reach the qubit. A name that is not yet allocated is | ||
| allocated as usual and may carry a value. | ||
|
qci-amos marked this conversation as resolved.
Outdated
|
||
| scope_id: Identity of the :class:`~dwave.gate.qcdl.Scope` this register | ||
| is derived from. The | ||
| :meth:`~dwave.gate.qcdl.QCDLModuleContainer.FixedPointRegister` | ||
|
|
@@ -825,7 +866,7 @@ def create_fixed_reg(q0, q1): | |
| def __init__( | ||
| self, | ||
| modules: Sequence[QCDLModule], | ||
| initial_value: float = 0.0, | ||
| initial_value: float | None = None, | ||
| name: str | None = None, | ||
| master_kwargs: dict[str, Any] | None = None, | ||
| alias: bool | str = False, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.