#! /usr/bin/env python3 # def graph_tools_test ( ): #*****************************************************************************80 # ## graph_tools_test() tests graph_tools(). # # Licensing: # # This code is distributed under the GNU LGPL license. # # Modified: # # 04 April 2023 # # Author: # # Original code by Bernd Klein, # Modifications by John Burkardt # # Reference: # # Bernd Klein, # Graph Theory and Graphs in Python, # https://python-course.eu/applications-python/graphs-python.php # print ( '' ) print ( 'graph_tools_test():' ) print ( ' Some simple functions for a directed graph ("digraph")' ) print ( ' defined using a Python dictionary:' ) print ( ' G = { from0: [ to0, to1, ... tok], from1:[], ...}' ) G = digraph_example1 ( ) print ( '' ) print ( ' The digraph:' ) print ( G ) nodes = digraph_nodes_all ( G ) print ( '' ) print ( ' The nodes:' ) print ( nodes ) edges = digraph_edges_all ( G ) print ( '' ) print ( ' The edges:' ) print ( edges ) digraph_plot ( G ) isolated = digraph_find_isolated_nodes ( G ) print ( '' ) print ( ' The isolated nodes:' ) print ( isolated ) print ( '' ) print ( ' Add node "Denver":' ) G = digraph_add_node ( G, 'Denver' ) print ( ' Add node "h":' ) G = digraph_add_node ( G, 'Pittsburgh' ) print ( ' Updated node list:' ) nodes = digraph_nodes_all ( G ) print ( nodes ) print ( '' ) print ( ' Add edge "Boston->Pittsburgh":' ) G = digraph_add_edge ( G, [ 'Boston', 'Pittsburgh' ] ) edges = digraph_edges_all ( G ) print ( ' Updated edge list.' ) print ( edges ) print ( '' ) print ( ' List all edges from node "Boston":' ) edges = digraph_edges_node_from ( G, 'Boston' ) print ( edges ) print ( '' ) print ( ' List all edges to node "New York":' ) edges = digraph_edges_node_to ( G, 'New York' ) print ( edges ) # # "reach" # print ( '' ) print ( ' What nodes can we reach from "Boston"?' ) reach = digraph_reach_node ( G, 'Boston' ) print ( reach ) print ( '' ) print ( ' What nodes can we reach from "Chicago"?' ) reach = digraph_reach_node ( G, 'Chicago' ) print ( reach ) # # Find a path from one node to another. # print ( '' ) print ( ' Seek path from "Boston" to "Phoenix"' ) path = digraph_path_find ( G, "Boston", "Phoenix" ) print ( path ) print ( '' ) print ( ' Seek path from "New York" to "Boston"' ) path = digraph_path_find ( G, "New York", "Boston" ) print ( path ) # # Shortest path. # print ( '' ) print ( ' Seek shortest path from "Boston" to "Phoenix"' ) path = digraph_shortest_path ( G, "Boston", "Phoenix" ) print ( path ) # # Terminate. # print ( '' ) print ( 'graph_tools_test():' ) print ( ' Normal end of execution.' ) return def digraph_add_edge ( G, e ): n0 = e[0] n1 = e[1] if ( n0 in G ): G[n0].append ( n1 ) else: G[n0] = [ n1 ] return G def digraph_add_node ( G, n ): if ( n not in G ): G[n] = [] else: print ( 'Node', n, 'is already in the digraph.' ) return G def digraph_edges_all ( G ): edges = [] for node in G.keys ( ): for dest in G[node]: edges.append ( [ node, dest ] ) return edges def digraph_edges_node_from ( G, node ): edges = [] if ( node in G.keys ( ) ): for dest in G[node]: edges.append ( [ node, dest ] ) else: print ( node, 'is not a node of this digraph!' ) return edges def digraph_edges_node_to ( G, node ): edges = [] for src in G.keys ( ): if ( node in G[src] ): edges.append ( [ src, node ] ) return edges def digraph_example1 ( ): G = { 'Boston' : [ 'Providence', 'New York' ], 'Providence' : [ 'Boston', 'New York' ], 'New York' : [ 'Chicago' ], 'Chicago' : [ 'Denver', 'Phoenix' ], 'Denver' : [ 'Phoenix', 'New York' ], 'Phoenix' : [], 'Los Angeles' : [ 'Boston' ] } return G def digraph_find_isolated_nodes ( G ): isolated = list() for node in G: if ( not G[node] ): isolated.append ( node ) return isolated def digraph_nodes_all ( G ): nodes = list ( G.keys ( ) ) return nodes def digraph_path_find ( G, start_node, end_node, path = None ): if ( path == None ): path = [] path = path + [ start_node ] if ( start_node == end_node ): return path if ( start_node not in G ): return None for node in G[start_node]: if ( node not in path ): extended_path = digraph_path_find ( G, node, end_node, path ) if ( extended_path ): return extended_path return None def digraph_plot ( G ): from graphviz import Digraph edges = digraph_edges_all ( G ) nodes = digraph_nodes_all ( G ) dot = Digraph ( format = 'png' ) for n in nodes: dot.node ( n ) for e in edges: dot.edge ( e[0], e[1] ) dot.render ( 'digraph.dot', view = False ) filename = 'digraph.dot.png' print ( ' Graphics saved as "%s"' % ( filename ) ) return def digraph_reach_node ( G, node ): # ## digraph_reach_node() returns the nodes reachable from a given node. # reach = [ node ] more = True while ( more ): more = False for n in reach: for dest in G[n]: if ( not dest in reach ): reach = reach + [ dest ] more = True return reach def digraph_shortest_path ( graph, node1, node2 ): path_list = [[node1]] path_index = 0 # This set keeps track of previously visited nodes previous_nodes = {node1} if node1 == node2: return path_list[0] while path_index < len(path_list): current_path = path_list[path_index] last_node = current_path[-1] next_nodes = graph[last_node] # Search goal node if node2 in next_nodes: current_path.append(node2) return current_path # Add new paths for next_node in next_nodes: if not next_node in previous_nodes: new_path = current_path[:] new_path.append(next_node) path_list.append(new_path) # To avoid backtracking previous_nodes.add(next_node) # Continue to next path in list path_index += 1 # No path was found return [] def timestamp ( ): #*****************************************************************************80 # ## timestamp() prints the date as a timestamp. # # Licensing: # # This code is distributed under the GNU LGPL license. # # Modified: # # 21 August 2019 # # Author: # # John Burkardt # import time t = time.time ( ) print ( time.ctime ( t ) ) return if ( __name__ == "__main__" ): timestamp ( ) graph_tools_test ( ) timestamp ( )