The eight test files Q98 leaves open (plus test_bfs.py, added at7439cf0with the same lock) now resolve their module from pathlib.Path(__file__).resolve().parent instead of /root/.hermes/wizard-outputs/<lane>. tools/import_resolve_check.py --repo . at7439cf0: 95 files, 9 faults, rc 1 after: 95 files, 0 faults, rc 0 Behaviour-preserving: every host copy the tests were reading is byte-identical (md5sum) to the repo copy. Three absolute literals in spark/test_validator.py that the static leg does not grade (load_json(...) argument strings at pre-fix lines 12/16/25) are re-pointed too. ledger/test_scratch_manifest.py was red before any edit (str ROOT vs gen_manifest ROOT.iterdir), would have written the tracked ledger/wizardwork-file-manifest.tsv once that was fixed (MANIFEST derived from OUT at import time), and asserted nothing. It now re-points ROOT/OUT/MANIFEST, guards MANIFEST inside a tempfile.mkdtemp(), and asserts the exact manifest row. Non-vacuity: scratch/q98b/poison_probe.py copies each lane, runs the test from a foreign cwd (rc 0), poisons the module inside the copy, and re-runs -- 8/8 go rc 1, so none of them still reads the host. Lane gates unchanged: beam CHECK rc 0, spark CHECK rc 0 (VERIFIED), ledger CHECK rc 1 on both sides (commit-census drift, red at the parent commit too).
71 lines
1.6 KiB
Python
71 lines
1.6 KiB
Python
import pathlib
|
|
import sys
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
|
|
from count_min_sketch import CountMinSketch, optimal_shape
|
|
import struct
|
|
|
|
def eq(a, b):
|
|
assert a == b, (a, b)
|
|
|
|
# shape
|
|
w, d = optimal_shape(0.01, 0.01)
|
|
assert w > 0 and d > 0
|
|
|
|
# basic counts
|
|
cms = CountMinSketch.create(0.01, 0.01)
|
|
for tok in ["a","b","a","c","a","b","d"]:
|
|
cms.add(tok)
|
|
eq(cms.estimate("a"), 3)
|
|
eq(cms.estimate("b"), 2)
|
|
eq(cms.estimate("c"), 1)
|
|
eq(cms.estimate("d"), 1)
|
|
# never underestimates for unseen? unseen estimate >= 0; check zero-ish (may be >0 due to collisions but >=0)
|
|
assert cms.estimate("zzzzzz") >= 0
|
|
eq(cms.total, 7)
|
|
|
|
# weighted add
|
|
cms2 = CountMinSketch(100, 5)
|
|
cms2.add("x", 5)
|
|
eq(cms2.estimate("x"), 5)
|
|
|
|
# validation
|
|
for bad in [0, -1]:
|
|
try:
|
|
CountMinSketch(bad, 3); raise SystemExit("no raise width")
|
|
except ValueError: pass
|
|
try:
|
|
CountMinSketch(3, bad); raise SystemExit("no raise depth")
|
|
except ValueError: pass
|
|
try:
|
|
cms2.add("y", -1); raise SystemExit("no raise neg count")
|
|
except ValueError: pass
|
|
|
|
# roundtrip
|
|
b = cms.to_bytes()
|
|
cms3 = CountMinSketch.from_bytes(b)
|
|
eq(cms3.estimate("a"), 3)
|
|
eq(cms3.total, 7)
|
|
eq(cms3.width, cms.width)
|
|
eq(cms3.depth, cms.depth)
|
|
|
|
# merge
|
|
# use same shape
|
|
a = CountMinSketch(50, 4)
|
|
b = CountMinSketch(50, 4)
|
|
a.add("k", 3); b.add("k", 2); b.add("l", 1)
|
|
m = a.merge(b)
|
|
eq(m.estimate("k"), 5)
|
|
eq(m.estimate("l"), 1)
|
|
eq(m.total, 6)
|
|
|
|
# top_k
|
|
top = cms.top_k(["a","b","c","d","zzz"], 2)
|
|
eq(top, ["a","b"])
|
|
|
|
# from_bytes validation
|
|
try:
|
|
CountMinSketch.from_bytes(b"short"); raise SystemExit("no raise trunc")
|
|
except ValueError: pass
|
|
|
|
print("ALL TESTS PASSED")
|