edsger 0.1.5__cp311-cp311-macosx_11_0_arm64.whl → 0.1.6__cp311-cp311-macosx_11_0_arm64.whl

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.
Binary file
edsger/bfs.pyx ADDED
@@ -0,0 +1,243 @@
1
+ """
2
+ Breadth-First Search (BFS) implementation.
3
+
4
+ cpdef functions:
5
+
6
+ - bfs_csr
7
+ Compute BFS tree using CSR format (forward traversal). Returns predecessors.
8
+ - bfs_csc
9
+ Compute BFS tree using CSC format (backward traversal). Returns predecessors.
10
+ """
11
+
12
+ # cython: language_level=3
13
+ # cython: boundscheck=False
14
+ # cython: wraparound=False
15
+ # cython: embedsignature=False
16
+ # cython: cdivision=True
17
+ # cython: initializedcheck=False
18
+
19
+ cimport numpy as cnp
20
+ import numpy as np
21
+
22
+ cpdef cnp.ndarray bfs_csr(
23
+ cnp.uint32_t[::1] csr_indptr,
24
+ cnp.uint32_t[::1] csr_indices,
25
+ int start_vert_idx,
26
+ int vertex_count,
27
+ int sentinel=-9999):
28
+ """
29
+ Compute BFS tree using CSR format (forward traversal from start vertex).
30
+
31
+ Parameters
32
+ ----------
33
+ csr_indptr : cnp.uint32_t[::1]
34
+ Pointers in the CSR format
35
+ csr_indices : cnp.uint32_t[::1]
36
+ Indices in the CSR format
37
+ start_vert_idx : int
38
+ Starting vertex index
39
+ vertex_count : int
40
+ Total number of vertices
41
+ sentinel : int, optional
42
+ Sentinel value for unreachable nodes and start vertex (default: -9999)
43
+
44
+ Returns
45
+ -------
46
+ predecessors : cnp.ndarray
47
+ Predecessor array where predecessors[i] contains the predecessor
48
+ of vertex i in the BFS tree. Unreachable vertices and the start
49
+ vertex have the sentinel value.
50
+ """
51
+
52
+ cdef:
53
+ size_t tail_vert_idx, head_vert_idx, idx
54
+ size_t queue_head = 0, queue_tail = 0
55
+ size_t start = <size_t>start_vert_idx
56
+ cnp.uint32_t[::1] queue
57
+ cnp.int8_t[::1] visited
58
+ cnp.int32_t[::1] predecessors
59
+
60
+ # Allocate arrays
61
+ queue = np.empty(vertex_count, dtype=np.uint32)
62
+ visited = np.zeros(vertex_count, dtype=np.int8)
63
+ predecessors = np.full(vertex_count, sentinel, dtype=np.int32)
64
+
65
+ with nogil:
66
+ # Initialize: mark start vertex as visited and enqueue it
67
+ visited[start] = 1
68
+ queue[queue_tail] = start
69
+ queue_tail += 1
70
+
71
+ # BFS main loop
72
+ while queue_head < queue_tail:
73
+ tail_vert_idx = queue[queue_head]
74
+ queue_head += 1
75
+
76
+ # Process all outgoing edges from tail_vert_idx
77
+ for idx in range(<size_t>csr_indptr[tail_vert_idx],
78
+ <size_t>csr_indptr[tail_vert_idx + 1]):
79
+ head_vert_idx = <size_t>csr_indices[idx]
80
+
81
+ # If not visited, mark as visited and enqueue
82
+ if visited[head_vert_idx] == 0:
83
+ visited[head_vert_idx] = 1
84
+ predecessors[head_vert_idx] = <int>tail_vert_idx
85
+ queue[queue_tail] = head_vert_idx
86
+ queue_tail += 1
87
+
88
+ # Convert to numpy array
89
+ return np.asarray(predecessors)
90
+
91
+
92
+ cpdef cnp.ndarray bfs_csc(
93
+ cnp.uint32_t[::1] csc_indptr,
94
+ cnp.uint32_t[::1] csc_indices,
95
+ int start_vert_idx,
96
+ int vertex_count,
97
+ int sentinel=-9999):
98
+ """
99
+ Compute BFS tree using CSC format (backward traversal from start vertex).
100
+
101
+ Parameters
102
+ ----------
103
+ csc_indptr : cnp.uint32_t[::1]
104
+ Pointers in the CSC format
105
+ csc_indices : cnp.uint32_t[::1]
106
+ Indices in the CSC format
107
+ start_vert_idx : int
108
+ Starting vertex index
109
+ vertex_count : int
110
+ Total number of vertices
111
+ sentinel : int, optional
112
+ Sentinel value for unreachable nodes and start vertex (default: -9999)
113
+
114
+ Returns
115
+ -------
116
+ predecessors : cnp.ndarray
117
+ Predecessor array where predecessors[i] contains the successor
118
+ of vertex i in the BFS tree (since we're traversing backward).
119
+ Unreachable vertices and the start vertex have the sentinel value.
120
+ """
121
+
122
+ cdef:
123
+ size_t tail_vert_idx, head_vert_idx, idx
124
+ size_t queue_head = 0, queue_tail = 0
125
+ size_t start = <size_t>start_vert_idx
126
+ cnp.uint32_t[::1] queue
127
+ cnp.int8_t[::1] visited
128
+ cnp.int32_t[::1] predecessors
129
+
130
+ # Allocate arrays
131
+ queue = np.empty(vertex_count, dtype=np.uint32)
132
+ visited = np.zeros(vertex_count, dtype=np.int8)
133
+ predecessors = np.full(vertex_count, sentinel, dtype=np.int32)
134
+
135
+ with nogil:
136
+ # Initialize: mark start vertex as visited and enqueue it
137
+ visited[start] = 1
138
+ queue[queue_tail] = start
139
+ queue_tail += 1
140
+
141
+ # BFS main loop (processing incoming edges using CSC)
142
+ while queue_head < queue_tail:
143
+ head_vert_idx = queue[queue_head]
144
+ queue_head += 1
145
+
146
+ # Process all incoming edges to head_vert_idx
147
+ for idx in range(<size_t>csc_indptr[head_vert_idx],
148
+ <size_t>csc_indptr[head_vert_idx + 1]):
149
+ tail_vert_idx = <size_t>csc_indices[idx]
150
+
151
+ # If not visited, mark as visited and enqueue
152
+ if visited[tail_vert_idx] == 0:
153
+ visited[tail_vert_idx] = 1
154
+ predecessors[tail_vert_idx] = <int>head_vert_idx
155
+ queue[queue_tail] = tail_vert_idx
156
+ queue_tail += 1
157
+
158
+ # Convert to numpy array
159
+ return np.asarray(predecessors)
160
+
161
+
162
+ # ============================================================================ #
163
+ # tests #
164
+ # ============================================================================ #
165
+
166
+
167
+ cdef generate_simple_graph_csr():
168
+ """
169
+ Generate a simple directed graph in CSR format.
170
+
171
+ Graph structure:
172
+ 0 -> 1 -> 3
173
+ 0 -> 2 -> 3
174
+
175
+ 4 vertices, 4 edges
176
+ """
177
+ csr_indptr = np.array([0, 2, 3, 4, 4], dtype=np.uint32)
178
+ csr_indices = np.array([1, 2, 3, 3], dtype=np.uint32)
179
+ return csr_indptr, csr_indices
180
+
181
+
182
+ cdef generate_simple_graph_csc():
183
+ """
184
+ Generate a simple directed graph in CSC format.
185
+
186
+ Graph structure (same as CSR version):
187
+ 0 -> 1 -> 3
188
+ 0 -> 2 -> 3
189
+
190
+ 4 vertices, 4 edges
191
+ """
192
+ csc_indptr = np.array([0, 0, 1, 2, 4], dtype=np.uint32)
193
+ csc_indices = np.array([0, 0, 1, 2], dtype=np.uint32)
194
+ return csc_indptr, csc_indices
195
+
196
+
197
+ cpdef test_bfs_csr_01():
198
+ """Test BFS CSR on simple graph from vertex 0."""
199
+ cdef int UNREACHABLE = -9999
200
+ csr_indptr, csr_indices = generate_simple_graph_csr()
201
+
202
+ predecessors = bfs_csr(csr_indptr, csr_indices, 0, 4)
203
+
204
+ # Expected: 0 is start, 1 and 2 have predecessor 0, 3 has predecessor 1 or 2
205
+ assert predecessors[0] == UNREACHABLE # start vertex
206
+ assert predecessors[1] == 0
207
+ assert predecessors[2] == 0
208
+ assert predecessors[3] in [1, 2] # could be reached from either 1 or 2
209
+
210
+
211
+ cpdef test_bfs_csc_01():
212
+ """Test BFS CSC on simple graph to vertex 3."""
213
+ cdef int UNREACHABLE = -9999
214
+ csc_indptr, csc_indices = generate_simple_graph_csc()
215
+
216
+ predecessors = bfs_csc(csc_indptr, csc_indices, 3, 4)
217
+
218
+ # Expected: working backward from 3
219
+ assert predecessors[3] == UNREACHABLE # start vertex
220
+ assert predecessors[1] in [3, UNREACHABLE] or predecessors[2] in [3, UNREACHABLE]
221
+ assert predecessors[0] in [1, 2, UNREACHABLE]
222
+
223
+
224
+ cpdef test_bfs_unreachable():
225
+ """Test BFS with unreachable vertices."""
226
+ cdef int UNREACHABLE = -9999
227
+ # Graph: 0 -> 1, 2 -> 3 (two disconnected components)
228
+ csr_indptr = np.array([0, 1, 1, 2, 2], dtype=np.uint32)
229
+ csr_indices = np.array([1, 3], dtype=np.uint32)
230
+
231
+ predecessors = bfs_csr(csr_indptr, csr_indices, 0, 4)
232
+
233
+ # From 0, can reach 1 but not 2 or 3
234
+ assert predecessors[0] == UNREACHABLE # start
235
+ assert predecessors[1] == 0
236
+ assert predecessors[2] == UNREACHABLE # unreachable
237
+ assert predecessors[3] == UNREACHABLE # unreachable
238
+
239
+
240
+ # author : Francois Pacull
241
+ # copyright : Architecture & Performance
242
+ # email: francois.pacull@architecture-performance.fr
243
+ # license : MIT