1.3. Algebra

These tactics deal with algebraic calculations.

🔗tactic
abel

Tactic for evaluating expressions in abelian groups.

  • abel! will use a more aggressive reducibility setting to determine equality of atoms.

  • abel1 fails if the target is not an equality.

For example:

example [AddCommMonoid α] (a b : α) : a + (b + a) = a + a + b := α:Type u_1inst✝:AddCommMonoid αa:αb:αa + (b + a) = a + a + b All goals completed! 🐙 example [AddCommGroup α] (a : α) : (3 : ) a = a + (2 : ) a := α:Type u_1inst✝:AddCommGroup αa:α3a = a + 2a All goals completed! 🐙
🔗tactic
group

Tactic for normalizing expressions in multiplicative groups, without assuming commutativity, using only the group axioms without any information about which group is manipulated.

(For additive commutative groups, use the abel tactic instead.)

Example:

example {G : Type} [Group G] (a b c d : G) (h : c = (a*b^2)*((b*b)⁻¹*a⁻¹)*d) : a*c*d⁻¹ = a := G:Typeinst✝:Group Ga:Gb:Gc:Gd:Gh:c = a * b ^ 2 * ((b * b)⁻¹ * a⁻¹) * da * c * d⁻¹ = a G:Typeinst✝:Group Ga:Gb:Gc:Gd:Gh:c = da * c * d⁻¹ = a -- normalizes `h` which becomes `h : c = d` G:Typeinst✝:Group Ga:Gb:Gc:Gd:Gh:c = da * d * d⁻¹ = a -- the goal is now `a*d*d⁻¹ = a` All goals completed! 🐙 -- which then normalized and closed
🔗tactic
ring

Tactic for evaluating expressions in commutative (semi)rings, allowing for variables in the exponent. If the goal is not appropriate for ring (e.g. not an equality) ring_nf will be suggested.

  • ring! will use a more aggressive reducibility setting to determine equality of atoms.

  • ring1 fails if the target is not an equality.

For example:

example (n : ) (m : ) : 2^(n+1) * m = 2 * 2^n * m := n:m:2 ^ (n + 1) * m = 2 * 2 ^ n * m All goals completed! 🐙 example (a b : ) (n : ) : (a + b)^(n + 2) = (a^2 + b^2 + a * b + b * a) * (a + b)^n := a:b:n:(a + b) ^ (n + 2) = (a ^ 2 + b ^ 2 + a * b + b * a) * (a + b) ^ n All goals completed! 🐙 example (x y : ) : x + id y = y + id x := x:y:x + id y = y + id x All goals completed! 🐙 example (x : ) (h : x * 2 > 5): x + x > 5 := x:h:x * 2 > 5x + x > 5 x:h:x * 2 > 5x * 2 > 5; All goals completed! 🐙 -- suggests ring_nf
🔗tactic
noncomm_ring

A tactic for simplifying identities in not-necessarily-commutative rings.

An example:

example {R : Type*} [Ring R] (a b c : R) : a * (b + c + c - b) = 2 * a * c := R:Type u_1inst✝:Ring Ra:Rb:Rc:Ra * (b + c + c - b) = 2 * a * c All goals completed! 🐙

You can use noncomm_ring [h] to also simplify using h.

🔗tactic
module

Given a goal which is an equality in a type M (with M an AddCommMonoid), parse the LHS and RHS of the goal as linear combinations of M-atoms over some commutative semiring R, and prove the goal by checking that the LHS- and RHS-coefficients of each atom are the same up to ring-normalization in R.

(If the proofs of coefficient-wise equality will require more reasoning than just ring-normalization, use the tactic match_scalars instead, and then prove coefficient-wise equality by hand.)

Example uses of the module tactic:

example [AddCommMonoid M] [CommSemiring R] [Module R M] (a b : R) (x : M) : a x + b x = (b + a) x := M:Type u_1R:Type u_2inst✝²:AddCommMonoid Minst✝¹:CommSemiring Rinst✝:Module R Ma:Rb:Rx:Max + bx = (b + a)x All goals completed! 🐙 example [AddCommMonoid M] [Field K] [CharZero K] [Module K M] (x : M) : (2:K)⁻¹ x + (3:K)⁻¹ x + (6:K)⁻¹ x = x := M:Type u_1K:Type u_2inst✝³:AddCommMonoid Minst✝²:Field Kinst✝¹:CharZero Kinst✝:Module K Mx:M2⁻¹x + 3⁻¹x + 6⁻¹x = x All goals completed! 🐙 example [AddCommGroup M] [CommRing R] [Module R M] (a : R) (v w : M) : (1 + a ^ 2) (v + w) - a (a v - w) = v + (1 + a + a ^ 2) w := M:Type u_1R:Type u_2inst✝²:AddCommGroup Minst✝¹:CommRing Rinst✝:Module R Ma:Rv:Mw:M(1 + a ^ 2)(v + w) - a(av - w) = v + (1 + a + a ^ 2)w All goals completed! 🐙 example [AddCommGroup M] [CommRing R] [Module R M] (a b μ ν : R) (x y : M) : (μ - ν) a x = (a μ x + b ν y) - ν (a x + b y) := M:Type u_1R:Type u_2inst✝²:AddCommGroup Minst✝¹:CommRing Rinst✝:Module R Ma:Rb:Rμ:Rν:Rx:My:M(μ - ν)ax = aμx + bνy - ν(ax + by) All goals completed! 🐙
🔗tactic
linear_combination

The linear_combination tactic attempts to prove an (in)equality goal by exhibiting it as a specified linear combination of (in)equality hypotheses, or other (in)equality proof terms, modulo (A) moving terms between the LHS and RHS of the (in)equalities, and (B) a normalization tactic which by default is ring-normalization.

Example usage:

example {a b : ℚ} (h1 : a = 1) (h2 : b = 3) : (a + b) / 2 = 2 := by
  linear_combination (h1 + h2) / 2

example {a b : ℚ} (h1 : a ≤ 1) (h2 : b ≤ 3) : (a + b) / 2 ≤ 2 := by
  linear_combination (h1 + h2) / 2

example {a b : ℚ} : 2 * a * b ≤ a ^ 2 + b ^ 2 := by
  linear_combination sq_nonneg (a - b)

example {x y z w : ℤ} (h₁ : x * z = y ^ 2) (h₂ : y * w = z ^ 2) :
    z * (x * w - y * z) = 0 := by
  linear_combination w * h₁ + y * h₂

example {x : ℚ} (h : x ≥ 5) : x ^ 2 > 2 * x + 11 := by
  linear_combination (x + 3) * h

example {R : Type*} [CommRing R] {a b : R} (h : a = b) : a ^ 2 = b ^ 2 := by
  linear_combination (a + b) * h

example {A : Type*} [AddCommGroup A]
    {x y z : A} (h1 : x + y = 10 • z) (h2 : x - y = 6 • z) :
    2 • x = 2 • (8 • z) := by
  linear_combination (norm := abel) h1 + h2

example (x y : ℤ) (h1 : x * y + 2 * x = 1) (h2 : x = y) :
    x * y = -2 * y + 1 := by
  linear_combination (norm := ring_nf) -2 * h2
  -- leaves goal `⊢ x * y + x * 2 - 1 = 0`

The input e in linear_combination e is a linear combination of proofs of (in)equalities, given as a sum/difference of coefficients multiplied by expressions. The coefficients may be arbitrary expressions (with nonnegativity constraints in the case of inequalities). The expressions can be arbitrary proof terms proving (in)equalities; most commonly they are hypothesis names h1, h2, ....

The left and right sides of all the (in)equalities should have the same type α, and the coefficients should also have type α. For full functionality α should be a commutative ring -- strictly speaking, a commutative semiring with "cancellative" addition (in the semiring case, negation and subtraction will be handled "formally" as if operating in the enveloping ring). If a nonstandard normalization is used (for example abel or skip), the tactic will work over types α with less algebraic structure: for equalities, the minimum is instances of [Add α] [IsRightCancelAdd α] together with instances of whatever operations are used in the tactic call.

The variant linear_combination (norm := tac) e specifies explicitly the "normalization tactic" tac to be run on the subgoal(s) after constructing the linear combination.

  • The default normalization tactic is ring1 (for equalities) or Mathlib.Tactic.Ring.prove{LE,LT} (for inequalities). These are finishing tactics: they close the goal or fail.

  • When working in algebraic categories other than commutative rings -- for example fields, abelian groups, modules -- it is sometimes useful to use normalization tactics adapted to those categories (field_simp, abel, module).

  • To skip normalization entirely, use skip as the normalization tactic.

  • The linear_combination tactic creates a linear combination by adding the provided (in)equalities together from left to right, so if tac is not invariant under commutation of additive expressions, then the order of the input hypotheses can matter.

The variant linear_combination (exp := n) e will take the goal to the nth power before subtracting the combination e. In other words, if the goal is t1 = t2, linear_combination (exp := n) e will change the goal to (t1 - t2)^n = 0 before proceeding as above. This variant is implemented only for linear combinations of equalities (i.e., not for inequalities).

🔗tactic
field_simp

The goal of field_simp is to reduce an expression in a field to an expression of the form n / d where neither n nor d contains any division symbol, just using the simplifier (with a carefully crafted simpset named field_simps) to reduce the number of division symbols whenever possible by iterating the following steps:

  • write an inverse as a division

  • in any product, move the division to the right

  • if there are several divisions in a product, group them together at the end and write them as a single division

  • reduce a sum to a common denominator

If the goal is an equality, this simpset will also clear the denominators, so that the proof can normally be concluded by an application of ring.

field_simp [hx, hy] is a short form for simp (disch := field_simp_discharge) [-one_div, -one_divp, -mul_eq_zero, hx, hy, field_simps]

Note that this naive algorithm will not try to detect common factors in denominators to reduce the complexity of the resulting expression. Instead, it relies on the ability of ring to handle complicated expressions in the next step.

As always with the simplifier, reduction steps will only be applied if the preconditions of the lemmas can be checked. This means that proofs that denominators are nonzero should be included. The fact that a product is nonzero when all factors are, and that a power of a nonzero number is nonzero, are included in the simpset, but more complicated assertions (especially dealing with sums) should be given explicitly. If your expression is not completely reduced by the simplifier invocation, check the denominators of the resulting expression and provide proofs that they are nonzero to enable further progress.

To check that denominators are nonzero, field_simp will look for facts in the context, and will try to apply norm_num to close numerical goals.

The invocation of field_simp removes the lemma one_div from the simpset, as this lemma works against the algorithm explained above. It also removes mul_eq_zero : x * y = 0 x = 0 y = 0, as norm_num can not work on disjunctions to close goals of the form 24 0, and replaces it with mul_ne_zero : x 0 y 0 x * y 0 creating two goals instead of a disjunction.

For example,

example (a b c d x y : ) (hx : x 0) (hy : y 0) : a + b / x + c / x^2 + d / x^3 = a + x⁻¹ * (y * b / y + (d / x + c) / x) := a:b:c:d:x:y:hx:x0hy:y0a + b / x + c / x ^ 2 + d / x ^ 3 = a + x⁻¹ * (y * b / y + (d / x + c) / x) a:b:c:d:x:y:hx:x0hy:y0(((a * x + b) * x ^ 2 + c * x) * x ^ 3 + d * (x * x ^ 2)) * (x * (x * x)) = (a * (x * (x * x)) + (b * (x * x) + (d + c * x))) * (x * x ^ 2 * x ^ 3) All goals completed! 🐙

Moreover, the field_simp tactic can also take care of inverses of units in a general (commutative) monoid/ring and partial division /ₚ, see Algebra.Group.Units for the definition. Analogue to the case above, the lemma one_divp is removed from the simpset as this works against the algorithm. If you have objects with an IsUnit x instance like (x : R) (hx : IsUnit x), you should lift them with lift x to Rˣ using id hx; rw [IsUnit.unit_of_val_units] clear hx before using field_simp.

See also the cancel_denoms tactic, which tries to do a similar simplification for expressions that have numerals in denominators. The tactics are not related: cancel_denoms will only handle numeric denominators, and will try to entirely remove (numeric) division from the expression by multiplying by a factor.

🔗tactic
cancel_denoms

cancel_denoms attempts to remove numerals from the denominators of fractions. It works on propositions that are field-valued inequalities.

variable [LinearOrderedField α] (a b c : α) example (h : a / 5 + b / 4 < c) : 4*a + 5*b < 20*c := α:Type u_1inst✝:LinearOrderedField αa:αb:αc:αh:a / 5 + b / 4 < c4 * a + 5 * b < 20 * c α:Type u_1inst✝:LinearOrderedField αa:αb:αc:αh:4 * a + 5 * b < 20 * c4 * a + 5 * b < 20 * c All goals completed! 🐙 example (h : a > 0) : a / 5 > 0 := α:Type u_1inst✝:LinearOrderedField αa:αb:αc:αh:a > 0a / 5 > 0 α:Type u_1inst✝:LinearOrderedField αa:αb:αc:αh:a > 00 < a All goals completed! 🐙
🔗tactic
algebraize

Tactic that, given RingHoms, adds the corresponding Algebra and (if possible) IsScalarTower instances, as well as Algebra corresponding to RingHom properties available as hypotheses.

Example: given f : A →+* B and g : B →+* C, and hf : f.FiniteType, algebraize [f, g] will add the instances Algebra A B, Algebra B C, and Algebra.FiniteType A B.

See the algebraize tag for instructions on what properties can be added.

The tactic also comes with a configuration option properties. If set to true (default), the tactic searches through the local context for RingHom properties that can be converted to Algebra properties. The macro algebraize_only calls algebraize (config := {properties := false}), so in other words it only adds Algebra and IsScalarTower instances.

🔗tactic
reduce_mod_char

The tactic reduce_mod_char looks for numeric expressions in characteristic p and reduces these to lie between 0 and p.

For example:

example : (5 : ZMod 4) = 1 := by reduce_mod_char
example : (X ^ 2 - 3 * X + 4 : (ZMod 4)[X]) = X ^ 2 + X := by reduce_mod_char

It also handles negation, turning it into multiplication by p - 1, and similarly subtraction.

This tactic uses the type of the subexpression to figure out if it is indeed of positive characteristic, for improved performance compared to trying to synthesise a CharP instance. The variant reduce_mod_char! also tries to use CharP R n hypotheses in the context. (Limitations of the typeclass system mean the tactic can't search for a CharP R n instance if n is not yet known; use have : CharP R n := inferInstance; reduce_mod_char! as a workaround.)

🔗tactic
compute_degree

compute_degree is a tactic to solve goals of the form

  • natDegree f = d,

  • degree f = d,

  • natDegree f d,

  • degree f d,

  • coeff f d = r, if d is the degree of f.

The tactic may leave goals of the form d' = d, d' d, or r 0, where d' in or WithBot is the tactic's guess of the degree, and r is the coefficient's guess of the leading coefficient of f.

compute_degree applies norm_num to the left-hand side of all side goals, trying to close them.

The variant compute_degree! first applies compute_degree. Then it uses norm_num on all the remaining goals and tries assumption.

🔗tactic
monicity

monicity tries to solve a goal of the form Monic f. It converts the goal into a goal of the form natDegree f n and one of the form f.coeff n = 1 and calls compute_degree on those two goals.

The variant monicity! starts like monicity, but calls compute_degree! on the two side-goals.