Published
Edited
May 11, 2020
6 forks
58 stars
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
Insert cell
Insert cell
Insert cell
sim
Insert cell
sim_results = sim.run_simulation(simulation_parameters, initial_infections)
Insert cell
sim = setup_infection_sim_full(data_gen_parameters)
Insert cell
function setup_infection_sim_full(data_params){
// Generate network data
const {nodes, edges, node_quadtree} = generate_network_data(
data_params.num_nodes.val,
build_poisson_mixture([
{mean: data_params.avg_num_edges.val, size: 1 - data_params.prop_popular_nodes.val},
{mean: data_params.avg_num_edges_pop.val, size: data_params.prop_popular_nodes.val}
])
);

function* run_simulation(sim_params, initial_infections = [], sim_speed = 1){

let time = 1;
const hist = [{
total_infections: initial_infections.length,
new_infections: initial_infections,
n_new: initial_infections.length,
potential_infectors: initial_infections.length,
time: time++
}];

// Reset all nodes before starting
nodes.forEach(node => {
node.infected = false;
node.inactive = false;
node.time_infected = -1;
node.uninfected_neighbors = [...node.neighbors];
});

// Setup the initial infecting nodes
initial_infections.forEach(id => {
const node = nodes[id];
node.infected = true;
node.time_infected = time;

// Create an array of potential neighbors to infect, make sure to not include already infected nodes
node.uninfected_neighbors = node.neighbors.filter(n => !(initial_infections.includes(n.id)));
});

// State vectors that keep track of what nodes are in what states
const potential_infectors = new Set(initial_infections);
const infections = new Set(initial_infections);

while(potential_infectors.size > 0 && infections.size < nodes.length) {
const no_longer_infecting = [];
const new_infections = new Set();

const expose_node = node => {
const was_infected = Math.random() < sim_params.prob_of_infect.val;
if(was_infected) new_infections.add(node.id);
};

potential_infectors.forEach(id => {
const node = nodes[id];

// Exposes all the node's neighbors
node.uninfected_neighbors.forEach(expose_node);

// Does this node randomly expose a "long edge"?
const has_long_edge = Math.random() < sim_params.prob_of_long_edge.val;
if (has_long_edge){
const random_node = nodes[Math.floor(Math.random() * nodes.length)];
const is_infectable = !(random_node.id === node.id || random_node.infected);
if(is_infectable) expose_node(random_node);
}

// Will node no longer be infections at the next time point?
if(time >= node.time_infected + sim_params.contagious_period.val) no_longer_infecting.push(id);
});

const set_inactive = id => {
nodes[id].inactive = true;
potential_infectors.delete(id);
}

// Remove nodes neighbors infected or have been infected for time period from potential infectors
no_longer_infecting.forEach(set_inactive);

// Run through newly infected nodes and set to infected
new_infections.forEach( id => {
infections.add(id);

const node = nodes[id];
node.infected = true;
node.time_infected = time;

// Create an array of potential neighbors to infect, make sure to not include already infected nodes
node.uninfected_neighbors = node.neighbors.filter(n => !(n.infected || new_infections.has(n.id)));

// Node can only infect others if it has neighbors that are not already infected
if(node.uninfected_neighbors.length !== 0) potential_infectors.add(id);

// Go to all of this node's neighbors and remove itself from their list of nodes they could infect
node.neighbors.forEach(neighbor => {
neighbor.uninfected_neighbors = neighbor.uninfected_neighbors.filter(n => n.id !== id);

if(neighbor.uninfected_neighbors.length == 0) set_inactive(neighbor.id);
});
});

// Update state history
hist.push({total_infections: infections.size,
new_infections: [...new_infections],
n_new: new_infections.size,
potential_infectors: potential_infectors.size,
time: time});

yield Promises.tick(sim_speed, {ids: [...infections],
history: hist,
timestep: time++,
running: true,
settings: sim_params,
status: "running"});
}
yield Promises.tick(sim_speed, {ids: [...infections],
history: hist,
timestep: hist.length,
settings: sim_params,
status: hist.length == 1 ? "waiting": "finished"});
}

return {nodes,
edges,
node_quadtree,
run_simulation,
settings: data_params};
}
Insert cell
Insert cell
colors = {
const early_infection = "#990000";
const late_infection = "#fc8d59";
const infected = "#e41a1c";
const healthy = "#377eb8";
const safe_edge = "#d9d9d9";
const interactive = "#2690ce";
const inactive = change_brightness(infected, -1);
const newly_infected = change_brightness(infected, 1);
const danger_edge = d3.interpolateHslLong(safe_edge, infected)(0.5);
const inactive_edge = d3.interpolateHslLong(safe_edge, inactive)(0.1);
return {infected,
inactive,
newly_infected,
healthy,
safe_edge,
inactive_edge,
danger_edge,
interactive,
early_infection,
late_infection};
}
Insert cell
Insert cell
mutable data_gen_parameters = ({
num_nodes: {
val: 3000,
min: 1000,
max: 15001,
step: 100,
int: true,
},
avg_num_edges: {
val: 8,
min: 1,
max: 25,
step: 0.1,
},
avg_num_edges_pop: {
val: 25,
min: 10,
max: 100,
step: 0.1,
},
prop_popular_nodes: {
val: 0.2,
min: 0,
max: 1,
step: 0.01,
percent: true,
}
});
Insert cell
mutable simulation_parameters = ({
prob_of_infect: {
val: 0.15,
min: 0.01,
max: 0.99,
start: 0.15,
step: 0.01,
},
prob_of_long_edge:
{
val: 0,
min: 0,
max: 0.2,
start: 0,
step: 0.005,
},
contagious_period: {
val: 10,
min: 1,
max: 30,
start: 10,
step: 1,
int: true,
}
});
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
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more