| 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, |