use of au.gov.asd.tac.constellation.plugins.algorithms.clustering.infomap.Node in project constellation by constellation-app.
the class InfomapGreedy method moveNodesToPredefinedModules.
@Override
protected void moveNodesToPredefinedModules() {
if (DEBUG) {
LOGGER.log(Level.INFO, "{0}.moveNodesToPredefinedModules", getClass().getSimpleName());
}
// Size of active network and cluster array should match.
assert moveTo.size() == activeNetwork.size();
final int numNodes = activeNetwork.size();
if (DEBUG) {
final String log = String.format("Begin moving %d nodes to predefined modules, starting with codelength %f...\n", numNodes, codelength);
LOGGER.log(Level.INFO, log);
}
int numMoved = 0;
for (int k = 0; k < numNodes; k++) {
final Node current = getNode(activeNetwork.get(k));
// == k
final int oldM = current.getIndex();
assert oldM == k;
final int newM = moveTo.get(k);
if (newM != oldM) {
final DeltaFlow oldModuleDelta = new DeltaFlow(oldM, 0, 0);
final DeltaFlow newModuleDelta = new DeltaFlow(newM, 0, 0);
addTeleportationDeltaFlowOnOldModuleIfMove(current, oldModuleDelta);
addTeleportationDeltaFlowOnNewModuleIfMove(current, newModuleDelta);
// For all outlinks.
for (final Edge<NodeBase> edge : current.getOutEdges()) {
if (edge.isSelfPointing()) {
continue;
}
final int otherModule = edge.getTarget().getIndex();
if (otherModule == oldM) {
oldModuleDelta.setDeltaExit(oldModuleDelta.getDeltaExit() + edge.getData().flow);
} else if (otherModule == newM) {
newModuleDelta.setDeltaExit(newModuleDelta.getDeltaExit() + edge.getData().flow);
} else {
// Do nothing
}
}
// For all inlinks.
for (final Edge<NodeBase> edge : current.getInEdges()) {
if (edge.isSelfPointing()) {
continue;
}
final int otherModule = edge.getSource().getIndex();
if (otherModule == oldM) {
oldModuleDelta.setDeltaEnter(oldModuleDelta.getDeltaEnter() + edge.getData().flow);
} else if (otherModule == newM) {
newModuleDelta.setDeltaEnter(newModuleDelta.getDeltaEnter() + edge.getData().flow);
} else {
// Do nothing
}
}
// Update empty module vector.
if (moduleMembers[newM] == 0) {
// Remove last element.
emptyModules.remove(emptyModules.size() - 1);
}
if (moduleMembers[oldM] == 1) {
emptyModules.add(oldM);
}
updateCodelength(current, oldModuleDelta, newModuleDelta);
moduleMembers[oldM] -= 1;
moduleMembers[newM] += 1;
current.setIndex(newM);
numMoved++;
}
}
if (DEBUG) {
final String log = String.format("Done! Moved %d nodes into %d modules to codelength: %.5f\n", numMoved, getNumActiveModules(), codelength);
LOGGER.log(Level.INFO, log);
}
}
use of au.gov.asd.tac.constellation.plugins.algorithms.clustering.infomap.Node in project constellation by constellation-app.
the class InfomapGreedy method initModuleOptimization.
@Override
protected void initModuleOptimization() {
final int numNodes = activeNetwork.size();
moduleFlowData = Resizer.resizeFlowBase(moduleFlowData, numNodes, treeData.getNodeFactory());
moduleMembers = new int[numNodes];
Arrays.fill(moduleMembers, 1);
emptyModules = new ArrayList<>(numNodes);
int i = 0;
for (final NodeBase nodeBase : activeNetwork) {
final Node node = getNode(nodeBase);
// Unique module index for each node
node.setIndex(i);
moduleFlowData[i] = node.getData().copy();
i++;
}
// Initiate codelength terms for the initial state of one module per node.
calculateCodelengthFromActiveNetwork(hasDetailedBalance());
}
use of au.gov.asd.tac.constellation.plugins.algorithms.clustering.infomap.Node in project constellation by constellation-app.
the class InfomapGreedy method tryMoveEachNodeIntoBestModule.
/**
* Try to minimize the codelength by trying to move nodes into the same
* modules as neighbouring nodes.
*
* For each node: 1. Calculate the change in codelength for a move to each
* of its neighbouring modules or to an empty module 2. Move to the one that
* reduces the codelength the most, if any.
*
* The first step would require O(d^2), where d is the degree, if
* calculating the full change at each neighbour, but a special data
* structure is used to accumulate the marginal effect of each link on its
* target, giving O(d).
*
* @return The number of nodes moved.
*/
int tryMoveEachNodeIntoBestModule() {
if (DEBUG) {
LOGGER.log(Level.INFO, "{0}.tryMoveEachNodeIntoBestModule", getClass().getSimpleName());
}
final int numNodes = activeNetwork.size();
dumpActiveNetwork("in");
// Get random enumeration of nodes.
final int[] randomOrder = new int[numNodes];
InfoMath.getRandomizedIndexVector(randomOrder, rand);
final DeltaFlow[] moduleDeltaEnterExit = new DeltaFlow[numNodes];
Arrays.fill(moduleDeltaEnterExit, new DeltaFlow());
final int[] redirect = new int[numNodes];
Arrays.fill(redirect, 0);
int offset = 1;
final int maxOffset = Integer.MAX_VALUE - 1 - numNodes;
int numMoved = 0;
for (int i = 0; i < numNodes; i++) {
// Reset offset before overflow.
if (offset > maxOffset) {
Arrays.fill(redirect, 0);
offset = 1;
}
// Pick nodes in random order.
final int flip = randomOrder[i];
final Node current = getNode(activeNetwork.get(flip));
// and others won't move into this. TODO: always best leave it alone?
if (current.getDegree() == 0 || (config.isIncludeSelfLinks() && (current.getOutDegree() == 1 && current.getInDegree() == 1) && current.getOutEdges().get(0).getTarget().equals(current))) {
LOGGER.log(Level.INFO, "SKIPPING isolated node {0}", current);
// TODO: if not skipping self-links, this yields different results from moveNodesToPredefinedModules!!
assert !config.isIncludeSelfLinks();
continue;
}
// Create vector with module links.
int numModuleLinks = 0;
if (current.isDangling()) {
redirect[current.getIndex()] = offset + numModuleLinks;
moduleDeltaEnterExit[numModuleLinks].setModule(current.getIndex());
moduleDeltaEnterExit[numModuleLinks].setDeltaExit(0);
moduleDeltaEnterExit[numModuleLinks].setDeltaEnter(0);
numModuleLinks++;
} else {
// For all outlinks.
for (final Edge<NodeBase> edge : current.getOutEdges()) {
if (edge.isSelfPointing()) {
continue;
}
final NodeBase neighbour = edge.getTarget();
if (redirect[neighbour.getIndex()] >= offset) {
moduleDeltaEnterExit[redirect[neighbour.getIndex()] - offset].setDeltaExit(moduleDeltaEnterExit[redirect[neighbour.getIndex()] - offset].getDeltaExit() + edge.getData().flow);
} else {
redirect[neighbour.getIndex()] = offset + numModuleLinks;
moduleDeltaEnterExit[numModuleLinks].setModule(neighbour.getIndex());
moduleDeltaEnterExit[numModuleLinks].setDeltaExit(edge.getData().flow);
moduleDeltaEnterExit[numModuleLinks].setDeltaEnter(0);
numModuleLinks++;
}
}
}
// For all inlinks.
for (final Edge<NodeBase> edge : current.getInEdges()) {
if (edge.isSelfPointing()) {
continue;
}
final Node neighbour = getNode(edge.getSource());
if (redirect[neighbour.getIndex()] >= offset) {
moduleDeltaEnterExit[redirect[neighbour.getIndex()] - offset].setDeltaEnter(moduleDeltaEnterExit[redirect[neighbour.getIndex()] - offset].getDeltaEnter() + edge.getData().flow);
} else {
redirect[neighbour.getIndex()] = offset + numModuleLinks;
moduleDeltaEnterExit[numModuleLinks].setModule(neighbour.getIndex());
moduleDeltaEnterExit[numModuleLinks].setDeltaExit(0);
moduleDeltaEnterExit[numModuleLinks].setDeltaEnter(edge.getData().flow);
numModuleLinks++;
}
}
// If alone in the module, add virtual link to the module (used when adding teleportation).
if (redirect[current.getIndex()] < offset) {
redirect[current.getIndex()] = offset + numModuleLinks;
moduleDeltaEnterExit[numModuleLinks].setModule(current.getIndex());
moduleDeltaEnterExit[numModuleLinks].setDeltaExit(0);
moduleDeltaEnterExit[numModuleLinks].setDeltaEnter(0);
numModuleLinks++;
}
// Empty function if no teleportation coding model.
addTeleportationDeltaFlowIfMove(current, moduleDeltaEnterExit, numModuleLinks);
// Option to move to empty module (if node not already alone).
if (moduleMembers[current.getIndex()] > 1 && !emptyModules.isEmpty()) {
moduleDeltaEnterExit[numModuleLinks].setModule(emptyModules.get(emptyModules.size() - 1));
moduleDeltaEnterExit[numModuleLinks].setDeltaExit(0);
moduleDeltaEnterExit[numModuleLinks].setDeltaEnter(0);
numModuleLinks++;
}
// Store the DeltaFlow of the current module.
final DeltaFlow oldModuleDelta = new DeltaFlow(moduleDeltaEnterExit[redirect[current.getIndex()] - offset]);
if (DEBUG) {
for (int j = 0; j < numModuleLinks - 1; ++j) {
LOGGER.log(Level.INFO, "{0}", moduleDeltaEnterExit[j].getModule());
}
}
// Randomize link order for optimized search.
for (int j = 0; j < numModuleLinks - 1; ++j) {
final int randPos = j + rand.randInt(numModuleLinks - j - 1);
final DeltaFlow t = moduleDeltaEnterExit[j];
moduleDeltaEnterExit[j] = moduleDeltaEnterExit[randPos];
moduleDeltaEnterExit[randPos] = t;
}
DeltaFlow bestDeltaModule = new DeltaFlow(oldModuleDelta);
double bestDeltaCodelength = 0;
// Find the move that minimizes the description length.
for (int j = 0; j < numModuleLinks; ++j) {
final int otherModule = moduleDeltaEnterExit[j].getModule();
if (otherModule != current.getIndex()) {
double deltaCodelength = getDeltaCodelength(current, oldModuleDelta, moduleDeltaEnterExit[j]);
if (deltaCodelength < bestDeltaCodelength) {
bestDeltaModule = new DeltaFlow(moduleDeltaEnterExit[j]);
bestDeltaCodelength = deltaCodelength;
}
}
}
// Make best possible move.
if (bestDeltaModule.getModule() != current.getIndex()) {
final int bestModuleIndex = bestDeltaModule.getModule();
// Update empty module vector.
if (moduleMembers[bestModuleIndex] == 0) {
emptyModules.remove(emptyModules.size() - 1);
}
if (moduleMembers[current.getIndex()] == 1) {
emptyModules.add(current.getIndex());
}
updateCodelength(current, oldModuleDelta, bestDeltaModule);
moduleMembers[current.getIndex()] -= 1;
moduleMembers[bestModuleIndex] += 1;
current.setIndex(bestModuleIndex);
numMoved++;
}
offset += numNodes;
}
dumpActiveNetwork("");
return numMoved;
}
use of au.gov.asd.tac.constellation.plugins.algorithms.clustering.infomap.Node in project constellation by constellation-app.
the class InfomapGreedy method calculateCodelengthFromActiveNetwork.
protected void calculateCodelengthFromActiveNetwork(final boolean detailedBalance) {
if (DEBUG) {
final String log = String.format("%s.calculateCodelengthFromActiveNetwork(%s)%n", getClass().getSimpleName(), detailedBalance);
LOGGER.log(Level.INFO, log);
}
flowLogFlow = 0;
exitLogExit = 0;
enterFlow = 0;
if (detailedBalance) {
// For each module...
for (final NodeBase nodeBase : activeNetwork) {
final Node node = getNode(nodeBase);
// Own node/module codebook.
flowLogFlow += plogp(node.getData().getFlow() + node.getData().getExitFlow());
// Use of index codebook.
enterFlow += node.getData().getExitFlow();
exitLogExit += plogp(node.getData().getExitFlow());
}
enterFlow += exitNetworkFlow;
enterFlowLogEnterFlow = plogp(enterFlow);
indexCodelength = enterFlowLogEnterFlow - exitLogExit - exitNetworkFlowLogExitNetworkFlow;
moduleCodelength = -exitLogExit + flowLogFlow - nodeFlowLogNodeFlow;
codelength = indexCodelength + moduleCodelength;
} else {
enterLogEnter = 0;
// For each module...
for (final NodeBase nodeBase : activeNetwork) {
final Node node = getNode(nodeBase);
// Own node/module codebook.
flowLogFlow += plogp(node.getData().getFlow() + node.getData().getExitFlow());
// Use of index codebook.
enterLogEnter += plogp(node.getData().getEnterFlow());
exitLogExit += plogp(node.getData().getExitFlow());
enterFlow += node.getData().getEnterFlow();
}
enterFlow += exitNetworkFlow;
enterFlowLogEnterFlow = plogp(enterFlow);
indexCodelength = enterFlowLogEnterFlow - enterLogEnter - exitNetworkFlowLogExitNetworkFlow;
moduleCodelength = -exitLogExit + flowLogFlow - nodeFlowLogNodeFlow;
codelength = indexCodelength + moduleCodelength;
}
}
use of au.gov.asd.tac.constellation.plugins.algorithms.clustering.infomap.Node in project constellation by constellation-app.
the class InfomapGreedy method initConstantInfomapTerms.
@Override
protected void initConstantInfomapTerms() {
nodeFlowLogNodeFlow = 0;
// For each module...
for (final NodeBase nodeBase : activeNetwork) {
final Node node = getNode(nodeBase);
nodeFlowLogNodeFlow += plogp(node.getData().getFlow());
}
}