networkx中的约束短路径算法?

问题描述 投票:0回答:3

我正在使用networkx来解决最短路径问题。我主要使用shortest_path。我想知道,使用当前版本的networkx,是否可以限制最短路径计算?

下面的示例生成了 A 和 G 之间的两条最短路径:

shortest route

左图显示了最小化“长度”属性时的最佳路线,右图显示了最小化“高度”属性时的最佳路线。

如果我们计算这些路线的统计数据,我们得到:

Best route by length: ['A', 'B', 'E', 'F', 'D', 'G']
Best route by height: ['A', 'C', 'E', 'G']
Stats best routes: {
                   'by_length': {'length': 13.7, 'height': 373.0}, 
                   'by_height': {'length': 24.8, 'height': 115.0}
                   }

有没有办法在计算最短路径时添加约束? (例如,通过最小化长度属性但同时保持

height<300

来计算最短路径

计算图网络的代码:

import matplotlib.pyplot as plt
from itertools import combinations, groupby
import os
import numpy as np
import networkx as nx
import random


# 1. Define test network 
MG = nx.MultiDiGraph()
MG.add_edges_from([("B", "A", {"length": 0.8}), ("A", "B", {"length": 1.}), ("D", "G", {"length": 3.5}),
                   ("B", "D", {"length": 20.8}), ("A", "C", {"length": 9.7}), ("D", "C", {"length": 0.3}),
                   ("B", "E", {"length": 4.8}), ("D", "E", {"length": 0.05}), ("C", "E", {"length": 0.1}),          
                   ("E", "C", {"length": 0.7}), ("E", "F", {"length": 0.4}), ("E", "G", {"length": 15.}),           
                   ("F", "C", {"length": 0.9}), ("F", "D", {"length": 4.}),                       
                  ])
attrs = {'B': {"x": -20., "y": 60.}, 'A': {"x": 28., "y":55.},
         'C': {"x": -12., "y": 40.}, 'D': {"x": 40., "y":45.},
         'E': {"x": 8., "y": 35.}, 'F': {"x": -8., "y":15.},    
         'G': {"x": 21., "y":5.},    
        }

for num, (k,v) in enumerate(attrs.items()):
    attrs[k]={**v,
             }  
nx.set_node_attributes(MG, attrs)

rng = np.random.default_rng(seed=42)
random_height = list(rng.uniform(low=0, high=100, size=len(MG.edges)).round(0))
attrs={}
for num, edge in enumerate(MG.edges):
    attrs[edge]={'height': random_height[num]}
nx.set_edge_attributes(MG, attrs)


# 2. Calculate shortest route
best_route_by_length = nx.shortest_path(MG, "A", "G",weight="length")
print(f"Best route by length: {best_route_by_length}")

best_route_by_height = nx.shortest_path(MG, "A", "G",weight="height")
print(f"Best route by height: {best_route_by_height}")

# 3. Get stats for each route

def get_route_edge_attributes(
    G, route, attribute=None, minimize_key="length", retrieve_default=None
):
    """
    """
    attribute_values = []
    for u, v in zip(route[:-1], route[1:]):
        # if there are parallel edges between two nodes, select the one with the
        # lowest value of minimize_key
        data = min(G.get_edge_data(u, v).values(), key=lambda x: x[minimize_key])
        if attribute is None:
            attribute_value = data
        elif retrieve_default is not None:
            attribute_value = data.get(attribute, retrieve_default(u, v))
        else:
            attribute_value = data[attribute]
        attribute_values.append(attribute_value)
    return attribute_values


stats_best_route={}
stats_best_route['by_length']={'length': sum(get_route_edge_attributes(MG,
                                                                   best_route_by_length,
                                                                  'length')),
                              'height': sum(get_route_edge_attributes(MG,
                                                                   best_route_by_length,
                                                                  'height'))}
stats_best_route['by_height']={'length': sum(get_route_edge_attributes(MG,
                                                                   best_route_by_height,
                                                                  'length')),
                              'height': sum(get_route_edge_attributes(MG,
                                                                   best_route_by_height,
                                                                  'height'))}

print(f"Stats best routes: {stats_best_route}")

编辑 07/06/21

我的问题可能太大,无法使用暴力解决方案。

我还在networkx讨论页面中发表了一篇文章,似乎可以实现一个在

single_source_dijkstra
中执行数字操作的特殊类。 然而,这一解决方案并不完美:如果有两条可接受的短路径(高度),算法将找到较短的一条,无论其高度如何。当存在允许的路径时,这可能会使到达目标节点看起来不可能......(更多详细信息请参见:https://github.com/networkx/networkx/discussions/4832#discussioncomment-778358

python networkx graph-theory shortest-path
3个回答
2
投票

大致解决方案:

  • 跑距离最短
  • 如果高度在限制范围内 然后完成
  • 删除路径中高度最大的链接
  • 重复直到完成。

障碍:

  • 如果最大高度的路径链接至关重要,删除它将使目的地无法到达。 如果发生这种情况,则有必要返回,替换最高的链接并删除第二高的链接......

  • 不能保证找到最佳路径。


1
投票

您真正的问题是否足够小,以至于您可以使用暴力来使用

all_simple_paths()

from networkx.algorithms.simple_paths import all_simple_paths

shortest_paths_constrained = []
for path in all_simple_paths(MG, "A", "G"):
    if sum(get_route_edge_attributes(MG, path, 'height')) < 300:
        path_length = sum(get_route_edge_attributes(MG, path, 'length'))
        result = (path, path_length)
        if not shortest_paths_constrained:
            shortest_paths_constrained.append(result)
        elif path_length == shortest_paths_constrained[0][1]:
            shortest_paths_constrained.append(result)
        elif path_length < shortest_paths_constrained[0][1]:
            shortest_paths_constrained = [result]

给出

shortest_paths_constrained
作为

[(['A', 'C', 'E', 'F', 'D', 'G'], 17.7)]

效率低下——但是,如果可行的话,它肯定会给出正确的解决方案。 请注意,如果它对您的问题很重要,它还应该在

length
约束内返回所有最短的
height
关系。 如果需要,您想如何打破可能的联系取决于您。

好消息是

all_simple_paths()
返回一个生成器,而不是尝试一次查找所有路径,因此无需担心将所有路径拟合到内存中。然而,完成计算需要多长时间是一个更大的谜......


0
投票

这可能是一个幼稚的答案,因为我实际上刚刚安装了networkx并且面临着类似的问题......但是复制图表并删除所有不满足约束的边是否可行?

import networkx as nx

MG = nx.MultiDiGraph()
MG.add_edge('a', 'b')
MG.add_edge('b', 'c')
MG.add_edge('c', 'd')
MG.add_edge('b', 'd', aura="orange")

assert nx.shortest_path(MG, 'a', 'd') == ['a', 'b', 'd']

MG_no_orange = MG.copy()
MG_no_orange.remove_edges_from(e for e in MG.edges if MG.edges[e].get('aura') == 'orange')

assert nx.shortest_path(MG_no_orange, 'a', 'd') == ['a', 'b', 'c', 'd']
© www.soinside.com 2019 - 2024. All rights reserved.