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