""" Proof that the Belief Engine reproduces the designed lessons from the GDD. Run: python prove_cases.py """ from belief_engine import (dempster, yager, pcr5, cautious, discount, fuse, belief, plausibility, conflict_K, verdict, report) def fs(*xs): return frozenset(xs) def line(t): print("\n" + "=" * 70 + "\n" + t + "\n" + "=" * 70) # ============================================================ CASE 1: "Two Truths" line("CASE 1 — 'Two Truths' (the paradox: Dempster vs Yager)") FRAME1 = {"Gardener", "Maid", "Secretary"} THRESH1 = 0.70 butler = {fs("Gardener"): 0.99, fs("Secretary"): 0.01} # "It was the Gardener" cook = {fs("Maid"): 0.99, fs("Secretary"): 0.01} # "It was the Maid" print("Ground truth: Gardener. Secretary is innocent and barely suspected.") print(f"Conflict K(Butler,Cook) = {conflict_K(butler, cook):.4f}") dem = dempster(butler, cook) print("\n-- Dempster (the Zealot) --") for s, r in report(dem, FRAME1).items(): print(f" {s:10s} suspicion(Bel)={r['suspicion']:.4f} shadow(Pl)={r['shadow']:.4f}") print(" VERDICT:", verdict(dem, FRAME1, THRESH1)) print(" >>> Dempster convicts the SECRETARY (the one BOTH thought least likely),") print(" >>> with total certainty. Convicting here = WRONGFUL EXECUTION.") yag = yager(butler, cook, FRAME1) print("\n-- Yager (the Agnostic) --") for s, r in report(yag, FRAME1).items(): print(f" {s:10s} suspicion(Bel)={r['suspicion']:.4f} shadow(Pl)={r['shadow']:.4f}") print(" VERDICT:", verdict(yag, FRAME1, THRESH1)) print(" >>> Yager refuses: nobody clears threshold. Honest paralysis (correct).") # escape path: add the Apothecary pointing at the Gardener, fuse with Yager apoth = {fs("Gardener"): 0.6, frozenset(FRAME1): 0.4} yag3 = fuse([butler, cook, apoth], "yager", FRAME1) print("\n-- Yager + Apothecary (the just path) --") for s, r in report(yag3, FRAME1).items(): print(f" {s:10s} suspicion(Bel)={r['suspicion']:.4f} shadow(Pl)={r['shadow']:.4f}") print(" VERDICT:", verdict(yag3, FRAME1, THRESH1), " <- evidence now tips toward the truly guilty Gardener") # ============================================================ CASE 2: "The Echo" line("CASE 2 — 'The Echo' (independence: Dempster inflates, Cautious does not)") FRAME2 = {"Sailor", "Innkeeper", "Stranger"} THRESH2 = 0.70 patron1 = {fs("Sailor"): 0.8, frozenset(FRAME2): 0.2} # rehearsed patron2 = {fs("Sailor"): 0.8, frozenset(FRAME2): 0.2} # same story, not independent print("Ground truth: Innkeeper. The two patrons are married & rehearsed (an Echo).") dem2 = dempster(patron1, patron2) caut2 = cautious(patron1, patron2, FRAME2) print(f"\n Two identical accusations of the Sailor (0.80 each):") print(f" Dempster -> Suspicion(Sailor) = {belief(dem2, fs('Sailor')):.4f} (INFLATED by repetition)") print(f" Cautious -> Suspicion(Sailor) = {belief(caut2, fs('Sailor')):.4f} (UNMOVED: idempotent)") print(" >>> The lesson is felt in the gap: repetition is not corroboration.") # idempotence sanity check caut_self = cautious(patron1, patron1, FRAME2) print(f"\n Idempotence check: cautious(m,m) Suspicion(Sailor) = " f"{belief(caut_self, fs('Sailor')):.4f} (should equal 0.8000)") # just path: discount the colluders, bring the Barmaid pointing at the Innkeeper barmaid = {fs("Innkeeper"): 0.55, frozenset(FRAME2): 0.45} p1_disc = discount(patron1, 0.85, FRAME2) # caught lying -> heavily discounted p2_disc = discount(patron2, 0.85, FRAME2) just2 = fuse([p1_disc, p2_disc, barmaid], "pcr5", FRAME2) print("\n-- Discount the colluders + Barmaid, fuse (PCR5): the just path --") for s, r in report(just2, FRAME2).items(): print(f" {s:10s} suspicion(Bel)={r['suspicion']:.4f} shadow(Pl)={r['shadow']:.4f}") print(" VERDICT:", verdict(just2, FRAME2, 0.50), " <- Innkeeper rises once the Echo is silenced (threshold relaxed for demo)") # ============================================================ CASE 3 quick check: PCR5 middle path line("CASE 3 — 'The Banquet' (PCR5 as the fair middle path under contradiction)") FRAME3 = {"Rival", "Widow", "Physician", "Heir"} T3 = frozenset(FRAME3) somm = {fs("Rival"): 0.8, T3: 0.2} maid = {fs("Physician"): 0.75, T3: 0.25} earl = {fs("Heir"): 0.9, T3: 0.1} # the Drunk: confident but unreliable coroner= {fs("Physician"): 0.6, fs("Rival"): 0.1, T3: 0.3} footman= {fs("Physician", "Widow"): 0.6, T3: 0.4} # set-valued: "one of the two" print("Ground truth: Physician (with Widow as accomplice).") earl_disc = discount(earl, 0.9, FRAME3) # interrogation reveals he was unconscious deps = [somm, maid, earl_disc, coroner, footman] for method in ("dempster", "yager", "pcr5", "cautious"): try: m = fuse(deps, method, FRAME3) rep = report(m, FRAME3) phys = rep["Physician"]; rival = rep["Rival"]; widow = rep["Widow"] print(f"\n {method.upper():9s} Physician Bel={phys['suspicion']:.3f}/Pl={phys['shadow']:.3f}" f" Rival Bel={rival['suspicion']:.3f}" f" Widow Pl={widow['shadow']:.3f}") print(" verdict @0.55:", verdict(m, FRAME3, 0.55)) except Exception as e: print(f"\n {method.upper():9s} -> {e}") print("\n >>> Different Methods reach different verdicts from identical evidence.") print(" >>> That is the entire game.") line("ALL CASE LESSONS REPRODUCED BY THE ENGINE.")