Skip to content

Commit ca7c0e3

Browse files
JovanGerbyuanyi-350
authored andcommitted
feat(Geometry/Euclidean/SignedDist): signedDist between two points (leanprover-community#27260)
original PR: leanprover-community#24245 This PR defines `signedDist`, the signed distance between two points. It also redefines `signedInfDist` so that it uses `signedDist`. The motivation is to use this in IMO2020Q6. Additionally this definition will be useful for properly reasoning about the power of a point [#mathlib4 > Signed distance between points](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Signed.20distance.20between.20points) some comments: * should `signedDistLinear` be `private`? * I'm not too happy about the hypothesis in `signedDist_congr (h : ∃ r > (0 : ℝ), r • v = w)`. This relationship between `v` and `w` is a symmetric one that should have some API around it, similar to `SameRay`. It could also be spelled as `(ℝ≥0 ∙ v) = ℝ≥0 ∙ w`
1 parent 6895836 commit ca7c0e3

File tree

2 files changed

+206
-28
lines changed

2 files changed

+206
-28
lines changed

Mathlib/Analysis/InnerProductSpace/Basic.lean

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ theorem real_inner_div_norm_mul_norm_eq_neg_one_of_ne_zero_of_neg_mul {x : F} {r
670670
mul_assoc, abs_of_neg hr, neg_mul, div_neg_eq_neg_div, div_self]
671671
exact mul_ne_zero hr.ne (mul_self_ne_zero.2 (norm_ne_zero_iff.2 hx))
672672

673+
variable (𝕜) in
673674
theorem norm_inner_eq_norm_tfae (x y : E) :
674675
List.TFAE [‖⟪x, y⟫‖ = ‖x‖ * ‖y‖,
675676
x = 0 ∨ y = (⟪x, y⟫ / ⟪x, x⟫) • x,
@@ -701,7 +702,7 @@ theorem norm_inner_eq_norm_iff {x y : E} (hx₀ : x ≠ 0) (hy₀ : y ≠ 0) :
701702
‖⟪x, y⟫‖ = ‖x‖ * ‖y‖ ↔ ∃ r : 𝕜, r ≠ 0 ∧ y = r • x :=
702703
calc
703704
‖⟪x, y⟫‖ = ‖x‖ * ‖y‖ ↔ x = 0 ∨ ∃ r : 𝕜, y = r • x :=
704-
(@norm_inner_eq_norm_tfae 𝕜 _ _ _ _ x y).out 0 2
705+
(norm_inner_eq_norm_tfae 𝕜 x y).out 0 2
705706
_ ↔ ∃ r : 𝕜, y = r • x := or_iff_right hx₀
706707
_ ↔ ∃ r : 𝕜, r ≠ 0 ∧ y = r • x :=
707708
fun ⟨r, h⟩ => ⟨r, fun hr₀ => hy₀ <| h.symm ▸ smul_eq_zero.2 <| Or.inl hr₀, h⟩,
@@ -735,7 +736,7 @@ theorem inner_eq_norm_mul_iff_div {x y : E} (h₀ : x ≠ 0) :
735736
rw [← norm_ne_zero_iff, Ne, ← @ofReal_eq_zero 𝕜] at h₀'
736737
constructor <;> intro h
737738
· have : x = 0 ∨ y = (⟪x, y⟫ / ⟪x, x⟫ : 𝕜) • x :=
738-
((@norm_inner_eq_norm_tfae 𝕜 _ _ _ _ x y).out 0 1).1 (by simp [h])
739+
((norm_inner_eq_norm_tfae 𝕜 x y).out 0 1).1 (by simp [h])
739740
rw [this.resolve_left h₀, h]
740741
simp [norm_smul, inner_self_ofReal_norm, mul_div_cancel_right₀ _ h₀']
741742
· conv_lhs => rw [← h, inner_smul_right, inner_self_eq_norm_sq_to_K]

Mathlib/Geometry/Euclidean/SignedDist.lean

Lines changed: 203 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Joseph Myers
55
-/
66
import Mathlib.Geometry.Euclidean.Projection
7-
import Mathlib.LinearAlgebra.AffineSpace.ContinuousAffineEquiv
8-
import Mathlib.LinearAlgebra.AffineSpace.FiniteDimensional
9-
import Mathlib.Topology.Algebra.AffineSubspace
107

118
/-!
129
# Signed distance to an affine subspace in a Euclidean space.
1310
14-
This file defines the signed distance between an affine subspace and a point, in the direction
15-
of a given reference point.
11+
This file defines the signed distance between two points, in the direction of a given a vector, and
12+
the signed distance between an affine subspace and a point, in the direction of a given
13+
reference point.
1614
1715
## Main definitions
1816
17+
* `signedDist` is the signed distance between two points
1918
* `AffineSubspace.signedInfDist` is the signed distance between an affine subspace and a point.
2019
* `Affine.Simplex.signedInfDist` is the signed distance between a face of a simplex and a point.
2120
In the case of a triangle, these distances are trilinear coordinates.
@@ -33,48 +32,222 @@ open scoped RealInnerProductSpace
3332
variable {V P : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V] [MetricSpace P]
3433
variable [NormedAddTorsor V P]
3534

35+
section signedDist
36+
37+
/-- Auxiliary definition for `signedDist`. It is the underlying linear map of `signedDist`. -/
38+
private noncomputable def signedDistLinear (v : V) : V →ₗ[ℝ] P →ᴬ[ℝ] ℝ where
39+
toFun w := .const ℝ P ⟪-(‖v‖⁻¹ • v), w⟫
40+
map_add' x y := by ext; simp [inner_add_right]
41+
map_smul' r x := by ext; simp [inner_smul_right]
42+
43+
private lemma signedDistLinear_apply (v w : V) :
44+
signedDistLinear v w = .const ℝ P ⟪-(‖v‖⁻¹ • v), w⟫ := rfl
45+
46+
/--
47+
The signed distance between two points `p` and `q`, in the direction of a reference vector `v`.
48+
It is the size of `q - p` in the direction of `v`.
49+
In the degenerate case `v = 0`, it returns `0`.
50+
51+
TODO: once we have a topology on `P →ᴬ[ℝ] ℝ`, the type should be `P →ᴬ[ℝ] P →ᴬ[ℝ] ℝ`.
52+
-/
53+
noncomputable def signedDist (v : V) : P →ᵃ[ℝ] P →ᴬ[ℝ] ℝ where
54+
toFun p := (innerSL ℝ (‖v‖⁻¹ • v)).toContinuousAffineMap.comp
55+
(ContinuousAffineMap.id ℝ P -ᵥ .const ℝ P p)
56+
linear := signedDistLinear v
57+
map_vadd' p v' := by
58+
ext q
59+
rw [signedDistLinear_apply]
60+
generalize ‖v‖⁻¹ • v = x
61+
simp [vsub_vadd_eq_vsub_sub, inner_sub_right, ← sub_eq_neg_add]
62+
63+
variable (v w : V) (p q r : P)
64+
65+
-- Lemmas about the definition of `signedDist`
66+
67+
lemma signedDist_apply : signedDist v p = (innerSL ℝ (‖v‖⁻¹ • v)).toContinuousAffineMap.comp
68+
(ContinuousAffineMap.id ℝ P -ᵥ .const ℝ P p) :=
69+
rfl
70+
71+
lemma signedDist_apply_apply : signedDist v p q = ⟪‖v‖⁻¹ • v, q -ᵥ p⟫ :=
72+
rfl
73+
74+
lemma signedDist_apply_linear : (signedDist v p).linear = innerₗ V (‖v‖⁻¹ • v) := by
75+
change (innerₗ V (‖v‖⁻¹ • v)).comp (LinearMap.id - 0) = _
76+
simp
77+
78+
lemma signedDist_apply_linear_apply : (signedDist v p).linear w = ⟪‖v‖⁻¹ • v, w⟫ := by
79+
simp [signedDist_apply_linear, real_inner_smul_left]
80+
81+
lemma signedDist_linear_apply : (signedDist v).linear w = .const ℝ P ⟪-(‖v‖⁻¹ • v), w⟫ :=
82+
rfl
83+
84+
lemma signedDist_linear_apply_apply : (signedDist v).linear w p = ⟪-(‖v‖⁻¹ • v), w⟫ :=
85+
rfl
86+
87+
-- Lemmas about the vector argument of `signedDist`
88+
89+
lemma signedDist_smul (r : ℝ) : signedDist (r • v) p q = SignType.sign r * signedDist v p q := by
90+
simp only [signedDist_apply_apply]
91+
rw [norm_smul, mul_inv_rev, smul_smul, mul_rotate, ← smul_smul, real_inner_smul_left]
92+
congr
93+
by_cases h : r = 0
94+
· simp [h]
95+
· rw [inv_mul_eq_iff_eq_mul₀ (by positivity)]
96+
simp only [Real.norm_eq_abs, abs_mul_sign]
97+
98+
@[simp] lemma signedDist_zero : signedDist 0 p q = 0 := by
99+
simpa using signedDist_smul 0 p q 0
100+
101+
@[simp] lemma signedDist_neg : signedDist (-v) p q = -signedDist v p q := by
102+
simpa using signedDist_smul v p q (-1)
103+
104+
lemma signedDist_congr (h : ∃ r > (0 : ℝ), r • v = w) : signedDist (P := P) v = signedDist w := by
105+
obtain ⟨r, _, _⟩ := h
106+
ext p q
107+
simpa [*] using (signedDist_smul v p q r).symm
108+
109+
-- Lemmas about permuting the point arguments of `signedDist`, analogous to lemmas about `dist`
110+
111+
@[simp] lemma signedDist_self : signedDist v p p = 0 := by
112+
simp [signedDist_apply_apply]
113+
114+
@[simp] lemma signedDist_anticomm : -signedDist v p q = signedDist v q p := by
115+
simp [signedDist_apply_apply, ← inner_neg_right]
116+
117+
@[simp]
118+
lemma signedDist_triangle : signedDist v p q + signedDist v q r = signedDist v p r := by
119+
simp only [signedDist_apply_apply]
120+
rw [add_comm, ← inner_add_right, vsub_add_vsub_cancel]
121+
122+
@[simp]
123+
lemma signedDist_triangle_left : signedDist v p q - signedDist v p r = signedDist v r q := by
124+
rw [sub_eq_iff_eq_add', signedDist_triangle]
125+
126+
@[simp]
127+
lemma signedDist_triangle_right : signedDist v p r - signedDist v q r = signedDist v p q := by
128+
rw [sub_eq_iff_eq_add, signedDist_triangle]
129+
130+
-- Lemmas about offsetting the point arguments of `signedDist` (with `+ᵥ` or `-ᵥ`)
131+
132+
lemma signedDist_vadd_left : signedDist v (w +ᵥ p) q = -⟪‖v‖⁻¹ • v, w⟫ + signedDist v p q := by
133+
simp [signedDist_linear_apply_apply]
134+
135+
lemma signedDist_vadd_right : signedDist v p (w +ᵥ q) = ⟪‖v‖⁻¹ • v, w⟫ + signedDist v p q := by
136+
simp [signedDist_apply_linear_apply]
137+
138+
-- TODO: find a better name for these 2 lemmas
139+
lemma signedDist_vadd_left_swap : signedDist v (w +ᵥ p) q = signedDist v p (-w +ᵥ q) := by
140+
rw [signedDist_vadd_left, signedDist_vadd_right, inner_neg_right]
141+
142+
lemma signedDist_vadd_right_swap : signedDist v p (w +ᵥ q) = signedDist v (-w +ᵥ p) q := by
143+
rw [signedDist_vadd_left_swap, neg_neg]
144+
145+
lemma signedDist_vadd_vadd : signedDist v (w +ᵥ p) (w +ᵥ q) = signedDist v p q := by
146+
rw [signedDist_vadd_left_swap, neg_vadd_vadd]
147+
148+
variable {v p q} in
149+
lemma signedDist_left_congr (h : ⟪v, p -ᵥ q⟫ = 0) : signedDist v p = signedDist v q := by
150+
ext r
151+
simpa [real_inner_smul_left, h] using signedDist_vadd_left v (p -ᵥ q) q r
152+
153+
variable {v q r} in
154+
lemma signedDist_right_congr (h : ⟪v, q -ᵥ r⟫ = 0) : signedDist v p q = signedDist v p r := by
155+
simpa [real_inner_smul_left, h] using signedDist_vadd_right v (q -ᵥ r) p r
156+
157+
variable {v p q} in
158+
lemma signedDist_eq_zero_of_orthogonal (h : ⟪v, q -ᵥ p⟫ = 0) : signedDist v p q = 0 := by
159+
simpa using signedDist_right_congr p h
160+
161+
-- Lemmas relating `signedDist` to `dist`
162+
163+
lemma abs_signedDist_le_dist : |signedDist v p q| ≤ dist p q := by
164+
rw [signedDist_apply_apply]
165+
by_cases h : v = 0
166+
· simp [h]
167+
· convert abs_real_inner_le_norm (‖v‖⁻¹ • v) (q -ᵥ p)
168+
simp [norm_smul, dist_eq_norm_vsub']
169+
field_simp
170+
171+
lemma signedDist_le_dist : signedDist v p q ≤ dist p q :=
172+
le_trans (le_abs_self _) (abs_signedDist_le_dist _ _ _)
173+
174+
lemma abs_signedDist_eq_dist_iff_vsub_mem_span :
175+
|signedDist v p q| = dist p q ↔ q -ᵥ p ∈ ℝ ∙ v := by
176+
rw [Submodule.mem_span_singleton]
177+
rw [signedDist_apply_apply, dist_eq_norm_vsub', real_inner_smul_left, abs_mul, abs_inv, abs_norm]
178+
by_cases h : v = 0
179+
· simp [h, eq_comm (a := (0 : ℝ)), eq_comm (a := (0 : V))]
180+
rw [inv_mul_eq_iff_eq_mul₀ (by positivity)]
181+
rw [← Real.norm_eq_abs, ((norm_inner_eq_norm_tfae ℝ v (q -ᵥ p)).out 0 2:)]
182+
simp [h, eq_comm]
183+
184+
open NNReal in
185+
lemma signedDist_eq_dist_iff_vsub_mem_span : signedDist v p q = dist p q ↔ q -ᵥ p ∈ ℝ≥0 ∙ v := by
186+
rw [Submodule.mem_span_singleton]
187+
rw [signedDist_apply_apply, dist_eq_norm_vsub', real_inner_smul_left]
188+
by_cases h : v = 0
189+
· simp [h, eq_comm (a := (0 : ℝ)), eq_comm (a := (0 : V))]
190+
rw [inv_mul_eq_iff_eq_mul₀ (by positivity)]
191+
rw [inner_eq_norm_mul_iff_real]
192+
simp only [smul_def]
193+
refine ⟨fun h => ?_, fun ⟨c, h⟩ => ?_⟩
194+
· simp only [NNReal.exists, coe_mk, exists_prop]
195+
use ‖v‖⁻¹ * ‖q -ᵥ p‖
196+
constructor
197+
· positivity
198+
· rw [← smul_smul, h, smul_smul, inv_mul_cancel₀ (by positivity), one_smul]
199+
· rw [← h, norm_smul, smul_smul, mul_comm]
200+
simp
201+
202+
@[simp] lemma signedDist_vsub_self : signedDist (q -ᵥ p) p q = dist p q := by
203+
rw [signedDist_eq_dist_iff_vsub_mem_span]
204+
apply Submodule.mem_span_singleton_self
205+
206+
@[simp] lemma signedDist_vsub_self_rev : signedDist (p -ᵥ q) p q = -dist p q := by
207+
rw [← neg_eq_iff_eq_neg, ← signedDist_neg, neg_vsub_eq_vsub_rev]
208+
apply signedDist_vsub_self
209+
210+
end signedDist
211+
36212
namespace AffineSubspace
37213

38-
variable (s : AffineSubspace ℝ P) [Nonempty s] [s.direction.HasOrthogonalProjection] (p : P)
214+
variable (s : AffineSubspace ℝ P) [Nonempty s] [s.direction.HasOrthogonalProjection] (p q : P)
39215

40216
/-- The signed distance between `s` and a point, in the direction of the reference point `p`.
41217
This is expected to be used when `p` does not lie in `s` (in the degenerate case where `p` lies
42218
in `s`, this yields 0) and when the point at which the distance is evaluated lies in the affine
43219
span of `s` and `p` (any component of the distance orthogonal to that span is disregarded). -/
44220
noncomputable def signedInfDist : P →ᴬ[ℝ] ℝ :=
45-
(innerSL ℝ (‖p -ᵥ orthogonalProjection s p‖⁻¹ •
46-
(p -ᵥ orthogonalProjection s p))).toContinuousAffineMap.comp
47-
((ContinuousAffineEquiv.refl ℝ P).toContinuousAffineMap -ᵥ
48-
s.subtypeA.comp (orthogonalProjection s))
49-
50-
lemma signedInfDist_apply (x : P) :
51-
s.signedInfDist p x = ⟪‖p -ᵥ orthogonalProjection s p‖⁻¹ • (p -ᵥ orthogonalProjection s p),
52-
x -ᵥ orthogonalProjection s x⟫ :=
53-
rfl
221+
signedDist (p -ᵥ orthogonalProjection s p) (Classical.ofNonempty : s)
54222

55-
lemma signedInfDist_linear : (s.signedInfDist p).linear =
56-
(innerₗ V (‖p -ᵥ orthogonalProjection s p‖⁻¹ • (p -ᵥ orthogonalProjection s p))).comp
57-
(LinearMap.id (R := ℝ) (M := V) -
58-
s.direction.subtype.comp s.direction.orthogonalProjection.toLinearMap) :=
223+
lemma signedInfDist_def :
224+
s.signedInfDist p = signedDist (p -ᵥ orthogonalProjection s p) ↑(Classical.ofNonempty : s) :=
59225
rfl
60226

61-
lemma signedInfDist_linear_apply (v : V) : (s.signedInfDist p).linear v =
62-
⟪‖p -ᵥ orthogonalProjection s p‖⁻¹ • (p -ᵥ orthogonalProjection s p),
63-
v - s.direction.orthogonalProjection v⟫ :=
64-
rfl
227+
variable {s p q} in
228+
lemma signedInfDist_eq_signedDist_of_mem (h : q ∈ s) :
229+
s.signedInfDist p = signedDist (p -ᵥ orthogonalProjection s p) q := by
230+
apply signedDist_left_congr
231+
apply s.direction.inner_left_of_mem_orthogonal
232+
· exact vsub_mem_direction (SetLike.coe_mem _) h
233+
· exact vsub_orthogonalProjection_mem_direction_orthogonal s p
234+
235+
lemma signedInfDist_eq_signedDist_orthogonalProjection {x : P} : s.signedInfDist p x =
236+
signedDist (p -ᵥ orthogonalProjection s p) ↑(orthogonalProjection s x) x := by
237+
rw [signedInfDist_eq_signedDist_of_mem (orthogonalProjection_mem x)]
65238

66239
@[simp] lemma signedInfDist_apply_self : s.signedInfDist p p = ‖p -ᵥ orthogonalProjection s p‖ := by
67-
simp [signedInfDist_apply, inner_smul_left, real_inner_self_eq_norm_sq, pow_two, ← mul_assoc]
240+
rw [signedInfDist_eq_signedDist_orthogonalProjection, signedDist_vsub_self, dist_eq_norm_vsub']
68241

69242
variable {s} in
70243
@[simp] lemma signedInfDist_apply_of_mem {x : P} (hx : x ∈ s) : s.signedInfDist p x = 0 := by
71-
simp [signedInfDist_apply, orthogonalProjection_eq_self_iff.2 hx]
244+
simp [signedInfDist_eq_signedDist_orthogonalProjection, orthogonalProjection_eq_self_iff.2 hx]
72245

73246
variable {s p} in
74247
@[simp] lemma signedInfDist_eq_const_of_mem (h : p ∈ s) :
75248
s.signedInfDist p = AffineMap.const ℝ P (0 : ℝ) := by
76249
ext x
77-
simp [signedInfDist_apply, orthogonalProjection_eq_self_iff.2 h]
250+
simp [signedInfDist_def, orthogonalProjection_eq_self_iff.2 h]
78251

79252
variable {s p} in
80253
lemma abs_signedInfDist_eq_dist_of_mem_affineSpan_insert {x : P}
@@ -85,6 +258,10 @@ lemma abs_signedInfDist_eq_dist_of_mem_affineSpan_insert {x : P}
85258
simp [hp₀, dist_eq_norm_vsub, orthogonalProjection_eq_self_iff.2 hp₀,
86259
orthogonalProjection_vsub_orthogonalProjection, norm_smul, abs_mul]
87260

261+
lemma signedInfDist_singleton :
262+
(affineSpan ℝ ({q} : Set P)).signedInfDist p = signedDist (p -ᵥ q) q := by
263+
simpa using signedInfDist_eq_signedDist_of_mem (mem_affineSpan ℝ (Set.mem_singleton q))
264+
88265
end AffineSubspace
89266

90267
namespace Affine

0 commit comments

Comments
 (0)