Skip to content

Commit

Permalink
add addPatternFor...() methods as helpers for addMultiRecombinant()
Browse files Browse the repository at this point in the history
  • Loading branch information
bhaller committed Jan 12, 2025
1 parent f98ccce commit 8d12555
Show file tree
Hide file tree
Showing 11 changed files with 1,257 additions and 42 deletions.
26 changes: 24 additions & 2 deletions QtSLiM/help/SLiMHelpClasses.html

Large diffs are not rendered by default.

460 changes: 457 additions & 3 deletions SLiMgui/SLiMHelpClasses.rtf

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ development head (in the master branch):
extend addRecombinant() and addMultiRecombinant() to allow NULL as a breaks vector with two strands, meaning "draw breakpoints for me", to avoid needing drawBreakpoints() in most cases
this new code path also allows gene conversion including heteroduplex mismatch repair and gBGC to work with addRecombinant() and addMultiRecombinant()
also change to allow a recombination rate to be set for haploid chromosomes; this is potentially useful with addRecombinant() etc.
add addPatternForClone(), addPatternForCross(), addPatternForNull(), and addPatternForRecombinant() methods to Species for use with addMultiRecombinant()


version 4.3 (Eidos version 3.3):
Expand Down
10 changes: 10 additions & 0 deletions core/slim_globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ EidosValue_String_SP gStaticEidosValue_StringC;
EidosValue_String_SP gStaticEidosValue_StringG;
EidosValue_String_SP gStaticEidosValue_StringT;

const std::string gStr_strand1("strand1");
const std::string gStr_strand2("strand2");
const std::string gStr_breaks1("breaks1");
const std::string gStr_strand3("strand3");
const std::string gStr_strand4("strand4");
const std::string gStr_breaks2("breaks2");

void SLiM_WarmUp(void)
{
Expand Down Expand Up @@ -1340,6 +1346,10 @@ const std::string &gStr_setSelectionCoeff = EidosRegisteredString("setSelectionC
const std::string &gStr_setMutationType = EidosRegisteredString("setMutationType", gID_setMutationType);
const std::string &gStr_drawSelectionCoefficient = EidosRegisteredString("drawSelectionCoefficient", gID_drawSelectionCoefficient);
const std::string &gStr_setDistribution = EidosRegisteredString("setDistribution", gID_setDistribution);
const std::string &gStr_addPatternForClone = EidosRegisteredString("addPatternForClone", gID_addPatternForClone);
const std::string &gStr_addPatternForCross = EidosRegisteredString("addPatternForCross", gID_addPatternForCross);
const std::string &gStr_addPatternForNull = EidosRegisteredString("addPatternForNull", gID_addPatternForNull);
const std::string &gStr_addPatternForRecombinant = EidosRegisteredString("addPatternForRecombinant", gID_addPatternForRecombinant);
const std::string &gStr_addSubpop = EidosRegisteredString("addSubpop", gID_addSubpop);
const std::string &gStr_addSubpopSplit = EidosRegisteredString("addSubpopSplit", gID_addSubpopSplit);
const std::string &gStr_chromosomesOfType = EidosRegisteredString("chromosomesOfType", gID_chromosomesOfType);
Expand Down
16 changes: 16 additions & 0 deletions core/slim_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,14 @@ std::ostream& operator<<(std::ostream& p_out, const NucleotideArray &p_nuc_array
// Additional global std::string objects. See script_globals.h for details.
//

// first are ones with no corresponding ID; these are just std::string globals, not registered
extern const std::string gStr_strand1;
extern const std::string gStr_strand2;
extern const std::string gStr_breaks1;
extern const std::string gStr_strand3;
extern const std::string gStr_strand4;
extern const std::string gStr_breaks2;

void SLiM_ConfigureContext(void);

extern const std::string &gStr_initializeAncestralNucleotides;
Expand Down Expand Up @@ -925,6 +933,10 @@ extern const std::string &gStr_setSelectionCoeff;
extern const std::string &gStr_setMutationType;
extern const std::string &gStr_drawSelectionCoefficient;
extern const std::string &gStr_setDistribution;
extern const std::string &gStr_addPatternForClone;
extern const std::string &gStr_addPatternForCross;
extern const std::string &gStr_addPatternForNull;
extern const std::string &gStr_addPatternForRecombinant;
extern const std::string &gStr_addSubpop;
extern const std::string &gStr_addSubpopSplit;
extern const std::string &gStr_chromosomesOfType;
Expand Down Expand Up @@ -1365,6 +1377,10 @@ enum _SLiMGlobalStringID : int {
gID_setMutationType,
gID_drawSelectionCoefficient,
gID_setDistribution,
gID_addPatternForClone,
gID_addPatternForCross,
gID_addPatternForNull,
gID_addPatternForRecombinant,
gID_addSubpop,
gID_chromosomesOfType,
gID_chromosomesWithIDs,
Expand Down
251 changes: 251 additions & 0 deletions core/species.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3167,6 +3167,257 @@ void Species::SimulationHasFinished(void)
}
}

void Species::InferInheritanceForClone(Chromosome *chromosome, Individual *parent, IndividualSex sex, Haplosome **strand1, Haplosome **strand3, const char *caller_name)
{
#if DEBUG
if (!chromosome || !parent || !strand1 || !strand3 || *caller_name)
EIDOS_TERMINATION << "ERROR (Species::InferInheritanceForClone): (internal error) parameter is nullptr." << EidosTerminate();
#endif

ChromosomeType chromosome_type = chromosome->Type();
unsigned int chromosome_index = chromosome->Index();
int first_haplosome_index = FirstHaplosomeIndices()[chromosome_index];
int last_haplosome_index = LastHaplosomeIndices()[chromosome_index];

// validate the offspring's sex; note that we allow kHF_HaploidFemaleInherited and
// kHM_HaploidMaleInherited to be inherited from the "wrong" sex, as does addCloned();
// those inheritance patterns are for biparental crosses specifically
IndividualSex parent_sex = parent->sex_;

if (sex == IndividualSex::kUnspecified)
sex = parent_sex;

if (sex != parent_sex)
if ((chromosome_type == ChromosomeType::kX_XSexChromosome) ||
(chromosome_type == ChromosomeType::kY_YSexChromosome) ||
(chromosome_type == ChromosomeType::kZ_ZSexChromosome) ||
(chromosome_type == ChromosomeType::kW_WSexChromosome) ||
(chromosome_type == ChromosomeType::kFL_HaploidFemaleLine) ||
(chromosome_type == ChromosomeType::kML_HaploidMaleLine) ||
(chromosome_type == ChromosomeType::kNullY_YSexChromosomeWithNull))
EIDOS_TERMINATION << "ERROR (Species::InferInheritanceForClone): clonal inheritance inference for " << caller_name << " requires that sex match the sex of the parent for chromosome type '" << chromosome_type << "' (symbol '" << chromosome->Symbol() << "'), since the haplosome configuration of that chromosome type depends upon sex. You can pass NULL for sex to match the parent automatically." << EidosTerminate();

// all returned entries not set are NULL
*strand1 = nullptr;
*strand3 = nullptr;

// for simplicity, we just test for a null haplosome and clone whatever is not null;
// if the parent is legal, the offspring will be legal too, given the sex check above
switch (chromosome_type)
{
// diploid types
case ChromosomeType::kA_DiploidAutosome:
case ChromosomeType::kX_XSexChromosome:
case ChromosomeType::kZ_ZSexChromosome:
case ChromosomeType::kNullY_YSexChromosomeWithNull:
case ChromosomeType::kHNull_HaploidAutosomeWithNull:
{
Haplosome *hap1 = parent->haplosomes_[first_haplosome_index];
Haplosome *hap2 = parent->haplosomes_[last_haplosome_index];

if (!hap1->IsNull()) *strand1 = hap1;
if (!hap2->IsNull()) *strand3 = hap2;
break;
}

// haploid types
case ChromosomeType::kH_HaploidAutosome:
case ChromosomeType::kY_YSexChromosome:
case ChromosomeType::kW_WSexChromosome:
case ChromosomeType::kHF_HaploidFemaleInherited:
case ChromosomeType::kHM_HaploidMaleInherited:
case ChromosomeType::kFL_HaploidFemaleLine:
case ChromosomeType::kML_HaploidMaleLine:
{
Haplosome *hap = parent->haplosomes_[first_haplosome_index];

if (!hap->IsNull()) *strand1 = hap;
break;
}
}
}

void Species::InferInheritanceForCross(Chromosome *chromosome, Individual *parent1, Individual *parent2, IndividualSex sex, Haplosome **strand1, Haplosome **strand2, Haplosome **strand3, Haplosome **strand4, const char *caller_name)
{
#if DEBUG
if (!chromosome || !parent1 || !parent2 || !strand1 || !strand2 || !strand3 || !strand4 || *caller_name)
EIDOS_TERMINATION << "ERROR (Species::InferInheritanceForCross): (internal error) parameter is nullptr." << EidosTerminate();
#endif

ChromosomeType chromosome_type = chromosome->Type();
unsigned int chromosome_index = chromosome->Index();
int first_haplosome_index = FirstHaplosomeIndices()[chromosome_index];
int last_haplosome_index = LastHaplosomeIndices()[chromosome_index];

// validate the offspring's sex; note that we allow kHF_HaploidFemaleInherited and
// kHM_HaploidMaleInherited to be inherited from the "wrong" sex, as does addCloned();
// those inheritance patterns are for biparental crosses specifically
IndividualSex parent1_sex = parent1->sex_;
IndividualSex parent2_sex = parent1->sex_;

if (sex_enabled_ && ((parent1_sex != IndividualSex::kFemale) || (parent2_sex != IndividualSex::kMale)))
EIDOS_TERMINATION << "ERROR (Species::InferInheritanceForCross): " << caller_name << " requires that parent1 be female and parent2 male, in a sexual model. If you require more flexibility than this, turn off separate sexes and track the sex of individuals yourself, or use addPatternForRecombinant() instead." << EidosTerminate();

if (sex == IndividualSex::kUnspecified)
if ((chromosome_type == ChromosomeType::kX_XSexChromosome) ||
(chromosome_type == ChromosomeType::kY_YSexChromosome) ||
(chromosome_type == ChromosomeType::kZ_ZSexChromosome) ||
(chromosome_type == ChromosomeType::kW_WSexChromosome) ||
(chromosome_type == ChromosomeType::kFL_HaploidFemaleLine) ||
(chromosome_type == ChromosomeType::kML_HaploidMaleLine) ||
(chromosome_type == ChromosomeType::kNullY_YSexChromosomeWithNull))
EIDOS_TERMINATION << "ERROR (Species::InferInheritanceForCross): crossed inheritance inference for " << caller_name << " requires that sex is specified explicitly as 'M' or 'F' for chromosome type '" << chromosome_type << "' (symbol '" << chromosome->Symbol() << "'), since the haplosome configuration of that chromosome type depends upon sex." << EidosTerminate();

// all returned entries not set are NULL
*strand1 = nullptr;
*strand2 = nullptr;
*strand3 = nullptr;
*strand4 = nullptr;

// figure out the inheritance patterns, which are complex!
switch (chromosome_type)
{
// diploid types
case ChromosomeType::kA_DiploidAutosome:
{
// we require all haplosomes non-null; if the user is playing games, they need to control them
Haplosome *hap1 = parent1->haplosomes_[first_haplosome_index];
Haplosome *hap2 = parent1->haplosomes_[last_haplosome_index];
Haplosome *hap3 = parent2->haplosomes_[first_haplosome_index];
Haplosome *hap4 = parent2->haplosomes_[last_haplosome_index];

if (hap1->IsNull() || hap2->IsNull() || hap3->IsNull() || hap4->IsNull())
EIDOS_TERMINATION << "ERROR (Species::InferInheritanceForCross): crossed inheritance inference for " << caller_name << " requires that all four parental strands are not null haplosomes for chromosome type 'A'), since the parental strands are supposed to be crossed. Use addPatternForRecombinant() to control more complex inheritance patterns." << EidosTerminate();

*strand1 = hap1;
*strand2 = hap2;
*strand3 = hap3;
*strand4 = hap4;
break;
}
case ChromosomeType::kH_HaploidAutosome:
{
// we require all haplosomes non-null; if the user is playing games, they need to control them
Haplosome *hap1 = parent1->haplosomes_[first_haplosome_index];
Haplosome *hap3 = parent2->haplosomes_[first_haplosome_index];

if (hap1->IsNull() || hap3->IsNull())
EIDOS_TERMINATION << "ERROR (Species::InferInheritanceForCross): crossed inheritance inference for " << caller_name << " requires that both parental strands are not null haplosomes for chromosome type 'H'), since the strands from the two parents are supposed to be crossed. Use addPatternForRecombinant() to control more complex inheritance patterns." << EidosTerminate();

*strand1 = hap1;
*strand2 = hap3;
break;
}
case ChromosomeType::kX_XSexChromosome:
{
// females are XX, males are X-
Haplosome *hap1 = parent1->haplosomes_[first_haplosome_index];
Haplosome *hap2 = parent1->haplosomes_[last_haplosome_index];
Haplosome *hap3 = parent2->haplosomes_[first_haplosome_index]; // hap4 is null

if (sex == IndividualSex::kMale)
{
// first offspring X is crossed from the female, second is null (a Y was inherited instead)
*strand1 = hap1;
*strand2 = hap2;
}
else
{
// first offspring X is crossed from the female, second is clonal from the male
*strand1 = hap1;
*strand2 = hap2;
*strand3 = hap3;
}
break;
}
case ChromosomeType::kY_YSexChromosome:
case ChromosomeType::kML_HaploidMaleLine:
{
// females are -, males are Y
Haplosome *hap3 = parent2->haplosomes_[first_haplosome_index];

if (sex == IndividualSex::kMale)
{
// offspring Y is inherited from the male
*strand1 = hap3;
}
break;
}
case ChromosomeType::kZ_ZSexChromosome:
{
// females are -Z, males are ZZ
Haplosome *hap2 = parent1->haplosomes_[last_haplosome_index]; // hap1 is null
Haplosome *hap3 = parent2->haplosomes_[first_haplosome_index];
Haplosome *hap4 = parent2->haplosomes_[last_haplosome_index];

if (sex == IndividualSex::kMale)
{
// first offspring Z is clonal from the female, second is crossed from the male
*strand1 = hap2;
*strand3 = hap3;
*strand4 = hap4;
}
else
{
// first offspring Z is null (a W was inherited instead), second is crossed from the male
*strand3 = hap3;
*strand4 = hap4;
}
break;
}
case ChromosomeType::kW_WSexChromosome:
case ChromosomeType::kFL_HaploidFemaleLine:
{
// females are W, males are -
Haplosome *hap1 = parent1->haplosomes_[first_haplosome_index];

if (sex == IndividualSex::kFemale)
{
// offspring W is inherited from the female
*strand1 = hap1;
}
break;
}
case ChromosomeType::kHF_HaploidFemaleInherited:
{
Haplosome *hap1 = parent1->haplosomes_[first_haplosome_index];
*strand1 = hap1;
break;
}
case ChromosomeType::kHM_HaploidMaleInherited:
{
Haplosome *hap3 = parent2->haplosomes_[first_haplosome_index];
*strand1 = hap3;
break;
}
case ChromosomeType::kNullY_YSexChromosomeWithNull:
{
// females are --, males are -Y
Haplosome *hap4 = parent2->haplosomes_[last_haplosome_index];

if (sex == IndividualSex::kMale)
{
// offspring Y is inherited from the male, to the second offspring haplosome
*strand3 = hap4;
}
break;
}
case ChromosomeType::kHNull_HaploidAutosomeWithNull:
{
EIDOS_TERMINATION << "ERROR (Population::GenerateIndividualCrossed): chromosome type 'H-' does not allow reproduction by biparental cross (only cloning); chromosome type 'H' provides greater flexibility for modeling haploids." << EidosTerminate();
}
}

// this method always randomizes the initial copy strand; even if randomizeStrands=F is passed
// to addMultiRecombinant(), inferred crosses should still behave like regular crosses
Eidos_RNG_State *rng_state = EIDOS_STATE_RNG(omp_get_thread_num());

if (*strand1 && *strand2 && Eidos_RandomBool(rng_state))
std::swap(*strand1, *strand2);
if (*strand3 && *strand4 && Eidos_RandomBool(rng_state))
std::swap(*strand3, *strand4);
}

void Species::Species_CheckIntegrity(void)
{
#if DEBUG
Expand Down
8 changes: 8 additions & 0 deletions core/species.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,10 @@ class Species : public EidosDictionaryUnretained
void SimulationHasFinished(void);
void Species_CheckIntegrity(void);

// Reproduction pattern inference
void InferInheritanceForClone(Chromosome *chromosome, Individual *parent, IndividualSex sex, Haplosome **strand1, Haplosome **strand3, const char *caller_name);
void InferInheritanceForCross(Chromosome *chromosome, Individual *parent1, Individual *parent2, IndividualSex sex, Haplosome **strand1, Haplosome **strand2, Haplosome **strand3, Haplosome **strand4, const char *caller_name);

// Shared shuffle buffer to save
inline bool RandomizingCallbackOrder(void) { return shuffle_buf_is_enabled_; }
slim_popsize_t *BorrowShuffleBuffer(slim_popsize_t p_buffer_size);
Expand Down Expand Up @@ -658,6 +662,10 @@ class Species : public EidosDictionaryUnretained

virtual EidosValue_SP ExecuteInstanceMethod(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter) override;

EidosValue_SP ExecuteMethod_addPatternForClone(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_addPatternForCross(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_addPatternForNull(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_addPatternForRecombinant(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_addSubpop(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_addSubpopSplit(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
EidosValue_SP ExecuteMethod_chromosomesOfType(EidosGlobalStringID p_method_id, const std::vector<EidosValue_SP> &p_arguments, EidosInterpreter &p_interpreter);
Expand Down
Loading

0 comments on commit 8d12555

Please sign in to comment.