hex

21.6. HexRCF: a decision procedure for univariate real arithmetic🔗

21.6.1. Introduction🔗

The rcf tactic proves closed propositions about exactly one quantified real variable by exact decision. Its input is one universal or existential quantifier over , or over a half-open interval Set.Ioc a b, with no other free variables. The quantifier body may use Boolean combinations of comparisons between univariate polynomials with rational coefficients.

The computation uses exact integer and rational arithmetic. It isolates the real roots that can change an atom's sign, evaluates the formula on the resulting cells, and emits a certificate for Lean's kernel to check. No floating-point approximation occurs.

Import HexRCF and write rcf at the goal:

example : x : , x ^ 2 + 1 > 0 := (x : ), x ^ 2 + 1 > 0 All goals completed! 🐙

21.6.2. The four quantifier forms🔗

Quantification over the whole real line may be universal or existential. The body can combine equalities, inequalities, conjunctions, disjunctions, negations, and implications:

/-- A universal sentence over the real line. -/ example : x : , x ^ 2 1 x ^ 4 - x ^ 2 0 := (x : ), x ^ 2 1 x ^ 4 - x ^ 2 0 All goals completed! 🐙 /-- An existential sentence over the real line. -/ example : x : , x ^ 3 - x - 1 = 0 1 < x x < 2 := x, x ^ 3 - x - 1 = 0 1 < x x < 2 All goals completed! 🐙

The bounded forms use Set.Ioc a b, which denotes (a, b]. The lower endpoint is excluded and the upper endpoint is included:

/-- Universal quantification over `(0, 1]`. -/ example : x : , x Set.Ioc (0 : ) 1 x > 0 := x Set.Ioc 0 1, x > 0 All goals completed! 🐙 /-- Existential quantification over `(0, 1]`. The upper endpoint is a witness. -/ example : x : , x Set.Ioc (0 : ) 1 x = 1 := x Set.Ioc 0 1, x = 1 All goals completed! 🐙

An interval is empty when a ≥ b. Universal statements on an empty interval are true, while existential statements are false. These examples also show that the lower endpoint is not part of a nonempty interval:

example : x : , x Set.Ioc (1 : ) 1 x ^ 2 < 0 := x Set.Ioc 1 1, x ^ 2 < 0 All goals completed! 🐙 example : ¬ x : , x Set.Ioc (1 : ) 1 x = x := ¬ x Set.Ioc 1 1, x = x x:hx:x Set.Ioc 1 1right✝:x = xFalse All goals completed! 🐙 example : x : , x Set.Ioc (0 : ) 1 x 0 := x Set.Ioc 0 1, x 0 All goals completed! 🐙

21.6.3. Polynomial and rational syntax🔗

Polynomial expressions may use numerals, the quantified variable, +, -, *, negation, natural powers, and division by rational constants. The first example exercises all of these arithmetic forms. The tactic clears rational denominators before constructing its certificate:

example : x : , -(2 * x - 1) ^ 2 / 3 + 1 / 5 1 / 5 := (x : ), -(2 * x - 1) ^ 2 / 3 + 1 / 5 1 / 5 All goals completed! 🐙 example : x : , (x < 0 x 0) (x 0 x > 0) (x = 0 x 0) := (x : ), (x < 0 x 0) (x 0 x > 0) (x = 0 x 0) All goals completed! 🐙

The coefficients may be any exact rationals, but bounded endpoints must be dyadic rationals. Thus 1 / 3 is valid as a coefficient and is not valid as an endpoint. An endpoint is supported exactly when its reduced denominator is a power of two.

The proposition must contain exactly one quantified real variable and no other free variables. Nested quantifiers, symbolic coefficients, division by an expression containing the variable, and non-polynomial functions such as Real.sin are outside the supported fragment. The tactic reports which of these conditions failed.

21.6.4. False sentences and fall-through🔗

The tactic constructs proofs only for true sentences. For a false universal sentence it identifies a cell on which the body is false:

example : x : , x ^ 2 > 0 := (x : ), x ^ 2 > 0 rcf: the universal sentence is false on the root cell isolated in (-2, 2] (x : ), x ^ 2 > 0
rcf: the universal sentence is false on the root cell isolated in (-2, 2]

For a false existential sentence it reports that all relevant cells were checked and that none supplies a witness:

example : x : , x ^ 2 + 1 = 0 := x, x ^ 2 + 1 = 0 rcf: the existential sentence is false. Every relevant decomposition cell was checked and found false, so there is no witness x, x ^ 2 + 1 = 0
rcf: the existential sentence is false. Every relevant decomposition
cell was checked and found false, so there is no witness

A supported sentence may be false even though reification and certified decision both succeed. Such a checked false verdict never becomes a proof term: rcf reports the relevant false-sentence diagnostic above. An out-of-fragment goal is different: reification fails before the decision procedure runs. Both are ordinary tactic failures, so tactic combinators may fall through to another method. Here the unquantified goal is outside the fragment and norm_num handles it:

example : (0 : ) < 1 := 0 < 1 first | 0 < 1 | All goals completed! 🐙

21.6.5. Rewriting other interval conventions🔗

Only Set.Ioc is accepted directly. If a goal uses a closed interval Set.Icc, separate the lower endpoint from the remaining half-open interval. For a predicate φ the exact rewrites are:

  • ∀ x ∈ Set.Icc a b, φ x becomes (a ≤ b → φ a) ∧ ∀ x ∈ Set.Ioc a b, φ x.

  • ∃ x ∈ Set.Icc a b, φ x becomes a ≤ b ∧ (φ a ∨ ∃ x ∈ Set.Ioc a b, φ x).

For an open interval Set.Ioo, exclude the upper endpoint from Set.Ioc:

  • ∀ x ∈ Set.Ioo a b, φ x becomes ∀ x ∈ Set.Ioc a b, x ≠ b → φ x.

  • ∃ x ∈ Set.Ioo a b, φ x becomes ∃ x ∈ Set.Ioc a b, φ x ∧ x ≠ b.

After the endpoint proposition has been handled separately, rcf can prove the remaining singly quantified part. This worked closed-interval example checks the lower endpoint separately and sends the Set.Ioc tail to rcf:

example : x : , x Set.Icc (0 : ) 1 x ^ 2 1 := x Set.Icc 0 1, x ^ 2 1 endpoint:0 ^ 2 1 x Set.Icc 0 1, x ^ 2 1 endpoint:0 ^ 2 1tail: x Set.Ioc 0 1, x ^ 2 1 x Set.Icc 0 1, x ^ 2 1 endpoint:0 ^ 2 1tail: x Set.Ioc 0 1, x ^ 2 1x:hx:x Set.Icc 0 1x ^ 2 1 endpoint:0 ^ 2 1tail: x Set.Ioc 0 1, x ^ 2 1x:hx:x Set.Icc 0 1h:0 = xx ^ 2 1endpoint:0 ^ 2 1tail: x Set.Ioc 0 1, x ^ 2 1x:hx:x Set.Icc 0 1h:0 < xx ^ 2 1 endpoint:0 ^ 2 1tail: x Set.Ioc 0 1, x ^ 2 1x:hx:x Set.Icc 0 1h:0 = xx ^ 2 1 endpoint:0 ^ 2 1tail: x Set.Ioc 0 1, x ^ 2 1hx:0 Set.Icc 0 10 ^ 2 1 All goals completed! 🐙 endpoint:0 ^ 2 1tail: x Set.Ioc 0 1, x ^ 2 1x:hx:x Set.Icc 0 1h:0 < xx ^ 2 1 All goals completed! 🐙

The open-interval rewrite is likewise accepted directly:

example : x : , x Set.Ioc (0 : ) 1 x 1 x < 1 := x Set.Ioc 0 1, x 1 x < 1 All goals completed! 🐙 example : x : , x Set.Ioc (0 : ) 1 x = 1 / 2 x 1 := x Set.Ioc 0 1, x = 1 / 2 x 1 All goals completed! 🐙

When rcf sees Set.Icc or Set.Ioo directly, its error message includes the corresponding rewrite above.

21.6.6. The reflected and compiled APIs🔗

Most users only need the rcf tactic. Programs that construct or inspect sentences directly use the reflected language below. A comparison applies to one integer polynomial, formulas combine comparisons, and a sentence adds the one quantifier supported by the decision procedure.

🔗inductive type

The six comparisons supported by reflected polynomial atoms.

Hex.RCF.Cmp.lt : Hex.RCF.Cmp

The relation <.

Hex.RCF.Cmp.le : Hex.RCF.Cmp

The relation .

Hex.RCF.Cmp.eq : Hex.RCF.Cmp

The relation =.

Hex.RCF.Cmp.ge : Hex.RCF.Cmp

The relation .

Hex.RCF.Cmp.gt : Hex.RCF.Cmp

The relation >.

Hex.RCF.Cmp.ne : Hex.RCF.Cmp

The relation .

🔗structure

An atomic comparison between an integer polynomial and zero.

Hex.RCF.Atom.mk
p : Hex.ZPoly

The integer polynomial on the left of the comparison.

cmp : Hex.RCF.Cmp

The comparison with zero.

🔗inductive type

Boolean combinations of univariate polynomial atoms.

Hex.RCF.Formula.atom (a : Hex.RCF.Atom) : Hex.RCF.Formula

A polynomial comparison.

Hex.RCF.Formula.and (φ ψ : Hex.RCF.Formula) :
  Hex.RCF.Formula

Boolean conjunction.

Hex.RCF.Formula.or (φ ψ : Hex.RCF.Formula) : Hex.RCF.Formula

Boolean disjunction.

Hex.RCF.Formula.imp (φ ψ : Hex.RCF.Formula) :
  Hex.RCF.Formula

Boolean implication.

🔗inductive type

A quantifier-free formula under exactly one real or bounded-real quantifier. Bounded quantifiers use the half-open convention (a, b] inherited from real-root isolations.

Hex.RCF.Sentence.forallReal (φ : Hex.RCF.Formula) :
  Hex.RCF.Sentence

Universal quantification over the whole domain.

Hex.RCF.Sentence.existsReal (φ : Hex.RCF.Formula) :
  Hex.RCF.Sentence

Existential quantification over the whole domain.

Hex.RCF.Sentence.forallIoc (a b : Dyadic)
  (φ : Hex.RCF.Formula) : Hex.RCF.Sentence

Universal quantification over the half-open dyadic interval (a, b].

Hex.RCF.Sentence.existsIoc (a b : Dyadic)
  (φ : Hex.RCF.Formula) : Hex.RCF.Sentence

Existential quantification over the half-open dyadic interval (a, b].

🔗def

Interpret a reflected sentence as a Lean proposition.

The #p[...] literal lists coefficients in ascending degree order and normalizes away trailing zeros. This direct construction represents ∀ x : ℝ, x² + 1 > 0 and sends it through the same compiled decision procedure used by the tactic:

open Hex.RCF private def positiveQuadratic : Sentence := .forallReal (.atom { p := #p[1, 0, 1] cmp := .gt }) #guard Hex.RCF.decide positiveQuadratic == some true

Bounded sentences use Lean's core Dyadic endpoints and the half-open interval (a, b]. This sentence represents ∀ x ∈ Set.Ioc (0 : ℝ) 1, x ≥ 0:

open Hex.RCF private def nonnegativeOnUnit : Sentence := .forallIoc (Dyadic.ofInt 0) (Dyadic.ofInt 1) (.atom { p := #p[0, 1] cmp := .ge }) #guard Hex.RCF.decide nonnegativeOnUnit == some true

For clients that need the evidence as well as the verdict, Hex.RCF.build? returns the certificate together with its replay verdict:

🔗def

Assemble a certificate and reject it if replay finds malformed evidence.

🔗structure

A compiled build result retains the certificate that produced its diagnostic or proof-producing verdict.

certificate : Hex.RCF.Certificate

The certificate accepted by three-valued replay.

verdict : Bool

The replay verdict. false remains diagnostic only.

open Hex.RCF example (s : Sentence) (result : BuildResult) (h : build? s = some result) : result.certificate.replay? s = some result.verdict := replay_build h

build? s = none means certificate construction or replay failed. It is distinct from a successful result whose verdict is false.

21.6.7. Certificates and the trust boundary🔗

The four certificate shapes correspond to empty bounded domains, formulas with no nonconstant atom polynomial, root-free carriers, and positive-root cell decompositions:

🔗inductive type

The four disjoint replay branches.

Hex.RCF.Certificate.emptyIoc : Hex.RCF.Certificate

Equal or reversed bounded endpoints.

Hex.RCF.Certificate.constants : Hex.RCF.Certificate

A formula containing no nonconstant atom polynomial.

Hex.RCF.Certificate.noRoots (data : Hex.RCF.DecompCert) :
  Hex.RCF.Certificate

A checked carrier with no real roots.

Hex.RCF.Certificate.cells (data : Hex.RCF.CellsCert) :
  Hex.RCF.Certificate

A checked carrier with at least one real root.

HexRCF is classified as a Mathlib-facing library because its reifier, tactic, real semantics, and soundness theorem use Mathlib. There is no separate HexRCFMathlib library. By contrast, HexRCF.DecisionCheck exposes the complete compiled search, certificate construction, replay, and Hex.RCF.decide path, with a mechanically checked Mathlib-free import closure. The soundness theorem remains on the Mathlib-facing side of this boundary.

Certificate construction runs as compiled elaboration code. That search is not trusted. The tactic embeds its reflected sentence and a literal Hex.RCF.Certificate, reduces the public Boolean checker Hex.RCF.Certificate.check, and applies the soundness theorem Hex.RCF.check_sound. Lean's kernel also checks the reifier's proof that the reflected sentence is equivalent to the source goal.

The public soundness boundary can be used independently of the tactic:

open Hex.RCF example (s : Sentence) (cert : Certificate) (h : Certificate.check s cert = true) : s.toProp := check_sound s cert h example (s : Sentence) (h : Hex.RCF.decide s = some true) : s.toProp := decide_sound s h

Hex.RCF.Certificate.replay? distinguishes malformed evidence (none), a checked false verdict (some false), and a checked true verdict (some true). Hex.RCF.Certificate.check accepts only the last case. The convenience function Hex.RCF.decide returns some true only after this checker accepts the certificate. Advanced clients can use Hex.RCF.build? to retain the certificate and its diagnostic or proof-producing verdict. A successfully checked false sentence produces some false. Neither Hex.RCF.check_sound nor the tactic turns some false into a proof of a negation.

A none from Hex.RCF.decide means the compiled path did not produce a checker-accepted certificate. It does not mean that the sentence is false.

The kernel replays polynomial identities, signs, root counts, endpoint comparisons, and Boolean folds on literal data. It does not repeat root isolation, polynomial gcd computation, or interval refinement. The emitted proof uses ordinary kernel reduction and does not use native_decide:

theorem rcf_square_nonnegative : x : , x ^ 2 0 := (x : ), x ^ 2 0 All goals completed! 🐙 'rcf_square_nonnegative' depends on axioms: [propext, Classical.choice, Quot.sound]#print axioms rcf_square_nonnegative
'rcf_square_nonnegative' depends on axioms: [propext, Classical.choice, Quot.sound]

21.6.8. Cross-references🔗

  • HexPolyZ provides the dense integer polynomial operations used to normalize atoms and check polynomial identities.

  • HexRealRoots provides the exact real-root isolator used by HexRCF during compiled certificate construction. The generalized multiplication-only Sturm certificates in HexRCF are kernel-replay evidence for the resulting root-count facts, not a separate compiled isolation algorithm.