Zulip Chat Archive

Stream: new members

Topic: exists.intro with two 'arguments'


Kevin Doran (Mar 26 2020 at 03:42):

Hello, Lean masters.

I'm struggling to see how to complete the below term-style proof. My main issue is that exists.intro is expecting something like exists (x : A) (y : B), <expression>, whereas I have only seen examples of how to work with exists (x : A), <expression>.

The problem areas are marked as ① and ② in the below snipped.

import data.real
import data.set

local notation `|`x`|` := abs x


def is_adherent (x : ) (X : set ) : Prop :=  ε > 0, y  X, |x - y| < ε

def closure(X : set ) : set  := {x :  | is_adherent x X }


lemma self_adherence1 (X : set  ) : X  closure(X) :=
begin
   intros x h₁ ε h₂,
   apply exists.intro x,
   norm_num,
   apply and.intro h₁ h₂
end


-- Question: what to do at ① and ② below?
lemma self_adherence2 (X : set  ) : X  closure(X) :=
   assume x,
   assume h₁ : x  X,
   have adh: is_adherent x X, from
       assume ε,
       assume h₂ : ε > 0,
       have h₃ : |x - x| < ε, from sorry  -- ①
       exists.intro x <what goes here?>,  -- ②
   show x  closure(X), from adh

Any other tips or comments greatly welcomed.

Mario Carneiro (Mar 26 2020 at 03:53):

exists (x : A) (y : B), <expression> is shorthand for exists (x : A), exists (y : B), <expression>

Kevin Doran (Mar 26 2020 at 03:55):

Ah, perfect, thanks!

Kevin Doran (Mar 26 2020 at 04:27):

My confidence was premature. How would I apply that to this situation. My attempt: have h2 : exists.intro x (exists.intro h₁ h₃),
My confusion possibly lies in the fact that in exists (x : A), exists (y : B), <expression>, y:B is a function λy. y∈X.

Kenny Lau (Mar 26 2020 at 04:30):

∃y ∈ X is shorthand for ∃ (y : _), ∃ (H: y ∈ X)

Kenny Lau (Mar 26 2020 at 04:33):

import data.real.basic
import data.set

local notation `|`x`|` := abs x


def is_adherent (x : ) (X : set ) : Prop :=
 ε > 0,  y  X, |x - y| < ε

def closure (X : set ) : set  :=
{ x :  | is_adherent x X }

lemma self_adherence1 ( X : set ) : X  closure(X) :=
λ x h₁ ε h₂, x, h₁, by convert h₂; norm_num

-- Question: what to do at ① and ② below?
lemma self_adherence2 (X : set  ) : X  closure(X) :=
assume x,
assume h₁ : x  X,
have adh: is_adherent x X, from
  assume ε,
  assume h₂ : ε > 0,
  have h₃ : |x - x| < ε, by convert h₂; norm_num,  -- ①
  exists.intro x (exists.intro h₁ h₃),  -- ②
show x  closure(X), from adh

Kevin Doran (Mar 26 2020 at 04:41):

Working now! Thanks.

Mario Carneiro (Mar 26 2020 at 04:54):

it is vexing to me that you use norm_num when there are no numbers involved

Kenny Lau (Mar 26 2020 at 04:59):

I'm quite surprised that convert worked

Kevin Buzzard (Mar 26 2020 at 13:31):

Is it because gt is reducible?

Kevin Buzzard (Mar 26 2020 at 13:33):

Mario Carneiro said:

it is vexing to me that you use norm_num when there are no numbers involved

You should just be using a simpset which doesn't mention abs, if abs 0 = 0 is not part of your remit.

Kevin Buzzard (Mar 26 2020 at 13:34):

Or is that current research? :-/

Kevin Doran (Mar 27 2020 at 02:13):

Continuing with the same example, I had two queries related to the updated snippet (no more norm_num :slight_smile: ):

lemma self_adherence (X : set  ) : X  closure(X) :=
begin
 intros x h₁ ε h₂,  -- where h₂ : ε > 0
 apply exists.intro x,
 apply exists.intro h₁,   -- ⊢|x - x| < ε
 simp,  -- ⊢ 0 <  ε
 exact h₂,  -- ⊢ true
end
  1. The simp tactic utilizes properties of the reals. Where can one find this property? How could I use it manually without using simp?
  2. exact h₂ works without having to specify how > relates to <. Where can one find the details of this?

Bryan Gin-ge Chen (Mar 27 2020 at 02:20):

  1. You can use squeeze_simp to see (most of) the lemmas that simp used. For an even more detailed look, try putting set_option trace.simplify.rewrite true before the lemma. There's lots of info on simp and how it works here.

Bryan Gin-ge Chen (Mar 27 2020 at 02:32):

  1. Here's where > is defined in the core library. Note that it's defined in terms of has_lt.lt, and < is just notation for that. Generally, Lean does a decent job of identifying when terms are identical up to unfolding definitions so it's not a huge surprise that exact works. In this case, the fact that gt is marked reducible makes the definition even more transparent, see 6.1 in TPiL. Sometimes you have to explicitly use change first to turn one term into a more convenient form. I'm not really sure precisely what causes Lean not to recognize two terms as "the same" in those cases (and it's probably different things in different situations).

Kevin Doran (Mar 27 2020 at 03:03):

Those are the revelations I needed, thanks!

Mario Carneiro (Mar 27 2020 at 03:47):

@Gabriel Ebner @Simon Hudon This reminds me of our conversation about when it would make a difference if > was a notation. This is a simplified version of the example here:

example {ε : } (h : ε > 0) : 0 < ε := by simp [h] -- fails
example {ε : } (h : 0 < ε) : 0 < ε := by simp [h] -- ok
example {ε : } (h : 0 < ε) : ε > 0 := by simp [h] -- fails

Mario Carneiro (Mar 27 2020 at 03:49):

Here's a variant using rw, with the same results:

example {ε : } (h : ε > 0) : 0 < ε := by rw [eq_true_intro h]; trivial -- fails
example {ε : } (h : 0 < ε) : 0 < ε := by rw [eq_true_intro h]; trivial -- ok
example {ε : } (h : 0 < ε) : ε > 0 := by rw [eq_true_intro h]; trivial -- fails

Mario Carneiro (Mar 27 2020 at 03:54):

As noted by kenny, because of head symbol matching this is a bit different if the gt is not at the head:

example {ε : } (h : ¬ ε > 0) : ¬ 0 < ε := by simp [h] -- fails
example {ε : } (h : ¬ 0 < ε) : ¬ 0 < ε := by simp [h] -- ok
example {ε : } (h : ¬ 0 < ε) : ¬ ε > 0 := by simp [h] -- fails
example {ε : } (h : ¬ ε > 0) : ¬ 0 < ε := by rw [eq_true_intro h]; trivial -- ok
example {ε : } (h : ¬ 0 < ε) : ¬ 0 < ε := by rw [eq_true_intro h]; trivial -- ok
example {ε : } (h : ¬ 0 < ε) : ¬ ε > 0 := by rw [eq_true_intro h]; trivial -- ok

Kenny Lau (Mar 27 2020 at 03:56):

interesting!

Mario Carneiro (Mar 27 2020 at 03:58):

one more for good measure:

example {ε : } (h : ε > 0) : 0 < ε := by simp [h, gt] -- fails
example {ε : } (h : 0 < ε) : 0 < ε := by simp [h, gt] -- ok
example {ε : } (h : 0 < ε) : ε > 0 := by simp [h, gt] -- ok

Mario Carneiro (Mar 27 2020 at 03:58):

still, the point is that we don't want to explicitly be unfolding gt


Last updated: Dec 20 2023 at 11:08 UTC