Introduction

Required background.

  • We assume familiarity with tskit. See tskit.dev.
  • Comfort with rust is required.
    • The book is a good place to start.
    • The Rustonomicon is for those who wish to dig deeper into unsafe rust and FFI. The 'nomicon is helpful for understanding how things like the tskit rust API can be built in the first place.

Conventions used in this document

Here, tskit means the rust API. tskit-c and tskit-python refer to the C and Python APIs, respectively.

The phrase "data model" refers to this. This document will make little sense without an understanding of the data model.

Relation to the C API

Where necessary, we will note differences from the behavior of tskit-c.

Much of the rust API works by calling the C API. We do not change the semantics of the C API. However, we do make stricter statements about the ownership relationships between types. For example, the C API can result in the following situation:

  • A heap-allocated table collection is used to record data about the ancestry of a sample.
  • That table collection is used to initialize a tree sequence. The tree sequence is told to take ownership of the tables.

This is a case where the C API requires that you respectfully no longer work with the heap-allocated table collection. To do so is undefined behavior.

The rust API forbids such situations. The creation of a tree sequence from tables consumes the tables via a move operation. Thus, any further actions on the tables is a compiler error.

This example is the kinds of differences between tskit and tskit-c. Undefined behavior is (close to) impossible with tskit.

Quick overview

Do you need tskit-rust?

The use-cases for tskit-rust are the same as for tskit-c:

  1. Developing a new performance-oriented application.
  2. The input/output of this application will be a .trees file.

What does tskit-rust add?

Briefly, you get the performance of C and the strong safety guarantees of rust.

What is tskit-rust missing?

The crate does not cover the entire C API. However, client code can make direct calls to that API via the module tskit::bindings.

Adding tskit as a dependency to a rust project

In your Cargo.toml:

[dependencies]
tskit = "~X.Y.Z"

The latest version to fill in X.Y.Z can be found here. See here for how to specify version numbers.

Feature flags.

tskit defines several cargo features. These are defined in the API docs.

Working with table collections

The next sections cover how to work with the TableCollection, which is one of tskit's fundamental types.

Creation

We initialize a TableCollection with a sequence length. In tskit-c, the genome length is a C double. Here it is a newtype called tskit::Position:

    let sequence_length = tskit::Position::from(100.0);
    if let Ok(tables) = tskit::TableCollection::new(sequence_length) {
        assert_eq!(tables.sequence_length(), sequence_length);
        // In tskit, the various newtypes can be compared to
        // the low-level types they wrap.
        assert_eq!(tables.sequence_length(), 100.0);
    } else {
        panic!(
            "TableCollection creation sequence length = {} failed",
            sequence_length
        );
    }

The newtype pattern gives type safety by disallowing you to send a position to a function where a time is required, etc.. However, it can be inconvenient to type out the full type names every time. Thus, the API defines most functions taking arguments Into<T> where T is one of our newtypes. This design means that the following is equivalent to what we wrote above:

    let tables = tskit::TableCollection::new(100.0).unwrap();

Adding rows to tables

  • For each table type (node, edge., etc.), we have a function to add a row.
  • We can only add rows to tables in mutable TableCollection instances.

For example, to add a node:

        let mut tables = tskit::TableCollection::new(100.0).unwrap();
        if let Ok(node_id) = tables.add_node(
            0,                         // Node flags
            tskit::Time::from(0.0),    // Birth time
            tskit::PopulationId::NULL, // Population id
            tskit::IndividualId::NULL, // Individual id
        ) {
            assert_eq!(node_id, 0);
        }

We see from the if let pattern that functions adding rows return Result. In general, errors only occur when the C back-end fails to allocate memory to expand the table columns. If we add a row with invalid data, no error is returned! To catch such errors, we must explicitly check table integrity (see below).

Again, we can take advantage of being able to pass in any type that is Into<_> the required newtype:

        let node_id = tables.add_node(0, 0.0, -1, -1).unwrap();

See the API docs for more details and examples.

Adding nodes using default values

This section is more advanced and may be skipped during a first read.

For some tables it may be common to input the same values over and over for some fields when adding rows. Let's take a look at how to use default values when adding rows to a node table.

Default instances of NodeDefaults contain default values for the flags, individual, and population fields:

    let defaults = tskit::NodeDefaults::default();

Add a node with these values and a given birth time:

    let node = tables.add_node_with_defaults(0.0, &defaults).unwrap();

We can use struct update syntax to create a new node marked as a sample while re-using our other defaults:

    let node = tables
        .add_node_with_defaults(
            0.0,
            // Create a new, temporary defaults instance
            &tskit::NodeDefaults {
                // Mark the new node as a sample
                flags: tskit::NodeFlags::new_sample(),
                // Use remaining values from our current defaults
                ..defaults
            },
        )
        .unwrap();

See the NodeDefaults section of the API reference for more.

Metadata

Metadata can complicate the picture a bit:

  • Metadata types are defined by the client and are thus a generic in the tskit API.
  • We do not want to impose too many trait bounds on the client-defined types.
  • Metadata is optional on a per-row basis for any given table.

NodeDefaultsWithMetadata handles the case where rows may or may not have metadata. The metadata type is generic with trait bound tskit::NodeMetadata. Because metadata are optional per-row, any metadata defaults are stored as an Option.

For the following examples, this will be our metadata type:

    pub struct NodeMetadata {
        pub value: i32,
    }
Case 1: no default metadata

A common use case is that the metadata differs for every row. For this case, it makes sense for the default value to be the None variant of the Option.

This case is straightforward:


    // Create a type alias for brevity
    type DefaultsWithMetadata = tskit::NodeDefaultsWithMetadata<NodeMetadata>;
    // Default metadata is None
    let defaults = DefaultsWithMetadata::default();

    // A row with no metadata
    let n0 = tables.add_node_with_defaults(0.0, &defaults).unwrap();

    // A row with metadata
    let n1 = tables
        .add_node_with_defaults(
            0.0,
            &DefaultsWithMetadata {
                population: 3.into(),
                metadata: Some(NodeMetadata { value: 42 }),
                ..defaults
            },
        )
        .unwrap();

    // Another row with metadata, different from the last.
    let n2 = tables
        .add_node_with_defaults(
            0.0,
            &DefaultsWithMetadata {
                population: 1.into(),
                metadata: Some(NodeMetadata { value: 1234 }),
                ..defaults
            },
        )
        .unwrap();
Case 2: default metadata

TL;DR:

  • If table row defaults include metadata, you can run into use-after-move issues. Fortunately, the compiler will catch this as an error.
  • The solution is for your metadata type to implement Clone.

Consider the following case:

    // What if there is default metadata for all rows?
    let defaults = DefaultsWithMetadata {
        metadata: Some(NodeMetadata { value: 42 }),
        ..Default::default()
    };

Imagine that the first row we add uses different metadata but all the other default values:

    let n0 = tables
        .add_node_with_defaults(
            0.0,
            &DefaultsWithMetadata {
                metadata: Some(NodeMetadata { value: 2 * 42 }),
                ..defaults
            },
        )
        .unwrap();

Nothing interesting has happened. However, let's take a look at what we need to do if our next row uses a non-default population field and the default metadata:

    let n1 = tables
        .add_node_with_defaults(
            0.0,
            &DefaultsWithMetadata {
                population: 6.into(),
                ..defaults.clone()
            },
        )
        .unwrap();

Note the call to ..defaults.clone(). (For that call to compile, NodeMetadata must implement Clone!.) Without that, our defaults instance would have moved, leading to a move-after-use compiler error when we add a third row:

    let n2 = tables
        .add_node_with_defaults(
            0.0,
            &DefaultsWithMetadata {
                individual: 7.into(),
                ..defaults
            },
        )
        .unwrap();

Accessing table columns

We can access table rows using either the relevant newtype or i32 (which is identical to the tskit-c typedef tsk_id_t). The following snippet adds and edge and then validates the data for that row of the table:

    if let Ok(edge_id) = tables.add_edge(left, right, parent, child) {
        // Take a reference to an edge table (& tskit::EdgeTable)
        let edges = tables.edges();
        if let Some(p) = edges.parent(edge_id) {
            assert_eq!(p, parent);
        }
        if let Some(c) = edges.child(edge_id) {
            assert_eq!(c, child);
        }
        if let Some(l) = edges.left(edge_id) {
            assert_eq!(l, left);
        }
        if let Some(r) = edges.right(edge_id) {
            assert_eq!(r, right);
        }
    } else {
        panic!("that should have worked...");
    }

The return type of the getters is the Option enum. The None variant is returned when row indexes are out of range:

    assert!(tables.edges().parent(tskit::EdgeId::NULL).is_none());

Accessing table rows

The rows of a table contain two types of data:

  • Numerical data consisting of a single value.
  • Ragged data. Examples include metadata for all tables, ancestral states for sites, derived states for mutations, parent and location information for individuals, etc..

tskit provides two ways to access row data. The first is by a "view", which contains non-owning references to the ragged column data. The second is by row objects containing copies of the ragged column data.

The former will be more efficient when the ragged columns are populated. The latter will be more convenient to work with because the API is a standard rust iterator.

By holding references, row views have the usual implications for borrowing. The row objects, however, own their data and are thus independent of their parent objects.

Row views

To generate a row view using a row id:

    if let Some(row) = tables.edges().row(edge_id) {
        assert_eq!(row.id, 0);
        assert_eq!(row.left, left);
        assert_eq!(row.right, right);
        assert_eq!(row.parent, parent);
        assert_eq!(row.child, child);
    } else {
        panic!("that should have worked...");
    }

To iterate over all views we use lending iterators:

    let mut edge_table_lending_iter = tables.edges().lending_iter();
    while let Some(row_view) = edge_table_lending_iter.next() {
        // there is only one row!
        assert_eq!(row_view.id, 0);
        assert_eq!(row_view.left, left);
        assert_eq!(row_view.right, right);
        assert_eq!(row_view.parent, parent);
        assert_eq!(row_view.child, child);
        assert!(row_view.metadata.is_none()); // no metadata in our table
    }

Lending iterators

The lending iterators are implemented using the streaming_iterator crate. (The community now prefers the term "lending" over "streaming" for this concept.) The tskit prelude includes the trait declarations that allow the code shown above to compile.

rust 1.65.0 stabilized Generic Associated Types, or GATs. GATs allows lending iterators to be implemented directly without the workarounds used in the streaming_iterator crate. We have decided not to implement our own lending iterator using GATs. Rather, we will see what the community settles on and will decide in the future whether or not to adopt it.

Row objects

We may access entire table rows by a row id:

    if let Some(row) = tables.edges().row(edge_id) {
        assert_eq!(row.id, 0);
        assert_eq!(row.left, left);
        assert_eq!(row.right, right);
        assert_eq!(row.parent, parent);
        assert_eq!(row.child, child);
    } else {
        panic!("that should have worked...");
    }

The row types are rust structures. The table data are copied into these structures, making them relatively more expensive to work with. We are looking into removing the copies and returning slices, but doing so currently impacts the design of table row iterators such as:

    for row in tables.edges_iter() {
        // there is only one row!
        assert_eq!(row.id, 0);
        assert_eq!(row.left, left);
        assert_eq!(row.right, right);
        assert_eq!(row.parent, parent);
        assert_eq!(row.child, child);
    }

These iterators are rust iterator types--they impl Iterator<Item=X>, where X is a table row type.

Checking table integrity

The data model involves lazy checking of inputs. In other words, we can add invalid row data that is not caught by the "add row" functions. We inherit this behavior from the C API.

We can check that the tables contain valid data by:

    let mut tables = tskit::TableCollection::new(100.0).unwrap();
    // Everything about this edge is wrong...
    tables.add_edge(-1.0, 110.0, 0, 1).unwrap();
    // ...and we can catch that here
    match tables.check_integrity(tskit::TableIntegrityCheckFlags::default()) {
        Ok(code) => panic!("expected Err(e) but got code: {}", code),
        // tskit::TskitError can be formatted into the same
        // error messages that tskit-c/tskit-python give.
        Err(e) => println!("{}", e),
    }

Working with tree sequences

The next several sections provide examples based on a tree sequence with two trees.

The first tree is:

     0
    +++
    | |  1
    | | +++
    2 3 4 5

The second tree is:

         0
       +-+-+
       1   |
     +-+-+ |
     2 4 5 3

Initialization from a table collection

The steps are:

  • Add rows to a table
  • Sort the table
  • Index the table
  • Create the tree sequence

For brevity, we skip careful pattern matching of return values and instead just unwrap them.

Adding rows

    use tskit::prelude::*;
    use tskit::TableCollection;
    use tskit::TableSortOptions;
    use tskit::TreeFlags;
    use tskit::TreeSequenceFlags;

    let mut tables = TableCollection::new(1000.).unwrap();
    tables
        .add_node(0, 2.0, PopulationId::NULL, IndividualId::NULL)
        .unwrap();
    tables
        .add_node(0, 1.0, PopulationId::NULL, IndividualId::NULL)
        .unwrap();
    tables
        .add_node(
            TSK_NODE_IS_SAMPLE,
            0.0,
            PopulationId::NULL,
            IndividualId::NULL,
        )
        .unwrap();
    tables
        .add_node(
            TSK_NODE_IS_SAMPLE,
            0.0,
            PopulationId::NULL,
            IndividualId::NULL,
        )
        .unwrap();
    tables
        .add_node(
            TSK_NODE_IS_SAMPLE,
            0.0,
            PopulationId::NULL,
            IndividualId::NULL,
        )
        .unwrap();
    tables
        .add_node(
            TSK_NODE_IS_SAMPLE,
            0.0,
            PopulationId::NULL,
            IndividualId::NULL,
        )
        .unwrap();
    tables.add_edge(500., 1000., 0, 1).unwrap();
    tables.add_edge(0., 500., 0, 2).unwrap();
    tables.add_edge(0., 1000., 0, 3).unwrap();
    tables.add_edge(500., 1000., 1, 2).unwrap();
    tables.add_edge(0., 1000., 1, 4).unwrap();
    tables.add_edge(0., 1000., 1, 5).unwrap();

Sorting

    tables.full_sort(TableSortOptions::default()).unwrap();

See the API docs for the details of sorting. The behavior of this function can be confusing at first. Only tables with strict sorting requirements are affected.

Indexing

    tables.build_index().unwrap();

Create the tree sequence

    let treeseq = tables.tree_sequence(TreeSequenceFlags::default()).unwrap();

Notes:

  • We could have skipped tables.build_index() and passed TreeSquenceFlags::BUILD_INDEXES instead of the default flags.
  • Creating a tree sequence from a table collection takes ownership of the table data and consumes the table collection variable. Any further attempts to manipulate the table collection variable will result in a compiler error.

Iterating over trees

To iterate over trees left-to-right:

    let mut tree_iterator = treeseq.tree_iterator(TreeFlags::default()).unwrap();

    while let Some(_tree) = tree_iterator.next() {
        // _tree is a tskit::Tree
    }

This API depends on traits most easily brought into scope via the crate prelude:

use tskit::prelude::*;

A next_back() function allows iteration to the next tree left of the current tree. We currently do not have an API expressing "build me an iterator starting from the rightmost tree". Such an thing is certainly doable.

Working with trees

Iterating over a tree sequence returns instances of tskit::Tree. This type is immutable. No access is provided to the underlying pointers.

The API provides a set of iterators over the data in a tree.

For example, to collect the siblings of each node into a vector:

    // This is an enum defining supported
    // traversal orders through a Tree.
    use tskit::NodeTraversalOrder;
    while let Some(tree) = tree_iterator.next() {
        for node in tree.traverse_nodes(NodeTraversalOrder::Preorder) {
            if let Some(parent) = tree.parent(node) {
                // Collect the siblings of node into a Vec
                // The children function returns another iterator
                let _siblings = tree
                    .children(parent)
                    .filter(|child| child != &node)
                    .collect::<Vec<_>>();
            }
        }
    }

We may do the same calculation using API elements giving &[NodeId] (slices of node ids). This method more closely matches the tskit-c API.

    while let Some(tree) = tree_iterator.next() {
        let parents = tree.parent_array();
        let rsibs = tree.right_sib_array();
        let lchildren = tree.left_child_array();
        for node in tree.traverse_nodes(NodeTraversalOrder::Preorder) {
            let mut siblings = vec![];
            assert!(!node.is_null());
            if let Some(parent) = parents.get(usize::try_from(node).unwrap()) {
                if !parent.is_null() {
                    if let Some(child) = lchildren.get(usize::try_from(*parent).unwrap()) {
                        let mut u = *child;
                        while !u.is_null() {
                            if u != node {
                                siblings.push(u);
                            }
                            if let Some(sib) = rsibs.get(usize::try_from(u).unwrap()) {
                                u = *sib;
                            }
                        }
                    }
                }
            }
        }
    }

This approach is more complex:

  • Slice element access is via usize (size_t in C/C++).
  • Row ids are i32 (tsk_id_t in tskit-c) behind the newtypes.
  • tskit implements TryFrom to help you out, forcing you to reckon with the fact that conversion from i32 to usize is fallible.

These conversion semantics require us to manually handle all possible error paths at each step.

We can have an intermediate level of complexity using getters from the tree arrays:

    while let Some(tree) = tree_iterator.next() {
        for node in tree.traverse_nodes(NodeTraversalOrder::Preorder) {
            let mut siblings = vec![];
            if let Some(parent) = tree.parent(node) {
                if let Some(child) = tree.left_child(parent) {
                    let mut u = child;
                    while !u.is_null() {
                        if u != node {
                            siblings.push(u);
                        }
                        if let Some(sib) = tree.right_sib(u) {
                            u = sib;
                        }
                    }
                }
            }
        }
    }

Iterating over edge differences

As with trees, the API provides a lending iterator over edge differences. Each step of the iterator advances to the next tree in the tree sequence. For each tree, a standard Iterator over removals and insertions is available:

    if let Ok(mut edge_diff_iterator) = treeseq.edge_differences_iter() {
        while let Some(diffs) = edge_diff_iterator.next() {
            for edge_removal in diffs.edge_removals() {
                println!("{}", edge_removal);
            }
            for edge_insertion in diffs.edge_insertions() {
                println!("{}", edge_insertion);
            }
        }
    } else {
        panic!("creating edge diffs iterator failed");
    }

Edge differences are the basis of efficient algorithms based on the incremental updating of summaries of trees. The following example co-iterates over edge differences and trees. The edge differences are used to calculate the parent array for each tree:

    let num_nodes = treeseq.nodes().num_rows().as_usize();
    // num_nodes + 1 to reflect a "virtual root" present in
    // the tree arrays
    let mut parents = vec![NodeId::NULL; num_nodes + 1];
    match treeseq.edge_differences_iter() {
        Ok(mut ediff_iter) => match treeseq.tree_iterator(0) {
            Ok(mut tree_iter) => {
                while let Some(diffs) = ediff_iter.next() {
                    let tree = tree_iter.next().unwrap();
                    for edge_out in diffs.edge_removals() {
                        let c = edge_out.child();
                        parents[c.as_usize()] = NodeId::NULL;
                    }
                    for edge_in in diffs.edge_insertions() {
                        let c = edge_in.child();
                        parents[c.as_usize()] = edge_in.parent();
                    }
                    assert_eq!(tree.parent_array(), &parents);
                }
            }
            Err(e) => panic!("error creating tree iter: {:?}", e),
        },
        Err(e) => panic!("error creating edge diff iter: {:?}", e),
    }

Miscellaneous operations

Writing to a file

treeseq.dump("file.trees", tskit::TableOutputOptions::default()).unwrap();

Loading from a file

let treeseq = tskit::TreeSequence::load("file.trees").unwrap();

Get a deep copy of the tables

Get a copy of the table collection in the tree sequence:

let tables = treeseq.dump_tables.unwrap();

This function can error because the tskit-c functions to copy ,may return an error code.

This function is not necessary to access the tables. See below.

Read-only table access

A TreeSequence has access to the tables. For example:

for _edge in treeseq.edges_iter() {
}

Metadata

Tables may contain additional information about rows that is not part of the data model. This metadata is optional. Tables are not required to have metadata. Tables with metadata do not require that every row has metadata.

The next sections showcase the metadata API.

Defining metadata types in rust

A key feature of the API is that metadata is specified on a per-table basis. In other words, a type to be used as node metadata implements the tskit::metadata::NodeMetadata trait.

Using the tskit cargo feature derive, we can use procedural macros to define metadata types. Here, we define a metadata type for a mutation table:

    #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
    #[serializer("serde_json")]
    struct MutationMetadata {
        effect_size: f64,
        dominance: f64,
    }

We require that you also manually specify the serde derive macros because the metadata API itself does not depend on serde. Rather, it expects raw bytes and serde happens to be a good way to get them from your data types.

The derive macro also enforces some helpful behavior at compile time. You will get a compile-time error if you try to derive two different metadata types for the same rust type. The error is due to conflicting implementations for a supertrait.

Metadata and tables

Let us create a table and add a row with our mutation metadata:

    let mut tables = tskit::TableCollection::new(50.0).unwrap();

    let md = MutationMetadata {
        effect_size: 1e-3,
        dominance: 1.0,
    };

    let mut_id_0 = tables
        .add_mutation_with_metadata(
            0,    // site id
            0,    // node id
            -1,   // mutation parent id
            0.0,  // time
            None, // derived state is Option<&[u8]>
            &md,  // metadata for this row
        )
        .unwrap();

Meta data is optional on a per-row basis:

    let mut_id_1 = tables
        .add_mutation(
            0,    // site id
            0,    // node id
            -1,   // mutation parent id
            0.0,  // time
            None, // derived state is Option<&[u8]>
        )
        .unwrap();

We can confirm that we have one row with, and one without, metadata:

    assert_eq!(
        tables
            .mutations_iter()
            .filter(|m| m.metadata.is_some())
            .count(),
        1
    );
    assert_eq!(
        tables
            .mutations_iter()
            .filter(|m| m.metadata.is_none())
            .count(),
        1
    );

Fetching our metadata from the table requires specifying the metadata type. The result of a metadata retrieval is Option<Result, TskitError>. The None variant occurs if a row does not have metadata or if a row id does not exist. The error state occurs if decoding raw bytes into the metadata type fails. The details of the error variant are here. The reason why the error type holds Box<dyn Error> is that the API is very general. We assume nothing about the API used to encode/decode metadata. Therefore, the error could be anything.

    let fetched_md = match tables.mutations().metadata::<MutationMetadata>(mut_id_0) {
        Some(Ok(m)) => m,
        Some(Err(e)) => panic!("metadata decoding failed: {:?}", e),
        None => panic!(
            "hmmm...row {} should have been a valid row with metadata...",
            mut_id_0
        ),
    };

    assert_eq!(md.effect_size, fetched_md.effect_size);
    assert_eq!(md.dominance, fetched_md.dominance);
    // There is no metadata at row 1, so
    // you get None back
    assert!(tables
        .mutations()
        .metadata::<MutationMetadata>(mut_id_1)
        .is_none());

    // There is also no metadata at row 2,
    // because that row does not exist, so
    // you get None back
    assert!(tables
        .mutations()
        .metadata::<MutationMetadata>(2.into())
        .is_none());

Metadata schema

For useful data interchange with tskit-python, we need to define metadata schema.

There are currently several points slowing down a rust API for schema:

  • It is not clear which serde formats are compatible with metadata on the Python side.
  • Experiments have shown that serde_json works with tskit-python.
    • Ideally, we would also like a binary format compatible with the Python struct module.
  • However, we have not found a solution eliminating the need to manually write the schema as a string and add it to the tables. Various crates to generate JSON schema from rust structs return schema that are over-specified and fail to validate in tskit-python.
  • We also have the problem that we will need to add some Python to our CI to prove to ourselves that some reasonable tests can pass.

Advanced topics

Bulk decoding of metadata

To populate a Vec with decoded metadata and accounting for some rows not having metadata:

  • Realize that the decode methods of the MetadataRoundtrip trait are associated functions.
  • We can use a lending iterator over rows to avoid unnecessary copying.

Therefore:

    let mut mutation_row_lending_iterator = tables.mutations().lending_iter();
    let mut decoded_md = vec![];
    while let Some(row_view) = mutation_row_lending_iterator.next() {
        match row_view.metadata {
            Some(slice) => decoded_md.push(Some(MutationMetadata::decode(slice).unwrap())),
            None => decoded_md.push(None),
        }
    }

To filter out rows without metadata:

    let mut mutation_row_lending_iterator = tables.mutations().lending_iter();
    let mut decoded_md = vec![];
    while let Some(row_view) = mutation_row_lending_iterator
        .next()
        .filter(|rv| rv.metadata.is_some())
    {
        decoded_md.push((
            row_view.id,
            // The unwrap will never panic because of our filter
            MutationMetadata::decode(row_view.metadata.unwrap()).unwrap(),
        ));
    }

The first method gives `Vec<Option<M

Error handling

The error type is tskit::error::TskitError.

This type implements Display and may thus be printed via:

let x = match operation() {
    Ok(y) => y,
    Err(e) => panic("{}", e);
};

The enum variant TskitError::ErrorCode represents integer return values from tskit-c. When printed via Display, the detailed error message is fetched. When printed via Debug, the numerical error code is shown.

Example programs

Haploid Wright-Fisher simulation

The following code simulates a haploid Wright-Fisher model. The code is a reimplementation of an example program distributed with tskit-c.

In addition to tskit, the example uses:

  • rand for random number generation.
  • anyhow for error propagation.
fn simulate(
    seed: u64,
    popsize: usize,
    num_generations: i32,
    simplify_interval: i32,
    update_bookmark: bool,
) -> Result<tskit::TreeSequence> {
    if popsize == 0 {
        return Err(anyhow::Error::msg("popsize must be > 0"));
    }
    if num_generations == 0 {
        return Err(anyhow::Error::msg("num_generations must be > 0"));
    }
    if simplify_interval == 0 {
        return Err(anyhow::Error::msg("simplify_interval must be > 0"));
    }
    let mut tables = tskit::TableCollection::new(1.0)?;

    // create parental nodes
    let mut parents_and_children = {
        let mut temp = vec![];
        let parental_time = f64::from(num_generations);
        for _ in 0..popsize {
            let node = tables.add_node(0, parental_time, -1, -1)?;
            temp.push(node);
        }
        temp
    };

    // allocate space for offspring nodes
    parents_and_children.resize(2 * parents_and_children.len(), tskit::NodeId::NULL);

    // Construct non-overlapping mutable slices into our vector.
    let (mut parents, mut children) = parents_and_children.split_at_mut(popsize);

    let parent_picker = rand::distributions::Uniform::new(0, popsize);
    let breakpoint_generator = rand::distributions::Uniform::new(0.0, 1.0);
    let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
    let mut bookmark = tskit::types::Bookmark::default();

    for birth_time in (0..num_generations).rev() {
        for c in children.iter_mut() {
            let bt = f64::from(birth_time);
            let child = tables.add_node(0, bt, -1, -1)?;
            let left_parent = parents
                .get(parent_picker.sample(&mut rng))
                .ok_or_else(|| anyhow::Error::msg("invalid left_parent index"))?;
            let right_parent = parents
                .get(parent_picker.sample(&mut rng))
                .ok_or_else(|| anyhow::Error::msg("invalid right_parent index"))?;
            let breakpoint = breakpoint_generator.sample(&mut rng);
            tables.add_edge(0., breakpoint, *left_parent, child)?;
            tables.add_edge(breakpoint, 1.0, *right_parent, child)?;
            *c = child;
        }

        if birth_time % simplify_interval == 0 {
            tables.sort(&bookmark, tskit::TableSortOptions::default())?;
            if update_bookmark {
                rotate_edges(&bookmark, &mut tables);
            }
            if let Some(idmap) =
                tables.simplify(children, tskit::SimplificationOptions::default(), true)?
            {
                // remap child nodes
                for o in children.iter_mut() {
                    *o = idmap[usize::try_from(*o)?];
                }
            }
            if update_bookmark {
                bookmark.set_edges(tables.edges().num_rows());
            }
        }
        std::mem::swap(&mut parents, &mut children);
    }

    tables.build_index()?;
    let treeseq = tables.tree_sequence(tskit::TreeSequenceFlags::default())?;

    Ok(treeseq)
}

The tskit prelude

The contents of the crate prelude are:

//! Export commonly-use types and traits

pub use crate::tsk_flags_t;
pub use crate::TSK_NODE_IS_SAMPLE;
pub use streaming_iterator::DoubleEndedStreamingIterator;
pub use streaming_iterator::StreamingIterator;
pub use {
    crate::EdgeId, crate::IndividualId, crate::Location, crate::MigrationId, crate::MutationId,
    crate::NodeId, crate::PopulationId, crate::Position, crate::RawFlags, crate::SiteId,
    crate::SizeType, crate::Time,
};

Changelog

All notable changes to this project will be documented in this file.

[0.14.1] - 2023-04-16

Bug Fixes

  • Tree now tied to TreeSequence lifetime (#497)

Miscellaneous Tasks

  • Update delegate requirement from 0.8.0 to 0.9.0 (#494)

[0.14.0] - 2023-03-17

Documentation

  • Book section on node defaults (#491)

Features

  • Allow use of bookmark in examples/haploid_wright_fisher (#479)
  • Impl Default for row id types (#484)
  • Impl Default for Bookmark (#486)
  • Implement Builder pattern for flags (#487)
  • Support adding nodes with defaults (#488)

Miscellaneous Tasks

  • [breaking] Bump MSRV to 1.60.0 (#478)

Refactor

  • Remove internal impl Deref/DerefMut for edge diff iterator. (#482)
  • Mod table_view is no longer pub (#483)
  • [breaking] TableCollection::simplify now takes &[NodeId] (#485)
  • Replace macros with fn for adding node table rows (#489)
  • Manually impl Default for NodeDefaultsWithMetadata (#490)

[0.12.0] - 2022-12-23

Documentation

  • Book section on lending row iterators (#406)
  • Advanced metadata topics in book (#407)
  • Book chapter on edge differences (#417)

Features

  • TreeSequence::edge_differences_iter (#410)
  • Add convience functions to get usize from row ids. (#413)
  • Tree::total_branch_length now works. (#429)
  • Add getters/setters for Bookmark. (#437)

Miscellaneous Tasks

  • Update bindgen requirement from 0.61.0 to 0.63.0 (#415)

Refactor

  • [breaking] Replace Deref with delegation of TableViews API (#409)
  • [breaking] Remove TskitTypeAccess trait (#411)
  • Allow TreeSequence::edge_differences_iter to Err. (#412)
  • [breaking] Usize to SizeType conversion is now fallible. (#419)
  • Macros only use newtype public API. (#420)
  • Move newtypes into module (#421)
  • The raw_metadata fn for tables now takes Into. (#425)
  • Add low-level table wrappers to sys (#426)
  • Generalize slice building in sys (#427)
  • Remove many unsafe calls from tree_interface.rs (#428)
  • Add sys::LLTreeSeq. (#430)
  • Sys module no longer depends on TskitError (#431)
  • Move Drop details for TreeSequence to sys. (#432)
  • Add low level owning tables to sys (#433)
  • Tidy up table definitions (#434)

Styling

  • [breaking] Rename all OwnedX tables to OwningX. (#408)
  • Replace column access macro with generic fn (#422)
  • Generic fns for ragged column access (#423)
  • Use fn to get array slices from Trees. (#424)

[0.12.0-alpha.1] - 2022-11-09

Bug Fixes

  • [breaking] MetadataError::RoundtripError now requires Send + Sync (#396)

Documentation

  • Add haploid WF example to book (#397)

Features

  • Add examples/haploid_wright_fisher.rs (#394)
  • Add lending iterators over table row "views" (#398)
  • Impl PartialEq for table row views. (#400)
  • Add ProvenenceTableRowView (#401)
  • Add row_view() for tables (#402)
  • Add row_view() for tables (#402)
  • Column slice getters for tables (#404)

Refactor

  • [breaking] Return &str from provenance table getters (#403)
  • Deprecate NodeTable::flags_array_mut and NodeTable::time_array_mut (#405)

[0.12.0-alpha.0] - 2022-11-06

Documentation

  • Add book sections on table data access (#379)
  • Remove chapter number from book intro (#380)
  • Start book sections on tree sequences (#381)
  • Book section on the tree API. (#382)
  • Add book section on misc. ops on treeseqs (#385)
  • Appendix material for book (#386)
  • Rewrite book intro (#389)
  • Show prelude contents as book appendix (#390)
  • Book sections on metadata (#391)
  • Book section on error handling (#393)

Refactor

  • [breaking] Improve Tree iterator ergonomics (#384)
  • [breaking] Improve Tree interface ergonomics (#388)

[0.11.1] - 2022-11-04

Documentation

Refactor

  • Apply functional style for NodeTable fns (#377)

[0.11.0] - 2022-11-04

Bug Fixes

  • Derive Debug for all table row types (#361)
  • [breaking] Update generic bounds on metadata getters (#370)
  • Impl DerefMut for "owned" tables. (#371)

Documentation

  • Add migration guide (#376)

Features

  • [breaking] Add TskitError::LibraryError (#342)

Refactor

  • Streamline code for generating table rows. (#340)
  • Replace unwrap with expect in _macros.rs (#341)
  • Replace unwrap with errors in table_collection.rs (#343)
  • Remove unwraps in trees source files. (#344)
  • Streamline building individual table rows (#345)
  • Remove unwraps from node_table.rs (#346)
  • Unwrap to expect in error.rs (#347)
  • Remove type info from Display for newtypes. (#349)
  • [breaking] Return None for out-of-range row indexes. (#350)
  • [breaking] Return None for out-of-range row indexes, part II. (#351)
  • [breaking] Return None for out-of-range row indexes, part III. (#355)
  • Improve safety of tsk array access (#362)
  • Tree now stores tsk_tree_t in MBox. (#367)
  • [breaking] Provenance table getters now return Option (#369)
  • [breaking] Remove lifetime annotation from table types (#373)

Styling

  • Fix lints from rust 1.65.0 (#374)
  • Fix lints from nightly (#375)

Testing

  • Rewrite table API tests (#356)
  • Add doc tests for node table (#359)

[0.10.0] - 2022-10-28

Documentation

  • Display feature flags in docs.rs (#338)

Miscellaneous Tasks

  • Set msrv to 1.57.0 (result of cargo msrv) (#322)
  • Fix tag_pattern in cliff.toml (#330)
  • Update github actions (#332)
  • Add github actions to monthly dependabot (#333)
  • Update bindgen requirement from 0.60.1 to 0.61.0 (#336)
  • Bump styfle/cancel-workflow-action from 0.6.0 to 0.11.0 (#334)

Performance

  • Use Bookmark in forward simulation example (#318)

Refactor

  • Streamline internal macros for adding nodes (#309)
  • Remove internal ffi module (#310)
  • Panics compatible with earlier rustc (#311)
  • Remove type name from SizeType Display output. (#315)
  • [breaking] TableCollection::simplify returns Option<&[NodeId]> (#316)
  • [breaking] Conversion from row id to usize is now fallible. (#319)
  • Use clap::Parser in example program. (#320)
  • Remove pontential panic! when instantiating table rows. (#329)
  • Examples/tree_traversals.rs now uses clap derive (#337)

Styling

  • Remove unneeded ( ) from match. (#323)
  • Use auto-deref for pointer deref. (#325)
  • Fix +nightly clippy lints for all targets/features. (#326)

Testing

  • Improve test work flow (#331)

[0.10.0-alpha.1] - 2022-07-31

Bug Fixes

  • Replace crate with $crate in macros (#287)

Features

  • Add tskit::OwnedMutationTable (#281)
  • Add tskit::OwnedSiteTable (#283)
  • Add tskit::OwnedIndividualTable (#284)
  • Add tskit::OwnedMigrationTable (#285)
  • Add tskit::provenance::OwnedProvenanceTable (#286)
  • Set tables in collection from owned tables. (#288)
  • [breaking] Impl Send/Sync for TreeSequence (#297)

Miscellaneous Tasks

  • Update to C API 1.1.1 (#304)

Performance

  • [breaking] Implement slice access to individual data (#292)
  • [breaking] Eliminate several copies from C to rust (#293)

Refactor

  • Deduplicate code for adding node table rows. (#278)
  • Use macros to implement adding table rows. (#279)
  • Add new macro to build owned tables (#282)
  • Separate tree ownership from API. (#300)
  • Bring back generic syntax for adding node table rows. (#302)
  • [breaking] Display for new types now omits type name. (#305)
  • Remove MBox for tsk_treeseq_t storage (#306)

[0.10.0-alpha.0] - 2022-07-19

Bug Fixes

  • Fix memory leak in TreeSequence::simplify (#274)
  • Fix leak in TableCollection::deepcopy (#275)
  • Fix leak in TreeSequence::dump_tables (#276)

Documentation

  • Fix warnings from "cargo doc" (#255)

Features

  • Make load from file generic over AsRef (#254)
  • Add tskit::OwnedPopulationTable (#267)
  • Add tskit::OwnedEdgeTable (#268)
  • Add tskit::OwnedNodeTable (#273)

Miscellaneous Tasks

  • Add cliff.toml (config file for git-cliff) (#257)
  • Prefix "chore: " to dependabot.yml (#258)
  • Use cargo-hack to streamline CI. (#259)
  • Add cargo-valgrind CI workflow (#277)

Refactor

  • Macro metadata_to_vector intputs changed. (#263)
  • Add macros to implement owned tables. (#269)

Testing

  • Add another experimental trait idea for metadata. (#261)
  • Redo doctest: TableCollection::new_from_file (#272)

2022-06-20, Version 0.9.0

Commits

  • [068fdccdea] Bump version to 0.9.0 (Kevin R. Thornton)
  • [d700af7294] doc sorting requirements (#243) (Kevin R. Thornton)
  • [6114d2bd65] Change ffi to private visibility. (#241) (Kevin R. Thornton)
  • [b53105112a] Add new_sample and is_sample to NodeFlags public API. (#238) (Kevin R. Thornton)

2022-05-26, Version 0.8.1

Commits

  • [bd72b689ac] Bump version to 0.8.1 (molpopgen)
  • [b0abdfad5c] impl TryFrom for TreeSequence (#236) (Kevin R. Thornton)
  • [71e927a9bb] TreeSequence::new now takes ownership of the TableCollection. (#235) (Kevin R. Thornton)

2022-05-24, Version 0.8.0

Commits

  • [e463302ced] Bump package version to 0.8.0 (molpopgen)
  • [deffc58127] Replace From for usize with TryFrom. (#234) (Kevin R. Thornton)
  • [a7c04213a1] update to CAPI 1.0.0 (#229) (Kevin R. Thornton)
  • [7c0b6169a3] Add CI for 32 bit Linux builds (#232) (Kevin R. Thornton)
  • [7458d85163] Invoke rust dependency caching after setting up rustc (#233) (Kevin R. Thornton)
  • [203a43a0e8] Only run CI for rust/stable. (#231) (Kevin R. Thornton)
  • [fb2128c0a5] Add dependency caching for CI. (#230) (Kevin R. Thornton)
  • [aff053c786] Improvements to handling of tsk_flags_t: (#227) (Kevin R. Thornton)
  • [5f558cb051] Audit pedantic lints re: integer casts (#223) (Kevin R. Thornton)
  • [609546185c] Fix several "pedantic" lints from clippy: (#222) (Kevin R. Thornton)

2022-03-24, Version 0.7.0

Commits

  • [8427c2fea7] Change order of generics to match argument order for all "add" (#221) (Kevin R. Thornton)
  • [6ef0406731] Fix typo in docstring for TableCollection::full_sort. (#219) (Kevin R. Thornton)
  • [a1a7e12208] Add TableCollection::topological_sort_individuals. (#218) (Kevin R. Thornton)
  • [4c17ca62a2] Update clap requirement from ~3.0.0 to ~3.1.3 (#215) (dependabot[bot])
  • [f40562a56a] Fix lint found by clippy. (#216) (Kevin R. Thornton)
  • [4efddb6f5d] Update clap requirement from ~2.34.0 to ~3.0.0 (#214) (dependabot[bot])

2021-12-15, Version 0.7.0-alpha.1

Fixes various minor issues with the first alpha.

Commits

  • [44965969c0] Bump version to 0.7.0.alpha.1 (molpopgen)
  • [5d6a289490] Fix issues with Time, Position, Location newtypes (#212) (Kevin R. Thornton)
  • [99930e8bb0] Add explicit tests of tree roots. (#211) (Kevin R. Thornton)

2021-12-15, Version 0.7.0-alpha.0

Breaking changes:

  • Update to tskit C API 0.99.15.
  • Add newtypes for Time, Position, and Location.
  • Add newtype SizeType, which wraps tsk_size_t.
  • tsk_id_t and tsk_size_t removed from library prelude.
  • The Provenance trait is removed and all "provenance stuff" is now a first-class table API supported by the TableAccess trait.

New features:

  • Update to tskit C API 0.99.15.
  • tskit C structs are now managed by MBox.
  • Support for virtual tree roots.
  • Add postorder node iterator for Tree

Commits

  • [272d0736e2] bump package version to 0.7.0-alpha.0 (molpopgen)
  • [b5946a4cc4] Redesign provenance feature: (#207) (Kevin R. Thornton)
  • [5699aab37f] Change return time of Tree::num_tracked_samples. (#206) (Kevin R. Thornton)
  • [2391c9e587] Add support for virtual roots (#205) (Kevin R. Thornton)
  • [4cf3496d82] Fix metadata links in lib.rs (#204) (Kevin R. Thornton)
  • [d7f70a7cfd] Use MBox to manage lifetimes of tskit C structs. (#203) (Kevin R. Thornton)
  • [034204ed88] Add newtypes for Time, Position, and for Location. (#199) (Kevin R. Thornton)
  • [4ba44ef0c5] Bump editions to 2021. (#198) (Kevin R. Thornton)
  • [c4cc6e875a] Add tskit::SizeType (#192) (Kevin R. Thornton)
  • [8429cc37d2] Use type erasure for all table row iteration functions. (#189) (Kevin R. Thornton)
  • [f4cb6350e0] Add TableCollection::check_integrity. (#188) (Kevin R. Thornton)
  • [562014b04f] Add preorder node traversal using latest tskit C API. (#186) (Kevin R. Thornton)
  • [db8550f6d7] Replace Box with malloc'd raw pointer for tskit types. (#184) (Kevin R. Thornton)
  • [90a2d84a9d] Update to C API 0.99.15 (breaking) (#183) (Kevin R. Thornton)
  • [58e7c5ed73] Update clap requirement from ~2.33.3 to ~2.34.0 (#176) (dependabot[bot])

2021-11-29, Version 0.6.1

Commits

  • [3eadcb161e] Replace chrono with humantime in provenance doc tests. (#175) (Kevin R. Thornton)
  • [4def777ddf] Add security audit work flow. (#172) (Kevin R. Thornton)

2021-11-23, Version 0.6.0

Commits

  • [b07c035077] bump version to 0.6.0 (molpopgen)
  • [8bb08be83d] Replace chrono dependency with humantime to avoid RUSTSEC-2020-0071 and RUSTSEC-2020-0159 (#171) (Momo Langenstein)
  • [896b5891e0] Implement Display for Id newtypes. (#168) (Kevin R. Thornton)
  • [d2c6383ae9] Update to C API 0.99.14 (#165) (Kevin R. Thornton)

2021-09-03, Version 0.5.0

Commits

Derive macros for table metadata.

  • [41633b60ed] Add metadata type registration via derive macros. (#163) (Kevin R. Thornton)

2021-08-31, Version 0.4.0

The theme of this release is "type safety". This release breaks API due to use of newtypes for row IDs and new metadata marker traits.

Commits

  • [a7d78b16aa] Bump version to 0.4.0 (molpopogen)
  • [b5e2c265dd] Add newtype row IDs to prelude. (#161) (Kevin R. Thornton)
  • [3dbe8c4b5e] Fix implementation of tree preorder stacking. (#160) (Kevin R. Thornton)
  • [fafd457033] Refactor metadata encoding: (#158) (Kevin R. Thornton)
  • [258b4ee5f0] replace IdIsNull trait with associated fn (#156) (Kevin R. Thornton)
  • [9987fc0472] Release build optimizations: (#155) (Kevin R. Thornton)
  • [58ac4a92d7] Refine what "NULL"-ness means for an Id type: (#154) (Kevin R. Thornton)
  • [ad4975fc70] Update bindgen requirement from 0.58.1 to 0.59.1 (#152) (dependabot[bot])
  • [8a553eaa80] fix clippy warnings that showed up in rust 1.54 (#153) (Kevin R. Thornton)
  • [bef4bb6c7a] Allow empty provenance records. (#151) (Kevin R. Thornton)
  • [86df702040] Improve test coverage of "adding rows to table collections" (#143): (Kevin R. Thornton)
  • [036680050b] * Update trees API to use NodeId (#145) (Kevin R. Thornton)
  • [7a716bbe76] Add additional member functions to TableCollection to (#141) (Kevin R. Thornton)
  • [baf6f17430] Fix Into< > type for TableCollection::add_individual_* (#142) (Kevin R. Thornton)
  • [689938eee7] fix link from ProvenanceId to ProvenanceTable in docs (#140) (Kevin R. Thornton)
  • [1203a5d523] strong id type cleanup (#139) (Kevin R. Thornton)
  • [6bae100e61] Add EdgeId (#138) (Kevin R. Thornton)
  • [9baff077b5] Add MigrationId and ProvenanceId (#137) (Kevin R. Thornton)
  • [7713ef669e] Add SiteId and MutationId (#136) (Kevin R. Thornton)
  • [de006a1f0e] Add PopulationId (#135) (Kevin R. Thornton)
  • [630ccd5f5e] Add IndividualId. (#133) (Kevin R. Thornton)
  • [8bf7d4b954] NodeTable::metadata now uses Into. (#134) (Kevin R. Thornton)
  • [3ac3d50f7b] * Establish a pattern for stronger ID types (#129) (Kevin R. Thornton)
  • [e19095d9a9] clippy vs bindgen (#130) (Kevin R. Thornton)
  • [d9abda5bb6] Change links in Cargo.toml to tskit-dev (#127) (Kevin R. Thornton)

2021-05-19, Version 0.3.0

This release has a few API changes, etc., that streamline the public interface.

The C code is updated to 0.99.12.

Commits

  • [ec0adc36ae] streamline API for creating table row objects (#126) (Kevin R. Thornton)
  • [edd36fa886] Add mutable access to NodeTable flag and time arrays. (#125) (Kevin R. Thornton)
  • [634df568af] Fix docs for Tree::total_branch_length. Fixes #114. (#124) (Kevin R. Thornton)
  • [202bcded3f] Give access to TableCollection edge indexes. (#123) (Kevin R. Thornton)
  • [3888fe5f10] update C code to version 0.99.12 (#122) (Kevin R. Thornton)
  • [6fa2057af6] Add prelude.rs (#119) (Kevin R. Thornton)
  • [a592126594] replace crate with $crate for all macros (#113) (Kevin R. Thornton)
  • [b618d75e5b] Add macro to aid returning from MetadataRoundtrip functions. (#110) (Kevin R. Thornton)
  • [79adaa1a4a] Add macro to remove code duplication (#111) (Kevin R. Thornton)
  • [159bbfe926] Add convenience macro to use all public traits. (#106) (Kevin R. Thornton)
  • [e912f7b2c8] Bump bindgen version. (#109) (Kevin R. Thornton)
  • [15fae9327b] Update clap requirement from ~2.27.0 to ~2.33.3 (#107) (dependabot[bot])
  • [55f18a1a4f] Refactor MetadataError: (#105) (Kevin R. Thornton)
  • [fbce6ca894] API fixes. (#104) (Kevin R. Thornton)

2021-04-27, Version 0.2.2

This release fixes issues with the lifetime relationships of Tree and the raw C arrays. Technically, this is an API change, but only with respect to return type and the integer type used to index that return type.

Commits

  • [89d234f674] Replace WrappedTskArray with idiomatic slices. Closes #99 (#101) (Kevin R. Thornton)
  • [ed8329b89e] Completely hide NodeIterator from public name spaces. (#100) (Kevin R. Thornton)

2021-04-26, Version 0.2.1

  • [ff1c7ced82] Add Provenance trait as optional feature (#98). (Kevin R. Thornton)
  • [0fdd30067e] Fix error in transmitting nodes. Closes #96 (#97) (Kevin R. Thornton)
  • [299c83ae35] Update repo/homepage name in Cargo.toml (molpopgen)
  • [35ecf29933] Remove re-exports that are part of existing bitflags types. (#95) (Kevin R. Thornton)

2021-04-21, Version 0.2.0

Commits

  • [daffeda43e] bump version to 0.2.0 (molpopgen)
  • [0dad5e39d2] Update docs (#93) (Kevin R. Thornton)
  • [f905b9c384] Add reverse tree iteration. (#92) (Kevin R. Thornton)
  • [a1a033f901] Add development documentation. (#91) (Kevin R. Thornton)
  • [ce3d8f4849] Refine forward sim example (#90) (Kevin R. Thornton)
  • [b1022e1f59] Refactor how tskit flags are handled. (#89) (Kevin R. Thornton)
  • [56e43cb0ff] Add forward simulation example. Closes #71. (#86) (Kevin R. Thornton)
  • [5530d7b234] Refactor all examples into examples/ (#85) (Kevin R. Thornton)
  • [651e4edcac] Collect public traits in traits.rs. Closes #81. (#84) (Kevin R. Thornton)
  • [11ad49f0e1] Remove mention of use NodeIterator. Closes #68. (#83) (Kevin R. Thornton)
  • [df28231d9a] Add test fixtures module. Closes #73 (#80) (Kevin R. Thornton)
  • [3a8a82637e] Rename Tree::nodes to Tree::traverse_nodes. (#79) (Kevin R. Thornton)
  • [4e7af98ac5] Update GitHub actions. (#78) (Kevin R. Thornton)
  • [43e14bb8a6] Add NodeListGenerator trait bound to TableAccess. Closes #67. (#75) (Kevin R. Thornton)
  • [ae203769f4] add id field to table row types. Closes #76 (#77) (Kevin R. Thornton)
  • [346c804be8] Make tree arrays public. Closes #70 (#74) (Kevin R. Thornton)
  • [a2dfcfe8f6] Add TableCollection and TreeSequence simplification (#64) (Kevin R. Thornton)
  • [c62ccd21aa] Add TableAccess trait and implement for TableCollection and TreeSequence. (#66) (Kevin R. Thornton)
  • [167209466a] Remove duplication of entire table collection when creating a tree sequence. (#65) (Kevin R. Thornton)
  • [df258ad8d1] Fix useless conversion in lib::c_api_version (#63) (Kevin R. Thornton)
  • [fcca8bb682] remove unnecessary function from util (#62) (Kevin R. Thornton)
  • [0ee9f86db4] Merge pull request #61 from molpopgen/add_individual_and_migration_tables (Kevin R. Thornton)
  • [8f072c47d1] Add migrations and individuals tables. (molpopogen)
  • [b2c891300b] Merge pull request #60 from molpopgen/tskit_C_version (Kevin R. Thornton)
  • [f48b2fed57] Add functions to return C API version info (molpopogen)
  • [87593d4bdc] Merge pull request #59 from molpopgen/add_test_metadata_some_columns_only (Kevin R. Thornton)
  • [5ab8e8c9d0] Add test of metadata decoding when not all rows have metadata. (molpopogen)
  • [adcc321cc8] Merge pull request #58 from molpopgen/table_row_access (Kevin R. Thornton)
  • [860a647bf3] * Add ::row for all tables (molpopgen)
  • [6b0a77e106] Add util.rs (molpopgen)
  • [6b9631e1f1] Merge pull request #57 from molpopgen/unify_table_iteration (Kevin R. Thornton)
  • [3d79f8bbfa] Add ability to iterate tables from a TableCollection: (molpopogen)
  • [392ce57e0a] Merge pull request #52 from molpopgen/table_iteration (Kevin R. Thornton)
  • [2fe5912c34] Add table iteration w/optional handling of metadata. (molpopgen)
  • [9a59a148f7] Merge pull request #55 from molpopgen/wrapped_tsk_array_iter_adapter (Kevin R. Thornton)
  • [84b0b496c1] Replace built-in iteration with iterator adapter for WrappedTskArray (molpopgen)
  • [18234998ea] Merge pull request #56 from molpopgen/metadata_macro_fixes (Kevin R. Thornton)
  • [49bbd0f2f4] metadata_to_vector macro is now a simple return (no ? operator). (molpopgen)
  • [6e293acf14] Remove unused paramter to metadata_to_vector. Fixes #54. (molpopgen)
  • [45347a576c] Change log for 0.1.2 (molpopgen)

2021-04-12, Version 0.1.2

Commits

  • [f475a5c624] Bump version to 0.1.2 (molpopgen)
  • [f1887d55e0] Merge pull request #46 from molpopgen/add_important_missing_functions (Kevin R. Thornton)
  • [f5598256dd] Merge pull request #47 from molpopgen/add_changelog (Kevin R. Thornton)
  • [fb85ba9606] Add Tree::num_tracked_samples and Tree::kc_distance. (molpopogen)
  • [08febd5062] Add change log (molpopogen)
  • [e6495a959a] Merge pull request #51 from molpopgen/test_beta_instead_of_nightly (Kevin R. Thornton)
  • [fa7cca91a5] Test beta instead of nightly. (molpopgen)
  • [bd7185eef2] Merge pull request #45 from molpopgen/documentation (Kevin R. Thornton)
  • [2400906762] Add documentation related to Tree/TreeSequence (molpopogen)
  • [550aa76597] Fix typo in function name. (molpopogen)
  • [56eaf875b6] Merge pull request #44 from molpopgen/fix_clippy_warnings (Kevin R. Thornton)
  • [20ffe557ca] Fix warnings from clippy (molpopogen)
  • [0bf1dada4d] Merge pull request #43 from molpopgen/kc_distance (Kevin R. Thornton)
  • [2e8b4c1cfc] Add TreeSequence::kc_distance (molpopogen)
  • [efe5ce2b0b] Merge pull request #42 from molpopgen/fix_tskit_array_access_macro (Kevin R. Thornton)
  • [82bb6ec6a1] Modify unsafe_tsk_column_access to support idiomatic fall-through. (molpopogen)
  • [40e898910d] Merge pull request #41 from molpopgen/sample_list_traversal (Kevin R. Thornton)
  • [cb0e9a4355] Add support for samples iteration: (molpopogen)
  • [f1acfb06f2] Merge pull request #40 from molpopgen/more_node_iteration (Kevin R. Thornton)
  • [22684248d1] * Rename NodeIteration to NodeIterator (molpopogen)
  • [faa105d577] Merge pull request #38 from molpopgen/Tree_API (Kevin R. Thornton)
  • [896f921e91] Several changes to Tree and TreeSequence: (molpopogen)
  • [683ba84b10] Merge pull request #39 from molpopgen/ffi_array_wrapper (Kevin R. Thornton)
  • [dd3df03ca3] Add non-owning wrappers to C arrays. (molpopogen)
  • [85cc4a591f] Merge pull request #37 from molpopgen/add_tree (Kevin R. Thornton)
  • [be97490641] Add minimial Tree interface. (molpopgen)
  • [4b6257ed2c] Merge pull request #36 from molpopgen/streamline_macros (Kevin R. Thornton)
  • [238f652d24] Remove macro redundancy. (molpopgen)
  • [85505887e1] Merge pull request #35 from molpopgen/refine_ffi (Kevin R. Thornton)
  • [2cb4993f07] Refactor the ffi module: (molpopgen)
  • [1b9a054ff0] Merge pull request #34 from molpopgen/improve_error_handling_macro (Kevin R. Thornton)
  • [20a5486132] Add overload of handle_tsk_return_value. (molpopgen)
  • [eae11b4c8a] Merge pull request #33 from molpopgen/add_tree_sequence (Kevin R. Thornton)
  • [a57b947352] Add TskitConsumingType, macro to build one, and convert TreeSequence (molpopgen)
  • [dcb5a02f3c] change dependabot to monthly (molpopgen)
  • [4eb407ac61] Merge pull request #31 from molpopgen/dependabot/add-v2-config-file (Kevin R. Thornton)
  • [a759e0441b] Merge pull request #32 from molpopgen/dependabot/cargo/bindgen-0.57.0 (dependabot-preview[bot])
  • [fb2a56c868] Update bindgen requirement from 0.56.0 to 0.57.0 (dependabot-preview[bot])
  • [a8a0b1aabf] Create Dependabot config file (dependabot-preview[bot])

2021-03-26, Version 0.1.1

Commits

  • [e9d73e4912] Fix crate name in README.md (molpopogen)
  • [28deef7d04] update README (molpopogen)
  • [ef79a446c1] Bump version to 0.1.1 (molpopogen)
  • [d3c31fa0fc] Merge pull request #30 from molpopgen/fix_warnings (Kevin R. Thornton)
  • [bedfa02f76] Merge pull request #29 from molpopgen/metadata_docs (Kevin R. Thornton)
  • [f6f3304d75] Remove Drop constraint on TskitType. (molpopogen)
  • [d8873a7941] Quick fix regarding documenting metadata. Closes #25 (molpopogen)
  • [4518db4946] Merge pull request #28 from molpopgen/update_to_tskit_0_3_5 (Kevin R. Thornton)
  • [80893566ed] Update C files to tskit 0.3.5 (molpopogen)
  • [e81aad0bf6] Merge pull request #27 from molpopgen/fix_example_file_names (Kevin R. Thornton)
  • [dbaf304b38] Fix swapped example file names. Fixes #26 (molpopogen)
  • [30c34c2023] Merge pull request #23 from molpopgen/rename_crate (Kevin R. Thornton)
  • [e01b3e530e] rename crate to tskit (molpopgen)
  • [a7a69ad31d] Merge pull request #20 from molpopgen/metadata (Kevin R. Thornton)
  • [54ac4312a1] clippy and fmt (molpopgen)
  • [a1aea5cd95] add examples of metadata round trips (molpopgen)
  • [7352ca95b5] Update table operations to metadata API. (molpopgen)
  • [275074da30] Add metadata trait and API. (molpopgen)
  • [d89dcd19dd] Merge pull request #18 from molpopgen/tskit_wrapper_trait (Kevin R. Thornton)
  • [3445af368d] TableCollection uses new trait. (molpopgen)
  • [93c43aa2c7] Add trait to define what it means to wrap a tskit type. (molpopgen)

Migration guide

This document helps with migration to newer versions of tskit-rust. This document will not be exhaustive. Rather, it will focus on highlights or "must know" items.

Regarding breaking changes:

  • This document will discuss breaking changes requiring change in client code.
  • Other changes listed in the change log as breaking are considered "technically breaking but unlikely to affect anyone in practice". Please file an issue if we are wrong!

Acronyms used:

  • UB = undefined behavior.

v0.12.0

Breaking changes

  • Provenance table getters now return Option<&str> rather than Option<String>. These two types are very close to identical in API. Breakage is only possible if code relied on the return value owning its data.
  • Several member functions of Tree previously accepted NodeId as arguments. They now take N: Into<NodeId> + Copy. Thus, code passing in integers will have to drop the .into() calls.
  • Tree functions returning iterators previously returned Option<Iterator<...>>. They now return Iterator<...>. Data leading to None being returned in previous versions now return an iterator that will end immediately.
  • Deref to TableViews was replaced with delegation because using Deref to model inheritance is an anti-pattern.
  • usize to SizeType conversions are now fallible (e.g., TryFrom replaces From).
  • The TskitTypeAccess trait was removed because it was a bad idea. (It makes no sense to be generic over returning pointers to low-level tskit types.) The functionality is replaced with impl fns.
  • The names of all Owned tables are now Owning.
  • MetadataError::RoundtripError now requires Send + Sync.

v0.11.0

Bug fixes

  • Issue 363, which was introduced in PR 300. The bug resulted in UB when advancing trees for release builds.

Breaking changes

New error variant.

The error enum now includes LibraryError. Any code previously matching on errors will now break because the match is no longer exhaustive.

Row getters

This change occurred over several PR.

All "getter" functions for table rows now return Option instead of Result when a row index is out of bounds. These functions now return None for the out of bounds case. This behavior change brings the API in line with other rust APIs.

To update:

  • If None should represent an error for your use case, use Option::ok_or_else. In the case of a previous Result<Option<_>, _> that is now an Option<Result<_, _>>, use transpose.

Lifetime annotations of table types

PR #373.

Previously, a non-owning view of an edge table had a type EdgeTable<'a>. Those lifetimes are no longer necessary, which is a big design win.

Two traits, TableAccess and NodeListRowGenerator, have been removed. The TableCollection and TreeSequence types now contain that functionality as Deref targets.

Removing these traits implies:

  • Their removal from the crate prelude.
  • They can no longer be used as trait bounds.

Some views of a node table that required mutability broke due to these changes. A new function in the Deref target is nodes_mut(&mut self) addresses this breakage.