Zulip Chat Archive

Stream: mathlib4

Topic: slow declaration debugging help


Kevin Buzzard (Jul 04 2024 at 17:55):

A student came to me with some innocuous-looking code which was taking over a minute to run on their laptop. After some simplification I've got it down to about 13 seconds on my laptop:

import Mathlib.RingTheory.SimpleModule
import Mathlib.Algebra.Ring.Equiv
import Mathlib.LinearAlgebra.Matrix.IsDiag

open Matrix BigOperators

set_option trace.profiler true in
set_option maxHeartbeats 800000 in
/--
For `A`-module `M`,
`Hom(Mⁿ, Mⁿ) ≅ Mₙ(Hom(M, M))`

-/
@[simps]
def endPowEquivMatrix (A : Type*) [Ring A]
    (M : Type*) [AddCommGroup M] [Module A M] (n : ):
    Module.End A (Fin n  M) ≃+* Matrix (Fin n) (Fin n) (Module.End A M) where
  toFun f i j :=
  { toFun := fun x  f (Function.update 0 j x) i
    map_add' := fun x y  show  f _ i = (f _ + f _) i by
      rw [ f.map_add,  Function.update_add, add_zero]
    map_smul' := fun x y  show f _ _ = (x  f _) _ by
      rw [ f.map_smul,  Function.update_smul, smul_zero] }
  invFun M' :=
  { toFun := fun x i   j : Fin n, M' i j (x j)
    map_add' := fun x y  by
      simp only [map_add,  Finset.sum_add_distrib, Pi.add_apply]
      ext
      dsimp
      exact Finset.sum_add_distrib
    map_smul' := by
      intro a x
      simp only [Pi.smul_apply, LinearMapClass.map_smul, RingHom.id_apply, Finset.smul_sum]
      ext
      dsimp
      exact Eq.symm Finset.smul_sum
  }
  left_inv f := by
    ext i x j : 3
    simp only [of_apply, LinearMap.coe_mk, AddHom.coe_mk, LinearMap.coe_comp, LinearMap.coe_single,
      Function.comp_apply, Finset.sum_apply, Function.update, eq_rec_constant, Pi.zero_apply,
      dite_eq_ite, Finset.sum_ite_eq, Finset.mem_univ, reduceIte]
    rw [ Fintype.sum_apply,  map_sum]
    congr! 1
    ext k : 1
    simp [Pi.single, Function.update]
  right_inv M := by
    dsimp
    ext i j x : 3
    simp only [Function.update, eq_rec_constant, Pi.zero_apply, dite_eq_ite, Finset.sum_apply,
      Finset.sum_ite_eq, Finset.mem_univ, reduceIte, of_apply, LinearMap.coe_mk, AddHom.coe_mk]
    rw [show  k : Fin n, ((M i k) (if k = j then x else 0)) =
       k : Fin n, if k = j then (M i k x) else 0
      from Finset.sum_congr rfl fun k _  by split_ifs <;> aesop]
    simp
  map_mul' f g := by -- slow defeq?
    ext i j x : 2
    simp only [of_apply, LinearMap.coe_mk, AddHom.coe_mk, mul_apply, LinearMap.coeFn_sum,
      Finset.sum_apply, LinearMap.mul_apply, AddSubmonoid.coe_finset_sum,
      Submodule.coe_toAddSubmonoid]
    rw [ Fintype.sum_apply,  map_sum]
    congr! 1
    ext k : 1
    simp [Function.update]
  map_add' _ _ := by ext i j x : 2; simp -- slow defeq?

I'm assuming that there is some defeq abuse going on but I can't see where -- I've looked at the data-carrying fields and I'm assuming that the proofs are irrelevant. Looking at the trace the issue is that map_mul' and map_add' are taking 5 seconds each because of defeq issues. For example map_add' looks like

[step] [5.265840] ext i j x : 2; simp

and the issue is not simp, because

        [] [5.184295] ext i j x : 2 ▶
        [] [0.081321] simp

In fact the issue is not ext either, it's just defeq issues. Unfolding further one finds about four things of the form

  [] [0.562556]  { toFun := fun f i j  { toFun := fun x  f (Function.update 0 j x) i, map_add' := , map_smul' :=  },
          invFun := fun M'  { toFun := fun x i   j : Fin n, (M' i j) (x j), map_add' := , map_smul' :=  },
          left_inv := , right_inv :=  }.toFun
      (x✝¹ + x) =?= ?M 
  [assign] [0.562541]  ?M := {
          toFun := fun f i j  { toFun := fun x  f (Function.update 0 j x) i, map_add' := , map_smul' :=  },
          invFun := fun M'  { toFun := fun x i   j : Fin n, (M' i j) (x j), map_add' := , map_smul' :=  },
          left_inv := , right_inv :=  }.toFun
      (x✝¹ + x)

and my understanding is that I shouldn't expect to be able to unravel [assign] any further. What is surprising me is that this is an assignment of a metavariable ?M : Matrix (Fin n) (Fin n) (Module.End A M) to something which, it seems to me, easily unravels to such a term, so I'm surprised that things are consistently taking nearly a second (and there are about 8 in total of them, which makes things add up). Here x✝¹ : Module.End A (Fin n → M) and the same for x✝, and Module.End is an abbrev for a type of linear maps.

set_option diagnostics true gives

[reduction] unfolded declarations (max: 648, num: 24): 
[reduction] unfolded instances (max: 2476, num: 88): 
[reduction] unfolded reducible declarations (max: 4719, num: 18): 
[type_class] used instances (max: 82, num: 108): 
[def_eq] heuristic for solving `f a =?= f b` (max: 120, num: 9): 
[kernel] unfolded declarations (max: 1050, num: 60): 
use `set_option diagnostics.threshold <num>` to control threshold for reporting counters

but I'm not yet au fait with these numbers -- are they huge?

Kim Morrison (Jul 16 2024 at 16:19):

Generally the numbers themselves on set_option diagnostics true don't tell you much besides the scale of the proof. You need to click the to get the declaration level counts, and see if anything "looks suspicious".

Kim Morrison (Jul 16 2024 at 16:19):

Have you just tried breaking out the fields into preliminary declarations?

Kevin Buzzard (Jul 21 2024 at 19:10):

Oh nice idea! Pulling out toFun and invFun just magically makes it quick! Thanks!

import Mathlib.RingTheory.SimpleModule
import Mathlib.Algebra.Ring.Equiv
import Mathlib.LinearAlgebra.Matrix.IsDiag

open Matrix BigOperators

namespace endPowEquivMatrix

variable {A : Type*} [Ring A]
    {M : Type*} [AddCommGroup M] [Module A M] {n : }

abbrev toFun : Module.End A (Fin n  M)  Matrix (Fin n) (Fin n) (Module.End A M) :=
  fun f i j 
  { toFun := fun x  f (Function.update 0 j x) i
    map_add' := fun x y  show  f _ i = (f _ + f _) i by
      rw [ f.map_add,  Function.update_add, add_zero]
    map_smul' := fun x y  show f _ _ = (x  f _) _ by
      rw [ f.map_smul,  Function.update_smul, smul_zero] }

abbrev invFun : Matrix (Fin n) (Fin n) (Module.End A M)  Module.End A (Fin n  M) :=
  fun M' 
  { toFun := fun x i   j : Fin n, M' i j (x j)
    map_add' := fun x y  by
      simp only [map_add,  Finset.sum_add_distrib, Pi.add_apply]
      ext
      dsimp
      exact Finset.sum_add_distrib
    map_smul' := by
      intro a x
      simp only [Pi.smul_apply, LinearMapClass.map_smul, RingHom.id_apply, Finset.smul_sum]
      ext
      dsimp
      exact Eq.symm Finset.smul_sum
  }

end endPowEquivMatrix

-- count_heartbeats in -- 17331
def endPowEquivMatrix (A : Type*) [Ring A]
    (M : Type*) [AddCommGroup M] [Module A M] (n : ):
    Module.End A (Fin n  M) ≃+* Matrix (Fin n) (Fin n) (Module.End A M) where
  toFun := endPowEquivMatrix.toFun
  invFun := endPowEquivMatrix.invFun
  left_inv f := by
    ext i x j : 3
    simp only [of_apply, LinearMap.coe_mk, AddHom.coe_mk, LinearMap.coe_comp, LinearMap.coe_single,
      Function.comp_apply, Finset.sum_apply, Function.update, eq_rec_constant, Pi.zero_apply,
      dite_eq_ite, Finset.sum_ite_eq, Finset.mem_univ, reduceIte]
    rw [ Fintype.sum_apply,  map_sum]
    congr! 1
    ext k : 1
    simp [Pi.single, Function.update]
  right_inv M := by
    ext i j x : 3
    simp only [Function.update, eq_rec_constant, Pi.zero_apply, dite_eq_ite, Finset.sum_apply,
      Finset.sum_ite_eq, Finset.mem_univ, reduceIte, of_apply, LinearMap.coe_mk, AddHom.coe_mk]
    rw [show  k : Fin n, ((M i k) (if k = j then x else 0)) =
       k : Fin n, if k = j then (M i k x) else 0
      from Finset.sum_congr rfl fun k _  by split_ifs <;> aesop]
    simp
  map_mul' f g := by
    ext i j x : 2
    simp only [of_apply, LinearMap.coe_mk, AddHom.coe_mk, mul_apply, LinearMap.coeFn_sum,
      Finset.sum_apply, LinearMap.mul_apply, AddSubmonoid.coe_finset_sum,
      Submodule.coe_toAddSubmonoid]
    rw [ Fintype.sum_apply,  map_sum]
    congr! 1
    ext k : 1
    simp [Function.update]
  map_add' _ _ := by ext i j x : 2; simp

@Edison Xie if you define the functions outside the equiv it's now quick for a reason I don't understand (this is the same code as above, just rearranged a bit).

Matthew Ballard (Jul 22 2024 at 10:56):

ext is probably prompting unification to unfold the inlined definitions.

Edison Xie (Jul 22 2024 at 22:36):

Oh nice thanks everyone!


Last updated: May 02 2025 at 03:31 UTC