def _single_source_dijkstra_path_basic(G, s, weight): weight = _weight_function(G, weight) # modified from Eppstein S = [] P = {} for v in G: P[v] = [] sigma = dict.fromkeys(G, 0.0) # sigma[v]=0 for v in G D = {} sigma[s] = 1.0 push = heappush pop = heappop seen = {s: 0} c = count() Q = [] # use Q as heap with (distance,node id) tuples push(Q, (0, next(c), s, s)) while Q: (dist, _, pred, v) = pop(Q) if v in D: continue # already searched this node. sigma[v] += sigma[pred] # count paths S.append(v) D[v] = dist for w, edgedata in G[v].items(): vw_dist = dist + weight(v, w, edgedata) if w not in D and (w not in seen or vw_dist < seen[w]): seen[w] = vw_dist push(Q, (vw_dist, next(c), v, w)) sigma[w] = 0.0 P[w] = [v] elif vw_dist == seen[w]: # handle equal paths sigma[w] += sigma[v] P[w].append(v) return S, P, sigma, D
def _add_edge_keys(G, betweenness, weight=None): r"""Adds the corrected betweenness centrality (BC) values for multigraphs. Parameters ---------- G : NetworkX graph. betweenness : dictionary Dictionary mapping adjacent node tuples to betweenness centrality values. weight : string or function See `_weight_function´ for details. Defaults to `None`. Returns ------- edges : dictionary The parameter `betweenness` including edges with keys and their betweenness centrality values. The BC value is divided among edges of equal weight. """ _weight = _weight_function(G, weight) edge_bc = dict.fromkeys(G.edges, 0.0) for u, v in betweenness: d = G[u][v] wt = _weight(u, v, d) keys = [k for k in d if _weight(u, v, {k: d[k]}) == wt] bc = betweenness[(u, v)] / len(keys) for k in keys: edge_bc[(u, v, k)] = bc return edge_bc
def beam_path_length(G, source, target, heuristic=None, weight='weight'): """Returns the length of the shortest path between source and target using the Beam Stack Search algorithm. Parameters ---------- G : NetworkX graph source : node Starting node for path target : node Ending node for path heuristic : function A function to evaluate the estimate of the distance from the a node to the target. The function takes two nodes arguments and must return a number. Raises ------ NetworkXNoPath If no path exists between source and target. See Also ------- """ if source not in G or target not in G: msg = f"Either source {source} or target {target} is not in G" raise nx.NodeNotFound(msg) weight = _weight_function(G, weight) path = beam_path(G, source, target, heuristic, weight) return sum(weight(u, v, G[u][v]) for u, v in zip(path[:-1], path[1:]))
def greedy_path(G, source, target, heuristic = None, weight = "weight"): #if source or target is not in G -> error msg if source not in G or target not in G: msg = f"Either source {source} or target {target} is not in G" raise nx.NodeNotFound(msg) #if heuristic is none -> define the default heuristic function: h = 0 if heuristic is None: def heuristic(u, v): return 0 #assegno a push la funzione heappush e a pop la funzione heappop. #A wieght invece assegno la funzione _weight_function(G, weight) #passando come parametro G e weight (input della funzione greedy_search) push = heappush pop = heappop getWeight = _weight_function(G, weight) #variabile di gestione dell'euristica nell'ordinamento (in caso di parità) c = count() #la fringe mantiene: priorità, nodo corrente (nel caso base è la radice), varibaile di gestione e il parent fringe = [(0, next(c), source, None)] #struttura dati che memorizza i parent dei nodi visitati explored = {} #struttura che mantiene i pesi dei cammini parziali weights = {} while queue: _, __, curNode, parent = pop(fringe) #caso base: target raggiunto. Costruzione del path tramite i parent if curNode == target: path = [curNode] node = parent while node is not None: path.append(node) node = explored[node] path.reverse() return path curWeight = weights[parent] + getWeight(parent, curNode, weight) if curNode in explored: if explored[curNode] is None: continue if curWeight > weights[curNode]: continue explored[curNode] = parent weights[curNode] = curWeight for neighbor, _ in G[curNode].items(): if neighbor not in explored: weights[neighbor] = weights[curNode] + getWeight(curnNode, neighbor, weight) heuristicValue = heuristic(neighbor, target) push(fringe, (heuristicValue, next(c), neighbor, curNode))
def __init__(self, G, weight='weight'): if not nx.is_weighted(G, weight=weight): raise nx.NetworkXError('Graph is not weighted.') self.G = G self.dist = {v: 0 for v in G} self.pred = {v: [] for v in G} self.weight = _weight_function(G, weight) # Calculate distance of shortest paths self.dist_bellman = _bellman_ford(G, list(G), self.weight, pred=self.pred, dist=self.dist)
def astar_path_length(G, source, target, heuristic=None, weight="weight"): """Returns the length of the shortest path between source and target using the A* ("A-star") algorithm. Parameters ---------- G : NetworkX graph source : node Starting node for path target : node Ending node for path heuristic : function A function to evaluate the estimate of the distance from the a node to the target. The function takes two nodes arguments and must return a number. weight : string or function If this is a string, then edge weights will be accessed via the edge attribute with this key (that is, the weight of the edge joining `u` to `v` will be ``G.edges[u, v][weight]``). If no such edge attribute exists, the weight of the edge is assumed to be one. If this is a function, the weight of an edge is the value returned by the function. The function must accept exactly three positional arguments: the two endpoints of an edge and the dictionary of edge attributes for that edge. The function must return a number. Raises ------ NetworkXNoPath If no path exists between source and target. See Also -------- astar_path """ if source not in G or target not in G: msg = f"Either source {source} or target {target} is not in G" raise nx.NodeNotFound(msg) weight = _weight_function(G, weight) path = astar_path(G, source, target, heuristic, weight) return sum(weight(u, v, G[u][v]) for u, v in zip(path[:-1], path[1:]))
def widest_path(G, source, target): from networkx.algorithms.shortest_paths.weighted import _weight_function from heapq import heappush, heappop weight = _weight_function(G, Capacity) paths = {source: [source]} # dictionary of paths G_succ = G._succ width_to = {} # dictionary of final distances seen = {} # fringe is heapq with 3-tuples (distance,c,node) # use the count c to avoid comparing nodes (may not be able to) c = count() fringe = [] seen[source] = float('inf') # seen maximum width heappush(fringe, (-float('inf'), next(c), source)) # use min-heap as max-heap while fringe: (w, _, v) = heappop(fringe) if v in width_to: continue # already searched this node. width_to[v] = -w if v == target: break for u, e in G_succ[v].items(): width = weight(v, u, e) if not width: continue vu_width = min((width, width_to[v])) if u not in seen or vu_width > seen[u]: seen[u] = vu_width heappush(fringe, (-vu_width, next(c), u)) paths[u] = paths[v] + [u] try: return width_to[target], paths[target] except KeyError: raise nx.NetworkXNoPath("No path to {}.".format(target))
def astar_path(G, source, target, heuristic=None, weight="weight"): """Returns a list of nodes in a shortest path between source and target using the A* ("A-star") algorithm. There may be more than one shortest path. This returns only one. Parameters ---------- G : NetworkX graph source : node Starting node for path target : node Ending node for path heuristic : function A function to evaluate the estimate of the distance from the a node to the target. The function takes two nodes arguments and must return a number. weight : string or function If this is a string, then edge weights will be accessed via the edge attribute with this key (that is, the weight of the edge joining `u` to `v` will be ``G.edges[u, v][weight]``). If no such edge attribute exists, the weight of the edge is assumed to be one. If this is a function, the weight of an edge is the value returned by the function. The function must accept exactly three positional arguments: the two endpoints of an edge and the dictionary of edge attributes for that edge. The function must return a number. Raises ------ NetworkXNoPath If no path exists between source and target. Examples -------- >>> G = nx.path_graph(5) >>> print(nx.astar_path(G, 0, 4)) [0, 1, 2, 3, 4] >>> G = nx.grid_graph(dim=[3, 3]) # nodes are two-tuples (x,y) >>> nx.set_edge_attributes(G, {e: e[1][0] * 2 for e in G.edges()}, "cost") >>> def dist(a, b): ... (x1, y1) = a ... (x2, y2) = b ... return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 >>> print(nx.astar_path(G, (0, 0), (2, 2), heuristic=dist, weight="cost")) [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)] See Also -------- shortest_path, dijkstra_path """ if source not in G or target not in G: msg = f"Either source {source} or target {target} is not in G" raise nx.NodeNotFound(msg) if heuristic is None: # The default heuristic is h=0 - same as Dijkstra's algorithm def heuristic(u, v): return 0 push = heappush pop = heappop weight = _weight_function(G, weight) # The queue stores priority, node, cost to reach, and parent. # Uses Python heapq to keep in priority order. # Add a counter to the queue to prevent the underlying heap from # attempting to compare the nodes themselves. The hash breaks ties in the # priority and is guaranteed unique for all nodes in the graph. c = count() queue = [(0, next(c), source, 0, None)] # Maps enqueued nodes to distance of discovered paths and the # computed heuristics to target. We avoid computing the heuristics # more than once and inserting the node into the queue too many times. enqueued = {} # Maps explored nodes to parent closest to the source. explored = {} while queue: # Pop the smallest item from queue. _, __, curnode, dist, parent = pop(queue) if curnode == target: path = [curnode] node = parent while node is not None: path.append(node) node = explored[node] path.reverse() return path if curnode in explored: # Do not override the parent of starting node if explored[curnode] is None: continue # Skip bad paths that were enqueued before finding a better one qcost, h = enqueued[curnode] if qcost < dist: continue explored[curnode] = parent for neighbor, w in G[curnode].items(): ncost = dist + weight(curnode, neighbor, w) if neighbor in enqueued: qcost, h = enqueued[neighbor] # if qcost <= ncost, a less costly path from the # neighbor to the source was already determined. # Therefore, we won't attempt to push this neighbor # to the queue if qcost <= ncost: continue else: h = heuristic(neighbor, target) enqueued[neighbor] = ncost, h push(queue, (ncost + h, next(c), neighbor, ncost, curnode)) raise nx.NetworkXNoPath(f"Node {target} not reachable from {source}")
def _bidirectional_dijkstra(G, source, target, weight="weight", ignore_nodes=None, ignore_edges=None): """Dijkstra's algorithm for shortest paths using bidirectional search. This function returns the shortest path between source and target ignoring nodes and edges in the containers ignore_nodes and ignore_edges. This is a custom modification of the standard Dijkstra bidirectional shortest path implementation at networkx.algorithms.weighted Parameters ---------- G : NetworkX graph source : node Starting node. target : node Ending node. weight: string, function, optional (default='weight') Edge data key or weight function corresponding to the edge weight ignore_nodes : container of nodes nodes to ignore, optional ignore_edges : container of edges edges to ignore, optional Returns ------- length : number Shortest path length. Returns a tuple of two dictionaries keyed by node. The first dictionary stores distance from the source. The second stores the path from the source to that node. Raises ------ NetworkXNoPath If no path exists between source and target. Notes ----- Edge weight attributes must be numerical. Distances are calculated as sums of weighted edges traversed. In practice bidirectional Dijkstra is much more than twice as fast as ordinary Dijkstra. Ordinary Dijkstra expands nodes in a sphere-like manner from the source. The radius of this sphere will eventually be the length of the shortest path. Bidirectional Dijkstra will expand nodes from both the source and the target, making two spheres of half this radius. Volume of the first sphere is pi*r*r while the others are 2*pi*r/2*r/2, making up half the volume. This algorithm is not guaranteed to work if edge weights are negative or are floating point numbers (overflows and roundoff errors can cause problems). See Also -------- shortest_path shortest_path_length """ if ignore_nodes and (source in ignore_nodes or target in ignore_nodes): raise nx.NetworkXNoPath(f"No path between {source} and {target}.") if source == target: return (0, [source]) # handle either directed or undirected if G.is_directed(): Gpred = G.predecessors Gsucc = G.successors else: Gpred = G.neighbors Gsucc = G.neighbors # support optional nodes filter if ignore_nodes: def filter_iter(nodes): def iterate(v): for w in nodes(v): if w not in ignore_nodes: yield w return iterate Gpred = filter_iter(Gpred) Gsucc = filter_iter(Gsucc) # support optional edges filter if ignore_edges: if G.is_directed(): def filter_pred_iter(pred_iter): def iterate(v): for w in pred_iter(v): if (w, v) not in ignore_edges: yield w return iterate def filter_succ_iter(succ_iter): def iterate(v): for w in succ_iter(v): if (v, w) not in ignore_edges: yield w return iterate Gpred = filter_pred_iter(Gpred) Gsucc = filter_succ_iter(Gsucc) else: def filter_iter(nodes): def iterate(v): for w in nodes(v): if (v, w) not in ignore_edges and ( w, v) not in ignore_edges: yield w return iterate Gpred = filter_iter(Gpred) Gsucc = filter_iter(Gsucc) push = heappush pop = heappop # Init: Forward Backward dists = [{}, {}] # dictionary of final distances paths = [{source: [source]}, {target: [target]}] # dictionary of paths fringe = [[], []] # heap of (distance, node) tuples for # extracting next node to expand seen = [{source: 0}, {target: 0}] # dictionary of distances to # nodes seen c = count() # initialize fringe heap push(fringe[0], (0, next(c), source)) push(fringe[1], (0, next(c), target)) # neighs for extracting correct neighbor information neighs = [Gsucc, Gpred] # variables to hold shortest discovered path # finaldist = 1e30000 finalpath = [] dir = 1 while fringe[0] and fringe[1]: # choose direction # dir == 0 is forward direction and dir == 1 is back dir = 1 - dir # extract closest to expand (dist, _, v) = pop(fringe[dir]) if v in dists[dir]: # Shortest path to v has already been found continue # update distance dists[dir][v] = dist # equal to seen[dir][v] if v in dists[1 - dir]: # if we have scanned v in both directions we are done # we have now discovered the shortest path return (finaldist, finalpath) wt = _weight_function(G, weight) for w in neighs[dir](v): if dir == 0: # forward minweight = wt(v, w, G.get_edge_data(v, w)) vwLength = dists[dir][v] + minweight else: # back, must remember to change v,w->w,v minweight = wt(w, v, G.get_edge_data(w, v)) vwLength = dists[dir][v] + minweight if w in dists[dir]: if vwLength < dists[dir][w]: raise ValueError( "Contradictory paths found: negative weights?") elif w not in seen[dir] or vwLength < seen[dir][w]: # relaxing seen[dir][w] = vwLength push(fringe[dir], (vwLength, next(c), w)) paths[dir][w] = paths[dir][v] + [w] if w in seen[0] and w in seen[1]: # see if this path is better than than the already # discovered shortest path totaldist = seen[0][w] + seen[1][w] if finalpath == [] or finaldist > totaldist: finaldist = totaldist revpath = paths[1][w][:] revpath.reverse() finalpath = paths[0][w] + revpath[1:] raise nx.NetworkXNoPath(f"No path between {source} and {target}.")
def shortest_simple_paths(G, source, target, weight=None): """Generate all simple paths in the graph G from source to target, starting from shortest ones. A simple path is a path with no repeated nodes. If a weighted shortest path search is to be used, no negative weights are allowed. Parameters ---------- G : NetworkX graph source : node Starting node for path target : node Ending node for path weight : string or function If it is a string, it is the name of the edge attribute to be used as a weight. If it is a function, the weight of an edge is the value returned by the function. The function must accept exactly three positional arguments: the two endpoints of an edge and the dictionary of edge attributes for that edge. The function must return a number. If None all edges are considered to have unit weight. Default value None. Returns ------- path_generator: generator A generator that produces lists of simple paths, in order from shortest to longest. Raises ------ NetworkXNoPath If no path exists between source and target. NetworkXError If source or target nodes are not in the input graph. NetworkXNotImplemented If the input graph is a Multi[Di]Graph. Examples -------- >>> G = nx.cycle_graph(7) >>> paths = list(nx.shortest_simple_paths(G, 0, 3)) >>> print(paths) [[0, 1, 2, 3], [0, 6, 5, 4, 3]] You can use this function to efficiently compute the k shortest/best paths between two nodes. >>> from itertools import islice >>> def k_shortest_paths(G, source, target, k, weight=None): ... return list(islice(nx.shortest_simple_paths(G, source, target, weight=weight), k)) >>> for path in k_shortest_paths(G, 0, 3, 2): ... print(path) [0, 1, 2, 3] [0, 6, 5, 4, 3] Notes ----- This procedure is based on algorithm by Jin Y. Yen [1]_. Finding the first $K$ paths requires $O(KN^3)$ operations. See Also -------- all_shortest_paths shortest_path all_simple_paths References ---------- .. [1] Jin Y. Yen, "Finding the K Shortest Loopless Paths in a Network", Management Science, Vol. 17, No. 11, Theory Series (Jul., 1971), pp. 712-716. """ if source not in G: raise nx.NodeNotFound(f"source node {source} not in graph") if target not in G: raise nx.NodeNotFound(f"target node {target} not in graph") if weight is None: length_func = len shortest_path_func = _bidirectional_shortest_path else: wt = _weight_function(G, weight) def length_func(path): return sum( wt(u, v, G.get_edge_data(u, v)) for (u, v) in zip(path, path[1:])) shortest_path_func = _bidirectional_dijkstra listA = list() listB = PathBuffer() prev_path = None while True: if not prev_path: length, path = shortest_path_func(G, source, target, weight=weight) listB.push(length, path) else: ignore_nodes = set() ignore_edges = set() for i in range(1, len(prev_path)): root = prev_path[:i] root_length = length_func(root) for path in listA: if path[:i] == root: ignore_edges.add((path[i - 1], path[i])) try: length, spur = shortest_path_func( G, root[-1], target, ignore_nodes=ignore_nodes, ignore_edges=ignore_edges, weight=weight, ) path = root[:-1] + spur listB.push(root_length + length, path) except nx.NetworkXNoPath: pass ignore_nodes.add(root[-1]) if listB: path = listB.pop() yield path listA.append(path) prev_path = path else: break
def a_star(G, source, target, heuristic="None", weight="weight"): hd = heapdict() if source not in G or target not in G: msg = f"Either source {source} or target {target} is not in G" raise nx.NodeNotFound(msg) if heuristic is None: # The default heuristic is h=0 - same as Dijkstra's algorithm def heuristic(u, v): return 0 weight = _weight_function(G, weight) for node in G.nodes: hd[node] = [INF, INF, (None, None)] hd[source] = [0, 0, (None, None)] explored = {} enqueued = {} while len(hd) != 0: temp = hd.popitem() curnode = temp[0] priority = temp[1][0] cost = temp[1][1] parent = temp[1][2] if curnode == target: path = [curnode] node = parent while node is not None: path.append(node) node = explored[node] if node == source: path.append(node) path.reverse() return path if curnode in explored: if explored[curnode] is None: continue qcost, h = enqueued[curnode] if qcost < cost: continue explored[curnode] = parent for neighbor, w in G[curnode].items(): if neighbor == target and source == curnode: return [curnode, target] if neighbor in enqueued: qcost, h = enqueued[neighbor] else: h = heuristic(neighbor, target) edge_cost = weight(curnode, neighbor, w) if neighbor not in explored: priority, neighbor_cost, parent = hd[neighbor] if neighbor_cost > cost + edge_cost: hd[neighbor] = [ cost + edge_cost + h, cost + edge_cost, curnode ] enqueued[neighbor] = cost + edge_cost, h raise nx.NetworkXNoPath(f"Node {target} not reachable from {source}")
def beam_path(G, source, target, heuristic, weight='weight'): """Returns a list of nodes in a shortest path between source and target using the Greedy Best First algorithm. There may be more than one shortest path. This returns only one. Parameters ---------- G : NetworkX graph source : node Starting node for path target : node Ending node for path heuristic : function A function to evaluate the estimate of the distance from the a node to the target. The function takes two nodes arguments and must return a number. weight: string, optional (default='weight') Edge data key corresponding to the edge weight. Weight data are not used here, since greedy algorithm uses as function values only heuristic ones Raises ------ NetworkXNoPath If no path exists between source and target. See Also -------- shortest_path, dijkstra_path """ if source not in G or target not in G: msg = f"Either source {source} or target {target} is not in G" raise nx.NodeNotFound(msg) push = heappush pop = heappop weight = _weight_function(G, weight) # The queue stores priority, node, cost to reach, and parent. # Uses Python heapq to keep in priority order. # Add a counter to the queue to prevent the underlying heap from # attempting to compare the nodes themselves. The hash breaks ties in the # priority and is guaranteed unique for all nodes in the graph. c = count() queue = [(0, next(c), source, 0, None)] # Maps enqueued nodes to distance of discovered paths and the # computed heuristics to target. We avoid computing the heuristics # more than once and inserting the node into the queue too many times. enqueued = {} # Maps explored nodes to parent closest to the source. explored = {} # Boolean used for memorize if a path was already found found = 0 beampath = [] # best_path returns the best one based on each total weight def best_path(path1, path2): cost1 = sum( weight(u, n, G[u][n]) for u, n in zip(path1[:-1], path1[1:])) cost2 = sum( weight(u, n, G[u][n]) for u, n in zip(path2[:-1], path2[1:])) if cost1 == min(cost1, cost2): return path1 else: return path2 def successors(n): return iter(sorted(G.neighbors(n), key=heuristic, reverse=True)[:2]) while queue: # Pop the smallest item from queue. _, __, curnode, dist, parent = pop(queue) if curnode == target: path = [curnode] node = parent while node is not None: path.append(node) print(node) node = explored[node] path.reverse() if found: best = best_path(path, beampath) beampath = best else: found = 1 beampath = path if curnode in explored: # Do not override the parent of starting node if explored[curnode] is None: continue # Skip bad paths that were enqueued before finding a better one queue_cost, h = enqueued[curnode] if queue_cost < dist: continue explored[curnode] = parent # TODO: Aggiungere STACK managment, uno stack per nodo, si esplora lo stack e si passa al nodo successivo print("Nodo corrente: " + curnode) for v in successors(curnode): print("Nodo successore: " + v) for neighbor, w in G[v].items(): print("Nodo vicino: " + neighbor) node_cost = dist + weight(curnode, neighbor, w) if neighbor in enqueued: queue_cost, h = enqueued[neighbor] if queue_cost <= node_cost: continue else: h = heuristic(target) enqueued[neighbor] = node_cost, h push(queue, (node_cost + h, next(c), neighbor, node_cost, curnode)) if beampath is None: raise nx.NetworkXNoPath(f"Node {target} not reachable from {source}") else: return beampath
def _calc_distance(graph, path, weight="weight") -> float: weight = _weight_function(graph, weight) return sum(weight(u, v, graph[u][v]) for u, v in zip(path[:-1], path[1:]))
def all_pairs_dijkstra_path_length(G, weight="weight", disjunction=sum): """Compute shortest path lengths between all nodes in a weighted graph. Parameters ---------- G : NetworkX graph weight : string or function If this is a string, then edge weights will be accessed via the edge attribute with this key (that is, the weight of the edge joining `u` to `v` will be ``G.edges[u, v][weight]``). If no such edge attribute exists, the weight of the edge is assumed to be one. If this is a function, the weight of an edge is the value returned by the function. The function must accept exactly three positional arguments: the two endpoints of an edge and the dictionary of edge attributes for that edge. The function must return a number. disjunction: function (default=sum) Whether to sum paths or use the max value. Use `sum` for metric and `max` for ultrametric. Returns ------- distance : iterator (source, dictionary) iterator with dictionary keyed by target and shortest path length as the key value. Examples -------- >>> G = nx.path_graph(5) >>> length = dict(all_pairs_dijkstra_path_length(G)) >>> for node in [0, 1, 2, 3, 4]: ... print(f"1 - {node}: {length[1][node]}") 1 - 0: 1 1 - 1: 0 1 - 2: 1 1 - 3: 2 1 - 4: 3 >>> length[3][2] 1 >>> length[2][2] 0 Notes ----- Edge weight attributes must be numerical. Distances are calculated as sums of weighted edges traversed. The dictionary returned only has keys for reachable node pairs. """ weight_function = _weight_function(G, weight) for n in G: yield (n, single_source_dijkstra_path_length( G, source=n, weight_function=weight_function, disjunction=disjunction))