• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geo-ant / varpro / 16667578290

01 Aug 2025 05:59AM UTC coverage: 76.836% (-1.6%) from 78.389%
16667578290

Pull #50

github

web-flow
Merge bd3d004fd into f242b6e14
Pull Request #50: Feature/documentation improvements 2

5 of 5 new or added lines in 1 file covered. (100.0%)

34 existing lines in 7 files now uncovered.

743 of 967 relevant lines covered (76.84%)

2656.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.64
/src/model/builder/modelfunction_builder/mod.rs
1
#[cfg(test)]
2
mod test;
3

4
use nalgebra::base::Scalar;
5

6
use crate::basis_function::BasisFunction;
7
use crate::model::builder::error::ModelBuildError;
8
use crate::model::detail::{
9
    check_parameter_names, create_index_mapping, create_wrapped_basis_function,
10
};
11
use crate::model::model_basis_function::ModelBasisFunction;
12

13
/// This is a library internal helper that allows us to construct basis functions with
14
/// derivatives for a model. It makes sure that only valid model functions can be built.
15
/// Such a model function is valid when
16
/// * the model parameters are unique and non-empty
17
/// * the function parameters are unique, non-empty and a subset of the model params
18
/// * a derivative is provided for each parameter that the function depends on
19
///   (all other derivatives are zero, because the function does not depend on other params)
20
///
21
#[doc(hidden)]
22
pub struct ModelBasisFunctionBuilder<ScalarType>
23
where
24
    ScalarType: Scalar,
25
{
26
    /// the model parameters for the model this function belongs to
27
    model_parameters: Vec<String>,
28
    /// the parameters that the function depends on. Must be a subset of the model parameters
29
    function_parameters: Vec<String>,
30
    /// the current result of the building process of the model function
31
    model_function_result: Result<ModelBasisFunction<ScalarType>, ModelBuildError>,
32
}
33

34
impl<ScalarType> ModelBasisFunctionBuilder<ScalarType>
35
where
36
    ScalarType: Scalar,
37
{
38
    /// begin constructing a modelfunction for a specific model. The modelfunction must take
39
    /// a subset of the model parameters. This is the first step in creating a function, because
40
    /// the modelfunction must have partial derivatives specified for each parameter it takes.
41
    ///
42
    /// # Arguments
43
    ///
44
    /// * `model_parameters`: the model parameters of the model to which this function belongs. This is important
45
    ///   so the builder understands how the parameters of the function relate to the parameters of the model.
46
    /// * `function_parameters`: the parameters that the function takes. Those must be in the order
47
    ///   of the parameter vector. The paramters must not be empty, nor may they contain duplicates
48
    /// * `function`: the actual function.
49
    ///
50
    /// # Result
51
    ///
52
    /// A model builder that can be used to add derivatives.
53
    pub fn new<FuncType, StrCollection, ArgList>(
50✔
54
        model_parameters: Vec<String>,
55
        function_parameters: StrCollection,
56
        function: FuncType,
57
    ) -> Self
58
    where
59
        FuncType: BasisFunction<ScalarType, ArgList> + 'static,
60
        StrCollection: IntoIterator,
61
        StrCollection::Item: AsRef<str>,
62
    {
63
        let function_parameters: Vec<String> = function_parameters
150✔
64
            .into_iter()
65
            .map(|s| s.as_ref().to_string())
184✔
66
            .collect();
67
        // check that the function parameter list is valid, otherwise continue with an
68
        // internal error state
69
        if let Err(err) = check_parameter_names(&function_parameters) {
52✔
70
            return Self {
×
71
                model_function_result: Err(err),
×
72
                model_parameters,
×
73
                function_parameters,
×
74
            };
75
        }
76

77
        let model_function_result =
48✔
78
            create_wrapped_basis_function(&model_parameters, &function_parameters, function).map(
240✔
UNCOV
79
                |function| ModelBasisFunction {
×
80
                    function,
46✔
81
                    derivatives: Default::default(),
46✔
82
                },
83
            );
84

85
        Self {
86
            model_function_result,
87
            model_parameters,
88
            function_parameters: function_parameters.to_vec(),
48✔
89
        }
90
    }
91

92
    /// Add a derivative for the function with respect to the given parameter.
93
    /// # Arguments
94
    /// * `parameter`: the parameter with respect to which the derivative is taken.
95
    ///   The parameter must be inside the set of model parameters. Furthermore the
96
    /// * `derivative`: the partial derivative of the function with which the
97
    ///   builder was created.
98
    pub fn partial_deriv<FuncType, ArgList>(mut self, parameter: &str, derivative: FuncType) -> Self
58✔
99
    where
100
        FuncType: BasisFunction<ScalarType, ArgList> + 'static,
101
    {
102
        //this makes sure that the index of the derivative is calculated with respect to the
103
        //model parameter list while also making sure that the given derivative exists in the function
104
        //parameters
105
        if let Some((deriv_index_in_model, _)) = self
115✔
106
            .model_parameters
58✔
107
            .iter()
108
            .enumerate()
109
            .filter(|(_idx, model_param)| self.function_parameters.contains(model_param))
403✔
110
            .find(|(_idx, model_param)| model_param == &parameter)
204✔
111
        {
112
            if let Ok(model_function) = self.model_function_result.as_mut() {
57✔
113
                match create_wrapped_basis_function(
×
114
                    &self.model_parameters,
×
115
                    &self.function_parameters,
×
116
                    derivative,
×
117
                ) {
118
                    Ok(deriv) => {
57✔
119
                        // push derivative and check that the derivative was not already in the set
UNCOV
120
                        if model_function
×
UNCOV
121
                            .derivatives
×
UNCOV
122
                            .insert(deriv_index_in_model, deriv)
×
123
                            .is_some()
124
                        {
125
                            self.model_function_result =
2✔
126
                                Err(ModelBuildError::DuplicateDerivative {
1✔
127
                                    parameter: parameter.into(),
1✔
128
                                });
129
                        }
130
                    }
131
                    Err(error) => {
×
132
                        self.model_function_result = Err(error);
×
133
                    }
134
                }
135
            }
136
            self
×
137
        } else {
138
            Self {
139
                model_function_result: Err(ModelBuildError::InvalidDerivative {
1✔
140
                    parameter: parameter.into(),
141
                    function_parameters: self.function_parameters.clone(),
142
                }),
143
                ..self
144
            }
145
        }
146
    }
147

148
    /// Build a modelfunction with derivatives from the contents of this builder
149
    /// # Result
150
    /// A modelfunction containing the given function and derivatives or an error
151
    /// variant if an error occurred during the building process
152
    pub fn build(self) -> Result<ModelBasisFunction<ScalarType>, ModelBuildError> {
50✔
153
        self.check_completion()?;
102✔
154
        self.model_function_result
48✔
155
    }
156

157
    /// Helper function to check if the function builder carries a complete and valid function
158
    /// If the modelfunction builder is carrying an Error result, this function returns Ok(()).
159
    /// If it carries an ok variant, then this function checks that the modelfunction
160
    /// has all necessary derivatives provided and if so returns Ok(()), otherwise it returns
161
    /// an error indicating that there are missing derivatives.
162
    fn check_completion(&self) -> Result<(), ModelBuildError> {
50✔
163
        match self.model_function_result.as_ref() {
50✔
164
            Ok(modelfunction) => {
44✔
165
                // this should not go wrong, but to be safe
UNCOV
166
                check_parameter_names(self.model_parameters.as_slice())?;
×
167
                check_parameter_names(self.function_parameters.as_slice())?;
44✔
168
                // create the index mapping
169
                let index_mapping = create_index_mapping(
UNCOV
170
                    self.model_parameters.as_slice(),
×
UNCOV
171
                    self.function_parameters.as_slice(),
×
172
                )?;
173
                // now make sure that the derivatives are provided for all indices of that mapping
174
                for (index, parameter) in index_mapping.iter().zip(self.function_parameters.iter())
112✔
175
                {
176
                    if !modelfunction.derivatives.contains_key(index) {
112✔
177
                        return Err(ModelBuildError::MissingDerivative {
2✔
178
                            missing_parameter: parameter.clone(),
2✔
179
                            function_parameters: self.function_parameters.clone(),
2✔
180
                        });
181
                    }
182
                }
183
                // this is a sanity check. if this came this far, there should not be an error here
184
                if index_mapping.len() != modelfunction.derivatives.len() {
42✔
185
                    // this also should never be the case and indicates a programming error in the library
186
                    panic!(
×
187
                        "Incorrect number of derivatives in set. This indicates a logic error in this library."
×
188
                    );
189
                }
190
                // otherwise
191
                Ok(())
42✔
192
            }
193
            Err(_) => Ok(()),
6✔
194
        }
195
    }
196
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc