Skip to content

Commit

Permalink
Rely on layout and clustering algorithms from networkanalysis-ts pack…
Browse files Browse the repository at this point in the history
…age (#22)

* Implement new network data structure

* Implement new Leiden algorithm

* Implement new VOS algorithm

* Remove old algorithms and data structures

* Add networkanalysis-ts package dependency
  • Loading branch information
neesjanvaneck authored Sep 3, 2022
1 parent 31ed4f3 commit 8735e3d
Show file tree
Hide file tree
Showing 15 changed files with 91 additions and 1,130 deletions.
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"lodash": "^4.17.21",
"mobx": "^6.6.1",
"mobx-react-lite": "^3.4.0",
"networkanalysis-ts": "^1.0.0",
"papaparse": "^5.3.2",
"qrcode.react": "^1.0.1",
"qs": "^6.11.0",
Expand Down
4 changes: 2 additions & 2 deletions src/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const VOSviewer = observer(({ width, targetRef, parameters = {}, data }) => {
break;
case 'end run layout':
uiStore.setLoadingScreenProgressValue(100);
visualizationStore.setLayout(data.bestLayout.coordinate);
visualizationStore.updateItemCoordinates(data.newCoordinates);
uiStore.setLoadingScreenIsOpen(false);
if (webworkerStore.runClustering) {
webworkerStore.startRunClustering(clusteringStore.getParameters());
Expand All @@ -152,7 +152,7 @@ const VOSviewer = observer(({ width, targetRef, parameters = {}, data }) => {
break;
case 'end run clustering':
uiStore.setLoadingScreenProgressValue(100);
visualizationStore.setClusters(data.bestClustering, uiStore.darkTheme);
visualizationStore.updateItemClusters(data.newClusters, uiStore.darkTheme);
uiStore.setLoadingScreenIsOpen(false);
_finalizeVisualization();
break;
Expand Down
11 changes: 11 additions & 0 deletions src/store/clustering/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export default class State {
mergeSmallClusters = ClusteringCreator.DEFAULT_MERGE_SMALL_CLUSTERS

nIterations = ClusteringCreator.DEFAULT_N_ITERATIONS

randomness = ClusteringCreator.DEFAULT_RANDOMNESS

nRandomStarts = ClusteringCreator.DEFAULT_N_RANDOM_STARTS

Expand All @@ -29,6 +31,7 @@ export default class State {
minClusterSize: this.minClusterSize,
mergeSmallClusters: this.mergeSmallClusters,
nIterations: this.nIterations,
randomness: this.randomness,
nRandomStarts: this.nRandomStarts,
fixedSeed: this.fixedSeed,
useRandomSeed: this.useRandomSeed,
Expand Down Expand Up @@ -63,6 +66,14 @@ export default class State {
}
}

setRandomness(randomness, onBlur) {
if (onBlur) {
this.randomness = _clamp(+randomness, 0.0005, 0.1);
} else {
this.randomness = randomness;
}
}

setNRandomStarts(nRandomStarts, onBlur) {
if (onBlur) {
this.nRandomStarts = _clamp(Math.round(+nRandomStarts), 1, 10000);
Expand Down
6 changes: 3 additions & 3 deletions src/store/visualization/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ export default class State {
this.largestComponent = largestComponent;
}

setLayout(coordinates) {
updateItemCoordinates(coordinates) {
_each(coordinates[0], (x, i) => {
const item = _find(this.items, d => d._initialOrderId === i);
if (item) {
Expand All @@ -796,10 +796,10 @@ export default class State {
this.updateLabelScalingFactors();
}

setClusters(clustering, darkTheme) {
updateItemClusters(clusters, darkTheme) {
this.clusterKey = mapDataHeaders.CLUSTER;
_each(this.items, (item) => {
const cluster = clustering.cluster[this.itemIdToIndex[item.id]];
const cluster = clusters[this.itemIdToIndex[item.id]];
item[this.clusterKey] = !_isNil(cluster) ? cluster + 1 : undefined;
});
this.clusters = _filter(_keys(_groupBy(this.items, this.clusterKey)), d => d !== "undefined");
Expand Down
91 changes: 0 additions & 91 deletions src/utils/networkanalysis/Clustering.js

This file was deleted.

43 changes: 20 additions & 23 deletions src/utils/networkanalysis/ClusteringCreator.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,56 @@
import Random from 'java-random';
import ClusteringTechnique from './ClusteringTechnique';
import { Clustering, LeidenAlgorithm } from 'networkanalysis-ts';

export const DEFAULT_RESOLUTION = 1.0;
export const DEFAULT_MIN_CLUSTER_SIZE = 1;
export const DEFAULT_MERGE_SMALL_CLUSTERS = true;
export const DEFAULT_N_ITERATIONS = 10;
export const DEFAULT_RANDOMNESS = 0.01;
export const DEFAULT_N_RANDOM_STARTS = 10;
export const DEFAULT_FIXED_SEED = 0;
export const DEFAULT_USE_RANDOM_SEED = false;

export class ClusteringCreator {
constructor() {
this.bestClustering = undefined;
this.maxQualityFunction = -Infinity;
}

init(network, parameters, useLinLogModularityNormalization) {
this.network = network;
this.resolution = parameters.resolution;
this.minClusterSize = parameters.minClusterSize;
this.mergeSmallClusters = parameters.mergeSmallClusters;
this.nIterations = parameters.nIterations;
this.randomness = parameters.randomness;
this.nRandomStarts = parameters.nRandomStarts;
this.fixedSeed = parameters.fixedSeed;
this.useRandomSeed = parameters.useRandomSeed;
this.useLinLogModularityNormalization = useLinLogModularityNormalization;

this.random = this.useRandomSeed ? new Random() : new Random(this.fixedSeed);

let resolution2 = parameters.resolution;
if (this.useLinLogModularityNormalization) {
resolution2 /= (2 * this.network.getTotalEdgeWeight());
}
this.clusteringAlgorithm = new LeidenAlgorithm();
this.clusteringAlgorithm.initializeBasedOnResolutionAndNIterationsAndRandomnessAndRandom(resolution2, this.nIterations, this.randomness, this.random);
this.bestClustering = undefined;
this.maxQualityFunction = -Infinity;
this.maxQuality = -Infinity;
this.randomStart = 0;
}

performRandomStart() {
let { resolution } = this;
if (this.useLinLogModularityNormalization) {
resolution /= (2 * this.network.getTotalEdgeWeight());
}
const clusteringTechnique = new ClusteringTechnique({ network: this.network, resolution });
clusteringTechnique.runIteratedSmartLocalMovingAlgorithm(this.nIterations, this.random);
const qualityFunction = clusteringTechnique.calcQualityFunction();
if ((this.bestClustering === undefined) || (qualityFunction > this.maxQualityFunction)) {
this.bestClustering = clusteringTechnique.getClustering();
this.maxQualityFunction = qualityFunction;
const clustering = new Clustering({ nNodes: this.network.getNNodes() });
this.clusteringAlgorithm.improveClustering(this.network, clustering);
const quality = this.clusteringAlgorithm.calcQuality(this.network, clustering);
if ((this.bestClustering === undefined) || (quality > this.maxQuality)) {
this.bestClustering = clustering;
this.maxQuality = quality;
}
this.randomStart += 1;
}

performPostProcessing() {
const { resolution, mergeSmallClusters, minClusterSize } = this;
const clusteringTechnique = new ClusteringTechnique({ network: this.network, clustering: this.bestClustering, resolution });
if (mergeSmallClusters) {
clusteringTechnique.removeSmallClustersBasedOnNNodes(minClusterSize);
if (this.mergeSmallClusters) {
this.clusteringAlgorithm.removeSmallClustersBasedOnNNodes(this.network, this.bestClustering, this.minClusterSize);
}

this.bestClustering = clusteringTechnique.getClustering();
this.bestClustering.orderClustersByNNodes();
}
}
Loading

0 comments on commit 8735e3d

Please sign in to comment.