dvcompute_branch/simulation/simulation/
mod.rs

1// Copyright (c) 2020-2022  David Sorokin <davsor@mail.ru>, based in Yoshkar-Ola, Russia
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use std::marker::PhantomData;
8
9use crate::simulation;
10use crate::simulation::Specs;
11use crate::simulation::Run;
12use crate::simulation::Point;
13use crate::simulation::event::Event;
14use crate::simulation::process::*;
15use crate::simulation::observable::disposable::*;
16use crate::simulation::composite::*;
17
18use dvcompute_utils::grc::Grc;
19
20#[cfg(feature="dist_mode")]
21use crate::simulation::comm::context::LogicalProcessContext;
22
23/// Additional operations.
24pub mod ops;
25
26/// Return a new `Simulation` computation by the specified pure value.
27#[inline]
28pub fn return_simulation<T>(val: T) -> Return<T> {
29    Return { val: val }
30}
31
32/// Delay the `Simulation` computation.
33#[inline]
34pub fn delay_simulation<F, M>(f: F) -> Delay<F, M>
35    where F: FnOnce() -> M,
36          M: Simulation
37{
38    Delay { f: f, _phantom: PhantomData }
39}
40
41/// Construct a new `Simulation` computation by the specified function.
42#[inline]
43pub fn cons_simulation<F, T>(f: F) -> Cons<F, T>
44    where F: FnOnce(&Run) -> simulation::Result<T>
45{
46    Cons { f: f, _phantom: PhantomData }
47}
48
49/// Create a sequence of computations.
50#[inline]
51pub fn simulation_sequence<I, M>(comps: I) -> Sequence<I::IntoIter, M>
52    where I: IntoIterator<Item = M>,
53          M: Simulation
54{
55    Sequence { comps: comps.into_iter(), _phantom: PhantomData }
56}
57
58/// Create a sequence of computations, where the result is ignored.
59#[inline]
60pub fn simulation_sequence_<I, M>(comps: I) -> Sequence_<I::IntoIter, M>
61    where I: IntoIterator<Item = M>,
62          M: Simulation
63{
64    Sequence_ { comps: comps.into_iter(), _phantom: PhantomData }
65}
66
67/// The computation of arbitrary function of simulation run.
68pub trait Simulation {
69
70    /// The type of the item that is returned in within the simulation run.
71    type Item;
72
73    /// Call the `Simulation` computation within the current simulation run.
74    #[doc(hidden)]
75    fn call_simulation(self, r: &Run) -> simulation::Result<Self::Item>;
76
77    /// Convert into the `Event` computation.
78    #[inline]
79    fn into_event(self) -> SimulationIntoEvent<Self>
80        where Self: Sized
81    {
82        SimulationIntoEvent { comp: self }
83    }
84
85    /// Convert to the `Process` computation.
86    #[inline]
87    fn into_process(self) -> SimulationIntoProcess<Self>
88        where Self: Sized
89    {
90        SimulationIntoProcess { comp: self }
91    }
92
93    /// Convert into the `Composite` computation.
94    #[inline]
95    fn into_composite(self) -> SimulationIntoComposite<Self>
96        where Self: Sized
97    {
98        SimulationIntoComposite { comp: self }
99    }
100
101    /// Bind the current computation with its continuation within the resulting computation.
102    #[inline]
103    fn and_then<U, F>(self, f: F) -> AndThen<Self, U, F>
104        where Self: Sized,
105              U: Simulation,
106              F: FnOnce(Self::Item) -> U,
107    {
108        AndThen { comp: self, f: f, _phantom: PhantomData }
109    }
110
111    /// Map the current computation using the specified transform.
112    #[inline]
113    fn map<B, F>(self, f: F) -> Map<Self, B, F>
114        where Self: Sized,
115              F: FnOnce(Self::Item) -> B,
116    {
117        Map { comp: self, f: f, _phantom: PhantomData }
118    }
119
120    /// Zip the current computation with another one within the resulting computation.
121    #[inline]
122    fn zip<U>(self, other: U) -> Zip<Self, U>
123        where Self: Sized,
124              U: Simulation
125    {
126        Zip { comp: self, other: other }
127    }
128
129    /// The function application.
130    #[inline]
131    fn ap<U, B>(self, other: U) -> Ap<Self, U, B>
132        where Self: Sized,
133              Self::Item: FnOnce(U::Item) -> B,
134              U: Simulation
135    {
136        Ap { comp: self, other: other, _phantom: PhantomData }
137    }
138
139    /// Finalize the current computation regardless of canceling it or not.
140    #[inline]
141    fn finally<U>(self, finalization: U) -> Finally<Self, U>
142        where Self: Sized,
143              U: Simulation<Item = ()>
144    {
145        Finally { comp: self, finalization: finalization }
146    }
147
148    /// Run the simulation.
149    #[cfg(feature="dist_mode")]
150    #[inline]
151    fn run(self, specs: Specs, ctx: &LogicalProcessContext) -> simulation::Result<Self::Item>
152        where Self: Sized
153    {
154        let run = Run::new(specs, ctx);
155        self.call_simulation(&run)
156    }
157
158    /// Run the simulation.
159    #[cfg(any(feature="branch_mode", feature="branch_wasm_mode"))]
160    #[inline]
161    fn run(self, specs: Specs) -> simulation::Result<Self::Item>
162        where Self: Sized
163    {
164        let run = Run::new(specs);
165        self.call_simulation(&run)
166    }
167
168    /// Run the simulation by index.
169    #[cfg(feature="dist_mode")]
170    #[inline]
171    fn run_by_index(self, specs: Specs, ctx: &LogicalProcessContext, run_index: usize, run_count: usize) -> simulation::Result<Self::Item>
172        where Self: Sized
173    {
174        let run = Run::by_index(specs, ctx, run_index, run_count);
175        self.call_simulation(&run)
176    }
177
178    /// Run the simulation by index.
179    #[cfg(any(feature="branch_mode", feature="branch_wasm_mode"))]
180    #[inline]
181    fn run_by_index(self, specs: Specs, run_index: usize, run_count: usize) -> simulation::Result<Self::Item>
182        where Self: Sized
183    {
184        let run = Run::by_index(specs, run_index, run_count);
185        self.call_simulation(&run)
186    }
187
188    /// Convert into a boxed value.
189    #[inline]
190    fn into_boxed(self) -> SimulationBox<Self::Item>
191        where Self: Sized + Clone + 'static
192    {
193        SimulationBox::new(move |r: &Run| { self.call_simulation(r) })
194    }
195}
196
197/// Allows converting to `Simulation` computations.
198pub trait IntoSimulation {
199
200    /// The target computation.
201    type Simulation: Simulation<Item = Self::Item>;
202
203    /// The type of item that is returned by the computation.
204    type Item;
205
206    /// Convert to the `Simulation` computation.
207    fn into_simulation(self) -> Self::Simulation;
208}
209
210impl<M: Simulation> IntoSimulation for M {
211
212    type Simulation = M;
213
214    type Item = M::Item;
215
216    #[inline]
217    fn into_simulation(self) -> Self::Simulation {
218        self
219    }
220}
221
222/// It represents the boxed `Simulation` computation.
223#[must_use = "computations are lazy and do nothing unless to be run"]
224pub struct SimulationBox<T> {
225    f: Box<dyn SimulationFnBoxClone<T>>
226}
227
228impl<T> SimulationBox<T> {
229
230    /// Create a new boxed computation.
231    #[doc(hidden)]
232    #[inline]
233    fn new<F>(f: F) -> Self
234        where F: FnOnce(&Run) -> simulation::Result<T> + Clone + 'static
235    {
236        SimulationBox {
237            f: Box::new(f)
238        }
239    }
240
241    /// Call the boxed function.
242    #[doc(hidden)]
243    #[inline]
244    pub fn call_box(self, arg: (&Run,)) -> simulation::Result<T> {
245        let SimulationBox { f } = self;
246        f.call_box(arg)
247    }
248}
249
250impl<T> Clone for SimulationBox<T> {
251
252    fn clone(&self) -> Self {
253        SimulationBox {
254            f: self.f.call_clone()
255        }
256    }
257}
258
259impl<T> Simulation for SimulationBox<T> {
260
261    type Item = T;
262
263    #[inline]
264    fn call_simulation(self, r: &Run) -> simulation::Result<Self::Item> {
265        self.call_box((r,))
266    }
267
268    #[inline]
269    fn into_boxed(self) -> SimulationBox<Self::Item>
270        where Self: Sized + Clone + 'static
271    {
272        self
273    }
274}
275
276/// A trait to support the stable version of Rust, where there is no `FnBox`.
277trait SimulationFnBox<T> {
278
279    /// Call the corresponding function.
280    fn call_box(self: Box<Self>, args: (&Run,)) -> simulation::Result<T>;
281}
282
283impl<T, F> SimulationFnBox<T> for F
284    where F: for<'a> FnOnce(&'a Run) -> simulation::Result<T>
285{
286    fn call_box(self: Box<Self>, args: (&Run,)) -> simulation::Result<T> {
287        let this: Self = *self;
288        this(args.0)
289    }
290}
291
292/// A trait to implement a cloneable `FnBox`.
293trait SimulationFnBoxClone<T>: SimulationFnBox<T> {
294
295    /// Clone the function.
296    fn call_clone(&self) -> Box<dyn SimulationFnBoxClone<T>>;
297}
298
299impl<T, F> SimulationFnBoxClone<T> for F
300    where F: for<'a> FnOnce(&'a Run) -> simulation::Result<T> + Clone + 'static
301{
302    fn call_clone(&self) -> Box<dyn SimulationFnBoxClone<T>> {
303        Box::new(self.clone())
304    }
305}
306
307/// Allows creating the `Simulation` computation from a pure value.
308#[must_use = "computations are lazy and do nothing unless to be run"]
309#[derive(Clone)]
310pub struct Return<T> {
311
312    /// Return a pure value, which is then transformed to the computation.
313    val: T
314}
315
316impl<T> Simulation for Return<T> {
317
318    type Item = T;
319
320    #[doc(hidden)]
321    #[inline]
322    fn call_simulation(self, _: &Run) -> simulation::Result<T> {
323        let Return { val } = self;
324        Result::Ok(val)
325    }
326}
327
328/// Allows delaying the `Simulation` computation by the specified function.
329#[must_use = "computations are lazy and do nothing unless to be run"]
330#[derive(Clone)]
331pub struct Delay<F, M> {
332
333    /// Return the computation.
334    f: F,
335
336    /// To keep the type parameter.
337    _phantom: PhantomData<M>
338}
339
340impl<F, M> Simulation for Delay<F, M>
341    where F: FnOnce() -> M,
342          M: Simulation
343{
344    type Item = M::Item;
345
346    #[doc(hidden)]
347    #[inline]
348    fn call_simulation(self, r: &Run) -> simulation::Result<M::Item> {
349        let Delay { f, _phantom } = self;
350        f().call_simulation(r)
351    }
352}
353
354/// Allows constructing the `Simulation` computation by the specified function.
355#[must_use = "computations are lazy and do nothing unless to be run"]
356#[derive(Clone)]
357pub struct Cons<F, T> {
358
359    /// The function of simulation run.
360    f: F,
361
362    /// To keep the type parameter.
363    _phantom: PhantomData<T>
364}
365
366impl<F, T> Simulation for Cons<F, T>
367    where F: FnOnce(&Run) -> simulation::Result<T>
368{
369    type Item = T;
370
371    #[doc(hidden)]
372    #[inline]
373    fn call_simulation(self, r: &Run) -> simulation::Result<T> {
374        let Cons { f, _phantom } = self;
375        f(r)
376    }
377}
378
379/// A conversion into the `Event` computation.
380#[must_use = "computations are lazy and do nothing unless to be run"]
381#[derive(Clone)]
382pub struct SimulationIntoEvent<M> {
383
384    /// The current computation.
385    comp: M
386}
387
388impl<M> Event for SimulationIntoEvent<M>
389    where M: Simulation
390{
391    type Item = M::Item;
392
393    #[doc(hidden)]
394    #[inline]
395    fn call_event(self, p: &Point) -> simulation::Result<M::Item> {
396        let SimulationIntoEvent { comp } = self;
397        comp.call_simulation(p.run)
398    }
399}
400
401/// A conversion into the `Composite` computation.
402#[must_use = "computations are lazy and do nothing unless to be run"]
403#[derive(Clone)]
404pub struct SimulationIntoComposite<M> {
405
406    /// The current computation.
407    comp: M
408}
409
410impl<M> Composite for SimulationIntoComposite<M>
411    where M: Simulation
412{
413    type Item = M::Item;
414
415    #[doc(hidden)]
416    #[inline]
417    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(M::Item, DisposableBox)> {
418        let SimulationIntoComposite { comp } = self;
419        let a = comp.call_simulation(p.run)?;
420        Result::Ok((a, disposable))
421    }
422}
423
424/// The monadic bind for the `Simulation` computation.
425#[must_use = "computations are lazy and do nothing unless to be run"]
426#[derive(Clone)]
427pub struct AndThen<M, U, F> {
428
429    /// The current computation.
430    comp: M,
431
432    /// The continuation of the current computation.
433    f: F,
434
435    /// To keep the type parameter.
436    _phantom: PhantomData<U>
437}
438
439impl<M, U, F> Simulation for AndThen<M, U, F>
440    where M: Simulation,
441          U: Simulation,
442          F: FnOnce(M::Item) -> U
443{
444    type Item = U::Item;
445
446    #[doc(hidden)]
447    #[inline]
448    fn call_simulation(self, r: &Run) -> simulation::Result<U::Item> {
449        let AndThen { comp, f, _phantom } = self;
450        match comp.call_simulation(r) {
451            Result::Ok(a) => {
452                let m = f(a);
453                m.call_simulation(r)
454            },
455            Result::Err(e) => {
456                Result::Err(e)
457            }
458        }
459    }
460}
461
462/// The functor for the `Simulation` computation.
463#[must_use = "computations are lazy and do nothing unless to be run"]
464#[derive(Clone)]
465pub struct Map<M, B, F> {
466
467    /// The current computation.
468    comp: M,
469
470    /// The transform.
471    f: F,
472
473    /// To keep the type parameter.
474    _phantom: PhantomData<B>
475}
476
477impl<M, B, F> Simulation for Map<M, B, F>
478    where M: Simulation,
479          F: FnOnce(M::Item) -> B
480{
481    type Item = B;
482
483    #[doc(hidden)]
484    #[inline]
485    fn call_simulation(self, r: &Run) -> simulation::Result<B> {
486        let Map { comp, f, _phantom } = self;
487        match comp.call_simulation(r) {
488            Result::Ok(a) => Result::Ok(f(a)),
489            Result::Err(e) => Result::Err(e)
490        }
491    }
492}
493
494/// The zip of two `Simulation` computations.
495#[must_use = "computations are lazy and do nothing unless to be run"]
496#[derive(Clone)]
497pub struct Zip<M, U> {
498
499    /// The current computation.
500    comp: M,
501
502    /// Another computation.
503    other: U,
504}
505
506impl<M, U> Simulation for Zip<M, U>
507    where M: Simulation,
508          U: Simulation
509{
510    type Item = (M::Item, U::Item);
511
512    #[doc(hidden)]
513    #[inline]
514    fn call_simulation(self, r: &Run) -> simulation::Result<(M::Item, U::Item)> {
515        let Zip { comp, other } = self;
516        comp.and_then(move |a| {
517            other.map(move |b| (a, b))
518        }).call_simulation(r)
519    }
520}
521
522/// The function application for the `Simulation` computation.
523#[must_use = "computations are lazy and do nothing unless to be run"]
524#[derive(Clone)]
525pub struct Ap<M, U, B> {
526
527    /// The current computation.
528    comp: M,
529
530    /// The continuation of the current computation.
531    other: U,
532
533    /// To keep the type parameter.
534    _phantom: PhantomData<B>
535}
536
537impl<M, U, B> Simulation for Ap<M, U, B>
538    where M: Simulation,
539          U: Simulation,
540          M::Item: FnOnce(U::Item) -> B
541{
542    type Item = B;
543
544    #[doc(hidden)]
545    #[inline]
546    fn call_simulation(self, r: &Run) -> simulation::Result<B> {
547        let Ap { comp, other, _phantom } = self;
548        comp.and_then(move |f| {
549            other.map(move |a| { f(a) })
550        }).call_simulation(r)
551    }
552}
553
554/// The finally block for the `Event` computation.
555#[must_use = "computations are lazy and do nothing unless to be run"]
556#[derive(Clone)]
557pub struct Finally<M, U> {
558
559    /// The current computation.
560    comp: M,
561
562    /// The finalization computation.
563    finalization: U
564}
565
566impl<M, U> Simulation for Finally<M, U>
567    where M: Simulation,
568          U: Simulation<Item = ()>
569{
570    type Item = M::Item;
571
572    #[doc(hidden)]
573    #[inline]
574    fn call_simulation(self, r: &Run) -> simulation::Result<Self::Item> {
575        let Finally { comp, finalization } = self;
576        let x = comp.call_simulation(r);
577        match finalization.call_simulation(r) {
578            Result::Ok(_) => x,
579            Result::Err(e0) => {
580                match x {
581                    Result::Ok(_) => Result::Err(e0),
582                    Result::Err(e) => Result::Err(e.merge(&e0))
583                }
584            }
585        }
586    }
587}
588
589/// Allows converting to the `Process` computation.
590#[must_use = "computations are lazy and do nothing unless to be run"]
591#[derive(Clone)]
592pub struct SimulationIntoProcess<M> {
593
594    /// The current computation.
595    comp: M
596}
597
598impl<M> Process for SimulationIntoProcess<M>
599    where M: Simulation
600{
601    type Item = M::Item;
602
603    #[doc(hidden)]
604    #[inline]
605    fn call_process<C>(self, cont: C, pid: Grc<ProcessId>, p: &Point) -> simulation::Result<()>
606        where C: FnOnce(simulation::Result<Self::Item>, Grc<ProcessId>, &Point) -> simulation::Result<()> + Clone + 'static
607    {
608        if is_process_cancelled(&pid, p) {
609            revoke_process(cont, pid, p)
610        } else {
611            let SimulationIntoProcess { comp } = self;
612            let t = comp.call_simulation(p.run);
613            cont(t, pid, p)
614        }
615    }
616
617    #[doc(hidden)]
618    #[inline]
619    fn call_process_boxed(self, cont: ProcessBoxCont<Self::Item>, pid: Grc<ProcessId>, p: &Point) -> simulation::Result<()> {
620        if is_process_cancelled(&pid, p) {
621            revoke_process_boxed(cont, pid, p)
622        } else {
623            let SimulationIntoProcess { comp } = self;
624            let t = comp.call_simulation(p.run);
625            cont.call_box((t, pid, p))
626        }
627    }
628}
629
630/// The sequence of computations.
631#[must_use = "computations are lazy and do nothing unless to be run"]
632#[derive(Clone)]
633pub struct Sequence<I, M> {
634
635    /// The computations.
636    comps: I,
637
638    /// To keep the type parameter.
639    _phantom: PhantomData<M>
640}
641
642impl<I, M> Simulation for Sequence<I, M>
643    where I: Iterator<Item = M>,
644          M: Simulation
645{
646    type Item = Vec<M::Item>;
647
648    #[doc(hidden)]
649    fn call_simulation(self, r: &Run) -> simulation::Result<Self::Item> {
650        let Sequence { comps, _phantom } = self;
651        let mut v = {
652            match comps.size_hint() {
653                (_, Some(n)) => Vec::with_capacity(n),
654                (_, None) => Vec::new()
655            }
656        };
657        for m in comps {
658            match m.call_simulation(r) {
659                Result::Ok(a) => {
660                    v.push(a)
661                },
662                Result::Err(e) => {
663                    return Result::Err(e)
664                }
665            }
666        }
667        Result::Ok(v)
668    }
669}
670
671/// The sequence of computations with ignored result.
672#[must_use = "computations are lazy and do nothing unless to be run"]
673#[derive(Clone)]
674pub struct Sequence_<I, M> {
675
676    /// The computations.
677    comps: I,
678
679    /// To keep the type parameter.
680    _phantom: PhantomData<M>
681}
682
683impl<I, M> Simulation for Sequence_<I, M>
684    where I: Iterator<Item = M>,
685          M: Simulation
686{
687    type Item = ();
688
689    #[doc(hidden)]
690    fn call_simulation(self, r: &Run) -> simulation::Result<Self::Item> {
691        let Sequence_ { comps, _phantom } = self;
692        for m in comps {
693            match m.call_simulation(r) {
694                Result::Ok(_) => (),
695                Result::Err(e) => return Result::Err(e)
696            }
697        }
698        Result::Ok(())
699    }
700}