rf_core/vm/
round_vm.rs

1use crate::context::Context;
2use crate::export::{Export, Result};
3use crate::path::Path;
4use crate::sensor_id::SensorId;
5use crate::slot::Slot;
6use crate::vm::vm_status::VMStatus;
7use std::str::FromStr;
8
9/// A Round correspond to a local computation in a device. Create the context, evaluate the aggregate program and share the exports to the neighborhood.
10///
11/// * `context` - The context of the current round.
12///
13/// * `status` - The status of the current round.
14///
15/// * `export_stack` - The stack of exports of the current round.
16#[derive(Debug, Clone)]
17pub struct RoundVM {
18    context: Context,
19    status: VMStatus,
20    export_stack: Vec<Export>,
21    isolated: bool,
22}
23
24impl RoundVM {
25    /// Create a new RoundVM
26    ///
27    /// ### Arguments
28    ///
29    /// * `context` - The context of the current round.
30    ///
31    /// # Returns
32    ///
33    /// A `RoundVM` instance.
34    pub fn new(context: Context) -> Self {
35        Self {
36            context,
37            status: VMStatus::new(),
38            export_stack: vec![],
39            isolated: false,
40        }
41    }
42
43    /// Get the first export of the stack.
44    ///
45    /// # Returns
46    ///
47    /// The first export of the stack, of type `&mut Export`.
48    pub fn export_data(&mut self) -> &mut Export {
49        self.export_stack.first_mut().unwrap()
50    }
51
52    /// # Returns
53    ///
54    /// The id of the device, of type `i32`.
55    pub fn self_id(&self) -> &i32 {
56        self.context.self_id()
57    }
58
59    /// Register the given value for the root path.
60    ///
61    /// # Arguments
62    ///
63    /// * `v` - The value to register.
64    ///
65    /// # Generic Parameters
66    ///
67    /// * `A` - The type of value. It must implement the `Copy` trait
68    ///         and have a `'static` lifetime.
69    pub fn register_root<A: 'static + Clone>(&mut self, v: A) {
70        self.export_data().put(Path::new(), v.clone());
71    }
72
73    /// If the computation is folding on a neighbor, return the id of the neighbor
74    ///
75    /// # Returns
76    ///
77    /// An `&Option<i32>` containing the id of the neighbor, if present
78    pub fn neighbor(&self) -> &Option<i32> {
79        self.status.neighbour()
80    }
81
82    /// # Returns
83    ///
84    ///  The index of the current computation.
85    pub fn index(&self) -> i32 {
86        self.status.index()
87    }
88
89    /// Obtain the value of the previous round for the current device and the current path.
90    ///
91    /// # Generic Parameters
92    ///
93    /// * `A` - The type of value. It must implement the `Clone` trait
94    ///         and have a `'static` lifetime.
95    ///
96    /// # Returns
97    ///
98    /// An `Option` containing the value of the current path for the current device, if present.
99    pub fn previous_round_val<A: 'static + Clone + FromStr>(&self) -> Result<A> {
100        self.context
101            .read_export_value::<A>(self.self_id(), self.status.path())
102    }
103
104    /// Obtain the value of the current path for the current neighbor
105    ///
106    /// # Generic Parameters
107    ///
108    /// * `A` - The type of value. It must implement the `Clone` trait
109    ///         and have a `'static` lifetime.
110    ///
111    /// # Returns
112    ///
113    ///  A `Result` containing the value of the current path for the current neighbor, if present.
114    pub fn neighbor_val<A: 'static + Clone + FromStr>(&self) -> Result<A> {
115        let n: Result<i32> = self.neighbor().ok_or("Isolated".into());
116        self.context.read_export_value::<A>(&n?, self.status.path())
117    }
118
119    /// Obtain the local value of a given sensor.
120    ///
121    /// # Arguments
122    ///
123    /// * - `sensor_id` - The id of the sensor.
124    ///
125    /// # Generic Parameters
126    ///
127    /// * `A` - The type of value returned by the sensor. It must have a `'static` lifetime.
128    ///
129    /// # Returns
130    ///
131    /// An `Option` containing the local value of the given sensor, if present.
132    pub fn local_sense<A: 'static>(&self, sensor_id: &SensorId) -> Option<&A> {
133        self.context.local_sense::<A>(sensor_id)
134    }
135
136    /// Obtain the value of a given sensor for the current neighbor.
137    ///
138    /// # Arguments
139    ///
140    /// * `sensor_id` - The id of the sensor.
141    ///
142    /// # Generic Parameters
143    ///
144    /// * `A` - The type of value returned by the sensor. It must have a `'static` lifetime.
145    ///
146    /// # Returns
147    ///
148    /// An `Option` containing the value of the given sensor for the current neighbor, if present.
149    pub fn nbr_sense<A: 'static>(&self, sensor_id: &SensorId) -> Option<&A> {
150        self.neighbor()
151            .map(|id| self.context.nbr_sense::<A>(sensor_id, &id))
152            .flatten()
153    }
154
155    /// Evaluates the given expression locally and return the result.
156    ///
157    /// # Arguments
158    ///
159    /// * `expr` The expression to evaluate, which takes a [RoundVM] as argument and returns a tuple of `RoundVM` and `A`.
160    ///
161    /// # Generic Parameters
162    ///
163    /// * `A` - The type of value returned by the expression.
164    /// * `F` - The type of the closure, which must be a mutable closure that takes a [RoundVM] as argument and returns a tuple of `RoundVM` and `A`.
165    ///
166    /// # Returns
167    ///
168    /// The result of the closure `expr`.
169    pub fn locally<A: Clone + 'static + FromStr, F>(&mut self, expr: F) -> A
170    where
171        F: Fn(&mut RoundVM) -> A,
172    {
173        let current_neighbour = *self.neighbor();
174        self.status.fold_out();
175        let result = expr(self);
176        self.status.fold_into(current_neighbour);
177        result
178    }
179
180    /// Perform a folded evaluation of the given expression in the given neighbor and return the result.
181    ///
182    /// # Arguments
183    ///
184    /// * `expr` - The expression to evaluate, which takes a [RoundVM] as argument and returns a tuple of `RoundVM` and `A`.
185    /// * `id` - The id of the neighbor. It is of type `i32`.
186    ///
187    /// # Generic Parameters
188    ///
189    /// * `A` - The type of value returned by the expression.
190    /// * `F` - The type of the expression, which must be a closure that takes a [RoundVM] as argument and returns a tuple of `RoundVM` and `A`.
191    ///
192    /// # Returns
193    ///
194    /// An `Option` containing the result of the expression.
195    pub fn folded_eval<A: Clone + 'static, F>(&mut self, expr: F, id: i32) -> Option<A>
196    where
197        F: Fn(&mut RoundVM) -> A,
198    {
199        self.status.push();
200        self.status.fold_into(Some(id));
201        let result = expr(self);
202        self.status.pop();
203        Some(result)
204    }
205
206    /// Evaluate the given expression while also writing on the [Export] stack.
207    ///
208    /// # Arguments
209    ///
210    /// * `slot` - The slot to write in the current [Path].
211    /// * `write` - A boolean indicating whether to write the result of the expression on the `Export` stack.
212    /// * `inc` - A boolean indicating whether to increment the index of the current [VMStatus].
213    /// * `expr` - The expression to evaluate, which takes a [RoundVM] as argument and returns a tuple of `RoundVM` and `A`.
214    ///
215    /// # Generic Parameters
216    ///
217    /// * `A` - The type of value returned by the expression.
218    /// * `F` - The type of the expression, which must be a closure that takes a [RoundVM] as argument and returns a tuple of `RoundVM` and `A`.
219    ///
220    /// # Returns
221    ///
222    /// A tuple of `RoundVM` and `A`.
223    pub fn nest<A: Clone + 'static + FromStr, F>(
224        &mut self,
225        slot: Slot,
226        write: bool,
227        inc: bool,
228        expr: F,
229    ) -> A
230    where
231        F: Fn(&mut RoundVM) -> A,
232    {
233        self.status.push();
234        self.status.nest(slot);
235        let val = expr(self);
236        let res = if write {
237            let cloned_path = self.status.path().clone();
238            self.export_data()
239                .get::<A>(&cloned_path)
240                .unwrap_or(
241                    self.export_data()
242                        .put_lazy_and_return(cloned_path, || val.clone()),
243                )
244                .clone()
245        } else {
246            val
247        };
248        if inc {
249            self.status.pop();
250            self.status.inc_index();
251        } else {
252            self.status.pop();
253        }
254        res
255    }
256
257    /// Get a vector of aligned neighbor identifiers.
258    ///
259    /// # Returns
260    ///
261    /// A vector of aligned neighbor identifiers.
262    pub fn aligned_neighbours<A: 'static + FromStr + Clone>(&self) -> Vec<i32> {
263        let mut tmp: Vec<i32> = Vec::new();
264        if !self.isolated {
265            tmp = self
266                .context
267                .exports()
268                .clone()
269                .into_iter()
270                .filter(|(id, _)| id != self.self_id())
271                .filter(|(_, export)| {
272                    self.status.path().is_root() || export.get::<A>(self.status.path()).is_ok()
273                })
274                .map(|(id, _)| id)
275                .collect();
276            tmp.insert(0, *self.self_id());
277        }
278        tmp
279    }
280
281    /// Isolate the current device and evaluate the given expression
282    ///
283    /// # Arguments
284    ///
285    /// * `expr` - The closure to execute, which takes a [RoundVM] as argument and returns a tuple of `RoundVM` and `A`.
286    ///
287    /// # Generic Parameters
288    ///
289    /// * `A` - The type of value returned by the closure.
290    /// * `F` - The type of the closure, which must be a mutable closure takes a [RoundVM] as argument and returns a tuple of `RoundVM` and `A`.
291    ///
292    /// # Returns
293    ///
294    /// The result of the closure `expr`.
295    pub fn isolate<A, F>(&mut self, expr: F) -> A
296    where
297        F: Fn(&mut RoundVM) -> A,
298    {
299        let was_isolated = self.isolated;
300        self.isolated = true;
301        let result = expr(self);
302        self.isolated = was_isolated;
303        result
304    }
305
306    /// Check if folding is not being performed on neighbor.
307    ///
308    /// # Returns
309    ///
310    /// - `true` if folding is being performed on self.
311    /// - `false` if folding is being performed on neighbor.
312    pub fn unless_folding_on_others(&self) -> bool {
313        match self.neighbor() {
314            Some(neighbor) => neighbor == self.self_id(),
315            None => true,
316        }
317    }
318
319    /// Check if folding is being performed on self.
320    ///
321    /// # Returns
322    ///
323    /// - `true` if folding is being performed on self.
324    /// - `false` otherwise.
325    pub fn only_when_folding_on_self(&self) -> bool {
326        match self.neighbor() {
327            Some(neighbor) => neighbor == self.self_id(),
328            _ => false,
329        }
330    }
331
332    pub fn context(&self) -> &Context {
333        &self.context
334    }
335
336    /// Create a new export stack with an empty [Export]. This function needs to be called when a new
337    /// [RoundVM] is created.
338    pub fn new_export_stack(&mut self) {
339        self.export_stack.push(Export::new())
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use crate::context::Context;
346    use crate::export;
347    use crate::export::Export;
348    use crate::path;
349    use crate::path::Path;
350    use crate::sensor_id::{sensor, SensorId};
351    use crate::slot::Slot::{Nbr, Rep};
352    use crate::vm::round_vm::RoundVM;
353    use crate::vm::vm_status::VMStatus;
354    use std::any::Any;
355    use std::collections::HashMap;
356    use std::rc::Rc;
357    fn round_vm_builder() -> RoundVM {
358        let local_sensor =
359            HashMap::from([(sensor("sensor1"), Rc::new(Box::new(10) as Box<dyn Any>))]);
360        let nbr_sensor = HashMap::from([(
361            sensor("sensor1"),
362            HashMap::from([(0, Rc::new(Box::new(4) as Box<dyn Any>))]),
363        )]);
364        let exports = HashMap::from([
365            (
366                7,
367                export!((path!(Nbr(0), Rep(0)), 10)),
368            ),
369            (
370                0,
371                export!((path!(Nbr(0), Rep(0)), 2)),
372            ),
373        ]);
374
375        let context = Context::new(7, local_sensor, nbr_sensor, exports);
376        let mut vm = RoundVM::new(context);
377        vm.export_stack.push(export!((Path::new(), 0)));
378        let status = VMStatus::new();
379        vm.status.fold_into(Some(0));
380        vm
381    }
382
383    fn expr(vm: &mut RoundVM) -> i32 {
384        5 * 3
385    }
386
387    #[test]
388    fn test_export_data() {
389        let mut vm = round_vm_builder();
390        assert_eq!(vm.export_data().root::<i32>(), 0)
391    }
392
393    #[test]
394    fn test_register_root() {
395        let mut vm = round_vm_builder();
396        vm.register_root(5 * 3);
397        assert_eq!(vm.export_data().root::<i32>(), 15)
398    }
399
400    #[test]
401    fn test_folded_eval() {
402        let mut vm = round_vm_builder();
403        let result = vm.folded_eval(expr, 7);
404        assert_eq!(round_vm_builder().status, vm.status);
405        assert_eq!(result.unwrap(), 15)
406    }
407
408    #[test]
409    fn test_previous_round_val() {
410        let mut vm = round_vm_builder();
411        vm.status.nest(Rep(0));
412        vm.status.nest(Nbr(0));
413        assert_eq!(vm.previous_round_val::<i32>().unwrap(), 10)
414    }
415
416    #[test]
417    fn test_neighbor_val() {
418        let mut vm = round_vm_builder();
419        vm.status.nest(Rep(0));
420        vm.status.nest(Nbr(0));
421        assert_eq!(vm.neighbor_val::<i32>().unwrap(), 2)
422    }
423
424    #[test]
425    fn test_local_sense() {
426        let vm = round_vm_builder();
427        assert_eq!(
428            vm.local_sense::<i32>(&SensorId::new("sensor1".to_string()))
429                .unwrap(),
430            &10
431        )
432    }
433
434    #[test]
435    fn test_nbr_sense() {
436        let vm = round_vm_builder();
437        assert_eq!(
438            vm.nbr_sense::<i32>(&SensorId::new("sensor1".to_string()))
439                .unwrap(),
440            &4
441        )
442    }
443
444    #[test]
445    fn test_aligned_neighbours() {
446        let vm = round_vm_builder();
447        assert_eq!(vm.aligned_neighbours::<i32>(), vec![7, 0])
448    }
449
450    #[test]
451    fn test_isolate() {
452        let mut vm = round_vm_builder();
453        let was_isolated = vm.isolated.clone();
454        let result = vm.isolate(|vm| 5 * 3);
455        assert_eq!(vm.isolated, was_isolated);
456        assert_eq!(result, 15)
457    }
458
459    #[test]
460    fn test_unless_folding_on_others() {
461        let mut vm = round_vm_builder();
462        assert!(!vm.unless_folding_on_others());
463        vm.status.fold_into(None);
464        assert!(vm.unless_folding_on_others());
465        vm.status.fold_into(Some(7));
466        assert!(vm.unless_folding_on_others());
467    }
468
469    #[test]
470    fn test_only_when_folding_on_self() {
471        let mut vm = round_vm_builder();
472        assert!(!vm.only_when_folding_on_self());
473        vm.status.fold_into(None);
474        assert!(!vm.only_when_folding_on_self());
475        vm.status.fold_into(Some(7));
476        assert!(vm.only_when_folding_on_self());
477    }
478}