-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblossom.h
380 lines (339 loc) · 13.1 KB
/
blossom.h
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#ifndef BLOSSOM_BLOSSOM_H
#define BLOSSOM_BLOSSOM_H
#include <stdexcept>
#include <stack>
#include <ranges>
#include "Graph.h"
/**
* Find all exposed vertices, meaning they aren't incident to any edge in the machting
*
* @param g graph
* @param m matching
* @return vector of exposed vertices
*/
std::vector<int> exposedVertices(Graph g, matching m) {
std::vector<int> exposed;
for (auto const& v : g.vertices) {
if (!m.existsVertex(v)) {
exposed.push_back(v);
}
}
return exposed;
}
/**
* Find index of tree in forest of which v is a part
*
* @param forest forest
* @param v vertex
* @return index
*
* @throws std::invalid_argument Thrown if forest does not contain v
*/
int findIdx(std::vector<Graph> forest, int v) {
for (int i = 0; i < forest.size(); ++i) {
for (auto const& w : forest[i].vertices) {
if (w == v) {
return i;
}
}
}
throw std::invalid_argument("Vertex not found in forest");
}
/**
* Find a unmarked vertex with even distance to root
*
* If none exist return i as -1
*
* @param forest forest
* @param fRoots roots of forest trees
* @param vMark marked vertices
* @param fNodes nodes in forest for order
* @return v candidate vertex
* @return i tree index of candidate vertex
*/
std::pair<int, int> candidateVertex(std::vector<Graph> forest, std::vector<int> fRoots, std::set<int> vMark, std::vector<int> fNodes) {
for (auto const& v : fNodes) {
if (!vMark.contains(v)) {
return {v, findIdx(forest, v)};
}
}
return {0, -1};
}
/**
* Find an unmarked edge incident to v
*
* @param g graph
* @param v vertex
* @param eMark marked edges
* @return w vertex such that (v, w) is a candidate edge
* @return state if successful
*/
std::pair<int, bool> candidateEdge(Graph g, int v, matching eMark) {
for (auto const &w : g.adjList[v]) { // neighbours of v
if (!eMark.edges.contains(edge(v, w))) {
return {w, true};
}
}
return {0, false};
}
/**
* Checks if vertex v is part of tree
*
* @param forest forest
* @param v vertex
*/
bool existInTree(std::vector<Graph> forest, int v) {
for (int i = 0; i < forest.size(); ++i) {
for (auto const& w : forest[i].vertices) {
if (w == v) {
return true;
}
}
}
return false;
}
/**
* Convert path noted as sequence of vertices to a sequence of edges
*
* @param vertexPath vertex sequence
* @return edge sequence
*/
std::vector<edge> pathConversion(std::vector<int> vertexPath) {
std::vector<edge> edgePath;
for (int i = 0; i < vertexPath.size() - 1; ++i) {
edgePath.push_back(edge(vertexPath[i], vertexPath[i+1]));
}
return edgePath;
}
/**
* Find base vertex of blossom
*
* @param m matching
* @param blossom blossom
* @return v vertex
* @return i index of vertex
*
* @throws std::invalid_argument Thrown if blossom has no base
*/
std::pair<int, int> findBase(matching m, std::vector<int> blossom) {
blossom.push_back(blossom[0]); // for circular for loop
blossom.push_back(blossom[1]);
for (int i = 0; i < blossom.size() - 2; ++i) {
bool a = !(m.edges.contains(edge(blossom[i], blossom[i+1])));
bool b = !(m.edges.contains(edge(blossom[i+1], blossom[i+2])));
if (!(m.edges.contains(edge(blossom[i], blossom[i+1]))) && !(m.edges.contains(edge(blossom[i+1], blossom[i+2])))) { // two successive edges not in matching
return {blossom[i+1], i+1};
}
}
throw std::invalid_argument("Blossom has no base");
}
/**
* Lift blossom with left stem
*
* @param g Graph
* @param blossom blossom
* @param lStem left stem
* @return lifted blossom
*
* @throws std::invalid_argument Thrown if left stem is not connected
*/
std::vector<int> liftLeft(Graph g, std::vector<int> blossom, std::vector<int> lStem) {
for (int i = 0; i < blossom.size(); ++i) {
if (g.existEdge(blossom[i], lStem.back())) {
if (i % 2 == 0) {
std::vector<int> result(blossom.begin(), blossom.begin() + i + 1);
std::reverse(result.begin(), result.end());
return result;
} else {
return std::vector<int>(blossom.begin() + i, blossom.end());
}
}
}
throw std::invalid_argument("Left stem is not connected");
}
/**
* Lift blossom with right stem
*
* @param g Graph
* @param blossom blossom
* @param rStem right stem
* @return lifted blossom
*
* @throws std::invalid_argument Thrown if right stem is not connected
*/
std::vector<int> liftRight(Graph g, std::vector<int> blossom, std::vector<int> rStem) {
for (int i = 0; i < blossom.size(); ++i) {
if (g.existEdge(blossom[i], rStem[0])) {
if (i % 2 == 0) {
return std::vector<int>(blossom.begin(), blossom.begin() + i + 1);
} else {
std::vector<int> result(blossom.begin() + i, blossom.end());
std::reverse(result.begin(), result.end());
return result;
}
}
}
throw std::invalid_argument("Right stem is not connected");
}
/**
* Find an augmenting path for m in g if one exists
*
* Pseudo code for the augmenting path can be found at https://en.m.wikipedia.org/wiki/Blossom_algorithm, with a
* detailed description of the lifting in https://github.com/amyshoe/CME323-Project/blob/master/seq_blossom.py
*
* @param g undirected graph
* @param m matching
* @return vector of augmenting path edges
*
* @throws std::out_of_range Thrown if blossom stack is empty
*/
std::vector<int> augmentingPath(Graph g, matching m, std::stack<int> stack = std::stack<int>()) {
std::vector<Graph> forest;
std::vector<int> fRoots; // roots of forest trees
std::vector<int> fNodes; // all vertices in the forest
std::set<int> vMark; // marked vertices
matching eMark = m; // marked edges, not necessarily a valid matching
for (auto const& v : exposedVertices(g, m)) {
Graph tempGraph; // add singleton {v} to forest
tempGraph.addVertex(v);
forest.push_back(tempGraph);
fRoots.push_back(v);
fNodes.push_back(v);
}
while (true) {
auto [v, vIdx] = candidateVertex(forest, fRoots, vMark, fNodes);
if (vIdx == -1) { // no candidate vertex exists
break;
}
while (true) {
auto [w, wState] = candidateEdge(g, v, eMark);
if (!wState) {
break;
}
if (!existInTree(forest, w)) { // w is matched, add edges to f
int u = m.matchedVertex(w);
forest[vIdx].addVertex(w);
forest[vIdx].addVertex(u);
fNodes.push_back(u);
forest[vIdx].addEdge(v, w);
forest[vIdx].addEdge(w, u);
} else {
int wIdx = findIdx(forest, w);
if (forest[wIdx].dist(w, fRoots[wIdx]) % 2 == 0) {
if (vIdx != wIdx) { // no blossom found
std::vector<int> pathV = forest[vIdx].path(fRoots[vIdx], v);
std::vector<int> pathW = forest[wIdx].path(w, fRoots[wIdx]);
pathV.insert(pathV.end(), pathW.begin(), pathW.end());
return pathV;
} else {
std::vector<int> blossom = forest[vIdx].path(v, w);
Graph gCont = g;
matching mCont = m;
for (auto const& u : blossom) {
if (u != w) {
gCont.contract(w, u);
mCont.removeIncidentEdge(u);
}
}
blossom.push_back(v); // for lifting afterwards append v also at the end
stack.push(w); // remember blossom
std::vector<int> augPath = augmentingPath(gCont, mCont, stack); // recursive call on contracted graph
if (stack.empty()) {
throw std::out_of_range("Blossom stack is empty");
}
int b = stack.top(); // blossom vertex
if (std::ranges::find(augPath, b) != augPath.end()) { // blossom part of augmenting path
auto it = std::find(augPath.begin(), augPath.end(), b); // index of b
int index = std::distance(augPath.begin(), it);
std::vector<int> lStem(augPath.begin(), augPath.begin() + index); // split into the stems
std::vector<int> rStem(augPath.begin() + index, augPath.end());
auto [base, baseIdx] = findBase(m, blossom);
std::rotate(blossom.begin(), blossom.begin() + baseIdx - 1, blossom.end());
if (lStem.empty() || rStem.empty()) { // blossom contains start or end point
if (!lStem.empty()) {
if (g.existEdge(base, lStem.back())) { // lStem is directly connected to base
lStem.push_back(base);
return lStem;
} else {
std::vector<int> liftedBlossom = liftLeft(g, blossom, lStem);
lStem.insert(lStem.end(), liftedBlossom.begin(), liftedBlossom.end());
return lStem;
}
} else {
if (g.existEdge(base, rStem[0])) { // rStem is directly connected to base
std::vector<int> path;
path.push_back(base);
path.insert(path.end(), rStem.begin(), rStem.end());
} else {
std::vector<int> liftedBlossom = liftRight(g, blossom, rStem);
liftedBlossom.insert(liftedBlossom.end(), rStem.begin(), rStem.end());
return liftedBlossom;
}
}
} else { // blossom is in the middle of the path
if (m.edges.contains(edge(base, lStem.back()))) { // if lStem attaches to base
if (g.existEdge(base, rStem[0])) {
lStem.push_back(base);
lStem.insert(lStem.end(), rStem.begin(), rStem.end());
return lStem;
} else {
std::vector<int> liftedBlossom = liftRight(g, blossom, rStem);
lStem.insert(lStem.end(), liftedBlossom.begin(), liftedBlossom.end());
lStem.insert(lStem.end(), rStem.begin(), rStem.end());
return lStem;
}
} else {
if (g.existEdge(base, lStem.back())) {
lStem.push_back(base);
lStem.insert(lStem.end(), rStem.begin(), rStem.end());
return lStem;
} else {
std::vector<int> liftedBlossom = liftLeft(g, blossom, lStem);
lStem.insert(lStem.end(), liftedBlossom.begin(), liftedBlossom.end());
lStem.insert(lStem.end(), rStem.begin(), rStem.end());
return lStem;
}
}
}
} else {
return augPath;
}
}
}
}
eMark.edges.insert(edge(v, w));
}
vMark.insert(v);
}
return std::vector<int>();
}
/**
* Find a maximum matching of the graph g
*
* @param g undirected graph
* @param m starting matching
* @return vector of matching edges
*/
matching maximumMatching(Graph g, matching m) {
std::vector<int> vertexPath = augmentingPath(g, m);
if (!vertexPath.empty()) {
std::vector<edge> path = pathConversion(vertexPath);
m.augment(path);
return maximumMatching(g, m);
} else {
return m;
}
}
/**
* Find a maximum matching of the graph g
*
* Select first edge of grpah as starting guess for the matching
*
* @param g undirected graph
* @return vector of matching edges
*/
matching maximumMatching(Graph g) {
matching m(std::vector<edge>({*g.edges.begin()}));
return maximumMatching(g, m);
}
#endif //BLOSSOM_BLOSSOM_H