OSCR

Neural posterior estimation for population genetics.

Code ↔ Paper

21 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 21 matches · 1 of them tie a paragraph to a whole file, not to given lines: a weak match, whose lines are not tinted
  1. [1] § Methods › Population genetic tasks › Comparisons to MSMC2 ↔ experiments/variable-popn-size/baselines/msmc2_utils.py, lines 279–351 · score 0.91 · EM iterations, segment pattern, diploid individuals, fixed recombination, mutation rate, tree sequences
  2. [2] § Methods › Population genetic tasks › Embedding networks and summary statistics ↔ workflow/scripts/ts_processors.py, lines 325–410 · score 0.89 · Rogers Huff, distance bin, SNP pairs, gap, logarithmically, r2
  3. [3] § Methods › Population genetic tasks › Embedding networks and summary statistics ↔ workflow/scripts/embedding_networks.py, lines 328–397 · score 0.75 · bi directional GRU, dropout, PyTorch, vector, concatenated, layer
  4. [4] § Methods › Population genetic tasks › Embedding networks and summary statistics ↔ workflow/scripts/embedding_networks.py, lines 71–179 · score 0.71 · permutation invariance, exchangeable CNN, haplotype, LD, SNPs, genotypes
  5. [5] § Results › Inference of population bottleneck parameters ↔ workflow/scripts/ts_simulators.py, lines 163–212 · score 0.70 · South Middle Atlas, Arabidopsis thaliana, demographic model, stdpopsim, event, population
  6. [6] § Methods › Population genetic tasks › Comparisons to ABC ↔ experiments/aratha-2epoch-extra/abc_utils.py, lines 274–334 · score 0.66 · closest simulations, ABC rejection, training simulations, posterior sample, quantile, NPE
  7. [7] § Results › Inference of population bottleneck parameters ↔ experiments/aratha-2epoch-extra/plot-posterior-surfaces.py, lines 20–73 · score 0.63 · npe cnn, npe rnn, npe sfs, curvature, surfaces, match
  8. [8] § Results › Inference of population bottleneck parameters ↔ experiments/aratha-2epoch-extra/plot-posterior-surfaces.py, lines 20–73 · score 0.63 · npe cnn, npe rnn, npe sfs, surface, rows, likelihood
  9. [9] § Methods › Population genetic tasks › Application to Drosophila melanogaster ↔ workflow/scripts/ts_simulators.py, lines 95–160 · score 0.62 · ancestral population, isolation, haploid, CO, FR, migration
  10. [10] § Results › D. melanogaster out-of-Africa model ↔ experiments/variable-popn-size/compare-posteriors.py, lines 325–370 · score 0.59 · marginal distributions, generations ago, credible intervals, posterior samples, model, population
  11. [11] § Results › D. melanogaster out-of-Africa model ↔ experiments/dependent-variable-popn-size/compare-posteriors.py, lines 333–377 · score 0.59 · marginal distributions, generations ago, credible intervals, posterior samples, model, population
  12. [12] § Results › Inference of historical population sizes in a one-population-model ↔ experiments/dependent-variable-popn-size/compare-posteriors.py, lines 488–543 · score 0.57 · credible interval width, CI width, geometric, recombination rate, log, models
  13. [13] § Results › Inference of historical population sizes in a one-population-model ↔ experiments/variable-popn-size/compare-posteriors.py, lines 467–522 · score 0.57 · credible interval width, CI width, geometric, recombination rate, log, models
  14. [14] § Results › D. melanogaster out-of-Africa model ↔ depr/amortized_dadi_workflow/scripts/dadi_simulators.py, lines 15–84 · score 0.56 · migration rates, ancestral population, demographic model, split, growth
  15. [15] § Results › Inference of historical population sizes in a one-population-model ↔ workflow/scripts/ts_simulators.py, lines 380–519 · score 0.56 · 100–100000, uniform distribution, tree sequences, log10, msprime, spaced
  16. [16] § Results › Inference of population bottleneck parameters ↔ depr/amortized_dadi_workflow/scripts/dadi_simulators.py, lines 86–125 · score 0.56 · Arabidopsis thaliana, catalog, demographic model, stdpopsim, event, population
  17. [17] § Methods › Population genetic tasks › Application to Drosophila melanogaster ↔ depr/amortized_dadi_workflow/scripts/dadi_simulators.py, lines 15–84 · score 0.54 · migration rates, ancestral population, split, growth, uniform, Demographic
  18. [18] § Methods › NPE workflow ↔ experiments/dependent-variable-popn-size/simulate-for-posterior.py, lines 98–220 · score 0.52 · simulating tree sequences, model parameters, Python, recombination rate, raw, flow
  19. [19] § Methods › NPE workflow ↔ experiments/variable-popn-size/simulate-for-posterior.py, lines 224–366 · score 0.52 · simulating tree sequences, model parameters, Python, recombination rate, raw, flow
  20. [20] § Results › Inference of historical population sizes in a one-population-model ↔ experiments/variable-popn-size/run-ne-comparison-three-scenarios.sh, the whole file · a weak match · score 0.51 · linear decline, linear growth, CNN, RNN, population, model
  21. [21] § Methods › Population genetic tasks › Application to Drosophila melanogaster ↔ workflow/scripts/ts_simulators.py, lines 95–160 · score 0.50 · mutation rate, recombination rate, Drosophila, melanogaster, uniform, demographic

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 519 lines · 20 KB · MIT · 4 matches

  1. ## ts_simulators outputs tree sequence. This cannot be used as a simulator for simulate_for_sbi!
  2. import tskit
  3. import msprime
  4. import demes
  5. import torch
  6. import numpy as np
  7. import stdpopsim
  8. from sbi.utils import BoxUniform
  9. class BaseSimulator:
  10. def __init__(self, config: dict, default: dict):
  11. for key in config:
  12. if key == "class_name": continue
  13. assert key in default, f"Option {key} not available for simulator"
  14. for key, default in default.items():
  15. setattr(self, key, config.get(key, default))
  16. class YRI_CEU(BaseSimulator):
  17. """
  18. Simulate a model defined in dadi's manual
  19. (https://dadi.readthedocs.io/en/latest/examples/YRI_CEU/YRI_CEU/).
  20. Ancestral population changes in size; splits into YRI/CEU; CEU population
  21. undergoes a bottleneck upon splitting and subsequently grows exponentially.
  22. There is continuous symmetric migration between YRI and CEU.
  23. """
  24. default_config = {
  25. # FIXED PARAMETERS
  26. "samples": {"YRI": 10, "CEU": 10},
  27. "sequence_length": 10e6,
  28. "recombination_rate": 1.5e-8,
  29. "mutation_rate": 1.5e-8,
  30. # RANDOM PARAMETERS (UNIFORM)
  31. "N_A": [1e2, 1e5],
  32. "N_YRI": [1e2, 1e5],
  33. "N_CEU_initial": [1e2, 1e5],
  34. "N_CEU_final": [1e2, 1e5],
  35. "M": [0, 5e-4],
  36. "Tp": [0, 6e4],
  37. "T": [0, 6e4],
  38. }
  39. def __init__(self, config: dict):
  40. super().__init__(config, self.default_config)
  41. self.parameters = ["N_A", "N_YRI", "N_CEU_initial", "N_CEU_final", "M", "Tp", "T"]
  42. self.prior = BoxUniform(
  43. low=torch.tensor([getattr(self, p)[0] for p in self.parameters]),
  44. high=torch.tensor([getattr(self, p)[1] for p in self.parameters]),
  45. )
  46. def __call__(self, seed: int = None) -> (tskit.TreeSequence, np.ndarray):
  47. torch.manual_seed(seed)
  48. theta = self.prior.sample().numpy()
  49. N_A, N_YRI, N_CEU_initial, N_CEU_final, M, Tp, T = theta
  50. graph = demes.Builder()
  51. graph.add_deme(
  52. "ancestral",
  53. epochs=[dict(start_size=N_A, end_time=Tp + T)]
  54. )
  55. graph.add_deme(
  56. "AMH",
  57. ancestors=["ancestral"],
  58. epochs=[dict(start_size=N_YRI, end_time=T)],
  59. )
  60. graph.add_deme(
  61. "CEU",
  62. ancestors=["AMH"],
  63. epochs=[dict(start_size=N_CEU_initial, end_size=N_CEU_final)],
  64. )
  65. graph.add_deme(
  66. "YRI",
  67. ancestors=["AMH"],
  68. epochs=[dict(start_size=N_YRI)],
  69. )
  70. graph.add_migration(demes=["CEU", "YRI"], rate=M)
  71. demog = msprime.Demography.from_demes(graph.resolve())
  72. ts = msprime.sim_ancestry(
  73. self.samples,
  74. demography=demog,
  75. sequence_length=self.sequence_length,
  76. recombination_rate=self.recombination_rate,
  77. random_seed=seed,
  78. )
  79. ts = msprime.sim_mutations(ts, rate=self.mutation_rate, random_seed=seed)
  80. return ts, theta
  81. class DroMel_CO_FR(BaseSimulator):
  82. """
  83. Simulate a two-population isolation-with-migration model for
  84. Drosophila melanogaster, Congolese and French populations,
  85. with growth in the French population
  86. """
  87. default_config = {
  88. # FIXED PARAMETERS
  89. "samples": {"CO": 10, "FR": 10},
  90. "sequence_length": 1e6,
  91. "recombination_rate": [0.5e-8, 3.0e-8],
  92. "mutation_rate": 5.49e-9,
  93. "haploid": True,
  94. # RANDOM PARAMETERS (UNIFORM IN LOGSPACE)
  95. "N_ANC": [3.5, 6.5], # log10 ancestral population size
  96. "N_CO": [3.5, 6.5], # log10 Congolese population size
  97. "N_FR0": [3.5, 6.5], # log10 French population size after split
  98. "N_FR1": [3.5, 6.5], # log10 French population size after growth
  99. "T": [3, 6], # log10 split time
  100. "m_CO_FR": [-8, -3], # log10 migration Congolese to French
  101. "m_FR_CO": [-8, -3], # log10 migration French to Congolese
  102. }
  103. def __init__(self, config: dict):
  104. super().__init__(config, self.default_config)
  105. self.parameters = ["N_ANC", "N_CO", "N_FR0", "N_FR1", "T", "m_CO_FR", "m_FR_CO"]
  106. self.prior = BoxUniform(
  107. low=torch.tensor([getattr(self, p)[0] for p in self.parameters]),
  108. high=torch.tensor([getattr(self, p)[1] for p in self.parameters]),
  109. )
  110. def __call__(self, seed: int = None) -> (tskit.TreeSequence, np.ndarray):
  111. torch.manual_seed(seed)
  112. theta = self.prior.sample().numpy()
  113. seeds = torch.randint(2 ** 32 - 1, (2, )).numpy()
  114. r = self.recombination_rate[0] + \
  115. (self.recombination_rate[1] - self.recombination_rate[0]) * \
  116. torch.rand(1).item()
  117. N_ANC, N_CO, N_FR0, N_FR1, T, m_CO_FR, m_FR_CO = (10 ** theta)
  118. G_FR = np.log(N_FR1 / N_FR0) / T
  119. demogr = msprime.Demography()
  120. demogr.add_population(name="CO", initial_size=N_CO)
  121. demogr.add_population(name="FR", initial_size=N_FR1, growth_rate=G_FR)
  122. demogr.add_population(name="ANC", initial_size=N_ANC)
  123. demogr.migration_matrix = np.array([[0, m_CO_FR, 0], [m_FR_CO, 0, 0], [0, 0, 0]])
  124. demogr.add_population_split(time=T, derived=["CO", "FR"], ancestral="ANC")
  125. samples = [
  126. msprime.SampleSet(n, population=p, ploidy=1 if self.haploid else 2)
  127. for p, n in self.samples.items()
  128. ]
  129. ts = msprime.sim_ancestry(
  130. samples,
  131. demography=demogr,
  132. sequence_length=self.sequence_length,
  133. recombination_rate=r,
  134. random_seed=seeds[0],
  135. )
  136. ts = msprime.sim_mutations(
  137. ts,
  138. rate=self.mutation_rate,
  139. random_seed=seeds[1],
  140. )
  141. return ts, theta
  142. class AraTha_2epoch(BaseSimulator):
  143. """
  144. Simulate the African2Epoch_1H18 model from stdpopsim for Arabidopsis thaliana.
  145. The model consists of a single population that undergoes a size change.
  146. """
  147. species = stdpopsim.get_species("AraTha")
  148. model = species.get_demographic_model("African2Epoch_1H18")
  149. default_config = {
  150. # FIXED PARAMETERS
  151. "samples": {"SouthMiddleAtlas": 10},
  152. "sequence_length": 10e6,
  153. # RANDOM PARAMETERS (UNIFORM)
  154. "nu": [0.01, 1], # Ratio of current to ancestral population size
  155. "T": [0.01, 1.5], # Time of size change (scaled)
  156. }
  157. def __init__(self, config: dict):
  158. super().__init__(config, self.default_config)
  159. self.parameters = ["nu", "T"]
  160. self.prior = BoxUniform(
  161. low=torch.tensor([getattr(self, p)[0] for p in self.parameters]),
  162. high=torch.tensor([getattr(self, p)[1] for p in self.parameters]),
  163. )
  164. def __call__(self, seed: int = None) -> (tskit.TreeSequence, np.ndarray):
  165. torch.manual_seed(seed)
  166. theta = self.prior.sample().numpy()
  167. nu, T = theta
  168. species = self.species
  169. contig = species.get_contig(
  170. length=self.sequence_length,
  171. )
  172. model = self.model
  173. # Scale the population size and time parameters
  174. N_A = model.model.events[0].initial_size # ancestral population size
  175. model.populations[0].initial_size = nu * N_A # current population size
  176. model.model.events[0].time = T * 2 * N_A # time of size change
  177. engine = stdpopsim.get_engine("msprime")
  178. ts = engine.simulate(
  179. model,
  180. contig,
  181. samples=self.samples,
  182. random_seed=seed
  183. )
  184. return ts, theta
  185. class VariablePopulationSize(BaseSimulator):
  186. """
  187. Simulate a model with varying population size across multiple time windows.
  188. The model consists of a single population that undergoes multiple size changes.
  189. """
  190. default_config = {
  191. # FIXED PARAMETERS
  192. "samples": {"pop": 10},
  193. "sequence_length": 10e6,
  194. "mutation_rate": 1.5e-8,
  195. "num_time_windows": 3,
  196. # RANDOM PARAMETERS (UNIFORM)
  197. "pop_sizes": [1e2, 1e5], # Range for population sizes (log10 space)
  198. "recomb_rate": [1e-9, 2e-8], # Range for recombination rate
  199. # TIME PARAMETERS
  200. "max_time": 100000, # Maximum time for population events
  201. "time_rate": 0.1, # Rate at which time changes across windows
  202. }
  203. def __init__(self, config: dict):
  204. super().__init__(config, self.default_config)
  205. # Set up parameters list
  206. self.parameters = [f"N_{i}" for i in range(self.num_time_windows)] + ["recomb_rate"]
  207. # Create parameter ranges in the same format as AraTha_2epoch
  208. # Population sizes (in log10 space)
  209. pop_size_ranges = [[np.log10(self.pop_sizes[0]), np.log10(self.pop_sizes[1])]
  210. for _ in range(self.num_time_windows)]
  211. # Add recombination rate range
  212. param_ranges = pop_size_ranges + [self.recomb_rate]
  213. # Set up prior using BoxUniform
  214. self.prior = BoxUniform(
  215. low=torch.tensor([r[0] for r in param_ranges]),
  216. high=torch.tensor([r[1] for r in param_ranges])
  217. )
  218. # Calculate fixed time points for population size changes
  219. self.change_times = self._calculate_change_times()
  220. def _calculate_change_times(self) -> np.ndarray:
  221. """Calculate the times at which population size changes occur using an exponential spacing."""
  222. #times = [(np.exp(np.log(1 + self.time_rate * self.max_time) * i /
  223. # (self.num_time_windows - 1)) - 1) / self.time_rate
  224. # for i in range(self.num_time_windows)]
  225. win = np.logspace(2, np.log10(self.max_time), self.num_time_windows)
  226. win[0] = 0
  227. times = win
  228. return np.around(times).astype(int)
  229. def __call__(self, seed: int = None) -> (tskit.TreeSequence, np.ndarray):
  230. if seed is not None:
  231. torch.manual_seed(seed)
  232. min_snps = 400
  233. max_attempts = 100
  234. attempt = 0
  235. while attempt < max_attempts:
  236. # Sample parameters directly from prior (like AraTha_2epoch)
  237. theta = self.prior.sample().numpy()
  238. # Convert population sizes from log10 space
  239. pop_sizes = 10 ** theta[:-1] # All but last element are population sizes
  240. recomb_rate = theta[-1] # Last element is recombination rate
  241. # Create demography
  242. demography = msprime.Demography()
  243. demography.add_population(name="pop0", initial_size=float(pop_sizes[0]))
  244. # Add population size changes at calculated time intervals
  245. for i in range(1, len(pop_sizes)):
  246. demography.add_population_parameters_change(
  247. time=self.change_times[i],
  248. initial_size=float(pop_sizes[i]),
  249. growth_rate=0,
  250. population="pop0"
  251. )
  252. # Simulate ancestry
  253. ts = msprime.sim_ancestry(
  254. samples={"pop0": self.samples["pop"]},
  255. demography=demography,
  256. sequence_length=self.sequence_length,
  257. recombination_rate=recomb_rate,
  258. random_seed=seed
  259. )
  260. # Add mutations
  261. ts = msprime.sim_mutations(ts, rate=self.mutation_rate, random_seed=seed)
  262. # Check if we have enough SNPs after MAF filtering
  263. geno = ts.genotype_matrix().T
  264. num_sample = geno.shape[0]
  265. if (geno==2).any():
  266. num_sample *= 2
  267. row_sum = np.sum(geno, axis=0)
  268. keep = np.logical_and.reduce([
  269. row_sum != 0,
  270. row_sum != num_sample,
  271. row_sum > num_sample * 0.05,
  272. num_sample - row_sum > num_sample * 0.05
  273. ])
  274. if np.sum(keep) >= min_snps:
  275. return ts, theta
  276. attempt += 1
  277. if seed is not None:
  278. seed += 1
  279. raise RuntimeError(f"Failed to generate tree sequence with at least {min_snps} SNPs after {max_attempts} attempts")
  280. class recombination_rate(BaseSimulator):
  281. """
  282. Simulate a one population model where recombination rate varies
  283. among replicates. The prior is a beta distribution shifted/scaled
  284. to a given interval (by default, a noninformative beta).
  285. """
  286. default_config = {
  287. # FIXED PARAMETERS
  288. "samples": {0: 10},
  289. "sequence_length": 1e6,
  290. "mutation_rate": 1.5e-8,
  291. "pop_size": 1e4,
  292. "mean_and_dispersion": [0.5, 0.5], # beta mean and dispersion
  293. # RANDOM PARAMETERS (UNIFORM)
  294. "recombination_rate": [0, 1e-8], # bounds on recombination rate
  295. }
  296. def __init__(self, config: dict):
  297. super().__init__(config, self.default_config)
  298. self.parameters = ["recombination_rate"]
  299. self.prior = BoxUniform(
  300. low=torch.tensor([getattr(self, p)[0] for p in self.parameters]),
  301. high=torch.tensor([getattr(self, p)[1] for p in self.parameters]),
  302. )
  303. def __call__(self, seed: int = None) -> (tskit.TreeSequence, np.ndarray):
  304. torch.manual_seed(seed)
  305. mean, dispersion = self.mean_and_dispersion
  306. alpha, beta = mean / dispersion, (1 - mean) / dispersion
  307. prior = torch.distributions.Beta(torch.FloatTensor([alpha]), torch.FloatTensor([beta]))
  308. low, high = self.recombination_rate
  309. theta = low + (high - low) * prior.sample().numpy()
  310. recombination_rate = theta.item()
  311. ts = msprime.sim_ancestry(
  312. self.samples,
  313. population_size=self.pop_size,
  314. sequence_length=self.sequence_length,
  315. recombination_rate=recombination_rate,
  316. random_seed=seed,
  317. discrete_genome=False, # don't want overlapping mutations
  318. )
  319. ts = msprime.sim_mutations(ts, rate=self.mutation_rate, random_seed=seed)
  320. return ts, theta
  321. class DependentVariablePopulationSize(BaseSimulator):
  322. """
  323. Simulate a population with variable population size across multiple time windows, with each
  324. population size dependent on the previous one.
  325. The model consists of a single population that undergoes multiple size changes.
  326. """
  327. default_config = {
  328. # FIXED PARAMETERS
  329. "samples": {"pop0": 25},
  330. "sequence_length": 2e6,
  331. "mutation_rate": 1e-8,
  332. "num_time_windows": 21,
  333. "maf": 0.05,
  334. # RANDOM PARAMETERS (UNIFORM)
  335. "pop_sizes": [1e2, 1e5], # Range for population sizes (log10 space)
  336. "pop_changes": [-1, 1], # Range for population size changes (* 10 ** beta)
  337. "recomb_rate": [1e-9, 1e-8], # Range for recombination rate
  338. # TIME PARAMETERS
  339. "max_time": 130000, # Maximum time for population events
  340. "time_rate": 0.1, # Rate at which time changes across windows
  341. }
  342. def __init__(self, config: dict):
  343. super().__init__(config, self.default_config)
  344. # Set up parameters list
  345. self.parameters = [f"N_{i}" for i in range(self.num_time_windows)] + ["recomb_rate"]
  346. # Create parameter ranges in the same format as AraTha_2epoch
  347. # Population sizes (in log10 space)
  348. pop_size_range = [[np.log10(self.pop_sizes[0]), np.log10(self.pop_sizes[1])]
  349. for _ in range(self.num_time_windows)]
  350. pop_change_ranges = [[self.pop_changes[0], self.pop_changes[1]]
  351. for _ in range(1,self.num_time_windows)]
  352. # Add recombination rate range
  353. param_ranges = pop_size_range + [self.recomb_rate]
  354. # Set up prior using BoxUniform
  355. self.prior = BoxUniform(
  356. low=torch.tensor([r[0] for r in param_ranges]),
  357. high=torch.tensor([r[1] for r in param_ranges])
  358. )
  359. self.beta_prior = BoxUniform(
  360. low=torch.tensor([b[0] for b in pop_change_ranges]),
  361. high=torch.tensor([b[1] for b in pop_change_ranges])
  362. )
  363. # Calculate fixed time points for population size changes
  364. self.change_times = self._calculate_change_times()
  365. def _calculate_change_times(self) -> np.ndarray:
  366. """Calculate the times at which population size changes occur using an exponential spacing."""
  367. times = [(np.exp(np.log(1 + self.time_rate * self.max_time) * i /
  368. (self.num_time_windows - 1)) - 1) / self.time_rate
  369. for i in range(self.num_time_windows)]
  370. return np.around(times).astype(int)
  371. def _generate_dependent_pop_sizes(self) -> np.ndarray:
  372. """
  373. Generate a sequence of population sizes where the first population size N_1
  374. is sampled from a uniform distribution corresponding to the most recent
  375. time window. The following population sizes are generated following
  376. N_i = N_{i-1} * 10 ^ β for i in [2,...,num_time_windows], unless N_i
  377. is outside of pop_ranges. If so N_i is set to the max/min population size
  378. """
  379. prior_sample = self.prior.sample().numpy()
  380. # only the first sample from prior will be used
  381. beta_sample = self.beta_prior.sample().numpy()
  382. modified_prior = prior_sample.copy()
  383. # The first value is uniformly sampled within the log10 bounds
  384. for i in range(self.num_time_windows-1):
  385. # For subsequent time windows, calculate the new value based on the previous one and beta
  386. new_value = modified_prior[i] + beta_sample[i]
  387. # If the new value is outside the bounds, set it to the max/min
  388. if new_value > np.log10(self.pop_sizes[1]):
  389. new_value = np.log10(self.pop_sizes[1])
  390. if new_value < np.log10(self.pop_sizes[0]):
  391. new_value = np.log10(self.pop_sizes[0])
  392. modified_prior[i+1] = new_value
  393. # Return the sampled prior and recombination rate
  394. return modified_prior
  395. def __call__(self, seed: int = None) -> (tskit.TreeSequence, np.ndarray):
  396. if seed is not None:
  397. torch.manual_seed(seed)
  398. min_snps = 400
  399. max_attempts = 100
  400. attempt = 0
  401. while attempt < max_attempts:
  402. # Sample parameters directly from prior (like AraTha_2epoch)
  403. theta = self._generate_dependent_pop_sizes()
  404. pop_sizes = 10**theta[:-1] # All but last element are for the population sizes
  405. recomb_rate = theta[-1] # Last element is recombination rate
  406. # Create demography
  407. demography = msprime.Demography()
  408. demography.add_population(name="pop0", initial_size=float(pop_sizes[0]))
  409. # Add population size changes at calculated time intervals
  410. for i in range(1, len(pop_sizes)):
  411. demography.add_population_parameters_change(
  412. time=self.change_times[i],
  413. initial_size=float(pop_sizes[i]),
  414. growth_rate=0,
  415. population="pop0"
  416. )
  417. # Simulate ancestry
  418. ts = msprime.sim_ancestry(
  419. samples=self.samples,
  420. demography=demography,
  421. sequence_length=self.sequence_length,
  422. recombination_rate=recomb_rate,
  423. random_seed=seed
  424. )
  425. # Add mutations
  426. ts = msprime.sim_mutations(ts, rate=self.mutation_rate, random_seed=seed)
  427. # Check if we have enough SNPs after MAF filtering
  428. geno = ts.genotype_matrix().T
  429. num_sample = ts.num_samples
  430. row_sum = np.sum(geno, axis=0)
  431. keep = np.logical_and.reduce([
  432. row_sum > num_sample * self.maf,
  433. num_sample - row_sum > num_sample * self.maf
  434. ])
  435. if np.sum(keep) >= min_snps:
  436. return ts, theta
  437. attempt += 1
  438. if seed is not None:
  439. seed += 1
  440. raise RuntimeError(f"Failed to generate tree sequence with at least {min_snps} SNPs after {max_attempts} attempts")

ts_simulators.py at commit e81dfe6, under MIT · at the source

Overview

Authors: Jiseon Min1, Yuxin Ning2, Nathaniel S Pope1, Franz Baumdicker2,3, Andrew D Kern1
  1. Institute of Ecology and Evolution, University of Oregon, Eugene, OR 97403, United States
  2. Institute for Bioinformatics and Medical Informatics (IBMI), University of Tübingen, Tübingen 72076, Germany
  3. Big Data Analytics in Bioinformatics, Justus-Liebig University, Gießen 35390, Germany
Institutions: University of Oregon (United States); University of Tübingen (Germany); Justus-Liebig-Universität Gießen (Germany)
Journal: Genetics, volume 233, issue 3, article iyag107
Dates: published online 6 July 2026; in print July 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1093/genetics/iyag107 · PMID 42032815 · PMCID PMC13334119 · OpenAlex W4417116550
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: drosophila (organism), computational (subfield)
Methods: Statistics, Preprocessing, Machine learning
Keywords: population genetics, deep learning, machine learning, Bayesian inference, simulation
MeSH: Genetics, Population*, Models, Genetic*, Neural Networks, Computer*, Animals, Bayes Theorem, Computer Simulation, Drosophila melanogaster, Machine Learning (* major topic)
Topic: Markov Chains and Monte Carlo Methods (Statistics and Probability, Mathematics), according to OpenAlex
Funding: NIH (R01HG012473, R01HG010774, R35GM148253); NIGMS NIH HHS (R35 GM148253); NIH HHS (R01HG012473, R01HG010774, R35GM148253); DFG; German Research Foundation; NHGRI NIH HHS (R01 HG010774, R01 HG012473); Deutsche Forschungsgemeinschaft
Citations: cited by 2 papers (Europe PMC); 74 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repository

Its files are read in the Code ↔ Paper reader above, with 21 matches between paragraphs and lines of code.

kr-colab/popgen-npe

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: e81dfe66d32ff194e42c3c68941e647f824e0a32, 22 June 2026
Languages: Python (52), Shell (9), Jupyter (1)
Size: 183 files, 62 scripts
Software Heritage: not archived
Found in: “Data availability”
Holds: README, license file, environment (environment.yaml, pyproject.toml, docs/requirements.txt), tests, documentation, 1 notebook
Not found: CITATION.cff, continuous integration
Tools: NumPy (47 files), PyTorch (25 files), Matplotlib (22 files), seaborn (13 files), PyTorch Lightning (6 files), pandas (5 files), Snakemake (5 files), SciPy (3 files), PyMC (2 files), pysam (2 files), BCFtools (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
64 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 62 scripts, each with its path and the digest of its content;
  • 21 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Code and data availability statement

The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1093/genetics/iyag107.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 2, 28 September 2026

  • Publisher: n/a → Oxford University Press

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 5 keywords, 8 MeSH terms, 7 funders, 61 references.

Cite

This paper

Min, J., Ning, Y., Pope, N. S., Baumdicker, F., & Kern, A. D. (2026). Neural posterior estimation for population genetics. Genetics, 233(3), iyag107. https://doi.org/10.1093/genetics/iyag107

BibTeX

@article{min2026neural,
author = {Min, Jiseon and Ning, Yuxin and Pope, Nathaniel S and Baumdicker, Franz and Kern, Andrew D},
title = {{Neural posterior estimation for population genetics}},
journal = {Genetics},
year = {2026},
month = jul,
volume = {233},
number = {3},
pages = {iyag107},
publisher = {Oxford University Press},
issn = {0016-6731},
doi = {10.1093/genetics/iyag107},
url = {https://doi.org/10.1093/genetics/iyag107},
pmid = {42032815},
pmcid = {PMC13334119}
}

RIS

TY - JOUR
AU - Min, Jiseon
AU - Ning, Yuxin
AU - Pope, Nathaniel S
AU - Baumdicker, Franz
AU - Kern, Andrew D
TI - Neural posterior estimation for population genetics
T2 - Genetics
J2 - Genetics
PY - 2026
DA - 2026/07/01
VL - 233
IS - 3
SP - iyag107
SN - 0016-6731
PB - Oxford University Press
DO - 10.1093/genetics/iyag107
UR - https://doi.org/10.1093/genetics/iyag107
LA - en
ER -

CSL-JSON

{
"id": "10.1093/genetics/iyag107",
"type": "article-journal",
"title": "Neural posterior estimation for population genetics",
"container-title": "Genetics",
"author": [
{
"family": "Min",
"given": "Jiseon"
},
{
"family": "Ning",
"given": "Yuxin"
},
{
"family": "Pope",
"given": "Nathaniel S"
},
{
"family": "Baumdicker",
"given": "Franz"
},
{
"family": "Kern",
"given": "Andrew D"
}
],
"container-title-short": "Genetics",
"volume": "233",
"issue": "3",
"page": "iyag107",
"DOI": "10.1093/genetics/iyag107",
"PMID": "42032815",
"PMCID": "PMC13334119",
"ISSN": "0016-6731",
"publisher": "Oxford University Press",
"URL": "https://doi.org/10.1093/genetics/iyag107",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
1
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s44400-026-00094-8 [code]
Haplotype-resolved DNA methylation at the &lt;i&gt;APOE&lt;/i&gt; locus identifies allele-specific epigenetic signatures relevant to Alzheimer's disease risk.
Journal: NPJ dementia
In common: Snakemake, BCFtools, pysam, 6 other tools
[2] doi:10.1016/j.celrep.2026.117110 [code]
Single-nucleus multiome analysis in the human prefrontal cortex identifies gene expression and cis-regulatory elements associated with aging.
Journal: Cell reports
In common: Snakemake, BCFtools, pysam, 6 other tools
[3] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: PyMC, BCFtools, PyTorch Lightning, 6 other tools
[4] doi:10.1093/evlett/qrag004 [code]
AI solutions for evolutionary genomics of nonmodel species.
Journal: Evolution letters
In common: NumPy, 6 references
[5] doi:10.1038/s41467-026-71790-5 [code]
Recurrent DNA break clusters drive replication-stress-induced copy number variants and genome diversification.
Journal: Nature communications
In common: Snakemake, BCFtools, pysam, 5 other tools
[6] doi:10.1016/j.xcrm.2026.102904 [code]
High-dose furmonertinib as first-line treatment for untreated EGFR-mutated advanced NSCLC with central nervous system metastases: A phase 2 trial.
Journal: Cell reports. Medicine
In common: PyMC, pysam, PyTorch Lightning, 4 other tools
[7] doi:10.1371/journal.pcbi.1014364 [code]
A comparative study of simulation-based inference methods for epidemic models with identifiability considerations.
Journal: PLoS computational biology
In common: PyTorch, pandas, SciPy, 2 other tools, computational, 3 references
[8] doi:10.1093/bioinformatics/btag652 [code]
mmVelo: a deep generative model for estimating cell state-dependent dynamics across multiple modalities.
Journal: Bioinformatics (Oxford, England)
In common: pysam, PyTorch Lightning, PyTorch, 5 other tools
[9] doi:10.1186/s13059-026-04177-w [code]
Genomic sequence evolution underlying human neocortical interareal diversification.
Journal: Genome biology
In common: Snakemake, pysam, seaborn, 4 other tools
[10] doi:10.1016/j.celrep.2026.117073 [code]
Single-cell epigenomics uncovers heterochromatin instability and transcription factor dysfunction during mouse brain aging.
Journal: Cell reports
In common: Snakemake, pysam, seaborn, 4 other tools

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.