# SPDX-FileCopyrightText: 2014-2024 Quantum Technology Group and Chair of Software Engineering, RWTH Aachen University
#
# SPDX-License-Identifier: LGPL-3.0-or-later
from typing import Optional, List, Tuple, Union, Dict, Set, Mapping, AbstractSet
from numbers import Real
import itertools
from qupulse.expressions import Expression, ExpressionScalar
from qupulse.utils.types import MeasurementWindow
from qupulse.parameter_scope import Scope
MeasurementDeclaration = Tuple[str, Union[Expression, str, Real], Union[Expression, str, Real]]
[docs]
class MeasurementDefiner:
def __init__(self, measurements: Optional[List[MeasurementDeclaration]]):
"""This is a superclass for pulse templates that define measurements.
Args:
measurements: Sequence of ``(name, begin, length)`` tuples which define which measurements to be associated
with this object. The ``begin`` times are relative to the beginning of the defining pulse.
"""
if measurements is None:
self._measurement_windows = []
else:
self._measurement_windows = [(name,
begin if isinstance(begin, Expression) else ExpressionScalar(begin),
length if isinstance(length, Expression) else ExpressionScalar(length))
for name, begin, length in measurements]
for _, _, length in self._measurement_windows:
if (length < 0) is True:
raise ValueError('Measurement window length may not be negative')
[docs]
def get_measurement_windows(self,
parameters: Union[Mapping[str, Real], Scope],
measurement_mapping: Mapping[str, Optional[str]]) -> List[MeasurementWindow]:
"""Calculate measurement windows with the given parameter set and rename them with the measurement mapping. This
method only returns the measurement windows that are defined on `self`. It does _not_ collect the measurement
windows defined on eventual child objects that `self` has/is composed of.
Args:
parameters: Used to calculate the numeric begins and lengths of symbolically defined measurement windows.
measurement_mapping: Used to rename/drop measurement windows. Windows mapped to None are dropped.
Returns:
List of measurement windows directly defined on self
"""
try:
volatile = parameters.get_volatile_parameters().keys()
except AttributeError:
volatile = frozenset()
resulting_windows = []
for name, begin, length in self._measurement_windows:
name = measurement_mapping[name]
if name is None:
continue
assert volatile.isdisjoint(begin.variables) and volatile.isdisjoint(length.variables), "volatile measurement parameters are not supported"
begin_val = begin.evaluate_in_scope(parameters)
length_val = length.evaluate_in_scope(parameters)
if begin_val < 0 or length_val < 0:
raise ValueError('Measurement window with negative begin or length: {}, {}'.format(begin, length))
resulting_windows.append(
(name,
begin_val,
length_val)
)
return resulting_windows
@property
def measurement_parameters(self) -> AbstractSet[str]:
"""Return the parameters of measurements that are directly declared on `self`.
Does _not_ visit eventual child objects."""
return set(var
for _, begin, length in self._measurement_windows
for var in itertools.chain(begin.variables, length.variables))
@property
def measurement_declarations(self) -> List[MeasurementDeclaration]:
"""Return the measurements that are directly declared on `self`. Does _not_ visit eventual child objects."""
return [(name,
begin,
length)
for name, begin, length in self._measurement_windows]
@property
def measurement_names(self) -> Set[str]:
"""Return the names of measurements that are directly declared on `self`.
Does _not_ visit eventual child objects."""
return {name for name, *_ in self._measurement_windows}