Skip to content

Commit 5b15d18

Browse files
Copy in definitions of OOA models from stdpopsim
Closes #2233
1 parent fcef2a6 commit 5b15d18

1 file changed

Lines changed: 236 additions & 31 deletions

File tree

tests/test_demography.py

Lines changed: 236 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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
#
@@ -33,7 +33,6 @@
3333

3434
import numpy as np
3535
import pytest
36-
import stdpopsim
3736
import tskit
3837

3938
import msprime
@@ -6519,6 +6518,209 @@ def test_as_lineage_movement_abstract(self):
65196518

65206519

65216520
class 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

Comments
 (0)