Zulip Chat Archive
Stream: general
Topic: Highest universe in mathlib
Yaël Dillies (Dec 24 2021 at 12:28):
We all know we have infinitely many universes in Lean, and basically everything is universe-polymorphic in mathlib, but how many universes do we really need currently? That is, to which point can we squish down all universes in mathlib and still have everything typecheck? Kevin argued that Type 1
was enough, but I suspect some constructions in category might something higher.
Johan Commelin (Dec 24 2021 at 12:29):
Certainly Type 2
will be good enough. I think that's what's needed for LTE.
Yaël Dillies (Dec 24 2021 at 14:05):
I must say I'm surprised we don't need anything higher!
Kevin Buzzard (Dec 24 2021 at 15:23):
I'm not. If you're a set theorist then you think everything is a set so everything's in Type. Definitions like ordinals and the category of all sets/groups etc are called classes, and they're all in Type 1.
Kevin Buzzard (Dec 24 2021 at 15:23):
Classically in mathematics you only deal with sets and classes
Kevin Buzzard (Dec 24 2021 at 15:27):
Remember that historically mathematicians didn't even need sets! People like Gauss and Euler worked with terms and functions. Then Galois came along and said "I think it would be helpful if we could identify the permutation group of +-sqrt(2) with the permutation group of +-sqrt(3) because I think I'm on to something". It was only around then that we started needing to talk about some kind of holder type G for a collection of terms g. So in some sense before that we didn't even need Type. We just had concrete examples like the naturals, reals and complexes
Kevin Buzzard (Dec 24 2021 at 15:28):
The push to develop abstract group theory and then abstract algebra and ideas like homomorphisms of groups are historically very recent. This is when people started to want to talk about maps between abstract types -- functions had of course existed for centuries before that but again only between concrete types such as the sine and cosine function on the reals
Kevin Buzzard (Dec 24 2021 at 15:30):
So types became "things" and then set theory gave a name to the things, but Russell's paradox showed that you had to be careful so then we got things like ZFC and at that point mathematics was going on purely within Type.
Kevin Buzzard (Dec 24 2021 at 15:32):
Then category theory and homological algebra and Grothendieck came along and then people wanted to start talking about abstract mathematical objects such as the category of all sets, which weren't even in Type. Grothendieck fixed this by adding an extra axiom to set theory to enable more than one universe of types, or of sets as he called them, and even then you don't need infinitely many, you just need to allow yourself to do the occasional universe bump. Hence Type 1
Mario Carneiro (Dec 24 2021 at 20:10):
I've been curious about this question for a while, so I decided to answer it for real instead of theorizing.
open tactic
meta inductive level_spec
| const : level → level_spec
| parametric : list name → level → level_spec
meta def level.has_any_param : level → bool
| (level.succ l) := l.has_any_param
| (level.max l₁ l₂) := l₁.has_any_param || l₂.has_any_param
| (level.imax l₁ l₂) := l₁.has_any_param || l₂.has_any_param
| (level.param _) := tt
| l := ff
meta def level.at_zero : level → nat
| (level.succ l) := l.at_zero + 1
| (level.max l₁ l₂) := max l₁.at_zero l₂.at_zero
| (level.imax l₁ l₂) := let n := l₂.at_zero in if n = 0 then 0 else max l₁.at_zero n
| _ := 0
meta def level_spec.at_zero : level_spec → nat
| (level_spec.const l) := l.at_zero
| (level_spec.parametric _ l) := l.at_zero
meta mutual def get_expr_level, get_const_spec (env : environment) (r : ref (name_map level_spec))
with get_expr_level : expr → level → tactic level
| v l := v.fold (pure l) $ λ e _ r, r >>= λ l,
match e with
| expr.const n us := do
sp ← get_const_spec n,
match sp with
| level_spec.const l' := pure $ l.max l'
| level_spec.parametric us' l' := pure $ l.max $ l'.instantiate (us'.zip us)
end
| expr.sort l' := pure $ l.max l'
| _ := pure l
end
with get_const_spec : name → tactic level_spec
| n := do
m ← read_ref r,
match m.find n with
| some sp := pure sp
| none := do
let process_decl (n : name) (us : list name) (l : level) : tactic level_spec := (do
let l := l.normalize,
let sp := if l.has_any_param then level_spec.parametric us l else level_spec.const l,
m ← read_ref r,
write_ref r (m.insert n sp),
pure sp),
d ← env.get n,
match d with
| (declaration.defn n us t v _ _) :=
get_expr_level t level.zero >>= get_expr_level v >>= process_decl n us
| (declaration.thm n us t v) :=
get_expr_level t level.zero >>= get_expr_level v.get >>= process_decl n us
| (declaration.cnst n us t _) := get_expr_level t level.zero >>= process_decl n us
| (declaration.ax n us t) := get_expr_level t level.zero >>= process_decl n us
end
end
run_cmd
using_new_ref mk_name_map $ λ m, do
env ← tactic.get_env,
(n, z) ← env.fold (pure (name.anonymous, 0)) $ λ d r, (do
p ← r,
let n := d.to_name,
sp ← get_const_spec env m n,
let z := sp.at_zero,
pure $ if p.2 < z then (n, z) else p),
trace (n, z)
Mario Carneiro (Dec 24 2021 at 20:10):
For the core library, the output is (sigma.mk.inj_eq, 3)
, which is to say, if you were to specialize every definition to every concrete universe, then the largest subterm of the form Sort u
that appears in the hierarchy of definitions leading to sigma.mk.inj_eq.{0, 0}
is Sort 3
, aka Type 2
. sigma.mk.inj_eq
is the only definition that gets as high as 3
.
For mathlib, the output is (measure_theory.L1.set_to_L1_congr_left', 4)
, and I'm checking now to see if there are any others at height 4. So the official answer seems to be that you need Type 3
to compile mathlib, or alternatively, mathlib can be translated to work in ZFC with 4 inaccessible cardinals.
Mario Carneiro (Dec 24 2021 at 20:12):
It looks like there are a huge number of definitions at height 4 (and measure_theory.L1.set_to_L1_congr_left'
is a random selection). I will look for minimal ones.
Kevin Buzzard (Dec 24 2021 at 20:30):
Just to be clear here -- if a random definition takes four types in, in universes u v w x, that doesn't artificially push the bound up to 4?
Kevin Buzzard (Dec 24 2021 at 20:30):
docs#measure_theory.L1.set_to_L1_congr_left'
Mario Carneiro (Dec 24 2021 at 20:30):
Here is the list of level 4 definitions that do not depend on any level 4 definitions (i.e. they are level 4 because they either directly use a large universe or reference a level 3 definition with a universe bump in the substitution):
ordinal.omin._match_1
nhds_is_measurably_generated
finprod_mem_def
finprod_eq_prod_of_mul_support_subset
add_monoid_hom.map_finsum
pSet.definable
pSet.arity.equiv._main
finprod_mem_univ
algebraic_geometry.LocallyRingedSpace.coproduct._proof_4
finprod_mem_empty
category_theory.types_glue._proof_1
category_theory.types_glue._proof_2
ordinal.lift.principal_seg._proof_1
hahn_series.summable_family.hsum._proof_1
smooth_partition_of_unity.mk
partition_of_unity.mk
bump_covering.to_pou_fun
finprod_cond_nonneg
finsum_eq_sum_of_support_subset
partition_of_unity.sum_eq_one'
partition_of_unity.sum_le_one'
smooth_partition_of_unity.sum_eq_one'
partition_of_unity.sum_nonneg
smooth_partition_of_unity.sum_le_one'
finsum_mem_def
finsum_mem_univ
finsum_mem_congr
algebraic_geometry.Scheme.basic_open_is_open_immersion
uchange
finsum_mem_induction
algebraic_geometry.LocallyRingedSpace.has_coequalizer.image_basic_open._proof_2
algebraic_geometry.LocallyRingedSpace.has_coequalizer.image_basic_open._proof_4
algebraic_geometry.LocallyRingedSpace.has_coequalizer.image_basic_open._proof_8
algebraic_geometry.LocallyRingedSpace.has_coequalizer.coequalizer_π_app_is_local_ring_hom
finsum_mem_empty
algebraic_geometry.LocallyRingedSpace.is_open_immersion.forget_preserves_pullback_of_left
algebraic_geometry.LocallyRingedSpace.coproduct._proof_3
category_theory.subcanonical_types_grothendieck_topology
algebraic_geometry.PresheafedSpace.is_open_immersion.to_LocallyRingedSpace._proof_4
algebraic_geometry.LocallyRingedSpace.coequalizer._proof_3
classical.all_definable._main._proof_1
finsum_mem_zero
monoid_hom.map_finprod
pSet.embed._match_1
finprod_mem_induction
classical.all_definable._main._proof_2
at_bot_is_measurably_generated
ordinal.typein_iso._match_1
ordinal.typein_iso._proof_1
pSet.arity.equiv._main._meta_aux
finsum_dmem
finprod_mem_one
finprod_mem_congr
category_theory.flat_iff_Lan_flat
finprod_dmem
at_top_is_measurably_generated
Mario Carneiro (Dec 24 2021 at 20:31):
@Kevin Buzzard No, having lots of universe parameters does not bump the level, but instantiating those parameters (directly or indirectly) with a u+1
somewhere does
Kevin Buzzard (Dec 24 2021 at 20:32):
measure_theory.L1.set_to_L1_congr_left'
just looks to me like some random technical lemma in measure theory that has nothing to do with universes
Mario Carneiro (Dec 24 2021 at 20:32):
The new list is probably more useful
Johan Commelin (Dec 24 2021 at 20:34):
How the hack can something like finsum_mem_empty
need a nontrivial amount of universes?
Mario Carneiro (Dec 24 2021 at 20:34):
One thing that is a bit dicey about any calculation like this is that if you just chop off the universe hierarchy, then not every term has a type. Right now, the program is asserting for every definition that its type and value have to be typecheckable; perhaps it would be better to skip the type for definitions and theorems
Kevin Buzzard (Dec 24 2021 at 20:34):
finprod_mem_empty
needs four universes? I still don't think I understand the question :-)
Mario Carneiro (Dec 24 2021 at 20:38):
finsum_mem_empty.{u_1, u_4}
has max universe max (u_1+4) (u_4+2)
Johan Commelin (Dec 24 2021 at 20:39):
Kevin Buzzard (Dec 24 2021 at 20:40):
lol what is going on
Yaël Dillies (Dec 24 2021 at 20:41):
Wow! Thanks, Mario.
Mario Carneiro (Dec 24 2021 at 20:42):
finsum_mem_empty.{u_1 u_4}
has max universemax (u_1+4) (u_4+2)
because it usesfinsum.{u_4 u_1+1}
finsum.{u_1 u_2}
has max universemax (u_1+2) (u_2+3)
, still not sure where this is coming from, I will make a program to unravel it
Kevin Buzzard (Dec 24 2021 at 20:46):
lol I wrote finprod_mem_empty
Kevin Buzzard (Dec 24 2021 at 20:47):
oh! Oh so the issue is finsum
, not my lemma
Yaël Dillies (Dec 24 2021 at 20:47):
You're in the highest universe, Kevin!
Kevin Buzzard (Dec 24 2021 at 20:48):
golly, I apparently wrote finprod
too
Yaël Dillies (Dec 24 2021 at 20:49):
Mario Carneiro said:
still not sure where this is coming from, I will make a program to unravel it
The answer is Kevin :upside_down:
Kevin Buzzard (Dec 24 2021 at 20:49):
I'm pretty sure that other people told me what to write in that definition :-)
Kevin Buzzard (Dec 24 2021 at 20:50):
What is all this plift stuff?
Mario Carneiro (Dec 24 2021 at 20:54):
Here's the program for outputting the level of a definition and its direct dependencies:
meta def level_spec.level : level_spec → level
| (level_spec.const l) := l
| (level_spec.parametric _ l) := l
meta def level_spec.args : level_spec → list name
| (level_spec.const l) := []
| (level_spec.parametric us _) := us
set_option pp.all true
run_cmd
using_new_ref mk_name_map $ λ m, do
env ← tactic.get_env,
let n := `finsum,
l ← get_const_spec env m n,
trace (l.args, l.level),
d ← env.get n,
let map := d.value.fold mk_name_set $ λ e _ m,
if e.is_constant then m.insert e.const_name else m,
map.fold (pure ()) $ λ n r, do
r,
l ← get_const_spec env m n,
trace (n, l.args, l.level)
Heather Macbeth (Dec 24 2021 at 20:57):
Kevin Buzzard said:
finprod_mem_empty
needs four universes? I still don't think I understand the question :-)
I don't think I understand most of these examples either ... is it possible that many of them are just "mistakes"? People mis-using universes when writing the lemma/definition?
Mario Carneiro (Dec 24 2021 at 20:59):
finsum_mem_empty.{u_1 u_4}
has max universemax (u_1+4) (u_4+2)
because it usesfinsum.{u_4 u_1+1}
finsum.{u_1 u_2}
has max universemax (u_1+2) (u_2+3)
, because it usesset.finite.to_finset.{u_2}
set.finite.to_finset.{u}
has max universeu+3
, because it usesset.finite.fintype.{u}
set.finite.fintype.{u}
has max universeu+3
, because it usesset.has_coe_to_sort.{u}
set.has_coe_to_sort.{u}
has max universeu+3
, because it useshas_coe_to_sort.mk.{(max (u+1) 1) u+2}
has_coe_to_sort.mk.{u v}
has max universemax u (v+1)
, because its type usesout_param.{v+1}
out_param.{u}
has max universeu
, because it mentionsSort u
Kevin Buzzard (Dec 24 2021 at 21:00):
It seems that the index types alpha,beta,iota are allowed to be Sort
s however finset
takes a Type
, that's what all the plift nonsense is all about.
Mario Carneiro (Dec 24 2021 at 21:01):
I don't think that anything like that is going to cause spurious universe bumps
Kevin Buzzard (Dec 24 2021 at 21:01):
set.has_coe_to_sort.{u} has max universe u+3, because it uses has_coe_to_sort.mk.{(max (u+1) 1) u+2}
lol what is this?
Mario Carneiro (Dec 24 2021 at 21:01):
whether the indexing starts at 0 or 1 for a given universe variable doesn't really matter
Mario Carneiro (Dec 24 2021 at 21:01):
yeah that one is a bit surprising
Mario Carneiro (Dec 24 2021 at 21:01):
but you can see it clearly in the pp.all
Mario Carneiro (Dec 24 2021 at 21:06):
Note that in the has_coe_to_sort.mk
step, I'm actually looking at the type of the definition rather than the value (since it's a constructor). Looking at types can cause universe bumps, since Type u : Type (u+1)
- if you insist that the type be well formed for the value to be then you will get no upper bound
Mario Carneiro (Dec 24 2021 at 21:18):
Here's another way to look at the derivation:
finsum_mem_empty.{0 0}
usesfinsum.{0 1}
finsum.{0 1}
usesset.finite.to_finset.{1}
set.finite.to_finset.{1}
usesset.finite.fintype.{1}
set.finite.fintype.{1}
usesset.has_coe_to_sort.{1}
set.has_coe_to_sort.{1}
useshas_coe_to_sort.mk.{2 3}
has_coe_to_sort.mk.{2 3}
usesout_param.{4}
out_param.{4}
containsSort 4
The last two examples might help to understand what's happening:
#check @finsum_mem_empty.{0 0}
#check @finsum.{0 1}
#check @set.finite.to_finset.{1}
#check @set.finite.fintype.{1}
#check @set.has_coe_to_sort.{1}
#check @has_coe_to_sort.mk.{2 3}
-- has_coe_to_sort.mk.{2 3} : Π {a : Type 1} {S : out_param.{4} (Type 2)}, (a → S) → has_coe_to_sort.{2 3} a S
#check @out_param.{4}
-- out_param.{4} : Type 3 → Type 3
out_param
is just an identity function, but in order to apply the identity function to Type 2
you need a Type 3
to write the type of the argument
Heather Macbeth (Dec 24 2021 at 21:19):
If the uses of set.has_coe_to_sort
were replaced by subtype
, would this get rid of the +3 universe bump?
Mario Carneiro (Dec 24 2021 at 21:21):
here's a simplified example how you can get lots of bumping without really doing anything weird
def foo : Prop := @id (@id (@id (@id (@id (Type 3) (Type 2)) (Type 1)) Type) Prop) true
Mario Carneiro (Dec 24 2021 at 21:29):
@Heather Macbeth Yes, you have to eliminate the uses of set.has_coe_to_sort
in dependents as well, but this version drops the level of set.finite.fintype
from u+3 to u+1:
inductive set.finite' {α} (s : set α) : Prop
| intro : fintype (subtype s) → set.finite'
lemma {u} set.finite_def' {α : Type u} {s : set α} : set.finite' s ↔ nonempty (fintype (subtype s)) := ⟨λ ⟨h⟩, ⟨h⟩, λ ⟨h⟩, ⟨h⟩⟩
noncomputable def {u} set.finite.fintype' {α : Type u} {s : set α} (h : set.finite' s) : fintype (subtype s) :=
classical.choice $ set.finite_def'.1 h
Heather Macbeth (Dec 24 2021 at 21:31):
Hmm, maybe set.has_coe_to_sort
should be considered harmful ...
Mario Carneiro (Dec 24 2021 at 21:33):
Probably a better approach for determining the real axiomatic strength of mathlib would be to allow for higher universes that don't support inductive types at all. As long as you only use them in id
applications like this that might be sufficient.
Mario Carneiro (Dec 24 2021 at 22:23):
Looking at this some more, finsum.{0 1} : Π {M α : Type}, ...
which looks normal, but it references
set.finite.to_finset.{1} : Π {α : Type 1} {s : set α}, s.finite → finset α
which seems gratuitous (why are we dealing with sets of elements in Type 1?). I suspect the reason is plift : Sort u -> Type u
. Perhaps we should try changing that to plift : Sort u -> Sort (max u 1)
and see what breaks
Mario Carneiro (Dec 24 2021 at 22:26):
Oh, that won't work:
@[irreducible] noncomputable def finsum {M : Type u} {α : Sort v} [add_comm_monoid M] (f : α → M) : M :=
if h : finite (support (f ∘ plift.down)) then ∑ i in h.to_finset, f i.down else 0
Here support (f ∘ plift.down)
is needed because support
takes a Type u'
, and if you had a variant of plift
returning Sort (max v 1)
then it wouldn't unify with Type u'
because u' + 1 = max v 1
does not have a solution for u'
in level arithmetic
Kevin Buzzard (Dec 24 2021 at 23:02):
try induction v
? ;-)
Kevin Buzzard (Dec 24 2021 at 23:02):
Whose idea was it to sum over Props anyway?
Yury G. Kudryashov (Dec 25 2021 at 01:28):
It was my idea
Yury G. Kudryashov (Dec 25 2021 at 01:29):
This way you can write \sum^f n < 5, n
Kevin Buzzard (Dec 25 2021 at 01:34):
We need pfinsum :-)
Yury G. Kudryashov (Dec 25 2021 at 02:28):
Why? You won't get nice notation if you don't use the same def
for sums over n : nat
and hn : n < 5
.
Yury G. Kudryashov (Dec 25 2021 at 02:28):
Do you really care about universe bump here?
Johan Commelin (Dec 25 2021 at 04:55):
For me, I find ∑ᶠ n < 5, n
more important than avoiding universe bumps.
Anne Baanen (Dec 25 2021 at 19:18):
I have heard of people who believe in the Axiom of Choice up to some aleph-n (n = 2 or 3 I think?). I haven't heard of anyone who doesn't believe in at most 3 inaccessible cardinals. So I'm curious why we care
Kevin Buzzard (Dec 25 2021 at 21:25):
Because I tell ZFC people that obviously it all works in ZFC?
Last updated: Dec 20 2023 at 11:08 UTC