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 haveIpart. 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 defs 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