Zulip Chat Archive

Stream: lean4

Topic: Misunderstanding of simp


Brandon Brown (May 11 2021 at 03:24):

So if I don't use an inductive type where it automatically creates theorems that make things inconsistent and just define a type like this it would work (be sound)? (found a post from Mario suggesting this). And then I'd somehow introduce my own recursor or something

variable (int : Type)
variable (zero : int)
variable (succ : int  int)
variable (pred : int  int)
variable (eq1 : {a : int}  succ (pred a) = a)

Daniel Selsam (May 11 2021 at 03:30):

Yes, the actual integers with standard zero/succ/pred form a model.

Mario Carneiro (May 11 2021 at 15:08):

@Brandon Brown Lean does not directly support constructing higher inductive types, but in many cases, including this one, you can build higher inductive types by quotienting a regular inductive type by the constraints on the constructors. So in this case we would write this:

inductive int_base : Type
| zero : int_base
| succ : int_base  int_base
| pred : int_base  int_base

inductive int_base.rel : int_base  int_base  Prop
| succ_congr : rel a b  rel (succ a) (succ b)
| pred_congr : rel a b  rel (pred a) (pred b)
| succ_pred : rel (succ (pred a)) a
| pred_succ : rel (pred (succ a)) a

def int := Quot int_base.rel
def int.succ (i : int) : int := i.lift (fun a => Quot.mk _ a.succ)
  fun a b h => Quot.sound (int_base.rel.succ_congr h)
def int.pred (i : int) : int := i.lift (fun a => Quot.mk _ a.pred)
  fun a b h => Quot.sound (int_base.rel.pred_congr h)
theorem int.succ_pred {a : int} : succ (pred a) = a :=
  by induction a using Quot.ind with | _ a =>
    exact Quot.sound int_base.rel.succ_pred
theorem int.pred_succ {a : int} : pred (succ a) = a :=
  by induction a using Quot.ind with | _ a =>
    exact Quot.sound int_base.rel.pred_succ

Brandon Brown (May 11 2021 at 16:17):

Oh that's neat, I think I finally get it; I really appreciate you taking the time to write that clear example of the quotient type, @Mario Carneiro . I still do wonder if Lean4 could be extended to support a kind of higher inductive type since it has extensible syntax. Would it be possible to just write syntax for a custom inductive type where you do something like

inductive int : Type where
| zero : int
| succ : int  int
| pred : int  int
and
| eq1 : {a : int}  succ (pred a) = a
| eq2 : {a : int}  pred (succ a) = a

And it would parse this into a set of variables in a namespace

variable (int : Type)
variable (zero : int)
variable (succ : int  int)
variable (pred : int  int)
variable (eq1 : {a : int}  succ (pred a) = a)
variable (eq2: {a : int}  pred (succ a) = a)

along with a recursor and all the other ancillaries that are needed to make an inductive type? Just wondering out of curiosity. And I have no interest in HoTT I just like that higher inductive types appear to make it easier to do some "synthetic mathematics" i.e. axiomatizing something and proving from there rather than having to keep deriving from simpler types.

Yakov Pechersky (May 11 2021 at 16:21):

If you want to do "synthetic mathematics" you can do the following:

class my_int (s : Type) where
  zero : s
  succ : s  s
  pred : s  s
  eq1 :  {a : s}, succ (pred a) = a
  eq2 :  {a : s}, pred (succ a) = a

François G. Dorais (May 11 2021 at 16:22):

Lean 4 syntax is intended to be easily extensible, so this should be possible. (I struggled with a similar construct some long time ago and gave up... I hope experts will chime in with a solution.) Should the "and" allow for anything other than equalities?

Yakov Pechersky (May 11 2021 at 16:22):

(If I got the lean4 syntax right)

Yakov Pechersky (May 11 2021 at 16:22):

Then you can postulate

variable (s : Type _) [my_int s]

Yakov Pechersky (May 11 2021 at 16:23):

Now, Mario showed above that one can actually construct an actual type that can be shown to have a my_int instance. But not all postulated collections are valid. So whatever higher inductive type you make, you'd have to show that it doesn't imply False

François G. Dorais (May 11 2021 at 16:25):

To be clear from my last message, a "solution" would need to create an inductive and then a quotient, perhaps with some equation lemmas to hide the machinery.

Mario Carneiro (May 11 2021 at 16:27):

Yakov Pechersky said:

Now, Mario showed above that one can actually construct an actual type that can be shown to have a my_int instance. But not all postulated collections are valid. So whatever higher inductive type you make, you'd have to show that it doesn't imply False

This is the main reason that Lean doesn't support HITs. In fact the consistency and even a general schema for sound HITs was an open problem for a long time. There are some reasonable looking HITs that are inconsistent

Mario Carneiro (May 11 2021 at 16:28):

The "in many cases" in my earlier post is doing a lot of work there

Mario Carneiro (May 11 2021 at 16:29):

François G. Dorais said:

(I struggled with a similar construct some long time ago and gave up... I hope experts will chime in with a solution.)

Could you elaborate? What did you want to implement?

Brandon Brown (May 11 2021 at 16:36):

I see. So HITs are actually more work than quotient types. I was just cursorily looking at this paper "THE INTEGERS AS A HIGHER INDUCTIVE TYPE" https://arxiv.org/pdf/2007.00167.pdf and was intrigued but looks like it is actually a quite involved construction. I will stick with quotients.

Mario Carneiro (May 11 2021 at 16:38):

Things get a lot more complicated when you need higher higher constructors like coh : (z : Z) → ap_succ(sec(z)) =ret(succ(z)) there

Alex J. Best (May 11 2021 at 16:42):

The associated talk by Thorsten is quite good iirc https://www.youtube.com/watch?v=Fov95A2bGDI

Mac (May 11 2021 at 18:09):

Yakov Pechersky said:

Now, Mario showed above that one can actually construct an actual type that can be shown to have a my_int instance. But not all postulated collections are valid. So whatever higher inductive type you make, you'd have to show that it doesn't imply False

It is important to note though, that even if your axioms schema does imply False with "synthetic mathematics" approach by Yakov Pechersky i,e,

Yakov Pechersky said:

If you want to do "synthetic mathematics" you can do the following:

class my_int (s : Type) where
  zero : s
  succ : s  s
  pred : s  s
  eq1 :  {a : s}, succ (pred a) = a
  eq2 :  {a : s}, pred (succ a) = a

and

Yakov Pechersky said:

Then you can postulate

variable (s : Type _) [my_int s]

False will only be provable in places s is in scope. You will not have rendered Lean as a whole inconsistent (because you will not actually be able to construct an instance of my_int in a vacuum).

Mario Carneiro (May 11 2021 at 18:12):

That's probably not much consolation if you want to work with my_int though

Mac (May 11 2021 at 21:19):

True, though luckily my_int does not prove False (at least I hope so XD).

Brandon Brown (Jun 20 2021 at 14:48):

So the inductive relation defined by @Mario Carneiro above

inductive int_base.rel : int_base  int_base  Prop
| succ_congr : rel a b  rel (succ a) (succ b)
| pred_congr : rel a b  rel (pred a) (pred b)
| succ_pred : rel (succ (pred a)) a
| pred_succ : rel (pred (succ a)) a

appears not equivalent to the subtype version of the integers I defined elsewhere based on if it's in norm form, i.e. { x : base_int // is_norm (x)}
because I want e.g. a.succ.succ.pred.pred to be equal to zero whereas with this relation that doesn't appear to be true. I've instead defined a relation using the norm function Mario wrote. But was hoping someone could give me some clues on how to make progress on the proof that my addition function on the base integer type respects the relation so I can lift it to the quotient type. I think this new relation can be easily made into an equivalence relation but not sure if using Quotient rather than Quot buys me anything in this particular case.

inductive b_int : Type where
| zero : b_int
| succ : b_int  b_int
| pred : b_int  b_int
deriving DecidableEq

open b_int

def up : b_int  b_int
| (pred a) => a
| a => succ a

def down : b_int  b_int
| (succ a) => a
| a => pred a

def norm_aux : b_int  b_int  b_int :=
  fun a b =>
  match b with
  | zero => a
  | succ b => norm_aux (up a) b
  | pred b => norm_aux (down a) b

def norm : b_int  b_int := norm_aux zero

protected inductive b_int.rel : b_int  b_int  Prop
| refl : (norm a) = (norm b)  b_int.rel a b
| succ_congr : b_int.rel a b  b_int.rel (succ a) (succ b)
| pred_congr : b_int.rel a b  b_int.rel (pred a) (pred b)

def int := Quot b_int.rel

protected def int.succ (a : int) : int := a.lift (fun (a : b_int) => Quot.mk b_int.rel a.succ)
  fun (a b : b_int) (h : b_int.rel a b) => Quot.sound (b_int.rel.succ_congr h)

protected def int.pred (a : int) : int := a.lift (fun (a : b_int) => Quot.mk b_int.rel a.pred)
  fun (a b : b_int) (h : b_int.rel a b) => Quot.sound (b_int.rel.pred_congr h)

instance : Coe b_int int where
  coe (a : b_int) := Quot.mk b_int.rel a

protected def b_int.add (a b : b_int) : int :=
  match a with
  | zero => Quot.mk b_int.rel b
  | succ a => (b_int.add a b).succ
  | pred a => (b_int.add a b).pred

theorem add_zero : b_int.add zero a = (a : int) := rfl

theorem add_succ : b_int.add a c = b_int.add a b  b_int.add (succ a) c = b_int.add (succ a) b := sorry
theorem add_pred : b_int.add a c = b_int.add a b  b_int.add (pred a) c = b_int.add (pred a) b := sorry
-- Not sure if this is the right direction
theorem add_eq2 :  (c b : b_int), (b_int.rel c b)  b_int.add a c = b_int.add a b := by
  intros c b h
  induction a with
  | zero =>
    show (c:int) = (b:int)
    apply Quot.sound
    assumption
  | succ a h2 =>
    apply add_succ
    assumption
  | pred a h3 =>
    apply add_pred
    assumption

-- have to apply lift twice
def int.add : int  int  int :=
  (Quot.lift (λ a : b_int => Quot.lift (b_int.add a) add_eq2) sorry)

Mario Carneiro (Jun 20 2021 at 14:51):

The relation is not itself an equivalence relation, but the equivalence relation generated by that relation makes a.succ.succ.pred.pred = a because a.succ.succ.pred.pred ~ a.succ.pred ~ a where the two relations are in int_base.rel and they are connected by transitivity

Mario Carneiro (Jun 20 2021 at 14:52):

That is, in int := Quot int_base.rel, [[a.succ.succ.pred.pred]] = [[a.succ.pred]] = [[a]] where we use Quot.sound for the individual equalities and Eq.trans to connect them

Kevin Buzzard (Jun 20 2021 at 14:52):

If you import mathlib's data.quot then you will have access to stuff like quot.lift₂, quotient.lift₂, quotient.map₂ and so on, which might make things easier.

Brandon Brown (Jun 20 2021 at 14:55):

Mathlib4 has quot.lift₂ ?

Mario Carneiro (Jun 20 2021 at 14:56):

mathlib 3 does

Mario Carneiro (Jun 20 2021 at 14:56):

there isn't really anything about what you are doing that needs lean 4

Mario Carneiro (Jun 20 2021 at 14:57):

you might as well learn how to do it in lean 3 where there are more theorems available

Kevin Buzzard (Jun 20 2021 at 14:58):

Apologies, I hadn't noticed what stream we're in. Please feel free to port data.quot to mathlib 4 :-)

Brandon Brown (Jun 22 2021 at 04:09):

Thanks, @Mario Carneiro . I get it now, it didn't occur to me that once I have the quotient my non-equivalence relation becomes an equivalence relation given the quotient sound axiom. Indeed, I can prove

example : (Quot.mk b_int.rel zero) = (Quot.mk b_int.rel zero.pred.pred.succ.succ) := by
  have h1 : Quot.mk b_int.rel zero.pred.succ = Quot.mk b_int.rel (succ (succ (pred (pred zero)))) := by {
    apply Quot.sound;
    apply b_int.rel.succ_congr;
    apply b_int.rel.succ_pred;
  };
  rw [h1];
  apply int_succ_pred;

Moreover, I got around having to use Quot.lift twice by defining my add function to take as the first argument an int and as the second argument a b_int.

protected def b_int.add (a : int) (b : b_int) : int :=
  match b with
  | zero => a
  | succ b => (b_int.add a b).succ
  | pred b => (b_int.add a b).pred

And I proved it respects the equivalence relation to make it into the desired int.add : int → int → int
I am now content and can move beyond my obsession defining the integers in various ways, now having defined the integers the way lean core does, as a subtype using norm and as a quotient type.

Full Code

Brandon Brown (Jun 22 2021 at 04:15):

And yes Lean 3 would be probably easier but I intend to write some tutorials on Lean 4 on my blog


Last updated: Dec 20 2023 at 11:08 UTC