File size: 2,330 Bytes
eab734a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | """Solver unit tests."""
from __future__ import annotations
import math
import pytest
from helpers import load_fixture
from statement_to_tikz.ir import SolveMode
from statement_to_tikz.pipeline import solve_with_fallback
from statement_to_tikz.solve import evaluate_residuals, solve_geometry
@pytest.mark.parametrize(
"fixture",
[
"equilateral.json",
"isosceles_median.json",
"right_altitude.json",
"parallelogram.json",
"circumcenter.json",
"angle_bisector.json",
],
)
def test_exact_fixtures(fixture: str) -> None:
ir, expected = load_fixture(fixture)
assert expected == "exact"
scene = solve_geometry(ir)
assert scene.mode == SolveMode.exact, scene.message
assert scene.max_residual <= 1e-4
def test_equilateral_side_lengths() -> None:
ir, _ = load_fixture("equilateral.json")
scene = solve_geometry(ir)
a = scene.coordinates["A"]
b = scene.coordinates["B"]
c = scene.coordinates["C"]
def dist(p, q):
return math.hypot(p[0] - q[0], p[1] - q[1])
assert abs(dist(a, b) - 1.0) < 1e-3
assert abs(dist(b, c) - 1.0) < 1e-3
assert abs(dist(c, a) - 1.0) < 1e-3
def test_midpoint_property() -> None:
ir, _ = load_fixture("isosceles_median.json")
scene = solve_geometry(ir)
b, c, d = (scene.coordinates[k] for k in ("B", "C", "D"))
assert abs(d[0] - 0.5 * (b[0] + c[0])) < 1e-3
assert abs(d[1] - 0.5 * (b[1] + c[1])) < 1e-3
a = scene.coordinates["A"]
# non-degenerate: A not on BC
area = abs((b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]))
assert area > 0.5
def test_underconstrained_becomes_schematic() -> None:
ir, expected = load_fixture("underconstrained_triangle.json")
assert expected == "schematic"
scene = solve_with_fallback(ir)
assert scene.mode == SolveMode.schematic
assert set(scene.coordinates) >= {"A", "B", "C"}
def test_conflicting_falls_back_schematic() -> None:
ir, expected = load_fixture("conflicting_lengths.json")
exact = solve_geometry(ir)
assert exact.mode != SolveMode.exact
scene = solve_with_fallback(ir)
assert scene.mode == SolveMode.schematic
# residuals remain large for conflicting lengths
res = evaluate_residuals(ir, scene.coordinates)
assert max(res) > 0.1
|