packages feed

haskell-fsrs-7.0.0: reference/check_golden.py

"""Check that the committed golden vectors still match the Python reference.

Regenerates the vectors in memory and compares them, token by token, against
``test/Test/FSRS/GoldenData.hs``.

Numbers are compared with a tolerance rather than textually. ``exp`` and
``pow`` are not required by IEEE-754 to be correctly rounded, so the last bit
of a literal can legitimately differ between the machine that generated the
file and the machine checking it — a plain ``git diff`` would go red for
reasons that have nothing to do with the model. Everything else — structure,
identifiers, how many vectors there are — must match exactly.

Run from the repository root:

    python3 reference/check_golden.py
"""

import os
import re
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

import gen_golden  # noqa: E402

# Relative tolerance. Several orders of magnitude looser than the last-bit
# noise we are trying to ignore, and several orders tighter than any change
# a real edit to the model would produce.
RTOL = 1e-9
ATOL = 1e-12

MAX_REPORTED = 5

_SPLIT = re.compile(r"([,()\[\]])|\s+")


def tokens(text):
    return [tok for tok in _SPLIT.split(text) if tok and tok.strip()]


def as_float(token):
    try:
        return float(token)
    except ValueError:
        return None


def close(a, b):
    return abs(a - b) <= ATOL + RTOL * max(abs(a), abs(b))


def main():
    expected = tokens(gen_golden.render())
    with open(gen_golden.OUT) as fh:
        actual = tokens(fh.read())

    if len(expected) != len(actual):
        print(
            f"{gen_golden.OUT} has {len(actual)} tokens, the reference "
            f"produces {len(expected)}: the two have diverged structurally."
        )
        return 1

    drifted = []
    nudged = 0
    for index, (want, got) in enumerate(zip(expected, actual)):
        if want == got:
            continue
        want_f, got_f = as_float(want), as_float(got)
        if want_f is not None and got_f is not None and close(want_f, got_f):
            nudged += 1
            continue
        drifted.append((index, want, got))

    if drifted:
        print(f"{len(drifted)} of {len(expected)} tokens have drifted:")
        for index, want, got in drifted[:MAX_REPORTED]:
            print(f"  token {index}: committed {got!r}, reference says {want!r}")
        if len(drifted) > MAX_REPORTED:
            print(f"  ... and {len(drifted) - MAX_REPORTED} more")
        print("Run 'python3 reference/gen_golden.py' and commit the result.")
        return 1

    print(
        f"{len(expected)} tokens checked, all in agreement"
        + (f" ({nudged} within tolerance but not bit-identical)" if nudged else "")
    )
    return 0


if __name__ == "__main__":
    sys.exit(main())