A Rust library for working with simplicial complexes.
This library provides tools for constructing and manipulating simplicial complexes in Rust. The main data type is SimplicialComplex
, which represents a simplicial complex as a collection of facets of various dimensions. This has the benefit of being the most memory efficient representation of a simplicial complex.
The ability to generate multiple different models of random simplicial complexes comes ready out of the box with this library.
To use simplicial-topology
in your Rust project, add the following to your Cargo.toml
:
```toml [dependencies] simplicialtopology = {version = "0.1.1", features = ["scplot"]}
```
```rust use simplicialtopology::{sc, simplicialcomplex::SimplicialComplex};
let sc = SimplicialComplex::newfromvec(vec![vec![0, 1], vec![1, 2], vec![1, 2, 3], vec![3, 4], vec![1, 3, 4]]); let sc = sc![vec![0, 1], vec![1, 2], vec![1, 2, 3], vec![3, 4], vec![1, 3, 4]]; // Note this is the shorthand macro to construct an identical SimplicialComplex to sc asserteq!(sc, _sc);
println!("The complex has {} facets", sc.facets.len()); // This will output "The complex has 3 facets" println!("The complex has dimension {}", sc.dimension()); // This will output "The complex has dimension 2"
```
```rust use simplicial_topology::sc;
let mut sc = sc![vec![1,2], vec![2,3], vec![1,3], vec![1,4], vec![4,5], vec![1,5]]; // This is the wedge of two simplicial circles (bdy of [1,2,3] and bdy of [1,4,5])
sc.addsimplex(simplex![1,4,5]); // Here we add in the simplex [1,4,5] filling in a circle. If the boundary of this simplex didn't exist then addsimplex would panic
println!("Betti vector: {:?}", sc.bettinumbers()); // This will output "Betti vector: [1, 1, 0]"
println!("Euler characteristic: {}", sc.eulercharacteristic()); // This will output "Euler characteristc: 0
Note that we could construct the original `sc` above slightly more neatly:
rust
use simplicialtopology::{simplex, simplicialcomplex::SimplicialComplex};
let sigma: Facet = simplex![1,2,3];
let tau: Facet = simplex![1,4,5];
let sc: SimplicialComplex = sigma.boundaryascomplex().union(&tau.boundaryascomplex()); // boundaryascomplex() returns the boundary of the simplex but as a SimplicialComplex, rather than Vec
```rust use simplicialtopology::simplicialcomplex::randomsimplicialcomplex::{generaterandomsimplicial_complex, Model};
let model = Model::LinialMeshulam {numvertices: 20, dimension: 4, prob: 0.314159265}; let sc = generaterandomsimplicialcomplex(model);
asserteq!(sc.containsfullkskeleton(3), true); // This is by definition true for this Linial-Meshulam random complex
The available model types are all derived from constructing a random hypergraph (random collection of vectors from a given vertex set) and then applying upward or downward closure.
rust
pub enum Model {
Lower {numvertices: usize, probvec: Vec
We can generate and plot the distribution of Betti numbers of a random simplicial complex. The below code returns an interactive plotly Histogram. The sc_plot
feature must be added to Cargo.toml for this functionality.
```rust
use simplicialtopology::simplicialcomplex::randomsimplicialcomplex::{generatemanyrandombettinumbers, Model};
use simplicialtopology::graphics::plot::bettinumber_histogram;
let n: usize = 20;
let probvec: Vec
let bettinumbers: Vec
Below gives simple examples for a bunch of other operations that can be chained together to form new complexes, or to get properties of an existing complex. ```rust use simplicialtopology::{sc, simplex, simplicialcomplex::SimplicialComplex};
let sc1 = sc![vec![1,2,3]]; let sc2 = simplex![2,3,4].boundaryascomplex(); sc1.dimension(); // 2 sc2.dimension(); // 1 sc1.intersection(&sc2); // sc![vec![2,3]]); sc1.union(&sc2); // sc![vec![1,2,3], vec![3,4], vec![2,4]] sc1.link(&simplex![2]); // sc![vec![1,3]] sc2.star(&simplex![2]); // sc![vec![2,3], vec![2,4]] sc2.addsimplex(simplex![2,3,4]); // sc![vec![2,3,4]], will change sc2 in place sc1.addsimplex(simplex![2,3,4]); // panics as the boundary of this simplex is not in sc1 sc1.kskeleton(1); // sc![vec![1,2], vec![1,3], vec![2,3]] sc2.kthbettinumber(1);; // 1 sc1.isconnected(); // True sc![vec![1,2,3], vec![4]].isconnected(); // False sc![vec![1,2,3], vec![4]].alexanderdual(); // sc![vec![1,2], vec![1,3], vec![2,3]] - the dual complex X* on [n] where \sigma is a face iff [n] - \sigma is not a face in X ```
As is standard in a lot of simplicial complex libraries, homology (or rather Betti numbers), are computed over $\mathbb{Z}/2\mathbb{Z}$. This is a cop out for keeping track of orientations.
Currently the basis of the homology group isn't tracked, so for example we know that the complex sc![vec![1,2], vec![1,3], vec![2,3]]
has $1$st Betti number equal to $1$ but not that cycle is generated by <[1,2], [1,3], [2,3]>
.
Accessing $k$-dimensional faces of a complex is slower than it may be for other libraries. This is because only the facets are stored in memory, so if we requires other faces they need to be computed on the fly.