3. Cohomology and clearing
The coboundary lists each cell’s cofaces. Its oracle mirrors the boundary’s:
/// Step 3: the coboundary matrix (the transpose of the boundary), with the same filtration.
#[derive(Clone, Copy)]
pub struct Coboundary<'a>(pub &'a Volume);
impl MatrixOracle for Coboundary<'_> {
type CoefficientField = Z2;
type ColT = Cell;
type RowT = Cell;
fn column(&self, col: Cell) -> impl Iterator<Item = (Z2, Cell)> {
self.0.cofaces(col.0).map(|coface| (Z2::one(), Cell(coface)))
}
}
impl HasRowFiltration for Coboundary<'_> {
type FiltrationT = NotNan<f32>;
fn filtration_value(&self, row: Cell) -> NotNan<f32> {
NotNan::new(self.0.value(row.0)).unwrap()
}
}
and now cohomology with clearing is available:
// Step 3: cohomology with clearing. Columns stop at the squares (for H0, H1 and H2): the cubes only ever appear
// as rows, cofaces of squares, and are never enumerated
let delta = Coboundary(&volume).with_basis(basis(&volume, 2));
let cohomology = timed("cohomology, clearing", || {
barcode::cohomology(&delta, Algorithm::Clearing(Clearing::new()))
});
Two things changed:
- The basis stops at the squares. For \(H_0\), \(H_1\) and \(H_2\) by cohomology the columns are vertices, edges and squares; cubes only appear as rows, produced by the oracle as cofaces of squares. They are never listed.
- Clearing.
barcode::cohomologyreverses the coboundary (the anti-transpose) and reduces it dimension by dimension, skipping every column that the previous dimension’s pairs show to be zero. The basis must be split by dimension, whichSplitBasisis.
On the 40³ volume this takes 157 ms, against 5.4 s for the standard algorithm on the boundary: 34× faster.
You can now use every algorithm in phlite that doesn’t need extra information about the matrix.