Zulip Chat Archive
Stream: general
Topic: nearly no_confusion
Kevin Buzzard (Mar 23 2018 at 23:22):
I spent some time with Chris at Xena last night trying to get to the bottom of no_confusion
. I seem to have a slightly simpler approach which gives what looks to me like everything we need for a proof of the idea that two nats are equal iff they were constructed using the same constructors in the same order.
Kevin Buzzard (Mar 23 2018 at 23:23):
inductive xnat | zero : xnat | succ : xnat → xnat open xnat def xnat_no_confusion_prop (s : xnat) (t : xnat) : Prop := xnat.rec (xnat.rec true (λ _ _,false) t) (λ m _,(xnat.rec false (λ n _,(m=n)) t)) s variables m n : xnat #reduce xnat_no_confusion_prop zero zero -- true #reduce xnat_no_confusion_prop (succ m) zero -- false #reduce xnat_no_confusion_prop zero (succ n) -- false #reduce xnat_no_confusion_prop (succ m) (succ n) -- (m = n) def xnat_no_confusion' (s t : xnat) (H : s = t) : xnat_no_confusion_prop s t := begin rw H, cases t with n, exact trivial, -- t = 0 show (n=n), -- t = succ n refl, end example : succ m = succ n → m = n := λ H, xnat_no_confusion' _ _ H example : ¬ succ m = zero := λ H, xnat_no_confusion' _ _ H ```
Kevin Buzzard (Mar 23 2018 at 23:24):
Here I prove injectivity of xnat.succ
and also that zero
is not a succ
using an easier variant of the no_confusion
/ no_confusion_type
strategy, where instead of carrying round a general sort P
I just use the relevant props.
Kevin Buzzard (Mar 23 2018 at 23:25):
Are there other applications of no_confusion_type
lemmas which go beyond this sort of "instance of inductive type is uniquely determined by how it was constructed" sort of thing? Or am I missing something else? Why is this sort P
used?
Kevin Buzzard (Mar 23 2018 at 23:27):
I'm writing a blog post about all this no_confusion
stuff and I was somehow expecting for this to come out in the wash but it didn't, so I need to think of something coherent to say about why the general sort P is involved before I post.
Andrew Ashworth (Mar 23 2018 at 23:56):
was mcbride's paper on no confusion
not helpful?
Kevin Buzzard (Mar 23 2018 at 23:58):
I either don't know this paper, or I looked at it before and at the time it was too much for me. Having actually spent some time thinking about no_confusion_type now perhaps I can ask you for a link to this paper in the hope that I can get something from it?
Andrew Ashworth (Mar 23 2018 at 23:59):
https://pdfs.semanticscholar.org/d224/e96c59a81a471625faf87118b6299094e1e4.pdf section 7.3
Andrew Ashworth (Mar 23 2018 at 23:59):
http://strictlypositive.org/thesis.pdf page 136
Andrew Ashworth (Mar 24 2018 at 00:04):
mcbride's phd thesis is definitely hard going
Andrew Ashworth (Mar 24 2018 at 00:06):
i had to sit and stare at it for awhile, and i still don't fully understand it :sweat_smile:
Kevin Buzzard (Mar 24 2018 at 00:08):
Well I am not an expert but it seems to me that what McBride is defining on p136ff is what is implemented in Lean.
Kevin Buzzard (Mar 24 2018 at 00:08):
Maybe I should just post my blog post. Hang on.
Mario Carneiro (Mar 24 2018 at 00:18):
I think the reason no_confusion
uses a general sort instead of using the consequent m = n
directly is because the number of conclusions varies with the constructors, so that for list
it would be something like cons a l = cons a' l' -> a = a' /\ l = l'
, which is a bit less natural to define inductively since the 0-ary case gets special consideration, and it also relies on the definition and
which may not be available yet. As it is defined normally no_confusion
for an arbitrary type needs nothing except the basics of DTT, plus eq
and heq
(which comes up when there are dependencies, for example sigma.mk a b = sigma.mk a' b' -> a = a' /\ b == b'
where the latter equality involves b : B a
and b' : B a'
which are different types.
Kevin Buzzard (Mar 24 2018 at 00:25):
https://xenaproject.wordpress.com/2018/03/24/no-confusion-over-no_confusion/
Kevin Buzzard (Mar 24 2018 at 00:25):
I'll perhaps add some comments about what you said above, later.
Mario Carneiro (Mar 24 2018 at 00:37):
Here's another interesting story to do with no_confusion:
inductive {u} mystery : Sort u | A : mystery | B : mystery def confused : Type := mystery def confused.A : confused := mystery.A def confused.B : confused := mystery.B open confused theorem A_ne_B_attempt_1 : A ≠ B := λ h, mystery.no_confusion h --fails: no_confusion not defined theorem A_ne_B_attempt_2 : A ≠ B := λ h, by cases h -- now I have to prove ⊢ false? theorem A_ne_B_attempt_3.no_confusion_type : mystery → mystery → Type | mystery.A mystery.A := _ -- already fails, can't case on mystery | mystery.B mystery.A := _ | mystery.A mystery.B := _ | mystery.A mystery.B := _ #print mystery.rec -- protected eliminator tc.mystery.rec : ∀ {C : mystery → Prop}, -- C mystery.A → C mystery.B → ∀ (n : mystery), C n -- -- Hm, the recursor only eliminates to Prop, so we can't -- define functions like no_confusion_type -- Maybe we can prove they are equal instead? If it was a Prop -- they would be equal by proof irrelevance... theorem A_eq_B_attempt_1 : A = B := rfl --nope
Mario Carneiro (Mar 24 2018 at 00:39):
The punch line is that confused
is a type with two elements A
and B
, for which it is impossible to prove whether they are equal or not
Mario Carneiro (Mar 24 2018 at 00:45):
By the way in the blog post you could have defined mytype_equal
like so:
def mytype_equal : mytype → mytype → Prop | mytype.AA mytype.AA := true | mytype.AA mytype.ZZ := false | mytype.ZZ mytype.AA := false | mytype.ZZ mytype.ZZ := true
Not sure if you were deliberately showing off other ways to construct it
Mario Carneiro (Mar 24 2018 at 00:51):
Re: the one-sided inverse, that approach actually does work in general, as a "partial projection". So for example you could define head l : option A
when l : list A
, to return none
on the empty list and a
on a::l
. In general, you have an inductive type with several constructors, each of which has several arguments, and you can project each element of each constructor in an option
, returning none
if the wrong constructor is passed in and some a
where a
is the appropriate argument to the constructor otherwise. You can reconstruct the cases_on
theorem using all these projections.
Kevin Buzzard (Mar 24 2018 at 01:05):
Aah excellent, I will beef up the post tomorrow
Kevin Buzzard (Mar 24 2018 at 01:05):
Thanks!
Kevin Buzzard (Mar 24 2018 at 16:30):
PS Do you think I'm wasting my time trying to get to the bottom of things like this? I am currently thinking about getting to the bottom of something else I've always been confused by -- "using-well-founded" (the error you get when you try and define something by recursion but things are slightly too convoluted for the elaborator(?) to figure out that what you're doing will definitely terminate after a finite time). Currently the only way I know how to figure stuff out is to look at the source code and then work stuff out by experimenting. My plan, like the no-confusion post, would be to work out some simple examples, and basically write some docs and either PR them to mathlib docs or blog about them. But when Lean 4 comes is there every chance that everything I write about these sorts of "obscure" parts of Lean will be no longer relevant?
Kevin Buzzard (Mar 24 2018 at 16:34):
I think I'm right in saying that using_well_founded is not mentioned in either the reference manual or TPIL (like no_confusion).
Kevin Buzzard (Mar 24 2018 at 16:49):
Re : mystery
-- why is it any different to bool
? It clearly is:
Kevin Buzzard (Mar 24 2018 at 16:49):
inductive {u} mystery : Sort u | A : mystery | B : mystery #print prefix mystery inductive bool' | TT : bool' | FF : bool' #print prefix bool'
Kevin Buzzard (Mar 24 2018 at 16:51):
we get bool'.no_confusion
but no mystery.no_confusion
; and bool.rec
eliminates to any Sort whereas mystery.rec
only eliminates to Prop
. Why is this?
Simon Hudon (Mar 24 2018 at 16:53):
Because mystery
can inhabit multiple types including Prop
and when it does, it's a bit like or
. If you could eliminate to anything other than Prop
, you could affect the behavior of a program with just a proof
Simon Hudon (Mar 24 2018 at 16:55):
for example:
def prog : mystery.{0} -> nat | A := 0 | B := 1
wouldn't make sense because of proof irrelevance
Kevin Buzzard (Mar 24 2018 at 17:08):
I see. The whole point is that mystery is allowed to be a Prop so everything is paranoid. You guys should get classical.
Andrew Ashworth (Mar 24 2018 at 17:09):
it's not a waste of time. some version of well-founded recursion will be in lean 4
Andrew Ashworth (Mar 24 2018 at 17:09):
what might change is the exact syntax / using_well_founded
, so perhaps you could discuss doing it manually using well_founded.fix
Kevin Buzzard (Mar 24 2018 at 17:10):
I know even less about well_founded.fix
.
Andrew Ashworth (Mar 24 2018 at 17:11):
i don't know much either about the fixed points of functions, so i would avidly read your blog post :)
Andrew Ashworth (Mar 24 2018 at 17:11):
it's a way of expressing well-founded induction
Kevin Buzzard (Mar 24 2018 at 17:11):
You should use Coq, everything seems to be defined using Fixpoint there.
Kevin Buzzard (Mar 24 2018 at 17:12):
Hey, I now realise I don't even understand Lean's definition of nat.div and I am supposed to be a professor of number theory :-/
Kevin Buzzard (Mar 24 2018 at 17:13):
Maybe this was mentioned in TPIL...
Kevin Buzzard (Mar 24 2018 at 17:15):
Maybe it's time I read TPIL Chapter 8 again in fact.
Moses Schönfinkel (Mar 24 2018 at 17:21):
Coq just uses the keyword Fixpoint
to express Definition
that can recurse, there's not much difference from Lean's def
Simon Hudon (Mar 24 2018 at 17:25):
The big difference is that Lean takes care of well founded recursion for you
Andrew Ashworth (Mar 24 2018 at 17:29):
regarding the div example in TPIL, I wish jeremy had pattern matched on y, it would have been clearer then IMO
Moses Schönfinkel (Mar 24 2018 at 17:29):
Except for when it doesn't and then you're stuck with quoted tactics and such :(.
Moses Schönfinkel (Mar 24 2018 at 17:29):
Coq has a better solution to provin gtermination.
Moses Schönfinkel (Mar 24 2018 at 17:31):
Not to mention that Coq can in some cases help you show your relation is well founded. Then you're left with proof of termination only.
Kevin Buzzard (Mar 24 2018 at 17:31):
Right -- this is what I see with using_well_founded
-- I am having to actually write a tactic (or know about the "`[" trick)
Kevin Buzzard (Mar 24 2018 at 17:32):
I don't know whether as a mathematician I should care about this
Kevin Buzzard (Mar 24 2018 at 17:32):
The reason I currently care is that I have been reading software foundations
Kevin Buzzard (Mar 24 2018 at 17:32):
and I defined a positive integer as an inductive type using binary notation: three constructors one
, double x
and double_and_then_add_one x
Kevin Buzzard (Mar 24 2018 at 17:33):
and when I tried to define addition on these things, I have trouble doing (2x+1)+(2y+1)
Simon Hudon (Mar 24 2018 at 17:34):
regarding the div example in TPIL, I wish jeremy had pattern matched on y, it would have been clearer then IMO
A clearer way to present that definition of division can be found:
I believe nat
has access to very little tactic machinery so they have to be frugal there.
Andrew Ashworth (Mar 24 2018 at 17:39):
i think it's worth caring about using well_founded.fix
by hand, just like no_confusion
Andrew Ashworth (Mar 24 2018 at 17:39):
it's applicable whenever you're doing complicated induction
Moses Schönfinkel (Mar 24 2018 at 17:42):
Coq just asks for proofs of well foundedness and termination (according some decreasing measure).
Kevin Buzzard (Mar 24 2018 at 17:42):
Yes, as an end user I forget all about this "what is the actual state of Lean when this object is being made" stuff. For example with no_confusion I was thinking "why not just return a=a' and L=L'" for list.no_confusion_type (a::L) (a'::L')
Moses Schönfinkel (Mar 24 2018 at 17:42):
Lean has this weird way of requiring tactics.
Kevin Buzzard (Mar 24 2018 at 17:42):
and then Mario points out that and
might not be defined at this point. Oops.
Kevin Buzzard (Mar 24 2018 at 17:43):
@Moses Schönfinkel Is it only weird because you are used to the Coq way? i.e. are you saying "I don't like it because I know Coq and Lean is different", or "I don't like it because I know Coq and Lean is worse for this sort of thing"
Moses Schönfinkel (Mar 24 2018 at 17:51):
It's worse because you can barely see the state of what you're proving, given it's in a tactic monad. All your feedback is nested exceptions from within the quoted tactics block.
Moses Schönfinkel (Mar 24 2018 at 17:52):
In Coq those are first-class proofs.
Moses Schönfinkel (Mar 24 2018 at 17:54):
So instead of a type you need to find a term for, in Lean you're asked to provide tactics.
Andrew Ashworth (Mar 24 2018 at 18:00):
yes, i agree, i always end up writing out everything using well_founded.fix
just so it's easier to read
Andrew Ashworth (Mar 24 2018 at 18:01):
i'm glad it's not just my poor Lean programming skills, haha
Kevin Buzzard (Mar 24 2018 at 18:11):
Would defining addition on that binary type I mentioned above be easier in Coq? How hard is it in Lean?
Kevin Buzzard (Mar 24 2018 at 18:12):
(the inductive type with constructors one, double and double_then_add_one)
Andrew Ashworth (Mar 24 2018 at 18:17):
i remember doing that exercise and trying to solve it by defining a coercion to nat. the trouble is you can have an infinite sequence of doub doub doub doub doub zero
and you need to normalize the expression before you can do so
Kevin Buzzard (Mar 24 2018 at 18:18):
I decided that zero was a bad thing to have, I started at one
Kevin Buzzard (Mar 24 2018 at 18:18):
then every positive integer is uniquely represented
Kevin Buzzard (Mar 24 2018 at 18:18):
and there is no confusion
Andrew Ashworth (Mar 24 2018 at 18:19):
that's cheating! haha
Andrew Ashworth (Mar 24 2018 at 18:21):
the same problem arises in the construction of the integers, where the pair (1,0) is the same as (2,1), but you need some way to say they are equivalent
Moses Schönfinkel (Mar 24 2018 at 18:23):
I remember doing it in Coq, I am not sure if it would be at all more difficult to do in Lean.
Chris Hughes (Mar 24 2018 at 18:58):
I solved the problem Kevin and Andrew were talking about.
lemma sizeof_pos : ∀ n : pint, 0 < pint.sizeof n | one := dec_trivial | (d m) := by unfold pint.sizeof; rw add_comm; exact succ_pos _ | (da m) := by unfold pint.sizeof; rw add_comm; exact succ_pos _ def padd2 : pint → pint → pint | one one := d one | one (d m) := da m | one (da m) := d (padd2 one m) | (d n) one := da n | (d n) (d m) := d (padd2 n m) | (d n) (da m) := da (padd2 n m) | (da n) one := d (padd2 n one) | (da n) (d m) := da (padd2 n m) | (da n) (da m):= have h : 0 < pint.sizeof n, from sizeof_pos _, d (padd2 one (padd2 n m))
The order of arguments in the final case matters, I think because in the default well-founded relation on Σ' p : pint, pint
, ⟨one, padd2 n m⟩ ≺ ⟨da n, da m⟩
, but not necessarily ⟨padd2 n m, one⟩ ≺ ⟨da n, da m⟩
I'm not sure why it wanted me to prove sizeof n
was pos however, but that was the goal that came up in the error message.
Mario Carneiro (Mar 24 2018 at 20:56):
@Kevin Buzzard You should look at mathlib num
type, which is binary naturals just as you are describing. You don't need wf recursion for any of the definitions
Andrew Ashworth (Mar 24 2018 at 21:33):
yeah, iirc that exercise asks you to define nat_to_bin
and bin_to_nat
. Once you've proven there is a bijection between the binary naturals and the"traditional" naturals, you can use nat addition on the binary numbers
Andrew Ashworth (Mar 24 2018 at 21:33):
this is hard to do if you don't have zero
Kevin Buzzard (Mar 24 2018 at 21:38):
it's harder to do if you do have zero
Kevin Buzzard (Mar 24 2018 at 21:38):
because double double double zero is zero
Kevin Buzzard (Mar 24 2018 at 21:38):
so bijectivity fails much worse
Kenny Lau (Mar 24 2018 at 21:39):
bijective binary to the rescue
Kenny Lau (Mar 24 2018 at 21:39):
ok it's literally inside the name
Andrew Ashworth (Mar 24 2018 at 21:40):
yeah, handling the doub doub doub zero
case is the real trick to the problem
Kenny Lau (Mar 24 2018 at 21:41):
or you can just surject to the naturals and quotient
Andrew Ashworth (Mar 24 2018 at 21:41):
i solved it a long time ago, let me see if i can find my solution, maybe...
Kenny Lau (Mar 24 2018 at 21:41):
literally a~b iff f(a)=f(b)
Kenny Lau (Mar 24 2018 at 21:42):
(deleted)
Kevin Buzzard (Mar 24 2018 at 21:43):
I only defined the map one way. Did it ask you for the other way later on?
Andrew Ashworth (Mar 24 2018 at 21:44):
so, it seems they've made many of the exercises different / a little easier in the new edition
Andrew Ashworth (Mar 24 2018 at 21:45):
the original problem was a 4 star advanced take you all day to complete if you're a learner hair-raiser
Andrew Ashworth (Mar 24 2018 at 21:45):
at least it was for me
Andrew Ashworth (Mar 24 2018 at 21:47):
which is why i remember some of the details about it
Kevin Buzzard (Mar 24 2018 at 21:48):
They dumbed down for the kids?
Andrew Ashworth (Mar 24 2018 at 21:49):
not too badly! and i agree with dumbing it down, it's an introductory text
Andrew Ashworth (Mar 24 2018 at 21:50):
i can't imagine teaching yourself from the original software foundations without guidance or help
Andrew Ashworth (Mar 24 2018 at 21:50):
i eventually found somebody's homework solutions online and figured out how to solve it
Andrew Ashworth (Mar 24 2018 at 21:57):
ahh, yes
Andrew Ashworth (Mar 24 2018 at 21:57):
http://staff.ustc.edu.cn/~bjhua/courses/theory/2012/slides/lec1notes.html
Andrew Ashworth (Mar 24 2018 at 21:58):
once you've normalized your binary number, then you can create a bijection, and life is good again
Andrew Ashworth (Mar 25 2018 at 00:36):
literally a~b iff f(a)=f(b)
if only i had known at the time. they didn't talk much about equivalence relations in engineering school
Andrew Ashworth (Mar 25 2018 at 00:39):
and if SF had started talking about how bin_to_nat
was onto but not one-to-one, I doubt many undergraduates would know what that really meant
Kevin Buzzard (Mar 25 2018 at 00:40):
That's funny. I teach that to maths undergraduates in year 1 term 1. And equivalence relations.
Simon Hudon (Mar 25 2018 at 00:41):
I remember learning that in first year of university too, in discrete math
Andrew Ashworth (Mar 25 2018 at 00:42):
i never had to take discrete math, I was an electrical engineer
Simon Hudon (Mar 25 2018 at 00:42):
(I did CS and not engineering though, if that makes a difference)
Andrew Ashworth (Mar 25 2018 at 00:42):
instead there was a 2 semester sequence on fourier transforms
Andrew Ashworth (Mar 25 2018 at 00:43):
where we basically learned very little theory and a lot of how to compute various integrals
Andrew Ashworth (Mar 25 2018 at 00:43):
i may have mentioned this before but i have issues with how they teach math in engineering school :)
Simon Hudon (Mar 25 2018 at 00:47):
It seems common that people who like math don't get enough of it in engineering school.
Andrew Ashworth (Mar 25 2018 at 00:48):
yes, we get a lot of boring computation instead
Andrew Ashworth (Mar 25 2018 at 00:48):
my linear algebra class was basically doing a lot of gauss-jordan elimination and calculating eigenvectors by hand
Andrew Ashworth (Mar 25 2018 at 00:49):
which ends up being completely a waste of time since no working engineer would ever do these things without a computer
Simon Hudon (Mar 25 2018 at 00:50):
Mine was like that too. I've never missed my calculator more than in that class
Simon Hudon (Mar 25 2018 at 00:52):
The follow up class was about numerical methods for linear algebra so I guess I should have been happy to do some implementation. I was not easy to satisfy I guess
Last updated: Dec 20 2023 at 11:08 UTC