SimulationVue/simulation.js
2021-08-10 17:32:42 +02:00

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();