feat: fingerprint numpy arrays by shape, dtype, and values - #6
feat: fingerprint numpy arrays by shape, dtype, and values#6AshSgDe29071999 wants to merge 1 commit into
Conversation
Closes #1 Lazy-import numpy and compare arrays structurally with float rounding so near-equal float arrays do not false-fail checks.
LuShadowX
left a comment
There was a problem hiding this comment.
Good work — lazy import, object-array handling, and tests included. Close to mergeable; three things.
1. This force-imports numpy for projects that never use it
try:
import numpy as np
except ImportError:
np = NoneThis sits in the hot path of fingerprint, which runs on every recorded outcome. If numpy is installed but the project under test never touches it, this imports it anyway — roughly 100ms, plus the memory, for no benefit.
sys.modules.get("numpy") gives you the same answer without ever triggering an import: if the target program has not imported numpy, no value can be an ndarray.
np = sys.modules.get("numpy")
if np is not None and isinstance(obj, np.ndarray):2. The tolerance is absolute, so it depends on magnitude
np.round(x, decimals=12) is a fixed decimal cut, not a relative tolerance. The same relative error passes or fails depending on scale:
1.0 vs 1.0 + 1e-15 -> equal (relative 1e-15)
1e20 vs 1e20 + 1e5 -> NOT equal (also relative 1e-15)
Fine for values near 1, surprising elsewhere. Scaling by magnitude, or rounding the mantissa via np.frexp, would behave consistently. Either way it is worth a comment saying what guarantee is actually being offered, since this deliberately trades a little sensitivity for stability.
3. NaN only works by accident
.tolist() puts raw nan floats in the fingerprint, and nan != nan:
fingerprint(np.array([1.0, nan])) == fingerprint(np.array([1.0, nan])) # FalseIt happens to work end to end only because compare.py round-trips through JSON and json.loads hands back the same module-level NaN object each time, so list comparison hits its identity shortcut. That is a CPython implementation detail — swap the serialiser and every array containing NaN becomes a false positive.
The scalar float path already handles this explicitly (["float", "nan"]). Worth doing the same here, and testing it: an array with NaN must fingerprint equal to itself.
Given a false positive is the one failure mode this project cannot afford, I would rather it be explicit than lucky.
Thanks again — the shape/dtype/values decomposition is the right structure.
|
Thanks for this — the gap was real and #1 is now closed by #19, which Closing this one rather than iterating. I checked the branch out and ran it,
Separately, the lazy #19 routes elements back through Real issue to pick up, and the shape/dtype part was right — please do send |
Summary
numpy.ndarrayfell through to the generic object path, so equal arrays could disagree across processes and float noise failed checks.Changes
Closes #1