Skip to content

Commit

Permalink
Add function DiploidPopulation.add_mutation. (#764)
Browse files Browse the repository at this point in the history
Add ability to drop mutations onto populations with existing ancestry.
  • Loading branch information
molpopgen committed Jun 14, 2021
1 parent 77d6619 commit 1b946d4
Show file tree
Hide file tree
Showing 14 changed files with 872 additions and 1 deletion.
5 changes: 5 additions & 0 deletions doc/misc/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ Bug fixes
{pr}`766`
{issue}`765`

New features

* Add {func}`fwdpy11.DiploidPopulation.add_mutation`.
* Add {class}`fwdpy11.NewMutationData`.

C++ back-end

* A population can now be checked that it is- or is not- being simulated.
Expand Down
3 changes: 3 additions & 0 deletions doc/pages/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ print(fwdpy11.NOGROWTH)
.. autoattribute:: __init__
.. autoclass:: fwdpy11.NewMutationData
:members:
```


7 changes: 6 additions & 1 deletion fwdpy11/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ set (ARRAY_PROXY_SOURCES src/array_proxies/init.cc)
set (MUTATION_DOMINANCE_SOURCES src/mutation_dominance/init.cc
src/mutation_dominance/MutationDominance.cc)

set (FUNCTION_SOURCES src/functions/init.cc
src/functions/add_mutation.cc
src/functions/_add_mutation.cc)

set(ALL_SOURCES ${FWDPP_TYPES_SOURCES}
${FWDPY11_TYPES_SOURCES}
${REGION_SOURCES}
Expand All @@ -112,7 +116,8 @@ set(ALL_SOURCES ${FWDPP_TYPES_SOURCES}
${EVOLVE_POPULATION_SOURCES}
${DISCRETE_DEMOGRAPHY_SOURCES}
${ARRAY_PROXY_SOURCES}
${MUTATION_DOMINANCE_SOURCES})
${MUTATION_DOMINANCE_SOURCES}
${FUNCTION_SOURCES})

set(LTO_OPTIONS)
if(ENABLE_PROFILING OR DISABLE_LTO)
Expand Down
1 change: 1 addition & 0 deletions fwdpy11/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
DiploidPopulation,
TreeIterator,
VariantIterator,
NewMutationData,
) # NOQA
from ._types.demography_debugger import DemographyDebugger # NOQA
from ._types.model_params import ModelParams, MutationAndRecombinationRates # NOQA
Expand Down
1 change: 1 addition & 0 deletions fwdpy11/_types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@
from .data_matrix_iterator import DataMatrixIterator # NOQA
from .diploid_population import DiploidPopulation # NOQA
from .model_params import ModelParams # NOQA
from .new_mutation_data import NewMutationData # NOQA
82 changes: 82 additions & 0 deletions fwdpy11/_types/diploid_population.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from .._fwdpy11 import DiploidGenotype, DiploidMetadata, ll_DiploidPopulation
from .model_params import ModelParams
from .new_mutation_data import NewMutationData
from .population_mixin import PopulationMixin
from .table_collection import TableCollection

Expand Down Expand Up @@ -321,3 +322,84 @@ def sample_timepoints(
md.flags.writeable = False
nodes.flags.writeable = False
yield self.generation, nodes, md

def add_mutation(
self,
rng: fwdpy11.GSLrng,
*,
window: Tuple[float, float] = None,
ndescendants: int = 1,
deme: Optional[int] = None,
data: NewMutationData = None,
) -> Optional[int]:
"""
Add a new mutation to the population.
.. versionadded:: 0.16.0
:param rng: Random number generator
:type rng: fwdpy11.GSLrng
The following arguments are keyword-only:
:param window: A window [left, right) within which to place the mutation.
The default is `None`, meaning the window is the entire
genome.
:type window: tuple[float, float]
:param ndescendants: The number of alive nodes carrying the new mutation.
Default is `1`, implying that a singleton mutation
will be generated.
:type ndescendants: int
:param deme: The deme in which to place the new mutation
The default is `None`, meaning that alive node demes are
not considered.
:type deme: int
:type data: The details of the new mutation
:type data: fwdpy11.NewMutationData
:returns: The key of the new mutation.
(The index of the variant in :attr:`DiploidPopulation.mutations`.)
Implementation details:
* A set of nodes are found within `window` that are ancestral to
exactly `ndescendants` alive nodes.
* From this list of candidate nodes, one is chosen randomly
to be the node where we place the new mutation.
* This node's time is the origin time of the new mutation.
* If `deme` is None, any set of `ndescendants` will be considered.
* If `deme` is :math:`\geq 0`, all `ndescendants` alive nodes must be
from that deme.
* If the `deme`/`ndescendants` requirements cannot be satisified,
the function returns `None`.
* The genetic values of individuals are not updated by this function.
Such updates will happen when the population begins evolution
forwards in time.
Exceptions:
:raises ValueError: for bad input.
:raises RuntimeError: if this function is applied during a simulation
:raises RuntimeError: if internal checks fail.
"""
if window is None:
_window = (0.0, self.tables.genome_length)
else:
_window = window

if deme is None:
_deme = -1
else:
_deme = deme

if ndescendants is None:
raise ValueError(f"ndescendants must be > 0: got {ndescendants}")

from fwdpy11._fwdpy11 import _add_mutation

key = _add_mutation(
rng, _window[0], _window[1], ndescendants, _deme, data, self
)
if key == np.iinfo(np.uint64).max:
return None
return key
50 changes: 50 additions & 0 deletions fwdpy11/_types/new_mutation_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import typing

import attr

from .._fwdpy11 import ll_NewMutationData


@attr.s(auto_attribs=True, frozen=True, repr_ns="fwdpy11", kw_only=True)
class NewMutationData(ll_NewMutationData):
"""
Data object for :func:`fwdpy11.DiploidPopulation.add_mutation`
.. versionadded:: 0.16.0
This class has the following attributes, whose names
are also `kwargs` for intitialization. The attribute names
also determine the order of positional arguments:
:param effect_size: The fixed/univariate effect size.
Will fill in :attr:`fwdpy11.Mutation.s`
:type effect_size: float
:param dominance: Heterozygous effect of the mutation.
Will fill in :attr:`fwdpy11.Mutation.h`
:type dominance: float
:param esizes: Data to fill :attr:`fwdpy11.Mutation.esizes`.
Default is `None`.
:type esizes: list[float]
:param heffects: Data to fill :attr:`fwdpy11.Mutation.heffects`.
Default is `None`.
:type heffects: list[float]
:param label: Data for :attr:`fwdpy11.Mutation.label`.
Default is 0.
:type label: int
"""

effect_size: float
dominance: float
esizes: typing.Optional[typing.List[float]] = None
heffects: typing.Optional[typing.List[float]] = None
label: int = 0

def __attrs_post_init__(self):
if self.esizes is None and self.heffects is None:
super(NewMutationData, self).__init__(
self.effect_size, self.dominance, [], [], self.label
)
else:
super(NewMutationData, self).__init__(
self.effect_size, self.dominance, self.esizes, self.heffects, self.label
)
2 changes: 2 additions & 0 deletions fwdpy11/src/_fwdpy11.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ void init_ts(py::module &);
void init_evolution_functions(py::module &);
void init_discrete_demography(py::module &m);
void init_array_proxies(py::module &m);
void initialize_functions(py::module & m);

PYBIND11_MODULE(_fwdpy11, m)
{
Expand All @@ -37,6 +38,7 @@ PYBIND11_MODULE(_fwdpy11, m)
init_evolution_functions(m);
init_discrete_demography(m);
init_array_proxies(m);
initialize_functions(m);

py::register_exception<fwdpy11::GSLError>(m, "GSLError");

Expand Down
34 changes: 34 additions & 0 deletions fwdpy11/src/functions/_add_mutation.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Copyright (C) 2021 Kevin Thornton <krthornt@uci.edu>
//
// This file is part of fwdpy11.
//
// fwdpy11 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// fwdpy11 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with fwdpy11. If not, see <http://www.gnu.org/licenses/>.
//
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "add_mutation.hpp"

namespace py = pybind11;

void
init_add_mutation(py::module& m)
{
m.def("_add_mutation", &add_mutation);

py::class_<new_mutation_data>(m, "ll_NewMutationData")
.def(py::init<double, double, std::vector<double>, std::vector<double>,
decltype(fwdpy11::Mutation::xtra)>());
}

0 comments on commit 1b946d4

Please sign in to comment.