Skip to content

Commit

Permalink
refactor should be finished
Browse files Browse the repository at this point in the history
  • Loading branch information
geo-ant committed Apr 19, 2024
1 parent d6239e1 commit 89059d5
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 199 deletions.
4 changes: 4 additions & 0 deletions src/model/builder/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,8 @@ pub enum ModelBuildError {
#[error("Missing initial guesses for model parameters")]
/// missing initial guesses for parameters
MissingInitialParameters,

#[error("Illegal call to 'partial_deriv': a call to this function can only follow a call to 'function' or another call to 'partial_deriv'")]
/// an illegal call to the partial_deriv function
IllegalCallToPartialDeriv,
}
250 changes: 51 additions & 199 deletions src/model/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,13 +396,61 @@ where
StrCollection: IntoIterator,
StrCollection::Item: AsRef<str>,
{
SeparableModelBuilderProxyWithDerivatives::new(self.model_result, function_params, function)
match self {
SeparableModelBuilder::Error(err) => Self::from(err),
SeparableModelBuilder::Normal(model) => {
let function_builder = ModelBasisFunctionBuilder::new(
model.parameter_names.clone(),
function_params,
function,
);
Self::FunctionBuilding {
model,
function_builder,
}
}
SeparableModelBuilder::FunctionBuilding {
model,
function_builder,
} => Self::from(extend_model(model, function_builder))
.function(function_params, function),
}
}

/// Add a partial derivative to a function, see the example in the documentation
/// to this structure.
/// A call to this function must only occur when it follows a call to
/// `function` or another call to `partial_derivative`. Other cases will
/// lead to errors when building the model.
pub fn partial_deriv<StrType: AsRef<str>, F, ArgList>(
self,
parameter: StrType,
derivative: F,
) -> Self
where
F: BasisFunction<ScalarType, ArgList> + 'static,
{
match self {
SeparableModelBuilder::Error(err) => Self::from(err),
SeparableModelBuilder::Normal(_model) => {
// only when we are in the process of building a function, may
// we call this function
Self::from(Err(ModelBuildError::IllegalCallToPartialDeriv))
}
SeparableModelBuilder::FunctionBuilding {
model,
function_builder,
} => Self::FunctionBuilding {
model,
function_builder: function_builder.partial_deriv(parameter.as_ref(), derivative),
},
}
}

/// Set the independent variable `$x$` which will be used when evaluating the model.
/// Also see the struct documentation of [SeparableModelBuilder](crate::model::builder::SeparableModelBuilder)
/// for information on how to use this method.
pub fn independent_variable(mut self, x: DVector<ScalarType>) -> Self {
pub fn independent_variable(self, x: DVector<ScalarType>) -> Self {
match self {
SeparableModelBuilder::Error(err) => Self::from(err),
SeparableModelBuilder::Normal(mut model) => {
Expand All @@ -419,7 +467,7 @@ where
/// Set the initial values for the model parameters `$\vec{\alpha}$`.
/// Also see the struct documentation of [SeparableModelBuilder](crate::model::builder::SeparableModelBuilder)
/// for information on how to use this method.
pub fn initial_parameters(mut self, initial_parameters: Vec<ScalarType>) -> Self {
pub fn initial_parameters(self, initial_parameters: Vec<ScalarType>) -> Self {
match self {
SeparableModelBuilder::Error(err) => Self::from(err),
SeparableModelBuilder::Normal(mut model) => {
Expand Down Expand Up @@ -513,202 +561,6 @@ impl<ScalarType: Scalar> TryInto<SeparableModel<ScalarType>> for UnfinishedModel
}
}
}
/// helper struct that contains a seperable model as well as a model function builder
/// used inside the SeparableModelBuilderProxyWithDerivatives.
struct ModelAndModelBasisFunctionBuilderPair<ScalarType>
where
ScalarType: Scalar,
{
model: UnfinishedModel<ScalarType>,
builder: ModelBasisFunctionBuilder<ScalarType>,
}

impl<ScalarType> ModelAndModelBasisFunctionBuilderPair<ScalarType>
where
ScalarType: Scalar,
{
fn new(
model: UnfinishedModel<ScalarType>,
builder: ModelBasisFunctionBuilder<ScalarType>,
) -> Self {
Self { model, builder }
}
}

/// This is just a proxy that does need to be used directly. For constructing a model from
/// a builder see the documentation for [SeparableModelBuilder](self::SeparableModelBuilder).
/// **Sidenote** This structure will hopefully be made more elegant using some metaprogramming techniques
/// in the future. Right now this exists to make sure that partial derivatives cannot accidentally
/// be added to invariant functions. The compiler simply will forbid it. In future the library aims
/// to make more such checks at compile time and reduce the need for runtime errors caused by invalid model
/// construction.
#[must_use = "This is meant as a transient expression proxy. Use build() to build a model."]
pub struct SeparableModelBuilderProxyWithDerivatives<ScalarType>
where
ScalarType: Scalar,
{
current_result: Result<ModelAndModelBasisFunctionBuilderPair<ScalarType>, ModelBuildError>,
}

impl<ScalarType> From<ModelBuildError> for SeparableModelBuilderProxyWithDerivatives<ScalarType>
where
ScalarType: Scalar,
{
fn from(err: ModelBuildError) -> Self {
Self {
current_result: Err(err),
}
}
}

impl<ScalarType> SeparableModelBuilderProxyWithDerivatives<ScalarType>
where
ScalarType: Scalar,
{
/// Construct an instance. This is invoked when adding a function that depends on model
/// parameters to the [SeparableModelBuilder](self::SeparableModelBuilder)
/// # Arguments
/// * `model_result`: the current model or an error
/// * `function_parameters`: the given list of nonlinear function parameters
/// * `function`: the function
fn new<F, StrCollection, ArgList>(
model_result: Result<UnfinishedModel<ScalarType>, ModelBuildError>,
function_parameters: StrCollection,
function: F,
) -> Self
where
F: BasisFunction<ScalarType, ArgList> + 'static,
StrCollection: IntoIterator,
StrCollection::Item: AsRef<str>,
{
match model_result {
Ok(model) => {
let model_parameters = model.parameter_names.clone();
Self {
current_result: Ok(ModelAndModelBasisFunctionBuilderPair::new(
model,
ModelBasisFunctionBuilder::new(
model_parameters,
function_parameters,
function,
),
)),
}
}
Err(err) => Self {
current_result: Err(err),
},
}
}

/// Add a partial derivative to a function. This function is documented as part of the
/// [SeparableModelBuilder](self::SeparableModelBuilder) documentation.
/// *Info*: the reason it appears in this proxy only is to make sure that partial derivatives
/// can only be added to functions that depend on model parameters.
pub fn partial_deriv<StrType: AsRef<str>, F, ArgList>(
self,
parameter: StrType,
derivative: F,
) -> Self
where
F: BasisFunction<ScalarType, ArgList> + 'static,
{
match self.current_result {
Ok(result) => Self {
current_result: Ok(ModelAndModelBasisFunctionBuilderPair {
model: result.model,
builder: result.builder.partial_deriv(parameter.as_ref(), derivative),
}),
},
Err(err) => Self::from(err),
}
}

/// Add a function `\vec{f}(\vec{x})` that does not depend on the model parameters. This is documented
/// as part of the [SeparableModelBuilder](self::SeparableModelBuilder) documentation.
pub fn invariant_function<F>(self, function: F) -> SeparableModelBuilder<ScalarType>
where
F: Fn(&DVector<ScalarType>) -> DVector<ScalarType> + 'static,
{
match self.current_result {
Ok(pair) => {
let model_result = extend_model(pair.model, pair.builder);
SeparableModelBuilder::from(model_result).invariant_function(function)
}
Err(err) => SeparableModelBuilder::from(err),
}
}

/// Add a function `\vec{f}(\vec{x},\alpha_j,...,\alpha_k)` that depends on a subset of the
/// model parameters. This functionality is documented as part of the [SeparableModelBuilder](self::SeparableModelBuilder)
/// 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>,
{
match self.current_result {
Ok(pair) => {
let model_result = extend_model(pair.model, pair.builder);
Self::new(model_result, function_params, function)
}
Err(err) => SeparableModelBuilderProxyWithDerivatives::from(err),
}
}

/// Set the independent variable `\vec{x}`
///
/// # Usage
/// For usage see the documentation of the [SeparableModelBuilder](crate::model::builder::SeparableModelBuilder)
/// struct documentation.
pub fn independent_variable(self, x: DVector<ScalarType>) -> SeparableModelBuilder<ScalarType> {
match self.current_result {
Ok(pair) => {
let model_result = extend_model(pair.model, pair.builder);
SeparableModelBuilder::from(model_result).independent_variable(x)
}
Err(err) => SeparableModelBuilder::from(err),
}
}

/// Set the initial value for the nonlinear parameters `\vec{\alpha}`
///
/// # Usage
/// For usage see the documentation of the [SeparableModelBuilder](crate::model::builder::SeparableModelBuilder)
/// struct documentation.
pub fn initial_parameters(
self,
initial_parameters: Vec<ScalarType>,
) -> SeparableModelBuilder<ScalarType> {
match self.current_result {
Ok(pair) => {
let model_result = extend_model(pair.model, pair.builder);
SeparableModelBuilder::from(model_result).initial_parameters(initial_parameters)
}
Err(err) => SeparableModelBuilder::from(err),
}
}

/// Finalized the building process and build a separable model.
/// This functionality is documented as part of the [SeparableModelBuilder](self::SeparableModelBuilder)
/// documentation.
pub fn build(self) -> Result<SeparableModel<ScalarType>, ModelBuildError> {
// this method converts the internal results into a separable model and uses its
// facilities to check for completion and the like
match self.current_result {
Ok(pair) => {
let model_result = extend_model(pair.model, pair.builder);
SeparableModelBuilder::from(model_result).build()
}
Err(err) => SeparableModelBuilder::from(err).build(),
}
}
}

/// try and extend a model with the given function in the builder
/// if building the function in the builder fails, an error is returned,
Expand Down

0 comments on commit 89059d5

Please sign in to comment.