demes_forward/
time.rs

1use std::ops::{Add, Sub};
2
3use crate::DemesForwardError;
4
5/// Representating of time moving in a forward direction.
6#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
7pub struct ForwardTime(f64);
8
9impl ForwardTime {
10    /// A valid time value is finite and positive
11    pub fn valid(&self) -> bool {
12        self.0.is_finite() && self.0.is_sign_positive()
13    }
14
15    /// Constructor
16    pub fn new<F: Into<ForwardTime>>(value: F) -> Self {
17        value.into()
18    }
19
20    /// Return the underlying value as [`std::primitive::f64`].
21    pub fn value(&self) -> f64 {
22        self.0
23    }
24}
25
26impl std::fmt::Display for ForwardTime {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        write!(f, "{}", self.0)
29    }
30}
31
32impl<T> From<T> for ForwardTime
33where
34    T: Into<f64>,
35{
36    fn from(value: T) -> Self {
37        Self(value.into())
38    }
39}
40
41impl Sub for ForwardTime {
42    type Output = Self;
43    fn sub(self, rhs: Self) -> Self::Output {
44        (self.0 - rhs.0).into()
45    }
46}
47
48impl Add for ForwardTime {
49    type Output = Self;
50    fn add(self, rhs: Self) -> Self::Output {
51        (self.0 + rhs.0).into()
52    }
53}
54
55pub(crate) struct TimeIterator {
56    current_time: ForwardTime,
57    final_time: ForwardTime,
58}
59
60impl Iterator for TimeIterator {
61    type Item = ForwardTime;
62
63    fn next(&mut self) -> Option<Self::Item> {
64        if self.current_time.0 < self.final_time.0 - 1.0 {
65            self.current_time = self.current_time + 1.0.into();
66            Some(self.current_time)
67        } else {
68            None
69        }
70    }
71}
72
73#[derive(Debug, Clone)]
74pub struct ModelTime {
75    backwards_burn_in_time: demes::Time,
76    model_duration: f64,
77    burnin_generation: f64,
78    minimum_epoch_end_time: f64,
79}
80
81impl ModelTime {
82    pub(crate) fn convert(
83        &self,
84        time: ForwardTime,
85    ) -> Result<Option<demes::Time>, DemesForwardError> {
86        if time.value() < self.model_duration + self.burnin_generation {
87            Ok(Some(
88                (self.burnin_generation + self.model_duration - 1.0 - time.value()
89                    + self.minimum_epoch_end_time)
90                    .try_into()?,
91            ))
92        } else {
93            Ok(None)
94        }
95    }
96
97    pub(crate) fn model_start_time(&self) -> demes::Time {
98        self.convert(0.into()).unwrap().unwrap()
99    }
100
101    pub fn backwards_burn_in_time(&self) -> demes::Time {
102        self.backwards_burn_in_time
103    }
104}
105
106fn get_model_start_time(graph: &demes::Graph) -> Result<demes::Time, demes::DemesError> {
107    // first end time of all demes with start time of infinity
108    let mut times = graph
109        .demes()
110        .iter()
111        .filter(|deme| deme.start_time() == f64::INFINITY)
112        .map(|deme| deme.epochs()[0].end_time())
113        .collect::<Vec<_>>();
114
115    // start times of all demes whose start time is not infinity
116    times.extend(
117        graph
118            .demes()
119            .iter()
120            .filter(|deme| deme.start_time() != f64::INFINITY)
121            .map(|deme| deme.start_time()),
122    );
123
124    times.extend(
125        graph
126            .migrations()
127            .iter()
128            .filter(|migration| migration.start_time() != f64::INFINITY)
129            .map(|migration| migration.start_time()),
130    );
131
132    times.extend(
133        graph
134            .migrations()
135            .iter()
136            .filter(|migration| migration.start_time() != f64::INFINITY)
137            .map(|migration| migration.end_time()),
138    );
139
140    times.extend(graph.pulses().iter().map(|pulse| pulse.time()));
141
142    debug_assert!(!times.is_empty());
143
144    demes::Time::try_from(f64::from(*times.iter().max().unwrap()) + 1.0)
145}
146
147impl ModelTime {
148    pub(crate) fn new_from_graph(
149        burnin_time_length: crate::ForwardTime,
150        graph: &demes::Graph,
151    ) -> Result<Self, crate::DemesForwardError> {
152        // The logic here is lifted from the fwdpy11
153        // demes import code by Aaron Ragsdale.
154
155        let model_start_time = get_model_start_time(graph)?;
156
157        let most_recent_deme_end = graph
158            .demes()
159            .iter()
160            .map(|deme| deme.end_time())
161            .collect::<Vec<_>>()
162            .into_iter()
163            .min()
164            .unwrap();
165        let model_duration = if most_recent_deme_end > 0.0 {
166            f64::from(model_start_time) - f64::from(most_recent_deme_end)
167        } else {
168            f64::from(model_start_time)
169        };
170
171        let burnin_generation = burnin_time_length.value();
172        Ok(Self {
173            backwards_burn_in_time: model_start_time,
174            model_duration,
175            burnin_generation,
176            minimum_epoch_end_time: graph.most_recent_deme_end_time().into(),
177        })
178    }
179
180    pub(crate) fn burnin_generation(&self) -> f64 {
181        self.burnin_generation
182    }
183
184    pub(crate) fn model_duration(&self) -> f64 {
185        self.model_duration
186    }
187
188    pub(crate) fn time_iterator(&self, start: Option<ForwardTime>) -> TimeIterator {
189        let current_time = match start {
190            Some(value) => (value.0 - 1.0).into(),
191            None => (-1.0).into(),
192        };
193        TimeIterator {
194            current_time,
195            final_time: (self.burnin_generation() + self.model_duration()).into(),
196        }
197    }
198}
199
200pub enum BackwardTimeWrapper {
201    Float(f64),
202    Time(demes::Time),
203}
204
205impl From<f64> for BackwardTimeWrapper {
206    fn from(value: f64) -> Self {
207        Self::Float(value)
208    }
209}
210
211impl From<demes::Time> for BackwardTimeWrapper {
212    fn from(value: demes::Time) -> Self {
213        Self::Time(value)
214    }
215}
216
217pub enum ForwardTimeWrapper {
218    Float(f64),
219    Time(ForwardTime),
220}
221
222impl From<f64> for ForwardTimeWrapper {
223    fn from(value: f64) -> Self {
224        Self::Float(value)
225    }
226}
227
228impl From<ForwardTime> for ForwardTimeWrapper {
229    fn from(value: ForwardTime) -> Self {
230        Self::Time(value)
231    }
232}