Skip to content

Commit 0ab81aa

Browse files
committed
recombination() callbacks can now have a chromosome specifier
1 parent 75dfa2d commit 0ab81aa

17 files changed

+205
-57
lines changed

QtSLiM/QtSLiMScriptTextEdit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ void QtSLiMTextEdit::updateStatusFieldFromSelection(void)
989989
else if (callName == "recombination")
990990
{
991991
static EidosCallSignature_CSP callbackSig = nullptr;
992-
if (!callbackSig) callbackSig = EidosCallSignature_CSP((new EidosFunctionSignature("recombination", nullptr, kEidosValueMaskLogical | kEidosValueMaskSingleton))->AddObject_OS("subpop", gSLiM_Subpopulation_Class, gStaticEidosValueNULLInvisible));
992+
if (!callbackSig) callbackSig = EidosCallSignature_CSP((new EidosFunctionSignature("recombination", nullptr, kEidosValueMaskLogical | kEidosValueMaskSingleton))->AddObject_OS("subpop", gSLiM_Subpopulation_Class, gStaticEidosValueNULLInvisible)->AddIntString_OSN("chromosome", gStaticEidosValueNULLInvisible));
993993
signature = callbackSig;
994994
}
995995
else if (callName == "survival")

QtSLiM/help/SLiMHelpCallbacks.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@
189189
<p class="p1"><i>5.13.6<span class="Apple-converted-space">  </span>ITEM: 7. </i><span class="s1"><i>recombination()</i></span><i> callbacks</i></p>
190190
<p class="p2">Typically, a simulation sets up a recombination map at the beginning of the run with <span class="s1">initializeRecombinationRate()</span>, and that map is used for the duration of the run.<span class="Apple-converted-space">  </span>Less commonly, the recombination map is changed dynamically from tick to tick, with <span class="s1">Chromosome</span>’s method <span class="s1">setRecombinationRate()</span>; but still, a single recombination map applies for all individuals of a species in a given tick.<span class="Apple-converted-space">  </span>However, in unusual circumstances a simulation may need to modify the way that recombination works on an individual basis; for this, the <span class="s1">recombination()</span> callback mechanism is provided.<span class="Apple-converted-space">  </span>This can be useful for models involving chromosomal inversions that prevent recombination within a region for some individuals, for example, or for models of the evolution of recombination.</p>
191191
<p class="p2">A <span class="s1">recombination()</span> callback is defined with a syntax much like that of other callbacks:</p>
192-
<p class="p3">[id] [t1 [: t2]] recombination([&lt;subpop-id&gt;]) { ... }</p>
193-
<p class="p2">The <span class="s1">recombination()</span> callback will be called during the generation of every gamete during the tick(s) in which it is active.<span class="Apple-converted-space">  </span>It may optionally be restricted to apply only to gametes generated by parents in a specified subpopulation, using the <span class="s1">&lt;subpop-id&gt;</span> specifier.<span class="Apple-converted-space">  </span>(In multispecies models, the definition must be preceded by a <span class="s1">species</span> specification as usual.)</p>
192+
<p class="p3">[id] [t1 [: t2]] recombination([&lt;subpop-id&gt; [, &lt;chromosome-id&gt;]]) { ... }</p>
193+
<p class="p2">The <span class="s1">recombination()</span> callback will be called during the generation of every gamete during the tick(s) in which it is active.<span class="Apple-converted-space">  </span>It may optionally be restricted to apply only to gametes generated by parents in a specified subpopulation, using the <span class="s1">&lt;subpop-id&gt;</span> specifier.<span class="Apple-converted-space">  </span>In addition, in multi-chromosome models it may optionally be restricted to apply only to a specified chromosome, using the <span class="s1">&lt;chromosome-id&gt;</span> specifier, which may be either the <span class="s1">id</span> or the <span class="s1">symbol</span> of a chromosome defined in the species.<span class="Apple-converted-space">  </span>(In multispecies models, the definition must be preceded by a <span class="s1">species</span> specification as usual.)</p>
194194
<p class="p2">When a <span class="s1">recombination()</span> callback is called, a parent has already been chosen to generate a gamete, and candidate recombination breakpoints for use in recombining the parental haplosomes have been drawn.<span class="Apple-converted-space">  </span>The relevant haplosomes of the focal parent are provided to the callback, as is the focal parent itself (as an <span class="s1">Individual</span> object) and the subpopulation in which it resides.<span class="Apple-converted-space">  </span>Furthermore, the proposed breakpoints are provided to the callback.<span class="Apple-converted-space">  </span>The callback may modify these breakpoints in order to change the breakpoints used, in which case it must return <span class="s1">T</span> to indicate that changes were made, or it may leave the proposed breakpoints unmodified, in which case it must return <span class="s1">F</span>.<span class="Apple-converted-space">  </span>(The behavior of SLiM is undefined if the callback returns the wrong <span class="s1">logical</span> value.)</p>
195195
<p class="p2">In addition to the standard SLiM globals, then, a <span class="s1">recombination()</span> callback is supplied with additional information passed through “pseudo-parameters”:</p>
196196
<p class="p9"><span class="s1">individual</span><span class="Apple-tab-span"> </span>The focal parent that is generating a gamete</p>

QtSLiM/help/SLiMHelpClasses.html

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

SLiMgui/SLiMHelpCallbacks.rtf

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -922,14 +922,20 @@ A
922922
\f2\fs22 callback is defined with a syntax much like that of other callbacks:\
923923
\pard\tx990\tx1260\tx1530\tx1800\tx2070\tx2340\tx2610\tx2880\tx3150\tx3420\pardeftab720\li547\ri720\sb180\sa180\partightenfactor0
924924

925-
\f3\fs18 \cf2 [id] [t1 [: t2]] recombination([<subpop-id>]) \{ ... \}\
925+
\f3\fs18 \cf2 [id] [t1 [: t2]] recombination([<subpop-id> [, <chromosome-id>]]) \{ ... \}\
926926
\pard\pardeftab397\fi274\ri720\sb40\sa40\partightenfactor0
927927

928928
\f2\fs22 \cf2 The
929929
\f3\fs18 recombination()
930930
\f2\fs22 callback will be called during the generation of every gamete during the tick(s) in which it is active. It may optionally be restricted to apply only to gametes generated by parents in a specified subpopulation, using the
931931
\f3\fs18 <subpop-id>
932-
\f2\fs22 specifier. (In multispecies models, the definition must be preceded by a
932+
\f2\fs22 specifier. In addition, in multi-chromosome models it may optionally be restricted to apply only to a specified chromosome, using the
933+
\f3\fs18 <chromosome-id>
934+
\f2\fs22 specifier, which may be either the
935+
\f3\fs18 id
936+
\f2\fs22 or the
937+
\f3\fs18 symbol
938+
\f2\fs22 of a chromosome defined in the species. (In multispecies models, the definition must be preceded by a
933939
\f3\fs18 species
934940
\f2\fs22 specification as usual.)\
935941
When a

SLiMgui/SLiMHelpClasses.rtf

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9056,16 +9056,16 @@ After this call, the fitness values used for all purposes in SLiM will be the ne
90569056

90579057
\f3\fs18 \cf0 \'96\'a0(object<SLiMEidosBlock>$)registerRecombinationCallback(Nis$\'a0id, string$\'a0source, [Nio<Subpopulation>$\'a0subpop
90589058
\f5 \'a0
9059-
\f3 =\'a0NULL], [Ni$\'a0start\'a0=\'a0NULL], [Ni$\'a0end\'a0=\'a0NULL])\
9059+
\f3 =\'a0NULL], [Ni$\'a0start\'a0=\'a0NULL], [Ni$\'a0end\'a0=\'a0NULL]\cf2 , [Niso<Chromosome>$\'a0chromosome\'a0=\'a0NULL]\cf0 )\
90609060
\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0
90619061

9062-
\f4\fs20 \cf0 Register a block of Eidos source code, represented as the
9062+
\f4\fs20 \cf2 Register a block of Eidos source code, represented as the
90639063
\f3\fs18 string
90649064
\f4\fs20 singleton
90659065
\f3\fs18 source
90669066
\f4\fs20 , as an Eidos
90679067
\f3\fs18 recombination()
9068-
\f4\fs20 callback in the current simulation\cf2 (specific to the target species)\cf0 , with optional subpopulation
9068+
\f4\fs20 callback in the current simulation (specific to the target species), with optional subpopulation
90699069
\f3\fs18 subpop
90709070
\f4\fs20 (which may be an
90719071
\f3\fs18 integer
@@ -9075,7 +9075,19 @@ After this call, the fitness values used for all purposes in SLiM will be the ne
90759075
\f3\fs18 start
90769076
\f4\fs20 and
90779077
\f3\fs18 end
9078-
\f4\fs20 ticks all limiting its applicability. The script block will be given identifier
9078+
\f4\fs20 ticks all limiting its applicability. In multi-chromosome models, parameter
9079+
\f3\fs18 chromosome
9080+
\f4\fs20 , if non-
9081+
\f3\fs18 NULL
9082+
\f4\fs20 , may specify a chromosome to which the callback will apply (as either an
9083+
\f3\fs18 integer
9084+
\f4\fs20 id, a
9085+
\f3\fs18 string
9086+
\f4\fs20 symbol, or a
9087+
\f3\fs18 Chromosome
9088+
\f4\fs20 object); otherwise,
9089+
\f3\fs18 NULL
9090+
\f4\fs20 indicates that the callback applies to all chromosomes. The script block will be given identifier
90799091
\f3\fs18 id
90809092
\f4\fs20 (specified as an
90819093
\f3\fs18 integer
@@ -9089,10 +9101,9 @@ After this call, the fitness values used for all purposes in SLiM will be the ne
90899101
\f3\fs18 SLiMEidosBlock
90909102
\f4\fs20 objects, and is active immediately; it
90919103
\f1\i may
9092-
\f4\i0 be eligible to execute in the current tick. The new
9104+
\f4\i0 be eligible to execute in the current tick (see section 26.11 for details). The new
90939105
\f3\fs18 SLiMEidosBlock
9094-
\f4\fs20 will be defined as a global variable immediately by this method, and will also be returned by this method.
9095-
\f5 \
9106+
\f4\fs20 will be defined as a global variable immediately by this method (see section 25.13), and will also be returned by this method.\
90969107
\pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0
90979108

90989109
\f3\fs18 \cf0 \'96\'a0(object<SLiMEidosBlock>$)registerReproductionCallback(Nis$\'a0id, string$\'a0source, [Nio<Subpopulation>$\'a0subpop
@@ -9272,7 +9283,9 @@ Register a block of Eidos source code, represented as the
92729283
\f3\fs18 NULL
92739284
\f4\fs20 , may specify a required value for the
92749285
\f3\fs18 id
9275-
\f4\fs20 property of the mutations to be returned. Parameter chromosome, if non-
9286+
\f4\fs20 property of the mutations to be returned. Parameter
9287+
\f3\fs18 chromosome
9288+
\f4\fs20 , if non-
92769289
\f3\fs18 NULL
92779290
\f4\fs20 , may specify a chromosome with which the mutations returned must be associated (as either an
92789291
\f3\fs18 integer

SLiMgui/SLiMWindowController.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4223,7 +4223,7 @@ - (void)updateStatusFieldFromSelection
42234223
static EidosCallSignature_CSP callbackSig = nullptr;
42244224

42254225
if (!callbackSig)
4226-
callbackSig = EidosCallSignature_CSP((new EidosFunctionSignature("recombination", nullptr, kEidosValueMaskLogical | kEidosValueMaskSingleton))->AddObject_OS("subpop", gSLiM_Subpopulation_Class, gStaticEidosValueNULLInvisible));
4226+
callbackSig = EidosCallSignature_CSP((new EidosFunctionSignature("recombination", nullptr, kEidosValueMaskLogical | kEidosValueMaskSingleton))->AddObject_OS("subpop", gSLiM_Subpopulation_Class, gStaticEidosValueNULLInvisible)->AddIntString_OSN("chromosome", gStaticEidosValueNULLInvisible));
42274227

42284228
sig = callbackSig.get();
42294229
}

VERSIONS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ development head (in the master branch):
107107
update calcHeterozygosity() for multi-chromosome support (but it assesses heterozygosity for only a single chromosome, for now)
108108
new PAR (pseudo-autosomal region) recipe for section 15.23, replacing the old recipe in section 14.5 (which will be moved to SLiM-Extras for posterity)
109109
new recipe 9.11 (Ne estimation) with a new multichrom-aware version of function estimateNe_Heterozygosity() that requires the chromosome to be supplied
110+
recombination() callbacks can now be optionally declared with a chromosome specifier (id or symbol), to apply to just one chromosome
111+
registerRecombinationCallback() now takes an optional chromosome parameter to specify that focal chromosome
110112

111113

112114
version 4.3 (Eidos version 3.3):

core/chromosome.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1709,7 +1709,7 @@ void Chromosome::DrawBreakpoints(Individual *p_parent, Haplosome *p_haplosome1,
17091709
{
17101710
parent_sex = p_parent->sex_;
17111711
parent_subpop = p_parent->subpopulation_;
1712-
recombination_callbacks = species_.CallbackBlocksMatching(community_.Tick(), SLiMEidosBlockType::SLiMEidosRecombinationCallback, -1, -1, parent_subpop->subpopulation_id_);
1712+
recombination_callbacks = species_.CallbackBlocksMatching(community_.Tick(), SLiMEidosBlockType::SLiMEidosRecombinationCallback, -1, -1, parent_subpop->subpopulation_id_, id_);
17131713

17141714
// SPECIES CONSISTENCY CHECK
17151715
if (&p_parent->subpopulation_->species_ != &species_)

core/community.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ void Community::ValidateScriptBlockCaches(void)
557557
}
558558
}
559559

560-
std::vector<SLiMEidosBlock*> Community::ScriptBlocksMatching(slim_tick_t p_tick, SLiMEidosBlockType p_event_type, slim_objectid_t p_mutation_type_id, slim_objectid_t p_interaction_type_id, slim_objectid_t p_subpopulation_id, Species *p_species)
560+
std::vector<SLiMEidosBlock*> Community::ScriptBlocksMatching(slim_tick_t p_tick, SLiMEidosBlockType p_event_type, slim_objectid_t p_mutation_type_id, slim_objectid_t p_interaction_type_id, slim_objectid_t p_subpopulation_id, int64_t p_chromosome_id, Species *p_species)
561561
{
562562
if (!script_block_types_cached_)
563563
ValidateScriptBlockCaches();
@@ -639,6 +639,15 @@ std::vector<SLiMEidosBlock*> Community::ScriptBlocksMatching(slim_tick_t p_tick,
639639
continue;
640640
}
641641

642+
// check that the chromosome id matches, if requested
643+
if (p_chromosome_id != -1)
644+
{
645+
int64_t chromosome_id = script_block->chromosome_id_;
646+
647+
if ((chromosome_id != -1) && (p_chromosome_id != chromosome_id))
648+
continue;
649+
}
650+
642651
// check that the species matches; this check is always on, nullptr means check that the species is nullptr
643652
if (p_species != script_block->species_spec_)
644653
continue;
@@ -1032,6 +1041,9 @@ void Community::AddScriptBlock(SLiMEidosBlock *p_script_block, EidosInterpreter
10321041
if (p_script_block->subpopulation_id_ != -1)
10331042
EIDOS_TERMINATION << "ERROR (Community::AddScriptBlock): (internal error) script block for a non-callback or initialize() callback has subpopulation_id_ set." << EidosTerminate(p_error_token);
10341043

1044+
if (p_script_block->chromosome_id_ != -1)
1045+
EIDOS_TERMINATION << "ERROR (Community::AddScriptBlock): (internal error) script block for a non-callback or initialize() callback has chromosome_id_ set." << EidosTerminate(p_error_token);
1046+
10351047
if (p_script_block->interaction_type_id_ != -1)
10361048
EIDOS_TERMINATION << "ERROR (Community::AddScriptBlock): (internal error) script block for a non-callback or initialize() callback has interaction_type_id_ set." << EidosTerminate(p_error_token);
10371049

@@ -2143,7 +2155,7 @@ void Community::AllSpecies_RunInitializeCallbacks(void)
21432155
// The zero tick is handled here by shared code, since it is the same for WF and nonWF models
21442156

21452157
// execute user-defined function blocks first; no need to profile this, it's just the definitions not the executions
2146-
std::vector<SLiMEidosBlock*> function_blocks = ScriptBlocksMatching(-1, SLiMEidosBlockType::SLiMEidosUserDefinedFunction, -1, -1, -1, nullptr);
2158+
std::vector<SLiMEidosBlock*> function_blocks = ScriptBlocksMatching(-1, SLiMEidosBlockType::SLiMEidosUserDefinedFunction, -1, -1, -1, -1, nullptr);
21472159

21482160
for (auto script_block : function_blocks)
21492161
ExecuteFunctionDefinitionBlock(script_block);
@@ -2232,7 +2244,7 @@ void Community::RunInitializeCallbacks(void)
22322244
num_modeltype_declarations_ = 0;
22332245

22342246
// execute `species all` initialize() callbacks, which should always have a tick of 0 set
2235-
std::vector<SLiMEidosBlock*> init_blocks = ScriptBlocksMatching(0, SLiMEidosBlockType::SLiMEidosInitializeCallback, -1, -1, -1, nullptr);
2247+
std::vector<SLiMEidosBlock*> init_blocks = ScriptBlocksMatching(0, SLiMEidosBlockType::SLiMEidosInitializeCallback, -1, -1, -1, -1, nullptr);
22362248

22372249
for (auto script_block : init_blocks)
22382250
ExecuteEidosEvent(script_block);
@@ -2470,7 +2482,7 @@ bool Community::_RunOneTickWF(void)
24702482
#endif
24712483

24722484
cycle_stage_ = SLiMCycleStage::kWFStage0ExecuteFirstScripts;
2473-
std::vector<SLiMEidosBlock*> first_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventFirst, -1, -1, -1, nullptr);
2485+
std::vector<SLiMEidosBlock*> first_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventFirst, -1, -1, -1, -1, nullptr);
24742486

24752487
for (auto script_block : first_blocks)
24762488
ExecuteEidosEvent(script_block);
@@ -2498,7 +2510,7 @@ bool Community::_RunOneTickWF(void)
24982510
#endif
24992511

25002512
cycle_stage_ = SLiMCycleStage::kWFStage1ExecuteEarlyScripts;
2501-
std::vector<SLiMEidosBlock*> early_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventEarly, -1, -1, -1, nullptr);
2513+
std::vector<SLiMEidosBlock*> early_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventEarly, -1, -1, -1, -1, nullptr);
25022514

25032515
for (auto script_block : early_blocks)
25042516
ExecuteEidosEvent(script_block);
@@ -2647,7 +2659,7 @@ bool Community::_RunOneTickWF(void)
26472659
#endif
26482660

26492661
cycle_stage_ = SLiMCycleStage::kWFStage5ExecuteLateScripts;
2650-
std::vector<SLiMEidosBlock*> late_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventLate, -1, -1, -1, nullptr);
2662+
std::vector<SLiMEidosBlock*> late_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventLate, -1, -1, -1, -1, nullptr);
26512663

26522664
for (auto script_block : late_blocks)
26532665
ExecuteEidosEvent(script_block);
@@ -2806,7 +2818,7 @@ bool Community::_RunOneTickNonWF(void)
28062818
#endif
28072819

28082820
cycle_stage_ = SLiMCycleStage::kNonWFStage0ExecuteFirstScripts;
2809-
std::vector<SLiMEidosBlock*> first_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventFirst, -1, -1, -1, nullptr);
2821+
std::vector<SLiMEidosBlock*> first_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventFirst, -1, -1, -1, -1, nullptr);
28102822

28112823
for (auto script_block : first_blocks)
28122824
ExecuteEidosEvent(script_block);
@@ -2934,7 +2946,7 @@ bool Community::_RunOneTickNonWF(void)
29342946
#endif
29352947

29362948
cycle_stage_ = SLiMCycleStage::kNonWFStage2ExecuteEarlyScripts;
2937-
std::vector<SLiMEidosBlock*> early_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventEarly, -1, -1, -1, nullptr);
2949+
std::vector<SLiMEidosBlock*> early_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventEarly, -1, -1, -1, -1, nullptr);
29382950

29392951
for (auto script_block : early_blocks)
29402952
ExecuteEidosEvent(script_block);
@@ -3079,7 +3091,7 @@ bool Community::_RunOneTickNonWF(void)
30793091
#endif
30803092

30813093
cycle_stage_ = SLiMCycleStage::kNonWFStage6ExecuteLateScripts;
3082-
std::vector<SLiMEidosBlock*> late_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventLate, -1, -1, -1, nullptr);
3094+
std::vector<SLiMEidosBlock*> late_blocks = ScriptBlocksMatching(tick_, SLiMEidosBlockType::SLiMEidosEventLate, -1, -1, -1, -1, nullptr);
30833095

30843096
for (auto script_block : late_blocks)
30853097
ExecuteEidosEvent(script_block);

core/community.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ class Community : public EidosDictionaryUnretained
205205

206206
// Managing script blocks; these two methods should be used as a matched pair, bracketing each cycle stage that calls out to script
207207
void ValidateScriptBlockCaches(void);
208-
std::vector<SLiMEidosBlock*> ScriptBlocksMatching(slim_tick_t p_tick, SLiMEidosBlockType p_event_type, slim_objectid_t p_mutation_type_id, slim_objectid_t p_interaction_type_id, slim_objectid_t p_subpopulation_id, Species *p_species);
208+
std::vector<SLiMEidosBlock*> ScriptBlocksMatching(slim_tick_t p_tick, SLiMEidosBlockType p_event_type, slim_objectid_t p_mutation_type_id, slim_objectid_t p_interaction_type_id, slim_objectid_t p_subpopulation_id, int64_t p_chromosome_id, Species *p_species);
209209
std::vector<SLiMEidosBlock*> &AllScriptBlocks();
210210
std::vector<SLiMEidosBlock*> AllScriptBlocksForSpecies(Species *p_species);
211211
void OptimizeScriptBlock(SLiMEidosBlock *p_script_block);

0 commit comments

Comments
 (0)