src/regularisation.rs

changeset 76
b921ed0ab99b
parent 72
e9a460a0e638
equal deleted inserted replaced
75:677a5fd1b014 76:b921ed0ab99b
10 use crate::sliding_fb::pointsource_sliding_fb_reg; 10 use crate::sliding_fb::pointsource_sliding_fb_reg;
11 use crate::types::*; 11 use crate::types::*;
12 use alg_tools::instance::{Instance, Space}; 12 use alg_tools::instance::{Instance, Space};
13 use alg_tools::linops::Mapping; 13 use alg_tools::linops::Mapping;
14 use alg_tools::loc::Loc; 14 use alg_tools::loc::Loc;
15 use alg_tools::norms::Norm; 15 use alg_tools::norms::{Dist, Norm, L1};
16 use numeric_literals::replace_float_literals; 16 use numeric_literals::replace_float_literals;
17 use serde::{Deserialize, Serialize}; 17 use serde::{Deserialize, Serialize};
18 18
19 use crate::subproblem::{ 19 use crate::subproblem::{
20 l1squared_nonneg::l1squared_nonneg, l1squared_unconstrained::l1squared_unconstrained, 20 l1squared_nonneg::l1squared_nonneg, l1squared_unconstrained::l1squared_unconstrained,
21 nonneg::quadratic_nonneg, unconstrained::quadratic_unconstrained, 21 nonneg::quadratic_nonneg, unconstrained::quadratic_unconstrained,
22 }; 22 };
23 use alg_tools::bounds::{Bounds, MinMaxMapping}; 23 use alg_tools::bounds::{Bounds, MinMaxMapping};
24 use alg_tools::iterate::AlgIteratorFactory; 24 use alg_tools::iterate::AlgIteratorFactory;
25 use alg_tools::nalgebra_support::ToNalgebraRealField; 25 use alg_tools::nalgebra_support::ToNalgebraRealField;
26 use nalgebra::{DMatrix, DVector}; 26 use nalgebra::{DMatrix, DVector, DVectorView, DVectorViewMut};
27 27
28 use std::cmp::Ordering::{Equal, Greater, Less}; 28 use std::cmp::Ordering::{Equal, Greater, Less};
29 29
30 /// The regularisation term $α\\|μ\\|\_{ℳ(Ω)} + δ_{≥ 0}(μ)$ for [`pointsource_fb_reg`] and other 30 /// The regularisation term $α\\|μ\\|\_{ℳ(Ω)} + δ_{≥ 0}(μ)$ for [`pointsource_fb_reg`] and other
31 /// algorithms. 31 /// algorithms.
184 &self, 184 &self,
185 μ: &mut DiscreteMeasure<Domain, F>, 185 μ: &mut DiscreteMeasure<Domain, F>,
186 τv: &mut M, 186 τv: &mut M,
187 τ: F, 187 τ: F,
188 ε: F, 188 ε: F,
189 tolerance_violation: Option<(Domain, F, bool)>,
190 config: &InsertionConfig<F>, 189 config: &InsertionConfig<F>,
191 stats: &mut IterInfo<F>, 190 stats: &mut IterInfo<F>,
192 ) where 191 ) where
193 M: Mapping<Domain, Codomain = F>; 192 M: MinMaxMapping<Domain, F>;
194 193
195 /// Verify that `d` is in bounds `ε` for a merge candidate `μ` 194 /// Verify that `d` is in bounds `ε` for a merge candidate `μ`
196 /// 195 ///
197 /// This version is s used for Radon-norm squared proximal term in 196 /// This version is s used for Radon-norm squared proximal term in
198 /// [`crate::prox_penalty::radon_squared`]. 197 /// [`crate::prox_penalty::radon_squared`].
341 &self, 340 &self,
342 μ: &mut DiscreteMeasure<Loc<N, F>, F>, 341 μ: &mut DiscreteMeasure<Loc<N, F>, F>,
343 τv: &mut M, 342 τv: &mut M,
344 τ: F, 343 τ: F,
345 ε: F, 344 ε: F,
346 tolerance_violation: Option<(Loc<N, F>, F, bool)>,
347 config: &InsertionConfig<F>, 345 config: &InsertionConfig<F>,
348 stats: &mut IterInfo<F>, 346 stats: &mut IterInfo<F>,
349 ) where 347 ) where
350 M: Mapping<Loc<N, F>, Codomain = F>, 348 M: MinMaxMapping<Loc<N, F>, F>,
351 { 349 {
352 let τα = τ * self.α(); 350 let skip_by_rough_check = config.conservative && !config.merging.enabled;
353 let mut g: Vec<_> = μ 351
352 let τα = τ * self.α();
353 let mut g = μ
354 .iter_locations() 354 .iter_locations()
355 .map(|ζ| F::to_nalgebra_mixed(-τv.apply(ζ))) 355 .map(|ζ| F::to_nalgebra_mixed(-τv.apply(ζ)))
356 .collect(); 356 .collect::<Vec<_>>();
357 357
358 let new_spike_initial_weight = if let Some((ξ, v_ξ, _in_bounds)) = tolerance_violation { 358 // Form finite-dimensional subproblem. The subproblem references to the original μ^k
359 // Don't insert if existing spikes are almost as good 359 // from the beginning of the iteration are all contained in the immutable c and g.
360 if g.iter().all(|minus_τv| { 360 // TODO: observe negation of -τv after switch from minus_τv: finite-dimensional
361 -F::from_nalgebra_mixed(*minus_τv) > v_ξ + ε * config.refinement.tolerance_mult 361 // problems have not yet been updated to sign change.
362 }) { 362 let mut y = μ.masses_vec();
363 // Weight is found out by running the finite-dimensional optimisation algorithm 363 let mut x = y.clone();
364 // above 364 // Solve finite-dimensional subproblem.
365 // NOTE: cannot set α here before y is extracted 365 let inner_tolerance = ε * config.inner.tolerance_mult;
366 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance);
367
368 let slack = if μ.len() > 0 && config.conservative {
369 let l = x.len();
370 let mut x_na = DVectorViewMut::from_slice(x.as_mut_slice(), l);
371 let y_na = DVectorView::from_slice(y.as_slice(), y.len());
372 let g_na = DVectorView::from_slice(g.as_slice(), g.len());
373 stats.inner_iters +=
374 l1squared_nonneg(&y_na, &g_na, τα, &mut x_na, &config.inner, inner_it);
375 F::from_nalgebra_mixed(y_na.dist(&x_na, L1))
376 } else {
377 0.0
378 };
379
380 let opt = match self.find_tolerance_violation(τv, τ, ε + slack, skip_by_rough_check, config)
381 {
382 None => !config.conservative,
383 Some((ξ, v_ξ, _in_bounds)) => {
384 // Don't insert if existing spikes are almost as good
385 // if g.iter().all(|minus_τv| {
386 // -F::from_nalgebra_mixed(*minus_τv) > v_ξ + ε * config.refinement.tolerance_mult
387 //}) {
388
366 *μ += DeltaMeasure { x: ξ, α: 0.0 /*-v_ξ - τα*/ }; 389 *μ += DeltaMeasure { x: ξ, α: 0.0 /*-v_ξ - τα*/ };
367 g.push(F::to_nalgebra_mixed(-v_ξ)); 390 g.push(F::to_nalgebra_mixed(-v_ξ));
368 Some(-v_ξ - τα) 391 y.push(F::to_nalgebra_mixed(0.0));
369 } else { 392 x.push(F::to_nalgebra_mixed(-v_ξ - τα));
370 None 393 true
371 } 394 }
372 } else {
373 None
374 }; 395 };
375 396
376 // Optimise weights 397 if opt {
377 if μ.len() > 0 { 398 let l = x.len();
378 // Form finite-dimensional subproblem. The subproblem references to the original μ^k 399 let mut x_na = DVectorViewMut::from_slice(x.as_mut_slice(), l);
379 // from the beginning of the iteration are all contained in the immutable c and g. 400 let y_na = DVectorView::from_slice(y.as_slice(), y.len());
380 // TODO: observe negation of -τv after switch from minus_τv: finite-dimensional 401 let g_na = DVectorView::from_slice(g.as_slice(), g.len());
381 // problems have not yet been updated to sign change. 402 stats.inner_iters +=
382 let y = μ.masses_dvector(); 403 l1squared_nonneg(&y_na, &g_na, τα, &mut x_na, &config.inner, inner_it);
383 let mut x = y.clone();
384 let g_na = DVector::from_vec(g);
385 if let (Some(β), Some(dest)) = (new_spike_initial_weight, x.as_mut_slice().last_mut())
386 {
387 *dest = F::to_nalgebra_mixed(β);
388 }
389 // Solve finite-dimensional subproblem.
390 let inner_tolerance = ε * config.inner.tolerance_mult;
391 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance);
392 stats.inner_iters += l1squared_nonneg(&y, &g_na, τα, &mut x, &config.inner, inner_it);
393
394 // Update masses of μ based on solution of finite-dimensional subproblem.
395 μ.set_masses_dvector(&x);
396 } 404 }
405
406 // Update masses of μ based on solution of finite-dimensional subproblem.
407 μ.set_masses_vec(&x);
397 } 408 }
398 409
399 fn verify_merge_candidate_radonsq<M>( 410 fn verify_merge_candidate_radonsq<M>(
400 &self, 411 &self,
401 d: &mut M, 412 d: &mut M,
600 &self, 611 &self,
601 μ: &mut DiscreteMeasure<Loc<N, F>, F>, 612 μ: &mut DiscreteMeasure<Loc<N, F>, F>,
602 τv: &mut M, 613 τv: &mut M,
603 τ: F, 614 τ: F,
604 ε: F, 615 ε: F,
605 tolerance_violation: Option<(Loc<N, F>, F, bool)>,
606 config: &InsertionConfig<F>, 616 config: &InsertionConfig<F>,
607 stats: &mut IterInfo<F>, 617 stats: &mut IterInfo<F>,
608 ) where 618 ) where
609 M: Mapping<Loc<N, F>, Codomain = F>, 619 M: MinMaxMapping<Loc<N, F>, F>,
610 { 620 {
611 let τα = τ * self.α(); 621 let skip_by_rough_check = config.conservative && !config.merging.enabled;
612 let mut g: Vec<_> = μ 622
623 let τα = τ * self.α();
624 let mut g = μ
613 .iter_locations() 625 .iter_locations()
614 .map(|ζ| F::to_nalgebra_mixed(τv.apply(-ζ))) 626 .map(|ζ| F::to_nalgebra_mixed(-τv.apply(ζ)))
615 .collect(); 627 .collect::<Vec<_>>();
616 628
617 let new_spike_initial_weight = if let Some((ξ, v_ξ, _in_bounds)) = tolerance_violation { 629 // Form finite-dimensional subproblem. The subproblem references to the original μ^k
618 // Don't insert if existing spikes are almost as good 630 // from the beginning of the iteration are all contained in the immutable c and g.
619 let n = v_ξ.abs(); 631 // TODO: observe negation of -τv after switch from minus_τv: finite-dimensional
620 if g.iter().all(|minus_τv| { 632 // problems have not yet been updated to sign change.
621 F::from_nalgebra_mixed(*minus_τv).abs() < n - ε * config.refinement.tolerance_mult 633 let mut y = μ.masses_vec();
622 }) { 634 let mut x = y.clone();
623 // Weight is found out by running the finite-dimensional optimisation algorithm 635 // Solve finite-dimensional subproblem.
624 // above 636 let inner_tolerance = ε * config.inner.tolerance_mult;
625 // NOTE: cannot initialise α before y is extracted. 637 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance);
626 *μ += DeltaMeasure { x: ξ, α: 0.0 /*-(n + τα) * v_ξ.signum()*/ }; 638
639 let slack = if μ.len() > 0 && config.conservative {
640 let l = x.len();
641 let mut x_na = DVectorViewMut::from_slice(x.as_mut_slice(), l);
642 let y_na = DVectorView::from_slice(y.as_slice(), y.len());
643 let g_na = DVectorView::from_slice(g.as_slice(), g.len());
644 stats.inner_iters +=
645 l1squared_unconstrained(&y_na, &g_na, τα, &mut x_na, &config.inner, inner_it);
646 F::from_nalgebra_mixed(y_na.dist(&x_na, L1))
647 } else {
648 0.0
649 };
650
651 let opt = match self.find_tolerance_violation(τv, τ, ε + slack, skip_by_rough_check, config)
652 {
653 None => !config.conservative,
654 Some((ξ, v_ξ, _in_bounds)) => {
655 // Don't insert if existing spikes are almost as good
656 // if g.iter().all(|minus_τv| {
657 // -F::from_nalgebra_mixed(*minus_τv) > v_ξ + ε * config.refinement.tolerance_mult
658 //}) {
659
660 *μ += DeltaMeasure { x: ξ, α: 0.0 /*-v_ξ - τα*/ };
627 g.push(F::to_nalgebra_mixed(-v_ξ)); 661 g.push(F::to_nalgebra_mixed(-v_ξ));
628 Some(-(n + τα) * v_ξ.signum()) 662 y.push(F::to_nalgebra_mixed(0.0));
629 } else { 663 x.push(F::to_nalgebra_mixed(-v_ξ - τα));
630 None 664 true
631 } 665 }
632 } else {
633 None
634 }; 666 };
635 667
636 // Optimise weights 668 if opt {
637 if μ.len() > 0 { 669 let l = x.len();
638 // Form finite-dimensional subproblem. The subproblem references to the original μ^k 670 let mut x_na = DVectorViewMut::from_slice(x.as_mut_slice(), l);
639 // from the beginning of the iteration are all contained in the immutable c and g. 671 let y_na = DVectorView::from_slice(y.as_slice(), y.len());
640 // TODO: observe negation of -τv after switch from minus_τv: finite-dimensional 672 let g_na = DVectorView::from_slice(g.as_slice(), g.len());
641 // problems have not yet been updated to sign change.
642 let y = μ.masses_dvector();
643 let mut x = y.clone();
644 if let (Some(β), Some(dest)) = (new_spike_initial_weight, x.as_mut_slice().last_mut())
645 {
646 *dest = F::to_nalgebra_mixed(β);
647 }
648 let g_na = DVector::from_vec(g);
649 // Solve finite-dimensional subproblem.
650 let inner_tolerance = ε * config.inner.tolerance_mult;
651 let inner_it = config.inner.iterator_options.stop_target(inner_tolerance);
652 stats.inner_iters += 673 stats.inner_iters +=
653 l1squared_unconstrained(&y, &g_na, τα, &mut x, &config.inner, inner_it); 674 l1squared_unconstrained(&y_na, &g_na, τα, &mut x_na, &config.inner, inner_it);
654
655 // Update masses of μ based on solution of finite-dimensional subproblem.
656 μ.set_masses_dvector(&x);
657 } 675 }
676
677 // Update masses of μ based on solution of finite-dimensional subproblem.
678 μ.set_masses_vec(&x);
658 } 679 }
659 680
660 fn verify_merge_candidate_radonsq<M>( 681 fn verify_merge_candidate_radonsq<M>(
661 &self, 682 &self,
662 d: &mut M, 683 d: &mut M,

mercurial