Explain a distance through its matching
Download the executed notebook · Build and environment record
All figures below are saved results; reading this page needs no Julia session. Select a figure to view it at full size.
Suppose two computations have given us barcodes, and their bottleneck distance is one. Which intervals account for that value? We will retain the matching, inspect its costs, and check the result against a small calculation.
Use a Julia environment with TamerOp and CairoMakie; the installation page explains setup. The inputs below are intervals in an increasing parameter, with the same units. If your inputs are ordinary persistence diagrams, first choose the same homological degree and compatible parameter conventions; see ordinary persistence.
using TamerOp, CairoMakie
const OP = TamerOp
const OA = TamerOp.Advanced
TamerOp.AdvancedRetain the assignment
Barcode A has two copies of [0,2), one short interval [8,10), and an essential interval born at four. Barcode B has two copies of [0,3) and an essential interval born at five. The copies are separate members: multiplicity two means two classes, even when the picture puts their points in the same place.
a = [(0.0, 2.0), (0.0, 2.0), (8.0, 10.0), (4.0, Inf)]
b = [(0.0, 3.0), (0.0, 3.0), (5.0, Inf)]
witness = OA.bottleneck_matching(a, b)
@assert witness.distance == 1
witness.distance
1.0Match the two [0,2) members to the two [0,3) members, at cost one each. Send [8,10) to the diagonal, at half its length: again one. Match the essential intervals at their birth difference, one. These choices give an upper bound of one; the essential birth difference supplies the matching lower bound. Thus the distance is exactly one.
The engine supplies an assignment attaining that value. It need not be the only optimal assignment. In particular, exchanging the equal copies gives no meaningful identification between the original classes.
OP.visualize(witness)
The dashed connector ends on the birth–death diagonal. In the barcode panel, the empty partner row tells the same story: the interval is discarded, rather than paired to a member of the other barcode. The infinity rail is only a drawing convention. The matching still uses an infinite death endpoint.
late_pair = findfirst(==((8.0, 10.0)), witness.points_a)
@assert witness.a_to_b[late_pair] == 0
OP.visualize(witness; pair=late_pair)
Check a change
Remove the essential member from B. A finite interval cannot replace it at finite bottleneck cost, nor can it be discarded to the diagonal at finite cost. Predict the answer before running the cell.
finite_b = b[1:2]
mismatch = OA.bottleneck_matching(a, finite_b)
@assert isinf(mismatch.distance)
mismatch.distance
InfAn infinite distance is the answer for these inputs. Dropping essential intervals would ask a different question. For ordinary diagrams, inspection_session(diagram_a, diagram_b; dim=1) uses the same degree-aware matching owner and exposes essential policies explicitly.
Inspect the members interactively
The static figure already explains the result. In a live Julia notebook, the following optional cell opens linked charts. Click a point or barcode row to select its pair; repeated clicks on coincident points cycle their distinct members. The pair-ID field also reaches members omitted by a display budget. Hover reads retained costs without recomputing persistence or optimizing a new matching.
using WGLMakie
session = OP.inspection_session(witness)
OP.visualize(session; backend=:wglmakie)Keep Julia running for the controls. For a static figure of the selected view, use OP.visualize(OA.inspection_snapshot(session); backend=:cairomakie). Closing the inspector releases its live inputs and callbacks; the final snapshot remains available.
For two finite encodings, the finite-window matching guide explains how to compare the same slice, explore a sample family, and request an exact optimizer witness. Matching intervals is a comparison of summaries; it does not identify source cycles or compute an interleaving between the original modules.

