Zulip Chat Archive

Stream: PR reviews

Topic: Feedback requested on PR #30962: lattice path lemmas


WangYiran01 (Oct 31 2025 at 12:00):

Hi everyone

I’ve opened (PR #30962), which adds some lemmas and counts for lattice paths in the Combinatorics/Enumerative part of mathlib.

I’d really appreciate feedback on:

  • whether the statements and proofs are idiomatic for mathlib,
  • lemma naming or organization,
  • possible generalizations or simplifications.

Thanks in advance for your time and suggestions!

WangYiran01 (Nov 10 2025 at 05:44):

Hi all,
Following up on PR#30962 — it’s been open for a while, and I’d really appreciate any comments or suggestions when convenient.
Thanks in advance for taking a look!

Thomas Waring (Nov 15 2025 at 00:16):

Hi :-) my suggestions would be that a) you should prove that your inductive definition count the concrete List Step objects you define and b) these objects could be generalised a lot, to any type with Mul or Add.

I'm no expert here, but I had a go at defining a version of this, and proving the result that your function does indeed count the lattice paths

import Mathlib

@[to_additive existing]
def prodPathsTo {α} [Mul α] [One α] (gs : Set α) (a : α) : Set (List α) := {l : List α | ( x  l, x  gs)  l.prod = a}

section

variable {α} [Mul α] [One α] (gs : Set α)

@[to_additive existing]
def cons_mem_prodPathsTo (g a : α) (l : List α) :
    g :: l  prodPathsTo gs a  g  gs  ( x  l, x  gs)  g * l.prod = a := by
  rw [and_assoc]
  simp [prodPathsTo]

@[to_additive existing]
theorem prodPathsTo_inj (a b : α) (h_eq : prodPathsTo gs a = prodPathsTo gs b) :
    (Set.Nonempty <| prodPathsTo gs a)  a = b
  | l, hl => by rw [hl.2, show l.prod = b by exact (h_eq  hl).2]

@[to_additive existing]
theorem prodPathsTo_union (a : α) (ha : a  1) :
    prodPathsTo gs a =  (g : gs),  (a' : {a' : α // g * a' = a}), (fun l : List α => (g : α) :: l)'' (prodPathsTo gs a') := by
  ext l
  simp only [Set.iUnion_coe_set, Set.mem_iUnion, Set.mem_image, Subtype.exists, exists_prop]
  constructor
  · intro hl_gs, hla
    have : l  [] := by grind [List.prod_nil]
    obtain g, l', hl' := List.exists_cons_of_ne_nil this
    have : g  gs := by grind
    refine g, this, l'.prod, ?_, l', ?_, ?_⟩
    · rw [hla, hl']
      simp
    all_goals grind [prodPathsTo]
  · intro h
    rcases h with g, hg, a', rfl, l', hl'_gs, rfl, rfl
    constructor
    · grind
    · exact List.prod_cons

end

def numSumPath :  ×   
  | 0, _⟩ => 1
  | ⟨_, 0 => 1
  | m + 1, n + 1 => numSumPath m + 1, n + numSumPath m, n + 1

theorem foo : (m : )  (n : )  numSumPath m, n = (sumPathsTo (α :=  × ) {1,0, 0,1} m, n).encard
  | 0, _ => sorry
  | _, 0 => sorry
  | m + 1, n + 1 => by
    have : sumPathsTo (α :=  × ) {1,0, 0,1} m + 1, n + 1 =
        (fun l => (0, 1) :: l)'' sumPathsTo (α :=  × ) {1,0, 0,1} m + 1, n 
        (fun l => 1, 0 :: l)'' sumPathsTo (α :=  × ) {1,0, 0,1} m, n + 1 := by
      ext l
      cases l
      case nil =>
        simp [sumPathsTo, OfNat.ofNat, Zero.zero]
      case cons g l =>
        rw [cons_mem_sumPathsTo]
        have : AddLeftCancelMonoid ( × ) := by infer_instance
        have : (m + 1, n + 1 :  × ) = 1, 0 + m, n + 1 := by norm_num; rw [add_comm]
        have : (m + 1, n + 1 :  × ) = 0, 1 + m + 1, n := by norm_num; rw [add_comm]
        constructor
        · intro hg, hl, hsum
          simp only [Set.mem_union, Set.mem_image, List.cons.injEq, exists_eq_right_right]
          simp only [Set.mem_insert_iff, Set.mem_singleton_iff] at hg
          cases hg
          case inl hg =>
            refine Or.inr ⟨⟨hl, ?_⟩, Eq.symm hg
            grind
          case inr hg =>
            refine Or.inl ⟨⟨hl, ?_⟩, Eq.symm hg
            grind
        · grind [sumPathsTo]
    rw [this, Set.encard_union_eq, numSumPath, Nat.cast_add]
    congr
    · rw [Function.Injective.encard_image]
      · exact foo ..
      · intro l l' h
        grind
    · rw [Function.Injective.encard_image]
      · exact foo ..
      · intro l l' h
        grind
    · grind [Set.disjoint_iff]

Lastly, I would guess that the specificity of ℕ × ℕ is not the right generality to prove this result — you can play the same game for any power ℕᵏ (where the closed form is a multinomial coefficient).


Last updated: Dec 20 2025 at 21:32 UTC