@@ -154,32 +154,12 @@ def test_sample_mapping_with_individuals(self, simple_ts):
154
154
assert format_obj.max_ploidy == 2
155
155
assert format_obj.individual_ploidies == [2, 2]
156
156
157
- # Should raise error if ploidy specified with individuals
158
- with pytest.raises(
159
- ValueError, match="Cannot specify ploidy when individuals are present"
160
- ):
161
- ts.TskitFormat(ts_path, ploidy=2)
162
-
163
- def test_sample_mapping_without_individuals(self, no_individuals_ts):
164
- ts_path, tree_sequence = no_individuals_ts
165
-
166
- # Default ploidy should be 1
167
- format_obj = ts.TskitFormat(ts_path)
168
- assert format_obj.num_samples == 4
169
- assert format_obj.max_ploidy == 1
170
- assert list(format_obj.individual_ploidies) == [1, 1, 1, 1]
171
-
172
- # Explicitly set ploidy to 2
173
- format_obj = ts.TskitFormat(ts_path, ploidy=2)
174
- assert format_obj.num_samples == 2
175
- assert format_obj.max_ploidy == 2
176
- assert list(format_obj.individual_ploidies) == [2, 2]
177
-
178
- with pytest.raises(ValueError, match="Ploidy must be >= 1"):
179
- ts.TskitFormat(ts_path, ploidy=0)
180
-
181
- with pytest.raises(ValueError, match="Sample size must be divisible by ploidy"):
182
- ts.TskitFormat(ts_path, ploidy=3)
157
+ def test_no_individuals(self, no_individuals_ts):
158
+ """Test that tree sequences without individuals raise an error."""
159
+ ts_path, _ = no_individuals_ts
160
+
161
+ with pytest.raises(ValueError, match="Tree sequence has no individuals"):
162
+ ts.TskitFormat(ts_path)
183
163
184
164
def test_schema_generation(self, simple_ts):
185
165
ts_path, _ = simple_ts
@@ -307,8 +287,7 @@ def test_variable_ploidy(self, tmp_path):
307
287
format_obj = ts.TskitFormat(ts_path)
308
288
309
289
assert format_obj.max_ploidy == 3
310
- assert format_obj.individual_ploidies == [2, 3]
311
-
290
+
312
291
shape = (2, 3) # (num_samples, max_ploidy)
313
292
results = list(format_obj.iter_alleles_and_genotypes(0, 2, shape, 2))
314
293
@@ -387,3 +366,103 @@ def insert_branch_sites(ts, m=1):
387
366
# Individual 2 should have missing values (-1) when isolated_as_missing=True
388
367
expected_gt_missing = np.array([[1], [0], [-1]])
389
368
assert np.array_equal(gt_missing, expected_gt_missing)
369
+
370
+
371
+ class TestIndividualNodes:
372
+
373
+ def test_basic_individual_nodes(self, tmp_path):
374
+ # Create a basic tree sequence with two individuals
375
+ tables = tskit.TableCollection(sequence_length=100)
376
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"")
377
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"")
378
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=0)
379
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=0)
380
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=1)
381
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=1)
382
+ tree_sequence = tables.tree_sequence()
383
+
384
+ result = ts.individual_nodes(tree_sequence)
385
+ assert result.shape == (2, 2)
386
+ assert np.array_equal(result, [[0, 1], [2, 3]])
387
+
388
+ def test_variable_ploidy(self, tmp_path):
389
+ tables = tskit.TableCollection(sequence_length=100)
390
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"") # Diploid
391
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"") # Haploid
392
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"") # Triploid
393
+
394
+ # Diploid individual
395
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=0)
396
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=0)
397
+
398
+ # Haploid individual
399
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=1)
400
+
401
+ # Triploid individual
402
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=2)
403
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=2)
404
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=2)
405
+
406
+ tree_sequence = tables.tree_sequence()
407
+
408
+ result = ts.individual_nodes(tree_sequence)
409
+
410
+ assert result.shape == (3, 3)
411
+
412
+ expected = np.array([
413
+ [0, 1, -1], # Diploid
414
+ [2, -1, -1], # Haploid
415
+ [3, 4, 5] # Triploid
416
+ ])
417
+ assert np.array_equal(result, expected)
418
+
419
+ def test_no_individuals(self):
420
+ tables = tskit.TableCollection(sequence_length=100)
421
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0)
422
+ tree_sequence = tables.tree_sequence()
423
+
424
+ with pytest.raises(ValueError, match="Tree sequence has no individuals"):
425
+ ts.individual_nodes(tree_sequence)
426
+
427
+ def test_no_samples_with_individuals(self):
428
+ tables = tskit.TableCollection(sequence_length=100)
429
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"")
430
+ # Node without individual reference
431
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0)
432
+ tree_sequence = tables.tree_sequence()
433
+
434
+ with pytest.raises(ValueError, match="No samples refer to individuals"):
435
+ ts.individual_nodes(tree_sequence)
436
+
437
+ def test_mixed_individual_references(self):
438
+ tables = tskit.TableCollection(sequence_length=100)
439
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"")
440
+ # One node with individual reference
441
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=0)
442
+ # One node without individual reference
443
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0)
444
+ tree_sequence = tables.tree_sequence()
445
+
446
+ with pytest.raises(ValueError, match="Sample nodes must all be associated with individuals"):
447
+ ts.individual_nodes(tree_sequence)
448
+
449
+ def test_individual_with_no_nodes(self):
450
+ tables = tskit.TableCollection(sequence_length=100)
451
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"")
452
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"")
453
+ # Only add nodes for first individual
454
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=0)
455
+ tree_sequence = tables.tree_sequence()
456
+
457
+ with pytest.raises(ValueError, match="Individual 1 not associated with any nodes"):
458
+ ts.individual_nodes(tree_sequence)
459
+
460
+ def test_mixed_sample_status(self):
461
+ tables = tskit.TableCollection(sequence_length=100)
462
+ tables.individuals.add_row(flags=0, location=(0, 0), metadata=b"")
463
+ tables.nodes.add_row(flags=tskit.NODE_IS_SAMPLE, time=0, individual=0)
464
+ tables.nodes.add_row(flags=0, time=0, individual=0)
465
+ tree_sequence = tables.tree_sequence()
466
+
467
+ with pytest.raises(ValueError, match="has nodes that are sample and non-samples"):
468
+ ts.individual_nodes(tree_sequence)
0 commit comments