mixle.utils.hvis.topology module

The fuzzy nerve of the learned cover, and map-fidelity receipts (design review R3/R2, partial).

The fitted mixture is a parametrized cover of the data manifold: components are cover elements and the posterior is a partition of unity subordinate to it. By the nerve theorem, the topology of the data (for a good cover) is the homotopy type of the cover’s NERVE – the simplicial complex whose k-simplices are (k+1)-wise overlapping components. fuzzy_nerve() computes its weighted 1- and 2-skeleton directly from the posteriors (this is soft Mapper with an EM-learned cover), and nerve_report() turns it into receipts a 2-D map cannot give on its own:

  • holes – an unfilled cycle of strongly-overlapping components (e.g. a ring-shaped manifold) is a loop in the data’s topology. A plane embedding may render it faithfully or may distort it; either way the user should KNOW the loop exists rather than infer it from blob positions.

  • disconnection – a cover that splits into multiple connected pieces.

Cycle classification is deliberately conservative: a 3-cycle is “filled” exactly when its triple overlap is strong (the 2-simplex is present); longer cycles are reported as candidate holes without attempting a full triangulation check (exact filling is a combinatorial problem; the honest output is “here is a cycle no strong triple fills directly”).

embedding_health() is the RENDERING-fidelity receipt: trustworthiness/continuity of the map’s neighborhoods against the model affinity that produced it. Note precisely what this does and does not audit – it catches a layout that misrepresents the model (rendering failure), NOT a model that misrepresents the data (model misfit, which remains the design review’s open R2 item).

component_tree(nerve)[source]

Single-linkage merge tree over components by nerve edge weight – the hierarchy skeleton.

Merges are emitted strongest-overlap-first: [{"a": frozenset, "b": frozenset, "weight": w, "merged": frozenset}, ...]. Cutting the tree at any weight gives coarse super-components (mixle.utils.hvis.front.Map uses it for zoom groups); the merge order is itself a receipt – which regimes are almost one regime.

Parameters:

nerve (dict)

Return type:

list[dict]

embedding_health(coords, mix_model, data, *, affinity='auto', k=10, field_weights=None, evidence_cap=1.0, max_rows=400, seed=0)[source]

Rendering-fidelity receipt: do the map’s neighborhoods agree with the model affinity?

Standard trustworthiness (are map-neighbors genuinely close under the model?) and continuity (are model-neighbors kept close in the map?), computed on a row subsample. This audits the LAYOUT against the MODEL – a low score means the picture misrepresents the affinities that produced it (bad init, unconverged optimizer, non-embeddable topology). It does NOT audit the model against the raw data; that receipt is still open (design review R2).

Parameters:
Return type:

dict

fuzzy_nerve(z, *, edge_threshold=0.02, triangle_threshold=0.02)[source]

Weighted 1- and 2-skeleton of the cover’s nerve, from the posteriors alone.

Edge weight w(k,l) = sum_i z_ik z_il / min(mass_k, mass_l) – the co-claimed fraction of the smaller component’s mass (1 when one component’s points are entirely co-claimed by the other, 0 when they never co-claim). Triangle weight is the same with a triple product. Simplices at or above their threshold are “strong” and drive nerve_report(); all nonzero weights are returned so thresholds are inspectable choices, not hidden ones.

Parameters:
Return type:

dict

model_fit_health(mix_model, data, *, holdout=None, field_weights=None, coverage_q=0.9, merged_sep_threshold=None, shattered_weight=0.5, min_component_points=20)[source]

The model<->data receipt (design review R2, second half): does the FITTED MODEL describe the data it is about to be a map of? Measured from the model’s own residual structure – no raw feature space is assumed, which is the whole point of HViS.

  • fiber calibration – per component, the squared Mahalanobis of its dominant points’ whitened fiber coordinates should look chi-squared: the fraction inside the coverage_q ball is compared against coverage_q. A large gap means the component’s shape claim is wrong (too wide, too narrow, or mis-shaped).

  • merged-regime detector – a deterministic 2-means split (top-PC sign init) of each component’s dominant fiber coordinates; a separation ratio above the threshold with a non-trivial minority says one component is covering what the data treats as two regimes (K too small). The threshold has a derivation plus a measured finite-sample correction: for a UNIMODAL normal the population statistic is 2 E|x| / sqrt(1 - 2/pi) ~ 2.65 regardless of scale, but at n=40 sample noise inflates it to ~3.4 (observed), so the default threshold is 2.65 + 6/sqrt(n) – ~3.6 at n=40, tightening toward the population value as n grows. Two unit-variance regimes 4 sigma apart score ~4.0 either way. Pass an explicit merged_sep_threshold to pin it.

  • shattered detector – nerve edges with weight >= shattered_weight are near-duplicate components claiming largely the same points (K too large).

  • held-out check – with holdout data, a mean log-density drop > 1 nat vs training is flagged (memorization / drift).

Parameters:
  • coverage_q (float)

  • merged_sep_threshold (float | None)

  • shattered_weight (float)

  • min_component_points (int)

Return type:

dict

nerve_report(nerve)[source]

Topology receipts from a fuzzy_nerve(): connected pieces, cycles, and candidate holes.

A hole is an independent cycle of strong edges not directly filled by a strong 2-simplex (3-cycles are checked exactly; longer cycles are conservatively reported as candidates). The diagnosis strings are the user-facing half: a loop in the cover is real data topology that a 2-D layout may distort silently.

Parameters:

nerve (dict)

Return type:

dict