mod distances;
mod hybrid_nj;
mod naive_nj;
mod newick;
mod property_tests;
mod rapid_nj;
pub use distances::DistanceMatrix;
pub use newick::to_newick;
pub use property_tests::tree_distances::{branch_score, robinson_foulds};
use std::error;
type ResultBox<T> = std::result::Result<T, Box<dyn error::Error>>;
pub type Tree = petgraph::graph::UnGraph<String, f64>;
pub struct NeighborJoiningSolver<U> {
algo: U,
dist: DistanceMatrix,
}
pub struct Canonical {}
impl NeighborJoiningSolver<Canonical> {
pub fn build(dist: DistanceMatrix) -> Self {
NeighborJoiningSolver {
algo: Canonical {},
dist,
}
}
pub fn default(dist: DistanceMatrix) -> Self {
Self::build(dist)
}
pub fn solve(self) -> ResultBox<Tree> {
naive_nj::canonical_neighbor_joining(self.dist)
}
}
pub struct RapidBtrees {
chunk_size: usize,
}
impl NeighborJoiningSolver<RapidBtrees> {
pub fn build(dist: DistanceMatrix, chunk_size: usize) -> Self {
NeighborJoiningSolver {
algo: RapidBtrees { chunk_size },
dist,
}
}
pub fn default(dist: DistanceMatrix) -> Self {
let n = dist.size();
let threads = rayon::current_num_threads();
let chunk_size = std::cmp::max(n / threads, 1);
if chunk_size == 0 {
panic!("Chunk size cannot be zero.");
}
NeighborJoiningSolver {
algo: RapidBtrees { chunk_size },
dist,
}
}
pub fn set_chunk_size(self, chunk_size: usize) -> Self {
if chunk_size < 1 {
panic!("Chunk size must be > 0.");
}
Self::build(self.dist, chunk_size)
}
pub fn solve(self) -> ResultBox<Tree> {
rapid_nj::rapid_nj(self.dist, self.algo.chunk_size)
}
}
pub struct Hybrid {
chunk_size: usize,
canonical_iters: usize,
}
impl NeighborJoiningSolver<Hybrid> {
pub fn build(dist: DistanceMatrix, chunk_size: usize, canonical_iters: usize) -> Self {
NeighborJoiningSolver {
algo: Hybrid {
chunk_size,
canonical_iters,
},
dist,
}
}
pub fn default(dist: DistanceMatrix) -> Self {
let n = dist.size();
let threads = rayon::current_num_threads();
let chunk_size = std::cmp::max(n / threads, 1);
let canonical_iters = std::cmp::max(n / 2, 1);
NeighborJoiningSolver {
algo: Hybrid {
chunk_size,
canonical_iters,
},
dist,
}
}
pub fn solve(self) -> ResultBox<Tree> {
hybrid_nj::neighbor_joining(self.dist, self.algo.canonical_iters, self.algo.chunk_size)
}
pub fn set_chunk_size(self, chunk_size: usize) -> Self {
if chunk_size < 1 {
panic!("Chunk size must be > 0.");
}
Self::build(self.dist, chunk_size, self.algo.canonical_iters)
}
pub fn set_canonical_steps(self, n: usize) -> Self {
if n < 1 {
panic!("n must be > 0.");
}
Self::build(self.dist, self.algo.chunk_size, n)
}
pub fn set_canonical_percentage(self, prop: f64) -> Self {
if prop <= 0.0 || prop >= 1.0 {
panic!("Proportion must be between 0 and 1.");
}
let n = self.dist.size() as f64 * prop / 100.0;
Self::build(self.dist, self.algo.chunk_size, n as usize)
}
}