From 1af6d3f50d3a9af7e5e5e1a77caf50e58f25ce35 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 14 Sep 2026 21:37:14 -0700 Subject: [PATCH] slimv5 model; deprecate old model --- CHANGELOG.md | 9 +++++ docs/api.md | 6 ++-- docs/mutations.md | 16 +++++---- lib/msprime.h | 2 +- lib/mutgen.c | 21 ++++++------ lib/tests/test_mutations.c | 31 ++++++++--------- msprime/__init__.py | 2 ++ msprime/_msprimemodule.c | 69 +++++++++++++++++++------------------- msprime/mutations.py | 23 ++++++++++++- tests/test_mutations.py | 36 ++++++++++++++++---- tests/test_python_c.py | 28 ++++++++-------- 11 files changed, 152 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57a4a7ee2..5f4bd3702 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ In development. +**Maintenance**: + +- The ``SLiMMutationModel`` is renamed ``SLiMv5MutationModel``. The class + ``SLiMMutationModel`` retains its previous behavior, but emits a FutureWarning. + This change is made because mutations made by the older SLiM mutation model + can be read as SLiM v6 mutations (but with meaningless values), producing a confusing + situation for downstream users. New code should use either ``SLiMv5MutationModel`` + or ``SLiMv6MutationModel``, as appropriate. ({pr}`2546`, {user}`petrelharp`) + ## [1.4.4] - 2026-09-01 Bugfix release. diff --git a/docs/api.md b/docs/api.md index e1ecd58ef..01e6cfa72 100644 --- a/docs/api.md +++ b/docs/api.md @@ -48,8 +48,8 @@ for discussion and examples of individual features. TPM EL2 InfiniteAlleles + SLiMv5MutationModel SLiMv6MutationModel - SLiMMutationModel ``` ### Demography @@ -219,11 +219,11 @@ for discussion and examples of individual features. ``` ```{eval-rst} -.. autoclass:: msprime.SLiMv6MutationModel() +.. autoclass:: msprime.SLiMv5MutationModel() ``` ```{eval-rst} -.. autoclass:: msprime.SLiMMutationModel() +.. autoclass:: msprime.SLiMv6MutationModel() ``` (sec_api_node_flags)= diff --git a/docs/mutations.md b/docs/mutations.md index 6775948e2..60a2e3937 100644 --- a/docs/mutations.md +++ b/docs/mutations.md @@ -105,12 +105,12 @@ for more information. {class}`.InfiniteAlleles` (Integers) : A generic infinite-alleles mutation model +{class}`.SLiMv5MutationModel` (Integers) +: An infinite-alleles model producing SLiM-style mutations for SLiM versions before 6.0 + {class}`.SLiMv6MutationModel` (Integers) : An infinite-alleles model producing SLiM-style mutations for SLiM v6+ -{class}`.SLiMMutationModel` (Integers) -: An infinite-alleles model producing SLiM-style mutations for older versions of SLiM - {class}`.MatrixMutationModel` (General finite state model) : Superclass of mutation models with a finite set of states @@ -488,8 +488,8 @@ Here are the available models; they are documented in more detail below. - {class}`.TPM`: Two-phase mutation model for microsatellite repeat copy number. DiRienzo et al. ('94) - {class}`.EL2`: Two-phase mutation model, equal rate, linear bias model for microsatellite repeat copy number. Garza et al. ('95) - {class}`.InfiniteAlleles`: A generic infinite-alleles mutation model +- {class}`.SLiMv5MutationModel`: An infinite-alleles model of mutation producing SLiM-style mutations for older versions of SLiM - {class}`.SLiMv6MutationModel`: An infinite-alleles model of mutation producing SLiM-style mutations for SLiM v6+ -- {class}`.SLiMMutationModel`: An infinite-alleles model of mutation producing SLiM-style mutations for older versions of SLiM (sec_mutations_matrix_mutations_models)= @@ -878,7 +878,7 @@ ancestral and derived states are modified (for instance, to be VCF-compliant) then there is no loss of information. There are in fact two mutation models for SLiM: -{class}`.SLiMv6MutationModel` and {class}`.SLiMMutationModel`, +{class}`.SLiMv5MutationModel` and {class}`.SLiMv6MutationModel`, because the format that SLiM uses to store mutations changed with version 6.0. Both store the same thing in derived state; the difference is in metadata. So, you probably want to use {class}`.SLiMv6MutationModel`; @@ -1035,14 +1035,16 @@ don't have the right tool for the job. #### Previous versions The technical difference between -{class}`.SLiMv6MutationModel` and {class}`.SLiMMutationModel` is as follows. +{class}`.SLiMv6MutationModel` and {class}`.SLiMv5MutationModel` is as follows. {class}`.SLiMv6MutationModel` stores the SLiM IDs in binary metadata as well as in the derived state, so that the actual metadata about mutations can be looked up from where SLiM expects it in top-level metadata. -The older version, {class}`.SLiMMutationModel`, +The older version, {class}`.SLiMv5MutationModel`, stores the metadata about SLiM mutations directly in each tskit mutation's metadata (and since individual SLiM mutations can appear in more than one tskit mutation, this leads to some redundancy; thus the change to how mutation metadata is handled in SLiMv6). For more information, see {ref}`the pyslim documentation`. +Formerly, {class}`.SLiMv5MutationModel` was named `SLiMMutationModel`; +the older name still works for backwards compatibility, but produces a warning. diff --git a/lib/msprime.h b/lib/msprime.h index 3a47b34e6..8e849dcc8 100644 --- a/lib/msprime.h +++ b/lib/msprime.h @@ -584,7 +584,7 @@ int matrix_mutation_model_factory(mutation_model_t *self, int model); int matrix_mutation_model_alloc(mutation_model_t *self, size_t num_alleles, char **alleles, size_t *allele_length, double *root_distribution, double *transition_matrix); -int slim_mutation_model_alloc(mutation_model_t *self, int32_t mutation_type_id, +int slim_v5_mutation_model_alloc(mutation_model_t *self, int32_t mutation_type_id, int64_t next_mutation_id, int32_t slim_generation, size_t block_size); int slim_v6_mutation_model_alloc( mutation_model_t *self, int64_t next_mutation_id, size_t block_size); diff --git a/lib/mutgen.c b/lib/mutgen.c index 820168972..f1db53574 100644 --- a/lib/mutgen.c +++ b/lib/mutgen.c @@ -342,7 +342,7 @@ slim_both_mutator_free(mutation_model_t *self) } /******************************* - * (Older) SLiM mutation model */ + * (Older) SLiMv5 mutation model */ /* Typedefs from MutationMetadataRec in slim_sim.h: * line 125 in v3.4, git hash b2c2b634199f35e53c4e7e513bd26c91c6d99fd9 @@ -373,7 +373,7 @@ slim_both_mutator_free(mutation_model_t *self) #define SLIM_MUTATION_METADATA_SIZE 17 // = 4 + 4 + 4 + 4 + 1 static void -copy_slim_mutation_metadata(slim_mutator_t *params, char *dest, double time) +copy_slim_v5_mutation_metadata(slim_mutator_t *params, char *dest, double time) { int32_t mutation_type_id = params->mutation_type_id; float selection_coeff = 0; @@ -394,7 +394,7 @@ copy_slim_mutation_metadata(slim_mutator_t *params, char *dest, double time) } static void -slim_mutator_print_state(mutation_model_t *self, FILE *out) +slim_v5_mutator_print_state(mutation_model_t *self, FILE *out) { slim_mutator_t params = self->params.slim_mutator; fprintf(out, "SLiM mutation model :: mutation type ID = %d\n", @@ -406,7 +406,7 @@ slim_mutator_print_state(mutation_model_t *self, FILE *out) } static int MSP_WARN_UNUSED -slim_mutator_check_validity(slim_mutator_t *self) +slim_v5_mutator_check_validity(slim_mutator_t *self) { int ret = 0; @@ -425,7 +425,7 @@ slim_mutator_check_validity(slim_mutator_t *self) } static int -slim_mutator_transition(mutation_model_t *self, gsl_rng *MSP_UNUSED(rng), +slim_v5_mutator_transition(mutation_model_t *self, gsl_rng *MSP_UNUSED(rng), const char *parent_allele, tsk_size_t parent_allele_length, const char *parent_metadata, tsk_size_t parent_metadata_length, mutation_t *mutation) { @@ -441,7 +441,8 @@ slim_mutator_transition(mutation_model_t *self, gsl_rng *MSP_UNUSED(rng), } // copy the final bit of metadata in (this is different than the v6 model) - copy_slim_mutation_metadata(params, buff + parent_metadata_length, mutation->time); + copy_slim_v5_mutation_metadata( + params, buff + parent_metadata_length, mutation->time); mutation->metadata = buff; // finally, set up for the next one @@ -655,7 +656,7 @@ matrix_mutation_model_alloc(mutation_model_t *self, size_t num_alleles, char **a } int MSP_WARN_UNUSED -slim_mutation_model_alloc(mutation_model_t *self, int32_t mutation_type_id, +slim_v5_mutation_model_alloc(mutation_model_t *self, int32_t mutation_type_id, int64_t next_mutation_id, int32_t slim_generation, size_t block_size) { int ret = 0; @@ -664,8 +665,8 @@ slim_mutation_model_alloc(mutation_model_t *self, int32_t mutation_type_id, memset(self, 0, sizeof(*self)); self->choose_root_state = &slim_both_mutator_choose_root_state; - self->transition = &slim_mutator_transition; - self->print_state = &slim_mutator_print_state; + self->transition = &slim_v5_mutator_transition; + self->print_state = &slim_v5_mutator_print_state; self->free = &slim_both_mutator_free; if (block_size == 0) { /* 8K is a good default, but we need to have the @@ -685,7 +686,7 @@ slim_mutation_model_alloc(mutation_model_t *self, int32_t mutation_type_id, params->next_mutation_id = next_mutation_id; params->slim_generation = slim_generation; - ret = slim_mutator_check_validity(params); + ret = slim_v5_mutator_check_validity(params); if (ret != 0) { goto out; } diff --git a/lib/tests/test_mutations.c b/lib/tests/test_mutations.c index 48c5e5abf..a37606981 100644 --- a/lib/tests/test_mutations.c +++ b/lib/tests/test_mutations.c @@ -914,7 +914,7 @@ verify_slim_metadata(char *metadata, size_t metadata_length, int32_t slim_time) } static void -test_mutgen_slim_mutations(void) +test_mutgen_slim_v5_mutations(void) { int ret = 0; int j, k; @@ -930,7 +930,7 @@ test_mutgen_slim_mutations(void) int32_t slim_generation = 17; CU_ASSERT_FATAL(rng != NULL); - ret = slim_mutation_model_alloc( + ret = slim_v5_mutation_model_alloc( &mut_model, mutation_type_id, next_mutation_id, slim_generation, 0); CU_ASSERT_EQUAL_FATAL(ret, 0); @@ -1011,7 +1011,7 @@ test_mutgen_slim_mutations(void) } static void -test_mutgen_slim_mutation_large_values(void) +test_mutgen_slim_v5_mutation_large_values(void) { int ret = 0; mutgen_t mutgen; @@ -1028,7 +1028,7 @@ test_mutgen_slim_mutation_large_values(void) insert_single_tree(&tables, -1); /* Trying to generate mutations that overflow raises an error */ - ret = slim_mutation_model_alloc(&mut_model, 0, INT64_MAX, 1, 0); + ret = slim_v5_mutation_model_alloc(&mut_model, 0, INT64_MAX, 1, 0); CU_ASSERT_EQUAL_FATAL(ret, 0); ret = mutgen_alloc(&mutgen, rng, &tables, &mut_model, 0); CU_ASSERT_EQUAL_FATAL(ret, 0); @@ -1043,7 +1043,7 @@ test_mutgen_slim_mutation_large_values(void) tsk_site_table_clear(&tables.sites); /* Try out with a large value that doesn't hit the ceiling */ next_mutation_id = INT64_MAX - 100; - ret = slim_mutation_model_alloc(&mut_model, 0, next_mutation_id, 1, 0); + ret = slim_v5_mutation_model_alloc(&mut_model, 0, next_mutation_id, 1, 0); CU_ASSERT_EQUAL_FATAL(ret, 0); ret = mutgen_alloc(&mutgen, rng, &tables, &mut_model, 0); CU_ASSERT_EQUAL_FATAL(ret, 0); @@ -1533,7 +1533,7 @@ test_matrix_mutation_model_properties(void) } static void -test_slim_mutation_model_errors(void) +test_slim_v5_mutation_model_errors(void) { int ret; mutation_model_t model; @@ -1542,21 +1542,21 @@ test_slim_mutation_model_errors(void) int32_t slim_generation = 1; next_mutation_id--; - ret = slim_mutation_model_alloc( + ret = slim_v5_mutation_model_alloc( &model, mutation_type_id, next_mutation_id, slim_generation, 0); CU_ASSERT_EQUAL_FATAL(ret, MSP_ERR_BAD_SLIM_PARAMETERS); mutation_model_free(&model); mutation_type_id--; next_mutation_id++; - ret = slim_mutation_model_alloc( + ret = slim_v5_mutation_model_alloc( &model, mutation_type_id, next_mutation_id, slim_generation, 0); CU_ASSERT_EQUAL_FATAL(ret, MSP_ERR_BAD_SLIM_PARAMETERS); mutation_model_free(&model); } static void -test_slim_mutation_model_properties(void) +test_slim_v5_mutation_model_properties(void) { int ret; mutation_model_t model; @@ -1564,7 +1564,7 @@ test_slim_mutation_model_properties(void) int64_t next_mutation_id = 2; int32_t slim_generation = 3; - ret = slim_mutation_model_alloc( + ret = slim_v5_mutation_model_alloc( &model, mutation_type_id, next_mutation_id, slim_generation, 0); CU_ASSERT_EQUAL_FATAL(ret, 0); CU_ASSERT_EQUAL_FATAL(model.params.slim_mutator.slim_generation, 3); @@ -1623,9 +1623,9 @@ main(int argc, char **argv) test_single_tree_mutgen_many_mutations }, { "test_jukes_cantor_has_silent_mutations", test_jukes_cantor_has_silent_mutations }, - { "test_mutgen_slim_mutations", test_mutgen_slim_mutations }, - { "test_mutgen_slim_mutation_large_values", - test_mutgen_slim_mutation_large_values }, + { "test_mutgen_slim_v5_mutations", test_mutgen_slim_v5_mutations }, + { "test_mutgen_slim_v5_mutation_large_values", + test_mutgen_slim_v5_mutation_large_values }, { "test_mutgen_slim_v6_mutations", test_mutgen_slim_v6_mutations }, { "test_mutgen_slim_v6_mutations_keeps", test_mutgen_slim_v6_mutations_keeps }, { "test_mutgen_slim_v6_mutation_large_values", @@ -1636,8 +1636,9 @@ main(int argc, char **argv) { "test_matrix_mutation_model_errors", test_matrix_mutation_model_errors }, { "test_matrix_mutation_model_properties", test_matrix_mutation_model_properties }, - { "test_slim_mutation_model_errors", test_slim_mutation_model_errors }, - { "test_slim_mutation_model_properties", test_slim_mutation_model_properties }, + { "test_slim_v5_mutation_model_errors", test_slim_v5_mutation_model_errors }, + { "test_slim_v5_mutation_model_properties", + test_slim_v5_mutation_model_properties }, { "test_slim_v6_mutation_model_errors", test_slim_v6_mutation_model_errors }, { "test_slim_v6_mutation_model_properties", test_slim_v6_mutation_model_properties }, diff --git a/msprime/__init__.py b/msprime/__init__.py index 1878d7661..58e0ed63c 100644 --- a/msprime/__init__.py +++ b/msprime/__init__.py @@ -81,6 +81,7 @@ NUCLEOTIDES, PAM, SLiMMutationModel, + SLiMv5MutationModel, SLiMv6MutationModel, sim_mutations, ) @@ -152,6 +153,7 @@ "RateMap", "RecombinationMap", "SLiMMutationModel", + "SLiMv5MutationModel", "SLiMv6MutationModel", "Sample", "SampleSet", diff --git a/msprime/_msprimemodule.c b/msprime/_msprimemodule.c index e9b38d673..3c67aa0cc 100644 --- a/msprime/_msprimemodule.c +++ b/msprime/_msprimemodule.c @@ -63,7 +63,7 @@ typedef struct { typedef struct { PyObject_HEAD mutation_model_t *mutation_model; -} SLiMMutationModel; +} SLiMv5MutationModel; typedef struct { PyObject_HEAD @@ -679,18 +679,18 @@ static PyTypeObject MatrixMutationModelType = { */ static int -SLiMMutationModel_check_state(SLiMMutationModel *self) +SLiMv5MutationModel_check_state(SLiMv5MutationModel *self) { int ret = 0; if (self->mutation_model == NULL) { - PyErr_SetString(PyExc_SystemError, "SLiMMutationModel not initialised"); + PyErr_SetString(PyExc_SystemError, "SLiMv5MutationModel not initialised"); ret = -1; } return ret; } static void -SLiMMutationModel_dealloc(SLiMMutationModel *self) +SLiMv5MutationModel_dealloc(SLiMv5MutationModel *self) { if (self->mutation_model != NULL) { mutation_model_free(self->mutation_model); @@ -701,7 +701,7 @@ SLiMMutationModel_dealloc(SLiMMutationModel *self) } static int -SLiMMutationModel_init(SLiMMutationModel *self, PyObject *args, PyObject *kwds) +SLiMv5MutationModel_init(SLiMv5MutationModel *self, PyObject *args, PyObject *kwds) { int ret = -1; int err; @@ -725,7 +725,7 @@ SLiMMutationModel_init(SLiMMutationModel *self, PyObject *args, PyObject *kwds) PyErr_NoMemory(); goto out; } - err = slim_mutation_model_alloc(self->mutation_model, (int32_t) type, + err = slim_v5_mutation_model_alloc(self->mutation_model, (int32_t) type, (int64_t) next_id, (int32_t) slim_generation, (size_t) block_size); if (err != 0) { handle_library_error(err); @@ -737,12 +737,12 @@ SLiMMutationModel_init(SLiMMutationModel *self, PyObject *args, PyObject *kwds) } static PyObject * -SLiMMutationModel_get_type(SLiMMutationModel *self, void *closure) +SLiMv5MutationModel_get_type(SLiMv5MutationModel *self, void *closure) { slim_mutator_t *params; PyObject *ret = NULL; - if (SLiMMutationModel_check_state(self) != 0) { + if (SLiMv5MutationModel_check_state(self) != 0) { goto out; } params = &self->mutation_model->params.slim_mutator; @@ -752,12 +752,12 @@ SLiMMutationModel_get_type(SLiMMutationModel *self, void *closure) } static PyObject * -SLiMMutationModel_get_next_id(SLiMMutationModel *self, void *closure) +SLiMv5MutationModel_get_next_id(SLiMv5MutationModel *self, void *closure) { slim_mutator_t *params; PyObject *ret = NULL; - if (SLiMMutationModel_check_state(self) != 0) { + if (SLiMv5MutationModel_check_state(self) != 0) { goto out; } params = &self->mutation_model->params.slim_mutator; @@ -767,12 +767,12 @@ SLiMMutationModel_get_next_id(SLiMMutationModel *self, void *closure) } static PyObject * -SLiMMutationModel_get_slim_generation(SLiMMutationModel *self, void *closure) +SLiMv5MutationModel_get_slim_generation(SLiMv5MutationModel *self, void *closure) { slim_mutator_t *params; PyObject *ret = NULL; - if (SLiMMutationModel_check_state(self) != 0) { + if (SLiMv5MutationModel_check_state(self) != 0) { goto out; } params = &self->mutation_model->params.slim_mutator; @@ -781,23 +781,23 @@ SLiMMutationModel_get_slim_generation(SLiMMutationModel *self, void *closure) return ret; } -static PyGetSetDef SLiMMutationModel_getsetters[] = { - { "type", (getter) SLiMMutationModel_get_type, NULL, "Return the mutation type" }, - { "next_id", (getter) SLiMMutationModel_get_next_id, NULL, +static PyGetSetDef SLiMv5MutationModel_getsetters[] = { + { "type", (getter) SLiMv5MutationModel_get_type, NULL, "Return the mutation type" }, + { "next_id", (getter) SLiMv5MutationModel_get_next_id, NULL, "Return the next mutation id" }, - { "slim_generation", (getter) SLiMMutationModel_get_slim_generation, NULL, + { "slim_generation", (getter) SLiMv5MutationModel_get_slim_generation, NULL, "Return the SLiM generation" }, { NULL } /* Sentinel */ }; -static PyTypeObject SLiMMutationModelType = { - .tp_name = "_msprime.SLiMMutationModel", - .tp_basicsize = sizeof(SLiMMutationModel), - .tp_dealloc = (destructor) SLiMMutationModel_dealloc, +static PyTypeObject SLiMv5MutationModelType = { + .tp_name = "_msprime.SLiMv5MutationModel", + .tp_basicsize = sizeof(SLiMv5MutationModel), + .tp_dealloc = (destructor) SLiMv5MutationModel_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = "SLiMMutationModel objects", - .tp_getset = SLiMMutationModel_getsetters, - .tp_init = (initproc) SLiMMutationModel_init, + .tp_doc = "SLiMv5MutationModel objects", + .tp_getset = SLiMv5MutationModel_getsetters, + .tp_init = (initproc) SLiMv5MutationModel_init, .tp_new = PyType_GenericNew, }; @@ -3110,7 +3110,7 @@ parse_mutation_model(PyObject *py_model) { mutation_model_t *model = NULL; MatrixMutationModel *matrix_mutation_model = NULL; - SLiMMutationModel *slim_mutation_model = NULL; + SLiMv5MutationModel *slim_v5_mutation_model = NULL; SLiMv6MutationModel *slim_v6_mutation_model = NULL; InfiniteAllelesMutationModel *infinite_alleles_model = NULL; @@ -3120,12 +3120,12 @@ parse_mutation_model(PyObject *py_model) goto out; } model = matrix_mutation_model->mutation_model; - } else if (PyObject_TypeCheck(py_model, &SLiMMutationModelType)) { - slim_mutation_model = (SLiMMutationModel *) py_model; - if (SLiMMutationModel_check_state(slim_mutation_model) != 0) { + } else if (PyObject_TypeCheck(py_model, &SLiMv5MutationModelType)) { + slim_v5_mutation_model = (SLiMv5MutationModel *) py_model; + if (SLiMv5MutationModel_check_state(slim_v5_mutation_model) != 0) { goto out; } - model = slim_mutation_model->mutation_model; + model = slim_v5_mutation_model->mutation_model; } else if (PyObject_TypeCheck(py_model, &SLiMv6MutationModelType)) { slim_v6_mutation_model = (SLiMv6MutationModel *) py_model; if (SLiMv6MutationModel_check_state(slim_v6_mutation_model) != 0) { @@ -3141,7 +3141,7 @@ parse_mutation_model(PyObject *py_model) } else { PyErr_SetString(PyExc_TypeError, "model must be an instance of MatrixMutationModel, " - "SLiMMutationModel, SLiMv6MutationModel or InfiniteAllelesMutationModel."); + "SLiMv5MutationModel, SLiMv6MutationModel or InfiniteAllelesMutationModel."); goto out; } out: @@ -3352,13 +3352,14 @@ PyInit__msprime(void) PyModule_AddObject( module, "MatrixMutationModel", (PyObject *) &MatrixMutationModelType); - /* SLiMMutationModel type */ - SLiMMutationModelType.tp_base = &BaseMutationModelType; - if (PyType_Ready(&SLiMMutationModelType) < 0) { + /* SLiMv5MutationModel type */ + SLiMv5MutationModelType.tp_base = &BaseMutationModelType; + if (PyType_Ready(&SLiMv5MutationModelType) < 0) { return NULL; } - Py_INCREF(&SLiMMutationModelType); - PyModule_AddObject(module, "SLiMMutationModel", (PyObject *) &SLiMMutationModelType); + Py_INCREF(&SLiMv5MutationModelType); + PyModule_AddObject( + module, "SLiMv5MutationModel", (PyObject *) &SLiMv5MutationModelType); /* SLiMv6MutationModel type */ SLiMv6MutationModelType.tp_base = &BaseMutationModelType; diff --git a/msprime/mutations.py b/msprime/mutations.py index 4d7fbdfa8..401e18167 100644 --- a/msprime/mutations.py +++ b/msprime/mutations.py @@ -24,6 +24,7 @@ import math import numbers import sys +import warnings import numpy as np import tskit @@ -106,7 +107,7 @@ def __str__(self): return s -class SLiMMutationModel(_msprime.SLiMMutationModel, MutationModel): +class SLiMv5MutationModel(_msprime.SLiMv5MutationModel, MutationModel): """ An infinite-alleles model of mutation producing "SLiM-style" mutations for versions of SLiM before 6.0. For SLiM version 6.0 and beyond, @@ -146,6 +147,26 @@ def __str__(self): ) +class SLiMMutationModel(SLiMv5MutationModel): + """ + An infinite-alleles model of mutation producing "SLiM-style" mutations + for versions of SLiM before 6.0. For SLiM version 6.0 and beyond, + use {class}`.SLiMv6MutationModel`. + + This is a deprecated alias for {class}`.SLiMv5MutationModel`. Newer + code should use {class}`.SLiMv5MutationModel` or {class}`.SLiMv6MutationModel` + as appropriate. + """ + + def __init__(self, *args, **kwargs): + warnings.warn( + "SLiMMutationModel will be removed in future versions. Use " + "SLiMv5MutationModel or SLiMv6MutationModel instead.", + category=FutureWarning, + ) + super().__init__(*args, **kwargs) + + class SLiMv6MutationModel(_msprime.SLiMv6MutationModel, MutationModel): """ An infinite-alleles model of mutation producing "SLiM-style" mutations diff --git a/tests/test_mutations.py b/tests/test_mutations.py index 1deae6608..a083b4722 100644 --- a/tests/test_mutations.py +++ b/tests/test_mutations.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2018-2021 University of Oxford +# Copyright (C) 2018-2026 University of Oxford # # This file is part of msprime. # @@ -1601,9 +1601,9 @@ def test_binary_many_trees(self): self.validate_slim_mutations(mts) -class TestSLiMMutationModel(SLiMModelMixin): +class TestSLiMv5MutationModel(SLiMModelMixin): """ - Tests for the SLiM mutation generator. + Tests for the SLiMv5 mutation generator. """ def parse_slim_metadata(self, metadata): @@ -1654,7 +1654,7 @@ def run_mutate( mutation_id=0, slim_generation=1, ): - model = msprime.SLiMMutationModel( + model = msprime.SLiMv5MutationModel( type=mutation_type, next_id=mutation_id, slim_generation=slim_generation ) mts1 = msprime.sim_mutations( @@ -1662,7 +1662,9 @@ def run_mutate( ) assert mts1.num_mutations == model.next_id - model = PythonSLiMMutationModel(mutation_type=mutation_type, next_id=mutation_id) + model = PythonSLiMv5MutationModel( + mutation_type=mutation_type, next_id=mutation_id + ) mts2 = py_sim_mutations( ts, rate=rate, random_seed=random_seed, model=model, discrete_genome=True ) @@ -1693,6 +1695,28 @@ def test_slim_generation(self): assert mts.num_mutations > 10 self.validate_slim_mutations(mts, slim_generation=slim_generation) + def test_deprecated_alias(self): + ts = msprime.sim_ancestry(4, sequence_length=2, random_seed=5) + mts = self.run_mutate(ts, rate=5.0, random_seed=23, mutation_type=3) + mutation_type = 3 + mutation_id = 123 + slim_generation = 456 + with pytest.warns(FutureWarning, match="SLiMv5"): + model = msprime.SLiMMutationModel( + type=mutation_type, next_id=mutation_id, slim_generation=slim_generation + ) + assert model.type == mutation_type + assert model.next_id == mutation_id + assert model.slim_generation == slim_generation + mts = msprime.sim_mutations( + ts, rate=5.0, random_seed=135, model=model, discrete_genome=True + ) + assert mts.num_mutations + mutation_id == model.next_id + assert mts.num_mutations > 10 + self.validate_slim_mutations( + mts, mutation_type=mutation_type, slim_generation=slim_generation + ) + class TestSLiMv6MutationModel(SLiMModelMixin): """ @@ -2187,7 +2211,7 @@ def transition_allele(self, rng, current_allele): @dataclasses.dataclass -class PythonSLiMMutationModel(PythonSLiMv6MutationModel): +class PythonSLiMv5MutationModel(PythonSLiMv6MutationModel): mutation_type: int = 0 diff --git a/tests/test_python_c.py b/tests/test_python_c.py index 105b35c09..d3519b02f 100644 --- a/tests/test_python_c.py +++ b/tests/test_python_c.py @@ -2765,27 +2765,27 @@ def test_bad_probabilities(self): _msprime.MatrixMutationModel(alleles, [0, 1], matrix) -class TestSLiMMutationModel: +class TestSLiMv5MutationModel: """ Tests for the slim mutation model class. """ def test_constructor_errors(self): with pytest.raises(TypeError): - _msprime.SLiMMutationModel() + _msprime.SLiMv5MutationModel() for bad_type in ["sdr", 0.222, None]: with pytest.raises(TypeError): - _msprime.SLiMMutationModel(type=bad_type, next_id=234) - _msprime.SLiMMutationModel(type=0, next_id=bad_type) - _msprime.SLiMMutationModel(type=0, next_id=0, block_size=bad_type) + _msprime.SLiMv5MutationModel(type=bad_type, next_id=234) + _msprime.SLiMv5MutationModel(type=0, next_id=bad_type) + _msprime.SLiMv5MutationModel(type=0, next_id=0, block_size=bad_type) with pytest.raises(_msprime.LibraryError): - _msprime.SLiMMutationModel(type=-1, next_id=0) - _msprime.SLiMMutationModel(type=1, next_id=-1) + _msprime.SLiMv5MutationModel(type=-1, next_id=0) + _msprime.SLiMv5MutationModel(type=1, next_id=-1) def test_uninitialised(self): - model = _msprime.SLiMMutationModel.__new__(_msprime.SLiMMutationModel) + model = _msprime.SLiMv5MutationModel.__new__(_msprime.SLiMv5MutationModel) with pytest.raises(SystemError): _ = model.type with pytest.raises(SystemError): @@ -2793,18 +2793,18 @@ def test_uninitialised(self): def test_type(self): for mutation_type in [0, 10, 2**31 - 1]: - model = _msprime.SLiMMutationModel(type=mutation_type) + model = _msprime.SLiMv5MutationModel(type=mutation_type) assert model.type == mutation_type assert model.next_id == 0 def test_next_id(self): for next_id in [0, 10, 2**63 - 1]: - model = _msprime.SLiMMutationModel(0, next_id=next_id) + model = _msprime.SLiMv5MutationModel(0, next_id=next_id) assert model.type == 0 assert model.next_id == next_id def test_generation(self): - model = _msprime.SLiMMutationModel(0) + model = _msprime.SLiMv5MutationModel(0) assert model.slim_generation == 1 @@ -3007,7 +3007,7 @@ def test_model(self): with pytest.raises(TypeError): _msprime.sim_mutations(tables, rng, rate_map=imap, model=bad_type) model_classes = [ - _msprime.SLiMMutationModel, + _msprime.SLiMv5MutationModel, _msprime.SLiMv6MutationModel, _msprime.InfiniteAllelesMutationModel, _msprime.MatrixMutationModel, @@ -3019,11 +3019,11 @@ def test_model(self): tables, rng, rate_map=imap, model=uninitialised_model ) - def test_slim_model(self): + def test_slim_v5_model(self): rng = _msprime.RandomGenerator(1) imap = uniform_rate_map(1) tables = _msprime.LightweightTableCollection(1.0) - model = _msprime.SLiMMutationModel(1234, 5678) + model = _msprime.SLiMv5MutationModel(1234, 5678) _msprime.sim_mutations(tables, rng, imap, model) assert model.next_id == 5678