src/weight_optim.rs

changeset 2
7a953a87b6c1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/weight_optim.rs	Tue Nov 29 15:36:12 2022 +0200
@@ -0,0 +1,131 @@
+//! Weight optimisation routines
+
+use numeric_literals::replace_float_literals;
+use alg_tools::nalgebra_support::ToNalgebraRealField;
+use alg_tools::loc::Loc;
+use alg_tools::iterate::AlgIteratorFactory;
+use crate::types::*;
+use crate::subproblem::*;
+use crate::measures::*;
+use crate::forward_model::ForwardModel;
+
+/// Helper struct for pre-initialising the finite-dimensional subproblems solver
+/// [`prepare_optimise_weights_l2`].
+///
+/// The pre-initialisation is done by [`prepare_optimise_weights_l2`].
+pub struct FindimData<F : Float> {
+    opAnorm_squared : F
+}
+
+/// Return a pre-initialisation struct for [`prepare_optimise_weights_l2`].
+///
+/// The parameter `opA` is the forward operator $A$.
+pub fn prepare_optimise_weights<F, A, const N : usize>(opA : &A) -> FindimData<F>
+where F : Float + ToNalgebraRealField,
+      A : ForwardModel<Loc<F, N>, F> {
+    FindimData{
+        opAnorm_squared : opA.opnorm_bound().powi(2)
+    }
+}
+
+/// Solve the finite-dimensional weight optimisation problem for the 2-norm-squared data fidelity
+/// point source localisation problem.
+///
+/// That is, we minimise
+/// <div>$$
+///     μ ↦ \frac{1}{2}\|Aμ-b\|_2^2 + α\|μ\|_ℳ + δ_{≥ 0}(μ)
+/// $$</div>
+/// only with respect to the weights of $μ$.
+///
+/// The parameter `μ` is the discrete measure whose weights are to be optimised.
+/// The `opA` parameter is the forward operator $A$, while `b`$ and `α` are as in the
+/// objective above. The method parameter are set in `inner` (see [`InnerSettings`]), while
+/// `iterator` is used to iterate the steps of the method, and `plotter` may be used to
+/// save intermediate iteration states as images. The parameter `findim_data` should be
+/// prepared using [`prepare_optimise_weights`]:
+///
+/// Returns the number of iterations taken by the method configured in `inner`.
+pub fn optimise_weights_l2<'a, F, A, I, const N : usize>(
+    μ : &mut DiscreteMeasure<Loc<F, N>, F>,
+    opA : &'a A,
+    b : &A::Observable,
+    α : F,
+    findim_data : &FindimData<F>,
+    inner : &InnerSettings<F>,
+    iterator : I
+) -> usize
+where F : Float + ToNalgebraRealField,
+      I : AlgIteratorFactory<F>,
+      A : ForwardModel<Loc<F, N>, F>
+{
+    // Form and solve finite-dimensional subproblem.
+    let (Ã, g̃) = opA.findim_quadratic_model(&μ, b);
+    let mut x = μ.masses_dvector();
+
+    // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to
+    // ℝ^n. This estimate is a good one for the matrix norm from ℝ^m to ℝ^n when the
+    // former is equipped with the 1-norm. We need the 2-norm. To pass from 1-norm to
+    // 2-norm, we estimate
+    //      ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2
+    //                 = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2},
+    // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no
+    // square root is needed when we scale:
+    let inner_τ = inner.τ0 / (findim_data.opAnorm_squared * F::cast_from(μ.len()));
+    let iters = quadratic_nonneg(inner.method, &Ã, &g̃, α, &mut x, inner_τ, iterator);
+    // Update masses of μ based on solution of finite-dimensional subproblem.
+    μ.set_masses_dvector(&x);
+
+    iters
+}
+
+/// Solve the finite-dimensional weight optimisation problem for the 2-norm-squared data fidelity
+/// point source localisation problem.
+///
+/// That is, we minimise
+/// <div>$$
+///     μ ↦ \|Aμ-b\|_1 + α\|μ\|_ℳ + δ_{≥ 0}(μ)
+/// $$</div>
+/// only with respect to the weights of $μ$.
+///
+/// The parameter `μ` is the discrete measure whose weights are to be optimised.
+/// The `opA` parameter is the forward operator $A$, while `b`$ and `α` are as in the
+/// objective above. The method parameter are set in `inner` (see [`InnerSettings`]), while
+/// `iterator` is used to iterate the steps of the method, and `plotter` may be used to
+/// save intermediate iteration states as images.
+///
+/// Returns the number of iterations taken by the method configured in `inner`.
+#[replace_float_literals(F::cast_from(literal))]
+pub fn optimise_weights_l1<'a, F, A, I, const N : usize>(
+    μ : &mut DiscreteMeasure<Loc<F, N>, F>,
+    opA : &'a A,
+    b : &A::Observable,
+    α : F,
+    findim_data : &FindimData<F>,
+    inner : &InnerSettings<F>,
+    iterator : I
+) -> usize
+where F : Float + ToNalgebraRealField,
+      I : AlgIteratorFactory<F>,
+      A : ForwardModel<Loc<F, N>, F>
+{
+    // Form and solve finite-dimensional subproblem.
+    let à = opA.findim_model(&μ);
+    let mut x = μ.masses_dvector();
+
+    // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to
+    // ℝ^n. This estimate is a good one for the matrix norm from ℝ^m to ℝ^n when the
+    // former is equipped with the 1-norm. We need the 2-norm. To pass from 1-norm to
+    // 2-norm, we estimate
+    //      ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2
+    //                 = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2},
+    // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no
+    // square root is needed when we scale:
+    let l = (findim_data.opAnorm_squared * F::cast_from(μ.len())).sqrt();
+    let inner_σ = (0.99 / inner.τ0) / l;
+    let inner_τ = inner.τ0 / l;
+    let iters = l1_nonneg(L1InnerMethod::PDPS, &Ã, b, α, &mut x, inner_τ, inner_σ, iterator);
+    // Update masses of μ based on solution of finite-dimensional subproblem.
+    μ.set_masses_dvector(&x);
+
+    iters
+}

mercurial