Constraints and Models with Z3
Z3 as a satisfiability checker for encoded nonlinear real arithmetic: the negated-goal method, its three outcomes, and why unsat is a fact about one formula, not a theorem.
A solver says unsat. What has been shown?
Not “the theorem is true.” What has been shown is that one specific formula
has no model: the conjunction of the recorded assumptions and the negation of the
goal. If that formula is the wrong encoding of the claim, unsat is a true fact
about the wrong formula. The evidence policy states the rule: “Interpret Z3
unsat only as unsatisfiability of the recorded negation and assumptions.”
Z3 is an SMT solver. Here it is used to decide satisfiability of encoded
nonlinear real-arithmetic formulas. Its nlsat engine is designed as a
complete decision procedure for quantifier-free polynomial real arithmetic, but
that is not
the same as handling arbitrary nonlinear arithmetic (transcendental terms, for
example). Z3 can still return unknown, and the engine does not check an unsat
certificate.
The engine uses Z3 in three ways: to support a claim when its negation is unsatisfiable, to refute a claim by finding a model of its negation, and to test whether a repair’s premise can be satisfied at all. This chapter explains the method and its three outcomes. It builds on Chapter 3 and Chapter 5.
Where Z3 is used
| Claim | Z3’s role | Recorded status |
|---|---|---|
lorentz-interval-invariance-1d-algebraic@1.0.0 |
The negated goal plus $\gamma^2(1-v^2)=1$ is unsat |
computation_verified_within_stated_domain |
incorrect-transform-preserves-interval@1.0.0 |
Search for a model of the negation; sat, then re-evaluate |
refuted_by_valid_counterexample |
pythagorean-coordinate-2d@1.0.0 |
The negated goal plus $\text{dot}=0$ is unsat |
computation_verified_within_stated_domain |
| P7 repair premises | Is the premise satisfiable? | A: sat; C ($0=1$): unsat |
The negated-goal method
To check “if $H$ then $G$”, ask Z3 for a model of $H \land \lnot G$:
unsat -> no assignment satisfies H and violates G (supports the claim)
sat -> here is an assignment satisfying H and violating G (a candidate witness)
unknown -> the solver gave up
For the algebraic Lorentz claim, the adapter declares four reals, adds
γ·γ·(1 − v·v) == 1, adds transformed != original and calls check().
Executed:
with the gamma hypothesis: unsat
without the gamma hypothesis: sat, model v = 0, t = 0, gamma = 0, x = -1
The second line is worth a minute. With $\gamma=0$, $t=0$, $v=0$ and $x=-1$, the boosted interval is $0^2-0^2=0$, while $t^2-x^2=-1$. So the hypothesis is doing real work: remove it, and Z3 produces a concrete model in a fraction of a second. This is the solver’s version of the hidden-assumption test from Chapter 2.
Three outcomes, three statuses
The adapter classifies the outcome in one small function, and its design is the point of the chapter:
| Z3 result on the negated claim | Evidence status | Execution status |
|---|---|---|
unsat |
computation_verified_within_stated_domain |
succeeded |
sat |
inconclusive |
failed |
unknown |
inconclusive |
unsupported |
A sat on the negated claim is not recorded as a refutation. The adapter’s
summary says: “Z3 found a model for the negated encoding; the encoding requires
audit.” The claim was expected to hold, so a model of its negation means either
that the claim is false or that the encoding is wrong. The engine refuses to guess
which, and sends the result to a person. Refutation is a separate path with its
own validation, used for claims known to be negative controls
(Chapter 5).
Z3 as a refuter
For the false shear, the counterexample path adds search constraints
($-1< v<1$, $v\neq0$, $t\neq0$, transformed $\neq$ original), gets sat, takes
the model, re-evaluates the residual with z3.simplify, and records
refuted_by_valid_counterexample only if the residual is nonzero. The recorded
witness is $t=-1$, $x=0$, $v=1/2$, with residual $-1/4$. A sat whose model
fails re-evaluation is inconclusive. An unsat here is also inconclusive:
“no counterexample under the encoded search constraints.”
Z3 as a check on premises
The repair work (the P7 phase) uses sat and unsat for a different job. A
repair’s premise must be satisfiable, or the repair is vacuous
(Chapter 27). For Candidate C, whose premise is $0=1$, Z3
returns unsat, and that is the engine’s evidence that the premise is impossible.
Here unsat is not support for a claim. It is a reason to reject. The same
solver answer carries opposite meaning depending on which formula was asked, which
is why every result stores its inputs (the assumptions and the negated goal)
next to the outcome.
What the encoding leaves out
The adapter’s recorded limitations list the gaps:
- “Unsat applies only to the exact nonlinear-real-arithmetic encoding.”
- “This is a solver result, not a proof reconstructed in Lean or Rocq.”
- “Z3’s encoding excludes square root and the physical interpretation of gamma.”
The last item is why the Z3 result is for the algebraic claim, while SymPy and the kernel bridges cover the physical one. Z3 never sees a square root. It reasons about a real $\gamma$ constrained by a polynomial equation, which is exactly the algebraic claim and nothing more.
What this establishes
Z3 is strongest as a witness-finder: a sat model checked by substitution is a
small certificate anyone can verify. It is weaker as a prover. The engine does not
check an unsat certificate; it takes the solver’s word, within the encoding, and
labels the result computation_verified_within_stated_domain, one rung below a
kernel proof. The hand check for the refutation is the substitution in
Chapter 5.
Z3 and the kernels agree on the algebraic claim, but as Chapter 6 warned, agreement is not an argument. Both work from statements written by the same project, so if the statement is wrong, they agree on the wrong thing. That is why the R3 audit compares the statements structurally (Chapter 12).
Try it yourself
uv run python content/examples/math/tool-chapters/ch09_10_sympy_z3_demo.py
(Download the script.)
Expect unsat with the gamma hypothesis and sat without it, with the model
above. Z3 may return a different model on another version; the chapter relies
only on the outcome.
Seeded error. Weaken the hypothesis to γ·γ·(1 − v·v) >= 1 in a scratch copy
and rerun. Executed on 2026-11-09 with Z3 5.3.1, using
ch05_10_18_22_24_checks.py:
ch10 weakened hypothesis negation: sat
ch10 weakened model: [x = -1, t = 0, gamma = -2, v = 0]
The weakened hypothesis leaves room for a model, so the negation is satisfiable. The original hypothesis is doing real work. The model is a candidate witness, not a refutation, until it is interpreted and re-evaluated.
Exercise. Check the weakened model by hand. Does it satisfy $\gamma^2(1-v^2)\ge1$? What are the boosted and original intervals? (Answer: $\gamma^2(1-v^2)=4\ge1$; $t'=0$ and $x'=2$, so the boosted interval is $-4$, while the original is $-1$.)
Open questions.
- Should a
saton a negated claim ever be recorded as a refutation without re-evaluation? Today the answer is no, and the result goes to audit. - Can the project check an
unsatcertificate (a proof object) and upgrade the status? That would move Z3 evidence toward the kernel rung. - Solver outcomes can vary with version and heuristics. Which of the P6 and P7
Z3 results would break if Z3 answered
unknownon a harder instance, and does the dossier degrade gracefully? (By design it should:unknownisinconclusive.)
Evidence record
From the accepted R2 report, Z3 5.3.1: math01-z3-algebraic-invariance-v1 is
succeeded, computation_verified_within_stated_domain;
math01-z3-incorrect-transform-counterexample-v1 is
refuted_by_valid_counterexample. P6 adds two Z3 checks (the coordinate unsat
and the false-claim witness). P7 adds four: candidate A premise sat, candidate B
negation unsat, candidate C premise unsat, and the overfit witness under the
patch sat.
Sources
- Leonardo de Moura and Nikolaj Bjørner, “Z3: An Efficient SMT Solver”, TACAS (2008), https://doi.org/10.1007/978-3-540-78800-3_24
- Dejan Jovanović and Leonardo de Moura, “Solving Non-Linear Arithmetic”, IJCAR
(2012), https://doi.org/10.1007/978-3-642-31365-3_27 (the
nlsatprocedure) - Z3: The Z3 Theorem Prover, https://github.com/Z3Prover/z3
The wider theory of real-closed-field decision procedures is not otherwise cited.
In the repository: src/math_engine/adapters/z3_adapter.py and
docs/methodology/evidence-policy.md.