Sex-specific selection

120 views
Skip to first unread message

Max Reuter

unread,
Feb 18, 2019, 9:56:25 AM2/18/19
to slim-discuss
Hi,

I am looking for a software that I could use to simulate loci under sex-specific and sexually antagonistic selection, where the selective advantage of alleles differs (or is opposed) between the two sexes. I would be grateful for some advice on whether SLiM would suit my purposes.

There are two specific elements to what I need. The first is that selection coefficients can differ between sexes. Having looked through the SLiM manual, I think this can be implemented with a fitness() callback that is specific to one sex. The other element is that fitness should be evaluated separately in each sex (comparing an individual's relative fitness to the average of that sex, rather than the population-wide average). This is potentially trickier and I've not been able to find anything obvious in the manual. But maybe I missed something?

Any input would be greatly appreciated!

Many thanks,

Max


Ben Haller

unread,
Feb 18, 2019, 6:44:38 PM2/18/19
to slim-discuss

Hi,

  Hi Max,
 
I am looking for a software that I could use to simulate loci under sex-specific and sexually antagonistic selection, where the selective advantage of alleles differs (or is opposed) between the two sexes. I would be grateful for some advice on whether SLiM would suit my purposes.

  Yes, SLiM should be able to handle this.
 
There are two specific elements to what I need. The first is that selection coefficients can differ between sexes. Having looked through the SLiM manual, I think this can be implemented with a fitness() callback that is specific to one sex.

  Yes.
 
The other element is that fitness should be evaluated separately in each sex (comparing an individual's relative fitness to the average of that sex, rather than the population-wide average). This is potentially trickier and I've not been able to find anything obvious in the manual. But maybe I missed something?

  Agreed that this is trickier.  :->  You will certainly want this to be a nonWF model, since the default WF model type won't give you enough flexibility.  Then to achieve what you want I think I would advise that you write an early() event that calls recalculateFitness() to force early fitness evaluation (so that you have fitness values to work with before the mortality phase), then call cachedFitness() to get the fitness values for individuals.  You can partition them into the fitness values for the males and for the females, and then you can use the fitnessScaling property of the individuals to rescale fitness values separately for males and females so that fitness is relative within each group.  Then when your early() event ends, SLiM will calculate fitness values again (because it is that point in the generation cycle) and the fitnessScaling values you set will be incorporated into the calculations, shifting the model from absolute fitness (which is what you normally get in a nonWF model) to relative fitness, within sex.

  That's pretty technical, so here's a quick mock-up of what I have in mind:

initialize() {

initializeSLiMModelType("nonWF");

defineConstant("K", 500); // carrying capacity

initializeMutationType("m1", 0.5, "f", 0.0);

m1.convertToSubstitution = T;

initializeMutationType("m2", 0.5, "f", 0.3); // sex-specific

m2.color = "red";

initializeGenomicElementType("g1", c(m1,m2), c(1.0, 0.001));

initializeGenomicElement(g1, 0, 99999);

initializeMutationRate(1e-7);

initializeRecombinationRate(1e-8);

initializeSex("A");

}

reproduction(NULL, "F") {

// females reproduce with a randomly chosen male mate

subpop.addCrossed(individual, p1.sampleIndividuals(1, sex="M"));

}

fitness(m2)

{

// m2 mutations are beneficial in males, deleterious in females

if (individual.sex == "M")

return relFitness;

else

return 1 / relFitness;

}

1 early() {

sim.addSubpop("p1", K);

}

early() {

// force an early fitness recalculation

// this is based only on mutations

sim.recalculateFitness();

// get fitness values for all individuals

inds = p1.individuals;

fit = p1.cachedFitness(NULL);

// calculate mean absolute fitness of males and females

m = (inds.sex == "M");

f = (inds.sex == "F");

mean_fit_M = mean(fit[m]);

mean_fit_F = mean(fit[f]);

// rescale to make fitness relative not absolute

inds[m].fitnessScaling = 1 / mean_fit_M;

inds[f].fitnessScaling = 1 / mean_fit_F;

// also have density-dependence so we stay near K

p1.fitnessScaling = K / p1.individualCount;

}

20000 late() { sim.outputFixedMutations(); }


  I think that ought to work; give it a try.  :->  I've only tested it very briefly; it seems to be working, but there might be an issue with it, so test carefully on your end.  It's quite an unusual model; in general the use of recalculateFitness() has not gotten much testing at all because it's such an unusual thing for a model to do.  This model exhibits an interesting behavior where the selected mutations tend to rise in frequency to ~0.5 and then stay there, I guess because they're under balancing selection due to the sex-specific fitness; that might also be connected to their dominance coefficient, which of course can be changed.  Have fun!
 
Any input would be greatly appreciated!

   Let me know if you have further questions.  :->

Cheers,
-B.

Benjamin C. Haller
Messer Lab
Cornell University

Max Reuter

unread,
Feb 19, 2019, 4:04:26 AM2/19/19
to slim-discuss
Hi Ben,

Many thanks for your reply, really very helpful! And very happy that this can work!

I'll play around a bit and see how I get on. One quick follow-up straightaway: you define fitness in female as the inverse of male fitness, and maybe one could also turn 1-s in males into 1+s in females. But is there a way of getting independent selection coefficients in the two sexes?

I imagine that to do that you would need another parameter in the function that defines the mutations, which might be tricky. Or is it best to introduce mutations at two completely linked loci and use one for males, one for females?

Cheers, Max

Ben Haller

unread,
Feb 19, 2019, 8:25:04 AM2/19/19
to slim-discuss
Hi Max.  You can attach an extra value to every new mutation in every generation pretty easily.  In a nonWF model like the one I posted, put some code at the *top* of the main early() event (so that it runs *before* you call recalculateFitness()) that (a) gets all m2 mutations, (b) narrows that down to the new ones using originGeneration, and (c) loops through the new ones and calls setValue() on them to assign whatever additional information you want them to carry.  You can see an example of this sort of thing in recipe 13.17, although you'll need to separate it out from a bunch of cruft that is unrelated to what you're trying to do.  In the end, each new m2 mutation will have an additional selection coefficient or fitness effect assigned with setValue().

If you're always introducing the m2 mutations yourself, rather than having SLiM auto-generate them for you, then it's much easier: whenever you call addNewMutation() or addNewDrawnMutation(), just then call setValue() on the new mutation you just added to attach the extra state.  Super easy.

Either way, then in your fitness() callback you can just use mut.getValue() to fetch that stored value, and return whatever fitness effect you want based upon that, rather than using relFitness.  (Probably you would use relFitness for one sex and use getValue() for the other sex).  The only complicating factor is that if you don't use relFitness you have to think about heterozygosity and dominance, since you need to account for that in your calculation (unless you want the mutations to be completely dominant, which is the easy case to code :->).  The reference section on fitness() callbacks, section 22.2 in the latest edition of the manual, discusses all the details you should need to do this correctly.  The whole thing ought to be ~5-10 lines of code, not too painful.  :->

If you are introducing the m2 mutations yourself, the other strategy you mention would also work fine: always introduce two mutations together at exactly the same position ("stacked", as one says in SLiM parlance – see manual section 1.5.3), of two different mutation types (m2 and m3, let's say), and use fitness(m2) and fitness(m3) callbacks to make one type neutral in males and the other type neutral in females.  That is quite elegant, and should work fine.  If you are always introducing the mutations yourself, rather than having SLiM auto-generate them as in the script I posted, then this is probably the best solution for its simplicity.  Nice thinking out of the box – this idea had not occurred to me.  :->

Let me know if you have further questions!

Cheers,
-B.

Benjamin C. Haller
Messer Lab
Cornell University

Max Reuter

unread,
Feb 19, 2019, 9:20:07 AM2/19/19
to slim-discuss
Hi Ben,

This is great, thanks again for your input! I'll get cracking!

Cheers, Max
Reply all
Reply to author
Forward
0 new messages