/* This file is part of the Gudhi Library. The Gudhi library
* (Geometric Understanding in Higher Dimensions) is a generic C++
* library for computational topology.
*
* Author(s): Pawel Dlotko
*
* Copyright (C) 2015 INRIA Sophia-Saclay (France)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef BITMAP_CUBICAL_COMPLEX_BASE_H_
#define BITMAP_CUBICAL_COMPLEX_BASE_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
/**
* This is a class implementing a basic bitmap data structure to store cubical complexes. It implements only the most basic subroutines.
* The idea of the bitmap is the following. Our aim is to have a memory efficient data structure to store d-dimensional cubical complex C being a cubical decomposition
* of a rectangular region of a space. This is achieved by storing C as a vector of bits (this is where the name 'bitmap' came from). Each cell is represented by a single
* bit (in case of black and white bitmaps, or by a single element of a type T (here T is a filtration type of a bitmap, typically a double). All the informations needed for homology and
* persistent homology computations (like dimension of a cell, boundary and coboundary elements of a cell, are then obtained from the position of the element in C.
*/
template
class Bitmap_cubical_complex_base {
public:
/**
* There are a few constructors of a Bitmap_cubical_complex_base class. First one, that takes vector, creates an empty bitmap of a dimension equal to the number of elements in the
* input vector and size in the i-th dimension equal the number in the position i-of the input vector.
*/
Bitmap_cubical_complex_base(std::vector sizes_);
/**
* The second constructor takes as a input a Perseus style file. For more details, please consult the documentations of Perseus software as well as examples attached to this
* implementation.
**/
Bitmap_cubical_complex_base(char* perseusStyleFile_);
/**
* The last constructor of a Bitmap_cubical_complex_base class accepts vector of dimensions (as the first one) together with vector of filtration values of top dimensional cells.
**/
Bitmap_cubical_complex_base(std::vector dimensions_, std::vector topDimensionalCells_);
/**
* The functions get_boundary_of_a_cell, get_coboundary_of_a_cell and get_cell_data are the basic functions that compute boundary / coboundary / dimension and the filtration
* value form a position of a cell in the structure of a bitmap. The input parameter of all of those function is a non-negative integer, indicating a position of a cube in the data structure.
* In the case of functions that compute (co)boundary, the output is a vector if non-negative integers pointing to the positions of (co)boundary element of the input cell.
*/
inline std::vector< size_t > get_boundary_of_a_cell(size_t cell_);
/**
* The functions get_boundary_of_a_cell, get_coboundary_of_a_cell, get_dimension_of_a_cell and get_cell_data are the basic functions that compute boundary / coboundary / dimension and the filtration
* value form a position of a cell in the structure of a bitmap. The input parameter of all of those function is a non-negative integer, indicating a position of a cube in the data structure.
* In the case of functions that compute (co)boundary, the output is a vector if non-negative integers pointing to the positions of (co)boundary element of the input cell.
**/
inline std::vector< size_t > get_coboundary_of_a_cell(size_t cell_);
/**
* In the case of get_dimension_of_a_cell function, the output is a non-negative integer indicating the dimension of a cell.
**/
inline unsigned get_dimension_of_a_cell(size_t cell_);
/**
* In the case of get_cell_data, the output parameter is a reference to the value of a cube in a given position.
**/
inline T& get_cell_data(size_t cell_);
/**
* Typical input used to construct a baseBitmap class is a filtration given at the top dimensional cells. Then, there are a few ways one can pick the filtration of lower dimensional
* cells. The most typical one is by so called lower star filtration. This function is always called by any constructor which takes the top dimensional cells. If you use such a constructor,
* then there is no need to call this function. Call it only if you are putting the filtration of the cells by your own (for instance by using topDimensionalCellsIterator).
**/
void impose_lower_star_filtration(); //assume that top dimensional cells are already set.
/**
* Returns dimension of a complex.
**/
inline unsigned dimension() {
return sizes.size();
}
/**
* Returns number of all cubes in the data structure.
**/
inline unsigned size_of_bitmap() {
return this->data.size();
}
/**
* Writing to stream operator.
**/
template
friend ostream& operator<<(ostream & os_, const Bitmap_cubical_complex_base& b_);
//ITERATORS
/**
* Iterator through all cells in the complex (in order they appear in the structure -- i.e. in lexicographical order).
**/
typedef typename std::vector< T >::iterator all_cells_iterator;
all_cells_iterator all_cells_begin()const {
return this->data.begin();
}
all_cells_iterator all_cells_end()const {
return this->data.end();
}
typedef typename std::vector< T >::const_iterator all_cells_const_iterator;
all_cells_const_iterator all_cells_const_begin()const {
return this->data.begin();
}
all_cells_const_iterator all_cells_const_end()const {
return this->data.end();
}
/**
* Iterator through top dimensional cells of the complex. The cells appear in order they are stored in the structure (i.e. in lexicographical order)
**/
class Top_dimensional_cells_iterator : std::iterator< std::input_iterator_tag, double > {
public:
Top_dimensional_cells_iterator(Bitmap_cubical_complex_base& b_) : b(b_) {
for (size_t i = 0; i != b_.dimension(); ++i) {
this->counter.push_back(0);
}
}
Top_dimensional_cells_iterator operator++() {
//first find first element of the counter that can be increased:
size_t dim = 0;
while ((dim != this->b.dimension()) && (this->counter[dim] == this->b.sizes[dim] - 1))++dim;
if (dim != this->b.dimension()) {
++this->counter[dim];
for (size_t i = 0; i != dim; ++i) {
this->counter[i] = 0;
}
} else {
++this->counter[0];
}
return *this;
}
Top_dimensional_cells_iterator operator++(int) {
Top_dimensional_cells_iterator result = *this;
++(*this);
return result;
}
Top_dimensional_cells_iterator operator=(const Top_dimensional_cells_iterator& rhs_) {
this->counter = rhs_.counter;
this->b = rhs_.b;
return *this;
}
bool operator==(const Top_dimensional_cells_iterator& rhs_) {
if (&this->b != &rhs_.b)return false;
if (this->counter.size() != rhs_.counter.size())return false;
for (size_t i = 0; i != this->counter.size(); ++i) {
if (this->counter[i] != rhs_.counter[i])return false;
}
return true;
}
bool operator!=(const Top_dimensional_cells_iterator& rhs_) {
return !(*this == rhs_);
}
T& operator*() {
//given the counter, compute the index in the array and return this element.
unsigned index = 0;
for (size_t i = 0; i != this->counter.size(); ++i) {
index += (2 * this->counter[i] + 1) * this->b.multipliers[i];
}
return this->b.data[index];
}
size_t computeIndexInBitmap() {
size_t index = 0;
for (size_t i = 0; i != this->counter.size(); ++i) {
index += (2 * this->counter[i] + 1) * this->b.multipliers[i];
}
return index;
}
void printCounter() {
for (size_t i = 0; i != this->counter.size(); ++i) {
cout << this->counter[i] << " ";
}
}
friend class Bitmap_cubical_complex_base;
protected:
std::vector< unsigned > counter;
Bitmap_cubical_complex_base& b;
};
Top_dimensional_cells_iterator top_dimensional_cells_begin() {
Top_dimensional_cells_iterator a(*this);
return a;
}
Top_dimensional_cells_iterator top_dimensional_cells_end() {
Top_dimensional_cells_iterator a(*this);
for (size_t i = 0; i != this->dimension(); ++i) {
a.counter[i] = this->sizes[i] - 1;
}
a.counter[0]++;
return a;
}
//****************************************************************************************************************//
//****************************************************************************************************************//
//****************************************************************************************************************//
//****************************************************************************************************************//
//****************************************************************************************************************//
//****************************************************************************************************************//
//****************************************************************************************************************//
//****************************************************************************************************************//
protected:
std::vector sizes;
std::vector multipliers;
std::vector data;
size_t totalNumberOfCells;
void set_up_containers(std::vector sizes_) {
unsigned multiplier = 1;
for (size_t i = 0; i != sizes_.size(); ++i) {
this->sizes.push_back(sizes_[i]);
this->multipliers.push_back(multiplier);
//multiplier *= 2*(sizes[i]+1)+1;
multiplier *= 2 * sizes_[i] + 1;
}
//std::reverse( this->sizes.begin() , this->sizes.end() );
std::vector data(multiplier);
std::fill(data.begin(), data.end(), INT_MAX);
this->totalNumberOfCells = multiplier;
this->data = data;
}
size_t compute_position_in_bitmap(std::vector< int > counter_) {
size_t position = 0;
for (size_t i = 0; i != this->multipliers.size(); ++i) {
position += this->multipliers[i] * counter_[i];
}
return position;
}
std::vector compute_counter_for_given_cell(size_t cell_) {
std::vector counter;
for (size_t dim = this->sizes.size(); dim != 0; --dim) {
counter.push_back(cell_ / this->multipliers[dim - 1]);
cell_ = cell_ % this->multipliers[dim - 1];
}
std::reverse(counter.begin(), counter.end());
return counter;
}
std::vector< size_t > generate_vector_of_shifts_for_bitmaps_with_periodic_boundary_conditions(std::vector< bool > directionsForPeriodicBCond_);
};
template
ostream& operator<<(ostream & out_, const Bitmap_cubical_complex_base& b_) {
//for ( typename bitmap::all_cells_const_iterator it = b.all_cells_const_begin() ; it != b.all_cells_const_end() ; ++it )
for (typename Bitmap_cubical_complex_base::all_cells_const_iterator it = b_.all_cells_const_begin(); it != b_.all_cells_const_end(); ++it) {
out_ << *it << " ";
}
return out_;
}
template
Bitmap_cubical_complex_base::Bitmap_cubical_complex_base(std::vector sizes_) {
this->set_up_containers(sizes_);
}
template
Bitmap_cubical_complex_base::Bitmap_cubical_complex_base(std::vector sizesInFollowingDirections_, std::vector topDimensionalCells_) {
this->set_up_containers(sizesInFollowingDirections_);
size_t numberOfTopDimensionalElements = 1;
for (size_t i = 0; i != sizesInFollowingDirections_.size(); ++i) {
numberOfTopDimensionalElements *= sizesInFollowingDirections_[i];
}
if (numberOfTopDimensionalElements != topDimensionalCells_.size()) {
cerr << "Error in constructor Bitmap_cubical_complex_base( std::vector sizesInFollowingDirections_ , std::vector topDimensionalCells_ ). Number of top dimensional elements that follow from sizesInFollowingDirections vector is different than the size of topDimensionalCells vector." << endl;
throw ("Error in constructor Bitmap_cubical_complex_base( std::vector sizesInFollowingDirections_ , std::vector topDimensionalCells_ ). Number of top dimensional elements that follow from sizesInFollowingDirections vector is different than the size of topDimensionalCells vector.");
}
Bitmap_cubical_complex_base::Top_dimensional_cells_iterator it(*this);
size_t index = 0;
for (it = this->top_dimensional_cells_begin(); it != this->top_dimensional_cells_end(); ++it) {
(*it) = topDimensionalCells_[index];
++index;
}
this->impose_lower_star_filtration();
}
template
Bitmap_cubical_complex_base::Bitmap_cubical_complex_base(char* perseusStyleFile_) {
bool dbg = false;
ifstream inFiltration, inIds;
inFiltration.open(perseusStyleFile_);
unsigned dimensionOfData;
inFiltration >> dimensionOfData;
if (dbg) {
cerr << "dimensionOfData : " << dimensionOfData << endl;
}
std::vector sizes;
for (size_t i = 0; i != dimensionOfData; ++i) {
int sizeInThisDimension;
inFiltration >> sizeInThisDimension;
sizeInThisDimension = abs(sizeInThisDimension);
sizes.push_back(sizeInThisDimension);
if (dbg) {
cerr << "sizeInThisDimension : " << sizeInThisDimension << endl;
}
}
this->set_up_containers(sizes);
Bitmap_cubical_complex_base::Top_dimensional_cells_iterator it(*this);
it = this->top_dimensional_cells_begin();
//TODO -- over here we also need to read id's of cell and put them to bitmapElement structure!
while (!inFiltration.eof()) {
double filtrationLevel;
inFiltration >> filtrationLevel;
if (dbg) {
cerr << "Cell of an index : " << it.computeIndexInBitmap() << " and dimension: " << this->get_dimension_of_a_cell(it.computeIndexInBitmap()) << " get the value : " << filtrationLevel << endl;
}
*it = filtrationLevel;
++it;
}
inFiltration.close();
this->impose_lower_star_filtration();
}
template
std::vector< size_t > Bitmap_cubical_complex_base::get_boundary_of_a_cell(size_t cell_) {
bool bdg = false;
//first of all, we need to take the list of coordinates in which the cell has nonzero length. We do it by using modified version to compute dimension of a cell:
std::vector< unsigned > dimensionsInWhichCellHasNonzeroLength;
unsigned dimension = 0;
size_t cell1 = cell_;
for (size_t i = this->multipliers.size(); i != 0; --i) {
unsigned position = cell1 / multipliers[i - 1];
if (position % 2 == 1) {
dimensionsInWhichCellHasNonzeroLength.push_back(i - 1);
dimension++;
}
cell1 = cell1 % multipliers[i - 1];
}
if (bdg) {
cerr << "dimensionsInWhichCellHasNonzeroLength : \n";
for (size_t i = 0; i != dimensionsInWhichCellHasNonzeroLength.size(); ++i) {
cerr << dimensionsInWhichCellHasNonzeroLength[i] << endl;
}
getchar();
}
std::vector< size_t > boundaryElements;
if (dimensionsInWhichCellHasNonzeroLength.size() == 0)return boundaryElements;
for (size_t i = 0; i != dimensionsInWhichCellHasNonzeroLength.size(); ++i) {
boundaryElements.push_back(cell_ - multipliers[ dimensionsInWhichCellHasNonzeroLength[i] ]);
boundaryElements.push_back(cell_ + multipliers[ dimensionsInWhichCellHasNonzeroLength[i] ]);
if (bdg) cerr << "multipliers[dimensionsInWhichCellHasNonzeroLength[i]] : " << multipliers[dimensionsInWhichCellHasNonzeroLength[i]] << endl;
if (bdg) cerr << "cell_ - multipliers[dimensionsInWhichCellHasNonzeroLength[i]] : " << cell_ - multipliers[dimensionsInWhichCellHasNonzeroLength[i]] << endl;
if (bdg) cerr << "cell_ + multipliers[dimensionsInWhichCellHasNonzeroLength[i]] : " << cell_ + multipliers[dimensionsInWhichCellHasNonzeroLength[i]] << endl;
}
return boundaryElements;
}
template
std::vector< size_t > Bitmap_cubical_complex_base::get_coboundary_of_a_cell(size_t cell_) {
bool bdg = false;
//first of all, we need to take the list of coordinates in which the cell has nonzero length. We do it by using modified version to compute dimension of a cell:
std::vector< unsigned > dimensionsInWhichCellHasZeroLength;
unsigned dimension = 0;
size_t cell1 = cell_;
for (size_t i = this->multipliers.size(); i != 0; --i) {
unsigned position = cell1 / multipliers[i - 1];
if (position % 2 == 0) {
dimensionsInWhichCellHasZeroLength.push_back(i - 1);
dimension++;
}
cell1 = cell1 % multipliers[i - 1];
}
std::vector counter = this->compute_counter_for_given_cell(cell_);
//reverse(counter.begin() , counter.end());
if (bdg) {
cerr << "dimensionsInWhichCellHasZeroLength : \n";
for (size_t i = 0; i != dimensionsInWhichCellHasZeroLength.size(); ++i) {
cerr << dimensionsInWhichCellHasZeroLength[i] << endl;
}
cerr << "\n counter : " << endl;
for (size_t i = 0; i != counter.size(); ++i) {
cerr << counter[i] << endl;
}
getchar();
}
std::vector< size_t > coboundaryElements;
if (dimensionsInWhichCellHasZeroLength.size() == 0)return coboundaryElements;
for (size_t i = 0; i != dimensionsInWhichCellHasZeroLength.size(); ++i) {
if (bdg) {
cerr << "Dimension : " << i << endl;
if (counter[dimensionsInWhichCellHasZeroLength[i]] == 0) {
cerr << "In dimension : " << i << " we cannot substract, since we will jump out of a Bitmap_cubical_complex_base \n";
}
if (counter[dimensionsInWhichCellHasZeroLength[i]] == 2 * this->sizes[dimensionsInWhichCellHasZeroLength[i]]) {
cerr << "In dimension : " << i << " we cannot substract, since we will jump out of a Bitmap_cubical_complex_base \n";
}
}
if ((cell_ > multipliers[dimensionsInWhichCellHasZeroLength[i]]) && (counter[dimensionsInWhichCellHasZeroLength[i]] != 0))
//if ( counter[dimensionsInWhichCellHasZeroLength[i]] != 0 )
{
if (bdg)cerr << "Subtracting : " << cell_ - multipliers[dimensionsInWhichCellHasZeroLength[i]] << endl;
coboundaryElements.push_back(cell_ - multipliers[dimensionsInWhichCellHasZeroLength[i]]);
}
if ((cell_ + multipliers[dimensionsInWhichCellHasZeroLength[i]] < this->data.size()) && (counter[dimensionsInWhichCellHasZeroLength[i]] != 2 * this->sizes[dimensionsInWhichCellHasZeroLength[i]]))
//if ( counter[dimensionsInWhichCellHasZeroLength[i]] != 2*this->sizes[dimensionsInWhichCellHasZeroLength[i]] )
{
coboundaryElements.push_back(cell_ + multipliers[dimensionsInWhichCellHasZeroLength[i]]);
if (bdg)cerr << "Adding : " << cell_ + multipliers[dimensionsInWhichCellHasZeroLength[i]] << endl;
}
}
return coboundaryElements;
}
template
unsigned Bitmap_cubical_complex_base::get_dimension_of_a_cell(size_t cell_) {
bool dbg = false;
if (dbg)cerr << "\n\n\n Computing position o a cell of an index : " << cell_ << endl;
unsigned dimension = 0;
for (size_t i = this->multipliers.size(); i != 0; --i) {
unsigned position = cell_ / multipliers[i - 1];
if (dbg)cerr << "i-1 :" << i - 1 << endl;
if (dbg)cerr << "cell_ : " << cell_ << endl;
if (dbg)cerr << "position : " << position << endl;
if (dbg)cerr << "multipliers[" << i - 1 << "] = " << multipliers[i - 1] << endl;
if (dbg)getchar();
if (position % 2 == 1) {
if (dbg)cerr << "Nonzero length in this direction \n";
dimension++;
}
cell_ = cell_ % multipliers[i - 1];
}
return dimension;
}
template
T& Bitmap_cubical_complex_base::get_cell_data(size_t cell_) {
return this->data[cell_];
}
template
void Bitmap_cubical_complex_base::impose_lower_star_filtration() {
bool dbg = false;
//this vector will be used to check which elements have already been taken care of in imposing lower star filtration:
std::vector isThisCellConsidered(this->data.size(), false);
std::vector indicesToConsider;
//we assume here that we already have a filtration on the top dimensional cells and we have to extend it to lower ones.
typename Bitmap_cubical_complex_base::Top_dimensional_cells_iterator it(*this);
for (it = this->top_dimensional_cells_begin(); it != this->top_dimensional_cells_end(); ++it) {
indicesToConsider.push_back(it.computeIndexInBitmap());
}
while (indicesToConsider.size()) {
if (dbg) {
cerr << "indicesToConsider in this iteration \n";
for (size_t i = 0; i != indicesToConsider.size(); ++i) {
cout << indicesToConsider[i] << " ";
}
getchar();
}
std::vector newIndicesToConsider;
for (size_t i = 0; i != indicesToConsider.size(); ++i) {
std::vector bd = this->get_boundary_of_a_cell(indicesToConsider[i]);
for (size_t boundaryIt = 0; boundaryIt != bd.size(); ++boundaryIt) {
if (this->data[ bd[boundaryIt] ] > this->data[ indicesToConsider[i] ]) {
this->data[ bd[boundaryIt] ] = this->data[ indicesToConsider[i] ];
}
if (isThisCellConsidered[ bd[boundaryIt] ] == false) {
newIndicesToConsider.push_back(bd[boundaryIt]);
isThisCellConsidered[ bd[boundaryIt] ] = true;
}
}
}
indicesToConsider.swap(newIndicesToConsider);
}
}
template
std::vector< size_t > Bitmap_cubical_complex_base::generate_vector_of_shifts_for_bitmaps_with_periodic_boundary_conditions(std::vector< bool > directionsForPeriodicBCond_) {
bool dbg = false;
if (this->sizes.size() != directionsForPeriodicBCond_.size())throw "directionsForPeriodicBCond_ vector size is different from the size of the bitmap. The program will now terminate \n";
std::vector sizes(this->sizes.size());
for (size_t i = 0; i != this->sizes.size(); ++i)sizes[i] = 2 * this->sizes[i];
counter c(sizes);
std::vector< size_t > result;
for (size_t i = 0; i != this->data.size(); ++i) {
size_t position;
if (!c.isFinal()) {
position = i;
//result.push_back( i );
} else {
std::vector< bool > finals = c.directionsOfFinals();
bool jumpInPosition = false;
for (size_t dir = 0; dir != finals.size(); ++dir) {
if (finals[dir] == false)continue;
if (directionsForPeriodicBCond_[dir]) {
jumpInPosition = true;
}
}
if (jumpInPosition == true) {
//in this case this guy is final, so we need to find 'the opposite one'
position = compute_position_in_bitmap(c.findOpposite(directionsForPeriodicBCond_));
} else {
position = i;
}
}
result.push_back(position);
if (dbg) {
cerr << " position : " << position << endl;
cerr << c << endl;
getchar();
}
c.increment();
}
return result;
}
#endif // BITMAP_CUBICAL_COMPLEX_BASE_H_