Complex

class relucent.Complex(net: Module)

Bases: object

Manages the polyhedral complex of a neural network.

This class provides methods for calculating, storing, and searching the h-representations (halfspace representations) of polyhedra in the complex.

static load(filename: str | PathLike[str]) Complex

Load a Complex from a pickle file.

Intended to be called as Complex.load(filename). The file must have been created by save().

Parameters:

filename – Path to the pickle file.

Returns:

The restored complex.

Return type:

Complex

add_point(data: Tensor | ndarray, check_exists: bool = True, **kwargs: Any) Polyhedron

Find the polyhedron containing a data point and add it to the complex.

Parameters:
  • data – A single data point as a torch.Tensor, np.ndarray, or array-like.

  • check_exists – If True, check whether the polyhedron already exists in the complex and return it if so. Only set to false if you know it does not. Defaults to True.

  • **kwargs – Additional arguments passed to the Polyhedron constructor.

Returns:

The polyhedron containing the given point, now stored in the complex.

Return type:

Polyhedron

add_polyhedron(p: Polyhedron, overwrite: bool = False, check_exists: bool = True) Polyhedron

Add a Polyhedron to the complex.

Parameters:
  • p – The Polyhedron object to add.

  • overwrite – If True and the polyhedron already exists, replace it with the new one. Defaults to False.

  • check_exists – If True, check whether the polyhedron already exists in the complex and return the existing one if so. If False, assume the polyhedron is new (skip the check). Defaults to True.

Returns:

The polyhedron that was added (or already existed) in the complex.

Return type:

Polyhedron

add_ss(ss: ndarray | Tensor, check_exists: bool = True, **kwargs: Any) Polyhedron

Convert a sign sequence to a Polyhedron and add it to the complex.

Parameters:
  • ss – A sign sequence as a torch.Tensor or np.ndarray.

  • check_exists – If True, return the existing polyhedron from the complex if it already exists. Defaults to True.

  • **kwargs – Additional arguments passed to the Polyhedron constructor.

Returns:

The polyhedron that was added (or already existed) in the complex.

Return type:

Polyhedron

bfs(**kwargs: Any) dict[str, Any]

Perform breadth-first search of the complex.

Explores the complex using a breadth-first strategy, discovering all polyhedra at depth d before moving to depth d+1. Uses a FIFO queue.

Parameters:

**kwargs – All arguments accepted by searcher().

Returns:

Search information dictionary (see searcher() documentation).

Return type:

dict

clean_data() None

Clean cached data from all polyhedra in the complex.

This method calls clean_data() on each polyhedron, which removes most of their computed data.

compute_geometric_properties(nworkers: int | None = None, get_volumes: bool = True, verbose: int = 1) dict[str, Any]

Compute geometric caches (center/inradius/interior-point/etc.) in parallel.

This is intended to run after a topology-only search pass.

contract() Complex

Contract the maximal cells in the complex.

dfs(**kwargs: Any) dict[str, Any]

Perform depth-first search of the complex.

Explores the complex using a depth-first strategy, following paths as deeply as possible before backtracking. Uses a LIFO queue.

Parameters:

**kwargs – All arguments accepted by searcher().

Returns:

Search information dictionary (see searcher() documentation).

Return type:

dict

get_betti_numbers() dict[int, int]

Compute Betti numbers of the complex over GF(2).

Uses get_chain_complex() to obtain k-cells by contracting from top-dimensional cells. The boundary operators are built from dual-graph incidences at each chain level, then Betti numbers are computed as beta_k = dim(C_k) - rank(∂_k) - rank(∂_{k+1}).

get_boundary_cells(i: int) set[Polyhedron]

Get all (d-1)-cells in neuron i’s BH.

get_boundary_complex(i: int) Complex

Get the boundary complex of neuron i.

get_boundary_edges(i: int) set[tuple[Polyhedron, Polyhedron]]

Get the boundary of neuron i by returning the set of edges in the dual graph with label i.

get_boundary_graph(i: int) Graph

Get the induced subgraph of neuron i’s BH.

get_chain_complex() list[Complex]

Get the chain complex of the complex.

get_dual_graph(auto_add: bool = False, relabel: bool = False, plot: bool = False, node_color: str | None = None, node_size: str | None = None, cmap: str = 'viridis', match_locations: bool = False, show_node_labels: bool = False, show_edge_labels: bool = False) Graph

Construct the dual graph of the complex.

The dual graph represents the connectivity structure of the complex, where nodes are polyhedra and edges connect adjacent polyhedra (those sharing a supporting hyperplane).

Parameters:
  • auto_add – If True, add polyhedra to the complex if they are not already present. Defaults to False.

  • relabel – If True, nodes are indexed by integers matching self.index2poly indices. If False, nodes are Polyhedron objects. Defaults to False.

  • plot – If True, prepare the graph for visualization with pyvis by adding layout and styling attributes. Defaults to False.

  • node_color – If “Wl2”, color nodes by their Wl2 (weight norm) value. If “volume”, color by volume. If None, no special coloring. Defaults to None.

  • node_size – If “volume”, size nodes proportionally to their volume. If None, use default size. Defaults to None.

  • cmap – Colormap to use when node_color is specified. Defaults to “viridis”.

  • match_locations – If True, position graph nodes at the center points of their polyhedra (only works for 2D complexes). Defaults to False.

  • show_node_labels – If True, show node labels in the graph. Defaults to False.

  • show_edge_labels – If True, show edge labels (SHI) in the graph. Defaults to False.

Returns:

The dual graph of the complex. Nodes are polyhedra

(or integers if relabel=True), edges connect adjacent polyhedra and have a “shi” attribute indicating which supporting hyperplane they cross.

Return type:

networkx.Graph

Raises:

ValueError – If match_locations is True and the complex is not 2D.

get_poly_attrs(attrs: Iterable[str]) dict[str, list[Any]]

Extract specified attributes from all polyhedra in the complex.

Useful for building tabular representations or dataframes of polyhedra properties. Attributes are returned in the same order as polyhedra were added to the complex.

Parameters:

attrs – A list of attribute names to extract (e.g., [“finite”, “Wl2”]).

Returns:

A dictionary mapping attribute names to lists of attribute

values, with one value per polyhedron in the complex based on the order they were added.

Return type:

dict

greedy_path(start: Tensor | ndarray | Polyhedron, end: Tensor | ndarray | Polyhedron) list[Polyhedron] | None

Greedily find a path between two data points.

Attempts to find a path through adjacent polyhedra from start to end using a greedy strategy. This method can be slow for large complexes as it explores many paths.

Parameters:
  • start – Starting data point as torch.Tensor or np.ndarray.

  • end – Ending data point as torch.Tensor or np.ndarray.

Returns:

A list of Polyhedron objects representing the path

from start to end, or None if no path is found.

Return type:

list or None

hamming_astar(start: Tensor | ndarray | Polyhedron, end: Tensor | ndarray | Polyhedron, nworkers: int | None = None, bound: float | None = None, max_polys: float = inf, show_pbar: bool = True, num_threads: int = 1, **kwargs: Any) dict[str, Any]

Find a path between two data polyhedra using the A* search algorithm.

Uses the A* pathfinding algorithm with a heuristic based on Hamming distance between sign sequences, plus Euclidean distance between interior points to break ties. The heuristic should be admissible for optimal paths.

Parameters:
  • start – Starting data point as torch.Tensor or np.ndarray.

  • end – Ending data point as torch.Tensor or np.ndarray.

  • nworkers – Number of worker processes for parallel computation. Defaults to 1.

  • bound – Constraint radius for numerical stability when computing halfspaces. Important for numerical stability. Defaults to config.DEFAULT_SEARCH_BOUND.

  • max_polys – Maximum number of polyhedra to explore during search. Defaults to infinity.

  • show_pbar – Whether to display a progress bar. Defaults to True.

  • **kwargs – Additional arguments passed to get_shis().

Returns:

Dictionary containing the path (if found) and

additional diagnostics/bounds.

Return type:

dict[str, Any]

Raises:

ValueError – If the start point lies exactly on a neuron’s boundary.

parallel_add(points: Iterable[Tensor | ndarray], nworkers: int | None = None, bound: float | None = None, **kwargs: Any) list[Polyhedron | None]

Add multiple polyhedra from data points using parallel processing.

Processes a batch of data points in parallel, computing their corresponding polyhedra and adding them to the complex.

Parameters:
  • points – A list or iterable of data points (each as torch.Tensor or np.ndarray).

  • nworkers – Number of worker processes to use. If None, uses the number of CPU cores. Defaults to None.

  • bound – Constraint radius for numerical stability when computing halfspaces. Defaults to config.DEFAULT_PARALLEL_ADD_BOUND.

  • **kwargs – Additional arguments passed to poly_calculations() and get_shis().

Returns:

A list of Polyhedron objects (or None for failed computations)

corresponding to the input points.

Return type:

list

plot_cells(label_regions: bool = False, color: Any = None, highlight_regions: Any = None, ss_name: bool = False, bound: float | None = None, show_axes: bool = False, fill_mode: str = 'wireframe', **kwargs: Any) Figure

Plot all cells in input space; 2D vs 3D is chosen from dim.

plot_graph(label_regions: bool = False, color: Any = None, highlight_regions: Any = None, show_axes: bool = False, project: bool = True, **kwargs: Any) Figure
point2poly(point: Tensor | ndarray, check_exists: bool = True, **kwargs: Any) Polyhedron

Convert a data point to its corresponding Polyhedron.

Finds the polyhedron that contains the given data point. Does not add the polyhedron to the complex.

Parameters:
  • point – A single data point as a torch.Tensor or np.ndarray.

  • check_exists – If True, return the existing polyhedron from the complex if it already exists. Defaults to True.

  • **kwargs – Additional arguments passed to the Polyhedron constructor.

Returns:

The polyhedron containing the given point.

Return type:

Polyhedron

point2ss(batch: Tensor | ndarray) ndarray | Tensor

Convert a batch of data points to sign sequences.

Computes the combined sign sequence across all ReLU layers for the given data points. Does not add the resulting polyhedra to the complex.

Parameters:

batch – A batch of input data points as a torch.Tensor, np.ndarray, or array-like.

Returns:

The sign sequences for the input batch, with shape

(batch_size, total_ReLU_neurons). Returns a torch.Tensor if batch is a torch.Tensor, otherwise a np.ndarray.

Return type:

torch.Tensor or np.ndarray

random_walk(**kwargs: Any) dict[str, Any]

Perform random walk search of the complex.

Explores the complex by randomly selecting which polyhedron to explore next from the queue.

Parameters:

**kwargs – All arguments accepted by searcher().

Returns:

Search information dictionary (see searcher() documentation).

Return type:

dict

recover_from_dual_graph(G: Graph, initial_ss: ndarray | Tensor, source: int, copy: bool = False) None

Recover a complex from its connectivity graph.

Reconstructs polyhedra in the complex by traversing the adjacency graph of top-dimensional cells, using the supporting hyperplane indices stored on edges to determine how to flip sign sequence elements. This is useful for storing large complexes efficiently, as you only need to store the graph structure and SHI indices on edges rather than full polyhedron data.

Parameters:
  • G – A networkx.Graph representing the dual graph. Edges must have a “shi” attribute indicating the supporting hyperplane index.

  • initial_ss – The sign sequence of the starting polyhedron as torch.Tensor or np.ndarray.

  • source – The node key in G for the polyhedron with sign sequence initial_ss.

  • copy – If True, operate on a copy of G; otherwise modify G in place. Defaults to False.

Returns:

The graph with polyhedron objects stored in node

attributes under the “poly” key.

Return type:

networkx.Graph

save(filename: str | PathLike[str], save_ssm: bool = True) None

Save the complex to a pickle file.

Parameters:
  • filename – Path to the output file.

  • save_ssm – If True, include the SSManager in the saved state so that sign-sequence lookups are preserved. Defaults to True.

searcher(start: Tensor | ndarray | Polyhedron | None = None, max_depth: float = inf, max_polys: float = inf, queue: Any = None, bound: float | None = None, nworkers: int | None = None, verbose: int = 1, cube_radius: float | None = None, cube_mode: str = 'unrestricted', **kwargs: Any) dict[str, Any]

Search for polyhedra in the complex by discovering neighbors.

This is a generic search method that can be configured for different traversal strategies (BFS, DFS, random walk) by providing different queue types. It starts from a given point and explores the complex by crossing supporting hyperplanes to discover adjacent polyhedra.

See bfs(), dfs(), and random_walk() for examples of how to use this function to define specific search strategies.

Parameters:
  • start – Starting point (torch.Tensor / np.ndarray / array-like) or a Polyhedron, or None (defaults to origin). Defaults to None.

  • max_depth – Maximum search depth (number of hyperplane crossings). Defaults to infinity.

  • max_polys – Maximum number of polyhedra to discover. Defaults to infinity.

  • queue – Queue object that defines the order in which polyhedra are searched. Must have push() and pop() methods. If None, uses BlockingQueue (FIFO). Defaults to None.

  • bound – Constraint radius for numerical stability when computing halfspaces. Important for numerical stability. Defaults to config.DEFAULT_SEARCH_BOUND.

  • nworkers – Number of worker processes for parallel computation. If None, uses the number of CPU cores. Defaults to None.

  • verbose – Whether to print progress information. Defaults to 1.

  • **kwargs – Additional arguments passed to get_shis().

Returns:

Search information dictionary containing:
  • ”Search Depth”: Maximum depth reached

  • ”Avg # Facets Uncorrected”: Average number of facets per polyhedron

  • ”Search Time”: Elapsed time in seconds

  • ”Bad SHI Computations”: List of failed computations

  • ”Complete”: Whether search completed (no unprocessed items)

Return type:

dict

Raises:

ValueError – If the start point lies on a hyperplane (has zero in SS).

ss2poly(ss: ndarray | Tensor, check_exists: bool = True, **kwargs: Any) Polyhedron

Convert a sign sequence to a Polyhedron.

Creates a Polyhedron object from the given sign sequence. Does not add it to the complex.

Parameters:
  • ss – A sign sequence as a torch.Tensor or np.ndarray.

  • check_exists – If True, return the existing polyhedron from the complex if it already exists. Defaults to True.

  • **kwargs – Additional arguments passed to the Polyhedron constructor.

Returns:

The polyhedron corresponding to the given sign sequence.

Return type:

Polyhedron

ss_iterator(batch: Tensor | ndarray) Generator[Tensor, None, None]

Generate sign sequences for each ReLU layer from a batch of data points.

Parameters:

batch – A batch of input data points as a torch.Tensor, np.ndarray, or array-like. Will be reshaped to match the network’s input shape.

Yields:

torch.Tensor

Sign sequences for each ReLU layer in

the network, indicating the activation pattern of that layer.

str_to_poly(name: str, ensure_unique: bool = True) Polyhedron

Convert a string to the first Polyhedron with that __repr__. These values may not be unique.

Parameters:

name – The string name.

Returns:

The polyhedron associated with the given name.

Return type:

Polyhedron

property G: Graph

The adjacency graph of top-dimensional cells in the complex.

property dim: int

The input dimension of the network.

property n: int

The number of bent hyperplanes/neurons in the network.

property net: NN

The neural network. May only be set once.