Quick Start Guide

This guide will cover several basic use cases for BATS. More specialized functionality is covered in the tutorials and examples. Ultimately, you can use the API reference.

Simplicial Complexes and Homology

BATS offers two implementations of simplicial complexes: SimplicialComplex and LightSimplicialComplex. While the internal representations differ, they both have the same interface which can be used. When dealing with stand-alone simplices, BATS uses std::vector<size_t> to represent simplices, from which the vertex set and dimension of the simplex can be extracted.

Warning

Simplices in bats should generally be assumed to be ordered, meaning that {0,1,2} is not the same as {1,2,0}. If you want to use unordered simplices, you can either add vertices in sorted order, or use a sorting algorithm before adding simplices to complexes.

The two main methods for adding simplices to simplicial complexes are add, which assumes you have already added the boundary of a simplex, and add_recursive which will add any faces that are not already present.

bats::SimplcialComplex X();
X.add_recursive({0,1,2});
X.add_recursive({2,3});
X.add({1,3});

X.print_summary();

The above code will create a SimplicialComplex with a single connected component and a single hole. The call to X.print_summary() will produce

SimplicialComplex, maxdim = 2
   dim 0 : 4 cells
   dim 1 : 5 cells
   dim 2 : 1 cells
10 total

Let’s now compute homology.

using F2 = ModP<int, 2>;
auto R = bats::Reduce(X, F2());

R.print_summary();

The output of bats::Reduce will be a ReducedChainComplex with F2 coefficients, which holds information used to compute homology. The output of R.print_summary() will be

ReducedChainComplex, maxdim =  2
   dim 0: 4, betti_0: 1
   dim 1: 5, betti_1: 1
   dim 2: 1, betti_2: 0

Persistent Homology

A filtration in BATS is a class which is templated over the type of the filtration values as well as the type of the underlying complex.

bats::Filtration<double, bats::SimplicialComplex> F;
std::vector<size_t> spx;
spx = {0,1,2}; F.add_recursive(0.0, spx);
spx = {2,3};   F.add_recursive(1.0, spx);
spx = {1,3};   F.add(2.0, spx);

F.complex().print_summary();

The underlying SimplicialComplex is the same as in the previous example:

SimplicialComplex, maxdim = 2
   dim 0 : 4 cells
   dim 1 : 5 cells
   dim 2 : 1 cells
10 total

Again, we can use the Reduce function to compute homology. Because we are using a filtration as input, persistent homology will be computed, returning a ReducedFilteredChainComplex.

using F2 = ModP<int, 2>;
auto R = bats::Reduce(F, F2());

for (size_t k = 0; k < R.maxdim(); ++k) {
   std::cout << "\n" << k << "-dimensional barcode:\n";
   for ( auto p : R.persistence_pairs(k)) {
      std::cout << p.str() << std::endl;
   }
}

The output will show one persistent 0-dimensional homology class as well as one persistent 1-dimensional homology class

0-dimensional barcode:
0 : (0,inf) <0,-1>
0 : (0,0) <1,0>
0 : (0,0) <2,1>
0 : (1,1) <3,3>

1-dimensional barcode:
1 : (0,0) <2,0>
1 : (2,inf) <4,-1>

The output of R.persistence_pairs(k) is a vector of PersistencePairs for k-dimensional persistent homology.

A PersistencePair includes 5 pieces of information: * The dimension of the homology class. * The birth and death parameters of the homology class. * The simplex indices responsible for birth and death.

Maps

BATS makes dealing with maps between topological spaces and associated chain maps and induced maps on homology easy. The relevant class is a CellularMap which keeps track of what cells in one complex map to what cells in another.

We’ll just look at a wrapper for CellularMap, called SimplcialMap which can be used to extend a map on the vertex set of a SimplicialComplex to a map of simplices.

First, we’ll build identical simplicial complexes X and Y which are both cycle graphs on four vertices.

bats::SimplicialComplex X;
X.add_recursive({0,1});
X.add_recursive({1,2});
X.add_recursive({2,3});
X.add_recursive({0,3});
bats::SimplicialComplex Y = X; // copy

We then build a simplicial map from X to Y which is extended from a reflection of the vertices.

std::vector<size_t> f0{2,1,0,3}; // reflection map
auto F = bats::SimplicialMap(X, Y, f0);

The map is extended by sending vertex i in X to vertex f0[i] in Y. Next, we can apply the chain functor. We’ll use F3 coefficients.

// apply the chain functor
using F3 = ModP<int, 3>;
auto CX = bats::Chain(X, F3());
auto CY = bats::Chain(Y, F3());
auto CF = bats::Chain(F, F3());

Finally, we can compute homology of the chain complexes and the induced maps.

auto RX = bats::ReducedChainComplex(CX);
auto RY = bats::ReducedChainComplex(CY);
RX.print_summary();
RY.print_summary();

for (size_t k = 0; k < 2; k++) {
   std::cout << "\nInduced map in dimension " << k << std::endl;
   auto HF = bats::induced_map(CF, RX, RY, k);
   HF.print();
}

The following output will be produced:

ReducedChainComplex, maxdim =  1
   dim 0: 4, betti_0: 1
   dim 1: 4, betti_1: 1
ReducedChainComplex, maxdim =  1
   dim 0: 4, betti_0: 1
   dim 1: 4, betti_1: 1

Induced map in dimension 0
[0x7fff6f336460] : 1 x 1 ColumnMatrix
   1

Induced map in dimension 1
[0x7fff6f336460] : 1 x 1 ColumnMatrix
   2

As expected, we see that X and Y both have 1-dimensional homology in dimensions 0 and 1. The induced map in dimension 0 is the identity, and the induced map in dimension 1 is multiplication by -1 (in mod-3 coefficients).

Zigzag Homology

We’ll now compute a simple zigzag barcode, using the above example. We’ll consider a diagram with two (identical) spaces, connected by a single edge which applies the reflection map in the above example.

bats::Diagram<bats::SimplicialComplex, bats::CellularMap> D(2,1);

bats::SimplicialComplex X;
X.add_recursive({0,1});
X.add_recursive({1,2});
X.add_recursive({2,3});
X.add_recursive({0,3});

std::vector<size_t> f0{2,1,0,3}; // reflection map
auto F = bats::SimplicialMap(X, X, f0);

D.set_node(0, X);
D.set_node(1, X);
D.set_edge(0, 0, 1, F); // edge 0: (0,1)

We can then apply the Chain and Hom functors, to obtain a diagram of homology spaces and maps between them

using F3 = ModP<int, 3>;
auto CD = bats::Chain(D, F3());

auto HD = bats::Hom(CD, (size_t) 1); // homology in dimension 1

Finally, we extract the barcode from the homology diagram

auto ps = barcode(HD, 1);
for (auto p : ps) {
   std::cout << p.str() << std::endl;
}

The output should look like: 1 : (0,1) <0,0>. This indicates there is a 1-dimensional homology bar, which is born in the space with index 0 and survives until the space with index 1. The <0,0> indicates which generators are associated with the homology class in the diagram.

Source Code

  1#include <vector>
  2#include <iostream>
  3#include <bats.hpp>
  4
  5int main() {
  6
  7    // Simplicial complexes and Homology
  8    {
  9        bats::SimplicialComplex X;
 10        X.add_recursive({0,1,2});
 11        X.add_recursive({2,3});
 12        X.add({1,3});
 13
 14        X.print_summary();
 15
 16        using F2 = ModP<int, 2>;
 17        auto R = bats::Reduce(X, F2());
 18
 19        R.print_summary();
 20    }
 21
 22    // Persistent homology
 23    {
 24        bats::Filtration<double, bats::SimplicialComplex> F;
 25        std::vector<size_t> spx;
 26        spx = {0,1,2}; F.add_recursive(0.0, spx);
 27        spx = {2,3};   F.add_recursive(1.0, spx);
 28        spx = {1,3};   F.add(2.0, spx);
 29
 30        F.complex().print_summary();
 31
 32        using F2 = ModP<int, 2>;
 33        auto R = bats::Reduce(F, F2());
 34
 35        for (size_t k = 0; k < R.maxdim(); ++k) {
 36            std::cout << "\n" << k << "-dimensional barcode:\n";
 37            for ( auto p : R.persistence_pairs(k)) {
 38                std::cout << p.str() << std::endl;
 39            }
 40        }
 41    }
 42
 43    // Maps
 44    {
 45        bats::SimplicialComplex X;
 46        X.add_recursive({0,1});
 47        X.add_recursive({1,2});
 48        X.add_recursive({2,3});
 49        X.add_recursive({0,3});
 50        bats::SimplicialComplex Y = X; // copy
 51
 52        X.print_summary();
 53
 54        std::vector<size_t> f0{2,1,0,3}; // reflection map
 55        auto F = bats::SimplicialMap(X, Y, f0);
 56
 57        // apply the chain functor
 58        using F3 = ModP<int, 3>;
 59        auto CX = bats::Chain(X, F3());
 60        auto CY = bats::Chain(Y, F3());
 61        auto CF = bats::Chain(F, F3());
 62
 63        auto RX = bats::ReducedChainComplex(CX);
 64        auto RY = bats::ReducedChainComplex(CY);
 65        RX.print_summary();
 66        RY.print_summary();
 67
 68        for (size_t k = 0; k < 2; k++) {
 69            std::cout << "\nInduced map in dimension " << k << std::endl;
 70            auto HF = bats::induced_map(CF, RX, RY, k);
 71            HF.print();
 72        }
 73    }
 74
 75    // Zigzag homology
 76    std::cout << "\nZigzag Example\n";
 77    {
 78        bats::Diagram<bats::SimplicialComplex, bats::CellularMap> D(2,1);
 79
 80        bats::SimplicialComplex X;
 81        X.add_recursive({0,1});
 82        X.add_recursive({1,2});
 83        X.add_recursive({2,3});
 84        X.add_recursive({0,3});
 85
 86        std::vector<size_t> f0{2,1,0,3}; // reflection map
 87        auto F = bats::SimplicialMap(X, X, f0);
 88
 89        D.set_node(0, X);
 90        D.set_node(1, X);
 91        D.set_edge(0, 0, 1, F); // edge 0: (0,1)
 92
 93        using F3 = ModP<int, 3>;
 94        auto CD = bats::Chain(D, F3());
 95
 96        auto HD = bats::Hom(CD, (size_t) 1); // homology in dimension 1
 97
 98        auto ps = bats::barcode(HD, 1);
 99    	for (auto p : ps) {
100    		std::cout << p.str() << std::endl;
101    	}
102
103
104    }
105
106
107    return EXIT_SUCCESS;
108}