DHART
Loading...
Searching...
No Matches
graph.cpp
Go to the documentation of this file.
1
7
9
10#include <graph.h>
11#include <algorithm>
12#include <cmath>
13#include <Constants.h>
14#include <cassert>
15#include <HFExceptions.h>
16#include <numeric>
17#include <iostream>
18#include <charconv>
19#include <json.hpp>
20#include <fstream>
21#include <ostream>
22
23using namespace Eigen;
24using std::vector;
25using std::string;
26using namespace HF::Exceptions;
27
28namespace HF::SpatialStructures {
29
30 inline bool IsInRange(int nnz, int num_rows, int parent) {
31 bool out_of_range = (nnz <= 0 || num_rows <= parent);
32 return !out_of_range;
33 }
34
43 inline TempMatrix CreateMappedCSR(const EdgeMatrix& g, const EdgeCostSet& ca) {
44
45 const Map<const SparseMatrix<float, 1>> m(
46 static_cast<int>(g.rows()),
47 static_cast<int>(g.cols()),
48 static_cast<int>(g.nonZeros()),
49 static_cast<const int*>(g.outerIndexPtr()),
50 static_cast<const int*>(g.innerIndexPtr()),
51 //static_cast<const float*>(g.valuePtr()),
52 static_cast<const float*>(ca.GetPtr()),
53 static_cast<const int*>(g.innerNonZeroPtr())
54 );
55
56 return m;
57 }
58
82 inline bool is_floating_type(const std::string & value) {
83 bool result = false;
84 char* ptr = nullptr;
85
86 std::strtof(value.c_str(), &ptr);
87
88 if (*ptr == '\0') {
89 char* next = nullptr;
90 std::strtof(value.c_str(), &next);
91 result = *next == '\0';
92 }
93
94 return result;
95 }
96
97 template<typename csr>
98 inline int IMPL_GetCost(const csr & c, int parent, int child) {
99
100 // Get the inner and outer index array pointers of the CSR
101 const auto outer_index_ptr = c.outerIndexPtr();
102 const auto inner_index_ptr = c.innerIndexPtr();
103
104 return IMPL_ValueArrayIndex(parent, child, outer_index_ptr, inner_index_ptr);
105
106 }
107
108 float Graph::GetCost(int parent_id, int child_id, const std::string & cost_type) const{
109 if (this->needs_compression)
110 throw std::logic_error("Graph must be compressed to read costs using getcost");
111
112 if (IsDefaultName(cost_type)) {
113 const int index = IMPL_GetCost(this->edge_matrix, parent_id, child_id);
114 if (index < 0 ) return NAN;
115 else return this->edge_matrix.valuePtr()[index];
116 }
117 else
118 return GetCostForSet(this->GetCostArray(cost_type), parent_id, child_id);
119 }
120
121 void Graph::ClearCostArrays(const std::string& cost_name)
122 {
123 // Delete them all if this is the default name
124 if (this->IsDefaultName(cost_name))
125 edge_cost_maps.clear();
126 else if (!this->HasCostArray(cost_name))
127 throw NoCost("Tried to delete a cost that doesn't already exist!");
128
129 else
130 edge_cost_maps.erase(cost_name);
131
132 }
133
134
135
136 inline float StringToFloat(const std::string& str_to_convert) {
137
138 // As stated in the docs for stod, this will throw if the string you're trying to convert
139 // can't be converted to a number. This will occur when the string is empty, as it is in
140 // the case of a parameter that doesn't exist.
141 try {
142 return std::stof(str_to_convert);
143 }
144 catch (std::invalid_argument) {
145 // Return -1 to signal that this string could not be converted
146 // to a decimal number
147 return -1;
148 }
149 }
150
151 inline std::vector<float> ConvertStringsToFloat(const std::vector<string>& strings) {
152 // Initialize a vector that's big enough to hold the result for every value in strings
153 std::vector<float> out_floats(strings.size());
154
155 // Iterate through every attribute in strings and convert them to floating point numbers
156 for (int i = 0; i < out_floats.size(); i++)
157 out_floats[i] = StringToFloat(strings[i]);
158
159 // Return output
160 return out_floats;
161 }
162
164 const std::string& node_attribute,
165 const std::string & out_attribute,
166 Direction gen_using)
167 {
168 // Throw if we don't have this attribute
169 if (!this->HasNodeAttribute(node_attribute))
170 throw std::out_of_range("Node Attribute" + node_attribute + " doesn't exist in the graph!");
171
172 // Delete the cost set that already exists at out_attribute unless out_attribute is the default cost set
173 // in which case throw because we'd be clearing the entire graph.
174 if (!this->IsDefaultName(out_attribute)) {
175 if (this->HasCostArray(out_attribute))
176 this->ClearCostArrays(out_attribute);
177 }
178 else
179 throw std::out_of_range("Cost Set" + out_attribute + " is the default cost of the graph and can't be overwritten!");
180
181 // Get the costs for this attribute and convert every value to float. In the case that a
182 // attribute score could not be converted due to not being a numeric value or not being set
183 // in the first place, those values will be set to -1.
184 const auto& scores = ConvertStringsToFloat(this->GetNodeAttributes(node_attribute));
185
186 // Iterate through all nodes in the graph
187 for (const auto& parent : ordered_nodes) {
188
189 // If this parent has no score for this attribute, don't do anything
190 if (scores[parent.id] == -1) continue;
191
192 // Get the subgraph for this node
193 const auto subgraph = this->GetIntEdges(parent.id);
194 // Iterate through every edge of this node
195 for (const IntEdge& edge : subgraph)
196 {
197 // If this child has no score for this attribute, skip it.
198 if (scores[edge.child] == -1) continue;
199
200 // Calculate the cost of this node based on the input direction
201 float cost = -1;
202 switch (gen_using) {
204 // If the direction is incoming, then the only cost we care about is the cost of
205 // the node that is being traversed to, the child.
206 cost= scores[edge.child];
207 break;
208 case Direction::BOTH:
209
210 // If BOTH is specified, then we sum the costs of both the parent node and the childn\
211 // node since we care about the costs of both
212 cost= scores[edge.child] + scores[parent.id];
213 break;
214
216 // If this is out going, then the score is entirely determined by the parent node
217 // since it is the node being traversed from.
218 cost = scores[parent.id];
219 break;
220 }
221
222 // Add it to the graph as an edge for the cost type specified in out_attribute
223 this->addEdge(parent.id, edge.child, cost, out_attribute);
224 }
225 }
226 }
227
228 int Graph::size() const { return ordered_nodes.size(); }
229
230 int Graph::MaxID() const {
231 // If the nodes are ordered, the maximum ID is the ID of the last node in the array
232 if(!this->nodes_out_of_order)
233 // If this empty, the MaxID should be zero.
234 return this->ordered_nodes.empty() ? 0 : ordered_nodes.back().id;
235 //Otherwise if the graph's nodes aren`t ordered, then we need to find the maximum
236 // id by comparing the IDS of every node in the graph.
237 else {
238 int max_id = -1;
239
240 for (const auto& node : ordered_nodes)
241 max_id = std::max(node.id, max_id);
242
243 return max_id;
244 }
245 }
246
247 int Graph::getID(const Node& node) const
248 {
249 // First check if we have this node
250 if (hasKey(node))
251
252 // If so, return its id
253 return idmap.at(node);
254
255 // If not, return -1
256 else
257 return -1;
258 }
259
260 EdgeCostSet& Graph::GetCostArray(const string& key)
261 {
262 // If this is the default key, then you're missing a check
263 // somewhere.
264 assert(!IsDefaultName(key));
265
266 if (!this->HasCostArray(key))
267 throw NoCost("Tried to access a key that doesn't exist in the cost_arrays");
268
269 // Return the cost map at key.
270 return (edge_cost_maps.at(key));
271 }
272
273 bool Graph::HasCostArray(const string & key) const {
274 return (edge_cost_maps.count(key) > 0);
275 }
276
277 EdgeCostSet& Graph::GetOrCreateCostType(const std::string& name)
278 {
279 // If this is the default name, then we must throw
280 if (this->IsDefaultName(name))
281 throw std::logic_error("Tried to create cost array with the graph's default name");
282
283 // If we already have this cost array, return a reference to it
284 if (this->HasCostArray(name))
285 return this->GetCostArray(name);
286
287 // Otherwise create a new one then return a reference to it.
288 else
289 return this->CreateCostArray(name);
290 }
291
292 EdgeCostSet& Graph::CreateCostArray(const std::string& name)
293 {
294 // If you manage to throw this, something is wrong with
295 // CheckCostArray
296 assert(!this->HasCostArray(name));
297
298 if (this->size() < 1)
299 throw std::logic_error("Tried to add a cost to a graph with 0 nodes");
300
301 // the size of the cost array must be large enough to hold all nonzeros
302 // for this specific CSR
303 const int nnz = edge_matrix.nonZeros();
304
305 // Create a new cost set large enough to hold all non-zeros in the graph
306 // then insert it into the hashmap.
307 edge_cost_maps.insert({ name, EdgeCostSet(nnz) });
308
309 // Get and return it
310 return GetCostArray(name);
311 }
312
313 const EdgeCostSet& Graph::GetCostArray(const std::string& key) const
314 {
315 // Ensure that we throw our custom exception if this key doesn't exist
316 if (!this->HasCostArray(key))
317 throw NoCost(key);
318
319
320 // Get the cost from the cost map
321 return (edge_cost_maps.at(key));
322 }
323
324 bool Graph::IsDefaultName(const string& name) const
325 {
326 // Check if the name is empty "" or it matches our default
327 // cost member.
328 return (name.empty() || (name == this->default_cost));
329 }
330
331 int IMPL_ValueArrayIndex(
332 int parent_id,
333 int child_id,
334 const int* outer_index_ptr,
335 const int* inner_index_ptr
336 ) {
337 // Get the bounds for our search
338 const int search_start_index = outer_index_ptr[parent_id];
339 const int search_end_index = outer_index_ptr[parent_id + 1];
340
341 // Get a pointer to the start and end of our search bounds.
342 const int* search_start_ptr = inner_index_ptr + search_start_index;
343 const int* search_end_ptr = inner_index_ptr + search_end_index;
344
345 // Use an iterator to find a pointer to the value in memory
346 auto itr = std::find(search_start_ptr, search_end_ptr, child_id);
347
348 // Return if we hit the end of our search bounds, indicating that the
349 // edge doesn't exist.
350
351 if (itr == search_end_ptr) {
352 return -1;
353 }
354 // Find the distance between the pointer we found earlier and the
355 // start of the values array
356 else {
357 int index = std::distance(inner_index_ptr, itr);
358
359 return index;
360 }
361
362 }
363
364 int Graph::FindValueArrayIndex(int parent_id, int child_id) const
365 {
366 if (!IsInRange(edge_matrix.nonZeros(), edge_matrix.rows(), parent_id)) return -1;
367
368 // Get the inner and outer index array pointers of the CSR
369 const auto outer_index_ptr = edge_matrix.outerIndexPtr();
370 const auto inner_index_ptr = edge_matrix.innerIndexPtr();
371
372
373 int index = IMPL_ValueArrayIndex(parent_id, child_id, outer_index_ptr, inner_index_ptr);
374 if (index > edge_matrix.nonZeros())
375 return -1;
376 else
377 return index;
378 }
379
380 void Graph::InsertEdgeIntoCostSet(int parent_id, int child_id, float cost, EdgeCostSet& cost_set) {
381 // Get the index of the edge between parent_id and child_id in the values array
382 const int value_index = FindValueArrayIndex(parent_id, child_id);
383
384 // If the index is < 0 that means this edge doesn't exist and we must throw
385 if (value_index < 0) {
386 throw std::out_of_range("Tried to insert into edge that doesn't exist in default graph. ");
387 }
388
389 // otherwise write this to the cost set.
390 cost_set[value_index] = cost;
391 }
392
393 void Graph::InsertEdgesIntoCostSet(EdgeCostSet& cost_set, const std::vector<EdgeSet>& es)
394 {
395 // Iterate through every edgeset in es
396 for (const auto& edge_set : es)
397 {
398 // Get the parent of this specific edge set
399 const int parent_id = edge_set.parent;
400
401 // Iterate through every edge in this edge set.
402 for (const auto& edge : edge_set.children) {
403
404 // Get the child id and cost of this edge
405 const int child_id = edge.child;
406 const float cost = edge.weight;
407
408 // Insert this edge into cost_set
409 InsertEdgeIntoCostSet(parent_id, child_id, cost, cost_set);
410 }
411 }
412 }
413
414 CSRPtrs Graph::GetCSRPointers(const string& cost_type)
415 {
416 const bool default_cost = this->IsDefaultName(cost_type);
417
418 // The graph must be compressed for this to work
419 if (default_cost)
420 Compress();
421
422 // Construct CSRPtr with the required info from edge_matrix.
423 CSRPtrs out_csr{
424 static_cast<int>(edge_matrix.nonZeros()),
425 static_cast<int>(edge_matrix.rows()),
426 static_cast<int>(edge_matrix.cols()),
427
428 edge_matrix.valuePtr(),
429 edge_matrix.outerIndexPtr(),
430 edge_matrix.innerIndexPtr()
431 };
432
433 // If this isn't the default cost, replace the pointer with
434 // the pointer of the given cost array.
435 if (!default_cost)
436 {
437 EdgeCostSet& cost_set = this->GetCostArray(cost_type);
438 out_csr.data = cost_set.GetPtr();
439 }
440
441 return out_csr;
442 }
443
444 Node Graph::NodeFromID(int id) const { return ordered_nodes.at(id); }
445
446 std::vector<Node> Graph::Nodes() const {
447 return ordered_nodes;
448 }
449
467 template<typename csr>
468 inline vector<Edge> IMPL_UndirectedEdges(const csr& edge_matrix, const int parent_id, const Graph * g) {
469
470 // If N is not in the graph, return an empty array.
471 const int node_id = parent_id;
472 if (node_id < 0) return vector<Edge>();
473
474 // Get the directed edges for this node from calling operator[]
475 vector<Edge> out_edges;
476
477 // Iterate through every other node
478 for (int i = 0; i < edge_matrix.rows(); i++) {
479
480 // Don't look in this node's edge array
481 if (i == node_id) continue;
482
483 // See if there's an edge between I and node_id
484 if (edge_matrix.coeff(i, node_id) != 0) {
485
486 // if so, get its cost
487 float cost = edge_matrix.coeff(i, node_id);
488
489 // Get the node belonging to the child's id
490 Node child_node = g->NodeFromID(i);
491
492 // Construct a new edge and push it back into ot_edges
493 Edge edge{ child_node, cost };
494 out_edges.push_back(edge);
495 }
496 }
497 return out_edges;
498 }
499
500 vector<Edge> Graph::GetUndirectedEdges(const Node& n, const std::string& cost_type) const {
501 // call GetEdgesForNode, since it already handles this.
502 return this->GetEdgesForNode(getID(n), true, cost_type);
503 }
504
505 std::vector<EdgeSet> Graph::GetEdges() const
506 {
507
508 // Throw if we're not compressed since this is a const function and compressing the graph
509 // will make it non-const
510 if (this->needs_compression)
511 throw std::exception("The graph must be compressed!");
512
513 // Preallocate an array of edge sets
514 vector<EdgeSet> out_edges(this->size());
515
516 // Iterate through every row in the csr
517 for (int node_index = 0; node_index < this->size(); ++node_index) {
518 const int node_id = node_index;
519
520 auto& edgeset = out_edges[node_id];
521 edgeset.parent = node_id;
522
523 // Iterate every column in the row.
524 for (SparseMatrix<float, 1>::InnerIterator it(edge_matrix, node_index); it; ++it)
525 {
526 // Add to array of edgesets
527 float cost = it.value();
528 int child =it.col();
529 edgeset.children.push_back(IntEdge{ child, cost });
530 }
531 }
532 return out_edges;
533 }
534
535 std::vector<IntEdge> Graph::GetIntEdges(int parent) const
536 {
537 // If this node is not in the graph, just return an empty array, since otherwise the following code
538 // will crash in release mode
539 if (parent > this->MaxID()) return std::vector<IntEdge>();
540
541 // Iterate through all of the edges in the graph
542 std::vector<IntEdge> intedges;
543 for (SparseMatrix<float, 1>::InnerIterator it(edge_matrix, parent); it; ++it)
544 {
545 // Get the cost and the child of this edge
546 float cost = it.value();
547 int child = it.col();
548
549 // Push it back to our return array
550 intedges.push_back(IntEdge{ child, cost });
551 }
552
553 // Return the edges for this node.
554 return intedges;
555 }
556
562
573 inline void Aggregate(float& out_total, float new_value, const COST_AGGREGATE agg_type, int & count)
574 {
575 switch (agg_type) {
577
578 // Only increment count if newvalue is equal to zero.
579 // In the case that out_total is zero and count is greater than zero,
580 // multiple new elements have been added, and we must not increment
581 if (new_value > 0 && !(out_total == 0 && count > 0))
582 count++;
583 out_total = count;
584 break;
586 out_total += new_value;
587 break;
589 // If this is zero this function won't work.
590 if (count == 0) count = 1;
591
592 out_total = out_total + (new_value - out_total) / static_cast<float>(count);
593 count++;
594 break;
595 }
596 default:
597 throw std::out_of_range("Unimplemented aggregation type");
598 break;
599 }
600 assert((out_total == 0 || isnormal(out_total)));
601 return;
602 }
603
626 template<typename csr>
627 std::vector<float> Impl_AggregateGraph(COST_AGGREGATE agg_type, int num_nodes, bool directed, const csr & edge_matrix) {
628
629 // Create an array of floats filled with zeros.
630 vector<float> out_costs(num_nodes, 0);
631
632 // If directed is true, then we only need the values in a node's row to calculate it's score.
633 if (directed)
634 for (int k = 0; k < num_nodes; ++k) {
635
636 // Sum all values in the row for node k
637 const float sum = edge_matrix.row(k).sum();
638
639 // Get the count of non_zeros for node k
640 int count = edge_matrix.row(k).nonZeros();
641
642 // Run aggregate once with the sum and count of the non-zeros in row k
643 Aggregate(out_costs[k], sum, agg_type, count);
644
645 }
646
647 // Based on implementation from Eigen's sparse matrix tutorial: Iterating over the nonzero
648 // coefficents: https://eigen.tuxfamily.org/dox/group__TutorialSparse.html If directed is
649 // false, then every edge needs to be iterated through since edges that go to a node also
650 // count towards its score.
651 else {
652 // We need to keep track of count per node as well for this algorithm
653 vector<int> count(num_nodes, 0);
654
655 // Iterate through every node in the graph
656 for (int k = 0; k < num_nodes; ++k) {
657
658 // Iterate through every edge for the node at column k
659 for (csr::InnerIterator it(edge_matrix, k); it; ++it)
660 {
661 // Get values from the iterator for this row/col.
662 float cost = it.value();
663 int child = it.col();
664 int parent = it.row();
665
666 // Aggregate costs for both the parent and the child.
667 Aggregate(out_costs[parent], cost, agg_type, count[parent]);
668 Aggregate(out_costs[child], cost, agg_type, count[child]);
669 }
670 }
671 }
672 return out_costs;
673 }
674
675 std::vector<float> Graph::AggregateGraph(COST_AGGREGATE agg_type, bool directed, const string& cost_type) const
676 {
677 // This won't work if the graph isn't compressed.
678 if (this->needs_compression) throw std::exception("The graph must be compressed!");
679
680 // Determine if this is the default cost.
681 const bool default_cost = this->IsDefaultName(cost_type);
682
683 // If this isn't the default cost, map to the cost array.
684 if (!default_cost) {
685
686 // Get the cost array and create a mapped CSR from it
687 const EdgeCostSet & cost_array = this->GetCostArray(cost_type);
688 const TempMatrix cost_matrix= CreateMappedCSR(this->edge_matrix, cost_array);
689
690 // Call the implementation of aggregate graph with this template
691 return Impl_AggregateGraph(agg_type, this->size(), directed, cost_matrix);
692 }
693 else
694 // If this is the default cost, just call AggregateGraph with the default CSR
695 return (Impl_AggregateGraph(agg_type, this->size(), directed, this->edge_matrix));
696 }
697
698 const std::vector<Edge> Graph::operator[](const Node& n) const
699 {
700 return GetEdgesForNode(this->getID(n));
701 }
702
703 void Graph::InsertOrUpdateEdge(int parent_id, int child_id, float score, const string& cost_type) {
704
705 // If this is the default graph, we don't need to worry aobut
706 if (IsDefaultName(cost_type)) {
707 if (this->needs_compression)
708 TripletsAddOrUpdateEdge(parent_id, child_id, score);
709 else
710 CSRAddOrUpdateEdge(parent_id, child_id, score);
711 }
712 else
713 // Can't add to an alternate cost if the graph isn't compressed
714 if (this->needs_compression)
715 throw std::logic_error("Tried to add an edge to an alternate cost type while uncompressed!");
716
717 // Add the cost to the cost type pointed to by cost_type
718 else
719 {
720 // Determine whether or not this cost type exists
721 const bool new_cost_array = !HasCostArray(cost_type);
722
723 // Create a new cost array or get a reference to an existing one
724 auto& cost_set = GetOrCreateCostType(cost_type);
725
726 // Try to add an edge. We need to ensure we maintain the graph's invariants
727 // in the case that this throws.
728 try {
729 InsertEdgeIntoCostSet(parent_id, child_id, score, cost_set);
730 }
731 catch(const std::out_of_range & e){
732
733 // If this is a new cost array, and we threw that would leave it in an invalid
734 // state which will cause problems later. Remove this half baked cost array
735 // before returning from this exception.
736 if (new_cost_array)
737 this->ClearCostArrays(cost_type);
738
739 // Rethrow here so callers know we failed to complete
740 throw e;
741 }
742 }
743 }
744
745 float Graph::GetCostForSet(const EdgeCostSet & set, int parent_id, int child_id) const
746 {
747 // Get the index for the cost of the edge between parent_id and child_id
748 const int index = FindValueArrayIndex(parent_id, child_id);
749
750 // If the index is <0, the edge doesn't exist.
751 // We represent non-existant edges as NAN
752 if (index < 0)
753 return NAN;
754
755 // Othrwise, return the value at this index in set
756 else
757 return set[index];
758 }
759
760 vector<Edge> Graph::GetEdgesForNode(int parent_id, bool undirected, const string & cost_type) const
761 {
762 // Get the row of this node
763 const int row = parent_id;
764 const bool default_name = this->IsDefaultName(cost_type);
765
766 std::vector<Edge> outgoing_edges;
767
768 // Get all of the outgoing edges by iterating through the array if it's the default
769 if (default_name) {
770 for (SparseMatrix<float, 1>::InnerIterator it(edge_matrix, row); it; ++it) {
771 const auto col = it.col();
772 float value = it.value();
773
774 outgoing_edges.emplace_back(Edge(NodeFromID(col), value));
775 }
776 }
777 // If using a custom cost, use our own indexing function instead of getting the value from the iterator
778 else {
779 const auto & cost_set = this->GetCostArray(cost_type);
780 std::vector<Edge> out_edges;
781 for (SparseMatrix<float, 1>::InnerIterator it(edge_matrix, row); it; ++it) {
782 const auto col = it.col();
783 auto value = this->GetCostForSet(cost_set, row, col);
784
785 outgoing_edges.emplace_back(Edge(NodeFromID(col), value));
786 }
787 }
788
789 // If this is undirected, we'll need to get a list of incoming edges as well.
790 if (undirected) {
791 vector<Edge> incoming_edges;
792
793 // Default name uses the default edge_matrix member
794 if (default_name)
795 incoming_edges = IMPL_UndirectedEdges(this->edge_matrix, parent_id, this);
796
797 // If we're using a custom type, map a CSR to it and use that instead
798 else
799 incoming_edges = IMPL_UndirectedEdges(this->MapCostMatrix(cost_type), parent_id, this);
800
801 // Combine the incoming and outgoing edges
802 vector<Edge> incoming_and_outgoing(incoming_edges.size() + outgoing_edges.size());
803
804 // Move the contents of outgoing and incoming into this output array, then return it
805 std::move(outgoing_edges.begin(), outgoing_edges.end(), incoming_and_outgoing.begin());
806 std::move(incoming_edges.begin(), incoming_edges.end(), incoming_and_outgoing.begin() + outgoing_edges.size());
807
808 return incoming_and_outgoing;
809 }
810 else
811 return outgoing_edges;
812
813 }
814
815 TempMatrix Graph::MapCostMatrix(const std::string& cost_type) const
816 {
817 const EdgeCostSet& cost_array = this->GetCostArray(cost_type);
818 const TempMatrix cost_matrix = CreateMappedCSR(this->edge_matrix, cost_array);
819 return cost_matrix;
820 }
821
822 bool Graph::HasNodeAttribute(const std::string& key) const
823 {
824 return this->node_attr_map.count(key) > 0;
825 }
826
827 void Graph::addEdge(const Node& parent, const Node& child, float score, const string & cost_type)
828 {
829 // ![GetOrAssignID_Node]
830
831 // Get parent/child ids
832 int parent_id = getOrAssignID(parent);
833 int child_id = getOrAssignID(child);
834
835 // If this is already compressed, update the CSR, otherwise add it to the list of triplets.
836 InsertOrUpdateEdge(parent_id, child_id, score, cost_type);
837 // ![GetOrAssignID_Node]
838 }
839
840 void Graph::addEdge(int parent_id, int child_id, float score, const string & cost_type)
841 {
842 // ![GetOrAssignID_int]
843
844 // Store these Ids in the hashmap if they don't exist already.
845 getOrAssignID(child_id);
846 getOrAssignID(parent_id);
847
848 InsertOrUpdateEdge(parent_id, child_id, score, cost_type);
849
850 // ![GetOrAssignID_int]
851 }
852
853 bool Graph::checkForEdge(int parent, int child) const {
854 // ![CheckForEdge]
855
856 // Get the index for parent and child
857 const int parent_index = parent;
858 const int child_index = child;
859
860 // If the parent is not even in the graph, or the graph doesn't have any zeros, return early.
861 // Calling the iterator in both of these cases is undefined behavior and should be avoided
862 if (!IsInRange(edge_matrix.nonZeros(), edge_matrix.rows(), parent)) return false;
863 // if (edge_matrix.nonZeros() <= 0 || edge_matrix.rows() <= parent) return false;
864
865 // Iterate through parent's row to see if it has child.
866 for (EdgeMatrix::InnerIterator it(edge_matrix, parent_index); it; ++it) {
867 if (it.col() == child_index) return true;
868 }
869
870 // If we've gotten to this point, then the child doesn't exist in parent's row
871 return false;
872 // ![CheckForEdge]
873 }
874
875 void Graph::CSRAddOrUpdateEdge(int parent_id, int child_id, float cost)
876 {
877 const int parent_index = parent_id;
878 const int child_index = child_id;
879
880 // Use coeffref if the cost already exists to avoid duplicate allocations
881 if (HasEdge(parent_index, child_index))
882 edge_matrix.coeffRef(parent_index, child_index) = cost;
883 else {
884 // Reallocate if we must, then insert.
886 edge_matrix.insert(parent_index, child_index) = cost;
887 }
888 }
889
890 void Graph::TripletsAddOrUpdateEdge(int parent_id, int child_id, float cost) {
891
892 // Add this edge to the list of triplets.
893 triplets.emplace_back(Eigen::Triplet<float>(parent_id, child_id, cost));
894 }
895
897 {
898 int num_nodes = -1;
899
900 // Find the maximum ID in the graph if using Int Nodes
902 num_nodes = MaxID();
903 else
904 num_nodes = size();
905
906 // You need one more row/col than capacity
907 num_nodes += 1;
908
909 // If the edge matrix can't fit this many nodes, expand it.
910 if (num_nodes > edge_matrix.rows())
911
912 // Conservative resize preserves all of the values in the graph
913 edge_matrix.conservativeResize(num_nodes, num_nodes);
914
915 assert(num_nodes <= edge_matrix.rows() && num_nodes <= edge_matrix.cols());
916 }
917
918 inline bool Graph::hasKey(int id) const
919 {
920 // If nodes are ordered, then we can simply compare this node's ID to the
921 // id of the final node in the ordered_nodes array.
922 if (!this->nodes_out_of_order)
923 return (id <= this->MaxID());
924
925 else
926 // Otherwise we need to loop through every node and check equality.
927 for (int i = 0; i < ordered_nodes.size(); i++)
928 if (ordered_nodes[i].id == id) return true;
929
930 return false;
931 }
932
933 bool Graph::HasEdge(int parent, int child, bool undirected, const string & cost_type) const {
934 // Check if these IDS even exist in the graph.
935 if (!this->hasKey(parent) || !this->hasKey(child)) return false;
936
937 // If this is the default name, check for the cost in the base CSR
938 else if (IsDefaultName(cost_type))
939 return (checkForEdge(parent, child) || ( undirected && checkForEdge(child, parent) ) );
940
941 // If this isn't the default name, then get it from the cost set it's asking for
942 else {
943 // If this doesn't have the cost array defined before, return
944 if (!this->HasCostArray(cost_type)) {
945 return false;
946 }
947 // Otherwise get the cost array and try to find it.
948 const auto& cost_array = GetCostArray(cost_type);
949 const auto cost = GetCostForSet(cost_array, parent, child);
950
951 // If undirected is specified, call this fucntion again from parent to child
952 // with undirected set to false.
953 bool check_undirected = undirected ? HasEdge(child, parent, false, cost_type) : false;
954
955 return (!isnan(cost) || check_undirected);
956 }
957 }
958
959 bool Graph::HasEdge(const Node& parent, const Node& child, const bool undirected, const string cost_type) const {
960
961 // Throw if the graph isn't compresesed.
962 if (!edge_matrix.isCompressed())
963 throw std::exception("Can't get this for uncompressed matrix!");
964
965 // Return early if parent or child don't exist in the graph
966 if (!hasKey(parent) || !hasKey(child)) return false;
967
968 // Get the id of both parent and child.
969 int parent_id = getID(parent);
970 int child_id = getID(child);
971
972 // Call integer overload.
973 return HasEdge(parent_id, child_id, undirected);
974 }
975
976 inline int Graph::getOrAssignID(const Node& input_node)
977 {
978 // If it's already in the hashmap, then just return the existing ID
979 if (hasKey(input_node))
980 return getID(input_node);
981
982 else {
983 // Set the id in the hashmap, and add the node to nodes
984 idmap[input_node] = next_id;
985 ordered_nodes.push_back(input_node);
986
987 ordered_nodes.back().id = next_id;
988 // Increment next_id
989 next_id++;
990
991 // Return the node's new ID
992 return next_id - 1;
993 }
994 }
995
996 int Graph::getOrAssignID(int input_int)
997 {
998 // If this ID isn't in our list, add it, and create an empty node in ordered
999 // nodes to take up space.
1000 if (!hasKey(input_int))
1001 {
1002 // If we're adding a new key that's an integer
1003 // ordered_nodes is no longer gauranteed to be
1004 // in order and we must tell the graph this.
1005 this->nodes_out_of_order = true;
1006
1007 // Add an empty node to ordered_nodes
1008 ordered_nodes.push_back(Node());
1009
1010 // Set the id of the empty node
1011 ordered_nodes.back().id = input_int;
1012
1013 this->next_id = std::max(input_int, this->next_id);
1014 }
1015
1016 return input_int;
1017 }
1018
1020 const vector<vector<int>>& edges,
1021 const vector<vector<float>> & distances,
1022 const vector<Node> & Nodes,
1023 const std::string & default_cost
1024 ) {
1025
1026 this->default_cost = default_cost;
1027
1028 // Generate an array with the size of every column from the size of the edges array
1029 assert(edges.size() == distances.size());
1030 vector<int> sizes(edges.size());
1031 for (int i = 0; i < edges.size(); i++)
1032 sizes[i] = edges[i].size();
1033
1034 // Create the graph then reserve these sizes
1035 edge_matrix.resize(edges.size(), edges.size());
1036 edge_matrix.reserve(sizes);
1037
1038 // Iterate through every node in nodes
1039 for (int row_num = 0; row_num < edges.size(); row_num++)
1040 {
1041 //Add this node to our dictionary/ordered_node list
1042 getOrAssignID(Nodes[row_num]);
1043
1044 // Get the row out of the edges array
1045 const auto & row = edges[row_num];
1046 for (int i = 0; i < row.size(); i++) {
1047
1048 // Get the column and distance from the row and distance array
1049 float dist = distances[row_num][i];
1050 int col_num = row[i];
1051
1052 // Insert it into the edge matrix.
1053 edge_matrix.insert(row_num, col_num) = dist;
1054 }
1055 }
1056
1057 // Compress the edge matrix to finalize it.
1058 edge_matrix.makeCompressed();
1059 //assert(edge_matrix.nonZeros() > 0);
1060 needs_compression = false;
1061 }
1062
1063 Graph::Graph(const std::string & default_cost_name)
1064 {
1065 // Assign default cost type, and create an edge matrix.
1066 this->default_cost = default_cost_name;
1067 }
1068
1069 bool Graph::HasEdge(const std::array<float, 3>& parent, const std::array<float, 3>& child, bool undirected) const {
1070 // Just convert it and pass it over to the other HasEdge
1071 const Node p(parent);
1072 const Node c(child);
1073 return HasEdge(p, c, undirected);
1074 }
1075
1076 bool Graph::hasKey(const Node& n) const { return (idmap.count(n) > 0); }
1077
1078 std::vector<std::array<float, 3>> Graph::NodesAsFloat3() const
1079 {
1080 // Get a constant reference to ordered_nodes to maintain const-ness of this function.
1081 const auto & N= ordered_nodes;
1082 int n = N.size();
1083
1084 // Preallocate output array
1085 std::vector <std::array<float, 3> > out_nodes(n);
1086
1087 // Assign x,y,z for every node in nodes.
1088 for (int i = 0; i < n; i++) {
1089 out_nodes[i][0] = N[i].x;
1090 out_nodes[i][1] = N[i].y;
1091 out_nodes[i][2] = N[i].z;
1092 }
1093
1094 return out_nodes;
1095 }
1096
1097 void Graph::Compress() {
1098
1099 // Only do this if the graph needs compression.
1100 if (needs_compression) {
1101
1102 // If this has cost arrays then we never should have come here
1103 assert(!this->has_cost_arrays);
1104
1105 // Note that the matrix must have atleast one extra row/column
1106 int array_size = this->size() + 1;
1107
1108 // Resize the edge matrix
1110
1111 // Set the edge matrix from triplets.
1112 edge_matrix.setFromTriplets(triplets.begin(), triplets.end());
1113
1114 // Mark this graph as not requiring compression
1115 needs_compression = false;
1116 }
1117 }
1118
1119 void Graph::Clear() {
1120 edge_matrix.setZero();
1121 edge_matrix.data().squeeze();
1122 triplets.clear();
1123 needs_compression = true;
1124
1125 // Other graph representations should be cleared too
1126 ordered_nodes.clear();
1127 idmap.clear();
1128
1129 // Clear all cost arrays
1130 // Clear all cost arrays.
1131 for (auto& cost_map : edge_cost_maps)
1132 cost_map.second.Clear();
1133 }
1134
1135 void Graph::AddEdges(const vector<EdgeSet>& edges, const string& cost_name)
1136 {
1137 for (const auto& set : edges)
1138 AddEdges(set, cost_name);
1139 }
1140
1141
1142 void Graph::AddEdges(const vector<vector<IntEdge>> & edges, const std::string & cost_type) {
1143 // Each outer vector represents a parent;
1144 for (int parent = 0; parent < edges.size(); parent++)
1145 {
1146 const auto& outgoing_edges = edges[parent];
1147 for (const auto& edge : outgoing_edges)
1148 this->addEdge(parent, edge.child, edge.weight, cost_type);
1149 }
1150 }
1151
1152 void Graph::AddEdges(const EdgeSet & edges, const string& cost_name) {
1153 const auto parent = edges.parent;
1154
1155 if (this->IsDefaultName(cost_name))
1156 for (const auto& edge : edges.children)
1157 this->addEdge(parent, edge.child, edge.weight);
1158 else
1159 for (const auto& edge : edges.children)
1160 this->addEdge(parent, edge.child, edge.weight, cost_name);
1161 }
1162
1163
1164 vector<EdgeSet> Graph::GetEdges(const string & cost_name) const
1165 {
1166 // Call the other function if they're asking for the default.
1167 if (this->IsDefaultName(cost_name))
1168 return GetEdges();
1169
1170 // Preallocate an array of edge sets
1171 vector<EdgeSet> out_edges(this->size());
1172
1173 // Get the asked for cost set
1174 const auto& cost_set = this->GetCostArray(cost_name);
1175
1176 // Iterate through every row in the csr
1177 for (int parent_index = 0; parent_index < this->size(); ++parent_index) {
1178
1179 auto& edgeset = out_edges[parent_index];
1180 edgeset.parent = parent_index;
1181
1182 // Iterate every column in the row.
1183 for (SparseMatrix<float, 1>::InnerIterator it(edge_matrix, parent_index); it; ++it)
1184 {
1185 // Add to array of edgesets
1186 int child_index = it.col();
1187
1188 int cost_index = this->FindValueArrayIndex(parent_index, child_index);
1189 float cost = cost_set[cost_index];
1190
1191 edgeset.children.push_back(IntEdge{ child_index, cost });
1192 }
1193 }
1194 return out_edges;
1195 }
1196
1197 vector<std::string> Graph::GetCostTypes() const
1198 {
1199 vector<string> cost_types;
1200
1201 // Iterate through the dictionary, adding the key of each
1202 // cost type into the output array
1203 for (const auto & it : this->edge_cost_maps)
1204 cost_types.push_back(it.first);
1205
1206 return cost_types;
1207 }
1208
1209 std::vector<Node> Graph::GetChildren(const Node& n) const {
1210 std::vector<Node> children;
1211
1212 auto edges = (*this)[n];
1213
1214 for (auto e : edges) {
1215 children.push_back(e.child);
1216 }
1217
1218 return children;
1219 }
1220
1221 std::vector<Node> Graph::GetChildren(const int parent_id) const {
1222 return GetChildren(NodeFromID(parent_id));
1223 }
1224
1225 Subgraph Graph::GetSubgraph(const Node & parent_node, const string & cost_type) const {
1226 return this->GetSubgraph(this->getID(parent_node), cost_type);
1227 }
1228
1229 Subgraph Graph::GetSubgraph(int parent_id, const string & cost_type) const {
1230 Node parent_node = ordered_nodes[parent_id];
1231 return Subgraph{ parent_node, this->GetEdgesForNode(parent_id, false, cost_type) };
1232 }
1233
1234 void Graph::AddNodeAttribute(int id, const std::string & attribute, const std::string & score) {
1235 // Check if this id belongs to any node in the graph
1236 if (id > this->MaxID()) return;
1237
1238 /* // requires #include <algorithm>, but not working?
1239 std::string lower_cased =
1240 std::transform(attribute.begin(), attribute.end(),
1241 [](unsigned char c) { return std::tolower(c); }
1242 );
1243 */
1244 std::string lower_cased = attribute;
1245
1246 // Retrieve an iterator to the [node attribute : NodeAttributeValueMap]
1247 // that corresponds with attribute
1248 auto node_attr_map_it = node_attr_map.find(lower_cased);
1249
1250 if (node_attr_map_it == node_attr_map.end()) {
1251 // If the attribute type does not exist...create it.
1252 node_attr_map[lower_cased] = NodeAttributeValueMap();
1253
1254 // Update this iterator so it can be used in the next code block
1255 node_attr_map_it = node_attr_map.find(lower_cased);
1256 }
1257
1258 // We now have the NodeAttributeValueMap for the desired attribute.
1259 // A NodeAttributeValueMap stores buckets of [node id : node attribute value as string]
1260 NodeAttributeValueMap& node_attr_value_map = node_attr_map_it->second;
1261
1262 // Need to see if id exists as a key within node_attr_value_map
1263 // This will give us the position of a bucket containing:
1264 // [node id : node attribute value as string]
1265 auto node_attr_value_map_it = node_attr_value_map.find(id);
1266
1267 if (node_attr_value_map_it == node_attr_value_map.end()) {
1268 // If the node id provided does not exist in the value map...add it.
1269 node_attr_value_map[id] = score;
1270
1271 // Update this iterator so it can be used in the next code block
1272 node_attr_value_map_it = node_attr_value_map.find(id);
1273 }
1274
1275 // Will be used to assess whether it is floating point, or not
1276 std::string found_attr_value = node_attr_value_map_it->second;
1277
1278 // Let's determine the data type of score:
1279 bool score_is_floating_pt = is_floating_type(score);
1280
1281 // Let's determine the data type of found_attr_value:
1282 bool attr_is_floating_pt = is_floating_type(found_attr_value);
1283
1284 /*
1285 Need to determine if found_attr_value is
1286 - a string
1287 - a floating point value
1288
1289 and if the data type for score matches that of found_attr_value
1290 */
1291 if (attr_is_floating_pt) {
1292 // if the current attribute value is floating point...
1293 if (score_is_floating_pt) {
1294 // Ok - data type matched.
1295 node_attr_value_map_it->second = score;
1296 }
1297 else {
1298 // error?
1299 }
1300 }
1301 else {
1302 // if the current attribute value is not floating point...
1303 if (score_is_floating_pt) {
1304 // error?
1305 }
1306 else {
1307 // Ok - data type matched
1308 node_attr_value_map_it->second = score;
1309 }
1310 }
1311 }
1312
1314 const vector<int> & id,
1315 const string & name,
1316 const vector<string> & scores
1317 ) {
1318 // If size of id container and size of scores container are not in alignment,
1319 // throw, since our precondition was violated
1320 if (id.size() != scores.size())
1321 throw std::logic_error("Tried to pass id and string arrays that are different lengths");
1322
1323 auto scores_iterator = scores.begin();
1324
1325 for (int node_id : id) {
1326
1327 // We can call AddNodeAttribute for each node_id in id.
1328 // If the attribute type name does not exist,
1329 // it will be created with the first invocation of AddNodeAttribute.
1330 AddNodeAttribute(node_id, name, *(scores_iterator++));
1331 }
1332 }
1333
1334 vector<string> Graph::GetNodeAttributes(string attribute) const {
1335
1336 // Return an empty array if this attribute doesn't exist
1337 if (node_attr_map.count(attribute) < 1) return vector<string>();
1338
1339 // Get the attribute map for attribute now that we know it exists
1340 const auto& attr_map = node_attr_map.at(attribute);
1341
1342 // Preallocate output array with empty strings. We'll only be modifying
1343 // the indexes that have scores assigned to them, and leaving the rest
1344 // as empty strings
1345 const int num_nodes = ordered_nodes.size();
1346 vector<string> out_attributes(ordered_nodes.size(), "");
1347
1348 // Iterate through attribute map to assign scores for the nodes
1349 // that have them
1350 for (const auto& it : attr_map)
1351 {
1352 // Get the id and score of this element in the hashmap
1353 const int id = it.first;
1354 const string& score = it.second;
1355
1356 // Copy it to the index of the node's ID in our output array
1357 out_attributes[id] = score;
1358 }
1359
1360 // Return all found attributes
1361 return out_attributes;
1362 }
1363
1364 void Graph::ClearNodeAttributes(std::string name) {
1365 /* // requires #include <algorithm>, but not working?
1366 std::string lower_cased =
1367 std::transform(attribute.begin(), attribute.end(),
1368 [](unsigned char c) { return std::tolower(c); }
1369 );
1370 */
1371 // std::string lower_cased = name;
1372
1373 // Check if we have this name in our dictionary. Only try to delete
1374 // it if it already exists.
1375 if (node_attr_map.count(name) > 0) {
1376
1377 // Erase the key from the dictionary, implicitly freeing the object
1378 // it contains.
1379 node_attr_map.erase(name);
1380 }
1381 }
1382
1383 using namespace nlohmann;
1384 bool Graph::DumpToJson(const std::string & path) {
1385 json j;
1386
1387 j["nodes"] = json::array();
1388
1389 for (const auto& node : ordered_nodes)
1390 j["nodes"].push_back(json::array({node[0], node[1], node[2] }));
1391
1392 std::ofstream out_file;
1393
1394 std::string json_str = j.dump();
1395 out_file.open(path);
1396 out_file.write(json_str.c_str(), json_str.size());
1397 out_file.close();
1398
1399 return true;
1400 }
1401
1402}
Contains definitions for the Exceptions namespace.
Contains definitions for the HF::SpatialStructures namespace.
Contains definitions for the Graph class.
Contains standard fundamental data structures for representing space used throughout DHARTAPI.
Eigen::SparseMatrix< float, 1 > EdgeMatrix
The type of matrix the graph uses internally.
Definition: graph.h:21
COST_AGGREGATE
Methods of aggregating the costs for edges for each node in the graph.
Definition: graph.h:28
@ AVERAGE
Average the cost of all edges.
@ COUNT
Count how many edges this node has.
@ SUM
Add the cost of all edges.
Eigen::Map< const EdgeMatrix > TempMatrix
A mapped matrix of EdgeMatrix. Only owns pointers to memory.
Definition: graph.h:22
Direction
Node to use for calculating the cost of an edge when converting node attributes to edge costs.
Definition: graph.h:39
void Aggregate(float &out_total, float new_value, const AGGREGATE_TYPE agg_type, int count=0)
Custom exceptions and error codes used interally by DHARTAPI.
Definition: HFExceptions.h:22
Eigen a C++ template library for linear algebra: matrices, vectors, numerical solvers,...
Definition: objloader.h:30
namespace for Niels Lohmann
Definition: json.hpp:82
@ value
the parser finished reading a JSON value
Thrown when a dependency is missing such as Embree.
Definition: HFExceptions.h:90
void InsertEdgesIntoCostSet(EdgeCostSet &cost_set, const std::vector< EdgeSet > &es)
Insert edges for a specific cost type into a cost set.
void ClearNodeAttributes(std::string name)
Clears the attribute at name and all of its contents from the internal hashmap
bool DumpToJson(const std::string &path)
EdgeMatrix edge_matrix
The underlying CSR containing edge information.
Definition: graph.h:502
float GetCostForSet(const EdgeCostSet &set, int parent_id, int child_id) const
Get the cost of traversing the edge between parent and child using set.
void ResizeIfNeeded()
Resize the CSR to fit all the nodes in ordered_nodes if needed.
void InsertOrUpdateEdge(int parent_id, int child_id, float score, const std::string &cost_type)
Insert an edge into the default cost array or a new cost array.
std::vector< EdgeSet > GetEdges() const
Get every in the given graph as IDs.
void TripletsAddOrUpdateEdge(int parent_id, int child_id, float cost)
Add a new edge to the triplets list.
bool IsDefaultName(const std::string &name) const
Check if this name belongs to the default graph.
bool needs_compression
If true, the CSR is inaccurate and requires compression.
Definition: graph.h:497
std::vector< Eigen::Triplet< float > > triplets
Edges to be converted to a CSR when Graph::Compress() is called.
Definition: graph.h:496
int size() const
Determine how many nodes are in the graph.
float GetCost(int parent_id, int child_id, const std::string &cost_type="") const
get the cost from parent_id to child_id in the given cost_type.
void AddNodeAttributes(const std::vector< int > &id, const std::string &name, const std::vector< std::string > &scores)
Add an attribute to the node at id. If the node at id already has a score for the attribute at name,...
void addEdge(const Node &parent, const Node &child, float score=1.0f, const std::string &cost_type="")
Add a new edge to the graph from parent to child.
TempMatrix MapCostMatrix(const std::string &cost_type) const
Construct a temp matrix for the specific cost type.
void Compress()
Compress the graph to a CSR and enable the usage of several functions.
bool HasCostArray(const std::string &key) const
Check if we have this edge matrix already defined.
bool HasNodeAttribute(const std::string &key) const
Check if this graph has a specific node attribute.
bool nodes_out_of_order
Determines whether or not the graph is using integer nodes.
Definition: graph.h:522
std::vector< float > AggregateGraph(COST_AGGREGATE agg_type, bool directed=true, const std::string &cost_type="") const
Summarize the costs of every outgoing edge for every node in the graph.
robin_hood::unordered_map< Node, int > idmap
Maps a list of X,Y,Z positions to positions in ordered_nodes.
Definition: graph.h:494
std::unordered_map< std::string, EdgeCostSet > edge_cost_maps
< The default cost type of the graph.
Definition: graph.h:505
int MaxID() const
Calculate the maximum ID of any node in the graph.
void AddEdges(const EdgeSet &edges, const std::string &cost_name="")
Add multiple edges to the graph.
Graph(const std::vector< std::vector< int > > &edges, const std::vector< std::vector< float > > &distances, const std::vector< Node > &Nodes, const std::string &default_cost="Distance")
Construct a graph from a list of nodes, edges, and distances.
Node NodeFromID(int id) const
Retrieve the node that corresponds to id.
const std::vector< Edge > operator[](const Node &n) const
CSRPtrs GetCSRPointers(const std::string &cost_type="")
Obtain the size of and pointers to the 3 arrays that comprise this graph's CSR. graph if it isn't com...
void AddNodeAttribute(int id, const std::string &attribute, const std::string &score)
Add an attribute to the node at id
EdgeCostSet & GetOrCreateCostType(const std::string &name)
Get a reference to the edge matrix, or create a new one if it doesn't exist.
std::vector< std::string > GetCostTypes() const
Get an array of all cost names within this graph.
Subgraph GetSubgraph(const Node &parent_node, const std::string &cost_type="") const
Retrieves a Subgraph using a Node.
void ClearCostArrays(const std::string &cost_name="")
Clear one or more cost arrays from the graph.
bool checkForEdge(int parent, int child) const
Determine if an edge between parent and child exists in the graph.
void Clear()
Clear all nodes and edges from the graph.
bool has_cost_arrays
Indicates that the graph has cost arrays.
Definition: graph.h:513
std::vector< Edge > GetEdgesForNode(int parent_id, bool undirected=false, const std::string &cost_type="") const
Get the edges for the given node.
std::vector< Node > Nodes() const
Get a list of nodes from the graph sorted by ID.
int FindValueArrayIndex(int parent_id, int child_id) const
Get the index of the cost at parent/child.
int next_id
The id for the next unique node.
Definition: graph.h:490
void CSRAddOrUpdateEdge(int parent_id, int child_id, float cost)
Add a new edge cost to the CSR or update if if a cost already exists.
std::string default_cost
Definition: graph.h:504
EdgeCostSet & CreateCostArray(const std::string &name)
Create a new edge matrix.
EdgeCostSet & GetCostArray(const std::string &key)
Get a reference to the edge matrix at the given key.
std::vector< Edge > GetUndirectedEdges(const Node &N, const std::string &cost_type="") const
Get a list of all edges to and from node N.
void AttrToCost(const std::string &node_attribute, const std::string &cost_to_store_as, Direction consider=Direction::INCOMING)
Generate edge costs from a set of node attributes.
robin_hood::unordered_map< int, std::string > NodeAttributeValueMap
Definition: graph.h:487
std::vector< IntEdge > GetIntEdges(int parent) const
Get children of a specific node as integers.
std::vector< Node > ordered_nodes
A list of nodes contained by the graph.
Definition: graph.h:491
void InsertEdgeIntoCostSet(int parent_id, int child_id, float score, EdgeCostSet &cost_set)
Add an edge to a cost set between parent_id and child_id.
std::vector< Node > GetChildren(const Node &n) const
Retrieve n's child nodes - n is a parent node
std::vector< std::string > GetNodeAttributes(std::string attribute) const
Get the score for the given attribute of every node in the graph. Nodes that do not have a score for ...
std::vector< std::array< float, 3 > > NodesAsFloat3() const
Get a list of nodes as float arrays.
robin_hood::unordered_map< std::string, NodeAttributeValueMap > node_attr_map
Node attribute type : Map of node id to node attribute.
Definition: graph.h:499
bool HasEdge(const std::array< float, 3 > &parent, const std::array< float, 3 > &child, bool undirected=false) const
Determine if the graph has an edge from parent to child.
int getID(const Node &node) const
Retrieve the ID for node in this graph.
bool hasKey(int id) const
Check if this ID has already been assigned.
int getOrAssignID(const Node &input_node)
Get the unique ID for this x, y, z position and assign it an new one if it doesn't already exist.
a class to store JSON values
Definition: json.hpp:16658
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
Definition: json.hpp:18759
void push_back(basic_json &&val)
add an object to an array
Definition: json.hpp:21713