Core pipeline

The main entry point is circle_bundles.Bundle.

class circle_bundles.Bundle(X, *, U=None, cover=None, distance_matrix=False, pou=None, landmarks=None, total_metric=None, max_simp_dim=3, show_summary=False)[source]

Bases: object

Primary bundle analysis driver.

This class takes either

  • a minimal Cover object (holding U, optional pou, and optional landmarks), or

  • a raw membership matrix U,

together with total-space data X (either a point cloud or a distance matrix). It then:

  1. Builds the nerve simplices (edges/triangles/tetrahedra) directly from U.

  2. Computes local trivializations and an O(2) cocycle on overlaps.

  3. Computes characteristic-class representatives and edge-driven persistence.

  4. Optionally computes global bundle-map coordinates and solver summaries.

Parameters:
  • X (np.ndarray) – Either a point cloud of shape (n_samples, D) (if distance_matrix=False), or a distance matrix of shape (n_samples, n_samples) (if distance_matrix=True).

  • U (Optional[np.ndarray]) – Boolean membership matrix of shape (n_sets, n_samples). Entry U[j, i] indicates whether sample i belongs to chart/set j. Provide exactly one of U or cover.

  • cover (Optional['Cover']) – Optional Cover object holding U and (optionally) pou and landmarks. If provided, its fields are used as defaults. Provide exactly one of U or cover.

  • distance_matrix (bool) – If True, interpret X as a distance matrix. If False, interpret X as a point cloud.

  • pou (Optional[np.ndarray]) – Optional partition of unity of shape (n_sets, n_samples). If cover is provided, this overrides cover.pou for this Bundle (and does not mutate the cover).

  • landmarks (Optional[np.ndarray]) – Optional landmark coordinates of shape (n_sets, dB) used by visualization helpers. If cover is provided, this overrides cover.landmarks for this Bundle.

  • total_metric (Optional[object]) – Optional metric object passed through to local-trivialization computations. Only used when distance_matrix=False.

  • max_simp_dim (int) – Maximum simplex dimension to precompute from U. Common values are 1, 2, or 3.

  • show_summary (bool)

Notes

This object caches intermediate results. Any method that changes upstream state invalidates downstream caches (e.g., computing new local trivializations clears class/persistence caches).

summary(modes=None)[source]

Display any summaries that are currently available and return them in a dict.

By default (modes=None), this displays: - "nerve" (always available), - "local_triv" if get_local_trivs() has been run, - "classes" if get_classes() has been run, - "bundle_map" if get_bundle_map() has been run.

Parameters:

modes (Iterable[Literal['nerve', 'local_triv', 'classes', 'bundle_map']] | None) – Iterable selecting which summaries to display. If None, uses the default policy above.

Returns:

Mapping from summary name to summary object (some entries may be None if unavailable).

Return type:

dict

get_local_trivs(*, cc='pca2', min_patch_size=10, min_points_edge=5, pou=None, show_summary=False, verbose=True)[source]

Compute local trivializations, estimate an O(2) cocycle, and compute diagnostics.

This is the upstream computation needed by most later steps: transitions/cocycle estimation and bundle-quality diagnostics.

Parameters:
  • cc (object) – Local coordinate constructor / charting method passed to compute_local_triv() (e.g. "pca2").

  • min_patch_size (int) – Minimum number of samples required to compute a local trivialization on a chart.

  • min_points_edge (int) – Minimum overlap size |U_j U_k| required to include an edge (j,k) in cocycle estimation.

  • pou (ndarray | None) – Optional partition-of-unity override used for computing quality diagnostics. If omitted, uses self.pou when available. This does not overwrite self.pou.

  • show_summary (bool) – If True, display the local-trivialization summary after computing (auto rendering).

  • verbose (bool) – Verbosity forwarded to the local trivialization routine.

Returns:

Container holding local trivializations, cocycle, and a quality report.

Return type:

LocalTrivsResult

Raises:

ValueError – If shapes are inconsistent (e.g. invalid pou shape).

Notes

Calling this method invalidates downstream caches (classes, global trivialization, bundle map).

get_classes(*, edge_weights=None, prefer_edge_weight='rms', show_classes=False, show_persistence=False, show_rounding_distance=False)[source]

Compute characteristic-class representatives and their persistence.

This method computes discrete representatives of the characteristic classes associated to the estimated bundle cocycle (e.g. Euler class, orientation data), and studies their stability under a filtration of the nerve of the cover.

The filtration is induced by assigning weights to edges of the nerve, typically derived from local trivialization quality (e.g. RMS angular error). Persistent cohomology is then used to identify robust class representatives and to restrict the computation to a well-supported subcomplex.

The result includes:
  • raw characteristic-class representatives on the full nerve,

  • persistent cocycle data with respect to the edge-weight filtration,

  • restricted class data computed on the induced persistent subcomplex,

  • a concise human-readable summary.

Local trivializations and an O(2)-valued cocycle must already be available; these are computed automatically by get_local_trivs() if needed.

Parameters:
  • edge_weights (dict[(int, int), float], optional) – Explicit edge weights for the nerve filtration. Keys should be unordered vertex pairs (i, j). If not provided, weights are inferred from bundle quality metrics (see prefer_edge_weight).

  • prefer_edge_weight ({"rms"}, default="rms") – Which quality-derived edge weight to use when edge_weights is not provided. Currently only RMS angular error is supported.

  • show_classes (bool, default=False) – If True, display the computed characteristic-class representatives.

  • show_persistence (bool, default=False) – If True, display class persistence information

  • show_rounding_distance (bool, default=False) – If True, include rounding-distance diagnostic when displaying summaries.

Returns:

An object containing:
  • reps: characteristic-class representatives on the full nerve,

  • persistence: persistence data for cocycles under the filtration,

  • restricted: class data recomputed on the persistent subcomplex,

  • summary_text: a concise textual summary suitable for logging or documentation output.

Return type:

ClassesAndPersistence

Notes

This method resets any cached global trivialization or bundle map, since class computation may change the effective subcomplex used for downstream constructions.

get_global_trivialization(weight=None, *, pou=None)[source]

Compute a global circle-valued coordinate using the Singer construction.

This method produces a global fiber coordinate (an \(\mathbb{S}^1\)-valued coordinatization) by solving a Singer-type global alignment problem on a certified orientable 1-skeleton, and then blending local angles using a partition of unity.

The returned array F is a global coordinate representation in \(\mathbb{R}^2\) (e.g., cosine/sine embedding), suitable for downstream visualization or learning.

Parameters:
  • weight (float | None) – Optional edge-weight threshold used to further restrict the certified max-trivial subcomplex. If None, the maximal-trivial cutoff is used. If provided, it must be less than or equal to the maximal-trivial threshold; otherwise an error is raised.

  • pou (ndarray | None) – Optional partition-of-unity override of shape (n_sets, n_samples).

Returns:

Global fiber coordinate array of shape (n_samples, 2) (degree-normalized).

Return type:

numpy.ndarray

Raises:
  • RuntimeError – If prerequisites have not been computed (see above), or if no partition of unity is available via pou or self.pou.

  • ValueError – If the maximal-trivial certified subcomplex is empty, if the chosen threshold yields no usable edges, if the cocycle is non-orientable on the selected subcomplex, or if weight exceeds the maximal-trivial threshold.

get_frame_dataset(*, pou=None, weight=None, packing='coloring2')[source]

Build the pre-projection frame dataset used by the bundle-map solver.

This method constructs the intermediate “frame” representation used by the bundle-map pipeline before any projection/reduction step.

Parameters:
  • pou (ndarray | None) – Optional partition-of-unity override of shape (n_sets, n_samples). If omitted, uses self.pou. The override does not overwrite self.pou.

  • weight (float | None) – Optional edge-weight threshold used to restrict the cocycle-certified subcomplex. If None, the full cocycle-certified subcomplex is used. If provided, it must be <= the cocycle-certification threshold (the largest weight at which the class representatives still certify as cocycles).

  • packing (Literal['none', 'coloring', 'coloring2']) – Frame-packing strategy used when assembling the frame dataset (implementation-defined). Typical values include "coloring2".

Returns:

A frame dataset object produced by the internal v2 pipeline (implementation-defined type). The returned dataset corresponds to stage="pre_projection".

Return type:

object

Raises:
  • RuntimeError – If prerequisites have not been computed, or if no partition of unity is available.

  • ValueError – If the cocycle-certified subcomplex is empty, or if weight exceeds the cocycle certification threshold, or if thresholding yields no edges.

compare_trivs(*, ncols='auto', title_size=14, align=False, s=1.0, save_path=None, max_pairs=25, metric='mean', show=True, return_selected=False, min_points_edge=1, edges=None)[source]

Compare local circular coordinates on overlaps.

This method produces a static matplotlib diagnostic figure showing pairs of local fiber coordinates on chart overlaps, optionally aligning the second chart to the first by an O(2) fit.

Notes

  • If there are more than max_pairs overlaps, the visualizer selects a subset according to metric (typically WORST / MEDIAN / BEST).

Parameters:
  • ncols (int | str) – Number of columns in the comparison grid, or "auto" for an automatic layout.

  • title_size (int) – Font size for subplot titles.

  • align (bool) – If True, align the second chart to the first on each overlap using an O(2) fit (useful when comparing angles up to a global reflection/rotation on overlaps).

  • s (float) – Marker size scaling for scatter plots.

  • save_path (str | None) – Optional path to save the figure (e.g., "compare_trivs.png").

  • max_pairs (int) – Maximum number of overlap pairs to display.

  • metric (str) –

    Overlap scoring metric used to rank/select pairs. Common values are:

    • "mean": mean circle-fit / angle disagreement on the overlap

    • "rms": RMS circle-fit / angle disagreement on the overlap

  • show (bool) – If True, display the figure (matplotlib). If False, return the figure without display.

  • return_selected (bool) – If True, also return diagnostics about which overlaps were displayed.

  • min_points_edge (int) – Minimum overlap size required to include an edge when edges is not provided.

  • edges (List[Tuple[int, int]] | None) – Optional explicit list of chart-index edges [(j, k), ...] to consider. If provided, this overrides min_points_edge.

Returns:

If return_selected=False (default), returns the matplotlib figure.

If return_selected=True, returns (fig, selected_edges, err_by_edge), where:

  • selected_edges is the list of overlaps actually displayed

  • err_by_edge maps each overlap edge to its diagnostic error value

Return type:

matplotlib.figure.Figure or tuple

Raises:

RuntimeError – If local trivializations have not been computed (call get_local_trivs() first).

show_nerve(*, landmarks=None, title=None, show_labels=True, show_axes=False, tri_opacity=0.25, tri_color='pink', cochains=None, weights=None, edge_cutoff=None, highlight_edges=None, highlight_color='red', prefer_local_weights='rms', use_slider=True, mark_cutoff=None, show_title_value=True)[source]

Visualize a single-cycle nerve in a canonical circle layout (matplotlib).

This visualization is designed for the common case where the 1-skeleton of the nerve is a single cycle graph. The vertices are arranged evenly on a circle, and edge annotations (weights, orientation data) are displayed directly on the plot.

Parameters:
  • use_max_trivial – If True and persistence results are available, highlight edges belonging to the max-trivial subcomplex (when meaningful). If False, draw all edges uniformly.

  • weights (Dict[Tuple[int, int], float] | None) – Weight-label source selector: "rms", "witness", or "none".

  • omega – Optional explicit edge-sign dict mapping (i, j) to ±1. If None, an attempt is made to pull a default from cached class representatives.

  • phi – Optional explicit vertex-sign dict mapping vertex index to ±1.

  • compute_phi – If True and phi is not provided, attempt to compute a consistent gauge from the cached cocycle. Requires local trivializations/cocycle to have been computed.

  • fail_if_not_cycle – If True (default), raise if the nerve graph is not a single cycle. If False, attempt to visualize “as-is” without canonical cycle reindexing.

  • title (str | None) – Optional plot title. Defaults to "Nerve Visualization".

  • save_path – Optional path to save the figure.

  • ax – Optional matplotlib Axes to draw into. If None, a new figure/axes are created.

  • figsize – Figure size (inches) used only when ax is None.

  • dpi – Optional DPI used only when ax is None.

  • r – Radius of the circle layout.

  • node_size – Node styling options.

  • node_facecolor – Node styling options.

  • node_edgecolor – Node styling options.

  • node_label_color – Node styling options.

  • removed_edge_color – Styling for edges not in the highlighted/kept set.

  • removed_edge_lw – Styling for edges not in the highlighted/kept set.

  • kept_edge_color – Styling for highlighted/kept edges.

  • kept_edge_lw – Styling for highlighted/kept edges.

  • omega_color – Text colors for omega/phi/weight annotations.

  • phi_color – Text colors for omega/phi/weight annotations.

  • weights_color – Text colors for omega/phi/weight annotations.

  • fontsize_node – Font sizes for node labels and annotations.

  • fontsize_omega – Font sizes for node labels and annotations.

  • fontsize_phi – Font sizes for node labels and annotations.

  • fontsize_weights – Font sizes for node labels and annotations.

  • omega_offset – Radial offsets used to place text annotations without overlapping edges.

  • weights_offset – Radial offsets used to place text annotations without overlapping edges.

  • phi_offset – Radial offsets used to place text annotations without overlapping edges.

  • landmarks (ndarray | None)

  • show_labels (bool)

  • show_axes (bool)

  • tri_opacity (float)

  • tri_color (str)

  • cochains (List[Dict[Tuple[int, ...], object]] | None)

  • edge_cutoff (float | None)

  • highlight_edges (Set[Tuple[int, int]] | None)

  • highlight_color (str)

  • prefer_local_weights (Literal['rms', 'none'])

  • use_slider (bool)

  • mark_cutoff (float | None)

  • show_title_value (bool)

Returns:

The matplotlib figure.

Return type:

matplotlib.figure.Figure

Raises:
show_circle_nerve(*, use_max_trivial=True, weights='rms', omega=None, phi=None, compute_phi=True, fail_if_not_cycle=True, title=None, save_path=None, ax=None, figsize=(5.0, 5.0), dpi=None, r=1.0, node_size=600, node_facecolor='lightblue', node_edgecolor='k', node_label_color='k', removed_edge_color='lightgray', removed_edge_lw=1.5, kept_edge_color='black', kept_edge_lw=4.0, omega_color='blue', phi_color='red', weights_color='black', fontsize_node=12, fontsize_omega=12, fontsize_phi=12, fontsize_weights=9, omega_offset=0.09, weights_offset=0.09, phi_offset=0.14)[source]

Visualize the nerve when it is a single cycle.

This helper draws the 1-skeleton of the cover nerve in a canonical circular layout and can optionally annotate it with:

  • kept edges from a persistence-derived “max trivial” subcomplex,

  • an edge cochain omega (typically the orientation/monodromy data),

  • a vertex cochain phi taking values in {±1} (an orientation gauge),

  • edge weights used for the filtration (e.g. RMS transition error).

The function is intended for the common situation where the nerve is a cycle graph (e.g. a good cover of a base circle), providing a compact diagnostic view of which edges are trusted and what twisting data is present.

Parameters:
  • use_max_trivial (bool, default=True) – If True and persistence data is available (from get_classes()), highlight the edges kept by the “max_trivial” persistent subcomplex. Kept edges are drawn with thicker styling.

  • weights ({"rms", "witness", "none"}, default="rms") – Which edge weights to display on the cycle. If “none”, no edge weights are shown. Otherwise, weights are pulled from the latest available source (persistence edge weights when available, else quality-derived).

  • omega (dict[(int, int), int], optional) – Edge labels to display (e.g. values in {0,1} or {±1} depending on convention). Keys should be unordered pairs (i, j). If not provided, the method attempts to use reps.omega_O1_used from the most recently computed class representatives.

  • phi (dict[int, int], optional) – Vertex labels (typically ±1) to display. If not provided and compute_phi=True, an attempt is made to compute phi by orienting the current cocycle along either the kept edges (if available) or all cycle edges.

  • compute_phi (bool, default=True) – Whether to attempt automatic computation of phi when phi is not provided. Requires that a cocycle has been computed via get_local_trivs().

  • fail_if_not_cycle (bool, default=True) – If True, raise a ValueError when the nerve is not a single cycle graph. If False, the method will still attempt a plot using the given edge set (without enforcing cycle ordering).

  • title (str, optional) – Title for the plot. Defaults to “Nerve Visualization”.

  • save_path (str, optional) – If provided, save the figure to this path.

  • ax (matplotlib Axes, optional) – Existing axes to draw into. If None, a new figure/axes are created.

  • figsize ((float, float), default=(5.0, 5.0)) – Matplotlib figure size in inches (used when ax is None).

  • dpi (int, optional) – DPI for the created figure (used when ax is None).

  • r (float, default=1.0) – Radius of the circular layout.

  • node_size (float) – Styling options for vertices and their labels.

  • node_facecolor (str) – Styling options for vertices and their labels.

  • node_edgecolor (str) – Styling options for vertices and their labels.

  • node_label_color (str) – Styling options for vertices and their labels.

  • removed_edge_color (str) – Styling for edges not in kept_edges (when use_max_trivial=True).

  • removed_edge_lw (float) – Styling for edges not in kept_edges (when use_max_trivial=True).

  • kept_edge_color (str) – Styling for kept edges.

  • kept_edge_lw (float) – Styling for kept edges.

  • omega_color (str) – Text colors for omega/phi/weight annotations.

  • phi_color (str) – Text colors for omega/phi/weight annotations.

  • weights_color (str) – Text colors for omega/phi/weight annotations.

  • fontsize_node (int) – Font sizes for node labels and annotations.

  • fontsize_omega (int) – Font sizes for node labels and annotations.

  • fontsize_phi (int) – Font sizes for node labels and annotations.

  • fontsize_weights (int) – Font sizes for node labels and annotations.

  • omega_offset (float) – Radial offsets for drawing omega/weights/phi text relative to the cycle.

  • weights_offset (float) – Radial offsets for drawing omega/weights/phi text relative to the cycle.

  • phi_offset (float) – Radial offsets for drawing omega/weights/phi text relative to the cycle.

Returns:

The created figure (or the figure associated with ax).

Return type:

matplotlib.figure.Figure

Raises:
  • RuntimeError – If nerve edge data is missing (no stored nerve edges), or if phi is requested to be computed but no cocycle is available.

  • ValueError – If the nerve is not a single cycle graph and fail_if_not_cycle=True.

Notes

When the nerve is a cycle, vertices and annotations are reindexed to follow the cyclic order for a clean, readable layout.

get_simplices(dims=None, *, weight=None, vertices_as_tuples=False, canonicalize=True, prefer_edge_weight='rms')[source]

Return cached simplices of the nerve, optionally filtered by weight or persistence stage.

This method provides access to the simplices of the nerve of the cover (vertices, edges, triangles, and tetrahedra) that were computed and cached during bundle construction. The returned simplices can be restricted either by an explicit edge-weight threshold or by a named persistence stage derived from class computation.

Parameters:
  • dims (int or iterable of int, optional) – Which simplex dimensions to return. For example: 0 for vertices, 1 for edges, (1, 2) for edges and triangles. If None, simplices of all available dimensions are returned.

  • weight (float or {"cocycle", "coboundary"}, optional) –

    Restrict simplices using either:

    • a numeric edge-weight cutoff (float), keeping only simplices whose constituent edges have weight ≤ weight; or

    • a named persistence stage (string), interpreted as:

      • "cocycle": the maximal stage where the class representatives are cocycles;

      • "coboundary": alias for the former "max_trivial" persistence stage.

    If None, no filtering is applied and the full cached nerve is returned.

  • vertices_as_tuples (bool, default=False) – If True, return vertices as singleton tuples (i,) rather than integers i. This can be useful for uniform handling of simplices across dimensions.

  • canonicalize (bool, default=True) – If True, return simplices in canonical sorted order with unique orientation conventions. If False, preserve the internal ordering as stored.

  • prefer_edge_weight (str, default="rms") – When weight is given as a string stage name, specifies which edge-weight source to prefer if multiple are available (e.g. RMS angular error).

Returns:

A dictionary (or nested structure) mapping each requested dimension to the corresponding list of simplices, with the exact structure depending on dims and formatting options.

Return type:

Any

Notes

Filtration convention. Vertices (0-simplices) are always assigned weight 0. Consequently, this method always returns the full vertex set {0, …, n_sets−1} (or singleton tuples), regardless of the value of weight. Only edges, triangles, and higher simplices are subject to filtering.