From 4a8e91a62f722dee180ff343b00d9f22d4370e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Guignard?= Date: Tue, 9 Dec 2025 14:01:57 +0100 Subject: [PATCH 1/3] add spatial resolution --- src/lineagetree/lineage_tree.py | 13 +++++++++++++ src/lineagetree/measure/spatial.py | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lineagetree/lineage_tree.py b/src/lineagetree/lineage_tree.py index 46a8410..f447513 100644 --- a/src/lineagetree/lineage_tree.py +++ b/src/lineagetree/lineage_tree.py @@ -143,6 +143,7 @@ def __init__( pos: dict[int, Iterable] | None = None, name: str | None = None, root_leaf_value: Sequence | None = None, + spatial_resolution: Sequence | None = None, **kwargs, ): """Create a LineageTree object from minimal information, without reading from a file. @@ -312,3 +313,15 @@ def __init__( self._custom_property_list.append(name) if not hasattr(self, "_comparisons"): self._comparisons = {} + + spatia_dimension = len(self.pos[next(iter(self.nodes))]) + if spatial_resolution is not None: + if len(spatial_resolution) == spatia_dimension: + self.spatial_resolution = np.array(spatial_resolution) + else: + raise ValueError( + "The spatial resolution should have the same dimension as the one of the positions:\n" + f"{len(spatial_resolution)=}, spatial dimension={spatia_dimension}" + ) + else: + self.spatial_resolution = np.ones(spatia_dimension) diff --git a/src/lineagetree/measure/spatial.py b/src/lineagetree/measure/spatial.py index 0068470..417ac05 100644 --- a/src/lineagetree/measure/spatial.py +++ b/src/lineagetree/measure/spatial.py @@ -40,7 +40,7 @@ def get_idx3d(lT: LineageTree, t: int) -> tuple[KDTree, np.ndarray]: data_corres = {} data = [] for i, C in enumerate(to_check_lT): - data.append(tuple(lT.pos[C])) + data.append(tuple(lT.pos[C] * lT.spatial_resolution)) data_corres[i] = C idx3d = KDTree(data) lT.kdtrees[t] = idx3d From 5289703866dea9ac00ccdbd9bf20b1b6715a2a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Guignard?= Date: Tue, 9 Dec 2025 14:56:44 +0100 Subject: [PATCH 2/3] bug fix on spatial resolution --- src/lineagetree/lineage_tree.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lineagetree/lineage_tree.py b/src/lineagetree/lineage_tree.py index f447513..47eff18 100644 --- a/src/lineagetree/lineage_tree.py +++ b/src/lineagetree/lineage_tree.py @@ -98,6 +98,8 @@ def load(clf, fname: str): ) if not hasattr(lT, "time_resolution"): lT.time_resolution = 1 + if not hasattr(lT, "spatial_resolution"): + lT.spatial_resolution = np.ones(3) return lT @@ -314,8 +316,12 @@ def __init__( if not hasattr(self, "_comparisons"): self._comparisons = {} - spatia_dimension = len(self.pos[next(iter(self.nodes))]) - if spatial_resolution is not None: + spatia_dimension = ( + len(self.pos[next(iter(self.nodes))]) + if self.nodes and self.pos + else 3 + ) + if self.nodes and spatial_resolution is not None: if len(spatial_resolution) == spatia_dimension: self.spatial_resolution = np.array(spatial_resolution) else: @@ -324,4 +330,6 @@ def __init__( f"{len(spatial_resolution)=}, spatial dimension={spatia_dimension}" ) else: - self.spatial_resolution = np.ones(spatia_dimension) + self.spatial_resolution = spatial_resolution or np.ones( + spatia_dimension + ) From 508e29d06169f7ad9f7d2184210f621c6b610211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Guignard?= Date: Tue, 9 Dec 2025 16:24:06 +0100 Subject: [PATCH 3/3] adding tests --- tests/test_lineageTree.py | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/test_lineageTree.py b/tests/test_lineageTree.py index 23cd953..bd0d54e 100644 --- a/tests/test_lineageTree.py +++ b/tests/test_lineageTree.py @@ -428,6 +428,62 @@ def test_bad_leaf(): ) +def test_spatial_resolution(): + successor_dict = { + 1: (2,), + 2: (3, 100), + 100: (101,), + 0: (1,), + 10: (0,), + 5: (), + 3: (), + 4: (), + 101: (), + } + test_lT = LineageTree( + successor=successor_dict, + pos=dict( + zip( + successor_dict.keys(), + np.random.random((len(successor_dict), 3)), + strict=True, + ) + ), + spatial_resolution=[1, 1, 1], + ) + assert (test_lT.spatial_resolution == [1, 1, 1]).all() + + +def test_wrong_spatial_resolution(): + successor_dict = { + 1: (2,), + 2: (3, 100), + 100: (101,), + 0: (1,), + 10: (0,), + 5: (), + 3: (), + 4: (), + 101: (), + } + with pytest.raises(ValueError) as excinfo: + LineageTree( + successor=successor_dict, + pos=dict( + zip( + successor_dict.keys(), + np.random.random((len(successor_dict), 3)), + strict=True, + ) + ), + spatial_resolution=[1, 1], + ) + assert ( + str(excinfo.value) + == "The spatial resolution should have the same dimension as the one of the positions:\nlen(spatial_resolution)=2, spatial dimension=3" + ) + + def test_multiple_predecessors(): with pytest.raises(ValueError) as excinfo: LineageTree(successor={2: (1,), 3: (2,), 4: (2,)})