Skip to content

Mass migration events at time zero are skipped #2353

Description

@nspope

Ran across this bug (?) while writing some tests-- consider a two-population island model, both populations of size $N$, with a mass migration event at time 0 where 50% of lineages from population B move to population A. Given this model, the expected proportion of (B, B) pairs that haven't coalesced by time $t$ is $0.5 + 0.5 \exp( -\frac{1}{2N} t )$. However, the demography debugger and simulations don't follow this expectation unless I set the time of the mass migration to a very small floating point value:

Note that in the first panel (with demography.add_mass_migration(time=0.0, ...) the demography debugger and simulator match neither one another nor the theoretical curve. Whereas, in the second panel (with demography.add_mass_migration(time=1e-15, ...) everything lines up. This is a bit of an edge case, as the mass migration will effectively just randomly reassign the population of sample nodes -- but silently ignoring it is probably not ideal (and I'm not sure what's going on with the debugger).

Code to reproduce:

import msprime
import numpy as np
import matplotlib.pyplot as plt

print(msprime.__version__) # '1.3.3.dev29+ge90c338' 

fig, axs = plt.subplots(
    1, 2, figsize=(8, 4), sharey=True, sharex=True, 
    constrained_layout=True,
)
for i, admix_time in enumerate([0.0, 1e-15]):
    # isolation model with "admixture event" at time 0 or slightly later
    demog = msprime.Demography()
    demog.add_population(name="A", initial_size=1e4)
    demog.add_population(name="B", initial_size=1e4)
    demog.add_population(name="C", initial_size=1e4)
    demog.add_mass_migration(time=admix_time, source="B", dest="A", proportion=0.5)
    demog.add_population_split(time=1e4, ancestral="C", derived=["A", "B"])
    time_grid = np.linspace(0, 1e4, 7)

    # surviving (B, B) pairs from debugger
    dbg = demog.debug()
    _, surv_debug = dbg.coalescence_rate_trajectory(lineages={"B": 2}, steps=time_grid)

    # surviving (B, B) pairs from simulation
    num_reps = 10000
    ts_gen = msprime.sim_ancestry(
        samples={"B":1}, 
        demography=demog, 
        recombination_rate=1e-8, 
        sequence_length=1e6, 
        num_replicates=num_reps,
    )
    E_cdf = np.zeros(time_grid.size)
    for ts in ts_gen:
        E_cdf[1:] += np.cumsum(
            ts.pair_coalescence_counts(
                time_windows=time_grid, 
                pair_normalise=True,
            )
        )
    surv_sim = 1 - E_cdf / num_reps

    # surviving (B, B) pairs from theory
    surv_theory = 0.5 * (1 + np.exp(-1 / 1e4 / 2 * time_grid))

    axs[i].plot(time_grid, surv_debug, "-o", color="red", label="debugger")
    axs[i].plot(time_grid, surv_sim, "-o", color="blue", label="simulation")
    axs[i].plot(time_grid, surv_theory, "-o", color="black", label="theory")
    axs[i].legend()
    axs[i].set_title(f"MassMig at time {admix_time}")
fig.supylabel("Proportion uncoalesced (B, B) pairs")
fig.supxlabel("Time ago")
plt.savefig("msp_skipped_massmig_bug.png")

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions