204 lines
5.3 KiB
JavaScript
204 lines
5.3 KiB
JavaScript
states = ["S", "I", "R"];
|
|
|
|
rules = [[ "I", "R", 1 ], // spontaneous rule I -> R with rate 1.0
|
|
[ "R", "S", 0.7 ], // spontaneous rule R -> S with rate 0.7
|
|
[ [ "I","S","I" ],[ "I","I","I" ], 0.8 ]]; // contact rule I+S -> I+I with rate 0.8
|
|
|
|
|
|
simulation = []
|
|
|
|
|
|
graph_as_edgelist = [[ 0, 4 ], [ 0, 1 ], [ 1, 5 ], [ 1, 2 ], [ 2, 6 ], [ 2, 3 ], [ 3, 7 ], [ 4, 8 ], [ 4, 5 ], [ 5, 9 ], [ 5, 6 ], [ 6, 10 ], [ 6, 7 ], [ 7, 11 ], [ 8, 12 ], [ 8, 9 ], [ 9, 13 ], [ 9, 10 ], [ 10, 14 ], [ 10, 11 ], [ 11, 15 ], [ 12, 13 ], [ 13, 14 ], [ 14, 15 ]];
|
|
|
|
horizon = 20.0; // wie lange wird simuliert
|
|
initial_distribution = [0.5, 0.5, 0.0]; // gleiche Reihenfolge wie states, musss zu rules passen und normalisiert werden
|
|
timepoint_num = 101;
|
|
|
|
function get_next_state(current_labels){
|
|
fastes_firing_time = 10000000.0; //dummy
|
|
firing_rule = null;
|
|
firing_node = null;
|
|
firing_edge = null;
|
|
|
|
//if(current_labels == null)
|
|
//iterate over nodes
|
|
for(node in nodes){
|
|
node = nodes[node];
|
|
current_state = current_labels[node];
|
|
for(rule in rules){
|
|
rule = rules[rule];
|
|
if(rule[0] instanceof Array){
|
|
//is contact rule
|
|
continue;
|
|
}
|
|
if(current_state == rule[0]){
|
|
current_fireing_time = randomExponential(rule[2]);
|
|
//addFiringTime(current_fireing_time);
|
|
if(current_fireing_time < fastes_firing_time){
|
|
fastes_firing_time = current_fireing_time;
|
|
firing_rule = rule;
|
|
firing_node = node;
|
|
firing_edge = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//iterate over edges
|
|
for(edge in graph_as_edgelist){
|
|
edge = graph_as_edgelist[edge];
|
|
node1 = node2 = edge;
|
|
current_state1 = current_labels[node1];
|
|
current_state2 = current_labels[node2];
|
|
for(rule in rules){
|
|
rule = rules[rule];
|
|
if(typeof rule[0] == typeof ""){
|
|
//is spont. rule
|
|
continue
|
|
}
|
|
if(current_state1 == rule[0][0] && current_state2 == rule[0][1] || current_state2 == rule[0][0] && current_state1 == rule[0][1]){
|
|
current_fireing_time = randomExponential(rule[2]);
|
|
if(current_fireing_time < fastes_firing_time){
|
|
fastes_firing_time = current_fireing_time;
|
|
firing_rule = rule;
|
|
firing_node = null;
|
|
firing_edge = edge;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(firing_rule == null){
|
|
//no rule could fire
|
|
return [null, fastes_firing_time]; //would happen anyway but still
|
|
}
|
|
//apply rule
|
|
new_labels = Array.from(current_labels);
|
|
|
|
if(firing_node != null){
|
|
new_labels[firing_node] = firing_rule[1];
|
|
return [new_labels, fastes_firing_time];
|
|
}
|
|
console.assert(firing_edge != null);
|
|
change_node1 = firing_edge[0];
|
|
change_node2 = firing_edge[1];
|
|
//we have to check which node changes in which direction
|
|
if(new_labels[change_node1] == firing_rule[0][0] && new_labels[change_node2] == firing_rule[0][1]){
|
|
new_labels[change_node1] = firing_rule[1][0];
|
|
new_labels[change_node2] = firing_rule[1][1];
|
|
}else{
|
|
new_labels[change_node1] = firing_rule[1][1];
|
|
new_labels[change_node2] = firing_rule[1][0];
|
|
}
|
|
|
|
return [new_labels, fastes_firing_time];
|
|
}
|
|
|
|
|
|
function count_states(current_labels){
|
|
counter = [states.length];
|
|
simulation.push(current_labels);
|
|
for(label in current_labels){
|
|
label = current_labels[label];
|
|
index = states[ label ];
|
|
counter[index] += 1;
|
|
}
|
|
}
|
|
|
|
function generateNodes(edgelist){
|
|
allNodes = [];
|
|
for(e in edgelist){
|
|
e = edgelist[e];
|
|
allNodes.push(e[0]);
|
|
allNodes.push(e[1]);
|
|
}
|
|
//sort
|
|
allNodes = allNodes.sort((a, b) => (a > b));
|
|
//remove duplicates
|
|
finalArray = [];
|
|
var last = -1;
|
|
for(var i = 0; i < allNodes.length; i ++){
|
|
if(last == allNodes[i]){
|
|
continue;
|
|
}
|
|
else{
|
|
finalArray.push(allNodes[i])
|
|
last = allNodes[i];
|
|
}
|
|
}
|
|
return finalArray;
|
|
}
|
|
|
|
nodes = generateNodes(graph_as_edgelist);
|
|
|
|
//setup
|
|
timepoints_samples = linspace(0, horizon, timepoint_num);
|
|
timepoints_samples_static = linspace(0, horizon, timepoint_num);
|
|
current_labels = randomChoice(states, nodes.length, initial_distribution);
|
|
global_clock = 0;
|
|
labels = [];
|
|
timepoints = [];
|
|
state_counts = [];
|
|
|
|
simulate(rules, states, initial_distribution);
|
|
function simulate(newRules, newStates, newDistr){
|
|
rules = newRules;
|
|
states = newStates;
|
|
initial_distribution = newDistr;
|
|
while(timepoints_samples.length > 0){
|
|
[new_labels, time_passed] = get_next_state(current_labels);
|
|
global_clock += time_passed;
|
|
while(timepoints_samples.length > 0 && global_clock > timepoints_samples[0]){
|
|
labels.push(Array.from(current_labels));
|
|
state_counts.push(count_states(current_labels));
|
|
timepoints_samples = timepoints_samples.slice(1, timepoints_samples.length);
|
|
}
|
|
current_labels = new_labels;
|
|
}
|
|
console.log(simulation);
|
|
}
|
|
|
|
// np helper functions
|
|
|
|
function randomExponential(rate){
|
|
if(rate == 0){ return 10000000 }
|
|
return -Math.log(Math.random()) / rate;
|
|
}
|
|
|
|
function linspace(start, end, num){
|
|
console.assert(start < end);
|
|
console.assert(num > 0);
|
|
distance = (end - start) / (num - 1);
|
|
current = start;
|
|
out = [];
|
|
while(current <= end){
|
|
out.push(Math.round(current * 100) / 100);
|
|
current += distance;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function randomChoice(states, num, distr){
|
|
out = [];
|
|
let s = distr.reduce((a,e) => a + e);
|
|
for(var i = 0; i < num; i++){
|
|
let r = Math.random() * s;
|
|
out.push(states.find((_,i) => (r -= distr[i]) < 0));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
//times = [];
|
|
//function addFiringTime(time){
|
|
//times.push(time);
|
|
//}
|
|
//
|
|
//function getAv(){
|
|
//var sum = 0;
|
|
//for(t in times){
|
|
//t = times[t];
|
|
//sum += t;
|
|
//}
|
|
//return sum / times.length;
|
|
//}
|
|
//simulate();
|