Skip to content

Commit 194a1d4

Browse files
committed
feat(Analysis/AperiodicOrder/Delone): Delone Sets (leanprover-community#32030)
Initial definitions and lemmas for Delone Sets. Co-authored-by: Newell Jensen <newell.jensen@gmail.com>
1 parent b6f12f1 commit 194a1d4

File tree

2 files changed

+205
-0
lines changed

2 files changed

+205
-0
lines changed

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,7 @@ public import Mathlib.Analysis.Analytic.RadiusLiminf
15161516
public import Mathlib.Analysis.Analytic.Uniqueness
15171517
public import Mathlib.Analysis.Analytic.WithLp
15181518
public import Mathlib.Analysis.Analytic.Within
1519+
public import Mathlib.Analysis.AperiodicOrder.Delone.Basic
15191520
public import Mathlib.Analysis.Asymptotics.AsymptoticEquivalent
15201521
public import Mathlib.Analysis.Asymptotics.Completion
15211522
public import Mathlib.Analysis.Asymptotics.Defs
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/-
2+
Copyright (c) 2026 Newell Jensen. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Newell Jensen
5+
-/
6+
module
7+
8+
public import Mathlib.Topology.MetricSpace.Cover
9+
10+
/-!
11+
# Delone sets
12+
13+
A **Delone set** `D ⊆ X` in a metric space is a set which is both:
14+
15+
* **Uniformly Discrete**: there exists `packingRadius > 0` such that distinct points of `D`
16+
are separated by a distance strictly greater than `packingRadius`;
17+
* **Relatively Dense**: there exists `coveringRadius > 0` such that every point of `X`
18+
lies within distance `coveringRadius` of some point of `D`.
19+
20+
The `DeloneSet` structure stores the set together with explicit radii witnessing
21+
these properties. The definitions use metric entourages so that the theory fits
22+
naturally into the uniformity framework.
23+
24+
Delone sets appear in discrete geometry, crystallography, aperiodic order, and tiling theory.
25+
26+
## Main definitions
27+
28+
* `Delone.DeloneSet X`: The main structure representing a Delone set in a metric space `X`.
29+
* `DeloneSet.mapBilipschitz`: Transports a Delone set along a bilipschitz equivalence,
30+
scaling the radii.
31+
* `DeloneSet.mapIsometry` Preserves the packing and covering radii exactly.
32+
33+
## Basic properties
34+
35+
* `packingRadius_lt_dist_of_mem_ne` : Distinct points in a Delone set are further apart than
36+
the packing radius.
37+
* `exists_dist_le_coveringRadius` : Every point of the space lies within the covering radius
38+
of the set.
39+
* `subset_ball_singleton` : Any ball of sufficiently small radius contains at most one point of
40+
the set.
41+
42+
## Implementation notes
43+
44+
* **Bundled Structure**: `DeloneSet` is bundled as a structure rather than a predicate
45+
(e.g., `IsDelone`). This facilitates dynamical systems constructions like hulls and patches by
46+
ensuring operations automatically preserve the required properties, eliminating the need to
47+
manually pass around proofs that the set remains Delone.
48+
* **Explicit Data**: Since radii are stored as explicit data, the map from `DeloneSet X` to `Set X`
49+
is not injective. We provide a `Membership` instance and `mem_carrier` to allow the convenience
50+
of `∈` notation while ensuring radii remain bundled, computationally accessible, and tracked by
51+
extensionality.
52+
-/
53+
54+
@[expose] public section
55+
56+
open Metric
57+
open scoped NNReal
58+
59+
variable {X Y : Type*} [MetricSpace X] [MetricSpace Y]
60+
61+
namespace Delone
62+
63+
/-- A **Delone set** consists of a set together with explicit radii
64+
witnessing uniform discreteness and relative denseness. -/
65+
@[ext]
66+
structure DeloneSet (X : Type*) [MetricSpace X] where
67+
/-- The underlying set. -/
68+
carrier : Set X
69+
/-- Radius such that distinct points of `carrier` are separated by more than `r`. -/
70+
packingRadius : ℝ≥0
71+
packingRadius_pos : 0 < packingRadius
72+
isSeparated_packingRadius : IsSeparated packingRadius carrier
73+
/-- Radius such that every point of the space is within `R` of `carrier`. -/
74+
coveringRadius : ℝ≥0
75+
coveringRadius_pos : 0 < coveringRadius
76+
isCover_coveringRadius : IsCover coveringRadius .univ carrier
77+
78+
namespace DeloneSet
79+
80+
/-- The underlying set of points of a Delone set. -/
81+
@[coe] def toSet (D : DeloneSet X) : Set X := D.carrier
82+
83+
instance : Coe (DeloneSet X) (Set X) where
84+
coe := DeloneSet.toSet
85+
86+
instance : Membership X (DeloneSet X) where
87+
mem D x := x ∈ (D : Set X)
88+
89+
@[simp, norm_cast]
90+
lemma mem_coe {D : DeloneSet X} {x : X} : x ∈ (D : Set X) ↔ x ∈ D := .rfl
91+
92+
@[simp] lemma mem_carrier {D : DeloneSet X} {x : X} :
93+
x ∈ D.carrier ↔ x ∈ D := .rfl
94+
95+
lemma nonempty [Nonempty X] (D : DeloneSet X) : (D : Set X).Nonempty :=
96+
D.isCover_coveringRadius.nonempty Set.univ_nonempty
97+
98+
/-- Copy of a Delone set with new fields equal to the old ones.
99+
Useful to fix definitional equalities. -/
100+
protected def copy (D : DeloneSet X) (carrier : Set X) (packingRadius coveringRadius : ℝ≥0)
101+
(h_carrier : carrier = D.carrier) (h_packing : packingRadius = D.packingRadius)
102+
(h_covering : coveringRadius = D.coveringRadius) :
103+
DeloneSet X where
104+
carrier := carrier
105+
packingRadius := packingRadius
106+
packingRadius_pos := by simpa [h_packing] using D.packingRadius_pos
107+
isSeparated_packingRadius := by
108+
simpa [h_carrier, h_packing] using D.isSeparated_packingRadius
109+
coveringRadius := coveringRadius
110+
coveringRadius_pos := by simpa [h_covering] using D.coveringRadius_pos
111+
isCover_coveringRadius := by
112+
simpa [h_carrier, h_covering] using D.isCover_coveringRadius
113+
114+
theorem copy_eq (D : DeloneSet X)
115+
(carrier packingRadius coveringRadius h_carrier h_packing h_covering) :
116+
D.copy carrier packingRadius coveringRadius h_carrier h_packing h_covering = D :=
117+
DeloneSet.ext h_carrier h_packing h_covering
118+
119+
lemma packingRadius_lt_dist_of_mem_ne (D : DeloneSet X) {x y : X}
120+
(hx : x ∈ D) (hy : y ∈ D) (hne : x ≠ y) :
121+
D.packingRadius < dist x y := by
122+
have hsep : ENNReal.ofReal D.packingRadius < ENNReal.ofReal (dist x y) := by
123+
simpa [edist_dist] using D.isSeparated_packingRadius hx hy hne
124+
exact (ENNReal.ofReal_lt_ofReal_iff (h := dist_pos.mpr hne)).1 hsep
125+
126+
lemma exists_dist_le_coveringRadius (D : DeloneSet X) (x : X) :
127+
∃ y ∈ D, dist x y ≤ D.coveringRadius := by
128+
obtain ⟨y, hy, hdist⟩ := D.isCover_coveringRadius (x := x) (by trivial)
129+
exact ⟨y, hy, by simpa [edist_dist] using hdist⟩
130+
131+
lemma eq_of_mem_ball (D : DeloneSet X) {r : ℝ≥0} (hr : r ≤ D.packingRadius / 2)
132+
{x y z : X} (hx : x ∈ D) (hy : y ∈ D) (hxz : x ∈ ball z r) (hyz : y ∈ ball z r) :
133+
x = y := by
134+
by_contra hne
135+
exact (D.packingRadius_lt_dist_of_mem_ne hx hy hne).not_gt <| calc
136+
dist x y ≤ dist x z + dist y z := dist_triangle_right x y z
137+
_ < r + r := by gcongr <;> simpa
138+
_ ≤ D.packingRadius := by rw [← add_halves D.packingRadius, NNReal.coe_add]; gcongr
139+
140+
/-- There exists a radius `r > 0` such that any ball of radius `r`
141+
centered at a point of `D` contains at most one point of `D`. -/
142+
lemma subset_ball_singleton (D : DeloneSet X) :
143+
∃ r > 0, ∀ {x y z}, x ∈ D → y ∈ D → x ∈ ball z r → y ∈ ball z r → x = y :=
144+
⟨D.packingRadius / 2, half_pos D.packingRadius_pos, fun hx hy => D.eq_of_mem_ball le_rfl hx hy⟩
145+
146+
/-- Bilipschitz maps send Delone sets to Delone sets. -/
147+
@[simps]
148+
noncomputable def mapBilipschitz (f : X ≃ Y) (K₁ K₂ : ℝ≥0) (hK₁ : 0 < K₁) (hK₂ : 0 < K₂)
149+
(hf₁ : AntilipschitzWith K₁ f) (hf₂ : LipschitzWith K₂ f) (D : DeloneSet X) : DeloneSet Y where
150+
carrier := f '' D.carrier
151+
packingRadius := D.packingRadius / K₁
152+
packingRadius_pos := div_pos D.packingRadius_pos hK₁
153+
isSeparated_packingRadius := D.isSeparated_packingRadius.image_antilipschitz hf₁ hK₁
154+
coveringRadius := K₂ * D.coveringRadius
155+
coveringRadius_pos := mul_pos hK₂ D.coveringRadius_pos
156+
isCover_coveringRadius := D.isCover_coveringRadius.image_lipschitz_of_surjective hf₂ f.surjective
157+
158+
@[simp] lemma mapBilipschitz_refl (D : DeloneSet X) (hK1 hK2 hA hL) :
159+
D.mapBilipschitz (.refl X) 1 1 hK1 hK2 hA hL = D := by
160+
ext <;> simp only [mapBilipschitz, Equiv.refl_apply, Set.image_id', div_one, one_mul]
161+
162+
lemma mapBilipschitz_trans {Z : Type*} [MetricSpace Z] (D : DeloneSet X)
163+
(f : X ≃ Y) (g : Y ≃ Z) (K₁f K₂f K₁g K₂g : ℝ≥0)
164+
(hf₁_pos : 0 < K₁f) (hf₂_pos : 0 < K₂f)
165+
(hg₁_pos : 0 < K₁g) (hg₂_pos : 0 < K₂g)
166+
(hf_anti : AntilipschitzWith K₁f f) (hf_lip : LipschitzWith K₂f f)
167+
(hg_anti : AntilipschitzWith K₁g g) (hg_lip : LipschitzWith K₂g g) :
168+
(D.mapBilipschitz f K₁f K₂f hf₁_pos hf₂_pos hf_anti hf_lip).mapBilipschitz
169+
g K₁g K₂g hg₁_pos hg₂_pos hg_anti hg_lip =
170+
D.mapBilipschitz (f.trans g) (K₁f * K₁g) (K₂g * K₂f)
171+
(mul_pos hf₁_pos hg₁_pos) (mul_pos hg₂_pos hf₂_pos)
172+
(hg_anti.comp hf_anti) (hg_lip.comp hf_lip) := by
173+
ext
174+
· simp only [mapBilipschitz_carrier, Equiv.trans_apply, Set.mem_image]
175+
exact exists_exists_and_eq_and
176+
· simp only [mapBilipschitz_packingRadius, NNReal.coe_div, div_div]
177+
· simp only [mapBilipschitz_coveringRadius, NNReal.coe_mul, mul_assoc]
178+
179+
/-- The image of a Delone set under an isometry. This is a specialization of
180+
`DeloneSet.mapBilipschitz` where the packing and covering radii are preserved because the
181+
Lipschitz constants are both 1. -/
182+
@[simps!]
183+
noncomputable def mapIsometry (f : X ≃ᵢ Y) : DeloneSet X ≃ DeloneSet Y where
184+
toFun D := (D.mapBilipschitz f.toEquiv 1 1 zero_lt_one zero_lt_one
185+
f.isometry.antilipschitz f.isometry.lipschitz).copy (f '' D.carrier)
186+
D.packingRadius D.coveringRadius rfl (by simp [mapBilipschitz]) (by simp [mapBilipschitz])
187+
invFun D := (D.mapBilipschitz f.symm.toEquiv 1 1 zero_lt_one zero_lt_one
188+
f.symm.isometry.antilipschitz f.symm.isometry.lipschitz).copy (f.symm '' D.carrier)
189+
D.packingRadius D.coveringRadius rfl (by simp [mapBilipschitz]) (by simp [mapBilipschitz])
190+
left_inv D := by ext <;> simp [copy_eq]
191+
right_inv D := by ext <;> simp [copy_eq]
192+
193+
@[simp] lemma mapIsometry_refl (D : DeloneSet X) : D.mapIsometry (.refl X) = D := by
194+
ext <;> simp [mapIsometry, IsometryEquiv.refl, DeloneSet.copy]
195+
196+
lemma mapIsometry_symm (f : X ≃ᵢ Y) : (mapIsometry f).symm = mapIsometry f.symm := rfl
197+
198+
lemma mapIsometry_trans {Z : Type*} [MetricSpace Z] (D : DeloneSet X) (f : X ≃ᵢ Y) (g : Y ≃ᵢ Z) :
199+
D.mapIsometry (f.trans g) = (D.mapIsometry f).mapIsometry g := by
200+
ext <;> simp [mapIsometry, DeloneSet.copy]
201+
202+
end DeloneSet
203+
204+
end Delone

0 commit comments

Comments
 (0)