Skip to content

Commit 3244381

Browse files
authored
feat: bijective change of variables in fitting sphere (#26)
1 parent 4c00eec commit 3244381

File tree

1 file changed

+63
-71
lines changed

1 file changed

+63
-71
lines changed

CvxLean/Examples/FittingSphere.lean

Lines changed: 63 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ open CvxLean Minimization Real BigOperators Matrix Finset
1212

1313
section LeastSquares
1414

15+
/- We first need some preliminaries on least squares. -/
16+
1517
def leastSquares {n : ℕ} (a : Fin n → ℝ) :=
1618
optimization (x : ℝ)
1719
minimize (∑ i, ((a i - x) ^ 2) : ℝ)
@@ -84,64 +86,81 @@ def fittingSphere :=
8486
optimization (c : Fin n → ℝ) (r : ℝ)
8587
minimize (∑ i, (‖(x i) - c‖ ^ 2 - r ^ 2) ^ 2 : ℝ)
8688
subject to
87-
h₁ : 0 < r
88-
89-
instance : ChangeOfVariables fun ((c, t) : (Fin n → ℝ) × ℝ) => (c, sqrt (t + ‖c‖ ^ 2)) :=
90-
{ inv := fun (c, r) => (c, r ^ 2 - ‖c‖ ^ 2),
91-
condition := fun (_, r) => 0 ≤ r,
92-
property := fun ⟨c, r⟩ h => by simp [sqrt_sq h] }
89+
h₁ : 0 ≤ r
90+
91+
-- Changes of variables ensuring bijection, which must also add the condition on `E` in the
92+
-- equivalence. TODO: Move to `CvxLean` core.
93+
94+
structure ChangeOfVariablesBij {D E} (c : E → D) where
95+
c_inv : D → E
96+
cond_D : D → Prop
97+
cond_E : E → Prop
98+
prop_D : ∀ x, cond_D x → c (c_inv x) = x
99+
prop_E : ∀ y, cond_E y → c_inv (c y) = y
100+
101+
@[equiv]
102+
def ChangeOfVariablesBij.toEquivalence {D E R} [Preorder R] {f : D → R} {cs : D → Prop} (c : E → D)
103+
(cov : ChangeOfVariablesBij c)
104+
(hD : ∀ x, cs x → cov.cond_D x)
105+
(hE : ∀ x, cs x → cov.cond_E (cov.c_inv x)) :
106+
⟨f, cs⟩ ≡ ⟨fun y => f (c y), fun y => cs (c y) ∧ cov.cond_E y⟩ :=
107+
Equivalence.ofStrongEquivalence <|
108+
{ phi := fun x => cov.c_inv x
109+
psi := fun y => c y
110+
phi_feasibility := fun x hx => by simp [feasible, cov.prop_D x (hD x hx)]; exact ⟨hx, hE x hx⟩
111+
psi_feasibility := fun y ⟨hy, _⟩ => hy
112+
phi_optimality := fun x hx => by simp [cov.prop_D x (hD x hx)]
113+
psi_optimality := fun y _ => by simp }
114+
115+
def covBij {n} : ChangeOfVariablesBij
116+
(fun ((c, t) : (Fin n → ℝ) × ℝ) => (c, sqrt (t + ‖c‖ ^ 2))) :=
117+
{ c_inv := fun (c, r) => (c, r ^ 2 - ‖c‖ ^ 2),
118+
cond_D := fun (_, r) => 0 ≤ r,
119+
cond_E := fun (c, t) => 0 ≤ t + ‖c‖ ^ 2,
120+
prop_D := fun (c, r) h => by simp [sqrt_sq h],
121+
prop_E := fun (c, t) h => by simp at h; simp [sq_sqrt h] }
93122

94123
equivalence* eqv/fittingSphereT (n m : ℕ) (x : Fin m → Fin n → ℝ) : fittingSphere n m x := by
95-
-- Change of variables.
124+
-- Change of variables (bijective) + some clean up.
125+
-- TODO: Do this with `change_of_variables` (or a new command `change_of_variables_bij`).
96126
equivalence_step =>
97-
apply ChangeOfVariables.toEquivalence
98-
(fun (ct : (Fin n → ℝ) × ℝ) => (ct.1, sqrt (ct.2 + ‖ct.1‖ ^ 2)))
99-
· rintro _ h; exact le_of_lt h
127+
apply ChangeOfVariablesBij.toEquivalence
128+
(fun (ct : (Fin n → ℝ) × ℝ) => (ct.1, sqrt (ct.2 + ‖ct.1‖ ^ 2))) covBij
129+
· rintro cr h; exact h
130+
. rintro ct _; simp [covBij, sq_nonneg]
100131
rename_vars [c, t]
101-
-- Clean up.
132+
rename_constrs [h₁, h₂]
102133
conv_constr h₁ => dsimp
103-
conv_obj => dsimp
134+
conv_constr h₂ => dsimp [covBij]
104135
-- Rewrite objective.
105136
rw_obj into (Vec.sum (((Vec.norm x) ^ 2 - 2 * (Matrix.mulVec x c) - Vec.const m t) ^ 2)) =>
106-
dsimp at h₁ ⊢; simp [Vec.sum, Vec.norm, Vec.const]; congr; funext i; congr 1;
107-
rw [norm_sub_sq (𝕜 := ℝ) (E := Fin n → ℝ), sq_sqrt (rpow_two _ ▸ le_of_lt (sqrt_pos.mp h₁))]
137+
simp [Vec.sum, Vec.norm, Vec.const]; congr; funext i; congr 1;
138+
rw [norm_sub_sq (𝕜 := ℝ) (E := Fin n → ℝ), sq_sqrt (rpow_two _ ▸ h₂)]
108139
simp [mulVec, inner, dotProduct]
140+
-- Remove redundant h₁.
141+
remove_constr h₁ => exact sqrt_nonneg _
109142

110143
#print fittingSphereT
111144
-- optimization (c : Fin n → ℝ) (t : ℝ)
112145
-- minimize Vec.sum ((Vec.norm x ^ 2 - 2 * mulVec x c - Vec.const m t) ^ 2)
113146
-- subject to
114-
-- h : 0 < sqrt (t + ‖c‖ ^ 2)
147+
-- h : 0 t + ‖c‖ ^ 2
115148

116-
-- Next, we proceed to remove the non-convex constraint by arguing that any (non-trivial) point that
117-
-- minimizes the objective function without the constraint, also satisfies the constraint. We define
118-
-- the problem directly, but note that we could also remove the constraint using the `relaxation`
119-
-- command.
149+
-- Next, we proceed to remove the non-convex constraint by arguing that any point that minimizes the
150+
-- objective function without the constraint, also satisfies the constraint. We define the problem
151+
-- directly, but note that we could also remove the constraint using the `reduction` command.
120152

121153
def fittingSphereConvex (n m : ℕ) (x : Fin m → Fin n → ℝ) :=
122154
optimization (c : Fin n → ℝ) (t : ℝ)
123155
minimize (Vec.sum ((Vec.norm x ^ 2 - 2 * mulVec x c - Vec.const m t) ^ 2) : ℝ)
124156

125-
/-- If the squared error is zero, then `aᵢ = x`. -/
126-
lemma vec_squared_norm_error_eq_zero_iff {n m : ℕ} (a : Fin m → Fin n → ℝ) (x : Fin n → ℝ) :
127-
∑ i, ‖a i - x‖ ^ 2 = 0 ↔ ∀ i, a i = x := by
128-
simp [rpow_two]
129-
rw [sum_eq_zero_iff_of_nonneg (fun _ _ => sq_nonneg _)]
130-
constructor
131-
· intros h i
132-
have hi := h i (by simp)
133-
rwa [sq_eq_zero_iff, norm_eq_zero, sub_eq_zero] at hi
134-
· intros h i _
135-
rw [sq_eq_zero_iff, norm_eq_zero, sub_eq_zero]
136-
exact h i
137-
138-
/-- This tells us that solving the relaxed problem is sufficient for optimal points if the solution
139-
is non-trivial. -/
157+
/-- This tells us that solving the relaxed problem is sufficient (i.e., it is a valid reduction). -/
140158
lemma optimal_convex_implies_optimal_t (hm : 0 < m) (c : Fin n → ℝ) (t : ℝ)
141-
(h_nontrivial : x ≠ Vec.const m c) (h_opt : (fittingSphereConvex n m x).optimal (c, t)) :
159+
(h_opt : (fittingSphereConvex n m x).optimal (c, t)) :
142160
(fittingSphereT n m x).optimal (c, t) := by
143161
simp [fittingSphereT, fittingSphereConvex, optimal, feasible] at h_opt ⊢
144162
constructor
163+
-- Feasibility.
145164
· let a := Vec.norm x ^ 2 - 2 * mulVec x c
146165
have h_ls : optimal (leastSquaresVec a) t := by
147166
refine ⟨trivial, ?_⟩
@@ -161,47 +180,20 @@ lemma optimal_convex_implies_optimal_t (hm : 0 < m) (c : Fin n → ℝ) (t : ℝ
161180
rw [norm_sub_sq (𝕜 := ℝ) (E := Fin n → ℝ)]
162181
congr
163182
-- We use the result to establish that `t + ‖c‖ ^ 2` is non-negative.
164-
have h_t_add_c2_nonneg : 0 ≤ t + ‖c‖ ^ 2 := by
165-
rw [h_t_add_c2_eq]
166-
apply mul_nonneg (by norm_num)
167-
apply sum_nonneg
168-
intros i _
169-
rw [rpow_two]
170-
exact sq_nonneg _
171-
cases (lt_or_eq_of_le h_t_add_c2_nonneg) with
172-
| inl h_t_add_c2_lt_zero =>
173-
-- If it is positive, we are done.
174-
convert h_t_add_c2_lt_zero; simp
175-
| inr h_t_add_c2_eq_zero =>
176-
-- Otherwise, it contradicts the non-triviality assumption.
177-
exfalso
178-
rw [h_t_add_c2_eq, zero_eq_mul] at h_t_add_c2_eq_zero
179-
rcases h_t_add_c2_eq_zero with (hc | h_sum_eq_zero)
180-
· simp at hc; linarith
181-
rw [vec_squared_norm_error_eq_zero_iff] at h_sum_eq_zero
182-
apply h_nontrivial
183-
funext i
184-
exact h_sum_eq_zero i
183+
rw [← rpow_two, h_t_add_c2_eq]
184+
apply mul_nonneg (by norm_num)
185+
apply sum_nonneg
186+
intros i _
187+
rw [rpow_two]
188+
exact sq_nonneg _
189+
-- Optimality.
185190
· intros c' x' _
186191
exact h_opt c' x'
187192

188-
/-- We express the nontriviality condition only in terms of `x` so that it can be checked. -/
189-
lemma non_triviality_condition (c : Fin n → ℝ) (hx : ∃ i j, x i ≠ x j) : x ≠ Vec.const m c := by
190-
intros h
191-
conv at hx => congr; ext i; rw [← not_forall]
192-
rw [← not_forall] at hx
193-
apply hx
194-
intros i j
195-
rw [congr_fun h i, congr_fun h j]
196-
simp [Vec.const]
197-
198193
/-- We show that we have a reduction via the identity map. -/
199-
def red (hm : 0 < m) (hx : ∃ i j, x i ≠ x j) :
200-
(fittingSphereT n m x) ≼ (fittingSphereConvex n m x) :=
194+
def red (hm : 0 < m) : (fittingSphereT n m x) ≼ (fittingSphereConvex n m x) :=
201195
{ psi := id,
202-
psi_optimality := fun (c, t) h_opt => by
203-
have h_nontrivial := non_triviality_condition n m x c hx
204-
exact optimal_convex_implies_optimal_t n m x hm c t h_nontrivial h_opt }
196+
psi_optimality := fun (c, t) h_opt => optimal_convex_implies_optimal_t n m x hm c t h_opt }
205197

206198
#print fittingSphereConvex
207199
-- optimization (c : Fin n → ℝ) (t : ℝ)

0 commit comments

Comments
 (0)