Zulip Chat Archive

Stream: lean4

Topic: tactic which is just a combination of other tactics


Kevin Buzzard (Oct 19 2023 at 18:11):

I want to prove lots of things (all the one-variable axioms for a commutative ring defined as a quotient) using this sequence of tactics:

  intro x
  refine Quot.induction_on x ?_
  rintro a, b
  apply Quot.sound
  simp
  try ring

How do I write one tactic which does this? I knew how to do this in Lean 3 and it's useful for teaching gimmicks.

Alex J. Best (Oct 19 2023 at 18:15):

Here is one way

import Mathlib.Tactic

macro "foo" : tactic => `(tactic|
  · intro a
    exact a)

example (h : 1= 1): 1= 1:= by
  revert h
  foo

Alex J. Best (Oct 19 2023 at 18:16):

Actually this isn't good, I'll try and make a better version

Kyle Miller (Oct 19 2023 at 18:17):

macro "buzzardify" : tactic =>
  `(tactic|
    intro x
    refine Quot.induction_on x ?_
    rintro a, b
    apply Quot.sound
    simp
    try ring)

Alex J. Best (Oct 19 2023 at 18:28):

I don't think that does what you think it does kyle, or at least I have no idea what that would do,
Look at the state after

macro "buzzardify" : tactic =>
  `(tactic|
    intro x
    ring)

example (h a s : 1= 1): 1= 1:= by
  revert h a s
  buzzardify

Alex J. Best (Oct 19 2023 at 18:33):

I think

macro "buzzardify" : tactic =>
  `(tactic|
  focus
    intro x
    refine Quot.induction_on x ?_
    rintro a, b
    apply Quot.sound
    simp
    try ring)

works

Kyle Miller (Oct 19 2023 at 18:35):

Oh, oops, should have tested more carefully (i.e., at all). You can use parentheses to group tactics:

macro "buzzardify" : tactic => `(tactic|(
  intro x
  refine Quot.induction_on x ?_
  rintro a, b
  apply Quot.sound
  simp
  try ring))

Kevin Buzzard (Oct 19 2023 at 18:46):

Works in my use case! Thanks both of you! This is something I've been teaching this week.

import Mathlib.Tactic

-- A "pre-integer" is just a pair of naturals.
abbrev MyPreint :=  × 

namespace MyPreint

/-- The equivalence relation on pre-integers, which we'll quotient out
by to get integers. -/
def R (x y : MyPreint) : Prop := x.1 + y.2 = x.2 + y.1

-- Lemma saying what definition of `R` is on ordered pairs
lemma R_def (a b c d : ) : R (a,b) (c,d)  a + d = b + c := by
  rfl

-- `linarith` tactic can do all the calculations to prove it's an equiv reln
lemma R_refl : Reflexive R := by
  rintro a, b
  simp only [R_def] at *
  linarith

lemma R_symm : Symmetric R := by
  rintro a, b c, d h
  simp only [R_def] at *
  linarith

lemma R_trans : Transitive R := by
  rintro a, b c, d e, f h1 h2
  simp only [R_def] at *
  linarith

/-- Enable `≈` notation for `R` and ability to quotient by it  -/
instance R_equiv : Setoid MyPreint where
  r := R
  iseqv := @R_refl, @R_symm, @R_trans

-- Teach the definition of `≈` to the simplifier
@[simp] lemma equiv_def (a b c d : ) : (a, b)  (c, d)  a + d = b + c := R_def a b c d

-- Teach the definition of `Setoid.r` to the simplifier
@[simp] lemma equiv_def' (a b c d : ) : Setoid.r (a, b) (c, d)  a + d = b + c := equiv_def a b c d

/-- Negation on pre-integers. -/
def neg (ab : MyPreint) : MyPreint := (ab.2, ab.1)

-- teach it to the simplifier
@[simp] lemma neg_def (a b : ) : neg (a, b) = (b, a) := rfl

/-- Addition on pre-integers. -/
def add (ab cd : MyPreint) : MyPreint := (ab.1 + cd.1, ab.2 + cd.2)

-- teach it to the simplifier
@[simp] lemma add_def (a b c d : ) : add (a, b) (c, d) = (a + c, b + d) := rfl

/-- Multiplication on pre-integers. -/
def mul (ab cd : MyPreint) : MyPreint := (ab.1 * cd.1 + ab.2 * cd.2, ab.1 * cd.2 + ab.2 * cd.1)

-- teach it to the simplifier
@[simp] lemma mul_def (a b c d : ) : mul (a, b) (c, d) = (a * c + b * d, a * d + b * c) := rfl

end MyPreint

open MyPreint

/-- Make the integers as a quotient of preintegers. -/
def MyInt := Quotient R_equiv

namespace MyInt

-- `0` notation (the equiv class of (0,0))
instance : Zero MyInt where zero := (0, 0)

-- `1` notation (the equiv class of (1,0))
instance : One MyInt where one := (1, 0)

/-- Negation on integers. -/
def neg : MyInt  MyInt := Quotient.map MyPreint.neg <| by
  -- to prove this is well-defined, we need to
  -- show some lemma or other
  rintro a, b c, d (h : a + d = b + c)
  simp [MyPreint.neg]
  -- So prove this lemma
  linarith

-- unary `-` notation
instance : Neg MyInt where neg := neg

/-- Addition on integers. -/
def add : MyInt  MyInt  MyInt  := Quotient.map₂ MyPreint.add <| by
  -- to show this is well-defined, we need to
  -- show some lemma or other
  rintro a, b c, d (h1 : a + d = b + c)
         e, f g, h (h2 : e + h = f + g)
  simp [MyPreint.add]
  -- So prove this lemma
  linarith

-- `+` notation
instance : Add MyInt where add := add

/-- Multiplication on integers. -/
def mul : MyInt  MyInt  MyInt  := Quotient.map₂ MyPreint.mul <| by
  -- to show this is well-defined, we need to show some lemma or other
  rintro a, b c, d (h1 : a + d = b + c)
         e, f g, h (h2 : e + h = f + g)
  simp [MyPreint.mul]
  -- so prove this lemma (which in this case is nonlinear)
  nlinarith

-- `*` notation
instance : Mul MyInt where mul := mul

open MyPreint

-- Every single proof of every single axiom is: "replace all integers with
-- pairs of naturals, turn the question into a question about naturals,
-- and then get the `ring` tactic to prove it if it's not already proved by accident"

macro "int_proof₁" : tactic =>
  `(tactic|
  focus
    intro x
    refine Quot.induction_on x ?_
    rintro a, b
    apply Quot.sound
    simp
    try ring)

macro "int_proof₂" : tactic =>
  `(tactic|
  focus
    intro x y
    refine Quot.induction_on₂ x y ?_
    rintro a, b c, d
    apply Quot.sound
    simp
    try ring)

macro "int_proof₃" : tactic =>
  `(tactic|
  focus
    intro x y z
    refine Quot.induction_on₃ x y z ?_
    rintro a, b c, d e, f
    apply Quot.sound
    simp
    try ring)

instance : CommRing MyInt where
  add := (. + .)
  add_assoc := by int_proof₃
  zero := 0
  zero_add := by int_proof₁
  add_zero := by int_proof₁
  add_comm := by int_proof₂
  mul := (. * .)
  left_distrib := by int_proof₃
  right_distrib := by int_proof₃
  zero_mul := by int_proof₁
  mul_zero := by int_proof₁
  mul_assoc := by int_proof₃
  one := 1
  one_mul := by int_proof₁
  mul_one := by int_proof₁
  neg := (- .)
  add_left_neg := by int_proof₁
  mul_comm := by int_proof₂

What's the next trick? Figure out just one int_proof tactic that counts the number of inputs?

Damiano Testa (Oct 19 2023 at 19:54):

If you do not expect to need more than the three arguments, you can simply combine them by trying them each, until one succeeds...

Kevin Buzzard (Oct 19 2023 at 20:30):

So then the only question left is whether lean 4 has a version of the refine_struct tactic meaning that I will be able to solve them all in one line

Adam Topaz (Oct 19 2023 at 20:48):

you can always use constructor <;> ... or something...

Adam Topaz (Oct 19 2023 at 20:48):

oh no I see you want to just define the data, I get it.

Adam Topaz (Oct 19 2023 at 20:49):

do we have refine_struct? did anyone actually use that in lean3?

Kevin Buzzard (Oct 19 2023 at 20:49):

And also ignore all the fields with default instances!

Kevin Buzzard (Oct 19 2023 at 20:49):

I used it in eg the complex number game to prove that the complexes were a ring using a very similar strategy to the above

Ruben Van de Velde (Oct 19 2023 at 21:08):

About a hundred uses in mathlib3

Alex J. Best (Oct 19 2023 at 22:45):

Kevin Buzzard said:

And also ignore all the fields with default instances!

Is https://github.com/leanprover-community/mathlib4/pull/5745/files#diff-e5d82c5386e25ce16218699349a5ac7456350e114dfbfceac6937ccc1d903239R27 what you want?

Kevin Buzzard (Oct 19 2023 at 22:48):

I don't know. Will that fill in an nsmul field which isn't populated by a tactic by default?

Alex J. Best (Oct 19 2023 at 22:51):

What do you mean? #mwe :grinning:?

Alex J. Best (Oct 19 2023 at 22:52):

You can see a test file there which shows what it does

Siddhartha Gadgil (Oct 21 2023 at 04:11):

Some baby examples of combining tactics are at: https://github.com/siddhartha-gadgil/proofs-and-programs-2023/blob/2d3312143811f0679175c2614e454ad6d163608f/PnP2023/Lec_01_11/NatLe.lean#L188


Last updated: Dec 20 2023 at 11:08 UTC