1-- Polymatrix games: the payoff to the owner of pure strategy i is the sum,2-- over the pure strategies j of the other players, of the opponent's3-- probability of j times a table entry T(i)(j). Two-player bimatrix games are4-- the case of two blocks. Every polymatrix game has a Nash equilibrium5-- Part of Cumulus, the Grace17 mathematics library.6-- Copyright (c) 2026 Grant Passmore.7-- Released under the Apache License 2.0. See LICENSE in the repository root.8-- Authors: Grant Passmore910module Grace.Cumulus.GameTheory.Polymatrix11import NashDeviation12import PureProfile1314-- The terms a(j)·x(j) of a row of the table, as a family of cube functions.15defgraph rowFamily (n a : Set) (j ∈ n) : functionSpace(unitCube(n), ℝ) := mulCube(n, constantFunction(unitCube(n), ℝ, a(j)), coordinateCube(n, j))16requires nNat : Nat(n)17requires aFunction : a : n → ℝ18proof19have aj := FunctionBetween.apply_closed with aFunction, member20have constant := constantCube.continuous at n, a(j) with aj21have coordinate := coordinateCube.continuous at n, j with member22have continuous := mulCube.continuous with constant, coordinate23have function := CubeFunctionContinuous.function with continuous24have between : mulCube(n, constantFunction(unitCube(n), ℝ, a(j)), coordinateCube(n, j)) : unitCube(n) → ℝ25exact CubeFunction.function with function26exact functionSpace.mem_iff.backward with between2728theorem rowFamily.continuous (n : Nat) (a : Set)29requires aFunction : a : n → ℝ30shows∀ j ∈ n, CubeFunctionContinuous(rowFamily(n, a)(j), n)31proof32intros j hj33have same := rowFamily.value at n, a, j with*34have aj := FunctionBetween.apply_closed with aFunction, hj35have constant := constantCube.continuous with aj36have coordinate := coordinateCube.continuous with hj37rw same38exact mulCube.continuous with constant, coordinate3940theorem rowFamily.functions (n : Nat) (a : Set)41requires aFunction : a : n → ℝ42shows∀ j ∈ n, CubeFunction(rowFamily(n, a)(j), n)43proof44intros j hj45have continuous := rowFamily.continuous with aFunction46exact CubeFunctionContinuous.function with (continuous with hj)4748theorem rowFamily.term (n : Nat) (a x j : Set)49requires aFunction : a : n → ℝ50requires hx : x ∈ unitCube(n)51requires hj : j ∈ n52shows rowFamily(n, a)(j)(x) = (a(j):R) * (x(j):R)53proof54have same := rowFamily.value at n, a, j with*55have aj := FunctionBetween.apply_closed with aFunction, hj56have constant := constantCube.continuous with aj57have coordinate := coordinateCube.continuous with hj58have constantFunction2 := CubeFunctionContinuous.function with constant59have coordinateFunction := CubeFunctionContinuous.function with coordinate60have product := mulCube.value with constantFunction2, coordinateFunction, hx61have constantValue := constantFunction.apply with hx, aj62have coordinateValue := coordinateCube.value with hj, hx63rw [same, product, constantValue, coordinateValue]64rfl6566-- The payoff to the owner of i against the other players' mixtures.67def polymatrixPayoff (n blk T i : Set) : Set:=68 weightedSum(n, n ∖ blockSet(n, blk, blk(i)), rowFamily(n, T(i)))6970theorem polymatrixPayoff.definition (n blk T i : Set)71shows polymatrixPayoff(n, blk, T, i) = weightedSum(n, n ∖ blockSet(n, blk, blk(i)), rowFamily(n, T(i)))72proof73unfold polymatrixPayoff74rfl7576theorem polymatrixPayoff.row (n T i : Set)77requires table : T : n → functionSpace(n, ℝ)78requires hi : i ∈ n79shows T(i) : n → ℝ80proof81have inSpace := FunctionBetween.apply_closed with table, hi82exact functionSpace.mem_iff.forward with inSpace8384theorem polymatrixPayoff.continuous (n : Nat) (blk T i : Set)85requires table : T : n → functionSpace(n, ℝ)86requires hi : i ∈ n87shows CubeFunctionContinuous(polymatrixPayoff(n, blk, T, i), n)88proof89have row := polymatrixPayoff.row with table, hi90have terms := rowFamily.continuous with row91have same := polymatrixPayoff.definition at n, blk, T, i92rw same93exact weightedSum.continuous with terms9495-- The payoff of i only depends on the coordinates outside i's own block.96theorem polymatrixPayoff.independent (n : Nat) (blk T i x z : Set)97requires table : T : n → functionSpace(n, ℝ)98requires hi : i ∈ n99requires hx : x ∈ unitCube(n)100requires hz : z ∈ unitCube(n)101requires agree : ∀ j ∈ n, blk(j) ≠ blk(i) → z(j) = x(j)102shows polymatrixPayoff(n, blk, T, i)(z) = polymatrixPayoff(n, blk, T, i)(x)103proof104have row := polymatrixPayoff.row with table, hi105have functions := rowFamily.functions with row106have same := polymatrixPayoff.definition at n, blk, T, i107have zValue := weightedSum.value at n, n ∖ blockSet(n, blk, blk(i)), rowFamily(n, T(i)), z with functions, hz108have xValue := weightedSum.value at n, n ∖ blockSet(n, blk, blk(i)), rowFamily(n, T(i)), x with functions, hx109have zPoints := assembledPoint.function with functions, hz110have xPoints := assembledPoint.function with functions, hx111have maskedZ := maskedPoint.sequence at n, n ∖ blockSet(n, blk, blk(i)), assembledPoint(n, rowFamily(n, T(i)), z) with zPoints112have maskedX := maskedPoint.sequence at n, n ∖ blockSet(n, blk, blk(i)), assembledPoint(n, rowFamily(n, T(i)), x) with xPoints113have termsAgree : ∀ k ∈ ℕ, k < n → maskedPoint(n, n ∖ blockSet(n, blk, blk(i)), assembledPoint(n, rowFamily(n, T(i)), z))(k) = maskedPoint(n, n ∖ blockSet(n, blk, blk(i)), assembledPoint(n, rowFamily(n, T(i)), x))(k)114intros k hk hkn115have hkIn : k ∈ n byexact membership.of_Less with hkn116have alternatives : k ∈ n ∖ blockSet(n, blk, blk(i)) ∨¬k ∈ n ∖ blockSet(n, blk, blk(i)) bytauto117cases alternatives with outsideBlock | insideBlock118 . have zSame := maskedPoint.on_block with zPoints, hkIn, outsideBlock119have xSame := maskedPoint.on_block with xPoints, hkIn, outsideBlock120have zPoint := assembledPoint.value with functions, hz, hkIn121have xPoint := assembledPoint.value with functions, hx, hkIn122have zTerm := rowFamily.term with row, hz, hkIn123have xTerm := rowFamily.term with row, hx, hkIn124have parts := difference.membership.forward with outsideBlock125have notOwn : blk(k) ≠ blk(i)126intro own127apply parts.right128apply blockSet.membership.backward129exact⟨hkIn, own⟩130have coordinate := agree with hkIn, notOwn131rw [zSame, xSame, zPoint, xPoint, zTerm, xTerm, coordinate]132rfl133 . have zSame := maskedPoint.off_block with zPoints, hkIn, insideBlock134have xSame := maskedPoint.off_block with xPoints, hkIn, insideBlock135rw [zSame, xSame]136rfl137have equal := realPartialSum.eq_prefix with maskedZ, maskedX, termsAgree138 rwa [same, zValue, xValue]139140-- The family of all payoff functions of the game.141defgraph polymatrixPayoffs (n blk T : Set) (i ∈ n) : functionSpace(unitCube(n), ℝ) := polymatrixPayoff(n, blk, T, i)142requires nNat : Nat(n)143requires table : T : n → functionSpace(n, ℝ)144proof145have continuous := polymatrixPayoff.continuous at n, blk, T, i with table, member146have function := CubeFunctionContinuous.function with continuous147have between : polymatrixPayoff(n, blk, T, i) : unitCube(n) → ℝ148exact CubeFunction.function with function149exact functionSpace.mem_iff.backward with between150151theorem polymatrixPayoffs.continuous (n : Nat) (blk T : Set)152requires table : T : n → functionSpace(n, ℝ)153shows∀ i ∈ n, CubeFunctionContinuous(polymatrixPayoffs(n, blk, T)(i), n)154proof155intros i hi156have same := polymatrixPayoffs.value at n, blk, T, i with*157rw same158exact polymatrixPayoff.continuous with table, hi159160-- Every polymatrix game has a Nash equilibrium: a profile x such that no161-- player improves their expected payoff by changing their own mixture.162theorem polymatrix.equilibrium (n : Nat) (m blk T rep : Set)163requires blkFunction : blk : n → m164requires repFunction : rep : m → n165requiressection : ∀ q ∈ m, blk(rep(q)) = q166requires table : T : n → functionSpace(n, ℝ)167shows∃ x ∈ simplexProduct(n, m, blk), ∀ p ∈ m, ∀ z ∈ simplexProduct(n, m, blk), (∀ j ∈ n, blk(j) ≠ p → z(j) = x(j)) → (expectedPayoff(n, blk, polymatrixPayoffs(n, blk, T), p)(z):R) ≤ (expectedPayoff(n, blk, polymatrixPayoffs(n, blk, T), p)(x):R)168proof169have anchor := pureProfile.in_product with blkFunction, repFunction, section170have family := polymatrixPayoffs.continuous at n, blk, T with table171obtain⟨x, equilibrium⟩:= nash.exists_equilibrium with blkFunction, anchor, family172have hx := Equilibrium.profile with equilibrium173have xCube := simplexProduct.cube with hx174witness x175constructor176 . exact hx177 . intros p hp z hz agree178have zCube := simplexProduct.cube with hz179have independent : ∀ i ∈ n, blk(i) = p → polymatrixPayoffs(n, blk, T)(i)(z) = polymatrixPayoffs(n, blk, T)(i)(x)180intros i hi own181have same := polymatrixPayoffs.value at n, blk, T, i with*182have agreeI : ∀ j ∈ n, blk(j) ≠ blk(i) → z(j) = x(j)183intros j hj notOwn184rw own at notOwn185exact agree with hj, notOwn186rw same187exact polymatrixPayoff.independent with table, hi, xCube, zCube, agreeI188exact nash.no_deviation with blkFunction, family, equilibrium, hz, hp, independent189