DHART
Loading...
Searching...
No Matches
path_finder.cpp
Go to the documentation of this file.
1
8
9#include <path_finder.h>
10
11#include <vector>
12#include <robin_hood.h>
13#include<omp.h>
14#include<thread>
15#include<math.h>
16#include<execution>
17#include <memory>
18
19#include <boost/graph/dijkstra_shortest_paths.hpp>
20#include <boost/graph/breadth_first_search.hpp>
21#include <boost/graph/dijkstra_shortest_paths_no_color_map.hpp>
22#include <boost/exception/exception.hpp>
23#include <boost/math/special_functions/fpclassify.hpp>
24
25#include <boost_graph.h>
26#include <path.h>
27
28using namespace HF::SpatialStructures;
29using namespace HF::Pathfinding;
30using std::vector;
31
32namespace HF::Pathfinding {
48 struct DistPred {
49 std::vector<float> distance;
50 std::vector<vertex_descriptor> predecessor;
51
56
61 DistPred(int n) {
62 distance.resize(n);
63 predecessor.resize(n);
64 }
65 };
66
79 int start,
80 int end,
81 const std::vector<size_t>& pred,
82 const std::vector<float>& distances
83 ) {
84
85 // Create a new path and add the end point.
86 Path p;
87 p.AddNode(end, 0);
88
89 // Return an empty path if there's no path from start to end indicated by the predecessor's
90 // value for the current node being the current node.
91 int current_node = end;
92 if (pred[current_node] == current_node) return Path{};
93
94 float last_cost = distances[current_node];
95
96 // Follow the predecessor matrix from end to start
97 while (current_node != start) {
98
99 // If this is triggered, there's something wrong with this algorithm because the path
100 // suddenly has more nodes than there are in the entire graph.
101 if (p.size() > pred.size())
102 throw std::exception("Path included more nodes than contaiend in the graph!");
103
104 // Get the next node from the predecessor matrix
105 int next_node = pred[current_node];
106
107 // Get the current cost for traversing the graph so far.
108 float current_cost = distances[next_node];
109
110 // Subtract the last cost from the graph to get the cost of traversing this node
111 float de_facto_cost = last_cost - current_cost;
112
113 // Add this node to the list
114 p.AddNode(next_node, de_facto_cost);
115
116 // Save this node and cost as the last node/cost
117 last_cost = current_cost;
118 current_node = next_node;
119 }
120
121 // Flip the order of this since this algorithm generates it from end to start
122 p.Reverse();
123 return p;
124 }
125
133 inline Path ConstructShortestPathFromPred(int start, int end, const DistPred& dist_pred) {
134 return ConstructShortestPathFromPred(start, end, dist_pred.predecessor, dist_pred.distance);
135 }
136
147
148 // Construct an isntance of DistPred that can hold every node in the graph
149 int n = num_vertices(g);
150 DistPred dist_pred(n);
151
152 // Give boost a reference to the array in distpred
153 auto pm = boost::predecessor_map(&dist_pred.predecessor[0]);
154 vertex_descriptor start_vertex = vertex(id, g);
155
156 // Fill distpred's distance and predecssor arrays with info from the graph
157 dijkstra_shortest_paths_no_color_map(
158 g,
159 start_vertex,
160 pm.distance_map(&dist_pred.distance[0]).weight_map(boost::get(&Edge_Cost::weight, g))
161 );
162
163 return dist_pred;
164 }
165
166 Path FindPath(BoostGraph* bg, int start_id, int end_id)
167 {
168 // Get a reference to the graph contained by this boost graph
169 const graph_t& graph = bg->g;
170
171 // Build the distance and predecessor arrays for the node at start_id
172 auto dist_pred = BuildDistanceAndPredecessor(graph, start_id);
173
174 // Construct the shortest path using the predecessor and distance arrays
175 return ConstructShortestPathFromPred(start_id, end_id, dist_pred.predecessor, dist_pred.distance);
176 }
177
178 vector<Path> FindPaths( BoostGraph * bg, const vector<int> & start_points, const vector<int> & end_points)
179 {
180 // Get the graph from bg
181 const graph_t& graph = bg->g;
182 vector<Path> paths(start_points.size());
183
184 // Generate predecessor matrices for every unique start point
185 robin_hood::unordered_map<int, DistPred> dpm;
186 for (int start_point : start_points)
187 if (dpm.count(start_point) == 0)
188 dpm[start_point] = BuildDistanceAndPredecessor(graph, start_point);
189
190 //Create shortestpaths for start_end pair
191 for (int i = 0; i < start_points.size(); i++) {
192 int start = start_points[i];
193 int end = end_points[i];
194 const auto& dist_pred = dpm[start];
195
196 paths[i] = ConstructShortestPathFromPred(start, end, dist_pred);
197 }
198 return paths;
199 }
200
202 const BoostGraph * bg,
203 const std::vector<int>& start_points,
204 const std::vector<int>& end_points,
205 HF::SpatialStructures::Path** out_paths,
206 HF::SpatialStructures::PathMember** out_path_members,
207 int* out_sizes
208 ) {
209 // Get graph from boost graph
210 const graph_t& graph = bg->g;
211 robin_hood::unordered_map<int, DistPred> dpm;
212
213 // Use maximum number of cores for this machine
214 int core_count = std::thread::hardware_concurrency();
215 int cores_to_use = std::min(core_count-1, static_cast<int>(start_points.size()));
216 omp_set_num_threads(cores_to_use);
217
218 // Copy and sort the input array of starting points
219 std::vector<int> start_copy = start_points;
220 std::sort(std::execution::par_unseq, start_copy.begin(),start_copy.end());
221
222 // Remove all duplicates, effectively creating an array of unique start ids
223 std::vector<int> unique_starts;
224 std::unique_copy(start_copy.begin(), start_copy.end(), std::back_inserter(unique_starts));
225
226 // Preallocate entries for the hash map in sequence so we don't corrupt the heap trying to
227 // add elements in parallel.
228 for (auto uc : unique_starts)
229 dpm.emplace(std::pair<int, DistPred>{uc, DistPred()});
230
231 // Build predecessor and distance matrices for each unique start point in parallel
232 #pragma omp parallel for schedule(dynamic) if (unique_starts.size() > cores_to_use && cores_to_use > 4)
233 for (int i = 0; i < unique_starts.size(); i++) {
234 int start_point = unique_starts[i];
235 dpm[start_point] = BuildDistanceAndPredecessor(graph, start_point);
236 }
237
238 // Create paths in parallel.
239#pragma omp parallel for schedule(dynamic) if (cores_to_use > 4)
240 for (int i = 0; i < start_points.size(); i++) {
241
242 // Get the start and end point for this path
243 int start = start_points[i];
244 int end = end_points[i];
245
246 // Get a reference to the distance and predecessor array for this start point.
247 const auto& dist_pred = dpm[start];
248
249 // Construct the shortest path, store a point for it in out_paths at index i
250 out_paths[i] = new Path(ConstructShortestPathFromPred(start, end, dist_pred));
251
252 // Store a pointer to that path's PathMembers in out_path_members
253 out_path_members[i] = out_paths[i]->GetPMPointer();
254
255 // Store the size of the path's PathMembers in index i of out_sizes
256 out_sizes[i] = out_paths[i]->size();
257
258 // If the size of the path is zero, delete the path and set it's pointer to a null
259 // pointer in the output array.
260 if (out_sizes[i] == 0) {
261 delete (out_paths[i]);
262 out_paths[i] = nullptr;
263 }
264 }
265 }
266
268 {
269 const auto & g = bg.g;
270
271 // Generate distance and predecessor matricies
272 const int num_nodes = bg.p.size();
273 DistanceAndPredecessor out_distpred(num_nodes);
274
275 // Iterate through every row in the array
276 #pragma omp parallel for schedule(dynamic)
277 for (int row = 0; row < num_nodes; row++) {
278
279 // Get pointers to the beginning of the row for both matricies
280 float* dist_row_start = out_distpred.GetRowOfDist(row);
281 int* pred_row_start = out_distpred.GetRowOfPred(row);
282
283 // Give boost a reference to the array in distpred
284 auto pm = boost::predecessor_map(pred_row_start);
285
286 // Get the descriptor of the starting vertex
287 vertex_descriptor start_vertex = vertex(row, g);
288
289 // calculate the shortest path using the row of the distance and predecessor arrays.
290 // Boost should fill these when calculating the distance/predecessor matricies
291 dijkstra_shortest_paths_no_color_map(
292 g,
293 start_vertex,
294 pm.distance_map(dist_row_start).weight_map(boost::get(&Edge_Cost::weight, g))
295 );
296
297 // The documentation of boost kind of lies here. An infinite cost is not infinite,
298 // as their pseudo code would lead you to believe. Actually it's setting the value
299 // of the distance matrix for paths that can`t be created to the value
300 // of std::numeric_limits<float>::max. This should be a NAN for the sake of readability.
301 for (int i = 0; i < num_nodes; i++)
302 {
303 float & dist_element = dist_row_start[i];
304 int & pred_element = pred_row_start[i];
305
306 // If the predecessor element is not not in the range of nodes,
307 // this is boost trying to signal to us that there is no connection
308 // between these nodes, so put a NAN there.
309 if (dist_element == std::numeric_limits<float>::max()) {
310 pred_element = -1; // Ints have no way of representing NaN
311 dist_element = -1;
312 }
313 }
314 }
315
316 return out_distpred;
317
318 }
319
320 std::unique_ptr<BoostGraph, BoostGraphDeleter> CreateBoostGraph(const Graph& g, const std::string & cost_type) {
321 // Construct a new boost graph using g, then wrap it in a unique pointer.
322 return std::unique_ptr<BoostGraph, BoostGraphDeleter>(new BoostGraph(g, cost_type));
323 }
324
326 delete bg;
327 }
328
329 void InsertAllToAllPathsIntoArray(BoostGraph* bg, Path** out_paths, PathMember** out_path_members, int* out_sizes) {
330 size_t node_count = bg->p.size();
331 size_t max_path = node_count * node_count;
332
333 std::vector<int> start_points(max_path);
334 std::vector<int> end_points(max_path);
335
336 int curr_id = 0;
337
338 // Populate the start points,
339 // size will be (node_count)^2
340 for (int i = 0; i < node_count; i++) {
341 for (int k = 0; k < node_count; k++) {
342 start_points[curr_id++] = i;
343 }
344 }
345
346 curr_id = 0;
347
348 // Populate the end points,
349 // size will be (node_count)^2
350 for (int i = 0; i < node_count; i++) {
351 for (int k = 0; k < node_count; k++) {
352 end_points[curr_id++] = k;
353 }
354 }
355
356 /*
357 The idea is that
358 start_points[0] and end_points[0]
359 will be the path from 0 to 0,
360
361 start_points[1] and end_points[1]
362 will be the path from 0 to 1
363
364 start_points[2] and end_points[2]
365 will be the path from 0 to 2,
366
367 ...etc...
368 */
369
370 // Run InsertPathsIntoArray and mutate out_paths, out_path_members, and out_sizes
371 InsertPathsIntoArray(bg, start_points, end_points, out_paths, out_path_members, out_sizes);
372 }
373}
Contains definitions for the BoostGraph class.
Contains definition for the BoostGraphDeleter structure.
Contains definitions for the Path structure.
Contains standard fundamental data structures for representing space used throughout DHARTAPI.
Algorithms to find the shortest path between nodes in a HF::SpatialStructures::Graph.
Definition: boost_graph.cpp:20
vector< Path > FindPaths(BoostGraph *bg, const vector< int > &start_points, const vector< int > &end_points)
Find a path from every id in start_ids to the matching end node in end_ids.
void InsertPathsIntoArray(const BoostGraph *bg, const std::vector< int > &start_points, const std::vector< int > &end_points, HF::SpatialStructures::Path **out_paths, HF::SpatialStructures::PathMember **out_path_members, int *out_sizes)
A special version of FindPaths optimized for the C_Interface.
Path FindPath(BoostGraph *bg, int start_id, int end_id)
Find a path between points A and B using Dijkstra's Shortest Path algorithm.
boost::graph_traits< graph_t >::vertex_descriptor vertex_descriptor
Quick alias to shorten the typename of vertex descriptors for our graph_t type. /summary>
Definition: boost_graph.h:139
void InsertAllToAllPathsIntoArray(BoostGraph *bg, Path **out_paths, PathMember **out_path_members, int *out_sizes)
A special version of FindPaths optimized for the C_Interface, such that all paths possible from each ...
boost::compressed_sparse_row_graph< boost::directedS, vertex_data, Edge_Cost > graph_t
Type of graph held by the BoostGraph.
Definition: boost_graph.h:135
Path ConstructShortestPathFromPred(int start, int end, const std::vector< size_t > &pred, const std::vector< float > &distances)
Construct the shortest path from start to end using the given predecessor and distance vectors.
Definition: path_finder.cpp:78
DistPred BuildDistanceAndPredecessor(const graph_t &g, int id)
Build a row of the distance and predecessor matrices for the node at id.
DistanceAndPredecessor GenerateDistanceAndPred(const BoostGraph &bg)
Generate the distance and predecessor matricies for a specific boost graph.
std::unique_ptr< BoostGraph, BoostGraphDeleter > CreateBoostGraph(const Graph &g, const std::string &cost_type)
Create a new boost graph from a HF::SpatialStructures:Graph.
float weight
Cost of traversing this edge.
Definition: boost_graph.h:100
A graph usable with the BoostGraphLibrary.
Definition: boost_graph.h:166
graph_t g
The underlying graph in boost.
Definition: boost_graph.h:168
std::vector< vertex_descriptor > p
Vertex array preallocated to the number of nodes in the graph.
Definition: boost_graph.h:169
A single row of a distance and predecessor matrix.
Definition: path_finder.cpp:48
std::vector< float > distance
Distance array.
Definition: path_finder.cpp:49
DistPred()
Construct a new instance of DistPred with empty distance and predecessor arrays.
Definition: path_finder.cpp:55
DistPred(int n)
Allocate enough space for n elements.
Definition: path_finder.cpp:61
std::vector< vertex_descriptor > predecessor
Predecessor array.
Definition: path_finder.cpp:50
void operator()(BoostGraph *bg) const
Delete a boost graph.
Holds and maintains a distance and predecessor matrix.
Definition: path_finder.h:409
int * GetRowOfPred(int i)
Get a pointer to the beginning of the ith row of the predecessor array.
Definition: path_finder.h:453
float * GetRowOfDist(int i)
Get a pointer to the beginning of the ith row of the distance array.
Definition: path_finder.h:439
A Graph of nodes connected by edges that supports both integers and HF::SpatialStructures::Node.
Definition: graph.h:486
The ID of a node, and the cost cost to the node after it.
Definition: path.h:19
A collection of nodes that form a path.
Definition: path.h:76
int size() const
Determine how many nodes are in this path.
Definition: path.cpp:20
void AddNode(int node, float cost)
Add a new node to the path.
Definition: path.cpp:12
PathMember * GetPMPointer()
Get a pointer to the path's underlying path members vector.
Definition: path.cpp:56
void Reverse()
Reverse the direction of this path.
Definition: path.cpp:24