Skip to content

Commit 28bf96b

Browse files
committed
Work around to allow Demography init from provenance with preset pop IDs
1 parent 8d65577 commit 28bf96b

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
## [1.3.5] - 2025-XX-XX
55

6+
**New features**
7+
8+
- Demography objects can now be created from provenance entries ({pr}`{2369}, {user}`hyanwong`)
9+
610
**Breaking changes**:
711

812
- The `.asdict()` methods for Demography, Population, and Event classes in the

msprime/provenance.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import tskit
3232

3333
from . import ancestry
34+
from . import demography
3435
from msprime import _msprime
3536

3637
__version__ = "undefined"
@@ -188,10 +189,21 @@ def hook(obj):
188189
elif "__npgeneric__" in obj:
189190
return numpy.array([obj["__npgeneric__"]]).astype(obj["dtype"])[0]
190191
elif "__class__" in obj:
191-
module, cls = obj["__class__"].rsplit(".", 1)
192+
module, cls = obj.pop("__class__").rsplit(".", 1)
192193
module = importlib.import_module(module)
193-
del obj["__class__"]
194-
return getattr(module, cls)(**obj)
194+
cls = getattr(module, cls)
195+
if cls == demography.Demography:
196+
# the Demography class normally generates its own population IDs, but
197+
# allow fixed IDs here to ensure they match e.g. IDs in event objects
198+
demography_object = object.__new__(cls)
199+
try:
200+
demography_object.__init__(**obj)
201+
except ValueError as e:
202+
if str(e).startswith("Population ID should not be set"):
203+
return demography_object # This is the allowed fixed ID err
204+
else:
205+
raise
206+
return cls(**obj)
195207
return obj
196208

197209
return json.JSONDecoder(object_hook=hook).decode(s)

tests/test_provenance.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,39 @@ def test_current_ts(self):
244244
assert command == "sim_mutations"
245245
assert prov["tree_sequence"] == ts1
246246

247+
def test_demography(self):
248+
demography = msprime.Demography.island_model([1, 1], 1 / 3)
249+
ts = msprime.sim_ancestry(
250+
demography=demography,
251+
samples=[
252+
msprime.SampleSet(1, population=0),
253+
msprime.SampleSet(1, population=1),
254+
],
255+
random_seed=3,
256+
)
257+
command, prov = msprime.provenance.parse_provenance(ts.provenance(-1), ts)
258+
assert command == "sim_ancestry"
259+
assert prov["demography"] == demography
260+
261+
def test_bad_demography(self):
262+
demography = msprime.Demography.island_model([1, 1], 1 / 3)
263+
ts = msprime.sim_ancestry(
264+
demography=demography,
265+
samples=[
266+
msprime.SampleSet(1, population=0),
267+
msprime.SampleSet(1, population=1),
268+
],
269+
random_seed=3,
270+
)
271+
prov = ts.provenance(-1)
272+
record = json.loads(prov.record)
273+
# Corrupt the provenance record
274+
assert len(record["parameters"]["demography"]["migration_matrix"]) == 2
275+
record["parameters"]["demography"]["migration_matrix"] = [1, 0, 0]
276+
prov.record = json.dumps(record)
277+
with pytest.raises(ValueError, match="Migration matrix must be square"):
278+
msprime.provenance.parse_provenance(prov, ts)
279+
247280

248281
class TestRoundTrip:
249282
"""

0 commit comments

Comments
 (0)