← Adversarial Math From First Principles

Symbolic Algebra with SymPy

Exact symbolic algebra for expanding residuals and substituting rationals, and what it does not establish: SymPy never checked the subluminal domain it is credited with.

The accepted report says the physical Lorentz claim is computation_verified_within_stated_domain, and the recorded domain is “real t, x, v with |v| < 1 and c = 1.” A reader naturally assumes SymPy checked the condition $\lvert v\rvert<1$.

It did not. Executed:

gamma = 1/sqrt(1 - v^2)       (v declared real)
simplify(gamma^2 (1 - v^2) - 1)               -> 0
same expression with v = 2    (so 1 - v^2 = -3) -> 0

The residual is zero at $v=2$, where $\lvert v\rvert<1$ is false and the square root is not real. SymPy’s sqrt(-3) is the imaginary number $i\sqrt3$, its square is $-3$, and the algebra closes anyway. The simplifier proves an identity of expressions; it has no idea what range of $v$ the physics wants. The adapter’s own recorded limitation says as much: “SymPy’s simplifier does not itself prove that physical side condition.”

So the “stated domain” in the status name says where the claim lives, not what SymPy enforced. That is why the kernel proof of the bridge (Chapter 11) matters: Lean needs $\lvert v\rvert<1$ explicitly, to avoid a junk-value trap of its own.

SymPy gives the engine exact symbolic algebra: expanding, factoring, simplifying residuals and substituting exact rationals. This chapter shows what a SymPy result establishes and what it does not. It builds on claims (Chapter 1), the status ladder (Chapter 3) and counterexamples (Chapter 5).

Where SymPy is used

Claim SymPy’s role Recorded status
lorentz-interval-invariance-1d-physical@1.0.0 Substitute $\gamma = 1/\sqrt{1-v^2}$ into the boost; the residual simplifies to $0$ computation_verified_within_stated_domain
lorentz-gamma-relation-1d@1.0.0 simplify(γ²(1−v²) − 1) is 0 computation_verified_within_stated_domain
incorrect-transform-preserves-interval@1.0.0 Validate the chosen witness $t=1$, $x=0$, $v=1/2$ by exact rational substitution refuted_by_valid_counterexample
pythagorean-coordinate-2d@1.0.0 Residual identity $d^2(B,C)-d^2(A,B)-d^2(A,C)+2\,\text{dot}=0$ computation_verified_within_stated_domain

What SymPy is good for

Residuals. Subtract the two sides of an identity and ask whether the result is zero. For the boost with $\gamma$ left symbolic, SymPy’s factor gives (executed):

$$ (\gamma(t-vx))^2 - (\gamma(x-vt))^2 - (t^2-x^2) = (x-t)(x+t)(\gamma^2v^2 - \gamma^2 + 1) = (x^2-t^2)\bigl(1-\gamma^2(1-v^2)\bigr). $$
The factored form is the [Chapter 20](../20-chapter/) derivation, produced by machine: the residual vanishes exactly when $\gamma^2(1-v^2)=1$. A factored residual tells you more than a bare `True`, because it shows *what* has to vanish.

For the false shear (executed), the residual factors as $-tv(tv+2x)$: the formula from Chapter 5, with its zero set in plain view.

Exact arithmetic. Rational(1, 2) is exact. The counterexample validator substitutes $t=1$, $x=0$, $v=1/2$ and gets $3/4$ and $1$, with residual $-1/4$. No floating-point rounding can hide or invent a difference.

A nonzero result is not a refutation

verify_physical_lorentz_claim declares t, x, v, gamma real, builds the boost, substitutes the gamma definition and calls simplify(together(...)). If the result equals 0, it returns computation_verified_within_stated_domain with execution succeeded. If not, it returns inconclusive with execution failed, not “refuted”.

That asymmetry is deliberate and correct, because simplify is a heuristic. A nonzero output can mean that the expression really is nonzero, or only that the simplifier did not recognize it as zero. Only the counterexample path (Chapter 5) turns a nonzero value into a refutation, and only after substituting a concrete witness.

Every result stores its own limitations. The physical one reads: “This is symbolic computation, not acceptance by a theorem-prover kernel,” “the domain condition |v| < 1 is recorded by the claim contract; SymPy’s simplifier does not itself prove that physical side condition,” and “This result addresses the mathematical encoding, not empirical adequacy.”

Why simplify can be trusted here and not everywhere

The identity $\left(1/\sqrt a\right)^2 a = 1$ holds for any $a\neq0$ once you accept that $\left(\sqrt a\right)^2 = a$. That is a property of the square root as an algebraic function, and SymPy applies it symbolically with no case split on the sign of $a$. In complex arithmetic it is still true; the reals add no extra constraint. This is why SymPy “succeeds” at $v=2$.

For identities whose truth depends on a branch, the same style of check becomes unsafe. Examples are $\sqrt{a^2} = \lvert a\rvert$ (not $a$), the logarithm of a product, and the ranges of inverse trigonometric functions. Declaring a symbol real=True helps some simplifications and not others. The engine’s rule is the right one: treat the output as computation within an encoding, record the encoding, and take anything that depends on a branch or a side condition to a kernel (Chapters 11 and 12).

What this establishes

SymPy is the engine’s fastest sanity check and its weakest evidence. It earns a status below kernel verification and above nothing: a quick, exact, repeatable computation whose scope is the expression it was given. The factorizations above can be checked by hand in a few lines; no review is recorded.

The SymPy encoding also differs from the kernels’ in structure. SymPy’s gamma binding quantifies only over $v$, because its proposition substitutes $\gamma$, while the claim-level, Lean and Rocq bindings quantify over both. The R3 audit recorded this, and the gamma contract was versioned to 1.1.0 so that $\gamma$ is explicit. The relation between the SymPy result and the kernel results is semantic_equivalence_not_established: SymPy works with a complex-capable sqrt, and each kernel uses its own real sqrt.

Try it yourself

uv run python content/examples/math/tool-chapters/ch09_10_sympy_z3_demo.py
uv run python scripts/ci_negative_controls.py

The first reproduces this chapter’s executed lines (download the script). The second reruns the engine’s own SymPy and Z3 refutations and the placeholder rejections.

Seeded error. In the demo, change 1 - v**2 to 1 + v**2 in gamma. The residual should no longer simplify to 0. In the adapter, that outcome is inconclusive, not “refuted”.

Exercise. By hand, show that $\sqrt{a^2} = a$ fails for $a=-2$. Then predict what simplify(sqrt(a**2) - a) returns when a has no assumptions, when it is declared real=True, and when it is declared nonnegative=True. (Answer: $\sqrt{4} = 2 \neq -2$. With SymPy 1.14 the three results are -a + sqrt(a**2) (left alone), -a + Abs(a) (the branch made explicit) and 0. Only a sign assumption lets the simplifier close the identity.)

Open questions.

  1. The recorded encoded_domain says $\lvert v\rvert<1$ although SymPy never enforces it. Should the status text change, or the domain field? Either would be a new claim version.
  2. Is there a SymPy workflow that would respect the side condition, such as refine with Q.positive(1 - v**2), and would it merit a stronger status? (Not tried here.)
  3. How should a reader tell a SymPy True from a kernel Qed at a glance? The status names differ, and the visual difference may need a convention.

Evidence record

From the accepted R2 report (generated 2026-10-23), SymPy 1.14.0: math01-sympy-physical-invariance-v1 and math01-sympy-gamma-relation-v1 are both succeeded / computation_verified_within_stated_domain; math01-sympy-incorrect-transform-counterexample-v1 is refuted_by_valid_counterexample. The P6 phase adds two SymPy checks and P7 three; the P6 and P7 records count them as 2 and 3 of their mathematical executions.

Sources

SymPy’s own documentation on simplify, assumptions and sqrt branches would be the right software_documentation citation; none is cited yet. In the repository: src/math_engine/adapters/sympy_adapter.py.