Skip to content

Commit eae11b4

Browse files
authored
Merge pull request #33 from molpopgen/add_tree_sequence
Add TreeSequence
2 parents dcb5a02 + a57b947 commit eae11b4

File tree

5 files changed

+179
-0
lines changed

5 files changed

+179
-0
lines changed

src/_macros.rs

+29
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,35 @@ macro_rules! build_tskit_type {
5858
};
5959
}
6060

61+
macro_rules! build_consuming_tskit_type {
62+
($name: ident, $ll_name: ty, $drop: ident, $consumed: ty) => {
63+
impl Drop for $name {
64+
fn drop(&mut self) {
65+
let rv = unsafe { $drop(&mut *self.inner) };
66+
panic_on_tskit_error!(rv);
67+
}
68+
}
69+
70+
impl crate::ffi::TskitConsumingType<$ll_name, $consumed> for $name {
71+
fn wrap(consumed: $consumed) -> Self {
72+
let temp: std::mem::MaybeUninit<$ll_name> = std::mem::MaybeUninit::uninit();
73+
$name {
74+
consumed,
75+
inner: unsafe { Box::<$ll_name>::new(temp.assume_init()) },
76+
}
77+
}
78+
79+
fn as_ptr(&self) -> *const $ll_name {
80+
&*self.inner
81+
}
82+
83+
fn as_mut_ptr(&mut self) -> *mut $ll_name {
84+
&mut *self.inner
85+
}
86+
}
87+
};
88+
}
89+
6190
macro_rules! metadata_to_vector {
6291
($T: ty, $self: expr, $row: expr) => {
6392
crate::metadata::char_column_to_vector(

src/ffi.rs

+22
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@ pub trait TskitType<T> {
1515
fn as_mut_ptr(&mut self) -> *mut T;
1616
}
1717

18+
/// Define what it means to wrap a tskit struct
19+
/// that contains another tskit type C (the
20+
/// "consumed" type).
21+
/// This trait models a type that takes another
22+
/// type as input for initialization and effectively
23+
/// owns it.
24+
/// A key example of such a type is a [`crate::TreeSequence`],
25+
/// which owns the underying [`crate::TableCollection`].
26+
/// In practice, one needs to implement Drop for
27+
/// test types, calling the tsk_foo_free() function
28+
/// corresponding to tsk_foo_t.
29+
pub trait TskitConsumingType<T, C> {
30+
/// Encapsulate tsk_foo_t and return rust
31+
/// object. Best practices seem to
32+
/// suggest using Box for this.
33+
fn wrap(consumed: C) -> Self;
34+
/// Return const pointer
35+
fn as_ptr(&self) -> *const T;
36+
/// Return mutable pointer
37+
fn as_mut_ptr(&mut self) -> *mut T;
38+
}
39+
1840
#[cfg(test)]
1941
mod tests {
2042
use super::*;

src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod node_table;
1616
mod population_table;
1717
mod site_table;
1818
mod table_collection;
19+
mod trees;
1920
pub mod types;
2021

2122
// re-export fundamental constants that
@@ -42,6 +43,8 @@ pub use node_table::NodeTable;
4243
pub use population_table::PopulationTable;
4344
pub use site_table::SiteTable;
4445
pub use table_collection::TableCollection;
46+
pub use trees::TreeSequence;
47+
4548
/// Handles return codes from low-level tskit functions.
4649
///
4750
/// When an error from the tskit C API is detected,

src/table_collection.rs

+40
Original file line numberDiff line numberDiff line change
@@ -375,11 +375,44 @@ impl TableCollection {
375375
pub fn equals(&self, other: &TableCollection, options: tsk_flags_t) -> bool {
376376
unsafe { ll_bindings::tsk_table_collection_equals(self.as_ptr(), other.as_ptr(), options) }
377377
}
378+
379+
/// Return a "deep" copy of the tables.
380+
pub fn deepcopy(&self) -> Result<TableCollection, TskitError> {
381+
let mut copy = TableCollection::new(1.)?;
382+
383+
let rv =
384+
unsafe { ll_bindings::tsk_table_collection_copy(self.as_ptr(), copy.as_mut_ptr(), 0) };
385+
386+
if rv < 0 {
387+
return Err(crate::error::TskitError::ErrorCode { code: rv });
388+
}
389+
390+
Ok(copy)
391+
}
392+
393+
/// Return a [`crate::TreeSequence`] based on the tables.
394+
/// This function will raise errors if tables are not sorted,
395+
/// not indexed, or invalid in any way.
396+
pub fn tree_sequence(self) -> Result<crate::TreeSequence, TskitError> {
397+
crate::TreeSequence::new(self)
398+
}
378399
}
379400

380401
#[cfg(test)]
381402
mod test {
382403
use super::*;
404+
use crate::TSK_NULL;
405+
406+
fn make_small_table_collection() -> TableCollection {
407+
let mut tables = TableCollection::new(1000.).unwrap();
408+
tables.add_node(0, 1.0, TSK_NULL, TSK_NULL).unwrap();
409+
tables.add_node(0, 0.0, TSK_NULL, TSK_NULL).unwrap();
410+
tables.add_node(0, 0.0, TSK_NULL, TSK_NULL).unwrap();
411+
tables.add_edge(0., 1000., 0, 1).unwrap();
412+
tables.add_edge(0., 1000., 0, 2).unwrap();
413+
tables.build_index(0).unwrap();
414+
tables
415+
}
383416

384417
#[test]
385418
fn test_sequence_length() {
@@ -577,4 +610,11 @@ mod test {
577610
let mut tables = TableCollection::new(1000.).unwrap();
578611
tables.free().unwrap();
579612
}
613+
614+
#[test]
615+
fn test_deepcopy() {
616+
let tables = make_small_table_collection();
617+
let dumps = tables.deepcopy().unwrap();
618+
assert!(tables.equals(&dumps, 0));
619+
}
580620
}

src/trees.rs

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use crate::bindings as ll_bindings;
2+
use crate::error::TskitError;
3+
use crate::ffi::{TskitConsumingType, TskitType};
4+
use crate::{tsk_flags_t, TableCollection, TSK_NULL};
5+
use ll_bindings::tsk_treeseq_free;
6+
7+
/// A tree sequence.
8+
///
9+
/// This is a thin wrapper around the C type `tsk_treeseq_t`.
10+
///
11+
/// When created from a [`TableCollection`], the input tables are
12+
/// moved into the `TreeSequence` object.
13+
/// # Examples
14+
///
15+
/// ```
16+
/// let mut tables = tskit::TableCollection::new(1000.).unwrap();
17+
/// tables.add_node(0, 1.0, tskit::TSK_NULL, tskit::TSK_NULL).unwrap();
18+
/// tables.add_node(0, 0.0, tskit::TSK_NULL, tskit::TSK_NULL).unwrap();
19+
/// tables.add_node(0, 0.0, tskit::TSK_NULL, tskit::TSK_NULL).unwrap();
20+
/// tables.add_edge(0., 1000., 0, 1).unwrap();
21+
/// tables.add_edge(0., 1000., 0, 2).unwrap();
22+
///
23+
/// // tables gets moved into our treeseq variable:
24+
/// let treeseq = tables.tree_sequence();
25+
/// ```
26+
pub struct TreeSequence {
27+
consumed: TableCollection,
28+
inner: Box<ll_bindings::tsk_treeseq_t>,
29+
}
30+
31+
build_consuming_tskit_type!(
32+
TreeSequence,
33+
ll_bindings::tsk_treeseq_t,
34+
tsk_treeseq_free,
35+
TableCollection
36+
);
37+
38+
impl TreeSequence {
39+
/// Create a tree sequence from a [`TableCollection`].
40+
/// In general, [`TableCollection::tree_sequence`] may be preferred.
41+
/// The table collection is moved/consumed.
42+
pub fn new(tables: TableCollection) -> Result<Self, TskitError> {
43+
let mut treeseq = Self::wrap(tables);
44+
let rv = unsafe {
45+
ll_bindings::tsk_treeseq_init(treeseq.as_mut_ptr(), treeseq.consumed.as_ptr(), 0)
46+
};
47+
if rv < 0 {
48+
return Err(crate::error::TskitError::ErrorCode { code: rv });
49+
}
50+
Ok(treeseq)
51+
}
52+
53+
/// Obtain a copy of the [`TableCollection`]
54+
pub fn dump_tables(&self) -> Result<TableCollection, TskitError> {
55+
self.consumed.deepcopy()
56+
}
57+
}
58+
59+
#[cfg(test)]
60+
mod test_trees {
61+
use super::*;
62+
63+
fn make_small_table_collection() -> TableCollection {
64+
let mut tables = TableCollection::new(1000.).unwrap();
65+
tables.add_node(0, 1.0, TSK_NULL, TSK_NULL).unwrap();
66+
tables.add_node(0, 0.0, TSK_NULL, TSK_NULL).unwrap();
67+
tables.add_node(0, 0.0, TSK_NULL, TSK_NULL).unwrap();
68+
tables.add_edge(0., 1000., 0, 1).unwrap();
69+
tables.add_edge(0., 1000., 0, 2).unwrap();
70+
tables.build_index(0).unwrap();
71+
tables
72+
}
73+
74+
#[test]
75+
fn test_create_treeseq_new_from_tables() {
76+
let tables = make_small_table_collection();
77+
let _treeseq = TreeSequence::new(tables).unwrap();
78+
}
79+
80+
#[test]
81+
fn test_create_treeseq_from_tables() {
82+
let tables = make_small_table_collection();
83+
let _treeseq = tables.tree_sequence().unwrap();
84+
}
85+
}

0 commit comments

Comments
 (0)