src/mapping/dataterm.rs

Wed, 30 Apr 2025 23:57:17 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Wed, 30 Apr 2025 23:57:17 -0500
branch
dev
changeset 111
e1455e48bd2b
parent 110
a1278320be26
child 124
6aa955ad8122
permissions
-rw-r--r--

clone

/*!
General deata terms of the form $g(Ax-b)$ for an operator $A$
to a [`Euclidean`] space, and a function g on that space.
*/

#![allow(non_snake_case)]

use super::{DifferentiableImpl, DifferentiableMapping, LipschitzDifferentiableImpl, Mapping};
use crate::convex::ConvexMapping;
use crate::error::DynResult;
use crate::instance::{Instance, Space};
use crate::linops::{BoundedLinear, Linear, Preadjointable};
use crate::norms::{Normed, L2};
use crate::types::Float;
use std::ops::Sub;

//use serde::{Deserialize, Serialize};

/// Functions of the form $g(Ax-b)$ for an operator $A$, data $b$, and fidelity $g$.
pub struct DataTerm<
    F: Float,
    Domain: Space,
    A: Mapping<Domain>,
    G: Mapping<A::Codomain, Codomain = F>,
> {
    // The operator A
    opA: A,
    // The data b
    b: <A as Mapping<Domain>>::Codomain,
    // The outer fidelity
    g: G,
}

// Derive has troubles with `b`.
impl<F, Domain, A, G> Clone for DataTerm<F, Domain, A, G>
where
    F: Float,
    Domain: Space,
    A: Mapping<Domain> + Clone,
    <A as Mapping<Domain>>::Codomain: Clone,
    G: Mapping<A::Codomain, Codomain = F> + Clone,
{
    fn clone(&self) -> Self {
        DataTerm {
            opA: self.opA.clone(),
            b: self.b.clone(),
            g: self.g.clone(),
        }
    }
}

#[allow(non_snake_case)]
impl<F: Float, Domain: Space, A: Mapping<Domain>, G: Mapping<A::Codomain, Codomain = F>>
    DataTerm<F, Domain, A, G>
{
    pub fn new(opA: A, b: A::Codomain, g: G) -> Self {
        DataTerm { opA, b, g }
    }

    pub fn operator(&self) -> &'_ A {
        &self.opA
    }

    pub fn data(&self) -> &'_ <A as Mapping<Domain>>::Codomain {
        &self.b
    }

    pub fn fidelity(&self) -> &'_ G {
        &self.g
    }
}

//+ AdjointProductBoundedBy<RNDM<F, N>, P, FloatType = F>,

impl<F, X, A, G> Mapping<X> for DataTerm<F, X, A, G>
where
    F: Float,
    X: Space,
    A: Mapping<X>,
    G: Mapping<A::Codomain, Codomain = F>,
    A::Codomain: for<'a> Sub<&'a A::Codomain, Output = A::Codomain>,
{
    type Codomain = F;

    fn apply<I: Instance<X>>(&self, x: I) -> F {
        // TODO: possibly (if at all more effcient) use GEMV once generalised
        // to not require preallocation. However, Rust should be pretty efficient
        // at not doing preallocations or anything here, as the result of self.opA.apply()
        // can be consumed, so maybe GEMV is no use.
        self.g.apply(self.opA.apply(x) - &self.b)
    }
}

impl<F, X, A, G> ConvexMapping<X, F> for DataTerm<F, X, A, G>
where
    F: Float,
    X: Normed<F>,
    A: Linear<X>,
    G: ConvexMapping<A::Codomain, F>,
    A::Codomain: Normed<F> + for<'a> Sub<&'a A::Codomain, Output = A::Codomain>,
{
}

impl<F, X, Y, A, G> DifferentiableImpl<X> for DataTerm<F, X, A, G>
where
    F: Float,
    X: Space,
    Y: Space + for<'a> Sub<&'a Y, Output = Y>,
    //<A as Mapping<X>>::Codomain: Euclidean<F>,
    A: Linear<X, Codomain = Y> + Preadjointable<X, G::DerivativeDomain>,
    //<<A as Mapping<X>>::Codomain as Euclidean<F>>::Output: Instance<<A as Mapping<X>>::Codomain>,
    G: DifferentiableMapping<Y, Codomain = F>,
{
    type Derivative = A::PreadjointCodomain;

    fn differential_impl<I: Instance<X>>(&self, x: I) -> Self::Derivative {
        // TODO: possibly (if at all more effcient) use GEMV once generalised
        // to not require preallocation. However, Rust should be pretty efficient
        // at not doing preallocations or anything here, as the result of self.opA.apply()
        // can be consumed, so maybe GEMV is no use.
        //self.opA.preadjoint().apply(self.opA.apply(x) - self.b)
        self.opA
            .preadjoint()
            .apply(self.g.diff_ref().apply(self.opA.apply(x) - &self.b))
    }
}

impl<'a, F, X, Y, A, G> LipschitzDifferentiableImpl<X, X::NormExp> for DataTerm<F, X, A, G>
where
    F: Float,
    X: Normed<F> + Clone,
    Y: Normed<F>,
    A: Clone + BoundedLinear<X, X::NormExp, L2, F, Codomain = Y>,
    G: Mapping<Y, Codomain = F> + LipschitzDifferentiableImpl<Y, Y::NormExp>,
    Self: DifferentiableImpl<X>,
{
    type FloatType = F;

    fn diff_lipschitz_factor(&self, seminorm: X::NormExp) -> DynResult<F> {
        Ok(self.opA.opnorm_bound(seminorm, L2)?.powi(2))
    }
}

mercurial