ring
tactic #
A tactic for solving equations in commutative (semi)rings, where the exponents can also contain variables. Based on http://www.cs.ru.nl/~freek/courses/tt-2014/read/10.1.1.61.3041.pdf .
More precisely, expressions of the following form are supported:
- constants (non-negative integers)
- variables
- coefficients (any rational number, embedded into the (semi)ring)
- addition of expressions
- multiplication of expressions (
a * b
) - scalar multiplication of expressions (
n • a
; the multiplier must have typeℕ
) - exponentiation of expressions (the exponent must have type
ℕ
) - subtraction and negation of expressions (if the base is a full ring)
The extension to exponents means that something like 2 * 2^n * b = b * 2^(n+1)
can be proved,
even though it is not strictly speaking an equation in the language of commutative rings.
Implementation notes #
The basic approach to prove equalities is to normalise both sides and check for equality.
The normalisation is guided by building a value in the type ExSum
at the meta level,
together with a proof (at the base level) that the original value is equal to
the normalised version.
The outline of the file:
- Define a mutual inductive family of types
ExSum
,ExProd
,ExBase
, which can represent expressions with+
,*
,^
and rational numerals. The mutual induction ensures that associativity and distributivity are applied, by restricting which kinds of subexpressions appear as arguments to the various operators. - Represent addition, multiplication and exponentiation in the
ExSum
type, thus allowing us to map expressions toExSum
(theeval
function drives this). We apply associativity and distributivity of the operators here (helped byEx*
types) and commutativity as well (by sorting the subterms; unfortunately not helped by anything). Any expression not of the above formats is treated as an atom (the same as a variable).
There are some details we glossed over which make the plan more complicated:
- The order on atoms is not initially obvious. We construct a list containing them in order of initial appearance in the expression, then use the index into the list as a key to order on.
- For
pow
, the exponent must be a natural number, while the base can be any semiringα
. We swap out operations for the base ringα
with those for the exponent ringℕ
as soon as we deal with exponents.
Caveats and future work #
The normalized form of an expression is the one that is useful for the tactic,
but not as nice to read. To remedy this, the user-facing normalization calls ringNFCore
.
Subtraction cancels out identical terms, but division does not.
That is: a - a = 0 := by ring
solves the goal,
but a / a := 1 by ring
doesn't.
Note that 0 / 0
is generally defined to be 0
,
so division cancelling out is not true in general.
Multiplication of powers can be simplified a little bit further:
2 ^ n * 2 ^ n = 4 ^ n := by ring
could be implemented
in a similar way that 2 * a + 2 * a = 4 * a := by ring
already works.
This feature wasn't needed yet, so it's not implemented yet.
Tags #
ring, semiring, exponent, power
A shortcut instance for CommSemiring ℕ
used by ring.
Equations
- Mathlib.Tactic.Ring.instCommSemiringNat = inferInstance
Instances For
A typed expression of type CommSemiring ℕ
used when we are working on
ring subexpressions of type ℕ
.
Instances For
The base e
of a normalized exponent expression.
- atom: {u : Lean.Level} → {α : Q(Type u)} → {sα : Q(CommSemiring «$α»)} → {e : Q(«$α»)} → ℕ → Mathlib.Tactic.Ring.ExBase sα e
An atomic expression
e
with idid
.Atomic expressions are those which
ring
cannot parse any further. For instance,a + (a % b)
hasa
and(a % b)
as atoms. Thering1
tactic does not normalize the subexpressions in atoms, butring_nf
does.Atoms in fact represent equivalence classes of expressions, modulo definitional equality. The field
index : ℕ
should be a unique number for each class, whilevalue : expr
contains a representative of this class. The functionresolve_atom
determines the appropriate atom for a given expression. - sum: {u : Lean.Level} →
{α : Q(Type u)} →
{sα : Q(CommSemiring «$α»)} → {e : Q(«$α»)} → Mathlib.Tactic.Ring.ExSum sα e → Mathlib.Tactic.Ring.ExBase sα e
A sum of monomials.
Instances For
A monomial, which is a product of powers of ExBase
expressions,
terminated by a (nonzero) constant coefficient.
- const: {u : Lean.Level} →
{α : Q(Type u)} →
{sα : Q(CommSemiring «$α»)} → {e : Q(«$α»)} → ℚ → optParam (Option Lean.Expr) none → Mathlib.Tactic.Ring.ExProd sα e
A coefficient
value
, which must not be0
.e
is a raw rat cast. Ifvalue
is not an integer, thenhyp
should be a proof of(value.den : α) ≠ 0
. - mul: {u : Lean.Level} → {α : Q(Type u)} → {sα : Q(CommSemiring «$α»)} → {x : Q(«$α»)} → {e : Q(ℕ)} → {b : Q(«$α»)} → Mathlib.Tactic.Ring.ExBase sα x → Mathlib.Tactic.Ring.ExProd Mathlib.Tactic.Ring.sℕ e → Mathlib.Tactic.Ring.ExProd sα b → Mathlib.Tactic.Ring.ExProd sα q(«$x» ^ «$e» * «$b»)
Instances For
A polynomial expression, which is a sum of monomials.
- zero: {u : Lean.Level} → {α : Q(Type u)} → {sα : Q(CommSemiring «$α»)} → Mathlib.Tactic.Ring.ExSum sα q(0)
Zero is a polynomial.
e
is the expression0
. - add: {u : Lean.Level} →
{α : Q(Type u)} →
{sα : Q(CommSemiring «$α»)} →
{a b : Q(«$α»)} →
Mathlib.Tactic.Ring.ExProd sα a → Mathlib.Tactic.Ring.ExSum sα b → Mathlib.Tactic.Ring.ExSum sα q(«$a» + «$b»)
A sum
a + b
is a polynomial ifa
is a monomial andb
is another polynomial.
Instances For
Equality test for expressions. This is not a BEq
instance because it is heterogeneous.
Equality test for expressions. This is not a BEq
instance because it is heterogeneous.
Equality test for expressions. This is not a BEq
instance because it is heterogeneous.
A total order on normalized expressions.
This is not an Ord
instance because it is heterogeneous.
A total order on normalized expressions.
This is not an Ord
instance because it is heterogeneous.
A total order on normalized expressions.
This is not an Ord
instance because it is heterogeneous.
Equations
- Mathlib.Tactic.Ring.instInhabitedSigmaQuotedExBase = { default := ⟨default, Mathlib.Tactic.Ring.ExBase.atom 0⟩ }
Equations
- Mathlib.Tactic.Ring.instInhabitedSigmaQuotedExSum = { default := ⟨q(0), Mathlib.Tactic.Ring.ExSum.zero⟩ }
Equations
- Mathlib.Tactic.Ring.instInhabitedSigmaQuotedExProd = { default := ⟨default, Mathlib.Tactic.Ring.ExProd.const 0⟩ }
Converts ExBase sα
to ExBase sβ
, assuming sα
and sβ
are defeq.
Converts ExProd sα
to ExProd sβ
, assuming sα
and sβ
are defeq.
Converts ExSum sα
to ExSum sβ
, assuming sα
and sβ
are defeq.
The result of evaluating an (unnormalized) expression e
into the type family E
(one of ExSum
, ExProd
, ExBase
) is a (normalized) element e'
and a representation E e'
for it, and a proof of e = e'
.
- expr : Q(«$α»)
The normalized result.
- val : E self.expr
The data associated to the normalization.
- proof : Q(«$e» = unknown_1)
A proof that the original expression is equal to the normalized result.
Instances For
Equations
- Mathlib.Tactic.Ring.instInhabitedResultOfSigmaQuoted = match default with | ⟨e', v⟩ => { default := { expr := e', val := v, proof := default } }
Constructs the expression corresponding to .const n
.
(The .const
constructor does not check that the expression is correct.)
Instances For
Constructs the expression corresponding to .const (-n)
.
(The .const
constructor does not check that the expression is correct.)
Instances For
Constructs the expression corresponding to .const (-n)
.
(The .const
constructor does not check that the expression is correct.)
Equations
- Mathlib.Tactic.Ring.ExProd.mkRat sα x q n d h = ⟨q(Rat.rawCast «$n» «$d»), Mathlib.Tactic.Ring.ExProd.const q (some h)⟩
Instances For
Embed an exponent (an ExBase, ExProd
pair) as an ExProd
by multiplying by 1.
Equations
- va.toProd vb = Mathlib.Tactic.Ring.ExProd.mul va vb (Mathlib.Tactic.Ring.ExProd.const 1)
Instances For
Embed ExProd
in ExSum
by adding 0.
Equations
- v.toSum = Mathlib.Tactic.Ring.ExSum.add v Mathlib.Tactic.Ring.ExSum.zero
Instances For
Get the leading coefficient of an ExProd
.
Equations
- (Mathlib.Tactic.Ring.ExProd.const i h).coeff = i
- (Mathlib.Tactic.Ring.ExProd.mul a₁ a₂ a₃).coeff = a₃.coeff
Instances For
Two monomials are said to "overlap" if they differ by a constant factor, in which case the constants just add. When this happens, the constant may be either zero (if the monomials cancel) or nonzero (if they add up); the zero case is handled specially.
- zero: {u : Lean.Level} →
{α : Q(Type u)} →
{sα : Q(CommSemiring «$α»)} →
{e : Q(«$α»)} → Q(Mathlib.Meta.NormNum.IsNat «$e» 0) → Mathlib.Tactic.Ring.Overlap sα e
The expression
e
(the sum of monomials) is equal to0
. - nonzero: {u : Lean.Level} →
{α : Q(Type u)} →
{sα : Q(CommSemiring «$α»)} →
{e : Q(«$α»)} → Mathlib.Tactic.Ring.Result (Mathlib.Tactic.Ring.ExProd sα) e → Mathlib.Tactic.Ring.Overlap sα e
The expression
e
(the sum of monomials) is equal to another monomial (with nonzero leading coefficient).
Instances For
Given monomials va, vb
, attempts to add them together to get another monomial.
If the monomials are not compatible, returns none
.
For example, xy + 2xy = 3xy
is a .nonzero
overlap, while xy + xz
returns none
and xy + -xy = 0
is a .zero
overlap.
Instances For
Adds two polynomials va, vb
together to get a normalized result polynomial.
0 + b = b
a + 0 = a
a * x + a * y = a * (x + y)
(forx
,y
coefficients; usesevalAddOverlap
)(a₁ + a₂) + (b₁ + b₂) = a₁ + (a₂ + (b₁ + b₂))
(ifa₁.lt b₁
)(a₁ + a₂) + (b₁ + b₂) = b₁ + ((a₁ + a₂) + b₂)
(if nota₁.lt b₁
)
Multiplies two monomials va, vb
together to get a normalized result monomial.
x * y = (x * y)
(forx
,y
coefficients)x * (b₁ * b₂) = b₁ * (b₂ * x)
(forx
coefficient)(a₁ * a₂) * y = a₁ * (a₂ * y)
(fory
coefficient)(x ^ ea * a₂) * (x ^ eb * b₂) = x ^ (ea + eb) * (a₂ * b₂)
(ifea
andeb
are identical except coefficient)(a₁ * a₂) * (b₁ * b₂) = a₁ * (a₂ * (b₁ * b₂))
(ifa₁.lt b₁
)(a₁ * a₂) * (b₁ * b₂) = b₁ * ((a₁ * a₂) * b₂)
(if nota₁.lt b₁
)
Multiplies a monomial va
to a polynomial vb
to get a normalized result polynomial.
a * 0 = 0
a * (b₁ + b₂) = (a * b₁) + (a * b₂)
Equations
- One or more equations did not get rendered due to their size.
- Mathlib.Tactic.Ring.evalMul₁ sα va Mathlib.Tactic.Ring.ExSum.zero = pure { expr := q(0), val := Mathlib.Tactic.Ring.ExSum.zero, proof := q(⋯) }
Instances For
Multiplies two polynomials va, vb
together to get a normalized result polynomial.
0 * b = 0
(a₁ + a₂) * b = (a₁ * b) + (a₂ * b)
Equations
- One or more equations did not get rendered due to their size.
- Mathlib.Tactic.Ring.evalMul sα Mathlib.Tactic.Ring.ExSum.zero vb = pure { expr := q(0), val := Mathlib.Tactic.Ring.ExSum.zero, proof := q(⋯) }
Instances For
Applies Nat.cast
to a nat polynomial to produce a polynomial in α
.
- An atom
e
causes↑e
to be allocated as a new atom. - A sum delegates to
ExSum.evalNatCast
.
Applies Nat.cast
to a nat monomial to produce a monomial in α
.
↑c = c
ifc
is a numeric literal↑(a ^ n * b) = ↑a ^ n * ↑b
Applies Nat.cast
to a nat polynomial to produce a polynomial in α
.
↑0 = 0
↑(a + b) = ↑a + ↑b
Constructs the scalar multiplication n • a
, where both n : ℕ
and a : α
are normalized
polynomial expressions.
a • b = a * b
ifα = ℕ
a • b = ↑a * b
otherwise
Equations
- One or more equations did not get rendered due to their size.
Instances For
Negates a monomial va
to get another monomial.
-c = (-c)
(forc
coefficient)-(a₁ * a₂) = a₁ * -a₂
Instances For
Negates a polynomial va
to get another polynomial.
-0 = 0
(forc
coefficient)-(a₁ + a₂) = -a₁ + -a₂
Equations
- One or more equations did not get rendered due to their size.
- Mathlib.Tactic.Ring.evalNeg sα rα Mathlib.Tactic.Ring.ExSum.zero = pure { expr := q(0), val := Mathlib.Tactic.Ring.ExSum.zero, proof := q(⋯) }
Instances For
Subtracts two polynomials va, vb
to get a normalized result polynomial.
a - b = a + -b
Equations
- One or more equations did not get rendered due to their size.
Instances For
The fallback case for exponentiating polynomials is to use ExBase.toProd
to just build an
exponent expression. (This has a slightly different normalization than evalPowAtom
because
the input types are different.)
x ^ e = (x + 0) ^ e * 1
Equations
- Mathlib.Tactic.Ring.evalPowProdAtom sα va vb = { expr := q((«$a» + 0) ^ «$b» * Nat.rawCast 1), val := (Mathlib.Tactic.Ring.ExBase.sum va.toSum).toProd vb, proof := q(⋯) }
Instances For
The fallback case for exponentiating polynomials is to use ExBase.toProd
to just build an
exponent expression.
x ^ e = x ^ e * 1 + 0
Equations
- Mathlib.Tactic.Ring.evalPowAtom sα va vb = { expr := q(«$a» ^ «$b» * Nat.rawCast 1 + 0), val := (va.toProd vb).toSum, proof := q(⋯) }
Instances For
Attempts to prove that a polynomial expression in ℕ
is positive.
- Atoms are not (necessarily) positive
- Sums defer to
ExSum.evalPos
Attempts to prove that a monomial expression in ℕ
is positive.
0 < c
(wherec
is a numeral) is true by the normalization invariant (c
is not zero)0 < x ^ e * b
if0 < x
and0 < b
Attempts to prove that a polynomial expression in ℕ
is positive.
0 < 0
fails0 < a + b
if0 < a
or0 < b
The main case of exponentiation of ring expressions is when va
is a polynomial and n
is a
nonzero literal expression, like (x + y)^5
. In this case we work out the polynomial completely
into a sum of monomials.
x ^ 1 = x
x ^ (2*n) = x ^ n * x ^ n
x ^ (2*n+1) = x ^ n * x ^ n * x
There are several special cases when exponentiating monomials:
1 ^ n = 1
x ^ y = (x ^ y)
whenx
andy
are constants(a * b) ^ e = a ^ e * b ^ e
In all other cases we use evalPowProdAtom
.
Instances For
The result of extractCoeff
is a numeral and a proof that the original expression
factors by this numeral.
- k : Q(ℕ)
A raw natural number literal.
- e' : Q(ℕ)
The result of extracting the coefficient is a monic monomial.
- ve' : Mathlib.Tactic.Ring.ExProd Mathlib.Tactic.Ring.sℕ self.e'
e'
is a monomial.
Instances For
Given a monomial expression va
, splits off the leading coefficient k
and the remainder
e'
, stored in the ExtractCoeff
structure.
Instances For
Exponentiates a polynomial va
by a monomial vb
, including several special cases.
a ^ 1 = a
0 ^ e = 0
if0 < e
(a + 0) ^ b = a ^ b
computed usingevalPowProd
a ^ b = (a ^ b') ^ k
ifb = b' * k
andk > 1
Otherwise a ^ b
is just encoded as a ^ b * 1 + 0
using evalPowAtom
.
Exponentiates two polynomials va, vb
.
a ^ 0 = 1
a ^ (b₁ + b₂) = a ^ b₁ * a ^ b₂
Equations
- One or more equations did not get rendered due to their size.
- Mathlib.Tactic.Ring.evalPow sα va Mathlib.Tactic.Ring.ExSum.zero = pure { expr := q(Nat.rawCast 1 + 0), val := (Mathlib.Tactic.Ring.ExProd.mkNat sα 1).snd.toSum, proof := q(⋯) }
Instances For
This cache contains data required by the ring
tactic during execution.
A ring instance on
α
, if available.- dα : Option Q(DivisionRing «$α»)
A division ring instance on
α
, if available. A characteristic zero ring instance on
α
, if available.
Instances For
Create a new cache for α
by doing the necessary instance searches.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Converts a proof by norm_num
that e
is a numeral, into a normalization as a monomial:
e = 0
ifnorm_num
returnsIsNat e 0
e = Nat.rawCast n + 0
ifnorm_num
returnsIsNat e n
e = Int.rawCast n + 0
ifnorm_num
returnsIsInt e n
e = Rat.rawCast n d + 0
ifnorm_num
returnsIsRat e n d
Instances For
Evaluates an atom, an expression where ring
can find no additional structure.
a = a ^ 1 * 1 + 0
Equations
- One or more equations did not get rendered due to their size.
Instances For
Applies ⁻¹
to a polynomial to get an atom.
Equations
- Mathlib.Tactic.Ring.evalInvAtom sα dα a = do let i ← Mathlib.Tactic.AtomM.addAtom q(«$a»⁻¹) pure { expr := q(«$a»⁻¹), val := Mathlib.Tactic.Ring.ExBase.atom i, proof := q(⋯) }
Instances For
Inverts a polynomial va
to get a normalized result polynomial.
c⁻¹ = (c⁻¹)
ifc
is a constant(a ^ b * c)⁻¹ = a⁻¹ ^ b * c⁻¹
Equations
- One or more equations did not get rendered due to their size.
Instances For
Inverts a polynomial va
to get a normalized result polynomial.
0⁻¹ = 0
a⁻¹ = (a⁻¹)
ifa
is a nontrivial sum
Instances For
Divides two polynomials va, vb
to get a normalized result polynomial.
a / b = a * b⁻¹
Equations
- One or more equations did not get rendered due to their size.
Instances For
A precomputed Cache
for ℕ
.
Equations
- Mathlib.Tactic.Ring.Cache.nat = { rα := none, dα := none, czα := some q(⋯) }
Instances For
Checks whether e
would be processed by eval
as a ring expression,
or otherwise if it is an atom or something simplifiable via norm_num
.
We use this in ring_nf
to avoid rewriting atoms unnecessarily.
Returns:
none
ifeval
would processe
as an algebraic ring expressionsome none
ifeval
would treate
as an atom.some (some r)
ifeval
would not processe
as an algebraic ring expression, butNormNum.derive
can nevertheless simplifye
, with resultr
.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Evaluates expression e
of type α
into a normalized representation as a polynomial.
This is the main driver of ring
, which calls out to evalAdd
, evalMul
etc.
CSLift α β
is a typeclass used by ring
for lifting operations from α
(which is not a commutative semiring) into a commutative semiring β
by using an injective map
lift : α → β
.
- lift : α → β
lift
is the "canonical injection" fromα
toβ
- inj : Function.Injective Mathlib.Tactic.Ring.CSLift.lift
lift
is an injective function
Instances
CSLiftVal a b
means that b = lift a
. This is used by ring
to construct an expression b
from the input expression a
, and then run the usual ring algorithm on b
.
- eq : b = Mathlib.Tactic.Ring.CSLift.lift a
The output value
b
is equal to the lift ofa
. This can be supplied by the default instance which setsb := lift a
, butring
will treat this as an atom so it is more useful when there are other instances which distribute addition or multiplication.
Instances
Equations
- ⋯ = ⋯
This is a routine which is used to clean up the unsolved subgoal
of a failed ring1
application. It is overridden in Mathlib.Tactic.Ring.RingNF
to apply the ring_nf
simp set to the goal.
Frontend of ring1
: attempt to close a goal g
, assuming it is an equation of semirings.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The core of proveEq
takes expressions e₁ e₂ : α
where α
is a CommSemiring
,
and returns a proof that they are equal (or fails).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Tactic for solving equations of commutative (semi)rings, allowing variables in the exponent.
- This version of
ring
fails if the target is not an equality. - The variant
ring1!
will use a more aggressive reducibility setting to determine equality of atoms.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Tactic for solving equations of commutative (semi)rings, allowing variables in the exponent.
- This version of
ring
fails if the target is not an equality. - The variant
ring1!
will use a more aggressive reducibility setting to determine equality of atoms.
Equations
- Mathlib.Tactic.Ring.tacticRing1! = Lean.ParserDescr.node `Mathlib.Tactic.Ring.tacticRing1! 1024 (Lean.ParserDescr.nonReservedSymbol "ring1!" false)