Skip to content

Commit f6e4000

Browse files
committed
fix #570, make it easier to zero out migration rates
1 parent 2499c9e commit f6e4000

5 files changed

Lines changed: 44 additions & 12 deletions

File tree

QtSLiM/help/SLiMHelpClasses.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,8 @@
13131313
<p class="p3">– (void)setCloningRate(numeric rate)</p>
13141314
<p class="p4">Set the cloning rate of this subpopulation.<span class="Apple-converted-space">  </span>The rate is changed to <span class="s1">rate</span>, which should be between 0.0 and 1.0, inclusive (see the SLiM manual for further details).<span class="Apple-converted-space">  </span>Clonal reproduction can be enabled in both non-sexual (i.e. hermaphroditic) and sexual simulations.<span class="Apple-converted-space">  </span>In non-sexual simulations, <span class="s1">rate</span> must be a singleton value representing the overall clonal reproduction rate for the subpopulation.<span class="Apple-converted-space">  </span>In sexual simulations, <span class="s1">rate</span> may be either a singleton (specifying the clonal reproduction rate for both sexes) or a vector containing two numeric values (the female and male cloning rates specified separately, at indices <span class="s1">0</span> and <span class="s1">1</span> respectively).<span class="Apple-converted-space">  </span>During mating and offspring generation, the probability that any given offspring individual will be generated by cloning – by asexual reproduction without gametes or meiosis – will be equal to the cloning rate (for its sex, in sexual simulations) set in the parental (not the offspring!) subpopulation.</p>
13151315
<p class="p3">– (void)setMigrationRates(io&lt;Subpopulation&gt; sourceSubpops, numeric rates)</p>
1316-
<p class="p4">Set the migration rates to this subpopulation from the subpopulations in <span class="s1">sourceSubpops</span> to the corresponding rates specified in <span class="s1">rates</span>; in other words, <span class="s1">rates</span> gives the expected fractions of the children in this subpopulation that will subsequently be generated from parents in the subpopulations <span class="s1">sourceSubpops</span> (see the SLiM manual for further details).<span class="Apple-converted-space">  </span>This method will only set the migration fractions from the subpopulations given; migration rates from other subpopulations will be left unchanged (explicitly set a zero rate to turn off migration from a given subpopulation).<span class="Apple-converted-space">  </span>The type of <span class="s1">sourceSubpops</span> may be either <span class="s1">integer</span>, specifying subpopulations by identifier, or <span class="s1">object</span>, specifying subpopulations directly.</p>
1316+
<p class="p6">Set the migration rates to this subpopulation from the subpopulations in <span class="s1">sourceSubpops</span> to the corresponding rates specified in <span class="s1">rates</span>; in other words, <span class="s1">rates</span> gives the expected fractions of the children in this subpopulation that will subsequently be generated from parents in the subpopulations <span class="s1">sourceSubpops</span> (see section 24.2.1).<span class="Apple-converted-space">  </span>The <span class="s1">rates</span> parameter may be a singleton value, in which case that rate is used for all subpopulations in <span class="s1">sourceSubpops</span>.<span class="Apple-converted-space">  </span>This method will only set the migration fractions from the subpopulations given; migration rates from other subpopulations will be left unchanged (explicitly set a zero rate to turn off migration from a given subpopulation).<span class="Apple-converted-space">  </span>The type of <span class="s1">sourceSubpops</span> may be either <span class="s1">integer</span>, specifying subpopulations by identifier, or <span class="s1">object</span>, specifying subpopulations directly.</p>
1317+
<p class="p6">In general it is illegal to try to set the migration rate into a subpopulation from itself; that rate is, by definition, equal to the remainder after all migration from other subpopulations.<span class="Apple-converted-space">  </span>As a special case for convenience, it is legal to set a rate of <span class="s1">0.0</span> for all subpopulations in the species, including the target subpopulation.<span class="Apple-converted-space">  </span>For example, <span class="s1">subpops.setMigrationRates(allSubpops, 0.0)</span> will turn off all migration into the subpopulations in <span class="s1">subpops</span>.<span class="Apple-converted-space">  </span>The given rate of <span class="s1">0.0</span> from a subpop into itself is simply ignored, for this specific case only.</p>
13171318
<p class="p3">– (void)setSelfingRate(numeric$ rate)</p>
13181319
<p class="p4">Set the selfing rate of this subpopulation.<span class="Apple-converted-space">  </span>The rate is changed to <span class="s1">rate</span>, which should be between 0.0 and 1.0, inclusive (see the SLiM manual for further details).<span class="Apple-converted-space">  </span>Selfing can only be enabled in non-sexual (i.e. hermaphroditic) simulations.<span class="Apple-converted-space">  </span>During mating and offspring generation, the probability that any given offspring individual will be generated by selfing – by self-fertilization via gametes produced by meiosis by a single parent – will be equal to the selfing rate set in the parental (not the offspring!) subpopulation.</p>
13191320
<p class="p3">– (void)setSexRatio(float$ sexRatio)</p>

SLiMgui/SLiMHelpClasses.rtf

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13663,22 +13663,34 @@ This method is similar to getting the
1366313663
\f5 \
1366413664
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
1366513665

13666-
\f4\fs20 \cf0 Set the migration rates to this subpopulation from the subpopulations in
13666+
\f4\fs20 \cf2 Set the migration rates to this subpopulation from the subpopulations in
1366713667
\f3\fs18 sourceSubpops
1366813668
\f4\fs20 to the corresponding rates specified in
1366913669
\f3\fs18 rates
1367013670
\f4\fs20 ; in other words,
1367113671
\f3\fs18 rates
1367213672
\f4\fs20 gives the expected fractions of the children in this subpopulation that will subsequently be generated from parents in the subpopulations
1367313673
\f3\fs18 sourceSubpops
13674-
\f4\fs20 (see the SLiM manual for further details). This method will only set the migration fractions from the subpopulations given; migration rates from other subpopulations will be left unchanged (explicitly set a zero rate to turn off migration from a given subpopulation). The type of
13674+
\f4\fs20 (see section 24.2.1). The
13675+
\f3\fs18 rates
13676+
\f4\fs20 parameter may be a singleton value, in which case that rate is used for all subpopulations in
13677+
\f3\fs18 sourceSubpops
13678+
\f4\fs20 . This method will only set the migration fractions from the subpopulations given; migration rates from other subpopulations will be left unchanged (explicitly set a zero rate to turn off migration from a given subpopulation). The type of
1367513679
\f3\fs18 sourceSubpops
1367613680
\f4\fs20 may be either
1367713681
\f3\fs18 integer
1367813682
\f4\fs20 , specifying subpopulations by identifier, or
1367913683
\f3\fs18 object
13680-
\f4\fs20 , specifying subpopulations directly.
13681-
\f5 \
13684+
\f4\fs20 , specifying subpopulations directly.\
13685+
In general it is illegal to try to set the migration rate into a subpopulation from itself; that rate is, by definition, equal to the remainder after all migration from other subpopulations. As a special case for convenience, it is legal to set a rate of
13686+
\f3\fs18 0.0
13687+
\f4\fs20 for all subpopulations in the species, including the target subpopulation. For example,
13688+
\f3\fs18 subpops.setMigrationRates(allSubpops, 0.0)
13689+
\f4\fs20 will turn off all migration into the subpopulations in
13690+
\f3\fs18 subpops
13691+
\f4\fs20 . The given rate of
13692+
\f3\fs18 0.0
13693+
\f4\fs20 from a subpop into itself is simply ignored, for this specific case only.\
1368213694
\pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0
1368313695

1368413696
\f3\fs18 \cf0 \'96\'a0(void)setSelfingRate(numeric$\'a0rate)

VERSIONS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ multitrait branch:
8484
Substitution: add read-only <trait-name>HemizygousDominance property
8585
policy change: the nucleotide and nucleotideValue properties of Substitution are now read-only (I think it was a bug that they were ever read-write...?)
8686
fix #564, initializeMutationRateFromFile() needs a `sex` parameter; I'm doing this in multitrait to avoid the annoying doc conflicts
87+
fix #570, setMigrationRates() should make it easier to stop all migration (done in multitrait to avoid the annoying doc conflicts)
88+
specifically, this entails two changes:
89+
allow a singleton migration rate value, applied for all subpops given
90+
allow the destination subpop to be given as a source, iff all rates supplied are 0.0, to stop all migration: allSubpops.setMigrationRates(allSubpops, 0.0)
8791

8892

8993
version 5.1 (Eidos version 4.1):

core/slim_test_core.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,10 +1337,13 @@ void _RunSubpopulationTests(void)
13371337
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(2, 4), c(0.1, 0.1)); } 10 early() { stop(); }", "not defined", __LINE__);
13381338
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(2, 2), c(0.1, 0.1)); } 10 early() { stop(); }", "two rates set", __LINE__);
13391339
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(p2, p2), c(0.1, 0.1)); } 10 early() { stop(); }", "two rates set", __LINE__);
1340-
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(2, 3), 0.1); } 10 early() { stop(); }", "to be equal in size", __LINE__);
1340+
SLiMAssertScriptStop(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(2, 3), 0.1); } 10 early() { stop(); }", __LINE__);
1341+
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(2, 3), float(0)); } 10 early() { stop(); }", "to be equal in size", __LINE__);
1342+
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(2, 3), c(0.1, 0.1, 0.1)); } 10 early() { stop(); }", "to be equal in size", __LINE__);
13411343
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(2, c(0.1, 0.1)); } 10 early() { stop(); }", "to be equal in size", __LINE__);
13421344
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(2, -0.0001); } 10 early() { stop(); }", "within [0,1]", __LINE__);
13431345
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(2, 1.0001); } 10 early() { stop(); }", "within [0,1]", __LINE__);
1346+
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(2, 3), 0.6); } 10 early() { stop(); }", "must sum to <= 1.0", __LINE__, false); // raise is from EvolveSubpopulation(); we don't force constraints prematurely
13441347
SLiMAssertScriptRaise(gen1_setup_p1p2p3 + "1 early() { p1.setMigrationRates(c(2, 3), c(0.6, 0.6)); } 10 early() { stop(); }", "must sum to <= 1.0", __LINE__, false); // raise is from EvolveSubpopulation(); we don't force constraints prematurely
13451348

13461349
// Test Subpopulation - (void)setSelfingRate(numeric$ rate)

core/subpopulation.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8975,22 +8975,34 @@ EidosValue_SP Subpopulation::ExecuteMethod_setMigrationRates(EidosGlobalStringID
89758975
int source_subpops_count = sourceSubpops_value->Count();
89768976
int rates_count = rates_value->Count();
89778977
std::vector<slim_objectid_t> subpops_seen;
8978+
bool saw_nonzero_rate = false, saw_self_reference = false;
89788979

8979-
if (source_subpops_count != rates_count)
8980-
EIDOS_TERMINATION << "ERROR (Subpopulation::ExecuteMethod_setMigrationRates): setMigrationRates() requires sourceSubpops and rates to be equal in size." << EidosTerminate();
8980+
if ((source_subpops_count != rates_count) && (rates_count != 1))
8981+
EIDOS_TERMINATION << "ERROR (Subpopulation::ExecuteMethod_setMigrationRates): setMigrationRates() requires sourceSubpops and rates to be equal in size, or rates to be singleton." << EidosTerminate();
89818982

89828983
for (int value_index = 0; value_index < source_subpops_count; ++value_index)
89838984
{
89848985
EidosObject *source_subpop = SLiM_ExtractSubpopulationFromEidosValue_io(sourceSubpops_value, value_index, &species_.community_, &species_, "setMigrationRates()"); // SPECIES CONSISTENCY CHECK
89858986
slim_objectid_t source_subpop_id = ((Subpopulation *)(source_subpop))->subpopulation_id_;
8986-
8987+
double migrant_fraction = ((rates_count == 1) ? rates_value->NumericAtIndex_NOCAST(0, nullptr) : rates_value->NumericAtIndex_NOCAST(value_index, nullptr));
8988+
8989+
// BCH 11/16/2025: We used to require that the target subpop was not a member of sourceSubpops; we would
8990+
// raise an error in all cases if that occurred. Now we relax those rules slightly, to make it easier
8991+
// to zero out all immigration into a subpop or subpops; we allow self-reference, but *only* if *all*
8992+
// rates specified in the call are 0.0. So you can do, e.g., allSubpops.setMigrationRates(allSubpops, 0).
8993+
// See https://github.com/MesserLab/SLiM/issues/570. As part of that fix, we also now allow rates to
8994+
// provide a singleton value, used for all sourceSubpops.
8995+
if (migrant_fraction != 0.0)
8996+
saw_nonzero_rate = true;
89878997
if (source_subpop_id == subpopulation_id_)
8988-
EIDOS_TERMINATION << "ERROR (Subpopulation::ExecuteMethod_setMigrationRates): setMigrationRates() does not allow migration to be self-referential (originating within the destination subpopulation)." << EidosTerminate();
8998+
saw_self_reference = true;
8999+
if (saw_self_reference && saw_nonzero_rate)
9000+
EIDOS_TERMINATION << "ERROR (Subpopulation::ExecuteMethod_setMigrationRates): setMigrationRates() does not allow migration to be self-referential (originating within the destination subpopulation), except when all rates are zero (for convenience)." << EidosTerminate();
9001+
9002+
// can't specify the same source subpopulation twice
89899003
if (std::find(subpops_seen.begin(), subpops_seen.end(), source_subpop_id) != subpops_seen.end())
89909004
EIDOS_TERMINATION << "ERROR (Subpopulation::ExecuteMethod_setMigrationRates): setMigrationRates() two rates set for subpopulation p" << source_subpop_id << "." << EidosTerminate();
89919005

8992-
double migrant_fraction = rates_value->NumericAtIndex_NOCAST(value_index, nullptr);
8993-
89949006
population_.SetMigration(*this, source_subpop_id, migrant_fraction);
89959007
subpops_seen.emplace_back(source_subpop_id);
89969008
}

0 commit comments

Comments
 (0)