From a37c435394848061f8079d91866986a87db8f317 Mon Sep 17 00:00:00 2001 From: Ben Haller Date: Sat, 23 Nov 2024 17:02:49 -0500 Subject: [PATCH] fix mutrun experiments for multichrom --- QtSLiM/help/SLiMHelpFunctions.html | 6 +- SLiMgui/SLiMHelpFunctions.rtf | 55 +++-- VERSIONS | 3 +- core/chromosome.cpp | 92 +++----- core/chromosome.h | 47 ++++- core/community_eidos.cpp | 2 +- core/population.cpp | 329 ++++++++++++++++++++++------- core/population.h | 4 +- core/slim_globals.h | 3 + core/slim_test_core.cpp | 1 - core/species.cpp | 76 ++++--- core/species.h | 8 + core/species_eidos.cpp | 26 ++- core/subpopulation.cpp | 242 +++++++++++++++------ core/subpopulation.h | 11 +- eidos/eidos_call_signature.cpp | 4 + eidos/eidos_interpreter.cpp | 4 + 17 files changed, 638 insertions(+), 275 deletions(-) diff --git a/QtSLiM/help/SLiMHelpFunctions.html b/QtSLiM/help/SLiMHelpFunctions.html index 7c110a59..14a5bfd3 100644 --- a/QtSLiM/help/SLiMHelpFunctions.html +++ b/QtSLiM/help/SLiMHelpFunctions.html @@ -60,7 +60,7 @@

Finally, some additional values of type are supported for backward compatibility (not intended for use in new models):

"H-" (haploid-null), specifying a haploid autosomal chromosome that is inherited only clonally (and will produce an error if a biparental cross ever occurs in the model), followed by a null haplosome.  This mirrors how haploid models were constructed in SLiM prior to SLiM 5, for backward compatibility.  This is somewhat similar to type "H" except that it keeps a null chromosome in the second slot.

"-Y" (null-Y) specifying a Y chromosome that is haploid (–Y) in males, absent (– –) in females; note that in males a null haplosome is present as the first haplosome, and in females two null haplosomes are present.  This mirrors how models of the Y were constructed in SLiM prior to SLiM 5, and how initializeSex("Y") single-chromosome models still work for backward compatibility.  This is the same as type "Y" except that it keeps a null haplosome in the first slot for all individuals.

-

Finally, the mutationRuns parameter specifies how many mutation runs the chromosome should use.  If mutationRuns is not 0, SLiM will use the value given as the number of mutation runs inside Haplosome objects for the chromosome; if it is 0 (the default), SLiM will calculate a number of mutation runs that it estimates will work well. Internally, SLiM divides haplosomes into a sequence of consecutive mutation runs, allowing more efficient internal computations. The optimal mutation run length is short enough that each mutation run is relatively unlikely to be modified by mutation/recombination events when inherited, but long enough that each mutation run is likely to contain a relatively large number of mutations; these priorities are in tension, so an intermediate balance between them is generally desirable. The optimal number of mutation runs will depend upon the machine and even the compiler used to build SLiM, so SLiM’s default value may not be optimal; for maximal performance it can thus be beneficial to experiment with different values and find the optimal value for the simulation – a process which SLiM can assist with (see section 21.4). Specifying the number of mutation runs is an advanced technique, but in some cases it can improve performance significantly.

+

The mutationRuns parameter specifies how many mutation runs the chromosome should use.  Internally, SLiM divides haplosomes into a sequence of consecutive mutation runs, allowing more efficient internal computations.  The optimal mutation run length is short enough that each mutation run is relatively unlikely to be modified by mutation/recombination events when inherited, but long enough that each mutation run is likely to contain a relatively large number of mutations; these priorities are in tension, so an intermediate balance between them is generally optimal.  The optimal number of mutation runs will depend on the model’s details, and may also depend upon the machine and even the compiler used to build SLiM.  If the mutationRuns parameter is not 0, SLiM will use the value given as the number of mutation runs inside Haplosome objects for the chromosome.  If mutationRuns is 0 (the default), then the behavior depends upon a parameter to the initializeSLiMOptions() function, doMutationRunExperiments.  If that flag is F, the behavior here is as if mutationRuns=1 had been passed: one mutation run will be used, and mutation run experiments will not be conducted.  If that flag is T (the default), then for mutationRuns=0 SLiM will conduct experiments at runtime, using different mutation run counts, to try to determine the number of mutation runs that produces the best performance.  The value that SLiM’s experiments determine may not be optimal, however, and in any case there is some overhead associated with conducting these experiments; for maximal performance it can thus be beneficial to determine the true optimal value for the simulation yourself, and set it explicitly using this parameter. Specifying the number of mutation runs is an advanced technique, but in some cases it can improve performance significantly.

The order in which initializeChromosome() calls are made is generally unimportant, since the chromosomes assort independently of each other anyway, but SLiM will preserve the order in which they were defined for you (for the chromosomes property of Species, for display in SLiMgui, for writing out to VCF, and so forth).  All of the above types of chromosomes can be defined any number of times; you can have any number of autosomal chromosomes, for example.  In a sexual model you could even have multiple defined X and Y chromosomes – not in the sense of a female being XX, but in the sense of a female being X1X1X2X2, where X1 and X2 are two different kinds of X chromosome.  Similarly, you could define both an X and a Z for a species, if you wish; each would segregate correctly according to the sex of the offspring.  In sexual models in SLiM the sex of an offspring is determined randomly or given by the user in script; it is not a function of the sex chromosomes present in the individual, although the sex chromosomes present in the individual will correlate with sex.  In other words, SLiM does not know and does not care what sex-determination system the species is using; the chromosomes follow the sex, rather than the sex following the chromosomes.  This should allow any sex-determination system to be modeled, even if it is unusual, non-genetic, etc.

(void)initializeGeneConversion(numeric$ nonCrossoverFraction, numeric$ meanLength, numeric$ simpleConversionFraction, [numeric$ bias = 0], [logical$ redrawLengthsOnFailure = F])

Calling this function switches the recombination model from a “simple crossover” model to a “double-stranded break (DSB)” model, and configures the details of the gene conversion tracts that will therefore be modeled.  The fraction of DSBs that will be modeled as non-crossover events is given by nonCrossoverFraction.  The mean length of gene conversion tracts (whether associated with crossover or non-crossover events) is given by meanLength; the actual extent of a gene conversion tract will be the sum of two independent draws from a geometric distribution with mean meanLength/2.  The fraction of gene conversion tracts that are modeled as “simple” is given by simpleConversionFraction; the remainder will be modeled as “complex”, involving repair of heteroduplex mismatches.  Finally, the GC bias during heteroduplex mismatch repair is given by bias, with the default of 0.0 indicating no bias, 1.0 indicating an absolute preference for G/C mutations over A/T mutations, and -1.0 indicating an absolute preference for A/T mutations over G/C mutations.  A non-zero bias may only be set in nucleotide-based models.  This function, and the way that gene conversion is modeled, fundamentally changed in SLiM 3.3.

@@ -122,12 +122,12 @@

(void)initializeSLiMModelType(string$ modelType)

Configure the type of SLiM model used for the simulation.  At present, one of two model types may be selected.  If modelType is "WF", SLiM will use a Wright-Fisher (WF) model; this is the model type that has always been supported by SLiM, and is the model type used if initializeSLiMModelType() is not called.  If modelType is "nonWF", SLiM will use a non-Wright-Fisher (nonWF) model instead; this is a new model type supported by SLiM 3.0 and above.

If initializeSLiMModelType() is called at all then it must be called before any other initialization function, so that SLiM knows from the outset which features are enabled and which are not.

-

(void)initializeSLiMOptions([logical$ keepPedigrees = F], [string$ dimensionality = ""], [string$ periodicity = ""], [integer$ mutationRuns = 0], [logical$ preventIncidentalSelfing = F], [logical$ nucleotideBased = F], [logical$ randomizeCallbacks = T])

+

(void)initializeSLiMOptions([logical$ keepPedigrees = F], [string$ dimensionality = ""], [string$ periodicity = ""], [logical$ doMutationRunExperiments = T], [logical$ preventIncidentalSelfing = F], [logical$ nucleotideBased = F], [logical$ randomizeCallbacks = T])

Configure options for the simulation.  If initializeSLiMOptions() is called at all then it must be called before any other initialization function (except initializeSLiMModelType()), so that SLiM knows from the outset which optional features are enabled and which are not.

If keepPedigrees is T, SLiM will keep pedigree information for every individual in the simulation, tracking the identity of its parents and grandparents.  This allows individuals to assess their degree of pedigree-based relatedness to other individuals (see Individual’s relatedness() and sharedParentCount() methods), as well as allowing a model to find “trios” (two parents and an offspring they generated) using the pedigree properties of Individual.  As a side effect of keepPedigrees being T, the pedigreeID, pedigreeParentIDs, and pedigreeGrandparentIDs properties of Individual will have defined values, as will the haplosomePedigreeID property of Haplosome.  Note that pedigree-based relatedness doesn’t necessarily correspond to genetic relatedness, due to effects such as assortment and recombination.  Beginning in SLiM 3.5, keepPedigrees=T also enables tracking of individual reproductive output, available through the reproductiveOutput property of Individual and the lifetimeReproductiveOutput property of Subpopulation.

If dimensionality is not "", SLiM will enable its optional “continuous space” facility.  Three values for dimensionality are presently supported: "x", "xy", and "xyz", specifying that continuous space should be enabled for one, two, or three dimensions, respectively, using (x), (x, y), and (x, y, z) coordinates respectively.  This has a number of side effects.  First of all, it means that the specified properties of Individual (x, y, and/or z) will be interpreted by SLiM as spatial positions; in particular, SLiMgui will use those properties to display subpopulations spatially.  Second, it allows spatial interactions to be defined, evaluated, and queried using initializeInteractionType() and interaction() callbacks.  And third, it enables the use of any other properties and methods related to continuous space, such as setting the spatial boundaries of subpopulations, which would otherwise raise an error.

If periodicity is not "", SLiM will designate the specified spatial dimensions as being periodic – wrapping around at the edges of the spatial boundaries of that dimension.  This option may only be used if the dimensionality parameter to initializeSLiMOptions() has been used to enable spatiality in the model, and only spatial dimensions that were specified in the dimensionality of the model may be declared to be periodic (but if desired, it is permissible to make just a subset of those dimensions periodic; it is not an all-or-none proposition).  For example, if the specified dimensionality is "xy", the model’s periodicity may be "x", "y", or "xy" (or "", the default, to specify that there are no periodic dimensions).  A one-dimensional periodic model would model a space like the perimeter of a circle.  A two-dimensional model periodic in one of those dimensions would model a space like a cylinder without its end caps; if periodic in both dimensions, the modeled space is a torus.  The shapes of three-dimensional periodic models are harder to visualize, but are essentially higher-dimensional analogues of these concepts.  Periodic boundary conditions are commonly used to model spatial scenarios without “edge effects”, since there are no edges in the periodic spatial dimensions.  The pointPeriodic() method of Subpopulation is typically used in conjunction with this option, to actually implement the periodic boundary condition for the specified dimensions.

-

The mutationRuns parameter has been deprecated.  In SLiM 5 and later, use the mutationRuns parameter to initializeChromosome() instead.

+

The doMutationRunExperiments parameter specifies whether SLiM should attempt to conduct experiments at runtime to determine the optimal number of mutation runs used in the model.  This is a performance optimization.  If doMutationRunExperiments is T (the default), this optimization is enabled for all chromosomes that do not have an explicitly specified mutation run count; this is generally desirable and may significantly improve performance.  If doMutationRunExperiments is F, this optimization is disabled and chromosomes that do not have an explicitly specified mutation run count will simply use a single mutation run.  See the documentation for initializeChromosome() for further discussion.  Note that this parameter used to be [integer$ mutationRuns = 0], specifying the mutation run count directly.  That parameter has been moved to initializeChromosome(), allowing a different mutation run count to be specified for each chromosome in multi-chromosome models.

If preventIncidentalSelfing is T, incidental selfing in hermaphroditic models will be prevented by SLiM.  By default (i.e., if preventIncidentalSelfing is F), SLiM chooses the first and second parents in a biparental mating event independently.  It is therefore possible for the same individual to be chosen as both the first and second parent, resulting in selfing events even when the selfing rate is zero.  In many models this is unimportant, since it happens fairly infrequently and does not have large consequences.  This behavior is SLiM’s default because it is the simplest option, and produces results that most closely align with simple analytical population genetics models.  However, in some models this selfing can be undesirable and problematic.  In particular, models that involve very high variance in fitness or very small effective population sizes may see elevated rates of selfing that substantially influence model results.  If preventIncidentalSelfing is set to T, all such incidental selfing will be prevented (by choosing a new second parent if the first parent was chosen again).  Non-incidental selfing, as requested by the selfing rate, will still be permitted.  Note that if incidental selfing is prevented, SLiM will hang if it is unable to find a different second parent; there must always be at least two individuals in the population with non-zero fitness, and mateChoice() and modifyChild() callbacks must not absolutely prevent those two individuals from producing viable offspring.  Enforcement of the prohibition on incidental selfing will occur after mateChoice() callbacks have been called (and thus the default mating weights provided to mateChoice() callbacks will not exclude the first parent!), but will occur before modifyChild() callbacks are called (so those callbacks may assume that the first and second parents are distinct).

If nucleotideBased is T, the model will be nucleotide-based.  In this case, auto-generated mutations (i.e., mutation types used by genomic element types) must be nucleotide-based, and an ancestral nucleotide sequence must be supplied with initializeAncestralNucleotides().  Non-nucleotide-based mutations may still be used, but may not be referenced by genomic element types.  A mutation rate (or rate map) may not be supplied with initializeMutationRate(); instead, a hotspot map may (optionally) be supplied with initializeHotspotMap().  This choice has many consequences across SLiM. 

If randomizeCallbacks is T (the default), the order in which individuals are processed in callbacks will be randomized to make it easier to avoid order-dependency bugs.  This flag exists because the order of individuals in each subpopulation is non-random; most notably, females always come before males in the individuals vector, but non-random ordering may also occur with respect to things like migrant versus non-migrant status, origin by selfing versus cloning versus biparental mating, and other factors.  When this option is F, individuals in a subpopulation are processed in the order of the individuals vector in each tick cycle stage, which may lead to order-dependency issues if there is an enabled callback whose behavior is not fully independent between calls.  Setting this option to T will cause individuals within each subpopulation to be processed in a randomized order in each tick cycle stage; specifically, this randomizes the order of calls to mutationEffect() callbacks in both WF and nonWF models, and calls to reproduction() and survival() callbacks in nonWF models.  Each subpopulation is still processed separately, in sequential order, so order-dependency issues between subpopulations are still possible if callbacks have effects that are not fully independent.  This feature was added in SLiM 4, breaking backward compatibility; to recover the behavior of previous versions of SLiM, pass F for this option (but then be very careful about order-dependency issues in your script).  The default of T is the safe option, but a small speed penalty is incurred by the randomization of the processing order – for most models the difference will be less than 1%, but in the worst case it may approach 10%.  Models that do not have any order-dependency issue may therefore run somewhat faster if this is set to F.  Note that anywhere that your script uses the individuals property of Subpopulation, the order of individuals returned will be non-random (regardless of the setting of this option); you should use sample() to shuffle the order of the individuals vector if necessary to avoid order-dependency issues in your script.

diff --git a/SLiMgui/SLiMHelpFunctions.rtf b/SLiMgui/SLiMHelpFunctions.rtf index 91a2578f..91b637a1 100644 --- a/SLiMgui/SLiMHelpFunctions.rtf +++ b/SLiMgui/SLiMHelpFunctions.rtf @@ -216,17 +216,31 @@ The \f1\fs18 "Y" \f2\fs20 except that it keeps a null haplosome in the first slot for all individuals.\ \pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 -\cf2 Finally, the +\cf2 The \f1\fs18 mutationRuns -\f2\fs20 parameter specifies how many mutation runs the chromosome should use. If +\f2\fs20 parameter specifies how many mutation runs the chromosome should use. Internally, SLiM divides haplosomes into a sequence of consecutive mutation runs, allowing more efficient internal computations. The optimal mutation run length is short enough that each mutation run is relatively unlikely to be modified by mutation/recombination events when inherited, but long enough that each mutation run is likely to contain a relatively large number of mutations; these priorities are in tension, so an intermediate balance between them is generally optimal. The optimal number of mutation runs will depend on the model\'92s details, and may also depend upon the machine and even the compiler used to build SLiM. If the \f1\fs18 mutationRuns -\f2\fs20 is not +\f2\fs20 parameter is not \f1\fs18 0 \f2\fs20 , SLiM will use the value given as the number of mutation runs inside \f1\fs18 Haplosome -\f2\fs20 objects for the chromosome; if it is +\f2\fs20 objects for the chromosome. If +\f1\fs18 mutationRuns +\f2\fs20 is \f1\fs18 0 -\f2\fs20 (the default), SLiM will calculate a number of mutation runs that it estimates will work well. Internally, SLiM divides haplosomes into a sequence of consecutive mutation runs, allowing more efficient internal computations. The optimal mutation run length is short enough that each mutation run is relatively unlikely to be modified by mutation/recombination events when inherited, but long enough that each mutation run is likely to contain a relatively large number of mutations; these priorities are in tension, so an intermediate balance between them is generally desirable. The optimal number of mutation runs will depend upon the machine and even the compiler used to build SLiM, so SLiM\'92s default value may not be optimal; for maximal performance it can thus be beneficial to experiment with different values and find the optimal value for the simulation \'96 a process which SLiM can assist with (see section 21.4). Specifying the number of mutation runs is an advanced technique, but in some cases it can improve performance significantly.\ +\f2\fs20 (the default), then the behavior depends upon a parameter to the +\f1\fs18 initializeSLiMOptions() +\f2\fs20 function, +\f1\fs18 doMutationRunExperiments +\f2\fs20 . If that flag is +\f1\fs18 F +\f2\fs20 , the behavior here is as if +\f1\fs18 mutationRuns=1 +\f2\fs20 had been passed: one mutation run will be used, and mutation run experiments will not be conducted. If that flag is +\f1\fs18 T +\f2\fs20 (the default), then for +\f1\fs18 mutationRuns=0 +\f2\fs20 SLiM will conduct experiments at runtime, using different mutation run counts, to try to determine the number of mutation runs that produces the best performance. The value that SLiM\'92s experiments determine may not be optimal, however, and in any case there is some overhead associated with conducting these experiments; for maximal performance it can thus be beneficial to determine the true optimal value for the simulation yourself, and set it explicitly using this parameter. Specifying the number of mutation runs is an advanced technique, but in some cases it can improve performance significantly.\ The order in which \f1\fs18 initializeChromosome() \f2\fs20 calls are made is generally unimportant, since the chromosomes assort independently of each other anyway, but SLiM will preserve the order in which they were defined for you (for the @@ -1026,7 +1040,7 @@ If \f2\fs20 is called at all then it must be called before any other initialization function, so that SLiM knows from the outset which features are enabled and which are not.\ \pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0 -\f1\fs18 \cf0 \kerning1\expnd0\expndtw0 (void)initializeSLiMOptions([logical$\'a0keepPedigrees\'a0=\'a0F], [string$\'a0dimensionality\'a0=\'a0""], [string$\'a0periodicity\'a0=\'a0""], [integer$\'a0mutationRuns\'a0=\'a00], [logical$\'a0preventIncidentalSelfing\'a0=\'a0F]\cf2 \expnd0\expndtw0\kerning0 +\f1\fs18 \cf0 \kerning1\expnd0\expndtw0 (void)initializeSLiMOptions([logical$\'a0keepPedigrees\'a0=\'a0F], [string$\'a0dimensionality\'a0=\'a0""], [string$\'a0periodicity\'a0=\'a0""], [logical$\'a0doMutationRunExperiments\'a0=\'a0T], [logical$\'a0preventIncidentalSelfing\'a0=\'a0F]\cf2 \expnd0\expndtw0\kerning0 , [logical$\'a0nucleotideBased\'a0=\'a0F], [logical$\'a0randomizeCallbacks\'a0=\'a0T]\cf0 \kerning1\expnd0\expndtw0 ) \f4 \ \pard\pardeftab397\li547\ri720\sb60\sa60\partightenfactor0 @@ -1140,18 +1154,27 @@ If \f2\fs20 method of \f1\fs18 Subpopulation \f2\fs20 is typically used in conjunction with this option, to actually implement the periodic boundary condition for the specified dimensions.\ -\pard\pardeftab397\li547\ri720\sb60\sa60\partightenfactor0 - -\f0\b \cf2 \kerning1\expnd0\expndtw0 The -\f5\fs18 mutationRuns -\f0\fs20 parameter has been deprecated. -\f2\b0 In SLiM 5 and later, use the -\f1\fs18 mutationRuns -\f2\fs20 parameter to +\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 +\cf2 \kerning1\expnd0\expndtw0 The +\f1\fs18 doMutationRunExperiments +\f2\fs20 parameter specifies whether SLiM should attempt to conduct experiments at runtime to determine the optimal number of mutation runs used in the model. This is a performance optimization. If +\f1\fs18 doMutationRunExperiments +\f2\fs20 is +\f1\fs18 T +\f2\fs20 (the default), this optimization is enabled for all chromosomes that do not have an explicitly specified mutation run count; this is generally desirable and may significantly improve performance. If +\f1\fs18 doMutationRunExperiments +\f2\fs20 is +\f1\fs18 F +\f2\fs20 , this optimization is disabled and chromosomes that do not have an explicitly specified mutation run count will simply use a single mutation run. See the documentation for +\f1\fs18 initializeChromosome() +\f2\fs20 for further discussion. Note that this parameter used to be +\f1\fs18 [integer$\'a0mutationRuns\'a0=\'a00] +\f2\fs20 , specifying the mutation run count directly. That parameter has been moved to \f1\fs18 initializeChromosome() -\f2\fs20 instead.\ +\f2\fs20 , allowing a different mutation run count to be specified for each chromosome in multi-chromosome models.\expnd0\expndtw0\kerning0 +\ \pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 -\cf0 If +\cf0 \kerning1\expnd0\expndtw0 If \f1\fs18 preventIncidentalSelfing \f2\fs20 is \f1\fs18 T diff --git a/VERSIONS b/VERSIONS index 88c465c2..a9c17070 100644 --- a/VERSIONS +++ b/VERSIONS @@ -63,7 +63,6 @@ development head (in the master branch): add autofixes in SLiMgui for the above API changes revise recipes for new terminology (but they will require more revision downstream) fix calc...() functions for new terminology (but they will require more revision downstream) - policy change: mutationRuns= for initializeSLiMOptions() has been deprecated and will error if used; pass that option to initializeChromosome() instead policy change: the old "haplosome type" (A/X/Y) in the genome metadata is now a chromosome type (different) in the haplosome metadata policy change: similarly, haplosome type A/X/Y in SLiM output (e.g., outputFull()) is now a chromosome type (different) policy change: initialization order is a bit stricter; initializeSex() with "X" or "Y" must come earlier, before calls that would define an implicit "A" chromosome @@ -72,6 +71,8 @@ development head (in the master branch): policy change: the 1D SFS graph and haplotype plot no longer depend upon the current chromosome range selection; that was weird, and doesn't work well in multichrom policy change: the "remove fixed mutations" stage of the WF tick cycle is now after "offspring become parents" (no user-visible difference except WF tick cycle diagram) add sexChromosomes property to Species + improved the accuracy of mutation run experiments, and the quality of the output generated by them, and extended them to be per-chromosome + policy change: mutationRuns= for initializeSLiMOptions() has been changed to [l$ doMutationRunExperiments=T]; pass mutationRuns= to initializeChromosome() instead version 4.3 (Eidos version 3.3): diff --git a/core/chromosome.cpp b/core/chromosome.cpp index e58036eb..317340f8 100644 --- a/core/chromosome.cpp +++ b/core/chromosome.cpp @@ -90,6 +90,11 @@ Chromosome::Chromosome(Species &p_species, ChromosomeType p_type, int64_t p_id, using_DSB_model_(false), non_crossover_fraction_(0.0), gene_conversion_avg_length_(0.0), gene_conversion_inv_half_length_(0.0), simple_conversion_fraction_(0.0), mismatch_repair_bias_(0.0), last_position_mutrun_(0) { + // If the user has said "no mutation run experiments" in initializeSLiMOptions(), then a count of zero + // supplied here is interpreted as a count of 1, and experiments will thus not be conducted. + if (!species_.UserWantsMutrunExperiments() && (preferred_mutrun_count_ == 0)) + preferred_mutrun_count_ = 1; + // Set up the default color for fixed mutations in SLiMgui color_sub_ = "#3333FF"; if (!color_sub_.empty()) @@ -217,7 +222,7 @@ void Chromosome::CreateNucleotideMutationRateMap(void) double rate = max_nucleotide_mut_rate * multiplier_M; if (rate > 1.0) - EIDOS_TERMINATION << "ERROR (Species::CreateNucleotideMutationRateMap): the maximum mutation rate in nucleotide-based models is 1.0." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Chromosome::CreateNucleotideMutationRateMap): the maximum mutation rate in nucleotide-based models is 1.0." << EidosTerminate(); mut_rates_M.emplace_back(rate); } @@ -226,7 +231,7 @@ void Chromosome::CreateNucleotideMutationRateMap(void) double rate = max_nucleotide_mut_rate * multiplier_F; if (rate > 1.0) - EIDOS_TERMINATION << "ERROR (Species::CreateNucleotideMutationRateMap): the maximum mutation rate in nucleotide-based models is 1.0." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Chromosome::CreateNucleotideMutationRateMap): the maximum mutation rate in nucleotide-based models is 1.0." << EidosTerminate(); mut_rates_F.emplace_back(rate); } @@ -242,7 +247,7 @@ void Chromosome::CreateNucleotideMutationRateMap(void) double rate = max_nucleotide_mut_rate * multiplier_H; if (rate > 1.0) - EIDOS_TERMINATION << "ERROR (Species::CreateNucleotideMutationRateMap): the maximum mutation rate in nucleotide-based models is 1.0." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Chromosome::CreateNucleotideMutationRateMap): the maximum mutation rate in nucleotide-based models is 1.0." << EidosTerminate(); mut_rates_H.emplace_back(rate); } @@ -253,7 +258,7 @@ void Chromosome::CreateNucleotideMutationRateMap(void) { // No hotspot map specified at all; use a rate of 1.0 across the chromosome with an inferred length if (max_nucleotide_mut_rate > 1.0) - EIDOS_TERMINATION << "ERROR (Species::CreateNucleotideMutationRateMap): the maximum mutation rate in nucleotide-based models is 1.0." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Chromosome::CreateNucleotideMutationRateMap): the maximum mutation rate in nucleotide-based models is 1.0." << EidosTerminate(); mut_rates_H.emplace_back(max_nucleotide_mut_rate); //mut_positions_H.emplace_back(?); // deferred; patched in Chromosome::InitializeDraws(). @@ -1718,7 +1723,7 @@ void Chromosome::SetUpMutationRunContexts(void) // Make per-thread MutationRunContexts; the number of threads that we set up for here is NOT gEidosMaxThreads, // but rather, the "base" number of mutation runs per haplosome chosen by Chromosome. The chromosome is divided // into that many chunks along its length (or a multiple thereof), and there is one thread per "base" chunk. - mutation_run_context_COUNT_ = chromosome_->mutrun_count_base_; + mutation_run_context_COUNT_ = mutrun_count_base_; mutation_run_context_PERTHREAD.resize(mutation_run_context_COUNT_); if (mutation_run_context_COUNT_ > 0) @@ -1825,6 +1830,9 @@ void Chromosome::InitiateMutationRunExperiments(void) } x_experiments_enabled_ = true; + species_.DoingMutrunExperimentsForChromosome(); + + x_experiment_count_ = 0; x_current_mutcount_ = mutrun_count_; x_current_runtimes_ = (double *)malloc(SLIM_MUTRUN_EXPERIMENT_LENGTH * sizeof(double)); @@ -1835,7 +1843,7 @@ void Chromosome::InitiateMutationRunExperiments(void) x_previous_buflen_ = 0; if (!x_current_runtimes_ || !x_previous_runtimes_) - EIDOS_TERMINATION << "ERROR (Species::InitiateMutationRunExperiments): allocation failed; you may need to raise the memory limit for SLiM." << EidosTerminate(nullptr); + EIDOS_TERMINATION << "ERROR (Chromosome::InitiateMutationRunExperiments): allocation failed; you may need to raise the memory limit for SLiM." << EidosTerminate(nullptr); x_continuing_trend_ = false; @@ -1872,37 +1880,10 @@ void Chromosome::ZeroMutationRunExperimentClock(void) x_total_gen_clocks_ = 0; } - } -} - -void Chromosome::StartMutationRunExperimentClock(void) -{ - // Mutation run experiment timing macros. We use these to accumulate clocks taken in critical sections of the code. - // Note that this design does NOT include time taken in first()/early()/late() events; since script blocks can do very - // different work from one cycle to the next, this seems best, although it does mean that the impact of the number - // of mutation runs on the execution time of Eidos events is not measured. - if (x_experiments_enabled_) - { - if (x_clock_running_) - std::cerr << "WARNING: mutation run experiment clock was started when already running!"; - x_clock_running_ = true; - x_current_clock_ = std::clock(); - } -} - -void Chromosome::StopMutationRunExperimentClock(void) -{ - if (x_experiments_enabled_) - { - std::clock_t end_clock = std::clock(); - - if (!x_clock_running_) - std::cerr << "WARNING: mutation run experiment clock was stopped when not running!"; - - x_clock_running_ = false; - x_total_gen_clocks_ += (end_clock - x_current_clock_); - x_current_clock_ = 0; +#if MUTRUN_EXPERIMENT_TIMING_OUTPUT + std::cout << "tick " << community_.Tick() << ", chromosome " << id_ << ": starting timing" << std::endl; +#endif } } @@ -1910,7 +1891,11 @@ void Chromosome::FinishMutationRunExperimentTiming(void) { if (x_experiments_enabled_) { - MaintainMutationRunExperiments(x_total_gen_clocks_ / (double)CLOCKS_PER_SEC); +#if MUTRUN_EXPERIMENT_TIMING_OUTPUT + std::cout << "tick " << community_.Tick() << ", chromosome " << id_ << ": ending timing with total count == " << x_total_gen_clocks_ << " (" << Eidos_ElapsedProfileTime(x_total_gen_clocks_) << " seconds)" << std::endl; +#endif + + MaintainMutationRunExperiments(Eidos_ElapsedProfileTime(x_total_gen_clocks_)); x_total_gen_clocks_ = 0; } } @@ -1981,7 +1966,7 @@ void Chromosome::MaintainMutationRunExperiments(double p_last_gen_runtime) { // Log the last cycle time into our buffer if (x_current_buflen_ >= SLIM_MUTRUN_EXPERIMENT_LENGTH) - EIDOS_TERMINATION << "ERROR (Species::MaintainMutationRunExperiments): Buffer overrun, failure to reset after completion of an experiment." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Chromosome::MaintainMutationRunExperiments): Buffer overrun, failure to reset after completion of an experiment." << EidosTerminate(); x_current_runtimes_[x_current_buflen_] = p_last_gen_runtime; @@ -2000,6 +1985,7 @@ void Chromosome::MaintainMutationRunExperiments(double p_last_gen_runtime) // and the experiment mean is worse than the baseline mean (if it is better, we want to continue collecting), // let's short-circuit the rest of the experiment and bail – like early termination of a medical trial. p = Eidos_TTest_TwoSampleWelch(x_current_runtimes_, x_current_buflen_, x_previous_runtimes_, x_previous_buflen_, ¤t_mean, &previous_mean); + x_experiment_count_++; if ((p < 0.01) && (current_mean > previous_mean)) { @@ -2070,6 +2056,7 @@ void Chromosome::MaintainMutationRunExperiments(double p_last_gen_runtime) // Otherwise, get a result from a t-test and decide what to do p = Eidos_TTest_TwoSampleWelch(x_current_runtimes_, x_current_buflen_, x_previous_runtimes_, x_previous_buflen_, ¤t_mean, &previous_mean); + x_experiment_count_++; early_ttest_passed: @@ -2155,7 +2142,7 @@ void Chromosome::MaintainMutationRunExperiments(double p_last_gen_runtime) #endif int32_t trend_next = (x_current_mutcount_ < x_previous_mutcount_) ? (x_current_mutcount_ / 2) : (x_current_mutcount_ * 2); - int32_t trend_limit = (x_current_mutcount_ < x_previous_mutcount_) ? mutrun_count_base_ : SLIM_MUTRUN_MAXIMUM_COUNT; // for single-threaded, chromosome_->mutrun_count_base_ == 1 + int32_t trend_limit = (x_current_mutcount_ < x_previous_mutcount_) ? mutrun_count_base_ : SLIM_MUTRUN_MAXIMUM_COUNT; // for single-threaded, mutrun_count_base_ == 1 if ((current_mean < previous_mean) || (!means_different_05 && (x_current_mutcount_ < x_previous_mutcount_))) { @@ -2298,7 +2285,7 @@ void Chromosome::MaintainMutationRunExperiments(double p_last_gen_runtime) #endif if (x_current_mutcount_ > SLIM_MUTRUN_MAXIMUM_COUNT) - EIDOS_TERMINATION << "ERROR (Species::MaintainMutationRunExperiments): (internal error) splitting mutation runs to beyond SLIM_MUTRUN_MAXIMUM_COUNT (x_current_mutcount_ == " << x_current_mutcount_ << ")." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Chromosome::MaintainMutationRunExperiments): (internal error) splitting mutation runs to beyond SLIM_MUTRUN_MAXIMUM_COUNT (x_current_mutcount_ == " << x_current_mutcount_ << ")." << EidosTerminate(); // We are splitting existing runs in two, so make a map from old mutrun index to new pair of // mutrun indices; every time we encounter the same old index we will substitute the same pair. @@ -2311,7 +2298,7 @@ void Chromosome::MaintainMutationRunExperiments(double p_last_gen_runtime) #if MUTRUN_EXPERIMENT_OUTPUT if (SLiM_verbosity_level >= 2) - SLIM_OUTSTREAM << "// ++ Splitting to achieve new mutation run count of " << chromosome_->mutrun_count_ << " took " << ((std::clock() - start_clock) / (double)CLOCKS_PER_SEC) << " seconds" << std::endl; + SLIM_OUTSTREAM << "// ++ Splitting to achieve new mutation run count of " << mutrun_count_ << " took " << ((std::clock() - start_clock) / (double)CLOCKS_PER_SEC) << " seconds" << std::endl; #endif } @@ -2322,7 +2309,7 @@ void Chromosome::MaintainMutationRunExperiments(double p_last_gen_runtime) #endif if (mutrun_count_multiplier_ % 2 != 0) - EIDOS_TERMINATION << "ERROR (Species::MaintainMutationRunExperiments): (internal error) joining mutation runs to beyond mutrun_count_base_ (mutrun_count_base_ == " << mutrun_count_base_ << ", x_current_mutcount_ == " << x_current_mutcount_ << ")." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Chromosome::MaintainMutationRunExperiments): (internal error) joining mutation runs to beyond mutrun_count_base_ (mutrun_count_base_ == " << mutrun_count_base_ << ", x_current_mutcount_ == " << x_current_mutcount_ << ")." << EidosTerminate(); // We are joining existing runs together, so make a map from old mutrun index pairs to a new // index; every time we encounter the same pair of indices we will substitute the same index. @@ -2335,12 +2322,12 @@ void Chromosome::MaintainMutationRunExperiments(double p_last_gen_runtime) #if MUTRUN_EXPERIMENT_OUTPUT if (SLiM_verbosity_level >= 2) - SLIM_OUTSTREAM << "// ++ Joining to achieve new mutation run count of " << chromosome_->mutrun_count_ << " took " << ((std::clock() - start_clock) / (double)CLOCKS_PER_SEC) << " seconds" << std::endl; + SLIM_OUTSTREAM << "// ++ Joining to achieve new mutation run count of " << mutrun_count_ << " took " << ((std::clock() - start_clock) / (double)CLOCKS_PER_SEC) << " seconds" << std::endl; #endif } if (mutrun_count_ != x_current_mutcount_) - EIDOS_TERMINATION << "ERROR (Species::MaintainMutationRunExperiments): Failed to transition to new mutation run count" << x_current_mutcount_ << "." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Chromosome::MaintainMutationRunExperiments): Failed to transition to new mutation run count" << x_current_mutcount_ << "." << EidosTerminate(); } } @@ -2348,10 +2335,10 @@ void Chromosome::PrintMutationRunExperimentSummary(void) { #if MUTRUN_EXPERIMENT_OUTPUT // Print a full mutation run count history if MUTRUN_EXPERIMENT_OUTPUT is enabled - if ((SLiM_verbosity_level >= 2) && x_experiments_enabled_) + if (x_experiments_enabled_) { SLIM_OUTSTREAM << std::endl; - SLIM_OUTSTREAM << "// Mutrun count history:" << std::endl; + SLIM_OUTSTREAM << "// Chromosome " << id_ << " mutrun count history:" << std::endl; SLIM_OUTSTREAM << "// mutrun_history <- c("; bool first_count = true; @@ -2372,7 +2359,7 @@ void Chromosome::PrintMutationRunExperimentSummary(void) // If verbose output is enabled and we've been running mutation run experiments, // figure out the modal mutation run count and print that, for the user's benefit. - if ((SLiM_verbosity_level >= 2) && x_experiments_enabled_) + if (x_experiments_enabled_) { int modal_index, modal_tally; int power_tallies[20]; // we only go up to 1024 mutruns right now, but this gives us some headroom @@ -2400,16 +2387,7 @@ void Chromosome::PrintMutationRunExperimentSummary(void) int modal_count = (int)round(pow(2.0, modal_index)); double modal_fraction = power_tallies[modal_index] / (double)(x_mutcount_history_.size()); - SLIM_OUTSTREAM << std::endl; - SLIM_OUTSTREAM << "// Mutation run modal count: " << modal_count << " (" << (modal_fraction * 100) << "% of cycles)" << std::endl; - SLIM_OUTSTREAM << "//" << std::endl; - SLIM_OUTSTREAM << "// It might (or might not) speed up your model to add a call to:" << std::endl; - SLIM_OUTSTREAM << "//" << std::endl; - SLIM_OUTSTREAM << "// initializeSLiMOptions(mutationRuns=" << modal_count << ");" << std::endl; - SLIM_OUTSTREAM << "//" << std::endl; - SLIM_OUTSTREAM << "// to your initialize() callback. The optimal value will change" << std::endl; - SLIM_OUTSTREAM << "// if your model changes. See the SLiM manual for more details." << std::endl; - SLIM_OUTSTREAM << std::endl; + SLIM_OUTSTREAM << "// Chromosome " << id_ << ": " << modal_count << " (" << (modal_fraction * 100) << "% of cycles, " << x_experiment_count_ << " experiments)" << std::endl; } } diff --git a/core/chromosome.h b/core/chromosome.h index d61a28b5..7025389d 100644 --- a/core/chromosome.h +++ b/core/chromosome.h @@ -150,6 +150,7 @@ class Chromosome : public EidosDictionaryRetained #define SLIM_MUTRUN_MAXIMUM_COUNT 1024 // the most mutation runs we will ever use; hard to imagine that any model will want more than this bool x_experiments_enabled_; // if false, no experiments are run and no cycle runtimes are recorded + int32_t x_experiment_count_; // a counter of the number of experiments we've run (the number of t-tests we've conducted) int32_t x_current_mutcount_; // the number of mutation runs we're currently using double *x_current_runtimes_; // cycle runtimes recorded at this mutcount (SLIM_MUTRUN_EXPERIMENT_MAXLENGTH length) @@ -169,10 +170,10 @@ class Chromosome : public EidosDictionaryRetained std::vector x_mutcount_history_; // a record of the mutation run count used in each cycle - std::clock_t x_current_clock_ = 0; // the clock for timing current being done + eidos_profile_t x_current_clock_ = 0; // the clock for timing current being done bool x_clock_running_ = false; - std::clock_t x_total_gen_clocks_ = 0; // a counter of clocks accumulated for the current cycle's runtime (across measured code blocks) + eidos_profile_t x_total_gen_clocks_ = 0; // a counter of clocks accumulated for the current cycle's runtime (across measured code blocks) // look at StartMutationRunExperimentClock() usage to see which blocks are measured // Mutation run experiments @@ -388,10 +389,48 @@ class Chromosome : public EidosDictionaryRetained // Mutation run experiments void InitiateMutationRunExperiments(void); void ZeroMutationRunExperimentClock(void); - void StartMutationRunExperimentClock(void); - void StopMutationRunExperimentClock(void); void FinishMutationRunExperimentTiming(void); void PrintMutationRunExperimentSummary(void); + inline __attribute__((always_inline)) bool MutationRunExperimentsEnabled(void) { return x_experiments_enabled_; } + + // Mutation run experiment timing. We use these methods to accumulate clocks taken in critical sections of the code. + // Note that this design does NOT include time taken in first()/early()/late() events; since script blocks can do very + // different work from one cycle to the next, this seems best, although it does mean that the impact of the number + // of mutation runs on the execution time of Eidos events is not measured. + inline __attribute__((always_inline)) void StartMutationRunExperimentClock(void) + { + if (x_experiments_enabled_) + { +#if DEBUG + if (x_clock_running_) + std::cerr << "WARNING: mutation run experiment clock was started when already running!"; +#endif + x_clock_running_ = true; + x_current_clock_ = Eidos_BenchmarkTime(); + } + } + + inline __attribute__((always_inline)) void StopMutationRunExperimentClock(__attribute__((unused)) const char *p_caller_name) + { + if (x_experiments_enabled_) + { + eidos_profile_t end_clock = Eidos_BenchmarkTime(); + +#if DEBUG + if (!x_clock_running_) + std::cerr << "WARNING: mutation run experiment clock was stopped when not running!"; +#endif + +#if MUTRUN_EXPERIMENT_TIMING_OUTPUT + // this log generates an unreasonable amount of output and is not usually desirable + //std::cout << " tick " << community_.Tick() << ", chromosome " << id_ << ": mutrun experiment clock for " << p_caller_name << " count == " << (end_clock - x_current_clock_) << std::endl; +#endif + + x_clock_running_ = false; + x_total_gen_clocks_ += (end_clock - x_current_clock_); + x_current_clock_ = 0; + } + } // diff --git a/core/community_eidos.cpp b/core/community_eidos.cpp index b56c352f..0ab28a57 100644 --- a/core/community_eidos.cpp +++ b/core/community_eidos.cpp @@ -133,7 +133,7 @@ const std::vector *Community::ZeroTickFunctionSignat sim_0_signatures_.emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature(gStr_initializeSex, nullptr, kEidosValueMaskVOID, "SLiM")) ->AddString_OSN("chromosomeType", gStaticEidosValueNULL)); sim_0_signatures_.emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature(gStr_initializeSLiMOptions, nullptr, kEidosValueMaskVOID, "SLiM")) - ->AddLogical_OS("keepPedigrees", gStaticEidosValue_LogicalF)->AddString_OS("dimensionality", gStaticEidosValue_StringEmpty)->AddString_OS("periodicity", gStaticEidosValue_StringEmpty)->AddInt_OS("mutationRuns", gStaticEidosValue_Integer0)->AddLogical_OS("preventIncidentalSelfing", gStaticEidosValue_LogicalF)->AddLogical_OS("nucleotideBased", gStaticEidosValue_LogicalF)->AddLogical_OS("randomizeCallbacks", gStaticEidosValue_LogicalT)); + ->AddLogical_OS("keepPedigrees", gStaticEidosValue_LogicalF)->AddString_OS("dimensionality", gStaticEidosValue_StringEmpty)->AddString_OS("periodicity", gStaticEidosValue_StringEmpty)->AddLogical_OS("doMutationRunExperiments", gStaticEidosValue_LogicalT)->AddLogical_OS("preventIncidentalSelfing", gStaticEidosValue_LogicalF)->AddLogical_OS("nucleotideBased", gStaticEidosValue_LogicalF)->AddLogical_OS("randomizeCallbacks", gStaticEidosValue_LogicalT)); sim_0_signatures_.emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature(gStr_initializeSpecies, nullptr, kEidosValueMaskVOID, "SLiM")) ->AddInt_OS("tickModulo", gStaticEidosValue_Integer1)->AddInt_OS("tickPhase", gStaticEidosValue_Integer1)->AddString_OS(gStr_avatar, gStaticEidosValue_StringEmpty)->AddString_OS("color", gStaticEidosValue_StringEmpty)); sim_0_signatures_.emplace_back((EidosFunctionSignature *)(new EidosFunctionSignature(gStr_initializeTreeSeq, nullptr, kEidosValueMaskVOID, "SLiM")) diff --git a/core/population.cpp b/core/population.cpp index f15b05b1..f7cc441b 100644 --- a/core/population.cpp +++ b/core/population.cpp @@ -1175,140 +1175,284 @@ void Population::EvolveSubpopulation(Subpopulation &p_subpop, bool p_mate_choice bool (Subpopulation::*MungeIndividualSelfed_TEMPLATED)(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); bool (Subpopulation::*MungeIndividualCloned_TEMPLATED)(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); - if (pedigrees_enabled) + if (species_.DoingAnyMutationRunExperiments()) { - if (recording_tree_sequence) + if (pedigrees_enabled) { - if (has_munge_callback) // has any of the callbacks that the Munge...() methods care about; this can be refined later + if (recording_tree_sequence) { - if (is_spatial) + if (has_munge_callback) // has any of the callbacks that the Munge...() methods care about; this can be refined later { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } else { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } } else { - if (is_spatial) + if (has_munge_callback) { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } else { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } } } else { - if (has_munge_callback) + if (recording_tree_sequence) { - if (is_spatial) + if (has_munge_callback) { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } else { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } } else { - if (is_spatial) + if (has_munge_callback) { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } else { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } } } } else { - if (recording_tree_sequence) + if (pedigrees_enabled) { - if (has_munge_callback) + if (recording_tree_sequence) { - if (is_spatial) + if (has_munge_callback) // has any of the callbacks that the Munge...() methods care about; this can be refined later { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } else { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } } else { - if (is_spatial) + if (has_munge_callback) { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } else { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } } } else { - if (has_munge_callback) + if (recording_tree_sequence) { - if (is_spatial) + if (has_munge_callback) { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } else { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } } else { - if (is_spatial) + if (has_munge_callback) { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } else { - MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; - MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; - MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + if (is_spatial) + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } + else + { + MungeIndividualCrossed_TEMPLATED = &Subpopulation::MungeIndividualCrossed; + MungeIndividualSelfed_TEMPLATED = &Subpopulation::MungeIndividualSelfed; + MungeIndividualCloned_TEMPLATED = &Subpopulation::MungeIndividualCloned; + } } } } @@ -2054,7 +2198,7 @@ void Population::EvolveSubpopulation(Subpopulation &p_subpop, bool p_mate_choice #ifdef _OPENMP bool can_parallelize = true; - for (Chromosome *chromosome : species_.Chrpmosomes()) + for (Chromosome *chromosome : species_.Chromosomes()) if (chromosome.using_DSB_model_) { can_parallelize = false; @@ -6653,7 +6797,7 @@ void Population::UniqueMutationRuns(void) void Population::SplitMutationRunsForChromosome(int32_t p_new_mutrun_count, Chromosome *p_chromosome) { // Note this method assumes that mutation run refcounts are correct; we enforce that here - TallyMutationRunReferencesForPopulation(); + TallyMutationRunReferencesForPopulationForChromosome(p_chromosome); int first_haplosome_index = species_.FirstHaplosomeIndices()[p_chromosome->Index()]; int last_haplosome_index = species_.LastHaplosomeIndices()[p_chromosome->Index()]; @@ -6862,7 +7006,7 @@ struct slim_pair_hash { void Population::JoinMutationRunsForChromosome(int32_t p_new_mutrun_count, Chromosome *p_chromosome) { // Note this method assumes that mutation run refcounts are correct; we enforce that here - TallyMutationRunReferencesForPopulation(); + TallyMutationRunReferencesForPopulationForChromosome(p_chromosome); int first_haplosome_index = species_.FirstHaplosomeIndices()[p_chromosome->Index()]; int last_haplosome_index = species_.LastHaplosomeIndices()[p_chromosome->Index()]; @@ -7191,7 +7335,7 @@ void Population::SwapGenerations(void) void Population::TallyMutationRunReferencesForPopulationForChromosome(Chromosome *p_chromosome) { if (child_generation_valid_) - EIDOS_TERMINATION << "ERROR (Population::TallyMutationRunReferencesForPopulation): (internal error) called with child generation active!" << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Population::TallyMutationRunReferencesForPopulationForChromosome): (internal error) called with child generation active!" << EidosTerminate(); slim_refcount_t tallied_haplosome_count = 0; int first_haplosome_index = species_.FirstHaplosomeIndices()[p_chromosome->Index()]; @@ -7200,7 +7344,7 @@ void Population::TallyMutationRunReferencesForPopulationForChromosome(Chromosome int mutrun_context_count = p_chromosome->ChromosomeMutationRunContextCount(); if (mutrun_count_multiplier * mutrun_context_count != p_chromosome->mutrun_count_) - EIDOS_TERMINATION << "ERROR (Population::TallyMutationRunReferencesForPopulation): (internal error) mutation run subdivision is incorrect." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Population::TallyMutationRunReferencesForPopulationForChromosome): (internal error) mutation run subdivision is incorrect." << EidosTerminate(); // THIS PARALLEL REGION CANNOT HAVE AN IF()! IT MUST ALWAYS EXECUTE PARALLEL! // the reduction() is a bit odd - every thread will generate the same value, and we just want that value, @@ -7215,7 +7359,7 @@ void Population::TallyMutationRunReferencesForPopulationForChromosome(Chromosome if (omp_get_num_threads() != mutrun_context_count) { std::cerr << "requested " << mutrun_context_count << " threads but got " << omp_get_num_threads() << std::endl; - THREAD_SAFETY_IN_ANY_PARALLEL("Population::TallyMutationRunReferencesForPopulation(): incorrect thread count!"); + THREAD_SAFETY_IN_ANY_PARALLEL("Population::TallyMutationRunReferencesForPopulationForChromosome(): incorrect thread count!"); } #endif @@ -7351,7 +7495,7 @@ void Population::TallyMutationRunReferencesForPopulationForChromosome(Chromosome } if (tallied_haplosome_count_CHECK != tallied_haplosome_count) - EIDOS_TERMINATION << "ERROR (Population::TallyMutationRunReferencesForPopulation): (internal error) tallied_haplosome_count_CHECK != tallied_haplosome_count (" << tallied_haplosome_count_CHECK << " != " << tallied_haplosome_count << ")." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Population::TallyMutationRunReferencesForPopulationForChromosome): (internal error) tallied_haplosome_count_CHECK != tallied_haplosome_count (" << tallied_haplosome_count_CHECK << " != " << tallied_haplosome_count << ")." << EidosTerminate(); for (int threadnum = 0; threadnum < p_chromosome->ChromosomeMutationRunContextCount(); ++threadnum) { @@ -7364,7 +7508,7 @@ void Population::TallyMutationRunReferencesForPopulationForChromosome(Chromosome const MutationRun *mutrun = inuse_pool[pool_index]; if (mutrun->use_count_CHECK_ != mutrun->use_count()) - EIDOS_TERMINATION << "ERROR (Population::TallyMutationRunReferencesForPopulation): (internal error) use_count_CHECK_ " << mutrun->use_count_CHECK_ << " != mutrun->use_count() " << mutrun->use_count() << "." << EidosTerminate(); + EIDOS_TERMINATION << "ERROR (Population::TallyMutationRunReferencesForPopulationForChromosome): (internal error) use_count_CHECK_ " << mutrun->use_count_CHECK_ << " != mutrun->use_count() " << mutrun->use_count() << "." << EidosTerminate(); } } } @@ -7375,11 +7519,25 @@ void Population::TallyMutationRunReferencesForPopulationForChromosome(Chromosome p_chromosome->tallied_haplosome_count_ = tallied_haplosome_count; } -void Population::TallyMutationRunReferencesForPopulation(void) +void Population::TallyMutationRunReferencesForPopulation(bool p_clock_for_mutrun_experiments) { // Each chromosome is tallied separately, in the present design; this allows parallelization to work differently for each - for (Chromosome *chromosome : species_.Chromosomes()) - TallyMutationRunReferencesForPopulationForChromosome(chromosome); + if (p_clock_for_mutrun_experiments) + { + for (Chromosome *chromosome : species_.Chromosomes()) + { + chromosome->StartMutationRunExperimentClock(); + + TallyMutationRunReferencesForPopulationForChromosome(chromosome); + + chromosome->StopMutationRunExperimentClock("TallyMutationRunReferencesForPopulation()"); + } + } + else + { + for (Chromosome *chromosome : species_.Chromosomes()) + TallyMutationRunReferencesForPopulationForChromosome(chromosome); + } } void Population::TallyMutationRunReferencesForSubpopsForChromosome(std::vector *p_subpops_to_tally, Chromosome *p_chromosome) @@ -7521,10 +7679,11 @@ void Population::TallyMutationRunReferencesForHaplosomes(const Haplosome * const void Population::FreeUnusedMutationRuns(void) { + // It is assumed by this method that mutation run tallies are up to date! + // The caller must ensure that by calling TallyMutationRunReferencesForPopulation()! for (Chromosome *chromosome : species_.Chromosomes()) { - // It is assumed by this method that mutation run tallies are up to date! - // The caller must ensure that by calling TallyMutationRunReferencesForPopulation()! + chromosome->StartMutationRunExperimentClock(); // free all in-use MutationRun objects that are not actually in use (use count == 0) // each thread does its own checking and freeing, for its own MutationRunContext @@ -7561,6 +7720,8 @@ void Population::FreeUnusedMutationRuns(void) } } } + + chromosome->StopMutationRunExperimentClock("FreeUnusedMutationRuns()"); } } @@ -7662,10 +7823,10 @@ void Population::TallyMutationReferencesAcrossPopulation() if (can_tally_using_mutruns) { // FAST PATH: Tally mutation run usage first, and then leverage that to tally mutations - TallyMutationRunReferencesForPopulation(); + TallyMutationRunReferencesForPopulation(/* p_clock_for_mutrun_experiments */ true); // Give the core work to our fast worker method; this zeroes and then tallies - _TallyMutationReferences_FAST_FromMutationRunUsage(); + _TallyMutationReferences_FAST_FromMutationRunUsage(/* p_clock_for_mutrun_experiments */ true); #if DEBUG { @@ -7909,7 +8070,7 @@ void Population::TallyMutationReferencesAcrossSubpopulations(std::vector 1); for (Chromosome *chromosome : species_.Chromosomes()) { + if (p_clock_for_mutrun_experiments) + chromosome->StartMutationRunExperimentClock(); + // each thread does its own tallying, for its own MutationRunContext #ifdef _OPENMP int mutrun_context_count = chromosome->ChromosomeMutationRunContextCount(); @@ -8089,6 +8253,9 @@ void Population::_TallyMutationReferences_FAST_FromMutationRunUsage(void) } } } + + if (p_clock_for_mutrun_experiments) + chromosome->StopMutationRunExperimentClock("_TallyMutationReferences_FAST_FromMutationRunUsage()"); } } diff --git a/core/population.h b/core/population.h index 8ca658a5..84ba0c35 100644 --- a/core/population.h +++ b/core/population.h @@ -265,7 +265,7 @@ class Population // place the number of non-null haplosomes that were tallied across into the tallied_haplosome_count_ value // of each chromosome involved in the tally. void TallyMutationRunReferencesForPopulationForChromosome(Chromosome *p_chromosome); - void TallyMutationRunReferencesForPopulation(void); + void TallyMutationRunReferencesForPopulation(bool p_clock_for_mutrun_experiments); void TallyMutationRunReferencesForSubpopsForChromosome(std::vector *p_subpops_to_tally, Chromosome *p_chromosome); void TallyMutationRunReferencesForSubpops(std::vector *p_subpops_to_tally); void TallyMutationRunReferencesForHaplosomes(const Haplosome * const *haplosomes_ptr, slim_popsize_t haplosomes_count); @@ -287,7 +287,7 @@ class Population void TallyMutationReferencesAcrossHaplosomes(const Haplosome * const *haplosomes, slim_popsize_t haplosomes_count); slim_refcount_t _CountNonNullHaplosomesForChromosome(Chromosome *p_chromosome); - void _TallyMutationReferences_FAST_FromMutationRunUsage(void); + void _TallyMutationReferences_FAST_FromMutationRunUsage(bool p_clock_for_mutrun_experiments); #if DEBUG void _CheckMutationTallyAcrossHaplosomes(const Haplosome * const *haplosomes_ptr, slim_popsize_t haplosomes_count); #endif diff --git a/core/slim_globals.h b/core/slim_globals.h index 8556eb5d..670d6514 100644 --- a/core/slim_globals.h +++ b/core/slim_globals.h @@ -502,6 +502,9 @@ void AccumulateMemoryUsageIntoTotal_Community(SLiMMemoryUsage_Community &p_usage // experiments performed by Species. #define MUTRUN_EXPERIMENT_OUTPUT 0 +// If 1, debug output will be generated for mutation run count experiment timing information +#define MUTRUN_EXPERIMENT_TIMING_OUTPUT 0 + // If 1, the MutationRun pointers inside Haplosome objects will be cleared to nullptr when the haplosome is // freed, or disposed of into a junkyard, or anything like that -- whenever it is no longer in use. This // could be useful for debugging problems with dereferencing stale MutationRun pointers. Otherwise it is diff --git a/core/slim_test_core.cpp b/core/slim_test_core.cpp index a2e51375..cb3bfa28 100644 --- a/core/slim_test_core.cpp +++ b/core/slim_test_core.cpp @@ -237,7 +237,6 @@ void _RunInitTests(void) SLiMAssertScriptStop("initialize() { initializeSLiMOptions(preventIncidentalSelfing=T); stop(); }", __LINE__); SLiMAssertScriptRaise("initialize() { initializeSLiMOptions(keepPedigrees=NULL); stop(); }", "cannot be type NULL", __LINE__); SLiMAssertScriptRaise("initialize() { initializeSLiMOptions(dimensionality=NULL); stop(); }", "cannot be type NULL", __LINE__); - SLiMAssertScriptRaise("initialize() { initializeSLiMOptions(mutationRuns=NULL); stop(); }", "cannot be type NULL", __LINE__); SLiMAssertScriptRaise("initialize() { initializeSLiMOptions(preventIncidentalSelfing=NULL); stop(); }", "cannot be type NULL", __LINE__); SLiMAssertScriptRaise("initialize() { initializeSLiMOptions(dimensionality='foo'); stop(); }", "legal non-empty values", __LINE__); SLiMAssertScriptRaise("initialize() { initializeSLiMOptions(dimensionality='y'); stop(); }", "legal non-empty values", __LINE__); diff --git a/core/species.cpp b/core/species.cpp index ac2f91f7..95c40303 100644 --- a/core/species.cpp +++ b/core/species.cpp @@ -2220,12 +2220,8 @@ void Species::MaintainMutationRegistry(void) { if (has_genetics_) { - TheChromosome().StartMutationRunExperimentClock(); - population_.MaintainMutationRegistry(); - TheChromosome().StopMutationRunExperimentClock(); - // Every hundredth cycle we unique mutation runs to optimize memory usage and efficiency. The number 100 was // picked out of a hat – often enough to perhaps be useful in keeping SLiM slim, but infrequent enough that if it // is a time sink it won't impact the simulation too much. This call is really quite fast, though – on the order @@ -2240,13 +2236,7 @@ void Species::MaintainMutationRegistry(void) void Species::RecalculateFitness(void) { - if (has_genetics_) - TheChromosome().StartMutationRunExperimentClock(); - population_.RecalculateFitness(cycle_); // used to be cycle_ + 1 in the WF cycle; removing that 18 Feb 2016 BCH - - if (has_genetics_) - TheChromosome().StopMutationRunExperimentClock(); } void Species::MaintainTreeSequence(void) @@ -2345,14 +2335,8 @@ void Species::WF_GenerateOffspring(void) if (no_active_callbacks) { - if (has_genetics_) - TheChromosome().StartMutationRunExperimentClock(); - for (std::pair &subpop_pair : population_.subpops_) population_.EvolveSubpopulation(*subpop_pair.second, false, false, false, false, false); - - if (has_genetics_) - TheChromosome().StopMutationRunExperimentClock(); } else { @@ -2408,14 +2392,8 @@ void Species::WF_GenerateOffspring(void) } // then evolve each subpop - if (has_genetics_) - TheChromosome().StartMutationRunExperimentClock(); - for (std::pair &subpop_pair : population_.subpops_) population_.EvolveSubpopulation(*subpop_pair.second, mate_choice_callbacks_present, modify_child_callbacks_present, recombination_callbacks_present, mutation_callbacks_present, type_s_dfes_present_); - - if (has_genetics_) - TheChromosome().StopMutationRunExperimentClock(); } } @@ -2505,15 +2483,9 @@ void Species::nonWF_GenerateOffspring(void) SLiMEidosBlockType old_executing_block_type = community_.executing_block_type_; community_.executing_block_type_ = SLiMEidosBlockType::SLiMEidosReproductionCallback; - if (has_genetics_) - TheChromosome().StartMutationRunExperimentClock(); - for (std::pair &subpop_pair : population_.subpops_) subpop_pair.second->ReproduceSubpopulation(); - if (has_genetics_) - TheChromosome().StopMutationRunExperimentClock(); - community_.executing_block_type_ = old_executing_block_type; // This completes the first half of the reproduction process; see Species::nonWF_MergeOffspring() for the second half @@ -2649,8 +2621,48 @@ void Species::SimulationHasFinished(void) // This is called by Community::SimulationHasFinished() for each species // Print mutation run experiment results - for (Chromosome *chromosome : chromosomes_) - chromosome->PrintMutationRunExperimentSummary(); + if (SLiM_verbosity_level >= 2) + { + int summary_count = 0; + + for (Chromosome *chromosome : chromosomes_) + summary_count += chromosome->MutationRunExperimentsEnabled(); + + if (summary_count > 0) + { + SLIM_OUTSTREAM << std::endl; + + SLIM_OUTSTREAM << "// Mutation run experiment data:" << std::endl; + SLIM_OUTSTREAM << "//" << std::endl; + SLIM_OUTSTREAM << "// For each chromosome that conducted experiments, the optimal" << std::endl; + SLIM_OUTSTREAM << "// mutation run count is given, with the percentage of cycles" << std::endl; + SLIM_OUTSTREAM << "// in which that number was used. The number of mutation run" << std::endl; + SLIM_OUTSTREAM << "// experiments conducted is also given; if that is small (less" << std::endl; + SLIM_OUTSTREAM << "// than 200 or so), or if the percentage of cycles is close to" << std::endl; + SLIM_OUTSTREAM << "// or below 50%, the optimal count may not be accurate, since" << std::endl; + SLIM_OUTSTREAM << "// insufficient data was gathered. In that case, you might" << std::endl; + SLIM_OUTSTREAM << "// wish to conduct your own timing experiments using different" << std::endl; + SLIM_OUTSTREAM << "// counts. Profile output also has more detail on this data." << std::endl; + SLIM_OUTSTREAM << "//" << std::endl; + + for (Chromosome *chromosome : chromosomes_) + chromosome->PrintMutationRunExperimentSummary(); + + SLIM_OUTSTREAM << "//" << std::endl; + SLIM_OUTSTREAM << "// It might (or might not) speed up your model to add:" << std::endl; + SLIM_OUTSTREAM << "//" << std::endl; + SLIM_OUTSTREAM << "// mutationRuns=X" << std::endl; + SLIM_OUTSTREAM << "//" << std::endl; + SLIM_OUTSTREAM << "// to the initializeChromosome() call" << (summary_count > 1 ? "s" : "") << " in your initialize()" << std::endl; + SLIM_OUTSTREAM << "// callback, where X is the optimal count for the chromosome." << std::endl; + SLIM_OUTSTREAM << "// (If your model does not call initializeChromosome(), you" << std::endl; + SLIM_OUTSTREAM << "// would need to add " << (summary_count > 1 ? "those calls" : "that call") << + ".) Optimal " << (summary_count > 1 ? "counts" : "count") << " may change" << std::endl; + SLIM_OUTSTREAM << "// if your model changes, or even if the model is just run on" << std::endl; + SLIM_OUTSTREAM << "// different hardware. See the SLiM manual for more details." << std::endl; + SLIM_OUTSTREAM << std::endl; + } + } } void Species::Species_CheckIntegrity(void) @@ -3295,6 +3307,10 @@ void Species::ReturnShuffleBuffer(void) #if SLIM_USE_NONNEUTRAL_CACHES void Species::CollectMutationProfileInfo(void) { + // FIXME MULTICHROM this profile info should be moved to be per-chromosome; for now, we protect against not having a chromosome + if (chromosomes_.size() == 0) + return; + Chromosome &chromosome = TheChromosome(); // only keeping the history for the first chromosome right now, should keep it for all // maintain our history of the number of mutruns per haplosome and the nonneutral regime diff --git a/core/species.h b/core/species.h index 6db6a53a..c1849192 100644 --- a/core/species.h +++ b/core/species.h @@ -258,6 +258,10 @@ class Species : public EidosDictionaryUnretained // preventing incidental selfing in hermaphroditic models bool prevent_incidental_selfing_ = false; + // mutation run timing experiment configuration + bool do_mutrun_experiments_ = true; // user-level flag in initializeSLiMOptions(); if false, experiments are never run + bool doing_any_mutrun_experiments_ = false; // is any chromosome actually running mutation run timing experiments? + // nucleotide-based models bool nucleotide_based_ = false; @@ -517,6 +521,10 @@ class Species : public EidosDictionaryUnretained if (p_z) *p_z = periodic_z_; } + inline __attribute__((always_inline)) bool UserWantsMutrunExperiments(void) const { return do_mutrun_experiments_; } + inline __attribute__((always_inline)) void DoingMutrunExperimentsForChromosome(void) { doing_any_mutrun_experiments_ = true; } + inline __attribute__((always_inline)) bool DoingAnyMutationRunExperiments(void) const { return doing_any_mutrun_experiments_; } + inline __attribute__((always_inline)) bool IsNucleotideBased(void) const { return nucleotide_based_; } diff --git a/core/species_eidos.cpp b/core/species_eidos.cpp index 1e5d01d4..e52dafd4 100644 --- a/core/species_eidos.cpp +++ b/core/species_eidos.cpp @@ -245,6 +245,9 @@ EidosValue_SP Species::ExecuteContextFunction_initializeChromosome(const std::st if (id < 0) EIDOS_TERMINATION << "ERROR (Species::ExecuteContextFunction_initializeChromosome): initializeChromosome() requires id to be non-negative." << EidosTerminate(); + if (ChromosomeFromID(id)) + EIDOS_TERMINATION << "ERROR (Species::ExecuteContextFunction_initializeChromosome): initializeChromosome() requires id to be unique within the species; two chromosomes in the same species may not have the same id." << EidosTerminate(); + slim_position_t start = SLiMCastToPositionTypeOrRaise(start_value->IntAtIndex_NOCAST(0, nullptr)); slim_position_t length = SLiMCastToPositionTypeOrRaise(length_value->IntAtIndex_NOCAST(0, nullptr)); @@ -275,6 +278,17 @@ EidosValue_SP Species::ExecuteContextFunction_initializeChromosome(const std::st else symbol = std::to_string(id); + if ((symbol.length() == 0) || (symbol.length() > 3)) + { + if (symbol_value->Type() == EidosValueType::kValueString) + EIDOS_TERMINATION << "ERROR (Species::ExecuteContextFunction_initializeChromosome): initializeChromosome() requires symbol to be a string with a length of 1-3 characters." << EidosTerminate(); + else + EIDOS_TERMINATION << "ERROR (Species::ExecuteContextFunction_initializeChromosome): initializeChromosome() requires symbol to be a string with a length of 1-3 characters; since the id given to the chromosome (" << id << ") is more than three digits, a symbol must be supplied explicitly to satisfy this requirement." << EidosTerminate(); + } + + if (ChromosomeFromSymbol(symbol)) + EIDOS_TERMINATION << "ERROR (Species::ExecuteContextFunction_initializeChromosome): initializeChromosome() requires symbol to be unique within the species; two chromosomes in the same species may not have the same symbol." << EidosTerminate(); + std::string name; if (name_value->Type() == EidosValueType::kValueString) @@ -1209,7 +1223,7 @@ EidosValue_SP Species::ExecuteContextFunction_initializeSex(const std::string &p return gStaticEidosValueVOID; } -// ********************* (void)initializeSLiMOptions([logical$ keepPedigrees = F], [string$ dimensionality = ""], [string$ periodicity = ""], [integer$ mutationRuns = 0], [logical$ preventIncidentalSelfing = F], [logical$ nucleotideBased = F], [logical$ randomizeCallbacks = T]) +// ********************* (void)initializeSLiMOptions([logical$ keepPedigrees = F], [string$ dimensionality = ""], [string$ periodicity = ""], [logical$ doMutationRunExperiments = T], [logical$ preventIncidentalSelfing = F], [logical$ nucleotideBased = F], [logical$ randomizeCallbacks = T]) // EidosValue_SP Species::ExecuteContextFunction_initializeSLiMOptions(const std::string &p_function_name, const std::vector &p_arguments, EidosInterpreter &p_interpreter) { @@ -1217,7 +1231,7 @@ EidosValue_SP Species::ExecuteContextFunction_initializeSLiMOptions(const std::s EidosValue *arg_keepPedigrees_value = p_arguments[0].get(); EidosValue *arg_dimensionality_value = p_arguments[1].get(); EidosValue *arg_periodicity_value = p_arguments[2].get(); - EidosValue *arg_mutationRuns_value = p_arguments[3].get(); + EidosValue *arg_doMutationRunExperiments_value = p_arguments[3].get(); EidosValue *arg_preventIncidentalSelfing_value = p_arguments[4].get(); EidosValue *arg_nucleotideBased_value = p_arguments[5].get(); EidosValue *arg_randomizeCallbacks_value = p_arguments[6].get(); @@ -1309,11 +1323,11 @@ EidosValue_SP Species::ExecuteContextFunction_initializeSLiMOptions(const std::s } { - // [integer$ mutationRuns = 0] - int64_t mutrun_count = arg_mutationRuns_value->IntAtIndex_NOCAST(0, nullptr); + // [logical$ doMutationRunExperiments = T] + // note this parameter position used to be [integer$ mutationRuns = 0] instead! + bool do_mutrun_experiments = arg_doMutationRunExperiments_value->LogicalAtIndex_NOCAST(0, nullptr); - if (mutrun_count != 0) - EIDOS_TERMINATION << "ERROR (Species::ExecuteContextFunction_initializeSLiMOptions): the mutationRuns option to initializeSLiMOptions() has been deprecated and no longer functions. Please pass 0 for this option, or simply leave it out; using named parameters to initializeSLiMOptions() is recommended so that you can skip over options you do not wish to specify. The preferred mutation run count is now specified on a per-chromosome basis in initializeChromosome()." << EidosTerminate(); + do_mutrun_experiments_ = do_mutrun_experiments; } { diff --git a/core/subpopulation.cpp b/core/subpopulation.cpp index 8c539920..81f9c1be 100644 --- a/core/subpopulation.cpp +++ b/core/subpopulation.cpp @@ -1565,12 +1565,25 @@ void Subpopulation::UpdateFitness(std::vector &p_mutationEffect // see Population::EvolveSubpopulation() for further comments on this optimization technique double (Subpopulation::*FitnessOfParent_TEMPLATED)(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); - if (!mutationEffect_callbacks_exist) - FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; - else if (single_mutationEffect_callback) - FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; + if (species_.DoingAnyMutationRunExperiments()) + { + // If *any* chromosome is doing mutrun experiments, we can't template them out + if (!mutationEffect_callbacks_exist) + FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; + else if (single_mutationEffect_callback) + FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; + else + FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; + } else - FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; + { + if (!mutationEffect_callbacks_exist) + FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; + else if (single_mutationEffect_callback) + FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; + else + FitnessOfParent_TEMPLATED = &Subpopulation::FitnessOfParent; + } // calculate fitnesses in parent population and cache the values if (sex_enabled_) @@ -1702,7 +1715,7 @@ void Subpopulation::UpdateFitness(std::vector &p_mutationEffect if (fitness > 0.0) { - fitness *= FitnessOfParent(female_index, p_mutationEffect_callbacks); + fitness *= (this->*FitnessOfParent_TEMPLATED)(female_index, p_mutationEffect_callbacks); #ifdef SLIMGUI parent_individuals_[female_index]->cached_unscaled_fitness_ = fitness; @@ -1915,7 +1928,7 @@ void Subpopulation::UpdateFitness(std::vector &p_mutationEffect if (fitness > 0.0) { - fitness *= FitnessOfParent(male_index, p_mutationEffect_callbacks); + fitness *= (this->*FitnessOfParent_TEMPLATED)(male_index, p_mutationEffect_callbacks); #ifdef SLIMGUI parent_individuals_[male_index]->cached_unscaled_fitness_ = fitness; @@ -2144,7 +2157,7 @@ void Subpopulation::UpdateFitness(std::vector &p_mutationEffect if (fitness > 0.0) { - fitness *= FitnessOfParent(individual_index, p_mutationEffect_callbacks); + fitness *= (this->*FitnessOfParent_TEMPLATED)(individual_index, p_mutationEffect_callbacks); #ifdef SLIMGUI parent_individuals_[individual_index]->cached_unscaled_fitness_ = fitness; @@ -2700,7 +2713,7 @@ double Subpopulation::ApplyFitnessEffectCallbacks(std::vector & // hit this case hard and see a speedup of as much as 25%, so the additional complexity seems worth it (since that's // quite a realistic and common case). The only unfortunate thing about this design is that p_mutationEffect_callbacks // has to get passed all the way down, even when we know we won't use it. LTO might optimize that away, with luck. -template +template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks) { // calculate the fitness of the individual at index p_individual_index in the parent population @@ -2711,6 +2724,8 @@ double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::ve for (Chromosome *chromosome : species_.Chromosomes()) { + if (f_mutrunexps) chromosome->StartMutationRunExperimentClock(); + switch (chromosome->Type()) { // diploid, possibly with one or both being null haplosomes @@ -2722,8 +2737,10 @@ double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::ve Haplosome *haplosome2 = individual->haplosomes_[haplosome_index+1]; w *= _Fitness_DiploidChromosome(haplosome1, haplosome2, p_mutationEffect_callbacks); - if (w <= 0.0) + if (w <= 0.0) { + if (f_mutrunexps) chromosome->StopMutationRunExperimentClock("FitnessOfParent()"); return 0.0; + } haplosome_index += 2; break; @@ -2741,8 +2758,10 @@ double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::ve Haplosome *haplosome = individual->haplosomes_[haplosome_index]; w *= _Fitness_HaploidChromosome(haplosome, p_mutationEffect_callbacks); - if (w <= 0.0) + if (w <= 0.0) { + if (f_mutrunexps) chromosome->StopMutationRunExperimentClock("FitnessOfParent()"); return 0.0; + } haplosome_index += 1; break; @@ -2754,8 +2773,10 @@ double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::ve Haplosome *haplosome = individual->haplosomes_[haplosome_index]; w *= _Fitness_HaploidChromosome(haplosome, p_mutationEffect_callbacks); - if (w <= 0.0) + if (w <= 0.0) { + if (f_mutrunexps) chromosome->StopMutationRunExperimentClock("FitnessOfParent()"); return 0.0; + } haplosome_index += 2; break; @@ -2765,21 +2786,28 @@ double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::ve Haplosome *haplosome = individual->haplosomes_[haplosome_index+1]; w *= _Fitness_HaploidChromosome(haplosome, p_mutationEffect_callbacks); - if (w <= 0.0) + if (w <= 0.0) { + if (f_mutrunexps) chromosome->StopMutationRunExperimentClock("FitnessOfParent()"); return 0.0; + } haplosome_index += 2; break; } } + + if (f_mutrunexps) chromosome->StopMutationRunExperimentClock("FitnessOfParent()"); } return w; } -template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); -template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); -template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); +template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); +template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); +template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); +template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); +template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); +template double Subpopulation::FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); template double Subpopulation::_Fitness_DiploidChromosome(Haplosome *haplosome1, Haplosome *haplosome2, std::vector &p_mutationEffect_callbacks) @@ -3345,6 +3373,8 @@ Individual *Subpopulation::GenerateIndividualCrossed(slim_pedigreeid_t p_pedigre EIDOS_TERMINATION << "ERROR (Population::GenerateIndividualCrossed): (internal error) a chromosome is defined for a no-genetics species!" << EidosTerminate(); #endif + chromosome->StartMutationRunExperimentClock(); + // Determine what kind of haplosomes to make for this chromosome ChromosomeType chromosomeType = chromosome->Type(); Haplosome *haplosome1 = nullptr, *haplosome2 = nullptr; @@ -3568,6 +3598,8 @@ Individual *Subpopulation::GenerateIndividualCrossed(slim_pedigreeid_t p_pedigre } } + chromosome->StopMutationRunExperimentClock("GenerateIndividualCrossed()"); + // For each haplosome generated, we need to add them to the individual. We also need // to record the null haplosomes for tree-seq; non-null haplosomes were already // recorded by the methods above, HaplosomeCrossed() and HaplosomeCloned(). We also @@ -3671,6 +3703,8 @@ Individual *Subpopulation::GenerateIndividualSelfed(slim_pedigreeid_t p_pedigree EIDOS_TERMINATION << "ERROR (Population::GenerateIndividualCrossed): (internal error) a chromosome is defined for a no-genetics species!" << EidosTerminate(); #endif + chromosome->StartMutationRunExperimentClock(); + // Determine what kind of haplosomes to make for this chromosome ChromosomeType chromosomeType = chromosome->Type(); Haplosome *haplosome1 = nullptr, *haplosome2 = nullptr; @@ -3718,6 +3752,8 @@ Individual *Subpopulation::GenerateIndividualSelfed(slim_pedigreeid_t p_pedigree break; } + chromosome->StopMutationRunExperimentClock("GenerateIndividualSelfed()"); + // For each haplosome generated, we need to add them to the individual. We also need // to record the null haplosomes for tree-seq; non-null haplosomes were already // recorded by the methods above, HaplosomeCrossed() and HaplosomeCloned(). We also @@ -3816,6 +3852,8 @@ Individual *Subpopulation::GenerateIndividualCloned(slim_pedigreeid_t p_pedigree EIDOS_TERMINATION << "ERROR (Population::GenerateIndividualCrossed): (internal error) a chromosome is defined for a no-genetics species!" << EidosTerminate(); #endif + chromosome->StartMutationRunExperimentClock(); + // Determine what kind of haplosomes to make for this chromosome // We just faithfully clone the existing haplosomes of the parent, regardless of type ChromosomeType chromosomeType = chromosome->Type(); @@ -3880,6 +3918,8 @@ Individual *Subpopulation::GenerateIndividualCloned(slim_pedigreeid_t p_pedigree } } + chromosome->StopMutationRunExperimentClock("GenerateIndividualCloned()"); + // For each haplosome generated, we need to add them to the individual. We also need // to record the null haplosomes for tree-seq; non-null haplosomes were already // recorded by the methods above, HaplosomeCrossed() and HaplosomeCloned(). We also @@ -3933,7 +3973,7 @@ Individual *Subpopulation::GenerateIndividualCloned(slim_pedigreeid_t p_pedigree return individual; } -template +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex) { Subpopulation &parent1_subpop = *p_parent1->subpopulation_; @@ -3998,6 +4038,8 @@ bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigree EIDOS_TERMINATION << "ERROR (Population::MungeIndividualCrossed): (internal error) a chromosome is defined for a no-genetics species!" << EidosTerminate(); #endif + if (f_mutrunexps) chromosome->StartMutationRunExperimentClock(); + // Determine what kind of haplosomes to make for this chromosome ChromosomeType chromosomeType = chromosome->Type(); Haplosome *haplosome1 = nullptr, *haplosome2 = nullptr; @@ -4239,6 +4281,8 @@ bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigree } } + if (f_mutrunexps) chromosome->StopMutationRunExperimentClock("MungeIndividualCrossed()"); + // We need to record the null haplosomes for tree-seq; non-null haplosomes were already // recorded by the methods above, HaplosomeCrossed() and HaplosomeCloned(). We also // have to set their haplosome_id_ as appropriate. @@ -4293,24 +4337,40 @@ bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigree return individual; } -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); -template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); - -template +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); +template bool Subpopulation::MungeIndividualCrossed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); + +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent) { Subpopulation &parent_subpop = *p_parent->subpopulation_; @@ -4367,6 +4427,8 @@ bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreei EIDOS_TERMINATION << "ERROR (Population::MungeIndividualSelfed): (internal error) a chromosome is defined for a no-genetics species!" << EidosTerminate(); #endif + if (f_mutrunexps) chromosome->StartMutationRunExperimentClock(); + // Determine what kind of haplosomes to make for this chromosome ChromosomeType chromosomeType = chromosome->Type(); Haplosome *haplosome1 = nullptr, *haplosome2 = nullptr; @@ -4418,6 +4480,8 @@ bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreei break; } + if (f_mutrunexps) chromosome->StopMutationRunExperimentClock("MungeIndividualSelfed()"); + // We need to record the null haplosomes for tree-seq; non-null haplosomes were already // recorded by the methods above, HaplosomeCrossed() and HaplosomeCloned(). We also // have to set their haplosome_id_ as appropriate. @@ -4472,24 +4536,40 @@ bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreei return individual; } -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); - -template +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualSelfed(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); + +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent) { IndividualSex parent_sex = p_parent->sex_; @@ -4543,6 +4623,8 @@ bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreei EIDOS_TERMINATION << "ERROR (Population::MungeIndividualCloned): (internal error) a chromosome is defined for a no-genetics species!" << EidosTerminate(); #endif + if (f_mutrunexps) chromosome->StartMutationRunExperimentClock(); + // Determine what kind of haplosomes to make for this chromosome // We just faithfully clone the existing haplosomes of the parent, regardless of type ChromosomeType chromosomeType = chromosome->Type(); @@ -4683,6 +4765,8 @@ bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreei } } + if (f_mutrunexps) chromosome->StopMutationRunExperimentClock("MungeIndividualCloned()"); + // We need to record the null haplosomes for tree-seq; non-null haplosomes were already // recorded by the methods above, HaplosomeCrossed() and HaplosomeCloned(). We also // have to set their haplosome_id_ as appropriate. @@ -4739,22 +4823,38 @@ bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreei return true; } -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); -template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); +template bool Subpopulation::MungeIndividualCloned(Individual *individual, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); Individual *Subpopulation::GenerateIndividualEmpty(slim_pedigreeid_t p_pedigree_id, slim_popsize_t p_individual_index, IndividualSex p_child_sex, slim_age_t p_age, double p_fitness, float p_mean_parent_age, bool p_haplosome1_null, bool p_haplosome2_null, bool p_run_modify_child, bool p_record_in_treeseq) { @@ -4782,6 +4882,8 @@ Individual *Subpopulation::GenerateIndividualEmpty(slim_pedigreeid_t p_pedigree_ EIDOS_TERMINATION << "ERROR (Population::GenerateIndividualEmpty): (internal error) a chromosome is defined for a no-genetics species!" << EidosTerminate(); #endif + chromosome->StartMutationRunExperimentClock(); + // Determine what kind of haplosomes to make for this chromosome ChromosomeType chromosomeType = chromosome->Type(); Haplosome *haplosome1 = nullptr, *haplosome2 = nullptr; @@ -4947,6 +5049,8 @@ Individual *Subpopulation::GenerateIndividualEmpty(slim_pedigreeid_t p_pedigree_ species_.RecordNewHaplosome(nullptr, haplosome2, nullptr, nullptr); } + chromosome->StopMutationRunExperimentClock("GenerateIndividualEmpty()"); + // move forward 1 or 2, depending on whether a haplosome2 was created currentHaplosomeIndex += (haplosome2 ? 2 : 1); } diff --git a/core/subpopulation.h b/core/subpopulation.h index f3b338fc..d28e68de 100644 --- a/core/subpopulation.h +++ b/core/subpopulation.h @@ -362,7 +362,7 @@ class Subpopulation : public EidosDictionaryUnretained void UpdateFitness(std::vector &p_mutationEffect_callbacks, std::vector &p_fitnessEffect_callbacks); // update fitness values based upon current mutations // calculate the fitness of a given individual; the x dominance coeff is used only if the X is modeled - template + template double FitnessOfParent(slim_popsize_t p_individual_index, std::vector &p_mutationEffect_callbacks); template double _Fitness_HaploidChromosome(Haplosome *haplosome, std::vector &p_mutationEffect_callbacks); @@ -373,6 +373,9 @@ class Subpopulation : public EidosDictionaryUnretained double ApplyFitnessEffectCallbacks(std::vector &p_fitnessEffect_callbacks, slim_popsize_t p_individual_index); // generate offspring individuals from parent individuals; these methods loop over chromosomes/haplosomes + // FIXME MULTICHROM these should be templated like the MungeIndividualX() methods are! + // could choose the template variant ONCE after initialize() and cache it across all nonWF calls! + // the registered callbacks won't change within the reproduction tick cycle stage, right? so that can also be figured out ONCE! Individual *GenerateIndividualCrossed(slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); Individual *GenerateIndividualSelfed(slim_pedigreeid_t p_pedigree_id, Individual *p_parent); Individual *GenerateIndividualCloned(slim_pedigreeid_t p_pedigree_id, Individual *p_parent); @@ -380,13 +383,13 @@ class Subpopulation : public EidosDictionaryUnretained // these WF-only "munge" variants munge an existing individual into the new child, reusing the individual // and its haplosome objects; they are all templated for speed, providing variants for different milieux - template + template bool MungeIndividualCrossed(Individual *p_child, slim_pedigreeid_t p_pedigree_id, Individual *p_parent1, Individual *p_parent2, IndividualSex p_child_sex); - template + template bool MungeIndividualSelfed(Individual *p_child, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); - template + template bool MungeIndividualCloned(Individual *p_child, slim_pedigreeid_t p_pedigree_id, Individual *p_parent); // WF only: diff --git a/eidos/eidos_call_signature.cpp b/eidos/eidos_call_signature.cpp index 7d411709..8b5baf48 100644 --- a/eidos/eidos_call_signature.cpp +++ b/eidos/eidos_call_signature.cpp @@ -351,6 +351,10 @@ void EidosCallSignature::CheckArgument(EidosValue *p_argument, int p_signature_i ((p_signature_index == 4) && (arg_type == EidosValueType::kValueLogical)))) EIDOS_TERMINATION << "ERROR (EidosCallSignature::CheckArgument): argument " << p_signature_index + 1 << " (" << arg_names_[p_signature_index] << ") cannot be type " << arg_type << " for " << CallType() << " " << call_name_ << "()." << std::endl << "NOTE: The defineSpatialMap() method was changed in SLiM 3.5, breaking backward compatibility. Please see the manual for guidance on updating your code." << EidosTerminate(nullptr); + // Special error-handling for initializeSLiMOptions() because its mutationRuns parameter changed to doMutationRunExperiments, and changed from integer to logical + if ((call_name_ == "initializeSLiMOptions") && (p_signature_index == 3) && (arg_type == EidosValueType::kValueInt)) + EIDOS_TERMINATION << "ERROR (EidosCallSignature::CheckArgument): argument " << p_signature_index + 1 << " (" << arg_names_[p_signature_index] << ") cannot be type " << arg_type << " for " << CallType() << " " << call_name_ << "()." << std::endl << "NOTE: The mutationRuns parameter to initializeSLiMOptions() was changed in SLiM 5, breaking backward compatibility. Please see the manual for guidance on updating your code." << EidosTerminate(nullptr); + EIDOS_TERMINATION << "ERROR (EidosCallSignature::CheckArgument): argument " << p_signature_index + 1 << " (" << arg_names_[p_signature_index] << ") cannot be type " << arg_type << " for " << CallType() << " " << call_name_ << "()." << EidosTerminate(nullptr); } diff --git a/eidos/eidos_interpreter.cpp b/eidos/eidos_interpreter.cpp index 895d4d5c..4d8b3e12 100644 --- a/eidos/eidos_interpreter.cpp +++ b/eidos/eidos_interpreter.cpp @@ -1205,6 +1205,10 @@ void EidosInterpreter::_CreateArgumentList(const EidosASTNode *p_node, const Eid if ((p_call_signature->call_name_ == "defineSpatialMap") && ((named_arg == "values") || (named_arg == "interpolate"))) EIDOS_TERMINATION << "ERROR (EidosInterpreter::_ProcessArgumentList): ran out of optional arguments while searching for named argument " << named_arg << "." << std::endl << "NOTE: The defineSpatialMap() method was changed in SLiM 3.5, breaking backward compatibility. Please see the manual for guidance on updating your code." << EidosTerminate(nullptr); + // Special error-handling for initializeSLiMOptions() because its mutationRuns parameter changed to doMutationRunExperiments in SLiM 5 + if ((p_call_signature->call_name_ == "initializeSLiMOptions") && (named_arg == "mutationRuns")) + EIDOS_TERMINATION << "ERROR (EidosInterpreter::_ProcessArgumentList): ran out of optional arguments while searching for named argument " << named_arg << "." << std::endl << "NOTE: The mutationRuns parameter to initializeSLiMOptions() was changed in SLiM 5, breaking backward compatibility. Please see the manual for guidance on updating your code." << EidosTerminate(nullptr); + EIDOS_TERMINATION << "ERROR (EidosInterpreter::_ProcessArgumentList): ran out of optional arguments while searching for named argument " << named_arg << "." << EidosTerminate(nullptr); } }