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
64struct 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,
161 During(usize),
163 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 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 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 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 }
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#[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 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 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 return Ok(());
535 }
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 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 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 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 pub fn num_demes_in_model(&self) -> usize {
672 self.graph.num_demes()
673 }
674
675 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 pub fn cloning_rates(&self) -> Option<&[demes::CloningRate]> {
702 self.get_slice_if(Generation::Child, self.cloning_rates.as_slice())
703 }
704
705 pub fn selfing_rates(&self) -> Option<&[demes::SelfingRate]> {
710 self.get_slice_if(Generation::Child, self.selfing_rates.as_slice())
711 }
712
713 pub fn last_time_updated(&self) -> Option<ForwardTime> {
716 self.last_time_updated
717 }
718
719 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 pub fn time_iterator(&self) -> impl Iterator<Item = ForwardTime> {
732 self.model_times.time_iterator(self.last_time_updated)
733 }
734
735 pub fn parental_deme_sizes(&self) -> Option<&[CurrentSize]> {
742 self.get_slice_if(Generation::Parent, self.parental_deme_sizes.as_slice())
743 }
744
745 pub fn offspring_deme_sizes(&self) -> Option<&[CurrentSize]> {
752 self.get_slice_if(Generation::Child, self.child_deme_sizes.as_slice())
753 }
754
755 pub fn any_extant_parental_demes(&self) -> bool {
758 self.any_extant_demes(Generation::Parent)
759 }
760
761 pub fn any_extant_offspring_demes(&self) -> bool {
764 self.any_extant_demes(Generation::Child)
765 }
766
767 pub fn num_extant_parental_demes(&self) -> usize {
770 self.num_extant_demes(Generation::Parent)
771 }
772
773 pub fn num_extant_offspring_demes(&self) -> usize {
776 self.num_extant_demes(Generation::Child)
777 }
778
779 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 apply_size_function(deme, index, Some(time_raw.try_into().unwrap()))
838 } else {
839 Ok(None)
840 }
841 }
842
843 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 pub fn backwards_start_time(&self) -> demes::Time {
904 self.model_times.model_start_time()
905 }
906
907 pub fn backwards_burn_in_time(&self) -> demes::Time {
917 self.model_times.backwards_burn_in_time()
918 }
919
920 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 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 pub fn deme_names(&self) -> Box<[&str]> {
973 self.graph.deme_names()
974 }
975
976 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.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 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 graph.update_state(151_i32).unwrap();
1087 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 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 #[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 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 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 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 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(); 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 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(); 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 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(); 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 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(); 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 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(); 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 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(); 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 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 g.update_state(199).unwrap();
1658 assert_eq!(g.migrations.len(), 0);
1659 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 g.update_state(235).unwrap();
1679 assert_eq!(g.migrations.len(), 2);
1680 }
1681
1682 #[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 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 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 fgraph.update_state(at).unwrap();
2068 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 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}