Skip to content

Commit

Permalink
fix(rng)!: use better seedable RNG (#352)
Browse files Browse the repository at this point in the history
* fix(rng)!: use better seedable RNG

BREAKING CHANGE: The layout for the same graph and seed is different
when compared to the previous version.

This replaces the Math.sin() based ones and Math.random() with Alea
based RNG from Vis Util.

* test(physics): add simple pinning tests

This simply runs some basic physics configurations on a few sets of data
and tests that the positions of the nodes after stabilization didn't
change from the last time.

* test(snapshots): remove only

I had to change Math.pow to **. No idea why but without this change it
didn't want to output the same values. I just hope this is not going to
break in CI or other people's machines.
  • Loading branch information
Thomaash committed Jan 18, 2020
1 parent 1deddf9 commit d10717b
Show file tree
Hide file tree
Showing 21 changed files with 2,202 additions and 523 deletions.
771 changes: 771 additions & 0 deletions __snapshots__/test/snapshots.test.ts.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/network/basic_usage/standalone.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
src="../../../standalone/umd/vis-network.js"
></script>
</head>

Expand Down
6 changes: 4 additions & 2 deletions examples/network/data/importingFromGephi.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@

<meta name="example-screenshot-selector" content="body" />

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>

<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<style type="text/css">
#mynetwork {
width: 800px;
Expand Down
10 changes: 5 additions & 5 deletions examples/network/exampleUtil.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/**
* Created by Alex on 5/20/2015.
*
* @remarks
* This depends on Alea from Vis Util and therefore has to be loaded AFTER Vis
* Util or standalone Vis Network has already been loaded.
*/

function loadJSON(path, success, error) {
Expand Down Expand Up @@ -69,11 +73,7 @@ function getScaleFreeNetwork(nodeCount) {
return {nodes:nodes, edges:edges};
}

var randomSeed = 764; // Math.round(Math.random()*1000);
function seededRandom() {
var x = Math.sin(randomSeed++) * 10000;
return x - Math.floor(x);
}
var seededRandom = vis.util.Alea('SEED')

function getScaleFreeNetworkSeeded(nodeCount, seed) {
if (seed) {
Expand Down
5 changes: 4 additions & 1 deletion examples/network/layout/hierarchicalLayout.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
}
</style>

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<script type="text/javascript">
var nodes = null;
Expand Down
5 changes: 4 additions & 1 deletion examples/network/other/animationShowcase.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@
}
</style>

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<script type="text/javascript">
var nodes = null;
Expand Down
6 changes: 4 additions & 2 deletions examples/network/other/configuration.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
}
</style>


<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<script type="text/javascript">
var nodes = null;
Expand Down
6 changes: 5 additions & 1 deletion examples/network/other/manipulation.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@
text-align: center;
}
</style>

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<script type="text/javascript">
var nodes = null;
Expand Down
6 changes: 5 additions & 1 deletion examples/network/other/manipulationEditEdgeNoDrag.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,12 @@
text-align: center;
}
</style>

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<script type="text/javascript">
var nodes = null;
Expand Down
5 changes: 4 additions & 1 deletion examples/network/other/navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@
}
</style>

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<script type="text/javascript">
var nodes = null;
Expand Down
5 changes: 4 additions & 1 deletion examples/network/other/performance.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
}
</style>

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<script type="text/javascript">
var nodes = null;
Expand Down
5 changes: 4 additions & 1 deletion examples/network/other/saveAndLoad.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@
}
</style>

<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>
</head>

<body>
Expand Down
5 changes: 4 additions & 1 deletion examples/network/physics/physicsConfiguration.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
</style>


<script
type="text/javascript"
src="../../../standalone/umd/vis-network.min.js"
></script>
<script type="text/javascript" src="../exampleUtil.js"></script>
<script type="text/javascript" src="../../../standalone/umd/vis-network.min.js"></script>

<script type="text/javascript">
var nodes = null;
Expand Down
16 changes: 8 additions & 8 deletions lib/network/modules/KamadaKawai.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class KamadaKawai {
*/
_getEnergy(m) {
let [dE_dx,dE_dy] = this.E_sums[m];
let delta_m = Math.sqrt(Math.pow(dE_dx, 2) + Math.pow(dE_dy, 2));
let delta_m = Math.sqrt((dE_dx ** 2) + (dE_dy ** 2));
return [delta_m, dE_dx, dE_dy];
}

Expand Down Expand Up @@ -150,10 +150,10 @@ class KamadaKawai {
let y_i = nodes[i].y;
let kmat = km[i];
let lmat = lm[i];
let denominator = 1.0 / Math.pow(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2), 1.5);
d2E_dx2 += kmat * (1 - lmat * Math.pow(y_m - y_i, 2) * denominator);
let denominator = 1.0 / ((((x_m - x_i) ** 2) + ((y_m - y_i) ** 2)) ** 1.5);
d2E_dx2 += kmat * (1 - lmat * ((y_m - y_i) ** 2) * denominator);
d2E_dxdy += kmat * (lmat * (x_m - x_i) * (y_m - y_i) * denominator);
d2E_dy2 += kmat * (1 - lmat * Math.pow(x_m - x_i, 2) * denominator);
d2E_dy2 += kmat * (1 - lmat * ((x_m - x_i) ** 2) * denominator);
}
}
// make the variable names easier to make the solving of the linear system easier to read
Expand Down Expand Up @@ -204,7 +204,7 @@ class KamadaKawai {
for (let i = 0; i < nodesArray.length; i++) {
this.K_matrix[nodesArray[i]] = {};
for (let j = 0; j < nodesArray.length; j++) {
this.K_matrix[nodesArray[i]][nodesArray[j]] = edgeStrength * Math.pow(D_matrix[nodesArray[i]][nodesArray[j]], -2);
this.K_matrix[nodesArray[i]][nodesArray[j]] = edgeStrength * (D_matrix[nodesArray[i]][nodesArray[j]] ** -2);
}
}
}
Expand Down Expand Up @@ -232,7 +232,7 @@ class KamadaKawai {
if (i !== m) {
let x_i = nodes[i].x;
let y_i = nodes[i].y;
let denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2));
let denominator = 1.0 / Math.sqrt(((x_m - x_i) ** 2) + ((y_m - y_i) ** 2));
this.E_matrix[m][iIdx] = [
this.K_matrix[m][i] * ((x_m - x_i) - this.L_matrix[m][i] * (x_m - x_i) * denominator),
this.K_matrix[m][i] * ((y_m - y_i) - this.L_matrix[m][i] * (y_m - y_i) * denominator)
Expand Down Expand Up @@ -274,7 +274,7 @@ class KamadaKawai {
//Calc new energy:
let x_i = nodes[i].x;
let y_i = nodes[i].y;
let denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2));
let denominator = 1.0 / Math.sqrt(((x_m - x_i) ** 2) + ((y_m - y_i) ** 2));
let dx = kcolm[i] * ((x_m - x_i) - lcolm[i] * (x_m - x_i) * denominator);
let dy = kcolm[i] * ((y_m - y_i) - lcolm[i] * (y_m - y_i) * denominator);
colm[iIdx] = [dx, dy];
Expand All @@ -292,4 +292,4 @@ class KamadaKawai {
}
}

export default KamadaKawai;
export default KamadaKawai;
39 changes: 23 additions & 16 deletions lib/network/modules/LayoutEngine.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
fillLevelsByDirectionLeaves,
fillLevelsByDirectionRoots
} from "./layout-engine";
import { Alea } from "vis-util";


/**
Expand Down Expand Up @@ -332,8 +333,10 @@ class LayoutEngine {
constructor(body) {
this.body = body;

this.initialRandomSeed = Math.round(Math.random() * 1000000);
this.randomSeed = this.initialRandomSeed;
// Make sure there always is some RNG because the setOptions method won't
// set it unless there's a seed for it.
this._resetRNG(Math.random() + ":" + Date.now());

this.setPhysics = false;
this.options = {};
this.optionsBackup = {physics:{}};
Expand Down Expand Up @@ -395,7 +398,10 @@ class LayoutEngine {
let prevHierarchicalState = hierarchical.enabled;
util.selectiveDeepExtend(["randomSeed", "improvedLayout", "clusterThreshold"],this.options, options);
util.mergeOptions(this.options, options, 'hierarchical');
if (options.randomSeed !== undefined) {this.initialRandomSeed = options.randomSeed;}

if (options.randomSeed !== undefined) {
this._resetRNG(options.randomSeed);
}

if (hierarchical.enabled === true) {
if (prevHierarchicalState === true) {
Expand Down Expand Up @@ -433,6 +439,16 @@ class LayoutEngine {
return allOptions;
}

/**
* Reset the random number generator with given seed.
*
* @param {any} seed - The seed that will be forwarded the the RNG.
*/
_resetRNG(seed) {
this.initialRandomSeed = seed;
this._rng = Alea(this.initialRandomSeed);
}

/**
*
* @param {Object} allOptions
Expand Down Expand Up @@ -514,26 +530,17 @@ class LayoutEngine {
return allOptions;
}

/**
*
* @returns {number}
*/
seededRandom() {
let x = Math.sin(this.randomSeed++) * 10000;
return x - Math.floor(x);
}

/**
*
* @param {Array.<Node>} nodesArray
*/
positionInitially(nodesArray) {
if (this.options.hierarchical.enabled !== true) {
this.randomSeed = this.initialRandomSeed;
this._resetRNG(this.initialRandomSeed);
let radius = nodesArray.length + 50;
for (let i = 0; i < nodesArray.length; i++) {
let node = nodesArray[i];
let angle = 2 * Math.PI * this.seededRandom();
let angle = 2 * Math.PI * this._rng();
if (node.x === undefined) {
node.x = radius * Math.cos(angle);
}
Expand Down Expand Up @@ -645,8 +652,8 @@ class LayoutEngine {
// Only perturb the nodes that aren't fixed
let node = this.body.nodes[indices[i]];
if (node.predefinedPosition === false) {
node.x += (0.5 - this.seededRandom())*offset;
node.y += (0.5 - this.seededRandom())*offset;
node.x += (0.5 - this._rng())*offset;
node.y += (0.5 - this._rng())*offset;
}
}

Expand Down
18 changes: 5 additions & 13 deletions lib/network/modules/components/physics/BarnesHutSolver.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Alea } from "vis-util";

/**
* Barnes Hut Solver
*/
Expand All @@ -12,7 +14,7 @@ class BarnesHutSolver {
this.physicsBody = physicsBody;
this.barnesHutTree;
this.setOptions(options);
this.randomSeed = 5;
this._rng = Alea("BARNES HUT SOLVER");

// debug: show grid
// this.body.emitter.on("afterDrawing", (ctx) => {this._debug(ctx,'#ff0000')})
Expand All @@ -30,16 +32,6 @@ class BarnesHutSolver {
this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap));
}

/**
*
* @returns {number} random integer
*/
seededRandom() {
var x = Math.sin(this.randomSeed++) * 10000;
return x - Math.floor(x);
}


/**
* This function calculates the forces the nodes apply on each other based on a gravitational model.
* The Barnes Hut method is used to speed up this N-body simulation.
Expand Down Expand Up @@ -323,8 +315,8 @@ class BarnesHutSolver {
// if there are two nodes exactly overlapping (on init, on opening of cluster etc.)
// we move one node a little bit and we do not put it in the tree.
if (children.children.data.x === node.x && children.children.data.y === node.y) {
node.x += this.seededRandom();
node.y += this.seededRandom();
node.x += this._rng();
node.y += this._rng();
}
else {
this._splitBranch(children);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import BarnesHutSolver from "./BarnesHutSolver"
import { Alea } from "vis-util";

/**
* @extends BarnesHutSolver
Expand All @@ -11,6 +12,8 @@ class ForceAtlas2BasedRepulsionSolver extends BarnesHutSolver {
*/
constructor(body, physicsBody, options) {
super(body, physicsBody, options);

this._rng = Alea("FORCE ATLAS 2 BASED REPULSION SOLVER");
}

/**
Expand All @@ -25,7 +28,7 @@ class ForceAtlas2BasedRepulsionSolver extends BarnesHutSolver {
*/
_calculateForces(distance, dx, dy, node, parentBranch) {
if (distance === 0) {
distance = 0.1 * Math.random();
distance = 0.1 * this._rng();
dx = distance;
}

Expand Down
Loading

0 comments on commit d10717b

Please sign in to comment.