Skip to content

Commit 7c992c6

Browse files
committed
fix CI warnings and errors, fix recalculateFitness()
1 parent ea8067f commit 7c992c6

File tree

14 files changed

+71
-26
lines changed

14 files changed

+71
-26
lines changed

QtSLiM/help/SLiMHelpClasses.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1093,9 +1093,10 @@
10931093
<p class="p6">As of SLiM 5.0, this method will read and restore substitutions if they were saved by <span class="s1">outputFull()</span> with its <span class="s1">substitutions=T</span> option.<span class="Apple-converted-space">  </span>This facility is only available when reading binary output from <span class="s1">outputFull()</span>, as chosen by its <span class="s1">binary=T</span> option; otherwise, an error will result.</p>
10941094
<p class="p6">This method can also be used to read tree-sequence information, in the form of single-chromosome <span class="s1">.trees</span> files and multi-chromosome trees archives, as saved by <span class="s1">treeSeqOutput()</span> or generated by the Python <span class="s1">pyslim</span> package.<span class="Apple-converted-space">  </span>Note that the user metadata for a tree-sequence file can be read separately with the <span class="s1">treeSeqMetadata()</span> function.<span class="Apple-converted-space">  </span>Beginning with SLiM 4, the <span class="s1">subpopMap</span> parameter may be supplied to re-order the populations of the input tree sequence when it is loaded in to SLiM.<span class="Apple-converted-space">  </span>This parameter must have a value that is a <span class="s1">Dictionary</span>; the keys of this dictionary should be SLiM population identifiers as <span class="s1">string</span> values (e.g., <span class="s1">"p2"</span>), and the values should be indexes of populations in the input tree sequence; a key/value pair of <span class="s1">"p2", 4</span> would mean that the fifth population in the input (the one at zero-based index <span class="s1">4</span>) should become <span class="s1">p2</span> on loading into SLiM.<span class="Apple-converted-space">  </span>If <span class="s1">subpopMap</span> is non-<span class="s1">NULL</span>, <i>all</i> populations in the tree sequence must be explicitly mapped, even if their index will not change and even if they will not be used by SLiM; the only exception is for unused slots in the population table, which can be explicitly remapped but do not have to be.<span class="Apple-converted-space">  </span>For instance, suppose we have a tree sequence in which population <span class="s1">0</span> is unused, population <span class="s1">1</span> is not a SLiM population (for example, an ancestral population produced by <span class="s1">msprime</span>), and population 2 is a SLiM population, and we want to load this in with population 2 as <span class="s1">p0</span> in SLiM.<span class="Apple-converted-space">  </span>To do this, we could supply a value of <span class="s1">Dictionary("p0", 2, "p1", 1, "p2", 0)</span> for <span class="s1">subpopMap</span>, or we could leave out slot <span class="s1">0</span> since it is unused, with <span class="s1">Dictionary("p0", 2, "p1", 1)</span>.<span class="Apple-converted-space">  </span>Although this facility cannot be used to remove populations in the tree sequence, note that it may <i>add</i> populations that will be visible when <span class="s1">treeSeqOutput()</span> is called (although these will not be SLiM populations); if, in this example, we had used <span class="s1">Dictionary("p0", 0, "p1", 1, "p5", 2)</span> and then we wrote the result out with <span class="s1">treeSeqOutput()</span>, the resulting tree sequence would have six populations, although three of them would be empty and would not be used by SLiM.<span class="Apple-converted-space">  </span>The use of <span class="s1">subpopMap</span> makes it easier to load simulation data that was generated in Python, since that typically uses an id of <span class="s1">0</span>.<span class="Apple-converted-space">  </span>The <span class="s1">subpopMap</span> parameter may not be used with file formats other than tree-sequence files, at the present time; setting up the correct subpopulation ids is typically easier when working with those other formats.<span class="Apple-converted-space">  </span>Note the <span class="s1">tskit</span> command-line interface can be used, like <span class="s1">python3 -m tskit populations file.trees</span>, to find out the number of subpopulations in a tree-sequence file and their IDs.</p>
10951095
<p class="p6">When loading a tree sequence, a crosscheck of the loaded data will be performed to ensure that the tree sequence was well-formed and was loaded correctly.<span class="Apple-converted-space">  </span>When running a Release build of SLiM, however, this crosscheck will only occur the first time that <span class="s1">readFromPopulationFile()</span> is called to load a tree sequence; subsequent calls will not perform this crosscheck, for greater speed when running models that load saved population state many times (such as models that are conditional on fixation).<span class="Apple-converted-space">  </span>If you suspect that a tree sequence file might be corrupted, or might be read incorrectly, running a Debug build of SLiM enables crosschecks after every load.</p>
1096-
<p class="p3">– (void)recalculateFitness([Ni$ tick = NULL])</p>
1096+
<p class="p3">– (void)recalculateFitness([Ni$ tick = NULL]<span class="s5">, [logical$ forceRecalc = T]</span>)</p>
10971097
<p class="p6">Force an immediate recalculation of fitness values for all individuals in all subpopulations.<span class="Apple-converted-space">  </span>Normally fitness values are calculated at a fixed point in each tick, and those values are cached and used until the next recalculation.<span class="Apple-converted-space">  </span>If simulation parameters are changed in script in a way that affects fitness calculations, and if you wish those changes to take effect immediately rather than taking effect at the next automatic recalculation, you may call <span class="s1">recalculateFitness()</span> to force an immediate recalculation and recache.</p>
10981098
<p class="p6">The optional parameter <span class="s1">tick</span> provides the tick for which <span class="s1">mutationEffect()</span> and <span class="s1">fitnessEffect()</span> callbacks should be selected; if it is <span class="s1">NULL</span> (the default), the current tick value for the simulation, <span class="s1">community.tick</span>, is used.<span class="Apple-converted-space">  </span>If you call <span class="s1">recalculateFitness()</span> in an <span class="s1">early()</span> event in a WF model, you may wish this to be <span class="s1">community.tick - 1</span> in order to utilize the <span class="s1">mutationEffect()</span> and <span class="s1">fitnessEffect()</span> callbacks for the previous tick, as if the changes that you have made to fitness-influencing parameters were already in effect at the end of the previous tick when the new generation was first created and evaluated (usually it is simpler to just make such changes in a <span class="s1">late()</span> event instead, however, in which case calling <span class="s1">recalculateFitness()</span> is probably not necessary at all since fitness values will be recalculated immediately afterwards).<span class="Apple-converted-space">  </span>Regardless of the value supplied for <span class="s1">tick</span> here, <span class="s1">community.tick</span> inside callbacks will report the true tick number, so if your callbacks consult that parameter in order to create tick-specific fitness effects you will need to handle the discrepancy somehow.<span class="Apple-converted-space">  </span>(Similar considerations apply for nonWF models that call <span class="s1">recalculateFitness()</span> in a <span class="s1">late()</span> event, which is also not advisable in general.)</p>
1099+
<p class="p6">If <span class="s1">forceRecalc</span> is <span class="s1">T</span> (the default), this method will force the recalculation of all trait values upon which fitness depends.<span class="Apple-converted-space">  </span>(If a trait does not have a direct effect on fitness, it will not be recalculated even when <span class="s1">forceRecalc</span> is <span class="s1">T</span>; use <span class="s1">demandPhenotype()</span> to force recalculation of such traits.)<span class="Apple-converted-space">  </span>If <span class="s1">forceRecalc</span> is <span class="s1">F</span>, trait values will only be recalculated if they are marked as invalid – in other words, if their value is <span class="s1">NAN</span>.<span class="Apple-converted-space">  </span>This option can improve performance by skipping redundant calculations, but can produce out-of-date fitness values if something in the model state has changed such that trait values actually do need to be recalculated even though they are not <span class="s1">NAN</span>.<span class="Apple-converted-space">  </span>This could occur if, for example, if a <span class="s1">mutationEffect()</span> callback’s activation state has been changed.</p>
10991100
<p class="p6">After this call, the fitness values used for all purposes in SLiM will be the newly calculated values.<span class="Apple-converted-space">  </span>Calling this method will trigger the calling of any enabled and applicable <span class="s1">mutationEffect()</span> and <span class="s1">fitnessEffect()</span> callbacks, so this is quite a heavyweight operation; you should think carefully about what side effects might result (which is why fitness recalculation does not just occur automatically after changes that might affect fitness values).</p>
11001101
<p class="p3">– (object&lt;SLiMEidosBlock&gt;$)registerFitnessEffectCallback(Nis$ id, string$ source, [Nio&lt;Subpopulation&gt;$ subpop<span class="s9"> </span>= NULL], [Ni$ start = NULL], [Ni$ end = NULL])</p>
11011102
<p class="p6">Register a block of Eidos source code, represented as the <span class="s1">string</span> singleton <span class="s1">source</span>, as an Eidos <span class="s1">fitnessEffect()</span> callback in the current simulation (specific to the target species), with an optional subpopulation <span class="s1">subpop</span> (which may be an <span class="s1">integer</span> identifier, or <span class="s1">NULL</span>, the default, to indicate all subpopulations), and optional <span class="s1">start</span> and <span class="s1">end</span> ticks all limiting its applicability.<span class="Apple-converted-space">  </span>The script block will be given identifier <span class="s1">id</span> (specified as an <span class="s1">integer</span>, or as a <span class="s1">string</span> symbolic name such as <span class="s1">"s5"</span>); this may be <span class="s1">NULL</span> if there is no need to be able to refer to the block later.<span class="Apple-converted-space">  </span>The registered callback is added to the end of the list of registered <span class="s1">SLiMEidosBlock</span> objects, and is active immediately; it <i>may</i> be eligible to execute in the current tick.<span class="Apple-converted-space">  </span>The new <span class="s1">SLiMEidosBlock</span> will be defined as a global variable immediately by this method, and will also be returned by this method.</p>

SLiMgui/SLiMHelpClasses.rtf

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10487,7 +10487,7 @@ When loading a tree sequence, a crosscheck of the loaded data will be performed
1048710487
\f4\fs20 is called to load a tree sequence; subsequent calls will not perform this crosscheck, for greater speed when running models that load saved population state many times (such as models that are conditional on fixation). If you suspect that a tree sequence file might be corrupted, or might be read incorrectly, running a Debug build of SLiM enables crosschecks after every load.\
1048810488
\pard\pardeftab397\li720\fi-446\ri720\sb180\sa60\partightenfactor0
1048910489

10490-
\f3\fs18 \cf0 \'96\'a0(void)recalculateFitness([Ni$\'a0tick\'a0=\'a0NULL])\
10490+
\f3\fs18 \cf0 \'96\'a0(void)recalculateFitness([Ni$\'a0tick\'a0=\'a0NULL]\cf2 , [logical$\'a0forceRecalc\'a0=\'a0T]\cf0 )\
1049110491
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
1049210492

1049310493
\f4\fs20 \cf2 Force an immediate recalculation of fitness values for all individuals in all subpopulations. Normally fitness values are calculated at a fixed point in each tick, and those values are cached and used until the next recalculation. If simulation parameters are changed in script in a way that affects fitness calculations, and if you wish those changes to take effect immediately rather than taking effect at the next automatic recalculation, you may call
@@ -10526,7 +10526,30 @@ The optional parameter
1052610526
\f4\fs20 in a
1052710527
\f3\fs18 late()
1052810528
\f4\fs20 event, which is also not advisable in general.)\
10529-
After this call, the fitness values used for all purposes in SLiM will be the newly calculated values. Calling this method will trigger the calling of any enabled and applicable
10529+
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
10530+
\cf2 If
10531+
\f3\fs18 forceRecalc
10532+
\f4\fs20 is
10533+
\f3\fs18 T
10534+
\f4\fs20 (the default), this method will force the recalculation of all trait values upon which fitness depends. (If a trait does not have a direct effect on fitness, it will not be recalculated even when
10535+
\f3\fs18 forceRecalc
10536+
\f4\fs20 is
10537+
\f3\fs18 T
10538+
\f4\fs20 ; use
10539+
\f3\fs18 demandPhenotype()
10540+
\f4\fs20 to force recalculation of such traits.) If
10541+
\f3\fs18 forceRecalc
10542+
\f4\fs20 is
10543+
\f3\fs18 F
10544+
\f4\fs20 , trait values will only be recalculated if they are marked as invalid \'96 in other words, if their value is
10545+
\f3\fs18 NAN
10546+
\f4\fs20 . This option can improve performance by skipping redundant calculations, but can produce out-of-date fitness values if something in the model state has changed such that trait values actually do need to be recalculated even though they are not
10547+
\f3\fs18 NAN
10548+
\f4\fs20 . This could occur if, for example, if a
10549+
\f3\fs18 mutationEffect()
10550+
\f4\fs20 callback\'92s activation state has been changed.\
10551+
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
10552+
\cf2 After this call, the fitness values used for all purposes in SLiM will be the newly calculated values. Calling this method will trigger the calling of any enabled and applicable
1053010553
\f3\fs18 mutationEffect()
1053110554
\f4\fs20 and
1053210555
\f3\fs18 fitnessEffect()

VERSIONS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ multitrait branch:
123123
note that there is no concept of independent dominance for the hemizygous dominance coefficient, since hemizygous mutations are present in only one copy by definition
124124
switch fitness calculation over to being based upon the calculated values of traits, rather than directly upon mutations
125125
eliminate overhead for setting up fitness buffers in neutral WF models; this should provide a significant speedup for such models, if they don't use a mateChoice() callback
126+
extend recalculateFitness() with [logical$ forceRecalc = T] option for backward compatibility, but allowing trait value recalculation not to be forced
126127

127128

128129
version 5.1 (Eidos version 4.1):

core/community.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2894,7 +2894,7 @@ bool Community::_RunOneTickWF(void)
28942894
if (is_explicit_species_)
28952895
gSLiMScheduling << "\tfitness recalculation: species " << species->name_ << std::endl;
28962896
#endif
2897-
species->RecalculateFitness();
2897+
species->RecalculateFitness(/* p_force_trait_recalculation */ false);
28982898

28992899
executing_species_ = nullptr;
29002900
}
@@ -3197,7 +3197,7 @@ bool Community::_RunOneTickNonWF(void)
31973197
if (is_explicit_species_)
31983198
gSLiMScheduling << "\tfitness recalculation: species " << species->name_ << std::endl;
31993199
#endif
3200-
species->RecalculateFitness();
3200+
species->RecalculateFitness(/* p_force_trait_recalculation */ false);
32013201

32023202
executing_species_ = nullptr;
32033203
}

core/haplosome.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2858,7 +2858,7 @@ EidosValue_SP Haplosome_Class::ExecuteMethod_addNewMutation(EidosGlobalStringID
28582858
// for the singleton case for each of the parameters, get all the info
28592859
MutationType *singleton_mutation_type_ptr = SLiM_ExtractMutationTypeFromEidosValue_io(arg_muttype, 0, &community, species, method_name.c_str()); // SPECIES CONSISTENCY CHECK
28602860

2861-
slim_effect_t singleton_selection_coeff = (arg_effect ? (slim_effect_t)arg_effect->NumericAtIndex_NOCAST(0, nullptr) : 0.0);
2861+
slim_effect_t singleton_selection_coeff = (arg_effect ? (slim_effect_t)arg_effect->NumericAtIndex_NOCAST(0, nullptr) : (slim_effect_t)0.0);
28622862

28632863
slim_position_t singleton_position = SLiMCastToPositionTypeOrRaise(arg_position->IntAtIndex_NOCAST(0, nullptr));
28642864

core/mutation.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ mutation_type_ptr_(p_mutation_type_ptr), position_(p_position), subpop_index_(p_
7878

7979
// FIXME MULTITRAIT: This constructor needs to change to have a whole vector of trait information passed in, for effect and dominance
8080
// for now we use the values passed in for trait 0, and make other traits neutral
81-
slim_effect_t effect = (trait_index == 0) ? p_selection_coeff : 0.0;
82-
slim_effect_t dominance = (trait_index == 0) ? p_dominance_coeff : 0.5; // can be NAN
81+
slim_effect_t effect = (trait_index == 0) ? p_selection_coeff : (slim_effect_t)0.0;
82+
slim_effect_t dominance = (trait_index == 0) ? p_dominance_coeff : (slim_effect_t)0.5; // can be NAN
8383
slim_effect_t hemizygous_dominance = mutation_type_ptr_->DefaultHemizygousDominanceForTrait(trait_index); // FIXME MULTITRAIT: This needs to come in from outside, probably
8484

8585
traitInfoRec->effect_size_ = effect;
@@ -305,8 +305,8 @@ mutation_type_ptr_(p_mutation_type_ptr), position_(p_position), subpop_index_(p_
305305

306306
// FIXME MULTITRAIT: This constructor needs to change to have a whole vector of trait information passed in, for effect and dominance
307307
// for now we use the values passed in for trait 0, and make other traits neutral
308-
slim_effect_t effect = (trait_index == 0) ? p_selection_coeff : 0.0;
309-
slim_effect_t dominance = (trait_index == 0) ? p_dominance_coeff : 0.5; // can be NAN
308+
slim_effect_t effect = (trait_index == 0) ? p_selection_coeff : (slim_effect_t)0.0;
309+
slim_effect_t dominance = (trait_index == 0) ? p_dominance_coeff : (slim_effect_t)0.5; // can be NAN
310310
slim_effect_t hemizygous_dominance = mutation_type_ptr_->DefaultHemizygousDominanceForTrait(trait_index); // FIXME MULTITRAIT: This needs to come in from outside, probably
311311

312312
traitInfoRec->effect_size_ = effect;

core/population.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5241,7 +5241,7 @@ void Population::AddTallyForMutationTypeAndBinNumber(int p_mutation_type_index,
52415241
}
52425242
#endif
52435243

5244-
void Population::RecalculateFitness(slim_tick_t p_tick)
5244+
void Population::RecalculateFitness(slim_tick_t p_tick, bool p_force_trait_recalculation)
52455245
{
52465246
// calculate the fitnesses of the parents and make lookup tables; the main thing we do here is manage the mutationEffect() callbacks
52475247
// as per the SLiM design spec, we get the list of callbacks once, and use that list throughout this stage, but we construct
@@ -5438,7 +5438,7 @@ void Population::RecalculateFitness(slim_tick_t p_tick)
54385438
std::vector<SLiMEidosBlock*> no_callbacks;
54395439

54405440
for (std::pair<const slim_objectid_t,Subpopulation*> &subpop_pair : subpops_)
5441-
subpop_pair.second->UpdateFitness(no_callbacks, no_callbacks, p_direct_effect_trait_indices);
5441+
subpop_pair.second->UpdateFitness(no_callbacks, no_callbacks, p_direct_effect_trait_indices, p_force_trait_recalculation);
54425442
}
54435443
else
54445444
{
@@ -5466,7 +5466,7 @@ void Population::RecalculateFitness(slim_tick_t p_tick)
54665466
}
54675467

54685468
// Update fitness values, using the callbacks
5469-
subpop->UpdateFitness(subpop_mutationEffect_callbacks, subpop_fitnessEffect_callbacks, p_direct_effect_trait_indices);
5469+
subpop->UpdateFitness(subpop_mutationEffect_callbacks, subpop_fitnessEffect_callbacks, p_direct_effect_trait_indices, p_force_trait_recalculation);
54705470
}
54715471
}
54725472

core/population.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ class Population
221221
Individual *(Subpopulation::*GenerateIndividualCloned_TEMPLATED)(Individual *p_parent) = nullptr;
222222

223223
// Recalculate all fitness values for the parental generation, including the use of mutationEffect() callbacks
224-
void RecalculateFitness(slim_tick_t p_tick);
224+
void RecalculateFitness(slim_tick_t p_tick, bool p_force_trait_recalculation);
225225

226226
// Scan through all mutation runs in the simulation and unique them
227227
void UniqueMutationRuns(void);

0 commit comments

Comments
 (0)