-
Notifications
You must be signed in to change notification settings - Fork 299
/
Copy pathBiPartiteMatching.cs
142 lines (113 loc) · 4.47 KB
/
BiPartiteMatching.cs
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
using System;
using System.Collections.Generic;
using Advanced.Algorithms.DataStructures.Graph;
using Advanced.Algorithms.DataStructures.Graph.AdjacencyList;
namespace Advanced.Algorithms.Graph;
/// <summary>
/// Compute Max BiParitite Edges using Ford-Fukerson algorithm.
/// </summary>
public class BiPartiteMatching<T>
{
private readonly IBiPartiteMatchOperators<T> @operator;
public BiPartiteMatching(IBiPartiteMatchOperators<T> @operator)
{
this.@operator = @operator;
}
/// <summary>
/// Returns a list of Max BiPartite Match Edges.
/// </summary>
public List<MatchEdge<T>> GetMaxBiPartiteMatching(IGraph<T> graph)
{
if (@operator == null)
throw new ArgumentException("Provide an operator implementation for generic type T during initialization.");
//check if the graph is BiPartite by coloring 2 colors
var mColorer = new MColorer<T, int>();
var colorResult = mColorer.Color(graph, new[] { 1, 2 });
if (colorResult.CanColor == false) throw new Exception("Graph is not BiPartite.");
return GetMaxBiPartiteMatching(graph, colorResult.Partitions);
}
/// <summary>
/// Get Max Match from Given BiPartitioned Graph.
/// </summary>
private List<MatchEdge<T>> GetMaxBiPartiteMatching(IGraph<T> graph,
Dictionary<int, List<T>> partitions)
{
//add unit edges from dymmy source to group 1 vertices
var dummySource = @operator.GetRandomUniqueVertex();
if (graph.ContainsVertex(dummySource))
throw new Exception("Dummy vertex provided is not unique to given graph.");
//add unit edges from group 2 vertices to sink
var dummySink = @operator.GetRandomUniqueVertex();
if (graph.ContainsVertex(dummySink)) throw new Exception("Dummy vertex provided is not unique to given graph.");
var workGraph = CreateFlowGraph(graph, dummySource, dummySink, partitions);
//run ford fulkerson using edmon karp method
var fordFulkerson = new EdmondKarpMaxFlow<T, int>(@operator);
var flowPaths = fordFulkerson
.ComputeMaxFlowAndReturnFlowPath(workGraph, dummySource, dummySink);
//now gather all group1 to group 2 edges in residual graph with positive flow
var result = new List<MatchEdge<T>>();
foreach (var path in flowPaths) result.Add(new MatchEdge<T>(path[1], path[2]));
return result;
}
/// <summary>
/// create a directed unit weighted graph with given dummySource to Patition 1 and Patition 2 to dummy sink.
/// </summary>
private static WeightedDiGraph<T, int> CreateFlowGraph(IGraph<T> graph,
T dummySource, T dummySink,
Dictionary<int, List<T>> partitions)
{
var workGraph = new WeightedDiGraph<T, int>();
workGraph.AddVertex(dummySource);
foreach (var group1Vertex in partitions[1])
{
workGraph.AddVertex(group1Vertex);
workGraph.AddEdge(dummySource, group1Vertex, 1);
}
workGraph.AddVertex(dummySink);
foreach (var group2Vertex in partitions[2])
{
workGraph.AddVertex(group2Vertex);
workGraph.AddEdge(group2Vertex, dummySink, 1);
}
//now add directed edges from group 1 vertices to group 2 vertices
foreach (var group1Vertex in partitions[1])
foreach (var edge in graph.GetVertex(group1Vertex).Edges)
workGraph.AddEdge(group1Vertex, edge.TargetVertexKey, 1);
return workGraph;
}
}
/// <summary>
/// The match result object.
/// </summary>
public class MatchEdge<T>
{
public MatchEdge(T source, T target)
{
Source = source;
Target = target;
}
public T Source { get; }
public T Target { get; }
public override bool Equals(object obj)
{
if (obj == this) return true;
var tgt = obj as MatchEdge<T>;
if (tgt is null) return false;
return tgt.Source.Equals(Source) && tgt.Target.Equals(Target);
}
public override int GetHashCode()
{
return new { Source, Target }.GetHashCode();
}
}
/// <summary>
/// Generic operator interface required by BiPartite matching algorithm.
/// </summary>
public interface IBiPartiteMatchOperators<T> : IFlowOperators<int>
{
/// <summary>
/// Get a random unique vertex not in graph
/// required for dummy source/destination vertex for ford-fulkerson max flow.
/// </summary>
T GetRandomUniqueVertex();
}