Skip to content

Commit 843aa83

Browse files
committed
redesign mateChoice() callback internals for speed
1 parent 7c992c6 commit 843aa83

File tree

9 files changed

+429
-117
lines changed

9 files changed

+429
-117
lines changed

VERSIONS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ multitrait branch:
124124
switch fitness calculation over to being based upon the calculated values of traits, rather than directly upon mutations
125125
eliminate overhead for setting up fitness buffers in neutral WF models; this should provide a significant speedup for such models, if they don't use a mateChoice() callback
126126
extend recalculateFitness() with [logical$ forceRecalc = T] option for backward compatibility, but allowing trait value recalculation not to be forced
127+
redesigned the internals of mateChoice() callbacks; there should be no user-visible consequence apart from better performance (even without mateChoice() callbacks!)
127128

128129

129130
version 5.1 (Eidos version 4.1):

core/population.cpp

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

core/slim_test.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ int RunSLiMTests(void)
435435
_RunIndividualTests();
436436
_RunSubstitutionTests();
437437
_RunSLiMEidosBlockTests();
438+
_RunMateChoiceTests();
438439
_RunContinuousSpaceTests();
439440
_RunSpatialMapTests();
440441
_RunNonWFTests();

core/slim_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ extern void _RunRelatednessTests(void);
5757
extern void _RunInteractionTypeTests(void);
5858
extern void _RunSubstitutionTests(void);
5959
extern void _RunSLiMEidosBlockTests(void);
60+
extern void _RunMateChoiceTests(void);
6061
extern void _RunContinuousSpaceTests(void);
6162
extern void _RunSpatialMapTests(void);
6263
extern void _RunNonWFTests(void);

core/slim_test_core.cpp

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2664,6 +2664,275 @@ void _RunSLiMEidosBlockTests(void)
26642664
SLiMAssertScriptRaise(tickexpr9, "unrecognized function name tuck", __LINE__);
26652665
}
26662666

2667+
#pragma mark mateChoice() callback tests
2668+
void _RunMateChoiceTests(void)
2669+
{
2670+
// With the multitrait work, I completely redesigned how mateChoice() callbacks work under the hood. They
2671+
// should be much faster, but their logic is a bit tricky, so I'm adding a raft of new tests.
2672+
2673+
// This script tags everybody, and marks one individual as the preferred mate. It then confirms that that
2674+
// mate was chosen after mating has completed. This is the basis of all the mateChoice() tests here.
2675+
std::string verifiableMating1(R"V0G0N(
2676+
initialize() {}
2677+
1 early() {
2678+
sim.addSubpop("p1", 50);
2679+
}
2680+
early() {
2681+
p1.individuals.tag = 0;
2682+
p1.sampleIndividuals(1).tag = 1;
2683+
}
2684+
mateChoice() {
2685+
return p1.subsetIndividuals(tag=1);
2686+
}
2687+
modifyChild() {
2688+
child.tag = parent2.tag;
2689+
return T;
2690+
}
2691+
1:100 late() { if (any(p1.individuals.tag != 1)) stop(); }
2692+
)V0G0N");
2693+
SLiMAssertScriptSuccess(verifiableMating1);
2694+
2695+
// add a no-op mateChoice() callback before the main one
2696+
std::string verifiableMating2(R"V0G0N(
2697+
initialize() {}
2698+
1 early() {
2699+
sim.addSubpop("p1", 50);
2700+
}
2701+
early() {
2702+
p1.individuals.tag = 0;
2703+
p1.sampleIndividuals(1).tag = 1;
2704+
}
2705+
mateChoice() {
2706+
return NULL;
2707+
}
2708+
mateChoice() {
2709+
return p1.subsetIndividuals(tag=1);
2710+
}
2711+
modifyChild() {
2712+
child.tag = parent2.tag;
2713+
return T;
2714+
}
2715+
1:100 late() { if (any(p1.individuals.tag != 1)) stop(); }
2716+
)V0G0N");
2717+
SLiMAssertScriptSuccess(verifiableMating2);
2718+
2719+
// choose a random mate, and then change our minds
2720+
std::string verifiableMating3(R"V0G0N(
2721+
initialize() {}
2722+
1 early() {
2723+
sim.addSubpop("p1", 50);
2724+
}
2725+
early() {
2726+
p1.individuals.tag = 0;
2727+
p1.sampleIndividuals(1).tag = 1;
2728+
}
2729+
mateChoice() {
2730+
return p1.sampleIndividuals(1);
2731+
}
2732+
mateChoice() {
2733+
return p1.subsetIndividuals(tag=1);
2734+
}
2735+
modifyChild() {
2736+
child.tag = parent2.tag;
2737+
return T;
2738+
}
2739+
1:100 late() { if (any(p1.individuals.tag != 1)) stop(); }
2740+
)V0G0N");
2741+
SLiMAssertScriptSuccess(verifiableMating3);
2742+
2743+
// return a random weights vector, and then change our minds
2744+
std::string verifiableMating4(R"V0G0N(
2745+
initialize() {}
2746+
1 early() {
2747+
sim.addSubpop("p1", 50);
2748+
}
2749+
early() {
2750+
p1.individuals.tag = 0;
2751+
p1.sampleIndividuals(1).tag = 1;
2752+
}
2753+
mateChoice() {
2754+
return runif(subpop.individualCount);
2755+
}
2756+
mateChoice() {
2757+
return p1.subsetIndividuals(tag=1);
2758+
}
2759+
modifyChild() {
2760+
child.tag = parent2.tag;
2761+
return T;
2762+
}
2763+
1:100 late() { if (any(p1.individuals.tag != 1)) stop(); }
2764+
)V0G0N");
2765+
SLiMAssertScriptSuccess(verifiableMating4);
2766+
2767+
// do some random action, and then change our minds
2768+
std::string verifiableMating5(R"V0G0N(
2769+
initialize() {}
2770+
1 early() {
2771+
sim.addSubpop("p1", 50);
2772+
}
2773+
early() {
2774+
p1.individuals.tag = 0;
2775+
p1.sampleIndividuals(1).tag = 1;
2776+
}
2777+
mateChoice() {
2778+
if (runif(1) < 0.3)
2779+
return float(0);
2780+
if (runif(1) < 0.3)
2781+
return p1.sampleIndividuals(1);
2782+
if (runif(1) < 0.3)
2783+
return runif(subpop.individualCount);
2784+
if (runif(1) < 0.3)
2785+
return weights;
2786+
return NULL;
2787+
}
2788+
mateChoice() {
2789+
return p1.subsetIndividuals(tag=1);
2790+
}
2791+
modifyChild() {
2792+
child.tag = parent2.tag;
2793+
return T;
2794+
}
2795+
1:100 late() { if (any(p1.individuals.tag != 1)) stop(); }
2796+
)V0G0N");
2797+
SLiMAssertScriptSuccess(verifiableMating5);
2798+
2799+
// choose the right individual, then return a weights vector representing that individual
2800+
std::string verifiableMating6(R"V0G0N(
2801+
initialize() {}
2802+
1 early() {
2803+
sim.addSubpop("p1", 50);
2804+
}
2805+
early() {
2806+
p1.individuals.tag = 0;
2807+
p1.sampleIndividuals(1).tag = 1;
2808+
}
2809+
mateChoice() {
2810+
return p1.subsetIndividuals(tag=1);
2811+
}
2812+
mateChoice() {
2813+
return weights;
2814+
}
2815+
modifyChild() {
2816+
child.tag = parent2.tag;
2817+
return T;
2818+
}
2819+
1:100 late() { if (any(p1.individuals.tag != 1)) stop(); }
2820+
)V0G0N");
2821+
SLiMAssertScriptSuccess(verifiableMating6);
2822+
2823+
// choose the right individual, then return a weights vector representing that individual
2824+
std::string verifiableMating7(R"V0G0N(
2825+
initialize() {}
2826+
1 early() {
2827+
sim.addSubpop("p1", 50);
2828+
}
2829+
early() {
2830+
p1.individuals.tag = 0;
2831+
p1.sampleIndividuals(1).tag = 1;
2832+
}
2833+
mateChoice() {
2834+
return p1.subsetIndividuals(tag=1);
2835+
}
2836+
mateChoice() {
2837+
return weights * runif(subpop.individualCount);
2838+
}
2839+
modifyChild() {
2840+
child.tag = parent2.tag;
2841+
return T;
2842+
}
2843+
1:100 late() { if (any(p1.individuals.tag != 1)) stop(); }
2844+
)V0G0N");
2845+
SLiMAssertScriptSuccess(verifiableMating7);
2846+
2847+
// add a bit of stochasticity to the previous, so that the fly individual isn't always chosen
2848+
std::string verifiableMating8(R"V0G0N(
2849+
initialize() {}
2850+
1 early() {
2851+
sim.addSubpop("p1", 50);
2852+
}
2853+
early() {
2854+
p1.individuals.tag = 0;
2855+
p1.sampleIndividuals(1).tag = 1;
2856+
}
2857+
mateChoice() {
2858+
return p1.subsetIndividuals(tag=1);
2859+
}
2860+
mateChoice() {
2861+
return weights + runif(subpop.individualCount, max=0.0001);
2862+
}
2863+
modifyChild() {
2864+
child.tag = parent2.tag;
2865+
return T;
2866+
}
2867+
1:100 late() { if (mean(p1.individuals.tag != 1) > 0.1) stop(); }
2868+
)V0G0N");
2869+
SLiMAssertScriptSuccess(verifiableMating8);
2870+
2871+
// test using a global weights vector
2872+
std::string verifiableMating9(R"V0G0N(
2873+
initialize() {}
2874+
1 early() {
2875+
sim.addSubpop("p1", 50);
2876+
}
2877+
early() {
2878+
p1.individuals.tag = 0;
2879+
p1.sampleIndividuals(1).tag = 1;
2880+
2881+
defineGlobal("WEIGHTS", rep(0.0, p1.individualCount));
2882+
WEIGHTS[whichMax(p1.individuals.tag)] = 1.0;
2883+
}
2884+
mateChoice() {
2885+
return runif(subpop.individualCount);
2886+
}
2887+
mateChoice() {
2888+
return WEIGHTS;
2889+
}
2890+
modifyChild() {
2891+
child.tag = parent2.tag;
2892+
return T;
2893+
}
2894+
1:100 late() { if (mean(p1.individuals.tag != 1) > 0.1) stop(); }
2895+
)V0G0N");
2896+
SLiMAssertScriptSuccess(verifiableMating9);
2897+
2898+
// finally, try to trigger an illegal modification of the global weights vector
2899+
std::string verifiableMating10(R"V0G0N(
2900+
initialize() {}
2901+
1 early() {
2902+
sim.addSubpop("p1", 50);
2903+
}
2904+
early() {
2905+
p1.individuals.tag = 0;
2906+
p1.sampleIndividuals(1).tag = 1;
2907+
2908+
defineGlobal("WEIGHTS", rep(0.0, p1.individualCount));
2909+
WEIGHTS[whichMax(p1.individuals.tag)] = 1.0;
2910+
defineGlobal("CHECK", WEIGHTS * 2.0);
2911+
}
2912+
mateChoice() {
2913+
// first return the global, which should get shared into returned_weights
2914+
return WEIGHTS;
2915+
}
2916+
mateChoice() {
2917+
// then return a chosen individual, which should set chosen_mate
2918+
return p1.subsetIndividuals(tag=1);
2919+
}
2920+
mateChoice() {
2921+
// then use weights, forcing a new build into returned_weights
2922+
return weights * 10.0;
2923+
}
2924+
modifyChild() {
2925+
child.tag = parent2.tag;
2926+
return T;
2927+
}
2928+
1:100 late() {
2929+
if (!identical(WEIGHTS * 2.0, CHECK)) stop();
2930+
if (mean(p1.individuals.tag != 1) > 0.1) stop();
2931+
}
2932+
)V0G0N");
2933+
SLiMAssertScriptSuccess(verifiableMating10);
2934+
}
2935+
26672936

26682937

26692938

core/species.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4455,8 +4455,8 @@ void Species::TabulateSLiMMemoryUsage_Species(SLiMMemoryUsage_Species *p_usage)
44554455

44564456
if (subpop.cached_parental_fitness_)
44574457
p_usage->subpopulationFitnessCaches += subpop.cached_fitness_capacity_ * sizeof(double);
4458-
if (subpop.cached_male_fitness_)
4459-
p_usage->subpopulationFitnessCaches += subpop.cached_fitness_capacity_ * sizeof(double);
4458+
if (subpop.mate_choice_weights_)
4459+
p_usage->subpopulationFitnessCaches += subpop.mate_choice_weights_->Count() * sizeof(double);
44604460

44614461
p_usage->subpopulationParentTables += subpop.MemoryUsageForParentTables();
44624462

0 commit comments

Comments
 (0)