xdevs/devstone/
homod.rs

1use super::{DEVStoneAtomic, DEVStoneSeeder};
2#[cfg(test)]
3use super::{SharedProbe, TestProbe};
4use crate::modeling::Coupled;
5
6pub struct HOmod {
7    pub coupled: Coupled,
8}
9
10impl HOmod {
11    pub fn create(
12        width: usize,
13        depth: usize,
14        int_delay: u64,
15        ext_delay: u64,
16        #[cfg(test)] probe: SharedProbe,
17    ) -> Coupled {
18        let mut coupled = Coupled::new("HOmod");
19        let seeder = DEVStoneSeeder::new("seeder");
20        let homod = Self::new(
21            width,
22            depth,
23            int_delay,
24            ext_delay,
25            #[cfg(test)]
26            probe,
27        );
28        let homod_name = homod.coupled.component.get_name().to_string();
29        coupled.add_component(Box::new(seeder));
30        coupled.add_component(Box::new(homod.coupled));
31        coupled.add_ic("seeder", "output", &homod_name, "input_1");
32        coupled.add_ic("seeder", "output", &homod_name, "input_2");
33        coupled
34    }
35
36    fn new(
37        width: usize,
38        depth: usize,
39        int_delay: u64,
40        ext_delay: u64,
41        #[cfg(test)] probe: SharedProbe,
42    ) -> Self {
43        // First we check the input parameters
44        if width < 1 {
45            panic!("width must be greater than 1")
46        }
47        if depth < 1 {
48            panic!("depth must be greater than 1")
49        }
50        // Next we create the model structure
51        let name = format!("coupled_{depth}");
52        let mut coupled = Coupled::new(&name);
53        coupled.add_in_port::<usize>("input_1");
54        coupled.add_in_port::<usize>("input_2");
55        coupled.add_out_port::<usize>("output");
56        // If this is the inner coupled model, we just add one atomic.
57        if depth == 1 {
58            let atomic = DEVStoneAtomic::new(
59                "inner_atomic",
60                int_delay,
61                ext_delay,
62                #[cfg(test)]
63                probe.clone(),
64            );
65            coupled.add_component(Box::new(atomic));
66            coupled.add_eic("input_1", "inner_atomic", "input");
67            coupled.add_eoc("inner_atomic", "output", "output");
68            // Otherwise, we add a subcoupled and a set of atomics.
69        } else {
70            let subcoupled = Self::new(
71                width,
72                depth - 1,
73                int_delay,
74                ext_delay,
75                #[cfg(test)]
76                probe.clone(),
77            );
78            let subcoupled_name = subcoupled.coupled.component.get_name().to_string();
79            coupled.add_component(Box::new(subcoupled.coupled));
80            coupled.add_eic("input_1", &subcoupled_name, "input_1");
81            coupled.add_eoc(&subcoupled_name, "output", "output");
82            let mut prev_row: Vec<String> = Vec::new();
83            let mut current_row: Vec<String> = Vec::new();
84            // First row
85            for i in 1..width {
86                let atomic_name = format!("atomic(1,{i}");
87                prev_row.push(atomic_name.clone());
88                let atomic = DEVStoneAtomic::new(
89                    &atomic_name,
90                    int_delay,
91                    ext_delay,
92                    #[cfg(test)]
93                    probe.clone(),
94                );
95                coupled.add_component(Box::new(atomic));
96                coupled.add_eic("input_2", &atomic_name, "input");
97                coupled.add_ic(&atomic_name, "output", &subcoupled_name, "input_2");
98            }
99            // Second row
100            for i in 1..width {
101                let atomic_name = format!("atomic(2,{i}");
102                current_row.push(atomic_name.clone());
103                let atomic = DEVStoneAtomic::new(
104                    &atomic_name,
105                    int_delay,
106                    ext_delay,
107                    #[cfg(test)]
108                    probe.clone(),
109                );
110                coupled.add_component(Box::new(atomic));
111                if i == 1 {
112                    coupled.add_eic("input_2", &atomic_name, "input");
113                }
114                for prev_name in &prev_row {
115                    coupled.add_ic(&atomic_name, "output", prev_name, "input");
116                }
117            }
118            // Rest of the tree
119            for layer in 3..(width + 1) {
120                prev_row = current_row;
121                current_row = Vec::new();
122                for i in 1..prev_row.len() {
123                    let atomic_name = format!("atomic({layer},{i}");
124                    current_row.push(atomic_name.clone());
125                    let atomic = DEVStoneAtomic::new(
126                        &atomic_name,
127                        int_delay,
128                        ext_delay,
129                        #[cfg(test)]
130                        probe.clone(),
131                    );
132                    coupled.add_component(Box::new(atomic));
133                    if i == 1 {
134                        coupled.add_eic("input_2", &atomic_name, "input");
135                    }
136                    coupled.add_ic(&atomic_name, "output", prev_row.get(i).unwrap(), "input");
137                }
138            }
139        }
140        // Before exiting, we update the probe if required
141        #[cfg(test)]
142        {
143            let mut x = probe.lock().unwrap();
144            x.n_eics += coupled.n_eics();
145            x.n_ics += coupled.n_ics();
146            x.n_eocs += coupled.n_eocs();
147        }
148        Self { coupled }
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155    use crate::simulation::*;
156    use std::sync::{Arc, Mutex};
157
158    fn expected_atomics(width: usize, depth: usize) -> usize {
159        (width - 1 + (width - 1) * width / 2) * (depth - 1) + 1
160    }
161
162    fn expected_eics(width: usize, depth: usize) -> usize {
163        (2 * (width - 1) + 1) * (depth - 1) + 1
164    }
165
166    fn expected_ics(width: usize, depth: usize) -> usize {
167        ((width - 1) * (width - 1) + (width - 1) * width / 2) * (depth - 1)
168    }
169
170    fn expected_eocs(_width: usize, depth: usize) -> usize {
171        depth
172    }
173
174    fn expected_internals(width: usize, depth: usize) -> usize {
175        let mut n = 1;
176        for d in 1..depth {
177            n += (1 + (d - 1) * (width - 1)) * (width - 1) * width / 2
178                + (width - 1) * (width + (d - 1) * (width - 1));
179        }
180        n
181    }
182
183    fn expected_events(width: usize, depth: usize) -> usize {
184        let mut n = 1;
185        if width > 1 && depth > 1 {
186            n += 2 * (width - 1);
187            let mut aux = 0;
188            for i in 2..depth {
189                aux += 1 + (i - 1) * (width - 1);
190            }
191            n += aux * 2 * (width - 1) * (width - 1);
192            n += (aux + 1) * ((width - 1) * (width - 1) + (width - 2) * (width - 1) / 2);
193        }
194        n
195    }
196
197    #[test]
198    fn test_homod() {
199        for width in (1..10).step_by(1) {
200            for depth in (1..10).step_by(1) {
201                let probe = Arc::new(Mutex::new(TestProbe::default()));
202                let coupled = HOmod::create(width, depth, 0, 0, probe.clone());
203                let mut simulator = RootCoordinator::new(coupled);
204                simulator.simulate(f64::INFINITY);
205
206                let x = probe.lock().unwrap();
207                assert_eq!(expected_atomics(width, depth), x.n_atomics);
208                assert_eq!(expected_eics(width, depth), x.n_eics);
209                assert_eq!(expected_ics(width, depth), x.n_ics);
210                assert_eq!(expected_eocs(width, depth), x.n_eocs);
211                assert_eq!(expected_internals(width, depth), x.n_internals);
212                assert_eq!(expected_internals(width, depth), x.n_externals);
213                assert_eq!(expected_events(width, depth), x.n_events);
214            }
215        }
216    }
217}