feat: Szenario ohne Ergebnis nicht mehr zuverlässig; Schlupf aus dem Solver-Prompt
check_scenarios_solved meldet Szenarien ohne Zielwert, die nicht belegt unlösbar sind (beobachtet: SCIP-Zeitlimit ohne Lösung als 'keine Lösung' gemeldet); Befund löst Solver-Reparatur aus, bleibt er, reliable=False. Solver-Prompt: timelimit ohne Lösung ist keine Unlösbarkeit, Warmstart über createSol/addSol. Der Fachbegriff Schlupf kam aus dem Prompt selbst und ist entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
dd7206aee5
commit
4bd701c87e
2 changed files with 110 additions and 5 deletions
|
|
@ -583,3 +583,25 @@ def test_rejected_result_cannot_be_continued_and_counts_as_failure(tmp_path, mon
|
|||
zeile = benchmark.run_case({"name": "gleichung", "task": "x! = 120", "expected": {}}, "fake", 1)
|
||||
assert zeile["solver_status"] == "abgelehnt" and zeile["task_ok"] is False
|
||||
benchmark.print_table([zeile]) # die Ergebnistabelle verkraftet die Zeile
|
||||
|
||||
|
||||
def _mit_szenario(wert):
|
||||
return STRUCTURED_CODE.replace(
|
||||
" )\n\n\nif __name__",
|
||||
' scenarios=[{"name": "Ausgewogen", "description": "-", "data_override": {"faktor": 2}, '
|
||||
f'"objective_value": {wert}}}],\n )\n\n\nif __name__')
|
||||
|
||||
|
||||
def test_scenario_without_result_is_repaired():
|
||||
# beobachtet (Portfolio Nr. 2): Zeitlimit ohne Lösung wurde als „keine Lösung" gemeldet
|
||||
session = FakeSession([GOOD], [OK], [SolverCode(code=_mit_szenario(None)), SolverCode(code=_mit_szenario(8.0))])
|
||||
result = run_pipeline("Minimiere die Kosten.", session=session, max_revisions=0, max_restarts=0, solver_retries=1)
|
||||
assert session.calls[SolverCode] == 2 # der Befund hat eine Reparatur ausgelöst
|
||||
assert result.reliable and result.solver_run.unsolved_scenarios == []
|
||||
|
||||
|
||||
def test_persistent_scenario_without_result_is_unreliable():
|
||||
session = FakeSession([GOOD], [OK], [SolverCode(code=_mit_szenario(None))] * 2)
|
||||
result = run_pipeline("Minimiere die Kosten.", session=session, max_revisions=0, max_restarts=0, solver_retries=1)
|
||||
assert not result.reliable
|
||||
assert any(p.startswith("Szenario ohne Ergebnis") for p in result.reliability_issues)
|
||||
|
|
|
|||
|
|
@ -765,3 +765,49 @@ def test_scenario_failing_without_error_text_gets_a_reason():
|
|||
run = run_solver_code(code)
|
||||
befunde = verify_scenarios(run, code)
|
||||
assert any("doppelter Preis" in b and "ohne Fehlertext" in b for b in befunde)
|
||||
|
||||
|
||||
def test_scip_warm_start_as_in_the_prompt():
|
||||
# Solver-Prompt: Lösung eines Szenarios als Startlösung für das nächste (createSol/setSolVal/addSol)
|
||||
code = (
|
||||
"from pyscipopt import Model, quicksum\n"
|
||||
"m = Model(); m.hideOutput(); m.setParam('limits/time', 5)\n"
|
||||
"x = [m.addVar(f'x{i}', vtype='B') for i in range(3)]\n"
|
||||
"m.addCons(quicksum(x) == 2)\n"
|
||||
"m.setObjective(quicksum((i + 1) * x[i] for i in range(3)), 'maximize')\n"
|
||||
"sol = m.createSol()\n"
|
||||
"for v, wert in zip(x, [1, 1, 0]):\n"
|
||||
" m.setSolVal(sol, v, wert)\n"
|
||||
"assert m.addSol(sol)\n"
|
||||
"m.optimize()\n"
|
||||
"report(m.getStatus(), m.getObjVal(), {})"
|
||||
)
|
||||
run = run_solver_code(code)
|
||||
assert run.status == SolverStatus.OPTIMAL, run.error
|
||||
assert run.objective_value == 5.0
|
||||
|
||||
|
||||
def test_verify_scenarios_records_the_status_of_the_rerun():
|
||||
from solving.solver_stage import verify_scenarios
|
||||
run = run_solver_code(SZENARIO_CODE)
|
||||
verify_scenarios(run, SZENARIO_CODE)
|
||||
assert [s.verified_status for s in run.scenarios] == ["optimal", "optimal"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("wert, status, befund", [
|
||||
(None, "error", True), # beobachtet: Zeitlimit ohne Lösung, als „keine Lösung" gemeldet
|
||||
(None, None, True), # gar nicht nachgerechnet
|
||||
(None, "infeasible", False), # belegt unlösbar – ehrlich
|
||||
(None, "unbounded", False),
|
||||
(12.5, "optimal", False),
|
||||
])
|
||||
def test_scenario_without_result_is_a_finding_unless_proven_unsolvable(wert, status, befund):
|
||||
from core.model_schema import Scenario, SolverRun
|
||||
from solving.solver_stage import check_scenarios_solved
|
||||
run = SolverRun(status=SolverStatus.OPTIMAL, objective_value=1.0, scenarios=[
|
||||
Scenario(name="Ausgewogene Skalarisierung", description="-", data_override={"lambda_1": 0.5},
|
||||
objective_value=wert, verified_status=status)])
|
||||
befunde = check_scenarios_solved(run)
|
||||
assert bool(befunde) == befund
|
||||
if befund:
|
||||
assert "Ausgewogene Skalarisierung" in befunde[0] and "Startlösung" in befunde[0]
|
||||
|
|
|
|||
Loading…
Reference in a new issue