11#
2- # Copyright (C) 2016-2021 University of Oxford
2+ # Copyright (C) 2016-2025 University of Oxford
33#
44# This file is part of msprime.
55#
3333
3434import numpy as np
3535import pytest
36- import stdpopsim
3736import tskit
3837
3938import msprime
@@ -6519,6 +6518,209 @@ def test_as_lineage_movement_abstract(self):
65196518
65206519
65216520class TestStdpopsimModels :
6521+
6522+ def stdpopsim_ooa_model (self ):
6523+ """
6524+ This is a copy of the code used in stdpopsim (copied 2025-07-25) to
6525+ generate the old-style demographic model. We do this rather than
6526+ using the actual stdpopsim code because the old-style model is
6527+ lost during intitialisation to msprime 1.0 code, and it's not worth
6528+ rewriting all the tests to accomodate this.
6529+ """
6530+ generation_time = 25
6531+
6532+ # First we set out the maximum likelihood values of the various parameters
6533+ # given in Table 1.
6534+ N_A = 7300
6535+ N_B = 2100
6536+ N_AF = 12300
6537+ N_EU0 = 1000
6538+ N_AS0 = 510
6539+ # Times are provided in years, so we convert into generations.
6540+
6541+ T_AF = 220e3 / generation_time
6542+ T_B = 140e3 / generation_time
6543+ T_EU_AS = 21.2e3 / generation_time
6544+ # We need to work out the starting (diploid) population sizes based on
6545+ # the growth rates provided for these two populations
6546+ r_EU = 0.004
6547+ r_AS = 0.0055
6548+ N_EU = N_EU0 / math .exp (- r_EU * T_EU_AS )
6549+ N_AS = N_AS0 / math .exp (- r_AS * T_EU_AS )
6550+ # Migration rates during the various epochs.
6551+ m_AF_B = 25e-5
6552+ m_AF_EU = 3e-5
6553+ m_AF_AS = 1.9e-5
6554+ m_EU_AS = 9.6e-5
6555+
6556+ population_configurations = [
6557+ msprime .PopulationConfiguration (initial_size = N_AF ),
6558+ msprime .PopulationConfiguration (initial_size = N_EU , growth_rate = r_EU ),
6559+ msprime .PopulationConfiguration (initial_size = N_AS , growth_rate = r_AS ),
6560+ ]
6561+ migration_matrix = [
6562+ [0 , m_AF_EU , m_AF_AS ], # noqa
6563+ [m_AF_EU , 0 , m_EU_AS ], # noqa
6564+ [m_AF_AS , m_EU_AS , 0 ], # noqa
6565+ ]
6566+ demographic_events = [
6567+ # CEU and CHB merge into B with rate changes at T_EU_AS
6568+ msprime .MassMigration (
6569+ time = T_EU_AS , source = 2 , destination = 1 , proportion = 1.0
6570+ ),
6571+ msprime .MigrationRateChange (time = T_EU_AS , rate = 0 ),
6572+ msprime .MigrationRateChange (time = T_EU_AS , rate = m_AF_B , matrix_index = (0 , 1 )),
6573+ msprime .MigrationRateChange (time = T_EU_AS , rate = m_AF_B , matrix_index = (1 , 0 )),
6574+ msprime .PopulationParametersChange (
6575+ time = T_EU_AS , initial_size = N_B , growth_rate = 0 , population_id = 1
6576+ ),
6577+ # Population B merges into YRI at T_B
6578+ msprime .MassMigration (time = T_B , source = 1 , destination = 0 , proportion = 1.0 ),
6579+ msprime .MigrationRateChange (time = T_B , rate = 0 ),
6580+ # Size changes to N_A at T_AF
6581+ msprime .PopulationParametersChange (
6582+ time = T_AF , initial_size = N_A , population_id = 0
6583+ ),
6584+ ]
6585+ return (
6586+ population_configurations ,
6587+ migration_matrix ,
6588+ demographic_events ,
6589+ )
6590+
6591+ def stdpopsim_ooa_archaic_admixture_model (self ):
6592+ """
6593+ See notes above for stdpopsim_ooa_model.
6594+ """
6595+
6596+ # First we set out the maximum likelihood values of the various parameters
6597+ # given in Table 1 (under archaic admixture).
6598+ N_0 = 3600
6599+ N_YRI = 13900
6600+ N_B = 880
6601+ N_CEU0 = 2300
6602+ N_CHB0 = 650
6603+
6604+ # Times are provided in years, so we convert into generations.
6605+ # In the published model, the authors used a generation time of 29 years to
6606+ # convert from genetic to physical units
6607+ generation_time = 29
6608+
6609+ T_AF = 300e3 / generation_time
6610+ T_B = 60.7e3 / generation_time
6611+ T_EU_AS = 36.0e3 / generation_time
6612+ T_arch_afr_split = 499e3 / generation_time
6613+ T_arch_afr_mig = 125e3 / generation_time
6614+ T_nean_split = 559e3 / generation_time
6615+ T_arch_adm_end = 18.7e3 / generation_time
6616+
6617+ # We need to work out the starting (diploid) population sizes based on
6618+ # the growth rates provided for these two populations
6619+ r_CEU = 0.00125
6620+ r_CHB = 0.00372
6621+ N_CEU = N_CEU0 / math .exp (- r_CEU * T_EU_AS )
6622+ N_CHB = N_CHB0 / math .exp (- r_CHB * T_EU_AS )
6623+
6624+ # Migration rates during the various epochs.
6625+ m_AF_B = 52.2e-5
6626+ m_YRI_CEU = 2.48e-5
6627+ m_YRI_CHB = 0e-5
6628+ m_CEU_CHB = 11.3e-5
6629+ m_AF_arch_af = 1.98e-5
6630+ m_OOA_nean = 0.825e-5
6631+
6632+ # Population IDs correspond to their indexes in the population
6633+ # configuration array. Therefore, we have 0=YRI, 1=CEU and 2=CHB
6634+ # initially.
6635+ # We also have two archaic populations, putative Neanderthals and
6636+ # archaicAfrican, which are population indices 3=Nean and 4=arch_afr.
6637+ # Their sizes are equal to the ancestral reference population size N_0.
6638+ population_configurations = [
6639+ msprime .PopulationConfiguration (initial_size = N_YRI ),
6640+ msprime .PopulationConfiguration (initial_size = N_CEU , growth_rate = r_CEU ),
6641+ msprime .PopulationConfiguration (initial_size = N_CHB , growth_rate = r_CHB ),
6642+ msprime .PopulationConfiguration (initial_size = N_0 ),
6643+ msprime .PopulationConfiguration (initial_size = N_0 ),
6644+ ]
6645+ migration_matrix = [ # noqa
6646+ [0 , m_YRI_CEU , m_YRI_CHB , 0 , 0 ], # noqa
6647+ [m_YRI_CEU , 0 , m_CEU_CHB , 0 , 0 ], # noqa
6648+ [m_YRI_CHB , m_CEU_CHB , 0 , 0 , 0 ], # noqa
6649+ [0 , 0 , 0 , 0 , 0 ], # noqa
6650+ [0 , 0 , 0 , 0 , 0 ], # noqa
6651+ ] # noqa
6652+ demographic_events = [
6653+ # first event is migration turned on between modern and archaic humans
6654+ msprime .MigrationRateChange (
6655+ time = T_arch_adm_end , rate = m_AF_arch_af , matrix_index = (0 , 4 )
6656+ ),
6657+ msprime .MigrationRateChange (
6658+ time = T_arch_adm_end , rate = m_AF_arch_af , matrix_index = (4 , 0 )
6659+ ),
6660+ msprime .MigrationRateChange (
6661+ time = T_arch_adm_end , rate = m_OOA_nean , matrix_index = (1 , 3 )
6662+ ),
6663+ msprime .MigrationRateChange (
6664+ time = T_arch_adm_end , rate = m_OOA_nean , matrix_index = (3 , 1 )
6665+ ),
6666+ msprime .MigrationRateChange (
6667+ time = T_arch_adm_end , rate = m_OOA_nean , matrix_index = (2 , 3 )
6668+ ),
6669+ msprime .MigrationRateChange (
6670+ time = T_arch_adm_end , rate = m_OOA_nean , matrix_index = (3 , 2 )
6671+ ),
6672+ # CEU and CHB merge into B with rate changes at T_EU_AS
6673+ msprime .MassMigration (
6674+ time = T_EU_AS , source = 2 , destination = 1 , proportion = 1.0
6675+ ),
6676+ msprime .MigrationRateChange (time = T_EU_AS , rate = 0 ),
6677+ msprime .MigrationRateChange (time = T_EU_AS , rate = m_AF_B , matrix_index = (0 , 1 )),
6678+ msprime .MigrationRateChange (time = T_EU_AS , rate = m_AF_B , matrix_index = (1 , 0 )),
6679+ msprime .MigrationRateChange (
6680+ time = T_EU_AS , rate = m_AF_arch_af , matrix_index = (0 , 4 )
6681+ ),
6682+ msprime .MigrationRateChange (
6683+ time = T_EU_AS , rate = m_AF_arch_af , matrix_index = (4 , 0 )
6684+ ),
6685+ msprime .MigrationRateChange (
6686+ time = T_EU_AS , rate = m_OOA_nean , matrix_index = (1 , 3 )
6687+ ),
6688+ msprime .MigrationRateChange (
6689+ time = T_EU_AS , rate = m_OOA_nean , matrix_index = (3 , 1 )
6690+ ),
6691+ msprime .PopulationParametersChange (
6692+ time = T_EU_AS , initial_size = N_B , growth_rate = 0 , population_id = 1
6693+ ),
6694+ # Population B merges into YRI at T_B
6695+ msprime .MassMigration (time = T_B , source = 1 , destination = 0 , proportion = 1.0 ),
6696+ msprime .MigrationRateChange (time = T_B , rate = 0 ),
6697+ msprime .MigrationRateChange (
6698+ time = T_B , rate = m_AF_arch_af , matrix_index = (0 , 4 )
6699+ ),
6700+ msprime .MigrationRateChange (
6701+ time = T_B , rate = m_AF_arch_af , matrix_index = (4 , 0 )
6702+ ),
6703+ # Beginning of migration between African and archaic African populations
6704+ msprime .MigrationRateChange (time = T_arch_afr_mig , rate = 0 ),
6705+ # Size changes to N_0 at T_AF
6706+ msprime .PopulationParametersChange (
6707+ time = T_AF , initial_size = N_0 , population_id = 0
6708+ ),
6709+ # Archaic African merges with moderns
6710+ msprime .MassMigration (
6711+ time = T_arch_afr_split , source = 4 , destination = 0 , proportion = 1.0
6712+ ),
6713+ # Neanderthal merges with moderns
6714+ msprime .MassMigration (
6715+ time = T_nean_split , source = 3 , destination = 0 , proportion = 1.0
6716+ ),
6717+ ]
6718+ return (
6719+ population_configurations ,
6720+ migration_matrix ,
6721+ demographic_events ,
6722+ )
6723+
65226724 def stdpopsim_browning_admixture_model (self ):
65236725 """
65246726 The currently released version of the AmericanAdmixture_4B11 model in
@@ -6636,12 +6838,12 @@ def test_browning_admixture(self):
66366838 demog_local .assert_equivalent (demog_sps , rel_tol = 1e-5 )
66376839
66386840 def test_ooa_remap (self ):
6639- # This test is temporary while we are updating stdpopsim to use the
6640- # msprime APIs. See the nodes in the _ooa_model() code.
6841+ (
6842+ population_configurations ,
6843+ migration_matrix ,
6844+ demographic_events ,
6845+ ) = self .stdpopsim_ooa_model ()
66416846 demog_local = msprime .Demography ._ooa_model ()
6642- model_sps = stdpopsim .get_species ("HomSap" ).get_demographic_model (
6643- "OutOfAfrica_3G09"
6644- )
66456847
66466848 # Map from local population names into the equivalent in the stdpopsim
66476849 # model, per epoch.
@@ -6651,22 +6853,21 @@ def test_ooa_remap(self):
66516853 {"AMH" : 0 },
66526854 {"ANC" : 0 },
66536855 ]
6654-
66556856 remapped_demog = msprime .Demography .from_old_style (
6656- model_sps . population_configurations ,
6657- migration_matrix = model_sps . migration_matrix ,
6658- demographic_events = model_sps . demographic_events ,
6857+ population_configurations ,
6858+ migration_matrix = migration_matrix ,
6859+ demographic_events = demographic_events ,
66596860 population_map = epoch_pop_map ,
66606861 )
66616862 demog_local .assert_equivalent (remapped_demog )
66626863
66636864 def test_ooa_trunk (self ):
6664- # This test is temporary while we are updating stdpopsim to use the
6665- # msprime APIs. See the notes in the _ooa_model() code.
6865+ (
6866+ population_configurations ,
6867+ migration_matrix ,
6868+ demographic_events ,
6869+ ) = self .stdpopsim_ooa_model ()
66666870 demog_local = msprime .Demography ._ooa_trunk_model ()
6667- model_sps = stdpopsim .get_species ("HomSap" ).get_demographic_model (
6668- "OutOfAfrica_3G09"
6669- )
66706871
66716872 # Map from local population names into the equivalent in the stdpopsim
66726873 # model, per epoch.
@@ -6678,22 +6879,24 @@ def test_ooa_trunk(self):
66786879 ]
66796880
66806881 remapped_demog = msprime .Demography .from_old_style (
6681- model_sps . population_configurations ,
6682- migration_matrix = model_sps . migration_matrix ,
6683- demographic_events = model_sps . demographic_events ,
6882+ population_configurations ,
6883+ migration_matrix = migration_matrix ,
6884+ demographic_events = demographic_events ,
66846885 population_map = epoch_pop_map ,
66856886 )
66866887 demog_local .assert_equivalent (remapped_demog )
66876888
66886889 def test_ooa_archaic (self ):
66896890 demog_local = msprime .Demography ._ooa_archaic_model ()
6690- model_sps = stdpopsim .get_species ("HomSap" ).get_demographic_model (
6691- "OutOfAfricaArchaicAdmixture_5R19"
6692- )
6891+ (
6892+ population_configurations ,
6893+ migration_matrix ,
6894+ demographic_events ,
6895+ ) = self .stdpopsim_ooa_archaic_admixture_model ()
66936896 demog_sps = msprime .Demography .from_old_style (
6694- model_sps . population_configurations ,
6695- migration_matrix = model_sps . migration_matrix ,
6696- demographic_events = model_sps . demographic_events ,
6897+ population_configurations ,
6898+ migration_matrix = migration_matrix ,
6899+ demographic_events = demographic_events ,
66976900 population_map = [
66986901 # Initial populations
66996902 {"AFR" : 0 , "CEU" : 1 , "CHB" : 2 , "Neanderthal" : 3 , "ArchaicAFR" : 4 },
@@ -6718,13 +6921,15 @@ def test_ooa_archaic(self):
67186921 def test_ooa_manual (self ):
67196922 demog_local = msprime .Demography ._ooa_model ()
67206923 debug_local = demog_local .debug ()
6721- model_sps = stdpopsim .get_species ("HomSap" ).get_demographic_model (
6722- "OutOfAfrica_3G09"
6723- )
6924+ (
6925+ population_configurations ,
6926+ migration_matrix ,
6927+ demographic_events ,
6928+ ) = self .stdpopsim_ooa_model ()
67246929 demog_sps = msprime .Demography .from_old_style (
6725- model_sps . population_configurations ,
6726- migration_matrix = model_sps . migration_matrix ,
6727- demographic_events = model_sps . demographic_events ,
6930+ population_configurations ,
6931+ migration_matrix = migration_matrix ,
6932+ demographic_events = demographic_events ,
67286933 )
67296934 debug_sps = demog_sps .debug ()
67306935
0 commit comments