text_to_solver_program/tests/test_resolve.py
dschlueter 7459690bf5 refactor: Pipeline-Stufen in Ordner mit ihren Prompts
Im Wurzelverzeichnis liegt nur noch TextToModel.py (Ablauf und CLI).
Jede Stufe liegt mit Code und Prompt in einem eigenen Ordner: core,
understanding, modeling, checks, solving, reporting, data, benchmark;
Tests unter tests/, Aufgaben unter tasks/. understand_task,
text_to_model/revise_model und review_assumptions/needs_work/select_best
sind aus TextToModel.py zu ihren Prompts gewandert, inhaltlich
unveraendert. Prompt-Kennung des Benchmarks bleibt 93744636095a.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:11:51 +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 core.model_schema import SolverStatus
from solving.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 core.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 == []