Commit 2025-01-24 08:47 ffaf9758

View on Github →

perf: improves the performance of the Repr (Equiv.Perm α) instance (2/4) (#20538) The instance of Repr (Equiv.Perm α) uses the following truncCycleFactors function:

def cycleFactorsAux [DecidableEq α] [Fintype α] (l : List α) (f : Perm α)
    (h : ∀ {x}, f x ≠ x → x ∈ l) :
    { l : List (Perm α) // l.prod = f ∧ (∀ g ∈ l, IsCycle g) ∧ l.Pairwise Disjoint } :=
  match l with
  | [] => ⟨[], ⋯⟩
  | x :: l =>
    if hx : f x = x then cycleFactorsAux l f ⋯
    else
      let ⟨m, hm⟩ := cycleFactorsAux l ((cycleOf f x)⁻¹ * f) ⋯
      ⟨cycleOf f x :: m, ⋯⟩
def truncCycleFactors [DecidableEq α] [Fintype α] (f : Perm α) :
    Trunc { l : List (Perm α) // l.prod = f ∧ (∀ g ∈ l, IsCycle g) ∧ l.Pairwise Disjoint } :=
  Quotient.recOnSubsingleton
    (motive := fun m =>
      (∀ x, f x ≠ x → x ∈ m) →
        Trunc { l // l.prod = f ∧ (∀ g ∈ l, g.IsCycle) ∧ List.Pairwise Disjoint l })
    (Finset.univ : Finset α).val
    (fun l h => Trunc.mk (cycleFactorsAux l f ⋯))
    ⋯

However, for a permutation consisted of many disjoint cycles, like (c[0, 1] * c[2, 3] * c[4, 5] * c[6, 7] : Equiv.Perm (Fin 10)), evaluating returned permutations takes too long. This is because the argument f in cycleFactorsAux gets more complex per step; f to (cycleOf f x)⁻¹ * f. To solve this problem, we memorize the first permutation:

def cycleFactorsAux [DecidableEq α] [Fintype α]
    (l : List α) (f : Perm α) (h : ∀ {x}, f x ≠ x → x ∈ l) :
    { pl : List (Perm α) // pl.prod = f ∧ (∀ g ∈ pl, IsCycle g) ∧ pl.Pairwise Disjoint } :=
  go l f ⋯ ⋯
where
  go (l : List α) (g : Perm α) (hg : ∀ {x}, g x ≠ x → x ∈ l)
    (hfg : ∀ {x}, g x ≠ x → cycleOf f x = cycleOf g x) :
    { pl : List (Perm α) // pl.prod = g ∧ (∀ g' ∈ pl, IsCycle g') ∧ pl.Pairwise Disjoint } :=
  match l with
  | [] => ⟨[], ⋯⟩
  | x :: l =>
    if hx : g x = x then go l g ⋯ ⋯
    else
      let ⟨m, hm₁, hm₂, hm₃⟩ := go l ((cycleOf f x)⁻¹ * g) ⋯ ⋯
      ⟨cycleOf f x :: m, ⋯⟩

Estimated changes