package org.egonet.graph;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class KPlexes<N> {
// graph is Map<N,Set<N>>
// kplex is <Set<N>>
public Map<N,Integer> connectionsByNode(Map<N,Set<N>> graph) {
Map<N,Integer> result = Maps.newHashMap();
for(N n : graph.keySet()) {
result.put(n, graph.get(n).size());
}
return result;
}
// This is similar to hIndex.
// Connectedness of N for having N connections to nodes with N connections.
public Map<N,Integer> connectednessByNode(Map<N,Set<N>> graph) {
Map<N,Integer> connectionsByNode = connectionsByNode(graph);
Map<N,Integer> results = Maps.newHashMap();
for(N n1 : graph.keySet()) {
List<Integer> connectednessOfConnections = Lists.newArrayList();
for(N n2 : graph.get(n1)) {
connectednessOfConnections.add(connectionsByNode.get(n2));
}
Collections.sort(connectednessOfConnections);
Collections.reverse(connectednessOfConnections);
Integer result = 0;
for(Integer i = 0; i < connectednessOfConnections.size(); i++) {
if(i < connectednessOfConnections.get(i)) {
result = i+1;
}
}
results.put(n1, result);
}
return results;
}
public Map<N,Integer> connectionsWithinSubgroup(Map<N,Set<N>> graph, Set<N> subgroup) {
Map<N,Integer> result = Maps.newHashMap();
for(N n : graph.keySet()) {
result.put(n, Sets.intersection(graph.get(n),subgroup).size());
}
return result;
}
public Integer highestConnectedness(Map<N,Set<N>> graph) {
Integer highest = 0;
for(Integer connectedness : connectednessByNode(graph).values()) {
if(connectedness > highest) {
highest = connectedness;
}
}
return highest;
}
public Set<N> meetConnectednessThreshold(Map<N,Set<N>> graph, Integer threshold) {
Set<N> result = Sets.newHashSet();
Map<N,Integer> connectedness = connectednessByNode(graph);
for(N n : graph.keySet()) {
if(connectedness.get(n) > threshold-1) {
result.add(n);
}
}
return result;
}
public Integer largestPossibleKPlex(Map<N,Set<N>> graph, Integer k) {
return highestConnectedness(graph)+k;
}
public Set<N> criticalNodesInKPlex(Map<N,Set<N>> graph, Set<N> kplex, Integer k) {
Map<N,Integer> connectionsWithinKPlex = connectionsWithinSubgroup(graph,kplex);
Set<N> criticalNodes = Sets.newHashSet();
for(N n : kplex) {
Integer connections = connectionsWithinKPlex.get(n);
if(connections == null) {
throw new RuntimeException("null connections for "+n+" in "+kplex+" of graph "+graph);
}
if(connections < kplex.size()-k+1) {
criticalNodes.add(n);
}
}
return criticalNodes;
}
public Set<N> nodesThatCanBeAddedToKPlex(Map<N,Set<N>> graph, Set<N> kplex, Integer k) {
if(kplex.isEmpty()) {
if(k > 0) {
return graph.keySet();
} else {
return new HashSet<N>();
}
}
Set<N> criticalNodes = criticalNodesInKPlex(graph, kplex, k);
if(criticalNodes.isEmpty()) {
Map<N,Integer> connectionsWithinKPlex = connectionsWithinSubgroup(graph,kplex);
Set<N> neighbors = Sets.newHashSet();
for(N n : graph.keySet()) {
if(connectionsWithinKPlex.get(n) > 0) {
neighbors.add(n);
}
}
neighbors.removeAll(kplex);
return Sets.difference(neighbors,kplex);
}
Set<N> eligible = Sets.difference(graph.keySet(),kplex);
for(N n : criticalNodes) {
// Must be neighbor of all critical nodes.
eligible = Sets.intersection(eligible, graph.get(n));
}
return eligible;
}
public Map<N,Set<N>> createSubgraph(Map<N,Set<N>> graph, Set<N> nodes) {
Map<N,Set<N>> subgraph = Maps.newHashMap();
for(N n : nodes) {
subgraph.put(n, Sets.intersection(graph.get(n),nodes));
}
return subgraph;
}
public Map<N,Set<N>> subgraphBoundingFinalKPlex(
Map<N,Set<N>> graph, Set<N> kplex, Integer k, Integer targetSize)
{
Set<N> includeInSubgraph =
Sets.union(kplex,
Sets.intersection(
meetConnectednessThreshold(graph,targetSize-k),
nodesThatCanBeAddedToKPlex(graph,kplex,k)));
return createSubgraph(graph,includeInSubgraph);
}
public N chooseNodeForInclusionInKPlex(Map<N,Set<N>> graph, Set<N> kplex, Integer k) {
Integer highScore = 0;
N choice = null;
Map<N,Integer> connectedness = connectednessByNode(graph);
Map<N,Integer> connectionsWithinKPlex = connectionsWithinSubgroup(graph,kplex);
Set<N> addable = nodesThatCanBeAddedToKPlex(graph, kplex, k);
for(N n : addable) {
Integer score = connectedness.get(n) + connectionsWithinKPlex.get(n);
if(score > highScore) {
highScore = score;
choice = n;
}
}
return choice;
}
public Set<N> growKPlex(Map<N,Set<N>> graph, Set<N> kplex, Integer k, Integer targetSize) {
Map<N,Set<N>> boundingGraph = subgraphBoundingFinalKPlex(graph,kplex,k,targetSize);
N newNode = chooseNodeForInclusionInKPlex(graph,kplex,k);
if(newNode == null) {
return kplex;
}
Set<N> newKPlex = Sets.newHashSet();
newKPlex.add(newNode);
newKPlex.addAll(kplex);
return growKPlex(boundingGraph,newKPlex,k,targetSize);
}
public Set<N> findLargeKPlex(Map<N,Set<N>> graph, Integer k) {
Set<N> largestFound = Sets.newHashSet();
for(Integer targetSize = largestPossibleKPlex(graph, k);
targetSize > largestFound.size();
targetSize--)
{
Set<N> seeds = meetConnectednessThreshold(graph, targetSize-k);
Map<N,Set<N>> boundedGraph = createSubgraph(graph,seeds);
for(N seed : seeds) {
Set<N> kplex = Sets.newHashSet();
kplex.add(seed);
kplex = growKPlex(boundedGraph,kplex,k,targetSize);
if(kplex.size() > largestFound.size()) {
largestFound = kplex;
}
}
}
return largestFound;
}
}