├── samples ├── graph_example.tsv ├── query_distance_main.cc ├── construct_index_main.cc └── benchmark_main.cc ├── Makefile ├── README.md └── src ├── pruned_landmark_labeling_test.cc └── pruned_landmark_labeling.h /samples/graph_example.tsv: -------------------------------------------------------------------------------- 1 | 1 2 2 | 1 3 3 | 2 3 4 | 2 4 5 | 2 5 6 | -------------------------------------------------------------------------------- /samples/query_distance_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "pruned_landmark_labeling.h" 4 | 5 | using namespace std; 6 | 7 | int main(int argc, char **argv) { 8 | if (argc != 2) { 9 | cerr << "usage: construct_index INDEX" << endl; 10 | exit(EXIT_FAILURE); 11 | } 12 | 13 | PrunedLandmarkLabeling<> pll; 14 | if (!pll.LoadIndex(argv[1])) { 15 | cerr << "error: Load failed" << endl; 16 | exit(EXIT_FAILURE); 17 | } 18 | 19 | for (int u, v; cin >> u >> v; ) { 20 | cout << pll.QueryDistance(u, v) << endl; 21 | } 22 | exit(EXIT_SUCCESS); 23 | } 24 | -------------------------------------------------------------------------------- /samples/construct_index_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "pruned_landmark_labeling.h" 4 | 5 | using namespace std; 6 | 7 | int main(int argc, char **argv) { 8 | if (argc != 3) { 9 | cerr << "usage: construct_index GRAPH INDEX" << endl; 10 | exit(EXIT_FAILURE); 11 | } 12 | 13 | PrunedLandmarkLabeling<> pll; 14 | if (!pll.ConstructIndex(argv[1])) { 15 | cerr << "error: Load or construction failed" << endl; 16 | exit(EXIT_FAILURE); 17 | } 18 | pll.PrintStatistics(); 19 | 20 | if (!pll.StoreIndex(argv[2])) { 21 | cerr << "error: Store failed" << endl; 22 | exit(EXIT_FAILURE); 23 | } 24 | exit(EXIT_SUCCESS); 25 | } 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | CXXFLAGS = -g -Wall -Wextra -O3 3 | 4 | 5 | all: bin bin/construct_index bin/query_distance bin/benchmark 6 | 7 | bin: 8 | mkdir -p bin 9 | 10 | bin/construct_index: samples/construct_index_main.cc src/pruned_landmark_labeling.h 11 | $(CXX) $(CXXFLAGS) -Isrc -o $@ $^ 12 | 13 | bin/query_distance: samples/query_distance_main.cc src/pruned_landmark_labeling.h 14 | $(CXX) $(CXXFLAGS) -Isrc -o $@ $^ 15 | 16 | bin/benchmark: samples/benchmark_main.cc src/pruned_landmark_labeling.h 17 | $(CXX) $(CXXFLAGS) -Isrc -o $@ $^ 18 | 19 | bin/pruned_landmark_labeling_test: src/pruned_landmark_labeling_test.cc src/pruned_landmark_labeling.h 20 | $(CXX) $(CXXFLAGS) -lgtest -lgtest_main -o $@ $^ 21 | 22 | 23 | 24 | .PHONY: test clean 25 | 26 | test: bin/pruned_landmark_labeling_test 27 | bin/pruned_landmark_labeling_test 28 | 29 | clean: 30 | rm -rf bin -------------------------------------------------------------------------------- /samples/benchmark_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "pruned_landmark_labeling.h" 6 | 7 | using namespace std; 8 | 9 | const int kNumQueries = 1000000; 10 | 11 | double GetCurrentTimeSec() { 12 | struct timeval tv; 13 | gettimeofday(&tv, NULL); 14 | return tv.tv_sec + tv.tv_usec * 1e-6; 15 | } 16 | 17 | int main(int argc, char **argv) { 18 | if (argc != 2) { 19 | cerr << "usage: construct_index GRAPH" << endl; 20 | } 21 | 22 | PrunedLandmarkLabeling<0> pll; 23 | if (!pll.ConstructIndex(argv[1])) { 24 | cerr << "error: Load or construction failed" << endl; 25 | exit(EXIT_FAILURE); 26 | } 27 | pll.PrintStatistics(); 28 | 29 | vector vs(kNumQueries), ws(kNumQueries); 30 | for (int i = 0; i < kNumQueries; ++i) { 31 | vs[i] = rand() % pll.GetNumVertices(); 32 | ws[i] = rand() % pll.GetNumVertices(); 33 | } 34 | 35 | double time_start = GetCurrentTimeSec(); 36 | for (int i = 0; i < kNumQueries; ++i) { 37 | pll.QueryDistance(vs[i], ws[i]); 38 | } 39 | double elapsed_time = GetCurrentTimeSec() - time_start; 40 | cout << "average query time: " 41 | << elapsed_time / kNumQueries * 1E6 42 | << " microseconds" << endl; 43 | 44 | exit(EXIT_SUCCESS); 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pruned Landmark Labeling 2 | ======================== 3 | 4 | Pruned landmark labeling is a new shortest-path distance querying algorithm for real-world graphs, such as social networks, web graphs, biological networks and computer networks. 5 | 6 | ## Advantages 7 | The algorithm has the following advantages (for details, please see our paper): 8 | 9 | * **Fast** --- it answers distance queries in microseconds, 10 | * **Scalable** --- it can be applied to networks with hundreds of millions of edges, 11 | * **Exact** --- unlike approximate methods, it always answers exactly correct distance, and 12 | * **Almost Parameter Free** --- unlike other state-of-the-art methods, it does not require any parameter tuning. 13 | 14 | Moreover, this implementation is: 15 | 16 | * **Easy to Use** --- by copying only one header file to your project, you can start using the index. 17 | 18 | ## Usage 19 | Given a graph, it first constructs an index. Then, using the index, it can quickly answer distance between two vertices. 20 | 21 | ### From CUI Interface 22 | 23 | $ make 24 | $ bin/construct_index samples/graph_example.tsv index_file 25 | $ bin/query_distance index_file <<< "1 4" 26 | 2 27 | 28 | * Execute `make` to build programs. 29 | * Execute `bin/construct_index` to construct an index from a graph. 30 | * Execute `bin/query_distance` and write pairs of vertices to STDIN to query distance between pairs of vertices. 31 | 32 | 33 | 34 | ### From Your Program 35 | 36 | PrunedLandmarkLabeling<> pll; 37 | pll.ConstructIndex(edge_list); 38 | cout << pll.QueryDistance(1, 4) << endl; 39 | 40 | * Call `ConstructIndex` to construct an index from a graph (an edge list or a file). 41 | * Call `QueryDistance` to query distance between two vertices. 42 | * Call `LoadIndex` and `StoreIndex` to load and store an index. 43 | 44 | For further information, please see `pruned_landmark_labeling.h`, samples and tests. 45 | 46 | ### Details 47 | 48 | * In a graph file, each line should contain two integers describing an edge (see `samples/graph_example.tsv`). 49 | * Vertices should be described by integers starting from zero. 50 | * Program `bin/query_distance` reads pairs of vertices until EOF, thus you can use it to process multiple pairs of vertices at once. 51 | * Execute `make test` to run tests (*google-gtest* is required). 52 | 53 | ## References 54 | 55 | * Takuya Akiba, Yoichi Iwata, and Yuichi Yoshida, **[Fast Exact Shortest-Path Distance Queries on Large Networks by Pruned Landmark Labeling](http://www-imai.is.s.u-tokyo.ac.jp/~takiba/papers/sigmod13_pll.pdf)**. 56 | In *SIGMOD 2013*, to appear. 57 | -------------------------------------------------------------------------------- /src/pruned_landmark_labeling_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pruned_landmark_labeling.h" 3 | 4 | using namespace std; 5 | using testing::Types; 6 | 7 | typedef Types< 8 | PrunedLandmarkLabeling<0>, 9 | PrunedLandmarkLabeling<1>, 10 | PrunedLandmarkLabeling<10>, 11 | PrunedLandmarkLabeling<50> 12 | > PllTypes; 13 | 14 | template class PllTest : public testing::Test {}; 15 | TYPED_TEST_CASE(PllTest, PllTypes); 16 | 17 | void BFS(const vector > &es, int s, vector *distance) { 18 | int V = s + 1; 19 | for (size_t i = 0; i < es.size(); ++i) { 20 | V = max(V, max(es[i].first, es[i].second) + 1); 21 | } 22 | vector > adj(V); 23 | for (size_t i = 0; i < es.size(); ++i) { 24 | int v = es[i].first, w = es[i].second; 25 | adj[v].push_back(w); 26 | adj[w].push_back(v); 27 | } 28 | distance->clear(); 29 | distance->resize(V, INT_MAX); 30 | 31 | queue que; 32 | que.push(s); 33 | distance->at(s) = 0; 34 | while (!que.empty()) { 35 | int v = que.front(); 36 | que.pop(); 37 | for (size_t i = 0; i < adj[v].size(); ++i) { 38 | int w = adj[v][i]; 39 | if (distance->at(w) == INT_MAX) { 40 | que.push(w); 41 | distance->at(w) = distance->at(v) + 1; 42 | } 43 | } 44 | } 45 | } 46 | 47 | template 48 | void Test(int V, const vector > &es) { 49 | TypeParam pll1; 50 | ASSERT_TRUE(pll1.ConstructIndex(es)); 51 | 52 | ostringstream oss1; 53 | ASSERT_TRUE(pll1.StoreIndex(oss1)); 54 | 55 | TypeParam pll2; 56 | istringstream iss1(oss1.str()); 57 | ASSERT_TRUE(pll2.LoadIndex(iss1)); 58 | 59 | ostringstream oss2; 60 | ASSERT_TRUE(pll2.StoreIndex(oss2)); 61 | ASSERT_EQ(oss1.str().length(), oss2.str().length()); 62 | 63 | for (int v = 0; v < V; ++v) { 64 | vector ds; 65 | BFS(es, v, &ds); 66 | ds.resize(V, INT_MAX); 67 | for (int w = 0; w < V; ++w) { 68 | ASSERT_EQ(ds[w], pll1.QueryDistance(v, w)) << v << " -> " << w; 69 | ASSERT_EQ(ds[w], pll2.QueryDistance(v, w)) << v << " -> " << w; 70 | } 71 | } 72 | } 73 | 74 | // Two vertices (0 -- 1) 75 | TYPED_TEST(PllTest, Trivial) { 76 | vector > es; 77 | es.push_back(make_pair(0, 1)); 78 | Test(2, es); 79 | } 80 | 81 | // Path (0 -- 1 -- 2 --...-- |L-2| -- |L-1|) 82 | TYPED_TEST(PllTest, Path) { 83 | const int L = 30; 84 | vector > es; 85 | for (int i = 0; i + 1 < L; ++i) { 86 | es.push_back(make_pair(i, i + 1)); 87 | } 88 | Test(L, es); 89 | } 90 | 91 | // Circle (0 -- 1 -- 2 --...-- |L-2| -- |L-1| -- 0) 92 | TYPED_TEST(PllTest, Ring) { 93 | const int L = 30; 94 | vector > es; 95 | for (int i = 0; i < L; ++i) { 96 | es.push_back(make_pair(i, (i + 1) % L)); 97 | } 98 | Test(L, es); 99 | } 100 | 101 | // Almost empty (for testing disconnected pairs) 102 | TYPED_TEST(PllTest, AlmostEmpty) { 103 | vector > es; 104 | es.push_back(make_pair(0, 3)); 105 | es.push_back(make_pair(3, 6)); 106 | es.push_back(make_pair(6, 0)); 107 | Test(10, es); 108 | } 109 | 110 | // Erdos-Renyi random graph 111 | TYPED_TEST(PllTest, ErdosRenyiGraph) { 112 | const int N = 50; 113 | vector > es; 114 | for (int v = 0; v < N; ++v) { 115 | for (int w = v + 1; w < N; ++w) { 116 | double x = rand() / double(RAND_MAX); 117 | if (x < 0.5) { 118 | es.push_back(make_pair(v, w)); 119 | } 120 | } 121 | } 122 | Test(N, es); 123 | } 124 | 125 | -------------------------------------------------------------------------------- /src/pruned_landmark_labeling.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013, Takuya Akiba 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Takuya Akiba nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | #ifndef PRUNED_LANDMARK_LABELING_H_ 31 | #define PRUNED_LANDMARK_LABELING_H_ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | // 50 | // NOTE: Currently only unweighted and undirected graphs are supported. 51 | // 52 | 53 | template 54 | class PrunedLandmarkLabeling { 55 | public: 56 | // Constructs an index from a graph, given as a list of edges. 57 | // Vertices should be described by numbers starting from zero. 58 | // Returns |true| when successful. 59 | bool ConstructIndex(const std::vector > &es); 60 | bool ConstructIndex(std::istream &ifs); 61 | bool ConstructIndex(const char *filename); 62 | 63 | // Returns distance vetween vertices |v| and |w| if they are connected. 64 | // Otherwise, returns |INT_MAX|. 65 | inline int QueryDistance(int v, int w); 66 | 67 | // Loads an index. Returns |true| when successful. 68 | bool LoadIndex(std::istream &ifs); 69 | bool LoadIndex(const char *filename); 70 | 71 | // Stores the index. Returns |true| when successful. 72 | bool StoreIndex(std::ostream &ofs); 73 | bool StoreIndex(const char *filename); 74 | 75 | int GetNumVertices() { return num_v_; } 76 | void Free(); 77 | void PrintStatistics(); 78 | 79 | PrunedLandmarkLabeling() 80 | : num_v_(0), index_(NULL), time_load_(0), time_indexing_(0) {} 81 | virtual ~PrunedLandmarkLabeling() { 82 | Free(); 83 | } 84 | 85 | private: 86 | static const uint8_t INF8; // For unreachable pairs 87 | 88 | struct index_t { 89 | uint8_t bpspt_d[kNumBitParallelRoots]; 90 | uint64_t bpspt_s[kNumBitParallelRoots][2]; // [0]: S^{-1}, [1]: S^{0} 91 | uint32_t *spt_v; 92 | uint8_t *spt_d; 93 | } __attribute__((aligned(64))); // Aligned for cache lines 94 | 95 | int num_v_; 96 | index_t *index_; 97 | 98 | double GetCurrentTimeSec() { 99 | struct timeval tv; 100 | gettimeofday(&tv, NULL); 101 | return tv.tv_sec + tv.tv_usec * 1e-6; 102 | } 103 | 104 | // Statistics 105 | double time_load_, time_indexing_; 106 | }; 107 | 108 | template 109 | const uint8_t PrunedLandmarkLabeling::INF8 = 100; 110 | 111 | template 112 | bool PrunedLandmarkLabeling 113 | ::ConstructIndex(const char *filename) { 114 | std::ifstream ifs(filename); 115 | return ifs && ConstructIndex(ifs); 116 | } 117 | 118 | template 119 | bool PrunedLandmarkLabeling 120 | ::ConstructIndex(std::istream &ifs) { 121 | std::vector > es; 122 | for (int v, w; ifs >> v >> w; ) { 123 | es.push_back(std::make_pair(v, w)); 124 | } 125 | if (ifs.bad()) return false; 126 | ConstructIndex(es); 127 | return true; 128 | } 129 | 130 | template 131 | bool PrunedLandmarkLabeling 132 | ::ConstructIndex(const std::vector > &es) { 133 | // 134 | // Prepare the adjacency list and index space 135 | // 136 | Free(); 137 | time_load_ = -GetCurrentTimeSec(); 138 | int E = es.size(); 139 | int &V = num_v_; 140 | V = 0; 141 | for (size_t i = 0; i < es.size(); ++i) { 142 | V = std::max(V, std::max(es[i].first, es[i].second) + 1); 143 | } 144 | std::vector > adj(V); 145 | for (size_t i = 0; i < es.size(); ++i) { 146 | int v = es[i].first, w = es[i].second; 147 | adj[v].push_back(w); 148 | adj[w].push_back(v); 149 | } 150 | time_load_ += GetCurrentTimeSec(); 151 | 152 | index_ = (index_t*)memalign(64, V * sizeof(index_t)); 153 | if (index_ == NULL) { 154 | num_v_ = 0; 155 | return false; 156 | } 157 | for (int v = 0; v < V; ++v) { 158 | index_[v].spt_v = NULL; 159 | index_[v].spt_d = NULL; 160 | } 161 | 162 | // 163 | // Order vertices by decreasing order of degree 164 | // 165 | time_indexing_ = -GetCurrentTimeSec(); 166 | std::vector inv(V); // new label -> old label 167 | { 168 | // Order 169 | std::vector > deg(V); 170 | for (int v = 0; v < V; ++v) { 171 | // We add a random value here to diffuse nearby vertices 172 | deg[v] = std::make_pair(adj[v].size() + float(rand()) / RAND_MAX, v); 173 | } 174 | std::sort(deg.rbegin(), deg.rend()); 175 | for (int i = 0; i < V; ++i) inv[i] = deg[i].second; 176 | 177 | // Relabel the vertex IDs 178 | std::vector rank(V); 179 | for (int i = 0; i < V; ++i) rank[deg[i].second] = i; 180 | std::vector > new_adj(V); 181 | for (int v = 0; v < V; ++v) { 182 | for (size_t i = 0; i < adj[v].size(); ++i) { 183 | new_adj[rank[v]].push_back(rank[adj[v][i]]); 184 | } 185 | } 186 | adj.swap(new_adj); 187 | } 188 | 189 | // 190 | // Bit-parallel labeling 191 | // 192 | std::vector usd(V, false); // Used as root? (in new label) 193 | { 194 | std::vector tmp_d(V); 195 | std::vector > tmp_s(V); 196 | std::vector que(V); 197 | std::vector > sibling_es(E); 198 | std::vector > child_es(E); 199 | 200 | int r = 0; 201 | for (int i_bpspt = 0; i_bpspt < kNumBitParallelRoots; ++i_bpspt) { 202 | while (r < V && usd[r]) ++r; 203 | if (r == V) { 204 | for (int v = 0; v < V; ++v) index_[v].bpspt_d[i_bpspt] = INF8; 205 | continue; 206 | } 207 | usd[r] = true; 208 | 209 | fill(tmp_d.begin(), tmp_d.end(), INF8); 210 | fill(tmp_s.begin(), tmp_s.end(), std::make_pair(0, 0)); 211 | 212 | int que_t0 = 0, que_t1 = 0, que_h = 0; 213 | que[que_h++] = r; 214 | tmp_d[r] = 0; 215 | que_t1 = que_h; 216 | 217 | int ns = 0; 218 | std::vector vs; 219 | sort(adj[r].begin(), adj[r].end()); 220 | for (size_t i = 0; i < adj[r].size(); ++i) { 221 | int v = adj[r][i]; 222 | if (!usd[v]) { 223 | usd[v] = true; 224 | que[que_h++] = v; 225 | tmp_d[v] = 1; 226 | tmp_s[v].first = 1ULL << ns; 227 | vs.push_back(v); 228 | if (++ns == 64) break; 229 | } 230 | } 231 | 232 | for (int d = 0; que_t0 < que_h; ++d) { 233 | int num_sibling_es = 0, num_child_es = 0; 234 | 235 | for (int que_i = que_t0; que_i < que_t1; ++que_i) { 236 | int v = que[que_i]; 237 | 238 | for (size_t i = 0; i < adj[v].size(); ++i) { 239 | int tv = adj[v][i]; 240 | int td = d + 1; 241 | 242 | if (d > tmp_d[tv]); 243 | else if (d == tmp_d[tv]) { 244 | if (v < tv) { 245 | sibling_es[num_sibling_es].first = v; 246 | sibling_es[num_sibling_es].second = tv; 247 | ++num_sibling_es; 248 | } 249 | } else { 250 | if (tmp_d[tv] == INF8) { 251 | que[que_h++] = tv; 252 | tmp_d[tv] = td; 253 | } 254 | child_es[num_child_es].first = v; 255 | child_es[num_child_es].second = tv; 256 | ++num_child_es; 257 | } 258 | } 259 | } 260 | 261 | for (int i = 0; i < num_sibling_es; ++i) { 262 | int v = sibling_es[i].first, w = sibling_es[i].second; 263 | tmp_s[v].second |= tmp_s[w].first; 264 | tmp_s[w].second |= tmp_s[v].first; 265 | } 266 | for (int i = 0; i < num_child_es; ++i) { 267 | int v = child_es[i].first, c = child_es[i].second; 268 | tmp_s[c].first |= tmp_s[v].first; 269 | tmp_s[c].second |= tmp_s[v].second; 270 | } 271 | 272 | que_t0 = que_t1; 273 | que_t1 = que_h; 274 | } 275 | 276 | for (int v = 0; v < V; ++v) { 277 | index_[inv[v]].bpspt_d[i_bpspt] = tmp_d[v]; 278 | index_[inv[v]].bpspt_s[i_bpspt][0] = tmp_s[v].first; 279 | index_[inv[v]].bpspt_s[i_bpspt][1] = tmp_s[v].second & ~tmp_s[v].first; 280 | } 281 | } 282 | } 283 | 284 | // 285 | // Pruned labeling 286 | // 287 | { 288 | // Sentinel (V, INF8) is added to all the vertices 289 | std::vector, std::vector > > 290 | tmp_idx(V, make_pair(std::vector(1, V), 291 | std::vector(1, INF8))); 292 | 293 | std::vector vis(V); 294 | std::vector que(V); 295 | std::vector dst_r(V + 1, INF8); 296 | 297 | for (int r = 0; r < V; ++r) { 298 | if (usd[r]) continue; 299 | index_t &idx_r = index_[inv[r]]; 300 | const std::pair, std::vector > 301 | &tmp_idx_r = tmp_idx[r]; 302 | for (size_t i = 0; i < tmp_idx_r.first.size(); ++i) { 303 | dst_r[tmp_idx_r.first[i]] = tmp_idx_r.second[i]; 304 | } 305 | 306 | int que_t0 = 0, que_t1 = 0, que_h = 0; 307 | que[que_h++] = r; 308 | vis[r] = true; 309 | que_t1 = que_h; 310 | 311 | for (int d = 0; que_t0 < que_h; ++d) { 312 | for (int que_i = que_t0; que_i < que_t1; ++que_i) { 313 | int v = que[que_i]; 314 | std::pair, std::vector > 315 | &tmp_idx_v = tmp_idx[v]; 316 | index_t &idx_v = index_[inv[v]]; 317 | 318 | // Prefetch 319 | _mm_prefetch(&idx_v.bpspt_d[0], _MM_HINT_T0); 320 | _mm_prefetch(&idx_v.bpspt_s[0][0], _MM_HINT_T0); 321 | _mm_prefetch(&tmp_idx_v.first[0], _MM_HINT_T0); 322 | _mm_prefetch(&tmp_idx_v.second[0], _MM_HINT_T0); 323 | 324 | // Prune? 325 | if (usd[v]) continue; 326 | for (int i = 0; i < kNumBitParallelRoots; ++i) { 327 | int td = idx_r.bpspt_d[i] + idx_v.bpspt_d[i]; 328 | if (td - 2 <= d) { 329 | td += 330 | (idx_r.bpspt_s[i][0] & idx_v.bpspt_s[i][0]) ? -2 : 331 | ((idx_r.bpspt_s[i][0] & idx_v.bpspt_s[i][1]) | 332 | (idx_r.bpspt_s[i][1] & idx_v.bpspt_s[i][0])) 333 | ? -1 : 0; 334 | if (td <= d) goto pruned; 335 | } 336 | } 337 | for (size_t i = 0; i < tmp_idx_v.first.size(); ++i) { 338 | int w = tmp_idx_v.first[i]; 339 | int td = tmp_idx_v.second[i] + dst_r[w]; 340 | if (td <= d) goto pruned; 341 | } 342 | 343 | // Traverse 344 | tmp_idx_v.first .back() = r; 345 | tmp_idx_v.second.back() = d; 346 | tmp_idx_v.first .push_back(V); 347 | tmp_idx_v.second.push_back(INF8); 348 | for (size_t i = 0; i < adj[v].size(); ++i) { 349 | int w = adj[v][i]; 350 | if (!vis[w]) { 351 | que[que_h++] = w; 352 | vis[w] = true; 353 | } 354 | } 355 | pruned: 356 | {} 357 | } 358 | 359 | que_t0 = que_t1; 360 | que_t1 = que_h; 361 | } 362 | 363 | for (int i = 0; i < que_h; ++i) vis[que[i]] = false; 364 | for (size_t i = 0; i < tmp_idx_r.first.size(); ++i) { 365 | dst_r[tmp_idx_r.first[i]] = INF8; 366 | } 367 | usd[r] = true; 368 | } 369 | 370 | for (int v = 0; v < V; ++v) { 371 | int k = tmp_idx[v].first.size(); 372 | index_[inv[v]].spt_v = (uint32_t*)memalign(64, k * sizeof(uint32_t)); 373 | index_[inv[v]].spt_d = (uint8_t *)memalign(64, k * sizeof(uint8_t )); 374 | if (!index_[inv[v]].spt_v || !index_[inv[v]].spt_d) { 375 | Free(); 376 | return false; 377 | } 378 | for (int i = 0; i < k; ++i) index_[inv[v]].spt_v[i] = tmp_idx[v].first[i]; 379 | for (int i = 0; i < k; ++i) index_[inv[v]].spt_d[i] = tmp_idx[v].second[i]; 380 | tmp_idx[v].first.clear(); 381 | tmp_idx[v].second.clear(); 382 | } 383 | } 384 | 385 | time_indexing_ += GetCurrentTimeSec(); 386 | return true; 387 | } 388 | 389 | template 390 | int PrunedLandmarkLabeling 391 | ::QueryDistance(int v, int w) { 392 | if (v >= num_v_ || w >= num_v_) return v == w ? 0 : INT_MAX; 393 | 394 | const index_t &idx_v = index_[v]; 395 | const index_t &idx_w = index_[w]; 396 | int d = INF8; 397 | 398 | _mm_prefetch(&idx_v.spt_v[0], _MM_HINT_T0); 399 | _mm_prefetch(&idx_w.spt_v[0], _MM_HINT_T0); 400 | _mm_prefetch(&idx_v.spt_d[0], _MM_HINT_T0); 401 | _mm_prefetch(&idx_w.spt_d[0], _MM_HINT_T0); 402 | 403 | for (int i = 0; i < kNumBitParallelRoots; ++i) { 404 | int td = idx_v.bpspt_d[i] + idx_w.bpspt_d[i]; 405 | if (td - 2 <= d) { 406 | td += 407 | (idx_v.bpspt_s[i][0] & idx_w.bpspt_s[i][0]) ? -2 : 408 | ((idx_v.bpspt_s[i][0] & idx_w.bpspt_s[i][1]) | (idx_v.bpspt_s[i][1] & idx_w.bpspt_s[i][0])) 409 | ? -1 : 0; 410 | 411 | if (td < d) d = td; 412 | } 413 | } 414 | for (int i1 = 0, i2 = 0; ; ) { 415 | int v1 = idx_v.spt_v[i1], v2 = idx_w.spt_v[i2]; 416 | if (v1 == v2) { 417 | if (v1 == num_v_) break; // Sentinel 418 | int td = idx_v.spt_d[i1] + idx_w.spt_d[i2]; 419 | if (td < d) d = td; 420 | ++i1; 421 | ++i2; 422 | } else { 423 | i1 += v1 < v2 ? 1 : 0; 424 | i2 += v1 > v2 ? 1 : 0; 425 | } 426 | } 427 | 428 | if (d >= INF8 - 2) d = INT_MAX; 429 | return d; 430 | } 431 | 432 | template 433 | bool PrunedLandmarkLabeling 434 | ::LoadIndex(const char *filename) { 435 | std::ifstream ifs(filename); 436 | return ifs && LoadIndex(ifs); 437 | } 438 | 439 | template 440 | bool PrunedLandmarkLabeling 441 | ::LoadIndex(std::istream &ifs) { 442 | Free(); 443 | 444 | int32_t num_v, num_bpr; 445 | ifs.read((char*)&num_v, sizeof(num_v)); 446 | ifs.read((char*)&num_bpr, sizeof(num_bpr)); 447 | num_v_ = num_v; 448 | if (ifs.bad() || kNumBitParallelRoots != num_bpr) { 449 | num_v_ = 0; 450 | return false; 451 | } 452 | 453 | index_ = (index_t*)memalign(64, num_v * sizeof(index_t)); 454 | if (index_ == NULL) { 455 | num_v_ = 0; 456 | return false; 457 | } 458 | for (int v = 0; v < num_v_; ++v) { 459 | index_[v].spt_v = NULL; 460 | index_[v].spt_d = NULL; 461 | } 462 | 463 | for (int v = 0; v < num_v_; ++v) { 464 | index_t &idx = index_[v]; 465 | 466 | for (int i = 0; i < kNumBitParallelRoots; ++i) { 467 | ifs.read((char*)&idx.bpspt_d[i] , sizeof(idx.bpspt_d[i] )); 468 | ifs.read((char*)&idx.bpspt_s[i][0], sizeof(idx.bpspt_s[i][0])); 469 | ifs.read((char*)&idx.bpspt_s[i][1], sizeof(idx.bpspt_s[i][1])); 470 | } 471 | 472 | int32_t s; 473 | ifs.read((char*)&s, sizeof(s)); 474 | if (ifs.bad()) { 475 | Free(); 476 | return false; 477 | } 478 | 479 | idx.spt_v = (uint32_t*)memalign(64, s * sizeof(uint32_t)); 480 | idx.spt_d = (uint8_t *)memalign(64, s * sizeof(uint8_t )); 481 | if (!idx.spt_v || !idx.spt_d) { 482 | Free(); 483 | return false; 484 | } 485 | 486 | for (int i = 0; i < s; ++i) { 487 | ifs.read((char*)&idx.spt_v[i], sizeof(idx.spt_v[i])); 488 | ifs.read((char*)&idx.spt_d[i], sizeof(idx.spt_d[i])); 489 | } 490 | } 491 | 492 | return ifs.good(); 493 | } 494 | 495 | template 496 | bool PrunedLandmarkLabeling 497 | ::StoreIndex(const char *filename) { 498 | std::ofstream ofs(filename); 499 | return ofs && StoreIndex(ofs); 500 | } 501 | 502 | template 503 | bool PrunedLandmarkLabeling 504 | ::StoreIndex(std::ostream &ofs) { 505 | uint32_t num_v = num_v_, num_bpr = kNumBitParallelRoots; 506 | ofs.write((const char*)&num_v, sizeof(num_v)); 507 | ofs.write((const char*)&num_bpr, sizeof(num_bpr)); 508 | 509 | for (int v = 0; v < num_v_; ++v) { 510 | index_t &idx = index_[v]; 511 | 512 | for (int i = 0; i < kNumBitParallelRoots; ++i) { 513 | int8_t d = idx.bpspt_d[i]; 514 | uint64_t a = idx.bpspt_s[i][0]; 515 | uint64_t b = idx.bpspt_s[i][1]; 516 | ofs.write((const char*)&d, sizeof(d)); 517 | ofs.write((const char*)&a, sizeof(a)); 518 | ofs.write((const char*)&b, sizeof(b)); 519 | } 520 | 521 | int32_t s; 522 | for (s = 1; idx.spt_v[s - 1] != num_v; ++s) continue; // Find the sentinel 523 | ofs.write((const char*)&s, sizeof(s)); 524 | for (int i = 0; i < s; ++i) { 525 | int32_t l = idx.spt_v[i]; 526 | int8_t d = idx.spt_d[i]; 527 | ofs.write((const char*)&l, sizeof(l)); 528 | ofs.write((const char*)&d, sizeof(d)); 529 | } 530 | } 531 | 532 | return ofs.good(); 533 | } 534 | 535 | template 536 | void PrunedLandmarkLabeling 537 | ::Free() { 538 | for (int v = 0; v < num_v_; ++v) { 539 | free(index_[v].spt_v); 540 | free(index_[v].spt_d); 541 | } 542 | free(index_); 543 | index_ = NULL; 544 | num_v_ = 0; 545 | } 546 | 547 | template 548 | void PrunedLandmarkLabeling 549 | ::PrintStatistics() { 550 | std::cout << "load time: " << time_load_ << " seconds" << std::endl; 551 | std::cout << "indexing time: " << time_indexing_ << " seconds" << std::endl; 552 | 553 | double s = 0.0; 554 | for (int v = 0; v < num_v_; ++v) { 555 | for (int i = 0; index_[v].spt_v[i] != uint32_t(num_v_); ++i) { 556 | ++s; 557 | } 558 | } 559 | s /= num_v_; 560 | std::cout << "bit-parallel label size: " << kNumBitParallelRoots << std::endl; 561 | std::cout << "average normal label size: " << s << std::endl; 562 | } 563 | 564 | #endif // PRUNED_LANDMARK_LABELING_H_ 565 | --------------------------------------------------------------------------------