1-- Brouwer's fixed point theorem for the unit cube: every continuous self-map2-- of [0, 1]ⁿ has a fixed point, from Kuhn's lemma on finer and finer grids3-- Part of Cumulus, the Grace17 mathematics library.4-- Copyright (c) 2026 Grant Passmore.5-- Released under the Apache License 2.0. See LICENSE in the repository root.6-- Authors: Grant Passmore78module Grace.Cumulus.FixedPoint.BrouwerCube9import BrouwerGrid1011-- At every grid level Kuhn's lemma gives a cell carrying, in each direction,12-- a point labelled 0 and a point labelled 1; one such cell is chosen.13defchoose kuhnCell (n f : Set, k : Nat) (base ∈ functionSpace(n, ℕ)) :=14 (∀ j ∈ n, base(j) < doubling(k)) ∧15∀ i ∈ n, ∃ u ∈ functionSpace(n, succ(doubling(k))), ∃ v ∈ functionSpace(n, succ(doubling(k))),16 (∀ j ∈ n, LessEqual(base(j), u(j)) ∧ LessEqual(u(j), add(base(j), (1:N)))) ∧ (∀ j ∈ n, LessEqual(base(j), v(j)) ∧ LessEqual(v(j), add(base(j), (1:N)))) ∧17 gridLabels(k, n, f)(u)(i) = (0:N) ∧ gridLabels(k, n, f)(v)(i) ≠ (0:N)18requires nNat : Nat(n)19proof20obtain⟨q, qNat, shape⟩:= doubling.successor at k21have labFunction := gridLabels.function at k, n, f22rw shape at labFunction23have zeroWall := gridLabels.zero_wall at k, n, q, f with shape24have fullWall := gridLabels.full_wall at k, n, q, f with shape25obtain⟨base, baseIn, bounds, pairs⟩:= kuhn.lemma at q, n, gridLabels(k, n, f), functionSpace(n, succ(succ(0))) with labFunction, zeroWall, fullWall26witness base27refine⟨baseIn, ?_, ?_⟩28 . intros j hj29have bound := bounds with hj30rw← shape at bound31exact bound32 . intros i hi33obtain⟨u, hu, v, hv, uCell, vCell, uLabel, vLabel⟩:= pairs with hi34rw← shape at hu35rw← shape at hv36witness u37constructor38 . exact hu39 . witness v40constructor41 . exact hv42 . exact⟨uCell, vCell, uLabel, vLabel⟩4344theorem kuhnCell.grid (n : Nat) (f : Set) (k : Nat)45shows kuhnCell(n, f, k) ∈ functionSpace(n, succ(doubling(k)))46proof47obtain⟨baseIn, bounds, pairs⟩:= kuhnCell.selected at n, f, k48have baseFunction := functionSpace.mem_iff.forward with baseIn49apply functionSpace.mem_iff.backward50apply FunctionBetween.codomain_of_values with baseFunction51intros j hj52have bound := bounds with hj53have valueIn := FunctionBetween.apply_closed with baseFunction, hj54have valueNat := nat.omega.backward with valueIn55have less : kuhnCell(n, f, k)(j) < succ(doubling(k)) byarith56exact membership.of_Less with less5758defchoose lowPoint (n f : Set, k : Nat, i : Set) (u ∈ functionSpace(n, succ(doubling(k)))) :=59 (∀ j ∈ n, LessEqual(kuhnCell(n, f, k)(j), u(j)) ∧ LessEqual(u(j), add(kuhnCell(n, f, k)(j), (1:N)))) ∧ gridLabels(k, n, f)(u)(i) = (0:N)60requires nNat : Nat(n)61requires hi : i ∈ n62proof63obtain⟨baseIn, bounds, pairs⟩:= kuhnCell.selected at n, f, k64obtain⟨u, hu, v, hv, uCell, vCell, uLabel, vLabel⟩:= pairs with hi65witness u66refine⟨hu, uCell, uLabel⟩6768defchoose highPoint (n f : Set, k : Nat, i : Set) (v ∈ functionSpace(n, succ(doubling(k)))) :=69 (∀ j ∈ n, LessEqual(kuhnCell(n, f, k)(j), v(j)) ∧ LessEqual(v(j), add(kuhnCell(n, f, k)(j), (1:N)))) ∧ gridLabels(k, n, f)(v)(i) ≠ (0:N)70requires nNat : Nat(n)71requires hi : i ∈ n72proof73obtain⟨baseIn, bounds, pairs⟩:= kuhnCell.selected at n, f, k74obtain⟨u, hu, v, hv, uCell, vCell, uLabel, vLabel⟩:= pairs with hi75witness v76refine⟨hv, vCell, vLabel⟩7778-- The chosen cells, low points, and high points, scaled into the cube.79defgraph basePoints (n f : Set) (k ∈ ℕ) : functionSpace(n, ℝ) := gridPoint(k, n)(kuhnCell(n, f, k))80requires nNat : Nat(n)81proof82have kNat := nat.omega.backward with member83have gridFunction := gridPoint.function at k, n with*84have pointIn := FunctionBetween.apply_closed with gridFunction, (kuhnCell.grid at n, f, k)85have parts := unitCube.membership.forward with pointIn86exact parts.left8788defgraph lowPoints (n f i : Set) (k ∈ ℕ) : functionSpace(n, ℝ) := gridPoint(k, n)(lowPoint(n, f, k, i))89requires nNat : Nat(n)90requires hi : i ∈ n91proof92have kNat := nat.omega.backward with member93have gridFunction := gridPoint.function at k, n with*94have selected := lowPoint.selected at n, f, k, i with*95have pointIn := FunctionBetween.apply_closed with gridFunction, selected.left96have parts := unitCube.membership.forward with pointIn97exact parts.left9899defgraph highPoints (n f i : Set) (k ∈ ℕ) : functionSpace(n, ℝ) := gridPoint(k, n)(highPoint(n, f, k, i))100requires nNat : Nat(n)101requires hi : i ∈ n102proof103have kNat := nat.omega.backward with member104have gridFunction := gridPoint.function at k, n with*105have selected := highPoint.selected at n, f, k, i with*106have pointIn := FunctionBetween.apply_closed with gridFunction, selected.left107have parts := unitCube.membership.forward with pointIn108exact parts.left109110theorem basePoints.sequence (n : Nat) (f : Set)111shows PointSequence(basePoints(n, f), n)112proof113exact basePoints.function at n, f with*114115theorem basePoints.in_cube (n : Nat) (f k : Set)116requires hk : k ∈ ℕ117shows basePoints(n, f)(k) ∈ unitCube(n)118proof119have kNat := nat.omega.backward with hk120have value := basePoints.value at n, f, k with*121rw value122have gridFunction := gridPoint.function at k, n with*123exact FunctionBetween.apply_closed with gridFunction, (kuhnCell.grid at n, f, k)124125theorem lowPoints.sequence (n : Nat) (f i : Set)126requires hi : i ∈ n127shows PointSequence(lowPoints(n, f, i), n)128proof129exact lowPoints.function at n, f, i with*130131theorem lowPoints.in_cube (n : Nat) (f i k : Set)132requires hi : i ∈ n133requires hk : k ∈ ℕ134shows lowPoints(n, f, i)(k) ∈ unitCube(n)135proof136have kNat := nat.omega.backward with hk137have value := lowPoints.value at n, f, i, k with*138rw value139have gridFunction := gridPoint.function at k, n with*140have selected := lowPoint.selected at n, f, k, i with*141exact FunctionBetween.apply_closed with gridFunction, selected.left142143theorem highPoints.sequence (n : Nat) (f i : Set)144requires hi : i ∈ n145shows PointSequence(highPoints(n, f, i), n)146proof147exact highPoints.function at n, f, i with*148149theorem highPoints.in_cube (n : Nat) (f i k : Set)150requires hi : i ∈ n151requires hk : k ∈ ℕ152shows highPoints(n, f, i)(k) ∈ unitCube(n)153proof154have kNat := nat.omega.backward with hk155have value := highPoints.value at n, f, i, k with*156rw value157have gridFunction := gridPoint.function at k, n with*158have selected := highPoint.selected at n, f, k, i with*159exact FunctionBetween.apply_closed with gridFunction, selected.left160161-- The low and high points stay within one grid width of the cell's base162-- point in every coordinate.163theorem lowPoints.close (n : Nat) (f i k j : Set)164requires hi : i ∈ n165requires hk : k ∈ ℕ166requires hj : j ∈ n167shows dist(lowPoints(n, f, i)(k)(j), basePoints(n, f)(k)(j)) ≤ (dyadicRadius(k):R)168proof169have kNat := nat.omega.backward with hk170have lowValue := lowPoints.value at n, f, i, k with*171have baseValue := basePoints.value at n, f, k with*172rw [lowValue, baseValue]173have selected := lowPoint.selected at n, f, k, i with*174have uIn := selected.left175have cell := selected.right.left with hj176have baseIn := kuhnCell.grid at n, f, k177have gridU := gridPoint.coordinate with uIn, hj178have gridB := gridPoint.coordinate with baseIn, hj179rw [gridU, gridB]180have uFunction := functionSpace.mem_iff.forward with uIn181have bFunction := functionSpace.mem_iff.forward with baseIn182have close := scaledPoint.close with bFunction, uFunction, hj, cell.left, cell.right183have uValue := scaledPoint.value at k, n, lowPoint(n, f, k, i), j with*184have bValue := scaledPoint.value at k, n, kuhnCell(n, f, k), j with*185 rwa [uValue, bValue] at close186187theorem highPoints.close (n : Nat) (f i k j : Set)188requires hi : i ∈ n189requires hk : k ∈ ℕ190requires hj : j ∈ n191shows dist(highPoints(n, f, i)(k)(j), basePoints(n, f)(k)(j)) ≤ (dyadicRadius(k):R)192proof193have kNat := nat.omega.backward with hk194have highValue := highPoints.value at n, f, i, k with*195have baseValue := basePoints.value at n, f, k with*196rw [highValue, baseValue]197have selected := highPoint.selected at n, f, k, i with*198have vIn := selected.left199have cell := selected.right.left with hj200have baseIn := kuhnCell.grid at n, f, k201have gridV := gridPoint.coordinate with vIn, hj202have gridB := gridPoint.coordinate with baseIn, hj203rw [gridV, gridB]204have vFunction := functionSpace.mem_iff.forward with vIn205have bFunction := functionSpace.mem_iff.forward with baseIn206have close := scaledPoint.close with bFunction, vFunction, hj, cell.left, cell.right207have vValue := scaledPoint.value at k, n, highPoint(n, f, k, i), j with*208have bValue := scaledPoint.value at k, n, kuhnCell(n, f, k), j with*209 rwa [vValue, bValue] at close210211-- Along a subsequence on which the base points converge, so do the low and212-- high points, since the grid widths shrink to nothing.213theorem closePoints.converges (n : Nat) (Y X s L j : Set)214requires ySequence : PointSequence(Y, n)215requires xSequence : PointSequence(X, n)216requires yCube : ∀ k ∈ ℕ, Y(k) ∈ unitCube(n)217requires increasing : StrictlyIncreasingNat(s)218requires hj : j ∈ n219requires LReal : L ∈ ℝ220requires close : ∀ k ∈ ℕ, dist(Y(k)(j), X(k)(j)) ≤ (dyadicRadius(k):R)221requires converges : ConvergesTo(coordinateSequence(pointSubsequence(X, n, s), n, j), L)222shows ConvergesTo(coordinateSequence(pointSubsequence(Y, n, s), n, j), L)223proof224have xSub := pointSubsequence.sequence with xSequence, increasing225have ySub := pointSubsequence.sequence with ySequence, increasing226have u := coordinateSequence.sequence with xSub, hj227have v := coordinateSequence.sequence with ySub, hj228have sFunction := increasing.left229apply ConvergesTo.of_asymptotically_close with converges230unfold AsymptoticallyClose231refine⟨u, v, ?_⟩232intros E EIn Epos233obtain⟨N, NIn, tail⟩:= dyadicRadii.eventually_below with Epos234witness N235constructor236 . exact NIn237 . intros m hm bound238have mNat := nat.omega.backward with hm239have NNat := nat.omega.backward with NIn240have index := StrictlyIncreasingNat.index_le_value at s, m with increasing241have sm := FunctionBetween.apply_closed with sFunction, hm242have smNat := nat.omega.backward with sm243have far : N≤ s(m) byarith244have small := tail with sm, far245have vValue := coordinateSequence.value at pointSubsequence(Y, n, s), n, j, m with ySub, hj246have uValue := coordinateSequence.value at pointSubsequence(X, n, s), n, j, m with xSub, hj247have ySubValue := pointSubsequence.value with ySequence, increasing, hm248have xSubValue := pointSubsequence.value with xSequence, increasing, hm249rw [vValue, uValue, ySubValue, xSubValue]250have near := close with sm251have yIn := yCube with sm252have yReal := unitCube.coordinate_real with yIn, hj253have xPoint := PointSequence.point with xSequence, sm254have xReal := FunctionBetween.apply_closed with xPoint, hj255apply openBall.membership.backward256constructor257 . exact yReal258 . linarith259260theorem lowPoints.coordinate (n : Nat) (f i s m j : Set)261requires hi : i ∈ n262requires increasing : StrictlyIncreasingNat(s)263requires hm : m ∈ ℕ264requires hj : j ∈ n265shows coordinateSequence(pointSubsequence(lowPoints(n, f, i), n, s), n, j)(m) = scaledPoint(s(m), n, lowPoint(n, f, s(m), i))(j)266proof267have lowSeq := lowPoints.sequence at n, f, i with hi268have lowSub := pointSubsequence.sequence with lowSeq, increasing269have sm := FunctionBetween.apply_closed with increasing.left, hm270have smNat := nat.omega.backward with sm271have value := coordinateSequence.value at pointSubsequence(lowPoints(n, f, i), n, s), n, j, m with lowSub, hj272have subValue := pointSubsequence.value with lowSeq, increasing, hm273have lowValue := lowPoints.value at n, f, i, s(m) with*274rw [value, subValue, lowValue]275have selected := lowPoint.selected at n, f, s(m), i with*276have gridValue := gridPoint.value at s(m), n, lowPoint(n, f, s(m), i) with*277rw gridValue278rfl279280theorem highPoints.coordinate (n : Nat) (f i s m j : Set)281requires hi : i ∈ n282requires increasing : StrictlyIncreasingNat(s)283requires hm : m ∈ ℕ284requires hj : j ∈ n285shows coordinateSequence(pointSubsequence(highPoints(n, f, i), n, s), n, j)(m) = scaledPoint(s(m), n, highPoint(n, f, s(m), i))(j)286proof287have highSeq := highPoints.sequence at n, f, i with hi288have highSub := pointSubsequence.sequence with highSeq, increasing289have sm := FunctionBetween.apply_closed with increasing.left, hm290have smNat := nat.omega.backward with sm291have value := coordinateSequence.value at pointSubsequence(highPoints(n, f, i), n, s), n, j, m with highSub, hj292have subValue := pointSubsequence.value with highSeq, increasing, hm293have highValue := highPoints.value at n, f, i, s(m) with*294rw [value, subValue, highValue]295have selected := highPoint.selected at n, f, s(m), i with*296have gridValue := gridPoint.value at s(m), n, highPoint(n, f, s(m), i) with*297rw gridValue298rfl299300theorem imageSequence.coordinate (n : Nat) (f Y s m j : Set)301requires map : CubeMap(f, n)302requires ySequence : PointSequence(Y, n)303requires yCube : ∀ k ∈ ℕ, Y(k) ∈ unitCube(n)304requires increasing : StrictlyIncreasingNat(s)305requires hm : m ∈ ℕ306requires hj : j ∈ n307shows coordinateSequence(imageSequence(f, pointSubsequence(Y, n, s), n), n, j)(m) = f(Y(s(m)))(j)308proof309have ySub := pointSubsequence.sequence with ySequence, increasing310have subCube : ∀ k ∈ ℕ, pointSubsequence(Y, n, s)(k) ∈ unitCube(n)311intros k hk312have subValue := pointSubsequence.value with ySequence, increasing, hk313rw subValue314exact yCube with (FunctionBetween.apply_closed with increasing.left, hk)315have imageSeq := imageSequence.sequence with map, ySub, subCube316have value := coordinateSequence.value at imageSequence(f, pointSubsequence(Y, n, s), n), n, j, m with imageSeq, hj317have imageValue := imageSequence.value with map, ySub, subCube, hm318have subValue := pointSubsequence.value with ySequence, increasing, hm319rw [value, imageValue, subValue]320rfl321322-- Brouwer's fixed point theorem for the cube.323theorem brouwer.cube (n : Nat) (f : Set)324requires continuous : CubeContinuous(f, n)325shows∃ x ∈ unitCube(n), f(x) = x326proof327have map := continuous.left328have fFunction := CubeMap.function with map329have baseSeq := basePoints.sequence at n, f330have baseCube : ∀ k ∈ ℕ, basePoints(n, f)(k) ∈ unitCube(n)331intros k hk332exact basePoints.in_cube with hk333obtain⟨s, increasing, x, xIn, limits⟩:= unitCube.bolzano_weierstrass with baseSeq, baseCube334have xFunction := unitCube.point with xIn335have fxIn := FunctionBetween.apply_closed with fFunction, xIn336have fxFunction := unitCube.point with fxIn337witness x338constructor339 . exact xIn340 . apply FunctionBetween.ext with fxFunction, xFunction341intros i hi342have xiReal := FunctionBetween.apply_closed with xFunction, hi343have fxiReal := FunctionBetween.apply_closed with fxFunction, hi344have lowSeq := lowPoints.sequence at n, f, i with hi345have lowCube : ∀ k ∈ ℕ, lowPoints(n, f, i)(k) ∈ unitCube(n)346intros k hk347exact lowPoints.in_cube with hi, hk348have lowSub := pointSubsequence.sequence with lowSeq, increasing349have lowSubCube : ∀ k ∈ ℕ, pointSubsequence(lowPoints(n, f, i), n, s)(k) ∈ unitCube(n)350intros k hk351have subValue := pointSubsequence.value with lowSeq, increasing, hk352rw subValue353exact lowCube with (FunctionBetween.apply_closed with increasing.left, hk)354have lowConverges : ∀ j ∈ n, ConvergesTo(coordinateSequence(pointSubsequence(lowPoints(n, f, i), n, s), n, j), x(j))355intros j hj356have xjReal := FunctionBetween.apply_closed with xFunction, hj357have close : ∀ k ∈ ℕ, dist(lowPoints(n, f, i)(k)(j), basePoints(n, f)(k)(j)) ≤ (dyadicRadius(k):R)358intros k hk359exact lowPoints.close with hi, hk, hj360exact closePoints.converges with lowSeq, baseSeq, lowCube, increasing, hj, xjReal, close, (limits with hj)361have imageLow := CubeContinuous.sequential with continuous, lowSub, lowSubCube, xIn, lowConverges362have lowLimit := lowConverges with hi363have imageLowLimit := imageLow with hi364have lowOrdered : PointwiseLE(coordinateSequence(pointSubsequence(lowPoints(n, f, i), n, s), n, i), coordinateSequence(imageSequence(f, pointSubsequence(lowPoints(n, f, i), n, s), n), n, i))365unfold PointwiseLE366intros m hm367have sm := FunctionBetween.apply_closed with increasing.left, hm368have smNat := nat.omega.backward with sm369haveleft:= lowPoints.coordinate at n, f, i, s, m, i with hi, increasing, hm, hi370haveright:= imageSequence.coordinate with map, lowSeq, lowCube, increasing, hm, hi371have lowValue := lowPoints.value at n, f, i, s(m) with*372rw [left, right, lowValue]373have selected := lowPoint.selected at n, f, s(m), i with*374have le := gridLabels.low_le with map, selected.left, hi, selected.right.right375exact realLe.characterization.forward with le376have lower := ConvergesTo.le_of_le_terms with lowLimit, imageLowLimit, lowOrdered377have highSeq := highPoints.sequence at n, f, i with hi378have highCube : ∀ k ∈ ℕ, highPoints(n, f, i)(k) ∈ unitCube(n)379intros k hk380exact highPoints.in_cube with hi, hk381have highSub := pointSubsequence.sequence with highSeq, increasing382have highSubCube : ∀ k ∈ ℕ, pointSubsequence(highPoints(n, f, i), n, s)(k) ∈ unitCube(n)383intros k hk384have subValue := pointSubsequence.value with highSeq, increasing, hk385rw subValue386exact highCube with (FunctionBetween.apply_closed with increasing.left, hk)387have highConverges : ∀ j ∈ n, ConvergesTo(coordinateSequence(pointSubsequence(highPoints(n, f, i), n, s), n, j), x(j))388intros j hj389have xjReal := FunctionBetween.apply_closed with xFunction, hj390have close : ∀ k ∈ ℕ, dist(highPoints(n, f, i)(k)(j), basePoints(n, f)(k)(j)) ≤ (dyadicRadius(k):R)391intros k hk392exact highPoints.close with hi, hk, hj393exact closePoints.converges with highSeq, baseSeq, highCube, increasing, hj, xjReal, close, (limits with hj)394have imageHigh := CubeContinuous.sequential with continuous, highSub, highSubCube, xIn, highConverges395have highLimit := highConverges with hi396have imageHighLimit := imageHigh with hi397have highOrdered : PointwiseLE(coordinateSequence(imageSequence(f, pointSubsequence(highPoints(n, f, i), n, s), n), n, i), coordinateSequence(pointSubsequence(highPoints(n, f, i), n, s), n, i))398unfold PointwiseLE399intros m hm400have sm := FunctionBetween.apply_closed with increasing.left, hm401have smNat := nat.omega.backward with sm402haveleft:= highPoints.coordinate at n, f, i, s, m, i with hi, increasing, hm, hi403haveright:= imageSequence.coordinate with map, highSeq, highCube, increasing, hm, hi404have highValue := highPoints.value at n, f, i, s(m) with*405rw [left, right, highValue]406have selected := highPoint.selected at n, f, s(m), i with*407have ge := gridLabels.high_ge with map, selected.left, hi, selected.right.right408exact realLe.characterization.forward with ge409have upper := ConvergesTo.le_of_le_terms with imageHighLimit, highLimit, highOrdered410have upperLe := realLe.characterization.forward with upper411have lowerLe := realLe.characterization.forward with lower412exact RealLe.antisymmetric with upperLe, lowerLe413