Skip to content

Commit

Permalink
Document builder.rs (#113)
Browse files Browse the repository at this point in the history
* Make API to add migration more convient.
  • Loading branch information
molpopgen authored Jun 15, 2022
1 parent de22f5d commit 381bdf1
Showing 1 changed file with 182 additions and 13 deletions.
195 changes: 182 additions & 13 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![warn(missing_docs)]
use crate::specification::Deme;
use crate::specification::GenerationTime;
use crate::specification::Graph;
Expand All @@ -10,12 +11,27 @@ use crate::specification::UnresolvedDemeHistory;
use crate::specification::UnresolvedEpoch;
use crate::DemesError;

/// This type allows building a [`Graph`](crate::Graph) using code
/// rather then using text input.
///
/// # Notes
///
/// * A "builder" in rust will never be as convenient
/// as one in, say, Python or Juilia.
/// The lack of a rust REPL and the strong type checking
/// are the primary reasons.
/// * All error checks are delayed until resolution.
pub struct GraphBuilder {
graph: Graph,
}

impl GraphBuilder {
// public API
/// Constructor
///
/// # Returns
///
/// This function returns an "builder" containing an unresolved
/// [`Graph`](crate::Graph).
pub fn new(
time_units: TimeUnits,
generation_time: Option<GenerationTime>,
Expand All @@ -26,18 +42,29 @@ impl GraphBuilder {
}
}

/// Construct a builder with time units in generations.
///
/// This function works by calling [`GraphBuilder::new`](crate::GraphBuilder::new).
pub fn new_generations(defaults: Option<GraphDefaults>) -> Self {
Self {
graph: Graph::new(TimeUnits::Generations, None, defaults),
}
}

pub fn resolve(self) -> Result<Graph, DemesError> {
let mut builder = self;
builder.graph.resolve()?;
Ok(builder.graph)
}

/// Add a [`Deme`](crate::Deme) to the graph.
///
/// # Examples
///
/// ```
/// let start_size = demes::DemeSize::from(100.);
/// let epoch = demes::UnresolvedEpoch{start_size: Some(start_size), ..Default::default()};
/// let history = demes::UnresolvedDemeHistory::default();
/// let mut b = demes::GraphBuilder::new_generations(None);
/// b.add_deme("A", vec![epoch], history, Some("this is deme A"));
/// b.resolve().unwrap();
/// ```
///
/// # Notes
pub fn add_deme(
&mut self,
name: &str,
Expand All @@ -49,28 +76,170 @@ impl GraphBuilder {
self.graph.add_deme(ptr);
}

pub fn add_migration(
/// Add an asymmetric migration
///
/// # Examples
///
/// ```
/// let start_size = demes::DemeSize::from(100.);
/// let epoch = demes::UnresolvedEpoch{start_size: Some(start_size), ..Default::default()};
/// let history = demes::UnresolvedDemeHistory::default();
/// let mut b = demes::GraphBuilder::new_generations(None);
/// b.add_deme("A", vec![epoch], history.clone(), Some("this is deme A"));
/// b.add_deme("B", vec![epoch], history, Some("this is deme B"));
/// b.add_asymmetric_migration(Some("A"),
/// Some("B"),
/// Some(demes::MigrationRate::from(1e-4)),
/// None, // Using None for the times
/// // will mean continuous migration for the
/// // duration for which the demes coexist.
/// None);
/// b.resolve().unwrap();
/// ```
pub fn add_asymmetric_migration<S: ToString, D: ToString>(
&mut self,
source: Option<S>,
dest: Option<D>,
rate: Option<MigrationRate>,
start_time: Option<Time>,
end_time: Option<Time>,
) {
self.add_migration::<String, _, _>(None, source, dest, rate, start_time, end_time);
}

/// Add a symmetric migration
///
/// # Examples
/// ```
/// let start_size = demes::DemeSize::from(100.);
/// let epoch = demes::UnresolvedEpoch{start_size: Some(start_size), ..Default::default()};
/// let history = demes::UnresolvedDemeHistory::default();
/// let mut b = demes::GraphBuilder::new_generations(None);
/// b.add_deme("A", vec![epoch], history.clone(), Some("this is deme A"));
/// b.add_deme("B", vec![epoch], history, Some("this is deme B"));
/// b.add_symmetric_migration(Some(&["A", "B"]),
/// Some(demes::MigrationRate::from(1e-4)),
/// None, // Using None for the times
/// // will mean continuous migration for the
/// // duration for which the demes coexist.
/// None);
/// b.resolve().unwrap();
/// ```
pub fn add_symmetric_migration<D: ToString>(
&mut self,
demes: Option<Vec<String>>,
source: Option<String>,
dest: Option<String>,
demes: Option<&[D]>,
rate: Option<MigrationRate>,
start_time: Option<Time>,
end_time: Option<Time>,
) {
self.add_migration::<_, String, String>(demes, None, None, rate, start_time, end_time);
}

/// Add an [`UnresolvedMigration`](crate::UnresolvedMigration) to the graph.
///
/// # Note
///
/// This function can be inconvenient due to the generics.
/// Prefer [`add_symmetric_migration`](crate::GraphBuilder::add_symmetric_migration)
/// or [`add_asymmetric_migration`](crate::GraphBuilder::add_asymmetric_migration).
///
/// # Examples
///
/// ## Adding an asymmetric migration
///
/// ```
/// let start_size = demes::DemeSize::from(100.);
/// let epoch = demes::UnresolvedEpoch{start_size: Some(start_size), ..Default::default()};
/// let history = demes::UnresolvedDemeHistory::default();
/// let mut b = demes::GraphBuilder::new_generations(None);
/// b.add_deme("A", vec![epoch], history.clone(), Some("this is deme A"));
/// b.add_deme("B", vec![epoch], history, Some("this is deme B"));
/// b.add_migration::<String, _, _>(None,
/// Some("A"),
/// Some("B"),
/// Some(demes::MigrationRate::from(1e-4)),
/// None, // Using None for the times
/// // will mean continuous migration for the
/// // duration for which the demes coexist.
/// None);
/// b.resolve().unwrap();
/// ```
///
/// ## Adding a symmetric migration
///
/// ```
/// let start_size = demes::DemeSize::from(100.);
/// let epoch = demes::UnresolvedEpoch{start_size: Some(start_size), ..Default::default()};
/// let history = demes::UnresolvedDemeHistory::default();
/// let mut b = demes::GraphBuilder::new_generations(None);
/// b.add_deme("A", vec![epoch], history.clone(), Some("this is deme A"));
/// b.add_deme("B", vec![epoch], history, Some("this is deme B"));
/// b.add_migration::<_, String, String>(Some(&["A", "B"]),
/// None,
/// None,
/// Some(demes::MigrationRate::from(1e-4)),
/// None, // Using None for the times
/// // will mean continuous migration for the
/// // duration for which the demes coexist.
/// None);
/// b.resolve().unwrap();
/// ```
pub fn add_migration<D: ToString, S: ToString, E: ToString>(
&mut self,
demes: Option<&[D]>,
source: Option<S>,
dest: Option<E>,
rate: Option<MigrationRate>,
start_time: Option<Time>,
end_time: Option<Time>,
) {
let demes = demes.map(|value| value.iter().map(|v| v.to_string()).collect::<Vec<_>>());
let source = source.map(|value| value.to_string());
let dest = dest.map(|value| value.to_string());
self.graph
.add_migration(demes, source, dest, rate, start_time, end_time);
}

/// Add an [`UnresolvedPulse`](crate::UnresolvedPulse) to the graph.
///
/// # Examples
///
/// ```
/// let start_size = demes::DemeSize::from(100.);
/// let epoch = demes::UnresolvedEpoch{start_size: Some(start_size), ..Default::default()};
/// let history = demes::UnresolvedDemeHistory::default();
/// let mut b = demes::GraphBuilder::new_generations(None);
/// b.add_deme("A", vec![epoch], history.clone(), Some("this is deme A"));
/// b.add_deme("B", vec![epoch], history, Some("this is deme B"));
/// b.add_pulse(Some(&["A"]),
/// Some("B"),
/// Some(demes::Time::from(50.0)),
/// Some(vec![demes::Proportion::from(0.5)]));
/// b.resolve().unwrap();
/// ```
pub fn add_pulse(
&mut self,
sources: Option<Vec<String>>,
dest: Option<String>,
sources: Option<&[&str]>,
dest: Option<&str>,
time: Option<Time>,
proportions: Option<Vec<Proportion>>,
) {
let sources = sources.map(|value| value.iter().map(|v| v.to_string()).collect::<Vec<_>>());
let dest = dest.map(|value| value.to_string());
self.graph.add_pulse(sources, dest, time, proportions);
}

/// Generate and return a resolved [`Graph`](crate::Graph).
///
/// # Errors
///
/// Returns [`DemesError'](crate::DemesError) if any
/// of the data are invalid.
pub fn resolve(self) -> Result<Graph, DemesError> {
let mut builder = self;
builder.graph.resolve()?;
Ok(builder.graph)
}
}

#[cfg(test)]
Expand Down

0 comments on commit 381bdf1

Please sign in to comment.