Classes#

Cell Complex#

Creation and manipulation of a 2d cell complex.

The class also supports attaching arbitrary attributes and data to cells.

A cell complex is abbreviated in CC.

class toponetx.classes.cell_complex.CellComplex(cells=None, regular: bool = True, **kwargs)[source]#

Class representing a cell complex.

A cell complex is a mathematical structure that is built up from simple building blocks called cells. These cells can be thought of as generalized versions of familiar shapes, such as points, line segments, triangles, and disks. By gluing these cells together in a prescribed way, one can create complex geometrical objects that are of interest in topology and geometry.

Cell complexes can be used to represent various mathematical objects, such as graphs, manifolds, and discrete geometric shapes. They are useful in many areas of mathematics, such as algebraic topology and geometry, where they can be used to study the structure and properties of these objects.

In TNX the class CellComplex supports building a regular or non-regular 2d cell complex. The class CellComplex only supports the construction of 2d cell complexes. If higher dimensional cell complexes are desired then one should utilize the class CombinatorialComplex.

Mathematically, in TNX a cell complex it a triplet $(V, E, C)$ where $V$ is a set of nodes, $E$ is a set of edges and $C$ is a set of 2-cells. In TNX each 2-cell C is consists of a finite sequence of nodes $C=(n_1,…,n_k,n_1)$ with $kgeq 2$. All edges between two consecutive nodes in C belong to E. Regular cells have unique nodes in C whereas non-regular cells allow for duplication.

In TNX, cell complexes are implemented to be dynamic in the sense that they can change by adding or subtracting objects (nodes, edges, 2-cells) from them.

  1. Dynamic construction of cell complexes, allowing users to add or remove objects from these

    structures after their initial creation.

  2. Compatibility with the NetworkX library, enabling users to leverage the powerful algorithms

    and data structures provided by this package.

  3. Support for attaching arbitrary attributes and data to cells in the complex, allowing users to store

    and manipulate additional information about these objects.

  4. Efficient storage and manipulation of complex data structures, using advanced data structures

    such as sparse matrices.

  5. Robust error handling and validation of input data, ensuring that the package is reliable and easy to use.

Parameters:
cellsiterable, optional

A list of cells to add to the complex.

regularbool, default=True

If True, then the complex is regular, otherwise it is non-regular.

**kwargskeyword arguments, optional

Attributes to add to the complex as key=value pairs.

Examples

Iteratively construct a cell complex:

>>> from toponetx.classes import CellComplex
>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> # the cell [1, 2, 3, 4] consists of the cycle (1,2), (2,3), (3,4), (4,5)
>>> # tnx creates these edges automatically if they are not inserted in the underlying graph
>>> CC.add_cell([2, 3, 4, 5], rank=2)
>>> CC.add_cell([5, 6, 7, 8], rank=2)

You can also pass a list of cells to the constructor:

>>> c1 = Cell((1, 2, 3))  # a cell here is always assumed to be 2d
>>> c2 = Cell((1, 2, 3, 4))
>>> CC = CellComplex([c1, c2])

TopoNetX is also compatible with NetworkX, allowing users to create a cell complex from a NetworkX graph:

>>> from toponetx.classes import CellComplex
>>> import networkx as nx
>>> g = nx.Graph()
>>> g.add_edge(1, 0)
>>> g.add_edge(2, 0)
>>> g.add_edge(1, 2)
>>> CC = CellComplex(g)
>>> CC.add_cells_from([[1, 2, 4], [1, 2, 7]], rank=2)
>>> CC.cells

By default, a regular cell complex is constructed. You can change this behaviour using the regular parameter when constructing the complex.

>>> from toponetx.classes import CellComplex
>>> # non-regular cell complex
>>> # by default CellComplex constructor assumes regular cell complex
>>> CC = CellComplex(regular=False)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([2, 3, 4, 5, 2, 3, 4, 5], rank=2)  # non-regular 2-cell
>>> c1 = Cell((1, 2, 3, 4, 5, 1, 2, 3, 4, 5), regular=False)
>>> CC.add_cell(c1)
>>> CC.add_cell([5, 6, 7, 8], rank=2)
>>> CC.is_regular
Attributes:
complexdict

A dictionary that can be used to store additional information about the complex.

add_cell(cell: tuple | list | Cell, rank: int | None = None, check_skeleton: bool = False, **attr)[source]#

Add a single cell to cell complex.

Parameters:
cellhashable

If hashable the cell returned will be empty.

rank{0, 1, 2}

Rank of the cell to be added.

check_skeletonbool, default=False

If true, this function checks the skeleton whether the given cell can be added.

**attrkeyword arguments, optional

Attributes to add to the cell as key=value pairs.

Examples

>>> CC = CellComplex()
>>> c1 = Cell((2, 3, 4), color="black")
>>> CC.add_cell(c1, weight=3)
>>> CC.add_cell([1, 2, 3, 4], rank=2, color="red")
>>> CC.add_cell([2, 3, 4, 5], rank=2, color="blue")
>>> CC.add_cell([5, 6, 7, 8], rank=2, color="green")
>>> CC.cells[(1, 2, 3, 4)]["color"]
'red'
add_cells_from(cell_set: Iterable[tuple | list | Cell], rank: int | None = None, check_skeleton: bool = False, **attr) None[source]#

Add cells to cell complex.

Parameters:
cell_setiterable of hashables or Cell

Iterable of cells to add to the complex.

rankint, optional

The rank of cells to be added. If cell_set consists of Cell objects, this parameter is ignored.

check_skeletonbool, default=False

If true, this function checks the skeleton whether the given cell can be added.

**attrkeyword arguments, optional

Attributes to add to the cells as key=value pairs.

add_edge(u_of_edge: Hashable, v_of_edge: Hashable, **attr) None[source]#

Add edge.

Parameters:
u_of_edgehashable

First node of the edge.

v_of_edgehashable

Second node of the edge.

**attrkeyword arguments, optional

Attributes to add to the edge as key=value pairs.

add_edges_from(ebunch_to_add: Iterable[tuple], **attr) None[source]#

Add edges.

Parameters:
ebunch_to_addIterable of edges

Each edge must be given as a tuple (u, v).

**attrkeyword arguments, optional

Attributes to add to the edges as key=value pairs.

add_node(node: Hashable, **attr) None[source]#

Add a single node to cell complex.

Parameters:
nodehashable

The node to be added.

**attrkeyword arguments, optional

Attributes to add to the node as key=value pairs.

adjacency_matrix(rank: int, signed: bool = False, weight: str | None = None, index: bool = False)[source]#

Compute adjacency matrix for a given rank.

Parameters:
rankint

The rank for which an adjacency matrix should be computed.

signedbool, default=False

Whether the returned adjacency matrix should be signed (i.e., respect orientations) or unsigned.

weightstr, optional

The name of the simplex attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the adjacency matrix.

Returns:
indicesdict

Dictionary mapping each row and column of the coadjacency matrix to a cell. Only returned if index is True.

adjacency_matrixscipy.sparse.csr.csr_matrix

The adjacency matrix of rank rank of this cell complex.

all_cell_to_node_coadjacency_matrix(s: int | None = None, weight: str | None = None, index: bool = False) csc_matrix | tuple[dict, dict, csc_matrix][source]#

Return coadjecency matrix of cells with respect to nodes.

Parameters:
sint

Minimum number of nodes that cells must share to be considered adjacent.

weightbool, optional

The name of the cell attribute to use as weights for the coadjacency matrix. If None, the matrix is binary.

indexbool, optional

Whether to return the indices of the rows and columns of the adjacency matrix.

Returns:
indicesdict

Dictionary assigning each row and column of the coadjacency matrix to a cell. Only returned if index is True.

scipy.sparse.csr.csc_matrix

The coadjacency matrix.

Notes

Two cells (1 dimensional or 2 dimensional) are s-coadjacent iff they share s nodes.

Examples

>>> CX = CellComplex([[1, 2, 3, 4], [2, 3, 6]])
>>> index, m = CX.all_cell_to_node_coadjacency_matrix(s=1, index=True)
>>> # m_ij iff cell i is coadjacency to cell j. Dimension of cells i,j are arbirary
>>> print(m.todense(), index)
cell_neighbors(cell, s: int = 1)[source]#

Return the neighboring cells of the given cell.

Parameters:
cellCell or Iterable

The cell for which to find neighbors.

sint, default=1

Minimum number of nodes shared by neighboring cell.

Returns:
list

List of cell neighbors.

property cells: CellView#

Return cells.

Returns:
CellView

The cell view of the Complex.

clear()[source]#

Remove all cells from a cell complex.

clone() CellComplex[source]#

Create a clone of the CellComplex.

Returns:
CellComplex

A copy of this CellComplex.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2, weight=5)
>>> CC.add_cell([2, 3, 4, 5], rank=2)
>>> CC.add_cell([5, 6, 7, 8], rank=2)
>>> CC.add_node(0)
>>> CX2 = CC.clone()
coadjacency_matrix(rank: int, signed: bool = False, weight: str | None = None, index: bool = False)[source]#

Compute coadjacency matrix for a given rank.

Parameters:
rankint

Rank of the coadjacency matrix.

signedbool

Whether to return the signed or unsigned coadjacency matrix.

weightstr, optional

The name of the cell attribute to use as weights for the coadjacency matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the coadjacency matrix.

Returns:
indicesdict

Dictionary mapping each row and column of the coadjacency matrix to a cell. Only returned if index is True.

coadjacency_matrixscipy.sparse.csr.csr_matrix

The coadjacency matrix of this cell complex.

degree(node: Hashable, rank: int = 1) int[source]#

Compute the number of cells of certain rank that contain node.

Parameters:
nodehashable

Identifier for the node.

rankint, default=1

Smallest size of cell to consider in degree.

Returns:
int

Number of cells of rank at least rank that contain node.

property dim: int#

Return maximum dimension.

Returns:
int

The maximum dimension of the Cell Complex.

dirac_operator_matrix(signed: bool = True, weight: str | None = None, index: bool = False)[source]#

Compute dirac operator matrix matrix.

Parameters:
signedbool, default=False

Whether the returned dirac matrix should be signed (i.e., respect rientations) or unsigned.

weightstr, optional

The name of the cell attribute to use as weights for the dirac operator matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the dirac operator matrix.

Returns:
row_indices, col_indicesdict

List identifying rows and columns of the dirac operator matrix. Only returned if index is True.

scipy.sparse.csr.csc_matrix

The dirac operator matrix of this cell complex.

Examples

>>> import networkx as nx
>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.dirac_operator_matrix()
down_laplacian_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False)[source]#

Compute down laplacian.

Parameters:
rank{1, 2}

Rank of the down Laplacian matrix.

signedbool

Whether to return the signed or unsigned down laplacian.

weightstr, optional

The name of the cell attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the laplacian.

Returns:
indicesdict

Dictionary assigning each row and column of the laplacian matrix to a cell. Only returned if index is True.

down_laplacianscipy.sparse.csr.csr_matrix

The down laplacian matrix of this cell complex.

Examples

>>> import networkx as nx
>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([2, 3, 4, 1], rank=2)
>>> CC.add_cell([1, 2, 4], rank=2)
>>> CC.add_cell([3, 4, 8], rank=2)
>>> CC.down_laplacian_matrix(2)
property edges: EdgeView#

Return edges.

Returns:
EdgeView

The edge view of the Complex.

euler_characterisitics() int[source]#

Return the euler characteristics of the cell complex.

Returns:
int

The Euler characteristics of the cell complex.

from_networkx_graph(G: Graph) None[source]#

Add edges and nodes from a graph G to self.

Parameters:
Gnx.Graph

A NetworkX graph.

Examples

>>> CC = CellComplex()
>>> CC.add_cells_from([[1, 2, 4], [1, 2, 7]], rank=2)
>>> G = Graph([(0, 1), (0, 2), (1, 2)])
>>> CC.from_networkx_graph(G)
>>> CC.edges
classmethod from_trimesh(mesh) CellComplex[source]#

Convert from trimesh object.

Parameters:
meshtrimesh.Trimesh

A trimesh object.

Returns:
CellComplex

The cell complex generated from the trimesh provided.

Examples

>>> import trimesh
>>> mesh = trimesh.Trimesh(vertices=[[0, 0, 0], [0, 0, 1], [0, 1, 0]],
                       faces=[[0, 1, 2]],
                       process=False)
>>> CC = CellComplex.from_trimesh(mesh)
>>> CC[0]["position"]
get_cell_attributes(name: str, rank: int) dict[tuple[Hashable, ...], Any][source]#

Get node attributes from graph.

Parameters:
namestr

Attribute name.

rankint

The rank for which to return attribute values.

Returns:
dict

Dictionary mapping each cell of rank rank to the value of the given attribute name.

Examples

>>> import networkx as nx
>>> from toponetx.classes import CellComplex
>>> G = nx.path_graph(3)
>>> d = {
...     ((1, 2, 3, 4), 0): {"color": "red", "attr2": 1},
...     (1, 2, 4): {"color": "blue", "attr2": 3},
... }
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([1, 2, 4], rank=2)
>>> CC.add_cell([3, 4, 8], rank=2)
>>> CC.set_cell_attributes(d, 2)
>>> CC.get_cell_attributes("color", 2)
{((1, 2, 3, 4), 0): 'red', (1, 2, 4): 'blue'}
get_cell_data(cell, rank, attr_name: str | None = None)[source]#

Retrieve data associated with a specific cell in the complex.

Parameters:
cellstr or tuple

The cell to retrieve data from.

rankint

The rank of the cell.

attr_namestr, optional

The name of the attribute to retrieve. Default is None.

Returns:
object

The value associated with the specified cell and attribute.

Raises:
KeyError

If the specified cell or attribute is not found.

Notes

  • For rank 0 cells (nodes), the data is retrieved from the ‘nodes’ dictionary.

  • For rank 1 cells (edges), the data is retrieved from the ‘edges’ dictionary.

  • For rank 2 cells (other cells), the data is retrieved from the ‘cells’ dictionary.

get_edge_attributes(name: str) dict[tuple, Any][source]#

Get edge attributes.

Parameters:
namestr

Attribute name.

Returns:
dict

Dictionary mapping each edge to the value of the given attribute name.

Examples

>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> d = {
...     (1, 2): {"color": "red", "attr2": 1},
...     (2, 3): {"color": "blue", "attr2": 3},
... }
>>> CC.set_edge_attributes(d)
>>> CC.get_edge_attributes("color")
{(1, 2): 'red', (2, 3): 'blue'}
get_filtration(name: str) dict[Hashable, Any][source]#

Get filtration.

Parameters:
namestr

The attribute name that holds the filtration values.

Returns:
dict

Dictionary mapping each cell to the value of the given attribute name.

Notes

This is equivalent to getting a feature defined on the entire cell complex

Examples

>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> d = {0: 1, 1: 0, 2: 2, (0, 1): 1, (1, 2): 3}
>>> CC.set_filtration(d, "f")
>>> CC.get_filtration("f")
{0: 1, 1: 0, 2: 2, (0, 1): 1, (1, 2): 3}
get_linegraph(s: int = 1, cells: bool = True) Graph[source]#

Create line graph of self.

If cells=True (default), the cells will be the vertices of the line graph. Two vertices are connected by an s-line-graph edge if the corresponding cell complex edges intersect in at least s cell complex nodes.

If cells=False, the cell complex nodes will be the vertices of the line graph. Two vertices are connected if the nodes they correspond to share at least s incident cell complex edges.

Parameters:
sint

The width of the connections.

cellsbool, optional

Determines if cells or nodes will be the vertices in the line graph.

Returns:
nx.Graph

A NetworkX graph representing the s-linegraph of the Cell Complex.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([0, 1, 2, 3, 4], rank=2)
>>> G = CC.get_linegraph()
get_node_attributes(name: str) dict[tuple, Any][source]#

Get node attributes.

Parameters:
namestr

Attribute name.

Returns:
dict

Dictionary mapping each node to the value of the given attribute name.

Examples

>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> d = {1: {"color": "red", "attr2": 1}, 2: {"color": "blue", "attr2": 3}}
>>> CC.set_node_attributes(d)
>>> CC.get_node_attributes("color")
{1: 'red', 2: 'blue'}
hodge_laplacian_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False) csr_matrix | tuple[dict, csr_matrix][source]#

Compute the Hodge Laplacian matrix for this cell complex.

Parameters:
rank{0, 1, 2}

Dimension of the Laplacian matrix.

signedbool

Whether to return the signed or unsigned hodge laplacian.

weightstr, optional

The name of the cell attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Indicates whether to return the indices that define the hodge laplacian matrix.

Returns:
indicesdict

Dictionary assigning each row and column of the incidence matrix to a cell. Only returned if index is True.

scipy.sparse.csr.csc_matrix

The Hodge Laplacian matrix of rank rank.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([3, 4, 5], rank=2)
>>> CC.hodge_laplacian_matrix(1)
incidence_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False) csr_matrix | tuple[dict, dict, csr_matrix][source]#

Incidence matrix for the cell complex indexed by nodes x cells.

Parameters:
rankint

The rank for which an incidence matrix should be computed.

signedbool, default=True

Whether the returned incidence matrix should be signed (i.e., respect orientations) or unsigned.

weightstr, optional

The name of the simplex attribute to use as weights for the incidence matrix. If None, the matrix is binary.

indexbool, optional

Whether to return the indices of the rows and columns of the incidende matrix.

Returns:
row_indices, col_indicesdict

Dictionary assigning each row and column of the incidence matrix to a cell. Only returned if index is True.

scipy.sparse.csr.csc_matrix

The incidence matrix.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([3, 4, 5], rank=2)
>>> B0 = CC.incidence_matrix(0)
>>> B1 = CC.incidence_matrix(1)
>>> B2 = CC.incidence_matrix(2)
>>> B1.dot(B2).todense()
>>> B0.dot(B1).todense()

Note that in this example, the first three cells are equivalent and hence they have similar incidence to lower edges they are incident to.

>>> import networkx as nx
>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([4, 3, 2, 1], rank=2)
>>> CC.add_cell([2, 3, 4, 1], rank=2)
>>> CC.add_cell(
...     [1, 2, 4],
...     rank=2,
... )
>>> CC.add_cell([3, 4, 8], rank=2)
>>> B1 = CC.incidence_matrix(1)
>>> B2 = CC.incidence_matrix(2)
>>> B1.dot(B2).todense()

Non-regular cell complex example:

>>> CC = CellComplex(regular=False)
>>> CC.add_cell([1, 2, 3, 2], rank=2)
>>> CC.add_cell([3, 4, 5, 3, 4, 5], rank=2)
>>> B1 = CC.incidence_matrix(1)
>>> B2 = CC.incidence_matrix(2)
>>> print(B2.todense())  # observe the non-unit entries
>>> B1.dot(B2).todense()
>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([3, 4, 5], rank=2)
>>> row, column, B1 = CC.incidence_matrix(1, index=True)
>>> print(row)
>>> print(column)
>>> print(B1.todense())
is_connected()[source]#

Determine if cell complex is s-connected.

Returns:
bool

True if the cell complex is connected, False otherwise.

is_insertable_cycle(cell: tuple | list | Cell, check_skeleton: bool = True, warnings_dis: bool = False) bool[source]#

Determine if a cycle is insertable to the cell complex.

Checks regularity if this CellComplex is regular, existence of required edges if check_skeleton is True, and that the cell has a minimum length of 2.

Parameters:
cellCell | tuple | list

The cell to check.

check_skeletonbool, default=True

Whether to check that all edges induced by the cell are part of the underlying graph. If False, missing edges will be ignored.

warnings_disbool, default=False

Whether to print a warning with the reason why the cell is not insertable.

Returns:
bool

True if the cell can be inserted, otherwise False.

property is_regular: bool#

Check the regularity condition.

Returns:
bool

Returns True if the regularity condition is satisfied.

Examples

>>> CC = CellComplex(regular=False)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([2, 3, 4, 5, 2, 3, 4, 5], rank=2)  # non-regular 2-cell
>>> c1 = Cell((1, 2, 3, 4, 5, 1, 2, 3, 4, 5), regular=False)
>>> CC.add_cell(c1)
>>> CC.add_cell([5, 6, 7, 8], rank=2)
>>> CC.is_regular
classmethod load_mesh(file_path, process: bool = False) CellComplex[source]#

Load a mesh.

Parameters:
file_pathstr or pathlib.Path

The file path of the data to be loaded.

processbool, default=False

Whether trimesh should process the mesh before loading it.

Returns:
CellComplex

The cell complex corresponding to the mesh.

Notes

file supported : obj, off, glb

Examples

>>> CC = CellComplex.load_mesh("bunny.obj")
property maxdim: int#

Return maximum dimension.

Returns:
int

The maximum dimension for Cell Complex.

neighbors(node)[source]#

Return nodes in cell complex which share s cell(s) with node.

Parameters:
nodehashable

Node in the cell complex for which to find neighbors.

Returns:
list

List of neighbors.

Raises:
KeyError

If node is not in the cell complex.

node_to_all_cell_adjacnecy_matrix(s: int | None = None, weight: str | None = None, index: bool = False) csc_matrix | tuple[dict, dict, csc_matrix][source]#

Compute s-adjacency matrix between nodes with respect to 2-cells.

Parameters:
sint

The dimension of the cells to consider.

weightstr, optional

If not given, all nonzero entries are 1.

indexbool, default=False

Whether to return the indices of the rows and columns of the incidence matrix.

Returns:
indicesdict

Dictionary assigning each row and column of the incidence matrix to a node. Only returned if index is True.

scipy.sparse.csr.csc_matrix

The adjacency matrix.

Notes

Two nodes are s-adjacent iff there exists a cell (1 dimensional or 2 dimensional) share contain them.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([3, 4, 5], rank=2)
>>> CC.node_to_all_cell_adjacnecy_matrix().todense()
matrix([[0., 2., 1., 2., 0.],
        [2., 0., 2., 1., 0.],
        [1., 2., 0., 3., 2.],
        [2., 1., 3., 0., 2.],
        [0., 0., 2., 2., 0.]])
>>> # observe the contrast with the regular 0-adjacency matrix
>>> CC.adjacency_matrix(0).todense()
matrix([[0., 1., 0., 1., 0.],
        [1., 0., 1., 0., 0.],
        [0., 1., 0., 1., 1.],
        [1., 0., 1., 0., 1.],
        [0., 0., 1., 1., 0.]])
node_to_all_cell_incidence_matrix(weight: str | None = None, index: bool = False) csc_matrix | tuple[dict, dict, csc_matrix][source]#

Nodes/all cells incidence matrix for the indexed by nodes X cells.

Parameters:
weightstr, optional

The name of the cell attribute to use as weights for the incidence matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the incidence matrix.

Returns:
row_indicesdict

Dictionary assigning each row of the incidence matrix to a node. Only returned if index is True.

col_indicesdict

Dictionary assigning each column of the incidence matrix to a cell. Only returned if index is True.

incidence_matrixscipy.sparse.csr.csc_matrix

The incidence matrix.

property nodes: NodeView#

Return nodes.

Returns:
NodeView

The node view of the Complex.

number_of_cells(cell_set: Iterable[tuple | list | Cell] | None = None) int[source]#

Compute the number of cells in cell_set belonging to cell complex.

Parameters:
cell_setiterable of cells, optional

Cells can be represented as a tuple, list, or Cell object. If None, then return the number of cells in cell complex.

Returns:
int

The number of cells in cell_set belonging to cell complex.

number_of_edges(edge_set: Iterable[tuple] | None = None) int[source]#

Compute number of edges in edge_set belonging to cell complex.

Parameters:
edge_setan iterable of edges, optional

If None, then return the number of edges in cell complex.

Returns:
int

The number of edges in edge_set belonging to cell complex.

number_of_nodes(node_set: Iterable[Hashable] | None = None)[source]#

Compute number of nodes in node_set belonging to cell complex.

Parameters:
node_setan iterable of nodes, optional

If None, then return the number of nodes in cell complex.

Returns:
int

Number of nodes in node_set belonging to cell complex.

order() int[source]#

Compute the number of nodes in the cell complex.

Returns:
int

Number of nodes in the cell complex.

remove_cell(cell: tuple | list | Cell)[source]#

Remove a single cell from Cell Complex.

Parameters:
cellcell’s node_set or Cell

The cell to remove from this cell complex.

Notes

Deletes reference to cell, keep it boundary edges in the cell complex

remove_cells(cell_set: Iterable[tuple | list | Cell])[source]#

Remove cells from a cell complex that are in cell_set.

Parameters:
cell_setiterable of hashables or RankedEntities

The cells to remove from this cell complex.

remove_equivalent_cells() None[source]#

Remove equivalent cells.

Remove cells from the cell complex which are homotopic. In other words, this is equivalent to identifying cells containing the same nodes and are equivalent up to cyclic permutation.

Notes

Remove all 2d- cells that are homotopic (equivalent to each other)

Examples

>>> import networkx as nx
>>> from toponetx.classes import CellComplex
>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([2, 3, 4, 1], rank=2)
>>> CC.add_cell(
...     [1, 2, 4],
...     rank=2,
... )
>>> CC.add_cell([3, 4, 8], rank=2)
>>> print(CC.cells)
>>> CC.remove_equivalent_cells()
>>> print(CC.cells)  # observe homotopic cells have been removed
remove_node(node: Hashable) None[source]#

Remove the given node from the cell complex.

This method removes the given node from the cell complex, along with any cells that contain the node.

Parameters:
nodehashable

The node to be removed from the cell complex.

Raises:
RuntimeError

If the given node does not exist in the cell complex.

remove_nodes(node_set: Iterable[Hashable]) None[source]#

Remove nodes from cells.

This also deletes references in cell complex nodes.

Parameters:
node_setiterable of hashables or Entities

Nodes in the cell complex.

remove_singletons() None[source]#

Remove singleton nodes (see CellComplex.singletons()).

restrict_to_cells(cell_set: Iterable[Cell | tuple], keep_edges: bool = False)[source]#

Construct cell complex using a subset of the cells in cell complex.

Parameters:
cell_setIterable[Cell | tuple]

A subset of elements of the cell complex’s cells (self.cells) and edges (self.edges). Cells can be represented as Cell objects or tuples with length > 2.

keep_edgesbool, default=False

If False, discards edges not required by or included in cell_set If True, all previous edges are kept.

Returns:
CellComplex

A new cell complex restricted to the cells in cell_set.

Examples

>>> CC = CellComplex()
>>> c1 = Cell((1, 2, 3))
>>> c2 = Cell((1, 2, 4))
>>> c3 = Cell((1, 2, 5))
>>> CC = CellComplex([c1, c2, c3])
>>> CC.add_edge(1, 0)
>>> cx1 = CC.restrict_to_cells([c1, (0, 1)])
>>> cx1.cells
CellView([Cell(1, 2, 3)])
restrict_to_nodes(node_set: Iterable[Hashable])[source]#

Restrict cell complex to nodes.

This constructs a new cell complex by restricting the cells in the cell complex to the nodes referenced by node_set.

Parameters:
node_setiterable of hashables

The nodes to restrict the cell complex to.

Returns:
CellComplex

A new cell complex restricted to the nodes in node_set.

Examples

>>> CC = CellComplex()
>>> c1 = Cell((1, 2, 3))
>>> c2 = Cell((1, 2, 4))
>>> c3 = Cell((1, 2, 5))
>>> CC = CellComplex([c1, c2, c3])
>>> CC.add_edge(1, 0)
>>> CC.restrict_to_nodes([1, 2, 3, 0])
set_cell_attributes(values: dict[Hashable | tuple | list | Cell, dict] | dict[Hashable | tuple | list | Cell, Any], rank: int, name: str | None = None) None[source]#

Set cell attributes.

Parameters:
valuesdict

Dictionary of attribute values to set. If name is specified, the dictionary must be of the form cell -> value. Otherwise, the dictionary must be of the form cell -> (attribute -> value).

rank{0, 1, 2}

0 for nodes, 1 for edges, 2 for 2-cells. ranks > 2 are currently not supported.

namestr, optional

Attribute name to use for setting the attribute values.

Notes

If the dict contains cells that are not in self.cells, they are silently ignored.

Examples

After computing some property of the cell of a cell complex, you may want to assign a cell attribute to store the value of that property for each cell:

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell(
...     [1, 2, 4],
...     rank=2,
... )
>>> CC.add_cell([3, 4, 8], rank=2)
>>> d = {(1, 2, 3, 4): "red", (1, 2, 4): "blue"}
>>> CC.set_cell_attributes(d, name="color", rank=2)
>>> CC.cells[(1, 2, 3, 4)]["color"]
'red'

If you provide a dictionary of dictionaries as the second argument, the entire dictionary will be used to update cell attributes:

>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell(

… [1, 2, 4], … rank=2, … ) >>> CC.add_cell([3, 4, 8], rank=2) >>> d = { … (1, 2, 3, 4): {“color”: “red”, “attr2”: 1}, … (1, 2, 4): {“color”: “blue”, “attr2”: 3}, … } >>> CC.set_cell_attributes(d) >>> CC.cells[(1, 2, 3, 4)][0][“color”] ‘red’

set_cell_data(cell, rank, attr_name: str, attr_value)[source]#

Set data for a specific cell in the complex.

Parameters:
cellstr or tuple

The cell to set data for.

rankint

The rank of the cell.

attr_namestr

The name of the attribute to set.

attr_valueobject

The value to set for the attribute.

Raises:
KeyError

If the specified cell is not found.

Notes

  • For rank 0 cells (nodes), the data is stored in the ‘nodes’ dictionary.

  • For rank 1 cells (edges), the data is stored in the ‘edges’ dictionary.

  • For rank 2 cells (other cells), the data is stored in the ‘cells’ dictionary.

set_edge_attributes(values: dict[tuple, dict] | dict[tuple, Any], name: str | None = None) None[source]#

Set edge attributes.

Parameters:
valuesdict

Dictionary of attribute values to set. If name is specified, the dictionary must be of the form edge -> value. Otherwise, the dictionary must be of the form edge -> (attribute -> value).

namestr, optional

Attribute name to use for setting the attribute values.

Examples

>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> d = {
...     (1, 2): {"color": "red", "attr2": 1},
...     (2, 3): {"color": "blue", "attr2": 3},
... }
>>> CC.set_edge_attributes(d)
>>> CC.edges[(1, 2)]["color"]
'red'
set_filtration(values: dict[Hashable | tuple | list | Cell, dict] | dict[Hashable | tuple | list | Cell, Any], name: str | None = None) None[source]#

Set filtration.

Parameters:
valuesdict

Dictionary of filtration values to set. If name is specified, the dictionary must be of the form cell -> value. Otherwise, the dictionary must be of the form cell -> (attribute -> value).

namestr, optional

The attribute name to use to store the filtration values.

Notes

This is equivalent to setting a real-valued feature defined on the entire cell complex

If the dict contains cells that are not in self.cells, they are silently ignored.

Examples

>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> d = {0: 1, 1: 0, 2: 2, (0, 1): 1, (1, 2): 3}
>>> CC.set_filtration(d, "f")
set_node_attributes(values: dict[Hashable, dict] | dict[Hashable, Any], name: str | None = None) None[source]#

Set node attributes.

Parameters:
valuesdict

Dictionary of attribute values to set. If name is specified, the dictionary must be of the form node -> value. Otherwise, the dictionary must be of the form node -> (attribute -> value).

namestr, optional

Attribute name to use for setting the attribute values.

Examples

>>> G = nx.path_graph(3)
>>> CC = CellComplex(G)
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> d = {1: {"color": "red", "attr2": 1}, 2: {"color": "blue", "attr2": 3}}
>>> CC.set_node_attributes(d)
>>> CC[1]["color"]
'red'
property shape: tuple[int, int, int]#

Return shape.

Returns:
tuple[int, int, int]

The tuple containing the number of entities corresponding to the nodes, edges, and cells of the Cell Complex respectively.

singletons()[source]#

Return list of singleton cell.

A singleton cell is a node of degree 0.

Returns:
list of Hashable

List of singleton nodes in this cell complex.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([2, 3, 4, 5], rank=2)
>>> CC.add_cell([5, 6, 7, 8], rank=2)
>>> CC.add_node(0)
>>> CC.add_node(10)
>>> CC.singletons()
size(cell: tuple | list | Cell, node_set: Iterable[Hashable] | None = None) int[source]#

Compute number of nodes in node_set that belong to cell.

If node_set is None then returns the size of cell.

Parameters:
cellhashable

The uid of an cell in the cell complex.

node_setan iterable of node elements

Node elements.

Returns:
int

Number of nodes in node_set that belong to cell.

skeleton(rank: int)[source]#

Compute skeleton.

Parameters:
rank{0, 1, 2}

The rank of the skeleton.

Returns:
Iterable

The atoms of given rank in this complex.

Raises:
ValueError

If rank is not 0, 1 or 2.

to_colored_hypergraph()[source]#

Convert to colored hypergraph.

A cell complex is a type of combinatorial complex. The rank of an element in a cell complex is its dimension, so vertices have rank 0, edges have rank 1, and faces have rank 2.

Returns:
ColoredHyperGraph

The colored hypergraph corresponding to this cell complex.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2, weight=1)
>>> CC.add_cell([2, 3, 4, 5], rank=2, weight=4)
>>> CC.add_cell([5, 6, 7, 8], rank=2, weight=0)
>>> CC.add_node(0, color="red")
>>> CCC = CC.to_colored_hypergraph()
>>> CCC.cells
to_combinatorial_complex()[source]#

Convert this cell complex to a combinatorial complex.

A cell complex is a type of combinatorial complex. The rank of an element in a cell complex is its dimension, so vertices have rank 0, edges have rank 1, and faces have rank 2.

Returns:
CombinatorialComplex

The combinatorial complex corresponding to this cell complex.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2, weight=1)
>>> CC.add_cell([2, 3, 4, 5], rank=2, weight=4)
>>> CC.add_cell([5, 6, 7, 8], rank=2, weight=0)
>>> CC.add_node(0, color="red")
>>> CCC = CC.to_combinatorial_complex()
>>> CCC.cells
to_hasse_graph() DiGraph[source]#

Create Hasse graph of self.

Returns:
nx.DiGraph

A NetworkX Digraph representing the Hasse graph of the Cell Complex.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> G = CC.to_hasse_graph()
to_hypergraph()[source]#

Convert this cell complex to a hypergraph.

Returns:
Hypergraph

The hyergraph corresponding to this cell complex.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2, color="red")
>>> CC.add_cell([2, 3, 4, 5], rank=2)
>>> CC.add_cell([5, 6, 7, 8], rank=2)
>>> HG = CC.to_hypergraph()
>>> HG
up_laplacian_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False)[source]#

Compute up laplacian.

Parameters:
rank{0, 1}

dimension of the up Laplacian matrix.

signedbool

Whether the returned laplacian matrix should be signed (i.e., respect orientations) or unsigned.

weightstr, optional

The name of the cell attribute to use as weights for the up laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the laplacian matrix.

Returns:
row_indices, col_indiceslist

List identifying the rows and columns of the laplacian matrix with the cells of this complex. Only returned if index is True.

up_laplacianscipy.sparse.csr.csr_matrix

The upper laplacian matrix of this cell complex.

Examples

>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3, 4], rank=2)
>>> CC.add_cell([3, 4, 5], rank=2)
>>> L1_up = CC.up_laplacian_matrix(1)
>>> CC = CellComplex()
>>> CC.add_cell([1, 2, 3], rank=2)
>>> CC.add_cell([3, 4, 5], rank=2)
>>> index, L1_up = CC.up_laplacian_matrix(1, index=True)
>>> print(index)
>>> print(L1_up)

Cell#

Cell class.

class toponetx.classes.cell.Cell(elements: Collection, regular: bool = True, **kwargs)[source]#

Class representing a 2D cell.

A 2D cell is an elementary building block used to build a 2D cell complex, whether regular or non-regular.

Parameters:
elementsiterable of hashable objects

An iterable that contains hashable objects representing the nodes of the cell. The order of the elements is important and defines the cell up to cyclic permutation.

regularbool, optional

A boolean indicating whether the cell satisfies the regularity condition. The default value is True. A 2D cell is regular if and only if there is no repetition in the boundary edges that define the cell. By default, the cell is assumed to be regular unless otherwise specified. Self-loops are not allowed in the boundary of the cell. If a cell violates the cell complex regularity condition, a ValueError is raised.

**kwargskeyword arguments, optional

Attributes belonging to the cell can be added as key-value pairs. Both the key and value must be hashable.

Notes

  • A cell is defined as an ordered sequence of nodes (n_1, …, n_k), where each two consecutive nodes (n_i, n_{i+1}) define an edge in the boundary of the cell. Note that the last edge (n_k, n_1) is also included in the boundary of the cell. For instance, if a Cell is defined as c = Cell((1, 2, 3)), then c.boundary will return [(1, 2), (2, 3), (3, 1)], which consists of three edges.

  • When a cell is created, its boundary is automatically created as a set of edges that encircle the cell.

Examples

>>> cell1 = Cell((1, 2, 3))
>>> cell2 = Cell((1, 2, 4, 5), weight=1)
>>> cell3 = Cell(("a", "b", "c"))
>>> # create geometric cell:
>>> v0 = (0, 0)
>>> v1 = (1, 0)
>>> v2 = (1, 1)
>>> v3 = (0, 1)
# create the cell with the vertices and edges
>>> cell = Cell([v0, v1, v2, v3], type="square")
>>> cell["type"]
>>> list(cell.boundary)
[((0, 0), (1, 0)), ((1, 0), (1, 1)), ((1, 1), (0, 1)),
((0, 1), (0, 0))]
property boundary#

Boundary.

A 2d cell is characterized by its boundary edges.

Returns:
Iterable[tuple[int, int]]

Iterator of tuple representing boundary edges given in cyclic order.

Examples

For a cell [1,2,3,4,5], the boundary is the edges that define that cell :

(1,2), (2,3), (3,4), (4,5) and (5,1).

clone() Cell[source]#

Clone the Cell with all attributes.

The clone method by default returns an independent shallow copy of the cell and attributes. That is, if an attribute is a container, that container is shared by the original and the copy. Use Python’s copy.deepcopy for new containers.

Returns:
Cell

A copy of this cell.

is_homotopic_to(cell) bool[source]#

Check if self is homotopic to input cell.

Parameters:
celltuple, list or Cell

The cell to check for homotopy.

Returns:
bool

Return True is self is homotopic to input cell and False otherwise.

property is_regular: bool#

Check if a cell is regular.

Returns:
bool

True if the Cell is regular, and False otherwise.

static is_valid_cell(elements: Sequence, regular: bool = False) bool[source]#

Check if a 2D cell defined by a list of elements is valid.

Parameters:
elementsSequence

List of elements defining the cell.

regularbool, default=False

Indicates if the cell is regular.

Returns:
bool

True if the cell is valid, False otherwise.

reverse()[source]#

Reverse the sequence of nodes that defines the cell.

Returns:
Cell

New cell with the new reversed elements.

sign(edge) Literal[-1, 1][source]#

Compute the sign of the given edge with respect to this cell.

This takes an edge as input and computes the sign of the edge with respect to the cell.

If the edge is in the boundary of the cell, then the sign is 1 if the edge is in the counterclockwise direction around the cell and -1 if it is in the clockwise direction.

If the edge is not in the boundary of the cell, a KeyError is raised.

Parameters:
edgeIterable

The edge whose sign should be computed.

Returns:
{1, -1}

1: if the edge is in the counterclockwise direction around the cell. -1: if the edge is in the clockwise direction around the cell.

Raises:
KeyError

If the input edge is not in the boundary of the cell.

ValueError

If the input edge is not valid.

TypeError

If the input edge is not iterable.

Colored Hypergraph#

Creation and manipulation of a Colored Hypergraph.

class toponetx.classes.colored_hypergraph.ColoredHyperGraph(cells: Collection | None = None, ranks: Collection | int | None = None, **kwargs)[source]#

Class for ColoredHyperGraph Complex.

A Colored Hypergraph (CHG) is a triplet CHG = (S, X, c) where: - S is an abstract set of entities, - X is a subset of the power set of S, and - c is the color function that associates a positive integer color or rank to each set x in X.

A CHG is a generalization of graphs, combinatorial complexes, hypergraphs, cellular, and simplicial complexes.

Parameters:
cellsCollection, optional

The initial collection of cells in the Colored Hypergraph.

ranksCollection, optional

Represents the color of cells.

**kwargskeyword arguments, optional

Attributes to add to the complex as key=value pairs.

Examples

Define an empty colored hypergraph:

>>> CHG = ColoredHyperGraph()

Add cells to the colored hypergraph:

>>> from toponetx.classes.colored_hypergraph import ColoredHyperGraph
>>> CHG = ColoredHyperGraph()
>>> CHG.add_cell([1, 2], rank=1)
>>> CHG.add_cell([3, 4], rank=1)
>>> CHG.add_cell([1, 2, 3, 4], rank=2)
>>> CHG.add_cell([1, 2, 4], rank=2)
>>> CHG.add_cell([1, 2, 3, 4, 5, 6, 7], rank=3)

Create a Colored Hypergraph and add groups of friends with corresponding ranks:

>>> CHG = ColoredHyperGraph()
>>> CHG.add_cell(
...     ["Alice", "Bob"], rank=1
... )  # Alice and Bob are in a close-knit group.
>>> CHG.add_cell(["Charlie", "David"], rank=1)  # Another closely connected group.
>>> CHG.add_cell(
...     ["Alice", "Bob", "Charlie", "David"], rank=2
... )  # Both groups together form a higher-ranked community.
>>> CHG.add_cell(["Alice", "Bob", "David"], rank=2)  # Overlapping connections.
>>> CHG.add_cell(
...     ["Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace"], rank=3
... )  # A larger, more influential community.

The code demonstrates how to represent social relationships using a Colored Hypergraph, where each group of friends (hyperedge) is assigned a rank based on the strength of the connection.

Attributes:
complexdict

A dictionary that can be used to store additional information about the complex.

add_cell(cell, rank=None, key=None, **attr)[source]#

Add a single cell to Colored Hypergraph.

Parameters:
cellhashable, iterable, or HyperEdge

If hashable, the cell returned will be empty.

rankint

The rank of a cell.

keyint, optional

Used to distinguish colored hyperedges among nodes.

**attrattributes associated with hyperedge

Attributes of the hyperedge.

Returns:
ColoredHyperGraph

The modified Colored Hypergraph.

Notes

The add_cell method adds a cell to the Colored Hypergraph instance. The cell can be a hashable, iterable, or HyperEdge. If the cell is hashable, the resulting cell will be empty. The rank parameter specifies the rank of the cell.

add_cells_from(cells, ranks=None)[source]#

Add cells to Colored Hypergraph.

Parameters:
cellsiterable of hashables

For hashables, the cells returned will be empty.

ranksIterable or int

When iterable, len(ranks) == len(cells).

Returns:
None

None.

add_node(node, **attr) None[source]#

Add a node.

Parameters:
nodehashable

The node to add.

**attrdict

Additional attributes to assign to the node.

Returns:
None

None.

adjacency_matrix(rank, via_rank, s: int = 1, index: bool = False)[source]#

Sparse weighted s-adjacency matrix.

Parameters:
rank, via_rankint, int

Two ranks for skeletons in the input Colored Hypergraph.

sint, list, optional

Minimum number of edges shared by neighbors with node.

indexbool, optional

If True, will return a rowdict of row to node uid.

Returns:
row dictionarydict

Dictionary identifying rows with nodes. If False, this does not exist.

adjacency_matrixscipy.sparse.csr.csr_matrix

The adjacency matrix.

Examples

>>> import networkx as nx
>>> from toponetx.classes.colored_hypergraph import ColoredHyperGraph
>>> G = nx.Graph()  # networkx graph
>>> G.add_edge(0, 1)
>>> G.add_edge(0, 3)
>>> G.add_edge(0, 4)
>>> G.add_edge(1, 4)
>>> CHG = ColoredHyperGraph(cells=G)
>>> CHG.adjacency_matrix(0, 1)
all_cell_to_node_coadjacency_matrix(index: bool = False, s: int | None = None)[source]#

Compute the cell co-adjacency matrix.

Parameters:
indexbool, optional, default=False

If True, return a row dictionary of row to node uid.

sint, list, optional, default=1

Minimum number of edges shared by neighbors with a node.

Returns:
row dictionarydict

Dictionary identifying rows with nodes. If False, this does not exist.

all cells co-adjacency matrixscipy.sparse.csr.csr_matrix

Cell co-adjacency matrix.

all_ranks_incidence_matrix(rank, weight: str | None = None, sparse: bool = True, index: bool = False)[source]#

Compute the incidence matrix for the Colored Hypergraph indexed by cells of rank n and all other cells.

Parameters:
rankint

The rank which filters the cells based on which the incidence matrix is computed.

weightstr, optional

If not given, all nonzero entries are 1.

sparsebool, optional

The output will be sparse.

indexbool, optional, default False

If True, the return will include a dictionary of node uid : row number and cell uid : column number.

Returns:
incidence_matrixscipy.sparse.csr.csr_matrix or np.ndarray

The incidence matrix.

row dictionarydict

Dictionary identifying rows with nodes.

column dictionarydict

Dictionary identifying columns with cells.

Notes

The all_ranks_incidence_matrix method computes the incidence matrix for the Colored Hypergraph, focusing on cells of rank n and all other cells. The weight parameter allows specifying the weight of the entries in the matrix. If index is True, dictionaries mapping node and cell identifiers to row and column numbers are included in the return.

property cells#

Object associated with self._cells.

Returns:
HyperEdgeView

HyperEdgeView of all cells and associated ranks.

clone() ColoredHyperGraph[source]#

Return a copy of the simplex.

The clone method by default returns an independent shallow copy of the simplex and attributes. That is, if an attribute is a container, that container is shared by the original and the copy. Use Python’s copy.deepcopy for new containers.

Returns:
ColoredHyperGraph

ColoredHyperGraph.

coadjacency_matrix(rank, via_rank, s: int = 1, index: bool = False)[source]#

Compute the coadjacency matrix.

The sparse weighted s-coadjacency matrix.

Parameters:
rankint

The rank for the primary skeleton in the input Colored Hypergraph.

via_rankint

The rank for the secondary skeleton in the input Colored Hypergraph.

sint, optional

Minimum number of edges shared by neighbors with the node.

indexbool, optional

If True, return will include a dictionary of row number to node uid.

Returns:
row dictionarydict

Dictionary identifying rows with nodes. If False, this does not exist.

co-adjacency_matrixscipy.sparse.csr.csr_matrix

The co-adjacency matrix.

degree(node, rank: int = 1, s: int = 0) int[source]#

Compute the number of cells of certain rank (or all ranks) that contain node.

Parameters:
nodehashable

Identifier for the node.

rankint, optional

The rank at which the degree of the node is computed. When None, degree of the input node is computed with respect to cells of all ranks.

sint, optional

Smallest size of cell to consider in degree.

Returns:
int

Number of cells of certain rank (or all ranks) that contain node.

degree_matrix(rank: int, index: bool = False)[source]#

Compute the degree matrix.

Parameters:
rankint

The rank (color) in the Colored Hypergraph to which the degree matrix is computed.

indexbool, default: False

If True, return will include a dictionary of row number to node uid.

Returns:
row dictionarydict

Dictionary identifying rows with nodes. If False, this does not exist.

degree_matrixscipy.sparse.csr.csr_matrix

The degree matrix.

property dim: int#

Return the dimension of the colored hypergraph.

Returns:
int

The dimension of the colored hypergraph.

from_networkx_graph(G) None[source]#

Construct a Colored Hypergraph from a networkx graph.

Parameters:
GNetworkX graph

A networkx graph.

Returns:
None

The method modifies the current Colored Hypergraph.

Examples

>>> from networkx import Graph
>>> G = Graph()
>>> G.add_edge(0, 1)
>>> G.add_edge(0, 4)
>>> G.add_edge(0, 7)
>>> CHG = ColoredHyperGraph()
>>> CHG.from_networkx_graph(G)
>>> CHG.nodes
classmethod from_trimesh(mesh: Trimesh)[source]#

Import from a trimesh.

Parameters:
meshtrimesh.Trimesh

The trimesh object to import.

Examples

>>> import trimesh
>>> mesh = trimesh.Trimesh(
...     vertices=[[0, 0, 0], [0, 0, 1], [0, 1, 0]],
...     faces=[[0, 1, 2]],
...     process=False,
... )
>>> CHG = ColoredHyperGraph.from_trimesh(mesh)
>>> CHG.nodes
get_adjacency_structure_dict(i, j)[source]#

Get the adjacency structure dictionary for cells of rank i and j.

Parameters:
iint

The rank (color) of the first set of cells.

jint

The rank (color) of the second set of cells.

Returns:
dict

The adjacency structure dictionary representing the relationship between cells of rank i and j.

Notes

The adjacency structure dictionary has cells of rank i as keys and lists of cells of rank j adjacent to them as values.

get_cell_attributes(name: str, rank=None)[source]#

Get cell attributes from the colored hypergraph.

Parameters:
namestr

Attribute name.

rankint, optional

Rank of the k-cell. If specified, the function returns attributes for k-cells with the given rank. If not provided, the function returns attributes for all cells.

Returns:
dict

Dictionary of attributes keyed by cell or k-cells if rank is not None.

Examples

>>> G = nx.path_graph(3)
>>> CHG = ColoredHyperGraph(G)
>>> d = {
...     ((1, 2), 0): {"color": "red", "attr2": 1},
...     ((0, 1), 0): {"color": "blue", "attr2": 3},
... }
>>> CHG.set_cell_attributes(d)
>>> cell_color = CHG.get_cell_attributes("color")
>>> cell_color[(frozenset({0, 1}), 0)]
'blue'
get_incidence_structure_dict(i, j)[source]#

Get the incidence structure dictionary for cells of rank i and j.

Parameters:
iint

The rank (color) of the first set of cells.

jint

The rank (color) of the second set of cells.

Returns:
dict

The incidence structure dictionary representing the relationship between cells of rank i and j.

Notes

The incidence structure dictionary has cells of rank i as keys and lists of cells of rank j incident to them as values.

get_node_attributes(name: str)[source]#

Get node attributes.

Parameters:
namestr

Attribute name.

Returns:
dict

Dictionary of attributes keyed by node.

Examples

>>> G = nx.path_graph(3)
>>> CHG = ColoredHyperGraph(G)
>>> d = {0: {"color": "red", "attr2": 1}, 1: {"color": "blue", "attr2": 3}}
>>> CHG.set_node_attributes(d)
>>> CHG.get_node_attributes("color")
{0: 'red', 1: 'blue'}
>>> G = nx.Graph()
>>> G.add_nodes_from([1, 2, 3], color="blue")
>>> CHG = ColoredHyperGraph(G)
>>> nodes_color = CHG.get_node_attributes("color")
>>> nodes_color[1]
'blue'
property incidence_dict#

Return dict keyed by cell uids with values the uids of nodes in each cell.

Returns:
dict

Dictionary of cell uids with values.

incidence_matrix(rank: int, to_rank: int, weight: bool | None = None, sparse: bool = True, index: bool = False)[source]#

Compute incidence matrix for the CHG indexed by nodes x cells.

Parameters:
rankint

The rank for computing the incidence matrix.

to_rankint

The rank for computing the incidence matrix.

weightbool, default=None

If False, all nonzero entries are 1. If True and self.static, all nonzero entries are filled by self.cells.cell_weight dictionary values.

sparsebool, default=True

Whether to return a sparse or dense incidence matrix.

indexbool, default False

If True, return will include a dictionary of node uid: row number and cell uid: column number.

Returns:
incidence_matrixscipy.sparse.csr.csr_matrix or np.ndarray

The incidence matrix.

row dictionarydict

Dictionary identifying rows with nodes.

column dictionarydict

Dictionary identifying columns with cells.

laplacian_matrix(rank, sparse=False, index=False)[source]#

Compute Laplacian matrix.

Parameters:
rankint

The rank (or color) in the complex to which the Laplacian matrix is computed.

sparsebool, default: False

Specifies whether the output matrix is a scipy sparse matrix or a numpy matrix.

indexbool, default: False

If True, return will include a dictionary of node uid: row number.

Returns:
numpy.ndarray, scipy.sparse.csr.csr_matrix

Array of dimension (N, N), where N is the number of nodes in the Colored Hypergraph. If index is True, return a dictionary mapping node uid to row number.

References

[1]

Lucas, M., Cencetti, G., & Battiston, F. (2020). Multiorder Laplacian for synchronization in higher-order networks. Physical Review Research, 2(3), 033410.

new_hyperedge_key(hyperedge, rank)[source]#

Add a new key for the given hyperedge.

Parameters:
hyperedgetuple or HyperEdge

Representing node elements of the hyperedge.

rankint

Rank (color) of the input hyperedge.

Returns:
int

The new key assigned to the hyperedge.

Notes

In the standard ColoredHyperGraph class, the new key is determined by counting the number of existing hyperedges between the nodes that define the input hyperedge. The count is increased if necessary to ensure the key is unused. The first hyperedge will have key 0, then 1, and so on. If a hyperedge is removed, further new_hyperedge_keys may not be in this order.

node_to_all_cell_adjacnecy_matrix(index: bool = False, s: int | None = None)[source]#

Compute the node/all cell adjacency matrix.

Parameters:
indexbool, optional

If True, will return a rowdict of row to node uid.

sint, optional

Minimum number of edges shared by neighbors with the node.

Returns:
row dictionarydict

Dictionary identifying rows with nodes. If False, this does not exist.

cell adjacency matrixscipy.sparse.csr.csr_matrix

The cell adjacency matrix.

node_to_all_cell_incidence_matrix(weight: str | None = None, index: bool = False) csc_matrix | tuple[dict, dict, csc_matrix][source]#

Nodes/all cells incidence matrix for the indexed by nodes X cells.

Parameters:
weightstr, optional

If not given, all nonzero entries are 1.

indexbool, default=False

If True return will include a dictionary of node uid : row number and cell uid : column number.

Returns:
scipy.sparse.csr.csc_matrix | tuple[dict, dict, scipy.sparse.csc_matrix]

The incidence matrix, if index is False, otherwise lower (row) index dict, upper (col) index dict, incidence matrix where the index dictionaries map from the entity (as Hashable or tuple) to the row or col index of the matrix.

property nodes#

Object associated with self.elements.

Returns:
NodeView

NodeView of all nodes.

number_of_cells(cell_set=None)[source]#

Compute the number of cells in the colored hypergraph.

Parameters:
cell_setiterable of HyperEdge, optional

If provided, computes the number of cells belonging to the specified cell_set. If None, returns the total number of cells in the hypergraph.

Returns:
int

The number of cells in the specified cell_set or the total number of cells if cell_set is None.

number_of_nodes(node_set=None)[source]#

Compute the number of nodes in node_set belonging to the CHG.

Parameters:
node_setan interable of Entities, optional

If None, then return the number of nodes in the CHG.

Returns:
int

Number of nodes in node_set belonging to the CHG.

order()[source]#

Compute the number of nodes in the CHG.

Returns:
int

The number of nudes in this hypergraph.

property ranks#

Return the sorted list of ranks in the colored hypergraph.

Returns:
list

The sorted list of ranks.

remove_cell(cell) None[source]#

Remove a single cell from the ColoredHyperGraph.

Parameters:
cellHashable or RankedEntity

The cell to be removed.

Returns:
None

The cell is removed in place.

Notes

Deletes the reference to the cell from all of its nodes. If any of its nodes do not belong to any other cells, the node is dropped from the ColoredHyperGraph.

remove_cells(cell_set) None[source]#

Remove cells from this colored hypergraph.

Parameters:
cell_setiterable of hashables

The cells to remove from this colored hypergraph.

remove_node(node)[source]#

Remove a node from the ColoredHyperGraph.

This method removes a node from the cells and deletes any reference in the nodes of the CHG.

Parameters:
nodeHyperEdge or Hashable

The node to be removed.

Returns:
ColoredHyperGraph

The ColoredHyperGraph instance after removing the node.

remove_nodes(node_set) None[source]#

Remove nodes from cells.

This also deletes references in colored hypergraph nodes.

Parameters:
node_setan iterable of hashables

The nodes to remove from this colored hypergraph.

remove_singletons()[source]#

Construct new CHG with singleton cells removed.

Returns:
ColoredHyperGraph

Return new CHG with singleton cells removed.

restrict_to_cells(cell_set)[source]#

Construct a Colored Hypergraph using a subset of the cells.

Parameters:
cell_sethashable

A subset of elements of the Colored Hypergraph cells.

Returns:
ColoredHyperGraph

The Colored Hypergraph constructed from the specified subset of cells.

restrict_to_nodes(node_set)[source]#

Restrict to a set of nodes.

Constructs a new Colored Hypergraph by restricting the cells in the Colored Hypergraph to the nodes referenced by node_set.

Parameters:
node_setiterable of hashables

References a subset of elements of self.nodes.

Returns:
ColoredHyperGraph

A new Colored Hypergraph restricted to the specified node_set.

set_cell_attributes(values, name: str | None = None) None[source]#

Set cell attributes.

Parameters:
valuesdict

Dictionary of attributes to set for the cell.

namestr, optional

Name of the attribute to set for the cell.

Returns:
None

Set the attributes of the cells.

Examples

After computing some property of the cell of a Colored Hypergraph, you may want to assign a cell attribute to store the value of that property for each cell:

>>> CHG = ColoredHyperGraph()
>>> CHG.add_cell([1, 2, 3, 4], rank=2)
>>> CHG.add_cell([1, 2, 4], rank=2)
>>> CHG.add_cell([3, 4], rank=2)
>>> d = {((1, 2, 3, 4), 0): "red", ((1, 2, 4), 0): "blue", ((3, 4), 0): "green"}
>>> CHG.set_cell_attributes(d, name="color")
>>> CHG.cells[((3, 4), 0)]["color"]
'green'

If you provide a dictionary of dictionaries as the second argument, the entire dictionary will be used to update edge attributes:

>>> G = nx.path_graph(3)
>>> CHG = ColoredHyperGraph(G)
>>> d = {
...     ((1, 2), 0): {"color": "red", "attr2": 1},
...     ((0, 1), 0): {"color": "blue", "attr2": 3},
... }
>>> CHG.set_cell_attributes(d)
>>> CHG.cells[((0, 1), 0)]["color"]
'blue'
3

Note that if the dict contains cells that are not in self.cells, they are silently ignored.

set_node_attributes(values, name: str | None = None) None[source]#

Set node attributes.

Parameters:
valuesdict

A dictionary where keys are nodes and values are the attributes to set.

namestr or None, optional

The name of the attribute to set for all nodes. If None, attributes will be set individually for each node.

Returns:
None

Set the attributes of the nodes.

property shape: tuple[int, ...]#

Return shape.

This is: (number of cells[i], for i in range(0,dim(CHG)) )

Returns:
tuple of ints

Tuple of number of cells in each rank.

singletons()[source]#

Return a list of singleton cell.

A singleton cell is a cell of size 1 with a node of degree 1.

Returns:
list

A list of cells uids.

size(cell)[source]#

Compute the number of nodes in the colored hypergraph that belong to a specific cell.

Parameters:
cellhashable or HyperEdge

The cell for which to compute the size.

Returns:
int

The number of nodes in the colored hypergraph that belong to the specified cell.

Raises:
ValueError

If the input cell is not in the cells of the colored hypergraph.

skeleton(rank: int)[source]#

Return skeleton.

Parameters:
rankint

Rank.

Returns:
int

Number of cells in specified rank.

Combinatorial Complex#

Creation and manipulation of a combinatorial complex.

class toponetx.classes.combinatorial_complex.CombinatorialComplex(cells: Collection | None = None, ranks: Collection | None = None, graph_based: bool = False, **kwargs)[source]#

Class for Combinatorial Complex.

A Combinatorial Complex (CCC) is a triple $CCC = (S, X, rk)$ where: - $S$ is an abstract set of entities, - $X$ a subset of the power set of $X$, and - $rk$ is the a rank function that associates for every set x in X a rank, a positive integer.

The rank function $rk$ must satisfy $x leq y$ then $rk(x) leq rk(y)$. We call this condition the CCC condition.

A CCC is a generlization of graphs, hypergraphs, cellular and simplicial complexes.

Mathematical Example:

Let $S = {1, 2, 3, 4}$ be a set of abstract entities. Let $X = {{1, 2}, {1, 2, 3}, {1, 3}, {1, 4}}$ be a subset of the power set of $S$. Let rk be the ranking function that assigns the length of a set as its rank, i.e. $rk({1, 2}) = 2$, $rk({1, 2, 3}) = 3$, etc.

Then, $(S, X, rk)$ is a combinatorial complex.

Parameters:
cellsCollection, optional

A collection of cells to add to the combinatorial complex.

ranksCollection, optional

When cells is an iterable or dictionary, ranks cannot be None and it must be iterable/dict of the same size as cells.

graph_basedbool, default=False

When true rank 1 edges must have cardinality equals to 1.

**kwargskeyword arguments, optional

Attributes to add to the complex as key=value pairs.

Raises:
TypeError

If cells is not given as an Iterable.

ValueError

If input cells is not an instance of HyperEdge when rank is None. If input HyperEdge has None rank when rank is specified. If cells and ranks do not have an equal number of elements.

Examples

Define an empty combinatorial complex:

>>> CCC = CombinatorialComplex()

Add cells to the combinatorial complex:

>>> CCC = CombinatorialComplex()
>>> CCC.add_cell([1, 2], rank=1)
>>> CCC.add_cell([3, 4], rank=1)
>>> CCC.add_cell([1, 2, 3, 4], rank=2)
>>> CCC.add_cell([1, 2, 4], rank=2)
>>> CCC.add_cell([1, 2, 3, 4, 5, 6, 7], rank=3)
Attributes:
complexdict

A dictionary that can be used to store additional information about the complex.

add_cell(cell, rank=None, **attr) None[source]#

Add a single cells to combinatorial complex.

Parameters:
cellhashable, iterable or HyperEdge

If hashable the cell returned will be empty.

rankint

Rank of the cell.

**attrkeyword arguments, optional

Attributes to add to the cell as key=value pairs.

add_cells_from(cells, ranks: Iterable[int] | int | None = None) None[source]#

Add cells to combinatorial complex.

Parameters:
cellsiterable of hashables

For hashables the cells returned will be empty.

ranksiterable or int, optional

When iterable, len(ranks) == len(cells).

add_node(node, **attr) None[source]#

Add a node to a CCC.

Parameters:
nodeHashable

The node to add to this combinatorial complex.

**attrkeyword arguments, optional

Attributes to add to the node as key=value pairs.

adjacency_matrix(rank, via_rank, s: int = 1, index: bool = False)[source]#

Sparse weighted s-adjacency matrix.

Parameters:
rank, via_rankint

Two ranks for skeletons in the input combinatorial complex.

sint, default=1

Minimum number of edges shared by neighbors with node.

indexbool, default=False

Whether to return the indices of the rows and columns of the adjacency matrix.

Returns:
indiceslist

List identifying the rows and columns of the adjacency matrix with the cells of the combinatorial complex. Only returned if index is True.

adjacency_matrixscipy.sparse.csr.csr_matrix

The adjacency matrix of this combinatorial complex.

Examples

>>> CCC = CombinatorialComplex()
>>> CCC.add_cell([1, 2], rank=1)
>>> CCC.add_cell([3, 4], rank=1)
>>> CCC.add_cell([1, 2, 3, 4], rank=2)
>>> CCC.add_cell([1, 2, 4], rank=2)
>>> CCC.add_cell([1, 2, 3, 4, 5, 6, 7], rank=3)
>>> CCC.adjacency_matrix(0, 1)
property cells#

Object associated with self._cells.

Returns:
HyperEdgeView

Returns all the present cells in the combinatorial complex along with their rank.

clone() CombinatorialComplex[source]#

Return a copy of the simplex.

The clone method by default returns an independent shallow copy of the simplex and attributes. That is, if an attribute is a container, that container is shared by the original and the copy. Use Python’s copy.deepcopy for new containers.

Returns:
CombinatorialComplex

A copy of this combinatorial complex.

coadjacency_matrix(rank, via_rank, s: int | None = None, index: bool = False)[source]#

Compute the coadjacency matrix of self.

The sparse weighted s-coadjacency matrix

Parameters:
rank, via_rankint

Two ranks for skeletons in the input combinatorial complex , such that r>k.

sint, optional

Minimum number of edges shared by neighbors with node.

indexbool, default=False

Whether to return the indices of the rows and columns of the coadjacency matrix.

Returns:
indiceslist

List identifying the rows and columns of the coadjacency matrix with the cells of the combinatorial complex. Only returned if index is True.

coadjacency_matrixscipy.sparse.csr.csr_matrix

The coadjacency matrix of this combinatorial complex.

dirac_operator_matrix(weight: str | None = None, index: bool = False)[source]#

Compute dirac operator matrix of self.

Parameters:
weightstr, optional

The name of the cell attribute to use as weights for the dirac operator matrix. If None, the matrix is binary.

indexbool, default=False

If True, return will include a dictionary of all cells in the complex uid.

Returns:
scipy.sparse.csr.csc_matrix | tuple[dict, dict, scipy.sparse.csc_matrix]

The dirac operator matrix, if index is False; otherwise, row_indices, col_indices : dict List identifying rows and columns of the dirac operator matrix. Only returned if index is True. dirac_matrix : scipy.sparse.csr.csc_matrix The dirac operator matrix of this combinatorial complex.

Examples

>>> from toponetx.classes import CombinatorialComplex
>>> CCC = CombinatorialComplex()
>>> CCC.add_cell([1, 2, 3, 4], rank=2)
>>> CCC.add_cell([1, 2], rank=1)
>>> CCC.add_cell([2, 3], rank=1)
>>> CCC.add_cell([1, 4], rank=1)
>>> CCC.add_cell([3, 4, 8], rank=2)
>>> CCC.dirac_operator_matrix()
get_cell_attributes(name: str, rank: int | None = None)[source]#

Get node attributes from graph.

Parameters:
namestr

Attribute name.

rankint

Restrict the returned attribute values to cells of a specific rank.

Returns:
dict

Dictionary of attributes keyed by cell or k-cells if k is not None.

Examples

>>> G = nx.path_graph(3)
>>> CCC = CombinatorialComplex(G)
>>> d = {
...     (1, 2): {"color": "red", "attr2": 1},
...     (0, 1): {"color": "blue", "attr2": 3},
... }
>>> CCC.set_cell_attributes(d)
>>> cell_color = CCC.get_cell_attributes("color")
>>> cell_color[frozenset({0, 1})]
'blue'
get_node_attributes(name: str) dict[Hashable, Any][source]#

Get node attributes.

Parameters:
namestr

Attribute name.

Returns:
dict[Hashable, Any]

Dictionary mapping each node to the value of the given attribute name.

Examples

>>> G = nx.path_graph(3)
>>> CCC = CombinatorialComplex(G)
>>> d = {0: {"color": "red", "attr2": 1}, 1: {"color": "blue", "attr2": 3}}
>>> CCC.set_node_attributes(d)
>>> CCC.get_node_attributes("color")
{0: 'red', 1: 'blue'}
>>> G = nx.Graph()
>>> G.add_nodes_from([1, 2, 3], color="blue")
>>> CCC = CombinatorialComplex(G)
>>> nodes_color = CCC.get_node_attributes("color")
>>> nodes_color[1]
'blue'
incidence_matrix(rank: int, to_rank=None, incidence_type: Literal['up', 'down'] = 'up', weight: str | None = None, sparse: bool = True, index: bool = False)[source]#

Compute incidence matrix for the CCC between rank and to_rank skeleti.

Parameters:
rank, to_rankint

For which rank of cells to compute the incidence matrix.

incidence_type{“up”, “down”}, default=”up”

Whether to compute the up or down incidence matrix.

weightbool, default=False

The name of the cell attribute to use as weights for the incidence matrix. If None, all cell weights are considered to be one.

sparsebool, default=True

Whether to return a sparse or dense incidence matrix.

indexbool, default=False

Whether to return the indices of the rows and columns of the incidence matrix.

Returns:
row_indices, col_indicesdict

Dictionary assigning each row and column of the incidence matrix to a cell.

incidence_matrixscipy.sparse.csr.csr_matrix

The incidence matrix.

property nodes#

Object associated with self.elements.

Returns:
NodeView

Returns all the nodes of the combinatorial complex.

number_of_cells(cell_set=None) int[source]#

Compute the number of cells in cell_set belonging to the CCC.

Parameters:
cell_setan interable of HyperEdge, optional

If None, then return the number of cells.

Returns:
int

The number of cells in cell_set belonging to this combinatorial complex.

number_of_nodes(node_set=None) int[source]#

Compute the number of nodes in node_set belonging to the CCC.

Parameters:
node_setan interable of Entities, optional

If None, then return the number of nodes in the CCC.

Returns:
int

The number of nodes in node_set belonging to this combinatorial complex.

order() int[source]#

Compute the number of nodes in the CCC.

Returns:
int

The number of nodes in this combinatorial complex.

remove_cell(cell) None[source]#

Remove a single cell from CCC.

Parameters:
cellhashable or RankedEntity

The cell to remove from this combinatorial complex.

Notes

Deletes reference to cell from all of its nodes. If any of its nodes do not belong to any other cells the node is dropped from self.

remove_cells(cell_set) None[source]#

Remove cells from CCC.

Parameters:
cell_setiterable of hashables

The cells to remove from this combinatorial complex.

remove_node(node) None[source]#

Remove node from cells.

This also deletes any reference in the nodes of the CCC. This also deletes cell references in higher ranks for the particular node.

Parameters:
nodehashable or HyperEdge

The node to remove from this combinatorial complex.

remove_nodes(node_set) None[source]#

Remove nodes from cells.

This also deletes references in combinatorial complex nodes.

Parameters:
node_setan iterable of hashables

The nodes to remove from this combinatorial complex.

remove_singletons()[source]#

Construct new CCC with singleton cells removed.

Returns:
CombinatorialComplex

A copy of this combinatorial complex with singleton cells removed.

set_cell_attributes(values, name: str | None = None) None[source]#

Set cell attributes.

Parameters:
valuesdict

Dictionary of cell attributes to set keyed by cell name.

namestr, optional

Attribute name.

Examples

After computing some property of the cell of a combinatorial complex, you may want to assign a cell attribute to store the value of that property for each cell:

>>> CCC = CombinatorialComplex()
>>> CCC.add_cell([1, 2, 3, 4], rank=2)
>>> CCC.add_cell([1, 2, 4], rank=2)
>>> CCC.add_cell([3, 4], rank=2)
>>> d = {(1, 2, 3, 4): "red", (1, 2, 3): "blue", (3, 4): "green"}
>>> CCC.set_cell_attributes(d, name="color")
>>> CCC.cells[(3, 4)]["color"]
'green'

If you provide a dictionary of dictionaries as the second argument, the entire dictionary will be used to update edge attributes:

>>> G = nx.path_graph(3)
>>> CCC = CombinatorialComplex(G)
>>> d = {
...     (1, 2): {"color": "red", "attr2": 1},
...     (0, 1): {"color": "blue", "attr2": 3},
... }
>>> CCC.set_cell_attributes(d)
>>> CCC.cells[(0, 1)]["color"]
'blue'
3

Note that if the dict contains cells that are not in self.cells, they are silently ignored.

property shape#

Return shape.

This is: (number of cells[i], for i in range(0,dim(CCC)) )

Returns:
tuple of ints

Shape of the CC object.

singletons()[source]#

Return a list of singleton cell.

A singleton cell is a node of degree 0.

Returns:
list

A list of cells uids.

Examples

>>> CCC = CombinatorialComplex()
>>> CCC.add_cell([1, 2], rank=1)
>>> CCC.add_cell([3, 4], rank=1)
>>> CCC.add_cell([9], rank=0)
>>> CCC.singletons()
skeleton(rank: int, level: Literal['equal', 'upper', 'up', 'lower', 'down', 'uppereq', 'upeq', 'lowereq', 'downeq'] = 'equal')[source]#

Skeleton of the CCC.

Parameters:
rankint

The rank of the skeleton.

levelstr, default=”equal”

Level of the skeleton.

Returns:
list of HyperEdge

The skeleton of the CCC.

Complex#

Abstract class for Complex and Atom.

class toponetx.classes.complex.Atom(elements: AtomCollectionType, **kwargs)[source]#

Abstract class representing an atom in a complex.

Parameters:
elementsCollection[Hashable]

The elements in the atom.

**kwargskeyword arguments, optional

Additional attributes to be associated with the atom.

update(attributes: dict) None[source]#

Update the attributes of the atom.

Parameters:
attributesdict

The attributes to be updated.

class toponetx.classes.complex.Complex(**kwargs)[source]#

Abstract class representing a complex.

A complex is a space that is constructed by attaching lower-dimensional cells to a topological space to form a new space. The cells are attached to the space in a specific way, and the resulting space has a well-defined structure.

Example of complexes:

(1) Cell Complexes : Cell complexes can be used to represent various mathematical objects, such as graphs, manifolds, and discrete geometric shapes. They are useful in many areas of mathematics, such as algebraic topology and geometry, where they can be used to study the structure and properties of these objects.

(2) Simplicial Complexes : Simplicial complexes are mathematical structures used to study the topological properties of shapes and spaces. They are made up of a set of points called vertices, and a collection of simplices (triangles, tetrahedra, etc.) that are connected to each other in a specific way. Each simplex is formed by a subset of the vertices and is considered a building block of the complex. The properties of the complex are determined by the combinatorial arrangement of the simplices and their connectivity. Simplicial complexes are used in many areas of mathematics and computer science, such as geometric modeling, data analysis, and machine learning.

(3) The CombinatorialComplex class represents a combinatorial complex, which is a mathematical structure consisting of a set of points, a subset of the power set of points, and a ranking function that assigns a rank to each subset based on its size. These classes are used in many areas of mathematics and computer science, such as geometric modeling, data analysis, and machine learning.

Parameters:
**kwargskeyword arguments, optional

Attributes to add to the complex as key=value pairs.

Attributes:
complexdict

A dictionary that can be used to store additional information about the complex.

abstract add_node(node: Hashable) None[source]#

Add node to the complex.

Parameters:
nodeHashable

The node to be added.

abstract adjacency_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False)[source]#

Return adjacency matrix of the complex.

Parameters:
rankint

The rank of the atoms to consider.

signedbool, default=True

If True, the adjacency matrix is signed, otherwise it is unsigned.

weightstr, optional

The name of the attribute to use as weights for the adjacency matrix.

indexbool, default=False

If True, the adjacency matrix is indexed by the atoms of the complex.

abstract clone() Complex[source]#

Clone complex.

abstract coadjacency_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False)[source]#

Return coadjacency matrix of the complex.

Parameters:
rankint

The rank of the atoms to consider.

signedbool, default=True

If True, the adjacency matrix is signed, otherwise it is unsigned.

weightstr, optional

The name of the attribute to use as weights for the adjacency matrix.

indexbool, default=False

If True, the adjacency matrix is indexed by the atoms of the complex.

abstract property dim: int#

Return dimension of the complex.

Returns:
int

Returns the dimension of the complex.

abstract incidence_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False)[source]#

Return incidence matrix of the complex.

Parameters:
rankint

The rank of the atoms to consider.

signedbool, default=True

If True, the incidence matrix is signed, otherwise it is unsigned.

weightstr, optional

The name of the attribute to use as weights for the incidence matrix.

indexbool, default=False

If True, the incidence matrix is indexed by the nodes of the complex.

abstract property nodes#

Return the node container.

abstract remove_nodes(node_set: Iterator[Hashable]) None[source]#

Remove the given nodes from the complex.

Any elements that become invalid due to the removal of nodes are also removed.

Parameters:
node_setIterator[Hashable]

The nodes to be removed.

abstract property shape: tuple[int, ...]#

Return number of cells for each rank.

Returns:
tuple of ints

The number of elements for each rank. If the complex is empty, an empty tuple is returned.

abstract skeleton(rank: int)[source]#

Return the atoms of given rank in this complex.

Parameters:
rankint

The rank of the skeleton.

Hyperedge#

HyperEdge classes.

class toponetx.classes.hyperedge.HyperEdge(elements: Collection, rank=None, **kwargs)[source]#

Class for a hyperedge (or a set-type cell).

This class represents a set-type cell in a combinatorial complex, which is a set of nodes with optional attributes and a rank. The nodes in a hyperedge must be hashable and unique, and the hyperedge itself is immutable.

Parameters:
elementsiterable of hashables

The nodes in the hyperedge.

rankint, optional

The rank of the hyperedge. Default is None.

**kwargsadditional attributes

Additional attributes of the hyperedge, as keyword arguments.

Examples

>>> ac1 = HyperEdge((1, 2, 3))
>>> ac2 = HyperEdge((1, 2, 4, 5))
>>> ac3 = HyperEdge(("a", "b", "c"))
>>> ac3 = HyperEdge(("a", "b", "c"), rank=10)
property rank#

Rank of the HyperEdge.

Returns:
int or None

The rank of the HyperEdge if it is not None, None otherwise.

Path Complex#

Path complex.

class toponetx.classes.path_complex.PathComplex(paths: Graph | Iterable[Sequence[Hashable] | Path] | None = None, reserve_sequence_order: bool = False, allowed_paths: Iterable[tuple[Hashable]] | None = None, max_rank: int = 3, **kwargs)[source]#

A class representing a path complex based on simple paths as proposed in [1].

The original path complex is defined in [2].

A path complex contains elementary p-paths that span the space of simple paths. Path complexes are a topological structure whose building blocks are paths, which are essentially different from simplicial complexes and cell complexes. If certain conditions are met, path complexes can generalize simplicial complexes.

For example, a triangle with three vertices 1, 2, and 3 can be represented as a simplicial complex (1, 2, 3). Similarly, it can be represented as a path complex (1, 2, 3) whose boundary 1-paths are (1, 2), (2, 3), and (1, 3). Another example is a simple path (1, 2, 3). In this case, we cannot lift the simple path to a 2-dimensional simplicial complex, as triangle does not exist. However, we can still represent the simple path as a path complex (1, 2, 3) whose boundary 1-paths are (1, 2) and (2, 3) (1-path (1,3) does not exist).

Parameters:
pathsnx.Graph or Iterable[Sequence[Hashable]]

The paths in the path complex. If a graph is provided, the path complex will be constructed from the graph, and allowed paths are automatically computed.

reserve_sequence_orderbool, default=False

If True, reserve the order of the sub-sequence of nodes in the elementary p-path. Else, the sub-sequence of nodes in the elementary p-path will be reversed if the first index is larger than the last index.

allowed_pathsIterable[tuple[Hashable]], optional

An iterable of allowed boundaries. If None and the input is a Graph, allowed paths are automatically computed by enumerating all simple paths in the graph whose length is less than or equal to max_rank. If None and the input is not a Graph, allowed paths contain the input paths, their truncated subsequences (sub-sequences where the first or the last index is omitted), and any sub-sequences of the truncated subsequences in a recursive manner.

max_rankint, default=3

The maximal length of a path in the path complex.

**kwargskeyword arguments, optional

Additional attributes to be associated with the path complex.

Notes

  • By the definition established by [2], a path complex P is a non-empty collection of elementary p-paths such that for any sequence

of vertices in a finite non-empty set V that belong to P, the truncated sequence of vertices (which is sometimes referred to as obvious boundaries) also belongs to P. The truncated sequences are sub-sequences of the original elementary p-path where the first or the last index is omitted. For instance, if we have an elementary p-path (1, 2, 3), the truncated sequences are (1,2) and (2,3). - Path complex in [1] is different from the path complex defined in [2] in the sense that elementary p-paths span the space of simple paths. The path complex originally proposed has elementary p-paths that span the space of boundary-invariant paths. - The path complex is a simplicial complex if certain conditions are met [2]. - Unlike hypergraphs, combinatorial, simplicial, and cell complexes, path complexes take into account the order of sequences.

References

[1] (1,2)

Truong and Chin. Generalizing Topological Graph Neural Networks with Paths. https://arxiv.org/abs/2308.06838.pdf

[2] (1,2,3,4)

Grigor’yan, Lin, Muranov, and Yau. Homologies of path complexes and digraphs. https://arxiv.org/pdf/1207.2834.pdf

Examples

>>> PC = PathComplex([(1, 2, 3)])
>>> PC.paths
PathView([(1,), (2,), (3,), (1, 2), (2, 3), (1, 2, 3)])
>>> PC.add_paths_from([(1, 2, 4), (1, 2, 5), (4, 5)])
>>> PC.paths
PathView([(1,), (2,), (3,), (4,), (5,), (1, 2), (2, 3), (2, 4), (2, 5), (4, 5), (1, 2, 3), (1, 2, 4), (1, 2, 5)])
>>> G = nx.Graph()
>>> G.add_edges_from([(1, 2), (2, 3), (1, 3)])
>>> PC = PathComplex(G)
>>> PC.paths
PathView([(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 3, 2), (1, 2, 3), (2, 1, 3)])
add_node(node: Hashable | Path, **attr) None[source]#

Add node to the path complex.

Parameters:
nodeHashable or Path

A Hashable or singleton Path representing a node in a path complex.

**attrkeyword arguments

Additional attributes to be provided as keyword arguments.

add_path(path: Hashable | Sequence[Hashable] | Path, **attr) None[source]#

Add an elementary path to the path complex.

An elementary p-path is a sequence of nodes $(n_1, …, n_p)$ where p is the length of the sequence. In a path complex, for every elementary p-path in the path complex, their truncated sequences $(n_2, …, n_p)$ and $(n_1, …, n_{p-1})$ are also in the path complex.

This method automatically initializes any obvious sub-paths (sub-paths where the first or last index is omitted) of the elementary path if not available. In order to add non-obvious sub-paths, manually add the sub-paths.

Parameters:
pathHashable or Sequence[Hashable] or Path

A Hashable or Sequence[Hashable] or Path representing a path in a path complex.

**attrkeyword arguments, optional

Additional keyword arguments to be passed to the method.

Examples

>>> PC = PathComplex([(1, 2, 3)])
>>> PC.paths
PathView([(1,), (2,), (3,), (1, 2), (2, 3), (1, 2, 3)])
>>> PC.add_path((1, 2, 4))
>>> PC.paths
PathView([(1,), (2,), (3,), (4,), (1, 2), (2, 3), (2, 4), (1, 2, 3), (1, 2, 4)])
add_paths_from(paths: Iterable[Sequence[Hashable] | Path]) None[source]#

Add elementary paths from an iterable of elementary paths.

An elementary p-path is a sequence of nodes $(n_1, …, n_p)$ where $p$ is the length of the sequence. In a path complex, for every elementary p-path in the path complex, their truncated sequences $(n_2, …, n_p)$ and $(n1, …, n_{p-1})$ are also in the path complex.

Parameters:
pathsIterable[Sequence[Hashable] or Path]

An iterable of paths as lists, tuples, or Path objects.

Examples

>>> PC = PathComplex([(1, 2, 3)])
>>> PC.paths
PathView([(1,), (2,), (3,), (1, 2), (2, 3), (1, 2, 3)])
>>> PC.add_paths_from([(1, 2, 4), (1, 2, 5), (4, 5)])
>>> PC.paths
PathView([(1,), (2,), (3,), (4,), (5,), (1, 2), (2, 3), (2, 4), (2, 5), (4, 5), (1, 2, 3), (1, 2, 4), (1, 2, 5)])
adjacency_matrix(rank: int, signed: bool = False, weight: str | None = None, index: bool = False) tuple[dict, lil_matrix] | lil_matrix[source]#

Compute adjacency matrix of the path complex.

Parameters:
rankint

The dimension of the adjacency matrix.

signedbool, default=False

If True, return signed adjacency matrix. Else, return absolute adjacency matrix.

weightstr, default=None, optional

The weight to be used for the adjacency matrix.

indexbool, default=False

If True, return adjacency matrix with indices. Else, return adjacency matrix without indices.

Returns:
tuple[dict, sp.sparse.lil_matrix] | sp.sparse.lil_matrix

If index is True, return a tuple of (idx_p, adjacency_matrix) else return adjacency_matrix.

clone() PathComplex[source]#

Return a copy of the path complex.

The clone method by default returns an independent shallow copy of the path complex. Use Python’s copy.deepcopy for new containers.

Returns:
PathComplex

Returns a copy of the PathComplex.

coadjacency_matrix(rank: int, signed: bool = False, weight: str | None = None, index: bool = False) tuple[dict, lil_matrix] | lil_matrix[source]#

Compute coadjacency matrix of the path complex.

Parameters:
rankint

The dimension of the coadjacency matrix.

signedbool, default=False

If True, return signed coadjacency matrix. Else, return absolute coadjacency matrix.

weightstr, default=None, optional

The weight to be used for the adjacency matrix.

indexbool, default=False

If True, return coadjacency matrix with indices. Else, return coadjacency matrix without indices.

Returns:
tuple[dict, sp.sparse.lil_matrix] | sp.sparse.lil_matrix

If index is True, return a tuple of (idx_p, coadjacency_matrix) else return coadjacency_matrix.

static compute_allowed_paths(graph: Graph, reserve_sequence_order: bool = False, max_rank: int = 3) set[list | tuple][source]#

Compute allowed paths from a graph.

Allowed paths are automatically computed by enumerating all simple paths in the graph whose length is less than or equal to max_rank. Allowed paths allow us to restrict the boundary of an elementary p-path to only sequences that exist in the graph.

Parameters:
graphnx.Graph

A graph.

reserve_sequence_orderbool, default=False

If True, reserve the order of the sub-sequence of nodes in the elmentary p-path. Else, the sub-sequence of nodes in the elementary p-path will be reversed if the first index is larger than the last index.

max_rankint, default=3

The maximal length of a path in the path complex.

Returns:
set[list | tuple]

A set of allowed paths.

Examples

>>> G = nx.Graph()
>>> G.add_edges_from([(1, 2), (2, 3), (1, 3), (0, 1)])
>>> allowed_paths = PathComplex.compute_allowed_paths(G, max_rank=2)
>>> allowed_paths
{(0, 1), (1, 3), (1, 2), (2,), (1, 3, 2), (0, 1, 2), (0, 1, 3), (1, 2, 3), (2, 1, 3), (2, 3), (1,), (0,), (3,)}
property dim: int#

Dimension.

Returns:
int

This is the highest dimension of any elementary p-path in the complex.

down_laplacian_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False) tuple[dict, lil_matrix] | lil_matrix[source]#

Compute down laplacian matrix of the path complex.

Parameters:
rankint

The dimension of the down laplacian matrix.

signedbool, default=True

If True, return signed down laplacian matrix. Else, return absolute down laplacian matrix.

weightstr, optional

If not given, all nonzero entries are 1.

indexbool, default=False

If True, return down laplacian matrix with indices. Else, return down laplacian matrix without indices.

Returns:
tuple[dict, sp.sparse.lil_matrix] | sp.sparse.lil_matrix

If index is True, return a tuple of (idx_p, down_laplacian_matrix) otherwise if index is False, return down_laplacian_matrix.

property edges: EdgeView#

Edges.

Returns:
EdgeView

A view of all edges in the path complex.

get_edge_attributes(name: str) dict[tuple[Hashable], Any][source]#

Get edge attributes from a path complex.

Parameters:
namestr

The name of the edge attribute.

Returns:
dict[tuple[Hashable], Any]

A dictionary of edge and its associated value for the given attribute name.

Examples

>>> PC = PathComplex()
>>> PC.add_paths_from([[0, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3]])
>>> PC.add_path([0, 1], weight=32)
>>> PC.add_path([1, 2], weight=98)
>>> PC.add_path([1, 3], color="red")
>>> PC.add_path([2, 3], color="blue")
>>> PC.get_edge_attributes("weight")
{(0, 1): 32, (1, 2): 98}
>>> PC.get_edge_attributes("color")
{(1, 3): "red", (2, 3): "blue"}
get_node_attributes(name: str) dict[tuple[Hashable], Any][source]#

Get node attributes from a path complex.

Parameters:
namestr

The name of the node attribute.

Returns:
dict[tuple[Hashable], Any]

A dictionary of node and its associated value for the given attribute name.

Examples

>>> PC = PathComplex()
>>> PC.add_node(0)
>>> PC.add_node(1, heat=55)
>>> PC.add_node(2, heat=66)
>>> PC.add_node(3, color="red")
>>> PC.add_node(2, color="blue")
>>> PC.add_paths_from([[0, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3]])
>>> PC.get_node_attributes("heat")
{(1, ): 55, (2, ): 66}
>>> PC.get_node_attributes("color")
{(2, ): "blue", (3, ): "red"}
get_path_attributes(name: str) dict[tuple[Hashable], Any][source]#

Get path attributes from a path complex.

Parameters:
namestr

The name of the path attribute.

Returns:
dict[tuple[Hashable], Any]

A dictionary of path and its associated value for the given attribute name.

Examples

>>> PC = PathComplex()
>>> PC.add_paths_from([[0, 1]])
>>> PC.add_path([0, 1, 2], weight=43)
>>> PC.add_path([0, 1, 3], weight=98)
>>> PC.add_path([1, 2, 3], color="red")
>>> PC.add_path([1, 3, 2], color="blue")
>>> PC.add_path([2, 1, 3], color="green")
>>> PC.get_path_attributes("weight")
{(0, 1, 2): 43, (0, 1, 3): 98}
>>> PC.get_path_attributes("color")
{(1, 2, 3): "red", (1, 3, 2): "blue", (2, 1, 3): "green"}
hodge_laplacian_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False) tuple[dict, lil_matrix] | lil_matrix[source]#

Compute Hodge Laplacian matrix of the path complex.

Parameters:
rankint

The dimension of the Hodge Laplacian matrix.

signedbool, default=False

If True, return signed Hodge Laplacian matrix. Else, return absolute Hodge Laplacian matrix.

weightstr, optional

If not given, all nonzero entries are 1.

indexbool, default=False

If True, return Hodge Laplacian matrix with indices. Else, return Hodge Laplacian matrix without indices.

Returns:
tuple[dict, sp.sparse.lil_matrix] | sp.sparse.lil_matrix

When index is True, return a tuple of (idx_p, Laplacian) else return Laplacian.

incidence_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False) tuple[dict, dict, lil_matrix] | lil_matrix[source]#

Compute incidence matrix of the path complex.

Parameters:
rankint

The dimension of the incidence matrix.

signedbool, default=True

If True, return signed incidence matrix. Else, return absolute incidence matrix.

weightstr, optional

If not given, all nonzero entries are 1.

indexbool, default=False

If True, return incidence matrix with indices. Else, return incidence matrix without indices.

Returns:
tuple[dict, dict, sp.sparse.lil_matrix] | sp.sparse.lil_matrix

If index is True, return a tuple of (idx_p_minus_1, idx_p, incidence_matrix) otherwise if index is False, return incidence_matrix.

property nodes: NodeView#

Nodes.

Returns:
NodeView

A view of all nodes in the path complex.

property paths: PathView#

Set of all elementary p-paths.

Returns:
PathView

A view of all elementary p-paths in the path complex.

remove_nodes(node_set: Iterable[Hashable]) None[source]#

Remove nodes from the path complex.

Parameters:
node_setIterable[Hashable]

An iterable of nodes to be removed.

restrict_to_nodes(node_set: Iterable[Hashable])[source]#

Return a new path complex restricted to a subset of nodes.

Parameters:
node_setIterable[Hashable]

The nodes to be used for the new restricted path complex.

Returns:
PathComplex

The new path complex restricted to the provided subset of nodes.

Examples

>>> PC = PathComplex(
...     [[0, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3], [0, 1, 2], [0, 1, 3]]
... )
>>> PC = PC.restrict_to_nodes([0, 1, 3])
>>> PC.paths
PathView([(0,), (1,), (3,), (0, 1), (1, 3), (0, 1, 3)])
restrict_to_paths(path_set: Iterable[Sequence[Hashable]])[source]#

Return a new path complex restricted to a subset of paths.

Parameters:
path_setIterable[Sequence[Hashable]]

The paths to be used for the new restricted path complex.

Returns:
PathComplex

The new path complex restricted to the provided subset of paths.

Examples

>>> PC = PathComplex(
...     [[0, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3], [0, 1, 2], [0, 1, 3]]
... )
>>> PC = PC.restrict_to_paths([[1, 2, 3]])
>>> PC.paths
PathView([(1,), (2,), (3,), (1, 2), (2, 3), (1, 2, 3)])
set_edge_attributes(values: dict[Sequence[Hashable], Hashable | dict[Hashable, Any]], name: str | None = None) None[source]#

Set edge attributes for a path complex.

Parameters:
valuesdict[Sequence[Hashable], Hashable | dict[Hashable, Any]]

A dictionary of edge and its associated attribute values.

namestr

The name of the edge attribute.

Examples

>>> PC = PathComplex()
>>> PC.add_paths_from([[0, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3]])
>>> PC.add_path([0, 1], weight=32)
>>> PC.add_path([1, 2], weight=98)
>>> PC.add_path([1, 3], color="red")
>>> PC.add_path([2, 3], color="blue")
>>> PC.get_edge_attributes("weight")
{(0, 1): 32, (1, 2): 98}
>>> PC.set_edge_attributes({(0, 1): 33}, name="weight")
>>> PC.get_edge_attributes("weight")
{(0, 1): 33, (1, 2): 98}
>>> PC.set_edge_attributes(
...     {
...         (1, 3): {"weight": 55, "color": "yellow"},
...         (2, 3): {"weight": 66, "color": "blue"},
...     }
... )
>>> PC[1, 3]
{'color': 'yellow', 'weight': 55}
>>> PC[2, 3]
{'color': 'blue', 'weight': 66}
set_node_attributes(values: dict[Sequence[Hashable], Hashable | dict[Hashable, Any]], name: str | None = None) None[source]#

Set node attributes for a path complex.

Parameters:
valuesdict[Sequence[Hashable], Hashable | dict[Hashable, Any]]

A dictionary of node and its associated attribute values.

namestr

The name of the node attribute.

Examples

>>> PC = PathComplex()
>>> PC.add_paths_from([[0, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3]])
>>> PC.set_node_attributes(
...     {
...         (1,): {"heat": 55, "color": "red"},
...         (2,): {"heat": 66, "color": "blue"},
...     }
... )
>>> PC[1]
{'heat': 55, 'color': 'red'}
>>> PC[2]
{'heat': 66, 'color': 'blue'}
>>> PC.set_node_attributes({(1,): 58, (2,): 60}, name="heat")
>>> PC[1]
{'heat': 58, 'color': 'red'}
>>> PC[2]
{'heat': 60, 'color': 'blue'}
set_path_attributes(values: dict[Sequence[Hashable], Hashable | dict[Hashable, Any]], name: str | None = None) None[source]#

Set path attributes for a path complex.

Parameters:
valuesdict[Sequence[Hashable], Hashable | dict[Hashable, Any]]

A dictionary of path and its associated attribute values.

namestr

The name of the path attribute.

Examples

>>> PC = PathComplex(
...     [[0, 1], [0, 1, 2], [0, 1, 3], [1, 2, 3], [1, 3, 2], [2, 1, 3]]
... )
>>> PC.set_path_attributes(
...     {
...         (0, 1, 2): {"weight": 43, "color": "red"},
...         (0, 1, 3): {"weight": 98, "color": "blue"},
...     }
... )
>>> PC.get_path_attributes("weight")
{(0, 1, 2): 43, (0, 1, 3): 98}
>>> PC.get_path_attributes("color")
{(0, 1, 2): "red", (0, 1, 3): "blue"}
>>> PC.set_path_attributes({0: 55}, "weight")
>>> PC.get_path_attributes("weight")
{(0,): 55, (0, 1, 2): 43, (0, 1, 3): 98}
>>> PC.set_path_attributes({(0, 1): "red"}, "color")
>>> PC.get_path_attributes("color")
{(0, 1): 'red', (0, 1, 2): 'red', (0, 1, 3): 'blue'}
property shape: tuple[int, ...]#

Shape of path complex.

(number of elementary p-paths for each dimension d, for d in range(0,dim(Pc)))

Returns:
tuple[int, …]

Returns a tuple representing the shape of the path complex.

skeleton(rank: int) list[tuple[Hashable]][source]#

Compute skeleton.

Parameters:
rankint

The rank to be used to generate the skeleton of elementary p-paths.

Returns:
set[tuple[Hashable]]

Set of elementary p-paths of dimension specified by rank.

up_laplacian_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False) tuple[dict, lil_matrix] | lil_matrix[source]#

Compute up laplacian matrix of the path complex.

Parameters:
rankint

The dimension of the up laplacian matrix.

signedbool, default=True

If True, return signed up laplacian matrix. Else, return absolute up laplacian matrix.

weightstr, optional

If not given, all nonzero entries are 1.

indexbool, default=False

If True, return up laplacian matrix with indices. Else, return up laplacian matrix without indices.

Returns:
tuple[dict, sp.sparse.lil_matrix] | sp.sparse.lil_matrix

If index is True, return a tuple of (idx_p, up_laplacian_matrix) otherwise if index is False, return up_laplacian_matrix.

Path#

Path class.

class toponetx.classes.path.Path(elements: Sequence[Hashable] | Hashable, construct_boundaries: bool = False, reserve_sequence_order: bool = False, allowed_paths: Iterable[tuple[Hashable]] | None = None, **kwargs)[source]#

Path class.

A class representing an elementary p-path in a path complex, which is the building block for a path complex. By the definition established in the original paper [2], an elementary p-path on a non-empty set of vertices V is any sequence of vertices with length p + 1.

Unlike the original paper [2] where elementary p-paths span the regular space of boundary-invariant paths, the elementary p-paths represented by this class span the space of simple paths with length p as proposed in [1].

Parameters:
elementsSequence[Hashable]

The nodes in the elementary p-path.

construct_boundariesbool, default=False

If True, construct the entire boundary of the elementary p-path.

reserve_sequence_orderbool, default=False

If True, reserve the order of the sub-sequence of nodes in the elementary p-path. Else, the sub-sequence of nodes in the elementary p-path will be reversed if the first index is larger than the last index.

allowed_pathsIterable[tuple[Hashable]], optional

A list of allowed boundaries of an elementary p-path. If None, all possible boundaries are constructed (similarly to simplex).

**kwargskeyword arguments, optional

Additional attributes to be associated with the elementary p-path.

Notes

  • An elementary p-path is defined as an ordered sequence of nodes (n1, …, np) if we reserve the sequence order. Thus, for this case, (n1, …, np) is different

from (np, …, n1). If we do not reserve the sequence order, then (n1, …, np) is the same as (np, …, n1). For this case, the first index must be smaller than the last index so that we can discard the other duplicate elementary p-path. As elementary p-paths may contain characters and numbers at the same time, we leverage lexicographical order to compare two indices. - Similarly to simplex, in order to find the boundary of an elementary p-path, we remove one node at a time from the elementary p-path. However, unlike simplex where order does not matter, the process of omitting nodes from an elementary p-path may result some non-existing paths. For instance, if we have an elementary p-path (1, 2, 3) and there is no path (1, 3), then applying boundary operation on the elementary p-path results in [(2,3), (1,3), (1,2)]. In order to avoid this, we can provide a list of allowed boundaries. If the boundary of an elementary p-path is not in the list of allowed boundaries, then it will not be included in the boundary of the elementary p-path. - When an elementary p-path is created and allowed paths are not specified, its boundary is automatically created by iteratively removing one node at a time from the elementary p-path, which is identical to a simplex.

References

[1]

Truong and Chin. Generalizing Topological Graph Neural Networks with Paths. https://arxiv.org/abs/2308.06838.pdf

[2] (1,2)

Grigor’yan, Lin, Muranov, and Yau. Homologies of path complexes and digraphs. https://arxiv.org/pdf/1207.2834.pdf

Examples

>>> path1 = Path((1, 2, 3))
>>> list(path1.boundary)
[]
>>> path2 = Path((1, 2, 3), construct_boundaries=True)
>>> list(path2.boundary)
[(2, 3), (1, 3), (1, 2)]
>>> path3 = Path(
...     (1, 2, 3), construct_boundaries=True, allowed_paths=[(1, 2), (2, 3)]
... )
>>> list(path3.boundary)
[(2, 3), (1, 2)]
>>> path4 = Path(
...     (1, 2, 3), construct_boundaries=True, allowed_paths=[(1, 3), (2, 3)]
... )
>>> list(path4.boundary)
[(2, 3), (1, 3)]
property boundary: list[tuple[Hashable, ...]]#

Return the boundary of this path.

The boundary of this path are all elementary (p-1)-path objects representing the elementary (p-1)-path on the boundary of the target elementary p-path.

Returns:
list[tuple[Hashable]]

List of elementary p-path objects representing the boundary (p-1)-paths.

clone() Path[source]#

Return a shallow copy of the elementary p-path.

Returns:
Path

A shallow copy of the elementary p-path.

static construct_path_boundaries(elements: Sequence[Hashable], reserve_sequence_order: bool = False, allowed_paths: Iterable[tuple[Hashable]] | None = None) list[tuple[Hashable, ...]][source]#

Return list of elementary p-path objects representing the boundaries.

Parameters:
elementsSequence[Hashable]

The nodes in the elementary p-path.

reserve_sequence_orderbool, default=False

If True, reserve the order of the sub-sequence of nodes in the elementary p-path. Else, the sub-sequence of nodes in the elementary p-path will be reversed if the first index is larger than the last index.

allowed_pathsIterable[tuple[Hashable]], optional

A list of allowed boundaries. If None, all possible boundaries are constructed (similarly to simplex).

Returns:
list[tuple[Hashable]]

List of elementary p-path objects representing the boundaries.

Notes

  • In order to construct a set of boundary (p-1)-paths of an elementary p-path (n1, …, np), we iteratively omit one node at a time from

the elementary p-path. The resulted boundary paths are elementary (p-1)-paths in the form of (n1, …, ni-1, ni+1, …, np), where i is the index of the omitted node. However, some of the elementary (p-1)-paths may be non-existing (or non-allowed in our context), so we discard them from the boundary set.

Examples

>>> Path.construct_path_boundaries((1, 2, 3), reserve_sequence_order=False)
[(2, 3), (1, 3), (1, 2)]
>>> Path.construct_path_boundaries(
...     (1, 2, 3), reserve_sequence_order=False, allowed_paths=[(1, 2), (2, 3)]
... )
[(2, 3), (1, 2)]

ReportViews#

Module with views.

Such as: HyperEdgeView, CellView, SimplexView, NodeView.

class toponetx.classes.reportviews.CellView[source]#

A CellView class for cells of a CellComplex.

raw(cell: tuple | list | Cell) Cell | list[Cell][source]#

Index the raw cell objects analogous to the overall index of CellView.

Parameters:
celltuple, list, or cell

The cell of interest.

Returns:
Cell or list of Cells

The raw Cell objects. If more than one cell with the same boundary exists, returns a list; otherwise a single cell.

Raises:
KeyError

If the cell is not in the cell dictionary.

class toponetx.classes.reportviews.ColoredHyperEdgeView[source]#

A class for viewing the cells/hyperedges of a colored hypergraph.

Provides methods for accessing, and retrieving information about the cells/hyperedges of a complex.

Examples

>>> hev = ColoredHyperEdgeView()
property allranks: list[int]#

All ranks.

Returns:
list[int]

The sorted list of all ranks.

get_rank(edge)[source]#

Get the rank of a given hyperedge.

Parameters:
edgeIterable, Hashable or ColoredHyperEdge

The edge for which to get the rank.

Returns:
int

The rank of the given colored hyperedge.

property shape: tuple[int, ...]#

Compute shape.

Returns:
tuple[int, …]

The shape of the ColoredHyperEdge.

skeleton(rank: int, store_hyperedge_key: bool = True)[source]#

Skeleton of the complex.

Parameters:
rankint

Rank of the skeleton.

store_hyperedge_keybool, default=True

Whether to return the hyperedge key or not.

Returns:
list of frozensets

The skeleton of rank rank.

class toponetx.classes.reportviews.HyperEdgeView[source]#

A class for viewing the cells/hyperedges of a combinatorial complex.

Provides methods for accessing, and retrieving information about the cells/hyperedges of a complex.

Examples

>>> hev = HyperEdgeView()
property allranks#

All ranks.

Returns:
list[hashable]

The sorted list of all ranks.

get_rank(edge)[source]#

Get the rank of a hyperedge.

Parameters:
edgeIterable, Hashable or HyperEdge

The edge for which to get the rank.

Returns:
int

The rank of the given hyperedge.

property shape: tuple[int, ...]#

Compute shape.

Returns:
tuple[int, …]

A tuple representing the shape of the hyperedge.

skeleton(rank: int, level: Literal['equal', 'upper', 'up', 'lower', 'down', 'uppereq', 'upeq', 'lowereq', 'downeq'] = 'equal')[source]#

Skeleton of the complex.

Parameters:
rankint

Rank of the skeleton.

levelstr, default=”equal”

Level of the skeleton.

Returns:
list of frozensets

The skeleton of rank rank.

class toponetx.classes.reportviews.NodeView(objectdict, cell_type, colored_nodes: bool = False)[source]#

Node view class.

Parameters:
objectdictdict

A dictionary of nodes with their attributes.

cell_typetype

The type of the cell.

colored_nodesbool, optional

Whether or not the nodes are colored.

class toponetx.classes.reportviews.SimplexView[source]#

Simplex View class.

The SimplexView class is used to provide a view/read only information into a subset of the nodes in a simplex. These classes are used in conjunction with the SimplicialComplex class for view/read only purposes for simplices in simplicial complexes.

Attributes:
max_dimint

Maximum dimension of the simplices in the SimplexView instance.

faces_dictlist of dict

A list containing dictionaries of faces for each dimension.

property shape: tuple[int, ...]#

Return the number of simplices in each dimension.

Returns:
tuple of ints

A tuple of integers representing the number of simplices in each dimension.

Simplex#

Simplex Class.

class toponetx.classes.simplex.Simplex(elements: Collection, construct_tree: bool = False, **kwargs)[source]#

A class representing a simplex in a simplicial complex.

This class represents a simplex in a simplicial complex, which is a set of nodes with a specific dimension. The simplex is immutable, and the nodes in the simplex must be hashable and unique.

Parameters:
elementsCollection

The nodes in the simplex.

construct_treebool, default=True

If True, construct the entire simplicial tree for the simplex.

**kwargskeyword arguments, optional

Additional attributes to be associated with the simplex.

Examples

>>> # Create a 0-dimensional simplex (point)
>>> s = Simplex((1,))
>>> # Create a 1-dimensional simplex (line segment)
>>> s = Simplex((1, 2))
>>> # Create a 2-dimensional simplex (triangle)
>>> simplex1 = Simplex((1, 2, 3))
>>> simplex2 = Simplex(("a", "b", "c"))
>>> # Create a 3-dimensional simplex (tetrahedron)
>>> simplex3 = Simplex((1, 2, 4, 5), weight=1)
property boundary: frozenset[Simplex]#

Return the set of the set of all n-1 faces in of the input n-simplex.

Returns:
frozenset[Simplex]

A frozenset representing boundary simplices.

Examples

For a n-simplex [1,2,3], the boundary is all the n-1 subsets of [1,2,3] :

(1,2), (2,3), (3,1).

clone() Simplex[source]#

Return a copy of the simplex.

The clone method by default returns an independent shallow copy of the simplex and attributes. That is, if an attribute is a container, that container is shared by the original and the copy. Use Python’s copy.deepcopy for new containers.

Returns:
Simplex

A copy of this simplex.

static construct_simplex_tree(elements: Collection) frozenset[Simplex][source]#

Return set of Simplex objects representing the faces.

Parameters:
elementsCollection

The simplex for which to construct the simplex tree.

Returns:
frozenset[Simplex]

The set of faces of the simplex.

property faces#

Get the set of faces of the simplex.

If construct_tree is True, return the precomputed set of faces _faces. Otherwise, construct the simplex tree and return the set of faces.

Returns:
frozenset[Simplex]

The set of faces of the simplex.

sign(face) int[source]#

Calculate the sign of the simplex with respect to a given face.

Parameters:
faceSimplex

A face of the simplex.

Simplicial Complex#

Class for creation and manipulation of simplicial complexes.

The class also supports attaching arbitrary attributes and data to cells.

class toponetx.classes.simplicial_complex.SimplicialComplex(simplices=None, **kwargs)[source]#

Class representing a simplicial complex.

Class for construction boundary operators, Hodge Laplacians, higher order (co)adjacency operators from a collection of simplices.

A simplicial complex is a topological space of a specific kind, constructed by “gluing together” points, line segments, triangles, and their higher-dimensional counterparts. It is a generalization of the notion of a triangle in a triangulated surface, or a tetrahedron in a tetrahedralized 3-dimensional manifold. Simplicial complexes are the basic objects of study in combinatorial topology.

For example, a triangle is a simplicial complex because it is a collection of three points that are connected to each other in a specific way. Similarly, a tetrahedron is a simplicial complex because it is a collection of four points that are connected to each other in a specific way. These simplices can be thought of as the “building blocks” of a simplicial complex, and the complex itself is constructed by combining these building blocks in a specific way. For example, a 2-dimensional simplicial complex could be a collection of triangles that are connected to each other to form a surface, while a 3-dimensional simplicial complex could be a collection of tetrahedra that are connected to each other to form a solid object.

The SimplicialComplex class is a class for representing simplicial complexes, which are a type of topological space constructed by “gluing together” points, line segments, triangles, and higher-dimensional counterparts. The class provides methods for computing boundary operators, Hodge Laplacians, and higher-order adjacency operators on the simplicial complex. It also allows for compatibility with NetworkX and the GUDHI library.

Features:

  1. The SimplicialComplex class allows for the dynamic construction of simplicial complexes,

    enabling users to add or remove simplices from the complex after its initial creation.

  2. The class provides methods for computing boundary operators, Hodge Laplacians,

    and higher-order adjacency operators on the simplicial complex.

  3. The class is compatible with the gudhi library, allowing users to leverage the powerful

    algorithms and data structures provided by this package.

  4. The class supports the attachment of arbitrary attributes and data to simplices,

    enabling users to store and manipulate additional information about these objects.

  5. The class has robust error handling and input validation, ensuring reliable and easy use of the class.

Parameters:
simplicesiterable, optional

Iterable of maximal simplices that define the simplicial complex.

**kwargskeyword arguments, optional

Attributes to add to the complex as key=value pairs.

Notes

A simplicial complex is determined by its maximal simplices, simplices that are not contained in any other simplices. If a maximal simplex is inserted, all faces of this simplex will be inserted automatically.

Examples

Define a simplicial complex using a set of maximal simplices:

>>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]])

TopoNetX is also compatible with NetworkX, allowing users to create a simplicial complex from a NetworkX graph. Existing node and edge attributes are copied to the simplicial complex:

>>> G = nx.Graph()
>>> G.add_edge(0, 1, weight=4)
>>> G.add_edges_from([(0, 3), (0, 4), (1, 4)])
>>> SC = SimplicialComplex(simplices=G)
>>> SC.add_simplex([1, 2, 3])
>>> SC.simplices
SimplexView([(0,), (1,), (3,), (4,), (2,), (0, 1), (0, 3), (0, 4), (1, 4), (1, 2), (1, 3), (2, 3), (1, 2, 3)])
>>> SC[(0, 1)]["weight"]
4
Attributes:
complexdict

A dictionary that can be used to store additional information about the complex.

add_elements_from_nx_graph(G: Graph) None[source]#

Add elements from a networkx graph to this simplicial complex.

Parameters:
Gnetworkx.Graph

A networkx graph instance.

add_node(node: Hashable, **kwargs) None[source]#

Add node to simplicial complex.

Parameters:
nodeHashable or Simplex

The node to be added to the simplicial complex.

**kwargskeyword arguments, optional

Additional attributes to be associated with the node.

add_simplex(simplex: Collection, **kwargs) None[source]#

Add simplex to simplicial complex.

Parameters:
simplexCollection

The simplex to be added to the simplicial complex.

**kwargskeyword arguments, optional

Additional attributes to be associated with the simplex.

add_simplices_from(simplices) None[source]#

Add simplices from iterable to simplicial complex.

Parameters:
simplicesiterable

Iterable of simplices to be added to the simplicial complex.

adjacency_matrix(rank, signed: bool = False, weight: str | None = None, index: bool = False)[source]#

Compute the adjacency matrix of the simplicial complex.

Parameters:
rankint

Rank of the adjacency matrix.

signedbool

Whether to return the signed or unsigned adjacency matrix.

weightstr, optional

The name of the simplex attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the adjacency matrix.

Returns:
indicesdict

Dictionary mapping each row and column of the coadjacency matrix to a simplex. Only returned if index is True.

adjacency_matrixscipy.sparse.csr.csr_matrix

The adjacency matrix of rank rank of this simplicial complex.

Examples

>>> SC = SimplicialComplex()
>>> SC.add_simplex([1, 2, 3, 4])
>>> SC.add_simplex([1, 2, 4])
>>> SC.add_simplex([3, 4, 8])
>>> adj1 = SC.adjacency_matrix(1)
clone() SimplicialComplex[source]#

Return a copy of the simplicial complex.

The clone method by default returns an independent shallow copy of the simplicial complex. Use Python’s copy.deepcopy for new containers.

Returns:
SimplicialComplex

A shallow copy of this simplicial complex.

coadjacency_matrix(rank: int, signed: bool = False, weight=None, index: bool = False)[source]#

Compute the coadjacency matrix of the simplicial complex.

Parameters:
rankint

Rank of the coadjacency matrix.

signedbool

Whether to return the signed or unsigned coadjacency matrix.

weightstr, optional

The name of the simplex attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the coadjacency matrix.

Returns:
indiceslist

List identifying the rows and columns of the coadjacency matrix with the simplices of the simplicial complex. Only returned if index is True.

coadjacency_matrixscipy.sparse.csr.csr_matrix

The coadjacency matrix of this simplicial complex.

coincidence_matrix(rank, signed: bool = True, weight=None, index: bool = False)[source]#

Compute coincidence matrix of the simplicial complex.

This is also called the coboundary matrix.

Parameters:
rankint

For which rank of simplices to compute the coincidence matrix.

signedbool, default=True

Whether to return the signed or unsigned coincidence matrix.

weightstr, optional

The name of the simplex attribute to use as weights for the coincidence matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the coincidence matrix.

Returns:
row_indices, col_indicesdict

Dictionary assigning each row and column of the coincidence matrix to a simplex. Only returned if index is True.

coincidence_matrixscipy.sparse.csr.csr_matrix

The coincidence matrix.

property dim: int#

Dimension of the simplicial complex.

This is the highest dimension of any simplex in the complex.

Returns:
int

The dimension of the simplicial complex.

dirac_operator_matrix(signed: bool = True, weight: str | None = None, index: bool = False)[source]#

Compute dirac operator matrix matrix.

Parameters:
signedbool, default=False

Whether the returned dirac matrix should be signed (i.e., respect orientations) or unsigned.

weightstr, optional

The name of the simplex attribute to use as weights for the dirac operator matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the dirac operator matrix.

Returns:
row_indices, col_indicesdict

List identifying rows and columns of the dirac operator matrix. Only returned if index is True.

scipy.sparse.csr.csc_matrix

The dirac operator matrix of this simplicial complex.

Examples

>>> from toponetx.classes import SimplicialComplex
>>> SC = SimplicialComplex()
>>> SC.add_simplex([1, 2, 3, 4])
>>> SC.add_simplex([1, 2, 4])
>>> SC.add_simplex([3, 4, 8])
>>> SC.dirac_operator_matrix()
down_laplacian_matrix(rank, signed: bool = True, weight=None, index: bool = False)[source]#

Compute the down Laplacian matrix of the simplicial complex.

Parameters:
rankint

Rank of the down Laplacian matrix.

signedbool

Whether to return the signed or unsigned down laplacian.

weightstr, optional

The name of the simplex attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the laplacian.

Returns:
indicesdict

Dictionary assigning each row and column of the laplacian matrix to a simplex. Only returned if index is True.

down_laplacianscipy.sparse.csr.csr_matrix

The down laplacian matrix of this simplicial complex.

classmethod from_gudhi(tree: SimplexTree) SimplicialComplex[source]#

Import from gudhi.

Parameters:
treegudhi.SimplexTree

The input gudhi simplex tree.

Returns:
SimplicialComplex

The resulting SimplicialComplex.

Examples

>>> from gudhi import SimplexTree
>>> tree = SimplexTree()
>>> _ = tree.insert([1, 2, 3, 5])
>>> SC = SimplicialComplex.from_gudhi(tree)
classmethod from_nx(G: Graph) SimplicialComplex[source]#

Convert from netwrokx graph.

Parameters:
Gnx.Graph

The graph to convert to a simplicial complex.

Returns:
SimplicialComplex

The simplicial complex.

Examples

>>> G = nx.Graph()  # or DiGraph, MultiGraph, MultiDiGraph, etc
>>> G.add_edge(1, 2, weight=2)
>>> G.add_edge(3, 4, weight=4)
>>> SC = SimplicialComplex.from_nx(G)
>>> SC[(1, 2)]["weight"]
2
classmethod from_spharpy(mesh) SimplicialComplex[source]#

Import from sharpy.

Parameters:
meshspharapy.trimesh.TriMesh

The input spharapy object.

Returns:
SimplicialComplex

The resulting SimplicialComplex.

Examples

>>> import spharapy.trimesh as tm
>>> import spharapy.spharabasis as sb
>>> import spharapy.datasets as sd
>>> mesh = tm.TriMesh(
...     [[0, 1, 2]], [[1.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 3.0]]
... )
>>> SC = SimplicialComplex.from_spharpy(mesh)
classmethod from_trimesh(mesh) SimplicialComplex[source]#

Import from trimesh.

Parameters:
meshtrimesh.Trimesh

The input trimesh object.

Returns:
SimplicialComplex

The resulting SimplicialComplex.

Examples

>>> import trimesh
>>> mesh = trimesh.Trimesh(
...     vertices=[[0, 0, 0], [0, 0, 1], [0, 1, 0]],
...     faces=[[0, 1, 2]],
...     process=False,
... )
>>> SC = SimplicialComplex.from_trimesh(mesh)
>>> print(SC.nodes)
>>> print(SC.simplices)
>>> SC[(0)]["position"]
get_all_maximal_simplices()[source]#

Get all maximal simplices of this simplicial complex.

A simplex is maximal if it is not a face of any other simplex in the complex.

Returns:
list of tuples

List of maximal simplices in this simplicial complex.

Examples

>>> SC = SimplicialComplex([(1, 2), (1, 2, 3), (1, 2, 4), (2, 5)])
>>> SC.get_all_maximal_simplices()
[(2, 5), (1, 2, 3), (1, 2, 4)]
static get_boundaries(simplices: Iterable, min_dim=None, max_dim=None) set[frozenset][source]#

Get boundaries of the given simplices.

Parameters:
simplicesIterable

Iterable of simplices for which to compute the boundaries.

min_dimint, optional

Constrain the max dimension of faces.

max_dimint, optional

Constrain the max dimension of faces.

Returns:
set of frozensets

Set of simplices that are boundaries of the given simplices. If min_dim or max_dim are given, only simplices with dimension in the given range are returned.

get_cofaces(simplex: Iterable[Hashable], codimension: int) list[frozenset][source]#

Get cofaces of simplex.

Parameters:
simplexlist, tuple or simplex

The simplex to get the cofaces of.

codimensionint

The codimension. If codimension = 0, all cofaces are returned.

Returns:
list of tuples

The cofaces of the given simplex.

static get_edges_from_matrix(matrix)[source]#

Get edges from matrix.

Parameters:
matrixnumpy or scipy array

The matrix to get the edges from.

Returns:
list of int

List of indices where the operator is not zero.

Notes

Most operaters (e.g. adjacencies/(co)boundary maps) that describe connectivity of the simplicial complex can be described as a G whose nodes are the simplices used to construct the operator and whose edges correspond to the entries in the matrix where the operator is not zero.

This property implies that many computations on simplicial complexes can be reduced to G computations.

get_maximal_simplices_of_simplex(simplex: Iterable[Hashable]) set[frozenset][source]#

Get maximal simplices of simplex.

Parameters:
simplexIterable

The simplex for which to compute the maximal simplices.

Returns:
set of frozensets

Set of maximal simplices of the given simplex.

get_node_attributes(name: str) dict[Hashable, Any][source]#

Get node attributes from combinatorial complex.

Parameters:
namestr

Attribute name.

Returns:
dict

Dictionary mapping each node to the value of the given attribute name.

Examples

>>> SC = SimplicialComplex()
>>> SC.add_simplex([1, 2, 3, 4])
>>> SC.add_simplex([1, 2, 4])
>>> SC.add_simplex([3, 4, 8])
>>> SC.set_simplex_attributes({1: "red", 2: "blue", 3: "black"}, name="color")
>>> SC.get_node_attributes("color")
{1: 'red', 2: 'blue', 3: 'black'}
get_simplex_attributes(name: str, rank: int | None = None) dict[tuple[Hashable, ...], Any][source]#

Get node attributes from simplical complex.

Parameters:
namestr

Attribute name.

rankint, optional

Restrict the returned attribute values to simplices of a specific rank.

Returns:
dict[tuple[Hashable, …], Any]

Dictionary mapping each simplex to the value of the given attribute name.

Examples

>>> SC = SimplicialComplex()
>>> SC.add_simplex([1, 2, 3, 4])
>>> SC.add_simplex([1, 2, 4])
>>> SC.add_simplex([3, 4, 8])
>>> d = {(1, 2): "red", (2, 3): "blue", (3, 4): "black"}
>>> SC.set_simplex_attributes(d, name="color")
>>> SC.get_simplex_attributes("color")
{frozenset({1, 2}): 'red', frozenset({2, 3}): 'blue', frozenset({3, 4}): 'black'}
get_star(simplex) list[frozenset][source]#

Get star.

Parameters:
simplexlist, tuple or simplex

The simplex represented by a list of its nodes.

Returns:
list[frozenset]

The star of the given simplex.

Notes

This function is equivalent to get_cofaces(simplex, 0).

hodge_laplacian_matrix(rank: int, signed: bool = True, weight=None, index: bool = False)[source]#

Compute hodge-laplacian matrix for the simplicial complex.

Parameters:
rankint

Dimension of the Laplacian matrix.

signedbool, default=True

Whether to return the signed or unsigned hodge laplacian.

weightstr, optional

The name of the simplex attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Indicates whether to return the indices that define the hodge laplacian matrix.

Returns:
indexlist

List assigning each row and column of the laplacian matrix to a simplex. Only available when index is True.

laplacianscipy.sparse.csr.csr_matrix

The hodge laplacian matrix of rank rank.

Examples

>>> SC = SimplicialComplex()
>>> SC.add_simplex([1, 2, 3, 4])
>>> SC.add_simplex([1, 2, 4])
>>> SC.add_simplex([3, 4, 8])
>>> L1 = SC.hodge_laplacian_matrix(1)
incidence_matrix(rank, signed: bool = True, weight: str | None = None, index: bool = False) csr_matrix | tuple[dict, dict, csr_matrix][source]#

Compute incidence matrix of the simplicial complex.

Getting the matrix that correpodnds to the boundary matrix of the input SC.

Parameters:
rankint

For which rank of simplices to compute the incidence matrix.

signedbool, default=True

Whether to return the signed or unsigned incidence matrix.

weightstr, optional

The name of the simplex attribute to use as weights for the incidence matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the incidence matrix.

Returns:
row_indices, col_indicesdict

Dictionary assigning each row and column of the incidence matrix to a simplex. Only returned if index is True.

incidence_matrixscipy.sparse.csr.csr_matrix

The incidence matrix.

Raises:
ValueError

If rank is negative or larger than the dimension of the simplicial complex.

Examples

>>> SC = SimplicialComplex()
>>> SC.add_simplex([1, 2, 3, 4])
>>> SC.add_simplex([1, 2, 4])
>>> SC.add_simplex([3, 4, 8])
>>> B1 = SC.incidence_matrix(1)
>>> B2 = SC.incidence_matrix(2)
is_connected() bool[source]#

Check if the simplicial complex is connected.

Returns:
bool

True if the simplicial complex is connected, False otherwise.

Notes

A simplicial complex is connected iff its 1-skeleton is connected.

is_maximal(simplex: Iterable) bool[source]#

Check if simplex is maximal.

Parameters:
simplexIterable

The Simplex to check.

Returns:
bool

True if the given simplex is maximal in this simplicial complex, False otherwise.

Raises:
ValueError

If simplex is not in the simplicial complex.

Examples

>>> SC = SimplicialComplex([[1, 2, 3]])
>>> SC.is_maximal([1, 2, 3])
True
>>> SC.is_maximal([1, 2])
False
is_triangular_mesh() bool[source]#

Check if the simplicial complex is a triangular mesh.

Returns:
bool

True if the simplicial complex can be converted to a triangular mesh, False otherwise.

laplace_beltrami_operator(mode: str = 'inv_euclidean')[source]#

Compute a laplacian matrix for a triangular mesh.

The method creates a laplacian matrix for a triangular mesh using different weighting function.

Parameters:
mode{‘unit’, ‘inv_euclidean’, ‘half_cotangent’}, optional

The methods for determining the edge weights. Using the option ‘unit’ all edges of the mesh are weighted by unit weighting function, the result is an adjacency matrix. The option ‘inv_euclidean’ results in edge weights corresponding to the inverse Euclidean distance of the edge lengths. The option ‘half_cotangent’ uses the half of the cotangent of the two angles opposed to an edge as weighting function. the default weighting function is ‘inv_euclidean’.

Returns:
numpy.ndarray, shape (n_vertices, n_vertices)

Matrix, which contains the discrete laplace operator for data defined at the vertices of a triangular mesh. The number of vertices of the triangular mesh is n_points.

classmethod load_mesh(file_path, process: bool = False) SimplicialComplex[source]#

Load a mesh.

Parameters:
file_pathstr or pathlib.Path

The source of the data to be loaded.

processbool

Whether trimesh should try to process the mesh before loading it.

Returns:
SimplicialComplex

The output simplicial complex stores the same structure stored in the mesh input file.

Notes

mesh files supported : obj, off, glb

Examples

>>> SC = SimplicialComplex.load_mesh("C:/temp/stanford-bunny.obj")
>>> SC.nodes
property maxdim: int#

Maximum dimension of the simplicial complex.

This is the highest dimension of any simplex in the complex.

Returns:
int

The maximum dimension of the simplicial complex.

property nodes#

Return the list of nodes in the simplicial complex.

Returns:
NodeView

A NodeView object representing the nodes of the simplicial complex.

normalized_laplacian_matrix(rank: int, weight: str | None = None)[source]#

Return the normalized hodge Laplacian matrix of simplicial complex .

The normalized hodge Laplacian is the matrix

\[N_d = D^{-1/2} L_d D^{-1/2}\]

where L is the simplicial complex Laplacian and D is the diagonal matrix of simplices of rank d.

Parameters:
rankint

Rank of the hodge laplacian matrix.

weightstr, optional

The name of the simplex attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

Returns:
Scipy sparse matrix

The normalized hodge Laplacian matrix.

Examples

>>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]])
>>> SC.normalized_laplacian_matrix(1)
remove_maximal_simplex(simplex: Collection) None[source]#

Remove maximal simplex from simplicial complex.

Parameters:
simplexCollection

The simplex to be removed from the simplicial complex.

Raises:
KeyError

If simplex is not in simplicial complex.

ValueError

If simplex is not maximal.

Examples

>>> SC = SimplicialComplex()
>>> SC.add_simplex((1, 2, 3, 4), weight=1)
>>> SC.add_simplex((1, 2, 3, 4, 5))
>>> SC.remove_maximal_simplex((1, 2, 3, 4, 5))
remove_nodes(node_set: Iterable[Hashable]) None[source]#

Remove the given nodes from the simplicial complex.

Any simplices that become invalid due to the removal of nodes are also removed.

Parameters:
node_setIterable

The nodes to be removed from the simplicial complex.

Examples

>>> SC = SimplicialComplex([(1, 2), (2, 3), (3, 4)])
>>> SC.remove_nodes([1])
>>> SC.simplices
SimplexView([(2,), (3,), (4,), (2, 3), (3, 4)])
restrict_to_nodes(node_set)[source]#

Construct a new simplicial complex by restricting the simplices.

The simplices are restricted to the nodes referenced by node_set.

Parameters:
node_setiterable of hashables

A subset of nodes of the simplicial complex to restrict to.

Returns:
SimplicialComplex

A new simplicial complex restricted to the nodes in node_set.

Examples

>>> c1 = Simplex((1, 2, 3))
>>> c2 = Simplex((1, 2, 4))
>>> c3 = Simplex((1, 2, 5))
>>> SC = SimplicialComplex([c1, c2, c3])
>>> new_complex = SC.restrict_to_nodes([1, 2, 3, 4])
>>> new_complex.simplices
SimplexView([(1,), (2,), (3,), (4,), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (1, 2, 3), (1, 2, 4)])
restrict_to_simplices(cell_set) SimplicialComplex[source]#

Construct a simplicial complex using a subset of the simplices.

Parameters:
cell_setiterable of hashables or simplices

A subset of elements of the simplicial complex that should be in the new simplicial complex.

Returns:
SimplicialComplex

New simplicial complex restricted to the simplices in cell_set.

Examples

>>> c1 = Simplex((1, 2, 3))
>>> c2 = Simplex((1, 2, 4))
>>> c3 = Simplex((1, 2, 5))
>>> SC = SimplicialComplex([c1, c2, c3])
>>> SC1 = SC.restrict_to_simplices([c1, (2, 4)])
>>> SC1.simplices
SimplexView([(1,), (2,), (3,), (4,), (1, 2), (1, 3), (2, 3), (2, 4), (1, 2, 3)])
set_simplex_attributes(values, name: str | None = None) None[source]#

Set simplex attributes.

Parameters:
valuesdict

Either provide a mapping from simplices to values or a dict of dicts. In the former case, the attribute name for each simplex is set to the corresponding value. In the latter case, the entire dictionary is used to update the attributes of the simplices.

namestr, optional

The name of the attribute to set.

Notes

If the dict contains simplices that are not in self.simplices, they are silently ignored.

Examples

After computing some property of the simplex of a simplicial complex, you may want to assign a simplex attribute to store the value of that property for each simplex:

>>> SC = SimplicialComplex()
>>> SC.add_simplex([1, 2, 3, 4])
>>> SC.add_simplex([1, 2, 4])
>>> SC.add_simplex([3, 4, 8])
>>> d = {(1, 2, 3): "red", (1, 2, 4): "blue"}
>>> SC.set_simplex_attributes(d, name="color")
>>> SC[(1, 2, 3)]["color"]
'red'

If you provide a dictionary of dictionaries as the second argument, the entire dictionary will be used to update simplex attributes:

>>> SC = SimplicialComplex()
>>> SC.add_simplex([1, 3, 4])
>>> SC.add_simplex([1, 2, 3])
>>> SC.add_simplex([1, 2, 4])
>>> d = {
...     (1, 3, 4): {"color": "red", "attr2": 1},
...     (1, 2, 4): {"color": "blue", "attr2": 3},
... }
>>> SC.set_simplex_attributes(d)
>>> SC[(1, 3, 4)]["color"]
'red'
property shape: tuple[int, ...]#

Shape of simplicial complex.

(number of simplices[i], for i in range(0,dim(Sc)) )

Returns:
tuple of ints

This gives the number of cells in each rank.

property simplices: SimplexView#

Set of all simplices in the simplicial complex.

Returns:
SimplexView

A SimplexView object representing the set of all simplices in the simplicial complex.

classmethod simplicial_closure_of_hypergraph(H) SimplicialComplex[source]#

Compute the simplicial complex closure of a hypergraph.

Parameters:
Hhyernetx hypergraph

The hypergraph to compute the simplicial complex closure of.

Returns:
SimplicialComplex

Simplicial complex closure of the hypergraph.

Examples

>>> import hypernetx as hnx
>>> hg = hnx.Hypergraph([[1, 2, 3, 4], [1, 2, 3]], static=True)
>>> sc = SimplicialComplex.simplicial_closure_of_hypergraph(hg)
>>> sc.simplices
SimplexView([(1,), (2,), (3,), (4,), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4), (1, 2, 3, 4)])
skeleton(rank: int) list[tuple[Hashable, ...]][source]#

Compute skeleton.

Parameters:
rankint

The rank of the skeleton to compute.

Returns:
list of tuples

Simplices of rank rank in the simplicial complex.

to_cell_complex()[source]#

Convert a simplicial complex to a cell complex.

Returns:
toponetx.classes.CellComplex

The cell complex corresponding to this simplicial complex.

Examples

>>> c1 = Simplex((1, 2, 3))
>>> c2 = Simplex((1, 2, 4))
>>> c3 = Simplex((2, 5))
>>> SC = SimplicialComplex([c1, c2, c3])
>>> SC.to_cell_complex()
to_combinatorial_complex()[source]#

Convert a simplicial complex to a combinatorial complex.

Returns:
toponetx.classes.CombinatorialComplex

The combinatorial complex equivalent to this simplicial complex.

Examples

>>> c1 = Simplex((1, 2, 3))
>>> c2 = Simplex((1, 2, 3))
>>> c3 = Simplex((1, 2, 4))
>>> SC = SimplicialComplex([c1, c2, c3])
>>> CCC = SC.to_combinatorial_complex()
to_hasse_graph() DiGraph[source]#

Create the hasse graph corresponding to this simplicial complex.

Returns:
nx.DiGraph

A NetworkX Digraph representing the Hasse graph of the Simplicial Complex.

Examples

>>> SC = SimplicialComplex()
>>> SC.add_simplex([0, 1, 2])
>>> G = SC.to_hasse_graph()
to_hypergraph() None[source]#

Convert a simplicial complex to a hypergraph.

Returns:
Hypergraph

The hypergraph corresponding to this simplicial complex.

Examples

>>> c1 = Simplex((1, 2, 3))
>>> c2 = Simplex((1, 2, 4))
>>> c3 = Simplex((2, 5))
>>> SC = SimplicialComplex([c1, c2, c3])
>>> SC.to_hypergraph()
Hypergraph({'e0': [1, 2], 'e1': [1, 3], 'e2': [1, 4], 'e3': [2, 3], 'e4': [2, 4], 'e5': [2, 5], 'e6': [1, 2, 3], 'e7': [1, 2, 4]},name=)
to_spharapy(vertex_position_name: str = 'position')[source]#

Convert to sharapy.

Parameters:
vertex_position_namestr, default=”position”

The simplex attribute name that contains the vertex positions.

Returns:
spharapy.trimesh.TriMesh

The spharapy object corresponding to this simplicial complex.

Examples

>>> import spharapy.trimesh as tm
>>> import spharapy.spharabasis as sb
>>> import spharapy.datasets as sd
>>> mesh = tm.TriMesh([[0, 1, 2]], [[0, 0, 0], [0, 0, 1], [0, 1, 0]])
>>> SC = SimplicialComplex.from_spharpy(mesh)
>>> mesh2 = SC.to_spharapy()
>>> mesh2.vertlist == mesh.vertlist
>>> mesh2.trilist == mesh.trilist
to_trimesh(vertex_position_name: str = 'position')[source]#

Convert simplicial complex to trimesh object.

Parameters:
vertex_position_namestr, default=”position”

The simplex attribute name that contains the vertex positions.

Returns:
trimesh.Trimesh

The trimesh object corresponding to this simplicial complex.

up_laplacian_matrix(rank: int, signed: bool = True, weight: str | None = None, index: bool = False)[source]#

Compute the up Laplacian matrix of the simplicial complex.

Parameters:
rankint

Rank of the up Laplacian matrix.

signedbool

Whether to return the signed or unsigned up laplacian.

weightstr, optional

The name of the simplex attribute to use as weights for the hodge laplacian matrix. If None, the matrix is binary.

indexbool, default=False

Whether to return the indices of the rows and columns of the laplacian.

Returns:
row_indices, col_indiceslist

List identifying the rows and columns of the laplacian matrix with the simplices of this complex. Only returned if index is True.

up_laplacianscipy.sparse.csr.csr_matrix

The upper laplacian matrix of this simplicial complex.

Examples

>>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]])
>>> SC.up_laplacian_matrix(1)