• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qutech / qupulse / 16052262973

03 Jul 2025 01:50PM UTC coverage: 88.579%. First build
16052262973

Pull #908

github

web-flow
Merge 61690a3b0 into 79e3fb42a
Pull Request #908: Cleanup program's `__init__` and rename SimpleExpression

91 of 102 new or added lines in 5 files covered. (89.22%)

18567 of 20961 relevant lines covered (88.58%)

5.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.46
/qupulse/program/values.py
1
from dataclasses import dataclass
6✔
2
from numbers import Real
6✔
3
from typing import TypeVar, Generic, Mapping, Union
6✔
4

5
from qupulse.program.volatile import VolatileRepetitionCount
6✔
6
from qupulse.utils.types import TimeType
6✔
7

8
from qupulse.expressions import sympy as sym_expr
6✔
9
from qupulse.utils.sympy import _lambdify_modules
6✔
10

11

12
NumVal = TypeVar('NumVal', bound=Real)
6✔
13

14

15
@dataclass
6✔
16
class DynamicLinearValue(Generic[NumVal]):
6✔
17
    """This is a potential runtime-evaluable expression of the form
18

19
    C + C1*R1 + C2*R2 + ...
20
    where R1, R2, ... are potential runtime parameters.
21

22
    The main use case is the expression of for loop-dependent variables where the Rs are loop indices. There the
23
    expressions can be calculated via simple increments.
24

25
    This class tries to pass a number and a :py:class:`sympy.expr.Expr` on best effort basis.
26

27
    Attributes:
28
        base: The part of this expression which is not runtime parameter-dependent
29
        factors: A mapping of inner parameter names to the factor with which they contribute to the final value.
30
    """
31

32
    base: NumVal
6✔
33
    factors: Mapping[str, NumVal]
6✔
34

35
    def __post_init__(self):
6✔
36
        assert isinstance(self.factors, Mapping)
6✔
37

38
    def value(self, scope: Mapping[str, NumVal]) -> NumVal:
6✔
39
        """Numeric value of the expression with the given scope.
40
        Args:
41
            scope: Scope in which the expression is evaluated.
42
        Returns:
43
            The numeric value.
44
        """
NEW
45
        value = self.base
×
NEW
46
        for name, factor in self.factors:
×
NEW
47
            value += scope[name] * factor
×
NEW
48
        return value
×
49

50
    def __add__(self, other):
6✔
51
        if isinstance(other, (float, int, TimeType)):
6✔
52
            return DynamicLinearValue(self.base + other, self.factors)
6✔
53

54
        if type(other) == type(self):
6✔
55
            offsets = dict(self.factors)
6✔
56
            for name, value in other.factors.items():
6✔
57
                offsets[name] = value + offsets.get(name, 0)
6✔
58
            return DynamicLinearValue(self.base + other.base, offsets)
6✔
59

60
        # this defers evaluation when other is still a symbolic expression
NEW
61
        return NotImplemented
×
62

63
    def __radd__(self, other):
6✔
NEW
64
        return self.__add__(other)
×
65

66
    def __sub__(self, other):
6✔
67
        return self.__add__(-other)
6✔
68

69
    def __rsub__(self, other):
6✔
NEW
70
        return (-self).__add__(other)
×
71

72
    def __neg__(self):
6✔
73
        return DynamicLinearValue(-self.base, {name: -value for name, value in self.factors.items()})
6✔
74

75
    def __mul__(self, other: NumVal):
6✔
76
        if isinstance(other, (float, int, TimeType)):
6✔
77
            return DynamicLinearValue(self.base * other, {name: other * value for name, value in self.factors.items()})
6✔
78

79
        # this defers evaluation when other is still a symbolic expression
NEW
80
        return NotImplemented
×
81

82
    def __rmul__(self, other):
6✔
83
        return self.__mul__(other)
6✔
84

85
    def __truediv__(self, other):
6✔
NEW
86
        inv = 1 / other
×
NEW
87
        return self.__mul__(inv)
×
88

89
    @property
6✔
90
    def free_symbols(self):
6✔
91
        """This is required for the :py:class:`sympy.expr.Expr` interface compliance. Since the keys of
92
        :py:attr:`.offsets` are internal parameters we do not have free symbols.
93

94
        Returns:
95
            An empty tuple
96
        """
97
        return ()
6✔
98

99
    def _sympy_(self):
6✔
100
        """This method is used by :py:`sympy.sympify`. This class tries to "just work" in the sympy evaluation pipelines.
101

102
        Returns:
103
            self
104
        """
105
        return self
6✔
106

107
    def replace(self, r, s):
6✔
108
        """We mock :class:`sympy.Expr.replace` here. This class does not support inner parameters so there is nothing
109
        to replace. Importantly, the keys of the offsets are no runtime variables!
110

111
        Returns:
112
            self
113
        """
114
        return self
6✔
115

116

117
# TODO: hackedy, hackedy
118
sym_expr.ALLOWED_NUMERIC_SCALAR_TYPES = sym_expr.ALLOWED_NUMERIC_SCALAR_TYPES + (DynamicLinearValue,)
6✔
119

120
# this keeps the simple expression in lambdified results
121
_lambdify_modules.append({'DynamicLinearValue': DynamicLinearValue})
6✔
122

123
RepetitionCount = Union[int, VolatileRepetitionCount, DynamicLinearValue[int]]
6✔
124
HardwareTime = Union[TimeType, DynamicLinearValue[TimeType]]
6✔
125
HardwareVoltage = Union[float, DynamicLinearValue[float]]
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc