## Introduction

There are two outstanding algorithms for computing the shortest paths from a source node in a graph. These two algorithms are the Dijkstra’s algorihtm and the Bellman-Ford algorithm. The two algorithms converges to an identical sink tree rooted at the source node.

### Dijkstra’s Algorithm

Below is a Python implementation of the Dijkstra’s algorithm. This implementation requires that the graph is connected otherwise the algorithm will be in an infinite loop.

#
# dijkstra.py
#
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt

def dijkstra(graph, srcid):
node_list = []            # list of nodes to work on
dist_to_src = dict()      # distance to source on shortest paths
prec_node_to_dst = dict() # preceding node to destination on shortest paths
for node in graph.nodes:
dist_to_src[node] = float('inf')
prec_node_to_dst[node] = None
node_list.append(node)
dist_to_src[list(graph.nodes)[srcid]] = 0

while node_list:
(curr_node,path_cost) = \
min([(node,dist_to_src[node]) \
for node in node_list], key = lambda t: t)
print('(current node,path cost to source) = ', (curr_node,path_cost))
node_list.remove(curr_node)
print('node_list = ', node_list)

for neighbor in graph.neighbors(curr_node):
if alt_cost < dist_to_src[neighbor]:
# path to the neighbor with shorter dist_to_srcance found
dist_to_src[neighbor] = alt_cost
prec_node_to_dst[neighbor] = curr_node
print('dist_to_src = ', dist_to_src)
print('prec_node_to_dst = ', prec_node_to_dst)
return dist_to_src, prec_node_to_dst

def plot_graph(graph):
plt.subplot(121)
pos = nx.spring_layout(graph)
nx.draw(graph, pos, with_labels=True)
edge_labels=dict([((u,v,),d['cost']) for u,v,d in graph.edges(data=True)])
nx.draw_networkx_edge_labels(graph, pos, edge_labels=edge_labels, \
label_pos=0.3, font_size=7)

if __name__=="__main__":

graph = nx.MultiDiGraph()

for i, node in nodesDf.iterrows():

for i,edge in edgesDf.iterrows():

nodeName = 'v1'
srcid = [i for i,name in enumerate(graph.nodes()) if name == nodeName]
dist_to_src,prec_node_to_dst = dijkstra(graph, srcid)
print('dist_to_src = ', dist_to_src)
print('prec_node_to_dst = ', prec_node_to_dst)

plot_graph(graph)

# show graph. this is blocking
plt.show()


The program takes a graph edge file and a graph node file, both of them are in the CSV format (i.e., the comma-separated-value format). Below is an example of a graph edge file called graph_edges.csv.

src,dst,cost
v1,v2,2
v2,v1,3
v1,v4,1
v4,v1,7
v2,v3,3
v3,v2,6
v4,v2,2
v2,v4,2
v4,v3,3
v3,v4,3
v4,v5,1
v5,v4,1
v3,v6,5
v6,v3,8
v3,v5,1
v5,v3,1
v5,v6,2
v6,v5,4


The following is an example of a graph node file called graph_nodes.csv.

id,x,y
v1,0,0
v2,10,10
v3,20,10
v4,10,-10
v5,20,-10
v6,0,30


Notice that columns x and y are unused in the program above and can be safely removed. The intention of these two columns is to control the layout of the graph when we plot it.

### Exercise and Exploration

The dijkstra.py program finds the shortest paths from a source node to all nodes in a graph. What are the challenges to apply the algorithm like this to solve the routing problem?

### Programming

The dijkstra.py returns two list, one is the list of the costs of the shortest paths to all node in the graph from the source node (i.e., the dist_to_src in the program), the other the list of the preceding node to the desination node along a shortest path (i.e., the prec_node_to_dst. Following the Bellman’s optimality we can obtain the shortest path to all destination nodes from the source. Here are two exercises,

1. Add a function to the dijkstra.py program to build the graph of the sink tree from the source node, e.g., the function with the following interface,
build_shortest_paths_tree(graph, prec_node_to_dst)

2. What minor modification can you apply to the dijkstra.py program so that the program also works for graphs that are not connected.

### Bellman-Ford Algorithm

Below is a Python implementation of the Bellman-Ford algorithm.

import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt

def bellmanford(graph, srcid):
node_dict = dict()        # list of nodes to work on
dist_to_src = dict()      # distance to source on shortest paths
prec_node_to_dst = dict() # preceding node to destination on shortest paths
for node in graph.nodes:
dist_to_src[node] = float('inf')
prec_node_to_dst[node] = None
node_dict[str(node)] = node
dist_to_src[list(graph.nodes)[srcid]] = 0

for node_key in node_dict:
node_u = node_dict[node_key]
edges_v = graph.out_edges(node_u, data=True)
for edge in edges_v:
edge_cost = edge['cost']
node_v = node_dict[str(edge)]
distance = dist_to_src[node_u] + edge_cost
if distance < dist_to_src[node_v]:
dist_to_src[node_v] = distance
prec_node_to_dst[node_v] = node_u

# logic to detect negative cycle ignored

return dist_to_src, prec_node_to_dst

def plot_graph(graph):
plt.subplot(121)
pos = nx.spring_layout(graph)
nx.draw(graph, pos, with_labels=True)
edge_labels=dict([((u,v,),d['cost']) for u,v,d in graph.edges(data=True)])
nx.draw_networkx_edge_labels(graph, pos, edge_labels=edge_labels, \
label_pos=0.3, font_size=7)

if __name__=="__main__":

graph = nx.MultiDiGraph()

for i, node in nodesDf.iterrows():

for i,edge in edgesDf.iterrows():

nodeName = 'v1'
srcid = [i for i,name in enumerate(graph.nodes()) if name == nodeName]
dist_to_src,prec_node_to_dst = bellmanford(graph, srcid)
print('dist_to_src = ', dist_to_src)
print('prec_node_to_dst = ', prec_node_to_dst)

plot_graph(graph)

# show graph. this is blocking
plt.show()


### Execise and Exploration

The Bellman-Ford algorithms permits a graph to have negative weight. It can detect whether there is a negative cycle reachable from a source node. The logic is negelected in the above program.

1. What is a negative cycle? Can you give a graph that contains a negative cycle?
2. Can you add logic to the graph to detect the existence of a negative cycle?