1-- The product of two metric spaces with the sum metric: its distance2-- function, the metric laws, the projections as Lipschitz maps, balls3-- against products of balls, and bounded products4-- Part of Cumulus, the Grace17 mathematics library.5-- Copyright (c) 2026 Grant Passmore.6-- Released under the Apache License 2.0. See LICENSE in the repository root.7-- Authors: Grant Passmore89module Grace.Cumulus.Metric.Product10import Subspace1112def productDistance (X d Y e : Set) : Set:=13 { q ∈ ((X × Y) × (X × Y)) × ℝ |∃ x ∈ X, ∃ y ∈ Y, ∃ a ∈ X, ∃ b ∈ Y,14 q = (((x, y), (a, b)) ↦ realAdd(d(x, a), e(y, b))) }1516theorem productDistance.sum_real (X d Y e x y a b : Set)17requires metricD : IsMetric(X, d)18requires metricE : IsMetric(Y, e)19requires hx : x ∈ X20requires hy : y ∈ Y21requires ha : a ∈ X22requires hb : b ∈ Y23shows realAdd(d(x, a), e(y, b)) ∈ ℝ24proof25have dxa := IsMetric.real with metricD, hx, ha26have eyb := IsMetric.real with metricE, hy, hb27have sum : d(x, a) + e(y, b) ∈ ℝ bytypecheck28exact sum2930theorem productDistance.relates (X d Y e x y a b : Set)31requires metricD : IsMetric(X, d)32requires metricE : IsMetric(Y, e)33requires hx : x ∈ X34requires hy : y ∈ Y35requires ha : a ∈ X36requires hb : b ∈ Y37shows relates(productDistance(X, d, Y, e), ((x, y), (a, b)),38 realAdd(d(x, a), e(y, b)))39proof40have sum := productDistance.sum_real with metricD, metricE, hx, hy, ha, hb41have first := product.pair at x, y, X, Y with hx, hy42have second := product.pair at a, b, X, Y with ha, hb43have pair := product.pair at (x, y), (a, b), X × Y, X × Y with first, second44have entry := product.pair at ((x, y), (a, b)), realAdd(d(x, a), e(y, b)),45 (X × Y) × (X × Y), ℝ with pair, sum46unfold relates47apply productDistance.membership.backward48refine⟨entry, x, hx, y, hy, a, ha, b, hb, ?_⟩49rfl5051theorem productDistance.value_shape (X d Y e q v : Set)52requires edge : relates(productDistance(X, d, Y, e), q, v)53shows∃ x ∈ X, ∃ y ∈ Y, ∃ a ∈ X, ∃ b ∈ Y,54 q = ((x, y), (a, b)) ∧ v = realAdd(d(x, a), e(y, b))55proof56unfold relates at edge57obtain⟨bound, x, hx, y, hy, a, ha, b, hb, shape⟩:=58 productDistance.membership.forward with edge59obtain⟨first, second⟩:= orderedPair.injective with shape60exact⟨x, hx, y, hy, a, ha, b, hb, first, second⟩6162theorem productDistance.relation (X d Y e : Set)63shows RelationBetween(productDistance(X, d, Y, e), (X × Y) × (X × Y), ℝ)64proof65intros q hq66have parts := productDistance.membership.forward with hq67exact parts.left6869theorem productDistance.functional (X d Y e : Set)70shows Functional(productDistance(X, d, Y, e))71proof72intros q v first w second73obtain⟨x, hx, y, hy, a, ha, b, hb, shapeV, valueV⟩:=74 productDistance.value_shape with first75obtain⟨x2, hx2, y2, hy2, a2, ha2, b2, hb2, shapeW, valueW⟩:=76 productDistance.value_shape with second77rw shapeV at shapeW78obtain⟨firstPair, secondPair⟩:= orderedPair.injective with shapeW79obtain⟨xx, yy⟩:= orderedPair.injective with firstPair80obtain⟨aa, bb⟩:= orderedPair.injective with secondPair81rw [valueV, valueW, xx, yy, aa, bb]82rfl8384theorem productDistance.total (X d Y e : Set)85requires metricD : IsMetric(X, d)86requires metricE : IsMetric(Y, e)87shows88∀ q ∈ (X × Y) × (X × Y), ∃ v ∈ ℝ, relates(productDistance(X, d, Y, e), q, v)89proof90intros q hq91obtain⟨bound, p, hp, p2, hp2, rfl⟩:= product.membership.forward with hq92obtain⟨boundP, x, hx, y, hy, rfl⟩:= product.membership.forward with hp93obtain⟨boundP2, a, ha, b, hb, rfl⟩:= product.membership.forward with hp294have sum := productDistance.sum_real with metricD, metricE, hx, hy, ha, hb95refine⟨realAdd(d(x, a), e(y, b)), sum, ?_⟩96exact productDistance.relates with metricD, metricE, hx, hy, ha, hb9798theorem productDistance.function (X d Y e : Set)99requires metricD : IsMetric(X, d)100requires metricE : IsMetric(Y, e)101shows productDistance(X, d, Y, e) : (X × Y) × (X × Y) → ℝ102proof103constructor104 . exact productDistance.relation at X, d, Y, e105 . constructor106 . exact productDistance.functional at X, d, Y, e107 . exact productDistance.total with metricD, metricE108109theorem productDistance.apply (X d Y e x y a b : Set)110requires metricD : IsMetric(X, d)111requires metricE : IsMetric(Y, e)112requires hx : x ∈ X113requires hy : y ∈ Y114requires ha : a ∈ X115requires hb : b ∈ Y116shows productDistance(X, d, Y, e)((x, y), (a, b)) = realAdd(d(x, a), e(y, b))117proof118have function := productDistance.function with metricD, metricE119have first := product.pair at x, y, X, Y with hx, hy120have second := product.pair at a, b, X, Y with ha, hb121have pair := product.pair at (x, y), (a, b), X × Y, X × Y with first, second122have edge := productDistance.relates with metricD, metricE, hx, hy, ha, hb123haveleft:= Metric.apply_pair at productDistance(X, d, Y, e), (x, y), (a, b)124rw←left125exact FunctionBetween.apply_value with function, pair, edge126127theorem productDistance.metric (X d Y e : Set)128requires metricD : IsMetric(X, d)129requires metricE : IsMetric(Y, e)130shows IsMetric(X × Y, productDistance(X, d, Y, e))131proof132constructor133 . exact productDistance.function with metricD, metricE134 . constructor135 . intros p hp q hq136obtain⟨boundP, x, hx, y, hy, rfl⟩:= product.membership.forward with hp137obtain⟨boundQ, a, ha, b, hb, rfl⟩:= product.membership.forward with hq138have value := productDistance.apply with metricD, metricE, hx, hy, ha, hb139rw value140have dxa := IsMetric.real with metricD, hx, ha141have eyb := IsMetric.real with metricE, hy, hb142have nonnegativeD := IsMetric.nonnegative with metricD, hx, ha143have nonnegativeE := IsMetric.nonnegative with metricE, hy, hb144linarith145 . constructor146 . intros p hp q hq147obtain⟨boundP, x, hx, y, hy, rfl⟩:= product.membership.forward with hp148obtain⟨boundQ, a, ha, b, hb, rfl⟩:= product.membership.forward with hq149have value := productDistance.apply with150 metricD, metricE, hx, hy, ha, hb151rw value152have dxa := IsMetric.real with metricD, hx, ha153have eyb := IsMetric.real with metricE, hy, hb154have nonnegativeD := IsMetric.nonnegative with metricD, hx, ha155have nonnegativeE := IsMetric.nonnegative with metricE, hy, hb156constructor157 . intro zero158have zeroD : d(x, a) = (0:R) bylinarith159have zeroE : e(y, b) = (0:R) bylinarith160have xa := IsMetric.eq_of_zero with metricD, hx, ha, zeroD161have yb := IsMetric.eq_of_zero with metricE, hy, hb, zeroE162rw [xa, yb]163rfl164 . intro same165obtain⟨xa, yb⟩:= orderedPair.injective with same166rw [xa, yb]167have selfA := IsMetric.self with metricD, ha168have selfB := IsMetric.self with metricE, hb169rw [selfA, selfB]170linarith171 . constructor172 . intros p hp q hq173obtain⟨boundP, x, hx, y, hy, rfl⟩:=174 product.membership.forward with hp175obtain⟨boundQ, a, ha, b, hb, rfl⟩:=176 product.membership.forward with hq177have value := productDistance.apply with178 metricD, metricE, hx, hy, ha, hb179have valueBack := productDistance.apply with180 metricD, metricE, ha, hb, hx, hy181rw [value, valueBack]182have symmetricD := IsMetric.symmetric with metricD, hx, ha183have symmetricE := IsMetric.symmetric with metricE, hy, hb184rw [symmetricD, symmetricE]185rfl186 . intros p hp q hq s hs187obtain⟨boundP, x, hx, y, hy, rfl⟩:=188 product.membership.forward with hp189obtain⟨boundQ, a, ha, b, hb, rfl⟩:=190 product.membership.forward with hq191obtain⟨boundS, c, hc, g, hg, rfl⟩:=192 product.membership.forward with hs193have valuePS := productDistance.apply with194 metricD, metricE, hx, hy, hc, hg195have valuePQ := productDistance.apply with196 metricD, metricE, hx, hy, ha, hb197have valueQS := productDistance.apply with198 metricD, metricE, ha, hb, hc, hg199rw [valuePS, valuePQ, valueQS]200have triangleD := IsMetric.triangle with metricD, hx, ha, hc201have triangleE := IsMetric.triangle with metricE, hy, hb, hg202have dxc := IsMetric.real with metricD, hx, hc203have dxa := IsMetric.real with metricD, hx, ha204have dac := IsMetric.real with metricD, ha, hc205have eyg := IsMetric.real with metricE, hy, hg206have eyb := IsMetric.real with metricE, hy, hb207have ebg := IsMetric.real with metricE, hb, hg208linarith209210-- The projections are 1-Lipschitz, hence continuous.211theorem firstProjection.lipschitz (X d Y e : Set)212requires metricD : IsMetric(X, d)213requires metricE : IsMetric(Y, e)214shows IsLipschitz(firstProjection(X, Y), X × Y, productDistance(X, d, Y, e),215 X, d, (1:R))216proof217have one : (0:R) ≤1bylinarith218unfold IsLipschitz219constructor220 . exact firstProjection.function at X, Y221 . constructor222 . exact one223 . intros p hp q hq224obtain⟨boundP, x, hx, y, hy, rfl⟩:= product.membership.forward with hp225obtain⟨boundQ, a, ha, b, hb, rfl⟩:= product.membership.forward with hq226have valueP := firstProjection.pair at X, Y, x, y with hx, hy227have valueQ := firstProjection.pair at X, Y, a, b with ha, hb228have value := productDistance.apply with metricD, metricE, hx, hy, ha, hb229rw [valueP, valueQ, value]230have dxa := IsMetric.real with metricD, hx, ha231have eyb := IsMetric.real with metricE, hy, hb232have nonnegativeE := IsMetric.nonnegative with metricE, hy, hb233have unit := realMulValue.one_left at d(x, a) + e(y, b)234rw unit235linarith236237theorem secondProjection.lipschitz (X d Y e : Set)238requires metricD : IsMetric(X, d)239requires metricE : IsMetric(Y, e)240shows IsLipschitz(secondProjection(X, Y), X × Y, productDistance(X, d, Y, e),241 Y, e, (1:R))242proof243have one : (0:R) ≤1bylinarith244unfold IsLipschitz245constructor246 . exact secondProjection.function at X, Y247 . constructor248 . exact one249 . intros p hp q hq250obtain⟨boundP, x, hx, y, hy, rfl⟩:= product.membership.forward with hp251obtain⟨boundQ, a, ha, b, hb, rfl⟩:= product.membership.forward with hq252have valueP := secondProjection.pair at X, Y, x, y with hx, hy253have valueQ := secondProjection.pair at X, Y, a, b with ha, hb254have value := productDistance.apply with metricD, metricE, hx, hy, ha, hb255rw [valueP, valueQ, value]256have dxa := IsMetric.real with metricD, hx, ha257have eyb := IsMetric.real with metricE, hy, hb258have nonnegativeD := IsMetric.nonnegative with metricD, hx, ha259have unit := realMulValue.one_left at d(x, a) + e(y, b)260rw unit261linarith262263theorem firstProjection.metric_continuous (X d Y e : Set)264requires metricD : IsMetric(X, d)265requires metricE : IsMetric(Y, e)266shows IsMetricContinuous(firstProjection(X, Y), X × Y,267 productDistance(X, d, Y, e), X, d)268proof269have product := productDistance.metric with metricD, metricE270have lipschitz := firstProjection.lipschitz with metricD, metricE271exact IsLipschitz.continuous with product, metricD, lipschitz272273theorem secondProjection.metric_continuous (X d Y e : Set)274requires metricD : IsMetric(X, d)275requires metricE : IsMetric(Y, e)276shows IsMetricContinuous(secondProjection(X, Y), X × Y,277 productDistance(X, d, Y, e), Y, e)278proof279have product := productDistance.metric with metricD, metricE280have lipschitz := secondProjection.lipschitz with metricD, metricE281exact IsLipschitz.continuous with product, metricE, lipschitz282283-- Balls in the product against products of balls.284theorem productDistance.ball_subset_product (X d Y e x y : Set, r : Real)285requires metricD : IsMetric(X, d)286requires metricE : IsMetric(Y, e)287requires hx : x ∈ X288requires hy : y ∈ Y289shows ball(X × Y, productDistance(X, d, Y, e), (x, y), r) ⊆290 ball(X, d, x, r) × ball(Y, e, y, r)291proof292intros q hq293have hqP := ball.point with hq294have near := ball.distance with hq295obtain⟨boundQ, a, ha, b, hb, rfl⟩:= product.membership.forward with hqP296have value := productDistance.apply with metricD, metricE, hx, hy, ha, hb297rw value at near298have dxa := IsMetric.real with metricD, hx, ha299have eyb := IsMetric.real with metricE, hy, hb300have nonnegativeD := IsMetric.nonnegative with metricD, hx, ha301have nonnegativeE := IsMetric.nonnegative with metricE, hy, hb302have nearA : d(x, a) < r bylinarith303have nearB : e(y, b) < r bylinarith304apply product.pair305 . exact ball.of_distance with ha, nearA306 . exact ball.of_distance with hb, nearB307308theorem productDistance.product_subset_ball (X d Y e x y : Set, r s : Real)309requires metricD : IsMetric(X, d)310requires metricE : IsMetric(Y, e)311requires hx : x ∈ X312requires hy : y ∈ Y313shows ball(X, d, x, r) × ball(Y, e, y, s) ⊆314 ball(X × Y, productDistance(X, d, Y, e), (x, y), r + s)315proof316intros q hq317obtain⟨boundQ, a, ha, b, hb, rfl⟩:= product.membership.forward with hq318have haX := ball.point with ha319have hbY := ball.point with hb320have nearA := ball.distance with ha321have nearB := ball.distance with hb322have value := productDistance.apply with metricD, metricE, hx, hy, haX, hbY323have member := product.pair at a, b, X, Y with haX, hbY324apply ball.of_distance with member325rw value326have dxa := IsMetric.real with metricD, hx, haX327have eyb := IsMetric.real with metricE, hy, hbY328linarith329330theorem IsBounded.product (X d Y e S T : Set)331requires metricD : IsMetric(X, d)332requires metricE : IsMetric(Y, e)333requires boundedS : IsBounded(X, d, S)334requires boundedT : IsBounded(Y, e, T)335shows IsBounded(X × Y, productDistance(X, d, Y, e), S × T)336proof337obtain⟨insideS, r, hr, boundS⟩:= boundedS338obtain⟨insideT, s, hs, boundT⟩:= boundedT339have typedR : Real(r) byexact hr340have typedS : Real(s) byexact hs341apply IsBounded.of_bound at342 X × Y, productDistance(X, d, Y, e), S × T, r + s with343 (product.monotone with insideS, insideT)344intros p hp q hq345obtain⟨boundP, x, hx, y, hy, rfl⟩:= product.membership.forward with hp346obtain⟨boundQ, a, ha, b, hb, rfl⟩:= product.membership.forward with hq347have hxX := insideS with x, hx348have haX := insideS with a, ha349have hyY := insideT with y, hy350have hbY := insideT with b, hb351have value := productDistance.apply with metricD, metricE, hxX, hyY, haX, hbY352rw value353have dxa := IsMetric.real with metricD, hxX, haX354have eyb := IsMetric.real with metricE, hyY, hbY355have estimateD := boundS with x, hx, a, ha356have estimateE := boundT with y, hy, b, hb357linarith358