+++ /dev/null
-#ifndef persistence_h_aPeinoXeiFeethuz
-#define persistence_h_aPeinoXeiFeethuz
-
-#include <iostream>
-#include <ostream>
-#include <vector>
-#include <cassert>
-
-#include "simplicialcomplex.h"
-#include "booleanmatrix.h"
-
-
-namespace libstick {
-
-
-/** Persistence computers various persistent homology information on a
- * simplex_order of a simplicial_complex. */
-template<int MAXDIM, class IT, class VT>
-class persistence {
-
- public:
- typedef simplicial_complex<MAXDIM, IT, VT> scomplex;
- typedef typename scomplex::simplex_order simplex_order;
- typedef boolean_colmatrix<IT> boundary_matrix;
- typedef boolean_colmatrix<IT> transformation_matrix;
- typedef std::vector<IT> lowestones_matrix;
-
- /** A point in a persistence diagram comprises birth- and death-index.
- * These are the indices of the simplices of 'order' that give birth
- * and death to the class. */
- struct diagram_point {
- /** 'birth'-th simplex in 'order' gave birth to this cycle. */
- IT birth;
- /** 'death'-th simplex in 'order' killed this cycle. If 'death' is
- * zero, the cycle never dies. Otherwise, 'death' is always larger
- * than 'birth'. */
- IT death;
- };
-
- /** Save the births and deaths of simplices of a fixed dimension, say p. */
- struct diagram {
- /** Saves the sequence of birth/death-points of p-simplices, sorted
- * by birth-index. If death-index is zero, the corresponding
- * homology class never dies. */
- std::vector<diagram_point> births;
-
-
- /** Gives the p-th Betti number, i.e., the number of immortal
- * p-dimensional homology classes. */
- size_t betti() const {
- size_t b = 0;
-
- for (unsigned i=0; i < births.size(); ++i)
- if (births[i].death == 0)
- b++;
-
- return b;
- }
-
- /** Gives the number of p-dimensional homology classes that are
- * born by simplex 'from' or earlier and die after simplex 'to' is
- * inserted. */
- size_t persistent_betti(IT from, IT to) const {
- assert(from <= to);
- size_t b = 0;
-
- for (unsigned i=0; i < births.size(); ++i) {
- // All following simplices are born too late.
- if (births[i].birth > from)
- break;
- if (births[i].death > to || births[i].death == 0)
- b++;
- }
-
- return b;
- }
- };
-
- public:
- /** Create a new peristence object on the given simplex_order */
- persistence(const simplex_order &order) :
- order(order),
- bm(0),
- rbm(0),
- lowestones(0),
- tm(0)
- {
- reset();
- }
-
- /** Reset all results gained from 'order'. */
- void reset() {
- done_matrices = false;
- done_diagrams = false;
- }
-
- /** Get simplicial order of this persistence object. */
- const simplex_order& get_order() const {
- return order;
- }
-
- /** Get boundary matrix 'bm' of 'order', compute reduces boundary
- * matrix 'rbm', and the transformation matrix 'tm' such that rbm = bm *
- * tm. */
- void compute_matrices() {
- if (done_matrices)
- return;
- done_matrices = true;
-
- bm = order.get_boundary_matrix();
- rbm = bm;
- tm = create_unit_matrix<transformation_matrix>(bm.width());
- lowestones = lowestones_matrix(bm.width());
-
- // Make every column reduced, i.e., it is a zero-column or contains only one 1.
- for (unsigned c=0; c < rbm.width(); ++c) {
- //if (c % 100 == 0)
- //std::cout << "c = " << c << " (" << (100.0*float(c)/rbm.width()) << " %)" << std::endl;
- // Reduce as long as we need to reduce
- while (!rbm.get_column(c).isempty()) {
- // (r, c) is the lowest one of col
- const IT r = rbm.get_column(c).back();
- // This column contains its lowest one on the same row
- const IT c2 = lowestones[r];
- assert(c2 < c);
-
- // There is no valid c2, hence we have completely reduced
- if (c2 == 0)
- break;
- // Reduce col by column c2
- else {
- assert(rbm.get(r, c));
- rbm.add_column(c, rbm.get_column(c2));
- tm.add_column(c, tm.get_column(c2));
- assert(!rbm.get(r, c));
- }
- }
-
- // A lowest one remained, recall it
- if (!rbm.get_column(c).isempty()) {
- const IT r = rbm.get_column(c).back();
- assert(lowestones[r] == 0);
- assert(r < c);
- lowestones[r] = c;
- }
- }
- }
-
- /** Get the lowest-one matrix of 'order'. */
- const lowestones_matrix& get_lowestones_matrix() const {
- assert(done_matrices);
- return lowestones;
- }
-
- /** Get the boundary matrix of 'order'. */
- const boundary_matrix& get_boundary_matrix() const {
- assert(done_matrices);
- return bm;
- }
-
- /** Get the reduced boundary matrix of 'order'. */
- const boundary_matrix& get_reduced_boundary_matrix() const {
- assert(done_matrices);
- return rbm;
- }
-
- /** Get the transformation matrix of 'order', which transforms the
- * boundary matrix to the reduced boundary matrix, when multiplied from
- * the right. */
- const transformation_matrix& get_transformation_matrix() const {
- assert(done_matrices);
- return tm;
- }
-
- /** Print the sequence of births and deaths of cycles (homology classes). */
- void interpret_reduction(std::ostream &os) const {
- assert(done_matrices);
-
- for (unsigned c=0; c < bm.width(); ++c) {
- os << c << ". inserting ";
-
- switch (bm.get_column(c).size()) {
- case 0:
- os << "dummy vertex of dim -1";
- break;
- case 1:
- os << "vertex";
- break;
- case 2:
- os << "edge";
- break;
- case 3:
- os << "triangle";
- break;
- case 4:
- os << "tetrahedron";
- break;
- default:
- os << "simplex";
- break;
- }
-
- os << " \t\tvalue: " << order.get_simplex(c).value << std::endl;
- os << " boundary: " << bm.get_column(c) << std::endl;
-
- typename boolean_colmatrix<IT>::column_type col = rbm.get_column(c);
- if (col.isempty()) {
- os << " \033[1;32mbirth\033[0;m of a cycle: " << tm.get_column(c) << std::endl;
- } else {
- os << " \033[1;31mdeath\033[0;m of a cycle: " << rbm.get_column(c) << std::endl;
- os << " boundary of: " << tm.get_column(c) << std::endl;
- }
- os << std::endl;
- }
- }
-
- /** Get the 'dim'-dimensional peristence diagram. */
- const diagram& get_persistence_diagram(int dim) const{
- assert(-1 <= dim);
- assert(dim <= MAXDIM);
- assert(done_diagrams);
-
- return diagrams[dim+1];
- }
-
- /** Compute persistence diagrams */
- void compute_diagrams() {
- if (done_diagrams)
- return;
- done_diagrams = true;
-
- compute_matrices();
-
-#ifndef NDEBUG
- std::set<IT> deaths, births;
-#endif
- //Clear all diagrams
- for (int d=0; d < MAXDIM + 2; ++d)
- diagrams[d] = diagram();
-
- for (unsigned c=0; c < lowestones.size(); ++c) {
- // A cycle was born
- if (rbm.get_column(c).isempty()) {
- const int dim = order.get_simplex(c).dim;
-
- // Create a diagram point
- diagram_point p;
- p.birth = c;
- p.death = 0;
- // If the class actually dies, get the index of the killing simplex
- if (lowestones[c] > 0)
- p.death = lowestones[c];
-
- _get_persistence_diagram(dim).births.push_back(p);
-#ifndef NDEBUG
- assert(births.count(p.birth) == 0);
- assert(p.death == 0 || deaths.count(p.death) == 0);
- births.insert(p.birth);
- deaths.insert(p.death);
-#endif
- }
- }
-#ifndef NDEBUG
- // Shakespeare principle: A simplex either gives birth or kills, be
- // or not be, tertium non datur.
- for (unsigned c=0; c < lowestones.size(); ++c) {
- // A class died with c
- if (!rbm.get_column(c).isempty())
- assert(c == 0 || deaths.count(c) > 0);
- else
- assert(births.count(c) > 0);
- }
-#endif
- }
-
- /** Print birth and death information in the persistence diagrams. */
- void interpret_persistence(std::ostream &os) const {
- assert(done_diagrams);
-
- for (int d=-1; d <= MAXDIM; ++d) {
- os << "Dimension " << d << std::endl;
- const diagram &dia = get_persistence_diagram(d);
-
- for (unsigned i=0; i < dia.births.size(); ++i) {
- const IT birth = dia.births[i].birth;
- const IT death = dia.births[i].death;
-
- os << " ";
- if (death > 0) {
- const scomplex& c = get_order().get_complex();
- const VT pers = c.simplices[death].value - c.simplices[birth].value;
- os << birth << "\033[1;31m -> " << death << "\033[0;m" << " \tpers: " << pers;
- } else {
- os << "\033[1;32m" << birth << "\033[0;m" << " \t\t[essential]";
- }
- os << std::endl;
- }
- }
- }
-
- private:
-
- diagram& _get_persistence_diagram(int dim) {
- assert(-1 <= dim);
- assert(dim <= MAXDIM);
- assert(done_diagrams);
-
- return diagrams[dim+1];
- }
-
- /** The underlying simplex order of this diagram. */
- const simplex_order ℴ
-
- bool done_matrices;
- bool done_diagrams;
-
- /** The boundary matrix of 'order' */
- boundary_matrix bm;
- /** The reduced boundary matrix of 'order' */
- boundary_matrix rbm;
- /** Only the lowest ones per column of the reduced boundary matrix.
- * lowestones[i] contains the column-index at which the lowest one at
- * row i appears, and zero if no such column exists. */
- lowestones_matrix lowestones;
- /** The transformation matrix that transforms bm into rbm, i.e. rbm = bm * tm. */
- transformation_matrix tm;
-
- /** The container for all p-dimensional diagrams. The p-th diagram is
- * save at index p+1, with -1 <= p <= MAXDIM. */
- diagram diagrams[MAXDIM+2];
-};
-
-
-} // namespace libstick
-
-#endif