Skip to content

Commit 8028e45

Browse files
committed
add a check for remapping an out-of-range pop table index
1 parent 208c57b commit 8028e45

File tree

3 files changed

+39
-9
lines changed

3 files changed

+39
-9
lines changed

QtSLiM/help/SLiMHelpClasses.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@
740740
<p class="p6"><span class="s3">As of SLiM 3.0, this method will read and restore the ages of individuals if that information is present in the output file and the simulation is based upon the nonWF model.<span class="Apple-converted-space">  </span>If ages are present but the simulation uses a WF model, an error will result; the WF model does not use age information.<span class="Apple-converted-space">  </span>If ages are not present but the simulation uses a nonWF model, an error will also result; the nonWF model requires age information.</span></p>
741741
<p class="p6"><span class="s3">As of SLiM 3.3, this method will restore the nucleotides of nucleotide-based mutations, and will restore the ancestral nucleotide sequence, if that information is present in the output file.<span class="Apple-converted-space">  </span>Loading an output file that contains nucleotide information in a non-nucleotide-based model, and <i>vice versa</i>, will produce an error.</span></p>
742742
<p class="p6">As of SLiM 3.5, this method will read and restore the pedigree IDs of individuals and genomes if that information is present in the output file (as requested with <span class="s1">outputFull(pedigreeIDs=T)</span>) <i>and</i> if SLiM’s optional pedigree tracking has been enabled with <span class="s1">initializeSLiMOptions(keepPedigrees=T)</span>.</p>
743-
<p class="p6">This method can also be used to read tree-sequence (<span class="s1">.trees</span>) files saved by <span class="s1">treeSeqOutput()</span> or generated by the Python <span class="s1">pyslim</span> package.<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 fourth population in the input 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.<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>.<span class="Apple-converted-space">  </span>Although this 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 subpopMap 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.</p>
743+
<p class="p6">This method can also be used to read tree-sequence (<span class="s1">.trees</span>) files saved by <span class="s1">treeSeqOutput()</span> or generated by the Python <span class="s1">pyslim</span> package.<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>
744744
<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 read incorrectly, running a Debug build of SLiM enables crosschecks after every load.</p>
745745
<p class="p3">– (void)recalculateFitness([Ni$ tick = NULL])</p>
746746
<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>

SLiMgui/SLiMHelpClasses.rtf

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5975,7 +5975,8 @@ As of SLiM 3.3, this method will restore the nucleotides of nucleotide-based mut
59755975
\f4\i0 if SLiM\'92s optional pedigree tracking has been enabled with
59765976
\f3\fs18 initializeSLiMOptions(keepPedigrees=T)
59775977
\f4\fs20 .\
5978-
This method can also be used to read tree-sequence (
5978+
\pard\pardeftab397\li547\ri720\sb60\sa60\partightenfactor0
5979+
\cf2 This method can also be used to read tree-sequence (
59795980
\f3\fs18 .trees
59805981
\f4\fs20 ) files saved by
59815982
\f3\fs18 treeSeqOutput()
@@ -5991,15 +5992,17 @@ This method can also be used to read tree-sequence (
59915992
\f3\fs18 "p2"
59925993
\f4\fs20 ), and the values should be indexes of populations in the input tree sequence; a key/value pair of
59935994
\f3\fs18 "p2", 4
5994-
\f4\fs20 would mean that the fourth population in the input should become
5995+
\f4\fs20 would mean that the fifth population in the input (the one at zero-based index
5996+
\f3\fs18 4
5997+
\f4\fs20 ) should become
59955998
\f3\fs18 p2
59965999
\f4\fs20 on loading into SLiM. If
59976000
\f3\fs18 subpopMap
59986001
\f4\fs20 is non-
59996002
\f3\fs18 NULL
60006003
\f4\fs20 ,
60016004
\f1\i all
6002-
\f4\i0 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. For instance, suppose we have a tree sequence in which population
6005+
\f4\i0 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. For instance, suppose we have a tree sequence in which population
60036006
\f3\fs18 0
60046007
\f4\fs20 is unused, population
60056008
\f3\fs18 1
@@ -6011,21 +6014,30 @@ This method can also be used to read tree-sequence (
60116014
\f3\fs18 Dictionary("p0", 2, "p1", 1, "p2", 0)
60126015
\f4\fs20 for
60136016
\f3\fs18 subpopMap
6014-
\f4\fs20 . Although this cannot be used to remove populations in the tree sequence, note that it may
6017+
\f4\fs20 , or we could leave out slot
6018+
\f3\fs18 0
6019+
\f4\fs20 since it is unused, with
6020+
\f3\fs18 Dictionary("p0", 2, "p1", 1)
6021+
\f4\fs20 . Although this facility cannot be used to remove populations in the tree sequence, note that it may
60156022
\f1\i add
60166023
\f4\i0 populations that will be visible when
60176024
\f3\fs18 treeSeqOutput()
60186025
\f4\fs20 is called (although these will not be SLiM populations); if, in this example, we had used
60196026
\f3\fs18 Dictionary("p0", 0, "p1", 1, "p5", 2)
60206027
\f4\fs20 and then we wrote the result out with
60216028
\f3\fs18 treeSeqOutput()
6022-
\f4\fs20 , the resulting tree sequence would have six populations, although three of them would be empty and would not be used by SLiM. The use of subpopMap makes it easier to load simulation data that was generated in Python, since that typically uses an id of
6029+
\f4\fs20 , the resulting tree sequence would have six populations, although three of them would be empty and would not be used by SLiM. The use of
6030+
\f3\fs18 subpopMap
6031+
\f4\fs20 makes it easier to load simulation data that was generated in Python, since that typically uses an id of
60236032
\f3\fs18 0
60246033
\f4\fs20 . The
60256034
\f3\fs18 subpopMap
6026-
\f4\fs20 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.\
6027-
\pard\pardeftab397\li547\ri720\sb60\sa60\partightenfactor0
6028-
\cf2 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. When running a Release build of SLiM, however, this crosscheck will only occur the first time that
6035+
\f4\fs20 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. Note the
6036+
\f3\fs18 tskit
6037+
\f4\fs20 command-line interface can be used, like
6038+
\f3\fs18 python3 -m tskit populations file.trees
6039+
\f4\fs20 , to find out the number of subpopulations in a tree-sequence file and their IDs.\
6040+
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. When running a Release build of SLiM, however, this crosscheck will only occur the first time that
60296041
\f3\fs18 readFromPopulationFile()
60306042
\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 read incorrectly, running a Debug build of SLiM enables crosschecks after every load.\
60316043
\pard\pardeftab397\li720\fi-446\ri720\sb180\sa60\partightenfactor0

core/species.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6779,6 +6779,24 @@ void Species::__RemapSubpopulationIDs(SUBPOP_REMAP_HASH &p_subpop_map, int p_fil
67796779
tsk_population_table_t &pop_table = tables_.populations;
67806780
tsk_size_t pop_count = pop_table.num_rows;
67816781

6782+
// Start by checking that no remap entry references a population table index that is out of range
6783+
if (pop_count == 0)
6784+
EIDOS_TERMINATION << "ERROR (Species::__RemapSubpopulationIDs): the population table is empty, and therefore cannot be remapped." << EidosTerminate(nullptr);
6785+
6786+
for (auto &remap_entry : p_subpop_map)
6787+
{
6788+
int64_t table_index = remap_entry.first;
6789+
//slim_objectid_t remapped_index = remap_entry.second;
6790+
6791+
//std::cout << "index " << table_index << " being remapped to " << remapped_index << std::endl;
6792+
6793+
if (table_index < 0)
6794+
EIDOS_TERMINATION << "ERROR (Species::__RemapSubpopulationIDs): (internal error) index " << table_index << " is out of range (less than zero)." << EidosTerminate(nullptr);
6795+
if (table_index >= (int64_t)pop_count)
6796+
EIDOS_TERMINATION << "ERROR (Species::__RemapSubpopulationIDs): index " << table_index << " is out of range (last valid index " << ((int64_t)pop_count - 1) << ")." << EidosTerminate(nullptr);
6797+
}
6798+
6799+
// OK, population table indices are in range; check the population table entry remappings one by one
67826800
for (tsk_size_t pop_index = 0; pop_index < pop_count; pop_index++)
67836801
{
67846802
// validate and parse metadata

0 commit comments

Comments
 (0)