0% found this document useful (0 votes)
385 views

Graphs in Python: Origins of Graph Theory

This document discusses graphs and graph theory. It begins with the origins of graph theory in solving the Königsberg bridge problem in the 18th century. It then provides an introduction to graph theory concepts like vertices, edges, directed and undirected graphs. It discusses representing graphs in Python using dictionaries and provides an example graph class implementation with methods for vertices, edges, adding vertices and edges. Finally, it discusses finding paths in graphs and provides a method for finding a path between vertices.

Uploaded by

tuadongsong
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
385 views

Graphs in Python: Origins of Graph Theory

This document discusses graphs and graph theory. It begins with the origins of graph theory in solving the Königsberg bridge problem in the 18th century. It then provides an introduction to graph theory concepts like vertices, edges, directed and undirected graphs. It discusses representing graphs in Python using dictionaries and provides an example graph class implementation with methods for vertices, edges, adding vertices and edges. Finally, it discusses finding paths in graphs and provides a method for finding a path between vertices.

Uploaded by

tuadongsong
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 21

GRAPHSINPYTHON

ORIGINSOFGRAPHTHEORY
Beforewestartwiththeactualimplementationsof
graphsinPythonandbeforewestartwiththe
introductionofPythonmodulesdealingwithgraphs,we
wanttodevoteourselvestotheoriginsofgraphtheory.
TheoriginstakeusbackintimetotheKnigsbergofthe
18thcentury.KnigsbergwasacityinPrussiathattime.
TheriverPregelflowedthroughthetown,creatingtwo
islands.Thecityandtheislandswereconnectedby
sevenbridgesasshown.Theinhabitantsofthecitywere
movedbythequestion,ifitwaspossibletotakeawalk
throughthetownbyvisitingeachareaofthetownand
crossingeachbridgeonlyonce?Everybridgemusthave
beencrossedcompletely,i.e.itisnotallowedtowalk
halfwayontoabridgeandthenturnaroundandlater
crosstheotherhalffromtheotherside.Thewalkneed
notstartandendatthesamespot.LeonhardEuler
solvedtheproblemin1735byprovingthatitisnot
possible.Hefoundoutthatthechoiceofarouteinside
eachlandareaisirrelevantandthattheonlythingwhich
matteredistheorder(orthesequence)inwhichthe
bridgesarecrossed.Hehadformulatedanabstractionoftheproblem,eliminatingunnecessaryfactsandfocussingon
thelandareasandthebridgesconnectingthem.Thisway,hecreatedthefoundationsofgraphtheory.Ifweseea"land
area"asavertexandeachbridgeasanedge,wehave"reduced"theproblemtoagraph.

INTRODUCTIONINTOGRAPHTHEORYUSINGPYTHON
BeforewestartourtreatizeonpossiblePythonrepresentations
ofgraphs,wewanttopresentsomegeneraldefinitionsofgraphs
anditscomponents.
A"graph"1inmathematicsandcomputerscienceconsistsof
"nodes",alsoknownas"vertices".Nodesmayormaynotbe
connectedwithoneanother.Inourillustration,whichisa
pictorialrepresentationofagraph,thenode"a"isconnected
withthenode"c",but"a"isnotconnectedwith"b".The
connectinglinebetweentwonodesiscalledanedge.Ifthe
edgesbetweenthenodesareundirected,thegraphiscalledan
undirectedgraph.Ifanedgeisdirectedfromonevertex(node)
toanother,agraphiscalledadirectedgraph.Andirectededge
iscalledanarc.
Thoughgraphsmaylookverytheoretical,manypractical
problemscanberepresentedbygraphs.Theyareoftenusedto
modelproblemsorsituationsinphysics,biology,psychologyandaboveallincomputerscience.Incomputerscience,
graphsareusedtorepresentnetworksofcommunication,dataorganization,computationaldevices,theflowof
computation,
Inthelattercase,theareusedtorepresentthedataorganisation,likethefilesystemofanoperatingsystem,or
communicationnetworks.Thelinkstructureofwebsitescanbeseenasagraphaswell,i.e.adirectedgraph,becausea
linkisadirectededgeoranarc.
Pythonhasnobuiltindatatypeorclassforgraphs,butitiseasytoimplementtheminPython.Onedatatypeisideal
forrepresentinggraphsinPython,i.e.dictionaries.Thegraphinourillustrationcanbeimplementedinthefollowing
way:
graph={"a":["c"],
"b":["c","e"],
"c":["a","b","d","e"],
"d":["c"],
"e":["c","b"],
"f":[]
}

Thekeysofthedictionaryabovearethenodesofourgraph.Thecorrespondingvaluesarelistswiththenodes,which
areconnectingbyanedge.Thereisnosimplerandmoreelegantwaytorepresentagraph.
Anedgecanbeseenasa2tuplewithnodesaselements,i.e.("a","b")
Functiontogeneratethelistofalledges:
defgenerate_edges(graph):
edges=[]
fornodeingraph:
forneighbouringraph[node]:
edges.append((node,neighbour))
returnedges
print(generate_edges(graph))
Thiscodegeneratesthefollowingoutput,ifcombinedwiththepreviouslydefinedgraphdictionary:

$python3graph_simple.py
[('a','c'),('c','a'),('c','b'),('c','d'),('c','e'),('b','c'),
('b','e'),('e','c'),('e','b'),('d','c')]
Aswecansee,thereisnoedgecontainingthenode"f"."f"isanisolatednodeofourgraph.
ThefollowingPythonfunctioncalculatestheisolatednodesofagivengraph:
deffind_isolated_nodes(graph):
"""returnsalistofisolatednodes."""
isolated=[]
fornodeingraph:
ifnotgraph[node]:
isolated+=node
returnisolated
Ifwecallthisfunctionwithourgraph,alistcontaining"f"willbereturned:["f"]

GRAPHSASAPYTHONCLASS
Beforewegoonwithwritingfunctionsforgraphs,wehavea
firstgoataPythongraphclassimplementation.Ifyoulookat
thefollowinglistingofourclass,youcanseeinthe__init__
methodthatweuseadictionary"self.__graph_dict"forstoring
theverticesandtheircorrespondingadjacentvertices.
"""APythonClass
AsimplePythongraphclass,
demonstratingtheessential
factsandfunctionalitiesofgraphs.
"""
classGraph(object):
def__init__(self,graph_dict=None):
"""initializesagraphobject
IfnodictionaryorNoneisgiven,
anemptydictionarywillbeused
"""
ifgraph_dict==None:
graph_dict={}
self.__graph_dict=graph_dict
defvertices(self):
"""returnstheverticesofagraph"""

returnlist(self.__graph_dict.keys())
defedges(self):
"""returnstheedgesofagraph"""
returnself.__generate_edges()
defadd_vertex(self,vertex):
"""Ifthevertex"vertex"isnotin
self.__graph_dict,akey"vertex"withanempty
listasavalueisaddedtothedictionary.
Otherwisenothinghastobedone.
"""
ifvertexnotinself.__graph_dict:
self.__graph_dict[vertex]=[]
defadd_edge(self,edge):
"""assumesthatedgeisoftypeset,tupleorlist
betweentwoverticescanbemultipleedges!
"""
edge=set(edge)
(vertex1,vertex2)=tuple(edge)
ifvertex1inself.__graph_dict:
self.__graph_dict[vertex1].append(vertex2)
else:
self.__graph_dict[vertex1]=[vertex2]
def__generate_edges(self):
"""Astaticmethodgeneratingtheedgesofthe
graph"graph".Edgesarerepresentedassets
withone(aloopbacktothevertex)ortwo
vertices
"""
edges=[]
forvertexinself.__graph_dict:
forneighbourinself.__graph_dict[vertex]:
if{neighbour,vertex}notinedges:
edges.append({vertex,neighbour})
returnedges
def__str__(self):
res="vertices:"
forkinself.__graph_dict:
res+=str(k)+""
res+="\nedges:"
foredgeinself.__generate_edges():
res+=str(edge)+""
returnres
if__name__=="__main__":
g={"a":["d"],
"b":["c"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":[]
}
graph=Graph(g)
print("Verticesofgraph:")
print(graph.vertices())
print("Edgesofgraph:")
print(graph.edges())
print("Addvertex:")

graph.add_vertex("z")
print("Verticesofgraph:")
print(graph.vertices())

print("Addanedge:")
graph.add_edge({"a","z"})

print("Verticesofgraph:")
print(graph.vertices())
print("Edgesofgraph:")
print(graph.edges())
print('Addinganedge{"x","y"}withnewvertices:')
graph.add_edge({"x","y"})
print("Verticesofgraph:")
print(graph.vertices())
print("Edgesofgraph:")
print(graph.edges())

Ifyoustartthismodulestandalone,youwillgetthefollowingresult:
$python3graph.py
Verticesofgraph:
['a','c','b','e','d','f']
Edgesofgraph:
[{'a','d'},{'c','b'},{'c'},{'c','d'},{'c','e'}]
Addvertex:
Verticesofgraph:
['a','c','b','e','d','f','z']
Addanedge:
Verticesofgraph:
['a','c','b','e','d','f','z']
Edgesofgraph:
[{'a','d'},{'c','b'},{'c'},{'c','d'},{'c','e'},{'a','z'}]
Addinganedge{"x","y"}withnewvertices:
Verticesofgraph:
['a','c','b','e','d','f','y','z']
Edgesofgraph:
[{'a','d'},{'c','b'},{'c'},{'c','d'},{'c','e'},{'a','z'},{'y',
'x'}]

PATHSINGRAPHS
Wewanttofindnowtheshortestpathfromonenodetoanothernode.BeforewecometothePythoncodeforthis
problem,wewillhavetopresentsomeformaldefinitions.
Adjacentvertices:
Twoverticesareadjacentwhentheyarebothincidenttoacommonedge.
PathinanundirectedGraph:
ApathinanundirectedgraphisasequenceofverticesP=(v1,v2,...,vn)VxVx...xVsuchthatviisadjacentto
v{i+1}for1i<n.SuchapathPiscalledapathoflengthnfromv1tovn.
SimplePath:
Apathwithnorepeatedverticesiscalledasimplepath.
Example:
(a,c,e)isasimplepathinourgraph,aswellas(a,c,e,b).(a,c,e,b,c,d)isapathbutnotasimple
path,becausethenodecappearstwice.
Thefollowingmethodfindsapathfromastartvertextoanendvertex:

deffind_path(self,start_vertex,end_vertex,path=None):
"""findapathfromstart_vertextoend_vertex
ingraph"""
ifpath==None:
path=[]
graph=self.__graph_dict
path.append(start_vertex)
ifstart_vertex==end_vertex:
returnpath
ifstart_vertexnotingraph:
returnNone
forvertexingraph[start_vertex]:
ifvertexnotinpath:
extended_path=self.find_path(vertex,
end_vertex,
path)
ifextended_path:
returnextended_path
returnNone
Ifwesaveourgraphclassincludingthefind_pathmethodas"graphs.py",wecancheckthewayofworkingofour
find_pathfunction:
fromgraphsimportGraph
g={"a":["d"],
"b":["c"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":[]
}
graph=Graph(g)
print("Verticesofgraph:")
print(graph.vertices())
print("Edgesofgraph:")
print(graph.edges())
print('Thepathfromvertex"a"tovertex"b":')
path=graph.find_path("a","b")
print(path)
print('Thepathfromvertex"a"tovertex"f":')
path=graph.find_path("a","f")
print(path)
print('Thepathfromvertex"c"tovertex"c":')
path=graph.find_path("c","c")
print(path)
Theresultofthepreviousprogramlookslikethis:
Verticesofgraph:
['e','a','d','f','c','b']
Edgesofgraph:
[{'e','c'},{'a','d'},{'d','c'},{'b','c'},{'c'}]
Thepathfromvertex"a"tovertex"b":
['a','d','c','b']
Thepathfromvertex"a"tovertex"f":
None
Thepathfromvertex"c"tovertex"c":
['c']

Themethodfind_all_pathsfindsallthepathsbetweenastartvertextoanendvertex:
deffind_all_paths(self,start_vertex,end_vertex,path=[]):
"""findallpathsfromstart_vertexto
end_vertexingraph"""
graph=self.__graph_dict
path=path+[start_vertex]
ifstart_vertex==end_vertex:
return[path]
ifstart_vertexnotingraph:
return[]
paths=[]
forvertexingraph[start_vertex]:
ifvertexnotinpath:
extended_paths=self.find_all_paths(vertex,
end_vertex,
path)
forpinextended_paths:
paths.append(p)
returnpaths
Weslightlychangedourexamplegraphbyaddingedgesfrom"a"to"f"andfrom"f"to"d"totestthepreviously
definedmethod:
fromgraphsimportGraph
g={"a":["d","f"],
"b":["c"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":["d"]
}
graph=Graph(g)
print("Verticesofgraph:")
print(graph.vertices())
print("Edgesofgraph:")
print(graph.edges())
print('Allpathsfromvertex"a"tovertex"b":')
path=graph.find_all_paths("a","b")
print(path)
print('Allpathsfromvertex"a"tovertex"f":')
path=graph.find_all_paths("a","f")
print(path)
print('Allpathsfromvertex"c"tovertex"c":')
path=graph.find_all_paths("c","c")
print(path)
Theresultlookslikethis:
Verticesofgraph:
['d','c','b','a','e','f']
Edgesofgraph:
[{'d','a'},{'d','c'},{'c','b'},{'c'},{'c','e'},{'f','a'},{'d',
'f'}]
Allpathsfromvertex"a"tovertex"b":
[['a','d','c','b'],['a','f','d','c','b']]
Allpathsfromvertex"a"tovertex"f":

[['a','f']]
Allpathsfromvertex"c"tovertex"c":
[['c']]

DEGREE
Thedegreeofavertexvinagraphisthenumberofedges
connectingit,withloopscountedtwice.Thedegreeofavertex
visdenoteddeg(v).ThemaximumdegreeofagraphG,denoted
by(G),andtheminimumdegreeofagraph,denotedby(G),
arethemaximumandminimumdegreeofitsvertices.
Inthegraphontherightside,themaximumdegreeis5atvertex
candtheminimumdegreeis0,i.etheisolatedvertexf.
Ifallthedegreesinagrapharethesame,thegraphisaregular
graph.Inaregulargraph,alldegreesarethesame,andsowe
canspeakofthedegreeofthegraph.
Thedegreesumformula(Handshakinglemma):
vVdeg(v)=2|E|
Thismeansthatthesumofdegreesofalltheverticesisequaltothenumberofedgesmultipliedby2.Wecan
concludethatthenumberofverticeswithodddegreehastobeeven.Thisstatementisknownasthehandshaking
lemma.Thename"handshakinglemma"stemsfromapopularmathematicalproblem:Inanygroupofpeoplethe
numberofpeoplewhohaveshakenhandswithanoddnumberofotherpeoplefromthegroupiseven.
Thefollowingmethodcalculatesthedegreeofavertex:
defvertex_degree(self,vertex):
"""Thedegreeofavertexisthenumberofedgesconnecting
it,i.e.thenumberofadjacentvertices.Loopsarecounted
double,i.e.everyoccurenceofvertexinthelist
ofadjacentvertices."""
adj_vertices=self.__graph_dict[vertex]
degree=len(adj_vertices)+adj_vertices.count(vertex)
returndegree
Thefollowingmethodcalculatesalistcontainingtheisolatedverticesofagraph:
deffind_isolated_vertices(self):
"""returnsalistofisolatedvertices."""
graph=self.__graph_dict
isolated=[]
forvertexingraph:
print(isolated,vertex)
ifnotgraph[vertex]:
isolated+=[vertex]
returnisolated
ThemethodsdeltaandDeltacanbeusedtocalculatetheminimumandmaximumdegreeofavertexrespectively:
defdelta(self):
"""theminimumdegreeofthevertices"""
min=100000000
forvertexinself.__graph_dict:
vertex_degree=self.vertex_degree(vertex)
ifvertex_degree<min:
min=vertex_degree
returnmin

defDelta(self):
"""themaximumdegreeofthevertices"""
max=0
forvertexinself.__graph_dict:
vertex_degree=self.vertex_degree(vertex)
ifvertex_degree>max:
max=vertex_degree
returnmax

DEGREESEQUENCE
Thedegreesequenceofanundirectedgraphisdefinedasthesequenceofitsvertexdegreesinanonincreasingorder.
Thefollowingmethodreturnsatuplewiththedegreesequenceoftheinstancegraph:
defdegree_sequence(self):
"""calculatesthedegreesequence"""
seq=[]
forvertexinself.__graph_dict:
seq.append(self.vertex_degree(vertex))
seq.sort(reverse=True)
returntuple(seq)
Thedegreesequenceofourexamplegraphisthefollowingsequenceofintegers:(5,2,1,1,1,0).
Isomorphicgraphshavethesamedegreesequence.However,twographswiththesamedegreesequencearenot
necessarilyisomorphic.
Thereisthequestionwhetheragivendegreesequencecanberealizedbyasimplegraph.TheErdsGallaitheorem
statesthatanonincreasingsequenceofnnumbersdi(fori=1,...,n)isthedegreesequenceofasimplegraphifand
onlyifthesumofthesequenceisevenandthefollowingconditionisfulfilled:

IMPLEMENTATIONOFTHEERDSGALLAITHEOREM
Ourgraphclassseefurtherdownforacompletelistingcontainsamethod"erdoes_gallai"whichdecides,ifa
sequencefulfillstheErdsGallaitheorem.First,wecheck,ifthesumoftheelementsofthesequenceisodd.Ifsothe
functionreturnsFalse,becausetheErdsGallaitheoremcan'tbefulfilledanymore.Afterthiswecheckwiththestatic
methodis_degree_sequencewhethertheinputsequenceisadegreesequence,i.e.thattheelementsofthesequence
arenonincreasing.Thisiskindofsuperfluous,astheinputissupposedtobeadegreesequence,soyoumaydropthis
checkforefficiency.Now,wecheckinthebodyofthesecondifstatement,iftheinequationofthetheoremis
fulfilled:
@staticmethod
deferdoes_gallai(dsequence):
"""ChecksiftheconditionoftheErdoesGallaiinequality
isfullfilled
"""
ifsum(dsequence)%2:
#sumofsequenceisodd
returnFalse
ifGraph.is_degree_sequence(dsequence):
forkinrange(1,len(dsequence)+1):
left=sum(dsequence[:k])
right=k*(k1)+sum([min(x,k)forxindsequence[k:]])
ifleft>right:
returnFalse
else:
#sequenceisincreasing

returnFalse
returnTrue

Versionwithoutthesuperfluousdegreesequencetest:
@staticmethod
deferdoes_gallai(dsequence):
"""ChecksiftheconditionoftheErdoesGallaiinequality
isfullfilled
dsequencehastobeavaliddegreesequence
"""
ifsum(dsequence)%2:
#sumofsequenceisodd
returnFalse
forkinrange(1,len(dsequence)+1):
left=sum(dsequence[:k])
right=k*(k1)+sum([min(x,k)forxindsequence[k:]])
ifleft>right:
returnFalse
returnTrue

GRAPHDENSITY
Thegraphdensityisdefinedastheratioofthenumberofedgesofagivengraph,andthetotalnumberofedges,the
graphcouldhave.Inotherwords:Itmeasureshowcloseagivengraphistoacompletegraph.
Themaximaldensityis1,ifagraphiscomplete.Thisisclear,becausethemaximumnumberofedgesinagraph
dependsontheverticesandcanbecalculatedas:
max.numberofedges=*|V|*(|V|1).
Ontheotherhandtheminimaldensityis0,ifthegraphhasnoedges,i.e.itisanisolatedgraph.
Forundirectedsimplegraphs,thegraphdensityisdefinedas:

Adensegraphisagraphinwhichthenumberofedgesisclosetothemaximalnumberofedges.Agraphwithonlya
fewedges,iscalledasparsegraph.Thedefinitionforthosetwotermsisnotverysharp,i.e.thereisnoleastupper
bound(supremum)forasparsedensityandnogreatestlowerbound(infimum)fordefiningadensegraph.
TheprecisestmathematicalnotationusesthebigOnotation:
SparseGraph:DenseGraph:
AdensegraphisagraphG=(V,E)inwhich|E|=(|V|2).
"density"isamethodofourclasstocalculatethedensityofagraph:
defdensity(self):
"""methodtocalculatethedensityofagraph"""
g=self.__graph_dict
V=len(g.keys())
E=len(self.edges())
return2.0*E/(V*(V1))
Wecantestthismethodwiththefollowingscript.
fromgraph2importGraph
g={"a":["d","f"],
"b":["c","b"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":["a"]
}

complete_graph={
"a":["b","c"],
"b":["a","c"],
"c":["a","b"]
}
isolated_graph={
"a":[],
"b":[],
"c":[]
}
graph=Graph(g)
print(graph.density())
graph=Graph(complete_graph)
print(graph.density())
graph=Graph(isolated_graph)
print(graph.density())
Acompletegraphhasadensityof1andisolatedgraphhasadensityof0,aswecanseefromtheresultsofthe
previoustestscript:
$pythontest_density.py
0.466666666667
1.0
0.0

CONNECTEDGRAPHS
Agraphissaidtobeconnectedifeverypairofverticesinthe
graphisconnected.Theexamplegraphontherightsideisa
connectedgraph.
Itpossibletodeterminewithasimplealgorithmwhetheragraph
isconnected:
1.ChooseanarbitrarynodexofthegraphGasthestarting
point
2.DeterminethesetAofallthenodeswhichcanbe
reachedfromx.
3.IfAisequaltothesetofnodesofG,thegraphis
connectedotherwiseitisdisconnected.
Weimplementamethodis_connectedtocheckifagraphisa
connectedgraph.Wedon'tputemphasisonefficiencybutonreadability.
defis_connected(self,
vertices_encountered=None,
start_vertex=None):
"""determinesifthegraphisconnected"""
ifvertices_encounteredisNone:
vertices_encountered=set()
gdict=self.__graph_dict
vertices=list(gdict.keys())#"list"necessaryinPython3
ifnotstart_vertex:
#chosseavertexfromgraphasastartingpoint
start_vertex=vertices[0]
vertices_encountered.add(start_vertex)
iflen(vertices_encountered)!=len(vertices):

forvertexingdict[start_vertex]:
ifvertexnotinvertices_encountered:
ifself.is_connected(vertices_encountered,vertex):
returnTrue
else:
returnTrue
returnFalse

Ifyouaddthismethodtoourgraphclass,wecantestitwiththefollowingscript.Assumingthatyousavethegraph
classasgraph2.py:
fromgraph2importGraph
g={"a":["d"],
"b":["c"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":[]
}
g2={"a":["d","f"],
"b":["c"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":["a"]
}
g3={"a":["d","f"],
"b":["c","b"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":["a"]
}
graph=Graph(g)
print(graph)
print(graph.is_connected())
graph=Graph(g2)
print(graph)
print(graph.is_connected())
graph=Graph(g3)
print(graph)
print(graph.is_connected())

AconnectedcomponentisamaximalconnectedsubgraphofG.Eachvertexbelongstoexactlyoneconnected
component,asdoeseachedge.

DISTANCEANDDIAMETEROFAGRAPH
Thedistance"dist"betweentwoverticesinagraphisthelengthoftheshortestpathbetweenthesevertices.No
backtracks,detours,orloopsareallowedforthecalculationofadistance.
Inourexamplegraphontheright,thedistancebetweenthevertexaandthevertexfis3,i.e.dist(a,f)=3,becausethe
shortestwayisviatheverticescande(orcandbalternatively).
Theeccentricityofavertexsofagraphgisthemaximaldistancetoeveryothervertexofthegraph:

e(s)=max({dist(s,v)|vV})
(Visthesetofallverticesofg)
Thediameterdofagraphisdefinedasthemaximum
eccentricityofanyvertexinthegraph.Thismeansthatthe
diameteristhelengthoftheshortestpathbetweenthemost
distancednodes.Todeterminethediameterofagraph,firstfind
theshortestpathbetweeneachpairofvertices.Thegreatest
lengthofanyofthesepathsisthediameterofthegraph.
Wecandirectlyseeinourexamplegraphthatthediameteris3,
becausetheminimallengthbetweenaandfis3andthereisno
otherpairofverticeswithalongerpath.
Thefollowingmethodimplementsanalgorithmtocalculatethe
diameter.
defdiameter(self):
"""calculatesthediameterofthegraph"""

v=self.vertices()
pairs=[(v[i],v[j])foriinrange(len(v)1)forjinrange(i+1,
len(v))]
smallest_paths=[]
for(s,e)inpairs:
paths=self.find_all_paths(s,e)
smallest=sorted(paths,key=len)[0]
smallest_paths.append(smallest)
smallest_paths.sort(key=len)
#longestpathisattheendoflist,
#i.e.diametercorrespondstothelengthofthispath
diameter=len(smallest_paths[1])
returndiameter
Wecancalculatethediameterofourexamplegraphwiththefollowingscript,assumingagain,thatourcomplete
graphclassissavedasgraph2.py:
fromgraph2importGraph
g={"a":["c"],
"b":["c","e","f"],
"c":["a","b","d","e"],
"d":["c"],
"e":["b","c","f"],
"f":["b","e"]
}
graph=Graph(g)
diameter=graph.diameter()
print(diameter)
Itwillprintoutthevalue3.

THECOMPLETEPYTHONGRAPHCLASS
InthefollowingPythoncode,youfindthecompletePythonClassModulewithallthediscussedmethodes:graph2.py

TREE/FOREST

Atreeisanundirectedgraphwhichcontainsnocycles.Thismeansthatanytwoverticesofthegraphareconnected
byexactlyonesimplepath.
Aforestisadisjointunionoftrees.Contrarytoforestsinnature,aforestingraphtheorycanconsistofasingletree!
Agraphwithonevertexandnoedgeisatree(andaforest).
Anexampleofatree:

Whilethepreviousexampledepictsagraphwhichisatreeandforest,thefollowingpictureshowsagraphwhich
consistsoftwotrees,i.e.thegraphisaforestbutnotatree:

OVERVIEWOFFORESTS:
Withonevertex:

Forestgraphswithtwovertices:

Forestgraphswiththreevertices:

SPANNINGTREE
AspanningtreeTofaconnected,undirectedgraphGisasubgraphG'ofG,whichisatree,andG'containsallthe
verticesandasubsetoftheedgesofG.G'containsalltheedgesofG,ifGisatreegraph.Informally,aspanningtree
ofGisaselectionofedgesofGthatformatreespanningeveryvertex.Thatis,everyvertexliesinthetree,butno
cycles(orloops)arecontained.
Example:
Afullyconnectedgraph:

Twospanningtreesfromthepreviousfullyconnectedgraph:

HAMILTONIANGAME

AnHamiltonianpathisapathinanundirectedordirectedgraphthatvisitseachvertexexactlyonce.AHamiltonian
cycle(orcircuit)isaHamiltonianpaththatisacycle.
Noteforcomputerscientists:Generally,itisnotnotpossibletodetermine,whethersuchpathsorcyclesexistin
arbitrarygraphs,becausetheHamiltonianpathproblemhasbeenproventobeNPcomplete.
ItisnamedafterWilliamRowanHamiltonwhoinventedthesocalled"icosiangame",orHamilton'spuzzle,which
involvesfindingaHamiltoniancycleintheedgegraphofthedodecahedron.Hamiltonsolvedthisproblemusingthe
icosiancalculus,analgebraicstructurebasedonrootsofunitywithmanysimilaritiestothequaternions,whichhealso
invented.

COMPLETELISTINGOFTHEGRAPHCLASS
"""APythonClass
AsimplePythongraphclass,demonstratingthe
essential
factsandfunctionalitiesofgraphs.
"""
classGraph(object):
def__init__(self,graph_dict=None):
"""initializesagraphobject
IfnodictionaryorNoneisgiven,anemptydictionarywillbe
used
"""
ifgraph_dict==None:
graph_dict={}
self.__graph_dict=graph_dict
defvertices(self):
"""returnstheverticesofagraph"""
returnlist(self.__graph_dict.keys())
defedges(self):
"""returnstheedgesofagraph"""
returnself.__generate_edges()
defadd_vertex(self,vertex):
"""Ifthevertex"vertex"isnotin
self.__graph_dict,akey"vertex"withanempty
listasavalueisaddedtothedictionary.
Otherwisenothinghastobedone.
"""
ifvertexnotinself.__graph_dict:
self.__graph_dict[vertex]=[]
defadd_edge(self,edge):
"""assumesthatedgeisoftypeset,tupleorlist
betweentwoverticescanbemultipleedges!
"""
edge=set(edge)
vertex1=edge.pop()
ifedge:
#notaloop
vertex2=edge.pop()
else:
#aloop
vertex2=vertex1
ifvertex1inself.__graph_dict:
self.__graph_dict[vertex1].append(vertex2)
else:
self.__graph_dict[vertex1]=[vertex2]
def__generate_edges(self):
"""Astaticmethodgeneratingtheedgesofthe
graph"graph".Edgesarerepresentedassets
withone(aloopbacktothevertex)ortwo
vertices
"""
edges=[]
forvertexinself.__graph_dict:
forneighbourinself.__graph_dict[vertex]:
if{neighbour,vertex}notinedges:

edges.append({vertex,neighbour})
returnedges
def__str__(self):
res="vertices:"
forkinself.__graph_dict:
res+=str(k)+""
res+="\nedges:"
foredgeinself.__generate_edges():
res+=str(edge)+""
returnres
deffind_isolated_vertices(self):
"""returnsalistofisolatedvertices."""
graph=self.__graph_dict
isolated=[]
forvertexingraph:
print(isolated,vertex)
ifnotgraph[vertex]:
isolated+=[vertex]
returnisolated
deffind_path(self,start_vertex,end_vertex,path=[]):
"""findapathfromstart_vertextoend_vertex
ingraph"""
graph=self.__graph_dict
path=path+[start_vertex]
ifstart_vertex==end_vertex:
returnpath
ifstart_vertexnotingraph:
returnNone
forvertexingraph[start_vertex]:
ifvertexnotinpath:
extended_path=self.find_path(vertex,
end_vertex,
path)
ifextended_path:
returnextended_path
returnNone

deffind_all_paths(self,start_vertex,end_vertex,path=[]):
"""findallpathsfromstart_vertexto
end_vertexingraph"""
graph=self.__graph_dict
path=path+[start_vertex]
ifstart_vertex==end_vertex:
return[path]
ifstart_vertexnotingraph:
return[]
paths=[]
forvertexingraph[start_vertex]:
ifvertexnotinpath:
extended_paths=self.find_all_paths(vertex,
end_vertex,
path)
forpinextended_paths:
paths.append(p)
returnpaths
defis_connected(self,
vertices_encountered=None,
start_vertex=None):
"""determinesifthegraphisconnected"""
ifvertices_encounteredisNone:
vertices_encountered=set()
gdict=self.__graph_dict
vertices=list(gdict.keys())#"list"necessaryinPython3
ifnotstart_vertex:
#chosseavertexfromgraphasastartingpoint

#chosseavertexfromgraphasastartingpoint
start_vertex=vertices[0]
vertices_encountered.add(start_vertex)
iflen(vertices_encountered)!=len(vertices):
forvertexingdifromgraph2importGraph
g={"a":["d"],
"b":["c"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":[]
}
graph=Graph(g)
print(graph)
fornodeingraph.vertices():
print(graph.vertex_degree(node))
print("Listofisolatedvertices:")
print(graph.find_isolated_vertices())
print("""Apathfrom"a"to"e":""")
print(graph.find_path("a","e"))
print("""Allpathesfrom"a"to"e":""")
print(graph.find_all_paths("a","e"))
print("Themaximumdegreeofthegraphis:")
print(graph.Delta())
print("Theminimumdegreeofthegraphis:")
print(graph.delta())
print("Edges:")
print(graph.edges())
print("DegreeSequence:")
ds=graph.degree_sequence()
print(ds)
fullfilling=[[2,2,2,2,1,1],
[3,3,3,3,3,3],
[3,3,2,1,1]
]
non_fullfilling=[[4,3,2,2,2,1,1],
[6,6,5,4,4,2,1],
[3,3,3,1]]
forsequenceinfullfilling+non_fullfilling:
print(sequence,Graph.erdoes_gallai(sequence))
print("Addvertex'z':")
graph.add_vertex("z")
print(graph)
print("Addedge('x','y'):")
graph.add_edge(('x','y'))
print(graph)
print("Addedge('a','d'):")
graph.add_edge(('a','d'))
print(graph)
ct[start_vertex]:
ifvertexnotinvertices_encountered:
ifself.is_connected(vertices_encountered,vertex):
returnTrue
else:
returnTrue

returnTrue
returnFalse
defvertex_degree(self,vertex):
"""Thedegreeofavertexisthenumberofedgesconnecting
it,i.e.thenumberofadjacentvertices.Loopsarecounted
double,i.e.everyoccurenceofvertexinthelist
ofadjacentvertices."""
adj_vertices=self.__graph_dict[vertex]
degree=len(adj_vertices)+adj_vertices.count(vertex)
returndegree
defdegree_sequence(self):
"""calculatesthedegreesequence"""
seq=[]
forvertexinself.__graph_dict:
seq.append(self.vertex_degree(vertex))
seq.sort(reverse=True)
returntuple(seq)
@staticmethod
defis_degree_sequence(sequence):
"""MethodreturnsTrue,ifthesequence"sequence"isa
degreesequence,i.e.anonincreasingsequence.
OtherwiseFalseisreturned.
"""
#checkifthesequencesequenceisnonincreasing:
returnall(x>=yforx,yinzip(sequence,sequence[1:]))

defdelta(self):
"""theminimumdegreeofthevertices"""
min=100000000
forvertexinself.__graph_dict:
vertex_degree=self.vertex_degree(vertex)
ifvertex_degree<min:
min=vertex_degree
returnmin

defDelta(self):
"""themaximumdegreeofthevertices"""
max=0
forvertexinself.__graph_dict:
vertex_degree=self.vertex_degree(vertex)
ifvertex_degree>max:
max=vertex_degree
returnmax
defdensity(self):
"""methodtocalculatethedensityofagraph"""
g=self.__graph_dict
V=len(g.keys())
E=len(self.edges())
return2.0*E/(V*(V1))
defdiameter(self):
"""calculatesthediameterofthegraph"""

v=self.vertices()
pairs=[(v[i],v[j])foriinrange(len(v))forjinrange(i+1,
len(v)1)]
smallest_paths=[]
for(s,e)inpairs:
paths=self.find_all_paths(s,e)
smallest=sorted(paths,key=len)[0]
smallest_paths.append(smallest)
smallest_paths.sort(key=len)
#longestpathisattheendoflist,

#longestpathisattheendoflist,
#i.e.diametercorrespondstothelengthofthispath
diameter=len(smallest_paths[1])
returndiameter
@staticmethod
deferdoes_gallai(dsequence):
"""ChecksiftheconditionoftheErdoesGallaiinequality
isfullfilled
"""
ifsum(dsequence)%2:
#sumofsequenceisodd
returnFalse
ifGraph.is_degree_sequence(dsequence):
forkinrange(1,len(dsequence)+1):
left=sum(dsequence[:k])
right=k*(k1)+sum([min(x,k)forxindsequence[k:]])
ifleft>right:
returnFalse
else:
#sequenceisincreasing
returnFalse
returnTrue
WecantestthisGraphclasswiththefollowingprogram:
fromgraphsimportGraph
g={"a":["d"],
"b":["c"],
"c":["b","c","d","e"],
"d":["a","c"],
"e":["c"],
"f":[]
}
graph=Graph(g)
print(graph)
fornodeingraph.vertices():
print(graph.vertex_degree(node))
print("Listofisolatedvertices:")
print(graph.find_isolated_vertices())
print("""Apathfrom"a"to"e":""")
print(graph.find_path("a","e"))
print("""Allpathesfrom"a"to"e":""")
print(graph.find_all_paths("a","e"))
print("Themaximumdegreeofthegraphis:")
print(graph.Delta())
print("Theminimumdegreeofthegraphis:")
print(graph.delta())
print("Edges:")
print(graph.edges())
print("DegreeSequence:")
ds=graph.degree_sequence()
print(ds)
fullfilling=[[2,2,2,2,1,1],
[3,3,3,3,3,3],
[3,3,2,1,1]
]
non_fullfilling=[[4,3,2,2,2,1,1],

[6,6,5,4,4,2,1],
[3,3,3,1]]
forsequenceinfullfilling+non_fullfilling:
print(sequence,Graph.erdoes_gallai(sequence))
print("Addvertex'z':")
graph.add_vertex("z")
print(graph)
print("Addedge('x','y'):")
graph.add_edge(('x','y'))
print(graph)
print("Addedge('a','d'):")
graph.add_edge(('a','d'))
print(graph)
Ifwestartthisprogram,wegetthefollowingoutput:
vertices:cefadb
edges:{'c','b'}{'c'}{'c','d'}{'c','e'}{'d','a'}
5
1
0
1
2
1
Listofisolatedvertices:
[]c
[]e
[]f
['f']a
['f']d
['f']b
['f']
Apathfrom"a"to"e":
['a','d','c','e']
Allpathesfrom"a"to"e":
[['a','d','c','e']]
Themaximumdegreeofthegraphis:
5
Theminimumdegreeofthegraphis:
0
Edges:
[{'c','b'},{'c'},{'c','d'},{'c','e'},{'d','a'}]
DegreeSequence:
(5,2,1,1,1,0)
[2,2,2,2,1,1]True
[3,3,3,3,3,3]True
[3,3,2,1,1]True
[4,3,2,2,2,1,1]False
[6,6,5,4,4,2,1]False
[3,3,3,1]False
Addvertex'z':
vertices:cefzadb
edges:{'c','b'}{'c'}{'c','d'}{'c','e'}{'d','a'}
Addedge('x','y'):
vertices:cefzaydb
edges:{'c','b'}{'c'}{'c','d'}{'c','e'}{'d','a'}{'y','x'}
Addedge('a','d'):
vertices:cefzaydb
edges:{'c','b'}{'c'}{'c','d'}{'c','e'}{'d','a'}{'y','x'}

Footnotes:
1Thegraphsstudiedingraphtheory(andinthischapterofourPythontutorial)shouldnotbeconfusedwiththe

graphsoffunctions
2Asingletonisasetthatcontainsexactlyoneelement.
Credits:
NarayanaChikkam,nchikkam(at)gmail(dot)com,pointedoutanindexerrorinthe"erdoes_gallai"method.Thankyou
verymuch,Narayana!

20112016,BerndKlein,BodenseoDesignbyDeniseMitchinsonadaptedforpythoncourse.eubyBerndKlein

You might also like