holobench 1.25.2__py3-none-any.whl → 1.27.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/bench_report.py +6 -109
- bencher/example/__init__.py +0 -0
- bencher/example/benchmark_data.py +196 -0
- bencher/example/example_all.py +45 -0
- bencher/example/example_categorical.py +99 -0
- bencher/example/example_composable_container.py +106 -0
- bencher/example/example_composable_container2.py +160 -0
- bencher/example/example_consts.py +39 -0
- bencher/example/example_custom_sweep.py +59 -0
- bencher/example/example_custom_sweep2.py +42 -0
- bencher/example/example_docs.py +34 -0
- bencher/example/example_filepath.py +27 -0
- bencher/example/example_float3D.py +101 -0
- bencher/example/example_float_cat.py +99 -0
- bencher/example/example_floats.py +89 -0
- bencher/example/example_floats2D.py +93 -0
- bencher/example/example_holosweep.py +98 -0
- bencher/example/example_holosweep_objects.py +111 -0
- bencher/example/example_holosweep_tap.py +144 -0
- bencher/example/example_image.py +155 -0
- bencher/example/example_levels.py +181 -0
- bencher/example/example_levels2.py +37 -0
- bencher/example/example_pareto.py +53 -0
- bencher/example/example_sample_cache.py +85 -0
- bencher/example/example_sample_cache_context.py +116 -0
- bencher/example/example_simple.py +134 -0
- bencher/example/example_simple_bool.py +35 -0
- bencher/example/example_simple_cat.py +48 -0
- bencher/example/example_simple_float.py +28 -0
- bencher/example/example_simple_float2d.py +29 -0
- bencher/example/example_strings.py +47 -0
- bencher/example/example_time_event.py +63 -0
- bencher/example/example_video.py +118 -0
- bencher/example/example_workflow.py +189 -0
- bencher/example/experimental/example_bokeh_plotly.py +38 -0
- bencher/example/experimental/example_hover_ex.py +45 -0
- bencher/example/experimental/example_hvplot_explorer.py +39 -0
- bencher/example/experimental/example_interactive.py +75 -0
- bencher/example/experimental/example_streamnd.py +49 -0
- bencher/example/experimental/example_streams.py +36 -0
- bencher/example/experimental/example_template.py +40 -0
- bencher/example/experimental/example_updates.py +84 -0
- bencher/example/experimental/example_vector.py +84 -0
- bencher/example/meta/example_meta.py +171 -0
- bencher/example/meta/example_meta_cat.py +25 -0
- bencher/example/meta/example_meta_float.py +23 -0
- bencher/example/meta/example_meta_levels.py +26 -0
- bencher/example/optuna/example_optuna.py +78 -0
- bencher/example/shelved/example_float2D_scatter.py +109 -0
- bencher/example/shelved/example_float3D_cone.py +96 -0
- bencher/example/shelved/example_kwargs.py +63 -0
- bencher/plotting/__init__.py +0 -0
- bencher/plotting/plot_filter.py +110 -0
- bencher/plotting/plt_cnt_cfg.py +75 -0
- bencher/results/__init__.py +0 -0
- bencher/results/bench_result.py +94 -0
- bencher/results/bench_result_base.py +476 -0
- bencher/results/composable_container/__init__.py +0 -0
- bencher/results/composable_container/composable_container_base.py +73 -0
- bencher/results/composable_container/composable_container_panel.py +39 -0
- bencher/results/composable_container/composable_container_video.py +184 -0
- bencher/results/float_formatter.py +44 -0
- bencher/results/holoview_result.py +753 -0
- bencher/results/optuna_result.py +354 -0
- bencher/results/panel_result.py +41 -0
- bencher/results/plotly_result.py +65 -0
- bencher/results/video_result.py +38 -0
- bencher/results/video_summary.py +222 -0
- bencher/variables/__init__.py +0 -0
- bencher/variables/inputs.py +202 -0
- bencher/variables/parametrised_sweep.py +208 -0
- bencher/variables/results.py +214 -0
- bencher/variables/sweep_base.py +162 -0
- bencher/variables/time.py +92 -0
- holobench-1.27.0.data/data/share/ament_index/resource_index/packages/bencher +0 -0
- holobench-1.27.0.data/data/share/bencher/package.xml +33 -0
- {holobench-1.25.2.dist-info → holobench-1.27.0.dist-info}/METADATA +5 -5
- holobench-1.27.0.dist-info/RECORD +93 -0
- holobench-1.25.2.dist-info/RECORD +0 -18
- {holobench-1.25.2.dist-info → holobench-1.27.0.dist-info}/LICENSE +0 -0
- {holobench-1.25.2.dist-info → holobench-1.27.0.dist-info}/WHEEL +0 -0
- {holobench-1.25.2.dist-info → holobench-1.27.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
import bencher as bch
|
2
|
+
import numpy as np
|
3
|
+
import optuna
|
4
|
+
from optuna.samplers import TPESampler
|
5
|
+
|
6
|
+
|
7
|
+
def objective(trial):
|
8
|
+
x = trial.suggest_float("x", -10, 10)
|
9
|
+
return x**2
|
10
|
+
|
11
|
+
|
12
|
+
study = optuna.create_study(sampler=TPESampler())
|
13
|
+
study.optimize(objective, n_trials=10)
|
14
|
+
|
15
|
+
rast_range = 1.5
|
16
|
+
|
17
|
+
optimal_value = 0.1234
|
18
|
+
|
19
|
+
|
20
|
+
class ToyOptimisationProblem(bch.ParametrizedSweep):
|
21
|
+
input1 = bch.FloatSweep(default=0, bounds=[-rast_range, rast_range], samples=10)
|
22
|
+
input2 = bch.FloatSweep(default=0, bounds=[-rast_range, rast_range], samples=10)
|
23
|
+
offset = bch.FloatSweep(default=0, bounds=[-rast_range, rast_range])
|
24
|
+
|
25
|
+
bump_scale = bch.FloatSweep(default=1.5, bounds=[1, 10])
|
26
|
+
|
27
|
+
# RESULTS
|
28
|
+
output = bch.ResultVar("ul", bch.OptDir.minimize)
|
29
|
+
|
30
|
+
def rastrigin(self, **kwargs) -> dict:
|
31
|
+
"""A modified version of the rastrigin function which is very difficult to find the global optimum
|
32
|
+
https://en.wikipedia.org/wiki/Rastrigin_function
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
dict: dictionary of return values
|
36
|
+
"""
|
37
|
+
self.update_params_from_kwargs(**kwargs)
|
38
|
+
x = np.array([self.input1 + optimal_value, self.input2 + optimal_value])
|
39
|
+
|
40
|
+
self.output = (
|
41
|
+
np.sum(x * x - self.bump_scale * np.cos(self.bump_scale * np.pi * x))
|
42
|
+
+ self.bump_scale * np.size(x)
|
43
|
+
+ self.offset
|
44
|
+
)
|
45
|
+
return self.get_results_values_as_dict()
|
46
|
+
|
47
|
+
|
48
|
+
def optuna_rastrigin(
|
49
|
+
run_cfg: bch.BenchRunCfg = bch.BenchRunCfg(), report: bch.BenchReport = bch.BenchReport()
|
50
|
+
):
|
51
|
+
explorer = ToyOptimisationProblem()
|
52
|
+
|
53
|
+
bench = bch.Bench("Rastrigin", explorer.rastrigin, run_cfg=run_cfg, report=report)
|
54
|
+
|
55
|
+
# res = bench.to_optuna(
|
56
|
+
# input_vars=[explorer.param.input1, explorer.param.input2],
|
57
|
+
# result_vars=[explorer.param.output],
|
58
|
+
# n_trials=30
|
59
|
+
# )
|
60
|
+
|
61
|
+
# run_cfg.use_optuna = True
|
62
|
+
res = bench.plot_sweep(
|
63
|
+
"Rastrigin",
|
64
|
+
input_vars=[explorer.param.input1, explorer.param.input2],
|
65
|
+
result_vars=[explorer.param.output],
|
66
|
+
run_cfg=run_cfg,
|
67
|
+
)
|
68
|
+
|
69
|
+
bench.report.append(res.to_optuna_plots())
|
70
|
+
bench.report.append(res.to_optuna_from_sweep(bench))
|
71
|
+
bench.report.append_markdown(
|
72
|
+
f"The optimal value should be input1:{-optimal_value},input2:{-optimal_value} with a value of 0"
|
73
|
+
)
|
74
|
+
return bench
|
75
|
+
|
76
|
+
|
77
|
+
if __name__ == "__main__":
|
78
|
+
optuna_rastrigin().report.show()
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# import random
|
2
|
+
|
3
|
+
# import bencher as bch
|
4
|
+
|
5
|
+
|
6
|
+
# class GaussianDist(bch.ParametrizedSweep):
|
7
|
+
# """A class to represent a gaussian distribution."""
|
8
|
+
|
9
|
+
# mean = bch.FloatSweep(
|
10
|
+
# default=0, bounds=[-1.0, 1.0], doc="mean of the gaussian distribution", samples=3
|
11
|
+
# )
|
12
|
+
# sigma = bch.FloatSweep(
|
13
|
+
# default=1, bounds=[0, 1.0], doc="standard deviation of gaussian distribution", samples=4
|
14
|
+
# )
|
15
|
+
|
16
|
+
|
17
|
+
# class Example2DGaussianResult(bch.ParametrizedSweep):
|
18
|
+
# """A class to represent the properties of a volume sample."""
|
19
|
+
|
20
|
+
# gauss_x = bch.ResultVar("m", doc="x value of the 2D gaussian")
|
21
|
+
# gauss_y = bch.ResultVar("m", doc="y value of the 2D gaussian")
|
22
|
+
|
23
|
+
# point2D = bch.ResultVec(2, "m", doc="2D vector of the point")
|
24
|
+
|
25
|
+
|
26
|
+
# def bench_fn(dist: GaussianDist) -> Example2DGaussianResult:
|
27
|
+
# """This function samples a point from a gaussian distribution.
|
28
|
+
|
29
|
+
# Args:
|
30
|
+
# dist (GaussianDist): Sample point
|
31
|
+
|
32
|
+
# Returns:
|
33
|
+
# Example2DGaussianResult: Value at that point
|
34
|
+
# """
|
35
|
+
# output = Example2DGaussianResult()
|
36
|
+
|
37
|
+
# output.gauss_x = random.gauss(dist.mean, dist.sigma)
|
38
|
+
# output.gauss_y = random.gauss(dist.mean, dist.sigma)
|
39
|
+
# output.point2D = [output.gauss_x, output.gauss_y]
|
40
|
+
|
41
|
+
# return output
|
42
|
+
|
43
|
+
|
44
|
+
# def example_floats2D_scatter(run_cfg: bch.BenchRunCfg) -> bch.Bench:
|
45
|
+
# """Example of how to perform a 3D floating point parameter sweep
|
46
|
+
|
47
|
+
# Args:
|
48
|
+
# run_cfg (BenchRunCfg): configuration of how to perform the param sweep
|
49
|
+
|
50
|
+
# Returns:
|
51
|
+
# Bench: results of the parameter sweep
|
52
|
+
# """
|
53
|
+
# bench = bch.Bench(
|
54
|
+
# "Bencher_Example_Floats_Scatter", bench_fn, GaussianDist, plot_lib=bch.PlotLibrary.default()
|
55
|
+
# )
|
56
|
+
|
57
|
+
# bench.plot_sweep(
|
58
|
+
# result_vars=[
|
59
|
+
# Example2DGaussianResult.param.point2D,
|
60
|
+
# Example2DGaussianResult.param.gauss_x,
|
61
|
+
# Example2DGaussianResult.param.gauss_y,
|
62
|
+
# ],
|
63
|
+
# title="Float 2D Scatter Example",
|
64
|
+
# run_cfg=run_cfg,
|
65
|
+
# )
|
66
|
+
|
67
|
+
# bench.plot_sweep(
|
68
|
+
# input_vars=[GaussianDist.param.mean],
|
69
|
+
# result_vars=[
|
70
|
+
# Example2DGaussianResult.param.point2D,
|
71
|
+
# Example2DGaussianResult.param.gauss_x,
|
72
|
+
# Example2DGaussianResult.param.gauss_y,
|
73
|
+
# ],
|
74
|
+
# title="Float 2D Scatter With Changing Mean",
|
75
|
+
# run_cfg=run_cfg,
|
76
|
+
# )
|
77
|
+
|
78
|
+
# bench.plot_sweep(
|
79
|
+
# input_vars=[GaussianDist.param.sigma],
|
80
|
+
# result_vars=[
|
81
|
+
# Example2DGaussianResult.param.point2D,
|
82
|
+
# Example2DGaussianResult.param.gauss_x,
|
83
|
+
# Example2DGaussianResult.param.gauss_y,
|
84
|
+
# ],
|
85
|
+
# title="Float 2D Scatter With Changing Sigma",
|
86
|
+
# run_cfg=run_cfg,
|
87
|
+
# )
|
88
|
+
|
89
|
+
# # future work
|
90
|
+
# # bench.plot_sweep(
|
91
|
+
# # input_vars=[GaussianDist.param.mean, GaussianDist.param.sigma],
|
92
|
+
# # result_vars=[
|
93
|
+
# # GaussianResult.param.point2D,
|
94
|
+
# # GaussianResult.param.gauss_x,
|
95
|
+
# # GaussianResult.param.gauss_y,
|
96
|
+
# # ],
|
97
|
+
# # title="Float 2D Scatter With Changing Sigma",
|
98
|
+
# # run_cfg=run_cfg,
|
99
|
+
# # )
|
100
|
+
|
101
|
+
# return bench
|
102
|
+
|
103
|
+
|
104
|
+
# if __name__ == "__main__":
|
105
|
+
# ex_run_cfg = bch.BenchRunCfg()
|
106
|
+
# ex_run_cfg.repeats = 50
|
107
|
+
# ex_run_cfg.over_time = True
|
108
|
+
# # ex_run_cfg.clear_history = True
|
109
|
+
# example_floats2D_scatter(ex_run_cfg).report.show()
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# import numpy as np
|
2
|
+
|
3
|
+
# import bencher as bch
|
4
|
+
|
5
|
+
|
6
|
+
# class VolumeSample(bch.ParametrizedSweep):
|
7
|
+
# """A class to represent a 3D point in space."""
|
8
|
+
|
9
|
+
# pos_samples = 1
|
10
|
+
|
11
|
+
# x = bch.FloatSweep(
|
12
|
+
# default=0, bounds=[-1.0, 1.0], samples=pos_samples, doc="x coordinate of the sample volume"
|
13
|
+
# )
|
14
|
+
# y = bch.FloatSweep(
|
15
|
+
# default=0, bounds=[-1.0, 1.0], samples=pos_samples, doc="y coordinate of the sample volume"
|
16
|
+
# )
|
17
|
+
# z = bch.FloatSweep(
|
18
|
+
# default=0, bounds=[-1.0, 1.0], samples=pos_samples, doc="z coordinate of the sample volume"
|
19
|
+
# )
|
20
|
+
|
21
|
+
# vec_samples = 5
|
22
|
+
|
23
|
+
# vec_x = bch.FloatSweep(
|
24
|
+
# default=0, bounds=[-1.0, 1.0], samples=vec_samples, doc="x coordinate of the sample volume"
|
25
|
+
# )
|
26
|
+
# vec_y = bch.FloatSweep(
|
27
|
+
# default=0, bounds=[-1.0, 1.0], samples=vec_samples, doc="y coordinate of the sample volume"
|
28
|
+
# )
|
29
|
+
# vec_z = bch.FloatSweep(
|
30
|
+
# default=0, bounds=[-1.0, 1.0], samples=vec_samples, doc="z coordinate of the sample volume"
|
31
|
+
# )
|
32
|
+
|
33
|
+
|
34
|
+
# class VolumeResult(bch.ParametrizedSweep):
|
35
|
+
# """A class to represent the properties of a volume sample."""
|
36
|
+
|
37
|
+
# vec_dir = bch.ResultVec(3, "vec", doc="A vector field with an interesting shape")
|
38
|
+
|
39
|
+
|
40
|
+
# def bench_fn(point: VolumeSample, normalise=False) -> VolumeResult:
|
41
|
+
# """This function takes a 3D point as input and returns distance of that point to the origin.
|
42
|
+
|
43
|
+
# Args:
|
44
|
+
# point (VolumeSample): Sample point
|
45
|
+
|
46
|
+
# Returns:
|
47
|
+
# VolumeResult: Value at that point
|
48
|
+
# """
|
49
|
+
# output = VolumeResult()
|
50
|
+
|
51
|
+
# vec = np.array([point.vec_x, point.vec_y, point.vec_z])
|
52
|
+
|
53
|
+
# if normalise:
|
54
|
+
# norm = np.linalg.norm(vec)
|
55
|
+
# if norm > 0:
|
56
|
+
# vec /= norm
|
57
|
+
|
58
|
+
# output.vec_dir = list(vec)
|
59
|
+
# return output
|
60
|
+
|
61
|
+
|
62
|
+
# def example_cone(run_cfg: bch.BenchRunCfg) -> bch.Bench:
|
63
|
+
# """Example of how to perform a 3D floating point parameter sweep
|
64
|
+
|
65
|
+
# Args:
|
66
|
+
# run_cfg (BenchRunCfg): configuration of how to perform the param sweep
|
67
|
+
|
68
|
+
# Returns:
|
69
|
+
# Bench: results of the parameter sweep
|
70
|
+
# """
|
71
|
+
# bench = bch.Bench("Bencher_Example_Cone", bench_fn, VolumeSample)
|
72
|
+
|
73
|
+
# bench.plot_sweep(
|
74
|
+
# input_vars=[
|
75
|
+
# VolumeSample.param.x,
|
76
|
+
# VolumeSample.param.y,
|
77
|
+
# VolumeSample.param.z,
|
78
|
+
# VolumeSample.param.vec_x,
|
79
|
+
# VolumeSample.param.vec_y,
|
80
|
+
# VolumeSample.param.vec_z,
|
81
|
+
# ],
|
82
|
+
# result_vars=[
|
83
|
+
# VolumeResult.param.vec_dir,
|
84
|
+
# ],
|
85
|
+
# title="Float 3D cone Example",
|
86
|
+
# description="""This example shows how to sample 3 floating point variables and plot a vector field representation of the results. The benchmark function returns the distance to the origin""",
|
87
|
+
# run_cfg=run_cfg,
|
88
|
+
# )
|
89
|
+
|
90
|
+
# return bench
|
91
|
+
|
92
|
+
|
93
|
+
# if __name__ == "__main__":
|
94
|
+
# ex_run_cfg = bch.BenchRunCfg()
|
95
|
+
# ex_run_cfg.use_cache = True
|
96
|
+
# example_cone(ex_run_cfg).report.show()
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# import math
|
2
|
+
|
3
|
+
# import bencher as bch
|
4
|
+
|
5
|
+
|
6
|
+
# def bench_function(
|
7
|
+
# theta: float = 0,
|
8
|
+
# offset: float = 0,
|
9
|
+
# scale: float = 1.0,
|
10
|
+
# trig_func: str = "sin",
|
11
|
+
# **kwargs, # pylint: disable=unused-argument
|
12
|
+
# ) -> dict:
|
13
|
+
# """All the other examples use classes and parameters to define the inputs and outputs to the function. However it makes the code less flexible when integrating with other systems, so this example shows a more basic interface that accepts and returns dictionaries. The classes still need to be defined however because that is how the sweep and plotting settings are calcuated"""
|
14
|
+
# output = {}
|
15
|
+
|
16
|
+
# if trig_func == "sin":
|
17
|
+
# output["voltage"] = offset + math.sin(theta) * scale
|
18
|
+
# elif trig_func == "cos":
|
19
|
+
# output["voltage"] = offset + math.cos(theta) * scale
|
20
|
+
|
21
|
+
# return output
|
22
|
+
|
23
|
+
|
24
|
+
# class InputCfg(bch.ParametrizedSweep):
|
25
|
+
# """This class is used to define the default values and bounds of the variables to benchmark."""
|
26
|
+
|
27
|
+
# theta = bch.FloatSweep(
|
28
|
+
# default=0.0,
|
29
|
+
# bounds=[0.0, 6.0],
|
30
|
+
# doc="Input angle to the trig function",
|
31
|
+
# units="rad",
|
32
|
+
# samples=10,
|
33
|
+
# )
|
34
|
+
|
35
|
+
# offset = bch.FloatSweep(
|
36
|
+
# default=0.0,
|
37
|
+
# bounds=[0.0, 3.0],
|
38
|
+
# doc="Add an offset voltage to the result of the trig function",
|
39
|
+
# units="v",
|
40
|
+
# samples=5,
|
41
|
+
# )
|
42
|
+
|
43
|
+
# trig_func = bch.StringSweep(["sin", "cos"], doc="Select what trigonometric function use")
|
44
|
+
|
45
|
+
|
46
|
+
# class OutputVoltage(bch.ParametrizedSweep):
|
47
|
+
# voltage = bch.ResultVar(units="v", doc="Output voltage")
|
48
|
+
|
49
|
+
|
50
|
+
# if __name__ == "__main__":
|
51
|
+
# # pass the objective function you have defined to bencher. The other examples pass the InputCfg type, but this benchmark function accepts a kwargs dictionary so you don't need to pass the inputCfg type.
|
52
|
+
# bench = bch.Bench("Bencher_Example_Categorical", bench_function)
|
53
|
+
|
54
|
+
# # Bencher needs to know the metadata of the variable in order to automatically sweep and plot it, so it is passed by using param's metadata syntax. InputCfg.param.* is how to access the metadata defined in the class description.
|
55
|
+
# bench.plot_sweep(
|
56
|
+
# input_vars=[InputCfg.param.theta, InputCfg.param.offset],
|
57
|
+
# result_vars=[OutputVoltage.param.voltage],
|
58
|
+
# title="Example with kwarg inputs and dict output",
|
59
|
+
# description=bench_function.__doc__,
|
60
|
+
# )
|
61
|
+
|
62
|
+
# # launch web server and view
|
63
|
+
# bench.report.show()
|
File without changes
|
@@ -0,0 +1,110 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Optional
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from bencher.plotting.plt_cnt_cfg import PltCntCfg
|
5
|
+
import panel as pn
|
6
|
+
|
7
|
+
|
8
|
+
class VarRange:
|
9
|
+
"""A VarRange represents the bounded and unbounded ranges of integers. This class is used to define filters for various variable types. For example by defining cat_var = VarRange(0,0), calling matches(0) will return true, but any other integer will not match. You can also have unbounded ranges for example VarRange(2,None) will match to 2,3,4... up to infinity. for By default the lower and upper bounds are set to -1 so so that no matter what value is passsed to matches() will return false. Matches only takes 0 and positive integers."""
|
10
|
+
|
11
|
+
def __init__(self, lower_bound: int = 0, upper_bound: int = -1) -> None:
|
12
|
+
"""
|
13
|
+
Args:
|
14
|
+
lower_bound (int, optional): The smallest acceptable value to matches(). Passing None will result in a lower bound of 0 (as matches only accepts positive integers). Defaults to 0.
|
15
|
+
upper_bound (int, optional): The largest acceptable value to matches(). Passing None will result in no upper bound. Defaults to -1 which results in a range with no matches.
|
16
|
+
"""
|
17
|
+
self.lower_bound = lower_bound
|
18
|
+
self.upper_bound = upper_bound
|
19
|
+
|
20
|
+
def matches(self, val: int) -> bool:
|
21
|
+
"""Checks that a value is within the variable range. lower_bound and upper_bound are inclusive (lower_bound<=val<=upper_bound )
|
22
|
+
|
23
|
+
Args:
|
24
|
+
val (int): A positive integer representing a number of items
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
bool: True if the items is within the range, False otherwise.
|
28
|
+
|
29
|
+
Raises:
|
30
|
+
ValueError: If val < 0
|
31
|
+
"""
|
32
|
+
if val < 0:
|
33
|
+
raise ValueError("val must be >= 0")
|
34
|
+
if self.lower_bound is not None:
|
35
|
+
lower_match = val >= self.lower_bound
|
36
|
+
else:
|
37
|
+
lower_match = True
|
38
|
+
|
39
|
+
if self.upper_bound is not None:
|
40
|
+
upper_match = val <= self.upper_bound
|
41
|
+
else:
|
42
|
+
upper_match = True
|
43
|
+
|
44
|
+
return lower_match and upper_match
|
45
|
+
|
46
|
+
def matches_info(self, val, name):
|
47
|
+
match = self.matches(val)
|
48
|
+
info = f"{name}\t{match}\t{self.lower_bound}>= {val} <={self.upper_bound}"
|
49
|
+
return match, info
|
50
|
+
|
51
|
+
def __str__(self) -> str:
|
52
|
+
return f"VarRange(lower_bound={self.lower_bound}, upper_bound={self.upper_bound})"
|
53
|
+
|
54
|
+
|
55
|
+
@dataclass
|
56
|
+
class PlotFilter:
|
57
|
+
"""A class for representing the types of results a plot is able to represent."""
|
58
|
+
|
59
|
+
float_range: VarRange = VarRange()
|
60
|
+
cat_range: VarRange = VarRange()
|
61
|
+
vector_len: VarRange = VarRange(1, 1)
|
62
|
+
result_vars: VarRange = VarRange(1, 1)
|
63
|
+
panel_range: VarRange = VarRange(0, 0)
|
64
|
+
repeats_range: VarRange = VarRange(1, None)
|
65
|
+
input_range: VarRange = VarRange(1, None)
|
66
|
+
|
67
|
+
def matches_result(self, plt_cnt_cfg: PltCntCfg, plot_name: str) -> PlotMatchesResult:
|
68
|
+
"""Checks if the result data signature matches the type of data the plot is able to display."""
|
69
|
+
return PlotMatchesResult(self, plt_cnt_cfg, plot_name)
|
70
|
+
|
71
|
+
|
72
|
+
# @dataclass
|
73
|
+
class PlotMatchesResult:
|
74
|
+
"""Stores information about which properites match the requirements of a particular plotter"""
|
75
|
+
|
76
|
+
def __init__(self, plot_filter: PlotFilter, plt_cnt_cfg: PltCntCfg, plot_name: str):
|
77
|
+
match_info = []
|
78
|
+
matches = []
|
79
|
+
|
80
|
+
match_candidates = [
|
81
|
+
(plot_filter.float_range, plt_cnt_cfg.float_cnt, "float"),
|
82
|
+
(plot_filter.cat_range, plt_cnt_cfg.cat_cnt, "cat"),
|
83
|
+
(plot_filter.vector_len, plt_cnt_cfg.vector_len, "vec"),
|
84
|
+
(plot_filter.result_vars, plt_cnt_cfg.result_vars, "results"),
|
85
|
+
(plot_filter.panel_range, plt_cnt_cfg.panel_cnt, "panels"),
|
86
|
+
(plot_filter.repeats_range, plt_cnt_cfg.repeats, "repeats"),
|
87
|
+
(plot_filter.input_range, plt_cnt_cfg.inputs_cnt, "inputs"),
|
88
|
+
]
|
89
|
+
|
90
|
+
for m, cnt, name in match_candidates:
|
91
|
+
match, info = m.matches_info(cnt, name)
|
92
|
+
matches.append(match)
|
93
|
+
if not match:
|
94
|
+
match_info.append(info)
|
95
|
+
|
96
|
+
self.overall = all(matches)
|
97
|
+
|
98
|
+
match_info.insert(0, f"plot {plot_name} matches: {self.overall}")
|
99
|
+
self.matches_info = "\n".join(match_info).strip()
|
100
|
+
self.plt_cnt_cfg = plt_cnt_cfg
|
101
|
+
|
102
|
+
if self.plt_cnt_cfg.print_debug:
|
103
|
+
print(f"checking {plot_name} result: {self.overall}")
|
104
|
+
if not self.overall:
|
105
|
+
print(self.matches_info)
|
106
|
+
|
107
|
+
def to_panel(self, **kwargs) -> Optional[pn.pane.Markdown]:
|
108
|
+
if self.plt_cnt_cfg.print_debug:
|
109
|
+
return pn.pane.Markdown(self.matches_info, **kwargs)
|
110
|
+
return None
|
@@ -0,0 +1,75 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import param
|
3
|
+
from bencher.bench_cfg import BenchCfg
|
4
|
+
from bencher.variables.results import PANEL_TYPES
|
5
|
+
|
6
|
+
from bencher.variables.inputs import IntSweep, FloatSweep, BoolSweep, EnumSweep, StringSweep
|
7
|
+
from bencher.variables.time import TimeSnapshot
|
8
|
+
|
9
|
+
|
10
|
+
class PltCntCfg(param.Parameterized):
|
11
|
+
"""Plot Count Config"""
|
12
|
+
|
13
|
+
float_vars = param.List(doc="A list of float vars in order of plotting, x then y")
|
14
|
+
float_cnt = param.Integer(0, doc="The number of float variables to plot")
|
15
|
+
cat_vars = param.List(doc="A list of categorical values to plot in order hue,row,col")
|
16
|
+
cat_cnt = param.Integer(0, doc="The number of cat variables")
|
17
|
+
vector_len = param.Integer(1, doc="The vector length of the return variable , scalars = len 1")
|
18
|
+
result_vars = param.Integer(1, doc="The number result variables to plot") # todo remove
|
19
|
+
panel_vars = param.List(doc="A list of panel results")
|
20
|
+
panel_cnt = param.Integer(0, doc="Number of results reprented as panel panes")
|
21
|
+
repeats = param.Integer(0, doc="The number of repeat samples")
|
22
|
+
inputs_cnt = param.Integer(0, doc="The number of repeat samples")
|
23
|
+
|
24
|
+
print_debug = param.Boolean(
|
25
|
+
True, doc="Print debug information about why a filter matches this config or not"
|
26
|
+
)
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def generate_plt_cnt_cfg(
|
30
|
+
bench_cfg: BenchCfg,
|
31
|
+
) -> PltCntCfg:
|
32
|
+
"""Given a BenchCfg work out how many float and cat variables there are and store in a PltCntCfg class
|
33
|
+
|
34
|
+
Args:
|
35
|
+
bench_cfg (BenchCfg): See BenchCfg definition
|
36
|
+
|
37
|
+
Raises:
|
38
|
+
ValueError: If no plotting procedure could be automatically detected
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
PltCntCfg: see PltCntCfg definition
|
42
|
+
"""
|
43
|
+
plt_cnt_cfg = PltCntCfg()
|
44
|
+
# plt_cnt_cfg.float_vars = deepcopy(bench_cfg.iv_time)
|
45
|
+
|
46
|
+
plt_cnt_cfg.cat_vars = []
|
47
|
+
plt_cnt_cfg.float_vars = []
|
48
|
+
|
49
|
+
for iv in bench_cfg.input_vars:
|
50
|
+
type_allocated = False
|
51
|
+
if isinstance(iv, (IntSweep, FloatSweep, TimeSnapshot)):
|
52
|
+
# if "IntSweep" in typestr or "FloatSweep" in typestr:
|
53
|
+
plt_cnt_cfg.float_vars.append(iv)
|
54
|
+
type_allocated = True
|
55
|
+
if isinstance(iv, (EnumSweep, BoolSweep, StringSweep)):
|
56
|
+
# if "EnumSweep" in typestr or "BoolSweep" in typestr or "StringSweep" in typestr:
|
57
|
+
plt_cnt_cfg.cat_vars.append(iv)
|
58
|
+
type_allocated = True
|
59
|
+
|
60
|
+
if not type_allocated:
|
61
|
+
raise ValueError(f"No rule for type {type(iv)}")
|
62
|
+
|
63
|
+
for rv in bench_cfg.result_vars:
|
64
|
+
if isinstance(rv, PANEL_TYPES):
|
65
|
+
plt_cnt_cfg.panel_vars.append(rv)
|
66
|
+
|
67
|
+
plt_cnt_cfg.float_cnt = len(plt_cnt_cfg.float_vars)
|
68
|
+
plt_cnt_cfg.cat_cnt = len(plt_cnt_cfg.cat_vars)
|
69
|
+
plt_cnt_cfg.panel_cnt = len(plt_cnt_cfg.panel_vars)
|
70
|
+
plt_cnt_cfg.repeats = bench_cfg.repeats
|
71
|
+
plt_cnt_cfg.inputs_cnt = len(bench_cfg.input_vars)
|
72
|
+
return plt_cnt_cfg
|
73
|
+
|
74
|
+
def __str__(self):
|
75
|
+
return f"float_cnt: {self.float_cnt}\ncat_cnt: {self.cat_cnt} \npanel_cnt: {self.panel_cnt}\nvector_len: {self.vector_len}"
|
File without changes
|
@@ -0,0 +1,94 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import List
|
3
|
+
import panel as pn
|
4
|
+
|
5
|
+
from bencher.results.bench_result_base import EmptyContainer
|
6
|
+
from bencher.results.video_summary import VideoSummaryResult
|
7
|
+
from bencher.results.panel_result import PanelResult
|
8
|
+
from bencher.results.plotly_result import PlotlyResult
|
9
|
+
from bencher.results.holoview_result import HoloviewResult
|
10
|
+
from bencher.utils import listify
|
11
|
+
|
12
|
+
|
13
|
+
class BenchResult(PlotlyResult, HoloviewResult, VideoSummaryResult):
|
14
|
+
"""Contains the results of the benchmark and has methods to cast the results to various datatypes and graphical representations"""
|
15
|
+
|
16
|
+
def __init__(self, bench_cfg) -> None:
|
17
|
+
PlotlyResult.__init__(self, bench_cfg)
|
18
|
+
HoloviewResult.__init__(self, bench_cfg)
|
19
|
+
|
20
|
+
@staticmethod
|
21
|
+
def default_plot_callbacks():
|
22
|
+
return [
|
23
|
+
# VideoSummaryResult.to_video_summary, #quite expensive so not turned on by default
|
24
|
+
HoloviewResult.to_bar,
|
25
|
+
HoloviewResult.to_scatter_jitter,
|
26
|
+
HoloviewResult.to_curve,
|
27
|
+
HoloviewResult.to_line,
|
28
|
+
HoloviewResult.to_heatmap,
|
29
|
+
PlotlyResult.to_volume,
|
30
|
+
# PanelResult.to_video,
|
31
|
+
PanelResult.to_panes,
|
32
|
+
]
|
33
|
+
|
34
|
+
@staticmethod
|
35
|
+
def plotly_callbacks():
|
36
|
+
return [HoloviewResult.to_surface, PlotlyResult.to_volume]
|
37
|
+
|
38
|
+
def plot(self) -> pn.panel:
|
39
|
+
"""Plots the benchresult using the plot callbacks defined by the bench run
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
pn.panel: A panel representation of the results
|
43
|
+
"""
|
44
|
+
if self.bench_cfg.plot_callbacks is not None:
|
45
|
+
return pn.Column(*[cb(self) for cb in self.bench_cfg.plot_callbacks])
|
46
|
+
return None
|
47
|
+
|
48
|
+
def to_auto(
|
49
|
+
self,
|
50
|
+
plot_list: List[callable] = None,
|
51
|
+
remove_plots: List[callable] = None,
|
52
|
+
**kwargs,
|
53
|
+
) -> List[pn.panel]:
|
54
|
+
self.plt_cnt_cfg.print_debug = False
|
55
|
+
plot_list = listify(plot_list)
|
56
|
+
remove_plots = listify(remove_plots)
|
57
|
+
|
58
|
+
if plot_list is None:
|
59
|
+
plot_list = BenchResult.default_plot_callbacks()
|
60
|
+
if remove_plots is not None:
|
61
|
+
for p in remove_plots:
|
62
|
+
plot_list.remove(p)
|
63
|
+
|
64
|
+
kwargs = self.set_plot_size(**kwargs)
|
65
|
+
|
66
|
+
row = EmptyContainer(pn.Row())
|
67
|
+
for plot_callback in plot_list:
|
68
|
+
if self.plt_cnt_cfg.print_debug:
|
69
|
+
print(f"checking: {plot_callback.__name__}")
|
70
|
+
# the callbacks are passed from the static class definition, so self needs to be passed before the plotting callback can be called
|
71
|
+
row.append(plot_callback(self, **kwargs))
|
72
|
+
|
73
|
+
self.plt_cnt_cfg.print_debug = True
|
74
|
+
if len(row.pane) == 0:
|
75
|
+
row.append(
|
76
|
+
pn.pane.Markdown("No Plotters are able to represent these results", **kwargs)
|
77
|
+
)
|
78
|
+
return row.pane
|
79
|
+
|
80
|
+
def to_auto_plots(self, **kwargs) -> pn.panel:
|
81
|
+
"""Given the dataset result of a benchmark run, automatically dedeuce how to plot the data based on the types of variables that were sampled
|
82
|
+
|
83
|
+
Args:
|
84
|
+
bench_cfg (BenchCfg): Information on how the benchmark was sampled and the resulting data
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
pn.pane: A panel containing plot results
|
88
|
+
"""
|
89
|
+
|
90
|
+
plot_cols = pn.Column()
|
91
|
+
plot_cols.append(self.to_sweep_summary(name="Plots View"))
|
92
|
+
plot_cols.append(self.to_auto(**kwargs))
|
93
|
+
plot_cols.append(self.bench_cfg.to_post_description())
|
94
|
+
return plot_cols
|