SLiM icon indicating copy to clipboard operation
SLiM copied to clipboard

Allow for expressions in Eidos event times

Open vsbuffalo opened this issue 6 years ago • 3 comments

I think a simple feature that could make Eidos a lot more extensible would be to allow for expressions in the generation that Eidos events are triggered. For example, (modifying an example from the manual):

initialize() {

defineConstant("N", 1000);
initializeMutationRate(1e-7);

initializeMutationType("m1", 0.5, "f", 0.0);

initializeGenomicElementType("g1", m1, 1.0);

initializeGenomicElement(g1, 0, 99999);

initializeRecombinationRate(1e-8);

}


1 { sim.addSubpop("p1", N); }


// start outputting after burnin, for twenty generations
(10*N):(10*N+20) late() { sim.outputFull(); }
10*N + 100 { sim.simulationFinished();
 }

Overall, this would allow for burnins that are dependent on N, which is what the manual recommends and allow N to be varied across runs easily. I can imagine allowing for any arbitrary expression might slow things down too much, so perhaps an expression of constants only that's run at initialization would suffice.

vsbuffalo avatar Dec 19 '18 02:12 vsbuffalo

Yes, this sort of thing would be useful. There are two problems. One, script blocks get parsed and set up in the scheduler before anything gets executed, so the value of N is not known at the time that the expression would be encountered. And two, "constants" are not really constant; N can be removed with rm() and re-defined with a different value, so the expressions involving N are not truly constant expressions. Those two difficulties have prevented me from doing anything with this sort of idea, until such time as I come with a solution. However, rescheduleScriptBlock() makes it pretty easy to achieve this sort of thing anyway, so it's mostly a matter of convenience; the functionality you want is fairly easily achieved, just with a less intuitive and pretty syntax than one might wish.

bhaller avatar Dec 19 '18 02:12 bhaller

Rather than using rescheduleScriptBlock(), which requires that an existing block be defined, I've been using registerLateEvent(). So the example given would become:

initialize() {

defineConstant("N", 1000);
initializeMutationRate(1e-7);

initializeMutationType("m1", 0.5, "f", 0.0);

initializeGenomicElementType("g1", m1, 1.0);

initializeGenomicElement(g1, 0, 99999);

initializeRecombinationRate(1e-8);

}


1 {
    sim.addSubpop("p1", N);
    // start outputting after burnin, for twenty generations
    sim.registerLateEvent(NULL, "{ sim.outputFull(); }", 10*N, 10*N+20);
}


For longer expressions, I create a function that is scheduled using registerLateEvent. The major disadvantage of this approach is that syntax errors in the lambda expressions don't have the appropriate source code line reported.

grahamgower avatar Jul 22 '19 10:07 grahamgower

Rather than using rescheduleScriptBlock(), which requires that an existing block be defined, I've been using registerLateEvent(). ... The major disadvantage of this approach is that syntax errors in the lambda expressions don't have the appropriate source code line reported.

Yes; with rescheduleScriptBlock() errors are correctly attributed. Error attribution within lambdas and user-defined functions is something that could use some work in Eidos...

bhaller avatar Jul 22 '19 13:07 bhaller

Well, it took a while, but it got fixed! Some architectural improvements were needed before it was practical to do. Any arbitrary expression is now allowed, except that variables can't be involved, only constants. Not clear what it would really mean to use variables anyway. :-> So you can do:

N+20 early() { ... }

and such, if N is a defined constant.

bhaller avatar Mar 09 '24 02:03 bhaller

Thanks Ben! This looks super nice!

vsbuffalo avatar Mar 09 '24 04:03 vsbuffalo

Amazing!

bryce-carson avatar Mar 21 '24 19:03 bryce-carson