Published
Edited
May 7, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
evolution = {
const data = [{
t: 0,
states: initialStates,
numOn: d3.sum(initialStates),
decimal: binToDecimal(initialStates),
}]

for (let t = 1; t < r; ++t) {
const states = getNextStates(data[t - 1].states)
data.push({
t,
states,
numOn: d3.sum(states),
decimal: binToDecimal(states),
})
}

return data
}
Insert cell
initialStates = {
buttons.randomizeInitialState;
return d3.range(numNodes).map(() => randBit())
}
Insert cell
cycleNetwork = {
const nodeMap = new Map()
const linkMap = new DefaultMap()
for (let t = 0; t < r; ++t) {
const curr = evolution[t].decimal
nodeMap.set(curr, (nodeMap.get(curr) ?? 0) + 1)
if (t > 0) {
const prev = evolution[t-1].decimal
const link = [prev, curr]
linkMap.set(link, linkMap.get(link) + 1)
}
}
const nodes = Array.from(nodeMap).map(([id, count]) => ({ id, count }))
const links = []
linkMap.forEach((count, link) => {
links.push({ source: link[0], target: link[1], count })
})
return { nodes, links }
}
Insert cell
function getNextStates(states) {
const next = []
for (let i = 0; i < numNodes; ++i) {
next.push(getNextState(i, states))
}
return next
}
Insert cell
function getNextState(nodeIndex, states=initialStates, operator=truthTable) {
const input = getInput(nodeIndex, states)
return truthOperators[operator](input, nodeIndex)
}
Insert cell
truthOperators = {
return {
"AND": (input) => d3.every(input, d => d) ? 1 : 0,
"OR": (input) => d3.some(input, d => d) ? 1 : 0,
"XOR": (input) => (!d3.every(input, d => d === 0) && !d3.every(input, d => d)) ? 1 : 0,
"0": () => 0,
"1": () => 1,
"Random": (input, nodeIndex) => randomTruthTables[nodeIndex][binToDecimal(input)],
}
}
Insert cell
function getInput(nodeIndex, states) {
return network.nodes[nodeIndex].inLinks.map(link => states[link.source])
}
Insert cell
getInput(1, initialStates)
Insert cell
getNextState(1, initialStates, "OR")
Insert cell
function binToDecimal(bits) {
return d3.sum(bits, (bit, i) => bit << bits.length - 1 - i)
}
Insert cell
binToDecimal([0,1,1])
Insert cell
generateRandomTruthTable(3)
Insert cell
function generateRandomTruthTable(numInputs) {
const numCombinations = 2 ** numInputs
return d3.range(numCombinations).map(() => randBit())
}
Insert cell
network
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
randomTruthTables = {
buttons.randomizeRandomTruthTable;
return network.nodes.map(n => generateRandomTruthTable(n.inLinks.length))
}
Insert cell
network = {
const network = isLattice ? graphLattice : graphNK

const links = network.links//.map(d => Object.create(d));
const nodes = network.nodes//.map(d => Object.create(d));
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.index).strength(1).distance(30).iterations(10))
.force("charge", d3.forceManyBody().strength(-60))
.force("center", d3.forceCenter(width / 2, height / 2));

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height);

const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", d => Math.sqrt(d.value));

const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("class", "node")
.attr("r", 5)
.attr("fill", "black")
.call(drag(simulation));

node.append("title")
.text(d => d.index);

simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);

node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
});

invalidation.then(() => simulation.stop());

return { nodes, links, svg, simulation }
}
Insert cell
graphLattice = {
const n = Math.ceil(Math.sqrt(N));
const nodes = Array.from({length: n * n}, (_, i) => ({index: i, outLinks: [], inLinks: []}));
const links = [];
for (let y = 0; y < n; ++y) {
for (let x = 0; x < n; ++x) {
if (y > 0) {
const link = {source: (y - 1) * n + x, target: y * n + x}
const oppositeLink = {source: link.target, target: link.source}
links.push(link);
nodes[link.source].outLinks.push({...link})
nodes[link.source].inLinks.push({...oppositeLink})
nodes[link.target].inLinks.push({...link})
nodes[link.target].outLinks.push({...oppositeLink})
}
if (x > 0) {
const link = {source: y * n + (x - 1), target: y * n + x}
const oppositeLink = {source: link.target, target: link.source}
links.push(link);
nodes[link.source].outLinks.push({...link})
nodes[link.source].inLinks.push({...oppositeLink})
nodes[link.target].inLinks.push({...link})
nodes[link.target].outLinks.push({...oppositeLink})
}
}
}
return {nodes, links, links2: links.map(l => ({...l}))};
}
Insert cell
function generateNetworkFromDegrees(degrees) {
// n is number of vertices
const n = degrees.length
const degreesLeft = degrees.map(d => d)

const A = mlm.Matrix.zeros(n, n)

for (let i = 0; i < n; ++i) {
for (let _j = i - 1; _j < n; ++_j) {
if (_j == i) continue
const j = _j < 0 ? n + _j : _j
if (degreesLeft[i] > 0 && degreesLeft[j] > 0) {
degreesLeft[i] -= 1
degreesLeft[j] -= 1
A.set(i, j, 1)
A.set(j, i, 1)
}
}
}

return A
}
Insert cell
A.data.map(d => d.join(',')).join('\n')
Insert cell
A = generateNetworkFromDegrees(degreeDistribution)
Insert cell
graphNK = {
const nodes = Array.from({length: N}, (_, i) => ({index: i, outLinks: [], inLinks: []}));
const links = [];
for (let i = 0; i < N; ++i) {
for (let j = 0; j < N; ++j) {
if (A.get(i, j) > 0) {
const link = {source: i, target: j}
links.push(link);
nodes[link.source].outLinks.push({...link})
nodes[link.target].inLinks.push({...link})
}
}
}
return {nodes, links};
}
Insert cell
function getNeighbours(node) {
return [...node.outLinks.map(link => link.target), ...node.inLinks.map(link => link.source)]
.map(index => network.nodes[index])
}
Insert cell
getNeighbours(network.nodes[1])
Insert cell
network.nodes[0].outLinks
Insert cell
Insert cell
pickRandom([
['a', 1],
['b', 2],
['c', 3],
['d', 4]
], item => item[1])
Insert cell
function pickRandom(items, getWeight = () => 1) {
// Will contain the sum of all weights
let sum = 0;
// Will contain an array of each weight accumilating the sum of previous weights
const accumulatedWeights = [];

items.forEach((item, i) => {
const weight = getWeight(item, i)
sum += weight
accumulatedWeights.push(sum);
})

const rand = random() * sum;
const item = items[accumulatedWeights.filter(element => element <= rand).length];

return item;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more