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 #
LGV1.LatticePoint- A point on the integer lattice ℤ²LGV1.integerLattice- The integer lattice as a MathlibDigraphLGV1.LatticePath- A path on the integer lattice using only east (right) and north (up) stepsLGV1.LatticePath.stepCount- The total number of steps in a pathLGV1.LatticePath.eastStepCount- The number of east steps in a pathLGV1.PathTuple- A k-tuple of lattice paths from k source vertices to k target verticesLGV1.PathTuple.isNonIntersecting- Property that no two paths share a vertex (nipat)LGV1.PathTuple.isIntersecting- Property that some two paths share a vertex (ipat)LGV1.kVertex- A k-tuple of lattice points
Main results #
latticePath_count- Number of paths from (a,b) to (c,d) equals C(c+d-a-b, c-a) (Proposition prop.lgv.1-paths.ct)lgv_two_paths- LGV lemma for k=2: determinant equals #nipats to (B,B') - #nipats to (B',B) (Proposition prop.lgv.2paths.count)baby_jordan_curve- Paths from A to B' and A' to B must intersect under NW conditions (Proposition prop.lgv.jordan-2)binom_log_concave- C(n,k)² ≥ C(n,k-1)·C(n,k+1) (Corollary cor.lgv.binom-unimod)lgv_k_paths- General LGV lemma: determinant equals signed sum of nipat counts (Proposition prop.lgv.kpaths.count)
References #
- Source: AlgebraicCombinatorics/tex/Determinants/LGV1.tex (sec.det.comb.lgv)
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:
- LGV1.lean: Uses Mathlib's
Digraphtype andLatticePath(list of steps: east/north) - LGV2.lean: Uses
LGV.SimpleDigraph(custom structure with irreflexivity) andSimpleDigraph.Path(list of vertices)
The integer lattice definitions have equivalent adjacency relations:
- LGV1:
integerLattice_adj_iff p q : integerLattice.Adj p q ↔ q = (p.1 + 1, p.2) ∨ q = (p.1, p.2 + 1) - LGV2:
integerLattice_arc_iff u v : integerLattice.arc u v ↔ v = (u.1 + 1, u.2) ∨ v = (u.1, u.2 + 1)
LatticeStep and LatticePath equivalence #
Both files define a LatticeStep type for representing east/north steps:
LGV1.LatticeStep(this file)LGV.LatticeStep'(in LGV2.lean)
These are equivalent types, with explicit conversion functions:
latticeStepEquiv : LatticeStep ≃ LGV.LatticeStep'latticePathToLatticePath' : LatticePath → LGV.LatticePath'latticePath'ToLatticePath : LGV.LatticePath' → LatticePath
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".
A point on the integer lattice ℤ². The vertices of the integer lattice are pairs of integers. Label: def.lgv.lattice
Equations
- LGV1.LatticePoint = (ℤ × ℤ)
Instances For
The x-coordinate of a lattice point.
Instances For
The y-coordinate of a lattice point.
Instances For
The coordinate sum of a lattice point (x + y).
Instances For
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:
- An east step: q = (p.1 + 1, p.2)
- A north step: q = (p.1, p.2 + 1)
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
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
- LGV1.integerLattice = { Adj := LGV1.IntegerLatticeAdj }
Instances For
Decidability of adjacency in the integer lattice.
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
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 : LatticeStep ≃ LGV.LatticeStep'.
Label: eq.def.lgv.lattice.east, eq.def.lgv.lattice.north
- east : LatticeStep
- north : LatticeStep
Instances For
Equations
- LGV1.instReprLatticeStep.repr LGV1.LatticeStep.east prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LGV1.LatticeStep.east")).group prec✝
- LGV1.instReprLatticeStep.repr LGV1.LatticeStep.north prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LGV1.LatticeStep.north")).group prec✝
Instances For
Equations
- LGV1.instReprLatticeStep = { reprPrec := LGV1.instReprLatticeStep.repr }
Equations
- LGV1.instFintypeLatticeStep = { elems := { val := ↑LGV1.LatticeStep.enumList, nodup := LGV1.LatticeStep.enumList_nodup }, complete := LGV1.instFintypeLatticeStep._proof_1 }
Apply a step to a lattice point.
Equations
Instances For
A step corresponds to an arc in the integer lattice digraph.
A lattice path is a list of steps.
This type is equivalent to LGV.LatticePath' defined in LGV2.lean.
Conversion functions are provided:
latticePathToLatticePath' : LatticePath → LGV.LatticePath'latticePath'ToLatticePath : LGV.LatticePath' → LatticePathlatticePathToLatticePath'_endpointproves endpoint preservation
Equations
Instances For
The endpoint of a path starting from a given point.
Equations
- path.endpoint start = List.foldl (fun (p : LGV1.LatticePoint) (s : LGV1.LatticeStep) => s.apply p) start path
Instances For
Simp lemma: the endpoint of an empty path is the start.
Simp lemma: the endpoint of a cons path applies the first step then continues.
The endpoint of appended paths: endpoint(p1 ++ p2, A) = endpoint(p2, endpoint(p1, A)).
Simp lemma: the step count of a cons path is one more than the rest.
The number of east steps in a path.
Equations
- path.eastStepCount = List.count LGV1.LatticeStep.east path
Instances For
Simp lemma: the east step count of an empty path is 0.
Simp lemma: the east step count of a path starting with an east step.
Simp lemma: the east step count of a path starting with a north step.
The number of north steps in a path.
Equations
- path.northStepCount = List.count LGV1.LatticeStep.north path
Instances For
Simp lemma: the north step count of an empty path is 0.
Simp lemma: the north step count of a path starting with a north step.
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
- path.vertices start = (start :: List.scanl (fun (p : LGV1.LatticePoint) (s : LGV1.LatticeStep) => s.apply p) start path).tail
Instances For
Check if a path goes from point a to point b.
Equations
- path.isPathFromTo a b = (path.endpoint a = b)
Instances For
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.
If head goes A→v and tail goes v→B, then head ++ tail goes A→B.
Each step increases the coordinate sum by 1.
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:
- C(c+d-a-b, c-a) if c+d ≥ a+b (i.e., d-b ≥ a-c)
- 0 if c+d < a+b
This follows from:
- Observation 1: Each path has exactly c+d-a-b steps
- Observation 2: Each path has exactly c-a east steps
- Observation 3: A path with these counts from (a,b) must end at (c,d)
Helper: The x-coordinate of the endpoint equals start.x + eastStepCount.
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
- LGV1.pathsFromTo a b = {path : LGV1.LatticePath | path.isPathFromTo a b}
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:
- Lattice paths from (0,0) to (m,n) with m east steps and n north steps
- Subsets S ⊆ {0, 1, ..., m+n-1} of cardinality m
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.
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.
A k-vertex is a k-tuple of lattice points. Label: def.lgv.path-tups (a)
Equations
- LGV1.kVertex k = (Fin k → LGV1.LatticePoint)
Instances For
Permute a k-vertex by a permutation σ. Label: def.lgv.path-tups (b)
Instances For
A path tuple from k-vertex A to k-vertex B. Label: def.lgv.path-tups (c)
- paths : Fin k → LatticePath
The i-th path in the tuple
- valid (i : Fin k) : (self.paths i).isPathFromTo (A i) (B i)
Each path goes from A_i to B_i
Instances For
The set of vertices visited by a path in a path tuple.
Equations
- pt.verticesOf i = {p : LGV1.LatticePoint | p ∈ (pt.paths i).vertices (A i)}
Instances For
A path tuple is non-intersecting (nipat) if no two distinct paths share a vertex. Label: def.lgv.path-tups (d)
Equations
- pt.isNonIntersecting = ∀ (i j : Fin k), i ≠ j → Disjoint (pt.verticesOf i) (pt.verticesOf j)
Instances For
A path tuple is intersecting (ipat) if it is not non-intersecting. Label: def.lgv.path-tups (e)
Equations
Instances For
The set of all path tuples from A to B.
Equations
Instances For
The set of all non-intersecting path tuples (nipats) from A to B.
Equations
- LGV1.nipatsFromTo A B = {pt : LGV1.PathTuple k A B | pt.isNonIntersecting}
Instances For
The set of all intersecting path tuples (ipats) from A to B.
Equations
- LGV1.ipatsFromTo A B = {pt : LGV1.PathTuple k A B | pt.isIntersecting}
Instances For
The set of path tuples from A to B is finite (follows from finiteness of paths).
The set of nipats from A to B is finite (subset of finite set).
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:
- Find the smallest path index i that contains a "crowded" vertex (shared with another path)
- Find the first crowded vertex v on path i (by position in the path)
- Find the largest path index j that contains v (j > i since v is crowded)
- Exchange the tails of paths i and j at v
- Compose σ with the transposition (i j) to flip the sign
This maps (σ, ipat) to (σ * swap(i,j), ipat') where:
- sign(σ * swap(i,j)) = -sign(σ) (transposition flips sign)
- ipat' is still intersecting (v is still a crowded point)
- The involution is its own inverse (applying twice returns the original)
Since every ipat has at least one crowded point, the involution has no fixed points.
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
The set of path indices that have a crowded vertex (shared with another path)
Equations
- pt.crowdedPathIndices = {i : Fin k | ∃ (j : Fin k), i ≠ j ∧ ¬Disjoint (pt.verticesOf i) (pt.verticesOf j)}
Instances For
An intersecting path tuple has nonempty crowdedPathIndices
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
- pt.minCrowdedPathIndex hip = pt.crowdedPathIndices.min' ⋯
Instances For
The min crowded path index is in crowdedPathIndices
The min crowded path index is minimal
The crowded vertices on a specific path: vertices that appear in multiple paths
Equations
- pt.crowdedVerticesOnPath i = {v : LGV1.LatticePoint | v ∈ pt.verticesOf i ∧ ∃ (j : Fin k), i ≠ j ∧ v ∈ pt.verticesOf j}
Instances For
The crowded vertices on the min crowded path are nonempty
The vertices on a path form a finite set
A path tuple with permutation: pairs (σ, pt) where pt is a path tuple from A to σ(B)
Equations
- LGV1.pathTupleWithPerm A B = ((σ : Equiv.Perm (Fin k)) × LGV1.PathTuple k A (B.permute σ))
Instances For
The sign of a path tuple with permutation
Equations
- LGV1.signOfPathTupleWithPerm sp = ↑(Equiv.Perm.sign sp.fst)
Instances For
The set of all ipats with permutation
Equations
- LGV1.ipatWithPerm A B = {sp : LGV1.pathTupleWithPerm A B | sp.snd.isIntersecting}
Instances For
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.
The path count matrix for two source and target points.
Equations
- LGV1.pathMatrix2 A A' B B' = !![↑(LGV1.numPaths A B), ↑(LGV1.numPaths A B'); ↑(LGV1.numPaths A' B), ↑(LGV1.numPaths A' B')]
Instances For
Number of nipats from (A,A') to (B,B'). Defined as the cardinality of the set of non-intersecting path tuples.
Equations
- LGV1.numNipats2 A A' B B' = (LGV1.nipatsFromTo ![A, A'] ![B, B']).ncard
Instances For
Helper: the 2x2 determinant expands to the difference of products.
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).
- path0 : LatticePath
The first path
- path1 : LatticePath
The second path
- toBB' : Bool
Whether this is to (B,B') (true) or (B',B) (false)
Path 0 goes from A to its destination
Path 1 goes from A' to its destination
Instances For
The sign of a signed path tuple.
Instances For
Check if a signed path tuple is intersecting (the two paths share a vertex).
Instances For
The set of all signed path tuples from (A,A') to (B,B') or (B',B).
Equations
- LGV1.signedPathTuples2 A A' B B' = Set.univ
Instances For
The set of intersecting signed path tuples.
Equations
- LGV1.signedIpats2 A A' B B' = {spt : LGV1.SignedPathTuple2 A A' B B' | spt.isIntersecting}
Instances For
The set of non-intersecting signed path tuples.
Equations
- LGV1.signedNipats2 A A' B B' = {spt : LGV1.SignedPathTuple2 A A' B B' | ¬spt.isIntersecting}
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.
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
- LGV1.firstIntersectionIdx spt _h = List.findIdx (fun (v : LGV1.LatticePoint) => decide (v ∈ spt.path1.vertices A')) (spt.path0.vertices A)
Instances For
The index of the first intersection point is valid (less than the length of path0's vertices).
The first intersection point of two intersecting paths. This is the first vertex (by index in path0) that is shared by both paths.
Equations
- LGV1.firstIntersection spt h = (spt.path0.vertices A)[LGV1.firstIntersectionIdx spt h]
Instances For
The first intersection point is in path0's vertices.
The first intersection point is in path1's vertices.
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:
- The head of path0 (indices 0..idx) is unchanged
- v is at index idx in both the original and swapped paths
- No earlier vertex in path0's head is in path1's vertices (by minimality of findIdx)
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 #
The sign-reversing involution on intersecting signed path tuples. Given an ipat, we:
- Find the first intersection point v (deterministically, by index in path0)
- Exchange the tails of the two paths at v
- 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
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:
- The head of path0 (vertices 0..idx) is unchanged
- v is at index idx in both the original and new path0's vertices
- No earlier vertex in head0 is in path1's vertices (by minimality)
This lemma is essential for proving that the involution is involutive.
The involution is its own inverse.
After applying the involution twice, we get back the original path tuple. The key insight is that:
- The first intersection point v is preserved after swapping tails (by firstIntersection_preserved)
- The indices idx0 and idx1 are also preserved
- Exchanging tails twice returns the original paths (take_append_drop)
- toBB' flips twice (!!b = b)
Proof strategy:
- Use
firstIntersection_preservedto show v' = v - Use
findIdx_beq_eq_firstIntersectionIdxandfindIdx_beq_newPath1_eqto show idx0' = idx0 and idx1' = idx1 - Apply List.take_append_drop to reconstruct the original paths
The set of signed path tuples is finite.
The set of non-intersecting signed path tuples is finite.
The set of intersecting signed path tuples is finite.
The sum over all signed path tuples equals the difference of path tuple counts.
The sum over intersecting path tuples is zero (by sign-reversing involution).
The sum over non-intersecting path tuples equals the difference of nipat counts.
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:
- Expand the determinant: det(M) = #paths(A→B)·#paths(A'→B') - #paths(A→B')·#paths(A'→B)
- By product rule, this equals #pathTuples(→(B,B')) - #pathTuples(→(B',B))
- Define signed path tuples with sign +1 for (B,B') and -1 for (B',B)
- The involution exchanges tails at the first intersection, flipping the sign
- 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:
- x(A') ≤ x(A) and y(A') ≥ y(A)
- x(B') ≤ x(B) and y(B') ≥ y(B)
Under these conditions, #nipats from (A,A') to (B',B) = 0.
Point p is weakly northwest of point q.
Instances For
Helper: If a path exists from a to b, then a.1 ≤ b.1 and a.2 ≤ b.2.
Helper: Start vertex is in vertices.
Helper: Vertices of a cons path.
Helper: Vertices of tail are contained in vertices of full path.
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/
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.
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 #
The ncard of pathTuplesFromTo equals the product of ncards of pathsFromTo. This is the "product rule" for path tuples.
The product of numPaths equals the ncard of pathTuplesFromTo.
The path count matrix for k source and target vertices.
Equations
- LGV1.pathMatrixK A B = Matrix.of fun (i j : Fin k) => ↑(LGV1.numPaths (A i) (B j))
Instances For
Number of nipats from A to σ(B). Defined as the cardinality of the set of non-intersecting path tuples.
Equations
- LGV1.numNipatsK A B σ = (LGV1.nipatsFromTo A (B.permute σ)).ncard
Instances For
Number of ipats from A to σ(B). Defined as the cardinality of the set of intersecting path tuples.
Equations
- LGV1.numIpatsK A B σ = (LGV1.ipatsFromTo A (B.permute σ)).ncard
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:
- LGV1.LatticeStep ≃ LGV.LatticeStep' (both are {east, north})
- LGV1.LatticePath ≃ LGV.LatticePath' (both are lists of steps)
- LGV1.PathTuple k A B ≃ LGV.LatticePath'Tuple k A B (same structure)
- LGV.LatticePath'Tuple k A B ≃ LGV.PathTuple integerLattice k A B (via latticePath'TupleEquiv)
The intersection property is preserved at each step, so:
- ipatsFromTo A B ≃ ipatSet integerLattice A B
- numIpatsK A B σ = (ipatFinset ... A (permuteKVertex σ B)).card
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
- LGV1.latticePathToLatticePath' path = List.map (⇑LGV1.latticeStepEquiv) path
Instances For
Map a LatticePath' to a LatticePath
Equations
- LGV1.latticePath'ToLatticePath path = List.map (⇑LGV1.latticeStepEquiv.symm) path
Instances For
The path mapping preserves endpoints
The path mapping preserves vertices
The permutation functions are equal
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)).
The key combinatorial lemma: intersecting path tuples cancel via sign-reversing involution.
This encapsulates the main combinatorial content of the LGV lemma:
- ∏ᵢ numPaths(A i, B(σ i)) = #path tuples from A to σ(B) (product rule)
- Intersecting path tuples cancel via sign-reversing involution
- 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
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:
- The determinant formula: det(M) = ∑ σ, sign(σ) * ∏ᵢ M(σ i, i)
- Rewriting in terms of path tuples to σ(B)
- 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:
- Find the first intersection point v (first crowded point on the path with smallest index)
- Let i be the smallest index of a path containing v
- Let j be the largest index of a path containing v (j > i since v is crowded)
- Exchange the tails (parts after v) of paths pᵢ and pⱼ
- Compose σ with the transposition (i j)
This involution:
- Maps ipats to ipats
- Reverses sign (since composing with a transposition changes parity)
- Has no fixed points
- Is its own inverse (applying twice returns the original)
By Lemma lem.sign.cancel2, the sum over ipats cancels, leaving only nipats.