Repository: tskit-dev/msprime (language: python)

Issue:
Add example pedigree files as test data
It would be good to have a set of standard (small) pedigree files that we can use as test data. These can go in ``tests/data/pedigrees``.

@ivan-krukov I think you have some of these lying around?


Discussion / hints:

Fix patch:
diff --git a/msprime/ancestry.py b/msprime/ancestry.py
index bb486a043..408e6471f 100644
--- a/msprime/ancestry.py
+++ b/msprime/ancestry.py
@@ -192,34 +192,23 @@ def _demography_factory(
     return demography.validate()
 
 
-def _build_initial_tables(*, sequence_length, samples, ploidy, demography, pedigree):
+def _build_initial_tables(*, sequence_length, samples, ploidy, demography):
     # NOTE: this is only used in the simulate() codepath.
     tables = tskit.TableCollection(sequence_length)
 
-    if pedigree is None:
-        for index, (population, time) in enumerate(samples):
-            tables.nodes.add_row(
-                flags=tskit.NODE_IS_SAMPLE,
-                time=time,
-                population=population,
+    for index, (population, time) in enumerate(samples):
+        tables.nodes.add_row(
+            flags=tskit.NODE_IS_SAMPLE,
+            time=time,
+            population=population,
+        )
+        if population < 0:
+            raise ValueError(f"Negative population ID in sample at index {index}")
+        if population >= demography.num_populations:
+            raise ValueError(
+                f"Invalid population reference '{population}' in sample "
+                f"at index {index}"
             )
-            if population < 0:
-                raise ValueError(f"Negative population ID in sample at index {index}")
-            if population >= demography.num_populations:
-                raise ValueError(
-                    f"Invalid population reference '{population}' in sample "
-                    f"at index {index}"
-                )
-    else:
-        # TODO This should be removed - pedigree code path should only be callable
-        # from sim_ancestry
-        for parents, time, is_sample in zip(
-            pedigree.parents, pedigree.time, pedigree.is_sample
-        ):
-            ind_id = tables.individuals.add_row(0, parents=parents)
-            node_flags = tskit.NODE_IS_SAMPLE if is_sample else 0
-            for _ in range(ploidy):
-                tables.nodes.add_row(node_flags, time, population=0, individual=ind_id)
 
     # This is for the simulate() code path so we don't add metadata schemas
     # and insert the user metadata in directly as encoded JSON, as before.
@@ -240,7 +229,6 @@ def _parse_simulate(
     recombination_rate=None,
     recombination_map=None,
     population_configurations=None,
-    pedigree=None,
     migration_matrix=None,
     samples=None,
     demographic_events=None,
@@ -345,12 +333,8 @@ def _parse_simulate(
         tables = _build_initial_tables(
             sequence_length=recombination_map.sequence_length,
             samples=samples,
-            # FIXME not clear how this is all working now. We shouldn't have
-            # the pedigree as a parameter here at all which would probably
-            # simplify things.
             ploidy=2,
             demography=demography,
-            pedigree=pedigree,
         )
     else:
         tables = from_ts.dump_tables()
@@ -419,7 +403,6 @@ def simulate(
     recombination_map=None,
     mutation_rate=None,
     population_configurations=None,
-    pedigree=None,
     migration_matrix=None,
     demographic_events=None,
     samples=None,
@@ -565,7 +548,6 @@ def simulate(
             recombination_map=recombination_map,
             mutation_rate=mutation_rate,
             population_configurations=population_configurations,
-            pedigree=pedigree,
             migration_matrix=migration_matrix,
             demographic_events=demographic_events,
             samples=samples,
@@ -620,7 +602,6 @@ def simulate(
         recombination_rate=recombination_rate,
         recombination_map=recombination_map,
         population_configurations=population_configurations,
-        pedigree=pedigree,
         migration_matrix=migration_matrix,
         demographic_events=demographic_events,
         samples=samples,
diff --git a/msprime/pedigrees.py b/msprime/pedigrees.py
index 2b38639fc..9acd09443 100644
--- a/msprime/pedigrees.py
+++ b/msprime/pedigrees.py
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2019-2020 University of Oxford
+# Copyright (C) 2019-2021 University of Oxford
 #
 # This file is part of msprime.
 #
@@ -19,10 +19,6 @@
 """
 Pedigree utilities.
 """
-import collections
-import inspect
-import os
-
 import numpy as np
 import tskit
 
@@ -134,345 +130,6 @@ def sim_pedigree(
     return tables
 
 
-#####
-# All code below is scheduled for deletion:
-# https://github.com/tskit-dev/msprime/issues/1856
-#####
-
-
-class Pedigree:
-    """
-    Class representing a pedigree for simulations.
-
-    :param ndarray individual: A 1D integer array containing strictly positive unique IDs
-        for each individual in the pedigree
-    :param ndarray parents: A 2D integer array containing the indices, in the
-        individual array, of an individual's parents
-    :param ndarray time: A 1D float array containing the time of each
-        individual, in generations in the past
-    :param int ploidy: The ploidy of individuals in the pedigree. Currently
-        only ploidy of 2 is supported
-    """
-
-    def __init__(self, individual, parents, time, is_sample=None, sex=None, ploidy=2):
-        if ploidy != 2:
-            raise NotImplementedError("Ploidy != 2 not currently supported")
-
-        if sex is not None:
-            raise NotImplementedError(
-                "Assigning individual sexes not currently supported"
-            )
-
-        if parents.shape[1] != ploidy:
-            raise ValueError(
-                "Ploidy {} conflicts with number of parents {}".format(
-                    ploidy, parents.shape[1]
-                )
-            )
-
-        if np.min(individual) <= 0:
-            raise ValueError("Individual IDs must be > 0")
-
-        self.individual = individual.astype(np.int32)
-        self.num_individuals = len(individual)
-        self.parents = parents.astype(np.int32)
-        self.time = time.astype(np.float64)
-        self.sex = sex
-        self.ploidy = int(ploidy)
-
-        self.is_sample = None
-        if is_sample is not None:
-            self.is_sample = is_sample.astype(np.uint32)
-            self.samples = np.array(is_sample)[np.where(is_sample == 1)]
-            self.num_samples = len(self.samples)
-
-    def set_samples(self, num_samples=None, sample_IDs=None, probands_only=True):
-        if probands_only is not True:
-            raise NotImplementedError("Only probands may currently be set as samples.")
-
-        if num_samples is None and sample_IDs is None:
-            raise ValueError("Must specify one of num_samples of sample_IDs")
-
-        if num_samples is not None and sample_IDs is not None:
-            raise ValueError("Cannot specify both samples and num_samples.")
-
-        self.is_sample = np.zeros((self.num_individuals), dtype=np.uint32)
-
-        all_indices = range(len(self.individual))
-        proband_indices = set(all_indices).difference(self.parents.ravel())
-
-        if num_samples is not None:
-            self.num_samples = num_samples
-            if self.num_samples > len(proband_indices):
-                raise ValueError(
-                    (
-                        "Cannot specify more samples ({}) than there are "
-                        "probands in the pedigree ({}) "
-                    ).format(self.num_samples, len(proband_indices))
-                )
-            sample_indices = np.random.choice(
-                list(proband_indices), size=self.num_samples, replace=False
-            )
-
-        elif sample_IDs is not None:
-            self.num_samples = len(sample_IDs)
-
-            indices = all_indices
-            if probands_only:
-                indices = proband_indices
-
-            sample_set = set(sample_IDs)
-            sample_indices = [i for i in indices if self.individual[i] in sample_set]
-
-        if len(sample_indices) != self.num_samples:
-            raise ValueError(
-                "Sample size mismatch - duplicate sample IDs or sample ID not "
-                "in pedigree"
-            )
-
-        self.is_sample[sample_indices] = 1
-
-    def get_proband_indices(self):
-        all_indices = range(len(self.individual))
-        proband_indices = set(all_indices).difference(self.parents.ravel())
-
-        return sorted(proband_indices)
-
-    def get_ll_representation(self):
-        """
-        Returns the low-level representation of this Pedigree.
-        """
-        return {
-            "individual": self.individual,
-            "parents": self.parents,
-            "time": self.time,
-            "is_sample": self.is_sample,
-        }
-
-    # FIXME this shouldn't be a static method, but should be called automatically
-    # if a time isn't specified.
-    @staticmethod
-    def get_times(individual, parent_IDs=None, parents=None, check=False):
-        """
-        For pedigrees without specified times, crudely assigns times to
-        all individuals.
-        """
-        if parents is None and parent_IDs is None:
-            raise ValueError("Must specify either parent IDs or parent indices")
-
-        if parents is None:
-            parents = Pedigree.parent_ID_to_index(individual, parent_IDs)
-
-        time = np.zeros(len(individual))
-        all_indices = range(len(individual))
-        proband_indices = set(all_indices).difference(parents.ravel())
-        climber_indices = proband_indices
-
-        t = 0
-        while len(climber_indices) > 0:
-            next_climbers = []
-            for c_idx in climber_indices:
-                if time[c_idx] < t:
-                    time[c_idx] = t
-
-                next_parents = [p for p in parents[c_idx] if p >= 0]
-                next_climbers.extend(next_parents)
-
-            climber_indices = list(set(next_climbers))
-            t += 1
-
-        if check:
-            Pedigree.check_times(individual, parents, time)
-
-        return time
-
-    @staticmethod
-    def check_times(individual, parents, time):
-        for i, ind in enumerate(individual):
-            for parent_ix in parents[i]:
-                if parent_ix >= 0:
-                    t1 = time[i]
-                    t2 = time[parent_ix]
-                    if t1 >= t2:
-                        raise ValueError(
-                            "Ind {} has time >= than parent {}".format(
-                                ind, individual[parent_ix]
-                            )
-                        )
-
-    @staticmethod
-    def parent_ID_to_index(individual, parent_IDs):
-        n_inds, n_parents = parent_IDs.shape
-        parents = np.zeros(parent_IDs.shape, dtype=int)
-        ind_to_index_dict = dict(zip(individual, range(n_inds)))
-
-        if 0 in ind_to_index_dict:
-            raise ValueError(
-                "Invalid ID: 0 reserved to denote individual" "not in the genealogy"
-            )
-        ind_to_index_dict[0] = -1
-
-        for i in range(n_inds):
-            for j in range(n_parents):
-                parent_ID = parent_IDs[i, j]
-                parents[i, j] = ind_to_index_dict[parent_ID]
-
-        return parents
-
-    @staticmethod
-    def parent_index_to_ID(individual, parents):
-        n_inds, n_parents = parents.shape
-        parent_IDs = np.zeros(parents.shape, dtype=int)
-
-        for i in range(n_inds):
-            for j in range(n_parents):
-                parent_ID = 0
-                if parents[i, j] >= 0:
-                    parent_ID = individual[parents[i, j]]
-                    parent_IDs[i, j] = parent_ID
-
-        return parent_IDs
-
-    @staticmethod
-    def default_format():
-        cols = {
-            "individual": 0,
-            "parents": [1, 2],
-            "time": 3,
-            "is_sample": None,
-            "sexes": None,
-        }
-
-        return cols
-
-    @staticmethod
-    def read_txt(pedfile, time_col=None, sex_col=None, **kwargs):
-        """
-        Creates a Pedigree instance from a text file.
-        """
-        cols = Pedigree.default_format()
-        cols["time"] = time_col
-        cols["sexes"] = sex_col
-
-        if sex_col:
-            raise NotImplementedError("Specifying sex of individuals not yet supported")
-
-        usecols = []
-        for c in cols.values():
-            if isinstance(c, collections.abc.Iterable):
-                usecols.extend(c)
-            elif c is not None:
-                usecols.append(c)
-        usecols = sorted(usecols)
-
-        data = np.genfromtxt(pedfile, skip_header=1, usecols=usecols, dtype=float)
-
-        individual = data[:, cols["individual"]].astype(int)
-        parent_IDs = data[:, cols["parents"]].astype(int)
-        parents = Pedigree.parent_ID_to_index(individual, parent_IDs)
-
-        if cols["time"] is not None:
-            time = data[:, cols["time"]]
-        else:
-            time = Pedigree.get_times(individual, parents=parents)
-
-        return Pedigree(individual, parents, time, **kwargs)
-
-    @staticmethod
-    def read_npy(pedarray_file, **kwargs):
-        """
-        Reads pedigree from numpy .npy file with columns:
-
-            ind ID, father array index, mother array index, time
-
-        where time is given in generations.
-        """
-        basename, ext = os.path.split(pedarray_file)
-        pedarray = np.load(pedarray_file)
-
-        cols = Pedigree.default_format()
-        if "cols" in kwargs:
-            cols = kwargs["cols"]
-
-        individual = pedarray[:, cols["individual"]]
-        parents = np.stack([pedarray[:, i] for i in cols["parents"]], axis=1)
-        parents = parents.astype(int)
-        time = pedarray[:, cols["time"]]
-
-        P = Pedigree(individual, parents, time, **kwargs)
-
-        return P
-
-    def build_array(self):
-        cols = Pedigree.default_format()
-
-        col_nums = []
-        for v in cols.values():
-            if isinstance(v, collections.abc.Iterable):
-                col_nums.extend(v)
-            elif v is not None:
-                col_nums.append(v)
-
-        n_cols = max(col_nums) + 1
-
-        if max(np.diff(col_nums)) > 1:
-            raise ValueError(f"Non-sequential columns in pedigree format: {col_nums}")
-
-        pedarray = np.zeros((self.num_individuals, n_cols))
-        pedarray[:, cols["individual"]] = self.individual
-        pedarray[:, cols["parents"]] = self.parents
-        pedarray[:, cols["time"]] = self.time
-
-        return pedarray
-
-    def save_txt(self, fname):
-        """
-        Saves pedigree in text format with columns:
-
-            ind ID, father array index, mother array index, time
-
-        where time is given in generations.
-        """
-        pedarray = self.build_array()
-        cols = self.default_format()
-        cols_to_save = [
-            cols["individual"],
-            cols["parents"][0],
-            cols["parents"][1],
-            cols["time"],
-        ]
-        pedarray = pedarray[cols_to_save]
-        parent_IDs = Pedigree.parent_index_to_ID(self.individual, self.parents)
-        pedarray[:, cols["parents"]] = parent_IDs
-
-        with open(fname, "w") as f:
-            header = "ind\tfather\tmother\ttime\n"
-            f.write(header)
-            for row in pedarray:
-                f.write("\t".join([str(x) for x in row]) + "\n")
-
-    def save_npy(self, fname):
-        """
-        Saves pedigree in numpy .npy format with columns:
-
-            ind ID, father array index, mother array index, time
-
-        where time is given in generations.
-        """
-        pedarray = self.build_array()
-        np.save(fname, pedarray)
-
-    def asdict(self):
-        """
-        Returns a dict of arguments to recreate this pedigree
-        """
-        return {
-            key: getattr(self, key)
-            for key in inspect.signature(self.__init__).parameters.keys()
-            if hasattr(self, key)
-        }
-
-
 def parse_fam(fam_file):
     """
     Parse PLINK .fam file and convert to tskit IndividualTable.


Test patch:
diff --git a/tests/test_pedigree.py b/tests/test_pedigree.py
index 66a569dac..4eda6a58f 100644
--- a/tests/test_pedigree.py
+++ b/tests/test_pedigree.py
@@ -1,9 +1,6 @@
 import dataclasses
-import os
-import shutil
 import sys
 import tempfile
-import unittest
 from dataclasses import asdict
 
 import numpy as np
@@ -15,6 +12,9 @@
 from msprime import pedigrees
 
 
+# Note: these functions are left over from older code when we didn't
+# have the PedigreeBuilder. New code should use this interface for
+# building test pedigrees.
 def get_base_tables(sequence_length, num_populations=1):
     demography = msprime.Demography.isolated_model([1] * num_populations)
     tables = pedigrees.PedigreeBuilder(demography).tables
@@ -42,193 +42,6 @@ def add_pedigree_individual(
     return ind_id
 
 
-class TestPedigree(unittest.TestCase):
-    """
-    Tests for the wf_ped model.
-    """
-
-    def setUp(self):
-        self.temp_dir = tempfile.mkdtemp(prefix="msp_ped_testcase_")
-        self.temp_pedigree_text_file = os.path.join(self.temp_dir, "pedigree.txt")
-        self.temp_pedigree_array_file = os.path.join(self.temp_dir, "pedigree.npy")
-
-    def tearDown(self):
-        shutil.rmtree(self.temp_dir)
-
-    def test_simple_case(self):
-        # Simple test to check that the current code is still roughly working.
-        # Should be replace by better tests.
-        individual = np.array([1, 2, 3, 4])
-        parents = np.array([-1, -1, -1, -1, 0, 1, 1, 0]).reshape(-1, 2)
-        time = np.array([1, 1, 0, 0])
-        is_sample = np.array([0, 0, 1, 1])
-
-        model = msprime.WrightFisherPedigree()
-        ped = pedigrees.Pedigree(
-            individual, parents, time, is_sample, sex=None, ploidy=2
-        )
-        ts = msprime.simulate(2, pedigree=ped, model=model, recombination_rate=1)
-        table = ts.tables.individuals
-        assert np.all(table[0].parents == [-1, -1])
-        assert np.all(table[1].parents == [-1, -1])
-        # FIXME these are currently being reversed.
-        # assert np.all(table[2].parents == [0, 1])
-        # assert np.all(table[3].parents == [1, 0])
-
-    @unittest.skip("Currently broken")
-    def test_pedigree_replicates(self):
-        individual = np.array([1, 2, 3, 4])
-        parents = np.array([2, 3, 2, 3, -1, -1, -1, -1]).reshape(-1, 2)
-        time = np.array([0, 0, 1, 1])
-        is_sample = np.array([1, 1, 0, 0])
-
-        model = msprime.WrightFisherPedigree()
-        ped = pedigrees.Pedigree(
-            individual, parents, time, is_sample, sex=None, ploidy=2
-        )
-        replicates = msprime.simulate(
-            2, pedigree=ped, model=model, recombination_rate=1, num_replicates=100
-        )
-        for ts in replicates:
-            assert ts is not None
-
-    @unittest.skip("Currently broken")
-    def test_pedigree_samples(self):
-        individual = np.array([1, 2, 3, 4])
-        parents = np.array([2, 3, 2, 3, -1, -1, -1, -1]).reshape(-1, 2)
-        time = np.array([0, 0, 1, 1])
-
-        ped = pedigrees.Pedigree(individual, parents, time)
-        # This interface is pretty poor - what we want is for the
-        # samples argument to simulate to be interpreted as individual
-        # IDs. For now, let's just leave it as it is, though.
-        ped.set_samples(2)
-        ts = msprime.simulate(2, pedigree=ped, model="wf_ped")
-        assert ts.num_edges > 0
-
-        ped.set_samples(sample_IDs=[1, 2])
-        ts = msprime.simulate(2, pedigree=ped, model="wf_ped")
-        assert ts is not None
-        with pytest.raises(ValueError):
-            ped.set_samples(sample_IDs=[1, 1])
-        with pytest.raises(ValueError):
-            ped.set_samples(sample_IDs=[1, 3])
-        with pytest.raises(NotImplementedError):
-            ped.set_samples(sample_IDs=[1, 3], probands_only=False)
-
-        ped.set_samples(sample_IDs=[1, 2])
-        assert ped.get_proband_indices() == [0, 1]
-
-    def test_pedigree_read_write(self):
-        individual = np.array([1, 2, 3, 4])
-        parents = np.array([2, 3, 2, 3, -1, -1, -1, -1]).reshape(-1, 2)
-        time = np.array([0, 0, 1, 1])
-        is_sample = np.array([1, 1, 0, 0])
-
-        ped = pedigrees.Pedigree(
-            individual, parents, time, is_sample, sex=None, ploidy=2
-        )
-
-        ped.save_txt(self.temp_pedigree_text_file)
-        with pytest.raises(NotImplementedError):
-            pedigrees.Pedigree.read_txt(
-                self.temp_pedigree_text_file,
-                sex_col=4,
-            )
-        # FIXME
-        # The compute_times should be done automatically in this case .
-        # ped_from_txt = pedigrees.Pedigree.read_txt(
-        #     self.temp_pedigree_text_file, time_col=None
-        # )
-        # ts = msprime.simulate(2, pedigree=ped_from_txt, model="wf_ped")
-        # self.assertTrue(ts is not None)
-        # ped_from_txt = pedigrees.Pedigree.read_txt(
-        #     self.temp_pedigree_text_file, time_col=3
-        # )
-        # ts = msprime.simulate(2, pedigree=ped_from_txt, model="wf_ped")
-        # self.assertTrue(ts is not None)
-
-        ped.save_npy(self.temp_pedigree_array_file)
-        ped_from_npy = pedigrees.Pedigree.read_npy(self.temp_pedigree_array_file)
-        # TODO compre this to the file above.
-        assert isinstance(ped_from_npy, pedigrees.Pedigree)
-
-    def test_pedigree_times(self):
-        individual = np.array([1, 2, 3, 4])
-        time = np.array([0, 0, 1, 1])
-
-        parent_IDs = np.array([3, 4, 3, 4, 0, 0, 0, 0]).reshape(-1, 2)
-        estimated_times = pedigrees.Pedigree.get_times(
-            individual, parent_IDs=parent_IDs, check=True
-        )
-        assert (time == estimated_times).all()
-
-    def test_pedigree_utils(self):
-        individual = np.array([1, 2, 3, 4])
-        parents = np.array([2, 3, 2, 3, -1, -1, -1, -1]).reshape(-1, 2)
-        parent_IDs = np.array([3, 4, 3, 4, 0, 0, 0, 0]).reshape(-1, 2)
-
-        bad_individual = np.array([0, 1, 2, 3])
-        with pytest.raises(ValueError):
-            pedigrees.Pedigree.parent_ID_to_index(bad_individual, parent_IDs)
-        assert (
-            pedigrees.Pedigree.parent_ID_to_index(individual, parent_IDs) == parents
-        ).all()
-        assert (
-            pedigrees.Pedigree.parent_index_to_ID(individual, parents) == parent_IDs
-        ).all()
-
-    def test_pedigree_sanity_checks(self):
-        individual = np.array([1, 2, 3, 4])
-        parents = np.array([2, 3, 2, 3, -1, -1, -1, -1]).reshape(-1, 2)
-        time = np.array([0, 0, 1, 1])
-        is_sample = np.array([1, 1, 0, 0])
-
-        with pytest.raises(NotImplementedError):
-            pedigrees.Pedigree(
-                individual=individual,
-                parents=parents,
-                time=time,
-                ploidy=1,
-            )
-        bad_parents = np.array([2, 3, 2, 3, -1, -1, -1, -1]).reshape(-1, 4)
-        with pytest.raises(ValueError):
-            pedigrees.Pedigree(
-                individual=individual,
-                parents=bad_parents,
-                time=time,
-            )
-        bad_individual = np.array([-1, 2, 3, 4])
-        with pytest.raises(ValueError):
-            pedigrees.Pedigree(
-                individual=bad_individual,
-                parents=parents,
-                time=time,
-            )
-
-        bad_times = np.array([1, 1, 1, 1])
-        ped = pedigrees.Pedigree(
-            individual, parents, bad_times, is_sample, sex=None, ploidy=2
-        )
-        with pytest.raises(ValueError):
-            ped.check_times(
-                individual=ped.individual,
-                parents=ped.parents,
-                time=ped.time,
-            )
-
-        ped = pedigrees.Pedigree(individual, parents, time, sex=None, ploidy=2)
-        with pytest.raises(ValueError):
-            ped.set_samples()
-        with pytest.raises(ValueError):
-            ped.set_samples(num_samples=10)
-        with pytest.raises(ValueError):
-            ped.set_samples(num_samples=2, sample_IDs=[1, 2])
-
-        with pytest.raises(ValueError):
-            ped.get_times(individual=ped.individual)
-
-
 def simulate_pedigree(
     num_founders,
     num_children_prob=(0, 0, 1),
diff --git a/tests/test_provenance.py b/tests/test_provenance.py
index b6589d238..4bb887237 100644
--- a/tests/test_provenance.py
+++ b/tests/test_provenance.py
@@ -29,7 +29,6 @@
 
 import msprime
 from msprime import _msprime
-from msprime import pedigrees
 
 
 class TestProvenance:
@@ -287,29 +286,6 @@ def test_population_configuration(self):
         )
         self.verify(ts)
 
-    @pytest.mark.skip("Pedigrees broken")
-    def test_pedigree(self):
-        inds = np.array([1, 2, 3, 4, 5, 6])
-        parent_indices = np.array([4, 5, 4, 5, 4, 5, 4, 5, -1, -1, -1, -1]).reshape(
-            -1, 2
-        )
-        times = np.array([0, 0, 0, 0, 1, 1])
-        is_sample = np.array([1, 1, 1, 1, 0, 0])
-        t = max(times)
-        model = msprime.WrightFisherPedigree()
-        ped = pedigrees.Pedigree(
-            inds, parent_indices, times, is_sample, sex=None, ploidy=2
-        )
-        ts = msprime.simulate(
-            sample_size=4,
-            pedigree=ped,
-            demographic_events=[
-                msprime.SimulationModelChange(t, msprime.DiscreteTimeWrightFisher())
-            ],
-            model=model,
-        )
-        self.verify(ts)
-
     def test_from_ts(self):
         from_ts = msprime.simulate(10, random_seed=5, end_time=0.5)
         ts = msprime.simulate(from_ts=from_ts, random_seed=2)