"""Wires CaseState through belief_engine for the four Methods + discounting.""" from __future__ import annotations from functools import reduce from belief_engine import ( dempster, yager, pcr5, cautious, discount as bel_discount, belief, plausibility, conflict_K, ) from .case_loader import CaseData from .case_state import CaseState, Deposition, Verdict def _conflict_total(masses: list[dict]) -> float: """Pairwise K averaged — a simple, readable proxy for the Doubt meter.""" if len(masses) < 2: return 0.0 pairs, total = 0, 0.0 for i in range(len(masses)): for j in range(i + 1, len(masses)): total += conflict_K(masses[i], masses[j]) pairs += 1 return total / pairs def _apply_discount(d: Deposition, frame: set[str]) -> dict: if d.alpha <= 0: return d.mass return bel_discount(d.mass, d.alpha, frame) def recompute(s: CaseState, c: CaseData) -> None: frame = set(c.dock) if not s.depositions: s.combined_mass = None s.bel = {sp: 0.0 for sp in c.dock} s.pl = {sp: 1.0 for sp in c.dock} s.conflict_K = 0.0 return masses = [_apply_discount(d, frame) for d in s.depositions] s.conflict_K = _conflict_total(masses) method = s.active_method try: if method == "dempster": combined = reduce(dempster, masses) elif method == "yager": combined = reduce(lambda a, b: yager(a, b, frame), masses) elif method == "pcr5": combined = reduce(pcr5, masses) elif method == "cautious": combined = reduce(lambda a, b: cautious(a, b, frame), masses) else: raise ValueError(method) except Exception: # Total-conflict guard etc. — leave bars at 0 rather than crash. s.combined_mass = None s.bel = {sp: 0.0 for sp in c.dock} s.pl = {sp: 0.0 for sp in c.dock} return s.combined_mass = combined s.bel = {sp: belief(combined, {sp}) for sp in c.dock} s.pl = {sp: plausibility(combined, {sp}) for sp in c.dock} def deliver_verdict(s: CaseState, c: CaseData, suspect: str) -> str: """Returns outcome string and mutates s.""" if s.bel is None or s.bel.get(suspect, 0.0) < c.threshold: s.outcome = "mistrial" elif suspect == c.ground_truth.culprit: s.outcome = "just" else: s.outcome = "wrongful" s.verdict = Verdict( suspect=suspect, method=s.active_method, suspicion=s.bel.get(suspect, 0.0), threshold=c.threshold, ) return s.outcome