Survival from fitness in nonWF models

20 views
Skip to first unread message

Rohini Janivara

unread,
Apr 20, 2024, 5:07:55 PMApr 20
to slim-discuss
Hello all,

I am trying to model a population of asexually reproducing species in a nonWF model. I want to model the viability based on relative fitness, rather than absolute. Though I have a carrying capacity, I would like one of birth or death to be fitness dependant and the other to be random. It doesn't matter which one is fitnes based. However, since viability is based on fitness values intrinsically, I want to to scale it so as to preserve some stochasticity in the system wrt population size around the carrying capacity. 

So far, I have implemented a finess influenced birth process with number of deaths (x) being dependant on K-p1.individualCount, while choosing individuals to die is completely random. I assign fitness values for everyone to be 10,000 and pick x individuals and set their fitness to 0.3 (since I still want to preserve some stochasticity). This however doesn't result in any deaths and my population size keeps growing. I do recalculate fitness in an early event since the birth processes are fitness dependant.

If anyone has insights on how survival probabilities are calculated from fitness values, or how a nonWF model can be implemented with relative fitness, I would really appreciate the help. In addition, I am having difficulty printing out individual fitness values to debug with.

Thanks in advance!
Rohini

Ben Haller

unread,
Apr 20, 2024, 6:04:55 PMApr 20
to Rohini Janivara, slim-discuss
Hi Rohini!  OK, let's see.
> I am trying to model a population of asexually reproducing species in
> a nonWF model.
OK, so using addCrossed(), I suppose?  (It's helpful, when asking a
question on the list, to show the code you've got.)
> I want to model the viability based on relative fitness, rather than
> absolute.
OK.  That is different from the standard nonWF model, where fitness is
absolute, as you realize I think.  Where is "fitness" coming from?  Is
it just from density-dependence, as you describe below?  Or are there
mutations affecting fitness, and/or other processes?
> Though I have a carrying capacity, I would like one of birth or death
> to be fitness dependant and the other to be random. It doesn't matter
> which one is fitnes based. However, since viability is based on
> fitness values intrinsically, I want to to scale it so as to preserve
> some stochasticity in the system wrt population size around the
> carrying capacity.
I'm not sure what you mean by "scale it" exactly, and why that "scaling"
preserves stochasticity, and so forth; can you clarify this?
> So far, I have implemented a finess influenced birth process with
> number of deaths (x) being dependant on K-p1.individualCount, while
> choosing individuals to die is completely random.
OK.  And how have you implemented those two things exactly?  Again,
seeing the code you're working on makes things much more concrete.
> I assign fitness values for everyone to be 10,000 and pick x
> individuals and set their fitness to 0.3 (since I still want to
> preserve some stochasticity). This however doesn't result in any
> deaths and my population size keeps growing. I do recalculate fitness
> in an early event since the birth processes are fitness dependant.
It's hard to guess what might be going wrong.  You say it "doesn't
result in any deaths".  How do you know that, versus that deaths are
perhaps being outbalanced by births?  What are you seeing in SLiMgui?
What does the age distribution plot show, for example?
> If anyone has insights on how survival probabilities are calculated
> from fitness values, or how a nonWF model can be implemented with
> relative fitness, I would really appreciate the help.
Chapters 23 and 24 of the SLiM manual spell out the details of exactly
what happens in the WF and nonWF tick cycles (23 and 24 respectively).
Section 24.4 details the viability/survival tick cycle stage in nonWF
models, and says:

For a given individual, a fitness of 0.0 or less results in certain
death; that individual is immediately removed from its subpopulation.  A
fitness of 1.0 or greater results in certain survival; that individual
remains in its subpopulation, and will live into the next tick cycle.  A
fitness greater than 0.0 and less than 1.0 is interpreted as a survival
probability; SLiM will do a random draw to determine whether the
individual survives or not.

There is a good deal of additional discussion in that section; perhaps
give it a read and come back if you have more questions about that.  As
for how a nonWF model can be implemented with relative fitness, see
section 15.13, on implementing a Wright–Fisher model with a nonWF model;
that will probably be helpful.
> In addition, I am having difficulty printing out individual fitness
> values to debug with.
What kind of difficulty?  What code did you try, and what happened?
Maybe this is a good time to plug a paper I wrote a little while ago,
Ten Simple Rules of Reporting a Bug:
https://doi.org/10.1371/journal.pcbi.1010540

And I hope you don't mind if I also ask: have you done the SLiM workshop
yet?  It seems like maybe you're struggling with some foundational
concepts here, and I always recommend that people start by doing the
workshop, otherwise the learning curve can indeed be rather steep! :->

Cheers,
-B.

Benjamin C. Haller
Messer Lab
Cornell University

Rohini Janivara

unread,
Apr 25, 2024, 1:51:21 PMApr 25
to slim-discuss
Hi Ben,

Thank you for your speedy and detailed response!

At the time of writing the email, I wanted information on the specifics of the sampling distribution used to convert fitness to survival probability for individuals of fitness between 0.0 and 1.0. I basically wanted to ensure fitness indeed depended on the number of selective mutations an individual had, and the mutation effect in each tick also depended on the population density. What I meant by scaling was that I wanted to constantly readjust the fitness scaling factor to increase the randomness of death (dilute the mutation fitness effects in a way). While implementing the traditional nonWF model I observed that with a significant increase in average fitness, there was an inevitable increase in population size beyond the predefined carrying capacity. My main aim was to override that last part which I achieved by changing the birth to be related to mutation fitness alone and have viability be truly random as the biological resource restriction of my model system is pretty rigid. 

I also figured out that the issue I had with printing fitness values during debugging had to do with the "recalculateFitness" function I had called in early events (to use fitness calculations from an early event to sample birth processes). Once I removed that, the fitness values made more sense. 

I understand that providing the code could've helped debugging much better. I will do so in my future inquiries.

Thank you,
Rohini

Ben Haller

unread,
Apr 26, 2024, 3:34:54 AMApr 26
to Rohini Janivara, slim-discuss
Hi Rohini!
> At the time of writing the email, I wanted information on the
> specifics of the sampling distribution used to convert fitness to
> survival probability for individuals of fitness between 0.0 and 1.0.
In the nonWF model, unless modified by a survival() callback, fitness
*is* survival probability; a final calculated fitness for an individual
of, say, 0.73 means that the individual has a 0.73 probability of survival.
> I basically wanted to ensure fitness indeed depended on the number of
> selective mutations an individual had, and the mutation effect in each
> tick also depended on the population density. What I meant by scaling
> was that I wanted to constantly readjust the fitness scaling factor to
> increase the randomness of death (dilute the mutation fitness effects
> in a way).
A survival() callback is probably the easiest way to implement this.  It
will receive SLiM's calculated fitness value for the focal individual
(which would normally be used as the survival probability, as just
described), and you could modify SLiM's survival decision in whatever
way you wish in that callback.  But in a model with density-dependence,
it seems to me that the simpler thing, if you want mutations to have a
smaller effect and stochasticity to have a larger effect, would be to
simply decrease the mutation effects by making their selection
coefficients smaller.  I guess I'd ask: what is really going on
biologically?  If there is some biological rationale for this constant
readjustment of the fitness scaling factor to increase the randomness of
death, as you describe, then ok, a survival() callback can do that for
you.  But I often find it useful to simply start from the biology,
decide what I think *that* is doing, and then translate that into a model.
> While implementing the traditional nonWF model I observed that with a
> significant increase in average fitness, there was an inevitable
> increase in population size beyond the predefined carrying capacity.
Yes; in the nonWF model type, beneficial mutations tend to increase
population size and deleterious mutations tend to decrease population
size, by default, because selection is hard and fitness is absolute, by
default.  Section 15.13, as I think I pointed out before, shows one way
to achieve WF-type dynamics, with a fixed population size, soft
selection, and relative fitness, in a nonWF model.  There are other ways
too.  The main thing really is to understand the calculations that SLiM
performs, how it does what it does, and the points where you can
intervene in that process (such as callbacks).  For the nonWF model,
chapter 24 is meant to set that out in detail, and should be read quite
carefully if you are doing complicated things with fitness calculations.
> My main aim was to override that last part which I achieved by
> changing the birth to be related to mutation fitness alone and have
> viability be truly random as the biological resource restriction of my
> model system is pretty rigid.
Sure.  I gather you have achieved that goal, at this point, with the
pointers I gave you in my previous reply?  Sorry – I'm not sure if there
is a question remaining here or not.
> I also figured out that the issue I had with printing fitness values
> during debugging had to do with the "recalculateFitness" function I
> had called in early events (to use fitness calculations from an early
> event to sample birth processes). Once I removed that, the fitness
> values made more sense.
Yes, it is rare to need to use recalculateFitness() in a model, and you
have to be quite careful that you are using it correctly.
> I understand that providing the code could've helped debugging much
> better. I will do so in my future inquiries.
Great.  :->
Reply all
Reply to author
Forward
0 new messages