1
1
# MIT License
2
2
#
3
- # Copyright (c) 2018-2019 Tskit Developers
3
+ # Copyright (c) 2018-2021 Tskit Developers
4
4
# Copyright (C) 2017 University of Oxford
5
5
#
6
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1175,6 +1175,143 @@ def algorithm_T(ts):
1175
1175
left = right
1176
1176
1177
1177
1178
+ class QuintuplyLinkedTree :
1179
+ def __init__ (self , n , root_threshold = 1 ):
1180
+ self .root_threshold = root_threshold
1181
+ self .parent = np .zeros (n + 1 , dtype = np .int32 ) - 1
1182
+ self .left_child = np .zeros (n + 1 , dtype = np .int32 ) - 1
1183
+ self .right_child = np .zeros (n + 1 , dtype = np .int32 ) - 1
1184
+ self .left_sib = np .zeros (n + 1 , dtype = np .int32 ) - 1
1185
+ self .right_sib = np .zeros (n + 1 , dtype = np .int32 ) - 1
1186
+ self .num_samples = np .zeros (n + 1 , dtype = np .int32 )
1187
+
1188
+ def __str__ (self ):
1189
+ s = "id\t parent\t lchild\t rchild\t lsib\t rsib\t nsamp\n "
1190
+ for j in range (len (self .parent )):
1191
+ s += (
1192
+ f"{ j } \t { self .parent [j ]} \t "
1193
+ f"{ self .left_child [j ]} \t { self .right_child [j ]} \t "
1194
+ f"{ self .left_sib [j ]} \t { self .right_sib [j ]} \t "
1195
+ f"{ self .num_samples [j ]} \n "
1196
+ )
1197
+ return s
1198
+
1199
+ def roots (self ):
1200
+ roots = []
1201
+ u = self .left_child [- 1 ]
1202
+ while u != - 1 :
1203
+ roots .append (u )
1204
+ u = self .right_sib [u ]
1205
+ return roots
1206
+
1207
+ def remove_branch (self , p , c ):
1208
+ lsib = self .left_sib [c ]
1209
+ rsib = self .right_sib [c ]
1210
+ if lsib == - 1 :
1211
+ self .left_child [p ] = rsib
1212
+ else :
1213
+ self .right_sib [lsib ] = rsib
1214
+ if rsib == - 1 :
1215
+ self .right_child [p ] = lsib
1216
+ else :
1217
+ self .left_sib [rsib ] = lsib
1218
+ self .parent [c ] = - 1
1219
+ self .left_sib [c ] = - 1
1220
+ self .right_sib [c ] = - 1
1221
+
1222
+ def insert_branch (self , p , c ):
1223
+ assert self .parent [c ] == - 1 , "contradictory edges"
1224
+ self .parent [c ] = p
1225
+ u = self .right_child [p ]
1226
+ if u == - 1 :
1227
+ self .left_child [p ] = c
1228
+ self .left_sib [c ] = - 1
1229
+ self .right_sib [c ] = - 1
1230
+ else :
1231
+ self .right_sib [u ] = c
1232
+ self .left_sib [c ] = u
1233
+ self .right_sib [c ] = - 1
1234
+ self .right_child [p ] = c
1235
+
1236
+ def is_root (self , u ):
1237
+ return self .num_samples [u ] >= self .root_threshold
1238
+
1239
+ # Note we cheat a bit here and use the -1 == last element semantics from Python.
1240
+ # We could use self.insert_branch(N, root) and then set self.parent[root] = -1.
1241
+ def insert_root (self , root ):
1242
+ self .insert_branch (- 1 , root )
1243
+
1244
+ def remove_root (self , root ):
1245
+ self .remove_branch (- 1 , root )
1246
+
1247
+ def remove_edge (self , edge ):
1248
+ self .remove_branch (edge .parent , edge .child )
1249
+
1250
+ u = edge .parent
1251
+ while u != - 1 :
1252
+ path_end = u
1253
+ path_end_was_root = self .is_root (u )
1254
+ self .num_samples [u ] -= self .num_samples [edge .child ]
1255
+ u = self .parent [u ]
1256
+
1257
+ if path_end_was_root and not self .is_root (path_end ):
1258
+ self .remove_root (path_end )
1259
+ if self .is_root (edge .child ):
1260
+ self .insert_root (edge .child )
1261
+
1262
+ def insert_edge (self , edge ):
1263
+ u = edge .parent
1264
+ while u != - 1 :
1265
+ path_end = u
1266
+ path_end_was_root = self .is_root (u )
1267
+ self .num_samples [u ] += self .num_samples [edge .child ]
1268
+ u = self .parent [u ]
1269
+
1270
+ if self .is_root (edge .child ):
1271
+ self .remove_root (edge .child )
1272
+ if self .is_root (path_end ) and not path_end_was_root :
1273
+ self .insert_root (path_end )
1274
+
1275
+ self .insert_branch (edge .parent , edge .child )
1276
+
1277
+
1278
+ def algorithm_R (ts , root_threshold = 1 ):
1279
+ """
1280
+ Quintuply linked tree with root tracking.
1281
+ """
1282
+ sequence_length = ts .sequence_length
1283
+ N = ts .num_nodes
1284
+ M = ts .num_edges
1285
+ tree = QuintuplyLinkedTree (N , root_threshold = root_threshold )
1286
+ edges = list (ts .edges ())
1287
+ in_order = ts .tables .indexes .edge_insertion_order
1288
+ out_order = ts .tables .indexes .edge_removal_order
1289
+
1290
+ # Initialise the tree
1291
+ for u in ts .samples ():
1292
+ tree .num_samples [u ] = 1
1293
+ if tree .is_root (u ):
1294
+ tree .insert_root (u )
1295
+
1296
+ j = 0
1297
+ k = 0
1298
+ left = 0
1299
+ while j < M or left < sequence_length :
1300
+ while k < M and edges [out_order [k ]].right == left :
1301
+ tree .remove_edge (edges [out_order [k ]])
1302
+ k += 1
1303
+ while j < M and edges [in_order [j ]].left == left :
1304
+ tree .insert_edge (edges [in_order [j ]])
1305
+ j += 1
1306
+ right = sequence_length
1307
+ if j < M :
1308
+ right = min (right , edges [in_order [j ]].left )
1309
+ if k < M :
1310
+ right = min (right , edges [out_order [k ]].right )
1311
+ yield (left , right ), tree
1312
+ left = right
1313
+
1314
+
1178
1315
class SampleListTree :
1179
1316
"""
1180
1317
Straightforward implementation of the quintuply linked tree for developing
@@ -1315,15 +1452,8 @@ def sample_lists(self):
1315
1452
sequence_length = ts .sequence_length
1316
1453
edges = list (ts .edges ())
1317
1454
M = len (edges )
1318
- time = [ts .node (edge .parent ).time for edge in edges ]
1319
- in_order = sorted (
1320
- range (M ),
1321
- key = lambda j : (edges [j ].left , time [j ], edges [j ].parent , edges [j ].child ),
1322
- )
1323
- out_order = sorted (
1324
- range (M ),
1325
- key = lambda j : (edges [j ].right , - time [j ], - edges [j ].parent , - edges [j ].child ),
1326
- )
1455
+ in_order = ts .tables .indexes .edge_insertion_order
1456
+ out_order = ts .tables .indexes .edge_removal_order
1327
1457
j = 0
1328
1458
k = 0
1329
1459
left = 0
@@ -1348,10 +1478,12 @@ def sample_lists(self):
1348
1478
left = right
1349
1479
1350
1480
1351
- class RootThresholdTree :
1481
+ class LegacyRootThresholdTree :
1352
1482
"""
1353
- Straightforward implementation of the quintuply linked tree for developing
1354
- and testing the root_threshold feature.
1483
+ Implementation of the quintuply linked tree with root tracking using the
1484
+ pre C 1.0/Python 0.4.0 algorithm. We keep this version around to make sure
1485
+ that we can be clear what the differences in the semantics of the new
1486
+ and old versions are.
1355
1487
1356
1488
NOTE: The interface is pretty awkward; it's not intended for anything other
1357
1489
than testing.
@@ -1512,15 +1644,8 @@ def iterate(self):
1512
1644
sequence_length = ts .sequence_length
1513
1645
edges = list (ts .edges ())
1514
1646
M = len (edges )
1515
- time = [ts .node (edge .parent ).time for edge in edges ]
1516
- in_order = sorted (
1517
- range (M ),
1518
- key = lambda j : (edges [j ].left , time [j ], edges [j ].parent , edges [j ].child ),
1519
- )
1520
- out_order = sorted (
1521
- range (M ),
1522
- key = lambda j : (edges [j ].right , - time [j ], - edges [j ].parent , - edges [j ].child ),
1523
- )
1647
+ in_order = ts .tables .indexes .edge_insertion_order
1648
+ out_order = ts .tables .indexes .edge_removal_order
1524
1649
j = 0
1525
1650
k = 0
1526
1651
left = 0
0 commit comments