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}