├── Makefile ├── LICENSE ├── hl ├── ordering.hpp ├── labeling_check.hpp ├── akiba.hpp ├── kheap.hpp ├── dijkstra.hpp ├── labeling.hpp ├── hhl.hpp ├── graph.hpp ├── uhhl.hpp └── ghl.hpp ├── degree.cpp ├── akiba.cpp ├── lcheck.cpp ├── ghl.cpp ├── hhl.cpp └── README.md /Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS = -Wall -Werror -fopenmp -O3 -I./hl 2 | LIB=hl/*.hpp 3 | PROGRAMS= hhl akiba degree lcheck ghl 4 | 5 | all: $(PROGRAMS) 6 | 7 | $(PROGRAMS):%: %.cpp $(LIB) Makefile 8 | $(CXX) $(CXXFLAGS) -o $@ $< 9 | 10 | clean: 11 | rm $(PROGRAMS) 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014, 2015 savrus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /hl/ordering.hpp: -------------------------------------------------------------------------------- 1 | // Vertex order is a list of vertices where vertices are sorted from the most important to the least important. 2 | // This file contains methods to write a vertex order to a file and read it from a file. 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "graph.hpp" 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace hl { 33 | 34 | // Class to read/write order 35 | struct Order { 36 | // Write vertex order to file 37 | static bool write(char *filename, std::vector &order) { 38 | std::ofstream file; 39 | file.open(filename); 40 | file << order.size() << std::endl; 41 | for (size_t i = 0; i < order.size(); ++i) file << order[i] << std::endl; 42 | file.close(); 43 | return file.good(); 44 | } 45 | 46 | // Read vertex order from file 47 | static bool read(char *filename, std::vector &order) { 48 | std::ifstream file; 49 | file.open(filename); 50 | size_t s = 0; 51 | file >> s; 52 | order.resize(s); 53 | for (size_t i = 0; i < s; ++i) file >> order[i]; 54 | file >> std::ws; 55 | file.close(); 56 | return file.eof() && !file.fail(); 57 | } 58 | }; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /hl/labeling_check.hpp: -------------------------------------------------------------------------------- 1 | // Since Hub Labels is a complex data structure one would like to verify that it is bult correctly. 2 | // This file contains a checker for labels. 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "graph.hpp" 27 | #include "labeling.hpp" 28 | #include "dijkstra.hpp" 29 | #include 30 | 31 | namespace hl { 32 | 33 | // Labeling checker implementation 34 | class LabelingCheck { 35 | Graph &g; 36 | std::vector dijkstra; 37 | int num_threads; 38 | 39 | public: 40 | LabelingCheck(Graph &g, int num_threads) : g(g), num_threads(num_threads) { 41 | for (int i = 0; i < num_threads; ++i) dijkstra.push_back(Dijkstra(g)); 42 | } 43 | 44 | // Check if distance reported by labels is the real one 45 | bool run(Labeling &labeling) { 46 | bool res = true; 47 | #pragma omp parallel for schedule(dynamic) 48 | for (Vertex v = 0; v < g.get_n(); ++v) { 49 | Dijkstra &dij = dijkstra[omp_get_thread_num()]; 50 | for (int side = 0; side < 2; ++side) { 51 | dij.run(v,side); 52 | for (Vertex u = 0; u < g.get_n(); ++u) { 53 | if (dij.get_distance(u) != labeling.query(v,u,side)) res = false; 54 | } 55 | } 56 | } 57 | return res; 58 | } 59 | }; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /hl/akiba.hpp: -------------------------------------------------------------------------------- 1 | // Akiba et al. presented a 'pruned labeling' algorithm to build Hierarchical Hub Labels from a vertex order. 2 | // This file contains Akiba et. al. algorithm implementation 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "graph.hpp" 27 | #include "dijkstra.hpp" 28 | #include "labeling.hpp" 29 | #include 30 | #include 31 | 32 | namespace hl { 33 | 34 | // Akiba et. al. 'pruned labeling' algorithm implementation 35 | class Akiba : BasicDijkstra { 36 | 37 | // Add i'th vertex from the order into the labels of reachable vertices 38 | void iteration(size_t i, bool forward, std::vector &order, Labeling &labeling) { 39 | clear(); 40 | Vertex v = order[i]; 41 | distance[v] = 0; 42 | update(v, 0); 43 | while (!queue.empty()) { 44 | Vertex u = queue.pop(); 45 | Distance d = distance[u]; 46 | // We use i for the hub id (instead of v) so the labels are already sorted by ids 47 | labeling.add(u, !forward, i, d); 48 | for (Graph::arc_iterator a = g->begin(u, forward), end = g->end(u, forward); a < end; ++a) { 49 | Distance dd = d + a->length; 50 | assert(dd > d && dd < infty); 51 | if (dd < distance[a->head] && dd < labeling.query(v, a->head, forward)) update(a->head, dd); 52 | } 53 | } 54 | } 55 | 56 | public: 57 | Akiba(Graph &g) : BasicDijkstra(g) {} 58 | 59 | // Buld HHL from a vertex order 60 | void run(std::vector &order, Labeling &labeling) { 61 | assert(order.size() == g->get_n()); 62 | labeling.clear(); 63 | for (size_t i = 0; i < order.size(); ++i) { 64 | iteration(i, false, order, labeling); 65 | iteration(i, true, order, labeling); 66 | } 67 | } 68 | }; 69 | 70 | } 71 | -------------------------------------------------------------------------------- /degree.cpp: -------------------------------------------------------------------------------- 1 | // This file contains a program to order vertices by their degree. 2 | // 3 | // Copyright (c) 2014, 2015 savrus 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "graph.hpp" 24 | #include "ordering.hpp" 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace hl; 33 | 34 | void usage(char *argv[]) { 35 | std::cout << "Usage: " << argv[0] << " -o ordering graph" << std::endl 36 | << " -o ordering\tFile with the vertex order" << std::endl; 37 | std::exit(1); 38 | } 39 | 40 | int main(int argc, char *argv[]) { 41 | char *graph_file = NULL; 42 | char *order_file = NULL; 43 | int argi; 44 | for (argi = 1; argi < argc; ++argi) { 45 | if (argv[argi][0] == '-') { 46 | if (!strcmp("--", argv[argi])) { ++argi; break; } 47 | else if (!strcmp("-h", argv[argi])) usage(argv); 48 | else if (!strcmp("-o", argv[argi])) { if (++argi >= argc) usage(argv); order_file = argv[argi]; } 49 | else usage(argv); 50 | } else if (graph_file == NULL) graph_file = argv[argi]; 51 | else break; 52 | } 53 | if (argi != argc || !graph_file || !order_file) usage(argv); 54 | 55 | Graph g; 56 | if (!g.read(graph_file)) { 57 | std::cerr << "Unable to read graph from file " << graph_file << std::endl; 58 | std::exit(1); 59 | } 60 | std::cout << "Graph has " << g.get_n() << " vertices and " << g.get_m() << " arcs" << std::endl; 61 | 62 | std::vector order(g.get_n()); 63 | std::vector< std::pair > d(g.get_n()); 64 | 65 | for (Vertex v = 0; v < g.get_n(); ++v) d[v] = std::make_pair(g.get_degree(v),v); 66 | std::sort(d.begin(),d.end()); 67 | for (size_t i = 0; i < d.size(); ++i) order[i] = d[d.size() - i - 1].second; 68 | 69 | if (order_file && (!Order::write(order_file, order))) std::cerr << "Unable to write order to file " << order_file << std::endl; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /akiba.cpp: -------------------------------------------------------------------------------- 1 | // Hierarchical Hub Labeling is a data structure used to build a distance oracle in graph. 2 | // This file contains a program to consrtuct HHL given a specific vertex order. 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #include "graph.hpp" 25 | #include "akiba.hpp" 26 | #include "labeling.hpp" 27 | #include "ordering.hpp" 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | using namespace hl; 34 | 35 | void usage(char *argv[]) { 36 | std::cout << "Usage: " << argv[0] << " [-l labeling] -o ordering graph" << std::endl 37 | << " -o ordering\tFile with the vertex order" << std::endl 38 | << " -l labeling\tFile to write the labeling" << std::endl; 39 | std::exit(1); 40 | } 41 | 42 | int main(int argc, char *argv[]) { 43 | char *graph_file = NULL; 44 | char *order_file = NULL; 45 | char *label_file = NULL; 46 | int argi; 47 | for (argi = 1; argi < argc; ++argi) { 48 | if (argv[argi][0] == '-') { 49 | if (!strcmp("--", argv[argi])) { ++argi; break; } 50 | else if (!strcmp("-h", argv[argi])) usage(argv); 51 | else if (!strcmp("-l", argv[argi])) { if (++argi >= argc) usage(argv); label_file = argv[argi]; } 52 | else if (!strcmp("-o", argv[argi])) { if (++argi >= argc) usage(argv); order_file = argv[argi]; } 53 | else usage(argv); 54 | } else if (graph_file == NULL) graph_file = argv[argi]; 55 | else break; 56 | } 57 | if (argi != argc || !graph_file || !order_file) usage(argv); 58 | 59 | Graph g; 60 | if (!g.read(graph_file)) { 61 | std::cerr << "Unable to read graph from file " << graph_file << std::endl; 62 | std::exit(1); 63 | } 64 | std::cout << "Graph has " << g.get_n() << " vertices and " << g.get_m() << " arcs" << std::endl; 65 | 66 | Labeling labels(g.get_n()); 67 | std::vector order; 68 | 69 | if (!Order::read(order_file, order)) { 70 | std::cerr << "Unable to read vertex order from file " << order_file << std::endl; 71 | std::exit(1); 72 | } 73 | if (order.size() != g.get_n()) { 74 | std::cerr << "Order is incompatible with graph." << std::endl; 75 | std::exit(1); 76 | } 77 | 78 | Akiba(g).run(order, labels); 79 | 80 | std::cout << "Average label size " << labels.get_avg() << std::endl; 81 | std::cout << "Maximum label size " << labels.get_max() << std::endl; 82 | 83 | if (label_file && (!labels.write(label_file))) std::cerr << "Unable to write labels to file " << label_file << std::endl; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /lcheck.cpp: -------------------------------------------------------------------------------- 1 | // This file contains a program to verify the labels. 2 | // 3 | // Copyright (c) 2014, 2015 savrus 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "graph.hpp" 24 | #include "labeling.hpp" 25 | #include "labeling_check.hpp" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace hl; 33 | 34 | void usage(char *argv[]) { 35 | std::cout << "Usage: " << argv[0] << " [-c] [-l labeling] [-t threads] graph" << std::endl 36 | << " -c \tCheck labals (without this option print statistics only)" << std::endl 37 | << " -l labeling\tFile to write the labeling" << std::endl 38 | << " -t threads \tNumber of threads" << std::endl; 39 | std::exit(1); 40 | } 41 | 42 | int main(int argc, char *argv[]) { 43 | char *graph_file = NULL; 44 | char *label_file = NULL; 45 | int num_threads = omp_get_max_threads(); 46 | bool check = false; 47 | int argi; 48 | for (argi = 1; argi < argc; ++argi) { 49 | if (argv[argi][0] == '-') { 50 | if (!strcmp("--", argv[argi])) { ++argi; break; } 51 | else if (!strcmp("-h", argv[argi])) usage(argv); 52 | else if (!strcmp("-c", argv[argi])) check = true; 53 | else if (!strcmp("-l", argv[argi])) { if (++argi >= argc) usage(argv); label_file = argv[argi]; } 54 | else if (!strcmp("-t", argv[argi])) { if (++argi >= argc) usage(argv); num_threads = strtoul(argv[argi], NULL, 10); } 55 | else usage(argv); 56 | } else if (graph_file == NULL) graph_file = argv[argi]; 57 | else break; 58 | } 59 | if (argi != argc || !graph_file || !label_file) usage(argv); 60 | assert(num_threads > 0); 61 | omp_set_num_threads(num_threads); 62 | 63 | Graph g; 64 | if (!g.read(graph_file)) { 65 | std::cerr << "Unable to read graph from file " << graph_file << std::endl; 66 | std::exit(1); 67 | } 68 | std::cout << "Graph has " << g.get_n() << " vertices and " << g.get_m() << " arcs" << std::endl; 69 | 70 | Labeling labels(g.get_n()); 71 | std::vector order; 72 | 73 | if (!labels.read(label_file, g.get_n())) { 74 | std::cerr << "Unable to read labels from file " << label_file << std::endl; 75 | std::exit(1); 76 | } 77 | 78 | if (check) { 79 | if (!LabelingCheck(g, num_threads).run(labels)) { 80 | std::cout << "Bad Labels" << std::endl; 81 | std::exit(1); 82 | } else std::cout << "Labels OK" << std::endl; 83 | } 84 | 85 | std::cout << "Average label size " << labels.get_avg() << std::endl; 86 | std::cout << "Maximum label size " << labels.get_max() << std::endl; 87 | } 88 | 89 | -------------------------------------------------------------------------------- /hl/kheap.hpp: -------------------------------------------------------------------------------- 1 | // k-way heap is a data structure used for priority queues. The 2-way heap is also known as binary heap. 2 | // This file contains k-way Heap implementation. 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | namespace hl { 31 | 32 | // k-way heap implementation 33 | template class KHeap { 34 | size_t size; // current number of elements in the heap 35 | size_t invalid; // invalid position 36 | std::vector heap; // array to store the heap 37 | std::vector heap_pos; // heap_pos[v] is index of v in the heap 38 | std::vector key; // key[v] is a vertex key 39 | 40 | // Swap elements with positions i and j 41 | void swap(size_t i, size_t j) { 42 | std::swap(heap_pos[heap[i]], heap_pos[heap[j]]); 43 | std::swap(heap[i], heap[j]); 44 | } 45 | // Get position of heap[i]'s smallest child 46 | size_t kid(size_t i) const { 47 | size_t c0 = i*k+1, m = 0; 48 | for (size_t j = 1; j < k && c0 + j < size; ++j) 49 | if (key[heap[c0 + j]] < key[heap[c0 + m]]) m = j; 50 | return c0 + m; 51 | } 52 | // Move heap[i] up or down 53 | void fixup(size_t i) { 54 | size_t c; 55 | while(i*k + 1 < size && key[heap[i]] > key[heap[(c=kid(i))]]) { swap(i, c); i = c; } 56 | while(i > 0 && key[heap[i]] < key[heap[(i-1)/k]]) { swap(i, (i-1)/k); i = (i-1)/k; } 57 | } 58 | 59 | public: 60 | KHeap(size_t n) : size(0), invalid(std::numeric_limits::max()), heap(n), heap_pos(n, invalid), key(n) {} 61 | 62 | bool empty() const { return size == 0; } // Is heap empty? 63 | V top() const { return heap[0]; } // Get minimum elenment (heap should be non-empty) 64 | V pop() { V v = top(); extract(v); return v; } // Get minimum element and extract it from the queue 65 | 66 | // Update v's key to new_key 67 | void update(V v, K kk) { 68 | if (heap_pos[v] == invalid) { heap_pos[v] = size++; heap[heap_pos[v]] = v; } 69 | key[v] = kk; 70 | fixup(heap_pos[v]); 71 | } 72 | 73 | // Extract v from the queue (if v is present) 74 | void extract(V v) { 75 | if (heap_pos[v] == invalid) return; 76 | if (heap_pos[v] < --size) { 77 | size_t pos = heap_pos[v]; 78 | swap(pos, size); 79 | fixup(pos); 80 | } 81 | heap_pos[v] = invalid; 82 | } 83 | 84 | // Clear heap 85 | void clear() { 86 | for (size_t i = 0; i < size; ++i) heap_pos[heap[i]] = invalid; 87 | size = 0; 88 | } 89 | }; 90 | 91 | } 92 | -------------------------------------------------------------------------------- /hl/dijkstra.hpp: -------------------------------------------------------------------------------- 1 | // Dijkstra algorithm is well-known algorithm for single-source shortes path problem. 2 | // This file contains Dijkstra algorithm implementation. 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "graph.hpp" 27 | #include "kheap.hpp" 28 | #include 29 | #include 30 | 31 | namespace hl { 32 | 33 | // Data structures to implement Dijkstra-like algorithms 34 | class BasicDijkstra { 35 | protected: 36 | Graph *g; // graph 37 | KHeap queue; // Dijkstra queue 38 | std::vector parent; // parent[v] is v's parent in the shortest path tree 39 | std::vector distance; // distance[v] is distance from source to v 40 | std::vector is_dirty; // is_dirty[v] is true if we touched v in current run 41 | std::vector dirty; // list of visited vertices 42 | 43 | // Update vertex v: set distance to d and parent to p 44 | void update(Vertex v, Distance d, Vertex p = none) { 45 | distance[v] = d; 46 | parent[v] = p; 47 | queue.update(v, d); 48 | if (!is_dirty[v]) { dirty.push_back(v); is_dirty[v] = true; } 49 | } 50 | 51 | // Clear internal structures 52 | void clear() { 53 | queue.clear(); 54 | for(size_t i = 0; i < dirty.size(); ++i) { 55 | parent[dirty[i]] = none; 56 | distance[dirty[i]] = infty; 57 | is_dirty[dirty[i]] = false; 58 | } 59 | dirty.clear(); 60 | dirty.reserve(g->get_n()); 61 | } 62 | 63 | BasicDijkstra(Graph &g) : 64 | g(&g), 65 | queue(g.get_n()), 66 | parent(g.get_n(), none), 67 | distance(g.get_n(), infty), 68 | is_dirty(g.get_n()) {} 69 | }; 70 | 71 | // Dijkstra algorithm implementation 72 | class Dijkstra : BasicDijkstra{ 73 | public: 74 | Dijkstra(Graph &g) : BasicDijkstra(g) {} 75 | 76 | Distance get_distance(Vertex v) { return distance[v]; } // Distance from source to v 77 | Vertex get_parent(Vertex v) { return parent[v]; } // v's parent in the shortest path tree 78 | 79 | // Find distances from v to all other vertices and build shortest path tree 80 | void run(Vertex v, bool forward = true) { 81 | clear(); 82 | update(v, 0); 83 | while (!queue.empty()) { 84 | Vertex u = queue.pop(); 85 | Distance d = distance[u]; 86 | for (Graph::arc_iterator a = g->begin(u, forward), end = g->end(u, forward); a < end; ++a) { 87 | Distance dd = d + a->length; 88 | assert(dd > d && dd < infty); 89 | if (dd < distance[a->head]) update(a->head, dd, u); 90 | } 91 | } 92 | } 93 | }; 94 | 95 | } 96 | -------------------------------------------------------------------------------- /ghl.cpp: -------------------------------------------------------------------------------- 1 | // Hub Labeling is a data structure used to build a distance oracle in graph. 2 | // This file contains a program to consrtuct approximately optimal HL using GHLp algorithm 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #include "graph.hpp" 25 | #include "ghl.hpp" 26 | #include "labeling.hpp" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | using namespace hl; 34 | 35 | void usage(char *argv[]) { 36 | std::cout << "Usage: " << argv[0] << " [-p norm] [-a alpha] [-l labeling] [-t threads] graph" << std::endl 37 | << " -p norm \tApproximate p-norm of labels. Use '-p max' to approximate maximum label size" << std::endl 38 | << " -a alpha \tAlpha parameter (>=1.0) to GHLp algorithm which sets tradeoff between speed and labeling size" << std::endl 39 | << " -l labeling\tFile to write the labeling" << std::endl 40 | << " -t threads \tNumber of threads" << std::endl; 41 | std::exit(1); 42 | } 43 | 44 | int main(int argc, char *argv[]) { 45 | char *graph_file = NULL; 46 | char *label_file = NULL; 47 | int num_threads = omp_get_max_threads(); 48 | double alpha = 1.1; 49 | double p = 1.0; 50 | bool linf = false; 51 | int argi; 52 | for (argi = 1; argi < argc; ++argi) { 53 | if (argv[argi][0] == '-') { 54 | if (!strcmp("--", argv[argi])) { ++argi; break; } 55 | else if (!strcmp("-h", argv[argi])) usage(argv); 56 | else if (!strcmp("-p", argv[argi])) { if (++argi >= argc) usage(argv); if (!strcmp("max", argv[argi])) linf = true; else p = strtod(argv[argi], NULL); } 57 | else if (!strcmp("-a", argv[argi])) { if (++argi >= argc) usage(argv); alpha = strtod(argv[argi], NULL); } 58 | else if (!strcmp("-l", argv[argi])) { if (++argi >= argc) usage(argv); label_file = argv[argi]; } 59 | else if (!strcmp("-t", argv[argi])) { if (++argi >= argc) usage(argv); num_threads = strtoul(argv[argi], NULL, 10); } 60 | else usage(argv); 61 | } else if (graph_file == NULL) graph_file = argv[argi]; 62 | else break; 63 | } 64 | if (argi != argc || !graph_file || alpha < 1.0) usage(argv); 65 | assert(num_threads > 0); 66 | omp_set_num_threads(num_threads); 67 | 68 | Graph g; 69 | if (!g.read(graph_file)) { 70 | std::cerr << "Unable to read graph from file " << graph_file << std::endl; 71 | std::exit(1); 72 | } 73 | std::cout << "Graph has " << g.get_n() << " vertices and " << g.get_m() << " arcs" << std::endl; 74 | 75 | Labeling labels(g.get_n()); 76 | if (linf) p = log(static_cast(g.get_n())); 77 | 78 | GHL(g, num_threads).run(labels, alpha, p); 79 | 80 | std::cout << "Average label size " << labels.get_avg() << std::endl; 81 | std::cout << "Maximum label size " << labels.get_max() << std::endl; 82 | 83 | if (label_file && (!labels.write(label_file))) std::cerr << "Unable to write labels to file " << label_file << std::endl; 84 | } 85 | -------------------------------------------------------------------------------- /hhl.cpp: -------------------------------------------------------------------------------- 1 | // Hierarchical Hub Labeling is a data structure used to build a distance oracle in graph. 2 | // This file contains a program to consrtuct HHL using greedy HHL algorithm 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #include "graph.hpp" 25 | #include "hhl.hpp" 26 | #include "uhhl.hpp" 27 | #include "labeling.hpp" 28 | #include "ordering.hpp" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | using namespace hl; 36 | 37 | void usage(char *argv[]) { 38 | std::cout << "Usage: " << argv[0] << " [-w] [-l labeling] [-o ordering] [-t threads] graph" << std::endl 39 | << " -w \tUse label-greedy algorithm instead of path-greedy" << std::endl 40 | << " -u \tAssume that shortest paths are unique" << std::endl 41 | << " -o ordering\tFile to write the vertex order" << std::endl 42 | << " -l labeling\tFile to write the labeling" << std::endl 43 | << " -t threads \tNumber of threads" << std::endl 44 | << "WARNING: performance may reduce dramatically when HyperThreading is active. Please bound the number of threads by real cores." << std::endl; 45 | std::exit(1); 46 | } 47 | 48 | int main(int argc, char *argv[]) { 49 | char *graph_file = NULL; 50 | char *order_file = NULL; 51 | char *label_file = NULL; 52 | int num_threads = omp_get_max_threads(); 53 | int type = 0; 54 | bool is_usp = false; 55 | int argi; 56 | for (argi = 1; argi < argc; ++argi) { 57 | if (argv[argi][0] == '-') { 58 | if (!strcmp("--", argv[argi])) { ++argi; break; } 59 | else if (!strcmp("-h", argv[argi])) usage(argv); 60 | else if (!strcmp("-w", argv[argi])) type = 1; 61 | else if (!strcmp("-u", argv[argi])) is_usp = true; 62 | else if (!strcmp("-l", argv[argi])) { if (++argi >= argc) usage(argv); label_file = argv[argi]; } 63 | else if (!strcmp("-o", argv[argi])) { if (++argi >= argc) usage(argv); order_file = argv[argi]; } 64 | else if (!strcmp("-t", argv[argi])) { if (++argi >= argc) usage(argv); num_threads = strtoul(argv[argi], NULL, 10); } 65 | else usage(argv); 66 | } else if (graph_file == NULL) graph_file = argv[argi]; 67 | else break; 68 | } 69 | if (argi != argc || !graph_file) usage(argv); 70 | assert(num_threads > 0); 71 | omp_set_num_threads(num_threads); 72 | 73 | Graph g; 74 | if (!g.read(graph_file)) { 75 | std::cerr << "Unable to read graph from file " << graph_file << std::endl; 76 | std::exit(1); 77 | } 78 | std::cout << "Graph has " << g.get_n() << " vertices and " << g.get_m() << " arcs" << std::endl; 79 | 80 | Labeling labels(g.get_n()); 81 | std::vector order; 82 | 83 | if (is_usp) UHHL(g, num_threads).run(type, order, labels); 84 | else HHL(g, num_threads).run(type, order, labels); 85 | 86 | std::cout << "Average label size " << labels.get_avg() << std::endl; 87 | std::cout << "Maximum label size " << labels.get_max() << std::endl; 88 | 89 | if (label_file && (!labels.write(label_file))) std::cerr << "Unable to write labels to file " << label_file << std::endl; 90 | if (order_file && (!Order::write(order_file, order))) std::cerr << "Unable to write order to file " << order_file << std::endl; 91 | } 92 | -------------------------------------------------------------------------------- /hl/labeling.hpp: -------------------------------------------------------------------------------- 1 | // Hub Labels are lists of hubs and distances to them attached to every vertex in a graph. 2 | // This file contains the class to store labels. 3 | // Available methods include making a query, write labels to file, read labels from file. 4 | // 5 | // Copyright (c) 2014, 2015 savrus 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | #pragma once 26 | 27 | #include "graph.hpp" 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace hl { 37 | 38 | // Class to store labels 39 | class Labeling { 40 | std::vector< std::vector< std::vector > > label_v; // Lists of forward/reverse hubs 41 | std::vector< std::vector< std::vector > > label_d; // Lists of distances to hubs 42 | Vertex n; 43 | 44 | public: 45 | Labeling(size_t n = 0) : 46 | label_v(n, std::vector< std::vector >(2)), 47 | label_d(n, std::vector< std::vector >(2)), 48 | n(n) {} 49 | 50 | // Find u-v distance 51 | Distance query(Vertex u, Vertex v, bool f = true) { 52 | Distance r = infty; 53 | for (size_t i=0, j=0; i < label_v[u][f].size() && j < label_v[v][!f].size();) { 54 | if (label_v[u][f][i] == label_v[v][!f][j]) { 55 | assert(label_d[u][f][i] < infty - label_d[v][!f][j]); 56 | r = std::min(r, label_d[u][f][i++] + label_d[v][!f][j++]); 57 | } else if (label_v[u][f][i] < label_v[v][!f][j]) ++i; 58 | else ++j; 59 | } 60 | return r; 61 | } 62 | 63 | // Add hub (v,d) to forward or reverse label of u 64 | void add(Vertex u, bool forward, Vertex v, Distance d) { 65 | label_v[u][forward].push_back(v); 66 | label_d[u][forward].push_back(d); 67 | } 68 | 69 | // Get labels 70 | std::vector< std::vector > &get_label_hubs(Vertex u) { return label_v[u]; } 71 | std::vector< std::vector > &get_label_distances(Vertex u) { return label_d[u]; } 72 | 73 | // Get maximum label size 74 | size_t get_max() const { 75 | size_t max = 0; 76 | for (Vertex v = 0; v < n; ++v) 77 | for (int side = 0; side < 2; ++side) 78 | max = std::max(max, label_v[v][side].size()); 79 | return max; 80 | } 81 | 82 | // Get average label size 83 | double get_avg() const { 84 | long long total = 0; 85 | for (Vertex v = 0; v < n; ++v) 86 | total += label_v[v][0].size() + label_v[v][1].size(); 87 | return static_cast(total)/n/2; 88 | } 89 | 90 | // Write labels to file 91 | bool write(char *filename) { 92 | std::ofstream file; 93 | file.open(filename); 94 | file << n << std::endl; 95 | for (Vertex v = 0; v < n; ++v) { 96 | for (int side = 0; side < 2; ++side) { 97 | file << label_v[v][side].size(); 98 | for (size_t i = 0; i < label_v[v][side].size(); ++i) { 99 | file << " " << label_v[v][side][i]; 100 | file << " " << label_d[v][side][i]; 101 | } 102 | file << std::endl; 103 | } 104 | } 105 | file.close(); 106 | return file.good(); 107 | } 108 | 109 | // Read labels from file 110 | bool read(char *filename, Vertex check_n = 0) { 111 | std::ifstream file; 112 | file.open(filename); 113 | file >> n; 114 | if (check_n && n != check_n) return false; 115 | label_v.resize(n, std::vector< std::vector >(2)); 116 | label_d.resize(n, std::vector< std::vector >(2)); 117 | for (Vertex v = 0; v < n; ++v) { 118 | for (int side = 0; side < 2; ++side) { 119 | size_t s; 120 | file >> s; 121 | label_v[v][side].resize(s); 122 | label_d[v][side].resize(s); 123 | for (size_t i = 0; i < s; ++i) { 124 | file >> label_v[v][side][i]; 125 | file >> label_d[v][side][i]; 126 | } 127 | } 128 | } 129 | file >> std::ws; 130 | file.close(); 131 | return file.eof() && !file.fail(); 132 | } 133 | 134 | // Clear labels 135 | void clear() { 136 | for (Vertex v = 0; v < n; ++v) { 137 | for (int side = 0; side < 2; ++side) { 138 | label_v[v][side].clear(); 139 | label_d[v][side].clear(); 140 | } 141 | } 142 | } 143 | 144 | // Sort labels before making queries 145 | void sort() { 146 | for (Vertex v = 0; v < n; ++v) { 147 | for (int side = 0; side < 2; ++side) { 148 | std::vector< std::pair > label(label_v[v][side].size()); 149 | for (size_t i = 0; i < label_v[v][side].size(); ++i) 150 | label[i] = std::make_pair(label_v[v][side][i], label_d[v][side][i]); 151 | std::sort(label.begin(),label.end()); 152 | for (size_t i = 0; i < label_v[v][side].size(); ++i) { 153 | label_v[v][side][i] = label[i].first; 154 | label_d[v][side][i] = label[i].second; 155 | } 156 | } 157 | } 158 | } 159 | 160 | #if 0 161 | // Print labels 162 | void print() const { 163 | for (Vertex v = 0; v < n; ++v) { 164 | for (int side = 0; side < 2; ++side) { 165 | std::cout << "L(" << v << "," << side << ") ="; 166 | for (size_t i = 0; i < label_v[v][side].size(); ++i) std::cout << " (" << label_v[v][side][i] << "," << label_d[v][side][i] << ")"; 167 | std::cout << std::endl; 168 | } 169 | } 170 | } 171 | #endif 172 | 173 | }; 174 | 175 | } 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hl - Hub Labeling Algorithms 2 | == 3 | 4 | *Hub Labeling* is a data structure used to find a distance between two vertices in a graph. 5 | It provides excellent query time for real-world graphs such as road networks and social networks. 6 | Hub Labeling has two stages: preprocessing and querying. 7 | While the query algorithm is simple, the preprocessing is much more involved and there are several preprocessing algorithms in the literature. 8 | 9 | There is an algorithm to find O(log n)-approximately optimal Hub Labels. However, it is slow. 10 | Practical labeling algorithms find a *Hierarchical Hub Labeling*. 11 | HHL algorithms order vertices and find hub labels which respect the order. 12 | In fact given an order one can build the minimum HHL with respect to this order. 13 | 14 | ## Usage 15 | 16 | There are several programs in the repository: 17 | 18 | * `hhl` — find Hierarchical Hub Labeling (and a vertex order) using greedy algorithm 19 | * `akiba` — construct Hierarchical Hub Labeling from a vertex order 20 | * `degree` — order vertices by their degree 21 | * `lcheck` — check labels 22 | * `ghl` — find O(log n) approximate Hub Labeling 23 | 24 | ### Build 25 | 26 | Just use `make` to build the project: 27 | ``` 28 | $ make 29 | g++ -fopenmp -O3 -I./lib -o akiba akiba.cpp 30 | g++ -fopenmp -O3 -I./lib -o degree degree.cpp 31 | g++ -fopenmp -O3 -I./lib -o hhl hhl.cpp 32 | g++ -fopenmp -O3 -I./lib -o lcheck lcheck.cpp 33 | g++ -fopenmp -O3 -I./lib -o ghl ghl.cpp 34 | ``` 35 | 36 | ### HHL 37 | 38 | Once you've built the `hhl` program you can compute HHL for a graph, say `email.graph`: 39 | ``` 40 | $ ./hhl email.graph 41 | Average label size 39.6134 42 | Maximum label size 78 43 | 44 | real 0m1.165s 45 | ``` 46 | The `hhl` program contains implementations of two greedy HHL algorithms: path-greedy and label-greedy (as defined in [1]). 47 | The default choice is path-greedy. To use label-greedy add `-w` argument: 48 | ``` 49 | $ ./hhl -w email.graph 50 | Average label size 36.797 51 | Maximum label size 82 52 | 53 | real 0m1.170s 54 | ``` 55 | 56 | If shortest paths are unique, greedy algorithms can be implemented more efficiently in terms of the running time. 57 | The `hhl` program has argument `-u` which turns on the unique shortest paths mode. 58 | You can use `-u` with any graph, `hhl` will just emulate the unique shortest path structure and run fast algorithm: 59 | ``` 60 | $ ./hhl -u email.graph 61 | Average label size 50.6531 62 | Maximum label size 112 63 | 64 | real 0m0.336s 65 | ``` 66 | 67 | Options `-o order_file` and `-l label_file` can be specified to write the ordering and labels to files: 68 | ``` 69 | $ ./hhl -o email.order -l email.labels email.graph 70 | Average label size 39.6134 71 | Maximum label size 78 72 | ``` 73 | 74 | If your CPU has Hyper Threading enabled, use `-t` argument to specify the number of threads to be equal to the real number of cores. 75 | Performance may reduce dramatically if there are more threads than cores. 76 | 77 | Keep in mind that `hhl` requires O(n^2) memory and the time complexity is O(n^3) (O(nm log n) if `-u` is set). 78 | 79 | 80 | ### Akiba 81 | 82 | Akiba et al. found an efficient algorithm to construct minimal HHL from a vertex order. 83 | You can use the `akiba` program to construct the labels if you have an order file prepared (for example, by the `hhl` program). 84 | ``` 85 | $ ./akiba -o email.order email.graph 86 | Average label size 39.6134 87 | Maximum label size 78 88 | 89 | real 0m0.081s 90 | ``` 91 | As you can see, `akiba` program reported exactly the same labeling size. 92 | What's interesting is that the order doesn't have to be from a rigorous algorithm. 93 | For example we can find the order in the unique shortest paths mode and use `akiba` program to find the best labels corresponding to this order: 94 | ``` 95 | $ ./hhl -u -o email.order email.graph 96 | Average label size 50.6531 97 | Maximum label size 112 98 | $ ./akiba -o email.order email.graph 99 | Average label size 39.707 100 | Maximum label size 82 101 | ``` 102 | Another simple way of ordering vertices is the degree order that works well for some social and computer networks: to order vertices by their degrees, breaking ties at random. 103 | Use `degree` program to find the degree order: 104 | ``` 105 | $ ./degree -o email.order email.graph 106 | $ ./akiba -o email.order email.graph 107 | Average label size 39.2462 108 | Maximum label size 89 109 | ``` 110 | Akiba et al algorithm is much faster than greedy HHL algorithms. Furthermore, it requires a linear amount of memory, so `degree`+`akiba` can be used for large graphs: 111 | ``` 112 | $ ./degree -o coAuthorsCiteseer.order coAuthorsCiteseer.graph 113 | $ ./akiba -o coAuthorsCiteseer.order coAuthorsCiteseer.graph 114 | Average label size 405.171 115 | Maximum label size 1170 116 | ``` 117 | This may take about 20 minutes. The coAuthorsCiteseer graph has about 200000 vertices which is too much for `hhl`. 118 | 119 | The `akiba` program also has argument `-l label_file` to write the labels. 120 | 121 | ### lcheck 122 | 123 | Once you have the labels from a complex algorithm you may want to verify that this labels are correct. 124 | You can use `lcheck` with an option `-c` for that. 125 | Without this option `lcheck` only reports the average and maximum label sizes. 126 | ``` 127 | $ ./lcheck -c -l email.labels email.graph 128 | Labels OK 129 | Average label size 39.6134 130 | Maximum label size 78 131 | 132 | real 0m0.233s 133 | ``` 134 | 135 | ### GHL 136 | 137 | The `ghl` program uses O(log n)-approximation algorithm to find optimal Hub Labels. 138 | ``` 139 | $ ./ghl email.graph 140 | Average label size 30.3954 141 | Maximum label size 61 142 | 143 | real 1m1.278s 144 | ``` 145 | 146 | When we talk about the optimum we need to specify what we are talking about. 147 | Labels can be viewed as vectors whith i'th coordinate equal to the size of label of vertex i. 148 | Natural way to calculate a scalar measure from a vector is to get it's norm. 149 | The 1-norm is a sum of vector's components, which is the total labeling size. 150 | The 2-norm is a usual Euclidian norm. 151 | The infinity norm is the maximum vector's component size, which is the maximum label size. 152 | By default the `ghl` program optimizes the total label size. 153 | If you want to optimize another norm use `-p norm` option where `norm` is a float value. 154 | For the infinity norm, type '-p max': 155 | ``` 156 | $ ./ghl -p max email.graph 157 | Average label size 32.8438 158 | Maximum label size 43 159 | 160 | real 4m36.269s 161 | ``` 162 | 163 | Another tricky option of the `ghl` program is `-a alpha`. Alpha is a tradeoff parameter between the running time and the labeling quality. 164 | By default alpha is 1.1. 165 | Use alpha equal to 1.0 to obtain best possible labels: 166 | ``` 167 | $ ./ghl -a 1.0 email.graph 168 | Average label size 30.0159 169 | Maximum label size 59 170 | 171 | real 2m18.289s 172 | ``` 173 | 174 | The `ghl` program also has argument `-l labeling_file` to write the labels. 175 | 176 | ## Graphs 177 | 178 | You can use any graph in DIMACS shortest path or METIS format. We suggest visiting [Dimacs 10 Challenge](http://www.cc.gatech.edu/dimacs10/archive/clustering.shtml) page for general graphs and [Dimacs 9 Challenge](http://www.dis.uniroma1.it/challenge9/download.shtml) for road networks. 179 | 180 | ## Basic Reference 181 | 182 | Material covered in Sections 2, 3.1, 3.2, and A1 of [1] should be enough to understand `akiba` and `hhl` programs. Those who seek deeper knowledge of the Hub Labeling topic are encouraged to read the other papers. 183 | 184 | 1. D. Delling, A.V. Goldberg, T. Pajor, and R. F. Werneck. *Robust Exact Distance Queries on Massive Networks.* Technical Report MSR-TR-2014-12, Microsoft Research, 2014. [link](http://research.microsoft.com/apps/pubs/default.aspx?id=208867) 185 | 186 | ## Advanced Reference 187 | 188 | The algorithm used in the `ghl` program is presented and studied in [5]. Theoretical background can be found in [3] and [4]. Simple version of the algorithm is presented in [4]. 189 | 190 | 1. I. Abraham, D. Delling, A.V. Goldberg, and R.F. Werneck. *Hierarchical Hub Labelings for Shortest Paths.* In Proc. 20th European Symposium on Algorithms (ESA 2012), 2012. [link](http://research.microsoft.com/apps/pubs/default.aspx?id=168725) 191 | 192 | 2. T. Akiba, Y. Iwata, and Y. Yoshida. *Fast Exact Shortest-path Distance Queries on Large Networks by Pruned Landmark Labeling.* In Proceedings of the 2013 ACM SIGMOD International Conference on Management of Data, SIGMOD'13, pages 349--360. ACM, 2013. [link](http://arxiv.org/abs/1304.4661) 193 | 194 | 3. M. Babenko, A.V. Goldberg, A. Gupta, and V. Nagarajan. *Algorithms for Hub Label Optimization.* In Proc. 30th ICALP, pages 69--80. LNCS vol. 7965, Springer, 2013. [link](http://research.microsoft.com/apps/pubs/default.aspx?id=192125) 195 | 196 | 4. E. Cohen, E. Halperin, H. Kaplan, and U. Zwick. *Reachability and Distance Queries via 2-hop Labels.* SIAM Journal on Computing, 32, 2003. [link](http://www.cs.tau.ac.il/~zwick/papers/labels-full.ps) 197 | 198 | 5. D. Delling, A.V. Goldberg, R. Savchenko, and R.F. Werneck. *Hub Labels: Theory and Practice.* In Proceedings of the 13th International Symposium on Experimental Algorithms (SEA'14), LNCS. Springer, 2014. [link](http://research.microsoft.com/apps/pubs/default.aspx?id=219802) 199 | -------------------------------------------------------------------------------- /hl/hhl.hpp: -------------------------------------------------------------------------------- 1 | // Hierarchical Hub Labels can be found by greedy algorithms. 2 | // This file contains implementation of path-greedy and level-greedy HHL algorithms. 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "graph.hpp" 27 | #include "dijkstra.hpp" 28 | #include "kheap.hpp" 29 | #include "labeling.hpp" 30 | #include 31 | #include 32 | 33 | namespace hl { 34 | 35 | // Hierarchical Hub Labeling implementation 36 | class HHL { 37 | // Class to store all shortest paths 38 | class SP { 39 | Graph &g; // Graph 40 | Vertex n; // number of vertices 41 | std::vector< std::vector > dist; // Distance table: dist[u][v] = distance(u,v) 42 | std::vector< std::vector > cover; // cover[u][v] is true if (u,v) pair is covered (int for thread safety) 43 | std::vector< std::vector > visited_pt; // mark visited vertices during graph traversal (one array per thread) 44 | 45 | // Check if v is on u--w path under condition that dist(v,w) = lenght. 46 | bool is_path(Vertex u, Vertex v, Vertex w, Distance length, bool forward) { 47 | return get_distance(u, w, forward) != infty 48 | && get_distance(u, v, forward) != infty 49 | && get_distance(u, w, forward) == get_distance(u, v, forward) + length; 50 | } 51 | 52 | public: 53 | SP(Graph &g, int num_threads) : 54 | g(g), 55 | n(g.get_n()), 56 | dist(n, std::vector(n, infty)), 57 | cover(n, std::vector(n)), 58 | visited_pt(num_threads, std::vector(n)) 59 | { 60 | // Calculate distance table 61 | std::vector dijkstra; 62 | for (int i = 0; i < num_threads; ++i) dijkstra.push_back(Dijkstra(g)); 63 | #pragma omp parallel for 64 | for (Vertex u = 0; u < n; ++u) { 65 | Dijkstra &dij = dijkstra[omp_get_thread_num()]; 66 | dij.run(u); 67 | for (Vertex v = 0; v < n; ++v) dist[u][v] = dij.get_distance(v); 68 | } 69 | } 70 | 71 | // Set (u,v) pair covered 72 | void set_cover(Vertex u, Vertex v) { cover[u][v] = true; } 73 | // Check if pair (u,v) is covered 74 | bool get_cover(Vertex u, Vertex v, bool forward = true) const { return forward ? cover[u][v] : cover[v][u]; } 75 | // Get distance(u,v) 76 | Distance get_distance(Vertex u, Vertex v, bool forward = true) { return forward ? dist[u][v] : dist[v][u]; } 77 | 78 | // Get v's descendants in u's shorted paths DAG 79 | void get_descendants(Vertex u, Vertex v, std::vector &d, bool forward = true) { 80 | std::vector &visited = visited_pt[omp_get_thread_num()]; 81 | d.clear(); 82 | if (get_cover(u,v,forward) || get_distance(u,v,forward) == infty) return; 83 | d.push_back(v); 84 | visited[v] = true; 85 | for (size_t i = 0; i < d.size(); ++i) { 86 | for (Graph::arc_iterator a = g.begin(d[i], forward), end = g.end(d[i], forward); a < end; ++a) { 87 | if (!visited[a->head] && !get_cover(u,a->head,forward) && is_path(u,d[i],a->head,a->length,forward)) { 88 | d.push_back(a->head); 89 | visited[a->head] = true; 90 | } 91 | } 92 | } 93 | for (size_t i = 0; i < d.size(); ++i) visited[d[i]] = false; 94 | } 95 | 96 | // Get v's ascendants in u's shorted paths DAG 97 | void get_ascendants(Vertex u, Vertex v, std::vector &d, bool forward = true) { 98 | std::vector &visited = visited_pt[omp_get_thread_num()]; 99 | d.clear(); 100 | if (get_cover(u,v,forward) || get_distance(u,v,forward) == infty) return; 101 | d.push_back(v); 102 | visited[v] = true; 103 | for (size_t i = 0; i < d.size(); ++i) { 104 | for (Graph::arc_iterator a = g.begin(d[i], !forward), end = g.end(d[i], !forward); a < end; ++a) { 105 | if (!visited[a->head] && is_path(u,a->head,d[i],a->length,forward)) { 106 | d.push_back(a->head); 107 | visited[a->head] = true; 108 | } 109 | } 110 | } 111 | for (size_t i = 0; i < d.size(); ++i) visited[d[i]] = false; 112 | } 113 | 114 | // Make all (u,v) pairs uncovered 115 | void clear() { 116 | for (Vertex u = 0; u < n; ++u) { 117 | for (Vertex v = 0; v < n; ++v) cover[u][v] = false; 118 | } 119 | } 120 | }; 121 | 122 | Graph &g; // Graph 123 | Vertex n; // Number of vertices 124 | int num_threads; // number of threads 125 | SP sp; // Shortest paths data structure 126 | KHeap queue; // Queue for greedy selectoin 127 | 128 | std::vector selected; // selected[v] is true if we already picked v into the cover 129 | std::vector cover_size; // cover[v] is number of (uncovered) paths covered by v 130 | std::vector sp_size; // sp_size[v] is the number of uncovered (v,x) and (x,v) pairs 131 | std::vector< std::vector > cover_diff_pt; // cover_diff_pt[i][v] is the difference in cover[v] observed by i'th thread 132 | 133 | // Vertex weight for HHL queue, type 0 for path-greedy, type 1 for weighted greedy 134 | double weight(Vertex v, int type) { 135 | if (type == 0) return 1.0/cover_size[v]; 136 | else if (type == 1) return static_cast(sp_size[v])/cover_size[v]; 137 | else assert(0); 138 | } 139 | 140 | public: 141 | HHL(Graph &g, int num_threads) : 142 | g(g), 143 | n(g.get_n()), 144 | num_threads(num_threads), 145 | sp(g, num_threads), 146 | queue(n), 147 | selected(n), 148 | cover_size(n), 149 | sp_size(n), 150 | cover_diff_pt(num_threads, std::vector(n)) {} 151 | 152 | // Build HHL using greedy strategy. type = 0 for path-greedy, type = 1 for label-greedy. 153 | void run(int type, std::vector &order, Labeling &labeling) { 154 | order.clear(); 155 | order.resize(n); 156 | labeling.clear(); 157 | queue.clear(); 158 | sp.clear(); 159 | for (Vertex v = 0; v < n; ++v) selected[v] = 0; 160 | 161 | // Calculate initial cover[v] and sp_size[v] 162 | #pragma omp parallel 163 | { 164 | std::vector d; 165 | #pragma omp for schedule(dynamic) 166 | for (Vertex v = 0; v < n; ++v) { 167 | for (Vertex u = 0; u < n; ++u) { 168 | sp.get_descendants(u, v, d); 169 | cover_size[v] += d.size(); 170 | if (u == v) sp_size[v] += d.size(); 171 | } 172 | sp.get_descendants(v, v, d, false); 173 | sp_size[v] += d.size(); 174 | } 175 | } 176 | 177 | // Initialize the greedy selection queue 178 | for (Vertex v = 0; v < n; ++v) queue.update(v, weight(v, type)); 179 | 180 | // HHL greedy selection loop 181 | for(Vertex wi = 0; !queue.empty(); ++wi) { 182 | Vertex w = queue.pop(); 183 | selected[w] = true; 184 | order[wi] = w; 185 | 186 | // Put w into labels of reachable vertices 187 | std::vector d; 188 | for (int forward = 0; forward < 2; ++forward) { 189 | sp.get_descendants(w, w, d, forward); 190 | // We use wi for the hub id (instead of w) so the labels are already sorted by ids 191 | for(size_t i = 0; i < d.size(); ++i) labeling.add(d[i], !forward, wi, sp.get_distance(d[i],w,!forward)); 192 | } 193 | 194 | // Update cover[v] and sp_size[v] 195 | #pragma omp parallel 196 | { 197 | std::vector d; 198 | std::vector a; 199 | for (int forward = 0; forward < 2; ++forward) { 200 | #pragma omp for schedule(dynamic) 201 | for (Vertex v = 0; v < n; ++v) { 202 | sp.get_descendants(v, w, d, forward); // Cet all v--w--d[i] shortest paths 203 | sp_size[v] -= d.size(); // Remove (v,d[i]) and (d[i],v) from sp_size[v] 204 | 205 | // Iterate over all v--d[i] shortest paths and update cover 206 | // Note that there could be a v--d[i] shortest path without w in the middle 207 | if (forward) { 208 | for(size_t i = 0; i < d.size(); ++i) { 209 | sp.get_ascendants(v, d[i], a, forward); 210 | for (size_t j = 0; j < a.size(); ++j) ++cover_diff_pt[omp_get_thread_num()][a[j]]; 211 | sp.set_cover(v, d[i]); 212 | } 213 | } 214 | } 215 | // Wait until all reverese paths are processed before updating cover 216 | #pragma omp barrier 217 | } 218 | } 219 | 220 | // Collect cover[v] updates from all threads 221 | for (int i = 0; i < num_threads; ++i) { 222 | for (Vertex v = 0; v < n; ++v) { 223 | cover_size[v] -= cover_diff_pt[i][v]; 224 | cover_diff_pt[i][v] = 0; 225 | } 226 | } 227 | assert(cover_size[w] == 0 && sp_size[w] == 0); 228 | 229 | // Update queue 230 | for (Vertex v = 0; v < n; ++v) if (!selected[v]) queue.update(v, weight(v, type)); 231 | } 232 | } 233 | }; 234 | 235 | } 236 | -------------------------------------------------------------------------------- /hl/graph.hpp: -------------------------------------------------------------------------------- 1 | // This file contains definitions of basic types: Vertex, Distance, Arc, and Graph. 2 | // Graph can be read from file in DIMACS or METIS format. 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace hl { 37 | 38 | // Types for vertices and distances to avoid typing 'int' for almost every variable 39 | typedef unsigned int Vertex; 40 | typedef int Distance; 41 | static const Vertex none = -1U; 42 | static const Distance infty = std::numeric_limits::max(); 43 | 44 | // Arc class 45 | struct Arc { 46 | Vertex head:30; // head vertex ID 47 | bool forward:1; // true if this is an outgoing arc 48 | bool reverse:1; // true if this is an incoming arc 49 | Distance length; // arc length 50 | Arc(Vertex h, Distance l, bool f, bool r) : head(h), forward(f), reverse(r), length(l) {} 51 | Arc() {} 52 | }; 53 | 54 | 55 | // Directed graph class 56 | class Graph { 57 | long long n; // Number of vertices 58 | long long m; // Number of arcs 59 | std::vector arcs; // Compressed adjacency lists 60 | std::vector< std::vector > a_begin; // Index of first outgoing/incoming arc 61 | std::vector< std::vector > a_end; // Index of last outgoing/incoming arc + 1 62 | std::vector< std::pair > tmp_arcs; // Temporary list of arcs 63 | 64 | // Reset graph 65 | void reset() { 66 | n = 0; m = 0; 67 | arcs.clear(); 68 | tmp_arcs.clear(); 69 | for (int i = 0; i < 2; ++i) { 70 | a_begin[i].clear(); 71 | a_end[i].clear(); 72 | } 73 | } 74 | 75 | // Compare arcs by head 76 | struct cmp_by_head { 77 | bool operator() (const std::pair &xx, const std::pair &yy) { 78 | const Arc &x = xx.second, &y = yy.second; 79 | if (xx.first != yy.first) return xx.first < yy.first; 80 | if (x.head != y.head) return x.head < y.head; 81 | return x.length < y.length; 82 | } 83 | }; 84 | 85 | // Compare arcs by direction 86 | struct cmp_by_direction { 87 | bool operator() (const std::pair &xx, const std::pair &yy) { 88 | const Arc &x = xx.second, &y = yy.second; 89 | if (xx.first != yy.first) return xx.first < yy.first; 90 | if (x.reverse != y.reverse) return x.reverse == true; 91 | if (x.forward != y.forward) return x.forward == false; 92 | if (x.head != y.head) return x.head < y.head; 93 | return x.length < y.length; 94 | } 95 | }; 96 | 97 | // Construct adjacency lists from the temporary list of arcs 98 | void init_arcs() { 99 | std::vector< std::pair > &a = tmp_arcs; 100 | size_t i; 101 | // Remove redundant arcs 102 | std::sort(a.begin(), a.end(), cmp_by_direction()); 103 | i = 0; 104 | for (size_t j = 1; j < a.size(); ++j) { 105 | Arc &ai = a[i].second, &aj = a[j].second; 106 | if (a[i].first == a[j].first && ai.head == aj.head && ai.forward == aj.forward && ai.reverse == aj.reverse) continue; 107 | if (++i < j) a[i] = a[j]; 108 | } 109 | a.resize(i+1); 110 | 111 | // Merge forward and reverse arcs into one bidirectional arc 112 | std::sort(a.begin(), a.end(), cmp_by_head()); 113 | i = 0; 114 | for (size_t j = 1; j < a.size(); ++j) { 115 | Arc &ai = a[i].second, &aj = a[j].second; 116 | if (a[i].first == a[j].first && ai.head == aj.head && ai.length == aj.length) { 117 | ai.forward |= aj.forward; 118 | ai.reverse |= aj.reverse; 119 | continue; 120 | } 121 | if (++i < j) a[i] = a[j]; 122 | } 123 | a.resize(i+1); 124 | 125 | // Order arcs by direction and determine adjacency lists 126 | std::sort(a.begin(), a.end(), cmp_by_direction()); 127 | arcs.resize(a.size()); 128 | a_begin[0].resize(n); 129 | a_begin[1].resize(n); 130 | a_end[0].resize(n); 131 | a_end[1].resize(n); 132 | for (size_t j = 0; j < a.size(); ++j) { 133 | arcs[j] = a[j].second; 134 | if (j == 0 || a[j].first != a[j-1].first) { 135 | if (arcs[j].reverse) a_begin[0][a[j].first] = &arcs[j]; 136 | if (arcs[j].forward) a_begin[1][a[j].first] = &arcs[j]; 137 | } 138 | if (j+1 == a.size() || a[j].first != a[j+1].first) { 139 | if (arcs[j].reverse) a_end[0][a[j].first] = &arcs[j] + 1; 140 | if (arcs[j].forward) a_end[1][a[j].first] = &arcs[j] + 1; 141 | } 142 | if (j > 0 && a[j].first == a[j-1].first) { 143 | if (arcs[j].reverse != arcs[j-1].reverse) a_end[0][a[j].first] = &arcs[j]; 144 | if (arcs[j].forward != arcs[j-1].forward) a_begin[1][a[j].first] = &arcs[j]; 145 | } 146 | } 147 | a.clear(); 148 | } 149 | 150 | // Add arc to the temporary list of arcs 151 | bool add_tmp_arc(Vertex u, Vertex v, Distance w, bool undirected) { 152 | if (u < 0 || v < 0 || u >= n || v >= n) return false; 153 | tmp_arcs.push_back(std::make_pair(u, Arc(v, w, true, undirected))); 154 | tmp_arcs.push_back(std::make_pair(v, Arc(u, w, undirected, true))); 155 | m += 1 + undirected; 156 | return true; 157 | } 158 | 159 | // Read graph from file in DIMACS format 160 | // p sp n m - header: n vertices, m edges 161 | // c bla-bla - comment line 162 | // a u v w - arc (u,v) with length w 163 | bool read_dimacs(FILE *file, bool undirected = false) { 164 | char buf[512]; 165 | long long u,v,w,m; 166 | bool inited = false; 167 | while (fgets(buf, sizeof(buf), file) != NULL) { 168 | if (buf[strlen(buf)-1] != '\n') { 169 | if (buf[0] != 'c') return false; 170 | while(!feof(file) && fgetc(file) != '\n'); 171 | } else if (buf[0] == 'p') { 172 | if (inited) return false; inited = true; 173 | if (sscanf(buf, "p sp %lld %lld", &n, &m) != 2) return false; 174 | } else if (buf[0] == 'a') { 175 | if (sscanf(buf, "a %lld %lld %lld", &u, &v, &w) != 3) return false; 176 | if (!add_tmp_arc(u-1, v-1, w, undirected)) return false; 177 | } else if (buf[0] == 'c') continue; 178 | else return false; 179 | } 180 | finalize(); 181 | return true; 182 | } 183 | 184 | // Write file in DIMACS format 185 | void write_dimacs(FILE *file) { 186 | long long m; 187 | for (Vertex v = 0; v < n; ++v) m += get_degree(v, true); 188 | fprintf(file, "p sp %lld %lld\n", n, m); 189 | for (Vertex v = 0; v < n; ++v) { 190 | for (arc_iterator a = begin(v), e = end(v); a < e; ++a) { 191 | long long u = v, w = a->head, l = a->length; 192 | fprintf(file, "a %lld %lld %lld\n", u+1, w+1, l); 193 | } 194 | } 195 | } 196 | 197 | // Read graph from file in METIS format 198 | // n m [fmt] [ncon] - header: fmt = ijk: i - s (vertex size) is present, 199 | // j - w (vertex weights) are present, 200 | // k - l (edge lengths) are present, 201 | // ncon - number of vertex weights. All defaults are 1. 202 | // ... 203 | // s w_1 .. w_ncon v_1 l_1 ... - line for a vertex: size (if i), vertex weights (if j), edges with lengths (if k) 204 | bool read_metis(FILE *file, bool undirected = false) { 205 | int c, i = 0, fmt = 0, ncon = 0, skip = 0; 206 | long long elem = 0; 207 | Vertex v = 0, head = none; 208 | bool newline = true, inelem = false; 209 | do { 210 | c = fgetc(file); 211 | if (newline && c == '%') { while (c != '\n' && c != EOF) c = fgetc(file); } 212 | else if (isdigit(c)) { inelem = true; elem = elem*10 + c - '0'; } 213 | else if (isspace(c) || c == EOF) { 214 | if (inelem) { 215 | if (v == 0) { 216 | if (i == 0) n = elem; 217 | else if (i == 1) /* m = elem */; 218 | else if (i == 2) { 219 | if ((elem % 10 > 1) || ((elem/10) % 10 > 1) || elem > 111) return false; 220 | fmt = elem; 221 | skip = (fmt >= 100) + (fmt % 100 >= 10); 222 | } else if (i == 3) { 223 | if (fmt % 100 < 10) return false; 224 | ncon = elem; 225 | skip = (fmt >= 100) + ncon; 226 | } else return false; 227 | } else if (i >= skip) { 228 | if (fmt % 10 == 1) { 229 | if ((i-skip) % 2 == 0) head = elem-1; 230 | else if (!add_tmp_arc(v-1, head, elem, undirected)) return false; 231 | } else if (!add_tmp_arc(v-1, elem-1, 1, undirected)) return false; 232 | } 233 | ++i; elem = 0; inelem = false; 234 | } 235 | if (c == '\n' || (!newline && c == EOF)) { 236 | if (v > 0 && (i < skip || (fmt % 10 == 1 && (i-skip) % 2 == 1))) return false; 237 | ++v; i = 0; newline = true; 238 | } 239 | } else return false; 240 | } while (c != EOF); 241 | finalize(); 242 | return true; 243 | } 244 | 245 | // Read graph from file in SNAP format 246 | // # Nodes: n Edges: m - header: n vertices, m edges 247 | // # - comment line 248 | // u v - arc (u,v) with unit length 249 | bool read_snap(FILE* file, bool undirected = false) { 250 | char buf[512]; 251 | long long u,v,m; 252 | bool inited = false; 253 | while (fgets(buf, sizeof(buf), file)) { 254 | if (buf[0] == '#') { 255 | if (sscanf(buf, "# Nodes: %lld Edges: %lld", &n, &m) == 2) { 256 | if (inited) return false; inited = true; 257 | } 258 | for (int c = buf[strlen(buf)-1]; c != '\n' && c != EOF; c = fgetc(file)); 259 | } else { 260 | if (buf[strlen(buf)-1] != '\n' && !feof(file)) return false; 261 | if (sscanf(buf, "%lld %lld", &u, &v) != 2) return false; 262 | if (!add_tmp_arc(u, v, 1, undirected)) return false; 263 | } 264 | } 265 | finalize(); 266 | return true; 267 | } 268 | 269 | public: 270 | Graph() : n(0), m(0), a_begin(2), a_end(2) {} 271 | 272 | Vertex get_n() const { return n; } // Get number of vertices 273 | size_t get_m() const { return m; } // Get number of arcs 274 | 275 | typedef Arc* arc_iterator; // Adjacency list iterator type 276 | 277 | // Get adjacency list iterator bounds 278 | arc_iterator begin(Vertex v, bool forward = true) const { return a_begin[forward][v]; } 279 | arc_iterator end(Vertex v, bool forward = true) const { return a_end[forward][v]; } 280 | 281 | // Get vertex degree 282 | size_t get_degree(Vertex v, bool forward) const { return end(v, forward) - begin(v, forward); } 283 | size_t get_degree(Vertex v) const { return get_degree(v, true) + get_degree(v, false); } 284 | 285 | // Read graph from file (if the file format is supported) 286 | bool read(char *filename, bool undirected = false) { 287 | FILE *file; 288 | if ((file = fopen(filename, "r")) == NULL) return false; 289 | if (read_dimacs(file, undirected)) { fclose(file); return true; } rewind(file); reset(); 290 | if (read_metis(file, undirected)) { fclose(file); return true; } rewind(file); reset(); 291 | if (read_snap(file, undirected)) { fclose(file); return true; } fclose(file); reset(); 292 | return false; 293 | } 294 | 295 | // Write graph to file 296 | bool write(char *filename) { 297 | FILE *file; 298 | if ((file = fopen(filename, "w")) == NULL) return false; 299 | write_dimacs(file); 300 | return ferror(file) ? (fclose(file) && false) : !fclose(file); 301 | } 302 | 303 | // Graph construction interface 304 | // User should set n, add some arcs, and call finalize() 305 | 306 | // Set the number of vertices 307 | void set_n(Vertex nn) { n = nn; }; 308 | // Add (u,v) arc (with (v,u) arc if undirected) to the temporary list of arcs 309 | void add_arc(Vertex u, Vertex v, Distance w, bool undirected = false) { bool r = add_tmp_arc(u, v, w, undirected); assert(r); } 310 | // Construct adjacency lists from the temporary list of arcs 311 | void finalize() { init_arcs(); } 312 | }; 313 | 314 | } 315 | -------------------------------------------------------------------------------- /hl/uhhl.hpp: -------------------------------------------------------------------------------- 1 | // Hierarchical Hub Labels can be found by greedy algorithms. 2 | // When shortest paths are unique (there is at most one shortest path between any two vertices) 3 | // greedy algorithms can be implemented more efficiently. 4 | // This file contains implementation of path-greedy and level-greedy HHL algorithms for USP systems. 5 | // 6 | // Copyright (c) 2014, 2015 savrus 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | #pragma once 27 | 28 | #include "graph.hpp" 29 | #include "dijkstra.hpp" 30 | #include "kheap.hpp" 31 | #include "labeling.hpp" 32 | #include 33 | #include 34 | 35 | namespace hl { 36 | 37 | // Hierarchical Hub Labeling implementation for unique shortest paths (USP) 38 | class UHHL { 39 | // Class to store shortest paths trees (SPT) 40 | class SP { 41 | // Class for Dijkstra algorithm with USP emulation 42 | class USPDijkstra : BasicDijkstra { 43 | std::vector hops; // hops[v] is the number of hops from source to v 44 | 45 | // Set new distance, parent and hops for v 46 | void uspd_update(Vertex v, Distance dd, int h, Vertex u = none) { 47 | hops[v] = h; 48 | update(v, dd, u); 49 | } 50 | 51 | // Clear internal structures 52 | void uspd_clear() { 53 | for (size_t i = 0; i < dirty.size(); ++i) hops[dirty[i]] = 0; 54 | clear(); 55 | } 56 | public: 57 | USPDijkstra(Graph &g) : BasicDijkstra(g), hops(g.get_n()) {} 58 | 59 | Distance get_distance(Vertex v) { return distance[v]; } // Distance from source to v 60 | Vertex get_parent(Vertex v) { return parent[v]; } // v's parent in the shortest path tree 61 | 62 | // Find distances from v to all other vertices and build shortest path tree 63 | // Choose parent with the smallest id to break ties 64 | void run(Vertex v, bool forward = true) { 65 | uspd_clear(); 66 | uspd_update(v, 0, 0); 67 | while (!queue.empty()) { 68 | Vertex u = queue.pop(); 69 | Distance d = distance[u]; 70 | for (Graph::arc_iterator a = g->begin(u, forward), end = g->end(u, forward); a < end; ++a) { 71 | Distance dd = d + a->length; 72 | assert(dd > d && dd < infty); 73 | if (dd < distance[a->head] 74 | || (dd == distance[a->head] && hops[u] + 1 < hops[a->head]) 75 | || (dd == distance[a->head] && hops[u] + 1 == hops[a->head] && u < parent[a->head]) ) 76 | uspd_update(a->head, dd, hops[u] + 1, u); 77 | } 78 | } 79 | } 80 | }; 81 | 82 | Graph &g; // Graph 83 | Vertex n; // number of vertices 84 | std::vector< std::vector > dist; // Distance table: dist[u][v] = distance(u,v) 85 | std::vector< std::vector > cover; // cover[u][v] is true if (u,v) pair is covered (int for thread safety) 86 | std::vector< std::vector > visited_pt; // mark visited vertices during graph traversal (one array per thread) 87 | std::vector< std::vector< std::vector > > parent; // Parent table: parent[forward][u][v] = v's parent in u's SPT 88 | 89 | // Check if v is on u--w path under condition that there is an arc (v,w) 90 | bool is_path(Vertex u, Vertex v, Vertex w, bool forward) { return v == get_parent(u, w, forward); } 91 | 92 | public: 93 | SP(Graph &g, int num_threads) : 94 | g(g), 95 | n(g.get_n()), 96 | dist(n, std::vector(n, infty)), 97 | cover(n, std::vector(n)), 98 | visited_pt(num_threads, std::vector(n)), 99 | parent(2, std::vector< std::vector >(n, std::vector(n, none))) 100 | { 101 | // Build distance table and forward SPT 102 | std::vector dijkstra; 103 | for (int i = 0; i < num_threads; ++i) dijkstra.push_back(USPDijkstra(g)); 104 | #pragma omp parallel for 105 | for (Vertex u = 0; u < n; ++u) { 106 | USPDijkstra &dij = dijkstra[omp_get_thread_num()]; 107 | dij.run(u); 108 | for (Vertex v = 0; v < n; ++v) { 109 | dist[u][v] = dij.get_distance(v); 110 | parent[true][u][v] = dij.get_parent(v); 111 | } 112 | } 113 | // Build reverse SPT from forward SPT to support non-USP graphs 114 | #pragma omp parallel 115 | { 116 | std::vector d; 117 | #pragma omp for 118 | for (Vertex u = 0; u < n; ++u) { 119 | for(Graph::arc_iterator a = g.begin(u,true), end = g.end(u,true); a < end; ++a) { 120 | if (parent[true][u][a->head] != u) continue; 121 | get_descendants(u, a->head, d); 122 | for (size_t i = 0; i < d.size(); ++i) parent[false][d[i]][u] = a->head; 123 | } 124 | } 125 | } 126 | } 127 | 128 | // Set (u,v) pair covered 129 | void set_cover(Vertex u, Vertex v) { cover[u][v] = true; } 130 | // Check if pair (u,v) is covered 131 | bool get_cover(Vertex u, Vertex v, bool forward = true) const { return forward ? cover[u][v] : cover[v][u]; } 132 | // Get distance(u,v) 133 | Distance get_distance(Vertex u, Vertex v, bool forward = true) { return forward ? dist[u][v] : dist[v][u]; } 134 | // Get v's parent in u's 'forward' SPT 135 | Vertex get_parent(Vertex u, Vertex v, bool forward = true) { return parent[forward][u][v]; } 136 | 137 | // Get v's descendants in u's SPT 138 | void get_descendants(Vertex u, Vertex v, std::vector &d, bool forward = true) { 139 | std::vector &visited = visited_pt[omp_get_thread_num()]; 140 | d.clear(); 141 | if (get_cover(u,v,forward) || (u != v && get_parent(u,v,forward) == none)) return; 142 | d.push_back(v); 143 | visited[v] = true; 144 | for (size_t i = 0; i < d.size(); ++i) { 145 | for (Graph::arc_iterator a = g.begin(d[i], forward), end = g.end(d[i], forward); a < end; ++a) { 146 | if (!visited[a->head] && !get_cover(u,a->head,forward) && is_path(u,d[i],a->head,forward)) { 147 | d.push_back(a->head); 148 | visited[a->head] = true; 149 | } 150 | } 151 | } 152 | for (size_t i = 0; i < d.size(); ++i) visited[d[i]] = false; 153 | } 154 | 155 | // Get v's ascendants in u's SPT 156 | void get_ascendants(Vertex u, Vertex v, std::vector &d, bool forward = true) { 157 | std::vector &visited = visited_pt[omp_get_thread_num()]; 158 | d.clear(); 159 | if (get_cover(u,v,forward) || (u != v && get_parent(u,v,forward) == none)) return; 160 | d.push_back(v); 161 | visited[v] = true; 162 | for (size_t i = 0; i < d.size(); ++i) { 163 | for (Graph::arc_iterator a = g.begin(d[i], !forward), end = g.end(d[i], !forward); a < end; ++a) { 164 | if (!visited[a->head] && is_path(u,a->head,d[i],forward)) { 165 | d.push_back(a->head); 166 | visited[a->head] = true; 167 | } 168 | } 169 | } 170 | for (size_t i = 0; i < d.size(); ++i) visited[d[i]] = false; 171 | } 172 | 173 | // Make all (u,v) pairs uncovered 174 | void clear() { 175 | for (Vertex u = 0; u < n; ++u) { 176 | for (Vertex v = 0; v < n; ++v) cover[u][v] = false; 177 | } 178 | } 179 | }; 180 | 181 | Graph &g; // Graph 182 | Vertex n; // Number of vertices 183 | int num_threads; // number of threads 184 | SP sp; // Shortest paths data structure 185 | KHeap queue; // Queue for greedy selectoin 186 | 187 | std::vector selected; // selected[v] is true if we already picked v into the cover 188 | std::vector cover_size; // cover[v] is number of (uncovered) paths covered by v 189 | std::vector sp_size; // sp_size[v] is the number of uncovered (v,x) and (x,v) pairs 190 | std::vector< std::vector > cover_diff_pt; // cover_diff[i][v] is the difference in cover[v] observed by i'th thread 191 | std::vector< std::vector > subtree_pt; // array to temporarily calculate subtree size; 192 | 193 | // Apply cover_diff found by threads to cover_size 194 | void apply_cover_diff() { 195 | for (int i = 0; i < num_threads; ++i) { 196 | for (Vertex v = 0; v < n; ++v) { 197 | cover_size[v] += cover_diff_pt[i][v]; 198 | cover_diff_pt[i][v] = 0; 199 | } 200 | } 201 | } 202 | 203 | // Vertex weight for HHL queue, type 0 for path-greedy, type 1 for weighted greedy 204 | double weight(Vertex v, int type) { 205 | if (type == 0) return 1.0/cover_size[v]; 206 | else if (type == 1) return static_cast(sp_size[v])/cover_size[v]; 207 | else assert(0); 208 | } 209 | 210 | public: 211 | UHHL(Graph &g, int num_threads) : 212 | g(g), 213 | n(g.get_n()), 214 | num_threads(num_threads), 215 | sp(g, num_threads), 216 | queue(n), 217 | selected(n), 218 | cover_size(n), 219 | sp_size(n), 220 | cover_diff_pt(num_threads, std::vector(n)), 221 | subtree_pt(num_threads, std::vector(n)) {} 222 | 223 | // Build HHL using greedy strategy. type = 0 for path-greedy, type = 1 for label-greedy. 224 | void run(int type, std::vector &order, Labeling &labeling) { 225 | order.clear(); 226 | order.resize(n); 227 | labeling.clear(); 228 | queue.clear(); 229 | sp.clear(); 230 | for (Vertex v = 0; v < n; ++v) selected[v] = 0; 231 | 232 | // Calculate initial cover_size[v] and sp_size[v] 233 | #pragma omp parallel 234 | { 235 | std::vector d; 236 | std::vector &subtree = subtree_pt[omp_get_thread_num()]; 237 | #pragma omp for schedule(dynamic) 238 | for (Vertex v = 0; v < n; ++v) { 239 | sp.get_descendants(v, v, d); 240 | sp_size[v] += d.size(); 241 | for (size_t i = d.size(); i > 0; --i) { 242 | Vertex q = d[i-1]; 243 | ++subtree[q]; 244 | cover_diff_pt[omp_get_thread_num()][q] += subtree[q]; 245 | if (i-1 > 0) subtree[sp.get_parent(v, q)] += subtree[q]; 246 | subtree[q] = 0; 247 | } 248 | sp.get_descendants(v, v, d, false); 249 | sp_size[v] += d.size(); 250 | } 251 | } 252 | apply_cover_diff(); 253 | 254 | // Initialize the greedy selection queue 255 | for (Vertex v = 0; v < n; ++v) queue.update(v, weight(v, type)); 256 | 257 | 258 | // HHL greedy selection loop 259 | for(Vertex wi = 0; !queue.empty(); ++wi) { 260 | Vertex w = queue.pop(); 261 | selected[w] = true; 262 | order[wi] = w; 263 | 264 | // Put w into labels of reachable vertices 265 | std::vector d; 266 | for (int forward = 0; forward < 2; ++forward) { 267 | sp.get_descendants(w, w, d, forward); 268 | // We use wi for the hub id (instead of w) so the labels are already sorted by ids 269 | for(size_t i = 0; i < d.size(); ++i) labeling.add(d[i], !forward, wi, sp.get_distance(d[i],w,!forward)); 270 | } 271 | 272 | // Update cover[v] and sp_size[v] 273 | #pragma omp parallel 274 | { 275 | std::vector d; 276 | std::vector &subtree = subtree_pt[omp_get_thread_num()]; 277 | for (int forward = 0; forward < 2; ++forward) { 278 | #pragma omp for schedule(dynamic) 279 | for (Vertex v = 0; v < n; ++v) { 280 | sp.get_descendants(v, w, d, forward); // Cet all v--w--d[i] shortest paths 281 | sp_size[v] -= d.size(); // Remove (v,d[i]) and (d[i],v) from sp_size[v] 282 | 283 | // Iterate over all v--d[i] shortest paths and update cover. 284 | // Here we use that for any d[i] there is only one v--w--d[i] shortest path. 285 | // We add v--w--a--d[i] paths to the cover[a]. 286 | // One may ask what about v--a--w--d[i]? Look at it from d[i] in the other direction. 287 | for (size_t i = d.size(); i > 0; --i) { 288 | Vertex q = d[i-1]; 289 | ++subtree[q]; 290 | if (i-1 > 0 || forward) cover_diff_pt[omp_get_thread_num()][q] -= subtree[q]; 291 | if (i-1 > 0) subtree[sp.get_parent(v, q, forward)] += subtree[q]; 292 | subtree[q] = 0; 293 | if (forward) sp.set_cover(v, q); 294 | } 295 | } 296 | // Wait until all reverese paths are processed before updating cover 297 | #pragma omp barrier 298 | } 299 | } 300 | 301 | // Collect cover[v] updates from all threads 302 | apply_cover_diff(); 303 | assert(cover_size[w] == 0 && sp_size[w] == 0); 304 | 305 | // Update queue 306 | for (Vertex v = 0; v < n; ++v) if (!selected[v]) queue.update(v, weight(v, type)); 307 | } 308 | } 309 | }; 310 | 311 | } 312 | -------------------------------------------------------------------------------- /hl/ghl.hpp: -------------------------------------------------------------------------------- 1 | // Hub Labels can be found by a greedy algorithm 2 | // This file contains implementation of GHLp algorithm for p-norm O(log n)-optimal hub labels. 3 | // 4 | // Copyright (c) 2014, 2015 savrus 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "graph.hpp" 27 | #include "dijkstra.hpp" 28 | #include "kheap.hpp" 29 | #include "labeling.hpp" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace hl { 37 | 38 | // General Hub Labeling implementation 39 | class GHL { 40 | // Class to store all shortest paths 41 | class SP { 42 | Graph &g; // Graph 43 | Vertex n; // number of vertices 44 | std::vector< std::vector > dist; // Distance table: dist[u][v] = distance(u,v) 45 | std::vector< std::vector > cover; // cover[u][v] is true if (u,v) pair is covered (int for thread safety) 46 | std::vector< std::vector > visited_pt; // mark visited vertices during graph traversal (one array per thread) 47 | 48 | // Check if v is on u--w path under condition that dist(v,w) = lenght. 49 | bool is_path(Vertex u, Vertex v, Vertex w, Distance length, bool forward) { 50 | return get_distance(u, w, forward) != infty 51 | && get_distance(u, v, forward) != infty 52 | && get_distance(u, w, forward) == get_distance(u, v, forward) + length; 53 | } 54 | 55 | public: 56 | SP(Graph &g, int num_threads) : 57 | g(g), 58 | n(g.get_n()), 59 | dist(n, std::vector(n, infty)), 60 | cover(n, std::vector(n)), 61 | visited_pt(num_threads, std::vector(n)) 62 | { 63 | // Calculate distance table 64 | std::vector dijkstra; 65 | for (int i = 0; i < num_threads; ++i) dijkstra.push_back(Dijkstra(g)); 66 | #pragma omp parallel for 67 | for (Vertex u = 0; u < n; ++u) { 68 | Dijkstra &dij = dijkstra[omp_get_thread_num()]; 69 | dij.run(u); 70 | for (Vertex v = 0; v < n; ++v) dist[u][v] = dij.get_distance(v); 71 | } 72 | } 73 | 74 | // Set (u,v) pair covered 75 | void set_cover(Vertex u, Vertex v) { cover[u][v] = true; } 76 | // Check if pair (u,v) is covered 77 | bool is_covered(Vertex u, Vertex v, bool forward = true) const { return forward ? cover[u][v] : cover[v][u]; } 78 | // Get distance(u,v) 79 | Distance get_distance(Vertex u, Vertex v, bool forward = true) { return forward ? dist[u][v] : dist[v][u]; } 80 | 81 | // Get v's descendants in u's shorted paths DAG 82 | void get_descendants(Vertex u, Vertex v, std::vector &d, bool forward = true) { 83 | std::vector &visited = visited_pt[omp_get_thread_num()]; 84 | d.clear(); 85 | if (get_distance(u,v,forward) == infty) return; 86 | d.push_back(v); 87 | visited[v] = true; 88 | for (size_t i = 0; i < d.size(); ++i) { 89 | for (Graph::arc_iterator a = g.begin(d[i], forward), end = g.end(d[i], forward); a < end; ++a) { 90 | if (!visited[a->head] && is_path(u,d[i],a->head,a->length,forward)) { 91 | d.push_back(a->head); 92 | visited[a->head] = true; 93 | } 94 | } 95 | } 96 | for (size_t i = 0; i < d.size(); ++i) visited[d[i]] = false; 97 | } 98 | 99 | // Get v's ascendants in u's shorted paths DAG 100 | void get_ascendants(Vertex u, Vertex v, std::vector &d, bool forward = true) { 101 | std::vector &visited = visited_pt[omp_get_thread_num()]; 102 | d.clear(); 103 | if (get_distance(u,v,forward) == infty) return; 104 | d.push_back(v); 105 | visited[v] = true; 106 | for (size_t i = 0; i < d.size(); ++i) { 107 | for (Graph::arc_iterator a = g.begin(d[i], !forward), end = g.end(d[i], !forward); a < end; ++a) { 108 | if (!visited[a->head] && is_path(u,a->head,d[i],a->length,forward)) { 109 | d.push_back(a->head); 110 | visited[a->head] = true; 111 | } 112 | } 113 | } 114 | for (size_t i = 0; i < d.size(); ++i) visited[d[i]] = false; 115 | } 116 | 117 | // Make all (u,v) pairs uncovered 118 | void clear() { 119 | for (Vertex u = 0; u < n; ++u) { 120 | for (Vertex v = 0; v < n; ++v) cover[u][v] = false; 121 | } 122 | } 123 | }; 124 | 125 | // Proxy class for labeling to keep track if v is in u's label 126 | class ProxyLabeling { 127 | Labeling *l; // Labeling 128 | std::vector< std::vector< std::vector > > inlabel; // inlabel[v][forward][u] is true if v is already in u's label 129 | public: 130 | ProxyLabeling(size_t n) : inlabel(n, std::vector< std::vector >(2, std::vector(n))) {} 131 | // Initialize with new labeling 132 | void set_labeling(Labeling &labeling) { clear(); labeling.clear(); l = &labeling; } 133 | // Add hub (v,d) to forward or reverse label of u 134 | void add(Vertex u, bool forward, Vertex v, Distance d) { 135 | if (!inlabel[v][forward][u]) { 136 | l->add(u, forward, v, d); 137 | inlabel[v][forward][u] = true; 138 | } 139 | } 140 | // Get u's forward or reverse label size 141 | size_t get_size(Vertex u, bool forward) { return l->get_label_hubs(u)[forward].size(); } 142 | // Get if v is in u's forward or reverse label 143 | bool is_in_label(Vertex u, bool forward, Vertex v) { return inlabel[v][forward][u]; } 144 | // Clear 145 | void clear() { 146 | for (Vertex v = 0; v < inlabel.size(); ++v) { 147 | for (int forward = 0; forward < 2; ++forward) { 148 | for (Vertex u = 0; u < inlabel[v][forward].size(); ++u) inlabel[v][forward][u] = false; 149 | } 150 | } 151 | } 152 | }; 153 | 154 | // Class for Approximate Maximum Density Subgraph computation 155 | class AMDS { 156 | Vertex n; // Number of vertices in graph 157 | SP *sp; // Shortest paths data structure 158 | ProxyLabeling *l; // Labeling with is_in_label() interface 159 | KHeap queue; // ADS queue 160 | std::vector< std::vector > degree; // degree[forward][u] is u's degree at `forward` side of the center graph 161 | std::vector< std::vector > inads; // inads[forward][u] is true if u is at `forward` side of the center graph 162 | 163 | // Get p-norm vertex weight 164 | double weight(Vertex u, bool forward, float p) { 165 | double base = l->get_size(u, forward); 166 | return pow(base + 1, p) - pow(base, p); 167 | } 168 | 169 | // Get edges/vertices_weight with correct value when vertices_weight = 0 170 | double ratio(size_t edges, double vertices_weight) { 171 | if (edges == 0) return 0; 172 | if (vertices_weight == 0) return std::numeric_limits::max(); 173 | return static_cast(edges)/vertices_weight; 174 | } 175 | 176 | public: 177 | AMDS(Graph &g, SP &sp, ProxyLabeling &l) : n(g.get_n()), sp(&sp), l(&l), queue(2*n), degree(2,std::vector(n)), inads(2, std::vector(n)) {} 178 | 179 | // Check if vertex u[forward] is in AMDS. 180 | // This should be used only if the density repored by run exceeds the limit 181 | bool is_in(Vertex u, bool forward) { return inads[forward][u]; } 182 | 183 | // Find the density of AMDS of v's center graph or the subgraph with density greater than the limit 184 | double run(Vertex v, float p, double limit = std::numeric_limits::max()) { 185 | queue.clear(); 186 | size_t edges = 0; 187 | double vertices_weight = 0; 188 | std::vector descendants; 189 | 190 | // Determine the initial center graph and put vertex weights into the queue 191 | for (Vertex u = 0; u < n; ++u) { 192 | for (int forward = 0; forward < 2; ++forward) { 193 | sp->get_descendants(u, v, descendants, forward); 194 | size_t d = 0; 195 | for (size_t i = 0; i < descendants.size(); ++i) { 196 | if (!sp->is_covered(u, descendants[i], forward)) ++d; 197 | } 198 | degree[forward][u] = d; 199 | inads[forward][u] = d > 0; 200 | if (forward) edges += d; 201 | if (d > 0 && !l->is_in_label(u, forward, v)) { 202 | double uw = weight(u, forward, p); 203 | queue.update(u + n*forward, static_cast(d)/uw); 204 | vertices_weight += uw; 205 | } 206 | } 207 | } 208 | 209 | // AMDS loop: iteratively remove smallest-weight vertex 210 | double r = ratio(edges, vertices_weight), best_r = r; 211 | while (!queue.empty() && r < limit) { 212 | Vertex u = queue.pop(); 213 | bool forward = u >= n; 214 | if (forward) u -= n; 215 | inads[forward][u] = false; 216 | edges -= degree[forward][u]; 217 | vertices_weight -= weight(u, forward, p); 218 | sp->get_descendants(u, v, descendants, forward); 219 | for(size_t i = 0; i < descendants.size(); ++i) { 220 | Vertex w = descendants[i]; 221 | if (!inads[!forward][w] || sp->is_covered(u, w, forward)) continue; 222 | assert(degree[!forward][w] > 0); 223 | --degree[!forward][w]; 224 | double ww = weight(w, !forward, p); 225 | if (degree[!forward][w] == 0) inads[!forward][w] = false; 226 | if (!l->is_in_label(w, !forward, v)) { 227 | if (degree[!forward][w] == 0) { 228 | queue.extract(w + n*(!forward)); 229 | vertices_weight -= ww; 230 | } else queue.update( w + n*(!forward), static_cast(degree[!forward][w])/ww); 231 | } 232 | } 233 | r = ratio(edges, vertices_weight); 234 | if (best_r < r) best_r = r; 235 | } 236 | return best_r; 237 | } 238 | }; 239 | 240 | Graph &g; // Graph 241 | Vertex n; // Number of vertices 242 | int num_threads; // number of threads 243 | SP sp; // Shortest paths data structure 244 | KHeap queue; // Queue for greedy selectoin 245 | ProxyLabeling proxy; // Proxy for labels to keep additional info 246 | std::vector density; // density[v] is a last_seen v's center graph AMDS density 247 | std::vector amds_pt; // amds_pt[i] - object to calculate AMDS for i'th thread 248 | 249 | // Add AMDS from v's center graph into the cover 250 | void increase_cover(Vertex v, AMDS &amds) { 251 | std::vector descendants; 252 | for (int forward = 0; forward < 2; ++forward) { 253 | for (Vertex u = 0; u < n; ++u) { 254 | if (!amds.is_in(u, forward)) continue; 255 | proxy.add(u, forward, v, sp.get_distance(u, v, forward)); 256 | if (forward) { 257 | sp.get_descendants(u, v, descendants); 258 | for (size_t i = 0; i < descendants.size(); ++i) { 259 | Vertex w = descendants[i]; 260 | if (amds.is_in(w, false)) { 261 | sp.set_cover(u,w); 262 | } 263 | } 264 | } 265 | } 266 | } 267 | } 268 | 269 | public: 270 | GHL(Graph &g, int num_threads) : 271 | g(g), 272 | n(g.get_n()), 273 | num_threads(num_threads), 274 | sp(g, num_threads), 275 | queue(n), 276 | proxy(n), 277 | density(n) 278 | { 279 | for (int i = 0; i < num_threads; ++i) amds_pt.push_back(AMDS(g, sp, proxy)); 280 | } 281 | 282 | // Build GHLp labels for p-norm labels 283 | void run(Labeling &labeling, float alpha = 1.1, float p = 1.0) { 284 | queue.clear(); 285 | sp.clear(); 286 | proxy.set_labeling(labeling); 287 | 288 | // Calculate initial density[v] 289 | #pragma omp parallel 290 | { 291 | #pragma omp for schedule(dynamic) 292 | for (Vertex v = 0; v < n; ++v) { 293 | double r = amds_pt[omp_get_thread_num()].run(v, p); 294 | density[v] = r; 295 | #pragma omp critical 296 | queue.update(v, 1.0/r); 297 | } 298 | } 299 | 300 | // GHLp lazy-update queue 301 | #if 0 302 | // Single-tread version 303 | AMDS &amds = amds_pt[0]; 304 | while (!queue.empty()) { 305 | Vertex v = queue.pop(); 306 | double r = amds.run(v, p, density[v]/alpha); 307 | if (r > std::numeric_limits::epsilon()) { 308 | density[v] = r; 309 | queue.update(v, 1.0/r); 310 | if (r - density[v]/alpha > std::numeric_limits::epsilon()) increase_cover(v, amds); 311 | } 312 | } 313 | #else 314 | // Multy-thread version 315 | while (!queue.empty()) { 316 | std::vector< std::pair > > top(num_threads, std::make_pair(-1.0, std::make_pair(none, -1))); 317 | for (int i = 0; i < num_threads && !queue.empty(); ++i) top[i].second = std::make_pair(queue.pop(), i); 318 | #pragma omp parallel 319 | { 320 | int i = omp_get_thread_num(); 321 | Vertex v = top[i].second.first; 322 | if (v != none) top[i].first = amds_pt[i].run(v, p, density[v]/alpha); 323 | } 324 | std::sort(top.begin(), top.end(), std::greater > >()); 325 | for (int i = 0; i < num_threads; ++i) { 326 | Vertex v = top[i].second.first; 327 | if (v != none && top[i].first > std::numeric_limits::epsilon()) { 328 | density[v] = top[i].first; 329 | queue.update(v, 1.0/density[v]); 330 | } 331 | } 332 | Vertex v = top[0].second.first; 333 | if (top[0].first - density[v]/alpha > std::numeric_limits::epsilon()) { 334 | increase_cover(v, amds_pt[top[0].second.second]); 335 | } 336 | } 337 | #endif 338 | 339 | // Order hubs in each label 340 | labeling.sort(); 341 | } 342 | }; 343 | 344 | } 345 | --------------------------------------------------------------------------------