spatial-graph 0.0.1__py3-none-any.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.
@@ -0,0 +1,654 @@
1
+ from cython cimport view
2
+ from cython.operator cimport dereference as deref, preincrement as inc
3
+ from libc.stdint cimport *
4
+ from libcpp.utility cimport pair
5
+ import numpy as np
6
+
7
+
8
+ cdef extern from *:
9
+ """
10
+ #include "src/graph_lite.h"
11
+
12
+ // partial template instantiation of graph_lite::Graph as
13
+ // GraphTmpl
14
+ template<typename NodeType, typename NodeData, typename EdgeData>
15
+ class GraphTmpl : public graph_lite::Graph<
16
+ NodeType,
17
+ NodeData,
18
+ EdgeData,
19
+ %if $directed
20
+ graph_lite::EdgeDirection::DIRECTED,
21
+ %else
22
+ graph_lite::EdgeDirection::UNDIRECTED,
23
+ %end if
24
+ graph_lite::MultiEdge::DISALLOWED,
25
+ graph_lite::SelfLoop::DISALLOWED,
26
+ graph_lite::Map::UNORDERED_MAP,
27
+ graph_lite::Container::VEC
28
+ > {};
29
+ """
30
+
31
+ cdef cppclass GraphTmpl[NodeType, NodeData, EdgeData]:
32
+
33
+ cppclass Iterator:
34
+ NodeType operator*()
35
+ Iterator operator++()
36
+ bint operator==(Iterator)
37
+ bint operator!=(Iterator)
38
+
39
+ cppclass EdgePropIterWrap[ED]:
40
+ ED& prop()
41
+
42
+ cppclass NeighborsIterator:
43
+ pair[NodeType, EdgePropIterWrap[EdgeData]] operator*()
44
+ NeighborsIterator operator++()
45
+ bint operator==(NeighborsIterator)
46
+ bint operator!=(NeighborsIterator)
47
+
48
+ int add_node_with_prop(NodeType& node, NodeData& prop)
49
+
50
+ int add_edge_with_prop(NodeType& source, NodeType& target, EdgeData& prop)
51
+
52
+ NodeData& node_prop[T](T& node) except +
53
+
54
+ EdgeData& edge_prop[T](T& u, T& v) except +
55
+
56
+ %if $directed
57
+ pair[NeighborsIterator, NeighborsIterator] out_neighbors(Iterator& node)
58
+ pair[NeighborsIterator, NeighborsIterator] out_neighbors(NodeType& node)
59
+ pair[NeighborsIterator, NeighborsIterator] in_neighbors(Iterator& node)
60
+ pair[NeighborsIterator, NeighborsIterator] in_neighbors(NodeType& node)
61
+ %else
62
+ pair[NeighborsIterator, NeighborsIterator] neighbors(Iterator& node)
63
+ pair[NeighborsIterator, NeighborsIterator] neighbors(NodeType& node)
64
+ %end if
65
+
66
+ int remove_nodes(NodeType& node)
67
+
68
+ %if $directed
69
+ int count_in_neighbors(NodeType& node)
70
+ int count_out_neighbors(NodeType& node)
71
+ %else
72
+ int count_neighbors(NodeType& node)
73
+ %end if
74
+
75
+ size_t size() const
76
+
77
+ size_t num_edges() const
78
+
79
+ Iterator begin()
80
+
81
+ Iterator end()
82
+
83
+ ctypedef $node_dtype.to_pyxtype() NodeType
84
+
85
+ %for class_name, dtypes in [
86
+ ("NodeData", $node_attr_dtypes),
87
+ ("EdgeData", $edge_attr_dtypes)
88
+ ]
89
+ cdef extern from *:
90
+ """
91
+ class $class_name {
92
+ public:
93
+ ${class_name}() {};
94
+ %if $dtypes
95
+ ${class_name}(
96
+ %set $sep=""
97
+ %for name, dtype in $dtypes.items()
98
+ $sep$dtype.to_c_decl("_" + $name)
99
+ %set $sep=", "
100
+ %end for
101
+ ) :
102
+ %set $sep=""
103
+ %for name, dtype in $dtypes.items()
104
+ $sep
105
+ %if $dtype.is_array
106
+ ${name}{
107
+ %set $isep=""
108
+ %for i in range($dtype.size)
109
+ ${isep}_${name}[$i]
110
+ %set $isep=", "
111
+ %end for
112
+ }
113
+ %else
114
+ ${name}(_$name)%slurp
115
+ %end if
116
+ %set $sep=", "
117
+ %end for
118
+ {};
119
+ %end if
120
+
121
+ %for name, dtype in $dtypes.items()
122
+ $dtype.to_c_decl($name);
123
+ %end for
124
+ };
125
+ """
126
+
127
+ cdef cppclass $class_name:
128
+
129
+ ${class_name}(
130
+ %set $sep=""
131
+ %for name, dtype in $dtypes.items()
132
+ $sep$dtype.to_c_decl("_" + $name)
133
+ %set $sep=", "
134
+ %end for
135
+ ) except +
136
+
137
+ %for name, dtype in $dtypes.items()
138
+ $dtype.to_pyxtype() $name
139
+ %end for
140
+
141
+ cdef class ${class_name}View:
142
+
143
+ cdef $class_name* _ptr
144
+
145
+ cdef set_ptr(self, $class_name* ptr):
146
+ self._ptr = ptr
147
+
148
+ %for name, dtype in $dtypes.items():
149
+ @property
150
+ def ${name}(self):
151
+ %if $dtype.is_array
152
+ return <${dtype.base_c_type}[:${dtype.size}]>(self._ptr.${name})
153
+ %else
154
+ return self._ptr.$name
155
+ %end if
156
+
157
+ @${name}.setter
158
+ def ${name}(self, value):
159
+ self._ptr.$name = value
160
+
161
+ %end for
162
+
163
+ %end for
164
+
165
+ ctypedef GraphTmpl[NodeType, NodeData, EdgeData] GraphType
166
+ ctypedef GraphType.Iterator NodeIterator
167
+ ctypedef GraphType.NeighborsIterator NeighborsIterator
168
+
169
+
170
+ cdef class Graph:
171
+
172
+ cdef GraphType _graph
173
+
174
+ %for kind, Kind, dtypes in [
175
+ ("node", "Node", $node_attr_dtypes),
176
+ ("edge", "Edge", $edge_attr_dtypes)
177
+ ]
178
+ def add_${kind}(
179
+ self,
180
+ %if kind == "node"
181
+ NodeType node,
182
+ %else
183
+ NodeType[:] edge,
184
+ %end if
185
+ %set sep=""
186
+ %for name, dtype in $dtypes.items()
187
+ $sep${dtype.to_pyxtype(use_memory_view=True)} $name
188
+ %set $sep=", "
189
+ %end for
190
+ ):
191
+
192
+ %for name, dtype in $dtypes.items()
193
+ %if dtype.is_array
194
+ cdef ${dtype.to_pyxtype()} _p_${name} = &${name}[0]
195
+ %end if
196
+ %end for
197
+
198
+ return self._graph.add_${kind}_with_prop(
199
+ %if kind == "node"
200
+ node,
201
+ %else
202
+ edge[0], edge[1],
203
+ %end if
204
+ ${Kind}Data(
205
+ %set sep=""
206
+ %for name, dtype in $dtypes.items()
207
+ %if $dtype.is_array
208
+ ${sep}_p_${name}%slurp
209
+ %else
210
+ ${sep}${name}%slurp
211
+ %end if
212
+ %set sep=", "
213
+ %end for
214
+
215
+ )
216
+ )
217
+
218
+ def add_${kind}s(
219
+ self,
220
+ %if kind == "node"
221
+ NodeType[::1] nodes,
222
+ %else
223
+ NodeType[:, :] edges,
224
+ %end if
225
+ %set sep=""
226
+ %for name, dtype in $dtypes.items()
227
+ $sep${dtype.to_pyxtype(use_memory_view=True, add_dim=True)} $name
228
+ %set $sep=", "
229
+ %end for
230
+ ):
231
+
232
+ %for name, dtype in $dtypes.items()
233
+ %if dtype.is_array
234
+ cdef ${dtype.to_pyxtype()} _p_${name}
235
+ %end if
236
+ %end for
237
+
238
+ cdef size_t num_added = 0
239
+ for i in range(len(${kind}s)):
240
+ %for name, dtype in $dtypes.items()
241
+ %if $dtype.is_array
242
+ _p_${name} = &${name}[i, 0]
243
+ %end if
244
+ %end for
245
+ num_added += self._graph.add_${kind}_with_prop(
246
+ %if kind == "node"
247
+ nodes[i],
248
+ %else
249
+ edges[i, 0], edges[i, 1],
250
+ %end if
251
+ ${Kind}Data(
252
+ %set sep=""
253
+ %for name, dtype in $dtypes.items()
254
+ %if $dtype.is_array
255
+ ${sep}_p_${name}%slurp
256
+ %else
257
+ ${sep}${name}[i]%slurp
258
+ %end if
259
+ %set $sep=", "
260
+ %end for
261
+
262
+ )
263
+ )
264
+
265
+ return num_added
266
+
267
+ %end for
268
+
269
+ def nodes(self):
270
+ """Get all node IDs."""
271
+
272
+ cdef NodeIterator it = self._graph.begin()
273
+ cdef NodeIterator end = self._graph.end()
274
+ node_ids = np.empty((self._graph.size(),), dtype="$node_dtype.base")
275
+
276
+ for i in range(self._graph.size()):
277
+ node_ids[i] = deref(it)
278
+ inc(it)
279
+
280
+ # graph_lite iterates over nodes in reverse order of addition, fix that
281
+ # here
282
+ return node_ids[::-1]
283
+
284
+ %if $directed
285
+ %set $prefixes=["in_", "out_"]
286
+ %else
287
+ %set $prefixes=[""]
288
+ %end if
289
+ %for prefix in $prefixes
290
+ def ${prefix}edges(self, node=None, bint data=False):
291
+
292
+ if node is not None:
293
+ for neighbor in self._${prefix}neighbors(<NodeType>node, data):
294
+ if data:
295
+ %if $prefix == "in_"
296
+ yield (neighbor[0], node), data
297
+ %else
298
+ yield (node, neighbor[0]), data
299
+ %end if
300
+ else:
301
+ %if $prefix == "in_"
302
+ yield (neighbor, node)
303
+ %else
304
+ yield (node, neighbor)
305
+ %end if
306
+ return
307
+
308
+ cdef NodeIterator node_it = self._graph.begin()
309
+ cdef NodeIterator node_end = self._graph.end()
310
+ cdef pair[NeighborsIterator, NeighborsIterator] view
311
+ cdef NodeType u, v
312
+ cdef EdgeDataView edge_data = EdgeDataView()
313
+
314
+ while node_it != node_end:
315
+ view = self._graph.${prefix}neighbors(node_it)
316
+ u = deref(node_it)
317
+ it = view.first
318
+ end = view.second
319
+ while it != end:
320
+ v = deref(it).first
321
+ %if not $directed
322
+ # avoid double-reporting undirected edges by returning only
323
+ # edges where u < v
324
+ if u >= v:
325
+ inc(it)
326
+ continue
327
+ %end if
328
+ %if $prefix == "in_"
329
+ edge = (v, u)
330
+ %else
331
+ edge = (u, v)
332
+ %end if
333
+ if data:
334
+ edge_data.set_ptr(&deref(it).second.prop())
335
+ yield edge, edge_data
336
+ else:
337
+ yield edge
338
+ inc(it)
339
+ inc(node_it)
340
+
341
+ # same as above, but for fast access to edges incident to an array of nodes
342
+ # NOTE: this will double-report edges between "nodes"
343
+ def ${prefix}edges_by_nodes(self, NodeType[::1] nodes):
344
+
345
+ cdef pair[NeighborsIterator, NeighborsIterator] view
346
+ cdef NodeType u, v
347
+ cdef Py_ssize_t i = 0
348
+
349
+ num_edges = self._num_${prefix}edges(nodes)
350
+ data = np.empty(shape=(num_edges, 2), dtype="$node_dtype.base")
351
+ cdef NodeType[:, ::1] edges = data
352
+
353
+ for u in nodes:
354
+ view = self._graph.${prefix}neighbors(u)
355
+ it = view.first
356
+ end = view.second
357
+ while it != end:
358
+ v = deref(it).first
359
+ %if $prefix == "in_"
360
+ edges[i, 0] = v
361
+ edges[i, 1] = u
362
+ %else
363
+ edges[i, 0] = u
364
+ edges[i, 1] = v
365
+ %end if
366
+ i += 1
367
+ inc(it)
368
+
369
+ return data[:i]
370
+ %end for
371
+
372
+ # generator access to node and edge data
373
+
374
+ def nodes_data(self, NodeType[::1] nodes = None):
375
+ cdef NodeIterator node_it = self._graph.begin()
376
+ cdef NodeIterator node_end = self._graph.end()
377
+ cdef NodeDataView node_data = NodeDataView()
378
+ if nodes is None:
379
+ while node_it != node_end:
380
+ node_data.set_ptr(&self._graph.node_prop(node_it))
381
+ yield deref(node_it), node_data
382
+ inc(node_it)
383
+ else:
384
+ for node in nodes:
385
+ node_data.set_ptr(&self._graph.node_prop(node))
386
+ yield node, node_data
387
+
388
+ def edges_data(self, NodeType[::1] us, NodeType[::1] vs):
389
+ cdef EdgeDataView edge_data = EdgeDataView()
390
+ num_edges = len(us)
391
+ for i in range(num_edges):
392
+ edge_data.set_ptr(&self._graph.edge_prop(us[i], vs[i]))
393
+ yield edge_data
394
+
395
+ # access to individual attributes (single and multiple nodes/edges)
396
+
397
+ %for name, dtype in $node_attr_dtypes.items()
398
+ def get_node_data_${name}(self, NodeType node):
399
+ %if $dtype.is_array
400
+ return np.array(self._graph.node_prop(node).${name})
401
+ %else
402
+ return self._graph.node_prop(node).${name}
403
+ %end if
404
+
405
+ def get_nodes_data_${name}(self, NodeType[:] nodes):
406
+
407
+ cdef NodeIterator node_it = self._graph.begin()
408
+ cdef NodeIterator node_end = self._graph.end()
409
+ cdef Py_ssize_t i = 0
410
+ cdef NodeData* node_data
411
+
412
+ # allocate array for data
413
+ cdef Py_ssize_t num_nodes = 0
414
+ if nodes is None:
415
+ num_nodes = self._graph.size()
416
+ else:
417
+ num_nodes = len(nodes)
418
+ data = np.empty(shape=(num_nodes,) + $dtype.shape, dtype="$dtype.base")
419
+ cdef $dtype.to_pyxtype(add_dim=True) view = data
420
+
421
+ # all nodes requested
422
+ if nodes is None:
423
+ while node_it != node_end:
424
+ %if $dtype.is_array
425
+ node_data = &self._graph.node_prop(node_it)
426
+ %for j in range($dtype.size)
427
+ view[i, $j] = node_data.${name}[$j]
428
+ %end for
429
+ %else
430
+ view[i] = self._graph.node_prop(node_it).${name}
431
+ %end if
432
+ inc(node_it)
433
+ i += 1
434
+ else:
435
+ for i in range(num_nodes):
436
+ %if $dtype.is_array
437
+ node_data = &self._graph.node_prop(nodes[i])
438
+ %for j in range($dtype.size)
439
+ view[i, $j] = node_data.${name}[$j]
440
+ %end for
441
+ %else
442
+ view[i] = self._graph.node_prop(nodes[i]).${name}
443
+ %end if
444
+
445
+ return data
446
+
447
+ def set_node_data_${name}(
448
+ self,
449
+ NodeType node,
450
+ $dtype.to_pyxtype(use_memory_view=True) $name):
451
+ %if $dtype.is_array
452
+ %for j in range($dtype.size)
453
+ self._graph.node_prop(node).${name}[$j] = ${name}[$j]
454
+ %end for
455
+ %else
456
+ self._graph.node_prop(node).${name} = $name
457
+ %end if
458
+
459
+ def set_nodes_data_${name}(
460
+ self,
461
+ NodeType[:] nodes,
462
+ $dtype.to_pyxtype(add_dim=True) $name):
463
+
464
+ cdef NodeIterator node_it = self._graph.begin()
465
+ cdef NodeIterator node_end = self._graph.end()
466
+ cdef Py_ssize_t i = 0
467
+
468
+ # all nodes requested
469
+ if nodes is None:
470
+ while node_it != node_end:
471
+ %if $dtype.is_array
472
+ node_data = &self._graph.node_prop(node_it)
473
+ %for j in range($dtype.size)
474
+ node_data.${name}[$j] = ${name}[i, $j]
475
+ %end for
476
+ %else
477
+ self._graph.node_prop(node_it).$name = ${name}[i]
478
+ %end if
479
+ inc(node_it)
480
+ i += 1
481
+ else:
482
+ assert len(nodes) == len($name)
483
+ for i in range(len(nodes)):
484
+ %if dtype.is_array
485
+ node_data = &self._graph.node_prop(nodes[i])
486
+ %for j in range($dtype.size)
487
+ node_data.${name}[$j] = ${name}[i, $j]
488
+ %end for
489
+ %else
490
+ self._graph.node_prop(nodes[i]).$name = ${name}[i]
491
+ %end if
492
+ %end for
493
+
494
+ %for name, dtype in $edge_attr_dtypes.items()
495
+ def get_edge_data_${name}(self, NodeType u, NodeType v):
496
+ %if $dtype.is_array
497
+ return np.array(self._graph.edge_prop(u, v).${name})
498
+ %else
499
+ return self._graph.edge_prop(u, v).${name}
500
+ %end if
501
+
502
+ def get_edges_data_${name}(self, NodeType[::1] us, NodeType[::1] vs):
503
+
504
+ cdef Py_ssize_t i = 0
505
+ cdef Py_ssize_t num_edges = 0
506
+ cdef NodeIterator node_it = self._graph.begin()
507
+ cdef NodeIterator node_end = self._graph.end()
508
+ cdef pair[NeighborsIterator, NeighborsIterator] edges_view
509
+ cdef NodeType u, v
510
+ cdef EdgeData* edge_data
511
+
512
+ # allocate array for data
513
+ if us is None and vs is None:
514
+ num_edges = self._graph.num_edges()
515
+ elif us is None or vs is None:
516
+ raise RuntimeError("Either both us and vs are None, or neither")
517
+ else:
518
+ num_edges = len(us)
519
+ data = np.empty(shape=(num_edges,) + $dtype.shape, dtype="$dtype.base")
520
+ cdef $dtype.to_pyxtype(add_dim=True) view = data
521
+
522
+ if us is None:
523
+
524
+
525
+ while node_it != node_end:
526
+ %if $directed
527
+ # iterate over all edges by iterating over all nodes u and
528
+ # their out neighbors
529
+ edges_view = self._graph.out_neighbors(node_it)
530
+ %else
531
+ # iterate over all edges by iterating over all nodes u and
532
+ # their neighbors v with u < v
533
+ edges_view = self._graph.neighbors(node_it)
534
+ %end if
535
+ u = deref(node_it)
536
+ it = edges_view.first
537
+ end = edges_view.second
538
+ while it != end:
539
+ v = deref(it).first
540
+ if ${directed} or u < v:
541
+ %if $dtype.is_array
542
+ edge_data = &deref(it).second.prop()
543
+ %for j in range($dtype.size)
544
+ view[i, $j] = edge_data.${name}[$j]
545
+ %end for
546
+ %else
547
+ view[i] = deref(it).second.prop().$name
548
+ %end if
549
+ i += 1
550
+ inc(it)
551
+ inc(node_it)
552
+
553
+ else:
554
+ for i in range(num_edges):
555
+ %if $dtype.is_array
556
+ edge_data = &self._graph.edge_prop(us[i], vs[i])
557
+ %for j in range($dtype.size)
558
+ view[i, $j] = edge_data.${name}[$j]
559
+ %end for
560
+ %else
561
+ view[i] = self._graph.edge_prop(us[i], vs[i]).$name
562
+ %end if
563
+
564
+ return data
565
+
566
+ def set_edge_data_${name}(
567
+ self,
568
+ NodeType u, NodeType v,
569
+ $dtype.to_pyxtype(use_memory_view=True) $name):
570
+ %if dtype.is_array
571
+ edge_data = &self._graph.edge_prop(u, v)
572
+ %for j in range($dtype.size)
573
+ edge_data.${name}[$j] = ${name}[$j]
574
+ %end for
575
+ %else
576
+ self._graph.edge_prop(u, v).${name} = $name
577
+ %end if
578
+
579
+ def set_edges_data_${name}(
580
+ self,
581
+ NodeType[::1] us, NodeType[::1] vs,
582
+ $dtype.to_pyxtype(add_dim=True) $name):
583
+
584
+ cdef Py_ssize_t i = 0
585
+ cdef Py_ssize_t num_edges = 0
586
+
587
+ assert len(us) == len(vs)
588
+ num_edges = len(us)
589
+
590
+ for i in range(num_edges):
591
+ %if $dtype.is_array
592
+ edge_data = &self._graph.edge_prop(us[i], vs[i])
593
+ %for j in range($dtype.size)
594
+ edge_data.${name}[$j] = ${name}[i, $j]
595
+ %end for
596
+ %else
597
+ self._graph.edge_prop(us[i], vs[i]).$name = ${name}[i]
598
+ %end if
599
+ %end for
600
+
601
+ # modify graph
602
+
603
+ def remove_node(self, NodeType node):
604
+ self._graph.remove_nodes(node)
605
+
606
+ def remove_nodes(self, NodeType[::1] nodes):
607
+ for i in range(len(nodes)):
608
+ self._graph.remove_nodes(nodes[i])
609
+
610
+ # read-only graph properties
611
+
612
+ %if $directed
613
+ %set $prefixes=["in_", "out_"]
614
+ %else
615
+ %set $prefixes=[""]
616
+ %end if
617
+ %for prefix in $prefixes
618
+ def count_${prefix}neighbors(self, NodeType[:] nodes):
619
+ num_nodes = len(nodes)
620
+ cdef int[:] counts = view.array(
621
+ shape=(num_nodes,),
622
+ itemsize=sizeof(int),
623
+ format="i")
624
+ for i in range(num_nodes):
625
+ counts[i] = self._graph.count_${prefix}neighbors(nodes[i])
626
+ return counts
627
+
628
+ def _${prefix}neighbors(self, NodeType node, bint data):
629
+
630
+ cdef pair[NeighborsIterator, NeighborsIterator] view = \
631
+ self._graph.${prefix}neighbors(node)
632
+ cdef NeighborsIterator it = view.first
633
+ cdef NeighborsIterator end = view.second
634
+ cdef EdgeDataView edge_data = EdgeDataView()
635
+
636
+ if data:
637
+ while it != end:
638
+ edge_data.set_ptr(&deref(it).second.prop())
639
+ yield deref(it).first, edge_data
640
+ inc(it)
641
+ else:
642
+ while it != end:
643
+ yield deref(it).first
644
+ inc(it)
645
+
646
+ def _num_${prefix}edges(self, NodeType[::1] nodes):
647
+ return np.sum(self.count_${prefix}neighbors(nodes))
648
+ %end for
649
+
650
+ def __len__(self):
651
+ return self._graph.size()
652
+
653
+ def num_edges(self):
654
+ return self._graph.num_edges()
@@ -0,0 +1,5 @@
1
+ from .point_rtree import PointRTree
2
+ from .line_rtree import LineRTree
3
+
4
+
5
+ __all__ = ["PointRTree", "LineRTree"]