38
38
from tsinfer import formats
39
39
40
40
41
- def ts_to_dataset (ts , chunks = None , samples = None ):
41
+ def ts_to_dataset (ts , chunks = None , samples = None , contigs = None ):
42
42
"""
43
43
# From https://github.com/sgkit-dev/sgkit/blob/main/sgkit/tests/test_popgen.py#L63
44
44
Convert the specified tskit tree sequence into an sgkit dataset.
@@ -63,7 +63,7 @@ def ts_to_dataset(ts, chunks=None, samples=None):
63
63
genotypes = np .expand_dims (genotypes , axis = 2 )
64
64
65
65
ds = sgkit .create_genotype_call_dataset (
66
- variant_contig_names = ["1" ],
66
+ variant_contig_names = ["1" ] if contigs is None else contigs ,
67
67
variant_contig = np .zeros (len (tables .sites ), dtype = int ),
68
68
variant_position = tables .sites .position .astype (int ),
69
69
variant_allele = alleles ,
@@ -280,18 +280,102 @@ def test_simulate_genotype_call_dataset(tmp_path):
280
280
ts = msprime .sim_ancestry (4 , sequence_length = 1000 , random_seed = 123 )
281
281
ts = msprime .sim_mutations (ts , rate = 2e-3 , random_seed = 123 )
282
282
ds = ts_to_dataset (ts )
283
- ds .update ({"variant_ancestral_allele" : ds ["variant_allele" ][:, 0 ]})
284
283
ds .to_zarr (tmp_path , mode = "w" )
285
- sd = tsinfer .VariantData (tmp_path , "variant_ancestral_allele" )
286
- ts = tsinfer .infer (sd )
287
- for v , ds_v , sd_v in zip (ts .variants (), ds .call_genotype , sd .sites_genotypes ):
284
+ vdata = tsinfer .VariantData (tmp_path , ds [ "variant_allele" ][:, 0 ]. values . astype ( str ) )
285
+ ts = tsinfer .infer (vdata )
286
+ for v , ds_v , vd_v in zip (ts .variants (), ds .call_genotype , vdata .sites_genotypes ):
288
287
assert np .all (v .genotypes == ds_v .values .flatten ())
289
- assert np .all (v .genotypes == sd_v )
288
+ assert np .all (v .genotypes == vd_v )
289
+
290
+
291
+ def test_simulate_genotype_call_dataset_length (tmp_path ):
292
+ # create_genotype_call_dataset does not save contig lengths
293
+ ts = msprime .sim_ancestry (4 , sequence_length = 1000 , random_seed = 123 )
294
+ ts = msprime .sim_mutations (ts , rate = 2e-3 , random_seed = 123 )
295
+ ds = ts_to_dataset (ts )
296
+ assert "contig_length" not in ds
297
+ ds .to_zarr (tmp_path , mode = "w" )
298
+ vdata = tsinfer .VariantData (tmp_path , ds ["variant_allele" ][:, 0 ].values .astype (str ))
299
+ assert vdata .sequence_length == ts .sites_position [- 1 ] + 1
300
+
301
+
302
+ class TestMultiContig :
303
+ def make_two_ts_dataset (self , path ):
304
+ # split ts into 2; put them as different contigs in the same dataset
305
+ ts = msprime .sim_ancestry (4 , sequence_length = 1000 , random_seed = 123 )
306
+ ts = msprime .sim_mutations (ts , rate = 2e-3 , random_seed = 123 )
307
+ split_at_site = 7
308
+ assert ts .num_sites > 10
309
+ site_break = ts .site (split_at_site ).position
310
+ ts1 = ts .keep_intervals ([(0 , site_break )]).rtrim ()
311
+ ts2 = ts .keep_intervals ([(site_break , ts .sequence_length )]).ltrim ()
312
+ ds = ts_to_dataset (ts , contigs = ["chr1" , "chr2" ])
313
+ ds .update ({"variant_ancestral_allele" : ds ["variant_allele" ][:, 0 ]})
314
+ variant_contig = ds ["variant_contig" ][:]
315
+ variant_contig [split_at_site :] = 1
316
+ ds .update ({"variant_contig" : variant_contig })
317
+ variant_position = ds ["variant_position" ].values
318
+ variant_position [split_at_site :] -= int (site_break )
319
+ ds .update ({"variant_position" : ds ["variant_position" ]})
320
+ ds .update (
321
+ {"contig_length" : np .array ([ts1 .sequence_length , ts2 .sequence_length ])}
322
+ )
323
+ ds .to_zarr (path , mode = "w" )
324
+ return ts1 , ts2
325
+
326
+ def test_unmasked (self , tmp_path ):
327
+ self .make_two_ts_dataset (tmp_path )
328
+ with pytest .raises (ValueError , match = r'multiple contigs \("chr1", "chr2"\)' ):
329
+ tsinfer .VariantData (tmp_path , "variant_ancestral_allele" )
330
+
331
+ def test_mask (self , tmp_path ):
332
+ ts1 , ts2 = self .make_two_ts_dataset (tmp_path )
333
+ vdata = tsinfer .VariantData (
334
+ tmp_path ,
335
+ "variant_ancestral_allele" ,
336
+ site_mask = np .array (ts1 .num_sites * [True ] + ts2 .num_sites * [False ]),
337
+ )
338
+ assert np .all (ts2 .sites_position == vdata .sites_position )
339
+ assert vdata .contig_id == "chr2"
340
+ assert vdata .sequence_length == ts2 .sequence_length
341
+
342
+ @pytest .mark .parametrize ("contig_id" , ["chr1" , "chr2" ])
343
+ def test_contig_id_param (self , contig_id , tmp_path ):
344
+ tree_seqs = {}
345
+ tree_seqs ["chr1" ], tree_seqs ["chr2" ] = self .make_two_ts_dataset (tmp_path )
346
+ vdata = tsinfer .VariantData (
347
+ tmp_path , "variant_ancestral_allele" , contig_id = contig_id
348
+ )
349
+ assert np .all (tree_seqs [contig_id ].sites_position == vdata .sites_position )
350
+ assert vdata .contig_id == contig_id
351
+ assert vdata .sequence_length == tree_seqs [contig_id ].sequence_length
352
+
353
+ def test_contig_id_param_and_mask (self , tmp_path ):
354
+ ts1 , ts2 = self .make_two_ts_dataset (tmp_path )
355
+ vdata = tsinfer .VariantData (
356
+ tmp_path ,
357
+ "variant_ancestral_allele" ,
358
+ site_mask = np .array (
359
+ (ts1 .num_sites + 1 ) * [True ] + (ts2 .num_sites - 1 ) * [False ]
360
+ ),
361
+ contig_id = "chr2" ,
362
+ )
363
+ assert np .all (ts2 .sites_position [1 :] == vdata .sites_position )
364
+ assert vdata .contig_id == "chr2"
365
+
366
+ @pytest .mark .parametrize ("contig_id" , ["chr1" , "chr2" ])
367
+ def test_contig_length (self , contig_id , tmp_path ):
368
+ tree_seqs = {}
369
+ tree_seqs ["chr1" ], tree_seqs ["chr2" ] = self .make_two_ts_dataset (tmp_path )
370
+ vdata = tsinfer .VariantData (
371
+ tmp_path , "variant_ancestral_allele" , contig_id = contig_id
372
+ )
373
+ assert vdata .sequence_length == tree_seqs [contig_id ].sequence_length
290
374
291
375
292
376
@pytest .mark .skipif (sys .platform == "win32" , reason = "File permission errors on Windows" )
293
377
class TestSgkitMask :
294
- @pytest .mark .parametrize ("sites" , [[1 , 2 , 3 , 5 , 9 , 27 ], [0 ], [] ])
378
+ @pytest .mark .parametrize ("sites" , [[1 , 2 , 3 , 5 , 9 , 27 ], [0 ]])
295
379
def test_sgkit_variant_mask (self , tmp_path , sites ):
296
380
ts , zarr_path = tsutil .make_ts_and_zarr (tmp_path )
297
381
ds = sgkit .load_dataset (zarr_path )
@@ -831,3 +915,51 @@ def test_unimplemented_from_tree_sequence(self):
831
915
# Requires e.g. https://github.com/tskit-dev/tsinfer/issues/924
832
916
with pytest .raises (NotImplementedError ):
833
917
tsinfer .VariantData .from_tree_sequence (None )
918
+
919
+ def test_multiple_contigs (self , tmp_path ):
920
+ path = tmp_path / "data.zarr"
921
+ ds = sgkit .simulate_genotype_call_dataset (n_variant = 3 , n_sample = 3 , phased = True )
922
+ ds ["contig_id" ] = (
923
+ ds ["contig_id" ].dims ,
924
+ np .array (["c10" , "c11" ], dtype = "<U3" ),
925
+ )
926
+ ds ["variant_contig" ] = (
927
+ ds ["variant_contig" ].dims ,
928
+ np .array ([0 , 0 , 1 ], dtype = ds ["variant_contig" ].dtype ),
929
+ )
930
+ sgkit .save_dataset (ds , path )
931
+ with pytest .raises (
932
+ ValueError , match = r'Sites belong to multiple contigs \("c10", "c11"\)'
933
+ ):
934
+ tsinfer .VariantData (path , ds ["variant_allele" ][:, 0 ].astype (str ))
935
+
936
+ def test_all_masked (self , tmp_path ):
937
+ path = tmp_path / "data.zarr"
938
+ ds = sgkit .simulate_genotype_call_dataset (n_variant = 3 , n_sample = 3 , phased = True )
939
+ sgkit .save_dataset (ds , path )
940
+ with pytest .raises (ValueError , match = "All sites have been masked out" ):
941
+ tsinfer .VariantData (
942
+ path , ds ["variant_allele" ][:, 0 ].astype (str ), site_mask = np .ones (3 , bool )
943
+ )
944
+
945
+ def test_bad_contig_param (self , tmp_path ):
946
+ path = tmp_path / "data.zarr"
947
+ ds = sgkit .simulate_genotype_call_dataset (n_variant = 3 , n_sample = 3 , phased = True )
948
+ sgkit .save_dataset (ds , path )
949
+ with pytest .raises (ValueError , match = '"XX" not found' ):
950
+ tsinfer .VariantData (
951
+ path , ds ["variant_allele" ][:, 0 ].astype (str ), contig_id = "XX"
952
+ )
953
+
954
+ def test_multiple_contig_param (self , tmp_path ):
955
+ path = tmp_path / "data.zarr"
956
+ ds = sgkit .simulate_genotype_call_dataset (n_variant = 3 , n_sample = 3 , phased = True )
957
+ ds ["contig_id" ] = (
958
+ ds ["contig_id" ].dims ,
959
+ np .array (["chr1" , "chr1" ], dtype = "<U4" ),
960
+ )
961
+ sgkit .save_dataset (ds , path )
962
+ with pytest .raises (ValueError , match = 'Multiple contigs named "chr1"' ):
963
+ tsinfer .VariantData (
964
+ path , ds ["variant_allele" ][:, 0 ].astype (str ), contig_id = "chr1"
965
+ )
0 commit comments