Download test_packing.py from DHDRL/AgentSquares: direct link, hf CLI and curl.
- Browser
- Download file 9.34 kB
-
https://huggingface.co/DHDRL/AgentSquares/resolve/main/test_packing.py
- Command line
-
hf download hf://DHDRL/AgentSquares/test_packing.py
-
curl -L -o test_packing.py https://huggingface.co/DHDRL/AgentSquares/resolve/main/test_packing.py
9.34 kB
| """Run: python test_packing.py (or: python -m pytest -q)""" | |
| from fractions import Fraction as F | |
| import math | |
| from packing import Sq, validate, parse_squares | |
| from bounds import classify, find_conflicts | |
| one, zero = F(1), F(0) | |
| def axis(x, y): | |
| return Sq(F(x), F(y), one, zero) | |
| def five(S): | |
| c, s = math.cos(math.pi / 4), math.sin(math.pi / 4) | |
| return [Sq(0.5, 0.5, 1.0, 0.0), Sq(S - 0.5, 0.5, 1.0, 0.0), | |
| Sq(0.5, S - 0.5, 1.0, 0.0), Sq(S - 0.5, S - 0.5, 1.0, 0.0), Sq(S / 2, S / 2, c, s)] | |
| def test_user_cases(): | |
| four = [axis(F(1, 2), F(1, 2)), axis(F(3, 2), F(1, 2)), axis(F(1, 2), F(3, 2)), axis(F(3, 2), F(3, 2))] | |
| r = validate(four, F(2)) | |
| assert r["valid"] and r["mode"] == "exact" and r["proof"] | |
| # draft bug: axis(1, ...) passed int 1 -> draft fell into float mode; now stays exact | |
| r = validate([axis(F(1, 2), F(1, 2)), axis(1, F(1, 2))], F(2)) | |
| assert not r["valid"] and r["max_overlap"] == 0.5 and r["mode"] == "exact" | |
| q = Sq(F(7, 10), F(7, 10), F(3, 5), F(4, 5)) | |
| assert validate([q], F(7, 5))["valid"] | |
| assert not validate([q], F(7, 5) - F(1, 10**9))["valid"] | |
| S = 2 + math.sqrt(2) / 2 | |
| r = validate(five(S), S) | |
| assert r["valid"] and not r["proof"] and r["mode"].startswith("float") | |
| assert not validate(five(2.70), 2.70)["valid"] | |
| def test_classify_conflicts(): | |
| ok = {"valid": True} | |
| assert classify(2.7, 2.7071, ok)[0] == "improvement" | |
| assert classify(2.7071, 2.7071, ok)[0] == "unchanged" | |
| assert classify(2.7, 2.7071, None)[0] == "unverified" | |
| assert classify(2.8, 2.7071, None)[0] == "worse" | |
| assert classify(None, 2.7071)[0] == "partial" | |
| assert classify(2.7, 2.7071, {"valid": False})[0] == "invalid" | |
| assert classify(7.5, None)[0] == "no_baseline" | |
| assert len(find_conflicts([{"n": 5, "s": 2.70, "source_url": "a"}, {"n": 5, "s": 2.71, "source_url": "b"}])) == 1 | |
| assert find_conflicts([{"n": 5, "s": 2.7, "source_url": "a"}, {"n": 6, "s": 9.0, "source_url": "b"}]) == [] | |
| def test_edge_cases(): | |
| # edge-touching side by side, exact: valid | |
| assert validate([axis(F(1, 2), F(1, 2)), axis(F(3, 2), F(1, 2))], F(2), expected_n=2)["valid"] | |
| # corner-touching diagonal: valid (skipped by dx^2+dy^2>2 prefilter? distance^2 = 2 -> checked, depth 0) | |
| assert validate([axis(F(1, 2), F(1, 2)), axis(F(3, 2), F(3, 2))], F(2))["valid"] | |
| # non-unit direction fails | |
| r = validate([Sq(F(1), F(1), F(3, 5), F(3, 5))], F(2)) | |
| assert not r["valid"] and any("unit vector" in i for i in r["issues"]) | |
| # expected_n mismatch fails | |
| r = validate([axis(F(1, 2), F(1, 2))], F(2), expected_n=2) | |
| assert not r["valid"] and "expected 2" in r["issues"][0] | |
| # sticking out | |
| assert not validate([axis(F(1, 2), F(1, 2))], F(9, 10))["valid"] | |
| # parse: exact strings and explicit degree unit | |
| sq = parse_squares([{"x": "7/10", "y": "7/10", "c": "3/5", "s": "4/5"}]) | |
| assert validate(sq, F(7, 5))["proof"] | |
| sq = parse_squares([{"x": 0.5, "y": 0.5, "theta_deg": 90}]) | |
| assert validate(sq, 1.0)["valid"] | |
| try: | |
| parse_squares([{"x": 0, "y": 0, "angle": 3}]) | |
| assert False | |
| except ValueError: | |
| pass | |
| # sweep early exit does not hide an overlap after a far square in x-order | |
| sqs = [axis(F(1, 2), F(1, 2)), axis(F(1, 2), F(5, 2)), axis(F(1, 2), F(3, 4))] | |
| assert not validate(sqs, F(3))["valid"] | |
| def test_thresholds(): | |
| from bounds import IMPROVEMENT_MATERIAL, VALIDATION_EPS, classify | |
| assert IMPROVEMENT_MATERIAL > VALIDATION_EPS | |
| OK = {"valid": True} | |
| assert classify(2.0 - 5e-9, 2.0, OK)[0] == "unchanged" # inside material band | |
| assert classify(2.0 - 2e-8, 2.0, OK)[0] == "improvement" | |
| assert classify(2.0 - 2e-8, 2.0, None)[0] == "unverified" | |
| def test_display_precision(): | |
| from tools import extract_claims | |
| cl, _ = extract_claims("s(5) = 2.70710678…", {"url": "u"}) | |
| assert cl[0]["s_uncertainty"] == 1e-8 | |
| cl, _ = extract_claims("s(68) ≤ 8.798795237218", {"url": "u"}) | |
| assert cl and cl[0]["s"] == 8.798795237218 and cl[0]["s_uncertainty"] == 0 | |
| def test_llm_plumbing(): | |
| from interfaces import LLMExtractor, MockLLMClient | |
| txt = "12 squares cannot fit below 3.9715. We found s(68) = 8.7987952 here." | |
| m = MockLLMClient([{"claims": [{"n": 12, "s": 3.9715, "kind": "lower_bound", "quote": "cannot fit below 3.9715"}, | |
| {"n": 68, "s": 8.7987952, "kind": "upper_bound_packing", "quote": "s(68) = 8.7987952"}, | |
| {"n": 29, "s": 5.9, "kind": "upper_bound_packing", "quote": "invented 5.9"}]}]) | |
| cl, notes = LLMExtractor(m).extract(txt, {"url": "u"}) | |
| assert [(c["n"], c["s"]) for c in cl] == [(68, 8.7987952)], cl | |
| cl, notes = LLMExtractor(MockLLMClient([RuntimeError("down")])).extract("s(68) = 8.7987952", {"url": "u"}) | |
| assert cl and any("rule fallback" in x for x in notes) | |
| def test_review_fixes(): | |
| from interfaces import LLMExtractor, MockLLMClient, chunk_lines, side_from_quote, RegexGuardExtractor | |
| X = lambda items, text: LLMExtractor(MockLLMClient([{"claims": items}])).extract(text, {"url": "u"})[0] | |
| # 1a. model-returned s is ignored: rounded 5.9 can't sneak in; s comes from the quote | |
| cl = X([{"n": 29, "s": 5.9, "kind": "upper_bound_packing", "quote": "s(29) = 5.93383346"}], "Best: s(29) = 5.93383346 today.") | |
| assert [(c["n"], c["s"]) for c in cl] == [(29, 5.93383346)], cl | |
| # 1b. a quote that stops mid-number is re-read from the source: 5.9 -> 5.93383346, never 5.9 | |
| cl = X([{"n": 29, "kind": "upper_bound_packing", "quote": "s(29) = 5.9"}], "s(29) = 5.93383346") | |
| assert [c["s"] for c in cl] == [5.93383346], cl | |
| # 1b'. a quote not present verbatim is dropped | |
| assert X([{"n": 29, "kind": "upper_bound_packing", "quote": "s(29) = 5.90"}], "s(29) = 5.93383346") == [] | |
| # 1c. integer s: '3' inside '13' or '2.35' never matches; n itself isn't taken as s | |
| assert side_from_quote("n=13 needs 2.35 more", 13)[0] == "2.35" | |
| assert side_from_quote("4 squares fit in side 2", 4)[0] == "2" | |
| assert side_from_quote("n = 3", 3)[0] is None | |
| # 1d. ambiguous quote (two side values) is rejected | |
| assert side_from_quote("for 29 squares, 5.9338 or 5.9340", 29)[0] is None | |
| # 1e. hedged line rejected even if the model labels it upper_bound_packing | |
| assert X([{"n": 29, "kind": "upper_bound_packing", "quote": "s(29) = 5.90"}], "Our goal: s(29) = 5.90") == [] | |
| # 1f. truncation marker outside the quote still sets uncertainty | |
| cl = X([{"n": 5, "kind": "upper_bound_packing", "quote": "s(5) = 2.70710678"}], "s(5) = 2.70710678...") | |
| assert cl and cl[0]["s_uncertainty"] == 1e-8 | |
| # 4. chunker splits on lines, never mid-number, with overlap | |
| t = "\n".join(f"row {i}: s(29) = 5.93383346" for i in range(50)) | |
| ch = chunk_lines(t, max_chars=200, overlap_lines=2) | |
| assert len(ch) > 1 and all(len(c) <= 200 for c in ch) | |
| assert all(l in t.splitlines() for c in ch for l in c.split("\n")) | |
| assert ch[0].split("\n")[-1] == ch[1].split("\n")[0] or ch[0].split("\n")[-2] == ch[1].split("\n")[0] | |
| assert "2.7071067811" in "".join(chunk_lines("x " * 30 + "2.7071067811", max_chars=40)) | |
| # 5. middle arm: guards drop hedged and impossible values | |
| cl, _ = RegexGuardExtractor().extract("n = 5 target 2.6\nn = 5 gives 1.2\nn = 5 gives 2.7071067812", {"url": "u"}) | |
| assert [(c["n"], c["s"]) for c in cl if not c.get("rejected")] == [(5, 2.7071067812)] | |
| # 7. float-validated claim just under the bound stays a candidate | |
| from bounds import float_near_bound_margin | |
| assert float_near_bound_margin(1000) > 1e-6 | |
| def test_float_near_bound(): | |
| import json as _j | |
| from agent import Config, run as _run | |
| from bounds import Bound | |
| from tools import MockTools | |
| from make_scenarios import five as _five, S5 as _S5 | |
| pg = "```packing\n" + _j.dumps(_five(_S5)) + "\n```" | |
| T = {"search": [{"url": "u", "date": "day-N"}], "pages": {"u": {"text": pg}}} | |
| r = _run({"n": [5]}, MockTools(T), {5: Bound(5, _S5 + 1.2e-8, "test", "", False)}, 8, Config()) | |
| assert not r["improvements"] and [c["n"] for c in r["candidates"]] == [5], r["summary"] | |
| r = _run({"n": [5]}, MockTools(T), {5: Bound(5, _S5 + 1e-4, "test", "", False)}, 8, Config()) | |
| assert [c["n"] for c in r["improvements"]] == [5] | |
| def test_n_and_comma(): | |
| from interfaces import LLMExtractor, MockLLMClient, n_in_quote | |
| X = lambda items, text: LLMExtractor(MockLLMClient([{"claims": items}])).extract(text, {"url": "u"})[0] | |
| assert X([{"n": 29, "kind": "upper_bound_packing", "quote": "side 2.70710678"}], "side 2.70710678 for something") == [] | |
| t = "n = 10: container side of 3,70711" | |
| cl = X([{"n": 10, "kind": "upper_bound_packing", "quote": t}], t) | |
| assert [(c["n"], c["s"]) for c in cl] == [(10, 3.70711)], cl | |
| assert n_in_quote("s(29) = 5.93", 29) and not n_in_quote("2.135 units", 13) | |
| if __name__ == "__main__": | |
| test_user_cases(); test_classify_conflicts(); test_edge_cases() | |
| print("all tests passed") | |
| test_thresholds(); test_display_precision(); test_llm_plumbing() | |
| print("threshold + llm-interface tests passed") | |
| test_review_fixes() | |
| print("review-fix tests passed") | |
| test_float_near_bound() | |
| print("float near-bound tests passed") | |
| test_n_and_comma() | |
| print("n_in_quote + comma-decimal tests passed") | |