catlog/stdlib/analyses/ode/
signed_coefficients.rs

1//! Helper module to build analyses based on signed coefficient matrices.
2
3use indexmap::IndexMap;
4use nalgebra::DMatrix;
5use num_traits::zero;
6
7use super::Parameter;
8use crate::{
9    dbl::model::FpDblModel,
10    zero::{QualifiedName, rig::Monomial},
11};
12
13/// Builder for signed coefficient matrices and analyses based on them.
14///
15/// Used to construct the [linear](Self::linear_ode_analysis) and
16/// [Lotka-Volterra](Self::lotka_volterra_analysis) ODE analyses.
17pub struct SignedCoefficientBuilder<ObType, MorType> {
18    var_ob_type: ObType,
19    positive_mor_types: Vec<MorType>,
20    negative_mor_types: Vec<MorType>,
21}
22
23impl<ObType, MorType> SignedCoefficientBuilder<ObType, MorType> {
24    /// Creates a new builder for the given object type.
25    pub fn new(var_ob_type: ObType) -> Self {
26        Self {
27            var_ob_type,
28            positive_mor_types: Vec::new(),
29            negative_mor_types: Vec::new(),
30        }
31    }
32
33    /// Adds a morphism type defining a positive interaction between objects.
34    pub fn add_positive(mut self, mor_type: MorType) -> Self {
35        self.positive_mor_types.push(mor_type);
36        self
37    }
38
39    /// Adds a morphism type defining a negative interaction between objects.
40    pub fn add_negative(mut self, mor_type: MorType) -> Self {
41        self.negative_mor_types.push(mor_type);
42        self
43    }
44
45    /// Builds the matrix of symbolic coefficients for the given model.
46    ///
47    /// Returns the coefficient matrix along with an ordered map from object
48    /// generators to integer indices.
49    pub fn build_matrix(
50        &self,
51        model: &impl FpDblModel<
52            ObType = ObType,
53            MorType = MorType,
54            Ob = QualifiedName,
55            ObGen = QualifiedName,
56            MorGen = QualifiedName,
57        >,
58    ) -> (DMatrix<Parameter<QualifiedName>>, IndexMap<QualifiedName, usize>) {
59        let ob_index: IndexMap<_, _> = model
60            .ob_generators_with_type(&self.var_ob_type)
61            .enumerate()
62            .map(|(i, x)| (x, i))
63            .collect();
64
65        let n = ob_index.len();
66        let mut mat = DMatrix::from_element(n, n, zero());
67        for mor_type in self.positive_mor_types.iter() {
68            for mor in model.mor_generators_with_type(mor_type) {
69                let i = *ob_index.get(&model.mor_generator_dom(&mor)).unwrap();
70                let j = *ob_index.get(&model.mor_generator_cod(&mor)).unwrap();
71                mat[(j, i)] += (1.0, Monomial::generator(mor));
72            }
73        }
74        for mor_type in self.negative_mor_types.iter() {
75            for mor in model.mor_generators_with_type(mor_type) {
76                let i = *ob_index.get(&model.mor_generator_dom(&mor)).unwrap();
77                let j = *ob_index.get(&model.mor_generator_cod(&mor)).unwrap();
78                mat[(j, i)] += (-1.0, Monomial::generator(mor));
79            }
80        }
81
82        (mat, ob_index)
83    }
84}