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,158 @@
1
+ from __future__ import annotations
2
+ import logging
3
+ from typing import Optional
4
+ import panel as pn
5
+ import holoviews as hv
6
+ from param import Parameter
7
+ import hvplot.xarray # noqa pylint: disable=duplicate-code,unused-import
8
+ import xarray as xr
9
+
10
+ from bencher.results.bench_result_base import ReduceType
11
+ from bencher.plotting.plot_filter import PlotFilter, VarRange
12
+ from bencher.variables.results import ResultVar
13
+ from bencher.results.holoview_results.holoview_result import HoloviewResult
14
+
15
+
16
+ class SurfaceResult(HoloviewResult):
17
+ """A class for creating 3D surface plots from benchmark results.
18
+
19
+ This class provides methods to visualize benchmark data as 3D surface plots,
20
+ which are useful for showing relationships between two input variables and
21
+ a result variable. Surface plots can also display standard deviation bounds
22
+ when benchmark runs include multiple repetitions.
23
+ """
24
+
25
+ def to_plot(
26
+ self, result_var: Parameter = None, override: bool = True, **kwargs
27
+ ) -> Optional[pn.pane.Pane]:
28
+ """Generates a 3D surface plot from benchmark data.
29
+
30
+ This is a convenience method that calls to_surface() with the same parameters.
31
+
32
+ Args:
33
+ result_var (Parameter, optional): The result variable to plot. If None, uses the default.
34
+ override (bool, optional): Whether to override filter restrictions. Defaults to True.
35
+ **kwargs: Additional keyword arguments passed to the plot rendering.
36
+
37
+ Returns:
38
+ Optional[pn.pane.Pane]: A panel containing the surface plot if data is appropriate,
39
+ otherwise returns filter match results.
40
+ """
41
+ return self.to_surface(result_var=result_var, override=override, **kwargs)
42
+
43
+ def to_surface(
44
+ self, result_var: Parameter = None, override: bool = True, **kwargs
45
+ ) -> Optional[pn.pane.Pane]:
46
+ """Generates a 3D surface plot from benchmark data.
47
+
48
+ This method applies filters to ensure the data is appropriate for a surface plot
49
+ and then passes the filtered data to to_surface_ds for rendering.
50
+
51
+ Args:
52
+ result_var (Parameter, optional): The result variable to plot. If None, uses the default.
53
+ override (bool, optional): Whether to override filter restrictions. Defaults to True.
54
+ **kwargs: Additional keyword arguments passed to the plot rendering.
55
+
56
+ Returns:
57
+ Optional[pn.pane.Pane]: A panel containing the surface plot if data is appropriate,
58
+ otherwise returns filter match results.
59
+ """
60
+ return self.filter(
61
+ self.to_surface_ds,
62
+ float_range=VarRange(2, None),
63
+ cat_range=VarRange(0, None),
64
+ input_range=VarRange(1, None),
65
+ reduce=ReduceType.REDUCE,
66
+ target_dimension=2,
67
+ result_var=result_var,
68
+ result_types=(ResultVar),
69
+ override=override,
70
+ **kwargs,
71
+ )
72
+
73
+ def to_surface_ds(
74
+ self,
75
+ dataset: xr.Dataset,
76
+ result_var: Parameter,
77
+ override: bool = True,
78
+ alpha: float = 0.3,
79
+ **kwargs,
80
+ ) -> Optional[pn.panel]:
81
+ """Creates a 3D surface plot from the provided dataset.
82
+
83
+ Given a filtered dataset, this method generates a 3D surface visualization showing
84
+ the relationship between two input variables and the result variable. When multiple
85
+ benchmark repetitions are available, standard deviation bounds can also be displayed.
86
+
87
+ Args:
88
+ dataset (xr.Dataset): The dataset containing benchmark results.
89
+ result_var (Parameter): The result variable to plot.
90
+ override (bool, optional): Whether to override filter restrictions. Defaults to True.
91
+ alpha (float, optional): The transparency level for standard deviation surfaces. Defaults to 0.3.
92
+ **kwargs: Additional keyword arguments passed to the surface plot options.
93
+
94
+ Returns:
95
+ Optional[pn.panel]: A panel containing the surface plot if data matches criteria,
96
+ otherwise returns filter match results.
97
+ """
98
+ matches_res = PlotFilter(
99
+ float_range=VarRange(2, 2),
100
+ cat_range=VarRange(0, None),
101
+ vector_len=VarRange(1, 1),
102
+ result_vars=VarRange(1, 1),
103
+ ).matches_result(self.plt_cnt_cfg, "to_surface_hv", override)
104
+ if matches_res.overall:
105
+ # xr_cfg = plot_float_cnt_2(self.plt_cnt_cfg, result_var)
106
+
107
+ # TODO a warning suggests setting this parameter, but it does not seem to help as expected, leaving here to fix in the future
108
+ # hv.config.image_rtol = 1.0
109
+
110
+ mean = dataset[result_var.name]
111
+
112
+ hvds = hv.Dataset(dataset[result_var.name])
113
+
114
+ x = self.plt_cnt_cfg.float_vars[0]
115
+ y = self.plt_cnt_cfg.float_vars[1]
116
+
117
+ try:
118
+ surface = hvds.to(hv.Surface, vdims=[result_var.name])
119
+ surface = surface.opts(colorbar=True)
120
+ except Exception as e: # pylint: disable=broad-except
121
+ logging.warning(e)
122
+
123
+ if self.bench_cfg.repeats > 1:
124
+ std_dev = dataset[f"{result_var.name}_std"]
125
+
126
+ upper = mean + std_dev
127
+ upper.name = result_var.name
128
+
129
+ lower = mean - std_dev
130
+ lower.name = result_var.name
131
+
132
+ surface *= (
133
+ hv.Dataset(upper)
134
+ .to(hv.Surface)
135
+ .opts(alpha=alpha, colorbar=False, backend="plotly")
136
+ )
137
+ surface *= (
138
+ hv.Dataset(lower)
139
+ .to(hv.Surface)
140
+ .opts(alpha=alpha, colorbar=False, backend="plotly")
141
+ )
142
+
143
+ surface = surface.opts(
144
+ zlabel=f"{result_var.name} [{result_var.units}]",
145
+ title=f"{result_var.name} vs ({x.name} and {y.name})",
146
+ backend="plotly",
147
+ **kwargs,
148
+ )
149
+
150
+ if self.bench_cfg.render_plotly:
151
+ hv.extension("plotly")
152
+ out = surface
153
+ else:
154
+ # using render disabled the holoviews sliders :(
155
+ out = hv.render(surface, backend="plotly")
156
+ return pn.Column(out, name="surface_hv")
157
+
158
+ return matches_res.to_panel()
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+ import holoviews as hv
3
+ from bencher.results.bench_result_base import ReduceType
4
+ from bencher.results.holoview_results.holoview_result import HoloviewResult
5
+
6
+
7
+ class TableResult(HoloviewResult):
8
+ def to_plot(self, **kwargs) -> hv.Table: # pylint:disable=unused-argument
9
+ """Convert the dataset to a Table visualization.
10
+
11
+ Returns:
12
+ hv.Table: A HoloViews Table object.
13
+ """
14
+ return self.to_hv_type(hv.Table, ReduceType.SQUEEZE)
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+ import panel as pn
3
+
4
+ from bencher.results.holoview_results.holoview_result import HoloviewResult
5
+
6
+
7
+ class TabulatorResult(HoloviewResult):
8
+ def to_plot(self, **kwargs) -> pn.widgets.Tabulator: # pylint:disable=unused-argument
9
+ """Create an interactive table visualization of the data.
10
+
11
+ Passes the data to the panel Tabulator type to display an interactive table.
12
+ See https://panel.holoviz.org/reference/widgets/Tabulator.html for extra options.
13
+
14
+ Args:
15
+ **kwargs: Additional parameters to pass to the Tabulator constructor.
16
+
17
+ Returns:
18
+ pn.widgets.Tabulator: An interactive table widget.
19
+ """
20
+ return pn.widgets.Tabulator(self.to_pandas())
@@ -5,11 +5,6 @@ from copy import deepcopy
5
5
  import numpy as np
6
6
  import optuna
7
7
  import panel as pn
8
- from collections import defaultdict
9
- from textwrap import wrap
10
-
11
- import pandas as pd
12
- import xarray as xr
13
8
 
14
9
 
15
10
  from optuna.visualization import (
@@ -18,9 +13,7 @@ from optuna.visualization import (
18
13
  )
19
14
  from bencher.utils import hmap_canonical_input
20
15
  from bencher.variables.time import TimeSnapshot, TimeEvent
21
- from bencher.bench_cfg import BenchCfg
22
- from bencher.plotting.plt_cnt_cfg import PltCntCfg
23
-
16
+ from bencher.results.bench_result_base import BenchResultBase, ReduceType
24
17
 
25
18
  # from bencher.results.bench_result_base import BenchResultBase
26
19
  from bencher.optuna_conversions import (
@@ -33,97 +26,15 @@ from bencher.optuna_conversions import (
33
26
  )
34
27
 
35
28
 
36
- def convert_dataset_bool_dims_to_str(dataset: xr.Dataset) -> xr.Dataset:
37
- """Given a dataarray that contains boolean coordinates, convert them to strings so that holoviews loads the data properly
38
-
39
- Args:
40
- dataarray (xr.DataArray): dataarray with boolean coordinates
41
-
42
- Returns:
43
- xr.DataArray: dataarray with boolean coordinates converted to strings
44
- """
45
- bool_coords = {}
46
- for c in dataset.coords:
47
- if dataset.coords[c].dtype == bool:
48
- bool_coords[c] = [str(vals) for vals in dataset.coords[c].values]
49
-
50
- if len(bool_coords) > 0:
51
- return dataset.assign_coords(bool_coords)
52
- return dataset
53
-
54
-
55
- class OptunaResult:
56
- def __init__(self, bench_cfg: BenchCfg) -> None:
57
- self.bench_cfg = bench_cfg
58
- # self.wrap_long_time_labels(bench_cfg) # todo remove
59
- self.ds = xr.Dataset()
60
- self.object_index = []
61
- self.hmaps = defaultdict(dict)
62
- self.result_hmaps = bench_cfg.result_hmaps
63
- self.studies = []
64
- self.plt_cnt_cfg = PltCntCfg()
65
- self.plot_inputs = []
66
- self.dataset_list = []
67
-
68
- # self.width=600/
69
- # self.height=600
70
-
71
- # bench_res.objects.append(rv)
72
- # bench_res.reference_index = len(bench_res.objects)
73
-
74
- def post_setup(self):
75
- self.plt_cnt_cfg = PltCntCfg.generate_plt_cnt_cfg(self.bench_cfg)
76
- self.bench_cfg = self.wrap_long_time_labels(self.bench_cfg)
77
- self.ds = convert_dataset_bool_dims_to_str(self.ds)
78
-
79
- def to_xarray(self) -> xr.Dataset:
80
- return self.ds
81
-
82
- def setup_object_index(self):
83
- self.object_index = []
84
-
85
- def to_pandas(self, reset_index=True) -> pd.DataFrame:
86
- """Get the xarray results as a pandas dataframe
87
-
88
- Returns:
89
- pd.DataFrame: The xarray results array as a pandas dataframe
90
- """
91
- ds = self.to_xarray().to_dataframe()
92
- if reset_index:
93
- return ds.reset_index()
94
- return ds
95
-
96
- def wrap_long_time_labels(self, bench_cfg):
97
- """Takes a benchCfg and wraps any index labels that are too long to be plotted easily
98
-
99
- Args:
100
- bench_cfg (BenchCfg):
101
-
102
- Returns:
103
- BenchCfg: updated config with wrapped labels
104
- """
105
- if bench_cfg.over_time:
106
- if self.ds.coords["over_time"].dtype == np.datetime64:
107
- # plotly catastrophically fails to plot anything with the default long string representation of time, so convert to a shorter time representation
108
- self.ds.coords["over_time"] = [
109
- pd.to_datetime(t).strftime("%d-%m-%y %H-%M-%S")
110
- for t in self.ds.coords["over_time"].values
111
- ]
112
- # wrap very long time event labels because otherwise the graphs are unreadable
113
- if bench_cfg.time_event is not None:
114
- self.ds.coords["over_time"] = [
115
- "\n".join(wrap(t, 20)) for t in self.ds.coords["over_time"].values
116
- ]
117
- return bench_cfg
118
-
119
- def to_optuna_plots(self) -> List[pn.pane.panel]:
29
+ class OptunaResult(BenchResultBase):
30
+ def to_optuna_plots(self, **kwargs) -> List[pn.pane.panel]:
120
31
  """Create an optuna summary from the benchmark results
121
32
 
122
33
  Returns:
123
34
  List[pn.pane.panel]: A list of optuna plot summarising the benchmark process
124
35
  """
125
36
 
126
- return self.collect_optuna_plots()
37
+ return self.collect_optuna_plots(**kwargs)
127
38
 
128
39
  def to_optuna_from_sweep(self, bench, n_trials=30):
129
40
  optu = self.to_optuna_from_results(
@@ -175,7 +86,8 @@ class OptunaResult:
175
86
  optuna.Study: optuna description of the study
176
87
  """
177
88
  if include_meta:
178
- df = self.to_pandas()
89
+ # df = self.to_pandas()
90
+ df = self.to_dataset(reduce=ReduceType.NONE).to_dataframe().reset_index()
179
91
  all_vars = []
180
92
  for v in self.bench_cfg.all_vars:
181
93
  if type(v) is not TimeEvent:
@@ -189,11 +101,15 @@ class OptunaResult:
189
101
  # if self.bench_cfg.repeats>1:
190
102
  # df = self.bench_cfg.ds.mean("repeat").to_dataframe().reset_index()
191
103
  # else:
192
- df = self.to_pandas().reset_index()
104
+ # df = self.to_pandas().reset_index()
105
+ df = self.to_dataset(reduce=ReduceType.AUTO).to_dataframe().reset_index()
193
106
  # df = self.bench_cfg.ds.mean("repeat").to_dataframe.reset_index()
194
107
  # self.bench_cfg.all_vars
195
108
  # del self.bench_cfg.meta_vars[1]
196
109
 
110
+ # optuna does not like the nan values so remove them.
111
+ df.dropna(inplace=True)
112
+
197
113
  trials = []
198
114
  distributions = {}
199
115
  for i in all_vars:
@@ -210,6 +126,9 @@ class OptunaResult:
210
126
  params[i.name] = row[1][i.name]
211
127
 
212
128
  for r in self.bench_cfg.optuna_targets():
129
+ # print(row[1][r])
130
+ # print(np.isnan(row[1][r]))
131
+ # if not np.isnan(row[1][r]):
213
132
  values.append(row[1][r])
214
133
 
215
134
  trials.append(
@@ -245,7 +164,9 @@ class OptunaResult:
245
164
  def get_pareto_front_params(self):
246
165
  return [p.params for p in self.studies[0].trials]
247
166
 
248
- def collect_optuna_plots(self) -> List[pn.pane.panel]:
167
+ def collect_optuna_plots(
168
+ self, pareto_width: float = None, pareto_height: float = None
169
+ ) -> List[pn.pane.panel]:
249
170
  """Use optuna to plot various summaries of the optimisation
250
171
 
251
172
  Args:
@@ -292,11 +213,18 @@ class OptunaResult:
292
213
  include_dominated_trials=False,
293
214
  )
294
215
  )
216
+ if pareto_width is not None:
217
+ study_pane[-1].width = pareto_width
218
+ if pareto_height is not None:
219
+ study_pane[-1].height = pareto_height
220
+ try:
221
+ study_pane.append(param_importance(self.bench_cfg, study))
222
+ param_str.append(
223
+ f" Number of trials on the Pareto front: {len(study.best_trials)}"
224
+ )
225
+ except RuntimeError as e:
226
+ study_pane.append(f"Error generating parameter importance: {str(e)}")
295
227
 
296
- study_pane.append(param_importance(self.bench_cfg, study))
297
- param_str.append(
298
- f" Number of trials on the Pareto front: {len(study.best_trials)}"
299
- )
300
228
  for t in study.best_trials:
301
229
  param_str.extend(summarise_trial(t, self.bench_cfg))
302
230
 
@@ -343,18 +271,5 @@ class OptunaResult:
343
271
  """Return a deep copy of these results"""
344
272
  return deepcopy(self)
345
273
 
346
- def set_plot_size(self, **kwargs) -> dict:
347
- if "width" not in kwargs:
348
- if self.bench_cfg.plot_size is not None:
349
- kwargs["width"] = self.bench_cfg.plot_size
350
- # specific width overrides general size
351
- if self.bench_cfg.plot_width is not None:
352
- kwargs["width"] = self.bench_cfg.plot_width
353
-
354
- if "height" not in kwargs:
355
- if self.bench_cfg.plot_size is not None:
356
- kwargs["height"] = self.bench_cfg.plot_size
357
- # specific height overrides general size
358
- if self.bench_cfg.plot_height is not None:
359
- kwargs["height"] = self.bench_cfg.plot_height
360
- return kwargs
274
+ def get_best_holomap(self, name: str = None):
275
+ return self.get_hmap(name)[self.get_best_trial_params(True)]
@@ -0,0 +1,38 @@
1
+ from typing import Optional
2
+ from pathlib import Path
3
+ import panel as pn
4
+
5
+
6
+ class VideoControls:
7
+ def __init__(self) -> None:
8
+ self.vid_p = []
9
+
10
+ def video_container(self, path, **kwargs):
11
+ if path is not None and Path(path).exists():
12
+ vid = pn.pane.Video(path, autoplay=True, **kwargs)
13
+ vid.loop = True
14
+ self.vid_p.append(vid)
15
+ return vid
16
+ return pn.pane.Markdown(f"video does not exist {path}")
17
+
18
+ def video_controls(self) -> Optional[pn.Column]:
19
+ def play_vid(_): # pragma: no cover
20
+ for r in self.vid_p:
21
+ r.paused = False
22
+
23
+ def reset_vid(_): # pragma: no cover
24
+ for r in self.vid_p:
25
+ r.paused = False
26
+ r.time = 0
27
+
28
+ button_names = ["Play Videos", "Pause Videos", "Loop Videos", "Reset Videos"]
29
+ buttom_cb = [play_vid, reset_vid]
30
+
31
+ buttons = pn.Row()
32
+
33
+ for name, cb in zip(button_names, buttom_cb):
34
+ button = pn.widgets.Button(name=name)
35
+ pn.bind(cb, button, watch=True)
36
+ buttons.append(button)
37
+
38
+ return pn.Column(buttons)
@@ -1,38 +1,41 @@
1
1
  from typing import Optional
2
- from pathlib import Path
2
+ from functools import partial
3
3
  import panel as pn
4
-
5
-
6
- class VideoControls:
7
- def __init__(self) -> None:
8
- self.vid_p = []
9
-
10
- def video_container(self, path, **kwargs):
11
- if path is not None and Path(path).exists():
12
- vid = pn.pane.Video(path, autoplay=True, **kwargs)
13
- vid.loop = True
14
- self.vid_p.append(vid)
15
- return vid
16
- return pn.pane.Markdown(f"video does not exist {path}")
17
-
18
- def video_controls(self) -> Optional[pn.Column]:
19
- def play_vid(_): # pragma: no cover
20
- for r in self.vid_p:
21
- r.paused = False
22
-
23
- def reset_vid(_): # pragma: no cover
24
- for r in self.vid_p:
25
- r.paused = False
26
- r.time = 0
27
-
28
- button_names = ["Play Videos", "Pause Videos", "Loop Videos", "Reset Videos"]
29
- buttom_cb = [play_vid, reset_vid]
30
-
31
- buttons = pn.Row()
32
-
33
- for name, cb in zip(button_names, buttom_cb):
34
- button = pn.widgets.Button(name=name)
35
- pn.bind(cb, button, watch=True)
36
- buttons.append(button)
37
-
38
- return pn.Column(buttons)
4
+ from param import Parameter
5
+ import holoviews as hv
6
+ from bencher.results.bench_result_base import BenchResultBase, ReduceType
7
+ from bencher.results.video_controls import VideoControls
8
+ from bencher.variables.results import (
9
+ PANEL_TYPES,
10
+ )
11
+
12
+
13
+ class VideoResult(BenchResultBase):
14
+ def to_video(self, result_var: Parameter = None, **kwargs):
15
+ vc = VideoControls()
16
+ return pn.Column(
17
+ vc.video_controls(),
18
+ self.to_panes(result_var=result_var, container=vc.video_container, **kwargs),
19
+ )
20
+
21
+ def to_panes(
22
+ self,
23
+ result_var: Parameter = None,
24
+ hv_dataset=None,
25
+ target_dimension: int = 0,
26
+ container=None,
27
+ level: int = None,
28
+ **kwargs,
29
+ ) -> Optional[pn.pane.panel]:
30
+ if hv_dataset is None:
31
+ hv_dataset = self.to_hv_dataset(ReduceType.SQUEEZE, level=level)
32
+ elif not isinstance(hv_dataset, hv.Dataset):
33
+ hv_dataset = hv.Dataset(hv_dataset)
34
+ return self.map_plot_panes(
35
+ partial(self.ds_to_container, container=container),
36
+ hv_dataset=hv_dataset,
37
+ target_dimension=target_dimension,
38
+ result_var=result_var,
39
+ result_types=PANEL_TYPES,
40
+ **kwargs,
41
+ )
@@ -8,7 +8,7 @@ from bencher.variables.results import ResultImage
8
8
  from bencher.plotting.plot_filter import VarRange, PlotFilter
9
9
  from bencher.utils import callable_name, int_to_col, color_tuple_to_255
10
10
  from bencher.video_writer import VideoWriter
11
- from bencher.results.video_result import VideoControls
11
+ from bencher.results.video_controls import VideoControls
12
12
  from bencher.results.composable_container.composable_container_video import (
13
13
  ComposableContainerVideo,
14
14
  ComposeType,
@@ -60,7 +60,7 @@ class VideoSummaryResult(BenchResultBase):
60
60
  input_range=VarRange(1, None),
61
61
  )
62
62
  matches_res = plot_filter.matches_result(
63
- self.plt_cnt_cfg, callable_name(self.to_video_grid_ds)
63
+ self.plt_cnt_cfg, callable_name(self.to_video_grid_ds), override=False
64
64
  )
65
65
 
66
66
  if pane_collection is None:
@@ -1,29 +1,50 @@
1
- import panel as pn
2
- import plotly.graph_objs as go
3
- from typing import Optional
4
- import xarray as xr
1
+ from typing import Optional, Any
5
2
 
3
+ import xarray as xr
6
4
  from param import Parameter
5
+ import panel as pn
6
+ import plotly.graph_objs as go
7
7
 
8
8
  from bencher.plotting.plot_filter import VarRange
9
9
  from bencher.results.bench_result_base import BenchResultBase, ReduceType
10
10
  from bencher.variables.results import ResultVar
11
11
 
12
12
 
13
- class PlotlyResult(BenchResultBase):
14
- def to_volume(self, result_var: Parameter = None, **kwargs):
13
+ class VolumeResult(BenchResultBase):
14
+ def to_plot(
15
+ self, result_var: Optional[Parameter] = None, override: bool = True, **kwargs: Any
16
+ ) -> Optional[pn.panel]:
17
+ """Generates a 3d volume plot from benchmark data.
18
+
19
+ Args:
20
+ result_var (Optional[Parameter]): The result variable to plot. If None, uses the default.
21
+ override (bool): Whether to override filter restrictions. Defaults to True.
22
+ **kwargs (Any): Additional keyword arguments passed to the plot rendering.
23
+
24
+ Returns:
25
+ Optional[pn.panel]: A panel containing the volume plot if data is appropriate,
26
+ otherwise returns filter match results.
27
+ """
28
+ return self.to_volume(
29
+ result_var=result_var,
30
+ override=override,
31
+ **kwargs,
32
+ )
33
+
34
+ def to_volume(self, result_var: Parameter = None, override: bool = True, **kwargs):
15
35
  return self.filter(
16
- self.to_volume_da,
36
+ self.to_volume_ds,
17
37
  float_range=VarRange(3, 3),
18
38
  cat_range=VarRange(-1, 0),
19
39
  reduce=ReduceType.REDUCE,
20
40
  target_dimension=3,
21
41
  result_var=result_var,
22
42
  result_types=(ResultVar),
43
+ override=override,
23
44
  **kwargs,
24
45
  )
25
46
 
26
- def to_volume_da(
47
+ def to_volume_ds(
27
48
  self, dataset: xr.Dataset, result_var: Parameter, width=600, height=600
28
49
  ) -> Optional[pn.pane.Plotly]:
29
50
  """Given a benchCfg generate a 3D surface plot