Zulip Chat Archive

Stream: new members

Topic: Proof by contradiction


Trevor Fancher (Sep 21 2021 at 14:54):

Sometimes the by_contradiction tactic doesn't work that way I would expect. For example,

theorem cofinite_not_principal :  S : set , cofinite   𝓟 S :=
begin
  intro S,
  by_contradiction,
end

fails with the message

tactic by_contradiction failed, target is not a proposition

(even if I remove the intro S line). One way I've come up with to deal with these situations is to do the following

theorem cofinite_not_principal :  S : set , cofinite   𝓟 S :=
begin
  intro S,
  have t : true := trivial,
  contrapose! t,
  rw not_true,
  -- Now I'm in the state that I would expect by_contradiction to put me in after the "intro S" line.
end

I'm still new to the game of formalizing proofs, but it seems by_contradiction should do this for me.

Am I just missing some another technique/tactic/way of using by_contradiction?

p.s. I would also expect contrapose! to turn ¬true to false.

p.p.s. To get a MWE just add the following above the theorem

MWE header

Riccardo Brasca (Sep 21 2021 at 14:56):

If your goal is a ≠ b you should start with intro h, and the goal will be false.

Riccardo Brasca (Sep 21 2021 at 14:57):

With h : a = b. There is no need of using a proof by contradiction here, since a ≠ b is defined as a = b → false .

Riccardo Brasca (Sep 21 2021 at 14:58):

(We all made this mistake I think, so you are in a good position :grinning_face_with_smiling_eyes: )

Ruben Van de Velde (Sep 21 2021 at 14:59):

intro h works, but why doesn't by_contra? Even more oddly, rw ne.def, by_contra h, works

Trevor Fancher (Sep 21 2021 at 15:01):

Ok. Thanks for enlightening me. I would never think to call intro when proving a ≠ b. Well, except now that I've been enlightened :)

Riccardo Brasca (Sep 21 2021 at 15:02):

It's a decidablity issue, adding open_locale classical should also work.

Trevor Fancher (Sep 21 2021 at 15:05):

I can confirm that

open_locale classical
theorem cofinite_not_principal :  S : set , cofinite   𝓟 S :=
begin
  intro S,
  by_contradiction,
end

does exactly what I expect. Is there a downside to that open_locale classical line?

Riccardo Brasca (Sep 21 2021 at 15:06):

If you want to do "standard" mathematics no. But if you are interested in constructive logic and/or computability then yes, but I will let someone else more experienced than me clarify this.

Trevor Fancher (Sep 21 2021 at 15:07):

Fair enough. Thanks :)

Riccardo Brasca (Sep 21 2021 at 15:09):

And beware that if at some point you want to PR this in mathlib the constructivist mafia will force you to use intro h here.

Riccardo Brasca (Sep 21 2021 at 15:11):

BTW, the issue here is probably very simple: without excluded middle you cannor prove ¬¬p → p, so ¬(a ≠ b) does not imply a = b.

Trevor Fancher (Sep 21 2021 at 15:15):

I'm fine with doing intro h and I would guess most people would be (other than for disoverability). I have no idea if it's possible or a good idea for by_contradiction to detect this situation and then just do intro h, but it seems reasonable. Because the state after intro S, intro h in the above theorem is exactly what I, in my limited understanding, would mean when I say "lets now prove this by contradiction".

Riccardo Brasca (Sep 21 2021 at 15:19):

I think that a good lesson to learn here is to realize that you never asked yourself what ¬h actually means. Of course we are very happy with the standard vague idea, and we can do a lot of beautiful math with it, but in Lean even very basic things must be defined (we even have a definition of =). ¬h is defined as h → false, so if the goal is an implication it is natural to start with intro.

Trevor Fancher (Sep 21 2021 at 15:27):

I agree. It is nice to know how ¬h is defined now. by_contradiction not handling this is overall a relatively minor pain point. But, from my couple weeks with lean now, it does seem like there are a lot of these minor pain points that seem like they would be easy to fix.

Trevor Fancher (Sep 21 2021 at 15:27):

Of course, seeming easy to fix is quite different than actually being easy :)

Riccardo Brasca (Sep 21 2021 at 15:30):

This is a feature, not a problem. It's a feature you are not interested if you want to do mathematics, and it becomes annoying in that case, but it is needed to do other things. If you want to forget about all of this, just start all files with

open_locale classical
noncomputable theory

and you are doing classical mathematics.

Trevor Fancher (Sep 21 2021 at 15:33):

Would a decent compromise being for by_contradiction to detect this situation and, rather than fail with a mysterious message (it was mysterious to me), to print out a few of the suggestions you just made? That would have saved me and probably other people some time.

Riccardo Brasca (Sep 21 2021 at 15:35):

My suggestion is already in the documentation.

Eric Wieser (Sep 21 2021 at 15:38):

Trevor, what version of lean are you using?

Trevor Fancher (Sep 21 2021 at 15:38):

I maintain that the error message "target is not a proposition" is not as actionable as it could be. But your point that I should read the documentation is well taken.

Ruben Van de Velde (Sep 21 2021 at 15:39):

This is the thing that doesn't make sense to me:

theorem cofinite_not_principal :  S : set , cofinite   𝓟 S :=
begin
  intro S,
  by_contra h, -- fails
  sorry,
end

theorem cofinite_not_principal' :  S : set , ¬(cofinite  = 𝓟 S) :=
begin
  intro S,
  by_contra h, -- works
  sorry,
end

Trevor Fancher (Sep 21 2021 at 15:42):

@Eric Wieser leanprover-community/lean:3.24.0. I'm working the Formalising Mathematics exercises and thats the version leanproject pulled.

Kyle Miller (Sep 21 2021 at 15:44):

I'd thought by_contra automatically used classical decidability instances since by_cases now does, but it seems like it doesn't.

Also, I found a way to get by_contra to infinite loop somehow:

example (α : Type*) (x y : α) : x  y :=
begin
  by_contra,
  -- never gets here
end

Eric Wieser (Sep 21 2021 at 15:45):

src#tactic.by_contradiction: it does

Kyle Miller (Sep 21 2021 at 15:46):

(By the way, the tactic#classical tactic is a way to do open_locale classical from within a tactic proof.)

Eric Wieser (Sep 21 2021 at 15:47):

This is clearly just a bug: by_contradiction is already programmed to do intro when presented with a not, it just doesn't know how to do it for an ne

Kyle Miller (Sep 21 2021 at 15:48):

@Eric Wieser It seems that I'm accidentally using an old version of Lean when I was poking through the source -- by_contra has been able to use classical decidable instances for 11 months now. https://github.com/leanprover-community/lean/commit/af2f0be9958b0bc390698ffad434c2305ab40e0f

Eric Wieser (Sep 21 2021 at 15:49):

So there are two bugs here:

  • by_contra is somehow not finding the classical instance needed to introduce h : ¬(x ≠ y)
  • the heuristic intended to just do intro on goals of the form ¬P (giving the more useful h : x = y) does not look through semireducible defs

Trevor Fancher (Sep 21 2021 at 15:52):

It certainly felt like a bug. I'm glad it's been surfaced and now I'll let the experts handle it :)

Riccardo Brasca (Sep 21 2021 at 15:53):

Wow, wonderful!

Riccardo Brasca (Sep 21 2021 at 15:53):

I mean, wonderful that we realize we have a problem.

Eric Wieser (Sep 21 2021 at 15:55):

The first bullet is easy to fix with:

meta def by_contradiction2 (H : name) : tactic expr :=
do tgt  target,
  (match_not tgt $> ()) <|>
  (mk_mapp `decidable.by_contradiction [some tgt, none] >>= eapply >> skip) <|>
  (mk_mapp `classical.by_contradiction [some tgt] >>= eapply >> skip) <|>
  fail "tactic by_contradiction failed, target is not a proposition",
  intro H

Ruben Van de Velde (Sep 21 2021 at 15:57):

"target is not a proposition" is still a weird error message, at least in this case

Eric Wieser (Sep 21 2021 at 16:05):

https://github.com/leanprover-community/lean/pull/622

Yaël Dillies (Sep 21 2021 at 16:05):

About not true being replaced by false, true is a type with one element, trivial, and false is a type with no element. So you can prove not true iff false by doing \la h, h trivial one way and \la h, h.elim rhe other way. But you see that this is much less natural than you would have expected, which indicates that this is not really the way we do maths in Lean.

Kyle Miller (Sep 21 2021 at 16:09):

I've never really understood why ne is behind a definition:

@[reducible] def ne {α : Sort u} (a b : α) := ¬(a = b)
infix `  `:50 := ne

Couldn't it have been this, saving some of these headaches?

notation a `  `:50 b := ¬ a = b

Eric Wieser (Sep 21 2021 at 16:09):

Then docs#ne.symm wouldn't work with dot notation

Eric Wieser (Sep 21 2021 at 16:10):

I'd argue the headache is that the tactic forgot to look through reducible definitions

Kyle Miller (Sep 21 2021 at 16:12):

Seems fine to me, so long as you're ok with it being in the not namespace:

local notation a `  `:50 b := ¬ a = b

def not.symm {α : Type*} {x y : α} (h : x  y) : y  x :=
λ h', h h'.symm

example (α : Type*) (x y : α) (h : y  x) : x  y :=
begin
  exact h.symm,
end

Kyle Miller (Sep 21 2021 at 16:14):

I've run into ne not being literally not-equals a number of times. There's always a simple workaround, but it's a bit of annoying friction.

Kyle Miller (Sep 21 2021 at 16:18):

This reminds me that there's no docs#not.def. The difference between not and implies-false is another small annoyance (¬p → false seems like it should simp to p)

Eric Wieser (Sep 21 2021 at 16:18):

Why would there need to be a not.def?

Eric Wieser (Sep 21 2021 at 16:18):

Instead of rw not.def you can use rw not

Kyle Miller (Sep 21 2021 at 16:19):

It's to rewrite it backwards

Kyle Miller (Sep 21 2021 at 16:20):

It would let you normalize all the implies-falses in an expression into nots, which have a lot more simp lemmas about them.

Eric Wieser (Sep 21 2021 at 16:21):

Hmm, there's something weird there.. I'll make a new thread

Kyle Miller (Sep 21 2021 at 16:21):

lemma not.def (p : Prop) : ¬p  (p  false) := iff.rfl

example (p : Prop) (h : p) : ¬p  false :=
begin
  simp [not.def],
  exact h
end

Eric Wieser (Sep 21 2021 at 16:23):

My point was that not should have an equation lemma so that you can just do simp [←not]. It is missing.

Kyle Miller (Sep 21 2021 at 16:33):

Does simp actually work that way though for definitions?

namespace foo

def not (p : Prop) := p  false
local prefix `¬`:40 := not

example (p : Prop) (h : p) : ¬p  false :=
begin
  simp [not],
  -- p : Prop,
  -- h : p
  -- ⊢ (p → false) → false
end

end foo

(rw [←not] does the expected thing, however.)

Eric Wieser (Sep 21 2021 at 16:37):

Ah, perhaps I was thinking of rw

Eric Wieser (Sep 21 2021 at 16:39):

That looks like a bug to me, simp has clearly done the opposite of what we asked it to

Eric Wieser (Sep 21 2021 at 16:58):

Filed as https://github.com/leanprover-community/lean/issues/623, I'm not familiar enough with the simplifier to debug that one

Arthur Paulino (Nov 10 2021 at 01:57):

I have hf: fintype.card α' = 0 and hn: nonempty α'. How do I use those to prove false?

(library_search couldn't help me)

Eric Rodriguez (Nov 10 2021 at 02:11):

docs#fintype.card_pos_iff :)

Arthur Paulino (Nov 10 2021 at 02:22):

Thanks!!

Johan Commelin (Nov 10 2021 at 04:39):

docs#fintype.card_ne_zero

Johan Commelin (Nov 10 2021 at 04:39):

@Arthur Paulino Was that hn : nonempty α already there at the beginning of the proof?

Johan Commelin (Nov 10 2021 at 04:40):

I'm asking, because if you created it later, you need to reset the instance cache using the resetI, tactic.
Maybe resetI, library_search would have been able to find the fintype.card_ne_zero lemma.

Arthur Paulino (Nov 10 2021 at 12:49):

Nope, I had to "fabricate" it. Dunno the exact term. Create? Insert?

Arthur Paulino (Nov 10 2021 at 12:49):

Ah, state then prove

Yaël Dillies (Nov 10 2021 at 13:23):

Synthesize!

Arthur Paulino (Nov 10 2021 at 13:31):

I did use tactic.unfreeze_local_instances,


Last updated: Dec 20 2023 at 11:08 UTC