From a1860410e41c1461d7d11ed944a03084661eb6eb Mon Sep 17 00:00:00 2001 From: "Kevin R. Thornton" Date: Thu, 16 Jun 2022 10:23:33 -0700 Subject: [PATCH] Document specification.rs (#119) * add missing docs lint to lib.rs * Add Deme::get_epoch --- src/builder.rs | 1 - src/lib.rs | 1 + src/specification.rs | 452 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 450 insertions(+), 4 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index dd0edfcc..cc13140d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,4 +1,3 @@ -#![warn(missing_docs)] use crate::specification::Deme; use crate::specification::GenerationTime; use crate::specification::Graph; diff --git a/src/lib.rs b/src/lib.rs index 4f4277d4..a8fa463a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,7 @@ //!} //! ``` +#![warn(missing_docs)] #![warn(rustdoc::broken_intra_doc_links)] mod macros; diff --git a/src/specification.rs b/src/specification.rs index 93826e10..be054bd8 100644 --- a/src/specification.rs +++ b/src/specification.rs @@ -13,6 +13,47 @@ use std::io::Read; use std::ops::Deref; use std::rc::Rc; +/// Store time values. +/// +/// This is a newtype wrapper for [`f64`](std::primitive::f64). +/// +/// # Notes +/// +/// * The units are in the [`TimeUnits`](crate::TimeUnits) +/// of the [`Graph`](crate::Graph). +/// * Invalid values are caught when a `Graph` is +/// resolved. Funcions that generate resolved graphs are: +/// - [`loads`](crate::loads) +/// - [`load`](crate::load) +/// - [`GraphBuilder::resolve`](crate::GraphBuilder::resolve) +/// +/// # Examples +/// +/// ## In a `YAML` record +/// +/// ``` +/// let yaml = " +/// time_units: years +/// generation_time: 25 +/// description: A deme that existed until 20 years ago. +/// demes: +/// - name: deme +/// epochs: +/// - start_size: 50 +/// end_time: 20 +/// "; +/// demes::loads(yaml).unwrap(); +/// ``` +/// +/// ## Using rust code +/// +/// Normally, one only needs to create a `Time` when +/// working with [`GraphBuilder`](crate::GraphBuilder). +/// +/// ``` +/// let t = demes::Time::from(0.0); +/// assert_eq!(t, 0.0); +/// ``` #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[repr(transparent)] #[serde(try_from = "TimeTrampoline")] @@ -126,11 +167,48 @@ impl PartialEq for HashableTime { impl Eq for HashableTime {} +/// The size of a [`Deme`](crate::Deme) at a given [`Time`](crate::Time). +/// +/// This is a newtype wrapper for [`f64`](std::primitive::f64). +/// +/// # Notes +/// +/// * The size may take on non-integer values. +/// +/// # Examples +/// +/// ## In a `YAML` record +/// +/// ``` +/// let yaml = " +/// time_units: years +/// generation_time: 25 +/// description: +/// A deme of 50 individuals that grew to 100 individuals +/// in the last 100 years. +/// demes: +/// - name: deme +/// epochs: +/// - start_size: 50 +/// end_time: 100 +/// - start_size: 50 +/// end_size: 100 +/// "; +/// demes::loads(yaml).unwrap(); +/// ``` +/// +/// ## Using rust code +/// +/// Normally, one only needs to create a `DemeSize` when +/// working with [`GraphBuilder`](crate::GraphBuilder). +/// +/// ``` +/// let t = demes::DemeSize::from(50.0); +/// assert_eq!(t, 50.0); +/// ``` #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[serde(from = "f64")] #[repr(transparent)] -/// Representation of deme sizes. -/// The underlying `f64` must be non-negative, non-NaN. pub struct DemeSize(f64); impl DemeSize { @@ -149,6 +227,83 @@ impl DemeSize { impl_newtype_traits!(DemeSize); +/// An ancestry proportion. +/// +/// This is a newtype wrapper for [`f64`](std::primitive::f64). +/// +/// # Interpretation +/// +/// With respect to a deme in an *offspring* time step, +/// a proportion is the fraction of ancsestry from a given +/// parental deme. +/// +/// # Examples +/// +/// ## In `YAML` input +/// +/// ### Ancestral proportions of demes +/// +/// ``` +/// let yaml = " +/// time_units: generations +/// description: +/// An admixed deme appears 100 generations ago. +/// Its initial ancestry is 90% from ancestor1 +/// and 10% from ancestor2. +/// demes: +/// - name: ancestor1 +/// epochs: +/// - start_size: 50 +/// end_time: 100 +/// - name: ancestor2 +/// epochs: +/// - start_size: 50 +/// end_time: 100 +/// - name: admixed +/// ancestors: [ancestor1, ancestor2] +/// proportions: [0.9, 0.1] +/// start_time: 100 +/// epochs: +/// - start_size: 200 +/// "; +/// demes::loads(yaml).unwrap(); +/// ``` +/// +/// ### Pulse proportions +/// +/// ``` +/// let yaml = " +/// time_units: generations +/// description: +/// Two demes coexist without migration. +/// Sixty three (63) generations ago, +/// deme1 contributes 50% of ancestry +/// to all individuals born in deme2. +/// demes: +/// - name: deme1 +/// epochs: +/// - start_size: 50 +/// - name: deme2 +/// epochs: +/// - start_size: 50 +/// pulses: +/// - sources: [deme1] +/// dest: deme2 +/// proportions: [0.5] +/// time: 63 +/// "; +/// demes::loads(yaml).unwrap(); +/// ``` +/// +/// ## Using rust code +/// +/// Normally, one only needs to create a `Proportion` when +/// working with [`GraphBuilder`](crate::GraphBuilder). +/// +/// ``` +/// let t = demes::Proportion::from(0.5); +/// assert_eq!(t, 0.5); +/// ``` #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[repr(transparent)] #[serde(from = "f64")] @@ -170,6 +325,7 @@ impl Proportion { impl_newtype_traits!(Proportion); +/// A half-open time interval `[present, past)`. #[derive(Clone, Copy, Debug)] pub struct TimeInterval { start_time: Time, @@ -221,10 +377,12 @@ impl TimeInterval { self.contains(other) } + /// Return the resolved start time (past) of the interval. pub fn start_time(&self) -> Time { self.start_time } + /// Return the resolved end time (present) of the interval. pub fn end_time(&self) -> Time { self.end_time } @@ -234,11 +392,70 @@ impl TimeInterval { } } +/// Specify how deme sizes change during an [`Epoch`](crate::Epoch). +/// +/// # Examples +/// +/// ``` +/// let yaml = " +/// time_units: years +/// generation_time: 25 +/// description: +/// A deme of 50 individuals that grew to 100 individuals +/// in the last 100 years. +/// Default behavior is that size changes are exponential. +/// demes: +/// - name: deme +/// epochs: +/// - start_size: 50 +/// end_time: 100 +/// - start_size: 50 +/// end_size: 100 +/// "; +/// let graph = demes::loads(yaml).unwrap(); +/// let deme = graph.get_deme_from_name("deme").unwrap(); +/// assert_eq!(deme.num_epochs(), 2); +/// let last_epoch = deme.get_epoch(1).unwrap(); +/// assert!(matches!(last_epoch.size_function(), +/// demes::SizeFunction::Exponential)); +/// let first_epoch = deme.get_epoch(0).unwrap(); +/// assert!(matches!(first_epoch.size_function(), +/// demes::SizeFunction::Constant)); +/// ``` +/// +/// Let's change the function to linear for the second +/// epoch: +/// +/// ``` +/// let yaml = " +/// time_units: years +/// generation_time: 25 +/// description: +/// A deme of 50 individuals that grew to 100 individuals +/// in the last 100 years. +/// demes: +/// - name: deme +/// epochs: +/// - start_size: 50 +/// end_time: 100 +/// - start_size: 50 +/// end_size: 100 +/// size_function: linear +/// "; +/// let graph = demes::loads(yaml).unwrap(); +/// let deme = graph.get_deme_from_name("deme").unwrap(); +/// let last_epoch = deme.get_epoch(1).unwrap(); +/// assert!(matches!(last_epoch.size_function(), +/// demes::SizeFunction::Linear)); +/// ``` #[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)] #[serde(rename_all = "lowercase")] pub enum SizeFunction { + #[allow(missing_docs)] Constant, + #[allow(missing_docs)] Exponential, + #[allow(missing_docs)] Linear, } @@ -253,6 +470,7 @@ impl Display for SizeFunction { } } +/// The cloning rate of an [`Epoch`](crate::Epoch). #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[repr(transparent)] #[serde(from = "f64")] @@ -280,6 +498,7 @@ impl Default for CloningRate { impl_newtype_traits!(CloningRate); +/// The selfing rate of an [`Epoch`](crate::Epoch). #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[repr(transparent)] #[serde(from = "f64")] @@ -307,6 +526,14 @@ impl Default for SelfingRate { impl_newtype_traits!(SelfingRate); +/// A migration rate. +/// +/// # Examples +/// +/// ## Using [`GraphBuilder`](crate::GraphBuilder) +/// +/// * [`GraphBuilder::add_symmetric_migration`](crate::GraphBuilder::add_symmetric_migration) +/// * [`GraphBuilder::add_asymmetric_migration`](crate::GraphBuilder::add_asymmetric_migration) #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[repr(transparent)] #[serde(from = "f64")] @@ -334,14 +561,41 @@ impl Default for MigrationRate { impl_newtype_traits!(MigrationRate); +/// An unresolved migration epoch. +/// +/// All input migrations are resolved to [`AsymmetricMigration`](crate::AsymmetricMigration) +/// instances. +/// +/// # Examples +/// +/// ## [`GraphBuilder`](crate::GraphBuilder) +/// +/// This type supports member field initialization using defaults. +/// This form of initalization is used in: +/// +/// * [`GraphDefaults`](crate::GraphDefaults) +/// +/// ``` +/// let _ = demes::UnresolvedMigration{source: Some("A".to_string()), +/// dest: Some("B".to_string()), +/// rate: Some(demes::MigrationRate::from(0.2)), +/// ..Default::default() +/// }; +/// ``` #[derive(Clone, Default, Debug, Serialize, Deserialize, Eq, PartialEq)] #[serde(deny_unknown_fields)] pub struct UnresolvedMigration { + /// The demes involved in symmetric migration epochs pub demes: Option>, + /// The source deme of an asymmetric migration epoch pub source: Option, + /// The destination deme of an asymmetric migration epoch pub dest: Option, + /// The start time of a migration epoch pub start_time: Option