Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

include validation function that makes sense for individual-based simulators #236

Open
grahamgower opened this issue Mar 3, 2021 · 4 comments

Comments

@grahamgower
Copy link
Member

grahamgower commented Mar 3, 2021

We currently allow things that are permissible in a coalescent framework, but makes no sense for individual-based and/or discrete-time simulators. E.g.

  • 0 < N < 1, and
  • epoch and migration time spans less than 1 generation in length (either directly, or indirectly when using time_units!="generations").

One way for an individual-based simulator to check this, is to convert the relevant numbers to integers, then rebuild the graph. This will be (or should be) a fairly common operation, so it seems reasonable that we should provide such functionality as part of demes.

What if we had something like this?

class Graph:

    ...

    def frob_time_and_size(self, func):
        """
        Return a copy of the graph with times and sizes modified by func.
        """
        b = Builder.fromdict(self.asdict())

        for deme in b.get("demes", []):
            deme["start_time"] = func(deme["start_time"])
            for epoch in deme.get("epochs", []):
                epoch["start_time"] = func(epoch["start_time"])
                epoch["end_time"] = func(epoch["end_time"])
                epoch["start_size"] = func(epoch["start_size"])
                epoch["end_size"] = func(epoch["end_size"])

        for migration in b.get("migrations", []):
            migration["start_time"] = func(migration["start_time"])
            migration["end_time"] = func(migration["end_time"])

        for pulse in b.get("pulse", []):
            pulse["time"] = func(pulse["time"])

        return b.resolve()

And then a simulator would call graph.frob_time_and_size(int). Or to scale the simulation by a factor Q: graph.frob_time_and_size(lambda x: int(x / Q)).

@grahamgower grahamgower transferred this issue from popsim-consortium/demes-spec Mar 3, 2021
@jeromekelleher
Copy link
Member

Seems reasonable - I expect @molpopgen will have thoughts?

@grahamgower
Copy link
Member Author

On second though, it should probably be frob_time_and_size(self, time_func, size_func), so that Graph.in_generations() can be implemented using it.

@molpopgen
Copy link
Collaborator

I'm starting to dust the cobwebs off here and will be thinking about this soon. @apragsdale has some prototype code that'll be the basis for the next fwdpy11 release. I'm sure we will come across what's necessary to check while working on that.

@molpopgen
Copy link
Collaborator

@apragsdale -- let's add a test of this when working on the fwdpy11 integration. We can just make a model with some small time intervals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants