Skip to content

Commit

Permalink
perf: whnf projections during defeq
Browse files Browse the repository at this point in the history
  • Loading branch information
gebner committed Feb 10, 2023
1 parent 1c35ff2 commit 702c987
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 21 deletions.
42 changes: 22 additions & 20 deletions src/Lean/Meta/ExprDefEq.lean
Expand Up @@ -1732,24 +1732,19 @@ private def isExprDefEqExpensive (t : Expr) (s : Expr) : MetaM Bool := do
-- TODO: investigate whether this is the place for putting this check
if (← (isDefEqEtaStruct t s <||> isDefEqEtaStruct s t)) then return true
if (← isDefEqProj t s) then return true
let t' ← whnfCore t
let s' ← whnfCore s
if t != t' || s != s' then
Meta.isExprDefEqAux t' s'
whenUndefDo (isDefEqNative t s) do
whenUndefDo (isDefEqNat t s) do
whenUndefDo (isDefEqOffset t s) do
whenUndefDo (isDefEqDelta t s) do
if t.isConst && s.isConst then
if t.constName! == s.constName! then isListLevelDefEqAux t.constLevels! s.constLevels! else return false
else if (← pure t.isApp <&&> pure s.isApp <&&> isDefEqApp t s) then
return true
else
whenUndefDo (isDefEqNative t s) do
whenUndefDo (isDefEqNat t s) do
whenUndefDo (isDefEqOffset t s) do
whenUndefDo (isDefEqDelta t s) do
if t.isConst && s.isConst then
if t.constName! == s.constName! then isListLevelDefEqAux t.constLevels! s.constLevels! else return false
else if (← pure t.isApp <&&> pure s.isApp <&&> isDefEqApp t s) then
return true
else
whenUndefDo (isDefEqProjInst t s) do
whenUndefDo (isDefEqStringLit t s) do
if (← isDefEqUnitLike t s) then return true else
isDefEqOnFailure t s
whenUndefDo (isDefEqProjInst t s) do
whenUndefDo (isDefEqStringLit t s) do
if (← isDefEqUnitLike t s) then return true else
isDefEqOnFailure t s

private def mkCacheKey (t : Expr) (s : Expr) : Expr × Expr :=
if Expr.quickLt t s then (t, s) else (s, t)
Expand All @@ -1774,9 +1769,16 @@ partial def isExprDefEqAuxImpl (t : Expr) (s : Expr) : MetaM Bool := withIncRecD
checkMaxHeartbeats "isDefEq"
whenUndefDo (isDefEqQuick t s) do
whenUndefDo (isDefEqProofIrrel t s) do
-- We perform `whnfCore` again with `deltaAtProj := true` at `isExprDefEqExpensive` after `isDefEqProj`
let t' ← whnfCore t (deltaAtProj := false)
let s' ← whnfCore s (deltaAtProj := false)
/-
We also reduce projections here to prevent expensive defeq checks when unifying TC operations.
When unifying e.g. `@Neg.neg α (@Field.toNeg α inst1) =?= @Neg.neg α (@Field.toNeg α inst2)`,
we only want to unify negation (and not all other field operations as well).
Unifying the field instances slowed down unification: https://github.com/leanprover/lean4/issues/1986
We used to *not* reduce projections here, to support unifying `(?a).1 =?= (x, y).1`.
NOTE: this still seems to work because we don't eagerly unfold projection definitions to primitive projections.
-/
let t' ← whnfCore t
let s' ← whnfCore s
if t != t' || s != s' then
isExprDefEqAuxImpl t' s'
else
Expand Down
1 change: 0 additions & 1 deletion src/Lean/Meta/WHNF.lean
Expand Up @@ -490,7 +490,6 @@ expand let-expressions, expand assigned meta-variables.
The parameter `deltaAtProj` controls how to reduce projections `s.i`. If `deltaAtProj == true`,
then delta reduction is used to reduce `s` (i.e., `whnf` is used), otherwise `whnfCore`.
We only set this flag to `false` when implementing `isDefEq`.
If `simpleReduceOnly`, then `iota` and projection reduction are not performed.
Note that the value of `deltaAtProj` is irrelevant if `simpleReduceOnly = true`.
Expand Down
204 changes: 204 additions & 0 deletions tests/lean/run/1986.lean
@@ -0,0 +1,204 @@
instance {ι : Type u} {α : ι → Type v} [∀ i, LE (α i)] : LE (∀ i, α i) where
le x y := ∀ i, x i ≤ y i

class Top (α : Type u) where
top : α

class Bot (α : Type u) where
bot : α

notation "" => Top.top

notation "" => Bot.bot

class Preorder (α : Type u) extends LE α, LT α where
le_refl : ∀ a : α, a ≤ a
le_trans : ∀ a b c : α, a ≤ b → b ≤ c → a ≤ c
lt := λ a b => a ≤ b ∧ ¬ b ≤ a
lt_iff_le_not_le : ∀ a b : α, a < b ↔ (a ≤ b ∧ ¬ b ≤ a)

class PartialOrder (α : Type u) extends Preorder α :=
(le_antisymm : ∀ a b : α, a ≤ b → b ≤ a → a = b)

def Set (α : Type u) := α → Prop

def setOf {α : Type u} (p : α → Prop) : Set α :=
p

namespace Set

protected def Mem (a : α) (s : Set α) : Prop :=
s a

instance : Membership α (Set α) :=
⟨Set.Mem⟩

def range (f : ι → α) : Set α :=
setOf (λ x => ∃ y, f y = x)

end Set

class InfSet (α : Type _) where
infₛ : Set α → α

class SupSet (α : Type _) where
supₛ : Set α → α

export SupSet (supₛ)

export InfSet (infₛ)

open Set

def supᵢ {α : Type _} [SupSet α] {ι} (s : ι → α) : α :=
supₛ (range s)

def infᵢ {α : Type _} [InfSet α] {ι} (s : ι → α) : α :=
infₛ (range s)

class HasSup (α : Type u) where
sup : α → α → α

class HasInf (α : Type u) where
inf : α → α → α

@[inherit_doc]
infixl:68 "" => HasSup.sup

@[inherit_doc]
infixl:69 "" => HasInf.inf

class SemilatticeSup (α : Type u) extends HasSup α, PartialOrder α where
protected le_sup_left : ∀ a b : α, a ≤ a ⊔ b
protected le_sup_right : ∀ a b : α, b ≤ a ⊔ b
protected sup_le : ∀ a b c : α, a ≤ c → b ≤ c → a ⊔ b ≤ c

class SemilatticeInf (α : Type u) extends HasInf α, PartialOrder α where
protected inf_le_left : ∀ a b : α, a ⊓ b ≤ a
protected inf_le_right : ∀ a b : α, a ⊓ b ≤ b
protected le_inf : ∀ a b c : α, a ≤ b → a ≤ c → a ≤ b ⊓ c

class Lattice (α : Type u) extends SemilatticeSup α, SemilatticeInf α

class CompleteSemilatticeInf (α : Type _) extends PartialOrder α, InfSet α where
infₛ_le : ∀ s, ∀ a, a ∈ s → infₛ s ≤ a
le_infₛ : ∀ s a, (∀ b, b ∈ s → a ≤ b) → a ≤ infₛ s

class CompleteSemilatticeSup (α : Type _) extends PartialOrder α, SupSet α where
le_supₛ : ∀ s, ∀ a, a ∈ s → a ≤ supₛ s
supₛ_le : ∀ s a, (∀ b, b ∈ s → b ≤ a) → supₛ s ≤ a

class CompleteLattice (α : Type _) extends Lattice α, CompleteSemilatticeSup α,
CompleteSemilatticeInf α, Top α, Bot α where
protected le_top : ∀ x : α, x ≤ ⊤
protected bot_le : ∀ x : α, ⊥ ≤ x

class Frame (α : Type _) extends CompleteLattice α where

class Coframe (α : Type _) extends CompleteLattice α where
infᵢ_sup_le_sup_infₛ (a : α) (s : Set α) : (infᵢ (λ b => a ⊔ b)) ≤ a ⊔ infₛ s
-- should be ⨅ b ∈ s but I had problems with notation

class CompleteDistribLattice (α : Type _) extends Frame α where
infᵢ_sup_le_sup_infₛ : ∀ a s, (infᵢ (λ b => a ⊔ b)) ≤ a ⊔ infₛ s
-- similarly this is not quite right mathematically but this doesn't matter

-- See note [lower instance priority]
instance (priority := 100) CompleteDistribLattice.toCoframe {α : Type _} [CompleteDistribLattice α] :
Coframe α :=
{ ‹CompleteDistribLattice α› with }

class OrderTop (α : Type u) [LE α] extends Top α where
le_top : ∀ a : α, a ≤ ⊤

class OrderBot (α : Type u) [LE α] extends Bot α where
bot_le : ∀ a : α, ⊥ ≤ a

class BoundedOrder (α : Type u) [LE α] extends OrderTop α, OrderBot α

instance(priority := 100) CompleteLattice.toBoundedOrder {α : Type _} [h : CompleteLattice α] :
BoundedOrder α :=
{ h with }

namespace Pi

variable {ι : Type _} {α' : ι → Type _}

instance [∀ i, Bot (α' i)] : Bot (∀ i, α' i) :=
⟨fun _ => ⊥⟩

instance [∀ i, Top (α' i)] : Top (∀ i, α' i) :=
⟨fun _ => ⊤⟩

protected instance LE {ι : Type u} {α : ι → Type v} [∀ i, LE (α i)] : LE (∀ i, α i) where
le x y := ∀ i, x i ≤ y i

instance Preorder {ι : Type u} {α : ι → Type v} [∀ i, Preorder (α i)] : Preorder (∀ i, α i) :=
{ Pi.LE with
le_refl := sorry
le_trans := sorry
lt_iff_le_not_le := sorry }

instance PartialOrder {ι : Type u} {α : ι → Type v} [∀ i, PartialOrder (α i)] :
PartialOrder (∀ i, α i) :=
{ Pi.Preorder with
le_antisymm := sorry }

instance semilatticeSup [∀ i, SemilatticeSup (α' i)] : SemilatticeSup (∀ i, α' i) where
le_sup_left _ _ _ := SemilatticeSup.le_sup_left _ _
le_sup_right _ _ _ := SemilatticeSup.le_sup_right _ _
sup_le _ _ _ ac bc i := SemilatticeSup.sup_le _ _ _ (ac i) (bc i)

instance semilatticeInf [∀ i, SemilatticeInf (α' i)] : SemilatticeInf (∀ i, α' i) where
inf_le_left _ _ _ := SemilatticeInf.inf_le_left _ _
inf_le_right _ _ _ := SemilatticeInf.inf_le_right _ _
le_inf _ _ _ ac bc i := SemilatticeInf.le_inf _ _ _ (ac i) (bc i)

instance lattice [∀ i, Lattice (α' i)] : Lattice (∀ i, α' i) :=
{ Pi.semilatticeSup, Pi.semilatticeInf with }

instance orderTop [∀ i, LE (α' i)] [∀ i, OrderTop (α' i)] : OrderTop (∀ i, α' i) :=
{ inferInstanceAs (Top (∀ i, α' i)) with le_top := sorry }

instance orderBot [∀ i, LE (α' i)] [∀ i, OrderBot (α' i)] : OrderBot (∀ i, α' i) :=
{ inferInstanceAs (Bot (∀ i, α' i)) with bot_le := sorry }

instance boundedOrder [∀ i, LE (α' i)] [∀ i, BoundedOrder (α' i)] : BoundedOrder (∀ i, α' i) :=
{ Pi.orderTop, Pi.orderBot with }

instance SupSet {α : Type _} {β : α → Type _} [∀ i, SupSet (β i)] : SupSet (∀ i, β i) :=
⟨fun s i => supᵢ (λ (f : {f : ∀ i, β i // f ∈ s}) => f.1 i)⟩

instance InfSet {α : Type _} {β : α → Type _} [∀ i, InfSet (β i)] : InfSet (∀ i, β i) :=
⟨fun s i => infᵢ (λ (f : {f : ∀ i, β i // f ∈ s}) => f.1 i)⟩

instance completeLattice {α : Type _} {β : α → Type _} [∀ i, CompleteLattice (β i)] :
CompleteLattice (∀ i, β i) :=
{ Pi.boundedOrder, Pi.lattice with
le_supₛ := sorry
infₛ_le := sorry
supₛ_le := sorry
le_infₛ := sorry
}

instance frame {ι : Type _} {π : ι → Type _} [∀ i, Frame (π i)] : Frame (∀ i, π i) :=
{ Pi.completeLattice with }

instance coframe {ι : Type _} {π : ι → Type _} [∀ i, Coframe (π i)] : Coframe (∀ i, π i) :=
{ Pi.completeLattice with infᵢ_sup_le_sup_infₛ := sorry }

end Pi

-- very quick (instantaneous) in Lean 4
instance Pi.completeDistribLattice' {ι : Type _} {π : ι → Type _}
[∀ i, CompleteDistribLattice (π i)] : CompleteDistribLattice (∀ i, π i) :=
CompleteDistribLattice.mk (Pi.coframe.infᵢ_sup_le_sup_infₛ)

-- takes around 2 seconds wall clock time on my PC (but very quick in Lean 3)
set_option maxHeartbeats 400 -- make sure it stays fast
set_option synthInstance.maxHeartbeats 400
instance Pi.completeDistribLattice'' {ι : Type _} {π : ι → Type _}
[∀ i, CompleteDistribLattice (π i)] : CompleteDistribLattice (∀ i, π i) :=
{ Pi.frame, Pi.coframe with }
-- quick Lean 3 version:
-- https://github.com/leanprover-community/mathlib/blob/b26e15a46f1a713ce7410e016d50575bb0bc3aa4/src/order/complete_boolean_algebra.lean#L210

0 comments on commit 702c987

Please sign in to comment.