Source code for qiskit_qm_provider.parameter_table._mixins

# Copyright 2026 Arthur Strauss
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Shared QUA field-table accessor mixin for runtime and measurement tables."""

from __future__ import annotations

from typing import Any, Protocol, Union, runtime_checkable

from qm.qua._expressions import QuaArrayVariable
from quam.utils.qua_types import QuaVariable

from ._scope import require_qua_program


[docs] @runtime_checkable class TableFieldProtocol(Protocol): """Minimal interface shared by runtime :class:`Parameter` and measurement fields. Implemented by :class:`~qiskit_qm_provider.parameter_table.Parameter` and :class:`~qiskit_qm_provider.backend.measurement_field.MeasurementRegisterField`. Used by :class:`QuaFieldTable` accessors. """ name: str @property def is_declared(self) -> bool: """Whether a QUA variable is bound for this field.""" @property def var(self) -> QuaVariable | QuaArrayVariable: """QUA variable for this field (requires program scope when read).""" @property def is_array(self) -> bool: """Whether this field holds a QUA array (``True``) or a scalar (``False``).""" @property def length(self) -> int: """Number of elements: ``0`` for a scalar, the array length otherwise."""
[docs] class QuaFieldTable: """Mixin providing ParameterTable-aligned QUA variable accessors. Subclasses must define ``self.table: dict[str, TableFieldProtocol]`` and may override :meth:`_resolve_index` for index lookup semantics. """ table: dict[str, TableFieldProtocol] def _resolve_index(self, index: int) -> TableFieldProtocol: """Return the field at positional ``index`` (default: insertion order). Override in :class:`~.parameter_table.ParameterTable` to use :meth:`~.parameter_table.Parameter.get_index`. """ for i, field in enumerate(self.table.values()): if i == index: return field raise IndexError(f"No parameter with index {index} in the parameter table.")
[docs] def get_parameter(self, parameter: Union[str, int, TableFieldProtocol]) -> TableFieldProtocol: """Return the field handle (not the QUA variable). Args: parameter: Field name, positional index, or handle instance. Returns: :class:`~qiskit_qm_provider.parameter_table.Parameter` or :class:`~qiskit_qm_provider.backend.measurement_field.MeasurementRegisterField` depending on table type. """ if isinstance(parameter, str): if parameter not in self.table: raise KeyError(f"No parameter named {parameter} in the parameter table.") return self.table[parameter] if isinstance(parameter, int): return self._resolve_index(parameter) if parameter in self.parameters: return parameter raise KeyError("Provided field not in this parameter table.")
[docs] def get_variable(self, parameter: Union[str, int, TableFieldProtocol]) -> QuaVariable | QuaArrayVariable: """Return the QUA variable for a field name, index, or handle. Must be called inside ``with program():`` when resolving by index or handle. String names delegate to :meth:`__getitem__`. """ if isinstance(parameter, str): return self[parameter] if isinstance(parameter, int): field = self._resolve_index(parameter) require_qua_program(f"{type(self).__name__}.get_variable") if not field.is_declared: raise ValueError( f"No QUA variable found for parameter with index {parameter}. " f"Please declare variables within a QUA program first." ) return field.var if parameter in self.parameters: require_qua_program(f"{type(self).__name__}.get_variable") if not parameter.is_declared: raise ValueError( f"No QUA variable found for parameter {parameter.name!r}. " f"Please declare variables within a QUA program first." ) return parameter.var raise ValueError("Invalid parameter name. Please use a string, int, or field handle.")
def __getitem__(self, item: Union[str, int]) -> QuaVariable | QuaArrayVariable: """Return the QUA variable for a field name or index. Must be called inside ``with program():``. For the Python handle, use :meth:`get_parameter`. """ require_qua_program(f"{type(self).__name__}.__getitem__") if isinstance(item, str): if item not in self.table: raise KeyError(f"No parameter named {item} in the parameter table.") field = self.table[item] if field.is_declared: return field.var raise ValueError( f"No QUA variable found for parameter {item}. " f"Please declare variables within a QUA program first." ) if isinstance(item, int): field = self._resolve_index(item) if field.is_declared: return field.var raise ValueError( f"No QUA variable found for parameter with index {item}. " f"Please declare variables within a QUA program first." ) raise ValueError("Invalid parameter name. Please use a string or an int.") def __getattr__(self, item: str) -> Any: """Attribute access by field name — returns the QUA variable (not the handle).""" if item == "table": # Guard against infinite recursion if table hasn't been set yet # (e.g. during __init__ before assignment, or during unpickling). raise AttributeError("table") if item in self.table: require_qua_program(f"{type(self).__name__}.__getattr__") return self.table[item].var raise AttributeError(f"No attribute named {item} in the parameter table.") def __len__(self) -> int: """Number of fields in the table.""" return len(self.table) @property def variables(self) -> list[QuaVariable | QuaArrayVariable]: """List of QUA variables for all fields (requires program scope).""" return [self[item] for item in self.table]