kwvtSA9ed3 / code /symbolic.py
DineshAI's picture
All six claims decided with reproducible evidence (5 VERIFIED, 1 FALSIFIED as stated in the main text)
e2d54c9 verified
Raw
History Blame Contribute Delete
3.04 kB
"""Shared machinery for the symbolic proof certificates (Route A).
The proofs in Appendix B.3 never touch individual atoms: every step is a statement
about the *aggregates*
A_i := integral over S_{1,eps} of e^{r_i} dp_t
B_i := integral over S_{2,eps} of e^{r_i} dp_t
M_i := integral over X \\ S_eps of e^{r_i} dp_t (so Z_i = A_i+B_i+M_i)
together with the masses (a_t, b_t, m_t). Reformulating a proof over these
finitely many symbols is therefore *exact*, not a discretisation, and it lets
sympy verify each step for ALL admissible parameter values at once -- which is
what a universally quantified lemma needs and a grid search can never give.
Convention throughout: r_1* = r_2* = 0 (WLOG, since the update is invariant under
adding a constant to r_i -- Z_i absorbs it; that invariance is itself verified in
`symbolic_claim1.check_shift_invariance`).
"""
from __future__ import annotations
import sympy as sp
def prove_nonneg(expr: sp.Expr, name: str) -> tuple[bool, str]:
"""Verify expr >= 0 for every value consistent with its symbols' assumptions."""
simplified = sp.simplify(expr)
verdict = simplified.is_nonnegative
if verdict is True:
return True, f"{name}: sympy proves {simplified} >= 0 from symbol assumptions"
# Fall back to a factorisation whose factors are individually signed.
factored = sp.factor(simplified)
if factored.is_nonnegative is True:
return True, f"{name}: factored as {factored} >= 0"
return False, f"{name}: could NOT establish {simplified} >= 0 (is_nonnegative={verdict})"
def prove_identity(lhs: sp.Expr, rhs: sp.Expr, name: str) -> tuple[bool, str]:
"""Verify lhs == rhs as an algebraic identity."""
diff = sp.simplify(sp.together(sp.expand(lhs - rhs)))
ok = sp.simplify(diff) == 0
return ok, f"{name}: simplify(lhs - rhs) = {diff}" + ("" if ok else " <-- NOT zero")
def prove_positive(expr: sp.Expr, name: str) -> tuple[bool, str]:
simplified = sp.simplify(expr)
if simplified.is_positive is True:
return True, f"{name}: sympy proves {simplified} > 0"
factored = sp.factor(simplified)
if factored.is_positive is True:
return True, f"{name}: factored as {factored} > 0"
return False, f"{name}: could NOT establish {simplified} > 0"
def implies_under_constraints(
hypothesis: list[sp.Expr], conclusion: sp.Expr, syms: list[sp.Symbol], name: str
) -> tuple[bool, str]:
"""Check that `conclusion >= 0` follows from `h >= 0 for h in hypothesis`.
Strategy: substitute each hypothesis by a fresh non-negative slack symbol and
ask sympy to establish the conclusion's sign. Used where a bare `simplify`
cannot see the sign because it depends on the hypotheses.
"""
subs = {}
slacks = []
for k, h in enumerate(hypothesis):
s = sp.Symbol(f"slack_{k}", nonnegative=True)
slacks.append((h, s))
subs[h] = s
expr = conclusion
for h, s in slacks:
expr = expr.subs(h, s)
return prove_nonneg(expr, name)