Skip to content

Commit

Permalink
Created a generalized algo to edmonds karp (TheAlgorithms#724)
Browse files Browse the repository at this point in the history
Edmonds Karp algorithm is traditionally with only one source and one sink. What do you do if you have multiple sources and sinks? This algorithm is a generalized algorithm that regardless of however many sinks and sources you have, will allow you to use this algorithm. It does this by using the traditional algorithm but adding an artificial source and sink that allows with "infinite" weight.
  • Loading branch information
viraatdas authored and poyea committed Apr 25, 2019
1 parent df04d94 commit 2fc2ae3
Showing 1 changed file with 182 additions and 0 deletions.
182 changes: 182 additions & 0 deletions Graphs/edmonds_karp_Multiple_SourceAndSink.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
class FlowNetwork:
def __init__(self, graph, sources, sinks):
self.sourceIndex = None
self.sinkIndex = None
self.graph = graph

self._normalizeGraph(sources, sinks)
self.verticesCount = len(graph)
self.maximumFlowAlgorithm = None

# make only one source and one sink
def _normalizeGraph(self, sources, sinks):
if sources is int:
sources = [sources]
if sinks is int:
sinks = [sinks]

if len(sources) == 0 or len(sinks) == 0:
return

self.sourceIndex = sources[0]
self.sinkIndex = sinks[0]

# make fake vertex if there are more
# than one source or sink
if len(sources) > 1 or len(sinks) > 1:
maxInputFlow = 0
for i in sources:
maxInputFlow += sum(self.graph[i])


size = len(self.graph) + 1
for room in self.graph:
room.insert(0, 0)
self.graph.insert(0, [0] * size)
for i in sources:
self.graph[0][i + 1] = maxInputFlow
self.sourceIndex = 0

size = len(self.graph) + 1
for room in self.graph:
room.append(0)
self.graph.append([0] * size)
for i in sinks:
self.graph[i + 1][size - 1] = maxInputFlow
self.sinkIndex = size - 1


def findMaximumFlow(self):
if self.maximumFlowAlgorithm is None:
raise Exception("You need to set maximum flow algorithm before.")
if self.sourceIndex is None or self.sinkIndex is None:
return 0

self.maximumFlowAlgorithm.execute()
return self.maximumFlowAlgorithm.getMaximumFlow()

def setMaximumFlowAlgorithm(self, Algorithm):
self.maximumFlowAlgorithm = Algorithm(self)


class FlowNetworkAlgorithmExecutor(object):
def __init__(self, flowNetwork):
self.flowNetwork = flowNetwork
self.verticesCount = flowNetwork.verticesCount
self.sourceIndex = flowNetwork.sourceIndex
self.sinkIndex = flowNetwork.sinkIndex
# it's just a reference, so you shouldn't change
# it in your algorithms, use deep copy before doing that
self.graph = flowNetwork.graph
self.executed = False

def execute(self):
if not self.executed:
self._algorithm()
self.executed = True

# You should override it
def _algorithm(self):
pass



class MaximumFlowAlgorithmExecutor(FlowNetworkAlgorithmExecutor):
def __init__(self, flowNetwork):
super(MaximumFlowAlgorithmExecutor, self).__init__(flowNetwork)
# use this to save your result
self.maximumFlow = -1

def getMaximumFlow(self):
if not self.executed:
raise Exception("You should execute algorithm before using its result!")

return self.maximumFlow

class PushRelabelExecutor(MaximumFlowAlgorithmExecutor):
def __init__(self, flowNetwork):
super(PushRelabelExecutor, self).__init__(flowNetwork)

self.preflow = [[0] * self.verticesCount for i in range(self.verticesCount)]

self.heights = [0] * self.verticesCount
self.excesses = [0] * self.verticesCount

def _algorithm(self):
self.heights[self.sourceIndex] = self.verticesCount

# push some substance to graph
for nextVertexIndex, bandwidth in enumerate(self.graph[self.sourceIndex]):
self.preflow[self.sourceIndex][nextVertexIndex] += bandwidth
self.preflow[nextVertexIndex][self.sourceIndex] -= bandwidth
self.excesses[nextVertexIndex] += bandwidth

# Relabel-to-front selection rule
verticesList = [i for i in range(self.verticesCount)
if i != self.sourceIndex and i != self.sinkIndex]

# move through list
i = 0
while i < len(verticesList):
vertexIndex = verticesList[i]
previousHeight = self.heights[vertexIndex]
self.processVertex(vertexIndex)
if self.heights[vertexIndex] > previousHeight:
# if it was relabeled, swap elements
# and start from 0 index
verticesList.insert(0, verticesList.pop(i))
i = 0
else:
i += 1

self.maximumFlow = sum(self.preflow[self.sourceIndex])

def processVertex(self, vertexIndex):
while self.excesses[vertexIndex] > 0:
for neighbourIndex in range(self.verticesCount):
# if it's neighbour and current vertex is higher
if self.graph[vertexIndex][neighbourIndex] - self.preflow[vertexIndex][neighbourIndex] > 0\
and self.heights[vertexIndex] > self.heights[neighbourIndex]:
self.push(vertexIndex, neighbourIndex)

self.relabel(vertexIndex)

def push(self, fromIndex, toIndex):
preflowDelta = min(self.excesses[fromIndex],
self.graph[fromIndex][toIndex] - self.preflow[fromIndex][toIndex])
self.preflow[fromIndex][toIndex] += preflowDelta
self.preflow[toIndex][fromIndex] -= preflowDelta
self.excesses[fromIndex] -= preflowDelta
self.excesses[toIndex] += preflowDelta

def relabel(self, vertexIndex):
minHeight = None
for toIndex in range(self.verticesCount):
if self.graph[vertexIndex][toIndex] - self.preflow[vertexIndex][toIndex] > 0:
if minHeight is None or self.heights[toIndex] < minHeight:
minHeight = self.heights[toIndex]

if minHeight is not None:
self.heights[vertexIndex] = minHeight + 1

if __name__ == '__main__':
entrances = [0]
exits = [3]
# graph = [
# [0, 0, 4, 6, 0, 0],
# [0, 0, 5, 2, 0, 0],
# [0, 0, 0, 0, 4, 4],
# [0, 0, 0, 0, 6, 6],
# [0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0],
# ]
graph = [[0, 7, 0, 0], [0, 0, 6, 0], [0, 0, 0, 8], [9, 0, 0, 0]]

# prepare our network
flowNetwork = FlowNetwork(graph, entrances, exits)
# set algorithm
flowNetwork.setMaximumFlowAlgorithm(PushRelabelExecutor)
# and calculate
maximumFlow = flowNetwork.findMaximumFlow()

print("maximum flow is {}".format(maximumFlow))

0 comments on commit 2fc2ae3

Please sign in to comment.