Zulip Chat Archive
Stream: mathlib4
Topic: brute force calculation of Bell/Stirling numbers
Yakov Pechersky (Apr 03 2023 at 01:59):
As part of familiarizing myself with lean4, I am attempting to formalize Bell's and Stirling numbers. With Bhavik, we tried the "brute force" counting approach for a small number, like 3. This code works in mathlib3, and doesn't time out up to n = 3
.
import order.partition.finpartition
open finset
instance fintype_finpartition {α : Type*} [decidable_eq α] (a : finset α) :
fintype (finpartition a) :=
{ elems := a.powerset.powerset.image
(λ p, if h : p.sup_indep id ∧ p.sup id = a ∧ ⊥ ∉ p then ⟨p, h.1, h.2.1, h.2.2⟩ else ⊥),
complete :=
begin
rintro p,
rw [finset.mem_image],
refine ⟨p.parts, _, _⟩,
{ simp only [finset.mem_powerset],
intros i hi,
rw finset.mem_powerset,
exact p.le hi },
{ rw [dif_pos],
ext : 1,
{ refl },
simp only [p.sup_indep, p.sup_parts, p.not_bot_mem, eq_self_iff_true, not_false_iff,
and_self] },
end }
example : @fintype.card (finpartition (∅ : finset unit)) (fintype_finpartition _) = 1 := rfl
example : @fintype.card (finpartition (∅ : finset unit)) (fintype_finpartition _) = 1 := dec_trivial
example : @fintype.card (finpartition (range 3)) (fintype_finpartition _) = 5 := rfl
Yakov Pechersky (Apr 03 2023 at 02:00):
Bhavik also has a version of an explicit partition construction on lists that can go up to n = 8
.
Yakov Pechersky (Apr 03 2023 at 02:00):
However, the equivalent code in mathlib4 dies even on the empty partition example!
Yakov Pechersky (Apr 03 2023 at 02:00):
import Mathlib.Order.Partition.Finpartition
open Finset
instance Finpartition.fintype_finset
{α : Type _} [DecidableEq α] (a : Finset α) : Fintype (Finpartition a) where
elems := a.powerset.powerset.image
(λ p => if h : p.SupIndep id ∧ p.sup id = a ∧ ⊥ ∉ p then ⟨p, h.1, h.2.1, h.2.2⟩ else ⊥)
complete := by
rintro p
rw [mem_image]
refine' ⟨p.parts, _, _⟩
· simp only [mem_powerset]
intros i hi
rw [mem_powerset]
exact p.le hi
· rw [dif_pos]
simp only [p.supIndep, p.supParts, p.not_bot_mem, eq_self_iff_true, not_false_iff, and_self]
example : @Fintype.card (Finpartition (∅ : Finset Unit)) (Finpartition.fintype_finset _) = 1 := rfl
-- (deterministic) timeout at 'whnf', maximum number of heartbeats (200000) has been reached (use 'set_option maxHeartbeats <num>' to set the limit)
Yakov Pechersky (Apr 03 2023 at 02:01):
what am I missing/what's the change that causes this "regression"?
Yaël Dillies (Apr 03 2023 at 07:00):
Could it be a missing computable recursor? We've been adding those by hand.
Reid Barton (Apr 03 2023 at 07:11):
that's a different kind of computation
Reid Barton (Apr 03 2023 at 07:13):
Maybe try with eta experiment? I'm not sure if it would make things better or worse
Yakov Pechersky (Apr 03 2023 at 15:23):
That seems to run indefinitely using 100% CPU
Chris Hughes (Apr 03 2023 at 17:20):
My guess would be that there's some use of Acc
blocking the computation. Either something now uses Acc
that didn't before, probably a function defined with pattern matching, or Acc
is reducing less often in Lean4 because in general reduction doesn't terminate and the heuristic has slightly changed.
Yakov Pechersky (Apr 03 2023 at 17:58):
Do you have a suggestion on how to inspect where this could be occurring?
Yakov Pechersky (Apr 03 2023 at 17:59):
I don't know how to "follow" a rfl proof in either lean3 or lean4, nor how to interrupt a long running rfl proof
Matthew Ballard (Apr 03 2023 at 18:02):
[Meta.isDefEq] [14.641187s] 💥 Fintype.card (Finpartition ∅) = 1 =?= ?m.4430 = ?m.4430
Matthew Ballard (Apr 03 2023 at 18:03):
I turned on
set_option profiler true in
set_option trace.Meta.isDefEq true in
but didn't poke it further
Matthew Ballard (Apr 03 2023 at 18:05):
Just turned off the heart beats bounds to see if it actually finishes
Matthew Ballard (Apr 03 2023 at 18:08):
It does
Matthew Ballard (Apr 03 2023 at 18:12):
And now I have 130k lines of output :/
Matthew Ballard (Apr 03 2023 at 18:19):
This looks like one blocker
Reid Barton (Apr 03 2023 at 18:35):
This would certainly also take me more than 100 seconds to prove
Reid Barton (Apr 03 2023 at 18:36):
Does it break down further into slow things?
Matthew Ballard (Apr 03 2023 at 18:36):
I am tracing instance synthesis to get more info. I have tried set_option trace.Meta.whnf true
but I haven't been able to make it work (ever)
Reid Barton (Apr 03 2023 at 18:38):
Oh right, there can still be unsolved decidability instance metavariables and stuff while we're doing isDefEq, though I'm not sure why it would be the case here?
Matthew Ballard (Apr 03 2023 at 18:39):
You are putting more thought into than me :)
Reid Barton (Apr 03 2023 at 18:40):
I am just assuming they could only occur in the example
itself, because ones in the def
s it uses would have been solved already.
Matthew Ballard (Apr 03 2023 at 18:41):
The decidability stuff is still not defeq right?
Reid Barton (Apr 03 2023 at 18:41):
Certainly we need to synthesize some Fintype instance but I guess that happen before everything else
Matthew Ballard (Apr 03 2023 at 18:41):
Hmm
Matthew Ballard (Apr 03 2023 at 18:42):
Going to let this finish but someone else could try using @
and (_)
on instance argumentsin the meantime
Reid Barton (Apr 03 2023 at 18:42):
what do you mean? If you have something like if h then x else y
then it really means to call Decidable.rec
on the implicit decidability instance for h
, with x
and y
as the other arguments... and all that stuff should show up as intermediate states in isDefEq
Reid Barton (Apr 03 2023 at 18:43):
And the decidability instance for say Finset.SupIndep p id ∧ Finset.sup p id = ∅ ∧ ¬⊥ ∈ p
should have been synthesized already in the definition of whatever contains that.
Matthew Ballard (Apr 03 2023 at 18:43):
Sorry, I was thinking of something else
Matthew Ballard (Apr 03 2023 at 18:58):
Maybe somewhere down line two different (slowly) defeq instances are being synthesized? But I am not sure where that would be. Only going of my limited experience of very slow but successful checks where this has been the majority of cases
Yakov Pechersky (Apr 03 2023 at 19:59):
I don't think it's about instances. It's having issues proving that {}
and {{}}
are different
Yakov Pechersky (Apr 03 2023 at 20:01):
import Mathlib.Data.Finset.Powerset
set_option autoImplicit false
set_option profiler true
set_option trace.Meta.isDefEq true
example : (∅ : Finset Unit).powerset = {∅} := rfl
/-
[0.034324s] ✅ Finset.powerset ∅ = {∅} =?= ?m.230 = ?m.230
-/
example : (∅ : Finset Unit).powerset.powerset = {∅, {∅}} := rfl
/-
[5.022038s] ✅ Finset.powerset (Finset.powerset ∅) = {∅, {∅}} =?= ?m.3885 = ?m.3885 ▼
[] [0.000001s] ✅ Finset (Finset (Finset Unit)) =?= Finset (Finset (Finset Unit))
[] [2.480647s] ✅ {∅,
{∅}}.val =?= Multiset.pmap Finset.mk (Multiset.powerset (Finset.powerset ∅).val)
(_ : ∀ (_t : Multiset (Finset Unit)), _t ∈ Multiset.powerset (Finset.powerset ∅).val → Multiset.Nodup _t) ▶
[] [2.541109s] ✅ {∅, {∅}}.nodup =?= Finset.powerset.proof_2 (Finset.powerset ∅) ▼
[] [2.540974s] ✅ {∅,
{∅}}.val =?= Eq.ndrecOn (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
(_ :
(Quot.lift
(Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
fun l H => ↑(List.pmap Finset.mk l H))
(_ :
∀ (a b : List (Multiset (Finset Unit))),
Setoid.r a b →
Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
(fun l H => ↑(List.pmap Finset.mk l H)) a =
Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
(fun l H => ↑(List.pmap Finset.mk l H)) b)
(Multiset.powerset (Finset.powerset ∅).val)).fst =
Multiset.powerset (Finset.powerset ∅).val)
(Quot.lift
(Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
fun l H => ↑(List.pmap Finset.mk l H))
(_ :
∀ (a b : List (Multiset (Finset Unit))),
Setoid.r a b →
Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
(fun l H => ↑(List.pmap Finset.mk l H)) a =
Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
(fun l H => ↑(List.pmap Finset.mk l H)) b)
(Multiset.powerset (Finset.powerset ∅).val)).snd
(_ :
∀ (_t : Multiset (Finset Unit)),
_t ∈ Multiset.powerset (Finset.powerset ∅).val → Multiset.Nodup _t) ▼
[] [2.540100s] ✅ {∅,
{∅}}.1 =?= (Quot.lift
(Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
fun l H => ↑(List.pmap Finset.mk l H))
(_ :
∀ (a b : List (Multiset (Finset Unit))),
Setoid.r a b →
Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
(fun l H => ↑(List.pmap Finset.mk l H)) a =
Quot.indep (motive := fun x =>
(∀ (a : Multiset (Finset Unit)), a ∈ x → (fun a => Multiset.Nodup a) a) →
Multiset (Finset (Finset Unit)))
(fun l H => ↑(List.pmap Finset.mk l H)) b)
(Multiset.powerset (Finset.powerset ∅).val)).2
(_ :
∀ (_t : Multiset (Finset Unit)),
_t ∈ Multiset.powerset (Finset.powerset ∅).val → Multiset.Nodup _t) ▼
[] [2.539566s] ✅ List.insert ∅
[{∅}] =?= List.pmap Finset.mk
(Multiset.powersetAux
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset ∅.val → Multiset.Nodup _t)))
(_ :
∀ (_t : Multiset (Finset Unit)),
_t ∈ Multiset.powerset (Finset.powerset ∅).val → Multiset.Nodup _t) ▼
-/
example : (∅ : Finset Unit).powerset.powerset.image (λ p => ((∅ : Finset Unit))) = {∅} := rfl
/-
(deterministic) timeout at 'isDefEq', maximum number of heartbeats (200000) has been reached (use 'set_option maxHeartbeats <num>' to set the limit)
[13.332704s] 💥 Finset.image (fun p => ∅) (Finset.powerset (Finset.powerset ∅)) = {∅} =?= ?m.467830 = ?m.467830 ▼
[] [0.000013s] ✅ Finset (Finset Unit) =?= ?m.467829 ▶
[] [0.000018s] ✅ Finset.image (fun p => ∅) (Finset.powerset (Finset.powerset ∅)) =?= ?m.467830 ▶
[] [13.332652s] 💥 {∅} =?= Finset.image (fun p => ∅) (Finset.powerset (Finset.powerset ∅)) ▼
[] [13.332633s] 💥 {∅} =?= Finset.image (fun p => ∅) (Finset.powerset (Finset.powerset ∅)) ▼
[] [13.332576s] 💥 {∅} =?= Multiset.toFinset (Multiset.map (fun p => ∅) (Finset.powerset (Finset.powerset ∅)).val) ▼
[] [13.332540s] 💥 {∅} =?= { val := Multiset.dedup (Multiset.map (fun p => ∅) (Finset.powerset (Finset.powerset ∅)).val),
nodup :=
(_ :
Multiset.Nodup
(Multiset.dedup (Multiset.map (fun p => ∅) (Finset.powerset (Finset.powerset ∅)).val))) } ▼
[] [0.000001s] ✅ Finset (Finset Unit) =?= Finset (Finset Unit)
[] [7.061364s] ✅ {∅}.val =?= Multiset.dedup (Multiset.map (fun p => ∅) (Finset.powerset (Finset.powerset ∅)).val) ▶
[] [6.271131s] 💥 {∅}.nodup =?= Multiset.toFinset.proof_1
(Multiset.map (fun p => ∅) (Finset.powerset (Finset.powerset ∅)).val) ▶
-/
Yakov Pechersky (Apr 03 2023 at 20:02):
I would think it would be fast, since it could just execute on the list level, seeing past the quotient...
Reid Barton (Apr 03 2023 at 20:16):
Interesting that it timed out while trying to defeq test two proofs
Yakov Pechersky (Apr 03 2023 at 20:21):
More specifically, here is checking the powerset rfl, comparing multiset and finset. I've avoided notation as much as possible to prevent any TC search for EmptyCollection or Singleton:
import Mathlib.Data.Finset.Powerset
set_option autoImplicit false
set_option profiler true
set_option trace.Meta.isDefEq true
example : (∅ : Finset Unit).powerset = {∅} := rfl
/-
[0.034324s] ✅ Finset.powerset ∅ = {∅} =?= ?m.230 = ?m.230
-/
example : (Multiset.zero : Multiset Unit).powerset.powerset =
(Multiset.cons Multiset.zero (Multiset.cons (Multiset.cons Multiset.zero Multiset.zero) Multiset.zero) : Multiset (Multiset (Multiset Unit))) := rfl
/-
[0.497044s] ✅ Multiset.powerset (Multiset.powerset Multiset.zero) =
Multiset.powerset
(Multiset.powerset
Multiset.zero) =?= Multiset.powerset (Multiset.powerset Multiset.zero) =
Multiset.zero ::ₘ (Multiset.zero ::ₘ Multiset.zero) ::ₘ Multiset.zero
-/
example : (Finset.empty : Finset Unit).powerset.powerset =
(Finset.cons Finset.empty (Finset.cons (Finset.cons Finset.empty Finset.empty sorry) Finset.empty sorry) sorry : Finset (Finset (Finset Unit))) := by
ext
simp [show (Finset.empty : Finset Unit) = ∅ from rfl, show (Finset.empty : Finset (Finset Unit)) = ∅ from rfl, show (Finset.empty : Finset (Finset (Finset Unit))) = ∅ from rfl]
example : (Finset.empty : Finset Unit).powerset.powerset =
(Finset.cons Finset.empty (Finset.cons (Finset.cons Finset.empty Finset.empty sorry) Finset.empty sorry) sorry : Finset (Finset (Finset Unit))) := rfl
/-
[3.660799s] ✅ Finset.powerset (Finset.powerset Finset.empty) =
Finset.cons Finset.empty
(Finset.cons (Finset.cons Finset.empty Finset.empty (_ : ¬Finset.empty ∈ Finset.empty)) Finset.empty
(_ : ¬Finset.cons Finset.empty Finset.empty (_ : ¬Finset.empty ∈ Finset.empty) ∈ Finset.empty))
(_ :
¬Finset.empty ∈
Finset.cons (Finset.cons Finset.empty Finset.empty (_ : ¬Finset.empty ∈ Finset.empty)) Finset.empty
(_ :
¬Finset.cons Finset.empty Finset.empty (_ : ¬Finset.empty ∈ Finset.empty) ∈
Finset.empty)) =?= ?m.4628 = ?m.4628
-/
Yakov Pechersky (Apr 03 2023 at 20:22):
Is it trying to defeq the proof terms themselves?
Yakov Pechersky (Apr 03 2023 at 20:23):
[] [1.802547s] ✅ List.pmap Finset.mk
(Multiset.powersetAux
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val → Multiset.Nodup _t)))
(_ :
∀ (_t : Multiset (Finset Unit)),
_t ∈ Multiset.powerset (Finset.powerset Finset.empty).val →
Multiset.Nodup
_t) =?= [Finset.empty,
Finset.cons Finset.empty Finset.empty (_ : ¬Finset.empty ∈ Finset.empty)] ▼
[] [0.277031s] ✅ {
val :=
↑(List.foldr
(fun a arr =>
Array.foldl (fun r l => Array.push (Array.push r l) (a :: l))
(Array.mkEmpty (Array.size arr * 2)) arr 0 (Array.size arr))
#[[]]
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val →
Multiset.Nodup _t)))[0],
nodup :=
(_ :
(fun a => Multiset.Nodup a)
↑(List.foldr
(fun a arr =>
Array.foldl (fun r l => Array.push (Array.push r l) (a :: l))
(Array.mkEmpty (Array.size arr * 2)) arr 0 (Array.size arr))
#[[]]
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val →
Multiset.Nodup _t)))[0]) } ::
List.pmap Finset.mk
(List.map Multiset.ofList
[(List.foldr
(fun a arr =>
Array.foldl (fun r l => Array.push (Array.push r l) (a :: l))
(Array.mkEmpty (Array.size arr * 2)) arr 0 (Array.size arr))
#[[]]
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val →
Multiset.Nodup _t)))[1]])
(_ :
∀ (x : Multiset (Finset Unit)),
x ∈
List.map Multiset.ofList
[(List.foldr
(fun a arr =>
Array.foldl
(fun r l => Array.push (Array.push r l) (a :: l))
(Array.mkEmpty (Array.size arr * 2)) arr 0
(Array.size arr))
#[[]]
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val →
Multiset.Nodup _t)))[1]] →
(fun a => Multiset.Nodup a)
x) =?= [Finset.empty,
Finset.cons Finset.empty Finset.empty (_ : ¬Finset.empty ∈ Finset.empty)] ▶
Yakov Pechersky (Apr 03 2023 at 20:24):
I can't find what's taking the 1.6 unaccounted for seconds there
Sebastian Ullrich (Apr 03 2023 at 20:29):
For what it's worth, https://github.com/leanprover/lean4/pull/2181 should be helpful with such performance investigations in the future
Matthew Ballard (Apr 03 2023 at 20:31):
Yakov Pechersky said:
I can't find what's taking the 1.6 unaccounted for seconds there
Is that the only node nested below List.pmap Finset.mk
?
Sebastian Ullrich (Apr 03 2023 at 20:38):
There may be hidden TC nodes, try synthInstance
as well
Matthew Ballard (Apr 03 2023 at 21:03):
import took 211ms
elaboration took 519ms
elaboration took 4.11s
cumulative profiling times:
attribute application 0.00429ms
compilation new 0.0254ms
elaboration 4.66s
import 211ms
initialization 13.7ms
interpretation 1.37s
linting 0.349ms
parsing 1.04ms
simp 7.04ms
tactic execution 1.24ms
type checking 11ms
typeclass inference 6.35ms
I dumped the output to a file output.txt. Have to go to dinner now.
Yakov Pechersky (Apr 03 2023 at 21:03):
There's not subnodes between the 1.9s and 0.2:
[] [1.926125s] ✅ List.pmap Finset.mk
(Multiset.powersetAux
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val → Multiset.Nodup _t)))
(_ :
∀ (_t : Multiset (Finset Unit)),
_t ∈ Multiset.powerset (Finset.powerset Finset.empty).val →
Multiset.Nodup
_t) =?= [Finset.empty,
Finset.cons Finset.empty Finset.empty (_ : ¬Finset.empty ∈ Finset.empty)] ▼
[] [0.294699s] ✅ {
val :=
↑(List.foldr
(fun a arr =>
Array.foldl (fun r l => Array.push (Array.push r l) (a :: l))
(Array.mkEmpty (Array.size arr * 2)) arr 0 (Array.size arr))
#[[]]
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val →
Multiset.Nodup _t)))[0],
nodup :=
(_ :
(fun a => Multiset.Nodup a)
↑(List.foldr
(fun a arr =>
Array.foldl
(fun r l => Array.push (Array.push r l) (a :: l))
(Array.mkEmpty (Array.size arr * 2)) arr 0
(Array.size arr))
#[[]]
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val →
Multiset.Nodup _t)))[0]) } ::
List.pmap Finset.mk
(List.map Multiset.ofList
[(List.foldr
(fun a arr =>
Array.foldl (fun r l => Array.push (Array.push r l) (a :: l))
(Array.mkEmpty (Array.size arr * 2)) arr 0 (Array.size arr))
#[[]]
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val →
Multiset.Nodup _t)))[1]])
(_ :
∀ (x : Multiset (Finset Unit)),
x ∈
List.map Multiset.ofList
[(List.foldr
(fun a arr =>
Array.foldl
(fun r l => Array.push (Array.push r l) (a :: l))
(Array.mkEmpty (Array.size arr * 2)) arr 0
(Array.size arr))
#[[]]
(List.pmap Finset.mk (Multiset.powersetAux [])
(_ :
∀ (_t : Multiset Unit),
_t ∈ Multiset.powerset Finset.empty.val →
Multiset.Nodup _t)))[1]] →
(fun a => Multiset.Nodup a)
x) =?= [Finset.empty,
Finset.cons Finset.empty Finset.empty (_ : ¬Finset.empty ∈ Finset.empty)] ▶
Yakov Pechersky (Apr 03 2023 at 22:00):
What's the right way to switch my lean version to try trace.profiler
?
Yakov Pechersky (Apr 03 2023 at 22:01):
because the flamegraph is boring otherwise:
image.png
Yakov Pechersky (Apr 11 2023 at 18:42):
I think this is also rearing its head even in tactic proofs. In here:
import Mathlib.Data.Finset.Powerset
import Mathlib.Algebra.BigOperators.Basic
open Finset
lemma foo : Finset.sum (range 4)
(λ n => card ((range 3).powerset.powerset.filter (λ s => s.card = n))) = 0 := by
rewrite [range_succ]
rewrite [sum_insert] -- slooow
Yakov Pechersky (Apr 11 2023 at 18:42):
I'm using rewrite
to try to avoid rfl
at the end of rw
, still
Yakov Pechersky (Apr 11 2023 at 22:05):
Fewer imports:
simp took 29.8s
Yakov Pechersky (Apr 11 2023 at 22:05):
import Mathlib.Data.Finset.Powerset
import Mathlib.Data.Fintype.Basic
set_option autoImplicit false
set_option profiler true in
set_option trace.Meta.synthInstance true in
set_option trace.Meta.isDefEq true in
set_option maxHeartbeats 0 in
set_option synthInstance.maxHeartbeats 0 in
lemma foo : ⟨∅, Finset.empty_mem_powerset (Finset.range 2)⟩ ∈
(Finset.univ : Finset ((Finset.range 2).powerset)).filter (λ t => t.1.card = 1) := by
simp only
Yakov Pechersky (Apr 11 2023 at 22:06):
I know the statement is false, but I'm not sure why simp only
would take so long. It times out on range 3
Yakov Pechersky (Apr 11 2023 at 22:10):
Even (config := {decide := false})
is slow
Yakov Pechersky (Apr 14 2023 at 11:39):
I think (config := { etaStruct := .none })
speeds it up a bit
Yakov Pechersky (Apr 14 2023 at 11:55):
But still:
[Meta.isDefEq] [8.192934s] ❌ Finset.univ =?= Finset.univ ▶
[Meta.Tactic.simp.unify] @Fintype.univ_ofSubsingleton:1000, failed to unify
Finset.univ
with
Finset.univ
[Meta.isDefEq] [8.263717s] ❌ Finset.univ =?= Finset.univ ▶
[Meta.Tactic.simp.unify] @Fintype.univ_of_isEmpty:1000, failed to unify
Finset.univ
with
Finset.univ
Yakov Pechersky (Apr 14 2023 at 11:59):
That's in this context:
[Meta.Tactic.simp.discharge] @Finset.univ_unique, failed to synthesize instance
Unique { x // x ∈ Finset.powerset (Finset.range 2) }
[Meta.isDefEq] [8.661934s] ❌ Finset.univ =?= Finset.univ ▶
[Meta.Tactic.simp.unify] @Fintype.univ_ofSubsingleton:1000, failed to unify
Finset.univ
with
Finset.univ
[Meta.isDefEq] [7.900318s] ❌ Finset.univ =?= Finset.univ ▶
[Meta.Tactic.simp.unify] @Fintype.univ_of_isEmpty:1000, failed to unify
Finset.univ
with
Finset.univ
Scratch.lean:13:58
Yakov Pechersky (Apr 14 2023 at 11:59):
import Mathlib.Data.Finset.Powerset
import Mathlib.Data.Fintype.Basic
set_option autoImplicit false
set_option profiler true in
set_option trace.Meta.synthInstance true in
set_option trace.Meta.isDefEq true in
set_option trace.Meta.Tactic.simp true in
set_option maxHeartbeats 0 in
set_option synthInstance.maxHeartbeats 0 in
lemma foo : ⟨∅, Finset.empty_mem_powerset (Finset.range 2)⟩ ∈
(Finset.univ : Finset ((Finset.range 2).powerset)) := by
simp (config := {
maxSteps := 100
maxDischargeDepth := 1
contextual := false
memoize := false
singlePass := false
zeta := false
beta := false
eta := false
etaStruct := .none
iota := false
proj := false
decide := false
arith := false
autoUnfold := false
dsimp := false
}) [-Finset.mem_univ, -Finset.mem_attach, -Finset.univ_eq_attach]
_
Yakov Pechersky (Apr 14 2023 at 12:01):
However, just a #synth
takes < 0.002 s:
set_option profiler true in
set_option trace.Meta.synthInstance true in
set_option trace.Meta.isDefEq true in
set_option trace.Meta.Tactic.simp true in
set_option maxHeartbeats 0 in
set_option synthInstance.maxHeartbeats 0 in
#synth Unique { x // x ∈ Finset.powerset (Finset.range 2) }
Yakov Pechersky (Apr 14 2023 at 12:05):
Ah, the Unique thing is fast anyway. It's about Subsingleton and IsEmpty checks
Kevin Buzzard (Apr 14 2023 at 12:58):
Can you make this fast yet?
import Mathlib.Data.Finset.Powerset
import Mathlib.Algebra.BigOperators.Basic
open Finset
set_option profiler true in
example : (Finset.sum (insert 3 (range 3)) fun n => card (filter (fun s => card s = n) (powerset (powerset (range 3))))) = 0 := by
rw [sum_insert] -- type checking takes 7 seconds on my machine
-- and running it several times causes a stack overflow on my machine
-- (or sometimes causes it to hang)
sorry
sorry
Bhavik Mehta (Sep 22 2023 at 14:26):
Bump on this - this issue shows up for me fairly often, and it's one of the reasons I need to continue using Lean 3 - do we have a fix yet?
Kevin Buzzard (Sep 22 2023 at 14:30):
Do we have a minimised example? Is there a github issue yet? Is it covered by any of the other slowness issues which have been opened recently? I'd like to get up to speed on this one, I can't remember the details of it any more.
Bhavik Mehta (Sep 22 2023 at 14:33):
Not that I know of unfortunately, I was hoping you'd remember!
Mauricio Collares (Sep 22 2023 at 16:42):
Data/Multiset/Powerset.lean starts with a suspicious TODO:
--Porting note: TODO: Write a more efficient version
/-- A helper function for the powerset of a multiset. Given a list `l`, returns a list
of sublists of `l` as multisets. -/
def powersetAux (l : List α) : List (Multiset α) :=
(sublists l).map (↑)
#align multiset.powerset_aux Multiset.powersetAux
Mauricio Collares (Sep 22 2023 at 18:12):
I replaced the powersetAux version by a hacky port of the mathlib3 one and it seems to have fixed the timeout. It's at branch#collares/redefine-powersetAux. I'm not sure what to do with the mathlib3 version of sublistsAux (sublistsAux₃ in my branch), and there's a theorem sorried out. Help is very much welcome!
Mauricio Collares (Sep 23 2023 at 11:43):
What's interesting is that #reduce (@Fintype.card (Finpartition (∅ : Finset Unit)) (Finpartition.fintype_finset _))
(inserted after this thread's original example) runs really fast, even with the current definition of powersetAux.
Mauricio Collares (Sep 23 2023 at 12:11):
(deleted)
Mauricio Collares (Sep 23 2023 at 14:19):
Cargo-culting #7232 to std4 seems to fix the timeouts. PR at std4#271.
EDIT: Turns out it didn't, sorry for the noise. See below.
Mauricio Collares (Sep 23 2023 at 17:25):
(deleted)
Mauricio Collares (Sep 24 2023 at 06:37):
As pointed out by @Eric Rodriguez last week in https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/decide.20regression/near/391860857, the slow instance in Std (docs#List.decidableBAll) is duplicated in mathlib4 (docs#List.decidableBall). The mathlib4 version is fast because it contains the workaround to lean4#2552 by @Eric Rodriguez and @Bhavik Mehta introduced in #7232; this means disabling the Std instance via attribute [-instance] List.decidableBAll
suffices to make the examples in this thread run fast.
Mauricio Collares (Sep 24 2023 at 06:42):
Wait, apparently it doesn't. How is that possible?
Kevin Buzzard (Sep 24 2023 at 06:42):
Because Bhavik is very good at finding subtle issues (actually Yakov found this one but Bhavik is very good at using these sorts of things in his projects)
Mauricio Collares (Sep 24 2023 at 06:58):
This is undoubtedly true, but it also turned out I made a pretty embarrassing testing mistake by not fully reverting the changes I made while testing branch#collares/redefine-powersetAux and they were responsible for the speedup after all, unfortunately. I'll close the Std4 PR.
Mauricio Collares (Sep 26 2023 at 19:40):
Okay, let's see if I didn't mess up this time: #7388
Mauricio Collares (Sep 27 2023 at 16:50):
I just noticed something really funny about the original example. Suppose we have the original instance, that is,
import Mathlib.Order.Partition.Finpartition
open Finset
instance Finpartition.fintype_finset
{α : Type _} [DecidableEq α] (a : Finset α) : Fintype (Finpartition a) where
elems := a.powerset.powerset.image
(λ p => if h : p.SupIndep id ∧ p.sup id = a ∧ ⊥ ∉ p then ⟨p, h.1, h.2.1, h.2.2⟩ else ⊥)
complete := by
rintro p
rw [mem_image]
refine' ⟨p.parts, _, _⟩
· simp only [mem_powerset]
intros i hi
rw [mem_powerset]
exact p.le hi
· rw [dif_pos]
simp only [p.supIndep, p.supParts, p.not_bot_mem, eq_self_iff_true, not_false_iff, and_self]
Now consider the following two snippets:
1) example : @Fintype.card (Finpartition (∅ : Finset Unit)) (Finpartition.fintype_finset _) = 1 := by rfl
.
2) example : @Fintype.card (Finpartition (∅ : Finset Unit)) (Finpartition.fintype_finset _) = 1 := rfl
.
As reported by count_heartbeats
, the first one uses 650 heartbeats and the second one uses 4865061 (!) heartbeats on master. After #7388, the first one uses 486 heartbeats and the second uses 3094. I really suspect that I'm doing something wrong, but I've tested a few times and it seems consistent.
Edit: As a corollary, make sure to use by rfl
and not rfl
if you're trying the @Fintype.card (Finpartition (range 3)) (Finpartition.fintype_finset _) = 5
original example with #7388!
Eric Rodriguez (Sep 27 2023 at 17:01):
Can this be done mathlibless?
Bhavik Mehta (Sep 27 2023 at 21:58):
Mauricio Collares said:
I just noticed something really funny about the original example. Suppose we have the original instance, that is,
import Mathlib.Order.Partition.Finpartition open Finset instance Finpartition.fintype_finset {α : Type _} [DecidableEq α] (a : Finset α) : Fintype (Finpartition a) where elems := a.powerset.powerset.image (λ p => if h : p.SupIndep id ∧ p.sup id = a ∧ ⊥ ∉ p then ⟨p, h.1, h.2.1, h.2.2⟩ else ⊥) complete := by rintro p rw [mem_image] refine' ⟨p.parts, _, _⟩ · simp only [mem_powerset] intros i hi rw [mem_powerset] exact p.le hi · rw [dif_pos] simp only [p.supIndep, p.supParts, p.not_bot_mem, eq_self_iff_true, not_false_iff, and_self]
Now consider the following two snippets:
1)
example : @Fintype.card (Finpartition (∅ : Finset Unit)) (Finpartition.fintype_finset _) = 1 := by rfl
.
2)example : @Fintype.card (Finpartition (∅ : Finset Unit)) (Finpartition.fintype_finset _) = 1 := rfl
.As reported by
count_heartbeats
, the first one uses 650 heartbeats and the second one uses 4865061 (!) heartbeats on master. After #7388, the first one uses 486 heartbeats and the second uses 3094. I really suspect that I'm doing something wrong, but I've tested a few times and it seems consistent.Edit: As a corollary, make sure to use
by rfl
and notrfl
if you're trying the@Fintype.card (Finpartition (range 3)) (Finpartition.fintype_finset _) = 5
original example with #7388!
Woah! I'd seen examples where rfl
and by rfl
were different but never this different! Nice find
Kevin Buzzard (Sep 27 2023 at 23:11):
Note Sebastian's comment about this phenomenon here.
Mauricio Collares (Oct 09 2023 at 13:35):
Modulo bumping macRecDepth
, the by rfl
/rfl
discrepancy was fixed in 9f50f44
. Thank you Leo and Sebastian!
Eric Wieser (Oct 22 2023 at 10:18):
In the latest master, this now works (but is still slow):
set_option maxRecDepth 1500 in
example : @Fintype.card (Finpartition (univ : Finset (Fin 3))) (Finpartition.fintype_finset _) = 5 := rfl
Mauricio Collares (Oct 22 2023 at 10:19):
(and no need for the maxRecDepth bump if you use by rfl
instead)
Last updated: Dec 20 2023 at 11:08 UTC