Dataset-Maker / tests /test_bench_solver.py
arittrabag's picture
chore: sync space with github main dd17146
c96d602 verified
Raw History Blame Contribute Delete
2.91 kB
"""edge-greedy baseline: answer-free, deterministic, solves an easy page."""
import ast
import os
import shutil
import numpy as np
import pytest
from src.bench import build, solver
from src.bench import evaluate as E
DOCS = {"train": 1, "val": 1, "test-dev": 1, "test": 1}
FORBIDDEN = {"tearing", "provenance", "build", "docgen", "fragments", "evaluate", "baselines",
"pipeline", "packager", "inspection"}
def test_solver_imports_nothing_that_knows_answers():
path = os.path.join(os.path.dirname(solver.__file__), "solver.py")
with open(path, encoding="utf-8") as fh:
tree = ast.parse(fh.read())
for node in ast.walk(tree):
names = []
if isinstance(node, ast.Import):
names = [a.name for a in node.names]
elif isinstance(node, ast.ImportFrom):
names = [node.module or ""] + [a.name for a in node.names]
for name in names:
assert not (set(name.split(".")) & FORBIDDEN), name
@pytest.fixture(scope="module")
def easy_page(tmp_path_factory):
rel = str(tmp_path_factory.mktemp("rel"))
build.write_release(rel, ["easy"], DOCS, ["val"])
answers = E.split_answers(rel, "easy", "val")
pid = sorted(answers)[0]
return rel, pid, answers[pid]
def test_solves_easy_page_without_answers(easy_page, tmp_path):
rel, pid, answer = easy_page
isolated = tmp_path / "puzzle"
shutil.copytree(os.path.join(rel, "puzzles", "easy", "val", pid), isolated) # no answers/, no spec
sol = solver.solve(str(isolated))
score = E.score_page(E.load_page(str(isolated), answer), sol)
assert score.direct_acc >= 0.9
assert score.neighbor_acc >= 0.9
assert sol["solver"] == f"{solver.SOLVER}@{solver.SOLVER_VERSION}"
def test_deterministic(easy_page):
rel, pid, _ = easy_page
pdir = os.path.join(rel, "puzzles", "easy", "val", pid)
first, second = solver.solve(pdir), solver.solve(pdir)
assert first == second
def test_moore_contour_terminates_and_orders():
square = np.zeros((6, 7), bool)
square[1:5, 2:6] = True
assert len(solver.moore_contour(square)) == 12
neck = np.zeros((8, 8), bool)
neck[2:6, 1:3] = True
neck[3, 3:6] = True # 1-px neck revisits pixels
assert len(solver.moore_contour(neck)) < 4 * neck.sum()
def test_straight_runs_flag_lines_not_waves():
t = np.arange(0, 400, 2.0)
line = np.stack([t, 0.3 * t], axis=1)
wave = np.stack([t, 12 * np.sin(t / 15.0)], axis=1)
assert solver.straight_runs(line).mean() > 0.9
assert solver.straight_runs(wave).mean() < 0.1
def test_batch_kabsch_residual_zero_for_rigid_copies():
rng = np.random.default_rng(0)
src = rng.uniform(0, 50, (5, 20, 2))
g = solver.rigid(1.1, 4.0, -7.0)
dst = np.stack([solver.apply(g, s) for s in src])
assert np.allclose(solver.batch_kabsch_residual(src, dst), 0.0, atol=1e-9)