bench: add optional CP-SAT reference
Provide a separately installed OR-Tools model to compare a generic constraint solver with the native first-solution path without adding a production or default-test dependency. Use no-overlap, exact-fill, edge and equal-copy symmetry constraints, validate placements independently, and record model size, memory, worker count and timings for orders 8 and 9. Keep the tool only as a reference and defer DLX absent new evidence. Tests: Release CTest (9 passed) Tests: Python reference tests and compilation checks Tests: independently validated CP-SAT orders 8 and 9 Refs: #5
This commit was merged in pull request #22.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Dependency-free tests for the CP-SAT reference support code."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "tools"))
|
||||
from partridge_validator import validate # noqa: E402
|
||||
|
||||
|
||||
KNOWN_ORDER_8 = {
|
||||
"order": 8,
|
||||
"board_side": 36,
|
||||
"placements": [
|
||||
{"x": x, "y": y, "side": side}
|
||||
for x, y, side in [
|
||||
(0, 0, 8), (8, 0, 8), (16, 0, 8), (24, 0, 8),
|
||||
(32, 0, 4), (32, 4, 4), (0, 8, 8), (8, 8, 8),
|
||||
(16, 8, 8), (24, 8, 6), (30, 8, 6), (24, 14, 5),
|
||||
(29, 14, 7), (0, 16, 6), (6, 16, 3), (9, 16, 8),
|
||||
(17, 16, 7), (6, 19, 3), (24, 19, 5), (29, 21, 7),
|
||||
(0, 22, 7), (7, 22, 2), (17, 23, 1), (18, 23, 6),
|
||||
(7, 24, 6), (13, 24, 5), (24, 24, 5), (29, 28, 3),
|
||||
(32, 28, 4), (0, 29, 7), (13, 29, 7), (20, 29, 7),
|
||||
(27, 29, 2), (7, 30, 6), (27, 31, 5), (32, 32, 4),
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
assert validate(KNOWN_ORDER_8) == []
|
||||
|
||||
invalid = json.loads(json.dumps(KNOWN_ORDER_8))
|
||||
invalid["placements"].pop()
|
||||
diagnostics = validate(invalid)
|
||||
assert any("side 4 has multiplicity 3; expected 4" in d for d in diagnostics)
|
||||
assert any("not completely covered" in d for d in diagnostics)
|
||||
|
||||
invalid = json.loads(json.dumps(KNOWN_ORDER_8))
|
||||
invalid["placements"][0]["x"] = 36
|
||||
diagnostics = validate(invalid)
|
||||
assert any("outside the board" in diagnostic for diagnostic in diagnostics)
|
||||
assert any("not completely covered" in diagnostic for diagnostic in diagnostics)
|
||||
|
||||
invalid = json.loads(json.dumps(KNOWN_ORDER_8))
|
||||
invalid["placements"][1]["x"] = invalid["placements"][0]["x"]
|
||||
invalid["placements"][1]["y"] = invalid["placements"][0]["y"]
|
||||
diagnostics = validate(invalid)
|
||||
assert any("overlaps placement 0" in diagnostic for diagnostic in diagnostics)
|
||||
|
||||
invalid = json.loads(json.dumps(KNOWN_ORDER_8))
|
||||
invalid["placements"][0]["side"] = 9
|
||||
diagnostics = validate(invalid)
|
||||
assert any("invalid side length" in diagnostic for diagnostic in diagnostics)
|
||||
|
||||
if importlib.util.find_spec("ortools") is not None:
|
||||
process = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "tools" / "cp_sat_reference.py"),
|
||||
"1",
|
||||
"--workers",
|
||||
"1",
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
report = json.loads(process.stdout)
|
||||
assert report["result"]["valid"]
|
||||
assert validate(report["solution"]) == []
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user