From c4bb62600169a77c82c620262cf53caa2485773c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Ballester?= Date: Fri, 16 Feb 2024 18:23:57 +0100 Subject: [PATCH 01/53] Fixing error on cell_complex down_laplacian_documentation. --- toponetx/classes/cell_complex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toponetx/classes/cell_complex.py b/toponetx/classes/cell_complex.py index 7c2a8d7e..6176e7e4 100644 --- a/toponetx/classes/cell_complex.py +++ b/toponetx/classes/cell_complex.py @@ -1877,7 +1877,7 @@ def down_laplacian_matrix( Parameters ---------- - rank : {0, 1} + rank : {1, 2} Rank of the down Laplacian matrix. signed : bool Whether to return the signed or unsigned down laplacian. From 3eb2e45402659c02c02e59b421723bfc224c039b Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Wed, 6 Mar 2024 12:06:07 +0100 Subject: [PATCH 02/53] Do not document `__init__` methods The function is already documented as part of the class docstring. This is also common practice in other libraries. --- docs/conf.py | 1 - pyproject.toml | 3 + toponetx/classes/cell.py | 17 ---- toponetx/classes/cell_complex.py | 11 --- toponetx/classes/colored_hypergraph.py | 106 ++++++++++------------ toponetx/classes/combinatorial_complex.py | 42 ++------- toponetx/classes/complex.py | 9 -- toponetx/classes/hyperedge.py | 22 ----- toponetx/classes/path.py | 65 ------------- toponetx/classes/path_complex.py | 63 ------------- toponetx/classes/reportviews.py | 38 -------- toponetx/classes/simplex.py | 26 ------ 12 files changed, 59 insertions(+), 344 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index b0e2a26e..78d0db1b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -113,6 +113,5 @@ } # configure numpydoc -numpydoc_validation_checks = {"all", "GL01", "ES01", "EX01", "SA01"} numpydoc_show_class_members = False numpydoc_class_members_toctree = False diff --git a/pyproject.toml b/pyproject.toml index 02b09ca5..f6f9d858 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -137,3 +137,6 @@ checks = [ "EX01", "SA01" ] +exclude = [ + '\.__init__$', +] diff --git a/toponetx/classes/cell.py b/toponetx/classes/cell.py index 58a6d05b..e8c7a9f1 100644 --- a/toponetx/classes/cell.py +++ b/toponetx/classes/cell.py @@ -57,23 +57,6 @@ class Cell(Atom): """ def __init__(self, elements: Collection, regular: bool = True, **kwargs) -> None: - """Initialize 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 - ---------- - elements : Collection[Hashable] - 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. - regular : bool, 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. - **kwargs : keyword arguments, optional - Attributes belonging to the cell can be added as key-value pairs. Both the key and value must be hashable. - """ super().__init__(tuple(elements), **kwargs) self._regular = regular diff --git a/toponetx/classes/cell_complex.py b/toponetx/classes/cell_complex.py index 6176e7e4..c50d054b 100644 --- a/toponetx/classes/cell_complex.py +++ b/toponetx/classes/cell_complex.py @@ -137,17 +137,6 @@ class CellComplex(Complex): """ def __init__(self, cells=None, regular: bool = True, **kwargs) -> None: - """Initialize a new Cell Complex. - - Parameters - ---------- - cells : iterable, optional - A list of cells to add to the complex. - regular : bool, default=True - If True, then the complex is regular, otherwise it is non-regular. - **kwargs : keyword arguments, optional - Attributes to add to the complex as key=value pairs. - """ super().__init__(**kwargs) self._regular = regular diff --git a/toponetx/classes/colored_hypergraph.py b/toponetx/classes/colored_hypergraph.py index ca3403d5..4ce09c9d 100644 --- a/toponetx/classes/colored_hypergraph.py +++ b/toponetx/classes/colored_hypergraph.py @@ -24,6 +24,13 @@ class ColoredHyperGraph(Complex): """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 ---------- cells : Collection, optional @@ -32,6 +39,46 @@ class ColoredHyperGraph(Complex): Represents the color of cells. **kwargs : keyword arguments, optional Attributes to add to the complex as key=value pairs. + + Attributes + ---------- + complex : dict + A dictionary that can be used to store additional information about the complex. + + 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. """ def __init__( @@ -40,65 +87,6 @@ def __init__( ranks: Collection | int | None = None, **kwargs, ) -> None: - """ - Initialize the Colored HyperGraph. - - 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 - ---------- - cells : Collection, optional - The initial collection of cells in the Colored Hypergraph. - ranks : Collection, optional - Represents the color of cells. - **kwargs : keyword arguments, optional - Attributes to add to the complex as key=value pairs. - - Attributes - ---------- - complex : dict - A dictionary that can be used to store additional information about the complex. - - 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. - """ super().__init__(**kwargs) self._complex_set = ColoredHyperEdgeView() diff --git a/toponetx/classes/combinatorial_complex.py b/toponetx/classes/combinatorial_complex.py index 6bd9e49e..34619806 100644 --- a/toponetx/classes/combinatorial_complex.py +++ b/toponetx/classes/combinatorial_complex.py @@ -48,6 +48,15 @@ class CombinatorialComplex(ColoredHyperGraph): **kwargs : keyword 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. + Attributes ---------- complex : dict @@ -76,39 +85,6 @@ def __init__( graph_based: bool = False, **kwargs, ) -> None: - """Generate a Combinatorial Complex. - - Parameters - ---------- - cells : Collection, optional - A collection of cells to add to the combinatorial complex. - ranks : Collection, 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_based : bool, default=False - When true rank 1 edges must have cardinality equals to 1. - **kwargs : keyword arguments, optional - Attributes to add to the complex as key=value pairs. - - Returns - ------- - None - Initialized Combinatorial Complex. - - 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. - - Notes - ----- - This function initializes a Combinatorial Complex with the given parameters. It adds cells to the complex based on the input. - If cells is a NetworkX graph, it adds nodes and edges accordingly. - """ Complex.__init__(self, **kwargs) self.graph_based = graph_based # rank 1 edges have cardinality equals to 1 self._node_membership = {} diff --git a/toponetx/classes/complex.py b/toponetx/classes/complex.py index a9f93af0..4b9f095c 100644 --- a/toponetx/classes/complex.py +++ b/toponetx/classes/complex.py @@ -23,15 +23,6 @@ class Atom(abc.ABC): name: str def __init__(self, elements: Collection[Hashable], **kwargs) -> None: - """Abstract class representing an atom in a complex. - - Parameters - ---------- - elements : Collection[Hashable] - The elements in the atom. - **kwargs : keyword arguments, optional - Additional attributes to be associated with the atom. - """ self.elements = elements self._attributes = {} diff --git a/toponetx/classes/hyperedge.py b/toponetx/classes/hyperedge.py index 4aa73bf1..4248ac58 100644 --- a/toponetx/classes/hyperedge.py +++ b/toponetx/classes/hyperedge.py @@ -33,28 +33,6 @@ class HyperEdge(Atom): """ def __init__(self, elements: Collection, rank=None, **kwargs) -> None: - """Generate instance of the 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 - ---------- - elements : iterable of hashables - The nodes in the hyperedge. - rank : int, optional - The rank of the hyperedge. Default is None. - **kwargs : additional 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) - """ for i in elements: if not isinstance(i, Hashable): raise TypeError("Every element of HyperEdge must be hashable.") diff --git a/toponetx/classes/path.py b/toponetx/classes/path.py index fa4efb7e..ebd0bef1 100644 --- a/toponetx/classes/path.py +++ b/toponetx/classes/path.py @@ -83,71 +83,6 @@ def __init__( allowed_paths: Iterable[tuple[Hashable]] | None = None, **kwargs, ) -> None: - """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 - ---------- - elements : Sequence[Hashable] - The nodes in the elementary p-path. - construct_boundaries : bool, default=False - If True, construct the entire boundary of the elementary p-path. - reserve_sequence_order : bool, 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_paths : Iterable[tuple[Hashable]], optional - A list of allowed boundaries of an elementary p-path. If None, all possible boundaries are constructed (similarly to simplex). - **kwargs : keyword 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] 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)] - """ self.__check_inputs(elements, reserve_sequence_order) if isinstance(elements, int | str): elements = [elements] diff --git a/toponetx/classes/path_complex.py b/toponetx/classes/path_complex.py index 56793d2d..fc2919b1 100644 --- a/toponetx/classes/path_complex.py +++ b/toponetx/classes/path_complex.py @@ -87,69 +87,6 @@ def __init__( max_rank: int = 3, **kwargs, ) -> None: - """Initialize an object of 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 - ---------- - paths : nx.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_order : bool, 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_paths : Iterable[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_rank : int, default=3 - The maximal length of a path in the path complex. - **kwargs : keyword 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] Truong and Chin. - Generalizing Topological Graph Neural Networks with Paths. - https://arxiv.org/abs/2308.06838.pdf - .. [2] 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)]) - """ super().__init__(**kwargs) self._path_set = PathView() diff --git a/toponetx/classes/reportviews.py b/toponetx/classes/reportviews.py index d21a4a3d..88ae444c 100644 --- a/toponetx/classes/reportviews.py +++ b/toponetx/classes/reportviews.py @@ -84,7 +84,6 @@ class CellView(AtomView[Cell]): """A CellView class for cells of a CellComplex.""" def __init__(self) -> None: - """Initialize an object for the CellView class for cells of a CellComplex.""" # Initialize a dictionary to hold cells, with keys being the tuple # that defines the cell, and values being dictionaries of cell objects # with different attributes @@ -260,12 +259,6 @@ class ColoredHyperEdgeView(AtomView): """ def __init__(self) -> None: - """Initialize a new instance of the ColoredHyperEdgeView class. - - Examples - -------- - >>> hev = ColoredHyperEdgeView() - """ self.hyperedge_dict = {} def __getitem__(self, hyperedge) -> dict: @@ -518,15 +511,6 @@ class HyperEdgeView(AtomView): """ def __init__(self) -> None: - """Initialize an instance of the 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() - """ self.hyperedge_dict = {} @staticmethod @@ -823,13 +807,6 @@ class SimplexView(AtomView[Simplex]): """ def __init__(self) -> None: - """Initialize an instance of the 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. - """ self.max_dim = -1 self.faces_dict = [] @@ -983,17 +960,6 @@ class NodeView: """ def __init__(self, objectdict, cell_type, colored_nodes: bool = False) -> None: - """Initialize an instance of the Node view class. - - Parameters - ---------- - objectdict : dict - A dictionary of nodes with their attributes. - cell_type : type - The type of the cell. - colored_nodes : bool, optional, default = False - Whether or not the nodes are colored. - """ if len(objectdict) != 0: self.nodes = objectdict[0] else: @@ -1091,10 +1057,6 @@ def __contains__(self, e) -> bool: class PathView(SimplexView): """Path view class.""" - def __init__(self) -> None: - """Initialize an instance of the Path view class.""" - super().__init__() - def __getitem__(self, path: Any) -> dict: """Get the dictionary of attributes associated with the given path. diff --git a/toponetx/classes/simplex.py b/toponetx/classes/simplex.py index 1cdb2b65..3bfb3c03 100644 --- a/toponetx/classes/simplex.py +++ b/toponetx/classes/simplex.py @@ -44,32 +44,6 @@ def __init__( construct_tree: bool = False, **kwargs, ) -> None: - """Initialize an instance of the 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 - ---------- - elements : Collection - The nodes in the simplex. - construct_tree : bool, default=True - If True, construct the entire simplicial tree for the simplex. - **kwargs : keyword 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) - """ if construct_tree is not False: warnings.warn( "The `construct_tree` argument is deprecated.", From 149de05dc3ff13848e263296c52ec601891bb57c Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Tue, 5 Mar 2024 17:00:42 +0100 Subject: [PATCH 03/53] Improve typing in algorithms module --- toponetx/algorithms/components.py | 113 ++++++++++++++++------- toponetx/algorithms/distance.py | 19 ++-- toponetx/algorithms/distance_measures.py | 21 +++-- 3 files changed, 106 insertions(+), 47 deletions(-) diff --git a/toponetx/algorithms/components.py b/toponetx/algorithms/components.py index ddb78f71..9fa5e1af 100644 --- a/toponetx/algorithms/components.py +++ b/toponetx/algorithms/components.py @@ -1,12 +1,12 @@ """Module to compute connected components on topological domains.""" from collections.abc import Generator, Hashable +from typing import Literal, TypeVar, overload import networkx as nx from toponetx.classes.cell_complex import CellComplex from toponetx.classes.colored_hypergraph import ColoredHyperGraph from toponetx.classes.combinatorial_complex import CombinatorialComplex -from toponetx.classes.complex import Complex __all__ = [ "s_connected_components", @@ -15,22 +15,51 @@ "connected_component_subcomplexes", ] +# In this module, only cell complexes, combinatorial complexes and colored +# hypergraphs are supported. We bound the type variable to these types. +ComplexType = CellComplex | CombinatorialComplex | ColoredHyperGraph +ComplexTypeVar = TypeVar("ComplexTypeVar", bound=ComplexType) + + +@overload +def s_connected_components( + domain: ComplexType, + s: int, + cells: Literal[True] = ..., + return_singletons: bool = ..., +) -> Generator[set[tuple[Hashable, ...]], None, None]: + pass + + +@overload +def s_connected_components( + domain: ComplexType, s: int, cells: Literal[False], return_singletons: bool = ... +) -> Generator[set[Hashable], None, None]: + pass + + +@overload +def s_connected_components( + domain: ComplexType, s: int, cells: bool, return_singletons: bool = ... +) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: + pass + def s_connected_components( - domain: Complex, s: int = 1, cells: bool = True, return_singletons: bool = False + domain: ComplexType, s: int, cells: bool = True, return_singletons: bool = False ) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: """Return generator for the s-connected components. Parameters ---------- - domain : Complex - Supported complexes are cell/combintorial and hypegraphs. - s : int, optional + domain : CellComplex or CombinatorialComplex or ColoredHyperGraph + The domain on which to compute the s-connected components. + s : int The number of intersections between pairwise consecutive cells. - cells : bool, optional + cells : bool, default=True If True will return cell components, if False will return node components. - return_singletons : bool, optional - When True, returns singletons connected components. + return_singletons : bool, default=False + When True, returns singleton connected components. Notes ----- @@ -60,21 +89,17 @@ def s_connected_components( >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) >>> list(s_connected_components(CC, s=1, cells=False)) - >>> # [{2, 3, 4}, {5, 6, 7}] + [{2, 3, 4}, {5, 6, 7}] >>> list(s_connected_components(CC, s=1, cells=True)) - >>> # [{(2, 3), (2, 3, 4), (2, 4), (3, 4)}, - >>> # {(5, 6), (5, 6, 7), (5, 7), (6, 7)}] + [{(2, 3), (2, 3, 4), (2, 4), (3, 4)}, {(5, 6), (5, 6, 7), (5, 7), (6, 7)}] >>> CHG = CC.to_colored_hypergraph() >>> list(s_connected_components(CHG, s=1, cells=False)) >>> CC.add_cell([4, 5], rank=1) >>> list(s_connected_components(CC, s=1, cells=False)) - >>> # [{2, 3, 4, 5, 6, 7}] + [{2, 3, 4, 5, 6, 7}] >>> CCC = CC.to_combinatorial_complex() >>> list(s_connected_components(CCC, s=1, cells=False)) """ - if not isinstance(domain, CellComplex | ColoredHyperGraph | CombinatorialComplex): - raise TypeError(f"Input complex {domain} is not supported.") - if cells: cell_dict, A = domain.all_cell_to_node_coadjacency_matrix(s=s, index=True) cell_dict = {v: k for k, v in cell_dict.items()} @@ -107,23 +132,26 @@ def s_connected_components( def s_component_subcomplexes( - domain: Complex, s: int = 1, cells: bool = True, return_singletons: bool = False -) -> Generator[Complex, None, None]: + domain: ComplexTypeVar, + s: int = 1, + cells: bool = True, + return_singletons: bool = False, +) -> Generator[ComplexTypeVar, None, None]: """Return a generator for the induced subcomplexes of s_connected components. Removes singletons unless return_singletons is set to True. Parameters ---------- - domain : Complex - Supported complexes are cell/combintorial and hypegraphs. - s : int, optional + domain : CellComplex or CombinatorialComplex or ColoredHyperGraph + The domain for which to compute the the s-connected subcomplexes. + s : int, default=1 The number of intersections between pairwise consecutive cells. - cells : bool, optional + cells : bool, default=True Determines if cell or node components are desired. Returns subcomplexes equal to the cell complex restricted to each set of nodes(cells) in the s-connected components or s-cell-connected components. - return_singletons : bool, optional + return_singletons : bool, default=False When True, returns singletons connected components. Yields @@ -154,8 +182,29 @@ def s_component_subcomplexes( yield domain.restrict_to_nodes(list(c)) +@overload +def connected_components( + domain: ComplexType, cells: Literal[True] = ..., return_singletons: bool = ... +) -> Generator[set[tuple[Hashable, ...]], None, None]: + pass + + +@overload +def connected_components( + domain: ComplexType, cells: Literal[False], return_singletons: bool = ... +) -> Generator[set[Hashable], None, None]: + pass + + +@overload +def connected_components( + domain: ComplexType, cells: bool, return_singletons: bool = ... +) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: + pass + + def connected_components( - domain: Complex, cells: bool = False, return_singletons: bool = True + domain: ComplexType, cells: bool = False, return_singletons: bool = True ) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: """Compute s-connected components with s=1. @@ -165,14 +214,14 @@ def connected_components( ---------- domain : Complex Supported complexes are cell/combintorial and hypegraphs. - cells : bool, optional + cells : bool, default=False If True will return cell components, if False will return node components. - return_singletons : bool, optional + return_singletons : bool, default=True When True, returns singletons connected components. Yields ------ - set[Hashable] | set[tuple[Hashable, ...]], None, None + set[Hashable] | set[tuple[Hashable, ...]] Yields subcomplexes generated by the cells (or nodes) in the cell(node) components of complex. @@ -190,20 +239,22 @@ def connected_components( >>> CC.add_cell([4, 5], rank=1) >>> list(CC.connected_components(CC, cells=False)) """ - yield from s_connected_components(domain, s=1, cells=cells, return_singletons=True) + yield from s_connected_components( + domain, s=1, cells=cells, return_singletons=return_singletons + ) def connected_component_subcomplexes( - domain: Complex, return_singletons: bool = True -) -> Generator[Complex, None, None]: + domain: ComplexTypeVar, return_singletons: bool = True +) -> Generator[ComplexTypeVar, None, None]: """Compute connected component subcomplexes with s=1. Same as :meth:`s_component_subcomplexes` with s=1. Parameters ---------- - domain : Complex - Supported complexes are cell/combintorial and hypegraphs. + domain : CellComplex or CombinaorialComplex or ColoredHyperGraph + The domain for which to compute the the connected subcomplexes. return_singletons : bool, optional When True, returns singletons connected components. diff --git a/toponetx/algorithms/distance.py b/toponetx/algorithms/distance.py index f26be452..ba56b882 100644 --- a/toponetx/algorithms/distance.py +++ b/toponetx/algorithms/distance.py @@ -9,19 +9,24 @@ from toponetx.classes.cell_complex import CellComplex from toponetx.classes.colored_hypergraph import ColoredHyperGraph from toponetx.classes.combinatorial_complex import CombinatorialComplex -from toponetx.classes.complex import Complex from toponetx.classes.hyperedge import HyperEdge __all__ = ["distance", "cell_distance"] +# In this module, only cell complexes, combinatorial complexes and colored +# hypergraphs are supported. +ComplexType = CellComplex | CombinatorialComplex | ColoredHyperGraph -def distance(domain: Complex, source: Hashable, target: Hashable, s: int = 1) -> int: + +def distance( + domain: ComplexType, source: Hashable, target: Hashable, s: int = 1 +) -> int: """Return shortest s-walk distance between two nodes in the cell complex. Parameters ---------- - domain : Complex - Supported complexes are cell/combintorial and hypegraphs. + domain : CellComplex or CombinaorialComplex or ColoredHyperGraph + The domain on which to compute the s-walk distance between source and target. source : Hashable A node in the input complex. target : Hashable @@ -81,7 +86,7 @@ def distance(domain: Complex, source: Hashable, target: Hashable, s: int = 1) -> def cell_distance( - domain: Complex, + domain: ComplexType, source: Iterable | HyperEdge | Cell, target: Iterable | HyperEdge | Cell, s: int = 1, @@ -90,8 +95,8 @@ def cell_distance( Parameters ---------- - domain : Complex - Supported complexes are cell/combintorial and hypegraphs. + domain : CellComplex or CombinatorialComplex or ColoredHyperGraph + The domain on which to compute the s-walk distance between source and target cells. source : Iterable or HyperEdge or Cell An Iterable representing a cell in the input complex cell complex. target : Iterable or HyperEdge or Cell diff --git a/toponetx/algorithms/distance_measures.py b/toponetx/algorithms/distance_measures.py index 0efd31cf..d775653b 100644 --- a/toponetx/algorithms/distance_measures.py +++ b/toponetx/algorithms/distance_measures.py @@ -6,17 +6,20 @@ from toponetx.classes.cell_complex import CellComplex from toponetx.classes.colored_hypergraph import ColoredHyperGraph from toponetx.classes.combinatorial_complex import CombinatorialComplex -from toponetx.classes.complex import Complex __all__ = ["node_diameters", "cell_diameters", "diameter", "cell_diameter"] +# In this module, only cell complexes, combinatorial complexes and colored +# hypergraphs are supported. +ComplexType = CellComplex | CombinatorialComplex | ColoredHyperGraph -def node_diameters(domain: Complex) -> tuple[list[int], list[set[Hashable]]]: + +def node_diameters(domain: ComplexType) -> tuple[list[int], list[set[Hashable]]]: """Return the node diameters of the connected components in cell complex. Parameters ---------- - domain : Complex + domain : CellComplex or CombinaorialComplex or ColoredHyperGraph The complex to be used to generate the node diameters for. Returns @@ -58,12 +61,12 @@ def node_diameters(domain: Complex) -> tuple[list[int], list[set[Hashable]]]: return diams, comps -def cell_diameters(domain: Complex, s: int = 1) -> tuple[list[int], list[set[int]]]: +def cell_diameters(domain: ComplexType, s: int = 1) -> tuple[list[int], list[set[int]]]: """Return the cell diameters of the s_cell_connected component subgraphs. Parameters ---------- - domain : Complex + domain : CellComplex or CombinatorialComplex or ColoredHyperGraph Supported complexes are cell/combintorial and hypegraphs. s : int, optional The number of intersections between pairwise consecutive cells. @@ -105,12 +108,12 @@ def cell_diameters(domain: Complex, s: int = 1) -> tuple[list[int], list[set[int return diams, comps -def diameter(domain: Complex) -> int: +def diameter(domain: ComplexType) -> int: """Return length of the longest shortest s-walk between nodes. Parameters ---------- - domain : Complex + domain : CellComplex or CombinatorialComplex or ColoredHyperGraph Supported complexes are cell/combintorial and hypegraphs. Returns @@ -151,12 +154,12 @@ def diameter(domain: Complex) -> int: raise RuntimeError("cc is not connected.") -def cell_diameter(domain: Complex, s: int | None = None) -> int: +def cell_diameter(domain: ComplexType, s: int | None = None) -> int: """Return the length of the longest shortest s-walk between cells. Parameters ---------- - domain : Complex + domain : CellComplex or CombinatorialComplex or ColoredHyperGraph Supported complexes are cell/combintorial and hypegraphs. s : int, optional The number of intersections between pairwise consecutive cells. From cbd9f3ee8f05520eac18a7b2748eaa51be5902ff Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Wed, 6 Mar 2024 14:37:18 +0100 Subject: [PATCH 04/53] Ignore doc validation for overloaded functions --- toponetx/algorithms/components.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/toponetx/algorithms/components.py b/toponetx/algorithms/components.py index 9fa5e1af..c9706239 100644 --- a/toponetx/algorithms/components.py +++ b/toponetx/algorithms/components.py @@ -27,21 +27,21 @@ def s_connected_components( s: int, cells: Literal[True] = ..., return_singletons: bool = ..., -) -> Generator[set[tuple[Hashable, ...]], None, None]: +) -> Generator[set[tuple[Hashable, ...]], None, None]: # numpydoc ignore=GL08 pass @overload def s_connected_components( domain: ComplexType, s: int, cells: Literal[False], return_singletons: bool = ... -) -> Generator[set[Hashable], None, None]: +) -> Generator[set[Hashable], None, None]: # numpydoc ignore=GL08 pass @overload def s_connected_components( domain: ComplexType, s: int, cells: bool, return_singletons: bool = ... -) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: +) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: # numpydoc ignore=GL08 pass @@ -185,21 +185,21 @@ def s_component_subcomplexes( @overload def connected_components( domain: ComplexType, cells: Literal[True] = ..., return_singletons: bool = ... -) -> Generator[set[tuple[Hashable, ...]], None, None]: +) -> Generator[set[tuple[Hashable, ...]], None, None]: # numpydoc ignore=GL08 pass @overload def connected_components( domain: ComplexType, cells: Literal[False], return_singletons: bool = ... -) -> Generator[set[Hashable], None, None]: +) -> Generator[set[Hashable], None, None]: # numpydoc ignore=GL08 pass @overload def connected_components( domain: ComplexType, cells: bool, return_singletons: bool = ... -) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: +) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: # numpydoc ignore=GL08 pass From 0e51e81eb63396b3d310e01b2041f1c05e854108 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Thu, 7 Mar 2024 17:23:00 +0100 Subject: [PATCH 05/53] Raise an exception for distances between non-connected elements. --- test/algorithms/test_distance.py | 45 ++++++++++-------------- toponetx/algorithms/components.py | 8 ++--- toponetx/algorithms/distance.py | 44 +++++++++++++++-------- toponetx/algorithms/distance_measures.py | 7 +--- toponetx/classes/colored_hypergraph.py | 8 +++++ toponetx/exception.py | 24 +++++++++++++ 6 files changed, 83 insertions(+), 53 deletions(-) create mode 100644 toponetx/exception.py diff --git a/test/algorithms/test_distance.py b/test/algorithms/test_distance.py index 093c3c9e..d991b3f7 100644 --- a/test/algorithms/test_distance.py +++ b/test/algorithms/test_distance.py @@ -8,6 +8,7 @@ from toponetx.classes.combinatorial_complex import CombinatorialComplex from toponetx.classes.hyperedge import HyperEdge from toponetx.classes.simplicial_complex import SimplicialComplex +from toponetx.exception import TopoNetXNoPath class TestDistance: @@ -15,16 +16,12 @@ class TestDistance: def test_distance(self): """Test for the distance method.""" - CC = CellComplex() # Initialize your class object + CC = CellComplex() - # Add some cells to the complex CC.add_cell([2, 3, 4], rank=2) CC.add_cell([5, 6, 7], rank=2) - # Test the function - result = distance(CC, 2, 3) - expected_result = 1 - assert result == expected_result + assert distance(CC, 2, 3) == 1 def test_distance_edgecases(self): """Test the distance method to check exceptions.""" @@ -33,34 +30,30 @@ def test_distance_edgecases(self): CCC.add_cell([2, 3, 4], rank=2) CCC.add_cell([5, 6, 7], rank=2) - with pytest.warns(): - distance(CCC, 2, 3) + with pytest.raises(TopoNetXNoPath): + distance(CCC, 2, 5) - with pytest.warns(): + with pytest.raises(KeyError): distance(CCC, [2, 3, 4], [5, 6, 7]) - with pytest.warns(): + with pytest.raises(KeyError): distance(CCC, Cell([2, 3, 4]), Cell([5, 6, 7])) - with pytest.raises(ValueError): + with pytest.raises(TypeError): distance(1, 2, 3) - with pytest.raises(ValueError): + with pytest.raises(TypeError): distance(SimplicialComplex(), 2, 3) def test_cell_distance(self): """Test for the cell_distance method.""" - CC = CellComplex() # Initialize your class object + CC = CellComplex() - # Add some cells to the complex CC.add_cell([2, 3, 4], rank=2) CC.add_cell([5, 6, 7], rank=2) CC.add_cell([2, 5], rank=1) - # Test the function - result = cell_distance(CC, (2, 3, 4), (5, 6, 7)) - expected_result = 2 - assert result == expected_result + assert cell_distance(CC, (2, 3, 4), (5, 6, 7)) == 2 def test_cell_distance_edgecases(self): """Test for cell_distance with exceptions.""" @@ -69,17 +62,17 @@ def test_cell_distance_edgecases(self): CCC.add_cell([2, 3, 4], rank=2) CCC.add_cell([5, 6, 7], rank=2) - with pytest.warns(): - cell_distance(CCC, 2, 3) + with pytest.raises(TopoNetXNoPath): + cell_distance(CCC, (2, 3, 4), (5, 6, 7)) - with pytest.warns(): - cell_distance(CCC, [2, 3, 4], [5, 6, 7]) + with pytest.raises(KeyError): + cell_distance(CCC, [3, 4, 5], [5, 6, 7]) - with pytest.warns(): - cell_distance(CCC, HyperEdge([2, 3, 4]), HyperEdge([5, 6, 7])) + with pytest.raises(KeyError): + cell_distance(CCC, HyperEdge([3, 4, 5]), HyperEdge([5, 6, 7])) - with pytest.raises(ValueError): + with pytest.raises(TypeError): cell_distance(1, 2, 3) - with pytest.raises(ValueError): + with pytest.raises(TypeError): cell_distance(SimplicialComplex(), 2, 3) diff --git a/toponetx/algorithms/components.py b/toponetx/algorithms/components.py index c9706239..0ae34034 100644 --- a/toponetx/algorithms/components.py +++ b/toponetx/algorithms/components.py @@ -115,12 +115,8 @@ def s_connected_components( else: node_dict, A = domain.node_to_all_cell_adjacnecy_matrix(s=s, index=True) - if isinstance(domain, ColoredHyperGraph) and not isinstance( - domain, CombinatorialComplex - ): - node_dict = {v: k[0] for k, v in node_dict.items()} - else: - node_dict = {v: k for k, v in node_dict.items()} + node_dict = {v: k for k, v in node_dict.items()} + G = nx.from_scipy_sparse_array(A) for c in nx.connected_components(G): if not return_singletons and len(c) == 1: diff --git a/toponetx/algorithms/distance.py b/toponetx/algorithms/distance.py index ba56b882..a69fb606 100644 --- a/toponetx/algorithms/distance.py +++ b/toponetx/algorithms/distance.py @@ -1,15 +1,14 @@ """Module to compute distance between nodes or cells on topological domains.""" from collections.abc import Hashable, Iterable -from warnings import warn import networkx as nx -import numpy as np from toponetx.classes.cell import Cell from toponetx.classes.cell_complex import CellComplex from toponetx.classes.colored_hypergraph import ColoredHyperGraph from toponetx.classes.combinatorial_complex import CombinatorialComplex from toponetx.classes.hyperedge import HyperEdge +from toponetx.exception import TopoNetXNoPath __all__ = ["distance", "cell_distance"] @@ -39,6 +38,11 @@ def distance( int Shortest s-walk distance between two nodes in the cell complex. + Raises + ------ + TopoNetXNoPath + If no s-walk path exists between source and target nodes. + See Also -------- cell_distance @@ -67,7 +71,7 @@ def distance( >>> list(node_diameters(CHG)) """ if not isinstance(domain, CellComplex | CombinatorialComplex | ColoredHyperGraph): - raise ValueError(f"Input complex {domain} is not supported.") + raise TypeError(f"Input complex {domain} is not supported.") if isinstance(source, Cell): source = source.elements if isinstance(target, Cell): @@ -76,13 +80,12 @@ def distance( source = tuple(source) if isinstance(target, Iterable): target = tuple(target) - rowdict, A = domain.node_to_all_cell_adjacnecy_matrix(index=True) + rowdict, A = domain.node_to_all_cell_adjacnecy_matrix(s=s, index=True) G = nx.from_scipy_sparse_array(A) try: return nx.shortest_path_length(G, rowdict[source], rowdict[target]) - except Exception: - warn(f"No {s}-path between {source} and {target}", stacklevel=2) - return np.inf + except nx.NetworkXNoPath as exc: + raise TopoNetXNoPath() from exc def cell_distance( @@ -107,10 +110,14 @@ def cell_distance( Returns ------- int - Shortest s-walk cell distance between `source` and `target`. - A shortest s-walk is computed as a sequence of cells, - the s-walk distance is the number of cells in the sequence - minus 1. If no such path exists returns np.inf. + Shortest s-walk cell distance between `source` and `target`. A shortest s-walk + is computed as a sequence of cells, the s-walk distance is the number of cells + in the sequence minus 1. + + Raises + ------ + TopoNetXNoPath + If no s-walk path exists between source and target cells. See Also -------- @@ -141,20 +148,27 @@ def cell_distance( >>> cell_distance(CCC, frozenset({2, 3}), frozenset({6, 7})) """ if not isinstance(domain, CellComplex | CombinatorialComplex | ColoredHyperGraph): - raise ValueError(f"Input complex {domain} is not supported.") + raise TypeError(f"Input complex {domain} is not supported.") if isinstance(source, Cell | HyperEdge): source = source.elements if isinstance(target, Cell | HyperEdge): target = target.elements + if isinstance(domain, CellComplex): if isinstance(source, Iterable): source = tuple(source) if isinstance(target, Iterable): target = tuple(target) + if isinstance(domain, ColoredHyperGraph): + if isinstance(source, Iterable): + source = frozenset(source) + if isinstance(target, Iterable): + target = frozenset(target) + cell_dict, A = domain.all_cell_to_node_coadjacency_matrix(s=s, index=True) + print(cell_dict) G = nx.from_scipy_sparse_array(A) try: return nx.shortest_path_length(G, cell_dict[source], cell_dict[target]) - except Exception: - warn(f"No {s}-path between {source} and {target}", stacklevel=2) - return np.inf + except nx.NetworkXNoPath as exc: + raise TopoNetXNoPath() from exc diff --git a/toponetx/algorithms/distance_measures.py b/toponetx/algorithms/distance_measures.py index d775653b..7d3e6f30 100644 --- a/toponetx/algorithms/distance_measures.py +++ b/toponetx/algorithms/distance_measures.py @@ -41,12 +41,7 @@ def node_diameters(domain: ComplexType) -> tuple[list[int], list[set[Hashable]]] >>> list(node_diameters(CHG)) """ node_dict, A = domain.node_to_all_cell_adjacnecy_matrix(index=True) - if isinstance(domain, ColoredHyperGraph) and not isinstance( - domain, CombinatorialComplex - ): - node_dict = {v: k[0] for k, v in node_dict.items()} - else: - node_dict = {v: k for k, v in node_dict.items()} + node_dict = {v: k for k, v in node_dict.items()} G = nx.from_scipy_sparse_array(A) diams = [] diff --git a/toponetx/classes/colored_hypergraph.py b/toponetx/classes/colored_hypergraph.py index 4ce09c9d..7d4a4bd6 100644 --- a/toponetx/classes/colored_hypergraph.py +++ b/toponetx/classes/colored_hypergraph.py @@ -1177,6 +1177,14 @@ def node_to_all_cell_incidence_matrix( 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. """ + if index: + ( + row_indices, + col_indices, + incidence_matrix, + ) = self.all_ranks_incidence_matrix(0, weight=weight, index=index) + row_indices = {next(iter(k)): v for k, v in row_indices.items()} + return row_indices, col_indices, incidence_matrix return self.all_ranks_incidence_matrix(0, weight=weight, index=index) def all_ranks_incidence_matrix( diff --git a/toponetx/exception.py b/toponetx/exception.py new file mode 100644 index 00000000..e40d715c --- /dev/null +++ b/toponetx/exception.py @@ -0,0 +1,24 @@ +"""Base errors and exceptions for TopoNetX.""" + +__all__ = [ + "TopoNetXException", + "TopoNetXAlgorithmError", + "TopoNetXUnfeasible", + "TopoNetXNoPath", +] + + +class TopoNetXException(Exception): + """Base class for exceptions in TopoNetX.""" + + +class TopoNetXAlgorithmError(TopoNetXException): + """Exception that is raised if an algorithm terminates unexpectedly.""" + + +class TopoNetXUnfeasible(TopoNetXAlgorithmError): + """Exception raised by algorithms trying to solve a problem instance that has no feasible solution.""" + + +class TopoNetXNoPath(TopoNetXUnfeasible): + """Exception for algorithms that should return a path or path length where such a path does not exist.""" From 71ecc6701af7f05a9aed49e782523c1ec7f98994 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Tue, 5 Mar 2024 14:00:02 +0100 Subject: [PATCH 06/53] Make `Atom` class generic for better type inference --- toponetx/classes/cell.py | 4 ++-- toponetx/classes/complex.py | 10 ++++++---- toponetx/classes/hyperedge.py | 2 +- toponetx/classes/path.py | 2 +- toponetx/classes/simplex.py | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/toponetx/classes/cell.py b/toponetx/classes/cell.py index e8c7a9f1..a5a4568f 100644 --- a/toponetx/classes/cell.py +++ b/toponetx/classes/cell.py @@ -1,7 +1,7 @@ """Cell class.""" from collections import Counter, deque -from collections.abc import Collection, Iterable, Sequence +from collections.abc import Collection, Hashable, Iterable, Sequence from itertools import zip_longest from typing import Literal @@ -10,7 +10,7 @@ __all__ = ["Cell"] -class Cell(Atom): +class Cell(Atom[tuple[Hashable]]): """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. diff --git a/toponetx/classes/complex.py b/toponetx/classes/complex.py index 4b9f095c..94ae583f 100644 --- a/toponetx/classes/complex.py +++ b/toponetx/classes/complex.py @@ -3,12 +3,14 @@ import abc from collections.abc import Collection, Hashable, Iterator -from typing import Any +from typing import Any, Generic, TypeVar __all__ = ["Atom", "Complex"] +AtomCollectionType = TypeVar("AtomCollectionType", bound=Collection[Hashable]) -class Atom(abc.ABC): + +class Atom(abc.ABC, Generic[AtomCollectionType]): """Abstract class representing an atom in a complex. Parameters @@ -19,10 +21,10 @@ class Atom(abc.ABC): Additional attributes to be associated with the atom. """ - elements: Collection[Hashable] + elements: AtomCollectionType name: str - def __init__(self, elements: Collection[Hashable], **kwargs) -> None: + def __init__(self, elements: AtomCollectionType, **kwargs) -> None: self.elements = elements self._attributes = {} diff --git a/toponetx/classes/hyperedge.py b/toponetx/classes/hyperedge.py index 4248ac58..dc00a42d 100644 --- a/toponetx/classes/hyperedge.py +++ b/toponetx/classes/hyperedge.py @@ -8,7 +8,7 @@ __all__ = ["HyperEdge"] -class HyperEdge(Atom): +class HyperEdge(Atom[frozenset[Hashable]]): """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 diff --git a/toponetx/classes/path.py b/toponetx/classes/path.py index ebd0bef1..a2a6ca04 100644 --- a/toponetx/classes/path.py +++ b/toponetx/classes/path.py @@ -8,7 +8,7 @@ __all__ = ["Path"] -class Path(Atom): +class Path(Atom[tuple[Hashable]]): """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 diff --git a/toponetx/classes/simplex.py b/toponetx/classes/simplex.py index 3bfb3c03..17ed3fd8 100644 --- a/toponetx/classes/simplex.py +++ b/toponetx/classes/simplex.py @@ -10,7 +10,7 @@ __all__ = ["Simplex"] -class Simplex(Atom): +class Simplex(Atom[frozenset[Hashable]]): """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 From eb4d3aafcd0a5b31f4bd4e48122c0d5182c68956 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Mon, 11 Mar 2024 17:03:13 +0100 Subject: [PATCH 07/53] Some typing improvements --- toponetx/algorithms/components.py | 8 +++- toponetx/algorithms/spectrum.py | 4 +- toponetx/classes/cell_complex.py | 4 +- toponetx/classes/colored_hypergraph.py | 4 +- toponetx/classes/path.py | 8 ++-- toponetx/classes/reportviews.py | 6 ++- toponetx/classes/simplicial_complex.py | 47 ++++++++----------- .../generators/random_simplicial_complexes.py | 7 +-- toponetx/readwrite/atomlist.py | 5 +- .../transform/graph_to_simplicial_complex.py | 11 ++--- 10 files changed, 53 insertions(+), 51 deletions(-) diff --git a/toponetx/algorithms/components.py b/toponetx/algorithms/components.py index c9706239..686381c0 100644 --- a/toponetx/algorithms/components.py +++ b/toponetx/algorithms/components.py @@ -41,7 +41,9 @@ def s_connected_components( @overload def s_connected_components( domain: ComplexType, s: int, cells: bool, return_singletons: bool = ... -) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: # numpydoc ignore=GL08 +) -> Generator[ + set[Hashable] | set[tuple[Hashable, ...]], None, None +]: # numpydoc ignore=GL08 pass @@ -199,7 +201,9 @@ def connected_components( @overload def connected_components( domain: ComplexType, cells: bool, return_singletons: bool = ... -) -> Generator[set[Hashable] | set[tuple[Hashable, ...]], None, None]: # numpydoc ignore=GL08 +) -> Generator[ + set[Hashable] | set[tuple[Hashable, ...]], None, None +]: # numpydoc ignore=GL08 pass diff --git a/toponetx/algorithms/spectrum.py b/toponetx/algorithms/spectrum.py index 58f53449..e6dae5be 100644 --- a/toponetx/algorithms/spectrum.py +++ b/toponetx/algorithms/spectrum.py @@ -1,5 +1,5 @@ """Module to compute spectra.""" -from typing import Literal +from typing import Any, Literal import numpy as np import scipy as sp @@ -26,7 +26,7 @@ ] -def _normalize(f): +def _normalize(f: dict[Any, Any]) -> dict[Any, Any]: """Normalize. Parameters diff --git a/toponetx/classes/cell_complex.py b/toponetx/classes/cell_complex.py index c50d054b..19463b36 100644 --- a/toponetx/classes/cell_complex.py +++ b/toponetx/classes/cell_complex.py @@ -1178,7 +1178,9 @@ def set_cell_attributes( else: raise ValueError(f"Rank must be 0, 1 or 2, got {rank}") - def get_cell_attributes(self, name: str, rank: int) -> dict[Hashable, Any]: + def get_cell_attributes( + self, name: str, rank: int + ) -> dict[tuple[Hashable, ...], Any]: """Get node attributes from graph. Parameters diff --git a/toponetx/classes/colored_hypergraph.py b/toponetx/classes/colored_hypergraph.py index 4ce09c9d..061a7ba1 100644 --- a/toponetx/classes/colored_hypergraph.py +++ b/toponetx/classes/colored_hypergraph.py @@ -67,9 +67,7 @@ class ColoredHyperGraph(Complex): >>> 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(["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. diff --git a/toponetx/classes/path.py b/toponetx/classes/path.py index a2a6ca04..09439d82 100644 --- a/toponetx/classes/path.py +++ b/toponetx/classes/path.py @@ -84,7 +84,7 @@ def __init__( **kwargs, ) -> None: self.__check_inputs(elements, reserve_sequence_order) - if isinstance(elements, int | str): + if not isinstance(elements, Sequence): elements = [elements] super().__init__(tuple(elements), **kwargs) if len(set(elements)) != len(self.elements): @@ -106,7 +106,7 @@ def construct_path_boundaries( elements: Sequence[Hashable], reserve_sequence_order: bool = False, allowed_paths: Iterable[tuple[Hashable]] | None = None, - ) -> list[tuple[Hashable]]: + ) -> list[tuple[Hashable, ...]]: """Return list of elementary p-path objects representing the boundaries. Parameters @@ -142,7 +142,7 @@ def construct_path_boundaries( """ boundaries = [] for i in range(len(elements)): - boundary = list(elements[0:i] + elements[(i + 1) :]) + boundary = list(elements[0:i]) + list(elements[(i + 1) :]) if ( not reserve_sequence_order and len(boundary) > 1 @@ -154,7 +154,7 @@ def construct_path_boundaries( return boundaries @property - def boundary(self) -> list[tuple[Hashable]]: + def boundary(self) -> list[tuple[Hashable, ...]]: """Return the boundary of this path. The boundary of this path are all elementary (p-1)-path objects representing diff --git a/toponetx/classes/reportviews.py b/toponetx/classes/reportviews.py index 88ae444c..db15b3ff 100644 --- a/toponetx/classes/reportviews.py +++ b/toponetx/classes/reportviews.py @@ -3,7 +3,7 @@ Such as: HyperEdgeView, CellView, SimplexView, NodeView. """ -from abc import ABC +from abc import ABC, abstractmethod from collections.abc import Collection, Hashable, Iterable, Iterator, Sequence from itertools import chain from typing import Any, Generic, Literal, TypeVar @@ -28,6 +28,7 @@ class AtomView(ABC, Generic[T_Atom]): """Abstract class representing a read-only view on a collection of atoms.""" + @abstractmethod def __contains__(self, atom: Any) -> bool: """Check if a given element is in the view. @@ -42,6 +43,7 @@ def __contains__(self, atom: Any) -> bool: Whether the element is in the view. """ + @abstractmethod def __getitem__(self, atom: Any) -> dict: """Get the attributes of a given element. @@ -61,6 +63,7 @@ def __getitem__(self, atom: Any) -> dict: If the element is not in the view. """ + @abstractmethod def __iter__(self) -> Iterator[T_Atom]: """Iterate over all elements in the view. @@ -70,6 +73,7 @@ def __iter__(self) -> Iterator[T_Atom]: Iterator to iterate over all elements in the view. """ + @abstractmethod def __len__(self) -> int: """Return the number of elements in the view. diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index f1286de8..ed61e264 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -3,7 +3,7 @@ The class also supports attaching arbitrary attributes and data to cells. """ -from collections.abc import Hashable, Iterable, Iterator +from collections.abc import Collection, Hashable, Iterable, Iterator from itertools import chain, combinations from typing import Any from warnings import warn @@ -104,15 +104,6 @@ class SimplicialComplex(Complex): """ def __init__(self, simplices=None, **kwargs) -> None: - """Initialize the simplicial complex. - - Parameters - ---------- - simplices : iterable, optional - Iterable of maximal simplices that define the simplicial complex. - **kwargs : keyword arguments, optional - Attributes to add to the complex as key=value pairs. - """ super().__init__(**kwargs) self._simplex_set = SimplexView() @@ -303,7 +294,7 @@ def __len__(self) -> int: """ return len(self.skeleton(0)) - def __getitem__(self, simplex): + def __getitem__(self, simplex) -> dict[Hashable, Any]: """Get the data associated with the given simplex. Parameters @@ -444,12 +435,12 @@ def get_boundaries( return face_set - def remove_maximal_simplex(self, simplex) -> None: + def remove_maximal_simplex(self, simplex: Collection) -> None: """Remove maximal simplex from simplicial complex. Parameters ---------- - simplex : Iterable + simplex : Collection The simplex to be removed from the simplicial complex. Raises @@ -552,12 +543,12 @@ def add_node(self, node: Hashable, **kwargs) -> None: else: self.add_simplex([node], **kwargs) - def add_simplex(self, simplex, **kwargs) -> None: + def add_simplex(self, simplex: Collection, **kwargs) -> None: """Add simplex to simplicial complex. Parameters ---------- - simplex : Hashable or Iterable or Simplex or str + simplex : Collection The simplex to be added to the simplicial complex. **kwargs : keyword arguments, optional Additional attributes to be associated with the simplex. @@ -1438,7 +1429,7 @@ def from_spharpy(cls, mesh) -> "SimplicialComplex": return SC def to_hasse_graph(self) -> nx.DiGraph: - """Create Hasse graph of self. + """Create the hasse graph corresponding to this simplicial complex. Returns ------- @@ -1596,14 +1587,16 @@ def to_trimesh(self, vertex_position_name: str = "position"): "input simplicial complex has dimension higher than 2 and hence it cannot be converted to a trimesh object" ) - vertices = list( - dict( - sorted(self.get_node_attributes(vertex_position_name).items()) - ).values() + vertices = np.array( + list( + dict( + sorted(self.get_node_attributes(vertex_position_name).items()) + ).values() + ) ) return trimesh.Trimesh( - faces=self.get_all_maximal_simplices(), vertices=vertices, process=False + vertices=vertices, faces=self.get_all_maximal_simplices(), process=False ) def to_spharapy(self, vertex_position_name: str = "position"): @@ -1712,7 +1705,6 @@ def is_connected(self) -> bool: """ G = nx.Graph() for edge in self.skeleton(1): - edge = list(edge) G.add_edge(edge[0], edge[1]) for node in self.skeleton(0): G.add_node(next(iter(node))) @@ -1781,11 +1773,12 @@ def to_hypergraph(self) -> Hypergraph: >>> 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=) """ - G = [] - for rank in range(1, self.dim + 1): - edge = [list(cell) for cell in self.skeleton(rank)] - G = G + edge - return Hypergraph(G, static=True) + hyperedges = [ + list(cell) + for rank in range(1, self.dim + 1) + for cell in self.skeleton(rank) + ] + return Hypergraph(hyperedges, static=True) def to_combinatorial_complex(self): """Convert a simplicial complex to a combinatorial complex. diff --git a/toponetx/generators/random_simplicial_complexes.py b/toponetx/generators/random_simplicial_complexes.py index 067d1929..ea596c96 100644 --- a/toponetx/generators/random_simplicial_complexes.py +++ b/toponetx/generators/random_simplicial_complexes.py @@ -79,7 +79,6 @@ def random_clique_complex(n: int, p: float, seed=None) -> SimplicialComplex: return graph_to_clique_complex(graph) -@nx.utils.py_random_state("seed") def multiparameter_linial_meshulam_complex( n: int, ps: Sequence[float], seed: random.Random | None = None ) -> SimplicialComplex: @@ -111,7 +110,9 @@ def multiparameter_linial_meshulam_complex( For `p[0] = 1.0` and `p[1] = p`, this recovers the standard random Linial-Meshulam complex provided by `random_linial_meshulam_complex`. """ - graph = nx.gnp_random_graph(n, ps[0], seed) + rng = nx.utils.create_py_random_state(seed) + + graph = nx.gnp_random_graph(n, ps[0], rng) SC = SimplicialComplex() for clique in nx.enumerate_all_cliques(graph): @@ -123,7 +124,7 @@ def multiparameter_linial_meshulam_complex( simplices = SC.simplices if any(s not in simplices for s in combinations(clique, len(clique) - 1)): continue - if seed.random() < ps[len(clique) - 2]: + if rng.random() < ps[len(clique) - 2]: SC.add_simplex(clique) else: # `nx.enumerate_all_cliques` returns cliques in ascending order of size. diff --git a/toponetx/readwrite/atomlist.py b/toponetx/readwrite/atomlist.py index 2b6234bb..55bea675 100644 --- a/toponetx/readwrite/atomlist.py +++ b/toponetx/readwrite/atomlist.py @@ -266,6 +266,7 @@ def parse_atomlist( """ from ast import literal_eval + domain: CellComplex | SimplicialComplex if complex_type == "cell": domain = CellComplex() elif complex_type == "simplicial": @@ -287,7 +288,7 @@ def parse_atomlist( if nodetype is not None: elements = [nodetype(e) for e in elements] - if complex_type == "cell": + if isinstance(domain, CellComplex): if "rank" in attributes: rank = attributes.pop("rank") else: @@ -297,7 +298,7 @@ def parse_atomlist( domain.add_node(elements[0], **attributes) else: domain.add_cell(elements, rank=rank, **attributes) - elif complex_type == "simplicial": + elif isinstance(domain, SimplicialComplex): domain.add_simplex(elements, **attributes) return domain diff --git a/toponetx/transform/graph_to_simplicial_complex.py b/toponetx/transform/graph_to_simplicial_complex.py index 6c3357f7..f8605e00 100644 --- a/toponetx/transform/graph_to_simplicial_complex.py +++ b/toponetx/transform/graph_to_simplicial_complex.py @@ -56,8 +56,9 @@ def graph_to_clique_complex( # `nx.enumerate_all_cliques` returns cliques in ascending order of size. Abort calling the generator once we reach # cliques larger than the requested max dimension. - if max_dim is not None: - cliques = takewhile(lambda clique: len(clique) <= max_dim, cliques) + cliques = takewhile( + lambda clique: max_dim is None or len(clique) <= max_dim, cliques + ) SC = SimplicialComplex(cliques) @@ -156,10 +157,8 @@ def is_in_vr_complex(clique): # numpydoc ignore=GL08 return all(G[u][v]["weight"] <= r for u, v in edges) all_cliques = nx.enumerate_all_cliques(G) - possible_cliques = ( - all_cliques - if max_dim is None - else takewhile(lambda face: len(face) <= max_dim, all_cliques) + possible_cliques = takewhile( + lambda face: max_dim is None or len(face) <= max_dim, all_cliques ) vr_cliques = filter(is_in_vr_complex, possible_cliques) return SimplicialComplex(list(vr_cliques)) From f5808c80caf378ca6f44b57cf21b0439c1479a36 Mon Sep 17 00:00:00 2001 From: "your.user.name" Date: Wed, 3 Apr 2024 21:20:23 -0700 Subject: [PATCH 08/53] fixng a bug in CCC --- test/classes/test_combinatorial_complex.py | 18 ++++++++++++++++++ toponetx/classes/combinatorial_complex.py | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/test/classes/test_combinatorial_complex.py b/test/classes/test_combinatorial_complex.py index 1e70c6e8..8557776c 100644 --- a/test/classes/test_combinatorial_complex.py +++ b/test/classes/test_combinatorial_complex.py @@ -73,6 +73,24 @@ def test_init_from_networkx_graph(self): assert "a" in CCC.cells + def test_node_membership(self): + """Test creation of _node_membership dictionary.""" + CCC = CombinatorialComplex() + CCC.add_cell([1, 2, 3], rank=1) + CCC.add_cell([1, 2, 3, 4], rank=2) + + assert 1 in CCC._node_membership + assert 2 in CCC._node_membership + assert 3 in CCC._node_membership + assert 4 in CCC._node_membership + assert frozenset({1, 2, 3, 4}) in CCC._node_membership[1] + assert frozenset({1, 2, 3}) in CCC._node_membership[1] + assert frozenset({1, 2, 3, 4}) in CCC._node_membership[2] + assert frozenset({1, 2, 3}) in CCC._node_membership[2] + assert frozenset({1, 2, 3, 4}) in CCC._node_membership[3] + assert frozenset({1, 2, 3}) in CCC._node_membership[3] + assert frozenset({1, 2, 3, 4}) in CCC._node_membership[4] + def test_add_cell(self): """Test adding a cell to a CCC.""" CCC = CombinatorialComplex() diff --git a/toponetx/classes/combinatorial_complex.py b/toponetx/classes/combinatorial_complex.py index 34619806..61a352fa 100644 --- a/toponetx/classes/combinatorial_complex.py +++ b/toponetx/classes/combinatorial_complex.py @@ -558,6 +558,10 @@ def _add_hyperedge_helper(self, hyperedge_, rank, **attr): else: self._complex_set.hyperedge_dict[rank][hyperedge_].update(**attr) self._add_nodes_of_hyperedge(hyperedge_) + for i in hyperedge_: + if i not in self._node_membership: + self._node_membership[i] = set() + self._node_membership[i].add(hyperedge_) def _CCC_condition(self, hyperedge_, rank): """Check if hyperedge_ satisfies the CCC condition. From cd720ecf62cc2e3bc9dada57eef4f8eb67e1ac7f Mon Sep 17 00:00:00 2001 From: "your.user.name" Date: Thu, 4 Apr 2024 00:59:51 -0700 Subject: [PATCH 09/53] small fix to chg addressing empty graph in compute laplacian --- toponetx/classes/colored_hypergraph.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/toponetx/classes/colored_hypergraph.py b/toponetx/classes/colored_hypergraph.py index 4ce09c9d..5caaabcc 100644 --- a/toponetx/classes/colored_hypergraph.py +++ b/toponetx/classes/colored_hypergraph.py @@ -67,9 +67,7 @@ class ColoredHyperGraph(Complex): >>> 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(["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. @@ -1244,7 +1242,9 @@ def adjacency_matrix(self, rank, via_rank, s: int = 1, index: bool = False): Examples -------- - >>> G = Graph() # networkx graph + >>> 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) @@ -1406,8 +1406,10 @@ def laplacian_matrix(self, rank, sparse=False, index=False): raise ValueError( "rank for the laplacian matrix must be larger or equal to 1, got {rank}" ) - - row_dict, A = self.adjacency_matrix(0, rank, index=True) + if len(self.nodes) == 0: + A = np.empty((0, 0)) + else: + row_dict, A = self.adjacency_matrix(0, rank, index=True) if A.shape == (0, 0): L = csr_array((0, 0)) if sparse else np.empty((0, 0)) From 98718425bb9c44172e80c548a66f9c492067049f Mon Sep 17 00:00:00 2001 From: "your.user.name" Date: Sat, 6 Apr 2024 00:11:19 -0700 Subject: [PATCH 10/53] fixing bug #288 --- test/classes/test_colored_hypergraph.py | 6 ++++++ toponetx/classes/colored_hypergraph.py | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/test/classes/test_colored_hypergraph.py b/test/classes/test_colored_hypergraph.py index 89d909cd..501f7ec5 100644 --- a/test/classes/test_colored_hypergraph.py +++ b/test/classes/test_colored_hypergraph.py @@ -453,6 +453,12 @@ def test_singletons(self): CHG.add_cell([9]) assert CHG.singletons() == [(frozenset({9}), 0)] + def test_restrict_to_cells(self): + """Test restrict_to_cells of CHG.""" + CHG = ColoredHyperGraph([[1, 2, 3], [2, 3, 4]], ranks=2) + CHG.add_cell([9]) + assert len(CHG.remove_singletons().cells) == 2 + def test_from_trimesh(self): """Test from_trimesh method of CHG.""" with pytest.raises(NotImplementedError): diff --git a/toponetx/classes/colored_hypergraph.py b/toponetx/classes/colored_hypergraph.py index 5caaabcc..98e9a143 100644 --- a/toponetx/classes/colored_hypergraph.py +++ b/toponetx/classes/colored_hypergraph.py @@ -1463,7 +1463,10 @@ def restrict_to_cells(self, cell_set): for c in valid_cells: if not isinstance(c, Iterable): raise ValueError(f"each element in cell_set must be Iterable, got {c}") - chg.add_cell(c, rank=self.cells.get_rank(c)) + if isinstance(c, tuple): + chg.add_cell(c[0], rank=self.cells.get_rank(c[0])) + else: + chg.add_cell(c, rank=self.cells.get_rank(c)) return chg def restrict_to_nodes(self, node_set): From 49f6fd1271187ef88603078d02d429bd5a2d01f3 Mon Sep 17 00:00:00 2001 From: "your.user.name" Date: Sat, 6 Apr 2024 00:18:45 -0700 Subject: [PATCH 11/53] fixing unit test for empty chg and its Laplacian --- test/classes/test_colored_hypergraph.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/classes/test_colored_hypergraph.py b/test/classes/test_colored_hypergraph.py index 501f7ec5..c1ccdd70 100644 --- a/test/classes/test_colored_hypergraph.py +++ b/test/classes/test_colored_hypergraph.py @@ -447,6 +447,10 @@ def test_laplacian_matrix(self): assert row == res assert isinstance(L, csr_array) + CHG = ColoredHyperGraph() + L = CHG.laplacian_matrix(1) + assert L.shape == (0, 0) + def test_singletons(self): """Test singletons of CHG.""" CHG = ColoredHyperGraph([[1, 2, 3], [2, 3, 4]], ranks=2) From 3ebff738f09ff547e4fa3bf373a6bf7840c43e7a Mon Sep 17 00:00:00 2001 From: rodroadl Date: Fri, 3 May 2024 15:49:11 -0400 Subject: [PATCH 12/53] typos and errors fixed --- tutorials/01_simplicial_complexes.ipynb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tutorials/01_simplicial_complexes.ipynb b/tutorials/01_simplicial_complexes.ipynb index a4306789..39caca5e 100644 --- a/tutorials/01_simplicial_complexes.ipynb +++ b/tutorials/01_simplicial_complexes.ipynb @@ -229,7 +229,7 @@ "\n", "### Example 2 of simplicial complex: a graph with faces\n", "\n", - "Consider the unidrected graph shown below, which consists of five vertices, seven edges and two faces. The faces are displayed as red triangles. While an edge represents a relation between two vertices, a face (red triangle) represents a relation between three vertices. A relation between two vertices (edge) is known as a *binary relation*, whereas a relation involving three or more vertices is known as a *higher-order relation*.\n", + "Consider the undirected graph shown below, which consists of five vertices, seven edges and two faces. The faces are displayed as red triangles. While an edge represents a relation between two vertices, a face (red triangle) represents a relation between three vertices. A relation between two vertices (edge) is known as a *binary relation*, whereas a relation involving three or more vertices is known as a *higher-order relation*.\n", "\n", "It is easy to confirm that this graph with faces is a simplicial complex. This simplicial complex comprises\n", "\n", @@ -241,13 +241,16 @@ ] }, { + "attachments": { + "simplicialcomplex_2d.png": { + "image/png": "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" + } + }, "cell_type": "markdown", - "id": "fc03ad1c", + "id": "15a7ca14", "metadata": {}, "source": [ - "
\n", - "\n", - "
" + "![simplicialcomplex_2d.png](attachment:simplicialcomplex_2d.png)" ] }, { From c3345634740193e50d3066653a7993c84a27d2d0 Mon Sep 17 00:00:00 2001 From: rodroadl Date: Fri, 3 May 2024 15:51:37 -0400 Subject: [PATCH 13/53] typos and errors fixed --- tutorials/01_simplicial_complexes.ipynb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tutorials/01_simplicial_complexes.ipynb b/tutorials/01_simplicial_complexes.ipynb index 39caca5e..aa5a51d1 100644 --- a/tutorials/01_simplicial_complexes.ipynb +++ b/tutorials/01_simplicial_complexes.ipynb @@ -313,7 +313,7 @@ "## 2.1 Abstract simplicial complexes\n", "TopoNetX implemenets what is called an abstract simplicial complex. \n", "\n", - "Formally, an \\emph{abstract simplicial complex} on a finite set $\\mathcal{V}$ is denoted by the pair $(\\mathcal{V}, \\mathcal{X})$, where $\\mathcal{X}$ is a subset of the power set of $\\mathcal{V}$, satisfying the property that if $x \\in \\mathcal{X}$ and $y \\subset x$, then $y \\in \\mathcal{X}$.\n", + "Formally, an *abstract simplicial complex* on a finite set $\\mathcal{V}$ is denoted by the pair $(\\mathcal{V}, \\mathcal{X})$, where $\\mathcal{X}$ is a subset of the power set of $\\mathcal{V}$, satisfying the property that if $x \\in \\mathcal{X}$ and $y \\subset x$, then $y \\in \\mathcal{X}$.\n", "\n", "Here, $\\mathcal{V}$ represents the set of vertices of the simplicial complex.\n", "\n", @@ -623,7 +623,7 @@ "0 & 1\\\\\n", "\\end{pmatrix}$.\n", "\n", - "The first column of $B_{1, 2}$ is related to face $(2, 3, 4)$. The edges incident to face $(2, 3, 4)$ are $(2, 3)$, $(2, 4)$ and $(3, 4)$, coresponding to the elements in the third, fourth and sixth rows (and first column) of $B_{1, 2}$, each with value equal to one. The rest of the elements (at the intersection of the first, second, fifth and seventh row with the first column) have zero values, since they correspond to edges not incident to face $(2, 3, 4)$. Similarly, the second column of $B_{1, 2}$ has non-zero elements only in the fourth, fifth and seventh rows, which correspond to edges $(2, 4)$, $(2, 5)$ and $(4, 5)$, all of which are incident to face $(2, 4, 5)$. $B_{1, 2}$ can be printed via `TopoNetX` as follows." + "The first column of $B_{1, 2}$ is related to face $(2, 3, 4)$. The edges incident to face $(2, 3, 4)$ are $(2, 3)$, $(2, 4)$ and $(3, 4)$, corresponding to the elements in the third, fourth and sixth rows (and first column) of $B_{1, 2}$, each with value equal to one. The rest of the elements (at the intersection of the first, second, fifth and seventh row with the first column) have zero values, since they correspond to edges not incident to face $(2, 3, 4)$. Similarly, the second column of $B_{1, 2}$ has non-zero elements only in the fourth, fifth and seventh rows, which correspond to edges $(2, 4)$, $(2, 5)$ and $(4, 5)$, all of which are incident to face $(2, 4, 5)$. $B_{1, 2}$ can be printed via `TopoNetX` as follows." ] }, { @@ -699,9 +699,9 @@ "source": [ "### Definition of up-Laplacian\n", "\n", - "The up-Laplacian of rank 0 is a $k \\times k$ matrix $\\mathcal{L}_{up}$, where $k$ is the number of vertices in the cell complex. Let $\\mathcal{L}_{up}(i,j)$ be the $(i, j)$-th element of $\\mathcal{L}_{up}$. For $i\\neq j$, $\\mathcal{L}_{up}(i,j)= 0$ if the $i$-th vertex is not incident to $j$-th vertex via an edge incident to $i$-th vertex, whereas $\\mathcal{L}_{up}(i,j)\\neq$ 0 if it is incident. For $i = j$, $\\mathcal{L}_{up}(i,j)$ = n $\\in \\mathbb{N}$ if the $i$-th vertex is incident to $n$ edges. \n", + "The up-Laplacian of rank 0 is a $k \\times k$ matrix $\\mathcal{L}_{up}$, where $k$ is the number of vertices in the simplicial complex. Let $\\mathcal{L}_{up}(i,j)$ be the $(i, j)$-th element of $\\mathcal{L}_{up}$. For $i\\neq j$, $\\mathcal{L}_{up}(i,j)= 0$ if the $i$-th vertex is not incident to $j$-th vertex via an edge incident to $i$-th vertex, whereas $\\mathcal{L}_{up}(i,j)\\neq$ 0 if it is incident. For $i = j$, $\\mathcal{L}_{up}(i,j)$ = n $\\in \\mathbb{N}$ if the $i$-th vertex is incident to $n$ edges. \n", "\n", - "The up-Laplacian of rank 1 is a $k \\times k$ matrix $\\mathcal{L}_{up}$, wher $k$ is the number of eges in the cell complex. For $i \\neq j$, $\\mathcal{L}_{up}(i,j) = 0$ if the $i$-th edge is not incident to $j$-th edge via a face incident to $i$-th edge, whereas $\\mathcal{L}_{up}(i,j)\\neq$ 0 if it is incident. For $i = j$, $\\mathcal{L}_{up}(i,j)$ = n $\\in \\mathbb{N}$ if the $i$-th edge is incident to $n$ faces. " + "The up-Laplacian of rank 1 is a $k \\times k$ matrix $\\mathcal{L}_{up}$, wher $k$ is the number of eges in the simplicial complex. For $i \\neq j$, $\\mathcal{L}_{up}(i,j) = 0$ if the $i$-th edge is not incident to $j$-th edge via a face incident to $i$-th edge, whereas $\\mathcal{L}_{up}(i,j)\\neq$ 0 if it is incident. For $i = j$, $\\mathcal{L}_{up}(i,j)$ = n $\\in \\mathbb{N}$ if the $i$-th edge is incident to $n$ faces. " ] }, { @@ -768,7 +768,7 @@ "\n", "Row 1 of this matrix is looking at vertex 1. As we can see, vertex 1 is incident to edges $[1,2], [1,3]$. In turn those edges are incident to the vertices 1, 2 and 3. This is seen in the matrix as the first, second and third values of row 1 are non-zero. We may notice that the first entry of this row is different to the other non-zero entries, this is because it is on the diagonal and so represents how many edges vertex 1 is incident to.\n", "\n", - "Row 5 of this matrix is looking at vertex 5. Vertex 5 is incident to edges $[2,5], [4,5]$ which are incident to the vertices 2, 4 and 5. This is seen in the matrix as on the fifth row the second, foruth and fifth entries are non-zero. The fifth entry is 2, as it is on the diagonal and so is also representing how many edges vertex 5 is incident to. " + "Row 5 of this matrix is looking at vertex 5. Vertex 5 is incident to edges $[2,5], [4,5]$ which are incident to the vertices 2, 4 and 5. This is seen in the matrix as on the fifth row the second, fourth and fifth entries are non-zero. The fifth entry is 2, as it is on the diagonal and so is also representing how many edges vertex 5 is incident to. " ] }, { @@ -806,7 +806,7 @@ "\n", "Row 1 is looking at the first edge in our list of edges, $[1,2]$. From the diagram we can easily see that this edge is not incident to any faces as there are no red areas adjacent to the edge. This explains why row 1 is full of zero-entries, as there are no faces incident to the edge it means there can not be edges incident to those faces for the output. \n", "\n", - "Row 4, however, has lots of non-zero entries. The fourth edge in our list is $[2,4]$, this edge is incident to faces $[2,3,4], [2,4,5]$. These faces are incident to the edges $[2,3], [2,4], [2,5] [3,4], [4,5]$, which are the third, fourth, fifth, sixth and seventh edges in the list. The matrix represent this as the equiavelnt entries in row 4 are non-zero. The fourth entry is different as it is on the diagonal, so it has value 2 to represent the 2 faces that the fourth edge is incident to.\n", + "Row 4, however, has lots of non-zero entries. The fourth edge in our list is $[2,4]$, this edge is incident to faces $[2,3,4], [2,4,5]$. These faces are incident to the edges $[2,3], [2,4], [2,5], [3,4], [4,5]$, which are the third, fourth, fifth, sixth and seventh edges in the list. The matrix represent this as the equivalent entries in row 4 are non-zero. The fourth entry is different as it is on the diagonal, so it has value 2 to represent the 2 faces that the fourth edge is incident to.\n", "\n", "There can be no up-Laplacian of rank 2 because it would mean that its input and output would both be faces, but there is no 'up' from a face to a higher dimension. Trying to do get an up-Laplacian of rank 2 will just throw an error. " ] @@ -914,7 +914,7 @@ "source": [ "This is the down-Laplacian of rank 1 for example2, it goes from edges to vertices to edges. \n", "\n", - "Row 1 represents the down-Laplacian for the first edge in the lsit of edges, this is the edge $[1,2]$. This is incident to the vertices 1 and 2. These vertices are then incident to the edges $[1,2], [1,3], [2,3], [2,4], [2,5]$. These edges are the first, second, third, fourth and fifth edges in our list of edges, so this is represented in the matrix by row 1 having non-zero values for those entries. The first number is on the diagonal, so it has a value of 2 to show that the first edge is incident to two vertices. " + "Row 1 represents the down-Laplacian for the first edge in the list of edges, this is the edge $[1,2]$. This is incident to the vertices 1 and 2. These vertices are then incident to the edges $[1,2], [1,3], [2,3], [2,4], [2,5]$. These edges are the first, second, third, fourth and fifth edges in our list of edges, so this is represented in the matrix by row 1 having non-zero values for those entries. The first number is on the diagonal, so it has a value of 2 to show that the first edge is incident to two vertices. " ] }, { @@ -1022,7 +1022,7 @@ "id": "0859d65c-c7a4-44e6-b55b-1f6cbac2f872", "metadata": {}, "source": [ - "This is the Hodge Laplacian matrix of rank 0 for example2. From our defintions above, we can see that this will just be the up-Laplacian matrix of rank 0 - directly compare this to the output above in the up-Laplacian section to confirm. " + "This is the Hodge Laplacian matrix of rank 0 for example2. From our definitions above, we can see that this will just be the up-Laplacian matrix of rank 0 - directly compare this to the output above in the up-Laplacian section to confirm. " ] }, { @@ -1085,7 +1085,7 @@ "id": "2c930a66-b8a0-482a-ba03-cc43c4f9feb0", "metadata": {}, "source": [ - "This is the Hodge Laplacian matrix of rank 2 for example2. From our defintions above, we can see that this will just be the down-Laplacian matrix of rank 2 - directly compare this to the output above in the down-Laplacian section to confirm." + "This is the Hodge Laplacian matrix of rank 2 for example2. From our definitions above, we can see that this will just be the down-Laplacian matrix of rank 2 - directly compare this to the output above in the down-Laplacian section to confirm." ] }, { @@ -1103,7 +1103,7 @@ "id": "23671327", "metadata": {}, "source": [ - "The next step is to start assigning data or ’features’ to the faces/edge/vertices.\n", + "The next step is to start assigning data or ’features’ to the faces/edges/vertices.\n", "One way of doing this is with the `set_simplex_attributes`\n", "function available. Below is an example of how it can be used." ] From 420ceb464e9fb924266ac135a4669eaaa3b7ec92 Mon Sep 17 00:00:00 2001 From: rodroadl Date: Fri, 3 May 2024 15:58:30 -0400 Subject: [PATCH 14/53] update the example to higher quality --- tutorials/01_simplicial_complexes.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/01_simplicial_complexes.ipynb b/tutorials/01_simplicial_complexes.ipynb index aa5a51d1..8fc1731d 100644 --- a/tutorials/01_simplicial_complexes.ipynb +++ b/tutorials/01_simplicial_complexes.ipynb @@ -243,7 +243,7 @@ { "attachments": { "simplicialcomplex_2d.png": { - "image/png": "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" + "image/png": "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zDOXd+7cue5vy71n+0899VT3Yz32DYwmAAAYpCeK9/i3kaOOOqp8VF3JHm/Dv9Wb44d18yohIiKiRLIHZu8Bey72G3SvLVu2uB8/iX0jowkgeec2yK6G0IQzGrKjIXZt5yYc35BtDbE3xWzC1gYd2YDDG3JIQ+ySnk04oCH7NGAv8R771nPccceVj6gr1X8YrzfnD+unCxERESWSPTB7D9gT1U/BW6/t27e7nwMAAEzHzk6L8x5b6835w7ohIiKiRPIeqNdVPw1vveybhWlerw4AANayl57F2Zu8eh9Xj9+sExER5d/Mv1n32GnudorepHenjV9XN6XfOWNAis5t0O6GnNGQHQ05pSHHN2RbQ45uyNYGHdmAwxtySEM2N+SAhuzTgNeLdzxZZZYnx9fLPo/3+R28Zp2IiCihGn3Nupn0uvVZ3mRO+IaBiIiG3IZv7trUD+tTPv7ybvBEREQJNvW7wU/DfsvuNcMP63zDQEREQ2/qy6ZOo94Mp8FznXUiIqKEm+obhmOOOaZ4Yzl7/Zyd8l5/Lbr9BsAu7eYVrgMbf7yDbxiIiGhMHSz1x8K51JvxNet2P4iIiCjRNvyGwd7UZp7sh3vv803ANwxERDS23ifeY+LUzj777PJRd6VJb0RXY1+XiIiIMmndbxjsFHf7BsB+ALdn7evZNwvhN+/e/18H3zAQEdGYu6V8Xewx8YLyz6aFM9i+Ifb1iIiIKLP4hoGIiKi/jpX64+W84v9vn5eIiIgGEt8wEBER9Ze9j8uJMuubwdrH2//jfWCIiIhGEN8wEBEREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREbXX0tL/B2CyB5VBG5NFAAAAAElFTkSuQmCC" } }, "cell_type": "markdown", From fd1eb96b35cfc43f0f9dbe2c23ce7769af087f10 Mon Sep 17 00:00:00 2001 From: rodroadl Date: Fri, 3 May 2024 16:01:21 -0400 Subject: [PATCH 15/53] fixed a typo and an error --- tutorials/02_cell_complexes.ipynb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/02_cell_complexes.ipynb b/tutorials/02_cell_complexes.ipynb index 9c9bb7ae..418cba64 100644 --- a/tutorials/02_cell_complexes.ipynb +++ b/tutorials/02_cell_complexes.ipynb @@ -76,11 +76,11 @@ "\n", "A cell complex is a topological space $X$ equipped with a collection of cells $\\{e_\\alpha\\}$ satisfying the following conditions:\n", "\n", - "1. \\textbf{Disjoint Union:} The space $X$ is the disjoint union of the cells $\\{e_\\alpha\\}$.\n", + "1. **Disjoint Union**: The space $X$ is the disjoint union of the cells $\\{e_\\alpha\\}$.\n", "\n", - "2. \\textbf{Homeomorphisms:} Each cell $e_\\alpha$ is homeomorphic to a standard geometric cell $D^n$ (e.g., a closed $n$-dimensional ball or an $n$-dimensional cube).\n", + "2. **Homeomorphisms**: Each cell $e_\\alpha$ is homeomorphic to a standard geometric cell $D^n$ (e.g., a closed $n$-dimensional ball or an $n$-dimensional cube).\n", "\n", - "3. \\textbf{Gluing Maps:} For each pair of cells $e_\\alpha$ and $e_\\beta$, if their intersection is non-empty, there is a continuous map $f_{\\alpha\\beta}: e_\\alpha \\cap e_\\beta \\to X$ such that the restriction of $f_{\\alpha\\beta}$ to $e_\\alpha \\cap e_\\beta$ is a homeomorphism onto its image in $X$.\n", + "3. **Gluing Maps**: For each pair of cells $e_\\alpha$ and $e_\\beta$, if their intersection is non-empty, there is a continuous map $f_{\\alpha\\beta}: e_\\alpha \\cap e_\\beta \\to X$ such that the restriction of $f_{\\alpha\\beta}$ to $e_\\alpha \\cap e_\\beta$ is a homeomorphism onto its image in $X$.\n", "\n", "The cells $\\{e_\\alpha\\}$ are often labeled with their dimensions, and the gluing maps capture how lower-dimensional cells are attached to higher-dimensional ones.\n", "\n", @@ -548,7 +548,7 @@ "id": "867bb45f", "metadata": {}, "source": [ - "### Defintion of Hodge Laplacian\n", + "### Definition of Hodge Laplacian\n", "\n", "Hodge Laplacian matrix has entry $\\mathcal{L}_p(i,j)$, made from up-Laplacian values $\\mathcal{L}_{up}(i, j)$ and down-Laplacian values $\\mathcal{L}_{down}(i, j)$ such that\n", "\n", From ff0ddd8ab75a0f4a119c8556053183d2a8279bf9 Mon Sep 17 00:00:00 2001 From: rodroadl Date: Fri, 3 May 2024 16:07:33 -0400 Subject: [PATCH 16/53] fixed typos and errors --- tutorials/03_combinatorial_complexes.ipynb | 74 ++++++++++++---------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/tutorials/03_combinatorial_complexes.ipynb b/tutorials/03_combinatorial_complexes.ipynb index 3c677500..849c0d64 100644 --- a/tutorials/03_combinatorial_complexes.ipynb +++ b/tutorials/03_combinatorial_complexes.ipynb @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "id": "24d69d5e", "metadata": {}, "outputs": [], @@ -72,7 +72,7 @@ "metadata": {}, "source": [ "A *combinatorial complex*, CCC, combines features of both hypergraphs and of cell complexes $[1]$. A hypergraph is a generalisation of a graph in which a single edge is not limited to joining just two vertices like in an ordinary graph, but can actually join multiple vertices together. 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. We can use cell complex very well to model all sorts of classical topological spaces, in particular many physical spaces.\n", - "Importantly, cellular complexes naturally provide a notion of hierarchical organisation: boundary elements are related to their \"interior\", yet the interior can itself be the boundary of a higher-order object. Hierarchical relations between the boundary and the interior are often essential when describing physical quantities. In cellular complexes, this type of coupling is however only mediated via cells of adjacent dimension (the boundary of a boundary is zero; a d-2 dimensinonal object has zero measure for integration in a d-dimensional space) -- this does not imply that multi-body interactions between nodes may not be encoded, yet, this aspect is arguably not the strong suit of cellular complexes.\n", + "Importantly, cellular complexes naturally provide a notion of hierarchical organisation: boundary elements are related to their \"interior\", yet the interior can itself be the boundary of a higher-order object. Hierarchical relations between the boundary and the interior are often essential when describing physical quantities. In cellular complexes, this type of coupling is however only mediated via cells of adjacent dimension (the boundary of a boundary is zero; a d-2 dimensional object has zero measure for integration in a d-dimensional space) -- this does not imply that multi-body interactions between nodes may not be encoded, yet, this aspect is arguably not the strong suit of cellular complexes.\n", "\n", "\n", "\n", @@ -94,7 +94,7 @@ "id": "cff96cdf", "metadata": {}, "source": [ - "$[1]$ Let S be a non-empty finite set and $\\mathcal{P}(S)$ its power set. A combinatorial complex (CCC) is a tuple $(X, \\imath)$ formed by a set $X \\subset \\mathcal{P}(S)$ \\ ${\\emptyset}$ together with a rank function $\\imath : X \\rightarrow \\mathbb{Z}^+$\n", + "$[1]$ Let $S$ be a non-empty finite set and $\\mathcal{P}(S)$ be its power set. A combinatorial complex (CCC) is a tuple $(X, \\imath)$ formed by a set $X \\subset \\mathcal{P}(S)$ \\ ${\\emptyset}$ together with a rank function $\\imath : X \\rightarrow \\mathbb{Z}^+$\n", "that \n", "\n", "  (i) $\\imath$({$x$}) $= 0$ for all $x \\in S$, and \n", @@ -128,14 +128,14 @@ "id": "9d28c791", "metadata": {}, "source": [ - "This is an example of a combinatorial complex (CCC), this example has six cells of rank 0, three cells of rank 1 and two cells of rank 2. As we can see this is different to a cell comples (CC) as cells of rank 2 can contain cells of rank 0 without needing cells of rank 0 also. \n", + "This is an example of a combinatorial complex (CCC), this example has six cells of rank 0, three cells of rank 1 and two cells of rank 2. As we can see this is different to a cell complexes (CC) as cells of rank 2 can contain cells of rank 0 without needing cells of rank 0 also. \n", "\n", "To express this example as code we may use the `add_cell` function. Examples of this can be seen below." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "0ce2f1f7", "metadata": {}, "outputs": [ @@ -143,11 +143,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Combinatorial Complex with 2 nodes and cells with ranks [0, 1] and sizes [2, 1] \n", - "Combinatorial Complex with 3 nodes and cells with ranks [0, 1] and sizes [3, 2] \n", - "Combinatorial Complex with 4 nodes and cells with ranks [0, 1, 2] and sizes [4, 2, 1] \n", - "Combinatorial Complex with 5 nodes and cells with ranks [0, 1, 2] and sizes [5, 3, 1] \n", - "Combinatorial Complex with 6 nodes and cells with ranks [0, 1, 2] and sizes [6, 3, 2] \n" + "Combinatorial Complex with 2 nodes and cells with ranks [0, 1] and sizes (2, 1) \n", + "Combinatorial Complex with 3 nodes and cells with ranks [0, 1] and sizes (3, 2) \n", + "Combinatorial Complex with 4 nodes and cells with ranks [0, 1, 2] and sizes (4, 2, 1) \n", + "Combinatorial Complex with 5 nodes and cells with ranks [0, 1, 2] and sizes (5, 3, 1) \n", + "Combinatorial Complex with 6 nodes and cells with ranks [0, 1, 2] and sizes (6, 3, 2) \n" ] } ], @@ -177,7 +177,7 @@ "source": [ "The output of this code clearly demonstrates how the CCC builds up. The first line of the output comes from adding the 1-cell $[1,2]$. This 1-cell is made up of two 0-cells - therefor the output says we have cells with ranks $[0,1]$ and these cells have sizes $[2,1]$. Sizes refers to the number of cells with that rank, so after our first line of input we have two 0-cells and one 1-cell.\n", "\n", - "Each futher line of input is adding another cell of varying rank. Finally, the last line of output is telling us that we have cells of rank 0, 1 and 2 and that they respectively have sizes of 6, 3 and 2. This is such that we have six 0-cells, three 1-cells and two 2-cells, just like our example figure does." + "Each further line of input is adding another cell of varying rank. Finally, the last line of output is telling us that we have cells of rank 0, 1 and 2 and that they respectively have sizes of 6, 3 and 2. This is such that we have six 0-cells, three 1-cells and two 2-cells, just like our example figure does." ] }, { @@ -198,7 +198,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "id": "43f9b5f6", "metadata": {}, "outputs": [ @@ -207,16 +207,16 @@ "output_type": "stream", "text": [ "rank 0:\n", - "OrderedDict([(frozenset({1}), 0), (frozenset({2}), 1), (frozenset({3}), 2), (frozenset({4}), 3), (frozenset({5}), 4), (frozenset({6}), 5)])\n", + "OrderedDict({frozenset({1}): 0, frozenset({2}): 1, frozenset({3}): 2, frozenset({4}): 3, frozenset({5}): 4, frozenset({6}): 5})\n", "rank 1:\n", - "OrderedDict([(frozenset({1, 2}), 0), (frozenset({1, 3}), 1), (frozenset({2, 5}), 2)])\n", + "OrderedDict({frozenset({1, 2}): 0, frozenset({1, 3}): 1, frozenset({2, 5}): 2})\n", "rank 2:\n", - "OrderedDict([(frozenset({1, 2, 3, 4}), 0), (frozenset({2, 4, 6}), 1)])\n" + "OrderedDict({frozenset({1, 2, 3, 4}): 0, frozenset({2, 4, 6}): 1})\n" ] } ], "source": [ - "row, column, B2 = example.incidence_matrix(0, 1, index=True)\n", + "row, column, B1 = example.incidence_matrix(0, 1, index=True)\n", "row1, column1, B2 = example.incidence_matrix(1, 2, index=True)\n", "print(\"rank 0:\")\n", "print(row)\n", @@ -278,7 +278,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "id": "c4348e5b", "metadata": {}, "outputs": [ @@ -293,6 +293,14 @@ " [0 1 0 0 0 0]\n", " [0 0 0 0 0 0]]\n" ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\kkgg3\\AppData\\Roaming\\Python\\Python312\\site-packages\\scipy\\sparse\\_index.py:143: SparseEfficiencyWarning: Changing the sparsity structure of a csc_matrix is expensive. lil_matrix is more efficient.\n", + " self._set_arrayXarray(i, j, x)\n" + ] } ], "source": [ @@ -315,12 +323,12 @@ "\n", "Looking at the output, the $0^{th}$ row tells us that the $0^{th}$ 0-cell is adjacent to the $1^{st}$ and $2^{nd}$ 0-cells. That is, {1} is adjacent to {2} and {3} via some 1-cell. By looking at our list of 1-cells it is easy to see that {1,2} and {1,3} are the 1-cells that {1} is adjacent to {2}, {3} via. \n", "\n", - "The $2^{nd}$ row tell us that that $2^{nd}$ 0-cell is adjacent to the $0^{th}$ 0-cell. This is such that {3} is adjacent to {1} via a 1-cell. The rest of the entries in that row are 0, which means {3} is only adjacent via a 1-cell to {1}, this is evident by looking at the diagram of our example." + "The $2^{nd}$ row tell us that $2^{nd}$ 0-cell is adjacent to the $0^{th}$ 0-cell. This is such that {3} is adjacent to {1} via a 1-cell. The rest of the entries in that row are 0, which means {3} is only adjacent via a 1-cell to {1}, this is evident by looking at the diagram of our example." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "id": "2d4b7b29", "metadata": {}, "outputs": [ @@ -329,9 +337,9 @@ "output_type": "stream", "text": [ "[[0 1 1 1 0 0]\n", - " [1 0 1 1 0 1]\n", + " [1 0 1 2 0 1]\n", " [1 1 0 1 0 0]\n", - " [1 1 1 0 0 1]\n", + " [1 2 1 0 0 1]\n", " [0 0 0 0 0 0]\n", " [0 1 0 1 0 0]]\n" ] @@ -362,7 +370,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "id": "6c83e931", "metadata": {}, "outputs": [ @@ -416,7 +424,7 @@ "id": "5a4a8cc1", "metadata": {}, "source": [ - "A *co-adjacency matrix* is a matrix that compares a class of objects to them selves, by seeing if they are coadjacent via some cell of a lower rank. \n", + "A *co-adjacency matrix* is a matrix that compares a class of objects to themselves, by seeing if they are coadjacent via some cell of a lower rank. \n", "\n", "That is, do the two cells in the class being compared, both fully contain the same member of a lower class? Below is two examples that might make this concept clearer." ] @@ -456,7 +464,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, "id": "0a8ba26c", "metadata": {}, "outputs": [ @@ -493,7 +501,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 9, "id": "c28c6c59", "metadata": {}, "outputs": [ @@ -501,8 +509,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[0 1]\n", - " [1 0]]\n" + "[[0 2]\n", + " [2 0]]\n" ] } ], @@ -531,7 +539,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 10, "id": "059ba518", "metadata": {}, "outputs": [ @@ -564,7 +572,7 @@ "\n", "*rank-2*: 0: {1,2,3,4}, 1: {2,4,6}.\n", "\n", - "Looking at these cells it is clear that both 2-cells do not share any 1-cells, given that {2,4,6} does nto fully contain a 1-cell this is inevitable. " + "Looking at these cells it is clear that both 2-cells do not share any 1-cells, given that {2,4,6} does not fully contain a 1-cell this is inevitable. " ] }, { @@ -600,7 +608,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 11, "id": "b0c7d06e", "metadata": {}, "outputs": [ @@ -644,7 +652,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 12, "id": "61ed45e6", "metadata": {}, "outputs": [ @@ -686,7 +694,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "id": "ea0d5a3e", "metadata": {}, "outputs": [ @@ -765,7 +773,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.9" + "version": "3.12.3" }, "vscode": { "interpreter": { From 703e5dd07042b2aceaa340859b7bdf81a93f1199 Mon Sep 17 00:00:00 2001 From: rodroadl Date: Fri, 3 May 2024 16:12:20 -0400 Subject: [PATCH 17/53] fixed typos and errors --- tutorials/04_colored_hypergraphs.ipynb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tutorials/04_colored_hypergraphs.ipynb b/tutorials/04_colored_hypergraphs.ipynb index 603724a0..f3d4baef 100644 --- a/tutorials/04_colored_hypergraphs.ipynb +++ b/tutorials/04_colored_hypergraphs.ipynb @@ -143,7 +143,7 @@ "id": "9d28c791", "metadata": {}, "source": [ - "This is an example of a colored hypergraph (CHG), this example has six cells of rank 0, three cells of rank 1 and two cells of rank 2. As we can see this is different to a cell comples (CC) as cells of rank 2 can contain cells of rank 0 without needing cells of rank 0 also. \n", + "This is an example of a colored hypergraph (CHG), this example has six cells of rank 0, three cells of rank 1 and two cells of rank 2. As we can see this is different to a cell complexes (CC) as cells of rank 2 can contain cells of rank 0 without needing cells of rank 0 also. \n", "\n", "To express this example as code we may use the `add_cell` function. Examples of this can be seen below." ] @@ -194,7 +194,7 @@ "source": [ "The output of this code clearly demonstrates how the CHG builds up. The first line of the output comes from adding the 1-cell $[1,2]$. This 1-cell is made up of two 0-cells - therefor the output says we have cells with ranks $[0,1]$ and these cells have sizes $[2,1]$. Sizes refers to the number of cells with that rank, so after our first line of input we have two 0-cells and one 1-cell.\n", "\n", - "Each futher line of input is adding another cell of varying rank. Finally, the last line of output is telling us that we have cells of rank 0, 1 and 2 and that they respectively have sizes of 6, 4 and 2. This is such that we have six 0-cells, three 1-cells and two 2-cells, just like our example figure does." + "Each further line of input is adding another cell of varying rank. Finally, the last line of output is telling us that we have cells of rank 0, 1 and 2 and that they respectively have sizes of 6, 4 and 2. This is such that we have six 0-cells, three 1-cells and two 2-cells, just like our example figure does." ] }, { @@ -337,7 +337,7 @@ "\n", "Looking at the output, the $0^{th}$ row tells us that the $0^{th}$ 0-cell is adjacent to the $1^{st}$ and $2^{nd}$ 0-cells. That is, {1} is adjacent to {2} and {3} via some 1-cell. By looking at our list of 1-cells it is easy to see that {1,2},{1,3} & {1,2,3,4,5,6} are the 1-cells that {1} is adjacent to {2}, {3} via. \n", "\n", - "The $2^{nd}$ row tell us that that $2^{nd}$ 0-cell is adjacent to the $0^{th}$ 0-cell. This is such that {3} is adjacent to {1} via a 1-cell. The rest of the entries in that row are 0, which means {3} is only adjacent via a 1-cell to {1}, this is evident by looking at the diagram of our example." + "The $2^{nd}$ row tell us that $2^{nd}$ 0-cell is adjacent to the $0^{th}$ 0-cell. This is such that {3} is adjacent to {1} via a 1-cell. The rest of the entries in that row are 0, which means {3} is only adjacent via a 1-cell to {1}, this is evident by looking at the diagram of our example." ] }, { @@ -451,7 +451,7 @@ "id": "5a4a8cc1", "metadata": {}, "source": [ - "A *co-adjacency matrix* is a matrix that compares a class of objects to them selves, by seeing if they are coadjacent via some cell of a lower rank. \n", + "A *co-adjacency matrix* is a matrix that compares a class of objects to themselves, by seeing if they are coadjacent via some cell of a lower rank. \n", "\n", "That is, do the two cells in the class being compared, both fully contain the same member of a lower class? " ] @@ -615,7 +615,7 @@ "\n", "*rank-2*: 0: {1,2,3,4}.\n", "\n", - "In this marix, each row represents one of the six 0-cells, and each column represents one of the two 2-cells. \n", + "In this marix, each row represents one of the six 0-cells, and the column represents the 2-cells {1,2,3,4}. \n", "\n", "The $0^{th}$ row has a non-zero entry for the $0^{th}$ column. This tells us that {1} is incident to {1,2,3,4}." ] @@ -650,7 +650,7 @@ "\n", "The $2^{nd}$ row tells us that {2,5} is not incident to {1,2,3,4}. \n", " \n", - "The $3^{rd}$ row tells us that {1,2,3,4,5,6} is not incident to {1,2,3,4} or {2,4,6}." + "The $3^{rd}$ row tells us that {1,2,3,4,5,6} is not incident to {1,2,3,4}." ] }, { From 276732c6a2cd3259726828c68abb42f124f0cf4c Mon Sep 17 00:00:00 2001 From: rodroadl Date: Fri, 3 May 2024 16:13:17 -0400 Subject: [PATCH 18/53] changed B2 -> B1 for B01 --- tutorials/04_colored_hypergraphs.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/04_colored_hypergraphs.ipynb b/tutorials/04_colored_hypergraphs.ipynb index f3d4baef..6181596a 100644 --- a/tutorials/04_colored_hypergraphs.ipynb +++ b/tutorials/04_colored_hypergraphs.ipynb @@ -233,7 +233,7 @@ } ], "source": [ - "row, column, B2 = example.incidence_matrix(0, 1, index=True)\n", + "row, column, B1 = example.incidence_matrix(0, 1, index=True)\n", "row1, column1, B2 = example.incidence_matrix(1, 2, index=True)\n", "print(\"rank 0:\")\n", "print(row)\n", From c6b4745d786d7f6a8d4070449935b2c945f55d01 Mon Sep 17 00:00:00 2001 From: rodroadl Date: Fri, 3 May 2024 16:31:04 -0400 Subject: [PATCH 19/53] fixed a typo --- tutorials/04_colored_hypergraphs.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/04_colored_hypergraphs.ipynb b/tutorials/04_colored_hypergraphs.ipynb index 6181596a..5f5d2800 100644 --- a/tutorials/04_colored_hypergraphs.ipynb +++ b/tutorials/04_colored_hypergraphs.ipynb @@ -194,7 +194,7 @@ "source": [ "The output of this code clearly demonstrates how the CHG builds up. The first line of the output comes from adding the 1-cell $[1,2]$. This 1-cell is made up of two 0-cells - therefor the output says we have cells with ranks $[0,1]$ and these cells have sizes $[2,1]$. Sizes refers to the number of cells with that rank, so after our first line of input we have two 0-cells and one 1-cell.\n", "\n", - "Each further line of input is adding another cell of varying rank. Finally, the last line of output is telling us that we have cells of rank 0, 1 and 2 and that they respectively have sizes of 6, 4 and 2. This is such that we have six 0-cells, three 1-cells and two 2-cells, just like our example figure does." + "Each further line of input is adding another cell of varying rank. Finally, the last line of output is telling us that we have cells of rank 0, 1 and 2 and that they respectively have sizes of 6, 4 and 1. This is such that we have six 0-cells, three 1-cells and one 2-cell, just like our example figure does." ] }, { From 11d8b7dc012ea2b972762ef728040caf82ce431c Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Tue, 21 May 2024 14:49:30 +0200 Subject: [PATCH 20/53] Add function for delaunay triangulation --- test/transform/test_delaunay.py | 41 +++++++++++++++++++++++++++++++++ toponetx/transform/__init__.py | 2 ++ toponetx/transform/delaunay.py | 27 ++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 test/transform/test_delaunay.py create mode 100644 toponetx/transform/delaunay.py diff --git a/test/transform/test_delaunay.py b/test/transform/test_delaunay.py new file mode 100644 index 00000000..93527872 --- /dev/null +++ b/test/transform/test_delaunay.py @@ -0,0 +1,41 @@ +"""Test delaunay triangulation.""" + +import numpy as np + +from toponetx.transform import delaunay_triangulation + + +def test_delaunay_triangulation_simple(): + """Test the Delaunay triangulation of a simple set of points.""" + points = np.array([[0, 0], [1, 0], [0, 1]]) + SC = delaunay_triangulation(points) + + assert set(SC.simplices) == { + frozenset([0]), + frozenset([1]), + frozenset([2]), + frozenset([0, 1]), + frozenset([0, 2]), + frozenset([1, 2]), + frozenset([0, 1, 2]), + } + + +def test_delaunay_triangulation(): + """Test the Delaunay triangulation of a set of points.""" + points = np.array([[0, 0], [1, 0], [0, 1], [5, 5]]) + SC = delaunay_triangulation(points) + + assert set(SC.simplices) == { + frozenset([0]), + frozenset([1]), + frozenset([2]), + frozenset([3]), + frozenset([0, 1]), + frozenset([0, 2]), + frozenset([1, 2]), + frozenset([1, 3]), + frozenset([2, 3]), + frozenset([0, 1, 2]), + frozenset([1, 2, 3]), + } diff --git a/toponetx/transform/__init__.py b/toponetx/transform/__init__.py index 46a25f14..b45b91d7 100644 --- a/toponetx/transform/__init__.py +++ b/toponetx/transform/__init__.py @@ -1,3 +1,5 @@ """Initialize the transform module.""" + +from .delaunay import * from .graph_to_cell_complex import * from .graph_to_simplicial_complex import * diff --git a/toponetx/transform/delaunay.py b/toponetx/transform/delaunay.py new file mode 100644 index 00000000..24526bc0 --- /dev/null +++ b/toponetx/transform/delaunay.py @@ -0,0 +1,27 @@ +"""Methods to compute the Delaunay triangulation of a set of points.""" + +import numpy as np +from scipy.spatial import Delaunay + +from toponetx.classes import SimplicialComplex + + +def delaunay_triangulation(points: np.ndarray) -> SimplicialComplex: + """ + Compute the Delaunay triangulation of a set of points in the plane. + + The resulting simplicial complex has nodes 0 to (n-1), where n is the number of + points following the coordinate order in the input array. + + Parameters + ---------- + points : np.ndarray + An array of shape (n, 2) containing the coordinates of the points. + + Returns + ------- + SimplicialComplex + The Delaunay triangulation as a SimplicialComplex object. + """ + triangles = Delaunay(points) + return SimplicialComplex(triangles.simplices) From f0324f4b6ffbb19027bf340ddf0573e0cd85349a Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Tue, 21 May 2024 17:22:56 +0200 Subject: [PATCH 21/53] Function to compute the graph skeleton of a simplicial complex --- test/classes/test_simplicial_complex.py | 17 +++++++++++++++++ toponetx/classes/simplicial_complex.py | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/test/classes/test_simplicial_complex.py b/test/classes/test_simplicial_complex.py index 1d57da01..8aa78f2b 100644 --- a/test/classes/test_simplicial_complex.py +++ b/test/classes/test_simplicial_complex.py @@ -1036,3 +1036,20 @@ def test_from_spharpy(self): assert [0] in simplices assert [1] in simplices assert [2] in simplices + + def test_graph_skeleton(self): + """Test the graph_skeleton method of SimplicialComplex.""" + simplicial_complex = SimplicialComplex( + [ + (2, 6), + (4, 5), + (4, 7), + (5, 6), + (5, 7), + (1, 2, 3), + (2, 3, 4), + ] + ) + G = simplicial_complex.graph_skeleton() + assert G.number_of_nodes() == 7 + assert G.number_of_edges() == 10 diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index ed61e264..4f2fd7be 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -1804,6 +1804,22 @@ def to_combinatorial_complex(self): CCC.add_cell(cell, rank=len(cell) - 1, **self[cell]) return CCC + def graph_skeleton(self) -> nx.Graph: + """Return the graph-skeleton of this simplicial complex. + + The graph-skeleton consists of the 0 and 1-simplices (i.e., nodes and edges) + of the simplicial complex. + + Returns + ------- + nx.Graph + The graph-skeleton of this simplicial complex. + """ + G = nx.Graph() + G.add_nodes_from(map(lambda n: n[0], self.skeleton(0))) + G.add_edges_from(self.skeleton(1)) + return G + def clone(self) -> "SimplicialComplex": """Return a copy of the simplicial complex. From 58d7a3bce9ece3e1e5492d424ac145347b098b3a Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Wed, 22 May 2024 09:07:16 +0200 Subject: [PATCH 22/53] Copy node and edge data in graph skeleton --- test/classes/test_simplicial_complex.py | 9 +++++++-- toponetx/classes/simplicial_complex.py | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/test/classes/test_simplicial_complex.py b/test/classes/test_simplicial_complex.py index 8aa78f2b..ae005eac 100644 --- a/test/classes/test_simplicial_complex.py +++ b/test/classes/test_simplicial_complex.py @@ -1039,7 +1039,7 @@ def test_from_spharpy(self): def test_graph_skeleton(self): """Test the graph_skeleton method of SimplicialComplex.""" - simplicial_complex = SimplicialComplex( + SC = SimplicialComplex( [ (2, 6), (4, 5), @@ -1050,6 +1050,11 @@ def test_graph_skeleton(self): (2, 3, 4), ] ) - G = simplicial_complex.graph_skeleton() + SC[1]["some_data"] = 1 + SC[(2, 6)]["some_data"] = 42 + + G = SC.graph_skeleton() assert G.number_of_nodes() == 7 assert G.number_of_edges() == 10 + assert G.nodes[1]["some_data"] == 1 + assert G.edges[(2, 6)]["some_data"] == 42 diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index 4f2fd7be..089e02b4 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -1816,8 +1816,10 @@ def graph_skeleton(self) -> nx.Graph: The graph-skeleton of this simplicial complex. """ G = nx.Graph() - G.add_nodes_from(map(lambda n: n[0], self.skeleton(0))) - G.add_edges_from(self.skeleton(1)) + for node in self.skeleton(0): + G.add_node(node[0], **self[node]) + for edge in self.skeleton(1): + G.add_edge(*edge, **self[edge]) return G def clone(self) -> "SimplicialComplex": From 34f28dde6d3708f311e37b5232103122239d81e3 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Tue, 21 May 2024 13:08:38 +0200 Subject: [PATCH 23/53] Use `Self` return type where applicable The `Self` return type has been introduced in Python 3.11. It allows to express that the return type is of the current enclosed class while correctly considering subclasses. As usual, the `Self` type is backported to older versions of Python in `typing-extensions`. We can use the native implementation `typing.Self` once Python 3.11+ is the minimum version. --- pyproject.toml | 1 + toponetx/classes/cell.py | 6 ++++-- toponetx/classes/cell_complex.py | 9 +++++---- toponetx/classes/colored_hypergraph.py | 7 ++++--- toponetx/classes/combinatorial_complex.py | 5 +++-- toponetx/classes/complex.py | 5 +++-- toponetx/classes/path.py | 6 ++++-- toponetx/classes/path_complex.py | 6 ++++-- toponetx/classes/simplex.py | 6 ++++-- toponetx/classes/simplicial_complex.py | 21 +++++++++++---------- 10 files changed, 43 insertions(+), 29 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f6f9d858..c81cf5e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ dependencies=[ "requests", "scipy", "trimesh", + "typing-extensions", "spharapy", ] diff --git a/toponetx/classes/cell.py b/toponetx/classes/cell.py index a5a4568f..9e557a08 100644 --- a/toponetx/classes/cell.py +++ b/toponetx/classes/cell.py @@ -5,6 +5,8 @@ from itertools import zip_longest from typing import Literal +from typing_extensions import Self + from toponetx.classes.complex import Atom __all__ = ["Cell"] @@ -85,7 +87,7 @@ def __init__(self, elements: Collection, regular: bool = True, **kwargs) -> None f"self loops are not permitted, got {(e[0],e[1])} as an edge in the cell's boundary" ) - def clone(self) -> "Cell": + def clone(self) -> Self: """Clone the Cell with all attributes. The clone method by default returns an independent shallow copy of the cell and @@ -97,7 +99,7 @@ def clone(self) -> "Cell": Cell A copy of this cell. """ - return Cell(self.elements, self._regular, **self._attributes) + return self.__class__(self.elements, self._regular, **self._attributes) @staticmethod def is_valid_cell(elements: Sequence, regular: bool = False) -> bool: diff --git a/toponetx/classes/cell_complex.py b/toponetx/classes/cell_complex.py index 19463b36..50d6a1d4 100644 --- a/toponetx/classes/cell_complex.py +++ b/toponetx/classes/cell_complex.py @@ -19,6 +19,7 @@ from networkx import Graph from networkx.classes.reportviews import EdgeView, NodeView from networkx.utils import pairwise +from typing_extensions import Self from toponetx.classes.cell import Cell from toponetx.classes.combinatorial_complex import ( @@ -2294,7 +2295,7 @@ def singletons(self): """ return [node for node in self.nodes if self.degree(node) == 0] - def clone(self) -> "CellComplex": + def clone(self) -> Self: """Create a clone of the CellComplex. Returns @@ -2312,7 +2313,7 @@ def clone(self) -> "CellComplex": >>> CX2 = CC.clone() """ _G = self._G.copy() - CC = CellComplex(_G) + CC = self.__class__(_G) for cell in self.cells: CC.add_cell(cell.clone()) return CC @@ -2427,7 +2428,7 @@ def from_networkx_graph(self, G: nx.Graph) -> None: self.add_node(node) @classmethod - def from_trimesh(cls, mesh) -> "CellComplex": + def from_trimesh(cls, mesh) -> Self: """Convert from trimesh object. Parameters @@ -2468,7 +2469,7 @@ def from_trimesh(cls, mesh) -> "CellComplex": return CC @classmethod - def load_mesh(cls, file_path, process: bool = False) -> "CellComplex": + def load_mesh(cls, file_path, process: bool = False) -> Self: """Load a mesh. Parameters diff --git a/toponetx/classes/colored_hypergraph.py b/toponetx/classes/colored_hypergraph.py index b4ed7ce5..049c4001 100644 --- a/toponetx/classes/colored_hypergraph.py +++ b/toponetx/classes/colored_hypergraph.py @@ -8,6 +8,7 @@ import scipy.sparse import trimesh from scipy.sparse import csr_array, diags +from typing_extensions import Self from toponetx.classes.complex import Complex from toponetx.classes.hyperedge import HyperEdge @@ -472,7 +473,7 @@ def _remove_node(self, node) -> None: raise KeyError(f"node {node} not in {self.__shortstr__}") self._remove_node_helper(node) - def remove_node(self, node): + def remove_node(self, node) -> Self: """Remove a node from the ColoredHyperGraph. This method removes a node from the cells and deletes any reference in the nodes of the CHG. @@ -1565,7 +1566,7 @@ def remove_singletons(self): cells = [cell for cell in self.cells if cell not in self.singletons()] return self.restrict_to_cells(cells) - def clone(self) -> "ColoredHyperGraph": + def clone(self) -> Self: """Return a copy of the simplex. The clone method by default returns an independent shallow copy of the simplex @@ -1578,7 +1579,7 @@ def clone(self) -> "ColoredHyperGraph": ColoredHyperGraph ColoredHyperGraph. """ - CHG = ColoredHyperGraph() + CHG = self.__class__() for cell, key in self.cells: CHG.add_cell(cell, key=key, rank=self.cells.get_rank(cell)) return CHG diff --git a/toponetx/classes/combinatorial_complex.py b/toponetx/classes/combinatorial_complex.py index 61a352fa..0ca1efa4 100644 --- a/toponetx/classes/combinatorial_complex.py +++ b/toponetx/classes/combinatorial_complex.py @@ -4,6 +4,7 @@ from typing import Any, Literal import networkx as nx +from typing_extensions import Self from toponetx.classes.colored_hypergraph import ColoredHyperGraph from toponetx.classes.complex import Complex @@ -1077,7 +1078,7 @@ def remove_cells(self, cell_set) -> None: """ super().remove_cells(cell_set) - def clone(self) -> "CombinatorialComplex": + def clone(self) -> Self: """Return a copy of the simplex. The clone method by default returns an independent shallow copy of the simplex @@ -1090,7 +1091,7 @@ def clone(self) -> "CombinatorialComplex": CombinatorialComplex A copy of this combinatorial complex. """ - CCC = CombinatorialComplex(graph_based=self.graph_based) + CCC = self.__class__(graph_based=self.graph_based) for cell in self.cells: CCC.add_cell(cell, self.cells.get_rank(cell)) return CCC diff --git a/toponetx/classes/complex.py b/toponetx/classes/complex.py index 94ae583f..88b4a9f7 100644 --- a/toponetx/classes/complex.py +++ b/toponetx/classes/complex.py @@ -1,10 +1,11 @@ """Abstract class for Complex and Atom.""" - import abc from collections.abc import Collection, Hashable, Iterator from typing import Any, Generic, TypeVar +from typing_extensions import Self + __all__ = ["Atom", "Complex"] AtomCollectionType = TypeVar("AtomCollectionType", bound=Collection[Hashable]) @@ -227,7 +228,7 @@ def __len__(self) -> int: """Return number of nodes.""" @abc.abstractmethod - def clone(self) -> "Complex": + def clone(self) -> Self: """Clone complex.""" @abc.abstractmethod diff --git a/toponetx/classes/path.py b/toponetx/classes/path.py index 09439d82..1356a5f5 100644 --- a/toponetx/classes/path.py +++ b/toponetx/classes/path.py @@ -3,6 +3,8 @@ from collections.abc import Hashable, Iterable, Sequence from typing import Any +from typing_extensions import Self + from toponetx.classes.complex import Atom __all__ = ["Path"] @@ -167,7 +169,7 @@ def boundary(self) -> list[tuple[Hashable, ...]]: """ return self._boundaries - def clone(self) -> "Path": + def clone(self) -> Self: """Return a shallow copy of the elementary p-path. Returns @@ -175,7 +177,7 @@ def clone(self) -> "Path": Path A shallow copy of the elementary p-path. """ - return Path( + return self.__class__( self.elements, construct_boundaries=self.construct_boundaries, **self._attributes, diff --git a/toponetx/classes/path_complex.py b/toponetx/classes/path_complex.py index fc2919b1..f1470b1c 100644 --- a/toponetx/classes/path_complex.py +++ b/toponetx/classes/path_complex.py @@ -1,4 +1,5 @@ """Path complex.""" + from collections.abc import Hashable, Iterable, Iterator, Sequence from typing import Any @@ -6,6 +7,7 @@ import numpy as np import scipy as sp from networkx.classes.reportviews import EdgeView, NodeView +from typing_extensions import Self from toponetx.classes.complex import Complex from toponetx.classes.path import Path @@ -313,7 +315,7 @@ def shape(self) -> tuple[int, ...]: """ return self._path_set.shape - def clone(self) -> "PathComplex": + def clone(self) -> Self: """Return a copy of the path complex. The clone method by default returns an independent shallow copy of the path @@ -324,7 +326,7 @@ def clone(self) -> "PathComplex": PathComplex Returns a copy of the PathComplex. """ - return PathComplex(list(self.paths), rank=self.dim) + return self.__class__(list(self.paths), rank=self.dim) def skeleton(self, rank: int) -> list[tuple[Hashable]]: """Compute skeleton. diff --git a/toponetx/classes/simplex.py b/toponetx/classes/simplex.py index 17ed3fd8..2ebc9cb2 100644 --- a/toponetx/classes/simplex.py +++ b/toponetx/classes/simplex.py @@ -5,6 +5,8 @@ from itertools import combinations from typing import Any +from typing_extensions import Self + from toponetx.classes.complex import Atom __all__ = ["Simplex"] @@ -191,7 +193,7 @@ def __str__(self) -> str: """ return f"Nodes set: {tuple(self.elements)}, attrs: {self._attributes}" - def clone(self) -> "Simplex": + def clone(self) -> Self: """Return a copy of the simplex. The clone method by default returns an independent shallow copy of the simplex @@ -204,4 +206,4 @@ def clone(self) -> "Simplex": Simplex A copy of this simplex. """ - return Simplex(self.elements, **self._attributes) + return self.__class__(self.elements, **self._attributes) diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index 089e02b4..597722f7 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -12,6 +12,7 @@ import numpy as np from gudhi import SimplexTree from scipy.sparse import csr_matrix, dok_matrix +from typing_extensions import Self from toponetx.classes.complex import Complex from toponetx.classes.reportviews import NodeView, SimplexView @@ -1307,7 +1308,7 @@ def add_elements_from_nx_graph(self, G: nx.Graph) -> None: self.add_simplices_from(_simplices) - def restrict_to_simplices(self, cell_set) -> "SimplicialComplex": + def restrict_to_simplices(self, cell_set) -> Self: """Construct a simplicial complex using a subset of the simplices. Parameters @@ -1332,7 +1333,7 @@ def restrict_to_simplices(self, cell_set) -> "SimplicialComplex": SimplexView([(1,), (2,), (3,), (4,), (1, 2), (1, 3), (2, 3), (2, 4), (1, 2, 3)]) """ rns = [cell for cell in cell_set if cell in self] - return SimplicialComplex(simplices=rns) + return self.__class__(simplices=rns) def restrict_to_nodes(self, node_set): """Construct a new simplicial complex by restricting the simplices. @@ -1389,7 +1390,7 @@ def get_all_maximal_simplices(self): return [tuple(s) for s in self.simplices if self.is_maximal(s)] @classmethod - def from_spharpy(cls, mesh) -> "SimplicialComplex": + def from_spharpy(cls, mesh) -> Self: """Import from sharpy. Parameters @@ -1453,7 +1454,7 @@ def to_hasse_graph(self) -> nx.DiGraph: return G @classmethod - def from_gudhi(cls, tree: SimplexTree) -> "SimplicialComplex": + def from_gudhi(cls, tree: SimplexTree) -> Self: """Import from gudhi. Parameters @@ -1479,7 +1480,7 @@ def from_gudhi(cls, tree: SimplexTree) -> "SimplicialComplex": return SC @classmethod - def from_trimesh(cls, mesh) -> "SimplicialComplex": + def from_trimesh(cls, mesh) -> Self: """Import from trimesh. Parameters @@ -1523,7 +1524,7 @@ def from_trimesh(cls, mesh) -> "SimplicialComplex": return SC @classmethod - def load_mesh(cls, file_path, process: bool = False) -> "SimplicialComplex": + def load_mesh(cls, file_path, process: bool = False) -> Self: """Load a mesh. Parameters @@ -1667,7 +1668,7 @@ def laplace_beltrami_operator(self, mode: str = "inv_euclidean"): return mesh.laplacianmatrix(mode=mode) @classmethod - def from_nx(cls, G: nx.Graph) -> "SimplicialComplex": + def from_nx(cls, G: nx.Graph) -> Self: """Convert from netwrokx graph. Parameters @@ -1711,7 +1712,7 @@ def is_connected(self) -> bool: return nx.is_connected(G) @classmethod - def simplicial_closure_of_hypergraph(cls, H) -> "SimplicialComplex": + def simplicial_closure_of_hypergraph(cls, H) -> Self: """Compute the simplicial complex closure of a hypergraph. Parameters @@ -1822,7 +1823,7 @@ def graph_skeleton(self) -> nx.Graph: G.add_edge(*edge, **self[edge]) return G - def clone(self) -> "SimplicialComplex": + def clone(self) -> Self: """Return a copy of the simplicial complex. The clone method by default returns an independent shallow copy of the @@ -1833,4 +1834,4 @@ def clone(self) -> "SimplicialComplex": SimplicialComplex A shallow copy of this simplicial complex. """ - return SimplicialComplex(self.simplices) + return self.__class__(self.simplices) From 4298a41f2ef4940ba6b5484c414097e36c99bd33 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Tue, 21 May 2024 12:19:28 +0200 Subject: [PATCH 24/53] Use new `@deprecated` decorator The `@deprecated` decorator has been proposed in PEP 702 and is part of Python 3.13. The huge benefit of the decorator is that it moves deprecations into the type system, allowing type checkers to catch calls to these methods. Runtime `DeprecationWarnings` are raised automatically and only once on first use. As usual, the `@deprecated` decorator is backported to older versions of Python in `typing_extensions`. We can use the native implementation `warnings.deprecated` once Python 3.13+ is the minimum version. --- toponetx/classes/cell_complex.py | 11 +++----- toponetx/classes/simplex.py | 26 ++++++------------- toponetx/classes/simplicial_complex.py | 11 +++----- .../transform/graph_to_simplicial_complex.py | 19 ++++++-------- 4 files changed, 24 insertions(+), 43 deletions(-) diff --git a/toponetx/classes/cell_complex.py b/toponetx/classes/cell_complex.py index 50d6a1d4..cf2e2f27 100644 --- a/toponetx/classes/cell_complex.py +++ b/toponetx/classes/cell_complex.py @@ -10,7 +10,6 @@ from collections.abc import Hashable, Iterable, Iterator from itertools import zip_longest from typing import Any -from warnings import warn import networkx as nx import numpy as np @@ -19,7 +18,7 @@ from networkx import Graph from networkx.classes.reportviews import EdgeView, NodeView from networkx.utils import pairwise -from typing_extensions import Self +from typing_extensions import Self, deprecated from toponetx.classes.cell import Cell from toponetx.classes.combinatorial_complex import ( @@ -200,6 +199,9 @@ def nodes(self) -> NodeView: return self._G.nodes @property + @deprecated( + "`CellComplex.maxdim` is deprecated and will be removed in the future, use `CellComplex.dim` instead." + ) def maxdim(self) -> int: """Return maximum dimension. @@ -208,11 +210,6 @@ def maxdim(self) -> int: int The maximum dimension for Cell Complex. """ - warn( - "`CellComplex.maxdim` is deprecated and will be removed in the future, use `CellComplex.dim` instead.", - DeprecationWarning, - stacklevel=2, - ) return self.dim @property diff --git a/toponetx/classes/simplex.py b/toponetx/classes/simplex.py index 2ebc9cb2..98dcb611 100644 --- a/toponetx/classes/simplex.py +++ b/toponetx/classes/simplex.py @@ -5,7 +5,7 @@ from itertools import combinations from typing import Any -from typing_extensions import Self +from typing_extensions import Self, deprecated from toponetx.classes.complex import Atom @@ -91,6 +91,7 @@ def __contains__(self, item: Any) -> bool: return super().__contains__(item) @staticmethod + @deprecated("`Simplex.construct_simplex_tree` is deprecated.") def construct_simplex_tree(elements: Collection) -> frozenset["Simplex"]: """Return set of Simplex objects representing the faces. @@ -104,11 +105,6 @@ def construct_simplex_tree(elements: Collection) -> frozenset["Simplex"]: frozenset[Simplex] The set of faces of the simplex. """ - warnings.warn( - "`Simplex.construct_simplex_tree` is deprecated.", - DeprecationWarning, - stacklevel=2, - ) faceset = set() for r in range(len(elements), 0, -1): @@ -119,6 +115,9 @@ def construct_simplex_tree(elements: Collection) -> frozenset["Simplex"]: return frozenset(faceset) @property + @deprecated( + "`Simplex.boundary` is deprecated, use `SimplicialComplex.get_boundaries()` on the simplicial complex that contains this simplex instead." + ) def boundary(self) -> frozenset["Simplex"]: """Return the set of the set of all n-1 faces in of the input n-simplex. @@ -132,12 +131,6 @@ def boundary(self) -> frozenset["Simplex"]: 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). """ - warnings.warn( - "`Simplex.boundary` is deprecated, use `SimplicialComplex.get_boundaries()` on the simplicial complex that contains this simplex instead.", - DeprecationWarning, - stacklevel=2, - ) - return frozenset( Simplex(elements, construct_tree=False) for elements in combinations(self.elements, len(self) - 1) @@ -154,6 +147,9 @@ def sign(self, face) -> int: raise NotImplementedError() @property + @deprecated( + "`Simplex.faces` is deprecated, use `SimplicialComplex.get_boundaries()` on the simplicial complex that contains this simplex instead." + ) def faces(self): """Get the set of faces of the simplex. @@ -165,12 +161,6 @@ def faces(self): frozenset[Simplex] The set of faces of the simplex. """ - warnings.warn( - "`Simplex.faces` is deprecated, use `SimplicialComplex.get_boundaries()` on the simplicial complex that contains this simplex instead.", - DeprecationWarning, - stacklevel=2, - ) - return Simplex.construct_simplex_tree(self.elements) def __repr__(self) -> str: diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index 597722f7..81a1bdfc 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -6,13 +6,12 @@ from collections.abc import Collection, Hashable, Iterable, Iterator from itertools import chain, combinations from typing import Any -from warnings import warn import networkx as nx import numpy as np from gudhi import SimplexTree from scipy.sparse import csr_matrix, dok_matrix -from typing_extensions import Self +from typing_extensions import Self, deprecated from toponetx.classes.complex import Complex from toponetx.classes.reportviews import NodeView, SimplexView @@ -157,6 +156,9 @@ def dim(self) -> int: return self._simplex_set.max_dim @property + @deprecated( + "`SimplicialComplex.maxdim` is deprecated and will be removed in the future, use `SimplicialComplex.max_dim` instead." + ) def maxdim(self) -> int: """ Maximum dimension of the simplicial complex. @@ -168,11 +170,6 @@ def maxdim(self) -> int: int The maximum dimension of the simplicial complex. """ - warn( - "`SimplicialComplex.maxdim` is deprecated and will be removed in the future, use `SimplicialComplex.max_dim` instead.", - DeprecationWarning, - stacklevel=2, - ) return self._simplex_set.max_dim @property diff --git a/toponetx/transform/graph_to_simplicial_complex.py b/toponetx/transform/graph_to_simplicial_complex.py index f8605e00..90826ae2 100644 --- a/toponetx/transform/graph_to_simplicial_complex.py +++ b/toponetx/transform/graph_to_simplicial_complex.py @@ -1,8 +1,9 @@ """Methods to lift a graph to a simplicial complex.""" + from itertools import combinations, takewhile -from warnings import warn import networkx as nx +from typing_extensions import deprecated from toponetx.classes.simplicial_complex import SimplicialComplex @@ -72,6 +73,9 @@ def graph_to_clique_complex( return SC +@deprecated( + "`graph_2_neighbor_complex` is deprecated and will be removed in a future version, use `graph_to_neighbor_complex` instead." +) def graph_2_neighbor_complex(G) -> SimplicialComplex: """Get the neighbor complex of a graph. @@ -90,14 +94,12 @@ def graph_2_neighbor_complex(G) -> SimplicialComplex: This type of simplicial complexes can have very large dimension (max degree of the graph) and it is a function of the distribution of the valency of the graph. """ - warn( - "`graph_2_neighbor_complex` is deprecated and will be removed in a future version, use `graph_to_neighbor_complex` instead.", - DeprecationWarning, - stacklevel=2, - ) return graph_to_neighbor_complex(G) +@deprecated( + "`graph_2_clique_complex` is deprecated and will be removed in a future version, use `graph_to_clique_complex` instead." +) def graph_2_clique_complex( G: nx.Graph, max_dim: int | None = None ) -> SimplicialComplex: @@ -115,11 +117,6 @@ def graph_2_clique_complex( SimplicialComplex The clique simplicial complex of dimension dim of the graph G. """ - warn( - "`graph_2_clique_complex` is deprecated and will be removed in a future version, use `graph_to_clique_complex` instead.", - DeprecationWarning, - stacklevel=2, - ) return graph_to_clique_complex(G, max_dim) From 7719c3103ae3750a5bf489da62f1fc205ea4edb7 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 24 May 2024 14:06:06 +0200 Subject: [PATCH 25/53] Add missing `ColoredHyperEdgeView` export to `classes` module --- toponetx/classes/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/toponetx/classes/__init__.py b/toponetx/classes/__init__.py index 8af4f35a..0fa236f9 100644 --- a/toponetx/classes/__init__.py +++ b/toponetx/classes/__init__.py @@ -1,4 +1,5 @@ """Initialize the classes module of toponetx.""" + from .cell import Cell from .cell_complex import CellComplex from .colored_hypergraph import ColoredHyperGraph @@ -7,7 +8,14 @@ from .hyperedge import HyperEdge from .path import Path from .path_complex import PathComplex -from .reportviews import CellView, HyperEdgeView, NodeView, PathView, SimplexView +from .reportviews import ( + CellView, + ColoredHyperEdgeView, + HyperEdgeView, + NodeView, + PathView, + SimplexView, +) from .simplex import Simplex from .simplicial_complex import SimplicialComplex @@ -20,6 +28,7 @@ "Complex", "HyperEdge", "HyperEdgeView", + "ColoredHyperEdgeView", "CellView", "SimplexView", "NodeView", From 49b9c0fc283d67eb9aaa60eb57e19cb85879a565 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Sat, 25 May 2024 16:22:34 +0200 Subject: [PATCH 26/53] Add missing `PathView` export to `reportviews` --- toponetx/classes/reportviews.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/toponetx/classes/reportviews.py b/toponetx/classes/reportviews.py index db15b3ff..02de1d2b 100644 --- a/toponetx/classes/reportviews.py +++ b/toponetx/classes/reportviews.py @@ -3,6 +3,7 @@ Such as: HyperEdgeView, CellView, SimplexView, NodeView. """ + from abc import ABC, abstractmethod from collections.abc import Collection, Hashable, Iterable, Iterator, Sequence from itertools import chain @@ -20,6 +21,7 @@ "CellView", "SimplexView", "NodeView", + "PathView", ] T_Atom = TypeVar("T_Atom", bound=Atom) From 13ac77e3c328c65af107e938dbadf720f72fef75 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Wed, 29 May 2024 16:29:40 +0200 Subject: [PATCH 27/53] Refactor `SimplicialComplex.add_simplex` function --- test/classes/test_simplicial_complex.py | 10 ++++ toponetx/classes/simplicial_complex.py | 78 ++++++++++++++----------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/test/classes/test_simplicial_complex.py b/test/classes/test_simplicial_complex.py index ae005eac..ddf35667 100644 --- a/test/classes/test_simplicial_complex.py +++ b/test/classes/test_simplicial_complex.py @@ -244,6 +244,10 @@ def test_add_simplex(self): assert (5,) in SC.simplices assert (6,) in SC.simplices + # call with unsupported type + with pytest.raises(TypeError): + SC.add_simplex(iter([1, 2])) + # simplex cannot contain unhashable elements with pytest.raises(TypeError): SC.add_simplex([[1, 2], [2, 3]]) @@ -252,6 +256,12 @@ def test_add_simplex(self): with pytest.raises(ValueError): SC.add_simplex((1, 2, 2)) + # use of reserved attributes is not allowed + with pytest.raises(ValueError): + SC.add_simplex((1, 2, 3), is_maximal=True) + with pytest.raises(ValueError): + SC.add_simplex((1, 2, 3), membership={}) + # add hashable, non iterable node to SC SC.add_simplex(11) assert 11 in SC.simplices diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index 81a1bdfc..f003cd41 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -390,9 +390,9 @@ def _update_faces_dict_entry(self, face, simplex, maximal_faces) -> None: self._simplex_set.faces_dict[k - 1][face]["is_maximal"] = False else: # make sure all children of previous maximal simplices do not have that membership anymore - self._simplex_set.faces_dict[k - 1][face][ - "membership" - ] -= maximal_faces + self._simplex_set.faces_dict[k - 1][face]["membership"] -= ( + maximal_faces + ) @staticmethod def get_boundaries( @@ -544,6 +544,12 @@ def add_node(self, node: Hashable, **kwargs) -> None: def add_simplex(self, simplex: Collection, **kwargs) -> None: """Add simplex to simplicial complex. + In case sub-simplices are missing, they are added without attributes to the + simplicial complex to fulfill the simplex requirements. + + If the given simplex is already part of the simplicial complex, its attributes + will be updated by the provided ones. + Parameters ---------- simplex : Collection @@ -551,41 +557,45 @@ def add_simplex(self, simplex: Collection, **kwargs) -> None: **kwargs : keyword arguments, optional Additional attributes to be associated with the simplex. """ + # Special internal attributes must not be provided as user attributes + if "is_maximal" in kwargs or "membership" in kwargs: + raise ValueError("Special attributes `is_maximal` and `membership` are reserved.") + + # Support some short-hand calls for adding nodes. The user does not have to + # provide a single-element list but can give the node directly. if isinstance(simplex, Hashable) and not isinstance(simplex, Iterable): simplex = [simplex] if isinstance(simplex, str): simplex = [simplex] - if isinstance(simplex, Iterable | Simplex): - if not isinstance(simplex, Simplex): - simplex_ = frozenset(simplex) - if len(simplex_) != len(simplex): - raise ValueError("a simplex cannot contain duplicate nodes") - else: - simplex_ = simplex.elements - self._update_faces_dict_length(simplex_) - - if ( - simplex_ in self._simplex_set.faces_dict[len(simplex_) - 1] - ): # simplex is already in the complex, just update the attributes if needed - self._simplex_set.faces_dict[len(simplex_) - 1][simplex_].update(kwargs) - return - - if self._simplex_set.max_dim < len(simplex) - 1: - self._simplex_set.max_dim = len(simplex) - 1 - - numnodes = len(simplex_) - maximal_faces = set() - - for r in range(numnodes, 0, -1): - for face in combinations(simplex_, r): - self._update_faces_dict_entry(face, simplex_, maximal_faces) - self._simplex_set.faces_dict[len(simplex_) - 1][simplex_].update(kwargs) - if isinstance(simplex, Simplex): - self._simplex_set.faces_dict[len(simplex_) - 1][simplex_].update( - simplex._attributes - ) - else: - self._simplex_set.faces_dict[len(simplex_) - 1][simplex_].update(kwargs) + + if isinstance(simplex, Simplex): + elements = simplex.elements + kwargs.update(simplex._attributes) + elif isinstance(simplex, Collection): + elements = frozenset(simplex) + if len(elements) != len(simplex): + raise ValueError("a simplex cannot contain duplicate nodes") + else: + raise TypeError( + f"Input simplex must be a collection or a `Simplex` object, got {type(simplex)}." + ) + + # if the simplex is already part of this complex, update its attributes + if elements in self: + self._simplex_set.faces_dict[len(elements) - 1][elements].update(kwargs) + return + + self._update_faces_dict_length(elements) + + if self._simplex_set.max_dim < len(simplex) - 1: + self._simplex_set.max_dim = len(simplex) - 1 + + maximal_faces = set() + for r in range(len(elements), 0, -1): + for face in combinations(elements, r): + self._update_faces_dict_entry(face, elements, maximal_faces) + + self._simplex_set.faces_dict[len(elements) - 1][elements].update(kwargs) def add_simplices_from(self, simplices) -> None: """Add simplices from iterable to simplicial complex. From eee18cb549771a97d68916b1fbcd811e71b52418 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 31 May 2024 10:15:14 +0200 Subject: [PATCH 28/53] Improve validation of reserved attributes in simplicial complex --- test/classes/test_simplex.py | 5 +++++ toponetx/classes/simplex.py | 19 +++++++++++++++++++ toponetx/classes/simplicial_complex.py | 4 +--- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/test/classes/test_simplex.py b/test/classes/test_simplex.py index 0a0affdd..e47401c0 100644 --- a/test/classes/test_simplex.py +++ b/test/classes/test_simplex.py @@ -21,6 +21,11 @@ def test_simplex_creation(self): with pytest.raises(ValueError): _ = Simplex([1, 2, 2]) + with pytest.raises(ValueError): + _ = Simplex([1, 2], is_maximal=True) + with pytest.raises(ValueError): + _ = Simplex([1, 2], membership={}) + def test_simplex_sign(self): """Test simplex sign method.""" s = Simplex( diff --git a/toponetx/classes/simplex.py b/toponetx/classes/simplex.py index 98dcb611..98664d7e 100644 --- a/toponetx/classes/simplex.py +++ b/toponetx/classes/simplex.py @@ -46,6 +46,8 @@ def __init__( construct_tree: bool = False, **kwargs, ) -> None: + self.validate_attributes(kwargs) + if construct_tree is not False: warnings.warn( "The `construct_tree` argument is deprecated.", @@ -90,6 +92,23 @@ def __contains__(self, item: Any) -> bool: return frozenset(item) <= self.elements return super().__contains__(item) + @staticmethod + def validate_attributes(attributes: dict) -> None: + """Validate the attributes of the simplex. + + Parameters + ---------- + attributes : dict + The attributes to be validated. + + Raises + ------ + ValueError + If the attributes contain the reserved keys `is_maximal` or `membership`. + """ + if "is_maximal" in attributes or "membership" in attributes: + raise ValueError("Special attributes `is_maximal` and `membership` are reserved.") + @staticmethod @deprecated("`Simplex.construct_simplex_tree` is deprecated.") def construct_simplex_tree(elements: Collection) -> frozenset["Simplex"]: diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index f003cd41..e8d2b666 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -557,9 +557,7 @@ def add_simplex(self, simplex: Collection, **kwargs) -> None: **kwargs : keyword arguments, optional Additional attributes to be associated with the simplex. """ - # Special internal attributes must not be provided as user attributes - if "is_maximal" in kwargs or "membership" in kwargs: - raise ValueError("Special attributes `is_maximal` and `membership` are reserved.") + Simplex.validate_attributes(kwargs) # Support some short-hand calls for adding nodes. The user does not have to # provide a single-element list but can give the node directly. From 143ebe7b5a785252bd628af7d2727b39faa0d48c Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 31 May 2024 10:24:52 +0200 Subject: [PATCH 29/53] Improve case that has attributes + additional attributes are provided --- test/classes/test_simplicial_complex.py | 10 +++++++++- toponetx/classes/simplicial_complex.py | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/test/classes/test_simplicial_complex.py b/test/classes/test_simplicial_complex.py index ddf35667..57c0c5cb 100644 --- a/test/classes/test_simplicial_complex.py +++ b/test/classes/test_simplicial_complex.py @@ -244,7 +244,15 @@ def test_add_simplex(self): assert (5,) in SC.simplices assert (6,) in SC.simplices - # call with unsupported type + # check that provided attributes are stored correctly + SC.add_simplex((1, 2), edge_flow=10) + assert SC[(1, 2)]["edge_flow"] == 10 + SC.add_simplex(Simplex((1, 2), edge_flow=20)) + assert SC[(1, 2)]["edge_flow"] == 20 + SC.add_simplex(Simplex((2, 3), a=1, b=2), b=5) + assert SC[(2, 3)]["a"] == 1 + assert SC[(2, 3)]["b"] == 5 + with pytest.raises(TypeError): SC.add_simplex(iter([1, 2])) diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index e8d2b666..7e0d9871 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -554,6 +554,10 @@ def add_simplex(self, simplex: Collection, **kwargs) -> None: ---------- simplex : Collection The simplex to be added to the simplicial complex. + + If a `Simplex` object is given, its attributes will be copied to the + simplicial complex. `kwargs` take precedence over the attributes of the + `Simplex` object. **kwargs : keyword arguments, optional Additional attributes to be associated with the simplex. """ @@ -568,7 +572,7 @@ def add_simplex(self, simplex: Collection, **kwargs) -> None: if isinstance(simplex, Simplex): elements = simplex.elements - kwargs.update(simplex._attributes) + kwargs = simplex._attributes | kwargs elif isinstance(simplex, Collection): elements = frozenset(simplex) if len(elements) != len(simplex): From 3a49c7c15181dc7750353f1c0c298ffbb5adb8cc Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 24 May 2024 16:22:28 +0200 Subject: [PATCH 30/53] New import convention for TopoNetX --- README.md | 23 +- conftest.py | 31 +++ toponetx/__init__.py | 12 +- toponetx/algorithms/components.py | 35 +-- toponetx/algorithms/distance.py | 17 +- toponetx/algorithms/distance_measures.py | 31 +-- toponetx/algorithms/spectrum.py | 40 ++- toponetx/classes/cell.py | 8 +- toponetx/classes/cell_complex.py | 162 +++++------- toponetx/classes/colored_hypergraph.py | 30 +-- toponetx/classes/combinatorial_complex.py | 22 +- toponetx/classes/hyperedge.py | 9 +- toponetx/classes/path.py | 12 +- toponetx/classes/path_complex.py | 36 +-- toponetx/classes/reportviews.py | 6 +- toponetx/classes/simplex.py | 12 +- toponetx/classes/simplicial_complex.py | 89 ++++--- toponetx/readwrite/atomlist.py | 11 +- tutorials/01_simplicial_complexes.ipynb | 287 ++------------------- tutorials/02_cell_complexes.ipynb | 145 ++--------- tutorials/03_combinatorial_complexes.ipynb | 8 +- tutorials/04_colored_hypergraphs.ipynb | 6 +- 22 files changed, 337 insertions(+), 695 deletions(-) create mode 100644 conftest.py diff --git a/README.md b/README.md index bd8313e2..31426324 100644 --- a/README.md +++ b/README.md @@ -85,64 +85,53 @@ pre-commit install ## Example 1: creating a simplicial complex ```python -from toponetx.classes import SimplicialComplex +import toponetx as tnx # Instantiate a SimplicialComplex object with a few simplices - -sc = SimplicialComplex([[1, 2, 3], [2, 3, 4], [0, 1]]) +sc = tnx.SimplicialComplex([[1, 2, 3], [2, 3, 4], [0, 1]]) # Compute the incidence matrix between 1-skeleton and 0-skeleton - B1 = sc.incidence_matrix(1) # Compute the incidence matrix between 2-skeleton and 1-skeleton - B2 = sc.incidence_matrix(2) ``` ## Example 2: creating a cell complex ```python -from toponetx.classes import CellComplex +import toponetx as tnx # Instantiate a CellComplex object with a few cells - -cx = CellComplex([[1, 2, 3, 4], [3, 4, 5, 6, 7, 8]], ranks=2) +cx = tnx.CellComplex([[1, 2, 3, 4], [3, 4, 5, 6, 7, 8]], ranks=2) # Add an edge (cell of rank 1) after initialization - cx.add_edge(0, 1) # Compute the Hodge Laplacian matrix of dimension 1 - L1 = cx.hodge_laplacian_matrix(1) # Compute the Hodge Laplacian matrix of dimension 2 - L2 = cx.hodge_laplacian_matrix(2) ``` ## Example 3: creating a combinatorial complex ```python -from toponetx.classes import CombinatorialComplex +import toponetx as tnx # Instantiate a combinatorial complex object with a few cells - -cc = CombinatorialComplex() +cc = tnx.CombinatorialComplex() # Add some cells of different ranks after initialization - cc.add_cell([1, 2, 3], rank=2) cc.add_cell([3, 4, 5], rank=2) cc.add_cells_from([[2, 3, 4, 5], [3, 4, 5, 6, 7]], ranks=3) # Compute the incidence matrix between cells of rank 0 and 2 - B02 = cc.incidence_matrix(0, 2) # Compute the incidence matrix between cells of rank 0 and 3 - B03 = cc.incidence_matrix(0, 3) ``` diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..5f78ae99 --- /dev/null +++ b/conftest.py @@ -0,0 +1,31 @@ +"""Configure our testing suite.""" + +import networkx +import numpy +import pytest + +import toponetx + + +@pytest.fixture(autouse=True) +def doctest_default_imports(doctest_namespace): + """Add default imports to the doctest namespace. + + This fixture adds the following default imports to every doctest, so that their use + is consistent across all doctests without boilerplate imports polluting the + doctests themselves: + + .. code-block:: python + + import numpy as np + import networkx as nx + import toponetx as tnx + + Parameters + ---------- + doctest_namespace : dict + The namespace of the doctest. + """ + doctest_namespace["np"] = numpy + doctest_namespace["nx"] = networkx + doctest_namespace["tnx"] = toponetx diff --git a/toponetx/__init__.py b/toponetx/__init__.py index 08eb00d0..6e1c207c 100644 --- a/toponetx/__init__.py +++ b/toponetx/__init__.py @@ -1,4 +1,14 @@ """Initialize the library with modules and other content.""" + __version__ = "0.0.2" -__all__ = ["algorithms", "classes", "datasets", "generators", "transform", "utils"] +from toponetx.algorithms import * +from toponetx.classes import * +from toponetx.exception import * +from toponetx.generators import * +from toponetx.readwrite import * +from toponetx.transform import * +from toponetx.utils import * + +# Do not import the contents of the following modules into the global namespace: +# from toponetx.datasets import * diff --git a/toponetx/algorithms/components.py b/toponetx/algorithms/components.py index 93d04811..f9a94f6f 100644 --- a/toponetx/algorithms/components.py +++ b/toponetx/algorithms/components.py @@ -1,4 +1,5 @@ """Module to compute connected components on topological domains.""" + from collections.abc import Generator, Hashable from typing import Literal, TypeVar, overload @@ -87,20 +88,20 @@ def s_connected_components( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) - >>> list(s_connected_components(CC, s=1, cells=False)) + >>> list(tnx.s_connected_components(CC, s=1, cells=False)) [{2, 3, 4}, {5, 6, 7}] - >>> list(s_connected_components(CC, s=1, cells=True)) + >>> list(tnx.s_connected_components(CC, s=1, cells=True)) [{(2, 3), (2, 3, 4), (2, 4), (3, 4)}, {(5, 6), (5, 6, 7), (5, 7), (6, 7)}] >>> CHG = CC.to_colored_hypergraph() - >>> list(s_connected_components(CHG, s=1, cells=False)) + >>> list(tnx.s_connected_components(CHG, s=1, cells=False)) >>> CC.add_cell([4, 5], rank=1) - >>> list(s_connected_components(CC, s=1, cells=False)) + >>> list(tnx.s_connected_components(CC, s=1, cells=False)) [{2, 3, 4, 5, 6, 7}] >>> CCC = CC.to_combinatorial_complex() - >>> list(s_connected_components(CCC, s=1, cells=False)) + >>> list(tnx.s_connected_components(CCC, s=1, cells=False)) """ if cells: cell_dict, A = domain.all_cell_to_node_coadjacency_matrix(s=s, index=True) @@ -160,16 +161,16 @@ def s_component_subcomplexes( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) - >>> list(s_component_subcomplexes(CC, 1, cells=False)) + >>> list(tnx.s_component_subcomplexes(CC, 1, cells=False)) >>> CCC = CC.to_combinatorial_complex() - >>> list(s_component_subcomplexes(CCC, s=1, cells=False)) + >>> list(tnx.s_component_subcomplexes(CCC, s=1, cells=False)) >>> CHG = CC.to_colored_hypergraph() - >>> list(s_component_subcomplexes(CHG, s=1, cells=False)) + >>> list(tnx.s_component_subcomplexes(CHG, s=1, cells=False)) >>> CC.add_cell([4, 5], rank=1) - >>> list(s_component_subcomplexes(CC, s=1, cells=False)) + >>> list(tnx.s_component_subcomplexes(CC, s=1, cells=False)) """ for c in s_connected_components( domain, s=s, cells=cells, return_singletons=return_singletons @@ -232,12 +233,12 @@ def connected_components( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) - >>> list(connected_components(CC, cells=False)) + >>> list(tnx.connected_components(CC, cells=False)) >>> CC.add_cell([4, 5], rank=1) - >>> list(CC.connected_components(CC, cells=False)) + >>> list(tnx.CC.connected_components(CC, cells=False)) """ yield from s_connected_components( domain, s=1, cells=cells, return_singletons=return_singletons @@ -270,11 +271,11 @@ def connected_component_subcomplexes( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) - >>> list(connected_component_subcomplexes(CC)) + >>> list(tnx.connected_component_subcomplexes(CC)) >>> CC.add_cell([4, 5], rank=1) - >>> list(connected_component_subcomplexes(CC)) + >>> list(tnx.connected_component_subcomplexes(CC)) """ yield from s_component_subcomplexes(domain, return_singletons=return_singletons) diff --git a/toponetx/algorithms/distance.py b/toponetx/algorithms/distance.py index a69fb606..75d20041 100644 --- a/toponetx/algorithms/distance.py +++ b/toponetx/algorithms/distance.py @@ -1,4 +1,5 @@ """Module to compute distance between nodes or cells on topological domains.""" + from collections.abc import Hashable, Iterable import networkx as nx @@ -61,14 +62,14 @@ def distance( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) - >>> list(node_diameters(CC)) + >>> list(tnx.node_diameters(CC)) >>> CCC = CC.to_combinatorial_complex() - >>> list(node_diameters(CCC)) + >>> list(tnx.node_diameters(CCC)) >>> CHG = CC.to_colored_hypergraph() - >>> list(node_diameters(CHG)) + >>> list(tnx.node_diameters(CHG)) """ if not isinstance(domain, CellComplex | CombinatorialComplex | ColoredHyperGraph): raise TypeError(f"Input complex {domain} is not supported.") @@ -137,15 +138,15 @@ def cell_distance( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) >>> CC.add_cell([5, 2], rank=1) - >>> cell_distance(CC, [2, 3], [6, 7]) + >>> tnx.cell_distance(CC, [2, 3], [6, 7]) >>> CHG = CC.to_colored_hypergraph() - >>> cell_distance(CHG, (frozenset({2, 3}), 0), (frozenset({6, 7}), 0)) + >>> tnx.cell_distance(CHG, (frozenset({2, 3}), 0), (frozenset({6, 7}), 0)) >>> CCC = CC.to_combinatorial_complex() - >>> cell_distance(CCC, frozenset({2, 3}), frozenset({6, 7})) + >>> tnx.cell_distance(CCC, frozenset({2, 3}), frozenset({6, 7})) """ if not isinstance(domain, CellComplex | CombinatorialComplex | ColoredHyperGraph): raise TypeError(f"Input complex {domain} is not supported.") diff --git a/toponetx/algorithms/distance_measures.py b/toponetx/algorithms/distance_measures.py index 7d3e6f30..6247dbce 100644 --- a/toponetx/algorithms/distance_measures.py +++ b/toponetx/algorithms/distance_measures.py @@ -1,4 +1,5 @@ """Module to distance measures on topological domains.""" + from collections.abc import Hashable import networkx as nx @@ -31,14 +32,14 @@ def node_diameters(domain: ComplexType) -> tuple[list[int], list[set[Hashable]]] Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) - >>> list(node_diameters(CC)) + >>> tnx.node_diameters(CC) >>> CCC = CC.to_combinatorial_complex() - >>> list(node_diameters(CCC)) + >>> tnx.node_diameters(CCC) >>> CHG = CC.to_colored_hypergraph() - >>> list(node_diameters(CHG)) + >>> tnx.node_diameters(CHG) """ node_dict, A = domain.node_to_all_cell_adjacnecy_matrix(index=True) node_dict = {v: k for k, v in node_dict.items()} @@ -79,11 +80,11 @@ def cell_diameters(domain: ComplexType, s: int = 1) -> tuple[list[int], list[set >>> CC = CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) - >>> list(cell_diameters(CC)) + >>> tnx.cell_diameters(CC) >>> CCC = CC.to_combinatorial_complex() - >>> list(cell_diameters(CCC)) + >>> tnx.cell_diameters(CCC) >>> CHG = CC.to_colored_hypergraph() - >>> list(cell_diameters(CHG)) + >>> tnx.cell_diameters(CHG) """ if not isinstance(domain, CellComplex | CombinatorialComplex | ColoredHyperGraph): raise TypeError(f"Input complex {domain} is not supported.") @@ -130,15 +131,15 @@ def diameter(domain: ComplexType) -> int: Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) >>> CC.add_cell([2, 5], rank=2) - >>> diameter(CC) + >>> tnx.diameter(CC) >>> CCC = CC.to_combinatorial_complex() - >>> diameter(CCC) + >>> tnx.diameter(CCC) >>> CHG = CC.to_colored_hypergraph() - >>> diameter(CHG) + >>> tnx.diameter(CHG) """ if not isinstance(domain, CellComplex | CombinatorialComplex | ColoredHyperGraph): raise TypeError(f"Input complex {domain} is not supported.") @@ -178,15 +179,15 @@ def cell_diameter(domain: ComplexType, s: int | None = None) -> int: Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([2, 3, 4], rank=2) >>> CC.add_cell([5, 6, 7], rank=2) >>> CC.add_cell([2, 5], rank=1) - >>> cell_diameter(CC) + >>> tnx.cell_diameter(CC) >>> CCC = CC.to_combinatorial_complex() - >>> cell_diameter(CCC) + >>> tnx.cell_diameter(CCC) >>> CHG = CC.to_colored_hypergraph() - >>> cell_diameter(CHG) + >>> tnx.cell_diameter(CHG) """ if not isinstance(domain, CellComplex | CombinatorialComplex | ColoredHyperGraph): raise TypeError(f"Input complex {domain} is not supported.") diff --git a/toponetx/algorithms/spectrum.py b/toponetx/algorithms/spectrum.py index e6dae5be..23906484 100644 --- a/toponetx/algorithms/spectrum.py +++ b/toponetx/algorithms/spectrum.py @@ -1,4 +1,5 @@ """Module to compute spectra.""" + from typing import Any, Literal import numpy as np @@ -70,10 +71,9 @@ def hodge_laplacian_eigenvectors( Examples -------- - >>> from toponetx.classes import SimplicialComplex - >>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) + >>> SC = tnx.SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) >>> L1 = SC.hodge_laplacian_matrix(1) - >>> vals, vecs = hodge_laplacian_eigenvectors(L1, 2) + >>> vals, vecs = tnx.hodge_laplacian_eigenvectors(L1, 2) """ Diag = diags(hodge_laplacian.diagonal()) if Diag.shape[0] > 10: @@ -117,9 +117,8 @@ def set_hodge_laplacian_eigenvector_attrs( Examples -------- - >>> from toponetx.classes import SimplicialComplex - >>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) - >>> set_hodge_laplacian_eigenvector_attrs(SC, 1, 2, "down") + >>> SC = tnx.SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) + >>> tnx.set_hodge_laplacian_eigenvector_attrs(SC, 1, 2, "down") >>> SC.get_simplex_attributes("0.th_eigen", 1) """ index = SC.skeleton(dim) @@ -163,8 +162,8 @@ def laplacian_beltrami_eigenvectors( Examples -------- - >>> SC = stanford_bunny() - >>> eigenvectors, eigenvalues = laplacian_beltrami_eigenvectors(SC) + >>> SC = tnx.datasets.stanford_bunny("simplicial") + >>> eigenvectors, eigenvalues = tnx.laplacian_beltrami_eigenvectors(SC) """ mesh = SC.to_spharapy() sphara_basis = sb.SpharaBasis(mesh, mode=mode) @@ -182,9 +181,8 @@ def set_laplacian_beltrami_eigenvectors(SC: SimplicialComplex) -> None: Examples -------- - >>> from toponetx.classes import SimplicialComplex - >>> SC = SimplicialComplex.load_mesh("bunny.obj") - >>> set_laplacian_beltrami_eigenvectors(SC) + >>> SC = tnx.datasets.stanford_bunny() + >>> tnx.set_laplacian_beltrami_eigenvectors(SC) >>> vec1 = SC.get_simplex_attributes("1.laplacian_beltrami_eigenvectors") """ index = SC.skeleton(0) @@ -231,12 +229,11 @@ def cell_complex_hodge_laplacian_spectrum( Examples -------- - >>> from toponetx.classes import CellComplex - >>> CC = CellComplex() + >>> CC = tnx.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) - >>> cell_complex_hodge_laplacian_spectrum(CC, 1) + >>> tnx.cell_complex_hodge_laplacian_spectrum(CC, 1) """ return laplacian_spectrum(CC.hodge_laplacian_matrix(rank=rank, weight=weight)) @@ -262,9 +259,8 @@ def simplicial_complex_hodge_laplacian_spectrum( Examples -------- - >>> from toponetx.classes import SimplicialComplex - >>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) - >>> spectrum = simplicial_complex_hodge_laplacian_spectrum(SC, 1) + >>> SC = tnx.SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) + >>> spectrum = tnx.simplicial_complex_hodge_laplacian_spectrum(SC, 1) """ return laplacian_spectrum(SC.hodge_laplacian_matrix(rank=rank)) @@ -314,12 +310,11 @@ def cell_complex_adjacency_spectrum(CC: CellComplex, rank: int): Examples -------- - >>> from toponetx.classes import CellComplex - >>> CC = CellComplex() + >>> CC = tnx.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) - >>> cell_complex_adjacency_spectrum(CC, 1) + >>> tnx.cell_complex_adjacency_spectrum(CC, 1) """ return laplacian_spectrum(CC.adjacency_matrix(rank=rank)) @@ -387,8 +382,7 @@ def combinatorial_complex_adjacency_spectrum( Examples -------- - >>> from toponetx.classes import CombinatorialComplex - >>> CCC = CombinatorialComplex(cells=[[1, 2, 3], [2, 3], [0]], ranks=[2, 1, 0]) - >>> s = laplacian_spectrum(CCC.adjacency_matrix(0, 2)) + >>> CCC = tnx.CombinatorialComplex(cells=[[1, 2, 3], [2, 3], [0]], ranks=[2, 1, 0]) + >>> tnx.combinatorial_complex_adjacency_spectrum(CCC, 0, 2) """ return laplacian_spectrum(CCC.adjacency_matrix(rank, via_rank)) diff --git a/toponetx/classes/cell.py b/toponetx/classes/cell.py index 9e557a08..06ee0ba2 100644 --- a/toponetx/classes/cell.py +++ b/toponetx/classes/cell.py @@ -42,16 +42,16 @@ class Cell(Atom[tuple[Hashable]]): Examples -------- - >>> cell1 = Cell((1, 2, 3)) - >>> cell2 = Cell((1, 2, 4, 5), weight=1) - >>> cell3 = Cell(("a", "b", "c")) + >>> cell1 = tnx.Cell((1, 2, 3)) + >>> cell2 = tnx.Cell((1, 2, 4, 5), weight=1) + >>> cell3 = tnx.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 = tnx.Cell([v0, v1, v2, v3], type="square") >>> cell["type"] >>> list(cell.boundary) [((0, 0), (1, 0)), ((1, 0), (1, 1)), ((1, 1), (0, 1)), diff --git a/toponetx/classes/cell_complex.py b/toponetx/classes/cell_complex.py index cf2e2f27..7046ac55 100644 --- a/toponetx/classes/cell_complex.py +++ b/toponetx/classes/cell_complex.py @@ -95,8 +95,7 @@ class CellComplex(Complex): -------- Iteratively construct a cell complex: - >>> from toponetx.classes import CellComplex - >>> CC = CellComplex() + >>> CC = tnx.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 @@ -105,32 +104,29 @@ class CellComplex(Complex): 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]) + >>> c1 = tnx.Cell((1, 2, 3)) # a cell here is always assumed to be 2d + >>> c2 = tnx.Cell((1, 2, 3, 4)) + >>> CC = tnx.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 = tnx.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 = tnx.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) + >>> c1 = tnx.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 @@ -278,13 +274,14 @@ def is_regular(self) -> bool: Examples -------- - >>> CC = CellComplex(regular=False) + >>> CC = tnx.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) + >>> c1 = tnx.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 + False """ return all(cell.is_regular for cell in self.cells) @@ -417,7 +414,7 @@ def _cell_equivalence_class(self) -> dict[Cell, set[Cell]]: Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell((1, 2, 3, 4), rank=2) >>> CC.add_cell((2, 3, 4, 1), rank=2) >>> CC.add_cell((1, 2, 3, 4), rank=2) @@ -425,7 +422,7 @@ def _cell_equivalence_class(self) -> dict[Cell, set[Cell]]: >>> CC.add_cell((3, 4, 1, 2), rank=2) >>> CC.add_cell((4, 3, 2, 1), rank=2) >>> CC.add_cell((1, 2, 7, 3), rank=2) - >>> c1 = Cell((1, 2, 3, 4, 5)) + >>> c1 = tnx.Cell((1, 2, 3, 4, 5)) >>> CC.add_cell(c1, rank=2) >>> CC._cell_equivalence_class() """ @@ -449,7 +446,7 @@ def _remove_equivalent_cells(self) -> None: Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell((1, 2, 3, 4), rank=2) >>> CC.add_cell((2, 3, 4, 1), rank=2) >>> CC.add_cell((1, 2, 3, 4), rank=2) @@ -457,7 +454,7 @@ def _remove_equivalent_cells(self) -> None: >>> CC.add_cell((3, 4, 1, 2), rank=2) >>> CC.add_cell((4, 3, 2, 1), rank=2) >>> CC.add_cell((1, 2, 7, 3), rank=2) - >>> c1 = Cell((1, 2, 3, 4, 5)) + >>> c1 = tnx.Cell((1, 2, 3, 4, 5)) >>> CC.add_cell(c1, rank=2) >>> CC._remove_equivalent_cells() >>> CC @@ -747,8 +744,8 @@ def add_cell( Examples -------- - >>> CC = CellComplex() - >>> c1 = Cell((2, 3, 4), color="black") + >>> CC = tnx.CellComplex() + >>> c1 = tnx.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") @@ -896,7 +893,7 @@ def set_filtration( Examples -------- >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.CellComplex(G) >>> d = {0: 1, 1: 0, 2: 2, (0, 1): 1, (1, 2): 3} >>> CC.set_filtration(d, "f") """ @@ -938,7 +935,7 @@ def get_filtration(self, name: str) -> dict[Hashable, Any]: Examples -------- >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.CellComplex(G) >>> d = {0: 1, 1: 0, 2: 2, (0, 1): 1, (1, 2): 3} >>> CC.set_filtration(d, "f") >>> CC.get_filtration("f") @@ -970,7 +967,7 @@ def set_node_attributes( Examples -------- >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.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) @@ -995,7 +992,7 @@ def get_node_attributes(self, name: str) -> dict[tuple, Any]: Examples -------- >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.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) @@ -1023,7 +1020,7 @@ def set_edge_attributes( Examples -------- >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.CellComplex(G) >>> CC.add_cell([1, 2, 3, 4], rank=2) >>> d = { ... (1, 2): {"color": "red", "attr2": 1}, @@ -1051,7 +1048,7 @@ def get_edge_attributes(self, name: str) -> dict[tuple, Any]: Examples -------- >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.CellComplex(G) >>> CC.add_cell([1, 2, 3, 4], rank=2) >>> d = { ... (1, 2): {"color": "red", "attr2": 1}, @@ -1095,12 +1092,9 @@ def set_cell_attributes( to assign a cell attribute to store the value of that property for each cell: - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([1, 2, 3, 4], rank=2) - >>> CC.add_cell( - ... [1, 2, 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) @@ -1111,13 +1105,10 @@ def set_cell_attributes( the entire dictionary will be used to update cell attributes:: >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.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([1, 2, 4], rank=2) >>> CC.add_cell([3, 4, 8], rank=2) >>> d = { ... (1, 2, 3, 4): {"color": "red", "attr2": 1}, @@ -1196,14 +1187,12 @@ def get_cell_attributes( 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 = tnx.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) @@ -1341,17 +1330,12 @@ def remove_equivalent_cells(self) -> None: Examples -------- - >>> import networkx as nx - >>> from toponetx.classes import CellComplex >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.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([1, 2, 4], rank=2) >>> CC.add_cell([3, 4, 8], rank=2) >>> print(CC.cells) >>> CC.remove_equivalent_cells() @@ -1474,7 +1458,7 @@ def node_to_all_cell_adjacnecy_matrix( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.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() @@ -1533,7 +1517,7 @@ def all_cell_to_node_coadjacency_matrix( Examples -------- - >>> CX = CellComplex([[1, 2, 3, 4], [2, 3, 6]]) + >>> CX = tnx.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) @@ -1580,7 +1564,7 @@ def incidence_matrix( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([1, 2, 3, 4], rank=2) >>> CC.add_cell([3, 4, 5], rank=2) >>> B0 = CC.incidence_matrix(0) @@ -1592,16 +1576,12 @@ def incidence_matrix( 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 = tnx.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([1, 2, 4], rank=2) >>> CC.add_cell([3, 4, 8], rank=2) >>> B1 = CC.incidence_matrix(1) >>> B2 = CC.incidence_matrix(2) @@ -1609,7 +1589,7 @@ def incidence_matrix( Non-regular cell complex example: - >>> CC = CellComplex(regular=False) + >>> CC = tnx.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) @@ -1617,7 +1597,7 @@ def incidence_matrix( >>> print(B2.todense()) # observe the non-unit entries >>> B1.dot(B2).todense() - >>> CC = CellComplex() + >>> CC = tnx.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) @@ -1734,7 +1714,7 @@ def hodge_laplacian_matrix( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([1, 2, 3, 4], rank=2) >>> CC.add_cell([3, 4, 5], rank=2) >>> CC.hodge_laplacian_matrix(1) @@ -1824,12 +1804,12 @@ def up_laplacian_matrix( Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.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 = tnx.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) @@ -1886,9 +1866,8 @@ def down_laplacian_matrix( Examples -------- - >>> import networkx as nx >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.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) @@ -2021,9 +2000,8 @@ def dirac_operator_matrix( Examples -------- - >>> import networkx as nx >>> G = nx.path_graph(3) - >>> CC = CellComplex(G) + >>> CC = tnx.CellComplex(G) >>> CC.add_cell([1, 2, 3, 4], rank=2) >>> CC.dirac_operator_matrix() """ @@ -2072,15 +2050,14 @@ def restrict_to_cells( Examples -------- - >>> CC = CellComplex() - >>> c1 = Cell((1, 2, 3)) - >>> c2 = Cell((1, 2, 4)) - >>> c3 = Cell((1, 2, 5)) - >>> CC = CellComplex([c1, c2, c3]) + >>> c1 = tnx.Cell((1, 2, 3)) + >>> c2 = tnx.Cell((1, 2, 4)) + >>> c3 = tnx.Cell((1, 2, 5)) + >>> CC = tnx.CellComplex([c1, c2, c3]) >>> CC.add_edge(1, 0) >>> cx1 = CC.restrict_to_cells([c1, (0, 1)]) >>> cx1.cells - CellView([Cell(1, 2, 3)]) + CellView([Cell((1, 2, 3))]) """ CC = CellComplex(cells=self._G.copy()) @@ -2138,11 +2115,10 @@ def restrict_to_nodes(self, node_set: Iterable[Hashable]): Examples -------- - >>> CC = CellComplex() - >>> c1 = Cell((1, 2, 3)) - >>> c2 = Cell((1, 2, 4)) - >>> c3 = Cell((1, 2, 5)) - >>> CC = CellComplex([c1, c2, c3]) + >>> c1 = tnx.Cell((1, 2, 3)) + >>> c2 = tnx.Cell((1, 2, 4)) + >>> c3 = tnx.Cell((1, 2, 5)) + >>> CC = tnx.CellComplex([c1, c2, c3]) >>> CC.add_edge(1, 0) >>> CC.restrict_to_nodes([1, 2, 3, 0]) """ @@ -2169,7 +2145,7 @@ def to_combinatorial_complex(self): Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.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) @@ -2201,7 +2177,7 @@ def to_colored_hypergraph(self): Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.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) @@ -2229,12 +2205,11 @@ def to_hypergraph(self): Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.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 + >>> CC.to_hypergraph() """ from hypernetx.classes.entity import EntitySet @@ -2289,6 +2264,7 @@ def singletons(self): >>> CC.add_node(0) >>> CC.add_node(10) >>> CC.singletons() + [0, 10] """ return [node for node in self.nodes if self.degree(node) == 0] @@ -2302,12 +2278,12 @@ def clone(self) -> Self: Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.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() + >>> CC.clone() """ _G = self._G.copy() CC = self.__class__(_G) @@ -2355,9 +2331,9 @@ def get_linegraph(self, s: int = 1, cells: bool = True) -> nx.Graph: Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([0, 1, 2, 3, 4], rank=2) - >>> G = CC.get_linegraph() + >>> CC.get_linegraph() """ if not isinstance(s, int) or s < 1: raise ValueError(f"'s' must be a positive integer, got {s}.") @@ -2382,9 +2358,9 @@ def to_hasse_graph(self) -> nx.DiGraph: Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell([1, 2, 3, 4], rank=2) - >>> G = CC.to_hasse_graph() + >>> CC.to_hasse_graph() """ G = nx.DiGraph() for n in self.nodes: @@ -2413,9 +2389,9 @@ def from_networkx_graph(self, G: nx.Graph) -> None: Examples -------- - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cells_from([[1, 2, 4], [1, 2, 7]], rank=2) - >>> G = Graph([(0, 1), (0, 2), (1, 2)]) + >>> G = nx.Graph([(0, 1), (0, 2), (1, 2)]) >>> CC.from_networkx_graph(G) >>> CC.edges """ @@ -2441,10 +2417,12 @@ def from_trimesh(cls, mesh) -> Self: 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) + >>> mesh = trimesh.Trimesh( + ... vertices=[[0, 0, 0], [0, 0, 1], [0, 1, 0]], + ... faces=[[0, 1, 2]], + ... process=False, + ... ) + >>> CC = tnx.CellComplex.from_trimesh(mesh) >>> CC[0]["position"] """ CC = cls(mesh.faces) @@ -2487,7 +2465,7 @@ def load_mesh(cls, file_path, process: bool = False) -> Self: Examples -------- - >>> CC = CellComplex.load_mesh("bunny.obj") + >>> CC = tnx.CellComplex.load_mesh("bunny.obj") """ import trimesh diff --git a/toponetx/classes/colored_hypergraph.py b/toponetx/classes/colored_hypergraph.py index 049c4001..c695cb46 100644 --- a/toponetx/classes/colored_hypergraph.py +++ b/toponetx/classes/colored_hypergraph.py @@ -50,12 +50,11 @@ class ColoredHyperGraph(Complex): -------- Define an empty colored hypergraph: - >>> CHG = ColoredHyperGraph() + >>> CHG = tnx.ColoredHyperGraph() Add cells to the colored hypergraph: - >>> from toponetx.classes.colored_hypergraph import ColoredHyperGraph - >>> CHG = ColoredHyperGraph() + >>> CHG = tnx.ColoredHyperGraph() >>> CHG.add_cell([1, 2], rank=1) >>> CHG.add_cell([3, 4], rank=1) >>> CHG.add_cell([1, 2, 3, 4], rank=2) @@ -64,7 +63,7 @@ class ColoredHyperGraph(Complex): Create a Colored Hypergraph and add groups of friends with corresponding ranks: - >>> CHG = ColoredHyperGraph() + >>> CHG = tnx.ColoredHyperGraph() >>> CHG.add_cell( ... ["Alice", "Bob"], rank=1 ... ) # Alice and Bob are in a close-knit group. @@ -727,7 +726,7 @@ def set_cell_attributes(self, values, name: str | None = None) -> None: to assign a cell attribute to store the value of that property for each cell: - >>> CHG = ColoredHyperGraph() + >>> CHG = tnx.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) @@ -740,7 +739,7 @@ def set_cell_attributes(self, values, name: str | None = None) -> None: the entire dictionary will be used to update edge attributes: >>> G = nx.path_graph(3) - >>> CHG = ColoredHyperGraph(G) + >>> CHG = tnx.ColoredHyperGraph(G) >>> d = { ... ((1, 2), 0): {"color": "red", "attr2": 1}, ... ((0, 1), 0): {"color": "blue", "attr2": 3}, @@ -748,7 +747,6 @@ def set_cell_attributes(self, values, name: str | None = None) -> None: >>> 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. @@ -779,7 +777,7 @@ def get_node_attributes(self, name: str): Examples -------- >>> G = nx.path_graph(3) - >>> CHG = ColoredHyperGraph(G) + >>> CHG = tnx.ColoredHyperGraph(G) >>> d = {0: {"color": "red", "attr2": 1}, 1: {"color": "blue", "attr2": 3}} >>> CHG.set_node_attributes(d) >>> CHG.get_node_attributes("color") @@ -787,7 +785,7 @@ def get_node_attributes(self, name: str): >>> G = nx.Graph() >>> G.add_nodes_from([1, 2, 3], color="blue") - >>> CHG = ColoredHyperGraph(G) + >>> CHG = tnx.ColoredHyperGraph(G) >>> nodes_color = CHG.get_node_attributes("color") >>> nodes_color[1] 'blue' @@ -817,7 +815,7 @@ def get_cell_attributes(self, name: str, rank=None): Examples -------- >>> G = nx.path_graph(3) - >>> CHG = ColoredHyperGraph(G) + >>> CHG = tnx.ColoredHyperGraph(G) >>> d = { ... ((1, 2), 0): {"color": "red", "attr2": 1}, ... ((0, 1), 0): {"color": "blue", "attr2": 3}, @@ -1251,14 +1249,12 @@ def adjacency_matrix(self, rank, via_rank, s: int = 1, index: bool = False): 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 = tnx.ColoredHyperGraph(cells=G) >>> CHG.adjacency_matrix(0, 1) """ if index: @@ -1449,7 +1445,7 @@ def from_trimesh(cls, mesh: trimesh.Trimesh): ... faces=[[0, 1, 2]], ... process=False, ... ) - >>> CHG = ColoredHyperGraph.from_trimesh(mesh) + >>> CHG = tnx.ColoredHyperGraph.from_trimesh(mesh) >>> CHG.nodes """ raise NotImplementedError() @@ -1520,14 +1516,14 @@ def from_networkx_graph(self, G) -> None: Examples -------- - >>> from networkx import Graph - >>> G = Graph() + >>> G = nx.Graph() >>> G.add_edge(0, 1) >>> G.add_edge(0, 4) >>> G.add_edge(0, 7) - >>> CHG = ColoredHyperGraph() + >>> CHG = tnx.ColoredHyperGraph() >>> CHG.from_networkx_graph(G) >>> CHG.nodes + NodeView([(0,), (1,), (4,), (7,)]) """ for node in G.nodes: # cells is a networkx graph self.add_node(node, **G.nodes[node]) diff --git a/toponetx/classes/combinatorial_complex.py b/toponetx/classes/combinatorial_complex.py index 0ca1efa4..55ef1b2a 100644 --- a/toponetx/classes/combinatorial_complex.py +++ b/toponetx/classes/combinatorial_complex.py @@ -67,11 +67,11 @@ class CombinatorialComplex(ColoredHyperGraph): -------- Define an empty combinatorial complex: - >>> CCC = CombinatorialComplex() + >>> CCC = tnx.CombinatorialComplex() Add cells to the combinatorial complex: - >>> CCC = CombinatorialComplex() + >>> CCC = tnx.CombinatorialComplex() >>> CCC.add_cell([1, 2], rank=1) >>> CCC.add_cell([3, 4], rank=1) >>> CCC.add_cell([1, 2, 3, 4], rank=2) @@ -393,7 +393,7 @@ def set_cell_attributes(self, values, name: str | None = None) -> None: to assign a cell attribute to store the value of that property for each cell: - >>> CCC = CombinatorialComplex() + >>> CCC = tnx.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) @@ -406,7 +406,7 @@ def set_cell_attributes(self, values, name: str | None = None) -> None: the entire dictionary will be used to update edge attributes: >>> G = nx.path_graph(3) - >>> CCC = CombinatorialComplex(G) + >>> CCC = tnx.CombinatorialComplex(G) >>> d = { ... (1, 2): {"color": "red", "attr2": 1}, ... (0, 1): {"color": "blue", "attr2": 3}, @@ -437,7 +437,7 @@ def get_node_attributes(self, name: str) -> dict[Hashable, Any]: Examples -------- >>> G = nx.path_graph(3) - >>> CCC = CombinatorialComplex(G) + >>> CCC = tnx.CombinatorialComplex(G) >>> d = {0: {"color": "red", "attr2": 1}, 1: {"color": "blue", "attr2": 3}} >>> CCC.set_node_attributes(d) >>> CCC.get_node_attributes("color") @@ -445,7 +445,7 @@ def get_node_attributes(self, name: str) -> dict[Hashable, Any]: >>> G = nx.Graph() >>> G.add_nodes_from([1, 2, 3], color="blue") - >>> CCC = CombinatorialComplex(G) + >>> CCC = tnx.CombinatorialComplex(G) >>> nodes_color = CCC.get_node_attributes("color") >>> nodes_color[1] 'blue' @@ -470,7 +470,7 @@ def get_cell_attributes(self, name: str, rank: int | None = None): Examples -------- >>> G = nx.path_graph(3) - >>> CCC = CombinatorialComplex(G) + >>> CCC = tnx.CombinatorialComplex(G) >>> d = { ... (1, 2): {"color": "red", "attr2": 1}, ... (0, 1): {"color": "blue", "attr2": 3}, @@ -862,7 +862,7 @@ def adjacency_matrix(self, rank, via_rank, s: int = 1, index: bool = False): Examples -------- - >>> CCC = CombinatorialComplex() + >>> CCC = tnx.CombinatorialComplex() >>> CCC.add_cell([1, 2], rank=1) >>> CCC.add_cell([3, 4], rank=1) >>> CCC.add_cell([1, 2, 3, 4], rank=2) @@ -926,8 +926,7 @@ def dirac_operator_matrix(self, weight: str | None = None, index: bool = False): Examples -------- - >>> from toponetx.classes import CombinatorialComplex - >>> CCC = CombinatorialComplex() + >>> CCC = tnx.CombinatorialComplex() >>> CCC.add_cell([1, 2, 3, 4], rank=2) >>> CCC.add_cell([1, 2], rank=1) >>> CCC.add_cell([2, 3], rank=1) @@ -1108,11 +1107,12 @@ def singletons(self): Examples -------- - >>> CCC = CombinatorialComplex() + >>> CCC = tnx.CombinatorialComplex() >>> CCC.add_cell([1, 2], rank=1) >>> CCC.add_cell([3, 4], rank=1) >>> CCC.add_cell([9], rank=0) >>> CCC.singletons() + [frozenset({9})] """ return [k for k in self.skeleton(0) if self.degree(next(iter(k)), None) == 0] diff --git a/toponetx/classes/hyperedge.py b/toponetx/classes/hyperedge.py index dc00a42d..74eb7d40 100644 --- a/toponetx/classes/hyperedge.py +++ b/toponetx/classes/hyperedge.py @@ -1,6 +1,5 @@ """HyperEdge classes.""" - from collections.abc import Collection, Hashable from toponetx.classes.complex import Atom @@ -26,10 +25,10 @@ class HyperEdge(Atom[frozenset[Hashable]]): Examples -------- - >>> ac1 = HyperEdge((1, 2, 3)) - >>> ac2 = HyperEdge((1, 2, 4, 5)) - >>> ac3 = HyperEdge(("a", "b", "c")) - >>> ac3 = HyperEdge(("a", "b", "c"), rank=10) + >>> ac1 = tnx.HyperEdge((1, 2, 3)) + >>> ac2 = tnx.HyperEdge((1, 2, 4, 5)) + >>> ac3 = tnx.HyperEdge(("a", "b", "c")) + >>> ac3 = tnx.HyperEdge(("a", "b", "c"), rank=10) """ def __init__(self, elements: Collection, rank=None, **kwargs) -> None: diff --git a/toponetx/classes/path.py b/toponetx/classes/path.py index 1356a5f5..64f4bf76 100644 --- a/toponetx/classes/path.py +++ b/toponetx/classes/path.py @@ -59,18 +59,18 @@ class Path(Atom[tuple[Hashable]]): Examples -------- - >>> path1 = Path((1, 2, 3)) + >>> path1 = tnx.Path((1, 2, 3)) >>> list(path1.boundary) [] - >>> path2 = Path((1, 2, 3), construct_boundaries=True) + >>> path2 = tnx.Path((1, 2, 3), construct_boundaries=True) >>> list(path2.boundary) [(2, 3), (1, 3), (1, 2)] - >>> path3 = Path( + >>> path3 = tnx.Path( ... (1, 2, 3), construct_boundaries=True, allowed_paths=[(1, 2), (2, 3)] ... ) >>> list(path3.boundary) [(2, 3), (1, 2)] - >>> path4 = Path( + >>> path4 = tnx.Path( ... (1, 2, 3), construct_boundaries=True, allowed_paths=[(1, 3), (2, 3)] ... ) >>> list(path4.boundary) @@ -135,9 +135,9 @@ def construct_path_boundaries( Examples -------- - >>> Path.construct_path_boundaries((1, 2, 3), reserve_sequence_order=False) + >>> tnx.Path.construct_path_boundaries((1, 2, 3), reserve_sequence_order=False) [(2, 3), (1, 3), (1, 2)] - >>> Path.construct_path_boundaries( + >>> tnx.Path.construct_path_boundaries( ... (1, 2, 3), reserve_sequence_order=False, allowed_paths=[(1, 2), (2, 3)] ... ) [(2, 3), (1, 2)] diff --git a/toponetx/classes/path_complex.py b/toponetx/classes/path_complex.py index f1470b1c..8a582840 100644 --- a/toponetx/classes/path_complex.py +++ b/toponetx/classes/path_complex.py @@ -68,7 +68,7 @@ class PathComplex(Complex): Examples -------- - >>> PC = PathComplex([(1, 2, 3)]) + >>> PC = tnx.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)]) @@ -76,7 +76,7 @@ class PathComplex(Complex): 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 = tnx.PathComplex(G) >>> PC.paths PathView([(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 3, 2), (1, 2, 3), (2, 1, 3)]) """ @@ -159,7 +159,7 @@ def add_paths_from(self, paths: Iterable[Sequence[Hashable] | Path]) -> None: Examples -------- - >>> PC = PathComplex([(1, 2, 3)]) + >>> PC = tnx.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)]) @@ -188,7 +188,7 @@ def add_path(self, path: Hashable | Sequence[Hashable] | Path, **attr) -> None: Examples -------- - >>> PC = PathComplex([(1, 2, 3)]) + >>> PC = tnx.PathComplex([(1, 2, 3)]) >>> PC.paths PathView([(1,), (2,), (3,), (1, 2), (2, 3), (1, 2, 3)]) >>> PC.add_path((1, 2, 4)) @@ -707,7 +707,7 @@ def restrict_to_nodes(self, node_set: Iterable[Hashable]): Examples -------- - >>> PC = PathComplex( + >>> PC = tnx.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]) @@ -737,7 +737,7 @@ def restrict_to_paths(self, path_set: Iterable[Sequence[Hashable]]): Examples -------- - >>> PC = PathComplex( + >>> PC = tnx.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]]) @@ -766,7 +766,7 @@ def get_node_attributes(self, name: str) -> dict[tuple[Hashable], Any]: Examples -------- - >>> PC = PathComplex() + >>> PC = tnx.PathComplex() >>> PC.add_node(0) >>> PC.add_node(1, heat=55) >>> PC.add_node(2, heat=66) @@ -774,9 +774,9 @@ def get_node_attributes(self, name: str) -> dict[tuple[Hashable], Any]: >>> 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} + {(1,): 55, (2,): 66} >>> PC.get_node_attributes("color") - {(2, ): "blue", (3, ): "red"} + {(2,): 'blue', (3,): 'red'} """ return {tuple(n): self[n][name] for n in self.skeleton(0) if name in self[n]} @@ -796,7 +796,7 @@ def set_node_attributes( Examples -------- - >>> PC = PathComplex() + >>> PC = tnx.PathComplex() >>> PC.add_paths_from([[0, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3]]) >>> PC.set_node_attributes( ... { @@ -856,7 +856,7 @@ def get_edge_attributes(self, name: str) -> dict[tuple[Hashable], Any]: Examples -------- - >>> PC = PathComplex() + >>> PC = tnx.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) @@ -865,7 +865,7 @@ def get_edge_attributes(self, name: str) -> dict[tuple[Hashable], Any]: >>> PC.get_edge_attributes("weight") {(0, 1): 32, (1, 2): 98} >>> PC.get_edge_attributes("color") - {(1, 3): "red", (2, 3): "blue"} + {(1, 3): 'red', (2, 3): 'blue'} """ return {tuple(e): self[e][name] for e in self.skeleton(1) if name in self[e]} @@ -885,7 +885,7 @@ def set_edge_attributes( Examples -------- - >>> PC = PathComplex() + >>> PC = tnx.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) @@ -943,7 +943,7 @@ def get_path_attributes(self, name: str) -> dict[tuple[Hashable], Any]: Examples -------- - >>> PC = PathComplex() + >>> PC = tnx.PathComplex() >>> PC.add_paths_from([[0, 1]]) >>> PC.add_path([0, 1, 2], weight=43) >>> PC.add_path([0, 1, 3], weight=98) @@ -953,7 +953,7 @@ def get_path_attributes(self, name: str) -> dict[tuple[Hashable], Any]: >>> 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"} + {(1, 2, 3): 'red', (1, 3, 2): 'blue', (2, 1, 3): 'green'} """ return { tuple(p): self[p][name] @@ -978,7 +978,7 @@ def set_path_attributes( Examples -------- - >>> PC = PathComplex( + >>> PC = tnx.PathComplex( ... [[0, 1], [0, 1, 2], [0, 1, 3], [1, 2, 3], [1, 3, 2], [2, 1, 3]] ... ) >>> PC.set_path_attributes( @@ -990,7 +990,7 @@ def set_path_attributes( >>> 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"} + {(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} @@ -1193,7 +1193,7 @@ def compute_allowed_paths( -------- >>> 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 = tnx.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,)} """ diff --git a/toponetx/classes/reportviews.py b/toponetx/classes/reportviews.py index 02de1d2b..4dc80909 100644 --- a/toponetx/classes/reportviews.py +++ b/toponetx/classes/reportviews.py @@ -261,7 +261,7 @@ class ColoredHyperEdgeView(AtomView): Examples -------- - >>> hev = ColoredHyperEdgeView() + >>> hev = tnx.ColoredHyperEdgeView() """ def __init__(self) -> None: @@ -513,7 +513,7 @@ class HyperEdgeView(AtomView): Examples -------- - >>> hev = HyperEdgeView() + >>> hev = tnx.HyperEdgeView() """ def __init__(self) -> None: @@ -895,7 +895,7 @@ def __contains__(self, atom: Any) -> bool: -------- Check if a node is in the simplex view: - >>> view = SimplexView() + >>> view = tnx.SimplexView() >>> view.faces_dict.append({frozenset({1}): {"weight": 1}}) >>> view.max_dim = 0 >>> 1 in view diff --git a/toponetx/classes/simplex.py b/toponetx/classes/simplex.py index 98664d7e..9d5713e6 100644 --- a/toponetx/classes/simplex.py +++ b/toponetx/classes/simplex.py @@ -30,14 +30,14 @@ class Simplex(Atom[frozenset[Hashable]]): Examples -------- >>> # Create a 0-dimensional simplex (point) - >>> s = Simplex((1,)) + >>> s = tnx.Simplex((1,)) >>> # Create a 1-dimensional simplex (line segment) - >>> s = Simplex((1, 2)) + >>> s = tnx.Simplex((1, 2)) >>> # Create a 2-dimensional simplex (triangle) - >>> simplex1 = Simplex((1, 2, 3)) - >>> simplex2 = Simplex(("a", "b", "c")) + >>> simplex1 = tnx.Simplex((1, 2, 3)) + >>> simplex2 = tnx.Simplex(("a", "b", "c")) >>> # Create a 3-dimensional simplex (tetrahedron) - >>> simplex3 = Simplex((1, 2, 4, 5), weight=1) + >>> simplex3 = tnx.Simplex((1, 2, 4, 5), weight=1) """ def __init__( @@ -78,7 +78,7 @@ def __contains__(self, item: Any) -> bool: Examples -------- - >>> s = Simplex((1, 2, 3)) + >>> s = tnx.Simplex((1, 2, 3)) >>> 1 in s True >>> 4 in s diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index 7e0d9871..bc247b15 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -87,7 +87,7 @@ class SimplicialComplex(Complex): -------- Define a simplicial complex using a set of maximal simplices: - >>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) + >>> SC = tnx.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: @@ -95,7 +95,7 @@ class SimplicialComplex(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 = tnx.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)]) @@ -215,7 +215,7 @@ def is_maximal(self, simplex: Iterable) -> bool: Examples -------- - >>> SC = SimplicialComplex([[1, 2, 3]]) + >>> SC = tnx.SimplicialComplex([[1, 2, 3]]) >>> SC.is_maximal([1, 2, 3]) True >>> SC.is_maximal([1, 2]) @@ -450,7 +450,7 @@ def remove_maximal_simplex(self, simplex: Collection) -> None: Examples -------- - >>> SC = SimplicialComplex() + >>> SC = tnx.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)) @@ -505,7 +505,7 @@ def remove_nodes(self, node_set: Iterable[Hashable]) -> None: Examples -------- - >>> SC = SimplicialComplex([(1, 2), (2, 3), (3, 4)]) + >>> SC = tnx.SimplicialComplex([(1, 2), (2, 3), (3, 4)]) >>> SC.remove_nodes([1]) >>> SC.simplices SimplexView([(2,), (3,), (4,), (2, 3), (3, 4)]) @@ -678,7 +678,7 @@ def set_simplex_attributes(self, values, name: str | None = None) -> None: to assign a simplex attribute to store the value of that property for each simplex: - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([1, 2, 3, 4]) >>> SC.add_simplex([1, 2, 4]) >>> SC.add_simplex([3, 4, 8]) @@ -690,7 +690,7 @@ def set_simplex_attributes(self, values, name: str | None = None) -> None: If you provide a dictionary of dictionaries as the second argument, the entire dictionary will be used to update simplex attributes: - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([1, 3, 4]) >>> SC.add_simplex([1, 2, 3]) >>> SC.add_simplex([1, 2, 4]) @@ -727,7 +727,7 @@ def get_node_attributes(self, name: str) -> dict[Hashable, Any]: Examples -------- - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([1, 2, 3, 4]) >>> SC.add_simplex([1, 2, 4]) >>> SC.add_simplex([3, 4, 8]) @@ -756,7 +756,7 @@ def get_simplex_attributes( Examples -------- - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([1, 2, 3, 4]) >>> SC.add_simplex([1, 2, 4]) >>> SC.add_simplex([3, 4, 8]) @@ -837,7 +837,7 @@ def incidence_matrix( Examples -------- - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([1, 2, 3, 4]) >>> SC.add_simplex([1, 2, 4]) >>> SC.add_simplex([3, 4, 8]) @@ -967,7 +967,7 @@ def hodge_laplacian_matrix( Examples -------- - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([1, 2, 3, 4]) >>> SC.add_simplex([1, 2, 4]) >>> SC.add_simplex([3, 4, 8]) @@ -1036,8 +1036,7 @@ def dirac_operator_matrix( Examples -------- - >>> from toponetx.classes import SimplicialComplex - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([1, 2, 3, 4]) >>> SC.add_simplex([1, 2, 4]) >>> SC.add_simplex([3, 4, 8]) @@ -1106,7 +1105,7 @@ def normalized_laplacian_matrix(self, rank: int, weight: str | None = None): Examples -------- - >>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) + >>> SC = tnx.SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) >>> SC.normalized_laplacian_matrix(1) """ import scipy as sp @@ -1155,7 +1154,7 @@ def up_laplacian_matrix( Examples -------- - >>> SC = SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) + >>> SC = tnx.SimplicialComplex([[1, 2, 3], [2, 3, 5], [0, 1]]) >>> SC.up_laplacian_matrix(1) """ if weight is not None: @@ -1246,7 +1245,7 @@ def adjacency_matrix( Examples -------- - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([1, 2, 3, 4]) >>> SC.add_simplex([1, 2, 4]) >>> SC.add_simplex([3, 4, 8]) @@ -1333,10 +1332,10 @@ def restrict_to_simplices(self, cell_set) -> Self: Examples -------- - >>> c1 = Simplex((1, 2, 3)) - >>> c2 = Simplex((1, 2, 4)) - >>> c3 = Simplex((1, 2, 5)) - >>> SC = SimplicialComplex([c1, c2, c3]) + >>> c1 = tnx.Simplex((1, 2, 3)) + >>> c2 = tnx.Simplex((1, 2, 4)) + >>> c3 = tnx.Simplex((1, 2, 5)) + >>> SC = tnx.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)]) @@ -1361,10 +1360,10 @@ def restrict_to_nodes(self, node_set): Examples -------- - >>> c1 = Simplex((1, 2, 3)) - >>> c2 = Simplex((1, 2, 4)) - >>> c3 = Simplex((1, 2, 5)) - >>> SC = SimplicialComplex([c1, c2, c3]) + >>> c1 = tnx.Simplex((1, 2, 3)) + >>> c2 = tnx.Simplex((1, 2, 4)) + >>> c3 = tnx.Simplex((1, 2, 5)) + >>> SC = tnx.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)]) @@ -1392,7 +1391,7 @@ def get_all_maximal_simplices(self): Examples -------- - >>> SC = SimplicialComplex([(1, 2), (1, 2, 3), (1, 2, 4), (2, 5)]) + >>> SC = tnx.SimplicialComplex([(1, 2), (1, 2, 3), (1, 2, 4), (2, 5)]) >>> SC.get_all_maximal_simplices() [(2, 5), (1, 2, 3), (1, 2, 4)] """ @@ -1421,7 +1420,7 @@ def from_spharpy(cls, mesh) -> Self: ... [[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) + >>> SC = tnx.SimplicialComplex.from_spharpy(mesh) """ vertices = np.array(mesh.vertlist) SC = cls(mesh.trilist) @@ -1448,7 +1447,7 @@ def to_hasse_graph(self) -> nx.DiGraph: Examples -------- - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex([0, 1, 2]) >>> G = SC.to_hasse_graph() """ @@ -1481,7 +1480,7 @@ def from_gudhi(cls, tree: SimplexTree) -> Self: >>> from gudhi import SimplexTree >>> tree = SimplexTree() >>> _ = tree.insert([1, 2, 3, 5]) - >>> SC = SimplicialComplex.from_gudhi(tree) + >>> SC = tnx.SimplicialComplex.from_gudhi(tree) """ SC = cls() for simplex, _ in tree.get_skeleton(tree.dimension()): @@ -1510,7 +1509,7 @@ def from_trimesh(cls, mesh) -> Self: ... faces=[[0, 1, 2]], ... process=False, ... ) - >>> SC = SimplicialComplex.from_trimesh(mesh) + >>> SC = tnx.SimplicialComplex.from_trimesh(mesh) >>> print(SC.nodes) >>> print(SC.simplices) >>> SC[(0)]["position"] @@ -1554,7 +1553,7 @@ def load_mesh(cls, file_path, process: bool = False) -> Self: Examples -------- - >>> SC = SimplicialComplex.load_mesh("C:/temp/stanford-bunny.obj") + >>> SC = tnx.SimplicialComplex.load_mesh("C:/temp/stanford-bunny.obj") >>> SC.nodes """ import trimesh @@ -1628,7 +1627,7 @@ def to_spharapy(self, vertex_position_name: str = "position"): >>> 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) + >>> SC = tnx.SimplicialComplex.from_spharpy(mesh) >>> mesh2 = SC.to_spharapy() >>> mesh2.vertlist == mesh.vertlist >>> mesh2.trilist == mesh.trilist @@ -1695,7 +1694,7 @@ def from_nx(cls, G: nx.Graph) -> Self: >>> 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 = tnx.SimplicialComplex.from_nx(G) >>> SC[(1, 2)]["weight"] 2 """ @@ -1738,7 +1737,7 @@ def simplicial_closure_of_hypergraph(cls, H) -> Self: -------- >>> import hypernetx as hnx >>> hg = hnx.Hypergraph([[1, 2, 3, 4], [1, 2, 3]], static=True) - >>> sc = SimplicialComplex.simplicial_closure_of_hypergraph(hg) + >>> sc = tnx.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)]) """ @@ -1756,10 +1755,10 @@ def to_cell_complex(self): Examples -------- - >>> c1 = Simplex((1, 2, 3)) - >>> c2 = Simplex((1, 2, 4)) - >>> c3 = Simplex((2, 5)) - >>> SC = SimplicialComplex([c1, c2, c3]) + >>> c1 = tnx.Simplex((1, 2, 3)) + >>> c2 = tnx.Simplex((1, 2, 4)) + >>> c3 = tnx.Simplex((2, 5)) + >>> SC = tnx.SimplicialComplex([c1, c2, c3]) >>> SC.to_cell_complex() """ from toponetx.classes.cell_complex import CellComplex @@ -1776,10 +1775,10 @@ def to_hypergraph(self) -> Hypergraph: Examples -------- - >>> c1 = Simplex((1, 2, 3)) - >>> c2 = Simplex((1, 2, 4)) - >>> c3 = Simplex((2, 5)) - >>> SC = SimplicialComplex([c1, c2, c3]) + >>> c1 = tnx.Simplex((1, 2, 3)) + >>> c2 = tnx.Simplex((1, 2, 4)) + >>> c3 = tnx.Simplex((2, 5)) + >>> SC = tnx.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=) """ @@ -1800,10 +1799,10 @@ def to_combinatorial_complex(self): Examples -------- - >>> c1 = Simplex((1, 2, 3)) - >>> c2 = Simplex((1, 2, 3)) - >>> c3 = Simplex((1, 2, 4)) - >>> SC = SimplicialComplex([c1, c2, c3]) + >>> c1 = tnx.Simplex((1, 2, 3)) + >>> c2 = tnx.Simplex((1, 2, 3)) + >>> c3 = tnx.Simplex((1, 2, 4)) + >>> SC = tnx.SimplicialComplex([c1, c2, c3]) >>> CCC = SC.to_combinatorial_complex() """ from toponetx.classes.combinatorial_complex import CombinatorialComplex diff --git a/toponetx/readwrite/atomlist.py b/toponetx/readwrite/atomlist.py index 55bea675..43da18a9 100644 --- a/toponetx/readwrite/atomlist.py +++ b/toponetx/readwrite/atomlist.py @@ -1,4 +1,5 @@ """Read and write complexes as a list of their atoms.""" + from collections.abc import Generator, Hashable, Iterable from itertools import combinations from typing import Literal, overload @@ -127,19 +128,17 @@ def generate_atomlist( -------- Generate a list of atoms from a simplicial complex: - >>> from toponetx.classes import SimplicialComplex - >>> SC = SimplicialComplex() + >>> SC = tnx.SimplicialComplex() >>> SC.add_simplex((1,), weight=1.0) >>> SC.add_simplex((1, 2, 3), weight=4.0) - >>> list(generate_atomlist(SC)) + >>> list(tnx.generate_atomlist(SC)) ["1 {'weight': 1.0}", "1 2 3 {'weight': 4.0}"] Generate a list of atoms from a cell complex: - >>> from toponetx.classes import CellComplex - >>> CC = CellComplex() + >>> CC = tnx.CellComplex() >>> CC.add_cell((1, 2, 3), rank=2, weight=4.0) - >>> list(generate_atomlist(CC)) + >>> list(tnx.generate_atomlist(CC)) ["1 2 3 {'weight': 4.0}"] """ if isinstance(domain, SimplicialComplex): diff --git a/tutorials/01_simplicial_complexes.ipynb b/tutorials/01_simplicial_complexes.ipynb index 8fc1731d..b1047188 100644 --- a/tutorials/01_simplicial_complexes.ipynb +++ b/tutorials/01_simplicial_complexes.ipynb @@ -21,19 +21,7 @@ "execution_count": 1, "id": "1d48b70e", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "from IPython import display\n", "\n", @@ -73,7 +61,7 @@ "source": [ "import numpy as np\n", "\n", - "from toponetx.classes import SimplicialComplex as sc" + "import toponetx as tnx" ] }, { @@ -188,7 +176,7 @@ "outputs": [], "source": [ "edge_set = [[1, 2], [2, 3], [2, 4], [3, 4], [2, 5], [5, 6], [5, 7], [6, 7]]\n", - "ex1_sc = sc(edge_set)" + "ex1_sc = tnx.SimplicialComplex(edge_set)" ] }, { @@ -204,18 +192,7 @@ "execution_count": 4, "id": "f49c211e", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SimplexView([(1,), (2,), (3,), (4,), (5,), (6,), (7,), (1, 2), (2, 3), (2, 4), (3, 4), (2, 5), (5, 6), (5, 7), (6, 7)])" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ex1_sc.simplices" ] @@ -271,7 +248,7 @@ "edge_set = [[1, 2], [1, 3]]\n", "face_set = [[2, 3, 4], [2, 4, 5]]\n", "\n", - "ex2_sc = sc(edge_set + face_set)" + "ex2_sc = tnx.SimplicialComplex(edge_set + face_set)" ] }, { @@ -287,18 +264,7 @@ "execution_count": 6, "id": "22d1fa43", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SimplexView([(1,), (2,), (3,), (4,), (5,), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4), (2, 5), (4, 5), (2, 3, 4), (2, 4, 5)])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ex2_sc.simplices" ] @@ -399,15 +365,7 @@ "execution_count": 8, "id": "fdd10897-1dca-47fa-a338-5d66f0512700", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(5, 7)\n" - ] - } - ], + "outputs": [], "source": [ "print(ex2_incidence1.shape)" ] @@ -425,16 +383,7 @@ "execution_count": 9, "id": "70b2618d-fe12-49ac-a342-4e2e7f38edc8", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{(1,): 0, (2,): 1, (3,): 2, (4,): 3, (5,): 4}\n", - "{(1, 2): 0, (1, 3): 1, (2, 3): 2, (2, 4): 3, (2, 5): 4, (3, 4): 5, (4, 5): 6}\n" - ] - } - ], + "outputs": [], "source": [ "print(ex2_incidence1_rows)\n", "print(ex2_incidence1_cols)" @@ -463,19 +412,7 @@ "execution_count": 10, "id": "aef05c8f-f0ea-49e7-b592-9d11dead86bf", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[1. 1. 0. 0. 0. 0. 0.]\n", - " [1. 0. 1. 1. 1. 0. 0.]\n", - " [0. 1. 1. 0. 0. 1. 0.]\n", - " [0. 0. 0. 1. 0. 1. 1.]\n", - " [0. 0. 0. 0. 1. 0. 1.]]\n" - ] - } - ], + "outputs": [], "source": [ "print(np.abs(ex2_incidence1.todense()))" ] @@ -515,19 +452,7 @@ "execution_count": 11, "id": "2889f192-e2a0-4d72-b981-a38de3aa3867", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[-1. -1. 0. 0. 0. 0. 0.]\n", - " [ 1. 0. -1. -1. -1. 0. 0.]\n", - " [ 0. 1. 1. 0. 0. -1. 0.]\n", - " [ 0. 0. 0. 1. 0. 1. -1.]\n", - " [ 0. 0. 0. 0. 1. 0. 1.]]\n" - ] - } - ], + "outputs": [], "source": [ "print(ex2_incidence1.todense())" ] @@ -565,15 +490,7 @@ "execution_count": 13, "id": "9abbd82a", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(7, 2)\n" - ] - } - ], + "outputs": [], "source": [ "print(ex2_incidence2.shape)" ] @@ -591,16 +508,7 @@ "execution_count": 14, "id": "f866ebb8-f629-4109-ae12-d5c83b4a5b71", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{(1, 2): 0, (1, 3): 1, (2, 3): 2, (2, 4): 3, (2, 5): 4, (3, 4): 5, (4, 5): 6}\n", - "{(2, 3, 4): 0, (2, 4, 5): 1}\n" - ] - } - ], + "outputs": [], "source": [ "print(ex2_incidence2_rows)\n", "print(ex2_incidence2_cols)" @@ -631,21 +539,7 @@ "execution_count": 15, "id": "fce5d9b8-2312-426d-af38-fbd58ccb2be8", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[0. 0.]\n", - " [0. 0.]\n", - " [1. 0.]\n", - " [1. 1.]\n", - " [0. 1.]\n", - " [1. 0.]\n", - " [0. 1.]]\n" - ] - } - ], + "outputs": [], "source": [ "print(np.abs(ex2_incidence2.todense()))" ] @@ -663,21 +557,7 @@ "execution_count": 16, "id": "a2dd61c9-2d6f-4235-a77b-ba07574f7c02", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 0. 0.]\n", - " [ 0. 0.]\n", - " [ 1. 0.]\n", - " [-1. 1.]\n", - " [ 0. -1.]\n", - " [ 1. 0.]\n", - " [ 0. 1.]]\n" - ] - } - ], + "outputs": [], "source": [ "print(ex2_incidence2.todense())" ] @@ -738,19 +618,7 @@ "execution_count": 17, "id": "b0129ffe", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 2. -1. -1. 0. 0.]\n", - " [-1. 4. -1. -1. -1.]\n", - " [-1. -1. 3. -1. 0.]\n", - " [ 0. -1. -1. 3. -1.]\n", - " [ 0. -1. 0. -1. 2.]]\n" - ] - } - ], + "outputs": [], "source": [ "# up-Laplacian rank 0: incidence between vertices via edges\n", "up_laplacian_0 = ex2_sc.up_laplacian_matrix(rank=0).todense()\n", @@ -776,21 +644,7 @@ "execution_count": 18, "id": "35e68379", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 0. 0. 0. 0. 0. 0. 0.]\n", - " [ 0. 0. 0. 0. 0. 0. 0.]\n", - " [ 0. 0. 1. -1. 0. 1. 0.]\n", - " [ 0. 0. -1. 2. -1. -1. 1.]\n", - " [ 0. 0. 0. -1. 1. 0. -1.]\n", - " [ 0. 0. 1. -1. 0. 1. 0.]\n", - " [ 0. 0. 0. 1. -1. 0. 1.]]\n" - ] - } - ], + "outputs": [], "source": [ "# up laplacian rank 1: edge to incident faces to incident edges\n", "up_laplacian_1 = ex2_sc.up_laplacian_matrix(rank=1).todense()\n", @@ -885,21 +739,7 @@ "execution_count": 19, "id": "4b52fdbe", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 2. 1. -1. -1. -1. 0. 0.]\n", - " [ 1. 2. 1. 0. 0. -1. 0.]\n", - " [-1. 1. 2. 1. 1. -1. 0.]\n", - " [-1. 0. 1. 2. 1. 1. -1.]\n", - " [-1. 0. 1. 1. 2. 0. 1.]\n", - " [ 0. -1. -1. 1. 0. 2. -1.]\n", - " [ 0. 0. 0. -1. 1. -1. 2.]]\n" - ] - } - ], + "outputs": [], "source": [ "# down laplacian rank 1: edges to incident nodes to incident edges\n", "down_laplacian_1 = ex2_sc.down_laplacian_matrix(rank=1).todense()\n", @@ -922,16 +762,7 @@ "execution_count": 20, "id": "48db5dcc-29b2-48ce-bec6-28d01d2dbe3c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 3. -1.]\n", - " [-1. 3.]]\n" - ] - } - ], + "outputs": [], "source": [ "# down laplacian rank 2: face to incident edges to incident faces\n", "down_laplacian_2 = ex2_sc.down_laplacian_matrix(rank=2).todense()\n", @@ -998,19 +829,7 @@ "execution_count": 21, "id": "8d9e01a3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 2. -1. -1. 0. 0.]\n", - " [-1. 4. -1. -1. -1.]\n", - " [-1. -1. 3. -1. 0.]\n", - " [ 0. -1. -1. 3. -1.]\n", - " [ 0. -1. 0. -1. 2.]]\n" - ] - } - ], + "outputs": [], "source": [ "hodge_laplacian_0 = ex2_sc.hodge_laplacian_matrix(rank=0).todense()\n", "\n", @@ -1030,21 +849,7 @@ "execution_count": 22, "id": "1479bc66-7fa5-4cae-9cea-64642031e393", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 2. 1. -1. -1. -1. 0. 0.]\n", - " [ 1. 2. 1. 0. 0. -1. 0.]\n", - " [-1. 1. 3. 0. 1. 0. 0.]\n", - " [-1. 0. 0. 4. 0. 0. 0.]\n", - " [-1. 0. 1. 0. 3. 0. 0.]\n", - " [ 0. -1. 0. 0. 0. 3. -1.]\n", - " [ 0. 0. 0. 0. 0. -1. 3.]]\n" - ] - } - ], + "outputs": [], "source": [ "hodge_laplacian_1 = ex2_sc.hodge_laplacian_matrix(rank=1).todense()\n", "\n", @@ -1064,16 +869,7 @@ "execution_count": 23, "id": "c3e471f4-5461-42c7-bbf0-e069ca2f6154", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 3. -1.]\n", - " [-1. 3.]]\n" - ] - } - ], + "outputs": [], "source": [ "hodge_laplacian_2 = ex2_sc.hodge_laplacian_matrix(rank=2).todense()\n", "\n", @@ -1135,16 +931,7 @@ "execution_count": 25, "id": "e1ed8daf", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{frozenset({2, 3, 4}): 1, frozenset({2, 4, 5}): 5}\n", - "[1 5]\n" - ] - } - ], + "outputs": [], "source": [ "face_weights = ex2_sc.get_simplex_attributes(\"weight\")\n", "example2_face_values = np.array(list(face_weights.values()))\n", @@ -1183,25 +970,7 @@ "execution_count": 26, "id": "9e66a464", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(7, 2)\n" - ] - }, - { - "data": { - "text/plain": [ - "array([ 0., 0., 1., 4., -5., 1., 5.])" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "print(ex2_incidence2.shape)\n", "ex2_incidence2.dot(example2_face_values)" @@ -1229,15 +998,7 @@ "execution_count": 27, "id": "1a8711fb", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ 6. 14.]\n" - ] - } - ], + "outputs": [], "source": [ "edge_data = {\n", " (1, 2): {\"attr\": 6},\n", diff --git a/tutorials/02_cell_complexes.ipynb b/tutorials/02_cell_complexes.ipynb index 418cba64..3f5449c5 100644 --- a/tutorials/02_cell_complexes.ipynb +++ b/tutorials/02_cell_complexes.ipynb @@ -21,22 +21,12 @@ "execution_count": 1, "id": "b4b84db3", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "from IPython import display\n", "\n", + "import toponetx\n", + "\n", "display.Image(\"cc.png\")" ] }, @@ -54,9 +44,7 @@ "id": "87af1a10", "metadata": {}, "outputs": [], - "source": [ - "from toponetx.classes import CellComplex as CC" - ] + "source": [] }, { "cell_type": "markdown", @@ -156,19 +144,9 @@ "execution_count": 2, "id": "a48e579f", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cell Complex with 0 nodes, 0 edges and 0 2-cells \n", - "Cell Complex with 2 nodes, 1 edges and 0 2-cells \n", - "Cell Complex with 5 nodes, 6 edges and 0 2-cells \n" - ] - } - ], + "outputs": [], "source": [ - "example_1 = CC()\n", + "example_1 = toponetx.CellComplex()\n", "print(example_1)\n", "\n", "example_1.add_cell([1, 2], rank=1)\n", @@ -205,19 +183,9 @@ "execution_count": 3, "id": "866efb53", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cell Complex with 0 nodes, 0 edges and 0 2-cells \n", - "Cell Complex with 3 nodes, 3 edges and 1 2-cells \n", - "Cell Complex with 5 nodes, 6 edges and 2 2-cells \n" - ] - } - ], + "outputs": [], "source": [ - "example_2 = CC()\n", + "example_2 = toponetx.CellComplex()\n", "print(example_2)\n", "\n", "example_2.add_cell([1, 2, 3], rank=2)\n", @@ -264,18 +232,7 @@ "execution_count": 4, "id": "609e6a6d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "edges:\n", - "{(1, 2): 0, (1, 3): 1, (2, 3): 2, (2, 4): 3, (3, 5): 4, (4, 5): 5}\n", - "2-cells:\n", - "{(1, 2, 3): 0, (2, 4, 5, 3): 1}\n" - ] - } - ], + "outputs": [], "source": [ "row, column, incidence_2 = example_2.incidence_matrix(rank=2, index=True)\n", "print(\"edges:\")\n", @@ -368,20 +325,7 @@ "execution_count": 5, "id": "99113d72", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 1. -1. 1. 0. 0. 0.]\n", - " [-1. 1. -1. 0. 0. 0.]\n", - " [ 1. -1. 2. -1. 1. -1.]\n", - " [ 0. 0. -1. 1. -1. 1.]\n", - " [ 0. 0. 1. -1. 1. -1.]\n", - " [ 0. 0. -1. 1. -1. 1.]]\n" - ] - } - ], + "outputs": [], "source": [ "laplacian_up_1 = example_2.up_laplacian_matrix(rank=1).todense()\n", "print(laplacian_up_1)" @@ -406,19 +350,7 @@ "execution_count": 6, "id": "160bd5e3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 2. -1. -1. 0. 0.]\n", - " [-1. 3. -1. -1. 0.]\n", - " [-1. -1. 3. 0. -1.]\n", - " [ 0. -1. 0. 2. -1.]\n", - " [ 0. 0. -1. -1. 2.]]\n" - ] - } - ], + "outputs": [], "source": [ "up_laplacian_0 = example_2.up_laplacian_matrix(rank=0).todense()\n", "print(up_laplacian_0)" @@ -504,20 +436,7 @@ "execution_count": 7, "id": "0f8a7393", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 2. 1. -1. -1. 0. 0.]\n", - " [ 1. 2. 1. 0. -1. 0.]\n", - " [-1. 1. 2. 1. -1. 0.]\n", - " [-1. 0. 1. 2. 0. -1.]\n", - " [ 0. -1. -1. 0. 2. 1.]\n", - " [ 0. 0. 0. -1. 1. 2.]]\n" - ] - } - ], + "outputs": [], "source": [ "down_laplacian_1 = example_2.down_laplacian_matrix(rank=1).todense()\n", "print(down_laplacian_1)" @@ -578,19 +497,7 @@ "execution_count": 8, "id": "ea4559bf", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 2. -1. -1. 0. 0.]\n", - " [-1. 3. -1. -1. 0.]\n", - " [-1. -1. 3. 0. -1.]\n", - " [ 0. -1. 0. 2. -1.]\n", - " [ 0. 0. -1. -1. 2.]]\n" - ] - } - ], + "outputs": [], "source": [ "hodge_laplacian_0 = example_2.hodge_laplacian_matrix(rank=0).todense()\n", "print(hodge_laplacian_0)" @@ -609,20 +516,7 @@ "execution_count": 9, "id": "b4db4148", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 3. 0. 0. -1. 0. 0.]\n", - " [ 0. 3. 0. 0. -1. 0.]\n", - " [ 0. 0. 4. 0. 0. -1.]\n", - " [-1. 0. 0. 3. -1. 0.]\n", - " [ 0. -1. 0. -1. 3. 0.]\n", - " [ 0. 0. -1. 0. 0. 3.]]\n" - ] - } - ], + "outputs": [], "source": [ "hodge_laplacian_1 = example_2.hodge_laplacian_matrix(rank=1).todense()\n", "print(hodge_laplacian_1)" @@ -643,16 +537,7 @@ "execution_count": 10, "id": "449df978", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 3. -1.]\n", - " [-1. 4.]]\n" - ] - } - ], + "outputs": [], "source": [ "hodge_laplacian_2 = example_2.hodge_laplacian_matrix(rank=2).todense()\n", "print(hodge_laplacian_2)" diff --git a/tutorials/03_combinatorial_complexes.ipynb b/tutorials/03_combinatorial_complexes.ipynb index 849c0d64..2c658d22 100644 --- a/tutorials/03_combinatorial_complexes.ipynb +++ b/tutorials/03_combinatorial_complexes.ipynb @@ -37,6 +37,8 @@ "source": [ "from IPython import display\n", "\n", + "import toponetx\n", + "\n", "display.Image(\"ccc.png\")" ] }, @@ -54,9 +56,7 @@ "id": "24d69d5e", "metadata": {}, "outputs": [], - "source": [ - "from toponetx.classes import CombinatorialComplex as CCC" - ] + "source": [] }, { "cell_type": "markdown", @@ -152,7 +152,7 @@ } ], "source": [ - "example = CCC()\n", + "example = toponetx.CombinatorialComplex()\n", "\n", "example.add_cell([1, 2], rank=1)\n", "print(example)\n", diff --git a/tutorials/04_colored_hypergraphs.ipynb b/tutorials/04_colored_hypergraphs.ipynb index 5f5d2800..c3f8e346 100644 --- a/tutorials/04_colored_hypergraphs.ipynb +++ b/tutorials/04_colored_hypergraphs.ipynb @@ -54,9 +54,7 @@ "id": "24d69d5e", "metadata": {}, "outputs": [], - "source": [ - "from toponetx.classes import ColoredHyperGraph as chg" - ] + "source": "import toponetx as tnx" }, { "cell_type": "markdown", @@ -167,7 +165,7 @@ } ], "source": [ - "example = chg()\n", + "example = tnx.ColoredHyperGraph()\n", "\n", "example.add_cell([1, 2], rank=1)\n", "print(example)\n", From a99433eb828cf4a5da057446e53ac7641d2e4939 Mon Sep 17 00:00:00 2001 From: mhajij Date: Mon, 3 Jun 2024 00:26:26 -0700 Subject: [PATCH 31/53] adding readme file --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bd8313e2..881e4775 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Codecov](https://codecov.io/gh/pyt-team/TopoNetX/branch/main/graph/badge.svg)](https://app.codecov.io/gh/pyt-team/TopoNetX) [![Python](https://img.shields.io/badge/python-3.10+-blue?logo=python)](https://www.python.org/) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7958504.svg)](https://doi.org/10.5281/zenodo.7958504) - +[![slack](https://img.shields.io/badge/chat-on%20slack-purple?logo=slack)](https://join.slack.com/t/pyt-teamworkspace/shared_invite/zt-2k63sv99s-jbFMLtwzUCc8nt3sIRWjEw) ![toponetx](https://github.com/mhajij/shrec_16/blob/main/logo.png) @@ -14,7 +14,7 @@ Many complex systems, ranging from socio-economic systems such as social networks, over to biological systems (e.g., proteins) and technical systems can be abstracted as a set of entities with are linked to each other via a set of relations. For instance, a social network may be abstracted as a set vertices corresponding to people linked via various social interactions, including pairwise relationships such as friendships and higher-order relationships involving multiple people. -This *relational data* can be abstracted as a topological domain such as a graph, hypergraph, simplicial, cellular or combinatorial complex, which enables the principled analysis of such data. +This *relational data* can be abstracted as a topological domain such as a graph, hypergraph, simplicial, cellular path or combinatorial complex, which enables the principled analysis of such data. `TopoNetX` provides a unified platform to compute with such relational data. @@ -38,7 +38,7 @@ found in higher-order networks such as simplicial, cellular, CW and combinatoria This package serves as a repository of the methods and algorithms we find most useful as we explore the knowledge that can be encoded via higher-order networks. -TNX supports the construction of many topological structures including the `CellComplex`, `SimplicialComplex` and `CombinatorialComplex` classes. +TNX supports the construction of many topological structures including the `CellComplex`, `PathComplex`, "ColoredHyperGraph" `SimplicialComplex` and `CombinatorialComplex` classes. These classes provide methods for computing boundary operators, Hodge Laplacians and higher-order adjacency operators on cell, simplicial and combinatorial complexes, respectively. The classes are used in many areas of mathematics and computer science, From 49e212499ea8974c66802660576754ae0318e488 Mon Sep 17 00:00:00 2001 From: Nina Miolane Date: Tue, 4 Jun 2024 12:46:34 -0700 Subject: [PATCH 32/53] Update README.md: New badges + Update logo --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21c32156..1f730c0a 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,37 @@ +

+ +

+ +

+ Computing with Relational Data abstracted as Topological Domains +

+ +
+ [![Test](https://github.com/pyt-team/TopoNetX/actions/workflows/test.yml/badge.svg)](https://github.com/pyt-team/TopoNetX/actions/workflows/test.yml) [![Lint](https://github.com/pyt-team/TopoNetX/actions/workflows/lint.yml/badge.svg)](https://github.com/pyt-team/TopoNetX/actions/workflows/lint.yml) [![Codecov](https://codecov.io/gh/pyt-team/TopoNetX/branch/main/graph/badge.svg)](https://app.codecov.io/gh/pyt-team/TopoNetX) +[![Docs](https://img.shields.io/badge/docs-website-brightgreen)](https://pyt-team.github.io/toponetx/index.html) [![Python](https://img.shields.io/badge/python-3.10+-blue?logo=python)](https://www.python.org/) +[![license](https://badgen.net/github/license/pyt-team/TopoNetX?color=green)](https://github.com/pyt-team/TopoNetX/blob/main/LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7958504.svg)](https://doi.org/10.5281/zenodo.7958504) [![slack](https://img.shields.io/badge/chat-on%20slack-purple?logo=slack)](https://join.slack.com/t/pyt-teamworkspace/shared_invite/zt-2k63sv99s-jbFMLtwzUCc8nt3sIRWjEw) -![toponetx](https://github.com/mhajij/shrec_16/blob/main/logo.png) +
+ +

+ Scope and Functionality • + Main Features • + Installing TopoNetX • + Getting Started • + References + Acknowledgements +

+ + + # 🌐 TopoNetX (TNX) 🍩 -# Computing with Relational Data abstracted as Topological Domains ![toponetx](https://user-images.githubusercontent.com/8267869/234068354-af9480f1-1d18-4914-92f1-916d9093e44d.png) From a6ec332b4f5fe06b32b29b114ff8ab4b809d49ac Mon Sep 17 00:00:00 2001 From: Nina Miolane Date: Tue, 4 Jun 2024 21:53:15 -0700 Subject: [PATCH 33/53] Add new logo --- resources/logo.png | Bin 0 -> 156463 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 resources/logo.png diff --git a/resources/logo.png b/resources/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..c9f36ec155c2b0751e143157b1245b961fcc7137 GIT binary patch literal 156463 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%0D8`M<4+>Skmj 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 z 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

Yfbys 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

;5Y)XQ}I&x&y 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

Tt@rYONX+}sIoJDc6w?e@C5(l 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 d6UEuz7jdDsM+{a_<7dD}T3kV_T*S!#{{X Date: Tue, 4 Jun 2024 21:54:38 -0700 Subject: [PATCH 34/53] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f730c0a..486fe91a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

From d49128b26f05c4745ec616f531a463fe42a63472 Mon Sep 17 00:00:00 2001 From: Nina Miolane Date: Tue, 4 Jun 2024 21:55:53 -0700 Subject: [PATCH 35/53] Update README.md --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 486fe91a..49564ff2 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,10 @@ [![Docs](https://img.shields.io/badge/docs-website-brightgreen)](https://pyt-team.github.io/toponetx/index.html) [![Python](https://img.shields.io/badge/python-3.10+-blue?logo=python)](https://www.python.org/) [![license](https://badgen.net/github/license/pyt-team/TopoNetX?color=green)](https://github.com/pyt-team/TopoNetX/blob/main/LICENSE) -[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7958504.svg)](https://doi.org/10.5281/zenodo.7958504) [![slack](https://img.shields.io/badge/chat-on%20slack-purple?logo=slack)](https://join.slack.com/t/pyt-teamworkspace/shared_invite/zt-2k63sv99s-jbFMLtwzUCc8nt3sIRWjEw) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7958504.svg)](https://doi.org/10.5281/zenodo.7958504) +

@@ -24,15 +25,12 @@ Main FeaturesInstalling TopoNetXGetting Started • - References + ReferencesAcknowledgements

- -# 🌐 TopoNetX (TNX) 🍩 - ![toponetx](https://user-images.githubusercontent.com/8267869/234068354-af9480f1-1d18-4914-92f1-916d9093e44d.png) Many complex systems, ranging from socio-economic systems such as social networks, over to biological systems (e.g., proteins) and technical systems can be abstracted as a set of entities with are linked to each other via a set of relations. From 07171802b96b203eaa05328053c95cc59cd58bb4 Mon Sep 17 00:00:00 2001 From: Nina Miolane Date: Tue, 4 Jun 2024 22:01:46 -0700 Subject: [PATCH 36/53] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 49564ff2..5e45ee74 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

From c53e22dfd20846123a29bbf330fdfd77d689f9ed Mon Sep 17 00:00:00 2001 From: Nina Miolane Date: Tue, 4 Jun 2024 22:19:20 -0700 Subject: [PATCH 37/53] Update README.md --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5e45ee74..7184f2c4 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,15 @@ Computing with Relational Data abstracted as Topological Domains

+

+ Scope and Functionality • + Main Features • + Installing TopoNetX • + Getting Started • + References • + Acknowledgements +

+
[![Test](https://github.com/pyt-team/TopoNetX/actions/workflows/test.yml/badge.svg)](https://github.com/pyt-team/TopoNetX/actions/workflows/test.yml) @@ -20,14 +29,7 @@
-

- Scope and Functionality • - Main Features • - Installing TopoNetX • - Getting Started • - References • - Acknowledgements -

+ From 3c8c9a7ca59c77233b900e3bc780e871ff5aa4c8 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Wed, 5 Jun 2024 11:37:31 +0200 Subject: [PATCH 38/53] Fix logo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7184f2c4..eb8f06c1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

From 25433b109e0b6ac029185a417a00b7670d1cf270 Mon Sep 17 00:00:00 2001 From: Josef Hoppe Date: Fri, 14 Jun 2024 18:15:16 +0200 Subject: [PATCH 39/53] README: Add ERC Funding Notice --- README.md | 6 ++++++ resources/erc_logo.png | Bin 0 -> 73852 bytes 2 files changed, 6 insertions(+) create mode 100644 resources/erc_logo.png diff --git a/README.md b/README.md index eb8f06c1..f196a6d5 100644 --- a/README.md +++ b/README.md @@ -194,3 +194,9 @@ Some of these packages include: - [`HyperNetX`](https://pnnl.github.io/HyperNetX/) - [`gudhi`](https://gudhi.inria.fr/python/latest/) - [`trimesh`](https://trimsh.org/index.html) + +## Funding + + + +Partially funded by the European Union (ERC, HIGH-HOPeS, 101039827). Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Council Executive Agency. Neither the European Union nor the granting authority can be held responsible for them. \ No newline at end of file diff --git a/resources/erc_logo.png b/resources/erc_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6c8c555f9aec0fa9af3c51887d875e8754c85d3a GIT binary patch literal 73852 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 literal 0 HcmV?d00001 From 318c38134d3606713078ccab39f52eebbcc2d7c6 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 12:58:02 +0200 Subject: [PATCH 40/53] Remove `wheel` from build dependencies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c81cf5e5..b81c1369 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools", "wheel"] +requires = ["setuptools"] build-backend = "setuptools.build_meta" [project] From 6a32e7d20902022f181316ed6eacbb7c31e59795 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 13:08:09 +0200 Subject: [PATCH 41/53] Maintain GitHub action versions with Dependabot --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..d15c975e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + actions: + patterns: + - "*" From d034228ef65000b68c52500d6459f7790ab23b07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:13:56 +0000 Subject: [PATCH 42/53] Bump the actions group with 3 updates Bumps the actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [actions/setup-python](https://github.com/actions/setup-python) and [codecov/codecov-action](https://github.com/codecov/codecov-action). Updates `actions/checkout` from 3 to 4 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) Updates `actions/setup-python` from 4 to 5 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) Updates `codecov/codecov-action` from 3 to 4 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 8 ++++---- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 561e1b57..cbce913e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,9 +17,9 @@ jobs: python-version: [3.10.11] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build using Python ${{matrix.python-version}} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{matrix.python-version}} cache: "pip" @@ -52,9 +52,9 @@ jobs: python-version: [3.11.3] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build using Python ${{matrix.python-version}} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{matrix.python-version}} cache: "pip" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 88d863b0..866e2c4f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,5 +10,5 @@ jobs: ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: chartboost/ruff-action@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 99dc531c..cd29197b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,10 +31,10 @@ jobs: flavor: ["dev", "all"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: "pip" @@ -54,7 +54,7 @@ jobs: pytest --cov --cov-report=xml:coverage.xml - name: Upload coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.xml From efcd322b68805f3e5c7e015d646091f4aa9f0e03 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 14:03:19 +0200 Subject: [PATCH 43/53] Update blacken-docs pre-commit hook --- .pre-commit-config.yaml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 647ab3af..ad3f19e9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,3 @@ -default_language_version: - python: python3.10 - repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 @@ -16,22 +13,21 @@ repos: args: - --maxkb=2048 - id: trailing-whitespace - - id: requirements-txt-fixer - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.1.14 hooks: - id: ruff - types_or: [ python, pyi, jupyter ] - args: [ --fix ] + types_or: [python, pyi, jupyter] + args: ["--fix", "--show-fixes"] - id: ruff-format - types_or: [ python, pyi, jupyter ] + types_or: [python, pyi, jupyter] - - repo: https://github.com/asottile/blacken-docs - rev: v1.12.0 + - repo: https://github.com/adamchainz/blacken-docs + rev: "1.16.0" hooks: - id: blacken-docs - additional_dependencies: [ black==20.8b0 ] + additional_dependencies: [black==24.*] - repo: https://github.com/numpy/numpydoc rev: v1.6.0 From 6cf0ab377e6b357ef48b0c8c28c8f66ac80bbfb4 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 14:05:08 +0200 Subject: [PATCH 44/53] Add prettier to pre-commit hooks --- .github/workflows/lint.yml | 4 ++-- .github/workflows/test.yml | 20 +++++++++----------- .pre-commit-config.yaml | 5 +++++ README.md | 31 +++++++++++++++++++------------ codecov.yml | 2 +- tutorials/README.md | 2 +- 6 files changed, 37 insertions(+), 27 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 866e2c4f..68cd8c14 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,9 +2,9 @@ name: Linting on: push: - branches: [ main,github-actions-test ] + branches: [main, github-actions-test] pull_request: - branches: [ main ] + branches: [main] jobs: ruff: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd29197b..cee71457 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,24 +2,22 @@ name: "Test" on: push: - branches: [main,github-actions-test] + branches: [main, github-actions-test] paths-ignore: - - 'docs/**' - - 'README.md' - - 'LICENSE.txt' - - '.gitignore' + - "docs/**" + - "README.md" + - "LICENSE.txt" + - ".gitignore" pull_request: branches: [main] paths-ignore: - - 'docs/**' - - 'README.md' - - 'LICENSE.txt' - - '.gitignore' - + - "docs/**" + - "README.md" + - "LICENSE.txt" + - ".gitignore" jobs: - build: runs-on: ${{ matrix.os }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad3f19e9..b95bfdff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,3 +33,8 @@ repos: rev: v1.6.0 hooks: - id: numpydoc-validation + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v3.1.0" + hooks: + - id: prettier diff --git a/README.md b/README.md index f196a6d5..452d3de9 100644 --- a/README.md +++ b/README.md @@ -29,15 +29,11 @@ - - - - ![toponetx](https://user-images.githubusercontent.com/8267869/234068354-af9480f1-1d18-4914-92f1-916d9093e44d.png) Many complex systems, ranging from socio-economic systems such as social networks, over to biological systems (e.g., proteins) and technical systems can be abstracted as a set of entities with are linked to each other via a set of relations. For instance, a social network may be abstracted as a set vertices corresponding to people linked via various social interactions, including pairwise relationships such as friendships and higher-order relationships involving multiple people. -This *relational data* can be abstracted as a topological domain such as a graph, hypergraph, simplicial, cellular path or combinatorial complex, which enables the principled analysis of such data. +This _relational data_ can be abstracted as a topological domain such as a graph, hypergraph, simplicial, cellular path or combinatorial complex, which enables the principled analysis of such data. `TopoNetX` provides a unified platform to compute with such relational data. @@ -73,32 +69,39 @@ TNX is developed by the [pyt-team](https://github.com/pyt-team) 1. Dynamic construction of cell, simplicial and combinatorial complexes, allowing users to add or remove objects from these structures after their initial creation. 2. Compatibility with the [`NetworkX`](https://networkx.org/) and [`gudhi`](https://gudhi.inria.fr/) packages, enabling users to -leverage the powerful algorithms and data structures provided by these packages. + leverage the powerful algorithms and data structures provided by these packages. 3. Support for attaching arbitrary attributes and data to cells, simplices and other entities in a complex, allowing users to store and manipulate a versatile range of information about these objects. 4. Computation of boundary operators, Hodge Laplacians and higher-order adjacency -operators on a complex, enabling users to study the topological properties of the space. + operators on a complex, enabling users to study the topological properties of the space. 5. Robust error handling and validation of input data, ensuring that the package is -reliable and easy to use. + reliable and easy to use. 6. Package dependencies are kept to a minimum, -to facilitate easy installation and -to reduce future installation issues arising from such dependencies. + to facilitate easy installation and + to reduce future installation issues arising from such dependencies. # 🤖 Installing TopoNetX 1. Clone a copy of `TopoNetX` from source: + ```bash git clone https://github.com/pyt-team/TopoNetX cd TopoNetX ``` + 2. If you have already cloned `TopoNetX` from source, update it: + ```bash git pull ``` + 3. Install `TopoNetX` in editable mode (requires pip ≥ 21.3 for [PEP 660](https://peps.python.org/pep-0610/) support): + ```bash pip install -e '.[all]' ``` + 4. Install pre-commit hooks: + ```bash pre-commit install ``` @@ -158,11 +161,12 @@ B02 = cc.incidence_matrix(0, 2) B03 = cc.incidence_matrix(0, 3) ``` -## 🔍 References ## +## 🔍 References To learn more about topological domains, and how they can be used in deep learning: - Mustafa Hajij, Ghada Zamzmi, Theodore Papamarkou, Nina Miolane, Aldo Guzmán-Sáenz, Karthikeyan Natesan Ramamurthy, Tolga Birdal, Tamal K. Dey, Soham Mukherjee, Shreyas N. Samaga, Neal Livesay, Robin Walters, Paul Rosen, Michael T. Schaub. [Topological Deep Learning: Going Beyond Graph Data](https://arxiv.org/abs/2206.00606). + ``` @misc{hajij2023topological, title={Topological Deep Learning: Going Beyond Graph Data}, @@ -173,7 +177,9 @@ To learn more about topological domains, and how they can be used in deep learni primaryClass={cs.LG} } ``` + - Mathilde Papillon, Sophia Sanborn, Mustafa Hajij, Nina Miolane. [Architectures of Topological Deep Learning: A Survey on Topological Neural Networks.](https://arxiv.org/pdf/2304.10031.pdf) + ``` @misc{papillon2023architectures, title={Architectures of Topological Deep Learning: A Survey on Topological Neural Networks}, @@ -190,6 +196,7 @@ To learn more about topological domains, and how they can be used in deep learni `TopoNetX` has been built with the help of several open-source packages. All of these are listed in setup.py. Some of these packages include: + - [`NetworkX`](https://networkx.org/) - [`HyperNetX`](https://pnnl.github.io/HyperNetX/) - [`gudhi`](https://gudhi.inria.fr/python/latest/) @@ -199,4 +206,4 @@ Some of these packages include: -Partially funded by the European Union (ERC, HIGH-HOPeS, 101039827). Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Council Executive Agency. Neither the European Union nor the granting authority can be held responsible for them. \ No newline at end of file +Partially funded by the European Union (ERC, HIGH-HOPeS, 101039827). Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Council Executive Agency. Neither the European Union nor the granting authority can be held responsible for them. diff --git a/codecov.yml b/codecov.yml index 85ba6f8b..08e6a00d 100644 --- a/codecov.yml +++ b/codecov.yml @@ -4,4 +4,4 @@ coverage: round: down precision: 2 ignore: - - "test/" \ No newline at end of file + - "test/" diff --git a/tutorials/README.md b/tutorials/README.md index 6fa373f8..d543cdd5 100644 --- a/tutorials/README.md +++ b/tutorials/README.md @@ -1,3 +1,3 @@ # Tutorials -Run these tutorials to get started with toponetx. \ No newline at end of file +Run these tutorials to get started with toponetx. From ab511db14ee351d0cca754e15aea7d6d690a6825 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 14:17:04 +0200 Subject: [PATCH 45/53] Add PyGrep pre-commit hooks --- .pre-commit-config.yaml | 7 +++++++ docs/api/index.rst | 14 +++++++------- docs/contributing/index.rst | 12 ++++++------ docs/index.rst | 14 +++++++------- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b95bfdff..fc81ff80 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,3 +38,10 @@ repos: rev: "v3.1.0" hooks: - id: prettier + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: "v1.10.0" + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal diff --git a/docs/api/index.rst b/docs/api/index.rst index 29b1c725..ce5ac0b6 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -2,14 +2,14 @@ API Reference ============= -The API reference gives an overview of `TopoNetX`, which consists of several modules: +The API reference gives an overview of ``TopoNetX``, which consists of several modules: -- `classes` implements the topological domains: simplicial complexes, cellular complexes, combinatorial complexes. -- `algorithms` implements signal processing techniques on topological domains, such as the eigendecomposition of a laplacian. -- `datasets` implements utilities to load small datasets on topological domains. -- `transform` implements functions to transform the topological domain that supports a dataset, effectively "lifting" the dataset onto another domain. -- `generators` implements functions to generate random topological domains. -- `transform` implements functions to convert a graph to a topological domain: simplicial complexes etc. +- ``classes`` implements the topological domains: simplicial complexes, cellular complexes, combinatorial complexes. +- ``algorithms`` implements signal processing techniques on topological domains, such as the eigendecomposition of a laplacian. +- ``datasets`` implements utilities to load small datasets on topological domains. +- ``transform`` implements functions to transform the topological domain that supports a dataset, effectively "lifting" the dataset onto another domain. +- ``generators`` implements functions to generate random topological domains. +- ``transform`` implements functions to convert a graph to a topological domain: simplicial complexes etc. .. toctree:: diff --git a/docs/contributing/index.rst b/docs/contributing/index.rst index a3ca021d..fb670b53 100644 --- a/docs/contributing/index.rst +++ b/docs/contributing/index.rst @@ -40,7 +40,7 @@ Follow these steps before submitting a PR: $ git add $ git commit -m "Add my feature" - to record your changes. Then push the changes to your fork of `TopoNextX` with: + to record your changes. Then push the changes to your fork of ``TopoNetX`` with: .. code-block:: bash @@ -54,9 +54,9 @@ Follow these steps before submitting a PR: Write Tests ----------- -The tests consist of classes appropriately named, located in the `test` folder, that check the validity of the code. +The tests consist of classes appropriately named, located in the ``test`` folder, that check the validity of the code. -Test functions should be located in files whose filenames start with `test_`. For example: +Test functions should be located in files whose filenames start with ``test_``. For example: .. code-block:: bash @@ -68,12 +68,12 @@ Test functions should be located in files whose filenames start with `test_`. Fo def test_capital_case(): assert add(4, 5) == 9 -Use an `assert` statement to check that the function under test returns the correct output. +Use an ``assert`` statement to check that the function under test returns the correct output. Run Tests ~~~~~~~~~ -Install `pytest` which is the software tools used to run tests: +Install ``pytest`` which is the software tools used to run tests: .. code-block:: bash @@ -85,7 +85,7 @@ Then run the test using: $ pytest test_add.py -Verify that the code you have added does not break `TopoNetX` by running all the tests. +Verify that the code you have added does not break ``TopoNetX`` by running all the tests. .. code-block:: bash diff --git a/docs/index.rst b/docs/index.rst index 2f63e2d6..5b8d9653 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ 🌐 TopoNetX (TNX) 🍩 ==================== -`TopoNetX` is a Python package for computing on topological domains. Topological domains are the natural mathematical structures representing relations between the components of a dataset. +``TopoNetX`` is a Python package for computing on topological domains. Topological domains are the natural mathematical structures representing relations between the components of a dataset. .. figure:: https://user-images.githubusercontent.com/8267869/234068354-af9480f1-1d18-4914-92f1-916d9093e44d.png :alt: natural shapes @@ -11,22 +11,22 @@ Many natural systems as diverse as social networks and proteins are characterized by relational structure. This is the structure of interactions between components in the system, such as social interactions between individuals or electrostatic interactions between atoms. -`TopoNetX` provides a unifying interface to compute with such relational data. +``TopoNetX`` provides a unifying interface to compute with such relational data. 🎯 Scope and functionality -------------------------- -`TopoNetX` (TNX) is a package for computing with topological domains and studying their properties. +``TopoNetX`` (TNX) is a package for computing with topological domains and studying their properties. With its dynamic construction capabilities and support for arbitrary -attributes and data, `TopoNetX` allows users to easily explore the topological structure +attributes and data, ``TopoNetX`` allows users to easily explore the topological structure of their data and gain insights into its underlying geometric and algebraic properties. Available functionality ranges from computing boundary operators and Hodge Laplacians on simplicial/cell/combinatorial complexes to performing higher-order adjacency calculations. -TNX is similar to `NetworkX`, a popular graph package, and extends its capabilities to support a +TNX is similar to ``NetworkX``, a popular graph package, and extends its capabilities to support a wider range of mathematical structures, including cell complexes, simplicial complexes and combinatorial complexes. @@ -35,7 +35,7 @@ found in higher-order networks such as simplicial, cellular, CW and combinatoria This package serves as a repository of the methods and algorithms we find most useful as we explore the knowledge that can be encoded via higher-order networks. -TNX supports the construction of topological structures including the `CellComplex`, `SimplicialComplex` and `CombinatorialComplex` classes. +TNX supports the construction of topological structures including the ``CellComplex``, ``SimplicialComplex`` and ``CombinatorialComplex`` classes. These classes provide methods for computing boundary operators, Hodge Laplacians and higher-order adjacency operators on cell, simplicial and combinatorial complexes, @@ -49,7 +49,7 @@ TNX was developed by the pyt-team. 1. Dynamic construction of cell, simplicial and combinatorial complexes, allowing users to add or remove objects from these structures after their initial creation. -2. Compatibility with the `NetworkX` and `gudhi` packages, enabling users to leverage the powerful algorithms and data structures provided by these packages. +2. Compatibility with the ``NetworkX`` and ``gudhi`` packages, enabling users to leverage the powerful algorithms and data structures provided by these packages. 3. Support for attaching arbitrary attributes and data to cells, simplices and other entities in a complex, allowing users to store and manipulate a versatile range of information about these objects. From b7139d0734d85146d3b53e5ba689ac241b378dfb Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 16:21:04 +0200 Subject: [PATCH 46/53] Apply pytest suggestions from scientific python --- .pre-commit-config.yaml | 2 +- pyproject.toml | 11 ++++++++++- test/classes/test_simplex.py | 17 ++++++++--------- toponetx/classes/cell_complex.py | 4 +--- toponetx/classes/simplicial_complex.py | 3 +-- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fc81ff80..2f355b78 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.14 + rev: v0.4.10 hooks: - id: ruff types_or: [python, pyi, jupyter] diff --git a/pyproject.toml b/pyproject.toml index b81c1369..d64ac0c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ dependencies=[ "networkx", "numpy", "pandas", + "pyarrow", "requests", "scipy", "trimesh", @@ -125,7 +126,15 @@ disable_error_code = ["import-untyped"] plugins = "numpy.typing.mypy_plugin" [tool.pytest.ini_options] -addopts = "--capture=no" +minversion = "7.0" +addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] +xfail_strict = true +filterwarnings = [ + "error", + "ignore::scipy.sparse._base.SparseEfficiencyWarning", +] +log_cli_level = "info" +testpaths = ["test"] [tool.coverage.report] exclude_lines = ["pragma: not covered", "@overload"] diff --git a/test/classes/test_simplex.py b/test/classes/test_simplex.py index e47401c0..e10fa46d 100644 --- a/test/classes/test_simplex.py +++ b/test/classes/test_simplex.py @@ -28,11 +28,7 @@ def test_simplex_creation(self): def test_simplex_sign(self): """Test simplex sign method.""" - s = Simplex( - [ - 1, - ] - ) + s = Simplex([1]) with pytest.raises(NotImplementedError): s.sign(face=1) @@ -73,15 +69,18 @@ def test__contains__(self): def test_construct_tree(self): """Test the construct_tree property of the simplex.""" - s = Simplex((1, 2, 3), construct_tree=True) - assert len(s.boundary) == 3 + with pytest.warns(DeprecationWarning): + s = Simplex((1, 2, 3), construct_tree=True) + with pytest.warns(DeprecationWarning): + assert len(s.boundary) == 3 s = Simplex((1, 2, 3), construct_tree=False) - assert len(s.boundary) == 3 + with pytest.warns(DeprecationWarning): + assert len(s.boundary) == 3 def test_getting_and_setting_items(self): """Test getting and setting items in the simplex.""" - s = Simplex((1, 2, 3), construct_tree=True) + s = Simplex((1, 2, 3)) s["weight"] = 1 assert s["weight"] == 1 diff --git a/toponetx/classes/cell_complex.py b/toponetx/classes/cell_complex.py index 7046ac55..71bfe04f 100644 --- a/toponetx/classes/cell_complex.py +++ b/toponetx/classes/cell_complex.py @@ -790,9 +790,7 @@ def add_cell( if not isinstance(cell, list): cell = list(cell) - if self.is_insertable_cycle( - cell, check_skeleton=check_skeleton, warnings_dis=True - ): + if self.is_insertable_cycle(cell, check_skeleton): edges_cell = set(zip_longest(cell, cell[1:] + [cell[0]])) self._G.add_edges_from(edges_cell) self._insert_cell(Cell(cell, regular=self._regular), **attr) diff --git a/toponetx/classes/simplicial_complex.py b/toponetx/classes/simplicial_complex.py index bc247b15..895cd944 100644 --- a/toponetx/classes/simplicial_complex.py +++ b/toponetx/classes/simplicial_complex.py @@ -463,12 +463,11 @@ def remove_maximal_simplex(self, simplex: Collection) -> None: if simplex_ in self._simplex_set.faces_dict[len(simplex_) - 1]: if self.is_maximal(simplex): del self._simplex_set.faces_dict[len(simplex_) - 1][simplex_] - faces = Simplex(simplex_).faces + faces = self.get_boundaries([simplex_]) for s in faces: if len(s) == len(simplex_): continue - s = s.elements self._simplex_set.faces_dict[len(s) - 1][s]["membership"] -= { simplex_ } From 24f0100189cd2d12976c8536f70707d736f5f10d Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 18:03:32 +0200 Subject: [PATCH 47/53] Clean-up ruff config according to scientific python suggestions --- pyproject.toml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d64ac0c4..1585f54c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,6 @@ homepage="https://github.com/pyt-team/TopoNetX" repository="https://github.com/pyt-team/TopoNetX" [tool.ruff] -target-version = "py310" extend-include = ["*.ipynb"] [tool.ruff.format] @@ -103,12 +102,12 @@ ignore = [ "PERF203", # allow try-except within loops ] +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F403"] + [tool.ruff.lint.pydocstyle] convention = "numpy" -[tool.ruff.per-file-ignores] -"__init__.py" = ["F403"] - [tool.setuptools.dynamic] version = {attr = "toponetx.__version__"} From eacb1cf397af45d18153acfae3680b83aa0e83b3 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 18:28:18 +0200 Subject: [PATCH 48/53] Auto-cancel running workflows with new commits in a pr --- .github/workflows/docs.yml | 5 +++++ .github/workflows/lint.yml | 4 ++++ .github/workflows/test.yml | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cbce913e..687e654a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,6 +5,11 @@ on: branches: [main, github-actions-test] pull_request: branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + permissions: contents: write diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 68cd8c14..1beb7a9c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,6 +6,10 @@ on: pull_request: branches: [main] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: ruff: runs-on: ubuntu-latest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cee71457..92eda4eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,10 @@ on: - "LICENSE.txt" - ".gitignore" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: build: runs-on: ${{ matrix.os }} From 2564777c383b3b12bc932fcbb6a897446bb1f1c6 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 18:32:24 +0200 Subject: [PATCH 49/53] Exclude dependabot in auto-generated changelogs --- .github/release.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/release.yml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000..ddb3e480 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,4 @@ +changelog: + exclude: + authors: + - dependabot From 31de2fd8d9d9b87fb6ec5aa36d7fedcae3f97587 Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Fri, 21 Jun 2024 20:46:43 +0200 Subject: [PATCH 50/53] Update mypy config --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1585f54c..8d912ffe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,8 +119,9 @@ include = [ [tool.mypy] warn_redundant_casts = true +warn_unreachable = true warn_unused_ignores = true -show_error_codes = true +enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] disable_error_code = ["import-untyped"] plugins = "numpy.typing.mypy_plugin" From 1a044814f52c9c013c11055bad33b2a4fcd6c7ce Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Mon, 24 Jun 2024 14:58:06 +0200 Subject: [PATCH 51/53] Update pre-commit hooks --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2f355b78..f3672641 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.6.0 hooks: - id: fix-byte-order-marker - id: check-case-conflict @@ -30,7 +30,7 @@ repos: additional_dependencies: [black==24.*] - repo: https://github.com/numpy/numpydoc - rev: v1.6.0 + rev: v1.7.0 hooks: - id: numpydoc-validation From 93229bc30d122e9b1dc65273c31283e61bd4e87f Mon Sep 17 00:00:00 2001 From: Florian Frantzen Date: Thu, 27 Jun 2024 12:43:38 +0200 Subject: [PATCH 52/53] Fix tests for scipy 1.14 --- test/classes/test_simplicial_complex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/classes/test_simplicial_complex.py b/test/classes/test_simplicial_complex.py index 57c0c5cb..4c124074 100644 --- a/test/classes/test_simplicial_complex.py +++ b/test/classes/test_simplicial_complex.py @@ -437,7 +437,7 @@ def test_hodge_laplacian_matrix_rank_2(self): column, L_hodge = SC.hodge_laplacian_matrix(rank=2, signed=False, index=True) expected_col = {(1, 2, 3): 0, (2, 3, 4): 1} assert column == expected_col - np.testing.assert_array_equal(L_hodge.A, np.array([[3, 1], [1, 3]])) + np.testing.assert_array_equal(L_hodge.toarray(), np.array([[3, 1], [1, 3]])) def test_hodge_laplacian_matrix_rank_1(self): """Test unsigned hodge_laplacian_matrix method with index for different ranks.""" @@ -453,7 +453,7 @@ def test_hodge_laplacian_matrix_rank_1(self): } assert column == expected_col np.testing.assert_array_equal( - L_hodge.A, + L_hodge.toarray(), np.array( [ [2.0, 1.0, 1.0, 0.0, 0.0, 0.0], From 04181fa6afd80e940ba1d1189d54ef938baa2fa5 Mon Sep 17 00:00:00 2001 From: Mustafa Hajij <61298952+mhajij@users.noreply.github.com> Date: Thu, 4 Jul 2024 02:13:26 -0700 Subject: [PATCH 53/53] nsf funding added (#375) Co-authored-by: Florian Frantzen <2105496+ffl096@users.noreply.github.com> --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 452d3de9..96dcfef8 100644 --- a/README.md +++ b/README.md @@ -207,3 +207,5 @@ Some of these packages include: Partially funded by the European Union (ERC, HIGH-HOPeS, 101039827). Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Council Executive Agency. Neither the European Union nor the granting authority can be held responsible for them. + +Partially funded by the National Science Foundation (DMS-2134231, DMS-2134241).