Source code for qupulse.pulses.measurement

# 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}