catlog/stdlib/analyses/ode/
signed_coefficients.rs

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