holobench 1.25.2__py3-none-any.whl → 1.26.3__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 (81) hide show
  1. bencher/example/__init__.py +0 -0
  2. bencher/example/benchmark_data.py +196 -0
  3. bencher/example/example_all.py +45 -0
  4. bencher/example/example_categorical.py +99 -0
  5. bencher/example/example_composable_container.py +106 -0
  6. bencher/example/example_composable_container2.py +160 -0
  7. bencher/example/example_consts.py +39 -0
  8. bencher/example/example_custom_sweep.py +59 -0
  9. bencher/example/example_custom_sweep2.py +42 -0
  10. bencher/example/example_docs.py +34 -0
  11. bencher/example/example_filepath.py +27 -0
  12. bencher/example/example_float3D.py +101 -0
  13. bencher/example/example_float_cat.py +99 -0
  14. bencher/example/example_floats.py +89 -0
  15. bencher/example/example_floats2D.py +93 -0
  16. bencher/example/example_holosweep.py +98 -0
  17. bencher/example/example_holosweep_objects.py +111 -0
  18. bencher/example/example_holosweep_tap.py +144 -0
  19. bencher/example/example_image.py +155 -0
  20. bencher/example/example_levels.py +181 -0
  21. bencher/example/example_levels2.py +37 -0
  22. bencher/example/example_pareto.py +53 -0
  23. bencher/example/example_sample_cache.py +85 -0
  24. bencher/example/example_sample_cache_context.py +116 -0
  25. bencher/example/example_simple.py +134 -0
  26. bencher/example/example_simple_bool.py +35 -0
  27. bencher/example/example_simple_cat.py +48 -0
  28. bencher/example/example_simple_float.py +28 -0
  29. bencher/example/example_simple_float2d.py +29 -0
  30. bencher/example/example_strings.py +47 -0
  31. bencher/example/example_time_event.py +63 -0
  32. bencher/example/example_video.py +118 -0
  33. bencher/example/example_workflow.py +189 -0
  34. bencher/example/experimental/example_bokeh_plotly.py +38 -0
  35. bencher/example/experimental/example_hover_ex.py +45 -0
  36. bencher/example/experimental/example_hvplot_explorer.py +39 -0
  37. bencher/example/experimental/example_interactive.py +75 -0
  38. bencher/example/experimental/example_streamnd.py +49 -0
  39. bencher/example/experimental/example_streams.py +36 -0
  40. bencher/example/experimental/example_template.py +40 -0
  41. bencher/example/experimental/example_updates.py +84 -0
  42. bencher/example/experimental/example_vector.py +84 -0
  43. bencher/example/meta/example_meta.py +171 -0
  44. bencher/example/meta/example_meta_cat.py +25 -0
  45. bencher/example/meta/example_meta_float.py +23 -0
  46. bencher/example/meta/example_meta_levels.py +26 -0
  47. bencher/example/optuna/example_optuna.py +78 -0
  48. bencher/example/shelved/example_float2D_scatter.py +109 -0
  49. bencher/example/shelved/example_float3D_cone.py +96 -0
  50. bencher/example/shelved/example_kwargs.py +63 -0
  51. bencher/plotting/__init__.py +0 -0
  52. bencher/plotting/plot_filter.py +110 -0
  53. bencher/plotting/plt_cnt_cfg.py +75 -0
  54. bencher/results/__init__.py +0 -0
  55. bencher/results/bench_result.py +94 -0
  56. bencher/results/bench_result_base.py +476 -0
  57. bencher/results/composable_container/__init__.py +0 -0
  58. bencher/results/composable_container/composable_container_base.py +73 -0
  59. bencher/results/composable_container/composable_container_panel.py +39 -0
  60. bencher/results/composable_container/composable_container_video.py +184 -0
  61. bencher/results/float_formatter.py +44 -0
  62. bencher/results/holoview_result.py +753 -0
  63. bencher/results/optuna_result.py +354 -0
  64. bencher/results/panel_result.py +41 -0
  65. bencher/results/plotly_result.py +65 -0
  66. bencher/results/video_result.py +38 -0
  67. bencher/results/video_summary.py +222 -0
  68. bencher/variables/__init__.py +0 -0
  69. bencher/variables/inputs.py +202 -0
  70. bencher/variables/parametrised_sweep.py +208 -0
  71. bencher/variables/results.py +214 -0
  72. bencher/variables/sweep_base.py +162 -0
  73. bencher/variables/time.py +92 -0
  74. holobench-1.26.3.data/data/share/ament_index/resource_index/packages/bencher +0 -0
  75. holobench-1.26.3.data/data/share/bencher/package.xml +33 -0
  76. {holobench-1.25.2.dist-info → holobench-1.26.3.dist-info}/METADATA +5 -5
  77. holobench-1.26.3.dist-info/RECORD +93 -0
  78. holobench-1.25.2.dist-info/RECORD +0 -18
  79. {holobench-1.25.2.dist-info → holobench-1.26.3.dist-info}/LICENSE +0 -0
  80. {holobench-1.25.2.dist-info → holobench-1.26.3.dist-info}/WHEEL +0 -0
  81. {holobench-1.25.2.dist-info → holobench-1.26.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,222 @@
1
+ from typing import Optional, List
2
+ from copy import deepcopy
3
+ import panel as pn
4
+ import xarray as xr
5
+ from param import Parameter
6
+ from bencher.results.bench_result_base import BenchResultBase, ReduceType
7
+ from bencher.variables.results import ResultImage
8
+ from bencher.plotting.plot_filter import VarRange, PlotFilter
9
+ from bencher.utils import callable_name, int_to_col, color_tuple_to_255
10
+ from bencher.video_writer import VideoWriter
11
+ from bencher.results.video_result import VideoControls
12
+ from bencher.results.composable_container.composable_container_video import (
13
+ ComposableContainerVideo,
14
+ ComposeType,
15
+ RenderCfg,
16
+ )
17
+
18
+
19
+ class VideoSummaryResult(BenchResultBase):
20
+ def to_video_summary(
21
+ self,
22
+ result_var: Parameter = None,
23
+ reverse: bool = True,
24
+ result_types=(ResultImage,),
25
+ **kwargs,
26
+ ) -> Optional[pn.panel]:
27
+ return self.to_video_grid(
28
+ result_var=result_var,
29
+ result_types=result_types,
30
+ time_sequence_dimension=-1,
31
+ reverse=reverse,
32
+ **kwargs,
33
+ )
34
+
35
+ def to_video_grid(
36
+ self,
37
+ result_var: Parameter = None,
38
+ result_types=(ResultImage,),
39
+ pane_collection: pn.pane = None,
40
+ time_sequence_dimension=0,
41
+ target_duration: float = None,
42
+ **kwargs,
43
+ ) -> Optional[pn.panel]:
44
+ """Returns the results compiled into a video
45
+
46
+ Args:
47
+ result_var (Parameter, optional): The result var to plot. Defaults to None.
48
+ result_types (tuple, optional): The types of result var to convert to video. Defaults to (ResultImage,).
49
+ collection (pn.pane, optional): If there are multiple results, use this collection to stack them. Defaults to pn.Row().
50
+
51
+ Returns:
52
+ Optional[pn.panel]: a panel pane with a video of all results concatenated together
53
+ """
54
+ plot_filter = PlotFilter(
55
+ float_range=VarRange(0, None),
56
+ cat_range=VarRange(0, None),
57
+ panel_range=VarRange(1, None),
58
+ input_range=VarRange(1, None),
59
+ )
60
+ matches_res = plot_filter.matches_result(
61
+ self.plt_cnt_cfg, callable_name(self.to_video_grid_ds)
62
+ )
63
+
64
+ if pane_collection is None:
65
+ pane_collection = pn.Row()
66
+
67
+ if matches_res.overall:
68
+ ds = self.to_dataset(ReduceType.SQUEEZE)
69
+ for rv in self.get_results_var_list(result_var):
70
+ if isinstance(rv, result_types):
71
+ pane_collection.append(
72
+ self.to_video_grid_ds(
73
+ ds,
74
+ rv,
75
+ time_sequence_dimension=time_sequence_dimension,
76
+ target_duration=target_duration,
77
+ **kwargs,
78
+ )
79
+ )
80
+ return pane_collection
81
+ return matches_res.to_panel()
82
+
83
+ def to_video_grid_ds(
84
+ self,
85
+ dataset: xr.Dataset,
86
+ result_var: Parameter,
87
+ reverse=True,
88
+ time_sequence_dimension=0,
89
+ video_controls: VideoControls = None,
90
+ target_duration: float = None,
91
+ **kwargs,
92
+ ):
93
+ cvc = self._to_video_panes_ds(
94
+ dataset,
95
+ self.plot_cb,
96
+ target_dimension=0,
97
+ horizontal=True,
98
+ compose_method=ComposeType.right,
99
+ time_sequence_dimension=time_sequence_dimension,
100
+ result_var=result_var,
101
+ final=True,
102
+ reverse=reverse,
103
+ target_duration=target_duration,
104
+ **kwargs,
105
+ )
106
+
107
+ filename = VideoWriter().write_video_raw(cvc)
108
+
109
+ if filename is not None:
110
+ if video_controls is None:
111
+ video_controls = VideoControls()
112
+ return video_controls.video_container(
113
+ filename, width=kwargs.get("width", None), height=kwargs.get("height", None)
114
+ )
115
+ return None
116
+
117
+ def plot_cb(self, dataset, result_var, **kwargs):
118
+ val = self.ds_to_container(dataset, result_var, container=None, **kwargs)
119
+ return val
120
+
121
+ def dataset_to_compose_list(
122
+ self,
123
+ dataset: xr.Dataset,
124
+ first_compose_method: ComposeType = ComposeType.down,
125
+ time_sequence_dimension: int = 0,
126
+ ) -> List[ComposeType]:
127
+ """ "Given a dataset, chose an order for composing the results. By default will flip between right and down and the last dimension will be a time sequence.
128
+
129
+ Args:
130
+ dataset (xr.Dataset): the dataset to render
131
+ first_compose_method (ComposeType, optional): the direction of the first composition method. Defaults to ComposeType.right.
132
+ time_sequence_dimension (int, optional): The dimension to start time sequencing instead of composing in space. Defaults to 0.
133
+
134
+ Returns:
135
+ List[ComposeType]: A list of composition methods for composing the dataset result
136
+ """
137
+
138
+ num_dims = len(dataset.sizes)
139
+ if time_sequence_dimension == -1: # use time sequence for everything
140
+ compose_method_list = [ComposeType.sequence] * (num_dims + 1)
141
+ else:
142
+ compose_method_list = [first_compose_method]
143
+ compose_method_list.extend(
144
+ ComposeType.flip(compose_method_list[-1]) for _ in range(num_dims - 1)
145
+ )
146
+ compose_method_list.append(ComposeType.sequence)
147
+
148
+ for i in range(min(len(compose_method_list), time_sequence_dimension + 1)):
149
+ compose_method_list[i] = ComposeType.sequence
150
+
151
+ return compose_method_list
152
+
153
+ def _to_video_panes_ds(
154
+ self,
155
+ dataset: xr.Dataset,
156
+ plot_callback: callable = None,
157
+ target_dimension=0,
158
+ compose_method=ComposeType.right,
159
+ compose_method_list=None,
160
+ result_var=None,
161
+ time_sequence_dimension=0,
162
+ root_dimensions=None,
163
+ reverse=False,
164
+ target_duration: float = None,
165
+ **kwargs,
166
+ ) -> pn.panel:
167
+ num_dims = len(dataset.sizes)
168
+ dims = list(d for d in dataset.sizes)
169
+ if reverse:
170
+ dims = list(reversed(dims))
171
+
172
+ if root_dimensions is None:
173
+ root_dimensions = num_dims
174
+
175
+ if compose_method_list is None:
176
+ compose_method_list = self.dataset_to_compose_list(
177
+ dataset, compose_method, time_sequence_dimension=time_sequence_dimension
178
+ )
179
+
180
+ # print(compose_method_list)
181
+
182
+ compose_method_list_pop = deepcopy(compose_method_list)
183
+ if len(compose_method_list_pop) > 1:
184
+ compose_method = compose_method_list_pop.pop()
185
+
186
+ if num_dims > (target_dimension) and num_dims != 0:
187
+ selected_dim = dims[-1]
188
+ outer_container = ComposableContainerVideo()
189
+ for i in range(dataset.sizes[selected_dim]):
190
+ sliced = dataset.isel({selected_dim: i})
191
+ label_val = sliced.coords[selected_dim].values.item()
192
+ inner_container = ComposableContainerVideo()
193
+
194
+ panes = self._to_video_panes_ds(
195
+ sliced,
196
+ plot_callback=plot_callback,
197
+ target_dimension=target_dimension,
198
+ compose_method_list=compose_method_list_pop,
199
+ result_var=result_var,
200
+ root_dimensions=root_dimensions,
201
+ time_sequence_dimension=time_sequence_dimension,
202
+ )
203
+ inner_container.append(panes)
204
+
205
+ rendered = inner_container.render(
206
+ RenderCfg(
207
+ var_name=selected_dim,
208
+ var_value=label_val,
209
+ compose_method=compose_method,
210
+ duration=target_duration,
211
+ )
212
+ )
213
+ outer_container.append(rendered)
214
+ return outer_container.render(
215
+ RenderCfg(
216
+ compose_method=compose_method,
217
+ duration=target_duration,
218
+ background_col=color_tuple_to_255(int_to_col(num_dims - 2, 0.05, 1.0)),
219
+ # background_col= (255,0,0),
220
+ )
221
+ )
222
+ return plot_callback(dataset=dataset, result_var=result_var, **kwargs)
File without changes
@@ -0,0 +1,202 @@
1
+ from enum import Enum
2
+ from typing import List, Any, Dict
3
+
4
+ import numpy as np
5
+ from param import Integer, Number, Selector
6
+ from bencher.variables.sweep_base import SweepBase, shared_slots
7
+
8
+
9
+ class SweepSelector(Selector, SweepBase):
10
+ """A class to reprsent a parameter sweep of bools"""
11
+
12
+ __slots__ = shared_slots
13
+
14
+ def __init__(self, units: str = "ul", samples: int = None, **params):
15
+ SweepBase.__init__(self)
16
+ Selector.__init__(self, **params)
17
+
18
+ self.units = units
19
+ if samples is None:
20
+ self.samples = len(self.objects)
21
+ else:
22
+ self.samples = samples
23
+
24
+ def values(self) -> List[Any]:
25
+ """return all the values for a parameter sweep. If debug is true return a reduced list"""
26
+ return self.indices_to_samples(self.samples, self.objects)
27
+
28
+
29
+ class BoolSweep(SweepSelector):
30
+ """A class to reprsent a parameter sweep of bools"""
31
+
32
+ def __init__(self, units: str = "ul", samples: int = None, default=True, **params):
33
+ SweepSelector.__init__(
34
+ self,
35
+ units=units,
36
+ samples=samples,
37
+ default=default,
38
+ objects=[True, False] if default else [False, True],
39
+ **params,
40
+ )
41
+
42
+
43
+ class StringSweep(SweepSelector):
44
+ """A class to reprsent a parameter sweep of strings"""
45
+
46
+ def __init__(
47
+ self,
48
+ string_list: List[str],
49
+ units: str = "ul",
50
+ samples: int = None,
51
+ **params,
52
+ ):
53
+ SweepSelector.__init__(
54
+ self,
55
+ objects=string_list,
56
+ instantiate=True,
57
+ units=units,
58
+ samples=samples,
59
+ **params,
60
+ )
61
+
62
+
63
+ class EnumSweep(SweepSelector):
64
+ """A class to reprsent a parameter sweep of enums"""
65
+
66
+ __slots__ = shared_slots
67
+
68
+ def __init__(self, enum_type: Enum | List[Enum], units="ul", samples=None, **params):
69
+ # The enum can either be an Enum type or a list of enums
70
+ list_of_enums = isinstance(enum_type, list)
71
+ selector_list = enum_type if list_of_enums else list(enum_type)
72
+ SweepSelector.__init__(
73
+ self,
74
+ objects=selector_list,
75
+ instantiate=True,
76
+ units=units,
77
+ samples=samples,
78
+ **params,
79
+ )
80
+ if not list_of_enums: # Grab the docs from the enum type def
81
+ self.doc = enum_type.__doc__
82
+
83
+
84
+ class IntSweep(Integer, SweepBase):
85
+ """A class to reprsent a parameter sweep of ints"""
86
+
87
+ __slots__ = shared_slots + ["sample_values"]
88
+
89
+ def __init__(self, units="ul", samples=None, sample_values=None, **params):
90
+ SweepBase.__init__(self)
91
+ Integer.__init__(self, **params)
92
+
93
+ self.units = units
94
+
95
+ if sample_values is None:
96
+ if samples is None:
97
+ if self.bounds is None:
98
+ raise RuntimeError("You must define bounds for integer types")
99
+ self.samples = 1 + self.bounds[1] - self.bounds[0]
100
+ else:
101
+ self.samples = samples
102
+ self.sample_values = None
103
+ else:
104
+ self.sample_values = sample_values
105
+ self.samples = len(self.sample_values)
106
+ if "default" not in params:
107
+ self.default = sample_values[0]
108
+
109
+ def values(self) -> List[int]:
110
+ """return all the values for a parameter sweep. If debug is true return the list"""
111
+ sample_values = (
112
+ self.sample_values
113
+ if self.sample_values is not None
114
+ else list(range(int(self.bounds[0]), int(self.bounds[1] + 1)))
115
+ )
116
+
117
+ return self.indices_to_samples(self.samples, sample_values)
118
+
119
+ ###THESE ARE COPIES OF INTEGER VALIDATION BUT ALSO ALLOW NUMPY INT TYPES
120
+ def _validate_value(self, val, allow_None):
121
+ if callable(val):
122
+ return
123
+
124
+ if allow_None and val is None:
125
+ return
126
+
127
+ if not isinstance(val, (int, np.integer)):
128
+ raise ValueError(
129
+ "Integer parameter %r must be an integer, " "not type %r." % (self.name, type(val))
130
+ )
131
+
132
+ ###THESE ARE COPIES OF INTEGER VALIDATION BUT ALSO ALLOW NUMPY INT TYPES
133
+ def _validate_step(self, val, step):
134
+ if step is not None and not isinstance(step, (int, np.integer)):
135
+ raise ValueError(
136
+ "Step can only be None or an " "integer value, not type %r" % type(step)
137
+ )
138
+
139
+
140
+ class FloatSweep(Number, SweepBase):
141
+ """A class to represent a parameter sweep of floats"""
142
+
143
+ __slots__ = shared_slots + ["sample_values"]
144
+
145
+ def __init__(self, units="ul", samples=10, sample_values=None, step=None, **params):
146
+ SweepBase.__init__(self)
147
+ Number.__init__(self, step=step, **params)
148
+
149
+ self.units = units
150
+
151
+ self.sample_values = sample_values
152
+
153
+ if sample_values is None:
154
+ self.samples = samples
155
+ else:
156
+ self.samples = len(self.sample_values)
157
+ if "default" not in params:
158
+ self.default = sample_values[0]
159
+
160
+ def values(self) -> List[float]:
161
+ """return all the values for a parameter sweep. If debug is true return a reduced list"""
162
+ samps = self.samples
163
+ if self.sample_values is None:
164
+ if self.step is None:
165
+ return np.linspace(self.bounds[0], self.bounds[1], samps)
166
+
167
+ return np.arange(self.bounds[0], self.bounds[1], self.step)
168
+ return self.sample_values
169
+
170
+
171
+ def box(name, center, width):
172
+ var = FloatSweep(default=center, bounds=(center - width, center + width))
173
+ var.name = name
174
+ return var
175
+
176
+
177
+ def p(
178
+ name: str, values: List[Any] = None, samples: int = None, max_level: int = None
179
+ ) -> Dict[str, Any]:
180
+ """
181
+ Create a parameter dictionary with optional values, samples, and max_level.
182
+
183
+ Args:
184
+ name (str): The name of the parameter.
185
+ values (List[Any], optional): A list of values for the parameter. Defaults to None.
186
+ samples (int, optional): The number of samples. Must be greater than 0 if provided. Defaults to None.
187
+ max_level (int, optional): The maximum level. Must be greater than 0 if provided. Defaults to None.
188
+
189
+ Returns:
190
+ Dict[str, Any]: A dictionary containing the parameter details.
191
+ """
192
+ if max_level is not None and max_level <= 0:
193
+ raise ValueError("max_level must be greater than 0")
194
+
195
+ if samples is not None and samples <= 0:
196
+ raise ValueError("samples must be greater than 0")
197
+ return {"name": name, "values": values, "max_level": max_level, "samples": samples}
198
+
199
+
200
+ def with_level(arr: list, level) -> list:
201
+ return IntSweep(sample_values=arr).with_level(level).values()
202
+ # return tmp.with_sample_values(arr).with_level(level).values()
@@ -0,0 +1,208 @@
1
+ from functools import partial
2
+ from typing import List, Tuple, Any
3
+ from param import Parameter, Parameterized
4
+ import holoviews as hv
5
+ import panel as pn
6
+
7
+
8
+ from bencher.utils import make_namedtuple, hash_sha1
9
+ from bencher.variables.results import ALL_RESULT_TYPES, ResultHmap
10
+
11
+
12
+ class ParametrizedSweep(Parameterized):
13
+ """Parent class for all Sweep types that need a custom hash"""
14
+
15
+ @staticmethod
16
+ def param_hash(param_type: Parameterized, hash_value: bool = True) -> int:
17
+ """A custom hash function for parametrised types with options for hashing the value of the type and hashing metadata
18
+
19
+ Args:
20
+ param_type (Parameterized): A parameter
21
+ hash_value (bool, optional): use the value as part of the hash. Defaults to True.
22
+ # hash_meta (bool, optional): use metadata as part of the hash. Defaults to False.
23
+
24
+ Returns:
25
+ int: a hash
26
+ """
27
+
28
+ curhash = 0
29
+ if hash_value:
30
+ for k, v in param_type.param.values().items():
31
+ if k != "name":
32
+ curhash = hash_sha1((curhash, hash_sha1(v)))
33
+
34
+ # if hash_meta:
35
+ # for k, v in param_type.param.objects().items():
36
+ # if k != "name":
37
+ # print(f"key:{k}, hash:{hash_sha1(k)}")
38
+ # print(f"value:{v}, hash:{hash_sha1(v)}")
39
+ # curhash = hash_sha1((curhash, hash_sha1(k), hash_sha1(v)))
40
+ return curhash
41
+
42
+ def hash_persistent(self) -> str:
43
+ """A hash function that avoids the PYTHONHASHSEED 'feature' which returns a different hash value each time the program is run"""
44
+ return ParametrizedSweep.param_hash(self, True)
45
+
46
+ def update_params_from_kwargs(self, **kwargs) -> None:
47
+ """Given a dictionary of kwargs, set the parameters of the passed class 'self' to the values in the dictionary."""
48
+ used_params = {}
49
+ for key in self.param.objects().keys():
50
+ if key in kwargs:
51
+ if key != "name":
52
+ used_params[key] = kwargs[key]
53
+
54
+ self.param.update(**used_params)
55
+
56
+ @classmethod
57
+ def get_input_and_results(cls, include_name: bool = False) -> Tuple[dict, dict]:
58
+ """Get dictionaries of input parameters and result parameters
59
+
60
+ Args:
61
+ cls: A parametrised class
62
+ include_name (bool): Include the name parameter that all parametrised classes have. Default False
63
+
64
+ Returns:
65
+ Tuple[dict, dict]: a tuple containing the inputs and result parameters as dictionaries
66
+ """
67
+ inputs = {}
68
+ results = {}
69
+ for k, v in cls.param.objects().items():
70
+ if isinstance(
71
+ v,
72
+ ALL_RESULT_TYPES,
73
+ ):
74
+ results[k] = v
75
+ else:
76
+ inputs[k] = v
77
+
78
+ if not include_name:
79
+ inputs.pop("name")
80
+ return make_namedtuple("inputresult", inputs=inputs, results=results)
81
+
82
+ def get_inputs_as_dict(self) -> dict:
83
+ """Get the key:value pairs for all the input variables"""
84
+ inp = self.get_input_and_results().inputs
85
+ vals = self.param.values()
86
+ return {i: vals[i] for i, v in inp.items()}
87
+
88
+ def get_results_values_as_dict(self, holomap=None) -> dict:
89
+ """Get a dictionary of result variables with the name and the current value"""
90
+ values = self.param.values()
91
+ output = {key: values[key] for key in self.get_input_and_results().results}
92
+ if holomap is not None:
93
+ output |= {"hmap": holomap}
94
+ return output
95
+
96
+ @classmethod
97
+ def get_inputs_only(cls) -> List[Parameter]:
98
+ """Return a list of input parameters
99
+
100
+ Returns:
101
+ List[param.Parameter]: A list of input parameters
102
+ """
103
+ return list(cls.get_input_and_results().inputs.values())
104
+
105
+ @staticmethod
106
+ def filter_fn(item, p_name):
107
+ return item.name != p_name
108
+
109
+ @classmethod
110
+ def get_input_defaults(
111
+ cls,
112
+ override_defaults: List = None,
113
+ ) -> List[Tuple[Parameter, Any]]:
114
+ inp = cls.get_inputs_only()
115
+ if override_defaults is None:
116
+ override_defaults = []
117
+ assert isinstance(override_defaults, list)
118
+
119
+ for p in override_defaults:
120
+ inp = filter(partial(ParametrizedSweep.filter_fn, p_name=p[0].name), inp)
121
+
122
+ return override_defaults + [[i, i.default] for i in inp]
123
+
124
+ @classmethod
125
+ def get_input_defaults_override(cls, **kwargs) -> dict[str, Any]:
126
+ inp = cls.get_inputs_only()
127
+ defaults = {}
128
+ for i in inp:
129
+ defaults[i.name] = i.default
130
+
131
+ for k, v in kwargs.items():
132
+ defaults[k] = v
133
+
134
+ return defaults
135
+
136
+ @classmethod
137
+ def get_results_only(cls) -> List[Parameter]:
138
+ """Return a list of result parameters
139
+
140
+ Returns:
141
+ List[param.Parameter]: A list of result parameters
142
+ """
143
+ return list(cls.get_input_and_results().results.values())
144
+
145
+ @classmethod
146
+ def get_inputs_as_dims(
147
+ self, compute_values=False, remove_dims: str | List[str] = None
148
+ ) -> List[hv.Dimension]:
149
+ inputs = self.get_inputs_only()
150
+
151
+ if remove_dims is not None:
152
+ if isinstance(remove_dims, str):
153
+ remove_dims = [remove_dims]
154
+ filtered_inputs = [i for i in inputs if i.name not in remove_dims]
155
+ inputs = filtered_inputs
156
+
157
+ return [iv.as_dim(compute_values) for iv in inputs]
158
+
159
+ def to_dynamic_map(
160
+ self, callback=None, name=None, remove_dims: str | List[str] = None, result_var: str = None
161
+ ) -> hv.DynamicMap:
162
+ if callback is None:
163
+ callback = self.__call__
164
+
165
+ if result_var is None:
166
+ result_vars = self.get_input_and_results().results
167
+ for k, rv in result_vars.items():
168
+ if isinstance(rv, ResultHmap):
169
+ result_var = k
170
+
171
+ def callback_wrapper(**kwargs):
172
+ return callback(**kwargs)[result_var]
173
+
174
+ return hv.DynamicMap(
175
+ callback=callback_wrapper,
176
+ kdims=self.get_inputs_as_dims(compute_values=False, remove_dims=remove_dims),
177
+ name=name,
178
+ ).opts(shared_axes=False, framewise=True, width=1000, height=1000)
179
+
180
+ def to_gui(self, result_var: str = None, **kwargs): # pragma: no cover
181
+ main = pn.Row(
182
+ self.to_dynamic_map(result_var=result_var, **kwargs),
183
+ )
184
+ main.show()
185
+
186
+ def to_holomap(self, callback, remove_dims: str | List[str] = None) -> hv.DynamicMap:
187
+ return hv.HoloMap(
188
+ hv.DynamicMap(
189
+ callback=callback,
190
+ kdims=self.get_inputs_as_dims(compute_values=True, remove_dims=remove_dims),
191
+ )
192
+ )
193
+
194
+ def __call__(self, **kwargs):
195
+ return self.get_results_values_as_dict()
196
+
197
+ def plot_hmap(self, **kwargs):
198
+ return self.__call__(**kwargs)["hmap"]
199
+
200
+ def to_bench(self, run_cfg=None, report=None, name: str = None):
201
+ from bencher import Bench
202
+
203
+ assert isinstance(self, ParametrizedSweep)
204
+
205
+ if name is None:
206
+ name = self.name[:-5] # param adds 5 digit number to the end, so remove it
207
+
208
+ return Bench(name, self, run_cfg=run_cfg, report=report)