feat: fehlende Werte und simulierte Daten müssen ausgewiesen und genannt werden
- check_generated_data: nennt extracted_values einen Wert als fehlend („nicht genannt“, „unbekannt“, „offener Parameter“; Werte aus beigelegten Dateien ausgenommen) und data_assumptions ist leer -> undeclared_data (Reparatur, sonst unzuverlässig). An 211 gespeicherten Läufen 8 Treffer, alle echt. - check_answers_disclose_assumptions: bei selbst erzeugten Daten muss Antwortsatz oder eine Antwort das sagen -> Reparatur, sonst Warnung. 1 Treffer, echt. - Summenprüfung: bleibt nach Abzug der Summenzeilen keine Zeile, ist nur die Schlusszeile die Summe (Fehlalarm „Summe (0)“ kostete zwei Reparaturen). Nachlauf 20260918-140124: ehrlich_fehlende_kursdaten und ehrlich_fehlender_bedarf 2/2. Ändert die Prompt-Kennung (Solver-Prompt.txt). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d6bad28cb2
commit
20ac3f14d7
3 changed files with 159 additions and 12 deletions
|
|
@ -805,3 +805,18 @@ def test_expected_rejection_has_no_routing_to_score(monkeypatch):
|
|||
erwartet = benchmark.run_case({"name": "g", "task": "-", "expected": {"behavior": ["rejected"]}}, "fake", 1)
|
||||
falsch = benchmark.run_case({"name": "lp", "task": "-", "expected": {"objective_value": 9, "tolerance": 1e-6}}, "fake", 1)
|
||||
assert erwartet["class_ok"] is None and falsch["class_ok"] is False # eine unerwartete Ablehnung bleibt ein Fehler
|
||||
|
||||
|
||||
def test_missing_value_without_declared_assumption_is_repaired_or_unreliable():
|
||||
# ehrlich_fehlender_bedarf: fehlender Wert still eingesetzt ⇒ Reparatur; bleibt es ⇒ unzuverlässig
|
||||
from core.model_schema import ParameterDefinition
|
||||
fehlend = GOOD.model_copy(update={"parameters": GOOD.parameters + [ParameterDefinition(
|
||||
symbol="d_3", dimension="Skalar", unit="Kisten", description="Bedarf G3",
|
||||
extracted_values="im Text nicht genannt")]})
|
||||
ausgewiesen = SolverCode(code=STRUCTURED_CODE, data_assumptions=["d_3 = 0 Kisten angenommen, im Text nicht genannt"])
|
||||
session = FakeSession([fehlend], [OK], [SolverCode(code=STRUCTURED_CODE), ausgewiesen])
|
||||
result = run_pipeline("Aufgabe", session=session, max_revisions=0, max_restarts=0)
|
||||
assert session.steps.count("Solver-Reparatur") == 1 and result.reliable
|
||||
session = FakeSession([fehlend], [OK], [SolverCode(code=STRUCTURED_CODE)] * 3)
|
||||
result = run_pipeline("Aufgabe", session=session, max_revisions=0, max_restarts=0)
|
||||
assert not result.reliable and any(i.startswith("Datenherkunft") for i in result.reliability_issues)
|
||||
|
|
|
|||
|
|
@ -396,3 +396,11 @@ def test_no_decomposition_needed_without_a_solution():
|
|||
from solving.solver_stage import check_objective_decomposition
|
||||
assert check_objective_decomposition(_lauf([], status="infeasible", objective=None), make_spec()) == []
|
||||
assert check_objective_decomposition(_lauf([_tabelle([["Kosten", 51.0]])]), make_spec()) == []
|
||||
|
||||
|
||||
def test_component_starting_with_summe_is_not_dropped():
|
||||
# „Summe (0) weicht vom Zielwert ab“ in zwei Reparaturen von ehrlich_fehlende_kursdaten (20260918-140124)
|
||||
from solving.solver_stage import check_table
|
||||
rows = [["Summe der Varianzbeiträge", 0.0174321], ["Summe", 0.0174321]]
|
||||
belegt, abweichung, _ = check_table(_tabelle(rows), 0.0174321)
|
||||
assert belegt and abweichung is None
|
||||
|
|
|
|||
|
|
@ -884,3 +884,57 @@ def test_timeout_ends_a_slow_program():
|
|||
code = "def main():\n while True:\n pass\n\n\nif __name__ == '__main__':\n main()\n"
|
||||
run = run_solver_code(code, timeout=1)
|
||||
assert run.status == SolverStatus.ERROR and "Timeout nach 1 s" in run.error
|
||||
|
||||
|
||||
# --- Fehlende Werte laut Spezifikation und verschwiegene Annahmen (ehrlich_* Fälle, 18.09.2026) ---
|
||||
|
||||
def _spec_mit_fehlendem_wert(text="d_{G1} = 30, d_{G2} = 40; d_{G3} im Text nicht genannt (offener Parameter)"):
|
||||
from test_model_checks import make_spec
|
||||
return make_spec(parameters=[{"symbol": "d_j", "dimension": "Vektor", "unit": "Kisten", "description": "Bedarf",
|
||||
"extracted_values": text}])
|
||||
|
||||
|
||||
def test_missing_value_in_the_specification_must_be_declared():
|
||||
# beobachtet an ehrlich_fehlender_bedarf: d_G3 „nicht genannt“, im Programm d_G3 = 40, „optimal 270, zuverlässig“
|
||||
from core.model_schema import SolverCode
|
||||
from solving.solver_stage import check_generated_data
|
||||
code = "DATA = {'d_G3': 40}\n"
|
||||
befunde = check_generated_data(code, SolverCode(code=code, data_assumptions=[]), _spec_mit_fehlendem_wert())
|
||||
assert len(befunde) == 1 and "d_j" in befunde[0] and "data_assumptions" in befunde[0]
|
||||
ausgewiesen = SolverCode(code=code, data_assumptions=["d_G3 = 40 Kisten angenommen (nicht genannt)"])
|
||||
assert check_generated_data(code, ausgewiesen, _spec_mit_fehlendem_wert()) == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("text", ["30, 40, 55", "aus kurse.csv berechnet (im Text nicht genannt)", ""])
|
||||
def test_complete_values_and_data_files_are_not_missing(text):
|
||||
from solving.solver_stage import missing_values
|
||||
assert missing_values(_spec_mit_fehlendem_wert(text), data_files=["/tmp/lauf/kurse.csv"]) == []
|
||||
|
||||
|
||||
def _lauf_mit_antworten(*texte, headline=None):
|
||||
from core.model_schema import Answer, SolverRun
|
||||
return SolverRun(status=SolverStatus.OPTIMAL, objective_value=1.0, headline=headline,
|
||||
answers=[Answer(question_id=str(i), text=t) for i, t in enumerate(texte, 1)])
|
||||
|
||||
|
||||
SIMULIERT = "import numpy as np\nrng = np.random.default_rng(1)\nkurse = rng.normal(0, 1, (504, 3))\n"
|
||||
|
||||
|
||||
def test_answers_must_say_that_data_is_simulated():
|
||||
# beobachtet an ehrlich_fehlende_kursdaten: „Aus den Tagesschlusskursen der letzten zwei Jahre ergeben sich …“
|
||||
from core.model_schema import SolverCode
|
||||
from solving.solver_stage import check_answers_disclose_assumptions
|
||||
code = SolverCode(code=SIMULIERT, data_assumptions=["Kurse simuliert, Seed 1"])
|
||||
verschwiegen = _lauf_mit_antworten("Die Renditen betragen 19,6 %, 33,9 % und 78,5 %.", "Alpha erhält 250.000 Euro.")
|
||||
assert check_answers_disclose_assumptions(verschwiegen, SIMULIERT, code)
|
||||
offen = _lauf_mit_antworten("Auf Basis simulierter Kurse erhält Alpha 250.000 Euro.", "Das Modell ist ein QP.")
|
||||
assert check_answers_disclose_assumptions(offen, SIMULIERT, code) == []
|
||||
|
||||
|
||||
def test_assumptions_without_generated_data_need_no_disclosure():
|
||||
# angenommene Währung oder ein konstanter Preis ohne Einfluss auf das Optimum: kein Befund (sonst Fehlalarme)
|
||||
from core.model_schema import SolverCode
|
||||
from solving.solver_stage import check_answers_disclose_assumptions
|
||||
code = "DATA = {'kosten': [3, 2]}\n"
|
||||
lauf = _lauf_mit_antworten("Die Kosten betragen 9.")
|
||||
assert check_answers_disclose_assumptions(lauf, code, SolverCode(code=code, data_assumptions=["Währung Euro"])) == []
|
||||
|
|
|
|||
Loading…
Reference in a new issue