Documentation

AlgebraicCombinatorics.Determinants.LGV1

The Lindström-Gessel-Viennot Lemma: Part 1 #

This file formalizes the first part of the Lindström-Gessel-Viennot (LGV) lemma, covering the basic definitions and counting results for lattice paths.

Main definitions #

Main results #

References #

Implementation notes #

We formalize lattice paths as lists of steps (east or north), rather than as sequences of vertices. This makes it easier to work with path concatenation and step counting.

The LGV lemma relates determinants to non-intersecting lattice paths. The key insight is a sign-reversing involution that cancels intersecting path tuples.

Relationship with LGV2.lean #

This file (LGV1.lean) and LGV2.lean both work with the integer lattice digraph, but use different representations:

The integer lattice definitions have equivalent adjacency relations:

LatticeStep and LatticePath equivalence #

Both files define a LatticeStep type for representing east/north steps:

These are equivalent types, with explicit conversion functions:

The duplication exists because LGV1 imports LGV2, preventing LGV2 from referencing LGV1's definitions. Both definitions are kept compatible with trivial conversions.

LGV2.lean contains the complete weighted LGV lemma proof. This file contains the counting case infrastructure with lattice-specific path counting results.

The Integer Lattice (Definition def.lgv.lattice) #

The integer lattice ℤ² is an infinite digraph with vertices at integer points and directed edges (arcs) going east (i,j) → (i+1,j) and north (i,j) → (i,j+1).

This digraph is acyclic and path-finite (only finitely many paths between any two points).

Formalization of Definition def.lgv.lattice #

From the TeX source (AlgebraicCombinatorics/tex/Determinants/LGV1.tex):

We consider the infinite simple digraph with vertex set ℤ² (so the vertices are pairs of integers) and arcs: (i,j) → (i+1,j) for all (i,j) ∈ ℤ² [east-steps/right-steps] (i,j) → (i,j+1) for all (i,j) ∈ ℤ² [north-steps/up-steps]

The entire digraph is denoted by ℤ² and called the "integer lattice" or "integer grid".

@[reducible, inline]

A point on the integer lattice ℤ². The vertices of the integer lattice are pairs of integers. Label: def.lgv.lattice

Equations
Instances For

    The x-coordinate of a lattice point.

    Equations
    Instances For

      The y-coordinate of a lattice point.

      Equations
      Instances For
        @[simp]
        theorem LGV1.LatticePoint.x_mk (a b : ) :
        x (a, b) = a

        Simp lemma for the x-coordinate of a constructed lattice point.

        @[simp]
        theorem LGV1.LatticePoint.y_mk (a b : ) :
        y (a, b) = b

        Simp lemma for the y-coordinate of a constructed lattice point.

        The coordinate sum of a lattice point (x + y).

        Equations
        Instances For
          @[simp]

          Simp lemma for the coordinate sum of a constructed lattice point.

          The Integer Lattice as a Digraph #

          We formalize the integer lattice as a Digraph instance, where an arc exists from vertex p to vertex q if and only if q is obtained from p by either:

          The adjacency relation for the integer lattice digraph. There is an arc from p to q iff q is one step east or north of p. Label: eq.def.lgv.lattice.east, eq.def.lgv.lattice.north

          Equations
          Instances For

            The integer lattice as a digraph. This is the digraph with vertex set ℤ² and arcs (i,j) → (i+1,j) and (i,j) → (i,j+1). Label: def.lgv.lattice

            Equations
            Instances For

              Decidability of adjacency in the integer lattice.

              Equations

              An arc exists from p to (p.1 + 1, p.2) (east step). Label: eq.def.lgv.lattice.east

              An arc exists from p to (p.1, p.2 + 1) (north step). Label: eq.def.lgv.lattice.north

              theorem LGV1.integerLattice_adj_iff (p q : LatticePoint) :
              integerLattice.Adj p q q = (p.1 + 1, p.2) q = (p.1, p.2 + 1)

              Characterization of adjacency: q is adjacent from p iff q is one step east or north. Label: def.lgv.lattice

              The integer lattice has no self-loops (irreflexive).

              The integer lattice is acyclic: the coordinate sum strictly increases along any arc. This implies there are no cycles.

              The integer lattice is acyclic: if there's a chain from p to q, then p.coordSum ≤ q.coordSum. This implies there are no cycles: any path from p to itself would require p.coordSum < p.coordSum.

              Lattice Steps and Paths #

              A step on the lattice is either east (right) or north (up). A lattice path is a sequence of steps starting from a given point.

              We formalize paths as lists of steps rather than lists of vertices, which makes it easier to work with path concatenation and step counting.

              A step on the integer lattice: either east (right) or north (up).

              • east: (i,j) → (i+1,j)
              • north: (i,j) → (i,j+1)

              This type is equivalent to LGV.LatticeStep' defined in LGV2.lean. The equivalence is provided by latticeStepEquiv : LatticeStepLGV.LatticeStep'.

              Label: eq.def.lgv.lattice.east, eq.def.lgv.lattice.north

              Instances For

                Apply a step to a lattice point.

                Equations
                Instances For

                  A step corresponds to an arc in the integer lattice digraph.

                  @[reducible, inline]

                  A lattice path is a list of steps.

                  This type is equivalent to LGV.LatticePath' defined in LGV2.lean. Conversion functions are provided:

                  Equations
                  Instances For

                    The endpoint of a path starting from a given point.

                    Equations
                    Instances For
                      @[simp]
                      theorem LGV1.LatticePath.endpoint_nil (start : LatticePoint) :
                      endpoint [] start = start

                      Simp lemma: the endpoint of an empty path is the start.

                      @[simp]
                      theorem LGV1.LatticePath.endpoint_cons (s : LatticeStep) (path : LatticePath) (start : LatticePoint) :
                      endpoint (s :: path) start = path.endpoint (s.apply start)

                      Simp lemma: the endpoint of a cons path applies the first step then continues.

                      theorem LGV1.LatticePath.endpoint_append (path1 path2 : LatticePath) (start : LatticePoint) :
                      (path1 ++ path2).endpoint start = path2.endpoint (path1.endpoint start)

                      The endpoint of appended paths: endpoint(p1 ++ p2, A) = endpoint(p2, endpoint(p1, A)).

                      The number of steps in a path.

                      Equations
                      Instances For
                        @[simp]

                        Simp lemma: the step count of an empty path is 0.

                        @[simp]

                        Simp lemma: the step count of a cons path is one more than the rest.

                        The number of east steps in a path.

                        Equations
                        Instances For
                          @[simp]

                          Simp lemma: the east step count of an empty path is 0.

                          @[simp]

                          Simp lemma: the east step count of a path starting with an east step.

                          @[simp]

                          Simp lemma: the east step count of a path starting with a north step.

                          The number of north steps in a path.

                          Equations
                          Instances For
                            @[simp]

                            Simp lemma: the north step count of an empty path is 0.

                            @[simp]

                            Simp lemma: the north step count of a path starting with a north step.

                            @[simp]

                            Simp lemma: the north step count of a path starting with an east step.

                            A path's step count equals east steps plus north steps.

                            All vertices visited by a path, including start and end.

                            Equations
                            Instances For

                              Check if a path goes from point a to point b.

                              Equations
                              Instances For
                                theorem LGV1.LatticePath.isPathFromTo_take_drop (path : LatticePath) (n : ) (A B : LatticePoint) (h : path.isPathFromTo A B) :
                                have mid := endpoint (List.take n path) A; isPathFromTo (List.take n path) A mid isPathFromTo (List.drop n path) mid B

                                If a path goes from A to B, then take n goes from A to an intermediate point, and drop n goes from that point to B.

                                theorem LGV1.LatticePath.isPathFromTo_append (head tail : LatticePath) (A v B : LatticePoint) (hhead : head.isPathFromTo A v) (htail : tail.isPathFromTo v B) :
                                (head ++ tail).isPathFromTo A B

                                If head goes A→v and tail goes v→B, then head ++ tail goes A→B.

                                Each step increases the coordinate sum by 1.

                                theorem LGV1.LatticePath.coordSum_endpoint (path : LatticePath) (start : LatticePoint) :
                                (path.endpoint start).coordSum = start.coordSum + path.stepCount

                                The coordinate sum increases by the step count along any path.

                                Counting Paths (Proposition prop.lgv.1-paths.ct) #

                                The number of lattice paths from (a,b) to (c,d) is:

                                This follows from:

                                theorem LGV1.path_stepCount_eq (path : LatticePath) (a b c d : ) (h : path.isPathFromTo (a, b) (c, d)) :
                                path.stepCount = c + d - a - b

                                Observation 1: Any path from (a,b) to (c,d) has exactly c+d-a-b steps. Label: pf.prop.lgv.1-paths.ct.o1

                                theorem LGV1.endpoint_x_eq (path : LatticePath) (start : LatticePoint) :
                                (path.endpoint start).1 = start.1 + path.eastStepCount

                                Helper: The x-coordinate of the endpoint equals start.x + eastStepCount.

                                theorem LGV1.path_eastStepCount_eq (path : LatticePath) (a b c d : ) (h : path.isPathFromTo (a, b) (c, d)) :
                                path.eastStepCount = c - a

                                Observation 2: Any path from (a,b) to (c,d) has exactly c-a east steps. Label: pf.prop.lgv.1-paths.ct.o2

                                The set of all lattice paths from point a to point b.

                                Equations
                                Instances For

                                  The set of paths from a to b is finite.

                                  The number of paths from (a,b) to (c,d). This equals C(c+d-a-b, c-a) when c ≥ a and d ≥ b, and 0 otherwise. (Proposition prop.lgv.1-paths.ct) Label: prop.lgv.1-paths.ct

                                  Equations
                                  Instances For

                                    Bijection between paths and subsets #

                                    The key to proving the counting formula is establishing a bijection between:

                                    The bijection: A path corresponds to choosing which m positions (out of m+n total steps) are east steps. The subset S records exactly those positions.

                                    theorem LGV1.numPaths_eq_card (a b : LatticePoint) (hfin : (pathsFromTo a b).Finite) :

                                    The number of paths formula is correct. Label: prop.lgv.1-paths.ct

                                    The proof uses a bijection between lattice paths from a to b and subsets of {0, ..., m+n-1} of size m, where m = (b.1 - a.1).toNat and n = (b.2 - a.2).toNat. A path corresponds to choosing which m positions (out of m+n total steps) are east steps. The number of such choices is C(m+n, m).

                                    No paths exist when c+d < a+b.

                                    Path Tuples, Nipats, and Ipats (Definition def.lgv.path-tups) #

                                    A k-vertex is a k-tuple of lattice points. A path tuple from k-vertex A to k-vertex B is a k-tuple of paths where the i-th path goes from A_i to B_i.

                                    A path tuple is non-intersecting (nipat) if no two paths share a vertex. A path tuple is intersecting (ipat) if some two paths share a vertex.

                                    @[reducible, inline]
                                    abbrev LGV1.kVertex (k : ) :

                                    A k-vertex is a k-tuple of lattice points. Label: def.lgv.path-tups (a)

                                    Equations
                                    Instances For
                                      def LGV1.kVertex.permute {k : } (v : kVertex k) (σ : Equiv.Perm (Fin k)) :

                                      Permute a k-vertex by a permutation σ. Label: def.lgv.path-tups (b)

                                      Equations
                                      Instances For
                                        structure LGV1.PathTuple (k : ) (A B : kVertex k) :

                                        A path tuple from k-vertex A to k-vertex B. Label: def.lgv.path-tups (c)

                                        Instances For
                                          def LGV1.PathTuple.verticesOf {k : } {A B : kVertex k} (pt : PathTuple k A B) (i : Fin k) :

                                          The set of vertices visited by a path in a path tuple.

                                          Equations
                                          Instances For
                                            def LGV1.PathTuple.isNonIntersecting {k : } {A B : kVertex k} (pt : PathTuple k A B) :

                                            A path tuple is non-intersecting (nipat) if no two distinct paths share a vertex. Label: def.lgv.path-tups (d)

                                            Equations
                                            Instances For
                                              def LGV1.PathTuple.isIntersecting {k : } {A B : kVertex k} (pt : PathTuple k A B) :

                                              A path tuple is intersecting (ipat) if it is not non-intersecting. Label: def.lgv.path-tups (e)

                                              Equations
                                              Instances For
                                                def LGV1.pathTuplesFromTo {k : } (A B : kVertex k) :
                                                Set (PathTuple k A B)

                                                The set of all path tuples from A to B.

                                                Equations
                                                Instances For
                                                  def LGV1.nipatsFromTo {k : } (A B : kVertex k) :
                                                  Set (PathTuple k A B)

                                                  The set of all non-intersecting path tuples (nipats) from A to B.

                                                  Equations
                                                  Instances For
                                                    def LGV1.ipatsFromTo {k : } (A B : kVertex k) :
                                                    Set (PathTuple k A B)

                                                    The set of all intersecting path tuples (ipats) from A to B.

                                                    Equations
                                                    Instances For

                                                      The set of path tuples from A to B is finite (follows from finiteness of paths).

                                                      theorem LGV1.nipatsFromTo_finite {k : } (A B : kVertex k) :

                                                      The set of nipats from A to B is finite (subset of finite set).

                                                      theorem LGV1.ipatsFromTo_finite {k : } (A B : kVertex k) :

                                                      The set of ipats from A to B is finite (subset of finite set).

                                                      General k Involution Infrastructure #

                                                      The sign-reversing involution for general k path tuples works as follows: Given an intersecting path tuple (ipat), we:

                                                      1. Find the smallest path index i that contains a "crowded" vertex (shared with another path)
                                                      2. Find the first crowded vertex v on path i (by position in the path)
                                                      3. Find the largest path index j that contains v (j > i since v is crowded)
                                                      4. Exchange the tails of paths i and j at v
                                                      5. Compose σ with the transposition (i j) to flip the sign

                                                      This maps (σ, ipat) to (σ * swap(i,j), ipat') where:

                                                      Since every ipat has at least one crowded point, the involution has no fixed points.

                                                      def LGV1.PathTuple.isCrowded {k : } {A B : kVertex k} (pt : PathTuple k A B) (v : LatticePoint) :

                                                      A vertex is crowded in a path tuple if it appears in at least two paths

                                                      Equations
                                                      Instances For

                                                        An intersecting path tuple has at least one crowded vertex

                                                        noncomputable def LGV1.PathTuple.crowdedPathIndices {k : } {A B : kVertex k} (pt : PathTuple k A B) :

                                                        The set of path indices that have a crowded vertex (shared with another path)

                                                        Equations
                                                        Instances For

                                                          An intersecting path tuple has nonempty crowdedPathIndices

                                                          noncomputable def LGV1.PathTuple.minCrowdedPathIndex {k : } {A B : kVertex k} (pt : PathTuple k A B) (hip : pt.isIntersecting) :
                                                          Fin k

                                                          The minimum crowded path index for an intersecting path tuple. This is the smallest index i such that path i shares a vertex with some other path.

                                                          This is a key component of the sign-reversing involution for general k: the involution swaps tails at the first crowded vertex on this path.

                                                          Equations
                                                          Instances For

                                                            The min crowded path index is in crowdedPathIndices

                                                            theorem LGV1.PathTuple.minCrowdedPathIndex_le {k : } {A B : kVertex k} (pt : PathTuple k A B) (hip : pt.isIntersecting) (i : Fin k) (hi : i pt.crowdedPathIndices) :

                                                            The min crowded path index is minimal

                                                            The crowded vertices on a specific path: vertices that appear in multiple paths

                                                            Equations
                                                            Instances For

                                                              The crowded vertices on the min crowded path are nonempty

                                                              theorem LGV1.PathTuple.verticesOf_finite {k : } {A B : kVertex k} (pt : PathTuple k A B) (i : Fin k) :

                                                              The vertices on a path form a finite set

                                                              def LGV1.pathTupleWithPerm {k : } (A B : kVertex k) :

                                                              A path tuple with permutation: pairs (σ, pt) where pt is a path tuple from A to σ(B)

                                                              Equations
                                                              Instances For

                                                                The sign of a path tuple with permutation

                                                                Equations
                                                                Instances For

                                                                  The set of all ipats with permutation

                                                                  Equations
                                                                  Instances For
                                                                    theorem LGV1.ipatWithPerm_finite {k : } (A B : kVertex k) :

                                                                    The set of ipats with permutation is finite

                                                                    The LGV Lemma for Two Paths (Proposition prop.lgv.2paths.count) #

                                                                    For two 2-vertices (A, A') and (B, B'):

                                                                    det | #paths(A→B) #paths(A→B') | | #paths(A'→B) #paths(A'→B') |

                                                                    = #nipats from (A,A') to (B,B') - #nipats from (A,A') to (B',B)

                                                                    The proof uses a sign-reversing involution on intersecting path tuples that exchanges the tails of two intersecting paths.

                                                                    def LGV1.pathMatrix2 (A A' B B' : LatticePoint) :
                                                                    Matrix (Fin 2) (Fin 2)

                                                                    The path count matrix for two source and target points.

                                                                    Equations
                                                                    Instances For
                                                                      noncomputable def LGV1.numNipats2 (A A' B B' : LatticePoint) :

                                                                      Number of nipats from (A,A') to (B,B'). Defined as the cardinality of the set of non-intersecting path tuples.

                                                                      Equations
                                                                      Instances For
                                                                        theorem LGV1.pathMatrix2_det (A A' B B' : LatticePoint) :
                                                                        (pathMatrix2 A A' B B').det = (numPaths A B) * (numPaths A' B') - (numPaths A B') * (numPaths A' B)

                                                                        Helper: the 2x2 determinant expands to the difference of products.

                                                                        structure LGV1.SignedPathTuple2 (A A' B B' : LatticePoint) :

                                                                        A path tuple from (A,A') to (B,B') or (B',B) with a sign. Sign is +1 for (B,B') and -1 for (B',B).

                                                                        Instances For
                                                                          def LGV1.SignedPathTuple2.sign {A A' B B' : LatticePoint} (spt : SignedPathTuple2 A A' B B') :

                                                                          The sign of a signed path tuple.

                                                                          Equations
                                                                          Instances For

                                                                            Check if a signed path tuple is intersecting (the two paths share a vertex).

                                                                            Equations
                                                                            Instances For

                                                                              The set of all signed path tuples from (A,A') to (B,B') or (B',B).

                                                                              Equations
                                                                              Instances For
                                                                                def LGV1.signedIpats2 (A A' B B' : LatticePoint) :
                                                                                Set (SignedPathTuple2 A A' B B')

                                                                                The set of intersecting signed path tuples.

                                                                                Equations
                                                                                Instances For
                                                                                  def LGV1.signedNipats2 (A A' B B' : LatticePoint) :
                                                                                  Set (SignedPathTuple2 A A' B B')

                                                                                  The set of non-intersecting signed path tuples.

                                                                                  Equations
                                                                                  Instances For

                                                                                    Deterministic intersection point selection #

                                                                                    The key to proving that the sign-reversing involution is involutive is to use a deterministic choice of intersection point. We define firstIntersection to be the first intersection point in path0's vertex list (by index). This ensures that after swapping tails, the same point is still the first intersection.

                                                                                    noncomputable def LGV1.firstIntersectionIdx {A A' B B' : LatticePoint} (spt : SignedPathTuple2 A A' B B') (_h : spt.isIntersecting) :

                                                                                    The index of the first intersection point in path0's vertices. This is the smallest index i such that (path0.vertices A)[i] ∈ path1.vertices A'.

                                                                                    Equations
                                                                                    Instances For

                                                                                      The index of the first intersection point is valid (less than the length of path0's vertices).

                                                                                      noncomputable def LGV1.firstIntersection {A A' B B' : LatticePoint} (spt : SignedPathTuple2 A A' B B') (h : spt.isIntersecting) :

                                                                                      The first intersection point of two intersecting paths. This is the first vertex (by index in path0) that is shared by both paths.

                                                                                      Equations
                                                                                      Instances For

                                                                                        The first intersection point is in path0's vertices.

                                                                                        The first intersection point is in path1's vertices.

                                                                                        theorem LGV1.firstIntersection_is_first {A A' B B' : LatticePoint} (spt : SignedPathTuple2 A A' B B') (h : spt.isIntersecting) :
                                                                                        have v := firstIntersection spt h; let idx := firstIntersectionIdx spt h; (spt.path0.vertices A)[idx] = v ∀ (i : ) (hi : i < idx), (spt.path0.vertices A)[i]spt.path1.vertices A'

                                                                                        The first intersection point is the first vertex (by index in path0) shared by both paths. This is a key property for proving the involution is involutive: after swapping tails, the same point v is still the first intersection because:

                                                                                        1. The head of path0 (indices 0..idx) is unchanged
                                                                                        2. v is at index idx in both the original and swapped paths
                                                                                        3. No earlier vertex in path0's head is in path1's vertices (by minimality of findIdx)
                                                                                        noncomputable def LGV1.splitPathAt (path : LatticePath) (start v : LatticePoint) (_hv : v path.vertices start) :

                                                                                        Split a path at a vertex, returning (head, tail) where head ends at v and tail starts at v.

                                                                                        Equations
                                                                                        • One or more equations did not get rendered due to their size.
                                                                                        Instances For

                                                                                          Helper lemmas for the involution proof #

                                                                                          noncomputable def LGV1.ipatInvolution {A A' B B' : LatticePoint} (spt : SignedPathTuple2 A A' B B') (h : spt.isIntersecting) :

                                                                                          The sign-reversing involution on intersecting signed path tuples. Given an ipat, we:

                                                                                          1. Find the first intersection point v (deterministically, by index in path0)
                                                                                          2. Exchange the tails of the two paths at v
                                                                                          3. Flip the sign (swap toBB')

                                                                                          This maps ipats to (B,B') ↔ ipats to (B',B).

                                                                                          Note: We use firstIntersection instead of Classical.choose to ensure the involution is truly involutive. The key property is that after swapping tails, the same point v is still the first intersection point.

                                                                                          Equations
                                                                                          • One or more equations did not get rendered due to their size.
                                                                                          Instances For
                                                                                            theorem LGV1.ipatInvolution_sign {A A' B B' : LatticePoint} (spt : SignedPathTuple2 A A' B B') (h : spt.isIntersecting) :

                                                                                            The involution is sign-reversing.

                                                                                            The involution preserves being intersecting.

                                                                                            Key lemma: The first intersection point is preserved after swapping tails.

                                                                                            If we swap tails at the first intersection point v, then v is still the first intersection point in the new paths. This is because:

                                                                                            1. The head of path0 (vertices 0..idx) is unchanged
                                                                                            2. v is at index idx in both the original and new path0's vertices
                                                                                            3. No earlier vertex in head0 is in path1's vertices (by minimality)

                                                                                            This lemma is essential for proving that the involution is involutive.

                                                                                            theorem LGV1.ipatInvolution_involutive {A A' B B' : LatticePoint} (spt : SignedPathTuple2 A A' B B') (h : spt.isIntersecting) :
                                                                                            have spt' := ipatInvolution spt h; ∃ (h' : spt'.isIntersecting), ipatInvolution spt' h' = spt

                                                                                            The involution is its own inverse.

                                                                                            After applying the involution twice, we get back the original path tuple. The key insight is that:

                                                                                            1. The first intersection point v is preserved after swapping tails (by firstIntersection_preserved)
                                                                                            2. The indices idx0 and idx1 are also preserved
                                                                                            3. Exchanging tails twice returns the original paths (take_append_drop)
                                                                                            4. toBB' flips twice (!!b = b)

                                                                                            Proof strategy:

                                                                                            • Use firstIntersection_preserved to show v' = v
                                                                                            • Use findIdx_beq_eq_firstIntersectionIdx and findIdx_beq_newPath1_eq to show idx0' = idx0 and idx1' = idx1
                                                                                            • Apply List.take_append_drop to reconstruct the original paths

                                                                                            The set of signed path tuples is finite.

                                                                                            theorem LGV1.signedNipats2_finite (A A' B B' : LatticePoint) :
                                                                                            (signedNipats2 A A' B B').Finite

                                                                                            The set of non-intersecting signed path tuples is finite.

                                                                                            theorem LGV1.signedIpats2_finite (A A' B B' : LatticePoint) :
                                                                                            (signedIpats2 A A' B B').Finite

                                                                                            The set of intersecting signed path tuples is finite.

                                                                                            theorem LGV1.sum_signedPathTuples2 (A A' B B' : LatticePoint) (hfin : (signedPathTuples2 A A' B B').Finite) :
                                                                                            spthfin.toFinset, spt.sign = (numPaths A B) * (numPaths A' B') - (numPaths A B') * (numPaths A' B)

                                                                                            The sum over all signed path tuples equals the difference of path tuple counts.

                                                                                            theorem LGV1.sum_signedIpats2_eq_zero (A A' B B' : LatticePoint) (hfin : (signedIpats2 A A' B B').Finite) :
                                                                                            spthfin.toFinset, spt.sign = 0

                                                                                            The sum over intersecting path tuples is zero (by sign-reversing involution).

                                                                                            theorem LGV1.sum_signedNipats2 (A A' B B' : LatticePoint) (hfin : (signedNipats2 A A' B B').Finite) :
                                                                                            spthfin.toFinset, spt.sign = (numNipats2 A A' B B') - (numNipats2 A A' B' B)

                                                                                            The sum over non-intersecting path tuples equals the difference of nipat counts.

                                                                                            theorem LGV1.lgv_two_paths (A A' B B' : LatticePoint) :
                                                                                            (pathMatrix2 A A' B B').det = (numNipats2 A A' B B') - (numNipats2 A A' B' B)

                                                                                            The LGV lemma for two paths. (Proposition prop.lgv.2paths.count) Label: prop.lgv.2paths.count

                                                                                            The proof uses a sign-reversing involution on intersecting path tuples:

                                                                                            1. Expand the determinant: det(M) = #paths(A→B)·#paths(A'→B') - #paths(A→B')·#paths(A'→B)
                                                                                            2. By product rule, this equals #pathTuples(→(B,B')) - #pathTuples(→(B',B))
                                                                                            3. Define signed path tuples with sign +1 for (B,B') and -1 for (B',B)
                                                                                            4. The involution exchanges tails at the first intersection, flipping the sign
                                                                                            5. By Lemma lem.sign.cancel2, ipats cancel, leaving only nipats

                                                                                            Baby Jordan Curve Theorem (Proposition prop.lgv.jordan-2) #

                                                                                            If A' is weakly northwest of A, and B' is weakly northwest of B, then any path from A to B' must intersect any path from A' to B.

                                                                                            This means:

                                                                                            Under these conditions, #nipats from (A,A') to (B',B) = 0.

                                                                                            Point p is weakly northwest of point q.

                                                                                            Equations
                                                                                            Instances For
                                                                                              theorem LGV1.path_coords_nondecreasing (path : LatticePath) (a b : LatticePoint) (h : path.isPathFromTo a b) :
                                                                                              a.1 b.1 a.2 b.2

                                                                                              Helper: If a path exists from a to b, then a.1 ≤ b.1 and a.2 ≤ b.2.

                                                                                              theorem LGV1.start_mem_vertices (path : LatticePath) (start : LatticePoint) :
                                                                                              start path.vertices start

                                                                                              Helper: Start vertex is in vertices.

                                                                                              theorem LGV1.mem_vertices_cons (s : LatticeStep) (rest : LatticePath) (start v : LatticePoint) :
                                                                                              v LatticePath.vertices (s :: rest) start v = start v rest.vertices (s.apply start)

                                                                                              Helper: Vertices of a cons path.

                                                                                              theorem LGV1.vertices_cons_subset (s : LatticeStep) (rest : LatticePath) (start v : LatticePoint) :
                                                                                              v rest.vertices (s.apply start)v LatticePath.vertices (s :: rest) start

                                                                                              Helper: Vertices of tail are contained in vertices of full path.

                                                                                              @[irreducible]
                                                                                              theorem LGV1.baby_jordan_curve (A A' B B' : LatticePoint) (hA : isWeaklyNorthwestOf A' A) (hB : isWeaklyNorthwestOf B' B) (p p' : LatticePath) (hp : p.isPathFromTo A B') (hp' : p'.isPathFromTo A' B) :
                                                                                              vp.vertices A, v p'.vertices A'

                                                                                              Baby Jordan curve theorem: Under NW conditions, paths must intersect. (Proposition prop.lgv.jordan-2) Label: prop.lgv.jordan-2

                                                                                              Proof strategy (from tex source, Section sec.details.det.comb):

                                                                                              The proof is by strong induction on ℓ(p) + ℓ(p'), the sum of path lengths.

                                                                                              Base case: If ℓ(p) + ℓ(p') = 0, then both paths are empty, so A = B' and A' = B. From the NW conditions:

                                                                                              • A'.x ≤ A.x = B'.x ≤ B.x = A'.x, so A'.x = A.x
                                                                                              • A'.y ≥ A.y = B'.y ≥ B.y = A'.y, so A'.y = A.y Thus A = A', and A is a common vertex.

                                                                                              Induction step: Assume the theorem holds for smaller path lengths. If A = A', then A is a common vertex and we're done. Otherwise, since A' is weakly NW of A and A ≠ A', we have either:

                                                                                              • Case 1: A'.y > A.y (A' strictly north of A)
                                                                                              • Case 2: A'.x < A.x (A' strictly west of A)

                                                                                              In Case 1 (A'.y > A.y):

                                                                                              • Let P be the next vertex after A on path p

                                                                                              • If the first step of p is north, then P = (A.x, A.y + 1)

                                                                                              • Since A'.y > A.y and integers, A'.y ≥ A.y + 1 = P.y

                                                                                              • So A' is still weakly NW of P

                                                                                              • The rest of p goes from P to B', and p' goes from A' to B

                                                                                              • By IH (with smaller path length), these paths intersect

                                                                                              • Any common vertex of (rest of p) and p' is also on p

                                                                                              • If the first step of p is east, then P = (A.x + 1, A.y)

                                                                                              • We need to look at the first step of p' and apply IH appropriately

                                                                                              Case 2 (A'.x < A.x) is symmetric, looking at the first step of p' instead.

                                                                                              The key insight is that the "river" created by path p from A to B' must be crossed by path p' from A' to B, since A' is northwest of A and B' is northwest of B.

                                                                                              Reference: https://math.stackexchange.com/questions/2870640/

                                                                                              theorem LGV1.no_nipats_under_nw (A A' B B' : LatticePoint) (hA : isWeaklyNorthwestOf A' A) (hB : isWeaklyNorthwestOf B' B) :
                                                                                              numNipats2 A A' B' B = 0

                                                                                              Under NW conditions, there are no nipats from (A,A') to (B',B). Label: prop.lgv.jordan-2

                                                                                              Log-Concavity of Binomial Coefficients (Corollary cor.lgv.binom-unimod) #

                                                                                              As an application of the LGV lemma: C(n,k)² ≥ C(n,k-1) · C(n,k+1)

                                                                                              This follows by choosing appropriate lattice points and applying Propositions prop.lgv.2paths.count and prop.lgv.jordan-2.

                                                                                              theorem LGV1.binom_log_concave (n k : ) (hk : 1 k) :
                                                                                              n.choose k * n.choose k n.choose (k - 1) * n.choose (k + 1)

                                                                                              Binomial coefficients are log-concave. (Corollary cor.lgv.binom-unimod) Label: cor.lgv.binom-unimod

                                                                                              For k ≥ 1: C(n,k)² ≥ C(n,k-1) · C(n,k+1)

                                                                                              Note: We require k ≥ 1 because in natural number arithmetic, (0:ℕ) - 1 = 0, so C(n, 0-1) = C(n, 0) = 1, and the inequality 1 ≥ n fails for n ≥ 2. In the mathematical statement, C(n, -1) = 0, so the k=0 case is trivially true.

                                                                                              Proof sketch (algebraic): Using the recurrences

                                                                                              • C(n, k+1) · (k+1) = C(n, k) · (n-k)
                                                                                              • C(n, k) · k = C(n, k-1) · (n-k+1) we can show that C(n,k)² / (C(n,k-1) · C(n,k+1)) = (k+1)(n-k+1) / (k(n-k)). This ratio is ≥ 1 because (k+1)(n-k+1) - k(n-k) = n + 1 ≥ 0.

                                                                                              Combinatorial proof (via LGV): Define lattice points A=(1,0), A'=(0,1), B=(k+1, n-k), B'=(k, n-k+1). Then det(path matrix) = #nipats ≥ 0.

                                                                                              The LGV Lemma for k Paths (Proposition prop.lgv.kpaths.count) #

                                                                                              For k-vertices A = (A₁, ..., Aₖ) and B = (B₁, ..., Bₖ):

                                                                                              det(#paths from Aᵢ to Bⱼ){i,j} = ∑{σ ∈ Sₖ} (-1)^σ · #nipats from A to σ(B)

                                                                                              The proof generalizes the k=2 case by using a sign-reversing involution that picks the first pair of intersecting paths and exchanges their tails.

                                                                                              Helper lemmas for the product rule #

                                                                                              theorem LGV1.pathTuplesFromTo_ncard_eq_prod {k : } (A B : kVertex k) :
                                                                                              (pathTuplesFromTo A B).ncard = i : Fin k, (pathsFromTo (A i) (B i)).ncard

                                                                                              The ncard of pathTuplesFromTo equals the product of ncards of pathsFromTo. This is the "product rule" for path tuples.

                                                                                              theorem LGV1.prod_numPaths_eq_pathTuples_ncard {k : } (A B : kVertex k) :
                                                                                              i : Fin k, numPaths (A i) (B i) = (pathTuplesFromTo A B).ncard

                                                                                              The product of numPaths equals the ncard of pathTuplesFromTo.

                                                                                              def LGV1.pathMatrixK {k : } (A B : kVertex k) :
                                                                                              Matrix (Fin k) (Fin k)

                                                                                              The path count matrix for k source and target vertices.

                                                                                              Equations
                                                                                              Instances For
                                                                                                noncomputable def LGV1.numNipatsK {k : } (A B : kVertex k) (σ : Equiv.Perm (Fin k)) :

                                                                                                Number of nipats from A to σ(B). Defined as the cardinality of the set of non-intersecting path tuples.

                                                                                                Equations
                                                                                                Instances For
                                                                                                  noncomputable def LGV1.numIpatsK {k : } (A B : kVertex k) (σ : Equiv.Perm (Fin k)) :

                                                                                                  Number of ipats from A to σ(B). Defined as the cardinality of the set of intersecting path tuples.

                                                                                                  Equations
                                                                                                  Instances For

                                                                                                    Bridge to LGV2.lean #

                                                                                                    We establish the connection between LGV1's path representation and LGV2's. The key observation is that both represent the same mathematical objects.

                                                                                                    The bridge uses the following chain of equivalences:

                                                                                                    1. LGV1.LatticeStep ≃ LGV.LatticeStep' (both are {east, north})
                                                                                                    2. LGV1.LatticePath ≃ LGV.LatticePath' (both are lists of steps)
                                                                                                    3. LGV1.PathTuple k A B ≃ LGV.LatticePath'Tuple k A B (same structure)
                                                                                                    4. LGV.LatticePath'Tuple k A B ≃ LGV.PathTuple integerLattice k A B (via latticePath'TupleEquiv)

                                                                                                    The intersection property is preserved at each step, so:

                                                                                                    Equivalence between LGV1.LatticeStep and LGV.LatticeStep'.

                                                                                                    This is a definitional equivalence: both types have the same structure (two constructors east and north), so the equivalence is trivial. The equivalence preserves the apply function: latticeStepEquiv_apply.

                                                                                                    Equations
                                                                                                    • One or more equations did not get rendered due to their size.
                                                                                                    Instances For

                                                                                                      The step equivalence preserves the apply function

                                                                                                      Map a LatticePath to a LatticePath'

                                                                                                      Equations
                                                                                                      Instances For

                                                                                                        Map a LatticePath' to a LatticePath

                                                                                                        Equations
                                                                                                        Instances For

                                                                                                          The path mapping preserves endpoints

                                                                                                          The path mapping preserves vertices

                                                                                                          The permutation functions are equal

                                                                                                          theorem LGV1.det_eq_sum_path_tuples {k : } (A B : kVertex k) :
                                                                                                          (pathMatrixK A B).det = σ : Equiv.Perm (Fin k), (Equiv.Perm.sign σ) * i : Fin k, (numPaths (A i) (B (σ i)))

                                                                                                          The determinant equals a sum over path tuples to σ(B). This rewrites det(M) = ∑ σ, sign(σ) * ∏ᵢ M(σ i, i) into det(M) = ∑ σ, sign(σ) * ∏ᵢ numPaths(A i, B(σ i)).

                                                                                                          theorem LGV1.lgv_involution_cancellation {k : } (A B : kVertex k) :
                                                                                                          σ : Equiv.Perm (Fin k), (Equiv.Perm.sign σ) * i : Fin k, (numPaths (A i) (B (σ i))) = σ : Equiv.Perm (Fin k), (Equiv.Perm.sign σ) * (numNipatsK A B σ)

                                                                                                          The key combinatorial lemma: intersecting path tuples cancel via sign-reversing involution.

                                                                                                          This encapsulates the main combinatorial content of the LGV lemma:

                                                                                                          1. ∏ᵢ numPaths(A i, B(σ i)) = #path tuples from A to σ(B) (product rule)
                                                                                                          2. Intersecting path tuples cancel via sign-reversing involution
                                                                                                          3. Only non-intersecting path tuples (nipats) survive

                                                                                                          The sign-reversing involution works by:

                                                                                                          • Finding the first crowded point v on the path with smallest index i
                                                                                                          • Finding the largest index j of a path containing v
                                                                                                          • Exchanging tails of paths pᵢ and pⱼ at v
                                                                                                          • Composing σ with the transposition (i,j) to flip the sign

                                                                                                          By Finset.sum_involution, the intersecting path tuples cancel. Label: pf.prop.lgv.kpaths.count.involution

                                                                                                          theorem LGV1.lgv_k_paths {k : } (A B : kVertex k) :
                                                                                                          (pathMatrixK A B).det = σ : Equiv.Perm (Fin k), (Equiv.Perm.sign σ) * (numNipatsK A B σ)

                                                                                                          The LGV lemma for k paths. (Proposition prop.lgv.kpaths.count)

                                                                                                          For k-vertices A = (A₁, ..., Aₖ) and B = (B₁, ..., Bₖ): det(#paths from Aᵢ to Bⱼ){i,j} = ∑{σ ∈ Sₖ} (-1)^σ · #nipats from A to σ(B)

                                                                                                          The proof uses:

                                                                                                          1. The determinant formula: det(M) = ∑ σ, sign(σ) * ∏ᵢ M(σ i, i)
                                                                                                          2. Rewriting in terms of path tuples to σ(B)
                                                                                                          3. A sign-reversing involution that cancels intersecting path tuples

                                                                                                          Label: prop.lgv.kpaths.count

                                                                                                          Sign-Reversing Involution (Proof Technique) #

                                                                                                          The proofs of the LGV lemmas use a sign-reversing involution on intersecting path tuples. Given an ipat, we:

                                                                                                          1. Find the first intersection point v (first crowded point on the path with smallest index)
                                                                                                          2. Let i be the smallest index of a path containing v
                                                                                                          3. Let j be the largest index of a path containing v (j > i since v is crowded)
                                                                                                          4. Exchange the tails (parts after v) of paths pᵢ and pⱼ
                                                                                                          5. Compose σ with the transposition (i j)

                                                                                                          This involution:

                                                                                                          By Lemma lem.sign.cancel2, the sum over ipats cancels, leaving only nipats.