@@ -184,6 +184,7 @@ def _init_as_actual(
184184 self ._obst = X .obst
185185 self ._vart = X .vart
186186 self ._alignment = X ._alignment
187+ self ._has_overlap = X .has_overlap
187188
188189 # init from scratch
189190 else :
@@ -199,8 +200,10 @@ def _init_as_actual(
199200 self ._allow_overlap = bool (allow_overlap )
200201 else :
201202 raise ValueError ("allow_overlap has to be a boolean" )
203+ self ._has_overlap = False
202204 self ._obst = AxisTrees (self , 0 , vals = obst )
203205 self ._vart = AxisTrees (self , 1 , vals = vart )
206+ self ._update_has_overlap ()
204207
205208 def _init_as_view (self , tdata_ref : TreeData , oidx : Index1D | None , vidx : Index1D | None ):
206209 super ()._init_as_view (tdata_ref , oidx = oidx , vidx = vidx )
@@ -209,6 +212,7 @@ def _init_as_view(self, tdata_ref: TreeData, oidx: Index1D | None, vidx: Index1D
209212 self ._tree_label = tdata_ref ._tree_label
210213 self ._alignment = tdata_ref ._alignment
211214 self ._allow_overlap = tdata_ref ._allow_overlap
215+ self ._has_overlap = tdata_ref ._has_overlap
212216
213217 # view of obst and vart
214218 self ._obst = tdata_ref .obst ._view (self , oidx )
@@ -259,6 +263,17 @@ def allow_overlap(self) -> bool:
259263 """Whether overlapping trees are allowed."""
260264 return self ._allow_overlap
261265
266+ @property
267+ def has_overlap (self ) -> bool :
268+ """
269+ Flag indicating whether stored trees contain overlapping nodes.
270+
271+ Returns
272+ -------
273+ bool - ``True`` when any stored trees share nodes, ``False`` otherwise.
274+ """
275+ return self ._has_overlap
276+
262277 @property
263278 def alignment (self ) -> Literal ["leaves" , "nodes" , "subset" ]:
264279 """Mapping between trees and observations/variables."""
@@ -278,11 +293,13 @@ def is_view(self) -> bool:
278293 def obst (self , value ):
279294 obst = AxisTrees (self , 0 , vals = dict (value ))
280295 self ._obst = obst
296+ self ._update_has_overlap ()
281297
282298 @vart .setter
283299 def vart (self , value ):
284300 vart = AxisTrees (self , 1 , vals = dict (value ))
285301 self ._vart = vart
302+ self ._update_has_overlap ()
286303
287304 @allow_overlap .setter
288305 def allow_overlap (self , value ):
@@ -295,6 +312,34 @@ def allow_overlap(self, value):
295312 f"One or more trees in { attr } have overlapping nodes. Cannot set allow_overlap to False."
296313 )
297314 self ._allow_overlap = value
315+ self ._update_has_overlap ()
316+
317+ def _update_has_overlap (self ) -> None :
318+ """
319+ Update the cached overlap indicator.
320+
321+ Ensures the cached `_has_overlap` flag matches the current state of stored
322+ trees.
323+
324+ Parameters
325+ ----------
326+ None
327+ This method does not accept any parameters.
328+
329+ Returns
330+ -------
331+ None - This function updates the `_has_overlap` attribute in place.
332+ """
333+ if not self ._allow_overlap :
334+ self ._has_overlap = False
335+ return
336+
337+ has_overlap = False
338+ if hasattr (self , "_obst" ):
339+ has_overlap = has_overlap or self ._obst ._check_tree_overlap ()
340+ if hasattr (self , "_vart" ):
341+ has_overlap = has_overlap or self ._vart ._check_tree_overlap ()
342+ self ._has_overlap = has_overlap
298343
299344 @alignment .setter
300345 def alignment (self , value ):
0 commit comments