src/regularisation.rs

changeset 52
f0e8704d3f0e
parent 51
0693cc9ba9f0
equal deleted inserted replaced
31:6105b5cd8d89 52:f0e8704d3f0e
1 /*! 1 /*!
2 Regularisation terms 2 Regularisation terms
3 */ 3 */
4 4
5 use serde::{Serialize, Deserialize}; 5 #[allow(unused_imports)] // Used by documentation.
6 use crate::fb::pointsource_fb_reg;
7 use crate::fb::FBGenericConfig;
8 use crate::measures::{DeltaMeasure, Radon, RNDM};
9 #[allow(unused_imports)] // Used by documentation.
10 use crate::sliding_fb::pointsource_sliding_fb_reg;
11 use crate::types::*;
12 use alg_tools::instance::Instance;
13 use alg_tools::linops::Mapping;
14 use alg_tools::loc::Loc;
6 use alg_tools::norms::Norm; 15 use alg_tools::norms::Norm;
7 use alg_tools::linops::Apply; 16 use numeric_literals::replace_float_literals;
8 use alg_tools::loc::Loc; 17 use serde::{Deserialize, Serialize};
9 use crate::types::*; 18
10 use crate::measures::{ 19 use crate::subproblem::{
11 DiscreteMeasure, 20 l1squared_nonneg::l1squared_nonneg, l1squared_unconstrained::l1squared_unconstrained,
12 Radon 21 nonneg::quadratic_nonneg, unconstrained::quadratic_unconstrained,
13 }; 22 };
14 #[allow(unused_imports)] // Used by documentation. 23 use alg_tools::bisection_tree::{
15 use crate::fb::generic_pointsource_fb_reg; 24 BTSearch, Bounded, Bounds, LocalAnalysis, P2Minimise, SupportGenerator, BTFN,
16 25 };
17 /// The regularisation term $α\\|μ\\|\_{ℳ(Ω)} + δ_{≥ 0}(μ)$ for [`generic_pointsource_fb_reg`]. 26 use alg_tools::iterate::AlgIteratorFactory;
27 use alg_tools::nalgebra_support::ToNalgebraRealField;
28 use nalgebra::{DMatrix, DVector};
29
30 use std::cmp::Ordering::{Equal, Greater, Less};
31
32 /// The regularisation term $α\\|μ\\|\_{ℳ(Ω)} + δ_{≥ 0}(μ)$ for [`pointsource_fb_reg`] and other
33 /// algorithms.
18 /// 34 ///
19 /// The only member of the struct is the regularisation parameter α. 35 /// The only member of the struct is the regularisation parameter α.
20 #[derive(Copy, Clone, Debug, Serialize, Deserialize)] 36 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
21 pub struct NonnegRadonRegTerm<F : Float>(pub F /* α */); 37 pub struct NonnegRadonRegTerm<F: Float>(pub F /* α */);
22 38
23 impl<'a, F : Float> NonnegRadonRegTerm<F> { 39 impl<'a, F: Float> NonnegRadonRegTerm<F> {
24 /// Returns the regularisation parameter 40 /// Returns the regularisation parameter
25 pub fn α(&self) -> F { 41 pub fn α(&self) -> F {
26 let &NonnegRadonRegTerm(α) = self; 42 let &NonnegRadonRegTerm(α) = self;
27 α 43 α
28 } 44 }
29 } 45 }
30 46
31 impl<'a, F : Float, const N : usize> Apply<&'a DiscreteMeasure<Loc<F, N>, F>> 47 impl<'a, F: Float, const N: usize> Mapping<RNDM<F, N>> for NonnegRadonRegTerm<F> {
32 for NonnegRadonRegTerm<F> { 48 type Codomain = F;
33 type Output = F; 49
34 50 fn apply<I>(&self, μ: I) -> F
35 fn apply(&self, μ : &'a DiscreteMeasure<Loc<F, N>, F>) -> F { 51 where
36 self.α() * μ.norm(Radon) 52 I: Instance<RNDM<F, N>>,
37 } 53 {
38 } 54 self.α() * μ.eval(|x| x.norm(Radon))
39 55 }
40 56 }
41 /// The regularisation term $α\|μ\|_{ℳ(Ω)}$ for [`generic_pointsource_fb_reg`]. 57
58 /// The regularisation term $α\|μ\|_{ℳ(Ω)}$ for [`pointsource_fb_reg`].
42 /// 59 ///
43 /// The only member of the struct is the regularisation parameter α. 60 /// The only member of the struct is the regularisation parameter α.
44 #[derive(Copy, Clone, Debug, Serialize, Deserialize)] 61 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
45 pub struct RadonRegTerm<F : Float>(pub F /* α */); 62 pub struct RadonRegTerm<F: Float>(pub F /* α */);
46 63
47 64 impl<'a, F: Float> RadonRegTerm<F> {
48 impl<'a, F : Float> RadonRegTerm<F> {
49 /// Returns the regularisation parameter 65 /// Returns the regularisation parameter
50 pub fn α(&self) -> F { 66 pub fn α(&self) -> F {
51 let &RadonRegTerm(α) = self; 67 let &RadonRegTerm(α) = self;
52 α 68 α
53 } 69 }
54 } 70 }
55 71
56 impl<'a, F : Float, const N : usize> Apply<&'a DiscreteMeasure<Loc<F, N>, F>> 72 impl<'a, F: Float, const N: usize> Mapping<RNDM<F, N>> for RadonRegTerm<F> {
57 for RadonRegTerm<F> { 73 type Codomain = F;
58 type Output = F; 74
59 75 fn apply<I>(&self, μ: I) -> F
60 fn apply(&self, μ : &'a DiscreteMeasure<Loc<F, N>, F>) -> F { 76 where
61 self.α() * μ.norm(Radon) 77 I: Instance<RNDM<F, N>>,
78 {
79 self.α() * μ.eval(|x| x.norm(Radon))
62 } 80 }
63 } 81 }
64 82
65 /// Regularisation term configuration 83 /// Regularisation term configuration
66 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] 84 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
67 pub enum Regularisation<F : Float> { 85 pub enum Regularisation<F: Float> {
68 /// $α \\|μ\\|\_{ℳ(Ω)}$ 86 /// $α \\|μ\\|\_{ℳ(Ω)}$
69 Radon(F), 87 Radon(F),
70 /// $α\\|μ\\|\_{ℳ(Ω)} + δ_{≥ 0}(μ)$ 88 /// $α\\|μ\\|\_{ℳ(Ω)} + δ_{≥ 0}(μ)$
71 NonnegRadon(F), 89 NonnegRadon(F),
72 } 90 }
73 91
74 impl<'a, F : Float, const N : usize> Apply<&'a DiscreteMeasure<Loc<F, N>, F>> 92 impl<'a, F: Float, const N: usize> Mapping<RNDM<F, N>> for Regularisation<F> {
75 for Regularisation<F> { 93 type Codomain = F;
76 type Output = F; 94
77 95 fn apply<I>(&self, μ: I) -> F
78 fn apply(&self, μ : &'a DiscreteMeasure<Loc<F, N>, F>) -> F { 96 where
97 I: Instance<RNDM<F, N>>,
98 {
79 match *self { 99 match *self {
80 Self::Radon(α) => RadonRegTerm(α).apply(μ), 100 Self::Radon(α) => RadonRegTerm(α).apply(μ),
81 Self::NonnegRadon(α) => NonnegRadonRegTerm(α).apply(μ), 101 Self::NonnegRadon(α) => NonnegRadonRegTerm(α).apply(μ),
82 } 102 }
83 } 103 }
84 } 104 }
105
106 /// Abstraction of regularisation terms.
107 pub trait RegTerm<F: Float + ToNalgebraRealField, const N: usize>:
108 Mapping<RNDM<F, N>, Codomain = F>
109 {
110 /// Approximately solve the problem
111 /// <div>$$
112 /// \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + τ G(x)
113 /// $$</div>
114 /// for $G$ depending on the trait implementation.
115 ///
116 /// The parameter `mA` is $A$. An estimate for its opeator norm should be provided in
117 /// `mA_normest`. The initial iterate and output is `x`. The current main tolerance is `ε`.
118 ///
119 /// Returns the number of iterations taken.
120 fn solve_findim(
121 &self,
122 mA: &DMatrix<F::MixedType>,
123 g: &DVector<F::MixedType>,
124 τ: F,
125 x: &mut DVector<F::MixedType>,
126 mA_normest: F,
127 ε: F,
128 config: &FBGenericConfig<F>,
129 ) -> usize;
130
131 /// Approximately solve the problem
132 /// <div>$$
133 /// \min_{x ∈ ℝ^n} \frac{1}{2} |x-y|_1^2 - g^⊤ x + τ G(x)
134 /// $$</div>
135 /// for $G$ depending on the trait implementation.
136 ///
137 /// Returns the number of iterations taken.
138 fn solve_findim_l1squared(
139 &self,
140 y: &DVector<F::MixedType>,
141 g: &DVector<F::MixedType>,
142 τ: F,
143 x: &mut DVector<F::MixedType>,
144 ε: F,
145 config: &FBGenericConfig<F>,
146 ) -> usize;
147
148 /// Find a point where `d` may violate the tolerance `ε`.
149 ///
150 /// If `skip_by_rough_check` is set, do not find the point if a rough check indicates that we
151 /// are in bounds. `ε` is the current main tolerance and `τ` a scaling factor for the
152 /// regulariser.
153 ///
154 /// Returns `None` if `d` is in bounds either based on the rough check, or a more precise check
155 /// terminating early. Otherwise returns a possibly violating point, the value of `d` there,
156 /// and a boolean indicating whether the found point is in bounds.
157 fn find_tolerance_violation<G, BT>(
158 &self,
159 d: &mut BTFN<F, G, BT, N>,
160 τ: F,
161 ε: F,
162 skip_by_rough_check: bool,
163 config: &FBGenericConfig<F>,
164 ) -> Option<(Loc<F, N>, F, bool)>
165 where
166 BT: BTSearch<F, N, Agg = Bounds<F>>,
167 G: SupportGenerator<F, N, Id = BT::Data>,
168 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
169 {
170 self.find_tolerance_violation_slack(d, τ, ε, skip_by_rough_check, config, F::ZERO)
171 }
172
173 /// Find a point where `d` may violate the tolerance `ε`.
174 ///
175 /// This version includes a `slack` parameter to expand the tolerances.
176 /// It is used for Radon-norm squared proximal term in [`crate::prox_penalty::radon_squared`].
177 ///
178 /// If `skip_by_rough_check` is set, do not find the point if a rough check indicates that we
179 /// are in bounds. `ε` is the current main tolerance and `τ` a scaling factor for the
180 /// regulariser.
181 ///
182 /// Returns `None` if `d` is in bounds either based on the rough check, or a more precise check
183 /// terminating early. Otherwise returns a possibly violating point, the value of `d` there,
184 /// and a boolean indicating whether the found point is in bounds.
185 fn find_tolerance_violation_slack<G, BT>(
186 &self,
187 d: &mut BTFN<F, G, BT, N>,
188 τ: F,
189 ε: F,
190 skip_by_rough_check: bool,
191 config: &FBGenericConfig<F>,
192 slack: F,
193 ) -> Option<(Loc<F, N>, F, bool)>
194 where
195 BT: BTSearch<F, N, Agg = Bounds<F>>,
196 G: SupportGenerator<F, N, Id = BT::Data>,
197 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>;
198
199 /// Verify that `d` is in bounds `ε` for a merge candidate `μ`
200 ///
201 /// `ε` is the current main tolerance and `τ` a scaling factor for the regulariser.
202 fn verify_merge_candidate<G, BT>(
203 &self,
204 d: &mut BTFN<F, G, BT, N>,
205 μ: &RNDM<F, N>,
206 τ: F,
207 ε: F,
208 config: &FBGenericConfig<F>,
209 ) -> bool
210 where
211 BT: BTSearch<F, N, Agg = Bounds<F>>,
212 G: SupportGenerator<F, N, Id = BT::Data>,
213 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>;
214
215 /// Verify that `d` is in bounds `ε` for a merge candidate `μ`
216 ///
217 /// This version is s used for Radon-norm squared proximal term in
218 /// [`crate::prox_penalty::radon_squared`].
219 /// The [measures][crate::measures::DiscreteMeasure] `μ` and `radon_μ` are supposed to have
220 /// same coordinates at same agreeing indices.
221 ///
222 /// `ε` is the current main tolerance and `τ` a scaling factor for the regulariser.
223 fn verify_merge_candidate_radonsq<G, BT>(
224 &self,
225 d: &mut BTFN<F, G, BT, N>,
226 μ: &RNDM<F, N>,
227 τ: F,
228 ε: F,
229 config: &FBGenericConfig<F>,
230 radon_μ: &RNDM<F, N>,
231 ) -> bool
232 where
233 BT: BTSearch<F, N, Agg = Bounds<F>>,
234 G: SupportGenerator<F, N, Id = BT::Data>,
235 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>;
236
237 /// TODO: document this
238 fn target_bounds(&self, τ: F, ε: F) -> Option<Bounds<F>>;
239
240 /// Returns a scaling factor for the tolerance sequence.
241 ///
242 /// Typically this is the regularisation parameter.
243 fn tolerance_scaling(&self) -> F;
244 }
245
246 /// Abstraction of regularisation terms for [`pointsource_sliding_fb_reg`].
247 pub trait SlidingRegTerm<F: Float + ToNalgebraRealField, const N: usize>: RegTerm<F, N> {
248 /// Calculate $τ[w(z) - w(y)]$ for some w in the subdifferential of the regularisation
249 /// term, such that $-ε ≤ τw - d ≤ ε$.
250 fn goodness<G, BT>(
251 &self,
252 d: &mut BTFN<F, G, BT, N>,
253 μ: &RNDM<F, N>,
254 y: &Loc<F, N>,
255 z: &Loc<F, N>,
256 τ: F,
257 ε: F,
258 config: &FBGenericConfig<F>,
259 ) -> F
260 where
261 BT: BTSearch<F, N, Agg = Bounds<F>>,
262 G: SupportGenerator<F, N, Id = BT::Data>,
263 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>;
264
265 /// Convert bound on the regulariser to a bond on the Radon norm
266 fn radon_norm_bound(&self, b: F) -> F;
267 }
268
269 #[replace_float_literals(F::cast_from(literal))]
270 impl<F: Float + ToNalgebraRealField, const N: usize> RegTerm<F, N> for NonnegRadonRegTerm<F>
271 where
272 Cube<F, N>: P2Minimise<Loc<F, N>, F>,
273 {
274 fn solve_findim(
275 &self,
276 mA: &DMatrix<F::MixedType>,
277 g: &DVector<F::MixedType>,
278 τ: F,
279 x: &mut DVector<F::MixedType>,
280 mA_normest: F,
281 ε: F,
282 config: &FBGenericConfig<F>,
283 ) -> usize {
284 let inner_tolerance = ε * config.inner.tolerance_mult;
285 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance);
286 quadratic_nonneg(mA, g, τ * self.α(), x, mA_normest, &config.inner, inner_it)
287 }
288
289 fn solve_findim_l1squared(
290 &self,
291 y: &DVector<F::MixedType>,
292 g: &DVector<F::MixedType>,
293 τ: F,
294 x: &mut DVector<F::MixedType>,
295 ε: F,
296 config: &FBGenericConfig<F>,
297 ) -> usize {
298 let inner_tolerance = ε * config.inner.tolerance_mult;
299 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance);
300 l1squared_nonneg(y, g, τ * self.α(), 1.0, x, &config.inner, inner_it)
301 }
302
303 #[inline]
304 fn find_tolerance_violation_slack<G, BT>(
305 &self,
306 d: &mut BTFN<F, G, BT, N>,
307 τ: F,
308 ε: F,
309 skip_by_rough_check: bool,
310 config: &FBGenericConfig<F>,
311 slack: F,
312 ) -> Option<(Loc<F, N>, F, bool)>
313 where
314 BT: BTSearch<F, N, Agg = Bounds<F>>,
315 G: SupportGenerator<F, N, Id = BT::Data>,
316 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
317 {
318 let τα = τ * self.α();
319 let keep_above = -τα - slack - ε;
320 let minimise_below = -τα - slack - ε * config.insertion_cutoff_factor;
321 let refinement_tolerance = ε * config.refinement.tolerance_mult;
322
323 // If preliminary check indicates that we are in bounds, and if it otherwise matches
324 // the insertion strategy, skip insertion.
325 if skip_by_rough_check && d.bounds().lower() >= keep_above {
326 None
327 } else {
328 // If the rough check didn't indicate no insertion needed, find minimising point.
329 d.minimise_below(
330 minimise_below,
331 refinement_tolerance,
332 config.refinement.max_steps,
333 )
334 .map(|(ξ, v_ξ)| (ξ, v_ξ, v_ξ >= keep_above))
335 }
336 }
337
338 fn verify_merge_candidate<G, BT>(
339 &self,
340 d: &mut BTFN<F, G, BT, N>,
341 μ: &RNDM<F, N>,
342 τ: F,
343 ε: F,
344 config: &FBGenericConfig<F>,
345 ) -> bool
346 where
347 BT: BTSearch<F, N, Agg = Bounds<F>>,
348 G: SupportGenerator<F, N, Id = BT::Data>,
349 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
350 {
351 let τα = τ * self.α();
352 let refinement_tolerance = ε * config.refinement.tolerance_mult;
353 let merge_tolerance = config.merge_tolerance_mult * ε;
354 let keep_above = -τα - merge_tolerance;
355 let keep_supp_below = -τα + merge_tolerance;
356 let bnd = d.bounds();
357
358 return (bnd.upper() <= keep_supp_below
359 || μ
360 .iter_spikes()
361 .all(|&DeltaMeasure { α, ref x }| (α == 0.0) || d.apply(x) <= keep_supp_below))
362 && (bnd.lower() >= keep_above
363 || d.has_lower_bound(
364 keep_above,
365 refinement_tolerance,
366 config.refinement.max_steps,
367 ));
368 }
369
370 fn verify_merge_candidate_radonsq<G, BT>(
371 &self,
372 d: &mut BTFN<F, G, BT, N>,
373 μ: &RNDM<F, N>,
374 τ: F,
375 ε: F,
376 config: &FBGenericConfig<F>,
377 radon_μ: &RNDM<F, N>,
378 ) -> bool
379 where
380 BT: BTSearch<F, N, Agg = Bounds<F>>,
381 G: SupportGenerator<F, N, Id = BT::Data>,
382 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
383 {
384 let τα = τ * self.α();
385 let refinement_tolerance = ε * config.refinement.tolerance_mult;
386 let merge_tolerance = config.merge_tolerance_mult * ε;
387 let slack = radon_μ.norm(Radon);
388 let bnd = d.bounds();
389
390 return {
391 μ.both_matching(radon_μ).all(|(α, rα, x)| {
392 let v = -d.apply(x); // TODO: observe ad hoc negation here, after minus_τv
393 // switch to τv.
394 let (l1, u1) = match α.partial_cmp(&0.0).unwrap_or(Equal) {
395 Greater => (τα, τα),
396 _ => (F::NEG_INFINITY, τα),
397 // Less should not happen; treated as Equal
398 };
399 let (l2, u2) = match rα.partial_cmp(&0.0).unwrap_or(Equal) {
400 Greater => (slack, slack),
401 Equal => (-slack, slack),
402 Less => (-slack, -slack),
403 };
404 // TODO: both fail.
405 (l1 + l2 - merge_tolerance <= v) && (v <= u1 + u2 + merge_tolerance)
406 })
407 } && {
408 let keep_above = -τα - slack - merge_tolerance;
409 bnd.lower() <= keep_above
410 || d.has_lower_bound(
411 keep_above,
412 refinement_tolerance,
413 config.refinement.max_steps,
414 )
415 };
416 }
417
418 fn target_bounds(&self, τ: F, ε: F) -> Option<Bounds<F>> {
419 let τα = τ * self.α();
420 Some(Bounds(τα - ε, τα + ε))
421 }
422
423 fn tolerance_scaling(&self) -> F {
424 self.α()
425 }
426 }
427
428 #[replace_float_literals(F::cast_from(literal))]
429 impl<F: Float + ToNalgebraRealField, const N: usize> SlidingRegTerm<F, N> for NonnegRadonRegTerm<F>
430 where
431 Cube<F, N>: P2Minimise<Loc<F, N>, F>,
432 {
433 fn goodness<G, BT>(
434 &self,
435 d: &mut BTFN<F, G, BT, N>,
436 _μ: &RNDM<F, N>,
437 y: &Loc<F, N>,
438 z: &Loc<F, N>,
439 τ: F,
440 ε: F,
441 _config: &FBGenericConfig<F>,
442 ) -> F
443 where
444 BT: BTSearch<F, N, Agg = Bounds<F>>,
445 G: SupportGenerator<F, N, Id = BT::Data>,
446 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
447 {
448 let w = |x| 1.0.min((ε + d.apply(x)) / (τ * self.α()));
449 w(z) - w(y)
450 }
451
452 fn radon_norm_bound(&self, b: F) -> F {
453 b / self.α()
454 }
455 }
456
457 #[replace_float_literals(F::cast_from(literal))]
458 impl<F: Float + ToNalgebraRealField, const N: usize> RegTerm<F, N> for RadonRegTerm<F>
459 where
460 Cube<F, N>: P2Minimise<Loc<F, N>, F>,
461 {
462 fn solve_findim(
463 &self,
464 mA: &DMatrix<F::MixedType>,
465 g: &DVector<F::MixedType>,
466 τ: F,
467 x: &mut DVector<F::MixedType>,
468 mA_normest: F,
469 ε: F,
470 config: &FBGenericConfig<F>,
471 ) -> usize {
472 let inner_tolerance = ε * config.inner.tolerance_mult;
473 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance);
474 quadratic_unconstrained(mA, g, τ * self.α(), x, mA_normest, &config.inner, inner_it)
475 }
476
477 fn solve_findim_l1squared(
478 &self,
479 y: &DVector<F::MixedType>,
480 g: &DVector<F::MixedType>,
481 τ: F,
482 x: &mut DVector<F::MixedType>,
483 ε: F,
484 config: &FBGenericConfig<F>,
485 ) -> usize {
486 let inner_tolerance = ε * config.inner.tolerance_mult;
487 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance);
488 l1squared_unconstrained(y, g, τ * self.α(), 1.0, x, &config.inner, inner_it)
489 }
490
491 fn find_tolerance_violation_slack<G, BT>(
492 &self,
493 d: &mut BTFN<F, G, BT, N>,
494 τ: F,
495 ε: F,
496 skip_by_rough_check: bool,
497 config: &FBGenericConfig<F>,
498 slack: F,
499 ) -> Option<(Loc<F, N>, F, bool)>
500 where
501 BT: BTSearch<F, N, Agg = Bounds<F>>,
502 G: SupportGenerator<F, N, Id = BT::Data>,
503 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
504 {
505 let τα = τ * self.α();
506 let keep_below = τα + slack + ε;
507 let keep_above = -(τα + slack) - ε;
508 let maximise_above = τα + slack + ε * config.insertion_cutoff_factor;
509 let minimise_below = -(τα + slack) - ε * config.insertion_cutoff_factor;
510 let refinement_tolerance = ε * config.refinement.tolerance_mult;
511
512 // If preliminary check indicates that we are in bonds, and if it otherwise matches
513 // the insertion strategy, skip insertion.
514 if skip_by_rough_check && Bounds(keep_above, keep_below).superset(&d.bounds()) {
515 None
516 } else {
517 // If the rough check didn't indicate no insertion needed, find maximising point.
518 let mx = d.maximise_above(
519 maximise_above,
520 refinement_tolerance,
521 config.refinement.max_steps,
522 );
523 let mi = d.minimise_below(
524 minimise_below,
525 refinement_tolerance,
526 config.refinement.max_steps,
527 );
528
529 match (mx, mi) {
530 (None, None) => None,
531 (Some((ξ, v_ξ)), None) => Some((ξ, v_ξ, keep_below >= v_ξ)),
532 (None, Some((ζ, v_ζ))) => Some((ζ, v_ζ, keep_above <= v_ζ)),
533 (Some((ξ, v_ξ)), Some((ζ, v_ζ))) => {
534 if v_ξ - τα > τα - v_ζ {
535 Some((ξ, v_ξ, keep_below >= v_ξ))
536 } else {
537 Some((ζ, v_ζ, keep_above <= v_ζ))
538 }
539 }
540 }
541 }
542 }
543
544 fn verify_merge_candidate<G, BT>(
545 &self,
546 d: &mut BTFN<F, G, BT, N>,
547 μ: &RNDM<F, N>,
548 τ: F,
549 ε: F,
550 config: &FBGenericConfig<F>,
551 ) -> bool
552 where
553 BT: BTSearch<F, N, Agg = Bounds<F>>,
554 G: SupportGenerator<F, N, Id = BT::Data>,
555 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
556 {
557 let τα = τ * self.α();
558 let refinement_tolerance = ε * config.refinement.tolerance_mult;
559 let merge_tolerance = config.merge_tolerance_mult * ε;
560 let keep_below = τα + merge_tolerance;
561 let keep_above = -τα - merge_tolerance;
562 let keep_supp_pos_above = τα - merge_tolerance;
563 let keep_supp_neg_below = -τα + merge_tolerance;
564 let bnd = d.bounds();
565
566 return ((bnd.lower() >= keep_supp_pos_above && bnd.upper() <= keep_supp_neg_below)
567 || μ
568 .iter_spikes()
569 .all(|&DeltaMeasure { α: β, ref x }| match β.partial_cmp(&0.0) {
570 Some(Greater) => d.apply(x) >= keep_supp_pos_above,
571 Some(Less) => d.apply(x) <= keep_supp_neg_below,
572 _ => true,
573 }))
574 && (bnd.upper() <= keep_below
575 || d.has_upper_bound(
576 keep_below,
577 refinement_tolerance,
578 config.refinement.max_steps,
579 ))
580 && (bnd.lower() >= keep_above
581 || d.has_lower_bound(
582 keep_above,
583 refinement_tolerance,
584 config.refinement.max_steps,
585 ));
586 }
587
588 fn verify_merge_candidate_radonsq<G, BT>(
589 &self,
590 d: &mut BTFN<F, G, BT, N>,
591 μ: &RNDM<F, N>,
592 τ: F,
593 ε: F,
594 config: &FBGenericConfig<F>,
595 radon_μ: &RNDM<F, N>,
596 ) -> bool
597 where
598 BT: BTSearch<F, N, Agg = Bounds<F>>,
599 G: SupportGenerator<F, N, Id = BT::Data>,
600 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
601 {
602 let τα = τ * self.α();
603 let refinement_tolerance = ε * config.refinement.tolerance_mult;
604 let merge_tolerance = config.merge_tolerance_mult * ε;
605 let slack = radon_μ.norm(Radon);
606 let bnd = d.bounds();
607
608 return {
609 μ.both_matching(radon_μ).all(|(α, rα, x)| {
610 let v = d.apply(x);
611 let (l1, u1) = match α.partial_cmp(&0.0).unwrap_or(Equal) {
612 Greater => (τα, τα),
613 Equal => (-τα, τα),
614 Less => (-τα, -τα),
615 };
616 let (l2, u2) = match rα.partial_cmp(&0.0).unwrap_or(Equal) {
617 Greater => (slack, slack),
618 Equal => (-slack, slack),
619 Less => (-slack, -slack),
620 };
621 (l1 + l2 - merge_tolerance <= v) && (v <= u1 + u2 + merge_tolerance)
622 })
623 } && {
624 let keep_below = τα + slack + merge_tolerance;
625 bnd.upper() <= keep_below
626 || d.has_upper_bound(
627 keep_below,
628 refinement_tolerance,
629 config.refinement.max_steps,
630 )
631 } && {
632 let keep_above = -τα - slack - merge_tolerance;
633 bnd.lower() >= keep_above
634 || d.has_lower_bound(
635 keep_above,
636 refinement_tolerance,
637 config.refinement.max_steps,
638 )
639 };
640 }
641
642 fn target_bounds(&self, τ: F, ε: F) -> Option<Bounds<F>> {
643 let τα = τ * self.α();
644 Some(Bounds(-τα - ε, τα + ε))
645 }
646
647 fn tolerance_scaling(&self) -> F {
648 self.α()
649 }
650 }
651
652 #[replace_float_literals(F::cast_from(literal))]
653 impl<F: Float + ToNalgebraRealField, const N: usize> SlidingRegTerm<F, N> for RadonRegTerm<F>
654 where
655 Cube<F, N>: P2Minimise<Loc<F, N>, F>,
656 {
657 fn goodness<G, BT>(
658 &self,
659 d: &mut BTFN<F, G, BT, N>,
660 _μ: &RNDM<F, N>,
661 y: &Loc<F, N>,
662 z: &Loc<F, N>,
663 τ: F,
664 ε: F,
665 _config: &FBGenericConfig<F>,
666 ) -> F
667 where
668 BT: BTSearch<F, N, Agg = Bounds<F>>,
669 G: SupportGenerator<F, N, Id = BT::Data>,
670 G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
671 {
672 let α = self.α();
673 let w = |x| {
674 let dx = d.apply(x);
675 ((-ε + dx) / (τ * α)).max(1.0.min(ε + dx) / (τ * α))
676 };
677 w(z) - w(y)
678 }
679
680 fn radon_norm_bound(&self, b: F) -> F {
681 b / self.α()
682 }
683 }

mercurial