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.
- bencher/__init__.py +20 -2
- bencher/bench_cfg.py +262 -54
- bencher/bench_report.py +2 -2
- bencher/bench_runner.py +96 -10
- bencher/bencher.py +421 -89
- bencher/class_enum.py +70 -7
- bencher/example/example_dataframe.py +2 -2
- bencher/example/example_levels.py +17 -173
- bencher/example/example_pareto.py +107 -31
- bencher/example/example_rerun2.py +1 -1
- bencher/example/example_simple_bool.py +2 -2
- bencher/example/example_simple_float2d.py +6 -1
- bencher/example/example_video.py +2 -0
- bencher/example/experimental/example_hvplot_explorer.py +2 -2
- bencher/example/inputs_0D/example_0_in_1_out.py +25 -15
- bencher/example/inputs_0D/example_0_in_2_out.py +12 -3
- bencher/example/inputs_0_float/example_0_cat_in_2_out.py +88 -0
- bencher/example/inputs_0_float/example_1_cat_in_2_out.py +98 -0
- bencher/example/inputs_0_float/example_2_cat_in_2_out.py +107 -0
- bencher/example/inputs_0_float/example_3_cat_in_2_out.py +111 -0
- bencher/example/inputs_1D/example1d_common.py +48 -12
- bencher/example/inputs_1D/example_0_float_1_cat.py +33 -0
- bencher/example/inputs_1D/example_1_cat_in_2_out_repeats.py +68 -0
- bencher/example/inputs_1D/example_1_float_2_cat_repeats.py +3 -0
- bencher/example/inputs_1D/example_1_int_in_1_out.py +98 -0
- bencher/example/inputs_1D/example_1_int_in_2_out.py +101 -0
- bencher/example/inputs_1D/example_1_int_in_2_out_repeats.py +99 -0
- bencher/example/inputs_1_float/example_1_float_0_cat_in_2_out.py +117 -0
- bencher/example/inputs_1_float/example_1_float_1_cat_in_2_out.py +124 -0
- bencher/example/inputs_1_float/example_1_float_2_cat_in_2_out.py +132 -0
- bencher/example/inputs_1_float/example_1_float_3_cat_in_2_out.py +140 -0
- bencher/example/inputs_2D/example_2_cat_in_4_out_repeats.py +104 -0
- bencher/example/inputs_2_float/example_2_float_0_cat_in_2_out.py +98 -0
- bencher/example/inputs_2_float/example_2_float_1_cat_in_2_out.py +112 -0
- bencher/example/inputs_2_float/example_2_float_2_cat_in_2_out.py +122 -0
- bencher/example/inputs_2_float/example_2_float_3_cat_in_2_out.py +138 -0
- bencher/example/inputs_3_float/example_3_float_0_cat_in_2_out.py +111 -0
- bencher/example/inputs_3_float/example_3_float_1_cat_in_2_out.py +117 -0
- bencher/example/inputs_3_float/example_3_float_2_cat_in_2_out.py +124 -0
- bencher/example/inputs_3_float/example_3_float_3_cat_in_2_out.py +129 -0
- bencher/example/meta/generate_examples.py +118 -7
- bencher/example/meta/generate_meta.py +88 -40
- bencher/job.py +174 -9
- bencher/plotting/plot_filter.py +52 -17
- bencher/results/bench_result.py +117 -25
- bencher/results/bench_result_base.py +117 -8
- bencher/results/dataset_result.py +6 -200
- bencher/results/explorer_result.py +23 -0
- bencher/results/{hvplot_result.py → histogram_result.py} +3 -18
- bencher/results/holoview_results/__init__.py +0 -0
- bencher/results/holoview_results/bar_result.py +79 -0
- bencher/results/holoview_results/curve_result.py +110 -0
- bencher/results/holoview_results/distribution_result/__init__.py +0 -0
- bencher/results/holoview_results/distribution_result/box_whisker_result.py +73 -0
- bencher/results/holoview_results/distribution_result/distribution_result.py +109 -0
- bencher/results/holoview_results/distribution_result/scatter_jitter_result.py +92 -0
- bencher/results/holoview_results/distribution_result/violin_result.py +70 -0
- bencher/results/holoview_results/heatmap_result.py +319 -0
- bencher/results/holoview_results/holoview_result.py +346 -0
- bencher/results/holoview_results/line_result.py +240 -0
- bencher/results/holoview_results/scatter_result.py +107 -0
- bencher/results/holoview_results/surface_result.py +158 -0
- bencher/results/holoview_results/table_result.py +14 -0
- bencher/results/holoview_results/tabulator_result.py +20 -0
- bencher/results/optuna_result.py +30 -115
- bencher/results/video_controls.py +38 -0
- bencher/results/video_result.py +39 -36
- bencher/results/video_summary.py +2 -2
- bencher/results/{plotly_result.py → volume_result.py} +29 -8
- bencher/utils.py +175 -26
- bencher/variables/inputs.py +122 -15
- bencher/video_writer.py +2 -1
- bencher/worker_job.py +31 -3
- {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/METADATA +24 -24
- holobench-1.43.0.dist-info/RECORD +147 -0
- bencher/example/example_levels2.py +0 -37
- bencher/example/inputs_1D/example_1_in_1_out.py +0 -62
- bencher/example/inputs_1D/example_1_in_2_out.py +0 -63
- bencher/example/inputs_1D/example_1_in_2_out_repeats.py +0 -61
- bencher/results/holoview_result.py +0 -796
- bencher/results/panel_result.py +0 -41
- holobench-1.41.0.dist-info/RECORD +0 -114
- {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/WHEEL +0 -0
- {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/licenses/LICENSE +0 -0
bencher/utils.py
CHANGED
@@ -41,16 +41,22 @@ def make_namedtuple(class_name: str, **fields) -> namedtuple:
|
|
41
41
|
return namedtuple(class_name, fields)(*fields.values())
|
42
42
|
|
43
43
|
|
44
|
-
def get_nearest_coords(dataset: xr.Dataset, collapse_list=False, **kwargs) -> dict:
|
45
|
-
"""
|
44
|
+
def get_nearest_coords(dataset: xr.Dataset, collapse_list: bool = False, **kwargs) -> dict:
|
45
|
+
"""Find the nearest coordinates in an xarray dataset based on provided coordinate values.
|
46
|
+
|
47
|
+
Given an xarray dataset and kwargs of key-value pairs of coordinate values, return a dictionary
|
48
|
+
of the nearest coordinate name-value pair that was found in the dataset.
|
46
49
|
|
47
50
|
Args:
|
48
|
-
|
51
|
+
dataset (xr.Dataset): The xarray dataset to search in
|
52
|
+
collapse_list (bool, optional): If True, when a coordinate value is a list, only the first
|
53
|
+
item is returned. Defaults to False.
|
54
|
+
**kwargs: Key-value pairs where keys are coordinate names and values are points to find
|
55
|
+
the nearest match for
|
49
56
|
|
50
57
|
Returns:
|
51
|
-
dict:
|
58
|
+
dict: Dictionary of coordinate name-value pairs with the nearest values found in the dataset
|
52
59
|
"""
|
53
|
-
|
54
60
|
selection = dataset.sel(method="nearest", **kwargs)
|
55
61
|
cd = selection.coords.to_dataset().to_dict()["coords"]
|
56
62
|
cd2 = {}
|
@@ -61,7 +67,19 @@ def get_nearest_coords(dataset: xr.Dataset, collapse_list=False, **kwargs) -> di
|
|
61
67
|
return cd2
|
62
68
|
|
63
69
|
|
64
|
-
def get_nearest_coords1D(val: Any, coords) -> Any:
|
70
|
+
def get_nearest_coords1D(val: Any, coords: List[Any]) -> Any:
|
71
|
+
"""Find the closest coordinate to a given value in a list of coordinates.
|
72
|
+
|
73
|
+
For numeric values, finds the value in coords that is closest to val.
|
74
|
+
For non-numeric values, returns the exact match if found, otherwise returns val.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
val (Any): The value to find the closest coordinate for
|
78
|
+
coords (List[Any]): The list of coordinates to search in
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
Any: The closest coordinate value from the list
|
82
|
+
"""
|
65
83
|
if isinstance(val, (int, float)):
|
66
84
|
return min(coords, key=lambda x_: abs(x_ - val))
|
67
85
|
for i in coords:
|
@@ -70,19 +88,28 @@ def get_nearest_coords1D(val: Any, coords) -> Any:
|
|
70
88
|
return val
|
71
89
|
|
72
90
|
|
73
|
-
def hash_sha1(var:
|
74
|
-
"""A hash function that avoids the PYTHONHASHSEED 'feature' which returns a different hash value each time the program is run
|
91
|
+
def hash_sha1(var: Any) -> str:
|
92
|
+
"""A hash function that avoids the PYTHONHASHSEED 'feature' which returns a different hash value each time the program is run.
|
93
|
+
|
94
|
+
Converts input to a consistent SHA1 hash string.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
var (Any): The variable to hash
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
str: A hexadecimal SHA1 hash of the string representation of the variable
|
101
|
+
"""
|
75
102
|
return hashlib.sha1(str(var).encode("ASCII")).hexdigest()
|
76
103
|
|
77
104
|
|
78
|
-
def capitalise_words(message: str):
|
79
|
-
"""Given a string of lowercase words, capitalise them
|
105
|
+
def capitalise_words(message: str) -> str:
|
106
|
+
"""Given a string of lowercase words, capitalise them.
|
80
107
|
|
81
108
|
Args:
|
82
109
|
message (str): lower case string
|
83
110
|
|
84
111
|
Returns:
|
85
|
-
|
112
|
+
str: capitalised string where each word starts with an uppercase letter
|
86
113
|
"""
|
87
114
|
capitalized_message = " ".join([word.capitalize() for word in message.split(" ")])
|
88
115
|
return capitalized_message
|
@@ -101,7 +128,16 @@ def un_camel(camel: str) -> str:
|
|
101
128
|
return capitalise_words(re.sub("([a-z])([A-Z])", r"\g<1> \g<2>", camel.replace("_", " ")))
|
102
129
|
|
103
130
|
|
104
|
-
def mult_tuple(inp: Tuple[float], val: float) -> Tuple[float]:
|
131
|
+
def mult_tuple(inp: Tuple[float, ...], val: float) -> Tuple[float, ...]:
|
132
|
+
"""Multiply each element in a tuple by a scalar value.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
inp (Tuple[float, ...]): The input tuple of floats to multiply
|
136
|
+
val (float): The scalar value to multiply each element by
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
Tuple[float, ...]: A new tuple with each element multiplied by val
|
140
|
+
"""
|
105
141
|
return tuple(np.array(inp) * val)
|
106
142
|
|
107
143
|
|
@@ -118,15 +154,17 @@ def tabs_in_markdown(regular_str: str, spaces: int = 2) -> str:
|
|
118
154
|
return regular_str.replace("\t", "".join([" "] * spaces))
|
119
155
|
|
120
156
|
|
121
|
-
def int_to_col(
|
157
|
+
def int_to_col(
|
158
|
+
int_val: int, sat: float = 0.5, val: float = 0.95, alpha: float = -1
|
159
|
+
) -> tuple[float, float, float] | tuple[float, float, float, float]:
|
122
160
|
"""Uses the golden angle to generate colors programmatically with minimum overlap between colors.
|
123
161
|
https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
|
124
162
|
|
125
163
|
Args:
|
126
|
-
int_val (
|
164
|
+
int_val (int): index of an object you want to color, this is mapped to hue in HSV
|
127
165
|
sat (float, optional): saturation in HSV. Defaults to 0.5.
|
128
166
|
val (float, optional): value in HSV. Defaults to 0.95.
|
129
|
-
alpha (
|
167
|
+
alpha (float, optional): transparency. If -1 then only RGB is returned, if 0 or greater, RGBA is returned. Defaults to -1.
|
130
168
|
|
131
169
|
Returns:
|
132
170
|
tuple[float, float, float] | tuple[float, float, float, float]: either RGB or RGBA vector
|
@@ -138,7 +176,23 @@ def int_to_col(int_val, sat=0.5, val=0.95, alpha=-1) -> tuple[float, float, floa
|
|
138
176
|
return rgb
|
139
177
|
|
140
178
|
|
141
|
-
def lerp(
|
179
|
+
def lerp(
|
180
|
+
value: float, input_low: float, input_high: float, output_low: float, output_high: float
|
181
|
+
) -> float:
|
182
|
+
"""Linear interpolation between two ranges.
|
183
|
+
|
184
|
+
Maps a value from one range [input_low, input_high] to another range [output_low, output_high].
|
185
|
+
|
186
|
+
Args:
|
187
|
+
value (float): The input value to interpolate
|
188
|
+
input_low (float): The lower bound of the input range
|
189
|
+
input_high (float): The upper bound of the input range
|
190
|
+
output_low (float): The lower bound of the output range
|
191
|
+
output_high (float): The upper bound of the output range
|
192
|
+
|
193
|
+
Returns:
|
194
|
+
float: The interpolated value in the output range
|
195
|
+
"""
|
142
196
|
input_low = float(input_low)
|
143
197
|
return output_low + ((float(value) - input_low) / (float(input_high) - input_low)) * (
|
144
198
|
float(output_high) - output_low
|
@@ -146,10 +200,26 @@ def lerp(value, input_low: float, input_high: float, output_low: float, output_h
|
|
146
200
|
|
147
201
|
|
148
202
|
def color_tuple_to_css(color: tuple[float, float, float]) -> str:
|
203
|
+
"""Convert a RGB color tuple to CSS rgb format string.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
color (tuple[float, float, float]): RGB color tuple with values in range [0.0, 1.0]
|
207
|
+
|
208
|
+
Returns:
|
209
|
+
str: CSS color string in format 'rgb(r, g, b)' with values in range [0, 255]
|
210
|
+
"""
|
149
211
|
return f"rgb{(color[0] * 255, color[1] * 255, color[2] * 255)}"
|
150
212
|
|
151
213
|
|
152
|
-
def color_tuple_to_255(color: tuple[float, float, float]) -> tuple[
|
214
|
+
def color_tuple_to_255(color: tuple[float, float, float]) -> tuple[int, int, int]:
|
215
|
+
"""Convert a RGB color tuple with values in range [0.0, 1.0] to values in range [0, 255].
|
216
|
+
|
217
|
+
Args:
|
218
|
+
color (tuple[float, float, float]): RGB color tuple with values in range [0.0, 1.0]
|
219
|
+
|
220
|
+
Returns:
|
221
|
+
tuple[int, int, int]: RGB color tuple with values clamped to range [0, 255]
|
222
|
+
"""
|
153
223
|
return (
|
154
224
|
min(int(color[0] * 255), 255),
|
155
225
|
min(int(color[1] * 255), 255),
|
@@ -157,25 +227,76 @@ def color_tuple_to_255(color: tuple[float, float, float]) -> tuple[float, float,
|
|
157
227
|
)
|
158
228
|
|
159
229
|
|
160
|
-
def gen_path(filename, folder="generic", suffix=".dat"):
|
230
|
+
def gen_path(filename: str, folder: str = "generic", suffix: str = ".dat") -> str:
|
231
|
+
"""Generate a unique path for a file in the cache directory.
|
232
|
+
|
233
|
+
Creates a directory structure in the 'cachedir' folder and returns a path
|
234
|
+
with a UUID to ensure uniqueness.
|
235
|
+
|
236
|
+
Args:
|
237
|
+
filename (str): Base name for the file
|
238
|
+
folder (str, optional): Subfolder within cachedir. Defaults to "generic".
|
239
|
+
suffix (str, optional): File extension. Defaults to ".dat".
|
240
|
+
|
241
|
+
Returns:
|
242
|
+
str: Absolute path to a unique file location
|
243
|
+
"""
|
161
244
|
path = Path(f"cachedir/{folder}/{filename}/")
|
162
245
|
path.mkdir(parents=True, exist_ok=True)
|
163
246
|
return f"{path.absolute().as_posix()}/{filename}_{uuid4()}{suffix}"
|
164
247
|
|
165
248
|
|
166
249
|
def gen_video_path(video_name: str = "vid", extension: str = ".mp4") -> str:
|
250
|
+
"""Generate a unique path for a video file in the cache directory.
|
251
|
+
|
252
|
+
Args:
|
253
|
+
video_name (str, optional): Base name for the video file. Defaults to "vid".
|
254
|
+
extension (str, optional): Video file extension. Defaults to ".mp4".
|
255
|
+
|
256
|
+
Returns:
|
257
|
+
str: Absolute path to a unique video file location
|
258
|
+
"""
|
167
259
|
return gen_path(video_name, "vid", extension)
|
168
260
|
|
169
261
|
|
170
|
-
def gen_image_path(image_name: str = "img", filetype=".png") -> str:
|
262
|
+
def gen_image_path(image_name: str = "img", filetype: str = ".png") -> str:
|
263
|
+
"""Generate a unique path for an image file in the cache directory.
|
264
|
+
|
265
|
+
Args:
|
266
|
+
image_name (str, optional): Base name for the image file. Defaults to "img".
|
267
|
+
filetype (str, optional): Image file extension. Defaults to ".png".
|
268
|
+
|
269
|
+
Returns:
|
270
|
+
str: Absolute path to a unique image file location
|
271
|
+
"""
|
171
272
|
return gen_path(image_name, "img", filetype)
|
172
273
|
|
173
274
|
|
174
|
-
def gen_rerun_data_path(rrd_name: str = "rrd", filetype=".rrd") -> str:
|
275
|
+
def gen_rerun_data_path(rrd_name: str = "rrd", filetype: str = ".rrd") -> str:
|
276
|
+
"""Generate a unique path for a rerun data file in the cache directory.
|
277
|
+
|
278
|
+
Args:
|
279
|
+
rrd_name (str, optional): Base name for the rerun data file. Defaults to "rrd".
|
280
|
+
filetype (str, optional): File extension. Defaults to ".rrd".
|
281
|
+
|
282
|
+
Returns:
|
283
|
+
str: Absolute path to a unique rerun data file location
|
284
|
+
"""
|
175
285
|
return gen_path(rrd_name, "rrd", filetype)
|
176
286
|
|
177
287
|
|
178
288
|
def callable_name(any_callable: Callable[..., Any]) -> str:
|
289
|
+
"""Extract the name of a callable object, handling various callable types.
|
290
|
+
|
291
|
+
This function attempts to extract the name of a callable object, including
|
292
|
+
regular functions, partial functions, and other callables.
|
293
|
+
|
294
|
+
Args:
|
295
|
+
any_callable (Callable[..., Any]): The callable object to get the name from
|
296
|
+
|
297
|
+
Returns:
|
298
|
+
str: The name of the callable
|
299
|
+
"""
|
179
300
|
if isinstance(any_callable, partial):
|
180
301
|
return any_callable.func.__name__
|
181
302
|
try:
|
@@ -184,8 +305,20 @@ def callable_name(any_callable: Callable[..., Any]) -> str:
|
|
184
305
|
return str(any_callable)
|
185
306
|
|
186
307
|
|
187
|
-
def listify(obj) ->
|
188
|
-
"""
|
308
|
+
def listify(obj: Any) -> List[Any] | None:
|
309
|
+
"""Convert an object to a list if it's not already a list.
|
310
|
+
|
311
|
+
This function handles conversion of various object types to lists, with special
|
312
|
+
handling for None values and existing list/tuple types.
|
313
|
+
|
314
|
+
Args:
|
315
|
+
obj (Any): The object to convert to a list
|
316
|
+
|
317
|
+
Returns:
|
318
|
+
List[Any] | None: A list containing the object, the object itself if it was
|
319
|
+
already a list, a list from the tuple if it was a tuple, or None if the
|
320
|
+
input was None
|
321
|
+
"""
|
189
322
|
if obj is None:
|
190
323
|
return None
|
191
324
|
if isinstance(obj, list):
|
@@ -195,13 +328,29 @@ def listify(obj) -> list:
|
|
195
328
|
return [obj]
|
196
329
|
|
197
330
|
|
198
|
-
def get_name(var):
|
331
|
+
def get_name(var: Any) -> str:
|
332
|
+
"""Extract the name from a variable, handling param.Parameter objects.
|
333
|
+
|
334
|
+
Args:
|
335
|
+
var (Any): The variable to extract the name from
|
336
|
+
|
337
|
+
Returns:
|
338
|
+
str: The name of the variable
|
339
|
+
"""
|
199
340
|
if isinstance(var, param.Parameter):
|
200
341
|
return var.name
|
201
342
|
return var
|
202
343
|
|
203
344
|
|
204
|
-
def params_to_str(param_list: List[param.Parameter]):
|
345
|
+
def params_to_str(param_list: List[param.Parameter]) -> List[str]:
|
346
|
+
"""Convert a list of param.Parameter objects to a list of their names.
|
347
|
+
|
348
|
+
Args:
|
349
|
+
param_list (List[param.Parameter]): List of parameter objects
|
350
|
+
|
351
|
+
Returns:
|
352
|
+
List[str]: List of parameter names
|
353
|
+
"""
|
205
354
|
return [get_name(i) for i in param_list]
|
206
355
|
|
207
356
|
|
@@ -212,8 +361,8 @@ def publish_file(filepath: str, remote: str, branch_name: str) -> str: # pragma
|
|
212
361
|
|
213
362
|
def publish_args(branch_name) -> Tuple[str, str]:
|
214
363
|
return (
|
215
|
-
"https://github.com/
|
216
|
-
f"https://github.com/
|
364
|
+
"https://github.com/blooop/bencher.git",
|
365
|
+
f"https://github.com/blooop/bencher/blob/{branch_name}")
|
217
366
|
|
218
367
|
|
219
368
|
Args:
|
bencher/variables/inputs.py
CHANGED
@@ -7,7 +7,15 @@ from bencher.variables.sweep_base import SweepBase, shared_slots
|
|
7
7
|
|
8
8
|
|
9
9
|
class SweepSelector(Selector, SweepBase):
|
10
|
-
"""A class
|
10
|
+
"""A class representing a parameter sweep for selectable options.
|
11
|
+
|
12
|
+
This class extends both Selector and SweepBase to provide parameter sweeping
|
13
|
+
capabilities for categorical variables that have a predefined set of options.
|
14
|
+
|
15
|
+
Attributes:
|
16
|
+
units (str): The units of measurement for the parameter
|
17
|
+
samples (int): The number of samples to take from the available options
|
18
|
+
"""
|
11
19
|
|
12
20
|
__slots__ = shared_slots
|
13
21
|
|
@@ -22,14 +30,26 @@ class SweepSelector(Selector, SweepBase):
|
|
22
30
|
self.samples = samples
|
23
31
|
|
24
32
|
def values(self) -> List[Any]:
|
25
|
-
"""
|
33
|
+
"""Return all the values for the parameter sweep.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
List[Any]: A list of parameter values to sweep through
|
37
|
+
"""
|
26
38
|
return self.indices_to_samples(self.samples, self.objects)
|
27
39
|
|
28
40
|
|
29
41
|
class BoolSweep(SweepSelector):
|
30
|
-
"""A class
|
42
|
+
"""A class representing a parameter sweep for boolean values.
|
43
|
+
|
44
|
+
This class extends SweepSelector to provide parameter sweeping capabilities
|
45
|
+
specifically for boolean values (True and False).
|
31
46
|
|
32
|
-
|
47
|
+
Attributes:
|
48
|
+
units (str): The units of measurement for the parameter
|
49
|
+
samples (int): The number of samples to take (typically 2 for booleans)
|
50
|
+
"""
|
51
|
+
|
52
|
+
def __init__(self, units: str = "ul", samples: int = None, default: bool = True, **params):
|
33
53
|
SweepSelector.__init__(
|
34
54
|
self,
|
35
55
|
units=units,
|
@@ -41,7 +61,15 @@ class BoolSweep(SweepSelector):
|
|
41
61
|
|
42
62
|
|
43
63
|
class StringSweep(SweepSelector):
|
44
|
-
"""A class
|
64
|
+
"""A class representing a parameter sweep for string values.
|
65
|
+
|
66
|
+
This class extends SweepSelector to provide parameter sweeping capabilities
|
67
|
+
specifically for a list of string values.
|
68
|
+
|
69
|
+
Attributes:
|
70
|
+
units (str): The units of measurement for the parameter
|
71
|
+
samples (int): The number of samples to take from the available strings
|
72
|
+
"""
|
45
73
|
|
46
74
|
def __init__(
|
47
75
|
self,
|
@@ -61,11 +89,21 @@ class StringSweep(SweepSelector):
|
|
61
89
|
|
62
90
|
|
63
91
|
class EnumSweep(SweepSelector):
|
64
|
-
"""A class
|
92
|
+
"""A class representing a parameter sweep for enum values.
|
93
|
+
|
94
|
+
This class extends SweepSelector to provide parameter sweeping capabilities
|
95
|
+
specifically for enumeration types, supporting both enum types and lists of enum values.
|
96
|
+
|
97
|
+
Attributes:
|
98
|
+
units (str): The units of measurement for the parameter
|
99
|
+
samples (int): The number of samples to take from the available enum values
|
100
|
+
"""
|
65
101
|
|
66
102
|
__slots__ = shared_slots
|
67
103
|
|
68
|
-
def __init__(
|
104
|
+
def __init__(
|
105
|
+
self, enum_type: Enum | List[Enum], units: str = "ul", samples: int = None, **params
|
106
|
+
):
|
69
107
|
# The enum can either be an Enum type or a list of enums
|
70
108
|
list_of_enums = isinstance(enum_type, list)
|
71
109
|
selector_list = enum_type if list_of_enums else list(enum_type)
|
@@ -82,11 +120,23 @@ class EnumSweep(SweepSelector):
|
|
82
120
|
|
83
121
|
|
84
122
|
class IntSweep(Integer, SweepBase):
|
85
|
-
"""A class
|
123
|
+
"""A class representing a parameter sweep for integer values.
|
124
|
+
|
125
|
+
This class extends both Integer and SweepBase to provide parameter sweeping capabilities
|
126
|
+
specifically for integer values within specified bounds or with custom sample values.
|
127
|
+
|
128
|
+
Attributes:
|
129
|
+
units (str): The units of measurement for the parameter
|
130
|
+
samples (int): The number of samples to take from the range
|
131
|
+
sample_values (List[int], optional): Specific integer values to use as samples instead of
|
132
|
+
generating them from bounds. If provided, overrides the samples parameter.
|
133
|
+
"""
|
86
134
|
|
87
135
|
__slots__ = shared_slots + ["sample_values"]
|
88
136
|
|
89
|
-
def __init__(
|
137
|
+
def __init__(
|
138
|
+
self, units: str = "ul", samples: int = None, sample_values: List[int] = None, **params
|
139
|
+
):
|
90
140
|
SweepBase.__init__(self)
|
91
141
|
Integer.__init__(self, **params)
|
92
142
|
|
@@ -107,7 +157,14 @@ class IntSweep(Integer, SweepBase):
|
|
107
157
|
self.default = sample_values[0]
|
108
158
|
|
109
159
|
def values(self) -> List[int]:
|
110
|
-
"""
|
160
|
+
"""Return all the values for the parameter sweep.
|
161
|
+
|
162
|
+
If sample_values is provided, returns those values. Otherwise generates values
|
163
|
+
within the specified bounds.
|
164
|
+
|
165
|
+
Returns:
|
166
|
+
List[int]: A list of integer values to sweep through
|
167
|
+
"""
|
111
168
|
sample_values = (
|
112
169
|
self.sample_values
|
113
170
|
if self.sample_values is not None
|
@@ -136,11 +193,29 @@ class IntSweep(Integer, SweepBase):
|
|
136
193
|
|
137
194
|
|
138
195
|
class FloatSweep(Number, SweepBase):
|
139
|
-
"""A class
|
196
|
+
"""A class representing a parameter sweep for floating point values.
|
197
|
+
|
198
|
+
This class extends both Number and SweepBase to provide parameter sweeping capabilities
|
199
|
+
specifically for floating point values within specified bounds or with custom sample values.
|
200
|
+
|
201
|
+
Attributes:
|
202
|
+
units (str): The units of measurement for the parameter
|
203
|
+
samples (int): The number of samples to take from the range
|
204
|
+
sample_values (List[float], optional): Specific float values to use as samples instead of
|
205
|
+
generating them from bounds. If provided, overrides the samples parameter.
|
206
|
+
step (float, optional): Step size between samples when generating values from bounds
|
207
|
+
"""
|
140
208
|
|
141
209
|
__slots__ = shared_slots + ["sample_values"]
|
142
210
|
|
143
|
-
def __init__(
|
211
|
+
def __init__(
|
212
|
+
self,
|
213
|
+
units: str = "ul",
|
214
|
+
samples: int = 10,
|
215
|
+
sample_values: List[float] = None,
|
216
|
+
step: float = None,
|
217
|
+
**params,
|
218
|
+
):
|
144
219
|
SweepBase.__init__(self)
|
145
220
|
Number.__init__(self, step=step, **params)
|
146
221
|
|
@@ -156,7 +231,14 @@ class FloatSweep(Number, SweepBase):
|
|
156
231
|
self.default = sample_values[0]
|
157
232
|
|
158
233
|
def values(self) -> List[float]:
|
159
|
-
"""
|
234
|
+
"""Return all the values for the parameter sweep.
|
235
|
+
|
236
|
+
If sample_values is provided, returns those values. Otherwise generates values
|
237
|
+
within the specified bounds, either using linspace (when step is None) or arange.
|
238
|
+
|
239
|
+
Returns:
|
240
|
+
List[float]: A list of float values to sweep through
|
241
|
+
"""
|
160
242
|
samps = self.samples
|
161
243
|
if self.sample_values is None:
|
162
244
|
if self.step is None:
|
@@ -166,7 +248,20 @@ class FloatSweep(Number, SweepBase):
|
|
166
248
|
return self.sample_values
|
167
249
|
|
168
250
|
|
169
|
-
def box(name, center, width):
|
251
|
+
def box(name: str, center: float, width: float) -> FloatSweep:
|
252
|
+
"""Create a FloatSweep parameter centered around a value with a given width.
|
253
|
+
|
254
|
+
This is a convenience function to create a bounded FloatSweep parameter with
|
255
|
+
bounds centered on a specific value, extending by the width in both directions.
|
256
|
+
|
257
|
+
Args:
|
258
|
+
name (str): The name of the parameter
|
259
|
+
center (float): The center value of the parameter
|
260
|
+
width (float): The distance from the center to the bounds in both directions
|
261
|
+
|
262
|
+
Returns:
|
263
|
+
FloatSweep: A FloatSweep parameter with the specified name, default, and bounds
|
264
|
+
"""
|
170
265
|
var = FloatSweep(default=center, bounds=(center - width, center + width))
|
171
266
|
var.name = name
|
172
267
|
return var
|
@@ -195,6 +290,18 @@ def p(
|
|
195
290
|
return {"name": name, "values": values, "max_level": max_level, "samples": samples}
|
196
291
|
|
197
292
|
|
198
|
-
def with_level(arr: list, level) -> list:
|
293
|
+
def with_level(arr: list, level: int) -> list:
|
294
|
+
"""Apply level-based sampling to a list of values.
|
295
|
+
|
296
|
+
This function uses an IntSweep with the provided values and applies level-based
|
297
|
+
sampling to it, returning the resulting values.
|
298
|
+
|
299
|
+
Args:
|
300
|
+
arr (list): List of values to sample from
|
301
|
+
level (int): The sampling level to apply (higher levels provide more samples)
|
302
|
+
|
303
|
+
Returns:
|
304
|
+
list: The level-sampled values
|
305
|
+
"""
|
199
306
|
return IntSweep(sample_values=arr).with_level(level).values()
|
200
307
|
# return tmp.with_sample_values(arr).with_level(level).values()
|
bencher/video_writer.py
CHANGED
@@ -88,7 +88,8 @@ class VideoWriter:
|
|
88
88
|
output_path = Path(output_path)
|
89
89
|
|
90
90
|
with moviepy.video.io.VideoFileClip.VideoFileClip(video_path) as video:
|
91
|
-
frame_time = time if time is not None else video.duration
|
91
|
+
frame_time = time if time is not None else video.duration - 2.0 / video.fps
|
92
|
+
frame_time = max(frame_time, 0)
|
92
93
|
frame = video.get_frame(frame_time)
|
93
94
|
Image.fromarray(frame).save(output_path)
|
94
95
|
|
bencher/worker_job.py
CHANGED
@@ -6,8 +6,30 @@ from bencher.utils import hmap_canonical_input
|
|
6
6
|
|
7
7
|
@dataclass
|
8
8
|
class WorkerJob:
|
9
|
-
|
10
|
-
|
9
|
+
"""Represents a benchmark worker job with input variables and caching information.
|
10
|
+
|
11
|
+
This class encapsulates the information needed to execute a benchmark function,
|
12
|
+
including input variables, dimension information, and caching metadata. It handles
|
13
|
+
the preparation of function inputs and calculation of hash signatures for caching.
|
14
|
+
|
15
|
+
Attributes:
|
16
|
+
function_input_vars (List): The values of the input variables to pass to the function
|
17
|
+
index_tuple (Tuple[int]): The indices of these values in the N-dimensional result array
|
18
|
+
dims_name (List[str]): The names of the input dimensions
|
19
|
+
constant_inputs (dict): Dictionary of any constant input values
|
20
|
+
bench_cfg_sample_hash (str): Hash of the benchmark configuration without repeats
|
21
|
+
tag (str): Tag for grouping related jobs
|
22
|
+
function_input (dict): Complete input as a dictionary with dimension names as keys
|
23
|
+
canonical_input (Tuple[Any]): Canonical representation of inputs for caching
|
24
|
+
fn_inputs_sorted (List[Tuple[str, Any]]): Sorted representation of function inputs
|
25
|
+
function_input_signature_pure (str): Hash of the function inputs and tag
|
26
|
+
function_input_signature_benchmark_context (str): Comprehensive hash including benchmark context
|
27
|
+
found_in_cache (bool): Whether this job result was found in cache
|
28
|
+
msgs (List[str]): Messages related to this job's execution
|
29
|
+
"""
|
30
|
+
|
31
|
+
function_input_vars: List[Any]
|
32
|
+
index_tuple: Tuple[int, ...]
|
11
33
|
dims_name: List[str]
|
12
34
|
constant_inputs: dict
|
13
35
|
bench_cfg_sample_hash: str
|
@@ -15,13 +37,19 @@ class WorkerJob:
|
|
15
37
|
|
16
38
|
function_input: dict = None
|
17
39
|
canonical_input: Tuple[Any] = None
|
18
|
-
fn_inputs_sorted: List[str] = None
|
40
|
+
fn_inputs_sorted: List[Tuple[str, Any]] = None
|
19
41
|
function_input_signature_pure: str = None
|
20
42
|
function_input_signature_benchmark_context: str = None
|
21
43
|
found_in_cache: bool = False
|
22
44
|
msgs: List[str] = field(default_factory=list)
|
23
45
|
|
24
46
|
def setup_hashes(self) -> None:
|
47
|
+
"""Set up the function inputs and calculate hash signatures for caching.
|
48
|
+
|
49
|
+
This method prepares the function inputs by combining function input variables
|
50
|
+
with dimensions and constant inputs. It also calculates hash signatures used
|
51
|
+
for caching results and tracking job execution.
|
52
|
+
"""
|
25
53
|
self.function_input = dict(zip(self.dims_name, self.function_input_vars))
|
26
54
|
|
27
55
|
self.canonical_input = hmap_canonical_input(self.function_input)
|