holobench 1.41.0__py3-none-any.whl → 1.43.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. bencher/__init__.py +20 -2
  2. bencher/bench_cfg.py +262 -54
  3. bencher/bench_report.py +2 -2
  4. bencher/bench_runner.py +96 -10
  5. bencher/bencher.py +421 -89
  6. bencher/class_enum.py +70 -7
  7. bencher/example/example_dataframe.py +2 -2
  8. bencher/example/example_levels.py +17 -173
  9. bencher/example/example_pareto.py +107 -31
  10. bencher/example/example_rerun2.py +1 -1
  11. bencher/example/example_simple_bool.py +2 -2
  12. bencher/example/example_simple_float2d.py +6 -1
  13. bencher/example/example_video.py +2 -0
  14. bencher/example/experimental/example_hvplot_explorer.py +2 -2
  15. bencher/example/inputs_0D/example_0_in_1_out.py +25 -15
  16. bencher/example/inputs_0D/example_0_in_2_out.py +12 -3
  17. bencher/example/inputs_0_float/example_0_cat_in_2_out.py +88 -0
  18. bencher/example/inputs_0_float/example_1_cat_in_2_out.py +98 -0
  19. bencher/example/inputs_0_float/example_2_cat_in_2_out.py +107 -0
  20. bencher/example/inputs_0_float/example_3_cat_in_2_out.py +111 -0
  21. bencher/example/inputs_1D/example1d_common.py +48 -12
  22. bencher/example/inputs_1D/example_0_float_1_cat.py +33 -0
  23. bencher/example/inputs_1D/example_1_cat_in_2_out_repeats.py +68 -0
  24. bencher/example/inputs_1D/example_1_float_2_cat_repeats.py +3 -0
  25. bencher/example/inputs_1D/example_1_int_in_1_out.py +98 -0
  26. bencher/example/inputs_1D/example_1_int_in_2_out.py +101 -0
  27. bencher/example/inputs_1D/example_1_int_in_2_out_repeats.py +99 -0
  28. bencher/example/inputs_1_float/example_1_float_0_cat_in_2_out.py +117 -0
  29. bencher/example/inputs_1_float/example_1_float_1_cat_in_2_out.py +124 -0
  30. bencher/example/inputs_1_float/example_1_float_2_cat_in_2_out.py +132 -0
  31. bencher/example/inputs_1_float/example_1_float_3_cat_in_2_out.py +140 -0
  32. bencher/example/inputs_2D/example_2_cat_in_4_out_repeats.py +104 -0
  33. bencher/example/inputs_2_float/example_2_float_0_cat_in_2_out.py +98 -0
  34. bencher/example/inputs_2_float/example_2_float_1_cat_in_2_out.py +112 -0
  35. bencher/example/inputs_2_float/example_2_float_2_cat_in_2_out.py +122 -0
  36. bencher/example/inputs_2_float/example_2_float_3_cat_in_2_out.py +138 -0
  37. bencher/example/inputs_3_float/example_3_float_0_cat_in_2_out.py +111 -0
  38. bencher/example/inputs_3_float/example_3_float_1_cat_in_2_out.py +117 -0
  39. bencher/example/inputs_3_float/example_3_float_2_cat_in_2_out.py +124 -0
  40. bencher/example/inputs_3_float/example_3_float_3_cat_in_2_out.py +129 -0
  41. bencher/example/meta/generate_examples.py +118 -7
  42. bencher/example/meta/generate_meta.py +88 -40
  43. bencher/job.py +174 -9
  44. bencher/plotting/plot_filter.py +52 -17
  45. bencher/results/bench_result.py +117 -25
  46. bencher/results/bench_result_base.py +117 -8
  47. bencher/results/dataset_result.py +6 -200
  48. bencher/results/explorer_result.py +23 -0
  49. bencher/results/{hvplot_result.py → histogram_result.py} +3 -18
  50. bencher/results/holoview_results/__init__.py +0 -0
  51. bencher/results/holoview_results/bar_result.py +79 -0
  52. bencher/results/holoview_results/curve_result.py +110 -0
  53. bencher/results/holoview_results/distribution_result/__init__.py +0 -0
  54. bencher/results/holoview_results/distribution_result/box_whisker_result.py +73 -0
  55. bencher/results/holoview_results/distribution_result/distribution_result.py +109 -0
  56. bencher/results/holoview_results/distribution_result/scatter_jitter_result.py +92 -0
  57. bencher/results/holoview_results/distribution_result/violin_result.py +70 -0
  58. bencher/results/holoview_results/heatmap_result.py +319 -0
  59. bencher/results/holoview_results/holoview_result.py +346 -0
  60. bencher/results/holoview_results/line_result.py +240 -0
  61. bencher/results/holoview_results/scatter_result.py +107 -0
  62. bencher/results/holoview_results/surface_result.py +158 -0
  63. bencher/results/holoview_results/table_result.py +14 -0
  64. bencher/results/holoview_results/tabulator_result.py +20 -0
  65. bencher/results/optuna_result.py +30 -115
  66. bencher/results/video_controls.py +38 -0
  67. bencher/results/video_result.py +39 -36
  68. bencher/results/video_summary.py +2 -2
  69. bencher/results/{plotly_result.py → volume_result.py} +29 -8
  70. bencher/utils.py +175 -26
  71. bencher/variables/inputs.py +122 -15
  72. bencher/video_writer.py +2 -1
  73. bencher/worker_job.py +31 -3
  74. {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/METADATA +24 -24
  75. holobench-1.43.0.dist-info/RECORD +147 -0
  76. bencher/example/example_levels2.py +0 -37
  77. bencher/example/inputs_1D/example_1_in_1_out.py +0 -62
  78. bencher/example/inputs_1D/example_1_in_2_out.py +0 -63
  79. bencher/example/inputs_1D/example_1_in_2_out_repeats.py +0 -61
  80. bencher/results/holoview_result.py +0 -796
  81. bencher/results/panel_result.py +0 -41
  82. holobench-1.41.0.dist-info/RECORD +0 -114
  83. {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/WHEEL +0 -0
  84. {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, Any
3
+ import panel as pn
4
+ import holoviews as hv
5
+ from param import Parameter
6
+ import xarray as xr
7
+
8
+ from bencher.results.holoview_results.distribution_result.distribution_result import (
9
+ DistributionResult,
10
+ )
11
+
12
+ from bencher.results.bench_result_base import ReduceType
13
+ from bencher.plotting.plot_filter import VarRange
14
+ from bencher.variables.results import ResultVar
15
+
16
+
17
+ class ScatterJitterResult(DistributionResult):
18
+ """A class for creating scatter jitter plots from benchmark results.
19
+
20
+ Scatter jitter plots display individual data points with slight random offsets
21
+ to avoid overlapping, making it easier to visualize the distribution of data.
22
+ This is particularly useful for smaller datasets where showing individual points
23
+ provides more insight than aggregate statistics, or alongside box plots to show
24
+ the actual data distribution.
25
+
26
+ Key features:
27
+ - Displays individual data points rather than statistical summaries
28
+ - Applies controlled random offsets to avoid point overlap
29
+ - Useful for revealing the actual sample size and distribution
30
+ - Complements statistical plots like box plots or violin plots
31
+ """
32
+
33
+ def to_plot(
34
+ self,
35
+ result_var: Optional[Parameter] = None,
36
+ override: bool = True,
37
+ jitter: float = 0.1,
38
+ **kwargs: Any,
39
+ ) -> Optional[pn.panel]:
40
+ """Generates a scatter jitter plot from benchmark data.
41
+
42
+ This method applies filters to ensure the data is appropriate for a scatter plot
43
+ and then passes the filtered data to to_scatter_jitter_ds for rendering.
44
+
45
+ Args:
46
+ result_var: The result variable to plot. If None, uses the default.
47
+ override: Whether to override filter restrictions. Defaults to True.
48
+ jitter: Amount of jitter to apply to points. Defaults to 0.1.
49
+ **kwargs: Additional keyword arguments passed to the plot rendering.
50
+
51
+ Returns:
52
+ A panel containing the scatter jitter plot if data is appropriate,
53
+ otherwise returns filter match results.
54
+ """
55
+ return self.filter(
56
+ self.to_scatter_jitter_ds,
57
+ float_range=VarRange(0, 0),
58
+ cat_range=VarRange(0, 1),
59
+ repeats_range=VarRange(2, None),
60
+ reduce=ReduceType.NONE,
61
+ target_dimension=self.plt_cnt_cfg.cat_cnt + 1, # +1 cos we have a repeats dimension
62
+ result_var=result_var,
63
+ result_types=(ResultVar,),
64
+ override=override,
65
+ jitter=jitter,
66
+ **kwargs,
67
+ )
68
+
69
+ def to_scatter_jitter_ds(
70
+ self, dataset: xr.Dataset, result_var: Parameter, jitter: float = 0.1, **kwargs: Any
71
+ ) -> hv.Scatter:
72
+ """Creates a scatter jitter plot from the provided dataset.
73
+
74
+ Given a filtered dataset, this method generates a scatter visualization showing
75
+ individual data points with random jitter to avoid overlapping, making the
76
+ distribution of values more visible.
77
+
78
+ Args:
79
+ dataset: The dataset containing benchmark results.
80
+ result_var: The result variable to plot.
81
+ jitter: Amount of jitter to apply to points. Defaults to 0.1.
82
+ **kwargs: Additional keyword arguments for plot customization, such as:
83
+ - color: Color for data points
84
+ - size: Size of data points
85
+ - alpha: Transparency of data points
86
+ - marker: Shape of data points ('o', 's', 'd', etc.)
87
+
88
+ Returns:
89
+ A HoloViews Scatter plot of the benchmark data with jittered points.
90
+ """
91
+ # Prepare the data using the common method from the parent class
92
+ return self._plot_distribution(dataset, result_var, hv.Scatter, jitter=jitter, **kwargs)
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, Any
3
+ import panel as pn
4
+ import holoviews as hv
5
+ from param import Parameter
6
+ import xarray as xr
7
+
8
+ from bencher.results.holoview_results.distribution_result.distribution_result import (
9
+ DistributionResult,
10
+ )
11
+
12
+
13
+ class ViolinResult(DistributionResult):
14
+ """A class for creating violin plots from benchmark results.
15
+
16
+ Violin plots combine aspects of box plots with kernel density plots, showing
17
+ the distribution shape of the data. This class provides methods to generate
18
+ these plots from benchmark data, which is particularly useful for visualizing
19
+ the distribution of metrics across different configurations or repetitions.
20
+
21
+ Violin plots display:
22
+ - The full probability density of the data (the width of the "violin" at each point)
23
+ - Summary statistics like median and interquartile ranges
24
+ - The overall distribution shape, revealing features like multi-modality that
25
+ box plots might miss
26
+ """
27
+
28
+ def to_plot(
29
+ self, result_var: Optional[Parameter] = None, override: bool = True, **kwargs: Any
30
+ ) -> Optional[pn.panel]:
31
+ """Generates a violin plot from benchmark data.
32
+
33
+ This method applies filters to ensure the data is appropriate for a violin plot
34
+ and then passes the filtered data to to_violin_ds for rendering.
35
+
36
+ Args:
37
+ result_var (Optional[Parameter]): The result variable to plot. If None, uses the default.
38
+ override (bool): Whether to override filter restrictions. Defaults to True.
39
+ **kwargs (Any): Additional keyword arguments passed to the plot rendering.
40
+
41
+ Returns:
42
+ Optional[pn.panel]: A panel containing the violin plot if data is appropriate,
43
+ otherwise returns filter match results.
44
+ """
45
+ return self.to_distribution_plot(
46
+ self.to_violin_ds,
47
+ result_var=result_var,
48
+ override=override,
49
+ **kwargs,
50
+ )
51
+
52
+ def to_violin_ds(self, dataset: xr.Dataset, result_var: Parameter, **kwargs: Any) -> hv.Violin:
53
+ """Creates a violin plot from the provided dataset.
54
+
55
+ Given a filtered dataset, this method generates a violin plot visualization showing
56
+ the distribution of values for a result variable, potentially grouped by a categorical variable.
57
+
58
+ Args:
59
+ dataset (xr.Dataset): The dataset containing benchmark results.
60
+ result_var (Parameter): The result variable to plot.
61
+ **kwargs (Any): Additional keyword arguments for plot customization, such as:
62
+ - violin_color: Color for the violin body
63
+ - inner_color: Color for inner statistics markers
64
+ - line_width: Width of outline lines
65
+ - bandwidth: Controls the smoothness of the density estimate
66
+
67
+ Returns:
68
+ hv.Violin: A HoloViews Violin plot of the benchmark data.
69
+ """
70
+ return self._plot_distribution(dataset, result_var, hv.Violin, **kwargs)
@@ -0,0 +1,319 @@
1
+ from __future__ import annotations
2
+ from typing import List, Optional
3
+ import panel as pn
4
+ import holoviews as hv
5
+ from param import Parameter
6
+ from functools import partial
7
+ import hvplot.xarray # noqa pylint: disable=duplicate-code,unused-import
8
+ import xarray as xr
9
+
10
+ from bencher.utils import (
11
+ get_nearest_coords1D,
12
+ )
13
+ from bencher.results.bench_result_base import ReduceType
14
+ from bencher.plotting.plot_filter import PlotFilter, VarRange
15
+ from bencher.variables.results import ResultVar
16
+ from bencher.results.holoview_results.holoview_result import HoloviewResult
17
+
18
+
19
+ class HeatmapResult(HoloviewResult):
20
+ """A class for creating heatmap visualizations from benchmark results.
21
+
22
+ Heatmaps are effective for visualizing the relationship between two input variables
23
+ and a result variable by using color intensity to represent the result values.
24
+ This class provides methods for generating interactive heatmaps that can display
25
+ additional information when hovering over or selecting points on the heatmap.
26
+ """
27
+
28
+ def to_plot(
29
+ self,
30
+ result_var: Parameter = None,
31
+ tap_var=None,
32
+ tap_container: pn.pane.panel = None,
33
+ tap_container_direction: pn.Column | pn.Row = None,
34
+ target_dimension=2,
35
+ override: bool = True,
36
+ use_tap: bool = None,
37
+ **kwargs,
38
+ ) -> Optional[pn.panel]:
39
+ """Generates a heatmap visualization from benchmark data.
40
+
41
+ This is a convenience method that calls to_heatmap() with the same parameters.
42
+
43
+ Args:
44
+ result_var (Parameter, optional): The result variable to plot. If None, uses the default.
45
+ tap_var: Variables to display when tapping on heatmap points.
46
+ tap_container (pn.pane.panel, optional): Container to hold tapped information.
47
+ tap_container_direction (pn.Column | pn.Row, optional): Layout direction for the tap container.
48
+ target_dimension (int, optional): Target dimensionality for the plot. Defaults to 2.
49
+ override (bool, optional): Whether to override filter restrictions. Defaults to True.
50
+ use_tap (bool, optional): Whether to enable tap functionality.
51
+ **kwargs: Additional keyword arguments passed to the plot rendering.
52
+
53
+ Returns:
54
+ Optional[pn.panel]: A panel containing the heatmap if data is appropriate,
55
+ otherwise returns filter match results.
56
+ """
57
+ return self.to_heatmap(
58
+ result_var=result_var,
59
+ tap_var=tap_var,
60
+ tap_container=tap_container,
61
+ tap_container_direction=tap_container_direction,
62
+ target_dimension=target_dimension,
63
+ override=override,
64
+ use_tap=use_tap,
65
+ **kwargs,
66
+ )
67
+
68
+ def to_heatmap(
69
+ self,
70
+ result_var: Parameter = None,
71
+ tap_var=None,
72
+ tap_container: pn.pane.panel = None,
73
+ tap_container_direction: pn.Column | pn.Row = None,
74
+ target_dimension=2,
75
+ override: bool = True,
76
+ use_tap: bool = None,
77
+ **kwargs,
78
+ ) -> Optional[pn.panel]:
79
+ """Generates a heatmap visualization from benchmark data.
80
+
81
+ This method applies filters to ensure the data is appropriate for a heatmap
82
+ and then passes the filtered data to the appropriate rendering method. If tap
83
+ functionality is enabled, it will create an interactive heatmap that displays
84
+ additional information when data points are selected.
85
+
86
+ Args:
87
+ result_var (Parameter, optional): The result variable to plot. If None, uses the default.
88
+ tap_var: Variables to display when tapping on heatmap points.
89
+ tap_container (pn.pane.panel, optional): Container to hold tapped information.
90
+ tap_container_direction (pn.Column | pn.Row, optional): Layout direction for the tap container.
91
+ target_dimension (int, optional): Target dimensionality for the plot. Defaults to 2.
92
+ override (bool, optional): Whether to override filter restrictions. Defaults to True.
93
+ use_tap (bool, optional): Whether to enable tap functionality.
94
+ **kwargs: Additional keyword arguments passed to the plot rendering.
95
+
96
+ Returns:
97
+ Optional[pn.panel]: A panel containing the heatmap if data is appropriate,
98
+ otherwise returns filter match results.
99
+ """
100
+ if tap_var is None:
101
+ tap_var = self.plt_cnt_cfg.panel_vars
102
+ elif not isinstance(tap_var, list):
103
+ tap_var = [tap_var]
104
+
105
+ if len(tap_var) == 0 or not use_tap:
106
+ heatmap_cb = self.to_heatmap_ds
107
+ else:
108
+ heatmap_cb = partial(
109
+ self.to_heatmap_container_tap_ds,
110
+ result_var_plots=tap_var,
111
+ container=tap_container,
112
+ tap_container_direction=tap_container_direction,
113
+ )
114
+
115
+ return self.filter(
116
+ heatmap_cb,
117
+ float_range=VarRange(0, None),
118
+ cat_range=VarRange(0, None),
119
+ input_range=VarRange(2, None),
120
+ panel_range=VarRange(0, None),
121
+ target_dimension=target_dimension,
122
+ result_var=result_var,
123
+ result_types=(ResultVar),
124
+ override=override,
125
+ **kwargs,
126
+ )
127
+
128
+ def to_heatmap_ds(
129
+ self, dataset: xr.Dataset, result_var: Parameter, **kwargs
130
+ ) -> Optional[hv.HeatMap]:
131
+ """Creates a basic heatmap from the provided dataset.
132
+
133
+ Given a filtered dataset, this method generates a heatmap visualization showing
134
+ the relationship between two input variables and a result variable using color intensity.
135
+
136
+ Args:
137
+ dataset (xr.Dataset): The dataset containing benchmark results.
138
+ result_var (Parameter): The result variable to plot.
139
+ **kwargs: Additional keyword arguments passed to the heatmap options.
140
+
141
+ Returns:
142
+ Optional[hv.HeatMap]: A heatmap visualization if the dataset has at least 2 dimensions,
143
+ otherwise returns None.
144
+ """
145
+ if len(dataset.dims) >= 2:
146
+ x = self.bench_cfg.input_vars[0].name
147
+ y = self.bench_cfg.input_vars[1].name
148
+ C = result_var.name
149
+ title = f"Heatmap of {result_var.name}"
150
+ time_args = self.time_widget(title)
151
+ return dataset.hvplot.heatmap(x=x, y=y, C=C, cmap="plasma", **time_args, **kwargs)
152
+ return None
153
+
154
+ def to_heatmap_container_tap_ds(
155
+ self,
156
+ dataset: xr.Dataset,
157
+ result_var: Parameter,
158
+ result_var_plots: List[Parameter] = None,
159
+ container: pn.pane.panel = None,
160
+ tap_container_direction: pn.Column | pn.Row = None,
161
+ **kwargs,
162
+ ) -> pn.Row:
163
+ """Creates an interactive heatmap with tap functionality.
164
+
165
+ This method generates a heatmap with interactive tap functionality that displays
166
+ additional information about selected points in separate containers.
167
+
168
+ Args:
169
+ dataset (xr.Dataset): The dataset containing benchmark results.
170
+ result_var (Parameter): The primary result variable to plot in the heatmap.
171
+ result_var_plots (List[Parameter], optional): Additional result variables to display when a point is tapped.
172
+ container (pn.pane.panel, optional): Container to display tapped information.
173
+ tap_container_direction (pn.Column | pn.Row, optional): Layout direction for the tap containers.
174
+ **kwargs: Additional keyword arguments passed to the heatmap options.
175
+
176
+ Returns:
177
+ pn.Row: A panel row containing the interactive heatmap and containers for tapped information.
178
+ """
179
+ htmap = self.to_heatmap_ds(dataset, result_var).opts(tools=["hover"], **kwargs)
180
+ result_var_plots, cont_instances = self.setup_results_and_containers(
181
+ result_var_plots, container
182
+ )
183
+ title = pn.pane.Markdown("Selected: None")
184
+
185
+ state = dict(x=None, y=None, update=False)
186
+
187
+ def tap_plot_heatmap(x, y): # pragma: no cover
188
+ # print(f"moved {x}{y}")
189
+ x_nearest_new = get_nearest_coords1D(
190
+ x, dataset.coords[self.bench_cfg.input_vars[0].name].data
191
+ )
192
+ y_nearest_new = get_nearest_coords1D(
193
+ y, dataset.coords[self.bench_cfg.input_vars[1].name].data
194
+ )
195
+
196
+ # xv = self.bench_cfg.input_vars[0].name
197
+ # yv = self.bench_cfg.input_vars[1].name
198
+ # nearest = get_nearest_coords(dataset, **{xv: x, yv: y})
199
+ # print(nearest)
200
+ # print(x_nearest_new,y_nearest_new)
201
+
202
+ if x_nearest_new != state["x"]:
203
+ state["x"] = x_nearest_new
204
+ state["update"] = True
205
+ if y_nearest_new != state["y"]:
206
+ state["y"] = y_nearest_new
207
+ state["update"] = True
208
+
209
+ if state["update"]:
210
+ kdims = {}
211
+ kdims[self.bench_cfg.input_vars[0].name] = state["x"]
212
+ kdims[self.bench_cfg.input_vars[1].name] = state["y"]
213
+
214
+ if hasattr(htmap, "current_key"):
215
+ for d, k in zip(htmap.kdims, htmap.current_key):
216
+ kdims[d.name] = k
217
+ for rv, cont in zip(result_var_plots, cont_instances):
218
+ ds = dataset[rv.name]
219
+ val = ds.sel(**kdims)
220
+ item = self.zero_dim_da_to_val(val)
221
+ title.object = "Selected: " + ", ".join([f"{k}:{v}" for k, v in kdims.items()])
222
+
223
+ cont.object = item
224
+ if hasattr(cont, "autoplay"): # container is a video, set to autoplay
225
+ cont.paused = False
226
+ cont.time = 0
227
+ cont.loop = True
228
+ cont.autoplay = True
229
+ state["update"] = False
230
+
231
+ def on_exit(x, y): # pragma: no cover # pylint: disable=unused-argument
232
+ state["update"] = True
233
+
234
+ htmap_posxy = hv.streams.PointerXY(source=htmap)
235
+ htmap_posxy.add_subscriber(tap_plot_heatmap)
236
+ ls = hv.streams.MouseLeave(source=htmap)
237
+ ls.add_subscriber(on_exit)
238
+
239
+ if tap_container_direction is None:
240
+ tap_container_direction = pn.Column
241
+ bound_plot = tap_container_direction(*cont_instances)
242
+
243
+ return pn.Row(htmap, pn.Column(title, bound_plot))
244
+
245
+ def to_heatmap_single(
246
+ self,
247
+ result_var: Parameter,
248
+ override: bool = True,
249
+ reduce: ReduceType = ReduceType.AUTO,
250
+ **kwargs,
251
+ ) -> hv.HeatMap:
252
+ """Creates a single heatmap from the result variable.
253
+
254
+ This method creates a heatmap directly from the benchmark results dataset without
255
+ applying the standard filtering pipeline.
256
+
257
+ Args:
258
+ result_var (Parameter): The result variable to plot.
259
+ override (bool, optional): Whether to override filter restrictions. Defaults to True.
260
+ reduce (ReduceType, optional): How to reduce the data. Defaults to ReduceType.AUTO.
261
+ **kwargs: Additional keyword arguments passed to the heatmap options.
262
+
263
+ Returns:
264
+ hv.HeatMap: A heatmap visualization if data matches criteria,
265
+ otherwise returns filter match results.
266
+ """
267
+ matches_res = PlotFilter(
268
+ float_range=VarRange(2, None),
269
+ cat_range=VarRange(0, None),
270
+ input_range=VarRange(1, None),
271
+ ).matches_result(self.plt_cnt_cfg, "to_heatmap", override)
272
+ if matches_res.overall:
273
+ z = result_var
274
+ title = f"{z.name} vs ("
275
+
276
+ for iv in self.bench_cfg.input_vars:
277
+ title += f" vs {iv.name}"
278
+ title += ")"
279
+
280
+ color_label = f"{z.name} [{z.units}]"
281
+
282
+ return self.to_hv_type(hv.HeatMap, reduce).opts(clabel=color_label, **kwargs)
283
+ return matches_res.to_panel()
284
+
285
+ def to_heatmap_tap(
286
+ self,
287
+ result_var: Parameter,
288
+ reduce: ReduceType = ReduceType.AUTO,
289
+ width=800,
290
+ height=800,
291
+ **kwargs,
292
+ ):
293
+ """Creates a tappable heatmap that shows details when tapped.
294
+
295
+ This method generates a heatmap with tap functionality that displays
296
+ additional visualizations when a point is selected.
297
+
298
+ Args:
299
+ result_var (Parameter): The result variable to plot.
300
+ reduce (ReduceType, optional): How to reduce the data. Defaults to ReduceType.AUTO.
301
+ width (int, optional): Width of the plot in pixels. Defaults to 800.
302
+ height (int, optional): Height of the plot in pixels. Defaults to 800.
303
+ **kwargs: Additional keyword arguments.
304
+
305
+ Returns:
306
+ hv.Layout: A layout containing both the heatmap and the dynamically updated detail view.
307
+ """
308
+ htmap = self.to_heatmap_single(result_var, reduce).opts(
309
+ tools=["hover", "tap"], width=width, height=height
310
+ )
311
+ htmap_posxy = hv.streams.Tap(source=htmap, x=0, y=0)
312
+
313
+ def tap_plot(x, y):
314
+ kwargs[self.bench_cfg.input_vars[0].name] = x
315
+ kwargs[self.bench_cfg.input_vars[1].name] = y
316
+ return self.get_nearest_holomap(**kwargs).opts(width=width, height=height)
317
+
318
+ tap_htmap = hv.DynamicMap(tap_plot, streams=[htmap_posxy])
319
+ return htmap + tap_htmap