Skip to content

python_pytest_helper.hypothesis_strategies

Hypothesis strategies for numbers, axes and signals, shared across the monorepo.

Numbers are bounded by MAX_MAGNITUDE by default, far from overflowing, but include zero and subnormal numbers, whose loss of precision the comparisons of python_pytest_helper.assertions account for.

The strategies compose: axis strategies take strategies of their sizes, spacings and orderings, and signal strategies take a strategy of their axes, e.g. monomial_signals(position_axes(orderings=st.just(IndexOrdering.FFT))).

Functions:

  • reals –

    A strategy for finite real numbers of bounded magnitude.

  • complexes –

    A strategy for finite complex numbers of bounded magnitude.

  • sample_arrays –

    A strategy for 1D arrays of finite real or complex numbers.

  • axis_sizes –

    A strategy for numbers of samples of an axis.

  • power_of_two_sizes –

    A strategy for numbers of samples 2**n, e.g. for FFTs or n qubits.

  • axis_spacings –

    A strategy for positive sampling periods of position axes.

  • integer_axes –

    A strategy for integer axes.

  • position_axes –

    A strategy for position axes.

  • physical_axes –

    A strategy for axes of a domain, Fourier ones conjugate to a position axis.

  • sampled_signals –

    A strategy for signals of finite sampled values of bounded magnitude.

  • monomial_signals –

    A strategy for monomial signals alpha * x**power.

  • positive_polynomial_signals –

    A strategy for non-negative signals, absolute values of polynomials.

Attributes:

MAX_MAGNITUDE module-attribute

MAX_MAGNITUDE = 1000.0

The default bound of the magnitude of generated numbers.

Far enough from the largest float, about 1e308, that sums, products and powers up to about 100 of such numbers stay finite, so tests never see overflows by accident.

index_orderings module-attribute

index_orderings = st.sampled_from(list(IndexOrdering))

A strategy for all index orderings.

axis_domains module-attribute

axis_domains = st.sampled_from(list(AxisDomain))

A strategy for all axis domains.

reals

reals(max_magnitude: float = MAX_MAGNITUDE) -> SearchStrategy[float]

A strategy for finite real numbers of bounded magnitude.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
38
39
40
def reals(max_magnitude: float = MAX_MAGNITUDE) -> st.SearchStrategy[float]:
    """A strategy for finite real numbers of bounded magnitude."""
    return st.floats(min_value=-max_magnitude, max_value=max_magnitude)

complexes

complexes(max_magnitude: float = MAX_MAGNITUDE) -> SearchStrategy[complex]

A strategy for finite complex numbers of bounded magnitude.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
43
44
45
46
47
def complexes(max_magnitude: float = MAX_MAGNITUDE) -> st.SearchStrategy[complex]:
    """A strategy for finite complex numbers of bounded magnitude."""
    return st.complex_numbers(
        max_magnitude=max_magnitude, allow_nan=False, allow_infinity=False
    )

sample_arrays

sample_arrays(size: int, dtype: DTypeLike = float64, max_magnitude: float = MAX_MAGNITUDE) -> SearchStrategy[NDArray]

A strategy for 1D arrays of finite real or complex numbers.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
50
51
52
53
54
55
56
57
58
59
def sample_arrays(
    size: int, dtype: npt.DTypeLike = np.float64, max_magnitude: float = MAX_MAGNITUDE
) -> st.SearchStrategy[npt.NDArray]:
    """A strategy for 1D arrays of finite real or complex numbers."""
    elements = (
        complexes(max_magnitude)
        if np.issubdtype(dtype, np.complexfloating)
        else reals(max_magnitude)
    )
    return arrays(dtype, size, elements=elements)

axis_sizes

axis_sizes(min_size: int = 1, max_size: int = 64) -> SearchStrategy[int]

A strategy for numbers of samples of an axis.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
69
70
71
def axis_sizes(min_size: int = 1, max_size: int = 64) -> st.SearchStrategy[int]:
    """A strategy for numbers of samples of an axis."""
    return st.integers(min_value=min_size, max_value=max_size)

power_of_two_sizes

power_of_two_sizes(min_exponent: int = 0, max_exponent: int = 6) -> SearchStrategy[int]

A strategy for numbers of samples 2**n, e.g. for FFTs or n qubits.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
74
75
76
77
78
79
80
def power_of_two_sizes(
    min_exponent: int = 0, max_exponent: int = 6
) -> st.SearchStrategy[int]:
    """A strategy for numbers of samples `2**n`, e.g. for FFTs or `n` qubits."""
    return st.integers(min_value=min_exponent, max_value=max_exponent).map(
        lambda exponent: 2**exponent
    )

axis_spacings

axis_spacings(min_value: float = 0.001, max_value: float = 10.0) -> SearchStrategy[float]

A strategy for positive sampling periods of position axes.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
83
84
85
86
87
def axis_spacings(
    min_value: float = 1e-3, max_value: float = 10.0
) -> st.SearchStrategy[float]:
    """A strategy for positive sampling periods of position axes."""
    return st.floats(min_value=min_value, max_value=max_value)

integer_axes

integer_axes(draw: DrawFn, sizes: SearchStrategy[int] = _SIZES, orderings: SearchStrategy[IndexOrdering] = index_orderings) -> IntegerAxis

A strategy for integer axes.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
 98
 99
100
101
102
103
104
105
@st.composite
def integer_axes(
    draw: st.DrawFn,
    sizes: st.SearchStrategy[int] = _SIZES,
    orderings: st.SearchStrategy[IndexOrdering] = index_orderings,
) -> IntegerAxis:
    """A strategy for integer axes."""
    return IntegerAxis(size=draw(sizes), ordering=draw(orderings))

position_axes

position_axes(draw: DrawFn, sizes: SearchStrategy[int] = _SIZES, spacings: SearchStrategy[float] = _SPACINGS, orderings: SearchStrategy[IndexOrdering] = index_orderings) -> PositionAxis

A strategy for position axes.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
108
109
110
111
112
113
114
115
116
117
118
@st.composite
def position_axes(
    draw: st.DrawFn,
    sizes: st.SearchStrategy[int] = _SIZES,
    spacings: st.SearchStrategy[float] = _SPACINGS,
    orderings: st.SearchStrategy[IndexOrdering] = index_orderings,
) -> PositionAxis:
    """A strategy for position axes."""
    return PositionAxis(
        size=draw(sizes), delta_x=draw(spacings), ordering=draw(orderings)
    )

physical_axes

physical_axes(domain: AxisDomain = POSITION, sizes: SearchStrategy[int] = _SIZES, spacings: SearchStrategy[float] = _SPACINGS, orderings: SearchStrategy[IndexOrdering] = index_orderings, hbar: float = 1.0) -> SearchStrategy[PhysicalAxis]

A strategy for axes of a domain, Fourier ones conjugate to a position axis.

Parameters:

  • domain (AxisDomain, default: POSITION ) –

    The domain of the axes.

  • sizes (SearchStrategy[int], default: _SIZES ) –

    The numbers of samples.

  • spacings (SearchStrategy[float], default: _SPACINGS ) –

    The sampling periods of the position axes, or of the position axes the Fourier ones are conjugate to.

  • orderings (SearchStrategy[IndexOrdering], default: index_orderings ) –

    The index orderings, kept by the Fourier axes.

  • hbar (float, default: 1.0 ) –

    The reduced Planck constant of momentum axes.

Returns:

  • SearchStrategy[PhysicalAxis] –

    The strategy, of the position axes themselves for the position domain.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def physical_axes(
    domain: AxisDomain = AxisDomain.POSITION,
    sizes: st.SearchStrategy[int] = _SIZES,
    spacings: st.SearchStrategy[float] = _SPACINGS,
    orderings: st.SearchStrategy[IndexOrdering] = index_orderings,
    hbar: float = 1.0,
) -> st.SearchStrategy[PhysicalAxis]:
    """A strategy for axes of a domain, Fourier ones conjugate to a position axis.

    Args:
        domain: The domain of the axes.
        sizes: The numbers of samples.
        spacings: The sampling periods of the position axes, or of the position
            axes the Fourier ones are conjugate to.
        orderings: The index orderings, kept by the Fourier axes.
        hbar: The reduced Planck constant of momentum axes.

    Returns:
        The strategy, of the position axes themselves for the position domain.
    """
    x_axes = position_axes(sizes=sizes, spacings=spacings, orderings=orderings)
    if domain == AxisDomain.POSITION:
        return x_axes
    conjugate = _CONJUGATE_AXES[AxisDomain(domain)]
    return x_axes.map(lambda x_axis: conjugate(x_axis, hbar))

sampled_signals

sampled_signals(draw: DrawFn, axes: SearchStrategy[PhysicalAxis] = _POSITION_AXES, dtype: DTypeLike = float64, max_magnitude: float = MAX_MAGNITUDE) -> Signal

A strategy for signals of finite sampled values of bounded magnitude.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
164
165
166
167
168
169
170
171
172
173
@st.composite
def sampled_signals(
    draw: st.DrawFn,
    axes: st.SearchStrategy[PhysicalAxis] = _POSITION_AXES,
    dtype: npt.DTypeLike = np.float64,
    max_magnitude: float = MAX_MAGNITUDE,
) -> Signal:
    """A strategy for signals of finite sampled values of bounded magnitude."""
    axis = draw(axes)
    return Signal(axis, draw(sample_arrays(axis.size, dtype, max_magnitude)))

monomial_signals

monomial_signals(draw: DrawFn, axes: SearchStrategy[PhysicalAxis] = _POSITION_AXES, alphas: SearchStrategy[float] = _ALPHAS, powers: SearchStrategy[int] = _POWERS) -> PolynomialSignal

A strategy for monomial signals alpha * x**power.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
176
177
178
179
180
181
182
183
184
@st.composite
def monomial_signals(
    draw: st.DrawFn,
    axes: st.SearchStrategy[PhysicalAxis] = _POSITION_AXES,
    alphas: st.SearchStrategy[float] = _ALPHAS,
    powers: st.SearchStrategy[int] = _POWERS,
) -> PolynomialSignal:
    """A strategy for monomial signals `alpha * x**power`."""
    return PolynomialSignal(axis=draw(axes), alpha=draw(alphas), power=draw(powers))

positive_polynomial_signals

positive_polynomial_signals(draw: DrawFn, axes: SearchStrategy[PhysicalAxis] = _POSITION_AXES, max_degree: int = 5, coefficients: SearchStrategy[float] = _COEFFICIENTS, total: float = 1.0) -> AlgebraicSignal

A strategy for non-negative signals, absolute values of polynomials.

The signals are scaled such that their samples sum up to total.

Returns:

  • AlgebraicSignal –

    The drawn signal, the scaled absolute value of a polynomial of degree 1 to

  • AlgebraicSignal –

    max_degree with coefficients drawn from coefficients.

Source code in packages-dev/python-pytest-helper/src/python_pytest_helper/hypothesis_strategies.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
@st.composite
def positive_polynomial_signals(
    draw: st.DrawFn,
    axes: st.SearchStrategy[PhysicalAxis] = _POSITION_AXES,
    max_degree: int = 5,
    coefficients: st.SearchStrategy[float] = _COEFFICIENTS,
    total: float = 1.0,
) -> AlgebraicSignal:
    """A strategy for non-negative signals, absolute values of polynomials.

    The signals are scaled such that their samples sum up to `total`.

    Returns:
        The drawn signal, the scaled absolute value of a polynomial of degree 1 to
        `max_degree` with coefficients drawn from `coefficients`.
    """
    axis = draw(axes)
    degree = draw(st.integers(min_value=1, max_value=max_degree))
    polynomial = np.polynomial.Polynomial(
        draw(arrays(np.float64, degree + 1, elements=coefficients))
    )
    scale = total / np.sum(np.abs(polynomial(axis.values)))
    return AlgebraicSignal(axis, lambda x: scale * np.abs(polynomial(x)))