1-- Bundled metric spaces: a space as one structure of its carrier and its2-- distance, the metric laws under their standard names, balls, the metric3-- topology as a space, convergence written u ⇝ x with its ε–N form, the4-- uniqueness of limits, and the metric rewrite set5-- 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.Metric.Structure11import Maps12import Topology.Spaces1314-- A metric space is a structure of its carrier and its distance, a15-- real-valued function on pairs of points written d(x, y) under a16-- hypothesis MetricSpace(X) and applied as X.dist(x, y) otherwise, built by17-- MetricSpace.mk(A, d) and read back by the projections X.carrier and18-- X.dist. MetricSpace(X) says that the distance is nonnegative, vanishes19-- exactly on the diagonal, is symmetric, and satisfies the triangle20-- inequality. IsMetric(A, d) remains the form over separate pieces, bridged21-- below.22structure MetricSpace23| carrier : Set24| dist : carrier × carrier → ℝ25where26 dist_nonneg : ∀ x y ∈ carrier, relates(realLe, (0:R), dist(x, y))27 dist_eq_zero : ∀ x y ∈ carrier, dist(x, y) = (0:R) ↔ x = y28 dist_comm : ∀ x y ∈ carrier, dist(x, y) = dist(y, x)29 dist_triangle : ∀ x y z ∈ carrier,30 relates(realLe, dist(x, z), realAdd(dist(x, y), dist(y, z)))3132-- The projection laws of a constructed metric space as one rewrite set.33tactic metric_projections :=simp [MetricSpace.mk.carrier, MetricSpace.mk.dist]3435-- Bridges between the structure and the form over separate pieces. The36-- bundled-to-unbundled direction is an instance, so `with *` reads the37-- metric off a space hypothesis.38instance MetricSpace.parts (X : Set)39requires metric : MetricSpace(X)40shows IsMetric(X.carrier, X.dist)41proof42obtain⟨shape, function, nonneg, zero, comm, triangle⟩:= metric43unfold IsMetric44exact⟨function, nonneg, zero, comm, triangle⟩4546theorem MetricSpace.of_parts (A d : Set)47requires metric : IsMetric(A, d)48shows MetricSpace(MetricSpace.mk(A, d))49proof50obtain⟨function, nonneg, zero, comm, triangle⟩:= metric51apply MetricSpace.intro52 . exact function53 . exact nonneg54 . exact zero55 . exact comm56 . exact triangle5758theorem MetricSpace.of_projections (X : Set)59requires shape : MetricSpace.Data(X)60requires metric : IsMetric(X.carrier, X.dist)61shows MetricSpace(X)62proof63obtain⟨function, nonneg, zero, comm, triangle⟩:= metric64unfold MetricSpace65exact⟨shape, function, nonneg, zero, comm, triangle⟩6667theorem MetricSpace.eta_of_space (X : Set)68requires metric : MetricSpace(X)69shows X = MetricSpace.mk(X.carrier, X.dist)70proof71obtain⟨shape, rest⟩:= metric72exact MetricSpace.eta with shape7374-- The metric topology as a space: the open sets are those containing a75-- ball around each of their points. Its carrier is the carrier of X and76-- its open sets are metricTopology(A, d) over the pieces.77def metricTopology (X : Set) : Set:=78 TopologicalSpace.mk(MetricSpace.carrier(X),79 metricTopology(MetricSpace.carrier(X), MetricSpace.dist(X)))8081theorem metricTopology.carrier (X : Set)82shows TopologicalSpace.carrier(metricTopology(X)) = MetricSpace.carrier(X)83proof84unfold metricTopology85 topology_projections8687theorem metricTopology.opens (X : Set)88shows TopologicalSpace.opens(metricTopology(X)) =89 metricTopology(MetricSpace.carrier(X), MetricSpace.dist(X))90proof91unfold metricTopology92 topology_projections9394instance MetricSpace.topological_space (X : Set)95requires metric : MetricSpace(X)96shows TopologicalSpace(metricTopology(X))97proof98have parts := MetricSpace.parts with metric99unfold metricTopology100exact TopologicalSpace.of_parts with (Open.metricTopology.topology with parts)101102instance MetricSpace.hausdorff (X : Set)103requires metric : MetricSpace(X)104shows Hausdorff(metricTopology(X))105proof106have parts := MetricSpace.parts with metric107unfold metricTopology108exact Hausdorff.of_mk with (Open.metricTopology.hausdorff with parts)109110-- A sequence converging to a point of a metric space is written u ⇝ x: it111-- is convergence in the metric topology. Under a hypothesis MetricSpace(X)112-- a binder (x : X) is a set variable with x ∈ X.carrier.113notationinfix50"⇝"=> Converges(metricTopology(X), _, _) viahypothesis MetricSpace(X) for X.carrier114115-- The distance between two points is written d(x, y) wherever a hypothesis116-- in scope supplies the space; it is the value X.dist(x, y).117notation function "d(x, y)"=> X.dist(x, y) viahypothesis MetricSpace(X) for X.carrier118119-- The open and closed balls. ball(A, d, x, r) and closedBall(A, d, x, r)120-- remain the forms over separate pieces.121def ball (X x : Set, r : Real) : Set:=122 ball(MetricSpace.carrier(X), MetricSpace.dist(X), x, r)123def closedBall (X x : Set, r : Real) : Set:=124 closedBall(MetricSpace.carrier(X), MetricSpace.dist(X), x, r)125126theorem ball.parts (X x : Set, r : Real)127shows ball(X, x, r) = ball(MetricSpace.carrier(X), MetricSpace.dist(X), x, r)128proof129simp [ball]130131theorem closedBall.parts (X x : Set, r : Real)132shows closedBall(X, x, r) =133 closedBall(MetricSpace.carrier(X), MetricSpace.dist(X), x, r)134proof135simp [closedBall]136137-- Under a hypothesis MetricSpace(X): Cauchy(X, u) says the terms of u are138-- eventually within any ε of each other, and Bounded(X, S) that the139-- distances within S have a bound. IsCauchy(A, d, u) and IsBounded(A, d, S)140-- remain the forms over separate pieces.141section Definitions142variable (X : Set)143hypothesis metric : MetricSpace(X)144145predicate Cauchy (u : Set) := IsCauchy(X.carrier, X.dist, u)146predicate Bounded (S : Set) := IsBounded(X.carrier, X.dist, S)147148end Definitions149150section MetricLaws151variable (X : Set)152hypothesis metric : MetricSpace(X)153154-- A distance is a real number; this is an instance, so the real155-- arithmetic and order on distances is synthesized.156instance dist_real (x y : X)157shows d(x, y) ∈ ℝ158proof159have parts := MetricSpace.parts with metric160exact IsMetric.member_reals with parts, *161162theorem dist_nonneg (x y : X)163shows (0:R) ≤ d(x, y)164proof165have parts := MetricSpace.parts with metric166exact IsMetric.nonnegative with parts, *167168theorem dist_self (x : X)169shows d(x, x) = (0:R)170proof171have parts := MetricSpace.parts with metric172exact IsMetric.self with parts, *173174theorem dist_comm (x y : X)175shows d(x, y) = d(y, x)176proof177have parts := MetricSpace.parts with metric178exact IsMetric.symmetric with parts, *179180theorem dist_triangle (x y z : X)181shows d(x, z) ≤ d(x, y) + d(y, z)182proof183have parts := MetricSpace.parts with metric184exact IsMetric.triangle with parts, *185186theorem dist_eq_zero (x y : X)187shows d(x, y) = (0:R) ↔ x = y188proof189have parts := MetricSpace.parts with metric190exact IsMetric.eq_zero_iff with parts, *191192theorem dist_pos (x y : X)193shows (0:R) < d(x, y) ↔ x ≠ y194proof195have parts := MetricSpace.parts with metric196exact IsMetric.positive_iff_ne with parts, *197198theorem dist_triangle_left (x y z : X)199shows d(y, z) ≤ d(x, y) + d(x, z)200proof201have parts := MetricSpace.parts with metric202exact IsMetric.triangle_from with parts, *203204theorem abs_dist_sub_le (x y z : X)205shows abs(d(x, z) - d(y, z)) ≤ d(x, y)206proof207have parts := MetricSpace.parts with metric208exact IsMetric.abs_sub_le with parts, *209210-- Balls.211theorem mem_ball (x y : X) (r : Real)212shows y ∈ ball(X, x, r) ↔ d(x, y) < r213proof214have yIn : y ∈ X byassumption215rw ball.parts216have characterization := ball.membership at X.carrier, X.dist, x, r, y217constructor218 . intro member219have parts := characterization.forward with member220exact parts.right221 . intro close222exact characterization.backward with⟨yIn, close⟩223224theorem mem_closedBall (x y : X) (r : Real)225shows y ∈ closedBall(X, x, r) ↔ d(x, y) ≤ r226proof227have yIn : y ∈ X byassumption228rw closedBall.parts229have characterization := closedBall.membership at X.carrier, X.dist, x, r, y230constructor231 . intro member232have parts := characterization.forward with member233exact parts.right234 . intro close235exact characterization.backward with⟨yIn, close⟩236237theorem ball_subset (x : X) (r : Real)238shows ball(X, x, r) ⊆ X239proof240rw ball.parts241exact Ball.ball.subset at X.carrier, X.dist, x, r242243theorem mem_ball_self (x : X) (r : Real)244requires positive : 0 < r245shows x ∈ ball(X, x, r)246proof247have parts := MetricSpace.parts with metric248rw ball.parts249exact ball.center with parts, *250251theorem ball_mono (x : X) (r s : Real)252requires radii : r ≤ s253shows ball(X, x, r) ⊆ ball(X, x, s)254proof255have parts := MetricSpace.parts with metric256rw [ball.parts, ball.parts]257exact Ball.ball.mono with parts, *258259theorem ball_subset_closedBall (x : X) (r : Real)260shows ball(X, x, r) ⊆ closedBall(X, x, r)261proof262have parts := MetricSpace.parts with metric263rw [ball.parts, closedBall.parts]264exact Ball.ball.subset_closedBall with parts, *265266-- The open sets of the metric topology: a set is open exactly when it267-- contains a ball around each of its points, and balls are open.268theorem Open.iff_balls (U : Set)269requires hU : U ⊆ X270shows Open(metricTopology(X), U) ↔271∀ x ∈ U, ∃ r ∈ ℝ, 0 < r ∧ ball(X, x, r) ⊆ U272proof273have space := MetricSpace.topological_space with metric274constructor275 . introsopen x hx276have member := Open.mem withopen277rw metricTopology.opens at member278have inside := metricTopology.ball_inside with member, hx279obtain⟨r, hr, positive, small⟩:= inside280witness r281rw ball.parts282exact⟨hr, positive, small⟩283 . intro balls284have engine : MetricOpen(X.carrier, X.dist, U)285unfold MetricOpen286refine⟨hU, ?_⟩287intros x hx288obtain⟨r, hr, positive, inside⟩:= balls with x, hx289rw ball.parts at inside290exact⟨r, hr, positive, inside⟩291have member : U ∈ TopologicalSpace.opens(metricTopology(X))292rw metricTopology.opens293exact metricTopology.of_open with engine294exact Open.of_mem with space, member295296instance ball_open (x : X) (r : Real)297shows Open(metricTopology(X), ball(X, x, r))298proof299have parts := MetricSpace.parts with metric300have space := MetricSpace.topological_space with metric301have member : ball(X, x, r) ∈ TopologicalSpace.opens(metricTopology(X))302rw [metricTopology.opens, ball.parts]303exact ball.member_topology with parts, *304exact Open.of_mem with space, member305306instance closedBall_closed (x : X) (r : Real)307shows Closed(metricTopology(X), closedBall(X, x, r))308proof309have parts := MetricSpace.parts with metric310have closed := Closed.closedBall.closed with parts, *311unfold MetricClosed at closed312rw closedBall.parts313unfold metricTopology314exact Closed.of_mk with (Open.metricTopology.topology with parts), closed315316-- Convergence: u ⇝ x is convergence in the metric topology, which is the317-- ε–N form over the pieces and the textbook form on distances.318theorem Converges.iff_parts (u : Set) (x : X)319requires sequence : u : ℕ → X.carrier320shows u ⇝ x ↔ ConvergesIn(X.carrier, X.dist, u, x)321proof322have parts := MetricSpace.parts with metric323have space := MetricSpace.topological_space with metric324have xIn : x ∈ X byassumption325have sequenceIn : SequenceIn(X.carrier, u)326unfold SequenceIn327exact sequence328have characterization := ConvergesIn.iff_open with parts, sequenceIn, xIn329constructor330 . intro converges331apply characterization.backward332intros U open hxU333have member : U ∈ TopologicalSpace.opens(metricTopology(X))334rw metricTopology.opens335exact metricTopology.of_open withopen336have openU := Open.of_mem with space, member337exact Converges.eventually with converges, openU, hxU338 . intro converges339have eventually : ∀ U, Open(metricTopology(X), U) → x ∈ U →340∃ N ∈ ℕ, ∀ n ∈ ℕ, N ≤ n → u(n) ∈ U341intros U open hxU342have member := Open.mem withopen343rw metricTopology.opens at member344have metricOpen := metricTopology.open with member345exact ConvergesIn.eventually_in_open with converges, metricOpen, hxU346have sequenceT : u : ℕ → TopologicalSpace.carrier(metricTopology(X))347rw metricTopology.carrier348exact sequence349have xInT : x ∈ TopologicalSpace.carrier(metricTopology(X))350rw metricTopology.carrier351exact xIn352exact Converges.of_eventually with space, sequenceT, xInT, eventually353354theorem Converges.iff_dist (u : Set) (x : X)355requires sequence : u : ℕ → X.carrier356shows u ⇝ x ↔357∀ ε ∈ ℝ, 0 < ε →358∃ N ∈ ℕ, ∀ n ∈ ℕ, N ≤ n → d(x, u(n)) < ε359proof360have bridge := Converges.iff_parts with*361have xIn : x ∈ X byassumption362have sequenceIn : SequenceIn(X.carrier, u)363unfold SequenceIn364exact sequence365constructor366 . intro converges367have engine := bridge.forward with converges368obtain⟨sequenceU, point, control⟩:= engine369exact control370 . intro control371apply bridge.backward372unfold ConvergesIn373exact⟨sequenceIn, xIn, control⟩374375-- A sequence has at most one limit.376theorem Converges.unique_metric (u : Set) (x y : X)377requires first : u ⇝ x378requires second : u ⇝ y379shows x = y380proof381have space := MetricSpace.topological_space with metric382have hausdorff := MetricSpace.hausdorff with metric383exact Spaces.Converges.unique with space, hausdorff, first, second384385theorem Converges.cauchy (u : Set) (x : X)386requires converges : u ⇝ x387shows Cauchy(X, u)388proof389have parts := MetricSpace.parts with metric390have space := MetricSpace.topological_space with metric391have sequence := Converges.sequence with space, converges392rw metricTopology.carrier at sequence393have bridge := Converges.iff_parts with sequence394have engine := bridge.forward with converges395unfold Cauchy396exact ConvergesIn.cauchy with parts, engine397398theorem Converges.bounded_range (u : Set) (x : X)399requires converges : u ⇝ x400shows Bounded(X, functionImage(u, ℕ, X.carrier))401proof402have parts := MetricSpace.parts with metric403have space := MetricSpace.topological_space with metric404have sequence := Converges.sequence with space, converges405rw metricTopology.carrier at sequence406have bridge := Converges.iff_parts with sequence407have engine := bridge.forward with converges408unfold Bounded409exact ConvergesIn.range_bounded with parts, engine410411instance Cauchy.parts (u : Set)412requires cauchy : Cauchy(X, u)413shows IsCauchy(X.carrier, X.dist, u)414proof415unfold Cauchy at cauchy416exact cauchy417418theorem Cauchy.of_parts (u : Set)419requires cauchy : IsCauchy(X.carrier, X.dist, u)420shows Cauchy(X, u)421proof422unfold Cauchy423exact cauchy424425instance Bounded.parts (S : Set)426requires bounded : Bounded(X, S)427shows IsBounded(X.carrier, X.dist, S)428proof429unfold Bounded at bounded430exact bounded431432theorem Bounded.of_parts (S : Set)433requires bounded : IsBounded(X.carrier, X.dist, S)434shows Bounded(X, S)435proof436unfold Bounded437exact bounded438439instance ball_bounded (x : X) (r : Real)440shows Bounded(X, ball(X, x, r))441proof442have parts := MetricSpace.parts with metric443unfold Bounded444rw ball.parts445exact Bounded.IsBounded.ball with parts, *446447theorem Cauchy.bounded_range (u : Set)448requires cauchy : Cauchy(X, u)449shows Bounded(X, functionImage(u, ℕ, X.carrier))450proof451have parts := MetricSpace.parts with metric452have pieces := Cauchy.parts with cauchy453unfold Bounded454exact Bounded.IsCauchy.range_bounded with parts, pieces455456end MetricLaws457458-- The metric rewrite set: the distance from a point to itself, the459-- vanishing criterion, and membership in balls.460tactic metric_simp :=simp [dist_self, dist_eq_zero, mem_ball, mem_closedBall]461