scgraph 3.1.0__tar.gz → 3.2.0__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.
- {scgraph-3.1.0 → scgraph-3.2.0}/CMakeLists.txt +1 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/PKG-INFO +6 -3
- {scgraph-3.1.0 → scgraph-3.2.0}/README.md +5 -2
- {scgraph-3.1.0 → scgraph-3.2.0}/build/cp314-cp314-linux_x86_64/CMakeFiles/CheckCXX/CMakeLists.txt +1 -1
- {scgraph-3.1.0 → scgraph-3.2.0}/pyproject.toml +1 -1
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/__init__.py +5 -2
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/cpp/bindings/graph_bindings.cpp +209 -6
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/cpp/src/contraction_hierarchies.hpp +1 -1
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/cpp/src/graph.cpp +90 -1
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/cpp/src/graph.hpp +12 -2
- scgraph-3.2.0/scgraph/cpp/src/transit_node_routing.cpp +276 -0
- scgraph-3.2.0/scgraph/cpp/src/transit_node_routing.hpp +45 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/geograph.py +8 -26
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/graph.py +158 -0
- scgraph-3.2.0/scgraph/transit_node_routing.py +394 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/LICENSE +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/contraction_hierarchies.py +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/cpp/src/bmssp.hpp +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/cpp/src/contraction_hierarchies.cpp +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/cpp/src/graph_utils.cpp +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/cpp/src/graph_utils.hpp +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/graph_utils.py +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/grid.py +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/helpers/__init__.py +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/helpers/geojson.py +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/helpers/visvalingam.py +0 -0
- {scgraph-3.1.0 → scgraph-3.2.0}/scgraph/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scgraph
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Determine an approximate distance and route between two points on earth.
|
|
5
5
|
Author-Email: Connor Makowski <conmak@mit.edu>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,7 +19,7 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
# SCGraph
|
|
20
20
|
[](https://badge.fury.io/py/scgraph)
|
|
21
21
|
[](https://opensource.org/licenses/MIT)
|
|
22
|
-
[](https://
|
|
22
|
+
[](https://pepy.tech/project/scgraph/)
|
|
23
23
|
|
|
24
24
|
**A high-performance, lightweight Python library for shortest path routing on geographic and supply chain networks.**
|
|
25
25
|
|
|
@@ -191,21 +191,24 @@ All algorithms are available on `Graph` objects and accessible from `GeoGraph` v
|
|
|
191
191
|
| `algorithm_fn` | Description | Time Complexity |
|
|
192
192
|
|---|---|---|
|
|
193
193
|
| `'dijkstra'` | Standard Dijkstra; general purpose, non-negative weights (default) | O((n+m) log n) |
|
|
194
|
+
| `'dijkstra_buckets'` | Dijkstra with buckets (Dial's algorithm); efficient for non-negative weights (ideally >= 1) | O(n+m+W) |
|
|
194
195
|
| `'dijkstra_negative'` | Dijkstra with cycle detection; supports negative weights | O(n·m) |
|
|
195
196
|
| `'a_star'` | A* with optional heuristic; faster than Dijkstra with a good heuristic | O((n+m) log n) |
|
|
196
197
|
| `'bellman_ford'` | Bellman-Ford; supports negative weights, slower than Dijkstra | O(n·m) |
|
|
197
198
|
| `'bmssp'` | [BMSSP Algorithm](https://arxiv.org/pdf/2504.17033) / [Implementation](https://github.com/connor-makowski/bmsspy) | O(m log^(2/3)(n)) |
|
|
198
199
|
| `'cached_shortest_path'` | Caches shortest path tree from origin; near-instant repeated queries | O((n+m) log n) first, O(1) after |
|
|
199
200
|
| `'contraction_hierarchy'` | Bidirectional Dijkstra on preprocessed CH graph; fast arbitrary queries | O(k log k) per query |
|
|
201
|
+
| `'tnr'` | [Transit Node Routing](https://en.wikipedia.org/wiki/Transit_node_routing); extremely fast for global queries | O(1) per query (global) |
|
|
200
202
|
|
|
201
203
|
## Performance Guide
|
|
202
204
|
|
|
203
205
|
| Scenario | Recommended Approach |
|
|
204
206
|
|---|---|
|
|
205
207
|
| Single query | `dijkstra` (default) |
|
|
208
|
+
| Weights generally >= 1 | `dijkstra_buckets` |
|
|
206
209
|
| Repeated queries from one origin | `cached_shortest_path` |
|
|
207
210
|
| Large distance matrix (same graph) | `distance_matrix` method |
|
|
208
|
-
| Many arbitrary queries on a fixed graph | `contraction_hierarchy` |
|
|
211
|
+
| Many arbitrary queries on a fixed graph | `contraction_hierarchy` or `tnr` |
|
|
209
212
|
| Graph with negative weights | `dijkstra_negative` |
|
|
210
213
|
|
|
211
214
|
## Heuristic Functions (for A*)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# SCGraph
|
|
2
2
|
[](https://badge.fury.io/py/scgraph)
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](https://
|
|
4
|
+
[](https://pepy.tech/project/scgraph/)
|
|
5
5
|
|
|
6
6
|
**A high-performance, lightweight Python library for shortest path routing on geographic and supply chain networks.**
|
|
7
7
|
|
|
@@ -173,21 +173,24 @@ All algorithms are available on `Graph` objects and accessible from `GeoGraph` v
|
|
|
173
173
|
| `algorithm_fn` | Description | Time Complexity |
|
|
174
174
|
|---|---|---|
|
|
175
175
|
| `'dijkstra'` | Standard Dijkstra; general purpose, non-negative weights (default) | O((n+m) log n) |
|
|
176
|
+
| `'dijkstra_buckets'` | Dijkstra with buckets (Dial's algorithm); efficient for non-negative weights (ideally >= 1) | O(n+m+W) |
|
|
176
177
|
| `'dijkstra_negative'` | Dijkstra with cycle detection; supports negative weights | O(n·m) |
|
|
177
178
|
| `'a_star'` | A* with optional heuristic; faster than Dijkstra with a good heuristic | O((n+m) log n) |
|
|
178
179
|
| `'bellman_ford'` | Bellman-Ford; supports negative weights, slower than Dijkstra | O(n·m) |
|
|
179
180
|
| `'bmssp'` | [BMSSP Algorithm](https://arxiv.org/pdf/2504.17033) / [Implementation](https://github.com/connor-makowski/bmsspy) | O(m log^(2/3)(n)) |
|
|
180
181
|
| `'cached_shortest_path'` | Caches shortest path tree from origin; near-instant repeated queries | O((n+m) log n) first, O(1) after |
|
|
181
182
|
| `'contraction_hierarchy'` | Bidirectional Dijkstra on preprocessed CH graph; fast arbitrary queries | O(k log k) per query |
|
|
183
|
+
| `'tnr'` | [Transit Node Routing](https://en.wikipedia.org/wiki/Transit_node_routing); extremely fast for global queries | O(1) per query (global) |
|
|
182
184
|
|
|
183
185
|
## Performance Guide
|
|
184
186
|
|
|
185
187
|
| Scenario | Recommended Approach |
|
|
186
188
|
|---|---|
|
|
187
189
|
| Single query | `dijkstra` (default) |
|
|
190
|
+
| Weights generally >= 1 | `dijkstra_buckets` |
|
|
188
191
|
| Repeated queries from one origin | `cached_shortest_path` |
|
|
189
192
|
| Large distance matrix (same graph) | `distance_matrix` method |
|
|
190
|
-
| Many arbitrary queries on a fixed graph | `contraction_hierarchy` |
|
|
193
|
+
| Many arbitrary queries on a fixed graph | `contraction_hierarchy` or `tnr` |
|
|
191
194
|
| Graph with negative weights | `dijkstra_negative` |
|
|
192
195
|
|
|
193
196
|
## Heuristic Functions (for A*)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# SCGraph
|
|
3
3
|
[](https://badge.fury.io/py/scgraph)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://
|
|
5
|
+
[](https://pepy.tech/project/scgraph/)
|
|
6
6
|
|
|
7
7
|
**A high-performance, lightweight Python library for shortest path routing on geographic and supply chain networks.**
|
|
8
8
|
|
|
@@ -174,21 +174,24 @@ All algorithms are available on `Graph` objects and accessible from `GeoGraph` v
|
|
|
174
174
|
| `algorithm_fn` | Description | Time Complexity |
|
|
175
175
|
|---|---|---|
|
|
176
176
|
| `'dijkstra'` | Standard Dijkstra; general purpose, non-negative weights (default) | O((n+m) log n) |
|
|
177
|
+
| `'dijkstra_buckets'` | Dijkstra with buckets (Dial's algorithm); efficient for non-negative weights (ideally >= 1) | O(n+m+W) |
|
|
177
178
|
| `'dijkstra_negative'` | Dijkstra with cycle detection; supports negative weights | O(n·m) |
|
|
178
179
|
| `'a_star'` | A* with optional heuristic; faster than Dijkstra with a good heuristic | O((n+m) log n) |
|
|
179
180
|
| `'bellman_ford'` | Bellman-Ford; supports negative weights, slower than Dijkstra | O(n·m) |
|
|
180
181
|
| `'bmssp'` | [BMSSP Algorithm](https://arxiv.org/pdf/2504.17033) / [Implementation](https://github.com/connor-makowski/bmsspy) | O(m log^(2/3)(n)) |
|
|
181
182
|
| `'cached_shortest_path'` | Caches shortest path tree from origin; near-instant repeated queries | O((n+m) log n) first, O(1) after |
|
|
182
183
|
| `'contraction_hierarchy'` | Bidirectional Dijkstra on preprocessed CH graph; fast arbitrary queries | O(k log k) per query |
|
|
184
|
+
| `'tnr'` | [Transit Node Routing](https://en.wikipedia.org/wiki/Transit_node_routing); extremely fast for global queries | O(1) per query (global) |
|
|
183
185
|
|
|
184
186
|
## Performance Guide
|
|
185
187
|
|
|
186
188
|
| Scenario | Recommended Approach |
|
|
187
189
|
|---|---|
|
|
188
190
|
| Single query | `dijkstra` (default) |
|
|
191
|
+
| Weights generally >= 1 | `dijkstra_buckets` |
|
|
189
192
|
| Repeated queries from one origin | `cached_shortest_path` |
|
|
190
193
|
| Large distance matrix (same graph) | `distance_matrix` method |
|
|
191
|
-
| Many arbitrary queries on a fixed graph | `contraction_hierarchy` |
|
|
194
|
+
| Many arbitrary queries on a fixed graph | `contraction_hierarchy` or `tnr` |
|
|
192
195
|
| Graph with negative weights | `dijkstra_negative` |
|
|
193
196
|
|
|
194
197
|
## Heuristic Functions (for A*)
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include <nanobind/operators.h>
|
|
13
13
|
#include "../src/graph.hpp"
|
|
14
14
|
#include "../src/contraction_hierarchies.hpp"
|
|
15
|
+
#include "../src/transit_node_routing.hpp"
|
|
15
16
|
|
|
16
17
|
namespace nb = nanobind;
|
|
17
18
|
using namespace nb::literals;
|
|
@@ -143,7 +144,17 @@ NB_MODULE(cpp, m) {
|
|
|
143
144
|
}, nb::arg("origin_id"), nb::arg("destination_id"),
|
|
144
145
|
"Find shortest path using Dijkstra's algorithm")
|
|
145
146
|
|
|
147
|
+
.def("dijkstra_buckets", [](Graph& self,
|
|
148
|
+
const std::variant<int, std::set<int>>& origin_id,
|
|
149
|
+
int destination_id,
|
|
150
|
+
std::optional<double> max_edge_weight) -> nb::dict {
|
|
151
|
+
return graph_result_to_dict(self.dijkstra_buckets(origin_id, destination_id, max_edge_weight));
|
|
152
|
+
}, nb::arg("origin_id"), nb::arg("destination_id"),
|
|
153
|
+
nb::arg("max_edge_weight") = nb::none(),
|
|
154
|
+
"Find shortest path using Dijkstra with buckets")
|
|
155
|
+
|
|
146
156
|
.def("dijkstra_negative", [](Graph& self,
|
|
157
|
+
|
|
147
158
|
const std::variant<int, std::set<int>>& origin_id,
|
|
148
159
|
int destination_id,
|
|
149
160
|
std::optional<int> cycle_check_iterations) -> nb::dict {
|
|
@@ -193,12 +204,21 @@ NB_MODULE(cpp, m) {
|
|
|
193
204
|
.def("create_contraction_hierarchy", &Graph::create_contraction_hierarchy,
|
|
194
205
|
nb::arg("heuristic_fn") = nullptr,
|
|
195
206
|
"Create a Contraction Hierarchies (CH) graph")
|
|
196
|
-
.def("contraction_hierarchy", [](Graph& self, int origin_id, int destination_id
|
|
197
|
-
bool length_only) -> nb::dict {
|
|
207
|
+
.def("contraction_hierarchy", [](Graph& self, int origin_id, int destination_id) -> nb::dict {
|
|
198
208
|
return graph_result_to_dict(self.contraction_hierarchy(origin_id, destination_id));
|
|
199
209
|
}, nb::arg("origin_id"), nb::arg("destination_id"),
|
|
200
|
-
|
|
201
|
-
|
|
210
|
+
"Get shortest path using Contraction Hierarchies")
|
|
211
|
+
|
|
212
|
+
// Transit Node Routing
|
|
213
|
+
.def("create_tnr_hierarchy", &Graph::create_tnr_hierarchy,
|
|
214
|
+
nb::arg("num_transit_nodes") = 100, nb::arg("heuristic_fn") = nullptr,
|
|
215
|
+
"Create a Transit Node Routing (TNR) graph")
|
|
216
|
+
.def("set_tnr_graph", &Graph::set_tnr_graph, nb::arg("tnr_graph"),
|
|
217
|
+
"Set the TNRGraph object for the graph")
|
|
218
|
+
.def("tnr", [](Graph& self, int origin_id, int destination_id, bool length_only) -> nb::dict {
|
|
219
|
+
return graph_result_to_dict(self.tnr(origin_id, destination_id, length_only));
|
|
220
|
+
}, nb::arg("origin_id"), nb::arg("destination_id"), nb::arg("length_only") = false,
|
|
221
|
+
"Get shortest path using Transit Node Routing");
|
|
202
222
|
|
|
203
223
|
// CHGraph class
|
|
204
224
|
nb::class_<CHGraph>(m, "CHGraph")
|
|
@@ -317,5 +337,188 @@ NB_MODULE(cpp, m) {
|
|
|
317
337
|
}
|
|
318
338
|
|
|
319
339
|
return CHGraph(nodes_count, ranks, forward_graph, backward_graph, shortcuts, original_graph);
|
|
320
|
-
|
|
321
|
-
|
|
340
|
+
}, nb::arg("filename"), "Load a CHGraph from a JSON file.");
|
|
341
|
+
|
|
342
|
+
// TNRGraph class
|
|
343
|
+
nb::class_<TNRGraph, CHGraph>(m, "TNRGraph")
|
|
344
|
+
.def(nb::init<const std::vector<std::unordered_map<int, double>>&, int, std::function<double(CHGraph*, int)>>(),
|
|
345
|
+
nb::arg("graph"), nb::arg("num_transit_nodes") = 100, nb::arg("heuristic_fn") = nullptr,
|
|
346
|
+
"Initialize and preprocess a TNRGraph")
|
|
347
|
+
.def(nb::init<int, const std::vector<int>&,
|
|
348
|
+
|
|
349
|
+
const std::vector<std::unordered_map<int, double>>&,
|
|
350
|
+
const std::vector<std::unordered_map<int, double>>&,
|
|
351
|
+
const std::unordered_map<std::pair<int, int>, int, pair_hash>&,
|
|
352
|
+
const std::optional<std::vector<std::unordered_map<int, double>>>&,
|
|
353
|
+
const std::set<int>&,
|
|
354
|
+
const std::unordered_map<std::pair<int, int>, double, pair_hash>&,
|
|
355
|
+
const std::vector<std::unordered_map<int, double>>&,
|
|
356
|
+
const std::vector<std::unordered_map<int, double>>&>(),
|
|
357
|
+
nb::arg("nodes_count"), nb::arg("ranks"), nb::arg("forward_graph"),
|
|
358
|
+
nb::arg("backward_graph"), nb::arg("shortcuts"), nb::arg("original_graph"),
|
|
359
|
+
nb::arg("transit_nodes"), nb::arg("distance_table"),
|
|
360
|
+
nb::arg("forward_access_nodes"), nb::arg("backward_access_nodes"),
|
|
361
|
+
"Initialize a TNRGraph from pre-calculated data")
|
|
362
|
+
.def("search", [](TNRGraph& self, int origin_id, int destination_id, bool length_only) -> nb::dict {
|
|
363
|
+
return graph_result_to_dict(self.search(origin_id, destination_id, length_only));
|
|
364
|
+
}, nb::arg("origin_id"), nb::arg("destination_id"), nb::arg("length_only") = false,
|
|
365
|
+
"Perform a bidirectional search on the TNR")
|
|
366
|
+
.def("get_shortest_path", [](TNRGraph& self, int origin_id, int destination_id, bool length_only) -> nb::dict {
|
|
367
|
+
return graph_result_to_dict(self.search(origin_id, destination_id, length_only));
|
|
368
|
+
}, nb::arg("origin_id"), nb::arg("destination_id"), nb::arg("length_only") = false,
|
|
369
|
+
"Wrapper for search to match scgraph naming conventions")
|
|
370
|
+
.def_prop_ro("transit_nodes", &TNRGraph::get_transit_nodes)
|
|
371
|
+
.def_prop_ro("distance_table", [](const TNRGraph& self) {
|
|
372
|
+
nb::dict d;
|
|
373
|
+
for (const auto& [key, dist] : self.get_distance_table()) {
|
|
374
|
+
d[nb::cast(key)] = dist;
|
|
375
|
+
}
|
|
376
|
+
return d;
|
|
377
|
+
})
|
|
378
|
+
.def_prop_ro("forward_access_nodes", &TNRGraph::get_forward_access_nodes)
|
|
379
|
+
.def_prop_ro("backward_access_nodes", &TNRGraph::get_backward_access_nodes)
|
|
380
|
+
.def("save_as_tnrjson", [](const TNRGraph& self, const std::string& filename) {
|
|
381
|
+
if (filename.size() < 8 || filename.substr(filename.size() - 8) != ".tnrjson") {
|
|
382
|
+
throw std::invalid_argument("Filename must end with .tnrjson");
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
nb::dict d;
|
|
386
|
+
d["type"] = "TNRGraph";
|
|
387
|
+
d["nodes_count"] = self.get_nodes_count();
|
|
388
|
+
|
|
389
|
+
nb::list t_nodes;
|
|
390
|
+
for (int node : self.get_transit_nodes()) {
|
|
391
|
+
t_nodes.append(node);
|
|
392
|
+
}
|
|
393
|
+
d["transit_nodes"] = t_nodes;
|
|
394
|
+
|
|
395
|
+
nb::dict dist_table_str;
|
|
396
|
+
for (const auto& [key, dist] : self.get_distance_table()) {
|
|
397
|
+
std::string key_str = "(" + std::to_string(key.first) + "," + std::to_string(key.second) + ")";
|
|
398
|
+
dist_table_str[nb::cast(key_str)] = dist;
|
|
399
|
+
}
|
|
400
|
+
d["distance_table"] = dist_table_str;
|
|
401
|
+
|
|
402
|
+
auto convert_access_nodes = [](const std::vector<std::unordered_map<int, double>>& access_nodes) {
|
|
403
|
+
nb::list access_str;
|
|
404
|
+
for (const auto& node_map : access_nodes) {
|
|
405
|
+
nb::dict d_map;
|
|
406
|
+
for (const auto& [k, v] : node_map) {
|
|
407
|
+
d_map[nb::cast(std::to_string(k))] = v;
|
|
408
|
+
}
|
|
409
|
+
access_str.append(d_map);
|
|
410
|
+
}
|
|
411
|
+
return access_str;
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
d["forward_access_nodes"] = convert_access_nodes(self.get_forward_access_nodes());
|
|
415
|
+
d["backward_access_nodes"] = convert_access_nodes(self.get_backward_access_nodes());
|
|
416
|
+
|
|
417
|
+
nb::dict ch_data;
|
|
418
|
+
ch_data["ranks"] = self.get_ranks();
|
|
419
|
+
ch_data["forward_graph"] = self.get_forward_graph();
|
|
420
|
+
ch_data["backward_graph"] = self.get_backward_graph();
|
|
421
|
+
|
|
422
|
+
nb::dict shortcuts_str;
|
|
423
|
+
for (const auto& [key, via_node_id] : self.get_shortcuts()) {
|
|
424
|
+
std::string key_str = "(" + std::to_string(key.first) + ", " + std::to_string(key.second) + ")";
|
|
425
|
+
shortcuts_str[nb::cast(key_str)] = via_node_id;
|
|
426
|
+
}
|
|
427
|
+
ch_data["shortcuts"] = shortcuts_str;
|
|
428
|
+
ch_data["original_graph"] = self.get_original_graph();
|
|
429
|
+
ch_data["nodes_count"] = self.get_nodes_count();
|
|
430
|
+
|
|
431
|
+
d["ch_data"] = ch_data;
|
|
432
|
+
|
|
433
|
+
nb::module_ json = nb::module_::import_("json");
|
|
434
|
+
nb::module_ builtins = nb::module_::import_("builtins");
|
|
435
|
+
nb::object f = builtins.attr("open")(filename, "w");
|
|
436
|
+
json.attr("dump")(d, f);
|
|
437
|
+
f.attr("close")();
|
|
438
|
+
}, nb::arg("filename"), "Save the current TNRGraph as a JSON file.")
|
|
439
|
+
.def_static("load_from_tnrjson", [](const std::string& filename) {
|
|
440
|
+
if (filename.size() < 8 || filename.substr(filename.size() - 8) != ".tnrjson") {
|
|
441
|
+
throw std::invalid_argument("Filename must end with .tnrjson");
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
nb::module_ json = nb::module_::import_("json");
|
|
445
|
+
nb::module_ builtins = nb::module_::import_("builtins");
|
|
446
|
+
nb::object f = builtins.attr("open")(filename, "r");
|
|
447
|
+
nb::dict data = nb::cast<nb::dict>(json.attr("load")(f));
|
|
448
|
+
f.attr("close")();
|
|
449
|
+
|
|
450
|
+
if (!data.contains("type") || nb::cast<std::string>(data["type"]) != "TNRGraph") {
|
|
451
|
+
throw std::invalid_argument("JSON file is not a valid TNRGraph.");
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
int nodes_count = nb::cast<int>(data["nodes_count"]);
|
|
455
|
+
|
|
456
|
+
std::set<int> transit_nodes;
|
|
457
|
+
for (auto item : nb::cast<nb::list>(data["transit_nodes"])) {
|
|
458
|
+
transit_nodes.insert(nb::cast<int>(item));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
std::unordered_map<std::pair<int, int>, double, pair_hash> distance_table;
|
|
462
|
+
for (auto [key, dist] : nb::cast<nb::dict>(data["distance_table"])) {
|
|
463
|
+
std::string key_str = nb::cast<std::string>(key);
|
|
464
|
+
size_t comma = key_str.find(',');
|
|
465
|
+
int t_f = std::stoi(key_str.substr(1, comma - 1));
|
|
466
|
+
int t_b = std::stoi(key_str.substr(comma + 1, key_str.size() - comma - 2));
|
|
467
|
+
distance_table[{t_f, t_b}] = nb::cast<double>(dist);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
auto convert_access_nodes = [](nb::list access_str) {
|
|
471
|
+
std::vector<std::unordered_map<int, double>> access_nodes;
|
|
472
|
+
for (auto item : access_str) {
|
|
473
|
+
nb::dict d = nb::cast<nb::dict>(item);
|
|
474
|
+
std::unordered_map<int, double> node_map;
|
|
475
|
+
for (auto [k, v] : d) {
|
|
476
|
+
node_map[std::stoi(nb::cast<std::string>(k))] = nb::cast<double>(v);
|
|
477
|
+
}
|
|
478
|
+
access_nodes.push_back(node_map);
|
|
479
|
+
}
|
|
480
|
+
return access_nodes;
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
std::vector<std::unordered_map<int, double>> forward_access_nodes = convert_access_nodes(nb::cast<nb::list>(data["forward_access_nodes"]));
|
|
484
|
+
std::vector<std::unordered_map<int, double>> backward_access_nodes = convert_access_nodes(nb::cast<nb::list>(data["backward_access_nodes"]));
|
|
485
|
+
|
|
486
|
+
nb::dict ch_data = nb::cast<nb::dict>(data["ch_data"]);
|
|
487
|
+
std::vector<int> ranks = nb::cast<std::vector<int>>(ch_data["ranks"]);
|
|
488
|
+
|
|
489
|
+
auto convert_graph = [](nb::list raw_graph) {
|
|
490
|
+
std::vector<std::unordered_map<int, double>> graph;
|
|
491
|
+
for (auto item : raw_graph) {
|
|
492
|
+
nb::dict d = nb::cast<nb::dict>(item);
|
|
493
|
+
std::unordered_map<int, double> node_map;
|
|
494
|
+
for (auto [k, v] : d) {
|
|
495
|
+
node_map[std::stoi(nb::cast<std::string>(k))] = nb::cast<double>(v);
|
|
496
|
+
}
|
|
497
|
+
graph.push_back(node_map);
|
|
498
|
+
}
|
|
499
|
+
return graph;
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
std::vector<std::unordered_map<int, double>> forward_graph = convert_graph(nb::cast<nb::list>(ch_data["forward_graph"]));
|
|
503
|
+
std::vector<std::unordered_map<int, double>> backward_graph = convert_graph(nb::cast<nb::list>(ch_data["backward_graph"]));
|
|
504
|
+
|
|
505
|
+
nb::dict shortcuts_raw = nb::cast<nb::dict>(ch_data["shortcuts"]);
|
|
506
|
+
std::unordered_map<std::pair<int, int>, int, pair_hash> shortcuts;
|
|
507
|
+
for (auto [key, via_node_id] : shortcuts_raw) {
|
|
508
|
+
std::string key_str = nb::cast<std::string>(key);
|
|
509
|
+
size_t comma = key_str.find(',');
|
|
510
|
+
int shortcut_origin_id = std::stoi(key_str.substr(1, comma - 1));
|
|
511
|
+
size_t start_dest = comma + 1;
|
|
512
|
+
while (start_dest < key_str.size() && (key_str[start_dest] == ' ' || key_str[start_dest] == '\t')) start_dest++;
|
|
513
|
+
int shortcut_destination_id = std::stoi(key_str.substr(start_dest, key_str.size() - start_dest - 1));
|
|
514
|
+
shortcuts[{shortcut_origin_id, shortcut_destination_id}] = nb::cast<int>(via_node_id);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
std::optional<std::vector<std::unordered_map<int, double>>> original_graph = std::nullopt;
|
|
518
|
+
if (ch_data.contains("original_graph") && !ch_data["original_graph"].is_none()) {
|
|
519
|
+
original_graph = convert_graph(nb::cast<nb::list>(ch_data["original_graph"]));
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return TNRGraph(nodes_count, ranks, forward_graph, backward_graph, shortcuts, original_graph, transit_nodes, distance_table, forward_access_nodes, backward_access_nodes);
|
|
523
|
+
}, nb::arg("filename"), "Load a TNRGraph from a JSON file.");
|
|
524
|
+
}
|
|
@@ -17,10 +17,11 @@ Graph::Graph(const std::vector<std::unordered_map<int, double>>& graph_data, boo
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
// Override reset_cache to also clear __ch_graph__
|
|
20
|
+
// Override reset_cache to also clear __ch_graph__ and __tnr_graph__
|
|
21
21
|
void Graph::reset_cache() {
|
|
22
22
|
GraphUtils::reset_cache();
|
|
23
23
|
__ch_graph__ = nullptr;
|
|
24
|
+
__tnr_graph__ = nullptr;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
// Tree algorithms
|
|
@@ -139,6 +140,78 @@ GraphResult Graph::dijkstra(const std::variant<int, std::set<int>>& origin_id, i
|
|
|
139
140
|
};
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
GraphResult Graph::dijkstra_buckets(const std::variant<int, std::set<int>>& origin_id, int destination_id,
|
|
144
|
+
std::optional<double> max_edge_weight) {
|
|
145
|
+
input_check(origin_id, destination_id);
|
|
146
|
+
auto origin_ids = get_origin_ids(origin_id);
|
|
147
|
+
|
|
148
|
+
double max_weight = 0.0;
|
|
149
|
+
if (max_edge_weight.has_value()) {
|
|
150
|
+
max_weight = max_edge_weight.value();
|
|
151
|
+
} else {
|
|
152
|
+
for (const auto& node_edges : graph) {
|
|
153
|
+
for (const auto& [connected_id, connected_distance] : node_edges) {
|
|
154
|
+
if (connected_distance > max_weight) {
|
|
155
|
+
max_weight = connected_distance;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
int num_buckets = static_cast<int>(std::ceil(max_weight)) + 1;
|
|
161
|
+
|
|
162
|
+
const size_t n = graph.size();
|
|
163
|
+
std::vector<double> distance_matrix(n, std::numeric_limits<double>::infinity());
|
|
164
|
+
std::vector<int> predecessor(n, -1);
|
|
165
|
+
std::vector<std::vector<int>> buckets(num_buckets);
|
|
166
|
+
|
|
167
|
+
for (int oid : origin_ids) {
|
|
168
|
+
distance_matrix[oid] = 0.0;
|
|
169
|
+
buckets[0].push_back(oid);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
int current_dist = 0;
|
|
173
|
+
size_t nodes_in_buckets = origin_ids.size();
|
|
174
|
+
|
|
175
|
+
while (nodes_in_buckets > 0) {
|
|
176
|
+
int bucket_idx = current_dist % num_buckets;
|
|
177
|
+
while (buckets[bucket_idx].empty()) {
|
|
178
|
+
current_dist++;
|
|
179
|
+
bucket_idx = current_dist % num_buckets;
|
|
180
|
+
if (nodes_in_buckets == 0) break;
|
|
181
|
+
if (distance_matrix[destination_id] < static_cast<double>(current_dist)) break;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (nodes_in_buckets == 0 || distance_matrix[destination_id] < static_cast<double>(current_dist)) break;
|
|
185
|
+
|
|
186
|
+
int current_id = buckets[bucket_idx].back();
|
|
187
|
+
buckets[bucket_idx].pop_back();
|
|
188
|
+
nodes_in_buckets--;
|
|
189
|
+
|
|
190
|
+
if (distance_matrix[current_id] < static_cast<double>(current_dist)) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for (const auto& [connected_id, connected_distance] : graph[current_id]) {
|
|
195
|
+
double possible_distance = distance_matrix[current_id] + connected_distance;
|
|
196
|
+
if (possible_distance < distance_matrix[connected_id]) {
|
|
197
|
+
distance_matrix[connected_id] = possible_distance;
|
|
198
|
+
predecessor[connected_id] = current_id;
|
|
199
|
+
buckets[static_cast<int>(possible_distance) % num_buckets].push_back(connected_id);
|
|
200
|
+
nodes_in_buckets++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (distance_matrix[destination_id] == std::numeric_limits<double>::infinity()) {
|
|
206
|
+
throw std::runtime_error("The origin and destination nodes are not connected.");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return GraphResult{
|
|
210
|
+
reconstruct_path(destination_id, predecessor),
|
|
211
|
+
distance_matrix[destination_id]
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
142
215
|
GraphResult Graph::dijkstra_negative(const std::variant<int, std::set<int>>& origin_id, int destination_id,
|
|
143
216
|
std::optional<int> cycle_check_iterations) {
|
|
144
217
|
input_check(origin_id, destination_id);
|
|
@@ -379,3 +452,19 @@ GraphResult Graph::contraction_hierarchy(int origin_id, int destination_id) {
|
|
|
379
452
|
}
|
|
380
453
|
return __ch_graph__->get_shortest_path(origin_id, destination_id);
|
|
381
454
|
}
|
|
455
|
+
|
|
456
|
+
std::shared_ptr<TNRGraph> Graph::create_tnr_hierarchy(int num_transit_nodes, std::function<double(CHGraph*, int)> heuristic_fn) {
|
|
457
|
+
__tnr_graph__ = std::make_shared<TNRGraph>(get_graph(), num_transit_nodes, heuristic_fn);
|
|
458
|
+
return __tnr_graph__;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
void Graph::set_tnr_graph(std::shared_ptr<TNRGraph> tnr_graph) {
|
|
462
|
+
__tnr_graph__ = tnr_graph;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
GraphResult Graph::tnr(int origin_id, int destination_id, bool length_only) {
|
|
466
|
+
if (__tnr_graph__ == nullptr) {
|
|
467
|
+
throw std::runtime_error("TNRGraph has not been set. Use set_tnr_graph() first.");
|
|
468
|
+
}
|
|
469
|
+
return __tnr_graph__->search(origin_id, destination_id, length_only);
|
|
470
|
+
}
|
|
@@ -6,17 +6,19 @@
|
|
|
6
6
|
#include <memory>
|
|
7
7
|
#include "graph_utils.hpp"
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
#include "contraction_hierarchies.hpp"
|
|
10
|
+
#include "transit_node_routing.hpp"
|
|
10
11
|
|
|
11
12
|
class Graph : public GraphUtils {
|
|
12
13
|
private:
|
|
13
14
|
std::shared_ptr<CHGraph> __ch_graph__ = nullptr;
|
|
15
|
+
std::shared_ptr<TNRGraph> __tnr_graph__ = nullptr;
|
|
14
16
|
|
|
15
17
|
public:
|
|
16
18
|
// Constructor
|
|
17
19
|
explicit Graph(const std::vector<std::unordered_map<int, double>>& graph_data, bool validate = false);
|
|
18
20
|
|
|
19
|
-
// Override reset_cache to also clear ch_graph
|
|
21
|
+
// Override reset_cache to also clear ch_graph and tnr_graph
|
|
20
22
|
void reset_cache();
|
|
21
23
|
|
|
22
24
|
// Tree algorithms
|
|
@@ -25,6 +27,8 @@ public:
|
|
|
25
27
|
|
|
26
28
|
// Shortest path algorithms
|
|
27
29
|
GraphResult dijkstra(const std::variant<int, std::set<int>>& origin_id, int destination_id);
|
|
30
|
+
GraphResult dijkstra_buckets(const std::variant<int, std::set<int>>& origin_id, int destination_id,
|
|
31
|
+
std::optional<double> max_edge_weight = std::nullopt);
|
|
28
32
|
GraphResult dijkstra_negative(const std::variant<int, std::set<int>>& origin_id, int destination_id,
|
|
29
33
|
std::optional<int> cycle_check_iterations = std::nullopt);
|
|
30
34
|
GraphResult a_star(const std::variant<int, std::set<int>>& origin_id, int destination_id,
|
|
@@ -38,4 +42,10 @@ public:
|
|
|
38
42
|
// Contraction Hierarchies
|
|
39
43
|
std::shared_ptr<CHGraph> create_contraction_hierarchy(std::function<double(CHGraph*, int)> heuristic_fn = nullptr);
|
|
40
44
|
GraphResult contraction_hierarchy(int origin_id, int destination_id);
|
|
45
|
+
|
|
46
|
+
// Transit Node Routing
|
|
47
|
+
std::shared_ptr<TNRGraph> create_tnr_hierarchy(int num_transit_nodes = 100, std::function<double(CHGraph*, int)> heuristic_fn = nullptr);
|
|
48
|
+
void set_tnr_graph(std::shared_ptr<TNRGraph> tnr_graph);
|
|
49
|
+
GraphResult tnr(int origin_id, int destination_id, bool length_only = false);
|
|
50
|
+
|
|
41
51
|
};
|