Dear SLiM community,
I have a question on the next generation is formed in SLiM's WF model.
Based on the experiments described below, I think what I assumed about SLiM's mechanism is wrong.
A common modeling assumption for the life-cycle of a population is
Parent generation (finite) -> Selection (finite) -> Gamete generation (infinite via sampling with replacement) -> Offspring generation (finite). The gamete pool in which the offspring generation is formed is considered infinite.
This serves as the basis of quantitative genetics modeling. We project the parent population's moments (e.g. mean, variance) to a corresponding Gaussian distribution. Then, we obtain the new population's mean and variance by sampling from the Gaussian distribution. For the variance side, this recursion yields a geometric brownian motion (when selection is absent). At stationarity, this variance follows an inverse-Gamma distribution with a polynomially decaying tail (much slower than normal's exponential tail).
In SLiM, I found that the population variance's distribution over replicate simulation is much narrower than what is predicted by the previous paragraphs. I simulated the following stabilizing selection scenario with very weak selection and high recombination to mimic an effectively neutral evolution.

Legend: Each point is a simulation replicate. The distribution of V_t (population additive variance) over replicates is more spread in theory quantile compared to actual slim outcome.
The slim code:
```
// Simulate quantitative trait with infinite sites model
initialize() {
if (!exists("N")) defineConstant("N", 1000);
if (!exists("L")) defineConstant("L", 1000000);
if (!exists("MUT_RATE")) defineConstant("MUT_RATE", 1e-8);
if (!exists("REC_RATE")) defineConstant("REC_RATE", 1e-8);
if (!exists("SIGMA_A")) defineConstant("SIGMA_A", 1.0);
if (!exists("VS")) defineConstant("VS", 10000.0);
if (!exists("MU")) defineConstant("MU", 10.0);
if (!exists("GENERATIONS")) defineConstant("GENERATIONS", 10000);
if (!exists("OUTPUT_FILE")) defineConstant("OUTPUT_FILE", "sim_output.csv");
if (!exists("TRAIT_OUTPUT_FILE")) defineConstant("TRAIT_OUTPUT_FILE", "sim_traits.csv");
if (!exists("TRAIT_SNAPSHOT_INTERVAL")) defineConstant("TRAIT_SNAPSHOT_INTERVAL", 100);
initializeMutationType("m2", 0.5, "n", 0.0, SIGMA_A);
initializeGenomicElementType("g1", m2, 1.0);
initializeGenomicElement(g1, 0, L-1);
initializeMutationRate(MUT_RATE);
initializeRecombinationRate(REC_RATE);
}
1 early() {
sim.addSubpop("p1", N);
// Initial centering to 0, optimum is MU
defineConstant("INITIAL_MEAN", 0.0);
writeFile(OUTPUT_FILE, "tick,mean,var", append=F);
writeFile(TRAIT_OUTPUT_FILE, "tick,individual_id,genetic_value", append=F);
community.rescheduleScriptBlock(s1, start=1, end=GENERATIONS);
community.rescheduleScriptBlock(s2, start=GENERATIONS, end=GENERATIONS);
}
mutationEffect(m2) {
return 1.0;
}
s1 1: late() {
genetic_values = p1.individuals.sumOfMutationsOfType(m2);
mean_p = mean(genetic_values);
var_p = var(genetic_values);
log_line = paste(c(community.tick, mean_p, var_p), sep=",");
writeFile(OUTPUT_FILE, log_line, append=T);
if (community.tick % TRAIT_SNAPSHOT_INTERVAL == 0) {
for (i in seqAlong(genetic_values)) {
trait_line = paste(c(community.tick, i, genetic_values[i]), sep=",");
writeFile(TRAIT_OUTPUT_FILE, trait_line, append=T);
}
}
}
fitnessEffect() {
phenotype_raw = individual.sumOfMutationsOfType(m2);
return exp( -1.0 * ((phenotype_raw - MU)^2) / (2.0 * VS) ); # stabilizing selection
}
s2 10000 late() {
catn("Simulation finished at tick " + community.tick);
community.simulationFinished();
}
```
Does SLiM follow a different offspring generation mechanism other than forming an infinite gamete pool? If so, can I adjust the option to follow this line of idea?
Best,
Hanbin