github.com/igggame/nebulas-go@v2.1.0+incompatible/nbre/runtime/nr/graph/algo.cpp (about) 1 // Copyright (C) 2018 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or 6 // modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // (at your option) any later version. 10 // 11 // the go-nebulas library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with the go-nebulas library. If not, see 18 // <http://www.gnu.org/licenses/>. 19 // 20 21 #include "runtime/nr/graph/algo.h" 22 #include "common/math.h" 23 #include "util/chrono.h" 24 #include <stack> 25 26 namespace neb { 27 namespace rt { 28 29 void graph_algo::dfs_find_a_cycle_from_vertex_based_on_time_sequence( 30 const transaction_graph::internal_graph_t &graph, 31 const transaction_graph::vertex_descriptor_t &s, 32 const transaction_graph::vertex_descriptor_t &v, 33 const std::unordered_set<transaction_graph::vertex_descriptor_t> &dead_v, 34 bool &has_cycle, 35 std::unordered_map<transaction_graph::vertex_descriptor_t, bool> &visited, 36 std::vector<transaction_graph::edge_descriptor_t> &edges, 37 std::vector<transaction_graph::edge_descriptor_t> &ret) { 38 39 auto in_time_order = 40 [&graph, &edges](const transaction_graph::oeiterator_t &oei) -> bool { 41 if (!edges.empty()) { 42 int64_t ts = boost::get(boost::edge_timestamp_t(), graph, edges.back()); 43 int64_t ts_next = boost::get(boost::edge_timestamp_t(), graph, *oei); 44 if (ts >= ts_next) { 45 return false; 46 } 47 } 48 return true; 49 }; 50 auto exit_cond = [&has_cycle, &edges, 51 &ret](const transaction_graph::oeiterator_t &oei) { 52 edges.push_back(*oei); 53 has_cycle = true; 54 55 for (auto it = edges.begin(); it != edges.end(); it++) { 56 ret.push_back(*it); 57 } 58 }; 59 60 transaction_graph::oeiterator_t oei, oei_end; 61 for (boost::tie(oei, oei_end) = boost::out_edges(v, graph); oei != oei_end; 62 oei++) { 63 if (has_cycle) { 64 return; 65 } 66 auto target = boost::target(*oei, graph); 67 if (dead_v.find(target) != dead_v.end()) { 68 continue; 69 } 70 71 if (target == s) { 72 if (!in_time_order(oei)) { 73 continue; 74 } 75 exit_cond(oei); 76 return; 77 } 78 79 if (visited[target]) { 80 if (!in_time_order(oei)) { 81 continue; 82 } 83 84 for (auto it = edges.begin(); it != edges.end();) { 85 auto t = boost::target(*it, graph); 86 it = edges.erase(it); 87 if (t == target) { 88 break; 89 } 90 } 91 92 exit_cond(oei); 93 return; 94 } 95 96 visited[target] = true; 97 98 if (edges.empty() || (!edges.empty() && in_time_order(oei))) { 99 edges.push_back(*oei); 100 dfs_find_a_cycle_from_vertex_based_on_time_sequence( 101 graph, s, target, dead_v, has_cycle, visited, edges, ret); 102 edges.pop_back(); 103 } 104 105 visited[target] = false; 106 } 107 return; 108 } 109 110 std::vector<transaction_graph::edge_descriptor_t> 111 graph_algo::find_a_cycle_from_vertex_based_on_time_sequence( 112 const transaction_graph::internal_graph_t &graph, 113 const transaction_graph::vertex_descriptor_t &v, 114 const std::unordered_set<transaction_graph::vertex_descriptor_t> &dead_v) { 115 116 std::vector<transaction_graph::edge_descriptor_t> ret; 117 std::vector<transaction_graph::edge_descriptor_t> edges; 118 std::unordered_map<transaction_graph::vertex_descriptor_t, bool> visited; 119 bool has_cycle = false; 120 121 visited[v] = true; 122 dfs_find_a_cycle_from_vertex_based_on_time_sequence( 123 graph, v, v, dead_v, has_cycle, visited, edges, ret); 124 return ret; 125 } 126 127 std::vector<transaction_graph::edge_descriptor_t> 128 graph_algo::find_a_cycle_based_on_time_sequence( 129 const transaction_graph::internal_graph_t &graph, 130 const std::unordered_set<transaction_graph::vertex_descriptor_t> &dead_v) { 131 std::vector<transaction_graph::edge_descriptor_t> ret; 132 133 std::vector<transaction_graph::vertex_descriptor_t> to_visit; 134 transaction_graph::viterator_t vi, vi_end; 135 for (boost::tie(vi, vi_end) = boost::vertices(graph); vi != vi_end; vi++) { 136 if (dead_v.find(*vi) == dead_v.end()) { 137 to_visit.push_back(*vi); 138 } 139 } 140 141 for (auto it = to_visit.begin(); it != to_visit.end(); it++) { 142 auto ret = 143 find_a_cycle_from_vertex_based_on_time_sequence(graph, *it, dead_v); 144 if (!ret.empty()) { 145 return ret; 146 } 147 } 148 return ret; 149 } 150 151 void graph_algo::bfs_decrease_graph_edges( 152 const transaction_graph::internal_graph_t &graph, 153 const std::unordered_set<transaction_graph::vertex_descriptor_t> &dead_v, 154 std::unordered_set<transaction_graph::vertex_descriptor_t> &tmp_dead, 155 std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> &dead_to, 156 std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> 157 &to_dead) { 158 159 std::queue<transaction_graph::vertex_descriptor_t> q; 160 161 auto update_dead_to = [&graph, &dead_v, &tmp_dead, &dead_to]( 162 const transaction_graph::vertex_descriptor_t &v) { 163 transaction_graph::oeiterator_t oei, oei_end; 164 for (boost::tie(oei, oei_end) = boost::out_edges(v, graph); oei != oei_end; 165 oei++) { 166 auto target = boost::target(*oei, graph); 167 if (dead_v.find(target) == dead_v.end() && 168 tmp_dead.find(target) == tmp_dead.end()) { 169 if (dead_to.find(target) != dead_to.end()) { 170 dead_to[target]++; 171 } else { 172 dead_to.insert(std::make_pair(target, 1)); 173 } 174 } 175 } 176 }; 177 auto update_to_dead = [&graph, &dead_v, &tmp_dead, &to_dead]( 178 const transaction_graph::vertex_descriptor_t &v) { 179 transaction_graph::ieiterator_t iei, iei_end; 180 for (boost::tie(iei, iei_end) = boost::in_edges(v, graph); iei != iei_end; 181 iei++) { 182 auto source = boost::source(*iei, graph); 183 if (dead_v.find(source) == dead_v.end() && 184 tmp_dead.find(source) == tmp_dead.end()) { 185 if (to_dead.find(source) != to_dead.end()) { 186 to_dead[source]++; 187 } else { 188 to_dead.insert(std::make_pair(source, 1)); 189 } 190 } 191 } 192 }; 193 194 for (auto &v : tmp_dead) { 195 q.push(v); 196 update_dead_to(v); 197 update_to_dead(v); 198 } 199 200 while (!q.empty()) { 201 auto &v = q.front(); 202 q.pop(); 203 204 transaction_graph::oeiterator_t oei, oei_end; 205 for (boost::tie(oei, oei_end) = boost::out_edges(v, graph); oei != oei_end; 206 oei++) { 207 auto target = boost::target(*oei, graph); 208 209 if (dead_v.find(target) == dead_v.end() && 210 tmp_dead.find(target) == tmp_dead.end()) { 211 auto ret = boost::in_degree(target, graph); 212 if (ret && dead_to.find(target) != dead_to.end() && 213 ret == dead_to[target]) { 214 q.push(target); 215 tmp_dead.insert(target); 216 update_dead_to(target); 217 } 218 } 219 } 220 221 transaction_graph::ieiterator_t iei, iei_end; 222 for (boost::tie(iei, iei_end) = boost::in_edges(v, graph); iei != iei_end; 223 iei++) { 224 auto source = boost::source(*iei, graph); 225 226 if (dead_v.find(source) == dead_v.end() && 227 tmp_dead.find(source) == tmp_dead.end()) { 228 auto ret = boost::out_degree(source, graph); 229 if (ret && to_dead.find(source) != to_dead.end() && 230 ret == to_dead[source]) { 231 q.push(source); 232 tmp_dead.insert(source); 233 update_to_dead(source); 234 } 235 } 236 } 237 } 238 } 239 240 bool graph_algo::decrease_graph_edges( 241 const transaction_graph::internal_graph_t &graph, 242 std::unordered_set<transaction_graph::vertex_descriptor_t> &dead_v, 243 std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> &dead_to, 244 std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> 245 &to_dead) { 246 247 std::unordered_set<transaction_graph::vertex_descriptor_t> tmp_dead; 248 transaction_graph::viterator_t vi, vi_end; 249 for (boost::tie(vi, vi_end) = boost::vertices(graph); vi != vi_end; vi++) { 250 if (dead_v.find(*vi) == dead_v.end()) { 251 auto ins = boost::in_degree(*vi, graph); 252 auto outs = boost::out_degree(*vi, graph); 253 if (!ins || !outs) { 254 tmp_dead.insert(*vi); 255 } 256 } 257 } 258 bfs_decrease_graph_edges(graph, dead_v, tmp_dead, dead_to, to_dead); 259 for (auto &tmp : tmp_dead) { 260 dead_v.insert(tmp); 261 } 262 return boost::num_vertices(graph) != dead_v.size(); 263 } 264 265 void graph_algo::remove_a_cycle( 266 transaction_graph::internal_graph_t &graph, 267 const std::vector<transaction_graph::edge_descriptor_t> &edges) { 268 269 wei_t min_w = -1; 270 for (auto it = edges.begin(); it != edges.end(); it++) { 271 wei_t w = boost::get(boost::edge_weight_t(), graph, *it); 272 min_w = (min_w == -1 ? w : math::min(min_w, w)); 273 } 274 275 for (auto it = edges.begin(); it != edges.end(); it++) { 276 wei_t w = boost::get(boost::edge_weight_t(), graph, *it); 277 boost::put(boost::edge_weight_t(), graph, *it, w - min_w); 278 if (w == min_w) { 279 boost::remove_edge(*it, graph); 280 } 281 } 282 } 283 284 void graph_algo::remove_cycles_based_on_time_sequence( 285 transaction_graph::internal_graph_t &graph) { 286 287 std::vector<transaction_graph::edge_descriptor_t> ret; 288 std::unordered_set<transaction_graph::vertex_descriptor_t> dead_v; 289 std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> dead_to; 290 std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> to_dead; 291 292 while (true) { 293 if (!decrease_graph_edges(graph, dead_v, dead_to, to_dead)) { 294 break; 295 } 296 ret = find_a_cycle_based_on_time_sequence(graph, dead_v); 297 if (ret.empty()) { 298 break; 299 } 300 remove_a_cycle(graph, ret); 301 } 302 } 303 304 void graph_algo::merge_edges_with_same_from_and_same_to( 305 transaction_graph::internal_graph_t &graph) { 306 307 transaction_graph::viterator_t vi, vi_end; 308 309 for (boost::tie(vi, vi_end) = boost::vertices(graph); vi != vi_end; vi++) { 310 transaction_graph::oeiterator_t oei, oei_end; 311 std::unordered_map<transaction_graph::vertex_descriptor_t, wei_t> 312 target_and_vals; 313 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, graph); 314 oei != oei_end; oei++) { 315 auto target = boost::target(*oei, graph); 316 wei_t val = boost::get(boost::edge_weight_t(), graph, *oei); 317 if (target_and_vals.find(target) == target_and_vals.end()) { 318 target_and_vals.insert(std::make_pair(target, val)); 319 } else { 320 target_and_vals[target] += val; 321 } 322 } 323 324 bool removed_all_edges = false; 325 while (!removed_all_edges) { 326 removed_all_edges = true; 327 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, graph); 328 oei != oei_end; oei++) { 329 removed_all_edges = false; 330 boost::remove_edge(oei, graph); 331 break; 332 } 333 } 334 335 for (auto it = target_and_vals.begin(); it != target_and_vals.end(); it++) { 336 boost::add_edge(*vi, it->first, {it->second, 0}, graph); 337 } 338 } 339 return; 340 } 341 342 transaction_graph *graph_algo::merge_two_graphs(transaction_graph *tg, 343 const transaction_graph *sg) { 344 345 transaction_graph::internal_graph_t sgi = sg->internal_graph(); 346 transaction_graph::viterator_t vi, vi_end; 347 348 for (boost::tie(vi, vi_end) = boost::vertices(sgi); vi != vi_end; vi++) { 349 transaction_graph::oeiterator_t oei, oei_end; 350 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, sgi); oei != oei_end; 351 oei++) { 352 auto source = boost::source(*oei, sgi); 353 auto target = boost::target(*oei, sgi); 354 auto from = to_address(boost::get(boost::vertex_name_t(), sgi, source)); 355 auto to = to_address(boost::get(boost::vertex_name_t(), sgi, target)); 356 wei_t w = boost::get(boost::edge_weight_t(), sgi, *oei); 357 358 tg->add_edge(from, to, w, 0); 359 } 360 } 361 return tg; 362 } 363 364 transaction_graph * 365 graph_algo::merge_graphs(const std::vector<transaction_graph_ptr_t> &graphs) { 366 if (!graphs.empty()) { 367 transaction_graph *ret = graphs.begin()->get(); 368 for (auto it = graphs.begin() + 1; it != graphs.end(); it++) { 369 transaction_graph *ptr = it->get(); 370 ret = merge_two_graphs(ret, ptr); 371 } 372 return ret; 373 } 374 return nullptr; 375 } 376 377 void graph_algo::merge_topk_edges_with_same_from_and_same_to( 378 transaction_graph::internal_graph_t &graph, uint32_t k) { 379 380 transaction_graph::viterator_t vi, vi_end; 381 382 struct edge_st { 383 wei_t weight; 384 transaction_graph::edge_descriptor_t edescriptor; 385 }; 386 387 auto cmp = [](const edge_st &e1, const edge_st &e2) -> bool { 388 return e1.weight > e2.weight; 389 }; 390 391 typedef std::priority_queue<edge_st, std::vector<edge_st>, decltype(cmp)> 392 pq_t; 393 394 for (boost::tie(vi, vi_end) = boost::vertices(graph); vi != vi_end; vi++) { 395 std::unordered_map<transaction_graph::vertex_descriptor_t, wei_t> 396 target_and_vals; 397 std::unordered_map<transaction_graph::vertex_descriptor_t, pq_t> 398 target_and_minheap; 399 400 transaction_graph::oeiterator_t oei, oei_end; 401 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, graph); 402 oei != oei_end; oei++) { 403 auto target = boost::target(*oei, graph); 404 wei_t val = boost::get(boost::edge_weight_t(), graph, *oei); 405 if (target_and_vals.find(target) == target_and_vals.end()) { 406 target_and_vals.insert(std::make_pair(target, val)); 407 pq_t min_heap(cmp); 408 min_heap.push(edge_st{val, *oei}); 409 target_and_minheap.insert(std::make_pair(target, min_heap)); 410 } else { 411 pq_t &min_heap = target_and_minheap.find(target)->second; 412 if (min_heap.size() < k) { 413 min_heap.push(edge_st{val, *oei}); 414 target_and_vals[target] += val; 415 } else { 416 edge_st e = min_heap.top(); 417 if (val > e.weight) { 418 // boost::remove_edge(e.edescriptor, graph); 419 min_heap.pop(); 420 target_and_vals[target] -= e.weight; 421 min_heap.push(edge_st{val, *oei}); 422 target_and_vals[target] += val; 423 } else { 424 // boost::remove_edge(oei, graph); 425 } 426 } 427 } 428 } 429 430 bool removed_all_edges = false; 431 while (!removed_all_edges) { 432 removed_all_edges = true; 433 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, graph); 434 oei != oei_end; oei++) { 435 removed_all_edges = false; 436 boost::remove_edge(oei, graph); 437 break; 438 } 439 } 440 441 for (auto it = target_and_vals.begin(); it != target_and_vals.end(); it++) { 442 boost::add_edge(*vi, it->first, {it->second, 0}, graph); 443 } 444 } 445 } 446 447 std::unique_ptr<std::unordered_map<address_t, in_out_val_t>> 448 graph_algo::get_in_out_vals(const transaction_graph::internal_graph_t &graph) { 449 450 auto ret = std::make_unique<std::unordered_map<address_t, in_out_val_t>>(); 451 452 transaction_graph::viterator_t vi, vi_end; 453 454 for (boost::tie(vi, vi_end) = boost::vertices(graph); vi != vi_end; vi++) { 455 transaction_graph::ieiterator_t iei, iei_end; 456 wei_t in_val = 0; 457 for (boost::tie(iei, iei_end) = boost::in_edges(*vi, graph); iei != iei_end; 458 iei++) { 459 wei_t val = boost::get(boost::edge_weight_t(), graph, *iei); 460 in_val += val; 461 } 462 463 transaction_graph::oeiterator_t oei, oei_end; 464 wei_t out_val = 0; 465 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, graph); 466 oei != oei_end; oei++) { 467 wei_t val = boost::get(boost::edge_weight_t(), graph, *oei); 468 out_val += val; 469 } 470 471 auto addr = to_address(boost::get(boost::vertex_name_t(), graph, *vi)); 472 ret->insert(std::make_pair(addr, in_out_val_t{in_val, out_val})); 473 } 474 return ret; 475 } 476 477 std::unique_ptr<std::unordered_map<address_t, wei_t>> 478 graph_algo::get_stakes(const transaction_graph::internal_graph_t &graph) { 479 480 auto ret = std::make_unique<std::unordered_map<address_t, wei_t>>(); 481 482 auto it_in_out_vals = get_in_out_vals(graph); 483 auto in_out_vals = *it_in_out_vals; 484 for (auto it = in_out_vals.begin(); it != in_out_vals.end(); it++) { 485 ret->insert( 486 std::make_pair(it->first, it->second.m_in_val - it->second.m_out_val)); 487 } 488 return ret; 489 } 490 491 std::unique_ptr<std::unordered_map<address_t, in_out_degree_t>> 492 graph_algo::get_in_out_degrees( 493 const transaction_graph::internal_graph_t &graph) { 494 495 auto ret = std::make_unique<std::unordered_map<address_t, in_out_degree_t>>(); 496 497 transaction_graph::viterator_t vi, vi_end; 498 499 for (boost::tie(vi, vi_end) = boost::vertices(graph); vi != vi_end; vi++) { 500 transaction_graph::ieiterator_t iei, iei_end; 501 uint32_t in_degree = 0; 502 for (boost::tie(iei, iei_end) = boost::in_edges(*vi, graph); iei != iei_end; 503 iei++) { 504 in_degree++; 505 } 506 507 transaction_graph::oeiterator_t oei, oei_end; 508 uint32_t out_degree = 0; 509 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, graph); 510 oei != oei_end; oei++) { 511 out_degree++; 512 } 513 514 auto addr = to_address(boost::get(boost::vertex_name_t(), graph, *vi)); 515 ret->insert(std::make_pair(addr, in_out_degree_t{in_degree, out_degree})); 516 } 517 return ret; 518 } 519 520 std::unique_ptr<std::unordered_map<address_t, uint32_t>> 521 graph_algo::get_degree_sum(const transaction_graph::internal_graph_t &graph) { 522 523 auto ret = std::make_unique<std::unordered_map<address_t, uint32_t>>(); 524 525 auto it_in_out_degrees = get_in_out_degrees(graph); 526 auto in_out_degrees = *it_in_out_degrees; 527 for (auto it = in_out_degrees.begin(); it != in_out_degrees.end(); it++) { 528 ret->insert(std::make_pair(it->first, it->second.m_in_degree + 529 it->second.m_out_degree)); 530 } 531 return ret; 532 } 533 534 class non_recursive_remove_cycles_based_on_time_sequence_helper { 535 public: 536 non_recursive_remove_cycles_based_on_time_sequence_helper( 537 transaction_graph::internal_graph_t &tg) 538 : m_graph(tg) {} 539 540 void remove_cycles_based_on_time_sequence( 541 transaction_graph::internal_graph_t &graph) { 542 543 std::unordered_map<transaction_graph::vertex_descriptor_t, 544 std::vector<transaction_graph::edge_descriptor_t>> 545 adj; 546 build_adj_graph(graph, adj); 547 auto start_nodes = possible_start_nodes_of_cycles(graph); 548 549 std::vector<transaction_graph::edge_descriptor_t> ret; 550 std::unordered_set<transaction_graph::vertex_descriptor_t> dead_v; 551 std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> dead_to; 552 std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> to_dead; 553 554 while (true) { 555 if (!::neb::rt::graph_algo::decrease_graph_edges(graph, dead_v, dead_to, 556 to_dead)) { 557 break; 558 } 559 ret = find_a_cycle_based_on_time_sequence(graph, adj, start_nodes, dead_v, 560 dead_to, to_dead); 561 if (ret.empty()) { 562 break; 563 } 564 remove_a_cycle(graph, adj, ret); 565 } 566 } 567 568 private: 569 void 570 show_path(const std::vector<transaction_graph::edge_descriptor_t> &edges) { 571 std::cout << ", show path: "; 572 for (auto &e : edges) { 573 auto source = boost::source(e, m_graph); 574 auto target = boost::target(e, m_graph); 575 576 auto s_name = boost::get(boost::vertex_name_t(), m_graph, source); 577 auto t_name = boost::get(boost::vertex_name_t(), m_graph, target); 578 579 std::cout << '(' << s_name << ',' << t_name << ')' << ','; 580 } 581 std::cout << std::endl; 582 } 583 584 void build_adj_graph( 585 const transaction_graph::internal_graph_t &graph, 586 std::unordered_map<transaction_graph::vertex_descriptor_t, 587 std::vector<transaction_graph::edge_descriptor_t>> 588 &adj) { 589 transaction_graph::viterator_t vi, vi_end; 590 for (boost::tie(vi, vi_end) = boost::vertices(graph); vi != vi_end; vi++) { 591 transaction_graph::oeiterator_t oei, oei_end; 592 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, graph); 593 oei != oei_end; oei++) { 594 if (adj.find(*vi) != adj.end()) { 595 auto &tmp = adj.find(*vi)->second; 596 tmp.push_back(*oei); 597 } else { 598 std::vector<transaction_graph::edge_descriptor_t> tmp; 599 tmp.push_back(*oei); 600 adj.insert(std::make_pair(*vi, tmp)); 601 } 602 } 603 } 604 } 605 606 std::vector<transaction_graph::vertex_descriptor_t> 607 possible_start_nodes_of_cycles( 608 const transaction_graph::internal_graph_t &graph) { 609 std::vector<transaction_graph::vertex_descriptor_t> nodes; 610 transaction_graph::viterator_t vi, vi_end; 611 for (boost::tie(vi, vi_end) = boost::vertices(m_graph); vi != vi_end; 612 vi++) { 613 auto ins = boost::in_degree(*vi, m_graph); 614 auto outs = boost::out_degree(*vi, m_graph); 615 if (ins && outs) { 616 int64_t ts_in_max = std::numeric_limits<int64_t>::min(); 617 int64_t ts_out_min = std::numeric_limits<int64_t>::max(); 618 619 transaction_graph::ieiterator_t iei, iei_end; 620 for (boost::tie(iei, iei_end) = boost::in_edges(*vi, graph); 621 iei != iei_end; ++iei) { 622 int64_t ts = boost::get(boost::edge_timestamp_t(), graph, *iei); 623 ts_in_max = std::max(ts, ts_in_max); 624 } 625 transaction_graph::oeiterator_t oei, oei_end; 626 for (boost::tie(oei, oei_end) = boost::out_edges(*vi, graph); 627 oei != oei_end; ++oei) { 628 int64_t ts = boost::get(boost::edge_timestamp_t(), graph, *oei); 629 ts_out_min = std::min(ts, ts_out_min); 630 } 631 if (ts_in_max >= ts_out_min) { 632 nodes.push_back(*vi); 633 } 634 } 635 } 636 return nodes; 637 } 638 639 std::vector<transaction_graph::edge_descriptor_t> 640 find_a_cycle_from_vertex_based_on_time_sequence( 641 const transaction_graph::internal_graph_t &graph, 642 const std::unordered_map< 643 transaction_graph::vertex_descriptor_t, 644 std::vector<transaction_graph::edge_descriptor_t>> &adj, 645 const transaction_graph::vertex_descriptor_t &v, 646 const std::unordered_set<transaction_graph::vertex_descriptor_t> &dead_v, 647 const std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> 648 &dead_to, 649 const std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> 650 &to_dead) { 651 652 std::vector<transaction_graph::edge_descriptor_t> edges; 653 typedef std::pair<transaction_graph::vertex_descriptor_t, size_t> st_t; 654 std::stack<st_t> st; 655 std::unordered_map<transaction_graph::vertex_descriptor_t, bool> visited; 656 st.push(std::make_pair(v, 0)); 657 visited[v] = true; 658 659 auto backtrace_cond = [&adj, &to_dead](const st_t &ele) { 660 size_t to_dead_cnt = 0; 661 auto it = to_dead.find(ele.first); 662 if (it != to_dead.end()) { 663 to_dead_cnt = it->second; 664 } 665 auto ite = adj.find(ele.first); 666 if (ite == adj.end()) { 667 return true; 668 } 669 return ele.second + to_dead_cnt == ite->second.size(); 670 }; 671 auto in_time_order = [&graph, &edges]( 672 const transaction_graph::edge_descriptor_t &edge) { 673 if (!edges.empty()) { 674 int64_t ts = boost::get(boost::edge_timestamp_t(), graph, edges.back()); 675 int64_t ts_next = boost::get(boost::edge_timestamp_t(), graph, edge); 676 if (ts > ts_next) { 677 return false; 678 } 679 } 680 return true; 681 }; 682 auto erase_tail = 683 [&edges, &graph](const transaction_graph::vertex_descriptor_t &target) { 684 for (auto it = edges.begin(); it != edges.end();) { 685 auto t = boost::target(*it, graph); 686 it = edges.erase(it); 687 if (t == target) { 688 break; 689 } 690 } 691 }; 692 693 std::unordered_set<int64_t> dead_edge; 694 auto is_dead_edge = 695 [&dead_edge, &graph](const transaction_graph::edge_descriptor_t &edge) { 696 auto edge_index = boost::get(boost::edge_sort_id_t(), graph, edge); 697 if (dead_edge.find(edge_index) != dead_edge.end()) { 698 return true; 699 } 700 return false; 701 }; 702 auto update_dead_edge = 703 [&dead_edge, &graph](const transaction_graph::edge_descriptor_t &edge) { 704 auto edge_index = boost::get(boost::edge_sort_id_t(), graph, edge); 705 dead_edge.insert(edge_index); 706 }; 707 708 while (!st.empty()) { 709 auto &ele = st.top(); 710 auto v_name = boost::get(boost::vertex_name_t(), graph, ele.first); 711 712 if (backtrace_cond(ele)) { 713 st.pop(); 714 visited[ele.first] = false; 715 if (!edges.empty()) { 716 auto &tmp = edges.back(); 717 update_dead_edge(tmp); 718 edges.pop_back(); 719 } 720 } else { 721 auto it = adj.find(ele.first); 722 auto &tmp = it->second; 723 auto &nxt = tmp[ele.second++]; 724 if (is_dead_edge(nxt)) { 725 continue; 726 } 727 728 if (!in_time_order(nxt)) { 729 continue; 730 } 731 732 auto target = boost::target(nxt, graph); 733 if (dead_v.find(target) != dead_v.end()) { 734 continue; 735 } 736 737 if (!visited[target]) { 738 st.push(std::make_pair(target, 0)); 739 visited[target] = true; 740 edges.push_back(nxt); 741 } else { 742 if (!edges.empty()) { 743 auto &e = edges.front(); 744 auto source = boost::source(e, graph); 745 if (source != target) { 746 erase_tail(target); 747 } 748 } 749 edges.push_back(nxt); 750 // show_path(edges); 751 break; 752 } 753 } 754 } 755 return edges; 756 } 757 758 std::vector<transaction_graph::edge_descriptor_t> 759 find_a_cycle_based_on_time_sequence( 760 const transaction_graph::internal_graph_t &graph, 761 const std::unordered_map< 762 transaction_graph::vertex_descriptor_t, 763 std::vector<transaction_graph::edge_descriptor_t>> &adj, 764 const std::vector<transaction_graph::vertex_descriptor_t> &start_nodes, 765 const std::unordered_set<transaction_graph::vertex_descriptor_t> &dead_v, 766 const std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> 767 &dead_to, 768 const std::unordered_map<transaction_graph::vertex_descriptor_t, size_t> 769 &to_dead) { 770 771 std::vector<transaction_graph::edge_descriptor_t> ret; 772 for (auto &v : start_nodes) { 773 if (dead_v.find(v) == dead_v.end()) { 774 auto v_name = boost::get(boost::vertex_name_t(), graph, v); 775 ret = find_a_cycle_from_vertex_based_on_time_sequence( 776 graph, adj, v, dead_v, dead_to, to_dead); 777 if (!ret.empty()) { 778 break; 779 } 780 } 781 } 782 return ret; 783 } 784 785 void remove_a_cycle( 786 transaction_graph::internal_graph_t &graph, 787 std::unordered_map<transaction_graph::vertex_descriptor_t, 788 std::vector<transaction_graph::edge_descriptor_t>> 789 &adj, 790 const std::vector<transaction_graph::edge_descriptor_t> &edges) { 791 792 wei_t min_w = -1; 793 for (auto &e : edges) { 794 wei_t w = boost::get(boost::edge_weight_t(), graph, e); 795 min_w = (min_w == -1 ? w : math::min(min_w, w)); 796 } 797 798 for (auto &e : edges) { 799 wei_t w = boost::get(boost::edge_weight_t(), graph, e); 800 boost::put(boost::edge_weight_t(), graph, e, w - min_w); 801 if (w == min_w) { 802 boost::remove_edge(e, graph); 803 804 auto source = boost::source(e, graph); 805 if (adj.find(source) != adj.end()) { 806 for (auto it_e = adj[source].begin(); it_e != adj[source].end();) { 807 if (*it_e == e) { 808 it_e = adj[source].erase(it_e); 809 } else { 810 it_e++; 811 } 812 } 813 } 814 } 815 } 816 } 817 818 private: 819 transaction_graph::internal_graph_t m_graph; 820 }; 821 822 void graph_algo::non_recursive_remove_cycles_based_on_time_sequence( 823 transaction_graph::internal_graph_t &graph) { 824 non_recursive_remove_cycles_based_on_time_sequence_helper nh(graph); 825 nh.remove_cycles_based_on_time_sequence(graph); 826 } 827 828 } // namespace rt 829 } // namespace neb