spatial-graph 0.0.3__tar.gz → 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/PKG-INFO +2 -2
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/pyproject.toml +1 -1
- spatial_graph-0.0.4/src/spatial_graph/_graph/graph.py +227 -0
- spatial_graph-0.0.4/src/spatial_graph/_graph/graph_base.py +321 -0
- spatial_graph-0.0.4/src/spatial_graph/_graph/views.py +173 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/line_rtree.py +1 -1
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/rtree.py +133 -121
- spatial_graph-0.0.4/src/spatial_graph/_rtree/tree_base.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_spatial_graph.py +10 -13
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/tests/test_bench.py +4 -12
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/tests/test_invalid_inputs.py +0 -16
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/tests/test_rtree.py +11 -11
- spatial_graph-0.0.3/src/spatial_graph/_graph/cgraph.pyi +0 -388
- spatial_graph-0.0.3/src/spatial_graph/_graph/graph.py +0 -391
- spatial_graph-0.0.3/tests/test_assert.py +0 -2
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/.github/workflows/ci.yml +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/.github/workflows/docs.yaml +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/.gitignore +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/.pre-commit-config.yaml +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/Dockerfile +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/LICENSE +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/README.md +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/docs/index.md +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/examples/basic_usage.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/examples/query_nearest_vispy.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/mkdocs.yml +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/__init__.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_dtypes.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_graph/__init__.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_graph/src/LICENSE.txt +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_graph/src/graph_lite.h +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_graph/wrapper_template.pyx +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/__init__.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/point_rtree.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/src/ARCHITECTURE.md +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/src/LICENSE +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/src/config.h +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/src/rtree.c +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/src/rtree.h +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_rtree/wrapper_template.pyx +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/src/spatial_graph/_util.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/tests/test_attributes.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/tests/test_dtype.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/tests/test_graph.py +0 -0
- {spatial_graph-0.0.3 → spatial_graph-0.0.4}/tests/test_spatial_graph.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatial-graph
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A spatial graph datastructure for python.
|
|
5
5
|
Project-URL: homepage, https://github.com/funkelab/spatial_graph
|
|
6
6
|
Project-URL: repository, https://github.com/funkelab/spatial_graph
|
|
@@ -20,7 +20,7 @@ Requires-Python: >=3.9
|
|
|
20
20
|
Requires-Dist: ct3>=3.3.3
|
|
21
21
|
Requires-Dist: numpy
|
|
22
22
|
Requires-Dist: setuptools>=75.8.0
|
|
23
|
-
Requires-Dist: witty>=v0.
|
|
23
|
+
Requires-Dist: witty>=v0.3.0
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
|
|
26
26
|
# spatial-graph
|
|
@@ -27,7 +27,7 @@ classifiers = [
|
|
|
27
27
|
"Programming Language :: Python :: 3.13",
|
|
28
28
|
"Typing :: Typed",
|
|
29
29
|
]
|
|
30
|
-
dependencies = ["witty>=v0.
|
|
30
|
+
dependencies = ["witty>=v0.3.0", "CT3>=3.3.3", "numpy", "setuptools>=75.8.0"]
|
|
31
31
|
|
|
32
32
|
[dependency-groups]
|
|
33
33
|
test = ["pytest>=8.3.5", "pytest-cov>=6.1.1"]
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Literal, overload
|
|
4
|
+
|
|
5
|
+
from .graph_base import GraphBase
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Iterator
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Graph(GraphBase):
|
|
14
|
+
directed: Literal[False] = False
|
|
15
|
+
|
|
16
|
+
def num_neighbors(self, nodes: np.ndarray) -> np.ndarray:
|
|
17
|
+
"""Return the number of neighbors for each node.
|
|
18
|
+
|
|
19
|
+
For undirected graphs, this counts all adjacent nodes regardless
|
|
20
|
+
of edge direction since edges are bidirectional.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
nodes : np.ndarray
|
|
25
|
+
Array of node identifiers to count neighbors for.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
np.ndarray
|
|
30
|
+
Array of neighbor counts for each node in the input array.
|
|
31
|
+
"""
|
|
32
|
+
return self._cgraph.num_neighbors(nodes)
|
|
33
|
+
|
|
34
|
+
@overload
|
|
35
|
+
def edges(
|
|
36
|
+
self, node: Any = ..., data: Literal[True] = ...
|
|
37
|
+
) -> Iterator[tuple[tuple, Any]]: ...
|
|
38
|
+
@overload
|
|
39
|
+
def edges(self, node: Any = ..., data: Literal[False] = ...) -> Iterator[tuple]: ...
|
|
40
|
+
def edges(self, node: Any = None, data: bool = False) -> Iterator[tuple]:
|
|
41
|
+
"""Iterate over edges in the graph.
|
|
42
|
+
|
|
43
|
+
For undirected graphs, each edge is yielded only once with nodes
|
|
44
|
+
ordered such that node1 < node2 to avoid duplicates.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
node : Any, optional
|
|
49
|
+
If provided, only iterate over edges incident to this node.
|
|
50
|
+
If None, iterate over all edges in the graph.
|
|
51
|
+
data : bool, default False
|
|
52
|
+
If True, yield (edge, edge_data) tuples. If False, yield
|
|
53
|
+
only edge tuples.
|
|
54
|
+
|
|
55
|
+
Yields
|
|
56
|
+
------
|
|
57
|
+
tuple or tuple[tuple, Any]
|
|
58
|
+
If `data=False`: tuples of (node1, node2) representing edges.
|
|
59
|
+
If `data=True`: tuples of ((node1, node2), edge_data) where
|
|
60
|
+
edge_data provides access to edge attributes.
|
|
61
|
+
"""
|
|
62
|
+
return self._cgraph.edges(node, data)
|
|
63
|
+
|
|
64
|
+
def edges_by_nodes(self, nodes: np.ndarray) -> np.ndarray:
|
|
65
|
+
"""Get all edges incident to the specified nodes.
|
|
66
|
+
|
|
67
|
+
This method provides fast access to edges incident to an array
|
|
68
|
+
of nodes. Note that edges between nodes in the input array will
|
|
69
|
+
be reported multiple times (once for each incident node).
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
nodes : np.ndarray
|
|
74
|
+
Array of node identifiers to find incident edges for.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
np.ndarray
|
|
79
|
+
2D array of shape (n_edges, 2) where each row contains
|
|
80
|
+
[node1, node2] representing an edge. For undirected graphs,
|
|
81
|
+
node1 <= node2.
|
|
82
|
+
"""
|
|
83
|
+
return self._cgraph.edges_by_nodes(nodes)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class DiGraph(GraphBase):
|
|
87
|
+
directed: Literal[True] = True
|
|
88
|
+
|
|
89
|
+
def num_in_neighbors(self, nodes: np.ndarray) -> np.ndarray:
|
|
90
|
+
"""Return the number of incoming neighbors for each node.
|
|
91
|
+
|
|
92
|
+
This counts only nodes that have edges pointing to the specified nodes
|
|
93
|
+
(i.e., predecessors).
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
nodes : np.ndarray
|
|
98
|
+
Array of node identifiers to count incoming neighbors for.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
np.ndarray
|
|
103
|
+
Array of incoming neighbor counts for each node in the input array.
|
|
104
|
+
"""
|
|
105
|
+
return self._cgraph.num_in_neighbors(nodes)
|
|
106
|
+
|
|
107
|
+
def num_out_neighbors(self, nodes: np.ndarray) -> np.ndarray:
|
|
108
|
+
"""Return the number of outgoing neighbors for each node.
|
|
109
|
+
|
|
110
|
+
This counts only nodes that the specified nodes
|
|
111
|
+
have edges pointing to (i.e., successors).
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
nodes : np.ndarray
|
|
116
|
+
Array of node identifiers to count outgoing neighbors for.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
np.ndarray
|
|
121
|
+
Array of outgoing neighbor counts for each node in the input array.
|
|
122
|
+
"""
|
|
123
|
+
return self._cgraph.num_out_neighbors(nodes)
|
|
124
|
+
|
|
125
|
+
@overload
|
|
126
|
+
def in_edges(
|
|
127
|
+
self, node: Any = ..., data: Literal[True] = ...
|
|
128
|
+
) -> Iterator[tuple[tuple, Any]]: ...
|
|
129
|
+
@overload
|
|
130
|
+
def in_edges(
|
|
131
|
+
self, node: Any = ..., data: Literal[False] = ...
|
|
132
|
+
) -> Iterator[tuple]: ...
|
|
133
|
+
def in_edges(self, node: Any = None, data: bool = False) -> Iterator[tuple]:
|
|
134
|
+
"""Iterate over incoming edges to a node.
|
|
135
|
+
|
|
136
|
+
Only edges directed toward the specified node are yielded.
|
|
137
|
+
|
|
138
|
+
Parameters
|
|
139
|
+
----------
|
|
140
|
+
node : Any
|
|
141
|
+
The target node to find incoming edges for.
|
|
142
|
+
data : bool
|
|
143
|
+
If True, yield (edge, edge_data) tuples. If False, yield
|
|
144
|
+
only edge tuples.
|
|
145
|
+
|
|
146
|
+
Yields
|
|
147
|
+
------
|
|
148
|
+
tuple or tuple[tuple, Any]
|
|
149
|
+
If `data=False`: tuples of (source_node, target_node) representing
|
|
150
|
+
incoming edges where target_node is the specified node.
|
|
151
|
+
If `data=True`: tuples of ((source_node, target_node), edge_data)
|
|
152
|
+
where edge_data provides access to edge attributes.
|
|
153
|
+
"""
|
|
154
|
+
return self._cgraph.in_edges(node, data)
|
|
155
|
+
|
|
156
|
+
def in_edges_by_nodes(self, nodes: np.ndarray) -> np.ndarray:
|
|
157
|
+
"""Get all incoming edges to the specified nodes.
|
|
158
|
+
|
|
159
|
+
This method provides fast access to incoming edges for an array
|
|
160
|
+
of nodes. Edges between nodes in the input array will be reported
|
|
161
|
+
multiple times if both source and target are in the array.
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
nodes : np.ndarray
|
|
166
|
+
Array of node identifiers to find incoming edges for.
|
|
167
|
+
|
|
168
|
+
Returns
|
|
169
|
+
-------
|
|
170
|
+
np.ndarray
|
|
171
|
+
2D array of shape (n_edges, 2) where each row contains
|
|
172
|
+
[source_node, target_node] representing an incoming edge
|
|
173
|
+
to one of the specified nodes.
|
|
174
|
+
"""
|
|
175
|
+
return self._cgraph.in_edges_by_nodes(nodes)
|
|
176
|
+
|
|
177
|
+
@overload
|
|
178
|
+
def out_edges(
|
|
179
|
+
self, node: Any = ..., data: Literal[True] = ...
|
|
180
|
+
) -> Iterator[tuple[tuple, Any]]: ...
|
|
181
|
+
@overload
|
|
182
|
+
def out_edges(
|
|
183
|
+
self, node: Any = ..., data: Literal[False] = ...
|
|
184
|
+
) -> Iterator[tuple]: ...
|
|
185
|
+
def out_edges(self, node: Any = None, data: bool = False) -> Iterator[tuple]:
|
|
186
|
+
"""Iterate over outgoing edges from a node.
|
|
187
|
+
|
|
188
|
+
Only edges directed away from the specified node are yielded.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
node : Any
|
|
193
|
+
The source node to find outgoing edges for.
|
|
194
|
+
data : bool
|
|
195
|
+
If True, yield (edge, edge_data) tuples. If False, yield
|
|
196
|
+
only edge tuples.
|
|
197
|
+
|
|
198
|
+
Yields
|
|
199
|
+
------
|
|
200
|
+
tuple or tuple[tuple, Any]
|
|
201
|
+
If `data=False`: tuples of (source_node, target_node) representing
|
|
202
|
+
outgoing edges where source_node is the specified node.
|
|
203
|
+
If `data=True`: tuples of ((source_node, target_node), edge_data)
|
|
204
|
+
where edge_data provides access to edge attributes.
|
|
205
|
+
"""
|
|
206
|
+
return self._cgraph.out_edges(node, data)
|
|
207
|
+
|
|
208
|
+
def out_edges_by_nodes(self, nodes: np.ndarray) -> np.ndarray:
|
|
209
|
+
"""Get all outgoing edges from the specified nodes.
|
|
210
|
+
|
|
211
|
+
This method provides fast access to outgoing edges for an array
|
|
212
|
+
of nodes. Edges between nodes in the input array will be reported
|
|
213
|
+
multiple times if both source and target are in the array.
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
nodes : np.ndarray
|
|
218
|
+
Array of node identifiers to find outgoing edges for.
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
np.ndarray
|
|
223
|
+
2D array of shape (n_edges, 2) where each row contains
|
|
224
|
+
[source_node, target_node] representing an outgoing edge
|
|
225
|
+
from one of the specified nodes.
|
|
226
|
+
"""
|
|
227
|
+
return self._cgraph.out_edges_by_nodes(nodes)
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
import witty
|
|
8
|
+
from Cheetah.Template import Template
|
|
9
|
+
|
|
10
|
+
from spatial_graph._dtypes import DType
|
|
11
|
+
|
|
12
|
+
from .views import EdgeAttrs, NodeAttrs
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Iterator, Mapping
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Set platform-specific compile arguments
|
|
21
|
+
if sys.platform == "win32": # pragma: no cover
|
|
22
|
+
# Use /O2 for optimization and /std:c++20 for C++20
|
|
23
|
+
EXTRA_COMPILE_ARGS = ["/O2", "/std:c++20", "/wd4101"]
|
|
24
|
+
else:
|
|
25
|
+
# -O3 for optimization and -std=c++20 for C++20
|
|
26
|
+
EXTRA_COMPILE_ARGS = [
|
|
27
|
+
"-O3",
|
|
28
|
+
"-std=c++20",
|
|
29
|
+
"-Wno-unused-variable",
|
|
30
|
+
"-Wno-unreachable-code",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
SRC_DIR = Path(__file__).parent
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _build_wrapper(
|
|
37
|
+
node_dtype: str,
|
|
38
|
+
node_attr_dtypes: Mapping[str, str] | None = None,
|
|
39
|
+
edge_attr_dtypes: Mapping[str, str] | None = None,
|
|
40
|
+
directed: bool = False,
|
|
41
|
+
) -> str:
|
|
42
|
+
if node_attr_dtypes is None:
|
|
43
|
+
node_attr_dtypes = {}
|
|
44
|
+
if edge_attr_dtypes is None:
|
|
45
|
+
edge_attr_dtypes = {}
|
|
46
|
+
if not all(str.isidentifier(name) for name in node_attr_dtypes):
|
|
47
|
+
raise ValueError("Node attribute names must be valid identifiers")
|
|
48
|
+
if not all(str.isidentifier(name) for name in edge_attr_dtypes):
|
|
49
|
+
raise ValueError("Edge attribute names must be valid identifiers")
|
|
50
|
+
|
|
51
|
+
wrapper_template = Template(
|
|
52
|
+
file=str(SRC_DIR / "wrapper_template.pyx"),
|
|
53
|
+
compilerSettings={"directiveStartToken": "%"},
|
|
54
|
+
)
|
|
55
|
+
wrapper_template.node_dtype = DType(node_dtype)
|
|
56
|
+
wrapper_template.node_attr_dtypes = {
|
|
57
|
+
name: DType(dtype) for name, dtype in node_attr_dtypes.items()
|
|
58
|
+
}
|
|
59
|
+
wrapper_template.edge_attr_dtypes = {
|
|
60
|
+
name: DType(dtype) for name, dtype in edge_attr_dtypes.items()
|
|
61
|
+
}
|
|
62
|
+
wrapper_template.directed = directed
|
|
63
|
+
|
|
64
|
+
return str(wrapper_template)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _compile_graph(
|
|
68
|
+
node_dtype: str,
|
|
69
|
+
node_attr_dtypes: Mapping[str, str] | None = None,
|
|
70
|
+
edge_attr_dtypes: Mapping[str, str] | None = None,
|
|
71
|
+
directed: bool = False,
|
|
72
|
+
) -> type:
|
|
73
|
+
wrapper_template = _build_wrapper(
|
|
74
|
+
node_dtype=node_dtype,
|
|
75
|
+
node_attr_dtypes=node_attr_dtypes,
|
|
76
|
+
edge_attr_dtypes=edge_attr_dtypes,
|
|
77
|
+
directed=directed,
|
|
78
|
+
)
|
|
79
|
+
wrapper = witty.compile_cython(
|
|
80
|
+
wrapper_template,
|
|
81
|
+
source_files=[str(SRC_DIR / "src" / "graph_lite.h")],
|
|
82
|
+
extra_compile_args=EXTRA_COMPILE_ARGS,
|
|
83
|
+
include_dirs=[str(SRC_DIR)],
|
|
84
|
+
language="c++",
|
|
85
|
+
quiet=True,
|
|
86
|
+
)
|
|
87
|
+
return wrapper.Graph
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class GraphBase:
|
|
91
|
+
directed: bool = False
|
|
92
|
+
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
node_dtype: str,
|
|
96
|
+
node_attr_dtypes: Mapping[str, str] | None = None,
|
|
97
|
+
edge_attr_dtypes: Mapping[str, str] | None = None,
|
|
98
|
+
):
|
|
99
|
+
super().__init__()
|
|
100
|
+
self.node_dtype = node_dtype
|
|
101
|
+
self.node_attr_dtypes = node_attr_dtypes or {}
|
|
102
|
+
self.edge_attr_dtypes = edge_attr_dtypes or {}
|
|
103
|
+
|
|
104
|
+
cgraph_cls = _compile_graph(
|
|
105
|
+
node_dtype=self.node_dtype,
|
|
106
|
+
node_attr_dtypes=self.node_attr_dtypes,
|
|
107
|
+
edge_attr_dtypes=self.edge_attr_dtypes,
|
|
108
|
+
directed=self.directed,
|
|
109
|
+
)
|
|
110
|
+
self._cgraph = cgraph_cls()
|
|
111
|
+
|
|
112
|
+
self.node_attrs = NodeAttrs(self)
|
|
113
|
+
self.edge_attrs = EdgeAttrs(self)
|
|
114
|
+
|
|
115
|
+
def add_node(self, node: Any, *data: Any, **kwargs: Any) -> int:
|
|
116
|
+
"""Add a single node to the graph.
|
|
117
|
+
|
|
118
|
+
The node attributes provided via *data and **kwargs must match the
|
|
119
|
+
data types and names specified in `node_attr_dtypes` when the graph
|
|
120
|
+
was created.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
node : Any
|
|
125
|
+
The node identifier to add to the graph.
|
|
126
|
+
*data : Any
|
|
127
|
+
Positional arguments for node attributes. Names/number of args
|
|
128
|
+
must match the `node_attr_dtypes`.
|
|
129
|
+
**kwargs : Any
|
|
130
|
+
Keyword arguments for node attributes. Names/number of kwargs
|
|
131
|
+
must match the `node_attr_dtypes`.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
int
|
|
136
|
+
Number of nodes added (1 if successful, 0 if node already exists).
|
|
137
|
+
"""
|
|
138
|
+
return self._cgraph.add_node(node, *data, **kwargs)
|
|
139
|
+
|
|
140
|
+
def add_nodes(self, nodes: np.ndarray, *data: Any, **kwargs: Any) -> int:
|
|
141
|
+
"""Add multiple nodes to the graph.
|
|
142
|
+
|
|
143
|
+
Node attributes provided via *data and **kwargs must match the
|
|
144
|
+
data types and names specified in `node_attr_dtypes`. Each attribute
|
|
145
|
+
array must have the same length as the `nodes` array.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
nodes : np.ndarray
|
|
150
|
+
Array of node identifiers to add to the graph.
|
|
151
|
+
*data : Any
|
|
152
|
+
Positional arguments for node attributes. Each argument should be
|
|
153
|
+
an array with length matching `nodes`. Names/number of args must
|
|
154
|
+
match the `node_attr_dtypes`.
|
|
155
|
+
**kwargs : Any
|
|
156
|
+
Keyword arguments for node attributes. Each argument should be
|
|
157
|
+
an array with length matching `nodes`. Names/number of kwargs
|
|
158
|
+
must match the `node_attr_dtypes`.
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
int
|
|
163
|
+
Number of nodes successfully added.
|
|
164
|
+
"""
|
|
165
|
+
return self._cgraph.add_nodes(nodes, *data, **kwargs)
|
|
166
|
+
|
|
167
|
+
def add_edge(self, edge: np.ndarray, *args: Any, **kwargs: Any) -> int:
|
|
168
|
+
"""Add an edge to the graph.
|
|
169
|
+
|
|
170
|
+
The edge attributes provided via *args and **kwargs must match the
|
|
171
|
+
data types and names specified in `edge_attr_dtypes` when the graph
|
|
172
|
+
was created.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
edge : np.ndarray
|
|
177
|
+
Array of length 2 containing [source_node, target_node].
|
|
178
|
+
*args : Any
|
|
179
|
+
Positional arguments for edge attributes. Names/number of args
|
|
180
|
+
must match the `edge_attr_dtypes`.
|
|
181
|
+
**kwargs : Any
|
|
182
|
+
Keyword arguments for edge attributes. Names/number of kwargs
|
|
183
|
+
must match the `edge_attr_dtypes`.
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
int
|
|
188
|
+
Number of edges added (1 if successful, 0 if edge already exists).
|
|
189
|
+
"""
|
|
190
|
+
return self._cgraph.add_edge(edge, *args, **kwargs)
|
|
191
|
+
|
|
192
|
+
def add_edges(
|
|
193
|
+
self, edges: np.ndarray, *args: np.ndarray, **kwargs: np.ndarray
|
|
194
|
+
) -> int:
|
|
195
|
+
"""Add multiple edges to the graph.
|
|
196
|
+
|
|
197
|
+
Edge attributes provided via *args and **kwargs must match the
|
|
198
|
+
data types and names specified in `edge_attr_dtypes`. Each attribute
|
|
199
|
+
array must have the same length as the number of edges.
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
edges : np.ndarray
|
|
204
|
+
2D array of shape (n_edges, 2) where each row contains
|
|
205
|
+
[source_node, target_node].
|
|
206
|
+
*args : np.ndarray
|
|
207
|
+
Positional arguments for edge attributes. Each argument should be
|
|
208
|
+
an array with length matching the number of edges. Names/number
|
|
209
|
+
of args must match the `edge_attr_dtypes`.
|
|
210
|
+
**kwargs : np.ndarray
|
|
211
|
+
Keyword arguments for edge attributes. Each argument should be
|
|
212
|
+
an array with length matching the number of edges. Names/number
|
|
213
|
+
of kwargs must match the `edge_attr_dtypes`.
|
|
214
|
+
|
|
215
|
+
Returns
|
|
216
|
+
-------
|
|
217
|
+
int
|
|
218
|
+
Number of edges successfully added.
|
|
219
|
+
"""
|
|
220
|
+
return self._cgraph.add_edges(edges, *args, **kwargs)
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def nodes(self) -> np.ndarray:
|
|
224
|
+
"""Get all node IDs in the graph.
|
|
225
|
+
|
|
226
|
+
The returned array is a copy and modifications will not affect
|
|
227
|
+
the graph structure.
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
np.ndarray
|
|
232
|
+
Array containing all node identifiers in the graph, ordered
|
|
233
|
+
by insertion order (earliest added first).
|
|
234
|
+
"""
|
|
235
|
+
return self._cgraph.nodes()
|
|
236
|
+
|
|
237
|
+
def remove_node(self, node: Any) -> None:
|
|
238
|
+
"""Remove a single node from the graph.
|
|
239
|
+
|
|
240
|
+
Removing a node will also remove all edges incident to that node.
|
|
241
|
+
|
|
242
|
+
Parameters
|
|
243
|
+
----------
|
|
244
|
+
node : Any
|
|
245
|
+
The node identifier to remove from the graph.
|
|
246
|
+
"""
|
|
247
|
+
return self._cgraph.remove_node(node)
|
|
248
|
+
|
|
249
|
+
def remove_nodes(self, nodes: np.ndarray) -> None:
|
|
250
|
+
"""Remove multiple nodes from the graph.
|
|
251
|
+
|
|
252
|
+
Removing nodes will also remove all edges incident to those nodes.
|
|
253
|
+
|
|
254
|
+
Parameters
|
|
255
|
+
----------
|
|
256
|
+
nodes : np.ndarray
|
|
257
|
+
Array of node identifiers to remove from the graph.
|
|
258
|
+
"""
|
|
259
|
+
return self._cgraph.remove_nodes(nodes)
|
|
260
|
+
|
|
261
|
+
def nodes_data(self, nodes: np.ndarray | None = None) -> Iterator[tuple[Any, Any]]:
|
|
262
|
+
"""Iterate over nodes and their associated data.
|
|
263
|
+
|
|
264
|
+
The node_data object provides access to node attributes as defined
|
|
265
|
+
by the `node_attr_dtypes` when the graph was created.
|
|
266
|
+
|
|
267
|
+
Parameters
|
|
268
|
+
----------
|
|
269
|
+
nodes : np.ndarray, optional
|
|
270
|
+
Array of specific node identifiers to iterate over. If None,
|
|
271
|
+
iterates over all nodes in the graph.
|
|
272
|
+
|
|
273
|
+
Yields
|
|
274
|
+
------
|
|
275
|
+
tuple[Any, Any]
|
|
276
|
+
Tuples of (node_id, node_data) where node_data is a view object
|
|
277
|
+
providing access to the node's attributes.
|
|
278
|
+
"""
|
|
279
|
+
return self._cgraph.nodes_data(nodes)
|
|
280
|
+
|
|
281
|
+
def edges_data(self, us: np.ndarray, vs: np.ndarray) -> Iterator:
|
|
282
|
+
"""Iterate over edge data for specified edges.
|
|
283
|
+
|
|
284
|
+
The arrays `us` and `vs` must have the same length. The edge data
|
|
285
|
+
objects provide access to edge attributes as defined by the
|
|
286
|
+
`edge_attr_dtypes` when the graph was created.
|
|
287
|
+
|
|
288
|
+
Parameters
|
|
289
|
+
----------
|
|
290
|
+
us : np.ndarray
|
|
291
|
+
Array of source node identifiers.
|
|
292
|
+
vs : np.ndarray
|
|
293
|
+
Array of target node identifiers.
|
|
294
|
+
|
|
295
|
+
Yields
|
|
296
|
+
------
|
|
297
|
+
Any
|
|
298
|
+
Edge data view objects providing access to edge attributes
|
|
299
|
+
for each edge (us[i], vs[i]).
|
|
300
|
+
"""
|
|
301
|
+
return self._cgraph.edges_data(us, vs)
|
|
302
|
+
|
|
303
|
+
def num_edges(self) -> int:
|
|
304
|
+
"""Get the total number of edges in the graph.
|
|
305
|
+
|
|
306
|
+
Returns
|
|
307
|
+
-------
|
|
308
|
+
int
|
|
309
|
+
The number of edges in the graph.
|
|
310
|
+
"""
|
|
311
|
+
return self._cgraph.num_edges()
|
|
312
|
+
|
|
313
|
+
def __len__(self) -> int:
|
|
314
|
+
"""Return the number of nodes in the graph.
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
int
|
|
319
|
+
The number of nodes in the graph.
|
|
320
|
+
"""
|
|
321
|
+
return self._cgraph.__len__()
|