response_time_analysis/arrival/poisson.rs
1use super::{ArrivalBound, Propagated};
2use crate::time::{Duration, Time};
3
4/// Model of a [Poisson](https://en.wikipedia.org/wiki/Poisson_distribution) arrival process.
5#[derive(Copy, Clone, Debug)]
6pub struct Poisson {
7 /// Mean arrival rate (lambda).
8 pub rate: f64,
9}
10
11impl Poisson {
12 pub fn arrival_probability(&self, delta: Duration, njobs: usize) -> f64 {
13 // quick and dirty naive factorial: k!
14 let mut denominator = 1.0;
15 for x in 1..(njobs + 1) {
16 denominator *= x as f64;
17 }
18 let mean = Time::from(delta) as f64 * self.rate;
19 let mut numerator = (-mean).exp(); // e^(- rate * delta)
20 numerator *= mean.powi(njobs as i32); // (rate * delta)**k
21 numerator / denominator
22 }
23
24 pub fn approximate(&self, epsilon: f64) -> ApproximatedPoisson {
25 ApproximatedPoisson {
26 poisson: *self,
27 epsilon,
28 }
29 }
30}
31
32/// A finite approximation of a Poisson process with bounded
33/// probability of under-approximation.
34///
35/// A Poisson arrival process as modeled by [Poisson] cannot comply
36/// with the the [ArrivalBound] interface as there is a diminishing,
37/// but non-zero probability for an arbitrary large number of
38/// arrivals to occur in any interval. The [ApproximatedPoisson]
39/// process implements the [ArrivalBound] interface with a
40/// configurable residual probability of excessive arrivals.
41///
42/// The bound [number_arrivals][ApproximatedPoisson::number_arrivals]
43/// ensures that the claimed number of job arrivals are not exceeded
44/// with probability at least `1 - epsilon`.
45///
46/// Note: the implementation is intended to be simple, but not
47/// necessarily fast, so it's best to convert it into an [arrival
48/// curve][super::Curve] before use if performance is of interest.
49#[derive(Copy, Clone, Debug)]
50pub struct ApproximatedPoisson {
51 /// The underlying Poisson process.
52 poisson: Poisson,
53 /// The acceptable probability of under-approximating the number
54 /// of arrivals. Must be small but non-zero.
55 epsilon: f64,
56}
57
58impl ApproximatedPoisson {
59 /// Construct a new approximated Poisson arrival process with
60 /// mean arrival rate `lambda` and under-approximation bound
61 /// `epsilon`.
62 pub fn new(lambda: f64, epsilon: f64) -> Self {
63 ApproximatedPoisson {
64 epsilon,
65 poisson: Poisson { rate: lambda },
66 }
67 }
68}
69
70impl ArrivalBound for ApproximatedPoisson {
71 /// Bound the number of jobs released in any interval of length `delta` with probability `1 - epsilon`.
72 fn number_arrivals(&self, delta: Duration) -> usize {
73 if delta.is_non_zero() {
74 let mut cumulative_prob = 0.0;
75 let mut njobs = 0;
76 loop {
77 cumulative_prob += self.poisson.arrival_probability(delta, njobs);
78 if cumulative_prob + self.epsilon >= 1.0 {
79 break;
80 }
81 njobs += 1;
82 }
83 njobs
84 } else {
85 0
86 }
87 }
88
89 fn clone_with_jitter(&self, jitter: Duration) -> Box<dyn ArrivalBound> {
90 Box::new(Propagated::with_jitter(self, jitter))
91 }
92}