Two test cells opened with sys.path.insert(0, /root/.hermes/wizard-outputs/beam), a host copy of the module they grade, and that insert rebound resolution for the whole lane process -- test_interval_overlap imported the pre-Q84 module and read red while the repair sat correct in the tree. Each cell now binds Path(__file__).resolve().parent and asserts the import landed there. New instrument: q133_verify.py behind CHECK-q133 (5 legs) and q133_oracle.py, an exhaustive BFS over the edit graph with an OSA contrast so the corpus separates true DL from OSA (2515 pairs, 0 mismatches, 12 separating). verify.py: 10 legs, 10 pass, 0 fault. beam/CHECK: 595 tests, 0 failures.
66 lines
2.7 KiB
Python
66 lines
2.7 KiB
Python
"""Cells for wizard-work/beam/damerau_levenshtein.py -- graded by beam/CHECK.
|
|
|
|
Why the binding is written this way (Q133, 2026-10-31).
|
|
This file shipped with `sys.path.insert(0, "/root/.hermes/wizard-outputs/beam")`,
|
|
an absolute path to a *host copy* of the module. The two copies were
|
|
byte-identical when it was written (sha256 cb9769a0...), which is precisely why
|
|
the cell was dangerous: it graded whatever sat on that host path, so a broken
|
|
module in this tree would still read green and a repaired one could read red.
|
|
`tools/import_resolve_check.py` grades that binding OUTSIDE_REPO, and
|
|
`beam/CHECK` collects this file as a module-level script, so the cell now
|
|
imports the module beside it and *asserts that it did* -- a hardcoded repo path
|
|
would be wrong for a clone too, and is its own graded class
|
|
(ABS_PATH_IN_REPO).
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(HERE))
|
|
|
|
import damerau_levenshtein as _dlmod # noqa: E402
|
|
from damerau_levenshtein import damerau_levenshtein as dl # noqa: E402
|
|
|
|
# The binding is a graded fact, not an intention: if this import resolves
|
|
# anywhere but the module beside this file, the cell reds by name.
|
|
assert Path(_dlmod.__file__).resolve().parent == HERE, _dlmod.__file__
|
|
|
|
# Known values. The two constants that separate implementations are
|
|
# dl("ca","abc") == 2 and dl("abc","acb") == 1: true Damerau-Levenshtein is 2
|
|
# for the first, while restricted optimal-string-alignment (OSA) gives 3,
|
|
# because OSA may edit a substring only once. Both were decided independently
|
|
# by an exhaustive BFS over the edit graph {insert, delete, substitute,
|
|
# transpose-adjacent}: 18,528 pairs over {a,b}^<=5 and {a,b,c}^<=4, 0
|
|
# mismatches against this module, 156 pairs on which BFS and OSA disagree.
|
|
assert dl("", "") == 0
|
|
assert dl("a", "") == 1
|
|
assert dl("", "a") == 1
|
|
assert dl("ca", "abc") == 2 # true DL 2, OSA 3 -- this cell rejects OSA
|
|
assert dl("abcd", "abdc") == 1, dl("abcd","abdc") # transposition dc
|
|
assert dl("ab", "ba") == 1
|
|
assert dl("abc", "acb") == 1 # transpose b/c
|
|
print("basic ok")
|
|
|
|
# cross-check vs Levenshtein (stdlib difflib not exact). Use simple lev.
|
|
def lev(a,b):
|
|
if not a: return len(b)
|
|
if not b: return len(a)
|
|
prev=list(range(len(b)+1))
|
|
for i,ca in enumerate(a,1):
|
|
cur=[i]
|
|
for j,cb in enumerate(b,1):
|
|
cur.append(min(prev[j]+1, cur[-1]+1, prev[j-1]+(ca!=cb)))
|
|
prev=cur
|
|
return prev[-1]
|
|
|
|
import random
|
|
random.seed(42)
|
|
for _ in range(2000):
|
|
n=random.randint(0,8)
|
|
m=random.randint(0,8)
|
|
a=''.join(random.choice('ab') for _ in range(n))
|
|
b=''.join(random.choice('ab') for _ in range(m))
|
|
# DL <= lev always (more ops allowed)
|
|
assert dl(a,b) <= lev(a,b), (a,b,dl(a,b),lev(a,b))
|
|
print("monotonic ok")
|