Files
troupe-standup/wizard-work/beam/test_damerau_levenshtein.py
T
volt 10ec491129 volt: q133 repair -- beam test cells bind the module beside them
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.
2026-10-31 02:10:33 +00:00

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")