Skip to content

Instantly share code, notes, and snippets.

@jling90
Last active August 29, 2015 14:15
Show Gist options
  • Save jling90/5046eb1767908c162d39 to your computer and use it in GitHub Desktop.
Save jling90/5046eb1767908c162d39 to your computer and use it in GitHub Desktop.
This was not intended
<body>
<div id="svgarea"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
text {
font: bold 48px monospace;
}
</style>
<script>
"use strict";
/* Default settings for Muller's Ratchet */
// Rate of Muller's ratchet is specified by
// - population size (pop_size)
// - selection coefficient (s)
// - mutation rate (u)
// Additionally we specify a max number of mutations kmax for
// graphic purposes.
var pop_size = 25;
var s = 0.01;
var u = 0.2;
var kmax = 8;
/* Display settings */
var pixel_size = 20;
var svg_height = 400;
var svg_width = 720;
var colour_scale = d3.scale.linear()
.domain([0, kmax])
.range(['white', 'red']);
var svg = d3.select("#svgarea").append("svg")
.attr("id", "svg_display")
.attr("width", svg_width)
.attr("height", svg_height)
.append("g")
.attr("transform", "translate(2," + pixel_size + ")");
/* 'Main' function */
window.onload=function(){
var pop = initialisePopulation();
update_view(pop);
// The 'Event loop'.
// 1) Apply new mutations and display them.
// 2) Select parents for new generation and highlight them.
// 3) Generate the new generation and display them. Repeat at (1).
setInterval(function(){
pop = addMutations(pop);
setTimeout(function(){
update_view(pop);
}, 1000);
var res = pickParents(pop);
//pop = res[0];
var new_pop = res[1];
/*
setTimeout(function(){
show_parents(pop);
}, 1000);
*/
update_view(pop);
}, 2000000);
}
/* Calculations */
function calc_row_n(n){
// Calculate number of pixels to display per row.
// Return a factor of n closest to sqrt(n). This will give the most
// square-ish grid of pixels. If this value exceeds the max
// number of pixels that will fit in a row, use the max instead.
var pixel_max = Math.floor(svg_width / (pixel_size * 1.2));
var x = Math.ceil(Math.sqrt(n));
while (n % x){
x++; // If svg is taller than long, do x-- instead.
}
var res = (x > 1 && x < pixel_max) ? x : pixel_max;
return res;
}
function initialisePopulation(){
var pop = [];
var pix_per_row = calc_row_n(pop_size);
for (var idx = 0; idx < pop_size; idx++){
pop.push({i:Math.floor(idx/pix_per_row), j:idx%pix_per_row, mu:0});
}
return pop;
}
// Populate new generation of equal size.
function pickParents(data){
var parents = data.slice()
var new_pop = [];
var parent_index = new Set();
while (new_pop.length < data.length){
var n = Math.floor(Math.random() * data.length);
console.log("n and data[n] are: ");
console.log(n);
console.log(data[n]);
var fitness = Math.pow((1 - s), (data[n]).mu);
if (fitness >= Math.random()){
new_pop.push(data[n]);
parent_index.add(n);
}
}
// We're done populating, now remove parents from the original
// array so we can do fun display things.
parent_index.forEach(function(element, index) {
parents.splice(element, 1);
});
console.log("new pop is: ");
console.log(new_pop);
return [parents, new_pop];
}
// Add mutations to an array, sampling from Poisson distribution
// with delta == mutation rate, 'u'.
function addMutations(data){
for (var i = 0; i < data.length; i++){
var L = Math.exp(-u);
var k = 0;
var p = 1.0;
do {
k++;
p *= Math.random();
} while (p > L);
if (data[i].mu < kmax){
data[i].mu += (k - 1);
}
}
return data;
}
// 'Kills' individuals with kmax mutations. Testing function only.
function purgeMutants(data){
for (var i = data.length; i--;){
if (data[i].mu == kmax){
data.splice(i,1);
}
}
return data;
}
/* Visualisation */
function show_parents(data){
var population = svg.selectAll("rect")
.data(data);
// ENTER
population.enter().append("rect")
.attr('class', 'pixel')
.attr('y', function(d) { return d.i * pixel_size * 1.2;})
.attr('x', function(d) { return d.j * pixel_size * 1.2;})
.style('stroke', 'rgb(0,0,0)')
.style('stroke-width', 1)
.attr('width', pixel_size)
.attr('height', pixel_size)
.style('fill', function(d) { return colour_scale(d.mu); })
.transition()
.duration(1500)
.style('fill-opacity', 1e-6)
.style('stroke-width', 0);
// EXIT
population.exit()
.attr("class", "exit")
.style('stroke-width', 15)
.transition()
.duration(1000)
// .attr('width', 0)
// .attr('height', 0)
.style('stroke-width', 1)
}
function update_view(data) {
var population = svg.selectAll("rect")
.data(data);
console.log(data);
// UPDATE
population.attr("class", "update")
.transition()
.duration(750)
.attr('y', function(d) { return d.i * pixel_size * 1.2;})
.attr('x', function(d) { return d.j * pixel_size * 1.2;})
.style('fill', function(d) { return colour_scale(d.mu); }) ;
// ENTER
population.enter().append("rect")
.attr('class', 'pixel')
.attr('y', function(d) { return d.i * pixel_size * 1.2;})
.attr('x', function(d) { return d.j * pixel_size * 1.2;})
.style('fill-opacity', 1e-6)
.style('stroke', 'rgb(0,0,0)')
.style('fill', function(d) { return colour_scale(d.mu); })
.transition()
.duration(750)
.style('stroke-width', 1)
.style('fill-opacity', 1);
// EXIT
population.exit()
.attr("class", "exit")
.transition()
.duration(1000)
// .attr('width', 0)
// .attr('height', 0)
// .style('stroke-width', 0)
// .style("fill-opacity", 1e-6)
.remove();
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment