1919
2020
2121logger = daiquiri .getLogger ()
22+ INFINITY = sys .float_info .max
2223
2324
2425class FenwickTree :
@@ -247,7 +248,7 @@ def _get_common_ancestor_waiting_time(self, np, t):
247248 Returns the random waiting time until a common ancestor event
248249 occurs within this population.
249250 """
250- ret = sys . float_info . max
251+ ret = INFINITY
251252 u = random .expovariate (2 * np )
252253 if self .growth_rate == 0 :
253254 ret = self .start_size * u
@@ -267,7 +268,7 @@ def _get_common_ancestor_waiting_time(self, np, t):
267268 def get_common_ancestor_waiting_time_hudson (self ):
268269 def _get_common_ancestor_waiting_time_hudson (t ):
269270 k = self .get_num_ancestors ()
270- ret = sys . float_info . max
271+ ret = INFINITY
271272 if k > 1 :
272273 np = k * (k - 1 ) / 2
273274 ret = self ._get_common_ancestor_waiting_time (np , t )
@@ -278,7 +279,7 @@ def _get_common_ancestor_waiting_time_hudson(t):
278279 def get_common_ancestor_waiting_time_smc_k (self ):
279280 def _get_common_ancestor_waiting_time_smc_k (t ):
280281 np = self .get_num_pairs ()
281- ret = sys . float_info . max
282+ ret = INFINITY
282283 if np > 0 :
283284 ret = self ._get_common_ancestor_waiting_time (np , t )
284285 return ret
@@ -885,6 +886,7 @@ def __init__(
885886 gene_conversion_length = 1 ,
886887 discrete_genome = True ,
887888 hull_offset = None ,
889+ stop_at_local_mrca = True ,
888890 ):
889891 # Must be a square matrix.
890892 N = len (migration_matrix )
@@ -906,6 +908,7 @@ def __init__(
906908 self .migration_matrix = migration_matrix
907909 self .num_labels = num_labels
908910 self .num_populations = N
911+ self .stop_at_local_mrca = stop_at_local_mrca
909912 self .max_segments = max_segments
910913 self .coalescing_segments_only = coalescing_segments_only
911914 self .additional_nodes = msprime .NodeType (additional_nodes )
@@ -964,7 +967,7 @@ def __init__(
964967 self .sweep_trajectory = sweep_trajectory
965968 self .time_slice = time_slice
966969
967- self .modifier_events = [(sys . float_info . max , None , None )]
970+ self .modifier_events = [(INFINITY , None , None )]
968971 for time , pop_id , new_size in population_size_changes :
969972 self .modifier_events .append (
970973 (time , self .change_population_size , (int (pop_id ), new_size ))
@@ -1253,7 +1256,7 @@ def simulate(self, end_time):
12531256 if self .model == "hudson" :
12541257 self .hudson_simulate (end_time )
12551258 elif self .model == "dtwf" :
1256- self .dtwf_simulate ()
1259+ self .dtwf_simulate (end_time )
12571260 elif self .model == "fixed_pedigree" :
12581261 self .pedigree_simulate ()
12591262 elif self .model == "single_sweep" :
@@ -1315,7 +1318,6 @@ def hudson_simulate(self, end_time):
13151318 """
13161319 Simulates the algorithm until all loci have coalesced.
13171320 """
1318- infinity = sys .float_info .max
13191321 non_empty_pops = {pop .id for pop in self .P if pop .get_num_ancestors () > 0 }
13201322 potential_destinations = self .get_potential_destinations ()
13211323
@@ -1326,31 +1328,31 @@ def hudson_simulate(self, end_time):
13261328 break
13271329 # self.print_state()
13281330 re_rate = self .get_total_recombination_rate (label = 0 )
1329- t_re = infinity
1331+ t_re = INFINITY
13301332 if re_rate > 0 :
13311333 t_re = random .expovariate (re_rate )
13321334
13331335 # Gene conversion can occur within segments ..
13341336 gc_rate = self .get_total_gc_rate (label = 0 )
1335- t_gcin = infinity
1337+ t_gcin = INFINITY
13361338 if gc_rate > 0 :
13371339 t_gcin = random .expovariate (gc_rate )
13381340 # ... or to the left of the first segment.
13391341 gc_left_rate = self .get_total_gc_left_rate (label = 0 )
1340- t_gc_left = infinity
1342+ t_gc_left = INFINITY
13411343 if gc_left_rate > 0 :
13421344 t_gc_left = random .expovariate (gc_left_rate )
13431345
13441346 # Common ancestor events occur within demes.
1345- t_ca = infinity
1347+ t_ca = INFINITY
13461348 for index in non_empty_pops :
13471349 pop = self .P [index ]
13481350 assert pop .get_num_ancestors () > 0
13491351 t = pop .get_common_ancestor_waiting_time (self .t )
13501352 if t < t_ca :
13511353 t_ca = t
13521354 ca_population = index
1353- t_mig = infinity
1355+ t_mig = INFINITY
13541356 # Migration events happen at the rates in the matrix.
13551357 for j in non_empty_pops :
13561358 source_size = self .P [j ].get_num_ancestors ()
@@ -1365,7 +1367,9 @@ def hudson_simulate(self, end_time):
13651367 mig_source = j
13661368 mig_dest = k
13671369 min_time = min (t_re , t_ca , t_gcin , t_gc_left , t_mig )
1368- assert min_time != infinity
1370+ assert (min_time != INFINITY ) or (
1371+ (min_time == INFINITY ) and not self .stop_at_local_mrca
1372+ )
13691373 if self .t + min_time > self .modifier_events [0 ][0 ]:
13701374 t , func , args = self .modifier_events .pop (0 )
13711375 self .t = t
@@ -1379,7 +1383,14 @@ def hudson_simulate(self, end_time):
13791383 event = "MOD"
13801384 else :
13811385 self .t += min_time
1382- if min_time == t_re :
1386+ if min_time >= INFINITY :
1387+ assert not self .stop_at_local_mrca
1388+ event = "END"
1389+ if end_time >= INFINITY :
1390+ end_time = self .t
1391+ else :
1392+ self .t = end_time
1393+ elif min_time == t_re :
13831394 event = "RE"
13841395 self .hudson_recombination_event (0 )
13851396 elif min_time == t_gcin :
@@ -1400,6 +1411,7 @@ def hudson_simulate(self, end_time):
14001411 non_empty_pops .remove (mig_source )
14011412 assert self .P [mig_dest ].get_num_ancestors () > 0
14021413 non_empty_pops .add (mig_dest )
1414+
14031415 logger .info (
14041416 "%s time=%f n=%d" ,
14051417 event ,
@@ -1524,20 +1536,39 @@ def pedigree_simulate(self):
15241536 self .pedigree = Pedigree (self .tables )
15251537 self .dtwf_climb_pedigree ()
15261538
1527- def dtwf_simulate (self ):
1539+ def dtwf_above_root_simulations_stop (self , events_happened ):
1540+ return (
1541+ not events_happened
1542+ and not self .stop_at_local_mrca
1543+ and sum (pop .get_num_ancestors () for pop in self .P ) == 1
1544+ )
1545+
1546+ def dtwf_simulate (self , end_time ):
15281547 """
15291548 Simulates the algorithm until all loci have coalesced.
15301549 """
1531- while self .ancestors_remain ():
1550+ while self .ancestors_remain () and self . t < end_time :
15321551 self .t += 1
15331552 self .verify ()
1534- self .dtwf_generation ()
1553+ events_happened = self .dtwf_generation ()
1554+
1555+ if self .dtwf_above_root_simulations_stop (events_happened ):
1556+ if end_time >= INFINITY :
1557+ end_time = self .t
1558+ else :
1559+ self .t = end_time
1560+ break
15351561
15361562 def dtwf_generation (self ):
15371563 """
15381564 Evolves one generation of a Wright Fisher population
1565+ Returns True if any events occurred, False otherwise.
15391566 """
15401567 # Migration events happen at the rates in the matrix.
1568+ nodes = self .tables .nodes .num_rows
1569+ edges = self .tables .edges .num_rows
1570+ no_migration = True
1571+
15411572 for j in range (len (self .P )):
15421573 source_size = self .P [j ].get_num_ancestors ()
15431574 for k in range (len (self .P )):
@@ -1549,6 +1580,7 @@ def dtwf_generation(self):
15491580 mig_source = j
15501581 mig_dest = k
15511582 self .migration_event (mig_source , mig_dest )
1583+ no_migration = False
15521584
15531585 for pop_idx , pop in enumerate (self .P ):
15541586 # Cluster haploid inds by parent
@@ -1605,6 +1637,14 @@ def dtwf_generation(self):
16051637 h , pop_idx , 0 , parent_nodes [ploid ]
16061638 ) # label 0 only
16071639 self .verify ()
1640+ if (
1641+ self .tables .nodes .num_rows == nodes
1642+ and self .tables .edges .num_rows == edges
1643+ and no_migration
1644+ ):
1645+ # dtwf generation had no events
1646+ return False
1647+ return True
16081648
16091649 def process_pedigree_common_ancestors (self , ind , ploid ):
16101650 """
@@ -2256,14 +2296,16 @@ def merge_ancestors(self, H, pop_id, label, new_node_id=-1):
22562296 if r_max not in self .S :
22572297 j = self .S .floor_key (r_max )
22582298 self .S [r_max ] = self .S [j ]
2299+
2300+ min_overlap = len (X ) if self .stop_at_local_mrca else 0
22592301 # Update the number of extant segments.
2260- if self .S [left ] == len ( X ) :
2302+ if self .S [left ] == min_overlap :
22612303 self .S [left ] = 0
22622304 right = self .S .succ_key (left )
22632305 else :
22642306 right = left
2265- while right < r_max and self .S [right ] != len ( X ) :
2266- self .S [right ] -= len ( X ) - 1
2307+ while right < r_max and self .S [right ] != min_overlap :
2308+ self .S [right ] -= min_overlap - 1
22672309 right = self .S .succ_key (right )
22682310 alpha = self .alloc_segment (left , right , new_node_id , pop_id )
22692311 # Update the heaps and make the record.
@@ -2394,6 +2436,7 @@ def merge_two_ancestors(self, population_index, label, x, y, u=-1):
23942436 new_lineage = self .alloc_lineage (None , population_index , label = label )
23952437 coalescence = False
23962438 defrag_required = False
2439+ min_overlap = 2 if self .stop_at_local_mrca else 0
23972440
23982441 while x is not None or y is not None :
23992442 alpha = None
@@ -2435,12 +2478,12 @@ def merge_two_ancestors(self, population_index, label, x, y, u=-1):
24352478 j = self .S .floor_key (r_max )
24362479 self .S [r_max ] = self .S [j ]
24372480 # Update the number of extant segments.
2438- if self .S [left ] == 2 :
2481+ if self .S [left ] == min_overlap :
24392482 self .S [left ] = 0
24402483 right = self .S .succ_key (left )
24412484 else :
24422485 right = left
2443- while right < r_max and self .S [right ] != 2 :
2486+ while right < r_max and self .S [right ] != min_overlap :
24442487 self .S [right ] -= 1
24452488 right = self .S .succ_key (right )
24462489 alpha = self .alloc_segment (
@@ -2860,6 +2903,7 @@ def run_simulate(args):
28602903 gene_conversion_length = mean_tract_length ,
28612904 discrete_genome = args .discrete ,
28622905 hull_offset = args .offset ,
2906+ stop_at_local_mrca = not args .continue_after_local_mrca ,
28632907 )
28642908 ts = s .simulate (args .end_time )
28652909 ts .dump (args .output_file )
@@ -2946,6 +2990,15 @@ def add_simulator_arguments(parser):
29462990 help = "The delta_t value for selective sweeps" ,
29472991 )
29482992 parser .add_argument ("--model" , default = "hudson" )
2993+ parser .add_argument (
2994+ "--continue-after-local-mrca" ,
2995+ action = "store_true" ,
2996+ default = False ,
2997+ help = (
2998+ "If set, continue after local MRCA (i.e., do not stop). "
2999+ "Default: False (stop at local MRCA)."
3000+ ),
3001+ )
29493002 parser .add_argument ("--offset" , type = float , default = 0.0 )
29503003 parser .add_argument (
29513004 "--from-ts" ,
0 commit comments