Zulip Chat Archive

Stream: new members

Topic: noob question(s)


Wojciech Nawrocki (Nov 20 2018 at 00:46):

What's the command to make Lean automatically derive decidable_eq for some custom inductive type?

Chris Hughes (Nov 20 2018 at 00:47):

@[derive decidable_eq]

Wojciech Nawrocki (Nov 20 2018 at 00:47):

ah I should've thought of that, thanks! :)

Wojciech Nawrocki (Nov 20 2018 at 03:18):

Hm, when I add

open classical
local attribute [instance] prop_decidable

to a file, definitions below it which used to pass now fail with:

equation compiler failed to generate bytecode for 'subst._main'
nested exception message:
code generation failed, VM does not have code for 'classical.choice'

Why might this be?

Is it basically because classical makes things uncomputable? If so, maybe Lean should detect that classical is not used in a particular case and still compile the definition?

Chris Hughes (Nov 20 2018 at 03:21):

Try [instance, priority 0]. Otherwise it uses classical decidability even when there's proper decidability.

Wojciech Nawrocki (Nov 20 2018 at 03:23):

Ah indeed, thanks Chris!

Kevin Buzzard (Nov 20 2018 at 08:22):

This trips lots of people up! I wonder where people are learning this trick? Not putting priority 0 can trip you up later in quite a confusing way

Patrick Massot (Nov 20 2018 at 08:23):

@Jeremy Avigad needs to fix the very bottom of https://leanprover.github.io/theorem_proving_in_lean/axioms_and_computation.html

Johan Commelin (Nov 20 2018 at 08:23):

I think Lean shows an error message when it can't find an instance for decidable, and that error message does not include setting the priority. It would be very helpful if it did.

Patrick Massot (Nov 20 2018 at 08:23):

and https://leanprover.github.io/theorem_proving_in_lean/type_classes.html

Jeremy Avigad (Nov 20 2018 at 15:03):

I'm on it -- I'll do it tomorrow.

Patrick Massot (Nov 20 2018 at 16:08):

Thanks!

Kenny Lau (Nov 20 2018 at 16:09):

how about not using classical.dec

Reid Barton (Nov 20 2018 at 16:10):

Let's just agree to not not use it

Kenny Lau (Nov 20 2018 at 16:11):

that doesn't mean we use it :P

Patrick Massot (Nov 20 2018 at 16:11):

You may have missed Reid's point

Reid Barton (Nov 20 2018 at 16:11):

It doesn't mean you use it

Wojciech Nawrocki (Nov 20 2018 at 22:59):

Is it possible to make Lean display the values of constant variables in the tactic state? E.g. if i have lst: list nat which is also empty, it would be nice to see that it's empty.

Kenny Lau (Nov 20 2018 at 23:01):

either this doesn't make sense, or subst lst

Wojciech Nawrocki (Nov 20 2018 at 23:04):

Hm I should elaborate, maybe I'm misunderstanding how induction works. Say I have a hypothesis h: InductiveFoo list.nil, where InductiveFoo: list nat -> Prop. So then running induction h creates cases for all the constructors of InductiveFoo, which take the list as an input, say lst. But the lst is empty, so it'd be nice to see that in the state.

Kenny Lau (Nov 20 2018 at 23:06):

you can't because it's forgotten

Kenny Lau (Nov 20 2018 at 23:06):

you might want to generalize_hyp

Wojciech Nawrocki (Nov 20 2018 at 23:16):

Thanks!

Wojciech Nawrocki (Nov 21 2018 at 15:03):

Perhaps it would be useful to have a reference sheet for translating from Coq to Lean tactics?

Mario Carneiro (Nov 21 2018 at 15:05):

I recall such a thing being made at one point. Maybe it's in mathlib docs?

Rob Lewis (Nov 21 2018 at 15:05):

https://github.com/jldodds/coq-lean-cheatsheet

Rob Lewis (Nov 21 2018 at 15:05):

Note the date though, it'll need updating.

Wojciech Nawrocki (Nov 21 2018 at 15:06):

Oh, nice!

Wojciech Nawrocki (Nov 21 2018 at 16:06):

Is there a general tactic for showing false by "this term could not have been constructed"? E.g.

inductive Foo: Prop  Prop  Prop
| FooT: Foo true true
| FooF: Foo false false

-- This term could not have been constructed
lemma impossible (h: Foo true false)
  : false := by sorry

Mario Carneiro (Nov 21 2018 at 16:11):

cases

Mario Carneiro (Nov 21 2018 at 16:11):

also empty match

Mario Carneiro (Nov 21 2018 at 16:11):

lemma impossible : Foo true false → false.

Rob Lewis (Nov 21 2018 at 16:12):

You have to make Foo : bool -> bool -> Prop for that.

Wojciech Nawrocki (Nov 21 2018 at 16:13):

Yeah, for Prop -> Prop -> Prop both of these fail, but that wasn't actually my problem, so thanks!

Patrick Massot (Nov 21 2018 at 16:16):

It seems that variations on this topic comes up again and again. We should really find a way to document that

Wojciech Nawrocki (Nov 21 2018 at 20:17):

That would be nice :) I was also surprised to see that more often than not contradiction fails when cases h works. Description: The contradiction tactic attempts to find in the current local context an hypothesis that is equivalent to an empty inductive type (e.g. false). I thought a hypothesis that cannot be constructed is exactly that, but maybe I'm misunderstanding it?

Wojciech Nawrocki (Nov 22 2018 at 01:12):

Is it possible to reserve some notation for an inductive type and then use it while defining the type, like in Coq? I tried this, but the parser seems to fail:

reserve infix ``:50

inductive Typeof: list Tp  Tp  Prop
| Z:  (Γ: list Tp) A, Typeof (Γ.append [A]) A
| S_:  (Γ: list Tp) A B, Typeof Γ A  Typeof (Γ.append [B]) A

inductive Typeof: list Tp  Tp  Prop
| Z:  (Γ: list Tp) A, (Γ.append [A])  A -- fails
| S_:  (Γ: list Tp) A B, Γ  A  (Γ.append [B])  A

infix  := Typeof

Mario Carneiro (Nov 22 2018 at 03:00):

Yes! You can just put a notation line between the inductive header and the first constructor

inductive Typeof{Tp}: list Tp  Tp  Prop
infix ``:50 := Typeof
| Z:  (Γ: list Tp) A, (Γ.append [A])  A
| S_:  (Γ: list Tp) A B, Γ  A  (Γ.append [B])  A

Jeremy Avigad (Nov 23 2018 at 16:29):

@Patrick Massot I added a discussion of the priority 0 trick to Section 10.4 of TPIL (search on "priority 0"):
https://leanprover.github.io/theorem_proving_in_lean/type_classes.html#decidable-propositions
I also added a back reference in Section 11:
https://leanprover.github.io/theorem_proving_in_lean/axioms_and_computation.html#the-law-of-the-excluded-middle

Finally, I fixed an old issue raised by @Joseph Corneli by changing all the examples in 6.4:
https://leanprover.github.io/theorem_proving_in_lean/interacting_with_lean.html#attributes
https://github.com/leanprover/theorem_proving_in_lean/issues/62

Teaching my class next semester will give me a chance to review and expand TPIL. I am planning to add one more chapter on some of the fine points of dependent type theory, e.g. explaining how to work with equality and dependent types (the dark side of type theory), and explaining how Lean manages recursion on arbitrary well-founded relations. I'll also try to write a less ambitious but up-to-date version of Programming in Lean. But I am counting on the mathlib crew to continue documenting mathlib and all the new tactics, and to provide useful guidance on using the library and proving theorems.

Kevin Buzzard (Nov 23 2018 at 16:33):

I have not been using Lean seriously since term started; there are three weeks to go before it finishes. After that I fully intend to go back to the perfectoid project. But when I don't understand something, my instinct is to write docs about it, because if I work something out and don't write down what I learnt then I realise a month later that I've forgotten it all again!

Patrick Massot (Nov 23 2018 at 20:02):

Thank you very much Jeremy! Your documentation work is really crucial.

Patrick Massot (Nov 23 2018 at 21:06):

I'm now reading random pieces of TPIL, and I have a couple more suggestions about chapter 10:

  • at several places, emacs is mentioned but not VScode. I guess this goes back to before the VScode extension was available, but it could be misleading
  • in 10.5. Managing Type Class Inference, I think it would be nice to add the standard tricks to see what's the name of an instance Lean is finding, and sometimes what's the actual definition, as in
#check (by apply_instance : has_add )
#reduce (infer_instance : has_add )

maybe find a better example for the second one since the answer is not super easy to read (every nice example coming to my mind are in mathlib...)

Patrick Massot (Nov 23 2018 at 21:09):

Oh, it seems apply_instance is never mentioned in TPIL :sad:

Patrick Massot (Nov 23 2018 at 21:10):

Another thing that would be very helpful, both because it can be puzzling and because it can be very helpful would be to discuss

def n : Type := 

example : has_add  := by apply_instance  -- ok
example : has_add n := by apply_instance  -- fails
example : has_add n := by unfold n ; apply_instance  -- ok

Jeremy Avigad (Nov 24 2018 at 13:23):

Thanks for the input. Yes, TPIL evolved over time, and the last major rewrite was early in the days of Lean 3, before there was a VSCode extension. I'll do a global search and try to make the text less emacs-centric.

I'll discuss apply_instance and infer_instance. I am thinking of using these examples:

/- example 1: using apply_instance -/

def foo : has_add nat := by apply_instance
def bar : inhabited (nat  nat) := by apply_instance

/- example 2: using infer_instance -/

def baz : has_add nat := infer_instance
def bla : inhabited (nat  nat) := infer_instance

/- example 3: seeing them -/

#print foo    -- nat.has_add
#reduce foo   -- (unreadable)

#print bar    -- pi.inhabited ℕ
#reduce bar   -- {default := λ (a : ℕ), 0}

#print baz    -- infer_instance
#reduce baz   -- (same as for #reduce foo)

#print bla    -- infer_instance
#reduce bla   -- {default := λ (a : ℕ), 0}

/- example 4: tricks to be more concise -/

#check (by apply_instance : inhabited )
#print nat.inhabited

#reduce (infer_instance : inhabited )

/- examples 5: core Lean can't find an instance for inhabited set -/

-- fails
-- example {α : Type*} : inhabited (set α) := by apply_instance

/- example 6: supplying one manually -/

def inhabited.set (α : Type*) : inhabited (set α) := 
#print inhabited.set     -- λ {α : Type u}, {default := ∅}
#reduce inhabited.set   -- {default := λ (a : ℕ), false}

/- example 7: unfolding a definition so Lean can find it -/

def inhabited.set (α : Type*) : inhabited (set α) :=
by unfold set; apply_instance
#print inhabited.set     -- λ (α : Type u), eq.mpr _ (pi.inhabited α)
#reduce inhabited.set   -- {default := λ (a : ℕ), true}

/- example 8: using dunfold instead -/

def inhabited.set (α : Type*) : inhabited (set α) :=
by dunfold set; apply_instance
#print inhabited.set     -- λ (α : Type u), id (pi.inhabited α)
#reduce inhabited.set   -- {default := λ (a : ℕ), true}

Patrick Massot (Dec 11 2018 at 09:17):

Yes! You can just put a notation line between the inductive header and the first constructor

inductive Typeof{Tp}: list Tp  Tp  Prop
infix ``:50 := Typeof
| Z:  (Γ: list Tp) A, (Γ.append [A])  A
| S_:  (Γ: list Tp) A B, Γ  A  (Γ.append [B])  A

Is there something similar for dependant structure? If one field of my structure is a binary operator, can I define an infix notation usable in the remaining fields declaration?

Kevin Buzzard (Dec 11 2018 at 09:18):

I usually make the structure extend the notation typeclass in this situation.

Kevin Buzzard (Dec 11 2018 at 09:19):

In fact I have been known to make new notation typeclasses called things like group_notation extending has_mul, has_one and has_inv, and then extending these too so I get a bunch of notation at once.

Patrick Massot (Dec 11 2018 at 09:21):

Thanks Kevin. I know all this, but I'm still interested in an answer to my question.

Kevin Buzzard (Dec 11 2018 at 09:21):

Yes I understand. For example if the notation is not in the standard notation list then it would be nicer to add it directly in the definition of the structure.

Rob Lewis (Dec 11 2018 at 12:05):

You can define notation in structures that's used in the remaining fields. But I think it's just local to the structure declaration.

structure patrick :=
(α : Type)
(f : α  α  α)
(infix `^^^`:50 := f)
(h :  a : α, a ^^^ a = a)

#check patrick.h

Patrick Massot (Dec 11 2018 at 13:29):

Thanks!

Jeremy Avigad (Jan 03 2019 at 18:49):

I just crossed this item off my to do list. apply_inference and such are now discussed here: https://leanprover.github.io/theorem_proving_in_lean/type_classes.html#managing-type-class-inference. And VS Code is now mentioned whenever Emacs is, with VS Code first.

Patrick Massot (Jan 03 2019 at 18:54):

If you add this to your file in Emacs mode and use C-c C-x to run an independent Lean process on your file, the output buffer will show a trace every time the type class resolution procedure is subsequently triggered.

This paragraph (in the section your referred to) is still Emacs centric

Jeremy Avigad (Jan 03 2019 at 19:20):

Yes, that is the only one, because I don't know how to start an independent Lean process from within VS Code. I guess I'll remind people that they can run Lean from the VS Code terminal.

Patrick Massot (Jan 03 2019 at 19:20):

Why would you do that?

Patrick Massot (Jan 03 2019 at 19:20):

The trace is printed in the info view

Jeremy Avigad (Jan 03 2019 at 19:23):

Oh! I forgot. Good point. I'll fix that.

Patrick Massot (Jan 03 2019 at 19:24):

Great!

Jeremy Avigad (Jan 03 2019 at 19:34):

Fixed. Thanks for catching it.
https://leanprover.github.io/theorem_proving_in_lean/type_classes.html#managing-type-class-inference

Wojciech Nawrocki (Jan 16 2019 at 12:50):

Hello! Is there anything special I need to do to make Lean recognise my instance : has_zero Foo as being equivalent to 0? I got this state while trying to use rw [this]:

rewrite tactic failed, did not find instance of the pattern in the target expression
  0 + π₂
state:
3 goals
π₂ : mult,
this : 0 + π₂ = π₂
 mult.Zero + π₂ = π₂ + mult.Zero

even though I have

instance : has_zero mult :=
  mult.Zero

above

Kevin Buzzard (Jan 16 2019 at 12:55):

rewrites don't recognise definitional equality, only syntactic equality.

Kevin Buzzard (Jan 16 2019 at 12:55):

So you could try "show 0 + pi2 = _" before the rewrite

Kevin Buzzard (Jan 16 2019 at 12:56):

or "change mult.Zero + _ = _ at this". Maybe it will work after one of these changes. But not after both ;-)

Reid Barton (Jan 16 2019 at 12:56):

convert this should also work

Kevin Buzzard (Jan 16 2019 at 12:57):

but this isn't the goal

Kevin Buzzard (Jan 16 2019 at 12:57):

Oh!

Kevin Buzzard (Jan 16 2019 at 12:58):

This convert trick works when the thing you're rewriting is precisely one side of the equality I guess.

Reid Barton (Jan 16 2019 at 12:58):

or erw this would also work but it's not as nice

Reid Barton (Jan 16 2019 at 13:01):

In fact you can use a' = b' to prove a = b with neither side matching definitionally (you'll get two new goals a = a' and b = b'), but then you run the risk that the new goals are not actually true :smile:

Wojciech Nawrocki (Jan 16 2019 at 13:13):

Ah, I see, thanks! And related, I'm proving that an object with three elements and custom add/mult tables is a semiring, how ugly is it to do all my proofs like this?

  lemma add_assoc (π₁ π₂ π₃: mult)
    : π₁ + π₂ + π₃ = π₁ + (π₂ + π₃) := by { cases π₁; cases π₂; cases π₃; refl }

Mario Carneiro (Jan 16 2019 at 13:17):

it will work, although there are simpler proofs where you ony case on one of them, I think

Wojciech Nawrocki (Jan 16 2019 at 13:20):

Hm, I found that without expanding every case, I had to do a bit more work by using other lemmas and so on. The definition of add is:

inductive mult: Type
| Zero: mult
| One: mult
| Omega: mult

instance : has_zero mult :=
  mult.Zero

instance : has_one mult :=
  mult.One

notation `ω` := mult.Omega

def add: mult  mult  mult
| 0 π := π
| π 0 := π
| 1 1 := ω
| ω _ := ω
| _ ω := ω

instance : has_add mult :=
  add

Mario Carneiro (Jan 16 2019 at 13:22):

right

Mario Carneiro (Jan 16 2019 at 13:24):

you should have lemmas like x + ω = ω as simp lemmas which should simplify most of the cases

Mario Carneiro (Jan 16 2019 at 13:25):

or you could just split into 27 cases if you want

Wojciech Nawrocki (Jan 16 2019 at 13:26):

Ah ok, i'll try it with simp as well, thanks!

Johan Commelin (Jan 16 2019 at 13:55):

Scott Morrison's case-bashing tactic would probably be useful here. But I don't know where that tactic lives at the moment...

Mario Carneiro (Jan 16 2019 at 14:01):

if you want to do a case bashing proof, another approach is to prove fintype mult and decidable_eq mult (you can derive this), and then you can just revert everything and use dec_trivial

Johan Commelin (Jan 16 2019 at 14:02):

Should even be reasonably fast, I guess.

Mario Carneiro (Jan 16 2019 at 14:02):

as long as you don't have too many variables; it is still 27 cases

Kevin Buzzard (Jan 16 2019 at 14:18):

Kind of a stupid question, but when I'm using other computer algebra systems I would expect checking a million cases to be very quick. Mario's comments suggest that 27 is rather large for Lean. What is happening here?

Mario Carneiro (Jan 16 2019 at 14:37):

There is a large overhead of the expression that is generated, elaboration for it, and typechecking

Mario Carneiro (Jan 16 2019 at 14:38):

I don't think 27 is that large in this context, I guess it's probably less than a second to check

Mario Carneiro (Jan 16 2019 at 14:38):

I just think it's better to have more "human" proofs with fewer cases

Kevin Buzzard (Jan 16 2019 at 14:38):

This is what I don't understand. We have to check that 27 things of the form add a (add b c) = add (add a b) c hold and in each case this is by refl.

Kevin Buzzard (Jan 16 2019 at 14:39):

How long does it take Lean to prove (0 + 1) + 1 = 0 + (1 + 1) in this type?

Mario Carneiro (Jan 16 2019 at 14:40):

there is also all the intermediate steps, the generation of motives, lots of abstraction and substitution going on, and large terms being built up behind the scenes before you even attack those 27 cases

Kevin Buzzard (Jan 16 2019 at 14:40):

So the bottleneck is elsewhere?

Mario Carneiro (Jan 16 2019 at 14:41):

I have heard it repeatedly asserted that the kernel is not a bottleneck

Kevin Buzzard (Jan 16 2019 at 14:41):

Again the idea of a "large" term is confusing to me. In python I could happily manipulate a list with 1000 elements.

Mario Carneiro (Jan 16 2019 at 14:41):

this term has way more than 1000 subterms

Kevin Buzzard (Jan 16 2019 at 14:41):

One of my kids has been learning about algorithms over the last few months and I realise now that I am far more aware of these things than I used to be.

Mario Carneiro (Jan 16 2019 at 14:42):

all in all it makes lean just look a lot slower to do "simple" things

Mario Carneiro (Jan 16 2019 at 14:42):

because there is a lot of bookkeeping in the background

Mario Carneiro (Jan 16 2019 at 14:44):

I would like to figure out ways to minimize the overhead, but that runs close to work on the lean compiler

Kevin Buzzard (Jan 16 2019 at 15:28):

this term has way more than 1000 subterms

In the same way that a set with 10 elements has more than 1000 subsets, or in a more serious "we really need to work with way more than 1000 things" way?

Mario Carneiro (Jan 16 2019 at 15:50):

in the more serious way. (It's tricky to count the "size" of an expression but number of subterms is a good proxy)

Mario Carneiro (Jan 16 2019 at 15:51):

there is no exponential growth because subterms can't overlap, they are either disjoint or in a containment relationship

Wojciech Nawrocki (Jan 18 2019 at 00:04):

Is it possible to define a custom synthesis strategy for an implicit argument? I would like to define a function which extracts concrete values from concrete lists, like so:

def get':  (l: list ) (n: ) {h: n < l.length}, 
| (x::xs) 0 _ := x
| (x::xs) (n+1) h := @get' xs n (nat.lt_of_succ_lt_succ h)
| [] n h := by { exfalso, simp at h, exact (nat.not_lt_zero n)

and for concrete args, h is always derivable with a custom tactic. I'd like Lean to use that tactic to synthesise it.
OR am I doing this completely wrong and there is a much simpler way?

Chris Hughes (Jan 18 2019 at 00:07):

unification hints! I don't know much about them though.

Mario Carneiro (Jan 18 2019 at 00:51):

This function is list.nth_le btw

Mario Carneiro (Jan 18 2019 at 00:53):

you can synthesize the argument using typeclasses, but exact_dec_trivial is another easy way to do it

Mario Carneiro (Jan 18 2019 at 00:54):

def get' (l : list ) (n : ) (h : n < l.length . exact_dec_trivial) :  := l.nth_le n h

example : get' [1, 2, 3] 1 = 2 := rfl

Wojciech Nawrocki (Jan 18 2019 at 01:07):

Ah indeed, thanks Mario! Can I use something like this in a Pi-type (to make the equation compiler work)? The foo . tactic syntax doesn't seem to work:

def debrujin_of_nat: Π {Γ: Env} (n: ) (h: n < Γ.length . tactic.exact_dec_trivial), (Γ  Γ.nth_le n h) -- ill-formed declaration

Mario Carneiro (Jan 18 2019 at 01:14):

If you can put it left of the colon, the dot notation should work. But if you can't write it that way, it's sugar for auto_param:

def get' : Π (l : list ) (n : ), auto_param (n < l.length) ``exact_dec_trivial   := list.nth_le

example : get' [1, 2, 3] 1 = 2 := rfl

Wojciech Nawrocki (Jan 18 2019 at 01:19):

Can I have a _named_ auto_param :sweat_smile:? I need to use the hypothesis in the type signature itself, more specifically in the return type.

Mario Carneiro (Jan 18 2019 at 01:21):

sure, just use a pi instead of an arrow

Mario Carneiro (Jan 18 2019 at 01:21):

auto_param T n is defeq to T so it doesn't cause any problems

Wojciech Nawrocki (Jan 18 2019 at 01:27):

Oh, I was sure I'd tried that but apparently not, thanks! Doesn't seem to work under #eval unfortunately:

don't know how to synthesize placeholder
context:
 auto_param (0 < list.length [Tp.Nat]) (name.mk_string "exact_dec_trivial" (name.mk_string "tactic" name.anonymous))

Mario Carneiro (Jan 18 2019 at 01:29):

what did you write?

Mario Carneiro (Jan 18 2019 at 01:30):

it's not really related to the context you write it in, but rather the expected type during elaboration

Wojciech Nawrocki (Jan 18 2019 at 01:35):

Apologies for the length, but this is the full context:

import tactic.find
import tactic.interactive
import tactic
import tactic.auto_cases
import tactic.tidy

@[derive decidable_eq]
inductive Tp
| Nat: Tp
| Bool: Tp
| Fn: Tp  Tp  Tp

local infixr `  `:50 := Tp.Fn

def Env := list Tp

inductive TypeIn: Env  Tp  Type
infix `  `:40 := TypeIn
| ZVar: Π {Γ T}, T::Γ  T
| SVar: Π {Γ T U}, Γ  T  U::Γ  T

local infix `  `:40 := TypeIn

open TypeIn

inductive Term: Env  Tp  Type
| Nat (n: ): Π {Γ}, Term Γ Tp.Nat -- in all environments, nat literals have type Nat
| Bool (b: bool): Π {Γ}, Term Γ Tp.Bool -- and booleans have type Bool
| Var: Π {Γ T}, Γ  T  Term Γ T -- A variable has type T given its de Brujin index
                                 -- is in the environment.
| Abs: Π {Γ T U}, Term (T::Γ) U  Term Γ (T  U)
| App: Π {Γ T U}, Term Γ (T  U)  Term Γ T  Term Γ U

open Term

def debrujin_of_nat: Π {Γ: Env} (n: ) {h: auto_param (n < Γ.length) ``tactic.exact_dec_trivial}, (Γ  Γ.nth_le n h)
| (T::Γ) 0 _ := ZVar
| (T::Γ) (n+1) h := SVar (@debrujin_of_nat Γ n (nat.lt_of_succ_lt_succ h))
| [] n h := by { exfalso, simp at h, exact (nat.not_lt_zero n) h }

local notation `#` n := Var (debrujin_of_nat n)

#eval (@App [] Tp.Nat Tp.Nat (@Abs [] Tp.Nat Tp.Nat (@Var [Tp.Nat] Tp.Nat (debrujin_of_nat 0))) (@Nat 3 []))

Wojciech Nawrocki (Jan 18 2019 at 01:37):

Basically given a concrete list Tp and a concrete n, I'd like it to figure out that n is within bounds and include the result of lst.nth_le n _ in the return type.

Wojciech Nawrocki (Jan 18 2019 at 01:41):

This does work: #eval (@App [] Tp.Nat Tp.Nat (@Abs [] Tp.Nat Tp.Nat (@Var [Tp.Nat] Tp.Nat (@debrujin_of_nat [Tp.Nat] 0 (by tactic.exact_dec_trivial)))) (@Nat 3 [])) (notice the explicit proof I put in)

Mario Carneiro (Jan 18 2019 at 02:06):

so what did you write?

Wojciech Nawrocki (Jan 18 2019 at 02:07):

Well, the #eval at the bottom of that long snippet is what fails synthesis. The #eval with an explicit proof works

Mario Carneiro (Jan 18 2019 at 02:07):

aha, you made the arg implicit

Mario Carneiro (Jan 18 2019 at 02:07):

auto params should be explicit

Mario Carneiro (Jan 18 2019 at 02:07):

def debrujin_of_nat: Π {Γ: Env} (n: ℕ) (h: auto_param (n < Γ.length) ``tactic.exact_dec_trivial), (Γ ∋ Γ.nth_le n h)

Wojciech Nawrocki (Jan 18 2019 at 02:09):

Oh thanks, now it does work, but still behaves as if it were implicit :thinking: is this currying at work, meaning I have to place auto_param last, s.t. given foo: nat -> auto_param blah -> nat, (foo n): nat (and foo n _ still fails)?

Wojciech Nawrocki (Jan 18 2019 at 23:39):

(unrelated to above)
I'm seeing a wierd error in an inductive type: invalid occurrence of recursive arg#3 of 'context.cons', the body of the functional type depends on it.. The type definition is below, and as far as I know it's a perfectly legit defn, so what's wrong?

inductive context: list   Type
| nil: context []
| cons {ns: list } (_: context ns) (n: ) (m: ): context (n::ns)

EDIT: swapping two arguments makes it compile, but why?

inductive context: list   Type
| nil: context []
| cons: Π {ns: list } (n: ) (_: context ns) (m: ), context (n::ns)

Mario Carneiro (Jan 18 2019 at 23:48):

I think Gabriel recently pointed out an example similar to this. You have a dependent pi (n) after a recursive arg (_ : context ns) and lean doesn't like this

Wojciech Nawrocki (Jan 19 2019 at 00:37):

Ah ok, maybe this could be fixed in Lean 4? :)

Wojciech Nawrocki (Jan 19 2019 at 01:51):

Do I need to do something special to make the semiring-ness of my custom type available to the ring tactic? I have a state like this:

π π' : mult,
π_1 : mult,
 π * π' * π_1 = π * (π' * π_1)

which is provable by exact mult.monoid.mul_assoc π π' π_1, but ring fails. I have instance : semiring mult shown a few lines above.

Mario Carneiro (Jan 19 2019 at 02:01):

you need to prove comm_semiring mult

Wojciech Nawrocki (Jan 21 2019 at 18:36):

Given def add (a b: foo): foo := blah, what's the difference between infix ++ := add and instance : has_add foo := ⟨add⟩? If i switch from the former to the latter and replace ++ with +, my proofs break at the simplification stage, namely addition seems to not be unfoldable anymore

Kevin Buzzard (Jan 21 2019 at 18:37):

They're very different in the sense that they're using different machinery to figure out what's going on.

Kevin Buzzard (Jan 21 2019 at 18:38):

I guess the infix trick is just syntax sugar, whereas the instance approach is using type class inference. Can you give an example of something which breaks?

Kevin Buzzard (Jan 21 2019 at 18:39):

I guess if you go via the instance approach then you have an extra layer of unfolding to do. + is has_add.add, which unfolds to your add.

Kevin Buzzard (Jan 21 2019 at 18:39):

Maybe that's the answer to your question. If you're trying to unfold things explicitly in the middle of a proof, maybe you have to insert some unfold has_add.add's

Kevin Buzzard (Jan 21 2019 at 18:40):

++ unfolds directly to your add, whereas + unfolds to has_add.add which unfolds to your add.

Kevin Buzzard (Jan 21 2019 at 18:42):

[NB I'm a bit of a CS noob, I don't know if "unfolds" is the right terminology for notation turning into its underlying definition]

Kevin Buzzard (Jan 21 2019 at 18:42):

[they might well be syntactically equal rather than just definitionally equal]

Wojciech Nawrocki (Jan 21 2019 at 19:29):

Ah indeed, unfolding twice does make it work - thanks!

Wojciech Nawrocki (Jan 22 2019 at 01:12):

Does there exist a general tactic for proving f a0 .. an = f b0 .. bn from a0 = b0 .. an = bn?

Mario Carneiro (Jan 22 2019 at 01:33):

congr

Wojciech Nawrocki (Jan 22 2019 at 01:36):

Hm, I tried congr but it seems to iterate the congruence, which gives me unprovable goals. Namely, I have a goal f (g x) = f (g y) and congr gives me x = y but I just want g x = g y. EDIT: congr' 1 works, thx!

Mario Carneiro (Jan 22 2019 at 02:37):

use congr' 1 and increase the number until you get a good result

Wojciech Nawrocki (Jan 22 2019 at 22:43):

The issue of has_add.add and its actual value not being definitionally equal makes a lot of my proofs quite ugly - I have to expand definitions first so that the expressions can simplify and then fold them back into the has_add.add version (or has_mul.mul, etc), because all the ring/module/whatever laws only work on those. For example:

  { /-
    case context.cons
    δ γ γ₁ : precontext,
    π₁ : mult,
    T₁ : tp,
    Γ₁ : context γ₁,
    ih₁ : ∀ {Γ₂ : context γ₁} {Ξ : matrix γ₁ δ}, vmul (Γ₁ + Γ₂) Ξ = vmul Γ₁ Ξ + vmul Γ₂ Ξ,
    Γ₂ : context (T₁ :: γ₁),
    Ξ : matrix (T₁ :: γ₁) δ
    ⊢ vmul (cons π₁ T₁ Γ₁ + Γ₂) Ξ = vmul (cons π₁ T₁ Γ₁) Ξ + vmul Γ₂ Ξ
    -/
    cases Γ₂ with _ π₂ _ Γ₂,
    -- unfold
    unfold vmul has_add.add context.add has_scalar.smul context.smul at *,
    simp *,
    -- fold back
    let a := vmul Γ₁ (λ (U : tp) (x : γ₁  U), Ξ U (SVar x)),
    let b := vmul Γ₂ (λ (U : tp) (x : γ₁  U), Ξ U (SVar x)),
    change
      (π₁ + π₂)  (Ξ T₁ ZVar) + (a + b)
      =
      (π₁(Ξ T₁ ZVar) + a) + (π₂(Ξ T₁ ZVar) + b),
    -- simplify using monoid laws
    simp [context.add_smul, context.add_assoc] },

is there some tactic or such that I could apply to do this automatically?

Mario Carneiro (Jan 22 2019 at 22:51):

This is what simp lemmas are for

Mario Carneiro (Jan 22 2019 at 22:53):

If you define add x (y :: z) := y :: add x z, for example, and then install add as a has_add instance, then you can prove x + (y :: z) = y :: (x + z) by rfl, and you should state this as a simp lemma

Mario Carneiro (Jan 22 2019 at 22:54):

You should not ever have to unfold has_add.add

Wojciech Nawrocki (Jan 22 2019 at 22:56):

Hm okay, so basically I need to "lift" the behaviour of my functions from the custom definition to one using has_op.op? I'll try

Wojciech Nawrocki (Jan 22 2019 at 23:09):

Is it fine to unfold has_zero.zero though? My definition of 0 for this type is

def zeros: Π γ, context γ
| [] := nil
| (T::δ) := cons 0 T (zeros δ)

and I need the cons to prove 0+Γ=Γ

Wojciech Nawrocki (Jan 22 2019 at 23:21):

In any case this is pretty awesome, all my proofs have shortened by half now without the unfolding, thanks a lot!

Mario Carneiro (Jan 23 2019 at 00:37):

For this, you should decide whether you prefer to write the empty context as 0 or [], and write a simp lemma like 0 = [] if you want to get rid of the 0 everywhere. In this case you should also make sure that all your other simp lemmas use the "preferred form" of this element on the LHS

Wojciech Nawrocki (Feb 23 2019 at 22:04):

What's the closest thing to the ring tactic when what I have is not a commutative semiring, e.g. just a monoid? Say my theorems are provable simply by repeated application of monoid or group laws, what tactic could I use?

Kevin Buzzard (Feb 23 2019 at 22:08):

If it's abelian then abel might work, and if it's not then you're best going with simp I think

Kevin Buzzard (Feb 23 2019 at 22:09):

You might have to write simp [mul_assoc] maybe, I can't remember if mul_assoc is a simp lemma

Wojciech Nawrocki (Feb 23 2019 at 22:15):

abel worked :) thanks. And no, mul_assoc doesn't seem to be a simp lemma.

Wojciech Nawrocki (Feb 23 2019 at 23:41):

Say I would like to work with a particular module which uses a variable v instantiated over a concrete v. For example, a file foo.lean could first declare variable {v: Type} and then use v in all definitions/lemmas in the file. I would like to import everything in foo instantiated with e.g. nat for v. This would be equivalent to partially applying everything in the file to nat. Is such a thing possible without manually redefining everything?

Andrew Ashworth (Feb 23 2019 at 23:42):

You could write a tactic to do it

Andrew Ashworth (Feb 23 2019 at 23:43):

But, why?

Wojciech Nawrocki (Feb 23 2019 at 23:47):

I would like to do this in order to:
a) Avoid type class search by giving it the right instance from the start.
b) Make inference work. I found that making one of my modules more generic (I changed it from using a particular semiring to being generic over arbitrary semirings) broke inference in a lot of places where I use this module because it no longer knows which semiring to pick and I don't want to specify it everywhere manually.
For a more concrete example which is close to what I'm doing, say I define a list inductive slist {α: Type} [semiring α]: Type which is a list the elements of which are also elements of a semiring and then derive a bunch of theorems about the behaviour of such a list under some transformations. I would then like to use all of these theorems only with a particular choice of α, even though the slist file/module is generic.

Kevin Buzzard (Feb 24 2019 at 00:06):

But if you're using {alpha : Type} correctly, then Lean should be able to infer what alpha is from subsequent terms, so you don't need to change it to nat, Lean will just guess it for you and do it itself. Have I missed the point?

Andrew Ashworth (Feb 24 2019 at 00:15):

"no longer knows which semiring to pick" this sounds fishy

Kevin Buzzard (Feb 24 2019 at 00:18):

The {} brackets aren't type class search, they are dealt with using unification.

Wojciech Nawrocki (Feb 24 2019 at 00:23):

You're right, Lean can still unify it and find the class instance - I was perhaps a bit rash there when figuring out what's going on. However, tactic state updates that used to take about 300ms when I was working over a concrete object now take several seconds, making it somewhat painful to work interactively. I was hoping that reinstantiating all the lemmas over the concrete objects again would alleviate this.

Kevin Buzzard (Feb 24 2019 at 00:24):

Again, if you're talking about {}s then this isn't to do with classes, it's something else.

Kevin Buzzard (Feb 24 2019 at 00:24):

But I agree that sometimes things get slow, and it can sometimes be quite difficult to find out why.

Wojciech Nawrocki (Feb 24 2019 at 00:25):

I'm talking about both - the {alpha} variable being unified and [semiring alpha] which needs a class instance.

Kevin Buzzard (Feb 24 2019 at 00:25):

Aah yes, that is a class instance. Sorry.

Kevin Buzzard (Feb 24 2019 at 00:25):

Does it help to actually put the type class instances explicitly into Lean at the top of the file?

Kevin Buzzard (Feb 24 2019 at 00:26):

e.g. you were working with a random alpha assumed to be a semiring, and now you're working with nat, so you could put instance : semiring nat := by apply_instance at the top of the file. However that instance should already be there so I can't imagine it will help :-/

Kevin Buzzard (Feb 24 2019 at 00:27):

oh this can't be the problem. Unless your actual instances are hard to find.

Wojciech Nawrocki (Feb 24 2019 at 00:30):

I couldn't say for sure without benchmarking, but I would guess that the two implicit arguments I added to everything makes it appreciably more difficult to carry out elaboration.

Kevin Buzzard (Feb 24 2019 at 00:30):

I'm afraid you'll have to speak to a computer scientist about this one :-)

Kevin Buzzard (Feb 24 2019 at 00:31):

In maths, everything runs instantly.

Kevin Buzzard (Feb 24 2019 at 00:31):

You should consider moving to the Platonic universe.

Kevin Buzzard (Feb 24 2019 at 00:31):

We don't have engineering troubles there.

Wojciech Nawrocki (Feb 24 2019 at 00:55):

Oh okay, I ran into the problem that made me think unification fails again. In fact, I believe it could eventually succeed, but currently simp * takes so long it times out, while it would work fine before I added the implicit parameters to one of my modules.

Kevin Buzzard (Feb 24 2019 at 01:00):

If you want to try to speed things up yourself, you could take a look at what simp was doing before and after by putting logging on.

Kevin Buzzard (Feb 24 2019 at 01:01):

set_option trace.simplify.rewrite true is what you need to see what simp is doing.

Wojciech Nawrocki (Feb 24 2019 at 23:55):

Does Lean have an equivalent of Haskell's/Agda's where? E.g. def abc := two where two := 2

Mario Carneiro (Feb 25 2019 at 00:06):

no

Mario Carneiro (Feb 25 2019 at 00:06):

it messes with elaboration order

Wojciech Nawrocki (Feb 25 2019 at 00:27):

Could you elaborate on what the issue is?

Mario Carneiro (Feb 25 2019 at 00:43):

Lean does pretty much all elaboration from left to right. If you use a postfix let, then the type and value of the defined variable will not be known when it is needed

Mario Carneiro (Feb 25 2019 at 00:43):

Is this stupid? Yes. I think lean 4 will relax the elaboration order a bit to allow stuff like this to work

Wojciech Nawrocki (Feb 28 2019 at 19:47):

Does anyone know where the extra argument comes from (I do know (n = 0) is probably not valid syntax for a hypothesis)?

def foo_fn := Π (n: ) (n = 0), string

#print foo_fn
/- def foo_fn : Type :=
   ℕ → Π (n : ℕ), n = 0 → string -/
-- ^ what is this?

Chris Hughes (Feb 28 2019 at 19:51):

Do you have a variable somewhere in your file?

Chris Hughes (Feb 28 2019 at 19:52):

Oh no actually. Π (n = 0), _ is shorthand for Π n, n = 0 -> _

Wojciech Nawrocki (Feb 28 2019 at 19:54):

Ah so it is valid syntax. Thanks!

Wojciech Nawrocki (Feb 28 2019 at 22:08):

I would like to define a subtype of Lean functions like so:

@[reducible]
def foo_fn :=   

inductive is_foo: foo_fn  Prop
| Id: is_foo (λ x, x)
| Rec: Π {f: foo_fn}, is_foo f  is_foo (λ x, (f x) + 1)

structure foo :=
(f: foo_fn)
(hf: is_foo f)

Where a foo is a ℕ → ℕ together with a proof that it's a valid kind of foo_fn, i.e. either the identity or some other foo plus 1. Then, I would also like to carry out transformations on the foo structure by modifying the foo_fn and adjusting its proof. Unfortunately the foo_fn is opaque as just a Lean function, so to actually do this it seems I need the following:

inductive foo': foo_fn  Type
| Id: foo' (λ x, x)
| Rec: Π {f: foo_fn}, foo' f  foo' (λ x, (f x) + 1)

My question is, can foo' be considered equivalent to foo in the sense that I can extract f: foo_fn out of foo' f and use it as I would use the f member of the structure foo?

Kevin Buzzard (Feb 28 2019 at 23:42):

I don't understand the question. Are you not happy with this sort of thing:

def foo_fn :=   

inductive is_foo: foo_fn  Prop
| Id: is_foo (λ x, x)
| Rec: Π {f: foo_fn}, is_foo f  is_foo (λ x, (f x) + 1)

structure foo :=
(f: foo_fn)
(hf: is_foo f)

def add_one : foo  foo :=
λ f, hf, ⟨λ x, f x + 1, is_foo.Rec hf

?

Wojciech Nawrocki (Mar 02 2019 at 14:09):

The problem is that I couldn't redefine add_one like this:

def add_one': foo  foo
/- induction tactic failed, recursor 'is_foo.dcases_on' can only eliminate into Prop -/
| f, is_foo.Id := ⟨λ x, f x + 1, is_foo.Rec is_foo.Id
| f, is_foo.Rec hf' := ⟨λ x, f x + 1, is_foo.Rec (is_foo.Rec hf')

And in general any definition which tries to extract an inner foo_fn out of an is_foo constructor will fail, but I need to do that sometimes in order to transform the foo_fn. So far the inductive foo': foo_fn -> Type definition seems to work well though.

Kevin Buzzard (Mar 02 2019 at 14:15):

Aah I see; the recursor for is_foo only eliminates into Prop. Yeah you need an expert, not a mathematician :-)

Chris Hughes (Mar 02 2019 at 14:26):

The reason this isn't possible in general, is that by proof irrelevance is_foo.Id = is_foo.rec _ if the proofs have the same type, so I cannot define functions that treat these two cases differently.

Since both cases are equal in your function, you can write

def add_one': foo  foo
| f, h := ⟨λ x, f x + 1, is_foo.rec_on h (is_foo.Rec is_foo.Id) (λ hf', is_foo.Rec (is_foo.Rec hf'))

Chris Hughes (Mar 02 2019 at 14:28):

Usually this form is preferred however

def add_one' (f : foo) : foo :=
 ⟨λ x, f.1 x + 1, is_foo.rec_on f.2 (is_foo.Rec is_foo.Id) (λ hf', is_foo.Rec (is_foo.Rec hf'))

Chris Hughes (Mar 02 2019 at 14:30):

The second add_one' will definitionally reduce in a nice way when applied to an argument f : foo, wherease the first definition will only reduce when applied to an argument of the form ⟨f, h⟩ : foo. This makes the first definition harder to work with in proofs.

Wojciech Nawrocki (Mar 02 2019 at 15:12):

Thanks!

drocta (Mar 03 2019 at 21:57):

I am trying to say something about free objects, using an existing definition of concrete categories in mathlib.
I couldn't find an existing definition in mathlib for free objects (edit: really, I just need to define free objects over a set or type with one element), so I was going to define them myself.
I have mathlib installed using leanpkg, and it looks like because I have lean version 3.4.1 installed, leanpkg used the branch with that name from the mathlib repository.
However, I notice that the master branch and the 3.4.1 branch have the definition of concrete categories in different places.
Should I go ahead and use the version which is defined in the 3.4.1 branch, and is in the category.lean file, or ought I somehow use the version in the concrete_category.lean version currently in the master branch?

Kevin Buzzard (Mar 03 2019 at 22:56):

The master branch of mathlib doesn't work with Lean 3.4.1.

Kevin Buzzard (Mar 03 2019 at 22:58):

If I were you I'd use 3.4.2 (probably the last release of Lean 3) and mathlib master. If you install elan then it will all work by magic and you won't have to worry.

drocta (Mar 03 2019 at 23:56):

thank you
(edit 7:33 : that worked for me)

drocta (Mar 04 2019 at 02:48):

I'm not sure where to find the documentation on out_param . Could you point me towards it?
the definition in core.lean looks like it is just an identity function, but I assume I'm missing something, seeing as I think the lack of it just caused me to get a type error.

drocta (Mar 04 2019 at 03:50):

found a way to do what I was trying to do which didn't require that, so nvm I guess?

Kevin Buzzard (Mar 04 2019 at 07:03):

out_param is something to do with telling Lean's elaborator what order to do type inference, or something like that. I'm afraid the only documentation that I know of is when I asked for details about it on either this chat or at the old chat, and Mario Carneiro explained it.

Kevin Buzzard (Mar 04 2019 at 07:52):

https://gitter.im/leanprover_public/Lobby?at=5a6e31685a9ebe4f75e77351

drocta (Mar 05 2019 at 02:35):

out_param is something to do with telling Lean's elaborator what order to do type inference, or something like that. I'm afraid the only documentation that I know of is when I asked for details about it on either this chat or at the old chat, and Mario Carneiro explained it.
https://gitter.im/leanprover_public/Lobby?at=5a6e31685a9ebe4f75e77351

Ah! Thank you!

Wojciech Nawrocki (Mar 05 2019 at 14:51):

What is the meaning of this error: rewrite tactic failed, motive is not type correct?
I get it when trying to apply rw [sesh_tp.dual_end_recv] where sesh_tp.dual_end_recv : sesh_tp.dual End? = End! to the following state:

 1(sesh_tp.dual End?)::?m_1 = matrix.identity ((sesh_tp.dual End?) :: γ) End! (ZVar γ End!)

I thought that rw should be able to simply replace the instances of sesh_tp.dual End?.
(End? and End! are constructors for the sesh_tp inductive type.)

Patrick Massot (Mar 05 2019 at 15:39):

We need an emoji for this error

Patrick Massot (Mar 05 2019 at 15:41):

It means that doing the rewrite will give you an expression which doesn't type check, presumably because you had a dependent type and rewrote the parameter. The classical example is:

set_option pp.notation false
set_option pp.implicit true

example (n m : ) (a : fin (n + m)) (b : fin (n + m)) (h : a = b) : true :=
begin
  rw add_comm at h,

  sorry
end

Patrick Massot (Mar 05 2019 at 15:41):

Try understanding this simple example and then your complicated one

Wojciech Nawrocki (Mar 05 2019 at 16:04):

Oh right, I get the issue, thanks! But if you rw add_comm both sides of the equation, it should still type check, no? I managed to solve my problem by explicitly stating the type of the goal with both instances of sesh_tp.dual End? rewritten to End!. Could rw not check the type after doing both rewrites?

Kevin Buzzard (Mar 05 2019 at 17:52):

rw is what it is. If you want more you can try lots of tricks, e.g. erw or simp only or using conv mode -- we have lots of workarounds :-) But this is dependent type theory, random rewriting can create terms which are not type correct and Lean pulls the plug when this happens.

Plam (Mar 05 2019 at 18:58):

Is there a way to make use of the fact that pattern matches in a theorem have the same structure as in a definition? In particular, that given a catchall pattern match later in the theorem, it can't be the case that the thing being matched against matches one of the earlier pattern matches?

Concretely, when proving simp_const_eq below, I want to match on plus (const a) (const b), times (const a) (const b) and then a catchall. Is this possible, or do I just need some extra cases that I can rfl my way through?

inductive aexpr : Type
| const :   aexpr
| var :   aexpr
| plus : aexpr  aexpr  aexpr
| times : aexpr  aexpr  aexpr

open aexpr

def aeval (v :   ) : aexpr  
| (const n) := n
| (var n) := v n
| (plus e₁ e₂) := (aeval e₁ + aeval e₂)
| (times e₁ e₂) := aeval e₁ * aeval e₂

def simp_const : aexpr  aexpr
| (plus (const n₁) (const n₂)) := const (n₁ + n₂)
| (times (const n₁) (const n₂)) := const (n₁ * n₂)
| e := e

theorem simp_const_eq (v :   ) :
   e : aexpr, aeval v (simp_const e) = aeval v e :=
sorry

Wojciech Nawrocki (Mar 18 2019 at 01:48):

While I don't think this is possible right now (correct me if I'm wrong), would it in principle be possible to extend Lean with support for custom type unification procedures? The way I defined some inductive families requires me to do a lot of conversions from terms of type T a to a type T b that Lean expects, where a = b. All of these are resolvable more or less with a single tactic, and it would be awesome if I could just teach Lean a heuristic like "If type U is expected and a term of type T is given, see if T or U (or both) contain a specific expression. If so, try to use this tactic to convert T to U".

Johan Commelin (Mar 18 2019 at 07:15):

Search the chat for "unification hints"

Johan Commelin (Mar 18 2019 at 07:15):

I'm hoping that might be exactly the thing you want. Note however that we don't have much experience with them. Unification hints haven't been used outside of a demo, as far as I am aware.

Mario Carneiro (Mar 18 2019 at 07:34):

If by a = b you mean you have a proof that they are equal, not that they are defeq, then unification hints won't help, and a lot of things will get more complicated

Wojciech Nawrocki (Mar 18 2019 at 12:38):

@Mario Carneiro indeed by a = b I mean eq a b. What I end up having in the actual terms is a bunch of eq.mprs which do the conversions from T a to T b. My idea here was, could I not just tell Lean to try to insert those conversions automatically when it fails to unify types?

Wojciech Nawrocki (Mar 18 2019 at 12:41):

@Johan Commelin I was looking into those but it seems they are not powerful enough to modify the term in question which is what seems to be needed when the types are not algorithmically definitonally equal according to Lean. Moreover, some of the problems that Coq resolves using Canonical Structures, which seem to be a type of unification hint, Lean resolves using type class inference.

Kevin Buzzard (Mar 18 2019 at 13:04):

There was some discussion about these issues at Lean Together in January. Note that Lean 4 will apparently be removing unification hints, perhaps because nobody uses them in Lean.

Kevin Buzzard (Mar 18 2019 at 13:04):

The speakers who talked about it were Assia Mahboubi and Cyril Cohen, and I am pretty sure their slides are online and linked to here somewhere.

Johan Commelin (Mar 18 2019 at 13:05):

See:
https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/unification.20hints/near/158613068

Jesse Michael Han (Mar 18 2019 at 14:01):

We also ran into this issue in Flypitch. I found it helpful to have a library of simp lemmas to normalize the location of the casts. I also had the occasional odd experience of being unable to prove an instance of a cast lemma by refl inside a larger proof, but was able to prove that lemma by refl separately, mark it as simp, and then simp would work...

Patrick Massot (Mar 18 2019 at 16:18):

Note that Lean 4 will apparently be removing unification hints, perhaps because nobody uses them in Lean.

Is this really clear? I thought that Assia and Cyril provided ample proofs that this would be a tragic mistake. I understand they were removed at the beginning of Lean 4, but many things were removed, and some of them will return at some point. Is there any update on this @Sebastian Ullrich?

Sebastian Ullrich (Mar 18 2019 at 16:21):

no

Wojciech Nawrocki (Mar 23 2019 at 18:30):

Why does auto_param seem to not work as an implicit argument? I'd like to have a constructor like foo {h: auto_param some_prop ``mytac}: Foo and match on it like | foo rather than | (foo _), but if I put the auto_param in curly brackets, it seems to not actually run the tactic when using def abc := foo - it fails with "failed to synthesize placeholder" rather than trying to run mytac.

drocta (Mar 25 2019 at 03:36):

I want the unique function from the type empty to another type. I thought I might be able to do that with empty.rec or empty.rec_on , (going off of the tutorial's use of that with days of the week), but it looks like those both expect to be given a function from empty as an argument, when that is what I'm trying to get.
What am I missing?

Johan Commelin (Mar 25 2019 at 05:38):

I think this is what you want:

src/logic/basic.lean:def empty.elim {C : Sort*} : empty  C.

Wojciech Nawrocki (Mar 27 2019 at 00:51):

congr gave me the following goal:

γ : precontext,
Γ : context γ,
M : @term γ Γ End?
 @Wait γ Γ M == @eq.mpr (@term γ (Γ + 0) tp.unit) (@term γ Γ tp.unit) _ (@Wait γ Γ M)

Having never worked with heq, I'm not sure if this is provable? Clearly both sides are the same expression, but one has been passed through eq.mpr to cast its type. If it's provable, what would be a good tactic/term to solve it? EDIT: It's probably worth noting that it has been casted to the wrong type, i.e. RHS has the same type as LHS before the cast, but a different one after. Can I undo eq.mpr in here somehow?

Wojciech Nawrocki (Mar 27 2019 at 01:25):

Ah h_generalize did it. I would still be interested in hearing if there's a more automated way of doing it.

Jesse Michael Han (Mar 27 2019 at 01:30):

cc can sometimes handle heqs of casts which are close to being definitional equalities

Jesse Michael Han (Mar 27 2019 at 01:30):

however, it works better when eq.mprs are hidden beneath casts

Wojciech Nawrocki (Mar 27 2019 at 01:35):

What's the difference between cast and eq.mpr?

Kenny Lau (Mar 27 2019 at 01:37):

@[inline]
def cast : Π {α β : Sort u}, α = β  α  β :=
λ {α β : Sort u} (h : α = β) (a : α), eq.rec a h

@[inline]
def eq.mpr : Π {α β : Sort u}, α = β  β  α :=
λ {α β : Sort u} (h₁ : α = β) (h₂ : β), eq.rec_on _ h₂

Wojciech Nawrocki (Mar 27 2019 at 01:40):

Oh, it's just reversed? Okay then :)

Kenny Lau (Mar 27 2019 at 01:41):

@[inline]
def eq.mp : Π {α β : Sort u}, α = β  α  β :=
λ {α β : Sort u}, eq.rec_on

Kevin Buzzard (Mar 27 2019 at 07:40):

There are people here who would say that if you're dealing with heqs, you're doing it wrong

Kevin Buzzard (Mar 27 2019 at 07:40):

Equality of types is evil in type theory

Kevin Buzzard (Mar 27 2019 at 07:41):

And maybe you'd be better off making an equiv if you have two types which you have identified in your mind

Kevin Buzzard (Mar 27 2019 at 07:41):

Many of the key facts about equality are also proved for equivs

Kevin Buzzard (Mar 27 2019 at 07:41):

The key missing one is rw

Kevin Buzzard (Mar 27 2019 at 07:42):

But we're working on it

Wojciech Nawrocki (Mar 27 2019 at 13:30):

Yeah, I (ab)use dependent types in a way that makes it necessary to prove type equality quite often. I try to give my functions built-in & semi-automated support for this by adding arguments like (h: auto_param (expected_T = actual_T) ``some_tac), but it's not always possible

Wojciech Nawrocki (Mar 27 2019 at 14:48):

While I'm at it, how can I rw using a heq? Given Hx: M == x, rw [Hx] tells me that lemma is not an equality nor an iff.

Chris Hughes (Mar 27 2019 at 14:49):

With great difficulty. You can try using heq.rec_on, but I think this is usually quite hard, because the resulting expression often won't type check.

Simon Hudon (Mar 27 2019 at 15:28):

This is a lesson you have to learn eventually: just because you can write it this way and that it is type correct doesn't mean it's a good idea. Keep the type of your definitions as simple as possible. Move your added logic to separate lemmas or separate definitions.

Jesse Michael Han (Mar 27 2019 at 15:47):

heq.subst will let you simulate some rewriting, but as Chris said, it is very painful

Kevin Buzzard (Mar 27 2019 at 20:15):

If you know you are abusing dependent types then maybe you are bringing the pain on yourself. I had a maths repo where I used equality of types, because I was learning type theory and didn't understand the difference between equality and definitional equality; I ran into real trouble, which was only fixed by a refactor. What I am pushing for now is a tactic which will rewrite appropriate terms along equivs (or however you say it: if P is a predicate on rings which is mathematically reasonable then for two isomorphic rings R and S, P R will be true iff P S is, and I want a tactic which will show this -- this is something mathematicians do all the time but which seems to be painful in type theory).

Wojciech Nawrocki (Mar 27 2019 at 22:27):

@Kevin Buzzard Well, kind of, but not necessarily. I'm still a newcomer to type theory/theorem proving, but from what I read and some discussions I've had, my understanding is that currently Coq can handle these kinds of things much better than Lean. Adding good support for working with dependent types which are only propositionally equal could be well worth the effort. In particular, Coq has a Program framework (which I think I've mentioned here at some point) which is able to find which eqs need to be proven while constructing an expression and generate them as external goals. This way we get a clean expression and all the nasty details are resolved externally. After the goals are proven, Program seems to automatically insert casts, more or less. Moreover, Coq can be told which tactics to use in order to automatically resolve most of such side equality goals. In PVS, I believe these things are called "Type Correctness Conditions" and are also dealt with outside the expression. HoTT also seems to provide an interesting answer to this in the form of paths, but I've only started looking into that.

Andrew Ashworth (Mar 28 2019 at 05:56):

Is Program ready for prime time? I only read about it in CPDT, Chlipala is not big on it. http://adam.chlipala.net/cpdt/html/Subset.html

Wojciech Nawrocki (Mar 29 2019 at 16:21):

In Coq, JMeq_eq is an axiom, but in Lean eq_of_heq seems to be a lemma with no non-core axioms below it. Does that mean it follows from proof irrelevance or another axiom that Lean has built-in?

Mario Carneiro (Mar 29 2019 at 17:56):

@Wojciech Nawrocki Yes, it relies on proof irrelevance.

lemma eq_of_heq {α : Sort u} {a a' : α} (h : a == a') : a = a' :=
have  (α' : Sort u) (a' : α') (h₁ : @heq α a α' a') (h₂ : α = α'), (eq.rec_on h₂ a : α') = a', from
  λ (α' : Sort u) (a' : α') (h₁ : @heq α a α' a'), heq.rec_on h₁ (λ h₂ : α = α, rfl),
show (eq.rec_on (eq.refl α) a : α) = a', from
  this α a' h (eq.refl α)

the rfl on line 3 has type eq.rec_on h₂ a = a, which only makes sense because h₂ : α = α is defeq to rfl by proof irrelevance

Wojciech Nawrocki (Mar 29 2019 at 18:16):

Gotcha, thanks!

Wojciech Nawrocki (Mar 31 2019 at 18:16):

How can I introduce a new constant into all goals using ;? sometac; have a := 2, doesn't seem to work. EDIT: all_goals { have a := 2 } worked. Unsure why ; doesn't.

Chris Hughes (Mar 31 2019 at 18:20):

This worked for me

example : false  false :=
begin
  split; have a := 2,
end

However, this didn't work

example : false  false :=
begin
  split, skip; have a := 2,
end

Maybe the semicolon only works for the goals generated by the last tactic.

Rob Lewis (Mar 31 2019 at 18:20):

sometac; have a := 2 works. Remember have is different from let. You'll get a : ℕ in the context, but won't see that it's 2.
Edit: oh, I see the confusion. ; doesn't mean "do the second tactic to all goals." I think t; s means "do t to the first goal, and then do s to all remaining goals."
Edit 2: I'm thinking about too many things at once and writing unclearly, sorry. Chris phrased what I meant better. t; s focuses on the first goal, applies t, and then applies s to all goals generated from that. "Remaining goals" meaning remaining in the focus.

Wojciech Nawrocki (Mar 31 2019 at 18:24):

Double-checked, it acts wierdly:

cases h: H; have a := 2, -- works
cases h: H, skip; have a := 2, -- doesn't work
cases h: H, case Foo { .. }; have a := 2, -- doesn't work

Reid Barton (Mar 31 2019 at 18:33):

What Chris said. x; y applies y to each goal produced by x.

Wojciech Nawrocki (Mar 31 2019 at 18:34):

Oooh, ok! Thanks

Wojciech Nawrocki (Mar 31 2019 at 18:41):

Different question - in every goal I have a hypothesis of the form h : hE' = Foo Γₑ' hΓ_1 N E_1 a, where Foo is that goal's case. I would like to solve each goal by applying that goal/case's constructor, but with different arguments. Can I somehow "extract" which constructor was used for the case and reuse it to solve the goal without quoting expressions and all that?

Wojciech Nawrocki (Apr 01 2019 at 14:09):

How can I debug a (deterministic) timeout when Lean tries to prove well-foundedness of a recursion given my custom tactic?

Wojciech Nawrocki (Apr 01 2019 at 14:13):

Oh actually I also get a "failed to prove well-foundedness" with the state:

h : hE' = EAppLeft Γₑ' hΓ_1 N E_1 a,
Γ : context γ,
M : term Γ A''
 eval_ctx.sizeof
      (((γ,
          Γₑ,
           Γ₁,
            A'',
             A',
              A_1A, E, hE, E_1, a, Γₑ + Γ₁, _⟩⟩⟩⟩⟩⟩⟩⟩⟩⟩⟩.snd).snd).fst)
      (((((((((γ,
                Γₑ,
                 Γ₁,
                  A'',
                   A',
                    A_1A,
                     E,
                      hE,
                       E_1,
                        a,
                         Γₑ + Γ₁,
                          _⟩⟩⟩⟩⟩⟩⟩⟩⟩⟩⟩.snd).snd).snd).snd).snd).snd).snd).snd).fst)
      a <
    eval_ctx.sizeof
      (((γ,
          Γₑ,
           Γₑ',
            A'', A', A, E, hE, E', hE', Γ, hΓ⟩⟩⟩⟩⟩⟩⟩⟩⟩⟩⟩.snd).snd).fst)
      (((((((((γ,
                Γₑ,
                 Γₑ',
                  A'',
                   A',
                    A,
                     E,
                      hE,
                       E',
                        hE', Γ, hΓ⟩⟩⟩⟩⟩⟩⟩⟩⟩⟩⟩.snd).snd).snd).snd).snd).snd).snd).snd).fst)
      hE'

Since h: hE' = EAppLeft ... a is in the context, why can't Lean just rewrite it using that and then show that sizeof a < sizeof EAppLeft .. a?

Andrew Ashworth (Apr 01 2019 at 14:47):

this error log is pretty hard to understand just by looking at it

Andrew Ashworth (Apr 01 2019 at 14:48):

what always gets linked when these things come up: TPIL and https://github.com/leanprover-community/mathlib/blob/master/docs/extras/well_founded_recursion.md

Andrew Ashworth (Apr 01 2019 at 14:55):

what is not mentioned in the mathlib doc is what I usually do when I don't want to stare at a bunch of errors: prove my relation is well-founded by hand well_founded r and directly use well_founded.fix

Wojciech Nawrocki (Apr 01 2019 at 15:01):

Oh yeah I'd read that, but was hoping Lean could employ a bit of automation when trying to prove the inequality. I'll try fix as I don't think I can use have( the fn definition is entirely in tactic mode), thanks.

Andrew Ashworth (Apr 01 2019 at 15:04):

oh, don't forget http://adam.chlipala.net/cpdt/html/GeneralRec.html

Andrew Ashworth (Apr 01 2019 at 15:04):

the definitions are sorta almost the same if you squint

snowbunting (Apr 02 2019 at 09:12):

What would be the correct way to define a function from ℤ to (abelian) groups? (i.e. having indexed groups G_1, G_2, ...)

I don't think that this here is working as it should. This would be a constant function, wouldn't it?

universes u
variables {α: Type u}
def my_indexes_groups (i:ℤ) := add_group α
#check my_indexes_groups 3

Thx :)

Johan Commelin (Apr 02 2019 at 09:12):

No, because then all groups have the same cardinality

Kevin Buzzard (Apr 02 2019 at 09:12):

You define a function from Z to Type

Johan Commelin (Apr 02 2019 at 09:12):

What you do is G : Z → Type

Johan Commelin (Apr 02 2019 at 09:13):

And then [\for i, add_group G i]

Kevin Buzzard (Apr 02 2019 at 09:13):

And then you define another function from Z to the structure of a group on the image of the first function :-)

Kevin Buzzard (Apr 02 2019 at 09:14):

And you also note that Lean's definition of add_group doesn't require commutativity so if you're doing exact sequences you might want to use add_comm_group

Kevin Buzzard (Apr 02 2019 at 09:15):

Johan and I are both interested in seeing exact sequences in Lean by the way, we're both mathematicians

Kevin Buzzard (Apr 02 2019 at 09:16):

The way to think about groups in Lean is that they are two things. First you have G, a type

Kevin Buzzard (Apr 02 2019 at 09:16):

And then you have another thing h, a term of type [group G]

Kevin Buzzard (Apr 02 2019 at 09:17):

And it's h that has all the data of the multiplication and the axioms

Kevin Buzzard (Apr 02 2019 at 09:17):

But amazingly you never have to mention h at all when you're working with groups

Kevin Buzzard (Apr 02 2019 at 09:18):

Because whenever Lean needs this data it gets it from G using something called type class inference

Kevin Buzzard (Apr 02 2019 at 09:18):

You try to do a group thing on G like multiplying two elements together

snowbunting (Apr 02 2019 at 09:19):

ok that works indeed! Maybe it would be actually ask to ask what the difference between Type and Type u is, although it feels super dumb...

Kevin Buzzard (Apr 02 2019 at 09:19):

And Lean says "what's this multiplication they're talking about? Does my big list of type class stuff contain anything which mentions a multiplication on G? And then it looks and it finds h and says "ok I'll just use that stuff"

Kevin Buzzard (Apr 02 2019 at 09:20):

Type is Type 0

Kevin Buzzard (Apr 02 2019 at 09:20):

In type theory everything has to have a type

Kevin Buzzard (Apr 02 2019 at 09:20):

So unfortunately Type has to have a type

Kevin Buzzard (Apr 02 2019 at 09:20):

Which is Type 1

Kevin Buzzard (Apr 02 2019 at 09:20):

You get the picture

Kevin Buzzard (Apr 02 2019 at 09:21):

Type u is a random universe somewhere up in the hierarchy

Kevin Buzzard (Apr 02 2019 at 09:21):

It's best practice to make functions and constructions take inputs from arbitrary universes in type theory

Kevin Buzzard (Apr 02 2019 at 09:22):

You should do a map from Z to Type u really

Kevin Buzzard (Apr 02 2019 at 09:23):

People might also write Type* -- this is the same thing, it just saves you having to name u

snowbunting (Apr 02 2019 at 09:28):

ok so u really is just a number, like Type u one of Type 0, Type 1, ...
And if I don't want to force two variables to be both for example Type 2 simultaneously, then I should use Type u and Type v in the definition, right?

And if I would write def xxx (a: Type*) (b: Type*): Type* := sorry then there is no constrains if they have the same type or not, is there?

Thx :)

Kevin Buzzard (Apr 02 2019 at 09:28):

That's right.

Kevin Buzzard (Apr 02 2019 at 09:29):

u is a natural number, but it's not a Lean natural number, it's a universe variable, which is treated differently; it's part of the core language or something.

snowbunting (Apr 02 2019 at 14:53):

And then [\for i, add_group G]

This works great together with variable, but how would I now include that into a class? This does not seem to be working:

universe u
class chain (α: Type u) :=
    (group (i:): Type u)
    (diff {i:}: (group i)  (group (i-1)))
    [x: i, add_comm_group (group i)]

variable (C: Type)
variable [chain C]
variables (g h: chain.group C 1)

#check g
#check chain.group C 1
#check chain.diff g
#check g + h    -- has_add (chain.group C 1)

Johan Commelin (Apr 02 2019 at 14:54):

Put it with () in the class. Then aftwerwards state:

instance foo (i : ) : add_group C i := C.x i

Johan Commelin (Apr 02 2019 at 14:54):

Modulo mistakes

Johan Commelin (Apr 02 2019 at 14:56):

Also, you might be interested in category_theory/

snowbunting (Apr 02 2019 at 14:56):

Of course, I should have remembered instance, used that before, thank you!

Kevin Buzzard (Apr 02 2019 at 17:59):

Your alpha doesn't do anything

Kevin Buzzard (Apr 02 2019 at 17:59):

You could just remove it, and you could change class to structure

Kevin Buzzard (Apr 02 2019 at 18:01):

Then a term of type chain would be a bunch of abelian groups and homs

Kevin Buzzard (Apr 02 2019 at 18:01):

You use class when there's only expected to be one term of that type

Scott Morrison (Apr 02 2019 at 22:05):

Also, you might be interested in category_theory/

Regarding initial experiments, this isn't meant at all as discouragement --- but eventually we'd like the definition of chain_complex to be as generic as possible (e.g. any category enriched in a category with_zero).

Wojciech Nawrocki (Apr 05 2019 at 20:42):

Is there anything I can do to debug "equation compiler failed to prove equation lemma (workaround: disable lemma generation using set_option eqn_compiler.lemmas false)"?

Kevin Buzzard (Apr 05 2019 at 21:10):

Wow that's some error! How did you get that?

Patrick Massot (Apr 05 2019 at 21:11):

Kevin is so jalous...

Wojciech Nawrocki (Apr 05 2019 at 22:24):

:) It happens in this definition (link)

Kevin Buzzard (Apr 05 2019 at 22:31):

it doesn't compile for me because it needs imports I don't have :-/

Kevin Buzzard (Apr 05 2019 at 22:31):

but even if it did compile for me I think I wouldn't be able to help.

Wojciech Nawrocki (Apr 05 2019 at 23:04):

Oh, it's self-contained modulo downloading mathlib, but for some reason leanpkg.path was in my .gitignore and it's needed for import paths to resolve - fixed that

Reid Barton (Apr 05 2019 at 23:31):

leanpkg.path should indeed be in .gitignore (assuming you are actually using leanpkg)

Mario Carneiro (Apr 06 2019 at 02:15):

Is it related to the use of let in the type of the function you are defining by pattern matching?

Wojciech Nawrocki (Apr 06 2019 at 21:09):

@Mario Carneiro it doesn't seem like it, I can inline the let, same error

Mario Carneiro (Apr 06 2019 at 21:10):

do you have a self contained MWE?

Mario Carneiro (Apr 06 2019 at 21:10):

(Try putting everything needed in one file, then start deleting things that don't matter for the error)

Wojciech Nawrocki (Apr 09 2019 at 19:57):

@Mario Carneiro thanks, that was good idea! It took a while but I was able to simplify it to this:

inductive foo:     Type
def foo_fn (n m: ): Type := foo n m  foo n m

inductive is_foo_fn
  : Π {n m: }, foo_fn n m  Type
| IsFooEta:
  Π {n m: } {f: foo_fn n m},
  is_foo_fn f
 is_foo_fn (λ M, f M)
open is_foo_fn

def ext: -- equation compiler failed to prove equation lemma (workaround: disable lemma generation using `set_option eqn_compiler.lemmas false`)
    Π {m n: }
      {f: foo_fn n m},
    is_foo_fn f
   Σ f': foo_fn n m, is_foo_fn f'
| _ _ _ (IsFooEta f) :=
  ⟨_, IsFooEta (ext f).snd

which still fails, while making the following change makes the eqn compiler work:

def ext: -- good
    Π {n m: }
      {f: foo_fn n m},
    is_foo_fn f
   Σ f': foo_fn n m, is_foo_fn f'
| _ _ _ (IsFooEta f) :=
  ⟨_, IsFooEta (ext f).snd

Can you spot the difference? :) (it's the order of arguments). To me this seems like an eqn_compiler bug
And indeed this stupid-simple fix works on my actual code :D

Wojciech Nawrocki (Apr 09 2019 at 20:30):

@Sebastian Ullrich What do you think? Is this a bug or am I missing something obvious?

Kevin Buzzard (Apr 09 2019 at 20:31):

Thanks for putting in the effort. I've never seen this error before.

Sebastian Ullrich (Apr 09 2019 at 20:34):

Me neither

Patrick Massot (Apr 09 2019 at 20:34):

Can you reproduce it in Lean 4?

Patrick Massot (Apr 09 2019 at 20:35):

If not, then who cares about this error?

Kevin Buzzard (Apr 09 2019 at 20:39):

those of us who are stuck with Lean 3?

Kevin Buzzard (Apr 09 2019 at 20:39):

;-)

Andrew Ashworth (Apr 09 2019 at 22:42):

I don't have time to look into this deeply, but just remarking that I have seen 'equation compiler failed to prove equation lemma' before

Andrew Ashworth (Apr 09 2019 at 22:42):

a very long time ago, I think the last time I saw it there was some problem with eta reduction

Andrew Ashworth (Apr 09 2019 at 22:43):

but i could be hallucinating how I fixed it, since it was awhile ago

Kevin Kappelmann (Apr 18 2019 at 09:51):

Given

def P (n: ) : Prop := n > 42

example (n: ) : P n = (P (nat.succ n)) := -- this is obviously false
begin
unfold P, -- this unfolds both occurrences
sorry
end

how can I unfold P just at the right-hand side?

Kevin Buzzard (Apr 18 2019 at 09:52):

you can just use change or show

Kevin Buzzard (Apr 18 2019 at 09:53):

def P (n: ) : Prop := n > 42

example (n: ) : P n = (P (nat.succ n)) := -- this is obviously false
begin
  show P n = ((nat.succ n) > 42),
sorry
end

Kevin Buzzard (Apr 18 2019 at 09:54):

If you're planning on using the rw tactic, then Lean might be very fussy about exactly which form a term is in

Kevin Buzzard (Apr 18 2019 at 09:55):

but conversely Lean is happy to switch between definitionally equal forms of the same term, using the change and show tactics (which do the same thing, change having the advantage that it also works for hypotheses in your context with change ... at H)

Kevin Kappelmann (Apr 18 2019 at 09:55):

Ah okay, thanks. If my goal statement is very long though, I end up copy-pasting a lot of text.

Kevin Buzzard (Apr 18 2019 at 09:56):

there are other ways

Kevin Buzzard (Apr 18 2019 at 09:57):

def P (n: ) : Prop := n > 42

example (n: ) : P n = (P (nat.succ n)) := -- this is obviously false
begin
  conv begin
    to_rhs,
    simp only [P],
  end,
sorry
end

Kevin Buzzard (Apr 18 2019 at 09:57):

conv mode is a mode which isn't mentioned at all in the official docs

Kevin Buzzard (Apr 18 2019 at 09:57):

It enables you to zoom into parts of terms

Kevin Buzzard (Apr 18 2019 at 09:58):

Unfortunately, when you're in conv mode, the tools available to you are completely different, and it doesn't look like unfold is available yet.

Kevin Buzzard (Apr 18 2019 at 09:58):

So I had to use simp only

Kevin Buzzard (Apr 18 2019 at 09:58):

which is close to the same thing

Kevin Kappelmann (Apr 18 2019 at 09:58):

uuh, the hidden secrets. that does not seem very elegant to me though :')

Kevin Buzzard (Apr 18 2019 at 09:58):

The community documented conv mode.

Kevin Kappelmann (Apr 18 2019 at 09:58):

I though I could make use of some pattern matching or occurrence counting when I was reading https://leanprover.github.io/tutorial/A1_Quick_Reference.html A1.6.2.1

Kevin Kappelmann (Apr 18 2019 at 09:59):

But I am either to silly or misinterpreting what is written there.

Kevin Buzzard (Apr 18 2019 at 09:59):

https://github.com/leanprover-community/mathlib/blob/master/docs/extras/conv.md

Kevin Buzzard (Apr 18 2019 at 10:00):

Your link there is to an out of date file :-/ That's Lean 2 you're looking at

Kevin Buzzard (Apr 18 2019 at 10:00):

You can use conv to zoom directly into the right hand side with conv in or conv at or something, I can never remember the details, but they're clearly documented in Patrick's conv.md write-up

Kevin Kappelmann (Apr 18 2019 at 10:01):

Oh damn, google fooled me

Kevin Buzzard (Apr 18 2019 at 10:01):

That way you avoid the to_rhs conv-mode tactic

Kevin Buzzard (Apr 18 2019 at 10:01):

Oh damn, google fooled me

call them up and complain

Kevin Buzzard (Apr 18 2019 at 10:02):

example (n: ) : P n = (P (nat.succ n)) := -- this is obviously false
begin
  conv in (P (nat.succ n)) begin
    simp only [P],
  end,
sorry
end

Kevin Buzzard (Apr 18 2019 at 10:03):

def P (n: ) : Prop := n > 42

example (n: ) : P n = (P (nat.succ n)) := -- this is obviously false
begin
  rw (show P (nat.succ n) = (nat.succ n > 42), from rfl),
sorry
end

Kevin Buzzard (Apr 18 2019 at 10:03):

This way avoids conv mode and is more precise about what you want done

Kevin Buzzard (Apr 18 2019 at 10:04):

What exactly are you looking for? Tactic-writers here are very good.

Kevin Buzzard (Apr 18 2019 at 10:04):

People can just make new stuff. Lean tactics are written in Lean and some people (not me though! I'm a mathematician) can just knock them off before breakfast.

Kevin Kappelmann (Apr 18 2019 at 10:11):

Basically, I had a rather long equation of the form l=r and I wanted to unfold an expression in r by its definition, which again, is a rather long term. Hence, I neither want to re-state the whole goal nor the result of the unfolded definition. What I want to do is something like

begin
unfold P {2} -- this should unfold the second occurrence of P in the goal (or the third if you count from zero)
sorry
end

Kevin Buzzard (Apr 18 2019 at 10:12):

@Gabriel Ebner is that already a tactic, do you know?

Kevin Buzzard (Apr 18 2019 at 10:12):

Is there any obstruction to making it?

Kevin Buzzard (Apr 18 2019 at 10:14):

@Kevin Kappelmann you could just do all the unfolds manually, ignore what happens to l, and then once you've got r into the form you want you can just delet everything and write show _ = [what you want r to be]

Kevin Buzzard (Apr 18 2019 at 10:14):

(the point being that show will take _ for the left hand side)

Kevin Buzzard (Apr 18 2019 at 10:16):

I think I've now told you all the tricks I know -- but I can see that your proposed idea is nicer. It might be a pain though -- if you want to unfold a bunch of stuff in r but not in l you might have to keep changing the numbers, depending on which things you want to unfold in r also show up in l. I think that if you're adamant that you want to change r and leave l untouched, just use conv mode and to_rhs. That's only a couple of lines and I think it's better than your idea because of the issue with numbers perhaps changing depending on exactly you want to unfold.

Gabriel Ebner (Apr 18 2019 at 10:17):

Yes, you can do this with conv. Unfortunately, unfold isn't wrapped so you need to use simp.

conv { for (P _) [2] { simp [P] } },

Kevin Buzzard (Apr 18 2019 at 10:17):

OTOH I guess your idea has its merits when there is more than one occurrence of P in the RHS.

Kevin Buzzard (Apr 18 2019 at 10:17):

Aah, I'd forgotten conv could take numbers!

Kevin Buzzard (Apr 18 2019 at 10:17):

Thanks Gabriel

Kevin Buzzard (Apr 18 2019 at 10:18):

hey what is this voodoo? for? I thought functional languages didn't have for loops!

Kevin Buzzard (Apr 18 2019 at 10:20):

Aah yes, I see now that for is documented in the conv document I linked to earlier.

Kevin Buzzard (Apr 18 2019 at 10:20):

It's about time I re-read it :D

Kevin Buzzard (Apr 18 2019 at 10:22):

I'll show you a Lean trick Kevin.

def P (n: ) : Prop := n > 42

#print prefix P

-- P.equations._eqn_1 : ∀ (n : ℕ), P n = (n > 42)

When you define P, Lean makes one or more "equation lemmas" for it. unfold P is basically the same as simp only [P.equations._eqn_1] or simp only [P] for short. It's a shame there's no unfold in conv mode, but these are the tricks you can use to emulate it.

Kevin Buzzard (Apr 18 2019 at 10:23):

def P (n: ) : Prop := n > 42

example (n: ) : P n = (P (nat.succ n)) := -- this is obviously false
begin
  rw P.equations._eqn_1 (nat.succ n),
  sorry
end

Kevin Kappelmann (Apr 18 2019 at 10:23):

Okay, I summarise: conv is pretty useful, I should read about it, and I guess it should also be included in the official docs at some point to avoid noobs like me wondering how to rewrite subterms :p

Kevin Buzzard (Apr 18 2019 at 10:24):

The Lean team is currently working on Lean 4, I don't think they're interested in documentation-writing at the minute.

Kevin Buzzard (Apr 18 2019 at 10:24):

If Lean 4 is a success then we'll all be porting the docs over to there

Kevin Buzzard (Apr 18 2019 at 10:25):

The community-written docs are really helpful for technical questions not covered by Theorem Proving In Lean.

Kevin Buzzard (Apr 18 2019 at 10:25):

They all live in the mathlib repo but they're not really anything to do with mathlib, they're just where these community-generated docs ended up.

Kevin Kappelmann (Apr 18 2019 at 10:29):

I see. I think rewriting subterms is quite common though, so I would mention it at least in the official doc. Anyway, thanks Kevin & Gabriel :)

Jesse Michael Han (Apr 18 2019 at 12:52):

you can use underscores with change and Lean will try to infer what should be there from the current goal. that way change _ = new_rhs lets you avoid copy-pasting the entire left hand side

Greg Conneen (Apr 21 2019 at 17:24):

How would I go about proving ∀ x : ℤ, even x ∨ odd x? I have even and odd as
def even : ℤ → Prop := λ n, ∃ m : ℤ, 2*m = n
def odd : ℤ → Prop := λ n, ∃ m : ℤ, 2*m + 1 = n
I assume that I would first have to show that odd is equivalent to not even, then apply classical.em. However, I'm having trouble proving odd x ↔ ¬(even x)

Kevin Buzzard (Apr 21 2019 at 17:28):

My instinct would be to prove even n -> odd (n+1) etc, and then prove by induction that every integer was odd or even.

Kevin Buzzard (Apr 21 2019 at 17:28):

There are induction hypotheses of the form (P 0 and (forall n, P n -> P(n+1)) and (forall N, P(n)->P(n-1))) implies P(n) for all n

Greg Conneen (Apr 21 2019 at 17:29):

Ah, I see. That makes sense. I'm not familiar enough with induction in Lean yet, but I'll try it out

Greg Conneen (Apr 21 2019 at 17:29):

thank you, I'll respond again if I'm stuck on the inductive bit

Kevin Buzzard (Apr 21 2019 at 17:29):

If you just apply the induction tactic out of the box, you'll get an unpleasant surprise

Kevin Buzzard (Apr 21 2019 at 17:30):

You'll end up with some question about natural numbers instead, and it will be pain converting from naturals to integers.

Kevin Buzzard (Apr 21 2019 at 17:30):

You'll need to find an appropriate custom induction term in the library.

Greg Conneen (Apr 21 2019 at 17:31):

Oh, okay. I'm not familiar enough with the library to get started with that. Where would I go to look for something like that?

Kevin Buzzard (Apr 21 2019 at 17:31):

int.induction_on should be fine for you.

Kevin Buzzard (Apr 21 2019 at 17:33):

import data.int.basic at the top of your file (you'll need the maths library mathlib) and then apply that function, and you'll be left with goals of the form "n even or n odd -> n+1 even or n+1 odd" which you should be able to cope with. Prove n even -> n+1 odd and the other three results (e.g. n odd -> n-1 even) first and then use them all to prove the result.

Greg Conneen (Apr 21 2019 at 17:34):

Neat. I have mathlib, but I find most of the algebra/analysis files unreadable despite being highly competent with the material on paper. Are there any other packages you would recommend before diving into that level of material?

Kevin Buzzard (Apr 21 2019 at 17:36):

I would recommend doing just what you're doing -- writing material yourself. I agree that the library is impenetrable for a beginner. Proofs are specifically shortened for efficiency reasons, they are not written to be readable. We are still lacking a bunch of good teaching material for mathematicians; I intend to make some this summer, however I also said that last summer.

Kevin Buzzard (Apr 21 2019 at 17:38):

Here's me trying to do some problem sheets which we give to 1st year mathematicians at Imperial: https://github.com/ImperialCollegeLondon/M1F_example_sheets

Greg Conneen (Apr 21 2019 at 17:39):

Oh wow, that's a substantial amount of material. Thank you so much!

Kevin Buzzard (Apr 21 2019 at 17:40):

The proofs are written in a far more longwinded way, they might be more instructional but there are still not many comments.

Kevin Buzzard (Apr 21 2019 at 17:40):

You can step through them in tactic mode and watch the state change, of course.

Greg Conneen (Apr 21 2019 at 17:42):

Right, I think that as long as I can just run through the tactic state I should be fine. Are there solutions for every single exercise?

Greg Conneen (Apr 21 2019 at 17:44):

Also, despite there being a real numbers package in mathlib, I've never seen it before. That's super useful!

Greg Conneen (Apr 21 2019 at 17:45):

oh wow, there's also complex numbers defined. That's amazing

Kevin Buzzard (Apr 21 2019 at 17:45):

I don't think I managed every single exercise. There were some which were hard to formalise.

Kevin Buzzard (Apr 21 2019 at 17:46):

Those answers are really just a big work in progress. They were just me trying to figure out if Lean was ready for a beginning undergraduate level maths course. It was.

Greg Conneen (Apr 21 2019 at 17:46):

Are you a professor at Imperial?

Kevin Buzzard (Apr 21 2019 at 17:46):

Yes

Greg Conneen (Apr 21 2019 at 17:47):

Oh, that's awesome. I'm just an undergrad math student

Kevin Buzzard (Apr 21 2019 at 17:48):

I felt like that when I was trying to get Lean to do my own problem sheets.

Greg Conneen (Apr 21 2019 at 17:50):

Haha yeah, the professor here at UVA has a tough time teaching Lean. It seems like it's one of those things where many trivial proofs on paper are just very difficult to formalise, despite familiarity

Kevin Buzzard (Apr 21 2019 at 17:51):

Yes. I spent about a year being very frustrated with not being able to make Lean do obvious things. I'm now finally over that initial learning hump and I can write even research level maths in Lean now, although it can take a long time.

Greg Conneen (Apr 21 2019 at 17:53):

Well congrats. I hope to get to that point someday. What maths research have you been able to formalise in Lean?

Kevin Buzzard (Apr 21 2019 at 17:53):

https://github.com/leanprover-community/lean-perfectoid-spaces

Kevin Buzzard (Apr 21 2019 at 17:53):

It's not finished yet but we're getting there.

Kevin Buzzard (Apr 21 2019 at 17:54):

I've just spent the last 30 minutes staring at https://leanprover.zulipchat.com/#narrow/stream/116395-maths/topic/Cute.20lemma/near/163135381

Kevin Buzzard (Apr 21 2019 at 17:54):

some technical lemma about topological spaces which we need.

Greg Conneen (Apr 21 2019 at 17:55):

that's... absolutely amazing. I never imagined we could do something like this in Lean so soon.

Kevin Buzzard (Apr 21 2019 at 17:55):

Yeah me neither

Kevin Buzzard (Apr 21 2019 at 17:55):

I've had a huge amount of support from other people here

Kevin Buzzard (Apr 21 2019 at 17:56):

18 months ago there weren't even complex numbers

Kevin Buzzard (Apr 21 2019 at 17:56):

Now we have schemes https://github.com/ramonfmir/lean-scheme and they work

Greg Conneen (Apr 21 2019 at 17:56):

Wow. Yeah, I'm now very happy that I've been introduced to this chat by Dr. Hölzl

Greg Conneen (Apr 21 2019 at 17:56):

Oh wow, algebraic geometry? I'll have to tell my advisor about this

Kevin Buzzard (Apr 21 2019 at 17:57):

we have stuff which reflects the interests of the people involved, I guess.

Greg Conneen (Apr 21 2019 at 17:58):

Thank you so much for all this info. I really appreciate it. I look forward to participating more in the future, when I get a little bit more acquainted with Lean.

Kevin Buzzard (Apr 21 2019 at 17:58):

https://leanprover.zulipchat.com/#narrow/stream/116395-maths/topic/Taking.20the.20Stacks.20Project.20formalisation.20forward

Kevin Buzzard (Apr 21 2019 at 17:58):

That's the schemes thread

Kevin Buzzard (Apr 21 2019 at 17:58):

we're going to re-write it using the category theory library; at the minute we make all the categories by hand.

Kevin Buzzard (Apr 21 2019 at 17:59):

There are universe issues, as ever in category theory, so it gets a bit hairy because you have to do it all properly in a system like this

Greg Conneen (Apr 21 2019 at 18:00):

That makes sense. Cat theory makes for the introduction of some interesting nuances

Greg Conneen (Apr 21 2019 at 18:14):

How do I apply one direction of an iff? Like, if I wanted to only apply the left implication in a proof.

Mario Carneiro (Apr 21 2019 at 18:36):

bla.1 or bla.mp

Mario Carneiro (Apr 21 2019 at 18:36):

which is short for iff.mp bla

Greg Conneen (Apr 21 2019 at 18:37):

oh okay, so if my iff statement was titled bla, bla.1 would be the left implication, and bla.2 would be the right?

Greg Conneen (Apr 21 2019 at 18:48):

Also, where can I find the xenalib package?

Greg Conneen (Apr 21 2019 at 18:50):

nvm, found it

Greg Conneen (Apr 22 2019 at 13:02):

Okay, I've got a Lean file I've tinkered around with in the past, which includes some simple proofs about integers. Some propositions I've failed to prove in Lean, and the rest I'm sure are anything but efficient. I was wondering if anyone would be willing to look over this file for me and give me some pointers on how to improve my proving techniques. int_props.lean

Greg Conneen (Apr 22 2019 at 13:03):

It's sort of a long file, and not very organised, but any help at all would be really appreciated. Sorry in advance.

Greg Conneen (Apr 23 2019 at 06:12):

I'm having trouble defining structure fields. The generic construction of structures is fine, I just don't understand fields. How would I go about appending them to a given structure?

Johan Commelin (Apr 23 2019 at 06:16):

Do you mean extending a structure?

Johan Commelin (Apr 23 2019 at 06:16):

Search for "extend" in mathlib and you will find lots of examples

Greg Conneen (Apr 23 2019 at 06:41):

oh, cool. Thank you. I didn't even know what I was looking for until you showed me

Kevin Buzzard (Apr 23 2019 at 06:58):

You might want to read the chapters on inductive types and structures in TPIL.

Patrick Massot (Apr 23 2019 at 07:22):

@Greg Conneen what Kevin really means is: if you have any interest in learning Lean, you want to read all of TPIL. The modern way of doing that is to launch VScode, open any Lean file, type Ctrl-shift-p type lean, select "Open documentation view", click "Theorem proving in Lean". This way you can click "Try it!" links on code snippets to open them right in VScode and play with them.

Patrick Massot (Apr 23 2019 at 07:32):

Trying to figure out the community map: Greg, are you a student of the Kevin Sullivan?

Greg Conneen (Apr 23 2019 at 12:47):

I was Prof. Sullivan's student last year, yes.

Greg Conneen (Apr 23 2019 at 12:49):

I've read the entirety of TPIL. I'm just still having trouble with implementation. I guess I'll tinker around with it a bit more

Greg Conneen (Apr 23 2019 at 13:09):

Okay, so if a group is defined like this

structure group : Type u  Type u
fields:
group.mul : Π {α : Type u} [c : group α], α  α  α
group.mul_assoc :  {α : Type u} [c : group α] (a b c_1 : α), a * b * c_1 = a * (b * c_1)
group.one : Π (α : Type u) [c : group α], α
group.one_mul :  {α : Type u} [c : group α] (a : α), 1 * a = a
group.mul_one :  {α : Type u} [c : group α] (a : α), a * 1 = a
group.inv : Π {α : Type u} [c : group α], α  α
group.mul_left_inv :  {α : Type u} [c : group α] (a : α), a⁻¹ * a

the way I would make something like this would be

structure {u} fake_group (a : Type u) :=
(mul : Π {α : Type u} [c : group α], α  α  α)
...

except, I want to change group α to fake_group α and I don't understand how to do that. I also in general don't understand the bracket notation or what pi does.

Johan Commelin (Apr 23 2019 at 13:14):

Note, you can use

```lean
foobar
```

to get code blocks with syntax highlighting

Greg Conneen (Apr 23 2019 at 13:15):

oh okay, thanks

Johan Commelin (Apr 23 2019 at 13:16):

Note that a group is not defined the way you did. (Internally that is what Lean sees, maybe. But it isn't how it's written in the Lean source files.)

Johan Commelin (Apr 23 2019 at 13:16):

Tip: write #print group in VScode, and Ctrl-click on group.

Johan Commelin (Apr 23 2019 at 13:16):

Or put your cursor on group and hit Ctrl - Shift - F10

Greg Conneen (Apr 23 2019 at 13:17):

ohhhh I see. So a group is simply an extension of a monoid, which I assume is an extension of a semigroup.

Greg Conneen (Apr 23 2019 at 13:18):

...which it is, 4 lines above. Thank you so much.

Greg Conneen (Apr 23 2019 at 13:22):

So, in general, classes are used in the backend, but Lean interprets them as structures. What's the difference?

Greg Conneen (Apr 23 2019 at 13:23):

Also, is there any way to formalise has_mul and has_one without extending those classes?

Johan Commelin (Apr 23 2019 at 13:24):

No the difference between classes and structures is type class inference

Johan Commelin (Apr 23 2019 at 13:24):

Also, is there any way to formalise has_mul and has_one without extending those classes?

Just copy paste their definitions.

Greg Conneen (Apr 23 2019 at 13:24):

I don't know what you mean by that

Johan Commelin (Apr 23 2019 at 13:24):

The word class is a red herring in this discussion

Johan Commelin (Apr 23 2019 at 13:24):

You could write structure everywhere

Greg Conneen (Apr 23 2019 at 13:25):

gotcha. So the only difference is that structures are't able to inherit the properties of other structures. So when would you use a structure over a class?

Johan Commelin (Apr 23 2019 at 13:26):

No, structures can inherit

Johan Commelin (Apr 23 2019 at 13:26):

The difference is "type class instance". Search for that in TPIL.

Greg Conneen (Apr 23 2019 at 13:26):

oh. Then what do you mean by "type class inference?"

Greg Conneen (Apr 23 2019 at 13:27):

oh okay, gotcha

Greg Conneen (Apr 23 2019 at 13:27):

must've skimmed over that section

Johan Commelin (Apr 23 2019 at 13:27):

inference, not inheritance :wink:

Johan Commelin (Apr 23 2019 at 13:27):

/me never read TPIL in detail. I'm a cargo cult Leaner.

Kevin Buzzard (Apr 23 2019 at 13:27):

The difference between a structure and a class is that classes are structures with extra magical sauce.

Greg Conneen (Apr 23 2019 at 13:27):

Ah, that's my confusion. Thank you for being patient with me

Kevin Buzzard (Apr 23 2019 at 13:27):

I was trying to write some advanced notes on type class inference

Kevin Buzzard (Apr 23 2019 at 13:28):

but before I wrote them I wrote some basic notes on type class inference for mathematicians

Kevin Buzzard (Apr 23 2019 at 13:28):

These might help. They present the same material as in TPIL but in a way far more suited to mathematicians.

Kevin Buzzard (Apr 23 2019 at 13:28):

They're in a branch of mathlib, hang on, I'll dig it out. I'm actually on an aeroplane at this point

Kevin Buzzard (Apr 23 2019 at 13:32):

https://github.com/leanprover-community/mathlib/blob/kbuzzard_typeclass_inference_doc/docs/extras/typeclass_inference.md

Kevin Buzzard (Apr 23 2019 at 13:33):

I found typeclass inference really hard to learn. It was only when I began to try to do normal maths in Lean that I began to see the point of it and understand it properly.

Kevin Buzzard (Apr 23 2019 at 13:36):

For reasons that you don't need to worry about and which might change, Lean keeps a ring in two packages not one. The first is a type called something like R. The second is a whole bunch of data which might be called something like _inst_1 : ring R and which you should never need to look at, but it's here that all the multiplication and the addition and the zero and the axioms are all stored. You will probably not need to dig out the axioms, you should just be applying lemmas, if you're doing mathematics -- but sometimes it's important to know where Lean magics up e.g. the proof of the distributivity law for the integers, and if you want to know this then you need to know about typeclass inference.

Greg Conneen (Apr 23 2019 at 14:10):

What if I wanted to talk about a ternary operator instead of a binary one (I want to define an algebraic heap)? I can't use has_mul, since it's strictly binary...

Andrew Ashworth (Apr 23 2019 at 14:12):

yup

Andrew Ashworth (Apr 23 2019 at 14:13):

what exactly is your question about ternary operators?

Greg Conneen (Apr 23 2019 at 14:17):

I want to define a class, let's start with semiheap. I only need it to have the following property, called para-associativity:

forall a b c d e : alpha, [[a b c] d e] = [a [d c b] e] = [a b [c d e]]

where [a b c] is a ternary operator. Although, If I have to use different notation given Lean's use of square brackets, that's fine

Greg Conneen (Apr 23 2019 at 14:19):

I assume I have to define some sort of infix for a ternary operator, but I don't know how to implement that

Johan Commelin (Apr 23 2019 at 14:19):

Does the operator have a "canonical" name in the literature?

Andrew Ashworth (Apr 23 2019 at 14:19):

section 10.3 in TPIL describes notation

Johan Commelin (Apr 23 2019 at 14:19):

You will first want to define it without the ternary notation.

Johan Commelin (Apr 23 2019 at 14:19):

Afterward, you can define the notation, and start using it.

Greg Conneen (Apr 23 2019 at 14:20):

Okay. I assume that I need to give it an operation on 3 elements, then state the rule

Greg Conneen (Apr 23 2019 at 14:21):

And no, unfortunately there's not really a canonical name for the operator. It's just a ternary operation, in the same way that the standard operation a group deals with is just a binary one

Johan Commelin (Apr 23 2019 at 14:22):

Yup. So you want fields

(tern_op : X  X  X  X)
(semi_assoc : \for a b c d e, blabla)

Johan Commelin (Apr 23 2019 at 14:22):

And no, unfortunately there's not really a canonical name for the operator. It's just a ternary operation, in the same way that the standard operation a group deals with is just a binary one

We "canonically" call it multiplication.

Greg Conneen (Apr 23 2019 at 14:23):

Ah, I see. That makes sense. Although, most mathematicians I know don't like to be so restrictive in terminology :P

Johan Commelin (Apr 23 2019 at 14:23):

I was just wondering whether there was a good alternative to tern_op.

Greg Conneen (Apr 23 2019 at 14:24):

Yeah, I figured that's what you were after. Heaps aren't really a common algebraic structure, I'm really just trying to implement this so I have a better understanding of how Lean deals with structures/classes

Greg Conneen (Apr 23 2019 at 14:29):

Also, am I able to say that 3 things are equal in one field without using "and"?

Greg Conneen (Apr 23 2019 at 14:29):

like how I wrote above

Patrick Massot (Apr 23 2019 at 14:31):

there are stupid indirect ways to say it, but using and is the reasonable one

Greg Conneen (Apr 23 2019 at 14:32):

Okay. Unfortunate, but I guess it's better than defining two separate fields

Patrick Massot (Apr 23 2019 at 14:33):

If you really want to you can build an API around this

Greg Conneen (Apr 23 2019 at 14:34):

No thank you lol

Patrick Massot (Apr 23 2019 at 14:34):

You will need lemmas about heaps saying that anyway

Patrick Massot (Apr 23 2019 at 14:35):

Whatever is inside your field, you'll want a lemma asserting the three relevant equalities

Greg Conneen (Apr 23 2019 at 14:35):

I don't know what you mean

Greg Conneen (Apr 23 2019 at 14:36):

Why would I need a lemma? Are you talking about showing that some object is a heap?

Greg Conneen (Apr 23 2019 at 14:36):

I'd expect that just implementing the classes wouldn't require lemmas

Johan Commelin (Apr 23 2019 at 14:36):

No, he is talking about making it usable

Greg Conneen (Apr 23 2019 at 14:37):

Oh, okay. So I can't refer to the fields until I define lemmas for them?

Patrick Massot (Apr 23 2019 at 14:39):

You will have:

class heap {X : Type*} :=
...

namespace heap
variables {X : Type*} [heap X] (a b c d e : X)
lemma assoc1  : [[a b c] d e] = [a [d c b] e] := sorry

lemma assoc2  : [a [d c b] e] = [a b [c d e]] := sorry

lemma assoc3  : [[a b c] d e] = [a b [c d e]] := sorry
end heap

Chris Hughes (Apr 23 2019 at 14:39):

I would have three fields rather than an and

Johan Commelin (Apr 23 2019 at 14:39):

You can refer to them, otherwise you can't state the lemmas. What Patrick is saying is that with only the fields you'll have something that is cumbersome to use.

Greg Conneen (Apr 23 2019 at 14:39):

yeah I'm putting it into 3 fields. I've realised that's easier

Patrick Massot (Apr 23 2019 at 14:39):

The point is: whatever the implementaion chosen, after writing those three lemmas, you will have your three equalities

Greg Conneen (Apr 23 2019 at 14:40):

What does that change? Just being able to refer to the property without using semiheap.*?

Patrick Massot (Apr 23 2019 at 14:40):

Of course if you put all three lemmas as fields then you don't have to state theses lemmas to use them (unless for some reason you want to change binder types, but that's a more advanced discussion)

Greg Conneen (Apr 23 2019 at 14:41):

Also, is Type* a way of referring to an arbitrary Type level without defining a universe?

Patrick Massot (Apr 23 2019 at 14:41):

And if you use redundant fields like that you will probably want a custom constructor

Greg Conneen (Apr 23 2019 at 14:43):

How would a custom constructor help me?

Greg Conneen (Apr 23 2019 at 14:43):

Sorry, but I'm still very new, and I don't really see the big picture yet

Patrick Massot (Apr 23 2019 at 14:45):

I think the discussion would be much easier if you write something and then we comment it

Patrick Massot (Apr 23 2019 at 14:45):

Abstract discussion won't help

Greg Conneen (Apr 23 2019 at 14:45):

Yeah I agree. I'll post in just a second

Greg Conneen (Apr 23 2019 at 15:00):

This is what I've got so far:

class semiheap (α : Type*) :=
(tern_op : α  α  α  α )
(para_assoc1 :  a b c d e : α,
    tern_op (tern_op a b c) d e = tern_op a (tern_op d c b) e)
(para_assoc2 :  a b c d e : α,
    tern_op (tern_op a b c) d e = tern_op a b (tern_op c d e))
(para_assoc3 :  a b c d e : α,
    tern_op a (tern_op d c b) e = tern_op a b (tern_op c d e))


namespace semiheap
notation `[` _ _ _ `]` := λ x y z : α, tern_op x y z
variables {α : Type*} [semiheap α] (a b c d e : α)

lemma para_assoc1 : sorry := sorry
lemma para_assoc2 : sorry := sorry
lemma para_assoc3 : sorry := sorry
end semiheap

The semiheap as defined is fine. I'm just having trouble with the notation, since it throws an error saying I'm referencing local variables if I put it below them, and I don't know how to reference the semiheap alpha if I put it above.

Patrick Massot (Apr 23 2019 at 15:12):

I don't think you can use space as a delimiter in this notation. You can play with

class semiheap (α : Type*) :=
(tern_op : α  α  α  α)
(notation `[` x `|` y `|` z `]` := tern_op x y z)
(para_assoc1 :  a b c d e : α, [[a | b | c] | d | e] = [a | [d | c | b] | e])
(para_assoc2 :  a b c d e : α, [a | [d | c | b] | e] = [a | b | [c | d | e]])
(para_assoc3 :  a b c d e : α, [[a | b | c] | d | e] = [a | b | [c | d | e]])


def semiheap.mk' {α : Type*} (op :α  α  α  α)
(h1 :  a b c d e : α,
    op (op a b c) d e = op a (op d c b) e)
(h2 :  a b c d e : α,
    op (op a b c) d e = op a b (op c d e)) : semiheap α :=
{ tern_op := op,
  para_assoc1 := h1,
  para_assoc2 := λ a b c d e, (h1 a b c d e)  (h2 a b c d e),
  para_assoc3 := h2}

Patrick Massot (Apr 23 2019 at 15:13):

The function is the custom constructor, it lets you build a structure by providing only two equalities

Greg Conneen (Apr 23 2019 at 15:16):

Why have we defined .mk'? What does it do? Also, what is ▸?

Patrick Massot (Apr 23 2019 at 15:17):

Please first try to define an instance of semiheap

Greg Conneen (Apr 23 2019 at 15:17):

It clearly rewrote, but what does it do in general?

Greg Conneen (Apr 23 2019 at 15:17):

Okay, I will

Greg Conneen (Apr 23 2019 at 16:01):

I've got it.

def tern_add :        := λ a b c, a+b+c
instance int_semiheap : semiheap  :=
begin
apply semiheap.mk' tern_add,
rw tern_add,
simp,
rw tern_add,
simp,
end

Greg Conneen (Apr 23 2019 at 16:01):

Thank you so much for your help.

Patrick Massot (Apr 23 2019 at 16:06):

Does that code compile?

Patrick Massot (Apr 23 2019 at 16:07):

Anyway, the point is you had to prove only para-associativity equalities instead of three

Patrick Massot (Apr 23 2019 at 16:07):

You can also study (after adding import tactic.abel on top of your file):

def tern_add :        := λ a b c, a+b+c

example : semiheap  :=
{ tern_op := tern_add,
  para_assoc1 := by { intros, dsimp[tern_add], abel },
  para_assoc2 := by { intros, dsimp[tern_add], abel },
  para_assoc3 := by { intros, dsimp[tern_add], abel } }


example : semiheap  :=
semiheap.mk' tern_add (by { intros, dsimp [tern_add], abel }) (by { intros, dsimp [tern_add], abel }

Patrick Massot (Apr 23 2019 at 16:08):

you can also replace dsimp [tern_add] by unfold tern_add

Greg Conneen (Apr 23 2019 at 16:10):

Okay, I will. I'm currently implementing heaps as an extension of semiheaps. How do I avoid having to write the notation as a field over and over?

Patrick Massot (Apr 23 2019 at 16:12):

you need to reissue the notation command after the semiheap definition. The one inside the definition has limited scope

Greg Conneen (Apr 23 2019 at 16:12):

Gotcha. So at most I'll only have to write it twice

Patrick Massot (Apr 23 2019 at 16:13):

yes

Greg Conneen (Apr 23 2019 at 19:07):

Is the symmetric group defined anywhere in mathlib? If not, I'd like to go about implementing it, and in doing so would need some guidance

Johan Commelin (Apr 23 2019 at 19:08):

It is:

src/data/equiv/basic.lean:/-- `perm α` is the type of bijections from `α` to itself. -/

Greg Conneen (Apr 23 2019 at 19:09):

Thank you

Greg Conneen (Apr 23 2019 at 19:10):

Followup question, has Cayley's theorem been proven?

Johan Commelin (Apr 23 2019 at 19:13):

@Chris Hughes I guess you did Cayley somewhere, right?

Chris Hughes (Apr 23 2019 at 19:14):

No.

Chris Hughes (Apr 23 2019 at 19:14):

But it won't be hard.

Johan Commelin (Apr 23 2019 at 19:14):

@Greg Conneen It would be a very nice project to start with!

Greg Conneen (Apr 23 2019 at 19:14):

Okay. I assume it won't be. That sounds like a great thing to do.

Greg Conneen (Apr 23 2019 at 19:15):

I'm going to need help understanding data.equiv.basic though

Greg Conneen (Apr 23 2019 at 19:17):

It looks like α ≃ β means α is isomorphic to β, given the defn. perm α makes sense. I'm still not completely comfortable with set theory notation and techniques in Lean, since I was introduced almost exclusively through propositions

Johan Commelin (Apr 23 2019 at 19:21):

@Greg Conneen Do you have an idea how you would formalise the statement?

Greg Conneen (Apr 23 2019 at 19:23):

I'm looking into that now. The proof is trivial to me on paper. I should be fine in abstracting it. I'll definitely ask for help with it when I need it

Patrick Massot (Apr 23 2019 at 19:28):

It looks like α ≃ β means α is isomorphic to β

This is an extremely confusing notation, but you'll get used to it

Patrick Massot (Apr 23 2019 at 19:29):

The first piece of truth is α ≃ β is the type of isomorphisms from α to β

Patrick Massot (Apr 23 2019 at 19:29):

So it's not a Prop, it contains data

Greg Conneen (Apr 23 2019 at 19:29):

oh no

Greg Conneen (Apr 23 2019 at 19:30):

lol that's certainly unintuitive

Patrick Massot (Apr 23 2019 at 19:30):

The next layer of confusion is that an isomorphism from α to β is not only a map from α to β which happens to be an isomorphism, it's a bundle containing such a map, the corresponding inverse map and two proofs

Patrick Massot (Apr 23 2019 at 19:31):

you need to read https://github.com/leanprover-community/mathlib/blob/master/src/data/equiv/basic.lean

Patrick Massot (Apr 23 2019 at 19:31):

keeping the above explanation in mind

Greg Conneen (Apr 23 2019 at 19:32):

That's perfectly fine. I can understand that such a map needs more info packaged in with it. So, what's the actual prop that A is iso to B?

Patrick Massot (Apr 23 2019 at 19:32):

don't forget to first read back the section on coercions in TPIL

Greg Conneen (Apr 23 2019 at 19:32):

And yeah I've been reading that file

Patrick Massot (Apr 23 2019 at 19:33):

nonempty (α ≃ β)

Patrick Massot (Apr 23 2019 at 19:33):

This is the Prop you are looking for

Patrick Massot (Apr 23 2019 at 19:35):

Probably I missed that piece of information: at what stage of your math studies are you? (Knowing this will help choosing explanations for you)

Greg Conneen (Apr 23 2019 at 19:38):

I don't think I've fully elaborated. I'm a 2nd year undergraduate math major currently taking my first semester of graduate real and complex analysis, and my third semester of graduate algebra. I plan on studying algebraic combinatorics, algebraic geometry, or algebraic topology. Either way, interested in algebra

Patrick Massot (Apr 23 2019 at 19:40):

I'm afraid my knowledge of the American university system is clearly not good enough to understand that answer. Can you give a couple of examples of theorems you recently learned in your most advanced courses?

Kevin Buzzard (Apr 23 2019 at 19:42):

nonempty (α ≃ β)

That just says alpha bijects with beta. You want an isomorphism of what structure?

Johan Commelin (Apr 23 2019 at 19:43):

He doesn't need more structure. He is trying to formalise Cayley's theorem.

Greg Conneen (Apr 23 2019 at 19:48):

Yeah. I'm currently in a course on wedderburn theory/commutative algebra. So, for example, we just covered the classification theorem for semisimple rings and semisimple algebras. In manifolds, we just covered the generalisation of stokes' theorem, and we just had an ending seminar on the Poincare lemma and the DeRham cohomology.

Patrick Massot (Apr 23 2019 at 19:49):

Thanks, this I understand (but I have no idea how to relate this to your abstract answer)

Greg Conneen (Apr 23 2019 at 19:50):

Well, I'm glad I could clarify for you

Kevin Kappelmann (Apr 26 2019 at 10:27):

What is the recommended/most elegant way to simplify/rewrite a hypothesis/assumption while re-stating the desired outcome of the simplification/rewriting? For example, doing

example (hyp : true  true = false) : false :=
have hyp : true = false, by { simp at hyp, assumption },
sorry

is not quite perfect as I have to write by { simp at hyp, assumption } (preferably, I'd like to simply write by simp at hyp) and leaves me with two hypotheses called hyp, namely

hyp : true  true = false,
hyp : true = false

Is there something like change for hypotheses?

Johan Commelin (Apr 26 2019 at 10:59):

There is rwa and simpa. Do those help you?

Kevin Kappelmann (Apr 26 2019 at 11:01):

Alright, that solves the former issue. Can I also somehow drop the non-simplified version from the list of assumptions?

Johan Commelin (Apr 26 2019 at 11:03):

@Kevin Kappelmann replace hyp : bla, by foobar?

Kevin Kappelmann (Apr 26 2019 at 11:08):

Yeah, that's exactly what I was looking for! thanks :) I do not think replace is mentioned in the tutorial. Is there some sort of document/website with "advanced tactics/APIs" that I can consult in such cases?

Johan Commelin (Apr 26 2019 at 11:09):

Which tutorial?

Johan Commelin (Apr 26 2019 at 11:09):

See the docs/ folder in mathlib. There is some stuff there.

Kevin Kappelmann (Apr 26 2019 at 11:10):

This document: https://leanprover.github.io/theorem_proving_in_lean/theorem_proving_in_lean.pdf

Patrick Massot (Apr 26 2019 at 11:38):

Is there something like change for hypotheses?

Yes, it's called change

Patrick Massot (Apr 26 2019 at 11:39):

If you have hyp : 1 + 1 = 2 you can change 2 = 2 at hyp

Kevin Kappelmann (Apr 29 2019 at 12:20):

It's me again :') I'm having some problems when using let-expressions + cases/induction. For example, this does not work

example (n m : ) : n + m = m + n :=
begin
let s := n + m,
induction n,
case nat.zero : {sorry}, -- error: could not find open goal of given case
case nat.succ : {sorry}
end

whereas this works

example (n m : ) : n + m = m + n :=
begin
induction n,
case nat.zero : {sorry},
case nat.succ : {sorry}
end

Does someone know what I am doing wrong?

Kevin Buzzard (Apr 29 2019 at 12:24):

Maybe that's a bug in case? You know that you don't need it, right?

example (n m : ) : n + m = m + n :=
begin
let s := n + m,
induction n,
{sorry},
{sorry}
end

Kevin Kappelmann (Apr 29 2019 at 12:25):

Yep, I'd really like to keep the case nat.xxx annotations though to keep my proofs more readable.

Kevin Buzzard (Apr 29 2019 at 12:25):

Make it a comment within the {}. And complain to the devs :-)

Kevin Kappelmann (Apr 29 2019 at 12:27):

Haha, alright :D Thanks

Kevin Buzzard (Apr 29 2019 at 12:29):

@Simon Hudon is this a bug?

Simon Hudon (Apr 29 2019 at 12:35):

I think it is. I'll look into it

Greg Conneen (Apr 30 2019 at 02:51):

Does unfold actually do anything other than change what the goal looks like? Isn't #print or Ctrl-click on what you're unfolding a replacement, since it's just a method of getting information about your goal? It seems that any proof can have the unfold statement removed, and it works just the same

Mario Carneiro (Apr 30 2019 at 03:01):

yes and no (and no). unfold will rewrite with equations that are not necessarily definitional equalities, so it might do more than just change the appearance of the goal. It's really a wrapper around simp with particular config options. dunfold is closer to your description, as it only does defeq simplifications. But there are two reasons that it's still not correct to say that it "doesn't actually do anything other than change what the goal looks like" and "any proof can have the dunfold statement removed and it works just the same". First, it does actually do something - it inserts an id term into the result (the partial proof being constructed). So you can see the result of dsimp and dunfold applications in the resulting proof if you #print it. This is done to keep typechecking time down by remembering the simplification path in the proof term.

The more important reason why dunfold and friends can't necessarily be removed from a proof is because many tactics depend on the syntactic shape of the goal or a hypothesis. For example, rw will not match against the goal if some definitional unfolding is required to see the constant that appears. For example if the goal is 2 * n = 0 where n : nat, then this is defeq to n + n = 0 but rw add_comm would only work on the second form of the goal. So anything that "changes what the goal looks like" could potentially affect whether a later tactic succeeds. That said, many tactics don't care about anything beyond defeq, in particular apply, refine and exact, and in many cases you can remove superfluous unfold and dunfold tactics without breaking the proof.

Simon Hudon (May 03 2019 at 21:45):

@Kevin Kappelmann I now have a fix for your let / case issue. It should be in the next nightly build of Lean 3.5.0c

Greg Conneen (May 04 2019 at 20:07):

Real quick, what is a meta variable and why is tactic.finish absolutely packed with them? I'm not sure exactly what's going on, but it seems that any trivial propositional technique is proven immediately with by finish

Patrick Massot (May 04 2019 at 20:08):

What do you mean finish is packed with metavariable?

Patrick Massot (May 04 2019 at 20:09):

A meta-variable is a kind of hole Lean will have to fill in. It can be the goal you need to solve or some implicit argument to be inferred by unification

Greg Conneen (May 04 2019 at 20:13):

I just mean that when I pressed Ctrl-Click on tactic.finish, I came upon a file that was filled with the keyword meta and I wasn't sure what any of it meant

Patrick Massot (May 04 2019 at 20:14):

oohh

Patrick Massot (May 04 2019 at 20:14):

that's a different meta

Greg Conneen (May 04 2019 at 20:15):

I suppose so, I just assumed that meta def was defining a metavariable, although I suppose that wouldn't make sense given my preconceived notion of what a metavariable is

Patrick Massot (May 04 2019 at 20:15):

meta in this context means "unsafe". It's code that won't be checked by Lean. It particular it doesn't have to be provably terminating

Greg Conneen (May 04 2019 at 20:15):

Why would one ever need such a thing?

Patrick Massot (May 04 2019 at 20:15):

Typically tactics (the commands you type between begin and end) are meta. They produce proofs that will be checked by Lean. But the way they produces those proofs is freestyle

Greg Conneen (May 04 2019 at 20:17):

Ah, okay. So it's not just a cheeky way of making axioms. So the reason I can solve so many things with by finish is because all of the structures in finish are using this meta keyword? I assume without it, there wouldn't be a way of taking arbitrary propositions and figuring out how to solve them

Greg Conneen (May 04 2019 at 20:18):

That is, finish wouldn't know how to solve a proposition handed to it if it weren't in a specific order of some sort

Scott Morrison (May 04 2019 at 20:18):

meta code can do non-terminating recursion, and it can also work with "reflected" versions of mathematical objects, i.e. their actual representations as expr objects in the C++ code.

Patrick Massot (May 04 2019 at 20:18):

finish constructs a proof for you

Greg Conneen (May 04 2019 at 20:19):

Okay, that's really cool @Scott Morrison

Patrick Massot (May 04 2019 at 20:19):

I don't think there is any risk of non-termination here. But the type of Lean expressions is meta, for reasons too long to explain (unless you understand what Scott wrote)

Scott Morrison (May 04 2019 at 20:20):

This is one of the lovely things about Lean --- you can write new tactics (i.e. programs that construct proofs, but aren't necessarily themselves correct) in Lean itself.

Greg Conneen (May 04 2019 at 20:20):

I understand C++ enough to generally know what he means

Scott Morrison (May 04 2019 at 20:20):

In previous interactive theorem provers you needed to step out, either into the base implementation language, or some DSL, to write new tactics.

Greg Conneen (May 04 2019 at 20:21):

So, even with meta, there's still no way to prove false? Because a bad tactic won't construct a good proof?

Greg Conneen (May 04 2019 at 20:21):

That makes sense

Scott Morrison (May 04 2019 at 20:21):

The meta keyword protects the real maths from these "helper" functions that we write to help construct proofs.

Greg Conneen (May 04 2019 at 20:21):

ahh

Scott Morrison (May 04 2019 at 20:22):

Exactly. The tactic framework lets you run meta code to produce proof terms, but those proof terms will be rejected if they refer to any meta functions.

Greg Conneen (May 04 2019 at 20:22):

so, how would a noob go about using meta? Or, should I just not even touch the thing?

Patrick Massot (May 04 2019 at 20:23):

It depends on your taste, background and goals

Scott Morrison (May 04 2019 at 20:23):

Read https://github.com/leanprover-community/mathlib/blob/master/docs/extras/tactic_writing.md

Scott Morrison (May 04 2019 at 20:23):

And start reading all the files in src/tactic/ in the mathlib repository.

Scott Morrison (May 04 2019 at 20:23):

and ask here :-)

Patrick Massot (May 04 2019 at 20:23):

Yes, reading that tutorial will give you a first glance at what it looks like

Patrick Massot (May 04 2019 at 20:24):

Reading src/tactic/ will give you a lot more (after a lot more time)

Greg Conneen (May 04 2019 at 20:24):

Great, thank you. My goal generally is to git gud in Lean, but eventually I want to start implementing some actual maths. I expect that will take me some time, given I'll have to read quite a bit of mathlib

Patrick Massot (May 04 2019 at 20:25):

You don't have to read all of mathlib before starting to implement actual maths

Greg Conneen (May 04 2019 at 20:28):

sure, but I'd like to understand a chunk of what's already there on a fundamental level

Scott Morrison (May 04 2019 at 20:28):

Do you have an idea what maths you'd like to do?

Scott Morrison (May 04 2019 at 20:29):

If you're ever tempted to add documentation to stuff you're reading, documentation-only pull requests are very welcome. :-)

Greg Conneen (May 04 2019 at 20:29):

I'd like to define a manifold, and maybe prove the inverse/implicit function theorem

Greg Conneen (May 04 2019 at 20:30):

I just really need to look over how topology and euclidean space are implemented first

Greg Conneen (May 04 2019 at 20:30):

Also, do we have the definition of a metric space?

Kevin Buzzard (May 04 2019 at 20:32):

@Greg Conneen If you want to implement some actual maths in Lean, then don't try to get good at Lean first, just try to implement some actual maths in Lean, get stuck, ask for help here, and you will succeed.

Sebastien Gouezel (May 04 2019 at 20:32):

I am precisely working on the definition of a manifold. Work in progress (very much in a preliminary state) at https://github.com/sgouezel/mathlib/blob/aa7fbab39d1e6a01665e24e6197b09338e855467/src/geometry/manifolds/basic.lean#L1598

Kevin Buzzard (May 04 2019 at 20:32):

I have no idea how to write tactics. Tactics are written in meta code because they can fail. You don't need to know anything about tactic-writing (which is what people use meta code for) to write maths.

Kevin Buzzard (May 04 2019 at 20:33):

However, what you do need to know is what is already there and what is being worked on, because this changes fast here

Kevin Buzzard (May 04 2019 at 20:34):

Yes we have metric spaces. You can see this yourself by firing up a project with mathlib as a dependency in VS Code and searching for metric space. This is a really important beginner skill to learn.

Kevin Buzzard (May 04 2019 at 20:35):

Make sure that "the cog doesn't have a box around it" in "files to exclude" in VS Code. That way you can search your dependencies.

Kevin Buzzard (May 04 2019 at 20:35):

Several people would like to define manifolds and people are sort of in the middle of things, so it might not be a perfect project right now for a beginner.

Kevin Buzzard (May 04 2019 at 20:36):

Definitions are harder than theorems; proofs can be sloppy but it's important to get definitions right.

Kevin Buzzard (May 04 2019 at 20:36):

One big obstruction to doing much with manifolds is that we don't have enough calculus, but again people are working on this and things are changing fast.

Kevin Buzzard (May 04 2019 at 20:37):

Bump functions are still a little out of reach at the minute, but it won't be long until we have them.

Kevin Buzzard (May 04 2019 at 20:38):

For polynomials we have multivariable ones and single variable ones; I am not an expert in either analysis or Lean-analysis, but I am wondering whether we are going to need a robust theory of calculus in one real variable, proving basic theorems such that the derivative of the integral is the function. I don't think we even have that.

Kevin Buzzard (May 04 2019 at 20:39):

We have no complex analysis worth mentioning, not even integrating along a reasonable path.

Kevin Buzzard (May 04 2019 at 20:40):

Because of our lack of analysis, this makes manifolds harder to do, however having goals like manifolds in mind is driving the library forwards in the right direction (as far as I am concerned). Lean seems to me to be focussing on the kind of mathematics mathematicians do, as opposed to the kind of mathematics that computer scientists have managed to pick up somehow.

Greg Conneen (May 04 2019 at 20:43):

Gotcha, thanks for the information. Do you think that complex would be a good thing to get working on, or do we not have it because it's hard to implement?

Kevin Buzzard (May 04 2019 at 20:49):

I am not an analyst and it's been nearly 30 years since I thought about the basics, Cauchy's integral formula etc. I've never taught the course either, so I am not the person to ask.

Kevin Buzzard (May 04 2019 at 20:50):

One thing I know was an issue is that someone has to make a decision about in what generality to define path integrals -- C^infinity paths isn't good enough because you want to integrate round semicircles and rectangles etc.

Kevin Buzzard (May 04 2019 at 20:51):

I really don't know what's there already though; I know a lot of the algebra stuff in Lean but the analysis stuff is changing quickly at the minute.

Patrick Massot (May 04 2019 at 20:56):

Kevin, the way this is going, we'll soon have manifolds with boundary and corners. When we'll have differential forms this will be much more than enough for rectangle and semi-circles

Kevin Buzzard (May 04 2019 at 21:17):

Woo hoo we'll have de Rham. cohomology one day. Is this in any other theorem prover?

Wojciech Nawrocki (Jun 10 2019 at 12:24):

Is it possible to "unimport" core/prelude, i.e. the basic definitions of nat, eq, etc, leaving an environment pretty much free of any definitions?

Kevin Buzzard (Jun 10 2019 at 12:25):

You can put prelude at the top of your file

Kevin Buzzard (Jun 10 2019 at 12:25):

which translates to "this file is part of the prelude" (so don't be importing all the prelude)

Kevin Buzzard (Jun 10 2019 at 12:25):

I've never used it, I've just seen people suggest it

Wojciech Nawrocki (Jun 10 2019 at 12:26):

Yup, that seems to work. Thank you!

Wojciech Nawrocki (Jun 11 2019 at 22:56):

Is the notation h.fn arg given class myclass (α: Type*) := (fn: α → ℕ) and [h: myclass α] discouraged? It stops working for example if fn: ℕ → α because the type argument α to myclass.fn becomes explicit, i.e. myclass.fn : Π (α : Type u_1) [c : myclass α], ℕ → α.

Adrian Chu (Jun 19 2019 at 15:44):

Given a positive integer n, how to define a vector with n entries? Thanks

Kenny Lau (Jun 19 2019 at 15:45):

vector \alpha n

Adrian Chu (Jun 19 2019 at 15:46):

Oh, and say x is the vector, how do we decribe its i-th entry?

Kenny Lau (Jun 19 2019 at 15:46):

import data.vector
universes u
variables (α : Type u) (n : )
#check vector α n

Kenny Lau (Jun 19 2019 at 15:47):

import data.vector
universes u
variables (α : Type u) (n : ) (v : vector α n) (k : fin n)
#check v.nth k

Adrian Chu (Jun 19 2019 at 15:55):

how to do summation from i=1 to n ?

Kenny Lau (Jun 19 2019 at 16:00):

import data.vector2
universes u
variables (α : Type u) [has_zero α] [has_add α] (n : ) (v : vector α n)
#check v.to_list.sum

Adrian Chu (Jun 19 2019 at 16:18):

one more question: how do we define new 3-vector, say, (x,y,x+y) where x, y are variable?

Adrian Chu (Jun 19 2019 at 16:20):

and more importantly, is there some online resource where I can read and get familiarized with these simple commands? I skimmed through theorem_proving_in_lean.pdf and couldn't find what I want... I feel like i'm asking too many noob questions like an idiot lol

Kenny Lau (Jun 19 2019 at 16:23):

import data.vector
universes u
variables (α : Type u) [has_add α] (x y : α)
#check ([x, y, x+y], rfl : vector α 3)

Adrian Chu (Jun 19 2019 at 16:25):

how about an n-vector like (x, 2x, 3x, ..., nx)? :mischievous:

Bryan Gin-ge Chen (Jun 19 2019 at 16:25):

is there some online resource where I can read and get familiarized with these simple commands

Unfortunately there isn't much beyond TPiL. You'll have to get used to reading source files. Here's data.vector.

Adrian Chu (Jun 19 2019 at 16:26):

is there some online resource where I can read and get familiarized with these simple commands

Unfortunately there isn't much beyond TPiL. You'll have to get used to reading source files. Here's data.vector.

Oh i see, thanks

Floris van Doorn (Jun 19 2019 at 16:28):

Currently, if you want to figure these things out yourself, the best way is just to look through the library files in mathlib (and core). You can go to data/vector2 in mathlib or the file Bryan mentioned in core. Also, operations on list might be useful to know for vectors. Other ways to find things you're searching for:

  • Browse through files (either on Github or locally)
  • "Go to definition" and "peek definition" (F12 and alt-F12 / option-F12?)
  • Search in VSCode (ctrl+shift+F)
  • Search on Github
  • Execute #print prefix vector in Lean
  • Find lemmas using the tactic library_search

Floris van Doorn (Jun 19 2019 at 16:29):

(x, 2x, 3x, ..., nx)

without looking at the library: hopefully vector.range and vector.map both exists, from which you should be able to figure it out (if not, use list.range and/or list.map)

Kenny Lau (Jun 19 2019 at 16:35):

how about an n-vector like (x, 2x, 3x, ..., nx)? :mischievous:

import algebra.group_power data.pnat data.vector
universes u
variables {α : Type u} [has_add α]
def multiple (x : α) :   α
| 0 := x -- junk value
| 1 := x
| (n+2) := multiple (n+1) + x
variables (x : α) (n : )
#check ((list.range n).map (λ i, multiple x (i+1)), by simp : vector α n)

Floris van Doorn (Jun 19 2019 at 16:43):

If \a is an add_monoid, you can use add_monoid.smul instead of multiple

Adrian Chu (Jun 19 2019 at 17:00):

import data.vector2
universes u
variables (α : Type u) [has_zero α] [has_add α] (n : ) (v : vector α n)
#check v.to_list.sum

Is there a way to do summation without using vector?

Johan Commelin (Jun 19 2019 at 17:02):

You might be interested in finset.sum and finset.range. The first gives you sums, the second gives you the finset "{0, ..., n-1}"

Kevin Buzzard (Jun 19 2019 at 17:04):

@Adrian Chu the problem is that TPIL just deals with core Lean, and you are asking about how to use stuff in the maths library. When I was learning the basics, I found the maths library very intimidating, but when I realised that I should just stop reading the proofs and instead just look at the definitions, I found that I could proceed with my mathematics very nicely, which in turn led to more understanding, which in turn made reading the proofs which I had been ignoring all this time much easier.

One thing I learnt fairly quickly was to abstract exactly what I wanted. For example you asked how to make (x, 2x, 3x, ..., nx). But in fact you can break this down into smaller questions. Probably you know well how to make a function like lam n, (n + 1) * x, so then you realise that what you really need is how to make (f 0, f 1, ..., f m). Now from general functional programming you might know that they like things like working out how to take a function f and to apply it to a vector like (0 1 2 ... n) so now you have two smaller questions -- how to make a vector like (0 1 2 ... n) and how to apply a function to every element of a vector. By this stage we are in the world where these ideas have a common name -- range for (0 1 2 ... n) and map for applying a function to a bunch of things at once. Now you can begin to guess the actual names of the functions you want.

Learning to think this way was a very important stepping stone for me; I slowly began to realise that I was developing a method for being able to answer my own questions of this nature. I am still learning, but this is the way to think about it. For every question, figure out how to build it from general tools and then figure out where those general tools are, perhaps by figuring out first what they should be called.

Adrian Chu (Jun 19 2019 at 17:16):

I see... I feel that TPiL is like a book teaching grammar, while mathlib is a dictionary. And knowing all the grammar does not immediately guarantee one can fluently read a dictionary, let alone writing sentences or paragraphs.

Kevin Buzzard (Jun 19 2019 at 19:27):

If you look through something like data.list.basic and take a look at just the names and the statements of the theorems, you can learn a lot about mathlib's naming conventions. The description above (breaking what you want down into small pieces) is I think how you're supposed to think about functional programming; the same sort of themes come up again and again; import data.finset and then use #check to look at the types of list.map and multiset.map and finset.map and it slowly dawns on you that map means a very specific thing in this game. Then try list.range and multiset.range and finset.range. It will slowly dawn on you by people are suggesting that you use vector.map and vector.range without even knowing if these functions are defined in Lean -- and if they're not then it's not hard to make them.

Luis Berlioz (Jun 20 2019 at 04:08):

Can I place a variable declaration in the middle of a proof?
For example instead of this:

variable s : α
example : r  (  x : α, r) :=
assume hr : r,
show (  x : α, r), from   s, hr 

I want the variable s : α declared inside the proof, like:

example : r  (  x : α, r) :=
assume hr : r,
variable s : α
show (  x : α, r), from   s, hr 

Johan Commelin (Jun 20 2019 at 04:10):

No, that's not possible. Maybe you mean have or let?

Johan Commelin (Jun 20 2019 at 04:11):

The goal is to prove that some alpha exists. You can't just grab it out of nowhere.

Luis Berlioz (Jun 20 2019 at 04:13):

Ok, that makes sense.

Adrian Chu (Jun 20 2019 at 10:14):

how to fix
def y := [1,2,3] #eval 4+y.nth 2 ? why can't I add?

Mario Carneiro (Jun 20 2019 at 10:16):

nth returns option nat

Mario Carneiro (Jun 20 2019 at 10:17):

you should be able to use y.nth_le 2 dec_trivial

Adrian Chu (Jun 20 2019 at 10:25):

oh it works, thanks! but what do .nth_le and dec_trivial means?

Mario Carneiro (Jun 20 2019 at 10:26):

no one wants an array out of bounds exception

Mario Carneiro (Jun 20 2019 at 10:26):

the dec_trivial is a proof that 2 < 3

Adrian Chu (Jun 20 2019 at 10:32):

i see

Adrian Chu (Jun 20 2019 at 10:52):

How about

def y := [1,2,3]
def g (i : fin 3) : ℕ := y.nth_le i-1
def f (i : fin 3) : ℕ := 4 + y.nth_le i-1 dec_trivial

? What's wrong?

Reid Barton (Jun 20 2019 at 11:42):

Clearly you intended to have parentheses around i-1, but I'm not sure what you are trying to achieve

Wojciech Nawrocki (Jun 20 2019 at 11:45):

Yep, function application binds tighter than subtraction, so to express what you want you need to put parentheses around i-1. You're also missing a proof that (i-i < list.length y) in g (no second argument is given).

Adrian Chu (Jun 20 2019 at 12:03):

Yep, function application binds tighter than subtraction, so to express what you want you need to put parentheses around i-1. You're also missing a proof that (i-i < list.length y) in g (no second argument is given).

How exactly should I give the proof then?

Mario Carneiro (Jun 20 2019 at 12:04):

You could use fin.last instead

Mario Carneiro (Jun 20 2019 at 12:06):

If you were doing this from scratch, you would be proving i-1 < 3 given i < 3. There are theorems in data.nat.basic to help with this

Mario Carneiro (Jun 20 2019 at 12:06):

alternatively, you can skip the proof and just handle the possibility of error using nth

Wojciech Nawrocki (Jun 20 2019 at 12:14):

I'm kinda surprised that forall (n m k: nat), n < m -> n-k < m isn't already in mathlib.

Mario Carneiro (Jun 20 2019 at 12:36):

it's just the composition of lt_of_le_of_lt and sub_le

Scott Morrison (Jun 20 2019 at 22:45):

That said, I think we should add it. Finding these lemmas is excruciating for beginners (who haven't even learnt that you can in principle guess the names of most basic facts, let alone learnt how to do it).

Scott Morrison (Jun 20 2019 at 22:47):

How bad is the downside of swamping Lean with these sort of "composition of two lemmas" lemmas?

Mario Carneiro (Jun 20 2019 at 22:47):

there is a combinatorial explosion

Mario Carneiro (Jun 20 2019 at 22:48):

it's not even clear which compositions are the most common

Mario Carneiro (Jun 20 2019 at 22:48):

and we don't even have the complete set of basic lemmas

Mario Carneiro (Jun 20 2019 at 22:49):

I would rather put more smarts into library_searchtype provers than flood the library with random facts

Scott Morrison (Jun 20 2019 at 23:27):

/me needs to get back to back

Tim Daly (Jun 21 2019 at 00:35):

There need not be a combinatorial explosion, nor does the naming convention have to be so baroque.

It appears to me that Lean is using a flat namespace. The naming convention makes it really hard to guess what you need.

Farmer and Carette (McMaster U.) have the notion of "tiny theories" which intrioduce a single axiom or a single signature. These can be combined to form larger objects which inherit the axioms and signatures. So, for instance, there would be a "commutative" axiom which can be inherited everywhere it makes sense.

This hierarchical organization enables re-use of axioms and signatures and allows the same name to occur in different paths in the inheritance graph.

Mario Carneiro (Jun 21 2019 at 00:37):

Lean has namespaces, which are used to label the major components in the theorem, or the broad area in which it lives

Mario Carneiro (Jun 21 2019 at 00:39):

The naming convention is designed to be easy to guess based on the statement, so you can think of a statement that you think is true and then search for the corresponding name. I'm curious how you would propose to improve on that scheme

Mario Carneiro (Jun 21 2019 at 00:41):

The number of theorems does increase exponentially as the depth increases. If there are n theorems, then there are O(n^2) ways to compose two of them, O(n^3) ways to compose three, and so on. Even 2n theorems is a major increase; n^2 is totally intractable

Tim Daly (Jun 21 2019 at 00:44):

Namespaces don't implement an inheritance graph in any generality. You'd like to state the commutative axiom in one place in the graph and have it available whereever it is used. What you really want is genreal "name polymorphism" so that the name 'factor' can have different semantics in different contexts. Using 'dotted namespace notation' forces the user to manage the namespace. This doesn't scale.

Scott Morrison (Jun 21 2019 at 00:45):

Well... only a fraction of those n^2 theorems actually make sense. I wish I had a better sense of what that fraction was. I guess this could be automated. :-)

Scott Morrison (Jun 21 2019 at 00:45):

@Tim Daly, of course Lean does state the commutative axiom only once.

Scott Morrison (Jun 21 2019 at 00:46):

(Okay, twice, once for "additive" structures and once for "multiplicative", which is lame, but still, it's only 2. :-)

Tim Daly (Jun 21 2019 at 00:58):

How does Lean handle a Lie algebra? Lie groups are skew-symmetric and they are non-associative. So

x*y = -y * x and

x * (y * z) = (x * y) * z + y * (x * z)

Do you have to "built it from nothing" using an odd naming convention for things like "algebra.lie.cross_product_skew_symmetric."?

Axiom, which uses something similar to Farmer/Carette "tiny theories" has 10,000 functions but a much smaller set of names.

Scott Morrison (Jun 21 2019 at 01:01):

As far as I'm aware no one has done anything on Lie algebras yet.

Mario Carneiro (Jun 21 2019 at 01:08):

We would normally use some naming convention based on the symbols in the axiom, but for core laws that have names we have a small set of name segments to describe the law. We've not dealt with these laws before, so I guess they would require a new terminology; I would use something like lie_algebra.mul_anticomm and lie_algebra.jacobi for these

Mario Carneiro (Jun 21 2019 at 01:10):

You should take a look at https://github.com/leanprover-community/mathlib/blob/master/docs/contribute/naming.md

Scott Morrison (Jun 21 2019 at 01:10):

I wonder, actually, if we'd even want to use mul for a Lie algebra. It's true that it distributes over addition just as a "normal" multiplication does, but otherwise has little in common.

Tim Daly (Jun 21 2019 at 01:23):

This places the burden of finding multiplication in an algebra on the user's ability to infer names. Polymorphic names could be disambiguated by a prolog-like match algorithm. The naming convention "is what it is", of course. But it seems to me that I have to read the original sources and "internalize" the names in my mind, which is a very small place.

Mario Carneiro (Jun 21 2019 at 01:24):

I'm not sure what problem you are trying to solve. There are multiple theorems with the same name in different namespaces, and we take care to have them be the same or clearly analogous

Mario Carneiro (Jun 21 2019 at 01:26):

If you have multiple theorems with the same name in scope (because you have opened multiple namespaces), it will disambiguate them based on the type

Andrew Ashworth (Jun 21 2019 at 01:29):

isn't this tiny theory system the type class inference system in disguise?

Tim Daly (Jun 21 2019 at 01:29):

Namespaces are a kind of type in that case. If they were all "in scope" then there is no need for namespaces?

Andrew Ashworth (Jun 21 2019 at 01:30):

specifically the often linked "unbundled type classes" section here: https://github.com/leanprover/lean/wiki/Refactoring-structures

Mario Carneiro (Jun 21 2019 at 01:30):

well no, namespaces are separate from types. The type system is DTT, the namespace system is just names and overloading

Tim Daly (Jun 21 2019 at 01:30):

Disguise? It is the type class inference problem.

Tim Daly (Jun 21 2019 at 01:31):

I admit ignorance of the full details. Further study on my part is needed.

Mario Carneiro (Jun 21 2019 at 01:32):

You can always refer to a theorem by its full name if the file that defines it has been imported. Opening a namespace just makes the name a bit shorter to reference

Adrian Chu (Jun 21 2019 at 03:35):

Btw, when Lean 4 comes out one day, what will happen to mathlib?

Mario Carneiro (Jun 21 2019 at 03:48):

Lean 4 is already "out", but not production ready. When it is solid we will start working on a port

Mario Carneiro (Jun 21 2019 at 03:49):

there isn't much point working on it at scale yet because it's too unstable and unfinished, although testing out a single file might be worthwhile

Adrian Chu (Jun 21 2019 at 03:52):

I see, then when it's stable enough, how much manual work is needed to update mathlib?

Bryan Gin-ge Chen (Jun 21 2019 at 03:53):

(deleted)

Mario Carneiro (Jun 21 2019 at 03:53):

somewhere between "a significant endeavor" and "a total overhaul"

Mario Carneiro (Jun 21 2019 at 03:54):

If we can get any refactoring tools in lean 3.5c then it may make this a lot easier. There is a lot of silly busywork like changing the naming convention

Adrian Chu (Jun 21 2019 at 09:12):

variable n : ℕ
def vec_exp1 (i : fin n) (x : vector ℕ n) (y : vector ℕ n) : ℕ :=
    (x.nth i)^(y.nth i)
def vec_exp2 (x : vector ℕ n) (y : vector ℕ n) : vector ℕ n :=
    ⟨(list.range n).map (λ i, vec_exp1 i x y), by simp⟩

Adrian Chu (Jun 21 2019 at 09:13):

I am trying to compute the entry-wise exponential of two n-vector. but the last line has error: the term x has type vector N n but is expected to have type fin i. what is wrong??

Adrian Chu (Jun 21 2019 at 09:14):

btw if i change n to 3, it works

Mario Carneiro (Jun 21 2019 at 09:17):

the variable n : N is getting added to vec_exp1, so it actually takes four arguments

Mario Carneiro (Jun 21 2019 at 09:17):

you should make it implicit by writing it in braces

Adrian Chu (Jun 21 2019 at 09:24):

ah i see! but now I'm getting another error in the last line: vec_exp1 i, the term i has type \N but is expected to have type fin ?m_1. how can I fix it?

Kevin Buzzard (Jun 21 2019 at 09:48):

If you hover over vec_exp1 you can see it has type vec_exp1 : Π {n : ℕ}, fin n → vector ℕ n → vector ℕ n → ℕ. So it wants an input of type fin n and an input of type vector ℕ n. If you hover over i you see it has type . So now you can understand the error. How to fix it -- make the types correct ;-) Do you have a more specific question?

Kevin Buzzard (Jun 21 2019 at 09:54):

Do you understand how to interpret (list.range n).map? Hover over list.range to see it has type ℕ → list ℕ. So list.range n has type list ℕ. Now this clever l.map thing means list.map l and doing #check list.map you can see the type of that too. You can just unravel everything. Your code doesn't work because when you unravel it it doesn't make sense.

Kevin Buzzard (Jun 21 2019 at 09:57):

Figuring out yourself what you want to do and then the types of the functions you want which will do them is a really good exercise for Lean beginners. It gets you thinking in the "functional programming" way.

Kevin Buzzard (Jun 21 2019 at 10:01):

I think you would be better off using vector.map than list.map because you're dealing with vectors. I see there is no vector.range though. It would be a good exercise to define vector.range first. Its type should be this:

def vector.range : Π (n : ℕ), vector ℕ n := sorry

Adrian Chu (Jun 21 2019 at 10:28):

If you hover over vec_exp1 you can see it has type vec_exp1 : Π {n : ℕ}, fin n → vector ℕ n → vector ℕ n → ℕ. So it wants an input of type fin n and an input of type vector ℕ n. If you hover over i you see it has type . So now you can understand the error. How to fix it -- make the types correct ;-) Do you have a more specific question?

yeah, i understand the error but just don't know what how to make i of type fin n. Should I use something like max i n? Also, why does #check max 3 4 works while #eval max 3 4 don't?

Adrian Chu (Jun 21 2019 at 10:29):

I think you would be better off using vector.map than list.map because you're dealing with vectors. I see there is no vector.range though. It would be a good exercise to define vector.range first. Its type should be this:

def vector.range : Π (n : ℕ), vector ℕ n := sorry

OK! i will try...

Johan Commelin (Jun 21 2019 at 10:35):

Actually... why do you want to work with vectors?

Johan Commelin (Jun 21 2019 at 10:35):

That's the annoying thing: you have to choose a data representation.

Johan Commelin (Jun 21 2019 at 10:35):

It could be list, or vector or maps from fin n.

Johan Commelin (Jun 21 2019 at 10:35):

Possibly other choices.

Johan Commelin (Jun 21 2019 at 10:36):

What is the end goal?

Johan Commelin (Jun 21 2019 at 10:36):

"Just goofing around" is a valid answer, in which case using vectors is totally fine.

Adrian Chu (Jun 21 2019 at 10:46):

What is the end goal?

The goal is simply to have an n-tuple of numbers. Now you mention it, fin n \to \N does sounds a lot more simplier. I will use this....

Johan Commelin (Jun 21 2019 at 11:11):

fin n → ℕ does sounds a lot more simplier.

Depends...

Johan Commelin (Jun 21 2019 at 11:11):

If you want to write down an explicit 5-tuple, I guess vector is easier.

Adrian Chu (Jun 21 2019 at 12:02):

fin n → ℕ does sounds a lot more simplier.

Depends...

say now i have f:fin n → ℕ and want to calculate f(1)+...+f(n). do I have to convert f to a vector and use .tolist_sum ? or is there a more direct way?

Alexander Bentkamp (Jun 21 2019 at 12:29):

Try finset.univ.sum.

Adrian Chu (Jun 21 2019 at 14:02):

i know that int.gcd can find the gcd of two numbers. how about finding the gcd of n numbers? what is the best way?

Marc Huisinga (Jun 21 2019 at 14:12):

since gcd is an associative function, you can foldr or foldl over the collection of numbers

Kevin Buzzard (Jun 21 2019 at 14:59):

yeah, i understand the error but just don't know what how to make i of type fin n. Should I use something like max i n?

Do you understand how to make a term of type fin n? By definition, a term of type fin n is a pair, consisting of a natural number i and a proof that i < n. In your function you seem to have a random natural number i with no conditions at all, so it will be impossible to prove that i < n. That was why I was encouraging you to step back a bit and think about the functions you're using and what you're doing.

Also, why does #check max 3 4 works while #eval max 3 4 don't?

#eval max 3 4 works for me. Can you post a MWE?

Adrian Chu (Jun 21 2019 at 15:17):

What does MWE stand for? lol

Adrian Chu (Jun 21 2019 at 15:18):

And the error message for #eval max 3 4 is "code generation failed, VM does not have code for lattice.lattice.conditionally_complete_linear_order_bot'"

Kevin Buzzard (Jun 21 2019 at 15:18):

Minimal working example. I type #eval max 3 4 and it worked fine. I was wondering exactly what you did. A MWE is something you can cut and paste so I can see the error you're seeing. Currently I can't do that becaue you're only posting the line which gave the error.

Adrian Chu (Jun 21 2019 at 15:27):

Oh, I figured it out. It is because I imported ring_theory.principal_ideal_domain

Adrian Chu (Jun 21 2019 at 16:49):

given positive integers a and b how can we prove that the sum of a b's is ab?

Kevin Buzzard (Jun 21 2019 at 16:50):

How are you defining the sum of a b's? The devil is in the detail for this one.

Reid Barton (Jun 21 2019 at 16:50):

And why do you want to prove it?

Reid Barton (Jun 21 2019 at 16:53):

You might decide the definition of a * b already answers your question

Reid Barton (Jun 21 2019 at 16:53):

Or possibly b * a

Adrian Chu (Jun 21 2019 at 16:54):

How are you defining the sum of a b's? The devil is in the detail for this one.

@Kevin Buzzard I'm using finset.univ.sum (λ i : fin a, b)

Adrian Chu (Jun 21 2019 at 16:56):

And why do you want to prove it?

@Reid Barton
This is a crucial lemma in a theorem I wanna prove. Specifically, I need to prove that given positive integers b, c, the sum of b (b^c)'s is b^(c+1)

Reid Barton (Jun 21 2019 at 16:57):

Well this statement still has the phrase "the sum of x ys" in it so I could ask the same question again

Reid Barton (Jun 21 2019 at 16:59):

We all know the sum of X Ys is X * Y, so why not just formalize the statement using *

Adrian Chu (Jun 21 2019 at 16:59):

Well this statement still has the phrase "the sum of x ys" in it so I could ask the same question again

I need to show finset.univ.sum (λ i : fin a, b)=a*b

Reid Barton (Jun 21 2019 at 16:59):

OK, that's a statement that we should be able to prove

Kevin Buzzard (Jun 21 2019 at 17:05):

I feel like this should be in the library. Is it sum_const or something? shrug

Kevin Buzzard (Jun 21 2019 at 17:06):

@Adrian Chu I would be tempted to find where finset.sum is defined in mathlib (just right click on it in VS Code and go to the definition) and take a look at the next 50 lemmas after its definition, to see if there is anything useful there.

Adrian Chu (Jun 21 2019 at 17:08):

I found this

theorem sum_const (ι : Type u) (a : cardinal.{u}) : sum (λ _:ι, a) = mk ι * a :=
quotient.induction_on a $ λ α, by simp; exact
  quotient.sound equiv.sigma_equiv_prod _ _⟩

in set theory/cardinal

Kevin Buzzard (Jun 21 2019 at 17:12):

That's the wrong kind of sum. This is exactly why the devil is in the detail.

Kevin Buzzard (Jun 21 2019 at 17:14):

Was there nothing near finset.sum?

Adrian Chu (Jun 21 2019 at 17:21):

I can't see what I want (possibly due to my own ignorance)

Kevin Buzzard (Jun 21 2019 at 17:24):

Ok I'll take a look.

Kevin Buzzard (Jun 21 2019 at 17:24):

It is indeed finset.sum_const

Kevin Buzzard (Jun 21 2019 at 17:25):

You could have just tried #check finset.sum_const

Kevin Buzzard (Jun 21 2019 at 17:35):

Aah, I bet you didn't have the right import.

Kevin Buzzard (Jun 21 2019 at 17:36):

import algebra.big_operators

Kevin Buzzard (Jun 21 2019 at 17:36):

Yeah, I don't know how to search for it if you don't have the right import. It would be nice if it was easy just to import everything temporarily.

Kevin Buzzard (Jun 21 2019 at 17:37):

Oh wait, but you need that import for finset.sum...

Kenny Lau (Jun 21 2019 at 17:52):

import algebra.big_operators data.fintype

example {a b : } : (finset.univ : finset $ fin a).sum (λ i, b) = a * b :=
by rw [finset.sum_const, finset.card_univ, fintype.card_fin, add_monoid.smul_eq_mul, nat.cast_id]

Kevin Buzzard (Jun 21 2019 at 18:10):

That's the way (or you can use simp instead of the last couple of things, I should think)

Kevin Buzzard (Jun 21 2019 at 18:11):

example (a b : ):  finset.univ.sum (λ i : fin a, b)=a*b :=
by simp [finset.sum_const, finset.card_univ, fintype.card_fin]

Adrian Chu (Jun 22 2019 at 04:49):

Thanks guys!

Adrian Chu (Jun 22 2019 at 08:01):

What command should I use to define a function f(x) from N to N with two cases, namely x >= some fixed n and x < n?

Kevin Buzzard (Jun 22 2019 at 08:02):

Lean has if ... then ... else

Kevin Buzzard (Jun 22 2019 at 08:03):

The tactic you need to know to introduce it is split_ifs.

Kevin Buzzard (Jun 22 2019 at 08:04):

if...then...else is harder to use than you might think, because Lean will by default refuse to do a case split on a condition unless it knows an algorithm for deciding whether the condition is true or not. In your case this should be no trouble.

Adrian Chu (Jun 22 2019 at 08:15):

given a function f : fin n -> N, how can I extend it to N -> N by defining it to be zero when the input is >= n ?

Mario Carneiro (Jun 22 2019 at 08:16):

if h : x < n then f <x, h> else 0

Kevin Buzzard (Jun 22 2019 at 10:01):

You shouldn't think of fin n as some sort of subset of nat. There's a map from fin n to nat which forgets part of the structure. From your recent questions you seen to think that a term of type fin n is a nat. It's not -- it's a pair.

Adrian Chu (Jun 22 2019 at 10:09):

You shouldn't think of fin n as some sort of subset of nat. There's a map from fin n to nat which forgets part of the structure. From your recent questions you seen to think that a term of type fin n is a nat. It's not -- it's a pair.

ya, i know, i just didn't know that we can directly write if h : x < n instead of if x<n

Adrian Chu (Jun 22 2019 at 10:14):

What is the correct syntax to define such a function def f (x : ℕ) : ℕ := if x < 10 then x else f (x-3) recursively? (since I don't want to use mod for some other reason)

Kevin Buzzard (Jun 22 2019 at 11:11):

You will have to prove, somehow, that your function is well-defined. One thing you could do is just use the equation compiler, but that would stink a bit in this situation. You could define f zero = 0, f 1 = 1, ..., and then f (n+10) to be f(n+7). That might work.

Mario Carneiro (Jun 22 2019 at 11:13):

you only have to go up to n+3

Kevin Buzzard (Jun 22 2019 at 11:13):

I'm not so sure it's as easy as that, because f(7) is not f(4)

Mario Carneiro (Jun 22 2019 at 11:14):

def f :   
| x@(y+3) := if x < 10 then x else f y
| x := x

Kevin Buzzard (Jun 22 2019 at 11:15):

Nice!

Mario Carneiro (Jun 22 2019 at 11:15):

it still has 4 cases though

Kevin Buzzard (Jun 22 2019 at 11:15):

I have never had to push the equation compiler to its limits, so I don't really know these tricks.

Mario Carneiro (Jun 22 2019 at 11:16):

try replacing 3 with 100

Adrian Chu (Jun 22 2019 at 11:48):

I see... Thanks.

Adrian Chu (Jun 22 2019 at 11:59):

def f :   
| x@(y+3) := if x < 10 then x else f y
| x := x

But can someone please explain a bit what does this means?

Kevin Buzzard (Jun 22 2019 at 12:00):

Have you read the section about the equation compiler in TPIL?

Kevin Buzzard (Jun 22 2019 at 12:00):

It says "if x = y + 3 for some other nat y then do the first thing, else do the second thing"

Adrian Chu (Jun 22 2019 at 12:03):

well not yet. you are right, i should read it first :)

Adrian Chu (Jun 22 2019 at 14:58):

Now i have read the relevant sections in TPIL. But I still got a problem: How can I check the well foundedness of

def f :  ×    × 
| (y+3, z+3) := if ((y+3) < 10)  ((z+3) < 10) then (y+3, z+3) else f (y, z)
| (y, z) := (y, z)

?

Kevin Buzzard (Jun 22 2019 at 16:22):

Some hints are here: https://github.com/leanprover-community/mathlib/blob/master/docs/extras/well_founded_recursion.md

Bryan Gin-ge Chen (Jun 23 2019 at 02:19):

I had a go at this here (web editor link):

import data.prod
/-
set_option pp.all true
-/

def f :  ×    × 
| (y+3, z+3) := have h : (y, z).lex (<) (<) (y+3, z+3) := begin
  simp [prod.lex_def],
  have hy : y < y + 3 := nat.lt_add_of_pos_right (dec_trivial),
  exact or.inl hy,
end,
  if ((y+3) < 10)  ((z+3) < 10) then
    (y+3, z+3) else f (y, z) -- error here, see below
| (y, z) := (y, z)

However, I'm not able to satisfy the equation compiler either:

failed to prove recursive application is decreasing, well founded relation
  @has_well_founded.r (ℕ × ℕ)
    (@prod.has_well_founded ℕ ℕ (@has_well_founded_of_has_sizeof ℕ nat.has_sizeof)
       (@has_well_founded_of_has_sizeof ℕ nat.has_sizeof))
Possible solutions:
  - Use 'using_well_founded' keyword in the end of your definition to specify tactics for synthesizing well founded relations and decreasing proofs.
  - The default decreasing tactic uses the 'assumption' tactic, thus hints (aka local proofs) can be provided using 'have'-expressions.
The nested exception contains the failure state for the decreasing tactic.
nested exception message:
match failed
state:
f : ℕ × ℕ → ℕ × ℕ,
y z : ℕ,
h : prod.lex has_lt.lt has_lt.lt (y, z) (y + 3, z + 3)
⊢ prod.lex has_lt.lt has_lt.lt (y, z) (y + 3, z + 3)

(Isn't h the same thing as the goal? Using pp.all true doesn't reveal any differences either)

I think it's easier to define f by working with the curried version, like this:

def f_curry :      × 
| (y+3) (z+3) := if ((y+3) < 10)  ((z+3) < 10) then
    (y+3, z+3) else f_curry y z
| y z := (y, z)

def f' (p :  × ) :  ×  := f_curry p.1 p.2

Mario Carneiro (Jun 23 2019 at 02:26):

I think, from the trajectory of the questions, that @Adrian Chu wants to know how to do general recursions, rather than breaking them down into structural recursions. It looks like this:

def f :  ×    × 
| (x, y) :=
  if h : x < 10  y < 10 then (x, y) else
  have has_well_founded.r (x - 3, y - 3) (x, y), from sorry,
  f (x - 3, y - 3)
using_well_founded { dec_tac := `[assumption] }

You have to give a proof that (x - 3, y - 3) < (x, y) according to some well founded relation. The default one here is lexicographic order on the natural numbers, which works in this case but may need to be replaced with something else in other examples

Adrian Chu (Jun 23 2019 at 03:57):

I see.. I didn't know we have `[assumption]

Adrian Chu (Jun 23 2019 at 03:59):

I am trying to prove the following variant of Bazout lemma:

lemma baz (a : ) (b : ) (h: nat.coprime a b):  x : ,  y : , a*x + 1 = b*y :=
sorry

As pointed before, we can use gcd_a and gcd_b

Adrian Chu (Jun 23 2019 at 04:04):

but the trouble is gcd_a, gcd_b can be negative

Adrian Chu (Jun 23 2019 at 04:05):

so my plan is to keep adding multiples of a*b to -gcd_a a b and gcd_b a b repsectively until they are both >= 0

Adrian Chu (Jun 23 2019 at 04:07):

But I run into 2 problems. First, (as an example,) given a recursive function like

def f :    
| (y+3) := if h:(y+3<10) then (y+3) else f y
| y := y

how can we show that f x < 10 for all x ?

Adrian Chu (Jun 23 2019 at 04:09):

second, given a function f from Z to Z and a proof that f x >=0 for all x, how can we define a new function F from Z to N using f?

Mario Carneiro (Jun 23 2019 at 04:35):

Rather than "repeatedly adding multiples of a*b", just add k*a*b for some k. This avoids all the mess of determining any well founded recursion, but it leaves the question - what is k?

Mario Carneiro (Jun 23 2019 at 04:36):

Also baz is really obviously false when a = b = 0

Mario Carneiro (Jun 23 2019 at 04:36):

or more generally when they aren't coprime

Adrian Chu (Jun 23 2019 at 04:44):

Also baz is really obviously false when a = b = 0

oops, I carelessly left out the coprime condition. now fixed

Mario Carneiro (Jun 23 2019 at 04:46):

If a and b are both nonzero, then a*b is also nonzero, so it is at least 1 and hence k*a*b is at least k. So you can pick k to be gcd_a a b and then k*a*b - gcd_a a b will be nonnegative

Adrian Chu (Jun 23 2019 at 04:49):

Rather than "repeatedly adding multiples of a*b", just add k*a*b for some k. This avoids all the mess of determining any well founded recursion, but it leaves the question - what is k?

Yes, I have thought of that, we can choose k=max (nat_abs (nat.gcd_a x y)) (nat_abs (nat.gcd_b x y))

Adrian Chu (Jun 23 2019 at 04:49):

yes yours is simpler!

Mario Carneiro (Jun 23 2019 at 04:50):

You can use either to_nat or nat_abs to turn a nonnegative int into a nat

Adrian Chu (Jun 23 2019 at 04:52):

ok, let me try now

Adrian Chu (Jun 23 2019 at 05:02):

If a and b are both nonzero, then a*b is also nonzero, so it is at least 1 and hence k*a*b is at least k. So you can pick k to be gcd_a a b and then k*a*b - gcd_a a b will be nonnegative

i was mistaken. we should add k*b to -gcd_a a b and add k*a to gcd_b a b

Adrian Chu (Jun 23 2019 at 05:04):

and take k = max (nat_abs (nat.gcd_a a b)) (nat_abs (nat.gcd_b a b))

Adrian Chu (Jun 23 2019 at 11:26):

May I ask how to prove
theorem test (a : ℤ) (ha : a >= 0) : a = nat_abs a ?

Kevin Buzzard (Jun 23 2019 at 11:26):

Does cases on a work?

Kevin Buzzard (Jun 23 2019 at 11:26):

Alternatively just use library_search.

Kevin Buzzard (Jun 23 2019 at 11:27):

You might want to import data.int.basic before you search

Kevin Buzzard (Jun 23 2019 at 11:28):

Here's where I'm up to: https://github.com/kbuzzard/xena/blob/cd8e0de23adf8c0e7c56d39f1b6f5a55d93bf6ef/Examples/mario_glueing.lean#L144

Kenny Lau (Jun 23 2019 at 12:17):

open int
theorem test : Π a : , 0  a  a = nat_abs a
| (of_nat n) h := rfl

Kenny Lau (Jun 23 2019 at 12:18):

import data.int.basic
open int
theorem test (a : ) (ha : 0  a) : a = nat_abs a :=
(nat_abs_of_nonneg ha).symm

Adrian Chu (Jun 23 2019 at 14:22):

Ah, so we already have this theorem. Thanks

Adrian Chu (Jun 23 2019 at 14:50):

I am proving using calc, the first and last expression has type nat but some of the intermediate steps have type int

Mario Carneiro (Jun 23 2019 at 14:51):

apply int.coe_nat_inj

Adrian Chu (Jun 23 2019 at 14:51):

which hence give type mismatch error

Mario Carneiro (Jun 23 2019 at 14:51):

before starting the calc block

Adrian Chu (Jun 23 2019 at 15:27):

can someone kindly write a simple example to illustrate how a proof involving int.coe_nat_inj and calc should look like? since I can't find this in mathlib

Mario Carneiro (Jun 23 2019 at 15:40):

you can't find the theorem?

Mario Carneiro (Jun 23 2019 at 15:41):

show me your proof and I'll fix it

Mario Carneiro (Jun 23 2019 at 15:44):

example (x y : ) (a : ) (h1 : a = x) (h2 : a = y) : x = y :=
int.coe_nat_inj $ calc
   x = a : h1.symm
  ... = y : h2

Adrian Chu (Jun 23 2019 at 15:47):

wow thanks.

Adrian Chu (Jun 23 2019 at 15:48):

lemma baz (a : ) (b : ) (h : nat.coprime a b) :  x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -nat.gcd_a a b + (gcd_b a b)*k,
  let y := nat.gcd_b a b + (gcd_a a b)*k,
  have hx : x = nat_abs x := sorry,
  have hy : y = nat_abs y := sorry,
  fapply exists.intro,
  exact nat_abs x,
  fapply exists.intro,
  exact nat_abs y,
  calc
    a*(nat_abs x) + 1 = b*(nat_abs y) : by sorry
end

Adrian Chu (Jun 23 2019 at 15:48):

bit by bit, I'm first dealing with the last sorry. do you think i should use int.coe_nat_inj here?

Mario Carneiro (Jun 23 2019 at 15:52):

lemma baz (a : ) (b : ) (h : nat.coprime a b) :  x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -nat.gcd_a a b + (gcd_b a b)*k,
  let y := nat.gcd_b a b + (gcd_a a b)*k,
  have hx : x = nat_abs x := sorry,
  have hy : y = nat_abs y := sorry,
  existsi nat_abs x,
  existsi nat_abs y,
  apply int.coe_nat_inj,
  calc
    (a*(nat_abs x) + 1 : ) = a*(nat_abs x) + 1 : by simp
      ... = a * x + 1 : by rw  hx
      ... = b * y : by sorry
      ... = b * (nat_abs y) : by rw  hy
      ... = (b * (nat_abs y) : ) : by simp
end

Adrian Chu (Jun 23 2019 at 15:59):

thx, i'll keep working on it

Adrian Chu (Jun 23 2019 at 16:20):

what should replace sorry in this step? +_+ why doesn't left_distrib works?

a*(-nat.gcd_a a b + b*k) + 1
... = - a*nat.gcd_a a b + a*b*k + 1 : by sorry

Mario Carneiro (Jun 23 2019 at 16:21):

it's not just left_distrib, you also associated + and distributed - over *

Mario Carneiro (Jun 23 2019 at 16:22):

simp [mul_add] should do it

Adrian Chu (Jun 23 2019 at 16:23):

by simp [mul_add] doesnt work...

Mario Carneiro (Jun 23 2019 at 16:24):

look at what you get, and add theorems that make them look more alike

Adrian Chu (Jun 23 2019 at 16:28):

i need mul_assoc !!

Adrian Chu (Jun 24 2019 at 05:05):

lemma baz (a : ) (b : ) (h : nat.gcd a b = 1) :  x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -nat.gcd_a a b + b*k,
  let y := nat.gcd_b a b + a*k,
  have hx : x = nat_abs x := sorry,
  have hy : y = nat_abs y := sorry,
  existsi nat_abs x,
  existsi nat_abs y,
  apply int.coe_nat_inj,
  calc
    (a*(nat_abs x) + 1 : ) = a * x + 1 : by simp [hx.symm]
      ... = - a*nat.gcd_a a b + nat.gcd a b + a*b*k : by simp [mul_add, mul_assoc, add_assoc, h]
      ... = - a*nat.gcd_a a b + a*nat.gcd_a a b + b*nat.gcd_b a b + a*b*k : by sorry
      ... = b*nat.gcd_b a b + b*a*k : by simp [add_neg_self, mul_comm]
      ... = b * y : by simp [mul_add, mul_assoc]
      ... = (b * (nat_abs y) : ) : by simp [hy.symm]
end

Adrian Chu (Jun 24 2019 at 05:06):

I have trouble to finish the last sorry (on the 5th last line)

Adrian Chu (Jun 24 2019 at 05:07):

I know that i should use gcd_eq_gcd_ab, but type problems of Z and N keep coming up, and I don't know how to use ↑ correctly

Mario Carneiro (Jun 24 2019 at 06:34):

make sure to bracket things correctly so that the work is isolated

Mario Carneiro (Jun 24 2019 at 06:34):

when you write a+b+c+d that gets associated as ((a+b)+c)+d, so you are mixing different concerns

Mario Carneiro (Jun 24 2019 at 06:36):

lemma baz (a : ) (b : ) (h : nat.gcd a b = 1) :  x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -nat.gcd_a a b + b*k,
  let y := nat.gcd_b a b + a*k,
  have hx : x = nat_abs x := sorry,
  have hy : y = nat_abs y := sorry,
  existsi nat_abs x,
  existsi nat_abs y,
  apply int.coe_nat_inj,
  calc
    (a*(nat_abs x) + 1 : ) = a * x + 1 : by simp [hx.symm]
      ... = - a*nat.gcd_a a b + nat.gcd a b + a*b*k : by simp [mul_add, mul_assoc, add_assoc, h]
      ... = - a*nat.gcd_a a b + (a*nat.gcd_a a b + b*nat.gcd_b a b) + a*b*k : by rw gcd_eq_gcd_ab
      ... = b*nat.gcd_b a b + b*a*k : by simp [add_neg_self, mul_comm]
      ... = b * y : by simp [mul_add, mul_assoc]
      ... = (b * (nat_abs y) : ) : by simp [hy.symm]
end

Adrian Chu (Jun 24 2019 at 06:38):

nope, this doesnt work, it has error message :

rewrite tactic failed, did not find instance of the pattern in the target expression
gcd ?m_4 ?m_5
state:
a b : ℕ,
h : nat.gcd a b = 1,
k : ℕ := max (nat_abs (gcd_a ↑a ↑b)) (nat_abs (gcd_b ↑a ↑b)),
x : ℤ := -nat.gcd_a a b + ↑b * ↑k,
y : ℤ := nat.gcd_b a b + ↑a * ↑k,
hx : x = ↑(nat_abs x),
hy : y = ↑(nat_abs y)
⊢ -↑a * nat.gcd_a a b + ↑(nat.gcd a b) + ↑a * ↑b * ↑k =
-↑a * nat.gcd_a a b + (↑a * nat.gcd_a a b + ↑b * nat.gcd_b a b) + ↑a * ↑b * ↑k
state:
a b : ℕ,
h : nat.gcd a b = 1,
k : ℕ := max (nat_abs (gcd_a ↑a ↑b)) (nat_abs (gcd_b ↑a ↑b)),
x : ℤ := -nat.gcd_a a b + ↑b * ↑k,
y : ℤ := nat.gcd_b a b + ↑a * ↑k,
hx : x = ↑(nat_abs x),
hy : y = ↑(nat_abs y)
⊢ ↑(a * nat_abs x + 1) = ↑(b * nat_abs y)

Mario Carneiro (Jun 24 2019 at 06:52):

this works for me:

import data.int.gcd
open nat int
lemma baz (a : ) (b : ) (h : nat.gcd a b = 1) :  x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -nat.gcd_a a b + b*k,
  let y := nat.gcd_b a b + a*k,
  have hx : x = nat_abs x := sorry,
  have hy : y = nat_abs y := sorry,
  existsi nat_abs x,
  existsi nat_abs y,
  apply int.coe_nat_inj,
  calc
    (a*(nat_abs x) + 1 : ) = a * x + 1 : by simp [hx.symm]
      ... = - a*nat.gcd_a a b + nat.gcd a b + a*b*k : by simp [mul_add, mul_assoc, add_assoc, h]
      ... = - a*nat.gcd_a a b + (a*nat.gcd_a a b + b*nat.gcd_b a b) + a*b*k : by rw gcd_eq_gcd_ab
      ... = b*nat.gcd_b a b + b*a*k : by simp [add_neg_self, mul_comm]
      ... = b * y : by simp [mul_add, mul_assoc]
      ... = (b * (nat_abs y) : ) : by simp [hy.symm]
end

Mario Carneiro (Jun 24 2019 at 06:54):

it's not clear to me what you have open, but it seems to work with nat and int open

Mario Carneiro (Jun 24 2019 at 06:55):

maybe you have something else imported so that gcd means something else?

Adrian Chu (Jun 24 2019 at 07:14):

I opened euclidean domain, thats why. but actually i don't need it. Thanks!

Adrian Chu (Jun 24 2019 at 09:45):

lemma baz (a : ) (b : ) (h : nat.gcd a b = 1) :  x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -nat.gcd_a a b + b*k,
  let y := nat.gcd_b a b + a*k,
  have hxx : 0  x :=
    calc 0  -nat.gcd_a a b + b*nat_abs (gcd_a a b) : by sorry
    ...  -nat.gcd_a a b + b*(max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)))
           : by sorry --simp [le_max_left]
    ... = -nat.gcd_a a b + b*k : by sorry--simp
    ... = x : by simp,

this time is about the 2nd and 3rd sorry. why dont those corresponding commands (which I commented above) work?

Kevin Buzzard (Jun 24 2019 at 11:32):

I insert my canonical remark. [It would be easier for people like me if you could just post fully working code. I need to open things, maybe import things, etc; can you do this part of the job for me please?].

Kevin Buzzard (Jun 24 2019 at 11:34):

But the answer to your question might be that the first sorry won't work because simp is designed to prove equalities, not inequalities, and the second proof should probably be rfl because it looks to me like it's true by definition.

Kevin Buzzard (Jun 24 2019 at 11:36):

All the simplifier does is that it proves things of the form X = Y by attempting to simplify both sides into a canonical form and then checking that they're true by definition. A simp lemma is of the form A = B, and if A is a sub-term in X then the simplifier will replace A by B; that's why simp lemmas should have (complicated) = (simpler) in that order.

Adrian Chu (Jun 24 2019 at 11:42):

I insert my canonical remark. [It would be easier for people like me if you could just post fully working code. I need to open things, maybe import things, etc; can you do this part of the job for me please?].

ok sure!

import data.int.basic data.int.gcd
open nat int

lemma baz (a : ) (b : ) (h : nat.gcd a b = 1) :  x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -gcd_a a b + b*k,
  let y := gcd_b a b + a*k,
  have hxx : x  0 :=
    calc 0  -gcd_a a b + b*nat_abs (gcd_a a b) : by sorry
    ...  -gcd_a a b + b*(max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b))) : by sorry --simp [le_max_left]
    ... = -gcd_a a b + b*k : sorry
    ... = x : by simp,
  have hx : x = nat_abs x := (nat_abs_of_nonneg hxx).symm,
  have hy : y = nat_abs y := sorry,
  existsi nat_abs x,
  existsi nat_abs y,
  apply int.coe_nat_inj,
  calc
    (a*(nat_abs x) + 1 : ) = a * x + 1 : by simp [hx.symm]
      ... = - a*gcd_a a b + nat.gcd a b + a*b*k : by simp [mul_add, mul_assoc, add_assoc, h]
      ... = - a*gcd_a a b + (a*gcd_a a b + b*gcd_b a b) + a*b*k : by rw gcd_eq_gcd_ab
      ... = b*gcd_b a b + b*a*k : by simp [add_neg_self, mul_comm]
      ... = b * y : by simp [mul_add, mul_assoc]
      ... = (b * (nat_abs y) : ) : by simp [hy.symm]
end

Adrian Chu (Jun 24 2019 at 11:44):

for the 3rd sorry, rfl doesnt work...

Adrian Chu (Jun 24 2019 at 11:44):

All the simplifier does is that it proves things of the form X = Y by attempting to simplify both sides into a canonical form and then checking that they're true by definition. A simp lemma is of the form A = B, and if A is a sub-term in X then the simplifier will replace A by B; that's why simp lemmas should have (complicated) = (simpler) in that order.

ah, i see

Kevin Buzzard (Jun 24 2019 at 11:47):

    calc 0  -gcd_a a b + b*nat_abs (gcd_a a b) : by sorry

a=1 and b=0 is a counterexample to this. This is the problem with skipping stuff -- even if your later two sorries are fixed the code might be unusable anyway.

Adrian Chu (Jun 24 2019 at 11:49):

no, we cant have a=1, b=0 because of the hypothesis h

Mario Carneiro (Jun 24 2019 at 11:49):

1 and 0 are coprime

Kevin Buzzard (Jun 24 2019 at 11:49):

The problem with the final sorry is that k is defined using the max on nat, and the other max is on int.

Adrian Chu (Jun 24 2019 at 11:50):

oh no! :(

Adrian Chu (Jun 24 2019 at 11:50):

i need to add more hypothesis then

Adrian Chu (Jun 24 2019 at 11:51):

The problem with the final sorry is that k is defined using the max on nat, and the other max is on int.

how should I get around?

Kevin Buzzard (Jun 24 2019 at 11:54):

There might be some magic tactic which does it, I've not tried those new cast tactics. If not, then you have to fix things up yourself.

My recommendation would be to change every single variable into an int as soon as possible, make all the problems go away, and then just prove that various things are >= 0 at the end and then cast them back to nats.

Mario Carneiro (Jun 24 2019 at 11:54):

I would keep the max on nat, because that's the definition of k

Kevin Buzzard (Jun 24 2019 at 11:54):

You're just in the typical nat/int hell which several of my students found themselves in over the summer. In my mind, if your proof uses ints, then why even use nats at all? Just use ints with a proof that they're >= 0.

Mario Carneiro (Jun 24 2019 at 11:55):

it might help to write some up arrows because it's important to know where they are

Kevin Buzzard (Jun 24 2019 at 11:55):

Maybe this would be a great place to test out the cast tactics. Did they get written up in the tactics docs?

Kevin Buzzard (Jun 24 2019 at 11:56):

https://github.com/leanprover-community/mathlib/blob/master/docs/tactics.md

They're right at the bottom. Maybe these can help. I've never used them though.

Mario Carneiro (Jun 24 2019 at 11:56):

to use the cast tactics, you have to actually have a specific goal that you can use them with

Mario Carneiro (Jun 24 2019 at 11:57):

distributing the arrows in this case is easily enough done by simp, that's already working in the example

Adrian Chu (Jun 24 2019 at 13:27):

yes, i should definitely use int instead of nat. because I have to add the condition that a, b >0 anyway, as you pointed out

Adrian Chu (Jun 24 2019 at 13:36):

but since gcd_a and gcd_b are for nat instead of int, other troubles will arise...

Adrian Chu (Jun 24 2019 at 14:24):

import data.int.basic data.int.gcd
open nat int

lemma baz (a : ) (b : ) (ha : a > 0) (hb : b > 0) (h : nat.gcd a b = 1) :
     x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -gcd_a a b + b*k,
  let y := gcd_b a b + a*k,
  have hxx : x  0 :=
    calc 0  -gcd_a a b + b*(nat_abs (gcd_a a b)) : by sorry
    ... = -gcd_a a b + b*(nat_abs (gcd_a a b)) : by simp
    ...  -gcd_a a b + b*(max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b))) :
        by sorry -- rw le_max_left
    ... = -gcd_a a b + b*k : by simp
    ... = x : by simp,
  have hx : x = nat_abs x := (nat_abs_of_nonneg hxx).symm,
  have hy : y = nat_abs y := sorry,
  existsi nat_abs x,
  existsi nat_abs y,
  apply int.coe_nat_inj,
  calc
    (a*(nat_abs x) + 1 : ) = a * x + 1 : by simp [hx.symm]
      ... = - a*gcd_a a b + nat.gcd a b + a*b*k : by simp [mul_add, mul_assoc, add_assoc, h]
      ... = - a*gcd_a a b + (a*gcd_a a b + b*gcd_b a b) + a*b*k : by rw gcd_eq_gcd_ab
      ... = b*gcd_b a b + b*a*k : by simp [add_neg_self, mul_comm]
      ... = b * y : by simp [mul_add, mul_assoc]
      ... = (b * (nat_abs y) : ) : by simp [hy.symm]
end

Adrian Chu (Jun 24 2019 at 14:25):

now i have changed the definition of k so that everything should be in int. why rw le_max_left still doesnt work?

Adrian Chu (Jun 24 2019 at 15:05):

oh, i know why, its because i need a proof for (b:nat)(a:int)(c:int)(a<=c) : b*a <= b*c. i guess this is proven somewhere already?

Adrian Chu (Jun 24 2019 at 15:25):

import data.int.basic data.int.gcd
open nat int

lemma baz (a : ) (b : ) (ha : a  1) (hb : b  1) (h : nat.gcd a b = 1) :
     x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -gcd_a a b + b*k,
  let y := gcd_b a b + a*k,
  have hxx : x  0 :=
    calc (0 : ) = 0 * nat_abs (gcd_a a b) : by simp
    ...  (b - 1) * (nat_abs (gcd_a a b)) : by sorry -- rw hb ...?
    ...  (b - 1) * (nat_abs (gcd_a a b)) : by simp
    ... = b*(nat_abs (gcd_a a b)) - 1*(nat_abs (gcd_a a b))
        : sub_mul (b) (1) (nat_abs (gcd_a a b))
    ...  b*(nat_abs (gcd_a a b)) - gcd_a a b : by sorry -- ...?
    ... = -gcd_a a b + b*(nat_abs (gcd_a a b)) : by simp
    ...  -gcd_a a b + b*(max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b))) :
        by sorry -- rw le_max_left ...?
    ... = -gcd_a a b + b*k : by simp
    ... = x : by simp,
  have hx : x = nat_abs x := (nat_abs_of_nonneg hxx).symm,
  have hy : y = nat_abs y := sorry,
  existsi nat_abs x,
  existsi nat_abs y,
  apply int.coe_nat_inj,
  calc
    (a*(nat_abs x) + 1 : ) = a * x + 1 : by simp [hx.symm]
      ... = - a*gcd_a a b + nat.gcd a b + a*b*k : by simp [mul_add, mul_assoc, add_assoc, h]
      ... = - a*gcd_a a b + (a*gcd_a a b + b*gcd_b a b) + a*b*k : by rw gcd_eq_gcd_ab
      ... = b*gcd_b a b + b*a*k : by simp [add_neg_self, mul_comm]
      ... = b * y : by simp [mul_add, mul_assoc]
      ... = (b * (nat_abs y) : ) : by simp [hy.symm]
end

Adrian Chu (Jun 24 2019 at 15:29):

OK, I can finally see the end. Only 3 sorry's (the first 3) are left to be filled, which I believe can be done using existing theorems. Can someone please tell me what the right commands are? Thanks!

Kevin Buzzard (Jun 24 2019 at 15:53):

Did you try library search?

Adrian Chu (Jun 24 2019 at 15:58):

How to do library_search? :exhausted:

Reid Barton (Jun 24 2019 at 15:58):

Read the documentation for library_search?

Reid Barton (Jun 24 2019 at 16:00):

in https://github.com/leanprover-community/mathlib/blob/master/docs/tactics.md

Adrian Chu (Jun 24 2019 at 16:08):

Read the documentation for library_search?

Ya, I just noticed this, embarrassing lol

Adrian Chu (Jun 24 2019 at 16:16):

library_search times out :(

Kevin Buzzard (Jun 24 2019 at 16:24):

So your first sorry is a proof of

 0 * nat_abs (gcd_a a b) ≤ (b - 1) * (nat_abs (gcd_a a b))

You wrote rw hb, and here hb : b >= 1. NB that's not the canonical way to write that inequality, the canonical way is 1 <= b. Yes, it does make a difference :-/

But you probably know what rw really does. For rw h to work, h must really be of the form A = B, and then rw takes the A's and replaces them with B's. So this is not a rewrite at all. What it is is a theorem. It's the theorem that x <= y and c >= 0 then x * c <= y * c. And that theorem will have a name, which you can either guess (once you understand the naming conventions), remember (once you've used Lean for a while) or look up using library-search (if it works).

Kevin Buzzard (Jun 24 2019 at 16:25):

example (a b c : ) (h : 0  c) (h2 : a  b) : a * c  b * c := by library_search -- works!

Kevin Buzzard (Jun 24 2019 at 16:30):

Your second sorry seems to be of the form

↑b*↑(nat_abs (gcd_a a b)) - ↑1*↑(nat_abs (gcd_a a b)) ≤ b*(nat_abs (gcd_a a b)) - gcd_a a b

Why are you proving an inequality? It looks to me like this should be an equality. It seems to me that there are three issues here. The first is the inequality. The second is that a bunch of arrows disappeared (either because you didn't write them and they're still there really, or because you really are switching from integers to naturals). To sort that out you'll either need some cast tactic, or you should just work with integers all the way through. The final thing is the issue that you are implicitly assuming 1 * x = x. That's a theorem, it's not true by definition, so it will have a name (I guess the name is one_mul), and that theorem needs to be applied somehow (by rewriting I guess) to get rid of that 1.

Kevin Buzzard (Jun 24 2019 at 16:39):

The third sorry is

-gcd_a a b + b*↑(nat_abs (gcd_a a b))
 ≤ -gcd_a a b + b*(max ↑(nat_abs (gcd_a a b)) ↑(nat_abs (gcd_b a b)))

You want to use le_max_left but again there are several steps which you're leaving out. As well as le_max_left there's the analogous theorem for the first sorry, but this time with conclusion c * a <= c * b (different conclusion = different theorem name), and you're also using the fact that a <= b implies c + a <= c + b. I think the simplifier is not well suited for these questions -- but I might be wrong. In some sense this is the problem with calc mode. You could just enter tactic mode again with a begin end, and then do the rewriting yourself.

    calc (0 : ) = 0 * nat_abs (gcd_a a b) : by simp
    ...  (b - 1) * (nat_abs (gcd_a a b)) : by sorry -- rw hb ...?
    ...  (b - 1) * (nat_abs (gcd_a a b)) : by simp
    ... = b*(nat_abs (gcd_a a b)) - 1*(nat_abs (gcd_a a b))
        : sub_mul (b) (1) (nat_abs (gcd_a a b))
    ...  b*(nat_abs (gcd_a a b)) - gcd_a a b : by sorry -- ...?
    ... = -gcd_a a b + b*(nat_abs (gcd_a a b)) : by simp
    ...  -gcd_a a b + b*(max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b))) :
        begin
          apply add_le_add_left,
          apply mul_le_mul_of_nonneg_left,
            exact le_max_left _ _,
          -- etc
          sorry
        end
    ... = -gcd_a a b + b*k : by simp

Tactic mode is the best mode really. You wrote rw le_max_left but that doesn't even make sense; you can only rewrite equalities. You are not thinking about what is really happening. What is really happening is that you are constantly applying little lemmas. The proof of c + d * a <= c + d * (max a b) is really not a rewrite. It is an application of several unrelated facts about inequalities, each of which has been painstakingly proved by the library creators. One of them is le_max_left but there are others.

Adrian Chu (Jun 24 2019 at 16:43):

import data.int.basic data.int.gcd tactic.library_search algebra.ordered_ring
open nat int

lemma baz (a : ) (b : ) (ha : a  1) (hb : b  1) (h : nat.gcd a b = 1) :
     x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b)),
  let x := -gcd_a a b + b*k,
  let y := gcd_b a b + a*k,
  have hg : 0  nat_abs (gcd_a a b) := by simp,
  have hb : 0  b - 1 := by simp,
  have hxx : x  0 :=
    calc (0 : ) = 0 * nat_abs (gcd_a a b) : by simp
    ...  (b - 1) * (nat_abs (gcd_a a b)) :
        by exact mul_le_mul_of_nonneg_right hb hg
    ...  (b - 1) * (nat_abs (gcd_a a b)) : by simp
    ... = b*(nat_abs (gcd_a a b)) - 1*(nat_abs (gcd_a a b)) :
        sub_mul (b) (1) (nat_abs (gcd_a a b))
    ... = b*(nat_abs (gcd_a a b)) - nat_abs (gcd_a a b) : by simp
    ...  b*(nat_abs (gcd_a a b)) - gcd_a a b : by sorry
    ... = -gcd_a a b + b*(nat_abs (gcd_a a b)) : by simp
    ...  -gcd_a a b + b*(max (nat_abs (gcd_a a b)) (nat_abs (gcd_b a b))) :
        by sorry -- rw le_max_left ...?
    ... = -gcd_a a b + b*k : by simp
    ... = x : by simp,
  have hx : x = nat_abs x := (nat_abs_of_nonneg hxx).symm,
  have hy : y = nat_abs y := sorry,
  existsi nat_abs x,
  existsi nat_abs y,
  apply int.coe_nat_inj,
  calc
    (a*(nat_abs x) + 1 : ) = a * x + 1 : by simp [hx.symm]
      ... = - a*gcd_a a b + nat.gcd a b + a*b*k : by simp [mul_add, mul_assoc, add_assoc, h]
      ... = - a*gcd_a a b + (a*gcd_a a b + b*gcd_b a b) + a*b*k : by rw gcd_eq_gcd_ab
      ... = b*gcd_b a b + b*a*k : by simp [add_neg_self, mul_comm]
      ... = b * y : by simp [mul_add, mul_assoc]
      ... = (b * (nat_abs y) : ) : by simp [hy.symm]
end

Adrian Chu (Jun 24 2019 at 16:44):

This is my attempt to resolve the first sorry, but I got an enormous error message. As for the second sorry, it should be an inequality since gcd_a can be negative. I will come back to this tomorrow, its so late at night. Anyway, thanks~

Kevin Buzzard (Jun 24 2019 at 16:48):

As for the second sorry, it should be an inequality since gcd_a can be negative.

Oh, apologies! I thought these were the nat ones. In which case my comments apply about how things like a <= b implies c - a >= c - b are not immediate or automatic, they are theorems which need applying. You'll also need one_mul, and the fact that z <= abs z.

Kevin Buzzard (Jun 24 2019 at 16:52):

The enormous error message is only enormous because Lean has expanded everything out for you to explain what the problem is. If it just said "I was expecting a proof that something was <= something, and you gave me a proof that something was <= something" then it would be confusing. But

type mismatch at application
  mul_le_mul_of_nonneg_right hb
term
  hb
has type
  @has_le.le nat nat.has_le 0
    (@has_sub.sub nat nat.has_sub
       (@coe nat nat (@coe_to_lift nat nat (@coe_base nat nat (@nat.cast_coe nat nat.has_zero nat.has_one nat.has_add)))
          b)
       1)
but is expected to have type
  @has_le.le int
    (@preorder.to_has_le int
       ...

says that hb has type @has_le.le nat ... (i.e. it's a proof that one nat is <= another nat) and you attempted to insert it into a function which was expecting something of type @has_le.le int ... i.e. expecting a proof that one int is <= another int.

Kevin Buzzard (Jun 24 2019 at 16:55):

replacing by exact mul_le_mul_of_nonneg_right with the more refined "attempt to solve the goal with this function but give me the inputs as new goals" refine tactic

begin refine mul_le_mul_of_nonneg_right _ _, sorry, sorry end

shows you what the problem is. You now need to fill in those two holes, and the results you have are not good enough because they have the wrong type. Did I mention the idea of just using integers everywhere by the way?

Adrian Chu (Jun 24 2019 at 17:00):

Did I mention the idea of just using integers everywhere by the way?

but gcd_a, gcd_b are defined for nat instead of int

Kevin Buzzard (Jun 24 2019 at 17:00):

But this issue is just a general pain in type theory. Do you understand how to use these new cast tactics? The first hole needs a resolution of this issue

hb : 0 ≤ ↑b - 1
⊢ 0 ≤ ↑b - 1

Here your hypothesis hb has an arrow in, which turns out to be a cast from nat to nat. You can see all the gory details of everything by set_option pp.all true. Setting this option will give you some real insight into how the computer is thinking about what you are doing. In particular you cannot cast from a nat to an int by just putting an up-arrow -- Lean doesn't know where you're casting to.

Kevin Buzzard (Jun 24 2019 at 17:01):

Did I mention the idea of just using integers everywhere by the way?

but gcd_a, gcd_b are defined for nat instead of int

Feed them absolute values of ints? I don't know if this is a feasible solution.

Adrian Chu (Jun 24 2019 at 17:04):

Did I mention the idea of just using integers everywhere by the way?

but gcd_a, gcd_b are defined for nat instead of int

Feed them absolute values of ints? I don't know if this is a feasible solution.

i will try tomorrow

Kevin Buzzard (Jun 24 2019 at 17:11):

I don't know if this is feasible. All I see when I look at your code though is a bunch of stuff about ints, with subtractions etc. You might well find the int analogue of the gcd_a functions in data.int.basic somewhere or maybe even in core.

Kevin Buzzard (Jun 24 2019 at 17:27):

Maybe now we have norm_cast you don't need to make everything an integer. This little lemma might be a nice test case. I just tried it on the first sorry and had positive results:

    calc (0 : ℤ) = 0 * nat_abs (gcd_a a b) : by simp
    ... ≤ (↑b - 1) * (nat_abs (gcd_a a b)) :
        begin apply mul_le_mul_of_nonneg_right,
          {norm_cast at hb ⊢, exact hb},
          {norm_cast, simp}
        end

Kevin Buzzard (Jun 24 2019 at 17:32):

For the next one you need the theorem that a <= b implies c - b <= c - a (note how I always stick with <=, otherwise there would be lots of ways of saying inequalities; there is a "canonical form" for many expressions in Lean and it is only slowly dawning on me how important this is in practice). To find that theorem you see that the conclusion is of the form "a subtraction is less than or equal to a subtraction" so I type "apply sub_le_sub" and then I press ctrl-space in VS Code and see a list of all Lean's theorems that start sub_le_sub and all their types too, so it's easy to find the one I want. That's another way of finding out the right name for a theorem.

Patrick Massot (Jun 24 2019 at 18:57):

@Adrian Chu I don't think you're going in the right direction. The first thing to understand is natural numbers are bad. You never noticed because real world always insert coercions to integers and all the coercion related lemmas. My advice is to first state and prove the integer version. Then we'll talk about deducing the evil version. Here is the exercise I propose:

import algebra.euclidean_domain
import tactic.linarith
open euclidean_domain

lemma foo (a b : ) (ha : a  1) (hb : b  1) (h : gcd a b = 1) :
     x y, a*x + 1 = b*y  0  x  0  y :=
begin
  let u := gcd_a a b,
  let v := gcd_b a b,
  let k := max (abs u) (abs v),
  use [-u+b*k, v + a*k],
  repeat { split },
  { rw show 1 = a*u + b*v, from sorry,
    ring },
  { suffices : u  b * max (abs u) (abs v), by linarith,
    -- now you can `calc`
    sorry },
  { sorry }
end

Patrick Massot (Jun 24 2019 at 18:58):

each calc block is 4 lines long

Patrick Massot (Jun 24 2019 at 18:59):

The first sorry is 20 characters (including spaces)

Patrick Massot (Jun 24 2019 at 19:16):

Hmm, do we have any lemma relating nat.gcd and euclidean_domain.gcd?

Patrick Massot (Jun 24 2019 at 19:19):

@Johan Commelin maybe?

Johan Commelin (Jun 24 2019 at 19:19):

I'm not too familiar with that part of the lib...

Patrick Massot (Jun 24 2019 at 19:19):

@Chris Hughes ?

Johan Commelin (Jun 24 2019 at 19:20):

I don't know, but I think there is glue between nat.gcd and int.gcd, and then also between int.gcd and euclidean_domain.gcd...

Patrick Massot (Jun 24 2019 at 19:20):

Where would that be?

Johan Commelin (Jun 24 2019 at 19:21):

Not sure... my memories might be wrong.

Patrick Massot (Jun 24 2019 at 19:22):

Going from the int version of Adrian's lemma to the evil version is trivial except for:

a b : ,
h : nat.gcd a b = 1
 gcd a b = 1

Johan Commelin (Jun 24 2019 at 19:25):

Hmm... if library_search can't close that, then we probably don't have it.

Patrick Massot (Jun 24 2019 at 19:27):

I'm sure British people can prove this

Kevin Buzzard (Jun 24 2019 at 19:32):

Maybe show they both have the same universal property? ;-)

Kevin Buzzard (Jun 24 2019 at 19:36):

example (a b : ) (h : nat.gcd a b = 1) : int.gcd (a : ) (b : ) = 1 := by rw h; unfold int.gcd; congr'

Kevin Buzzard (Jun 24 2019 at 19:38):

example (a b : ) (h : nat.gcd a b = 1) : int.gcd (a : ) (b : ) = 1 := by convert h

Kevin Buzzard (Jun 24 2019 at 19:39):

I was slightly surprised this worked until I realised that int.nat_abs (\u m) = m was defeq for m a nat.

Patrick Massot (Jun 24 2019 at 19:40):

Nice! This bridges to int.gcd but not to euclidean_domain.gcd

Patrick Massot (Jun 24 2019 at 19:49):

and the by convert is useless...

Kevin Buzzard (Jun 24 2019 at 19:50):

example (a b : ) (h : nat.gcd a b = 1) : int.gcd (a : ) (b : ) = 1 := h

Oh yeah! I used convert because I "knew" there would be trouble with the cast to int and back.

Patrick Massot (Jun 24 2019 at 19:51):

but the game is to use euclidean_domain.gcd

Kevin Buzzard (Jun 24 2019 at 20:00):

#eval euclidean_domain.gcd (-1 : ℤ) (1 : ℤ) -- -1
#eval int.gcd (-1 : ℤ) (1 : ℤ) -- +1

:-(

Kevin Buzzard (Jun 24 2019 at 20:08):

example (a b : ) : int.nat_abs (euclidean_domain.gcd a b) = int.gcd a b :=
begin
  apply gcd.induction a b,
    intro x, rw gcd_zero_left, exact (nat.gcd_zero_left (int.nat_abs x)).symm,
  intros a b ha hab,
  convert hab using 1,
    rw gcd_val a b,
  -- ⊢ int.gcd a b = int.gcd (b % a) a
  sorry
end

This code (thanks, whoever wrote gcd.induction!) reduces the question to int.gcd a b = int.gcd (b % a) a.

Kevin Buzzard (Jun 24 2019 at 20:09):

...which is almost a question about nat.gcd apart from the fact that one needs to relate |b%a| to |b|%|a|.

Patrick Massot (Jun 24 2019 at 20:13):

What you are doing is not what I asked for

Kevin Buzzard (Jun 24 2019 at 20:13):

why don't you formalise the question?

Patrick Massot (Jun 24 2019 at 20:13):

I did!

Kevin Buzzard (Jun 24 2019 at 20:14):

Oh -- I misunderstood the arrows.

Patrick Massot (Jun 24 2019 at 20:14):

Going from the int version of Adrian's lemma to the evil version is trivial except for:

a b : ,
h : nat.gcd a b = 1
 gcd a b = 1

Kevin Buzzard (Jun 24 2019 at 20:14):

I thought I did that one.

Kevin Buzzard (Jun 24 2019 at 20:14):

I have Lean code which looks like that on my screen right now, with int open :-)

Patrick Massot (Jun 24 2019 at 20:14):

You need to open euclidean_domain instead

Kevin Buzzard (Jun 24 2019 at 20:15):

you didn't post a MWE ;-)

Patrick Massot (Jun 24 2019 at 20:15):

I did

Patrick Massot (Jun 24 2019 at 20:15):

a few messages above

Patrick Massot (Jun 24 2019 at 20:15):

anyway

Kevin Buzzard (Jun 24 2019 at 20:15):

It was too minimal -- I guessed you'd opened the wrong thing :-)

I still propose we prove the thing I said about int. You can't use the inductive predicate for euclidean_domain.gcd on nats.

Kevin Buzzard (Jun 24 2019 at 20:16):

So unless you want to get your hands dirty, we prove some statement which is valid for all ints (as I was in the middle of doing) and then deduce the nat thing via some dirty work.

Patrick Massot (Jun 24 2019 at 20:16):

The context was my first message to Adrian, which included all imports and open

Kevin Buzzard (Jun 24 2019 at 20:17):

And the best statement I could find which was true for all ints was the one I posted.

Kevin Buzzard (Jun 24 2019 at 20:20):

bleurgh we need (a b : int), both >=0 implies euclidean_domain.gcd a b >=0 :-/

Kevin Buzzard (Jun 24 2019 at 20:37):

theorem useful (a b : ) : int.nat_abs (b % a) = (int.nat_abs b) % (int.nat_abs a) := sorry

Meh.

Kevin Buzzard (Jun 24 2019 at 20:56):

import algebra.euclidean_domain

open euclidean_domain

theorem useful (a b : ) : int.nat_abs (b % a) = (int.nat_abs b) % (int.nat_abs a) := sorry

theorem useful2 (a b : ) : a  0  b  0  euclidean_domain.gcd a b  0 := sorry

theorem abs_gcd_eq_int_gcd (a b : ) : int.nat_abs (euclidean_domain.gcd a b) = int.gcd a b :=
begin
  apply gcd.induction a b,
    intro x, rw gcd_zero_left, exact (nat.gcd_zero_left (int.nat_abs x)).symm,
  intros a b ha hab,
  convert hab using 1,
    rw gcd_val a b,
  --goal now : a ≠ 0 → int.gcd a b = int.gcd (b % a) a -- should be in mathlib
  unfold int.gcd,
  rw useful,
  have h :  x y : , x  0  nat.gcd x y = nat.gcd (y % x) x,
  { intros x y hx,
    cases x with x, revert hx, simp,
    simp -- nat.gcd equation lemma
  },
  have h2 : int.nat_abs a  0 := λ h3, ha (int.eq_zero_of_nat_abs_eq_zero h3), -- missing a trick here
  generalize h3 : int.nat_abs a = A,
  generalize : int.nat_abs b = B,
  apply h, rwa h3,
end

theorem patrick (a b : ) (h : nat.gcd a b = 1) : euclidean_domain.gcd (a : ) b = 1 :=
begin
  show gcd (a : ) b = (1 : ),
  rw h,
  show _ = (int.gcd a b : ),
  rw abs_gcd_eq_int_gcd (a : ) b,
  convert (int.of_nat_nat_abs_eq_of_nonneg _).symm,
  -- ⊢ euclidean_domain.gcd ↑a ↑b ≥ 0
  apply useful2; exact int.of_nat_nonneg _,
end

Two sorries at the top. @Chris Hughes @Mario Carneiro what tricks am I missing?

Kevin Buzzard (Jun 24 2019 at 20:56):

The goal is to prove theorem patrick.

Kevin Buzzard (Jun 24 2019 at 21:37):

theorem useful2 (a b : ) : a  0  b  0  euclidean_domain.gcd a b  0 :=
begin
  apply gcd.induction a b,
    intros x h hx, rwa gcd_zero_left,
  intros c d hcn h hc hd,
  rw gcd_val,
  apply h _ hc,
  exact int.mod_nonneg d hcn,
end

Chris Hughes (Jun 24 2019 at 21:48):

Personally I would question why you want to prove that theorem?

Mario Carneiro (Jun 24 2019 at 21:57):

I don't see why euclidean_domain.gcd is getting involved at all if the goal is to prove adrian's original statement

Kevin Buzzard (Jun 24 2019 at 22:17):

Yeah I already tried that but Patrick moaned

Kevin Buzzard (Jun 24 2019 at 22:18):

I'm having trouble working with -A % B with (A B : nat) and int.mod

Kevin Buzzard (Jun 24 2019 at 22:19):

1 goal
B : ℕ,
A : ℕ,
⊢ int.nat_abs (-↑A % ↑B) = int.nat_abs (-↑A) % B

Kevin Buzzard (Jun 24 2019 at 22:34):

That's the last goal but it's bedtime

Kevin Buzzard (Jun 24 2019 at 22:36):

The left mod is int.mod

Kevin Buzzard (Jun 24 2019 at 22:45):

Aargh I don't think some of these are true :-/

Kevin Buzzard (Jun 24 2019 at 22:45):

No wonder I was struggling :-)

Kevin Buzzard (Jun 24 2019 at 22:46):

Do we have a tool which checks statements like these for a few random values of the integers in question?

Mario Carneiro (Jun 24 2019 at 22:47):

sure, you can use list ops to make a mini quickcheck

Mario Carneiro (Jun 24 2019 at 22:49):

#eval do
  A  list.range 10,
  B  list.range 10,
  guard (int.nat_abs (-A % B)  int.nat_abs (-A) % B),
  return (A, B)
-- [(1, 3), ...]

Adrian Chu (Jun 25 2019 at 15:14):

Adrian Chu I don't think you're going in the right direction. The first thing to understand is natural numbers are bad. You never noticed because real world always insert coercions to integers and all the coercion related lemmas. My advice is to first state and prove the integer version. Then we'll talk about deducing the evil version. Here is the exercise I propose:

import algebra.euclidean_domain
import tactic.linarith
open euclidean_domain

lemma foo (a b : ) (ha : a  1) (hb : b  1) (h : gcd a b = 1) :
     x y, a*x + 1 = b*y  0  x  0  y :=
begin
  let u := gcd_a a b,
  let v := gcd_b a b,
  let k := max (abs u) (abs v),
  use [-u+b*k, v + a*k],
  repeat { split },
  { rw show 1 = a*u + b*v, from sorry,
    ring },
  { suffices : u  b * max (abs u) (abs v), by linarith,
    -- now you can `calc`
    sorry },
  { sorry }
end

So i guess i should try finishing this version of my lemma, and forget everything about nat.gcd

Patrick Massot (Jun 25 2019 at 15:17):

If you can afford that then of course everything becomes much easier. I still think it's a problem that mathlib has at least three gcd with no lemmas relating them in the case of integers

Patrick Massot (Jun 25 2019 at 15:17):

Did you manage to fill in the sorries in my exercise?

Adrian Chu (Jun 25 2019 at 15:18):

I am working on them now

Patrick Massot (Jun 25 2019 at 15:20):

Ok, I hope I inserted them wisely. I first wrote the full proof and then removed it to help you getting better training. Don't hesitate to ask questions if it's still too painful

Adrian Chu (Jun 25 2019 at 15:50):

import algebra.euclidean_domain
import tactic.linarith tactic.library_search
open euclidean_domain

lemma foo (a b : ) (ha : a  1) (hb : b  1) (h : gcd a b = 1) :
     x y, a*x + 1 = b*y  0  x  0  y :=
begin
  let u := gcd_a a b,
  let v := gcd_b a b,
  let k := max (abs u) (abs v),
  have hk : 0  k :=
    calc 0  abs u : abs_nonneg u
    ...  max (abs u) (abs v) : le_max_left (abs u) (abs v)
    ... = k : by simp,
  use [-u+b*k, v + a*k],
  repeat { split },
  { rw show 1 = a*u + b*v, from sorry,
    ring },
  { suffices : u  b * max (abs u) (abs v), by linarith,
    calc u  abs u : by exact le_max_left u (-u)
    ...  max (abs u) (abs v) : le_max_left (abs u) (abs v)
    ... = 1 * max (abs u) (abs v) : by simp
    ...  b * max (abs u) (abs v) : by exact mul_le_mul_of_nonneg_right hb hk },
  { suffices : -v  a * max (abs u) (abs v), by linarith,
    calc -v  abs (-v) : by exact le_max_left (-v) (-(-v))
    ... = abs v : abs_neg v
    ...  max (abs u) (abs v) : le_max_right (abs u) (abs v)
    ... = 1 * max (abs u) (abs v) : by simp
    ...  a * max (abs u) (abs v) : by exact mul_le_mul_of_nonneg_right ha hk}
end

Adrian Chu (Jun 25 2019 at 15:50):

I'm not able to figure out the first sorry (with 20 characters only). i know we need to use gcd_eq_gcd_ab @Patrick Massot

Adrian Chu (Jun 25 2019 at 15:55):

Personally I would question why you want to prove that theorem?

This is a crucial lemma in a theorem i want to prove

Kevin Buzzard (Jun 25 2019 at 15:57):

Personally I would question why you want to prove that theorem?

This is a crucial lemma in a theorem i want to prove

I think that "that theorem" might have referred to the compatibility of the various notions of gcd which we have in Lean.

Patrick Massot (Jun 25 2019 at 16:06):

You can shorten your first calc block to

calc 0  abs u : abs_nonneg u
      ...  _ : le_max_left _ _

(or of course you can say have hk : 0 ≤ k, from le_trans (abs_nonneg u) (le_max_left _ _),)

Patrick Massot (Jun 25 2019 at 16:06):

The sorry you couldn't do is h ▸ gcd_eq_gcd_ab a b

Patrick Massot (Jun 25 2019 at 16:07):

I hope you've learn a couple of tricks (those are no so easy to document)

Adrian Chu (Jun 25 2019 at 16:09):

wow thanks a lot!

Adrian Chu (Jun 25 2019 at 16:09):

i can finally move one to the second lemma, and then my main theorem :)

Kevin Buzzard (Jun 25 2019 at 16:09):

The triangle is term mode's version of the rewrite tactic.

Adrian Chu (Jun 25 2019 at 16:53):

import algebra.euclidean_domain algebra.big_operators data.finset

open euclidean_domain

def fin_n_to_list {n : } (x: fin n  ) : list  :=
  (list.range n).map (λ i, if h : i < n then x i, h else 0)

def n_lcm {n : } (x : fin n  ) :  :=
  list.foldl lcm 1 (fin_n_to_list x)

lemma n_lcm_coprime {n : } (x : fin n  ) (y : )
  (hx :  i : fin n, 1  x i) (hy : 1  y)
  (coprime :  i : fin n, gcd (x i) y = 1) :
  gcd (n_lcm x) y = 1 :=
sorry

Adrian Chu (Jun 25 2019 at 16:54):

i will start to prove this lemma. is my formulation of definitions and the statement of lemma appropriate?

Kevin Buzzard (Jun 25 2019 at 17:07):

Why are you using maps fin n -> int at all? You are carrying around the length of your list, but you could just read it off by looking at the length of the list.

Kevin Buzzard (Jun 25 2019 at 17:13):

import algebra.euclidean_domain algebra.big_operators data.finset

open euclidean_domain

def list.lcm (x : list ) :  :=
  list.foldl lcm 1 x

lemma list.lcm_coprime (x : list ) (y : )
  (hx :  s  x, (1 : )  s) (hy : 1  y)
  (coprime :  s  x, gcd s y = 1) :
  gcd (x.lcm) y = 1 :=
sorry

No n in sight -- it's never needed.

Kevin Buzzard (Jun 25 2019 at 17:14):

Note also x.lcm for list.lcm x, it's a cool thing which I only recently understood.

Kevin Buzzard (Jun 25 2019 at 17:15):

The CS purists might even say that these should all be theorems about multisets not lists, because the lcm does not depend on the order, so why are you carrying that around?

Adrian Chu (Jun 26 2019 at 04:04):

a naive question: can a list have infinite length?

Mario Carneiro (Jun 26 2019 at 04:13):

No

Johan Commelin (Jun 26 2019 at 04:16):

Nope, they can not.

Johan Commelin (Jun 26 2019 at 04:16):

list.length is defined for all lists, and it is a function to nat.

Johan Commelin (Jun 26 2019 at 04:16):

Infinite lists are usually called "streams", and they need a different implementation. Not sure if we have them in Lean.

Johan Commelin (Jun 26 2019 at 04:16):

Sorry... laggy internet connection...

Mario Carneiro (Jun 26 2019 at 04:20):

we have streams, they are just defined as stream A := nat -> A

Adrian Chu (Jun 26 2019 at 04:21):

i see. all I want is an n-tuple of numbers. so i will use list Z instead of fin n -> Z.

Mario Carneiro (Jun 26 2019 at 04:21):

There are also lazy lists, which are more like haskell lists in implementation but are still finite according to the theory. But in meta land you can construct infinite lazy lists

Johan Commelin (Jun 26 2019 at 04:22):

i see. all I want is an n-tuple of numbers. so i will use list Z instead of fin n -> Z.

The question is... is your n fixed? Because with a list, you don't know if it has length n.

Adrian Chu (Jun 26 2019 at 04:25):

no, the lemma (and my main theorem) just need an arbitrary finite number of positive integers as inputs.

Adrian Chu (Jun 26 2019 at 04:26):

so list should be good

Adrian Chu (Jun 26 2019 at 04:48):

i just realized a problem. in my main theorem, i have a statement involving a^b, where a and b are positive integers. but since we are now letting a, b be int instead of nat, i cant use the default a^b function

Adrian Chu (Jun 26 2019 at 04:53):

well i can use (nat_abs a)^(nat_abs b). its a bit ugly, but nvm

Mario Carneiro (Jun 26 2019 at 05:13):

I think you should keep them as nats rather than do something like that

Mario Carneiro (Jun 26 2019 at 05:13):

if they are actually nats then there's nothing wrong with that

Johan Commelin (Jun 26 2019 at 05:28):

@Adrian Chu You can let a and b be nats but nevertheless use int.gcd instead of nat.gcd.

Johan Commelin (Jun 26 2019 at 05:28):

I agree that it's quite messy.

Adrian Chu (Jun 26 2019 at 05:45):

Adrian Chu You can let a and b be nats but nevertheless use int.gcd instead of nat.gcd.

I don't think we can, since this gives error:

theorem mythm (r : list ) (s : ) (hr :  z  r, 1  z) (hs : 1  s)
  (coprime :  z  r, int.gcd z s = 1) : 1 = 1 := sorry

Mario Carneiro (Jun 26 2019 at 05:47):

what error? You may need to put some up arrows in there

Adrian Chu (Jun 26 2019 at 05:49):

OK, fixed

Adrian Chu (Jun 26 2019 at 06:01):

let x : list nat and I want to sum it. if for some reason I want to use finset.univ.sum instead of x.sum, what is the correct syntax?

Adrian Chu (Jun 26 2019 at 06:04):

import data.fintype

variable x : list 
#check finset.univ.sum (λ i, x.nth i)
#check finset.univ.sum (λ i : fin x.range, x.nth i)

this doesnt work

Johan Commelin (Jun 26 2019 at 06:05):

You need to range of the length of the list.

Johan Commelin (Jun 26 2019 at 06:05):

Why do you want to use finset.sum?

Johan Commelin (Jun 26 2019 at 06:05):

If so... shouldn't you be using finset nat instead of list nat?

Adrian Chu (Jun 26 2019 at 06:09):

well I have x : list nat and r : list nat, lists of positive integers of the same length, and i want to sum (i-th term of x)^(i-th term of r) over i

Adrian Chu (Jun 26 2019 at 06:09):

i thought using finset.univ.sum is the easiest

Mario Carneiro (Jun 26 2019 at 06:10):

I would do something like zip_with to put the powers together and list.sum to add them up

Johan Commelin (Jun 26 2019 at 06:10):

well I have x : list nat and r : list nat, lists of positive integers of the same length, and i want to sum (i-th term of x)^(i-th term of r) over i

??? why are they of the same length?

Adrian Chu (Jun 26 2019 at 06:11):

well I have x : list nat and r : list nat, lists of positive integers of the same length, and i want to sum (i-th term of x)^(i-th term of r) over i

??? why are they of the same length?

by assumption

Scott Morrison (Jun 26 2019 at 07:13):

Can you arrange to have a list (nat x nat)?

Adrian Chu (Jun 26 2019 at 07:29):

Can you arrange to have a list (nat x nat)?

how will this help?

Marc Huisinga (Jun 26 2019 at 07:31):

it ensures that you have two lists of the same size, but is this really the easiest way to do it? why not pass a proof that the lengths are equal instead?

Mario Carneiro (Jun 26 2019 at 07:31):

it separates the information somewhat and generally makes the proofs harder

Mario Carneiro (Jun 26 2019 at 07:32):

I don't know about the direction of this thread though. What is the actual goal? I think the encoding decisions are being made without a good idea of the target theorem and that's bad news

Adrian Chu (Jun 26 2019 at 07:33):

okok, let me write out the complete thm

Adrian Chu (Jun 26 2019 at 07:37):

import data.fintype

theorem mythm (n : ) (r : fin n  ) (s : ) (hr :  i, 1  r i) (hs : 1  s)
  (coprime :  i : fin n, nat.coprime (r i) s) :
   x : fin n  ,  y : ,
  ( i, 1  x i)  (0  y)  (finset.univ.sum (λ i, (x i)^(r i)) = y^s) :=
sorry

Adrian Chu (Jun 26 2019 at 07:37):

this is the original form of my theorem

Adrian Chu (Jun 26 2019 at 07:38):

and we can discuss whether (1) to use nat or int, and (2) to use fin n -> nat or list nat

Marc Huisinga (Jun 26 2019 at 07:40):

the only adv of fin n -> nat i can think of is that fin n -> nat can be handy when dealing with nested inductive types

Adrian Chu (Jun 26 2019 at 07:43):

and I will use these 2 lemmas

import data.fintype
import algebra.euclidean_domain
import algebra.big_operators

open euclidean_domain

def fin_n_to_list {n : } (x: fin n  ) : list  :=
  (list.range n).map (λ i, if h : i < n then x i, h else 0)

def n_lcm {n : } (x : fin n  ) :  :=
  list.foldl nat.lcm 1 (fin_n_to_list x)

lemma n_lcm_coprime {n : } (x : fin n  ) (y : )
  (coprime :  i : fin n, nat.coprime (x i) y) :
  nat.coprime (n_lcm x) y := sorry -- not yet proven

lemma bazout (a b : ) (ha : a  1) (hb : b  1) (h : gcd a b = 1) :
     x y, a*x + 1 = b*y  0  x  0  y := sorry -- already proven

Mario Carneiro (Jun 26 2019 at 07:47):

The statement of mythm looks fine, except that you can replace fin n with any fintype A, and the 0 <= y is redundant

Adrian Chu (Jun 26 2019 at 09:58):

no... i just tried, the conversion between int and nat is so frustrating (since my bazout lemma is using int). i want to use int in mythm. do we have a^b for a, b in int ?

Mario Carneiro (Jun 26 2019 at 10:02):

You want to have your bezout lemma on nat too

Adrian Chu (Jun 26 2019 at 10:13):

good idea, i can make a new lemma bazout2 for nat using bazout

Adrian Chu (Jun 26 2019 at 10:22):

my strategy now is to use a, b : nat, but use euclidean_domain.gcd (a:int) b

Mario Carneiro (Jun 26 2019 at 10:30):

Since you seem to already have a solution, I will show you how I would have proven your original bezout theorem:

lemma bezout (a : ) (b : ) (ha : a  1) (hb : b  1) (h : nat.gcd a b = 1) :
     x : ,  y : , a*x + 1 = b*y :=
begin
  let k := max (int.nat_abs (nat.gcd_a a b)) (int.nat_abs (nat.gcd_b a b)) + 1,
  let x :  := b*k - nat.gcd_a a b,
  let y :  := a*k + nat.gcd_b a b,
  refine int.to_nat x, int.to_nat y, int.coe_nat_inj _⟩,
  suffices : (a * int.to_nat x + 1 : ) = b * int.to_nat y, {simpa},
  have k1 : 1  k := nat.le_add_left _ _,
  have ha' : (1:)  a := int.coe_nat_le.2 ha,
  have hb' : (1:)  b := int.coe_nat_le.2 hb,
  have x0 : 0  x,
  { refine sub_nonneg.2 _,
    have := mul_le_mul_of_nonneg_right hb' (int.coe_nat_nonneg k),
    rw one_mul at this,
    refine le_trans (le_trans int.le_nat_abs _) this,
    refine int.coe_nat_le.2 _,
    exact nat.le_succ_of_le (le_max_left _ _) },
  have y0 : 0  y,
  { refine sub_le_iff_le_add.1 _,
    rw zero_sub,
    have := mul_le_mul_of_nonneg_right ha' (int.coe_nat_nonneg k),
    rw one_mul at this,
    refine le_trans (le_trans int.le_nat_abs _) this,
    rw [int.nat_abs_neg, int.coe_nat_le],
    exact nat.le_succ_of_le (le_max_right _ _) },
  rw [int.to_nat_of_nonneg x0, int.to_nat_of_nonneg y0],
  have := nat.gcd_eq_gcd_ab a b,
  rw [h, int.coe_nat_one] at this,
  rw [this, mul_sub,  add_assoc, sub_add_cancel, mul_left_comm,  mul_add],
end

I don't think it's necessary to ban the use of nats as long as you are conscientious in your use of the up arrow. In particular if it makes you use more unusual functions with a worse interface then it's not worth it

Patrick Massot (Jun 26 2019 at 11:00):

I think this is completely crazy. How can it be a good idea to have nat.gcd, int.gcd and euclidean_domain.gcd and not a single lemma relating those?

Mario Carneiro (Jun 26 2019 at 11:00):

Of course there should be such lemmas

Mario Carneiro (Jun 26 2019 at 11:01):

but we don't need the other gcds for this theorem

Patrick Massot (Jun 26 2019 at 11:02):

The proof of this theorem is much nicer with euclidean_domain.gcd. Your proof is hideous, even if it could be nicer using norm_cast

Mario Carneiro (Jun 26 2019 at 11:02):

None of the proof has to do with gcd

Mario Carneiro (Jun 26 2019 at 11:03):

It's quite possible that norm_cast can be used in a few places, but it's mostly about dealing with max and simple algebra

Mario Carneiro (Jun 26 2019 at 11:03):

also I don't really care about calc blocks

Mario Carneiro (Jun 26 2019 at 11:05):

Are you saying that there is a completely different proof that uses euclidean_domain.gcd with some different lemmas about it? I'm mostly following adrian's proof sketch here

Kevin Buzzard (Jun 26 2019 at 11:06):

The fact that we have

theorem gcd_eq_gcd_ab : (gcd a b : ) = a * gcd_a a b + b * gcd_b a b := ...

and this proof is still nearly 30 lines is what is so jarring.

Mario Carneiro (Jun 26 2019 at 11:06):

This theorem is proving something completely different

Kevin Buzzard (Jun 26 2019 at 11:06):

Hmm, I guess gcd_a is probably an int, so there's some content getting all the signs right

Mario Carneiro (Jun 26 2019 at 11:07):

The important part is the fact that you can shift around solutions by a multiple

Kevin Buzzard (Jun 26 2019 at 11:07):

Right.

Mario Carneiro (Jun 26 2019 at 11:07):

and then you have some inequalities to check

Patrick Massot (Jun 26 2019 at 11:07):

I mean

lemma foo (a b : ) (ha : a  1) (hb : b  1) (h : gcd a b = 1) :
     x y, a*x + 1 = b*y  0  x  0  y :=
begin
  let u := gcd_a a b,
  let v := gcd_b a b,
  let k := max (abs u) (abs v),
  have hk : 0  k := le_trans (abs_nonneg u) (le_max_left _ _),
  use [-u+b*k, v + a*k],
  repeat { split },
  { rw show 1 = a*u + b*v, from h  gcd_eq_gcd_ab a b,
    ring },
  { suffices : u  b * max (abs u) (abs v), by linarith,
    calc u  abs u : le_abs_self u
    ...  max (abs u) (abs v) : le_max_left _ _
    ... = 1*max (abs u) (abs v) : by simp
    ...  b*max (abs u) (abs v) : mul_le_mul_of_nonneg_right hb hk },
  { suffices : -v  a * max (abs u) (abs v), by linarith,
    calc -v  abs v : neg_le_abs_self _
    ...  max (abs u) (abs v) : le_max_right _ _
    ... = 1*max (abs u) (abs v) : by simp [le_max_right ]
    ...  a*max (abs u) (abs v) : mul_le_mul_of_nonneg_right ha hk }
end

Kevin Buzzard (Jun 26 2019 at 11:08):

I think our views on the meaning of the phrase "completely different" might have diverged a bit but I do take your point that more needs to be said.

Mario Carneiro (Jun 26 2019 at 11:08):

Ah, you linarith'd

Patrick Massot (Jun 26 2019 at 11:08):

And then, modulo relating different versions of gcd, the proof of the nat version is:

lemma baz (a : ) (b : ) (ha : a  1) (hb : b  1) (h : nat.gcd a b = 1) :
     x : ,  y : , a*x + 1 = b*y :=
begin
  rcases foo a b (by exact_mod_cast ha) (by exact_mod_cast hb) sorry with x, y, h, hx, hy,
  use [x.to_nat, y.to_nat],
  rw [show x = (x.to_nat : ), by simp [hx],
      show y = (y.to_nat : ), by simp [hy]] at h,
  exact_mod_cast h
end

Kevin Buzzard (Jun 26 2019 at 11:08):

and he rung.

Patrick Massot (Jun 26 2019 at 11:10):

Of course I linarith, I ring, and norm_cast. I very strongly believe all this is essential for the future of proof assistants for mathematicians

Adrian Chu (Jun 26 2019 at 11:15):

million thanks. i am building up the framework of the proof of mythm currently.

Kevin Buzzard (Jun 26 2019 at 11:27):

It's quite possible that norm_cast can be used in a few places, but it's mostly about dealing with max and simple algebra

This is just the metamath-mario trying to get out. You can see a proof from very low-level principles and so figure that this is a good way to do it. The mathematicians see the theorem for int and figure that the boring work is already done, and that we should just use that.

Mario Carneiro (Jun 26 2019 at 11:29):

I don't use a high level tactic until I understand very well how effective it is and in what circumstances it can be used. AKA "no magic"

Kevin Buzzard (Jun 26 2019 at 11:30):

That's exactly what I'm saying. You were brought up on metamath and this has formed the way you think about how to solve goals.

Mario Carneiro (Jun 26 2019 at 11:30):

That said there are also a few proof tricks in Patrick's proof that I missed

Patrick Massot (Jun 26 2019 at 11:30):

We need both. We need Mario on one side, and we need Rob and Paul-Nicolas (and hopefully many more) on the other side

Kevin Buzzard (Jun 26 2019 at 11:31):

Yes absolutely. I'm just saying that it's interesting.

Kevin Buzzard (Jun 26 2019 at 11:32):

I think Mario wrote ring ;-)

Mario Carneiro (Jun 26 2019 at 11:33):

The difference between us is I feel the pressure of the actual proof term at all times

Mario Carneiro (Jun 26 2019 at 11:33):

If I use a complicated tactic that spews some gigantic... thing... in for my proof, I think it's a long and ugly proof

Kevin Buzzard (Jun 26 2019 at 11:33):

That's an interesting comment. I have no concept of what proof terms look like.

Mario Carneiro (Jun 26 2019 at 11:33):

even if the proof script is just by magic

Mario Carneiro (Jun 26 2019 at 11:34):

When I use refine and apply and rw, I know exactly what proof term is getting generated and how heavy that's going to be for the kernel

Kevin Buzzard (Jun 26 2019 at 11:35):

With Olympiad training as a kid I was taught the importance of getting the solution out as quickly as possible, who cares about other stuff like elegance.

Kevin Buzzard (Jun 26 2019 at 11:35):

I'm hoping that refine and apply don't add too much weight to the proof term! I know how to do them in term mode :-)

Mario Carneiro (Jun 26 2019 at 11:35):

that's exactly the point

Patrick Massot (Jun 26 2019 at 11:36):

Then we need more CS people working on the magic tactics, so that they produce nicer terms

Mario Carneiro (Jun 26 2019 at 11:36):

You don't really need them - you could do them in term mode. They are just slight shorthands for building that proof term

Kevin Buzzard (Jun 26 2019 at 11:37):

why do I care about the size of a proof term? It's immediately forgotten.

Mario Carneiro (Jun 26 2019 at 11:37):

I wish that was a bigger concern in lean-land. I care about it very much, but lean fights me when it comes to proof optimization

Mario Carneiro (Jun 26 2019 at 11:37):

It's not forgotten, it's stored and passed around and checked hundreds of time on travis and burns many CPU hours around the world

Patrick Massot (Jun 26 2019 at 11:45):

I'm seriously thinking about buying a bigger CPU tonight

Patrick Massot (Jun 26 2019 at 11:46):

I think I want more cores and more RAM, right?

Kevin Buzzard (Jun 26 2019 at 11:46):

gone are the days of more pixels. I remember wanting more colours once!

Kevin Buzzard (Jun 26 2019 at 11:46):

I upgraded from 2 to 8

Reid Barton (Jun 26 2019 at 11:47):

But some of the 8 colors were different on every other pixel so it was really more like... 12

Patrick Massot (Jun 26 2019 at 11:47):

I think VScode doesn't use much more than 8 colors, so you should be fine now. Let's go for CPU cores instead

Kevin Buzzard (Jun 26 2019 at 11:47):

Is a GPU of any use?

Patrick Massot (Jun 26 2019 at 11:47):

Unfortunately no

Patrick Massot (Jun 26 2019 at 11:48):

But wait until @Gabriel Ebner starts working on Lean 4 on GPU

Kevin Buzzard (Jun 26 2019 at 11:51):

This isn't really a noob question and probably deserves its own thread. My laptop has something like 4 cores and 16 gigs of ram and even if I compile mathlib I don't ever get close to those 16 gigs.

Patrick Massot (Jun 26 2019 at 11:51):

I have RAM problems when I want to compile mathlib while using VScode on some other project

Kevin Buzzard (Jun 26 2019 at 11:52):

In particular, I wonder whether somehow there's a theorem of the form "if you have x cores, then don't buy any more than c * x gigs of ram because Lean won't use that much" for some constant c.

Kevin Buzzard (Jun 26 2019 at 11:53):

When I want to compile mathlib and do something else too, I see how much free ram I have and then compile from the command line with the -M flag. Occasionally the build just runs out of memory and stops, and then I just start it again.

Johan Commelin (Jun 26 2019 at 11:54):

I haven't compile mathlib in the last 6 weeks. cache-olean is quite awesome.

Kevin Buzzard (Jun 26 2019 at 11:54):

Using the M flag also stopped my desktop (which had no swap) from randomly crashing when Lean suddenly goes beserk and fills up all memory.

Kevin Buzzard (Jun 26 2019 at 11:55):

IIRC it still didn't stop Lean going crazy when invoked via VS Code, so I added some swap anyway.

Patrick Massot (Jun 26 2019 at 11:55):

compiling mathlib is still required when working on mathlib

Kevin Buzzard (Jun 26 2019 at 12:56):

It's not forgotten, it's stored and passed around and checked hundreds of time on travis and burns many CPU hours around the world

You make it sound like I'm responsible for climate change!

Patrick Massot (Jun 26 2019 at 12:57):

This is clearly what he means.

Patrick Massot (Jun 26 2019 at 12:57):

And now you'll make it worse by flying to Portland

Adrian Chu (Jun 26 2019 at 14:49):

import algebra.euclidean_domain data.vector
import tactic.linarith tactic.library_search algebra.big_operators data.fintype
import data.finset data.nat.basic

open euclidean_domain

def fin_n_to_list {n : } (x: fin n  ) : list  :=
  (list.range n).map (λ i, if h : i < n then x i, h else 0)

def n_lcm {n : } (x : fin n  ) :  :=
  list.foldl nat.lcm 1 (fin_n_to_list x)

lemma sum_to_mul (a b : ):  finset.univ.sum (λ i : fin a, b) = a*b :=
by simp [finset.sum_const, finset.card_univ, fintype.card_fin]

lemma n_lcm_coprime {n : } (x : fin n  ) (y : )
  (coprime :  i : fin n, nat.gcd (x i) y = 1) :
  nat.gcd (n_lcm x) y = 1 :=
sorry -- need to prove

lemma bazout_int (a b : ) (ha : a  1) (hb : b  1) (h : gcd a b = 1) :
     x : ,  y : , a*x + 1 = b*y  0  x  0  y :=
sorry -- already proven

lemma bazout_nat (a b : ) (ha : a  1) (hb : b  1) (h : nat.gcd a b = 1) :
  -- this is Patrick's version, or we can use mario's version above
     x : ,  y : , a*x + 1 = b*y :=
begin
  rcases bazout_int a b (by exact_mod_cast ha) (by exact_mod_cast hb) sorry with x, y, h, hx, hy,
-- need to prove
  use [x.to_nat, y.to_nat],
  rw [show x = (x.to_nat : ), by simp [hx],
      show y = (y.to_nat : ), by simp [hy]] at h,
  exact_mod_cast h
end

theorem mythm (n : ) (r : fin n  ) (s : )
    (hn  1) (hr :  i, r i  1) (hs : s  1)
    (coprime :  i : fin n, nat.gcd (r i) s = 1) :
     x : fin n  ,  y : ,
    ( i, x i  1)  (finset.univ.sum (λ i, (x i)^(r i)) = y^s) :=
begin
    let t := n_lcm r,
    have t_geq_1 : t  1 := sorry,  -- need to prove
    have ri_div_t : ( i : fin n, t % (r i) = 0) := sorry,  -- need to prove
    let t_s_coprime := n_lcm_coprime r s coprime,
    cases (bazout_nat t s t_geq_1 hs t_s_coprime) with a ha,
    cases ha with b hab,
    let x := λ i : fin n, n^((a*t)/r i),
    let y := n^b,
    have trivial1 : a*t + 1 = t*a + 1 := by simp [mul_comm],
    fapply exists.intro,
    exact x,
    fapply exists.intro,
    exact y,
    repeat {split},
    {intro i, sorry}, -- for all i xi ≥ 1
    calc finset.univ.sum (λ i, (n^((a*t)/r i))^(r i))
    = finset.univ.sum (λ i, (n^(((a*t)/r i)*(r i)))) : by sorry
    ... = finset.univ.sum (λ i: fin n, n^(a*t)) : by sorry
    ... = n*(n^(a*t)) : sum_to_mul n (n^(a*t))
    ... = n^(a*t + 1): by exact mul_comm n (n^(a*t))
    ... = n^(t*a + 1) : by exact congr_arg (pow n) trivial1
    ... = n^(s*b) : by exact congr_arg (pow n) hab
    ... = n^(b*s) : by exact congr_arg (pow n) (mul_comm s b)
    ... = (n^b)^s : nat.pow_mul b s n
end

Adrian Chu (Jun 26 2019 at 14:50):

this is a record of my (our) progress, i am (we are) getting closer! but for me it's time to rest

Reid Barton (Jun 26 2019 at 14:54):

I know Lean doesn't care, but the name is bezout (or even better, Bézout)

Johan Commelin (Jun 26 2019 at 14:55):

I'm inclined to say baz… (oh, well, never mind…)

Patrick Massot (Jun 26 2019 at 15:25):

I assumed all along this was an intentional reference to the foo, bar, baz sequence, being a variation on Bézout

Patrick Massot (Jun 26 2019 at 15:26):

but maybe I was wrong and Adrian wants to also credit Bachet

Kevin Kappelmann (Jun 26 2019 at 19:14):

When using induction t : e, I get a weird inductive hypothesis. For example,

example {n : } : n  n * n :=
begin
induction n_eq : n with m IH,
{ simp },
{ sorry } -- ill-formed IH here
end

will create a goal

case nat.succ
n m : ,
IH : n = m  m  m * m,
n_eq : n = nat.succ m
 nat.succ m  nat.succ m * nat.succ m

but clearly, n ≠ m so I cannot use the IH for my proof. Am I doing something wrong? Note: I need n_eq in the theorem I am actually proving, so just dropping n_eq : is not an option.

Kevin Buzzard (Jun 26 2019 at 19:20):

What does "I need n_eq in the theorem I am actually proving" mean? What's wrong with just induction n with m IH?

Johan Commelin (Jun 26 2019 at 19:21):

Wrong thread?

Kevin Buzzard (Jun 26 2019 at 19:21):

(deleted)

Kevin Buzzard (Jun 26 2019 at 19:22):

What does "I need n_eq in the theorem I am actually proving" mean? What's wrong with just induction n with m IH?

I mean, what hypothesis do you actually want, if it's not what you got?

Kevin Buzzard (Jun 26 2019 at 19:27):

But yeah something is weird there. It's as if induction n_eq : n will always cause you trouble.

Kevin Kappelmann (Jun 26 2019 at 19:27):

That was ill-phrased, let me clarify. I did not want to say that I need n_eq, but rather, I still want to be able to refer to n in both cases. Basically, I want to do some work for both case nat.zero and nat.succ using all_goals and referring to n like this:

example {n : } : n  n * n :=
begin
induction n_eq : n with m IH,
all_goals {
  have : 0  n, from n.zero_le
},
{ simp },
{ sorry }
end

Kevin Buzzard (Jun 26 2019 at 19:28):

But when you do induction n, doesn't n literally disappear from the context?

Kevin Kappelmann (Jun 26 2019 at 19:28):

Yep, that's what I want to avoid!

Kevin Buzzard (Jun 26 2019 at 19:29):

But isn't that how induction works?

Kevin Buzzard (Jun 26 2019 at 19:29):

I see what you're trying to do.

Kevin Buzzard (Jun 26 2019 at 19:29):

Why don't you just do what you want to do with n before you start on the induction?

Kevin Kappelmann (Jun 26 2019 at 19:32):

Because then it introduces these statements as premises in my IH as well. Cf:

example {n : } : n  n * n :=
begin
have : 0  n, from n.zero_le,
induction n with m IH,
{ simp },
{ sorry } -- check IH here
end

Kevin Buzzard (Jun 26 2019 at 19:32):

Yes you're right.

Kevin Buzzard (Jun 26 2019 at 19:34):

It's just reverting all hypotheses with an n in before starting the induction.

Kevin Buzzard (Jun 26 2019 at 19:35):

example {n : } : n  n * n :=
begin
have : 0  n, from n.zero_le,
apply nat.rec_on n,

Kevin Buzzard (Jun 26 2019 at 19:35):

It must just be what the tactic does.

Reid Barton (Jun 26 2019 at 19:35):

I think you can use set to rename succ m to n if you like

Kevin Buzzard (Jun 26 2019 at 19:35):

You can get round it by just applying the recursor directly like in the above.

Chris Hughes (Jun 26 2019 at 19:36):

I think you want to prove something like forall t > 0, n \le n * t

Kevin Buzzard (Jun 26 2019 at 19:36):

No, my idea doesn't work either.

Chris Hughes (Jun 26 2019 at 19:36):

And then apply it to n, and prove the case n =0 separately

Kevin Buzzard (Jun 26 2019 at 19:37):

Chris the question is how to get induction not to revert facts about n which you want left alone.

Chris Hughes (Jun 26 2019 at 19:37):

clear first

Reid Barton (Jun 26 2019 at 19:38):

Oh I guess I didn't really understand what you are trying to do

Kevin Buzzard (Jun 26 2019 at 19:38):

The question is how to get from

1 goal
n : ℕ,
this : 0 ≤ n
⊢ n ≤ n * n

to

2 goals
case nat.zero
this : 0 ≤ 0
⊢ 0 ≤ 0 * 0

case nat.succ
m : ℕ,
IH : m ≤ m * m,
this : 0 ≤ nat.succ m
⊢ nat.succ m ≤ nat.succ m * nat.succ m

Kevin Buzzard (Jun 26 2019 at 19:39):

i.e. "do cases on this but do induction on the goal"

Chris Hughes (Jun 26 2019 at 19:41):

In general that's impossible, because if it didn't put the hypothesis at the start you could prove contradictions right. Unless I misunderstand.

Kevin Buzzard (Jun 26 2019 at 19:41):

induction n with m IH gives

case nat.succ
m : ℕ,
IH : 0 ≤ m → m ≤ m * m,
this : 0 ≤ nat.succ m
⊢ nat.succ m ≤ nat.succ m * nat.succ m

and induction h : n with m IH gives

case nat.succ
n : ℕ,
this : 0 ≤ n,
m : ℕ,
IH : n = m → m ≤ m * m,
h : n = nat.succ m
⊢ nat.succ m ≤ nat.succ m * nat.succ m

In both cases the inductive hypothesis is rendered useless because the assumption is false.

Kevin Buzzard (Jun 26 2019 at 19:41):

You might be right that it's impossible.

Kevin Buzzard (Jun 26 2019 at 19:43):

@Kevin Kappelmann That n really does not exist any more. This is the problem. If you have a statement which is true for all all nats, then don't prove it for n, prove it for all nats before the induction starts and then apply it to whatever you want to apply it to afterwards.

Kevin Buzzard (Jun 26 2019 at 19:43):

I think it's misleading to think that the n has "become succ m" in the inductive step. The n is meaningfully attached to both m and succ m here.

Chris Hughes (Jun 26 2019 at 19:52):

Here's a contradiction I can prove

open nat
lemma rubbish (n m : ) (h : n = m) (hm : m  1) : n = 0  n > m :=
begin
  induction n,
  { left, refl },
  { -- case nat.succ
    -- m : ℕ,
    -- hm : m ≠ 1,
    -- n_n : ℕ,
    -- n_ih : n_n = m → n_n = 0 ∨ n_n > m,
    -- h : succ n_n = m
    -- ⊢ succ n_n = 0 ∨ succ n_n > m
    have : n_n = 0  n_n > m, from sorry,
    cases this,
    subst this, exfalso,
    exact hm h.symm,
    exact or.inr (lt_succ_of_lt this) }
end

example : false := absurd (rubbish 2 2) dec_trivial

Kevin Kappelmann (Jun 26 2019 at 19:54):

Alright, thanks. Using induction with this pattern induction t : e is pretty useless then, isn't it?

Chris Hughes (Jun 26 2019 at 19:57):

Maybe for naturals. There are definitely some uses, but I can't think of any right now.

Kevin Buzzard (Jun 26 2019 at 19:58):

cases t : e is surely useful.

Kevin Buzzard (Jun 26 2019 at 19:59):

But when it really is an induction, won't the inductive hypothesis end up contradicting another hypothesis most of the time?

Chris Hughes (Jun 26 2019 at 20:00):

I think all of the time, given the freely generated nature of inductive types.

Kevin Buzzard (Jun 26 2019 at 20:00):

Yeah, I'm just trying to prove list.rec with induction h : l and I don't think it can be done.

Chris Hughes (Jun 26 2019 at 20:01):

Maybe it would be useful for custom recursors when you do induction ... using ...

Kevin Buzzard (Jun 26 2019 at 20:03):

-- attempt to prove list.rec
example : Π {T : Type} {C : list T  Type},
    C list.nil  (Π (hd : T) (tl : list T), C tl  C (hd :: tl))  Π (n : list T), C n :=
begin
  intros T C hnil hcons l,
  induction h : l,
    exact hnil,
  apply hcons,
  apply ih,
  -- dead
  sorry
end

Reid Barton (Jun 26 2019 at 20:05):

mathlib has a fair number of uses (git grep '\binduction .*:') but I didn't check whether they could be replaced by cases

Kevin Buzzard (Jun 26 2019 at 20:20):

Or just search \binduction .*: in VS Code but switch on the .* option (use regular expression)

Kevin Buzzard (Jun 26 2019 at 20:23):

All the ones I checked, could be replaced by cases. There are loads in data/seq and I didn't check any of them.

Scott Viteri (Jun 27 2019 at 02:12):

Hello,
Upon adding mathlib as a project dependency, searching for definitions with emacs helm errors out due to excessive memory consumption. I don't see any mentions of this in github lean-mode issues. Is this to be expected?
Thanks,
Scott

Reid Barton (Jun 27 2019 at 02:16):

Running leanpkg build in your project should help

Reid Barton (Jun 27 2019 at 02:16):

That will build whatever parts of mathlib your project currently imports, so that later lean won't have to compile them on the fly

Reid Barton (Jun 27 2019 at 02:17):

If you just want to build all of mathlib, I think lean --make _target/deps/mathlib/src should work

Scott Viteri (Jun 27 2019 at 02:20):

I see -- I tried 'leanpkg build' and and still getting memory issues. I'll try building all of mathlib.

Reid Barton (Jun 27 2019 at 02:21):

You might need to restart the lean server after running leanpkg build as well

Reid Barton (Jun 27 2019 at 02:22):

My brain isn't in Lean mode so I forget the key

Scott Viteri (Jun 27 2019 at 02:22):

Oh, that did it

Scott Viteri (Jun 27 2019 at 02:22):

C-c C-r

Scott Viteri (Jun 27 2019 at 02:23):

Wonderful, thank you

Scott Viteri (Jun 27 2019 at 02:24):

My brain isn't in Lean mode so I forget the key

If only brains could context switch with a simple M-x ...

Scott Viteri (Jun 27 2019 at 04:25):

Separate question -- is there a general rule of thumb of when to use the tactic mode for proofs?

Scott Viteri (Jun 27 2019 at 04:25):

Or is it just a stylistic choice

Mario Carneiro (Jun 27 2019 at 04:26):

when it's easier

Mario Carneiro (Jun 27 2019 at 04:27):

or more compact

Mario Carneiro (Jun 27 2019 at 04:27):

The best approach is to use a combination of both and be comfortable going back and forth

Scott Viteri (Jun 27 2019 at 04:30):

is going from regular to tactic mode using "by"?

Scott Viteri (Jun 27 2019 at 04:31):

and is the opposite direction using exact?

Mario Carneiro (Jun 27 2019 at 04:32):

yes

Scott Viteri (Jun 27 2019 at 04:34):

yes to both directions?

Scott Viteri (Jun 27 2019 at 04:36):

Also I didn't realize a bit ago that you can use start begin end blocks in the middle of a proof

Scott Morrison (Jun 27 2019 at 04:40):

I find that for "easy" stuff (i.e. proofs that you know ought to be easy, so you don't actually plan ahead of time what you're doing!), I always start in tactic mode, blunder about for a bit (often letting tidy do some of the work), then end up with a successful tactic script. Now you're only half done, and you convert all the parts of the proof that are more succinct in term mode back into term mode.

Scott Viteri (Jun 27 2019 at 04:45):

What is tidy?

Scott Viteri (Jun 27 2019 at 04:46):

I found it

Scott Viteri (Jun 27 2019 at 04:51):

Is there a preference for more specificity in the proofs? Eg why not leave tidy in the proof

Scott Viteri (Jun 27 2019 at 04:56):

Is there a Lean analog of crush?

Mario Carneiro (Jun 27 2019 at 04:56):

that's tidy

Mario Carneiro (Jun 27 2019 at 04:57):

You don't want to leave tidy in the proof because it's slow. It's basically a meta-tactic that finds a tactic script that you should use in place of tidy

Scott Viteri (Jun 27 2019 at 05:06):

makes sense

Scott Viteri (Jun 27 2019 at 05:08):

It seems that in order to find tidy in emacs definition search I must first import it

Scott Viteri (Jun 27 2019 at 05:10):

which I guess is fine, I could just grep through my mathlib directory if I'm looking for something

Scott Viteri (Jun 27 2019 at 05:11):

but is this expected behavior

Mario Carneiro (Jun 27 2019 at 05:17):

yes, this is unfortunate but unavoidable

Mario Carneiro (Jun 27 2019 at 05:17):

You can try importing everything if you want the searches to have good results, but that can take a lot of memory

Scott Viteri (Jun 27 2019 at 05:24):

ok

Scott Viteri (Jun 27 2019 at 05:33):

Thanks for the help!

Scott Viteri (Jun 27 2019 at 06:47):

Where can I find documentation for a tactic such as apply_assumption?

Kevin Buzzard (Jun 27 2019 at 06:49):

Not at a computer right now but there's a big file in docs in mathlib which contains information about most tactics. Is it docs/tactics.md or something?

Scott Viteri (Jun 27 2019 at 06:51):

This is useful for mathlib, thanks

Scott Viteri (Jun 27 2019 at 06:52):

but apply_assumption is in core.lean

Scott Morrison (Jun 27 2019 at 06:52):

You can also just type apply_assumption inside a begin ... end block, and hover the mouse over it. The tooltip that pops up should contain the doc-comment, which is usually just a slightly abbreviated version of the documentation in docs/tactics.md.

Scott Morrison (Jun 27 2019 at 06:52):

Read the definition, then. :-)

Scott Morrison (Jun 27 2019 at 06:52):

Put your cursor in apply_assumption, and hit F12 to jump to the definition.

Scott Morrison (Jun 27 2019 at 06:53):

The community branch of Lean would probably accept PRs adding doc comments to any/all of these core tactics! :-)

Scott Morrison (Jun 27 2019 at 06:53):

(Remember here that core Lean is frozen while development of Lean 4 takes place, so the community branch, which is only just getting off the ground now, is the only place we can improve the documentation of core stuff.)

Scott Viteri (Jun 27 2019 at 06:54):

I take back what I said -- apply assumption is in mathlib/src/tactic/core.lean

Scott Viteri (Jun 27 2019 at 06:55):

so why doesn't docs/tactics.md mention it?

Scott Morrison (Jun 27 2019 at 06:56):

Sorry about the confusing naming. src/tactic/core.lean is really basic tooling stuff (mostly not used interactively). src/tactic/basic.lean is all the most common interactive tactics, and src/tactic/default.lean is the kitchen sink, importing nearly all the mathlib defined tactics.

Scott Morrison (Jun 27 2019 at 06:57):

Probably because apply_assumption has been around a long time, perhaps predating docs/tactics.md? I'm not sure. Comment PR definitely accepted. :-)

Scott Viteri (Jun 27 2019 at 06:59):

Where does the tooltip come from?

Scott Viteri (Jun 27 2019 at 07:00):

Because I do get a short description this way

Scott Morrison (Jun 27 2019 at 07:00):

The doc-comment.

Scott Morrison (Jun 27 2019 at 07:00):

When you make a definition, you can put a comment before it

Scott Morrison (Jun 27 2019 at 07:00):

using the /-- ... -/ syntax (note the double hyphen at the open comment)

Scott Viteri (Jun 27 2019 at 07:00):

But the definition of apply_assumption has no such comment above it

Scott Morrison (Jun 27 2019 at 07:00):

Hmm.

Scott Morrison (Jun 27 2019 at 07:01):

Mine does?

Scott Viteri (Jun 27 2019 at 07:01):

There are two apply_assumption s

Scott Viteri (Jun 27 2019 at 07:01):

The one in solve_by_elim.lean has the comment

Scott Morrison (Jun 27 2019 at 07:01):

When I hit F12 on apply_assumption, I was taken to the one in solve_by_elim.lean.

Scott Morrison (Jun 27 2019 at 07:02):

Ah!

Scott Morrison (Jun 27 2019 at 07:02):

There are very often two versions of a given tactic.

Scott Morrison (Jun 27 2019 at 07:02):

One in the tactic.interactive namespace, and one in the tactic namespace.

Scott Viteri (Jun 27 2019 at 07:02):

Do you often have to be careful which version of a tactic you are importing?

Scott Morrison (Jun 27 2019 at 07:02):

The interactive one is accessible in begin ... end blocks, and very often does some parsing tricks so you can conveniently control it

Scott Morrison (Jun 27 2019 at 07:03):

and non-interactive one is usually not intended for use in begin ... end blocks, but instead by other people writing tactics.

Scott Viteri (Jun 27 2019 at 07:03):

ok

Scott Morrison (Jun 27 2019 at 07:03):

Unless you know what you're doing, typically always use the interactive version.

Scott Viteri (Jun 27 2019 at 07:07):

This did solve my original question though -- the documentation is (besides the tactics.md) in the comment above the definition. I just didn't see this because I was looking at the wrong version of the definition

Scott Viteri (Jun 27 2019 at 07:13):

is leanprover-community/lean much different from the original?

Scott Morrison (Jun 27 2019 at 07:18):

I don't think so. Simon Hudon is the one to ask.

Scott Viteri (Jun 27 2019 at 07:20):

ok

Kevin Buzzard (Jun 27 2019 at 07:21):

It's not different enough from the original Lean to have ever made me contemplate switching.

Mario Carneiro (Jun 27 2019 at 07:37):

right now it's just accumulating bugfixes and little things

Mario Carneiro (Jun 27 2019 at 07:37):

we'll let you know when it's ready for prime time

Jesse Michael Han (Jun 27 2019 at 08:54):

helm often gives me the same error. i find it usually works again after C-c C-r

Kevin Kappelmann (Jun 28 2019 at 12:45):

What's the recommended way to use zeta-reduction at a hypothesis? E.g. going from hyp : let n : ℕ := 2 in 1 + 1 = n to hyp : 1 + 1 = 2? I know that delta at hyp works, but isn't this strictly speaking a zeta-reduction I am performing?
edit: fix typo

Kevin Buzzard (Jun 28 2019 at 12:46):

These are definitionally equal I guess, so you can just use change.

Kevin Kappelmann (Jun 28 2019 at 12:48):

yep, sorry for the typo. I do not want to re-state the unfolded hypothesis though.

Wojciech Nawrocki (Jun 28 2019 at 14:11):

Why do additive monoids have scalar multiplication defined in terms of exponentiation? I found this really surprising:

def add_monoid.smul [add_monoid α] (n : ) (a : α) : α :=
@monoid.pow (multiplicative α) _ a n

Kevin Buzzard (Jun 28 2019 at 14:17):

multiplicative alpha means "I know it's addition, but pretend it's multiplication". It's some sort of attempt to not duplicate effort.

Kevin Buzzard (Jun 28 2019 at 14:18):

a monoid and an additive monoid are the same, but Lean is less good than mathematicans at dealing with things like this. At least in this situation we have some automation to use.

Wojciech Nawrocki (Jun 28 2019 at 14:20):

That makes sense, but even when the monoid operation is interpreted as multiplication, is it conventional to call pow scalar multiplication? To me, smul seems to only make sense with vector spaces/modules.

Wojciech Nawrocki (Jul 10 2019 at 10:33):

Does anyone know why the strictness of when names are resolved is kind of flipped between quoted names and expressions (assuming I understand it correctly)? By this I mean that using `my.name the name is not resolved when parsing the tactic, while with ``my.name it is, so more backticks => resolve sooner. But with expressions, it's the opposite - with `(expr), symbols are resolved at parse time, with ``(expr) partially so, and with ```(expr) resolving symbols is deferred to tactic runtime. EDIT: fixed backticks, thanks Johan!

Johan Commelin (Jul 10 2019 at 10:39):

On Zulip formatting: Use more ```backticks ``to `surround ```the code then the number of `adjacent backticks in the code.

Kevin Buzzard (Jul 10 2019 at 12:30):

And use the "quote and reply" option to see exactly what Johan did :-)

Scott Viteri (Jul 10 2019 at 16:21):

Does the syntax

theorem perm_sort : forall l, sort l ~ l
| [] := refl
| (a::l) := ...

not give inductive hypotheses?

Scott Viteri (Jul 10 2019 at 16:22):

Right now in the second block I have proven
sort l ~ l, and could really use a forall

Mario Carneiro (Jul 10 2019 at 16:26):

use perm_sort l in the second block to access the IH

Scott Viteri (Jul 10 2019 at 16:27):

oh ok

Scott Viteri (Jul 10 2019 at 16:28):

Thanks

Scott Viteri (Jul 10 2019 at 17:00):

How do I specify which instance of a definition rw unfolds?

Scott Viteri (Jul 10 2019 at 17:01):

suppose I have the goal rev (rev (h :: t)) = h :: t

Scott Viteri (Jul 10 2019 at 17:02):

rw rev results in rev_aux (rev (h :: t)) nil = h :: t

Scott Viteri (Jul 10 2019 at 17:02):

But I want rw to act on the inner instance of rev

Scott Viteri (Jul 10 2019 at 17:06):

I just found change, which seems to do the trick

Jesse Michael Han (Jul 10 2019 at 17:25):

change will work if the rewrite is definitional, otherwise you can try convert_to which will let you supply the equality proof obligation after

conv {} will let you navigate inside an expression and surgically rewrite

Marc Huisinga (Jul 10 2019 at 23:16):

is there a neat way to only generalize a specific variable?

Yury G. Kudryashov (Jul 10 2019 at 23:22):

Hi, what is the meaning of @@?

Kevin Buzzard (Jul 10 2019 at 23:39):

is there a neat way to only generalize a specific variable?

Does the dosctring for generalize answer your question?

Kevin Buzzard (Jul 10 2019 at 23:42):

Hi, what is the meaning of @@?

It's explained just above here in TPIL. Searching for @@ didn't work for me but I knew it was in there.

Marc Huisinga (Jul 10 2019 at 23:43):

i don't think so - i should have been more clear, sorry! i'm looking for a neat way to generalize a specific occurence of a variable, not all of them

Marc Huisinga (Jul 11 2019 at 00:17):

oh, i guess something along the lines of

lemma foo {α : Type*} (P : α → α → Prop) (a : α) : P a a :=
begin
  have h : ∀ b : α, P b a, sorry,
  exact h a
end

will do

Yury G. Kudryashov (Jul 11 2019 at 00:20):

Is it possible to redefine the "default constructor"? I mean, the one used by ⟨⟩ notation.

Marc Huisinga (Jul 11 2019 at 00:25):

afaik you can only use that notation if there is only one constructor

Kevin Buzzard (Jul 11 2019 at 01:18):

You can make a second structure with the constructor you like and then define a map to the first structure. If they were classes you could do this with type class inference I guess (on a good day with a following wind)

Kevin Buzzard (Jul 11 2019 at 01:20):

You want more than

lemma foo {α : Type*} (P : α  α  Prop) (a : α) : P a a :=
begin
  suffices h :  b : α, P b a,
    exact h a,
  sorry
end

?

Kevin Buzzard (Jul 11 2019 at 01:21):

I am struggling a bit to imagine the syntax of what some super-precise generalize tactic would look like but I understand your question now (and don't know the answer)

Marc Huisinga (Jul 11 2019 at 08:36):

ah, suffices is a neat syntax, i forgot about that one.
it turns out that for my specific use case, simply a precise generalize wouldn't do. i also needed additional assumptions about b (in my use-case, α was list foo and i additionally needed b ⊆ a to prove my lemma).
it probably isn't a good idea to have tactics for use cases this concrete, and "suffices" is certainly readable enough.

Wojciech Nawrocki (Jul 11 2019 at 13:17):

What's the convention for defining stuff on structures that can be both mul-based and add-based, e.g. semigroup/add_semigroup? Should I define it on semigroup and then somehow transport to add_semigroup using additive, the other way around, or something else?

Chris Hughes (Jul 11 2019 at 13:22):

Define it on semigroup and transfer with to_additive.

Wojciech Nawrocki (Jul 11 2019 at 13:25):

Thanks Chris!

Kevin Buzzard (Jul 11 2019 at 14:15):

See group_theory/quotient_group.lean for an example of how this looks.

Wojciech Nawrocki (Jul 13 2019 at 14:29):

Can the equational lemmas for a function do something more than definitional/beta-reduction? I have a goal of the form (simplified) fn1 (fn2 some_expr) = fn1 (fn2 other_expr), s.t. fn1 \comp fn2 takes both exprs to the same value (and the exprs contain some more calls to fn1/2). So, this should follow from refl, but it doesn't. If however I do repeat { rw [fn1] }, repeat { rw [fn2] } first, refl works.

Wojciech Nawrocki (Jul 13 2019 at 15:26):

A related question - is there a tactic equivalent of #reduce?

Kevin Buzzard (Jul 13 2019 at 16:23):

The equation lemmas for a function are not always definitionally true

Kevin Buzzard (Jul 13 2019 at 16:24):

They're simp lemmas though so simp might work

Wojciech Nawrocki (Jul 13 2019 at 17:23):

Huh okay, that is kind of surprising - I thought they were all refl. Thanks!

Mario Carneiro (Jul 13 2019 at 19:06):

The main time when they are not rfl is when well founded recursion is used. This has to do with why definitional equality is undecidable and lean's defeq is an underapproximation of the real thing

Wojciech Nawrocki (Jul 13 2019 at 19:23):

Oooh yeah it is recursive - it's transitivity that's missing, right? It seemed to me like it should still reduce but is what you're saying that the has_well_foundeds prevent it from reducing?

Wojciech Nawrocki (Jul 13 2019 at 19:26):

I came across this while trying to write some reflection tactics a la ring2, where in correctness you have (H : horner_expr.of_csexpr r₁ = horner_expr.of_csexpr r₂) by rfl. of_csexpr is defined recursively but it seems to reduce just fine, however I have something more like (of_csexpr r1).eval = (of_csexpr r2).eval, where eval is also recursive and this breaks (needs simp; refl).

Wojciech Nawrocki (Jul 13 2019 at 19:26):

I can get rid of the evals so this isn't really a problem, just confusing.

Mario Carneiro (Jul 13 2019 at 19:30):

Structural recursion is fine, it's just when you use well founded recursion, i.e. recursion on acc that it gets flaky

Wojciech Nawrocki (Jul 13 2019 at 19:37):

I see, I'm doing some reassociation which I think has to rely on sizeof:

def reassoc: Expr -> Expr
| (Mul (Mul a b) c) := Mul (reassoc a) $ reassoc (Mul b c)
| e := e

Mario Carneiro (Jul 13 2019 at 19:49):

Right, that's not a structural recursion, but you can rewrite it to be one

Wojciech Nawrocki (Jul 13 2019 at 20:00):

Yup, refls now, thanks!

Wojciech Nawrocki (Jul 19 2019 at 23:32):

Is this assumption correct: "after compiling somefile.lean with lean --make, forall theorems in somefile, the resulting somefile.olean contains the theorems' names and types, but because they are in Prop and have already been typechecked, the proof terms are discarded and Lean trusts previously built modules to have correctly-typechecked definitions"?

Wojciech Nawrocki (Jul 19 2019 at 23:54):

Okay, it's partly correct and partly not. Lean keeps the proof terms (i.e. the "values" of theorem definitions) in .olean files, but they're stored with a high "trust level" value, and I think that when a such a high-trust definition is imported, its value is not typechecked again and the type is trusted.

Yury G. Kudryashov (Jul 21 2019 at 04:08):

From lean --help:

  --trust=num -t     trust level (default: max) 0 means do not trust any macro,
                     and type check all imported modules

Yury G. Kudryashov (Jul 21 2019 at 04:09):

I think, by default Lean trusts that all imported .olean files are correct but you can tell it to double-check them.

Lennard Henze (Jul 22 2019 at 16:32):

Trying one of the excercises I am stuck with deriving A \or \not A:

example : ¬ (A  ¬ A) :=
have h₂ : A  ¬ A, from sorry,
assume h₁ : (A  ¬ A),
show false, from or.elim h₂
        (assume : A,
           have h₃ : ¬A, from iff.mp h₁ this,
            show false, from h₃ this)
        (assume : ¬ A,
            have h₃ : A, from iff.mpr h₁ this,
            show false, from this h₃)

Can someone point me in the right direction?

Kevin Buzzard (Jul 22 2019 at 16:38):

This is just the law of the excluded middle, which is not true in constructive logic

Kevin Buzzard (Jul 22 2019 at 16:38):

Are you allowed to use classical logic?

Wojciech Nawrocki (Jul 22 2019 at 16:38):

(See also TPIL 11.6)

Kevin Buzzard (Jul 22 2019 at 16:39):

It can be done constructively I believe (but don't trust me, I am a classical guy)

Kevin Buzzard (Jul 22 2019 at 16:40):

It's something like classical.em, what you want

Lennard Henze (Jul 22 2019 at 16:41):

its just a task from https://leanprover.github.io/logic_and_proof/propositional_logic_in_lean.html, i have not read anything about classical logic being forbidden

Kenny Lau (Jul 22 2019 at 16:42):

lol

Lennard Henze (Jul 22 2019 at 16:43):

i think it is meant to be done without imports

Wojciech Nawrocki (Jul 22 2019 at 16:49):

Here is a constructive proof, adapted from iff_not_self:

example : ¬ (A  ¬ A) :=
assume h: A  ¬ A,
have hna: ¬A, from (
  assume ha: A,
  show false, from (h.mp ha) ha),
show false, from hna (h.mpr hna)

Lennard Henze (Jul 22 2019 at 16:53):

nice thanks!!

Gihan Marasingha (Aug 05 2019 at 16:19):

Is there an escape sequence for producing french quotes in the web editor / VS Code? More generally, is there a comprehensive list of escape sequences? Ta.

Kevin Buzzard (Aug 05 2019 at 16:19):

Hey Gihan!

Kevin Buzzard (Aug 05 2019 at 16:19):

It's in some translations.json file somewhere, hang on...

Kevin Buzzard (Aug 05 2019 at 16:20):

https://github.com/leanprover/vscode-lean/blob/master/translations.json

Gihan Marasingha (Aug 05 2019 at 16:21):

Thanks very much Kevin!

I see you're teaching M1F to a virtual tutee. Have you had success teaching Lean to corporeal M1F students?

Kevin Buzzard (Aug 05 2019 at 16:22):

@Kenny Lau and @Chris Hughes are both Imperial undergraduates who have contributed far more than me to Lean's maths library.

Kevin Buzzard (Aug 05 2019 at 16:23):

\f< and \f> apparently

Kevin Buzzard (Aug 05 2019 at 16:23):

I don't usually use them

Gihan Marasingha (Aug 05 2019 at 16:24):

Amazing! I'm tempted to try this at Exeter, but I'm worried that formal proof may put off the bulk of students.

Kevin Buzzard (Aug 05 2019 at 16:25):

I have a lot of tips. If you do it naively then definitely you can put a lot of people off. One initial tip is not to teach people new maths and Lean at the same time.

Kevin Buzzard (Aug 05 2019 at 16:25):

by "people" I mean "UG mathematicians"

Kevin Buzzard (Aug 05 2019 at 16:26):

so if it's your job to teach them new maths then it can get quite tricky finding the balance.

Kevin Buzzard (Aug 05 2019 at 16:27):

I'm speaking in Exeter at the end of Nov, we can maybe talk then. Unfortunately I'm giving the number theory seminar rather than a general colloquium.

Gihan Marasingha (Aug 05 2019 at 16:32):

OK, that would be great. I look forward to it.

My main issue really is how to teach proof tout court. Most written arguments I see from new students are full of non sequiturs. I thought Lean might be a way for students to determine whether their arguments were valid.

Kevin Buzzard (Aug 05 2019 at 16:32):

This was one of the reasons I got interested in Lean too.

Kevin Buzzard (Aug 05 2019 at 16:33):

I'm sure this sort of software has got a lot of potential to change the way undergraduate mathematics is taught.

Kevin Buzzard (Aug 05 2019 at 16:33):

But I'm still very much experimenting with how to do it. Patrick Massot also tried

Kevin Buzzard (Aug 05 2019 at 16:34):

using Lean with a bunch of UGs at Orsay in their introduction to proof course

Gihan Marasingha (Aug 05 2019 at 16:36):

Thanks. I've also seen Jeremy Avigad's 'Logic and Proof' notes.

Kevin Buzzard (Aug 05 2019 at 16:37):

Yeah, we're all trying to change the world.

Kevin Buzzard (Aug 05 2019 at 16:37):

An education specialist is writing a paper about my intervention, should be ready next month apparently.

Kevin Buzzard (Aug 05 2019 at 16:38):

She interviewed lots of M1F students

Gihan Marasingha (Aug 05 2019 at 16:40):

I'd be interested in reading the article. Where will it be published?

Kevin Buzzard (Aug 05 2019 at 16:40):

Don't know, I guess they will have some education journal ecosystem but I don't know anything about it.

Gihan Marasingha (Aug 05 2019 at 16:43):

Well, maybe I'll find out in November. See you then and thanks for the information.

Floris van Doorn (Aug 05 2019 at 19:44):

@Gihan Marasingha: If you see a character in VSCode, and you are wondering how to input it, you can also hover your mouse over the character, and in a pop-up it will tell you how to type it.

Gihan Marasingha (Aug 05 2019 at 21:56):

Thanks!

Kevin Kappelmann (Aug 14 2019 at 21:02):

Question related to proof irrelevance: I was wondering what Lean is doing in this example:

structure test := (n : ) (pos: 0 < n)

example {t1 t2 : test} (h : t1.n = t2.n) : t1 = t2  :=
begin
  cases t1 with n1 pos1,
  cases t2 with n2 pos2, -- current goal : {n := n1, pos := pos1} = {n := n2, pos := pos2}
  suffices : n1 = n2, by simpa only [], -- current goal : n1 = n2, but why?
  exact h
end

Is simp matching the parts of the structure that live in Prop and decides that we need n1 = n2? How is it deciding that? Just by unification? (PS. I know that Lean auto-generates test.mk.inj_eq)

Floris van Doorn (Aug 14 2019 at 21:37):

Yeah, probably these mk.inj_eq lemmas are hard-coded to be simp lemmas, even when doing simp only.

Rob Lewis (Aug 14 2019 at 21:53):

The simp option constructor_eq := ff disables this behavior.

Kevin Kappelmann (Sep 03 2019 at 14:32):

Given a function f taking arguments from a set S : set ℕ, a number n : ℕ, and the knowledge that n ∈ S, how can I apply f on n, that is, how do I make this work:

import data.set.basic

example {S : set } {n : } (f : S  ) (hyp : n  S) :  := f n -- type mismatch

Chris Hughes (Sep 03 2019 at 14:36):

f ⟨n, hyp⟩

Kevin Kappelmann (Sep 03 2019 at 14:37):

Ahh, right! Thanks - you're the best!

Patrick Massot (Sep 03 2019 at 14:42):

Maybe the piece you missed is that Lean can make sense of (f : S → ℕ) only because it inserts a coercion of S to the subtype {n : ℕ // n ∈ S}

Kevin Kappelmann (Sep 03 2019 at 14:53):

Maybe the piece you missed is that Lean can make sense of (f : S → ℕ) only because it inserts a coercion of S to the subtype {n : ℕ // n ∈ S}

That was indeed the case :)

Gihan Marasingha (Sep 08 2019 at 09:17):

I tried proving a very simple result, the formula 6i=0ni2=n(n+1)(2n+1)6\sum_{i=0}^n i^2 = n(n+1)(2n+1). My Lean proof, using equational reasoning, is much longer than I anticipated (30 lines). Could someone suggest a way to shorten the proof?

theorem ssquares_formula (n : ) : 6*(ssquares n) = n*(n+1)*(2*n+1) :=
nat.rec_on n
rfl -- trivial base case
(
assume k,
assume h :  6*(ssquares k) = k*(k+1)*(2*k+1),
show 6*(ssquares (k+1)) = (k+1)*((k+1)+1)*(2*(k+1)+1), from
calc
6*(ssquares (k+1)) = 6*((k+1)*(k+1) + (ssquares k)) : rfl
    ... = 6*((k+1)*(k+1)) + k*(k+1)*(2*k+1) : by rw [left_distrib, h]
    ... = 6*(k+1)*(k+1) + k*(k+1)*(2*k+1) : by rw mul_assoc
    ... = (k+1)*6*(k+1) + k*(k+1)*(2*k+1) : by rw mul_comm 6 (k+1)
    ... = (k+1)*6*(k+1) + (k+1)*k*(2*k+1) : by rw mul_comm k (k+1)
    ... = (k+1)*(6*(k+1)) + (k+1)*(k*(2*k+1)) : by rw [mul_assoc, mul_assoc]
    ... = (k+1)*(6*(k+1)+(k*(2*k+1))) : by rw left_distrib
    ... = (k+1)*(6*k+6+(k*(2*k+1))) : by rw [left_distrib 6 k 1, mul_one]
    ... = (k+1)*(6*k+6+(k*(2*k)+k)) : by rw [left_distrib k (2*k) 1, mul_one]
    ... = (k+1)*((3+3)*k+6+(k*(2*k)+k)) : rfl
    ... = (k+1)*(3*k+3*k+6+(k*(2*k)+k)) : by rw right_distrib 3 3 k
    ... = (k+1)*(3*k+3*k+(6+k*(2*k))+k) : by rw [add_assoc,add_assoc]
    ... = (k+1)*(3*k+3*k+(k*(2*k)+6)+k) : by rw add_comm 6 (k*(2*k))
    ... = (k+1)*(3*k+(3*k+k*(2*k))+6+k) : by rw [add_assoc,add_assoc]
    ... = (k+1)*(3*k+(k*(2*k)+3*k)+6+k) : by rw add_comm (3*k) (k*(2*k))
    ... = (k+1)*((3*k+k*(2*k))+3*k+6+k) : by rw [add_assoc,add_assoc]
    ... = (k+1)*((k*(2*k)+3*k)+3*k+6+k) : by rw add_comm (3*k) (k*(2*k))
    ... = (k+1)*(k*(2*k)+3*k+3*k+(6+k)) : by rw add_assoc
    ... = (k+1)*(k*(2*k)+3*k+3*k+(k+6)) : by rw add_comm 6 k
    ... = (k+1)*(k*(2*k)+3*k+(3*k+k)+6) : by rw [add_assoc,add_assoc]
    ... = (k+1)*(k*(2*k)+3*k+(3*k+k*1)+6) : by rw mul_one
    ... = (k+1)*(k*(2*k)+3*k+(3*k+1*k)+6) : by rw mul_comm k 1
    ... = (k+1)*(k*(2*k)+3*k+((3+1)*k)+6) : by rw right_distrib 3 1 k
    ... = (k+1)*(k*(2*k)+3*k+((2*2)*k)+6) : rfl
    ... = (k+1)*(k*(2*k)+3*k+(2*(2*k))+6) : by rw mul_assoc 2 2 k
    ... = (k+1)*(k*(2*k)+3*k+(2*(2*k)+2*3)) : rfl
    ... = (k+1)*(k*(2*k)+k*3+(2*(2*k)+2*3))  : by rw mul_comm 3 k
    ... = (k+1)*(k*(2*k)+k*3+2*((2*k)+3)) : by rw left_distrib 2 (2*k) 3
    ... = (k+1)*(k*(2*k+3)+2*(2*k+3)) : by rw left_distrib k (2*k) 3
    ... = (k+1)*((k+2)*(2*k+3)) : by rw right_distrib k 2 (2*k+3)
    ... = (k+1)*((k+2)*(2*k+2*1+1)) : rfl
    ... = (k+1)*((k+2)*(2*(k+1)+1)) : by rw left_distrib
    ... = (k+1)*(k+2)*(2*(k+1)+1) : by rw mul_assoc
    ... = (k+1)*((k+1)+1)*(2*(k+1)+1) : rfl
)

Keeley Hoek (Sep 08 2019 at 09:20):

What is your definition of ssquares?

Gihan Marasingha (Sep 08 2019 at 09:21):

def ssquares :   
| 0     := 0
| (n+1) := (n+1)*(n+1) + (ssquares n)

Keeley Hoek (Sep 08 2019 at 09:23):

Let me introduce you to the ring tactic of mathlib:

import tactic

def ssquares :   
| 0     := 0
| (n+1) := (n+1)*(n+1) + (ssquares n)

theorem ssquares_formula (n : ) : 6*(ssquares n) = n*(n+1)*(2*n+1) :=
nat.rec_on n
rfl -- trivial base case
$
assume k,
assume h :  6*(ssquares k) = k*(k+1)*(2*k+1),
show 6*(ssquares (k+1)) = (k+1)*((k+1)+1)*(2*(k+1)+1), from
calc
6*(ssquares (k+1)) = 6*((k+1)*(k+1) + (ssquares k)) : rfl
    ... = 6*((k+1)*(k+1)) + k*(k+1)*(2*k+1) : by rw [left_distrib, h]
    ... = (k+1)*((k+1)+1)*(2*(k+1)+1) : by ring

Keeley Hoek (Sep 08 2019 at 09:24):

But really, mathlib has machinery for doing finite sums already, without starting from the beginning e.g. with your definition of ssquares. A place to start to look is in data.finset, for finset.sum.

Gihan Marasingha (Sep 08 2019 at 09:25):

@Keeley Hoek thanks so much.

Keeley Hoek (Sep 08 2019 at 09:34):

@Gihan Marasingha , here is also a way it could be done in tactic mode

import tactic

def ssquares :   
| 0     := 0
| (n + 1) := (ssquares n) + (n + 1) * (n + 1)

theorem ssquares_formula (n : ) : 6 * (ssquares n) = n * (n + 1) * (2 * n + 1) :=
begin
  induction n with k h,
  { refl },
  { dsimp [ssquares],
    rw [left_distrib, h],
    rw (show nat.succ k = k + 1, from rfl), -- It is sad that this line is needed
    ring, }
end

Keeley Hoek (Sep 08 2019 at 09:34):

@Mario Carneiro, why is the commented line needed there? Is this a bug?

Mario Carneiro (Sep 08 2019 at 09:37):

Ring doesn't know about succ

Keeley Hoek (Sep 08 2019 at 09:37):

How do we make it

Mario Carneiro (Sep 08 2019 at 09:37):

I guess it is one more case in the recursion

Keeley Hoek (Sep 08 2019 at 09:39):

Is there any appetite for some tactic-ish thing normalize which does all these common rewrites? Kevin could make his induction' just (essentially) induction >> normalize

Mario Carneiro (Sep 08 2019 at 09:45):

I think the normalization is domain specific

Gihan Marasingha (Sep 09 2019 at 19:19):

Lean works fine on my Windows machine, but I have a couple of issues on the Mac.

My main problems is that VS Code doesn't seem to access the added dependencies.

From the Mac command line, If I move into the directory of the Lean project I've already created on Windows (and synced via OneDrive) to which I have already added mathlib as a dependency, I can execute .lean files perfectly, but I get import resolution errors when attempting the same through VS Code.

For example, if the file tsum.lean has the contents

import algebra.big_operators

open finset

def tsum (n : ) :  :=
    sum (range (n+1)) (λ i, i)

#eval tsum 10

then executing, at the terminal, lean tsum.lean produces the expected output 55.

But VS Code presents me with the error messages:

file 'algebra/big_operators' not found in the LEAN_PATH

invalid import: algebra.big_operators
could not resolve import: algebra.big_operators

Bryan Gin-ge Chen (Sep 09 2019 at 19:45):

Did you open the Lean package directory in VS Code or just the file tsum.lean? The vscode-lean extension won't work properly unless you do the former.

Gihan Marasingha (Sep 09 2019 at 20:22):

Thanks so much @Bryan Gin-ge Chen ! It wasn't a Mac problem after all. Just me not understanding how to use VS Code.

Bryan Gin-ge Chen (Sep 09 2019 at 20:23):

We could probably have the extension pop up a warning if it can't find a leanpkg.toml file.

Kevin Buzzard (Sep 09 2019 at 21:19):

@Gihan Marasingha very early on in my Lean career I wrote a blog post about proving mathematical stuff by induction: https://xenaproject.wordpress.com/2018/03/30/proofs-by-induction/ . The first response in the comments is by a 1st year undergraduate giving lots of examples of how one can do various things in Lean, maybe it's helpful for you? He includes a link to his code.

Sayantan Majumdar (Sep 19 2019 at 22:32):

is there something wrong in the documentation in "prepositions and proof" in the "3.6 Examples of Prepositional Validities" when they write
p \and false \iff false

Kevin Buzzard (Sep 19 2019 at 22:35):

...that example : ¬(p ↔ ¬p) := sorry can be done constructively? That's what people usually ask about this section ;-) Yes, it can be done constructively.

Sayantan Majumdar (Sep 19 2019 at 22:36):

no is there something wrong with p \and false \iff false

Kevin Buzzard (Sep 19 2019 at 22:36):

I think that one is OK.

Sayantan Majumdar (Sep 19 2019 at 22:38):

how does false implies p is false?

Kevin Buzzard (Sep 19 2019 at 22:38):

false implies anything ;-)

Kevin Buzzard (Sep 19 2019 at 22:38):

example (p : Prop) : p  false  false :=
begin
  split,
  { intro h,
    cases h,
    assumption,
  },
  {
    intro h,
    cases h,
  }
end

Kevin Buzzard (Sep 19 2019 at 22:38):

false is an inductive type with no constructors, so the induction principle for false, namely false.elim is just false -> X for any X.

Kevin Buzzard (Sep 19 2019 at 22:39):

It might be easier to think of things in terms of classical logic. Then P is either true or false, and in either case we see both sides are false.

Kevin Buzzard (Sep 19 2019 at 22:40):

example (p : Prop) : p  false  false := and.right, false.elim

Sayantan Majumdar (Sep 19 2019 at 22:44):

thanks

Yufan Lou (Oct 02 2019 at 01:35):

How do I fix this?

import data.nat.modeq
import tactic.library_search

example (a b : ) : a^2  b^2 [MOD 2]  a  b [MOD 2] :=
begin
  assume hyp,
  have : 2  b^2 - a^2, from nat.modeq.dvd_of_modeq hyp,
end
7:29 error:
invalid type ascription, term has type
  ↑2 ∣ ↑(b ^ 2) - ↑(a ^ 2)
but is expected to have type
  2 ∣ b ^ 2 - a ^ 2
state:
2 goals
a b : ℕ,
hyp : a ^ 2 ≡ b ^ 2 [MOD 2]
⊢ 2 ∣ b ^ 2 - a ^ 2

a b : ℕ,
hyp : a ^ 2 ≡ b ^ 2 [MOD 2],
this : 2 ∣ b ^ 2 - a ^ 2
⊢ a ≡ b [MOD 2]

Floris van Doorn (Oct 02 2019 at 02:01):

Your have : 2 ∣ b^2 - a^2 statement is probably not what you want here. The b^2 and a^2 are natural numbers, so - is subtraction between natural numbers. To make the output a natural number, so we define it as 0 when a^2 > b^2. This "truncated" subtraction is very often not what you want to use.

Floris van Doorn (Oct 02 2019 at 02:01):

So you want to give Lean a hint that you want to use integer subtraction:

import data.nat.modeq data.int.all

example (a b : ) : a^2  b^2 [MOD 2]  a  b [MOD 2] :=
begin
  assume hyp,
  have : 2  (b : )^2 - a^2, exact_mod_cast nat.modeq.dvd_of_modeq hyp,
  -- rw [nat.modeq.modeq_iff_dvd] at hyp, norm_cast at hyp,
end

Floris van Doorn (Oct 02 2019 at 02:02):

I added an import, so that Lean knows about exponentiation of integers. You can alternatively also rewrite with nat.modeq.modeq_iff_dvd (see commented out code)

Floris van Doorn (Oct 02 2019 at 02:03):

norm_cast and exact_mod_cast are useful tactics which will automatically deal with the cast from nat to int.

Floris van Doorn (Oct 02 2019 at 02:03):

See https://github.com/leanprover-community/mathlib/blob/master/docs/tactics.md#norm_cast

Kevin Buzzard (Oct 02 2019 at 06:30):

One very confusing point for beginners is that the natural number 2 and the integer 2 are not "equal" in Lean -- indeed it doesn't even make sense to ask that they are equal, because they don't have the same type. I would suggest that you work with integers all the way through rather than natural numbers, because what you want is true for integers. Of course then you have to change all the functions you use to the integer version :-/ Yes, this isn't like the way mathematicians treat it -- I know :-/

Yufan Lou (Oct 02 2019 at 22:44):

LOL I really overlooked that, thanks a lot

Yufan Lou (Oct 02 2019 at 22:47):

I guess it's just better to work under integer most of the time

Reid Barton (Oct 03 2019 at 02:14):

Clearly the natural numbers weren't meant to be formalized

Yufan Lou (Oct 03 2019 at 02:19):

example (a b : ) : a^2  b^2 [ZMOD 2]  a  b [ZMOD 2] :=
begin
  assume hyp,
  have : 2  b^2 - a^2, from int.modeq.modeq_iff_dvd.mp hyp,
  have : 2  (b - a) * (b + a), by library_search,
  library_search
end

back at it but this gives me fail, meanwhile I found this with library_search

example (a b : ) : a^2 - b^2 = (a + b) * (a - b) := sq_sub_sq a b

How do I apply it?

Yufan Lou (Oct 03 2019 at 02:29):

nvm I was dumb XD forgot to specify at with rw

Yufan Lou (Oct 03 2019 at 02:47):

ahhhh... now I have to apply the fundamental theorem of arithmetic but don't know how

Jesse Michael Han (Oct 03 2019 at 03:57):

you can do it by case exhaustion on a and b:

-- requires import data.zmod.basic
example (a b : ) : a^2  b^2 [ZMOD 2]  a  b [ZMOD 2] :=
begin
  assume hyp,
  cases int.mod_two_eq_zero_or_one a with H₁ H₁;
  cases int.mod_two_eq_zero_or_one b with H₂ H₂,
    { change _  0 [ZMOD 2] at H₁, change _  0 [ZMOD 2] at H₂,
      have := @zmod.eq_iff_modeq_int 2, simp at this,
      rw this at  H₁ H₂ hyp, cc },
    { change _  0 [ZMOD 2] at H₁, change _  1 [ZMOD 2] at H₂,
      have := @zmod.eq_iff_modeq_int 2, simp at this,
      rw this at  H₁ H₂ hyp,
      suffices : (0 : zmod 2) = (1 : zmod 2),
        by simp*,
      simp at H₁ H₂, simp[*, -hyp] at hyp, simpa using hyp },
    { change _  1 [ZMOD 2] at H₁, change _  0 [ZMOD 2] at H₂,
      have := @zmod.eq_iff_modeq_int 2, simp at this,
      rw this at  H₁ H₂ hyp,
      suffices : (1 : zmod 2) = (0 : zmod 2),
        by simp*,
      simp at H₁ H₂, simp[*, -hyp] at hyp, simpa using hyp },
    { change _  1 [ZMOD 2] at H₁, change _  1 [ZMOD 2] at H₂,
      have := @zmod.eq_iff_modeq_int 2, simp at this,
      rw this at  H₁ H₂ hyp, cc }
end

there's also fermat's little theorem for zmod, which might give a cleaner proof

Kenny Lau (Oct 03 2019 at 04:28):

import data.zmod.quadratic_reciprocity

theorem frob {p : } (hp : nat.prime p) {a : zmodp p hp} : a ^ p = a :=
decidable.by_cases
  (assume ha : a = 0, by rw [ha, zero_pow hp.pos])
  (assume ha : a  0, by conv_lhs { congr, skip, rw  nat.sub_add_cancel (show p  1, from le_of_lt hp.one_lt) };
    rw [pow_add, zmodp.fermat_little hp ha, pow_one, one_mul])

theorem frob' {p : } (hp : nat.prime p) {a : } : a ^ p  a [ZMOD p] :=
by rw [ zmodp.eq_iff_modeq_int hp, int.cast_pow, frob]

example (a b : ) (h : a^2  b^2 [ZMOD 2]) : a  b [ZMOD 2] :=
calc  a  a^2 [ZMOD 2] : (frob' nat.prime_two).symm
    ...  b^2 [ZMOD 2] : h
    ...  b [ZMOD 2] : frob' nat.prime_two

Yufan Lou (Oct 03 2019 at 08:30):

Thank you both! I am understanding the proofs by tracing them step by step. I see that the key is to understand zmod n itself as a number type and either turning the mod congruence in nat into equality in zmod n and prove it there, or turning equality in zmod n into mod congruence in nat and apply them here. Hmm... I have a vague intuition about this from my Haskell experience, but I need to think hard about how to explain this to my maths colleagues...

I still don't get many intricacies in there either, like why I need to show 0 = 1 and why -hyp is necessary in simp[*, -hyp]. conv_lhs has not yet been documented. The first two steps congr, skip seems very arbitrary. Although I understand the proofs now, I can hardly imagine writing them.

Chris B (Oct 18 2019 at 18:57):

Is there a nicer way of doing this?

lemma always_less :  (a : ), (if a < 5 then a else 2) < 6 :=
λ a, decidable.cases_on (nat.decidable_lt a 5)
  (λ hnc, dec_trivial)
  (λ hc, show a < 6, from lt.step hc)

Kenny Lau (Oct 18 2019 at 20:06):

import tactic.split_ifs tactic.omega

lemma always_less (a : ) : (if a < 5 then a else 2) < 6 :=
by split_ifs; omega

Chris B (Oct 18 2019 at 20:58):

Rad, thanks. I didn't know about split_ifs.

Kenny Lau (Oct 18 2019 at 21:00):

I'm Kenny, but you're welcome

Yury G. Kudryashov (Oct 19 2019 at 14:31):

How do I prove ∀ x : ℤ, 0 ≤ x → x ≤ 1 → x ∈ ({0, 1} : set ℤ)?

Yury G. Kudryashov (Oct 19 2019 at 14:36):

Do we have something like fintype {m : ℤ | a ≤ m /\ m < b}?

Kevin Buzzard (Oct 19 2019 at 15:42):

There was a thread about this only recently

Kevin Buzzard (Oct 19 2019 at 15:43):

I mean the fintype

Kevin Buzzard (Oct 19 2019 at 15:50):

import tactic.interactive

example :  x : , 0  x  x  1  x  ({0, 1} : set ) :=
begin
  intros x h0 h1,
  rw le_iff_lt_or_eq at h0,
  cases h0, swap,
    rw h0, right, left, refl,
  rw [int.lt_iff_add_one_le, zero_add, le_iff_lt_or_eq] at h0,
  cases h0,
    exfalso,
    rw lt_iff_not_ge at h0,
    apply h0,
    exact h1,
  left, exact h0.symm,
end

Kevin Buzzard (Oct 19 2019 at 15:52):

https://leanprover.zulipchat.com/#narrow/stream/116395-maths/topic/Some.20numerology That's more than you need for the fintype. As you can see, I partially PR'ed it (some nat lemmas) and then teaching started at my university and I never finished the job. Feel absolutely free to PR that stuff! I'm still snowed under until November.

Yury G. Kudryashov (Oct 19 2019 at 15:53):

Thank you for the link. I'll possibly PR it next week.

Patrick Massot (Oct 19 2019 at 21:00):

The correct thing to do is probably to resume work on https://github.com/leanprover-community/mathlib/tree/nat_cases

Chris B (Oct 20 2019 at 17:10):

If anyone has opinions about when to prefer a decidable prop over a boolean valued function as a predicate I'd like to read them if you get a moment. In a general context like lists, core doesn't seem to take a hard line stance either way IE list.filter and list.partition use decidable predicates whereas list.all and list.any use a boolean valued function. Mathlib often defines a function for both.

Andrew Ashworth (Oct 20 2019 at 17:39):

If you're going to still use Lean next year, I would use a bool-valued function

Andrew Ashworth (Oct 20 2019 at 17:40):

Lean 4's core library is moving towards explicit usage of boolean-valued predicates

Chris B (Oct 20 2019 at 18:37):

Thanks, that definitely simplifies things, bool it is then.

Yury G. Kudryashov (Oct 25 2019 at 13:19):

Hi, is it possible to express ∃ b > a, p b using < only? I understand that I can write ∃ b (H : a < b), p b but this is less readable.

Kevin Buzzard (Oct 25 2019 at 13:21):

I think what you suggest (the less readable thing) is what we're supposed to be writing nowadays.

Seewoo Lee (Oct 25 2019 at 13:56):

Hi, here's a noob question: I'm trying to prove currying & uncurrying, i.e. (p -> q -> r) <-> ((p \and q) -> r). Here's my try:

theorem t3 : ((p ∧ q) → r) → (p → q → r) :=
    assume h₁ : (p ∧ q) → r,
    assume hp : p,
    assume hq : q,
    have h₂ : p ∧ q, from and.intro hp hq,
    h₁ h₂

theorem t4 : (p → q → r) → ((p ∧ q) → r) :=
    assume h₁ : p → q → r,
    assume h₂ : p ∧ q,
    have hp : p, from and.left h₂,
    have hq : q, from and.right h₂,
    have h₃ : q → r, from h₁ hp,
    have hr : r, from h₃ hq
    hr

t3 seems to have no problem, but t4 has. It gives an error like

function expected at
  h₃ hq
term has type
  r

and

unknown identifier 'hr'

but I can't find the reason. Any helps?

Seewoo Lee (Oct 25 2019 at 13:58):

I forgot one: I put variables p q r : Prop before t3 and t4, of course.

Kenny Lau (Oct 25 2019 at 13:59):

because you forgot a comma

Kevin Buzzard (Oct 25 2019 at 14:00):

these proofs are so hard to debug because they're not in tactic mode :-/

Seewoo Lee (Oct 25 2019 at 14:00):

Oh you're right, thanks!

Seewoo Lee (Oct 25 2019 at 14:00):

Is there any efficient way to do these? I'm still not familiar with lean's grammar.

Kevin Buzzard (Oct 25 2019 at 14:01):

"Don't forget the comma" occurs at least once in about the first six levels of http://wwwf.imperial.ac.uk/~buzzard/xena/natural_number_game/

Kevin Buzzard (Oct 25 2019 at 14:01):

because extensive testing showed that it was by far the most common error which newcomers made.

Kevin Buzzard (Oct 25 2019 at 14:01):

often in bold face

Kenny Lau (Oct 25 2019 at 14:02):

Is there any efficient way to do these? I'm still not familiar with lean's grammar.

variables p q r : Prop

theorem t3 : ((p  q)  r)  (p  q  r) :=
λ h₁ hp hq, h₁ hp, hq

theorem t4 : (p  q  r)  ((p  q)  r) :=
λ h₁ h₂, h₁ h₂.1 h₂.2

Seewoo Lee (Oct 25 2019 at 14:03):

@Kenny Lau This is much better, thanks!

Kevin Buzzard (Oct 25 2019 at 14:04):

theorem t4 : (p  q  r)  ((p  q)  r) :=
begin
  intro h₁, -- I can see what is happening
  intro h₂, -- I can still see what is happening
  have hp : p, -- oh wow now I can see I have two goals
    exact and.left h₂, -- now back down to one
  have hq : q, -- this is so much better than what TPIL chapter 3 says
    exact and.right h₂, -- I wish I had read chapter 5 much earlier
  have h₃ : q  r, -- :D
    exact h₁ hp,
  have hr : r,
    exact h₃ hq,
  exact hr,
end

Kevin Buzzard (Oct 25 2019 at 14:04):

Kenny's method is a pure term mode proof. To write these you need to know the trick with _

Kevin Buzzard (Oct 25 2019 at 14:05):

This one was particularly easy, but in a longer one you can write something like

theorem t4 : (p  q  r)  ((p  q)  r) :=
λ h₁ h₂, h₁ _ _

and hover over the _s to see what needs to go there.

Kevin Buzzard (Oct 25 2019 at 14:06):

In tactic mode you can see everything at all times, so it's better for mathematician beginners. What Kenny is saying is perhaps more appealing for CS beginners, as long as you know the _ trick. You are just making the terms explicitly there.

Seewoo Lee (Oct 25 2019 at 14:09):

Yes I agree with you, and I just know what tactic mode is! Both seems great for me but tactic thing seems more intuitive.

Jason Gross (Oct 30 2019 at 04:08):

Question: does simp / rewrite work under binders?

Kevin Buzzard (Oct 30 2019 at 04:13):

not very well. Suggestions: (1) erw "extended rewrite" sometimes gets to places standard rw doesn't get to. (2) learn about conv mode: https://github.com/leanprover-community/mathlib/blob/master/docs/extras/conv.md

Johan Commelin (Oct 30 2019 at 05:27):

Actually simp usually does quite well, I think. But rw is really bad at this.

Jason Gross (Oct 30 2019 at 06:47):

Thanks

Jason Gross (Oct 30 2019 at 06:47):

Another question: Does pp.max_depth do anything? I have it set to 1000000000 and still see in my goal

Floris van Doorn (Oct 30 2019 at 13:14):

The _ in your goal is likely a proof. Use pp.proofs

Jason Gross (Oct 30 2019 at 19:31):

@Floris van Doorn It's not _, it's , and my term has ~no proofs. I see things like […, …, …, …, (… * 2 ^256, …), (and then more), I don't see how these could possibly be proofs

Chris Hughes (Oct 30 2019 at 19:41):

There's another pp option that does it. I don't remember what it's called.

Floris van Doorn (Oct 30 2019 at 19:41):

Oh, I have never encountered that. Does setting pp.max_steps help?

Jason Gross (Oct 30 2019 at 20:00):

That does seem to help, but not interactively. I get 11:1: excessive memory consumption detected at 'replace' (potential solution: increase memory consumption threshold) And it takes something like 15 GB on the command line. Here's an example:

@[simp]
def big :     
| v 0 := v
| v (nat.succ c) := big (v + v) c

set_option pp.max_depth 1000000000
set_option pp.max_steps 1000000000
example (v : ) : big v 20 = 0 :=
begin
  simp only [big]
end

Jason Gross (Oct 30 2019 at 20:36):

If I get kernel failed to type check declaration 'int.pow_of_nat' this is usually due to a buggy tactic or a bug in the builtin elaborator on a definition I'm giving by rfl, is that a bug in lean?

Scott Morrison (Oct 30 2019 at 20:42):

Probably not... :-) Can you minimise your example enough to post here?

Jason Gross (Oct 30 2019 at 20:58):

def int.pow_nat :     
| (int.of_nat b) e := int.of_nat (b ^ e)
| (int.neg_succ_of_nat b) 0 := 1
| (int.neg_succ_of_nat b) (k+1) := (int.neg_succ_of_nat b) * int.pow_nat (int.neg_succ_of_nat b) k

instance int_has_pow_nat : has_pow int nat := int.pow_nat

def int.pow (b : ) :   
| (int.of_nat n) := b ^ n
| (int.neg_succ_of_nat n) := 0

instance : has_pow int int := int.pow

@[simp]
def int.pow_of_nat (b : ) (e : ) : int.pow b e = int.of_nat (b ^ e) := rfl
-- 15:5: kernel failed to type check declaration 'int.pow_of_nat' this is usually due to a buggy tactic or a bug in the builtin elaborator
-- elaborated type:
--   ∀ (b e : ℕ), int.pow ↑b ↑e = int.of_nat (b ^ e)
-- elaborated value:
--   λ (b e : ℕ), rfl
-- nested exception message:
-- type mismatch at definition 'int.pow_of_nat', has type
--   ∀ (b e : ℕ), int.pow ↑b ↑e = int.pow ↑b ↑e
-- but is expected to have type
--   ∀ (b e : ℕ), int.pow ↑b ↑e = int.of_nat (b ^ e)

Scott Morrison (Oct 30 2019 at 21:27):

Looks like a bug to me... Hopefully someone who knows better will chime in soon.

Jason Gross (Oct 30 2019 at 21:28):

reported as https://github.com/leanprover-community/lean/issues/76

Chris Hughes (Oct 30 2019 at 22:06):

I don't think it is a bug. I wouldn't expect rfl to be able to prove that, you should have to induct on e first.

I don't think that's the usual error message, but I think that might be because it is a def rather than a lemma which would be more usual.

Jason Gross (Oct 30 2019 at 22:15):

If I do def bad : true = false := rfl, then I get a standard type error, without the extra words about it being possibly a bug

Reid Barton (Oct 30 2019 at 22:52):

It looks to me like the elaborator thought that int.pow_nat (int.of_nat b) e would reduce to int.of_nat (b ^ e), but if you #print int.pow_nat._main you can see it actually won't.

Reid Barton (Oct 30 2019 at 22:53):

Surprisingly to me and the elaborator (but apparently not to Chris) the equation compiler generated an outermost match on the second argument, not the first.

Jason Gross (Oct 30 2019 at 23:32):

If I get error: deep recursion was detected at 'expression equality test' (potential solution: increase stack space in your system), how do I increase stack space?

Mario Carneiro (Oct 30 2019 at 23:34):

It's a command line argument to lean

Mario Carneiro (Oct 30 2019 at 23:35):

but there is an adage that says that if you have to increase resource limits then you are probably doing something wrong

Mario Carneiro (Oct 30 2019 at 23:35):

I hope you don't actually expect that big example to work, it's a term 2^20 large

Jason Gross (Oct 30 2019 at 23:37):

@Mario Carneiro No, I'm getting this error on a different example, the big example was just creating something to exhibit the .... The thing I'm trying to do is in https://github.com/mit-plv/fiat-crypto/blob/6aaf03aed733d08ff124dffded9d8e2c7f4cc25a/src-lean/fiat_crypto.lean#L405, trying to get the norm_num tactic to finish when I change open ex to open ex2 (because ex was just a toy example; ex2 is the actual code I'm trying to work on)

Mario Carneiro (Oct 30 2019 at 23:39):

For these kinds of big CS problems, my recommendation is to make sure you have clean inputs to the tactics

Jason Gross (Oct 30 2019 at 23:39):

What do you mean by "clean inputs"?

Mario Carneiro (Oct 30 2019 at 23:39):

If you can, use natural number equalities and known functions only to norm_num

Mario Carneiro (Oct 30 2019 at 23:40):

give norm_num space to decide the problem and simplify the if statement afterward

Mario Carneiro (Oct 30 2019 at 23:40):

you may need to write a tactic to do this, the standard interactive mode chaining is probably not sufficient

Mario Carneiro (Oct 30 2019 at 23:41):

You can still use the existing tactics, but you are doing something specific by unfolding some functions, targeting a particular subterm for norm_num simplification, then going back to the simplifier and so on

Mario Carneiro (Oct 30 2019 at 23:42):

the norm_num front end handles this by just iterating norm_num1, simp, norm_num1, simp until stuff stops changing but that's only a heuristic

Jason Gross (Oct 30 2019 at 23:48):

I mean, iterating norm_num1, simp, ... seems not wrong, and in fact the call I have there produces the correct output for the smaller toy example. (I can't see if it produces correct output on a slightly larger example because I get the recursion depth error)

Mario Carneiro (Oct 31 2019 at 00:06):

So I looked through that fiat-crypto file, and what I see are 300 lines of definitions without any theorems, where almost every definition is marked for unfolding. This will explode in lean

Mario Carneiro (Oct 31 2019 at 00:07):

What you want are rewrites that unfold what matters, in the cases that matter. If you have a recursive definition (defined with *.rec), it should never be directly unfolded; instead you should have a theorem for the various constructors of the inductive type that you are recursing on

Mario Carneiro (Oct 31 2019 at 00:08):

What is the point of the let_in definition? It seems plainly useless

Mario Carneiro (Oct 31 2019 at 00:09):

Note that lean actually has a let construct

Jason Gross (Oct 31 2019 at 00:12):

@Mario Carneiro The let_in definition is one of the very few definitions that should never be unfolded

Jason Gross (Oct 31 2019 at 00:14):

After reduction/unfolding/rewriting, the only remaining definitions which are not unfolded should be *, +, %, /, and let_in

Jason Gross (Oct 31 2019 at 00:15):

I can change my definitions to not be in terms of recursors and instead be recursive themselves, but I don't see how that will fix anything. The times to unfold them are in fact when the underlying recursor is ready to be reduced

Mario Carneiro (Oct 31 2019 at 00:21):

The following definitions should not be simp: list.flat_map (aka list.bind), list.combine (aka list.zip), int.to_nat_bit(0/1) (the if statement should be a precondition), associational.eval, mul, definitely square, negate_snd, split, reduce, definitely reduce_square, carryterm, positional.from_associational, from_associational_cons, chained_carries, chained_carries_no_reduce.

Mario Carneiro (Oct 31 2019 at 00:22):

You don't necessarily need to change the definition (although using the equation compiler will do some of these things automatically for you), but you should add equational lemmas in the cases where you want the definition to unfold. Asking everything to unfold all at once is asking for trouble

Jason Gross (Oct 31 2019 at 00:25):

Wait, why should list.combine not be simp? It's just there so that I could make code translation easier, it literally just unfolds to list.zip (which I don't touch the flags of). from_associational_cons is telling from_associational to reduce in as close a match to the case where it should that I can get (in the case where it's applied to a concrete list)

Mario Carneiro (Oct 31 2019 at 00:25):

For example:

open prod

@[simp]
def associational.eval (p : list ( × )) :  :=
  list.foldr (λ x y, x + y) 0 (list.map (λ p, fst p * snd p) p)

should be

def associational.eval (p : list ( × )) :  :=
  list.foldr (λ x y, x + y) 0 (list.map (λ p, fst p * snd p) p)

@[simp] theorem associational.eval_nil : associational.eval [] = 0 := rfl
@[simp] theorem associational.eval_cons (a b l) :
  associational.eval ((a, b) :: l) = a * b + associational.eval l := rfl

Jason Gross (Oct 31 2019 at 00:26):

Sure. Though I will note that I don't use associational.eval anywhere right now, so not unfolding it is not a high priority

Mario Carneiro (Oct 31 2019 at 00:27):

or

@[simp] def associational.eval : list ( × )  
| [] := 0
| ((a, b) :: l) := a * b + associational.eval l

Mario Carneiro (Oct 31 2019 at 00:27):

It's an example; I can demonstrate on something else if you like

Jason Gross (Oct 31 2019 at 00:27):

Of the ones you listed, I use list.flat_map, list.combine, mul, split, reduce, carryterm, positional.from_associational, and chained_carries

Jason Gross (Oct 31 2019 at 00:30):

@Mario Carneiro Could you demonstrate on reduce (or split)?

Jason Gross (Oct 31 2019 at 00:31):

I'd also be interested in seeing carryterm

Jason Gross (Oct 31 2019 at 00:31):

reduce because I don't see how to write a simplification lemma for it; carryterm because the conditions under which it should reduce are complicated (namely, when fst t and w are numerals/closed terms)

Mario Carneiro (Oct 31 2019 at 00:36):

open prod
def let_in {A B : Type*} (x : A) (f : A  B) := f x

def associational.mul (p q : list ( × )) : list ( × ) := sorry

def associational.split (s : ) (p : list ( × )) : list ( × ) × list ( × ) :=
let (a, b) := list.partition (λ t, (fst t) % s = 0) p in
(b, list.map (λ t, (fst t / s, snd t)) a)

@[simp] theorem associational.split_val {s : } {p : list ( × )} {a b}
  (h : list.partition (λ t, (prod.fst t) % s = 0) p = (a, b)) :
  associational.split s p = (b, list.map (λ t, (fst t / s, snd t)) a) :=
by rw [associational.split, h, associational.split]

def associational.reduce (s c p) : list ( × ) :=
let (a, b) := associational.split s p in
a ++ associational.mul c b

@[simp] theorem associational.reduce_val (s c p) {a b}
  (h : associational.split s p = (a, b)) :
  associational.reduce s c p = a ++ associational.mul c b :=
by rw [associational.reduce, h, associational.reduce]

Mario Carneiro (Oct 31 2019 at 00:39):

def associational.carryterm (w fw:) (t: × ) :=
  if (fst t = w)
  then let_in (snd t)         (λ t2,
       let_in (t2 / fw)       (λ d2,
       let_in (t2 % fw) (λ m2,
       [(w * fw, d2), (w,m2)])))
  else [t]

@[simp] theorem associational.carryterm_pos (w fw b) :
  associational.carryterm w fw (w, b) =
  let_in b               (λ t2,
  let_in (t2 / fw)       (λ d2,
  let_in (t2 % fw) (λ m2,
  [(w * fw, d2), (w,m2)]))) := if_pos rfl

@[simp] theorem associational.carryterm_neg {w fw a b}
  (h : a  w) : associational.carryterm w fw (a, b) = [(a, b)] := if_neg h

Mario Carneiro (Oct 31 2019 at 00:41):

The basic idea is to have all arguments to the functions be in weak head normal form (i.e. for a list (Z x Z) it should be an empty list or a cons of a pair), and additionally when there are destructuring lets or other pattern matches inside the function the results of these should be hypotheses

Mario Carneiro (Oct 31 2019 at 00:42):

lean's equation compiler gives you something similar with an auxiliary function; if you print the definition of associational.split you will see that the destructuring let was done by creating an auxiliary that is defined by pattern matching

Jason Gross (Oct 31 2019 at 00:43):

And then how do I make use of these? Do I interleave "prove side-conditions with refl with simp for split_val? And it looks like rewriting with associational.carryterm_pos is incompatible with rewriting with associational.carryterm_neg; how do I get lean to pick the right one to rewrite with and prove the side-condition automatically?

Mario Carneiro (Oct 31 2019 at 00:43):

simp will do this

Mario Carneiro (Oct 31 2019 at 00:44):

But I should admit that lean has for a long time needed a tactic like Coq's cbv, for just doing evaluations like this

Mario Carneiro (Oct 31 2019 at 00:46):

simp uses an auxiliary side condition discharger (that is by default simp to true) for using conditional rewrite lemmas

Kevin Buzzard (Oct 31 2019 at 00:46):

What does CBV stand for?

Mario Carneiro (Oct 31 2019 at 00:47):

norm_num uses norm_num as the simp discharger, so it should be able to do the numeric part even inside if conditions and such

Mario Carneiro (Oct 31 2019 at 00:47):

call by value

Mario Carneiro (Oct 31 2019 at 00:47):

there is also cbn for call by name

Mario Carneiro (Oct 31 2019 at 00:47):

it refers to the order of evaluation. It's basically treating terms as a functional program and evaluating functions according to their definitions

Mario Carneiro (Oct 31 2019 at 00:48):

which is what rfl does already, but rfl has to go to completion while the hypothetical cbv tactic would just stop when it gets stuck

Mario Carneiro (Oct 31 2019 at 00:48):

right now people have to write mile-long simp invocations unfolding everything in sight

Mario Carneiro (Oct 31 2019 at 00:49):

or use carefully designed simp lemmas like the ones I've given above to guide the evaluation

Jason Gross (Oct 31 2019 at 01:09):

@Mario Carneiro if I try that, rw [associational.split_val] gives rewrite tactic failed, motive is not type correct and nested exception message: check failed, application type mismatch (use 'set_option trace.check true' for additional details)

Mario Carneiro (Oct 31 2019 at 01:09):

Are you rewriting in an if statement?

Jason Gross (Oct 31 2019 at 01:10):

Yes

Mario Carneiro (Oct 31 2019 at 01:10):

If you use the suggested approach, you should never have to see an if statement, but you can also use simp in these situations to rewrite and also fix the dependent decidability argument

Jason Gross (Oct 31 2019 at 01:14):

Also, associational.carryterm_neg doesn't seem to be triggering when the location to rewrite is under binders?

Jason Gross (Oct 31 2019 at 01:26):

I am not sure if this strategy is going to work; it seems that norm_num [lem1 lems] fails to handle cases where rw [lem1], norm_num [lems], norm_num [lems] works, and rw doesn't really work under binders.

import tactic.norm_num
open prod

universe u

@[simp]
def list.nth_default {A : Type u} (default : A) :  (ls : list A) (n : ), A
| []        _             := default
| (x :: xs) 0             := x
| (x :: xs) (nat.succ n') := list.nth_default xs n'


def associational.split (s : ) (p : list ( × )) : list ( × ) × list ( × ) :=
  let (a, b) := list.partition (λ t, (fst t) % s = 0) p in
  (b, list.map (λ t, (fst t / s, snd t)) a)

@[simp] theorem associational.split_val {s : } {p : list ( × )} {a b}
  (h : list.partition (λ t, (prod.fst t) % s = 0) p = (a, b)) :
  associational.split s p = (b, list.map (λ t, (fst t / s, snd t)) a) :=
by rw [associational.split, h, associational.split]


example (f g : list ) : (associational.split 65536 [(1, list.nth_default 0 f 0 * list.nth_default 0 g 0)]).snd = [] :=
begin
  rw [associational.split_val], norm_num [associational.split_val, (), list.filter], norm_num [associational.split_val, (), list.filter]
end

example (f g : list ) : (associational.split 65536 [(1, list.nth_default 0 f 0 * list.nth_default 0 g 0)]).snd = [] :=
begin
  norm_num [associational.split_val, (), list.filter]
-- 30:3: norm_num failed to simplify
-- state:
-- f g : list ℤ
-- ⊢ (associational.split 65536 [(1, list.nth_default 0 f 0 * list.nth_default 0 g 0)]).snd = list.nil
end

What's going on here?

Mario Carneiro (Oct 31 2019 at 01:41):

rw doesn't work under binders, simp does

Mario Carneiro (Oct 31 2019 at 02:03):

@Jason Gross hm, you may be right. This works:

@[simp] def associational.split (s : ) (p : list ( × )) : list ( × ) × list ( × ) :=
  let (a, b) := list.partition (λ t, (fst t) % s = 0) p in
  (b, list.map (λ t, (fst t / s, snd t)) a)

example (f g : list ) : (associational.split 65536 [(1, list.nth_default 0 f 0 * list.nth_default 0 g 0)]).snd = [] :=
begin
  simp [associational.split, list.filter], norm_num1, simp,
end

I've gone back to simp directly with the definition, but keeping the destructuring let, which prevents the unfolding from getting out of hand.

I should more seriously consider writing some equivalent to cbv that will do all of these things in the right order

Mario Carneiro (Oct 31 2019 at 05:24):

@Jason Gross I managed to get your original ex2 problem to compute to a value with a few modifications: https://gist.github.com/digama0/7649577d7c8af881cdbad50f9e84d81f

Jason Gross (Oct 31 2019 at 05:31):

@Mario Carneiro Thanks!

-def let_in {A : Type u} {B : Type v} (x : A) (f : A → B) := f x
+@[simp] def let_in {A : Type u} {B : Type v} (x : A) (f : A → B) := f x

Uh, it's pretty important to not make this change; it results in ~exponential blowup, and in some of the examples even larger than this one, it becomes even more of a problem. Is it essential to your way of making things work?

Mario Carneiro (Oct 31 2019 at 05:32):

I don't think so, it was mostly to get it out of the way for the other changes. A suitable set of other simp lemmas about let_in should suffice

Mario Carneiro (Oct 31 2019 at 05:32):

Lean does internally deduplicate expressions though, so it shouldn't cause exponential blowup

Mario Carneiro (Oct 31 2019 at 05:33):

basically, your approach is interfering with lean's own approach to the same problem

Jason Gross (Oct 31 2019 at 05:34):

The issue is that the goal is to generate C code, eventually, and we don't want exponential blow-up in the C code output, so we need to not unfold let_in. This is what all of the let_in.lift* lemmas at the bottom are about

Mario Carneiro (Oct 31 2019 at 05:35):

In that case, why is let_in not a constructor?

Jason Gross (Oct 31 2019 at 05:36):

Because I didn't want to re-write a bunch of code to be in a monad

Jason Gross (Oct 31 2019 at 05:36):

But, sure, you could make it a constructor, and sprinkle bind everywhere

Jason Gross (Oct 31 2019 at 05:36):

(But then you also need a way to do list (M T) -> M (list T), etc)

Mario Carneiro (Oct 31 2019 at 05:37):

The thing is that you are also trying to compute with this in lean, and in that context the let_in doesn't make any sense

Mario Carneiro (Oct 31 2019 at 05:38):

And you can't compute a C code expression this way because let_in would be reduced away

Jason Gross (Oct 31 2019 at 05:38):

The final step to get to C would be reifying or otherwise pretty-printing the code

Jason Gross (Oct 31 2019 at 05:39):

-  def positional.to_associational (n:ℕ) (xs:list ℤ) : list (ℤ × ℤ)
-    := list.combine (list.map weight (list.seq 0 n)) xs
+  def positional.to_associational (n:ℕ) (xs:list ℤ) : list (ℕ × ℤ)
+    := list.enum xs

This seems wrong. Did you deliberately remove weight here, or was it an oversight?

Mario Carneiro (Oct 31 2019 at 05:39):

the removal of weight was an oversight

Mario Carneiro (Oct 31 2019 at 05:39):

I wasn't sure about whether the removal of n was correct either

Mario Carneiro (Oct 31 2019 at 05:40):

But if n is much larger than xs the original approach has a lot of unnecessary computation

Jason Gross (Oct 31 2019 at 05:41):

n is always equal to the length of xs

Mario Carneiro (Oct 31 2019 at 05:41):

In that case I would want to have just one loop instead of two here

Jason Gross (Oct 31 2019 at 05:41):

Sure, that seems fine

Jason Gross (Oct 31 2019 at 05:42):

Also, how long should I expect this to take? The code you gave me has already been running for 14 minutes

Mario Carneiro (Oct 31 2019 at 05:42):

my test ran 7 minutes

Jason Gross (Oct 31 2019 at 05:43):

If I pass --tstack=1000000, does that make it slower?

Mario Carneiro (Oct 31 2019 at 05:43):

I doubt it

Jason Gross (Oct 31 2019 at 05:43):

Maybe your machine is more than 2x as fast as mine?

Mario Carneiro (Oct 31 2019 at 05:44):

let me fix the bugs and try again to make sure I posted the right file

Jason Gross (Oct 31 2019 at 05:45):

(Seems doubtful that you're twice as fast as me; my cpu is 3.6 GHz...)

Jason Gross (Oct 31 2019 at 05:46):

What does by exact _match l do in | (a::l) := positional.carry_reduce n s c a (by exact _match l)? (What's _match, and why are you doing it this way?)

Mario Carneiro (Oct 31 2019 at 05:53):

Unfortunately lean does not have Coq's fix constructor for making a recursive definition in the middle of a term. It has match for invoking the equation compiler, but it doesn't expose the name of the recursive function so you can make a recursive call. However it is exposed in tactics with the name _match

Mario Carneiro (Oct 31 2019 at 05:54):

An equivalent approach would be:

    @[simp] def positional.chained_carries_aux (n s c p) : list nat  list 
    | [] := p
    | (a::l) := positional.carry_reduce n s c a (positional.chained_carries_aux l)

    @[simp] def positional.chained_carries (n s c p) (idxs : list nat) :=
    positional.chained_carries_aux n s c p (list.reverse idxs)

Reid Barton (Oct 31 2019 at 05:56):

It's really annoying too when apply notices that this funny _match thing has the same type as the goal (imagine that)

Reid Barton (Oct 31 2019 at 05:56):

Er, maybe not apply but things that try assumption

Mario Carneiro (Oct 31 2019 at 05:58):

oh, I think I know what might be the difference: when I first tested it, I ran the norm_num invocation with carry_mulmod ... = sorry, and it finished after 7 minutes with the goal [0, 0, 0, 0, 0] = sorry. The version I put on the gist has the [0,0,0,0,0] edited in, which means that this also includes the kernel typechecking time

Jason Gross (Oct 31 2019 at 06:41):

If it finishes with [0,0,0,0,0], that's a bug (perhaps the removal of weight that I mentioned?). It's not supposed to reduce to a numeral, it's supposed to reduce to a bunch of let_ins expressing arithmetic about elements of f and g, followed by a list of bound variables

Mario Carneiro (Oct 31 2019 at 06:42):

When I take out the let_in simplification, it blocks on a function that takes a list, being passed a let_in applied to a list literal

Mario Carneiro (Oct 31 2019 at 07:02):

Also, for the record this looks to be a tad bit abusive of simp and regular functions. I have a better idea of what you are trying to do now, and I think that all of these functions should be tactics. They will run a lot faster, and you can still produce proofs along the way

Jason Gross (Oct 31 2019 at 19:04):

@Mario Carneiro Indeed, it blocks on a function taking a list, and that is what the lemmas like let_in.lift_map are for, to unblock reduction without inlining things

Jason Gross (Oct 31 2019 at 19:05):

Which functions should be tactics? The definitions on lists, or the reduction and let-lifting?

Mario Carneiro (Nov 01 2019 at 03:54):

@Jason Gross It's not quite clear to me how many of the functions on lists should be converted to tactics, but the basic idea would be to have all the stuff you actually want in the output (notably the let_in constructs) as exprs, with most of the list functions being functions that manipulate lists of exprs and such, so that you can evaluate them in the VM instead of all the simping.

What isn't clear to me is what you want to assert about the resulting let lifted expression. As it is (assuming the simp stuff can be made to work), you end up with a proof that asserts something like carry_mulmod ... = let_in (f 1 * g 1) (\lam x, let_in ...), but as a theorem this doesn't appear particularly valuable since carry_mulmod is just a function that computes the thing on the right. Presumably you have some semantics about mulmod that you actually care about, and this should be what you want to prove a relation to. Or maybe you don't want to prove it is anything in particular, you just want the expression on the right, in which case it can be done a lot faster in the VM.

Jason Gross (Nov 01 2019 at 03:59):

@Mario Carneiro The theorem I want to prove is that associational.eval (positional.to_associational (carry_mulmod f g)) = (eval f * eval g) % (s - associational.eval c). (Where def asociational.eval (p) := list.foldr (+) 0 (list.map (\lambda (w, v), w * v) p).) Each of the list definitions has a corresponding lemma about what it does under eval, c.f. https://github.com/mit-plv/fiat-crypto/blob/master/src/Arithmetic/Core.v

Jason Gross (Nov 01 2019 at 04:01):

Then I can combine this theorem with the carry_mulmod ... = let_in ... theorem to get a theorem about the evaluation of the final expression

Mario Carneiro (Nov 01 2019 at 08:05):

@Jason Gross I don't think I can help you with this one. I've tried several methods, but I think lean's kernel evaluation just isn't up to the task - it's abysmally slow at just about every approach to this that doesn't involve a complete rewrite. (It doesn't help that the algorithm itself is not super efficient; I can see that this would probably evaluate well enough in Coq but the Coq kernel evaluator is more efficient than lean's.) The fact that it's not completely a refl proof but has embedded subproofs makes this evaluation significantly more elaborate than it otherwise would be, and the constant allocation of new variables and lifting the term is also a performance bottleneck. This is possibly a good test case for lean 4.

Jason Gross (Nov 01 2019 at 08:31):

I appreciate all the help you've provided; thank you. And feel free to take it as a test case for lean 4. I will note that Coq only performs adequately if I rewrite the entire thing in CPS (so no term lifting needs to be done to reduce), make all the things that don't get unfolded axioms, and then run it in the vm. Coq's rewriting tactics perform more slowly than Lean's (by a factor of 6, in the one case I could test). Getting reasonable performance on this benchmark has been my PhD project for the last year or two, sort-of, and required writing a reflective rewriter mixed with NbE to be run in vm compute, and I'm currently in the process of writing this up in a paper for PLDI

Mario Carneiro (Nov 01 2019 at 09:05):

I updated the gist with my latest version: https://gist.github.com/digama0/7649577d7c8af881cdbad50f9e84d81f

I put it in a monad after all:

inductive let_bound (α : Type*)
| base : α  let_bound
| dlet :   (  let_bound)  let_bound
| mlet {β : Type} : β  (β  let_bound)  let_bound

The meaning is that dlet is the constructor for let_in, and mlet is a subgoal that should be solved by norm_num. In theory, you should be able to take the (carry_mulmod ...).eval term, and then successively normalize it (by whnf) to (mlet x f).eval which rewrites to (f y).eval after figuring out x ~> y by other tactics, and (dlet x f).eval which rewrites to let_in x (\lam a, (f a).eval)and work continues inside the lambda.

Mario Carneiro (Nov 01 2019 at 09:07):

In practice, it seems to take about 1 sec per let binding, plus my tactics for doing the reduction are super janky

Jason Gross (Nov 02 2019 at 04:02):

@Mario Carneiro Neat, thanks! Is that 1 second per internal let-binder (including mlet), or 1 second per emitted dlet? I will note that real examples range from a couple dozen dlets at the small end to about 3800 dlets (possibly more for some of the code we don't build routinely) at the large end.

Mario Carneiro (Nov 02 2019 at 04:05):

I think what I was seeing was mlets. In ex2 there are about a dozen of them (solving stupidly large goals like to_bool (1 % <big number> = 0)) before you get to the first dlet

Jason Gross (Nov 02 2019 at 04:06):

Ah, yes, there are a lot of those

Jason Gross (Nov 02 2019 at 04:16):

solving stupidly large goals like to_bool (1 % <big number> = 0)

Shouldn't solving this be fast/easy, because 1 % <big number> is always 1, and the division computation should be relatively fast because the numerator is small?

Jason Gross (Nov 02 2019 at 04:22):

@Mario Carneiro I tried running your code, but it seems to fail with ~no progress after 77 seconds?

$ /usr/bin/time -f "$@ (real: %e, user: %U, sys: %S, mem: %M ko)" lean fiat-test.lean
to_bool (weight machine_wordsize 1 0 * weight machine_wordsize 1 0 % s = 0)
2 goals
f g : ℕ → ℤ,
a : ℤ
⊢ let_bound.eval
      (let_bound.bind (positional.carry_reduce (… 1) n s c 1)
         ((λ (x : ℤ), let_bound.bind (… s c 0) ((λ (x : ℤ), let_bound.map … (… …)) x)) a)) =
    ?m_1 a

f g : ℕ → ℤ
⊢ ℤ → list ℤ
to_bool (weight machine_wordsize 1 0 = weight machine_wordsize 1 0)
f g : ℕ → ℤ,
a a_1 : ℤ
⊢ let_bound.eval
      (let_bound.bind (positional.carry_reduce (… 1) n s c 1)
         ((λ (x : ℤ),
             let_bound.bind (λ (a : list ℤ), let x : list … := … a in … …)
               ((λ (x : ℤ), let_bound.bind … (… x)) x))
            a_1)) =
    ?m_1 a_1
f g : ℕ → ℤ,
a a_1 a_2 : ℤ
⊢ let_bound.eval
      (let_bound.bind (positional.carry_reduce (… 1) n s c 1)
         ((λ (x : ℤ),
             let_bound.bind (λ (a : list ℤ), let x : list … := … a in … …)
               ((λ (x : ℤ), let_bound.bind … (… x)) x))
            a_2)) =
    ?m_1 a_2
/home/jgross/Documents/repos/fiat-lean-test/fiat-test.lean:345:2: error: convert tactic failed, there are unsolved goals
state:
f g : ℕ → ℤ,
a a_1 a_2 : ℤ
⊢ let_bound.eval
      (let_bound.bind (positional.carry_reduce (… 1) n s c 1)
         ((λ (x : ℤ),
             let_bound.bind (λ (a : list ℤ), let x : list … := … a in … …)
               ((λ (x : ℤ), let_bound.bind … (… x)) x))
            a_2)) =
    ?m_1 a_2
Command exited with non-zero status 1
 (real: 76.88, user: 76.73, sys: 0.24, mem: 396720 ko)

Mario Carneiro (Nov 02 2019 at 04:22):

it probably doesn't work

Jason Gross (Nov 02 2019 at 04:22):

(Maybe I am misreading the output, though...)

Mario Carneiro (Nov 02 2019 at 04:23):

if you can find some use for the code, great, but it comes with no warranty

Mario Carneiro (Nov 02 2019 at 04:24):

the tactic itself is extremely experimental

Jason Gross (Nov 02 2019 at 04:24):

Okay. I think I'm not yet sufficiently experienced with Lean to extract value from the tactic code, and I've satisfied my curiosity about how good Lean's features are out-of-the-box at solving this sort of goal.

Mario Carneiro (Nov 02 2019 at 04:24):

it got a bit hard to test because everything takes forever

Mario Carneiro (Nov 02 2019 at 04:25):

This is definitely not something anyone has attempted to do before in lean

Jason Gross (Nov 02 2019 at 04:25):

it got a bit hard to test because everything takes forever

That's the story of my PhD. I've probably spent 80% of my time trying to figure out how to work around performance issues in Coq

Mario Carneiro (Nov 02 2019 at 04:26):

My instincts say "fix the algorithm"

Jason Gross (Nov 02 2019 at 04:26):

What do you mean "fix the algorithm"?

Mario Carneiro (Nov 02 2019 at 04:26):

There is a ton of list manipulation code in here that seems to be unnecessary

Mario Carneiro (Nov 02 2019 at 04:27):

I mean, if it's a benchmark then that's fine, but I think you can obtain equivalent results with another approach that doesn't involve all these let bindings

Mario Carneiro (Nov 02 2019 at 04:28):

and what's with all this back and forth between positional and associational representations?

Jason Gross (Nov 02 2019 at 04:29):

The ultimate goal is to produce code like this: https://github.com/mit-plv/fiat-crypto/blob/5a51b7a2f9fc8aad46963f0d9bbe64047de0704f/curve25519_64.c#L96-L154

Mario Carneiro (Nov 02 2019 at 04:31):

I would write a tactic that produces that, and a simultaneously proof of the semantics of it

Mario Carneiro (Nov 02 2019 at 04:32):

Z would never show up in the algorithm

Mario Carneiro (Nov 02 2019 at 04:32):

(except in the tactic code itself)

Jason Gross (Nov 02 2019 at 04:34):

The back-and-forth between positional and associational is because some algorithms are easy to express and prove in the associational representation (addition of numbers is just list concatenation, multiplication is just all the ways of combining pairs of elements of the lists with *), while positional is the format that is actually used to store the numbers in memory / transmit them on the wire. But you're right, there's a lot of unnecessary back-and-forth in chained_carries, which could probably benefit from being moved to associational.

Mario Carneiro (Nov 02 2019 at 04:36):

The point is to separate the parts of the algorithm that actually need to be executed, and the parts that are actually the output (creating the list of let bindings)

Mario Carneiro (Nov 02 2019 at 04:36):

the part that gets executed can happen in the VM (fast and untrusted), and the part that is output is term creation with a proof of correctness

Mario Carneiro (Nov 02 2019 at 04:37):

Right now everything is happening as term manipulation, so it's all slow

Jason Gross (Nov 02 2019 at 04:40):

Maybe I don't understand what you mean by a tactic that produces both the term and the proof. e.g., what would this look like for associational.carryterm, whose spec is associational.eval (associational.carryterm w fw t) = associational.eval [t] (Coq proof here)

Jason Gross (Nov 02 2019 at 04:41):

(The proof is pretty simple: it's basically just the spec of div and mod, plus ring)

Jason Gross (Nov 02 2019 at 04:43):

Do you mean something like, a tactic that builds a sigma type { ct : list (Z * Z) | associational.eval ct = associational.eval [t] }?

Jason Gross (Nov 02 2019 at 04:43):

or, er, maybe it is all inside the let monad

Mario Carneiro (Nov 02 2019 at 04:44):

You have a tactic called carryterm (...) : tactic (term x expr), which is given some inputs and produces a pair of a term and a proof of associational.eval t' = associational.eval [t] (where t' is the reification of the returned term)

Mario Carneiro (Nov 02 2019 at 04:46):

yes, you could also view it as a sigma type, although you want ct here to be a concrete list, not an arbitrary term of type list (Z * Z)

Jason Gross (Nov 02 2019 at 04:46):

Yes, a concrete list, but it's under let binders

Mario Carneiro (Nov 02 2019 at 04:47):

which is why it is returned as a term (which is some inductive type you define that specifies the valid constructions, in this case a list under let binders)

Mario Carneiro (Nov 02 2019 at 04:47):

I suspect that you actually should be keeping the let binders on the side though; that is, you have some growing context of let binders and your monad allows you to add to it

Jason Gross (Nov 02 2019 at 04:48):

Hm, but the proofs are invalid if you don't know the values of the binders. I get the evaluation for the proofs looks things up from the binders on the side?

Mario Carneiro (Nov 02 2019 at 04:49):

Yes, you would be passing it as another argument to eval in that case

Jason Gross (Nov 02 2019 at 04:49):

Okay, so I see how I could produce the term, but I don't see how producing the proof on the fly every time I call carryterm is going to be any faster. It seems like it can only possibly be slower, because now not only are you computing equality of list manipulation, you're also computing equality of associational.eval

Jason Gross (Nov 02 2019 at 04:51):

Oh, I see, at least for carryterm, you don't need to worry about what the bool computes you, you just produce a proof of one side or the other

Jason Gross (Nov 02 2019 at 04:52):

(I still think the proof is going to be expensive here, because you're calling ring)

Mario Carneiro (Nov 02 2019 at 04:52):

Both parts are produced by application of theorems like those I mentioned earlier. You aren't "computing" anything in the DTT sense; you are applying theorems that build up eval equalities about these reified terms

Mario Carneiro (Nov 02 2019 at 04:52):

You actually don't need to call ring at all this way

Mario Carneiro (Nov 02 2019 at 04:52):

That's the main benefit

Mario Carneiro (Nov 02 2019 at 04:53):

If you have something like if a % s = 0 then ... else ... in the algorithm, that's fine, it runs in the VM and uses a bignum library

Mario Carneiro (Nov 02 2019 at 04:55):

The only reason you need to run norm_num is if you needed that fact a % s = 0 in order to produce the correctness proof, but with this approach it's easy to run it and provide the input where it is needed

Jason Gross (Nov 02 2019 at 04:57):

Hm, this is interesting, I'll have to think more about this approach

Jason Gross (Nov 02 2019 at 04:58):

(And, indeed, in associational.split, we need a proof of a % s = 0 for the then branch, but don't need to know anything in the else branch)

Mario Carneiro (Nov 02 2019 at 04:59):

Additionally, you can easily shortcut the computation by applying a theorem with the alternate hypothesis a = 0, or a = s, or a = k * s where you precompute what k should be

Jason Gross (Nov 02 2019 at 05:04):

Presumably I still need norm_num to prove a = k * s, though, right? Because of https://webcache.googleusercontent.com/search?q=cache:tVeSAlPEQN4J:https://github.com/leanprover/lean/issues/1799+&cd=2&hl=en&ct=clnk&gl=us

Jason Gross (Nov 02 2019 at 05:05):

(Did someone delete all the issues on leanprover/lean? There's no issue page anymore, and all the pre-existing issues give 404 now...)

Mario Carneiro (Nov 02 2019 at 05:17):

What the... It appears Leo simplified the readme and deleted the entire issues page for leanprover/lean. I didn't even know that was possible, but I hope we can get the history migrated to the community version

Mario Carneiro (Nov 02 2019 at 05:21):

Yes, you would want to use norm_num to prove the a = k * s side goal.

Mario Carneiro (Nov 02 2019 at 05:23):

Indeed, the approach I am describing is basically what norm_num does itself, for arithmetic goals

Jason Gross (Nov 02 2019 at 05:30):

Is there any chance I can get Lean to manage the context for me, or do I have to do it myself and reason about, e.g., uniqueness of names and context-lookup, etc?

Jason Gross (Nov 02 2019 at 05:32):

There's a checkbox under "options" of a github repository to enable/disable issues. I could believe that when you disable issues, github just makes them inaccessible to non-admins or something. (I could also believe it deletes them)

Jason Gross (Nov 02 2019 at 05:34):

Yeah, when you uncheck the issues box, github makes all issues be 404s. When you recheck it, they all come back

Bryan Gin-ge Chen (Nov 02 2019 at 05:34):

Indeed, according to this page re-enabling the issues would make the old ones accessible again. I could only find this page on migrating single open issues, and it didn't look too promising. Maybe github support could work some magic though.

Jason Gross (Nov 02 2019 at 05:35):

It's definitely possible to mass-migrate issues, though you might need to write a tool to do it using the github api (and get Leo to re-enable issues temporarily)

Jason Gross (Nov 02 2019 at 05:35):

Coq migrated all of it's issues from bugzilla to github, mostly preserving issue numbers (but they all show as authored by coqbot, unfortunately)

Jason Gross (Nov 02 2019 at 05:37):

https://github.com/IQAndreas/github-issues-import

Jason Gross (Nov 02 2019 at 05:39):

Also, http://www.alexhadik.com/blog/2016/5/26/migrating-github-repositories-with-gitmover

Andrew Ashworth (Nov 02 2019 at 16:03):

you're going to want my fork: https://github.com/alashworth/github-issues-import

Andrew Ashworth (Nov 02 2019 at 16:03):

also you will need to speak to github admin to whitelist your account, otherwise you'll get autobanned for spamming

Andrew Ashworth (Nov 02 2019 at 16:03):

(went through this in march)

Gihan Marasingha (Nov 10 2019 at 19:11):

(deleted)

Gihan Marasingha (Nov 10 2019 at 19:12):

(deleted)

Reuben Rowe (Nov 16 2019 at 17:35):

If I have an instance denumerable α and two values x y : α, is there a way to be able to write if x = y then ... rather than if (encodable.encode x) = (encodable.encode y) then .... That is, how to I get Lean to infer decidable (x = y)?

Mario Carneiro (Nov 16 2019 at 17:38):

you want to assume decidable_eq A too

Reuben Rowe (Nov 16 2019 at 17:40):

Great, thanks - but it seemed to me that this can be derived from denumerability. Why should I have to assume it separately?

Mario Carneiro (Nov 16 2019 at 17:41):

because the implementation might be different

Reuben Rowe (Nov 16 2019 at 17:41):

Derived from denumerability, given that equality on \Nat is decidable

Mario Carneiro (Nov 16 2019 at 17:41):

it's not usually the best way to decide equality

Reuben Rowe (Nov 16 2019 at 17:47):

OK, sure. Thanks!

Bryan Gin-ge Chen (Dec 30 2019 at 18:40):

How does rcases / rintro with rfl in the pattern work? I couldn't find it documented anywhere.

Johan Commelin (Dec 30 2019 at 18:49):

I you have some expression of the form x = a, then the rfl pattern works as if you just wrote h, and followed the rcases/rintro with a subst h.

Greg Langmead (Jan 08 2020 at 23:15):

Any tips on defining the unit sphere in Euclidean space? I'm using @Sebastien Gouezel 's definition def euclidean_space (n : ℕ) : Type := (fin n → ℝ) and can't figure out how to form the summation of the squares def unit_sphere (n : ℕ) := { x : euclidean_space n // ...}

Kevin Buzzard (Jan 08 2020 at 23:18):

import data.real.basic

def euclidean_space (n : ) : Type := (fin n  )

def unit_sphere (n : ) := { x : euclidean_space n // finset.sum finset.univ (λ i, (x i) ^ 2) = 1}

Kevin Buzzard (Jan 08 2020 at 23:19):

Maybe def unit_sphere (n : ℕ) := { x : euclidean_space n // finset.univ.sum (λ i, (x i) ^ 2) = 1} is a bit nicer.

Kevin Buzzard (Jan 08 2020 at 23:22):

#check @finset.sum
/-
finset.sum : Π {α : Type u_1} {β : Type u_2} [_inst_1 : add_comm_monoid β], finset α → (α → β) → β
-/

finset.sum eats a finite subset of alpha, and a function from alpha to a commutative monoid beta, and spits out the sum of this function over the given finite subset of alpha.

Kevin Buzzard (Jan 08 2020 at 23:26):

finset.univ is all of the set alpha (which Lean figures out must be fin n by "unification"), and Lean also figures out that alpha is a finite type (by "type class inference")

Nicholas Talin (Jan 08 2020 at 23:59):

What would be the best introductory book on analysis if I wanted to use Lean to formalize its statements and solve exercises along the way?

Greg Langmead (Jan 09 2020 at 01:11):

Thank you Kevin, that works great. I will need to stare longer at finset.univ. Now I'll work towards proving these are smooth manifolds!

Yury G. Kudryashov (Jan 09 2020 at 01:17):

@Greg Langmead Would it be convenient to have the implicit function theorem first?

Yury G. Kudryashov (Jan 09 2020 at 01:19):

Then we would be able to prove that a smooth function defines a submanifold. Or you prefer to have some explicit set of charts (e.g., two stereographic projections)?

Greg Langmead (Jan 09 2020 at 01:38):

@Yury G. Kudryashov I'm planning on doing it both ways, i.e. with explicit charts, and then proving various more general things. I'm thinking along the lines of an introductory differential geometry text that is also an introduction to formalization.

Andrew Ashworth (Jan 09 2020 at 05:29):

@Nicholas Talin Several people have used Tao's Analysis 1 + 2; it's quite formal for an introductory text. You are on your own converting the lemmas and definitions to work with mathlib though, since it uses set theory as its foundations

Kevin Buzzard (Jan 09 2020 at 06:26):

I dunno about "on your own" -- I for one would be very happy to help. And set theory/type theory hopefully shouldn't make too much of a difference here. I've proved basic analysis results about series/sequences etc in Lean, I think @Kenny Lau proved Bolzano-Weierstrass somewhere, etc.

Yury G. Kudryashov (Jan 09 2020 at 23:22):

@Greg Langmead I have a branch with inverse function theorem (mostly done). I'm going to make a PR in a few days.

Spencer Peters (Jan 14 2020 at 21:18):

Hi everyone! I just got introduced to Lean through the natural number game, and I started playing around with writing up the first homework from a discrete math class I TA'd recently. In that homework, students were supposed to define a sudoku puzzle as a function f: {1, 2, ..., 9} x {1, 2, ..., 9} -> {1, 2, ..., 9}. So I defined the subtype

def nine : Type := {v :  // v < 9}

Now I have two questions. First, although I can coerce a subtype of nat to nat, I can't seem to coerce a "nine" to a nat. What I mean is that this works:

constant x : {v :  // v < 9}
#check (x:)

But this produces an error:

def nine : Type := {x :  // x < 9}
constant y : nine
#check (y:)

Is there a line I can add which will cause all the typeclass instances of {x : ℕ // x < 9} to carry over to the new type nine?
My second question has to do with sets in Lean. In our discrete math class obviously we work with sets, not inductive types. So I would like the students to be able to work with something like (edited, set nine changed to set nat)

def nine_set : set nat := {1, 2, 3, 4, 5, 6, 7, 8, 9}

But if I then try to define a function from nine_set to nine_set, I run into the issue that nine_set is not a Type! I'm guessing that there isn't any way around this, but I thought I'd ask.

Bryan Gin-ge Chen (Jan 14 2020 at 21:21):

Welcome! You might find this doc on "set-like objects" in mathlib useful. I'll take a look at the more detailed questions later if no one else has gotten to them.

Kevin Buzzard (Jan 14 2020 at 22:15):

We already have a type fin 9. You might be better off sticking with that.

You can add the coercion manually:

def nine : Type := {v :  // v < 9}

instance : has_coe nine  :=
⟨λ n, n.1

constant y : nine
#check (y : ) -- works

The switch from finite sets to types will be painful. I constantly tell the student that a term of type fin 9 is a pair consisting of a number and a proof, and eventually this will dawn on them.

Spencer Peters (Jan 14 2020 at 23:17):

Thanks Bryan! The mathlib doc was useful. And thank you Kevin! It's interesting that fin 9 also has no coercion to nat by default, e.g. the following doesn't work:

constant z : fin 9
#check (↑z : ℕ)

That the switch will be painful doesn't surprise me--I also had some difficulty wrapping my head around the definition of a subtype. For context, I want to improve an introductory class for computer science majors who don't, by and large, have any background in formal math. My goal is to use Lean to help students understand formal math via their familiarity with programming. In my experience, students understand what it means for code to compile/not compile, but they don't understand as clearly what it means for a proof to be valid/invalid. In principle, Lean seems like a great way to bridge the gap, although in practice I'm not sure owing to the overhead of things like sets vs. inductive types. I'm also interested in using Lean as (or as the basis for) an interactive development environment for proofs, as an alternative to pencil and paper or TeX.

Kevin Buzzard (Jan 14 2020 at 23:20):

Have you seen Avigad's Logic and proof? Perhaps his students are similar to yours? I usually deal with mathematicians so have different problems -- they might well have some idea what a proof is, but have never seen a functional language or type theory before.

Spencer Peters (Jan 15 2020 at 08:46):

Yes, thanks for the pointer! This curriculum is similar to what I'd like to teach, although I can't afford to spend quite as much time on natural deduction and logic. In the course I'm working from (https://courses.cs.cornell.edu/cs2800/wiki/index.php/CS_2800_Fall_2019), we have to quickly cover a lot of topics relevant to computer science, starting from sets, functions and relations. I'm hoping that I can set up something that will let students start working right away without detailed knowledge of the logical foundations (like the natural number game).

Kevin Buzzard (Jan 15 2020 at 12:38):

I did functions and relations in my course last term.Here are the example sheets:

https://github.com/ImperialCollegeLondon/M40001_lean

Kevin Buzzard (Jan 15 2020 at 13:34):

@Jason KY. have you formalised some of the lecture notes in some form? Maybe Spencer would be interested.

Jason KY. (Jan 15 2020 at 13:47):

Jason KY. have you formalised some of the lecture notes in some form? Maybe Spencer would be interested.

Emm, well I've formalised the first part of the intro module and am currently working on analysis but I've not written any comments for those parts.
They are all here if anyone is interested :)
https://github.com/JasonKYi/M4000x_LEAN_formalisation

Kevin Buzzard (Jan 15 2020 at 13:50):

Spencer -- feel free to build on this stuff if it's of any use to you. Jason is one of my undergraduates; he took my class.

Spencer Peters (Jan 15 2020 at 23:44):

Thank you! I'll dig into these. Much appreciated :)

Anton Lorenzen (Jan 16 2020 at 16:40):

Do you know how I can tell Lean that for c, d, e in Prop and c decidable, "c -> d" and "c -> ite c d e" are the same type? Or lift a value of the first type to a value of the second? (This is my first post here, so I hope this is the right place to ask that)

Joe (Jan 16 2020 at 17:04):

Both of them have the type Prop. I guess you want to prove the following?

lemma foo (c d e : Prop) [decidable c] : (c  d)  (c  if c then d else e) :=

Anton Lorenzen (Jan 16 2020 at 17:08):

@Joe Yes

Kevin Buzzard (Jan 16 2020 at 17:11):

It's called something like ifpos [edit: if_pos]

Joe (Jan 16 2020 at 17:17):

Can you use tactics?

lemma foo (c d e : Prop) [decidable c] : (c  d)  (c  if c then d else e) :=
iff.intro
  (λ h hc, by { rw if_pos hc, exact h hc })
  (λ h hc, by { have := h hc, rw if_pos hc at this, assumption })

Anton Lorenzen (Jan 16 2020 at 17:18):

Got this with if_pos. Looks good!

lemma foo (c d e : Prop) [decidable c] : (c → d) ↔ (c → if c then d else e) :=
  iff.intro (λ f c, eq.mp (eq.symm (if_pos c)) (f c)) (λ f c, eq.mp (if_pos c) (f c))

Greg Langmead (Jan 25 2020 at 19:48):

I'm having trouble working with continuous functions on subsets of euclidean space. I think I lack some idiom. The first proof below is working for all of ℝ but the second gives an error because it needs some sort of coercion maybe?

import geometry.manifold.real_instances

lemma certain_func_is_continuous : continuous (λ x:ℝ, 4 * (x:ℝ)) :=
begin
  apply continuous.mul _ _,
  apply_instance,
  exact continuous_const,
  exact continuous_id,
end

def real_gt_one := {x: ℝ | x > 1}

lemma certain_func_is_continuous_gtone : continuous (λ x:real_gt_one, 4 * (x:ℝ)) :=
begin
  apply continuous.mul _ _,
  apply_instance,
  exact continuous_const,
  exact continuous_id,
end

the error is

invalid type ascription, term has type
  continuous id
but is expected to have type
  continuous (λ (x : ↥real_gt_one), ↑x)

My short term goal is to define functions that are only continuous on the subset like 1/(1-x).

Alex J. Best (Jan 25 2020 at 19:58):

Your function is from real_gt_one to , is that what you want? In this case your function at the end is not id the identity function but rather coe the coercion from real_gt_one to .

Yury G. Kudryashov (Jan 25 2020 at 19:58):

I guess you need continuous_subtype_val

Alex J. Best (Jan 25 2020 at 19:58):

If you run dsimp before your last line lean simplifies your expression to show that the goal is continuous coe really.

Yury G. Kudryashov (Jan 25 2020 at 20:01):

BTW, I wonder if the following will work in most cases to automatically prove continuity: (1) mark many lemmas with @[continuity], and (2) make acontinuity tactic to be a shorthand for apply_rules [continuity].

Yury G. Kudryashov (Jan 25 2020 at 20:02):

I have no time do give it a try in the next week or two.

Kevin Buzzard (Jan 25 2020 at 20:09):

There was some talk about the continuity tactic in Pittsburgh

Greg Langmead (Jan 25 2020 at 20:39):

Thanks, dsimp and continuous_subtype_val helped. I am defining various real-valued functions so definitely want values in ℝ. Eventually I'll bring this back to stereographic projection which is my first mini-project.

With regard to making continuity easier to prove, it seems to me (disclaimer: a noob) that it's a heavy-handed paradigm to edit all the other files to mark various lemmas that might be useful. It feels like the wrong separation of concerns. I'm not sure what the alternative is, but is it possible to build up my own personal collections of often-used lemmas (and wildcard lemmas like "continuous*"), and have some tactic search among those?

Greg Langmead (Jan 25 2020 at 20:56):

To partially answer my own question, I see I can make a local attribute and add things to it per the apply_rules docs https://github.com/leanprover-community/mathlib/blob/master/docs/tactics.md.

Greg Langmead (Jan 25 2020 at 21:10):

To keep running with this, if I define my_continuity_lemmas : user_attribute then I can do apply_rules [my_continuity_lemmas] but this won't prove my new lemmas by itself, I also need other tactics like apply_instance. Can I package up apply_rules [my_continuity_lemmas] as a tactic apply_my_continuity_lemmas so I could then do chain [apply_instance, apply_my_continuity_lemmas]? I have a sinking feeling I'm starting to talk nonsense.

Yury G. Kudryashov (Jan 25 2020 at 22:58):

Yes, you can create your own attribute. I was talking about a way to handle this everywhere in mathlib.

Yury G. Kudryashov (Jan 25 2020 at 22:59):

@Kevin Buzzard Could you please tell me some details?

Kevin Buzzard (Jan 25 2020 at 23:03):

I don't understand things well enough to be able to say anything coherent.

Yury G. Kudryashov (Jan 25 2020 at 23:10):

Then you can tag one of those who discussed this.

Kevin Buzzard (Jan 25 2020 at 23:10):

@Reid Barton made some comments at some point...

Joe (Jan 25 2020 at 23:12):

I think that would work, except that tactics such as apply refine exact won't stop on the simplest cases. @Yury G. Kudryashov

Joe (Jan 25 2020 at 23:12):

Here is an example I discovered a long time ago, where using apply' continuous_tan' on continuous (λx, sin x) results in a timeout. The root of this problem is exact, so I guess there is nothing you can do. Though I think there must be a way to make things irreducible or something, so that unifying mismatched expressions can end quickly.

import analysis.complex.exponential
import tactic.basic
import tactic.tidy
import tactic.apply
import tactic.apply_fun

namespace real
variables {α : Type*} [topological_space α] {f : α  } (hf : continuous f)
include hf

lemma continuous_tan' (h : a, cos (f a)  0) : continuous (λa, tan (f a)) :=
show continuous ((tan  @subtype.val  (λx, (cos x)  0))  λa, f a, h a),
  from continuous.comp continuous_tan (continuous_subtype_mk _ hf)

end real

open real

lemma foo : continuous (λx, sin x) :=
begin
  have := @continuous_tan'  _ _ _ _,
  exact this,
  -- apply' this,
  -- refine this,
  -- apply this,
end

Reid Barton (Jan 25 2020 at 23:45):

Yes, probably a case of excessive (and fruitless) definitional unfolding.

Chris Hughes (Jan 25 2020 at 23:51):

You can just set tactic.apply to only unfold reducibles right?

Joe (Jan 26 2020 at 00:07):

I don't know if that would work because apply is bugged. Even applying continuous_sin' to continuous (λx, sin x) fails.

import analysis.complex.exponential
import tactic.apply

namespace real
variables {α : Type*} [topological_space α] {f : α  } (hf : continuous f)
include hf

lemma continuous_sin' : continuous (λa, sin (f a)) := sorry

end real

open real

lemma foo : continuous (λx, sin x) :=
begin
  apply continuous_sin',  -- fails
end

Chris Hughes (Jan 26 2020 at 00:09):

There's apply' which fixes the bug.

Joe (Jan 26 2020 at 00:09):

On the other hand, if you look at the source code of apply', it tries exact first:

private meta def retry_apply_aux : Π (e : expr) (cfg : apply_cfg), list (bool × name ×  expr)  tactic (list (name × expr))
| e cfg gs :=
focus1 (do {
     tgt : expr  target, t  infer_type e,
     unify t tgt,                             -- apply' is already stuck here
     exact e,
     gs'  get_goals,
     let r := reorder_goals gs cfg.new_goals,
     set_goals (gs' ++ r.map prod.snd),
     return r }) <|>
do (expr.pi n bi d b)  infer_type e >>= whnf | apply_core e cfg,  -- so it will never get here
   v  mk_meta_var d,
   let b := b.has_var,
   e  head_beta $ e v,
   retry_apply_aux e cfg ((b, n, v) :: gs)

Greg Langmead (Jan 26 2020 at 18:55):

@Yury G. Kudryashov I was suggesting that instead of sprinkling @[continuity] hints around mathlib, there could be one file with packages of continuity lemmas being exported under various names. That seems more general than deciding in a one-size-fits-all fashion at the point where the lemma is defined.

Greg Langmead (Jan 26 2020 at 18:58):

Now I'm trying to define continuity of a function of two variables and I can't get continuous_snd to work with my subset of ℝ×ℝ:

lemma certain_twovar_func_continuous : continuous (λx:real_gt_one × real_gt_one, (x.1:ℝ) * (1 - (x.2:ℝ)⁻¹)) :=
begin
  apply continuous.mul _ _,
  apply_instance,
  sorry, -- writing "apply continuous_snd" here gives: invalid apply tactic, failed to unify continuous (λ (x : ↥real_gt_one × ↥real_gt_one), ↑(x.fst)) with continuous prod.snd
  apply continuous.sub,
  apply continuous_const,
  apply real.continuous.inv _ sorry,
  sorry,
end

Greg Langmead (Jan 26 2020 at 18:59):

I have an instance that indicates real_gt_one is a topological_space but somehow continous_snd is not type matching. Is it because I am using a lambda?

Joe (Jan 26 2020 at 19:03):

Looking at the error message. it seems that you should apply continuous_fst?

Joe (Jan 26 2020 at 19:07):

Also there is a coercion sign, so perhaps you can try refine continuous.comp _ _ and see what comes out.

Greg Langmead (Jan 26 2020 at 19:18):

Sorry yes continuous_fst is what I should be using, and gives me the error, and I prepared my post with _snd by mistake.

invalid apply tactic, failed to unify continuous (λ (x : ↥real_gt_one × ↥real_gt_one), ↑(x.fst)) with continuous prod.snd

I didn't succeed yet with continuous.comp, will keep trying.

Alex J. Best (Jan 26 2020 at 19:24):

lemma certain_twovar_func_continuous : continuous (λx:real_gt_one × real_gt_one, (x.1:) * (1 - (x.2:)⁻¹)) :=
begin
  apply continuous.mul _ _,
  apply_instance,
  refine continuous.comp _ _,
  apply continuous_subtype_val,
  apply continuous_fst,
  apply continuous.sub,
  apply continuous_const,
  refine real.continuous.inv _ _,
  intros,
  have := a.2.2,
  intro,
  sorry,
  refine continuous.comp _ _,
  apply continuous_subtype_val,
  apply continuous_snd,
end

now I got stuck proving that a.snd.val \ne 0

Joe (Jan 26 2020 at 19:28):

@Greg Langmead I guess you can take a look at how nnreal is defined?

Joe (Jan 26 2020 at 19:29):

#check nnreal
#check nnreal.continuous_coe

Greg Langmead (Jan 27 2020 at 14:15):

@Alex J. Best I'm stuck there too. I'm having trouble getting from being an element of real_gt_one to being > 1. All real_gt_one is is a function to Prop and I can't discover the paradigm for accessing the values for which that Prop is true.

Kevin Buzzard (Jan 27 2020 at 14:27):

real_gt_one gets promoted to a type here, because you have terms of that type. So it's not a function to Prop when you use it (there is probably a little up-arrow next to it indicating the promotion). If x has type real_gt_one then x.2 will be a proof that x.1>1.

Alex J. Best (Jan 27 2020 at 14:29):

Yeah I ended up getting stuck with x.1 vs x.val vs \u x or some other silliness, that exact mod cast didn't want to blast through for me.

Johan Commelin (Jan 27 2020 at 14:34):

Those should all be definitionally equal...

Johan Commelin (Jan 27 2020 at 14:34):

Can you paste code?

Kevin Buzzard (Jan 27 2020 at 14:41):

  have := a.2.2,
  { intro,
    change (a.snd : ) > 1 at this,
    linarith },

Jonathan Sejr (Jan 27 2020 at 18:54):

Hi, I am working on the natural numbers game (great stuff btw), and am currently stuck at advanced addition world l. 13. I am having trouble manipulating the expressions when my goal is false. My code so far is
python intro h, rw succ_eq_add_one at h, rw ← zero_add(n) at h, rw add_comm at h, rw ← add_comm(0) at h, rw add_assoc at h, rw zero_add(n+1) at h, rw ← add_comm(1) at h, rw one_eq_succ_zero at h,
Leaving me with n : mynat, h : 0 + n = succ 0 + n ⊢ false I want to use add_right_cancel to get 0=succ 0 and then finish off the goal. But I cannot apply add_right_cancelbecause the goal is just a false proposition.
I am really lost on how to manipulate a false goal, so any tips are appreciated.

Kevin Buzzard (Jan 27 2020 at 18:57):

add_right_cancel is a theorem of the form "if m+p=n+p then m=n"

Kevin Buzzard (Jan 27 2020 at 18:58):

so it's actually a function which eats a proof of m+p=n+p and spits out a proof of m=n

Kevin Buzzard (Jan 27 2020 at 18:58):

so instead of manipulating the goal, you can make a new hypothesis

Kevin Buzzard (Jan 27 2020 at 19:00):

have h2 := add_right_cancel _ _ _ h, would give you h2 : 0 = 1

Kevin Buzzard (Jan 27 2020 at 19:00):

oh great and then you can use zero_ne_succ

Jonathan Sejr (Jan 27 2020 at 19:07):

I was actually trying have, but didn't get that I should tag an h at the end thanks! And now I am unsure how to finish it, h2 : 0 = 1should now be a false statement so I should be able to finish with exact h2, but that doesn't work.
Edit: One finishes off with
have h3 := zero_ne_succ _ h2, exact h3,
because h3 will be false .

Kevin Buzzard (Jan 27 2020 at 19:57):

Remember that lots of things are functions

Kevin Buzzard (Jan 27 2020 at 19:58):

A not= B is the same as (A=B) -> false

Kevin Buzzard (Jan 27 2020 at 19:59):

You can probably just do exact zero_ne_succ _ h2

Kevin Buzzard (Jan 27 2020 at 19:59):

And you can probably go a step further back

Alex Kontorovich (Jan 28 2020 at 14:52):

Hi all, I'm a mathematician, completely new to Lean. Finally followed Kevin's advice and joined here (so I don't have to bug him directly for help). To get started, I want to formalize that the square of an odd number is odd. Here's what I came up with for a statement:

lemma square_of_odd_is_odd : ∀ n : ℕ, (∃ k : ℕ, n=2k+1) -> (∃ l : ℕ, nn = 2*l+1) :=
begin
intro n,
intro p,

At this point I'm stuck. How do I tell it to use that k and stick it in to n?

Kenny Lau (Jan 28 2020 at 14:52):

```lean
[some lean code]
```

Kenny Lau (Jan 28 2020 at 14:53):

you can do cases p

Alex Kontorovich (Jan 28 2020 at 14:56):

Ok great, thanks! Then

use 2*p_w^2+2*p_w,

Then how do I apply p_h?

Johan Commelin (Jan 28 2020 at 15:00):

I would suggest cases p with k p

Johan Commelin (Jan 28 2020 at 15:00):

To get nicer names

Johan Commelin (Jan 28 2020 at 15:01):

After that, you are looking for rw p_h (or rw p, if you change the cases)

Johan Commelin (Jan 28 2020 at 15:02):

Alternative: subst n. This will look for n = ... in you context, and replace all occurences of n with ...

Alex Kontorovich (Jan 28 2020 at 15:02):

Ah yes, rw! Thanks. Now shouldn't simp do the algebra for me and finish?

Let's see if this works:

lemma square_of_odd_is_odd :  n : , ( k : , n=2*k+1) -> ( l : , n*n = 2*l+1)
:=
begin
    intro n,
    intro p,
    cases p with k p,
    use 2*k^2+2*k,
    rw p,
    simp,
end

Johan Commelin (Jan 28 2020 at 15:03):

I don't think simp will do that for you. But ring should.

Alex Kontorovich (Jan 28 2020 at 15:03):

Yes! Works! Thanks that's great!

Johan Commelin (Jan 28 2020 at 15:03):

You might need import tactic at the top of your file.

Sam Stites (Jan 28 2020 at 16:31):

Super dumb question (I've never touched a theorem prover before, just going through the lean book now -- apologies in advance). I'm wondering if lean4 (or maybe even in lean3) you can construct objects via a C/C++ FFI. This was a feature that I noticed in other theorem provers, but haven't seen anywhere in the lean docs.

Johan Commelin (Jan 28 2020 at 16:32):

Lean 3, not really
Lean 4, yep, that's the plan

Sam Stites (Jan 28 2020 at 16:34):

very cool! could you give a rough idea of when we can expect lean4?

Johan Commelin (Jan 28 2020 at 16:34):

Not really... maybe end of this year. Maybe next year...

Johan Commelin (Jan 28 2020 at 16:34):

It's open source. Some parts are already usable.

Johan Commelin (Jan 28 2020 at 16:35):

But the tactic framework isn't there yet. So for maths (my field) it isn't really usable yet.

Sam Stites (Jan 28 2020 at 16:37):

that's a good enough estimate for me!

Sam Stites (Jan 28 2020 at 16:38):

Would the google groups be the best way to track progress? or perhaps the releases on github? nevermind! just going to do both. Thanks!

ROCKY KAMEN-RUBIO (Jan 29 2020 at 06:28):

Hi everyone, I'm a new Lean user and trying to work through some of the code in Kevin's lecture tutorial. I'm trying to define is_even using the inductive type like he does, but keep getting this error. Does anyone know what's causing this? Thanks! Screen-Shot-2020-01-29-at-1.26.38-AM.png

Alex J. Best (Jan 29 2020 at 06:33):

I think you are missing the bar character | at the start of each line

Alex J. Best (Jan 29 2020 at 06:33):

inductive is_even :   Prop
| zero : is_even 0
| step {n} : is_even n  is_even (n+2)

Nicholas Talin (Jan 30 2020 at 01:42):

Are there versioned tarball releases of Mathlib? I made an XBPS template for Lean itself and would like to make one for Mathlib too. It looks like the latest releases in the repo are from 2019.

Bryan Gin-ge Chen (Jan 30 2020 at 01:44):

The mathlib-nightly releases may have what you want.

Johan Commelin (Jan 30 2020 at 06:38):

@Nicholas Talin Cool! I'm also using Void. Haven't yet looked into making XBPS templates though. Thanks for doing this. It might be useful to create a template for the supporting tools cache-olean and update-mathlib. Because if you have 10 Lean projects you might want them to depend on 10 different versions of mathlib. In practice a global mathlib install isn't used very much by most people in the community.

Nicholas Talin (Jan 30 2020 at 09:23):

Since it looks like both scripts are in PyPI's mathlibtools, I guess the Lean template is all that's needed.

Nicholas Talin (Jan 30 2020 at 09:44):

Should I be using 3.4.2 instead of 3.5.0?

Johan Commelin (Jan 30 2020 at 10:00):

We are in the middle of a transition to 3.5c

Johan Commelin (Jan 30 2020 at 10:01):

@Nicholas Talin So I think you're good when you stick with that.

Mason Marche (Jan 30 2020 at 17:43):

Is there a stream for troubleshooting installation? I'm having an issue with the mathlib

Scott Morrison (Jan 30 2020 at 17:44):

Right here!

Kevin Buzzard (Jan 30 2020 at 17:46):

Are you following https://github.com/leanprover-community/mathlib#installation ?

Mason Marche (Jan 30 2020 at 17:47):

Right now I'm on macos and it looks like I've done everything correctly to install and build mathlib, but for some reason, VScode gives me the error 'Unknown Identifier Q' whenever I type \Q

Kevin Buzzard (Jan 30 2020 at 17:47):

Did you import data.rat or data.rat.basic or whatever it's called now?

Kevin Buzzard (Jan 30 2020 at 17:48):

(probably either work now)

Mason Marche (Jan 30 2020 at 17:48):

That fixed it, thanks!

Kevin Buzzard (Jan 30 2020 at 17:49):

If that worked then you're almost certainly up and running.

Mason Marche (Jan 30 2020 at 17:49):

It looks like I am

Mason Marche (Jan 31 2020 at 00:15):

Second dumb question, what's the library I need to import to access real numbers (ℝ)?

Yury G. Kudryashov (Jan 31 2020 at 00:16):

data.real.basic

Mason Marche (Jan 31 2020 at 00:16):

thank you

Chris B (Jan 31 2020 at 00:22):

Is the implementation of nat.sqrt/nat.sqrt_aux in mathlib original or is there somewhere I can read about it? I don't interact with bitwise stuff very frequently and I'm curious why it works the way it does.

Daniel Keys (Jan 31 2020 at 01:16):

Just starting chapter 4 in "Theorem proving with Lean". How does one produce an arbitrary element of type α in this example (among the exercises)?

variable α : Type
variable a : α
variable r : Prop
example : r  ( x : α, r) :=
begin
    intro pr,
    exact exists.intro _ pr
end

Jeremy Avigad (Jan 31 2020 at 01:22):

If the exercise doesn't give t to you, it is a mistake: you need something like variable a : α in the assumptions. In Lean, types can be empty.

Daniel Keys (Jan 31 2020 at 01:23):

There is a variable a : α, but how can I make use of it inside the example?

Chris B (Jan 31 2020 at 01:26):

If you have variable a : α somewhere, you need to put include <variable name> above the term if you want to use variables in tactic blocks (between the begin and end)

variable α : Type
variable r : Prop
variable a : α

include a
example : r → (∃ x : α, r) :=
begin
    intro pr,
    exact exists.intro a pr
end

Chris B (Jan 31 2020 at 01:26):

But in the snippet you only have variable α : Type.

Bryan Gin-ge Chen (Jan 31 2020 at 01:27):

You can also refer to a if you write the proof in term mode:

variables (α : Type)
variable a : α
variable r : Prop

example : r  ( x : α, r) := λ hr, exists.intro a hr

Daniel Keys (Jan 31 2020 at 01:29):

Thanks! Including a, this is what I was missing! I edited the snippet and added a to the variables.

Chris B (Jan 31 2020 at 01:31):

There's some more detail and some stuff explaining why you actually need the include/omit bits in the Lean reference manual at the bottom of p. 37.

Bryan Gin-ge Chen (Jan 31 2020 at 01:41):

There's a discussion in 6.2 of TPiL as well.

Mario Carneiro (Jan 31 2020 at 10:45):

Is the implementation of nat.sqrt/nat.sqrt_aux in mathlib original or is there somewhere I can read about it? I don't interact with bitwise stuff very frequently and I'm curious why it works the way it does.

@Chris B I think I am the one responsible for the current implementation of nat.sqrt. I am pretty sure I got it from a wikipedia article, and I think it is the "iterative algorithm" mentioned here.

Mario Carneiro (Jan 31 2020 at 10:47):

I'm not finding the description I originally worked from, the ones I can find all look different

Mario Carneiro (Jan 31 2020 at 10:50):

Aha, it is the isqrt() function given here: Methods of computing square roots

Kevin Buzzard (Jan 31 2020 at 11:30):

Aah, the old "bring digits down two at a time" method -- this is the method my father taught me for computing square roots by hand, although he used base 10.

Daniel Keys (Feb 02 2020 at 18:58):

Hi all, is there a way to write the lambda expression I'm using below in more of a tactics mode style?

variables (α : Type) (p q : α  Prop)
variable a : α
variable r : Prop

include a
theorem T10L : (( x, p x)  r)   x, p x  r :=
begin
    intros Axpx_r,
    cases ( em ( x, p x) ) with Axpx nAxpx,
    { -- case ax
        exact exists.intro a ( λ w, Axpx_r Axpx )
    },
    { sorry }
end

Kevin Buzzard (Feb 02 2020 at 19:39):

theorem T10L : (( x, p x)  r)   x, p x  r :=
begin
    intros Axpx_r,
    cases ( em ( x, p x) ) with Axpx nAxpx,
    { use a,
      intro w,
      apply Axpx_r,
      assumption
    },
    { sorry }
end

But use is a lean tactic from the maths library so you'll need import tactic at the top of your file (and mathlib).

Without mathlib you can just write existsi instead of use, but existsi doesn't work so well as an introduction rule for more complicated existential statements.

Daniel Keys (Feb 02 2020 at 19:43):

Thanks Kevin! I did see similar things in the number game, but apparently I need some more work before it sinks.

Kevin Buzzard (Feb 02 2020 at 19:43):

This software has a huge learning curve.

Kevin Buzzard (Feb 02 2020 at 19:44):

It's worth the climb though ;-)

Daniel Keys (Feb 02 2020 at 19:53):

I'm having a very good time learning it, too bad I can basically only use the weekends. The community is very helpful though!

Paul van Wamelen (Feb 05 2020 at 14:45):

Ultra noob question: Why can't I prove that x^2 = x * x over the integers?

variable x : ℤ
#reduce x + (-6)^2

example (x : ℤ) : x = x := by refl

example (x : ℤ) : x^2 = x * x := by simp

Gives simplify tactic failed to simplify. Actually with import data.int.basic the -6 doesn't even reduce. Using import data.zmod.basic reduces -6, but not (apparently) x^2. What am I missing? Any clues would be greatly appreciated!

Kevin Buzzard (Feb 05 2020 at 14:50):

import algebra.group_power

example (x : ) : x^2 = x * x := by library_search
-- exact pow_two x

Kevin Buzzard (Feb 05 2020 at 14:51):

and indeed pow_two hasn't been tagged with the simp attribute, so simp doesn't know about this lemma.

Kevin Buzzard (Feb 05 2020 at 14:55):

If you only import data.int.basic then you don't get the function aba^b with aZa\in\mathbb{Z} and bNb\in\mathbb{N}. This is because the definition of the power function is made for aa in an arbitrary monoid and bNb\in\mathbb{N}, so you have to import some group theory library to get it, not the integer library!

Paul van Wamelen (Feb 05 2020 at 15:14):

Got it! Thanks! I think I'm going to have some follow up questions though :)

Kevin Buzzard (Feb 05 2020 at 15:17):

I've been having followup questions since 2017 ;-)

Kevin Buzzard (Feb 05 2020 at 15:18):

it turns out that some stuff that mathematicians think of as "should be straightforward" is actually an interesting research project in the formal proof verification community.

Kevin Buzzard (Feb 05 2020 at 15:21):

import algebra.group_power

attribute [simp] pow_two

example (x : ) : x^2 = x * x := by simp

I am not 100% clear about why pow_two is not tagged with simp, but simp-tagging is a subtle issue best left to those who know a lot more computer science than me -- I don't really understand the algorithm. All I know is that tagging everything with simp is a bad idea.

Chris Hughes (Feb 05 2020 at 15:22):

by ring should work here, or perhaps by abel if it has been updated to deal with multiplicative monoids.

JDM (Feb 05 2020 at 16:28):

Hi people, it's been a while since I have done anything concerning Lean but I am back with renewed interest. Back when I took notice there was no real way to get it working with Windows and I didn't have a reasonble option to use Linux, but now I do. So as a complete and utter noob with building things on Linux: how do I go about this?

Patrick Massot (Feb 05 2020 at 16:28):

Don't build it.

JDM (Feb 05 2020 at 16:29):

Okay, in that case, are there detailed installation instructions?

Johan Commelin (Feb 05 2020 at 16:29):

Yes, see github readme

Johan Commelin (Feb 05 2020 at 16:29):

Lemme fetch the link

Johan Commelin (Feb 05 2020 at 16:30):

https://github.com/leanprover-community/mathlib#installation

JDM (Feb 05 2020 at 16:31):

Oh, that's great, thanks a lot!

Johan Commelin (Feb 05 2020 at 16:31):

We actually went through a transition to a new version of Lean today

Johan Commelin (Feb 05 2020 at 16:31):

For the first time in ~ 2 years

JDM (Feb 05 2020 at 16:31):

Seems like a good time to get started then

Johan Commelin (Feb 05 2020 at 16:31):

So let's hope the instructions still work

JDM (Feb 05 2020 at 16:31):

That's a good point :-D

Johan Commelin (Feb 05 2020 at 16:32):

If anything goes wrong, please report here :wink:

JDM (Feb 05 2020 at 16:33):

Will do, I'm absolutely thrilled in any case :-)

Rob Lewis (Feb 05 2020 at 16:33):

For the first time in ~ 2 years

That's not really true, 3.4.2 moved some stuff from core to mathlib and that was done about a year ago. But this is the first time we're pointing to a version of Lean off the official leanprover site, and the first time with a lot of the supporting tools.

Johan Commelin (Feb 05 2020 at 16:35):

Ok, maybe I was exagerating. Your explanation is more exciting anyway :slight_smile:

Matt Earnshaw (Feb 06 2020 at 12:31):

is there a way to "unfold" what a tactic is doing in a particular case? for example I can prove x \in {x} by finish (which is how set.mem_singleton is defined), but I'm a bit curious what's going on there

Cerek Hillen (he) (W2'20) (Feb 06 2020 at 12:53):

Not sure if this is exactly what you want, but you could do #check on the theorem that includes it?

Daniel Keys (Feb 06 2020 at 14:01):

You can use #print, which gives you the whole proof. Many times that will be more than you want.

Cerek Hillen (he) (W2'20) (Feb 06 2020 at 14:05):

Sorry, mixed up my macros--meant #print haha

Daniel Keys (Feb 06 2020 at 14:09):

Is there anyone who can help me with advice on how to embed Lean code in LaTex? There are lines of code in the mathlib-paper in section 4.2 for example, but I couldn't make that look not nearly as nicely myself. Verbatim doesn't work because of the UTF characters, even with additional packages. I can hack text to look like Lean code by using \texttt and isolating math characters, but that's a lot of tedious work. If someone has a sample document.tex file with Lean code to attach (like the mathlib-paper.tex stripped down, for example) it would help a lot.

Anne Baanen (Feb 06 2020 at 14:13):

You can use the lstlean.tex file available here: https://github.com/leanprover-community/lean/tree/master/extras/latex

Daniel Keys (Feb 06 2020 at 14:18):

Works great, thank you!

Matt Earnshaw (Feb 06 2020 at 14:25):

@Daniel Keys @Cerek Hillen (he) (W2'20) perfect thanks

Kevin Buzzard (Feb 06 2020 at 15:34):

Note that the simp and tidy and library_search tactics have options where they can print out what they did. With simp you have to run squeeze_simp instead -- I think for the other two it just works automatically.

As for Lean code in LaTeX, when I tried this when I wrote my article for the LMS newsletter, some stuff came out really poorly, and when I asked @Rob Lewis he suggested that I had a super-old version of lstlean.tex and he sent me a newer one. I think the super-old version is the one which Anne has linked to. I had problems with some unicode characters IIRC.

Johan Commelin (Feb 06 2020 at 15:39):

If that is the case, we should update that link.

Rob Lewis (Feb 06 2020 at 15:39):

I'll update it to my current version in a bit.

Daniel Keys (Feb 06 2020 at 16:01):

Yes indeed, several of the Unicode characters in sample.tex seem to give me quite some trouble.

Mario Carneiro (Feb 06 2020 at 16:04):

You have to include a package for unicode characters, or else all the lean unicode characters will be garbled in tex

Mario Carneiro (Feb 06 2020 at 16:05):

I think it is \usepackage[utf8]{inputenc}

Daniel Keys (Feb 06 2020 at 16:06):

Also some symbols, like $\mathbb{C}$ are not listed, and trying to use math mode in \lstinline doesn't work.
@Mario Carneiro Yes, that package is included in the preamble.

Mario Carneiro (Feb 06 2020 at 16:06):

or possibly \usepackage[utf8x]{inputenc}

Mario Carneiro (Feb 06 2020 at 16:07):

Math mode doesn't work inside \lstinline. I think there is a way to set the escape character so this works though

Rob Lewis (Feb 06 2020 at 16:08):

The package requirements are mentioned in the md file in the directory Anne linked.

Mario Carneiro (Feb 06 2020 at 16:08):

If a symbol is not listed, you can add it to lstlean.tex. (This is why Floris, and Rob, and I, all have uncommitted modifications to lstlean.tex since the last version some 3 years ago)

Rob Lewis (Feb 06 2020 at 16:11):

https://github.com/leanprover-community/lean/pull/110

Rob Lewis (Feb 06 2020 at 16:12):

Probably a bunch more keywords could be removed, since they're mostly used in tactic mode and it's weird to highlight them there.

Daniel Keys (Feb 06 2020 at 16:38):

Since the Unicode characters are quite difficult to copy/paste and most of us are used to LaTex math symbols, it would be helpful if anyone knew how to use (escape into) math mode inside \lstinline.

Mario Carneiro (Feb 06 2020 at 16:43):

The majority of lean snippets in tex papers I get by copying from a real lean file

Mario Carneiro (Feb 06 2020 at 16:43):

which of course has much better facilities for producing unicode

Mario Carneiro (Feb 06 2020 at 16:45):

There is a mathescape= option in lstlean.tex that you can set to true if you want $foo$ to enter math mode. This interferes with lean's use of $, though, so it's usually turned off

Mario Carneiro (Feb 06 2020 at 16:45):

I think you can also set these options directly in your preamble

Mario Carneiro (Feb 06 2020 at 16:48):

You can of course always write everything in tex if you want to do that, i.e. $\mathtt{\color{blue}{inductive} foo (\alpha : Type) : Type}$

Daniel Keys (Feb 08 2020 at 22:11):

Anyone can show me how to construct a finset ℤ from range n, where n : ℕ and every member is the negative of one of the elements in the range? Some kind of mapping should be possible.

Patrick Massot (Feb 08 2020 at 22:26):

finset.image (λ m : ℕ, -(m : ℤ)) (finset.range n)

Patrick Massot (Feb 08 2020 at 22:27):

I have no idea why finset.map is not what you were looking for.

Chris Hughes (Feb 08 2020 at 22:29):

Also finset.Ico_ℤ

Daniel Keys (Feb 08 2020 at 22:32):

Thanks @Patrick Massot @Chris Hughes
I don't know how to use finset.map yet. For example, I tried to get the same entries (not the negatives) with finset.map int.of_nat (range 5), but that doesn't work. The symbol used in finset.lean for the mapping function is new to me at this point (the "curved" arrow).

Chris Hughes (Feb 08 2020 at 22:35):

You can do it with map like this

example (n : ) : finset  := finset.map int.of_nat, @int.of_nat.inj
  (finset.range n)

map only works with injective functions, the curly arrow is an embedding, a pair of a function, and a proof that it's injective.

map is faster for computation since it doesn't erase duplicates.

Patrick Massot (Feb 08 2020 at 22:37):

Why isn't called fast_map or inj_map then? It would leave the canonical name for the canonical operation.

Patrick Massot (Feb 08 2020 at 22:38):

Daniel, you can use "jump to definition" in VScode also on symbols.

Sayantan Majumdar (Feb 09 2020 at 16:49):

How do I get the proofs for simple things like n + n = 2 * n or 1 < 2? How do I prove 1 < 2? I was expecting these proofs would already be in the library and not hiding from the user

Patrick Massot (Feb 09 2020 at 16:50):

ring and norm_num would do these for you.

Patrick Massot (Feb 09 2020 at 16:50):

Assuming you use mathlib.

Sayantan Majumdar (Feb 09 2020 at 16:54):

in chapter 8... haven't been introduced to these yet

Patrick Massot (Feb 09 2020 at 16:55):

You need to give us more context then. Are you doing a specific exercise in TPIL?

Sayantan Majumdar (Feb 09 2020 at 16:56):

In 8.4, the second last example. trying to solve the sorry

Sayantan Majumdar (Feb 09 2020 at 16:57):

nat_to_bin

Patrick Massot (Feb 09 2020 at 16:59):

I don't think this is meant as an exercise. I think Jeremy didn't want to distract readers with details of this proof.

Daniel Keys (Feb 09 2020 at 17:00):

@Sayantan Majumdar Here is a slightly more complex proof. Something like you want is part of it, see the have h1:

theorem sumUpToN_1 (n : ) : 2 * (range (n + 1)).sum id = n * (n + 1) :=
begin
  induction n with d hd,
  refl,
  rw sum_range_succ,
  rw mul_add,
  rw hd,
  rw id.def,
  rw nat.succ_eq_add_one,
  ---------- can get the goal thus
  rw add_assoc d 1 1,
  rw  nat.succ_eq_add_one 1,
  rw  add_mul 2 d (d+1),
  have h1 : nat.succ 1 = 2, from rfl,
  rw h1,
  rw add_comm 2 d,
  rw mul_comm (d+2) (d+1),
  ------------ or simply by:
  -- ring,
end

Kevin Buzzard (Feb 09 2020 at 17:02):

Daniel -- instead of rewriting h1, which is true by definition, you can use the change tactic to just rewrite the goal to what you want it to be

Sayantan Majumdar (Feb 09 2020 at 17:03):

I understand the h1, 1 < 2 seems far too complicated to prove. It would be easier to shift to Coq

Patrick Massot (Feb 09 2020 at 17:03):

Again, norm_num proves it. What else do you want?

Patrick Massot (Feb 09 2020 at 17:04):

How can a proof be simpler than a single tactic invocation?

Daniel Keys (Feb 09 2020 at 17:05):

@Patrick Massot Yes, Patrick, but we beginners need to learn the basics of expressing ourselves in Lean.

Sayantan Majumdar (Feb 09 2020 at 17:05):

So, how do I prove it with norm_num?

Patrick Massot (Feb 09 2020 at 17:06):

example : 1 < 2 := by norm_num

Daniel Keys (Feb 09 2020 at 17:06):

@Sayantan Majumdar Here is a proof for your initial quest:

variable n : 
theorem twoEqOneOne : 2 * n = n + n :=
begin
  have h1 : 2 = nat.succ 1, from rfl,
  rw h1,
  rw nat.succ_eq_add_one 1,
  rw add_mul,
  rw one_mul, done
end

Sayantan Majumdar (Feb 09 2020 at 17:07):

thanks. How did you find the succ_eq_add_one? is there a good way of searching for these?

Kevin Buzzard (Feb 09 2020 at 17:09):

Here is another proof:

variable n : 
theorem twoEqOneOne : 2 * n = n + n :=
begin
  show (1 + 1) * n = n + n,
  rw add_mul,
  rw one_mul
end

Patrick Massot (Feb 09 2020 at 17:09):

import tactic

example (n : ) : 2*n = n + n :=
begin
  ring,
end

example (n : ) : 2*n = n + n :=
begin
  library_search,
end

Daniel Keys (Feb 09 2020 at 17:09):

I had all those problems you have before going through the natural number game. Kevin Buzzard did a great job with that! It can get you started.

Patrick Massot (Feb 09 2020 at 17:10):

Again, either you want to suffer because you think this is a good exercise, and then I don't understand what you are complaining about. Or you can use the answers I provided one minute after you asked.

Kevin Buzzard (Feb 09 2020 at 17:11):

@Sayantan Majumdar certain lemmas you just end up learning because they come up a lot. You can use library_search to find them.

import tactic

variable n : 

open nat

theorem what_is_this_called : succ n = n + 1 :=
begin
  library_search -- we learn it is true by definition
end

Daniel Keys (Feb 09 2020 at 17:11):

@Kevin Buzzard Kevin, does change need import tactic?

Kevin Buzzard (Feb 09 2020 at 17:12):

show certainly doesn't. I prefer change because it works on hypotheses as well as goals so it's easier for the beginner.

Kevin Buzzard (Feb 09 2020 at 17:13):

@Sayantan Majumdar there is also a naming convention which we stick to in Lean, meaning that most users can after a while guess that succ n = n + 1 will be called succ_eq_add_one. But it takes a while to learn the conventions.

Sayantan Majumdar (Feb 09 2020 at 17:14):

Thanks @Kevin Buzzard @Daniel Keys was looking for a better way to find all the proofs available. I was using the naming convention and using print and relying on vscode intelligence

Kevin Buzzard (Feb 09 2020 at 17:15):

I have no reason to believe that this is any harder in Lean than in Coq or any other theorem prover. There will be naming conventions, and tactics for people who don't know the names but want to get the job done.

Kevin Buzzard (Feb 09 2020 at 17:16):

The best way to get information about this sort of thing is to ask here. The existence of this chat room is one thing which makes learning Lean easier than learning all the other systems -- for the other systems you have to ask on a mailing list or stackoverflow.

Patrick Massot (Feb 09 2020 at 17:17):

He asked, by then decided to ignore the answer and start writing he should use Coq.

Daniel Keys (Feb 09 2020 at 17:18):

Here's a chat newbie question, how do you get a reply box to look reddish instead of white?

Patrick Massot (Feb 09 2020 at 17:18):

Mention some name using @

Patrick Massot (Feb 09 2020 at 17:18):

That person will see a reddish box

Sayantan Majumdar (Feb 09 2020 at 17:18):

@Patrick Massot I have to use theorems that I have already proved to proof this, I can't use rings and norm_num because I haven't reached there yet

Patrick Massot (Feb 09 2020 at 17:19):

Again, I think you misunderstood the status of this theorem. It's not an exercise. And if you want to make it an exercise, then why shouldn't you learn how to search properly?

Mario Carneiro (Feb 09 2020 at 17:20):

1 < 2 can also be proven by dec_trivial

Daniel Keys (Feb 09 2020 at 17:22):

@Sayantan Majumdar Have a look at this, it involves an inequality:

https://stackoverflow.com/questions/59669492/how-to-switch-types-in-lean-theorem-prover-when-constants-are-involved#comment105499922_59669492

Patrick Massot (Feb 09 2020 at 17:22):

library_search would have solved both your goals.

Mario Carneiro (Feb 09 2020 at 17:23):

If you are working from the absolute basics, 1 < 2 is nat.le_refl 2, where nat.le_refl is proven by induction

Kevin Buzzard (Feb 09 2020 at 17:27):

But I think Patrick is right -- I don't think there is enough in TPIL for the learner working through the book to easily remove that sorry. It is trivial to remove using the machinery that mathlib provides but because TPIL is about Lean not mathlib, Jeremy leaves it there. If you're trying to remove it then you're moving away from the carefully chosen basic examples in TPIL and the real world of actual mathematical formalisation. mathlib has solved all your problems in lots of different ways but you have to learn mathlb like you have to learn Lean.

Mario Carneiro (Feb 09 2020 at 17:28):

two_mul has a proof using commutativity to turn it inro n*2 which is defeq to 0+n+n

example (n : ) : 2 * n = n + n :=
eq.trans (mul_comm 2 n) $
congr_arg (λ i, i + n) $ zero_add _

But this is a really low level proof. Just use the theorems, or better yet, the general tactics to kill this goal.

Patrick Massot (Feb 09 2020 at 17:29):

For the record, the full sorry can be replaced by by rw nat.div_lt_iff_lt_mul ; linarith,

Kevin Buzzard (Feb 09 2020 at 17:29):

@Sayantan Majumdar Mario's proof probably works without mathlib :-)

Patrick Massot (Feb 09 2020 at 17:30):

(after being disappointed by omega :sad:)

Kevin Buzzard (Feb 09 2020 at 17:30):

omega doesn't do it??

Patrick Massot (Feb 09 2020 at 17:30):

omega doesn't like me. It never does what I ask it to do.

Mario Carneiro (Feb 09 2020 at 17:31):

This works for me:

example (n : ) : 2 * n = n + n := by omega

Kevin Buzzard (Feb 09 2020 at 17:31):

it's because you were too rude about nat subtraction a year ago

Patrick Massot (Feb 09 2020 at 17:31):

This is not the full thing, Mario.

Kevin Buzzard (Feb 09 2020 at 17:31):

Patrick is proving (n+2)/2<n+2

Kevin Buzzard (Feb 09 2020 at 17:31):

that's the sorry in TPIL

Mario Carneiro (Feb 09 2020 at 17:31):

aha

Mario Carneiro (Feb 09 2020 at 17:32):

example (n : ) : (n+2)/2<n+2 :=
nat.div_lt_self dec_trivial dec_trivial

Kevin Buzzard (Feb 09 2020 at 17:33):

this is the problem when people know the libraries too well ;-)

Mario Carneiro (Feb 09 2020 at 17:33):

example (n : ) : (n+2)/2<n+2 :=
by rw nat.div_lt_iff_lt_mul; omega

Mario Carneiro (Feb 09 2020 at 17:34):

I'm not sure what omega's status is wrt integer division

Kevin Buzzard (Feb 09 2020 at 17:34):

so omega just hates Patrick personally

Patrick Massot (Feb 09 2020 at 17:34):

No, I meant by omega alone.

Patrick Massot (Feb 09 2020 at 17:35):

No rewriting first.

Kevin Buzzard (Feb 09 2020 at 17:35):

So I guess division is beyond the capabilities of omega is the concluson

Rob Lewis (Feb 09 2020 at 17:36):

FYI, https://github.com/leanprover-community/mathlib/issues/1484#issuecomment-561260385

Rob Lewis (Feb 09 2020 at 17:36):

Although #1748 definitely needs to be finished first.

Patrick Massot (Feb 09 2020 at 17:43):

Thanks Rob!

Paul van Wamelen (Feb 09 2020 at 19:11):

Oh! I figured out what TPIL stands for :). Can I suggest a small improvement to TPIL? (see, I can even use it in a sentence):
In exercise 1 at the end of chapter 3, as one of the many "other properties", the reader is asked to prove ¬(p ↔ ¬p), but then this is also broken out as exercise 3. As exercise 2 is all about classical the assumption is that exercise 1 should be without it. Exercise 3 is certainly interesting enough that breaking it out seems justified. I would suggest removing it from exercise 1.

Kevin Buzzard (Feb 09 2020 at 19:14):

¬(p ↔ ¬p) can be proved without LEM, but it's tricky.

Paul van Wamelen (Feb 09 2020 at 19:50):

Right, and exercise 3 asks the reader to do it without LEM. But even in exercise 1 the assumption/implication is that they should be done without LEM. I'm just saying the duplication should probably be removed. I was stuck on exercise 1 for a long time until I noticed that I was essentially already working on exercise 3. By the time I was through with exercise 2, I could (finally) work through 3.

Sayantan Majumdar (Feb 09 2020 at 21:49):

Anyone have an idea how to solve this e I have

Sayantan Majumdar (Feb 09 2020 at 21:49):


Sayantan Majumdar (Feb 09 2020 at 21:50):

example (n  : \N) ( h : 1 * (n + 2) < 2 * (n + 2)) : (n + 2) < 2 * (n + 2)

Sayantan Majumdar (Feb 09 2020 at 21:59):

I was wondering if someone could help me out with this simple proof

example (n  : \N) ( h : 1 * (n + 2) < 2 * (n + 2)) : (n + 2) < 2 * (n + 2)

Kevin Buzzard (Feb 09 2020 at 22:17):

you can rewrite one_mul at h. After a while you'll be able to guess that one_mul is the name of the theorem which says 1*x=x.

Sayantan Majumdar (Feb 09 2020 at 22:26):

the rewrite will only work will = not <

Sayantan Majumdar (Feb 09 2020 at 22:26):

*with = not <

Reid Barton (Feb 09 2020 at 22:36):

Don't rewrite using h, rewrite h itself: rw one_mul at h

Sayantan Majumdar (Feb 09 2020 at 22:43):

thanks

Sayantan Majumdar (Feb 09 2020 at 23:04):

Anybody got any suggestions for this

example (n : ) (h₁ : 2 > 0) (h₂ : (n + 2) < ((n + 2) * 2)) : (n + 2) / 2 < (n + 2)

was thinking about using div_lt_of_mul_lt_of_pos but there seems to be an issue

Kevin Buzzard (Feb 09 2020 at 23:06):

example (n : ) (h₁ : 2 > 0) (h₂ : (n + 2) < ((n + 2) * 2)) : (n + 2) / 2 < (n + 2) :=
begin
  rw nat.div_lt_iff_lt_mul _ _ h₁,
  exact h₂
end

Kevin Buzzard (Feb 09 2020 at 23:07):

#check @div_lt_of_mul_lt_of_pos
/-
div_lt_of_mul_lt_of_pos :
  ∀ {α : Type u_1} [_inst_1 : linear_ordered_field α] {a b c : α}, c > 0 → b < a * c → b / c < a
  -/

div_lt_of_mul_lt_of_pos is a theorem about linearly ordered fields.

Kevin Buzzard (Feb 09 2020 at 23:08):

example (n : ) (h₁ : 2 > 0) (h₂ : (n + 2) < ((n + 2) * 2)) : (n + 2) / 2 < (n + 2) :=
(nat.div_lt_iff_lt_mul _ _ h₁).2 h₂

Kevin Buzzard (Feb 09 2020 at 23:09):

example (n : ) (h₁ : 2 > 0) (h₂ : (n + 2) < ((n + 2) * 2)) : (n + 2) / 2 < (n + 2) :=
begin
  rw nat.div_lt_iff_lt_mul,
  { assumption},
  { assumption}
end

Kevin Buzzard (Feb 09 2020 at 23:21):

example (n : ) (h₁ : 2 > 0) (h₂ : (n + 2) < ((n + 2) * 2)) : (n + 2) / 2 < (n + 2) :=
by rwa nat.div_lt_iff_lt_mul _ _ h₁

Matt Earnshaw (Feb 11 2020 at 13:38):

I have a function producing Props a bit like the following (but in general there can be any finite number of disjuncts)

def g (p q r s : ℕ) : Prop := (p = q) ∨ (r = s) ∨ false

now I suppose these should be decidable and hence printable, which is my aim. I have read a bit about decidable props but can't quite figure out how to reduce an application of g to a true or false

Rob Lewis (Feb 11 2020 at 13:43):

Depending on the exact structure of your function, Lean can probably infer that it's decidable already. You need either @[reducible] def g or @[derive decidable] def g (the latter will only work with mathlib). Then you can #eval to_bool (g 1 2 3 4).

Matt Earnshaw (Feb 11 2020 at 14:14):

@Rob Lewis thanks. actually my Prop was set membership, so I had to stipulate finset but then this works. at some point I will need to understand better what is going on with this "attribute" business but for now...

Rob Lewis (Feb 11 2020 at 14:23):

By default, Lean won't unfold the definition of g to see if it's decidable, because this gets very expensive. @[reducible] says "go ahead and unfold this anyway," and @[derive decidable] says "unfold this temporarily to check that it's decidable, and add an instance to the environment."

Matt Earnshaw (Feb 12 2020 at 14:44):

is there an isomorphism Type x Type with Type? I have two endofunctors on Type and it would seem nice to be able to see their product as again being (via isomorphism) another such endofunctor. perhaps this is nonsense

Yury G. Kudryashov (Feb 12 2020 at 14:48):

You clearly have Type × Type → Type. How do you want to construct Type → Type × Type?

Yury G. Kudryashov (Feb 12 2020 at 14:48):

Disclaimer: I'm not an expert in category theory.

Matt Earnshaw (Feb 12 2020 at 14:56):

well, it can just be the diagonal, but if we say def f : Type ⥤ (Type × Type) := λx, x × x, it does not typecheck

Anne Baanen (Feb 12 2020 at 15:02):

So I understand the question better: what is the inverse of (ℕ, ℝ) supposed to be under f?

Yury G. Kudryashov (Feb 12 2020 at 15:09):

A functor is more than a map.

Yury G. Kudryashov (Feb 12 2020 at 15:09):

You need at least {obj := ..., map := ...} if I remember field names correctly.

Yury G. Kudryashov (Feb 12 2020 at 15:11):

You can define functors diag : Type ⥤ (Type × Type) and prod : (Type × Type) ⥤ Type. What relations on these functors do you want?

Matt Earnshaw (Feb 12 2020 at 15:18):

yes, excuse me. I mean, I think clearly this does not work in the naive sense. So let me rephrase, why should N x N : Type? I think you can say something like type formation applies to types of a particular universe and not on that universe itself. But this typechecking example seems to indicate that we shoil be able to view any product of types as itself a Type. Am I hopelessly confused?

Anne Baanen (Feb 12 2020 at 15:23):

You can view (×) as a function prod : Type → Type → Type, Thus, ℕ : Type implies prod ℕ ℕ : Type, just like mul : ℕ → ℕ → ℕ implies that mul 1 2 : ℕ. Does that help?

Matt Earnshaw (Feb 12 2020 at 15:35):

thanks. I'm not sure now how this "thought" got off the ground at all, will press on

Kevin Buzzard (Feb 12 2020 at 16:31):

is there an isomorphism Type x Type with Type? I have two endofunctors on Type and it would seem nice to be able to see their product as again being (via isomorphism) another such endofunctor. perhaps this is nonsense

To do that you don't need an identification of Type x Type with Type -- you just compose the endofunctors, right?

Kevin Buzzard (Feb 12 2020 at 16:31):

There will be functor.comp or something like that.

Matt Earnshaw (Feb 12 2020 at 23:15):

There will be functor.comp or something like that.

I really needed their categorial product, not comp, but I have realized now that my mistake is that I want their product in the functor category Fun(Type, Type) (which is "calculated pointwise"), not their product in the category Type (which is what functor.prod gives, essentially)...

In defense of the idea that we might identify Type with Type × Type, it is the difference between whether there are only (types of) things in the world or if pairs (of types) of things are distinct, and not themselves (types of) things. In other words, I was thinking that if Type is sort of like a "universe of sets", and we thought that "everything is a set" then a pair of sets should itself be a set. Again, naively this doesn't work but I thought there might be some kind of "universe trick" or the like... something like the following (although this fails):

variable f : Type u  Type u
variable g : Type w  Type w

#check functor.prod f g
 -- functor.prod f g : Type u × Type w ⥤ Type u × Type w

def dummy (fg : Type (max (w+1) (u+1))  Type (max (w+1) (u+1))) := 0

#check dummy (functor.prod f g)  -- fails

Kevin Buzzard (Feb 12 2020 at 23:17):

It's just not true that Type = Type x Type -- does this help?

Kevin Buzzard (Feb 12 2020 at 23:17):

This equality says that given a set there is some nice way to get two sets from it.

Kevin Buzzard (Feb 12 2020 at 23:18):

You can certainly bundle together two types to get a new type, but there's no natural inverse map from Type to Type x Type because most types are not two other types bundled together.

Kevin Buzzard (Feb 12 2020 at 23:19):

BTW if you write ```lean instead of just ``` at the top of your quoted code then you get syntax highlighting :-)

Matt Earnshaw (Feb 12 2020 at 23:20):

no argument there

Kevin Buzzard (Feb 12 2020 at 23:21):

In other words, I was thinking that if Type is sort of like a "universe of sets", and we thought that "everything is a set" then a pair of sets should itself be a set.

This part is fine -- that's going from Type x Type to Type.

Kevin Buzzard (Feb 12 2020 at 23:22):

This is called sum in Lean, with notation (in VS Code it's \oplus)

Chris Hughes (Feb 12 2020 at 23:24):

I'm not sure it's fine in the sense that it was meant, there's some natural functions Type x Type to Type, but I can't just give Lean a pair of types when it expects a Type.

Chris Hughes (Feb 12 2020 at 23:26):

sums of Types are not pairs of Types.

Kevin Buzzard (Feb 12 2020 at 23:26):

Oh of course you're right.

Kevin Buzzard (Feb 12 2020 at 23:31):

so it's hard to get dummy to typecheck because given a pair of types I can manufacture a type, but I can't go the other way, so I can't construct a functor Type -> Type from a functor Type^2 -> Type^2

Chris Hughes (Feb 12 2020 at 23:40):

And even though in ZFC, a pair of sets is a set, this is a useless construction categorically, since it's always just a set with two elements I think.

Daniel Keys (Feb 16 2020 at 15:33):

Is there a simple way to obtain the truth value of a Prop? Here is what I mean: when doing #reduce tt && (0=0), output is tt. Obviously Lean evaluates the truth value of (0=0) : Prop and produces a bool. But if one tries #reduce 0 = 0, the output is not a bool. I can evaluate the Prop by something like def is_tt : bool := 0 = 0 and then #reduce is_tt, but there should be a more straightforward way that Lean itself appears to use and I couldn't find.

Kevin Buzzard (Feb 16 2020 at 15:34):

There is to_bool. Is this what you're after?

Daniel Keys (Feb 16 2020 at 15:39):

That is it! Somehow I couldn't find it, although I did find something like to_bool_true_eq_tt which didn't immediately help. Thanks!

Kevin Buzzard (Feb 16 2020 at 15:59):

Don't expect to learn your way around immediately. It took me years. Tools are getting better for you to solve many problems on your own nowadays, but sometimes asking is more effective.

PolyB (Feb 17 2020 at 17:29):

I've written:

inductive type_with (v : Type → Type)
| mk (α : Type) : v α → α → with_

def f : list (type_with has_to_string) → string := ....

Is there a way to write something equivalent without having to use a "helper" type ?
If it's not possible, does type_with already exists in lean standard library/mathlib ?

Yury G. Kudryashov (Feb 17 2020 at 18:27):

If you write lean after opening a code block, then you get syntax highlighting. Are you looking for Σ α : Type, has_to_string α?

Andrew Ashworth (Feb 17 2020 at 18:30):

you might want to look at https://github.com/leanprover/lean/blob/ceacfa7445953cbc8860ddabc55407430a9ca5c3/library/init/data/rbmap/basic.lean line 60

Andrew Ashworth (Feb 17 2020 at 18:31):

that line defines a string representation for a red-black tree

PolyB (Feb 17 2020 at 18:45):

Thanks for the responses,
sigma looks almost equivalent to my type_with , but it looks like I loose instance inference

Reid Barton (Feb 17 2020 at 19:20):

I'm not sure what instance inference you have with type_with...?

Gabriel Ruiz (Feb 17 2020 at 19:33):

Hi everyone! I'm beginning my journey in Lean, and I'm following along the tutorial here:
https://leanprover.github.io/theorem_proving_in_lean/dependent_type_theory.html

Why is it that when you input #check Type to lean's interpreter(?), you're returned Type 1 instead of Type 0?

Reid Barton (Feb 17 2020 at 19:37):

Type 0 is the same as Type. If we had Type : Type, then the system would be logically inconsistent (you could encode Cantor's paradox or something).

Reid Barton (Feb 17 2020 at 19:38):

Type 1 is a "bigger universe" which can contain things like Type = Type 0, and Type 1 is itself a type that lives in the next larger universe Type 2, and so on

Yury G. Kudryashov (Feb 17 2020 at 19:46):

@Andrew Ashworth You can write def bundle_to_string (α : Type) [h : has_to_string α] : Σ α : Type, has_to_string α := ⟨α, h⟩ (didn't test).

Reid Barton (Feb 17 2020 at 19:49):

Reid Barton said:

Type 1 is a "bigger universe" which can contain things like Type = Type 0, and Type 1 is itself a type that lives in the next larger universe Type 2, and so on

I realize now I pretty much explained just the same thing it already says in https://leanprover.github.io/theorem_proving_in_lean/dependent_type_theory.html#types-as-objects

Gabriel Ruiz (Feb 17 2020 at 19:50):

@Reid Barton I'm following, and now I'm starting to see my confusion. What then does #check do?

Reid Barton (Feb 17 2020 at 19:51):

#check tells you the type of something

Reid Barton (Feb 17 2020 at 19:51):

e.g. #check 3 will print something like 3 : nat

Gabriel Ruiz (Feb 17 2020 at 19:54):

Right, which is why I'm perplexed that it doesn't actually return Type 0 when I put in #check Type 0. I understand that it's also of Type 1, but why does it tell me that instead of its actual type: Type 0?

Johan Commelin (Feb 17 2020 at 19:54):

Because that is not its type

Johan Commelin (Feb 17 2020 at 19:55):

You can't be your own type

Gabriel Ruiz (Feb 17 2020 at 19:55):

Ohhhhhhhhhhhhhhhh

Gabriel Ruiz (Feb 17 2020 at 19:55):

That clicked, thank you!

Johan Commelin (Feb 17 2020 at 19:56):

Similarly, Type 1 will have type Type 2, and Type 2 will have type ...

Paul van Wamelen (Feb 17 2020 at 20:25):

How would one do:

lemma lol : (real.sqrt 3 : ) = 2 * real.sqrt 3 / 2 :=
begin
  sorry
end

by ring manages in , should I use some coe is injective?

Floris van Doorn (Feb 17 2020 at 21:21):

The tactic norm_cast will help you with anything coe-related:

import data.complex.basic

lemma lol : (real.sqrt 3 : ) = 2 * real.sqrt 3 / 2 :=
begin
  norm_cast, ring
end

Paul van Wamelen (Feb 17 2020 at 21:58):

Thanks!
I thought maybe the real.sqrt 3 was confusing things, so I tried to prove

lemma lol' (x : ) : x = 2 * x / 2

first. I finally got this

lemma lol' (x : ) : x = 2 * x / 2 :=
begin
   rw mul_comm,
  rw div_eq_mul_inv,
  rw mul_assoc,
  rw @complex.mul_inv_cancel (2 : ),
  rw mul_one,
  intro h,
  norm_cast at h
end

(I was trying exact absurd h dec_trivial, which wasn't working, where there is now a norm_cast, thanks again! :)).
Is it really this hard? Why doesn't dec_trivial work here (it worked in very similar circumstances before)

Kevin Buzzard (Feb 17 2020 at 22:03):

dec_trivial won't prove anything about the complexes, because the complexes don't have decidable equality.

Kevin Buzzard (Feb 17 2020 at 22:04):

dec_trivial works for naturals, integers and rationals, not for reals, complexes or pp-adics.

Kevin Buzzard (Feb 17 2020 at 22:10):

import data.complex.basic

lemma lol' (x : ) : x = 2 * x / 2 :=
begin
  field_simp,
  ring,
end

Paul van Wamelen (Feb 17 2020 at 22:11):

field_simp!!!!!!

Kevin Buzzard (Feb 17 2020 at 22:13):

ring doesn't always work when you have denominators, for obvious reasons. field_simp is, I think, some kind of denominator tactic. It's the first time I ever used it; I remembered people talking about it a month or two ago and I just looked it up in the tactic list.

Paul van Wamelen (Feb 17 2020 at 22:14):

Thanks for the dec_trivial explanation. I had h : 2 = 0 but couldn't figure out in which ring 2 and 0 were. Is there a #print setting that will show me?

Kevin Buzzard (Feb 17 2020 at 22:14):

You can set_option pp.all true before your proof. Then you'll see what Lean is actually doing behind the scenes. Be warned though, it's not a pretty sight sometimes.

Paul van Wamelen (Feb 17 2020 at 22:15):

:) Thanks!

Kevin Buzzard (Feb 17 2020 at 22:16):

Maybe you could try set_option pp.numerals false instead but I'm not sure what will happen then.

Rob Lewis (Feb 17 2020 at 22:19):

lemma lol' (x : ℂ) : x = 2 * x / 2

This should be provable by ring alone -- notice it works if you change C to R. IIRC there's something in the C++ norm_num that looks for an order instance unnecessarily when it does denominator cancellation. Probably fixable in 3.5c.

Matt Earnshaw (Feb 18 2020 at 00:14):

I am defining a type of automata (DFA). Initially I had something like:

structure DFA :=
 (Alphabet : Type) (State : Type)  (initialState : Q)  (AcceptingStates : finset Q) (δ : State  Alphabet  State)

this works ok for a while, I can define some enumerated type for an alphabet and so on. but then for some applications, it would be really useful to be able to map δ over all of the letters of the alphabet. So it has to be a list,

variable {β : Type}
structure DFA :=
 (Alphabet : list β) (State : Type) (initialState : Q)  (AcceptingStates : finset Q) (δ : State  Alphabet  State)

but of course now we have bad syntax at δ because Alphabet has become a term. I can change it to δ : forall a \in Alphabet, State → State), say, but then it seems like I have to provide proofs of membership all the time. Of course there is the basic solution δ : Q → list β → Q, but this is not a very expressive type since we do not want any old list β. Is there another possibility I've missed?

Matt Earnshaw (Feb 18 2020 at 00:29):

the other possibility is my original structure is good and my desire to map is a hangover from other languages. Actually, maybe I can use rec on my alphabets qua enumerated types instead... will see if that can give the intended effect tomorrow.

Chris B (Feb 18 2020 at 01:40):

@Matt E There's a thing called fintype in mathlib that might be what you want for the alphabet, or at least be a good jumping off point. It's sort of secretly a list (the hierarchy for its definition is fintype -> finset -> multiset -> quotient of list A over list permutation). It looks like there are functions fintype.to_finset and then finset.map, or you might be able to use coercions to get what you want. For the membership stuff I would assume there are helpful lemmas in those modules. Someone more knowledgeable can probably give details.

Kevin Buzzard (Feb 18 2020 at 10:13):

IIRC there's something in the C++ norm_num that looks for an order instance unnecessarily when it does denominator cancellation.

The point being that the order instance guarantees injectivity of any ring hom from int, rat or real, so is a cheap way of checking nonzero denominators

Kevin Buzzard (Feb 18 2020 at 10:17):

it would be really useful to be able to map δ over all of the letters of the alphabet. So it has to be a list

Lean has all sorts of maps, as well as things like set.image. What exactly do you need? Lists are lists and if you feel like your mental model isn't a list then maybe you need something else.

Kevin Buzzard (Feb 18 2020 at 10:17):

Maybe it's as simple as using set.range?

Matt Earnshaw (Feb 18 2020 at 14:17):

@Chris B that is indeed probably what I want. so I can put structure DFA (α : Type) [fintype α] := ... so far so good. now let

@[derive decidable_eq]
inductive α | A | B | C

now I need an instance of fintype, but I'm stuck at:

instance α_fin : fintype α := begin
  refine {elems := {α.A, α.B, α.C}, complete := _},
  intro,
  sorry
end

Kevin Buzzard (Feb 18 2020 at 14:20):

instance α_fin : fintype α := begin
  refine {elems := {α.A, α.B, α.C}, complete := _},
  intro,
  cases x; simp,
end

Kevin Buzzard (Feb 18 2020 at 14:21):

cases is your friend here. Given x : α where α is any inductive type, cases will split into a case for each constructor.

Matt Earnshaw (Feb 18 2020 at 14:22):

Kevin Buzzard said:

Maybe it's as simple as using set.range?

that might work once I have this fintype stuff working, the real block is having something that is finite, decidable etc.

Matt Earnshaw (Feb 18 2020 at 14:22):

Kevin Buzzard said:

cases is your friend here. Given x : α where α is any inductive type, cases will split into a case for each constructor.

perfect, thanks!

Kevin Buzzard (Feb 18 2020 at 14:23):

Using tactic mode to define data is something I instinctively avoid doing. It is probably not an issue here, but my instinct would be instance α_fin : fintype α := {elems := {α.A, α.B, α.C}, complete := λ x, by cases x; simp}.

Kevin Buzzard (Feb 18 2020 at 14:24):

The logic is that some tactics make terrifying terms which mean that it becomes hard to reason about the objects you've defined. I strongly suspect that refine is not one of them, but I am never sure. I do know that rw can really make things scary.

Kevin Buzzard (Feb 18 2020 at 14:25):

that might work once I have this fintype stuff working, the real block is having something that is finite, decidable etc.

Is your alphabet finite and decidable? You can just add these as assumptions of course.

Matt Earnshaw (Feb 18 2020 at 15:19):

Kevin Buzzard said:

Is your alphabet finite and decidable? You can just add these as assumptions of course.

not exactly sure what you mean

Matt Earnshaw (Feb 18 2020 at 15:20):

that sounds like it would suffice to prove some things abstractly, but I am working with particular alphabets

Kevin Buzzard (Feb 18 2020 at 16:10):

I just meant (Alphabet : Type) [decidable_eq Alphabet] [fintype Alphabet]

Matt Earnshaw (Feb 19 2020 at 12:32):

if I have a quotient on type X and a myset : set X, is there a way to turn the latter into a set X/~? Is quotient.lift myset ... on the right track?

Sebastien Gouezel (Feb 19 2020 at 12:58):

You want to take the image of a set under a function. This is done with the notation '', which is a shortcut for set.image.

Chris B (Feb 21 2020 at 23:04):

Is there a simple-ish answer for what Lean does internally with attributes? Also are there any community docs on how to define attributes? That part of the reference manual is blank. There's a one-liner in mathlib's commands.md doc that shows the literal syntax for defining a local attribute but that's about it.

Kevin Buzzard (Feb 22 2020 at 00:45):

There aren't that many attributes. A simp tag means "simp can use this", I think refl_lemna means dsimp can use it, ext means ext can use it, to_additive means that lean will try and generate an additive version of this multiplicative definition, there are some casty ones to do with norm_cast tactic and these are explained in the tactics doc, and I think that's most of the ones I know about

Kevin Buzzard (Feb 22 2020 at 00:47):

refl andsymm and transmean that the associated tactic can use them and they can also be used in calc proofs

Mario Carneiro (Feb 22 2020 at 00:48):

You can list all the attributes, there are a lot you don't know about

Mario Carneiro (Feb 22 2020 at 00:49):

#print attributes

Kevin Buzzard (Feb 22 2020 at 00:50):

Are most of them not for human consumption though? It's not as if I see these all over mathlib

Mario Carneiro (Feb 22 2020 at 00:51):

I'm sure they are zipf distributed

Bryan Gin-ge Chen (Feb 22 2020 at 01:30):

@Rob Lewis 's doc PR will put all documentation for custom attributes into a new file. The header for that file is still WIP in the corresponding doc-gen PR here, and that would be a good spot to add info about how attributes work.

Chris B (Feb 23 2020 at 03:00):

I meant more like "what does Lean do internally when it encounters an attribute" as opposed to what a particular attribute does, but looking closer at how they're defined in mathlib's tactics module it looks like it's pretty involved meta stuff, so I think therein lies my answer.

Kevin Buzzard (Feb 23 2020 at 08:09):

Attributes are just ways of tagging a definition and simp is written in C++ so in some cases it can be C++ code not Lean code which is using those tags

Rocky Kamen-Rubio (Feb 24 2020 at 04:17):

I keep getting these errors when I try to define a matrix. I have mathlib installed and working and already did import data.matrix.basic and import data.matrix.pequiv. I feel like I'm missing something obvious Screen-Shot-2020-02-23-at-11.14.34-PM.png Screen-Shot-2020-02-23-at-11.14.24-PM.png

def m : matrix (fin 2) (fin 2)  := {{1,2},{3,4}}

Alex J. Best (Feb 24 2020 at 04:22):

Did you copy this from https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Notation.20for.20matrices.20and.20vectors ? I don't think this way of defining matrices is standard at all.

Scott Morrison (Feb 24 2020 at 04:53):

(I'd recommend not using screenshots: it's worth your effort, and everyone's here, to prepare minimum working examples of the problems you're having. Generally, at least half the time while I'm trying to prepare an example to post here for help, the process of minimising makes me realise what I'm doing wrong.)

Kevin Buzzard (Feb 24 2020 at 07:23):

Yeah, screenshots are much harder to view on mobile. Post code!

Rocky Kamen-Rubio (Feb 24 2020 at 18:01):

Scott Morrison said:

(I'd recommend not using screenshots: it's worth your effort, and everyone's here, to prepare minimum working examples of the problems you're having. Generally, at least half the time while I'm trying to prepare an example to post here for help, the process of minimising makes me realise what I'm doing wrong.)

I'll keep that in mind going forward. I did post the code below the screenshot as well, but maybe got hidden because it was only one line. I do think I've relied maybe a little too heavily on screenshots here so I will be more conscientious of that. Thank you!

Alex J. Best said:

Did you copy this from https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Notation.20for.20matrices.20and.20vectors ? I don't think this way of defining matrices is standard at all.

I did! Looking back at that post I'm realizing that initially I thought that notation was standard and the post was about defining new notation for other operations. I got it to work using functions, but am still wondering if there's a better way to define a matrix with arbitrary values than defining a function like this

--Lambda definition. This seems ok if there's a functional relationship between the values x and y and the element at position (x,y)
def m : matrix (fin 5) (fin 5)  := λ x : (fin 5), (λ y : (fin 5), (10*x + y))

--inconvenient function definition. I know I could condense this down to a single lambda statement, but it still doesn't seem great if I have a list of values I want to turn into a matrix. Is this a necessary consequence of functional programming? I'm not seeing a more convenient "constructor " in mathlib/src/data/matrix

def r1 (y : (fin 3)) :  :=
  if (y = 1) then 1 else
  if (y = 2) then 2 else
  3

def r2 (y : (fin 3)):  :=
  if (y = 1) then 4 else
  if (y = 2) then 5 else
  6

def r3 (y : (fin 3)):  :=
  if (y = 1) then 7 else
  if (y = 2) then 8 else
  9

def c (x : (fin 3)) : (fin 3)   :=
if x = 1 then r1 else
if x = 2 then r2 else
r3

def myMatrix : matrix (fin 3) (fin 3)  := c

--EDIT: somewhat better way of representing a matrix but this still doesn't seem great.
def myMatrix2 : matrix (fin 3) (fin 3)  := λ (x y : fin 3),
if x = 1 then
  if y = 1 then 1 else
  if y = 2 then 2 else
  3
else if x = 2 then
  if y = 1 then 4 else
  if y = 2 then 5 else
  6
else
  if y = 1 then 7 else
  if y = 2 then 8 else
  9

Bryan Gin-ge Chen (Feb 24 2020 at 18:16):

Do you know about pattern-matching syntax?

def r1 : fin 3  
| 1, _⟩ := 1
| 2, _⟩ := 2
| _ := 3

Alex J. Best (Feb 24 2020 at 18:42):

You could define a little helper function like this:

import data.list.defs

variables {α : Type} [inhabited α]

def list_of_list_to_mat {n : } : list (list α)  (matrix (fin n) (fin n) α) :=
λ ll x y, (ll.inth (x)).inth (y)

#eval (list_of_list_to_mat [[1,2],[0,2]] : matrix (fin 2) (fin 2) )  1 0

Rocky Kamen-Rubio (Feb 24 2020 at 20:29):

Cool! This seems useful. Thank you!

Ethan Horsfall (Feb 29 2020 at 16:07):

for the logic and proof tutorial, when i run the code in visual studios it sometimes doesn't run and instead just says 'updating' indefinitely. This has happened on a few examples. The current one is
variables A B : Prop

example : A ∧ ¬ B → ¬ B ∧ A :=
assume h : A ∧ ¬ B,
show ¬ B ∧ A, from and.intro (and.right h) (and.left h)

While on my RHS under 'Lean messages' it says 'Updating'

Johan Commelin (Feb 29 2020 at 16:12):

Pro tip:

```lean
code like this gets highlighted
```

Johan Commelin (Feb 29 2020 at 16:12):

Concerning your question: how have you installed Lean?

Ethan Horsfall (Feb 29 2020 at 16:44):

I followed the instructions from 'logical verification in lean' and then enabled the extension in VS studios. When doing that one of the steps I think didn't work (or, I messed it up, which is probably the case as I have never used vs studios before). Some of the code is running however. I can always use the online browser tool for the moment

Kevin Buzzard (Feb 29 2020 at 16:58):

NB it's supposed to say "updating" indefinitely. This just means "if you press a button, I'll update"

Patrick Massot (Feb 29 2020 at 17:00):

We really really need to rephrase that "Updating". Every newbie gets confused by this message.

Kevin Buzzard (Feb 29 2020 at 17:03):

How about "ready"?

Bryan Gin-ge Chen (Feb 29 2020 at 17:56):

Maybe "Active" or "Live" makes more sense since Lean may or may not be busy processing stuff?

The intention of that button is to be able to pause / unpause the updating of the info view. This is useful if you're making changes to a slow proof and you want to keep the info view from refreshing over and over while you refer to something in the context.

Patrick Massot (Feb 29 2020 at 19:15):

Maybe the clearest thing would be to name the button rather than describing state. For instance "Freeze display"/"Unfreeze display"

Kevin Buzzard (Feb 29 2020 at 19:27):

The issue is not when expert users are editing a slow proof, the issue is when beginners think that Lean has hung because (a) it doesn't seem to be doing anything [because everything is working] and (b) it says "updating" in the top right [which makes it sound a lot like Lean is doing something]. This can happen in particular if a user has managed to click on the "display messages" button rather than the "display goal" button -- if all is well then "display messages" can be empty, so Lean just "sits there updating".

If we're in the business of changing stuff like this, can we also change the "Lean Messages" title of the "Display Goal" view to "Lean Goal"?

Bryan Gin-ge Chen (Feb 29 2020 at 19:33):

I agree completely. I'll make a PR later with some changes.

Bryan Gin-ge Chen (Feb 29 2020 at 19:54):

Would it actually even be better if we just removed the text next to the button altogether? The button already has a tooltip assigned to it that says "Stop updating" / "Continue updating", which I can change to "Freeze display" / "Unfreeze display".

Kevin Buzzard (Feb 29 2020 at 20:55):

Kids these days all speak some universal language of symbols, and that pause button symbol is certainly one of the standard symbols in this language, as is the green triangle which appears when you click it.

Patrick Massot (Feb 29 2020 at 20:55):

Yes, maybe removing the words would be enough.

Bryan Gin-ge Chen (Feb 29 2020 at 21:06):

:+1:

PR: https://github.com/leanprover/vscode-lean/pull/145

Reid Barton (Feb 29 2020 at 22:35):

you don't press ctrl-S to freeze the info window, and ctrl-Q to unfreeze it?

Reid Barton (Feb 29 2020 at 22:35):

Kids these days

Bryan Gin-ge Chen (Feb 29 2020 at 22:39):

Haha, you could bind both ctrl+S and ctrl+Q to lean.infoView.toggleUpdating for that old-school feel if you really wanted.

Bobby Lindsey (Mar 02 2020 at 03:21):

Hey guys, hoping I can contribute to this project. But having a hard time figuring out where (other than "issues" list in GitHub). E.g. this page of list of theorems that have been proved lead to 404 errors: https://github.com/leanprover-community/mathlib/blob/master/docs/theories.md. Recommendations for good places to start?

Yury G. Kudryashov (Mar 02 2020 at 03:24):

Which area of mathematics do you prefer?

Yury G. Kudryashov (Mar 02 2020 at 03:24):

There are plenty of holes in any area.

Yury G. Kudryashov (Mar 02 2020 at 03:26):

By "holes" I mean "not yet formalized definitions/theorems".

Bobby Lindsey (Mar 02 2020 at 03:26):

Probability, stats, or linear algebra. But not sure if all undergrad theorems/lemma have been implemented already... I only have a M.S. in math. I'd be happy to attempt to contribute elsewhere though if need be

Yury G. Kudryashov (Mar 02 2020 at 03:32):

We have some linear algebra and measure theory. There should be quite a few holes in our linear algebra library but I notice them only when I need them, so let's wait for someone who has better understanding of the current state of src/linear_algebra.

Mario Carneiro (Mar 02 2020 at 03:35):

Not sure what's up with the broken links. @Reid Barton you were the last one to touch this file, where have the functions.md et al files gone?

Reid Barton (Mar 02 2020 at 03:36):

The topology one isn't broken :upside_down:

Bobby Lindsey (Mar 02 2020 at 03:36):

Other than cross referencing linear algebra theorems/lemmas with those mentioned in textbooks, is there any better way to enumerate these holes? Better yet, an index of what has been proven would go a long way... perhaps this documentation is something I can help out with as well

Mario Carneiro (Mar 02 2020 at 03:37):

The enumeration of holes is difficult because the set of holes in mathlib is cofinite

Mario Carneiro (Mar 02 2020 at 03:40):

As for what has been proven, there are of course the source files themselves, which take some getting used to but are not an unreasonable way to familiarize yourself with the contents. But the mathlib docs are a less intimidating view on the source files

Yury G. Kudryashov (Mar 02 2020 at 03:42):

If you see a theorem that has a name, then adding a docstring with this name is definitely worth a PR.

Bobby Lindsey (Mar 02 2020 at 03:48):

Thanks @Mario Carneiro , I'll check those docs out. And thanks @Yury G. Kudryashov for the suggestion. I'll start combing through the linear_algebra module and cross-referencing its contents with theorems/lemmas in some textbooks I have, and try to fill in any documentation and missing theorems/lemmas along the way. Thanks guys :)

Sam Stites (Mar 02 2020 at 16:59):

I'm trying to say the following lemma: Let A be a ring with the following properties: for all rings R, there exists a unique homomorphism A → R. Then A ≅ ℤ.

I have a few attempts, but could someone help me formulate this? My first attempt looks like the following (It's basically how I would do this in haskell):

import algebra.ring
import logic.unique

variables (A : ring Type) (R : ring Type)
lemma lemma03_v0 : A → (∀ R → unique (A → R)) → (A ≅ ℤ) := sorry

Things that I think are wrong: \forall should be \exists and maybe there should only be one → before A ≅ ℤ? I still get a lot of unexpected tokens or invalid expressions, though.

Anne Baanen (Mar 02 2020 at 17:01):

To say "A is a ring", you can write variables (A : Type*) [ring A]. In Haskell terms, [ring A] can be interpreted as the typeclass constraint (Ring A) => ...

Anne Baanen (Mar 02 2020 at 17:03):

The next error is ∀ R → ...: the should be a , (something that I still mess up continually, coming from Agda)

Reid Barton (Mar 02 2020 at 17:07):

This kind of thing is easier using the bundled/category theory approach

import algebra.category.CommRing

variables (A : Ring.{0})
lemma lemma03_v0 : (Π (R : Ring.{0}), unique (A  R))  (A  Ring.of ) :=
sorry

Reid Barton (Mar 02 2020 at 17:17):

otherwise, I think it's

variables (A : Type) [ring A]
lemma lemma03_v0 : (Π (R : Type) [ring R], by exactI unique (ring_hom A R))  (A +* ) :=
sorry

Patrick Massot (Mar 02 2020 at 17:18):

Why would you use the Pi notation instead of forall here? I know they are the same to Lean, I'm asking about psychology

Reid Barton (Mar 02 2020 at 17:19):

Well, unique is data. Really it should be def too, and not lemma

Patrick Massot (Mar 02 2020 at 17:20):

Oh. I would have never suspected unique to be data.

Johan Commelin (Mar 02 2020 at 17:20):

Yeah, I should have called it cunique...

Johan Commelin (Mar 02 2020 at 17:21):

But now you have a chance to PR discrete_unique. Doesn't that make you feel happy?

Sam Stites (Mar 02 2020 at 17:23):

cool! thanks for all of this -- I'm running into some weird compilation stuff right now (probably need to clean up this experiment-repo).

Patrick Massot (Mar 02 2020 at 17:24):

The version without category stuff doesn't read nice, but I guess this is fair: the lemma is really category theoretic in flavor.

Patrick Massot (Mar 02 2020 at 17:24):

Sam, do you understand what exactI is doing?

Sam Stites (Mar 02 2020 at 17:27):

not at the moment (I was going to wait for matlib to rebuild before examining in depth). I am also wondering what the longer arrow is in your unique (A --> R) is.

Patrick Massot (Mar 02 2020 at 17:27):

Why are you rebuilding mathlib?

Patrick Massot (Mar 02 2020 at 17:28):

Reid's longer arrow is Hom in the category library.

Sam Stites (Mar 02 2020 at 17:28):

Oh! it's a tactic? I was about to start reading that chapter next.

Sam Stites (Mar 02 2020 at 17:28):

cool

Patrick Massot (Mar 02 2020 at 17:28):

Yes, exactI is a tactic, and not the easiest one to understand.

Patrick Massot (Mar 02 2020 at 17:28):

But first we need to make sure you don't need to rebuild mathlib. Why do you think you need to do that?

Sam Stites (Mar 02 2020 at 17:31):

ehh... it's a little silly -- I am using emacs for the first time (normally I use vim) and flycheck seems to be maxing out on errors. I think it's a caching issue since I was trying to use some certigrad files.

Patrick Massot (Mar 02 2020 at 17:31):

Lean is already difficult, don't add emacs difficulty on top of it if you are not a emacs user.

Patrick Massot (Mar 02 2020 at 17:32):

Why don't you use VScode?

Sam Stites (Mar 02 2020 at 17:32):

ah, it's working out fine, actually! that's not my main concern : )

Sam Stites (Mar 02 2020 at 17:33):

Yeah, so I've cleaned up the repo and rebuilt with leanpkg build -- the flycheck seems to be working perfectly fine now

Sam Stites (Mar 02 2020 at 17:34):

I'm at the point in my programming career where it's quite painful not to use vim and emacs has good support for this via spacemacs. It's self-inflicted pain, no need to worry about it!

Johan Commelin (Mar 02 2020 at 17:34):

VScode has a vim plugin

Johan Commelin (Mar 02 2020 at 17:34):

Both Patrick and I use it.

Johan Commelin (Mar 02 2020 at 17:35):

It doesn't have all the goodies of regular vim, but it's quite close.

Sam Stites (Mar 02 2020 at 17:35):

okay! I'll check it out as well

Patrick Massot (Mar 02 2020 at 17:36):

I still strongly advise you to use VScode. But let's go back to exactI because I have very little time before leaving my office (Paris time-zone). So the issue is ring_hom A R expects to find a (semi)-ring structure on A and R by type class search. The one on A will be found without problem, because it's there from the beginning. The one on R is mentioned too late in the game to be picked up if you don't ask Lean to search. That's what exactI is doing (asking Lean).

Patrick Massot (Mar 02 2020 at 17:37):

If you want to avoid that exactI you'll need to tell Lean to forget about how this semi-ring structure was meant to be found, and provide it yourself. This would be:

lemma lemma03_v0' :
( (R : Type) ( hR : ring R), ∃! φ : @ring_hom A R _ hR.to_semiring, true)  (A +* ) :=
sorry

which also has a silly way of saying there exists a unique φ with type @ring_hom A R _ hR.to_semiring.

Patrick Massot (Mar 02 2020 at 17:38):

Now I need to go. Have fun!

Sam Stites (Mar 02 2020 at 17:38):

thank you!

Kevin Buzzard (Mar 02 2020 at 18:10):

If you move the universal hypothesis before the colon, you don't need all this exactI stuff:

import data.equiv.algebra

variables (A : Type) [ring A]

lemma lemma03_v0' (huniv :  (R : Type) [ring R], ∃! φ : ring_hom A R, true) : (A +* ) :=
sorry

Kevin Buzzard (Mar 02 2020 at 18:29):

import data.equiv.algebra

variables (A : Type) [ring A]

noncomputable lemma lemma03_v0' (huniv :  (R : Type) [ring R], ∃! φ : ring_hom A R, true) : (A +* ) :=
{ to_fun := (classical.some (huniv )).to_fun,
  inv_fun := λ n, sorry, -- what's the map from ℤ to an arbitrary ring?
  left_inv := sorry,
  right_inv := sorry,
  map_mul' := (classical.some (huniv )).map_mul,
  map_add' := (classical.some (huniv )).map_add }

Once I figure out the name of the canonical map from the integers to an arbitrary ring, I think we're nearly done (the ring hom A -> A must be the identity by uniqueness).

Kevin Buzzard (Mar 02 2020 at 18:31):

Oh -- I hadn't seen the unique trick, that makes it cleaner.

Reid Barton (Mar 02 2020 at 18:31):

I hadn't realized the "left of the colon" would work that way in this context, thanks.

Kevin Buzzard (Mar 02 2020 at 18:32):

Funnily enough, I realised this myself just the other day when I was trying to formalise the assertion that if X×YYX\times Y\to Y was always closed then XX was compact.

Kevin Buzzard (Mar 02 2020 at 18:33):

Another thing I noticed was that if I restricted to YY in the same universe as XX then the result was actually stronger not weaker -- universe monomorphism FTW. The same is going on in here (possibly unintentionally)

Kevin Buzzard (Mar 02 2020 at 18:34):

import data.equiv.algebra

variables (A : Type) [ring A]

lemma lemma03_v0' (huniv :  (R : Type) [ring R], unique (ring_hom A R)) : (A +* ) :=
{ to_fun := (huniv ).default.to_fun,
  inv_fun := λ n, sorry, -- what's the map from ℤ to an arbitrary ring?
  left_inv := sorry,
  right_inv := sorry,
  map_mul' := (huniv ).default.map_mul,
  map_add' := (huniv ).default.map_add }

Yury G. Kudryashov (Mar 02 2020 at 18:34):

I don't think that it's stronger because you can't say "for all Y from all universes"

Kevin Buzzard (Mar 02 2020 at 18:35):

Yes but this is an input, not an output, so if I know it for all Y in all universes, I can deduce it for all Y in the same universe as X, which is all I need.

Yury G. Kudryashov (Mar 02 2020 at 18:35):

So, if you use (X : Type u) (Y : Type v), then it says "if for all Y from some universe v (possibly not u), then ..."

Kevin Buzzard (Mar 02 2020 at 18:37):

Yes.

Yury G. Kudryashov (Mar 02 2020 at 18:37):

And this does not immediately imply "for all Y : Type u"

Kevin Buzzard (Mar 02 2020 at 18:37):

Yes.

Kevin Buzzard (Mar 02 2020 at 18:38):

But u might equal v

Kevin Buzzard (Mar 02 2020 at 18:38):

so in this case, I win (and I agree that usually I lose)

Yury G. Kudryashov (Mar 02 2020 at 18:38):

So, the result with (X : Type u) (Y : Type v) is stronger.

Kevin Buzzard (Mar 02 2020 at 18:38):

No

Kevin Buzzard (Mar 02 2020 at 18:39):

because I am demanding more from my input and only getting the same for my output

Kevin Buzzard (Mar 02 2020 at 18:40):

The point is exactly that this is not def f (X : Type u) (Y : Type v) ...

Kevin Buzzard (Mar 02 2020 at 18:41):

I'm saying "assume that something is true for all Y in the same universe as X. Then P(X)", which is a better thing to do than saying "assume that something is true for all Y in all universes, including X's and others. Then P(X)"

Yury G. Kudryashov (Mar 02 2020 at 18:41):

Try this:

universes v u
constant P : Type u  Type v  Prop
axiom L1 (X : Type u) (Y : Type v) : P X Y
lemma L2 (X Y : Type u) : P X Y := L1 X Y

Kevin Buzzard (Mar 02 2020 at 18:42):

Your examples are not a good mirror of what is going on here.

Kevin Buzzard (Mar 02 2020 at 18:42):

Read the statement of the lemma.

Yury G. Kudryashov (Mar 02 2020 at 18:43):

OK, which lemma are we talking about?

Kevin Buzzard (Mar 02 2020 at 18:43):

lemma lemma03_v0' (A : Type) [ring A] (huniv : ∀ (R : Type) [ring R], unique (ring_hom A R)) : (A ≃+* ℤ) := ...

Kevin Buzzard (Mar 02 2020 at 18:44):

If I let R : Type u then huniv is harder to verify, but the result is still the same.

Kevin Buzzard (Mar 02 2020 at 18:45):

So it seems to be a weird case where less polymorphism is better. I noticed this last week when I was proving that if X×YYX\times Y\to Y is closed for all YY then XX is compact.

Yury G. Kudryashov (Mar 02 2020 at 18:45):

Ah, it should be

universes v u
constant p : Type u  Type v  Prop
constant q : Type u  Prop
axiom L1 (X : Type u) (H :  Y : Type v, p X Y) : q X
lemma L2 (X : Type u) (H :  Y : Type u, p X Y) : q X := L1 X H

Kevin Buzzard (Mar 02 2020 at 18:46):

I was trying to prove this for all YY in all universes, and then I ran into some issue with universes, so then I thought "crap I am going to have to restrict to YY in the same universe as XX" and then I thought "oh wait, that's a stronger result!"

Kevin Buzzard (Mar 02 2020 at 18:47):

wait -- so you're still saying I'm wrong? :-/

Yury G. Kudryashov (Mar 02 2020 at 18:53):

Actually L1 is not one axiom but a series of axioms enumerated by v and u.

Kevin Buzzard (Mar 02 2020 at 18:56):

I completely see that your code compiles. However what is wrong with the argument that says the following: I have X and I want to prove q X. Now X is in a universe u. Would I rather prove H X Y for all Y in all universes v, or would I rather prove H X Y in the easier case that Y is in universe u as well? Clearly I would rather stick to Y in universe u, so L2 is a more useful lemma than L1 because it applies more widely.

Reid Barton (Mar 02 2020 at 18:57):

Kevin you're comparing L2 with a hypothetical L2' that can't be expressed in Lean, but would look something like (X : Type u) (H : forall v (Y : Type v), p X Y) -> q X

Reid Barton (Mar 02 2020 at 18:58):

And you're correct that L2 is stronger than L2'

Johan Commelin (Mar 02 2020 at 18:58):

Apparently in Lean it's not forall v but exists v

Kevin Buzzard (Mar 02 2020 at 18:58):

L1?

Reid Barton (Mar 02 2020 at 18:58):

Yury's L1 becomes L2 when you specialize it to v = u

Johan Commelin (Mar 02 2020 at 18:58):

Unless you require v = u

Reid Barton (Mar 02 2020 at 18:59):

Probably L1 is false in this setting if you allow v < u. You would only be able to show compactness for v-sized filters, roughly

Reid Barton (Mar 02 2020 at 19:00):

The other thing going on here is that in the implication in the other direction, you can state it for all v (because now v is at the outer level) and it is true.

Reid Barton (Mar 02 2020 at 19:01):

So for X : Top.{u}, you have "X is u-universally closed" => "X is compact" => "for any v, X is v-universally closed"

Reid Barton (Mar 02 2020 at 19:01):

and in that sense you've proved a stronger statement than you expected

Kevin Buzzard (Mar 02 2020 at 19:06):

No wonder I was having trouble proving it in the fully polymorphic setting! I wanted to let Y be this option X with a filter topology.

Reid Barton (Mar 02 2020 at 19:08):

To take a slightly silly extreme case, it's clearly not true if you only allow Y to range over Prop/subsingletons

Kevin Buzzard (Mar 02 2020 at 19:09):

That was surprisingly interesting/insightful. So back to the original question -- if we make the construction fully universe polymorphic then it seems to me that things get a bit more tedious because I have to start using ulift to get the map from Z\mathbb{Z} to A

Reid Barton (Mar 02 2020 at 19:12):

Yeah, I stuck to Type 0 in order to avoid this ulift stuff. In the "category theory" version it will appear already in the statement, but even in the unbundled version it will appear in the proof.

Kevin Buzzard (Mar 02 2020 at 19:31):

import data.equiv.algebra algebra.group_power

variables (A : Type) [ring A]

open_locale add_group -- breakthrough

lemma lemma03_v0' (huniv :  (R : Type) [ring R], unique (ring_hom A R)) : (A +* ) :=
{ to_fun := (huniv ).default.to_fun,
  inv_fun := λ n, n  1,
  left_inv := sorry,
  right_inv := sorry,
  map_mul' := (huniv ).default.map_mul,
  map_add' := (huniv ).default.map_add }

Kevin Buzzard (Mar 02 2020 at 19:39):

Is this not a thing in mathlib?

def of_int (A : Type*) [ring A] :  →+* A :=
{ to_fun := λ n, n  1,
  map_one' := by simp,
  map_mul' := λ x y, show (x * y)  (1 : A)= x  1 * (y  1), by simp [gsmul_eq_mul],
  map_zero' := zero_gsmul _,
  map_add' := λ _ _, add_gsmul _ _ _ }

Mario Carneiro (Mar 02 2020 at 21:17):

That's int.cast

Mario Carneiro (Mar 02 2020 at 21:17):

not sure if it's been bundled yet

Kevin Buzzard (Mar 02 2020 at 21:18):

Thanks. I was pretty sure I'd seen it before.

Mario Carneiro (Mar 02 2020 at 21:18):

also known as the coercion from int to a ring

Kevin Buzzard (Mar 02 2020 at 22:00):

import data.equiv.algebra algebra.group_power

variables (A : Type) [ring A]

def of_int :  →+* A := ring_hom.of coe

theorem eq_of_int (φ :  →+* ) (a : ) : φ a = a := @int.induction_on' (λ x, φ x = x) a 0
   φ.map_zero (λ k _ hk, by rw [φ.map_add, hk, φ.map_one]) (λ k _ hk, by rw [φ.map_sub, hk, φ.map_one])

lemma eq_int_of_unique_hom (huniv :  (R : Type) [ring R], unique (ring_hom A R)) : (A +* ) :=
{ to_fun := (huniv ).default,
  inv_fun := of_int A,
  left_inv := begin
    intro x,
    show _ = (ring_hom.id A) x,
    have h1 := (huniv A).uniq (ring_hom.id A),
    have h2 := (huniv A).uniq ((of_int A).comp (huniv ).default),
    rw h1 at h2,
    rw h2,
    refl,
  end,
  right_inv := λ n, eq_of_int ((huniv ).default.comp (of_int A)) n,
  map_mul' := (huniv ).default.map_mul,
  map_add' := (huniv ).default.map_add }

Kevin Buzzard (Mar 02 2020 at 22:05):

I hadn't found the coercion earlier because I didn't have the right import. Kind of annoying that I don't know how to say "hey Lean, I want a coercion from int to a ring, go find me one in mathlib". All I knew was that when I tried it, it didn't work.

Kevin Buzzard (Mar 02 2020 at 22:06):

I thought data.equiv.algebra would give me everything ringy, but it was in data.int.basic. I guess this just shows that I don't understand the import tree well enough. I would have guessed integers were more fundamental than ring stuff (probably because I learnt them earlier in my mathematical education)

Bobby Lindsey (Mar 02 2020 at 23:48):

How are we using Kolmogorov axioms for probability theorems? For example, what would lean code look like for P(empty set) = 0. Do we have to appeal to measury theory instead? Sorry, still trying to find my way around this project... but I'm trying to write all probability and stats theorems/lemmas in an undergrad textbook with lean.

Yury G. Kudryashov (Mar 02 2020 at 23:50):

As far as I understand, we don't plan to have a separate probability theory. Just deal with a measure space of total measure one.

Bobby Lindsey (Mar 02 2020 at 23:52):

Would anyone think it'd be useful? I know I would have loved to have something like this while going through probability theory as a student - if anything just to exercise proof-solving skills. But many students who take probability theory don't go on to take measure theory (unless they're math majors).

Bryan Gin-ge Chen (Mar 03 2020 at 00:00):

We're not currently suffering from an overabundance of expository Lean files (to say the least), so yes, I think it'd be very useful!

Bobby Lindsey (Mar 03 2020 at 00:03):

@Bryan Gin-ge Chen :) any suggestions for where to put it in the mathlib project? Like maybe src/probability_theory? Or should expository stuff go somewhere else

Scott Morrison (Mar 03 2020 at 00:04):

Well... it's not exactly clear what place there is for purely "expository" stuff at all.

Scott Morrison (Mar 03 2020 at 00:05):

We do have the docs/tutorial/ folder, which I think is a great place to show people how to do "standard" / "undergraduate" stuff using the highbrow API that Lean tends to provide.

Bobby Lindsey (Mar 03 2020 at 00:10):

@Scott Morrison Cool, thanks for the suggestion. Now to figure out how to create all these axioms and whatnot...

Bryan Gin-ge Chen (Mar 03 2020 at 00:15):

It really depends what you end up formalizing. Stuff that will be useful for future developments shouldn't (only) be in docs/tutorial and should go in something like src/probability_theory. I wouldn't worry about it too much now though.

Bobby Lindsey (Mar 03 2020 at 00:50):

Sooo.. I might be out of my depth here. I'm trying to figure out how to define a sample space to start building Kolmogorov axioms but have no idea how to fit this into LEAN's type theory. I'm afraid I've bit off more than I can chew :( How did you experts bootstrap this learning curve?

Mario Carneiro (Mar 03 2020 at 00:56):

I think it might help to focus your question a bit more. What did you try and how did you get stuck?

Mario Carneiro (Mar 03 2020 at 00:58):

Regarding probability theory, I think we have some things about measure_space and possibly probability_space if @Koundinya Vajjha PR'd his stuff

Mario Carneiro (Mar 03 2020 at 01:00):

If you look at the definition of outer_measure, measure and measure_space you will get an idea how we do things like P(empty set) = 0. (hint: we write P ∅ = 0)

Scott Morrison (Mar 03 2020 at 02:03):

You might also tell us your background: have you tried formalising anything simpler yet? Have you played the natural numbers game, and/or read some of Theorem Proving in Lean?

Bobby Lindsey (Mar 03 2020 at 02:24):

Haven't formalized anything simple yet - currently working through the natural numbers game. Love it so far. I'm probably getting ahead of myself though which is why looking at formalizing Kolmogorov axioms seems so daunted to me atm :/.

Bobby Lindsey (Mar 03 2020 at 14:27):

How would I go about defining a probability space in LEAN? This would be some probability triple but what data type would a sample space belong to? I started trying to define the first axioms like def prob_event_nonnegative (E: set ℝ) := { x : ℝ | x ≥ 0} and def prob_sample_space_is_one (Ω: ??) := but I soon realized I don't have a probability space defined. Not sure if I'm on the right track or not, just trying to get to a point where I can start writing prob 101 theorems and solving them

Johan Commelin (Mar 03 2020 at 14:41):

I'm not a probability theorist, so I don't know the maths definitions. Is a probability space just a measure space such that the measure of the total space is 1?

Bobby Lindsey (Mar 03 2020 at 14:49):

Yeah, that's right. I'd like to define it separately so as to assume no knowledge of measure theory though, but having a hard time as to how it should be typed.

Reid Barton (Mar 03 2020 at 14:53):

Do you know about structures in general?

Bobby Lindsey (Mar 03 2020 at 15:00):

I do not. Just found it in the docs though :)

Reid Barton (Mar 03 2020 at 15:01):

If the definitions of measure space and probability space have 90% overlap, then someone interested in probability spaces cannot really have no knowledge of measure theory

Reid Barton (Mar 03 2020 at 15:03):

quite aside from the practical advantages of reusing the existing formalization (no reinvention of wheels, near-duplication of proofs, etc)

Reid Barton (Mar 03 2020 at 15:08):

Of course if your goal is to reinvent wheels and reformalize proofs for learning purposes, that's another matter

Bobby Lindsey (Mar 03 2020 at 15:13):

It's mostly for learning purposes. I'm taking the viewpoint of a student who takes probability theory, like an engineer, but does not go on to taking measure theory.

Reid Barton (Mar 03 2020 at 15:33):

There is "measure theory" and then there is the definition of a measure space. A student who missed the lecture on the axiom "the probability of the whole space is 1" knows the definition of a measure space. (Okay, maybe not quite since a measure can also take the value ++\infty.)

Arjun Pitchanathan (Mar 03 2020 at 15:50):

Maybe it would be interesting to formalize the proofs of the Markov and Chebyshev inequalities. As far as I can tell they don't exist in mathlib already.

Koundinya Vajjha (Mar 04 2020 at 02:47):

I have the Markov+Chebyshev inequalities proven in another repository. I really should PR them. Feel free to build off of these, in any case.

https://github.com/jtristan/stump-learnable/blob/21358ffd6746d838f76b6d1a275d361d5ea66125/src/lib/attributed/to_mathlib.lean#L154

Daniel Keys (Mar 12 2020 at 21:37):

Here's a stripped-down version of some proofs I need:

def X : set  := { n | n^2 < 10 }

lemma Xexplicit : {0, 1, 2, 3}  X :=
begin
  dsimp,
  have h1 : 1 ^2 < 10, norm_num,
  sorry,
end

While it's trivial to prove all the elements in the enumerated set satisfy the condition for X-membership, as shown for 1, how does one put things like these together to obtain the result? Do I need to revert to something simpler, like n ∈ X \iff n = 0 \or n = 1 \or ...? In that case things can get a little ugly for larger sets.

Kevin Buzzard (Mar 12 2020 at 21:39):

I'm not sure this is true, because I think means but not =

Kevin Buzzard (Mar 12 2020 at 21:43):

import tactic

def X : set  := { n | n^2 < 10 }

lemma Xexplicit : {0, 1, 2, 3}  X :=
begin
  simp [X, set.subset_def],
  intros x hx,
  repeat {cases hx}; exact dec_trivial
end

Daniel Keys (Mar 12 2020 at 21:43):

Actually I need that to prove equality, so I need inclusion both sides.

Daniel Keys (Mar 12 2020 at 21:43):

I edited to have \subseteq.

Daniel Keys (Mar 12 2020 at 21:44):

Thank you!

Kevin Buzzard (Mar 12 2020 at 21:45):

Confession: @Chris Hughes suggested the first line, but then wanted to finish with finish and we couldn't get it to work, hence the hack

Daniel Keys (Mar 12 2020 at 21:46):

Anyway much shorter than what I was contemplating.

Yury G. Kudryashov (Mar 13 2020 at 00:04):

BTW, what do you think about using subsetneq instead of subset?

Yury G. Kudryashov (Mar 13 2020 at 00:06):

This will make statements more readable

Kevin Buzzard (Mar 13 2020 at 00:12):

I never use (\ssub) in my work or my lectures, because it is one of these ambiguous symbols used by some people to mean one thing, and used by others to mean something else. But then again I never use \nat either, for the same reason. The counter-argument is that if mathlib decides to stick to a convention and advertises that in some sort of "notation" page, this would perhaps suffice. I don't think →ₗ, etc are particularly readable either (but this is a little different because perhaps there is no standard notation for them?)

Mario Carneiro (Mar 13 2020 at 02:34):

I think you can do better than that:

lemma Xexplicit : {0, 1, 2, 3}  X :=
by simp [X, set.insert_subset]; exact dec_trivial

Mario Carneiro (Mar 13 2020 at 02:35):

insert_subset should just be a simp lemma, I don't think there are any downsides to triggering it

Daniel Keys (Mar 14 2020 at 20:41):

Trying some basic analysis stuff on my own:

def unbounded_above (A : set ) :=  x : , x > 0   a  A, x < a

then I want to prove, among others, unbounded_above ℕ. So I need to coerce the whole of nat as set of real. Can that be done?

Kevin Buzzard (Mar 14 2020 at 20:42):

I guess it would be something like coe '' nat and you'll need import data.set.basic

Kevin Buzzard (Mar 14 2020 at 20:42):

wait that's not quite right

Kevin Buzzard (Mar 14 2020 at 20:43):

set.range (coe : nat -> real)

Kevin Buzzard (Mar 14 2020 at 20:44):

Why not just ∀ x : ℝ, ∃ a ∈ A, x < a?

Daniel Keys (Mar 14 2020 at 20:52):

Yeap, this works:

lemma nats_unbounded_above : unbounded_above ( set.range (coe :  -> ) ) := sorry

Thank you! Agreed, I could get rid of the x>0 here; trying to show equivalence with a version of the Archimedes principle where that will be helpful though (keep 1/x in the nats instead of ints).

Kevin Buzzard (Mar 14 2020 at 20:53):

You could also use {x : ℝ | ∃ n : ℕ, x = n}

Kevin Buzzard (Mar 14 2020 at 20:54):

the equality is between two terms of different types. Lean reads the left hand side first and decides this is an equality between reals, and then coerces the natural automatically

Daniel Keys (Mar 14 2020 at 20:54):

Oh wait! That's exactly the same thing, right?

Kevin Buzzard (Mar 14 2020 at 20:55):

{x : ℝ | ∃ n : ℕ, x = ↑n} also works. They're the same sets in the sense that they have the same elements, and they might well be defeq

Kevin Buzzard (Mar 14 2020 at 20:55):

oh apparently they're not :-/

Kevin Buzzard (Mar 14 2020 at 20:57):

the range is defeq to {x : ℝ | ∃ n : ℕ, (n : ℝ) = x}

Daniel Keys (Mar 15 2020 at 01:10):

Can someone help me at this stage?

import data.real.basic
def unboundedAbove (A : set ) :=  x : , x > 0   a  A, x < a
def archimPrinciple :=  x : , x > 0    n : , (1/n : ) < x
lemma nats_unboundedAbove :
    unboundedAbove {x :  |  n : , x = n}  archimPrinciple :=
begin
    split,
    intros unb x hx,
    set A := {x :  |  n : , x = n} with hA,
    have h1x : (1/x) > 0, from one_div_pos_of_pos hx,
    have h1 := unb (1/x) h1x,
    cases h1 with nx hnx,
    cases hnx with h1 h2,
    -- existsi nx,  -- this won't work because nx is real, although it should be nat as well
    -- but I don't know how to get that from h1
end

Chris Hughes (Mar 15 2020 at 01:42):

You can use ceil nx to find the next natural number after nx

Mario Carneiro (Mar 15 2020 at 01:45):

I think that trivializes the statement though

Bryan Gin-ge Chen (Mar 15 2020 at 01:45):

I think you want something like this?

import data.real.basic
def unboundedAbove (A : set ) : Prop :=  x : , x > 0   a  A, x < a
def archimPrinciple : Prop :=  x : , x > 0    n : , (1/n : ) < x
lemma nats_unboundedAbove :
  unboundedAbove {x :  |  n : , x = n}  archimPrinciple :=
begin
  split,
  { intros unb x hx,
    set A := {x :  |  n : , x = n} with hA,
    have h1x : (1/x) > 0, from one_div_pos_of_pos hx,
    have h1 := unb (1/x) h1x,
    rcases h1 with nx, n, hn, h2,
    use n,
    sorry, },
  { sorry, },
end

Mario Carneiro (Mar 15 2020 at 01:47):

you can use rfl instead of hn

Daniel Keys (Mar 15 2020 at 01:47):

@Bryan Gin-ge Chen Right, I didn't know how to work with the set membership relation to get it.

Daniel Keys (Mar 15 2020 at 01:48):

Thank you!

Daniel Keys (Mar 15 2020 at 15:22):

Another little bump in the other direction apparently. Does anyone see a nice way to go past that existsi line in a nice way that wouldn't need a proof for h2n?

import data.real.basic

def unboundedAbove (A : set ) :=  x : , x > 0   a  A, x < a
def archimPrinciple :=  x : , x > 0    n : , (1/n : ) < x
lemma nats_unboundedAbove_eq_arc :
    unboundedAbove {x :  |  n : , x = n}  archimPrinciple :=
begin
    split,
    -- left-right implication
    {sorry, -- this works
    },
    -- right-left implication
    intros arc x hx,
    have h1x : (1/x) > 0, from one_div_pos_of_pos hx,
    have h1 := arc (1/x) h1x,
    cases h1 with n hn,
    use n, split,
    existsi n, refl,      -- any nice solution from here?
    set xn :  := n with hxn,
    have h2n : 0 < xn, from sorry,    -- without a need for this h2n?
    exact lt_of_one_div_lt_one_div h2n hn,    -- wants h2n with xn, not ↑n --??
    done
end

Kevin Buzzard (Mar 15 2020 at 15:59):

I don't think the goal is solvable. n can be zero.

Kevin Buzzard (Mar 15 2020 at 15:59):

1/0=0 in Lean

Daniel Keys (Mar 15 2020 at 17:04):

You're right. I need to restrict my naturals to >=1 only, for this to work.

Kevin Buzzard (Mar 15 2020 at 17:17):

Aah, the good old British naturals.

Daniel Keys (Mar 15 2020 at 17:37):

Also the ones I grew up with. Base case for proof by induction was n=1 in my young years.

Viktoriya Malyasova (Mar 16 2020 at 06:52):

Hi everyone! I've worked through first 7 chapters of "Theorem proving in lean", but can't understand how pattern matching works, e.g. how to solve the first exercise in chapter 8. How do you pattern-match an expression like ∀ (b : β), ∃ (a : α), f a = b? Can I find some similar examples somewhere? In the chapter there are no examples with a universal quantifier.

Kenny Lau (Mar 16 2020 at 06:55):

bnot_bnot is an example with a universal quantifier

jack (Mar 16 2020 at 12:17):

Is anybody familiar with first order login in LEAN?

Johan Commelin (Mar 16 2020 at 12:17):

See Flypitch

Johan Commelin (Mar 16 2020 at 12:17):

https://github.com/flypitch/flypitch

Johan Commelin (Mar 16 2020 at 12:18):

Corollary: @Floris van Doorn and @Jesse Michael Han are familiar with FOL in Lean

Johan Commelin (Mar 16 2020 at 12:18):

(There might be others :wink:)

jack (Mar 16 2020 at 12:18):

Thanks Johan, but im doing my homework :(

Johan Commelin (Mar 16 2020 at 12:19):

Aha... so what is your question?

jack (Mar 16 2020 at 12:19):

How to prove ¬ (∀ x, P x) → (∃ y, ¬ P y) :=

jack (Mar 16 2020 at 12:19):

I'm stucking at how to deal with \not \forall.

Johan Commelin (Mar 16 2020 at 12:19):

Do you have mathlib?

jack (Mar 16 2020 at 12:20):

nope

Johan Commelin (Mar 16 2020 at 12:20):

jack said:

I'm stucking at how to deal with \not \forall.

Aha, there must be a lemma called just like that not_forall.

Johan Commelin (Mar 16 2020 at 12:20):

Does #check classical.not_forall give you output?

jack (Mar 16 2020 at 12:22):

open classical
#check classical.not_forall

gives me unknown identifier 'classical.not_forall'

Johan Commelin (Mar 16 2020 at 12:22):

Hmmm, that's probably already mathlib

jack (Mar 16 2020 at 12:23):

I'm writing my homework at https://leanprover.github.io/live/latest/

jack (Mar 16 2020 at 12:23):

Noob for LEAN.

Johan Commelin (Mar 16 2020 at 12:27):

Have you tried to use classical.by_contradiction?

jack (Mar 16 2020 at 12:31):

I'm supposed to use that, from the description of the question.

jack (Mar 16 2020 at 12:32):

Actually I have no idea how to use it.

Johan Commelin (Mar 16 2020 at 12:33):

Are you allowed to use tactic mode?

Johan Commelin (Mar 16 2020 at 12:33):

begin ... end blocks?

Johan Commelin (Mar 16 2020 at 12:33):

Or are you forced to hand in term-mode proofs?

jack (Mar 16 2020 at 12:33):

yes!

Johan Commelin (Mar 16 2020 at 12:34):

Yes to what?

jack (Mar 16 2020 at 12:34):

In the first saw I was thinking that such a LEAN is so stupid, because I have to write the proof by hand.

jack (Mar 16 2020 at 12:34):

yes with begin..end

Johan Commelin (Mar 16 2020 at 12:35):

Ok, so

  assume H,
  apply classical.by_contradiction,

Johan Commelin (Mar 16 2020 at 12:35):

That should get you started.

Johan Commelin (Mar 16 2020 at 12:37):

Just follow your nose. If you get stuck, write

  apply classical.by_contradiction,

Johan Commelin (Mar 16 2020 at 12:37):

If you have the feeling that you are going in circles, follow your other nose :stuck_out_tongue_wink:

jack (Mar 16 2020 at 12:39):

But I still have no idea how to use apply .... I have never seen that before in the class.

jack (Mar 16 2020 at 12:39):

Actually the question is quite stupid..

Johan Commelin (Mar 16 2020 at 12:40):

Aah, so maybe they don't use begin ... end in class?

Johan Commelin (Mar 16 2020 at 12:40):

Because after assume/intro I would think that apply is the next tactic that you learn.

jack (Mar 16 2020 at 12:41):

Just begin a section and then end that.

Johan Commelin (Mar 16 2020 at 12:42):

Ok, so no tactic mode.

jack (Mar 16 2020 at 12:42):

I remember it's section..end not begin..end. Sorry my fault.

Johan Commelin (Mar 16 2020 at 12:42):

In that case, start with

assume H, classical.by_contradiction $
_

Johan Commelin (Mar 16 2020 at 12:43):

Put your cursor on the _ to see the updated goal state.

jack (Mar 16 2020 at 12:45):

It always complains
```
type mismatch, term
λ (s1 : ¬∀ (x : U), P x) (a : U) (s2 : ¬P a), by_contradiction ?m_2[s1, a, s2]
has type
Π (s1 : ¬∀ (x : U), P x) (a : U) (s2 : ¬P a), ?m_1[s1, a, s2]
but is expected to have type
(¬∀ (x : U), P x) → (∃ (y : U), ¬P y)


jack (Mar 16 2020 at 12:47):

I guess the fist step must be assume h : ¬ ∀ x, P x

Johan Commelin (Mar 16 2020 at 12:47):

example {X} (P : X  Prop) : ¬ ( x, P x)  ( y, ¬ P y) :=
assume h, classical.by_contradiction $
_

Doesn't complain for me.

jack (Mar 16 2020 at 12:51):

Then I want assume (¬∃ (y : U), ¬P y) :anguished: just next line ?

Johan Commelin (Mar 16 2020 at 12:52):

assume h', _

jack (Mar 16 2020 at 12:53):

type mismatch, term
λ (h : ¬∀ (x : U), P x), by_contradiction
has type
Π (h : ¬∀ (x : U), P x), (¬?m_1[h] → false) → ?m_1[h]
but is expected to have type
(¬∀ (x : U), P x) → (∃ (y : U), ¬P y)

Johan Commelin (Mar 16 2020 at 12:53):

Please post your code

Johan Commelin (Mar 16 2020 at 12:54):

You have a syntax error, but I can't guess it from here

Reid Barton (Mar 16 2020 at 12:54):

by_contradiction needs an argument. You can put _ for now

jack (Mar 16 2020 at 12:58):

It gives out

jack (Mar 16 2020 at 12:58):

h : ¬∀ (x : X), P x
⊢ (¬∃ (y : X), ¬P y) → false

So what's next :cold_sweat:

Johan Commelin (Mar 16 2020 at 12:59):

No

Johan Commelin (Mar 16 2020 at 12:59):

Post all your code

Johan Commelin (Mar 16 2020 at 12:59):

Not the goal state

Johan Commelin (Mar 16 2020 at 12:59):

I already told you what is next

jack (Mar 16 2020 at 12:59):

example {X} (P : X → Prop) : ¬ (∀ x, P x) → (∃ y, ¬ P y) :=
assume h, classical.by_contradiction $
_

Johan Commelin (Mar 16 2020 at 12:59):

Replace the _ with

assume H, _

Johan Commelin (Mar 16 2020 at 13:00):

After that, put your cursor on the _ and see what the goal state is.

Johan Commelin (Mar 16 2020 at 13:00):

(By the way, which course is this?)

jack (Mar 16 2020 at 13:00):

OK I got you. One question please, what does $ mean?

Johan Commelin (Mar 16 2020 at 13:01):

foo $ bar x y z is the same as foo (bar x y z)

Johan Commelin (Mar 16 2020 at 13:01):

It saves you some parentheses

jack (Mar 16 2020 at 13:01):

Logic and Computation

Johan Commelin (Mar 16 2020 at 13:01):

Uni?

jack (Mar 16 2020 at 13:01):

Birmingham

Johan Commelin (Mar 16 2020 at 13:01):

Which?

jack (Mar 16 2020 at 13:03):

So difficult to me :(, especially the LEAN part.

Johan Commelin (Mar 16 2020 at 13:03):

So, what does the next goal state say?

jack (Mar 16 2020 at 13:04):

h1 : ¬∀ (x : X), P x,
h2 : ¬∃ (y : X), ¬P y
⊢ false

Johan Commelin (Mar 16 2020 at 13:04):

Great, so what do you do?

jack (Mar 16 2020 at 13:04):

assume t : X and specify h2?

Johan Commelin (Mar 16 2020 at 13:05):

Nope, you can't assume at this point

Johan Commelin (Mar 16 2020 at 13:05):

You've got the h2 by contradiction, so using that right now would undo all the hard work

jack (Mar 16 2020 at 13:05):

~Why not?~

Johan Commelin (Mar 16 2020 at 13:06):

Because assume only works if your goal is of the form X -> Y

Johan Commelin (Mar 16 2020 at 13:06):

But false isn't like that.

jack (Mar 16 2020 at 13:07):

Then what about exists.intro?

Johan Commelin (Mar 16 2020 at 13:07):

No, that only works if your goal is of the form \exists x, ...

Johan Commelin (Mar 16 2020 at 13:07):

You need to build a term of type false.

Johan Commelin (Mar 16 2020 at 13:08):

So if you have a function of the form h1 : foobar -> false, then you could use that.

Johan Commelin (Mar 16 2020 at 13:08):

You happen to have exactly such an h1 in you list of assumptions, only the -> false is hidden by notation.

jack (Mar 16 2020 at 13:09):

Teacher says ¬ A is A → false

Johan Commelin (Mar 16 2020 at 13:09):

So I suggest that you write h1 $ _ in place of the final underscore.

Johan Commelin (Mar 16 2020 at 13:09):

jack said:

Teacher says ¬ A is A → false

Exactly!

jack (Mar 16 2020 at 13:10):

The goal moves to ∀ (x : X), P x. Does that mean I can assume t : X now?

Johan Commelin (Mar 16 2020 at 13:11):

Yep, now you are in good shape!

Johan Commelin (Mar 16 2020 at 13:11):

Replace the final underscore by assume x, _

Johan Commelin (Mar 16 2020 at 13:12):

After that, you'll need to build a term of type P x.

Johan Commelin (Mar 16 2020 at 13:12):

Or P t, if you wrote assume t

jack (Mar 16 2020 at 13:12):

Now I should elim exists..

Johan Commelin (Mar 16 2020 at 13:12):

How?

jack (Mar 16 2020 at 13:13):

image.png

Johan Commelin (Mar 16 2020 at 13:13):

That only works if you have h2' : \exists y, ... as an assumption. But you don't.

Johan Commelin (Mar 16 2020 at 13:13):

Your h2 has a \not in front of the exists.

jack (Mar 16 2020 at 13:14):

I have \not \exists ..., just like h1 in the example.

Johan Commelin (Mar 16 2020 at 13:14):

Sure, but the example doesn't use exists.elim either.

jack (Mar 16 2020 at 13:15):

Then it must be exists.intro :yum:

Johan Commelin (Mar 16 2020 at 13:15):

Well.... that works if the goal is of the form \exists y, .... Which it isn't.

Johan Commelin (Mar 16 2020 at 13:16):

To summarise... you're about as stuck as you were at the beginning of your proof.

Johan Commelin (Mar 16 2020 at 13:16):

What did you do to get unstuck?

jack (Mar 16 2020 at 13:16):

by_contradiction!

jack (Mar 16 2020 at 13:19):

Then type error again...

jack (Mar 16 2020 at 13:19):

example {X} (P : X → Prop) : ¬ (∀ x, P x) → (∃ y, ¬ P y) :=
assume h1, classical.by_contradiction $
assume h2,
h1
(
assume x,
assume h3, classical.by_contradiction $
_
)

Johan Commelin (Mar 16 2020 at 13:20):

Sure, assume h3 gives the error

Johan Commelin (Mar 16 2020 at 13:20):

In the beginning of your proof you didn't get stuck at the 1st step. Only at the 2nd...

Johan Commelin (Mar 16 2020 at 13:21):

So you shouldn't copy the assume .. from the beginning of the proof. Only the by_contra

jack (Mar 16 2020 at 13:21):

If i remove the assume, it comes to ¬P x → false

Johan Commelin (Mar 16 2020 at 13:21):

Progress!

jack (Mar 16 2020 at 13:22):

Then write a h2 to make the form -> false

Johan Commelin (Mar 16 2020 at 13:22):

Not so fast

Johan Commelin (Mar 16 2020 at 13:22):

h2 only works if your goal is false

Johan Commelin (Mar 16 2020 at 13:22):

But your goal is of the form X -> Y

jack (Mar 16 2020 at 13:23):

Ok now we assume.

Johan Commelin (Mar 16 2020 at 13:23):

assume hPx

jack (Mar 16 2020 at 13:24):

.. and write a h2

jack (Mar 16 2020 at 13:26):

..and then exists.introOh my god !

jack (Mar 16 2020 at 13:27):

Thank you very much ! Appreciate your help!

jack (Mar 16 2020 at 13:27):

Very kind of you!

jack (Mar 16 2020 at 13:28):

Are you the developer of LEAN?

Johan Commelin (Mar 16 2020 at 13:28):

Nope, lol

Johan Commelin (Mar 16 2020 at 13:28):

Just a random user

jack (Mar 16 2020 at 13:30):

I've no idea whether I can get the credit using such advanced grammar.

Johan Commelin (Mar 16 2020 at 13:30):

But you didn't right?

Johan Commelin (Mar 16 2020 at 13:31):

Without by_contra you can't do this

jack (Mar 16 2020 at 13:32):

Yes, thats why exactly it does not mention intuitionistic logic in this question.

jack (Mar 16 2020 at 13:33):

I'm sure there must be sth. wrong with my hand-made provement.

jack (Mar 16 2020 at 13:34):

One more question, is classical.by_contradiction equivalent to A <-> ~~A?

Johan Commelin (Mar 16 2020 at 13:36):

Yup

Johan Commelin (Mar 16 2020 at 13:36):

Which results from classical.* were you allowed to use?

jack (Mar 16 2020 at 13:37):

I have very very few examples in the class.

jack (Mar 16 2020 at 13:37):

https://gist.github.com/benediktahrens/a52dc0137db2e845bb96b5f67645bad7

Johan Commelin (Mar 16 2020 at 13:38):

https://gist.github.com/benediktahrens/a52dc0137db2e845bb96b5f67645bad7#file-lec9-lean-L250

jack (Mar 16 2020 at 13:38):

So i have no idea how to deal with \not at all.

Johan Commelin (Mar 16 2020 at 13:38):

uses by_contradiction

jack (Mar 16 2020 at 13:39):

Yeah. But I still don't know how to use is ... until NOW.

Johan Commelin (Mar 16 2020 at 13:40):

#check classical.by_contradiction
-- classical.by_contradiction : (¬?M_1 → false) → ?M_1

Johan Commelin (Mar 16 2020 at 13:40):

Does that help?

jack (Mar 16 2020 at 13:40):

Thanks again for your kindly help.

Johan Commelin (Mar 16 2020 at 13:41):

So it is exactly: "prove A by proving \not \not A"

jack (Mar 16 2020 at 13:41):

Actually I ve no idea about the type signature, i.e. ?M_

jack (Mar 16 2020 at 13:42):

I thought it is just lambda calculus, that's all. The goal is really really helpful.

jack (Mar 16 2020 at 13:42):

But still, the error message is quite confusing, especially for beginners.

Johan Commelin (Mar 16 2020 at 13:42):

?M_1 is just a weird way of writing A or P

Johan Commelin (Mar 16 2020 at 13:43):

It's a "metavariable"

jack (Mar 16 2020 at 13:45):

Are u a mathematician @Johan Commelin ?

Bryan Gin-ge Chen (Mar 16 2020 at 13:45):

It looks nicer if you write #check @classical.by_contradiction:

classical.by_contradiction :  {p : Prop}, (¬p  false)  p

The @ tells Lean to make all arguments explicit. Along the way the metavariables get nicer names.

jack (Mar 16 2020 at 13:46):

Thank u guys!

Donald Sebastian Leung (Mar 16 2020 at 14:01):

jack said:

Are u a mathematician Johan Commelin ?

Yes - in fact, he is one of the three mathematicians (the other two being Kevin Buzzard and Patrick Massot) who formalized perfectoid spaces in Lean

(Correct me if I am wrong)

jack (Mar 16 2020 at 14:10):

WOW My pleasure!

Viktoriya Malyasova (Mar 16 2020 at 14:28):

Kenny Lau said:

bnot_bnot is an example with a universal quantifier

Okay, okay, I'm wrong, still, can I see any other examples somewhere? I don't want to do case analysis on b like in the example, I want to do the analogue of intros b.

Viktoriya Malyasova (Mar 16 2020 at 14:37):

Виктория Малясова said:

Kenny Lau said:

bnot_bnot is an example with a universal quantifier

Okay, okay, I'm wrong, still, can I see any other examples somewhere? I don't want to do case analysis on b like in the example, I want to do the analogue of intros b.

Never mind, figured it out.

Yury G. Kudryashov (Mar 16 2020 at 19:36):

@Виктория Малясова What exactly do you want to do: do you want to apply a hypothesis with a universal qualifier, or to prove a goal? BTW, if you'll transliterate your nickname, more people will be able to read it, and it'll be easier to tag you in a reply.

Yury G. Kudryashov (Mar 16 2020 at 19:36):

BTW, welcome to the club.

Yury G. Kudryashov (Mar 16 2020 at 19:38):

If you want to use h : ∀ b, ∃ x, ..., then you can use something like let ⟨x, hx⟩ := h b in ....

Yury G. Kudryashov (Mar 16 2020 at 19:38):

If you want to prove ∀ b, ∃ x, P x, then you can use something like assume b, ⟨x, hx⟩

Yury G. Kudryashov (Mar 16 2020 at 19:39):

(or λ b instead of assume b)

Daniel Keys (Mar 16 2020 at 22:23):

Can anyone please point me to where the (proof of the) division algorithm a = b * q + r is hiding?

Mario Carneiro (Mar 16 2020 at 22:25):

I guess that would be a combination of the definition of nat.div and nat.mod, and nat.mod_add_div

Daniel Keys (Mar 16 2020 at 22:26):

Thanks! OK, so we don't have a stand-alone proof of it? That explains why I was searching in vain.

Daniel Keys (Mar 16 2020 at 22:34):

Although nat is strange, since I expected it to be an int thing.

Mario Carneiro (Mar 16 2020 at 22:36):

there is an int version too, but most int theorems reduce to nat counterparts

Mario Carneiro (Mar 16 2020 at 22:37):

the int version is more complicated because the modulus can be negative and conventions differ on what to do about that

Shing Tak Lam (Mar 17 2020 at 13:25):

Hello, I'm struggling with getting rewrite to do what I want it to do, I have (extra goals removed)

hn2 : n % 2 = 1,
 n = 1 + 2 * (n / 2)

and I want to rewrite it to

 n = n % 2 + 2 * (n / 2)

But if I use rw <-hn2 it gives me

 n = n % 2 + bit0 (n % 2) * (n / bit0 (n % 2))

as it replaces 2 with bit0 (n % 2)

Thank you!

Kevin Buzzard (Mar 17 2020 at 13:29):

ha ha! This is happening because of the way numerals are internally stored (2 = bit0 1). There are ways around this.

Kevin Buzzard (Mar 17 2020 at 13:31):

example (n : ) (hn2 : n % 2 = 1) : n = 1 + 2 * (n / 2) :=
begin
  conv begin
    -- entering "conv mode" where we can pull terms off the goal
    to_rhs,
    congr,
    -- now we have the 1
    rw hn2, -- rewrite succeeds
  end,
  -- done
  sorry
end

Kevin Buzzard (Mar 17 2020 at 13:32):

here is more information about conv.

Kevin Buzzard (Mar 17 2020 at 13:34):

Here is a more direct approach:

example (n : ) (hn2 : n % 2 = 1) : n = 1 + 2 * (n / 2) :=
begin
  conv in (1) begin
    rw hn2,
  end,
  sorry
end

Shing Tak Lam (Mar 17 2020 at 13:34):

Yeah I see. So numerals are stored in binary in reverse order? That seems fascinating and I remember playing around with that in Haskell. Makes sense that Lean would use that.

Anyways, this seemed to work

have hnm': 1 + 2 * (n / 2) = n % 2 + 2 * (n / 2) := by rw hn2,
rw hnm',

Thanks a lot!

Kevin Buzzard (Mar 17 2020 at 13:36):

Yes, your approach should also work. Nice!

Yes, Lean internally stores numerals as bit0 (bit1 (bit1 (...))). You can see what's going on internally with the option set_option pp.numerals false.

Shing Tak Lam (Mar 17 2020 at 16:10):

Alright, thanks a lot.

Another question, is there a way to paste the highlighted value from Ctrl-P # to the location of the cursor? For example with a lemma that has a long name (such as div_le_div_of_le_of_pos), after finding it I press enter to go to the file and I copy the name from there. Is there a quicker way?

Thanks!

Bryan Gin-ge Chen (Mar 17 2020 at 16:18):

Not at the moment, but that's an interesting feature request. Please open an issue here.

Shing Tak Lam (Mar 17 2020 at 16:19):

Alright thanks. Will do.

Kevin Buzzard (Mar 17 2020 at 16:32):

If you find the lemma by typing div_le_div and then pressing ctrl-space and scrolling down the options with the arrow keys then you can complete with tab and there it is

Kevin Buzzard (Mar 17 2020 at 16:32):

Press ctrl-space a second time to see the full types

Shing Tak Lam (Mar 18 2020 at 05:48):

Alright thanks. That doesn't work all the time for me though, but I think it's an issue with my VSCode installation rather than with the Lean extension.

Shing Tak Lam (Mar 18 2020 at 05:52):

Another question, how do I prove this?

(r is a nat)

(2 * r + 1) % 2 = 1

I've found a few things that are interesting in int., but nothing came up when looking in nat, and searching for combinations of add, mul and mod didn't give anything. I guess what I'm looking for is

a < b -> (b * n + a) % b = a

Bryan Gin-ge Chen (Mar 18 2020 at 05:52):

The VS Code extension queries Lean for autocomplete information and sadly Lean doesn't always provide anything useful at a given position in a file. If you have some specific examples we can take a look.

Bryan Gin-ge Chen (Mar 18 2020 at 06:04):

This is what I get after trying suggest:

import tactic

example (a b n: ) :
a < b -> (b * n + a) % b = a := begin
  refine norm_num.nat_mod_helper (b * n + a) b n a _,
  ring,
end

Surely there's a way to do this without pulling in something written for norm_num though.

Mario Carneiro (Mar 18 2020 at 06:48):

example (a b n: ) (h : a < b) : (b * n + a) % b = a :=
by rw [add_comm, nat.add_mul_mod_self_left, nat.mod_eq_of_lt h]

Kenny Lau (Mar 18 2020 at 06:50):

import tactic.norm_num

example (r : ): (2 * r + 1) % 2 = 1 :=
by rw [add_comm, nat.add_mul_mod_self_left]; norm_num

Kenny Lau (Mar 18 2020 at 06:50):

lol you're faster

Shing Tak Lam (Mar 18 2020 at 06:57):

Thank you all!

Daniel Keys (Mar 20 2020 at 19:18):

Could someone please help with the last sorry in this proof? I expect I need revert or maybe generalize. I don't know how to move along with either, though!

import data.real.basic

def sum_of_sets (A : set ) (B : set ) := { x :  |  y  A,  z  B, x = y + z}
lemma proposition_2_6 (A : set ) (B : set ) (h1A : A.nonempty) (h1B : B.nonempty)
  (h2A : bdd_above A) (h2B : bdd_above B) (a : ) (b : ) :
  (is_lub A a)  (is_lub B b)  is_lub (sum_of_sets A B) (a + b) :=
begin
  intro h,
  cases h with hA hB,
  split,
  { sorry,  -- this side works
  },
  -- now prove that (a+b) is the least upper bound
  intros S hS,
  change  xab  (sum_of_sets A B), xab  S at hS,
  --cases h1A with ya hya,
  cases h1B with z hz,
  have H1 :  y  A, (y + z)  (sum_of_sets A B),
    intros y hy,
    unfold sum_of_sets,
    existsi y, existsi hy, existsi z, existsi hz, refl,
  have H2 :  y  A, (y + z)  S,
    intros y hy,
    apply hS, exact H1 y hy,
  have H3 :  y  A, y  S - z,
    intros y hy,
    have H3a := H2 y hy,
    linarith,
  have H4 : (S - z)  upper_bounds A, exact H3,
  have H5 : a  (S - z),
    have H13A := hA.right,
    have h13 := H13A H4, exact h13,
  have H6 : z  S - a, linarith,
  have H7 : (S - a)  upper_bounds B,  -- got stuck here
    --revert z,
    --does generalize work? How?
   sorry,
end

Kevin Buzzard (Mar 20 2020 at 19:27):

z is now a fixed real number in B. In particular you only know that t <= S - a for one particular t in B, namely t = z. You can make progress in the proof with intros t ht, but that is basically your only next move. You can't use z to prove the goal.

Daniel Keys (Mar 20 2020 at 19:35):

I thought I could move on by reverting z. So I'm wrong there?

Daniel Keys (Mar 20 2020 at 19:40):

I should be able to use the fact that the initial z was chosen arbitrarily. I also thought about starting with forall z at the very top, but am just trying to follow one of the standard proofs - and learn some more Lean in the process!

Kevin Buzzard (Mar 20 2020 at 19:45):

But z is not arbitrary, z is one specific element of B which is the witness to the non-emptiness of B.

Kevin Buzzard (Mar 20 2020 at 19:46):

h1B is like saying "this specific constant real number z is an element of B". It's no different to saying "assume πB\pi\in B". You can prove whatever you like about π\pi now, but you won't be able to deduce anything about an arbitrary element of BB.

Daniel Keys (Mar 20 2020 at 19:47):

OK. So I'm not supposed to be thinking about it as arbitrary? Then I need to rewrite from the start I guess.

Kevin Buzzard (Mar 20 2020 at 19:47):

Yes it's definitely not arbitrary, it's some fixed constant.

Kevin Buzzard (Mar 20 2020 at 19:50):

hB has, buried within it, a statement about an arbitrary element of B.

Kevin Buzzard (Mar 20 2020 at 19:54):

I set this question in an undergraduate exam in 2018 and @Abhimanyu Pallavi Sudhir formalised the solutions. @Kenny Lau sat the exam and his written solution was constructive and could be basically cut and pasted into Lean. It was funny reading it. He kept writing assume :-)

Kevin Buzzard (Mar 20 2020 at 19:56):

You need to be proving things like this:

  have H2 :  y  A,  z  B, (y + z)  S,
    intros y hy z hz,
    apply hS, use y, split, use hy, use z, split, use hz, refl,

Kevin Buzzard (Mar 20 2020 at 19:56):

sorry for ugly proof, just got to run

Daniel Keys (Mar 20 2020 at 20:04):

Kevin Buzzard said:

You need to be proving things like this:

  have H2 :  y  A,  z  B, (y + z)  S,
    intros y hy z hz,
    apply hS, use y, split, use hy, use z, split, use hz, refl,

Right. This is what I meant by starting over. Thanks again for the help! Hopefully I get there in another day or so -:)

Kevin Buzzard (Mar 20 2020 at 20:39):

have H2 : ∀ y ∈ A, ∀ z ∈ B, (y + z) ≤ S := λ y hy z hz, hS _ ⟨y, hy, z, hz, rfl⟩,

Daniel Keys (Mar 20 2020 at 20:47):

Yes, I have changed all that and now I get stuck here:

-- now prove that (a+b) is the least upper bound
  intros S hS,
  change  xab  (sum_of_sets A B), xab  S at hS,
  have H1 :  y  A,  z  B, (y + z)  (sum_of_sets A B),
    intros y hy z hz,
    unfold sum_of_sets,
    existsi y, existsi hy, existsi z, existsi hz, refl,
  have H2 :  y  A,  z  B, (y + z)  S,
    intros y hy z hz,
    apply hS, exact H1 y hy z hz,
  have H3 :  y  A,  z  B, y  S - z,
    intros y hy z hz,
    have H3a := H2 y hy z hz,
    linarith,
  have h21B := hB.right, have h22B := hB.left,
  change  z  B, z  b at h22B,
  have H4 :  y  A,  z  B, (S - z)  upper_bounds A, --!
    intros y hy z hz,
    have H4a := h22B z hz,
    have H4b := H3 y hy z hz,
    change  y  A, y  (S - z),   -- exact H4b doesn't do it

Kevin Buzzard (Mar 20 2020 at 20:50):

The y : ℝ is a constant. Your goal has a variable y in.

Daniel Keys (Mar 20 2020 at 20:50):

Failing to see the same thing again, huh?

Kevin Buzzard (Mar 20 2020 at 20:51):

Your H4 has a superfluous y in

Kevin Buzzard (Mar 20 2020 at 20:51):

the conclusion of H4 doesn't mention y, so you're still on track

Kevin Buzzard (Mar 20 2020 at 20:52):

yeah this is fine, your H4 is just not correctly stated.

Daniel Keys (Mar 20 2020 at 20:56):

OK, that helped. I'm past it now.

Kevin Buzzard (Mar 20 2020 at 21:02):

  intros S hS,
  have H3 :  y  A,  z  B, y  S - z :=
    λ y hy z hz, le_sub_right_of_add_le $ hS y, hy, z, hz, rfl,

Here's how I would skip H1 and H2 and prove H3 in term mode.

Kevin Buzzard (Mar 20 2020 at 21:03):

have h21B := hB.right, have h22B := hB.left, could just be cases hB with h22B h21B,, although note that the latter destroys hB.

Kevin Buzzard (Mar 20 2020 at 21:05):

Try commenting out all the lines which mention change. Interesting, huh?

Daniel Keys (Mar 20 2020 at 21:17):

Yes - it's still useful for me at this stage to see things explicitly.
You were right, setting up your own problems teaches you a lot! If you also get some help...

Now here's where I'm right now, I literally have it - but Lean doesn't think so!

intros S hS,
  have H1 :  y  A,  z  B, (y + z)  (sum_of_sets A B),
    intros y hy z hz,
    unfold sum_of_sets,
    existsi y, existsi hy, existsi z, existsi hz, refl,
  have H2 :  y  A,  z  B, (y + z)  S,
    intros y hy z hz,
    apply hS, exact H1 y hy z hz,
  have H3 :  y  A,  z  B, y  S - z,
    intros y hy z hz,
    have H3a := H2 y hy z hz,
    linarith,
  have h21B := hB.right, have h22B := hB.left,
  --change ∀ z ∈ B, z ≤ b at h22B,
  have H4 :  z  B, (S - z)  upper_bounds A, --!
    intros z hz y hy,
    exact H3 y hy z hz,
  have H5 :  z  B, a  (S - z),
    intros z hz,
    have H13A := hA.right,
    change  u  upper_bounds A, a  u at H13A,
    have H5a := H4 z hz,   -- can't get to use H13A at all

Daniel Keys (Mar 20 2020 at 21:18):

Oh, wait

Daniel Keys (Mar 20 2020 at 21:19):

Done.

Kevin Buzzard (Mar 20 2020 at 21:19):

proof script or it didn't happen

Patrick Massot (Mar 20 2020 at 21:19):

What is this proving?

Kevin Buzzard (Mar 20 2020 at 21:20):

sup S + sup T = sup (S+T)

Patrick Massot (Mar 20 2020 at 21:20):

I mean: what is the Lean statement?

Kevin Buzzard (Mar 20 2020 at 21:20):

def sum_of_sets (A : set ) (B : set ) := { x :  |  y  A,  z  B, x = y + z}
lemma proposition_2_6 (A : set ) (B : set ) (h1A : A.nonempty) (h1B : B.nonempty)
  (h2A : bdd_above A) (h2B : bdd_above B) (a : ) (b : ) :
  (is_lub A a)  (is_lub B b)  is_lub (sum_of_sets A B) (a + b) :=

Patrick Massot (Mar 20 2020 at 21:21):

The proof can't possibly start with intros S hS

Daniel Keys (Mar 20 2020 at 21:22):

Kevin Buzzard said:

proof script or it didn't happen

I have H5 : ∀ z ∈ B, a ≤ (S - z). Still a little ways to go.

Kevin Buzzard (Mar 20 2020 at 21:22):

begin
  intro h,
  cases h with hA hB,
  split,
  { sorry,  -- this side works
  },
  -- now prove that (a+b) is the least upper bound

Kevin Buzzard (Mar 20 2020 at 21:22):

Daniel Keys said:

I have H5 : ∀ z ∈ B, a ≤ (S - z). Still a little ways to go.

Oh! I'm rooting for you :-)

Kevin Buzzard (Mar 20 2020 at 21:22):

I'm making some other proofs

Kevin Buzzard (Mar 20 2020 at 21:24):

Patrick, we're just talking about this part:

lemma proposition_2_6'' (A : set ) (B : set ) (h1A : A.nonempty) (h1B : B.nonempty)
  (h2A : bdd_above A) (h2B : bdd_above B) (a : ) (b : )
  (hA : is_lub A a) (hB : is_lub B b) :
  a + b  lower_bounds (upper_bounds (sum_of_sets A B)) :=

Patrick Massot (Mar 20 2020 at 21:26):

How can it be that complicated?

Kevin Buzzard (Mar 20 2020 at 21:27):

He's a beginner!

Kevin Buzzard (Mar 20 2020 at 21:28):

We're going to refactor after.

Patrick Massot (Mar 20 2020 at 21:28):

Ok, I should go to the PR and issues lists.

Daniel Keys (Mar 20 2020 at 21:34):

Kevin Buzzard said:

proof script or it didn't happen

OK now, this is the finish. Uncountable thanks, Kevin!

have H5 :  z  B, a  (S - z),
    intros z hz,
    have H13A := hA.right,
    change  u  upper_bounds A, a  u at H13A,
    have H5a := H4 z hz,
    exact H13A (S-z) H5a,
  have H6 :  z  B, z  S - a,
    intros z hz,
    have H6a := H5 z hz, linarith,
  have H7 : (S - a)  upper_bounds B, exact H6,
  have H8 : b  (S-a),
    have H13B := hB.right,
    change  u  upper_bounds B, b  u at H13B,
    exact H13B (S-a) H7,
  linarith, done

Kevin Buzzard (Mar 20 2020 at 21:34):

lemma proposition_2_6''' (A : set ) (B : set ) (a : ) (b : )
  (hA : is_lub A a) (hB : is_lub B b) :
  a + b  lower_bounds (upper_bounds (sum_of_sets A B)) :=
λ S hS, add_le_of_le_sub_left $ hB.2 $ λ z hz, le_sub.1 $ hA.2 $ λ y hy, le_sub_right_of_add_le $ hS y, hy, z, hz, rfl

Daniel Keys (Mar 20 2020 at 21:35):

Still need to work on term mode. On my to do list.

Kevin Buzzard (Mar 20 2020 at 21:35):

So do you want to refactor your proof to make it much shorter?

Daniel Keys (Mar 20 2020 at 21:36):

Meaning?

Kevin Buzzard (Mar 20 2020 at 21:37):

do you want me to show you how to think about your proof so that the next time you have to write such a proof it will come out better, shorter, and will compile quicker?

Kevin Buzzard (Mar 20 2020 at 21:37):

lemma proposition_2_6' (A : set ) (B : set ) (h1A : A.nonempty) (h1B : B.nonempty)
  (h2A : bdd_above A) (h2B : bdd_above B) (a : ) (b : )
  (hA : is_lub A a) (hB : is_lub B b) :
  a + b  lower_bounds (upper_bounds (sum_of_sets A B)) :=
begin
  intros S hS,
  have H1 :  y  A,  z  B, (y + z)  (sum_of_sets A B),
    intros y hy z hz,
    unfold sum_of_sets,
    existsi y, existsi hy, existsi z, existsi hz, refl,
  have H2 :  y  A,  z  B, (y + z)  S,
    intros y hy z hz,
    apply hS, exact H1 y hy z hz,
  have H3 :  y  A,  z  B, y  S - z,
    intros y hy z hz,
    have H3a := H2 y hy z hz,
    linarith,
  have h21B := hB.right, have h22B := hB.left,
  --change ∀ z ∈ B, z ≤ b at h22B,
  have H4 :  z  B, (S - z)  upper_bounds A, --!
    intros z hz y hy,
    exact H3 y hy z hz,
have H5 :  z  B, a  (S - z),
    intros z hz,
    have H13A := hA.right,
    change  u  upper_bounds A, a  u at H13A,
    have H5a := H4 z hz,
    exact H13A (S-z) H5a,
  have H6 :  z  B, z  S - a,
    intros z hz,
    have H6a := H5 z hz, linarith,
  have H7 : (S - a)  upper_bounds B, exact H6,
  have H8 : b  (S-a),
    have H13B := hB.right,
    change  u  upper_bounds B, b  u at H13B,
    exact H13B (S-a) H7,
  linarith,
end

Daniel Keys (Mar 20 2020 at 21:37):

Of course.

Kevin Buzzard (Mar 20 2020 at 21:38):

That's your proof currently. I tidied up the beginning -- I removed the sorry and I did the intro and cases (I changed the goal a bit so hA and hB are now hypotheses and I changed the conclusion so it was just the goal you were working on)

Daniel Keys (Mar 20 2020 at 21:39):

Right, I did see that.

Kevin Buzzard (Mar 20 2020 at 21:39):

So the thing I like least about your proof is the linariths

Kevin Buzzard (Mar 20 2020 at 21:40):

I mean, it's great that linarith exists and can do all sorts of linear inequalities, but when you use it you're using a sledgehammer to crack a nut.

Daniel Keys (Mar 20 2020 at 21:40):

OK, I did those since I know how to deal with those goals.

Kevin Buzzard (Mar 20 2020 at 21:40):

The first time you use it is about 11 lines into your proof.

Daniel Keys (Mar 20 2020 at 21:40):

But I agree with you.

Kevin Buzzard (Mar 20 2020 at 21:41):

Your local context is

...
H3a : y + z ≤ S
⊢ y ≤ S - z

Kevin Buzzard (Mar 20 2020 at 21:41):

and you solve this with linarith

Kevin Buzzard (Mar 20 2020 at 21:41):

So I look at that and think "that is a completely standard fact so it's going to be in the library"

Daniel Keys (Mar 20 2020 at 21:42):

Yes, agreed. Just lazy, there were bigger fish...

Kevin Buzzard (Mar 20 2020 at 21:42):

The problem is, I don't know whether it's going to be y + z ≤ S -> y ≤ S - z or y ≤ S - z <-> y ≤ S - z in the library, and I don't know the name of the lemma either.

Kevin Buzzard (Mar 20 2020 at 21:43):

But if you know the tricks, you can sort this out really easily.

Daniel Keys (Mar 20 2020 at 21:43):

library_search? I also browse through files many times.

Kevin Buzzard (Mar 20 2020 at 21:43):

You don't need anything like that

Kevin Buzzard (Mar 20 2020 at 21:44):

you can do this just with VS Code tricks. Your goal is y <= S - z and so the Lean name for the theorem you want will start le_sub

Kevin Buzzard (Mar 20 2020 at 21:44):

so you can replace that first linarith with exact le_sub but we're not finished yet

Daniel Keys (Mar 20 2020 at 21:44):

sub for subtraction here?

Kevin Buzzard (Mar 20 2020 at 21:45):

Right. There's a code which you eventually pick up. is le, < is lt etc

Daniel Keys (Mar 20 2020 at 21:45):

I saw lots of these in NNG, but there was no sub there. So this one's new.

Kevin Buzzard (Mar 20 2020 at 21:46):

If I delete that first linarith and replace it with exact le_sub then VS Code immediately gives me a list of possible completions. Does that work for you?

Kevin Buzzard (Mar 20 2020 at 21:47):

If it doesn't, try exact le_sub and then hit ctrl-space. This is a really important trick to learn.

Daniel Keys (Mar 20 2020 at 21:47):

Yes, five options. Thanks for the hint!

Kevin Buzzard (Mar 20 2020 at 21:47):

and you can scroll up and down with the arrow keys and see what all the suggestions are.

Kevin Buzzard (Mar 20 2020 at 21:48):

and le_sub_right_of_add_le is exactly what you want.

Kevin Buzzard (Mar 20 2020 at 21:49):

so you can hit tab and it will complete for you, and you can change that linarith to exact le_sub_right_of_add_le H3a,

Patrick Massot (Mar 20 2020 at 21:49):

library_search would have been faster.

Kevin Buzzard (Mar 20 2020 at 21:49):

You'd have to revert H3 or something.

Patrick Massot (Mar 20 2020 at 21:49):

Why?

Kevin Buzzard (Mar 20 2020 at 21:50):

Oh you don't have to?

Patrick Massot (Mar 20 2020 at 21:50):

Don't underestimate the power of library_search

Kevin Buzzard (Mar 20 2020 at 21:50):

For the second one we have

H6a : a ≤ S - z
⊢ z ≤ S - a

Let's try library_search

Patrick Massot (Mar 20 2020 at 21:50):

Go back watching Gabriel's talk in Pittsburgh.

Kevin Buzzard (Mar 20 2020 at 21:51):

library_search doesn't give the optimal answer for the second one: exact le_sub.mp (H5 z hz)

Daniel Keys (Mar 20 2020 at 21:51):

Patrick Massot said:

Go back watching Gabriel's talk in Pittsburgh.

Is that a video you're talking about, Patrick?

Kevin Buzzard (Mar 20 2020 at 21:52):

If I try exact le_sub I see that le_sub is pretty much nearly what we want, except that it's an if and only if

Kevin Buzzard (Mar 20 2020 at 21:53):

and you can pull out one way of an if and only if with a .1, so

    have H6a := H5 z hz, exact le_sub.1 H6a,

Kevin Buzzard (Mar 20 2020 at 21:54):

The third one is on the last line of the script, and it's

H8 : b ≤ S - a
⊢ a + b ≤ S

Kevin Buzzard (Mar 20 2020 at 21:54):

and again library_search gives a terrifying answer

Kevin Buzzard (Mar 20 2020 at 21:55):

but I'm going to change it to exact add_le_of_le_sub_left H8,

Kevin Buzzard (Mar 20 2020 at 21:55):

so now we have this:

lemma proposition_2_6' (A : set ) (B : set ) (h1A : A.nonempty) (h1B : B.nonempty)
  (h2A : bdd_above A) (h2B : bdd_above B) (a : ) (b : )
  (hA : is_lub A a) (hB : is_lub B b) :
  a + b  lower_bounds (upper_bounds (sum_of_sets A B)) :=
begin
  intros S hS,
  have H1 :  y  A,  z  B, (y + z)  (sum_of_sets A B),
    intros y hy z hz,
    unfold sum_of_sets,
    existsi y, existsi hy, existsi z, existsi hz, refl,
  have H2 :  y  A,  z  B, (y + z)  S,
    intros y hy z hz,
    apply hS, exact H1 y hy z hz,
  have H3 :  y  A,  z  B, y  S - z,
    intros y hy z hz,
    have H3a := H2 y hy z hz,
    exact le_sub_right_of_add_le H3a,
  have h21B := hB.right, have h22B := hB.left,
  --change ∀ z ∈ B, z ≤ b at h22B,
  have H4 :  z  B, (S - z)  upper_bounds A, --!
    intros z hz y hy,
    exact H3 y hy z hz,
have H5 :  z  B, a  (S - z),
    intros z hz,
    have H13A := hA.right,
    change  u  upper_bounds A, a  u at H13A,
    have H5a := H4 z hz,
    exact H13A (S-z) H5a,
  have H6 :  z  B, z  S - a,
    intros z hz,
    have H6a := H5 z hz, exact le_sub.1 H6a,
  have H7 : (S - a)  upper_bounds B, exact H6,
  have H8 : b  (S-a),
    have H13B := hB.right,
    change  u  upper_bounds B, b  u at H13B,
    exact H13B (S-a) H7,
  exact add_le_of_le_sub_left H8,
end

Kevin Buzzard (Mar 20 2020 at 21:55):

and now our proof uses only really basic tactics so it's in a really good form to play with.

Kevin Buzzard (Mar 20 2020 at 21:56):

Next thing: did you know that change doesn't really do anything? It's necessary for rewrites, but not for stuff like apply and intro.

Daniel Keys (Mar 20 2020 at 21:56):

Thanks Kevin, for taking all this time! Yes, I know about change.

Kevin Buzzard (Mar 20 2020 at 21:57):

actually let's leave these. Let me get to the bombshell.

Daniel Keys (Mar 20 2020 at 21:57):

These are supposed to be readable by students, once I get up standing myself.

Kevin Buzzard (Mar 20 2020 at 21:57):

The bombshell is that your proof is twice as long as it needs to be, because you wrote it in a direction which a mathematician would call "forwards"

Patrick Massot (Mar 20 2020 at 21:57):

https://www.youtube.com/watch?v=WydyJJYKyTs&list=PLlF-CfQhukNkWwZt45vkNfWfuO-tBBqPN&index=16&t=0s explains how powerfull is library_search

Kevin Buzzard (Mar 20 2020 at 21:58):

and which a computer scientist would call "the wrong way around"

Kevin Buzzard (Mar 20 2020 at 21:58):

You prove 8 sub-theorems H1 to H8 and then finished the job

Kevin Buzzard (Mar 20 2020 at 21:58):

Thanks @Patrick Massot

Kevin Buzzard (Mar 20 2020 at 21:58):

And each H(n+1) followed from Hn

Kevin Buzzard (Mar 20 2020 at 21:59):

and at the end of it your local context looks like this:

1 goal
A B : set ℝ,
h1A : set.nonempty A,
h1B : set.nonempty B,
h2A : bdd_above A,
h2B : bdd_above B,
a b : ℝ,
hA : is_lub A a,
hB : is_lub B b,
S : ℝ,
hS : S ∈ upper_bounds (sum_of_sets A B),
H1 : ∀ (y : ℝ), y ∈ A → ∀ (z : ℝ), z ∈ B → y + z ∈ sum_of_sets A B,
H2 : ∀ (y : ℝ), y ∈ A → ∀ (z : ℝ), z ∈ B → y + z ≤ S,
H3 : ∀ (y : ℝ), y ∈ A → ∀ (z : ℝ), z ∈ B → y ≤ S - z,
h21B : b ∈ lower_bounds (upper_bounds B),
h22B : b ∈ upper_bounds B,
H4 : ∀ (z : ℝ), z ∈ B → S - z ∈ upper_bounds A,
H5 : ∀ (z : ℝ), z ∈ B → a ≤ S - z,
H6 : ∀ (z : ℝ), z ∈ B → z ≤ S - a,
H7 : S - a ∈ upper_bounds B,
H8 : b ≤ S - a
⊢ a + b ≤ S

Kevin Buzzard (Mar 20 2020 at 21:59):

You proved H1 -> H2 -> H3 -> H4 -> H5 -> H6 -> H7 -> H8

Kevin Buzzard (Mar 20 2020 at 21:59):

and H8 -> goal

Kevin Buzzard (Mar 20 2020 at 22:00):

Let's now write exactly the same proof, but backwards.

Daniel Keys (Mar 20 2020 at 22:00):

You mean using apply?

Kevin Buzzard (Mar 20 2020 at 22:01):

lemma proposition_2_6' (A : set ) (B : set ) (h1A : A.nonempty) (h1B : B.nonempty)
  (h2A : bdd_above A) (h2B : bdd_above B) (a : ) (b : )
  (hA : is_lub A a) (hB : is_lub B b) :
  a + b  lower_bounds (upper_bounds (sum_of_sets A B)) :=
begin
  intros S hS,
  sorry
end

The goal is ⊢ a + b ≤ S

Kevin Buzzard (Mar 20 2020 at 22:02):

and the last line of our current proof is exact add_le_of_le_sub_left H8, so let's make the first line of our new proof be apply add_le_of_le_sub_left,

Kevin Buzzard (Mar 20 2020 at 22:02):

lemma proposition_2_6'' (A : set ) (B : set ) (h1A : A.nonempty) (h1B : B.nonempty)
  (h2A : bdd_above A) (h2B : bdd_above B) (a : ) (b : )
  (hA : is_lub A a) (hB : is_lub B b) :
  a + b  lower_bounds (upper_bounds (sum_of_sets A B)) :=
begin
  intros S hS,
  apply add_le_of_le_sub_left,
  sorry -- goal is ⊢ b ≤ S - a
end

Kevin Buzzard (Mar 20 2020 at 22:02):

and now our goal is H8

Daniel Keys (Mar 20 2020 at 22:02):

OK. Let me take it from here.

Kevin Buzzard (Mar 20 2020 at 22:03):

Go for it! I did it, it's really good fun :-)

Daniel Keys (Mar 20 2020 at 22:03):

I have to go now, but will definitely try it later. Will haul back for help if needed.

Kevin Buzzard (Mar 20 2020 at 22:03):

Note that H7 and H6 are exactly the same goal!

Patrick Massot (Mar 20 2020 at 22:03):

I don't think you need to do backwards to make it shorter.

Kevin Buzzard (Mar 20 2020 at 22:03):

so you can skip H7

Kevin Buzzard (Mar 20 2020 at 22:04):

Patrick did you see my one line term proof? I made that by turning Daniel's proof backwards and then termifying it

Daniel Keys (Mar 20 2020 at 22:04):

Patrick Massot said:

I don't think you need to do backwards to make it shorter.

Either way is good practice for me.

Patrick Massot (Mar 20 2020 at 22:04):

No, I haven't seen your proof term.

Patrick Massot (Mar 20 2020 at 22:05):

But I guess it's hard to read.

Kevin Buzzard (Mar 20 2020 at 22:05):

https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/noob.20question%28s%29/near/191305486

Kevin Buzzard (Mar 20 2020 at 22:05):

It's the same as @Daniel Keys 's proof :-)

Patrick Massot (Mar 20 2020 at 22:05):

It looks has I expected...

Patrick Massot (Mar 20 2020 at 22:06):

My advice for Daniel is: try to get a clearer mathematical picture.

Kevin Buzzard (Mar 20 2020 at 22:06):

It is super-satisfying to turn this proof backwards

Mario Carneiro (Mar 20 2020 at 22:06):

If you break that term mode proof into a couple applys and intros it should be plenty readable

Daniel Keys (Mar 20 2020 at 22:06):

Patrick Massot said:

My advice for Daniel is: try to get a clearer mathematical picture.

Why exactly, Patrick?

Patrick Massot (Mar 20 2020 at 22:08):

I would start with

  intros S hS,
  have H1 :  x  A, S - x  upper_bounds B,
    sorry,
  have H2 : S - b  upper_bounds A,
    sorry,
  sorry,

where each sorry is at most three lines long.

Patrick Massot (Mar 20 2020 at 22:09):

I claim this is a readable outline of the proof.

Kevin Buzzard (Mar 20 2020 at 22:10):

I think it's interesting that I set this question to about 250 students and out of all of them, only one person proved it constructively.

Patrick Massot (Mar 20 2020 at 22:10):

And this does not come through Lean mastery, it's the mathematical understanding.

Mario Carneiro (Mar 20 2020 at 22:11):

what's up with these unbracketed indented blocks after have? This isn't python

Kevin Buzzard (Mar 20 2020 at 22:11):

Everyone said that if some upper bound was less than a+b then you could find some element of A which was within epsilon/2 of the upper bound for A etc.

Kevin Buzzard (Mar 20 2020 at 22:12):

indented blocks: I thought that TPIL said that this was OK. Doesn't it?

Patrick Massot (Mar 20 2020 at 22:12):

Mario, I can tell you very precisely where they come from.

Mario Carneiro (Mar 20 2020 at 22:12):

they have the same problem as unbracketed blocks elsewhere

Mario Carneiro (Mar 20 2020 at 22:13):

tactics can end up doing too much and messing up other branches of the proof

Kevin Buzzard (Mar 20 2020 at 22:13):

https://leanprover.github.io/theorem_proving_in_lean/tactics.html?highlight=indent#basic-tactics

Patrick Massot (Mar 20 2020 at 22:13):

Last year I taught curly brackets to my undergrads. But they were unable to balance them properly. And then Lean stopped displaying anything, or wrote very confusing error messages (which means any error message for my students). This year I taught indented blocks, and Lean became easy.

Kevin Buzzard (Mar 20 2020 at 22:14):

unbalanced bracket = really really horrible errors. You either get sync or you just get literally nothing. No display at any point in your proof.

Kevin Buzzard (Mar 20 2020 at 22:14):

Really hard to debug

Patrick Massot (Mar 20 2020 at 22:14):

Kevin Buzzard said:

https://leanprover.github.io/theorem_proving_in_lean/tactics.html?highlight=indent#basic-tactics

And Mario adds one line to his "When he won't be my advisor anymore" notebook.

Mario Carneiro (Mar 20 2020 at 22:15):

Does anyone want to teach python to lean? I agree the error messages are terrible

Kevin Buzzard (Mar 20 2020 at 22:15):

A lot of the kids know python anyway, at least mathematicians do, they are taught it at my university

Mario Carneiro (Mar 20 2020 at 22:15):

I think coq uses some kind of markdown list thing

Patrick Massot (Mar 20 2020 at 22:17):

I like Lean's {/}, but my teaching taught me bad habits.

Patrick Massot (Mar 20 2020 at 22:18):

I need to go, so I'll post spoilers for Daniel:
.
.
.
.
.
.
.
.
.
.
.
.

  intros S hS,
  have H1 :  x  A, S - x  upper_bounds B,
  { intros x hx y hy,
    suffices : x + y  S, by linarith, -- by rwa le_sub_iff_add_le',
    exact hS x, hx, y, hy, rfl, },
  have H2 : S - b  upper_bounds A,
  { intros x hx,
    suffices : b  S - x, by linarith, -- by rwa le_sub,
    exact hB.2 (H1 x hx) },
  linarith [hA.2 H2], --exact le_sub_iff_add_le.mp (hA.2 H2),

The commented our version are only for Kevin's pleasure. I don't see any point in learning those lemmas.

Kevin Buzzard (Mar 20 2020 at 22:24):

I was going to see if I could take Daniel to the full term proof, and there they're important; we don't enter tactic mode at all there, so you can really see that the proof is just a function. This is kind of a startling revelation to a mathematician.

Kevin Buzzard (Mar 20 2020 at 22:43):

I don't see any point in learning those lemmas.

Another reason for showing the ctrl-space technique is that even though of course you're right that linarith will prove all of them, it is generally a very important technique, especially when you're in situations like trying to figure out how to prove a \in {a}, where what I explained is exactly how a beginner should find that proof (mem_singleton-ctrl-space)

Stephanie Zhou (Mar 21 2020 at 00:57):

How do I use e (as in Euler's number) in lean?

Mario Carneiro (Mar 21 2020 at 02:29):

I don't think e has been defined, but you can use real.exp 1 if you import data.complex.exponential

Paul van Wamelen (Mar 21 2020 at 17:41):

The following:

import data.int.gcd
import tactic

example (a b : ) (h : nat.gcd (2 * a) b = 1) : 1 = (2 * a : ) * nat.gcd_a (2 * a) b + b * nat.gcd_b (2 * a) b :=
begin
    rw nat.gcd_eq_gcd_ab (2 * a) b, simp [h]
end

fails at the rw because lean can't match ↑(2 * a) with 2 * ↑a. I can do:

example (a b : ) (h : nat.gcd (2 * a) b = 1) : 1 = (2 * a : ) * nat.gcd_a (2 * a) b + b * nat.gcd_b (2 * a) b :=
begin
    let a2 := 2 * a,
    change 1 = (a2 : ) * nat.gcd_a a2 b + b * nat.gcd_b a2 b,
    rw nat.gcd_eq_gcd_ab (2 * a) b, simp [h]
end

Is there a better way? (I should probably ask: what is the better way :smiley: )

Kevin Buzzard (Mar 21 2020 at 17:43):

One question might be: if you're interested in integers, why are you using naturals at all?

Kevin Buzzard (Mar 21 2020 at 17:44):

But there is a tactic which can help called norm_cast if you really want to mix your types like this

Kevin Buzzard (Mar 21 2020 at 17:45):

I would imagine that norm_cast will turn \u(2*a) into 2*\u a

Paul van Wamelen (Mar 21 2020 at 18:00):

Ugh, I should have been able to do that myself. norm_cast works. Thanks!!
Is there an int version of gcd_eq_gcd_ab? Well, even gcd_eq_gcd_ab is in an int file. Oh, the more general statement is probably an euclidean_domain thing. I'll find it.

Daniel Keys (Mar 21 2020 at 18:25):

I have this in my context:

m : ,
h51 : 0  m - 1,
h6b : m  m - 1
 false

but am not able to satisfy the goal; linarith, dec_trivial, norm_num and everything else I tried all fail. I suppose this may be due to a type conversion problem, as m-1 may be seen as an integer by Lean. Any hint on how to proceed appreciated!

Kevin Buzzard (Mar 21 2020 at 18:26):

You can't solve that goal :-(

Kevin Buzzard (Mar 21 2020 at 18:26):

If m = 0 then all the hypotheses are true

Kevin Buzzard (Mar 21 2020 at 18:27):

#eval (0 : ℕ) - 1 -- 0

Kevin Buzzard (Mar 21 2020 at 18:27):

It's Patrick's favourite Lean fact.

Patrick Stevens (Mar 21 2020 at 18:29):

There's a good reason every introductory text I've ever seen to Peano arithmetic defines - with a superscript dot, because it's very much not the same as - :P really it's the Billion Dollar Mistake again, we (= the world) shouldn't pun between N and Maybe N in this way

Kevin Buzzard (Mar 21 2020 at 18:29):

#print has_sub
/-
@[class]
structure has_sub : Type u → Type u
fields:
has_sub.sub : Π {α : Type u} [c : has_sub α], α → α → α
-/

The subtraction typeclass in Lean is a notation typeclass, and subtraction itself has type α → α → α which means that whatever you're subtracting, the two inputs and the output have to have the same type. So there's no way around it, if you want subtraction on the naturals then it has to return a natural.

Kevin Buzzard (Mar 21 2020 at 18:30):

I have pushed in the past for 0 - 1 to be 37 but the idea never gained any traction.

Daniel Keys (Mar 21 2020 at 18:31):

But things would change if I switched to int instead, right?

Kevin Buzzard (Mar 21 2020 at 18:32):

Indeed

Patrick Stevens (Mar 21 2020 at 18:32):

The Real Problem (tm) is that - just doesn't (morally) have type N -> N -> N; is there a good reason why it has that type in Lean?

Kevin Buzzard (Mar 21 2020 at 18:32):

I would not be arguing that the integer 0-1 should be 37

Kevin Buzzard (Mar 21 2020 at 18:32):

The answer used to be "because it's in core and we can't change core"

Kevin Buzzard (Mar 21 2020 at 18:33):

but even now it could be changed in theory, the CS people won't let you change it because they read the same books on Peano arithmetic that you did and they liked it

Patrick Stevens (Mar 21 2020 at 18:34):

There are many places where I have strong opinions but recognise that reasonable people may reasonably differ; but this is not one of them

Kevin Buzzard (Mar 21 2020 at 18:34):

A subtraction of the form A -> A -> B would probably cause a lot more problems than it solved.

Kevin Buzzard (Mar 21 2020 at 18:35):

For example if you wanted to prove a - b + b = a then there are problems because suddenly a -b has type B (or int or whatever) so now when you add b (which has type nat) you will have to coerce, and now you end up with the integer a

Marc Huisinga (Mar 21 2020 at 18:35):

i think that this is actually a pretty common theme in lean. requiring functions to be partial often results in needing lots of plumbing. instead we just define those functions to be some arbitrary value and demand some extra hypothesis in proofs to ensure that the case doesn't happen.

Daniel Keys (Mar 21 2020 at 18:35):

Kevin Buzzard said:

A subtraction of the form A -> A -> B would probably cause a lot more problems than it solved.

OK that makes sense - thanks a lot!

Kevin Buzzard (Mar 21 2020 at 18:35):

As a mathematician I want to believe that these coercions don't exist, but they are there and they cause problems for the CS people.

Patrick Stevens (Mar 21 2020 at 18:35):

Kevin Buzzard said:

A subtraction of the form A -> A -> B would probably cause a lot more problems than it solved.

To me, it's "completely obvious" that if you can prove a < b, then you should be using something of type (a b : N) -> (a <= b) -> N, and otherwise you should be using (a b : N) -> Maybe N

Kevin Buzzard (Mar 21 2020 at 18:36):

right -- that would be another approach, and when I started here I thought that this was manifestly the correct approach and all the CS people were crazy

Kevin Buzzard (Mar 21 2020 at 18:37):

but then I tried it in practice. When I started here there was no real.sqrt and I was teaching it in my class so I defined it in Lean and I my definition took as input a real number r and a proof that it was >= 0, and then it defined the square root to be some sup

Kevin Buzzard (Mar 21 2020 at 18:37):

and then a passing computer scientist came along and said "ha ha look at the silly mathematician, he is asking for an input to his function and then he never uses the input!"

Kevin Buzzard (Mar 21 2020 at 18:37):

and indeed, you could just remove the hypothesis as an input and the definition compiled just fine

Kevin Buzzard (Mar 21 2020 at 18:38):

and when I started thinking about it that way, I started having doubts

Patrick Stevens (Mar 21 2020 at 18:38):

But that's why God gave us the notion of irrelevance :P for when you need to tell things to the compiler which it would be annoying to express in any other way

Kevin Buzzard (Mar 21 2020 at 18:38):

and then when I started formalising all my problem sheet questions, and every time I even wrote the square root I had to prove that something was non-negative, so I would constantly be proving that 2>=0 when working with sqrt(2)

Kevin Buzzard (Mar 21 2020 at 18:39):

and in the end I got so sick of proving 2>=0 (this was before the days of norm_num so it wasn't even that easy) that I gave up and I dropped the hypothesis in the definition of sqrt, and instead I just put the hypothesis in the theorem statements

Kevin Buzzard (Mar 21 2020 at 18:40):

because the square root of every negative integer was coming out to be 37 so I needed it in the theorem statements

Kevin Buzzard (Mar 21 2020 at 18:40):

(well OK, it was coming out to be 0)

Kevin Buzzard (Mar 21 2020 at 18:40):

but I was having to prove 2>=0 an order of magnitude fewer times this way

Patrick Stevens (Mar 21 2020 at 18:40):

Fair enough, though I weep to hear that things might simply have to be this way

Kevin Buzzard (Mar 21 2020 at 18:40):

and then I just realised that I had defined a square root with a dot on it

Kevin Buzzard (Mar 21 2020 at 18:41):

and somehow that made me feel a bit better

Daniel Keys (Mar 21 2020 at 18:42):

It is interesting to see how these very simple textbook problems (in this case, I was trying to prove a positive real number has a floor and a ceiling in the naturals) lead to such discussions and insight.

Marc Huisinga (Mar 21 2020 at 18:44):

Patrick Stevens said:

Fair enough, though I weep to hear that things might simply have to be this way

it's a fun experiment to define something new and carry around some invariant together with data! i think that even something as simple as Maybe will quickly lead to headaches.
that being said, there are cases where the invariant is carefully maintained by the library, and you won't have to deal with the pain if what you want is in the library.
e.g. https://github.com/leanprover-community/mathlib/blob/master/src/data/finset.lean

Johan Commelin (Mar 21 2020 at 19:16):

@Patrick Stevens I know exactly how you feel. It's really quite crazy.

Johan Commelin (Mar 21 2020 at 19:18):

In the end it boils, I've come to realise that it boils down to a pragmatic usability argument.
And like Kevin said. Just imagine that there is a little dot written above almost every function that you see...

Johan Commelin (Mar 21 2020 at 19:18):

/, and ^-1 (inversion) and sqrt etc... they are all cheating on inputs where mathematicians would say they are undefined.

Patrick Stevens (Mar 21 2020 at 20:03):

I don't reeeallly mind the cheating - I have aesthetic objections, but really the thing I actually care about is "will this lead me to make mistakes", and I'm liable to make lots of mistakes if the types don't constrain me :P

Daniel Keys (Mar 21 2020 at 20:08):

Still having trouble even with int, shouldn't this goal follow from h6 below?

x : ,
m : ,
h6 : (m - 1)  x
 m - 1  x

Where can a type mismatch arise from here? Trying exact h6 I get:

invalid type ascription, term has type
  ↑(m - 1) ≤ x
but is expected to have type
  ↑m - 1 ≤ x

Kevin Buzzard (Mar 21 2020 at 20:35):

The problem is that it's not exactly h6 :-)

Kevin Buzzard (Mar 21 2020 at 20:36):

The order of the subtraction and the coercion are different, right? The brackets are in different places

Kevin Buzzard (Mar 21 2020 at 20:37):

Just do convert h6. That will probably change the goal to the assertion that the coercion commutes with subtraction

Kevin Buzzard (Mar 21 2020 at 20:37):

Then you can probably use norm_cast to finish the job

Kevin Buzzard (Mar 21 2020 at 20:38):

Or perhaps norm_cast will change either the goal into the hypothesis or vice versa

Daniel Keys (Mar 21 2020 at 20:39):

It worked! I had tried some 20 other ways.

Johan Commelin (Mar 21 2020 at 21:21):

Patrick Stevens said:

I don't reeeallly mind the cheating - I have aesthetic objections, but really the thing I actually care about is "will this lead me to make mistakes", and I'm liable to make lots of mistakes if the types don't constrain me :P

Well, the first thing that you do after your definition, is prove a lemma that under the assumption that the input makes sense, the definition behaves the way it should. Once you've proven that lemma, you can't really make mistakes.

Patrick Massot (Mar 21 2020 at 21:22):

Even before that you cannot really make a mistake in a sense. As worse you can state and prove something which is not what you have in mind.

Johan Commelin (Mar 21 2020 at 21:23):

Well, that depends on what you mean with mistake...

Johan Commelin (Mar 21 2020 at 21:23):

lemma fermat_last_thm : true := trivial

is a mistake, in my book.

Patrick Massot (Mar 21 2020 at 21:24):

This is completely independent from our discussion

Patrick Massot (Mar 21 2020 at 21:25):

You can write:

import data.real.basic

example : (1 : )/0 = 0 := by simp

and call it a mistake or a misunderstanding of the statement, depending on your point of view.

Kevin Buzzard (Mar 21 2020 at 22:10):

example :  (n : ), 1 / n = 0 := 0, rfl

This is an example which every mathematician would say was false, but there's a proof.

Stephanie Zhou (Mar 22 2020 at 01:25):

Mario Carneiro said:

I don't think e has been defined, but you can use real.exp 1 if you import data.complex.exponential

I can't import either of those, as lean says "not found in the LEAN_PATH". Actually, I'm getting this issue for "import standard" and a lot of other common import statements. Would you know how to resolve this?

Mario Carneiro (Mar 22 2020 at 01:28):

import standard doesn't exist AFAIK, but if import data.complex.exponential doesn't work then you most likely don't have mathlib installed. Could someone with leanproject experience give setup advice here?

Kenny Lau (Mar 22 2020 at 01:28):

https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/Inequality.20World/near/190568509

Mario Carneiro (Mar 22 2020 at 01:30):

@Patrick Stevens By the way, there is a function called nat.psub : nat -> nat -> option nat that actually returns none when it's not supposed to be defined. You have to write funny monadic code if you want to compose it with other operations though.

Alex Mathers (Mar 22 2020 at 07:00):

I'm looking at the files defining submonoids, and I'm trying to understand some Lean notation. I see the following

instance has_mul : has_mul S := λ a b, a.1 * b.1, S.mul_mem a.2 b.2⟩⟩

and I'm trying to understand what the a.1 and a.2 mean. When I try to evaluate to see what these mean here is what I see:

variables (S : submonoid M) (a : S)
#check a     --- a : ↥S
#check a.1     --- a.val : M
#check a.2     --- a.property : (λ (x : M), x ∈ has_coe_t_aux.coe (set M) S) (a.val)

so I take it it has to do with the coercion from S to M. But what is really happening here?

Mario Carneiro (Mar 22 2020 at 07:03):

a has type ↥S, which is actually {x : M // x \in S}. So a is a pair of an element of M and a proof that that element is in S

Mario Carneiro (Mar 22 2020 at 07:05):

You can also write \u a instead of a.1 to produce the element of M

Mario Carneiro (Mar 22 2020 at 07:05):

or just a, since the coercion is implicitly inserted

Alex Mathers (Mar 22 2020 at 07:05):

I see! Thanks.

Mario Carneiro (Mar 22 2020 at 07:05):

the idea with all the notations is to try to make this act as much like a subset as possible

Daniel Keys (Mar 22 2020 at 21:28):

Does anyone know a short way to obtain a contradiction here, or should I dig further into where uniqueness of the supremum, lub, resides?

D : is_lub A 3,
E : is_lub A 0
 false

Daniel Keys (Mar 22 2020 at 21:30):

Oh, I got it. It is in the same file, bounds.lean. Can easily use eq_of_is_lub_of_is_lub.

Kevin Buzzard (Mar 22 2020 at 21:30):

It looks to me like it would be worth proving the lemma that if A has two least upper bounds then they are equal, or (if your definition of is_lub is from the library and not home-rolled) using the result in mathlib (assuming it's there, which it surely will be). If you're using mathlib's is_lub (assuming it has one) then just take a look at the 100 lines of code after the definition.

Kevin Buzzard (Mar 22 2020 at 21:31):

The rule is: if it's a standard result about a definition in the library, then it's proved in the library, probably not too far after the definition.

Stephanie Zhou (Mar 23 2020 at 18:07):

Does lean support trig functions and power series?

Johan Commelin (Mar 23 2020 at 18:09):

Trig functions: yes. Power series: to some extent.

Patrick Massot (Mar 23 2020 at 18:11):

Note that the question is not quite right. Lean itself virtually supports any mathematics, and actually knows very little. Then formalizations written in Lean know some stuff. The big one is mathlib, which is meant to be the base for all other mathematics developments, and this is what Johan's answer refer to.

Stephanie Zhou (Mar 23 2020 at 18:16):

What is the command for sin and cos?

Patrick Massot (Mar 23 2020 at 18:17):

Do you have a working Lean project together with a compiled mathlib?

Stephanie Zhou (Mar 23 2020 at 18:18):

Yes

Patrick Massot (Mar 23 2020 at 18:19):

import analysis.complex.exponential
open complex
#check sin

Reed Mullanix (Mar 23 2020 at 18:47):

Hey all, stupid question: Does lean have some analog to agda's interactive holes?

Alex J. Best (Mar 23 2020 at 18:51):

I don't know agda, but from a quick google, it sounds like _ is the replacement of agda's ?, in that you can write terms with _ to see what the type of _ should be and then fill it in.

Bryan Gin-ge Chen (Mar 23 2020 at 18:51):

Lean does support "hole commands" but they're not as easy to use as Agda's. I don't think we have anything like Agda's Auto, for instance. (**Edit: on second thought library_search is kind of like Auto.)

Reed Mullanix (Mar 23 2020 at 18:52):

Hmm, I tried that, and it looks like the typechecker got stuck

 types.lean    42  95 error           type mismatch, term
   congr_fun ?m_5 ?m_6
 has type
   ?m_3 ?m_2 = ?m_4 ?m_2
 but is expected to have type
   σ.app Y (F.map f x) = G.map f (σ.app X x) (lean-checker)

Alex J. Best (Mar 23 2020 at 18:52):

What were you trying to do?

Reed Mullanix (Mar 23 2020 at 18:54):

I'm just messing around with some existing proofs to try to get a better feel on how to use the system :)

For context, the proof in question is

lemma naturality (f : X ⟶ Y) (x : F.obj X) : σ.app Y ((F.map f) x) = (G.map f) (σ.app X x) := congr_fun _ _

From mathlib's category_theory/types.lean

Alex J. Best (Mar 23 2020 at 18:59):

I guess there are too many implicit things in that example, if you just write
lemma naturality (f : X ⟶ Y) (x : F.obj X) : σ.app Y ((F.map f) x) = (G.map f) (σ.app X x) := congr_fun _ x
you get to see what the type of _ should be

Reed Mullanix (Mar 23 2020 at 19:01):

Yeah, it barfs about not being able to synthesize a placeholder context, which isn't _super_ suprising, it's a pretty gnarly unification problem

Kevin Buzzard (Mar 23 2020 at 20:19):

If you write @f instead of f you can fill in some of the unification holes yourself

Reed Mullanix (Mar 23 2020 at 22:53):

More silly questions: I'm trying to use the rewrite tactic, and it's complaining that it can't find an instance of the pattern, even though there obviously is one:

 braided.lean    45   3 error           rewrite tactic failed, did not find instance of the pattern in the target expression
   (λ_ (X ⊗ 𝟙_ C)).hom ≫ (braid X (𝟙_ C)).hom
 state:
 C : Type u,
 _inst_1 : category C,
 _inst_2 : monoidal_category C,
 𝒞 : braided_category C,
 X : C
 ⊢ (((braid X (𝟙_ C)).hom ⊗ 𝟙 (𝟙_ C)) ≫ (α_ (𝟙_ C) X (𝟙_ C)).hom ≫ (λ_ (X ⊗ 𝟙_ C)).hom) ≫
       (braid X (𝟙_ C)).hom =
     ((ρ_ X).hom ⊗ 𝟙 (𝟙_ C)) ≫ (braid X (𝟙_ C)).hom (lean-checker)

Alex J. Best (Mar 23 2020 at 22:54):

Can you post full code? A minimal working example

Reed Mullanix (Mar 23 2020 at 22:55):

Of course! This is what I've got so far:

import category_theory.monoidal.category
import category_theory.natural_isomorphism

open category_theory

universes v u

namespace category_theory


class braided_category (C : Type u) [category.{v} C] [𝒞 : monoidal_category.{v} C] :=
(braid : Π X Y : C, (X ⊗ Y) ≅ (Y ⊗ X))
(braid_naturality' :
  ∀ {X₁ X₂ Y₁ Y₂} (f₁ : X₁ ⟶ Y₁) (f₂ : X₂ ⟶ Y₂),
  (f₁ ⊗ f₂) ≫ (braid Y₁ Y₂).hom = (braid X₁ X₂).hom ≫ (f₂ ⊗ f₁) . obviously)
-- first hexagon identity:
(hexagon' : ∀ X Y Z : C,
  (α_ X Y Z).hom ≫ (braid X (Y ⊗ Z)).hom ≫ (α_ Y Z X).hom
  = ((braid X Y).hom ⊗ (𝟙 Z)) ≫ (α_ Y X Z).hom ≫ ((𝟙 Y) ⊗ (braid X Z).hom) . obviously)
-- second hexagon identity:
(hexagon_inv' : ∀ X Y Z : C,
  (α_ X Y Z).inv ≫ (braid (X ⊗ Y) Z).hom ≫ (α_ Z X Y).inv
  = ((𝟙 X) ⊗ (braid Y Z).hom) ≫ (α_ X Z Y).inv ≫ ((braid X Z).hom ⊗ (𝟙 Y)) . obviously)

restate_axiom braided_category.braid_naturality'
restate_axiom braided_category.hexagon'
restate_axiom braided_category.hexagon_inv'

open monoidal_category
open braided_category

namespace braided_category

variables {C : Type u} [category.{v} C] [monoidal_category.{v} C] [𝒞 : braided_category.{v} C]
include 𝒞

section

lemma braid_coherence {X : C} :
  ((braid X (𝟙_ C)).hom ⊗ 𝟙 (𝟙_ C)) ≫ ((left_unitor X).hom ⊗ 𝟙 (𝟙_ C)) = ((right_unitor X).hom ⊗ 𝟙 (𝟙_ C)) :=
begin
  apply (cancel_mono (braid X (𝟙_ C)).hom).1,
  rw [←left_unitor_tensor, ←left_unitor_naturality C (braid X (𝟙_ C)).hom],
  sorry
end

end

end braided_category

end category_theory

Reed Mullanix (Mar 23 2020 at 22:58):

I suspect that it may have something to do with associativity actually, missed a pair of parens :face_palm:

Alex J. Best (Mar 23 2020 at 23:00):

Ah okay, does that fix it then?

Alex J. Best (Mar 23 2020 at 23:01):

In general if rw is having trouble you can use simp only [your_eq] or use conv to isolate the part you want to rewrite.

Reed Mullanix (Mar 23 2020 at 23:02):

Mucking about to figure out how to get assoc into scope, but I suspect that will do it. Thanks for the help :slight_smile:

Kevin Buzzard (Mar 23 2020 at 23:14):

\gg is right associative:

import category_theory.functor

#print notation 
-- _ `≫`:80 _:79 := category_theory.category_struct.comp #1 #0

80>79

Kevin Buzzard (Mar 23 2020 at 23:15):

@Reed Mullanix if you post code with ```lean at the top then you get syntax highlighting.

Alex Mathers (Mar 25 2020 at 01:14):

How are powers of elements (say elements in a monoid or group) defined in Lean? In this file (https://github.com/leanprover-community/mathlib/blob/master/src/group_theory/submonoid.lean) where submonoids are defined, they use expressions like x^n but searching around I don't see where this notation is first implemented. When I try to type it into Lean myself it looks like it's notation for has_pow M ℕ, so were monoids at some point endowed with this has_pow attribute and I just don't see where?

Shing Tak Lam (Mar 25 2020 at 01:17):

https://github.com/leanprover-community/mathlib/blob/24b82c91583843597e8cfeb7928d446dec776456/src/algebra/group_power.lean#L20

(the docs link below is probably better)

Alex J. Best (Mar 25 2020 at 01:18):

https://leanprover-community.github.io/mathlib_docs/algebra/group_power.html#monoid.has_pow

Alex J. Best (Mar 25 2020 at 01:18):

Too quick for me ;)

Shing Tak Lam (Mar 25 2020 at 01:19):

I just Ctrl-P monoid.pow and I had mathlib open on GitHub already :) The docs link is probably the better one though.

Alex Mathers (Mar 25 2020 at 01:21):

Not sure how I missed that there's an entire file titled group_power! Thanks

Shing Tak Lam (Mar 25 2020 at 01:21):

Are you on VSCode?

Alex J. Best (Mar 25 2020 at 01:22):

Yeah I just run a search in vscode for instance.*monoid.*has_pow

Alex Mathers (Mar 25 2020 at 01:32):

I am on VSCode, I had no idea I could do that

Shing Tak Lam (Mar 25 2020 at 01:34):

Or you can just press Ctrl-P then type in # in the search bar. It searches for whatever comes after the # in mathlib. So I just searched for monoid.pow. You could have also (probably a better idea) searched for monoid.has_pow.

Alex Mathers (Mar 25 2020 at 01:37):

This is definitely good information I was lacking. Thank you both!

Miguel Raz Guzmán Macedo (Mar 25 2020 at 05:11):

Hey all, I screwed up the basic install on fedora and lean in VSCODE says file topology/basic not found in teh LEAN_PATH

Miguel Raz Guzmán Macedo (Mar 25 2020 at 05:11):

Any help?

Yury G. Kudryashov (Mar 25 2020 at 05:14):

Which instructions did you follow? Are you editing a file in mathlib, or using it as a dependency?

Miguel Raz Guzmán Macedo (Mar 25 2020 at 05:34):

@Yury G. Kudryashov I tried the pip3 install mathlibtools for installing the leanproject, but it didn't seem to install.
I can use lean and leanpkg just fine, they've been updated to 3.7 no problem.

Bryan Gin-ge Chen (Mar 25 2020 at 07:09):

Not sure if this is the problem, since you haven't answered Yury's second question, but note that you should not open a single Lean file in VS Code, you need to open a directory containing a Lean project. Check the instructions here if you haven't already.

Patrick Massot (Mar 25 2020 at 16:26):

Miguel Raz Guzmán Macedo said:

I tried the pip3 install mathlibtools for installing the leanproject, but it didn't seem to install.

There is no way anyone could help you based on such vague information.

Johan Commelin (Mar 25 2020 at 16:32):

@Miguel Raz Guzmán Macedo As user or as root? Maybe prepend sudo or use pip3 install --user.

Patrick Massot (Mar 25 2020 at 16:43):

Johan, you are trying to guess too much. Maybe install worked but leanproject is not in the PATH. We can't know without further information;

Miguel Raz Guzmán Macedo (Mar 25 2020 at 17:43):

Thanks for the replies !
@Johan Commelin I tried both already. I found leanproject.py in my /usr/local/lib/python3.7/site-packages-mathlibtools/leanproject.py, but the Installation instructions didn't say anything about adding that manually to the path, so lemme try that.

Patrick Massot (Mar 25 2020 at 17:44):

You should not add that to your path

Kevin Buzzard (Mar 25 2020 at 17:45):

We are still keen to see the error message.

Kevin Buzzard (Mar 25 2020 at 17:46):

and your answer to Yury's second question.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 17:48):

I am not trying to edit a file in mathlib, I am just trying to run the tutorial here (the topological_space check)

Miguel Raz Guzmán Macedo (Mar 25 2020 at 17:49):

Thank you all for your time and patience by the way, it means a lot to help on-ramping beginners :+1:

Patrick Massot (Mar 25 2020 at 17:49):

How did you create a project?

Miguel Raz Guzmán Macedo (Mar 25 2020 at 17:49):

@Johan Commelin if I run sudo pip3 install mathlibtools, it says all of the requirements for all the packages are satisfied.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 17:50):

@Patrick Massot I had used leanpkg instead of leanproject, I am trying to use leanproject now.

Patrick Massot (Mar 25 2020 at 17:51):

Can you run leanproject now? (without adding weird things to your path)

Mario Carneiro (Mar 25 2020 at 17:51):

Hey, now that we have control over lean, we should REALLY remove that misleading error message that says "add this to your LEAN_PATH"

Miguel Raz Guzmán Macedo (Mar 25 2020 at 17:52):

Patrick Massot said:

Can you run leanproject now? (without adding weird things to your path)

No, in a new terminal leanproject does says
fish: leanproject command not found

Miguel Raz Guzmán Macedo (Mar 25 2020 at 17:54):

I am guessing leanproject should have been installed when I did sudo pip3 install mathlibtools?

Patrick Massot (Mar 25 2020 at 17:56):

Yes, it should be in /usr/local/bin/leanproject (at least assuming a Debian-like OS)

Miguel Raz Guzmán Macedo (Mar 25 2020 at 17:58):

Sorry, there's no leanproject there :(

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:00):

If I try to do pip3 install --user mathlibtools I get this error at the end -

PermissionError: [Errno 13] Permission denied: '/usr/local/lib64/python3.7/site-packages/wrapt-1.12.1-py3.7.egg-info'

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:01):

And it is entirely possible I may have bungled up my entire environment, so suggestions are welcome.

Patrick Massot (Mar 25 2020 at 18:01):

It very much looks like your python environment is destroyed. What is your OS/distrib?

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:01):

fedora latest.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:06):

I tried to chmod -R au+x ..../site-packages/ and now I get
Permission denied: '/usr/local/lib/python3.7/site-packages/tqdm-4.43.0.dist-info'

Patrick Massot (Mar 25 2020 at 18:10):

You shouldn't randomly change permissions in /usr/local

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:10):

Oh.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:11):

:S

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:15):

Well now pip3 install --user mathlibtools works, and all requirements are already satisfied :D

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:41):

Aha, it seems I needed to add ~/.local/bin to path, uninstalling and installing seems to have suggested that and leanproject new leanjl now works. Thank you all for the help on that.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:44):

Well, import topology.basic still says file 'init' not found in the LEAN_PATH

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:45):

test.lean:1:0: error
file 'init' not found in the LEAN_PATH
test.lean:1:0: error
invalid import: init
could not resolve import: init
test.lean:1:0: error
invalid import: topology.basic
/home/mrg/deps/lean/mathlib/src/topology/basic.lean:48:25: error: unexpected token

Kevin Buzzard (Mar 25 2020 at 18:54):

So now you need to answer the question about whether you opened the project directory with the Open Folder command, or just a random file.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:55):

Ah, sorry.

Kevin Buzzard (Mar 25 2020 at 18:56):

You need to open the root directory of the project in VS code. You're nearly there

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:56):

I did leanproject new leanjl to start a new project, opened it with VSCode via code leanjl, and added a file in src

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:56):

just called test.lean.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 18:59):

and that's where I put the

import topology.basic

#check topological_space

Kevin Buzzard (Mar 25 2020 at 19:26):

I can't reproduce. I just did exactly that and I get topological_space : Type u_1 → Type u_1

Kevin Buzzard (Mar 25 2020 at 19:27):

leanjl.png

Kevin Buzzard (Mar 25 2020 at 19:29):

VS Code View -> Terminal followed by lean --version gives me Lean (version 3.7.2, commit 44fb9f994d0f, Release) . My leanpkg.path is

builtin_path
path _target/deps/mathlib/src
path ./src

and my leanpkg.toml is

[package]
name = "leanjl"
version = "0.1"
lean_version = "leanprover-community/lean:3.7.2"
path = "src"

[dependencies]
mathlib = {git = "https://github.com/leanprover-community/mathlib", rev = "24b82c91583843597e8cfeb7928d446dec776456"}

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:39):

Thanks a lot for the follow - through @Kevin Buzzard .
I get the exact same configs down to the hashes for lean 3.7.2 and mathlib and both files are identical.

Kevin Buzzard (Mar 25 2020 at 19:43):

cool

Kevin Buzzard (Mar 25 2020 at 19:44):

you beat the system

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:45):

If I had a penny...

Kevin Buzzard (Mar 25 2020 at 19:48):

Are you absolutely sure that your VS Code looks exactly the same as my screenshot?

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:48):

VSCode is maybe picking up a previous install of lean:
I get this error there as well:

invalid import: topology.basic
/home/mrg/deps/lean/mathlib/src/topology/basic.lean:48:25: error: unexpected token

Kevin Buzzard (Mar 25 2020 at 19:49):

That is definitely not the mathlib you are looking for

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:50):

imma nuke it brb.

Kevin Buzzard (Mar 25 2020 at 19:50):

the mathlib you're supposed to be using is in _target in your project folder

Kevin Buzzard (Mar 25 2020 at 19:51):

If you had posted that error much earlier then life would have been a bit easier. Can you post all the details of every error which you are currently experiencing?

Kevin Buzzard (Mar 25 2020 at 19:51):

Exit VS Code and start it again and post all errors which occur anywhere.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:52):

Miguel Raz Guzmán Macedo said:

test.lean:1:0: error
file 'init' not found in the LEAN_PATH
test.lean:1:0: error
invalid import: init
could not resolve import: init
test.lean:1:0: error
invalid import: topology.basic
/home/mrg/deps/lean/mathlib/src/topology/basic.lean:48:25: error: unexpected token

I did post it earler - I should have read more closely.

Kevin Buzzard (Mar 25 2020 at 19:52):

Where are you seeing this error?

Kevin Buzzard (Mar 25 2020 at 19:53):

Do you have a LEAN_PATH variable set? If so, nuke it.

Kevin Buzzard (Mar 25 2020 at 19:53):

unset LEAN_PATH

Kevin Buzzard (Mar 25 2020 at 19:54):

then restart VS Code and see if the error has changed

Kevin Buzzard (Mar 25 2020 at 19:54):

(from the same terminal where you nuked LEAN_PATH)

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:55):

DING DING DING

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:55):

I had monkey-patched an earlier version of LEAN_PATH in my configs and it was polluting the env.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:55):

I now get a very happy topological_space : Type u_1 → Type u_1

Kevin Buzzard (Mar 25 2020 at 19:56):

That's great to know. That one was a real toughie. LEAN_PATH was something which you had to worry about in 2018.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:56):

Huh, yeah, I think that was my first install attempt or so.

Kevin Buzzard (Mar 25 2020 at 19:57):

So it will be rare that users run into this problem but perhaps not impossible. Assuming you rebooted your computer in the past few years you might want to try and figure out what is setting LEAN_PATH.

Kevin Buzzard (Mar 25 2020 at 19:57):

(unless you were setting it yourself)

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:57):

Thank you all a lot @Johan Commelin @Patrick Massot and @Kevin Buzzard , that was very attentive of all of you.

Kevin Buzzard (Mar 25 2020 at 19:57):

The perfectoid project team -- installing mathlib on a computer near you.

Miguel Raz Guzmán Macedo (Mar 25 2020 at 19:58):

Kevin Buzzard said:

So it will be rare that users run into this problem but perhaps not impossible. Assuming you rebooted your computer in the past few years you might want to try and figure out what is setting LEAN_PATH.

Nope, all me and my footguns.

Kevin Buzzard (Mar 25 2020 at 19:59):

Wherever did you read about LEAN_PATH? We should nuke the reference

Kevin Buzzard (Mar 25 2020 at 20:00):

By the way I think you just accidentally created a new stream. Usually we talk in threads in the streams we already have.

Bryan Gin-ge Chen (Mar 25 2020 at 20:48):

Wherever did you read about LEAN_PATH?

Whenever Lean is unable to find an imported file, it says "error: file 'does/not/exist' not found in the LEAN_PATH". People might read this and think they need to set LEAN_PATH, when that's rarely the right thing to do.

Bryan Gin-ge Chen (Mar 25 2020 at 20:49):

It wouldn't be hard to change the message. Any suggestions?

Kevin Buzzard (Mar 25 2020 at 20:55):

"error: file 'does/not/exist' not found" would already be strictly better, given what we just went through.

Kevin Buzzard (Mar 25 2020 at 20:56):

The moment you mention leanpkg.path we'll have people changing that.

Kevin Buzzard (Mar 25 2020 at 20:56):

which is also rarely the right thing to do.

Mario Carneiro (Mar 25 2020 at 20:56):

How about "lean file does.not.exist not found. We searched: <print result of lean --path>"

Kevin Buzzard (Mar 25 2020 at 20:57):

Now that might even be a helpful message!

Kevin Buzzard (Mar 25 2020 at 20:57):

Actually on looking at the output

{
  "is_user_leanpkg_path": true,
  "leanpkg_path_file": "/home/buzzard/.lean/leanpkg.path",
  "path": [
    "/home/buzzard/.elan/toolchains/stable/bin/../library",
    "/home/buzzard/.elan/toolchains/stable/bin/../lib/lean/library"
  ]
}

I am suddenly less sure.

Mario Carneiro (Mar 25 2020 at 20:58):

Well, lean --path is json formatted for no good reason

Mario Carneiro (Mar 25 2020 at 20:58):

we can print the same info in a more human readable way

Mario Carneiro (Mar 25 2020 at 21:00):

It might be sufficient to just focus on (1) whether a leanpkg.path file could be found, and (2) what the location and contents of the path file are

Kevin Buzzard (Mar 25 2020 at 21:00):

{
  "is_user_leanpkg_path": false,
  "leanpkg_path_file": "/home/buzzard/lean-projects/lean-perfectoid-spaces/leanpkg.path",
  "path": [
    "/home/buzzard/.elan/toolchains/leanprover-community-lean-3.5.1/bin/../library",
    "/home/buzzard/.elan/toolchains/leanprover-community-lean-3.5.1/bin/../lib/lean/library",
    "/home/buzzard/lean-projects/lean-perfectoid-spaces/_target/deps/mathlib/src",
    "/home/buzzard/lean-projects/lean-perfectoid-spaces/./src"
  ]
}

Here's a more accurate representation (I ran lean --path in a project). I still think it's a risk mentioning leanpkg.path but perhaps that vector of paths is something worth printing out?

Mario Carneiro (Mar 25 2020 at 21:01):

the idea here is to get the low level information about where lean is looking

Kevin Buzzard (Mar 25 2020 at 21:01):

why is it not a user leanpkg path?

Mario Carneiro (Mar 25 2020 at 21:01):

we can also include more suggestive comments about checking their toml file and so on

Kevin Buzzard (Mar 25 2020 at 21:01):

When I tried not in a project I got is_user_leanpkg_path true and in a project I get that it's false?

Mario Carneiro (Mar 25 2020 at 21:02):

I think is_user_leanpkg_path means it is using the global leanpkg.path file in your home directory

Bryan Gin-ge Chen (Mar 25 2020 at 22:37):

One wrinkle is that Lean currently spits out a separate message for each file it can't find, so we may need to change the error handling somehow so that we don't repeat the same helpful message over and over.

Stephanie Zhou (Mar 26 2020 at 01:19):

How does one use imaginary numbers in lean?

Yury G. Kudryashov (Mar 26 2020 at 03:07):

Complex numbers are defined in data.complex. What do you want to prove about them?

Alex Mathers (Mar 26 2020 at 08:10):

Can anybody tell me the problem with some code? for the purposes of practicing some things I have defined subgroups in the "bundled" way and am trying to define the intersection of a family of subgroups (really just the underlying set at this point) but I'm getting a lot of unexpected error messages. My code looks like this

def Inf {ι : Type*} (s : ι  subgrp G) : subgrp G :=
{carrier := ( i, s i),
mult_mem' := sorry,
inv_mem' := sorry
}

and the error messages that come up are:

grps.lean:221:15: error
unknown identifier 'i'
grps.lean:221:16: error
invalid expression, `)` expected
grps.lean:221:20: error
invalid structure instance, ':=' expected
grps.lean:221:21: error
invalid structure instance, '}' expected
grps.lean:221:21: error
command expected

edit: also as some extra comments, I'm basing the omission of any kind of explicit i \in \io off of what I've seen in mathlib, and adding it doesn't seem to change anything. And I have the coercion set up so that Lean understands it is really taking the intersection of the underlying "carrier" sets, so I don't think the issue is there.

Mario Carneiro (Mar 26 2020 at 08:21):

I think you have the wrong intersection symbol

Johan Commelin (Mar 26 2020 at 08:21):

Try \bigcap

Alex Mathers (Mar 26 2020 at 08:23):

That fixed it. So is \cap for two things and \bigcap for arbitrary intersections?

Stephanie Zhou (Mar 26 2020 at 13:11):

Yury G. Kudryashov said:

Complex numbers are defined in data.complex. What do you want to prove about them?

I'm trying to prove Euler's formula using polar coordinates, so I wanted to use i.

Johan Commelin (Mar 26 2020 at 13:12):

Have you found it?

Kevin Buzzard (Mar 26 2020 at 13:12):

Is that theorem already in Lean? You should check the docs.

Kevin Buzzard (Mar 26 2020 at 13:12):

https://leanprover-community.github.io/mathlib_docs/

Kevin Buzzard (Mar 26 2020 at 13:16):

I found it -- it's here

 theorem complex.exp_mul_I (x : ℂ) :
(x * complex.I).exp = x.cos + x.sin * complex.I

I can live with complex.I but do we really want x.cos rather than cos(x)\cos(x) in the docs? Can we have maths mode docs?

Kevin Buzzard (Mar 26 2020 at 13:16):

@Patrick Massot how does that work?

Kevin Buzzard (Mar 26 2020 at 13:17):

And where does the Wikipedia link to Euler's formula fit into the docs?

Kevin Buzzard (Mar 26 2020 at 13:17):

@Rob Lewis ?

Mario Carneiro (Mar 26 2020 at 13:19):

presumably on the relevant formula...?

Mario Carneiro (Mar 26 2020 at 13:20):

there is no rule saying you can't put doc strings on theorems, that's just systematic laziness on our part

Kevin Buzzard (Mar 26 2020 at 13:23):

Can a docstring have a URL?

Rob Lewis (Mar 26 2020 at 13:24):

Sure, just put it in angle brackets. <www.kevin.com>

Kevin Buzzard (Mar 26 2020 at 13:24):

I just want the docstring to say "<link>Euler's formula</link>. The statement that eiθ=cos(θ)+isin(θ)e^{i\theta}=\cos(\theta)+i\sin(\theta)"

Rob Lewis (Mar 26 2020 at 13:25):

Or [Euler's formula](www.kevin.com)

Kevin Buzzard (Mar 26 2020 at 13:25):

Can we get the docs to display maths mode somehow?

Kevin Buzzard (Mar 26 2020 at 13:25):

I want @Stephanie Zhou to solve her problem herself by just going to the docs and typing "Euler's formula" into the search box.

Kevin Buzzard (Mar 26 2020 at 13:26):

Note that there are about ten Euler's formulas.

Rob Lewis (Mar 26 2020 at 13:26):

They do, you asked for this a while back. $...$ inline, $$...$$ block, but there are potential bad interactions with markdown so don't put anything in math mode that's also valid markdown.

Kevin Buzzard (Mar 26 2020 at 13:28):

But then I think @Sebastien Gouezel complained that he didn't want to see $ signs cluttering up his docstrings when one could just as easily write eⁱᶿ = cos(θ) + i sin (θ) in unicode.

Kevin Buzzard (Mar 26 2020 at 13:29):

and I just look at that rendering and think "OMG it's Microsoft Word all over again"

David Wärn (Mar 28 2020 at 14:54):

example (α : Type) [nonempty α] (f : α  ) :  a,  b, f a  f b := sorry

Does this theorem have a name? library_search didn't succeed

Kevin Buzzard (Mar 28 2020 at 14:56):

I guess you can prove it with nat.find but I don't know the name.

Kevin Buzzard (Mar 28 2020 at 14:57):

there's probably a one-liner with some clever application of a well-ordering principle

David Wärn (Mar 28 2020 at 15:02):

Yes, it follows from well-foundedness of f a < f b and the fact that a nonempty well-founded type has a minimal element. But I also couldn't find this latter fact...

David Wärn (Mar 28 2020 at 15:03):

I guess that's "find" for general well-orders

Kevin Buzzard (Mar 28 2020 at 15:20):

Maybe this and nearby stuff in order.basic are useful?

Kenny Lau (Mar 28 2020 at 15:30):

what on earth is this mode? I keep learning new stuff about github

Kenny Lau (Mar 28 2020 at 15:34):

import order.complete_lattice

#check (by apply_instance : lattice.has_Inf )

Kenny Lau (Mar 28 2020 at 15:34):

why is there no such instance

Kenny Lau (Mar 28 2020 at 15:34):

oh because it doesn't have a top element

Kenny Lau (Mar 28 2020 at 15:34):

lattice is confusing

Kevin Buzzard (Mar 28 2020 at 15:37):

It's probably a semilattice_sup_bot or something

David Wärn (Mar 28 2020 at 15:38):

example (α : Type) [nonempty α] (f : α  ) :  a,  b, f a  f b := begin
  have : well_founded (λ a b, f a < f b), from measure_wf _,
  set h := well_founded.has_min this set.univ set.univ_nonempty,
  simpa using h,
end

This seems to work

Kevin Buzzard (Mar 28 2020 at 15:38):

ooh they're not going to like that non-terminal simp

Kevin Buzzard (Mar 28 2020 at 15:38):

Can you use simpa somehow?

David Wärn (Mar 28 2020 at 15:40):

Better now?

Kevin Buzzard (Mar 28 2020 at 15:40):

example (α : Type) [nonempty α] (f : α  ) :  a,  b, f a  f b := begin
  have : well_founded (λ a b, f a < f b), from measure_wf _,
  simpa using well_founded.has_min this set.univ set.univ_nonempty,
end

Kevin Buzzard (Mar 28 2020 at 15:40):

you beat me to it :-)

Alex Mathers (Mar 30 2020 at 05:44):

Is there any difference in practice between the following:

class unique_factorization_domain (α : Type*) [integral_domain α] :=
(factors : α  ... )
(factors_prod : ... )
(prime_factors : ... )

and

class unique_factorization_domain (α : Type*) extends integral_domain α :=
(factors : α  ... )
(factors_prod : ... )
(prime_factors : ... )

Just curious because I saw the former in a mathlib file whereas I'm used to seeing things like the latter

Mario Carneiro (Mar 30 2020 at 06:08):

Yes; while they basically perform the same function, there are a number of issues that come into play regarding which is better, mostly to do with how type class inference works

Mario Carneiro (Mar 30 2020 at 06:09):

The advice I usually give is to use extends unless the number of type arguments goes up (as in module which has two type arguments)

Alex Mathers (Mar 30 2020 at 06:17):

Perfect, I'll blindly stick to this then

Alex Mathers (Mar 31 2020 at 02:24):

what's this mean? or more importantly I guess, how do I "increase the setting option" as described?

maximum class-instance resolution depth has been reached (the limit can be increased by setting option 'class.instance_max_depth') (the class-instance resolution trace can be visualized by setting option 'trace.class_instances')

Bryan Gin-ge Chen (Mar 31 2020 at 02:25):

e.g. set_option class.instance_max_depth 100

Alex Mathers (Mar 31 2020 at 02:26):

Sweet. Should I be concerned or is this a normal adjustment to have to make?

Bryan Gin-ge Chen (Mar 31 2020 at 02:27):

It depends. If you share your code, we can make suggestions.

Kevin Buzzard (Mar 31 2020 at 07:18):

In my experience, the bug sometimes means "you have asked the type class inference system to do something super-complicated, which can be fixed with the set_option suggestion above" or "you have accidentally asked the type class inference system to do something impossible, your code is wrong and this error is a super-unhelpful way of informing you of this". Earlier you were getting errors about Lean not being able to find instances; this one might mean "you asked me to find an instance which is hard or impossible to find and I'm just getting confused".

Reid Barton (Mar 31 2020 at 15:04):

Yes, this is probably the second least predictive error message, after (deterministic) timeout

Miroslav Olšák (Apr 01 2020 at 09:00):

I wanted to add some shows to my code, and I am getting an error. Why is the show tactic failing in the following example? I thought it should succeed when I just copy the goal.

example (l : list ) : l.sum  0 :=
begin
  induction l,
  show list.sum list.nil  0,
  sorry, sorry,
end

Johan Commelin (Apr 01 2020 at 09:01):

It doesn't know what type of lists you want

Johan Commelin (Apr 01 2020 at 09:02):

Try show list.sum (list.nil : list int) ≥ 0

Johan Commelin (Apr 01 2020 at 09:02):

In fact, it will see the 0 at the RHS, and by default assume that this is (0 : nat)

Johan Commelin (Apr 01 2020 at 09:02):

Unless you already convinced it that you were talking about a different type.

Miroslav Olšák (Apr 01 2020 at 09:38):

Ah, the integers, thanks. show [].sum ≥ (0:ℤ) is working.

Shing Tak Lam (Apr 05 2020 at 13:55):

Is there a tactic that would solve goals like this? I tried hint, but that suggested ring, which didn't simplify things much (or at all).

(2 ^ 2 ^ succ n + 1) * (2 ^ 2 ^ succ n - 1) = 2 ^ 2 ^ succ (succ n) - 1

Patrick Massot (Apr 05 2020 at 13:59):

try ring_exp

Kevin Buzzard (Apr 05 2020 at 14:00):

Oh it's bloody nat subtraction again

Kevin Buzzard (Apr 05 2020 at 14:00):

example (n : ) :

(2 ^ 2 ^ succ n + 1) * (2 ^ 2 ^ succ n - 1) = 2 ^ 2 ^ succ (succ n) - 1 :=
begin
  repeat {rw nat.pow_succ},
  repeat {rw nat.pow_mul},
  generalize : 2 ^ 2 ^ n = t,
  -- ⊢ (t ^ 2 + 1) * (t ^ 2 - 1) = (t ^ 2) ^ 2 - 1
  ring, -- fails

Kevin Buzzard (Apr 05 2020 at 14:04):

import tactic

open nat

example (n : ) :
  (2 ^ 2 ^ succ n + 1) * (2 ^ 2 ^ succ n - 1) = 2 ^ 2 ^ succ (succ n) - 1 :=
begin
  repeat {rw nat.pow_succ},
  repeat {rw nat.pow_mul},
  generalize : (2 ^ 2 ^ n) ^ 2= t,
  cases t, refl,
  rw [succ_eq_add_one, nat.add_sub_cancel],
  ring,
end

@Shing Tak Lam the problem with a tactic is that you need to somehow insert the assertion that the subtraction doesn't give a junk answer. omega is good at this with very simple goals but this might be hard in general.

Kevin Buzzard (Apr 05 2020 at 14:06):

import tactic

open nat

example (n : ) :
  ((2 : ) ^ 2 ^ succ n + 1) * (2 ^ 2 ^ succ n - 1) = 2 ^ 2 ^ succ (succ n) - 1 :=
begin
  repeat {rw nat.pow_succ},
  repeat {rw pow_mul},
  ring,
end

Note the coercion in the statement: it's now a theorem about integers.

Shing Tak Lam (Apr 05 2020 at 14:22):

Kevin Buzzard said:

Oh it's bloody nat subtraction again

I know... Seems like everything I formalise I come across nat subtraction... Anyways, at least for this part (yes it's another random STEP question that I've come across), I think it's the same if I do it in the nats versus the ints, so I guess I can just change it to int. Although I might need data.nat.prime later on, so there may be issues if I switch.

I guess 2 ^ 2 ^ succ n > 1 is something that is obvious to me, but not obvious to lean.

Kevin Buzzard (Apr 05 2020 at 15:14):

It's not hard to prove, maybe even the monotonicity tactic will prove 2^2^(succ n)>2^2^0 or something

Asger Hautop Drewsen (Apr 06 2020 at 12:35):

At least in the "Natural number game" the "ring" tactic doesn't seem to be able to prove that

succ (a + b) = succ a + b

for a b : mynat.

Why is this?

Kevin Buzzard (Apr 06 2020 at 12:37):

ring will only work once it knows that the structure is a semiring. Once it knows that, it will only work on terms which only involve functions which rings have, and succ isn't one of these. If Lean already knows the natural numbers are a semiring then you could rewrite succ_eq_add_one and it should work.

Kenny Lau (Apr 06 2020 at 12:37):

proposal to add succ_eq_add_one into ring

Kevin Buzzard (Apr 06 2020 at 12:37):

what about mynat.succ_eq_add_one?

Kevin Buzzard (Apr 06 2020 at 12:38):

because I suspect that's the one we're talking about here.

Kenny Lau (Apr 06 2020 at 12:38):

fair enough

Asger Hautop Drewsen (Apr 06 2020 at 12:38):

Ah, that makes sense, this works:

repeat {rw succ_eq_add_one}, ring,

Donald Sebastian Leung (Apr 06 2020 at 12:39):

Perhaps related: the omega tactic (a tactic specific to the natural numbers / integers, based on a fragment of Peano arithmetic called Presburger arithmetic) should be able to solve this. You probably won't be able to use it in the NNG, but in an actual Lean development, you can require it with import tactic (given that you have installed mathlib)

Kevin Buzzard (Apr 06 2020 at 12:41):

I don't know if it's possible to make omega work on mynat. One should be able to give omega a term of type X \equiv+* nat and then it would work on X by magic :-)

Stephanie Zhou (Apr 06 2020 at 17:07):

How would I write out sin and cos as an infinite series? I can't seem to find infinite series in the docs

Kevin Buzzard (Apr 06 2020 at 17:08):

Did you find the definition of sin?

Stephanie Zhou (Apr 06 2020 at 17:11):

Ah, this?
def sin (z : ℂ) : ℂ := ((exp (-z * I) - exp (z * I)) * I) / 2

Kevin Buzzard (Apr 06 2020 at 17:11):

Right. And can you find the definition of exp?

Kevin Buzzard (Apr 06 2020 at 17:12):

if you have mathlib open at sin then you might be able to right click on exp and jump to it.

Kevin Buzzard (Apr 06 2020 at 17:14):

although having done this myself now, I see that you don't have to jump very far.

Stephanie Zhou (Apr 06 2020 at 17:48):

Okay, would I define sin and cos in this way at the beginning to get lean to rewrite this way?

Kevin Buzzard (Apr 06 2020 at 17:50):

I don't really understand the question. Mathlib has defined them that way, so if you want to use mathlib's sin and cos then you'll have to use their definitions. But exp is defined as a power series so you could mimic that if you wanted to define sin as a power series.

Brandon B (Apr 07 2020 at 03:23):

I'm trying to understand the difference between the theorem I wrote and the one that's in the Lean book to prove that, assuming p, one can prove p or q :

variables p q : Prop
theorem t1 (hp : p) : p  p  q := λ hp, or.intro_left q hp      --mine
theorem t2 (hp : p) : p  q := or.intro_left q hp      --in the book

I thought that a definition's (therefore theorem's) type should indicate the theorem to be proved, which should be "p -> p v q" not just "p v q" as in t2.

Donald Sebastian Leung (Apr 07 2020 at 03:29):

In t2, hp : p is already introduced as a hypothesis in the theorem statement so there's no need to add another p → in front of p ∨ q

Brandon B (Apr 07 2020 at 03:38):

I see, thanks

Lynn (Apr 07 2020 at 14:25):

Is there some way to check expressions inside a tactics block? I'd like to write something like

example (a b : Prop) : a ∨ b → b ∨ a :=
begin
  intro H,
  check H,
  ...

but this is not allowed (invalid tactic expression).

Johan Commelin (Apr 07 2020 at 14:26):

What would you like to happen?

Johan Commelin (Apr 07 2020 at 14:27):

If you use an editor like VScode, you can open the "Goal window" and it will show you the type of H

Patrick Massot (Apr 07 2020 at 14:28):

In this case Johan is right, but I think we still need a tactic doing that. This was discussed recently, did we do anything in the end?

Lynn (Apr 07 2020 at 14:29):

I would like to see H : a ∨ b printed to the console. It would be useful to insert check lines into some of the proofs in https://leanprover.github.io/tutorial so I can see what they're doing

Johan Commelin (Apr 07 2020 at 14:29):

The intended usage is that you open the tutorial in VScode

Patrick Massot (Apr 07 2020 at 14:30):

Arg, this is a very old tutorial!

Patrick Massot (Apr 07 2020 at 14:30):

This is not even Lean 3.

Patrick Massot (Apr 07 2020 at 14:31):

We really need to get rid of this trap.

Marc Huisinga (Apr 07 2020 at 14:31):

is it still linked somewhere or did you find it via google?

Johan Commelin (Apr 07 2020 at 14:31):

@Lynn Thanks for finding this! Please tell us how you found it, so that we can help the internet to unfind it.

Lynn (Apr 07 2020 at 14:32):

I suppose a clunky alternative is commenting out some suffix of the proof, to inspect the hypotheses and subgoals at that point.
I found it via Google (I probably looked for lean tutorial)

Kevin Buzzard (Apr 07 2020 at 14:33):

If you have Lean installed using VS Code then all this information is available for you in a window.

Kevin Buzzard (Apr 07 2020 at 14:34):

code.png

Kevin Buzzard (Apr 07 2020 at 14:35):

The Lean 2 tutorial is the #1 hit on duckduckgo if you search for lean tutorial

Marc Huisinga (Apr 07 2020 at 14:35):

Kevin Buzzard said:

The Lean 2 tutorial is the #1 hit on duckduckgo if you search for lean tutorial

oh no

Alex J. Best (Apr 07 2020 at 14:35):

Yeah I just tried a number of variations on googling "lean tutorial" or just "lean prover" that page is listed very highly. Can we (@Gabriel Ebner ) move that git repo to "lean-2-tutorial" perhaps, or should we have something to replace it first?

Kevin Buzzard (Apr 07 2020 at 14:36):

The Lean 2 links directly to a Lean 3 tutorial and there's also a warning

Rob Lewis (Apr 07 2020 at 14:36):

To be fair, the tutorial says about as explicitly as it can that it's out of date and links to the new one.

Lynn (Apr 07 2020 at 14:36):

I will try VS Code! I was enjoying the in-browser Lean environment, and the way it lets me play around without needing to install anything yet. Though I see https://leanprover.github.io/live offers the same functionality

Kevin Buzzard (Apr 07 2020 at 14:36):

That's the old in-browser lean environment :-)

Patrick Massot (Apr 07 2020 at 14:37):

The Lean 3 tutorial it links too is also badly outdated, right?

Alex J. Best (Apr 07 2020 at 14:37):

Rob Lewis said:

To be fair, the tutorial says about as explicitly as it can that it's out of date and links to the new one.

Sure, but its still weird that the old tutorial is coming up so highly on search engines.

Kevin Buzzard (Apr 07 2020 at 14:37):

leanprover is dead (actually, it's just resting), long live leanprover-community

Kevin Buzzard (Apr 07 2020 at 14:37):

This version of the web editor has an up to date Lean and an up to date mathlib.

Lynn (Apr 07 2020 at 14:37):

My hasty reading of the warning:

Please note that this is the tutorial for Lean 2, which allows the use of homotopy type theory (HoTT). It is not the tutorial for the current version of Lean.

made me think the “current version of Lean” was _not yet_ Lean 2, and that this was an experimental tutorial for a new “Lean 2”. (It doesn't mention that the current version is Lean 3, rather than Lean 1.)

Johan Commelin (Apr 07 2020 at 14:38):

It would be really nice if leanprover.github.io would just show a message

Hi! We're really busy developing Lean 4.

In the mean time, please take a look at https://leanprover-community.github.io.

Johan Commelin (Apr 07 2020 at 14:39):

@Lynn Thanks! These bug reports are helpful! (We tend to gloss over them as more experienced users...)

Kevin Buzzard (Apr 07 2020 at 14:39):

I don't see any reason why a newcomer would know whether the current version of Lean is Lean 2 or Lean 4 or whatever. Thanks for this observation Lynn.

Lynn (Apr 07 2020 at 14:39):

Specifying “which allows the use of homotopy type theory (HoTT)” sounds like the “current version of Lean” doesn't allow that, so Lean 2 must be surely more featureful, so the “current version” must be <2 :slight_smile:

Patrick Massot (Apr 07 2020 at 14:39):

Makes sense (when you don't know the story).

Marc Huisinga (Apr 07 2020 at 14:43):

i think the recommended ways to get into the current version of lean (lean 3) are either "theorem proving in lean" (https://leanprover.github.io/theorem_proving_in_lean/index.html) or kevin's natural number game (https://wwwf.imperial.ac.uk/~buzzard/xena/natural_number_game/).
theorem proving in lean (TPIL) explains things from first principles and (optionally) uses the in-browser lean environment. if you've got experience with functional programming, cs in general or other theorem provers, this might be up your alley, either as a reference or a tutorial, depending on how much you already know.
the natural number game (NNG) jumps right into proving things with tactics for natural numbers. people coming from mathematics seem to love it.

lastly, there's the new hitchhiker's guide to logical verification (https://github.com/blanchette/logical_verification_2020/raw/master/hitchhikers_guide.pdf), which i haven't read yet, but i'm sure it's great.

Lynn (Apr 07 2020 at 14:46):

I played through the “natural number game” a few months ago and really liked it :slight_smile: I should probably go through it again to jog my memory.

Ryan Lahfa (Apr 07 2020 at 16:08):

Patrick Massot said:

Makes sense (when you don't know the story).

I'm interested in the story for removing HoTT, it was not useful enough? Or too difficult to keep it in the Lean codebase?

Kevin Buzzard (Apr 07 2020 at 16:10):

Leo just decided he wasn't going to support it. My guess is that he wasn't hearing any particular need to support it from the parts of the CS community which he listens to.

Kevin Buzzard (Apr 07 2020 at 16:11):

What I've learnt from the HoTT chat is there are still a lot of foundational questions which one has to answer after deciding to have some kind of univalence principle, and I'm not sure you can please all of the people all of the time.

Ryan Lahfa (Apr 07 2020 at 16:11):

Kevin Buzzard said:

What I've learnt from the HoTT chat is there are still a lot of foundational questions which one has to answer after deciding to have some kind of univalence principle, and I'm not sure you can please all of the people all of the time.

Makes sense

Michael J. Curry (Apr 07 2020 at 17:05):

I'm a machine learning person who's been hearing a lot about theorem provers recently (like many other people just started trying out the natural numbers game). I think it might be neat to see if it's possible to use Lean to prove some simple theorems that might be of interest in an ML setting. For example, Markov's inequality for random variables, or bounds on the range of a Lipschitz function. These seem very simple and like they might be fun to prove -- would it be possible? Or is Lean still mainly meant to be used for much more foundational stuff than that?

Ryan Lahfa (Apr 07 2020 at 17:06):

Michael J. Curry said:

I'm a machine learning person who's been hearing a lot about theorem provers recently (like many other people just started trying out the natural numbers game). I think it might be neat to see if it's possible to use Lean to prove some simple theorems that might be of interest in an ML setting. For example, Markov's inequality for random variables, or bounds on the range of a Lipschitz function. These seem very simple and like they might be fun to prove -- would it be possible? Or is Lean still mainly meant to be used for much more foundational stuff than that?

It's definitely possible to prove both of what you mentioned.
It is just that some stuff requires integrals / derivation which is currently being built in mathlib

Patrick Massot (Apr 07 2020 at 17:07):

@Koundinya Vajjha will tell you about https://arxiv.org/abs/1911.00385

Alex J. Best (Apr 07 2020 at 17:09):

There was also @Daniel Selsam et al.'s http://proceedings.mlr.press/v70/selsam17a/selsam17a.pdf

Andrew Ashworth (Apr 07 2020 at 17:14):

it is possible, if you have the correct background. For me, as an engineer, the difficulty is in getting to grips with the formal definitions of everything; measure-theoretic probability is not taught at all in the standard curriculum so you kind of have to teach yourself all the background material before can even state results that you take for granted

Michael J. Curry (Apr 07 2020 at 17:15):

thanks, those are both fantastic things to start looking at! the second paper in particular looks like it has some way of defining integrals and expectation operators that can be formally manipulated without actually having to worry about defining integration in a rigorous way.

Koundinya Vajjha (Apr 07 2020 at 20:38):

Markov's inequality and Chebyshev's Inequality were low hanging fruit since mathlib's measure theory library is very mature. So I proved those without too much hassle in a different repository.

Yury G. Kudryashov (Apr 07 2020 at 20:40):

One version of Chebyshev's inequality is now in mathlib, see docs

Yury G. Kudryashov (Apr 07 2020 at 20:41):

I factored out its proof from the next lemma while refactoring Borel spaces

Patrick Massot (Apr 07 2020 at 20:45):

But this features the weird ∫⁻

Yury G. Kudryashov (Apr 07 2020 at 20:47):

AFAIK, for a non-negative function Bochner integral equals lintegral.

Yury G. Kudryashov (Apr 07 2020 at 20:48):

And formalizing Chebyshev's inequality was not my goal.

Patrick Massot (Apr 07 2020 at 20:49):

I'm not complaining, I'm pointing out it's still hard to recognize.

Scott Morrison (Apr 07 2020 at 23:30):

Koundinya Vajjha said:

Markov's inequality and Chebyshev's Inequality were low hanging fruit since mathlib's measure theory library is very mature. So I proved those without too much hassle in a different repository.

Please make a PR!

Brandon B (Apr 07 2020 at 23:45):

Why can we create a pi type using the pi symbol \Pi but \Sigma doesn't work and instead we have to use sigma.mk or is there another command ?

Yury G. Kudryashov (Apr 07 2020 at 23:48):

When you declare a pi type, you use Π i, α i. When you define a variable of a pi type, you use λ i, f i.

Yury G. Kudryashov (Apr 07 2020 at 23:49):

When you declare a sigma type, you can use Σ i, α i. When you define a variable of a sigma type, you use either sigma.mk or ⟨i, y⟩.

Brandon B (Apr 08 2020 at 00:44):

ahh; thank you

Niclas Kupper (Apr 08 2020 at 12:23):

How can you rewrite the RHS of an equation? when I write rw add_comm at h for my equality h it always only rewrites the LHS. Thanks

Kevin Buzzard (Apr 08 2020 at 12:26):

It rewrites the first occurrence of a+b it runs into

Kevin Buzzard (Apr 08 2020 at 12:27):

Try rw add_comm X Y if you want to change X+Y into Y+X

Kevin Buzzard (Apr 08 2020 at 12:27):

Or just rw add_comm X if you want to change the first X+something

Niclas Kupper (Apr 08 2020 at 12:31):

That worked, thanks!

Scott Morrison (Apr 08 2020 at 13:02):

Also try conv.

Johan Commelin (Apr 08 2020 at 13:30):

@Niclas Kupper https://github.com/leanprover-community/mathlib/blob/master/docs/extras/conv.md

Matt Watson (Apr 08 2020 at 13:55):

Complete noob question. Working on the natural numbers game and I want to use rw add_comm on t + t * b in t * n + t * b + t = t * n + t + t * b but I don't know how to apply it to t * b rather than just a naked variable

Victor Ahlquist (Apr 08 2020 at 13:57):

Matt Watson said:

Complete noob question. Working on the natural numbers game and I want to use rw add_comm on t + t * b in t * n + t * b + t = t * n + t + t * b but I don't know how to apply it to t * b rather than just a naked variable

Try enclosing tb in parentheses like this: rw add_comm t (tb),

Victor Ahlquist (Apr 08 2020 at 13:59):

I'm currently also doing the natural number game and I am stuck on level 9 in advanced proposition world. Is it possible to solve the problem without using either of cc, tauto and exfalso? Exfalso is introduced in the next problem, hence my question.

Matt Watson (Apr 08 2020 at 14:03):

Victor Ahlquist said:

Try enclosing t*b in parentheses like this: rw add_comm t  (t*b),

It didn't work for t and t*b, I think my problem was making assumptions about associativity. But knowing for sure that parenthesis worked helped solve it. Thankyou

Kevin Buzzard (Apr 08 2020 at 14:05):

Yes a+b+c in Lean means (a+b)+c

Mario Carneiro (Apr 08 2020 at 14:05):

you need add_right_comm here

Kevin Buzzard (Apr 08 2020 at 14:06):

I'm not sure we prove that at all

Kevin Buzzard (Apr 08 2020 at 14:06):

I can't remember, I think I just went for the stuff that made simp work

Mario Carneiro (Apr 08 2020 at 14:07):

simp only needs add_left_comm because it likes right associating addition

Kevin Buzzard (Apr 08 2020 at 14:07):

Advanced prop world level 9 I think someone else pointed out that they wanted to use exfalso. I'll take a look at this today

Mario Carneiro (Apr 08 2020 at 14:07):

which is too bad since the pretty printer prefers left associated

Kevin Buzzard (Apr 08 2020 at 14:07):

You can look at my solutions in the repo

Mario Carneiro (Apr 08 2020 at 14:08):

you could make + right associative...

Victor Ahlquist (Apr 08 2020 at 14:09):

Kevin Buzzard said:

Advanced prop world level 9 I think someone else pointed out that they wanted to use exfalso. I'll take a look at this today

Very nice, thanks.

Matt Watson (Apr 08 2020 at 14:19):

Does the page use local storage, or have I lost my entire game if I crashed the tab?

Kevin Buzzard (Apr 08 2020 at 14:19):

you can just go to any level at any time

Kevin Buzzard (Apr 08 2020 at 14:20):

all you lose is the colouring in dots

Johan Commelin (Apr 08 2020 at 14:20):

@Kevin Buzzard You should ask for some help with improving the machinery of the game on that reddit page

Kenny Lau (Apr 08 2020 at 14:20):

Kevin Buzzard said:

you can just go to any level at any time

the levels are a lie

Johan Commelin (Apr 08 2020 at 14:20):

Probably lots of people over there that would love to add a "use local storage" feature

Johan Commelin (Apr 08 2020 at 14:21):

Kenny Lau said:

Kevin Buzzard said:

you can just go to any level at any time

the levels are a lie

There are no levels... you only imagine them

Bryan Gin-ge Chen (Apr 08 2020 at 14:21):

I think the progress is saved using cookies rather than local storage.

Kenny Lau (Apr 08 2020 at 14:21):

Johan Commelin said:

Probably lots of people over there that would love to add a "use local storage" feature

like this game i enjoyed from many years ago: https://alf.nu/RegexGolf

Andrew Ashworth (Apr 08 2020 at 14:23):

wow i'm irrationally annoyed at the levels beyond classic

Kenny Lau (Apr 08 2020 at 14:25):

why?

Andrew Ashworth (Apr 08 2020 at 14:29):

i'm remembering a specific set of exam questions in my compilers class that "pure" regex (dfas) cannot be used to count and quantify, and getting them to do it is a hack. of course regular people use regex for this all the time, so maybe i was just insufficiently traumatized by my instructor

Marc Huisinga (Apr 08 2020 at 14:33):

regexp with backtracking can also lead to exponential blowup iirc

Marc Huisinga (Apr 08 2020 at 14:34):

so i think there are good reasons to restrict yourself to a regular subset when using regexes

Marc Huisinga (Apr 08 2020 at 14:41):

ah, this site was down for a while, but this is the post that i read years ago: https://swtch.com/~rsc/regexp/regexp1.html

Matt Watson (Apr 08 2020 at 14:47):

Not only that, but having to support a non-regular language makes a language slower when dealing with expressions that are regular.

Scott Morrison (Apr 08 2020 at 15:01):

That is an incredibly unfortunate story. :-(

Johan Commelin (Apr 08 2020 at 15:10):

Andrew Ashworth said:

of course regular people use regex for this all the time,

Sure, regular people do... but what about expressive people?

Niclas Kupper (Apr 08 2020 at 15:14):

I'm currently on the first advanced multiplication world level in the natural number game and I used cases and my equation got a term zero instead of 0, which I can not manipulate with the usual proofs. What can I do now?

Niclas Kupper (Apr 08 2020 at 15:14):

It might be because I tried to define a lemma in the proof

Matt Watson (Apr 08 2020 at 15:15):

@Johan Commelin Or push down automatons?

Johan Commelin (Apr 08 2020 at 15:16):

@Niclas Kupper Trying to define a lemma in a proof seems like it cannot work...

Johan Commelin (Apr 08 2020 at 15:17):

Can you copy paste all the code the entire code block (starting with lemma ... until ... end

Johan Commelin (Apr 08 2020 at 15:17):

```lean
put code here
```

Niclas Kupper (Apr 08 2020 at 15:17):

intro p1,
intro p2,
intro k,
induction b with d hd,
rw mul_zero a at k,
exact p2 k,
lemma pos_mul (a b : mynat) : a * b = 0 → a = 0 ∨ b = 0 :=
begin
  intro k,
  cases a with d,
  ```

Niclas Kupper (Apr 08 2020 at 15:18):

This is where the problem appeared, I assume that normally I would put a lemma like that in its own file?

Andrew Ashworth (Apr 08 2020 at 15:30):

use the have keyword / tactic

Andrew Ashworth (Apr 08 2020 at 15:31):

if you want to do it inside a proof; normally such a fundamental fact would be broken out separately though, as you say

Niclas Kupper (Apr 08 2020 at 15:32):

How do I want to use have?

Andrew Ashworth (Apr 08 2020 at 15:33):

https://leanprover.github.io/theorem_proving_in_lean/propositions_and_proofs.html#introducing-auxiliary-subgoals

Niclas Kupper (Apr 08 2020 at 15:34):

Thanks!

Victor Ahlquist (Apr 08 2020 at 15:44):

Will using cases with a mynat always separate into cases zero and successor?

Donald Sebastian Leung (Apr 08 2020 at 15:44):

Yes

Victor Ahlquist (Apr 08 2020 at 15:45):

Thanks

Kevin Buzzard (Apr 08 2020 at 15:55):

Using cases with any inductive type separates into the constructors for that type. For nat this is zero / succ, for P \or Q this is a proof of P / a proof of Q etc.

Victor Ahlquist (Apr 08 2020 at 15:59):

Thanks, googling some of those terms brought up a nice page. I'll be sure to read the entire "Theorem proving in Lean" once I'm done with the game.

Matt Watson (Apr 08 2020 at 16:03):

Why are propositions distinct from inhabitation of types?

Matt Watson (Apr 08 2020 at 16:05):

Or am I importing half-understood ideas from a different type theory?

Kevin Buzzard (Apr 08 2020 at 16:06):

The Prop universe is proof irrelevant which means that if P : Prop and a b : P then a = b definitionally.

Kevin Buzzard (Apr 08 2020 at 16:07):

This makes some type theory people unhappy but it seems to make mathematicians happy

Victor Ahlquist (Apr 08 2020 at 17:05):

Is there any reason why proof of contrapositive reasoning is not added to theorem list after advanced proposition world?

Kevin Buzzard (Apr 08 2020 at 17:11):

I guess I just randomly wrote a load of worlds and didn't particularly plan anything in some coherent way. What do you suggest I do? I'm going to make some minor updates later on today.

Victor Ahlquist (Apr 08 2020 at 17:18):

Well I have no particular suggestion. Having to "redo" a proof backwards has been useful in learning how to use lean. I was just wondering if this was intended or not.

Victor Ahlquist (Apr 08 2020 at 17:19):

I really appreciate the work you have done with the game. This is a really exciting way to study mathematics.

Kevin Buzzard (Apr 08 2020 at 17:21):

Oh -- are you saying I failed to add it to some list which it should be on? Sorry, I misunderstood. Probably the answer is that the current version of the Lean game maker forces me to do some things twice -- once in Lean and once in a comment so that it's registered by the system to add to a list, and maybe I failed to do it here. This was what caused the pow_succ fail: the theorem list had an incorrect version of pow_succ because of a copy paste fail.

Victor Ahlquist (Apr 08 2020 at 17:23):

Oh I see. I have been using the list of theorems to the right as a kind of "Allowed theorems to use" list(which is also handy because the list has the names), which is why I wondered.

Matt Watson (Apr 08 2020 at 18:05):

Piece of feedback: I seem to alternate between feeling completely lost, and like the body text is spoiling the answer.
Perhaps the more direct instructions could be hidden behind a spoiler tag so we can struggle on our own a bit?

Kevin Buzzard (Apr 08 2020 at 18:09):

I absolutely agree. Firstly, I need a competent editor -- I waffle and waffle. Secondly, unfortunately, the infrastructure is not there yet for me to be able to implement the spoiler tag stuff, but I can add it as an issue to Mohammad's game maker -- I had independently thought of this recently.

Matt Watson (Apr 08 2020 at 18:53):

If you add something like this to editor.main.css

.hover-item {
    color: #fff;
}

.hover-item:hover {
    color: #000;
}

and then wrap the spoiler in <span class="hover-item"> it should work

Matt Watson (Apr 08 2020 at 18:54):

I can see about writing an extension to showdown to make it a little more erganomic

Kevin Buzzard (Apr 08 2020 at 18:55):

Yeah but I don't know what a css file is -- you could maybe put some comment on the issue I opened on Mohammad's repo (he is about to defend his thesis viva so we might not hear from him for a few days)

Matt Watson (Apr 08 2020 at 18:55):

(showdown doesn't touch the span tags, adding them to the js objects worked but I'm not sure if lean will eat them)

Kevin Buzzard (Apr 08 2020 at 18:57):

If you want to make some PR and tell me how to change my repo I would be interested.

Matt Watson (Apr 08 2020 at 18:59):

It looks like you're pulling the relevant file in during the build/deploy process, so I can send the PR to Mohammad's repo.

Kevin Buzzard (Apr 08 2020 at 19:00):

but then presumably I have to indicate which part of the Lean file should be folded, somehow?

Matt Watson (Apr 08 2020 at 19:01):

Yeah, the thing I proposed would be a fast/simple but not great way of doing it.

More correct would be to write an extension for the markdown formatter (called showdown), then you'd just wrap the hidden section in something like >! Text to hide <

Matt Watson (Apr 08 2020 at 19:02):

That's one common syntax. Another is [spoiler] [/spoiler]

Matt Watson (Apr 08 2020 at 19:05):

I'll look into doing it properly (if you don't hear from me in a couple of days it means I got sidetracked). In the interim, here is other formatting syntax that should work with what you're already running if you were not already aware of it http://demo.showdownjs.com/

Matt Watson (Apr 08 2020 at 19:49):

How do I make rw happen on a hypothesis?
Specifically I have

a b u : mynat,
ht : a + u = b + u → a = b,
h : a + succ u = b + succ u
⊢ a + u = b + u

and I'm trying to use rw add_succ on both sides of h, but I keep getting 'did not find instance of pattern'

Anas Himmi (Apr 08 2020 at 19:52):

rw [add_succ,add,succ] at h

Matt Watson (Apr 08 2020 at 19:53):

Thanks

Brandon B (Apr 09 2020 at 00:19):

What exactly is a tactic under the hood? Is it just syntactic sugar for function composition? Is it a macro?

Bryan Gin-ge Chen (Apr 09 2020 at 00:25):

They're functions / programs that manipulate the "tactic state". You can read more in the tactic writing tutorial.

Scott Morrison (Apr 09 2020 at 01:52):

I found https://doi.org/10.1145/3110278 really helpful.

Victor Ahlquist (Apr 09 2020 at 14:20):

The link from https://github.com/ImperialCollegeLondon/natural_number_game/blob/master/WHATS_NEXT.md to the lean web editor seems to be broken.

Bryan Gin-ge Chen (Apr 09 2020 at 14:29):

The correct link is https://github.com/kbuzzard/xena/tree/master/Maths_Challenges/README.md . I'll open a PR if there isn't one already.

edit: ImperialCollegeLondon/natural_number_game#66

Victor Ahlquist (Apr 09 2020 at 15:01):

Thanks, I haven't really used Github before so wasn't sure how to report it.

Victor Ahlquist (Apr 09 2020 at 15:03):

Going to read up on the lean documentation now at least. This seems like a productive thing to use my free time on.

Will P (Apr 09 2020 at 15:14):

Hi, thanks for all the work put into lean. I'm having a go at the natural numbers game and I got stuck at level 10 on the advanced addition. I'm not sure exactly how to use not equals during a proof. My current attempt is

cases b with d,
refl,
exfalso ,
rw add_succ at H,
have q := succ_ne_zero(a + d) at H,
rw H at q,

a d : mynat,
H : succ (a + d) = 0,
q : 0  0
 false

I've got 0 /=0 but no way of making that false

Kenny Lau (Apr 09 2020 at 15:17):

hint: use the following code to enable syntax highlighting:

```lean
[your code here]
```

Kenny Lau (Apr 09 2020 at 15:18):

Also, please copy the tactic state after the final comma.

Victor Ahlquist (Apr 09 2020 at 15:28):

Nothing seems to happen when I'm using the lean web editor. I pressed the first "try it!" button here: https://leanprover.github.io/theorem_proving_in_lean/introduction.html , but I can't see the current goal only the block of code.

Marc Huisinga (Apr 09 2020 at 15:29):

that proof is in term mode, so it won't have a goal view

Kevin Buzzard (Apr 09 2020 at 15:30):

The community web editor is better.

Marc Huisinga (Apr 09 2020 at 15:30):

chapter 5 introduces tactics, here's one example

Victor Ahlquist (Apr 09 2020 at 15:32):

Ah thanks

Kenny Lau (Apr 09 2020 at 15:32):

@Will P change your last two lines to:

apply succ_ne_zero (a + d),
exact H,

Marc Huisinga (Apr 09 2020 at 15:35):

(deleted)

Victor Ahlquist (Apr 09 2020 at 15:36):

About Will P's question: I also had some times when doing the natural number game when it would have been great to get a "trivial" hypothesis such as 0=0. I tried the have command, but it did not work. Is there any other way to get an equality?

Kenny Lau (Apr 09 2020 at 15:37):

have h : 0 = 0,
refl,

Kevin Buzzard (Apr 09 2020 at 15:37):

If you have

q : 0 ≠ 0
⊢ false

then you can apply q and then refl because q is 0 = 0 -> false

Victor Ahlquist (Apr 09 2020 at 15:40):

Ah, I tried have h := 0=0, which only gave me a h: Prop. Thanks for the answers

Kenny Lau (Apr 09 2020 at 15:41):

h := 0=0 means h is defined to be 0=0, which is a Prop

Victor Ahlquist (Apr 09 2020 at 15:43):

I see, hopefully all of this will make more sense after reading the book.

Kevin Buzzard (Apr 09 2020 at 15:50):

Be clear about the difference between a Proposition (a true/false statement) and its proof. In mathematics we are very vague about this, we have our own conventions which seem to work fine but don't stand up to scrutiny. When we say "by theorem 10" we mean "by the fact that we proved theorem 10"; theorem 10 is just a statement. A true/false statement, otherwise known as a Proposition in Lean, is P : Prop. It's proof is h : P. For example injective f : Prop := \forall x y, f x = f y -> x = y is the definition of injectivity. There is no proof here, it's a definition of a true/false statement, and its truth value depends on what f is. But h : injective succ := <proof that succ is injective> is a proof. The propositions are types, and the proofs are terms. Be clear about what level you are working at.

Victor Ahlquist (Apr 09 2020 at 16:56):

Kevin Buzzard said:

Be clear about the difference between a Proposition (a true/false statement) and its proof. In mathematics we are very vague about this, we have our own conventions which seem to work fine but don't stand up to scrutiny. When we say "by theorem 10" we mean "by the fact that we proved theorem 10"; theorem 10 is just a statement. A true/false statement, otherwise known as a Proposition in Lean, is P : Prop. It's proof is h : P. For example injective f : Prop := \forall x y, f x = f y -> x = y is the definition of injectivity. There is no proof here, it's a definition of a true/false statement, and its truth value depends on what f is. But h : injective succ := <proof that succ is injective> is a proof. The propositions are types, and the proofs are terms. Be clear about what level you are working at.

Thanks for the elaborate reply, now it is very clear why h : 0=0 ,is used

Victor Ahlquist (Apr 09 2020 at 18:37):

By the way, a project of "perfectoid spaces" was mentioned after finishing the game. It said you needed a "whole lot of commutative algebra" to do that. Does this mean PhD level?

Marc Huisinga (Apr 09 2020 at 18:44):

Victor Ahlquist said:

By the way, a project of "perfectoid spaces" was mentioned after finishing the game. It said you needed a "whole lot of commutative algebra" to do that. Does this mean PhD level?

https://arxiv.org/abs/1910.12320

Victor Ahlquist (Apr 09 2020 at 18:47):

Looks like a yes then.

Filip Szczepański (Apr 09 2020 at 22:10):

Is there anything I can do when the Lean interpreter on the Natural Number Game gets stuck, besides refreshing the page and losing my progress?

Kevin Buzzard (Apr 09 2020 at 22:13):

no progress is kept. You can go to any level at any time.

Bryan Gin-ge Chen (Apr 09 2020 at 22:14):

What's causing the interpreter to get stuck? Is it something you can reproduce?

Filip Szczepański (Apr 09 2020 at 22:14):

That's true. I appreciate seeing how far I've gotten, though

Filip Szczepański (Apr 09 2020 at 22:15):

One time I got it stuck with repeat { rw add_comm }, and this time it was something else with repeat

Kevin Buzzard (Apr 09 2020 at 22:15):

You can get a tactic block which loops, but in VS Code this isn't a problem. Is it a problem in NNG?

Filip Szczepański (Apr 09 2020 at 22:16):

Yeah, just on the NNG website

Filip Szczepański (Apr 09 2020 at 22:17):

I wouldn't mind going through NNG in vscode, but it seems the website is the recommended way? Also vscode doesn't list the tactics and theorems

Kevin Buzzard (Apr 09 2020 at 22:17):

You're right, and I don't really know how to solve this.

Bryan Gin-ge Chen (Apr 09 2020 at 22:18):

I can't seem to get the web editor to loop. If you run into another loop, please paste the full code so I can take a look.

Kevin Buzzard (Apr 09 2020 at 22:18):

@Bryan Gin-ge Chen try going to advanced addition world at http://wwwf.imperial.ac.uk/~buzzard/xena/natural_number_game/ , level 5, and then try repeat {rw add_comm}. This loops. Unsurprisingly, you get "Lean is busy...". But it doesn't go away

Filip Szczepański (Apr 09 2020 at 22:19):

Obviously, the interpreter can just keep going between a+b and b+a forever

Kevin Buzzard (Apr 09 2020 at 22:19):

Right. But in VS Code if you just delete the line repeat {rw add_comm} everything goes back to normal.

Kevin Buzzard (Apr 09 2020 at 22:20):

In NNG it seems that this breaks the game forever, you can go back to the overworld and then click on another world and any level and Lean is still busy

Kevin Buzzard (Apr 09 2020 at 22:25):

https://github.com/mpedramfar/Lean-game-maker/issues/6

Bryan Gin-ge Chen (Apr 09 2020 at 22:29):

Interesting. my wild guess would be because the web editor runs single-threaded (?) and so nothing is able to interrupt the loop. For the NNG, maybe you could wrap repeat in try_for 100000 to protect users from this.

Bryan Gin-ge Chen (Apr 09 2020 at 22:30):

Well, I'm not sure what numbers would be good to use in try_for, maybe 100000 is still too low.

Filip Szczepański (Apr 09 2020 at 22:38):

I think the Lean interpreter is running in something like a web worker, otherwise I'd expect the whole tab to freeze up

Scott Morrison (Apr 09 2020 at 22:39):

My son did exactly this, and it really upset him when he lost his progress. :-) It was a good life lesson...

Bryan Gin-ge Chen (Apr 09 2020 at 23:05):

Yep, there's one web worker for the Lean server and one worker for Monaco (the text editor).

Victor Ahlquist (Apr 10 2020 at 13:14):

The link to "2 add 2 isn't 5" from here https://github.com/kbuzzard/xena/blob/master/Maths_Challenges/README.md is broken

Victor Ahlquist (Apr 10 2020 at 13:15):

Actually all of the links except the first two appears to be broken

Kevin Buzzard (Apr 10 2020 at 13:21):

Nobody ever does these challenges, I don't think I've ever got any feedback from them. Thanks for letting me know.

Victor Ahlquist (Apr 10 2020 at 13:23):

Oh, the first two were nice to do after NNG. Guess I'll look for other things to do.

Kevin Buzzard (Apr 10 2020 at 13:23):

They'll all be fixed in 5 minutes :-)

Victor Ahlquist (Apr 10 2020 at 13:23):

Nice, thanks :)

Kevin Buzzard (Apr 10 2020 at 13:29):

Links to questions should now all work, I'll check hints and solutions. Thanks again.

Victor Ahlquist (Apr 10 2020 at 13:34):

Thanks, they're working now

Victor Ahlquist (Apr 10 2020 at 13:38):

By the way, I saw you mentioned in an old reddit thread that there still was some undergrad math that needed to be done in Lean. Is this still the case?

Kevin Buzzard (Apr 10 2020 at 13:43):

3rd year undergraduate maths there's lots.

Kevin Buzzard (Apr 10 2020 at 13:43):

1st and 2nd year we're basically covered apart from contour integrals

Kevin Buzzard (Apr 10 2020 at 13:44):

3rd year undergrad we have most of a basic number theory course but as far as I know we don't have any of: ideals in a number field factor uniquely into prime ideals, finiteness of class group, rank of unit group.

Kevin Buzzard (Apr 10 2020 at 13:45):

we have a bunch of commutative algebra but no homological algebra

Johan Commelin (Apr 10 2020 at 13:45):

Well... some people are working on combinatorics/probability theory/graph theory... but there are huge holes there.

Kevin Buzzard (Apr 10 2020 at 13:46):

Oh here's a list of random things we don't have: https://github.com/kbuzzard/xena/blob/master/many_maths_challenges.txt

Victor Ahlquist (Apr 10 2020 at 13:48):

Oh, some of those topics definitely sound approachable. I'm a 2nd year undergrad, but in Swedish school system so not sure how things translate. I'll have a lot of free time this summer and some free time the weeks until then so I'll definitely start looking into this.

Victor Ahlquist (Apr 10 2020 at 13:48):

Thanks for the list!

Patrick Massot (Apr 10 2020 at 13:49):

homological algebra is coming, I've seen many PRs about this recently

Kevin Buzzard (Apr 10 2020 at 13:49):

I'm working with a bunch of 2nd year undergraduates on stuff this summer, it was all going to happen at Imperial College but now it's probably going to happen online, you'd be welcome to join us -- most people will be beginners.

Kevin Buzzard (Apr 10 2020 at 13:50):

homological algebra has in some sense been coming for some time. Another way of looking at it is that a mathematician writes "let Mn1MnMn+1\cdots\to M_{n-1}\to M_n\to M_{n+1}\to \cdots be an exact sequence of abelian groups, and then we spend months trying to figure out how best to say that in Lean without getting caught up in type theory issues.

Kevin Buzzard (Apr 10 2020 at 13:51):

or category theory issues

Victor Ahlquist (Apr 10 2020 at 13:52):

Kevin Buzzard said:

I'm working with a bunch of 2nd year undergraduates on stuff this summer, it was all going to happen at Imperial College but now it's probably going to happen online, you'd be welcome to join us -- most people will be beginners.

Wow that would be great. What would the mathematical prerequisites be?

Kevin Buzzard (Apr 10 2020 at 13:53):

2nd year undergrad?

Kevin Buzzard (Apr 10 2020 at 13:53):

Whatever you show up with, really.

Victor Ahlquist (Apr 10 2020 at 13:58):

Neat, I'll just practice using lean until then in that case.

Victor Ahlquist (Apr 10 2020 at 13:58):

It is really impressing that there's already so much math formalized in lean.

Kevin Buzzard (Apr 10 2020 at 14:01):

In 2017 there was nothing.

Kevin Buzzard (Apr 10 2020 at 14:01):

Definitions of rings, groups, top spaces, basic stuff about compact spaces, hardly any theorems about anything else. Loads and loads and loads of theorems about lists finite sets though, which turn out to be really important.

Victor Ahlquist (Apr 10 2020 at 14:02):

I'm looking forward to seeing how this will influence mathematics

Kevin Buzzard (Apr 10 2020 at 14:02):

Most stuff written by Mario Carneiro and Johannes Hoelzl, who have a computer science background. Then mathematicians started to get involved and the focus of the library changed much more to undergraduate level mathematics

Victor Ahlquist (Apr 10 2020 at 14:03):

Ah I see. It is nice that there's some cooperation between different subjects going on.

Kevin Buzzard (Apr 10 2020 at 14:03):

I should say that Mario almost certainly would not say there was nothing -- there was a ton of stuff in late 2017 which was of foundational importance.

Kevin Buzzard (Apr 10 2020 at 14:03):

But if you look at what there was and compare with the things taught to maths undergraduates, there was very little.

Kevin Buzzard (Apr 10 2020 at 14:04):

Because we don't teach them that the union of two finite sets is finite, this doesn't need a proof as far as we are concerned :-) In Lean this sort of thing needs to be proved, and then you have to decide what the definition of a finite set is, and then suddenly you are wrestling with foundational type theory issues.

Patrick Massot (Apr 10 2020 at 14:05):

Even at that time there was much more general topology than what we teach undergrads.

Kevin Buzzard (Apr 10 2020 at 14:06):

Yes that is true, it was some weird outlier.

Kevin Buzzard (Apr 10 2020 at 14:06):

Probably all the results in my 2nd year topology course were there.

Kevin Buzzard (Apr 10 2020 at 14:07):

But I am pretty sure there was no first isomorphism theorem for groups or rank-nullity for vector spaces in 2017.

Patrick Massot (Apr 10 2020 at 14:08):

Note there was a lot of topology but no normed space.

Victor Ahlquist (Apr 10 2020 at 14:08):

I guess some maths is easier to formalize than others?

Victor Ahlquist (Apr 10 2020 at 14:09):

Although that's a wild guess from my side.

Filip Szczepański (Apr 10 2020 at 14:13):

What happens when I write have foo := bar, and there was already something with the name foo in scope?

Donald Sebastian Leung (Apr 10 2020 at 14:16):

You get two foos in your context, and IIRC the latest foo shadows the earlier one (so later tactics referring to foo would only see the latest one)

Filip Szczepański (Apr 10 2020 at 14:16):

Ok, that's fairly convenient. Thanks.

Donald Sebastian Leung (Apr 10 2020 at 14:17):

This is one of the things that sometimes gets me in Lean, but doesn't happen in Coq. Does anyone know the rationale behind this design decision?

Patrick Massot (Apr 10 2020 at 14:18):

Honestly I also wish Lean would rise at least a warning in this situation.

Patrick Massot (Apr 10 2020 at 14:18):

Here Lean feels very pythonic we're-all-consenting-adults.

Filip Szczepański (Apr 10 2020 at 14:47):

I finally finished mul_left_cancel

Filip Szczepański (Apr 10 2020 at 14:47):

I bet my proof is overcomplicated

Kevin Buzzard (Apr 10 2020 at 14:48):

My proof looks pretty long

Filip Szczepański (Apr 10 2020 at 14:51):

https://gist.github.com/FreeFull/01d57489ac996db3ea2f8e31254d466c This is mine. I didn't use assumption because I didn't know about it

Filip Szczepański (Apr 10 2020 at 14:51):

Not too nicely formatted either

Victor Ahlquist (Apr 10 2020 at 16:19):

I tried downloading and installing leanprover and commands like #eval 1+1 work without trouble. However when I try importing things such as import data.finset algebra.big_operators tactic.ring, I get a file not found error. Any idea what could be causing this?

Bryan Gin-ge Chen (Apr 10 2020 at 16:54):

Did you follow all the instructions for your OS here and then follow the instructions to create a project here?

Kevin Buzzard (Apr 10 2020 at 17:15):

Note however that you cannot use mathlib, and in particular any imports, in the file test.lean created above. To use mathlib you will need to set up or download a Lean project. You should now read instructions about creating and working on Lean projects.

What is your OS? Does this comment (at the bottom of the install files) apply to you?

Victor Ahlquist (Apr 10 2020 at 19:13):

Bryan Gin-ge Chen said:

Did you follow all the instructions for your OS here and then follow the instructions to create a project here?

Ah no, I did not follow the instructions to create a project only the instructions on the install page. I'll try this

Victor Ahlquist (Apr 10 2020 at 19:26):

Great, now it works perfectly! Thanks for the help

Victor Ahlquist (Apr 10 2020 at 19:34):

Not sure how I managed to miss that last message

Kevin Buzzard (Apr 10 2020 at 19:42):

I specially put it into the docs a couple of weeks ago because people keep asking this :-)

Kevin Buzzard (Apr 10 2020 at 19:43):

Maybe I'll put it in flashing red letters :-)

Victor Ahlquist (Apr 10 2020 at 19:43):

Haha, as you posted it I can recall reading it and then promptly forgetting about it.

Victor Ahlquist (Apr 10 2020 at 20:05):

Chapter 2 was an interesting but tough read, reminds me of my course in Java programming

Kevin Buzzard (Apr 10 2020 at 20:05):

Chapter 2 of the installation instructions?

Filip Szczepański (Apr 10 2020 at 20:06):

I have hb : b = b + d + c and I'm wondering if there's a more straightforward way to get to 0 = d + c than going through ← add_zero, add_assoc and add_left_cancel

Victor Ahlquist (Apr 10 2020 at 20:07):

Kevin Buzzard said:

Chapter 2 of the installation instructions?

Haha no, chapter 2 of theorem proving in lean.

Victor Ahlquist (Apr 10 2020 at 20:15):

Filip Szczepański said:

I have hb : b = b + d + c and I'm wondering if there's a more straightforward way to get to 0 = d + c than going through ← add_zero, add_assoc and add_left_cancel

I'm new to Lean so this is probably wrong, but perhaps you could leave out add_zero.

Mario Carneiro (Apr 10 2020 at 23:36):

Donald Sebastian Leung said:

You get two foos in your context, and IIRC the latest foo shadows the earlier one (so later tactics referring to foo would only see the latest one)

Patrick Massot said:

Honestly I also wish Lean would rise at least a warning in this situation.

Actually lean does raise a warning in this situation, at least in some cases, especially after a tactic fails; it will suggest that you run the dedup tactic

Mario Carneiro (Apr 10 2020 at 23:42):

Donald Sebastian Leung said:

This is one of the things that sometimes gets me in Lean, but doesn't happen in Coq.

What does Coq do in this situation? It seems to me that if we gave a warning on every name shadowing we would get a ridiculous number of false positives, especially because have := ... is deliberately reusing and shadowing the this variable

Filip Szczepański (Apr 11 2020 at 04:59):

Having some trouble figuring out how to get started on le_total in the natural number game. I'm guessing this is the next level that people tend to get stuck on, after mul_left_cancel?

Victor Ahlquist (Apr 11 2020 at 05:51):

I can't recall exactly how I did but I think I started with induction on b

Shing Tak Lam (Apr 11 2020 at 05:55):

I think you need to revert a first.

Shing Tak Lam (Apr 11 2020 at 06:00):

So my proof starts with

  revert a,
  induction b with b IH,

Filip Szczepański (Apr 11 2020 at 06:22):

Thanks, the revert certainly seems handy

Donald Sebastian Leung (Apr 11 2020 at 06:27):

Mario Carneiro said:

Donald Sebastian Leung said:

This is one of the things that sometimes gets me in Lean, but doesn't happen in Coq.

What does Coq do in this situation? It seems to me that if we gave a warning on every name shadowing we would get a ridiculous number of false positives, especially because have := ... is deliberately reusing and shadowing the this variable

Coq simply prevents you from reusing hypothesis names by raising an error. On the other hand, if you don't explicitly name your hypotheses in Coq then Coq just auto-generates gibberish hypothesis names instead of (re-using) a nice name such as this

Mario Carneiro (Apr 11 2020 at 06:58):

One thing we could do is make have have the same behavior as replace, that is, if the name would collide then the old version is deleted

Mario Carneiro (Apr 11 2020 at 06:59):

however this could still break some proofs because you can refer to shadowed variables using assumption and other things that enumerate the local context

Victor Ahlquist (Apr 11 2020 at 07:17):

Shing Tak Lam said:

I think you need to revert a first.

What's the purpose of revert in this case? Will it lead to a shorter proof?

Victor Ahlquist (Apr 11 2020 at 07:18):

induction b with b IH,
right,
exact zero_le a,
cases IH with p q,
left,
exact le_succ a b p,
cases q with c,
cases c,
use succ(0),
rw q_h,
refl,
right,
use c,
rw q_h,
rw succ_add,
rw add_succ,
refl,

I redid it and got this.

Victor Ahlquist (Apr 11 2020 at 07:20):

Actually the second last line is redundant.

Shing Tak Lam (Apr 11 2020 at 07:21):

This is my proof with revert a

  revert a,
  induction b with b IH,
    intro a,
    right,
    exact zero_le _,
    intro a,
    cases a with a,
      left,
      exact zero_le _,
      cases (IH a),
        left,
        exact succ_le_succ a b h,
        right,
        exact succ_le_succ b a h,

So it's a bit shorter?

Donald Sebastian Leung (Apr 11 2020 at 07:21):

Victor Ahlquist said:

Shing Tak Lam said:

I think you need to revert a first.

What's the purpose of revert in this case? Will it lead to a shorter proof?

Notice the difference in your IH with and without revert.

Shing Tak Lam (Apr 11 2020 at 07:23):

But the main reason was to do with the induction hypothesis. The IH after using revert a is much easier to use than the one without.

Kevin Buzzard (Apr 11 2020 at 07:24):

Filip Szczepański said:

I have hb : b = b + d + c and I'm wondering if there's a more straightforward way to get to 0 = d + c than going through ← add_zero, add_assoc and add_left_cancel

If you're working with usual Lean naturals then the omega tactic will solve this for you:

import tactic

example (b c d : ) (hb : b = b + d + c) : 0 = d + c := by omega

But if you're working with home-grown naturals like mynat then you might have to work harder.

Victor Ahlquist (Apr 11 2020 at 07:24):

Ah I see, it does look like a more powerful hypothesis.

Victor Ahlquist (Apr 11 2020 at 07:27):

Guess I'm just scared of quantifiers in Lean

Victor Ahlquist (Apr 11 2020 at 13:33):

There's not much explanation of this in the book, but why does a list have Type u \r Type u as it's type?

Kenny Lau (Apr 11 2020 at 13:34):

Given a type \a, list \a is a type

Kenny Lau (Apr 11 2020 at 13:34):

So list itself sends types to types

Victor Ahlquist (Apr 11 2020 at 13:35):

Ah right, that makes sense thanks.

Grayson Burton (Apr 11 2020 at 17:34):

Why don't interactive theorem provers (including Lean to my knowledge) provide a recursor for Type? Does it cause ""fun"" I'm not noticing in my few minutes' consideration?

Johan Commelin (Apr 11 2020 at 17:35):

What would that look like?

Grayson Burton (Apr 11 2020 at 17:41):

def {u} wacky: Type  Type u
| (  ) := ulift 
| (string  ) := ulift string
| α := ulift α

Something like this?

Grayson Burton (Apr 11 2020 at 17:42):

Obviously a contrived example :)

Kenny Lau (Apr 11 2020 at 17:43):

Type is not inductive.

Kenny Lau (Apr 11 2020 at 17:43):

you can use if then else instead.

Grayson Burton (Apr 11 2020 at 17:45):

Oh, right. Of course, thanks.

David Wärn (Apr 11 2020 at 19:03):

Isn't (ℕ → ℕ) ≠ (string → ℕ) unprovable? If so you'd have little hope of being able to functions as above

Reid Barton (Apr 11 2020 at 19:20):

For anything like this you'd actually want to do, you'd probably want to use classes instead.

Mario Carneiro (Apr 11 2020 at 22:57):

Assuming you want an actual recursor and not just pick off a few cases, it's not consistent. Because there are types you can construct using choice that are provably different from every "constructable" type (since there are countably many constructable types and so there is some aleph less than aleph_(aleph_1) that was missed)

Brandon B (Apr 11 2020 at 23:20):

So I see one aim of Lean would be to implement as much of modern mathematics into it as possible, starting with all of undergrad math as Kevin Buzzard is doing so that research level math can eventually be done. My question is, if we already have a bunch of theorems that have been proved with paper and pencil, why can't we just add them in as constants (basically assuming they're true w/o explicit construction of a proof) and build from there?

Andrew Ashworth (Apr 11 2020 at 23:23):

the exact formal statement of a mathematical theorem can be extremely subtle, at times

Andrew Ashworth (Apr 11 2020 at 23:24):

using them all over the place defeats the purpose of formalization, in terms of checking correctness of proofs

Mario Carneiro (Apr 11 2020 at 23:24):

I think this is fine in limited quantities

Mario Carneiro (Apr 11 2020 at 23:24):

FAbstracts is using essentially this method

Andrew Ashworth (Apr 11 2020 at 23:25):

i don't think there's any getting around using them here and there, especially for results that depend on a whole bunch of other material

Mario Carneiro (Apr 11 2020 at 23:26):

Maybe I'm not looking at the right theorems, but it's not very often I see a theorem where the statement is tractable but the proof is not, that I want to assume for something else

Mario Carneiro (Apr 11 2020 at 23:26):

most of the time the theorem would be some work but not unreasonably so

Mario Carneiro (Apr 11 2020 at 23:27):

and I feel much better about the result afterwards

Mario Carneiro (Apr 11 2020 at 23:27):

or else the statement of the theorem is already hopeless requiring some big library development that doesn't yet exist

Mario Carneiro (Apr 11 2020 at 23:28):

after all, it's not enough just to state the theorem, you also have to be able to prove the basic consequences of all the definitions used in the theorem if you expect to build on top of an assumption with that statement

Scott Morrison (Apr 11 2020 at 23:40):

Note in Mario's statement the all-important clause "that I want to assume for something else". FLT would be a great counterexample to his claim otherwise, but actually fits perfectly. :-) We are very far from being able to state the "actual usable theorems" associated to FLT.

Brandon B (Apr 11 2020 at 23:51):

On another note - is there a way to compose functions left to right in Lean? e.g. it gets tiresome doing h(g(f(x))) when I'd rather do something like x>f>g>h

Mario Carneiro (Apr 11 2020 at 23:54):

I recall seeing isabelle using the |> notation for this, although we will have to take that notation back from the useless option.lhoare

Mario Carneiro (Apr 11 2020 at 23:55):

You can use h $ g $ f $ x to make the chain more linear but that doesn't resolve the order

Mario Carneiro (Apr 11 2020 at 23:55):

Oh wait, I just remembered that we already did this

Mario Carneiro (Apr 11 2020 at 23:55):

it's $<

Mario Carneiro (Apr 11 2020 at 23:55):

in one of the mathlib files

Andrew Ashworth (Apr 11 2020 at 23:55):

... I wish it was |>, that's the pipeline operator in multiple fp languages, not just isabelle

Mario Carneiro (Apr 11 2020 at 23:56):

now that we've taken over core we can delete those notations

Mario Carneiro (Apr 11 2020 at 23:56):

which have been used literally zero times

Mario Carneiro (Apr 11 2020 at 23:57):

even the functions they abbreviate are never used

Anas Himmi (Apr 12 2020 at 01:27):

how to prove this?

lemma nat.rec_on_sup {C :   Prop} (n i: ) (hp:n  i) (hi: C i) (hr :  (n : ), C n  C (nat.succ n)) : C n := sorry

when i do a proof by induction like this:

begin
induction n with n hn,
{ rwa [eq_bot_iff.mpr hp] at hi},
sorry
end

i get this goal state:

1 goal
case nat.succ
C :   Prop,
i : ,
hi : C i,
hr :  (n : ), C n  C (nat.succ n),
n : ,
hn : n  i  C n,
hp : nat.succ n  i
 C (nat.succ n)

so i can't access hn

Kevin Buzzard (Apr 12 2020 at 07:43):

Brandon B said:

starting with all of undergrad math as Kevin Buzzard is doing

I might have been one of the people who thought it was a good idea to focus on undergraduate mathematics, but when it comes to doing it, much of the community is involved and I am only playing an extremely small part. For example the basic theory of multivariable calculus, manifolds etc is (a) undoubtedly undergraduate mathematics (b) extremely subtle to do in a theorem prover (c) something I have had absolutely nothing to do with and (d) currently making huge progress thanks to people like @Sebastien Gouezel and @Patrick Massot and @Yury G. Kudryashov and others. I am the loud Lean public relations guy with access to a mathematical audience and this plays some role, but this is not "my project" in any way: it is the project I am passionate about, but that's something different.

Jason KY. (Apr 12 2020 at 11:18):

Anas Himmi said:

how to prove this?

I solved it by taking the cases of ini \le n then doing an induction on i<ni < n

import tactic

lemma nat.rec_on_sup
{C :   Prop} (n i : ) (hp : n  i) (hi : C i)
(hr :  (n : ), C n  C (nat.succ n)) : C n :=
begin
  cases lt_or_eq_of_le hp,
    {induction h with k hle ht,
      {exact hr i hi},
      replace hle : i + 1  k := hle,
      refine hr k (ht $ le_trans (nat.le_succ i) hle)},
    rwa h
end

Anas Himmi (Apr 12 2020 at 11:23):

Woow thank you!

Kevin Buzzard (Apr 12 2020 at 11:27):

Mario Carneiro said:

Maybe I'm not looking at the right theorems, but it's not very often I see a theorem where the statement is tractable but the proof is not, that I want to assume for something else

So in more serious mathematics this phenomenon shows up a lot -- I would say it's more the rule than the exception. But the point remains that at the level I'm talking about we cannot currently state the results we are interested in because we don't have the definitions in mathlib. Cheating on a definition has far more serious consequences than cheating on a proof -- indeed IIRC someone once posted an example where if you cheated on a definition then you got a proof for free, which isn't ideal because then when someone fills in the actual definition, the proof breaks.

Kevin Buzzard (Apr 12 2020 at 11:29):

@Brandon B I am hoping that once we have a ton of meaty definitions, e.g. in algebraic geometry, then there will be a lot of scope for stating interesting theorems and skipping proofs (not in mathlib, but in another place where one is concentrating on e.g. making search for mathematicians)

Kevin Buzzard (Apr 12 2020 at 11:31):

Unfortunately, there are a bunch of interesting theorems in mathematics about etale cohomology of Noetherian schemes, and it is going to take a whole bunch of category theory to even define etale cohomology. Maybe one cheap definition uses derived functors on an abelian category, but we don't even have abelian categories yet.

Mario Carneiro (Apr 12 2020 at 11:33):

So in more serious mathematics this phenomenon shows up a lot -- I would say it's more the rule than the exception. But the point remains that at the level I'm talking about we cannot currently state the results we are interested in because we don't have the definitions in mathlib.

Right, I certainly get the impression that deep theorems abound in higher mathematics, but most of these theorems are not in an accessible place from the point of view of definitions. You spent a bunch of time and effort on perfectoid spaces -- can we state the tilting lemma now? Can we prove basic (and I mean really basic) properties about perfectoid spaces?

Kevin Buzzard (Apr 12 2020 at 11:34):

We can't state Scholze's tilting theorem yet because we still need a load more machinery.

Mario Carneiro (Apr 12 2020 at 11:35):

Do you know any place where we would get a lot of mileage out of assuming a hard theorem that we can state today?

Kevin Buzzard (Apr 12 2020 at 11:35):

This might sound like a stupid thing to say, but I'm not sure there are any basic theorems about perfectoid spaces. I know that in UG maths you make a new object e.g. a ring, and then all of a sudden you can ask if the product of rings is a ring or whatever, but life is not like that on the mountains.

Mario Carneiro (Apr 12 2020 at 11:36):

I can imagine that something like that could unlock complex analysis

Kevin Buzzard (Apr 12 2020 at 11:36):

Mario Carneiro said:

Do you know any place where we would get a lot of mileage out of assuming a hard theorem that we can state today?

No. FLT is useless.

Kevin Buzzard (Apr 12 2020 at 11:36):

For complex analysis there is still the issue of figuring out what we're going to integrate around and I would imagine that we'd need a bunch of API for whatever it turns out to be before we can get going

Filip Szczepański (Apr 12 2020 at 11:48):

not_succ_le_self in the NNG mentions conv begin, but I'm not sure how one would actually use it. My solution ended up just doing a simple induction on a and is only seven lines, and I don't see where conv could be slotted in

Kevin Buzzard (Apr 12 2020 at 11:49):

Oh that's great. I can quite believe that my solutions aren't optimal.

Kevin Buzzard (Apr 12 2020 at 11:50):

The conv thing is just a very poorly-explained description of how to rewrite on only one side of an expression.

Filip Szczepański (Apr 12 2020 at 12:17):

It doesn't seem like either of the solutions for it in the NNG repo uses conv either

Kevin Buzzard (Apr 12 2020 at 12:18):

I thought I just checked that the "official" one (in src/game) did

Filip Szczepański (Apr 12 2020 at 12:30):

Ah, right, I was looking at the old ones

Filip Szczepański (Apr 12 2020 at 12:30):

My solution ended up just being

intro h,
cases h with c h,
induction a with a ha,
  rw succ_add at h,
  exact zero_ne_succ _ h,

  rw succ_add at h,
  exact ha(succ_inj h),

Filip Szczepański (Apr 12 2020 at 12:32):

I think my job just ended up being a lot easier because I did the cases before the induction instead of after?

Victor Ahlquist (Apr 12 2020 at 13:01):

Any idea why this doesn't work?

example : ((p ∨ q) → r) ↔ (p → r) ∧ (q → r) := iff.intro
    (assume h : (p ∨ q) → r,

        have f1 : p → r, from
            assume h1 : p,
            h (or.intro_left q h1),
        have f2 : q → r, from
            assume h1 : q,
            h (or.intro_right p h1),
        and.intro f1 f2,
    )

Victor Ahlquist (Apr 12 2020 at 13:02):

I get error message

type mismatch at application
  prod.mk _
term
  λ (h : p ∨ q → r),
    have f1 : p → r, from λ (h1 : p), h (or.intro_left q h1),
    have f2 : q → r, from λ (h1 : q), h (or.intro_right p h1),
    ⟨f1, f2⟩
has type
  (p ∨ q → r) → (p → r) ∧ (q → r) : Prop
but is expected to have type
  ?m_1 : Type ?
Additional information:
c:\Users\Victor\Desktop\LeanMath\my_project\src\test.lean:139:4: context: switched to simple application elaboration procedure because failed to use expected type to elaborate it, error message
  type mismatch, term
    (?m_3, ?m_4)
  has type
    ?m_1 × ?m_2 : Type (max ? ?)
  but is expected to have type
    (p ∨ q → r) → (p → r) ∧ (q → r) : Prop

Victor Ahlquist (Apr 12 2020 at 13:03):

Nvm just an extra comma.

Kevin Buzzard (Apr 12 2020 at 13:05):

Commas are a nightmare to track down in this verbose term mode.

Kevin Buzzard (Apr 12 2020 at 13:06):

Start like this:

example (p q r : Prop) : ((p  q)  r)  (p  r)  (q  r) :=
⟨_, _⟩

and go on from there, you're always in control then.

Reid Barton (Apr 12 2020 at 13:07):

I'm surprised you didn't get some kind of parse error

Reid Barton (Apr 12 2020 at 13:07):

Is (a, ) a tuple section (prod.mk a) or something?

Kevin Buzzard (Apr 12 2020 at 13:09):

⟨λ h, ⟨λ h1, h $ or.intro_left q h1, _⟩, _⟩

etc

Filip Szczepański (Apr 12 2020 at 13:13):

Would it make sense to replace the solutions in the repo with simpler ones where viable?

Kevin Buzzard (Apr 12 2020 at 13:16):

I don't really ever explicitly link to the repo but I guess there would be no harm in doing so in the future.

Victor Ahlquist (Apr 12 2020 at 13:27):

Kevin Buzzard said:

Start like this:

example (p q r : Prop) : ((p  q)  r)  (p  r)  (q  r) :=
⟨_, _⟩

and go on from there, you're always in control then.

Looks like a good way to do it. Starting from the bottom.

Victor Ahlquist (Apr 12 2020 at 15:08):

Any advice on how to complete:

example : ¬(p ↔ ¬p) :=

without law of the excluded middle?

Kevin Buzzard (Apr 12 2020 at 15:09):

congratulations on being the 1000th person to ask this question on the chat :-) Yeah, that's a great question! It's in TPIL right?

Kevin Buzzard (Apr 12 2020 at 15:09):

It can be done :-)

Victor Ahlquist (Apr 12 2020 at 15:11):

This was a tough one, I'll have to think a bit more then

Victor Ahlquist (Apr 12 2020 at 15:12):

And yes, it is in TPIL chapter 3

Bryan Gin-ge Chen (Apr 12 2020 at 15:18):

I once wrote a relatively detailed post on how to prove that one via "chasing underscores". See here to get "spoiled", though it should be written in a way where you can stop and try things out for yourself at each step.

Victor Ahlquist (Apr 12 2020 at 15:27):

Might have an idea

Victor Ahlquist (Apr 12 2020 at 15:27):

Is it possible to use other examples you've proved?

Bryan Gin-ge Chen (Apr 12 2020 at 15:32):

The beginning of that post links to this example. I also apparently did an exercise from Logic & Proof this way.

Victor Ahlquist (Apr 12 2020 at 15:35):

Well I don't want any spoilers. I just mean if it is possible in lean to reference a proof you have done earlier as an example.

Victor Ahlquist (Apr 12 2020 at 15:37):

The proof I wanted to reference was quite short so I just copied now, but would be good to know for future use.

Bryan Gin-ge Chen (Apr 12 2020 at 15:39):

Oh, sorry, I misinterpreted "you" in that reply. No, to reference a previous result it has to be a named theorem or lemma or def.

Victor Ahlquist (Apr 12 2020 at 15:40):

I see, I guess that's quite logical.

Kevin Buzzard (Apr 12 2020 at 15:40):

@Victor Ahlquist there's a canonical hint, if you want it.

Victor Ahlquist (Apr 12 2020 at 15:41):

I managed to solve it btw, posting here for anyone interested in my messy solution

Victor Ahlquist (Apr 12 2020 at 15:41):

example : ¬(p ↔ ¬p) :=
    (assume h : p ↔ ¬p,
        have h1 : p → ¬p, from iff.elim_left h,
        have h2 : ¬p → p, from iff.elim_right h,
        have h3 : p → false, from
            (assume f : p,
                h1 f f
            ),
        h3 (h2 h3)
    )

Kevin Buzzard (Apr 12 2020 at 15:41):

Also, I made a video about how I would prove your earlier question ((p ∨ q) → r) ↔ (p → r) ∧ (q → r).

Kevin Buzzard (Apr 12 2020 at 15:42):

Yeah, the hint is "prove not p"

Kevin Buzzard (Apr 12 2020 at 15:42):

Now watch my video and learn how to prove stuff like this without all this assume have stuff :-)

Victor Ahlquist (Apr 12 2020 at 15:42):

Kevin Buzzard said:

Now watch my video and learn how to prove stuff like this without all this assume have stuff :-)

I definitely will :)

Victor Ahlquist (Apr 12 2020 at 15:43):

Just need a minute to wrap my mind around what I just did, felt like a bunch of symbol pushing

Bryan Gin-ge Chen (Apr 12 2020 at 15:49):

Well, assume is just long-hand for λ. I wouldn't avoid using have, though I'm used to using ":=" instead of ", from".

Victor Ahlquist (Apr 12 2020 at 15:51):

Now that you say it, I remember that the notation in NNG was indeed := not from

Victor Ahlquist (Apr 12 2020 at 15:51):

I'm going through TPIL so I'm copying the notation from there.

Kevin Buzzard (Apr 12 2020 at 15:53):

TPIL gets on to tactic mode in chapter 5 and for mathematicians tactic mode is far less painful than all this assume stuff.

Kevin Buzzard (Apr 12 2020 at 15:53):

The natural number game is 100% tactic mode

Bryan Gin-ge Chen (Apr 12 2020 at 15:59):

Hmm, for some reason I thought the syntax have h, from blah only worked in term mode, but apparently it can be used in tactic mode as well.

Victor Ahlquist (Apr 12 2020 at 16:01):

Kevin Buzzard said:

TPIL gets on to tactic mode in chapter 5 and for mathematicians tactic mode is far less painful than all this assume stuff.

Yeah tactic mode is nicer for getting an overview of the proof and what needs to be done. I'm still intimated by all the lambda notation in term mode as you might have noticed from my code.

Victor Ahlquist (Apr 12 2020 at 16:09):

For me writing all the "assume" makes the code more understandable, but I guess that will change as I get more familiar with Lean.

Bryan Gin-ge Chen (Apr 12 2020 at 16:17):

Now that you've solved it, did you have a look at the "spoiler" posts I linked? Presumably your thought process was not too different from what I described there.

I think it's good that proving simple logical propositions like this feels like symbol pushing. To me it's like getting used to arithmetic or solving algebraic equations in school. Once you're at the stage where these calculations are "just symbol pushing", you can move on to letting a calculator or computer do it for you. In Lean, there are various clever tactics like cc which can solve goals like these automatically.

Victor Ahlquist (Apr 12 2020 at 16:21):

Ah sorry forgot about the link. Will check it out after dinner.

PV (Apr 12 2020 at 16:23):

Thanks for linking those posts. It helped get a better feel for not being in tactic mode, but having the same strategy for building out the proof.

Bryan Gin-ge Chen (Apr 12 2020 at 16:29):

Yeah, once I learned to read and use the error messages, term mode became a lot less intimidating. I think in principle we could hack Lean to show info in the goal window when putting your cursor in a term mode proof, but that's beyond my abilities. For example, a while back, @Mario Carneiro showed a neat trick where you insert by {}; exact somewhere in a term mode proof and then putting your cursor inside {} shows the context.

Victor Ahlquist (Apr 12 2020 at 17:05):

That was a nice method with the underscores, will probably come in handy in the future. When I did the problem I noticed that p \r p\r false is the same as p \and p \r false which simplifies to p \r false.

Alex Mira (Apr 12 2020 at 17:11):

Hey, this thread is for noob members right? I've spent the last few days playing around with lean, I finished the Nat Num game, and yesterday I downloaded elan to my computer. There's a lotta things I don't understand rn, but currently I'm just wondering how much the base version of Lean knows? It seems to have a \nat type that works well, and an \int type as well but I'm not sure if that one works. It just evaluated 5 - 7 to 0? I've heard there are libraries I could download, but I'd also like the practice of building up to the real numbers myself, if that's possible. Anyway, I'm mostly looking for a place to start, can anyone help me out?

Patrick Massot (Apr 12 2020 at 17:15):

Numbers 5 and 7 are interpreted as natural numbers by default, so Lean used nat subtraction which has type nat -> nat -> nat. Then read https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/integer.20subtraction.20.2F.20rational.20division/near/192901989

Patrick Massot (Apr 12 2020 at 17:15):

Defining real numbers from core library only is way too ambitious as a beginner goal. But you can play with mathlib real numbers.

Kevin Buzzard (Apr 12 2020 at 17:16):

What's your background @Alex Mira ? Maths/CS/other?

Bryan Gin-ge Chen (Apr 12 2020 at 17:16):

If you haven't already, follow the instructions here for your OS to install leanproject and to set up a Lean project that depends on mathlib.

Kevin Buzzard (Apr 12 2020 at 17:17):

Took the words out of my mouth :-)

Alex Mira (Apr 12 2020 at 17:18):

Alright, I'll definitely do that then. My background is in math, I'm an undergrad at U of T

Kevin Buzzard (Apr 12 2020 at 17:18):

Core Lean knows basic definitions of e.g. a group. Lean's maths library mathlib knows e.g. the theorems a 1st year undergraduate math group theory course, I think it knows one but not all of Sylow's theorems?

Bryan Gin-ge Chen (Apr 12 2020 at 17:18):

There are some nice resources under "Learn Lean" on the "Lean Links" page.

Kevin Buzzard (Apr 12 2020 at 17:19):

Actually isn't there some link on the docs page where I summarised what mathlib knew last Dec?

Kevin Buzzard (Apr 12 2020 at 17:19):

https://leanprover-community.github.io/mathlib_docs/overview.html

Alex Mira (Apr 12 2020 at 17:19):

I'm interested though, what Lean calls a Group, it wouldn't technically be the same thing that I think of as a group right? Namely, I'm trying to figure out just how much I can treat Types like they're sets.

Kevin Buzzard (Apr 12 2020 at 17:20):

What Lean thinks a group is is absolutely the same as what you think a group is.

Kevin Buzzard (Apr 12 2020 at 17:20):

Here's a question for you. Let G be a group. Let g be an element of G. What is g? Is it a set?

Kevin Buzzard (Apr 12 2020 at 17:20):

For sure G is a set. But what is g?

Patrick Massot (Apr 12 2020 at 17:20):

This is a natural feeling. You simply need to unlearn the lie that people told you you are using set theory.

Alex Mira (Apr 12 2020 at 17:20):

Heh, I figured the answer would be something like that.

Kevin Buzzard (Apr 12 2020 at 17:21):

If you set up mathematics in ZFC set theory then everything is a set. This is great for G, which you really want to be a set, but it is not great for g, because you want g to be an "atom" of some kind.

Patrick Massot (Apr 12 2020 at 17:21):

Type theory is exactly maths the way you think about them, except that you are very good at inserting inclusion maps and isomorphisms, and computer are not quite there yet (but they try really hard).

Kevin Buzzard (Apr 12 2020 at 17:21):

In Type theory, G is a Type, and g is a term. They're different things. g doesn't have elements or anything -- it doesn't even make sense to ask what is inside g. g is an atom.

Kenny Lau (Apr 12 2020 at 17:21):

I can imagine a parallel universe where one needs to instead unlearn the lie that we are using type theory; my question is, then what are we using that is somehow an abstracted version of type theory and set theory? or is the foundation separate from the maths?

Patrick Massot (Apr 12 2020 at 17:22):

And now is dinner time, so I'll let the IC team continue this explanation.

Kevin Buzzard (Apr 12 2020 at 17:22):

I think you're absolutely right Kenny -- I don't think Gauss was using either type theory or set theory

Kenny Lau (Apr 12 2020 at 17:22):

so all this nonsense about foundations and two solutions to Russell's Paradox -- what is the point?

Kevin Buzzard (Apr 12 2020 at 17:22):

@Alex Mira which U of T? There are several :-)

Alex Mira (Apr 12 2020 at 17:22):

Toronto

Kevin Buzzard (Apr 12 2020 at 17:23):

Russell's Paradox has a profound effect on foundations but a mathematician like Poincare will have heard about it and will have realised that it did not affect him in any way whatsoever

Kevin Buzzard (Apr 12 2020 at 17:24):

The problem with Lean's type theory, as far as mathematicians are concerned, is that if G and H are different types, then there can be no term x of type G and of type H: distinct types are disjoint by definition.

Alex Mira (Apr 12 2020 at 17:24):

Alright, sounds like my goal rn is to set up a Lean Project, and from there...what are some good goals to set myself if I want to get familiar with the software?

Kevin Buzzard (Apr 12 2020 at 17:25):

You could try working on some undergrad problems, that's how I learnt Lean

Kevin Buzzard (Apr 12 2020 at 17:25):

Try proving that the composite of two injective functions is injective.

Kenny Lau (Apr 12 2020 at 17:26):

just like how you would learn a natural language: you pick it up by using / reading it a lot

Kenny Lau (Apr 12 2020 at 17:26):

and asking us a lot of questions

Kenny Lau (Apr 12 2020 at 17:27):

with MWEs (Minimal Working Examples)

Kevin Buzzard (Apr 12 2020 at 17:27):

variables {X Y Z : Type}

def injective (f : X  Y) :=  a b : X, f a = f b  a = b

lemma injective_comp :  (f : X  Y) (g : Y  Z),
  injective f  injective g  injective (g  f) :=
begin
  sorry
end

Kevin Buzzard (Apr 12 2020 at 17:27):

Note I write a b : X instead of a,bXa,b\in X because I'm using the type theory conventions. a : X means "a is a term of type X" which is just the type theory way of saying "a is an element of the set X"

Bryan Gin-ge Chen (Apr 12 2020 at 17:28):

I think people often work through Theorem Proving in Lean next. The "Hitchhiker's Guide" is a new book that I like too.

Kevin Buzzard (Apr 12 2020 at 17:28):

And now you can forget that you're doing type theory and just think of it all in set theory because at this sort of level they are the same theory.

Kevin Buzzard (Apr 12 2020 at 17:28):

TPIL is a great way to find out more about type theory in this context.

Alex Mira (Apr 12 2020 at 17:28):

Heh, I already did that, and the surjective one. Those were the challenges that didn't require imports I think. Thanks so much!

Alex Mira (Apr 12 2020 at 17:28):

TPIL?

Alex Mira (Apr 12 2020 at 17:29):

Oh

Kevin Buzzard (Apr 12 2020 at 17:29):

I am always in more of a hurry to recommend it to CS people than maths people.

Kevin Buzzard (Apr 12 2020 at 17:29):

Do you know Florian Herzig Alex?

Alex Mira (Apr 12 2020 at 17:29):

I do! I'm in his Algebra class!

Bryan Gin-ge Chen (Apr 12 2020 at 17:29):

We're still waiting on Kevin to write his book on Lean for Generic Mathematicians... :joy:

Kenny Lau (Apr 12 2020 at 17:29):

you might not be able to build the theory of real numbers completely; but you'll be sure to learn a lot on the way

Kevin Buzzard (Apr 12 2020 at 17:30):

Yeah, I'm still working on it.

Kevin Buzzard (Apr 12 2020 at 17:30):

Books are so 20th century though :-/

Kevin Buzzard (Apr 12 2020 at 17:30):

Florian works in the same area as me.

Kevin Buzzard (Apr 12 2020 at 17:31):

Part of the reason he has tenure at UT is that I wrote him a nice reference letter :-)

Bryan Gin-ge Chen (Apr 12 2020 at 17:31):

I think you can still call it a book even if it has a bunch of interactive bells and whistles.

Kevin Buzzard (Apr 12 2020 at 17:40):

@Alex Mira if you have got leanproject working on your computer then you can clone the group theory game and open it in VS Code and then fill in the sorrys in src/group/level01.lean

Kevin Buzzard (Apr 12 2020 at 17:40):

This is a work in progress

Kevin Buzzard (Apr 12 2020 at 17:42):

leanproject get ImperialCollegeLondon/group-theory-game

and then in VS Code go to "open folder" and then open the group-theory-game folder which just appeared in the directory where you were when you ran leanproject.

Kevin Buzzard (Apr 12 2020 at 17:44):

It's really hard to get started with the group theory game, because I only give you the minimal axioms needed for a group. Proving mul_one and mul_right_inv is a bit of a challenge. Then it gets much easier.

Alex Mira (Apr 12 2020 at 17:46):

Sounds fun! Ill definitely try it out.

Victor Ahlquist (Apr 12 2020 at 18:38):

Is there a tactic that applies a function to both sides of an equality?

Kenny Lau (Apr 12 2020 at 18:41):

replace h := congr_arg f h

Victor Ahlquist (Apr 12 2020 at 18:44):

thanks

Kevin Buzzard (Apr 12 2020 at 18:48):

This is something which mathematicians frequently want to do -- did @Patrick Massot ever write a tactic to do this?

Bryan Gin-ge Chen (Apr 12 2020 at 18:49):

https://leanprover-community.github.io/mathlib_docs/tactics.html#apply_fun

Kevin Buzzard (Apr 12 2020 at 18:50):

apply_fun f at h is a better answer to this question nowadays. It feels more natural.

Bryan Gin-ge Chen (Apr 12 2020 at 18:51):

We should have a tactic named have_fun.

Victor Ahlquist (Apr 12 2020 at 19:01):

Kevin Buzzard said:

apply_fun f at h is a better answer to this question nowadays. It feels more natural.

Yeah it does. Now the second problem is: How do I make a function

Victor Ahlquist (Apr 12 2020 at 19:02):

I guess I shouldn't start defining things carelessly. I just want a function that multiplies by an element from left/right in the group theory game.

Kenny Lau (Apr 12 2020 at 19:02):

wait, what is the group theory game?

Kevin Buzzard (Apr 12 2020 at 19:02):

it doesn't exist yet

Kevin Buzzard (Apr 12 2020 at 19:03):

https://github.com/ImperialCollegeLondon/group-theory-game

Kenny Lau (Apr 12 2020 at 19:04):

multiplication on the left by g is ((*)g)

Kevin Buzzard (Apr 12 2020 at 19:04):

apply_fun (λ x, g * x) at h,

Kenny Lau (Apr 12 2020 at 19:04):

multiplication on the right by g is (*g)

Kevin Buzzard (Apr 12 2020 at 19:04):

the power of lambda.

Victor Ahlquist (Apr 12 2020 at 19:06):

Thanks, I'll try to learn to handle the lambdas

Kevin Buzzard (Apr 12 2020 at 19:07):

λ x, y just means xyx\mapsto y

Kevin Buzzard (Apr 12 2020 at 19:07):

It's a way of defining a function without giving it a name

Victor Ahlquist (Apr 12 2020 at 19:07):

Yeah I know, I'm just not used to the notation. Chapter 2 of TPIL had a lot of lambda functions

Victor Ahlquist (Apr 12 2020 at 19:08):

Is it possible to use the apply_fun to a goal?

Kevin Buzzard (Apr 12 2020 at 19:08):

no

Kevin Buzzard (Apr 12 2020 at 19:08):

If I had to prove x=y and I was allowed to multiply both sides by 0, I'd be in very good shape

Victor Ahlquist (Apr 12 2020 at 19:09):

Haha that's true.

Victor Ahlquist (Apr 12 2020 at 19:09):

Hmm but wait that wouldn't work anyway.

Kevin Buzzard (Apr 12 2020 at 19:09):

Your question is "if f(x)=f(y) then does x=y?"

Kevin Buzzard (Apr 12 2020 at 19:09):

and the answer is "only if f is injective"

Kevin Buzzard (Apr 12 2020 at 19:10):

applying it a hypothesis, the question is "if x = y, does f(x)=f(y)" which is a rather different question

Victor Ahlquist (Apr 12 2020 at 19:11):

Ah right.

Kevin Buzzard (Apr 12 2020 at 19:13):

Changing the goal is working backwards.

Victor Ahlquist (Apr 12 2020 at 19:13):

But wait

Kevin Buzzard (Apr 12 2020 at 19:14):

If your goal is f x = f y you can change it to x = y with apply congr_arg

Victor Ahlquist (Apr 12 2020 at 19:14):

Say I had goal a *1 = a 1, and for some reason I do not use refl immediately. Then wouldn't it be possible to do something like apply apply_fun (\lambda x. ax), to get 1=1 instead

Victor Ahlquist (Apr 12 2020 at 19:14):

Yeah that's what I want

Kevin Buzzard (Apr 12 2020 at 19:15):

No, because apply_fun does not work on goals.

Kevin Buzzard (Apr 12 2020 at 19:15):

The fact that it is logically sometimes true doesn't mean that the tactic will ever work.

Victor Ahlquist (Apr 12 2020 at 19:15):

Yea happly_fun was a placeholder for another similar function

Kenny Lau (Apr 12 2020 at 19:15):

Kevin Buzzard said:

If your goal is f x = f y you can change it to x = y with apply congr_arg

congr' 1

Kevin Buzzard (Apr 12 2020 at 19:15):

Right

Kevin Buzzard (Apr 12 2020 at 19:16):

You have to write `a * 1` or else Zulip thinks you're trying to put something in italics.

Victor Ahlquist (Apr 12 2020 at 19:16):

Ah ok

Kevin Buzzard (Apr 12 2020 at 19:17):

You can't get from a * x = a * y to x = y because a could be 0.

Victor Ahlquist (Apr 12 2020 at 19:17):

Not in a group

Kevin Buzzard (Apr 12 2020 at 19:18):

Sure

Kevin Buzzard (Apr 12 2020 at 19:18):

Can you prove it in a group though?

Victor Ahlquist (Apr 12 2020 at 19:18):

Do I specify the function after congr_arg?

Victor Ahlquist (Apr 12 2020 at 19:18):

Kevin Buzzard said:

Can you prove it in a group though?

Yep just did :)

Kevin Buzzard (Apr 12 2020 at 19:18):

#check @congr_arg to see what you have to specify

Victor Ahlquist (Apr 12 2020 at 19:19):

Doing the mul_one now, which is why I asked the above question. Then I realized I would need mul_one to use that tactic but still good to know the answer.

Victor Ahlquist (Apr 12 2020 at 19:19):

Kevin Buzzard said:

#check @congr_arg to see what you have to specify

Thanks

Kevin Buzzard (Apr 12 2020 at 19:37):

apply is a clever tactic. It unifies the conclusion of what you're applying with the goal and then solves the corresponding puzzle

Victor Ahlquist (Apr 12 2020 at 20:04):

That was a short and sweet game :)

Kevin Buzzard (Apr 12 2020 at 20:05):

It's not finished yet :-)

Kevin Buzzard (Apr 12 2020 at 20:05):

So here's something I don't know how to do

Victor Ahlquist (Apr 12 2020 at 20:05):

Kevin Buzzard said:

apply is a clever tactic. It unifies the conclusion of what you're applying with the goal and then solves the corresponding puzzle

I usually like apply, but now I managed without using it a single time

Kevin Buzzard (Apr 12 2020 at 20:06):

In the natural number game, you prove add_left_cancel and add_assoc and add_comm and you tag them all with simp and then you can prove (a+(b+(c+d)))=(d+b)+(c+a)(a+(b+(c+d)))=(d+b)+(c+a) by simp

Kevin Buzzard (Apr 12 2020 at 20:06):

I am a bit unclear about what the analogous story is for groups

Kevin Buzzard (Apr 12 2020 at 20:06):

and I am even more unclear about what the story is for proving implications

Kevin Buzzard (Apr 12 2020 at 20:06):

i.e. gh=gk    h=kgh=gk\implies h=k

Kevin Buzzard (Apr 12 2020 at 20:07):

My gut feeling is that you should prove a few of them and then let AI take over

Kevin Buzzard (Apr 12 2020 at 20:07):

but the analogous story for rings is the theory of Groebner bases

Kevin Buzzard (Apr 12 2020 at 20:08):

There was a discussion here

Kevin Buzzard (Apr 12 2020 at 20:09):

but I knew a bit less about confluent rewrites back then

Reid Barton (Apr 12 2020 at 20:09):

Groebner bases are for commutative rings. For groups this is the word problem, and it is undecidable in general.

Kevin Buzzard (Apr 12 2020 at 20:10):

For implications it's simpler than that

Kevin Buzzard (Apr 12 2020 at 20:11):

https://www3.nd.edu/~andyp/notes/OneRelator.pdf

Reid Barton (Apr 12 2020 at 20:12):

The statement forall G (a b c ... z : G), eqn1 -> eqn2 -> ... -> eqnN is the same as asking whether eqnN holds in the free group presented by the assumptions.

Reid Barton (Apr 12 2020 at 20:12):

Okay yes, for a single assumption

Kevin Buzzard (Apr 12 2020 at 20:12):

The non-obvious thing we ran into was a * b * c * d = 1 -> d * a * b * c = 1

Kevin Buzzard (Apr 12 2020 at 20:14):

Patrick's group tactic seems to use a finite list of theorems

Kenny Lau (Apr 12 2020 at 20:15):

can Section 3 (the solution to the word problem for one-relator groups) be formalized (easily)?

Victor Ahlquist (Apr 12 2020 at 20:16):

Kevin Buzzard said:

The non-obvious thing we ran into was a * b * c * d = 1 -> d * a * b * c = 1

Non-trivial for a computer to solve?

Bryan Gin-ge Chen (Apr 12 2020 at 20:24):

can Section 3 (the solution to the word problem for one-relator groups) be formalized (easily)?

How do we express "the word problem is solvable" in Lean? Theorem 2.1 looks like a good first target in any case.

Kenny Lau (Apr 12 2020 at 20:30):

decidable_eq

Jalex Stark (Apr 12 2020 at 22:29):

what's the name of the obvious term of type inhabited \a \to \a ?

Reid Barton (Apr 12 2020 at 22:31):

you should find it either by jump-to-definition to inhabited and look nearby, or inhabited.<completion>

Jalex Stark (Apr 12 2020 at 22:33):

Thanks for the advice! I think the term is default
oh, more accurate is that if q: inhabited \a then q.default:\a

Mario Carneiro (Apr 12 2020 at 23:35):

Bryan Gin-ge Chen said:

Hmm, for some reason I thought the syntax have h, from blah only worked in term mode, but apparently it can be used in tactic mode as well.

This is due to some clever tricks in tactic parsing. have allows you to omit any of its pieces and it makes a subgoal if you skip the proof of the have, so have h : foo, will make a subgoal proving |- foo, and have h, will make a subgoal proving ?m_1. The comma splits this into two tactics, and the second part is from blah, so from needs to be a tactic. So I made it an alias for exact, and then from blah will make blah the proof of ?m_1 and then the type gets determined and everything else works out.

Bryan Gin-ge Chen (Apr 12 2020 at 23:44):

That's great! Thanks for the explanation.

Mario Carneiro (Apr 12 2020 at 23:45):

I try to only use from after have, but it's one character shorter and I sometimes see Kenny using it for code golfing :)

Reid Barton (Apr 12 2020 at 23:52):

In French you could save two more characters

Mario Carneiro (Apr 12 2020 at 23:57):

Are we going to localize lean then?

Mario Carneiro (Apr 12 2020 at 23:58):

I never really understood why non-english programmers were content to just use english programming languages

Andrew Ashworth (Apr 13 2020 at 00:05):

unicode support is still terrible in lots of mainstream languages

Andrew Ashworth (Apr 13 2020 at 00:06):

documentation tends to be written in english, it makes it easier to search stackoverflow if you use english terminology

Andrew Ashworth (Apr 13 2020 at 00:07):

for a lot of domain-specific software technology, there is no translation of the terminology

Scott Morrison (Apr 13 2020 at 00:51):

Victor Ahlquist said:

I managed to solve it btw, posting here for anyone interested in my messy solution

Here's a cute proof, observing that tidy can follow the hint:

example (p : Prop) : ¬(p  ¬p) :=
λ h, by { have : ¬p, tidy, }

Chris M (Apr 13 2020 at 06:30):

How do we do syntax highlighting for Lean in zulipchat? I've tried triple grave accent followed by lean, but it doesn't work?

axiom test : \forallx, x=x

edit: ok it only didn't work in "Drafts"

Victor Ahlquist (Apr 13 2020 at 15:24):

(deleted)

Victor Ahlquist (Apr 13 2020 at 16:39):

def prime (n : ℕ) : Prop :=  n > 1 ∧ (∀ (k : ℕ), k ∣ n → (k = 1) ∨ (k = n))

def infinitely_many_primes : Prop := (∀ (n : ℕ), ∃ (prime p), p > n))

I'm trying to define "infinitely many primes" but this gets a ton of errors. Not sure if any of it is right.

Victor Ahlquist (Apr 13 2020 at 16:39):

Errors at 2nd line, first is ok.

Jason KY. (Apr 13 2020 at 16:42):

Would this work for you?

def prime (n : ) : Prop :=  n > 1  ( (k : ), k  n  (k = 1)  (k = n))

def infinitely_many_primes : Prop :=( (n : ),  p : , p > n  prime p)

Victor Ahlquist (Apr 13 2020 at 16:43):

Thanks that looks much better. Now I understand what Lean meant by "prime is not a type"

Victor Ahlquist (Apr 13 2020 at 17:06):

Which of these ways of writing is preferable in Lean?

def goldbach_conjecture : Prop := ∀ (n : ℕ), even n ∧ n > 2 →  ∃ (p1 p2 : ℕ), prime p1 ∧ prime p2 ∧ n = p1 + p2
def goldbach_conjecture : Prop := ∀ (n : ℕ),  ∃ (p1 p2 : ℕ), even n ∧ n > 2 → prime p1 ∧ prime p2 ∧ n = p1 + p2

Reid Barton (Apr 13 2020 at 17:13):

The first one seems better but mostly because it's what one actually means to say

Victor Ahlquist (Apr 13 2020 at 17:15):

Yes, the first one looks like it will play better with Lean too, but I don't think I've ever seen a statement written like that before.

Victor Ahlquist (Apr 13 2020 at 17:15):

Although I guess I haven't seen many formalizations.

Bryan Gin-ge Chen (Apr 13 2020 at 17:16):

I think I'd write the first one like this, removing all the :

def goldbach_conjecture : Prop :=
 (n : ), even n  n > 2   (p1 p2 : ) (hp1 : prime p1) (hp2 : prime p2), n = p1 + p2

Victor Ahlquist (Apr 13 2020 at 17:18):

That does look more Leany

Reid Barton (Apr 13 2020 at 17:27):

compare https://github.com/leanprover-community/mathlib/blob/master/src/topology/separation.lean#L344 for example

Kevin Buzzard (Apr 13 2020 at 17:28):

Are you saying that normal_space could be easier to use if the ands are removed, or are you saying that the way it's done in mathlib is a good idea?

Reid Barton (Apr 13 2020 at 17:29):

what would you replace the ands with?

Reid Barton (Apr 13 2020 at 17:29):

I'm just saying this is normal Lean code

Kevin Buzzard (Apr 13 2020 at 17:30):

exists (u) (v) (hu : is_open u) (hv : is_open v) (hsu : s \sub u) ...

Reid Barton (Apr 13 2020 at 17:30):

Also, it really doesn't make much difference whether you pass two hypotheses as an and or separately. This is just style.
The main thing is to put the assumptions and the quantifiers in the right order

Reid Barton (Apr 13 2020 at 17:31):

Ah, well, this seems kind of worse but also not important

Kevin Buzzard (Apr 13 2020 at 17:31):

..., true

Bryan Gin-ge Chen (Apr 13 2020 at 17:31):

I guess we could get everything with rcases either way.

Reid Barton (Apr 13 2020 at 17:33):

Or a let, and it will even be the same syntax

Bryan Gin-ge Chen (Apr 13 2020 at 17:45):

On the other hand, if we have to prove a goal of the form exists (u) (v) (hu : is_open u) ..., then we can write use [u, v, hu, ...], whereas if it's exists (u) (v), is_open u ∧ ... then it'd be use [u, v, \<hu, ... \>]. Slightly more annoying but not a big deal.

In the case of normal_space, it's more important that it's consistent with all the other separation statements in the file that use ands.

Reid Barton (Apr 13 2020 at 17:46):

Ah, I just always use refine, so it makes no difference there as well

ROCKY KAMEN-RUBIO (Apr 13 2020 at 19:47):

I've had issues getting various assoc tactics to cooperate. I think I'm misunderstanding something about the way they're supposed to be used. For example, in this proof of zero_add , I want to rewrite the LHS of my goal state so I can apply my inductive hypothesis, but I get an error that Lean couldn't find an instance of the pattern even though I clearly see that the pattern is there. Does it think the k I'm feeding add_assoc is different from the k that's in the goal state?

import data.nat.basic
open nat
theorem zero_add (m : nat) : zero + m = m :=
begin
induction m with k hk,
refl,
rw succ_eq_add_one,
rw  add_assoc 0 k 1,
end

Edit: added the import and open statements before the theorem.
Screen-Shot-2020-04-13-at-3.41.15-PM.png

Kenny Lau (Apr 13 2020 at 19:53):

it works for me

Kenny Lau (Apr 13 2020 at 19:53):

MWE?

Kevin Buzzard (Apr 13 2020 at 19:57):

Just try rw \l add_assoc

Kenny Lau (Apr 13 2020 at 19:57):

what I meant is, since this snippet doesn't even compile, that the following works:

theorem zero_add' (m : nat) : nat.zero + m = m :=
begin
induction m with k hk,
refl,
rw nat.succ_eq_add_one,
rw  add_assoc 0 k 1,
end

Kenny Lau (Apr 13 2020 at 19:57):

tactic failed, there are unsolved goals
state:
case nat.succ
k : ,
hk : 0 + k = k
 0 + k + 1 = k + 1

ROCKY KAMEN-RUBIO (Apr 13 2020 at 20:54):

Kenny Lau said:

what I meant is, since this snippet doesn't even compile, that the following works:

theorem zero_add' (m : nat) : nat.zero + m = m :=
begin
induction m with k hk,
refl,
rw nat.succ_eq_add_one,
rw  add_assoc 0 k 1,
end

Sorry, I should have included the import data.nat.basic and open nat at the beginning. I just added those. This was part of a larger proof and I forgot about those dependencies.

Kevin Buzzard (Apr 13 2020 at 20:55):

import data.nat.basic
open nat
theorem zero_add' (m : nat) : zero + m = m :=
begin
induction m with k hk,
refl,
rw succ_eq_add_one,
rw  add_assoc 0 k 1,
rw hk,
end

Kevin Buzzard (Apr 13 2020 at 20:56):

This works for me.

Kevin Buzzard (Apr 13 2020 at 20:56):

Can you please post a fully working code snippet which shows your problem? So far you have not done this. PS don't edit, just post below.

ROCKY KAMEN-RUBIO (Apr 13 2020 at 21:08):

I restarted my computer for an unrelated reason and now the issue seems to be resolved. This has happened to me before in Lean and I don't completely understand why.

Anyway here is a working code snippet. I'll repost instead of editing in the future. Thanks for all your help!

import data.nat.basic
open nat
theorem zero_add' (m : nat) : zero + m = m :=
begin
induction m with k hk,
refl,
rw succ_eq_add_one,
rw  add_assoc 0 k 1,
rw hk,
end

Kevin Buzzard (Apr 13 2020 at 21:09):

I have certainly seen weird errors in the past which have been fixed by just quitting VS Code and restarting it again.

Kevin Buzzard (Apr 13 2020 at 21:10):

There are certain errors about memory overflows which really screw Lean up (and probably screw up any computer program). If you get one of those you should probably at the very least restart Lean (with ctrl-shift-P restart Lean)

Kevin Buzzard (Apr 13 2020 at 21:10):

but it continues to function just about normally if you don't.

ROCKY KAMEN-RUBIO (Apr 13 2020 at 21:13):

These errors were happening in a file of ~500 lines. This file also had a few other unrelated unresolved errors. Could that be a factor?

Kevin Buzzard (Apr 13 2020 at 21:13):

Yes! Your actual problem is the first error in the file.

Kevin Buzzard (Apr 13 2020 at 21:13):

https://xenaproject.wordpress.com/2020/03/24/no-errors/

ROCKY KAMEN-RUBIO (Apr 13 2020 at 21:18):

Good to know. I was leaving errors in place so that it would be easier to come back to them if I was working through a long set of problems, but I guess I should stop doing that.

ROCKY KAMEN-RUBIO (Apr 13 2020 at 21:18):

Sometimes I see students whose file is absolutely full of errors. Their file is 400 lines long, getting really slow, and has 23 errors. They ask me why something doesn’t work, and I know from experience that the answer might be of the form “because something you wrote 100 lines up which caused an error and means that things aren’t the way you think they are”. Some people can get into a real state.

LOL literally me.

Kevin Buzzard (Apr 13 2020 at 21:20):

You are but one of many ;-)

Brandon B (Apr 13 2020 at 21:41):

I was reading Kevin Buzzard's recent post "Proofs are not programs" < https://xenaproject.wordpress.com/2019/06/15/proofs-are-not-programs/ > which argues that only in constructive logic is the Curry-Howard correspondence held (proofs are programs) and that in general in ZFC with the axiom of choice and excluded middle etc that Curry-Howard does not apply; which is why is why in lean we label certain things as noncomputable. However, looking at https://en.wikipedia.org/wiki/Lambda-mu_calculus it seems to suggest that there are more sophisticated lambda calculi that can model "classical logic" with excluded middle etc and still maintain a form of the curry-howard correspondence. Is lean's type system CoC just not rich enough to model classical deductions with computational content?

Kevin Buzzard (Apr 13 2020 at 21:46):

I bet they can't model the axiom of choice.

Kevin Buzzard (Apr 13 2020 at 21:47):

Lean's system is rich enough to model that -- and that is why I'm still here.

Andrew Ashworth (Apr 13 2020 at 22:05):

urgh, if you really want to go into gory detail, you could try chapter 8 of http://disi.unitn.it/~bernardi/RSISE11/Papers/curry-howard.pdf

Andrew Ashworth (Apr 13 2020 at 22:07):

but this is written for computer scientists studying type theory

Kevin Buzzard (Apr 13 2020 at 22:14):

gaargh it's got those weird fractions in :-/

Andrew Ashworth (Apr 13 2020 at 22:18):

that's why I prefixed my statement with "urgh", but maybe I should've added "argh" and "oh no, warning: theoretical computer science"

Mario Carneiro (Apr 13 2020 at 23:06):

The basic idea behind computational semantics for EM (or more simply, double negation elimination) is that you put everything in continuation passing style, the logical equivalent being putting double negations on everything, and then all the classical theorems become intuitionistically provable, and the computational semantics are that of call/cc or exception handling.

If you wanted to do something similar with full choice, I think you could do it like so: Suppose you want to produce an element of type A satisfying p. Save the current continuation, and then start enumerating well formed terms of type A. Return the first one you find (whether or not it satisfies p). Later, the program may later discover that you lied to it and will prove that the element does not in fact satisfy p, deriving a contradiction. When you call false.elim on this proof of false, the eliminator goes back to the original continuation, "rewinding the universe" to the original state, whereupon it tries the next term it finds.

This should satisfy type correctness, but it is not strongly normalizing as you might be enumerating terms forever looking for one that satisfies p.

Marc Huisinga (Apr 13 2020 at 23:06):

as far as i can tell, there's a catch to this lambda-mu-stuff. you won't get "computational content" in the traditional sense, otherwise every proposition would be decidable (in the sense that a TM can output yes/no correctly and halt). so i don't think kevin is wrong here, but you can stretch the definitions a bit to get something that you might call "computational content" again.

as in the link by andrew, the trick is that you enter a special "continuation world" whenever you work with some classical proof that you'd write with LEM or peirce's law. i think mario has described this with time travel before somewhere in this chat, which is quite a cool analogy. the problem is that this "continuation world" does not behave like the normal world in which you program things. for instance, you can't just use LEM, print 0 in the first path and print 1 in the second path, because then you'd have a decider for every proposition.

so now some stuff that i was wondering about, please correct me if it's nonsense.
so when you've got some proof that lives in this continuation world, you need to leave it again so that you can actually observe the results of your program. from what i understand, to do this, you eventually need to construct the object, and if you apply a continuation with your constructed object, the whole chain of continuations unfolds itself.
so in the end, this type of computational content turns these classical proofs into powerful control operators, but to do anything "useful" with them, you eventually need to perform the construction.

i think that there's something similar for choice, but i can't find the paper right now and i didn't understand it well to begin with.

Mario Carneiro (Apr 13 2020 at 23:09):

You can leave the continuation world fairly simply by applying the CPS converted program to the continuation print

Mario Carneiro (Apr 13 2020 at 23:09):

You can do so with a proof of em p and it will print p is false

Marc Huisinga (Apr 13 2020 at 23:10):

... which is not very helpful

Mario Carneiro (Apr 13 2020 at 23:11):

It's sort of "false until proven true"

Mario Carneiro (Apr 13 2020 at 23:12):

It's something like a maximal overestimate of soundness that is still type correct

Mario Carneiro (Apr 13 2020 at 23:12):

which is to say, it's not sound

Mario Carneiro (Apr 13 2020 at 23:12):

but it's also not inconsistent

Marc Huisinga (Apr 13 2020 at 23:15):

if you want to use the not p you get from em, you need a proof of p. so you apply it to not p and then the program will happily continue on the path where p is true without continuations, right?

Marc Huisinga (Apr 13 2020 at 23:37):

would this stuff help with automation in theorem provers?

Mario Carneiro (Apr 13 2020 at 23:40):

I know that Cont A is a monad, and this is sometimes used for exception passing in haskell, but for the most part I just see this as a cute trick. It is generally best to say what you mean when programming and these techniques push somewhat in the wrong direction

PV (Apr 14 2020 at 01:12):

Are you able to do a "use" like tactic on a premise in this scenario to choose some value for c?
case or.inr
a h : mynat,
hdd : ∃ (c : mynat), a = h + c
⊢ a = h+c

Shing Tak Lam (Apr 14 2020 at 01:17):

Where does the c in your goal come from? since it's not in your list of local hypotheses.

Also is hdd is a proof that there is a c such that a = h + c, but you're required to prove that a = h + c for all c

Shing Tak Lam (Apr 14 2020 at 01:18):

Also seeing that your goal is mynat, I presume this is the natural numbers game?

PV (Apr 14 2020 at 01:18):

yea, i think i may have ended up in a name collision

Shing Tak Lam (Apr 14 2020 at 01:19):

Is that the whole goal/tactic state? Even if there was a name collision c should still be there.

Shing Tak Lam (Apr 14 2020 at 01:19):

So I guess post your code (and which level it's for)?

PV (Apr 14 2020 at 01:23):

code here:
induction b with h hd,
right,
apply zero_le,
left,
apply le_succ,
cases hd with hh hdd,
assumption,
rw le_iff_exists_add at hdd,
rw le_iff_exists_add,
use 0,
symmetry,
simp,

level 9 on inequality world.

Shing Tak Lam (Apr 14 2020 at 01:26):

Level 9, so le_total?

I just pasted your code into the Natural Numbers Game, and the tactic state is

a h : mynat,
hdd : ∃ (c : mynat), a = h + c
⊢ a = h

Which is different to what you posted... Also none of your hypotheses are useful here, so you've gone down the wrong path

Shing Tak Lam (Apr 14 2020 at 01:27):

Yeah you definitely have, going back up a bit your goal is

case or.inr
a h : mynat,
hdd : h ≤ a
⊢ a ≤ h

PV (Apr 14 2020 at 01:29):

ahh thanks

Nam (Apr 14 2020 at 04:41):

What is the next step I could try to prove this? i'm reading page 63 of Hitchhiker's Guide but would like to do away with simp.

α : Type,
_inst_1 : inhabited α,
xs_hd : α,
xs_tl : list α,
xs_ih : map (λ (x : α), x) xs_tl = xs_tl
⊢ map (λ (x : α), x) (xs_hd :: xs_tl) = xs_hd :: xs_tl

Shing Tak Lam (Apr 14 2020 at 04:42):

I haven't read it, but try unfold map or rw map?

Nam (Apr 14 2020 at 04:43):

oh, nice! thank you. they both work. i didn't think of rewrite.

Nam (Apr 14 2020 at 04:48):

unfold seems to be better

Nam (Apr 14 2020 at 05:04):

now i'm stuck at

Nam (Apr 14 2020 at 05:04):

case list.cons
α β : Type,
f : α → β,
ys : list α,
xs_hd : α,
xs_tl : list α,
xs_ih : map f (xs_tl ++ ys) = map f xs_tl ++ map f ys
⊢ map f (xs_hd :: xs_tl ++ ys) = map f (xs_hd :: xs_tl) ++ map f ys

Nam (Apr 14 2020 at 05:05):

unfold does not pop xs_hd out of map

Shing Tak Lam (Apr 14 2020 at 05:05):

rw map_append?

Nam (Apr 14 2020 at 05:06):

map_append is what i want to prove ;)

Shing Tak Lam (Apr 14 2020 at 05:06):

Ahh.

Nam (Apr 14 2020 at 05:10):

i see. i need to make the input list (xs_hd :: xs_tl ++ ys) into the right form with rewrite list.cons_append, then the unfold works nicely.

Shing Tak Lam (Apr 14 2020 at 05:11):

yeah, I was gonna suggest you prove cons_append but I guess using the one provided is fine :)

Charley Hutchison (Apr 14 2020 at 06:51):

Are von Neumann algebras (or C^* algebras) a long way from being defined (in terms of how much necessary prerequisite material is implemented, not interest), or could this be done with some concerted effort? I'm not sure whether or not the analysis done so far would is sufficient

Kevin Buzzard (Apr 14 2020 at 07:29):

You could ask this in the "is there code for X" stream. Not all the experts read #new members and it's a perfectly respectable question. In general the nontrivial mathematical stuff seems to be stuff put there by mathematicians who work in that area. I don't even remember seeing Hilbert spaces in mathlib, although we have all the ingredients for that.

Kevin Buzzard (Apr 14 2020 at 07:29):

My guess is that it would not be hard to define them, given what we have, but that nobody defined them yet

Sebastien Gouezel (Apr 14 2020 at 07:35):

I don't think we have anything serious on Hilbert spaces, notably no adjoint operator and no positive operator, so it would be some work (but not too much, I think) to give the usual constructions of concrete C^* algebras (Note that we already have the norm topology and topological stuff, so saying that a set is closed for the norm topology is already there). Doing the abstract theory (complex Banach algebra with a nice involution) would probably be easier, and rather quick.

Scott Morrison (Apr 14 2020 at 07:47):

I had a student last year do a lot of the basics (including adjoint operators, I think) on Hilbert spaces, but it never made it to a PR. I think this is all good juicy material for mathlib!

Patrick Massot (Apr 14 2020 at 07:59):

I think this is a very good first project.

Valentin Vinoles (Apr 14 2020 at 10:02):

If I do the following

variables (E : Type)(G : group E)(H : group E)
variables (x : E)(y : E)
#check x*y

Did * refers to the operation on G or the operation on H?

Kenny Lau (Apr 14 2020 at 10:03):

variables (E : Type)(G : group E)(H : group E)
variables (x : E)(y : E)
set_option pp.all true
#check x*y
-- @has_mul.mul.{0} E (@semigroup.to_has_mul.{0} E (@monoid.to_semigroup.{0} E (@group.to_monoid.{0} E H))) x y : E

Kenny Lau (Apr 14 2020 at 10:03):

the answer is H

Kenny Lau (Apr 14 2020 at 10:03):

the real answer is don't ever do this

Valentin Vinoles (Apr 14 2020 at 10:04):

Thank you ! So what should I do if I want to consider two different groups (i.e. two different operations) on the same set ?

Kenny Lau (Apr 14 2020 at 10:07):

Are you trying to do Eckmann--Hilton?

Kenny Lau (Apr 14 2020 at 10:08):

Johan Commelin said on Sep 07, 2018:

Today I thought it was a good idea to stretch the type class system a bit. In fact, I ended up not using it at all :rolling_on_the_floor_laughing:
https://gist.github.com/jcommelin/2e031b5446ca54089576ea9f66f12abf
Statement: Two unital binary operations that distribute over each other are in fact one and the same. Also, they are commutative and associative, so in fact a monoid structure.
This is used to prove that homotopy groups are abelian.

Valentin Vinoles (Apr 14 2020 at 10:10):

Not really, it was just a question because I have troubles to understand exactly how Lean links a group and its operation. I will look for your suggestion, thanks !

Kevin Buzzard (Apr 14 2020 at 10:35):

The answer is: (1) if you want to consider a group operation on a type then in Lean you should use square brackets [group E] and not name the group structure at all; (2) 99% of the time you would never consider two different group operations on the same type (which is why [group E] does not say [G : group E]; indeed Lean's type class system (the [] system) is not set up to support more than one instance of a class, and (3) if you do want to do this then you either do it with (G : group E) (H : group E) and then explicitly write stuff like G.mul rather than *, or you define a new type E' := E and you put [group E] and [group E'].

Victor Ahlquist (Apr 14 2020 at 19:27):

Is there a tactic for using lem in tactic mode to check cases?

Kevin Buzzard (Apr 14 2020 at 19:34):

by_cases h : P

Kevin Buzzard (Apr 14 2020 at 19:35):

If you go to the tactic docs and click on "filter by tag" and then select "logic" you will see the sorts of tactic that do this sort of thing.

Victor Ahlquist (Apr 14 2020 at 19:41):

Thanks

Victor Ahlquist (Apr 14 2020 at 19:46):

Is there any danger in using the following?

open_locale classical

Kenny Lau (Apr 14 2020 at 19:48):

an army of intuitionistic logicians might send you hate mails

Victor Ahlquist (Apr 14 2020 at 19:48):

Haha I can live with that. Although the command doesn't seem to work

Kevin Buzzard (Apr 14 2020 at 19:49):

Victor Ahlquist said:

Although the command doesn't seem to work

-1 for vague complaint instead of MWE

Victor Ahlquist (Apr 14 2020 at 19:51):

I think the problem is I'm not sure where to use it. At the top?

Kevin Buzzard (Apr 14 2020 at 19:51):

Well, not too near the top :-)

Kevin Buzzard (Apr 14 2020 at 19:52):

There's currently a "feature" of Lean where you can't use it directly after an import. Try using it after any imports and also after a random command like "universe u"

Victor Ahlquist (Apr 14 2020 at 19:52):

open classical
open_locale classical

Gives error invalid namespace name

Kevin Buzzard (Apr 14 2020 at 19:52):

Just delete open classical?

Victor Ahlquist (Apr 14 2020 at 19:52):

Works fine if I remove second row

Kevin Buzzard (Apr 14 2020 at 19:53):

import tactic

noncomputable theory

open_locale classical

is a very mathematician-like way to start a file

Reid Barton (Apr 14 2020 at 19:53):

You need to import something from mathlib.

Victor Ahlquist (Apr 14 2020 at 19:53):

Ah I see

Kevin Buzzard (Apr 14 2020 at 19:54):

import tactic

/-! hello mum -/

open_locale classical

is another way

Victor Ahlquist (Apr 14 2020 at 19:56):

Thanks that solves it. I didn't realize I had to import it. I have all of the exercises in a chapter from TPIL in a giant lean file so I don't mess with the top of it very much.

Nam (Apr 14 2020 at 23:03):

what is the most basic way to prove h: 1 = 0 |- false? i'm looking for something like refl, not simp / cc

Moses Schönfinkel (Apr 14 2020 at 23:06):

The most basic way in tactic mode (as you've listed tactics) is probably cases h.

Nam (Apr 14 2020 at 23:12):

what does it do? i understand that cases break down the term into its inductive constructors

Nam (Apr 14 2020 at 23:13):

how does it break down h?

Reid Barton (Apr 14 2020 at 23:14):

Fundamentally the way to prove this is to define, by recursion, a function sending 0 to true and succ _ to false, using the hypothesis 1 = 0 to conclude false = true, and using trivial : true to obtain a proof of false

Nam (Apr 14 2020 at 23:20):

there is something uneasy about that. the function is custom defined, right? it isn't one that comes from inductive nat : Type, is it?

Nam (Apr 14 2020 at 23:20):

or is it one of the axioms that we have to admit?

Reid Barton (Apr 14 2020 at 23:22):

I don't understand. You can write it using nat.rec.

Nam (Apr 14 2020 at 23:26):

if i "write it", that means the function is defined by me. and it can do anything. how then do we ensure it is logical?

Matt Earnshaw (Apr 14 2020 at 23:27):

Nam said:

how does it break down h?

there are no constructors and so the proof is complete

Reid Barton (Apr 14 2020 at 23:27):

Why do you not have the same concern about every function you write?

Reid Barton (Apr 14 2020 at 23:29):

We're just talking about a perfectly ordinary function, namely

def f :   Prop := λ n, nat.rec_on n true (λ _ _, false)

Reid Barton (Apr 14 2020 at 23:32):

nat.rec is an axiom if you like, but if you are suddenly worried about nat.rec, then you should be worried about everything

Nam (Apr 14 2020 at 23:34):

no i'm not worry about nat.rec. but i have a bad feeling about f.

Reid Barton (Apr 14 2020 at 23:36):

I don't know what to say. Forget all this 0 = 1 business. Do you agree that if someone hands you a natural number, you can inspect it and return one thing if it is 0, and another thing if it is the successor of something? And the first thing could be true, and the second thing false?

Nam (Apr 14 2020 at 23:37):

yes, i can do that. at the same time, i can also return true for the first 10 numbers, and false for the rest.

Andrew Ashworth (Apr 14 2020 at 23:38):

https://xenaproject.wordpress.com/2018/03/24/no-confusion-over-no_confusion/

Andrew Ashworth (Apr 14 2020 at 23:38):

this is the blog post you're looking for

Reid Barton (Apr 14 2020 at 23:38):

Is the question, like, why did I choose this particular f?

Nam (Apr 14 2020 at 23:55):

yes. sorry i didn't make it clear.

Nam (Apr 14 2020 at 23:56):

@Andrew Ashworth thanks for that link. i just finished reading it. there's a lot to take in for a noob like me. but it helped me understand better what @Reid Barton suggested.

Brandon B (Apr 15 2020 at 05:23):

True and false are terms of type Prop, but are themselves types? Unlike 1 which is only a term of type \nat ? I guess I don't totally understand what a Prop is in comparison to types like Nat or list

Kenny Lau (Apr 15 2020 at 05:25):

Prop is a Sort, meaning that types of Prop are also types

Kenny Lau (Apr 15 2020 at 05:26):

Type is also a Sort

Mario Carneiro (Apr 15 2020 at 05:48):

Prop and Type are universes, which means that the elements of these types are themselves types

Brandon B (Apr 15 2020 at 05:52):

oh right, Prop is the lowest universe level and Type is + 1. What's the point of this separation?

Mario Carneiro (Apr 15 2020 at 06:02):

Prop behaves differently from Type u in a number of respects

Mario Carneiro (Apr 15 2020 at 06:03):

The most important one is that elements of Prop are proof irrelevant, that is, if p : Prop and h1 h2 : p then h1 and h2 are definitionally equal

Mario Carneiro (Apr 15 2020 at 06:05):

If you want to talk about both Prop and Type, you can use Sort u, since Prop = Sort 0 and Type u = Sort (u+1)

Brandon B (Apr 15 2020 at 19:40):

How do you know if you need to use the law of the excluded middle? In TPIL there are some basic logical theorems some of which are noted to require classical reasoning, but its not obvious to me how one would figure out what kind of axioms one would need to prove it without just trial and error

Kevin Buzzard (Apr 15 2020 at 19:51):

This sort of nonsense might be undecidable or something. But I'm only guessing

Robin Carlier (Apr 15 2020 at 19:55):

Hey,
So here is a quite noob question: in the category_theory package, all the definitions have those . obviously, as in here:

extends category_struct.{v} obj : Type (max u (v+1)) :=
(id_comp' :  {X Y : obj} (f : hom X Y), 𝟙 X  f = f . obviously)
(comp_id' :  {X Y : obj} (f : hom X Y), f  𝟙 Y = f . obviously)
(assoc'   :  {W X Y Z : obj} (f : hom W X) (g : hom X Y) (h : hom Y Z),
  (f  g)  h = f  (g  h) . obviously)

I just want to understand clearly what's going on with the syntax here: am I right to understand that this mean that, whenever I instanciate a category, it runs an "obviously" on the arguments I give, which I assume tries to kill off the goal if it's "clear" that the argument I'm giving is an identity/associativity/whatever, and leave it as a goal if there are more things to do?

Kenny Lau (Apr 15 2020 at 20:03):

intuitionistic 0-th order logic is decidable but not 1-st order logic

Kenny Lau (Apr 15 2020 at 20:03):

@Robin Carlier yes

Robin Carlier (Apr 15 2020 at 20:05):

Okay, thank you, and so there the obviously tactic is defined to be simply the tidy tactic, the doc is a bit elusive about what it does exactly, is it like a simp but with less algebraic manipulations and more "rewriting/unfolding definitions" kind of moves?

Kenny Lau (Apr 15 2020 at 20:07):

/-
The propositional fields of `category` are annotated with the auto_param `obviously`,
which is defined here as a
[`replacer` tactic](https://leanprover-community.github.io/mathlib_docs/commands.html#def_replacer).
We then immediately set up `obviously` to call `tidy`. Later, this can be replaced with more
powerful tactics.
-/
def_replacer obviously
@[obviously] meta def obviously' := tactic.tidy

Kenny Lau (Apr 15 2020 at 20:08):

whenever you declare an instance of category, it will try to use by obviously to prove id_comp', and then change the name to id_comp

Kevin Buzzard (Apr 15 2020 at 20:11):

The dot here means "if the user supplies the proof then great, but if they don't then try running the obviously tactic

Robin Carlier (Apr 15 2020 at 20:12):

Thank you for your explanation!

Kevin Buzzard (Apr 15 2020 at 20:12):

[the dot notation in the reference manual] (https://leanprover.github.io/reference/expressions.html#implicit-arguments)

Robin Carlier (Apr 15 2020 at 20:14):

Damn I didn't know there was a reference manual! I thought there was only Theorem proving in lean, well thank you for the link.

Kevin Buzzard (Apr 15 2020 at 20:14):

These things are quite well hidden!

orlando (Apr 15 2020 at 20:16):

Do we have an instance of a category where obviously fail ?

Kevin Buzzard (Apr 15 2020 at 20:32):

Obviously often fails -- it just tries a bunch of obvious things. It will fail if you give it a proof which needs an idea, for example an existence proof

Jason KY. (Apr 15 2020 at 21:24):

Hi!
I would like to define a function that maps even and odd numbers differently. I thought to use nat.mod_two_eq_zero_or_one n but that doesn't seem to work. How should I go about defining this function?

def nat_func :   
| 0 := 0
| 1 := 1
| (n + 2) :=
begin
  -- I would like to map even n to n and odd n to 3 * n
  cases nat.mod_two_eq_zero_or_one n, --induction tactic failed, recursor 'or.dcases_on' can only eliminate into Prop
  sorry
end

Bryan Gin-ge Chen (Apr 15 2020 at 21:26):

I would just use if ... then ... else ..., e.g.:

def collatz_fn (n : ) :  := if (2  n) then n / 2 else 3 * n + 1

Jason KY. (Apr 15 2020 at 21:28):

Right, that seems like a reasonable way of doing it!

Brandon B (Apr 16 2020 at 10:43):

I’m struggling to prove “not(p iff not(p))” one of the exercises in TPIL. So far I first assumed “p iff not(p)” with the goal to derive false. I tried using “iff.elim_left” on that assumption but everything I try is giving me an error. Any hints?

Kevin Buzzard (Apr 16 2020 at 10:45):

When I was a young man, I used to follow a USENET newsgroup called rec.puzzles

Kevin Buzzard (Apr 16 2020 at 10:46):

And once a month or so, someone would show up and tell us that there were three words in the English language that ended in GRY.

Kevin Buzzard (Apr 16 2020 at 10:46):

Two of them were hungry and angry, and the question was: what was the third one.

Kevin Buzzard (Apr 16 2020 at 10:47):

And for some reason this question would just repeatedly show up, again and again, despite there being extensive discussions about the question (and also about why it kept coming up!)

Johan Commelin (Apr 16 2020 at 10:48):

cat /usr/share/dict/words | grep gry
angry
hungry

Kevin Buzzard (Apr 16 2020 at 10:48):

However one key difference here is that there is no other word in the English language that ends in GRY (or at least no common one)

Kevin Buzzard (Apr 16 2020 at 10:48):

whereas this question is just a darn good question :-)

Kevin Buzzard (Apr 16 2020 at 10:49):

but it has been answered many times before in #new members. However searching for it will just give you spoilers :-(

Kevin Buzzard (Apr 16 2020 at 10:49):

The hint is : prove "not p".

Kevin Buzzard (Apr 16 2020 at 10:49):

Either that or get with the program and use classical logic :P

Rob Lewis (Apr 16 2020 at 10:50):

I feel like we should give this question a name and refer to it by that name in TPIL and L&P and wherever else it appears.

Rob Lewis (Apr 16 2020 at 10:50):

And in an answer here.

Rob Lewis (Apr 16 2020 at 10:50):

So it's easily found with a search.

Kevin Buzzard (Apr 16 2020 at 10:50):

The epilogue to the USENET story is that newcomers to rec.puzzles became known as nugrys.

Kevin Buzzard (Apr 16 2020 at 11:11):

https://tinyurl.com/not-piffnotp

Kevin Buzzard (Apr 16 2020 at 11:16):

Also, https://www.definitions.net/definition/nugry

Robin Carlier (Apr 16 2020 at 13:19):

Is there a way to fix the value of a variable within the same namespace? In text limits.lean there's this chunk of code :

section colim_functor

variables [has_colimits_of_shape J C]

def colim : (J  C)  C :=

If I reopen this namespace and go #check colim it gives me (?M_1 ⥤ ?M_3) ⥤ ?M_3,
I added an instance [has_colimits_of_shape I C] for some I, but I can't manage to get colim I : (I \functor C) \functor C and colim J: (J \functor C) \functor C to work, any idea?

Reid Barton (Apr 16 2020 at 13:20):

You need newlines for multiline blocks, and single backticks for inline code.

Robin Carlier (Apr 16 2020 at 13:20):

Thank you

Reid Barton (Apr 16 2020 at 13:22):

To answer your actual question, if you want the colimit functor for a fixed index category I, the easiest way is to write colim : (I ⥤ C) ⥤ C. The variable I is implicit (since normally you would want to infer it from the functor you're taking the colimit of).

Robin Carlier (Apr 16 2020 at 13:24):

Yep, works like a charm, thank you!

Reid Barton (Apr 16 2020 at 13:24):

To answer your original question: Fixing the value of a variable within a namespace doesn't make much sense; namespaces are just a way to organize names. Fixing the value of a variable in a section does make some sense, and that's what parameters does. However, I don't think you want to use it here.

Robin Carlier (Apr 16 2020 at 13:31):

My original question was ill-formulated yeah, All I wanted to do is get that colim to have the right type, and I think I get the idea: if I want something to have "X type", just tell that to lean with the :and let it infer the rest :D

Reid Barton (Apr 16 2020 at 13:32):

Another option is to write @colim which makes all the implicit arguments into explicit ones, but in this case there are a lot of them.

Robin Carlier (Apr 16 2020 at 13:34):

Yeah I tried that initially but got lost in the number of arguments

Robin Carlier (Apr 16 2020 at 14:38):

Okay one more question: I'm looking around in the category_theory package, and I see the category Cat of all categories has been formalized, but I don't quite get the code nor how to use it. How should I do, for instance, to introduce the category of all u-Small categories for a fixed universe u? Just Cat.{u u}?

Robin Carlier (Apr 16 2020 at 14:41):

Cat.{u u} type checks as Type u+1 which is what I want I guess, but will lean recognize its objects as instances of small-category in the right context?

Robin Carlier (Apr 16 2020 at 17:22):

Also, I'm completely stuck on the following, which is quite related to my last question: I have a some category C and D, defined as variables {C : Type u} [𝒞 : category.{u} C], idem with D, and I want to define some functor (over C) ⥤ D in wich I need to make use of the fact that the objects of over Cshould be functors A ⥤ C for some category A. Yet when I try to code that, I can get some morphism A ⟶ C and get Aas well, but I can't get lean to be ok with the fact that A should be a category and that the morphism A ⟶ C should be a functor. I tried various stuff like uliftor apply_instance but nothing work. I guess that's because I'm being vague with the exact typing /instance of over C, and I should be able to tell lean that this over category should be taken in Cat, but despite sniffing around in the code I have no idea about how to do this kind of stuff. Any idea?

Reid Barton (Apr 16 2020 at 17:22):

As far as I know, no one has ever used Cat for anything.

Reid Barton (Apr 16 2020 at 17:24):

I even defined it in a separate project (before it was in mathlib) and then proceeded to not use that for anything. :upside_down:

Robin Carlier (Apr 16 2020 at 17:27):

Ok so it's not possible in a "neat" way to do what I want using Cat then? If not, what could be possible options?

Reid Barton (Apr 16 2020 at 17:30):

You want to use Cat indeed, but fix it to make the things you want to do neat.

Robin Carlier (Apr 16 2020 at 17:34):

So I guess I'll ding into Cat and see what I can do with that then, thank you!

Kevin Buzzard (Apr 16 2020 at 17:35):

disclaimer: I am not a category theorist. I would imagine that Cat is not a category but rather some kind of 2-category. Presumably in Lean one would stick to the category of categories whose objects were in Type u and whose morphism types were all in Type v for some fixed u and v? As for what you're stuck on, I would really encourage you to post some fully working code, because then people like me will just tinker a bit and try and be helpful. [in particular I don't know what over C is but if you give me a file with the right imports at the top I will just be able to look; whilst I am not a category theorist I do know what apply_instance does]

Reid Barton (Apr 16 2020 at 17:38):

This will mostly involve things like trying to define coercions, then getting frustrated when they don't work properly.

Reid Barton (Apr 16 2020 at 17:39):

But, for sure, in your over example, you would want to start with C : Cat.{u u} and not the unbundled form.

Robin Carlier (Apr 16 2020 at 18:08):

@Kevin Buzzard Yeah ideally Cat should be a 2-category, but tbh I don't think I'm up to implementing 2-categories and 2-functoriality and so on yet :D
(Also I'm not a category theorist as well)
I'll look around for defining coercions with cat.
As for some code example, this looks like this so far, feel free to tinker:

import category_theory.limits.shapes
import category_theory.category.Cat
import category_theory.comma
import category_theory.concrete_category.bundled
open category_theory category_theory.category category_theory.limits

universes v u
variables {C : Type u} [𝒞 : category.{u} C]

include 𝒞

namespace colim_functor
  variables [has_colimits.{u} C]
  #check (bundled.of C : Cat.{u u})
  #check over.{u} C
  /- #check over (C : Cat.{u u}) : fails-/
  def colimit_functor : (over C)  C :=
    { obj :=
      begin
        intro a,
        have morph := a.hom,
        tidy,
        have test := (over.forget).obj morph,
        have test2 := test  C, apply_instance,  /- Error here, can't get test to be a category -/

      end,
      map := sorry,
    }

end colim_functor

Anyway thanks @Reid Barton for the advice

Kevin Buzzard (Apr 16 2020 at 18:28):

I don't understand the maths at all, but test isn't typechecking. because (over.forget).obj is a function over ? -> ? and you're giving it morph which has type a.left -> C. You could give it a instead, which would make test a term of type Type u. What do you expect the type of test to be? At the moment it's not even defined.

Robin Carlier (Apr 16 2020 at 18:34):

Yeah right that's bogus, I don't know why I changed with morph, but I had initially tried with a.
So yeah with ait gives Type u, that much is expected, but I wanted lean to get that this Type u should actually be a Type u with a category instance. So that the functor notation on the line after work
Also there it is test but really this could have been a.left (they're supposed to be the same), but the problem remains if I change test with a.left

Kevin Buzzard (Apr 16 2020 at 18:35):

If you want Type u to be a category I think you will need an import

Jeremy Avigad (Apr 16 2020 at 18:36):

"hangry": https://www.foxnews.com/food-drink/hangry-is-officially-a-word-in-the-oxford-english-dictionary

Kevin Buzzard (Apr 16 2020 at 18:36):

rofl

Kevin Buzzard (Apr 16 2020 at 18:38):

The import category_theory.types makes Type u into a category -- but it's a large category.

Jeremy Avigad (Apr 16 2020 at 18:40):

Harry Potter fans will have no trouble coming up with a word that starts with GRY.

Robin Carlier (Apr 16 2020 at 18:40):

My main issue there is that morph has type a.left \hom C (and not just \l), and that C is a category, so that the information that a.left is a category ought to be already there, otherwise that \hom in the description of a would not type check, see what I mean?
Thanks for the import suggestion tho, I'll try to see if I can get something within types.

Kevin Buzzard (Apr 16 2020 at 18:48):

morph has type a.left \hom C so a.left must be an object of some category. Looking at the type of morph with set_option pp.all true I can see category_theory.types so you must be importing it somewhere already.

Kevin Buzzard (Apr 16 2020 at 18:49):

This morphism morph looks to me like it is a morphism in the category Type u.

Kevin Buzzard (Apr 16 2020 at 18:50):

This makes sense -- C : Type u and Type u has a category structure, so C is an object of the category Type u.

Robin Carlier (Apr 16 2020 at 18:52):

I see it yeah, this set_option pp.all true is quite handy, didn't know about it :P.

Well, then it just confirms what Reid said: I definitely want to tinker with Cat since C should be an object of the category Cat.{u u}, rather that Type uthank you a lot for your help and your time!

Kevin Buzzard (Apr 16 2020 at 19:09):

You defined {C : Type u} so C is a term of type Type u. I have no idea how to work with Cat I'm afraid.

Robin Carlier (Apr 16 2020 at 19:14):

Yeah I get that I should change C to something else to get that to work, or at least coerce it into something else. I'll play around with Cat . I think the object I want to work with is ((bundled.of C) : Cat.{u u}) but unfortunately over doesn't seem to like it

Robin Allison (Apr 16 2020 at 22:01):

Is this an appropriate forum for asking about proof assistants in extremely broad terms? Like, I'm starting from the idea of "technology to assist with proofs" (mostly informal) and proof assistants just don't even come close to exhausting that category. Nevertheless I imagine proof assistants form an important subset of that technology and in particular address the technical issues you would encounter when asking the broader question.

Kevin Buzzard (Apr 16 2020 at 22:09):

People mostly stick to Lean-related stuff here, it is a very focussed chat in fact. But asking something in #general about general proof assistants isn't going to upset anyone I shouldn't think

Robin Allison (Apr 16 2020 at 23:04):

Gotcha. I'm probably want to go beyond the scope of just proof assistants, but I might as well test the water there. Thanks.

Mario Carneiro (Apr 17 2020 at 00:40):

I always like to read about more general topics in formal methods, and the people subscribed to this chat are not only lean people but also involved in other areas so it's not a bad place to ask these questions.

ROCKY KAMEN-RUBIO (Apr 17 2020 at 03:36):

I'm getting a rewrite tactic failed, did not find instance of the pattern in the target expression error on this code again. Not sure if I'm doing something wrong or if my compiler is just getting confused (this has happened in the past in large files with lots of errors, but that is not the case here).

import init.data.nat.basic
open nat

theorem rearrange (a b c : ) : a + (b - c) = b + (a - c) :=
begin
rw  add_sub_assoc,
end

Shing Tak Lam (Apr 17 2020 at 03:39):

What you're proving isn't necessarily true.

-- a = 0, b = 2, c = 1
#eval 0 + (2 - 1) -- 1
#eval 2 + (0 - 1) -- 2

Shing Tak Lam (Apr 17 2020 at 03:41):

If you give the arguments explicitly to add_sub_assoc, so like this,

rw add_sub_assoc a b c,

You get this error

failed to synthesize type class instance for
a b c : ℕ
⊢ add_group ℕ

Which is what I was referring to

Shing Tak Lam (Apr 17 2020 at 03:43):

Essentially what this boils down to is that in lean with the nats, 0 - x = 0.

Shing Tak Lam (Apr 17 2020 at 03:44):

So you need either that b ≥ c ∧ a ≥ c as a hypothesis (but this rw still won't work, you could probably solve it with omega), or switch to ints.

Shing Tak Lam (Apr 17 2020 at 03:46):

So either

import data.nat.basic
       tactic
open nat

theorem rearrange (a b c : )
  (h1 : b  c)
  (h2 : a  c)
: a + (b - c) = b + (a - c) :=
begin
omega,
end

or

import data.int.basic
open int

theorem rearrange (a b c : ) : a + (b - c) = b + (a - c) :=
begin
rw add_sub_assoc,
-- etc...
end

ROCKY KAMEN-RUBIO (Apr 17 2020 at 03:57):

I see, that makes sense. I guess I assumed that add_sub_assoc somehow checked that the values made sense on its own but in retrospect that seems unrealistic. I'll read more about how omega works. Is there a way to prove this by using the assumption b ≥ c ∧ a ≥ c directly without switching to ints?

Shing Tak Lam (Apr 17 2020 at 04:00):

Yes.

import data.nat.basic
       tactic
open nat

theorem rearrange (a b c : )
  (h1 : b  c)
  (h2 : a  c)
: a + (b - c) = b + (a - c) :=
begin
  rw nat.add_sub_assoc h1,
  rw nat.add_sub_assoc h2,
  rw add_comm,
end

Shing Tak Lam (Apr 17 2020 at 04:01):

There are two different add_sub_assocs, the one that you want is nat.add_sub_assoc, not the one from algebra.group

Shing Tak Lam (Apr 17 2020 at 04:03):

When you use rw ←add_sub_assoc Lean uses the one from algebra.group and not the one from nat. So in this case you have to tell Lean that you want the one from nat.

Shing Tak Lam (Apr 17 2020 at 04:04):

The one from nat has a different type signature, and that is where you provide the fact that b ≥ c.

Shing Tak Lam (Apr 17 2020 at 04:06):

import data.nat.basic
       tactic
open nat

#check add_sub_assoc
-- add_sub_assoc : ∀ (a b c : ?M_1), a + b - c = a + (b - c)
#check nat.add_sub_assoc
-- nat.add_sub_assoc : ?M_2 ≤ ?M_1 → ∀ (n : ℕ), n + ?M_1 - ?M_2 = n + (?M_1 - ?M_2)

ROCKY KAMEN-RUBIO (Apr 17 2020 at 04:08):

Interesting, I would have expected doing open nat would default to the nat version, but I guess the different type signature means it never actually looks for the one in nat?

Mario Carneiro (Apr 17 2020 at 04:09):

open nat does nothing for type inference

Mario Carneiro (Apr 17 2020 at 04:10):

actually in this case the issue is that nat.add_sub_assoc is protected

Mario Carneiro (Apr 17 2020 at 04:10):

which means that the name has to be qualified even if you open the namespace

ROCKY KAMEN-RUBIO (Apr 17 2020 at 04:57):

Gotcha. This helps a lot. Thank you!

ROCKY KAMEN-RUBIO (Apr 17 2020 at 04:59):

Unrelated question: I'm getting errors when I try to access certain namespaces even though I think I've imported them correctly. For example

import init.data.nat
import init.data.list.basic
--https://observablehq.com/@bryangingechen/fibonacci-formalized-1-some-sums?collection=@bryangingechen/lean&fbclid=IwAR1UQq-DS6CG403IjSkBQS5n_evm9soXWgq-NKDyuyxB1Myc0J4tXd8xHhc

open nat list
#eval [1,2,3].sum

This gives me an error on the .sum. Similar thing is happening on le_iff_exists_add and when I try to use a use 0, tactic to kill an inequality. Screen-Shot-2020-04-17-at-12.58.33-AM.png

Shing Tak Lam (Apr 17 2020 at 05:00):

Delete the inits.

So

import data.nat.basic
import data.list.basic
--https://observablehq.com/@bryangingechen/fibonacci-formalized-1-some-sums?collection=@bryangingechen/lean&fbclid=IwAR1UQq-DS6CG403IjSkBQS5n_evm9soXWgq-NKDyuyxB1Myc0J4tXd8xHhc

open nat list
#eval [1,2,3].sum

Nam (Apr 17 2020 at 15:09):

is there a series of proofs that i should start from in mathlib, the very foundational ones?

Kevin Buzzard (Apr 17 2020 at 15:13):

What do you mean?

Bryan Gin-ge Chen (Apr 17 2020 at 15:17):

You might want to take a look at the core Lean library, which contains all the code which is imported into every Lean file by default. Start with the imports of init.default in order, e.g. init.core, init.logic, until you get bored...

Bryan Gin-ge Chen (Apr 17 2020 at 15:21):

I've personally had more fun picking files or even specific defs / theorems that contain math that I'm familiar with and then working backwards, e.g. looking up the definitions / proofs of unfamiliar declarations.

Reid Barton (Apr 17 2020 at 15:23):

I suppose it could be worthwhile though to look at the definitions and basic facts about and, or, Exists, sigma, etc. though I don't know whether they're all in one place

Orlando Marigliano (Apr 17 2020 at 15:29):

Hi, I just installed Lean on Ubuntu following "the fast way" in https://github.com/leanprover-community/mathlib/blob/master/docs/install/debian.md.
My problem is: import real.data.basics gives me file 'data/real/basics' not found in the LEAN_PATH. How can I fix this?

Patrick Massot (Apr 17 2020 at 15:29):

Remove the extra s at the end

Patrick Massot (Apr 17 2020 at 15:30):

And use correct order of words

Patrick Massot (Apr 17 2020 at 15:30):

import data.real.basic

Orlando Marigliano (Apr 17 2020 at 15:30):

import data.real.basic gives me the same error

Patrick Massot (Apr 17 2020 at 15:31):

Did you follow instructions all the way to the crucial last line?

Patrick Massot (Apr 17 2020 at 15:31):

Which is a link to https://github.com/leanprover-community/mathlib/blob/master/docs/install/project.md

Orlando Marigliano (Apr 17 2020 at 15:32):

I did not, going through that page now

Patrick Massot (Apr 17 2020 at 15:34):

I think we really need to change all those installation help pages, inserting between every single line: "At the end of this file, you'll need to follow the link".

Patrick Massot (Apr 17 2020 at 15:35):

It seems nobody is capable of clicking that link otherwise.

Orlando Marigliano (Apr 17 2020 at 15:37):

Following that link indeed solved my problem. Thanks!

orlando (Apr 17 2020 at 15:55):

Hey Orlando :grinning_face_with_smiling_eyes:

Orlando Marigliano (Apr 17 2020 at 15:58):

Hey other Orlando :))

Robin Carlier (Apr 17 2020 at 17:17):

Does the injection tactic and its derivative automatically recognize a function is injective once it has been proved with function.injective?
In category_theory/opposites.lean there's stuff like

lemma has_hom.hom.op_inj {X Y : C} :
  function.injective (has_hom.hom.op : (X  Y)  (op Y  op X)) :=
λ _ _ H, congr_arg has_hom.hom.unop H

and similar lemmas have no @[simp] attribute, will this kind of lemma be detected by injection?

Patrick Massot (Apr 17 2020 at 17:18):

No, this has nothing to do with injection.

Patrick Massot (Apr 17 2020 at 17:18):

This tactic has no real world analogue, it's very tightly coupled to the CIC foundations

Patrick Massot (Apr 17 2020 at 17:20):

I don't think I ever used that injection tactic

Robin Carlier (Apr 17 2020 at 17:21):

Me neither, that's why I'm trying to figure out what it does :sweat_smile:

Kevin Buzzard (Apr 17 2020 at 17:29):

injection docs. It proves things like nat.zero ≠ nat.succ n because zero and succ are distinct constructors for nat.

Patrick Stevens (Apr 17 2020 at 18:49):

I'm scared to see things like "constructors of inductive data types are injections", given that it's inconsistent with excluded middle in Agda (https://coq-club.inria.narkive.com/iDuSeltD/agda-with-the-excluded-middle-is-inconsistent) - but my Lean is not good enough to attempt to show that that construction of False fails in Lean

Kevin Buzzard (Apr 17 2020 at 18:52):

injection proves that the constructors are injective. There are no constructors in the definition of I in that link, so the tactic says nothing in this case.

Robin Carlier (Apr 17 2020 at 20:11):

I have some quite noob coercion question: I have the following code, which doesn't work :

import category_theory.category.Cat

universes v u
open category_theory

namespace Cat
    instance cat_to_elem_of_Cat {C: Type u} : has_coe (category.{v} C) (Cat.{v u}) := ⟨λ 𝒞, @Cat.of C 𝒞
    variables {X : Type u} [𝒳 : category.{u} X]
    /- #check (X : Cat)
    #check (𝒳 : Cat)
    Both fail-/
end Cat

I think that's because my instance of has_coe have C as an implicit argument, but I can't see how I can remove it? Basically I want to tell lean that whenever there's a type C with a category instance that is typed as `Cat, then it should coerce it with Cat.of, how should I do that? My problem is mainly that there's both the parameter of the type and the instance of category on that type, rather than just the type.

Kevin Buzzard (Apr 17 2020 at 20:43):

What happens if you change the v to a u? Lean 3 struggles with universe unification in situations like this

Robin Carlier (Apr 17 2020 at 20:47):

If I change the v to a u in (category.{v} C), then it doesn't type check. If I change it also in Cat.{v u}, then it type checks but the problem remains and it won't coerce X

Robin Carlier (Apr 17 2020 at 20:53):

Also same if I try to use only one universe uand coerce with (X : Cat.{u u})

Robin Carlier (Apr 17 2020 at 21:02):

I think that's because the type from which I want to coerce from is not the right one, what I really want would be the type of "types equippend with an instance of category on the first type" but I don't know how to write it so that it will accept a coercion from X or 𝒳. The type of "types + a category on said type" is basically already what the Cat type is so it's going in circles.

Reid was right when he was talking about "Trying to define coercions and then getting frustrated when they don't work" :sweat_smile:

Reid Barton (Apr 17 2020 at 21:25):

I don't really understand what you wrote but I would go the other way. Write X : Cat, then add has_coe_to_sort so that X can also be treated as a type and then add a category instance for this type.

Robin Carlier (Apr 17 2020 at 21:26):

Yeah that much I already did. But I wanted to go able to go the other way, so that there wouldn't be Cat.of ... everywhere

Reid Barton (Apr 17 2020 at 21:27):

I don't recommend that

Reid Barton (Apr 17 2020 at 21:27):

It sounds like you would have a loop

Robin Carlier (Apr 17 2020 at 21:28):

I see, then I'll just keep the coe to sort for X : Catand keep writing Cat.ofwhenever needed

Reid Barton (Apr 17 2020 at 21:30):

You shouldn't have to write it very often once you get going, since probably there are not that many different ways you produce a category.

Reid Barton (Apr 17 2020 at 21:31):

I still wonder whether it would be better if the whole category theory library was written in bundled style (though, ironically, I'm not sure what happens with Cat itself then)

Robin Carlier (Apr 17 2020 at 21:37):

:shrug: I'll just get used to however it is

Scott Morrison (Apr 18 2020 at 00:07):

It did start off bundled, and I was talked out of it. :-) People like their typeclasses.

Scott Morrison (Apr 18 2020 at 00:09):

I think writing C.hom X Y would have been fine instead of X ⟶ Y. (Another notation that would work with fully bundled categories is X ⟶[C] Y.)

Reid Barton (Apr 18 2020 at 00:40):

You should be able to keep the existing notation, I think

Reid Barton (Apr 18 2020 at 00:41):

With X Y : ↥C

Reid Barton (Apr 18 2020 at 00:51):

I didn't realize it was ever bundled (I assume you mean in your pre-mathlib library)

Kevin Buzzard (Apr 18 2020 at 00:58):

People liked their is_group_hom typeclasses but ultimately changed their minds.

Scott Morrison (Apr 18 2020 at 01:11):

Hmm... maybe it is still doable.

Reid Barton (Apr 18 2020 at 01:12):

You should be able to use the same arrow for functors, too

Reid Barton (Apr 18 2020 at 01:14):

There might be the usual problems with coercions though (do those happen for has_coe_to_sort also? I mainly encounter them with functions)

Reid Barton (Apr 18 2020 at 01:15):

Like X : Over Z might not work because C needs to be inferred (maybe not the greatest example as math writes C in the notation anyways)

Jalex Stark (Apr 18 2020 at 03:43):

where is calc mode documented? (or maybe I just need to look at an example?)

Shing Tak Lam (Apr 18 2020 at 03:50):

https://leanprover.github.io/theorem_proving_in_lean/quantifiers_and_equality.html#calculational-proofs

Jalex Stark (Apr 18 2020 at 04:28):

advice on how to close
lemma case_bash (a : ℕ) (ha : a ≤ 4) : a ∈ [0,1,2,3,4] := sorry
?

Oh, it looks like the interval_cases is supposed to do this. I should remind myself how to update mathlib.

Johan Commelin (Apr 18 2020 at 05:06):

Jalex Stark said:

where is calc mode documented? (or maybe I just need to look at an example?)

There is also https://github.com/leanprover-community/mathlib/blob/master/docs/extras/calc.md

Nam (Apr 18 2020 at 05:17):

I'm trying to reforming the r* relation in Hitchhiker guide.

inductive star {α : Type} : (α -> α -> Prop) -> α -> α -> Prop
| base (r : α -> α -> Prop) (a b : α) : star r a b
| refl (r : α -> α -> Prop) (a : α) : star r a a
| trans {a b c : α} {r : α -> α -> Prop} (star r a b) (star r b c) : star r a c

but it produces error "don't know how to synthesize placeholder".what did i not get right?

Nam (Apr 18 2020 at 05:45):

ahh, i think the left side before : must not have the inductive prop that is being defined.

Shing Tak Lam (Apr 18 2020 at 06:13):

Johan Commelin said:

Jalex Stark said:

where is calc mode documented? (or maybe I just need to look at an example?)

There is also https://github.com/leanprover-community/mathlib/blob/master/docs/extras/calc.md

Seems like everything else in that folder is in the mathlib docs except the document about calc.

Johan Commelin (Apr 18 2020 at 07:10):

@Shing Tak Lam I guess that folder could use some love now that we have the new docs system. (One for of love could be retirement :wink:)

Bryan Gin-ge Chen (Apr 18 2020 at 07:31):

https://github.com/leanprover-community/doc-gen/pull/18

Johan Commelin (Apr 18 2020 at 07:36):

Great! It's on the queue

Bryan Gin-ge Chen (Apr 18 2020 at 07:36):

haha, bors doesn't run the doc-gen repo (yet?)

Gabriel Ebner (Apr 18 2020 at 07:55):

The doc-gen repo runs Gabriel, which is as almost as fast.

Robin Carlier (Apr 18 2020 at 07:58):

Is there a way to "rewrite along an equality" purely in term mode? for instance in the following :

import category_theory.isomorphism

universes v u
open category_theory

section
    variables {C : Type u} [𝒞 : category.{v} C]
    variables (a b: C)
    variable P1 : (a = b)
    include 𝒞 P1
    def canonical : (a  b) := by {have i := 𝟙 a, rw  P1, exact i}
end

I use by and tactic mode to get my (a ⟶ b), is there a term mode way to tell lean "take identity and a and replace one of the a by b since they're equal"?

Bryan Gin-ge Chen (Apr 18 2020 at 08:02):

You can try to use eq.subst (infix notation ), but it's often much more finicky than using rw

Robin Carlier (Apr 18 2020 at 08:09):

Yeah I tried to mess around with it but it "failed to compute the motive"
The question was because I have read that it's better to use term mode to define stuff and leave tactic mode for proving stuff, but now I see that for this particular example it's in tactic mode even in mathlib

Bryan Gin-ge Chen (Apr 18 2020 at 08:31):

You can always reverse-engineer a term mode proof if you really want:

import category_theory.isomorphism

universes v u
open category_theory

section
    variables {C : Type u} [𝒞 : category.{v} C]
    variables (a b: C)
    variable P1 : (a = b)
    include 𝒞 P1
    def canonical : (a  b) := by {have i := 𝟙 a, rw  P1, exact i}

    set_option pp.proofs true
    #print canonical
    #print canonical._proof_1

    def canonical' : (a  b) :=
    (P1  eq.refl (a  a) : (a  a) = (a  b)).mp (𝟙 a)
end

I guess eq.subst doesn't work directly here because a ⟶ b is not a Prop.

edit: golfed a little more

Reid Barton (Apr 18 2020 at 12:00):

Two somewhat contradictory pieces of advice:

  1. You shouldn't use rw to construct data like this; it will cause a lot of headaches later.
  2. This canonical already exists in category_theory.eq_to_hom.

Kevin Buzzard (Apr 18 2020 at 12:11):

Jalex Stark said:

advice on how to close
lemma case_bash (a : ℕ) (ha : a ≤ 4) : a ∈ [0,1,2,3,4] := sorry
?

Oh, it looks like the interval_cases is supposed to do this. I should remind myself how to update mathlib.

Indeed:

lemma case_bash (a : ) (ha : a  4) : a  [0,1,2,3,4] := by interval_cases a; simp

"Case bashing" is a tactic tag at the docs; this sort of thing comes up a lot and it is only relatively recently that Scott sorted it out. Thanks Scott :heart:

Finally, if you have leanproject installed (the documentation covers it; installing might be as simple as something like sudo pip3 install mathlibtools if you already have elan) then leanproject up will update your project if it is either (a) mathlib or (b) contains mathlib as a dependency. Nowadays it is as simple as that, thanks to the amazing new tooling.

Robin Carlier (Apr 18 2020 at 12:17):

Yeah @Reid Barton I saw that that canonical was in eq_hom... And is defined with a rw :grinning:
So I ended up using the one in eq_hom anyway, and it beign constructed with rwdid cause a lot of headeaches

Paul van Wamelen (Apr 18 2020 at 15:26):

I have the following start to a file:

import ring_theory.unique_factorization_domain

variables {α : Type*}

namespace associates
open unique_factorization_domain associated lattice
variables [integral_domain α] [normalization_domain α] [unique_factorization_domain α] [decidable_eq (associates α)]

local attribute [instance] associated.setoid
-- local attribute [instance] to_gcd_domain

variable p : { a : associates α // irreducible a }

end associates

But I want to use the fact that a unique factorization domain is a gcd domain, so I'd like to add the commented local attribute [instance] to_gcd_domain. But when I do that I get the maximum class-instance resolution depth has been reached error at the variable p line. Am I doing something wrong? Can I fix this somehow?

Kevin Buzzard (Apr 18 2020 at 15:30):

[class_instances] (596) ?x_1789 : normalization_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := @gcd_domain.to_normalization_domain ?x_1792 ?x_1793
[class_instances] (597) ?x_1793 : gcd_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := @to_gcd_domain ?x_1794 ?x_1795 ?x_1796 ?x_1797
[class_instances] (598) ?x_1795 : normalization_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := _inst_2
failed is_def_eq
[class_instances] (598) ?x_1795 : normalization_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := int.normalization_domain
failed is_def_eq
[class_instances] (598) ?x_1795 : normalization_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := @gcd_domain.to_normalization_domain ?x_1798 ?x_1799
[class_instances] (599) ?x_1799 : gcd_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := @to_gcd_domain ?x_1800 ?x_1801 ?x_1802 ?x_1803
[class_instances] (600) ?x_1801 : normalization_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := _inst_2
failed is_def_eq
[class_instances] (600) ?x_1801 : normalization_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := int.normalization_domain
failed is_def_eq
[class_instances] (600) ?x_1801 : normalization_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := @gcd_domain.to_normalization_domain ?x_1804 ?x_1805
[class_instances] (601) ?x_1805 : gcd_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := @to_gcd_domain ?x_1806 ?x_1807 ?x_1808 ?x_1809
[class_instances] (602) ?x_1807 : normalization_domain (@associates α (@ring.to_monoid α (@domain.to_ring α (@integral_domain.to_domain α _inst_1)))) := _inst_2
failed is_def_eq
...

Kevin Buzzard (Apr 18 2020 at 15:32):

gcd_domain extends normalization_domain and gcd_domain.to_normalization_domain is an instance, so when you add your instance you make a loop. This is presumably why

def unique_factorization_domain.to_gcd_domain
  (α : Type*) [normalization_domain α] [unique_factorization_domain α] [decidable_eq (associates α)] :
  gcd_domain α := ...

is a def but not an instance in mathlib.

Reid Barton (Apr 18 2020 at 15:35):

Paul van Wamelen said:

variables [integral_domain α] [normalization_domain α] [unique_factorization_domain α] [decidable_eq (associates α)]

normalization_domain extends integral_domain, so you don't want both. That would give you two unrelated ring structures on the type.

Kevin Buzzard (Apr 18 2020 at 15:37):

Now the loop is

[class_instances] (106) ?x_319 : normalization_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := _inst_1
failed is_def_eq
[class_instances] (106) ?x_319 : normalization_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := int.normalization_domain
failed is_def_eq
[class_instances] (106) ?x_319 : normalization_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := @gcd_domain.to_normalization_domain ?x_322 ?x_323
[class_instances] (107) ?x_323 : gcd_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := @to_gcd_domain ?x_324 ?x_325 ?x_326 ?x_327
[class_instances] (108) ?x_325 : normalization_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := _inst_1
failed is_def_eq
[class_instances] (108) ?x_325 : normalization_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := int.normalization_domain
failed is_def_eq
[class_instances] (108) ?x_325 : normalization_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := @gcd_domain.to_normalization_domain ?x_328 ?x_329
[class_instances] (109) ?x_329 : gcd_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := @to_gcd_domain ?x_330 ?x_331 ?x_332 ?x_333
[class_instances] (110) ?x_331 : normalization_domain
  (@associates α
     (@ring.to_monoid α
        (@domain.to_ring α (@integral_domain.to_domain α (@normalization_domain.to_integral_domain α _inst_1))))) := _inst_1
failed is_def_eq
...

Reid Barton (Apr 18 2020 at 15:38):

It seems like you might as well just ask for a gcd_domain

Kevin Buzzard (Apr 18 2020 at 15:40):

I always wondered what they were for, maybe Paul is about to tell us :-)

Kevin Buzzard (Apr 18 2020 at 15:41):

that crackpot CS class :-)

Kevin Buzzard (Apr 18 2020 at 15:42):

Didn't we once decide that Z\mathbb{Z} and k[X]k[X] were the only examples?

Kevin Buzzard (Apr 18 2020 at 15:43):

and then there was a proposal to change it all to semrings so we could have N\mathbb{N} and N[X]\mathbb{N}[X] as well :-)

Kevin Buzzard (Apr 18 2020 at 15:45):

https://math.stackexchange.com/questions/2901858/are-there-any-non-orientable-integral-domains

Robin Carlier (Apr 18 2020 at 15:53):

And here I am with yet another noob question arising while I'm trying to make something out of Cat :grinning_face_with_smiling_eyes:
What should I usually do when unification blatantly fails, as in :

import category_theory.isomorphism
import category_theory.category.Cat
import category_theory.opposites
universes v u
open category_theory

section
    instance b_cat_to_sort: has_coe_to_sort (Cat.{v u}) :=
    { S := Type u, coe := λ S, S.α }
    variables {C : Type u} [𝒞 : category.{v} C]
    def opfunctor : Cat.{u u}  Cat.{u u} :=
    {
      obj := λ C, Cat.of ((C.α)ᵒᵖ),
      map := λ C D F, functor.op F,
    }
    lemma op_is_involution : (opfunctor  opfunctor : Cat.{u u}  Cat.{u u}) = 𝟭 Cat.{u u} := begin
        apply functor.ext,
    end
end

Here the proof should be straightforward: look at objects, look at morphisms, done, yet functor.ext won't apply despite the goal being precisely of the right form, I'm a bit lost.

Reid Barton (Apr 18 2020 at 15:54):

You've fallen into a classic trap. Try jump-to-definition on functor.ext.

Bryan Gin-ge Chen (Apr 18 2020 at 15:56):

Robin Carlier said:

Yeah Reid Barton I saw that that canonical was in eq_hom... And is defined with a rw :grinning:
So I ended up using the one in eq_hom anyway, and it beign constructed with rwdid cause a lot of headeaches

I'm not familiar with the category theory library, but would it help if we replaced it with a term mode definition like the one I constructed above?

Robin Carlier (Apr 18 2020 at 15:57):

jump-to-definition?

Bryan Gin-ge Chen (Apr 18 2020 at 15:58):

If you're using VS code you can hit F12 when the cursor is in the name you're interested in to open an editor window at its definition, or you can right click and select "Go to definition".

Robin Carlier (Apr 18 2020 at 15:59):

Oh, thank you. well I end up in category rather than category_theory? IS that the "classic trap"?

Kevin Buzzard (Apr 18 2020 at 16:02):

Almost certainly

Kevin Buzzard (Apr 18 2020 at 16:02):

category is for computer scientists only

Robin Carlier (Apr 18 2020 at 16:03):

Okay, and so how should I call the "right" functor.ext then? category_theory.functor.ext won't work.

Robin Carlier (Apr 18 2020 at 16:06):

Alright I found it, just had to rename when I opened the category_theory namespace, is there any other way than renaming?

Kevin Buzzard (Apr 18 2020 at 16:08):

What did you rename?

Robin Carlier (Apr 18 2020 at 16:09):

I renamed functor.ext to something else, so when I apply, I get the thing I want and not that stuff from category

Robin Carlier (Apr 18 2020 at 16:09):

open category_theory (renaming functor.ext → functor.extension) And then apply functor.extension works like a charm

Robin Carlier (Apr 18 2020 at 16:11):

But was simply wondering if there's another way

Kevin Buzzard (Apr 18 2020 at 16:14):

I had almost forgotten that that was even possible. Are you sure that just using the full name doesn't work? It surely should

Robin Carlier (Apr 18 2020 at 16:17):

category_theory.functor.ext? yeah it works to, probably mistyped earlier then.

Reid Barton (Apr 18 2020 at 16:18):

By the way, in the category theory library we usually refrain from proving equalities of functors like this because it ends up committing you to the eq_to_hom world. However, I don't think it's necessarily a bad idea, and if you want to work in the 1-category Cat seriously, you have to do this.

Robin Carlier (Apr 18 2020 at 16:19):

Yeah I tried to avoid that so far but in this particular case (proving the opposite is an auto-equivalence ) it looks like I have to do that

Robin Carlier (Apr 18 2020 at 16:20):

@Bryan Gin-ge Chen regarding that definition with rw I seriously don't know, it's in eq_to_hom and as Reid said this is already quite a weird place

Reid Barton (Apr 18 2020 at 16:28):

When you define data using eq.subst/rw it becomes quite painful to prove anything about it. eq_to_hom was an effort to systematize that pain.

Robin Carlier (Apr 18 2020 at 16:28):

Yeah it saved my butt earlier in the day

Robin Carlier (Apr 18 2020 at 16:37):

Also, is there an intermediate step between the default verbosity and set_option pp.all true? My goal is showing some _terms that should be known (had the same stuff earlier), is there a way to make them display in full?

Bryan Gin-ge Chen (Apr 18 2020 at 16:38):

set_option pp.proofs true might help.

Robin Carlier (Apr 18 2020 at 16:41):

Yes, it removes the underscores! Thank you! Also, I have stuff like ?m_1[X] in my goal, which I assume is "some term that depends on X"? is there a way to make them explicit as well? They remain after set_option pp.proofs true

Reid Barton (Apr 18 2020 at 16:48):

They are "some term that depends (or rather, may depend) on X" and Lean doesn't know more about them.

Bryan Gin-ge Chen (Apr 18 2020 at 16:50):

Frequently you get metavariables in goals because Lean was unable to infer some implicit arguments to a term that you used in apply or refine.

Kevin Buzzard (Apr 18 2020 at 16:52):

If you have multiple goals then you might find that the ?m_1 in one of them is the solution to another one.

ROCKY KAMEN-RUBIO (Apr 18 2020 at 17:24):

Shing Tak Lam said:

Delete the inits.

So

import data.nat.basic
import data.list.basic
--https://observablehq.com/@bryangingechen/fibonacci-formalized-1-some-sums?collection=@bryangingechen/lean&fbclid=IwAR1UQq-DS6CG403IjSkBQS5n_evm9soXWgq-NKDyuyxB1Myc0J4tXd8xHhc

open nat list
#eval [1,2,3].sum

I tried doing that and got an error that mathlib wasn't in the lean path. Going back to my previous work it looks like I've always been usingimport init.data.nat.basic instead of import data.nat.basic. How do these two differ? How would one give me partial access to some nat stuff if mathlib wasn't in my lean path to begin with?

Reid Barton (Apr 18 2020 at 17:25):

Some of the nat stuff is in the core library rather than mathlib, and it happens to be in a module named init.data.nat.basic. But you get this automatically even if you don't import it (that's a feature of core library stuff under init.*).

Bryan Gin-ge Chen (Apr 18 2020 at 17:26):

I don't see how import init.data.nat.basic could do anything unless you have prelude at the very top of your file.

Bryan Gin-ge Chen (Apr 18 2020 at 17:31):

Oh, I see. Your file wasn't relying on mathlib at all before. So instead of just removing the inits from the imports, I would just delete the import data.nat.basic and import data.list.basic entirely.

If you do want to use stuff from mathlib, then you'll need to fix the path issue, and the best way to do that is to look at the install docs again, specifically the "project" doc if you've gotten leanproject working.

Nam (Apr 18 2020 at 17:40):

i have this

inductive sorted : list  -> Prop
| nil {} : sorted []
| one {x : } : sorted [x]
| other {x y : } {tail : list } (h: x <= y) (hs : sorted (y :: tail)) : sorted (x :: y :: tail)

how do i def a function that takes a sorted list and returns a sorted list?

Kevin Buzzard (Apr 18 2020 at 17:41):

id?

Kevin Buzzard (Apr 18 2020 at 17:42):

Is there a typo?

Kenny Lau (Apr 18 2020 at 17:42):

there are 3 Coq kata, all authored by @Donald Sebastian Leung, asking us to verify his sorting algorithms

Nam (Apr 18 2020 at 17:42):

ah, right. sorry. i missed it. i meant to ask for a function that takes an \N, a sorted list, and returns another sorted list.

Kevin Buzzard (Apr 18 2020 at 17:43):

Well then you can throw away the natural and return the sorted list.

Nam (Apr 18 2020 at 17:43):

(i.e. insertion sort)

Kevin Buzzard (Apr 18 2020 at 17:43):

Oh, this function has some hitherto undisclosed properties :-)

Kevin Buzzard (Apr 18 2020 at 17:44):

I would imagine that defining this function isn't too hard using the equation compiler.

Kenny Lau (Apr 18 2020 at 17:44):

(removed)

Reid Barton (Apr 18 2020 at 17:44):

For some reason people on Haskell channels like to ask questions like this, as though it's always obvious what any function would do from its type.

Kevin Buzzard (Apr 18 2020 at 17:44):

Actually, you might end up having to ask Chris how to get your recursion to be well-founded.

Kenny Lau (Apr 18 2020 at 17:45):

Kevin Buzzard said:

Actually, you might end up having to ask Chris how to get your recursion to be well-founded.

can't you just deconstruct the list one element by one element

Kenny Lau (Apr 18 2020 at 17:45):

each time you only need to recurse on the normal thing

Nam (Apr 18 2020 at 17:46):

@Kenny Lau i assume your Coq resource was directed to me. how does that help me understand Lean syntax?

Kenny Lau (Apr 18 2020 at 17:46):

that was before you clarified that you want insertion sort

Nam (Apr 18 2020 at 17:47):

there was some resource in Software Foundations about insertion sort, where my inspiration comes

Kevin Buzzard (Apr 18 2020 at 17:47):

Why not post the Coq code and ask how to translate it?

Nam (Apr 18 2020 at 17:47):

the problem i'm having is to find the right incantation

Kevin Buzzard (Apr 18 2020 at 17:48):

I don't think the mods will mind Coq code, as long as you don't do it too often.

Nam (Apr 18 2020 at 17:48):

i prefer it to be as natural as possible. i'm trying to learn the intuition behind it, not the mechanical translation of Coq -> Lean.

ROCKY KAMEN-RUBIO (Apr 18 2020 at 17:49):

Bryan Gin-ge Chen said:

Oh, I see. Your file wasn't relying on mathlib at all before. So instead of just removing the inits from the imports, I would just delete the import data.nat.basic and import data.list.basic entirely.

If you do want to use stuff from mathlib, then you'll need to fix the path issue, and the best way to do that is to look at the install docs again, specifically the "project" doc if you've gotten leanproject working.

This seems to be what my problem was - must have built my current project workspace incorrectly. I follow the directions to make a new workspace and now import data.nat.basic doesn't error anymore. Thank you!

Kenny Lau (Apr 18 2020 at 17:50):

@Nam have you seen how to recurse on a list?

Kenny Lau (Apr 18 2020 at 17:50):

if not, recurse on a natural number?

Kenny Lau (Apr 18 2020 at 17:50):

(I'm too busy to type any Lean code)

Nam (Apr 18 2020 at 17:51):

no worries! any idea is welcome. yes i know the concepts. what i'm asking isn't the implementation of such function. i'm only asking for the signature of the def.

Kenny Lau (Apr 18 2020 at 17:52):

oh that I can write: def insertion_sort : \N \to list \N \to list \N

Kenny Lau (Apr 18 2020 at 17:52):

(my algorithm doesn't need the list to be sorted in the first place to work)

Kenny Lau (Apr 18 2020 at 17:52):

(but I can guarantee that if you input a sorted list you will get a sorted list)

Kenny Lau (Apr 18 2020 at 17:52):

here in Lean we prefer to totalize our functions

Kenny Lau (Apr 18 2020 at 17:53):

and then theorem insertion_sort_correct: \forall n L, sorted L \to sorted (insertion_sort n L)

Kenny Lau (Apr 18 2020 at 17:53):

or some other properties that you want to verify

Nam (Apr 18 2020 at 17:55):

i don't quite understand your insertion_sort. does it sort a list, or does it insert a new element to a sorted list?

Kenny Lau (Apr 18 2020 at 17:55):

it insert a new element to a list in the appropriate place

Nam (Apr 18 2020 at 17:55):

so then that list must be sorted.

Kenny Lau (Apr 18 2020 at 17:55):

such that if the list is sorted to begin with, the output list will be sorted

Kenny Lau (Apr 18 2020 at 17:55):

we don't like partial functions

Reid Barton (Apr 18 2020 at 17:55):

It should probably be called something more like sorted_insert

Reid Barton (Apr 18 2020 at 17:56):

Also, the alternative isn't really a "partial function", it's a function with a precondition

Kenny Lau (Apr 18 2020 at 17:56):

I mean, the algorithm is basically: if the new element is smaller than the first element then put it before the first element; otherwise if it is smaller than the second element then put it before the second element, etc

Kenny Lau (Apr 18 2020 at 17:56):

none of the processes here requires the list to be sorted

Bryan Gin-ge Chen (Apr 18 2020 at 17:57):

I know you're doing this as a learning exercise, but at some point you may find it interesting to compare with the sorting code in mathlib: https://github.com/leanprover-community/mathlib/blob/master/src/data/list/sort.lean

Reid Barton (Apr 18 2020 at 17:57):

Nam, how is it written in your Coq book?

Nam (Apr 18 2020 at 17:58):

https://softwarefoundations.cis.upenn.edu/vfa-current/Sort.html

Reid Barton (Apr 18 2020 at 17:58):

Yes, it's just the same in Lean then.

Nam (Apr 18 2020 at 17:59):

yes, my sorted is very similar.

Reid Barton (Apr 18 2020 at 17:59):

I mean the syntax is not literally the same, because of the way the equation compiler works.

Nam (Apr 18 2020 at 17:59):

but i don't like the idea of feeding my "insert one element" with any list.

Reid Barton (Apr 18 2020 at 18:00):

But the separation of program from properties that Kenny is advocating is also in the original Coq version.

Nam (Apr 18 2020 at 18:00):

true. that's where i'd like to divert from the book. doing my own thing, if you will ;).

Nam (Apr 18 2020 at 18:01):

i think it's okay for the main sort function to take any list, but the insert one element function should only accept sorted list.

Reid Barton (Apr 18 2020 at 18:03):

Okay, you can do that by writing def insert : ℕ → Π (l : list ℕ), sorted l → list ℕ

Reid Barton (Apr 18 2020 at 18:03):

but if you implement this function, you'll find you never use the sorted l hypothesis

Nam (Apr 18 2020 at 18:03):

can i also qualify the output?

Nam (Apr 18 2020 at 18:04):

i think that's where this hypothesis will be used

Reid Barton (Apr 18 2020 at 18:04):

def insert : ℕ → Π (l : list ℕ), sorted l → {l' : list ℕ // sorted l'}

Kevin Buzzard (Apr 18 2020 at 18:04):

But if you do it like that then you'll have to do your work in the definition

Reid Barton (Apr 18 2020 at 18:05):

Now you have completely abandoned "separate program from properties". Sometimes it's easier this way.

Kevin Buzzard (Apr 18 2020 at 18:05):

Why not factor things out into the definition everyone else is suggesting

Kevin Buzzard (Apr 18 2020 at 18:05):

And then prove the theorem

Kevin Buzzard (Apr 18 2020 at 18:05):

And then make your definition

Kevin Buzzard (Apr 18 2020 at 18:05):

Which by this point will be one line long

Reid Barton (Apr 18 2020 at 18:05):

Well, sometimes you end up repeating work if you prove the properties separately

Reid Barton (Apr 18 2020 at 18:06):

Like in this case it might well be easier to prove the property at the same time as making the definition

Kevin Buzzard (Apr 18 2020 at 18:06):

I see

Reid Barton (Apr 18 2020 at 18:07):

Of course in this case, it's also easy either way

Kevin Buzzard (Apr 18 2020 at 18:07):

Right

Kevin Buzzard (Apr 18 2020 at 18:07):

But I see the general principle

Nam (Apr 18 2020 at 18:07):

i'm a software engineer. this is the natural way i would approach insertion sort.

Nam (Apr 18 2020 at 18:07):

but i see your points about "total function" or something like that.

Nam (Apr 18 2020 at 18:08):

(of course i don't know what that means yet)

Reid Barton (Apr 18 2020 at 18:08):

There are reasons you might prefer one over the other but I wouldn't exclude either approach a priori.

Kevin Buzzard (Apr 18 2020 at 18:08):

It means define the function without assuming the import list is sorted -- define it on all lists

Kenny Lau (Apr 18 2020 at 18:08):

if you're a software engineer, then you don't prove any properties at all when you build your function, and you would build your function exactly how I suggested: without caring about whether the original list is sorted

Kevin Buzzard (Apr 18 2020 at 18:09):

Like the square root function in mathlib is defined on all real numbers and just returns junk for negative ones

Kevin Buzzard (Apr 18 2020 at 18:09):

Lean offers you a way to prove that your code has no bugs

Kevin Buzzard (Apr 18 2020 at 18:09):

That would be the theorem you'd prove about your total function

Kevin Buzzard (Apr 18 2020 at 18:10):

You would accept the garbage in garbage out principle. Why check the list is sorted? It just wastes time

Kevin Buzzard (Apr 18 2020 at 18:11):

So in the definition you can take any list

Kevin Buzzard (Apr 18 2020 at 18:11):

And then the theorem you prove is that if the list is sorted your function isn't buggy

Nam (Apr 18 2020 at 18:12):

thanks! i'll need to adopt that thinking.

Kevin Buzzard (Apr 18 2020 at 18:12):

But you don't release the theorem to the customer, you just give them the super fast function and a warning that if they put garbage in they'll get garbage out

Kevin Buzzard (Apr 18 2020 at 18:13):

And if they want to check their input before they give it to your function that's their choice

Kevin Buzzard (Apr 18 2020 at 18:14):

They might instead choose to prove that their code which generates the list they will feed to your function is always sorted

Kevin Buzzard (Apr 18 2020 at 18:14):

And they can put that in the unit tests and not ship it

Nam (Apr 18 2020 at 18:21):

my take of dependent type is it helps me ensure some properties at compile time. e.g. sqrt n, n >= 0 ensures that clients cannot pass invalid input throughout the whole program. i like that very much.

Kevin Buzzard (Apr 18 2020 at 18:31):

Well you can offer both functions to the customer

Kevin Buzzard (Apr 18 2020 at 18:33):

The one which demands a certificate that the list is sorted costs more to use but has the advantage that it outputs a guarantee that the output is also sorted

Kevin Buzzard (Apr 18 2020 at 18:34):

You shouldn't be writing your own algorithms that check your customer's list is sorted, the customer's lists might have properties which only they know about which might make checking much easier for them than for you

Kevin Buzzard (Apr 18 2020 at 18:35):

The compile time win is what you get when you define the more sophisticated function

Luca Seemungal (Apr 18 2020 at 20:15):

Question: Given h : ∃ x : α, p x, how do I pull out a term x : α and a term hx : p x where p : α → Prop?

Discussion: In TPIL, it says:

"The existential elimination rule, exists.elim, performs the opposite operation. It allows us to prove a proposition q from ∃ x : α, p x, by showing that q follows from p w for an arbitrary value w. Roughly speaking, since we know there is an x satisfying p x, we can give it a name, say, w. If q does not mention w, then showing that q follows from p w is tantamount to showing the q follows from the existence of any such x."

What has q got to do with anything?

Kenny Lau (Apr 18 2020 at 20:17):

well when you say "pull out a term ..." you have a goal in mind, i.e. a proposition you want to prove, and that is q

Kenny Lau (Apr 18 2020 at 20:18):

but I would agree that "if q does not mention w" sounds a bit clumsy to me

Luca Seemungal (Apr 18 2020 at 20:22):

yeah, the proposition i want to prove is the stuff that is in the goal next to the turnstile, right?

sorry, i'm not sure if i understand. I'm kind of expecting to be at a point where i can have x : α and hx : p x in my assumptions, and I don't know what q has got anything to do with me wanting that

Reid Barton (Apr 18 2020 at 20:25):

If you're in tactic mode, then probably the answer you're looking for is cases h with x hx.

Reid Barton (Apr 18 2020 at 20:25):

TPIL is explaining the theory behind the term that that tactic will generate.

Luca Seemungal (Apr 18 2020 at 20:27):

yes, that tactic worked! I should probably at some point figure out the theory though

Luca Seemungal (Apr 18 2020 at 20:27):

cheers

Kevin Buzzard (Apr 18 2020 at 20:45):

If h : ∃ x : α, p x is a hypothesis and your goal is ⊢ q then what you are trying to do is to prove (∃ x : α, p x) -> q

Kevin Buzzard (Apr 18 2020 at 20:47):

I wouldn't worry about what TPIL says. The authors are thinking about how to explain what is going on to computer scientists who think about logic in some weird way involving gigantic fractions with sideways T's on the numerator and denominator. Mathematicians don't need to think about that because they only use one logic and they have completely internalised it.

Kevin Buzzard (Apr 18 2020 at 20:48):

You just need to know that clearly if we have proved there exists x such that p(x) then we can obviously just take such an x. Computer scientists might want to think very carefully about what just happened there, but I don't see any reason why mathematicians need to.

Nam (Apr 18 2020 at 21:06):

i have h1 : Prop := x ≤ head, how do i turn that into x ≤ head : Prop?

Nam (Apr 18 2020 at 21:06):

sorry if that sounds silly

Kevin Buzzard (Apr 18 2020 at 21:07):

How did you make h1 and are you in term mode or tactic mode?

Nam (Apr 18 2020 at 21:07):

i've been trying to make Reid's suggestion work.

inductive sorted : list  -> Prop
| nil {} : sorted []
| one {x : } : sorted [x]
| other {x y : } {tail : list } (h: x <= y) (hs : sorted (y :: tail)) : sorted (x :: y :: tail)

lemma tail_of_sorted_list_is_sorted {xs : list } (hs : sorted xs) : sorted (list.tail xs) :=
begin
  induction xs,
  case list.nil { apply sorted.nil },
  case list.cons : head tail {
    rewrite list.tail,
    cases hs,
    case sorted.one { exact sorted.nil },
    case sorted.other : tail_head tail_tail h_lte h_sorted { exact h_sorted }
  },
end

def ins_sort_insert :  -> Π (l: list ), sorted l -> {l' : list  // sorted l'}
| x [] h := [x], sorted.one
| x (head :: tail) h :=
  let h1 := x <= head in
    if h1 then
    begin
      exact  x :: (head :: tail), sorted.other h1 h,
    end
    else
    begin
      exact head :: (ins_sort_insert x tail (tail_of_sorted_list_is_sorted h)), h,
    end

Nam (Apr 18 2020 at 21:08):

h1 is created by the let clause

Kevin Buzzard (Apr 18 2020 at 21:08):

Can you post complete working code so I can just cut and paste?

Kevin Buzzard (Apr 18 2020 at 21:10):

type mismatch at application
  sorted.other h1
term
  h1
has type
  Prop : Type
but is expected to have type
  x  head : Prop

Kevin Buzzard (Apr 18 2020 at 21:10):

h1 is a proposition; h1 : Prop.

Kevin Buzzard (Apr 18 2020 at 21:10):

But the first input to sorted.other isn't something of type Prop, it's a proof

Kevin Buzzard (Apr 18 2020 at 21:11):

Don't confuse a theorem statement and a theorem proof.

Nam (Apr 18 2020 at 21:11):

so by h1 then?

Kevin Buzzard (Apr 18 2020 at 21:11):

h1 is a type. You don't want to feed in h1, you want to feed in a term of type h1.

Kevin Buzzard (Apr 18 2020 at 21:12):

I don't see a proof of h1 in your context, because you used if and not a dependent if.

Kevin Buzzard (Apr 18 2020 at 21:12):

You need to do this:

  let h1 := x <= head in
    if h1proof : h1 then

Kevin Buzzard (Apr 18 2020 at 21:13):

This carries the proof of h1 into the then clause

Nam (Apr 18 2020 at 21:14):

oh, thanks! that's another incantation for me to remember.

Nam (Apr 18 2020 at 21:14):

actually, i don't need h1, i need its proof.

Kevin Buzzard (Apr 18 2020 at 21:14):

if c then ... else is syntax sugar for ite:

ite : Π (c : Prop) [h : decidable c] {α : Sort u_1}, α  α  α

and the case split doesn't carry the proof along with it.

Nam (Apr 18 2020 at 21:14):

can i do let h1 : (x <= head : Prop) := x <= head in?

Kevin Buzzard (Apr 18 2020 at 21:16):

x <= head is a Proposition. It's a term of type Prop. 2+2=5 is a proposition. You can write let h1 := 2 + 2 = 5 in ... and that's fine. You've just defined h1 to be a proposition which is always false.

Kevin Buzzard (Apr 18 2020 at 21:16):

No amount of shuffling syntax around is going to come up with a proof of 2+2=5 though.

Kevin Buzzard (Apr 18 2020 at 21:16):

Prop is the universe, P : Prop is the type and h : P is the term. h is a proof of P.

Kevin Buzzard (Apr 18 2020 at 21:17):

You can make as many P's as you like, but making h's is hard.

Kevin Buzzard (Apr 18 2020 at 21:17):

You can't just create a proof that x <= head because it might not be true.

Kevin Buzzard (Apr 18 2020 at 21:18):

What you called h1 is not the usual notation for this -- usually h is used for proofs and stuff like p or P is used for the propositions.

Nam (Apr 18 2020 at 21:18):

i see. that's why it's only available in the case split.

Kevin Buzzard (Apr 18 2020 at 21:18):

Right. In the dependent case split you split into either there being a proof of h1 or a proof of not h1

Kevin Buzzard (Apr 18 2020 at 21:19):

if h : P then ... else ... is syntax sugar for dite:

dite : Π (c : Prop) [h : decidable c] {α : Sort u_1}, (c  α)  (¬c  α)  α

dite is better than ite because you get a proof of c or not c as appropriate when you're trying to construct your terms of type alpha which you need to fill in the inputs to the dite.

Nam (Apr 18 2020 at 21:19):

btw, does this look "idiomatic" other than the naming?

Mario Carneiro (Apr 18 2020 at 21:20):

the begin exact end is superfluous

Kevin Buzzard (Apr 18 2020 at 21:20):

begin exact h end is the same as h

Nam (Apr 18 2020 at 21:20):

ah well, i dno't know how many lines i would need, so begin and end

Mario Carneiro (Apr 18 2020 at 21:20):

the let h1 := x <= head seems very odd but you may have fixed it by now

Nam (Apr 18 2020 at 21:21):

i was just going to call it p

Nam (Apr 18 2020 at 21:21):

maybe that's not enough?

Mario Carneiro (Apr 18 2020 at 21:21):

you don't need the let at all

Kevin Buzzard (Apr 18 2020 at 21:21):

if h : x <= head then

Mario Carneiro (Apr 18 2020 at 21:21):

just use if h : x <= head then ... else ...

Nam (Apr 18 2020 at 21:21):

got it.

Kevin Buzzard (Apr 18 2020 at 21:22):

wooah you already have an h

Kevin Buzzard (Apr 18 2020 at 21:22):

if h2 : x <= head then

Kevin Buzzard (Apr 18 2020 at 21:25):

def ins_sort_insert :  -> Π (l: list ), sorted l -> {l' : list  // sorted l'}
| x [] h := [x], sorted.one
| x (head :: tail) h :=
  if h2 : x <= head then
   begin
      exact  x :: (head :: tail), sorted.other h2 h,
    end
    else
    begin
      refine head :: (ins_sort_insert x tail (tail_of_sorted_list_is_sorted h)), _⟩,
      sorry -- you have some work to do
    end

Mario Carneiro (Apr 18 2020 at 21:25):

You can also inline / simplify the proof of tail_of_sorted_list_is_sorted in ins_sort_insert

Nam (Apr 18 2020 at 21:26):

i can't use h2 in the else, can i?

Mario Carneiro (Apr 18 2020 at 21:26):

you can

Kevin Buzzard (Apr 18 2020 at 21:26):

The missing proof (indicated with an _) is not h

Kevin Buzzard (Apr 18 2020 at 21:26):

h2 is a proof of ¬x ≤ head in the else branch

Nam (Apr 18 2020 at 21:26):

but h2 won't be accepted by sorted

Kevin Buzzard (Apr 18 2020 at 21:27):

The type of h2 is currently not (le x head)

Kevin Buzzard (Apr 18 2020 at 21:27):

and sorted doesn't seem to have any inputs of that type

Nam (Apr 18 2020 at 21:27):

should it?

Mario Carneiro (Apr 18 2020 at 21:27):

no

Nam (Apr 18 2020 at 21:27):

sorted seems to be complete. yeah.

Mario Carneiro (Apr 18 2020 at 21:28):

what did you try?

Nam (Apr 18 2020 at 21:28):

i've only tried sorted.other h2 h, similar to the if clause.

Mario Carneiro (Apr 18 2020 at 21:28):

code

Nam (Apr 18 2020 at 21:28):

else
  begin
    exact head :: (ins_sort_insert x tail (tail_of_sorted_list_is_sorted h)), sorted.other h_x_head h,
  end

Mario Carneiro (Apr 18 2020 at 21:28):

complete code?

Kevin Buzzard (Apr 18 2020 at 21:29):

What is h_x_head? Please post complete code

Nam (Apr 18 2020 at 21:29):

def ins_sort_insert :  -> Π (l: list ), sorted l -> {l' : list  // sorted l'}
| x [] h := [x], sorted.one
| x (head :: tail) h :=
  if h_x_head : x <= head then
    x :: (head :: tail), sorted.other h_x_head h
  else
  begin
    exact head :: (ins_sort_insert x tail (tail_of_sorted_list_is_sorted h)), sorted.other h_x_head h,
  end

Nam (Apr 18 2020 at 21:29):

it is what i call your h2

Nam (Apr 18 2020 at 21:30):

i understand that current code won't work.

Nam (Apr 18 2020 at 21:30):

(as Kevin showed before)

Kevin Buzzard (Apr 18 2020 at 21:30):

sorted.other needs a proof of A <= B for some A and B, and h_x_head has type not (something) which is not the same as le A B

Kevin Buzzard (Apr 18 2020 at 21:30):

You can see this in the error message. You have to prove a theorem here.

Nam (Apr 18 2020 at 21:30):

right, that's what i'm trying to reconcile.

Mario Carneiro (Apr 18 2020 at 21:31):

You should use \not (x <= head) to prove head <= x, using le_of_not_ge, but even then I think it will not work because h isn't the right second argument

Kevin Buzzard (Apr 18 2020 at 21:31):

But you can't just get this for free, it's not just a simple thing, this is where the work is.

Mario Carneiro (Apr 18 2020 at 21:32):

indeed I would be surprised if you can finish the proof from here, because you don't know enough in the inductive hypothesis

Kevin Buzzard (Apr 18 2020 at 21:32):

In fact I am not even sure that your theorem is true

Nam (Apr 18 2020 at 21:33):

insert to a sorted list does not yield another sorted list?

Kevin Buzzard (Apr 18 2020 at 21:33):

All you know is head < x, but you need that head <= (smallest element of tail) for your list to be sorted

Nam (Apr 18 2020 at 21:33):

smallest element of tail is its head

Nam (Apr 18 2020 at 21:33):

because tail of a sorted list is sorted

Kevin Buzzard (Apr 18 2020 at 21:33):

Right, but you don't know anything about the head of tail.

Mario Carneiro (Apr 18 2020 at 21:34):

To clarify Kevin's assertion, of course the function is in fact going to sort the list, but you can't just prove that by induction, the induction hypothesis needs to be stronger than that

Kevin Buzzard (Apr 18 2020 at 21:34):

Actually I think we might be OK

Nam (Apr 18 2020 at 21:34):

ahhh

Kevin Buzzard (Apr 18 2020 at 21:34):

We have h : sorted (head :: tail)

Kevin Buzzard (Apr 18 2020 at 21:35):

so head <= head(tail)

Kevin Buzzard (Apr 18 2020 at 21:35):

wait

Mario Carneiro (Apr 18 2020 at 21:35):

If you had a separate definition, this would be less of a problem because you would know what list is being returned, but here you only know you have some sorted list

Mario Carneiro (Apr 18 2020 at 21:35):

so who knows if adding head to the front of it keeps it sorted

Mario Carneiro (Apr 18 2020 at 21:35):

it has no relation to the input

Nam (Apr 18 2020 at 21:35):

the input is x

Nam (Apr 18 2020 at 21:36):

and x > head

Mario Carneiro (Apr 18 2020 at 21:36):

That is, you don't know that ins_sort_insert x tail (tail_of_sorted_list_is_sorted h) contains x or tail or has any relation to them. It might be the empty list

Mario Carneiro (Apr 18 2020 at 21:36):

the only thing the type tells you is that it is a sorted list

Kevin Buzzard (Apr 18 2020 at 21:37):

The problem is that because of your choice of induction, all you know about ins_sort_insert x tail _ is that it is a sorted list.

Nam (Apr 18 2020 at 21:37):

ahh. so i need a stronger condition? say, list.length > 0?

Mario Carneiro (Apr 18 2020 at 21:37):

that assumption won't help you prove the theorem

Mario Carneiro (Apr 18 2020 at 21:38):

you need something that relates the output to the input

Nam (Apr 18 2020 at 21:38):

what have i got into. lol.

Nam (Apr 18 2020 at 21:38):

i thought this would be a walk in the park ;)

Kevin Buzzard (Apr 18 2020 at 21:38):

theorem proving :-)

Kevin Buzzard (Apr 18 2020 at 21:38):

Did you prove that the reverse of the reverse of a list was itself yet?

Kevin Buzzard (Apr 18 2020 at 21:38):

That was the point where I realised that lists were harder than they looked.

Kevin Buzzard (Apr 18 2020 at 21:39):

That theorem is delicate in the same way that this is delicate.

Kevin Buzzard (Apr 18 2020 at 21:39):

Mario can probably write some incomprehensible 5-line definition which works fine

Mario Carneiro (Apr 18 2020 at 21:39):

lol

Kevin Buzzard (Apr 18 2020 at 21:39):

but what I learnt very early on is that just because it can be done in 5 lines of code, doesn't mean at all that it is easy to do in 5 lines of code.

Nam (Apr 18 2020 at 21:40):

not so sure if i've done that. i started with Hitchhiker's guide, and it solved that by simp

Mario Carneiro (Apr 18 2020 at 21:40):

I want to reiterate the suggestion to separate the definition and the theorem

Kevin Buzzard (Apr 18 2020 at 21:41):

Yeah, it was only Reid who suggested to do them both at once, and now he's gone

Mario Carneiro (Apr 18 2020 at 21:41):

this gives you a lot more freedom to inspect and case analyze the definition in the theorem, because the inductions aren't wrapped up into one

Nam (Apr 18 2020 at 21:42):

the downside is it doesn't give me any assurance about the inputs / outputs.

Mario Carneiro (Apr 18 2020 at 21:42):

Of course it does

Nam (Apr 18 2020 at 21:42):

i'm used to "contract" kind of defensive programming

Mario Carneiro (Apr 18 2020 at 21:42):

It gives you the assurance by its definition

Nam (Apr 18 2020 at 21:43):

it doesn't. my users can feed it any list.

Mario Carneiro (Apr 18 2020 at 21:43):

The key thing that separates programming in lean from other functional languages you may be used to is that all defs are transparent; you know exactly what they evaluate to even after you are done with the definition itself

Nam (Apr 18 2020 at 21:43):

(even though it is an internal function for insertion sort and users shouldn't see it)

Mario Carneiro (Apr 18 2020 at 21:43):

they can, and they will get what they get

Mario Carneiro (Apr 18 2020 at 21:44):

and there are equations telling them what they will get

Mario Carneiro (Apr 18 2020 at 21:44):

but the point that I want to make here is that even your "improved" function, saying that it returns a sorted list, isn't good enough because you will want to know more than that

Mario Carneiro (Apr 18 2020 at 21:45):

what good is an insertion sort function that always returns []? or [0]?

Nam (Apr 18 2020 at 21:45):

yes, that's a fair critique.

Mario Carneiro (Apr 18 2020 at 21:45):

but in lean, you can also look at the definition later and see what it does, and prove more theorems about the definition

Mario Carneiro (Apr 18 2020 at 21:46):

and for this it is best if the definition is as simple and nondependent as possible

Nam (Apr 18 2020 at 21:47):

are you saying that "dependent types" should be used more in proving than, erm, for the lack of better words, coding?

Mario Carneiro (Apr 18 2020 at 21:48):

So you can write a function containing the "logical core" of insertion sort, with no sorted requirements, and then prove a theorem saying that this function produces sorted output from sorted input. You can also prove that it produces a permutation of x::xs (which you didn't put in the original definition), or even more precisely you can prove that there exist l1 l2 such that xs = l1 ++ l2 and the output is l1 ++ x :: l2

Mario Carneiro (Apr 18 2020 at 21:48):

That's right

Nam (Apr 18 2020 at 21:49):

i see. apparently i was hoping for something like https://en.wikipedia.org/wiki/Design_by_contract but at compile time.

Mario Carneiro (Apr 18 2020 at 21:50):

Oh, I'm not done. Once you've done all that, it is now trivial to write insertion_sort : A -> {l : list A // sorted l} -> {l : list A // sorted l}

Mario Carneiro (Apr 18 2020 at 21:50):

and this will give you all the contracts you want

Mario Carneiro (Apr 18 2020 at 21:52):

But the core of the program involves assumptions only rarely. The main place you see assumptions showing up in the "coding" part is to avoid unreachable branches, for example in list.nth_le (l : list A) (n : nat) : n <= length l -> A

Nam (Apr 18 2020 at 21:52):

i see. is that the same list l in both output and input?

Mario Carneiro (Apr 18 2020 at 21:52):

no

Mario Carneiro (Apr 18 2020 at 21:52):

there are two separate binders

Nam (Apr 18 2020 at 21:53):

then it won't solve the argument you mentioned before, that the output isn't related to the input.

Mario Carneiro (Apr 18 2020 at 21:53):

you are right

Mario Carneiro (Apr 18 2020 at 21:54):

but you can also have an improved insertion sort contract like insertion_sort (x : A) (l : list A) : sorted l -> {l' : list A // sorted l' /\ x::l ~ l'}

Mario Carneiro (Apr 18 2020 at 21:54):

where now the postcondition is relating the output to the input

Nam (Apr 18 2020 at 21:55):

i see. i assume that this separation would be easier to prove (via composition etc.)

Mario Carneiro (Apr 18 2020 at 21:55):

and hint hint this kind of style can also be used for a direct induction proof

Nam (Apr 18 2020 at 21:56):

yeah, so i was going to ask, what if i strengthen the postcondition of ins_sort_insert...

Mario Carneiro (Apr 18 2020 at 21:57):

yes, there is a way to strengthen the postcondition such that you can prove the theorem. If you try proving the theorem as is you will eventually get stuck and the place where you get stuck will give you the hint for what to put in the postcondition

Nam (Apr 18 2020 at 21:59):

sounds good! thank you, folks. this practice has yielded much more insights than i hoped for.

Scott Morrison (Apr 18 2020 at 23:18):

Kevin Buzzard said:

category is for computer scientists only

I'd like to rename category to control, and category_theory to category. How would people feel about this? (Possibly some alternatives to control?) I could update the docs explaining the connection between the two.

Nam (Apr 18 2020 at 23:57):

what is control about? Z-transform? you know, there's control theory too ;)

Reid Barton (Apr 18 2020 at 23:58):

The root problem (so to speak) is that functor and all that stuff is not in any namespace.

Reid Barton (Apr 18 2020 at 23:59):

I was going to suggest moving it into category but we could also change the name.

Scott Morrison (Apr 19 2020 at 00:06):

If we commit to a name, I could move functor and friends in core into that namespace, and then make the rename in mathlib once that lands. (I guess in 3.10 or beyond!)

Reid Barton (Apr 19 2020 at 00:07):

I assume the name control comes from the Haskell library though I don't know specifically how that name was chosen

Scott Morrison (Apr 19 2020 at 00:08):

I just remember control being suggested as an alternative last time we talked about this.

Scott Morrison (Apr 19 2020 at 00:08):

I do sort of like the Haskell connotation: "you'll feel at home in this directory if you learnt category theory from Haskell".

Jasmin Blanchette (Apr 19 2020 at 08:05):

@Nam your star syntax is a bit off. Try (hab : star r a b) and similarly for star r b c. I don't know why Lean forces you to specify names here.

Kenny Lau (Apr 19 2020 at 08:52):

import tactic

inductive sorted : list  -> Prop
| nil {} : sorted []
| one {x : } : sorted [x]
| other {x y : } {tail : list } (h: x <= y) (hs : sorted (y :: tail)) : sorted (x :: y :: tail)

lemma sorted_tail_of_sorted {hd tl} (HL : sorted (hd::tl)) : sorted tl :=
by { cases HL, constructor, assumption }

def insertion_sort_core :   list   list 
| x []       := [x]
| x (hd::tl) := if x  hd then x::hd::tl else hd :: (insertion_sort_core x tl)

theorem insertion_sort_aux {n L} (HL : sorted L) : sorted (insertion_sort_core n L) :=
begin
  induction L with x tl ih, { exact sorted.one },
  unfold insertion_sort_core, split_ifs with hx, { exact sorted.other hx HL },
  specialize ih (sorted_tail_of_sorted HL),
  cases tl with y tl, { exact sorted.other (le_of_not_le hx) sorted.one },
  unfold insertion_sort_core at ih ,
  split_ifs at ih  with hy,
  { exact sorted.other (le_of_not_le hx) ih },
  { cases HL, exact sorted.other x  y ih }
end

def insertion_core (n : ) (L : list ) (HL : sorted L) : { L' : list  // sorted L' } :=
insertion_sort_core n L, insertion_sort_aux HL

Kenny Lau (Apr 19 2020 at 08:52):

@Nam separate definition from theorems

Kevin Buzzard (Apr 19 2020 at 08:54):

Ok so it was ten lines not five

Kenny Lau (Apr 19 2020 at 08:54):

maybe I should include more assertions

Mario Carneiro (Apr 19 2020 at 09:03):

I think you can skip sorted_tail_of_sorted if you cases HL instead of cases tl in insertion_sort_aux

Kenny Lau (Apr 19 2020 at 09:07):

import tactic

inductive sorted : list  -> Prop
| nil {} : sorted []
| one {x : } : sorted [x]
| other {x y : } {tail : list } (h: x <= y) (hs : sorted (y :: tail)) : sorted (x :: y :: tail)

def insertion_sort_core :   list   list 
| x []       := [x]
| x (hd::tl) := if x  hd then x::hd::tl else hd :: (insertion_sort_core x tl)

theorem insertion_sort_aux {n L} (HL : sorted L) : sorted (insertion_sort_core n L) :=
begin
  induction L with x tl ih, { exact sorted.one },
  unfold insertion_sort_core, split_ifs with hx, { exact sorted.other hx HL },
  cases HL with _ _ y tl hxy htl, { exact sorted.other (le_of_not_le hx) sorted.one },
  unfold insertion_sort_core at ih , split_ifs at ih  with hy,
  { exact sorted.other (le_of_not_le hx) (ih htl) },
  { exact sorted.other hxy (ih htl) }
end

def insertion_core (n : ) (L : list ) (HL : sorted L) : { L' : list  // sorted L' } :=
insertion_sort_core n L, insertion_sort_aux HL

Mario Carneiro (Apr 19 2020 at 09:13):

did you want to call the final function insertion_sort instead of insertion_core?

Mario Carneiro (Apr 19 2020 at 09:14):

also we would normally call the theorem insertion_sort_core_sorted

Kenny Lau (Apr 19 2020 at 09:16):

yeah I meant insertion_sort

Luca Seemungal (Apr 19 2020 at 09:44):

how do i use a stronger induction principle that usual? so i want something like
property holds for 0 and 1, and if property holds for n and n+1, then property holds for n+2

Kevin Buzzard (Apr 19 2020 at 09:45):

Deleted

Mario Carneiro (Apr 19 2020 at 09:45):

You can often get the equation compiler to prove these for you

Kevin Buzzard (Apr 19 2020 at 09:45):

(typo fixed)

Kevin Buzzard (Apr 19 2020 at 09:46):

Luca I have a good exercise for you

Kevin Buzzard (Apr 19 2020 at 09:46):

Why not formalise the statement of the theorem you want?

Kenny Lau (Apr 19 2020 at 09:46):

consider the property "n <= 2"

Luca Seemungal (Apr 19 2020 at 09:47):

ok i'll try that

Luca Seemungal (Apr 19 2020 at 09:47):

also whats an equation compiler, how do I use it

Kevin Buzzard (Apr 19 2020 at 09:48):

Ie theorem luca_induction which takes P: nat -> Prop and the assumptions that P 0 and P 1 and the inductive hyp and the conclusion is for all n, P n

Kevin Buzzard (Apr 19 2020 at 09:48):

Forget about the equation compiler for now

Kevin Buzzard (Apr 19 2020 at 09:48):

That only works in toy examples

Kevin Buzzard (Apr 19 2020 at 09:48):

You can't do it in the middle of a tactic proof

Kevin Buzzard (Apr 19 2020 at 09:48):

That's the CS approach

Kenny Lau (Apr 19 2020 at 09:48):

Kevin Buzzard said:

You can't do it in the middle of a tactic proof

factor it out

Mario Carneiro (Apr 19 2020 at 09:49):

you can use the equation compiler to prove luca_induction though

Kevin Buzzard (Apr 19 2020 at 09:49):

Forget about the equation compiler

Kevin Buzzard (Apr 19 2020 at 09:49):

It's a great trick once you know what you're doing

Kevin Buzzard (Apr 19 2020 at 09:49):

Let's start by formalising the statement

Kenny Lau (Apr 19 2020 at 09:49):

what will you do in the middle of a tactic proof

Kevin Buzzard (Apr 19 2020 at 09:49):

And then let's formalise the proof

Kevin Buzzard (Apr 19 2020 at 09:50):

And then let's use the equation compiler after

Kevin Buzzard (Apr 19 2020 at 09:51):

The equation compiler is term mode trickery. There is a more basic principle at stake here which can be entirely dealt with in tactic mode

Luca Seemungal (Apr 19 2020 at 09:54):

ok

p is our predicate on the naturals, and we're supposing that p holds for some n₀ and n₀+1, and we're also supposing that if p holds for some n and n+1 then it holds for n+2. From that we want to prove that p holds for any n

theorem luca_induction (p :   Prop) (n₀ : ) (n : ) (h₀ : p n₀)
    (h₁ : p (n₀+1)) (H : p n  p (n+1)  p (n+2)) :
p n := sorry

Luca Seemungal (Apr 19 2020 at 09:55):

i'll have a stab at proving it now

Kenny Lau (Apr 19 2020 at 09:56):

consider p n = (n <= 2), n0 = 0, n = 37

Kevin Buzzard (Apr 19 2020 at 09:56):

You have to get the statement right first

Kevin Buzzard (Apr 19 2020 at 09:56):

This is a really good exercise

Kevin Buzzard (Apr 19 2020 at 09:56):

It's all about getting the quantifiers in the right order

Luca Seemungal (Apr 19 2020 at 09:57):

ah right yes

theorem luca_induction (p :   Prop) (n₀ : ) (n₁ : ) (n : ) (h₀ : p n₀)
    (h₁ : p (n₀+1)) (H : p n  p (n+1)  p (n+2)) : (hn : n  n₀)  p n := sorry

Kenny Lau (Apr 19 2020 at 09:58):

theorem bad_induction_1 :
  ¬  (p :   Prop) (n₀ : ) (n : ) (h₀ : p n₀) (h₁ : p (n₀+1)) (H : p n  p (n+1)  p (n+2)), p n :=
λ H, absurd (H (λ n, n  2) 0 37 dec_trivial dec_trivial dec_trivial) dec_trivial

Kenny Lau (Apr 19 2020 at 09:59):

the new one doesn't even compile

Luca Seemungal (Apr 19 2020 at 09:59):

oh gosh yes

Kenny Lau (Apr 19 2020 at 09:59):

but still:

theorem bad_induction_2 :
  ¬  (p :   Prop) (n₀ : ) (n : ) (h₀ : p n₀) (h₁ : p (n₀+1)) (H : p n  p (n+1)  p (n+2)) (hn : n  n₀) , p n :=
λ H, absurd (H (λ n, n  2) 0 37) dec_trivial

Kenny Lau (Apr 19 2020 at 10:00):

@Kevin Buzzard I discovered the golf by accident lol

Luca Seemungal (Apr 19 2020 at 10:00):

theorem luca_induction (p :   Prop) (n₀ : ) (n : ) (h₀ : p n₀)
    (h₁ : p (n₀+1)) (H : p n  p (n+1)  p (n+2)) (hn : n  n₀) : p n := sorry

Luca Seemungal (Apr 19 2020 at 10:00):

oh right

hmmm

Luca Seemungal (Apr 19 2020 at 10:00):

ill have another little think

Kevin Buzzard (Apr 19 2020 at 10:03):

If you want a hint Luca you could try #check nat.induction_on or something, to see the statement of usual induction written as a function in Lean

Kevin Buzzard (Apr 19 2020 at 10:03):

I'm not sure what the actual term is called, I'm not at a computer right now

Kenny Lau (Apr 19 2020 at 10:04):

nat.rec_on

Kevin Buzzard (Apr 19 2020 at 10:04):

For every attempt of yours which compiles there's a little game which is to either formally prove it or formally disprove it

Kevin Buzzard (Apr 19 2020 at 10:05):

Kenny I don't want something which mentions Sort ideally

Kevin Buzzard (Apr 19 2020 at 10:05):

Luca is an UG mathematician like you

Kenny Lau (Apr 19 2020 at 10:05):

there's nothing that doesn't mention Sort

Kevin Buzzard (Apr 19 2020 at 10:06):

Oh rotten luck

Kevin Buzzard (Apr 19 2020 at 10:06):

Well we'll just have to remember that Prop is Sort 0

Luca Seemungal (Apr 19 2020 at 10:12):

Ah, I see where my mistake is!

Luca Seemungal (Apr 19 2020 at 10:12):

theorem luca_induction (p :   Prop) (n : ) (n₀ : ) (h₀ : p n₀  p (n₀+1))
    (H :  n : , p n  p (n+1)  p (n+2)) (hn : n  n₀) := sorry

Luca Seemungal (Apr 19 2020 at 10:12):

that should have fixed it

Kenny Lau (Apr 19 2020 at 10:12):

except it doesn't compile again

Luca Seemungal (Apr 19 2020 at 10:12):

you're right, i ignored the red squiggly line

Luca Seemungal (Apr 19 2020 at 10:13):

theorem luca_induction (p :   Prop) (n : ) (n₀ : ) (h₀ : p n₀  p (n₀+1))
    (H :  n : , p n  p (n+1)  p (n+2)) (hn : n  n₀) : p n := sorry

this one doesn't have any red squiggly lines, sorry about that

Kenny Lau (Apr 19 2020 at 10:14):

that looks correct

Kenny Lau (Apr 19 2020 at 10:15):

@Kevin Buzzard what's the next step

Luca Seemungal (Apr 19 2020 at 10:16):

i guess i'll start proving it, my first guess is to try

cases classical.em (n = n₀ ∨ n = n₀+1),

Kenny Lau (Apr 19 2020 at 10:19):

maybe Kevin will tell you to write down the proof in maths first

Luca Seemungal (Apr 19 2020 at 10:21):

good point

Kevin Buzzard (Apr 19 2020 at 10:29):

Sorry, was dealing with reality

Kevin Buzzard (Apr 19 2020 at 10:29):

Now let's prove it in tactic mode by induction!

Kevin Buzzard (Apr 19 2020 at 10:31):

The question now is precisely which statement we're going to prove by induction

Kevin Buzzard (Apr 19 2020 at 10:32):

We need some other statement Q(n) such that Q(n_0) is true, and Q(m)->Q(m+1), and Q(m)->P(m)

Kevin Buzzard (Apr 19 2020 at 10:32):

We're doing two things at once. Can we start with n_0=0?

Kevin Buzzard (Apr 19 2020 at 10:33):

theorem luca_induction_aux (p :   Prop) (n : )  (h₀ : p 0  p 1)
    (H :  n : , p n  p (n+1)  p (n+2)) : p n :=
begin
  sorry
end

Kevin Buzzard (Apr 19 2020 at 10:33):

I reckon that level is solvable

Luca Seemungal (Apr 19 2020 at 10:33):

ok, i'll give it a go

Kevin Buzzard (Apr 19 2020 at 10:34):

but when Lean defines nat you get the two constructors zero and succ, and just the one eliminator, which is proof by normal induction

Kevin Buzzard (Apr 19 2020 at 10:34):

So you need to find Q which is provable by normal induction and which implies P

Luca Seemungal (Apr 19 2020 at 10:35):

Q(m) = P(m-1) and P(m)

Kevin Buzzard (Apr 19 2020 at 10:35):

Subtraction is always bad

Kevin Buzzard (Apr 19 2020 at 10:35):

this is nat

Kevin Buzzard (Apr 19 2020 at 10:35):

You won't be able to prove m-1+1=m because it's not true

Luca Seemungal (Apr 19 2020 at 10:35):

right ok I see

i might have also got my Qs and Ps mixed up

Luca Seemungal (Apr 19 2020 at 10:38):

yes ok i think i've got it

Q(m) = P(m) and P(m+1)

Luca Seemungal (Apr 19 2020 at 10:39):

Q(0) = P(0) and P(1) which are both true so Q(0) is true

if Q(m) is true, then P(m) and P(m+1) are true, but then we also have that P(m+2) is true, so we have that Q(m+1) is true

Kevin Buzzard (Apr 19 2020 at 10:40):

Right

Kevin Buzzard (Apr 19 2020 at 10:41):

So the first line of your proof is have Q : forall n, P n and P n+1,

Kevin Buzzard (Apr 19 2020 at 10:41):

And then { } because we're starting a new goal

Kevin Buzzard (Apr 19 2020 at 10:42):

And in the bracket intro m, induction m with d hd

Kevin Buzzard (Apr 19 2020 at 10:42):

And we're on the way

Luca Seemungal (Apr 19 2020 at 10:42):

cool! thanks for this!

Luca Seemungal (Apr 19 2020 at 10:49):

are there any style issues with this code?

theorem luca_induction (p :   Prop) (n : ) (h : p 0  p 1)
    (H :  n : , p n  p (n+1)  p (n+2)) : p n :=
begin
    have q : forall n, p (n)  p (n+1),
    {
        intro m,
        induction m with m hm,
        { simp, exact h, },
        {
            have h2 :=  H m hm.left hm.right,
            exact and.intro hm.right h2,
        }
    },
    induction n with n hn,
    { exact h.left, },
    { exact (q n).right, }
end

Kevin Buzzard (Apr 19 2020 at 10:51):

Do you need induction at the end?

Kevin Buzzard (Apr 19 2020 at 10:51):

I mean can't you just prove p n from q n?

Luca Seemungal (Apr 19 2020 at 10:52):

no, youre right, i dont. I just need exact (q n).left

Kevin Buzzard (Apr 19 2020 at 10:52):

You committed a cardinal sin in your base case, you used a non terminal simp

Kenny Lau (Apr 19 2020 at 10:52):

theorem luca_induction (p :   Prop) (n : ) (h : p 0  p 1)
  (H :  n : , p n  p (n+1)  p (n+2)) : p n :=
begin
  suffices q : p n  p (n+1),
  { exact q.left },
  induction n with n ih,
  { exact h },
  { exact ih.right, H n ih.left ih.right }
end

Kevin Buzzard (Apr 19 2020 at 10:52):

Kenny is sin free

Kevin Buzzard (Apr 19 2020 at 10:53):

He's also written the proof backwards

Kevin Buzzard (Apr 19 2020 at 10:53):

He deduced p from q first

Luca Seemungal (Apr 19 2020 at 10:53):

yes, i don't need that simp either

I just kind of assumed i needed to simp 0+1 to 1 or whatever

Kevin Buzzard (Apr 19 2020 at 10:53):

You know 0+1=1 is true by definition?

Kevin Buzzard (Apr 19 2020 at 10:54):

Try removing the simp completely

Luca Seemungal (Apr 19 2020 at 10:54):

what is a non terminal simp? I assume it's a simp that doesn't terminate, but how do you tell (without going into the halting problem) beforehand

Kenny Lau (Apr 19 2020 at 10:54):

terminal = final = at the end

Kenny Lau (Apr 19 2020 at 10:54):

if it didn't terminate your proof wouldn't have compiled

Luca Seemungal (Apr 19 2020 at 10:54):

ohhh right

Kevin Buzzard (Apr 19 2020 at 10:54):

You should never use simp to make a goal simpler

Kevin Buzzard (Apr 19 2020 at 10:55):

You should only use it to kill a goal completely

Kevin Buzzard (Apr 19 2020 at 10:55):

What happens if you remove the simp?

Luca Seemungal (Apr 19 2020 at 10:55):

ah ok

everything works without the simp

Kenny Lau (Apr 19 2020 at 10:56):

at this point we're getting into:

  1. definitional equality
  2. mechanism of simp

Luca Seemungal (Apr 19 2020 at 10:56):

Kevin Buzzard said:

You know 0+1=1 is true by definition?

I see, so lean will just know because it'll unfold the definitions or something?

Kenny Lau (Apr 19 2020 at 10:56):

I'm not sure if Luca is comfortable with discussing these two concepts

Luca Seemungal (Apr 19 2020 at 10:57):

haha probably not

Kevin Buzzard (Apr 19 2020 at 10:57):

In the natural number game there is zero_add and add_zero

Luca Seemungal (Apr 19 2020 at 10:57):

"equality is just equality, right?"

Kevin Buzzard (Apr 19 2020 at 10:57):

And one of them is true by definition

Kevin Buzzard (Apr 19 2020 at 10:57):

Because x+0 is defined to be x

Kevin Buzzard (Apr 19 2020 at 10:57):

But the other one is true because of a theorem

Luca Seemungal (Apr 19 2020 at 10:58):

ah yes i remember this now

Kevin Buzzard (Apr 19 2020 at 10:58):

Which is proved by induction on x

Luca Seemungal (Apr 19 2020 at 10:58):

but once you've proved the theorem, surely it doesn't matter whether its true by definition or by theorem, it's just true?

Kevin Buzzard (Apr 19 2020 at 10:58):

So 0+1 is by definition 0+succ(0)

Kevin Buzzard (Apr 19 2020 at 10:58):

Which is by definition succ(0+0)

Kevin Buzzard (Apr 19 2020 at 10:59):

Which is by definition succ (0)

Kevin Buzzard (Apr 19 2020 at 10:59):

Which is by definition 1

Kenny Lau (Apr 19 2020 at 10:59):

0+1 → nat.add 0 1 → nat.add 0 (nat.succ 0) → nat.succ (nat.add 0 0) → nat.succ 0
1 → nat.succ 0

Kevin Buzzard (Apr 19 2020 at 11:00):

The rw tactic really needs to see things being exactly equal, the same buttons on your keyboard equal, before it will work

Kevin Buzzard (Apr 19 2020 at 11:00):

But the exact tactic will work up to definitional equality

Kenny Lau (Apr 19 2020 at 11:00):

I can't get Lean to show me this (using set_option trace.type_context.is_def_eq_detail true) only tells me:

[type_context.is_def_eq_detail] [1]: p 0  p 1 =?= p 0  p (0 + 1)
[type_context.is_def_eq_detail] [2]: p 0 =?= p 0
[type_context.is_def_eq_detail] [3]: 0 =?= 0
[type_context.is_def_eq_detail] [2]: p 1 =?= p (0 + 1)
[type_context.is_def_eq_detail] [3]: 1 =?= 0 + 1
[type_context.is_def_eq_detail] process_assignment ?m_1 := h
[type_context.is_def_eq_detail] [1]: p 0  p (0 + 1) =?= p 0  p 1
[type_context.is_def_eq_detail] [2]: p 0 =?= p 0
[type_context.is_def_eq_detail] [3]: 0 =?= 0
[type_context.is_def_eq_detail] [2]: p (0 + 1) =?= p 1
[type_context.is_def_eq_detail] [3]: 0 + 1 =?= 1
[type_context.is_def_eq_detail] assign: ?m_1 := h

Kevin Buzzard (Apr 19 2020 at 11:00):

Ok lemme get to a computer

Kenny Lau (Apr 19 2020 at 11:01):

Kevin Buzzard said:

The rw tactic really needs to see things being exactly equal, the same buttons on your keyboard equal, before it will work

this is a rule with 37 exceptions

Kevin Buzzard (Apr 19 2020 at 11:02):

Ok so now let's use luca_induction_aux

Kenny Lau (Apr 19 2020 at 11:02):

@Luca Seemungal anyway this is how simp works: we have a database of "simp lemmas" (i.e. theorems we have tagged with @[simp]), and then simp tries to simplify the goal using each of them, until it reaches true

Luca Seemungal (Apr 19 2020 at 11:02):

ah I see

Kenny Lau (Apr 19 2020 at 11:02):

so the reason you shouldn't use non-terminal simp is because you can't predict the outcome of simp

Kenny Lau (Apr 19 2020 at 11:03):

because anyone can add simp lemmas

Kenny Lau (Apr 19 2020 at 11:03):

then your proof might not compile, because the rest of the proof after simp relied on the exact form of the goal after using simp

Kevin Buzzard (Apr 19 2020 at 11:04):

def fib :   
| 0 := 0
| 1 := 1
| (n+2) := fib (n+1) + fib(n)

lemma fib_zero : fib 0 = 0 := rfl

lemma fib_one : fib 1 = 1 := rfl

lemma fib_ss (n : ) : fib(n+2)=fib(n+1)+fib(n) := rfl

Kenny Lau (Apr 19 2020 at 11:04):

but I've always been advocating not to use simp at all, because it's an expensive tactic

Kenny Lau (Apr 19 2020 at 11:04):

i.e. it's slow

Kevin Buzzard (Apr 19 2020 at 11:04):

theorem fib_nonneg :  n : , 0  fib(n) :=
begin
  sorry
end

Kevin Buzzard (Apr 19 2020 at 11:05):

There's a theorem which you can't prove by usual induction.

Kevin Buzzard (Apr 19 2020 at 11:05):

If you try proving it by induction, then the inductive case is Fn0    Fn+10F_n\geq0\implies F_{n+1}\geq0

Kevin Buzzard (Apr 19 2020 at 11:05):

and you can't prove it because maybe Fn1<0F_{n-1}<0. You know in your heart that this is not going to be the case in practice, but induction is brutal and won't let you cheat like that

Kevin Buzzard (Apr 19 2020 at 11:07):

But we can prove it with luca_induction

Luca Seemungal (Apr 19 2020 at 11:07):

right yes, I see. The fibonacci numbers was something I had in mind when doing this double induction thing

Kevin Buzzard (Apr 19 2020 at 11:08):

So we start the proof of fib_nonneg with intro n

Kevin Buzzard (Apr 19 2020 at 11:08):

and then we want to apply luca_induction somehow, but we need to give it some inputs

Kevin Buzzard (Apr 19 2020 at 11:09):

Well, we could give it p, but given that we know that p n is supposed to be 0 <= fib(n) Lean will be able to work out p

Luca Seemungal (Apr 19 2020 at 11:09):

ah thats quite cool

Kevin Buzzard (Apr 19 2020 at 11:09):

but we will have to tell it that we're doing induction on n rather than some other random integer variable like n^2

Kevin Buzzard (Apr 19 2020 at 11:09):

so I'm hoping apply luca_induction _ n will work for the next line

Kevin Buzzard (Apr 19 2020 at 11:10):

and it does :-)

Kevin Buzzard (Apr 19 2020 at 11:10):

import tactic -- always handy

theorem luca_induction (p :   Prop) (n : ) (h : p 0  p 1)
    (H :  n : , p n  p (n+1)  p (n+2)) : p n :=
begin
    have q : forall n, p (n)  p (n+1),
    {
        intro m,
        induction m with m hm,
        { exact h, },
        {
            have h2 :=  H m hm.left hm.right,
            exact and.intro hm.right h2,
        }
    },
    induction n with n hn,
    { exact h.left, },
    { exact (q n).right, }
end

def fib :   
| 0 := 0
| 1 := 1
| (n+2) := fib (n+1) + fib(n)

lemma fib_zero : fib 0 = 0 := rfl

lemma fib_one : fib 1 = 1 := rfl

lemma fib_ss (n : ) : fib(n+2)=fib(n+1)+fib(n) := rfl

theorem fib_nonneg :  n : , 0  fib(n) :=
begin
  intro n,
  apply luca_induction _ n,
  { sorry},
  { sorry}
end

Luca Seemungal (Apr 19 2020 at 11:10):

don't you have to tell lean about fib_zero and fib_one?

Kevin Buzzard (Apr 19 2020 at 11:10):

Note new line 1

Luca Seemungal (Apr 19 2020 at 11:11):

obviously you don't i guess it figures it out

Luca Seemungal (Apr 19 2020 at 11:11):

yeah ok I see

Kevin Buzzard (Apr 19 2020 at 11:11):

When we apply your induction theorem, a bunch of new goals appear!

Kenny Lau (Apr 19 2020 at 11:11):

the correct tactic is induction n using luca_induction,

Kevin Buzzard (Apr 19 2020 at 11:12):

Your way might be the "correct" way but look at the mess it causes

Kevin Buzzard (Apr 19 2020 at 11:12):

case h
 (λ (n : ), 0  fib n) 0  (λ (n : ), 0  fib n) 1

case H
n_n : ,
n_a : 0  fib n_n,
n_a_1 : 0  fib (n_n + 1)
 0  fib (n_n + 2)

Luca Seemungal (Apr 19 2020 at 11:12):

do I always have to put the underscore before any variable that I specify myself?

Luca Seemungal (Apr 19 2020 at 11:12):

what's the rules regarding these underscores?

Kevin Buzzard (Apr 19 2020 at 11:12):

My way:

2 goals
n : 
 0  fib 0  0  fib 1

n : 
  (n : ), 0  fib n  0  fib (n + 1)  0  fib (n + 2)

Kevin Buzzard (Apr 19 2020 at 11:13):

underscore just means "I don't want to tell you this right now"

Kevin Buzzard (Apr 19 2020 at 11:13):

"either work it out for yourself or leave it to me as a new goal"

Luca Seemungal (Apr 19 2020 at 11:13):

ah ok

Kevin Buzzard (Apr 19 2020 at 11:13):

theorem fib_nonneg :  n : , 0  fib(n) :=
begin
  intro n,
  apply luca_induction _ n _ _,
end

Kenny Lau (Apr 19 2020 at 11:13):

@Mario Carneiro why does induction _ using _ create un-beta-reduced goals?

Kevin Buzzard (Apr 19 2020 at 11:14):

"I don't want to tell you p, and I don't want to tell you the proof of p0 and p1, and I don't want to tell you the proof of the inductive step"

Kenny Lau (Apr 19 2020 at 11:14):

refine is better than apply if you want to specify all the underscores anyway

Patrick Massot (Apr 19 2020 at 11:14):

This is a secret code. induction ... using ... is a coded way to tell Lean you want to suffer.

Kevin Buzzard (Apr 19 2020 at 11:15):

and Lean replies "well, for this apply tactic to work at all, I'd better make sure that p n equals 0 <= fib(n), so I can figure out that p must be "forall n, 0 <= fib(n)", but I can't figure out the proofs of the base case and the inductive step so I'll just leave them to you as new goals"

Kenny Lau (Apr 19 2020 at 11:15):

p is "fun n, 0 < fib n"

Kevin Buzzard (Apr 19 2020 at 11:16):

apply is a clever tactic, when we apply luca_induction _ n it says "hey wait a minute, luca_induction is a function with four inputs, I'll just add in two underscores"

Kevin Buzzard (Apr 19 2020 at 11:17):

refine luca_induction _ n _ _ also works, but refine luca_induction _ n doesn't, because refine is more fussy, it wants the exact number of holes

Luca Seemungal (Apr 19 2020 at 11:18):

i see

Kevin Buzzard (Apr 19 2020 at 11:18):

So now can you finish the proof?

Luca Seemungal (Apr 19 2020 at 11:18):

so when would one use refine instead of apply

Kevin Buzzard (Apr 19 2020 at 11:18):

when you run into the apply bug :-)

Kenny Lau (Apr 19 2020 at 11:18):

when you're me

Luca Seemungal (Apr 19 2020 at 11:18):

yes I can

Luca Seemungal (Apr 19 2020 at 11:19):

cheers!!

Luca Seemungal (Apr 19 2020 at 11:19):

hahahaah, I'll keep an eye out

Kevin Buzzard (Apr 19 2020 at 11:19):

So this is a general principle. If you want a fancier induction, you can typically make it yourself.

Kevin Buzzard (Apr 19 2020 at 11:19):

Shing and I made a new induction principle for multivariable polynomials just a couple of weeks ago

Kevin Buzzard (Apr 19 2020 at 11:20):

we wanted to prove something about partial differentiation on multivariable polynomials and we couldn't find the principle we wanted so we just proved a new one like this

Mario Carneiro (Apr 19 2020 at 11:20):

I never use induction using

Mario Carneiro (Apr 19 2020 at 11:20):

refine does more or less the same thing

Kenny Lau (Apr 19 2020 at 11:21):

then what's the point of induction using

Kenny Lau (Apr 19 2020 at 11:21):

I mean, induction doesn't give you un-beta-reduced goals

Mario Carneiro (Apr 19 2020 at 11:21):

:shrug:

Patrick Massot (Apr 19 2020 at 11:21):

It's meant to try harder to elaborate

Mario Carneiro (Apr 19 2020 at 11:22):

Note that you have to mark your custom induction principle with @[elab_as_eliminator] for unification to work properly

Kevin Buzzard (Apr 19 2020 at 11:22):

@Luca Seemungal for homework you can try and figure out how to start at n0n_0 :-)

Kenny Lau (Apr 19 2020 at 11:22):

I tried that, it still don't beta reduce

Kenny Lau (Apr 19 2020 at 11:23):

also it worked without that

Kevin Buzzard (Apr 19 2020 at 11:23):

You might have to get your hands dirty with natural number subtraction though :-(

Kenny Lau (Apr 19 2020 at 11:23):

breaking news: Mario doesn't understand Lean :P

Mario Carneiro (Apr 19 2020 at 11:23):

What did you try it on?

Kenny Lau (Apr 19 2020 at 11:24):

theorem luca_induction {p :   Prop} (n : ) (h : p 0  p 1)
  (H :  n : , p n  p (n+1)  p (n+2)) : p n :=
begin
  suffices q : p n  p (n+1),
  { exact q.left },
  induction n with n ih,
  { exact h },
  { exact ih.right, H n ih.left ih.right }
end

def fib :   
| 0 := 0
| 1 := 1
| (n+2) := fib (n+1) + fib(n)

lemma fib_zero : fib 0 = 0 := rfl

lemma fib_one : fib 1 = 1 := rfl

lemma fib_ss (n : ) : fib(n+2)=fib(n+1)+fib(n) := rfl

theorem fib_nonneg :  n : , 0  fib(n) :=
λ n, luca_induction n _ _

Kenny Lau (Apr 19 2020 at 11:24):

works perfectly

Kevin Buzzard (Apr 19 2020 at 11:24):

PS @Luca Seemungal I defined Fibonacci numbers using the equation compiler. As you can see, to prove that this even is a valid definition there must be somewhere some form of your induction principle going on in the background.

Kenny Lau (Apr 19 2020 at 11:24):

Kevin Buzzard said:

You might have to get your hands dirty with natural number subtraction though :-(

no you don't

Kevin Buzzard (Apr 19 2020 at 11:25):

The equation compiler is those funny |s in definitions. It's a very cool little tool for playing around with basic stuff like this and the CS people are sometimes super-excited about how clever it can be, but because you can only use it in a definition rather than in the middle of things it's often a bit limited in mathematics.

Luca Seemungal (Apr 19 2020 at 11:27):

is this an "inductive type" that I've seen in TPIL

Kenny Lau (Apr 19 2020 at 11:28):

an inductive type is what allows you to use the equation compiler

Kevin Buzzard (Apr 19 2020 at 11:28):

Here's a proof of fib_nonneg which uses the equation compiler:

theorem fib_nonneg :  n : , 0  fib(n)
| 0 := by {rw fib_zero}
| 1 := by {rw fib_one, linarith}
| (n+2) := by {rw fib_ss, have hn := fib_nonneg n, have hn2 := fib_nonneg (n+1), linarith}

Yes, fib is an inductive type.

Kevin Buzzard (Apr 19 2020 at 11:29):

Note that we do not need your inductive principle at all. It's somehow inbuilt into the equation compiler. As I say, it's cool but limited.

Kenny Lau (Apr 19 2020 at 11:30):

fib is not an inductive type

Kevin Buzzard (Apr 19 2020 at 11:30):

rotten luck

Kevin Buzzard (Apr 19 2020 at 11:31):

I still don't understand the basics

Kevin Buzzard (Apr 19 2020 at 11:31):

What I know is that you don't need to :-)

Kevin Buzzard (Apr 19 2020 at 11:31):

It's a function so it's a pi type I guess

Kevin Buzzard (Apr 19 2020 at 11:31):

nat is the inductive type

Kevin Buzzard (Apr 19 2020 at 11:31):

so that's why I can use the equation compiler

Kevin Buzzard (Apr 19 2020 at 11:32):

You can't prove that partial differentation is additive using the equation compiler because polynomials are not an inductive type. You have to make your own induction principles for them.

Kenny Lau (Apr 19 2020 at 11:33):

I tried to make polynomials an inductive type very early on in my Lean experience

Kenny Lau (Apr 19 2020 at 11:33):

it failed miserably

Kevin Buzzard (Apr 19 2020 at 11:33):

You will have problems with uniqueness

Kenny Lau (Apr 19 2020 at 11:34):

well you can require the first coefficient to not be zero

Kevin Buzzard (Apr 19 2020 at 11:34):

Although you can make them a quotient of an inductive type and then use quotient.induction but then you can't use the equation compiler because you're in the middle of something.

Kevin Buzzard (Apr 19 2020 at 11:35):

I see, you can define a polynomial to be either 0 or c*X^n+a polynomial with c non-zero

Kevin Buzzard (Apr 19 2020 at 11:35):

It would be hell to work with though, making the basic interface would be a real pain

Kenny Lau (Apr 19 2020 at 11:35):

exactly

Reid Barton (Apr 19 2020 at 11:35):

In HoTT you would be able to use the equation compiler for a quotient.

Mario Carneiro (Apr 19 2020 at 11:35):

Kenny Lau said:

works perfectly

Or does it?

theorem fib_nonneg (h1 : 0  fib 0  0  fib 1) :  n : , 0  fib(n) :=
λ n, luca_induction n h1 _

Kevin Buzzard (Apr 19 2020 at 11:36):

maximum class-instance resolution depth has been reached (the limit can be increased by setting option 'class.instance_max_depth') (the class-instance resolution trace can be visualized by setting option 'trace.class_instances')

Ashwin Iyengar (Apr 19 2020 at 15:43):

If I try

universe u

class discrete_valuation_ring (α : Type u) extends principal_ideal_domain α :=
(prime_ideal : ideal α)
(is_prime : prime_ideal.is_prime)
(unique_nonzero_prime_ideal :  P : ideal α, P.is_prime  P = 0  P = prime_ideal)

namespace discrete_valuation_ring

variable {α : Type u}
variables [discrete_valuation_ring α]

#check prime_ideal

lemma prime_ideal_is_maximal : prime_ideal.is_maximal :=
begin
sorry,
end

end discrete_valuation_ring

then I get

don't know how to synthesize placeholder
context:
 Type ?

I'm sure I'm misunderstanding something basic here. How can I write down the proposition which just states that in discrete_valuation_ring the ideal prime_ideal is maximal?

Kenny Lau (Apr 19 2020 at 15:46):

alpha cannot be inferred

Kenny Lau (Apr 19 2020 at 15:46):

how about (prime_ideal \a).is_maximal

Ashwin Iyengar (Apr 19 2020 at 15:48):

then I get

function expected at
  prime_ideal
term has type
  submodule ?m_1 ?m_1

and

invalid field notation, type is not of the form (C ...) where C is a constant
  
has type
  ?m_1

Ashwin Iyengar (Apr 19 2020 at 15:48):

the #check part actually works, it's the lemma where it fails

Kenny Lau (Apr 19 2020 at 15:55):

then (@prime_ideal \a).is_maximal

Ashwin Iyengar (Apr 19 2020 at 15:58):

hmm then i get

invalid field notation, type is not of the form (C ...) where C is a constant
  prime_ideal
has type
  Π [c : discrete_valuation_ring α], ideal α

Reid Barton (Apr 19 2020 at 15:59):

(prime_ideal : ideal \a).is_maximal?

Ashwin Iyengar (Apr 19 2020 at 16:02):

oh that works! do you know why? naively i would think that it shouldn't be necessary to specify that prime_ideal is an ideal in \alpha, but i must be misunderstanding something

Reid Barton (Apr 19 2020 at 16:10):

Well, it could be, like, \beta instead. Why not?

Reid Barton (Apr 19 2020 at 16:10):

I don't really know how to explain this.

Reid Barton (Apr 19 2020 at 16:13):

There's no reason for it to know you mean \alpha, so it doesn't know.

Ashwin Iyengar (Apr 19 2020 at 16:36):

I guess my confusion is that writing variable [discrete_valuation_ring \alpha] doesn't make lean automatically guess that prime_ideal should be an ideal in \alpha?

Reid Barton (Apr 19 2020 at 16:44):

No, instances don't work that way (this is a common confusion). It just means that Lean can use the fact that \alpha is a DVR if it needs it.
Imagine you had a second

variable {beta : Type u}
variables [discrete_valuation_ring beta]

and this should probably be clearer.

Ashwin Iyengar (Apr 19 2020 at 16:53):

ohh ok that makes more sense. thanks

Nam (Apr 19 2020 at 17:07):

Jasmin Blanchette said:

Nam your star syntax is a bit off. Try (hab : star r a b) and similarly for star r b c. I don't know why Lean forces you to specify names here.

Yes. I tried to formulate it again after reading your doc. I think I have a shorter and more syntactically consistent. I was going to send your a PR then I found out that you have not published the text yet (only PDF is availabe).

Nam (Apr 19 2020 at 17:09):

Kenny Lau said:

Nam separate definition from theorems

Thanks!

Aniruddh Agarwal (Apr 19 2020 at 17:20):

I'm trying to use the use tactic from the "natural numbers game", but it appears this tactic is not available in lean by default. What do I need to import to make it available?

Reid Barton (Apr 19 2020 at 17:31):

import tactic should do it

Reid Barton (Apr 19 2020 at 17:31):

It's part of mathlib

Jannis Limperg (Apr 19 2020 at 17:54):

@Aniruddh Agarwal To see all the mathlib tactics and which import incantation you need, check the index.

Aniruddh Agarwal (Apr 19 2020 at 21:46):

This really is a noob question, but how do I close a proof if my goal is "true"?

Jason KY. (Apr 19 2020 at 21:47):

trivial

Bryan Gin-ge Chen (Apr 19 2020 at 21:47):

example : true := trivial

Aniruddh Agarwal (Apr 19 2020 at 21:48):

Is it possible to just use these tactics to close true:

intro
apply (or, better, refine)
left, right, cases, split
assumption (or, better, exact)
have,
simp
contradiction (or, better, false.elim)

Bryan Gin-ge Chen (Apr 19 2020 at 21:48):

You can use exact trivial then.

Aniruddh Agarwal (Apr 19 2020 at 21:49):

Ah, is trivial both a tactic and a "term"?

Kevin Buzzard (Apr 19 2020 at 21:50):

Does simp not do it??

Kevin Buzzard (Apr 19 2020 at 21:50):

That looks like a fine list of tactics, by the way.

Aniruddh Agarwal (Apr 19 2020 at 21:50):

Nope, simp doesn't do it unfortunately

Kevin Buzzard (Apr 19 2020 at 22:00):

rofl

Kevin Buzzard (Apr 19 2020 at 22:00):

tauto! will presumably do it

Kevin Buzzard (Apr 19 2020 at 22:00):

It does all logic proofs

Kevin Buzzard (Apr 19 2020 at 22:00):

and finish will presumably do it too

Kevin Buzzard (Apr 19 2020 at 22:00):

and maybe cc

Kevin Buzzard (Apr 19 2020 at 22:01):

and it's always worth trying norm_num even though it wasn't designed to do it

Kevin Buzzard (Apr 19 2020 at 22:01):

because it does random things it wasn't designed to do

Reid Barton (Apr 19 2020 at 22:02):

I've had simp reduce my goal to true before, it's a bit puzzling that it can't finish the job from there

Kevin Buzzard (Apr 19 2020 at 22:13):

Maybe we should add true \iff (true \iff true) as a simp lemma :-)

Brandon B (Apr 20 2020 at 07:25):

I don't understand this example in TPIL

example :  x : , x > 0 :=
        have h : 1 > 0, from nat.zero_lt_succ 0,
        exists.intro 1 h

It's as if the proposition 1 > 0 is being applied to the argument 1 but this proposition doesn't have any variables

Patrick Stevens (Apr 20 2020 at 07:32):

Brandon B said:

I don't understand this example in TPIL

example :  x : , x > 0 :=
        have h : 1 > 0, from nat.zero_lt_succ 0,
        exists.intro 1 h

It's as if the proposition 1 > 0 is being applied to the argument 1 but this proposition doesn't have any variables

Which part has you applying 1 > 0 to the argument 1? Line 1 is a type signature of the example; line 2 is the manifestation of a term of type 1 > 0; line 3 is the construction of an object of type ∃ x : ℕ, x > 0 from the two pieces of data 1 and h that comprise an existential statement.

Brandon B (Apr 20 2020 at 07:33):

I guess i dont understand line 3. Why would it need both 1 and 1 > 0. Shouldn't 1 > 0 suffice?

Brandon B (Apr 20 2020 at 07:36):

Nevermind - reading on I think I get it now

Luca Seemungal (Apr 20 2020 at 08:40):

what tactic do I use to solve the following example? norm_num doesn't do it, and i've no idea what else to use and I don't feel like expanding the brackets etc. Do I have to expand the brackets etc?

example : (1 + real.sqrt 2) ^ 2 / 3 = (2*real.sqrt 2) / 3 + 1 :=
begin
    norm_num, -- norm_num failed to simplify
    sorry,
end

Shing Tak Lam (Apr 20 2020 at 08:42):

try ring? Then norm_num?

Luca Seemungal (Apr 20 2020 at 08:44):

hmmm
lean is taking a long time to check that; it hasn't finished after half a minute

Shing Tak Lam (Apr 20 2020 at 08:45):

Yeah, that doesn't seem to work. ring did expand the brackets though.

Luca Seemungal (Apr 20 2020 at 08:45):

yes it did

Mario Carneiro (Apr 20 2020 at 08:50):

norm_num doesn't know about real.sqrt. If you view it as an atom x, then you will have to give it a hint that x^2 = 2 (as well as the hint of how to use that to finish the proof)

Johan Commelin (Apr 20 2020 at 08:52):

But simp does know about real.sqrt. Does that help?

Mario Carneiro (Apr 20 2020 at 08:57):

example : (1 + real.sqrt 2) ^ 2 / 3 = (2*real.sqrt 2) / 3 + 1 :=
calc  (1 + real.sqrt 2) ^ 2 / 3
    = (1 + 2 * real.sqrt 2 + real.sqrt 2 ^ 2) / 3 : by ring
... = (2*real.sqrt 2) / 3 + 1 : by rw real.sqr_sqrt; [ring, norm_num]

Luca Seemungal (Apr 20 2020 at 08:57):

example : (1 + real.sqrt 2) ^ 2 / 3 = (2*real.sqrt 2) / 3 + 1 :=
begin
    ring, -- ⊢ (1 / 3 * real.sqrt 2 + 2 / 3) * real.sqrt 2 + 1 / 3 = 2 / 3 * real.sqrt 2 + 1
    simp, -- ⊢ (3⁻¹ * real.sqrt 2 + 2 / 3) * real.sqrt 2 + 3⁻¹ = 2 / 3 * real.sqrt 2 + 1
    sorry
end

Luca Seemungal (Apr 20 2020 at 08:58):

simp doesn't solve the goal, and I've been told not to use a non terminal simp

Mario Carneiro (Apr 20 2020 at 08:58):

actually simp doesn't really do much of anything on that goal

Luca Seemungal (Apr 20 2020 at 08:58):

oh, cool

cheers!

Mario Carneiro (Apr 20 2020 at 08:59):

the antidote to non-terminal simp is saying what you want your intermediate goal to look like

Shing Tak Lam (Apr 20 2020 at 09:00):

Johan Commelin said:

But simp does know about real.sqrt. Does that help?

There are a few simp lemmas, but this doesn't work...

example : sqrt 2 * sqrt 2 = 2 :=
begin
  simp, -- fails
end

Marc Huisinga (Apr 20 2020 at 09:00):

you can also use simp only, but i think stating the intermediate goal is much nicer

Mario Carneiro (Apr 20 2020 at 09:00):

there is a side condition, isn't there? There was in the theorem I used

Mario Carneiro (Apr 20 2020 at 09:01):

example : real.sqrt 2 * real.sqrt 2 = 2 :=
by simp [show (0:)  2, by norm_num]

Shing Tak Lam (Apr 20 2020 at 09:02):

Ah. So that's how you provide that condition.

Mario Carneiro (Apr 20 2020 at 09:02):

you could also have it as a hypothesis

Luca Seemungal (Apr 20 2020 at 09:03):

i managed to solve it this way:

example : (1 + real.sqrt 2) ^ 2 / 3 = (2*real.sqrt 2) / 3 + 1 :=
begin
    ring,
    rw add_mul,
    have h2 := @real.sqrt_mul' 2 2 (by norm_num),
    rw mul_assoc,
    rw h2,
    have h3 := @real.sqrt_mul_self 2 (by norm_num),
    rw h3,
    ring,
end

but presumably Mario's way is better

Mario Carneiro (Apr 20 2020 at 09:04):

It's hard to say which approach I would use when golfing. Writing the type is often more verbose than tactic based proofs

Mario Carneiro (Apr 20 2020 at 09:05):

I don't usually use non-terminal ring either, although it does leave the goal in a fairly clean state

Mario Carneiro (Apr 20 2020 at 09:05):

In this case ring SOP puts you in a much better state

Mario Carneiro (Apr 20 2020 at 09:06):

example : (1 + real.sqrt 2) ^ 2 / 3 = (2*real.sqrt 2) / 3 + 1 :=
by ring SOP; rw real.sqr_sqrt; [ring, norm_num]

Johan Commelin (Apr 20 2020 at 09:13):

Hmmm... I think there is room for a little hammer here

Mario Carneiro (Apr 20 2020 at 09:13):

ring_with_sqrt?

Mario Carneiro (Apr 20 2020 at 09:14):

I mean it all fits into the Grobner basis tactic of the future, but I'm in no hurry to write it

Johan Commelin (Apr 20 2020 at 09:14):

https://en.wikipedia.org/wiki/Uchide_no_kozuchi

Mario Carneiro (Apr 20 2020 at 09:16):

Notice that this is a proof in rings modulo equalities, that is, x^2 = 2 -> (1 + x) ^ 2 / 3 = (2*x) / 3 + 1

Brandon B (Apr 20 2020 at 11:34):

This is really embarrassing but how do I prove some even number is even?

def is_even (a : ) :=  b, a = 2 * b
example : is_even 6 := _

Mario Carneiro (Apr 20 2020 at 11:35):

\<3, rfl\> should work

Brandon B (Apr 20 2020 at 11:35):

Indeed it does., thanks!

AMM (Apr 20 2020 at 12:52):

Hey! sorry, I was just trying what I thought was going to be a trivial example and I can't seem to make lean like my answer:

def axiom_5: ( x : α , (B x  C x))  (B y  ( y, C y)) :=
begin
intros h1 h2 y,
apply h1 y,
/- Tactic State
1 goal
α : Type,
B C : α → Prop,
y : α,
h1 : ∀ (x : α), B x → C x,
h2 : B y,
y : α
⊢ B y-/
assumption,
/- getting error:
assumption tactic failed
state:
α : Type,
B C : α → Prop,
y : α,
h1 : ∀ (x : α), B x → C x,
h2 : B y,
y : α
⊢ B y

If I do 'exact h2' I get:
invalid type ascription, term has type
  B y
but is expected to have type
  B y
types contain aliased name(s): y
remark: the tactic `dedup` can be used to rename aliases
state:
α : Type,
B C : α → Prop,
y : α,
h1 : ∀ (x : α), B x → C x,
h2 : B y,
y : α
⊢ B y
but then dedup doesn't seem to help progressing the proof very much
-/
end

Mario Carneiro (Apr 20 2020 at 12:53):

the theorem is false

Mario Carneiro (Apr 20 2020 at 12:54):

you have two variables named y (you can see them in your context) and this is getting you confused

Mario Carneiro (Apr 20 2020 at 12:55):

To make it clearer, your theorem is the same as

def axiom_5: ( x : α , (B x  C x))  (B y  ( z, C z)) := sorry

which should be more obviously false

AMM (Apr 20 2020 at 12:56):

I see my bad, that makes sense actually, I need B to not have free occurrences of the variable

Mario Carneiro (Apr 20 2020 at 12:56):

In which case you can write B instead of B y

AMM (Apr 20 2020 at 12:57):

right, but then " type expected at
B
term has type
α → Prop"
and it's used with B x before

Mario Carneiro (Apr 20 2020 at 12:59):

make it B there too and change the type of B to Prop

AMM (Apr 20 2020 at 12:59):

I think actually what I want is $(∀ x_i)(B → C) → (B → (∀ x_i)C)$ if $B$ has no free occurrences of $x_i$, so not what I wrote probably

AMM (Apr 20 2020 at 12:59):

that makes sense

Mario Carneiro (Apr 20 2020 at 13:00):

this amounts to exactly your not free condition since you can substitute any expression for B but it can't mention x because it is out of scope

AMM (Apr 20 2020 at 13:01):

I'd just used (∀ x_i)B(x_i) so forgot to get rid of variables and make a new section for this one

Mario Carneiro (Apr 20 2020 at 13:01):

you could also use a different letter if you don't want to change your variables

AMM (Apr 20 2020 at 13:03):

ofc, that would be good too
all good with

def axiom_5: ( x : α , B  C )  (B  ( x : α , C)) :=

AMM (Apr 20 2020 at 13:03):

stuff works as expected, thank you mario

Mario Carneiro (Apr 20 2020 at 13:52):

@AMM Actually, that's gone too far the other way, now. Because C doesn't depend on x, the binder over x is vacuous; that is, C is also being constrained to not mention x just as B is. As a result, this theorem is true but less applicable than it should be. You want (∀ x : α , B → C x) → (B → (∀ x : α , C x)) here

AMM (Apr 20 2020 at 13:58):

right ofc, i have fixed that now

def axiom_5: ( x : α , B  C x)  (B  ( x : α , C x)) :=
begin
intros h1 h2 y,
apply h1 y,
assumption,
end

Kevin Buzzard (Apr 20 2020 at 15:27):

import tactic

variables (α : Type) (B : Prop) (C : α  Prop)

def axiom_5: ( x : α , B  C x)  (B  ( x : α , C x)) :=
begin
  finish,
end

Kevin Buzzard (Apr 20 2020 at 15:28):

@Brandon B

import tactic

def is_even (a : ) :=  b, a = 2 * b
example : is_even 6 :=
begin
  unfold is_even,
  -- ⊢ ∃ (b : ℕ), 6 = 2 * b
  use 3,
  -- ⊢ 6 = 2 * 3
  norm_num
end

Ashwin Iyengar (Apr 20 2020 at 15:48):

Hi, can someone please let me know what I'm doing wrong here? I just want to say that an element of a ring is contained in the ideal generated by it

import ring_theory.ideals

universe u

variables {R : Type u} [nonzero_comm_ring R]

example (π : R) : Prop :=
begin
  have h : {π}  ideal.span {π} := ideal.subset_span {π},
end

Johan Commelin (Apr 20 2020 at 15:52):

have h : \pi \in ideal.span {\pi}, should also work...

Johan Commelin (Apr 20 2020 at 15:52):

@Ashwin Iyengar could you be more explicit about what you want to see? Do you not like the statement, or is there something else?

Kenny Lau (Apr 20 2020 at 15:55):

import ring_theory.ideals

universe u

variables {R : Type u} [nonzero_comm_ring R]

example (π : R) : true :=
begin
  have h : ({π} : set R)  (ideal.span {π}) := ideal.subset_span,
end
  1. the argument to ideal.subset_span is implicit, so it should not be supplied
  2. always specify that {π} is a set
  3. ideal.span {π} is an ideal, so you need to coerce it to a set

Ashwin Iyengar (Apr 20 2020 at 16:01):

ahh ok great thanks.

orlando (Apr 20 2020 at 16:43):

@Kevin Buzzard shorter :
def axiom_5: (∀ x : α , B → C x) → (B → (∀ x : α , C x)) := λ φ η , λ ζ , φ ζ η

Sebastien Gouezel (Apr 20 2020 at 17:26):

Kevin could have written

def axiom_5: ( x : α , B  C x)  (B  ( x : α , C x)) := by finish

or even

def axiom_5: ( x : α , B  C x)  (B  ( x : α , C x)) := by tauto

but he chose the longer form with begin ... end just for pedagogical reasons.

Kevin Buzzard (Apr 20 2020 at 17:29):

def axiom_5: (∀ x : α , B → C x) → (B → (∀ x : α , C x)) :=λφ η ζ,φ ζ η

Kevin Buzzard (Apr 20 2020 at 17:30):

def axiom_5: (∀ x : α , B → C x) → (B → (∀ x : α , C x)) :=forall_swap.1

Kevin Buzzard (Apr 20 2020 at 17:31):

variables (α: Type) (B : Prop) (C : α  Prop)

open function

def axiom_5: ( x : α , B  C x)  (B  ( x : α , C x)) := swap

Sebastien Gouezel (Apr 20 2020 at 17:32):

Hey, you opened function, this is cheating.

Kevin Buzzard (Apr 20 2020 at 17:33):

aah but I didn't import tactic

Sebastien Gouezel (Apr 20 2020 at 17:38):

Importing tactic is not cheating :)

Kevin Buzzard (Apr 20 2020 at 18:03):

Indeed, it should be made mandatory!

Nam (Apr 20 2020 at 22:18):

what is this error induction tactic failed, recursor 'sorted.dcases_on' can only eliminate into Prop about?

Kevin Buzzard (Apr 20 2020 at 22:18):

It probably means that you were in tactic mode, your goal was T with T : Type rather than P with P : Prop, and then you tried the cases tactic.

Kevin Buzzard (Apr 20 2020 at 22:19):

Maybe you were trying to get a : A from some statement of the form exists a : A (you weren't)

Nam (Apr 20 2020 at 22:19):

inductive sorted : list  -> Prop
| nil {} : sorted []
| one {x : } : sorted [x]
| other {x y : } {tail : list } (h: x <= y) (hs : sorted (y :: tail)) : sorted (x :: y :: tail)

lemma what_is_this {l} (h : sorted l) : Prop :=
begin
  cases h,
end

Kevin Buzzard (Apr 20 2020 at 22:20):

Your goal is Prop and Prop : Type

Kevin Buzzard (Apr 20 2020 at 22:20):

You can solve your goal with exact 2 + 2 = 37

Kevin Buzzard (Apr 20 2020 at 22:21):

You probably want your goal to be P : Prop for some proposition P

Kevin Buzzard (Apr 20 2020 at 22:21):

sorted.rec :
   {C : list   Prop},
    C list.nil 
    ( {x : }, C [x]) 
    ( {x y : } {tail : list }, x  y  sorted (y :: tail)  C (y :: tail)  C (x :: y :: tail)) 
     {a : list }, sorted a  C a

Kevin Buzzard (Apr 20 2020 at 22:22):

That's the recursor which Lean has generated for sorted, and the motive C is a map list nat -> Prop so it will only work if your goal has type Prop. You have (probably unintentionally) made your goal equal to Prop, so in particular it has type Type

Nam (Apr 20 2020 at 22:24):

if i change the declaration of the lemma to lemma what_is_this {l} (h : sorted l) : sorted l :=, then i can cases h.

Kevin Buzzard (Apr 20 2020 at 22:24):

That's because now your goal is sorted l and sorted l : Prop

Kevin Buzzard (Apr 20 2020 at 22:25):

cases will work if the type of your goal is Prop. If you are in tactic mode, the type of your goal should always be Prop.

Kevin Buzzard (Apr 20 2020 at 22:25):

Tactic mode is for proving theorems, and theorem statements are terms of type Prop.

Nam (Apr 20 2020 at 22:26):

what is the equivalence in a def?

Kevin Buzzard (Apr 20 2020 at 22:26):

I don't understand the question.

Kevin Buzzard (Apr 20 2020 at 22:26):

Oh -- you mean the equivalent story?

Nam (Apr 20 2020 at 22:27):

i mean the equivalent "statement(?)"

Kevin Buzzard (Apr 20 2020 at 22:27):

With a def, you are usually either making T with T : Type or t with t : T

Kevin Buzzard (Apr 20 2020 at 22:27):

and you almost never do the entire thing in tactic mode

Kevin Buzzard (Apr 20 2020 at 22:27):

Sometimes, making a definition involves doing some proofs along the way, and it's fine to go into tactic mode then

Nam (Apr 20 2020 at 22:28):

so how do you "cases" a hypothesis, or to extract the terms from that hypothesis?

Kevin Buzzard (Apr 20 2020 at 22:28):

This question is too vague to admit an answer

Kevin Buzzard (Apr 20 2020 at 22:29):

A lot will depend on the form of the recursor for the head term of the hypothesis

Nam (Apr 20 2020 at 22:29):

say, the signature def what_is_this {l} (h : sorted l) : N, how do i yield values depending on what form h is?

Kevin Buzzard (Apr 20 2020 at 22:29):

If you are in tactic mode, then cases is almost always the right answer.

Nam (Apr 20 2020 at 22:29):

oh, i think "match ... with" is what i might be looking for. is it right?

Kevin Buzzard (Apr 20 2020 at 22:30):

What is N? I think I am going to need a more precise question before I can help you.

Kevin Buzzard (Apr 20 2020 at 22:30):

h is a proof so it can't really help you with making a definition. It can only help you making other proofs.

Nam (Apr 20 2020 at 22:30):

i don't know how to type the math font for natural numbers

Kevin Buzzard (Apr 20 2020 at 22:30):

Oh, just call them nat, that's their ASCII name

Kevin Buzzard (Apr 20 2020 at 22:31):

How is the fact that you know a theorem about l going to help you define a natural number?

Nam (Apr 20 2020 at 22:31):

i was thinking of enums

Kevin Buzzard (Apr 20 2020 at 22:31):

I don't know what they are.

Nam (Apr 20 2020 at 22:31):

converting sorted.nil / sorted.one / sorted.other into 0, 1 or 2

Kevin Buzzard (Apr 20 2020 at 22:32):

But that can just be read off from the length of l

Nam (Apr 20 2020 at 22:32):

that's true.

Nam (Apr 20 2020 at 22:32):

but it won't be as natural

Kevin Buzzard (Apr 20 2020 at 22:33):

Lean has a proof-irrelevant Prop. This means that h has no idea how it was proved.

Kevin Buzzard (Apr 20 2020 at 22:33):

I might be wrong about this, I don't know much about all this core logic stuff.

Nam (Apr 20 2020 at 22:34):

how does cases work though? it knows how h was constructed.

Kevin Buzzard (Apr 20 2020 at 22:34):

No it doesn't, it just splits into cases: "if h was constructed in this way, do this, if it was constucted in this other way, do that,..."

Kevin Buzzard (Apr 20 2020 at 22:35):

and you can only do that whilst in the middle of another proof, because your recursor is only eliminating into Prop.

Kevin Buzzard (Apr 20 2020 at 22:35):

I have no idea whether you can make a recursor which eliminates into Type

Nam (Apr 20 2020 at 22:35):

that's exactly what i want. if h was constructed in this way, return 0, etc.

Kevin Buzzard (Apr 20 2020 at 22:35):

For all I know, this sort of functionality would make Lean inconsistent.

Reid Barton (Apr 20 2020 at 22:36):

Yes, it would

Kevin Buzzard (Apr 20 2020 at 22:36):

It might be what you want, but it's a fact that Lean has a proof-irrelevant Prop.

Kevin Buzzard (Apr 20 2020 at 22:38):

In general this sort of thing could be quite problematic. For example if you had a proof h of A or B, and you wanted to define a function which was 1 if the thing you actually proved was A, and was 2 if the thing you actually proved was B, then imagine what happens if you have proofs of both?

Kevin Buzzard (Apr 20 2020 at 22:39):

Any two proofs of A or B are equal by definition. Lean cannot tell or.inl hA and or.inr hB apart, they are both represented as a little tick by the statement A or B, representing the fact that it is proved.

Kevin Buzzard (Apr 20 2020 at 22:39):

And that's why or only eliminates to Prop.

Nam (Apr 20 2020 at 22:40):

i see. thanks for all the explanations!

Reid Barton (Apr 20 2020 at 22:40):

Since you are already thinking about programming: A Prop is not represented by any data at runtime. That's why you can't do case analysis on h : p if p is a Prop in order to construct data, because the data you would need to decide what to do does not exist. You can do case analysis on h to prove another Prop though, since that doesn't require constructing any data.

Reid Barton (Apr 20 2020 at 22:40):

And the reason that h is not represented by data is that any two proofs of a Prop are equal anyways, so there is no sense in it.

Kevin Buzzard (Apr 20 2020 at 22:41):

My understanding is that other proof systems do not have this spectacularly good idea inbuilt into them, because some people who do type theory are uncomfortable with this set-up.

Reid Barton (Apr 20 2020 at 22:41):

If you don't want this behavior, no problem: don't use Prop.

Kevin Buzzard (Apr 20 2020 at 22:41):

So then they have the joy of being able to worry about whether two proofs of something are equal, a concept which basically makes no sense to most mathematicians.

Kevin Buzzard (Apr 20 2020 at 22:44):

You could redefine sorted to be some kind of a data-carrying witness of the fact that your list is sorted. If your list is long then the data-carrying witness might be quite large.

Nam (Apr 20 2020 at 22:44):

got it.

Kevin Buzzard (Apr 20 2020 at 22:44):

Perhaps I should not say any more though because this really is at the edge of my understanding; I am not entirely clear on how this would work.

Kevin Buzzard (Apr 20 2020 at 22:46):

Oh it might be as easy as

inductive sorted : list  -> Type
| nil {} : sorted []
| one {x : } : sorted [x]
| other {x y : } {tail : list } (h: x <= y) (hs : sorted (y :: tail)) : sorted (x :: y :: tail)

Kevin Buzzard (Apr 20 2020 at 22:47):

Now sorted l has type Type, the recursor (#check @sorted.rec) can be checked to have a motive C : Π (a : list ℕ), sorted a → Sort u_1 which is what you want, and the cases command will probably work.

Nam (Apr 20 2020 at 22:48):

it does seem to work.

Kevin Buzzard (Apr 20 2020 at 22:48):

But now later on if you have two terms a and b of type sorted l you will have to work to prove that they are equal.

Kevin Buzzard (Apr 20 2020 at 22:49):

You can recover your Prop-valued function by defining def is_sorted (l : list ℕ) := nonempty (sorted l)

Kevin Buzzard (Apr 20 2020 at 22:51):

Because in your case the constructors work in disjoint cases (one only works when the list is empty, one only works when it has size 1 and the last one only works if the size is 2 or more) you should be able to prove subsingleton (sorted l), i.e. any two terms of type sorted l are equal.

Kevin Buzzard (Apr 20 2020 at 22:51):

(NB Sort 0 = Prop, Sort 1 = Type, Sort 2 = Type 1 etc)

Nam (Apr 20 2020 at 22:53):

these are pretty advanced tips ;). i think i get them, but not confident enough to explain them back to someone else. hah.

Kevin Buzzard (Apr 20 2020 at 22:53):

But this is a design decision which will come with consequences later; some of Lean's unification algorithms will not know that sorted l is a subsingleton and you might start getting weird errors. It also seems a bit strange to want to carry all this data around when you can instantly reconstruct it from the length of the list.

Nam (Apr 20 2020 at 22:54):

i don't have any specific need. i was just playing with the syntax to familiarize myself.

Nam (Apr 20 2020 at 22:54):

there are still so much to learn.

Kevin Buzzard (Apr 20 2020 at 22:55):

With the Type-valued sorted l all you have is another data structure, of the same size as l, which basically tells you nothing more than the fact that you checked a bunch of inequalities and they were all true, and it took time O(n) to do it, with n=length(l). With the Prop-valued sorted l you just have a tick in a box saying "I checked this, and it was fine". It costs you nothing extra and is free to carry around.

ROCKY KAMEN-RUBIO (Apr 21 2020 at 01:00):

ROCKY KAMEN-RUBIO said:

Bryan Gin-ge Chen said:

Oh, I see. Your file wasn't relying on mathlib at all before. So instead of just removing the inits from the imports, I would just delete the import data.nat.basic and import data.list.basic entirely.

If you do want to use stuff from mathlib, then you'll need to fix the path issue, and the best way to do that is to look at the install docs again, specifically the "project" doc if you've gotten leanproject working.

This seems to be what my problem was - must have built my current project workspace incorrectly. I follow the directions to make a new workspace and now import data.nat.basic doesn't error anymore. Thank you!

UPDATE: I suspect my issue was that I wasn't re-opening my entire workspace folder when I was opening new VSCode windows. This would explain why I had lost the mathlib dependency despite having installed mathlib when I created my workspace. Making a new workspace did this for me in my new VSCode window.

Nam (Apr 21 2020 at 04:36):

what is the syntax to return 2 lists, with some properties? e.g. def split (input : list nat) -> {(prefix suffix : list nat) // prefix ++ suffix = input} (this obviously doesn't work)

Mario Carneiro (Apr 21 2020 at 04:36):

You could return a pair, or you could use \Sigma prefix, {suffix // P prefix suffix}

Mario Carneiro (Apr 21 2020 at 04:38):

with a pair it would be something like {p : list nat \times list nat // p.1 ++ p.2 = input}

Nam (Apr 21 2020 at 04:38):

i also tried {(pre : list ℕ) × (suf : list ℕ) // pre++suf = input}

Nam (Apr 21 2020 at 04:38):

ahh, i see.

Nam (Apr 21 2020 at 04:38):

my understanding of pair is completely off.

Nam (Apr 21 2020 at 04:39):

thanks Mario

Mario Carneiro (Apr 21 2020 at 04:40):

If you don't like the asymmetry of the first version, you can also use \Sigma' which is a generalization of \Sigma and {x // p x}, as in \Sigma' prefix suffix, prefix ++ suffix = input

Kevin Buzzard (Apr 21 2020 at 08:24):

Nam said:

i also tried {(pre : list ℕ) × (suf : list ℕ) // pre++suf = input}

× is for putting the types together, not the terms.

Anas Himmi (Apr 21 2020 at 18:05):

in data/set.lean there is this code:

protected def subset (s₁ s₂ : set α) :=
     a⦄, a  s₁  a  s₂

what does ⦃⦄ mean?

Bryan Gin-ge Chen (Apr 21 2020 at 18:09):

I forget what these are officially called, but see https://leanprover.github.io/theorem_proving_in_lean/interacting_with_lean.html#more-on-implicit-arguments

Anas Himmi (Apr 21 2020 at 18:10):

thank you!

Nam (Apr 21 2020 at 23:02):

@Kenny Lau insertion sort with design contract at the insert helper.

inductive sorted : list  -> Prop
| nil {} : sorted []
| one {x : } : sorted [x]
| other {x y : } {tail : list } (h_i: x <= y) (hs : sorted (y :: tail)) : sorted (x :: y :: tail)

lemma tail_of_sorted {hd tl} (h_i : sorted (hd::tl)) : sorted tl :=
by {cases h_i, constructor, assumption}

lemma aux {head n : } {pre suf : list }
    (h1 : sorted (head::(pre++suf))) (h2 : sorted (pre++n::suf)) (h3 : head <= n)
    : sorted (head::pre++n::suf) :=
begin
  cases pre,
  case list.nil { apply sorted.other h3 h2 },
  case list.cons {
    cases h1 with _ _ _ _ head_lte_pre_hd,
    exact sorted.other head_lte_pre_hd h2,
  },
end

def insert (n : ) :
    Π (i : list ), sorted i  {p : list  × list  // p.1 ++ p.2 = i /\ sorted (p.1 ++ n :: p.2)}
| [] h_i := ([], []), begin split, refl, apply sorted.one end
| (head::tail) h_i :=
  if h_n : n  head then
    ([], head::tail),
      begin
        split, refl, apply sorted.other h_n h_i
      end
  else
    let o_rec := insert tail (tail_of_sorted h_i) in
    ([head] ++ o_rec.1.1, o_rec.1.2),
      begin
        split,
        {simp, apply o_rec.2.1},
        {
          simp,
          exact aux (by {rewrite  o_rec.2.1 at h_i, assumption}) o_rec.2.2 (le_of_not_le h_n)}
      end

def sort : list  -> {o : list  // sorted o}
| [] := [], sorted.nil
| [x] := [x], sorted.one
| (head::tail) :=
  let sorted_tail := sort tail in
    let pre_suf := insert head sorted_tail.val sorted_tail.property in
      pre_suf.val.1 ++ head :: pre_suf.val.2, pre_suf.property.2

#eval (sort [3, 4, 1, 5, 2, 1]).1 -- [1, 1, 2, 3, 4, 5]

Nam (Apr 21 2020 at 23:12):

reviews are much appreciated ;)

Kevin Buzzard (Apr 21 2020 at 23:18):

Well done :-)

Nam (Apr 21 2020 at 23:23):

double the amount of code than the other approach. i don't understand why though. i suspect the other approach could cases the insert function, while this approach cannot because it is being defined.

Nam (Apr 21 2020 at 23:25):

(i.e. it cannot introspect its own incomplete definition)

Frank Dai (Apr 22 2020 at 10:07):

Hello, I have a hypothesis of the form n : \N and h : n < 0. What's a way I can extract false from this?

Mario Carneiro (Apr 22 2020 at 10:08):

nat.not_lt_zero _ h

Kenny Lau (Apr 22 2020 at 10:09):

cases h

Shing Tak Lam (Apr 22 2020 at 12:03):

Is there a way to rename the hypothesis introduced by interval_cases? When I use it it creates a hypothesis called h_1 and I'd like to name it something else.

Kevin Buzzard (Apr 22 2020 at 12:06):

Maybe now's the time to learn some basic metaprogramming Shing :-)

Shing Tak Lam (Apr 22 2020 at 12:13):

Perhaps, digging into it I think I know where the automatically generated name is coming from, which is a call to note_anon. Since I had a hypothesis h already, it uses h_1. So I don't think I can do much without significant changes :(

and the way that renaming hypothesis is done for other tactics (with ...) means changing the parser as well. So I definitely don't know enough. My knowledge of parsers ends at the very basic BNF from A Level CS.

Shing Tak Lam (Apr 22 2020 at 12:14):

I guess rename exists if I cared enough.

Reid Barton (Apr 22 2020 at 12:33):

You don't need to do any parser implementation. The allowed syntax for a tactic is encoded into its type in a neat way.

Shing Tak Lam (Apr 22 2020 at 12:42):

I see. Looking at it now it does seem to be that way. I would have to figure out how parse worked. It does seem fairly straightforward (at least for something basic) from what I can tell, and there are plenty of other tactics that I could take a look at. Although I don't have much time right now to dig into this further at the moment.

AMM (Apr 22 2020 at 14:42):

how do I set up a proof in lean that takes (first assumes) Σ∪{α} proves β (with a sorry maybe), then show Σ proves (α→β) (as a second proof)? (yes, proving the deduction theorem).

Kenny Lau (Apr 22 2020 at 14:47):

first define "proves"

Mario Carneiro (Apr 22 2020 at 14:51):

\lam, or intro

Mario Carneiro (Apr 22 2020 at 14:54):

example {Sigma α β} (s : Sigma) : α  β :=
begin
  intro a,
  -- |- β
  sorry
end

AMM (Apr 22 2020 at 14:55):

Kenny Lau said:

first define "proves"

Just the usual first order def that it's the formula that is either an axiom or is obtained from applying rules of inference to previous formulae

Kenny Lau (Apr 22 2020 at 14:56):

define it in Lean

Kenny Lau (Apr 22 2020 at 14:56):

not define it here

AMM (Apr 22 2020 at 14:56):

haha yes okay

AMM (Apr 22 2020 at 14:56):

Mario Carneiro said:

\lam, or intro

okay cool!

AMM (Apr 22 2020 at 14:57):

what does {Sigma α β} notation mean sorry? and then s is of type sigma?

Mario Carneiro (Apr 22 2020 at 14:59):

I assume by Sigma you mean you have some additional assumptions. For the example it doesn't really matter what they are

Mario Carneiro (Apr 22 2020 at 14:59):

{Sigma α β} is the same as {Sigma : Sort*} {α : Sort*} {β : Sort*}, it's a bunch of type variables

Kenny Lau (Apr 22 2020 at 15:02):

@Mario Carneiro I don't think that's what they mean...

Kenny Lau (Apr 22 2020 at 15:03):

@AMM are you trying to formalize the theory of first order logic?

Reid Barton (Apr 22 2020 at 15:03):

I think it probably is what they mean but it's worth being explicit about what we are doing here.

AMM (Apr 22 2020 at 15:11):

Mario Carneiro said:

I assume by Sigma you mean you have some additional assumptions. For the example it doesn't really matter what they are

yes, a set of formulae (assumptions)

AMM (Apr 22 2020 at 15:12):

Mario Carneiro said:

{Sigma α β} is the same as {Sigma : Sort*} {α : Sort*} {β : Sort*}, it's a bunch of type variables

okay that's useful to know

AMM (Apr 22 2020 at 15:14):

Kenny Lau said:

AMM are you trying to formalize the theory of first order logic?

I was wanting, as a smaller exercise, to give a brief lean argument as to why the deduction theorem holds, and yes this would be in that context of first order logic.

[Im sure mathlib already has this (any pointers to finding the deduction theorem in mathlib?), and if not other project have for sure used all these first order results, like for flypitch]

AMM (Apr 22 2020 at 15:32):

Feels appropriate to be posting this in the noob thread because I'm sure the syntax is horrible, hope I'm not making people lose their patience. How do I fix my definition that I could later use it on the part:

example {Sigma α β} (s : Sigma) : α  β :=

definition:

def proof_of_β {Sigma α β} (s: Sigma) : β :=
--something that captures s has type sigma union alpha not just sigma [probably not possible in type theory?]
sorry

Reid Barton (Apr 22 2020 at 15:36):

mathlib doesn't have a formalization of first-order logic, so it doesn't have the deduction theorem.

Matt Earnshaw (Apr 22 2020 at 15:37):

similarly flypitch's version of FOL uses natural deduction, so there is no deduction theorem, it is just a given rule namely implication introduction (just as in Lean's logical framework, or as in simply typed lambda calculus where it corresponds to lambda abstraction)
cf. https://github.com/flypitch/flypitch/blob/master/src/fol.lean#L818

AMM (Apr 22 2020 at 15:37):

Matt Earnshaw said:

similarly flypitch's version of FOL uses natural deduction, so there is no deduction theorem, it is just a given rule namely implication introduction (just as in Lean's logical framework, or as in simply typed lambda calculus where it corresponds to lambda abstraction)
cf. https://github.com/flypitch/flypitch/blob/master/src/fol.lean#L818

interesting!

Kenny Lau (Apr 22 2020 at 15:38):

even if Lean used first order logic, the deduction theorem is a meta theorem, so you wouldn't be table to prove it (or even formalize it) in Lean

Kenny Lau (Apr 22 2020 at 15:38):

so if you want to "prove deduction theorem in Lean", you first need to create a subsystem inside Lean, i.e. formalize first order logic inside Lean

Kenny Lau (Apr 22 2020 at 15:39):

and then the deduction theorem will be a meta-theorem in the sub-system, i.e. a theorem

Reid Barton (Apr 22 2020 at 15:41):

And, it would be a theorem about whatever specific logical system you formalized.

AMM (Apr 22 2020 at 15:44):

Kenny Lau said:

and then the deduction theorem will be a meta-theorem in the sub-system, i.e. a theorem

I see, this is a fair point about the metatheorem.
yet the idea that if you take any two things that prove a third thing and, then, only had the first thing and used the previous result to get the conditional involving the second and third would seem very much in the spirit of the deduction theorem.

AMM (Apr 22 2020 at 15:45):

Also this other thing pseudo deduction theorem seems like it should be almost trivial in lean without first formalising first order logic. Is my reasoning wrong?

Reid Barton (Apr 22 2020 at 15:50):

Do you consider what Mario posted earlier to be an answer?

Reid Barton (Apr 22 2020 at 15:53):

Namely, before intro the goal ends with

s : Sigma
 α  β

and after intro the goal ends with

s : Sigma,
a : α
 β

which means if we have a proof of β from Sigma and α, we could stick intro before it to get a proof of α → β from Sigma.

AMM (Apr 22 2020 at 16:04):

yes I think for my informal purposes Mario's comment would be an answer!

AMM (Apr 22 2020 at 16:06):

is there any way of writing in lean ' we have a proof of β from Sigma and α' in a def before that with a sorry? Otherwise, I guess I can just use English

Ashwin Iyengar (Apr 22 2020 at 16:20):

Hi:

import ring_theory.ideals

universe u

variables {R : Type u} [nonzero_comm_ring R]

def S : set R := {r | r = 0}

def x (r : R) (s : R) [h1: r  (S : set R)] [h2: s  (S : set R)] : r = s :=
begin
  rw h1,
end

(this is a bit contrived, but illustrates my confusion) Gives me the error

rewrite tactic failed, lemma is not an equality nor a iff
state:
R : Type u,
_inst_1 : nonzero_comm_ring R,
r s : R,
h1 : r  S,
h2 : s  S
 r = s

Am I incorrect in assuming that h1 is definitionally equivalent to the hypothesis s = 0 ?

Ashwin Iyengar (Apr 22 2020 at 16:20):

Sorry i mean r=0

Kenny Lau (Apr 22 2020 at 16:21):

rw (show r = 0, from h1)

Reid Barton (Apr 22 2020 at 16:22):

You are not wrong about this, however rw wants to see something that's syntactically an equality or iff.

Ashwin Iyengar (Apr 22 2020 at 16:23):

Oh ok. Is there another, better tactic to use besides rw? Or is @Kenny Lau's solution the best one?

Kevin Buzzard (Apr 22 2020 at 17:19):

def x (r : R) (s : R) (h1: r  (S : set R)) (h2: s  (S : set R)) : r = s :=
begin
  exact eq.trans h1 h2.symm
end

Kevin Buzzard (Apr 22 2020 at 17:19):

rw only works up to syntactic equality, whereas many other things (e.g. term mode) work up to definitional equality.

Kevin Buzzard (Apr 22 2020 at 17:25):

(and you don't want the h_i in square brackets)

Kevin Buzzard (Apr 22 2020 at 17:27):

def x (r : R) (s : R) (h1: r  (S : set R)) (h2: s  (S : set R)) : r = s :=
begin
  change r = 0 at h1,
  rw h1,
  exact h2.symm -- h2 has type eq _ _ so h2.symm means eq.symm h2
end

Kevin Buzzard (Apr 22 2020 at 17:28):

def x (r : R) (s : R) (h1: r  (S : set R)) (h2: s  (S : set R)) : r = s :=
begin
  unfold has_mem.mem set.mem S set_of at h1,
  rw h1,
  exact h2.symm
end

Kevin Buzzard (Apr 22 2020 at 17:28):

(you can see what to unfold if you write set_option pp.notation false)

Kevin Buzzard (Apr 22 2020 at 17:30):

set_option pp.notation false
def x (r : R) (s : R) (h1: r  (S : set R)) (h2: s  (S : set R)) : r = s :=
begin
  unfold has_mem.mem at h1, -- h1 : set.mem r S
  unfold set.mem at h1, -- h1 : S r
  unfold S at h1, -- h1 : set_of (λ (r : R), eq r 0) r
  unfold set_of at h1, -- h1 : eq r 0
  rw h1,
  exact h2.symm
end

Kevin Buzzard (Apr 22 2020 at 17:30):

def x (r : R) (s : R) (h1: r  (S : set R)) (h2: s  (S : set R)) : r = s :=
eq.trans h1 h2.symm

Kenny Lau (Apr 22 2020 at 17:31):

def x (r : R) (s : R) [h1: r  (S : set R)] [h2: s  (S : set R)] : r = s :=
begin
  cases h1, cases h2, refl
end

Kenny Lau (Apr 22 2020 at 17:31):

also it's a theorem not a def

Nam (Apr 22 2020 at 23:53):

how do i get a "decidable" version of {a : Type} [has_le a]? it looks like has_decidable_le is only for list.

Mario Carneiro (Apr 23 2020 at 00:07):

[decidable_rel ((≤):a->a->Prop)]

Nam (Apr 23 2020 at 00:09):

i tried with a.has_le.le :'(

Nam (Apr 23 2020 at 00:09):

{α : Type} [has_le α] [decidable_rel α.has_le.le]

Nam (Apr 23 2020 at 00:09):

why does that not work?

Mario Carneiro (Apr 23 2020 at 00:16):

this notation α.has_le.le doesn't work

Mario Carneiro (Apr 23 2020 at 00:16):

you can write @has_le.le α _ if you don't like the type ascription way (which is I think more readable)

Nam (Apr 23 2020 at 00:46):

{α : Type} [has_le α] [decidable_rel (@has_le.le α _)] is certainly more readable. thank you!

Mario Carneiro (Apr 23 2020 at 00:47):

What's wrong with using [decidable_rel ((≤):a->a->Prop)] instead? (this is the one I was saying was more readable)

Nam (Apr 23 2020 at 00:48):

too many symbols.

Scott Morrison (Apr 23 2020 at 01:08):

(I think Mario's is much more readable, for what it's worth.)

Nam (Apr 23 2020 at 01:10):

i can see why that style is more prevalent in the repo now :-D

Shing Tak Lam (Apr 23 2020 at 01:13):

How should I pattern match on ℕ+? since the way I'd expect doesn't work

def star : ℕ+  ℕ+  ℕ+
| 1 n := n + 1 -- error

Bryan Gin-ge Chen (Apr 23 2020 at 02:15):

My instinct would be to do this:

import tactic

def star : ℕ+  ℕ+  ℕ+
| 1, h1 n, hn := n + 1, by linarith
| n1, hn1 n2, hn2 := sorry

Shing Tak Lam (Apr 23 2020 at 02:15):

Ok thank you. I'll give that a shot.

AMM (Apr 24 2020 at 16:37):

Hello everyone! How would you say Lean compares to other theorem provers? In particular, what would you say are its main virtues?

Nam (Apr 24 2020 at 17:42):

@AMM https://jiggerwit.wordpress.com/2018/09/18/a-review-of-the-lean-theorem-prover/

Nam (Apr 24 2020 at 17:42):

2018 though.

Johan Commelin (Apr 24 2020 at 17:46):

It only got better...

Johan Commelin (Apr 24 2020 at 17:46):

@AMM What are you looking for? A comparison can be really long. Is there some specific thing you care about?

Johan Commelin (Apr 24 2020 at 17:46):

Do you care about some kind of maths? Or more about software verification? Or something else?

Johan Commelin (Apr 24 2020 at 17:46):

Do you care about type theory, or not at all?

Nam (Apr 24 2020 at 17:53):

i care mainly about software verification. what other languages should i look into?

Johan Commelin (Apr 24 2020 at 18:17):

Coq has a lot on that. And then there are others that I know very little about, like TLA+ and such

Sayantan Majumdar (Apr 24 2020 at 18:27):

I was trying the nat.below and nat.brec_on things. I am having some trouble prooving a few things not sure how to go about doing these

def fib1 :   
    | 0 := 1
    | 1 := 1
    | (n + 2) := fib1 (n + 1) + fib1 n

    def fib2 :    :=
        assume n : ,
        @nat.brec_on (λ n : , ) n
        (
            assume n : ,
            assume h : nat.below (λ n : , ) n,
            (
                λ (n : ) (h : nat.below (λ n : , ) n),
                (@nat.cases_on (λ n : , nat.below (λ n : , ) n  ) n
                (λ h : nat.below (λ n : , ) 0, 1)
                (
                    assume n : ,
                    assume h : nat.below (λ n : , ) (nat.succ n),
                    (@nat.cases_on (λ n : , nat.below (λ n : , ) (nat.succ n)  ) n
                    (λ h : nat.below (λ n : , ) 1, 1)
                    (
                        assume n : ,
                        assume h : nat.below (λ n : , ) (nat.succ (nat.succ n)),
                        (h.fst.fst + h.fst.snd.fst.fst)
                    )) h
                )) h
            ) n h
        )

    #reduce fib1 0
    #reduce fib1 1
    #reduce fib1 2
    #reduce fib1 3
    #reduce fib1 4
    #reduce fib1 5
    #reduce fib1 7

    #reduce fib2 0
    #reduce fib2 1
    #reduce fib2 2
    #reduce fib2 3
    #reduce fib2 4
    #reduce fib2 5
    #reduce fib2 7

    example :  n : , fib1 n = fib2 n
    | 0 := rfl
    | 1 := rfl
    | (n + 2) := by rw [fib1, fib2]

Sayantan Majumdar (Apr 24 2020 at 18:28):

The last part (n + 2) := sorry case

AMM (Apr 24 2020 at 18:43):

Johan Commelin said:

AMM What are you looking for? A comparison can be really long. Is there some specific thing you care about?

Ill have a look at that review, also just the system description was quite helpful. I just wanted some thoughts, this is great!

Aniruddh Agarwal (Apr 24 2020 at 20:36):

What's the difference between bundled x and unbundled x in mathlib?

Patrick Massot (Apr 24 2020 at 20:39):

This is explained in Section 4.1.1 of https://arxiv.org/abs/1910.09336

Kevin Buzzard (Apr 24 2020 at 20:47):

@AMM I think https://artagnon.com/articles/leancoq does a good job of comparing Lean and Coq. In particular, I learnt from this article that Lean breaks "good type theoretic properties like strong normalization, subject reduction, and canonicity", and I have heard from Coq people that this is a big deal for some, but as a mathematician this breakage has never bothered me in the slightest. Another big difference is that "generic mathematicians" use Lean (people who do not specialise in type theory or higher categories or whatever -- they are just number theorists or analysts or geometers or topologists) -- and for me this is another of the main attractions.

Kevin Buzzard (Apr 24 2020 at 20:47):

@AMM (sorry, didn't work first time)

Kevin Buzzard (Apr 24 2020 at 20:53):

@Sayantan Majumdar

    example :  n : , fib1 n = fib2 n
    | 0 := rfl
    | 1 := rfl
    | (n + 2) := begin
     rw [fib1, fib2, _example n, _example (nat.succ n)],
     refl,
    end

Michael Barz (Apr 24 2020 at 20:54):

I'm trying to learn lean at the moment by formalizing a few statements about lattices to get a feel for proving things. I was having two difficulties.

At the moment, I have a list of easy theorems about lattices with a closure operator I'd like to try writing up in Lean. From the source code in order.lattice, I decided to define `class closed_lattice (P : Type u) extends lattice P' to be a structure obeying four properties (has a closure function + 3 axioms of closure).

With the very first theorem on my list, I encountered a difficulty: If I want to prove a result about finite lattices with closure operator, how do I add finiteness as a hypothesis to a theorem?

Reid Barton (Apr 24 2020 at 20:55):

Probably by adding [fintype P] in the list of arguments somewhere

Kevin Buzzard (Apr 24 2020 at 20:56):

Close code quoting with ` (the same character as opening it). And post full working code if you want more precise answers.

Michael Barz (Apr 24 2020 at 21:00):

Thank you Reid, that seems to have worked! Sorry about the lack of code--Reid's answer seems to be enough to work at the moment, though I'll be sure to include actual code snippets if I have future questions.

Scott Guest (Apr 24 2020 at 22:06):

Hi, I'm new to Lean and wanted to get my feet wet proving some basic things in ZFC, but I'm struggling to actually make use of set_theory.ZFC. Could someone give me guidance on how I would formalize even the statement of something like "If S is a singleton and A is any set, then |S x A| = |A|"?

Kevin Buzzard (Apr 24 2020 at 22:07):

You know that you could just prove that in type theory and not worry about the ZFC stuff?

Kevin Buzzard (Apr 24 2020 at 22:07):

i.e. if S is a singleton and A is any type, then |S x A| = |A|

Reid Barton (Apr 24 2020 at 22:10):

Where |A| is cardinal.mk A, from set_theory.cardinal; unrelated to set_theory.zfc.

Kevin Buzzard (Apr 24 2020 at 22:10):

It says localized "notation # := cardinal.mk" in cardinal in that file.

Kevin Buzzard (Apr 24 2020 at 22:11):

What does that mean?

Scott Guest (Apr 24 2020 at 22:11):

I'm taking a set theory course right now which works in ZFC, so I would like to actually formalize it in Lean's model of ZFC if possible. However, I guess this is also something I'm a little unclear on: what exactly would it look like to phrase this just in type theory versus in ZFC?

Kevin Buzzard (Apr 24 2020 at 22:11):

Because a type is just a beefed-up set, you would never notice the difference if you proved it in type theory.

Kevin Buzzard (Apr 24 2020 at 22:12):

import set_theory.cardinal

example (S A : Type) : cardinal.mk S = 1  cardinal.mk (S × A) = cardinal.mk A :=
sorry

I guess that's the type theory version

Reid Barton (Apr 24 2020 at 22:12):

Even if you formalize it as a theorem about the model of ZFC in mathlib, if you prove it, you are not using the axioms of ZFC but rather the "metatheory" (Lean).

Reid Barton (Apr 24 2020 at 22:14):

And in particular you wouldn't know whether it holds in other models of ZFC.

Scott Guest (Apr 24 2020 at 22:16):

Thank you, that makes sense. If I want to prove theorems about any model of ZFC then, is there a way to actually do this in Lean, or should I just stick to proving the analogous statements with types?

Reid Barton (Apr 24 2020 at 22:17):

You can check out https://github.com/flypitch/flypitch/, but maybe it would be more ergonomic to use a theorem prover based on ZFC.

Kevin Buzzard (Apr 24 2020 at 22:18):

If there's a bijection between two sets then they have the same cardinality.

Kevin Buzzard (Apr 24 2020 at 22:19):

So it would suffice to prove that there's a bijection between AA and S×AS\times A, and that would be a rather pleasant beginner exercise in Lean, and the proof would be exactly the same as the set theory proof.

Kevin Buzzard (Apr 24 2020 at 22:22):

import data.equiv.basic -- theory of bijections

variables {S A : Type} (s : S) (hs :  t : S, s = t)

include hs

def one_way : S × A  A := sorry

def other_way : A  S × A := sorry

def they_biject : S × A  A :=
{ to_fun := one_way s hs,
  inv_fun := other_way s hs,
  left_inv := sorry,
  right_inv := sorry }

Scott Guest (Apr 24 2020 at 22:26):

Got it, it's clear now. I definitely have a lot to learn about Lean, thank you for being so willing to help new users!

Michael Barz (Apr 24 2020 at 22:26):

Hello again! I have one more question about finiteness. Reid's [fintype P] solution has worked well for me to use finiteness as a hypothesis in a theorem, although I've encountered difficulties with a related problem. Below is some code trying to express the idea that "any two suitable sets M and N have the same number of elements" (in the code, lattice is just the standard mathlib order.lattice)

import order.lattice
import data.fintype.basic

def is_totally_ordered (P : Type u) [lattice P] (M : set P) := ( m : M,  n : M, m  n  n  m)
def is_maximal (P : Type u) [lattice P] (M : set P) := ( p : P,  m : M, (p  m  p  m)  p  M)
def is_chain (P : Type u) [lattice P] (M : set P) := is_totally_ordered P M  is_maximal P M


class chain_condition (P : Type u) extends lattice P :=
(finite_chains :  M : set P, (is_totally_ordered P M  fintype M))
(chains_equal_length :  M N : set P, ((is_chain P M  is_chain P N  fintype M  fintype N)  (fintype.elems M).card = (fintype.elems N).card))
(max_elm :  M : P, (forall q : P, q  M))
(min_elm :  m : P, (forall q : P, m  q))

The main issue I'm having is with the second axiom--what I wrote errors in Lean (specifically at the last \and in the condition, with a somewhat long error message I could reproduce if needed), and I suspect because while [fintype M] is the right way of asserting M is finite in a hypothesis of a theorem, what I'm doing is the wrong way of asserting the finiteness of M in this implication.

Kevin Buzzard (Apr 24 2020 at 22:27):

What imports do I need?

Michael Barz (Apr 24 2020 at 22:27):

import order.lattice

Michael Barz (Apr 24 2020 at 22:27):

import data.fintype.basic too, sorry!

Kevin Buzzard (Apr 24 2020 at 22:27):

Why not edit the code to make it work out of the box?

Michael Barz (Apr 24 2020 at 22:28):

Oh, sure

Michael Barz (Apr 24 2020 at 22:28):

It's edited with imports now, sorry about that!

Scott Morrison (Apr 24 2020 at 22:29):

(Although note that for any "content-ful" edits, people usually prefer if you just post a new copy of the MWE, so it doesn't screw up the conversation history.)

Kevin Buzzard (Apr 24 2020 at 22:29):

fintype M is probably not what you want. It's data, not a proposition.

Kevin Buzzard (Apr 24 2020 at 22:29):

Because M : set P you might want to instead use M : finset P, the type of finite subsets of P. This would solve your problems immediately.

Kevin Buzzard (Apr 24 2020 at 22:30):

Then you could just drop fintype M completely.

Michael Barz (Apr 24 2020 at 22:30):

Oh, thank you!
For future reference though, how would I get a proposition asserting that M is a fintype?

Kevin Buzzard (Apr 24 2020 at 22:31):

For M : set P, if you want to conclude "M is finite" then set.finite M is the way to go.

Kevin Buzzard (Apr 24 2020 at 22:31):

(import data.set.finite)

Michael Barz (Apr 24 2020 at 22:32):

Thank you!

I think I will end up going with the set.finite M route, since implementing the M : finset P approach errors that is_chain is defined on types M : set P, not for M : finset P

Kevin Buzzard (Apr 24 2020 at 22:34):

I am not immediately sure how to start making assertions about the cardinality of a finite set though :-/

Michael Barz (Apr 24 2020 at 22:36):

Ah, I do not either. However, I figured out that changing the is_chain to work on \u M instead of M has lean recognize that finsets are still sets, and so the is_chain definition and M.card both work

Reid Barton (Apr 24 2020 at 22:36):

I think there's a nat-valued cardinality function somewhere that returns 0 for infinite sets

Scott Guest (Apr 24 2020 at 23:14):

Hello again, I had a few more questions about working with sets. As discussed a bit ago, I see how statements analogous to "If S is a singleton and A is a set, then |S x A| = |A|" can be formalized by letting S A : Type, but I don't see how to extend this to more complicated statements.

Namely, say I wanted to prove "For any sets X and Y, there is some set Z with |X| = |Z| and Z ∩ Y = ∅." Unless I missed something, it seems like types in Lean don't support intersection, so I don't know how to specify the disjointness part. The statement also isn't true in general if I assume X Y : set U for some fixed type U, say because U could be finite with Y containing every element of U. Any advice?

Kevin Buzzard (Apr 24 2020 at 23:27):

This is the sort of question which you have to deal with in set theory but which typically you don't ever need to worry about in type theory.

Kevin Buzzard (Apr 24 2020 at 23:28):

This is some sort of fiddly foundational thing which sometimes comes up in set theory. Why would you ever need that in practice?

Kevin Buzzard (Apr 24 2020 at 23:31):

Type theory is different to set theory. It's more powerful. A "bare type" is just like a set, but if T is a type then a term t : T of type T isn't like a set -- t is more like an "atom". This is a good thing, because if G is a group then in set theory the elements of G are also sets, but they are sets whose elements are irrelevant and never talked about by mathematicians. So letting G be a type is actually a better model for groups. However, if S and T are two distinct types and you have terms s : S and t : T then it does not even make sense to ask if s = t. This is much more disconcerting for someone used to set-theoretic mathematics.

Scott Guest (Apr 24 2020 at 23:37):

Ah, I had a hunch I was generally barking up the wrong tree trying to do this type of thing. Eventually, I wanted to formalize Tarksi's proof that choice is equivalent over ZF to the statement "For all infinite A, |A x A| = |A|", and it's needed for a line in that, so this is indeed a fiddly foundation thing.

Kevin Buzzard (Apr 24 2020 at 23:41):

In Lean, A×AA\times A and AA will automatically be "disjoint", if that helps.

Scott Guest (Apr 24 2020 at 23:42):

I need disjointness between some set and it's Hartogs number, but since those are different types it seems I get that for free. Thank you again.

Nam (Apr 25 2020 at 00:34):

does anyone know of a sample big-O analysis in Lean?

Reid Barton (Apr 25 2020 at 01:04):

Of an algorithm?

Mario Carneiro (Apr 25 2020 at 01:09):

@Scott Guest The statement "For all infinite A, |A x A| = |A|" is perfectly reasonable in type theory, and you can prove it without having to assume ZFC foundations. However, proving an equivalence with choice is going to be tough because lean assumes choice all over the place, so proofs in "ZF" are not at all nice.

Nam (Apr 25 2020 at 01:50):

Reid Barton said:

Of an algorithm?

yes, of an algorithm, preferably some well-known one.

Mario Carneiro (Apr 25 2020 at 01:51):

There is the big-O symbol, but runtime analysis of algorithms is not lean's strong suit. The usual way we represent algorithms, as functions, precludes the ability to measure the time complexity of the algorithm, so you need some other mechanism like a step counting monad

Shing Tak Lam (Apr 25 2020 at 01:53):

I guess there's this? Might be more to do with Rust not Lean though. I'm not sure tbh.

https://github.com/Kha/electrolysis

(Last section of the linked thesis)

Andrew Ashworth (Apr 25 2020 at 01:59):

Let me plug Verified Functional Algorithms here: https://softwarefoundations.cis.upenn.edu/current/vfa-current/index.html

Andrew Ashworth (Apr 25 2020 at 02:04):

The author says this about runtime calculations:

"3. There's no notion of "run time" in Coq. That is, we can't say what it means that a Coq function "takes N steps to evaluate." Therefore, we can't prove that binary search trees are efficient. - SOLUTION 1: Don't prove (in Coq) that they're efficient; just prove that they are correct. Prove things about their efficiency the old-fashioned way, on pencil and paper. - SOLUTION 2: Prove in Coq some facts about the height of the trees, which have direct bearing on their efficiency. We'll explore that in later chapters."

Andrew Ashworth (Apr 25 2020 at 02:07):

and here's a stackexchange post on the topic: https://cstheory.stackexchange.com/questions/38818/proving-running-time-upper-bounds-for-algorithms-in-dependent-type-theory

ROCKY KAMEN-RUBIO (Apr 25 2020 at 02:10):

Is there a preferred way to express divisibility? Looking through mathlib/other posts I'm seeing it expressed in many different equivalent forms depending on the context. Mathematically this doesn't bother me, but it seems like having multiple definitions for things is discouraged in computer science land?

Nam (Apr 25 2020 at 02:12):

can't users (we) guide Lean / Coq with some assumptions / hints?

Nam (Apr 25 2020 at 02:13):

e.g. this part / line takes one instruction

Nam (Apr 25 2020 at 02:14):

i guess that's what Mario referred to as steps monad/

Mario Carneiro (Apr 25 2020 at 02:14):

@Nam First you have to define what it means for something to take time

Mario Carneiro (Apr 25 2020 at 02:16):

Ideally, we would want (\lam x, e1) e2 ~> [e2/x] e1 to take some time, it is a complicated operation after all, but to lean they are equal and so no function can distinguish them

Mario Carneiro (Apr 25 2020 at 02:24):

Here's an example of using a step counting monad to prove time bounds:

import data.list.basic

def step (α : Type) := α × 

def step.time {α} (x : step α) :  := x.2

instance : monad step :=
{ pure := λ α a, (a, 0),
  bind := λ α β x f, ((f x.1).1, x.2 + (f x.1).2) }

def delay (n : ) : step unit := ((), n)

def tick : step unit := delay 1

def addM (m n : ) : step  :=
(m + n, nat.size m + nat.size n)

def mulM (m n : ) : step  :=
(m * n, nat.size m * nat.size n)

def sumM : list   step 
| [] := pure 0
| (a::l) := sumM l >>= addM a

theorem sum_correct :  l, (sumM l).1 = l.sum  := sorry
theorem sum_time :  l, (sumM l).time  2 * (l.map nat.size).sum := sorry

Nam (Apr 25 2020 at 02:41):

thanks! that's very useful.

Kenny Lau (Apr 25 2020 at 04:02):

@Scott Guest you can formalize what it means to be a model of ZFC, then you can work in a general model of ZFC if you want

Kenny Lau (Apr 25 2020 at 04:02):

remember to show that the "standard" model in mathlib is indeed a model of ZFC

Kenny Lau (Apr 25 2020 at 04:03):

This is actually something I want to do once I have more time

Kevin Buzzard (Apr 25 2020 at 08:54):

ROCKY KAMEN-RUBIO said:

Is there a preferred way to express divisibility? Looking through mathlib/other posts I'm seeing it expressed in many different equivalent forms depending on the context. Mathematically this doesn't bother me, but it seems like having multiple definitions for things is discouraged in computer science land?

In which type?

ROCKY KAMEN-RUBIO (Apr 25 2020 at 18:48):

Kevin Buzzard said:

ROCKY KAMEN-RUBIO said:

Is there a preferred way to express divisibility? Looking through mathlib/other posts I'm seeing it expressed in many different equivalent forms depending on the context. Mathematically this doesn't bother me, but it seems like having multiple definitions for things is discouraged in computer science land?

In which type?

Either nat or int.

Kevin Buzzard (Apr 25 2020 at 18:55):

a \| b is what is used for both of those types.

Frank Dai (Apr 25 2020 at 19:02):

I have a hypothesis of the form h : f = g, how can I get a hypothesis of the form hx : f x = g x?

Kevin Buzzard (Apr 25 2020 at 19:03):

have hx : f x = g x, rw h

Kevin Buzzard (Apr 25 2020 at 19:03):

have is a very useful way to make new hypothesis. It creates a new goal, but if the proof is easy then this is no problem.

Frank Dai (Apr 25 2020 at 19:04):

is there a way to do it without copying f and g (they're both really long)?

Johan Commelin (Apr 25 2020 at 19:04):

The term-mode variant is have hx := congr_fun h x

Johan Commelin (Apr 25 2020 at 19:04):

And it doesn't require copying f and g

Sam Raleigh (Apr 25 2020 at 19:05):

If I wanted to prove results about graphs in lean, is there a graph type of some kind?

Frank Dai (Apr 25 2020 at 19:05):

I prefer ap but I guess that's okay :P

Johan Commelin (Apr 25 2020 at 19:09):

Sam Raleigh said:

If I wanted to prove results about graphs in lean, is there a graph type of some kind?

See the hedetniemi branch

Johan Commelin (Apr 25 2020 at 19:09):

It's very much work in progress

Bryan Gin-ge Chen (Apr 25 2020 at 19:10):

See also the associated Zulip thread.

ROCKY KAMEN-RUBIO (Apr 25 2020 at 19:47):

Does Lean have a floor/ceiling function to get from non-negative reals to nats? Looking through mathlib I'm not seeing anything, but my intuition for mathlib's organization and naming conventions still isn't great.

Mario Carneiro (Apr 25 2020 at 19:49):

They are in algebra.archimedean

Mario Carneiro (Apr 25 2020 at 19:49):

grepping for floor also helps

ROCKY KAMEN-RUBIO (Apr 25 2020 at 20:00):

Mario Carneiro said:

They are in algebra.archimedean

Looks like exists_nat_gt coupled with a cases does the job. A little clunky though. I'll keep looking to see if there's a direct function.

Chris Hughes (Apr 25 2020 at 20:21):

ceil gives you an int

Kevin Buzzard (Apr 25 2020 at 20:35):

I suspect that the precise functions you're asking about aren't there Rocky, you might want to write it yourself. It could take as input a real x and output a natural real.natfloor x, which is 0 if x is negative and the floor otherwise. You'd then have to make a little API for the function so it was usable

Mario Carneiro (Apr 25 2020 at 20:41):

You could always use int.to_nat (floor x)

Mario Carneiro (Apr 25 2020 at 20:42):

but if cases exists_nat_gt does the job for you, I would actually suggest using that instead, since the floor function is more complicated than that

Sam Raleigh (Apr 26 2020 at 01:59):

Given a b c : nat, how do I rewrite a hypothesis of the form a < b as a*c < b*c?

Sam Raleigh (Apr 26 2020 at 01:59):

Assuming c > 0 ofc

Bryan Gin-ge Chen (Apr 26 2020 at 02:01):

Here's one way to find lemmas like this:

import tactic

example (a b c : ) (hc : c > 0) (h : a < b) : a * c < b * c := by library_search
-- Try this: exact mul_lt_mul_of_pos_right h hc

Sam Raleigh (Apr 26 2020 at 02:02):

Haha you answered my next question before I could even ask it: how do I find lemmas like this in general?

Sam Raleigh (Apr 26 2020 at 02:02):

thanks!

Sam Raleigh (Apr 26 2020 at 03:16):

Say I have some hypothesis h : 2^a * (2^(1-b) * c) < 2^1 * 1 and my goal looks like2^(a - b) * c * 2 < 2^a. On paper all you need to do to get from the goal to the hypothesis is rearrange things, but this is proving to be tedious to implement in lean. Is there some automating tactic like ring that would close the goal given h?

Sam Raleigh (Apr 26 2020 at 03:17):

I don't suppose there's by library_search for tactics, eh @Bryan Gin-ge Chen ?

Shing Tak Lam (Apr 26 2020 at 03:17):

hint

Mario Carneiro (Apr 26 2020 at 03:17):

there's no way that theorem is in the library

Scott Morrison (Apr 26 2020 at 03:17):

are your variables all int?

Sam Raleigh (Apr 26 2020 at 03:18):

oh sorry, I should have specified they are all nat

Scott Morrison (Apr 26 2020 at 03:18):

(Shing is saying that the closest thing for library_search for tactics is hint. But it is very primitive, and will get you nowhere here.)

Mario Carneiro (Apr 26 2020 at 03:18):

nat subtraction will cause a lot of problems here

Scott Morrison (Apr 26 2020 at 03:18):

Your first step should probably be to work with int. :-)

Mario Carneiro (Apr 26 2020 at 03:18):

hopefully you know that b is at most 1?

Mario Carneiro (Apr 26 2020 at 03:19):

and b <= a

Mario Carneiro (Apr 26 2020 at 03:20):

you could also "rearrange things" in lean using calc

Sam Raleigh (Apr 26 2020 at 03:25):

@Scott Morrison @Mario Carneiro Thanks, I completely forgot about nats and subtraction.

Sam Raleigh (Apr 26 2020 at 03:33):

Now supposing that a b c : int how would I use calc given h? I tried searching the mathlib and core library documentation, but there doesn't appear to be anything on calc in either.

Shing Tak Lam (Apr 26 2020 at 03:34):

https://leanprover-community.github.io/mathlib_docs/calc.html

Sam Raleigh (Apr 26 2020 at 03:35):

Ah thank you @Shing Tak Lam , I made the mistake of looking only under tactics

Johan Commelin (Apr 26 2020 at 03:38):

Shing Tak Lam said:

https://leanprover-community.github.io/mathlib_docs/calc.html

Why don't we get syntax highlighting in these docs?

Bryan Gin-ge Chen (Apr 26 2020 at 03:42):

I'm planning to look into adding it after the next pygments release which should have the updated Lean syntax highlighting.

Bryan Gin-ge Chen (Apr 26 2020 at 03:43):

No idea when that will be though...

Billy Price (Apr 26 2020 at 04:40):

How do see the fully unpacked version of this term I'm trying to check?

def ex_unique (A : type) (φ : term Ω) : term Ω :=
  ' A $ |{A | φ}|  |{var A 3}|

#check ex_unique 𝟙 

Frank Dai (Apr 26 2020 at 04:49):

How do I compute with snd on dependent pairs? Slightly more generally, how do I see what the computation rules for foo_thing are?

def foo : Type 1 := Σ (B : Type), (A  B)

def foo_thing : foo := nat, id

lemma foo_map_is_id : foo_thing.snd = id := sorry

Kenny Lau (Apr 26 2020 at 05:10):

variables {A : Type}
def foo : Type 1 := Σ (B : Type), (A  B)

def foo_thing : foo := nat, id
#print prefix foo_thing
-- foo_thing.equations._eqn_1 : foo_thing = ⟨ℕ, id ℕ⟩

lemma foo_map_is_id : foo_thing.snd = id := rfl

Kenny Lau (Apr 26 2020 at 05:10):

@Billy Price set_option pp.all true

Sam Raleigh (Apr 26 2020 at 07:06):

lemma num_2_cols_Kn_with_monochromatic_Kk_lt_num_2_cols_Kn
    (n k : ) (h : 2^(1 - k.choose(2)) * n.choose(k) < 1) :
    2^(n.choose(2) - k.choose(2)) * 2 * n.choose(k) < 2^(n.choose(2)) :=
calc
    2^(n.choose(2) - k.choose(2)) * 2 * n.choose(k) = 2^(n.choose(k)) * 2^(-(k.choose(2))) * 2 * n.choose(k) :

I'm getting an error on the last line that says

failed to synthesize type class instance for
n k : ℕ,
h : 2 ^ (1 - nat.choose k 2) * nat.choose n k < 1
⊢ has_neg ℕ

Now I know this is likely happening because nat and subtraction aren't playing nicely together and I should switch to k n : int.
My only concern is that int doesn't contain a choose function the way nat does. Should I define my own binomial coefficient that accepts integers? If so, I'd greatly appreciate it if someone could point me towards resources on defining your own functions. Or is there a way I could still use nat.choose with k n : int?

Kenny Lau (Apr 26 2020 at 07:08):

how about don't write 2^-k.choose 2

Kenny Lau (Apr 26 2020 at 07:08):

because in all interpretations it is just 0

Kenny Lau (Apr 26 2020 at 07:08):

instead 2^(nC2-kC2) = 2^(nC2) / 2^(kC2) makes more sense

Kenny Lau (Apr 26 2020 at 07:09):

also 2^(1 - k.choose(2)) * n.choose(k) < 1 probably doesn't mean what you think it means

Mario Carneiro (Apr 26 2020 at 07:09):

Maybe lift everything to Q

Kenny Lau (Apr 26 2020 at 07:10):

right, you can coerce n.choose (k) to \Q

Kenny Lau (Apr 26 2020 at 07:10):

without having to define rat.choose

Sam Raleigh (Apr 26 2020 at 07:20):

Thanks for your answers. What do you mean by coerce?

Kenny Lau (Apr 26 2020 at 07:24):

lemma num_2_cols_Kn_with_monochromatic_Kk_lt_num_2_cols_Kn
    (n k : ) (h : (2^(1 - k.choose(2)) * n.choose(k) : \Q) < 1) :
    (2^(n.choose(2) - k.choose(2)) * 2 * n.choose(k) : \Q) < 2^(n.choose(2)) :=

Kenny Lau (Apr 26 2020 at 07:24):

remember to import data.rat.basic

Kenny Lau (Apr 26 2020 at 07:25):

oh and you need to coerce the exponents to \Z

Kenny Lau (Apr 26 2020 at 07:25):

lemma num_2_cols_Kn_with_monochromatic_Kk_lt_num_2_cols_Kn
    (n k : ) (h : (2^(1 - k.choose(2) : \Z) * n.choose(k) : \Q) < 1) :
    (2^(n.choose(2) - k.choose(2) : \Z) * 2 * n.choose(k) : \Q) < 2^(n.choose(2) : \Z) :=

Kenny Lau (Apr 26 2020 at 07:26):

coercion is basically an injection, in this case the injection \N -> \Z

Sam Raleigh (Apr 26 2020 at 07:30):

Oh wow I was hoping it was possible to do something just like this. Thanks a bunch : D

Apurva Nakade (Apr 26 2020 at 16:23):

What would be a good method to define "a monoidal category which is also a groupoid"?

Anas Himmi (Apr 26 2020 at 16:47):

how to show this?

example (s:set (set α)) (h: a, a  s  a = ) : s={}  s= := sorry

Kenny Lau (Apr 26 2020 at 16:56):

import tactic

example {α : Type*} (s : set (set α)) (h :  a, a  s  a = ) : s = {}  s =  :=
or.cases_on (set.subsingleton.eq_empty_or_singleton $ λ x hx y hy, (h x hx).trans (h y hy).symm) or.inr $
by { rintro t, rfl, left, rw set.singleton_eq_singleton_iff, exact h t (set.mem_singleton t) }

Anas Himmi (Apr 26 2020 at 16:59):

thank you!!

Reid Barton (Apr 26 2020 at 17:16):

Apurva Nakade said:

What would be a good method to define "a monoidal category which is also a groupoid"?

variables {C : Type u} [𝒞 : groupoid.{v} C] [monoidal_category C] should do it.

Apurva Nakade (Apr 26 2020 at 18:06):

Reid Barton said:

Apurva Nakade said:

What would be a good method to define "a monoidal category which is also a groupoid"?

variables {C : Type u} [𝒞 : groupoid.{v} C] [monoidal_category C] should do it.

Thanks a lot!

Sam Raleigh (Apr 26 2020 at 20:43):

@Kenny Lau I added the coercions:

lemma num_2_cols_Kn_with_mono_Kk_lt_num_2_cols_Kn
    (n k : ) (h : (2^(1 - k.choose(2) : ) * n.choose(k) : ) < 1) :
    (2^(n.choose(2) - k.choose(2) : ) * 2 * n.choose(k) : ) < 2^(n.choose(2) : ) :=
calc
    2^(n.choose(2) - k.choose(2)) * 2 * n.choose(k) = 2^(n.choose(2) - k.choose(2)) * 2 * n.choose(k) :
    ...

But I'm still getting this issue:

failed to synthesize type class instance for
n k : ℕ
⊢ has_pow ℚ ℤ

Kenny Lau (Apr 26 2020 at 20:44):

you need to import algebra.field_power

Sam Raleigh (Apr 26 2020 at 20:45):

Aha! I was wondering why exponentiation didn't seem to be defined for int, thanks

Sam Raleigh (Apr 26 2020 at 20:50):

Why do I still get a similar issue for this?
example (a b : ℤ) : 2^(a - b) = 2^a / 2^b :=

Sam Raleigh (Apr 26 2020 at 20:51):

failed to synthesize type class instance for
a b : ℤ
⊢ has_pow ℕ ℤ

Sam Raleigh (Apr 26 2020 at 20:52):

Is there another import I'm missing?

Bryan Gin-ge Chen (Apr 26 2020 at 20:52):

Lean thinks the 2s are nats. You might just have to include the coercions explicitly:

example (a b : ) : (2 : )^(a - b) = (2 : )^a / (2 : )^b :=

edit: sorry, you'll want , not for the 2s.

Sam Raleigh (Apr 26 2020 at 20:54):

Thanks! How come I didn't get this issue earlier with the exponents involving binomial coefficients?

Bryan Gin-ge Chen (Apr 26 2020 at 20:54):

I guess it's because Lean was able to correctly infer the rats from the LHS: (2^(n.choose(2) - k.choose(2) : ℤ) * 2 * n.choose(k) : ℚ)

Sam Raleigh (Apr 26 2020 at 20:57):

neat, thank you

Sam Raleigh (Apr 26 2020 at 21:01):

I'm surprised example (a b : ℤ) : (2 : ℚ)^(a - b) = (2 : ℚ)^a / (2 : ℚ)^b := by library_search turned up nothing. Is there really no theorem for this?

Kenny Lau (Apr 26 2020 at 21:03):

#check @fpow_sub
/- fpow_sub :
  ∀ {G₀ : Type u_1} [_inst_1 : group_with_zero G₀] {a : G₀},
    a ≠ 0 → ∀ (z1 z2 : ℤ), a ^ (z1 - z2) = a ^ z1 / a ^ z2
 -/

Sam Raleigh (Apr 26 2020 at 21:05):

How come by library_searchdidn't turn this up? How did you find this?

Sam Raleigh (Apr 26 2020 at 21:06):

Also what does the ampersand do? Sorry I'm extremely new to this

Kenny Lau (Apr 26 2020 at 21:07):

the @ shows all the parameters instead of just the explicit parameters (i.e. the ones you need to specify)

Bryan Gin-ge Chen (Apr 26 2020 at 21:07):

I guess it doesn't show up in by library_suggest by library_search because you need to convince Lean that 2 ≠ 0. It's the 3rd thing that comes up in by suggest.

Bryan Gin-ge Chen (Apr 26 2020 at 21:08):

import algebra.field_power

example (a b : ) : (2 : )^(a - b) = (2 : )^a / (2 : )^b :=
fpow_sub dec_trivial a b
-- this also works:
-- fpow_sub two_ne_zero a b

Sam Raleigh (Apr 26 2020 at 21:12):

What are the differences between by library_search, by library_suggest and by suggest? I was unaware that the last two existed until now.

Bryan Gin-ge Chen (Apr 26 2020 at 21:13):

Whoops, library_suggest doesn't exist. That was a typo on my part. You can read more about them in the tactic docs: https://leanprover-community.github.io/mathlib_docs/tactics.html#suggest

Sam Raleigh (Apr 26 2020 at 21:13):

thank you!

Kevin Buzzard (Apr 26 2020 at 21:19):

library_search finds the exact lemma which will solve your goal. It doesn't know that 2 isn't 0 so it won't be able to solve it. I suspect 0^{a-b}=0^a/0^b is false in general. suggestjust looks for lemmas that look like they might make progress, and doesn't claim to solve the goal in general (indeed you'd be left with the goal 2 \ne 0 if you applied fpow_sub directly).

There is another method though, and although it is a lot harder to master, it is in some sense the most powerful. The method is to learn the conventions used in naming lemmas in the library, and then start to guess what the name of the lemma you are looking for is. For example pow means raising something to a natural power, and fpow means raising something to an integer power. add is addition, sub is subtraction, not to be confused with neg which is unary negation. div is division, not to be confused with dvd, which means "divides" (like 3 divides 6), and so on and so on. After a while you can start predicting theorem names, and then typing a guess and hitting ctrl-space might bring up a list of possibilities, which (possibly after pressing ctrl-space again) you can start to scroll through until you hit the one you want.

Bryan Gin-ge Chen (Apr 26 2020 at 21:21):

This mathlib doc might help re: the naming conventions.

Brandon Brown (Apr 26 2020 at 22:53):

Looks for some hints for this one.
example : (∃ x, p x) ↔ ¬ (∀ x, ¬ p x) := sorry
My guess is its a longer proof, so just looking for some general guidance. I find proofs involving \not challenging for some reason.
Here's my starting point:

        example : ( x, p x)  ¬ ( x, ¬ p x) :=
            iff.intro
            (
                assume h1,
                match h1 with x, p1 :=
                    by_contradiction
                    (
                        _
                    )
                end
            )
            (
                assume h1,
                _
            )

Which gives me a first goal of
⊢ (¬¬∀ (x : α), ¬p x) → false
and second goal of
⊢ ∃ (x : α), p x

Marc Huisinga (Apr 26 2020 at 23:18):

hint: the first direction does not require classical reasoning

Kevin Buzzard (Apr 26 2020 at 23:28):

Does finish or tauto! do it?

Brandon Brown (Apr 26 2020 at 23:37):

You mean like by finish ? Doesn't seem to work

Kevin Buzzard (Apr 26 2020 at 23:39):

not P is by definition P -> false, if this helps

Kevin Buzzard (Apr 26 2020 at 23:39):

But these super-verbose term mode proofs are hard to steer

Sam Raleigh (Apr 27 2020 at 00:31):

What does "maximum class-instance resolution depth has been reached" mean?

Bryan Gin-ge Chen (Apr 27 2020 at 00:36):

@Brandon Brown by finish (or even by simp) works after you import tactic. Here are some hints towards an "honest" (or "tedious" depending on your tastes) term proof:

open classical
variables (α : Type) (x : α) (p : α  Prop)
example : ( x, p x)  ¬ ( x, ¬ p x) :=
    iff.intro
    (
        assume h1 h2,
        exists.elim h1 _
    )
    (
        assume h1,
        by_contradiction (assume h2 : ¬ _, h2 (h1 _).elim)
    )

Bryan Gin-ge Chen (Apr 27 2020 at 00:37):

@Sam Raleigh It can mean different things, depending on the state of your code. Sometimes it means you have a bad instance. Sometimes it means that there's some other issue with your code. Can you post a MWE (minimum working example)?

Brandon Brown (Apr 27 2020 at 00:46):

thanks @Bryan Gin-ge Chen !

Sam Raleigh (Apr 27 2020 at 00:57):

Actually I think I figured it out, thank you Bryan

Sam Raleigh (Apr 27 2020 at 00:57):

I am curious as to what you meant by "bad instance" though

Bryan Gin-ge Chen (Apr 27 2020 at 01:03):

If you're not careful about the instances you declare, Lean's type class search can get into an infinite loop. I can't come up with an example off the top of my head, but searching Zulip for "type class loop" will pull up a bunch of conversations where these sorts of things get debugged. (If by chance you haven't learned about Lean's type classes yet, here's the relevant section of TPiL).

Sam Raleigh (Apr 27 2020 at 01:09):

lemma strict_ineq
    (a b c : )
    (h : (2^(1 - b) * c : ) < 1) :
    (2^(a - b) * 2 * c : ) < 2^a :=
begin
    rw fpow_sub two_ne_zero a b
    --other stuff
end

I don't understand how I am getting the following type mismatch error:

type mismatch at application
fpow_sub two_ne_zero
term
two_ne_zero
has type
2 ≠ 0
but is expected to have type
?m_2 ≠ 0

How are 2 \neq 0 and ?m_2 ≠ 0 a mismatch?

Bryan Gin-ge Chen (Apr 27 2020 at 01:19):

Hmm, looks like there's something funky going on with the zero instances:

import data.rat.basic
import algebra.field_power

-- set_option pp.all true
lemma strict_ineq
    (a b c : )
    (h : (2^(1 - b) * c : ) < 1) :
    (2^(a - b) * 2 * c : ) < 2^a :=
begin
    rw [fpow_sub (begin
      convert two_ne_zero,
/-
Tactic State:
4 goals
a b c : ℤ,
h : 2 ^ (1 - b) * ↑c < 1
⊢ no_zero_divisors.to_has_zero ?m_1 = mul_zero_class.to_has_zero ?m_1

a b c : ℤ,
h : 2 ^ (1 - b) * ↑c < 1
⊢ group_with_zero ?m_1

a b c : ℤ,
h : 2 ^ (1 - b) * ↑c < 1
⊢ Type ?

a b c : ℤ,
h : 2 ^ (1 - b) * ↑c < 1
⊢ linear_ordered_semiring ?m_1

-/
    end) a b],
    --other stuff
end

Bryan Gin-ge Chen (Apr 27 2020 at 01:34):

This works, but this issue deserves more attention from someone who understands what's going on above:

import data.rat.basic
import algebra.field_power

-- set_option pp.all true
lemma strict_ineq
    (a b c : )
    (h : (2^(1 - b) * c : ) < 1) :
    (2^(a - b) * 2 * c : ) < 2^a :=
begin
    rw [fpow_sub (@two_ne_zero  _) a b],
    -- other stuff
end

Mario Carneiro (Apr 27 2020 at 01:51):

rw @fpow_sub ℚ _ _ two_ne_zero a b also works. Think about what the elaborator has to do here. The error happens at fpow_sub two_ne_zero, where

fpow_sub :
   {G₀ : Type u_1} [_inst_1 : group_with_zero G₀] {a : G₀},
    a  0   (z1 z2 : ), a ^ (z1 - z2) = a ^ z1 / a ^ z2
two_ne_zero :  {α : Type u_1} [_inst_1 : linear_ordered_semiring α], 2  0

Since the term fpow_sub two_ne_zero a b has no expected type (because it is in a rw), lean has to just elaborate it to find out what type it has. The a and b are no help to find the target type since they are always of type int in the lemma, so we just have a general variable α, which is apparently a linear_ordered_semiring and also a group_with_zero in order to make the applications make sense. But wait: we have an application here of one theorem to the other, and so 2 ≠ 0 in one theorem needs to match a ≠ 0 in the other, where the 0's are being provided by different typeclasses (recall that at this point we know only that α has two unrelated typeclasses, linear_ordered_semiring and group_with_zero, on it). At this point lean fails, because asking it to solve a commutative diagram in typeclass inference is too much. (It needs a type which has both typeclasses, such that the zero projections out of them are defeq.)

Bryan Gin-ge Chen (Apr 27 2020 at 01:58):

Thanks, that makes sense! It's surprising to me that rw can't find from the goal somehow.

Mario Carneiro (Apr 27 2020 at 01:58):

It first elaborates the term, then matches the target type against the subterms

Mario Carneiro (Apr 27 2020 at 01:58):

Otherwise it would have to re-elaborate the term many times

Kenny Lau (Apr 27 2020 at 05:02):

variables (α : Type) (x : α) (p : α  Prop)
example : ( x, p x)  ¬ ( x, ¬ p x) :=
⟨λ x, hx ha, ha x hx,
λ ha, classical.by_contradiction $ λ he, ha $ λ x hx, he x, hx⟩⟩

Kenny Lau (Apr 27 2020 at 05:03):

to write term mode proofs you just need to use a lot of underscores

Kenny Lau (Apr 27 2020 at 05:27):

and think backwards from the goal

Brandon Brown (Apr 27 2020 at 07:07):

I'm not familiar with the $ operator, and I don't see it used in TPIL

Kenny Lau (Apr 27 2020 at 07:09):

it just means open parenthesis

Kenny Lau (Apr 27 2020 at 07:09):

so it's the same as:

variables (α : Type) (x : α) (p : α  Prop)
example : ( x, p x)  ¬ ( x, ¬ p x) :=
⟨λ x, hx ha, ha x hx,
λ ha, classical.by_contradiction (λ he, ha (λ x hx, he x, hx))

Brandon Brown (Apr 27 2020 at 07:14):

ah it just saves a bunch of parentheses I see

AMM (Apr 27 2020 at 11:00):

Kevin Buzzard said:

@AMM I think https://artagnon.com/articles/leancoq does a good job of comparing Lean and Coq. In particular, I learnt from this article that Lean breaks "good type theoretic properties like strong normalization, subject reduction, and canonicity", and I have heard from Coq people that this is a big deal for some, but as a mathematician this breakage has never bothered me in the slightest. Another big difference is that "generic mathematicians" use Lean (people who do not specialise in type theory or higher categories or whatever -- they are just number theorists or analysts or geometers or topologists) -- and for me this is another of the main attractions.

Thank you very much!! This is really helpful. I also found your Newsletter of LMS article really useful when thinking about what the future looks like for interactive theorem proving!!

Dan Stanescu (Apr 27 2020 at 15:11):

I have a function defined like this: (X Y : set ℝ) (f : X → Y) and would like to use image_subset_range to get f(X) ⊆ Ybut not sure how to handle the set type casts that are involved. Can someone please help?

Kenny Lau (Apr 27 2020 at 15:15):

don't use partial functions

Chris Hughes (Apr 27 2020 at 15:16):

If f : X -> Y then f(X)f(X), will be written in Lean as set.range f. This is because when you write f : X -> Y, then X is being treated as a type rather than a set.

Chris Hughes (Apr 27 2020 at 15:16):

There's a coercion that turns sets into types

Chris Hughes (Apr 27 2020 at 15:17):

set.range f has type set Y, so it doesn't actually make sense in Lean straight away to say set.range f \subseteq Y since they have different types.

Dan Stanescu (Apr 27 2020 at 15:17):

So if I have my function like this I can't get that result? Because I saw that I could use f : \real \to \real instead.

Chris Hughes (Apr 27 2020 at 15:18):

It might be more sensible to use f : real -> real

Chris Hughes (Apr 27 2020 at 15:18):

And then I guess you'll need the assumption that f(X)Yf(X) \subseteq Y

Dan Stanescu (Apr 27 2020 at 15:22):

OK, thanks! Unfortunately that will make the result I'm eventually after quite a bit more difficult.

Kenny Lau (Apr 27 2020 at 15:22):

@Dan Stanescu do you have more context?

Patrick Massot (Apr 27 2020 at 15:24):

Dan Stanescu said:

OK, thanks! Unfortunately that will make the result I'm eventually after quite a bit more difficult.

This is a very natural belief, but almost surely wrong.

Dan Stanescu (Apr 27 2020 at 15:26):

@Kenny Lau The larger context is this:

import data.real.basic
import topology.basic
open function
open set

theorem countable_inj (X Y : set ) (f : X  Y) (hY : countable Y) :
    injective f  countable X :=
begin
   sorry,
end

Kenny Lau (Apr 27 2020 at 15:27):

it has nothing to do with R so might as well let X and Y be arbitrary types

Dan Stanescu (Apr 27 2020 at 15:27):

I agree. Never mind that.

Kenny Lau (Apr 27 2020 at 15:28):

also countable isn't defined

Dan Stanescu (Apr 27 2020 at 15:29):

I get it with my imports.

Patrick Massot (Apr 27 2020 at 15:29):

https://leanprover-community.github.io/mathlib_docs/data/set/countable.html

Chris Hughes (Apr 27 2020 at 15:31):

I never understood set.countable, set.finite, set.infinite etc. Shouldn't all of these things be about types?

Kenny Lau (Apr 27 2020 at 15:31):

import data.real.basic
import topology.basic
import data.set.countable
open function
open set

universes u v

theorem countable_inj (X Y : set ) (f : X  Y) (hY : countable Y) :
    injective f  countable X :=
λ hf, let g, hg := countable_iff_exists_injective.1 hY in
countable_iff_exists_injective.2 g  f, injective_comp hg hf

Patrick Massot (Apr 27 2020 at 15:31):

We need both, and the set version covers the type use through univ. But maybe we need more API around that.

Chris Hughes (Apr 27 2020 at 15:32):

But the type version covers sets by coercion.

Dan Stanescu (Apr 27 2020 at 15:33):

Kenny Lau said:

import data.real.basic
import topology.basic
import data.set.countable
open function
open set

universes u v

theorem countable_inj (X Y : set ) (f : X  Y) (hY : countable Y) :
    injective f  countable X :=
λ hf, let g, hg := countable_iff_exists_injective.1 hY in
countable_iff_exists_injective.2 g  f, injective_comp hg hf

Thanks Kenny! I think this will actually do.

Patrick Massot (Apr 27 2020 at 15:33):

Coercions can be nasty, so basing everything on them is dangerous

Frank Dai (Apr 27 2020 at 17:56):

Is there a way to have implicit arguments for \Sigma with the \< and \> syntax?

Kevin Buzzard (Apr 27 2020 at 17:56):

Just use _?

Sam Raleigh (Apr 28 2020 at 05:42):

universe u
variables {α : Type u} (K : finset α) {n : } [decidable_eq α]

def mono_sub_colorings (n : ) (k : ) : finset (finset (finset α)) := sorry

lemma num_mono_sub_colorings (n k : ) (h : n  k)
  := (card (mono_sub_colorings n k) : ) = (((2 : )^((n.choose(2)) - (k.choose(2))) : ) * n.choose(k)
begin
  sorry
end

Getting the following error:
"
type mismatch at application
2 ^ (choose n 2 - choose k 2) * ⇑(choose n k) ?m_1
term
⇑(choose n k) ?m_1
has type

but is expected to have type

"

Why is choose n k being coerced to \R? How can I fix this?

Sam Raleigh (Apr 28 2020 at 05:46):

What's even more bizarre to me is that if I remove the coercion from (card (mono_sub_colorings n k) : ℚ), then I get n.choose(k) is of type \Q.

Mario Carneiro (Apr 28 2020 at 06:12):

⇑(choose n k) ?m_1 this looks like choose is getting more arguments than it should

Mario Carneiro (Apr 28 2020 at 06:13):

You are missing a punctuation between the n.choose(k) and begin on the next line

Mario Carneiro (Apr 28 2020 at 06:13):

universe u
variables {α : Type u} (K : finset α) {n : } [decidable_eq α]

def mono_sub_colorings (n : ) (k : ) : finset (finset (finset α)) := sorry

lemma num_mono_sub_colorings (n k : ) (h : n  k)
  : (card (mono_sub_colorings n k) : ) = (((2 : )^((n.choose(2)) - (k.choose(2))) : ) * n.choose(k) :=
begin
  sorry
end

Mario Carneiro (Apr 28 2020 at 06:14):

It is a bit surprising that nat has a has_coe_to_fun involving real though

Mario Carneiro (Apr 28 2020 at 06:15):

what are your imports?

Scott Morrison (Apr 28 2020 at 07:37):

Bryan Gin-ge Chen said:

I guess it doesn't show up in by library_suggest by library_search because you need to convince Lean that 2 ≠ 0. It's the 3rd thing that comes up in by suggest.

Thanks to @Lucas Allen's recent PR improving library_search, you can now do things like:

import algebra.field_power

example (a b : ) : (2 : )^(a - b) = (2 : )^a / (2 : )^b :=
by library_search {discharger := `[exact dec_trivial]}

which works, reporting fpow_sub (of_as_true trivial) a b.

Scott Morrison (Apr 28 2020 at 07:41):

Another variant of library_search now available is

import algebra.field_power

example (a b : ) : (2 : )^(a - b) = (2 : )^a / (2 : )^b :=
by library_search [two_ne_zero]

Scott Morrison (Apr 28 2020 at 07:42):

which finds fpow_sub two_ne_zero a b.

Johan Commelin (Apr 28 2020 at 08:05):

@Lucas Allen Thank you very very much!

Mario Carneiro (Apr 28 2020 at 08:23):

does by library_search {discharger := `[sorry]} work as expected?

Johan Commelin (Apr 28 2020 at 08:24):

example : false := by library_search {discharger := `[sorry]}

should hopefully return exact sorry... I guess

Johan Commelin (Apr 28 2020 at 08:25):

Indeed:

Try this: exact sorry

Mario Carneiro (Apr 28 2020 at 08:25):

no, the discharger should only be called on the subgoals of the theorem that was found

Johan Commelin (Apr 28 2020 at 08:25):

So you want to find id sorry?

Johan Commelin (Apr 28 2020 at 08:26):

Or absurd true sorry?

Mario Carneiro (Apr 28 2020 at 08:26):

I'm worried that this is what would happen

Johan Commelin (Apr 28 2020 at 08:26):

Why are you worried?

Mario Carneiro (Apr 28 2020 at 08:26):

because that's a useless answer

Johan Commelin (Apr 28 2020 at 08:27):

It sounds like you are looking for suggest, which works reasonably well.

Mario Carneiro (Apr 28 2020 at 08:27):

what I want is fpow_sub sorry a b.

Johan Commelin (Apr 28 2020 at 08:27):

That is probably what suggest would suggest

Scott Morrison (Apr 28 2020 at 08:37):

fpow_sub _ a b is the fifth of so result for me from suggest.

Scott Morrison (Apr 28 2020 at 08:37):

You're not going to do better than that with discharger := `[sorry].

Scott Morrison (Apr 28 2020 at 08:38):

(i.e. in order to get fpow_sub sorry a b back from library_search, it's going to have to be the first result in suggest.)

Scott Morrison (Apr 28 2020 at 08:39):

Actually, I think there must be a bug, because fpow_sub _ a b should come before everything that suggest puts ahead of it, on account of using more hypotheses.

Scott Morrison (Apr 28 2020 at 08:39):

I'll look into it.

Kenny Lau (Apr 28 2020 at 08:42):

cf. the corresponding Coq tactic:

Require Import Arith.

Search (_ / _ = 0).

(*
Nat.div_small: forall a b : nat, a < b -> a / b = 0
Nat.div_0_l: forall a : nat, a <> 0 -> 0 / a = 0
Nat.div_1_l: forall a : nat, 1 < a -> 1 / a = 0
Nat.b2n_div2: forall a0 : bool, Nat.b2n a0 / 2 = 0
Nat.div_small_iff: forall a b : nat, b <> 0 -> a / b = 0 <-> a < b
*)

Mario Carneiro (Apr 28 2020 at 08:43):

I think suggest should also score matches based on how "deep" the match is, i.e. the number of constructor-equal-constructor unification subproblems. I'm not sure how best to phrase this in terms of the available information

Scott Morrison (Apr 28 2020 at 08:43):

Oh, no, the problem is that I count "hypotheses used" just by checking if they appear in the partial result, and appearances in implicit arguments count too, so eq.symm _ gets counted as using a and b just as much as fpow_sub _ a b.

Scott Morrison (Apr 28 2020 at 08:43):

Which is dumb...

Scott Morrison (Apr 28 2020 at 08:43):

there are some easy improvements to make, at least

Mario Carneiro (Apr 28 2020 at 08:44):

It's possible that simply weighting larger theorems as better (provided that they still have few remaining hypotheses) will do the trick

Reid Barton (Apr 28 2020 at 10:24):

Sam Raleigh said:

 := (card (mono_sub_colorings n k) : ) = (((2 : )^((n.choose(2)) - (k.choose(2))) : ) * n.choose(k)

There are so many parentheses here that it's hard to notice that the parentheses are not even balanced. You can simply write

(card (mono_sub_colorings n k) : ) = ((2 : )^(n.choose 2 - k.choose 2) : ) * n.choose k

All I did was remove parentheses and it's already much easier to read, but now you could notice that some of the : ℚs are redundant, as well.

Kenny Lau (Apr 28 2020 at 10:31):

(2 : ℚ) is redundant

Stephanie Zhou (Apr 28 2020 at 12:36):

Is there any reason why by_library search would not work anymore? About halfway through my proof, Lean just doesn't seem to recognize the command anymore.

Kevin Buzzard (Apr 28 2020 at 12:40):

What is the first error in your file?

Reid Barton (Apr 28 2020 at 12:41):

Are you actually writing by_library search, or by library_search?

Stephanie Zhou (Apr 28 2020 at 12:42):

It's at the very end, which is just "tactic failed, there are unsolved goals"

Stephanie Zhou (Apr 28 2020 at 12:42):

And yeah, I misspelled, I was writing the latter @Reid Barton

Mario Carneiro (Apr 28 2020 at 12:42):

you should be writing the latter

Mario Carneiro (Apr 28 2020 at 12:43):

what error do you get when you write it?

Stephanie Zhou (Apr 28 2020 at 12:46):

Unknown identifier "library_search"

Kevin Buzzard (Apr 28 2020 at 12:46):

Are you in tactic mode? Can you just post code instead of making us play this guessing game?

Kevin Buzzard (Apr 28 2020 at 12:46):

Post fully working code so we can all see the error

Stephanie Zhou (Apr 28 2020 at 12:48):

Okay, should I post the whole thing or a screenshot? How should I do it?

Kevin Buzzard (Apr 28 2020 at 12:49):

```
<paste code here>
```

Stephanie Zhou (Apr 28 2020 at 12:49):

import analysis.normed_space.basic
import topology.instances.ennreal
import analysis.normed_space.basic
import topology.instances.ennreal
import algebra.archimedean algebra.geom_sum
import data.nat.choose data.complex.basic
import tactic.linarith
import analysis.calculus.deriv
import data.complex.exponential

open finset
open cau_seq
namespace complex
noncomputable theory


lemma is_cau_abs_cos (z : ) : is_cau_seq _root_.abs
  (λ n, (range n).sum (λ m, abs (
      ((-1) ^ m) * z ^ (2 * m ) / nat.fact (2 * m )))) :=
begin
sorry,
end

lemma is_cau_abs_sin (z : ) : is_cau_seq _root_.abs
  (λ n, (range n).sum (λ m, abs (
      ((-1) ^ m) * z ^ (2 * m + 1) / nat.fact (2 * m + 1)))) :=
begin
sorry,
/-
let ⟨n, hn⟩ := exists_nat_gt (abs z) in
have hn0 : (0 : ℝ) < n, from lt_of_le_of_lt (abs_nonneg _) hn,
series_ratio_test n (complex.abs z / n)
(div_nonneg_of_nonneg_of_pos (complex.abs_nonneg _) hn0)
  (by rwa [div_lt_iff hn0, one_mul])
  (λ m hm,
    by rw [abs_abs, abs_abs, nat.fact_succ, pow_succ,
      mul_comm m.succ, nat.cast_mul, ← div_div_eq_div_mul, mul_div_assoc,
      mul_div_right_comm, abs_mul, abs_div, abs_cast_nat];
    exact mul_le_mul_of_nonneg_right
      (div_le_div_of_le_left (abs_nonneg _) hn0
        (nat.cast_le.2 (le_trans hm (nat.le_succ _)))) (abs_nonneg _))
-/
end

lemma is_cau_sin (z : ) :
 is_cau_seq abs (λ n, (range n).sum (λ m,
 ((-1) ^ m) * z ^ (2 * m + 1) / nat.fact (2 * m + 1)))
:=
begin
    exact is_cau_series_of_abv_cau (is_cau_abs_sin z),
end

lemma is_cau_cos (z : ) :
 is_cau_seq abs (λ n, (range n).sum (λ m,
 ((-1) ^ m) * z ^ (2 * m ) / nat.fact (2 * m)))
:=
begin
    exact is_cau_series_of_abv_cau (is_cau_abs_cos z),
end

def sin' (z : ) : cau_seq  complex.abs :=
 ⟨λ n, (range n).sum
 (λ m, ((-1) ^ m) * z ^ (2 * m + 1) / nat.fact (2 * m + 1)),
 is_cau_sin z
def sin1 (z : ) :  := lim (sin' z)

def cos' (z : ) : cau_seq  complex.abs :=
 ⟨λ n, (range n).sum
 (λ m, (-1) ^ m * z ^ (2 * m) / nat.fact (2 * m)),
 is_cau_cos z
def cos1 (z : ) :  := lim (cos' z)

theorem euler :  x, exp (x * I) = cos1 x + sin1 x * I
:=
begin
    intros,

    have partials:  n: , (exp' (x*I)).1 (2*n+1) =
     (cos' x).1 (n+1) + ((sin' x).1 n) * I,
    {
        intros,
        rw exp',
        simp,
        rw cos',
        simp,
        rw sin',
        simp,

        induction n with n0 hn,
        { -- case n0=0
            simp,
        },
        { -- induction on n0
            rw sum_range_succ _ _, -- takes out last term in cos
            have lastSin :
            sum (range (nat.succ n0)) (λ (x_1 : ), (-1) ^ x_1 * x ^ (1 + 2 * x_1) / (nat.fact (1 + 2 * x_1)))
            =
            (-1) ^ n0 * x ^ (1 + 2 * n0) / (nat.fact (1 + 2 * n0))
            +
            sum (range (n0)) (λ (x_1 : ), (-1) ^ x_1 * x ^ (1 + 2 * x_1) / (nat.fact (1 + 2 * x_1)))
            ,
            {
              rw sum_range_succ _ _,
            },
            have sinFactorial :
            sum (range (nat.succ n0)) (λ (x_1 : ), (-1) ^ x_1 * x ^ (2 * x_1 + 1) / ((2 * x_1 + 1) * (nat.fact (2 * x_1))))
            =
            sum (range (nat.succ n0)) (λ (x_1 : ), (-1) ^ x_1 * x ^ (1 + 2 * x_1) / (nat.fact (1 + 2 * x_1))),
            {
              sorry,
              --lean should fucking know this!
            },
            rw sinFactorial,
            rw lastSin,

            have twoFromExp:
            sum (range (nat.succ (2 * nat.succ n0))) (λ (x_1 : ),
            (x * I) ^ x_1 / (nat.fact x_1))
            =
            (x * I) ^ ( 2 * nat.succ n0) / (nat.fact ( 2 * nat.succ n0))+
            sum (range ( 2 * nat.succ n0)) (λ (x_1 : ),
            (x * I) ^ x_1 / (nat.fact x_1))
            ,
            {
              rw sum_range_succ _ ( 2 * nat.succ n0),
            },
            have twoFromExpv1 :
            (x * I) ^ ( 2 * nat.succ n0) / (nat.fact ( 2 * nat.succ n0))+sum (range ( 2 * nat.succ n0)) (λ (x_1 : ), (x * I) ^ x_1 / (nat.fact x_1))
            =
            sum (range (nat.succ (2 * nat.succ n0))) (λ (x_1 : ),
            (x * I) ^ x_1 / (nat.fact x_1)),
             {
               by exact eq.symm twoFromExp,
             },

            have twoNP1 : 1+ (2 * nat.succ n0) =nat.succ (2 * nat.succ n0)
            ,
            {
              exact add_comm 1 (2 * nat.succ n0),
            },

            have twoNP1v1 : nat.succ ( 2 * nat.succ n0) = 1 + (2 * nat.succ n0),
            {
              by exact eq.symm twoNP1,
            },

            --rw twoNP1v1,
            rw twoFromExpv1,

            have oneFromExp:
            sum (range (2 * nat.succ n0)) (λ (x_1 : ), (x * I) ^ x_1 / (nat.fact x_1))
            =
            (x * I) ^ (1+2 * n0) / (nat.fact (1+2 * n0))
            +
            sum (range (1+ 2 * n0)) (λ (x_1 : ), (x * I) ^ x_1 / (nat.fact x_1))
            ,
            {
              have stupid : 2 * nat.succ n0 = nat.succ(1+2*n0),
              {
                have RRS : nat.succ(1+2*n0) = 1+(1+2*n0) ,
                {
                  sorry,
                },
                rw RRS,

                have realSt : 1+(1+2*n0) = 1+1+2*n0,
                {
                  simp,
                },
                rw realSt,
                exact nat.add_comm (nat.mul 2 n0) 2,
              },
              rw stupid,
              rw sum_range_succ _ (1 + 2 * n0),
            },
            --rw oneFromExp,
            --rw hn,
            --simp,

            --  need (x*I)^(2 n0 )= x^(2n0) (-1)^n0 etc, commutativity

            sorry,
            --ring,
        },
    },

    have partialExp : lim (exp' (x * I)) = exp (x*I),
    {
      sorry,
    },

    rw exp,
    rw partialExp,

    -- Now: need to take limits on both sides, they're same,
    -- but need to convince Lean that limit of exp' (2n+1) is same as
    -- limit of exp' (n) which is what exp is defined to be
end

end complex

Kevin Buzzard (Apr 28 2020 at 12:51):

I get an error on line 96 but it's not about library_search

Kevin Buzzard (Apr 28 2020 at 12:52):

How do I make the error Unknown identifier "library_search" appear on my screen?

Stephanie Zhou (Apr 28 2020 at 12:53):

Funny, I sent this code to my professor and he got a different error on a different line, which I didn't see on my computer

Kevin Buzzard (Apr 28 2020 at 12:53):

:-)

Stephanie Zhou (Apr 28 2020 at 12:53):

I believe after line 88, Lean stops recognizing "by library_search"

Kevin Buzzard (Apr 28 2020 at 12:54):

If people use different versions of Lean or mathlib they'll see different errors. Which version of Lean and mathlib are you using? [neither Lean nor mathlib are backwards compatible].

Stephanie Zhou (Apr 28 2020 at 12:56):

For lean, it's 0.15.8

Patrick Massot (Apr 28 2020 at 12:56):

No, this is the VS code extension version.

Mario Carneiro (Apr 28 2020 at 12:56):

??

Kevin Buzzard (Apr 28 2020 at 12:56):

You can click on terminal in the bottom window and type lean --version

Kevin Buzzard (Apr 28 2020 at 12:57):

terminal.png

Reid Barton (Apr 28 2020 at 12:57):

Presumably if we know the mathlib version, we can work out the Lean version.

Kevin Buzzard (Apr 28 2020 at 12:58):

You can "pull up" on the blue bar at the bottom if you don't have this little window

Kevin Buzzard (Apr 28 2020 at 12:58):

#print lean.version will also do it

Stephanie Zhou (Apr 28 2020 at 13:01):

Hm, #print lean.version just gives this

Stephanie Zhou (Apr 28 2020 at 13:01):

Capture.PNG

Johan Commelin (Apr 28 2020 at 13:02):

Seems like 3.7.2, which isn't too old.

Kevin Buzzard (Apr 28 2020 at 13:03):

Your code is taking a very long time to compile though. library_search might just be running out of memory.

Reid Barton (Apr 28 2020 at 13:03):

Hang on, isn't

euler.lean:88:8: error
invalid expression, unexpected token

an error which occurs earlier than the end of your file, and not "tactic failed, there are unsolved goals"?

Stephanie Zhou (Apr 28 2020 at 13:04):

Right, that line compiles fine on my end, so I thought something was weird

Mario Carneiro (Apr 28 2020 at 13:04):

but you took the screenshot?

Reid Barton (Apr 28 2020 at 13:04):

I'm confused, aren't you showing screenshots of your own screen?

Stephanie Zhou (Apr 28 2020 at 13:05):

Yes, I am

Stephanie Zhou (Apr 28 2020 at 13:05):

Oh sorry, I see what yo mean now

Kevin Buzzard (Apr 28 2020 at 13:05):

She's written #print in the tactic proof

Patrick Massot (Apr 28 2020 at 13:05):

And how does Lean should know all those sum are finset sums? Did you open finset somewhere?

Stephanie Zhou (Apr 28 2020 at 13:05):

Right, #print lean.version seems to give a compile error

Kevin Buzzard (Apr 28 2020 at 13:05):

this works, but causes an error as well.

Reid Barton (Apr 28 2020 at 13:05):

Ohhh haha okay. I see.

Kevin Buzzard (Apr 28 2020 at 13:06):

Yeah, you can get rid of #print now.

Kevin Buzzard (Apr 28 2020 at 13:06):

Right -- in 3.9.0 the first problem is on line 96, with Lean not knowing about the sum

Reid Barton (Apr 28 2020 at 13:08):

open finset is on line 11 (if I counted correctly).

Kevin Buzzard (Apr 28 2020 at 13:09):

@Patrick Massot what's the easiest way to get Lean 3.7.2 running with e.g. the last mathlib commit for 3.7.2?

Sam Raleigh (Apr 28 2020 at 13:10):

Mario Carneiro said:

what are your imports?

Thanks! my imports are:

import data.nat.basic
import data.int.basic
import data.rat.basic

import algebra.field_power

import tactic

import data.finset
import algebra.big_operators
import data.real.basic
import data.set.lattice

Kevin Buzzard (Apr 28 2020 at 13:10):

I've made a new project, I can edit the toml to change 3.9.0 to 3.7.2 but now what?

Stephanie Zhou (Apr 28 2020 at 13:10):

Ah, if I go into terminal I get this

Stephanie Zhou (Apr 28 2020 at 13:10):

Capture.PNG

Johan Commelin (Apr 28 2020 at 13:11):

Hmm, that doesn't mean too much

Kevin Buzzard (Apr 28 2020 at 13:11):

I'm not sure who to trust

Reid Barton (Apr 28 2020 at 13:11):

Right, so lean --version was actually not a useful question.

Johan Commelin (Apr 28 2020 at 13:11):

Because if you installed elan than the version of Lean depends on which directory you are in.

Kevin Buzzard (Apr 28 2020 at 13:11):

the terminal or the #print output

Reid Barton (Apr 28 2020 at 13:11):

All that matters is the mathlib version.

Patrick Massot (Apr 28 2020 at 13:11):

Kevin, this use case is not supported by our tooling, you'll need to do it by hand (going to _target/deps/mathlib/ checkout the lean-3.7.2 branch, then leanproject get-matghlib-cache)

Reid Barton (Apr 28 2020 at 13:12):

(I mean assuming the user has elan installed and working correctly, but otherwise we'd have more obvious problems.)

Kevin Buzzard (Apr 28 2020 at 13:12):

Are you working in a project @Stephanie Zhou ? Can you send the leanpkg.toml of the project?

Patrick Massot (Apr 28 2020 at 13:12):

She ran this terminal thing from a random folder so it doesn't mean anything relevant

Mario Carneiro (Apr 28 2020 at 13:12):

@Sam Raleigh I'm still missing something. Do you have an MWE for the first example?

Patrick Massot (Apr 28 2020 at 13:13):

But this whole discussion is a bit pointless. There is clearly a syntax error somewhere that confuses Lean. The problem will disappear simply by cleaning things up

Stephanie Zhou (Apr 28 2020 at 13:13):

leanpkg.toml

Patrick Massot (Apr 28 2020 at 13:13):

Mario, did you actually wanted to ping Sam here?

Patrick Massot (Apr 28 2020 at 13:14):

If yes then we really need to get rid of this "noob question(s)" topic

Patrick Massot (Apr 28 2020 at 13:15):

The whole stream is for noob questions, and putting all of them in the same topic is losing the organization by topic

Kevin Buzzard (Apr 28 2020 at 13:15):

So Patrick I want to see if there's a syntax error with Stephanie's mathlib commit. If I change directory to _target/deps/mathlib and checkout the commit in her toml, I can just use leanproject get-mathlib-cache?

Mario Carneiro (Apr 28 2020 at 13:15):

I think that this topic is better than (no topic) for people who choose not to use topics, but we need a protocol for branching out a conversation to a real topic

Patrick Massot (Apr 28 2020 at 13:15):

yes

Kevin Buzzard (Apr 28 2020 at 13:16):

Yes there are two questions going on at once here

Sam Raleigh (Apr 28 2020 at 13:16):

I could shift my questions over to a different topic if that would help

Reid Barton (Apr 28 2020 at 13:16):

Now there are 3 :upside_down:

Kevin Buzzard (Apr 28 2020 at 13:19):

OK so with Stephanie's mathlib commit, there are no errors before line 198

Stephanie Zhou (Apr 28 2020 at 13:20):

Right, that's what I get too

Kevin Buzzard (Apr 28 2020 at 13:21):

but library_search works for me on line 194, it returns failed because the result you want isn't in the library

Kevin Buzzard (Apr 28 2020 at 13:22):

In general I would recommend not making such long proofs though, especially long proofs with the occasional sorry in. Lean gets slow in long proofs.

Kevin Buzzard (Apr 28 2020 at 13:24):

For example, your super-long proof on line 73 starts immediately with a have partials: ... . Why not make that its own lemma? Its proof is over 100 lines long which is still way too long.

Kevin Buzzard (Apr 28 2020 at 13:25):

--lean should fucking know this! rofl

Kevin Buzzard (Apr 28 2020 at 13:25):

Our library still needs some work :-)

Reid Barton (Apr 28 2020 at 13:25):

We've all been there.

Stephanie Zhou (Apr 28 2020 at 13:25):

Hm okay I see, Lean just doesn't return anything for by library_search, so I guess it timed out

Kevin Buzzard (Apr 28 2020 at 13:26):

Oh, I might have default timeout set to higher

Kevin Buzzard (Apr 28 2020 at 13:26):

I am an optimist

Stephanie Zhou (Apr 28 2020 at 13:28):

Okay, how do I tinker with the default timeout setting?

Kevin Buzzard (Apr 28 2020 at 13:28):

            sum (range (nat.succ n0)) (λ (x_1 : ), (-1) ^ x_1 * x ^ (2 * x_1 + 1) / ((2 * x_1 + 1) * (nat.fact (2 * x_1))))
            =
            sum (range (nat.succ n0)) (λ (x_1 : ), (-1) ^ x_1 * x ^ (1 + 2 * x_1) / (nat.fact (1 + 2 * x_1))),

Lean shouldn't know this, it will follow from (1) commutativity of addition (2) nat.fact_succ (3) some statement about coercions commuting with equality. All three facts are known to Lean but it's too stupid to put everything together in the right order.

Kevin Buzzard (Apr 28 2020 at 13:28):

Or maybe it should know this -- but we haven't managed to put together a tactic which does everything obvious at once.

Kevin Buzzard (Apr 28 2020 at 13:29):

I agree that any maths student should know this!

Patrick Massot (Apr 28 2020 at 13:31):

You can probably use congr' here to extract things from this mess.

Kevin Buzzard (Apr 28 2020 at 13:35):

It won't make library_search work :-) I went to File -> Preferences -> Settings and then searched for Lean Time Limit and changed the number to 900000. All that happens then is that library_search takes longer to fail. You should fix the cause of the problem though.

Kevin Buzzard (Apr 28 2020 at 13:36):

You are writing like a mathematician, with all this forward reasoning.

Kevin Buzzard (Apr 28 2020 at 13:37):

You make hypothesis after hypothesis, proving them all with relatively simple rewrites or sorry

Kevin Buzzard (Apr 28 2020 at 13:37):

and then you rewrite each hypothesis to change your goal a bit.

Kevin Buzzard (Apr 28 2020 at 13:38):

It might be easier to use suffices a lot; that way your local context won't become clogged up with all these intermediate lemmas.

Kenny Lau (Apr 28 2020 at 13:39):

or prove more lemmas!

Stephanie Zhou (Apr 28 2020 at 13:41):

Do you mean replace all the sorrys with suffices?

Kevin Buzzard (Apr 28 2020 at 13:42):

Stuff like this:

              have stupid : 2 * nat.succ n0 = nat.succ(1+2*n0),
              {
                have RRS : nat.succ(1+2*n0) = 1+(1+2*n0) ,
                {
                  sorry,
                },
                rw RRS,

                have realSt : 1+(1+2*n0) = 1+1+2*n0,
                {
                  simp,
                },
                rw realSt,
                exact nat.add_comm (nat.mul 2 n0) 2,
              },

just clogs up the local context. The ring tactic will solve all goals of this form, as long as you rw nat.succ_eq_add_one first to change the succs to +1's. But why make stupid a new hypothesis at all? The bigger you make your local context, the more difficult it is to see where you're going.

Steffan (Apr 28 2020 at 13:43):

But isn't sorry just a placeholder?

Kevin Buzzard (Apr 28 2020 at 13:43):

I mean that instead of

begin
  have A,
  { proof of A}
  rw A,
  have B,
  { proof of B},
  rw B,
  have C,
  ...

you could do other things (which won't leave A,B,C lying around as long hypotheses you'll never use again

Patrick Massot (Apr 28 2020 at 13:43):

Seeing all those nat.succ don't look good either. This function is really an implementation detail of natural numbers, it shouldn't show up outside of data.nat.basic

Kevin Buzzard (Apr 28 2020 at 13:44):

They are there because it's a complicated induction proof.

Kevin Buzzard (Apr 28 2020 at 13:44):

After the induction tactic in one of the have goals, the goal is

 sum (range (2 * nat.succ n0 + 1)) (λ (m : ), (x * I) ^ m / (nat.fact m)) =
    sum (range (nat.succ n0 + 1)) (λ (m : ), (-1) ^ m * x ^ (2 * m) / (nat.fact (2 * m))) +
      sum (range (nat.succ n0))
          (λ (x_1 : ), (-1) ^ x_1 * x ^ (2 * x_1 + 1) / ((2 * x_1 + 1) * (nat.fact (2 * x_1)))) *
        I

Steffan (Apr 28 2020 at 13:45):

open nat, that's what I do

Kevin Buzzard (Apr 28 2020 at 13:45):

that will get rid of the nat but not the succ

Steffan (Apr 28 2020 at 13:45):

I know
But it looks cleaner to me.

Kevin Buzzard (Apr 28 2020 at 13:45):

You could rw nat.succ_eq_add_one right now and you'll hopefully never see them again

Kevin Buzzard (Apr 28 2020 at 13:49):

and then all of this lastSin -- you only make that proof so you can rewrite it, and you prove it with a rewrite, so you could just rewrite in your goal directly and never make lastSin at all.

Kevin Buzzard (Apr 28 2020 at 13:49):

Just work directly on the goal instead of making all these new hypotheses

Steffan (Apr 28 2020 at 13:53):

The ring tactic will solve all goals of this form

Turns out omega can solve that one too

Adeeb K (Apr 28 2020 at 15:12):

question - I have a proof p : f x = f y for an injective function f. What tactics would I use to conclude that x = y?
for reference, here's what I wrote for injectivity:

def injective {X Y} (f : X  Y) :=  x₁ x₂, f x₁ = f x₂  x₁ = x₂

Bryan Gin-ge Chen (Apr 28 2020 at 15:15):

injective f x y p is a proof of x = y.

Bryan Gin-ge Chen (Apr 28 2020 at 15:16):

(In your tactic proof, you would write exact injective f x y p)

Kevin Buzzard (Apr 28 2020 at 15:17):

injective f is a function which eats two variables x₁ and x₂ and a proof that f x₁ = f x₂, and spits out a proof that x₁ = x₂

Brandon Brown (Apr 28 2020 at 15:18):

Can someone elucidate how the recursor is working for addition on the natural numbers?

-- nat.rec : ?M_1 0 → (Π (n : ℕ), ?M_1 n → ?M_1 (nat.succ n)) → Π (n : ℕ), ?M_1 n
def add (n m : nat) : nat :=
nat.rec_on n m (λ n z, nat.succ z)

Adeeb K (Apr 28 2020 at 15:19):

ah makes sense. so then I have p2 : f x = f y -> x = y and p : f x = f y. What would be the syntax to get the proposition that x = y?

Kevin Buzzard (Apr 28 2020 at 15:20):

#check @nat.rec_on
/-
nat.rec_on : Π {C : ℕ → Sort u_1} (n : ℕ), C 0 → (Π (n : ℕ), C n → C (nat.succ n)) → C n
-/

Kevin Buzzard (Apr 28 2020 at 15:20):

p2 is also a function

Kevin Buzzard (Apr 28 2020 at 15:20):

What do you think p2 takes as an input and what do you think it gives as an output?

Adeeb K (Apr 28 2020 at 15:20):

proof that f x = f y?

Kevin Buzzard (Apr 28 2020 at 15:21):

Right, that's the input

Kevin Buzzard (Apr 28 2020 at 15:21):

Now f x = f y is a type in Lean's type theory, and p : f x = f y is a term of that type. Do you know what that means?

Adeeb K (Apr 28 2020 at 15:22):

Yes, at least I believe I do?

Kevin Buzzard (Apr 28 2020 at 15:22):

So what is p?

Kevin Buzzard (Apr 28 2020 at 15:22):

in normal language?

Adeeb K (Apr 28 2020 at 15:23):

p is a proposition of type f x = f y

Kevin Buzzard (Apr 28 2020 at 15:23):

no, p is not a proposition.

Kevin Buzzard (Apr 28 2020 at 15:23):

f x = f y is a proposition.

Kevin Buzzard (Apr 28 2020 at 15:23):

p : f x = f y doesn't mean that p is equal to f x = f y

Bryan Gin-ge Chen (Apr 28 2020 at 15:24):

@Brandon Brown Are you already looking at the relevant section of Theorem Proving in Lean? If so, can you say more about what you'd like elucidated?

Kevin Buzzard (Apr 28 2020 at 15:24):

Lean needs to figure out the motive C, as you can see from the #check output with @ turned on.

Brandon Brown (Apr 28 2020 at 15:24):

Yes I am - I thought I understood everything up until that point in TPIL. Maybe I just need to stare at it longer

Adeeb K (Apr 28 2020 at 15:24):

is it more that p is of type f x = f y?

Kevin Buzzard (Apr 28 2020 at 15:25):

It knows that m : C 0

Kevin Buzzard (Apr 28 2020 at 15:25):

so C 0 = ℕ

Kevin Buzzard (Apr 28 2020 at 15:26):

and we know that (λ n z, nat.succ z) has type (Π (n : ℕ), C n → C (nat.succ n))

Adeeb K (Apr 28 2020 at 15:29):

oh, so now I have that x = y. Here, x and y are dependent pairs (x1, x2) and (y1, y2). How would I conclude that x1 = y1?

Kevin Buzzard (Apr 28 2020 at 15:30):

Well the problem is that you might not have names for x1 and y1 yet

Billy Price (Apr 28 2020 at 15:30):

@Adeeb K That's the exactly what using : in the statement p : f x = f y means. So now the question is "what is the data that inhabits the type f x = f y, which is a proposition" - i.e. what "is" p? In lean we don't actually have to define that, but it makes sense to call it a "proof" (of that proposition).

Kevin Buzzard (Apr 28 2020 at 15:31):

so you might want to do cases x with x1 x2 and cases y with y1 y2 first

Brandon Brown (Apr 28 2020 at 15:31):

But it looks like the first argument going to nat.rec_on is n which corresponds to Π {C : ℕ → Sort u_1}, C 0

Adeeb K (Apr 28 2020 at 15:31):

thanks @Billy Price , that makes sense!
and @Kevin Buzzard I'll try that out now

Reid Barton (Apr 28 2020 at 15:31):

In fact, what you will want to do next depends on your exact goal

Kevin Buzzard (Apr 28 2020 at 15:31):

and then the next step will depend on exactly what the type of x and y was. If they're subtypes then rw subtype.ext will do it, or perhaps ext

Billy Price (Apr 28 2020 at 15:32):

And if you think about, deciding that the inhabitants of a proposition are proofs, gives you the correspondence between implication A==>B and functions A -> B

Kevin Buzzard (Apr 28 2020 at 15:32):

The squiggly brackets means "don't fill this in at all, Lean will fill it in for you"

Kevin Buzzard (Apr 28 2020 at 15:32):

so n in add is the same as n in nat.rec_on

Kevin Buzzard (Apr 28 2020 at 15:33):

and because nat.succ z has type , Lean can figure out that C (nat.succ _) = ℕ

Kevin Buzzard (Apr 28 2020 at 15:34):

so Lean can figure out from the information given that C is the constant function sending every natural to

Brandon Brown (Apr 28 2020 at 15:34):

hmm ok, thanks - I need to chew on this a bit longer

Adeeb K (Apr 28 2020 at 15:35):

Okay I used cases to introduce x1 and x2, and I do have names for y1 and y2. How would I conclude that x1 = y1?

Kevin Buzzard (Apr 28 2020 at 15:35):

which is a roundabout way of saying that the function we're defining by induction is a function from the naturals to the naturals

Kevin Buzzard (Apr 28 2020 at 15:35):

Adeeb K said:

Okay I used cases to introduce x1 and x2, and I do have names for y1 and y2. How would I conclude that x1 = y1?

That depends on the type of x

Adeeb K (Apr 28 2020 at 15:36):

it's a pair (k, k < m). So x1 is a natural

Reid Barton (Apr 28 2020 at 15:36):

Probably cc will do it, if you want a black-box answer.

Kevin Buzzard (Apr 28 2020 at 15:36):

So now we have to figure out what this function is. We can think of the function as a sequence of naturals. The first one is m, and then the inductive step (or rather the recursive step) sends z to succ(z), so the sequence is m, succ m, succ succ m, ...

Reid Barton (Apr 28 2020 at 15:36):

(Also, if you used cases on x and y, then presumably they were variables before and it would likely have been easier to subst the equality instead.)

Adeeb K (Apr 28 2020 at 15:37):

for context, I am trying to get x1 = y1 because I have a proof that x1 < y1, which is a contradiction

Reid Barton (Apr 28 2020 at 15:37):

How can you have x1, though?

Reid Barton (Apr 28 2020 at 15:37):

Do you mean you have a proof of x.1 < y.1?

Kevin Buzzard (Apr 28 2020 at 15:38):

so add n m is the n'th term in that sequence

Adeeb K (Apr 28 2020 at 15:39):

I have a proof that x.1 < y.1

Kevin Buzzard (Apr 28 2020 at 15:40):

well if x just disappeared then you might have to dsimp that proof now

Reid Barton (Apr 28 2020 at 15:40):

Great, I am with you then. These might seem like terribly nitpicky questions but it's only because the correct tactics to use depend on details like this.

Kevin Buzzard (Apr 28 2020 at 15:40):

Adeeb K said:

it's a pair (k, k < m). So x1 is a natural

The question is not what the type of x1 is, it's what the type of x is.

Kevin Buzzard (Apr 28 2020 at 15:41):

Did you roll your own pair, or use fin m?

Adeeb K (Apr 28 2020 at 15:41):

so about a week ago I started a topic about pigeonhole, since I was writing it in lean for a seminar project. For the most part, it's done (the proof of pigeonhole itself is done), but I need to squash out some of the sorries from my helper functions

Adeeb K (Apr 28 2020 at 15:41):

I used my own pair

Kevin Buzzard (Apr 28 2020 at 15:41):

did you prove the extensionality lemma for your pair?

Adeeb K (Apr 28 2020 at 15:41):

def finite_subset (n : ) := Σ' k, k < n

Adeeb K (Apr 28 2020 at 15:42):

not sure what that is exactly

Reid Barton (Apr 28 2020 at 15:42):

This isn't the direction for an extensionality lemma.

Kevin Buzzard (Apr 28 2020 at 15:42):

it's an ext_iff, right?

Reid Barton (Apr 28 2020 at 15:43):

You say you have p : x = y, and you want x.1 = y.1. There are several ways to do this, such as rewriting using p, or using congr_arg.

Adeeb K (Apr 28 2020 at 15:43):

so the outline of my plan is this:

Reid Barton (Apr 28 2020 at 15:44):

Another thing you could do is subst p, which will replace one of the variables (probably y) everywhere it occurs by x, leaving you with x.1 < x.1.

Adeeb K (Apr 28 2020 at 15:44):

eventually it came down to showing there is no injection from [m + 2] = {0, 1, ..., m + 1} to [m + 1], where I had an inductive hypothesis that there is no injection from [m + 1] to [m].

Reid Barton (Apr 28 2020 at 15:44):

Maybe it will replace x, I'm not sure.

Kevin Buzzard (Apr 28 2020 at 15:45):

lemma ext_iff (n : ) (a b : finite_subset n) : a = b  a.1 = b.1 :=
begin
  cases a,
  cases b,
  split,
  { intro h, rw h},
  { intro h, cases h, refl,}
end

Adeeb K (Apr 28 2020 at 15:46):

okay, I'll try those now!

Adeeb K (Apr 28 2020 at 15:48):

wait, I'm not sure how to use ext_iff in tactics
I tried let p4 := ext_iff p3 where p3 : x = y

Kevin Buzzard (Apr 28 2020 at 15:48):

rw

Adeeb K (Apr 28 2020 at 15:48):

I'm sorry, it feels like I'm asking the same question over and over again..

Kevin Buzzard (Apr 28 2020 at 15:49):

I think we're there now though

Adeeb K (Apr 28 2020 at 15:49):

rw failed I think?

Kevin Buzzard (Apr 28 2020 at 15:49):

rw ext_iff at p3

Adeeb K (Apr 28 2020 at 15:49):

I used rw ext_iff

Kevin Buzzard (Apr 28 2020 at 15:49):

that rewrites the goal

Adeeb K (Apr 28 2020 at 15:49):

ah right my bad

Kevin Buzzard (Apr 28 2020 at 15:50):

Alternatively have p4 := (ext_iff m x y).1 p3 will probably work

Adeeb K (Apr 28 2020 at 15:52):

so I have that x.1 and y.1 are the same. Here's the sketch of my proof:

Adeeb K (Apr 28 2020 at 15:54):

I wish to show that for an injective function f : [m + 2] -> [m + 1], the restriction of f to [m + 1]in the domain means that that there is no j in [m + 1] exists such that f (j, j < m + 2) = f (m + 1, m + 1 < m + 2)

Adeeb K (Apr 28 2020 at 15:56):

a human proof would go like the following: suppose not. Then we have by injectivity of f that (j, j < m + 2) = (m + 1, m + 1 < m + 2), which means j = m + 1, but we know that j < m + 1. Contradiction

Adeeb K (Apr 28 2020 at 15:57):

Here is the relevant code:

Adeeb K (Apr 28 2020 at 15:57):

def lift_one
(m : )
: finite_subset m  finite_subset (m + 1)
:= (lift_finite m (m+1) (succ_greater_than_nat m))

lemma miss_proof
(l : )
{m} (f : finite_subset (m + 2)  finite_subset (m + 1))
(inj : injective f)
{pf: m + 1 < m + 2}
:  j : finite_subset (m + 1), (f  lift_one (m + 1)) j  f m + 1,  pf
:= begin
introv p,
change f (lift_one (m+1) j) = f m + 1,  pf at p,
let p2 := inj (lift_one (m+1) j) m + 1,  pf,
let p3 := p2 p,
cases (lift_one (m + 1) j) with x1 x2,
rw ext_iff at p3,


end

Adeeb K (Apr 28 2020 at 15:58):

(I know I have to clean it up a bit)

Adeeb K (Apr 28 2020 at 15:59):

So the issue is that I have in the goal window that p3 : ⟨x1, x2⟩.fst = ⟨m + 1, pf⟩.fst, but how do I relate both sides to j and m + 1 respectively?

Reid Barton (Apr 28 2020 at 16:02):

So, Kevin gave you bad advice to use cases because you said you had a proof of x = y, suggesting x and y are variables, but actually you have a proof of lift_one (m + 1) j = ⟨m + 1, pf⟩.

Adeeb K (Apr 28 2020 at 16:03):

it was actually probably my bad for not sharing this in the first place, though

Adeeb K (Apr 28 2020 at 16:05):

either way, it doesn't seem linarith is seeing a contradiction after rw ext_iff at p3

Reid Barton (Apr 28 2020 at 16:06):

You should delete the cases line if you haven't already.

Adeeb K (Apr 28 2020 at 16:07):

yeah, that's done

Adeeb K (Apr 28 2020 at 16:07):

I now have p3 : (lift_one (m + 1) j).fst = ⟨m + 1, pf⟩.fst

Adeeb K (Apr 28 2020 at 16:10):

linarithisn't helping me here still

Reid Barton (Apr 28 2020 at 16:10):

Well, linarith doesn't know what to make of (lift_one (m + 1) j).fst of course.

Reid Barton (Apr 28 2020 at 16:12):

First you need to turn it into j.fst or whatever it should be.

Adeeb K (Apr 28 2020 at 16:13):

I see

Adeeb K (Apr 28 2020 at 16:13):

I'm not sure how to start with that

Reid Barton (Apr 28 2020 at 16:14):

Well, starting is the easy part:

lemma lift_one_fst ..... : (lift_one m j).fst = j.fst :=
...

Reid Barton (Apr 28 2020 at 16:14):

You haven't actually given us the definition of lift_one, I think.

Adeeb K (Apr 28 2020 at 16:15):

lemme do so

Adeeb K (Apr 28 2020 at 16:16):

lemma succ_greater_than_nat (n : ) : nat.succ n > n
:=
begin
  rw nat.succ_eq_add_one,
  linarith
end

/--
Type of pairs (k,p) where k
is a natural number and p is a witness to the proof that k < n.
-/
def finite_subset (n : ) := Σ' k, k < n

/--
Every pair that lives in finite_subest m lives in finite_subset n
where m < n
-/
def lift_finite (m n : ) (p : m < n) : finite_subset m  finite_subset n
    := λ k, k.1, lt.trans k.2 p

/--
Application of lift_finite from m to m + 1
-/
def lift_one
(m : )
: finite_subset m  finite_subset (m + 1)
:= (lift_finite m (m+1) (succ_greater_than_nat m))

Reid Barton (Apr 28 2020 at 16:19):

In that case, proving the lemma will be quite easy: it's true "by definition".

Adeeb K (Apr 28 2020 at 16:23):

oh? just refl?

Adeeb K (Apr 28 2020 at 16:24):

one moment

Adeeb K (Apr 28 2020 at 16:31):

okay, I have the lemma typed out

Adeeb K (Apr 28 2020 at 16:32):

lemma lift_one_fst {m} (j : finite_subset m) : (lift_one m j).fst = j.fst
:=
begin
refl,
end

Adeeb K (Apr 28 2020 at 16:32):

so now, I would:

rw (lift_one_fst j) p3

Adeeb K (Apr 28 2020 at 16:33):

or would I not?

Adeeb K (Apr 28 2020 at 16:36):

oh I got it

Adeeb K (Apr 28 2020 at 16:37):

let equal := lift_one_fst j,
rw equal at p3,

Adeeb K (Apr 28 2020 at 16:38):

okay cool, looks like this theorem is done with:

...
rw ext_iff at p3,
let equal := lift_one_fst j,
rw equal at p3,
let p4 := j.2,
linarith,

Reid Barton (Apr 28 2020 at 16:38):

You can just write rw lift_one_fst at p3. rw will figure out what arguments to insert for you.

Adeeb K (Apr 28 2020 at 16:41):

ah I see

Adeeb K (Apr 28 2020 at 16:41):

okay cool, that worked for this theorem! I'll likely be back with some more questions as I encounter problems in the main lemma

adriana (Apr 28 2020 at 17:13):

can anyone help me prove this lemma for the IVT? I'm very new and don't yet understand much.

def continuous2 (f:ℝ → ℝ ) :=
∀ x : ℝ, ∀ ε >0, ∃ δ > 0, ∀ y, abs (x-y) <δ → abs (f x - f y) < ε

lemma continuous_implies {f : ℝ → ℝ} (Hf : continuous2 f)
{a b : ℝ} (Hab : a < b) (K := {x : ℝ | x>a ∧ x<b ∧ f x < 0})
(c:= Sup K):
f c = 0
:=
by_contradiction

Patrick Massot (Apr 28 2020 at 17:19):

@adriana did you meant to post this twice? Using a dedicated topic was the right idea.

Adeeb K (Apr 28 2020 at 18:05):

hi small question

Adeeb K (Apr 28 2020 at 18:05):

suppose I have a variable named x_2 and just want to rename it y. How would I do so?

Reid Barton (Apr 28 2020 at 18:16):

I assume there is a rename tactic or something, but normally one would arrange for the variable to have the correct name in the first place; how did you get x_2?

Adeeb K (Apr 28 2020 at 18:17):

I used the intros tactic

Adeeb K (Apr 28 2020 at 18:18):

err

Adeeb K (Apr 28 2020 at 18:18):

the introv tactic

Adeeb K (Apr 28 2020 at 18:18):

but more than that, I have this:

orlando (Apr 28 2020 at 18:19):

intros y, ?

Adeeb K (Apr 28 2020 at 18:19):

let p := relabel (m + 1) k pf (f (lift_one (m + 1) x)) = relabel (m + 1) k pf (f (lift_one (m + 1) x₂)),
let x_l := (lift_one (m + 1) x),
let y_l := (lift_one (m + 1) x₂),

Adeeb K (Apr 28 2020 at 18:20):

How would I be able to then get the statement:

relabel (m + 1) k pf (f x_l) = relabel (m + 1) k pf (f y_l)

Adeeb K (Apr 28 2020 at 18:21):

I should add that I have p from my tactic state window

Reid Barton (Apr 28 2020 at 18:21):

p

Adeeb K (Apr 28 2020 at 18:21):

p?

Reid Barton (Apr 28 2020 at 18:22):

p already equals what you wrote

Reid Barton (Apr 28 2020 at 18:24):

if you have a proof of p and you really need a proof of relabel (m + 1) k pf (f x_l) = relabel (m + 1) k pf (f y_l) in particular then you can use change

Reid Barton (Apr 28 2020 at 18:24):

but most other tactics will not notice the difference anyways

Reid Barton (Apr 28 2020 at 18:25):

let is a tricky thing and it would be better to understand everything else first. But in general you can pretend something defined with let is the same as its definition.

Adeeb K (Apr 28 2020 at 18:25):

ah, I see.

Adeeb K (Apr 28 2020 at 18:27):

thanks!

Adeeb K (Apr 28 2020 at 18:28):

Okay, so new question:

Adeeb K (Apr 28 2020 at 18:29):

I have a definition for a function using if-then-else

Adeeb K (Apr 28 2020 at 18:29):

particularly:

Adeeb K (Apr 28 2020 at 18:29):

def relabel
(m k : )
(p: k < m)
: finite_subset m  finite_subset (m - 1)
:= λ j, if H : j.1  k then j.1, my_le_trans j.1 k m p H  else j.1 - 1, inequality_fact j.1 m j.2

Adeeb K (Apr 28 2020 at 18:31):

the function you helped me with earlier tells me that with an injective function f, there is no j in {0, 1, ..., m} such that f(j) = f(m + 1)

Adeeb K (Apr 28 2020 at 18:32):

With that, I want to show that the composition (relabel (m + 1) k pf) ∘ f ∘ lift_one (m + 1) is injective. I have already have that f is injective, and I have a lemma that any lifting is injective as well

Adeeb K (Apr 28 2020 at 18:32):

this is what I have so far:

Adeeb K (Apr 28 2020 at 18:32):

lemma relabel_inj
(m k : )
(p: k < m)
(f: finite_subset (m + 2)  finite_subset (m + 1))
(inj: injective f)
{pf} (miss:  j : finite_subset (m + 1), (f  lift_one (m + 1)) j  k,  pf )
: injective ((relabel (m + 1) k pf)  f  lift_one (m + 1))
:=
begin

  introv x,
  let y := x₂,
  intros,
  change (relabel (m + 1) k pf (f (lift_one (m + 1) x))) = (relabel (m + 1) k pf (f (lift_one (m + 1) x₂))) at a,
  let x_l := (lift_one (m + 1) x),
  let y_l := (lift_one (m + 1) x₂),
  change relabel (m + 1) k pf (f x_l) = relabel (m + 1) k pf (f y_l) at a,

  sorry,
end

Adeeb K (Apr 28 2020 at 18:33):

at this step, I want to be able to break this up into cases

Adeeb K (Apr 28 2020 at 18:34):

where relabel (f x_l) <= k and and when relabel (f x_l) > k

Adeeb K (Apr 28 2020 at 18:34):

and then apply my proof that f x_l != k to show we only have the cases for < and >

Adeeb K (Apr 28 2020 at 18:34):

does that make sense?

Adeeb K (Apr 28 2020 at 18:35):

would cases help me here in any way?

Adeeb K (Apr 28 2020 at 18:35):

or refine?

Adeeb K (Apr 28 2020 at 18:47):

Is this a better question as a new topic?

Bryan Gin-ge Chen (Apr 28 2020 at 18:50):

Yes, pretty much all questions that require > 1 reply would work better in their own topics.

Adeeb K (Apr 28 2020 at 18:50):

gotcha, gonna make it now.

Stephanie Zhou (Apr 28 2020 at 19:53):

I have version 3.7.2, but is it possible to downgrade to version 3.4.2? Will downloading v3.4.2 from github automatically overwrite the version I have?

Kevin Buzzard (Apr 28 2020 at 20:15):

Why would you want to downgrade? But you can -- it's not hard (I did it today when I created a 3.7.2 project to look at your code). It's just not documented :-)

Kevin Buzzard (Apr 28 2020 at 20:15):

Do you know which commit of mathlib you want? As Reid said, this is the real question

Frank Dai (Apr 28 2020 at 22:22):

In my goal, I have a bunch of terms that look like this, after using simp

      {x := x, y := y, ar1 := q, ar2 := p}.ar1

Is there a way to get simp to unfold that to q?

Kevin Buzzard (Apr 28 2020 at 22:30):

dsimp

Adeeb K (Apr 28 2020 at 22:46):

if I have x ≤ k and want to split into cases where x < k and x = k, how would I do so?

Alex J. Best (Apr 28 2020 at 22:48):

 cases lt_or_eq_of_le h,

if h is the name of your inequality.

Adeeb K (Apr 28 2020 at 22:49):

got it, thanks!

Kevin Buzzard (Apr 28 2020 at 22:50):

import tactic

example (x k : ) : x  k  x < k  x = k := by library_search

is one way to answer questions like this yourself.

Kevin Buzzard (Apr 28 2020 at 22:52):

And another way is to learn the conventions for theorem naming. of means "is implied by", so you want to deduce something is less than, or equal to, given that it's lessthanorequalto, and the convention for less than is lt, and the convention for equals is eq, and the one for <= is le, so actually if you know these tricks then you can even guess the name of the theorem without using library_search.

Kevin Buzzard (Apr 28 2020 at 22:53):

It's not a perfect system -- for example it could have been eq_or_lt_of_le -- but it's a good start.

Kevin Buzzard (Apr 28 2020 at 22:53):

I guess after a while you've seen so many of them that you just start to learn them

Adeeb K (Apr 28 2020 at 22:53):

that makes sense. I've been going back and forth between the documentation, library search, and the natural number game.

Kevin Buzzard (Apr 28 2020 at 22:53):

Yeah, learning the names of all the 1000000 lemmas which you need to do anything at all is a tough part of the learning curve

Kevin Buzzard (Apr 28 2020 at 22:54):

in nng I only work with one very basic type and build most things from scratch

Kevin Buzzard (Apr 28 2020 at 22:54):

but you are working on a far harder problem so you need to know all sorts of things right from square 1, this is why it's so slow going

Adeeb K (Apr 28 2020 at 22:56):

yeah, it's definitely been an odyssey navigating through lean
that being said I've been having a lot of fun with this

Adeeb K (Apr 28 2020 at 22:57):

I underestimated how much work it would take to follow through even stating pigeonhole correctly - much less proving it all the way through

Bryan Gin-ge Chen (Apr 28 2020 at 22:57):

Feel free to suggest any improvements to docs here or on github!

Kevin Buzzard (Apr 28 2020 at 23:00):

Do you think it would be ok to add a bunch of examples to the (say core) docs? There is an art here

Kevin Buzzard (Apr 28 2020 at 23:01):

You don't want to say too much, but on the other hand I think that basic examples of usage of tactics would be helpful. In nng I tried to give an example of usage for every tactic I documented

Bryan Gin-ge Chen (Apr 28 2020 at 23:02):

I think that would be very helpful. More module docs that help "section" some of our giant files would be good too.

Kevin Buzzard (Apr 28 2020 at 23:02):

I pointed Adeeb to the unfold docs earlier and then Reid pointed out that they were not ideal for a beginner

Kevin Buzzard (Apr 28 2020 at 23:03):

A couple of basic examples would have been very handy there

Adeeb K (Apr 28 2020 at 23:04):

I think 1 - 2 good "vertical slice" examples (ie, one example using a tactic in its simplest form and another similar to how I wanted to use unfold) would be really nice

Adeeb K (Apr 28 2020 at 23:15):

Question: I have equality between dependent pairs <x.fst, _> = <y.fst - 1, _> and an inequality k - 1 < y.fst - 1. How would I be able to rewrite the inequality as k - 1 < x.fst? The change tactic is saying a simple match fails.

Adeeb K (Apr 28 2020 at 23:18):

Would the extensionality theorem ext_iffwork here?

Adeeb K (Apr 28 2020 at 23:19):

update - it didn't work

Adeeb K (Apr 28 2020 at 23:20):

I used extensionality to say <x.fst, _>.fst = <y.fst - 1, _>.fst, but then change k - 1 < x.fst failed.

Reid Barton (Apr 28 2020 at 23:23):

change only works with things that are definitionally equal.

Adeeb K (Apr 28 2020 at 23:24):

ah, would there be another tactic for propositional equality?

Reid Barton (Apr 28 2020 at 23:26):

You already know how to rewrite things.

Adeeb K (Apr 28 2020 at 23:28):

I see

Adeeb K (Apr 28 2020 at 23:28):

that being said, I still got an error:

rewrite tactic failed, did not find instance of the pattern in the target expression
  x.fst, _⟩.fst

Adeeb K (Apr 28 2020 at 23:29):

This was when I used rw p2 at h4, where p2 : ⟨x.fst, _⟩.fst = ⟨y.fst - 1, _⟩.fst and h4 : k - 1 < y.fst - 1

Adeeb K (Apr 28 2020 at 23:38):

is there anything I'm missing? Any extra things I need to verify are equal?

Adeeb K (Apr 28 2020 at 23:39):

do I have to show that ⟨y.fst - 1, _⟩.fst = y.fst - 1?

Mario Carneiro (Apr 28 2020 at 23:40):

rfl

Reid Barton (Apr 28 2020 at 23:42):

You can change p2 into the required form for rw.

Adeeb K (Apr 28 2020 at 23:43):

okay, I'll try the latter suggestion first since that makes more immediate sense to me

Adeeb K (Apr 28 2020 at 23:43):

however, out of curiosity, how would I use rfl in this case @Mario Carneiro

Mario Carneiro (Apr 28 2020 at 23:44):

have: ⟨y.fst - 1, _⟩.fst = y.fst - 1 := rfl, more or less

Mario Carneiro (Apr 28 2020 at 23:44):

or rw show ⟨y.fst - 1, _⟩.fst = y.fst - 1, from rfl

Mario Carneiro (Apr 28 2020 at 23:46):

change is probably easier to use in your situation

Adeeb K (Apr 28 2020 at 23:54):

I see.

Adeeb K (Apr 28 2020 at 23:55):

So, I haveh : x.fst ≤ k and h_4 : nat.sub k 0 < x.fst but linearith is not giving me a contradiction.

Reid Barton (Apr 28 2020 at 23:57):

linarith does not know about this silly nat.sub. How did you even get it?

Frank Dai (Apr 28 2020 at 23:58):

How do you use the let tactic?

let a := begin /- long proof here -/ end,
-- how do you a term of the form a = <long expression>?

Reid Barton (Apr 28 2020 at 23:59):

on the other hand I suppose linarith does not know about - on nat either. Just get rid of it

Adeeb K (Apr 29 2020 at 00:00):

I used nat.le_of_pred_lt h4 (where h4 : k - 1 < x.fst) to get k ≤ x.fst

Adeeb K (Apr 29 2020 at 00:01):

except I didn't get k ≤ x.fst

Adeeb K (Apr 29 2020 at 00:01):

I got nat.sub k 0 ≤ x.fst

Reid Barton (Apr 29 2020 at 00:01):

Before you said nat.sub k 0 < x.fst

Reid Barton (Apr 29 2020 at 00:02):

@Frank Dai a is definitionally equal to <long expression>, so you can use rfl.

Adeeb K (Apr 29 2020 at 00:02):

oh yeah, this line is then after I split into cases where nat.sub k 0 < x.fst or nat.sub k 0 = x.fst

Frank Dai (Apr 29 2020 at 00:02):

I can do that but that requires me to copy the giant machine-generated expression that is a

Reid Barton (Apr 29 2020 at 00:03):

@Adeeb K I suggest moving your questions to a new topic.

Adeeb K (Apr 29 2020 at 00:04):

sure thing, thanks

Alex J. Best (Apr 29 2020 at 00:08):

@Frank Dai what do you need a term of the form a = long exp for?

Frank Dai (Apr 29 2020 at 00:09):

inside a proof, I am defining a function by a induction, and trying to prove something about that function

Frank Dai (Apr 29 2020 at 00:10):

I'm defining the function by

let f := begin <long induction proof> end,

and I want to prove later f x = f y or something.

Frank Dai (Apr 29 2020 at 00:11):

I don't know how to get the goal from f x = f y to <long expression> x = <long expression> y

Kenny Lau (Apr 29 2020 at 00:14):

my advice from experience is to never use the let tactic

Reid Barton (Apr 29 2020 at 00:14):

unfold f, or similar

Frank Dai (Apr 29 2020 at 00:14):

how do I make a local definition without let?

Frank Dai (Apr 29 2020 at 00:15):

I can't unfold f beacuse it's a local definition, not a global one

Kenny Lau (Apr 29 2020 at 00:15):

don't make local definitions

Reid Barton (Apr 29 2020 at 00:15):

Try anyways?

Reid Barton (Apr 29 2020 at 00:15):

Local definitions are fine, but you should definitely think twice about whether you want to make a big one

Alex J. Best (Apr 29 2020 at 00:17):

I'm a little confused you are using let for a proof? Use have if it is a term of type Prop.
That said

example : false :=
begin
let f := λ n, n+2,
suffices : f 1 = f 2, sorry, -- this is irrelevant I just wanted a goal like you have
dsimp [f],
end

Frank Dai (Apr 29 2020 at 00:18):

that works if the goal is trivial, the problem is that I don't know how to easily rewrite f into \lam n, n + 2

Frank Dai (Apr 29 2020 at 00:18):

which you need to do for a non-trivial proof

Kenny Lau (Apr 29 2020 at 00:18):

the answer is dsimp only [f]

Kenny Lau (Apr 29 2020 at 00:19):

but my answer is make it a global definition

Frank Dai (Apr 29 2020 at 00:21):

more trivial question: how do you apply a tactic to all goals simultaneously

Alex J. Best (Apr 29 2020 at 00:21):

all_goals { tactic }

Kenny Lau (Apr 29 2020 at 00:22):

if the goals are produced by a single tactic then you can use ; instead of ,

Mario Carneiro (Apr 29 2020 at 00:23):

You should not use tactics to define functions if you can help it

Mario Carneiro (Apr 29 2020 at 00:23):

your inductive definition should be a separate auxiliary

Frank Dai (Apr 29 2020 at 00:24):

is the reason for this that there's more magic for global definitions?

Mario Carneiro (Apr 29 2020 at 00:27):

basically

Mario Carneiro (Apr 29 2020 at 00:28):

Most of the time when you do this local definition thing lean is making an auxiliary anyway

Mario Carneiro (Apr 29 2020 at 00:28):

when you write a recursive definition with the equation compiler, you get nice equations that don't require you to look at giant machine generated definitions

Frank Dai (Apr 29 2020 at 00:32):

after reaching a 250 line-long goal buffer, I see your point

Mario Carneiro (Apr 29 2020 at 00:34):

and it's more than visual overload; that term is much harder to work with using the normal tactics

Stephanie Zhou (Apr 29 2020 at 00:44):

@Kevin Buzzard how did you downgrade lean this morning?

Billy Price (Apr 29 2020 at 00:48):

I want a collection data type where I can define a function out of it which folds it into a proposition based on the index of each element in the collection. Which collection should I use and where should I look for examples of how to use it?

Mario Carneiro (Apr 29 2020 at 01:04):

maybe \forall i, a i?

Mario Carneiro (Apr 29 2020 at 01:06):

If you are thinking about the context data type for your type theory project, I would suggest sticking to lists

Mario Carneiro (Apr 29 2020 at 01:06):

there are ways to talk about the nth item of a list

Frank Dai (Apr 29 2020 at 01:44):

When do you use simp vs dsimp in general? They seem to have subtly different behavior. Are there general heuristics on when to use which one?

Kenny Lau (Apr 29 2020 at 01:46):

dsimp only simplifies using definitional equalities (lemmas proved by rfl)

Kenny Lau (Apr 29 2020 at 01:47):

(but my answer is to avoid them and always append only if you have to use them)

Frank Dai (Apr 29 2020 at 01:51):

What are the alternatives?

Kenny Lau (Apr 29 2020 at 01:52):

rw

Frank Dai (Apr 29 2020 at 01:56):

like for instance I have (lam x, <something>) xI want to rewrite to <something>

Kenny Lau (Apr 29 2020 at 01:57):

dsimp only,

Adeeb K (Apr 29 2020 at 02:33):

Question - if I have a function defined with the if-then-else pattern, and I have a proof that an input falls into the if case, how can I then conclude that I have an exact form for my function?

Adeeb K (Apr 29 2020 at 02:35):

that is, suppose I have the function

def function (z : N) : N := if z < 5 then z else z + 1

Now suppose I have

p : z < 5

How can I conclude that f z = z?

Reid Barton (Apr 29 2020 at 02:36):

simp [function] would be the easiest way

Adeeb K (Apr 29 2020 at 02:37):

So in the above, I would say

let g := f z

and then
simp g
?

Adeeb K (Apr 29 2020 at 02:37):

that gave me an error

Adeeb K (Apr 29 2020 at 02:39):

I'm not sure how to use simp here then

Kenny Lau (Apr 29 2020 at 02:45):

rw [function, if_pos p]

Reid Barton (Apr 29 2020 at 02:46):

Right, you need to include p too of course.

Adeeb K (Apr 29 2020 at 02:48):

would function here be g or f?

Kenny Lau (Apr 29 2020 at 02:48):

you shouldn't use let

Kenny Lau (Apr 29 2020 at 02:48):

]

Adeeb K (Apr 29 2020 at 02:48):

what should I use?

Kenny Lau (Apr 29 2020 at 02:50):

have : function z = z, rw [function, if_pos p]

Adeeb K (Apr 29 2020 at 02:52):

It's not working

Kenny Lau (Apr 29 2020 at 02:53):

MWE

Adeeb K (Apr 29 2020 at 02:53):

should I send you a MWE?

Adeeb K (Apr 29 2020 at 02:57):

import tactic


lemma succ_greater_than_nat (n : ) : nat.succ n > n
:=
begin
  rw nat.succ_eq_add_one,
  linarith
end

def injective {X Y} (f : X  Y) :=  x₁ x₂, f x₁ = f x₂  x₁ = x₂

def finite_subset (n : ) := { k // k < n }

def lift_finite (m n : ) (p : m < n) : finite_subset m  finite_subset n
    := λ k, k.1, lt.trans k.2 p


lemma ext_iff (n : ) (a b : finite_subset n) : a = b  a.1 = b.1 :=
begin
  cases a,
  cases b,
  split,
  { intro h, rw h},
  { intro h, cases h, refl,}
end

def lift_one
(m : )
: finite_subset m  finite_subset (m + 1)
:= (lift_finite m (m+1) (succ_greater_than_nat m))


lemma lift_one_fst {m} (j : finite_subset m) : (lift_one m j).1 = j.1
:= begin refl, end


def relabel (m k : ) (h : k < m) (j : finite_subset m) : finite_subset (m - 1) :=
if H : j.1  k then j.1, sorry else j.1 - 1, sorry

lemma miss_proof
{m} (f : finite_subset (m + 2)  finite_subset (m + 1))
(inj : injective f)
{pf: m + 1 < m + 2}
:  j : finite_subset (m + 1), (f  lift_one (m + 1)) j  f m + 1,  pf
:= begin

introv p,
change f (lift_one (m+1) j) = f m + 1,  pf at p,
let p2 := inj (lift_one (m+1) j) m + 1,  pf,
let p3 := p2 p,
rw ext_iff at p3,
rw lift_one_fst at p3,
let p4 := j.2,
linarith,

end

lemma relabel_behavior (m k : ) (h : k < m) (x y : finite_subset m) (hxy : relabel m k h x = relabel m k h y) :
  (x.1  k  y.1  k)  (x.1  k  y.1  k) :=
begin
  unfold relabel at hxy,
  split_ifs at hxy with hxk hyk hyk,
  { left; split; assumption },
  { right; split,


    { rw subtype.mk_eq_mk at hxy,


      cases lt_or_eq_of_le hxk with hxlk hxek,
      { rw hxy at hxlk, exact absurd (nat.le_of_pred_lt hxlk) hyk },
      { exact ge_of_eq hxek } },
    { exact le_of_not_ge hyk } },
  { right; split,
    { exact le_of_not_ge hxk },
    { rw subtype.mk_eq_mk at hxy,
      cases lt_or_eq_of_le hyk with hylk hyek,
      { rw  hxy at hylk, exact absurd (nat.le_of_pred_lt hylk) hxk },
      { exact ge_of_eq hyek } } },
  { right; split; apply le_of_not_ge; assumption }


end


lemma relabel_inj
(m k : )
(p: k < m)
(f: finite_subset (m + 2)  finite_subset (m + 1))
(inj: injective f)
{pf} (miss:  j : finite_subset (m + 1), (f  lift_one (m + 1)) j  k,  pf )
: injective ((relabel (m + 1) k pf)  f  lift_one (m + 1))
:=
begin



  introv x,
  let y := x₂,
  intros,

  let miss_x := miss x,
  let miss_y := miss y,

  change (relabel (m + 1) k pf (f (lift_one (m + 1) x))) = (relabel (m + 1) k pf (f (lift_one (m + 1) x₂))) at a,
  let x_l := (lift_one (m + 1) x),
  let y_l := (lift_one (m + 1) x₂),
  change relabel (m + 1) k pf (f x_l) = relabel (m + 1) k pf (f y_l) at a,

  let r_x_l := relabel (m + 1) k pf (f x_l),

  let r_y_l := relabel (m + 1) k pf (f y_l),



  --split_ifs at a,
  --change r_x_l = r_y_l at a,

  sorry,
end

Adeeb K (Apr 29 2020 at 02:58):

The issue I'm having is towards the bottom of relabel_inj after the last change

Kenny Lau (Apr 29 2020 at 03:09):

what's the point of let miss_x := miss x,?

Adeeb K (Apr 29 2020 at 03:10):

that will let me conclude that x.1 < k or x.1 > k

Kenny Lau (Apr 29 2020 at 03:11):

I don't see the point of any of your lets

Adeeb K (Apr 29 2020 at 03:12):

I started writing let's to make the statement simpler, but then the others here suggested I write lemmas to help out.

Adeeb K (Apr 29 2020 at 03:12):

relabel_behavior being one and miss_proof being another

Reid Barton (Apr 29 2020 at 03:12):

You should write a better lemma maybe.

Adeeb K (Apr 29 2020 at 03:12):

(miss is supposed to be a consequence of miss_proof)

Adeeb K (Apr 29 2020 at 03:13):

as in a better lemma than relabel_inj?

Kenny Lau (Apr 29 2020 at 03:13):

your lemma is fine

Reid Barton (Apr 29 2020 at 03:13):

No, I assume you already have a purpose for relabel_inj.

Reid Barton (Apr 29 2020 at 03:13):

I mean a lemma that you can use to prove relabel_inj

Kenny Lau (Apr 29 2020 at 03:13):

but my suggestion didn't work because it's supposed to be dif_pos

Adeeb K (Apr 29 2020 at 03:14):

I'm not exactly sure what that means or where to go from there

Kenny Lau (Apr 29 2020 at 03:15):

lemma relabel_inj (m k : ) (hkm : k < m) (f: finite_subset (m + 2)  finite_subset (m + 1)) (inj : injective f)
  {pf} (miss :  j : finite_subset (m + 1), (f  lift_one (m + 1)) j  k, pf ) :
  injective ((relabel (m + 1) k pf)  f  lift_one (m + 1)) :=
begin
  intros x y h,
  change (relabel (m + 1) k pf (f (lift_one (m + 1) x))) = (relabel (m + 1) k pf (f (lift_one (m + 1) y))) at h,
  rcases relabel_behavior _ _ _ _ _ h with h1, h2 | h1, h2⟩; unfold relabel at h,
  { rw [dif_pos h1, dif_pos h2, subtype.mk_eq_mk] at h, }
end

Kenny Lau (Apr 29 2020 at 03:15):

start with this

Kenny Lau (Apr 29 2020 at 03:16):

learn to work with long expressions

Adeeb K (Apr 29 2020 at 03:17):

okay, give me a second

Adeeb K (Apr 29 2020 at 03:17):

thanks again

Adeeb K (Apr 29 2020 at 03:19):

I'm getting an error within the parentheses on the bottom

solve1 tactic failed, focused goal has not been solved

Reid Barton (Apr 29 2020 at 03:20):

I think that is because the goal has not been solved.

Adeeb K (Apr 29 2020 at 03:20):

oh..I need to fill in the stuff there. right right, my bad

Adeeb K (Apr 29 2020 at 03:21):

wait question about this

Adeeb K (Apr 29 2020 at 03:21):

This gives me

h1 : (f (lift_one (m + 1) x)).val  k,
h2 : (f (lift_one (m + 1) y)).val  k,

Jalex Stark (Apr 29 2020 at 03:23):

Adeeb K said:

wait question about this

what's the question?

Adeeb K (Apr 29 2020 at 03:24):

how would I conclude that

(relabel m k p (f (lift_one (m + 1) x))) =   (f (lift_one (m + 1) x)).val,  pf : (f (lift_one (m + 1) x)).val < m ,

Reid Barton (Apr 29 2020 at 03:24):

Adeeb likes to warn us when a question is coming, so we are not too surprised.

Reid Barton (Apr 29 2020 at 03:25):

Weren't you planning to write a lemma for this?

Adeeb K (Apr 29 2020 at 03:26):

I was, but then Kenny mentioned it was only a single line, but he seemed to use what he told me to give me another code snippet?

Reid Barton (Apr 29 2020 at 03:26):

Everything is a single line to Kenny

Jalex Stark (Apr 29 2020 at 03:27):

just because something is a single line doesn't mean it's a bad lemma

Reid Barton (Apr 29 2020 at 03:27):

anyways, you need to use dif_pos

Jalex Stark (Apr 29 2020 at 03:27):

there are a lot of lemmas in mathlib whose proof is just rfl

Adeeb K (Apr 29 2020 at 03:27):

alright, I'll give it a shot. I'll post the statement in a second

Mario Carneiro (Apr 29 2020 at 03:27):

there are a lot of lemmas that are one line, possibly the majority

Jalex Stark (Apr 29 2020 at 03:29):

but if you think like a mathematician a lot of those one-liners are longer than one line

Mario Carneiro (Apr 29 2020 at 03:29):

yeah, they correspond to mathematician proofs of about 1 - 5 lines

Steffan (Apr 29 2020 at 03:30):

some easy ones are just by ring/by omega or something in Lean

Mario Carneiro (Apr 29 2020 at 03:30):

of course many of them have the one line proof "Trivial."

Mario Carneiro (Apr 29 2020 at 03:31):

I think it's pretty rare to see lemmas that are literally by heavy_tactic

Mario Carneiro (Apr 29 2020 at 03:31):

except possibly all the by obviously proofs in category theory

Adeeb K (Apr 29 2020 at 03:31):

okay, here we are:

lemma apply_relabel_gt
(m k : )
(p : k < m)
(z : finite_subset m)
(p2 : z.1  k)
(p3 : z.1 < m - 1)
: (relabel m k p) z =  z.1, p3 
:=
begin
sorry,
end

Adeeb K (Apr 29 2020 at 03:32):

Actually I'll make this an MWE

Adeeb K (Apr 29 2020 at 03:33):

import tactic


def finite_subset (n : ) := { k // k < n }

def relabel (m k : ) (h : k < m) (j : finite_subset m) : finite_subset (m - 1) :=
if H : j.1  k then j.1, sorry else j.1 - 1, sorry


lemma apply_relabel_gt
(m k : )
(p : k < m)
(z : finite_subset m)
(p2 : z.1  k)
(p3 : z.1 < m - 1)
: (relabel m k p) z =  z.1, p3 
:=
begin
    sorry,
end

Adeeb K (Apr 29 2020 at 03:33):

I'm not sure how to proceed with dif_pos.

Steffan (Apr 29 2020 at 03:35):

I think it's pretty rare to see lemmas that are literally by heavy_tactic

yeah, it seems kind of crazy to do this heavy ring to prove n + 1 + 1 = n + 2

but I don't terribly care about performance in theorem proving

Mario Carneiro (Apr 29 2020 at 03:36):

It starts to matter when you have a big library with many dependent files like mathlib

Steffan (Apr 29 2020 at 03:36):

yeah, but that != me atm :D

Mario Carneiro (Apr 29 2020 at 03:37):

but it's not (just) about performance; it's often the case that the reason this is a lemma is because it isn't easily killed by one tactic

Reid Barton (Apr 29 2020 at 03:37):

Or it is used by the implementation of the tactic that would kill it.

Adeeb K (Apr 29 2020 at 03:37):

I'm also not seeing any entry for dif_pos in the mathlib tactics documentation?

Mario Carneiro (Apr 29 2020 at 03:37):

it might be by induction n; heavy_tactic or by simpa using nonobvious term

Kenny Lau (Apr 29 2020 at 03:38):

import tactic

def finite_subset (n : ) := { k // k < n }

def relabel (m k : ) (h : k < m) (j : finite_subset m) : finite_subset (m - 1) :=
if H : j.1  k then j.1, sorry else j.1 - 1, sorry

lemma apply_relabel_gt (m k : ) (hkm : k < m) (z : finite_subset m) (h2 : z.1  k) (h3 : z.1 < m - 1) :
  relabel m k hkm z = z.1, h3 :=
begin
  unfold relabel, rw dif_pos h2
end

Reid Barton (Apr 29 2020 at 03:38):

by nonobviously :upside_down:

Steffan (Apr 29 2020 at 03:38):

you're right, it's kinda pointless to prove trivial stuff anyway

Mario Carneiro (Apr 29 2020 at 03:38):

but rfl is also a common way to prove nontrivial theorems trivially

Adeeb K (Apr 29 2020 at 03:38):

oh. thanks again Kenny

Mario Carneiro (Apr 29 2020 at 03:40):

@Adeeb K You don't often have to refer to dif_pos since simp knows about it

Adeeb K (Apr 29 2020 at 03:40):

oh wait, so when I try and do the other case

Adeeb K (Apr 29 2020 at 03:40):

I get an error

Mario Carneiro (Apr 29 2020 at 03:40):

unless you are avoiding simp like kenny

Steffan (Apr 29 2020 at 03:40):

like literally, rfl will solve 6 * finset.sum (range (0 + 1)) (λ (n : ℕ), n ^ 2) = 0 * (0 + 1) * (2 * 0 + 1)

Adeeb K (Apr 29 2020 at 03:41):

@Kenny Lau I tried using what you wrote for the case when z > k. Here it is:

import tactic

def finite_subset (n : ) := { k // k < n }

def relabel (m k : ) (h : k < m) (j : finite_subset m) : finite_subset (m - 1) :=
if H : j.1  k then j.1, sorry else j.1 - 1, sorry

lemma apply_relabel_gt (m k : ) (hkm : k < m) (z : finite_subset m) (h2 : z.1 > k) (h3 : z.1 < m - 1) :
  relabel m k hkm z = z.1, h3 :=
begin
  unfold relabel,
  rw dif_pos h2,
end

Mario Carneiro (Apr 29 2020 at 03:41):

The asymptotic complexity of checking that a proof by rfl is correct is gargantuan

Adeeb K (Apr 29 2020 at 03:42):

I'm getting an error on the last line rw dif_pos h2

Kenny Lau (Apr 29 2020 at 03:42):

import tactic

def finite_subset (n : ) := { k // k < n }

def relabel (m k : ) (h : k < m) (j : finite_subset m) : finite_subset (m - 1) :=
if H : j.1  k then j.1, sorry else j.1 - 1, sorry

lemma apply_relabel_gt (m k : ) (hkm : k < m) (z : finite_subset m) (h2 : k < z.1) (h3 : z.1 < m - 1) :
  relabel m k hkm z = z.1, h3 :=
begin
  unfold relabel,
  rw dif_neg (not_le_of_lt h2),
end

Reid Barton (Apr 29 2020 at 03:43):

Adeeb, you should read the error and look at the type of dif_pos.

Mario Carneiro (Apr 29 2020 at 03:43):

dif_pos says that a dependent if-then-else expression is equal to the then case when the condition is true

Mario Carneiro (Apr 29 2020 at 03:43):

if the condition is false of course you shouldn't try to prove it is true

Adeeb K (Apr 29 2020 at 03:43):

this gives me

m k : ,
hkm : k < m,
z : finite_subset m,
h2 : k < z.val,
h3 : z.val < m - 1
 z.val - 1, _⟩ = z.val, h3

Kenny Lau (Apr 29 2020 at 03:44):

if you see a false conclusion it's time to review your statement

Steffan (Apr 29 2020 at 03:45):

relabel has sorry in it

Kenny Lau (Apr 29 2020 at 03:45):

that doesn't matter

Mario Carneiro (Apr 29 2020 at 03:45):

actually it won't matter because of proof irrelevance

Mario Carneiro (Apr 29 2020 at 03:45):

but you should probably fix that sorry

Steffan (Apr 29 2020 at 03:46):

vscode is bugging me about it

Adeeb K (Apr 29 2020 at 03:47):

oh shoot right, I forgot to use a -1 when modifying

Adeeb K (Apr 29 2020 at 03:48):

so this is correct:

lemma apply_relabel_gt (m k : )
(hkm : k < m)
(z : finite_subset m)
(h2 : k < z.1)
(h3 : z.1 - 1 < m - 1)
: relabel m k hkm z = z.1 - 1, h3
:= begin
  unfold relabel,
  rw dif_neg (not_le_of_lt h2),
end

Kenny Lau (Apr 29 2020 at 03:50):

if VSCode doesn't complain about it then it's correct

Steffan (Apr 29 2020 at 03:53):

but I mean... it looks like you're proving that sorry = h3.

Mario Carneiro (Apr 29 2020 at 03:54):

this is true because sorry is a proof of z.1 - 1 < m - 1 and so is h3 so they are defeq by proof irrelevance

Mario Carneiro (Apr 29 2020 at 03:54):

because z.1 - 1 < m - 1 : Prop

Adeeb K (Apr 29 2020 at 03:55):

@Kenny Lau you said that I shouldn't use let. Then if I have h1 : (f (lift_one (m + 1) x)).val ≤ k, how would I extract a statement that relabel m k hkm (f (lift_one (m + 1) x)) = ⟨ (f (lift_one (m + 1) x).val , (f (lift_one (m + 1) x) < m ⟩ using apply_relabel_gt?

Mario Carneiro (Apr 29 2020 at 03:56):

what is f? Is it a let?

Mario Carneiro (Apr 29 2020 at 03:56):

If you don't use let then it just has the full expression in there already

Reid Barton (Apr 29 2020 at 03:57):

f is an argument to the theorem

Reid Barton (Apr 29 2020 at 03:57):

a variable

Adeeb K (Apr 29 2020 at 03:57):

f : finite_subset (m + 2) → finite_subset (m + 1) is given as an argument

Mario Carneiro (Apr 29 2020 at 03:57):

in that case I don't understand the question, what does let have to do with anything

Reid Barton (Apr 29 2020 at 03:58):

Anyways you just apply apply_relabel_gt to the right things. Have you done the natural number game?

Mario Carneiro (Apr 29 2020 at 03:58):

relabel is a def and it has theorems about it, and you use those theorems and that's all there is to it

Adeeb K (Apr 29 2020 at 03:58):

I mean, I have h : (f (lift_one (m + 1) x)).val = (f (lift_one (m + 1) y)).val

Reid Barton (Apr 29 2020 at 03:59):

I think Adeeb means: instead of naming f (lift_one (m+1) x) or something with a let. How to do it without any let.

Reid Barton (Apr 29 2020 at 03:59):

Not that the let would help of course.

Steffan (Apr 29 2020 at 04:00):

sorry was a macro for a placeholder, I thought? and Lean threw errors about it? I guess I don't even know what sorry is. ;)

Mario Carneiro (Apr 29 2020 at 04:02):

You can imagine that there is axiom sorry : \forall {A : Sort*}, A and whenever you use sorry it magically has whatever type it needs to have. It's not quite implemented like that but that's the idea

Reid Barton (Apr 29 2020 at 04:02):

sorry is yet another of those things nobody really understands. That's why you shouldn't use it in your proofs :upside_down:

Reid Barton (Apr 29 2020 at 04:02):

Like what even is a macro?

Mario Carneiro (Apr 29 2020 at 04:02):

a deeply mysterious thing

Steffan (Apr 29 2020 at 04:03):

that's why I don't like it :upside_down:
like, VSCode says a warning, yet it compiles...

Like what even is a macro?

Umm, I don't know, but in a github issue it said sorry was a macro.

Mario Carneiro (Apr 29 2020 at 04:03):

If you look at the expr type you will see that one of the options for an expr is a macro

Mario Carneiro (Apr 29 2020 at 04:04):

this is a kind of suspended computation, a promise that there is an expr here

Mario Carneiro (Apr 29 2020 at 04:04):

and some of them are lies like sorry

Adeeb K (Apr 29 2020 at 04:04):

so my proof is as follows: since (f (lift_one (m + 1) x)).val ≤ k and (f (lift_one (m + 1) y)).val ≤ k, by apply_relabel_lt we have relabel m k p (f (lift_one (m + 1) x)) = ⟨ (f (lift_one (m + 1) x)).val , (f (lift_one (m + 1) x)).val < m ⟩ and relabel m k p (f (lift_one (m + 1) y)) = ⟨ (f (lift_one (m + 1) x)).val , (f (lift_one (m + 1) y)).val < m ⟩. Since relabel m k p (f (lift_one (m + 1) x)) = relabel m k p (f (lift_one (m + 1) y))``. From this I would get something like (f (lift_one (m + 1) x)) = (f (lift_one (m + 1) y))```

Adeeb K (Apr 29 2020 at 04:05):

and then I could unpack using the injectivity of f and lift_one

Mario Carneiro (Apr 29 2020 at 04:06):

I suggest you modify the statement of apply_relabel_lt so that it says (relabel m k p x).1 = ... instead

Mario Carneiro (Apr 29 2020 at 04:06):

that way you don't have to worry about that proof component, that doesn't matter anyway

Adeeb K (Apr 29 2020 at 04:07):

okay, one second then

Adeeb K (Apr 29 2020 at 04:08):

alright, that's done.

Steffan (Apr 29 2020 at 04:10):

I proved that 1 = 2 with the help of sorry. Really.

theorem oet : 1 = 2 := sorry

theorem oett : 1 = 2 := begin
  apply oet,
end

Adeeb K (Apr 29 2020 at 04:21):

question

Adeeb K (Apr 29 2020 at 04:21):

suppose I have h : (f (lift_one (m + 1) x)).val = (f (lift_one (m + 1) y)).val

Adeeb K (Apr 29 2020 at 04:21):

how can I conclude that (f (lift_one (m + 1) x)) = (f (lift_one (m + 1) y)) knowing that the second part of the pair is that their values are bounded by m?

Reid Barton (Apr 29 2020 at 04:22):

Maybe Kevin wrote a lemma that can help you?

Adeeb K (Apr 29 2020 at 04:23):

oh, extensionality?

Adeeb K (Apr 29 2020 at 04:23):

wait, I thought that was the opposite

Adeeb K (Apr 29 2020 at 04:23):

...wait it was an iff

Adeeb K (Apr 29 2020 at 04:25):

I'm still getting an error

rewrite tactic failed, did not find instance of the pattern in the target expression

Adeeb K (Apr 29 2020 at 04:25):

when I used rw ext_iff at h,

Adeeb K (Apr 29 2020 at 04:25):

and h : (f (lift_one (m + 1) x)).val = (f (lift_one (m + 1) y)).val

Adeeb K (Apr 29 2020 at 04:34):

I'm not sure how to apply ext_iff in syntax here

Adeeb K (Apr 29 2020 at 04:34):

since rw ext_iff at h isn't working

Kenny Lau (Apr 29 2020 at 04:41):

-- subtype.ext : ∀ {α : Sort u_1} {p : α → Prop} {a1 a2 : {x // p x}}, a1 = a2 ↔ a1.val = a2.val
#check @subtype.ext

Adeeb K (Apr 29 2020 at 04:44):

I'm getting the same error using subtype.ext

Adeeb K (Apr 29 2020 at 04:44):

namely

rewrite tactic failed, did not find instance of the pattern in the target expression

Kenny Lau (Apr 29 2020 at 04:45):

you need to use \l

Kenny Lau (Apr 29 2020 at 04:45):

rw \l suubtype.ext

Kenny Lau (Apr 29 2020 at 04:45):

that's a lowercase L

Adeeb K (Apr 29 2020 at 04:52):

okay, that worked

Adeeb K (Apr 29 2020 at 05:13):

@Kenny Lau I'm not sure how to start the second part of the proof where

h :
  dite ((f (lift_one (m + 1) x)).val  k)
      (λ (H : (f (lift_one (m + 1) x)).val  k), (f (lift_one (m + 1) x)).val, _⟩)
      (λ (H : ¬(f (lift_one (m + 1) x)).val  k), (f (lift_one (m + 1) x)).val - 1, _⟩) =
    dite ((f (lift_one (m + 1) y)).val  k)
      (λ (H : (f (lift_one (m + 1) y)).val  k), (f (lift_one (m + 1) y)).val, _⟩)
      (λ (H : ¬(f (lift_one (m + 1) y)).val  k), (f (lift_one (m + 1) y)).val - 1, _⟩)

I remember that in the first part, you used this:

rw [dif_pos h1, dif_pos h2, subtype.mk_eq_mk] at h,

I'm wondering how I could adapt that for this case where (f (lift_one (m + 1) y)).val ≥ k.

Adeeb K (Apr 29 2020 at 05:15):

I'm not sure how to match this with dif_neg

Kenny Lau (Apr 29 2020 at 05:29):

well you need to prove strict inequality

Adeeb K (Apr 29 2020 at 05:29):

I think I have that with miss right?

Kenny Lau (Apr 29 2020 at 05:29):

you still need to prove it

Adeeb K (Apr 29 2020 at 05:29):

miss : ∀ j : finite_subset (m + 1), (f ∘ lift_one (m + 1)) j ≠ ⟨k, pf⟩

Kenny Lau (Apr 29 2020 at 05:30):

because dite says "if p is true then ...; else ..."

Kenny Lau (Apr 29 2020 at 05:30):

if you want to trigger the "else" clause then you need a proof of "not p"

Adeeb K (Apr 29 2020 at 05:31):

okay so then miss x is a proof that (f ∘ lift_one (m + 1)) x ≠ ⟨k, pf⟩ yes?

Adeeb K (Apr 29 2020 at 05:32):

so then subtype.ext (miss x) should be ((f ∘ lift_one (m + 1)) x).val ≠ ⟨k, pf⟩.val yes?

Kenny Lau (Apr 29 2020 at 05:32):

sure

Kenny Lau (Apr 29 2020 at 05:32):

now prove not (k \le _)

Adeeb K (Apr 29 2020 at 05:33):

I'm not sure what to do there

Adeeb K (Apr 29 2020 at 05:34):

do I break into cases using lt_or_eq_of_le?

Adeeb K (Apr 29 2020 at 05:35):

should I provide an MWE to show where I'm stuck?

Adeeb K (Apr 29 2020 at 05:35):

the problem I'm having is that I'm just not sure what syntax to use here..

Kenny Lau (Apr 29 2020 at 05:36):

have hnk : \not k \le ...,

Kenny Lau (Apr 29 2020 at 05:36):

and after you prove it, rw dif_neg hnk

Adeeb K (Apr 29 2020 at 05:37):

one sec then

Adeeb K (Apr 29 2020 at 05:40):

wait, question about have.

Adeeb K (Apr 29 2020 at 05:41):

...what does it exactly do for me, and what would be the form to prove it?

Adeeb K (Apr 29 2020 at 05:51):

actually here's a related question

Adeeb K (Apr 29 2020 at 05:52):

how do I know that x ≠ y is~(x = y).

Bryan Gin-ge Chen (Apr 29 2020 at 05:54):

have lets you prove something that you want to use later and add it to the context. You may want to review some of the early chapters of TPiL, specifically this section.

Adeeb K (Apr 29 2020 at 05:55):

ah, so the basic pattern here is have <fact> from <proof>?

Bryan Gin-ge Chen (Apr 29 2020 at 05:57):

Not quite. have var_name : p, from <proof> or have var_name : p := <proof> where p : Prop.

Adeeb K (Apr 29 2020 at 05:58):

I see. One second

Bryan Gin-ge Chen (Apr 29 2020 at 05:58):

how do I know that x ≠ y is ~(x = y).

This is the definition of ne: https://github.com/leanprover-community/lean/blob/master/library/init/logic.lean#L97

Adeeb K (Apr 29 2020 at 06:03):

Then question: I have (f ∘ lift_one (m + 1)) x ≠ ⟨k, pf⟩. How do I conclude that ((f ∘ lift_one (m + 1)) x).1 ≠ ⟨k, pf⟩.1

Shing Tak Lam (Apr 29 2020 at 06:07):

(deleted)

Bryan Gin-ge Chen (Apr 29 2020 at 06:13):

Both sides are subtypes, right? I bet you can use something from data.subtype: https://github.com/leanprover-community/mathlib/blob/0fc4e6a8e19449312d943d5376df9d4e55e9d022/src/data/subtype.lean#L35

Bryan Gin-ge Chen (Apr 29 2020 at 06:16):

Oh, I see that Kenny already suggested subtype.ext to you.

Bryan Gin-ge Chen (Apr 29 2020 at 06:19):

Maybe this helps:

import tactic

example (α : Type*) (P : α  Prop) (p q : {a // P a}) : p  q  p.1  q.1 := begin
  intros h1 h2,
  apply h1,
  apply subtype.eq,
  exact h2,
  -- exact h1 (subtype.eq h2)
end

Adeeb K (Apr 29 2020 at 06:39):

oh sorry, I just saw this now

Adeeb K (Apr 29 2020 at 06:40):

I think I'm in homestretch right now, with the last case for relabel_inj

Adeeb K (Apr 29 2020 at 06:40):

I'm gonna read this over real quick

Adeeb K (Apr 29 2020 at 06:46):

wait, so @Bryan Gin-ge Chen I have m_x : (f ∘ lift_one (m + 1)) x ≠ f ⟨m + 1, _⟩ and tried using `apply subtype.eq at m_x , but it failed.

Frank Dai (Apr 29 2020 at 06:53):

Is there a version of rewrite that works with heq? Alternatively, how can I prove manually that if a == b then f a == f b?

Bryan Gin-ge Chen (Apr 29 2020 at 07:10):

@Adeeb K Well that's different from what you said above, so copying a random line from my example isn't likely to work. You will have to do something with f. I can't really help further since I can't reproduce the state of your proof.

Adeeb K (Apr 29 2020 at 07:14):

okay

Adeeb K (Apr 29 2020 at 07:14):

should I provide an MWE?

Mario Carneiro (Apr 29 2020 at 07:15):

the answer to that question is always yes

Adeeb K (Apr 29 2020 at 07:15):

(not to say I'm not gonna try and adapt what you wrote further)

Adeeb K (Apr 29 2020 at 07:15):

okay I'll post it in a sec

Adeeb K (Apr 29 2020 at 07:20):

import tactic

def injective {X Y} (f : X  Y) :=  x₁ x₂, f x₁ = f x₂  x₁ = x₂

theorem comp_inj_is_inj
{X Y Z} (f : X  Y) (g : Y  Z)
(p1 : injective f)
(p2 : injective g)
:  injective (g  f)
:= begin
  introv x p3,
  change g (f x) = g (f x₂) at p3,
  apply p1,
  apply p2,
  apply p3,
end

lemma succ_greater_than_nat (n : ) : nat.succ n > n
:=
begin
  rw nat.succ_eq_add_one,
  linarith
end

def finite_subset (n : ) := { k // k < n }

lemma ext_iff (n : ) (a b : finite_subset n) : a = b  a.1 = b.1 :=
begin
  cases a,
  cases b,
  split,
  { intro h, rw h},
  { intro h, cases h, refl,}
end

def lift_finite (m n : ) (p : m < n) : finite_subset m  finite_subset n
    := λ k, k.1, lt.trans k.2 p


def lift_one
(m : )
: finite_subset m  finite_subset (m + 1)
:= (lift_finite m (m+1) (succ_greater_than_nat m))

theorem lift_finite_injective (m n : ) (p : m < n) :
 injective (lift_finite m n p) :=
begin
  intros x₁ x₂ h,
  rw ext_iff at  h,
  exact h
end


lemma lift_one_injective (m : )
: injective (lift_one m)
:= begin
apply lift_finite_injective m (m + 1) (succ_greater_than_nat m),
end


def relabel (m k : ) (h : k < m) (j : finite_subset m) : finite_subset (m - 1) :=
if H : j.1  k then j.1, sorry else j.1 - 1, sorry


lemma apply_relabel_lt (m k : ) (hkm : k < m) (z : finite_subset m) (h2 : z.1  k) (h3 : z.1 < m - 1) :
  relabel m k hkm z = z.1, h3 :=
begin
  unfold relabel,
  rw dif_pos h2,
end


lemma apply_relabel_gt (m k : )
(hkm : k < m)
(z : finite_subset m)
(h2 : k < z.1)
(h3 : z.1 - 1 < m - 1)
: relabel m k hkm z = z.1 - 1, h3
:= begin
  unfold relabel,
  rw dif_neg (not_le_of_lt h2),
end


lemma relabel_behavior (m k : ) (h : k < m) (x y : finite_subset m) (hxy : relabel m k h x = relabel m k h y) :
  (x.1  k  y.1  k)  (x.1  k  y.1  k) :=
begin
  unfold relabel at hxy,
  split_ifs at hxy with hxk hyk hyk,
  {
    left; split; assumption
  },
  { right; split,
    { rw subtype.mk_eq_mk at hxy,
      cases lt_or_eq_of_le hxk with hxlk hxek,
      {
        rw hxy at hxlk, exact absurd (nat.le_of_pred_lt hxlk) hyk
      },

      {
        exact ge_of_eq hxek
      },
    },

    {
      exact le_of_not_ge hyk
    },
  },
  { right; split,
    {
      exact le_of_not_ge hxk
    },

    { rw subtype.mk_eq_mk at hxy,
      cases lt_or_eq_of_le hyk with hylk hyek,
      {
        rw  hxy at hylk, exact absurd (nat.le_of_pred_lt hylk) hxk
      },
      { exact ge_of_eq hyek
      },
    },
  },

  {
    right; split; apply le_of_not_ge; assumption
  },


end

lemma relabel_inj (m k : ) (hkm : k < m + 1)
(f: finite_subset (m + 2)  finite_subset (m + 1))
(inj : injective f)
(pf : k < m + 1)
(miss :  j : finite_subset (m + 1), (f  lift_one (m + 1)) j  f m + 1,  succ_greater_than_nat (m + 1) ) :
injective ((relabel (m + 1) k pf)  f  lift_one (m + 1)) :=
begin
  intros x y h,
  change (relabel (m + 1) k pf (f (lift_one (m + 1) x))) = (relabel (m + 1) k pf (f (lift_one (m + 1) y))) at h,
  rcases relabel_behavior _ _ _ _ _ h with h1, h2 | h1, h2⟩; unfold relabel at h,
  {
    rw [dif_pos h1, dif_pos h2, subtype.mk_eq_mk] at h,
    rw  subtype.ext at h,
    let comp_inj := comp_inj_is_inj (lift_one (m + 1)) f (lift_one_injective (m + 1)) inj,
    change (f  lift_one (m + 1)) x = (f  lift_one (m + 1)) y at h,
    apply comp_inj,
    apply h,
  },

  {
    let m_x := (miss x),
    apply subtype.eq at m_x,


    sorry
  },

end

Kevin Buzzard (Apr 29 2020 at 07:20):

@Frank Dai feel free to start a new topic, topics are cheap and having several questions in one thread is confusing. I would happily see this noob questions topic locked. I thought that == was for terms that didn't have the same type, but if f a and f b make sense then a and b must have the same type. Can you upgrade your == to an =? When I see an == I have usually done something wrong, I try and avoid them

Adeeb K (Apr 29 2020 at 07:20):

@Mario Carneiro @Bryan Gin-ge Chen I've posted an MWE above

Mario Carneiro (Apr 29 2020 at 07:20):

You need to work on the "M" in MWE

Adeeb K (Apr 29 2020 at 07:21):

The major part of this is relabel_inj but I put in above it the bare minimum to get no errors

Mario Carneiro (Apr 29 2020 at 07:21):

sorry out everything that doesn't matter for your question

Adeeb K (Apr 29 2020 at 07:21):

okay one sec then

Kevin Buzzard (Apr 29 2020 at 07:22):

I am looking at your code on a phone and it's impossible to understand because it's got too long. Isolate the problem, make a new file and copy the minimum amount over until you can reproduce your question. A good question takes work

Adeeb K (Apr 29 2020 at 07:23):

okay

Adeeb K (Apr 29 2020 at 07:23):

after sorrying everything out, I got ~100 in the MWE, which is probably still way too long

Mario Carneiro (Apr 29 2020 at 07:24):

deleting unnecessary newlines would be a good start

Adeeb K (Apr 29 2020 at 07:24):

ah sure, on it

Mario Carneiro (Apr 29 2020 at 07:25):

one thing you can do to "skip to the good part" is to take your tactic state and copy it into a new theorem statement so that you don't have to run through the first 50 lines of proof to get there

Mario Carneiro (Apr 29 2020 at 07:26):

you can also sorry out sub-branches of the proof that are complete and not under discussion

Adeeb K (Apr 29 2020 at 07:27):

ah, so like this?

theorem state :
<everything in the tactics window>
...
:= begin sorry, end

Mario Carneiro (Apr 29 2020 at 07:27):

yes

Mario Carneiro (Apr 29 2020 at 07:28):

you have to edit it slightly so it matches the syntax of a theorem statement

Adeeb K (Apr 29 2020 at 07:28):

I'll do that going forward then

Shing Tak Lam (Apr 29 2020 at 07:28):

extract_goal might help here

Mario Carneiro (Apr 29 2020 at 07:28):

oh right I recall someone made a tactic to do this

Shing Tak Lam (Apr 29 2020 at 07:29):

Put extract_goal before the last sorry. Then you can just copy out the example from the window on the right.

Adeeb K (Apr 29 2020 at 07:30):

my current block is 48 lines

import tactic

def injective {X Y} (f : X  Y) :=  x₁ x₂, f x₁ = f x₂  x₁ = x₂
theorem comp_inj_is_inj {X Y Z} (f : X  Y) (g : Y  Z)(p1 : injective f) (p2 : injective g) :  injective (g  f):= begin sorry, end
lemma succ_greater_than_nat (n : ) : nat.succ n > n := begin sorry,end

def finite_subset (n : ) := { k // k < n }

lemma ext_iff (n : ) (a b : finite_subset n) : a = b  a.1 = b.1 :=
begin sorry, end

def lift_finite (m n : ) (p : m < n) : finite_subset m  finite_subset n
    := λ k, k.1, lt.trans k.2 p

def lift_one (m : ) : finite_subset m  finite_subset (m + 1) := (lift_finite m (m+1) (succ_greater_than_nat m))
theorem lift_finite_injective (m n : ) (p : m < n) : injective (lift_finite m n p) := begin sorry,end
lemma lift_one_injective (m : )  : injective (lift_one m) := begin sorry, end

def relabel (m k : ) (h : k < m) (j : finite_subset m) : finite_subset (m - 1) :=
if H : j.1  k then j.1, sorry else j.1 - 1, sorry

lemma apply_relabel_lt (m k : ) (hkm : k < m) (z : finite_subset m) (h2 : z.1  k) (h3 : z.1 < m - 1) :
  relabel m k hkm z = z.1, h3 := begin sorry,end
lemma apply_relabel_gt (m k : ) (hkm : k < m) (z : finite_subset m) (h2 : k < z.1) (h3 : z.1 - 1 < m - 1) : relabel m k hkm z = z.1 - 1, h3 := begin sorry,end
lemma relabel_behavior (m k : ) (h : k < m) (x y : finite_subset m) (hxy : relabel m k h x = relabel m k h y) :(x.1  k  y.1  k)  (x.1  k  y.1  k) := begin sorry,end


lemma relabel_inj (m k : ) (hkm : k < m + 1)
(f: finite_subset (m + 2)  finite_subset (m + 1))
(inj : injective f)
(pf : k < m + 1)
(miss :  j : finite_subset (m + 1), (f  lift_one (m + 1)) j  f m + 1,  succ_greater_than_nat (m + 1) ) :
injective ((relabel (m + 1) k pf)  f  lift_one (m + 1)) :=
begin
  intros x y h,
  change (relabel (m + 1) k pf (f (lift_one (m + 1) x))) = (relabel (m + 1) k pf (f (lift_one (m + 1) y))) at h,
  rcases relabel_behavior _ _ _ _ _ h with h1, h2 | h1, h2⟩; unfold relabel at h,
  {
    sorry
  },

  {
    let m_x := (miss x),
    apply subtype.eq at m_x,
    sorry
  },

end

Adeeb K (Apr 29 2020 at 07:31):

@Shing Tak Lam can I copy the example as is into my code as a theorem, or do I need to modify it?

Shing Tak Lam (Apr 29 2020 at 07:37):

Well it should be a top level statement, so it'd be like having a new lemma, which you can either copy the solution into your current lemma or use it as `exact.

In your current scenario however, I don't think it's a good idea. h is massive when extracted, and the result of extract_goal isn't valid (is this a bug?).

Mario Carneiro (Apr 29 2020 at 07:38):

That's a big improvement. Here it is again, a bit more compressed:

import tactic

def injective {X Y} (f : X  Y) :=  x₁ x₂, f x₁ = f x₂  x₁ = x₂
lemma succ_greater_than_nat (n : ) : nat.succ n > n := sorry
def finite_subset (n : ) := { k // k < n }
def lift_finite (m n : ) (p : m < n) : finite_subset m  finite_subset n :=
λ k, k.1, lt.trans k.2 p
def lift_one (m : ) : finite_subset m  finite_subset (m + 1) := (lift_finite m (m+1) (succ_greater_than_nat m))
theorem lift_finite_injective (m n : ) (p : m < n) : injective (lift_finite m n p) := sorry
lemma lift_one_injective (m : )  : injective (lift_one m) := sorry
def relabel (m k : ) (h : k < m) (j : finite_subset m) : finite_subset (m - 1) :=
if H : j.1  k then j.1, sorry else j.1 - 1, sorry
lemma apply_relabel_lt (m k : ) (hkm : k < m) (z : finite_subset m) (h2 : z.1  k) (h3 : z.1 < m - 1) : relabel m k hkm z = z.1, h3 := sorry
lemma apply_relabel_gt (m k : ) (hkm : k < m) (z : finite_subset m) (h2 : k < z.1) (h3 : z.1 - 1 < m - 1) : relabel m k hkm z = z.1 - 1, h3 := sorry
lemma relabel_behavior (m k : ) (h : k < m) (x y : finite_subset m) (hxy : relabel m k h x = relabel m k h y) : (x.1  k  y.1  k)  (x.1  k  y.1  k) := sorry

lemma relabel_inj (m k : ) (hkm : k < m + 1)
  (f: finite_subset (m + 2)  finite_subset (m + 1))
  (inj : injective f)
  (pf : k < m + 1)
  (miss :  j : finite_subset (m + 1), (f  lift_one (m + 1)) j  f m + 1,  succ_greater_than_nat (m + 1)) :
  injective ((relabel (m + 1) k pf)  f  lift_one (m + 1)) :=
begin
  intros x y h,
  change (relabel (m + 1) k pf (f (lift_one (m + 1) x))) = (relabel (m + 1) k pf (f (lift_one (m + 1) y))) at h,
  rcases relabel_behavior _ _ _ _ _ h with h1, h2 | h1, h2⟩; unfold relabel at h,
  { sorry },
  { let m_x := (miss x),
    apply subtype.eq at m_x, -- invalid 'begin-end' expression, ',' expected
    sorry },
end

Note the comment indicating where people should be looking

Adeeb K (Apr 29 2020 at 07:41):

Ah, I see
I should comment then the specific errors

Mario Carneiro (Apr 29 2020 at 07:41):

Anyway, the error here is simple: apply e at h is not valid syntax. Given that you are applying subtype.eq to m_x : (f ∘ lift_one (m + 1)) x ≠ f ⟨m + 1, _⟩ I'm not sure exactly what you are trying to accomplish

Adeeb K (Apr 29 2020 at 07:42):

I want to get ((f ∘ lift_one (m + 1)) x).1 ≠ (f ⟨m + 1, _⟩).1

Adeeb K (Apr 29 2020 at 07:43):

ultimately I want to change h1 : (f (lift_one (m + 1) x)).val ≥ k into h1 : (f (lift_one (m + 1) x)).val > k

Mario Carneiro (Apr 29 2020 at 07:44):

This works:

  { have m_x := (miss x),
    replace m_x := mt subtype.eq m_x,
    sorry },

Mario Carneiro (Apr 29 2020 at 07:44):

I think there is a mathlib tactic for apply at but I'm not finding it atm

Bryan Gin-ge Chen (Apr 29 2020 at 07:47):

apply_fun?

Mario Carneiro (Apr 29 2020 at 07:47):

By the way, I think unfold relabel was a bad move. It puts a giant assumption in your context and there is no need since you have apply_relabel_lt and apply_relabel_gt to tell you what relabel does

Adeeb K (Apr 29 2020 at 07:47):

alright, lemme see where that gets me

Mario Carneiro (Apr 29 2020 at 07:48):

apply_fun f at h is very confusingly named (I always look for a tactic named congr-something) but it's really apply congr_arg f at h

Mario Carneiro (Apr 29 2020 at 07:49):

that is, it changes an assumption h : x = y to h : f x = f y, while I want to change h : P to h : Q given f : P -> Q

Adeeb K (Apr 29 2020 at 07:49):

I see, but it was Kenny that gave me this general skeleton to work with, so I was working off that.

Johan Commelin (Apr 29 2020 at 07:50):

Mario Carneiro said:

apply_fun f at h is very confusingly named (I always look for a tactic named congr-something) but it's really apply congr_arg f at h

I guess it's only confusing for experts that now about "congr"-yadda... :lol:

Kevin Buzzard (Apr 29 2020 at 07:50):

@Stephanie Zhou this "noob questions" stream is hard to follow right now, feel free to start your own topic in #new members. The answer is that I edited my leanpkg.toml file so that it looked like a 3.4.2 file and then I typed leanproject get-mathlib-cache. So this reduces the problem to figuring out what a good Lean 3.4.2 toml looks like

Mario Carneiro (Apr 29 2020 at 07:50):

to me applying a function is what I wrote as the second thing

Mario Carneiro (Apr 29 2020 at 07:50):

that is, what apply at should do if it existed

Johan Commelin (Apr 29 2020 at 07:51):

But what about congr_fun?

Mario Carneiro (Apr 29 2020 at 07:52):

Why not congr at

Mario Carneiro (Apr 29 2020 at 07:54):

actually congr at doesn't work so well because the arguments are different

Johan Commelin (Apr 29 2020 at 07:54):

Hmm, maybe. Doesnt read that natural either

Johan Commelin (Apr 29 2020 at 07:54):

apply h to x ?? meh...

Mario Carneiro (Apr 29 2020 at 07:54):

congr_fun works for me

Adeeb K (Apr 29 2020 at 07:55):

okay so question

Mario Carneiro (Apr 29 2020 at 07:55):

it's only slightly confusing that this is actually sugar for congr_arg, while congr_fun also exists and proves f = g -> f x = g x

Johan Commelin (Apr 29 2020 at 07:55):

But it's a weird word... you never see it in a normal proof. I would like a verb

Adeeb K (Apr 29 2020 at 07:56):

I have

(f (lift_one (m + 1) y)).val  (f m + 1, _⟩).val

and I have

¬((f  lift_one (m + 1)) x).val = (f m + 1, _⟩).val

how can I conclude the following?

((f  lift_one (m + 1)) x).val > (f m + 1, _⟩).val

Johan Commelin (Apr 29 2020 at 07:56):

Did you mean to have that \not on the last line?

Johan Commelin (Apr 29 2020 at 07:56):

lt_of_le_of_ne

Adeeb K (Apr 29 2020 at 07:56):

no I didn't. I'll edit that out.

Johan Commelin (Apr 29 2020 at 07:57):

Sounds like what you want

Adeeb K (Apr 29 2020 at 07:57):

do you mean lt_or_le_of_ne?

Mario Carneiro (Apr 29 2020 at 07:57):

no

Johan Commelin (Apr 29 2020 at 07:57):

Nope

Adeeb K (Apr 29 2020 at 07:57):

...oh no, it's actually of.

Kevin Buzzard (Apr 29 2020 at 07:58):

@Stephanie Zhou I think that if your leanpkg.toml looks like this:

[package]
name = "your_project_name"
version = "0.1"
lean_version = "leanprover-community/lean:3.4.2"
path = "src"

[dependencies]
mathlib = {git = "https://github.com/leanprover-community/mathlib", rev = "dd8da5165bd00b07408dbb87173e96908c6926a4"}

and you type leanproject get-mathlib-cache you will downgrade your project to 3.4.2 (and of course it might break if you do this, and if you can't fix it then you'll have to change your toml back to what it was before you started this change, so perhaps keep a local copy)

Johan Commelin (Apr 29 2020 at 07:58):

You should read it as "You get lt of a proof of le and of a proof of ne"

Mario Carneiro (Apr 29 2020 at 08:00):

The correct preposition is "from", but when clearly character count is more important than grammar

Kevin Buzzard (Apr 29 2020 at 08:00):

Oh wait, there might be additional problems because various files and directories might have appeared in mathlib and might cause confusion. You should probably change directory into _target/deps/mathlib and type git checkout dd8da5165bd00b07408dbb87173e96908c6926a4 before you type leanproject get-mathlib-cache. I don't know enough about the set-up to know whether this will work. Downgrading projects is not really supported and this is just a manual hack, don't be surprised if this causes problems. I would save your work elsewhere before starting on this.

Kevin Buzzard (Apr 29 2020 at 08:00):

I did it yesterday with a completely new repo with no files in

Adeeb K (Apr 29 2020 at 08:01):

I see. I'm getting an error from the following:

let h1_strict := lt_of_le_of_ne h1 m_x,

which is giving me the error:

type mismatch at application
  lt_of_le_of_ne h1 m_x
term
  m_x
has type
  ¬((f  lift_one (m + 1)) x).val = (f m + 1, _⟩).val
but is expected to have type
  (f m + 1, _⟩).val  (f (lift_one (m + 1) x)).val

Mario Carneiro (Apr 29 2020 at 08:02):

Adeeb, could you perhaps continue this on another thread? It's generating a lot of back and forth

Adeeb K (Apr 29 2020 at 08:02):

Oh, sure. I'll be on the 'showing injectivity' thread then.

Billy Price (Apr 29 2020 at 12:19):

Mario Carneiro said:

there are ways to talk about the nth item of a list

I had a look in data.list.basic, and all I can see is a bunch of theorems and lemma about lists. How does one go about understanding the basic usage of a data type in lean? I haven't had the best experience so far so I'm wondering if you have any tips.

Kevin Buzzard (Apr 29 2020 at 12:25):

I just read all the theorems and lemmas nowadays. In fact that's a slightly silly thing to say. I read the definitions, and then I guess which theorems will be in the library, and then either find them in there or PR them if they're not. But with lists it's a bit tricky because part of the theory is developed in core Lean and part in mathlib.

Billy Price (Apr 29 2020 at 12:34):

Where can I find the defs/documentation for lists?

Billy Price (Apr 29 2020 at 12:34):

I don't see any in data.list.basic

Johan Commelin (Apr 29 2020 at 12:34):

You've found a hole in our docs...

Rob Lewis (Apr 29 2020 at 12:36):

The mathlib ones are in data.list.defs

Patrick Massot (Apr 29 2020 at 12:46):

Billy, I don't have a really satisfying answer, but there is a clear workaround for lists. This is a super standard data type for functional programming languages. You can read doc about lists in Haskell or CaML, and you should be good to go.

Billy Price (Apr 29 2020 at 13:00):

I am familiar with lists, I was just looking for the syntax and available definitions in Lean.

Reid Barton (Apr 29 2020 at 13:05):

Just read through the source of data.list.defs and you will pick it up, then.

Billy Price (Apr 29 2020 at 13:06):

Yep I'm picking it up now :)

Billy Price (Apr 29 2020 at 13:10):

Here's an even more basic question. I want to use nth_le : Π (l : list α) (n), n < l.length → α to produce a proposition that A is equal to the nth element of my list, but I just want to keep the n < l.length as a hypothesis.

Here's my attempt, which isn't synthesising properly (but I'm guessing there's a more direct way)

def WF : Π A : type, term A  list type   Prop
| _ (var n A) Γ  := (n < Γ.length)  (Γ.nth_le n _ = A)

Kenny Lau (Apr 29 2020 at 13:11):

well it wouldn't compile because A is not a type (despite what you might think)

Kenny Lau (Apr 29 2020 at 13:12):

so list A is not defined

Billy Price (Apr 29 2020 at 13:12):

ah sorry I copied it over wrong, will edit

Reid Barton (Apr 29 2020 at 13:17):

Okay, what is the error? Did you read it?

Billy Price (Apr 29 2020 at 13:19):

don't know how to synthesize placeholder
context:
WF : Π (A : type), term A  context  Prop,
A : type,
n : ,
Γ : context,
a : n < list.length Γ
 n < list.length Γ

Billy Price (Apr 29 2020 at 13:20):

(context is list type)

Kenny Lau (Apr 29 2020 at 13:20):

then supply a to the underscore

Billy Price (Apr 29 2020 at 13:21):

Yeah I tried doing it like this but clearly this isn't the correct syntax

def WF : Π A : type, term A  context   Prop
| _ (var n A) Γ  := (a : n < Γ.length)  (Γ.nth_le n a = A)

Kenny Lau (Apr 29 2020 at 13:22):

you need \forall

Kenny Lau (Apr 29 2020 at 13:22):

\forall a : _, _ n a = A

Reid Barton (Apr 29 2020 at 13:22):

Also, you should think about whether this is actually what you mean

Billy Price (Apr 29 2020 at 13:27):

Cool that worked. Is there a way I can say it more directly? Like just "inject" the =A into Γ.nth_le?

Kenny Lau (Apr 29 2020 at 13:27):

define the proposition inductively

Reid Barton (Apr 29 2020 at 13:28):

This will probably also have the side effect of changing the definition into the one you likely intended.

Billy Price (Apr 29 2020 at 13:28):

Inductively on the list?

Billy Price (Apr 29 2020 at 13:30):

@Reid Barton What is the issue you are referring to?

Reid Barton (Apr 29 2020 at 13:30):

What is WF supposed to mean exactly?

Kenny Lau (Apr 29 2020 at 13:30):

inductive WF : _
| some_name : \forall _, WF _ (var n (\Gamma.nth_le _ _))

Reid Barton (Apr 29 2020 at 13:30):

In the case you have so far

Kenny Lau (Apr 29 2020 at 13:31):

I've just discovered accidentally that you don't need the closing triple backticks

Kenny Lau (Apr 29 2020 at 13:31):

@Billy Price also, start a new thread

Billy Price (Apr 29 2020 at 13:32):

I guess I'll go back to Type Theory? I came here just because I had some more basic lean questions.

Kevin Buzzard (Apr 29 2020 at 14:39):

Feel free to start a new thread in #new members -- noob questions is getting really crowded

Steffan (Apr 30 2020 at 16:55):

unless you are avoiding simp like kenny

He broke it! XD https://www.codewars.com/kata/reviews/5eaa45ea72511e00016538ad/groups/5eaad1c97c30340001e5bbb7

Kenny Lau (Apr 30 2020 at 18:27):

that's a simp only

Kevin Buzzard (Apr 30 2020 at 19:17):

I just solved the kata to see what all the fuss was about! I thought he'd broken the kata, not some vow to use simp (and as he said, he didn't use it)

Steffan (Apr 30 2020 at 20:05):

Oh, then I misunderstood that, as simp only still is the simp tactic.

Kevin Buzzard (Apr 30 2020 at 20:09):

Kenny doesn't like simp because it can take a very long time looking through all the tactics tagged simp and his computer is slow. But simp only is ok because it only looks through the list you give it

Brandon Brown (Apr 30 2020 at 22:21):

Can every proof that uses calc ... be simplified to a sequence of rewrites?

Brandon Brown (Apr 30 2020 at 22:24):

I ask because I proved

theorem append_nil (t : list α) : t ++ list.nil = t :=
list.rec_on t (rfl)
(
    assume e : α,
    assume a : list α,
    assume ha : a ++ list.nil = a,
    show (e :: a) ++ list.nil = e :: a, from
    calc
        e :: a ++ list.nil = e :: (a ++ list.nil) : rfl
        ... = e :: (a) : by rw ha
)

But I don't think I can prove the same by using by rw [ ... ] instead of the calc block as I would need auxiliary theorems

Kevin Buzzard (Apr 30 2020 at 23:02):

Every proof which uses calc ... can be "simplified" to a sequence of applications of lemmas tagged [trans] (such as lt_of_le_of_lt, if you're proving a<ea<e by ab<c=d<ea\leq b<c=d<e) followed by whatever happened after the colon in your calc proof, but after the colon you can have arbitrary Lean code.

Brandon Brown (May 01 2020 at 06:31):

How do I do a calc style proof but where I can manipulate both the LHS and RHS? Looks like you can only rewrite the RHS with calc

Bryan Gin-ge Chen (May 01 2020 at 06:37):

You can't do it all in one calc block. But you could do it in several, using e.g. the symmetry tactic in between to swap LHS and RHS.

Brandon Brown (May 01 2020 at 06:38):

ohh neat

Patrick Massot (May 01 2020 at 08:32):

Brandon Brown said:

How do I do a calc style proof but where I can manipulate both the LHS and RHS? Looks like you can only rewrite the RHS with calc

No, this is an illusion. There is nothing special when proving a calc block step. If you post a #mwe then we'll help understand your rewriting problem.

Johan Commelin (May 01 2020 at 08:37):

calc LHS = newLHS : proof
     ... = newRHS : proof
     ... = RHS    : proof

Jalex Stark (May 01 2020 at 12:09):

Usually when I'm trying to make a calc proof I start with boilerplate like what johan said, and then I work both forward and backwards until i meet in the middle

Jack J Garzella (May 01 2020 at 13:31):

If I have a ring injection R →+* S, and a polynomial S, but I want to think about it as a polynomial R (I know that the coefficients are in R) how might I go about doing that?

Johan Commelin (May 01 2020 at 13:31):

We do not have very good machinery for that, at the moment )-;

Johan Commelin (May 01 2020 at 13:33):

Ideally, the lift tactic would help here.

Johan Commelin (May 01 2020 at 13:35):

@Jack J Garzella Would you like to prove the lemma? It would make a nice first PR.

Kevin Buzzard (May 01 2020 at 13:53):

I thought there was a way to do this somewhere in ring_theory

Brandon Brown (May 01 2020 at 18:29):

How can I substitute a sub-expression that is definitionally equal to a sub-expression in the goal using tactics? For rw I need to have an expression term in my context to substitute. This is easy to do using the calc... block but I'm trying to do it all in begin ... end block

Brandon Brown (May 01 2020 at 18:31):

Nevermind I got it, I used have ... by

Jack J Garzella (May 01 2020 at 18:33):

I found the lemma to_subring in polynomial.lean that does this @Kevin Buzzard @Johan Commelin

orlando (May 01 2020 at 18:33):

Brandon :

Perhaps change a with b ? Do you have an example ?

Brandon Brown (May 01 2020 at 18:35):

I'm trying to prove length (s ++ t) = length s + length t where length operates on the list type and returns the number of elements of the list; x::y represents list.cons x y (head element and tail list) and ++ is list concatenation.
One of my goals is length (x :: y ++ t) = length (x :: y) + length t and I know definitionally from the definition of length function that length (x::y) = length(y) + 1 so I'm trying to substitute that in my goal.

Jalex Stark (May 01 2020 at 18:37):

do you have an #mwe? (e.g. if you post your solution with have, someone may be able to rewrite it in the more clever way that you were asking about)

orlando (May 01 2020 at 18:38):

Hum try :
change length (x::y) with length(y) + 1
Or

 change  _  =   length(y) + 1  + _

Brandon Brown (May 01 2020 at 18:40):

namespace hidden
universe u

inductive list (α : Type u)
| nil {} : list
| cons : α  list  list
notation h :: t  := list.cons h t
def append {α : Type u} : list α  list α  list α
| list.nil l1 := l1
| (list.cons h t) l2 := list.cons h (append t l2)

local notation a `++` b := append a b

def length {α : Type u} : list α  
| list.nil := 0
| (h :: t) := nat.succ (length t) -- or just + 1

notation `[` l:(foldr `,` (h t, list.cons h t) list.nil) `]` := l
notation `[]` := list.nil
variable (α : Type u)
example {a : Type u} {s t : list α} : length (s ++ t) = length s + length t :=
list.rec_on s
(
    -- length t = length [] + length t
    -- length [] = 0 by definition
    -- 0 + x = x by definition
    show length ([] ++ t) = length [] + length t, from
    begin
        have z : [] ++ t = t, from rfl,
        rw z,
        have g : length [] = 0, from rfl,
        assumption,
        rw g,
        rw zero_add,
    end
)
(
    λ x y z,
    -- z : length (y ++ t) = length y + length t
    show length (x :: y ++ t) = length (x :: y) + length t, from
    begin
        show length (x :: y ++ t) = ((length y) + 1) + length t,
        have r : length (x :: y) = ((length y) + 1), refl,
        -- unfinished
    end
)

end hidden

Brandon Brown (May 01 2020 at 18:41):

You may need
notation [] := list.nil

Brandon Brown (May 01 2020 at 18:42):

But I'm not using mathlib yet, I'm just using whatever is in the base library and described in TPIL

Kevin Buzzard (May 01 2020 at 18:43):

Your example is not yet a #mwe -- it doesn't compile for me. If you could make it work for me, I'll try to help you.

Brandon Brown (May 01 2020 at 18:53):

Updated with MWE

Kevin Buzzard (May 01 2020 at 18:56):

I think x :: y ++ t means (x :: y) ++ t

Kevin Buzzard (May 01 2020 at 18:56):

#print notation :: -- 67
#print notation ++ -- 65

Kevin Buzzard (May 01 2020 at 18:57):

oh I see, this is not relevant

Johan Commelin (May 01 2020 at 18:57):

@Brandon Brown I think the proof might become easier if you perform induction on t

Brandon Brown (May 01 2020 at 18:57):

Yes it left associates - I think I'm more asking if what I'm doing is widely inefficient. I think I can end up getting it right shortly

Johan Commelin (May 01 2020 at 18:57):

Reason: see the definition of addition on nat.

Kevin Buzzard (May 01 2020 at 18:57):

I don't understand what the question is

Kevin Buzzard (May 01 2020 at 18:59):

Your question seemed to be "how to substitute length (x :: y) = length(y) + 1 into length (x :: y ++ t) = length (x :: y) + length t but in the MWE you have done this with the show command.

orlando (May 01 2020 at 19:00):

I don't understand nothing : but goals accomplished :joy:

universe u
open list
notation `[]` := list.nil
example (α : Type) {a : Type u} {s t : list α} : length (s ++ t) = length s + length t :=
list.rec_on s
(
    -- length t = length [] + length t
    -- length [] = 0 by definition
    -- 0 + x = x by definition
    show length ([] ++ t) = length [] + length t, from
    begin
        have z : [] ++ t = t, from rfl,
        rw z,
        have g : length [] = 0, from rfl,
        assumption,
        rw g,
        rw zero_add,
    end
)
(
    λ x y z,
    -- z : length (y ++ t) = length y + length t
    show length (x :: y ++ t) = list.length (x :: y) + length t, from
    begin
        change  _  =   length(y) + 1  + _,
        rw add_comm, rw  add_assoc, rw add_comm (length t) (length y) , rw  z, exact rfl,
    end
)

Kevin Buzzard (May 01 2020 at 19:00):

At the beginning of your begin/end block the goal is ⊢ length (x :: y ++ t) = length (x :: y) + length t but one line later it is length (x :: y ++ t) = length y + 1 + length t so you have achieved what you wanted to achieve, right?

Brandon Brown (May 01 2020 at 19:00):

Yes - sorry. I ended up figuring it out using the have ... from tactic. But Jalex Stark suggested I give my code in case someone had a more clever way.

Kevin Buzzard (May 01 2020 at 19:02):

By "more clever" do you mean a shorter tactic mode proof, a term mode proof, or what? There are lots of ways to prove this.

Brandon Brown (May 01 2020 at 19:04):

Well for example I just learned about the change tactic from what orlando just posted above completing the proof. I'll also try re-proving inducting on t to see if that's easier.

Kevin Buzzard (May 01 2020 at 19:05):

I think s is OK. I'll construct a tactic mode proof. Tactic mode is my favourite mode.

Kevin Buzzard (May 01 2020 at 19:06):

You have made some really inconvenient definitions here!

Kevin Buzzard (May 01 2020 at 19:08):

example {s t : list α} : length (s ++ t) = length s + length t :=
begin
  induction s with a L IH,
  { exact (zero_add _).symm},
  { show _ + 1 = _ + 1 + _,
    rw IH,
    simp [add_assoc, add_comm (length t)],
  }
end

Kevin Buzzard (May 01 2020 at 19:09):

Maybe there is no way to make the definitions convenient. The goal ends up being ⊢ length L + length t + 1 = length L + 1 + length t, actually, isn't this in the library?

Kevin Buzzard (May 01 2020 at 19:10):

example {s t : list α} : length (s ++ t) = length s + length t :=
begin
  induction s with a L IH,
  { exact (zero_add _).symm},
  { show _ + 1 = _ + 1 + _,
    rw IH,
    apply add_right_comm
  }
end

Brandon Brown (May 01 2020 at 19:11):

wow that's way shorter than mine. I will study this, thanks. And I dont know about add_right_comm but I wrote my own proof of the commutativity of addition earlier in TPIL as one of the exercies

Kevin Buzzard (May 01 2020 at 19:12):

The base case, you write

show length ([] ++ t) = length [] + length t, from ...

but now you might want to think about what this equals, by definition.

Brandon Brown (May 01 2020 at 19:13):

I'm glad to have learned about the induction tactic from your example, having to use .rec_on for everything didn't seem elegant

Kevin Buzzard (May 01 2020 at 19:13):

By definition, [] ++ t is t. By definition, length [] is 0. So the goal by definition is length t = 0 + length t

Kevin Buzzard (May 01 2020 at 19:13):

I do a lot of stuff in tactic mode, because I'm a mathematician so I don't really understand term mode

Kevin Buzzard (May 01 2020 at 19:14):

And zero_add X is the theorem that 0 + X = X, so (zero_add X).symm is the theorem that X = 0 + X which is exactly what we want. I was too lazy to type length t because I knew Lean would be able to work it out.

Brandon Brown (May 01 2020 at 19:14):

I was focusing on term mode proofs at first because tactics didnt make sense to me but now I'm starting to see how powerful they are

Kevin Buzzard (May 01 2020 at 19:15):

You can't get away with rw zero_add immediately though, because rw works with syntactic equality, i.e. literally the same keypress kind of equality, so rw zero_add won't work on a goal of the form length [] + X = ...

Kevin Buzzard (May 01 2020 at 19:16):

But exact is less fussy, it works with definitional equality

Johan Commelin (May 01 2020 at 19:16):

Can't you make the definitions in such a way that they match up nicely with the definition of nat.add?

Kevin Buzzard (May 01 2020 at 19:21):

example {s t : list α} : length (s ++ t) = length s + length t :=
list.rec_on s
(
    show length t = 0 + length t, from
    begin
        rw zero_add,
    end
)
(
    λ x y z,
    show length (y ++ t) + 1 = length y + 1 + length t, from
    begin
        rw z,
        apply add_right_comm,
    end
)

There's the proof written in that hybrid mode you were using

Kevin Buzzard (May 01 2020 at 19:21):

I used show to do more of the definitional rewriting

Johan Commelin (May 01 2020 at 19:22):

@Kevin Buzzard Why don't you induct on t?

Kevin Buzzard (May 01 2020 at 19:22):

because I don't know what to do with s ++ (x :: L)

Johan Commelin (May 01 2020 at 19:23):

Aah, I see.

Kevin Buzzard (May 01 2020 at 19:26):

Here's a term mode proof:

example {s t : list α} : length (s ++ t) = length s + length t :=
list.rec_on s
(zero_add _).symm
(λ x y z,
  show length (y ++ t) + 1 = length y + 1 + length t,
  by simp [z, add_right_comm (length y) (length t) 1])

Brandon Brown (May 01 2020 at 19:32):

Impressive. Thanks for all the examples, I have definitely expanded my proof-writing horizons

Kevin Buzzard (May 01 2020 at 19:40):

The proof in core Lean is this:

@[simp] lemma length_append (s t : list α) : length (s ++ t) = length s + length t :=
by induction s; simp [*, add_comm, add_left_comm]

Brandon Brown (May 01 2020 at 19:47):

So add_comm and add_left_comm don't have the @[simp] attribute otherwise you should be able to just use ... simp [*] right

Kevin Buzzard (May 01 2020 at 19:49):

If you gave add_comm the @[simp] attribute then simp would start timing out because when it got stuck it would rewrite a+b to b+a to a+b to b+a to ... :-)

Kevin Buzzard (May 01 2020 at 19:49):

Mathematicians are amazing people, we see how to use stuff like commutativity in ways that are not completely moronic

Mario Carneiro (May 02 2020 at 01:06):

actually it doesn't, because lean is smart enough to notice commutativity lemmas

Nam (May 02 2020 at 02:59):

What tool / format should I use to write documents in .lean files?

Nam (May 02 2020 at 03:00):

is definition the same as def? if yes, why two keywords?

Jalex Stark (May 02 2020 at 03:01):

What do you mean by documents?

Nam (May 02 2020 at 03:02):

literate programming, like RMarkdown?

Mario Carneiro (May 02 2020 at 03:03):

There are lots of duplicate keywords. Pretend definition doesn't exist

Mario Carneiro (May 02 2020 at 03:04):

You can use leandoc to create HTML pages like the mathlib docs; you write section comments in /-! ... -/

Nam (May 02 2020 at 03:05):

somehow def is not highlighted in my Sphinx doc (with Pygments 2.6.1)

Mario Carneiro (May 02 2020 at 03:06):

pygments lags behind in syntax highlighting; I think that there are some open PRs to the pygments repo

Bryan Gin-ge Chen (May 02 2020 at 03:07):

The updated Lean syntax highlighting has been merged into pygments master. I hope they'll release a new version soon...

Mario Carneiro (May 02 2020 at 03:07):

def x := true
definition y := false

Mario Carneiro (May 02 2020 at 03:07):

even here it doesn't work yet

Nam (May 02 2020 at 03:07):

Bryan Gin-ge Chen said:

The updated Lean syntax highlighting has been merged into pygments master. I hope they'll release a new version soon...

ah, yes, i saw the new commit to Lean 3 in master.

Nam (May 02 2020 at 03:08):

2.6.1 does not have any def in LeanLexer.

Nam (May 02 2020 at 03:16):

pip3 install --user --upgrade 'git+https://github.com/pygments/pygments.git@master#egg=Pygments' solved it. thanks for the code, Bryan!

Nam (May 02 2020 at 03:17):

Mario Carneiro said:

You can use leandoc to create HTML pages like the mathlib docs; you write section comments in /-! ... -/

i can't find leandoc anywhere in my system. i installed Lean with elan.

Mario Carneiro (May 02 2020 at 03:27):

I am not the expert at this, but I think it is in https://github.com/leanprover-community/mathlib-tools ?

Mario Carneiro (May 02 2020 at 03:28):

https://github.com/leanprover-community/format_lean also looks relevant

Mario Carneiro (May 02 2020 at 03:31):

aha, https://github.com/leanprover-community/doc-gen is the tool responsible for making the mathlib docs

Mario Carneiro (May 02 2020 at 03:33):

@Rob Lewis will know if this is suitable for other projects than mathlib

Nam (May 02 2020 at 03:38):

thanks. it looks like lean formatter produces Pandoc. great.

Bryan Gin-ge Chen (May 02 2020 at 03:51):

I just made the PR, I think the code changes were all by Sebastian, Gabriel and Reid.

Rob Lewis (May 02 2020 at 08:38):

doc-gen is hard coded to mathlib + core right now, but it wouldn't take very much work to generalize that.

Patrick Massot (May 02 2020 at 08:57):

Nam said:

thanks. it looks like lean formatter produces Pandoc. great.

I have idea what it means. Assuming you mean format_lean then this is mostly backward. It consumes markdown in comments. In principle it could output anything (including LaTeX for instance) but currently it outputs HTML.

ROCKY KAMEN-RUBIO (May 02 2020 at 19:14):

Where is % defined? I'd like to rewrite n%2=0 as an algebraic expression but am only finding modeq stuff in mathlib. Is it defined in the kernel?

Jalex Stark (May 02 2020 at 19:17):

#print notation %
( redirected to https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/where.20is.20.25.20defined.3F )

Brandon Brown (May 02 2020 at 20:22):

Hoping someone help me understand the vector α n type . It's supposed to be a kind of list of type α of length n but I don't see how this type enforces the length to be n

inductive vector (α : Type u) : nat  Type u
| nil {}                              : vector zero
| cons {n : } (a : α) (v : vector n) : vector (succ n)

I'm not really sure how to use this type.

Jalex Stark (May 02 2020 at 20:24):

redirected https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/vector.20type

ROCKY KAMEN-RUBIO (May 02 2020 at 21:19):

Brandon Brown said:

Hoping someone help me understand the vector α n type . It's supposed to be a kind of list of type α of length n but I don't see how this type enforces the length to be n

inductive vector (α : Type u) : nat  Type u
| nil {}                              : vector zero
| cons {n : } (a : α) (v : vector n) : vector (succ n)

I'm not really sure how to use this type.

I think the idea is that every vector is either the trivial vector of length zero, or a cons with a vector of length one smaller. This way we get a stack of vectors with increasing index, and that ensures that the length is correct. I was confused by this at first because I was used to thinking of vectors the way they're defined in languages like C where there are some key differences in memory allocation, but that's not happening here AFAIK.

ROCKY KAMEN-RUBIO (May 02 2020 at 22:18):

I also found it useful to copy the vector definition into a lean file and play around with a lot of #check statements to get a better picture of what's going on here. You might need to rename it to avoid a naming conflict. For example:

inductive my_vec (α : Type u) : nat  Type u
| nil {}                              : my_vec zero
| cons {n : } (a : α) (v : my_vec n) : my_vec (succ n)

#check my_vec
#check my_vec Prop
#check my_vec Prop 1
#check my_vec.nil
#check my_vec.cons tt my_vec.nil
#check my_vec.cons ff (my_vec.cons tt my_vec.nil)

Anas Himmi (May 06 2020 at 22:37):

I f i have (f:α → β) and (hf: function.bijective f) how do i get the inverse function of f?

Jalex Stark (May 06 2020 at 22:38):

I would do it by applying the theorem that says given an injective function, there exists a left inverse

Jalex Stark (May 06 2020 at 22:39):

I think hf.left : function.injective f

Chris Hughes (May 06 2020 at 22:39):

Not every injective function has a left inverse.

Chris Hughes (May 06 2020 at 22:39):

You can use function.surj_inv,but there might be better.

Anas Himmi (May 06 2020 at 22:41):

okay thank you!

Kevin Buzzard (May 06 2020 at 23:10):

Don't expect it to be computable though ;-)

Jalex Stark (May 06 2020 at 23:13):

hmm if you knew that your bijection was efficiently computable then you would know that the inverse is computable

Jalex Stark (May 06 2020 at 23:13):

not that we have the right notion of efficiency written down in mathlib, yet, just an idle thought

Kevin Buzzard (May 06 2020 at 23:15):

Even if the sets were infinite?

Jalex Stark (May 06 2020 at 23:15):

yeah

Jalex Stark (May 06 2020 at 23:15):

to even talk about "efficiently computable" you need to express your infinite set as an increasing chain of finite sets

Jalex Stark (May 06 2020 at 23:15):

think of the nth one "the stuff that's representable in n bits"

Jalex Stark (May 06 2020 at 23:16):

hmm maybe I'm talking nonsense. The intuition that got me here was "if the forward direction is in P then the reverse direction is in NP, which you can compute in exptime"

Kevin Buzzard (May 06 2020 at 23:16):

And you need decidable equality on the sets as well I guess

Mario Carneiro (May 06 2020 at 23:17):

I don't think this is right. There are definitely examples of computable bijections with no computable inverse

Jalex Stark (May 06 2020 at 23:18):

examples of bijections computable in polynomial time with no computable inverse?

Mario Carneiro (May 06 2020 at 23:19):

If the sets are (computably) countable and equality is decidable then it's possible

Mario Carneiro (May 06 2020 at 23:19):

you can exhaustively search a countable set if you know the answer is in there

Mario Carneiro (May 06 2020 at 23:19):

that's nat.find

Jalex Stark (May 06 2020 at 23:21):

yeah, I guess my comment really means "if your function is computable in the sense that you have an implementation running on a computer, then you probably have a computable enumeration and decidable equality lying around"

Mario Carneiro (May 06 2020 at 23:23):

Here's an example: Let A := bool and let B be the collection of all turing machines quotient by "one halts iff the other does". There is a computable map A -> B which sends tt to a halting TM and ff to a non-halting machine, and this is a bijection with no computable inverse

Mario Carneiro (May 06 2020 at 23:23):

This fails because although both sets are computably countable, B does not have decidable equality

Reid Barton (May 06 2020 at 23:24):

It's only a bijection if you assume LEM though, right?

Mario Carneiro (May 06 2020 at 23:24):

sure

Mario Carneiro (May 06 2020 at 23:24):

I'm not doing intuitionistic maths here

Mario Carneiro (May 06 2020 at 23:25):

By computable bijection, I mean a computable function that is classically a bijection

Mario Carneiro (May 06 2020 at 23:25):

which is what function.bijection gives you

Anas Himmi (May 07 2020 at 21:19):

I am having trouble showing that @surj_inv id _ = id ...

lemma surj_inv_id { α : Type* } : surj_inv (surjective_id) = ( id : α  α )  := sorry

Shing Tak Lam (May 08 2020 at 01:36):

#mwe ? What is surjective_id and I can't seem to find surj_inv?

Anas Himmi (May 08 2020 at 01:37):

Oh sorry i forgot to put open function before the lemma

Shing Tak Lam (May 08 2020 at 01:47):

Ok cool. There's also a missing import for logic.function.

Shing Tak Lam (May 08 2020 at 01:48):

Do you know what ext does?

Anas Himmi (May 08 2020 at 01:49):

It says that two function are equal if each for each x the image of both functions are equal

Shing Tak Lam (May 08 2020 at 01:49):

Yup. Are you doing this in term mode or tactic mode?

Anas Himmi (May 08 2020 at 01:49):

Both are fine

Shing Tak Lam (May 08 2020 at 01:49):

lemma surj_inv_id { α : Type* } : surj_inv (surjective_id) = ( id : α  α ) :=
begin
  ext1,
  sorry,
end

Shing Tak Lam (May 08 2020 at 01:51):

And you also have the fact that the surj_inv is a right inverse (right_inverse_surj_inv)

Shing Tak Lam (May 08 2020 at 01:56):

Which you can use to get id (surj_inv surjective_id x) = x, which is pretty much the goal.

Anas Himmi (May 08 2020 at 02:05):

lemma surj_inv_id {α : Type* }: surj_inv (surjective_id) = (id:αα) :=
begin
ext1,
have h:=right_inverse_surj_inv (surjective_id:surjective (id:α  α)),
exact h x
end

this works! thank you

Shing Tak Lam (May 08 2020 at 02:07):

:) Also I found the same issue with copying the definition from the have and it not working. You can fix that by using show.

lemma surj_inv_id { α : Type* } : surj_inv (surjective_id) = ( id : α  α ) :=
begin
  ext1,
  exact (show id (surj_inv surjective_id x) = x,
         from right_inverse_surj_inv surjective_id x),
end

and if all you're using is ext and exact, there isn't much point to tactic mode

lemma surj_inv_id' { α : Type* } : surj_inv (surjective_id) = ( id : α  α ) :=
funext (λ x, show id (surj_inv surjective_id x) = x,
                    from right_inverse_surj_inv surjective_id x)

Anas Himmi (May 08 2020 at 02:07):

also i have another question, when i have to prove such lemmas for other functions i need to prove that they are surjective when stating the lemma and not when actually proving it, isn't it possible avoid doing so?

Shing Tak Lam (May 08 2020 at 02:10):

I'm not too sure what you're asking? I thought the only function where the inverse is itself is id. So I'm not sure what you mean by that?

Anas Himmi (May 08 2020 at 02:13):

like here for example, i need to fill in the third underscore when stating the lemma (the left side), while it would seem more natural to do somehow do it when proving the lemme (the right side)

lemma surj_inv_add_one : @surj_inv _ _ (λ x:,x+1) _ = (λ x:,x-1) := _

Shing Tak Lam (May 08 2020 at 02:31):

I see what you mean. Like what refine does with underscores. I don't know if such a thing exists (or can even exist, intuition says no).

Sorry :(

Anas Himmi (May 08 2020 at 02:33):

okay, thank you for your help!

Pedro Minicz (May 08 2020 at 02:43):

How do I simplify factors 6 in the following example?

import data.nat.prime
open nat
example : factors 6 = [2, 3] := by simp

The simp tactic fails, although #eval factors 6 evaluates to [2, 3]. I have heard lean has multiple reduction engines, I think this may play a role here.

Mario Carneiro (May 08 2020 at 02:46):

norm_num [factors] may work

Mario Carneiro (May 08 2020 at 02:47):

rfl may also work

Bryan Gin-ge Chen (May 08 2020 at 02:49):

by norm_num [factors] works, but rfl doesn't. I was hoping dec_trivial would work, since we can #eval factors 6, but it doesn't.

Mario Carneiro (May 08 2020 at 02:51):

it's those darn pesky well founded recursions

Mario Carneiro (May 08 2020 at 02:52):

I wonder if it is possible to make some proofs reducible and get it to compute

Mario Carneiro (May 08 2020 at 04:00):

Aha, I figured out how to investigate this properly.

import data.nat.prime

meta def environment.head_position (env : environment) : expr  expr
| e :=
  match e.get_app_fn with
  | expr.const c _ :=
    if env.is_recursor c then
      match env.get c with
      | exceptional.success d :=
        environment.head_position (e.get_app_args.inth (d.type.pi_arity - 1))
      | _ := e
      end
    else match env.is_projection c with
    | some p := environment.head_position (e.get_app_args.inth p.nparams)
    | _ := e
    end
  | _ := e
  end

run_cmd do
  env  tactic.get_env,
  let whnf e := tactic.whnf e tactic.transparency.all,
  let e := `((nat.factors 6).tail),
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  e  env.head_position <$> whnf e,
  tactic.trace e
-- propext
--   (lt_iff_not_ge ((nat.add 3 (nat.add 1 0) + 2) / nat.min_fac (nat.add 3 (nat.add 1 0) + 2))
--      (nat.add 3 (nat.add 1 0) + 2))

The head_position function finds the location where reduction takes place in a term. In a stuck term, the reason the term is stuck is because there is a stuck term in the head position. For example, nat.rec_on (choice nat) bla bla is stuck because choice nat is stuck. It turns out that even if you unfold lemmas (using set_option type_context.unfold_lemmas true), the computation of factors is stuck because it uses a well founded recursion and if you reduce the proof of well foundedness you eventually hit propext, which is stuck because it is an axiom.

Mario Carneiro (May 08 2020 at 04:01):

You can try removing some of the env.head_position lines to see it work its way through the stuck proof terms, unfolding one and getting stuck on the next

Mario Carneiro (May 08 2020 at 04:22):

The problem is in this proof:

theorem div_lt_iff_lt_mul (x y : ) {k : } (Hk : k > 0) : x / k < y  x < y * k :=
begin
  simp [lt_iff_not_ge],
  apply not_iff_not_of_iff,
  apply le_div_iff_mul_le _ _ Hk
end

The simp call rewrites the two sides using propext lt_iff_not_ge (when it could use iff.trans instead, which does not block computation). It's a similar issue to what happens when you use rw to prove a decidability theorem.

Pedro Minicz (May 08 2020 at 18:58):

@Mario Carneiro Thank you very much! norm_num [factors] indeed works. Where can I read about normalization in Lean, it seems quite nuanced compared to most proof assistants due to its multiple reduction engines.

Pedro Minicz (May 08 2020 at 19:03):

Is there a way to mark a definition for automatic unfolding? Ideally, I would like to omit unfold perfect in the following proof:

import data.finset
import data.nat.prime
import tactic.norm_num

open nat list

def finset.sum (s : finset ) :  := finset.fold (+) 0 id s

-- This definition is wrong, by the way.
def perfect (n : ) := finset.sum (to_finset (factors n)) + 1 = n

example : perfect 6 :=
begin
  unfold perfect,
  norm_num [factors],
  refl,
end

Johan Commelin (May 08 2020 at 19:06):

PR #2589 is a overhaul of norm_num. But I'm not sure if you are looking for code (-;

Johan Commelin (May 08 2020 at 19:06):

I'm not aware of docs on norm_num though

Kevin Buzzard (May 08 2020 at 19:07):

Other than what's in the mathlib docs of course

Heather Macbeth (May 09 2020 at 02:33):

I have a nonempty metric space A (it satisfies h : nonempty A), and I am trying to extract an element of it. How can I do this? I tried set.nonempty.some h, but it seems that set.nonempty.some requires argument of type set.nonempty ?m_1, and nonempty A is not coerced to that.

Reid Barton (May 09 2020 at 02:40):

set.nonempty is a different thing, I think.

Bryan Gin-ge Chen (May 09 2020 at 02:41):

classical.choice h will get you an element of type A.

Reid Barton (May 09 2020 at 02:42):

The normal thing would be to use cases on h, or :this:

Heather Macbeth (May 09 2020 at 02:44):

Thank you!

Bryan Gin-ge Chen (May 09 2020 at 02:47):

It's a little embarrassing, but I had to look it up using library_search:

import topology.metric_space.basic

variables (A : Type*) [metric_space A]
example (h : nonempty A) : A := by library_search

(I couldn't remember if default worked for nonempty -- it doesn't, it requires inhabited.)

Johan Commelin (May 09 2020 at 02:57):

@Heather Macbeth nonempty A is a typeclass, so you can write [nonempty A]. If you do that, there is a tactic inhabit A, it will give you an element of A.

Johan Commelin (May 09 2020 at 02:57):

But doing cases also works.

Johan Commelin (May 09 2020 at 02:58):

Re Bryan: default indeed requires inhabited. The inhabit tactic lifts a nonempty A to inhabited A.

Mario Carneiro (May 09 2020 at 04:58):

Pedro Minicz said:

Mario Carneiro Thank you very much! norm_num [factors] indeed works. Where can I read about normalization in Lean, it seems quite nuanced compared to most proof assistants due to its multiple reduction engines.

Is there a way to mark a definition for automatic unfolding? Ideally, I would like to omit unfold perfect in the following proof:

If you mark factors and perfect with @[simp] or local attribute [simp], then by norm_num alone will work. You can also write norm_num [factors, perfect].

What is happening is this: the core norm_num tactic (which can be called using norm_num1) itself doesn't know anything about evaluating functions other than 2 + 2, 2 * 2, as well as prime 2 and min_fac 2, which are the key to evaluating factors. The actual tactic norm_num [factors] is equivalent to norm_num1, simp [factors], norm_num1, simp [factors], ... repeated until the goal stops changing. So it is bouncing back and forth between simp to do general reduction of functions "in the wild" like perfect, to_finset, and factors, and norm_num for evaluating things once all the arguments are numerals.

Kevin Buzzard (May 09 2020 at 07:19):

@Heather Macbeth if your goal is data then you have to use some because computer scientists think that moving from Prop to Type is the axiom of choice :rofl:

Kevin Buzzard (May 09 2020 at 07:20):

But if you just want the element to prove another Prop thencases is fine

Patrick Massot (May 09 2020 at 09:15):

@Heather Macbeth you probably need a bit more of the global context here. You're hitting one of the few places where constructivism managed to intrude into mathlib to try preventing people from doing mathematics (if you were still using Coq that would be everywhere). Because it's highly relevant to software programming and verification, there is a distinction between assuming something is not empty and happily giving you en element of that something. The first one is [nonempty X] and the second one is [inhabited X]. Fortunately there are tricks to go from one to the other, but it means you need to learn something which seems completely artificial. From our point of view, this is a bug, but we need to keep in mind that proof assistants are very flexible tools that have a wide range of applications. And without the other applications we wouldn't have these tools at all, since applications to math bring almost no money or academic positions (yet).

Anas Himmi (May 09 2020 at 13:47):

deleted

Anas Himmi (May 09 2020 at 14:23):

Hi, I have a problem with vscode right now where the output panel keeps appearing when i'm typing something, how to solve this?

Anas Himmi (May 09 2020 at 14:23):

it says LEAN ASSERTION VIOLATION

Kenny Lau (May 09 2020 at 14:25):

#mwe

Anas Himmi (May 09 2020 at 14:25):

nevermind, i just closed and reopened the file and now it works

Jalex Stark (May 09 2020 at 14:26):

I'm pleasantly surprised by how cheap it is to restart VSCode and how many problems can be solved by it

Mario Carneiro (May 09 2020 at 14:26):

classic microsoft

Anas Himmi (May 09 2020 at 14:27):

deleted

Ashwin Iyengar (May 10 2020 at 11:59):

This is a syntax question. Let's say P and Q are some propositions, and I'm in the situation

theorem T : P  Q := sorry

theorem : P := ???

Let's say I want to prove P and I know how to prove ¬Q in tactic mode. What's the cleanest way of proving P?

Ashwin Iyengar (May 10 2020 at 12:00):

I can do

begin
  cases T with h,
  {exact h},
  {exfalso, ...}
end

Ashwin Iyengar (May 10 2020 at 12:01):

But somehow it seems there should be a more elegant way

Shing Tak Lam (May 10 2020 at 12:04):

refine or.resolve_left _ or refine or.resolve_right _ depending on which way round the or is

Ashwin Iyengar (May 10 2020 at 12:36):

Ok so If I want to do this without entering tactic mode until I need to prove \not Q then I do

theorem T : P  Q := sorry

theorem x : P :=
by refine or.resolve_right T
begin
  sorry
end

?

Reid Barton (May 10 2020 at 12:38):

You can delete by refine here

Reid Barton (May 10 2020 at 12:38):

(by is tactic mode, so if your goal is to avoid entering tactic mode then you have to delete it)

Ashwin Iyengar (May 10 2020 at 12:42):

Ok thanks.

Golol (May 10 2020 at 18:15):

Where can I find the actual lean library with already formalized mathematics?

Kevin Buzzard (May 10 2020 at 18:17):

https://github.com/leanprover-community/mathlib

Kevin Buzzard (May 10 2020 at 18:17):

See also the documentation at https://leanprover-community.github.io/mathlib_docs/

Bhavik Mehta (May 10 2020 at 18:18):

See also https://github.com/leanprover-community/mathlib/blob/master/docs/theories.md

Golol (May 10 2020 at 18:29):

So, I checked this out a bit and it makes me wonder: Is there a guideline to the question which level of generality a piece of mathematics should be formalized? Should the divergence theorem be written down and proven in the way it is normally done in an analysis course, or should the most super general form of stokes theorem be formalized and then the divergence theorem is noted as a corollary of the more general theorem?

Kevin Buzzard (May 10 2020 at 18:30):

The mathlib philosophy is that the super-general form should be formalised first. This is why we had Bochner integrals before any integral taught to maths undergraduates.

Kevin Buzzard (May 10 2020 at 18:31):

We might have had multivariable calculus before single-variable calculus but I am less sure about this. We definitely had multivariable polynomial rings before single-variable polynomial rings.

Golol (May 10 2020 at 18:31):

And if someone comes up with an even new generalization "above" the current "top level" one, would it be optimal to adjust the database and prove the new even more general concept first?

Kevin Buzzard (May 10 2020 at 18:31):

You might well ask! But you'll have to ask those wacky computer scientists, not me :-)

Golol (May 10 2020 at 18:32):

Or is that exponentially complicated

Golol (May 10 2020 at 18:32):

Seems a bit like a strange situation, like building sth inside out but I cant pin it down. I also dont deeply understand lean yet ofc

Kevin Buzzard (May 10 2020 at 18:32):

@Patrick Massot and @Sebastien Gouezel can tell you about the design decisions for analysis in mathlib; they understand the situation very well.

Reid Barton (May 10 2020 at 18:32):

These are good questions.

Bryan Gin-ge Chen (May 10 2020 at 18:33):

We are very far from doing things optimally. The best thing to do is just try – try proving something, ask questions here if you get stuck, then try PRing it to mathlib and address the feedback that you'll get in response to that, etc. etc.

Reid Barton (May 10 2020 at 18:33):

It's reasonable to imagine that there is an infinite chain (or more complicated graph!) of generalizations, so there is no "most general" thing to formalize, yet you have to pick a point somewhere anyways.

Reid Barton (May 10 2020 at 18:34):

Also if you proved your theorem in some super duper ultra generalized setting, it might take some work to turn it back into the theorem in R3\mathbb{R}^3 or whatever.

Golol (May 10 2020 at 18:36):

yea, that makes sense, I suppose.

Kevin Buzzard (May 10 2020 at 18:40):

In mathlib the design decision appears to be to aim high.

Mario Carneiro (May 10 2020 at 18:40):

I would say aim for the tasks that you expect to accomplish

Reid Barton (May 10 2020 at 18:41):

Right because usually if X is a special case of Y, and you expect to want both X and Y, then it's less effort in total to prove Y and conclude X

Reid Barton (May 10 2020 at 18:42):

but the infinite regress eventually stops because Y is a special case of Z, but you don't care about Z or it's impractical to formalize it or you don't expect to get around to it any time soon

Golol (May 10 2020 at 18:43):

Mario Carneiro said:

I would say aim for the tasks that you expect to accomplish

Yea, I was just thinking about the "ultimate goal" where formalizing n lines of informal mathematics takes C * n lines in lean, where C is a constant. I hope this situation doesnt impact that

Mario Carneiro (May 10 2020 at 18:44):

Also, keep in mind that in some sense the "limiting case" of generalization is tautology, i.e. theorem X is true in all the cases when the prerequisites for X being true are satisfied

Mario Carneiro (May 10 2020 at 18:44):

So there is a tapering off of usefulness as you tend to more and more generality

Alex J. Best (May 10 2020 at 18:46):

One interesting feature of formalization is that for certain types of generalisation it can sometimes be far easier to generalise a result than it is on paper. Once a result is formalized you can start deleting assumptions and lean will tell you exactly what breaks and where, you can then fix that part and move on. It happens pretty often that once a result is formalized people realise some assumptions aren't necessary at all, they just helped us mentally when writing the initial proof. You won't get from one integration theory to another this way but if you have some hypothesis on a mathematical object that don't need to be there you can have an easier time proceeding this way.

Golol (May 10 2020 at 18:46):

That is true, from a practical view this is not really an issue

Mario Carneiro (May 10 2020 at 18:46):

Yea, I was just thinking about the "ultimate goal" where formalizing n lines of informal mathematics takes C * n lines in lean, where C is a constant. I hope this situation doesnt impact that

The thing is, you have to have a goal first, and that goal has to have boundaries. Given such a goal, there is an appropriate value of C, but if you later change or extend the goal then C will change (not necessarily up or down because both the informal and the formal versions grow)

Kevin Buzzard (May 10 2020 at 18:47):

In practice it took forever to define differentiation of functions from R\mathbb{R} to R\mathbb{R} even though we had limits, so there was an example where C was huge and could have been much smaller, but does it matter?

Reid Barton (May 10 2020 at 18:47):

This question of whether to treat the special case or to treat the general case and derive the special case also exists on the informal mathematics side.

Kevin Buzzard (May 10 2020 at 18:47):

On the informal side there is the third even more powerful approach of course

Reid Barton (May 10 2020 at 18:48):

I mean in general the "cost model" for informal mathematics is a lot different

Mario Carneiro (May 10 2020 at 18:48):

I think the issue with differentiation was that we could have done one variable derivatives at any time, but we held out for the more general case because there are people who need it now

Kevin Buzzard (May 10 2020 at 18:48):

Treat the special case and remark that the general case follows in a similar manner

Mario Carneiro (May 10 2020 at 18:49):

Doing derivatives the undergraduate way is not hard, but it would have been a decent chunk of work that would later be superceded

Mario Carneiro (May 10 2020 at 18:49):

considering how long we went without it as a result of holding out for the general version, that might have been worth it

Golol (May 10 2020 at 18:49):

Suppose I open an analysis book and start going at it. In formalize derivatives in 1 dimension. Then a few chapters later the total derivative is defined. Could it be possible to prove a theorem "the derivative is a special case of the total derivative" and make lean automatically replace all statements about the derivative with.. statements about the total derivative in 1 dimension. And replace the proofs with references to more general theorems. So the tower "rebuilds itself" adjusting for the new goal

Kevin Buzzard (May 10 2020 at 18:49):

Right. So I waited patiently even though there were students at Xena who wanted the chain rule

Reid Barton (May 10 2020 at 18:50):

Golol, maybe, but you still spent the time formalizing all the one-variable stuff.

Mario Carneiro (May 10 2020 at 18:50):

Golol said:

make lean automatically replace

no

Mario Carneiro (May 10 2020 at 18:51):

these things are never as automatic as you would hope

Golol (May 10 2020 at 18:51):

aah a shame :D

Golol (May 10 2020 at 18:52):

Thanks for the answers, very interesting.

Mario Carneiro (May 10 2020 at 18:52):

However, you can do that, write a PR making the necessary changes and so on, and it is probably not that expensive, maybe a day's work for a month or more worth of material

Mario Carneiro (May 10 2020 at 18:53):

But if I were in that position, I would have kicked myself for not reading ahead in the book and realizing I was going to to this eventually

Kevin Buzzard (May 10 2020 at 18:53):

We occasionally change definitions, for example the definition of a normal subgroup will change at some point in the future to something mathematically equivalent, and then one will have to go and redo all the proofs

Mario Carneiro (May 10 2020 at 18:54):

not all the proofs, just the foundational ones

Kevin Buzzard (May 10 2020 at 18:54):

Getting it right the first time is not always easy

Ashwin Iyengar (May 10 2020 at 19:22):

Given an element x : ideal.quotient I for some I : ideal R how does one prove that there exists a lift in R? I can't seem to find it anywhere. Is quot.ind the right thing?

Bhavik Mehta (May 10 2020 at 19:23):

exists_rep

Patrick Massot (May 10 2020 at 20:16):

Kevin Buzzard said:

On the informal side there is the third even more powerful approach of course

And I guessed right!

Patrick Massot (May 10 2020 at 20:30):

Golol said:

So, I checked this out a bit and it makes me wonder: Is there a guideline to the question which level of generality a piece of mathematics should be formalized? Should the divergence theorem be written down and proven in the way it is normally done in an analysis course, or should the most super general form of stokes theorem be formalized and then the divergence theorem is noted as a corollary of the more general theorem?

Others already explained the general issue pretty well, but I can comment on the specific question of Stokes. In this case the theorem is complicated enough that optimization is welcome. And here the optimization is clearly to prove the general version first. Actually I've never seen a satisfying account of the special case. Without the proper geometric setup this theorem is very hard to prove rigorously. With the proper geometric setup you prove the general case.

Ryan Lahfa (May 10 2020 at 23:03):

Given a ≤ b, with a b: R, how can I have \exists ϵ > 0, a = b - ϵ ? I tried cases, but it didn't work (bonus question with <)

Kenny Lau (May 10 2020 at 23:04):

you can't

Ryan Lahfa (May 10 2020 at 23:05):

good news

Ryan Lahfa (May 10 2020 at 23:06):

@Kenny Lau but why? like, obviously, ϵ = b - a works, right?

Kenny Lau (May 10 2020 at 23:06):

reread your question

Ryan Lahfa (May 10 2020 at 23:07):

Kenny Lau said:

reread your question

fixed

Kenny Lau (May 10 2020 at 23:07):

still not true

Ryan Lahfa (May 10 2020 at 23:10):

Kenny Lau said:

still not true

a - b ≤ 0, thus: b - a ≥ 0, taking e = b - a, a = b - e = b - (b - a) = a, right?
Is there any counterexample, it's a bit late so I might sound completely stupid

Kenny Lau (May 10 2020 at 23:13):

ϵ > 0?

Ryan Lahfa (May 10 2020 at 23:13):

Indeed, ≥

Ryan Lahfa (May 10 2020 at 23:14):

Kenny Lau said:

ϵ > 0?

Fixed, but even assuming the fix, is there a way?

Kenny Lau (May 10 2020 at 23:15):

use b-a

Ryan Lahfa (May 10 2020 at 23:16):

Kenny Lau said:

use b-a

I was more thinking of a lemma which takes a a ≤ b and outputs an hypothesis h: a = b + ϵ and h2: ϵ ≥ 0 and ϵ: R

Ryan Lahfa (May 10 2020 at 23:17):

which I could do using obtain < ϵ, hϵ, h > := this_lemma

Kenny Lau (May 10 2020 at 23:19):

why not just take b-a

Ryan Lahfa (May 10 2020 at 23:21):

Kenny Lau said:

why not just take b-a

Maybe I'm wrong but it looks simple rather than having to pause in the proof and show that I have this b - a, which verifies hϵ and h, but maybe there is simpler things to do?

Mario Carneiro (May 10 2020 at 23:25):

not really, it sort of gets rolled up into later parts of the proof

Ryan Lahfa (May 10 2020 at 23:26):

Makes sense

Ryan Lahfa (May 10 2020 at 23:28):

But doesn't it become too repetitive; is there any example of how it's done in mathlib @Mario Carneiro ?

Mario Carneiro (May 10 2020 at 23:28):

do you have a #mwe?

Mario Carneiro (May 10 2020 at 23:28):

I can demonstrate with an example

Reid Barton (May 10 2020 at 23:29):

If you really want, you can just state and prove a lemma yourself

Ryan Lahfa (May 10 2020 at 23:30):

import data.real.basic
def converge (x:   ) (l : ) :=
   ε > 0,  N,  n  N, (abs (l - (x n))  < ε)

def limit_ge_of_seq_ge (x:   ) (l: ) (H: converge x l) (a: ): ( n, x n  a)  l  a :=
begin
intro Hineq,
by_contra hla,
push_neg at hla,
sorry
-- obtain ⟨ ε, hε, h ⟩ := eq_of_lt hla,
end

Ryan Lahfa (May 10 2020 at 23:31):

Reid Barton said:

If you really want, you can just state and prove a lemma yourself

this is what I'm doing, but really curious of the proper way

Ryan Lahfa (May 10 2020 at 23:32):

@Mario Carneiro ^

Reid Barton (May 10 2020 at 23:33):

You missed the part about "working" I think. what are your imports? what is d?

Ryan Lahfa (May 10 2020 at 23:35):

Reid Barton said:

You missed the part about "working" I think. what are your imports? what is d?

Fixed, sorry for that

Ryan Lahfa (May 10 2020 at 23:55):

Here's my full proof up to the famous lemma:

import data.real.basic
def converge (x:   ) (l : ) :=
   ε > 0,  N,  n  N, (abs (l - (x n))  < ε)

lemma eq_of_lt {x a: }: x < a   ε > 0, a = x + ε := sorry
def limit_ge_of_seq_ge (x:   ) (l: ) (H: converge x l) (a: ): ( n, x n  a)  l  a :=
begin
intro Hineq,
by_contra hla,
push_neg at hla,
obtain  ε, , h  := eq_of_lt hla,
obtain  N, hcv  := H (ε/2) (by linarith),
have hcv_N: (x N) - l < ε/2 := by calc
    (x N) - l  abs ((x N) - l) : le_abs_self _
    ... = abs (l - (x N)) : abs_sub _ _
    ... < ε/2 : hcv N (by simp),
  have Hineq_N: (x N)  l + ε := by calc
    (x N)  a : Hineq _
    ... = l + ε : h,
  linarith,
end

Don't know if that could be trivially done, note that I didn't really think through the mathematical content, so maybe a direct proof would look better

Mario Carneiro (May 11 2020 at 00:12):

Here's a really simple modification that avoids the lemma:

import data.real.basic
def converge (x:   ) (l : ) :=
   ε > 0,  N,  n  N, (abs (l - (x n))  < ε)

def limit_ge_of_seq_ge (x:   ) (l: ) (H: converge x l) (a: ): ( n, x n  a)  l  a :=
begin
intro Hineq,
by_contra hla,
push_neg at hla,
set ε := a - l with ,
obtain  N, hcv  := H (ε/2) (by linarith),
have hcv_N: (x N) - l < ε/2 := by calc
    (x N) - l  abs ((x N) - l) : le_abs_self _
    ... = abs (l - (x N)) : abs_sub _ _
    ... < ε/2 : hcv N (by simp),
  have Hineq_N: (x N)  l + ε := by calc
    (x N)  a : Hineq _
    ... = l + ε : by linarith,
  linarith,
end

Mario Carneiro (May 11 2020 at 00:16):

and here's a more radical simplification making use of linarith for all the hard work:

import data.real.basic
def converge (x:   ) (l : ) :=
   ε > 0,  N,  n  N, (abs (l - (x n))  < ε)

theorem limit_ge_of_seq_ge (x:   ) (l: ) (H: converge x l) (a: ): ( n, x n  a)  l  a :=
begin
  intro Hineq,
  by_contra hla,
  obtain  N, hcv  := H ((a-l)/2) (by linarith),
  linarith [Hineq N, (abs_lt.1 (hcv N (le_refl _))).1],
end

Ryan Lahfa (May 11 2020 at 00:21):

That was super clear, many thanks @Mario Carneiro

Patrick Massot (May 11 2020 at 07:18):

@Ryan Lahfa you should probably do the tutorials exercises anyway. You're mostly beyond what is taught here, but this kind of lemma is totally covered there. Also you should stop using that "noob question(s)" thread.

Ryan Lahfa (May 11 2020 at 11:34):

Patrick Massot said:

Ryan Lahfa you should probably do the tutorials exercises anyway. You're mostly beyond what is taught here, but this kind of lemma is totally covered there. Also you should stop using that "noob question(s)" thread.

Sorry for that, I thought that sounded like a noob question, where are tutorials exercises?

Kevin Buzzard (May 11 2020 at 11:35):

#tutorials ?

Kevin Buzzard (May 11 2020 at 11:35):

oh no way, it worked :D

Jalex Stark (May 11 2020 at 12:12):

sure it's a noob question, but we'd rather that the noob question thread did not exist. new questions get new threads.

Aniruddh Agarwal (May 15 2020 at 17:53):

Is the following a valid way to state "If R is a commie ring, x is a nilpotent of R and 1 is the mult. identity of R, then 1 + x is a unit.":

theorem one_add_nilpotent {R : Type*} [comm_ring R] {r : R} {n : } (hr : r ^ n = 0) : is_unit (1 + r)
:= sorry

Johan Commelin (May 15 2020 at 17:55):

We might want to add is_nilpotent

Johan Commelin (May 15 2020 at 17:55):

But your statement looks fine to me.

Johan Commelin (May 15 2020 at 17:56):

The name would be is_unit_add_one_of_is_nilpotent

Aniruddh Agarwal (May 15 2020 at 17:56):

I wasn't sure if lean would automatically apply the homomorphism Z -> R for me or not

Mario Carneiro (May 15 2020 at 17:56):

well, one_add with this version

Bhavik Mehta (May 15 2020 at 17:56):

Doesn't nilpotent include that n > 0? I'm pretty sure your result would still be true but anyway

Mario Carneiro (May 15 2020 at 17:57):

If n = 0, then 1 = 0 and therefore r ^ 1 = 0 too

Bhavik Mehta (May 15 2020 at 17:58):

Mario Carneiro said:

If n = 0, then 1 = 0 and therefore r ^ 1 = 0 too

Yeah but nilpotent elements are defined to be non-zero if I remember right

Bhavik Mehta (May 15 2020 at 17:59):

As in, the result should still be provable but it's not exactly the statement desired

Mario Carneiro (May 15 2020 at 18:00):

hm, I would leave that out of the definition

Alex J. Best (May 15 2020 at 18:00):

I'm not sure, I think you want statements like "the set of nilpotents is the intersection of all prime ideals" rather than "the set of nilpotents and zero is the intersection of all primes" but maybe thats just me

Mario Carneiro (May 15 2020 at 18:00):

that doesn't sound like the kind of constraint that would be useful for theorem proving

Johan Commelin (May 15 2020 at 18:00):

Well, I would like { x | is_nilpotent x } to be an ideal

Bhavik Mehta (May 15 2020 at 18:01):

I think I'm not being clear! I absolutely don't mean to say that OP is wrong or insist on any definition of nilpotent, but just to say that OP's question was whether or not they'd stated the maths theorem in lean correctly - going by the definition of nilpotent on wikipedia and mathworld, they would need n > 0 for it to match perfectly

Alex J. Best (May 15 2020 at 18:03):

btw I added an epsilon of things on nilpotents in https://github.com/leanprover-community/mathlib/pull/1822/files if you want to extract them (hopefully I'll clean it up this branch one day soon)

Reid Barton (May 15 2020 at 18:03):

Huh. nlab helpfully says "natural number nn"

Reid Barton (May 15 2020 at 18:03):

(obviously, it doesn't make a real difference for reasons already given above)

Reid Barton (May 15 2020 at 18:05):

stacks project says "for some nNn \in \mathbf{N}"

Reid Barton (May 15 2020 at 18:05):

of course the stacks project probably defines N\mathbf{N}... somewhere

Johan Commelin (May 15 2020 at 18:05):

But they define N

Johan Commelin (May 15 2020 at 18:06):

https://stacks.math.columbia.edu/tag/055X

Johan Commelin (May 15 2020 at 18:06):

No 0

Johan Commelin (May 15 2020 at 18:06):

It's not even a semiring...

Johan Commelin (May 15 2020 at 18:07):

Comments (2)

Comment #3544 by Laurent Moret-Bailly on August 27, 2018 at 14:12
Do you really mean that 0∉N?

Comment #3676 by Johan on October 22, 2018 at 10:26
Yes, I do. Also, I think it would be too late to change it now. Sorry if this isn't the standard convention.

Aniruddh Agarwal (May 15 2020 at 18:08):

Also, can someone show me how to define the jacobson radical (intersection of all maximal ideals) of a commutative ring in Lean? I don't think this is defined in mathlib

Johan Commelin (May 15 2020 at 18:09):

I think it's the last PR in the list

Johan Commelin (May 15 2020 at 18:09):

#768

Johan Commelin (May 15 2020 at 18:09):

please-adopt

Carlo Cabrera (May 15 2020 at 19:40):

Hey, I just wanted to check I'm parsing the following definition (from the tutorials) correctly:

Carlo Cabrera (May 15 2020 at 19:40):

def is_seq_sup (M : ℝ) (u : ℕ → ℝ) := (∀ n, u n ≤ M) ∧ ∀ ε > 0, ∃ n₀, u n₀ ≥ M - ε

Does this mean that for each n with ```u n \le

Carlo Cabrera (May 15 2020 at 19:40):

Ugh, sorry. I keep sending the message before I've finished. Let me try that again.

Johan Commelin (May 15 2020 at 19:41):

Uncheck "Press Enter to send"

Johan Commelin (May 15 2020 at 19:41):

Then you can send with Ctrl-Enter

Patrick Stevens (May 15 2020 at 19:41):

"M is a sup of sequence u" is defined to be "all members of u are less than or equal to M, and u gets arbitrarily close to M"

Carlo Cabrera (May 15 2020 at 19:42):

Patrick Stevens said:

"M is a sup of sequence u" is defined to be "all members of u are less than or equal to M, and u gets arbitrarily close to M"

Great, thanks. For some reason I was reading that as for all n`` such that u n \le M```, which would obviously not be the right definition.

Patrick Stevens (May 15 2020 at 19:43):

You can quote things inline using single backticks - triple backticks mark out a multi-line block

Johan Commelin (May 15 2020 at 19:43):

See #backticks

Carlo Cabrera (May 15 2020 at 19:44):

Thank you. That helps.

Carlo Cabrera (May 15 2020 at 19:46):

Patrick Stevens said:

"M is a sup of sequence u" is defined to be "all members of u are less than or equal to M, and u gets arbitrarily close to M"

So this is not quite a standard definition, no? Or am I confused? For instance, the sequence {1/n}, I would typically think of the sup of that sequence as 1, but that does not satisfy the definition I stated.

Carlo Cabrera (May 15 2020 at 19:47):

Here n is in {1,2,3,...}.

Jalex Stark (May 15 2020 at 19:47):

Carlo Cabrera said:

Patrick Stevens said:

"M is a sup of sequence u" is defined to be "all members of u are less than or equal to M, and u gets arbitrarily close to M"

So this is not quite a standard definition, no? Or am I confused? For instance, the sequence {1/n}, I would typically think of the sup of that sequence as 1, but that does not satisfy the definition I stated.

yes, 1/n gets arbitrarily close to 1
for any ε>0\varepsilon>0 there is an element of the sequence within ε\varepsilon of 1
(namely, it's 1)

Carlo Cabrera (May 15 2020 at 19:48):

Right, of course, I am just confused. Thank you!

Patrick Stevens (May 15 2020 at 19:48):

Specifically, 1 is that element, for all epsilon

Carlo Cabrera (May 15 2020 at 19:54):

Ah, yes. I see it now. I was mentally inserting parentheses in the wrong place. Silly me. Thanks for assist, everyone.

Aniruddh Agarwal (May 15 2020 at 20:01):

Does leanpkg build always do a full build, or does it only compile the files that were changed (and their dependents) since the last build?

Johan Commelin (May 15 2020 at 20:01):

The last one

Johan Commelin (May 15 2020 at 20:01):

You need to manually remove all .olean files if you want a full build

Aniruddh Agarwal (May 15 2020 at 21:12):

https://github.com/leanprover-community/mathlib/pull/2691

@Kevin Buzzard @Kenny Lau I believe this addresses the issues with #768?

Kevin Buzzard (May 15 2020 at 21:14):

But you don't add the Jacobson ring stuff?

Aniruddh Agarwal (May 15 2020 at 21:14):

Oh is there a list of stuff that needs to be added?

Bryan Gin-ge Chen (May 15 2020 at 21:14):

It's a PR into Kenny's branch.

Aniruddh Agarwal (May 15 2020 at 21:15):

I just addressed @Chris Hughes's comments on the PR

Kevin Buzzard (May 15 2020 at 21:15):

I missed that it was a PR to the branch. Many thanks!

Reid Barton (May 15 2020 at 21:15):

Github could have made that more obvious

Kenny Lau (May 15 2020 at 21:16):

thanks!

Aniruddh Agarwal (May 15 2020 at 21:17):

Actually I would be interested in formalizing some stuff about Jacobson rings. I could start by looking at what the stacks project has on the subject and try to formalize that?

Aniruddh Agarwal (May 15 2020 at 21:17):

Np, it was really easy and I don't really understand what I did tbh

Kevin Buzzard (May 15 2020 at 21:17):

That's a great idea. For any new concept, such as Jacobson rings, it's good to make a decent API.

Bryan Gin-ge Chen (May 15 2020 at 21:17):

I edited the PR comment. I suggest discussing this PR and related matters in a separate thread.

Kenny Lau (May 15 2020 at 21:18):

I merged it, but it seems like there are unresolved conflicts with master

Reid Barton (May 15 2020 at 21:18):

Imagine that!

Kenny Lau (May 15 2020 at 21:20):

<<<<<<< primary-ideal
theorem is_prime.comap {hK : K.is_prime} : (comap f K).is_prime :=
=======
instance is_prime.comap [hK : K.is_prime] : (comap f K).is_prime :=
>>>>>>> master

Kenny Lau (May 15 2020 at 21:20):

I don't know what to do with this. Advice?

Kenny Lau (May 15 2020 at 21:21):

the change is in accordance with one of Chris' comments

Reid Barton (May 15 2020 at 21:30):

When in doubt don't make it an instance

Apurva Nakade (May 15 2020 at 22:06):

When simp tactic solves a goal, is it possible to make Lean print out all the steps it had taken internally to reach the goal?

Bryan Gin-ge Chen (May 15 2020 at 22:07):

Yes, add this before your code: set_option trace.simplify.rewrite true

See https://leanprover-community.github.io/extras/simp.html for more tricks.

Apurva Nakade (May 15 2020 at 22:08):

Thanks!

Jalex Stark (May 15 2020 at 22:10):

hi Apurva! an intermediate step between naked simp and printing everything is to use squeeze_simp, which just tells you which lemmas simp used, and also lets you replace your slow simp with a fast simp only [list, of, lemmas]

Apurva Nakade (May 15 2020 at 22:14):

Hey Jalex! :D

Thanks, this is really cool.

Jalex Stark (May 15 2020 at 22:19):

it's possible you came here from this link, but if not you should check out our "community webpage"
https://leanprover-community.github.io/

Apurva Nakade (May 15 2020 at 23:10):

Yeah, or at least I think so. Looks like the website has gotten updated!

Jalex Stark (May 15 2020 at 23:11):

yes, it's definitely still in development, so if there's any information you think should be more or less obvious there, let us know :)

Aniruddh Agarwal (May 15 2020 at 23:15):

How do I ask Lean to unroll definitions?

Jalex Stark (May 15 2020 at 23:16):

unfold <name> often works

Jalex Stark (May 15 2020 at 23:17):

if you post #mwe showing the place you want a definition unfolded, then I could play with it and give a more useful answer

Mario Carneiro (May 15 2020 at 23:20):

rw <name>, simp [<name>], dsimp [<name>], unfold <name>, dunfold <name>, and delta <name> can all be used to unfold definitions

Mario Carneiro (May 15 2020 at 23:21):

if you aren't sure which is appropriate, unfold and rw are good choices

Aniruddh Agarwal (May 15 2020 at 23:25):

I was looking for unfold, thank you!

Aniruddh Agarwal (May 16 2020 at 00:36):

I'm getting tons of errors when compiling mathlib locally...is that a problem?

Kevin Buzzard (May 16 2020 at 00:37):

leanproject up should be doing all of this for you.

Kevin Buzzard (May 16 2020 at 00:38):

The errors might mean that you're e.g. using the wrong version of Lean to compile. leanproject will get it right.

Aniruddh Agarwal (May 16 2020 at 00:39):

I simply ran leanpkg build. Looks like it was related to some changes I made (I simply added an extra import to one file, and that caused an explosion of errors). Reverting to master seems to have fixed it

Kevin Buzzard (May 16 2020 at 00:40):

Don't use leanpkg any more. It doesn't work with modern Lean I don't think. It's a deprecated tool, completely replaced by leanproject.

Reid Barton (May 16 2020 at 00:40):

No it's completely fine still

Kevin Buzzard (May 16 2020 at 00:41):

I know leanproject still uses it under the hood but I'm telling the party line aren't I? It might still work but it's also deprecated.

Kevin Buzzard (May 16 2020 at 00:41):

I thought something didn't work with Leans which weren't <= 3.4.2

Reid Barton (May 16 2020 at 00:42):

Oh if this is for propaganda purposes, then okay

Reid Barton (May 16 2020 at 00:42):

I still use leanproject

Reid Barton (May 16 2020 at 00:42):

I mean leanpkg

Aniruddh Agarwal (May 16 2020 at 00:44):

Is leanproject up a drop-in replacement for leanpkg build?

Aniruddh Agarwal (May 16 2020 at 00:45):

Oh...looks like it just downloads compiled lean files whenever it can

Aniruddh Agarwal (May 16 2020 at 00:45):

/me feels sad for having made my poor laptop compile the entire mathlib many, many times

Aniruddh Agarwal (May 16 2020 at 00:48):

Actually leanproject up doesn't seem to work when I edit files

Kevin Buzzard (May 16 2020 at 00:49):

And "whenever it can" includes all branches of mathlib, but obviously it can't download oleans from a cloud storage if you have just edited a lean file on your machine and the olean file does not yet exit

Aniruddh Agarwal (May 16 2020 at 00:49):

I was hoping that it would detect the edited files and dependents, and just recompile those

Bryan Gin-ge Chen (May 16 2020 at 00:49):

Yeah, leanproject up only downloads files, if you want to compile stuff you still have to run leanpkg build (which just runs lean --make src/ I think).

Bryan Gin-ge Chen (May 16 2020 at 00:50):

Lean already does its best to not compile stuff it doesn't have to. The problem is that any file that (transitively) imports a changed file has to be recompiled.

Aniruddh Agarwal (May 16 2020 at 00:57):

Ok, I'm trying to state that ordered abelian groups are torsion-free. I figured something like:

theorem [ordered_add_comm_group α] {a : α} {n : } : n * a = 0  n = 0 := sorry

would do it, but Lean doesn't like the n * a expression (it wants a to be a natural). Any advice on how to proceed?

Jalex Stark (May 16 2020 at 00:59):

do it with a multiplicative group and write a^n?

Bryan Gin-ge Chen (May 16 2020 at 00:59):

Instead of *, I think you want to use whatever symbol is used for smul (scalar multiplication).

Bryan Gin-ge Chen (May 16 2020 at 01:00):

See https://github.com/leanprover-community/mathlib/blob/master/src/group_theory/group_action.lean

Aniruddh Agarwal (May 16 2020 at 01:06):

Oof, importing group.theory.group_action in algebra.ordered_group is causing a cyclical import error

Kevin Buzzard (May 16 2020 at 01:12):

\bub is the notation for the nat action on an additive group

Kevin Buzzard (May 16 2020 at 01:13):

* has type X-> X-> X so can't be used here. This explains your error

Aniruddh Agarwal (May 16 2020 at 01:14):

I think I need to include group_theory.group_action to be able to use \bub

Jalex Stark (May 16 2020 at 01:14):

Aniruddh Agarwal said:

Oof, importing group.theory.group_action in algebra.ordered_group is causing a cyclical import error

uh, I think what you want to be doing is making a new file that imports both of these
unless you're contributing to mathlib and have some very strong reason that your theorems need to go into that file

Jalex Stark (May 16 2020 at 01:16):

If when writing a math paper, you needed to make a definition in section 5 that required notions from sections 2 and 3, this is fine

Jalex Stark (May 16 2020 at 01:16):

but you would try not to put that notion into section 2

Jalex Stark (May 16 2020 at 01:16):

Lean instead forces this good practice on you

Aniruddh Agarwal (May 16 2020 at 01:18):

Huh I guess it would be nice to be able to contribute this to mathlib eventually. I'm working towards some results about valuation rings

Jalex Stark (May 16 2020 at 01:32):

sure, regardless of what you're going to do, you should not currently be doing your work in the ordered_ring file in the library. that is sure to cause you all sorts of problems.

Jalex Stark (May 16 2020 at 01:33):

you should have a project that "depends on mathlib" in the sense that it comes out of leanproject new <project-name>

Jalex Stark (May 16 2020 at 01:33):

and then you can write new files in the src/ directory of that project

Bryan Gin-ge Chen (May 16 2020 at 01:37):

Well, if the work is likely to be contributed to mathlib then it could also make sense to work directly on the mathlib files in a branch. The question of which file(s) new material should go into can get tricky, but you could always create a new file somewhere arbitrary and move things after you get feedback.

Aniruddh Agarwal (May 16 2020 at 01:42):

This is what I have so far:

import algebra.ordered_group
import group_theory.group_action

open ordered_add_comm_group

universe u
variable {α : Type u}

variables [ordered_add_comm_group α] {a : α}
variables (n : )

theorem torsion_free : n  a = 0  n = 0 := sorry

and I'm getting this error:

configuring mathlib 0.1
> lean --make src
/Users/anrddh/dev/lean/mathlib/src/ring_theory/valuation_rings.lean:11:30: error: failed to synthesize type class instance for
α : Type u,
_inst_1 : ordered_add_comm_group α,
a : α,
n : 
 has_scalar  α
external command exited with status 1

Jalex Stark (May 16 2020 at 01:57):

I'm not at Lean right now, but what if you try integer n instead of natural n?

Bryan Gin-ge Chen (May 16 2020 at 01:58):

It works if you replace your imports with import algebra.module.

Aniruddh Agarwal (May 16 2020 at 01:59):

Makes sense, ty!

Bryan Gin-ge Chen (May 16 2020 at 01:59):

I think this might be the relevant instance.

Bryan Gin-ge Chen (May 16 2020 at 02:26):

Yep, and here's how I confirmed this guess:

import algebra.module

open ordered_add_comm_group

universe u
variable {α : Type u}

variables [ordered_add_comm_group α] {a : α}
variables (n : )

-- At first I tried using:
-- set_option trace.class_instances true
-- but that spits out hundreds of lines of output which is hard to parse.

-- So I opted for a more manual way
lemma blah : has_scalar  α := by apply_instance
-- What did `apply_instance` find?
#print blah
/-
theorem blah : Π {α : Type u} [_inst_1 : ordered_add_comm_group α], has_scalar ℕ α :=
λ {α : Type u} [_inst_1 : ordered_add_comm_group α], mul_action.to_has_scalar
-/
-- So now I want to know where the `mul_action` comes from
lemma blah2 : mul_action  α := by apply_instance

#print blah2
/-
theorem blah2 : Π {α : Type u} [_inst_1 : ordered_add_comm_group α], mul_action ℕ α :=
λ {α : Type u} [_inst_1 : ordered_add_comm_group α], distrib_mul_action.to_mul_action
-/
-- And where does `distrib_mul_action` come from?
lemma blah3 : distrib_mul_action  α := by apply_instance

#print blah3
/-
theorem blah3 : Π {α : Type u} [_inst_1 : ordered_add_comm_group α], distrib_mul_action ℕ α :=
λ {α : Type u} [_inst_1 : ordered_add_comm_group α], semimodule.to_distrib_mul_action
-/
-- Aha, now we know a `semimodule` instance is in play
lemma blah4 : semimodule  α := by apply_instance

#print blah4
/-
theorem blah4 : Π {α : Type u} [_inst_1 : ordered_add_comm_group α], semimodule ℕ α :=
λ {α : Type u} [_inst_1 : ordered_add_comm_group α], add_comm_monoid.nat_semimodule
-/
-- and there it is.

Aniruddh Agarwal (May 16 2020 at 21:17):

What's the difference between:

theorem is_prime.comap [hK : K.is_prime] : (comap f K).is_prime :=
comap_ne_top _ hK.1, λ x y,
  by simp only [mem_comap, f.map_mul]; apply hK.2

and

theorem is_prime.comap {hK : K.is_prime} : (comap f K).is_prime :=
comap_ne_top _ hK.1, λ x y,
  by simp only [mem_comap, f.map_mul]; apply hK.2

Patrick Stevens (May 16 2020 at 21:26):

In the former, you're saying that whenever is_prime.comap is called, the type class resolution system will try to find an instance of is_prime K somewhere in the ambient world - which may involve "looking arbitrarily far away" from the call site. (Analogy: once you've proved that Z is a ring, Lean's type class resolution system will be able to supply this fact wherever it's required later.) In the latter, you're asking the caller of is_prime.comap to provide an instance of is_prime K at every call, but you're also kind of promising that local type inference will be able to find an appropriate instance locally each time without the programmer having to specify it manually.

Patrick Massot (May 16 2020 at 21:28):

Here, the second version is doomed

Patrick Massot (May 16 2020 at 21:29):

You need to read https://leanprover.github.io/theorem_proving_in_lean/dependent_type_theory.html#implicit-arguments and maybe https://leanprover.github.io/theorem_proving_in_lean/interacting_with_lean.html#more-on-implicit-arguments

Aniruddh Agarwal (May 16 2020 at 21:29):

Hmm, why would I ever prefer the second version to the first version?

Patrick Massot (May 16 2020 at 21:29):

In this case the second version will fail

Patrick Massot (May 16 2020 at 21:30):

But in general this mechanism is very useful, and much cheaper than the square bracket one

Kevin Buzzard (May 16 2020 at 21:41):

These are extremely delicate issues for a new Lean user

Kevin Buzzard (May 16 2020 at 21:45):

@Aniruddh Agarwal here is the way I explain it to beginners. Lean is all about making functions, and functions have inputs. Lean function inputs are basically of three kinds, and you can tell them apart by the brackets they use: (x), {x} and [x]. The first kind of bracket means "the user will supply this input when they are writing the code". The second of bracket means "an algorithm called unification, written in C++ and which you don't need to know too much about right now, will supply this input" and the third means "an algorithm called type class inference, written in C++ and which you don't need to know too much about right now, will supply this input". That's the place to start.

Kevin Buzzard (May 16 2020 at 21:46):

I wrote a lot of functions with () inputs initially, because they were the only ones I understood. Then I got into {} a bit because actually it's not too hard. It took me a long time to understand [] and in some sense only very few people here understand it well. But for the longest time I just did was I was told -- people would advise me on which brackets to use and I would use them as directed.

Kevin Buzzard (May 16 2020 at 21:47):

{} just means "if you think about, you can actually guess this input yourself". [] means "if you put enough information into a system called the type class system, Lean will be able to figure this one out".

Jalex Stark (May 16 2020 at 22:41):

@Aniruddh Agarwal, make new topics for new questions please! This topic is a hot mess, and you're generating questions for which it would be nice to have easily-findable answers later :)

Scott Morrison (May 17 2020 at 04:36):

@Aniruddh Agarwal, I think you can actually go back to your first question, and edit the topic there. There's a checkbox for "change for later messages in this topic", too.

Billy Price (May 22 2020 at 02:24):

How does one work with an ite expression in a tactic proof?

Scott Morrison (May 22 2020 at 02:30):

split_ifs

Scott Morrison (May 22 2020 at 02:31):

(or rw if_pos)

Apurva Nakade (May 24 2020 at 15:15):

I am trying to run this code:

import tactic data.zmod.basic data.nat.basic data.int.basic

lemma f (a : ) : (a + 1)  (a^3 + 1) :=
begin
rw char_p.int_cast_eq_zero_iff (zmod (a+1)) (a+1) (a^3 + 1),
sorry,
end

and am getting this error:

rewrite tactic failed, did not find instance of the pattern in the target expression
  (a + 1)  a ^ 3 + 1
state:
a : 
 a + 1  a ^ 3 + 1

It looks like LEAN is unable to coerce but I am not able to figure out how to do that explicitly.

This is the output of : #check char_p.int_cast_eq_zero_iff

char_p.int_cast_eq_zero_iff :
   (R : Type u_1) [_inst_1 : ring R] (p : ) [_inst_2 : char_p R p] (a : ), a = 0  p  a

Alex J. Best (May 24 2020 at 15:18):

I think you need char_p.cast_eq_zero_iff as your a is a nat not an int.

Alex J. Best (May 24 2020 at 15:22):

I'd recommend you change a to be an int instead however to prove this.

Apurva Nakade (May 24 2020 at 15:24):

Thanks a lot !! That worked.

It is a bit annoying though that there are different set of theorems for nat and int and lean cannot coerce automatically between the two.
The nomenclature is also a bit inconsistent. I spent a lot of time searching for a nat_cast_eq_zero_iff :-/

Scott Morrison (May 24 2020 at 15:25):

It turns out dealing with the difference between nat and int is hard... :-)

Scott Morrison (May 24 2020 at 15:26):

You definitely want to learn about norm_cast, push_cast, and their friends.

Scott Morrison (May 24 2020 at 15:26):

But it is still a struggle.

Apurva Nakade (May 24 2020 at 15:32):

Haha, will do!

Wonder if it is possible/worthwhile to create meta-theorems that can pick the appropriate nat or int versions based on the input, some kind of a Sigma-type perhaps...

Kevin Buzzard (May 24 2020 at 15:34):

If you are not a masochist then my advice is to use integers from the start if you are doing arithmetic. Or even rationals, if you have division.

Kevin Buzzard (May 24 2020 at 15:37):

You (hopefully) don't need to know any of the names of the lemmas if you learn how to use norm_cast:

import tactic data.zmod.basic data.nat.basic data.int.basic

lemma f (a : ) : (a + 1)  (a^3 + 1) :=
begin
  suffices : (a : ) + 1  (a : )^3 + 1,
    norm_cast at this,
    assumption,
  -- now this is a question about integers
  sorry
end

But if this is the first line of your proof, then why didn't you just let a be an integer from the start?

Apurva Nakade (May 24 2020 at 15:45):

Yes, I will switch to integers. I was just playing around and couldn't figure out why coercion wasn't working.
Thanks!

Kevin Buzzard (May 24 2020 at 15:52):

Apurva Nakade said:

It is a bit annoying though that there are different set of theorems for nat and int and lean cannot coerce automatically between the two.

Spoken like a true mathematician -- we all know NZ\mathbb{N}\subseteq\mathbb{Z}. The norm_cast tactic was written at least partially as a response to all the pain mathematicians were going through with this sort of nonsense.

Kris Brown (May 24 2020 at 16:59):

Hi, first time posting! I'm doing the quantifier exercises (chapter 4 of proving in lean) and hit a road block on something I know how to do informally:

example : ( x, p x)  ¬ ( x, ¬ p x) := iff.intro
        (assume h :  x, p x,
            match h with w, pw :=
                assume h2:  x, ¬ p x, absurd pw (h2 w)
            end)
        (assume h : ¬ ( x, ¬ p x),
            -- for some arbitrary b : α
            -- if it were the case that ¬ p b, then that means ∀ x, ¬ p x (which is absurd by h)
            -- so p b (via excluded middle), and therefore ∃ x, p x
            _ )

But I cannot define an arbitrary b, at this point in the code, since this requires "assume b : α, ..." and the current type I am constructing is '∃ (x : α), p x' which takes no args. Is there another way to think about this?

Kris Brown (May 24 2020 at 17:29):

I see examples like this

example (a b : Prop) (h : a  b) : a :=
by library_search -- should say: `exact h.left`

But in my fresh install of Lean, viewing in vscode I get the error that "library_search" is an unknown identifier. (#library_search doesn't work as a command either, which I also saw someone do in Zulip). Is there any special setup needed for library search?

Bhavik Mehta (May 24 2020 at 17:31):

Do import tactic at the top of your file

Kenny Lau (May 24 2020 at 17:31):

import tactic

Kris Brown (May 24 2020 at 17:37):

thanks, does this need to be installed like mathlib? (I get error "file 'tactic' not found in the LEAN_PATH")

Johan Commelin (May 24 2020 at 17:38):

You need to do this in a project that has mathlib as dependency (or in mathlib itself)

Johan Commelin (May 24 2020 at 17:39):

And you need to "open project" in vscode, instead of "open file"

Kris Brown (May 24 2020 at 17:53):

Thanks, that worked!

Kris Brown (May 24 2020 at 18:01):

I tried searching for a standard library function like this def const(α : Type*) (β : Type*) (a:α): β -> α := λ _, a but did not find any (I'm used to it from, Haskell prelude for example) - is there no such thing? My library_search also failed to find exact h.left in the example above, so I'm not sure whether to trust it

Johan Commelin (May 24 2020 at 18:15):

I'm not sure we have that... I think we just always write the lambda, when we need it.

Kevin Buzzard (May 24 2020 at 18:20):

library_search is usually used to search for theorem proofs rather than definitions

Mario Carneiro (May 24 2020 at 21:08):

It's function.const

Jake (May 25 2020 at 00:41):

is there a way to shorten the following using normal functional programming pattern matching?

example (p q : Prop) : p ∧ q → q ∧ p
| (and.intro h₁ h₂) := and.intro h₂ h₁

to something like

example (p q : Prop) : p ∧ q → q ∧ p
| (h₁, h₂) = (h₂, h₁)

Kevin Buzzard (May 25 2020 at 00:43):

example (p q : Prop) : p  q  q  p
:= λ h₁, h₂, h₂, h₁

Jake (May 25 2020 at 00:45):

thats great, but out of curiosity is there a way of doing that with pattern matching though without lambdas?

Kevin Buzzard (May 25 2020 at 00:49):

example (p q : Prop) : p  q  q  p
| h₁, h₂ := h₂, h₁

Jake (May 25 2020 at 00:51):

awesome thank you

Jake (May 25 2020 at 00:56):

are there any code generation tools like Idris where the cases are auto generated (if you hold down ctrl-alt-c)?

Kevin Buzzard (May 25 2020 at 00:57):

shrug

Shing Tak Lam (May 25 2020 at 01:10):

Jake said:

are there any code generation tools like Idris where the cases are auto generated (if you hold down ctrl-alt-c)?

Put {!!} after your statement, click on the lightbulb, select "Generate a list of equations for a recursive definition"

example (p q : Prop) : p  q  q  p := {!!}

Then you get this:

example (p q : Prop) : p  q  q  p := -- do not forget to erase `:=`!!
| (and.intro left right) := _

Delete the := on the first line like suggested

example (p q : Prop) : p  q  q  p
| (and.intro left right) := _

Although it uses and.intro left right instead of ⟨left, right⟩

Bryan Gin-ge Chen (May 25 2020 at 01:12):

I was in the middle of typing that up too :slight_smile: . The only thing I'd add is that you can avoid having to delete the extra := by not typing it in the first place (and you can do that by writing {! p ∧ q → q ∧ p !} instead of p ∧ q → q ∧ p := {! !}). Here are the docs on that hole command. Note that you have to import tactic to use it.

Kevin Buzzard (May 25 2020 at 01:15):

Can we make ctrl-alt-c do this?

Bryan Gin-ge Chen (May 25 2020 at 01:16):

See this post for how you can bind whatever you like to adding {! !} around selected text.

Shing Tak Lam (May 25 2020 at 01:22):

and after you add that command, you can use Ctrl-. to bring up the drop down menu.

So it's Ctrl-', Ctrl-., down*6, Enter.

Jake (May 25 2020 at 01:57):

is there a way to make binds for one of the specific dropdown menu items like "generate a list of equations for a recursive definition"?

Bryan Gin-ge Chen (May 25 2020 at 02:04):

Not at the moment, but I think it's feasible. Please open an issue: https://github.com/leanprover/vscode-lean/issues

Jake (May 25 2020 at 02:08):

you got it. its just very nice in Idris, where you just keep using commands to expand cases and fill holes, so we basically almost have that here

Patrick Stevens (May 25 2020 at 05:50):

Now if we can just make Ctrl-C Ctrl-C do the same thing as agda-mode… ;)

Bryan Gin-ge Chen (May 25 2020 at 05:59):

Yeah, I think we'd have to write a nice case-split hole command first.

Jake (May 25 2020 at 15:38):

how do you pattern match on a term like p ↔ q?

Shing Tak Lam (May 25 2020 at 15:43):

example (p q : Prop) : (p  q)  (q  p)
| hp, hq := hq, hp

Basically the same way you'd pattern match on and

Kevin Buzzard (May 25 2020 at 15:45):

like and is an inductive type with one constructor which takes two inputs

Barinder Singh Banwait (May 26 2020 at 18:25):

i'm trying to define the set {rn1is1+i  0in1}\left\{ r^{n-1-i}s^{1+i} \ | \ 0 \leq i \leq n-1\right\}, for input nNn \in \mathbb{N}, r,sRr,s \in R, for RR a ring. I tried the following:

variables (R : Type u)
variables [comm_ring R]
variables i : 

def my_set (r : R) (s : R) (n : ) := { r^(n-1 - i) * s^(1+i) | 0  i  n-1 }

this isn't the correct syntax, i'm getting invalid '{' expression, ',', '}', '..', // or | expected and command expected. What's the correct way to define sets like this? Thanks!

Alex J. Best (May 26 2020 at 18:40):

You need to have just a variable name left of the | for leans set builder notation, so def my_set (r : R) (s : R) (n : ℕ) := { x | ∃ (i:ℕ ) (h : 0≤ i) (h2 :0≤ n-1), x = r^(n-1-i) *s^(i+1) }

Alex J. Best (May 26 2020 at 18:42):

You could alternatively define it as a range of a function taking the set [0, ..., n-1] to R, which is more like your original. set.image (λ i : fin n, r^(n-1-↑i) * s^(↑i + 1))

Barinder Singh Banwait (May 26 2020 at 18:46):

ooo yes, i like that one! what is that upwards-pointing arrow next to the i? is that some sort of typeclass promotion?

Alex J. Best (May 26 2020 at 18:49):

Yeah its a coercion, as I let i : fin n which is the type whose elements are naturals which are less than n, lean sometimes needs a reminder that I want to just treat it as a natural number and not a [natural number, proof that the number is less than n]

Barinder Singh Banwait (May 26 2020 at 18:50):

Alex J. Best said:

Yeah its a coercion, as I let i : fin n which is the type whose elements are naturals which are less than n, lean sometimes needs a reminder that I want to just treat it as a natural number and not a [natural number, proof that the number is less than n]

ok, thanks for the explanation!

Alex J. Best (May 26 2020 at 18:51):

In fact the one on the left (n-1-↑i) isn't needed, as lean realises that if I want to subtract i from n I must really want to interpret i as a nat, but the right one is as there are no such hints for lean.

Patrick Massot (May 26 2020 at 19:03):

Explicitly using this up arrow is almost always a bad idea though. Adding a type ascription is much better.

Pedro Minicz (May 26 2020 at 20:16):

Is there a simple lemma to solve a * c ≤ b given a ≤ b / c as a hypothesis?

Johan Commelin (May 26 2020 at 20:18):

I guess you need c ≠ 0 for that

Johan Commelin (May 26 2020 at 20:18):

Must be something about linear_ordered_field

Kevin Buzzard (May 26 2020 at 20:20):

This will be a lemma in the library.

Kevin Buzzard (May 26 2020 at 20:20):

You need c positive

Kevin Buzzard (May 26 2020 at 20:20):

Otherwise it's not true

Pedro Minicz (May 26 2020 at 20:20):

Yes, I have c > 0.

Bryan Gin-ge Chen (May 26 2020 at 20:20):

import tactic

example (α : Type*) [linear_ordered_field α] (a b c : α) (h : a  b / c) (H : c > 0) : a*c  b := by library_search
-- Try this: refine (le_div_iff H).mp h

Kevin Buzzard (May 26 2020 at 20:21):

Then you can probably find it with library_search

Pedro Minicz (May 26 2020 at 20:21):

Thank you @Bryan Gin-ge Chen!

Kevin Buzzard (May 26 2020 at 20:21):

For results like this you'll find it's quicker to find the lemma with library_search than asking

Kevin Buzzard (May 26 2020 at 20:22):

Basic results like this are guaranteed to be in the library

Pedro Minicz (May 26 2020 at 20:22):

library_search was failing my particular situation, I should have created a lemma to specify exactly what I wanted.

Kevin Buzzard (May 26 2020 at 20:22):

Right

Bryan Gin-ge Chen (May 26 2020 at 20:24):

For whatever reason library_search returns a proof using an iff plus .mp, the more direct proof term is mul_le_of_le_div H h.

Bryan Gin-ge Chen (May 26 2020 at 20:27):

This is in principle guessable if you're comfortable with the #naming conventions: the result desired is a mul_le, so we start typing mul_le_of, then mul_le_of_le_div should be in the first few auto-complete suggestions.

Kevin Buzzard (May 26 2020 at 20:41):

It's worth looking at and around the theorem that library_search finds I guess

Susan Rutter (May 28 2020 at 18:23):

Hi, I'm very new at this, and I don't really understand how have works. I'm trying to use it to introduce a supplementary proposition (call this p), and then show the goal is true when p is true, and also when p is false. I thought this would be cases p, but the error message given is that cases is not applicable to given hypothesis (p). Where have I gone wrong?

Bryan Gin-ge Chen (May 28 2020 at 18:25):

I think you want by_cases p? https://leanprover-community.github.io/mathlib_docs/tactics.html#by_cases

Bryan Gin-ge Chen (May 28 2020 at 18:26):

It's confusing, but cases is quite different.

Susan Rutter (May 28 2020 at 18:27):

Ah yes, thanks

Patrick Massot (May 28 2020 at 18:57):

There is a link though, you can write:

example (P : Prop) : true :=
begin
  have em : P  ¬ P,
    from classical.em P,
  cases em with h h,
  { trivial },
  { trivial },
end

where em stands for "excluded middle". But writing by_cases h : P is simply more convenient.

Susan Rutter (May 28 2020 at 19:03):

Oh great, that actually really helps me understand how LEAN works

Patrick Massot (May 28 2020 at 19:11):

Good. People normally write Lean, with lower case after the initial, unless you use the logo which has quantifiers in the middle

Jalex Stark (May 29 2020 at 02:46):

Bryan Gin-ge Chen said:

I think you want by_cases p? https://leanprover-community.github.io/mathlib_docs/tactics.html#by_cases

Did you know you can type tactic#by_cases to generate the same link?

Robin Carlier (May 30 2020 at 19:53):

What would be a nice way to prove that a function defined on a subtype, applied to elements with same "value", yield the same result? As in this :

section
variables (α : Type u) (p : α  Prop)
variables (β : Type b)
variables (x : α) (y : α) (px : p x) (py : p y)
variable (f : {u : α // p u}  β)
example (h : x = y) : f x, px = f y, py := begin
  sorry
end

end

If I try to just rw h I get an error, I guess it's because they're part of px and py. I guess it has to do with stuff like subtype.ext, but I can't get anything to work.
Also I would rather avoid proving directly that ⟨x, px⟩ = ⟨y, py⟩, the example above is a reduction from a situation where I basically can't know px and py (they're proofs generated by lean that displays as _ in my goal.

Kevin Buzzard (May 30 2020 at 19:55):

example (h : x = y) : f x, px = f y, py := begin
  simp [h],
end

Kevin Buzzard (May 30 2020 at 19:56):

As you spotted, you can't rewrite directly, because f ⟨y, px⟩ isn't a valid term. But the simplifier knows some basic tricks, and if you regard it as rewrite on steroids then this is what you're naturally led to. You can see what actually happened by using squeeze_simp instead. (oh, it just says simp only [h] :-) )

Robin Carlier (May 30 2020 at 19:57):

Mmmh, this works in the MWE, but not directly in the "big situation" sadly :(

Johan Commelin (May 30 2020 at 19:57):

congr, apply subtype.ext, exact h?

Robin Carlier (May 30 2020 at 19:57):

I'll try to turn this as a little lemma in my case and see if It can be applied this way tho.

Johan Commelin (May 30 2020 at 19:57):

Would that work better?

Robin Carlier (May 30 2020 at 19:58):

yay congr worked!

Robin Carlier (May 30 2020 at 19:58):

So what does congr do basically?

Kevin Buzzard (May 30 2020 at 19:58):

tactic#congr

Johan Commelin (May 30 2020 at 19:59):

tl;dr: It strips away stuff that is similar on both sides of an equality

Robin Carlier (May 30 2020 at 19:59):

Perfect! thank you a lot

Scott Morrison (May 30 2020 at 20:52):

See also tactic#congr', when you need more control over how much stuff it strips away.

Apurva Nakade (May 30 2020 at 21:09):

How would one prove the following?

example (m: ) : (1  m)  (m = 0  m = 1)
:=
begin
  sorry,
end

Induction is taking down a weird path.

Kenny Lau (May 30 2020 at 21:10):

don't use \ge

Kenny Lau (May 30 2020 at 21:11):

import tactic.interval_cases

example (m : ) : m  1  m = 0  m = 1 :=
begin
  intro h, interval_cases m,
  { left, refl },
  { right, refl }
end

Kenny Lau (May 30 2020 at 21:13):

example (m : ) (H : m  1) : m = 0  m = 1 :=
begin
  cases H with H H, { right, refl },
  cases H, { left, refl },
end

Kenny Lau (May 30 2020 at 21:13):

induct on the inequality instead of the natural number

Apurva Nakade (May 30 2020 at 21:14):

Oh! Thanks thanks!

I did not know this was even possible. Will read about interval_cases

Kevin Buzzard (May 30 2020 at 21:35):

Scott Morrison said:

See also tactic#congr', when you need more control over how much stuff it strips away.

The linkifier failed for that

Kevin Buzzard (May 30 2020 at 21:36):

Apurva Nakade said:

Oh! Thanks thanks!

I did not know this was even possible. Will read about interval_cases

You can case on any inductive type, and a lot of things are inductive types.

Johan Commelin (May 31 2020 at 04:03):

Kevin Buzzard said:

Scott Morrison said:

See also tactic#congr', when you need more control over how much stuff it strips away.

The linkifier failed for that

@Mario Carneiro :up:

Mario Carneiro (May 31 2020 at 04:28):

Johan Commelin said:

Kevin Buzzard said:

Scott Morrison said:

See also tactic#congr', when you need more control over how much stuff it strips away.

The linkifier failed for that

Mario Carneiro :up:

Mario Carneiro (May 31 2020 at 04:30):

The link will fail for things like tactic#elide, because the header does not match the name of the tactic

Mario Carneiro (May 31 2020 at 04:31):

Is it possible to insert extra anchors in mathlib docs?

Robin Carlier (May 31 2020 at 09:15):

Kind of a vague question, but is there a tactic/syntax trick to add both sides of a given equality to the context?
Like, for instance, my goal is a = b, I'd like to have something that does let u := aand let v := b but automatically (since a and b are quite complicated in my case), is there a way?

Reid Barton (May 31 2020 at 09:22):

example : 1 = 2 := begin
  let u := _,
  let v := _,
  change u = v,
  /-
u : ℕ := 1,
v : ℕ := 2
⊢ u = v
  -/
end

Robin Carlier (May 31 2020 at 09:25):

Ok, so basically here u and v are "anonymous" at first, and then when you do the "change" thing it automatically gets what u and v should be? Very nice!
Well thank you!

Reid Barton (May 31 2020 at 09:34):

More precisely, the uninferrable _s in the definitions of u and v become metavariables, then these metavariables get assigned as a result of unifying u = v with the goal.

Bhavik Mehta (May 31 2020 at 17:55):

there's also tactic#generalize

Jake (Jun 01 2020 at 21:41):

hey, when I try doing import tactic it's saying file 'tactic' not found in the LEAN_PATH. Any ideas how to fix this? Did i mess something up setting up the project?

Bryan Gin-ge Chen (Jun 01 2020 at 21:42):

Newer versions of Lean give a link to this URL in the error message, maybe one of the suggestions there will help: https://leanprover-community.github.io/file-not-found.html

Jake (Jun 01 2020 at 21:56):

kk fixed it thank you!

Jake (Jun 02 2020 at 16:00):

Forgive me for this dumb question, but can you compile a "Hello World" program in lean?

Reid Barton (Jun 02 2020 at 16:00):

Compile, in the sense of producing a standalone binary: no.

Jake (Jun 02 2020 at 16:01):

So it's 15 mb no matter what?

Reid Barton (Jun 02 2020 at 16:01):

In Lean 4 you can.

Reid Barton (Jun 02 2020 at 16:01):

I guess so.

Jake (Jun 02 2020 at 16:01):

When's lean 4 expected to release?

Reid Barton (Jun 02 2020 at 16:02):

I don't think anyone knows, but for hello world purposes it should be usable already

Jake (Jun 02 2020 at 16:11):

Kk thank you!

Brandon Brown (Jun 02 2020 at 23:40):

I'm trying to prove two setoids are equal, is using apply setoid.ext the right starting point?

Mario Carneiro (Jun 02 2020 at 23:56):

yes?

Mario Carneiro (Jun 02 2020 at 23:56):

does it make progress toward the goal you want to prove? If so then yes

Brandon Brown (Jun 03 2020 at 00:49):

I guess more generally how does one prove two structures are equal in lean? I assume you have to prove each field is equal but I'm not sure how to do this for some custom made structure type

Mario Carneiro (Jun 03 2020 at 00:50):

This is usually a theorem called T.ext. It is proven by cases on each argument, followed by congr

Charlie Conneen (Jun 03 2020 at 18:18):

I've never seen fin n before but I understand it's used sort of like a cardinality. How would I explicitly define a vector doing this?

def fn (n : nat) : Type := fin n  

example : fn 3 := sorry

Charlie Conneen (Jun 03 2020 at 18:19):

I'd like to map 0, 1, and 2 to just any real number I'd like

Johan Commelin (Jun 03 2020 at 18:21):

https://leanprover-community.github.io/mathlib_docs/data/matrix/notation.html
Does this help?

Charlie Conneen (Jun 03 2020 at 18:28):

It does, thank you

Charlie Conneen (Jun 03 2020 at 18:28):

although... does this have to be done inductively?

Charlie Conneen (Jun 03 2020 at 18:29):

or, rather, recursively

Johan Commelin (Jun 03 2020 at 18:30):

@Charlie Conneen What do you mean?

Charlie Conneen (Jun 03 2020 at 18:32):

Oh, I just didn't see the bit about the notation of how to construct a vector all in one go

Charlie Conneen (Jun 03 2020 at 18:32):

I thought I would have to make 3 vectors to make one of length 3, and was concerned for a second

Charlie Conneen (Jun 03 2020 at 18:33):

but ![...] solves that readily

Bassem Safieldeen (Jun 03 2020 at 19:14):

Hi,

My name is Bassem and this is my first post here :)

I hope to get skilled enough with Lean to formalize quantum Shannon theory, among other things I work on.

But first I would be grateful if someone could give me some pointers on how to start proving this

example {L : list } : 0  L  L.prod > 0 :=
begin
sorry,
end

As a lazy solution, I have tried finish, hint, suggest but none worked for me.

If I were proving this to a person in the traditional way I might say: "by our assumptions, all the numbers in the list are positive integers. And the product of positive integers must be positive."
I don't know how to tell Lean that.

Thanks!

Kevin Buzzard (Jun 03 2020 at 19:15):

Maybe induction on the list?

Jalex Stark (Jun 03 2020 at 19:19):

I think the proof you gave can be translated into some gentle applications of things in the big_operators file

Jalex Stark (Jun 03 2020 at 19:25):

https://leanprover-community.github.io/mathlib_docs/algebra/big_operators.html#finset.prod_le_prod

Kevin Buzzard (Jun 03 2020 at 19:28):

import data.list.basic tactic

open list

example {L : list } : 0  L  0 < L.prod :=
begin
  -- assume 0 ∉ L
  intro h0,
  -- let's do induction on the list
  induction L with n tl ih,
  { -- in the case where the list is empty it's true because 0<1
    norm_num},
  { -- now say the list is n :: tl
    -- suffices to prove 0 < n * prod(tl),
    rw prod_cons,
    -- the product of two positive things is positive
    apply mul_pos,
    { -- 0 < n is easy to check
      finish,
    },
    { -- finally need to know the product over the tail is positive.
      -- Use the inductive hypothesis
      apply ih,
      -- and now it all follows by logic
      finish,
    }
  },
end

Reid Barton (Jun 03 2020 at 19:28):

or prod_pos, but the hypotheses do not apply to nat

Jalex Stark (Jun 03 2020 at 19:28):

Here is the lemma that a product of positive things is positive:
https://leanprover-community.github.io/mathlib_docs/algebra/big_operators.html#finset.prod_pos
ah, yes reid points out that it's a theorem about rings, not semirings. This could prove it for the integers, and then one could lift to the nats to finish

Kevin Buzzard (Jun 03 2020 at 19:30):

Oh man we still have those horrible underlines in the docs changing all < to <= ?

Bassem Safieldeen (Jun 03 2020 at 19:33):

I will try out these suggestions. Thanks a lot!

Reid Barton (Jun 03 2020 at 19:35):

Is there something we could replace linear_ordered_comm_ring with that includes nat?

Reid Barton (Jun 03 2020 at 19:37):

+ doesn't even appear in the statement, let alone -.

Kevin Buzzard (Jun 03 2020 at 19:38):

a totally ordered monoid_with_zero

Reid Barton (Jun 03 2020 at 19:38):

It doesn't even really have to be totally ordered

Johan Commelin (Jun 03 2020 at 19:38):

But it must be commutative!

Reid Barton (Jun 03 2020 at 19:38):

Yes

Johan Commelin (Jun 03 2020 at 19:40):

decidable_linear_ordered_(add_)comm_monoid is a thing

Jalex Stark (Jun 03 2020 at 19:41):

there are comments in the source saying

/- this is also true for a ordered commutative multiplicative monoid -/

Jalex Stark (Jun 03 2020 at 19:41):

there are three lemmas that want to be updated in this way

Reid Barton (Jun 03 2020 at 19:41):

right, why doesn't Lean just use that comment! haha

Reid Barton (Jun 03 2020 at 19:41):

we need a remark top-level command

Kevin Buzzard (Jun 03 2020 at 19:42):

The example which the OP posted needs there to be a 0 which is the smallest element. I'm not sure that a general ordered comm mult monoid has a 0 (it has a 1, but that's another matter)

Kevin Buzzard (Jun 03 2020 at 19:42):

Isn't the reals an ordered comm monoid?

Sebastien Gouezel (Jun 03 2020 at 19:43):

What about canonically_ordered_comm_semiring?

Jalex Stark (Jun 03 2020 at 19:45):

Kevin Buzzard said:

The example which the OP posted needs there to be a 0 which is the smallest element. I'm not sure that a general ordered comm mult monoid has a 0 (it has a 1, but that's another matter)

I think prod_pos is the right generalization
if you want, you can pass to the nonnegative cone of the monoid and then the version originally posted is true

Reid Barton (Jun 03 2020 at 19:45):

Yeah, you'd need to patch up the statement slightly to relate "nonzero" with "greater than zero". Another approach is prod_ne_zero_iff.

Jalex Stark (Jun 03 2020 at 19:49):

Sebastien Gouezel said:

What about canonically_ordered_comm_semiring?

Do these have 0<1?

Jalex Stark (Jun 03 2020 at 19:49):

class canonically_ordered_comm_semiring (α : Type*) extends
  canonically_ordered_add_monoid α, comm_semiring α, zero_ne_one_class α :=
(mul_eq_zero_iff (a b : α) : a * b = 0  a = 0  b = 0)

Jalex Stark (Jun 03 2020 at 19:50):

it looks like no?

Johan Commelin (Jun 03 2020 at 19:50):

That's from an old mathlib, I guess

Jalex Stark (Jun 03 2020 at 19:50):

oh okay

Johan Commelin (Jun 03 2020 at 19:50):

Kenny refactored zero_ne_one_class

Johan Commelin (Jun 03 2020 at 19:50):

But it shouldn't matter for this discussion

Patrick Massot (Jun 03 2020 at 19:57):

Kevin Buzzard said:

Oh man we still have those horrible underlines in the docs changing all < to <= ?

Could someone fix this in the next two Hours? :wink:

Bryan Gin-ge Chen (Jun 03 2020 at 20:04):

@Ryan Lahfa has a PR in progress at doc-gen#20, but it's not ready in the current form.

Reid Barton (Jun 03 2020 at 20:15):

I commented on the PR there, but really I think almost any change that gets rid of the underlines is an improvement

Bryan Gin-ge Chen (Jun 03 2020 at 20:19):

I might have to check again, but I thought that what's currently there only removes some of the underlines.

Reid Barton (Jun 03 2020 at 20:24):

The underlines all seem to be gone when I make the change in my browser, at least in the file I'm looking at.
Maybe the issue is what to do about this decl_args color? I actually never noticed the arguments weren't black until I changed the link color to blue and then noticed that links in arguments were missing...

Reid Barton (Jun 03 2020 at 20:33):

Oh I understand your comment now Bryan. I was looking at the original diff, which is the same as what I did in my browser, but the revised diff does look like it wouldn't apply to all links.

Reid Barton (Jun 03 2020 at 20:34):

Er, I didn't do exactly the same thing, but something approximately the same.

Reid Barton (Jun 03 2020 at 21:11):

@Gabriel Ebner I'm not sure if you realized that what was in my screenshot was much different than the current state of the PR.

Gabriel Ebner (Jun 03 2020 at 21:14):

Yeah, the change only removes the underlines (showing them on hover). This is also in the PR description. As I've said already, please submit follow-up PRs if you don't like it.

Bryan Gin-ge Chen (Jun 03 2020 at 21:15):

I'm trying to put together something along the lines of Reid's suggestion in the comments right now.

Reid Barton (Jun 03 2020 at 21:15):

It also doesn't do that consistently though.

Reid Barton (Jun 03 2020 at 21:17):

I'll make another PR.

Reid Barton (Jun 03 2020 at 21:21):

doc-gen#26 (will this work)

Reid Barton (Jun 03 2020 at 21:21):

Amazing

Gabriel Ebner (Jun 03 2020 at 21:27):

I'm not sure I like the blue, but it's almost midnight so I'm gonna merge it. Complaints are accepted after tomorrow morning.

Reid Barton (Jun 03 2020 at 21:27):

I don't really like the blue either, but it's at least functional.

Gabriel Ebner (Jun 03 2020 at 21:31):

I've restarted the ci on master. The docs should be live soon. Good luck!

Ashvni Narayanan (Jun 05 2020 at 20:04):

I am trying to define Discrete Valuation Rings, and in particular uniformizers:

import ring_theory.ideals

import ring_theory.principal_ideal_domain

import ring_theory.localization

universe u

class discrete_valuation_ring (R : Type u) extends principal_ideal_domain R :=
(max_ideal : ideal R)
(maximal : max_ideal.is_maximal)
(unique_prime_ideal : ∀ M : ideal R, M.is_prime → M = ⊥ ∨ M = max_ideal)

variable {R : Type u}

structure discrete_valuation_ring.discrete_valuation (R : Type u) [discrete_valuation_ring R] extends localization.fraction_ring R →* ℤ :=

(map_top' : to_fun 0 = 1)

(map_add_leq' : ∀ x y, to_fun (x + y) ≤ max (to_fun x) (to_fun y))

def uniformizers : set R := { π | ideal.span {π} = max_ideal R }

I end up getting the following errors:

  1. don't know how to synthesize placeholder
    context:
    R : Type u,
    π : R
    ⊢ Type ?

  2. unknown identifier 'max_ideal'

Any help is appreciated!

Jalex Stark (Jun 05 2020 at 20:04):

#backticks will make the code easier to read

Ender Doe (Jun 05 2020 at 22:49):

Hi I have a question regarding Theorem proving in Lean ,chapter 8, at the very end of 8.6. "The map function is even more tedious to define by hand than the tail function. We encourage you to try it, using rec_on, cases_on and no_confusion." I have gotten this far

inductive vector (α : Type u) : nat  Type u
| nil {} : vector 0
| cons   : Π {n}, α  vector n  vector (n+1)

def map {α β γ : Type} (f : α  β  γ) :
  Π {n}, vector α n  vector β n  vector γ n :=
(assume n a b, show vector γ n, from
    nat.rec_on n
    (show vector γ 0, from vector.nil)
    (assume n ih, show vector γ (succ n), from ih.cons (f
        (show α, from vector.rec_on a
            (show α, from sorry)
            (assume n1 itm vec ih, show α, from itm)
        )
        (show β, from sorry))
    )
)

I receive the following error "eliminator" elaberator failed to compute the motive for vector.rec_on a. How should I interpret this error message? I am not so concerned with solving the problem as I am understanding what this error means. To the best of my knowledge the eliminator refers to the process of deriving a proof from a rec_on or similair induction expression. Thanks for your help, I am still new at this.

Yakov Pechersky (Jun 05 2020 at 23:15):

If I understand correctly, your first sorry should be a contradiction because that path indicates that there are no more elements of a to get.

James Arthur (Jun 07 2020 at 17:02):

I'm trying to do epsilon delta definition of a limit in lean (if this is actually possible?!). Here is my code thus far, but I kind of feel I am missing something

import data.real.basic
import algebra.pi_instances

notation `|`x`|` := abs x

lemma limit (L c : ) (x : ) (f :   ) : ε>0, δ>0, |x-c| < δ  |f x - L|< ε :=
begin
  intros ε ε_pos,
  use ε,
  split, linarith,
  intros ,
  rw abs_lt,
 -- I got stuck here
end

Here is my tactic state thus far aswell:

1 goal
L c x : ,
f :   ,
ε : ,
ε_pos : ε > 0,
 : |x - c| < ε
 -ε < f x - L  f x - L < ε

Reid Barton (Jun 07 2020 at 17:05):

What are you trying to do?

Reid Barton (Jun 07 2020 at 17:06):

You've stated that every function converges to every value at every point--of course you're not going to be able to prove this.

Kevin Buzzard (Jun 07 2020 at 17:09):

The epsilon delta definition of a limit is definitely possible in Lean and you can even prove that the definition coincides with the fancy filter one

James Arthur (Jun 07 2020 at 17:10):

I'm trying to tell lean about limits. The end goal is to prove existence and uniqueness of ODE solutions, so I thought putting limits in was a good starting point.

Kevin Buzzard (Jun 07 2020 at 17:11):

The intro tactic takes a "forall x" in the goal to an x in the hypotheses and the revert tactic puts it back

Reid Barton (Jun 07 2020 at 17:12):

I think you want to start with something more like

def limit (L c : ) (x : ) (f :   ) := ε>0, δ>0, |x-c| < δ  |f x - L|< ε

Kevin Buzzard (Jun 07 2020 at 17:12):

So, putting all the variables to the right of the colon, your goal says "for all L and for all c and for all x and for all functions, ..."

Kevin Buzzard (Jun 07 2020 at 17:12):

I don't think you should be proving a lemma. I think you should be making a definition

Kevin Buzzard (Jun 07 2020 at 17:13):

Like what Reid did when I was struggling to type that into my phone

James Arthur (Jun 07 2020 at 17:13):

OK, I got myself confused then. I thought it was something I'd have to prove, not just type in.

Kevin Buzzard (Jun 07 2020 at 17:14):

What's your academic background?

Kevin Buzzard (Jun 07 2020 at 17:14):

Mathematicians use the word "proof" in quite a vague way

James Arthur (Jun 07 2020 at 17:15):

I'm a Mathematics student at Exeter.

Kevin Buzzard (Jun 07 2020 at 17:15):

Sometimes this is half the problem. They think they want to prove something but actually they want to define a function

Kevin Buzzard (Jun 07 2020 at 17:15):

In Lean there are these two universes, Prop and Type

Kevin Buzzard (Jun 07 2020 at 17:15):

And after a while you start sorting mathematical objects into these two universes

Kevin Buzzard (Jun 07 2020 at 17:16):

And then you understand types and terms

Kevin Buzzard (Jun 07 2020 at 17:17):

And then you realise that every mathematical object fits into this square -- types or terms, theorem statements or proofs

Kevin Buzzard (Jun 07 2020 at 17:18):

And there are rules for when you use def and when you use lemma but in maths departments they don't always do this correctly

Kevin Buzzard (Jun 07 2020 at 17:18):

Eg the correspondence theorem in group theory isn't a theorem

James Arthur (Jun 07 2020 at 17:22):

I've got to be really careful with what the difference between def and lemma / theorem are. Thanks!

Patrick Massot (Jun 07 2020 at 17:22):

James, proving easy stuff is actually simpler to learn than writing definitions. You should probably start with tutorials where the definitions are already there.

Patrick Massot (Jun 07 2020 at 17:22):

#mil or, especially since you want to work with limits, the tutorials project

James Arthur (Jun 07 2020 at 17:39):

Thankyou, I had recently completed the tutorial project and I had totally forgot that limits were in there, I shall go back and redo some bits of it. Thankyou Patrick.

Kevin Buzzard (Jun 07 2020 at 18:25):

I think it would be a good exercise to define the predicate is_limit f a L for f a function from the reals to the reals and a and L real numbers, saying that the limit of f(x) is L as x tends to a. This is a definition. Give the epsilon delta definition. Then figure out how to prove the theorem that says that for a given f, a and L, some fancy thing involving filters holds if and only if your predicate holds

Kevin Buzzard (Jun 07 2020 at 18:26):

@James Arthur

Pedro Minicz (Jun 07 2020 at 20:23):

How do you reopen the Lean goals window on VSCode?

Bryan Gin-ge Chen (Jun 07 2020 at 20:28):

You can hit ctrl+shift+enter, or click one of the icons at the top right of the editor panel.

Pedro Minicz (Jun 07 2020 at 20:33):

Thank you!

Brandon Brown (Jun 07 2020 at 23:44):

Is there a way to reduce a type to basic elements? For example, if I have a goal type of transitive r for some relation r how can I get lean to show that this really means r a b -> r b c -> r a c ? In this specific case I can use intro to expose this but in other cases perhaps not.

Anas Himmi (Jun 07 2020 at 23:49):

unfold transitive?

Brandon Brown (Jun 07 2020 at 23:50):

oh nice, thanks!

Scott Morrison (Jun 08 2020 at 05:00):

But be aware that unfold is typically not actually a useful step in a proof. It can be helpful when you're getting started to understand what a goal is saying, but thinking you want to use it in a proof is often either a sign you're doing it wrong, or things are badly set up (e.g. that there are missing @[simp] lemmas for the definitions you're working with).

Dave (Jun 10 2020 at 06:22):

Mario Carneiro said:

Here's an example: Let A := bool and let B be the collection of all turing machines quotient by "one halts iff the other does". There is a computable map A -> B which sends tt to a halting TM and ff to a non-halting machine, and this is a bijection with no computable inverse

How are you not quotienting out by all TM's? They are determined by where they halt. this would be quotienting all of them by all of them. It is not clelar you are doing anything.

Dave (Jun 10 2020 at 06:25):

Also, on the argument presented here:

Dave (Jun 10 2020 at 06:25):

https://xenaproject.wordpress.com/2019/06/11/the-inverse-of-a-bijection/

Dave (Jun 10 2020 at 06:26):

if you were to encode these sequences as natural numbers clearly there would be a bijection. If, as usual, you reject the possibility of infinite inputs, no such problem occurs. I'm fairly certain computable bijections have computable inverses. Kind of the point.

Mario Carneiro (Jun 10 2020 at 06:27):

What do you mean by "they are determined by where they halt"? When I say "the collection of all turing machines" this is a discrete countable set of turing machine descriptions, which is then quotiented by some relation. In particular, two turing machines are considered distinct if they have different representations even if they are extensionally equivalent, so the base set before the quotient is perfectly well computable - it's isomorphic to nat

Dave (Jun 10 2020 at 06:27):

I only saw the one other example, but the wording made it seem as though nothing was being done since you quotient by everything. I would like to see some justification for this idea that computable bijections don't have computable inverses. Seems confusing to me

Dave (Jun 10 2020 at 06:29):

Mario Carneiro said:

What do you mean by "they are determined by where they halt"? When I say "the collection of all turing machines" this is a discrete countable set of turing machine descriptions, which is then quotiented by some relation. In particular, two turing machines are considered distinct if they have different representations even if they are extensionally equivalent, so the base set before the quotient is perfectly well computable - it's isomorphic to nat

never said it wasn't computable. I think of tm's as just partial computable functions. you can enumerate with a universal TM. Clearly gunna be iso to nat regardless, that adds no info. I do disagree on the distinction in definition despite being extensionally equivalent though. Odd choice. Probably pragmatically motivated

Mario Carneiro (Jun 10 2020 at 06:30):

I'm constructing a counterexample here. Go with my definitions for a bit

Dave (Jun 10 2020 at 06:30):

Ok

Mario Carneiro (Jun 10 2020 at 06:30):

A TM is given by a string, and these are obviously countable

Mario Carneiro (Jun 10 2020 at 06:30):

and computable discrete etc

Dave (Jun 10 2020 at 06:31):

why do you emphasize discrete?

Mario Carneiro (Jun 10 2020 at 06:31):

That is, equality is decidable

Dave (Jun 10 2020 at 06:31):

ok, fair

Mario Carneiro (Jun 10 2020 at 06:31):

I emphasize it because this property will fail to hold after the quotient

Dave (Jun 10 2020 at 06:32):

of decidability of equality as a predicate (or relation, whatever you want to say)

Dave (Jun 10 2020 at 06:32):

?

Mario Carneiro (Jun 10 2020 at 06:33):

You can construct a computable function that will determine in finite time for any two TMs whether they are textually identical

Mario Carneiro (Jun 10 2020 at 06:34):

Now, the relation "TM A halts iff TM B halts on input 0" is an equivalence relation, and so we can form a new type which is this set of TMs quotiented by the relation. This type is classically equivalent to bool because either a TM halts or it doesn't

Mario Carneiro (Jun 10 2020 at 06:35):

We can also map bool to this quotient set by mapping true to the equivalence class of halt and false to the equivalence class of loop {}

Mario Carneiro (Jun 10 2020 at 06:35):

This function is also computable

Mario Carneiro (Jun 10 2020 at 06:36):

And we can prove it is a bijection using classical axioms

Mario Carneiro (Jun 10 2020 at 06:36):

specifically LEM

Mario Carneiro (Jun 10 2020 at 06:37):

because if A halts on input 0 then [[A]] = [[halt]], and if A doesn't halt on input 0 then [[A]] = [[loop {}]]

Dave (Jun 10 2020 at 06:38):

I imagine you'll say it can't have computable bij or you compute the halting set

Mario Carneiro (Jun 10 2020 at 06:38):

Now suppose there was a computable inverse to this function. This would be a function from the quotient to bool, which by definition means it is a function on the set of TM strings which respects the equivalence relation

Mario Carneiro (Jun 10 2020 at 06:38):

And indeed, this function is a halting oracle

Dave (Jun 10 2020 at 06:39):

aha

Kenny Lau (Jun 10 2020 at 06:39):

where, in the arithmetical hierarchy, does the halting set { s | s halts } lie?

Dave (Jun 10 2020 at 06:39):

sigma 0 1

Dave (Jun 10 2020 at 06:39):

above delta 0 1

Dave (Jun 10 2020 at 06:39):

which is the complexity of {0 , 1}

Dave (Jun 10 2020 at 06:39):

the other set is sigma 0 1

Dave (Jun 10 2020 at 06:39):

like we just agreed to

Dave (Jun 10 2020 at 06:40):

by Myhill Isomorphism this isn't a computable bij

Dave (Jun 10 2020 at 06:40):

compelling argument though

Mario Carneiro (Jun 10 2020 at 06:45):

I think of tm's as just partial computable functions. you can enumerate with a universal TM. Clearly gunna be iso to nat regardless, that adds no info.

By the way, when I say isomorphic to nat I mean constructively/computably isomorphic. It is not true that the set of partial computable functions is isomorphic to nat in this sense

Mario Carneiro (Jun 10 2020 at 06:45):

because equality of partial computable functions is not decidable

Mario Carneiro (Jun 10 2020 at 06:45):

and equality on nat is

Dave (Jun 10 2020 at 06:45):

I think we are talking about different uses of "computable" because otherwise this makes little sense

Mario Carneiro (Jun 10 2020 at 06:46):

how so?

Dave (Jun 10 2020 at 06:46):

they are enummerable, they have codes. they are certainly isomorphic to nat

Mario Carneiro (Jun 10 2020 at 06:46):

They are enumerable, but only with repeats

Dave (Jun 10 2020 at 06:46):

right, yes

Mario Carneiro (Jun 10 2020 at 06:46):

and you can't tell when a repeat has occurred

Mario Carneiro (Jun 10 2020 at 06:47):

so you can't construct a computable bijection

Mario Carneiro (Jun 10 2020 at 06:47):

er, equiv

Mario Carneiro (Jun 10 2020 at 06:47):

(= computable bijection with computable inverse)

Mario Carneiro (Jun 10 2020 at 06:50):

Actually I'm not even sure there is a computable bijection from PCF to nat, in either direction

Dave (Jun 10 2020 at 06:50):

I don't understand? why would the repeat matter? you just check the code of the repeat, it goes back to another number and that'[s it

Mario Carneiro (Jun 10 2020 at 06:51):

You don't know when you have repeated or what you are repeating so you can't remove the repeats

Dave (Jun 10 2020 at 06:51):

idk why you wanna remove them though

Mario Carneiro (Jun 10 2020 at 06:53):

You start enumerating: PCF 0, let's call that 0. PCF 1, well this might be equal to PCF 0 in which case we should skip it, or maybe not and we should allocate 1. If you make the former choice when you shouldn't you get a repeat (it's not injective), and if you make the latter choice when you shouldn't then it's not a function at all (it assigns two values to the same input)

Mario Carneiro (Jun 10 2020 at 06:54):

So you have to determine whether PCF 0 = PCF 1, which is equivalent to the non-halting of a certain turing machine

Dave (Jun 10 2020 at 06:55):

ah I see. if we give up extensional equality and just go by how they are written though we don't have to worry about this though

Mario Carneiro (Jun 10 2020 at 06:55):

right

Dave (Jun 10 2020 at 06:56):

but then you'd have a bijection?

Mario Carneiro (Jun 10 2020 at 06:57):

Sure, you can set it up so that codes for partial recursive functions are computably isomorphic to nat

Mario Carneiro (Jun 10 2020 at 06:57):

In fact I think this is proved in mathlib

Dave (Jun 10 2020 at 06:57):

oh ok perfect well then we agree on this. that's good

Dave (Jun 10 2020 at 06:58):

and probably proven by you?

Mario Carneiro (Jun 10 2020 at 06:58):

:)

Dave (Jun 10 2020 at 06:58):

hehehe

Mario Carneiro (Jun 10 2020 at 06:58):

src#nat.partrec.code.denumerable

Dave (Jun 10 2020 at 06:58):

ayy, nice

Pedro Minicz (Jun 10 2020 at 23:16):

Say I have f : α → ℝ, is it possible to define i=0f(i)\sum_{i=0}^{\infty} f(i) if α = ℕ?

Pedro Minicz (Jun 11 2020 at 00:02):

I ended up settling for finset.sum univ f where [fintype α], but that looks horribly ugly...

Pedro Minicz (Jun 11 2020 at 00:03):

Is there notation for that? I think algebra.big_operators should be it, but I haven't managed to get them working.

Pedro Minicz (Jun 11 2020 at 00:09):

Anyone have a #mwe for algebra.big_operators? The following doesn't work for me:

import algebra.big_operators
import data.fintype.basic

open_locale big_operators

variables {α : Type} [fintype α] (f : α  )

example : add_comm_monoid  := by apply_instance

set_option pp.notation false

#check Σ x, f x -- Should be \sum not \S

Pedro Minicz (Jun 11 2020 at 00:10):

Wait, nevermind, \sum and \S are different... okay :innocent:

Johan Commelin (Jun 11 2020 at 00:57):

Pedro Minicz said:

Wait, nevermind, \sum and \S are different... okay :innocent:

Yup, this is unfortunate...

Lucas Viana (Jun 11 2020 at 01:16):

What is the definition in the library for the set of integers from 0 to n?

Jalex Stark (Jun 11 2020 at 01:21):

i think you want finset.range

Lucas Viana (Jun 11 2020 at 01:27):

thanks!

Bryan Gin-ge Chen (Jun 11 2020 at 01:31):

There's also fin n which is the finite type consisting of terms 0, 1, ..., n-1.

Lucas Viana (Jun 11 2020 at 02:02):

How can I lift a (a : set (finset.range n)) to afinset?

Jalex Stark (Jun 11 2020 at 02:03):

have you poked through the finset files?

Jalex Stark (Jun 11 2020 at 02:03):

does docs#finset work

Johan Commelin (Jun 11 2020 at 02:04):

@Lucas Viana I think you want fin n instead of finset.range n.

Johan Commelin (Jun 11 2020 at 02:05):

The former is a type, the latter a finset. So fin n is an "ambient set" and finset.range n is a "subset".

Lucas Viana (Jun 11 2020 at 02:09):

Thanks. But can I turn a a:set (fin n) into a finset? The point is that I want to do card a. I am trying to define a measure on a finite set.

Johan Commelin (Jun 11 2020 at 02:10):

How did you get a?

Johan Commelin (Jun 11 2020 at 02:10):

The direct answer is (hopefully)

lift a to finset

Johan Commelin (Jun 11 2020 at 02:11):

It will ask you to prove finite a, which you can get from something that is hopefully called fintype.finite or something like that.

Lucas Viana (Jun 11 2020 at 02:19):

This is how I am trying to do it:

import measure_theory.measure_space

open set measure_theory finset

instance finite_measurable (n) : measurable_space (fin n) :=
{ is_measurable := λ a, true,
  is_measurable_empty := by trivial,
  is_measurable_compl := λ a, λ b, trivial,
  is_measurable_Union := λ a, λ b, trivial }

instance finite_measure (n) : measure_space (fin n) :=
⟨⟨⟨λ a, card a,_,_,_⟩,_,_⟩⟩ -- a is a set(fin n)

Johan Commelin (Jun 11 2020 at 02:23):

Note that mathlib has counting measures. You might want to reuse those.

Lucas Viana (Jun 11 2020 at 02:24):

Hmm, good! The measure_space file is too big and I didn't notice it :laughing:

Johan Commelin (Jun 11 2020 at 02:24):

src/data/set/finite.lean:theorem finite.of_fintype [fintype α] (s : set α) : finite s :=

Johan Commelin (Jun 11 2020 at 02:25):

I found this using git grep "fintype.*finite" on the command line

Lucas Viana (Jun 11 2020 at 02:26):

I am trying to formalize my probability theory class. Thanks.

Johan Commelin (Jun 11 2020 at 02:26):

https://leanprover-community.github.io/mathlib_docs/measure_theory/measure_space.html#measure_theory.measure.count

Johan Commelin (Jun 11 2020 at 02:27):

Probability is a sore spot... we don't have anything in mathlib :sad:

Lucas Viana (Jun 11 2020 at 02:39):

Does mathlib has something like the fact that measures form a cone in a vector space? Like, I want the measure to be a probability, so I would multiply the counting measure by 1/n

Lucas Viana (Jun 11 2020 at 02:41):

Johan Commelin said:

Probability is a sore spot... we don't have anything in mathlib :sad:

If I ever figure out how to do it maybe I can contribute something during the Xena summer projects then :upside_down:

Lucas Viana (Jun 11 2020 at 02:45):

Lucas Viana said:

Does mathlib has something like the fact that measures form a cone in a vector space? Like, I want the measure to be a probability, so I would multiply the counting measure by 1/n

Or, could I define a new measure by multiplying one by a measurable function?

Jalex Stark (Jun 11 2020 at 02:47):

I think you're going to resolve your questions a lot more efficiently by reading the measure_theory files and coming back with specific questions

Jalex Stark (Jun 11 2020 at 02:48):

especially if your questions are sorrys

Lucas Viana (Jun 11 2020 at 11:40):

I see. Thanks

Pedro Minicz (Jun 11 2020 at 22:30):

Is there a more elegant way of proving the following?

import set_theory.ordinal

open cardinal

example {a b : cardinal} (h : omega  a * b) : omega  a  omega  b :=
begin
  by_cases ha : omega  a,
  { left, exact ha },
  { by_cases hb : omega  b,
    { right, exact hb },
    have : a * b < omega := mul_lt_omega (not_le.mp ha) (not_le.mp hb),
    exfalso, exact ne_of_lt this (le_antisymm (le_of_lt this) h) }
end

Mario Carneiro (Jun 11 2020 at 22:31):

mul_eq_max

Pedro Minicz (Jun 11 2020 at 22:31):

If there is one tactic from Coq I would love to have on Lean is auto.

Pedro Minicz (Jun 11 2020 at 22:32):

Is there something similar, that would allow me to write:

  by_cases ha : omega  a; auto,
  by_cases hb : omega  b; auto,

Pedro Minicz (Jun 11 2020 at 22:32):

cc doesn't close a \or b if you have one of them as a hypothesis.

Pedro Minicz (Jun 11 2020 at 22:35):

@Mario Carneiro I don't see how I could use mul_eq_max.

Jason Orendorff (Jun 11 2020 at 22:39):

def even (k : ) : Prop := k % 2 = 0

instance : decidable_pred even :=
  λ n, dite (n % 2 = 0) decidable.is_true decidable.is_false

Is there a better way to convince Lean that even is decidable?

Jason Orendorff (Jun 11 2020 at 22:54):

In general, if I want to prove something about code that uses, say, array.to_list, ...do I probably have to prove a bunch of theorems about array.to_list first? And won't those break if any definitions change?

Jason Orendorff (Jun 11 2020 at 22:54):

(that's unrelated to my question about even)

Kyle Miller (Jun 11 2020 at 23:03):

Jason Orendorff said:

Is there a better way to convince Lean that even is decidable?

I just found this:

instance : decidable_pred even := λ a, eq.decidable _ _

using

instance : decidable_pred even := begin
  intro a,
  library_search,
end

Kyle Miller (Jun 11 2020 at 23:05):

Filling in the underscores (which are unified), this would be

instance : decidable_pred even := λ a, eq.decidable (a % 2) 0

Pedro Minicz (Jun 11 2020 at 23:10):

A few days ago @Kevin Buzzard noted that non-terminal simps should be avoided.

Pedro Minicz (Jun 11 2020 at 23:10):

I just found a funny situation where squeeze_simp suggests simp only [].

Pedro Minicz (Jun 11 2020 at 23:10):

In this case, using dsimp, should be fine, right?

Pedro Minicz (Jun 11 2020 at 23:11):

From what I've gathered, using non-terminal dsimps shouldn't be problematic. Is that correct?

Mario Carneiro (Jun 11 2020 at 23:14):

@Pedro Minicz Ah, I see now that the assumptions to mul_eq_max are not appropriate.

example {a b : cardinal} (h : omega  a * b) : omega  a  omega  b :=
begin
  by_contra h₂, push_neg at h₂,
  refine not_lt_of_ge h (mul_lt_omega h₂.1 h₂.2),
end

Mario Carneiro (Jun 11 2020 at 23:14):

there isn't really much to the proof besides contraposition on mul_lt_omega

Kevin Buzzard (Jun 11 2020 at 23:15):

I believe that non-terminal dsimps are also problematic but I don't really understand why. I guess the behaviour of dsimp can change over time, like simp.

Mario Carneiro (Jun 11 2020 at 23:16):

example {a b : cardinal} : omega  a * b  omega  a  omega  b :=
begin
  refine not_imp_not.1 _,
  push_neg,
  simp [mul_lt_omega] {contextual := tt},
end

Mario Carneiro (Jun 11 2020 at 23:17):

Nonterminal dsimp only is fine

Mario Carneiro (Jun 11 2020 at 23:18):

@Jason Orendorff

@[derive decidable_pred]
def even (k : ) : Prop := k % 2 = 0

Pedro Minicz (Jun 11 2020 at 23:19):

I just noticed dsimp (or simp only []) makes no difference in my situation.

Pedro Minicz (Jun 11 2020 at 23:19):

It was pretty much behaving like unfold, just changing how the goal looks.

Mario Carneiro (Jun 11 2020 at 23:20):

simp only [] can do some things that dsimp only [] doesn't

Pedro Minicz (Jun 11 2020 at 23:20):

I guess that is the case for simp only []. Still, I am a bit surprised it does something. :thinking:

Mario Carneiro (Jun 11 2020 at 23:20):

dsimp only will unfold beta redexes and other things like that

Mario Carneiro (Jun 11 2020 at 23:21):

simp only will also expand constructor equalities like a :: l = b :: l2 ~> a = b /\ l = l2

Pedro Minicz (Jun 11 2020 at 23:27):

I see, interesting.

Pedro Minicz (Jun 11 2020 at 23:27):

Where can I read on simp options, like { contextual := tt } used above?

Mario Carneiro (Jun 11 2020 at 23:42):

I don't have a very good source, but you can at least look at src#tactic.simp_config to get a sense of what there is

Mario Carneiro (Jun 11 2020 at 23:43):

the only options I have ever had occasion to use are contextual and single_pass

Mario Carneiro (Jun 11 2020 at 23:44):

also discharger, which is in src#tactic.simp_config_ext

Kenny Lau (Jun 12 2020 at 06:55):

import set_theory.cardinal

open cardinal

example {a b : cardinal} (h : omega  a * b) : omega  a  omega  b :=
begin
  contrapose! h,
  exact mul_lt_omega h.1 h.2
end

Pedro Minicz (Jun 12 2020 at 15:19):

How exactly does @[to_additive] work?

Johan Commelin (Jun 12 2020 at 15:32):

It knows about

  1. a list of strings (one, mul, etc) and translates them to zero, add, etc...
  2. a database of names of lemmas and definitions mul_one etc... and matching additive versions add_zero, etc...

It uses (1) to build the additive version of the lemma it is working on, and uses (2) to reconstruct an additive proof from the given multiplicative proof.

Finally, it adds this new lemma to the database mentioned in (2).

Johan Commelin (Jun 12 2020 at 15:32):

@Pedro Minicz :up:

Bryan Gin-ge Chen (Jun 12 2020 at 15:45):

We should add tactic docs for the to_additive attribute.

Paul van Wamelen (Jun 12 2020 at 15:54):

https://leanprover-community.github.io/mathlib_docs/algebra/group/to_additive.html

Bryan Gin-ge Chen (Jun 12 2020 at 15:56):

Yes, I mean we need to port some of that text to a tactic doc so that to_additive shows up with the other attributes here.

Pedro Minicz (Jun 12 2020 at 17:23):

@Johan Commelin I see, thank you!

Jason Orendorff (Jun 13 2020 at 00:37):

Is there a way to #print an instance? I know that amounts to has_dvd.dvd, but don't know where the instance comes from.

Jason Orendorff (Jun 13 2020 at 00:38):

I've been doing a lot of grepping in lean/library/init, it's tiring

Pedro Minicz (Jun 13 2020 at 00:46):

What expression appears in?

Pedro Minicz (Jun 13 2020 at 00:47):

I usually find class instances by "reverse engineering" the expressions where they appear.

Jason Orendorff (Jun 13 2020 at 00:47):

It means "divides". So for example

theorem divides_self (a : ) : a  a := exists.intro 1 a.mul_one.symm
theorem one_divides (n : ) : 1  n := exists.intro n n.one_mul.symm

Pedro Minicz (Jun 13 2020 at 00:50):

In this case its the instance of has_dvd. On VSCode pressing F12 with the cursor on nat.has_dvd opens the definition:

#check nat.has_dvd

Pedro Minicz (Jun 13 2020 at 00:50):

Not sure if exactly what you wanted.

Mario Carneiro (Jun 13 2020 at 00:51):

Here's a short idiom for looking up how a typeclass instance is solved, assuming you know the name of the typeclass already (here has_dvd):

#check (by apply_instance : has_dvd int)
-- comm_semiring_has_dvd : has_dvd ℤ

Jason Orendorff (Jun 13 2020 at 00:53):

neat

Jason Orendorff (Jun 13 2020 at 00:53):

thanks

Pedro Minicz (Jun 13 2020 at 01:17):

Say I have f : ℕ → ℝ, is it possible to define i=0f(i)\sum_{i=0}^{\infty} f(i)?

Pedro Minicz (Jun 13 2020 at 01:18):

It should be certainly possible if f : ℕ → ennreal, because then I have a value even if the sequence diverges.

Mario Carneiro (Jun 13 2020 at 02:01):

You can use has_sum from topology.algebra.infinite_sum

Mario Carneiro (Jun 13 2020 at 02:03):

src#ennreal.summable proves that every ennreal sum converges

Walter Moreira (Jun 13 2020 at 02:30):

(deleted)

Pedro Minicz (Jun 13 2020 at 03:36):

@Mario Carneiro thanks!

Bryan Gin-ge Chen (Jun 13 2020 at 04:30):

Bryan Gin-ge Chen said:

We should add tactic docs for the to_additive attribute.

#3055

Alena Gusakov (Jun 13 2020 at 19:01):

I get this error when I try to apply mul_mul_le_of_le_div to the hypothesis h: 0 < 2. Not sure how to fix?

import data.real.basic

variables a b : 

example : abs (a*b)  (a^2 + b^2) / 2 :=
begin
  have h : 0 < 2, {sorry},
  have h2 : 2  2, {exact le_refl 2},
  apply abs_le_of_le_of_neg_le,
  have hp1 : 0  a^2 - 2*a*b + b^2,
    calc
      a^2 - 2*a*b + b^2 = (a - b)^2 : by ring
      ...  0                       : by apply pow_two_nonneg,
  have hp2 : 2*a*b  a^2 + b^2,
    by linarith,
  rw [mul_comm,  mul_assoc, mul_comm,  mul_assoc] at hp2,
  --error happens here:
  --apply mul_le_of_le_div h,
  have hm1 : 0  a^2 + 2*a*b + b^2,
    calc
      a^2 + 2*a*b + b^2 = (a + b)^2 : by ring
      ...  0                       : by apply pow_two_nonneg,
  have hm2 : -(2*a*b)  a^2 + b^2,
    by linarith,
  sorry,
  sorry,
end

Here's the error message

type mismatch at application
  mul_le_of_le_div h
term
  h
has type
  0 < 2
but is expected to have type
  0 < ?m_3

Kevin Buzzard (Jun 13 2020 at 19:01):

If you hover over mul_le_of_le_div you'll see which kind of objects it applies to, and I conjecture that the objects you're applying it to aren't that kind of object.

Kevin Buzzard (Jun 13 2020 at 19:02):

mul_le_of_le_div :  {α : Type u_1} [_inst_1 : linear_ordered_field α] {a b c : α}, 0 < c  a  b / c  a * c  b

so I conjecture that the 0 and 2 in h are not elements of a linearly ordered field.

Alena Gusakov (Jun 13 2020 at 19:03):

I'm doing this in the real numbers

Kevin Buzzard (Jun 13 2020 at 19:03):

oh they are definitely a linearly ordered field. So now I conjecture that you have accidentally made h a statement about the natural numbers.

Kevin Buzzard (Jun 13 2020 at 19:03):

But you would get better answers here, from me and others, if you were to post an #mwe

Alena Gusakov (Jun 13 2020 at 19:09):

Okay, edited

Pedro Minicz (Jun 13 2020 at 19:13):

How do I get to_additive to ignore some definitions?

import group_theory.quotient_group
import set_theory.ordinal

open quotient_group

namespace cardinal

variables {α : Type*} [group α] {s : set α} [is_subgroup s]

@[to_additive]
lemma mk_group_eq_mk_quotient_mul_mk_subgroup :
  mk α = mk (quotient s) * mk s :=
begin
  rw [mul_def, cardinal.eq],
  exact is_subgroup.group_equiv_quotient_times_subgroup _⟩
end

Kevin Buzzard (Jun 13 2020 at 19:14):

So when you write have h : 0 < 2, {sorry},, you didn't say which 0 and 2 you meant, so Lean assumed you meant the natural number 0.

Alena Gusakov (Jun 13 2020 at 19:14):

Reposting

I get this error when I try to apply mul_mul_le_of_le_div to the hypothesis h: 0 < 2. Not sure how to fix?

import data.real.basic

variables a b : 

example : abs (a*b)  (a^2 + b^2) / 2 :=
begin
  have h : 0 < 2, {sorry},
  have h2 : 2  2, {exact le_refl 2},
  apply abs_le_of_le_of_neg_le,
  have hp1 : 0  a^2 - 2*a*b + b^2,
    calc
      a^2 - 2*a*b + b^2 = (a - b)^2 : by ring
      ...  0                       : by apply pow_two_nonneg,
  have hp2 : 2*a*b  a^2 + b^2,
    by linarith,
  rw [mul_comm,  mul_assoc, mul_comm,  mul_assoc] at hp2,
  --error happens here:
  --apply mul_le_of_le_div h,
  have hm1 : 0  a^2 + 2*a*b + b^2,
    calc
      a^2 + 2*a*b + b^2 = (a + b)^2 : by ring
      ...  0                       : by apply pow_two_nonneg,
  have hm2 : -(2*a*b)  a^2 + b^2,
    by linarith,
  sorry,
  sorry,
end

Here's the error message

type mismatch at application
  mul_le_of_le_div h
term
  h
has type
  0 < 2
but is expected to have type
  0 < ?m_3

Kevin Buzzard (Jun 13 2020 at 19:14):

THIS IS REALLY ANNOYING WHEN LOTS OF PEOPLE USE THIS STUPID NOOB QUESTIONS THREAD AT THE SAME TIME. MAKE YOUR OWN THREADS!

Johan Commelin (Jun 13 2020 at 19:14):

@Pedro Minicz please start a new thread

Johan Commelin (Jun 13 2020 at 19:14):

@Alena Gusakov idem dito

Kevin Buzzard (Jun 13 2020 at 19:14):

I want to delete this thread

Alena Gusakov (Jun 13 2020 at 19:15):

Sorryyyy lol. Maybe it should be a stream?

Johan Commelin (Jun 13 2020 at 19:15):

Well, it is already in the "new members" stream...

Kevin Buzzard (Jun 13 2020 at 19:15):

So @Alena Gusakov what you want to do is have h : (0 : \R) < 2 := by linarith,, and then you should be back on track.

Kevin Buzzard (Jun 13 2020 at 19:16):

Seriously, can we somehow kill this topic? Anyone can ask any question, however basic, in #new members, they are mostly noob questions and this is fine. But when two people talk over each other it's like gitter.

Johan Commelin (Jun 13 2020 at 19:16):

I think the most effective way to kill this thread is if all people that regularly answer questions click the mute button... on this particular thread (not the stream). I'm doing that now.

Alena Gusakov (Jun 13 2020 at 19:17):

Thanks!

Kevin Buzzard (Jun 13 2020 at 19:18):

(I know Alena from discord and I know Pedro from here, I'm not annoyed at them, I'm annoyed at this topic :-) )

Kevin Buzzard (Jun 13 2020 at 19:18):

OK I am going to follow Johan's advice and mute this topic.

Reid Barton (Jun 13 2020 at 21:27):

If everyone does this, won't this just result in new users asking questions here and not getting answers?

Bryan Gin-ge Chen (Jun 13 2020 at 22:31):

@Alex J. Best I think we've decided not to use this thread anymore. Please make a new thread.

Alex J. Best (Jun 13 2020 at 22:32):

Bryan Gin-ge Chen said:

Alex J. Best I think we've decided not to use this thread anymore. Please make a new thread.

apologies, it was a reply to an old message in this thread so it defaulted, should be changed now

Bhavik Mehta (Jun 13 2020 at 23:00):

Reid Barton said:

If everyone does this, won't this just result in new users asking questions here and not getting answers?

I think so too... Maybe more effective would be a notice telling new users not to ask questions here

Jalex Stark (Jun 14 2020 at 00:09):

effectively that would be locking the topic, where the last message is a directive to ost a new topic instead

Reed Mullanix (Jun 14 2020 at 07:21):

Hey all! Started playing around with lean again after a bit of a hiatus, and had a quick question:
What is the best way to handle "dependent equalities"? For context, this is what I'm trying to accomplish:

structure algebra (T : C  C) [monad.{v₁} T] : Type (max u₁ v₁) :=
(A : C)
(a : T.obj A  A)
(unit' : (η_ T).app A  a = 𝟙 A . obviously)
(assoc' : ((μ_ T).app A  a) = (T.map a  a) . obviously)


restate_axiom algebra.unit'
restate_axiom algebra.assoc'

namespace algebra
variables {T : C  C} [monad.{v₁} T]

lemma algebra.ext {A B : algebra T} (a_obj : A.A = B.A) (a_action : A.a = B.a) : A = B
begin
  sorry
end

Reed Mullanix (Jun 14 2020 at 07:24):

Obviously algebra.ext won't typecheck, as the the type of algebra.a depends on algebra.A, so a_action is not well typed. Is there some sort of dependent equality a la PathP from HoTT that will get the job done?

Reed Mullanix (Jun 14 2020 at 07:28):

Also, what is the general take on heterogenous equality?

Carl Friedrich Bolz-Tereick (Jun 14 2020 at 07:31):

@Reed Mullanix please make a new thread for this topic, 'noob questions' is muted by several question answerers, it seems

Scott Morrison (Jun 14 2020 at 07:50):

@Reed Mullanix, if you go back to edit your first post in this thread, you should be able to change the topic (and leave the default setting to also change the topic of all subsequent messages).

Scott Morrison (Jun 14 2020 at 07:51):

We've decided that the noob questions(s) topic is a bad idea --- we very much want the questions, we just want them slightly easier to follow / organise, so I'll hold off on answering for now. :-)

Kenny Lau (Jun 14 2020 at 08:13):

PSA: don't post here! (use another name for your topic)

Rob Lewis (Jun 14 2020 at 09:16):

WE ARE NO LONGER USING THE NOOB QUESTIONS TOPIC. There is no cost to making a new topic for your question, and it makes it easier for others to find. Please do not post in this topic.

James Arthur (Jun 25 2020 at 18:35):

removed

Enrico Borba (Jan 30 2021 at 19:04):

removed

Si Yu How (Nov 30 2023 at 23:56):

(deleted)

Kevin Buzzard (Dec 17 2023 at 11:09):

See above -- we are no longer using this thread. Please start a new one with a more helpful title.


Last updated: Dec 20 2023 at 11:08 UTC