Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 593b1bb

Browse files
committed
feat(linear_algebra/affine_space): lemmas on affine spans (#3453)
Add more lemmas on affine spans; in particular, that the points in an `affine_span` are exactly the `affine_combination`s where the sum of weights equals 1, provided the underlying ring is nontrivial.
1 parent 65208ed commit 593b1bb

File tree

1 file changed

+290
-5
lines changed

1 file changed

+290
-5
lines changed

src/linear_algebra/affine_space.lean

Lines changed: 290 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Author: Joseph Myers.
55
-/
66
import algebra.add_torsor
7+
import data.indicator_function
78
import linear_algebra.basis
89

910
noncomputable theory
@@ -59,6 +60,14 @@ def vector_span (s : set P) : submodule k V := submodule.span k (vsub_set V s)
5960
lemma vector_span_def (s : set P) : vector_span k V s = submodule.span k (vsub_set V s) :=
6061
rfl
6162

63+
variables (P)
64+
65+
/-- The `vector_span` of the empty set is `⊥`. -/
66+
@[simp] lemma vector_span_empty : vector_span k V (∅ : set P) = ⊥ :=
67+
by rw [vector_span_def, vsub_set_empty, submodule.span_empty]
68+
69+
variables {P}
70+
6271
/-- The `vsub_set` lies within the `vector_span`. -/
6372
lemma vsub_set_subset_vector_span (s : set P) : vsub_set V s ⊆ vector_span k V s :=
6473
submodule.subset_span
@@ -85,11 +94,18 @@ lemma mem_span_points (p : P) (s : set P) : p ∈ s → p ∈ span_points k V s
8594
lemma subset_span_points (s : set P) : s ⊆ span_points k V s :=
8695
λ p, mem_span_points k V p s
8796

88-
/-- The set of points in the affine span of a nonempty set of points
89-
is nonempty. -/
90-
lemma span_points_nonempty_of_nonempty {s : set P} :
91-
s.nonempty → (span_points k V s).nonempty
92-
| ⟨p, hp⟩ := ⟨p, mem_span_points k V p s hp⟩
97+
/-- The `span_points` of a set is nonempty if and only if that set
98+
is. -/
99+
@[simp] lemma span_points_nonempty (s : set P) :
100+
(span_points k V s).nonempty ↔ s.nonempty :=
101+
begin
102+
split,
103+
{ contrapose,
104+
rw [set.not_nonempty_iff_eq_empty, set.not_nonempty_iff_eq_empty],
105+
intro h,
106+
simp [h, span_points] },
107+
{ exact λ ⟨p, hp⟩, ⟨p, mem_span_points k V p s hp⟩ }
108+
end
93109

94110
/-- Adding a point in the affine span and a vector in the spanning
95111
submodule produces a point in the affine span. -/
@@ -199,6 +215,16 @@ begin
199215
rw [vsub_self, smul_zero]
200216
end
201217

218+
/-- The weighted sum is unaffected by changing the weights to the
219+
corresponding indicator function and adding points to the set. -/
220+
lemma weighted_vsub_of_point_indicator_subset (w : ι → k) (p : ι → P) (b : P) {s₁ s₂ : finset ι}
221+
(h : s₁ ⊆ s₂) :
222+
s₁.weighted_vsub_of_point V p b w = s₂.weighted_vsub_of_point V p b (set.indicator ↑s₁ w) :=
223+
begin
224+
rw [weighted_vsub_of_point_apply, weighted_vsub_of_point_apply],
225+
exact set.sum_indicator_subset_of_eq_zero w (λ i wi, wi • (p i -ᵥ b : V)) h (λ i, zero_smul k _)
226+
end
227+
202228
/-- A weighted sum of the results of subtracting a default base point
203229
from the given points, as a linear map on the weights. This is
204230
intended to be used when the sum of the weights is 0; that condition
@@ -222,6 +248,17 @@ lemma weighted_vsub_eq_weighted_vsub_of_point_of_sum_eq_zero (w : ι → k) (p :
222248
(h : ∑ i in s, w i = 0) (b : P) : s.weighted_vsub V p w = s.weighted_vsub_of_point V p b w :=
223249
s.weighted_vsub_of_point_eq_of_sum_eq_zero V w p h _ _
224250

251+
/-- The `weighted_vsub` for an empty set is 0. -/
252+
@[simp] lemma weighted_vsub_empty (w : ι → k) (p : ι → P) :
253+
(∅ : finset ι).weighted_vsub V p w = 0 :=
254+
by simp [weighted_vsub_apply]
255+
256+
/-- The weighted sum is unaffected by changing the weights to the
257+
corresponding indicator function and adding points to the set. -/
258+
lemma weighted_vsub_indicator_subset (w : ι → k) (p : ι → P) {s₁ s₂ : finset ι} (h : s₁ ⊆ s₂) :
259+
s₁.weighted_vsub V p w = s₂.weighted_vsub V p (set.indicator ↑s₁ w) :=
260+
weighted_vsub_of_point_indicator_subset _ _ _ _ h
261+
225262
/-- A weighted sum of the results of subtracting a default base point
226263
from the given points, added to that base point. This is intended to
227264
be used when the sum of the weights is 1, in which case it is an
@@ -231,6 +268,18 @@ require it. -/
231268
def affine_combination (w : ι → k) (p : ι → P) : P :=
232269
s.weighted_vsub_of_point V p (classical.choice S.nonempty) w +ᵥ (classical.choice S.nonempty)
233270

271+
/-- Applying `affine_combination` with given weights. This is for the
272+
case where a result involving a default base point is OK (for example,
273+
when that base point will cancel out later); a more typical use case
274+
for `affine_combination` would involve selecting a preferred base
275+
point with
276+
`affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one` and
277+
then using `weighted_vsub_of_point_apply`. -/
278+
lemma affine_combination_apply (w : ι → k) (p : ι → P) :
279+
s.affine_combination V w p =
280+
s.weighted_vsub_of_point V p (classical.choice S.nonempty) w +ᵥ (classical.choice S.nonempty) :=
281+
rfl
282+
234283
/-- `affine_combination` gives the sum with any base point, when the
235284
sum of the weights is 1. -/
236285
lemma affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one (w : ι → k) (p : ι → P)
@@ -255,6 +304,31 @@ begin
255304
exact (linear_map.map_sub _ _ _).symm
256305
end
257306

307+
/-- An `affine_combination` equals a point if that point is in the set
308+
and has weight 1 and the other points in the set have weight 0. -/
309+
@[simp] lemma affine_combination_of_eq_one_of_eq_zero (w : ι → k) (p : ι → P) {i : ι}
310+
(his : i ∈ s) (hwi : w i = 1) (hw0 : ∀ i2 ∈ s, i2 ≠ i → w i2 = 0) :
311+
s.affine_combination V w p = p i :=
312+
begin
313+
have h1 : ∑ i in s, w i = 1 := hwi ▸ finset.sum_eq_single i hw0 (λ h, false.elim (h his)),
314+
rw [s.affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one V w p h1 (p i),
315+
weighted_vsub_of_point_apply],
316+
convert zero_vadd V (p i),
317+
convert finset.sum_eq_zero _,
318+
intros i2 hi2,
319+
by_cases h : i2 = i,
320+
{ simp [h] },
321+
{ simp [hw0 i2 hi2 h] }
322+
end
323+
324+
/-- An affine combination is unaffected by changing the weights to the
325+
corresponding indicator function and adding points to the set. -/
326+
lemma affine_combination_indicator_subset (w : ι → k) (p : ι → P) {s₁ s₂ : finset ι}
327+
(h : s₁ ⊆ s₂) :
328+
s₁.affine_combination V w p = s₂.affine_combination V (set.indicator ↑s₁ w) p :=
329+
by rw [affine_combination_apply, affine_combination_apply,
330+
weighted_vsub_of_point_indicator_subset _ _ _ _ h]
331+
258332
end finset
259333

260334
section affine_independent
@@ -895,6 +969,217 @@ end
895969

896970
end affine_subspace
897971

972+
namespace affine_space
973+
974+
variables (k : Type*) (V : Type*) {P : Type*} [ring k] [add_comm_group V] [module k V]
975+
[affine_space k V P]
976+
variables {ι : Type*}
977+
978+
/-- The `vector_span` is the span of the pairwise subtractions with a
979+
given point on the left. -/
980+
lemma vector_span_eq_span_vsub_set_left {s : set P} {p : P} (hp : p ∈ s) :
981+
vector_span k V s = submodule.span k {v | ∃ p2 ∈ s, v = p -ᵥ p2} :=
982+
begin
983+
rw vector_span_def,
984+
refine le_antisymm _ (submodule.span_mono _),
985+
{ rw submodule.span_le,
986+
rintros v ⟨p1, hp1, p2, hp2, hv⟩,
987+
rw ←vsub_sub_vsub_cancel_left V p1 p2 p at hv,
988+
rw [hv, submodule.mem_coe, submodule.mem_span],
989+
exact λ m hm, submodule.sub_mem _ (hm ⟨p2, hp2, rfl⟩) (hm ⟨p1, hp1, rfl⟩) },
990+
{ rintros v ⟨p2, hp2, hv⟩,
991+
exact ⟨p, hp, p2, hp2, hv⟩ }
992+
end
993+
994+
/-- The `vector_span` is the span of the pairwise subtractions with a
995+
given point on the right. -/
996+
lemma vector_span_eq_span_vsub_set_right {s : set P} {p : P} (hp : p ∈ s) :
997+
vector_span k V s = submodule.span k {v | ∃ p2 ∈ s, v = p2 -ᵥ p} :=
998+
begin
999+
rw vector_span_def,
1000+
refine le_antisymm _ (submodule.span_mono _),
1001+
{ rw submodule.span_le,
1002+
rintros v ⟨p1, hp1, p2, hp2, hv⟩,
1003+
rw ←vsub_sub_vsub_cancel_right V p1 p2 p at hv,
1004+
rw [hv, submodule.mem_coe, submodule.mem_span],
1005+
exact λ m hm, submodule.sub_mem _ (hm ⟨p1, hp1, rfl⟩) (hm ⟨p2, hp2, rfl⟩) },
1006+
{ rintros v ⟨p2, hp2, hv⟩,
1007+
exact ⟨p2, hp2, p, hp, hv⟩ }
1008+
end
1009+
1010+
/-- The `vector_span` of an indexed family is the span of the pairwise
1011+
subtractions with a given point on the left. -/
1012+
lemma vector_span_range_eq_span_range_vsub_left (p : ι → P) (i0 : ι) :
1013+
vector_span k V (set.range p) = submodule.span k (set.range (λ (i : ι), p i0 -ᵥ p i)) :=
1014+
begin
1015+
simp_rw [vector_span_eq_span_vsub_set_left k V (set.mem_range_self i0), set.exists_range_iff],
1016+
conv_lhs { congr, congr, funext, conv { congr, funext, rw eq_comm } },
1017+
refl
1018+
end
1019+
1020+
/-- The `vector_span` of an indexed family is the span of the pairwise
1021+
subtractions with a given point on the right. -/
1022+
lemma vector_span_range_eq_span_range_vsub_right (p : ι → P) (i0 : ι) :
1023+
vector_span k V (set.range p) = submodule.span k (set.range (λ (i : ι), p i -ᵥ p i0)) :=
1024+
begin
1025+
simp_rw [vector_span_eq_span_vsub_set_right k V (set.mem_range_self i0), set.exists_range_iff],
1026+
conv_lhs { congr, congr, funext, conv { congr, funext, rw eq_comm } },
1027+
refl
1028+
end
1029+
1030+
/-- The affine span of a set is nonempty if and only if that set
1031+
is. -/
1032+
lemma affine_span_nonempty (s : set P) :
1033+
(affine_span k V s : set P).nonempty ↔ s.nonempty :=
1034+
span_points_nonempty k V s
1035+
1036+
variables {k}
1037+
1038+
/-- A `weighted_vsub` with sum of weights 0 is in the `vector_span` of
1039+
an indexed family. -/
1040+
lemma weighted_vsub_mem_vector_span {s : finset ι} {w : ι → k}
1041+
(h : ∑ i in s, w i = 0) (p : ι → P) :
1042+
s.weighted_vsub V p w ∈ vector_span k V (set.range p) :=
1043+
begin
1044+
by_cases hn : nonempty ι,
1045+
{ cases hn with i0,
1046+
rw [vector_span_range_eq_span_range_vsub_right k V p i0, ←set.image_univ,
1047+
finsupp.mem_span_iff_total,
1048+
finset.weighted_vsub_eq_weighted_vsub_of_point_of_sum_eq_zero V s w p h (p i0),
1049+
finset.weighted_vsub_of_point_apply],
1050+
let w' := set.indicator ↑s w,
1051+
have hwx : ∀ i, w' i ≠ 0 → i ∈ s := λ i, set.mem_of_indicator_ne_zero,
1052+
use [finsupp.on_finset s w' hwx, set.subset_univ _],
1053+
rw [finsupp.total_apply, finsupp.on_finset_sum hwx],
1054+
{ apply finset.sum_congr rfl,
1055+
intros i hi,
1056+
simp [w', set.indicator_apply, if_pos hi] },
1057+
{ exact λ _, zero_smul k _ } },
1058+
{ simp [finset.eq_empty_of_not_nonempty hn s] }
1059+
end
1060+
1061+
/-- An `affine_combination` with sum of weights 1 is in the
1062+
`affine_span` of an indexed family, if the underlying ring is
1063+
nontrivial. -/
1064+
lemma affine_combination_mem_affine_span [nontrivial k] {s : finset ι} {w : ι → k}
1065+
(h : ∑ i in s, w i = 1) (p : ι → P) :
1066+
s.affine_combination V w p ∈ affine_span k V (set.range p) :=
1067+
begin
1068+
have hnz : ∑ i in s, w i ≠ 0 := h.symm ▸ one_ne_zero,
1069+
have hn : s.nonempty := finset.nonempty_of_sum_ne_zero hnz,
1070+
cases hn with i1 hi1,
1071+
let w1 : ι → k := function.update (function.const ι 0) i1 1,
1072+
have hw1 : ∑ i in s, w1 i = 1,
1073+
{ rw [finset.sum_update_of_mem hi1, finset.sum_const_zero, add_zero] },
1074+
have hw1s : s.affine_combination V w1 p = p i1 :=
1075+
s.affine_combination_of_eq_one_of_eq_zero V w1 p hi1 (function.update_same _ _ _)
1076+
(λ _ _ hne, function.update_noteq hne _ _),
1077+
have hv : s.affine_combination V w p -ᵥ p i1 ∈ (affine_span k V (set.range p)).direction,
1078+
{ rw [direction_affine_span, ←hw1s, finset.affine_combination_vsub],
1079+
apply weighted_vsub_mem_vector_span,
1080+
simp [pi.sub_apply, h, hw1] },
1081+
rw ←vsub_vadd V (s.affine_combination V w p) (p i1),
1082+
exact affine_subspace.vadd_mem_of_mem_direction hv (mem_affine_span k V (set.mem_range_self _))
1083+
end
1084+
1085+
variables (k) {V}
1086+
1087+
/-- A vector is in the `vector_span` of an indexed family if and only
1088+
if it is a `weighted_vsub` with sum of weights 0. -/
1089+
lemma mem_vector_span_iff_eq_weighted_vsub {v : V} {p : ι → P} :
1090+
v ∈ vector_span k V (set.range p) ↔
1091+
∃ (s : finset ι) (w : ι → k) (h : ∑ i in s, w i = 0), v = s.weighted_vsub V p w :=
1092+
begin
1093+
split,
1094+
{ by_cases hn : nonempty ι,
1095+
{ cases hn with i0,
1096+
rw [vector_span_range_eq_span_range_vsub_right k V p i0, ←set.image_univ,
1097+
finsupp.mem_span_iff_total],
1098+
rintros ⟨l, hl, hv⟩,
1099+
use insert i0 l.support,
1100+
set w := (l : ι → k) -
1101+
function.update (function.const ι 0 : ι → k) i0 (∑ i in l.support, l i) with hwdef,
1102+
use w,
1103+
have hw : ∑ i in insert i0 l.support, w i = 0,
1104+
{ rw hwdef,
1105+
simp_rw [pi.sub_apply, finset.sum_sub_distrib,
1106+
finset.sum_update_of_mem (finset.mem_insert_self _ _), finset.sum_const_zero,
1107+
finset.sum_insert_of_eq_zero_if_not_mem finsupp.not_mem_support_iff.1,
1108+
add_zero, sub_self] },
1109+
use hw,
1110+
have hz : w i0 • (p i0 -ᵥ p i0 : V) = 0 := (vsub_self V (p i0)).symm ▸ smul_zero _,
1111+
change (λ i, w i • (p i -ᵥ p i0 : V)) i0 = 0 at hz,
1112+
rw [finset.weighted_vsub_eq_weighted_vsub_of_point_of_sum_eq_zero V _ w p hw (p i0),
1113+
finset.weighted_vsub_of_point_apply, ←hv, finsupp.total_apply,
1114+
finset.sum_insert_zero hz],
1115+
change ∑ i in l.support, l i • _ = _,
1116+
congr,
1117+
ext i,
1118+
by_cases h : i = i0,
1119+
{ simp [h] },
1120+
{ simp [hwdef, h] } },
1121+
{ rw [set.range_eq_empty.2 hn, vector_span_empty, submodule.mem_bot],
1122+
intro hv,
1123+
use [∅],
1124+
simp [hv] } },
1125+
{ rintros ⟨s, w, hw, rfl⟩,
1126+
exact weighted_vsub_mem_vector_span V hw p }
1127+
end
1128+
1129+
variables {k}
1130+
1131+
/-- A point in the `affine_span` of an indexed family is an
1132+
`affine_combination` with sum of weights 1. -/
1133+
lemma eq_affine_combination_of_mem_affine_span {p1 : P} {p : ι → P}
1134+
(h : p1 ∈ affine_span k V (set.range p)) :
1135+
∃ (s : finset ι) (w : ι → k) (hw : ∑ i in s, w i = 1), p1 = s.affine_combination V w p :=
1136+
begin
1137+
have hn : ((affine_span k V (set.range p)) : set P).nonempty := ⟨p1, h⟩,
1138+
rw [affine_span_nonempty, set.range_nonempty_iff_nonempty] at hn,
1139+
cases hn with i0,
1140+
have h0 : p i0 ∈ affine_span k V (set.range p) := mem_affine_span k V (set.mem_range_self i0),
1141+
have hd : p1 -ᵥ p i0 ∈ (affine_span k V (set.range p)).direction :=
1142+
affine_subspace.vsub_mem_direction h h0,
1143+
rw [direction_affine_span, mem_vector_span_iff_eq_weighted_vsub] at hd,
1144+
rcases hd with ⟨s, w, h, hs⟩,
1145+
let s' := insert i0 s,
1146+
let w' := set.indicator ↑s w,
1147+
have h' : ∑ i in s', w' i = 0,
1148+
{ rw [←h, set.sum_indicator_subset _ (finset.subset_insert i0 s)] },
1149+
have hs' : s'.weighted_vsub V p w' = p1 -ᵥ p i0,
1150+
{ rw hs,
1151+
exact (finset.weighted_vsub_indicator_subset _ _ _ (finset.subset_insert i0 s)).symm },
1152+
let w0 : ι → k := function.update (function.const ι 0) i0 1,
1153+
have hw0 : ∑ i in s', w0 i = 1,
1154+
{ rw [finset.sum_update_of_mem (finset.mem_insert_self _ _), finset.sum_const_zero, add_zero] },
1155+
have hw0s : s'.affine_combination V w0 p = p i0 :=
1156+
s'.affine_combination_of_eq_one_of_eq_zero V w0 p
1157+
(finset.mem_insert_self _ _)
1158+
(function.update_same _ _ _)
1159+
(λ _ _ hne, function.update_noteq hne _ _),
1160+
use [s', w0 + w'],
1161+
split,
1162+
{ simp [pi.add_apply, finset.sum_add_distrib, hw0, h'] },
1163+
{ rw [add_comm, ←finset.weighted_vsub_vadd_affine_combination, hw0s, hs', vsub_vadd] }
1164+
end
1165+
1166+
variables (k V)
1167+
1168+
/-- A point is in the `affine_span` of an indexed family if and only
1169+
if it is an `affine_combination` with sum of weights 1, provided the
1170+
underlying ring is nontrivial. -/
1171+
lemma mem_affine_span_iff_eq_affine_combination [nontrivial k] {p1 : P} {p : ι → P} :
1172+
p1 ∈ affine_span k V (set.range p) ↔
1173+
∃ (s : finset ι) (w : ι → k) (hw : ∑ i in s, w i = 1), p1 = s.affine_combination V w p :=
1174+
begin
1175+
split,
1176+
{ exact eq_affine_combination_of_mem_affine_span },
1177+
{ rintros ⟨s, w, hw, rfl⟩,
1178+
exact affine_combination_mem_affine_span V hw p }
1179+
end
1180+
1181+
end affine_space
1182+
8981183
/-- An `affine_map k V1 P1 V2 P2` is a map from `P1` to `P2` that
8991184
induces a corresponding linear map from `V1` to `V2`. -/
9001185
structure affine_map (k : Type*) (V1 : Type*) (P1 : Type*) (V2 : Type*) (P2 : Type*)

0 commit comments

Comments
 (0)