| 80 Self::Radon(α) => RadonRegTerm(α).apply(μ), |
104 Self::Radon(α) => RadonRegTerm(α).apply(μ), |
| 81 Self::NonnegRadon(α) => NonnegRadonRegTerm(α).apply(μ), |
105 Self::NonnegRadon(α) => NonnegRadonRegTerm(α).apply(μ), |
| 82 } |
106 } |
| 83 } |
107 } |
| 84 } |
108 } |
| |
109 |
| |
110 /// Abstraction of regularisation terms for [`generic_pointsource_fb_reg`]. |
| |
111 pub trait RegTerm<F : Float + ToNalgebraRealField, const N : usize> |
| |
112 : for<'a> Apply<&'a DiscreteMeasure<Loc<F, N>, F>, Output = F> { |
| |
113 /// Approximately solve the problem |
| |
114 /// <div>$$ |
| |
115 /// \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + τ G(x) |
| |
116 /// $$</div> |
| |
117 /// for $G$ depending on the trait implementation. |
| |
118 /// |
| |
119 /// The parameter `mA` is $A$. An estimate for its opeator norm should be provided in |
| |
120 /// `mA_normest`. The initial iterate and output is `x`. The current main tolerance is `ε`. |
| |
121 /// |
| |
122 /// Returns the number of iterations taken. |
| |
123 fn solve_findim( |
| |
124 &self, |
| |
125 mA : &DMatrix<F::MixedType>, |
| |
126 g : &DVector<F::MixedType>, |
| |
127 τ : F, |
| |
128 x : &mut DVector<F::MixedType>, |
| |
129 mA_normest : F, |
| |
130 ε : F, |
| |
131 config : &FBGenericConfig<F> |
| |
132 ) -> usize; |
| |
133 |
| |
134 /// Find a point where `d` may violate the tolerance `ε`. |
| |
135 /// |
| |
136 /// If `skip_by_rough_check` is set, do not find the point if a rough check indicates that we |
| |
137 /// are in bounds. `ε` is the current main tolerance and `τ` a scaling factor for the |
| |
138 /// regulariser. |
| |
139 /// |
| |
140 /// Returns `None` if `d` is in bounds either based on the rough check, or a more precise check |
| |
141 /// terminating early. Otherwise returns a possibly violating point, the value of `d` there, |
| |
142 /// and a boolean indicating whether the found point is in bounds. |
| |
143 fn find_tolerance_violation<G, BT>( |
| |
144 &self, |
| |
145 d : &mut BTFN<F, G, BT, N>, |
| |
146 τ : F, |
| |
147 ε : F, |
| |
148 skip_by_rough_check : bool, |
| |
149 config : &FBGenericConfig<F>, |
| |
150 ) -> Option<(Loc<F, N>, F, bool)> |
| |
151 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
152 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
153 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
154 + LocalAnalysis<F, Bounds<F>, N>; |
| |
155 |
| |
156 /// Verify that `d` is in bounds `ε` for a merge candidate `μ` |
| |
157 /// |
| |
158 /// `ε` is the current main tolerance and `τ` a scaling factor for the regulariser. |
| |
159 fn verify_merge_candidate<G, BT>( |
| |
160 &self, |
| |
161 d : &mut BTFN<F, G, BT, N>, |
| |
162 μ : &DiscreteMeasure<Loc<F, N>, F>, |
| |
163 τ : F, |
| |
164 ε : F, |
| |
165 config : &FBGenericConfig<F>, |
| |
166 ) -> bool |
| |
167 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
168 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
169 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
170 + LocalAnalysis<F, Bounds<F>, N>; |
| |
171 |
| |
172 /// TODO: document this |
| |
173 fn target_bounds(&self, τ : F, ε : F) -> Option<Bounds<F>>; |
| |
174 |
| |
175 /// Returns a scaling factor for the tolerance sequence. |
| |
176 /// |
| |
177 /// Typically this is the regularisation parameter. |
| |
178 fn tolerance_scaling(&self) -> F; |
| |
179 } |
| |
180 |
| |
181 /// Abstraction of regularisation terms for [`pointsource_sliding_fb_reg`]. |
| |
182 pub trait SlidingRegTerm<F : Float + ToNalgebraRealField, const N : usize> |
| |
183 : RegTerm<F, N> { |
| |
184 /// Calculate $τ[w(z) - w(y)]$ for some w in the subdifferential of the regularisation |
| |
185 /// term, such that $-ε ≤ τw - d ≤ ε$. |
| |
186 fn goodness<G, BT>( |
| |
187 &self, |
| |
188 d : &mut BTFN<F, G, BT, N>, |
| |
189 μ : &DiscreteMeasure<Loc<F, N>, F>, |
| |
190 y : &Loc<F, N>, |
| |
191 z : &Loc<F, N>, |
| |
192 τ : F, |
| |
193 ε : F, |
| |
194 config : &FBGenericConfig<F>, |
| |
195 ) -> F |
| |
196 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
197 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
198 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
199 + LocalAnalysis<F, Bounds<F>, N>; |
| |
200 } |
| |
201 |
| |
202 #[replace_float_literals(F::cast_from(literal))] |
| |
203 impl<F : Float + ToNalgebraRealField, const N : usize> RegTerm<F, N> |
| |
204 for NonnegRadonRegTerm<F> |
| |
205 where Cube<F, N> : P2Minimise<Loc<F, N>, F> { |
| |
206 fn solve_findim( |
| |
207 &self, |
| |
208 mA : &DMatrix<F::MixedType>, |
| |
209 g : &DVector<F::MixedType>, |
| |
210 τ : F, |
| |
211 x : &mut DVector<F::MixedType>, |
| |
212 mA_normest : F, |
| |
213 ε : F, |
| |
214 config : &FBGenericConfig<F> |
| |
215 ) -> usize { |
| |
216 let inner_tolerance = ε * config.inner.tolerance_mult; |
| |
217 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance); |
| |
218 let inner_τ = config.inner.τ0 / mA_normest; |
| |
219 quadratic_nonneg(config.inner.method, mA, g, τ * self.α(), x, |
| |
220 inner_τ, inner_it) |
| |
221 } |
| |
222 |
| |
223 #[inline] |
| |
224 fn find_tolerance_violation<G, BT>( |
| |
225 &self, |
| |
226 d : &mut BTFN<F, G, BT, N>, |
| |
227 τ : F, |
| |
228 ε : F, |
| |
229 skip_by_rough_check : bool, |
| |
230 config : &FBGenericConfig<F>, |
| |
231 ) -> Option<(Loc<F, N>, F, bool)> |
| |
232 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
233 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
234 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
235 + LocalAnalysis<F, Bounds<F>, N> { |
| |
236 let τα = τ * self.α(); |
| |
237 let keep_below = τα + ε; |
| |
238 let maximise_above = τα + ε * config.insertion_cutoff_factor; |
| |
239 let refinement_tolerance = ε * config.refinement.tolerance_mult; |
| |
240 |
| |
241 // If preliminary check indicates that we are in bonds, and if it otherwise matches |
| |
242 // the insertion strategy, skip insertion. |
| |
243 if skip_by_rough_check && d.bounds().upper() <= keep_below { |
| |
244 None |
| |
245 } else { |
| |
246 // If the rough check didn't indicate no insertion needed, find maximising point. |
| |
247 d.maximise_above(maximise_above, refinement_tolerance, config.refinement.max_steps) |
| |
248 .map(|(ξ, v_ξ)| (ξ, v_ξ, v_ξ <= keep_below)) |
| |
249 } |
| |
250 } |
| |
251 |
| |
252 fn verify_merge_candidate<G, BT>( |
| |
253 &self, |
| |
254 d : &mut BTFN<F, G, BT, N>, |
| |
255 μ : &DiscreteMeasure<Loc<F, N>, F>, |
| |
256 τ : F, |
| |
257 ε : F, |
| |
258 config : &FBGenericConfig<F>, |
| |
259 ) -> bool |
| |
260 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
261 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
262 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
263 + LocalAnalysis<F, Bounds<F>, N> { |
| |
264 let τα = τ * self.α(); |
| |
265 let refinement_tolerance = ε * config.refinement.tolerance_mult; |
| |
266 let merge_tolerance = config.merge_tolerance_mult * ε; |
| |
267 let keep_below = τα + merge_tolerance; |
| |
268 let keep_supp_above = τα - merge_tolerance; |
| |
269 let bnd = d.bounds(); |
| |
270 |
| |
271 return ( |
| |
272 bnd.lower() >= keep_supp_above |
| |
273 || |
| |
274 μ.iter_spikes().map(|&DeltaMeasure{ α : β, ref x }| { |
| |
275 (β == 0.0) || d.apply(x) >= keep_supp_above |
| |
276 }).all(std::convert::identity) |
| |
277 ) && ( |
| |
278 bnd.upper() <= keep_below |
| |
279 || |
| |
280 d.has_upper_bound(keep_below, refinement_tolerance, config.refinement.max_steps) |
| |
281 ) |
| |
282 } |
| |
283 |
| |
284 fn target_bounds(&self, τ : F, ε : F) -> Option<Bounds<F>> { |
| |
285 let τα = τ * self.α(); |
| |
286 Some(Bounds(τα - ε, τα + ε)) |
| |
287 } |
| |
288 |
| |
289 fn tolerance_scaling(&self) -> F { |
| |
290 self.α() |
| |
291 } |
| |
292 } |
| |
293 |
| |
294 #[replace_float_literals(F::cast_from(literal))] |
| |
295 impl<F : Float + ToNalgebraRealField, const N : usize> SlidingRegTerm<F, N> |
| |
296 for NonnegRadonRegTerm<F> |
| |
297 where Cube<F, N> : P2Minimise<Loc<F, N>, F> { |
| |
298 |
| |
299 fn goodness<G, BT>( |
| |
300 &self, |
| |
301 d : &mut BTFN<F, G, BT, N>, |
| |
302 _μ : &DiscreteMeasure<Loc<F, N>, F>, |
| |
303 y : &Loc<F, N>, |
| |
304 z : &Loc<F, N>, |
| |
305 τ : F, |
| |
306 ε : F, |
| |
307 _config : &FBGenericConfig<F>, |
| |
308 ) -> F |
| |
309 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
310 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
311 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
312 + LocalAnalysis<F, Bounds<F>, N> { |
| |
313 //let w = |x| 1.0.min((ε + d.apply(x))/(τ * self.α())); |
| |
314 let τw = |x| τ.min((ε + d.apply(x))/self.α()); |
| |
315 τw(z) - τw(y) |
| |
316 } |
| |
317 } |
| |
318 |
| |
319 #[replace_float_literals(F::cast_from(literal))] |
| |
320 impl<F : Float + ToNalgebraRealField, const N : usize> RegTerm<F, N> for RadonRegTerm<F> |
| |
321 where Cube<F, N> : P2Minimise<Loc<F, N>, F> { |
| |
322 fn solve_findim( |
| |
323 &self, |
| |
324 mA : &DMatrix<F::MixedType>, |
| |
325 g : &DVector<F::MixedType>, |
| |
326 τ : F, |
| |
327 x : &mut DVector<F::MixedType>, |
| |
328 mA_normest: F, |
| |
329 ε : F, |
| |
330 config : &FBGenericConfig<F> |
| |
331 ) -> usize { |
| |
332 let inner_tolerance = ε * config.inner.tolerance_mult; |
| |
333 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance); |
| |
334 let inner_τ = config.inner.τ0 / mA_normest; |
| |
335 quadratic_unconstrained(config.inner.method, mA, g, τ * self.α(), x, |
| |
336 inner_τ, inner_it) |
| |
337 } |
| |
338 |
| |
339 fn find_tolerance_violation<G, BT>( |
| |
340 &self, |
| |
341 d : &mut BTFN<F, G, BT, N>, |
| |
342 τ : F, |
| |
343 ε : F, |
| |
344 skip_by_rough_check : bool, |
| |
345 config : &FBGenericConfig<F>, |
| |
346 ) -> Option<(Loc<F, N>, F, bool)> |
| |
347 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
348 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
349 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
350 + LocalAnalysis<F, Bounds<F>, N> { |
| |
351 let τα = τ * self.α(); |
| |
352 let keep_below = τα + ε; |
| |
353 let keep_above = -τα - ε; |
| |
354 let maximise_above = τα + ε * config.insertion_cutoff_factor; |
| |
355 let minimise_below = -τα - ε * config.insertion_cutoff_factor; |
| |
356 let refinement_tolerance = ε * config.refinement.tolerance_mult; |
| |
357 |
| |
358 // If preliminary check indicates that we are in bonds, and if it otherwise matches |
| |
359 // the insertion strategy, skip insertion. |
| |
360 if skip_by_rough_check && Bounds(keep_above, keep_below).superset(&d.bounds()) { |
| |
361 None |
| |
362 } else { |
| |
363 // If the rough check didn't indicate no insertion needed, find maximising point. |
| |
364 let mx = d.maximise_above(maximise_above, refinement_tolerance, |
| |
365 config.refinement.max_steps); |
| |
366 let mi = d.minimise_below(minimise_below, refinement_tolerance, |
| |
367 config.refinement.max_steps); |
| |
368 |
| |
369 match (mx, mi) { |
| |
370 (None, None) => None, |
| |
371 (Some((ξ, v_ξ)), None) => Some((ξ, v_ξ, keep_below >= v_ξ)), |
| |
372 (None, Some((ζ, v_ζ))) => Some((ζ, v_ζ, keep_above <= v_ζ)), |
| |
373 (Some((ξ, v_ξ)), Some((ζ, v_ζ))) => { |
| |
374 if v_ξ - τα > τα - v_ζ { |
| |
375 Some((ξ, v_ξ, keep_below >= v_ξ)) |
| |
376 } else { |
| |
377 Some((ζ, v_ζ, keep_above <= v_ζ)) |
| |
378 } |
| |
379 } |
| |
380 } |
| |
381 } |
| |
382 } |
| |
383 |
| |
384 fn verify_merge_candidate<G, BT>( |
| |
385 &self, |
| |
386 d : &mut BTFN<F, G, BT, N>, |
| |
387 μ : &DiscreteMeasure<Loc<F, N>, F>, |
| |
388 τ : F, |
| |
389 ε : F, |
| |
390 config : &FBGenericConfig<F>, |
| |
391 ) -> bool |
| |
392 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
393 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
394 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
395 + LocalAnalysis<F, Bounds<F>, N> { |
| |
396 let τα = τ * self.α(); |
| |
397 let refinement_tolerance = ε * config.refinement.tolerance_mult; |
| |
398 let merge_tolerance = config.merge_tolerance_mult * ε; |
| |
399 let keep_below = τα + merge_tolerance; |
| |
400 let keep_above = -τα - merge_tolerance; |
| |
401 let keep_supp_pos_above = τα - merge_tolerance; |
| |
402 let keep_supp_neg_below = -τα + merge_tolerance; |
| |
403 let bnd = d.bounds(); |
| |
404 |
| |
405 return ( |
| |
406 (bnd.lower() >= keep_supp_pos_above && bnd.upper() <= keep_supp_neg_below) |
| |
407 || |
| |
408 μ.iter_spikes().map(|&DeltaMeasure{ α : β, ref x }| { |
| |
409 use std::cmp::Ordering::*; |
| |
410 match β.partial_cmp(&0.0) { |
| |
411 Some(Greater) => d.apply(x) >= keep_supp_pos_above, |
| |
412 Some(Less) => d.apply(x) <= keep_supp_neg_below, |
| |
413 _ => true, |
| |
414 } |
| |
415 }).all(std::convert::identity) |
| |
416 ) && ( |
| |
417 bnd.upper() <= keep_below |
| |
418 || |
| |
419 d.has_upper_bound(keep_below, refinement_tolerance, |
| |
420 config.refinement.max_steps) |
| |
421 ) && ( |
| |
422 bnd.lower() >= keep_above |
| |
423 || |
| |
424 d.has_lower_bound(keep_above, refinement_tolerance, |
| |
425 config.refinement.max_steps) |
| |
426 ) |
| |
427 } |
| |
428 |
| |
429 fn target_bounds(&self, τ : F, ε : F) -> Option<Bounds<F>> { |
| |
430 let τα = τ * self.α(); |
| |
431 Some(Bounds(-τα - ε, τα + ε)) |
| |
432 } |
| |
433 |
| |
434 fn tolerance_scaling(&self) -> F { |
| |
435 self.α() |
| |
436 } |
| |
437 } |
| |
438 |
| |
439 #[replace_float_literals(F::cast_from(literal))] |
| |
440 impl<F : Float + ToNalgebraRealField, const N : usize> SlidingRegTerm<F, N> |
| |
441 for RadonRegTerm<F> |
| |
442 where Cube<F, N> : P2Minimise<Loc<F, N>, F> { |
| |
443 |
| |
444 fn goodness<G, BT>( |
| |
445 &self, |
| |
446 d : &mut BTFN<F, G, BT, N>, |
| |
447 _μ : &DiscreteMeasure<Loc<F, N>, F>, |
| |
448 y : &Loc<F, N>, |
| |
449 z : &Loc<F, N>, |
| |
450 τ : F, |
| |
451 ε : F, |
| |
452 _config : &FBGenericConfig<F>, |
| |
453 ) -> F |
| |
454 where BT : BTSearch<F, N, Agg=Bounds<F>>, |
| |
455 G : SupportGenerator<F, N, Id=BT::Data>, |
| |
456 G::SupportType : Mapping<Loc<F, N>,Codomain=F> |
| |
457 + LocalAnalysis<F, Bounds<F>, N> { |
| |
458 |
| |
459 let α = self.α(); |
| |
460 // let w = |x| { |
| |
461 // let dx = d.apply(x); |
| |
462 // ((-ε + dx)/(τ * α)).max(1.0.min(ε + dx)/(τ * α)) |
| |
463 // }; |
| |
464 let τw = |x| { |
| |
465 let dx = d.apply(x); |
| |
466 ((-ε + dx)/α).max(τ.min(ε + dx)/α) |
| |
467 }; |
| |
468 τw(z) - τw(y) |
| |
469 } |
| |
470 } |