Zulip Chat Archive
Stream: lean4
Topic: slow simp in rejecting Subsingleton
Yakov Pechersky (May 05 2023 at 00:03):
Minimized to be std- and mathlib-free. Consider trying to prove that the empty (fin)set is in the univ of a powerset. The simp lemma Fintype.univ_ofSubsingleton
tries to fire, and spends a huge amount of time (6 seconds on my machine) struggling to show that, unfortunately, range 2
is not a subsingleton. In mathlib, it's even worse, because it then tries to do the same with IsEmpty
.
namespace List
/-- There is only one list of an empty type -/
inductive Perm : List α → List α → Prop
| nil : Perm [] []
| cons (x : α) {l₁ l₂ : List α} : Perm l₁ l₂ → Perm (x :: l₁) (x :: l₂)
| swap (x y : α) (l : List α) : Perm (y :: x :: l) (x :: y :: l)
| trans {l₁ l₂ l₃ : List α} : Perm l₁ l₂ → Perm l₂ l₃ → Perm l₁ l₃
infixl:50 " ~ " => Perm
@[simp]
protected theorem Perm.refl : ∀ l : List α, l ~ l
| [] => Perm.nil
| x :: xs => (Perm.refl xs).cons x
protected theorem Perm.symm {l₁ l₂ : List α} (p : l₁ ~ l₂) : l₂ ~ l₁ :=
p.rec
.nil
(fun x _ _ _ r₁ => .cons x r₁)
(fun x y l => .swap y x l)
(fun _ _ r₁ r₂ => .trans r₂ r₁)
theorem Perm.eqv (α) : Equivalence (@Perm α) :=
⟨Perm.refl, Perm.symm, Perm.trans⟩
instance isSetoid (α) : Setoid (List α) :=
Setoid.mk (@Perm α) (Perm.eqv α)
variable (R : α → α → Prop)
inductive Pairwise : List α → Prop
| nil : Pairwise []
| cons : ∀ {a : α} {l : List α}, (∀ a', Mem a' l → R a a') → Pairwise l → Pairwise (a :: l)
def Nodup : List α → Prop := Pairwise (· ≠ ·)
def sublists (l : List α) : List (List α) :=
let f a arr := arr.foldl (init := Array.mkEmpty (arr.size * 2))
fun r l => (r.push l).push (a :: l)
(l.foldr f #[[]]).toList
@[simp]
def pmap {p : α → Prop} (f : ∀ a, p a → β) : ∀ l : List α, (∀ a, Mem a l → p a) → List β
| [], _ => []
| a :: l, H => f a sorry :: pmap f l sorry
inductive Sublist {α} : List α → List α → Prop
| slnil : Sublist [] []
| cons a : Sublist l₁ l₂ → Sublist l₁ (a :: l₂)
| cons₂ a : Sublist l₁ l₂ → Sublist (a :: l₁) (a :: l₂)
scoped infixl:50 " <+ " => Sublist
def Subperm (l₁ l₂ : List α) : Prop :=
∃ (l : _)(_ : l ~ l₁), l <+ l₂
infixl:50 " <+~ " => Subperm
end List
section Quot
variable {α : Sort u} {r : α → α → Prop} {motive : Quot r → Sort v}
@[elab_as_elim]
protected abbrev Quot.recOn'
(q : Quot r)
(f : (a : α) → motive (Quot.mk r a))
(h : (a b : α) → (p : r a b) → Eq.ndrec (f a) (Quot.sound p) = f b)
: motive q :=
q.rec f h
end Quot
def Multiset.{u} (α : Type u) : Type u :=
Quotient (List.isSetoid α)
namespace Multiset
def ofList : List α → Multiset α :=
Quot.mk _
instance : Coe (List α) (Multiset α) :=
⟨ofList⟩
def Nodup (s : Multiset α) : Prop :=
Quot.liftOn s List.Nodup fun _ _ p => propext sorry
def range (n : Nat) : Multiset Nat :=
List.range n
def powersetAux (l : List α) : List (Multiset α) :=
(List.sublists l).map (ofList)
def powerset (s : Multiset α) : Multiset (Multiset α) :=
Quot.liftOn s
(fun l => (powersetAux l : Multiset (Multiset α)))
(fun _ _ h => Quot.sound sorry)
def Mem (a : α) (s : Multiset α) : Prop :=
Quot.liftOn s (fun l => a ∈ l) fun l₁ l₂ (e : l₁ ~ l₂) => propext <| sorry
instance : Membership α (Multiset α) :=
⟨Mem⟩
nonrec def pmap {p : α → Prop} (f : ∀ a, p a → β) (s : Multiset α) : (∀ a, Mem a s → p a) → Multiset β :=
Quot.recOn' s (fun l H => ofList (List.pmap f l H)) fun l₁ l₂ (pp : l₁ ~ l₂) =>
funext fun H₂ : ∀ a, List.Mem a l₂ → p a =>
have H₁ : ∀ a, List.Mem a l₁ → p a := fun a h => H₂ a sorry
have : ∀ {s₂ e H}, @Eq.ndrec (Multiset α) l₁ (fun s => (∀ a, Mem a s → p a) → Multiset β)
(fun _ => ofList (List.pmap f l₁ H₁)) s₂ e H = ofList (List.pmap f l₁ H₁) := by
intro s₂ e _ ; subst e; rfl
this.trans <| Quot.sound <| sorry
protected def Le (s t : Multiset α) : Prop :=
(Quotient.liftOn₂ s t (· <+~ ·)) fun _ _ _ _ p₁ p₂ =>
propext sorry
instance : LE (Multiset α) where
le := Multiset.Le
def attach (s : Multiset α) : Multiset { x // x ∈ s } :=
pmap Subtype.mk s fun _a => id
end Multiset
structure Finset (α : Type _) where
val : Multiset α
nodup : Multiset.Nodup val
namespace Finset
instance : Membership α (Finset α) :=
⟨fun a s => a ∈ s.1⟩
instance : LE (Finset α) :=
⟨fun s t => ∀ ⦃a⦄, a ∈ s → a ∈ t⟩
def range (n : Nat) : Finset Nat :=
⟨Multiset.range n, sorry⟩
def powerset (s : Finset α) : Finset (Finset α) :=
⟨(s.1.powerset.pmap Finset.mk) fun _t h => sorry, sorry⟩
instance {α : Type u} : CoeSort (Finset α) (Type u) :=
⟨fun s => { x // x ∈ s }⟩
def attach (s : Finset α) : Finset { x // x ∈ s } :=
⟨Multiset.attach s.1, sorry⟩
end Finset
section
open Function
open Nat
universe u v
variable {α β γ : Type _}
class Fintype (α : Type _) where
elems : Finset α
complete : ∀ x : α, x ∈ elems
namespace Finset
variable [Fintype α] {s t : Finset α}
def univ : Finset α :=
@Fintype.elems α _
end Finset
namespace Fintype
/-- Any subsingleton type with a witness is a fintype (with one term). -/
def ofSubsingleton (a : α) [Subsingleton α] : Fintype α :=
-- ⟨{a}, fun _ => Finset.mem_singleton.2 (Subsingleton.elim _ _)⟩
⟨⟨Quotient.mk' [a], sorry⟩, sorry⟩
@[simp]
theorem univ_ofSubsingleton (a : α) [Subsingleton α] : @Finset.univ _ (ofSubsingleton a) = ⟨Quotient.mk' [a], sorry⟩ :=
rfl
end Fintype
instance Finset.fintypeCoeSort {α : Type u} (s : Finset α) : Fintype s :=
⟨s.attach, sorry⟩
end Finset
set_option autoImplicit false
#check (⟨⟨Quotient.mk' [], sorry⟩, sorry⟩ : (Finset.range 2).powerset)
set_option trace.profiler true
theorem foo : ⟨⟨Quotient.mk' [], sorry⟩, sorry⟩ ∈
(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
}) -- if this line, Fintype.univ_ofSubsingleton fires, and is slow (6 s on my computer)
-- }) [-Fintype.univ_ofSubsingleton]
sorry
Yakov Pechersky (May 05 2023 at 02:00):
This is the unification step that is slow:
[7.749492s] ❌ [?a] =?= List.pmap Subtype.mk
(List.pmap Finset.mk (Multiset.powersetAux (List.range 2))
(_ :
∀ (_t : Multiset Nat),
Multiset.Mem _t (Multiset.powerset (Finset.range 2).val) →
Multiset.Nodup _t))
(_ :
∀ (_a : Finset Nat),
Multiset.Mem _a (Finset.powerset (Finset.range 2)).val →
Multiset.Mem _a (Finset.powerset (Finset.range 2)).val)
Kevin Buzzard (May 05 2023 at 07:00):
Congratulations on finally minimising this monster!
Yakov Pechersky (May 05 2023 at 13:28):
Even smaller, with no references to any quotients, only lists. Still 1.5 s
class Fintype (α : Type _) where
elems : List α
complete : ∀ x : α, x ∈ elems
namespace List
def sublists (l : List α) : List (List α) :=
let f a arr := arr.foldl (init := Array.mkEmpty (arr.size * 2))
fun r l => (r.push l).push (a :: l)
(l.foldr f #[[]]).toList
@[simp]
def pmap {p : α → Prop} (f : ∀ a, p a → β) : ∀ l : List α, (∀ a, Mem a l → p a) → List β
| [], _ => []
| a :: l, H => f a sorry :: pmap f l sorry
def attach (s : List α) : List { x // x ∈ s } :=
pmap Subtype.mk s fun _a => id
@[simp]
theorem mem_attach (l : List α) : ∀ x, x ∈ l.attach
| ⟨a, h⟩ => sorry
instance {α : Type u} : CoeSort (List α) (Type u) :=
⟨fun s => { x // x ∈ s }⟩
variable [Fintype α]
def univ : List α :=
@Fintype.elems α _
end List
namespace Fintype
/-- Any subsingleton type with a witness is a fintype (with one term). -/
def ofSubsingleton (a : α) [Subsingleton α] : Fintype α :=
⟨[a], fun x => by
rw [Subsingleton.elim x a]
exact List.Mem.head []⟩
@[simp]
theorem univ_ofSubsingleton (a : α) [Subsingleton α] : @List.univ _ (ofSubsingleton a) = [a] := rfl
end Fintype
instance List.fintypeCoeSort {α : Type u} (s : List α) : Fintype s :=
⟨s.attach, mem_attach _⟩
set_option autoImplicit false
#check (⟨[], sorry⟩ : (List.range 2).sublists)
set_option trace.profiler true
theorem foo : ⟨[], sorry⟩ ∈
(List.univ : List ((List.range 2).sublists)) := 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
}) -- if this line, Fintype.univ_ofSubsingleton fires, and is slow (1.5 s on my computer)
-- }) [-Fintype.univ_ofSubsingleton]
sorry
Yakov Pechersky (May 05 2023 at 13:29):
Is it that just that univ_ofSubsingleton
is a bad simp lemma?
Scott Morrison (May 07 2023 at 07:16):
With set_option trace.Meta.isDefEq true in
we get a bit more useful information.
[?a] =?= List.pmap Subtype.mk (List.sublists (List.range 2))
(_ :
∀ (_a : List Nat),
List.Mem _a (List.sublists (List.range 2)) → List.Mem _a (List.sublists (List.range 2)))
is taking all the time, but mysteriously its children (where it successfully unifies ?a
with the head of the RHS, and then fails to unify []
with the tail of the RHS) are fast. I'm not sure where the time is being sucked up.
Last updated: Dec 20 2023 at 11:08 UTC