CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

Try doing some basic maths questions in the Lean Theorem Prover. Functions, real numbers, equivalence relations and groups. Click on README.md and then on "Open in CoCalc with one click".

Project: Xena
Views: 18536
License: APACHE
/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Sébastien Gouëzel

Construction of a good coupling between nonempty compact metric spaces, minimizing
their Hausdorff distance. This construction is instrumental to study the Gromov-Hausdorff
distance between nonempty compact metric spaces -/

import topology.bounded_continuous_function topology.metric_space.gluing
topology.metric_space.hausdorff_distance

noncomputable theory
open_locale classical
open_locale topological_space
universes u v w

open classical lattice set function topological_space filter metric quotient
open bounded_continuous_function
open sum (inl inr)
set_option class.instance_max_depth 50

local attribute [instance] metric_space_sum

namespace Gromov_Hausdorff

section Gromov_Hausdorff_realized
/- This section shows that the Gromov-Hausdorff distance
is realized. For this, we consider candidate distances on the disjoint union
α ⊕ β of two compact nonempty metric spaces, almost realizing the Gromov-Hausdorff
distance, and show that they form a compact family by applying Arzela-Ascoli
theorem. The existence of a minimizer follows. -/

section definitions
variables (α : Type u) (β : Type v)
  [metric_space α] [compact_space α] [nonempty α]
  [metric_space β] [compact_space β] [nonempty β]

@[reducible] private def prod_space_fun : Type* := ((α ⊕ β) × (α ⊕ β)) → ℝ
@[reducible] private def Cb : Type* := bounded_continuous_function ((α ⊕ β) × (α ⊕ β)) ℝ

private def max_var : nnreal :=
2 * ⟨diam (univ : set α), diam_nonneg⟩ + 1 + 2 * ⟨diam (univ : set β), diam_nonneg⟩

private lemma one_le_max_var : 1 ≤ max_var α β := calc
  (1 : real) = 2 * 0 + 1 + 2 * 0 : by simp
  ... ≤ 2 * diam (univ : set α) + 1 + 2 * diam (univ : set β) :
  by apply_rules [add_le_add, mul_le_mul_of_nonneg_left, diam_nonneg, diam_nonneg]; norm_num

/-- The set of functions on α ⊕ β that are candidates distances to realize the
minimum of the Hausdorff distances between α and β in a coupling -/
def candidates : set (prod_space_fun α β) :=
  {f | (((((∀x y : α, f (sum.inl x, sum.inl y) = dist x y)
    ∧ (∀x y : β, f (sum.inr x, sum.inr y) = dist x y))
    ∧ (∀x y,     f (x, y) = f (y, x)))
    ∧ (∀x y z,   f (x, z) ≤ f (x, y) + f (y, z)))
    ∧ (∀x,       f (x, x) = 0))
    ∧ (∀x y,     f (x, y) ≤ max_var α β) }

/-- Version of the set of candidates in bounded_continuous_functions, to apply
Arzela-Ascoli -/
private def candidates_b : set (Cb α β) := {f : Cb α β | f.val ∈ candidates α β}

end definitions --section

section constructions

variables {α : Type u} {β : Type v}
[metric_space α] [compact_space α] [nonempty α] [metric_space β] [compact_space β] [nonempty β]
{f : prod_space_fun α β} {x y z t : α ⊕ β}
local attribute [instance, priority 10] inhabited_of_nonempty'

private lemma max_var_bound : dist x y ≤ max_var α β := calc
  dist x y ≤ diam (univ : set (α ⊕ β)) :
    dist_le_diam_of_mem (bounded_of_compact compact_univ) (mem_univ _) (mem_univ _)
  ... = diam (inl '' (univ : set α) ∪ inr '' (univ : set β)) :
    by apply congr_arg; ext x y z; cases x; simp [mem_univ, mem_range_self]
  ... ≤ diam (inl '' (univ : set α)) + dist (inl (default α)) (inr (default β)) + diam (inr '' (univ : set β)) :
    diam_union (mem_image_of_mem _ (mem_univ _)) (mem_image_of_mem _ (mem_univ _))
  ... = diam (univ : set α) + (dist (default α) (default α) + 1 + dist (default β) (default β)) + diam (univ : set β) :
    by { rw [isometry.diam_image isometry_on_inl, isometry.diam_image isometry_on_inr], refl }
  ... = 1 * diam (univ : set α) + 1 + 1 * diam (univ : set β) : by simp
  ... ≤ 2 * diam (univ : set α) + 1 + 2 * diam (univ : set β) :
  begin
    apply_rules [add_le_add, mul_le_mul_of_nonneg_right, diam_nonneg, diam_nonneg, le_refl],
    norm_num, norm_num
  end

private lemma candidates_symm (fA : f ∈ candidates α β) : f (x, y) = f (y ,x) := fA.1.1.1.2 x y

private lemma candidates_triangle (fA : f ∈ candidates α β) : f (x, z) ≤ f (x, y) + f (y, z) :=
  fA.1.1.2 x y z

private lemma candidates_refl (fA : f ∈ candidates α β) : f (x, x) = 0 := fA.1.2 x

private lemma candidates_nonneg (fA : f ∈ candidates α β) : 0 ≤ f (x, y) :=
begin
  have : 0 ≤ 2 * f (x, y) := calc
    0 = f (x, x) : (candidates_refl fA).symm
    ... ≤ f (x, y) + f (y, x) : candidates_triangle fA
    ... = f (x, y) + f (x, y) : by rw [candidates_symm fA]
    ... = 2 * f (x, y) : by ring,
  by linarith
end

private lemma candidates_dist_inl (fA : f ∈ candidates α β) (x y: α) : f (inl x, inl y) = dist x y :=
fA.1.1.1.1.1 x y

private lemma candidates_dist_inr (fA : f ∈ candidates α β) (x y : β) : f (inr x, inr y) = dist x y :=
fA.1.1.1.1.2 x y

private lemma candidates_le_max_var (fA : f ∈ candidates α β) : f (x, y) ≤ max_var α β :=
fA.2 x y

/-- candidates are bounded by max_var α β -/
private lemma candidates_dist_bound  (fA : f ∈ candidates α β) :
  ∀ {x y : α ⊕ β}, f (x, y) ≤ max_var α β * dist x y
| (inl x) (inl y) := calc
    f (inl x, inl y) = dist x y : candidates_dist_inl fA x y
    ... = dist (inl x) (inl y) : by { rw @sum.dist_eq α β, refl }
    ... = 1 * dist (inl x) (inl y) : by simp
    ... ≤ max_var α β * dist (inl x) (inl y) :
      mul_le_mul_of_nonneg_right (one_le_max_var α β) dist_nonneg
| (inl x) (inr y) := calc
    f (inl x, inr y) ≤ max_var α β : candidates_le_max_var fA
    ... = max_var α β * 1 : by simp
    ... ≤ max_var α β * dist (inl x) (inr y) :
      mul_le_mul_of_nonneg_left sum.one_dist_le (le_trans (zero_le_one) (one_le_max_var α β))
| (inr x) (inl y) := calc
    f (inr x, inl y) ≤ max_var α β : candidates_le_max_var fA
    ... = max_var α β * 1 : by simp
    ... ≤ max_var α β * dist (inl x) (inr y) :
      mul_le_mul_of_nonneg_left sum.one_dist_le (le_trans (zero_le_one) (one_le_max_var α β))
| (inr x) (inr y) := calc
    f (inr x, inr y) = dist x y : candidates_dist_inr fA x y
    ... = dist (inr x) (inr y) : by { rw @sum.dist_eq α β, refl }
    ... = 1 * dist (inr x) (inr y) : by simp
    ... ≤ max_var α β * dist (inr x) (inr y) :
      mul_le_mul_of_nonneg_right (one_le_max_var α β) dist_nonneg

/-- Technical lemma to prove that candidates are Lipschitz -/
private lemma candidates_lipschitz_aux (fA : f ∈ candidates α β) : f (x, y) - f (z, t) ≤ 2 * max_var α β * dist (x, y) (z, t) :=
calc
  f (x, y) - f(z, t) ≤ f (x, t) + f (t, y) - f (z, t) : add_le_add_right (candidates_triangle fA) _
  ... ≤ (f (x, z) + f (z, t) + f(t, y)) - f (z, t) :
    add_le_add_right (add_le_add_right (candidates_triangle fA) _ ) _
  ... = f (x, z) + f (t, y) : by simp
  ... ≤ max_var α β * dist x z + max_var α β * dist t y :
    add_le_add (candidates_dist_bound fA) (candidates_dist_bound fA)
  ... ≤ max_var α β * max (dist x z) (dist t y) + max_var α β * max (dist x z) (dist t y) :
  begin
    apply add_le_add,
    apply mul_le_mul_of_nonneg_left (le_max_left (dist x z) (dist t y)) (le_trans zero_le_one (one_le_max_var α β)),
    apply mul_le_mul_of_nonneg_left (le_max_right (dist x z) (dist t y)) (le_trans zero_le_one (one_le_max_var α β)),
  end
  ... = 2 * max_var α β * max (dist x z) (dist y t) :
    by { simp [dist_comm], ring }
  ... = 2 * max_var α β * dist (x, y) (z, t) : by refl

/-- Candidates are Lipschitz -/
private lemma candidates_lipschitz (fA : f ∈ candidates α β) :
  lipschitz_with (2 * max_var α β) f :=
begin
  rintros ⟨x, y⟩ ⟨z, t⟩,
  rw real.dist_eq,
  apply abs_le_of_le_of_neg_le,
  { exact candidates_lipschitz_aux fA },
  { have : -(f (x, y) - f (z, t)) = f (z, t) - f (x, y), by ring,
    rw [this, dist_comm],
    exact candidates_lipschitz_aux fA }
end

/-- candidates give rise to elements of bounded_continuous_functions -/
def candidates_b_of_candidates (f : prod_space_fun α β) (fA : f ∈ candidates α β) : Cb α β :=
bounded_continuous_function.mk_of_compact f (candidates_lipschitz fA).to_continuous

lemma candidates_b_of_candidates_mem (f : prod_space_fun α β) (fA : f ∈ candidates α β) :
  candidates_b_of_candidates f fA ∈ candidates_b α β := fA

/-- The distance on α ⊕ β is a candidate -/
private lemma dist_mem_candidates : (λp : (α ⊕ β) × (α ⊕ β), dist p.1 p.2) ∈ candidates α β :=
begin
  simp only [candidates, dist_comm, forall_const, and_true, add_comm, eq_self_iff_true,
             and_self, sum.forall, set.mem_set_of_eq, dist_self],
  repeat { split
    <|> exact (λa y z, dist_triangle_left _ _ _)
    <|> exact (λx y, by refl)
    <|> exact (λx y, max_var_bound) }
end

def candidates_b_dist (α : Type u) (β : Type v) [metric_space α] [compact_space α] [inhabited α]
  [metric_space β] [compact_space β] [inhabited β] : Cb α β := candidates_b_of_candidates _ dist_mem_candidates

lemma candidates_b_dist_mem_candidates_b : candidates_b_dist α β ∈ candidates_b α β :=
candidates_b_of_candidates_mem _ _

private lemma candidates_b_nonempty : (candidates_b α β).nonempty :=
⟨_,  candidates_b_dist_mem_candidates_b⟩

/-- To apply Arzela-Ascoli, we need to check that the set of candidates is closed and equicontinuous.
Equicontinuity follows from the Lipschitz control, we check closedness -/
private lemma closed_candidates_b : is_closed (candidates_b α β) :=
begin
  have I1 : ∀x y, is_closed {f : Cb α β | f (inl x, inl y) = dist x y} :=
    λx y, is_closed_eq continuous_evalx continuous_const,
  have I2 : ∀x y, is_closed {f : Cb α β | f (inr x, inr y) = dist x y } :=
    λx y, is_closed_eq continuous_evalx continuous_const,
  have I3 : ∀x y, is_closed {f : Cb α β | f (x, y) = f (y, x)} :=
    λx y, is_closed_eq continuous_evalx continuous_evalx,
  have I4 : ∀x y z, is_closed {f : Cb α β | f (x, z) ≤ f (x, y) + f (y, z)} :=
    λx y z, is_closed_le continuous_evalx (continuous_evalx.add continuous_evalx),
  have I5 : ∀x, is_closed {f : Cb α β | f (x, x) = 0} :=
    λx, is_closed_eq continuous_evalx continuous_const,
  have I6 : ∀x y, is_closed {f : Cb α β | f (x, y) ≤ max_var α β} :=
    λx y, is_closed_le continuous_evalx continuous_const,
  have : candidates_b α β = (⋂x y, {f : Cb α β | f ((@inl α β x), (@inl α β y)) = dist x y})
               ∩ (⋂x y, {f : Cb α β | f ((@inr α β x), (@inr α β y)) = dist x y})
               ∩ (⋂x y, {f : Cb α β | f (x, y) = f (y, x)})
               ∩ (⋂x y z, {f : Cb α β | f (x, z) ≤ f (x, y) + f (y, z)})
               ∩ (⋂x, {f : Cb α β | f (x, x) = 0})
               ∩ (⋂x y, {f : Cb α β | f (x, y) ≤ max_var α β}) :=
    begin ext, unfold candidates_b, unfold candidates, simp [-sum.forall], refl end,
  rw this,
  repeat { apply is_closed_inter _ _
       <|> apply is_closed_Inter _
       <|> apply I1 _ _
       <|> apply I2 _ _
       <|> apply I3 _ _
       <|> apply I4 _ _ _
       <|> apply I5 _
       <|> apply I6 _ _
       <|> assume x },
end

/-- Compactness of candidates (in bounded_continuous_functions) follows -/
private lemma compact_candidates_b : compact (candidates_b α β) :=
begin
  refine arzela_ascoli₂ (Icc 0 (max_var α β)) compact_Icc (candidates_b α β) closed_candidates_b _ _,
  { rintros f ⟨x1, x2⟩ hf,
    simp only [set.mem_Icc],
    exact ⟨candidates_nonneg hf, candidates_le_max_var hf⟩ },
  { refine equicontinuous_of_continuity_modulus (λt, 2 * max_var α β * t) _ _ _,
    { have : tendsto (λ (t : ℝ), 2 * (max_var α β : ℝ) * t) (𝓝 0) (𝓝 (2 * max_var α β * 0)) :=
        tendsto_const_nhds.mul tendsto_id,
      simpa using this },
    { assume x y f hf,
      exact candidates_lipschitz hf _ _ } }
end

/-- We will then choose the candidate minimizing the Hausdorff distance. Except that we are not
in a metric space setting, so we need to define our custom version of Hausdorff distance,
called HD, and prove its basic properties. -/
def HD (f : Cb α β) := max (supr (λx:α, infi (λy:β, f (inl x, inr y))))
                           (supr (λy:β, infi (λx:α, f (inl x, inr y))))

/- We will show that HD is continuous on bounded_continuous_functions, to deduce that its
minimum on the compact set candidates_b is attained. Since it is defined in terms of
infimum and supremum on ℝ, which is only conditionnally complete, we will need all the time
to check that the defining sets are bounded below or above. This is done in the next few
technical lemmas -/

lemma HD_below_aux1 {f : Cb α β} (C : ℝ) {x : α} : bdd_below (range (λ (y : β), f (inl x, inr y) + C)) :=
let ⟨cf, hcf⟩ := (real.bounded_iff_bdd_below_bdd_above.1 bounded_range).1 in
⟨cf + C, forall_range_iff.2 (λi, add_le_add_right ((λx, hcf (mem_range_self x)) _) _)⟩

private lemma HD_bound_aux1 (f : Cb α β) (C : ℝ) : bdd_above (range (λ (x : α), infi (λy:β, f (inl x, inr y) + C))) :=
begin
  rcases (real.bounded_iff_bdd_below_bdd_above.1 bounded_range).2 with ⟨Cf, hCf⟩,
  refine ⟨Cf + C, forall_range_iff.2 (λx, _)⟩,
  calc infi (λy:β, f (inl x, inr y) + C) ≤ f (inl x, inr (default β)) + C :
    cinfi_le (HD_below_aux1 C)
    ... ≤ Cf + C : add_le_add ((λx, hCf (mem_range_self x)) _) (le_refl _)
end

lemma HD_below_aux2 {f : Cb α β} (C : ℝ) {y : β} : bdd_below (range (λ (x : α), f (inl x, inr y) + C)) :=
let ⟨cf, hcf⟩ := (real.bounded_iff_bdd_below_bdd_above.1 bounded_range).1 in
⟨cf + C, forall_range_iff.2 (λi, add_le_add_right ((λx, hcf (mem_range_self x)) _) _)⟩

private lemma HD_bound_aux2 (f : Cb α β) (C : ℝ) : bdd_above (range (λ (y : β), infi (λx:α, f (inl x, inr y) + C))) :=
begin
  rcases (real.bounded_iff_bdd_below_bdd_above.1 bounded_range).2 with ⟨Cf, hCf⟩,
  refine ⟨Cf + C, forall_range_iff.2 (λy, _)⟩,
  calc infi (λx:α, f (inl x, inr y) + C) ≤ f (inl (default α), inr y) + C :
    cinfi_le (HD_below_aux2 C)
  ... ≤ Cf + C : add_le_add ((λx, hCf (mem_range_self x)) _) (le_refl _)
end

/-- Explicit bound on HD (dist). This means that when looking for minimizers it will
be sufficient to look for functions with HD(f) bounded by this bound. -/
lemma HD_candidates_b_dist_le : HD (candidates_b_dist α β) ≤ diam (univ : set α) + 1 + diam (univ : set β) :=
begin
  refine max_le (csupr_le (λx, _)) (csupr_le (λy, _)),
  { have A : infi (λy:β, candidates_b_dist α β (inl x, inr y)) ≤ candidates_b_dist α β (inl x, inr (default β)) :=
      cinfi_le (by simpa using HD_below_aux1 0),
    have B : dist (inl x) (inr (default β)) ≤ diam (univ : set α) + 1 + diam (univ : set β) := calc
      dist (inl x) (inr (default β)) = dist x (default α) + 1 + dist (default β) (default β) : rfl
      ... ≤ diam (univ : set α) + 1 + diam (univ : set β) :
      begin
        apply add_le_add (add_le_add _ (le_refl _)),
        exact dist_le_diam_of_mem (bounded_of_compact (compact_univ)) (mem_univ _) (mem_univ _),
        exact dist_le_diam_of_mem (bounded_of_compact (compact_univ)) (mem_univ _) (mem_univ _)
      end,
    exact le_trans A B },
  { have A : infi (λx:α, candidates_b_dist α β (inl x, inr y)) ≤ candidates_b_dist α β (inl (default α), inr y) :=
      cinfi_le (by simpa using HD_below_aux2 0),
    have B : dist (inl (default α)) (inr y) ≤ diam (univ : set α) + 1 + diam (univ : set β) := calc
      dist (inl (default α)) (inr y) = dist (default α) (default α) + 1 + dist (default β) y : rfl
      ... ≤ diam (univ : set α) + 1 + diam (univ : set β) :
      begin
        apply add_le_add (add_le_add _ (le_refl _)),
        exact dist_le_diam_of_mem (bounded_of_compact (compact_univ)) (mem_univ _) (mem_univ _),
        exact dist_le_diam_of_mem (bounded_of_compact (compact_univ)) (mem_univ _) (mem_univ _)
      end,
    exact le_trans A B },
end

/- To check that HD is continuous, we check that it is Lipschitz. As HD is a max, we
prove separately inequalities controlling the two terms (relying too heavily on copy-paste...) -/
private lemma HD_lipschitz_aux1 (f g : Cb α β) :
  supr (λx:α, infi (λy:β, f (inl x, inr y))) ≤ supr (λx:α, infi (λy:β, g (inl x, inr y))) + dist f g :=
begin
  rcases (real.bounded_iff_bdd_below_bdd_above.1 bounded_range).1 with ⟨cg, hcg⟩,
  have Hcg : ∀x, cg ≤ g x := λx, hcg (mem_range_self x),
  rcases (real.bounded_iff_bdd_below_bdd_above.1 bounded_range).1 with ⟨cf, hcf⟩,
  have Hcf : ∀x, cf ≤ f x := λx, hcf (mem_range_self x),

  -- prove the inequality but with `dist f g` inside, by using inequalities comparing
  -- supr to supr and infi to infi
  have Z : supr (λx:α, infi (λy:β, f (inl x, inr y))) ≤ supr (λx:α, infi (λy:β, g (inl x, inr y) + dist f g)) :=
    csupr_le_csupr (HD_bound_aux1 _ (dist f g))
      (λx, cinfi_le_cinfi ⟨cf, forall_range_iff.2(λi, Hcf _)⟩ (λy, coe_le_coe_add_dist)),
  -- move the `dist f g` out of the infimum and the supremum, arguing that continuous monotone maps
  -- (here the addition of `dist f g`) preserve infimum and supremum
  have E1 : ∀x, infi (λy:β, g (inl x, inr y)) + dist f g =
             infi ((λz, z + dist f g) ∘ (λy:β, (g (inl x, inr y)))),
  { assume x,
    refine cinfi_of_cinfi_of_monotone_of_continuous (_ : continuous (λ (z : ℝ), z + dist f g)) _ _,
    { exact continuous_id.add continuous_const },
    { assume x y hx, simpa },
    { show bdd_below (range (λ (y : β), g (inl x, inr y))),
        from ⟨cg, forall_range_iff.2(λi, Hcg _)⟩ } },
  have E2 : supr (λx:α, infi (λy:β, g (inl x, inr y))) + dist f g =
         supr ((λz, z + dist f g) ∘ (λx:α, infi (λy:β, g (inl x, inr y)))),
  { refine csupr_of_csupr_of_monotone_of_continuous (_ : continuous (λ (z : ℝ), z + dist f g)) _ _,
    { exact continuous_id.add continuous_const },
    { assume x y hx, simpa },
    { by simpa using HD_bound_aux1 _ 0 } },
  -- deduce the result from the above two steps
  simp only [add_comm] at Z,
  simpa [E2, E1, function.comp]
end

private lemma HD_lipschitz_aux2 (f g : Cb α β) :
  supr (λy:β, infi (λx:α, f (inl x, inr y))) ≤ supr (λy:β, infi (λx:α, g (inl x, inr y))) + dist f g :=
begin
  rcases (real.bounded_iff_bdd_below_bdd_above.1 bounded_range).1 with ⟨cg, hcg⟩,
  have Hcg : ∀x, cg ≤ g x := λx, hcg (mem_range_self x),
  rcases (real.bounded_iff_bdd_below_bdd_above.1 bounded_range).1 with ⟨cf, hcf⟩,
  have Hcf : ∀x, cf ≤ f x := λx, hcf (mem_range_self x),

  -- prove the inequality but with `dist f g` inside, by using inequalities comparing
  -- supr to supr and infi to infi
  have Z : supr (λy:β, infi (λx:α, f (inl x, inr y))) ≤ supr (λy:β, infi (λx:α, g (inl x, inr y) + dist f g)) :=
    csupr_le_csupr (HD_bound_aux2 _ (dist f g))
      (λy, cinfi_le_cinfi  ⟨cf, forall_range_iff.2(λi, Hcf _)⟩ (λy, coe_le_coe_add_dist)),
  -- move the `dist f g` out of the infimum and the supremum, arguing that continuous monotone maps
  -- (here the addition of `dist f g`) preserve infimum and supremum
  have E1 : ∀y, infi (λx:α, g (inl x, inr y)) + dist f g =
             infi ((λz, z + dist f g) ∘ (λx:α, (g (inl x, inr y)))),
  { assume y,
    refine cinfi_of_cinfi_of_monotone_of_continuous (_ : continuous (λ (z : ℝ), z + dist f g)) _ _,
    { exact continuous_id.add continuous_const },
    { assume x y hx, simpa },
    { show bdd_below (range (λx:α, g (inl x, inr y))),
        from ⟨cg, forall_range_iff.2(λi, Hcg _)⟩ } },
  have E2 : supr (λy:β, infi (λx:α, g (inl x, inr y))) + dist f g =
         supr ((λz, z + dist f g) ∘ (λy:β, infi (λx:α, g (inl x, inr y)))),
  { refine csupr_of_csupr_of_monotone_of_continuous (_ : continuous (λ (z : ℝ), z + dist f g)) _ _,
    { exact continuous_id.add continuous_const },
    { assume x y hx, simpa },
    { by simpa using HD_bound_aux2 _ 0 } },
  -- deduce the result from the above two steps
  simp only [add_comm] at Z,
  simpa [E2, E1, function.comp]
end

private lemma HD_lipschitz_aux3 (f g : Cb α β) : HD f ≤ HD g + dist f g :=
max_le (le_trans (HD_lipschitz_aux1 f g) (add_le_add_right (le_max_left _ _) _))
       (le_trans (HD_lipschitz_aux2 f g) (add_le_add_right (le_max_right _ _) _))

/-- Conclude that HD, being Lipschitz, is continuous -/
private lemma HD_continuous : continuous (HD : Cb α β → ℝ) :=
lipschitz_with.to_continuous (lipschitz_with.one_of_le_add HD_lipschitz_aux3)

end constructions --section

section consequences
variables (α : Type u) (β : Type v) [metric_space α] [compact_space α] [nonempty α] [metric_space β] [compact_space β] [nonempty β]

/- Now that we have proved that the set of candidates is compact, and that HD is continuous,
we can finally select a candidate minimizing HD. This will be the candidate realizing the
optimal coupling. -/
private lemma exists_minimizer : ∃f ∈ candidates_b α β, ∀g ∈ candidates_b α β, HD f ≤ HD g :=
compact_candidates_b.exists_forall_le candidates_b_nonempty HD_continuous.continuous_on

private definition optimal_GH_dist : Cb α β := classical.some (exists_minimizer α β)

private lemma optimal_GH_dist_mem_candidates_b : optimal_GH_dist α β ∈ candidates_b α β :=
by cases (classical.some_spec (exists_minimizer α β)); assumption

private lemma HD_optimal_GH_dist_le (g : Cb α β) (hg : g ∈ candidates_b α β) : HD (optimal_GH_dist α β) ≤ HD g :=
let ⟨Z1, Z2⟩ := classical.some_spec (exists_minimizer α β) in Z2 g hg

/-- With the optimal candidate, construct a premetric space structure on α ⊕ β, on which the
predistance is given by the candidate. Then, we will identify points at 0 predistance
to obtain a genuine metric space -/
def premetric_optimal_GH_dist : premetric_space (α ⊕ β) :=
{ dist := λp q, optimal_GH_dist α β (p, q),
  dist_self := λx, candidates_refl (optimal_GH_dist_mem_candidates_b α β),
  dist_comm := λx y, candidates_symm (optimal_GH_dist_mem_candidates_b α β),
  dist_triangle := λx y z, candidates_triangle (optimal_GH_dist_mem_candidates_b α β) }

local attribute [instance] premetric_optimal_GH_dist premetric.dist_setoid

/-- A metric space which realizes the optimal coupling between α and β -/
@[derive [metric_space]] definition optimal_GH_coupling : Type* :=
premetric.metric_quot (α ⊕ β)

/-- Injection of α in the optimal coupling between α and β -/
def optimal_GH_injl (x : α) : optimal_GH_coupling α β := ⟦inl x⟧

/-- The injection of α in the optimal coupling between α and β is an isometry. -/
lemma isometry_optimal_GH_injl : isometry (optimal_GH_injl α β) :=
begin
  refine isometry_emetric_iff_metric.2 (λx y, _),
  change dist ⟦inl x⟧ ⟦inl y⟧ = dist x y,
  exact candidates_dist_inl (optimal_GH_dist_mem_candidates_b α β) _ _,
end

/-- Injection of β  in the optimal coupling between α and β -/
def optimal_GH_injr (y : β) : optimal_GH_coupling α β := ⟦inr y⟧

/-- The injection of β in the optimal coupling between α and β is an isometry. -/
lemma isometry_optimal_GH_injr : isometry (optimal_GH_injr α β) :=
begin
  refine isometry_emetric_iff_metric.2 (λx y, _),
  change dist ⟦inr x⟧ ⟦inr y⟧ = dist x y,
  exact candidates_dist_inr (optimal_GH_dist_mem_candidates_b α β) _ _,
end

/-- The optimal coupling between two compact spaces α and β is still a compact space -/
instance compact_space_optimal_GH_coupling : compact_space (optimal_GH_coupling α β) :=
⟨begin
  have : (univ : set (optimal_GH_coupling α β)) =
           (optimal_GH_injl α β '' univ) ∪ (optimal_GH_injr α β '' univ),
  { refine subset.antisymm (λxc hxc, _) (subset_univ _),
    rcases quotient.exists_rep xc with ⟨x, hx⟩,
    cases x; rw ← hx,
    { have : ⟦inl x⟧ = optimal_GH_injl α β x := rfl,
      rw this,
      exact mem_union_left _ (mem_image_of_mem _ (mem_univ _)) },
    { have : ⟦inr x⟧ = optimal_GH_injr α β x := rfl,
      rw this,
      exact mem_union_right _ (mem_image_of_mem _ (mem_univ _)) } },
  rw this,
  exact (compact_univ.image (isometry_optimal_GH_injl α β).continuous).union
    (compact_univ.image (isometry_optimal_GH_injr α β).continuous)
end⟩

/-- For any candidate f, HD(f) is larger than or equal to the Hausdorff distance in the
optimal coupling. This follows from the fact that HD of the optimal candidate is exactly
the Hausdorff distance in the optimal coupling, although we only prove here the inequality
we need. -/
lemma Hausdorff_dist_optimal_le_HD {f} (h : f ∈ candidates_b α β) :
  Hausdorff_dist (range (optimal_GH_injl α β)) (range (optimal_GH_injr α β)) ≤ HD f :=
begin
  refine le_trans (le_of_forall_le_of_dense (λr hr, _)) (HD_optimal_GH_dist_le α β f h),
  have A : ∀ x ∈ range (optimal_GH_injl α β), ∃ y ∈ range (optimal_GH_injr α β), dist x y ≤ r,
  { assume x hx,
    rcases mem_range.1 hx with ⟨z, hz⟩,
    rw ← hz,
    have I1 : supr (λx:α, infi (λy:β, optimal_GH_dist α β (inl x, inr y))) < r :=
      lt_of_le_of_lt (le_max_left _ _) hr,
    have I2 : infi (λy:β, optimal_GH_dist α β (inl z, inr y)) ≤
        supr (λx:α, infi (λy:β, optimal_GH_dist α β (inl x, inr y))) :=
      le_cSup (by simpa using HD_bound_aux1 _ 0) (mem_range_self _),
    have I : infi (λy:β, optimal_GH_dist α β (inl z, inr y)) < r := lt_of_le_of_lt I2 I1,
    rcases exists_lt_of_cInf_lt (range_nonempty _) I with ⟨r', r'range, hr'⟩,
    rcases mem_range.1 r'range with ⟨z', hz'⟩,
    existsi [optimal_GH_injr α β z', mem_range_self _],
    have : (optimal_GH_dist α β) (inl z, inr z') ≤ r := begin rw hz', exact le_of_lt hr' end,
    exact this },
  refine Hausdorff_dist_le_of_mem_dist _ A _,
  { rcases exists_mem_of_nonempty α with ⟨xα, _⟩,
    have : optimal_GH_injl α β xα ∈ range (optimal_GH_injl α β) := mem_range_self _,
    rcases A _ this with ⟨y, yrange, hy⟩,
    exact le_trans dist_nonneg hy },
  { assume y hy,
    rcases mem_range.1 hy with ⟨z, hz⟩,
    rw ← hz,
    have I1 : supr (λy:β, infi (λx:α, optimal_GH_dist α β (inl x, inr y))) < r :=
      lt_of_le_of_lt (le_max_right _ _) hr,
    have I2 : infi (λx:α, optimal_GH_dist α β (inl x, inr z)) ≤
        supr (λy:β, infi (λx:α, optimal_GH_dist α β (inl x, inr y))) :=
      le_cSup (by simpa using HD_bound_aux2 _ 0) (mem_range_self _),
    have I : infi (λx:α, optimal_GH_dist α β (inl x, inr z)) < r := lt_of_le_of_lt I2 I1,
    rcases exists_lt_of_cInf_lt (range_nonempty _) I with ⟨r', r'range, hr'⟩,
    rcases mem_range.1 r'range with ⟨z', hz'⟩,
    existsi [optimal_GH_injl α β z', mem_range_self _],
    have : (optimal_GH_dist α β) (inl z', inr z) ≤ r := begin rw hz', exact le_of_lt hr' end,
    rw dist_comm,
    exact this }
end

end consequences
/- We are done with the construction of the optimal coupling -/
end Gromov_Hausdorff_realized

end Gromov_Hausdorff