text_to_solver_program/test_resolve.py
dschlueter b74445b8d6 Erste öffentliche Fassung: Math Model Generator & Solver
Übersetzt natürlichsprachige Optimierungsaufgaben (Deutsch) per LLM in
eine geprüfte Modellspezifikation, erzeugt Solver-Code und führt ihn in
einer bubblewrap-Sandbox aus. Deterministische Prüfungen ohne LLM,
Report als Markdown/HTML, Benchmark mit 19 verifizierten Fällen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:42:58 +02:00

98 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Punkt 1: dasselbe Programm mit geänderten Daten neu rechnen – ohne LLM-Aufruf."""
import pytest
from model_schema import SolverStatus
from solver_stage import run_solver_code
# Programm nach der Vorgabe des Solver-Prompts: alle Zahlen stehen in DATA
DATA_CODE = '''
from scipy.optimize import linprog
DATA = {
"kosten": [3, 2], # Kosten je Tonne [Tsd. €]
"bedarf": 4, # Mindestmenge [t]
"obergrenze": 3, # je Rohstoff lieferbar [t]
}
def solve_model():
"""Baut das LP aus DATA auf und löst es."""
grenze = DATA["obergrenze"]
return linprog(DATA["kosten"], A_ub=[[-1, -1]], b_ub=[-DATA["bedarf"]],
bounds=[(0, grenze), (0, grenze)], method="highs")
def main():
"""Löst das Modell und meldet Ergebnis samt Restriktionen."""
res = solve_model()
x, y = float(res.x[0]), float(res.x[1])
report("optimal" if res.status == 0 else "error", float(res.fun), {"x": x, "y": y},
constraints=[{"name": "Bedarf", "lhs": x + y, "sense": ">=", "rhs": DATA["bedarf"]}],
variables=[{"name": "x", "value": x, "var_type": "continuous", "lb": 0, "ub": DATA["obergrenze"]}])
if __name__ == "__main__":
main()
'''
def test_run_without_override_uses_own_data():
run = run_solver_code(DATA_CODE)
assert run.status == SolverStatus.OPTIMAL
assert run.objective_value == pytest.approx(9) # 1t * 3 + 3t * 2
assert run.data["bedarf"] == 4
def test_changed_data_is_used_without_touching_the_code():
run = run_solver_code(DATA_CODE, data={"bedarf": 5})
assert run.status == SolverStatus.OPTIMAL
assert run.objective_value == pytest.approx(12) # 2t * 3 + 3t * 2
assert run.data["bedarf"] == 5 and run.data["kosten"] == [3, 2] # nur der geänderte Wert wird ersetzt
def test_constraints_follow_the_changed_data():
run = run_solver_code(DATA_CODE, data={"bedarf": 5})
assert run.constraints[0].rhs == 5
def test_infeasible_data_is_reported_not_crashing():
run = run_solver_code(DATA_CODE, data={"bedarf": 99})
assert run.status in (SolverStatus.ERROR, SolverStatus.INFEASIBLE)
# --- CLI-Modus: gespeichertes Ergebnis neu rechnen ---
def gespeichertes_ergebnis(tmp_path):
import json
from model_schema import PipelineResult, SolverCode
from test_model_checks import make_spec
from test_review import make_review
result = PipelineResult(task="Aufgabe", specification=make_spec(), review=make_review(), revisions=0,
solver_code=SolverCode(code=DATA_CODE))
pfad = tmp_path / "ergebnis.json"
pfad.write_text(result.model_dump_json(indent=2), encoding="utf-8")
return pfad
def test_resolve_without_data_reproduces_the_result(tmp_path):
from TextToModel import resolve_result
neu = resolve_result(str(gespeichertes_ergebnis(tmp_path)))
assert neu.solver_run.objective_value == pytest.approx(9)
assert neu.reliable and neu.usage is None # kein LLM-Aufruf
def test_resolve_with_changed_data(tmp_path):
import json
from TextToModel import resolve_result
daten = tmp_path / "daten.json"
daten.write_text(json.dumps({"bedarf": 5}), encoding="utf-8")
neu = resolve_result(str(gespeichertes_ergebnis(tmp_path)), str(daten))
assert neu.solver_run.objective_value == pytest.approx(12)
assert neu.solver_run.data["bedarf"] == 5
def test_resolve_keeps_specification_and_checks_feasibility(tmp_path):
from TextToModel import resolve_result
neu = resolve_result(str(gespeichertes_ergebnis(tmp_path)))
assert neu.specification.problem_class.value.startswith("Linear")
assert neu.solver_run.feasibility == []