pebble/tools/generate_pdcs/graph.py

145 lines
5.1 KiB
Python
Raw Permalink Normal View History

# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Credit to Bernd Klein for this graph class.
http://www.python-course.eu/graphs_python.php
Modifications by kmisquitta:
1) Added support for pretty printing
2) Added function to return a vertex's neighbours
3) Added support for traversing to a vertex more than once in find_all_paths
4) Removed support for adding edges that are sets
5) Removed support for multiple edges between two vertices
6) Added support for traversing beyond the end vertex in find_all_paths
7) Removed many unneeded features
"""
""" A Python Class
A simple Python graph class, demonstrating the essential
facts and functionalities of graphs.
"""
import pprint
def is_line_segment_in_path(path, vertex_1, vertex_2):
for i in range(len(path) - 1):
if path[i] == vertex_1 and path[i + 1] == vertex_2 \
or path[i] == vertex_2 and path[i + 1] == vertex_1:
return True
return False
class Graph(object):
def __init__(self, graph_dict={}):
""" initializes a graph object """
self.__graph_dict = graph_dict
def get_vertices(self):
""" returns the vertices of a graph """
return list(self.__graph_dict.keys())
def get_edges(self):
""" returns the edges of a graph """
return self.__generate_edges()
def get_neighbours(self, vertex):
""" returns the neighbours of a vertex """
return list(self.__graph_dict[vertex])
def add_vertex(self, vertex):
""" If the vertex "vertex" is not in
self.__graph_dict, a key "vertex" with an empty
list as a value is added to the dictionary.
Otherwise nothing has to be done.
"""
if vertex not in self.__graph_dict:
self.__graph_dict[vertex] = []
def add_edge(self, edge):
""" assumes that edge is of type tuple or list """
if len(edge) < 2:
return
vertex1 = edge[0]
vertex2 = edge[1]
if vertex1 in self.__graph_dict:
if not (vertex2 in self.get_neighbours(vertex1)):
self.__graph_dict[vertex1].append(vertex2)
else:
self.__graph_dict[vertex1] = [vertex2]
def __generate_edges(self):
""" A static method generating the edges of the
graph "graph". Edges are represented as sets
with one (a loop back to the vertex) or two
vertices
"""
edges = []
for vertex in self.__graph_dict:
for neighbour in self.__graph_dict[vertex]:
if {vertex, neighbour} not in edges:
edges.append({vertex, neighbour})
return edges
def __str__(self):
res = "vertices: "
for k in self.__graph_dict:
res += str(k) + " "
res += "\nedges: "
for edge in self.__generate_edges():
res += str(edge) + " "
return res
def find_all_paths(self, start_vertex, end_vertex, path=[]):
""" Recursive function that finds all paths from the start vertex to the end vertex.
Starts from the start vertex and traverses through vertices until the end vertex is reached.
If there are untraversed edges when the end vertex is reached, will continue traversing
to check for paths back to the end vertex (loops).
There is no limit to how many times a vertex can be traversed.
An edge may be traversed only once.
"""
graph = self.__graph_dict
paths = []
path = path + [start_vertex]
if start_vertex == end_vertex:
# Check if additional traversals is possible
neighbours = self.get_neighbours(end_vertex)
no_possible_traversals = True
for neighbour in neighbours:
if not is_line_segment_in_path(path, end_vertex, neighbour):
no_possible_traversals = False
break
if no_possible_traversals: # Base case
return [path]
else:
paths.append(path) # Add current path, continue finding
if start_vertex not in graph:
return []
for vertex in graph[start_vertex]:
if not is_line_segment_in_path(path, vertex, start_vertex):
extended_paths = self.find_all_paths(vertex,
end_vertex,
path)
for p in extended_paths:
paths.append(p)
return paths
def prettyprint(self):
pprint.pprint(self.__graph_dict)