Add `Q`, `R`, `C`, and `GF2` fields
- add `lalib.fields.base.Field`, a blueprint for all concrete fields,
providing a unified interface to be used outside of the
`lalib.fields` sub-package
- implement `lalib.fields.complex_.ComplexField`, or `C` for short,
the field over the complex numbers (modeled as `complex` numbers)
- implement `lalib.fields.galois.GaloisField2`, or `GF2` for short,
the (finite) field over the two elements `one` and `zero`
+ adapt `lalib.elements.galois.GF2Element.__eq__()` to return
`NotImplemented` instead of `False` for non-castable `other`s
=> this fixes a minor issue with `pytest.approx()`
- implement `lalib.fields.rational.RationalField`, or `Q` for short,
the field over the rational numbers (modeled as `fractions.Fraction`s)
- implement `lalib.fields.real.RealField`, or `R` for short,
the field over the real numbers (modeled as `float`s)
- organize top-level imports for `lalib.fields`,
making `Q`, `R`, `C`, and `GF2` importable with
`from lalib.fields import *`
- provide extensive unit and integration tests for the new objects:
+ test generic and common behavior in `tests.fields.test_base`
+ test specific behavior is other modules
+ test the well-known math axioms for all fields (integration tests)
+ test the new objects' docstrings
+ add "pytest-repeat" to run randomized tests many times
2024-10-14 15:17:42 +02:00
|
|
|
"""Utilities to test the `lalib.fields` sub-package."""
|
|
|
|
|
|
|
|
import decimal
|
|
|
|
import fractions
|
|
|
|
import os
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
from lalib import elements
|
|
|
|
from lalib import fields
|
2024-10-14 16:36:02 +02:00
|
|
|
from tests import utils as root_utils
|
Add `Q`, `R`, `C`, and `GF2` fields
- add `lalib.fields.base.Field`, a blueprint for all concrete fields,
providing a unified interface to be used outside of the
`lalib.fields` sub-package
- implement `lalib.fields.complex_.ComplexField`, or `C` for short,
the field over the complex numbers (modeled as `complex` numbers)
- implement `lalib.fields.galois.GaloisField2`, or `GF2` for short,
the (finite) field over the two elements `one` and `zero`
+ adapt `lalib.elements.galois.GF2Element.__eq__()` to return
`NotImplemented` instead of `False` for non-castable `other`s
=> this fixes a minor issue with `pytest.approx()`
- implement `lalib.fields.rational.RationalField`, or `Q` for short,
the field over the rational numbers (modeled as `fractions.Fraction`s)
- implement `lalib.fields.real.RealField`, or `R` for short,
the field over the real numbers (modeled as `float`s)
- organize top-level imports for `lalib.fields`,
making `Q`, `R`, `C`, and `GF2` importable with
`from lalib.fields import *`
- provide extensive unit and integration tests for the new objects:
+ test generic and common behavior in `tests.fields.test_base`
+ test specific behavior is other modules
+ test the well-known math axioms for all fields (integration tests)
+ test the new objects' docstrings
+ add "pytest-repeat" to run randomized tests many times
2024-10-14 15:17:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
ALL_FIELDS = (fields.Q, fields.R, fields.C, fields.GF2)
|
|
|
|
NON_10_FIELDS = (fields.Q, fields.R, fields.C)
|
|
|
|
|
|
|
|
ONES = (
|
|
|
|
1,
|
|
|
|
1.0,
|
|
|
|
fractions.Fraction(1, 1),
|
|
|
|
decimal.Decimal("1.0"),
|
|
|
|
elements.one,
|
|
|
|
True,
|
|
|
|
)
|
|
|
|
|
|
|
|
ZEROS = (
|
|
|
|
0,
|
|
|
|
0.0,
|
|
|
|
fractions.Fraction(0, 1),
|
|
|
|
decimal.Decimal("+0.0"),
|
|
|
|
decimal.Decimal("-0.0"),
|
|
|
|
elements.zero,
|
|
|
|
False,
|
|
|
|
)
|
|
|
|
|
|
|
|
ONES_N_ZEROS = ONES + ZEROS
|
|
|
|
|
|
|
|
NON_ONES_N_ZEROS = (
|
|
|
|
+42,
|
|
|
|
+42.0,
|
|
|
|
fractions.Fraction(+42, 1),
|
|
|
|
decimal.Decimal("+42.0"),
|
|
|
|
-42,
|
|
|
|
-42.0,
|
|
|
|
fractions.Fraction(-42, 1),
|
|
|
|
decimal.Decimal("-42.0"),
|
|
|
|
)
|
|
|
|
|
|
|
|
NUMBERS = ONES_N_ZEROS + NON_ONES_N_ZEROS
|
|
|
|
|
2024-10-14 16:36:02 +02:00
|
|
|
DEFAULT_THRESHOLD = root_utils.DEFAULT_THRESHOLD
|
|
|
|
WITHIN_THRESHOLD = root_utils.WITHIN_THRESHOLD
|
|
|
|
NOT_WITHIN_THRESHOLD = root_utils.NOT_WITHIN_THRESHOLD
|
Add `Q`, `R`, `C`, and `GF2` fields
- add `lalib.fields.base.Field`, a blueprint for all concrete fields,
providing a unified interface to be used outside of the
`lalib.fields` sub-package
- implement `lalib.fields.complex_.ComplexField`, or `C` for short,
the field over the complex numbers (modeled as `complex` numbers)
- implement `lalib.fields.galois.GaloisField2`, or `GF2` for short,
the (finite) field over the two elements `one` and `zero`
+ adapt `lalib.elements.galois.GF2Element.__eq__()` to return
`NotImplemented` instead of `False` for non-castable `other`s
=> this fixes a minor issue with `pytest.approx()`
- implement `lalib.fields.rational.RationalField`, or `Q` for short,
the field over the rational numbers (modeled as `fractions.Fraction`s)
- implement `lalib.fields.real.RealField`, or `R` for short,
the field over the real numbers (modeled as `float`s)
- organize top-level imports for `lalib.fields`,
making `Q`, `R`, `C`, and `GF2` importable with
`from lalib.fields import *`
- provide extensive unit and integration tests for the new objects:
+ test generic and common behavior in `tests.fields.test_base`
+ test specific behavior is other modules
+ test the well-known math axioms for all fields (integration tests)
+ test the new objects' docstrings
+ add "pytest-repeat" to run randomized tests many times
2024-10-14 15:17:42 +02:00
|
|
|
|
|
|
|
N_RANDOM_DRAWS = os.environ.get("N_RANDOM_DRAWS") or 1
|
|
|
|
|
|
|
|
|
|
|
|
def is_field_element(field, value):
|
|
|
|
"""Utility method to avoid redundant logic in tests."""
|
|
|
|
element = field.cast(value)
|
|
|
|
|
|
|
|
assert element == value
|
|
|
|
assert field.validate(value)
|
|
|
|
|
|
|
|
|
|
|
|
def is_not_field_element(field, value):
|
|
|
|
"""Utility method to avoid redundant logic in tests."""
|
|
|
|
with pytest.raises(ValueError, match="not an element of the field"):
|
|
|
|
field.cast(value)
|
|
|
|
|
|
|
|
assert not field.validate(value)
|
|
|
|
assert not field.validate(value, silent=True)
|
|
|
|
|
|
|
|
with pytest.raises(ValueError, match="not an element of the field"):
|
|
|
|
field.validate(value, silent=False)
|