Zulip Chat Archive
Stream: general
Topic: contribute lemma to mathlib
Bernd Losert (Jan 13 2022 at 09:17):
Here is the lemma:
lemma ultrafilter_comap_set (f : X โ Y) (A : set X) (๐ฑ : ultrafilter Y) (h : f '' A โ ๐ฑ) :
โ ๐ฐ : ultrafilter X, ๐ฐ.map f = ๐ฑ โง A โ ๐ฐ := begin
-- ๐ฐ will be the ultrafilter generated by the filter base { (f โปยน' V โฉ A) | V โ ๐ฑ }
let Bโ : set X := fโปยน' (f '' A) โฉ A,
let โฌ : filter_basis X := {
sets := { (f โปยน' V โฉ A) | V โ ๐ฑ },
nonempty := โจBโ, f '' A, h, rflโฉ,
inter_sets := begin
assume B : set X,
assume B' : set X,
rintro โจV : set Y, hโ : V โ ๐ฑ, hโ : f โปยน' V โฉ A = Bโฉ,
rintro โจV' : set Y, hโ' : V' โ ๐ฑ, hโ' : f โปยน' V' โฉ A = B'โฉ,
let B'' := B โฉ B',
use B'',
split,
have h' : f โปยน' (V โฉ V') โฉ A = B'', from calc
f โปยน' (V โฉ V') โฉ A = (f โปยน' V โฉ f โปยน' V') โฉ A : by rw set.preimage_inter
... = (f โปยน' V โฉ A) โฉ (f โปยน' V' โฉ A) : by {ext, simp, tauto}
... = B โฉ B' : by rw [hโ, hโ'],
exact โจV โฉ V', ๐ฑ.inter_sets hโ hโ', h'โฉ,
simp [set.subset.rfl],
end,
},
let โฑ := โฌ.filter,
have : A โ โฑ := โฌ.mem_filter_iff.mpr โจBโ, โจf '' A, h, rflโฉ, set.inter_subset_right (fโปยน' (f '' A)) Aโฉ,
haveI : โฑ.ne_bot := begin
rw โฌ.has_basis.ne_bot_iff,
assume B : set X,
rintro โจV : set Y, hโ : V โ ๐ฑ, hโ : f โปยน' V โฉ A = Bโฉ,
rw (set.inter_comm (fโปยน' V) A) at hโ,
rw โ hโ,
unfold id,
rw โ set.image_inter_nonempty_iff,
have : f '' A โฉ V โ ๐ฑ, from ๐ฑ.inter_sets h hโ,
exact ultrafilter.nonempty_of_mem this,
end,
let ๐ฐ := ultrafilter.of โฑ,
use ๐ฐ,
split,
show ๐ฐ.map f = ๐ฑ, begin
have : โ (V : set Y), V โ ๐ฑ โ f โปยน' V โ โฑ, begin
assume V : set Y,
assume hV : V โ ๐ฑ,
rw filter_basis.mem_filter_iff โฌ,
let B := f โปยน' V โฉ A,
use B,
split,
show B โ โฌ, from โจV, hV, rflโฉ,
show B โ f โปยน' V, from set.inter_subset_left (f โปยน' V) A,
end,
have : map f ๐ฐ โค ๐ฑ, from calc
map f ๐ฐ โค map f โฑ : filter.map_mono (ultrafilter.of_le โฑ)
... โค ๐ฑ : by exact (filter.tendsto_def.mpr this),
have : map f ๐ฐ = ๐ฑ, from (๐ฑ.unique this),
exact_mod_cast this,
end,
show A โ ๐ฐ, from filter.le_def.mp (ultrafilter.of_le โฑ) A (by assumption : A โ โฑ),
end
I couldn't find anything like this in mathlib. I thought proving it would be simple, but it turned out not to be the case. I was thinking about contributing this since it is a non-trivial result. Any thoughts? Should I just open a PR?
Eric Wieser (Jan 13 2022 at 09:22):
Can you promote that from an existential to a construction?
Eric Wieser (Jan 13 2022 at 09:25):
That is, something like
def ultrafilter_comap_set (f : X โ Y) (A : set X) (๐ฑ : ultrafilter Y) (h : f '' A โ ๐ฑ) :
ultrafilter X
lemma map_ultrafilter_comap_set (f : X โ Y) (A : set X) (๐ฑ : ultrafilter Y) (h : f '' A โ ๐ฑ) :
(ultrafilter_comap_set f A๐ฑ h).map f = ๐ฑ
lemma mem_ultrafilter_comap_set (f : X โ Y) (A : set X) (๐ฑ : ultrafilter Y) (h : f '' A โ ๐ฑ) :
A โ ultrafilter_comap_set f A๐ฑ h
Bernd Losert (Jan 13 2022 at 09:42):
Yes I can.
Bernd Losert (Jan 13 2022 at 09:43):
I know I will also need to change the variable names to fit the style of order.filter.ultrafilter. Also, I'm not sure about the name of this thing.
Patrick Massot (Jan 13 2022 at 10:05):
I think this proof is needlessly complicated, I'll try to find time to write a shorter proof.
Bernd Losert (Jan 13 2022 at 10:11):
Yeah, my tactic skills (or lack thereof) shows.
Patrick Massot (Jan 13 2022 at 12:45):
This is not a question of tactic skills. You need to get used to how much structure we use on filters, especially push-forward, pull-back, inf and sup. And of course you need to know the library.
import order.filter.bases order.filter.ultrafilter
open filter set
open_locale filter
variables {X Y : Type*}
lemma ultrafilter.eq_of_le {a b : ultrafilter X} (h : (a : filter X) โค b) : a = b :=
ultrafilter.coe_injective (b.unique h)
open ultrafilter
lemma ultrafilter_comap_set {f : X โ Y} {A : set X} {V : ultrafilter Y} (h : f '' A โ V) :
โ ๐ฐ : ultrafilter X, ๐ฐ.map f = V โง A โ ๐ฐ :=
begin
haveI : (comap f V โ ๐ A).ne_bot := comap_inf_principal_ne_bot_of_image_mem V.ne_bot h,
refine โจof (comap f V โ ๐ A), _, of_le _ (mem_inf_of_right $ mem_principal_self A)โฉ,
apply eq_of_le,
calc filter.map f (of (comap f V โ ๐ A))
โค map f (comap f V โ ๐ A) : map_mono (of_le _)
... โค (map f $ comap f V) โ map f (๐ A) : map_inf_le
... = (map f $ comap f V) โ (๐ $ f '' A) : by rw map_principal
... โค V โ (๐ $ f '' A) : inf_le_inf_right _ map_comap_le
... = V : inf_of_le_left (le_principal_iff.mpr h)
end
Patrick Massot (Jan 13 2022 at 12:46):
As you can see, the core of the proof is a computation using all the structure I mentioned. As far as I know, this doesn't exist anywhere else (it certainly doesn't exist in Bourbaki).
Bernd Losert (Jan 13 2022 at 17:00):
I did try to make it work with comap f V โ ๐ A
, but I ran into problems when I wanted to show the equality ๐ฐ.map f = V
, so I tried a different approach.
I have not used the refine
tactic yet, so I'm not sure what magic it does.
Bernd Losert (Jan 13 2022 at 17:03):
And of course you need to know the library.
Yeah, this is a major issue I have.
Kyle Miller (Jan 13 2022 at 17:06):
@Bernd Losert refine
is like exact
but any holes you leave (the underscores) become new goals if Lean doesn't otherwise figure them out for you.
Kyle Miller (Jan 13 2022 at 17:08):
For example, say f
has two arguments, then apply f
and refine f _ _
have a similar effect.
Bernd Losert (Jan 13 2022 at 17:08):
Ah, that's interesting.
Kyle Miller (Jan 13 2022 at 17:12):
It's also useful for using custom induction lemmas since it gives you more control (and also the induction
tactic seems to have some longstanding bugs...). Setting up strong induction, for example: refine nat.strong_induction_on n (ฮป n' ih, _)
Patrick Massot (Jan 13 2022 at 18:08):
refine
isn't doing anything fancy here. You could write the exact same proof as:
lemma ultrafilter_comap_set {f : X โ Y} {A : set X} {V : ultrafilter Y} (h : f '' A โ V) :
โ ๐ฐ : ultrafilter X, ๐ฐ.map f = V โง A โ ๐ฐ :=
begin
haveI : (comap f V โ ๐ A).ne_bot := comap_inf_principal_ne_bot_of_image_mem V.ne_bot h,
use of (comap f V โ ๐ A),
split,
{ apply eq_of_le,
calc filter.map f (of (comap f V โ ๐ A)) โค map f (comap f V โ ๐ A) : map_mono (of_le _)
... โค (map f $ comap f V) โ map f (๐ A) : map_inf_le
... = (map f $ comap f V) โ (๐ $ f '' A) : by rw map_principal
... โค V โ (๐ $ f '' A) : inf_le_inf_right _ map_comap_le
... = V : inf_of_le_left (le_principal_iff.mpr h) },
{ exact of_le _ (mem_inf_of_right $ mem_principal_self A) }
end
Patrick Massot (Jan 13 2022 at 18:09):
If anything is fancy here then it's the anonymous constructor notation (the angle brackets) and its convenient "associativity".
Patrick Massot (Jan 13 2022 at 18:11):
refine
is a really fundamental tactic. In a very real sense, every tactic is a special case of refine
.
Bernd Losert (Jan 13 2022 at 18:14):
I don't remember seeing refine
in "Theorem proving with Lean". Most of my tactic knowledge comes from there.
Adam Topaz (Jan 13 2022 at 18:14):
Isn't refine
the only tactic that exists in agda? (This is only partially a joke)
Bernd Losert (Jan 13 2022 at 18:15):
The only tactic I know of in Agda is rewrite
.
Arthur Paulino (Jan 13 2022 at 18:16):
I'm not aware of how updated this list is, but https://leanprover-community.github.io/mathlib_docs/tactics.html contains a more complete set of possibilities available in mathlib
(tip: it's a bit overwhelming, so don't worry too much)
Adam Topaz (Jan 13 2022 at 18:17):
@Bernd Losert refine
is the analogue of agda's C-c C-r
(in emacs)
Bernd Losert (Jan 13 2022 at 18:19):
Ah, I don't use emacs with Agda, so no clue.
Adam Topaz (Jan 13 2022 at 18:19):
Is it possible to write agda without emacs?
Bernd Losert (Jan 13 2022 at 18:20):
Of course. I'm also writing Lean without emacs or vscode.
Bernd Losert (Jan 13 2022 at 18:20):
I use vim.
Adam Topaz (Jan 13 2022 at 18:21):
" C-c C-r -> \r
nnoremap <buffer> <LocalLeader>r :call AgdaRefine("False")<CR>
nnoremap <buffer> <LocalLeader>R :call AgdaRefine("True")<CR>
Bernd Losert (Jan 13 2022 at 18:23):
:)
My vim setup is very plain. I just run agda
on the files, so no hole-driven development for me.
Julian Berman (Jan 13 2022 at 18:25):
Bernd Losert said:
I use vim.
Are you using lean.nvim or doing something similarly barebones for lean
Bernd Losert (Jan 13 2022 at 21:26):
Barebones. I basically run !lean %
to type check the file I'm working on.
Julian Berman (Jan 13 2022 at 21:30):
Brave.
Yakov Pechersky (Jan 13 2022 at 21:32):
By doing so, you've really decreased the "interactive" in interactive theorem prober
Yakov Pechersky (Jan 13 2022 at 21:32):
Because you've lost the ability to see the goal automatically at different places in your tactic proofs
Yakov Pechersky (Jan 13 2022 at 21:33):
So you're basically limited to constructing proofs that are basically the equivalent of term mods proofs with holes.
Yakov Pechersky (Jan 13 2022 at 21:34):
Which is now explains why your proof at the beginning of the thread is the way it is
Bernd Losert (Jan 13 2022 at 21:37):
Yes. I tried using vscode (again). The feedback in goals is super fast. Unfortunately, vscode has the bad habit of dying on me and sending all my cpus to 100%.
Bernd Losert (Jan 13 2022 at 21:37):
It's also a giant time sink to be honest.
Bernd Losert (Jan 13 2022 at 21:38):
I used to use emacs before too. But after I while I also realized it was a giant time sink.
Adam Topaz (Jan 13 2022 at 21:39):
Have you tried doom emacs? It works pretty much right out of the box.
Adam Topaz (Jan 13 2022 at 21:39):
Bernd Losert said:
Barebones. I basically run
!lean %
to type check the file I'm working on.
This is definitely not recommended.
Bernd Losert (Jan 13 2022 at 21:43):
Never tried doom emacs. Maybe I'll give it a try again at some point. I'm doing fine with vim at the moment.
Julian Berman (Jan 13 2022 at 22:12):
Any reason you're not using lean.nvim I suppose I should ask (even just for feedback)?
Julian Berman (Jan 13 2022 at 22:13):
Is it because you're using vanilla vim?
Bernd Losert (Jan 13 2022 at 22:17):
Correct.
Julian Berman (Jan 13 2022 at 22:22):
Seems like switching to neovim should be easier than switching to emacs :) -- but even if you stick with your current thing, even just using coc
is going to be lots more fun than !lean %
, but it's your party.
Bernd Losert (Jan 13 2022 at 22:23):
What's coc?
Julian Berman (Jan 13 2022 at 22:24):
An LSP client (one that works for vanilla vim).
Bernd Losert (Jan 13 2022 at 22:28):
Ah, OK. There was a time when I used neovim with LSP and all sorts of customizations. But at some point I realized that the benefit I got from it was marginal and started reverting to a more simpler setup. I work as developer, so I've gone through a couple of IDEs. And I've always abandoned them for vim in the end.
Julian Berman (Jan 13 2022 at 22:30):
Sure, I understand, I hate IDEs and don't use anything but (neo)vim :), but I've never done so quite as barebones as you're doing now, I think it'd get quite annoying to me not seeing the goal state.Do what makes you happy (says the guy who ignored people who said lean doesn't support vim and did it anyway :P) but you may try something else for a bit just to see what you're missing from Lean, and then if you're really happy the way you were you go back. And in a biased way I'd try lean.nvim as the closest to what you're doing now :P.
Julian Berman (Jan 13 2022 at 22:31):
Or watch the demo at least maybe -- https://github.com/Julian/lean.nvim/ it's out of date, things look nicer now, but it's close to what my setup looks like.
Bernd Losert (Jan 13 2022 at 23:10):
Looks pretty good. Thanks for suggesting it. The only thing that would annoy me is the pop-ups. I've grown to dislike pop-ups.
Julian Berman (Jan 13 2022 at 23:13):
None of that is required (and in my own setup, no popups at this point happen automatically, they're all key driven).
Arthur Paulino (Jan 14 2022 at 03:02):
The discussion has turned into a talk about text editors (someone might split this thread). So I am going to say something: learning vim/neovim is an old dream of mine and I'd love to learn how to use it with Lean :smiley:
Bernd Losert (Jan 14 2022 at 10:40):
@Eric Wieser : I have broken-up the proof by @Patrick Massot like this:
namespace ultrafilter
lemma eq_of_le {f g : ultrafilter ฮฑ} (h : (f : filter ฮฑ) โค g) : f = g :=
coe_injective (g.unique h)
def comap_inf_principal_ne_bot_of_image_mem {m : ฮฑ โ ฮฒ} {s : set ฮฑ} {g : ultrafilter ฮฒ} (h : m '' s โ g) : (filter.comap m g โ ๐ s).ne_bot :=
filter.comap_inf_principal_ne_bot_of_image_mem g.ne_bot h
noncomputable def of_comap_inf_principal {m : ฮฑ โ ฮฒ} {s : set ฮฑ} {g : ultrafilter ฮฒ} (h : m '' s โ g) : ultrafilter ฮฑ :=
begin
haveI : (filter.comap m g โ ๐ s).ne_bot := comap_inf_principal_ne_bot_of_image_mem h,
exact of (filter.comap m g โ ๐ s)
end
def of_comap_inf_principal_ultrafilter_mem {m : ฮฑ โ ฮฒ} {s : set ฮฑ} {g : ultrafilter ฮฒ} (h : m '' s โ g) : s โ of_comap_inf_principal h :=
begin
haveI : (filter.comap m g โ ๐ s).ne_bot := comap_inf_principal_ne_bot_of_image_mem h,
have : s โ filter.comap m g โ ๐ s := mem_inf_of_right (mem_principal_self s),
exact filter.le_def.mp (of_le _) s this
end
def of_comap_inf_principal_ultrafilter_eq_of_map {m : ฮฑ โ ฮฒ} {s : set ฮฑ} {g : ultrafilter ฮฒ} (h : m '' s โ g) : (of_comap_inf_principal h).map m = g :=
begin
haveI : (filter.comap m g โ ๐ s).ne_bot := comap_inf_principal_ne_bot_of_image_mem h,
apply eq_of_le,
calc filter.map m (of (filter.comap m g โ ๐ s)) โค filter.map m (filter.comap m g โ ๐ s) : filter.map_mono (of_le _)
... โค (filter.map m $ filter.comap m g) โ filter.map m (๐ s) : filter.map_inf_le
... = (filter.map m $ filter.comap m g) โ (๐ $ m '' s) : by rw filter.map_principal
... โค g โ (๐ $ m '' s) : inf_le_inf_right _ filter.map_comap_le
... = g : inf_of_le_left (le_principal_iff.mpr h)
end
end ultrafilter
One thing that bugs me is that I have to keep repeating the haveI
part. Is there a way to prevent this?
If anyone has any other feedback about this code, please let me know.
Eric Wieser (Jan 14 2022 at 11:00):
Most of those def
s should be lemmas, only the actual construction (of_comap_inf_principal
) should be a def
. I'm not familiar with this part of mathlib so can't really advise much beyond that.
Bernd Losert (Jan 14 2022 at 11:04):
Ah, good point.
Bernd Losert (Jan 14 2022 at 11:11):
Any thoughts on the haveI
issue?
Eric Wieser (Jan 14 2022 at 11:13):
There might be a trick - this works, but is uglier than what you have:
lemma of_comap_inf_principal_ultrafilter_mem {m : ฮฑ โ ฮฒ} {s : set ฮฑ} {g : ultrafilter ฮฒ} (h : m '' s โ g) : s โ of_comap_inf_principal h :=
begin
have : s โ filter.comap m g โ ๐ s := mem_inf_of_right (mem_principal_self s),
exact filter.le_def.mp (@of_le _ _ (id _)) s this, -- find the instance by unification rather than typeclass search
end
There might be a nicer way to spell that.
Bernd Losert (Jan 14 2022 at 11:57):
I made a PR: https://github.com/leanprover-community/mathlib/pull/11445.
Last updated: Dec 20 2023 at 11:08 UTC