Rhomboid Bifiltration (multicover)

The Rhomboid Bifiltration is a topologically equivalent bifiltration to the multicover bifiltration, introduced in [Computing the Multicover Bifiltration], whose code is available here.

import numpy as np
import gudhi as gd
import multipers as mp
import matplotlib.pyplot as plt

Definition

Let \(P\) be a point cloud in some metric space \((X,d)\). The multicover bifiltration is the bifiltration \(F\) indexed over \(\mathbb{R} \times \mathbb{N}^{\mathrm{op}}\), defined as follows:

\[\begin{split} \begin{align} \forall (r,k)\in \mathbb{R} \times \mathbb{N}^{\mathrm{op}}, \quad & F_{r,k}=\bigcup_{\substack{i_1, \dots, i_k\\ \forall j_1\neq j_2, i_{j_1} \neq i_{j_2}}} \bigcap_{j=1}^k B(p_{i_j}, r)\\ &= \left\{ x\in X \mid \exists k \textnormal{ distinct points } (p_i)_{1\le i \le k} \in P, \textnormal{ s.t. } \max_i d(p_i, x)\le r\right\} \end{align} \end{split}\]

Note that this filtration is not 1-critical/free. The Rhomboid Tiling bifiltration is a topologically equivalent 1-critical bifiltration, that we do not develop here.

We identify \(\mathbb N^{\mathrm{op}}\) with \(-\mathbb N\).

A first example

np.random.seed(0)
x = mp.data.noisy_annulus(100,0, dim=2) # dataset
plt.scatter(*x.T)
k_max = 30 # the maximum of ball intersection to look at
degree=1 # homological degree
../_images/5cdb6db0aa7aa1b8c8c71817b110286e07f6b6b52d2fd7d5f767b277b9714422.png
s = mp.filtrations.RhomboidBifiltration(x, k_max=k_max, degree=degree)
len(s)
715517

This filtration is very large in general ; before computing an invariant it is generally recommended to simplify it, via a sequence of squeezes or minimal presentations.

multicover = (s
    .grid_squeeze(
        # resolution=200, # coarsens the filtration grid
        # strategy="regular_closest", 
        threshold_min=[0, -k_max], # thresholds the filtration values
        threshold_max=[4, 0],
    )
    .minpres(degree) # mpfree
    .astype(vineyard=True) # for mma
)
len(multicover)
59380

Hilbert Function

The signed barcodes of this filtration are not very readable

sm, = mp.signed_measure(multicover, degree=degree, plot=True);
../_images/47585eedce9563aec942516f665f28f02f38dd77a772bf4e0bfd7538d9c50aca.png

The induced Hilbert function suggest a significant cycle

mp.point_measure.integrate_measure(*sm, plot=True);
../_images/9709295c58ec1dd3c431d0365660df32e33b3c72509256542157b2c27343e929.png

Module approximation

mod = mp.module_approximation(multicover)
mod.plot()
/var/folders/w6/5k5w13s94bq0dfsx2xzqxcsh0000gn/T/ipykernel_62909/2670604959.py:1: UserWarning: (copy warning) Got a squeezed input. 
  mod = mp.module_approximation(multicover)
../_images/8bff723312872fbabb93732ccbf41d054e06391b736e27e6cd5ea007904d899b.png

This is a bit bloated, so we can just plot the summand larger than a given threshold. We clearly recover the inital cycle here.

mod.plot(min_persistence = .1)
../_images/bd56ba6c7e1d53267a2de60c91653a9171956ad9d137c08f26102ccd8533c1db.png

Another alternative is to look at a representation of MMA, which weights the summands w.r.t. their “size”.

mod.representation(plot=True, degrees=[1]);
../_images/dee0ceaed298886c4dfc5f1a2d04c95aad9c41aaa7024f2d09c0af3e5f7a7dc6.png

Scaling

As far as I tested, this filtration doesn’t scale much more than with a few hundreds of points.