Skip to main content

demes_forward/
graph.rs

1use crate::iterators::DemeSizeHistory;
2use crate::square_matrix::SquareMatrix;
3use crate::time::ModelTime;
4use crate::CurrentSize;
5use crate::DemeSizeAt;
6use crate::DemesForwardError;
7use crate::ForwardTime;
8
9enum Generation {
10    Parent,
11    Child,
12}
13
14fn time_minus_1(time: demes::Time) -> demes::Time {
15    demes::Time::try_from(f64::from(time) - 1.0).unwrap()
16}
17
18fn get_epoch_start_time_discrete_time_model(
19    deme: &demes::Deme,
20    epoch_index: usize,
21) -> Result<demes::Time, DemesForwardError> {
22    let epoch = deme.epochs().get(epoch_index).ok_or_else(|| {
23        DemesForwardError::InternalError(format!(
24            "could not obtain epoch {} from deme {}",
25            epoch_index,
26            deme.name()
27        ))
28    })?;
29    Ok(time_minus_1(epoch.start_time()))
30}
31
32fn time_is_rounded(time: demes::Time, level: &str) -> Result<(), DemesForwardError> {
33    let f = f64::from(time);
34    if f.fract() == 0.0 || f.is_infinite() {
35        Ok(())
36    } else {
37        Err(DemesForwardError::TimeError(format!(
38            "{level} not rounded to integer: {time}",
39        )))
40    }
41}
42
43fn validate_model_times(graph: &demes::Graph) -> Result<(), DemesForwardError> {
44    graph
45        .demes()
46        .iter()
47        .try_for_each(|deme| time_is_rounded(deme.start_time(), "Deme start_time"))?;
48    graph.demes().iter().try_for_each(|deme| {
49        deme.epochs()
50            .iter()
51            .try_for_each(|epoch| time_is_rounded(epoch.end_time(), "Epoch end_time"))
52    })?;
53    graph
54        .pulses()
55        .iter()
56        .try_for_each(|pulse| time_is_rounded(pulse.time(), "Pulse time"))?;
57    graph.migrations().iter().try_for_each(|migration| {
58        time_is_rounded(migration.start_time(), "Migration start_time")?;
59        time_is_rounded(migration.end_time(), "Migration end_time")
60    })?;
61    Ok(())
62}
63
64// #[derive(Copy, Clone)]
65struct SizeFunctionDetails {
66    epoch_start_time: demes::Time,
67    epoch_end_time: demes::Time,
68    epoch_start_size: demes::DemeSize,
69    epoch_end_size: demes::DemeSize,
70    backwards_time: demes::Time,
71}
72
73impl SizeFunctionDetails {
74    fn duration(&self) -> f64 {
75        f64::from(self.epoch_start_time) - f64::from(self.epoch_end_time)
76    }
77
78    fn time_from_epoch_start(&self) -> f64 {
79        f64::from(self.epoch_start_time) - f64::from(self.backwards_time)
80    }
81}
82
83fn linear_size_change(details: SizeFunctionDetails) -> f64 {
84    let duration = details.duration() + 1.0;
85    let x = details.time_from_epoch_start() + 1.0;
86    let size_diff = f64::from(details.epoch_end_size) - f64::from(details.epoch_start_size);
87    (f64::from(details.epoch_start_size) + (x / duration) * size_diff).round()
88}
89
90fn exponential_size_change(details: SizeFunctionDetails) -> f64 {
91    let duration = details.duration() + 1.0;
92    let nt = f64::from(details.epoch_end_size).round();
93    let n0 = f64::from(details.epoch_start_size).round();
94    let growth_rate = (nt / n0).powf(1. / duration) - 1.;
95    let x = details.time_from_epoch_start() + 1.0;
96    (n0 * (1. + growth_rate).powf(x)).round()
97}
98
99fn apply_size_function(
100    deme: &demes::Deme,
101    epoch_index: usize,
102    backwards_time: Option<demes::Time>,
103) -> Result<Option<CurrentSize>, DemesForwardError> {
104    match backwards_time {
105        Some(btime) => {
106            let epoch_start_time = get_epoch_start_time_discrete_time_model(deme, epoch_index)?;
107            let current_epoch = match deme.get_epoch(epoch_index) {
108                Some(epoch) => epoch,
109                None => {
110                    return Err(DemesForwardError::InternalError(format!(
111                        "could not retrieve epoch {} from deme {}",
112                        epoch_index,
113                        deme.name()
114                    )))
115                }
116            };
117
118            let epoch_end_time = current_epoch.end_time();
119            let epoch_start_size = current_epoch.start_size();
120            let epoch_end_size = current_epoch.end_size();
121
122            let size_function_details = match current_epoch.size_function() {
123                demes::SizeFunction::Constant => return Ok(Some(epoch_start_size.try_into()?)),
124                demes::SizeFunction::Linear => linear_size_change,
125                demes::SizeFunction::Exponential => exponential_size_change,
126                _ => unimplemented!("unimplemented size function variant"),
127            };
128
129            let size: f64 = size_function_details(SizeFunctionDetails {
130                epoch_start_time,
131                epoch_end_time,
132                epoch_start_size,
133                epoch_end_size,
134                backwards_time: btime,
135            });
136
137            if !size.gt(&0.0) || !size.is_finite() {
138                Err(DemesForwardError::InvalidDemeSize(size))
139            } else {
140                Ok(Some(CurrentSize::try_from(size)?))
141            }
142        }
143        None => Ok(None),
144    }
145}
146
147#[derive(Debug, Clone)]
148struct Deme {
149    deme: demes::Deme,
150    status: DemeStatus,
151    backwards_time: Option<demes::Time>,
152    ancestors: Vec<usize>,
153    proportions: Vec<demes::Proportion>,
154}
155
156#[derive(Debug, Clone)]
157enum DemeStatus {
158    /// Before the deme first appears.
159    /// (Moving forwards in time.)
160    Before,
161    /// During the deme's Epochs
162    During(usize),
163    /// After the deme ceases to exist.
164    /// (Moving forwards in time.)
165    After,
166}
167
168impl Deme {
169    fn new(deme: demes::Deme) -> Self {
170        Self {
171            deme,
172            status: DemeStatus::Before,
173            backwards_time: None,
174            ancestors: vec![],
175            proportions: vec![],
176        }
177    }
178
179    fn is_extant(&self) -> bool {
180        matches!(self.status, DemeStatus::During(_))
181    }
182
183    fn epoch_index_for_update(&self) -> usize {
184        match self.status {
185            DemeStatus::Before => 0,
186            DemeStatus::During(x) => x,
187            DemeStatus::After => self.deme.num_epochs(),
188        }
189    }
190
191    // return None if !self.is_extant()
192    fn current_size(&self) -> Result<Option<CurrentSize>, DemesForwardError> {
193        match self.status {
194            DemeStatus::During(epoch_index) => match self.deme.get_epoch(epoch_index) {
195                Some(_) => apply_size_function(&self.deme, epoch_index, self.backwards_time),
196                None => panic!("fatal error: epoch_index out of range"),
197            },
198            _ => Ok(None),
199        }
200    }
201
202    fn ancestors(&self) -> &[usize] {
203        &self.ancestors
204    }
205
206    fn proportions(&self) -> &[demes::Proportion] {
207        &self.proportions
208    }
209
210    fn update(
211        &mut self,
212        time: demes::Time,
213        update_ancestors: bool,
214        deme_to_index: &std::collections::HashMap<String, usize>,
215    ) -> Result<CurrentSize, DemesForwardError> {
216        self.ancestors.clear();
217        self.proportions.clear();
218        let mut current_size = CurrentSize::try_from(0.0)?;
219        if time < self.deme.start_time() {
220            let i = self.epoch_index_for_update();
221
222            // NOTE: by having enumerate BEFORE
223            // skip, the j value is the offset
224            // from .epoch()[0]!!!
225            if let Some((j, _epoch)) = self
226                .deme
227                .epochs()
228                .iter()
229                .enumerate()
230                .skip(i)
231                .find(|index_epoch| time >= index_epoch.1.end_time())
232            {
233                self.status = DemeStatus::During(j);
234                self.backwards_time = Some(time);
235                if update_ancestors {
236                    // NOTE: PR 272 introduced much stricter value
237                    // checking in demes. The strict checks
238                    // led to a regression here if we try
239                    // to convert this value into demes::Time.
240                    // (The value can get < 0.0.)
241                    //
242                    // TODO: investigate why we subtract 2 here.
243                    // It seems "off" to me.
244                    let generation_to_check_ancestors = f64::from(self.deme.start_time()) - 2.0;
245                    if time > generation_to_check_ancestors {
246                        for (name, proportion) in self
247                            .deme
248                            .ancestor_names()
249                            .iter()
250                            .zip(self.deme.proportions().iter())
251                        {
252                            let index = *deme_to_index.get(name).ok_or_else(|| {
253                                DemesForwardError::InternalError(format!(
254                                    "could not get deme {name} from deme_to_index map",
255                                ))
256                            })?;
257                            self.ancestors.push(index);
258                            self.proportions.push(*proportion);
259                        }
260                    }
261                    //}
262                }
263                let deme_size = self.current_size()?;
264                current_size = deme_size.ok_or_else(|| {
265                    DemesForwardError::InternalError(format!(
266                        "failed up update current size of deme {}",
267                        self.deme.name(),
268                    ))
269                })?;
270            } else {
271                self.status = DemeStatus::After;
272                self.backwards_time = None;
273            }
274        }
275        Ok(current_size)
276    }
277}
278
279fn update_demes(
280    backwards_time: Option<demes::Time>,
281    update_ancestors: bool,
282    deme_to_index: &std::collections::HashMap<String, usize>,
283    graph: &demes::Graph,
284    demes: &mut Vec<Deme>,
285    sizes: &mut Vec<CurrentSize>,
286) -> Result<(), DemesForwardError> {
287    match backwards_time {
288        Some(time) => {
289            if demes.is_empty() {
290                sizes.clear();
291                for deme in graph.demes().iter() {
292                    demes.push(Deme::new(deme.clone()));
293                    sizes.push(CurrentSize::try_from(0.0)?);
294                }
295            }
296
297            demes.iter_mut().enumerate().try_for_each(
298                |(i, deme)| -> Result<(), DemesForwardError> {
299                    let size = deme.update(time, update_ancestors, deme_to_index)?;
300                    sizes[i] = size;
301                    Ok(())
302                },
303            )?;
304        }
305        None => demes.clear(),
306    }
307    Ok(())
308}
309
310/// Forward-time representation of a [`demes::Graph`].
311#[derive(Debug, Clone)]
312pub struct ForwardGraph {
313    graph: demes::Graph,
314    model_times: ModelTime,
315    parent_demes: Vec<Deme>,
316    child_demes: Vec<Deme>,
317    last_time_updated: Option<ForwardTime>,
318    deme_to_index: std::collections::HashMap<String, usize>,
319    pulses: Vec<demes::Pulse>,
320    migrations: Vec<demes::AsymmetricMigration>,
321    ancestry_proportions: SquareMatrix,
322    migration_matrix: SquareMatrix,
323    cloning_rates: Vec<demes::CloningRate>,
324    selfing_rates: Vec<demes::SelfingRate>,
325    parental_deme_sizes: Vec<CurrentSize>,
326    child_deme_sizes: Vec<CurrentSize>,
327}
328
329impl ForwardGraph {
330    /// Constructor
331    ///
332    /// # Parameters
333    ///
334    /// * graph: a [`demes::Graph`].
335    /// * burnin_time: Burn-in time for the model.
336    pub fn new_discrete_time<F: Into<ForwardTime> + std::fmt::Debug + Copy>(
337        graph: demes::Graph,
338        burnin_time: F,
339    ) -> Result<Self, crate::DemesForwardError> {
340        if let Some((name, index)) = graph.has_non_integer_sizes() {
341            let deme = graph.get_deme(name).unwrap();
342            let epoch = deme.epochs()[index];
343            for i in [f64::from(epoch.start_size()), f64::from(epoch.end_size())] {
344                if i.is_finite() && i.fract() != 0.0 {
345                    return Err(DemesForwardError::InvalidDemeSize(i));
346                }
347            }
348        }
349        let burnin_time = burnin_time.into();
350        if !burnin_time.valid() {
351            return Err(DemesForwardError::TimeError(format!(
352                "invalid time value: {burnin_time:?}",
353            )));
354        }
355        let graph = graph.into_integer_generations()?;
356
357        validate_model_times(&graph)?;
358
359        let model_times = ModelTime::new_from_graph(burnin_time, &graph)?;
360        let child_demes = vec![];
361        let parent_demes = vec![];
362        let mut deme_to_index = std::collections::HashMap::default();
363        for (i, deme) in graph.demes().iter().enumerate() {
364            deme_to_index.insert(deme.name().to_string(), i);
365        }
366        let pulses = vec![];
367        let ancestry_proportions = SquareMatrix::zeros(deme_to_index.len());
368        let migration_matrix = SquareMatrix::zeros(deme_to_index.len());
369        Ok(Self {
370            graph,
371            model_times,
372            parent_demes,
373            child_demes,
374            last_time_updated: None,
375            deme_to_index,
376            pulses,
377            migrations: vec![],
378            ancestry_proportions,
379            migration_matrix,
380            cloning_rates: vec![],
381            selfing_rates: vec![],
382            parental_deme_sizes: vec![],
383            child_deme_sizes: vec![],
384        })
385    }
386
387    fn update_pulses(&mut self, backwards_time: Option<demes::Time>) {
388        self.pulses.clear();
389        match backwards_time {
390            None => (),
391            Some(time) => self.graph.pulses().iter().for_each(|pulse| {
392                if !(time > pulse.time() || time < pulse.time()) {
393                    self.pulses.push(pulse.clone());
394                }
395            }),
396        }
397    }
398
399    // NOTE: performance here is poop emoji.
400    // Migrations tend to occur over long epochs
401    // and we are figuring this out from scratch each time.
402    // This may not be a "big deal" so this note is here in
403    // case of future profiling.
404    // Alternative:
405    // * Maintain a vec of current epochs that are (index, Mig)
406    // * Remove epochs no longer needed
407    // * Only add epochs not already there.
408    fn update_migrations(&mut self, backwards_time: Option<demes::Time>) {
409        self.migrations.clear();
410        match backwards_time {
411            None => (),
412            Some(time) => self.graph.migrations().iter().for_each(|migration| {
413                if time >= migration.end_time() && time < migration.start_time() {
414                    self.migrations.push(migration.clone());
415                }
416            }),
417        }
418    }
419
420    fn initialize_ancestry_proportions(&mut self) {
421        self.ancestry_proportions.fill(0.0);
422        for (i, deme) in self.child_demes.iter().enumerate() {
423            if deme.is_extant() {
424                if deme.ancestors().is_empty() {
425                    self.ancestry_proportions.set(i, i, 1.0);
426                } else {
427                    deme.ancestors()
428                        .iter()
429                        .zip(deme.proportions().iter())
430                        .for_each(|(ancestor, proportion)| {
431                            self.ancestry_proportions
432                                .set(i, *ancestor, f64::from(*proportion));
433                        });
434                }
435            }
436        }
437    }
438
439    fn update_ancestry_proportions_from_pulses(
440        &mut self,
441        parental_generation_time: ForwardTime,
442    ) -> Result<(), DemesForwardError> {
443        let mut sources = vec![];
444        let mut proportions = vec![];
445        for pulse in &self.pulses {
446            sources.clear();
447            proportions.clear();
448            let dest = *self.deme_to_index.get(pulse.dest()).ok_or_else(|| {
449                DemesForwardError::InternalError(format!(
450                    "could not fetch {} from deme_to_index map",
451                    pulse.dest()
452                ))
453            })?;
454
455            {
456                let child_deme = self.child_demes.get(dest).ok_or_else(|| {
457                    DemesForwardError::InternalError(format!(
458                        "destination deme index {dest} is invalid",
459                    ))
460                })?;
461                if !child_deme.is_extant() {
462                    return Err(DemesForwardError::InternalError(format!(
463                    "pulse dest deme {} is extinct at (forward) time {:?}, which is backwards time {:?}",
464                    self.graph.demes()[dest].name(),
465                    parental_generation_time, self.model_times.convert(parental_generation_time),
466                )));
467                }
468            }
469
470            let mut sum = 0.0;
471
472            for (source, proportion) in pulse.sources().iter().zip(pulse.proportions().iter()) {
473                let index: usize = *self.deme_to_index.get(source).ok_or_else(|| {
474                    DemesForwardError::InternalError(format!(
475                        "could not fetch deme {source} from deme_to_index map",
476                    ))
477                })?;
478                let parent_deme = self.parent_demes.get(index).ok_or_else(|| {
479                    DemesForwardError::InternalError(format!("deme index {index} is invalid",))
480                })?;
481                if !parent_deme.is_extant() {
482                    return Err(DemesForwardError::InternalError(format!(
483                            "pulse source deme {} is extinct at (forward) time {:?}, which is backwards time {:?},",
484                            self.graph.demes()[index].name(),
485                            parental_generation_time, self.model_times.convert(parental_generation_time),
486                        )));
487                }
488                sources.push(index);
489                let p = f64::from(*proportion);
490                sum += p;
491                proportions.push(p);
492            }
493
494            self.ancestry_proportions
495                .row_mut(dest)
496                .iter_mut()
497                .for_each(|v| *v *= 1. - sum);
498            sources
499                .iter()
500                .zip(proportions.iter())
501                .for_each(|(source, proportion)| {
502                    self.ancestry_proportions.set(dest, *source, *proportion);
503                });
504        }
505        Ok(())
506    }
507
508    fn update_migration_matrix(
509        &mut self,
510        parental_generation_time: ForwardTime,
511    ) -> Result<(), DemesForwardError> {
512        self.migration_matrix.fill(0.0);
513        for migration in &self.migrations {
514            let source = self.deme_to_index.get(migration.source()).ok_or_else(|| {
515                DemesForwardError::InternalError(format!(
516                    "could not fetch deme {} from deme_to_index map",
517                    migration.source()
518                ))
519            })?;
520            let dest = self.deme_to_index.get(migration.dest()).ok_or_else(|| {
521                DemesForwardError::InternalError(format!(
522                    "could not fetch deme {} from deme_to_index map",
523                    migration.dest()
524                ))
525            })?;
526            if !self.parent_demes[*source].is_extant() {
527                // This block was changed to return Ok(())
528                // in PR238.
529                // The reason is that a deme may be first appearing
530                // now and immediately involved in migration.
531                // However, the fact that it doesn't exist as parents
532                // to itself should not be an error, provided that
533                // it does have ancestry from elsewhere.
534                return Ok(());
535                //return Err(DemesForwardError::InternalError(format!(
536                //    "migration source deme {} is extinct at (forward) time {:?}, which is backwards time {:?}",
537                //    self.graph.demes()[*source].name(),
538                //    parental_generation_time,
539                //    self.model_times.convert(parental_generation_time),
540                //)));
541            }
542            if !self.child_demes[*dest].is_extant() {
543                return Err(DemesForwardError::InternalError(format!(
544                    "migration dest deme {} is extinct at forward time {:?}, which is backwards time {:?}",
545                    self.graph.demes()[*dest].name(),
546                    parental_generation_time,
547                    self.model_times.convert(parental_generation_time),
548                )));
549            }
550            self.migration_matrix
551                .set(*dest, *source, migration.rate().into());
552        }
553        Ok(())
554    }
555
556    // NOTE: this doesn't Err b/c:
557    // It is called after update_migration_matrix, which
558    // does the extant/extinct checks already
559    fn update_ancestry_proportions_from_migration_matrix(&mut self) {
560        for row in 0..self.ancestry_proportions.nrows() {
561            let migs = self.migration_matrix.row(row);
562            let props = self.ancestry_proportions.row_mut(row);
563            let one_minus_sum: f64 = 1. - migs.iter().sum::<f64>();
564
565            props.iter_mut().zip(migs.iter()).for_each(|(i, j)| {
566                *i *= one_minus_sum;
567                *i += *j;
568            });
569        }
570    }
571
572    fn deme_slice(&self, generation: Generation) -> &[Deme] {
573        match generation {
574            Generation::Parent => self.parent_demes.as_slice(),
575            Generation::Child => self.child_demes.as_slice(),
576        }
577    }
578
579    fn any_extant_demes(&self, generation: Generation) -> bool {
580        let s = self.deme_slice(generation);
581        s.iter().any(|deme| deme.is_extant())
582    }
583
584    fn num_extant_demes(&self, generation: Generation) -> usize {
585        let s = self.deme_slice(generation);
586        s.iter().filter(|deme| deme.is_extant()).count()
587    }
588
589    fn get_slice_if<'a, T: Sized>(&self, generation: Generation, s: &'a [T]) -> Option<&'a [T]> {
590        let is_empty = match generation {
591            Generation::Parent => self.parent_demes.is_empty(),
592            Generation::Child => self.child_demes.is_empty(),
593        };
594        if !is_empty {
595            Some(s)
596        } else {
597            None
598        }
599    }
600
601    /// Update the internal state of the graph to the *parental*
602    /// generation time `parental_generation_time`.
603    pub fn update_state<F: Into<ForwardTime> + std::fmt::Debug + Copy>(
604        &mut self,
605        parental_generation_time: F,
606    ) -> Result<(), DemesForwardError> {
607        let parental_generation_time = parental_generation_time.into();
608        if parental_generation_time.value().is_sign_negative()
609            || !parental_generation_time.value().is_finite()
610        {
611            return Err(DemesForwardError::TimeError(format!(
612                "invalid time for update_state: {parental_generation_time:?}",
613            )));
614        }
615        if let Some(time) = self.last_time_updated {
616            if parental_generation_time < time {
617                // gotta reset...
618                self.parent_demes.clear();
619                self.child_demes.clear();
620            }
621        }
622        let backwards_time = self.model_times.convert(parental_generation_time)?;
623        update_demes(
624            backwards_time,
625            false,
626            &self.deme_to_index,
627            &self.graph,
628            &mut self.parent_demes,
629            &mut self.parental_deme_sizes,
630        )?;
631        let child_generation_time = ForwardTime::from(parental_generation_time.value() + 1.0);
632        let backwards_time = self.model_times.convert(child_generation_time)?;
633        update_demes(
634            backwards_time,
635            true,
636            &self.deme_to_index,
637            &self.graph,
638            &mut self.child_demes,
639            &mut self.child_deme_sizes,
640        )?;
641        self.update_pulses(backwards_time);
642        self.update_migrations(backwards_time);
643
644        self.selfing_rates.clear();
645        self.cloning_rates.clear();
646        for deme in &self.child_demes {
647            match deme.status {
648                DemeStatus::During(x) => {
649                    self.cloning_rates
650                        .push(deme.deme.epochs()[x].cloning_rate());
651                    self.selfing_rates
652                        .push(deme.deme.epochs()[x].selfing_rate());
653                }
654                _ => {
655                    self.cloning_rates.push(demes::CloningRate::try_from(0.0)?);
656                    self.selfing_rates.push(demes::SelfingRate::try_from(0.0)?);
657                }
658            }
659        }
660
661        self.initialize_ancestry_proportions();
662        self.update_ancestry_proportions_from_pulses(parental_generation_time)?;
663        self.update_migration_matrix(parental_generation_time)?;
664        self.update_ancestry_proportions_from_migration_matrix();
665        self.last_time_updated = Some(parental_generation_time);
666
667        Ok(())
668    }
669
670    /// The total number of demes in the graph.
671    pub fn num_demes_in_model(&self) -> usize {
672        self.graph.num_demes()
673    }
674
675    /// The ancestry proporitions for a given offspring deme at the current time.
676    ///
677    /// # Parameters
678    ///
679    /// * offspring_deme: the index of an offspring deme.
680    ///
681    /// # Returns
682    ///
683    /// * `Some(&[f64])` if `offspring_deme` is a valid index and extant
684    ///   offspring demes exist.
685    /// * `None` otherwise.
686    pub fn ancestry_proportions(&self, offspring_deme: usize) -> Option<&[f64]> {
687        if offspring_deme >= self.num_demes_in_model() {
688            return None;
689        }
690        if !self.child_demes.is_empty() {
691            Some(self.ancestry_proportions.row(offspring_deme))
692        } else {
693            None
694        }
695    }
696
697    /// Get cloning rates of all offspring demes.
698    ///
699    /// Returns `None` if there are no extant offspring
700    /// demes.
701    pub fn cloning_rates(&self) -> Option<&[demes::CloningRate]> {
702        self.get_slice_if(Generation::Child, self.cloning_rates.as_slice())
703    }
704
705    /// Get selfing rates of all offspring demes.
706    ///
707    /// Returns `None` if there are no extant offspring
708    /// demes.
709    pub fn selfing_rates(&self) -> Option<&[demes::SelfingRate]> {
710        self.get_slice_if(Generation::Child, self.selfing_rates.as_slice())
711    }
712
713    /// Obtain the time corresponding to the last
714    /// call of [`ForwardGraph::update_state`].
715    pub fn last_time_updated(&self) -> Option<ForwardTime> {
716        self.last_time_updated
717    }
718
719    /// Obtain the end time of the model.
720    pub fn end_time(&self) -> ForwardTime {
721        let burnin_gen = self.model_times.burnin_generation();
722        let model_duration = self.model_times.model_duration();
723
724        (burnin_gen + model_duration).into()
725    }
726
727    /// Return an iterator over time values.
728    ///
729    /// The iterator starts at the last updated time and
730    /// continues until the end time.
731    pub fn time_iterator(&self) -> impl Iterator<Item = ForwardTime> {
732        self.model_times.time_iterator(self.last_time_updated)
733    }
734
735    /// Obtain the sizes of each parental deme.
736    ///
737    /// The length of the slice is equal to the number of demes
738    /// in the graph (see [`ForwardGraph::num_demes_in_model`]).
739    ///
740    /// Returns `None` if there are no parental demes at the current time.
741    pub fn parental_deme_sizes(&self) -> Option<&[CurrentSize]> {
742        self.get_slice_if(Generation::Parent, self.parental_deme_sizes.as_slice())
743    }
744
745    /// Obtain the sizes of each offspring deme.
746    ///
747    /// The length of the slice is equal to the number of demes
748    /// in the graph (see [`ForwardGraph::num_demes_in_model`]).
749    ///
750    /// Returns `None` if there are no offspring demes at the current time.
751    pub fn offspring_deme_sizes(&self) -> Option<&[CurrentSize]> {
752        self.get_slice_if(Generation::Child, self.child_deme_sizes.as_slice())
753    }
754
755    /// Return `true` if there are any extant parental
756    /// demes at the current time.
757    pub fn any_extant_parental_demes(&self) -> bool {
758        self.any_extant_demes(Generation::Parent)
759    }
760
761    /// Return `true` if there are any extant offspring
762    /// demes at the current time.
763    pub fn any_extant_offspring_demes(&self) -> bool {
764        self.any_extant_demes(Generation::Child)
765    }
766
767    /// Return the number of extant parental demes
768    /// at the current time.
769    pub fn num_extant_parental_demes(&self) -> usize {
770        self.num_extant_demes(Generation::Parent)
771    }
772
773    /// Return the number of extant offspring demes
774    /// at the current time.
775    pub fn num_extant_offspring_demes(&self) -> usize {
776        self.num_extant_demes(Generation::Child)
777    }
778
779    /// Obtain the size (number of parental individuals) of a given
780    /// deme at a given time.
781    ///
782    /// # Parameters
783    ///
784    /// * `deme`: a deme identifier
785    /// * `time`: a time
786    ///
787    /// # Returns
788    ///
789    /// * `Some(DemeSize)` if there are parents at the given time.
790    /// * `None` if `deme` does not exist or the deme exists but is
791    ///   not in existence at `time`.
792    ///
793    /// # Errors
794    ///
795    /// [`DemesForwardError`] if the calculation of deme size returns an
796    /// invalid [`demes::DemeSize`] or if `time` is not a valid value.
797    ///
798    /// # Notes
799    ///
800    /// * Time is measured backwards in time from 0.0 (inclusive of zero).
801    ///   The reason is that it is usually easier to reason about time
802    ///   in this way.
803    /// * Unlike a [`demes::Graph`], time does not go back in the past
804    ///   to "infinity". The first parental generation is considered
805    ///   to exist at a time one generation prior to the start of events
806    ///   in the graph plus the burn-in time.
807    pub fn size_at<'a, I: Into<demes::DemeId<'a>>, T: Into<crate::time::BackwardTimeWrapper>>(
808        &self,
809        deme: I,
810        time: T,
811    ) -> Result<Option<CurrentSize>, DemesForwardError> {
812        let time_raw: f64 = match time.into() {
813            crate::time::BackwardTimeWrapper::Float(time) => demes::Time::try_from(time)?.into(),
814            crate::time::BackwardTimeWrapper::Time(time) => time.into(),
815        };
816        if time_raw.is_nan() || !time_raw.is_sign_positive() {
817            return Err(DemesForwardError::TimeError(format!(
818                "invalid time value: {time_raw}"
819            )));
820        }
821        if time_raw > self.model_times.model_start_time() {
822            return Ok(None);
823        }
824        let id = deme.into();
825        let deme = match self.graph.get_deme(id) {
826            Some(d) => d,
827            None => return Ok(None),
828        };
829        if let Some((index, _epoch)) = deme
830            .epochs()
831            .iter()
832            .enumerate()
833            .find(|(_, e)| time_raw >= e.end_time() && time_raw < e.start_time())
834        {
835            // We can unwrap b/c we either made a valid demes::Time
836            // or had one passed in.
837            apply_size_function(deme, index, Some(time_raw.try_into().unwrap()))
838        } else {
839            Ok(None)
840        }
841    }
842
843    /// Generate an iterator over the size history of a deme.
844    ///
845    /// # Parameters
846    ///
847    /// * `deme`: the index or name of the deme
848    ///
849    /// # Returns
850    ///
851    /// An iterator over instances of [`DemeSizeAt`].
852    ///
853    /// # Errors
854    ///
855    /// This function will return an error if:
856    ///
857    /// * `deme` does not exist in the graph
858    ///
859    /// # Note
860    ///
861    /// The iterated values are lazily evaluated.
862    /// First `self` is cloned.
863    /// Then, the entire model history is iterated over,
864    /// and the output data is returned during iteration.
865    ///
866    /// # Panics
867    ///
868    /// * Cloning the graph requires allocations which will
869    ///   panic if the system runs out of memory
870    pub fn deme_size_history<'a, I>(
871        &self,
872        deme: I,
873    ) -> Result<impl Iterator<Item = DemeSizeAt>, DemesForwardError>
874    where
875        I: std::fmt::Debug + Into<demes::DemeId<'a>>,
876    {
877        let deme_index = match deme.into() {
878            demes::DemeId::Index(index) => index,
879            demes::DemeId::Name(name) => match self.deme_to_index.get(name) {
880                Some(&index) => index,
881                None => {
882                    return Err(
883                        demes::DemesError::DemeError(format!("invalid deme id {name}")).into(),
884                    );
885                }
886            },
887        };
888        if deme_index >= self.graph.num_demes() {
889            return Err(
890                demes::DemesError::DemeError(format!("invalid deme index {deme_index}")).into(),
891            );
892        }
893        let graph = self.clone();
894        let forward_model_start_time = f64::from(self.model_times.model_start_time());
895        DemeSizeHistory::new(graph, deme_index, forward_model_start_time)
896    }
897
898    /// Return the time in the past when the first generation
899    /// of the model exists.
900    /// This time point represents the individuals alive at
901    /// time zero (forwards in time) who are the ancestors
902    /// of those individuals born at time 1 of the model.
903    pub fn backwards_start_time(&self) -> demes::Time {
904        self.model_times.model_start_time()
905    }
906
907    /// Return the time in the model at the end of
908    /// any "burn-in".
909    ///
910    /// # Note
911    ///
912    /// This value is distinct from the burn-in *duration*.
913    /// Instead, this value represents the time when all alive
914    /// individuals are the ancestors of the first finite
915    /// time in the [`demes::Graph`].
916    pub fn backwards_burn_in_time(&self) -> demes::Time {
917        self.model_times.backwards_burn_in_time()
918    }
919
920    /// Convert a forward time value ([`ForwardTime`]) into a backwards time value
921    /// ([`demes::Time`])
922    ///
923    /// # Returns
924    ///
925    /// * `None` if `time` is ancestral to the start of the model
926    ///   or more recent than the model's end.
927    pub fn time_to_backward<T: Into<crate::time::ForwardTimeWrapper>>(
928        &self,
929        time: T,
930    ) -> Result<Option<demes::Time>, DemesForwardError> {
931        let time: ForwardTime = match time.into() {
932            crate::time::ForwardTimeWrapper::Float(time) => time.into(),
933            crate::time::ForwardTimeWrapper::Time(time) => time,
934        };
935        if !time.valid() {
936            return Err(DemesForwardError::TimeError(format!(
937                "invalid time value: {time:?}"
938            )));
939        }
940        self.model_times.convert(time)
941    }
942
943    /// Convert a backwards time value ([`demes::Time`]) into a forward time value
944    /// ([`ForwardTime`])
945    ///
946    /// # Returns
947    ///
948    /// * `None` if `time` is ancestral to the start of the model
949    ///   or more recent than the model's end.
950    pub fn time_to_forward<T: Into<crate::time::BackwardTimeWrapper>>(
951        &self,
952        time: T,
953    ) -> Result<Option<ForwardTime>, DemesForwardError> {
954        let time: demes::Time = match time.into() {
955            crate::time::BackwardTimeWrapper::Float(value) => value.try_into()?,
956            crate::time::BackwardTimeWrapper::Time(value) => value,
957        };
958        if time > self.backwards_start_time() || time < self.graph.most_recent_deme_end_time() {
959            Ok(None)
960        } else {
961            Ok(Some(
962                (f64::from(self.backwards_start_time()) - f64::from(time)).into(),
963            ))
964        }
965    }
966
967    /// Get the names of each deme in the model.
968    ///
969    /// # Note
970    ///
971    /// Implemented by calling [`demes::Graph::deme_names`].
972    pub fn deme_names(&self) -> Box<[&str]> {
973        self.graph.deme_names()
974    }
975
976    /// Access to the underlying [`demes::Graph`]
977    pub fn demes_graph(&self) -> &demes::Graph {
978        &self.graph
979    }
980}
981
982#[cfg(test)]
983mod graphs_for_testing {
984    pub fn four_deme_model() -> demes::Graph {
985        let yaml = "
986time_units: generations
987demes:
988 - name: A
989   epochs:
990    - start_size: 100
991      end_time: 50
992 - name: B
993   ancestors: [A]
994   epochs:
995    - start_size: 100
996 - name: C
997   ancestors: [A]
998   epochs:
999    - start_size: 100
1000      end_time: 49
1001 - name: D
1002   ancestors: [C, B]
1003   proportions: [0.5, 0.5]
1004   start_time: 49
1005   epochs:
1006    - start_size: 50
1007";
1008        demes::loads(yaml).unwrap()
1009    }
1010
1011    pub fn one_generation_model() -> demes::Graph {
1012        let yaml = "
1013time_units: generations
1014demes:
1015 - name: A
1016   epochs:
1017    - start_size: 50
1018      end_time: 1
1019    - start_size: 100
1020";
1021        demes::loads(yaml).unwrap()
1022    }
1023}
1024
1025#[cfg(test)]
1026mod graph_tests {
1027    use super::*;
1028
1029    fn two_epoch_model() -> demes::Graph {
1030        let yaml = "
1031time_units: generations
1032demes:
1033 - name: A
1034   epochs:
1035    - start_size: 200
1036      end_time: 50
1037    - start_size: 100
1038";
1039        demes::loads(yaml).unwrap()
1040    }
1041
1042    fn two_epoch_model_invalid_conversion_to_generations() -> demes::Graph {
1043        let yaml = "
1044time_units: years
1045description:
1046  50/1000 = 0.05, rounds to zero.
1047  Thus, the second epoch has length zero.
1048generation_time: 1000.0
1049demes:
1050 - name: A
1051   epochs:
1052    - start_size: 200
1053      end_time: 50
1054    - start_size: 100
1055";
1056        demes::loads(yaml).unwrap()
1057    }
1058
1059    #[test]
1060    fn one_deme_two_epochs() {
1061        let demes_graph = two_epoch_model();
1062        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 100_u32).unwrap();
1063        assert!(graph.update_state(-1.0).is_err());
1064        assert!(graph.update_state(f64::INFINITY).is_err());
1065        graph.update_state(125_i32).unwrap();
1066        assert_eq!(graph.num_extant_parental_demes(), 1);
1067        assert_eq!(
1068            graph.parental_deme_sizes().unwrap().first(),
1069            Some(&CurrentSize::try_from(100.0).unwrap())
1070        );
1071        graph.update_state(75_i32).unwrap();
1072        // assert_eq!(graph.parental_demes().unwrap().iter().count(), 1);
1073        assert_eq!(graph.num_extant_parental_demes(), 1);
1074        assert_eq!(
1075            graph.parental_deme_sizes().unwrap().first(),
1076            Some(&CurrentSize::try_from(200.0).unwrap())
1077        );
1078
1079        // The last generation
1080        graph.update_state(150_i32).unwrap();
1081        assert_eq!(graph.num_extant_parental_demes(), 1);
1082        assert_eq!(graph.num_extant_offspring_demes(), 0);
1083        assert!(graph.ancestry_proportions(0).is_none());
1084
1085        // One past the last generation
1086        graph.update_state(151_i32).unwrap();
1087        //assert!(graph.parental_demes().is_none());
1088        assert_eq!(graph.num_extant_parental_demes(), 0);
1089        assert_eq!(graph.num_extant_offspring_demes(), 0);
1090        assert!(graph.offspring_deme_sizes().is_none());
1091
1092        // Test what happens as we "evolve through"
1093        // an epoch boundary.
1094        let expected_sizes = |generation: i32| -> (f64, f64) {
1095            if generation < 100 {
1096                (200.0, 200.0)
1097            } else if generation < 101 {
1098                (200.0, 100.0)
1099            } else {
1100                (100.0, 100.0)
1101            }
1102        };
1103        for generation in [99_i32, 100, 101, 102] {
1104            graph.update_state(generation).unwrap();
1105            let expected = expected_sizes(generation);
1106
1107            assert!(graph
1108                .parental_deme_sizes()
1109                .unwrap()
1110                .iter()
1111                .all(|size| size == &expected.0));
1112
1113            assert!(graph
1114                .offspring_deme_sizes()
1115                .unwrap()
1116                .iter()
1117                .all(|size| size == &expected.1));
1118        }
1119    }
1120
1121    #[test]
1122    fn invalid_conversion_error() {
1123        let demes_graph = two_epoch_model_invalid_conversion_to_generations();
1124        let result = ForwardGraph::new_discrete_time(demes_graph, 100.0);
1125        assert!(matches!(
1126            result,
1127            Err(crate::DemesForwardError::DemesError(
1128                demes::DemesError::EpochError(_)
1129            ))
1130        ));
1131    }
1132
1133    #[test]
1134    fn invalid_forward_time() {
1135        {
1136            let x = ForwardTime::new(-1_i32);
1137            assert!(!x.valid());
1138        }
1139        {
1140            let x = ForwardTime::from(-1_f64);
1141            assert!(!x.valid());
1142        }
1143        {
1144            let x = ForwardTime::from(f64::INFINITY);
1145            assert!(!x.valid());
1146        }
1147        {
1148            let x = ForwardTime::from(f64::NAN);
1149            assert!(!x.valid());
1150            let graph = two_epoch_model();
1151            assert!(ForwardGraph::new_discrete_time(graph, x).is_err());
1152        }
1153    }
1154
1155    #[test]
1156    fn test_one_generation_model() {
1157        {
1158            let demes_graph = graphs_for_testing::one_generation_model();
1159            let mut graph = ForwardGraph::new_discrete_time(demes_graph, 0).unwrap();
1160            assert_eq!(graph.end_time(), 2.0.into());
1161            for i in graph.time_iterator() {
1162                graph.update_state(i).unwrap();
1163                assert!(graph
1164                    .parental_deme_sizes()
1165                    .unwrap()
1166                    .iter()
1167                    .any(|size| size > &0.0));
1168                if i == graph.end_time() - 1.0.into() {
1169                    assert!(graph.offspring_deme_sizes().is_none(), "time = {i:?}");
1170                } else {
1171                    assert!(graph.offspring_deme_sizes().is_some(), "time = {i:?}");
1172                }
1173            }
1174        }
1175    }
1176}
1177
1178#[cfg(test)]
1179mod test_nonlinear_size_changes {
1180    use super::*;
1181
1182    fn two_epoch_model_linear_growth() -> demes::Graph {
1183        let yaml = "
1184time_units: generations
1185demes:
1186 - name: A
1187   epochs:
1188    - start_size: 200
1189      end_time: 50
1190    - start_size: 100
1191      end_size: 200
1192      size_function: linear
1193";
1194        demes::loads(yaml).unwrap()
1195    }
1196
1197    fn two_epoch_model_linear_decline() -> demes::Graph {
1198        let yaml = "
1199time_units: generations
1200demes:
1201 - name: A
1202   epochs:
1203    - start_size: 200
1204      end_time: 50
1205    - start_size: 200
1206      end_size: 100
1207      size_function: linear
1208";
1209        demes::loads(yaml).unwrap()
1210    }
1211
1212    fn two_epoch_model_exponential_growth() -> demes::Graph {
1213        let yaml = "
1214time_units: generations
1215demes:
1216 - name: A
1217   epochs:
1218    - start_size: 200
1219      end_time: 50
1220    - start_size: 100
1221      end_size: 200
1222      size_function: exponential
1223";
1224        demes::loads(yaml).unwrap()
1225    }
1226
1227    fn two_deme_split_with_ancestral_size_change() -> demes::Graph {
1228        let yaml = "
1229description: Two deme model with migration and size changes.
1230time_units: generations
1231demes:
1232- name: ancestral
1233  description: ancestral deme, two epochs
1234  epochs:
1235  - {end_time: 20, start_size: 100}
1236  - {end_time: 10, start_size: 200}
1237- name: deme1
1238  description: child 1
1239  epochs:
1240  - {start_size: 250, end_size: 500, end_time: 0}
1241  ancestors: [ancestral]
1242- name: deme2
1243  description: child 2
1244  epochs:
1245  - {start_size: 50, end_size: 200, end_time: 0}
1246  ancestors: [ancestral]
1247migrations:
1248- {demes: [deme1, deme2], rate: 1e-3}
1249";
1250        demes::loads(yaml).unwrap()
1251    }
1252
1253    // This test is lifted from fwdpy11 and is what brought up
1254    // the need for PR #235
1255    #[test]
1256    fn test_size_history_two_deme_split_with_ancestral_size_change() {
1257        let demes_graph = two_deme_split_with_ancestral_size_change();
1258        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 100_u32).unwrap();
1259
1260        // Manually iterate graph until we hit deme 1 for the first time as a child.
1261        graph.update_state(0.0).unwrap();
1262        let mut found = false;
1263        for time in graph.time_iterator() {
1264            graph.update_state(time).unwrap();
1265            let o = graph.offspring_deme_sizes().unwrap();
1266            if o[1] > 0.0 {
1267                // Then we make sure it is the right size
1268                assert_eq!(time.value(), 110.0);
1269                let g = (500_f64 / 250.).powf(1. / 10.) - 1.;
1270                assert_eq!(o[1], (250_f64 * (1. + g).powf(1.0)).round());
1271                found = true;
1272                break;
1273            }
1274        }
1275        assert!(found);
1276        found = false;
1277        // Manually iterate graph until we hit deme 2 for the first time as a child.
1278        graph.update_state(0.0).unwrap();
1279        for time in graph.time_iterator() {
1280            graph.update_state(time).unwrap();
1281            if let Some(o) = graph.offspring_deme_sizes() {
1282                if o[2] > 0.0 {
1283                    // Then we make sure it is the right size
1284                    assert_eq!(time.value(), 110.0);
1285                    let g = (200_f64 / 50.).powf(1. / 10.) - 1.;
1286                    assert_eq!(o[2], (50_f64 * (1. + g).powf(1.0)).round());
1287                    found = true;
1288                    break;
1289                }
1290            }
1291        }
1292        assert!(found);
1293    }
1294
1295    #[test]
1296    fn test_two_epoch_model_linear_growth() {
1297        let demes_graph = two_epoch_model_linear_growth();
1298        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 100_u32).unwrap();
1299
1300        let slope: f64 = (200. - 100.) / 50.;
1301        graph.update_state(100).unwrap(); // last generation of the 1st epoch
1302        if let Some(deme) = graph.parent_demes.first() {
1303            assert_eq!(
1304                deme.current_size().unwrap(),
1305                Some(CurrentSize::try_from(200.).unwrap())
1306            );
1307        } else {
1308            panic!();
1309        }
1310        if let Some(deme) = graph.child_demes.first() {
1311            assert_eq!(
1312                deme.current_size().unwrap(),
1313                Some(CurrentSize::try_from((100. + slope).round()).unwrap())
1314            );
1315        } else {
1316            panic!();
1317        }
1318        // one generation before end
1319        graph.update_state(149).unwrap();
1320        if let Some(deme) = graph.child_demes.first() {
1321            assert_eq!(
1322                deme.current_size().unwrap(),
1323                Some(CurrentSize::try_from(200.).unwrap())
1324            );
1325        } else {
1326            panic!();
1327        }
1328        graph.update_state(150).unwrap(); // last gen
1329        assert!(graph.child_demes.is_empty());
1330        if let Some(deme) = graph.parent_demes.first() {
1331            assert_eq!(
1332                deme.current_size().unwrap(),
1333                Some(CurrentSize::try_from(200.).unwrap())
1334            );
1335        } else {
1336            panic!();
1337        }
1338
1339        // 1/2-way into the final epoch
1340        graph.update_state(125).unwrap();
1341        let expected_size: f64 = (100.0 + 25_f64 * slope).round();
1342        let expected_size = CurrentSize::try_from(expected_size.round()).unwrap();
1343        assert_eq!(
1344            graph.parental_deme_sizes().unwrap().first(),
1345            Some(&expected_size)
1346        );
1347
1348        let expected_size: f64 = (100.0 + 26_f64 * slope).round();
1349        let expected_size = CurrentSize::try_from(expected_size.round()).unwrap();
1350        assert_eq!(
1351            graph.offspring_deme_sizes().unwrap().first(),
1352            Some(&expected_size)
1353        );
1354    }
1355
1356    #[test]
1357    fn test_two_epoch_model_linear_decline() {
1358        let demes_graph = two_epoch_model_linear_decline();
1359        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 100_u32).unwrap();
1360        graph.update_state(100).unwrap(); // last generation of the 1st epoch
1361        if let Some(deme) = graph.parent_demes.first() {
1362            assert_eq!(
1363                deme.current_size().unwrap(),
1364                Some(CurrentSize::try_from(200.).unwrap())
1365            );
1366        } else {
1367            panic!();
1368        }
1369        let slope: f64 = (100. - 200.) / 50.;
1370        if let Some(deme) = graph.child_demes.first() {
1371            assert_eq!(
1372                deme.current_size().unwrap(),
1373                Some(CurrentSize::try_from((200. + slope).round()).unwrap())
1374            );
1375        } else {
1376            panic!();
1377        }
1378        // one generation before end
1379        graph.update_state(149).unwrap();
1380        if let Some(deme) = graph.child_demes.first() {
1381            assert_eq!(
1382                deme.current_size().unwrap(),
1383                Some(CurrentSize::try_from(100.).unwrap())
1384            );
1385        } else {
1386            panic!();
1387        }
1388        graph.update_state(150).unwrap(); // last gen
1389        assert!(graph.child_demes.is_empty());
1390        if let Some(deme) = graph.parent_demes.first() {
1391            assert_eq!(
1392                deme.current_size().unwrap(),
1393                Some(CurrentSize::try_from(100.).unwrap())
1394            );
1395        } else {
1396            panic!();
1397        }
1398
1399        // 1/2-way into the final epoch
1400        graph.update_state(125).unwrap();
1401        let expected_size: CurrentSize = (200_f64 + 25.0 * slope).round().try_into().unwrap();
1402        assert_eq!(
1403            graph.parental_deme_sizes().unwrap().first(),
1404            Some(&expected_size)
1405        );
1406        let expected_size = CurrentSize::try_from((200_f64 + 26.0 * slope).round()).unwrap();
1407        assert_eq!(
1408            graph.offspring_deme_sizes().unwrap().first(),
1409            Some(&expected_size)
1410        );
1411    }
1412
1413    #[test]
1414    fn test_two_epoch_model_exponential_growth() {
1415        let demes_graph = two_epoch_model_exponential_growth();
1416        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 100_u32).unwrap();
1417        graph.update_state(100).unwrap(); // last generation of the 1st epoch
1418        if let Some(deme) = graph.parent_demes.first() {
1419            assert_eq!(
1420                deme.current_size().unwrap(),
1421                Some(CurrentSize::try_from(200.).unwrap())
1422            );
1423        } else {
1424            panic!();
1425        }
1426        let g = (200_f64 / 100.).powf(1. / 50.) - 1.;
1427        if let Some(deme) = graph.child_demes.first() {
1428            assert_eq!(
1429                deme.current_size().unwrap(),
1430                Some(CurrentSize::try_from((100. * (1. + g)).round()).unwrap())
1431            );
1432        } else {
1433            panic!();
1434        }
1435        // one generation before end
1436        graph.update_state(149).unwrap();
1437        if let Some(deme) = graph.child_demes.first() {
1438            assert_eq!(
1439                deme.current_size().unwrap(),
1440                Some(CurrentSize::try_from(200.).unwrap())
1441            );
1442        } else {
1443            panic!();
1444        }
1445        graph.update_state(150).unwrap(); // last gen
1446        assert!(graph.child_demes.is_empty());
1447        if let Some(deme) = graph.parent_demes.first() {
1448            assert_eq!(
1449                deme.current_size().unwrap(),
1450                Some(CurrentSize::try_from(200.).unwrap())
1451            );
1452        } else {
1453            panic!();
1454        }
1455
1456        // 1/2-way into the final epoch
1457        graph.update_state(125).unwrap();
1458        let g = (200_f64 / 100_f64).powf(1. / 50.0) - 1.;
1459        let expected_size = CurrentSize::try_from((100.0 * ((1. + g).powf(25.))).round()).unwrap();
1460        assert_eq!(
1461            graph.parental_deme_sizes().unwrap().first(),
1462            Some(&expected_size)
1463        );
1464        let expected_size = CurrentSize::try_from((100.0 * ((1. + g).powf(26.0))).round()).unwrap();
1465        assert_eq!(
1466            graph.offspring_deme_sizes().unwrap().first(),
1467            Some(&expected_size)
1468        );
1469        crate::test_functions::test_model_duration(&mut graph);
1470    }
1471}
1472
1473#[cfg(test)]
1474mod test_deme_ancestors {
1475    use super::*;
1476    use crate::test_functions::test_model_duration;
1477
1478    #[test]
1479    fn test_four_deme_model() {
1480        let demes_graph = graphs_for_testing::four_deme_model();
1481        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 100).unwrap();
1482
1483        {
1484            graph.update_state(0).unwrap();
1485            for (i, deme) in graph.child_demes.iter().enumerate() {
1486                if i < 1 {
1487                    assert!(deme.is_extant());
1488                    assert!(graph
1489                        .ancestry_proportions(i)
1490                        .unwrap()
1491                        .iter()
1492                        .any(|p| p > &0.0));
1493                } else {
1494                    assert!(!graph
1495                        .ancestry_proportions(i)
1496                        .unwrap()
1497                        .iter()
1498                        .any(|p| p > &0.0));
1499                }
1500            }
1501        }
1502
1503        {
1504            graph.update_state(100).unwrap();
1505            let deme = graph.parent_demes.first().unwrap();
1506            assert!(deme.is_extant());
1507            assert_eq!(deme.ancestors().len(), 0);
1508
1509            for descendant_deme in [1_usize, 2] {
1510                let deme = graph.child_demes.get(descendant_deme).unwrap();
1511                assert!(deme.is_extant());
1512                assert_eq!(deme.ancestors().len(), 1);
1513                assert_eq!(deme.ancestors()[0], 0_usize);
1514                match graph.ancestry_proportions(descendant_deme) {
1515                    Some(ancestry) => {
1516                        for ancestor in deme.ancestors() {
1517                            assert_eq!(ancestry[*ancestor], 1.0);
1518                        }
1519                    }
1520                    None => panic!("expected Some(ancestry)"),
1521                }
1522            }
1523        }
1524        {
1525            graph.update_state(101).unwrap();
1526            let deme = graph.parent_demes.first().unwrap();
1527            assert_eq!(deme.ancestors().len(), 0);
1528
1529            for descendant_deme in [1_usize, 2] {
1530                let deme = graph.child_demes.get(descendant_deme).unwrap();
1531                if descendant_deme == 2 {
1532                    assert_eq!(
1533                        graph.offspring_deme_sizes().unwrap().get(2),
1534                        Some(&CurrentSize::try_from(0.0).unwrap())
1535                    );
1536                } else {
1537                    assert!(deme.is_extant());
1538                }
1539                assert_eq!(deme.ancestors().len(), 0);
1540            }
1541            let deme = graph.child_demes.get(3).unwrap();
1542            assert!(deme.is_extant());
1543            assert_eq!(deme.ancestors().len(), 2);
1544            assert_eq!(deme.ancestors(), &[2, 1]);
1545            assert_eq!(deme.proportions().len(), 2);
1546            assert_eq!(deme.proportions(), &[0.5, 0.5]);
1547
1548            for ancestor in deme.ancestors() {
1549                assert!(graph.parent_demes.get(*ancestor).unwrap().is_extant());
1550            }
1551        }
1552
1553        {
1554            graph.update_state(102).unwrap();
1555            for deme in graph.child_demes.iter() {
1556                assert!(deme.ancestors().is_empty());
1557                assert!(deme.proportions().is_empty());
1558            }
1559        }
1560        test_model_duration(&mut graph);
1561    }
1562
1563    #[test]
1564    fn test_four_deme_model_duration() {
1565        let demes_graph = graphs_for_testing::four_deme_model();
1566        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 73).unwrap();
1567        test_model_duration(&mut graph);
1568    }
1569}
1570
1571#[cfg(test)]
1572mod test_pulses {
1573    use super::*;
1574
1575    fn model_with_pulses() -> demes::Graph {
1576        let yaml = "
1577time_units: generations
1578demes:
1579 - name: A
1580   epochs:
1581    - start_size: 50
1582 - name: B
1583   epochs:
1584    - start_size: 50
1585pulses:
1586 - sources: [A]
1587   dest: B
1588   time: 100
1589   proportions: [0.5]
1590";
1591        demes::loads(yaml).unwrap()
1592    }
1593
1594    #[test]
1595    fn test_pulses() {
1596        let demes_g = model_with_pulses();
1597        let mut g = ForwardGraph::new_discrete_time(demes_g, 200.).unwrap();
1598
1599        for time in [199, 200] {
1600            g.update_state(time).unwrap();
1601            for deme in 0..g.num_demes_in_model() {
1602                let ap = crate::test_functions::ancestry_proportions_from_graph(&g, deme).unwrap();
1603                let p = g.ancestry_proportions(deme).unwrap();
1604                for pi in p {
1605                    assert!(pi <= &1.0);
1606                }
1607                p.iter()
1608                    .zip(ap.iter())
1609                    .for_each(|(a, b)| assert!((a - b).abs() <= 1e-9, "{a} {b}"));
1610            }
1611        }
1612    }
1613}
1614
1615#[cfg(test)]
1616mod test_migrations {
1617    use super::*;
1618    fn model_with_migrations() -> demes::Graph {
1619        let yaml = "
1620time_units: generations
1621demes:
1622 - name: A
1623   epochs:
1624    - start_size: 50
1625 - name: B
1626   epochs:
1627    - start_size: 50
1628migrations:
1629 - source: A
1630   dest: B
1631   rate: 0.25
1632   start_time: 50
1633   end_time: 25
1634 - source: B
1635   dest: A
1636   rate: 0.1
1637   start_time: 40
1638   end_time: 20
1639 - demes: [A, B]
1640   rate: 0.05
1641   start_time: 15
1642";
1643        demes::loads(yaml).unwrap()
1644    }
1645
1646    #[test]
1647    fn test_migration_rate_changes_1() {
1648        let demes_g = model_with_migrations();
1649        let mut g = ForwardGraph::new_discrete_time(demes_g, 200.).unwrap();
1650
1651        // Making sure ;)
1652        // g.update_state(250).unwrap();
1653        // assert!(g.child_demes().is_none());
1654        // assert!(g.parental_demes().is_some());
1655
1656        // One gen before everyone starts migrating
1657        g.update_state(199).unwrap();
1658        assert_eq!(g.migrations.len(), 0);
1659        // At forward time 201, we are at the
1660        // start of the first migration epoch,
1661        // meaning that children born at 201 can be migrants
1662        g.update_state(200).unwrap();
1663        assert_eq!(g.migrations.len(), 1);
1664
1665        g.update_state(209).unwrap();
1666        assert_eq!(g.migrations.len(), 1);
1667
1668        g.update_state(210).unwrap();
1669        assert_eq!(g.migrations.len(), 2);
1670
1671        g.update_state(229).unwrap();
1672        assert_eq!(g.migrations.len(), 1);
1673
1674        g.update_state(230).unwrap();
1675        assert_eq!(g.migrations.len(), 0);
1676
1677        // Symmetric mig, so 2 Asymmetric deals...
1678        g.update_state(235).unwrap();
1679        assert_eq!(g.migrations.len(), 2);
1680    }
1681
1682    // Another test from fwdpy11 that caught a bug here.
1683    #[test]
1684    fn test_migration_rate_changes_2() {
1685        let yaml = "
1686time_units: generations
1687generation_time: 1
1688demes:
1689- name: A
1690  epochs:
1691  - {end_time: 0, start_size: 100}
1692- name: B
1693  epochs:
1694  - {end_time: 0, start_size: 100}
1695migrations:
1696- demes: [A, B]
1697  rate: 0.0
1698  end_time: 30
1699- demes: [A, B]
1700  rate: 0.5
1701  start_time: 30
1702  end_time: 20
1703- demes: [A, B]
1704  rate: 0.0
1705  start_time: 20
1706  end_time: 10
1707- demes: [A, B]
1708  rate: 0.5
1709  start_time: 10
1710";
1711        let demes_g = demes::loads(yaml).unwrap();
1712        let mut g = ForwardGraph::new_discrete_time(demes_g, 10_u32).unwrap();
1713
1714        //All ancestor demes are constant size.
1715        //Therefore, at the end of burn-in, the child generation
1716        //will have nonzero ancestry from each deme.
1717        g.update_state(10.0).unwrap();
1718        for deme in [0_usize, 1] {
1719            assert!(g
1720                .ancestry_proportions(deme)
1721                .unwrap()
1722                .iter()
1723                .all(|x| x > &0.0));
1724        }
1725
1726        // Advance to the final parental generation
1727        g.update_state(g.end_time() - 2_f64.into()).unwrap();
1728        for deme in [0_usize, 1] {
1729            assert!(g
1730                .ancestry_proportions(deme)
1731                .unwrap()
1732                .iter()
1733                .all(|x| x > &0.0));
1734        }
1735    }
1736}
1737
1738#[cfg(test)]
1739mod test_fractional_times {
1740    use super::*;
1741
1742    fn bad_epoch_end_time() -> demes::Graph {
1743        let yaml = "
1744time_units: generations
1745demes:
1746 - name: A
1747   epochs:
1748    - start_size: 100
1749      end_time: 10.2
1750";
1751        demes::loads(yaml).unwrap()
1752    }
1753
1754    fn bad_deme_start_time() -> demes::Graph {
1755        let yaml = "
1756time_units: generations
1757demes:
1758 - name: A
1759   epochs:
1760    - start_size: 100
1761      end_time: 10
1762 - name: B
1763   start_time: 50.1
1764   ancestors: [A]
1765   epochs:
1766    - start_size: 50
1767";
1768        demes::loads(yaml).unwrap()
1769    }
1770
1771    fn bad_pulse_time() -> demes::Graph {
1772        let yaml = "
1773time_units: generations
1774demes:
1775 - name: A
1776   epochs:
1777    - start_size: 100
1778      end_time: 10
1779 - name: B
1780   start_time: 50
1781   ancestors: [A]
1782   epochs:
1783    - start_size: 50
1784pulses:
1785 - sources: [A]
1786   dest: B
1787   proportions: [0.5]
1788   time: 30.2
1789";
1790        demes::loads(yaml).unwrap()
1791    }
1792
1793    fn bad_migration_start_time() -> demes::Graph {
1794        let yaml = "
1795time_units: generations
1796demes:
1797 - name: A
1798   epochs:
1799    - start_size: 100
1800      end_time: 10
1801 - name: B
1802   start_time: 50
1803   ancestors: [A]
1804   epochs:
1805    - start_size: 50
1806migrations:
1807 - source: A
1808   dest: B
1809   rate: 0.5
1810   start_time: 30.2
1811   end_time: 10
1812";
1813        demes::loads(yaml).unwrap()
1814    }
1815
1816    fn bad_migration_end_time() -> demes::Graph {
1817        let yaml = "
1818time_units: generations
1819demes:
1820 - name: A
1821   epochs:
1822    - start_size: 100
1823      end_time: 10
1824 - name: B
1825   start_time: 50
1826   ancestors: [A]
1827   epochs:
1828    - start_size: 50
1829migrations:
1830 - source: A
1831   dest: B
1832   rate: 0.5
1833   start_time: 30
1834   end_time: 10.2
1835";
1836        demes::loads(yaml).unwrap()
1837    }
1838
1839    fn run_invalid_model(f: fn() -> demes::Graph) {
1840        let demes_graph = f();
1841        assert!(ForwardGraph::new_discrete_time(demes_graph, 1).is_ok());
1842    }
1843
1844    #[test]
1845    fn test_invalid_models() {
1846        run_invalid_model(bad_epoch_end_time);
1847        run_invalid_model(bad_deme_start_time);
1848        run_invalid_model(bad_pulse_time);
1849        run_invalid_model(bad_migration_start_time);
1850        run_invalid_model(bad_migration_end_time);
1851    }
1852}
1853
1854#[cfg(test)]
1855mod test_ancestry_proportions {
1856    use super::*;
1857    use crate::test_functions::*;
1858
1859    #[test]
1860    fn sequential_pulses_at_same_time_two_demes() {
1861        let yaml = "
1862time_units: generations
1863demes:
1864 - name: A
1865   epochs:
1866    - start_size: 1000
1867 - name: B
1868   epochs:
1869    - start_size: 1000
1870 - name: C
1871   epochs:
1872    - start_size: 1000
1873pulses:
1874- sources: [A]
1875  dest: C
1876  proportions: [0.33]
1877  time: 10
1878- sources: [B]
1879  dest: C
1880  proportions: [0.25]
1881  time: 10
1882";
1883        let demes_graph = demes::loads(yaml).unwrap();
1884        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 50).unwrap();
1885        let index_a: usize = 0;
1886        let index_b: usize = 1;
1887        let index_c: usize = 2;
1888        let mut ancestry_proportions = vec![0.0; 3];
1889        ancestry_proportions[index_c] = 1.0;
1890        update_ancestry_proportions(&[index_a], &[0.33], &mut ancestry_proportions);
1891        update_ancestry_proportions(&[index_b], &[0.25], &mut ancestry_proportions);
1892        graph.update_state(49.0).unwrap();
1893        assert!(!graph.pulses.is_empty());
1894        assert_eq!(graph.ancestry_proportions(2).unwrap().len(), 3);
1895        graph
1896            .ancestry_proportions(2)
1897            .unwrap()
1898            .iter()
1899            .zip(ancestry_proportions.iter())
1900            .for_each(|(a, b)| assert!((a - b).abs() <= 1e-9));
1901    }
1902
1903    #[test]
1904    fn sequential_pulses_at_same_time_two_demes_reverse_pulse_order() {
1905        let yaml = "
1906time_units: generations
1907demes:
1908 - name: A
1909   epochs:
1910    - start_size: 1000
1911 - name: B
1912   epochs:
1913    - start_size: 1000
1914 - name: C
1915   epochs:
1916    - start_size: 1000
1917pulses:
1918- sources: [B]
1919  dest: C
1920  proportions: [0.25]
1921  time: 10
1922- sources: [A]
1923  dest: C
1924  proportions: [0.33]
1925  time: 10
1926";
1927        let demes_graph = demes::loads(yaml).unwrap();
1928        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 50).unwrap();
1929        graph.update_state(49.0).unwrap();
1930        assert!(!graph.pulses.is_empty());
1931        assert_eq!(graph.ancestry_proportions(2).unwrap().len(), 3);
1932        let ancestry_proportions = ancestry_proportions_from_graph(&graph, 2).unwrap();
1933        assert_eq!(ancestry_proportions.len(), 3);
1934        graph
1935            .ancestry_proportions(2)
1936            .unwrap()
1937            .iter()
1938            .zip(ancestry_proportions.iter())
1939            .for_each(|(a, b)| assert!((a - b).abs() <= 1e-9, "{a} {b}"));
1940
1941        for child_deme in [0, 1] {
1942            let independent = ancestry_proportions_from_graph(&graph, child_deme).unwrap();
1943            assert_eq!(
1944                graph.ancestry_proportions(child_deme),
1945                Some(independent.as_slice())
1946            );
1947        }
1948    }
1949
1950    #[test]
1951    fn test_pulse_that_does_nothing() {
1952        let yaml = "
1953time_units: generations
1954description:
1955 The pulse does nothing because
1956 demes B and C are new after time 50.
1957 Demes with finite start_time must
1958 have 100% of their ancestry accounted
1959 for in the ancestors field.
1960demes:
1961 - name: A
1962   epochs:
1963    - start_size: 100
1964      end_time: 50
1965 - name: B
1966   ancestors: [A]
1967   epochs:
1968    - start_size: 100
1969 - name: C
1970   ancestors: [A]
1971   epochs:
1972    - start_size: 100
1973pulses:
1974 - sources: [A]
1975   dest: B
1976   time: 50
1977   proportions: [0.5]
1978";
1979        let demes_graph = demes::loads(yaml).unwrap();
1980        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 0).unwrap();
1981        graph.update_state(0.0).unwrap();
1982        for child_deme in [1, 2] {
1983            match graph.child_demes[child_deme].current_size() {
1984                Ok(value) => assert!(value.is_some()),
1985                Err(_) => panic!("unexpected Error"),
1986            }
1987            assert_eq!(graph.child_demes[child_deme].ancestors().len(), 1);
1988            assert_eq!(
1989                graph.ancestry_proportions(child_deme),
1990                Some([1., 0., 0.].as_slice())
1991            );
1992        }
1993    }
1994    #[test]
1995    fn sequential_pulses_at_same_time_two_demes_with_symmetric_migration() {
1996        let yaml = "
1997time_units: generations
1998demes:
1999 - name: A
2000   epochs:
2001    - start_size: 1000
2002 - name: B
2003   epochs:
2004    - start_size: 1000
2005 - name: C
2006   epochs:
2007    - start_size: 1000
2008pulses:
2009- sources: [A]
2010  dest: C
2011  proportions: [0.33]
2012  time: 10
2013- sources: [B]
2014  dest: C
2015  proportions: [0.25]
2016  time: 10
2017migrations:
2018 - demes: [A, B, C]
2019   rate: 1e-3
2020   start_time: 11
2021   end_time: 0
2022";
2023        let demes_graph = demes::loads(yaml).unwrap();
2024        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 50).unwrap();
2025        for parental_generation_time in [49.0, 50.0, 51.0, 52.0] {
2026            graph.update_state(parental_generation_time).unwrap();
2027
2028            for child_deme in 0..3 {
2029                let a = graph.ancestry_proportions(child_deme).unwrap();
2030                let e = ancestry_proportions_from_graph(&graph, child_deme).unwrap();
2031                assert_eq!(a.len(), e.len());
2032                a.iter().zip(e.iter()).for_each(|(a, b)| {
2033                    assert!(
2034                        (a - b).abs() <= 1e-9,
2035                        "failure at {parental_generation_time}, {:?}",
2036                        graph.migrations
2037                    )
2038                });
2039            }
2040        }
2041    }
2042
2043    #[test]
2044    fn test_ancestry_proportions_from_ancestors() {
2045        let yaml = "
2046time_units: generations
2047demes:
2048 - name: A1
2049   epochs:
2050    - end_time: 50
2051      start_size: 100
2052 - name: A2
2053   epochs:
2054    - end_time: 50
2055      start_size: 100
2056 - name: D
2057   start_time: 50
2058   ancestors: [A1, A2]
2059   proportions: [0.5, 0.5]
2060   epochs:
2061    - start_size: 100
2062";
2063        let graph = demes::loads(yaml).unwrap();
2064        let mut fgraph = ForwardGraph::new_discrete_time(graph, 1.).unwrap();
2065        let at = fgraph.time_to_forward(50.0).unwrap().unwrap();
2066        // We are updating to a PARENTAL generation
2067        fgraph.update_state(at).unwrap();
2068        // So the ancestry proportions of the OFFSPRING should be:
2069        let aprops = fgraph.ancestry_proportions(2).unwrap().to_owned();
2070        assert_eq!(aprops, vec![0.5, 0.5, 0.0]);
2071        let ap = ancestry_proportions_from_graph(&fgraph, 2).unwrap();
2072        assert_eq!(aprops, ap);
2073
2074        let at = fgraph.time_to_forward(49.0).unwrap().unwrap();
2075        fgraph.update_state(at).unwrap();
2076        let aprops = fgraph.ancestry_proportions(2).unwrap().to_owned();
2077        assert_eq!(aprops, vec![0., 0., 1.]);
2078        let ap = ancestry_proportions_from_graph(&fgraph, 2).unwrap();
2079        assert_eq!(aprops, ap);
2080
2081        let at = fgraph.time_to_forward(51.0).unwrap().unwrap();
2082        fgraph.update_state(at).unwrap();
2083        let aprops = fgraph.ancestry_proportions(2).unwrap().to_owned();
2084        assert_eq!(aprops, vec![0., 0., 0.]);
2085        let ap = ancestry_proportions_from_graph(&fgraph, 2).unwrap();
2086        assert_eq!(ap, vec![0., 0., 0.]);
2087    }
2088}
2089
2090#[cfg(test)]
2091mod migration_matrix {
2092    use super::*;
2093    use crate::test_functions::*;
2094
2095    #[test]
2096    fn simple_two_way_migration() {
2097        let yaml = "
2098time_units: generations
2099demes:
2100 - name: A
2101   epochs:
2102    - start_size: 1000
2103 - name: B
2104   epochs:
2105    - start_size: 1000
2106 - name: C
2107   epochs:
2108    - start_size: 1000
2109migrations:
2110- source: B
2111  dest: C
2112  rate: 0.25
2113- source: A
2114  dest: C
2115  rate: 0.25 
2116";
2117        let demes_graph = demes::loads(yaml).unwrap();
2118        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 10).unwrap();
2119        // Parental generations 0-9 have no migration,
2120        // which starts at generation 10
2121        for generation in 0..9 {
2122            graph.update_state(generation).unwrap();
2123            assert_eq!(
2124                graph.ancestry_proportions(2),
2125                Some([0.25, 0.25, 0.5].as_slice())
2126            );
2127            assert_eq!(
2128                graph.ancestry_proportions(2),
2129                Some([0.25, 0.25, 0.5].as_slice())
2130            );
2131        }
2132    }
2133
2134    #[test]
2135    fn simple_two_way_symmetric_migration() {
2136        let yaml = "
2137time_units: generations
2138demes:
2139 - name: A
2140   epochs:
2141    - start_size: 1000
2142 - name: B
2143   epochs:
2144    - start_size: 1000
2145 - name: C
2146   epochs:
2147    - start_size: 1000
2148migrations:
2149- demes: [A, B, C]
2150  rate: 0.25
2151";
2152        let demes_graph = demes::loads(yaml).unwrap();
2153        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 10).unwrap();
2154        for generation in 0..10 {
2155            graph.update_state(generation).unwrap();
2156            for deme in 0..3 {
2157                let expected = ancestry_proportions_from_graph(&graph, deme).unwrap();
2158                assert_eq!(
2159                    graph.ancestry_proportions(deme),
2160                    Some(expected.as_slice()),
2161                    "{deme} {expected:?}",
2162                );
2163            }
2164        }
2165    }
2166}
2167
2168#[cfg(test)]
2169mod test_cloning_selfing_rates {
2170    use super::*;
2171
2172    #[test]
2173    fn test_rate_changes() {
2174        let yaml = "
2175time_units: generations
2176demes:
2177 - name: A
2178   epochs:
2179    - selfing_rate: 0.5
2180      end_time: 10
2181      start_size: 50
2182    - cloning_rate: 0.25
2183";
2184        let demes_graph = demes::loads(yaml).unwrap();
2185        let mut graph = ForwardGraph::new_discrete_time(demes_graph, 10).unwrap();
2186        graph.update_state(0.0).unwrap();
2187        match graph.selfing_rates() {
2188            Some(rates) => assert_eq!(rates[0], 0.5),
2189            None => panic!(),
2190        }
2191        match graph.cloning_rates() {
2192            Some(rates) => assert_eq!(rates[0], 0.0),
2193            None => panic!(),
2194        }
2195        graph.update_state(10.0).unwrap();
2196        match graph.selfing_rates() {
2197            Some(rates) => assert_eq!(rates[0], 0.0),
2198            None => panic!(),
2199        }
2200        match graph.cloning_rates() {
2201            Some(rates) => assert_eq!(rates[0], 0.25),
2202            None => panic!(),
2203        }
2204    }
2205}