00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "absl/synchronization/internal/graphcycles.h"
00016
00017 #include <map>
00018 #include <random>
00019 #include <unordered_set>
00020 #include <utility>
00021 #include <vector>
00022
00023 #include "gtest/gtest.h"
00024 #include "absl/base/internal/raw_logging.h"
00025 #include "absl/base/macros.h"
00026
00027 namespace absl {
00028 namespace synchronization_internal {
00029
00030
00031
00032
00033 using Nodes = std::vector<int>;
00034 struct Edge {
00035 int from;
00036 int to;
00037 };
00038 using Edges = std::vector<Edge>;
00039 using RandomEngine = std::mt19937_64;
00040
00041
00042 typedef std::map<int, GraphId> IdMap;
00043 static GraphId Get(const IdMap& id, int num) {
00044 auto iter = id.find(num);
00045 return (iter == id.end()) ? InvalidGraphId() : iter->second;
00046 }
00047
00048
00049 static bool IsReachable(Edges *edges, int from, int to,
00050 std::unordered_set<int> *seen) {
00051 seen->insert(from);
00052 if (from == to) return true;
00053 for (const auto &edge : *edges) {
00054 if (edge.from == from) {
00055 if (edge.to == to) {
00056 return true;
00057 } else if (seen->find(edge.to) == seen->end() &&
00058 IsReachable(edges, edge.to, to, seen)) {
00059 return true;
00060 }
00061 }
00062 }
00063 return false;
00064 }
00065
00066 static void PrintEdges(Edges *edges) {
00067 ABSL_RAW_LOG(INFO, "EDGES (%zu)", edges->size());
00068 for (const auto &edge : *edges) {
00069 int a = edge.from;
00070 int b = edge.to;
00071 ABSL_RAW_LOG(INFO, "%d %d", a, b);
00072 }
00073 ABSL_RAW_LOG(INFO, "---");
00074 }
00075
00076 static void PrintGCEdges(Nodes *nodes, const IdMap &id, GraphCycles *gc) {
00077 ABSL_RAW_LOG(INFO, "GC EDGES");
00078 for (int a : *nodes) {
00079 for (int b : *nodes) {
00080 if (gc->HasEdge(Get(id, a), Get(id, b))) {
00081 ABSL_RAW_LOG(INFO, "%d %d", a, b);
00082 }
00083 }
00084 }
00085 ABSL_RAW_LOG(INFO, "---");
00086 }
00087
00088 static void PrintTransitiveClosure(Nodes *nodes, Edges *edges) {
00089 ABSL_RAW_LOG(INFO, "Transitive closure");
00090 for (int a : *nodes) {
00091 for (int b : *nodes) {
00092 std::unordered_set<int> seen;
00093 if (IsReachable(edges, a, b, &seen)) {
00094 ABSL_RAW_LOG(INFO, "%d %d", a, b);
00095 }
00096 }
00097 }
00098 ABSL_RAW_LOG(INFO, "---");
00099 }
00100
00101 static void PrintGCTransitiveClosure(Nodes *nodes, const IdMap &id,
00102 GraphCycles *gc) {
00103 ABSL_RAW_LOG(INFO, "GC Transitive closure");
00104 for (int a : *nodes) {
00105 for (int b : *nodes) {
00106 if (gc->IsReachable(Get(id, a), Get(id, b))) {
00107 ABSL_RAW_LOG(INFO, "%d %d", a, b);
00108 }
00109 }
00110 }
00111 ABSL_RAW_LOG(INFO, "---");
00112 }
00113
00114 static void CheckTransitiveClosure(Nodes *nodes, Edges *edges, const IdMap &id,
00115 GraphCycles *gc) {
00116 std::unordered_set<int> seen;
00117 for (const auto &a : *nodes) {
00118 for (const auto &b : *nodes) {
00119 seen.clear();
00120 bool gc_reachable = gc->IsReachable(Get(id, a), Get(id, b));
00121 bool reachable = IsReachable(edges, a, b, &seen);
00122 if (gc_reachable != reachable) {
00123 PrintEdges(edges);
00124 PrintGCEdges(nodes, id, gc);
00125 PrintTransitiveClosure(nodes, edges);
00126 PrintGCTransitiveClosure(nodes, id, gc);
00127 ABSL_RAW_LOG(FATAL, "gc_reachable %s reachable %s a %d b %d",
00128 gc_reachable ? "true" : "false",
00129 reachable ? "true" : "false", a, b);
00130 }
00131 }
00132 }
00133 }
00134
00135 static void CheckEdges(Nodes *nodes, Edges *edges, const IdMap &id,
00136 GraphCycles *gc) {
00137 int count = 0;
00138 for (const auto &edge : *edges) {
00139 int a = edge.from;
00140 int b = edge.to;
00141 if (!gc->HasEdge(Get(id, a), Get(id, b))) {
00142 PrintEdges(edges);
00143 PrintGCEdges(nodes, id, gc);
00144 ABSL_RAW_LOG(FATAL, "!gc->HasEdge(%d, %d)", a, b);
00145 }
00146 }
00147 for (const auto &a : *nodes) {
00148 for (const auto &b : *nodes) {
00149 if (gc->HasEdge(Get(id, a), Get(id, b))) {
00150 count++;
00151 }
00152 }
00153 }
00154 if (count != edges->size()) {
00155 PrintEdges(edges);
00156 PrintGCEdges(nodes, id, gc);
00157 ABSL_RAW_LOG(FATAL, "edges->size() %zu count %d", edges->size(), count);
00158 }
00159 }
00160
00161 static void CheckInvariants(const GraphCycles &gc) {
00162 if (ABSL_PREDICT_FALSE(!gc.CheckInvariants()))
00163 ABSL_RAW_LOG(FATAL, "CheckInvariants");
00164 }
00165
00166
00167
00168 static int RandomNode(RandomEngine* rng, Nodes *nodes) {
00169 std::uniform_int_distribution<int> uniform(0, nodes->size()-1);
00170 return uniform(*rng);
00171 }
00172
00173
00174
00175 static int RandomEdge(RandomEngine* rng, Edges *edges) {
00176 std::uniform_int_distribution<int> uniform(0, edges->size()-1);
00177 return uniform(*rng);
00178 }
00179
00180
00181 static int EdgeIndex(Edges *edges, int from, int to) {
00182 int i = 0;
00183 while (i != edges->size() &&
00184 ((*edges)[i].from != from || (*edges)[i].to != to)) {
00185 i++;
00186 }
00187 return i == edges->size()? -1 : i;
00188 }
00189
00190 TEST(GraphCycles, RandomizedTest) {
00191 int next_node = 0;
00192 Nodes nodes;
00193 Edges edges;
00194 IdMap id;
00195 GraphCycles graph_cycles;
00196 static const int kMaxNodes = 7;
00197 static const int kDataOffset = 17;
00198 int n = 100000;
00199 int op = 0;
00200 RandomEngine rng(testing::UnitTest::GetInstance()->random_seed());
00201 std::uniform_int_distribution<int> uniform(0, 5);
00202
00203 auto ptr = [](intptr_t i) {
00204 return reinterpret_cast<void*>(i + kDataOffset);
00205 };
00206
00207 for (int iter = 0; iter != n; iter++) {
00208 for (const auto &node : nodes) {
00209 ASSERT_EQ(graph_cycles.Ptr(Get(id, node)), ptr(node)) << " node " << node;
00210 }
00211 CheckEdges(&nodes, &edges, id, &graph_cycles);
00212 CheckTransitiveClosure(&nodes, &edges, id, &graph_cycles);
00213 op = uniform(rng);
00214 switch (op) {
00215 case 0:
00216 if (nodes.size() < kMaxNodes) {
00217 int new_node = next_node++;
00218 GraphId new_gnode = graph_cycles.GetId(ptr(new_node));
00219 ASSERT_NE(new_gnode, InvalidGraphId());
00220 id[new_node] = new_gnode;
00221 ASSERT_EQ(ptr(new_node), graph_cycles.Ptr(new_gnode));
00222 nodes.push_back(new_node);
00223 }
00224 break;
00225
00226 case 1:
00227 if (nodes.size() > 0) {
00228 int node_index = RandomNode(&rng, &nodes);
00229 int node = nodes[node_index];
00230 nodes[node_index] = nodes.back();
00231 nodes.pop_back();
00232 graph_cycles.RemoveNode(ptr(node));
00233 ASSERT_EQ(graph_cycles.Ptr(Get(id, node)), nullptr);
00234 id.erase(node);
00235 int i = 0;
00236 while (i != edges.size()) {
00237 if (edges[i].from == node || edges[i].to == node) {
00238 edges[i] = edges.back();
00239 edges.pop_back();
00240 } else {
00241 i++;
00242 }
00243 }
00244 }
00245 break;
00246
00247 case 2:
00248 if (nodes.size() > 0) {
00249 int from = RandomNode(&rng, &nodes);
00250 int to = RandomNode(&rng, &nodes);
00251 if (EdgeIndex(&edges, nodes[from], nodes[to]) == -1) {
00252 if (graph_cycles.InsertEdge(id[nodes[from]], id[nodes[to]])) {
00253 Edge new_edge;
00254 new_edge.from = nodes[from];
00255 new_edge.to = nodes[to];
00256 edges.push_back(new_edge);
00257 } else {
00258 std::unordered_set<int> seen;
00259 ASSERT_TRUE(IsReachable(&edges, nodes[to], nodes[from], &seen))
00260 << "Edge " << nodes[to] << "->" << nodes[from];
00261 }
00262 }
00263 }
00264 break;
00265
00266 case 3:
00267 if (edges.size() > 0) {
00268 int i = RandomEdge(&rng, &edges);
00269 int from = edges[i].from;
00270 int to = edges[i].to;
00271 ASSERT_EQ(i, EdgeIndex(&edges, from, to));
00272 edges[i] = edges.back();
00273 edges.pop_back();
00274 ASSERT_EQ(-1, EdgeIndex(&edges, from, to));
00275 graph_cycles.RemoveEdge(id[from], id[to]);
00276 }
00277 break;
00278
00279 case 4:
00280 if (nodes.size() > 0) {
00281 int from = RandomNode(&rng, &nodes);
00282 int to = RandomNode(&rng, &nodes);
00283 GraphId path[2*kMaxNodes];
00284 int path_len = graph_cycles.FindPath(id[nodes[from]], id[nodes[to]],
00285 ABSL_ARRAYSIZE(path), path);
00286 std::unordered_set<int> seen;
00287 bool reachable = IsReachable(&edges, nodes[from], nodes[to], &seen);
00288 bool gc_reachable =
00289 graph_cycles.IsReachable(Get(id, nodes[from]), Get(id, nodes[to]));
00290 ASSERT_EQ(path_len != 0, reachable);
00291 ASSERT_EQ(path_len != 0, gc_reachable);
00292
00293
00294
00295 ASSERT_LE(path_len, kMaxNodes + 1);
00296 if (path_len != 0) {
00297 ASSERT_EQ(id[nodes[from]], path[0]);
00298 ASSERT_EQ(id[nodes[to]], path[path_len-1]);
00299 for (int i = 1; i < path_len; i++) {
00300 ASSERT_TRUE(graph_cycles.HasEdge(path[i-1], path[i]));
00301 }
00302 }
00303 }
00304 break;
00305
00306 case 5:
00307 CheckInvariants(graph_cycles);
00308 break;
00309
00310 default:
00311 ABSL_RAW_LOG(FATAL, "op %d", op);
00312 }
00313
00314
00315 std::bernoulli_distribution one_in_1024(1.0 / 1024);
00316 if (one_in_1024(rng)) {
00317 CheckEdges(&nodes, &edges, id, &graph_cycles);
00318 CheckTransitiveClosure(&nodes, &edges, id, &graph_cycles);
00319 for (int i = 0; i != 256; i++) {
00320 int new_node = next_node++;
00321 GraphId new_gnode = graph_cycles.GetId(ptr(new_node));
00322 ASSERT_NE(InvalidGraphId(), new_gnode);
00323 id[new_node] = new_gnode;
00324 ASSERT_EQ(ptr(new_node), graph_cycles.Ptr(new_gnode));
00325 for (const auto &node : nodes) {
00326 ASSERT_NE(node, new_node);
00327 }
00328 nodes.push_back(new_node);
00329 }
00330 for (int i = 0; i != 256; i++) {
00331 ASSERT_GT(nodes.size(), 0);
00332 int node_index = RandomNode(&rng, &nodes);
00333 int node = nodes[node_index];
00334 nodes[node_index] = nodes.back();
00335 nodes.pop_back();
00336 graph_cycles.RemoveNode(ptr(node));
00337 id.erase(node);
00338 int j = 0;
00339 while (j != edges.size()) {
00340 if (edges[j].from == node || edges[j].to == node) {
00341 edges[j] = edges.back();
00342 edges.pop_back();
00343 } else {
00344 j++;
00345 }
00346 }
00347 }
00348 CheckInvariants(graph_cycles);
00349 }
00350 }
00351 }
00352
00353 class GraphCyclesTest : public ::testing::Test {
00354 public:
00355 IdMap id_;
00356 GraphCycles g_;
00357
00358 static void* Ptr(int i) {
00359 return reinterpret_cast<void*>(static_cast<uintptr_t>(i));
00360 }
00361
00362 static int Num(void* ptr) {
00363 return static_cast<int>(reinterpret_cast<uintptr_t>(ptr));
00364 }
00365
00366
00367 GraphCyclesTest() {
00368 for (int i = 0; i < 100; i++) {
00369 id_[i] = g_.GetId(Ptr(i));
00370 }
00371 CheckInvariants(g_);
00372 }
00373
00374 bool AddEdge(int x, int y) {
00375 return g_.InsertEdge(Get(id_, x), Get(id_, y));
00376 }
00377
00378 void AddMultiples() {
00379
00380 for (int x = 1; x < 25; x++) {
00381 EXPECT_TRUE(AddEdge(x, 2*x)) << x;
00382 EXPECT_TRUE(AddEdge(x, 3*x)) << x;
00383 }
00384 CheckInvariants(g_);
00385 }
00386
00387 std::string Path(int x, int y) {
00388 GraphId path[5];
00389 int np = g_.FindPath(Get(id_, x), Get(id_, y), ABSL_ARRAYSIZE(path), path);
00390 std::string result;
00391 for (int i = 0; i < np; i++) {
00392 if (i >= ABSL_ARRAYSIZE(path)) {
00393 result += " ...";
00394 break;
00395 }
00396 if (!result.empty()) result.push_back(' ');
00397 char buf[20];
00398 snprintf(buf, sizeof(buf), "%d", Num(g_.Ptr(path[i])));
00399 result += buf;
00400 }
00401 return result;
00402 }
00403 };
00404
00405 TEST_F(GraphCyclesTest, NoCycle) {
00406 AddMultiples();
00407 CheckInvariants(g_);
00408 }
00409
00410 TEST_F(GraphCyclesTest, SimpleCycle) {
00411 AddMultiples();
00412 EXPECT_FALSE(AddEdge(8, 4));
00413 EXPECT_EQ("4 8", Path(4, 8));
00414 CheckInvariants(g_);
00415 }
00416
00417 TEST_F(GraphCyclesTest, IndirectCycle) {
00418 AddMultiples();
00419 EXPECT_TRUE(AddEdge(16, 9));
00420 CheckInvariants(g_);
00421 EXPECT_FALSE(AddEdge(9, 2));
00422 EXPECT_EQ("2 4 8 16 9", Path(2, 9));
00423 CheckInvariants(g_);
00424 }
00425
00426 TEST_F(GraphCyclesTest, LongPath) {
00427 ASSERT_TRUE(AddEdge(2, 4));
00428 ASSERT_TRUE(AddEdge(4, 6));
00429 ASSERT_TRUE(AddEdge(6, 8));
00430 ASSERT_TRUE(AddEdge(8, 10));
00431 ASSERT_TRUE(AddEdge(10, 12));
00432 ASSERT_FALSE(AddEdge(12, 2));
00433 EXPECT_EQ("2 4 6 8 10 ...", Path(2, 12));
00434 CheckInvariants(g_);
00435 }
00436
00437 TEST_F(GraphCyclesTest, RemoveNode) {
00438 ASSERT_TRUE(AddEdge(1, 2));
00439 ASSERT_TRUE(AddEdge(2, 3));
00440 ASSERT_TRUE(AddEdge(3, 4));
00441 ASSERT_TRUE(AddEdge(4, 5));
00442 g_.RemoveNode(g_.Ptr(id_[3]));
00443 id_.erase(3);
00444 ASSERT_TRUE(AddEdge(5, 1));
00445 }
00446
00447 TEST_F(GraphCyclesTest, ManyEdges) {
00448 const int N = 50;
00449 for (int i = 0; i < N; i++) {
00450 for (int j = 1; j < N; j++) {
00451 ASSERT_TRUE(AddEdge(i, i+j));
00452 }
00453 }
00454 CheckInvariants(g_);
00455 ASSERT_TRUE(AddEdge(2*N-1, 0));
00456 CheckInvariants(g_);
00457 ASSERT_FALSE(AddEdge(10, 9));
00458 CheckInvariants(g_);
00459 }
00460
00461 }
00462 }