distributed_control/
mas.rs

1//! This module contains the primary structs, types, and traits
2//! for use in defining and analyzing a Multi-agent System (MAS).
3
4use ndarray::LinalgScalar;
5
6use crate::dynamics::{compact_dynamics, compact_output, Dynamics, MasDynamics};
7
8/// A homogenous MAS, i.e., the dynamics of each agent are identical.
9pub struct HomMas<'a, T: LinalgScalar> {
10    hom_dynamics: &'a dyn Dynamics<T>,
11    n_agents: usize,
12}
13
14impl<'a, T: LinalgScalar> HomMas<'a, T> {
15    /// create a new homogenous MAS with the given dynamics and number of agents.
16    pub fn new(hom_dynamics: &dyn Dynamics<T>, n_agents: usize) -> HomMas<T> {
17        HomMas {
18            hom_dynamics,
19            n_agents,
20        }
21    }
22}
23
24impl<'a, T: LinalgScalar> MasDynamics<T> for HomMas<'a, T> {
25    fn mas_dynamics(&self, i: usize) -> Result<&dyn Dynamics<T>, &str> {
26        if i < self.n_agents {
27            Ok(self.hom_dynamics)
28        } else {
29            Err("The agent index exceeds the number of agents.")
30        }
31    }
32    fn n_agents(&self) -> usize {
33        self.n_agents
34    }
35}
36
37impl<'a, T: LinalgScalar> Dynamics<T> for HomMas<'a, T> {
38    fn dynamics(
39        &self,
40        t: T,
41        x: &ndarray::prelude::Array1<T>,
42        u: &ndarray::prelude::Array1<T>,
43    ) -> ndarray::prelude::Array1<T> {
44        compact_dynamics(self, t, x, u)
45    }
46
47    fn output(
48        &self,
49        t: T,
50        x: &ndarray::prelude::Array1<T>,
51        u: &ndarray::prelude::Array1<T>,
52    ) -> ndarray::prelude::Array1<T> {
53        compact_output(self, t, x, u)
54    }
55
56    fn n_input(&self) -> usize {
57        (0..self.n_agents())
58            .map(|i| self.mas_dynamics(i).unwrap().n_input())
59            .sum()
60    }
61
62    fn n_state(&self) -> usize {
63        (0..self.n_agents())
64            .map(|i| self.mas_dynamics(i).unwrap().n_state())
65            .sum()
66    }
67
68    fn n_output(&self) -> usize {
69        (0..self.n_agents())
70            .map(|i| self.mas_dynamics(i).unwrap().n_output())
71            .sum()
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use ndarray::array;
78
79    use super::*;
80    use crate::dynamics;
81
82    #[test]
83    fn test_hom_mas() {
84        let lin_dyn = dynamics::LtiDynamics::new(array![[0.]], array![[1.]]);
85        let mas = HomMas::new(&lin_dyn, 3);
86        assert_eq!(mas.n_agents(), 3);
87        assert_eq!(
88            mas.mas_dynamics(1)
89                .unwrap()
90                .dynamics(0., &array![1.,], &array![2.]),
91            array![2.]
92        );
93        assert_eq!(
94            mas.dynamics(0., &array![1., 2., 3.], &array![1., 1., 1.]),
95            array![1., 1., 1.]
96        );
97    }
98
99    #[test]
100    #[should_panic(expected = "agent index exceeds")]
101    fn test_hom_mas_out_of_index() {
102        let lin_dyn = dynamics::LtiDynamics::new(array![[0.]], array![[1.]]);
103        let mas = HomMas::new(&lin_dyn, 3);
104        mas.mas_dynamics(3).unwrap();
105    }
106}