Struct varpro::model::builder::SeparableModelBuilder[][src]

#[must_use =
  "The builder should be transformed into a model using the build() method"]pub struct SeparableModelBuilder<ScalarType> where
    ScalarType: Scalar
{ /* fields omitted */ }

! A builder that allows us to construct a valid SeparableModel.

Introduction

As explained elsewhere, the separable model $\vec{f}(\vec{x},\vec{\alpha},\vec{c})$ is a vector valued function that is the linear combination of nonlinear base functions.

 \vec{f}(\vec{x},\vec{\alpha},\vec{c}) = \sum_{j=1}^{N_{basis}} c_j \vec{f}_j(\vec{x},S_j(\alpha))

The basis functions $\vec{f}_j(\vec{x},S_j(\alpha))$ depend on the independent variable $\vec{x}$ and a subset $S_j(\alpha)$ of the nonlinear model parameters $\vec{\alpha}$. The subset may or may not be different for each model function. It is okay if two or more model functions depend on the same parameters.

Usage

The SeparableModelBuilder is concerned with building a model from basis functions and their derivatives. This is done as a step by step process.

Constructing an Empty Builder

The first step is to create an empty builder by specifying the complete set of nonlinear parameters that the model will be depending on. This is done by calling SeparableModelBuilder::new and specifying the list of parameters of the model by name.

Adding Basis Functions to The Model

Basis functions come in two flavors. Those that depend on a subset of the nonlinear parameters $\vec{\alpha}$ and those that do not. Both function types have to obey certain rules to be considered valid:

Function Arguments and Output

  • The first argument of the function is a &DVector type and is a reference to the vector of grid points $\vec{x}$.
  • The function outputs a DVector type of the same size as $\vec{x}$

** Linear Independence** The basis functions must be linearly independent. That means adding $\vec{f_1}(\vec{x})=\vec{x}$ and $\vec{f_1}(\vec{x})=2\,\vec{x}$ is a bad idea. It is a bad idea because it might destabilize the calculations. It is also a bad idea because it adds no value, since the model functions have associated linear expansion coefficients anyways.

For some models, e.g. sums of exponential decays it might happen that the basis functions become linearly dependent for some combinations of nonlinear model parameters. This isn’t great but it is okay, since the VarPro algorithm in this crate exhibits a level of robustness against basis functions becoming collinear (see LevMarProblemBuilder::epsilon).

Invariant Basis Functions

Basis functions that do not depend on model parameters are treated specially. The library refers to them as invariant functions and they are added to a builder by calling SeparableModelBuilder::invariant_function. Since the basis function depends only on $\vec{x}$ it can be written as $\vec{f}_j(\vec{x})$. In Rust this translates to a signature Fn(&DVector<ScalarType>) -> DVector<ScalarType> + 'static for the callable.

Example: Calling SeparableModelBuilder::invariant_function adds the function to the model. These calls can be chained to add more functions.

use nalgebra::DVector;
use varpro::prelude::SeparableModelBuilder;
fn squared(x: &DVector<f64>) -> DVector<f64> {
    x.map(|x|x.powi(2))
}

let builder = SeparableModelBuilder::<f64>::new(&["alpha","beta"])
                // we can add an invariant function using a function pointer
                .invariant_function(squared)
                // or we can add it using a lambda
                .invariant_function(|x|x.map(|x|(x+1.).sin()));

Caveat: we cannot successfully build a model containing only invariant functions. It would make no sense to use the varpro library to fit such a model because that is purely a linear least squares problem. See the next section for adding parameter dependent functions.

Basis Functions

Most of the time we’ll be adding basis functions to the model that depend on some of the model parameters. We can add a basis function to a builder by calling builder.function. Each call must be immediately followed by calls to partial_deriv for each of the parameters that the basis function depends on.

Example: The procedure is best illustrated with an example

// exponential decay f(t,tau) = exp(-t/tau)
use nalgebra::{Scalar, DVector};
use num_traits::Float;
use varpro::prelude::SeparableModelBuilder;
pub fn exp_decay<ScalarType: Float + Scalar>(
    tvec: &DVector<ScalarType>,
    tau: ScalarType,
) -> DVector<ScalarType> {
    tvec.map(|t| (-t / tau).exp())
}

// derivative of exp decay with respect to tau
pub fn exp_decay_dtau<ScalarType: Scalar + Float>(
    tvec: &DVector<ScalarType>,
    tau: ScalarType,
) -> DVector<ScalarType> {
    tvec.map(|t| (-t / tau).exp() * t / (tau * tau))
}

// function sin (omega*t+phi)
pub fn sin_ometa_t_plus_phi<ScalarType: Scalar + Float>(
    tvec: &DVector<ScalarType>,
    omega: ScalarType,
    phi: ScalarType,
) -> DVector<ScalarType> {
    tvec.map(|t| (omega * t + phi).sin())
}

// derivative d/d(omega) sin (omega*t+phi)
pub fn sin_ometa_t_plus_phi_domega<ScalarType: Scalar + Float>(
    tvec: &DVector<ScalarType>,
    omega: ScalarType,
    phi: ScalarType,
) -> DVector<ScalarType> {
    tvec.map(|t| t * (omega * t + phi).cos())
}

// derivative d/d(phi) sin (omega*t+phi)
pub fn sin_ometa_t_plus_phi_dphi<ScalarType: Scalar + Float>(
    tvec: &DVector<ScalarType>,
    omega: ScalarType,
    phi: ScalarType,
) -> DVector<ScalarType> {
    tvec.map(|t| (omega * t + phi).cos())
}

let model = SeparableModelBuilder::<f64>::new(&["tau","omega","phi"])
              // add the exp decay and all derivatives
              .function(&["tau"],exp_decay)
              .partial_deriv("tau",exp_decay_dtau)
              // a new call to function finalizes adding the previous function
              .function(&["omega","phi"],sin_ometa_t_plus_phi)
              .partial_deriv("phi", sin_ometa_t_plus_phi_dphi)
              .partial_deriv("omega",sin_ometa_t_plus_phi_domega)
              // we can also add invariant functions. Same as above, the
              // call tells the model builder that the previous function has all
              // the partial derivatives finished
              .invariant_function(|x|x.clone())
              // we build the model calling build. This returns either a valid model
              // or an error variant which is pretty helpful in understanding what went wrong
              .build().unwrap();

There is some special macro magic that allows us to pass a function $f(\vec{x},a_1,..,a_n)$ as any item that implements the Rust trait Fn(&DVector<ScalarType>, ScalarType,... ,ScalarType)->DVector<ScalarType> + 'static. This allows us to write the functions in an intuitive fashion in Rust code. All nonlinear parameters $\alpha$ are simply scalar arguments in the parameter list of the function. This works for functions taking up to 10 nonlinear arguments, but can be extended easily by modifying this crates source.

Rules for Model Functions

There are several rules for adding model functions. One of them is enforced by the compiler, some of them are enforced at runtime (when trying to build the model) and others simply cannot be enforced by the library.

**Purely Semantic Rules:

  • Basis functions must be nonlinear in the parameters they take. If they aren’t, you can always rewrite the problem so that the linear parameters go in the coefficient vector $\vec{c}$. This means that each partial derivative also depend on all the parameters that the basis function depends on.
  • Derivatives must take the same parameter arguments and in the same order as the original basis function. This means if basis function $\vec{f}_j$ is given as $\vec{f}_j(\vec{x},a,b)$, then the derivatives must also be given with the parameters $a,b$ in the same order, i.e. $\partial/\partial a \vec{f}_j(\vec{x},a,b)$, $\partial/\partial b \vec{f}_j(\vec{x},a,b)$.

Rules Enforced at Compile Time

  • Partial derivatives cannot be added to invariant functions. This is the reason for the weird proxy in the module.

Rules Enforced at Runtime

  • A partial derivative must be given for each parameter that the basis function depends on.
  • Basis functions may only depend on the parameters that the model depends on.

The builder allows us to provide basis functions for a separable model as a step by step process.

Building a Model

The model is finalized and built using the SeparableModelBuilder::build method. This method returns a valid model or an error variant doing a pretty good job of explaning why the model is invalid.

Implementations

impl<ScalarType> SeparableModelBuilder<ScalarType> where
    ScalarType: Scalar
[src]

pub fn new<StrCollection>(parameter_names: StrCollection) -> Self where
    StrCollection: IntoIterator,
    StrCollection::Item: AsRef<str>, 
[src]

Create a new builder for a model that depends on this list of paramters The model parameters indices correspond to the order of appearance in here. Model parameter indices start at 0.

Arguments

  • parameter_names A collection containing all the nonlinear model parameters

Requirements on the Parameters

  • The list of parameters must only contain unique names
  • The list of parameter names must not be empty
  • Parameter names must not contain a comma. This is a precaution because &["alpha,beta"] most likely indicates a typo for &["alpha","beta"]. Any other form of punctuation is allowed.

pub fn invariant_function<F>(self, function: F) -> Self where
    F: Fn(&DVector<ScalarType>) -> DVector<ScalarType> + 'static, 
[src]

Add a function $\vec{f}(\vec{x})$ to the model. In the varpro library this is called an invariant function because the model function is independent of the model parameters

Usage

For usage see the documentation of the SeparableModelBuilder struct documentation.

pub fn function<F, StrCollection, ArgList>(
    self,
    function_params: StrCollection,
    function: F
) -> SeparableModelBuilderProxyWithDerivatives<ScalarType> where
    F: BasisFunction<ScalarType, ArgList> + 'static,
    StrCollection: IntoIterator,
    StrCollection::Item: AsRef<str>, 
[src]

Add a function $\vec{f}(\vec{x},\alpha_1,...,\alpha_n)$ to the model that depends on the location parameter \vec{x} and nonlinear model parameters.

Usage

For usage see the documentation of the SeparableModelBuilder struct documentation.

pub fn build(self) -> Result<SeparableModel<ScalarType>, ModelBuildError>[src]

Build a separable model from the contents of this builder.

Result

A valid separable model or an error indicating why a valid model could not be constructed.

Valid Models

A model is valid if the following conditions where upheld during construction

  • The list of parameters is valid (see SeparableModelBuilder::new)
  • Each basis function depends on valid parameters
  • Each basis function only depends on (a subset of) the parameters given on model construction
  • For each parameter in the model, there is at least one function that depends on it

Order of the Basis Functions in the Model

Note The order of basis functions in the model is order in which the basis functions where provided during the builder stage. That means the first basis functions gets index 0 in the model, the second gets index 1 and so on.

Trait Implementations

impl<ScalarType> From<ModelBuildError> for SeparableModelBuilder<ScalarType> where
    ScalarType: Scalar
[src]

create a SeparableModelBuilder which contains an error variant

impl<ScalarType> From<Result<SeparableModel<ScalarType>, ModelBuildError>> for SeparableModelBuilder<ScalarType> where
    ScalarType: Scalar
[src]

create a SeparableModelBuilder with the given result variant

Auto Trait Implementations

impl<ScalarType> !RefUnwindSafe for SeparableModelBuilder<ScalarType>

impl<ScalarType> !Send for SeparableModelBuilder<ScalarType>

impl<ScalarType> !Sync for SeparableModelBuilder<ScalarType>

impl<ScalarType> Unpin for SeparableModelBuilder<ScalarType>

impl<ScalarType> !UnwindSafe for SeparableModelBuilder<ScalarType>

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T> Same<T> for T

type Output = T

Should always be Self

impl<SS, SP> SupersetOf<SS> for SP where
    SS: SubsetOf<SP>, 

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.