Zulip Chat Archive

Stream: general

Topic: classical.choice_elim


Kevin Buzzard (Sep 19 2019 at 13:13):

I had a brain freeze and couldn't remember the name of the function (∃ (x : α), p x) → α. I tried library_search:

example : Π {α : Sort u} {p : α → Prop}, (∃ (x : α), p x) → α := by library_search -- fails

but it failed! Is this a bug in library_search or is the issue that it's not a theorem? With my tail between my legs I opened up TPIL for the first time in a long time, and went to the section on choice. I learnt classical.choice : Π {α : Sort u_1}, nonempty α → α and thought "oh I can take it from here". I used library_search to discover
nonempty_of_exists : (∃ (x : α), p x) → nonempty αand so I just defined my function to be classical.choice (nonempty_of_exists h) and moved on.

A bit later I needed that the x provided by my function actually satisfied p x, and I took one look at that problem and thought "Oh -- that's classical.some_spec". I then remembered that the function I wanted but couldn't find was called classical.some and I figured that this would be no problem because the definition of classical.some will boil down to what I wrote. So I tried to prove this:

example (α : Type) (p : α → Prop) (h : (∃ (x : α), p x)) : classical.choice (nonempty_of_exists h) = classical.some h := sorry

with rfl, but it didn't work! Here is me unwinding what went wrong:

set_option pp.proofs true
example (α : Type) (p : α → Prop) (h : (∃ (x : α), p x)) : classical.choice (nonempty_of_exists h) = classical.some h :=
begin
  cases h with x hx,
  unfold classical.some,
  delta classical.indefinite_description,
  dsimp, -- changes the proofs!
  delta id_rhs,
  show classical.choice (nonempty_of_exists (Exists.intro x hx)) =
       (classical.choice (nonempty.intro (⟨x, hx⟩ : {d : α // p d}))).val,
  sorry
end

The problem is that classical.choice is not as functorial as you'd want -- I guess there is no eliminator. The goal ended up being

classical.choice (nonempty.intro x) = (classical.choice (nonempty.intro ⟨x, hx⟩)).val

This would be provable if we could prove this:

theorem classical.choice_elim (α : Type) (x : α) : classical.choice (nonempty.intro x) = x := sorry

But that can't be proved in Lean, right? Because classical.choice is just an axiom so you can't prove anything about it which doesn't boil down to rfl in some sense. But OTOH the result is true in some funny sense, because the VM mumble mumble I don't know what I'm talking about stores it as x internally but the kernel forgets it or something. This is at the boundary of my understanding (I don't really understand the VM or the kernel but perhaps this example would help me understand them).

I was wondering whether it would be "safe" to add classical.choice (nonempty.intro x) = x as an axiom to Lean? Or can it somehow already be proved and I'm barking up the wrong tree?

Keeley Hoek (Sep 19 2019 at 13:26):

axiom kevin_ax {α : Type} (x : α) : classical.choice (nonempty.intro x) = x

theorem uh_oh_kevin : false :=
begin
  have h₀ : classical.choice (nonempty.intro 0) = 0 := kevin_ax _,
  have h₁ : classical.choice (nonempty.intro 1) = 1 := kevin_ax _,
  have : 0 = 1 := eq.trans h₀.symm h₁,
  contradiction
end

Keeley Hoek (Sep 19 2019 at 13:27):

:(

Keeley Hoek (Sep 19 2019 at 13:29):

Propext makes the whole world blow up when you add kevin_ax

Kevin Buzzard (Sep 19 2019 at 13:35):

nooo!

Kevin Buzzard (Sep 19 2019 at 13:35):

but it's true in the VM!

Kevin Buzzard (Sep 19 2019 at 13:35):

or something

Keeley Hoek (Sep 19 2019 at 13:41):

Could you have this without a contradiction?

Keeley Hoek (Sep 19 2019 at 13:41):

axiom choice_commutes {α β : Type} (f : α  β) (h : nonempty α) : f (classical.choice h) = classical.choice (h.map f)

Keeley Hoek (Sep 19 2019 at 13:42):

I'm very afraid

Keeley Hoek (Sep 19 2019 at 13:52):

That's a negative

theorem uh_oh_keeley : false :=
begin
  let f : bool  bool := λ _, tt,
  let g : bool  bool := λ _, ff,
  let h : nonempty bool := nonempty.intro tt,

  have hv : f.map h = g.map h := rfl,

  have : tt = ff :=
    calc tt = f (classical.choice h) : by cases classical.choice h; refl
        ... = classical.choice (h.map f) : choice_commutes _ _
        ... = classical.choice (h.map g) : by rw hv
        ... = g (classical.choice h) : (choice_commutes _ _).symm
        ... = ff : by cases classical.choice h; refl,

  contradiction
end

Keeley Hoek (Sep 19 2019 at 13:52):

all hope is lost Kevin

Kevin Buzzard (Sep 19 2019 at 14:10):

I can't deal with concepts that aren't functorial.

Kevin Buzzard (Sep 19 2019 at 14:10):

you can imagine why the HoTT people have trouble with choice...

Reid Barton (Sep 19 2019 at 14:19):

I would argue you should "never" use choice

Reid Barton (Sep 19 2019 at 14:19):

where "never" = of course there are exceptions

Reid Barton (Sep 19 2019 at 14:20):

Except as unique_choice, which I guess only exists in my head

Kevin Buzzard (Sep 19 2019 at 14:25):

How do you avoid it in the following situation (a simplified version of where I was):

def next_to (a b : ) :=  d  ([-1, 1] : list ), a + d = b

lemma next_to.symm {a b : } (hab : next_to a b) :  next_to b a := ⟨-classical.some hab, sorry

Johan Commelin (Sep 19 2019 at 14:28):

I guess that falls under the category unique_choice.

Kevin Buzzard (Sep 19 2019 at 14:30):

I need to import Reid's head.

Chris Hughes (Sep 19 2019 at 14:37):

import tactic
def next_to (a b : ) :=  d  ([-1, 1] : list ), a + d = b

lemma next_to.symm {a b : } (hab : next_to a b) : next_to b a :=
by rcases hab with x, hx₁, hx₂⟩;
  exact ⟨-x, by simpa [eq_neg_iff_eq_neg, eq_comm, or_comm] using hx₁, by rw  hx₂; simp

Chris Hughes (Sep 19 2019 at 14:40):

Although I would be tempted to define next_to as

inductive next_to :     Prop
| left :  x, next_to x (x + 1)
| right :  x, next_to (x + 1) x

Reid Barton (Sep 19 2019 at 14:48):

You're proving a proposition and you only need to deconstruct a single existential, so as Chris says you don't need to use choice at all.

Kevin Buzzard (Sep 19 2019 at 15:12):

Regarding the definition: I went for that because that's what I was given in https://competition.isabelle.systems/competitions/contest/14/tasks/2/ . I agree another definition would be better for Lean.

Kevin Buzzard (Sep 19 2019 at 15:13):

I had a proof of what I needed in tactic mode and was considering transferring it to term mode, but then ran into this issue. I note that Chris' solution also uses tactics.

Keeley Hoek (Sep 19 2019 at 15:14):

Is there a way of defeating the GOAL notation by defining custom notation which includes a space in the token like GOAL so it doesn't clash with GOAL?

Mario Carneiro (Sep 19 2019 at 15:53):

I don't think you can declare notations with embedded spaces

Floris van Doorn (Sep 20 2019 at 04:01):

I'm quite sure that spaces in notation are only hints for the printer, not for the parser. For example + is declared with spaces around it, but writing a+b still parses correctly.

Floris van Doorn (Sep 20 2019 at 04:02):

notation `GOAL` := 0
notation ` GOAL ` := 1
example : GOAL = 2 := _

gives

ambiguous overload, possible interpretations
  1
  0

Keeley Hoek (Sep 20 2019 at 14:28):

Definitely user_command parsing respects spaces in the token name.

Keeley Hoek (Sep 20 2019 at 14:28):

I assumed the same would be the case for a custom user_notation. Perhaps it is not.


Last updated: Dec 20 2023 at 11:08 UTC