高效并行算法,用于更新加权有向图中所有简单路径的遍历成本

时间:2017-12-20 22:30:11

标签: parallel-processing gpgpu graph-databases graph-traversal blazegraph

我使用相对较小的有向图(~10个节点),每个图都有~10,000个简单的路径和周期。我想维护一个排序的总成本列表来遍历所有这些简单的路径和周期。我的边有几个不同的权重,但聚合函数对于所有这些都是交换/关联的(例如总和和产品)。

现在,我正在使用rethinkdb(一个nosql数据库)和python。我正在预先计算所有可能的简单路径,将它们存储在哈希映射中,并且每次更新边权重时,只需暴力重新计算遍历就会花费它们。我的哈希映射将给定边(其权重刚刚更新)指向它所属的所有简单路径和周期。然后我去重新计算每一个的遍历成本。

好吧,我发现这很慢并且无法扩展!我知道这是一个难题,但我希望这对我相对较小的图表是可行的。

我原始方法的一个低效率似乎是在每条路径的浪费冗余计算中,即使有些是彼此的聚合。例如,A→B→C→D→E的成本是A→B→C和C→D→E的组成。那么为什么不巧妙地计算它们呢?我提出了一种做到这一点的方法,它似乎没有帮助一点,这让我觉得我真的需要退后一步。

所以我上网并做了一些搜索,偶然发现了这篇非常有用的文章: https://blog.blazegraph.com/?p=628。它说:

  

大图反模式是“将所有东西都扔进一个大图表中   然后使用相同的工具,为我们提供水平缩放   问题:map / reduce和键值存储。“

这让我觉得这正是我一直在做的(错误的)。

似乎GPU是文章中提到的内存带宽问题的正确解决方案......除了我不确定如何并行处理这个问题。

问题:

  1. 如何并行处理此问题?聚集应用分散正确的方向?以前在哪里做过?

  2. 如何在不平行的情况下有效优化当前方法?

  3. 作为参考,这是我当前算法的草图:

    1. 枚举我的图表中的所有简单路径和周期
    2. 保留边缘及其权重的字典。例如,如果('A','B') 是从节点A到节点B的边缘,

      edges_weights[('A','B')] # -> {'x': 1.3, 'y': 32, 'z': 0.231232}
      
    3. 保留每个边缘所涉及的所有简单路径和周期的字典,例如:

      paths_containing_edges[('A','B')] 
      # ->
      # [
      #      (('A','B'), ('B','C')), 
      #      (('A','B'), ('B','C'), ('C','D')), 
      #      (('A','B'), ('B','C'), ('C','A')), 
      #      ... 
      #      (('A','B'), ('B','C'), ('C','D'), ('D','A'))
      # ] 
      
    4. 此外,初始化路径字典及其成本:

      paths_costs = {
              (('A','B'), ('B','C')): {'x': ..., 'y': ..., 'z': ...}
      }
      
    5. 更新边缘时:

      我。在edges_weights

      中更新其权重

      II。查找包含此边的所有简单路径并更新它们:

      fn = lambda p,q: p+q # the cost aggregation function        
      weight_keys = ['x','y','z']
      for path in paths_containing_edges[updated_edge]:
        # path is a tuple of edge tuples, i.e. (('A','B'),('B','C'),('C','D'))
        for w in weight_keys:
          paths_costs[path][w] = reduce(fn,[edges_weights[e][w] for e in path])
      
    6. 显然,有很多嵌套循环和查找正在发生......但我很难看到我能以不同方式做事。

1 个答案:

答案 0 :(得分:1)

我不确定我是否理解你的问题。我还有一个尝试:

如果您有n个节点,那么节点之间会有max(n * n-n)/ 2个边。

如果n为10,则表示有50条可能的边。

在获得一个循环之前,10个节点的最大路径长度为10。 一个简单的边缘阵列应确保您可以访问边缘的称重信息。 如果是,例如存在路径A-> B-> C-> D-> E-> G-> H-> I-> J

您将不得不总结(A-> B)(B-> C)(D-> E)(E-> F)(E-> G)(HI)(I- > J)的 现在的问题是:什么更快 - 查找此子集的解决方案的查找成本,或者只是添加数组指针并保留这些指针?保持10.000指针并总结数字应该非常快。特别是如果你将权重保持为CPU可以很好地处理的数字。我假设它们是int,long而不是float或double,即使使用64位cpu,这也不成问题。

鉴于您的数字很少,我会尝试使用适当的编程语言(如C / Assembler)创建一个最有效的计算,这将降低计算的CPU周期。我的直觉是,这比找到一个有效的算法要快(但只有少数n - 我想知道切换点会在哪里......)