Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to ensure streams are correctly mapped to plots #6415

Merged
merged 6 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions holoviews/plotting/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from ..core.options import Compositor, SkipRendering, Store, lookup_options
from ..core.overlay import CompositeOverlay, NdOverlay, Overlay
from ..core.spaces import DynamicMap, HoloMap
from ..core.util import isfinite, stream_parameters
from ..core.util import isfinite, stream_parameters, unique_iterator
from ..element import Graph, Table
from ..selection import NoOpSelectionDisplay
from ..streams import RangeX, RangeXY, RangeY, Stream
Expand Down Expand Up @@ -1817,8 +1817,8 @@ def _create_subplots(self, ranges):
keys, vmaps = self.hmap._split_overlays()

if isinstance(self.hmap, DynamicMap):
dmap_streams = [get_nested_streams(layer) for layer in
split_dmap_overlay(self.hmap)]
dmap_streams = [streams+get_nested_streams(layer) for layer, streams in
zip(*split_dmap_overlay(self.hmap))]
else:
dmap_streams = [None]*len(keys)

Expand All @@ -1830,6 +1830,8 @@ def _create_subplots(self, ranges):

subplots = {}
for (key, vmap, streams) in zip(keys, vmaps, dmap_streams):
if streams:
streams = list(unique_iterator(streams))
subplot = self._create_subplot(key, vmap, streams, ranges)
if subplot is None:
continue
Expand Down
17 changes: 12 additions & 5 deletions holoviews/plotting/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def isoverlay_fn(obj):
"""
Determines whether object is a DynamicMap returning (Nd)Overlay types.
"""
return isinstance(obj, DynamicMap) and (isinstance(obj.last, CompositeOverlay))
return isinstance(obj, CompositeOverlay) or (isinstance(obj, DynamicMap) and (isinstance(obj.last, CompositeOverlay)))


def overlay_depth(obj):
Expand Down Expand Up @@ -233,28 +233,35 @@ def split_dmap_overlay(obj, depth=0):
to determine if a stream update should redraw a particular
subplot.
"""
layers = []
layers, streams = [], []
if isinstance(obj, DynamicMap):
initialize_dynamic(obj)
if issubclass(obj.type, NdOverlay) and not depth:
for _ in obj.last.values():
layers.append(obj)
streams.append(obj.streams)
elif issubclass(obj.type, Overlay):
if obj.callback.inputs and is_dynamic_overlay(obj):
for inp in obj.callback.inputs:
layers += split_dmap_overlay(inp, depth+1)
split, sub_streams = split_dmap_overlay(inp, depth+1)
layers += split
streams += [s+obj.streams for s in sub_streams]
else:
for _ in obj.last.values():
layers.append(obj)
streams.append(obj.streams)
else:
layers.append(obj)
return layers
streams.append(obj.streams)
return layers, streams
if isinstance(obj, Overlay):
for _k, v in obj.items():
layers.append(v)
streams.append([])
else:
layers.append(obj)
return layers
streams.append([])
return layers, streams


def initialize_dynamic(obj):
Expand Down
35 changes: 21 additions & 14 deletions holoviews/tests/plotting/test_plotutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ def test_dynamic_compute_overlayable_zorders_mixed_dynamic_and_non_dynamic_ndove
self.assertIn(curve, sources[2])
self.assertNotIn(ndoverlay, sources[2])

def test_dynamic_compute_overlayable_zorders_ndoverlays_as_input(self):
Copy link
Member

@maximlt maximlt Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this test cover the two bugs fixed by the PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had real trouble finding a smaller reproducible example for the first issue.

ndoverlay1 = NdOverlay({i: Area(range(10+i)) for i in range(2)}).apply(lambda el: el.get(0), dynamic=True)
ndoverlay2 = NdOverlay({i: Area((range(15, 25+i), range(10+i))) for i in range(2)}).apply(lambda el: el.get(0), dynamic=True)
combined = ndoverlay1*ndoverlay2
combined[()]
sources = compute_overlayable_zorders(combined)
assert len(sources) == 2

def test_dynamic_compute_overlayable_zorders_mixed_dynamic_and_dynamic_ndoverlay_with_streams(self):
ndoverlay = DynamicMap(lambda x: NdOverlay({i: Area(range(10+i)) for i in range(2)}),
Expand Down Expand Up @@ -364,61 +371,61 @@ def test_dmap_ndoverlay(self):
test = self.dmap_ndoverlay
initialize_dynamic(test)
layers = [self.dmap_ndoverlay, self.dmap_ndoverlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_overlay(self):
test = self.dmap_overlay
initialize_dynamic(test)
layers = [self.dmap_overlay, self.dmap_overlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_element_mul_dmap_overlay(self):
test = self.dmap_element * self.dmap_overlay
initialize_dynamic(test)
layers = [self.dmap_element, self.dmap_overlay, self.dmap_overlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_element_mul_dmap_ndoverlay(self):
test = self.dmap_element * self.dmap_ndoverlay
initialize_dynamic(test)
layers = [self.dmap_element, self.dmap_ndoverlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_element_mul_element(self):
test = self.dmap_element * self.element
initialize_dynamic(test)
layers = [self.dmap_element, self.element]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_element_mul_overlay(self):
test = self.dmap_element * self.overlay
initialize_dynamic(test)
layers = [self.dmap_element, self.el1, self.el2]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_element_mul_ndoverlay(self):
test = self.dmap_element * self.ndoverlay
initialize_dynamic(test)
layers = [self.dmap_element, self.ndoverlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_overlay_mul_dmap_ndoverlay(self):
test = self.dmap_overlay * self.dmap_ndoverlay
initialize_dynamic(test)
layers = [self.dmap_overlay, self.dmap_overlay, self.dmap_ndoverlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_overlay_mul_element(self):
test = self.dmap_overlay * self.element
initialize_dynamic(test)
layers = [self.dmap_overlay, self.dmap_overlay, self.element]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_overlay_mul_overlay(self):
test = self.dmap_overlay * self.overlay
initialize_dynamic(test)
layers = [self.dmap_overlay, self.dmap_overlay, self.el1, self.el2]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_all_combinations(self):
test = (self.dmap_overlay * self.element * self.dmap_ndoverlay *
Expand All @@ -427,28 +434,28 @@ def test_dmap_all_combinations(self):
layers = [self.dmap_overlay, self.dmap_overlay, self.element,
self.dmap_ndoverlay, self.el1, self.el2, self.dmap_element,
self.ndoverlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_overlay_operation_mul_dmap_ndoverlay(self):
mapped = operation(self.dmap_overlay)
test = mapped * self.dmap_ndoverlay
initialize_dynamic(test)
layers = [mapped, mapped, self.dmap_ndoverlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_overlay_linked_operation_mul_dmap_ndoverlay(self):
mapped = operation(self.dmap_overlay, link_inputs=True)
test = mapped * self.dmap_ndoverlay
initialize_dynamic(test)
layers = [mapped, mapped, self.dmap_ndoverlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)

def test_dmap_overlay_linked_operation_mul_dmap_element_ndoverlay(self):
mapped = self.dmap_overlay.map(lambda x: x.get(0), Overlay)
test = mapped * self.element * self.dmap_ndoverlay
initialize_dynamic(test)
layers = [mapped, self.element, self.dmap_ndoverlay]
self.assertEqual(split_dmap_overlay(test), layers)
self.assertEqual(split_dmap_overlay(test)[0], layers)


class TestPlotColorUtils(ComparisonTestCase):
Expand Down