Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

phlite: explicit complexes

The phlite package computes persistence of a filtered chain complex you build yourself. There are three steps:

flowchart LR
    A["ChainComplex<br/>from_pairs / from_columns /<br/>from_csc / from_cells"] -->|".filtration(values)"| B["FilteredComplex"]
    B -->|".persistence(...)"| C["Barcode"]
    A -->|".filtration(other values)"| B2["another FilteredComplex"]

1. Build a chain complex

A ChainComplex is the cells, their dimensions and their boundaries, with no filtration. It is validated once.

import phlite

# A hollow triangle on vertices 0, 1, 2, filled in by cell 6
pairs = [
    (0, []), (0, []), (0, []),        # vertices
    (1, [0, 1]), (1, [1, 2]), (1, [0, 2]),  # edges
    (2, [3, 4, 5]),                   # the triangle
]
chain = phlite.ChainComplex.from_pairs(pairs)
ConstructorTakesNotes
from_pairs(pairs)[(dimension, boundary), ...]as PHAT and LoPHAT
from_columns(boundaries, dimensions)two lists
from_csc(indptr, indices, dimensions, data=None)numpy arraysthe fast path: no per-entry Python objects
from_cells(cells, ...)your own objects (or tuples) with a dimension and a boundaryresults come back as your objects

All take coefficients= (per boundary entry) and modulus= (2, 3, 5, 7, 11 or 13).

Your own cells. Any hashable object with a dimension and a boundary() works (the same protocol as grpphati’s Column), or plain values with functions saying how to get the pieces:

chain = phlite.ChainComplex.from_cells(
    simplices,                                   # tuples of vertices
    dimension=lambda s: len(s) - 1,
    boundary=lambda s: {s[:i] + s[i + 1:]: (-1) ** i for i in range(len(s))} if len(s) > 1 else {},
    modulus=3,
)

For large complexes of your own objects, key= (an injective map to integers) matches cells in Rust instead of through Python hashing, from_key= avoids storing the objects at all, and face_keys=True lets boundary return face keys rather than face objects (usually the real speed-up, since building face objects is the slow part).

2. Attach a filtration

filtered = chain.filtration([0, 0, 0, 1, 1, 2, 3])      # one value per cell

Cells enter in order of value, ties broken by index; every face must enter no later than its cofaces (otherwise a ValueError names the offending cells). values can be a sequence or numpy array (fastest), a mapping from cells to values, a function of a cell, or omitted (cells’ entrance_time, else their order). With extend=True missing values are filled in as the maximum over the faces, so values on vertices give the lower-star filtration.

One chain complex can be filtered many times, cheaply: the validation and indexing are done once.

3. Compute persistence

barcode = filtered.persistence(
    max_dim=None,            # default: the top dimension
    method="cohomology",     # or "homology"
    algorithm=None,          # an Algorithm, default Algorithm.serial()
    representatives=False,
)

The Barcode is in terms of your cells:

barsevery bar: dimension, birth, death, birth_value, death_value, persistence, is_essential, representative
pairings, essential(birth, death) cells of finite bars, births of infinite ones
bar(cell), representative(cell)the bar born at a cell
diagram(dim), diagrams()(k, 2) numpy arrays of (birth, death) values, inf for essential bars

Representatives are (cell, coefficient) lists in decreasing filtration order, so the birth cell comes first. Bars of length zero are omitted (with the default index filtration there are none, so you get every pair, as with PHAT).

Performance

The explicit complexes chapter explains how a filtered complex is laid out. Against PHAT’s twist reduction, on one thread with the matrix prebuilt:

inputrandomstructured (long bars)
Rips complexes, by cohomologyphlite 7–10× fasterphlite 9–13× faster
cubical complexesPHAT 1.1–1.2× fasterPHAT 1.2–1.3× faster
triangulated surfaceslevel to PHAT 1.1× faster
sparse cell complexesphlite 1.1–2× faster
dense boundarieslevel

See the notebooks for worked examples: an image, a point cloud’s alpha-like complex, and a lower-star filtration on a mesh.