wb_cache/test/simulation/scriptwriter/model/
customer.rs1use fieldx::fxstruct;
2
3use crate::test::simulation::scriptwriter::math::bisect;
4
5#[fxstruct(sync, new(off), default(off), builder(post_build), get(copy))]
6pub struct CustomerModel {
7 initial_customers: f64,
9 market_capacity: f64,
11 inflection_point: f64,
13 #[fieldx(lock, set)]
15 growth_rate: f64,
16 #[fieldx(default(0.0001))]
18 tolerance: f64,
19 #[fieldx(private, set, builder(off))]
20 v: f64,
21 #[fieldx(private, set, builder(off))]
22 q: f64,
23}
24
25impl CustomerModel {
26 pub fn new(initial_customers: f64) -> Self {
27 Self::builder().initial_customers(initial_customers).build().unwrap()
28 }
29
30 fn post_build(mut self) -> Self {
31 self.set_v(self.calc_v());
32 self.set_q(self.calc_q());
33 self
34 }
35
36 fn calc_v(&self) -> f64 {
37 let v0 = 0.0;
39 let v1 = 10.0;
40 let expected = self.inflection_point / self.market_capacity;
41
42 #[inline(always)]
43 fn coeff(v: f64) -> f64 {
44 (v / (1.0 + v)).powf(1.0 / v)
45 }
46
47 bisect(v0, v1, expected, self.tolerance, coeff).expect("failed to bisect Richards model asymmetry parameter v")
48 }
49
50 fn calc_q(&self) -> f64 {
51 (self.market_capacity / self.initial_customers).powf(self.v()) - 1.0
52 }
53
54 pub fn adjust_growth_rate(&self, expected_customers: f64, day: i32) {
55 let gr0 = 0.0;
56 let gr1 = 1.0;
57
58 self.set_growth_rate(gr1);
59
60 self.set_growth_rate(
61 bisect(gr0, gr1, expected_customers, self.tolerance, |gr| {
62 self.set_growth_rate(gr);
63 self.expected_customers(day)
64 })
65 .unwrap_or_else(|err| {
66 panic!("failed to bisect growth rate for expected customers {expected_customers} on day {day}: {err}")
67 }),
68 );
69 }
70
71 pub fn expected_customers(&self, t: i32) -> f64 {
73 let t = t as f64;
74 self.market_capacity / (1.0 + self.q() * (-self.growth_rate() * t).exp()).powf(1.0 / self.v)
75 }
76}
77
78#[cfg(test)]
79mod test {
80 use super::*;
81
82 #[test]
83 fn test_v_param() {
84 let richards = CustomerModel::builder()
85 .initial_customers(1.)
86 .market_capacity(1_000_000.)
87 .inflection_point(200_000.)
88 .tolerance(0.0001)
89 .growth_rate(0.05)
90 .build()
91 .unwrap();
92 let v = richards.v();
93 assert_eq!((v * 10000.0).round(), 6058.);
94 richards.adjust_growth_rate(500_000., 365);
95 assert_eq!((richards.growth_rate() * 10000.0).round(), 0247.);
96 }
97}