Skip to content

Commit b99ea4e

Browse files
authored
Merge pull request #48 from pankaj-bind/AIEdmondsKarpTest
Added Edmonds-Karp Algorithm Implementation
2 parents ea1fd39 + 2ff6e9e commit b99ea4e

3 files changed

Lines changed: 283 additions & 0 deletions

File tree

src/AI-Algorithms-Graph-Components/AINetworkFlowEdge.class.st

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,9 @@ AINetworkFlowEdge >> flow: anObject [
4343
AINetworkFlowEdge >> initialize [
4444
flow:=0
4545
]
46+
47+
{ #category : 'accessing' }
48+
AINetworkFlowEdge >> residualCapacity [
49+
"Return the residual capacity of this edge"
50+
^ capacity - flow
51+
]
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"
2+
Test class for the Edmonds-Karp algorithm
3+
"
4+
Class {
5+
#name : 'AIEdmondsKarpTest',
6+
#superclass : 'TestCase',
7+
#category : 'AI-Algorithms-Graph-Tests',
8+
#package : 'AI-Algorithms-Graph-Tests'
9+
}
10+
11+
{ #category : 'tests' }
12+
AIEdmondsKarpTest >> testBasicMaxFlow [
13+
"Test basic maximum flow computation"
14+
15+
| nodes edges edmondsKarp maxFlow |
16+
nodes := #( $s $a $b $t ).
17+
edges := #( #( $s $a 10 ) #( $s $b 8 ) #( $a $b 5 ) #( $a $t 10 ) #( $b $t 10 ) ).
18+
19+
edmondsKarp := AIEdmondsKarp new.
20+
edmondsKarp nodes: nodes.
21+
edmondsKarp
22+
edges: edges
23+
from: [ :each | each first ]
24+
to: [ :each | each second ]
25+
capacity: [ :each | each third ].
26+
27+
edmondsKarp source: $s sink: $t.
28+
maxFlow := edmondsKarp run.
29+
30+
self assert: maxFlow equals: 18
31+
]
32+
33+
{ #category : 'tests' }
34+
AIEdmondsKarpTest >> testComplexNetwork [
35+
"Test with a more complex flow network"
36+
37+
| nodes edges edmondsKarp maxFlow |
38+
nodes := #( 1 2 3 4 5 6 ).
39+
edges := #(
40+
#( 1 2 16 ) #( 1 3 13 )
41+
#( 2 3 10 ) #( 2 4 12 )
42+
#( 3 2 4 ) #( 3 5 14 )
43+
#( 4 3 9 ) #( 4 6 20 )
44+
#( 5 4 7 ) #( 5 6 4 ) ).
45+
46+
edmondsKarp := AIEdmondsKarp new.
47+
edmondsKarp nodes: nodes.
48+
edmondsKarp
49+
edges: edges
50+
from: [ :each | each first ]
51+
to: [ :each | each second ]
52+
capacity: [ :each | each third ].
53+
54+
edmondsKarp source: 1 sink: 6.
55+
maxFlow := edmondsKarp run.
56+
57+
self assert: maxFlow equals: 23
58+
]
59+
60+
{ #category : 'tests' }
61+
AIEdmondsKarpTest >> testNoPath [
62+
"Test when there's no path from source to sink"
63+
64+
| nodes edges edmondsKarp maxFlow |
65+
nodes := #( $a $b $c $d ).
66+
edges := #( #( $a $b 5 ) #( $c $d 3 ) ).
67+
68+
edmondsKarp := AIEdmondsKarp new.
69+
edmondsKarp nodes: nodes.
70+
edmondsKarp
71+
edges: edges
72+
from: [ :each | each first ]
73+
to: [ :each | each second ]
74+
capacity: [ :each | each third ].
75+
76+
edmondsKarp source: $a sink: $d.
77+
maxFlow := edmondsKarp run.
78+
79+
self assert: maxFlow equals: 0
80+
]
81+
82+
{ #category : 'tests' }
83+
AIEdmondsKarpTest >> testSingleEdge [
84+
"Test with a single edge from source to sink"
85+
86+
| nodes edges edmondsKarp maxFlow |
87+
nodes := #( $s $t ).
88+
edges := #( #( $s $t 15 ) ).
89+
90+
edmondsKarp := AIEdmondsKarp new.
91+
edmondsKarp nodes: nodes.
92+
edmondsKarp
93+
edges: edges
94+
from: [ :each | each first ]
95+
to: [ :each | each second ]
96+
capacity: [ :each | each third ].
97+
98+
edmondsKarp source: $s sink: $t.
99+
maxFlow := edmondsKarp run.
100+
101+
self assert: maxFlow equals: 15
102+
]
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
"
2+
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 to find the shortest augmenting path from source to sink in terms of the number of edges.
3+
4+
The algorithm works by:
5+
1. Initialize flow to 0 for all edges
6+
2. While there exists an augmenting path from source to sink:
7+
a. Find the shortest augmenting path using BFS
8+
b. Find the minimum residual capacity along this path
9+
c. Update the flow along this path
10+
3. Return the maximum flow value
11+
12+
Time complexity: O(VE²) where V is the number of vertices and E is the number of edges.
13+
14+
Example usage:
15+
|nodes edges edmondsKarp maxFlow|
16+
nodes := #( $s $a $b $t ).
17+
edges := #( #( $s $a 10 ) #( $s $b 8 ) #( $a $b 5 ) #( $a $t 10 ) #( $b $t 10 ) ).
18+
19+
edmondsKarp := AIEdmondsKarp new.
20+
edmondsKarp nodes: nodes.
21+
edmondsKarp
22+
edges: edges
23+
from: [ :each | each first ]
24+
to: [ :each | each second ]
25+
capacity: [ :each | each third ].
26+
27+
edmondsKarp source: $s sink: $t.
28+
maxFlow := edmondsKarp run.
29+
"
30+
Class {
31+
#name : 'AIEdmondsKarp',
32+
#superclass : 'AIGraphAlgorithm',
33+
#instVars : [
34+
'source',
35+
'sink',
36+
'parent'
37+
],
38+
#category : 'AI-Algorithms-Graph-Maximum Flow',
39+
#package : 'AI-Algorithms-Graph',
40+
#tag : 'Maximum Flow'
41+
}
42+
43+
{ #category : 'configuration' }
44+
AIEdmondsKarp >> edgeClass [
45+
^ AINetworkFlowEdge
46+
]
47+
48+
{ #category : 'building - graph' }
49+
AIEdmondsKarp >> edges: aCollection from: source to: target capacity: capacityFunction [
50+
| edge reverseEdge |
51+
aCollection do: [ :eModel |
52+
edge := self addEdge: eModel from: source to: target.
53+
edge ifNotNil: [
54+
edge capacity: (capacityFunction value: eModel).
55+
edge flow: 0 ].
56+
57+
"Add reverse edge with 0 capacity for residual graph"
58+
reverseEdge := self addEdge: eModel from: target to: source.
59+
reverseEdge ifNotNil: [
60+
reverseEdge capacity: 0.
61+
reverseEdge flow: 0 ] ]
62+
]
63+
64+
{ #category : 'private' }
65+
AIEdmondsKarp >> findAugmentingPathBFS [
66+
"Find an augmenting path from source to sink using BFS.
67+
Returns true if a path exists, false otherwise.
68+
Updates the parent array to reconstruct the path."
69+
70+
| queue visited |
71+
queue := LinkedList new.
72+
visited := Set new.
73+
parent := Dictionary new.
74+
75+
queue addLast: source.
76+
visited add: source.
77+
78+
[ queue isNotEmpty ] whileTrue: [
79+
| current |
80+
current := queue removeFirst.
81+
82+
current = sink ifTrue: [ ^ true ].
83+
84+
current outgoingEdges do: [ :edge |
85+
| neighbor residualCapacity |
86+
neighbor := edge to.
87+
residualCapacity := edge capacity - edge flow.
88+
89+
(visited includes: neighbor) not & (residualCapacity > 0) ifTrue: [
90+
queue addLast: neighbor.
91+
visited add: neighbor.
92+
parent at: neighbor put: edge ] ] ].
93+
94+
^ false
95+
]
96+
97+
{ #category : 'private' }
98+
AIEdmondsKarp >> findBottleneckCapacity [
99+
"Find the minimum residual capacity along the augmenting path"
100+
101+
| current minCapacity |
102+
current := sink.
103+
minCapacity := Float infinity.
104+
105+
[ current ~= source ] whileTrue: [
106+
| edge residualCapacity |
107+
edge := parent at: current.
108+
residualCapacity := edge capacity - edge flow.
109+
minCapacity := minCapacity min: residualCapacity.
110+
current := edge from ].
111+
112+
^ minCapacity
113+
]
114+
115+
{ #category : 'private' }
116+
AIEdmondsKarp >> findReverseEdge: anEdge [
117+
"Find the reverse edge for the given edge"
118+
119+
^ anEdge to outgoingEdges
120+
detect: [ :edge | edge to = anEdge from ]
121+
ifNone: [ nil ]
122+
]
123+
124+
{ #category : 'initialization' }
125+
AIEdmondsKarp >> initialize [
126+
super initialize.
127+
parent := Dictionary new
128+
]
129+
130+
{ #category : 'configuration' }
131+
AIEdmondsKarp >> nodeClass [
132+
^ AIPathDistanceNode
133+
]
134+
135+
{ #category : 'running' }
136+
AIEdmondsKarp >> run [
137+
"Execute the Edmonds-Karp algorithm and return the maximum flow value"
138+
139+
| maxFlow pathFlow |
140+
maxFlow := 0.
141+
142+
[ self findAugmentingPathBFS ] whileTrue: [
143+
pathFlow := self findBottleneckCapacity.
144+
self updateFlowAlongPath: pathFlow.
145+
maxFlow := maxFlow + pathFlow ].
146+
147+
^ maxFlow
148+
]
149+
150+
{ #category : 'accessing' }
151+
AIEdmondsKarp >> source: sourceNode sink: sinkNode [
152+
source := self findNode: sourceNode.
153+
sink := self findNode: sinkNode
154+
]
155+
156+
{ #category : 'private' }
157+
AIEdmondsKarp >> updateFlowAlongPath: flowValue [
158+
"Update the flow along the augmenting path found by BFS"
159+
160+
| current |
161+
current := sink.
162+
163+
[ current ~= source ] whileTrue: [
164+
| edge reverseEdge |
165+
edge := parent at: current.
166+
167+
"Update forward edge flow"
168+
edge flow: edge flow + flowValue.
169+
170+
"Update reverse edge flow"
171+
reverseEdge := self findReverseEdge: edge.
172+
reverseEdge flow: reverseEdge flow - flowValue.
173+
174+
current := edge from ]
175+
]

0 commit comments

Comments
 (0)