Nature, in Code

Frequency-Dependent Selection: Coevolution

Frequency-dependent selection occurs when the fitness of a genotype depends on its frequency in the population. Host-parasite or predator-prey interactions are classical examples of negative frequency-dependent selection, leading to cyclical, coevolutionary dynamics. These dynamics are often called "Red Queen" dynamics.

Below is a simulation of a simple host-parasite interaction, following a haploid matching-allele model. Its dynamic is strongly dependent on initial genotype frequencies (the four lines show the two host and two parasite genotype frequencies):

RELOAD SIMULATION

Stable coevolutionary dynamics are an artefact of purely deterministic models, however. Even when slightly perturbed, the coevolutionary dynamics will change dramatically:

RELOAD SIMULATION

Code

First plot

var data = [];
var generations = 400;

var host_frequencies = [0,0];
var parasite_frequencies = [0,0];

var sh = 0.2;
var sp = 0.5;

function initialize_frequencies() {
	host_frequencies[0] = Math.random();
	data.push([host_frequencies[0]]);
	host_frequencies[1] = 1 - host_frequencies[0];
	data.push([host_frequencies[1]]);
	parasite_frequencies[0] = Math.random();
	data.push([parasite_frequencies[0]]);
	parasite_frequencies[1] = 1 - parasite_frequencies[0];
	data.push([parasite_frequencies[1]]);
}

initialize_frequencies();

for (var i = 0; i < generations; i = i + 1) {
	host_selection();
	parasite_selection();
	data[0].push(host_frequencies[0]);
	data[1].push(host_frequencies[1]);
	data[2].push(parasite_frequencies[0]);
	data[3].push(parasite_frequencies[1]);
}
draw_line_chart(data,"Generation","p",[]);

function host_selection() {
	var sum_host_frequencies = 0;
	for (var i = 0; i < host_frequencies.length; i = i + 1) {
		var current_host_fitness = 0;
		for (var ii = 0; ii < parasite_frequencies.length; ii = ii + 1) {
			current_host_fitness = current_host_fitness + get_host_fitness(i, ii);
		}
		host_frequencies[i] = host_frequencies[i] * current_host_fitness;
		sum_host_frequencies = sum_host_frequencies + host_frequencies[i];
	}
	for (i = 0; i < host_frequencies.length; i = i + 1) {
		host_frequencies[i] = host_frequencies[i] / sum_host_frequencies;
	}
}

function get_host_fitness(i,ii){
	return (i == ii ? 1-sh : 1) * parasite_frequencies[ii]
}

function parasite_selection() {
	var sum_parasite_frequencies = 0;
	for (var i = 0; i < parasite_frequencies.length; i = i + 1) {
		var current_parasite_fitness = 0;
		for (var ii = 0; ii < host_frequencies.length; ii = ii + 1) {
			current_parasite_fitness = current_parasite_fitness + get_parasite_fitness(i, ii);
		}
		parasite_frequencies[i] = parasite_frequencies[i] * current_parasite_fitness;
		sum_parasite_frequencies = sum_parasite_frequencies + parasite_frequencies[i];
	}
	for (i = 0; i < parasite_frequencies.length; i = i + 1) {
		parasite_frequencies[i] = parasite_frequencies[i] / sum_parasite_frequencies;
	}
}

function get_parasite_fitness(i,ii) {
	return (i == ii ? 1 : 1-sp) * host_frequencies[ii]
}
			
Second plot

var data = [];
var generations = 2000;

var host_frequencies = [0,0];
var parasite_frequencies = [0,0];

var sh = 0.2;
var sp = 0.5;

function initialize_frequencies() {
	host_frequencies[0] = Math.random();
	data.push([host_frequencies[0]]);
	host_frequencies[1] = 1 - host_frequencies[0];
	data.push([host_frequencies[1]]);
	parasite_frequencies[0] = Math.random();
	data.push([parasite_frequencies[0]]);
	parasite_frequencies[1] = 1 - parasite_frequencies[0];
	data.push([parasite_frequencies[1]]);
}

initialize_frequencies();

for (var i = 0; i < generations; i = i + 1) {
	host_selection();
	parasite_selection();
	data[0].push(host_frequencies[0]);
	data[1].push(host_frequencies[1]);
	data[2].push(parasite_frequencies[0]);
	data[3].push(parasite_frequencies[1]);
}
draw_line_chart(data,"Generation","p",[]);

function host_selection() {
	var sum_host_frequencies = 0;
	for (var i = 0; i < host_frequencies.length; i = i + 1) {
		var current_host_fitness = 0;
		for (var ii = 0; ii < parasite_frequencies.length; ii = ii + 1) {
			current_host_fitness = current_host_fitness + get_host_fitness(i, ii);
		}
		host_frequencies[i] = host_frequencies[i] * current_host_fitness;
		host_frequencies[i] = host_frequencies[i] + Math.random() * 0.01;
		sum_host_frequencies = sum_host_frequencies + host_frequencies[i];
	}
	for (i = 0; i < host_frequencies.length; i = i + 1) {
		host_frequencies[i] = host_frequencies[i] / sum_host_frequencies;
	}
}

function get_host_fitness(i,ii){
	return (i == ii ? 1-sh : 1) * parasite_frequencies[ii]
}

function parasite_selection() {
	var sum_parasite_frequencies = 0;
	for (var i = 0; i < parasite_frequencies.length; i = i + 1) {
		var current_parasite_fitness = 0;
		for (var ii = 0; ii < host_frequencies.length; ii = ii + 1) {
			current_parasite_fitness = current_parasite_fitness + get_parasite_fitness(i, ii);
		}
		parasite_frequencies[i] = parasite_frequencies[i] * current_parasite_fitness;
		sum_parasite_frequencies = sum_parasite_frequencies + parasite_frequencies[i];
	}
	for (i = 0; i < parasite_frequencies.length; i = i + 1) {
		parasite_frequencies[i] = parasite_frequencies[i] / sum_parasite_frequencies;
	}
}

function get_parasite_fitness(i,ii) {
	return (i == ii ? 1 : 1-sp) * host_frequencies[ii]
}
			
Note: the draw_line_chart function is built with D3.js and can be found here.