-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathMF5.swift
134 lines (125 loc) · 6.02 KB
/
MF5.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//
// MF5.swift
// AlgorithmsSwift
//
// Created by Michael Ho on 11/5/20.
//
class MF5 {
let mf4 = MF4()
/**
Rather than transporting flow from a single source to a single sink, there may be a collection
of supply nodes/vertices that need to be shipped to a collection of demand nodes/vertices.
Each supply node/vertex is associated with the amount of product it wants to ship and each
demand node is associated with the amount that it wants receive. The issue to be addressed becomes
whether there is some way to get the products from the supplies to the demands, subject to the
capacity constraints. This is a decision problem (or feasibility problem), meaning that it has
a yes-no answer, as opposed to maximum flow, which is an optimization problem.
Reference: https://github.com/SleekPanther/circulation-with-demands-network-flow
- Parameter graph: An directed graph.
- Returns: A boolean indicates if the graph has circulation and max feasible flow.
*/
func circulationWithDemands(_ graph: Graph, _ vertexDemands: [Vertex: Int]) -> Bool {
// Initial state variables
var sumOfDemands = 0
var sumOfSupplies = 0
var maxFlow = 0
var demandVertices = [Vertex]()
var supplyVertices = [Vertex]()
var mutableDemands = vertexDemands
for vertex in vertexDemands.keys {
let demand = vertexDemands[vertex]!
if demand > 0 {
demandVertices.append(vertex)
sumOfDemands += demand
} else if demand < 0 {
supplyVertices.append(vertex)
sumOfSupplies -= demand
}
// Do nothing if demand = 0 since vertex is not connected to source or sink.
}
// Graph and weights
var graphWeightMap = graph.buildtWeightDictionary()
var lowerBounds = [String : Int]() // ["src, dest" : weight]
var upperBounds = [String : Int]() // ["src, dest" : weight]
var lowerBoundsUpdatedSumOfDemands = 0
var lowerBoundsUpdatedSumOfSupplies = 0
// Flags
var isDemandsMatchSupplies = sumOfSupplies == sumOfDemands
var hasLowerBounds = false
// Updating supplies and demands
if isDemandsMatchSupplies {
for vertex in graph.adjacencyLists.keys {
let edges = graph.adjacencyLists[vertex]!
for edge in edges {
let edgeKey = Graph.getKey(edge.src, edge.dest)
if let lower = lowerBounds[edgeKey], lower != 0 {
hasLowerBounds = true
let oldLowerBound = lower
if graphWeightMap[edgeKey] == nil {
graphWeightMap[edgeKey] = 0
}
graphWeightMap[edgeKey]! = upperBounds[edgeKey] ?? 0 - oldLowerBound
upperBounds[edgeKey] = graphWeightMap[edgeKey]!
lowerBounds[edgeKey] = 0
// Update supplies/demands for source and destination of the edge.
if mutableDemands[edge.src]! > 0 {
mutableDemands[edge.src]! -= oldLowerBound
} else {
mutableDemands[edge.src]! += oldLowerBound
}
if mutableDemands[edge.dest]! > 0 {
mutableDemands[edge.dest]! -= oldLowerBound
} else {
mutableDemands[edge.dest]! += oldLowerBound
}
}
}
}
// Recalculate the sum of supplies/demands with updated bounds.
if hasLowerBounds {
lowerBoundsUpdatedSumOfDemands = 0
lowerBoundsUpdatedSumOfSupplies = 0
for vertex in graph.adjacencyLists.keys {
// Update supplies/demands for source and destination of the edge.
if mutableDemands[vertex]! > 0 {
lowerBoundsUpdatedSumOfDemands += mutableDemands[vertex]!
} else if mutableDemands[vertex]! < 0 {
lowerBoundsUpdatedSumOfSupplies -= mutableDemands[vertex]!
}
// Do nothing if demand = 0
}
if lowerBoundsUpdatedSumOfSupplies != lowerBoundsUpdatedSumOfDemands {
isDemandsMatchSupplies = false
}
}
if isDemandsMatchSupplies {
let updatedGraph = Graph.buildGraphFromWeightDictionary(graphWeightMap)
// Add s and t, connect to supply/demand vertices.
let unuseVal = graph.adjacencyLists.keys.map { $0.val }.max()! + 1
let sourceVal = unuseVal
let sinkVal = unuseVal + 1
// Connect demand vertices to sink.
for vertex in demandVertices {
updatedGraph.addEdge(from: vertex.val, to: sinkVal, weight: vertexDemands[vertex]!)
}
// Connect source vertex to supply vertices.
for vertex in supplyVertices {
updatedGraph.addEdge(from: sourceVal, to: vertex.val, weight: -vertexDemands[vertex]!)
}
// Run Edmonds-Karp algorithm to find the max flow.
maxFlow = mf4.maximumFlowByEdmondsKarp(updatedGraph, sourceVal, sinkVal).maxFlow
}
}
// Determine if the graph has circulation.
if(!isDemandsMatchSupplies){
return false
} else if hasLowerBounds {
if maxFlow != lowerBoundsUpdatedSumOfSupplies || maxFlow != lowerBoundsUpdatedSumOfDemands {
return false
}
} else if maxFlow != sumOfSupplies || maxFlow != sumOfDemands {
return false
}
return true
}
}