diff --git a/EidosScribe/EidosHelpController.mm b/EidosScribe/EidosHelpController.mm index b3073547..ab65a139 100644 --- a/EidosScribe/EidosHelpController.mm +++ b/EidosScribe/EidosHelpController.mm @@ -1263,6 +1263,7 @@ - (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)clipRect @"–\u00A0addCloned()", @"–\u00A0addCrossed()", @"–\u00A0addEmpty()", + @"–\u00A0addMultiRecombinant()", @"–\u00A0addRecombinant()", @"–\u00A0addSelfed()", @"–\u00A0removeSubpopulation()", diff --git a/PARALLEL b/PARALLEL index ace86511..d31dcbf1 100644 --- a/PARALLEL +++ b/PARALLEL @@ -70,7 +70,7 @@ PARALLEL changes (now in the master branch, but disabled): add support for parallel reproduction when a non-default stacking policy is in use add support for parallel reproduction with tree-sequence recording add parallel reproduction in nonWF models, with no callbacks (recombination(), mutation()) active - modifyChild() callbacks are legal but cannot access child haplosomes since they are deferred - this is achieved by passing defer=T to addCrossed(), addCloned(), addSelfed(), or addRecombinant() + this is achieved by passing defer=T to addCrossed(), addCloned(), addSelfed(), addMultiRecombinant(), or addRecombinant() thread-safety work - break in backward reproducibility for scripts that use a type 's' DFE, because the code path for that shifted algorithm change for nearestNeighbors() and nearestNeighborsOfPoint(), when count is >1 and Chromosomes().size() == 1) { - Chromosome *chromosome = focalSpecies->Chromosomes()[0]; - + chromosome = focalSpecies->Chromosomes()[0]; focalChromosomeSymbol_ = chromosome->Symbol(); - return chromosome; } } diff --git a/QtSLiM/QtSLiMChromosomeWidget_GL.cpp b/QtSLiM/QtSLiMChromosomeWidget_GL.cpp index 367f1e29..67bd3718 100644 --- a/QtSLiM/QtSLiMChromosomeWidget_GL.cpp +++ b/QtSLiM/QtSLiMChromosomeWidget_GL.cpp @@ -269,7 +269,7 @@ void QtSLiMChromosomeWidget::glDrawMutations(QRect &interiorRect, Chromosome *ch EIDOS_BZERO(heightBuffer, static_cast(displayPixelWidth) * sizeof(int16_t)); // Scan through the mutation list for mutations of this type with the right selcoeff - for (int mutation_index = 0; mutation_index < mutations.size(); ++mutation_index) + for (int mutation_index = 0; mutation_index < (int)mutations.size(); ++mutation_index) { const Mutation *mutation = mutations[mutation_index]; @@ -333,7 +333,7 @@ void QtSLiMChromosomeWidget::glDrawMutations(QRect &interiorRect, Chromosome *ch else { // We're not displaying this mutation type, so we need to mark off all the mutations belonging to it as handled - for (int mutation_index = 0; mutation_index < mutations.size(); ++mutation_index) + for (int mutation_index = 0; mutation_index < (int)mutations.size(); ++mutation_index) { const Mutation *mutation = mutations[mutation_index]; @@ -354,7 +354,7 @@ void QtSLiMChromosomeWidget::glDrawMutations(QRect &interiorRect, Chromosome *ch if (remainingMutations < 1000) { // Plot the remainder by brute force, since there are not that many - for (int mutation_index = 0; mutation_index < mutations.size(); ++mutation_index) + for (int mutation_index = 0; mutation_index < (int)mutations.size(); ++mutation_index) { //if (mutation->gui_scratch_reference_count_ == 0) if (!mutationsPlotted[mutation_index]) @@ -383,7 +383,7 @@ void QtSLiMChromosomeWidget::glDrawMutations(QRect &interiorRect, Chromosome *ch EIDOS_BZERO(heightBuffer, static_cast(displayPixelWidth) * sizeof(int16_t)); // Find the tallest bar in each column - for (int mutation_index = 0; mutation_index < mutations.size(); ++mutation_index) + for (int mutation_index = 0; mutation_index < (int)mutations.size(); ++mutation_index) { //if (mutation->gui_scratch_reference_count_ == 0) if (!mutationsPlotted[mutation_index]) diff --git a/QtSLiM/QtSLiMChromosomeWidget_QT.cpp b/QtSLiM/QtSLiMChromosomeWidget_QT.cpp index 9c082016..089dbc45 100644 --- a/QtSLiM/QtSLiMChromosomeWidget_QT.cpp +++ b/QtSLiM/QtSLiMChromosomeWidget_QT.cpp @@ -265,7 +265,7 @@ void QtSLiMChromosomeWidget::qtDrawMutations(QRect &interiorRect, Chromosome *ch EIDOS_BZERO(heightBuffer, static_cast(displayPixelWidth) * sizeof(int16_t)); // Scan through the mutation list for mutations of this type with the right selcoeff - for (int mutation_index = 0; mutation_index < mutations.size(); ++mutation_index) + for (int mutation_index = 0; mutation_index < (int)mutations.size(); ++mutation_index) { const Mutation *mutation = mutations[mutation_index]; @@ -329,7 +329,7 @@ void QtSLiMChromosomeWidget::qtDrawMutations(QRect &interiorRect, Chromosome *ch else { // We're not displaying this mutation type, so we need to mark off all the mutations belonging to it as handled - for (int mutation_index = 0; mutation_index < mutations.size(); ++mutation_index) + for (int mutation_index = 0; mutation_index < (int)mutations.size(); ++mutation_index) { const Mutation *mutation = mutations[mutation_index]; @@ -350,7 +350,7 @@ void QtSLiMChromosomeWidget::qtDrawMutations(QRect &interiorRect, Chromosome *ch if (remainingMutations < 1000) { // Plot the remainder by brute force, since there are not that many - for (int mutation_index = 0; mutation_index < mutations.size(); ++mutation_index) + for (int mutation_index = 0; mutation_index < (int)mutations.size(); ++mutation_index) { //if (mutation->gui_scratch_reference_count_ == 0) if (!mutationsPlotted[mutation_index]) @@ -379,7 +379,7 @@ void QtSLiMChromosomeWidget::qtDrawMutations(QRect &interiorRect, Chromosome *ch EIDOS_BZERO(heightBuffer, static_cast(displayPixelWidth) * sizeof(int16_t)); // Find the tallest bar in each column - for (int mutation_index = 0; mutation_index < mutations.size(); ++mutation_index) + for (int mutation_index = 0; mutation_index < (int)mutations.size(); ++mutation_index) { //if (mutation->gui_scratch_reference_count_ == 0) if (!mutationsPlotted[mutation_index]) @@ -707,7 +707,6 @@ void QtSLiMChromosomeWidget::qtDrawMutationIntervals(QRect &interiorRect, Chromo void QtSLiMChromosomeWidget::qtDrawRateMaps(QRect &interiorRect, Chromosome *chromosome, QtSLiMRange displayedRange, QPainter &painter) { - Species *displaySpecies = &chromosome->species_; bool recombinationWorthShowing = false; bool mutationWorthShowing = false; diff --git a/QtSLiM/QtSLiMHaplotypeManager.cpp b/QtSLiM/QtSLiMHaplotypeManager.cpp index ac1a2bd3..25bde10f 100644 --- a/QtSLiM/QtSLiMHaplotypeManager.cpp +++ b/QtSLiM/QtSLiMHaplotypeManager.cpp @@ -232,11 +232,10 @@ void QtSLiMHaplotypeManager::CreateHaplotypePlot(QtSLiMChromosomeWidgetControlle } // then create and install the haplotype managers, one by one; each will display once it is completed - for (int index = 0; index < chromosomes.size(); ++index) + for (int index = 0; index < (int)chromosomes.size(); ++index) { Chromosome *chromosome = chromosomes[index]; QtSLiMHaplotypeView *haplotypeView = haplotypeViews[index]; - size_t haplosomeSampleSize = optionsPanel.haplosomeSampleSize(); // First generate the haplotype plot data, with a progress panel QtSLiMHaplotypeManager *haplotypeManager = new QtSLiMHaplotypeManager(nullptr, clusteringMethod, clusteringOptimization, controller, diff --git a/QtSLiM/QtSLiMHelpWindow.cpp b/QtSLiM/QtSLiMHelpWindow.cpp index f1ebb432..ae51e78f 100644 --- a/QtSLiM/QtSLiMHelpWindow.cpp +++ b/QtSLiM/QtSLiMHelpWindow.cpp @@ -149,6 +149,7 @@ void QtSLiMHelpOutlineDelegate::paint(QPainter *painter, const QStyleOptionViewI "– addCloned()", "– addCrossed()", "– addEmpty()", + "– addMultiRecombinant()", "– addRecombinant()", "– addSelfed()", "– removeSubpopulation()", diff --git a/QtSLiM/QtSLiMWindow.cpp b/QtSLiM/QtSLiMWindow.cpp index c41be8ca..b846d3db 100644 --- a/QtSLiM/QtSLiMWindow.cpp +++ b/QtSLiM/QtSLiMWindow.cpp @@ -2988,8 +2988,6 @@ void QtSLiMWindow::updateUIEnabling(void) ui->browserButton->setEnabled(true); ui->jumpToPopupButton->setEnabled(true); - Species *species = focalDisplaySpecies(); - ui->chromosomeActionButton->setEnabled(!invalidSimulation_); ui->chromosomeDisplayButton->setEnabled(!invalidSimulation_); ui->clearOutputButton->setEnabled(!invalidSimulation_); @@ -5234,7 +5232,7 @@ void QtSLiMWindow::chromosomeDisplayPopupButtonRunMenu(void) QAction *chromAction = contextMenu.addAction(menuItemTitle); - chromAction->setData(chrom->ID()); // we use the ID temporarily, to run the menu, but the symbol will be the actual key + chromAction->setData(QVariant::fromValue(chrom->ID())); // we use the ID temporarily, to run the menu, but the symbol will be the actual key chromAction->setEnabled(!disableAll); } diff --git a/QtSLiM/help/SLiMHelpCallbacks.html b/QtSLiM/help/SLiMHelpCallbacks.html index 54bf65a5..9fb9dc19 100644 --- a/QtSLiM/help/SLiMHelpCallbacks.html +++ b/QtSLiM/help/SLiMHelpCallbacks.html @@ -257,7 +257,7 @@

The callback may perform a variety of actions related to the generated mutation.  The selection coefficient of the mutation can be changed with setSelectionCoefficient(), and the mutation type of the mutation can be changed with setMutationType(); the drawSelectionCoefficient() method of MutationType may also be useful here.  A tag property value may be set for the mutation, and named values may be attached to the mutation with setValue().  In nucleotide-based models, the nucleotide (or nucleotideValue) property of the mutation may also be changed; note that the original nucleotide at the focal position in the parental haplosome is provided through originalNuc (it could be retrieved with haplosome.nucleotides(), but SLiM already has it at hand anyway).  All of these modifications to the new mutation may be based upon the state of the parent, including its genetic state, or upon any other model state.

It is possible, of course, to do actions unrelated to mutation inside mutation() callbacks, but it is not recommended; first(), early(), and late() events should be used for general-purpose scripting.  Besides providing conceptual clarity, following this design principle will also decrease the probability of bugs, since actions that are unrelated to mutation should not influence or be influenced by the dynamics of mutation.

The proposed mutation will not appear in the sim.mutations vector of segregating mutations until it has been added to a haplosome; it will therefore not be visible in that vector within its own mutation() callback invocation, and indeed, may not be visible in subsequent callbacks during the reproduction tick cycle stage until such time as the offspring individual being generated has been completed.  If that offspring is ultimately rejected, in particular by a modifyChild() callback, the proposed mutation may not be used by SLiM at all.  It may therefore be unwise to assume, in a mutation() callback, that the focal mutation will ultimately be added to the simulation, depending upon the rest of the model’s script.

-

There is one subtlety to be mentioned here, having to do with subpopulations.  The subpop pseudo-parameter discussed above is always the subpopulation of the parent which possesses the haplosome that is being copied and is mutating; there is no ambiguity about that whatsoever.  The <subpop-id> specified in the mutation() callback declaration, however, is a bit more subtle; above it was said that it restricts the callback “to individuals generated by a specified subpopulation”, and that is usually true but requires some explanation.  In WF models, recall that migrants are generated in a source subpopulation and placed in a target subpopulation, as a model of juvenile migration; in that context, the <subpop-id> specifies the source subpopulation to which the mutation() callback will be restricted.  In nonWF models, offspring are generated by the add...() family of Subpopulation methods, which can cross individuals from two different subpopulations and place the result in a third target subpopulation; in that context, in general, the <subpop-id> specifies the source subpopulation that is generating the particular gamete that is sustaining a mutation during its production.  The exception to this rule is addRecombinant(); since there are four different source subpopulations potentially in play there, it was deemed simpler in that case for the <subpop-id> to specify the target subpopulation to which the mutation() callback will be restricted.  If restriction to the source subpopulation is needed with addRecombinant(), the subpop pseudo-parameter may be consulted rather than using <subpop-id>.

+

There is one subtlety to be mentioned here, having to do with subpopulations.  The subpop pseudo-parameter discussed above is always the subpopulation of the parent which possesses the haplosome that is being copied and is mutating; there is no ambiguity about that whatsoever.  The <subpop-id> specified in the mutation() callback declaration, however, is a bit more subtle; above it was said that it restricts the callback “to individuals generated by a specified subpopulation”, and that is usually true but requires some explanation.  In WF models, recall that migrants are generated in a source subpopulation and placed in a target subpopulation, as a model of juvenile migration; in that context, the <subpop-id> specifies the source subpopulation to which the mutation() callback will be restricted.  In nonWF models, offspring are generated by the add...() family of Subpopulation methods, which can cross individuals from two different subpopulations and place the result in a third target subpopulation; in that context, in general, the <subpop-id> specifies the source subpopulation that is generating the particular gamete that is sustaining a mutation during its production.  The exception to this rule is addRecombinant() and addMultiRecombinant(); since there are four different source subpopulations potentially in play there per mutation, it was deemed simpler in that case for the <subpop-id> to specify the target subpopulation to which the mutation() callback will be restricted.  If restriction to the source subpopulation is needed with addRecombinant() or addMultiRecombinant(), the subpop pseudo-parameter may be consulted rather than using <subpop-id>.

Note that mutation() callbacks are only called for mutations that are auto-generated by SLiM, as a consequence of the mutation rate and the genetic structure defined.  Mutations that are created in script, using addNewMutation() or addNewDrawnMutation(), will not trigger mutation() callbacks; but of course the script may modify or tailor such added mutations in whatever way is desired, so there is no need for callbacks in that situation.

As with the other callback types, multiple mutation() callbacks may be registered and active.  In this case, all registered and active callbacks will be called for each generated mutation to which they apply, in the order that the callbacks were registered.

5.13.10  ITEM: 11. survival() callbacks

diff --git a/QtSLiM/help/SLiMHelpClasses.html b/QtSLiM/help/SLiMHelpClasses.html index 3ed42510..e6945028 100644 --- a/QtSLiM/help/SLiMHelpClasses.html +++ b/QtSLiM/help/SLiMHelpClasses.html @@ -142,7 +142,7 @@

For purposes related to interpreting the nucleotide sequence as a coding sequence, a format of "codon" is also supported.  This format will return an integer vector with values from 0 to 63, based upon successive nucleotide triplets in the sequence (which, for this format, must have a length that is a multiple of three).  The codon value for a given nucleotide triplet XYZ is 16X + 4Y + Z, where X, Y, and Z have the usual values A=0, C=1, G=2, T=3.  For example, the triplet AAA has a codon value of 0, AAC is 1, AAG is 2, AAT is 3, ACA is 4, and on upward to TTT which is 63.  If the nucleotide sequence AACACATTT is requested in codon format, the codon vector 1 4 63 will therefore be returned.  These codon values can be useful in themselves; they can also be passed to codonToAminoAcid() to translate them into the corresponding amino acid sequence if desired.

– (integer)drawBreakpoints([No<Individual>$ parent = NULL], [Ni$ n = NULL])

Draw recombination breakpoints, using the chromosome’s recombination rate map, the current gene conversion parameters, and (in some cases – see below) any active and applicable recombination() callbacks.  The number of breakpoints to generate, n, may be supplied; if it is NULL (the default), the number of breakpoints will be drawn based upon the overall recombination rate and the chromosome length (following the standard procedure in SLiM).  Note that if the double-stranded breaks model has been chosen, the number of breakpoints generated will probably not be equal to the number requested, because most breakpoints will entail gene conversion tracts, which entail additional crossover breakpoints.

-

It is generally recommended that the parent individual be supplied to this method, but parent is NULL by default.  The individual supplied in parent is used for two purposes.  First, in sexual models that define separate recombination rate maps for males versus females, the sex of parent will be used to determine which map is used; in this case, a non-NULL value must be supplied for parent, since the choice of recombination rate map must be determined.  Second, in models that define recombination() callbacks, parent is used to determine the various pseudo-parameters that are passed to recombination() callbacks (individual, haplosome1, haplosome2, subpop), and the subpopulation to which parent belongs is used to select which recombination() callbacks are applicable; given the necessity of this information, recombination() callbacks will not be called as a side effect of this method if parent is NULL.  Apart from these two uses, parent is not used, and the caller does not guarantee that the generated breakpoints will actually be used to recombine the haplosomes of parent in particular.  If a recombination() callback is called, haplosome1 for that callback will always be the first haplosome of parent for the chromosome; in other words, drawBreakpoints() will always treat the first haplosome of a homologous pair as the initial copy strand.  If the caller wishes to randomly choose an initial copy strand (which is usually desirable), they should do that themselves (note that the addRecombinant() method has a flag to facilitate this).

+

It is generally recommended that the parent individual be supplied to this method, but parent is NULL by default.  The individual supplied in parent is used for two purposes.  First, in sexual models that define separate recombination rate maps for males versus females, the sex of parent will be used to determine which map is used; in this case, a non-NULL value must be supplied for parent, since the choice of recombination rate map must be determined.  Second, in models that define recombination() callbacks, parent is used to determine the various pseudo-parameters that are passed to recombination() callbacks (individual, haplosome1, haplosome2, subpop), and the subpopulation to which parent belongs is used to select which recombination() callbacks are applicable; given the necessity of this information, recombination() callbacks will not be called as a side effect of this method if parent is NULL.  Apart from these two uses, parent is not used, and the caller does not guarantee that the generated breakpoints will actually be used to recombine the haplosomes of parent in particular.  If a recombination() callback is called, haplosome1 for that callback will always be the first haplosome of parent for the chromosome; in other words, drawBreakpoints() will always treat the first haplosome of a homologous pair as the initial copy strand.  If the caller wishes to randomly choose an initial copy strand (which is usually desirable), they should do that themselves (note that the addRecombinant() and addMultiRecombinant() methods have a flag to facilitate this).

– (object<GenomicElement>)genomicElementForPosition(integer positions)

Returns a vector of GenomicElement objects corresponding to the given vector positions, which contains base positions along the chromosome.  If every position lies within a defined genomic element, the returned vector will have the same length as positions, and will correspond one-to-one with it.  However, if a position in positions is not within a genomic element, no GenomicElement object will be present for it in the returned vector, and so the returned vector will no longer have the same length as positions, and will no longer correspond one-to-one with it.  The method hasGenomicElementForPosition() can be used to detect this circumstance.

– (logical)hasGenomicElementForPosition(integer positions)

@@ -384,7 +384,7 @@

index => (integer$)

The index of the individual in the individuals vector of its Subpopulation.

meanParentAge => (float$)

-

The average age of the parents of this individual, measured in cycles.  Parentless individuals will have a meanParentAge of 0.0.  The mean parent age is determined when a new offspring is generated, from the age property of the parent or parents involved in generating the offspring.  For addRecombinant() that is somewhat complex; see that method for details.

+

The average age of the parents of this individual, measured in cycles.  Parentless individuals will have a meanParentAge of 0.0.  The mean parent age is determined when a new offspring is generated, from the age property of the parent or parents involved in generating the offspring.  For addRecombinant() and addMultiRecombinant() that is somewhat complex; see those methods for details.

migrant => (logical$)

Set to T if the individual is a recent migrant, F otherwise.  The definition of “recent” depends upon the model type (WF or nonWF).

In WF models, this flag is set at the point when a new child is generated if it is a migrant (i.e., if its source subpopulation is not the same as its subpopulation), and remains valid, with the same value, for the rest of the individual’s lifetime.

@@ -396,7 +396,7 @@

pedigreeGrandparentIDs => (integer)

If pedigree tracking is turned on with initializeSLiMOptions(keepPedigrees=T), pedigreeGrandparentIDs contains the values of pedigreeID that were possessed by the grandparents of an individual; it is thus a vector of four values.  If pedigree tracking is not enabled, this property is unavailable.  Grandparental values may be -1 if insufficient ticks have elapsed for that information to be available (because the simulation just started, or because a subpopulation is new).

reproductiveOutput => (integer$)

-

If pedigree tracking is turned on with initializeSLiMOptions(keepPedigrees=T), reproductiveOutput contains the number of offspring for which this individual has been a parent.  If pedigree tracking is not enabled, this property is unavailable.  If an individual is a parent by cloning or selfing, or as both parents for a biparental mating, this value is incremented by two.  Involvement of an individual as a parent for an addRecombinant() call does not change this property’s value, since the reproductive contribution in that case is unclear; one must conduct separate bookkeeping for that case if necessary.

+

If pedigree tracking is turned on with initializeSLiMOptions(keepPedigrees=T), reproductiveOutput contains the number of offspring for which this individual has been a parent.  If pedigree tracking is not enabled, this property is unavailable.  If an individual is a parent by cloning or selfing, or as both parents for a biparental mating, this value is incremented by two.  Involvement of an individual as a parent for an addRecombinant() or addMultiRecombinant() call does not change this property’s value, since the reproductive contribution in that case is unclear; one must conduct separate bookkeeping for that case if necessary, or use tree-sequence recording to infer it from the inheritance record.

See also the Subpopulation property lifetimeReproductiveOutput.

sex => (string$)

The sex of the individual.  This will be "H" if sex is not enabled in the simulation (i.e., for hermaphrodites), otherwise "F" or "M" as appropriate.

@@ -1049,6 +1049,14 @@

For all chromosome types except "A", null haplosomes will be generated as dictated by the sex of the individual and type of the chromosome.  For example, for chromosome type "X" a female would be generated with two empty haplosomes for that chromosome (XX), whereas a male would be generated with one empty haplosome and one null haplosome (X–, in SLiM parlance).  For chromosome type "H" an empty haplosome is always generated, not a null haplosome.  But for chromosome type "A", in particular, more control is afforded.  Passing NULL (the default) or F for haplosome1Null will make the first haplosome for every chromosome of type "A" be a non-null (empty) haplosome, the standard behavior.  More interestingly, passing T for haplosome1Null would make the first haplosome for every chromosome of type "A" be a null haplosome.  Similarly, passing T for haplosome2Null would make the second haplosome for every chromosome of type "A" be a null haplosome.  This option could be useful for situations such as adding new haploids into a haplodiploid model.  (Separate control over the haploid or diploid configuration of each chromosome of type "A" is not presently supported, but would be a simple extension to the design, by allowing haplosome1Null and haplosome2Null to provide a whole vector of logical flags rather than just a singleton value; please request this feature if you require it.)

Beginning in SLiM 4.1, the count parameter dictates how many offspring will be generated (previously, exactly one offspring was generated).  Each offspring is generated independently, based upon the given parameters.  The returned vector contains all generated offspring, except those that were rejected by a modifyChild() callback.  If all offspring are rejected, object<Individual>(0) is returned, which is a zero-length object vector of class Individual; note that this is a change in behavior from earlier versions, which would return NULL.

Note that this method is only for use in nonWF models.  See addCrossed() for further general notes on the addition of new offspring individuals.

+

– (object<Individual>)addMultiRecombinant(object<Dictionary>$ pattern, [Nfs$ sex = NULL], [No<Individual>$ parent1 = NULL], [No<Individual>$ parent2 = NULL], [logical$ randomizeStrands = F], [integer$ count = 1], [logical$ defer = F])

+

Generates a new offspring individual based upon the inheritance pattern specified by pattern, which is a dictionary of dictionaries.  This method is a multi-chromosome version of the addRecombinant() method.  For single-chromosome models, using addRecombinant() will be simpler; and it will be easier to understand this method if you understand addRecombinant() first.

+

The top-level “pattern dictionary” given by pattern specifies the way in which each chromosome should be handled.  It can use integer keys, in which case each key is the id of a chromosome, or string keys, in which case each key is the symbol of a chromosome.  In either case, a chromosome’s inheritance pattern is specified by an “inheritance dictionary” in pattern attached to such an id or symbol key.  That inheritance dictionary should itself contain up to six keys, with the standard names "strand1", "strand2", "breaks1", "strand3", "strand4", and "breaks2".  Any key which is missing in an inheritance dictionary is assumed to have a value of NULL.  These key-value pairs are used in precisely the same way as the parameters of the same names for addRecombinant(), to produce the offspring haplosome(s) for the specified chromosome.  There is considerable complication here regarding how these six values are used to produce results like crossing, cloning, and selfing, involving as many as four different “parents” for each chromosome.  Rather than repeating all of that documentation here, please see the addRecombinant() documentation for more information; this method works in exactly the same way, except for multiple chromosomes.

+

The sex parameter optionally specifies the sex of the offspring; it can be a string sex ("M" or "F"), a float probability of being male, or NULL for the default behavior (equal probabilities of male and female).  In a non-sexual model, only NULL may be passed.  See addCrossed() for further details.

+

By default, the offspring is considered to have no parents for the purposes of pedigree tracking, since there may be more than two “parents” in the general case.  If pedigree tracking of parentage is desired, parent1 and/or parent2 may be passed to explicitly establish particular individuals as the parents of the offspring for purposes of pedigree tracking.  In this case, if only one of parent1 and parent2 is non-NULL, that individual will be set as both of the parents of the offspring, mirroring the way that parentage is tracked for other cases such as addCloned() and addSelfed().  It is not required for parent1 or parent2 to actually be a genetic parent of the offspring at all, although typically they would be.

+

When recombination is involved, the inheritance dictionary will specify two parental strands with crossover breakpoints between them to generate one offspring strand – for example, the "strand1", "strand2", and "breaks1" keys, as described above, but the same is true of the "strand3", "strand4", and "breaks2" keys, and the discussion that follows applies to both cases.  If randomizeStrands is F (the default), these strands are used precisely as given; for example, "strand1" will be the initial copy strand when generating the first gamete to form the offspring.  If randomizeStrands is T, then if "strand1" and "strand2" are both non-NULL, 50% of the time they will be swapped, making "strand2" the initial copy strand for the first gamete instead.  This is probably usually the desired behavior, to avoid an inheritance bias due to a lack of randomization in the initial copy strand, so passing T for randomizeStrands is recommended unless you specifically desire otherwise.  It is not the default behavior only for reasons of backward compatibility.

+

Finally, count is the number of offspring to generate using the given pattern and parameters, and defer is used for deferral of offspring generation, as described for addRecombinant().

+

It is important to note that not all chromosomes need to be specified by pattern; whenever SLiM’s default inheritance behavior is well-defined and is desired for a given chromosome, the inheritance dictionary for that chromosome may be omitted.  The default inheritance behavior is based upon the values of parent1 and parent2.  If two parents are specified (that is, non-NULL), the default behavior is as it would be for a call to addCrossed() with those two parents, given in that order.  If only one parent is specified (non-NULL), the default behavior is as it would be for a call to addCloned() with that one parent.  (If selfing is desired as the default behavior, pass the same individual for both parent1 and parent2.)  Precisely what the default behavior consists of depends upon the type of chromosome, as specified in the documentation for initializeChromosome().  If the default behavior for a given chromosome is not well-defined, or if both parent1 and parent2 are NULL, the inheritance pattern for that chromosome must be specified in pattern, or an error will be raised.  This default behavior makes it easy to specify a reproduction event like a biparental cross involving many chromosomes, but deviate from that default to specify a different reproductive pattern just for one particular chromosome that behaves in a special way.

– (object<Individual>)addRecombinant(No<Haplosome>$ strand1, No<Haplosome>$ strand2, Ni breaks1, No<Haplosome>$ strand3, No<Haplosome>$ strand4, Ni breaks2, [Nfs$ sex = NULL], [No<Individual>$ parent1 = NULL], [No<Individual>$ parent2 = NULL], [logical$ randomizeStrands = F], [integer$ count = 1], [logical$ defer = F])

Generates a new offspring individual from the given parental haplosomes with the specified crossover breakpoints, queues it for addition to the target subpopulation, and returns it.  The new offspring will not be visible as a member of the target subpopulation until the end of the offspring generation tick cycle stage.  The target subpopulation will be used to locate applicable mutation() and modifyChild() callbacks governing the generation of the offspring individual (unlike the other addX() methods, because there are potentially up to four parental individuals to reference); recombination() callbacks will not be called by this method.  This method is an advanced feature; most models will use addCrossed(), addSelfed(), or addCloned() instead.

This method supports several possible configurations for strand1, strand2, and breaks1 (and the same applies for strand3, strand4, and breaks2).  If strand1 and strand2 are both NULL, the corresponding haplosome in the generated offspring will be empty, as from addEmpty(), with no parental haplosomes and no added mutations; in this case, breaks1 must be NULL or zero-length.  If strand1 is non-NULL but strand2 is NULL, the corresponding haplosome in the generated offspring will be a clonal copy of strand1 with mutations added, as from addCloned(); in this case, breaks1 must similarly be NULL or zero-length.  If strand1 and strand2 are both non-NULL, the corresponding haplosome in the generated offspring will result from recombination between strand1 and strand2 with mutations added, as from addCrossed(), with strand1 being the initial copy strand; copying will switch between strands at each breakpoint in breaks1, which must be non-NULL but need not be sorted or uniqued (SLiM will sort and unique the supplied breakpoints internally).  (It is not currently legal for strand1 to be NULL and strand2 non-NULL; that variant may be assigned some meaning in future.)  Again, this discussion applies equally to strand3, strand4, and breaks2, mutatis mutandis.  Note that when new mutations are generated by addRecombinant(), their subpopID property will be the id of the offspring’s subpopulation, since the parental subpopulation is ambiguous in the general case; this behavior differs from the other add...() methods.

diff --git a/SLiMgui/ChromosomeView.mm b/SLiMgui/ChromosomeView.mm index f449630f..d75f940b 100644 --- a/SLiMgui/ChromosomeView.mm +++ b/SLiMgui/ChromosomeView.mm @@ -31,8 +31,6 @@ static const int numberOfTicksPlusOne = 4; static const int tickLength = 5; static const int heightForTicks = 16; -static const int selectionKnobSizeExtension = 2; // a 5-pixel-width knob is 2: 2 + 1 + 2, an extension on each side plus the one pixel of the bar in the middle -static const int selectionKnobSize = selectionKnobSizeExtension + selectionKnobSizeExtension + 1; static const int spaceBetweenChromosomes = 5; diff --git a/SLiMgui/SLiMHelpCallbacks.rtf b/SLiMgui/SLiMHelpCallbacks.rtf index 3b7f9b87..ce3372ca 100644 --- a/SLiMgui/SLiMHelpCallbacks.rtf +++ b/SLiMgui/SLiMHelpCallbacks.rtf @@ -1464,7 +1464,9 @@ There is one subtlety to be mentioned here, having to do with subpopulations. T \f0\i gamete \f2\i0 that is sustaining a mutation during its production. The exception to this rule is \f3\fs18 addRecombinant() -\f2\fs22 ; since there are four different source subpopulations potentially in play there, it was deemed simpler in that case for the +\f2\fs22 and +\f3\fs18 addMultiRecombinant() +\f2\fs22 ; since there are four different source subpopulations potentially in play there per mutation, it was deemed simpler in that case for the \f3\fs18 \f2\fs22 to specify the \f0\i target @@ -1472,6 +1474,8 @@ There is one subtlety to be mentioned here, having to do with subpopulations. T \f3\fs18 mutation() \f2\fs22 callback will be restricted. If restriction to the source subpopulation is needed with \f3\fs18 addRecombinant() +\f2\fs22 or +\f3\fs18 addMultiRecombinant() \f2\fs22 , the \f3\fs18 subpop \f2\fs22 pseudo-parameter may be consulted rather than using diff --git a/SLiMgui/SLiMHelpClasses.rtf b/SLiMgui/SLiMHelpClasses.rtf index 06b0e154..1c3082c8 100644 --- a/SLiMgui/SLiMHelpClasses.rtf +++ b/SLiMgui/SLiMHelpClasses.rtf @@ -602,7 +602,9 @@ For purposes related to interpreting the nucleotide sequence as a coding sequenc \f3\fs18 drawBreakpoints() \f4\fs20 will always treat the first haplosome of a homologous pair as the initial copy strand. If the caller wishes to randomly choose an initial copy strand (which is usually desirable), they should do that themselves (note that the \f3\fs18 addRecombinant() -\f4\fs20 method has a flag to facilitate this).\ +\f4\fs20 and +\f3\fs18 addMultiRecombinant() +\f4\fs20 methods have a flag to facilitate this).\ \pard\pardeftab720\li720\fi-446\ri720\sb180\sa60\partightenfactor0 \f3\fs18 \cf2 \'96\'a0(object)genomicElementForPosition(integer\'a0positions)\ @@ -1708,7 +1710,6 @@ Beginning in SLiM 2.1, this is a class method, not an instance method. This mea \f4\fs20 method \f3\fs18 recalculateFitness() \f4\fs20 \'96 but see the documentation of that method for caveats.\ -\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 \cf2 Note that in nonWF models that use tree-sequence recording, mutations cannot be added to an individual after the tick in which the individual is created (i.e., when the \f3\fs18 age \f4\fs20 of the individual is greater than @@ -1819,7 +1820,6 @@ Beginning in SLiM 2.1, this is a class method, not an instance method. This mea \f4\fs20 method \f3\fs18 recalculateFitness() \f4\fs20 \'96 but see the documentation of that method for caveats.\ -\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 \cf2 Note that in nonWF models that use tree-sequence recording, mutations cannot be added to an individual after the tick in which the individual is created (i.e., when the \f3\fs18 age \f4\fs20 of the individual is greater than @@ -2884,7 +2884,9 @@ The value of \f3\fs18 age \f4\fs20 property of the parent or parents involved in generating the offspring. For \f3\fs18 addRecombinant() -\f4\fs20 that is somewhat complex; see that method for details.\ +\f4\fs20 and +\f3\fs18 addMultiRecombinant() +\f4\fs20 that is somewhat complex; see those methods for details.\ \pard\pardeftab397\li720\fi-446\ri720\sb180\sa60\partightenfactor0 \f3\fs18 \cf2 \expnd0\expndtw0\kerning0 @@ -2978,7 +2980,9 @@ In nonWF models, this flag is \f1\i both \f4\i0 parents for a biparental mating, this value is incremented by two. Involvement of an individual as a parent for an \f3\fs18 addRecombinant() -\f4\fs20 call does not change this property\'92s value, since the reproductive contribution in that case is unclear; one must conduct separate bookkeeping for that case if necessary.\ +\f4\fs20 or +\f3\fs18 addMultiRecombinant() +\f4\fs20 call does not change this property\'92s value, since the reproductive contribution in that case is unclear; one must conduct separate bookkeeping for that case if necessary, or use tree-sequence recording to infer it from the inheritance record.\ See also the \f3\fs18 Subpopulation \f4\fs20 property @@ -9528,6 +9532,162 @@ Note that this method is only for use in nonWF models. See \f4\fs20 for further general notes on the addition of new offspring individuals.\ \pard\pardeftab397\li720\fi-446\ri720\sb180\sa60\partightenfactor0 +\f3\fs18 \cf2 \'96 (object)addMultiRecombinant(object$\'a0pattern, [Nfs$\'a0sex\'a0=\'a0NULL], [No$\'a0parent1\'a0=\'a0NULL], [No$\'a0parent2\'a0=\'a0NULL], [logical$\'a0randomizeStrands\'a0=\'a0F], [integer$\'a0count\'a0=\'a01], [logical$\'a0defer\'a0=\'a0F])\ +\pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 + +\f4\fs20 \cf2 Generates a new offspring individual based upon the inheritance pattern specified by +\f3\fs18 pattern +\f4\fs20 , which is a dictionary of dictionaries. This method is a multi-chromosome version of the +\f3\fs18 addRecombinant() +\f4\fs20 method. For single-chromosome models, using +\f3\fs18 addRecombinant() +\f4\fs20 will be simpler; and it will be easier to understand this method if you understand +\f3\fs18 addRecombinant() +\f4\fs20 first.\ +The top-level \'93pattern dictionary\'94 given by +\f3\fs18 pattern +\f4\fs20 specifies the way in which each chromosome should be handled. It can use +\f3\fs18 integer +\f4\fs20 keys, in which case each key is the +\f3\fs18 id +\f4\fs20 of a chromosome, or +\f3\fs18 string +\f4\fs20 keys, in which case each key is the +\f3\fs18 symbol +\f4\fs20 of a chromosome. In either case, a chromosome\'92s inheritance pattern is specified by an \'93inheritance dictionary\'94 in +\f3\fs18 pattern +\f4\fs20 attached to such an +\f3\fs18 id +\f4\fs20 or +\f3\fs18 symbol +\f4\fs20 key. That inheritance dictionary should itself contain up to six keys, with the standard names +\f3\fs18 "strand1" +\f4\fs20 , +\f3\fs18 "strand2" +\f4\fs20 , +\f3\fs18 "breaks1" +\f4\fs20 , +\f3\fs18 "strand3" +\f4\fs20 , +\f3\fs18 "strand4" +\f4\fs20 , and +\f3\fs18 "breaks2" +\f4\fs20 . Any key which is missing in an inheritance dictionary is assumed to have a value of +\f3\fs18 NULL +\f4\fs20 . These key-value pairs are used in precisely the same way as the parameters of the same names for +\f3\fs18 addRecombinant() +\f4\fs20 , to produce the offspring haplosome(s) for the specified chromosome. There is considerable complication here regarding how these six values are used to produce results like crossing, cloning, and selfing, involving as many as four different \'93parents\'94 for each chromosome. Rather than repeating all of that documentation here, please see the +\f3\fs18 addRecombinant() +\f4\fs20 documentation for more information; this method works in exactly the same way, except for multiple chromosomes.\ +The +\f3\fs18 sex +\f4\fs20 parameter optionally specifies the sex of the offspring; it can be a +\f3\fs18 string +\f4\fs20 sex ( +\f3\fs18 "M" +\f4\fs20 or +\f3\fs18 "F" +\f4\fs20 ), a +\f3\fs18 float +\f4\fs20 probability of being male, or +\f3\fs18 NULL +\f4\fs20 for the default behavior (equal probabilities of male and female). In a non-sexual model, only +\f3\fs18 NULL +\f4\fs20 may be passed. See +\f3\fs18 addCrossed() +\f4\fs20 for further details.\ +By default, the offspring is considered to have no parents for the purposes of pedigree tracking, since there may be more than two \'93parents\'94 in the general case. If pedigree tracking of parentage is desired, +\f3\fs18 parent1 +\f4\fs20 and/or +\f3\fs18 parent2 +\f4\fs20 may be passed to explicitly establish particular individuals as the parents of the offspring for purposes of pedigree tracking. In this case, if only one of +\f3\fs18 parent1 +\f4\fs20 and +\f3\fs18 parent2 +\f4\fs20 is non- +\f3\fs18 NULL +\f4\fs20 , that individual will be set as +\f1\i both +\f4\i0 of the parents of the offspring, mirroring the way that parentage is tracked for other cases such as +\f3\fs18 addCloned() +\f4\fs20 and +\f3\fs18 addSelfed() +\f4\fs20 . It is not required for +\f3\fs18 parent1 +\f4\fs20 or +\f3\fs18 parent2 +\f4\fs20 to actually be a genetic parent of the offspring at all, although typically they would be.\ +When recombination is involved, the inheritance dictionary will specify two parental strands with crossover breakpoints between them to generate one offspring strand \'96 for example, the +\f3\fs18 "strand1" +\f4\fs20 , +\f3\fs18 "strand2" +\f4\fs20 , and +\f3\fs18 "breaks1" +\f4\fs20 keys, as described above, but the same is true of the +\f3\fs18 "strand3" +\f4\fs20 , +\f3\fs18 "strand4" +\f4\fs20 , and +\f3\fs18 "breaks2" +\f4\fs20 keys, and the discussion that follows applies to both cases. If +\f3\fs18 randomizeStrands +\f4\fs20 is +\f3\fs18 F +\f4\fs20 (the default), these strands are used precisely as given; for example, +\f3\fs18 "strand1" +\f4\fs20 will be the initial copy strand when generating the first gamete to form the offspring. If +\f3\fs18 randomizeStrands +\f4\fs20 is T, then if +\f3\fs18 "strand1" +\f4\fs20 and +\f3\fs18 "strand2" +\f4\fs20 are both non- +\f3\fs18 NULL +\f4\fs20 , 50% of the time they will be swapped, making +\f3\fs18 "strand2" +\f4\fs20 the initial copy strand for the first gamete instead. This is probably usually the desired behavior, to avoid an inheritance bias due to a lack of randomization in the initial copy strand, so passing +\f3\fs18 T +\f4\fs20 for +\f3\fs18 randomizeStrands +\f4\fs20 is recommended unless you specifically desire otherwise. It is not the default behavior only for reasons of backward compatibility.\ +Finally, +\f3\fs18 count +\f4\fs20 is the number of offspring to generate using the given pattern and parameters, and +\f3\fs18 defer +\f4\fs20 is used for deferral of offspring generation, as described for +\f3\fs18 addRecombinant() +\f4\fs20 .\ +It is important to note that not all chromosomes need to be specified by +\f3\fs18 pattern +\f4\fs20 ; whenever SLiM\'92s default inheritance behavior is well-defined and is desired for a given chromosome, the inheritance dictionary for that chromosome may be omitted. The default inheritance behavior is based upon the values of +\f3\fs18 parent1 +\f4\fs20 and +\f3\fs18 parent2 +\f4\fs20 . If two parents are specified (that is, non- +\f3\fs18 NULL +\f4\fs20 ), the default behavior is as it would be for a call to +\f3\fs18 addCrossed() +\f4\fs20 with those two parents, given in that order. If only one parent is specified (non- +\f3\fs18 NULL +\f4\fs20 ), the default behavior is as it would be for a call to +\f3\fs18 addCloned() +\f4\fs20 with that one parent. (If selfing is desired as the default behavior, pass the same individual for both +\f3\fs18 parent1 +\f4\fs20 and +\f3\fs18 parent2 +\f4\fs20 .) Precisely what the default behavior consists of depends upon the type of chromosome, as specified in the documentation for +\f3\fs18 initializeChromosome() +\f4\fs20 . If the default behavior for a given chromosome is not well-defined, or if both +\f3\fs18 parent1 +\f4\fs20 and +\f3\fs18 parent2 +\f4\fs20 are +\f3\fs18 NULL +\f4\fs20 , the inheritance pattern for that chromosome must be specified in +\f3\fs18 pattern +\f4\fs20 , or an error will be raised. This default behavior makes it easy to specify a reproduction event like a biparental cross involving many chromosomes, but deviate from that default to specify a different reproductive pattern just for one particular chromosome that behaves in a special way.\ +\pard\pardeftab397\li720\fi-446\ri720\sb180\sa60\partightenfactor0 + \f3\fs18 \cf2 \expnd0\expndtw0\kerning0 \'96\'a0(object)addRecombinant(No$\'a0strand1, No$\'a0strand2, Ni\'a0breaks1, No$\'a0strand3, No$\'a0strand4, Ni\'a0breaks2, [Nfs$\'a0sex\'a0=\'a0NULL], [No$\'a0parent1\'a0=\'a0NULL], [No$\'a0parent2\'a0=\'a0NULL], [logical$\'a0randomizeStrands\'a0=\'a0F], [integer$\'a0count\'a0=\'a01], [logical$\'a0defer\'a0=\'a0F])\ \pard\pardeftab720\li547\ri720\sb60\sa60\partightenfactor0 diff --git a/SLiMgui/SLiMWindowController.h b/SLiMgui/SLiMWindowController.h index ea008713..fef59731 100644 --- a/SLiMgui/SLiMWindowController.h +++ b/SLiMgui/SLiMWindowController.h @@ -232,7 +232,6 @@ class Community; - (IBAction)graphMutationFixationTimeHistogram:(id)sender; - (IBAction)graphFitnessOverTime:(id)sender; - (IBAction)graphPopulationVisualization:(id)sender; -- (IBAction)graphHaplotypes:(id)sender; - (IBAction)playOneStep:(id)sender; - (IBAction)play:(id)sender; @@ -264,51 +263,10 @@ class Community; - (IBAction)exportOutput:(id)sender; // wired through firstResponder because these are menu items - (IBAction)exportPopulation:(id)sender; // wired through firstResponder because these are menu items - // Eidos SLiMgui method forwards - (void)eidos_openDocument:(NSString *)path; - (void)eidos_pauseExecution; - -// Haplotype plot options sheet - -// Outlets connected to objects in SLiMHaplotypeOptionsSheet.xib -@property (nonatomic, retain) IBOutlet NSWindow *haplotypeOptionsSheet; -@property (nonatomic, assign) IBOutlet NSTextField *haplotypeSampleTextField; -@property (nonatomic, assign) IBOutlet NSButton *haplotypeOKButton; - -@property (nonatomic) int haplotypeSample; // 0 = all haplosomes, 1 = sample -@property (nonatomic) int haplotypeClustering; // 0 = nearest neighbor, 1 = greedy, 2 = greedy + 2-opt -@property (nonatomic) int haplotypeSampleSize; // from haplotypeSampleTextField - -- (void)runHaplotypePlotOptionsSheet; - -- (IBAction)changedHaplotypeSample:(id)sender; -- (IBAction)changedHaplotypeClustering:(id)sender; - -- (IBAction)validateHaplotypeSheetControls:(id)sender; - -- (IBAction)haplotypeSheetCancel:(id)sender; -- (IBAction)haplotypeSheetOK:(id)sender; - - -// Haplotype plot progress sheet -@property (nonatomic, retain) IBOutlet NSWindow *haplotypeProgressSheet; -@property (nonatomic, assign) IBOutlet NSProgressIndicator *haplotypeProgressDistances; -@property (nonatomic, assign) IBOutlet NSProgressIndicator *haplotypeProgressClustering; -@property (nonatomic, assign) IBOutlet NSProgressIndicator *haplotypeProgressOptimization; -@property (nonatomic, assign) IBOutlet NSTextField *haplotypeProgressOptimizationLabel; -@property (nonatomic, assign) IBOutlet NSLayoutConstraint *haplotypeProgressNoOptConstraint; - -- (void)runHaplotypePlotProgressSheetWithHaplosomeCount:(int)haplosome_count; - -- (IBAction)haplotypeProgressSheetCancel:(id)sender; - -- (void)setHaplotypeProgress:(int)progress forStage:(int)stage; // the background task indicates progress on task -- (BOOL)haplotypeProgressIsCancelled; // the background task asks: has the user cancelled our task? -- (void)haplotypeProgressTaskStarting; // the background task tells us it is starting -- (void)haplotypeProgressTaskFinished; // the background task tells us it has finished - @end diff --git a/SLiMgui/SLiMWindowController.mm b/SLiMgui/SLiMWindowController.mm index 4f48c536..bd91dc65 100644 --- a/SLiMgui/SLiMWindowController.mm +++ b/SLiMgui/SLiMWindowController.mm @@ -1254,16 +1254,6 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem return !(invalidSimulation); if (sel == @selector(graphFitnessOverTime:)) return !(invalidSimulation); - if (sel == @selector(graphHaplotypes:)) - { - // must be past initialize() and have subpops - if (invalidSimulation || !community || (community->Tick() <= 1)) - return NO; - - Species *displaySpecies = [self focalDisplaySpecies]; - - return (displaySpecies && (displaySpecies->population_.subpops_.size() > 0)); - } if (sel == @selector(checkScript:)) return !(continuousPlayOn || tickPlayOn); diff --git a/core/chromosome.cpp b/core/chromosome.cpp index 4f05b206..efa759d5 100644 --- a/core/chromosome.cpp +++ b/core/chromosome.cpp @@ -2901,8 +2901,9 @@ EidosValue_SP Chromosome::ExecuteMethod_drawBreakpoints(EidosGlobalStringID p_me // Get the indices of the haplosomes associated with this chromosome. Note that the first/last indices might be the same, // if this is a haploid chromosome. That is OK here. The user is allowed to set a recombination rate on a haploid // chromosome and generate breakpoints for it; what they do with that information is up to them. (They might use them - // in an addRecombinant() call, for example.) In that case, of a haploid chromosome, the same single parent haplosome - // will be passed twice to recombination() callbacks; that seems better than not defining one of the pseudo-parameters. + // in an addRecombinant() or addMultiRecombinant() call, for example.) In that case, of a haploid chromosome, the same + // single parent haplosome will be passed twice to recombination() callbacks; that seems better than not defining one of + // the pseudo-parameters. int first_haplosome_index = species_.FirstHaplosomeIndices()[index_]; int last_haplosome_index = species_.LastHaplosomeIndices()[index_]; diff --git a/core/population.cpp b/core/population.cpp index 53b5755a..31e13fe6 100644 --- a/core/population.cpp +++ b/core/population.cpp @@ -3791,7 +3791,7 @@ void Population::HaplosomeRecombined(Chromosome &p_chromosome, Haplosome &p_chil p_child_haplosome.check_cleared_to_nullptr(); #endif - // for addRecombinant(), we use the destination subpop as the mutation origin + // for addRecombinant() and addMultiRecombinant(), we use the destination subpop as the mutation origin Subpopulation *dest_subpop = p_child_haplosome.individual_->subpopulation_; // The parent sex is used for mutation generation; we might have sex-specific mutation @@ -5926,7 +5926,6 @@ void Population::AssessMutationRuns(void) int first_haplosome_index = species_.FirstHaplosomeIndices()[chromosome_index]; int last_haplosome_index = species_.LastHaplosomeIndices()[chromosome_index]; - int haplosome_count_per_individual = species_.HaplosomeCountPerIndividual(); slim_refcount_t total_haplosome_count = 0, total_mutrun_count = 0, total_shared_mutrun_count = 0; int mutrun_count = 0, use_count_total = 0; slim_position_t mutrun_length = 0; diff --git a/core/slim_globals.cpp b/core/slim_globals.cpp index 5124906c..52578ba4 100644 --- a/core/slim_globals.cpp +++ b/core/slim_globals.cpp @@ -1405,6 +1405,7 @@ const std::string &gStr_setSubpopulationSize = EidosRegisteredString("setSubpopu const std::string &gStr_addCloned = EidosRegisteredString("addCloned", gID_addCloned); const std::string &gStr_addCrossed = EidosRegisteredString("addCrossed", gID_addCrossed); const std::string &gStr_addEmpty = EidosRegisteredString("addEmpty", gID_addEmpty); +const std::string &gStr_addMultiRecombinant = EidosRegisteredString("addMultiRecombinant", gID_addMultiRecombinant); const std::string &gStr_addRecombinant = EidosRegisteredString("addRecombinant", gID_addRecombinant); const std::string &gStr_addSelfed = EidosRegisteredString("addSelfed", gID_addSelfed); const std::string &gStr_takeMigrants = EidosRegisteredString("takeMigrants", gID_takeMigrants); diff --git a/core/slim_globals.h b/core/slim_globals.h index ff520de3..5ac3cd94 100644 --- a/core/slim_globals.h +++ b/core/slim_globals.h @@ -990,6 +990,7 @@ extern const std::string &gStr_setSubpopulationSize; extern const std::string &gStr_addCloned; extern const std::string &gStr_addCrossed; extern const std::string &gStr_addEmpty; +extern const std::string &gStr_addMultiRecombinant; extern const std::string &gStr_addRecombinant; extern const std::string &gStr_addSelfed; extern const std::string &gStr_takeMigrants; @@ -1429,6 +1430,7 @@ enum _SLiMGlobalStringID : int { gID_addCloned, gID_addCrossed, gID_addEmpty, + gID_addMultiRecombinant, gID_addRecombinant, gID_addSelfed, gID_takeMigrants, diff --git a/core/species.cpp b/core/species.cpp index ed5674ac..fa9d9622 100644 --- a/core/species.cpp +++ b/core/species.cpp @@ -220,7 +220,7 @@ void Species::_MakeHaplosomeMetadataRecords(void) for (Chromosome *chromosome : chromosomes_) { slim_chromosome_index_t chromosome_index = chromosome->Index(); - bool haplosome_1_is_null_or_unused, haplosome_2_is_null_or_unused; + bool haplosome_1_is_null_or_unused = false, haplosome_2_is_null_or_unused = false; switch (chromosome->Type()) { @@ -380,7 +380,7 @@ void Species::AddChromosome(Chromosome *p_chromosome) switch (type) { // these chromosome types do not (normally) employ null haplosomes - // if addRecombinant() etc. places a null haplosome, it will fix this flag + // if addRecombinant(), addMultiRecombinant(), etc. places a null haplosome, it will fix this flag case ChromosomeType::kA_DiploidAutosome: case ChromosomeType::kH_HaploidAutosome: case ChromosomeType::kHF_HaploidFemaleInherited: diff --git a/core/subpopulation.cpp b/core/subpopulation.cpp index ab504ec4..c54a3c56 100644 --- a/core/subpopulation.cpp +++ b/core/subpopulation.cpp @@ -342,7 +342,6 @@ void Subpopulation::GenerateChildrenToFitWF() void Subpopulation::GenerateParentsToFit(slim_age_t p_initial_age, double p_sex_ratio, bool p_allow_zero_size, bool p_require_both_sexes, bool p_record_in_treeseq, bool p_haploid, float p_mean_parent_age) { bool recording_tree_sequence = p_record_in_treeseq && species_.RecordingTreeSequence(); - bool pedigrees_enabled = species_.PedigreesEnabled(); cached_parent_individuals_value_.reset(); @@ -1385,7 +1384,6 @@ void Subpopulation::UpdateFitness(std::vector &p_mutationEffect int mutationEffect_callback_count = (int)p_mutationEffect_callbacks.size(); bool mutationEffect_callbacks_exist = (mutationEffect_callback_count > 0); bool single_mutationEffect_callback = false; - MutationType *single_callback_mut_type = nullptr; if (mutationEffect_callback_count == 1) { @@ -1398,7 +1396,6 @@ void Subpopulation::UpdateFitness(std::vector &p_mutationEffect { // We have a single callback that applies to a known mutation type among more than one defined type; we can optimize that single_mutationEffect_callback = true; - single_callback_mut_type = found_muttype; } // else there is only one mutation type, so the callback applies to all mutations in the simulation } @@ -6196,6 +6193,7 @@ EidosValue_SP Subpopulation::ExecuteInstanceMethod(EidosGlobalStringID p_method_ case gID_addCloned: return ExecuteMethod_addCloned(p_method_id, p_arguments, p_interpreter); case gID_addCrossed: return ExecuteMethod_addCrossed(p_method_id, p_arguments, p_interpreter); case gID_addEmpty: return ExecuteMethod_addEmpty(p_method_id, p_arguments, p_interpreter); + case gID_addMultiRecombinant: return ExecuteMethod_addMultiRecombinant(p_method_id, p_arguments, p_interpreter); case gID_addRecombinant: return ExecuteMethod_addRecombinant(p_method_id, p_arguments, p_interpreter); case gID_addSelfed: return ExecuteMethod_addSelfed(p_method_id, p_arguments, p_interpreter); case gID_removeSubpopulation: return ExecuteMethod_removeSubpopulation(p_method_id, p_arguments, p_interpreter); @@ -6299,7 +6297,7 @@ IndividualSex Subpopulation::_ValidateHaplosomesAndChooseSex(ChromosomeType p_ch // case ChromosomeType::kA_DiploidAutosome: { - // This type allows any null haplosome configuration in any individual, in addRecombinant() + // This type allows any null haplosome configuration in any individual, in addRecombinant() and addMultiRecombinant() break; } case ChromosomeType::kX_XSexChromosome: @@ -6343,7 +6341,7 @@ IndividualSex Subpopulation::_ValidateHaplosomesAndChooseSex(ChromosomeType p_ch // case ChromosomeType::kH_HaploidAutosome: { - // This type allows any null haplosome configuration in any individual, in addRecombinant() + // This type allows any null haplosome configuration in any individual, in addRecombinant() and addMultiRecombinant() break; } case ChromosomeType::kY_YSexChromosome: @@ -6754,6 +6752,14 @@ EidosValue_SP Subpopulation::ExecuteMethod_addEmpty(EidosGlobalStringID p_method return EidosValue_SP(result); } +// ********************* – (object)addMultiRecombinant(object$ pattern, [Nfs$ sex = NULL], [No$ parent1 = NULL], [No$ parent2 = NULL], [logical$ randomizeStrands = F], [integer$ count = 1], [logical$ defer = F]) +EidosValue_SP Subpopulation::ExecuteMethod_addMultiRecombinant(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter) +{ +#pragma unused (p_method_id, p_arguments, p_interpreter) +#warning need to implement ExecuteMethod_addMultiRecombinant() + return gStaticEidosValueNULL; +} + // ********************* – (o)addRecombinant(No$ strand1, No$ strand2, Ni breaks1, // No$ strand3, No$ strand4, Ni breaks2, // [Nfs$ sex = NULL], [No$ parent1 = NULL], [No$ parent2 = NULL], @@ -10352,6 +10358,7 @@ const std::vector *Subpopulation_Class::Methods(void) methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_addCloned, kEidosValueMaskObject, gSLiM_Individual_Class))->AddObject_S("parent", gSLiM_Individual_Class)->AddInt_OS("count", gStaticEidosValue_Integer1)->AddLogical_OS("defer", gStaticEidosValue_LogicalF)); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_addCrossed, kEidosValueMaskObject, gSLiM_Individual_Class))->AddObject_S("parent1", gSLiM_Individual_Class)->AddObject_S("parent2", gSLiM_Individual_Class)->AddArgWithDefault(kEidosValueMaskNULL | kEidosValueMaskFloat | kEidosValueMaskString | kEidosValueMaskSingleton | kEidosValueMaskOptional, "sex", nullptr, gStaticEidosValueNULL)->AddInt_OS("count", gStaticEidosValue_Integer1)->AddLogical_OS("defer", gStaticEidosValue_LogicalF)); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_addEmpty, kEidosValueMaskObject, gSLiM_Individual_Class))->AddArgWithDefault(kEidosValueMaskNULL | kEidosValueMaskFloat | kEidosValueMaskString | kEidosValueMaskSingleton | kEidosValueMaskOptional, "sex", nullptr, gStaticEidosValueNULL)->AddLogical_OSN("haplosome1Null", gStaticEidosValueNULL)->AddLogical_OSN("haplosome2Null", gStaticEidosValueNULL)->AddInt_OS("count", gStaticEidosValue_Integer1)); + methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_addMultiRecombinant, kEidosValueMaskObject, gSLiM_Individual_Class))->AddObject_SN("pattern", gEidosDictionaryRetained_Class)->AddArgWithDefault(kEidosValueMaskNULL | kEidosValueMaskFloat | kEidosValueMaskString | kEidosValueMaskSingleton | kEidosValueMaskOptional, "sex", nullptr, gStaticEidosValueNULL)->AddObject_OSN("parent1", gSLiM_Individual_Class, gStaticEidosValueNULL)->AddObject_OSN("parent2", gSLiM_Individual_Class, gStaticEidosValueNULL)->AddLogical_OS("randomizeStrands", gStaticEidosValue_LogicalF)->AddInt_OS("count", gStaticEidosValue_Integer1)->AddLogical_OS("defer", gStaticEidosValue_LogicalF)); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_addRecombinant, kEidosValueMaskObject, gSLiM_Individual_Class))->AddObject_SN("strand1", gSLiM_Haplosome_Class)->AddObject_SN("strand2", gSLiM_Haplosome_Class)->AddInt_N("breaks1")->AddObject_SN("strand3", gSLiM_Haplosome_Class)->AddObject_SN("strand4", gSLiM_Haplosome_Class)->AddInt_N("breaks2")->AddArgWithDefault(kEidosValueMaskNULL | kEidosValueMaskFloat | kEidosValueMaskString | kEidosValueMaskSingleton | kEidosValueMaskOptional, "sex", nullptr, gStaticEidosValueNULL)->AddObject_OSN("parent1", gSLiM_Individual_Class, gStaticEidosValueNULL)->AddObject_OSN("parent2", gSLiM_Individual_Class, gStaticEidosValueNULL)->AddLogical_OS("randomizeStrands", gStaticEidosValue_LogicalF)->AddInt_OS("count", gStaticEidosValue_Integer1)->AddLogical_OS("defer", gStaticEidosValue_LogicalF)); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_addSelfed, kEidosValueMaskObject, gSLiM_Individual_Class))->AddObject_S("parent", gSLiM_Individual_Class)->AddInt_OS("count", gStaticEidosValue_Integer1)->AddLogical_OS("defer", gStaticEidosValue_LogicalF)); methods->emplace_back((EidosInstanceMethodSignature *)(new EidosInstanceMethodSignature(gStr_takeMigrants, kEidosValueMaskVOID))->AddObject("migrants", gSLiM_Individual_Class)); diff --git a/core/subpopulation.h b/core/subpopulation.h index 8bbc5c9f..e4aefcd6 100644 --- a/core/subpopulation.h +++ b/core/subpopulation.h @@ -440,6 +440,7 @@ class Subpopulation : public EidosDictionaryUnretained EidosValue_SP ExecuteMethod_addCloned(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); EidosValue_SP ExecuteMethod_addCrossed(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); EidosValue_SP ExecuteMethod_addEmpty(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); + EidosValue_SP ExecuteMethod_addMultiRecombinant(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); EidosValue_SP ExecuteMethod_addRecombinant(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); EidosValue_SP ExecuteMethod_addSelfed(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter); EidosValue_SP ExecuteMethod_removeSubpopulation(EidosGlobalStringID p_method_id, const std::vector &p_arguments, EidosInterpreter &p_interpreter);