-
Notifications
You must be signed in to change notification settings - Fork 1
/
KClique.cs
179 lines (163 loc) · 6.15 KB
/
KClique.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
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
using MNCD.Clique;
using MNCD.Components;
using MNCD.Core;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MNCD.CommunityDetection.SingleLayer
{
/// <summary>
/// Implements KClique community detection algorithm.
/// </summary>
public class KClique
{
/// <summary>
/// Find k-clique communities in network using the percolation method.
///
/// A k-clique community is the union of all cliques of size k that
/// can be reached through adjacent (sharing k-1 nodes) k-cliques.
/// </summary>
/// <param name="network">Network in which to find communities.</param>
/// <param name="k">Size of smallest clique.</param>
/// <returns>List of communities.</returns>
public List<Community> GetKCommunities(Network network, int k)
{
if (k < 2)
{
throw new ArgumentException("K must be greater than 1.");
}
var cliques = new BronKerbosch()
.GetMaximalCliques(network)
.Where(c => c.Count >= k)
.ToList();
var membership = AssignMembership(cliques);
var cliqueToActor = GetCliqueToActor(cliques);
var actorToClique = GetActorToClique(cliqueToActor);
var percNetwork = GetPercolationNetwork(cliques, membership, cliqueToActor, k);
return percNetwork.Layers
.First()
.GetConnectedComponents(percNetwork.Actors)
.Select(c => new Community(c.SelectMany(a => actorToClique[a])))
.ToList();
}
/// <summary>
/// Gets percolation network.
/// </summary>
/// <param name="cliques">Cliques.</param>
/// <param name="membership">Membership.</param>
/// <param name="cliqueToActor">Mapping of cliques to actors.</param>
/// <param name="k">Size of smallest clique.</param>
/// <returns>Percolation network.</returns>
internal Network GetPercolationNetwork(
List<List<Actor>> cliques,
Dictionary<Actor, List<List<Actor>>> membership,
Dictionary<List<Actor>, Actor> cliqueToActor,
int k)
{
var percNetwork = new Network
{
Actors = cliques.Select(c => cliqueToActor[c]).ToList(),
};
percNetwork.Layers.Add(new Layer());
var edges = new List<Edge>();
foreach (var clique in cliques)
{
foreach (var adjacentClique in GetAdjacentCliques(clique, membership))
{
if (clique.Intersect(adjacentClique).Count() >= (k - 1))
{
var from = cliqueToActor[clique];
var to = cliqueToActor[adjacentClique];
var edge = new Edge(from, to);
if (!edges.Any(e => (e.From == from && e.To == to) ||
(e.From == to && e.To == from)))
{
edges.Add(edge);
}
}
}
}
percNetwork.Layers[0].Edges = edges;
return percNetwork;
}
/// <summary>
/// Creates actor to clique membership.
/// </summary>
/// <param name="cliqueToActor">Clique to actor membership.</param>
/// <returns>Mapping of actor to a clique.</returns>
internal Dictionary<Actor, List<Actor>> GetActorToClique(
Dictionary<List<Actor>, Actor> cliqueToActor)
{
var dict = new Dictionary<Actor, List<Actor>>();
foreach (var pair in cliqueToActor)
{
dict.Add(pair.Value, pair.Key);
}
return dict;
}
/// <summary>
/// Returns mapping of clique to an actor.
/// </summary>
/// <param name="cliques">List of cliques.</param>
/// <returns>Mapping of clique to an actor.</returns>
internal Dictionary<List<Actor>, Actor> GetCliqueToActor(List<List<Actor>> cliques)
{
var i = 0;
return cliques.ToDictionary(c => c, c =>
{
var id = i++;
return new Actor(id, "c" + id);
});
}
/// <summary>
/// Computes adjacent cliques for supplied clique.
/// </summary>
/// <param name="clique">Clique for which adjacent cliques should be found.</param>
/// <param name="membership">Membership.</param>
/// <returns>List of adjacent cliques.</returns>
internal List<List<Actor>> GetAdjacentCliques(
List<Actor> clique,
Dictionary<Actor, List<List<Actor>>> membership)
{
var adjacent = new HashSet<List<Actor>>();
foreach (var actor in clique)
{
foreach (var adjacentClique in membership[actor])
{
if (adjacentClique != clique)
{
adjacent.Add(adjacentClique);
}
}
}
return adjacent.ToList();
}
/// <summary>
/// Creates membership of actor to clique.
/// </summary>
/// <param name="cliques">List of cliques.</param>
/// <returns>Dictionary of actors and cliques.</returns>
internal Dictionary<Actor, List<List<Actor>>> AssignMembership(IEnumerable<List<Actor>> cliques)
{
var membership = new Dictionary<Actor, List<List<Actor>>>();
foreach (var c in cliques)
{
foreach (var a in c)
{
if (membership.ContainsKey(a))
{
membership[a].Add(c);
}
else
{
membership[a] = new List<List<Actor>>
{
c,
};
}
}
}
return membership;
}
}
}