Skip to content

Commit bd1ae74

Browse files
committed
shift from taus2 and MT19937-64 to PCG RNGs
1 parent 68ad4b2 commit bd1ae74

27 files changed

Lines changed: 3457 additions & 890 deletions

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ if(CPPCHECK)
9090
find_program(CPPCHECK_EXECUTABLE cppcheck)
9191
if (CPPCHECK_EXECUTABLE)
9292
message(STATUS "CPPCHECK is ${CPPCHECK}; building with cppcheck (for development)")
93-
set(CPPCHECK_COMMAND "${CPPCHECK_EXECUTABLE}" "--enable=all;--suppress=missingIncludeSystem;--suppress=syntaxError;--suppress=unmatchedSuppression;--inline-suppr;--std=c++11;--quiet;--suppress=*:robin_hood.h;--suppress=*:lodepng.cpp;--suppress=checkersReport;--suppress=*:eidos_openmp.h;--suppress=unusedFunction")
93+
set(CPPCHECK_COMMAND "${CPPCHECK_EXECUTABLE}" "--enable=all;--suppress=missingIncludeSystem;--suppress=syntaxError;--suppress=unmatchedSuppression;--inline-suppr;--std=c++11;--quiet;--suppress=*:robin_hood.h;--suppress=*:lodepng.cpp;--suppress=*:pcg_extras.hpp;--suppress=*:pcg_random.hpp;--suppress=checkersReport;--suppress=*:eidos_openmp.h;--suppress=unusedFunction")
9494
message(STATUS "+++ cppcheck is at ${CPPCHECK_EXECUTABLE}")
9595
message(STATUS "+++ CPPCHECK_COMMAND is ${CPPCHECK_COMMAND}")
9696
else()

SLiM.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,8 @@
21892189
98D7D6642AB24CBC002AFE34 /* chisq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = chisq.c; sourceTree = "<group>"; };
21902190
98D7EBEE28CE557C00DEAAC4 /* eidos_multi */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = eidos_multi; sourceTree = BUILT_PRODUCTS_DIR; };
21912191
98D7ED2D28CE58FC00DEAAC4 /* slim_multi */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = slim_multi; sourceTree = BUILT_PRODUCTS_DIR; };
2192+
98D957882EB53494008314C1 /* pcg_extras.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = pcg_extras.hpp; sourceTree = "<group>"; };
2193+
98D957892EB53494008314C1 /* pcg_random.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = pcg_random.hpp; sourceTree = "<group>"; };
21922194
98DB3D6D1E6122AE00E2C200 /* interaction_type.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = interaction_type.cpp; sourceTree = "<group>"; };
21932195
98DB3D6E1E6122AE00E2C200 /* interaction_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interaction_type.h; sourceTree = "<group>"; };
21942196
98DC9838289986B300160DD8 /* GitSHA1.cpp.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GitSHA1.cpp.in; sourceTree = "<group>"; };
@@ -2389,6 +2391,8 @@
23892391
98235687252FDD120096A745 /* lodepng.h */,
23902392
98235681252FDCF50096A745 /* lodepng.cpp */,
23912393
98186DB8254A8B1600F9118C /* robin_hood.h */,
2394+
98D957882EB53494008314C1 /* pcg_extras.hpp */,
2395+
98D957892EB53494008314C1 /* pcg_random.hpp */,
23922396
);
23932397
name = dependencies;
23942398
sourceTree = "<group>";

SLiM.xcodeproj/xcshareddata/xcschemes/SLiM.xcscheme

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
</Testables>
4141
</TestAction>
4242
<LaunchAction
43-
buildConfiguration = "Debug"
43+
buildConfiguration = "Release"
4444
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
4545
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
4646
launchStyle = "0"
@@ -70,7 +70,7 @@
7070
</CommandLineArgument>
7171
<CommandLineArgument
7272
argument = "-time"
73-
isEnabled = "YES">
73+
isEnabled = "NO">
7474
</CommandLineArgument>
7575
<CommandLineArgument
7676
argument = "-d MUTDEFS=\&apos;initializeMutationType(\\&quot;m1\\&quot;, 0.5, &quot;f&quot;, 0.0);\&apos; &quot;/Users/bhaller/Desktop/cmdline test.slim&quot;"
@@ -86,7 +86,7 @@
8686
</CommandLineArgument>
8787
<CommandLineArgument
8888
argument = "-mem"
89-
isEnabled = "YES">
89+
isEnabled = "NO">
9090
</CommandLineArgument>
9191
<CommandLineArgument
9292
argument = "-define &quot;N=1000&quot; -d &quot;THETA=5&quot; -d &quot;mu=THETA/(4*N)&quot;"
@@ -98,7 +98,7 @@
9898
</CommandLineArgument>
9999
<CommandLineArgument
100100
argument = "-testSLiM"
101-
isEnabled = "NO">
101+
isEnabled = "YES">
102102
</CommandLineArgument>
103103
<CommandLineArgument
104104
argument = "-x"

VERSIONS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ development head (in the master branch):
1515
extend text() to support drawing text and an angle, with new [float angle = 0.0] parameter
1616
add mtext() call to Plot, for drawing text in the margins outside the plot area
1717
add rowSums() and colSums() functions to Eidos, for use with matrices as a faster alternative to apply()
18+
add the PCG random number generator, switch to pcg32_fast and pcg64_fast, remove all use of the old taus2 and MT19937-64 generators; note this completely breaks backward reproducibility
1819

1920

2021
version 5.1 (Eidos version 4.1):

core/chromosome.cpp

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -990,19 +990,17 @@ int Chromosome::DrawSortedUniquedMutationPositions(int p_count, IndividualSex p_
990990
}
991991

992992
// draw all the positions, and keep track of the genomic element type for each
993-
gsl_rng *rng = EIDOS_GSL_RNG(omp_get_thread_num());
994-
Eidos_MT_State *mt = EIDOS_MT_RNG(omp_get_thread_num());
993+
gsl_rng *rng_gsl = EIDOS_GSL_RNG(omp_get_thread_num());
994+
EidosRNG_64_bit &rng_64 = EIDOS_64BIT_RNG(omp_get_thread_num());
995995

996996
for (int i = 0; i < p_count; ++i)
997997
{
998-
int mut_subrange_index = static_cast<int>(gsl_ran_discrete(rng, lookup));
998+
int mut_subrange_index = static_cast<int>(gsl_ran_discrete(rng_gsl, lookup));
999999
const GESubrange &subrange = (*subranges)[mut_subrange_index];
10001000
GenomicElement *source_element = subrange.genomic_element_ptr_;
10011001

10021002
// Draw the position along the chromosome for the mutation, within the genomic element
1003-
slim_position_t position = subrange.start_position_ + static_cast<slim_position_t>(Eidos_rng_uniform_int_MT64(mt, subrange.end_position_ - subrange.start_position_ + 1));
1004-
// old 32-bit position not MT64 code:
1005-
//slim_position_t position = subrange.start_position_ + static_cast<slim_position_t>(Eidos_rng_uniform_int(rng, (uint32_t)(subrange.end_position_ - subrange.start_position_ + 1)));
1003+
slim_position_t position = subrange.start_position_ + static_cast<slim_position_t>(Eidos_rng_interval_uint64(rng_64, subrange.end_position_ - subrange.start_position_ + 1));
10061004

10071005
p_positions.emplace_back(position, source_element);
10081006
}
@@ -1310,8 +1308,8 @@ MutationIndex Chromosome::DrawNewMutationExtended(std::pair<slim_position_t, Gen
13101308

13111309
// OK, now we know the background nucleotide; determine the mutation rates to derived nucleotides
13121310
double *nuc_thresholds = genomic_element_type.mm_thresholds + (size_t)original_nucleotide * 4;
1313-
gsl_rng *rng = EIDOS_GSL_RNG(omp_get_thread_num());
1314-
double draw = Eidos_rng_uniform(rng);
1311+
EidosRNG_64_bit &rng_64 = EIDOS_64BIT_RNG(omp_get_thread_num());
1312+
double draw = Eidos_rng_uniform_doubleCO(rng_64);
13151313

13161314
if (draw < nuc_thresholds[0]) nucleotide = 0;
13171315
else if (draw < nuc_thresholds[1]) nucleotide = 1;
@@ -1384,8 +1382,8 @@ MutationIndex Chromosome::DrawNewMutationExtended(std::pair<slim_position_t, Gen
13841382
// OK, now we know the background nucleotide; determine the mutation rates to derived nucleotides
13851383
int trinuc = ((int)background_nuc1) * 16 + ((int)original_nucleotide) * 4 + (int)background_nuc3;
13861384
double *nuc_thresholds = genomic_element_type.mm_thresholds + (size_t)trinuc * 4;
1387-
gsl_rng *rng = EIDOS_GSL_RNG(omp_get_thread_num());
1388-
double draw = Eidos_rng_uniform(rng);
1385+
EidosRNG_64_bit &rng_64 = EIDOS_64BIT_RNG(omp_get_thread_num());
1386+
double draw = Eidos_rng_uniform_doubleCO(rng_64);
13891387

13901388
if (draw < nuc_thresholds[0]) nucleotide = 0;
13911389
else if (draw < nuc_thresholds[1]) nucleotide = 1;
@@ -1486,13 +1484,13 @@ void Chromosome::_DrawCrossoverBreakpoints(IndividualSex p_parent_sex, const int
14861484
}
14871485

14881486
// draw recombination breakpoints
1489-
gsl_rng *rng = EIDOS_GSL_RNG(omp_get_thread_num());
1490-
Eidos_MT_State *mt = EIDOS_MT_RNG(omp_get_thread_num());
1487+
gsl_rng *rng_gsl = EIDOS_GSL_RNG(omp_get_thread_num());
1488+
EidosRNG_64_bit &rng_64 = EIDOS_64BIT_RNG(omp_get_thread_num());
14911489

14921490
for (int i = 0; i < p_num_breakpoints; i++)
14931491
{
14941492
slim_position_t breakpoint = 0;
1495-
int recombination_interval = static_cast<int>(gsl_ran_discrete(rng, lookup));
1493+
int recombination_interval = static_cast<int>(gsl_ran_discrete(rng_gsl, lookup));
14961494

14971495
// choose a breakpoint anywhere in the chosen recombination interval with equal probability
14981496

@@ -1508,7 +1506,7 @@ void Chromosome::_DrawCrossoverBreakpoints(IndividualSex p_parent_sex, const int
15081506
// positions to the left of its enclosed bases, up to and including the position to the left of the final base given as the
15091507
// end position of the interval. The next interval's first owned recombination position is therefore to the left of the
15101508
// base that is one position to the right of the end of the preceding interval. So we have to add one to the position
1511-
// given by recombination_end_positions_[recombination_interval - 1], at minimum. Since Eidos_rng_uniform_int() returns
1509+
// given by recombination_end_positions_[recombination_interval - 1], at minimum. Since Eidos_rng_interval_uint64() returns
15121510
// a zero-based random number, that means we need a +1 here as well.
15131511
//
15141512
// The key fact here is that a recombination breakpoint position of 1 means "break to the left of the base at position 1" –
@@ -1517,7 +1515,7 @@ void Chromosome::_DrawCrossoverBreakpoints(IndividualSex p_parent_sex, const int
15171515
// breakpoint. When their position is *equal*, the breakpoint gets serviced by switching strands. That logic causes the
15181516
// breakpoints to fall to the left of their designated base.
15191517
//
1520-
// Note that Eidos_rng_uniform_int() crashes (well, aborts fatally) if passed 0 for n. We need to guarantee that that doesn't
1518+
// Note that Eidos_rng_interval_uint64() crashes (well, aborts fatally) if passed 0 for n. We need to guarantee that that doesn't
15211519
// happen, and we don't want to waste time checking for that condition here. For a 1-base model, we are guaranteed that
15221520
// the overall recombination rate will be zero, by the logic in InitializeDraws(), and so we should not be called in the
15231521
// first place. For longer chromosomes that start with a 1-base recombination interval, the rate calculated by
@@ -1526,9 +1524,9 @@ void Chromosome::_DrawCrossoverBreakpoints(IndividualSex p_parent_sex, const int
15261524
// since we guarantee that recombination end positions are in strictly ascending order. So we should never crash. :->
15271525

15281526
if (recombination_interval == 0)
1529-
breakpoint = static_cast<slim_position_t>(Eidos_rng_uniform_int_MT64(mt, (*end_positions)[recombination_interval]) + 1);
1527+
breakpoint = static_cast<slim_position_t>(Eidos_rng_interval_uint64(rng_64, (*end_positions)[recombination_interval]) + 1);
15301528
else
1531-
breakpoint = (*end_positions)[recombination_interval - 1] + 1 + static_cast<slim_position_t>(Eidos_rng_uniform_int_MT64(mt, (*end_positions)[recombination_interval] - (*end_positions)[recombination_interval - 1]));
1529+
breakpoint = (*end_positions)[recombination_interval - 1] + 1 + static_cast<slim_position_t>(Eidos_rng_interval_uint64(rng_64, (*end_positions)[recombination_interval] - (*end_positions)[recombination_interval - 1]));
15321530

15331531
p_crossovers.emplace_back(breakpoint);
15341532
}
@@ -1604,7 +1602,9 @@ void Chromosome::_DrawDSBBreakpoints(IndividualSex p_parent_sex, const int p_num
16041602
// to a collision, because such redrawing would be liable to produce bias towards shorter extents. (Redrawing the crossover/
16051603
// noncrossover and simple/complex decisions would probably be harmless, but it is simpler to just make all decisions up front.)
16061604
int try_count = 0;
1607-
gsl_rng *rng = EIDOS_GSL_RNG(omp_get_thread_num());
1605+
gsl_rng *rng_gsl = EIDOS_GSL_RNG(omp_get_thread_num());
1606+
EidosRNG_64_bit &rng_64 = EIDOS_64BIT_RNG(omp_get_thread_num());
1607+
16081608
static std::vector<std::tuple<slim_position_t, slim_position_t, bool, bool>> dsb_infos; // using a static prevents reallocation
16091609

16101610
// If the redrawLengthsOnFailure parameter to initializeGeneConversion() is T, we jump back here on layout failure
@@ -1618,8 +1618,8 @@ void Chromosome::_DrawDSBBreakpoints(IndividualSex p_parent_sex, const int p_num
16181618
for (int i = 0; i < p_num_breakpoints; i++)
16191619
{
16201620
// If the gene conversion tract mean length is < 2.0, gsl_ran_geometric() will blow up, and we should treat the tract length as zero
1621-
bool noncrossover = (Eidos_rng_uniform(rng) <= non_crossover_fraction_); // tuple position 2
1622-
bool simple = (Eidos_rng_uniform(rng) <= simple_conversion_fraction_); // tuple position 3
1621+
bool noncrossover = (Eidos_rng_uniform_doubleCO(rng_64) <= non_crossover_fraction_); // tuple position 2
1622+
bool simple = (Eidos_rng_uniform_doubleCO(rng_64) <= simple_conversion_fraction_); // tuple position 3
16231623

16241624
dsb_infos.emplace_back(0, 0, noncrossover, simple);
16251625
}
@@ -1628,10 +1628,10 @@ void Chromosome::_DrawDSBBreakpoints(IndividualSex p_parent_sex, const int p_num
16281628
{
16291629
for (int i = 0; i < p_num_breakpoints; i++)
16301630
{
1631-
slim_position_t extent1 = gsl_ran_geometric(rng, gene_conversion_inv_half_length_); // tuple position 0
1632-
slim_position_t extent2 = gsl_ran_geometric(rng, gene_conversion_inv_half_length_); // tuple position 1
1633-
bool noncrossover = (Eidos_rng_uniform(rng) <= non_crossover_fraction_); // tuple position 2
1634-
bool simple = (Eidos_rng_uniform(rng) <= simple_conversion_fraction_); // tuple position 3
1631+
slim_position_t extent1 = gsl_ran_geometric(rng_gsl, gene_conversion_inv_half_length_); // tuple position 0
1632+
slim_position_t extent2 = gsl_ran_geometric(rng_gsl, gene_conversion_inv_half_length_); // tuple position 1
1633+
bool noncrossover = (Eidos_rng_uniform_doubleCO(rng_64) <= non_crossover_fraction_); // tuple position 2
1634+
bool simple = (Eidos_rng_uniform_doubleCO(rng_64) <= simple_conversion_fraction_); // tuple position 3
16351635

16361636
dsb_infos.emplace_back(extent1, extent2, noncrossover, simple);
16371637
}
@@ -1650,19 +1650,18 @@ void Chromosome::_DrawDSBBreakpoints(IndividualSex p_parent_sex, const int p_num
16501650
}
16511651

16521652
// First draw DSB points; dsb_points contains positions and a flag for whether the breakpoint is at a rate=0.5 position
1653-
Eidos_MT_State *mt = EIDOS_MT_RNG(omp_get_thread_num());
16541653
static std::vector<std::pair<slim_position_t, bool>> dsb_points; // using a static prevents reallocation
16551654
dsb_points.resize(0);
16561655

16571656
for (int i = 0; i < p_num_breakpoints; i++)
16581657
{
16591658
slim_position_t breakpoint = 0;
1660-
int recombination_interval = static_cast<int>(gsl_ran_discrete(rng, lookup));
1659+
int recombination_interval = static_cast<int>(gsl_ran_discrete(rng_gsl, lookup));
16611660

16621661
if (recombination_interval == 0)
1663-
breakpoint = static_cast<slim_position_t>(Eidos_rng_uniform_int_MT64(mt, (*end_positions)[recombination_interval]) + 1);
1662+
breakpoint = static_cast<slim_position_t>(Eidos_rng_interval_uint64(rng_64, (*end_positions)[recombination_interval]) + 1);
16641663
else
1665-
breakpoint = (*end_positions)[recombination_interval - 1] + 1 + static_cast<slim_position_t>(Eidos_rng_uniform_int_MT64(mt, (*end_positions)[recombination_interval] - (*end_positions)[recombination_interval - 1]));
1664+
breakpoint = (*end_positions)[recombination_interval - 1] + 1 + static_cast<slim_position_t>(Eidos_rng_interval_uint64(rng_64, (*end_positions)[recombination_interval] - (*end_positions)[recombination_interval - 1]));
16661665

16671666
if ((*rates)[recombination_interval] == 0.5)
16681667
dsb_points.emplace_back(breakpoint, true);

0 commit comments

Comments
 (0)