Genetic draft, selective interference, and population genetics of rapid adaptation



Menu:

Site frequency spectra (SFS)

Here, we will use FFPopSim to explore the site frequency spectra in a scenario dominated by adaptation mutations. The genealogies of in this case where discussed on page coalescence.html. We will look at frequencies of beneficial, deleterious mutations, as well as neutral mutations. Note that we do not need to simulate a large number of neutral loci to investigate their frequency spectra. We can simply look at the genealogy at one neutral locus, and calculate the SFS that would result from sprinkling many neutral mutations onto the tree: Mutations that happen on a particular branch of the tree are going to be present in all downstream nodes. The expected number of such mutations is proportional to the branch length. By adding contributions from all branches, we can calculated the expected neutral SFS corresponding to a particular tree. This is implemented in SFS.py (download here).

The selection coefficients are set-up such that every odd locus is beneficial and every even locus is deleterious. The locus L/2 has a very small, almost neutral, coefficient (which doesn't matter at all in the asexual case)
#set the effect sizes of the mutations that are injected (the same at each site in this case)
selection_coefficients = np.ones(L)*s*0.5
selection_coefficients[::2] = -selection_coefficients[::2]
selection_coefficients[L/2] = 1e-10
pop.set_fitness_additive(selection_coefficients)
After the usual burn-in, the SFS spectra of neutral, deleterious and beneficial mutations are accumulated.
#allocate arrays to accumulate the SFS
neutralSFS = np.zeros_like(bincenters)
deleteriousSFS = np.zeros_like(bincenters)
beneficialSFS = np.zeros_like(bincenters)
for si in xrange(nsamples):
	pop.evolve(dt)
	print "sample", si, "out of", nsamples
	#get the tree and pass it to the function that calculate the neutral allele frequency spectrum
	BPtree = pop.genealogy.get_tree(L/2).to_Biopython_tree()
	sample_size,tmpSFS = get_SFS(BPtree)
	y,x = np.histogram(tmpSFS[:,0], weights = tmpSFS[:,1], bins=bins)
	neutralSFS+=y
	#get the frequencies of selected alleles. Partition into deleterious (even) and beneficial (odd) ones
	derived_af = pop.get_derived_allele_frequencies()
	y,x = np.histogram(derived_af[::2], bins=bins)
	deleteriousSFS+=y
	y,x = np.histogram(derived_af[1::2], bins=bins)
	beneficialSFS+=y
The result is then plotted on a scale such that the behavior near 0 and 1 are both apparent (logit scale).

Asexual

In the asexual case, one finds that neutral, beneficial, and deleterious mutations have more or less the same non-monotonic shape. Beneficial mutation are enriched relative to neutral at high frequencies, while deleterious mutations are rarer than neutral ones -- as expected.

Note that the non-monotonicity is a generic feature of derived site frequency spectra in rapidly adapting population. It is a consequence of the skewed branching at the deep nodes in the tree: All mutations that fall onto the branch from which the majority descends are going to be very close to fixation. Such uneven branching is rare in the Kingman coalescent and the SFS are monotonically decreasing.

Facultatively sexual

Increasing the outcrossing rate decouples the fate of beneficial and deleterious mutations and allows the selection to weed out the deleterious ones. For an outcrossing rate of 0.02 (one crossover per genome), one finds

An outcrossing rate to r=0.05 enables the population to largely prevent fixation of deleterious mutations.