Graph Theory - Edmonds-Karp Algorithm



Edmonds-Karp Algorithm

The Edmonds-Karp algorithm is an implementation of the Ford-Fulkerson method for computing the maximum flow in a flow network. It uses breadth-first search (BFS) to find augmenting paths in the residual graph, ensuring that the shortest augmenting path is found in each iteration.

An augmenting path is a path from the source to the sink where every edge in the path has a positive residual capacity (i.e., the flow can still be increased along the edge).

This improves the performance of the basic Ford-Fulkerson algorithm by providing a more structured method for searching augmenting paths. The algorithm guarantees finding the maximum flow in polynomial time, specifically in O(VE2), where V is the number of vertices and E is the number of edges in the graph.

The key difference between Edmonds-Karp and the Ford-Fulkerson algorithm is that Edmonds-Karp always chooses the shortest augmenting path (in terms of the number of edges) by using BFS.

Overview of Edmonds-Karp Algorithm

The Edmonds-Karp algorithm works by finding augmenting paths in the residual graph using breadth-first search (BFS). The main steps of the algorithm are as follows −

  • Initialization: Start with a flow of 0 on all edges and construct the residual graph.
  • Find Augmenting Path: Use BFS to find the shortest augmenting path in the residual graph.
  • Augment Flow: Increase the flow along the found augmenting path by the minimum residual capacity of the edges in the path.
  • Update Residual Graph: Update the residual capacities of the edges in the augmenting path.
  • Repeat: Repeat the process of finding augmenting paths and updating the flow until no augmenting path exists in the residual graph.

Properties of Edmonds-Karp Algorithm

Edmonds-Karp has several important properties and characteristics that differentiate it from other flow algorithms −

  • Polynomial Time: Unlike the basic Ford-Fulkerson algorithm, which can take exponential time in the worst case, the Edmonds-Karp algorithm runs in polynomial time (O(VE2)), where V is the number of vertices and E is the number of edges in the graph.
  • Shortest Path Search: The algorithm always finds the shortest augmenting path using breadth-first search (BFS), ensuring efficient flow updates.
  • Optimal Flow: The algorithm guarantees the maximum flow solution, just like the Ford-Fulkerson algorithm, by iterating through augmenting paths until no more augmenting paths can be found.
  • Capacity Constraints: The algorithm respects the capacity constraints by updating the residual graph after each flow augmentation.

Steps of Edmonds-Karp Algorithm

The steps of the Edmonds-Karp algorithm are as follows −

  • Step 1: Initialize Flow: Start with an initial flow of 0 for all edges. The initial flow configuration is often represented as a flow matrix or an adjacency list of flow values.
  • Step 2: Construct Residual Graph: Create a residual graph representing the remaining capacity of each edge in the original graph. Initially, the residual capacity of each edge is equal to its capacity in the original graph.
  • Step 3: Find Augmenting Path: Use breadth-first search (BFS) to find an augmenting path in the residual graph. This is the shortest path from the source to the sink that still has positive residual capacity along every edge.
  • Step 4: Augment Flow: Increase the flow along the found augmenting path. The flow increase is limited by the smallest residual capacity along the path, called the bottleneck capacity.
  • Step 5: Update Residual Graph: After augmenting the flow, update the residual graph. The capacity of the forward edges along the path is decreased by the flow amount, and the reverse edges are updated to allow for potential flow reversal.
  • Step 6: Repeat: Repeat the process of finding augmenting paths and augmenting the flow until no more augmenting paths exist. When no more augmenting paths are found, the algorithm terminates, and the flow is the maximum flow in the network.

Example of Edmonds-Karp Algorithm

Consider the following graph −

Edmonds-Karp Algorithm

We want to find the maximum flow from the source (S) to the sink (T) using the Edmonds-Karp algorithm.

Step 1: Initialize Flow

Start with an initial flow of 0 on all edges:

flow = {(S, A): 0, (S, B): 0, (A, T): 0, (B, T): 0, (A, B): 0}

Step 2: Find Augmenting Path

In the first iteration, we perform a BFS and find the augmenting path: S A T. The minimum residual capacity along this path is 3 (the capacity of the edge S A).

Step 3: Augment Flow

We augment the flow along this path by 3. After augmenting, the flow becomes:

flow = {(S, A): 3, (S, B): 0, (A, T): 3, (B, T): 0, (A, B): 0}

Step 4: Update Residual Graph

Update the residual capacities:

  • Residual Capacity of (S, A): 5 - 3 = 2
  • Residual Capacity of (A, T): 3 - 3 = 0

Step 5: Find Augmenting Path Again

In the next iteration, we perform a BFS and find a new augmenting path: S B T. The minimum residual capacity along this path is 2 (the capacity of the edge S B).

Step 6: Augment Flow

We augment the flow by 2 along the path S B T:

flow = {(S, A): 3, (S, B): 2, (A, T): 3, (B, T): 2, (A, B): 0}

Step 7: Update Residual Graph

Update the residual capacities:

  • Residual Capacity of (S, B): 4 - 2 = 2
  • Residual Capacity of (B, T): 2 - 2 = 0

Step 8: Termination

At this point, no more augmenting paths exist, and the algorithm terminates. The maximum flow in the network is the total flow that has been augmented from the source to the sink. In this case, the maximum flow is 5 (3 + 2).

Complete Python Implementation

Following is the complete Python implementation of the Edmonds-Karp algorithm −

from collections import deque

class Graph:
   def __init__(self, vertices):
      self.V = vertices
      self.graph = {}

   def add_edge(self, u, v, w):
      if u not in self.graph:
         self.graph[u] = {}
      if v not in self.graph:
         self.graph[v] = {}
      self.graph[u][v] = self.graph[u].get(v, 0) + w
	  # Initialize the reverse edge with 0 capacity
      self.graph[v][u] = self.graph[v].get(u, 0)  

   def bfs(self, source, sink, parent):
      visited = {key: False for key in self.graph}
      queue = deque([source])
      visited[source] = True

      while queue:
         u = queue.popleft()

         for v in self.graph[u]:
            if not visited[v] and self.graph[u][v] > 0:
               if v == sink:
                  parent[v] = u
                  return True
               queue.append(v)
               parent[v] = u
               visited[v] = True
      return False

   def edmonds_karp(self, source, sink):
      parent = {}
      max_flow = 0

      while self.bfs(source, sink, parent):
         path_flow = float("Inf")
         s = sink
         while s != source:
            path_flow = min(path_flow, self.graph[parent[s]][s])
            s = parent[s]

         max_flow += path_flow
         v = sink
         while v != source:
            u = parent[v]
            self.graph[u][v] -= path_flow
     		# Update the reverse edge capacity
            self.graph[v][u] = self.graph[v].get(u, 0) + path_flow  
            v = parent[v]

      return max_flow

# Create a graph and add edges
g = Graph(6)
g.add_edge('S', 'A', 10)
g.add_edge('S', 'B', 5)
g.add_edge('A', 'T', 10)
g.add_edge('B', 'T', 5)
g.add_edge('A', 'B', 15)

# Find the maximum flow
max_flow = g.edmonds_karp('S', 'T')
print("The maximum possible flow is", max_flow)

Following is the output obtained −

The maximum possible flow is 15

Complexity Analysis

The time complexity of the Edmonds-Karp algorithm is O(VE2), where V is the number of vertices and E is the number of edges in the graph. This is because the algorithm performs a breadth-first search (BFS) to find augmenting paths, and each augmenting path requires at most O(E) time.

The algorithm performs at most O(VE) augmentations, leading to the overall time complexity.

The space complexity is O(V + E) due to the storage of the graph and the parent array used for BFS.

Advertisements