holobench 1.25.1__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/benchmark_data.py +196 -0
  2. bencher/example/example_all.py +45 -0
  3. bencher/example/example_categorical.py +99 -0
  4. bencher/example/example_composable_container.py +106 -0
  5. bencher/example/example_composable_container2.py +160 -0
  6. bencher/example/example_consts.py +39 -0
  7. bencher/example/example_custom_sweep.py +59 -0
  8. bencher/example/example_custom_sweep2.py +42 -0
  9. bencher/example/example_docs.py +34 -0
  10. bencher/example/example_filepath.py +27 -0
  11. bencher/example/example_float3D.py +101 -0
  12. bencher/example/example_float_cat.py +99 -0
  13. bencher/example/example_floats.py +89 -0
  14. bencher/example/example_floats2D.py +93 -0
  15. bencher/example/example_holosweep.py +98 -0
  16. bencher/example/example_holosweep_objects.py +111 -0
  17. bencher/example/example_holosweep_tap.py +144 -0
  18. bencher/example/example_image.py +155 -0
  19. bencher/example/example_levels.py +181 -0
  20. bencher/example/example_levels2.py +37 -0
  21. bencher/example/example_pareto.py +53 -0
  22. bencher/example/example_sample_cache.py +85 -0
  23. bencher/example/example_sample_cache_context.py +116 -0
  24. bencher/example/example_simple.py +134 -0
  25. bencher/example/example_simple_bool.py +35 -0
  26. bencher/example/example_simple_cat.py +48 -0
  27. bencher/example/example_simple_float.py +28 -0
  28. bencher/example/example_simple_float2d.py +29 -0
  29. bencher/example/example_strings.py +47 -0
  30. bencher/example/example_time_event.py +63 -0
  31. bencher/example/example_video.py +118 -0
  32. bencher/example/example_workflow.py +189 -0
  33. bencher/example/experimental/example_bokeh_plotly.py +38 -0
  34. bencher/example/experimental/example_hover_ex.py +45 -0
  35. bencher/example/experimental/example_hvplot_explorer.py +39 -0
  36. bencher/example/experimental/example_interactive.py +75 -0
  37. bencher/example/experimental/example_streamnd.py +49 -0
  38. bencher/example/experimental/example_streams.py +36 -0
  39. bencher/example/experimental/example_template.py +40 -0
  40. bencher/example/experimental/example_updates.py +84 -0
  41. bencher/example/experimental/example_vector.py +84 -0
  42. bencher/example/meta/example_meta.py +171 -0
  43. bencher/example/meta/example_meta_cat.py +25 -0
  44. bencher/example/meta/example_meta_float.py +23 -0
  45. bencher/example/meta/example_meta_levels.py +26 -0
  46. bencher/example/optuna/example_optuna.py +78 -0
  47. bencher/example/shelved/example_float2D_scatter.py +109 -0
  48. bencher/example/shelved/example_float3D_cone.py +96 -0
  49. bencher/example/shelved/example_kwargs.py +63 -0
  50. bencher/plotting/__init__.py +0 -0
  51. bencher/plotting/plot_filter.py +110 -0
  52. bencher/plotting/plt_cnt_cfg.py +75 -0
  53. bencher/results/__init__.py +0 -0
  54. bencher/results/bench_result.py +94 -0
  55. bencher/results/bench_result_base.py +476 -0
  56. bencher/results/composable_container/__init__.py +0 -0
  57. bencher/results/composable_container/composable_container_base.py +73 -0
  58. bencher/results/composable_container/composable_container_panel.py +39 -0
  59. bencher/results/composable_container/composable_container_video.py +184 -0
  60. bencher/results/float_formatter.py +44 -0
  61. bencher/results/holoview_result.py +753 -0
  62. bencher/results/optuna_result.py +354 -0
  63. bencher/results/panel_result.py +41 -0
  64. bencher/results/plotly_result.py +65 -0
  65. bencher/results/video_result.py +38 -0
  66. bencher/results/video_summary.py +222 -0
  67. bencher/variables/__init__.py +0 -0
  68. bencher/variables/inputs.py +202 -0
  69. bencher/variables/parametrised_sweep.py +208 -0
  70. bencher/variables/results.py +214 -0
  71. bencher/variables/sweep_base.py +162 -0
  72. bencher/variables/time.py +92 -0
  73. holobench-1.26.3.data/data/share/ament_index/resource_index/packages/bencher +0 -0
  74. {holobench-1.25.1.dist-info → holobench-1.26.3.dist-info}/METADATA +5 -7
  75. holobench-1.26.3.dist-info/RECORD +93 -0
  76. holobench-1.25.1.dist-info/RECORD +0 -20
  77. /holobench-1.25.1.data/data/share/ament_index/resource_index/packages/bencher → /bencher/example/__init__.py +0 -0
  78. {holobench-1.25.1.data → holobench-1.26.3.data}/data/share/bencher/package.xml +0 -0
  79. {holobench-1.25.1.dist-info → holobench-1.26.3.dist-info}/LICENSE +0 -0
  80. {holobench-1.25.1.dist-info → holobench-1.26.3.dist-info}/WHEEL +0 -0
  81. {holobench-1.25.1.dist-info → holobench-1.26.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,111 @@
1
+ # THIS IS NOT A WORKING EXAMPLE YET
2
+
3
+ # pylint: disable=duplicate-code,unused-argument
4
+
5
+
6
+ import bencher as bch
7
+ import math
8
+ import random
9
+ import numpy as np
10
+ import holoviews as hv
11
+
12
+ from strenum import StrEnum
13
+ from enum import auto
14
+
15
+
16
+ class Function(StrEnum):
17
+ fn_cos = auto()
18
+ fn_sin = auto()
19
+ fn_log = auto()
20
+ fn_arctan = auto()
21
+
22
+ def call(self, arg) -> float:
23
+ """Calls the function defined by the name of the enum
24
+
25
+ Returns:
26
+ float: The result of calling the function defined by the enum
27
+ """
28
+ return getattr(np, self.removeprefix("fn_"))(arg)
29
+
30
+
31
+ class FunctionInputs(bch.ParametrizedSweep):
32
+ phase = bch.FloatSweep(
33
+ default=0, bounds=[0, math.pi], doc="Input angle", units="rad", samples=5
34
+ )
35
+
36
+ freq = bch.FloatSweep(default=1, bounds=[0, math.pi], doc="Input angle", units="rad", samples=5)
37
+
38
+ theta = bch.FloatSweep(
39
+ default=0, bounds=[0, math.pi], doc="Input angle", units="rad", samples=10
40
+ )
41
+
42
+ compute_fn = bch.EnumSweep(Function)
43
+
44
+
45
+ class FunctionOutputs(bch.ParametrizedSweep):
46
+ fn_output = bch.ResultVar(units="v", doc="sin of theta with some noise")
47
+
48
+ out_sum = bch.ResultVar(units="v", doc="The sum")
49
+
50
+ hmap = bch.ResultHmap()
51
+
52
+ hmap2 = bch.ResultHmap()
53
+
54
+
55
+ def bench_fn(self, **kwargs) -> dict:
56
+ fin = FunctionInputs()
57
+ fin.update_params_from_kwargs(**kwargs)
58
+
59
+ output = FunctionOutputs()
60
+
61
+ output.fn_output = fin.compute_fn.call(fin.phase + fin.freq * fin.theta) + random.uniform(0, 0)
62
+ output.hmap = hv.Text(0, 0, f"{fin.phase}\n{fin.freq}\n {fin.theta}")
63
+ output.hmap2 = hv.Ellipse(0, 0, 1)
64
+ return output
65
+
66
+
67
+ def plot_holo(self, plot=True) -> hv.core.ViewableElement:
68
+ """Plots a generic representation of the object that is not a basic hv datatype. In this case its an image of the values of the object, but it could be any representation of the object, e.g. a screenshot of the object state"""
69
+ if plot:
70
+ pt = hv.Text(0, 0, f"{self.phase}\n{self.freq}\n {self.theta}")
71
+ pt *= hv.Ellipse(0, 0, 1)
72
+ return pt
73
+ return None
74
+
75
+
76
+ def example_holosweep(
77
+ run_cfg: bch.BenchRunCfg = bch.BenchRunCfg(), report: bch.BenchReport = bch.BenchReport()
78
+ ) -> bch.Bench:
79
+ # wv = PlotFunctions()
80
+
81
+ bench = bch.Bench(
82
+ "waves", bench_fn, worker_input_cfg=FunctionInputs, run_cfg=run_cfg, report=report
83
+ )
84
+
85
+ res = bench.plot_sweep(
86
+ "phase",
87
+ input_vars=[FunctionInputs.param.theta, FunctionInputs.param.freq],
88
+ result_vars=[
89
+ FunctionOutputs.param.fn_output,
90
+ FunctionOutputs.param.hmap,
91
+ FunctionOutputs.param.hmap2,
92
+ ],
93
+ )
94
+
95
+ # bench.report.append(res.summarise_sweep())
96
+ # bench.report.append(res.to_optuna())
97
+ # bench.report.append(res.get_best_holomap())
98
+ # bench.report.append(res.to_curve(), "Slider view")
99
+ # bench.report.append(res.to_holomap().layout())
100
+ # print(res.to_holomap())
101
+ bench.report.append(res.to_holomap())
102
+ # bench.report.append(res.to_holomap())
103
+
104
+ return bench
105
+
106
+
107
+ if __name__ == "__main__":
108
+ bench_run = bch.BenchRunner("bench_runner_test", run_cfg=bch.BenchRunCfg())
109
+
110
+ bench_run.add_run(example_holosweep)
111
+ bench_run.run(level=4, show=True)
@@ -0,0 +1,144 @@
1
+ # THIS IS NOT A WORKING EXAMPLE YET
2
+
3
+ # pylint: disable=duplicate-code,unused-argument
4
+
5
+
6
+ import bencher as bch
7
+ import math
8
+ import random
9
+ import numpy as np
10
+ import holoviews as hv
11
+
12
+ from strenum import StrEnum
13
+ from enum import auto
14
+
15
+
16
+ class Function(StrEnum):
17
+ fn_cos = auto()
18
+ fn_sin = auto()
19
+ fn_log = auto()
20
+ fn_arctan = auto()
21
+
22
+ def call(self, arg) -> float:
23
+ """Calls the function defined by the name of the enum
24
+
25
+ Returns:
26
+ float: The result of calling the function defined by the enum
27
+ """
28
+ return getattr(np, self.removeprefix("fn_"))(arg)
29
+
30
+
31
+ class PlotFunctions(bch.ParametrizedSweep):
32
+ phase = bch.FloatSweep(
33
+ default=0, bounds=[0, math.pi], doc="Input angle", units="rad", samples=5
34
+ )
35
+
36
+ freq = bch.FloatSweep(default=1, bounds=[0, math.pi], doc="Input angle", units="rad", samples=5)
37
+
38
+ theta = bch.FloatSweep(
39
+ default=0, bounds=[0, math.pi], doc="Input angle", units="rad", samples=10
40
+ )
41
+
42
+ compute_fn = bch.EnumSweep(Function)
43
+
44
+ fn_output = bch.ResultVar(units="v", doc="sin of theta with some noise")
45
+
46
+ out_sum = bch.ResultVar(units="v", doc="The sum")
47
+
48
+ hmap = bch.ResultHmap()
49
+
50
+ def __call__(self, plot=True, **kwargs) -> dict:
51
+ self.update_params_from_kwargs(**kwargs)
52
+ noise = 0.1
53
+
54
+ self.fn_output = self.compute_fn.call(self.phase + self.freq * self.theta) + random.uniform(
55
+ 0, noise
56
+ )
57
+ self.hmap = self.plot_holo(plot)
58
+
59
+ return self.get_results_values_as_dict()
60
+
61
+ def plot_holo(self, plot=True) -> hv.core.ViewableElement:
62
+ """Plots a generic representation of the object that is not a basic hv datatype. In this case its an image of the values of the object, but it could be any representation of the object, e.g. a screenshot of the object state"""
63
+ if plot:
64
+ pt = hv.Text(0, 0, f"phase:{self.phase}\nfreq:{self.freq}\ntheta:{self.theta}")
65
+ pt *= hv.Ellipse(0, 0, 1)
66
+ return pt
67
+ return None
68
+
69
+ def calc_vec(self, **kwargs) -> dict:
70
+ theta = self.param.theta.values()
71
+ kwargs.pop("theta", 0)
72
+ dat = [self.__call__(plot=False, theta=i, **kwargs)["fn_output"] for i in theta]
73
+ # print(dat)
74
+ self.out_sum = sum(dat)
75
+ self.hmap = hv.Curve((theta, dat), "theta", "voltage")
76
+ # pt = hv.Text(0, 0, f"{self.compute_fn}\n{self.phase}\n{self.freq}")
77
+ # pt *= hv.Ellipse(0, 0, 1)
78
+ return self.get_results_values_as_dict()
79
+
80
+
81
+ def example_holosweep_tap(
82
+ run_cfg: bch.BenchRunCfg = bch.BenchRunCfg(), report: bch.BenchReport = bch.BenchReport()
83
+ ) -> bch.Bench:
84
+ wv = PlotFunctions()
85
+
86
+ run_cfg.use_optuna = True
87
+ run_cfg.auto_plot = False
88
+ bench = bch.Bench("waves", wv, run_cfg=run_cfg, report=report)
89
+
90
+ res = bench.plot_sweep(
91
+ "phase",
92
+ input_vars=[PlotFunctions.param.theta, PlotFunctions.param.freq],
93
+ result_vars=[PlotFunctions.param.fn_output, PlotFunctions.param.hmap],
94
+ run_cfg=run_cfg,
95
+ )
96
+
97
+ bench.report.append(res.describe_sweep())
98
+ bench.report.append(res.to_heatmap_tap(PlotFunctions.param.fn_output))
99
+
100
+ return bench
101
+
102
+
103
+ def example_holosweep_tap_slider(
104
+ run_cfg: bch.BenchRunCfg = bch.BenchRunCfg(), report: bch.BenchReport = bch.BenchReport()
105
+ ) -> bch.Bench: # pragma: no cover
106
+ wv = PlotFunctions()
107
+
108
+ run_cfg.use_optuna = True
109
+ bench = bch.Bench("waves", wv, run_cfg=run_cfg, report=report)
110
+
111
+ res = bench.plot_sweep(
112
+ "phase",
113
+ input_vars=[PlotFunctions.param.theta, PlotFunctions.param.freq, PlotFunctions.param.phase],
114
+ result_vars=[PlotFunctions.param.fn_output],
115
+ run_cfg=run_cfg,
116
+ )
117
+
118
+ print(res.get_best_trial_params())
119
+ bench.report.append(res.get_best_holomap())
120
+
121
+ heatmap = res.to_heatmap().opts(tools=["hover", "tap"])
122
+ posxy = hv.streams.Tap(source=heatmap, x=0, y=0)
123
+ sld1 = wv.param.phase.as_slider()
124
+
125
+ def tap_plot(x, y):
126
+ print(x, y)
127
+ selres = bch.get_nearest_coords(res.ds, theta=x, freq=y, phase=sld1.value, repeat=1)
128
+ return res.hmaps[bch.hmap_canonical_input(selres)]
129
+
130
+ tap_dmap = hv.DynamicMap(tap_plot, streams=[posxy])
131
+
132
+ bench.report.append_tab(heatmap + tap_dmap, "Interactive Heatmap")
133
+ bench.report.append(sld1)
134
+
135
+ bench.report.append_tab(res.to_curve(), "Slider view")
136
+
137
+ return bench
138
+
139
+
140
+ if __name__ == "__main__":
141
+ example_holosweep_tap(bch.BenchRunCfg()).report.show()
142
+
143
+
144
+ # todo https://discourse.holoviz.org/t/pointdraw-as-parameterized-class/3539
@@ -0,0 +1,155 @@
1
+ import bencher as bch
2
+ import numpy as np
3
+ import math
4
+ import matplotlib.pyplot as plt
5
+
6
+
7
+ def polygon_points(radius: float, sides: int, start_angle: float):
8
+ points = []
9
+ for ang in np.linspace(0, 360, sides + 1):
10
+ angle = math.radians(start_angle + ang)
11
+ points.append(([math.sin(angle) * radius, math.cos(angle) * radius]))
12
+ return points
13
+
14
+
15
+ class BenchPolygons(bch.ParametrizedSweep):
16
+ sides = bch.IntSweep(default=3, bounds=(3, 7))
17
+ radius = bch.FloatSweep(default=1, bounds=(0.2, 1))
18
+ linewidth = bch.FloatSweep(default=1, bounds=(1, 10))
19
+ linestyle = bch.StringSweep(["solid", "dashed", "dotted"])
20
+ color = bch.StringSweep(["red", "green", "blue"])
21
+ start_angle = bch.FloatSweep(default=0, bounds=[0, 360])
22
+ polygon = bch.ResultImage()
23
+ area = bch.ResultVar()
24
+ side_length = bch.ResultVar()
25
+
26
+ def __call__(self, **kwargs):
27
+ self.update_params_from_kwargs(**kwargs)
28
+ points = polygon_points(self.radius, self.sides, self.start_angle)
29
+ # self.hmap = hv.Curve(points)
30
+ self.polygon = self.points_to_polygon_png(points, bch.gen_image_path("polygon"))
31
+
32
+ self.side_length = 2 * self.radius * math.sin(math.pi / self.sides)
33
+ self.area = (self.sides * self.side_length**2) / (4 * math.tan(math.pi / self.sides))
34
+ return super().__call__()
35
+
36
+ def points_to_polygon_png(self, points: list[float], filename: str):
37
+ """Draw a closed polygon and save to png"""
38
+ fig = plt.figure(frameon=False)
39
+ ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0], frameon=False)
40
+ ax.set_axis_off()
41
+ ax.plot(
42
+ [p[0] for p in points],
43
+ [p[1] for p in points],
44
+ linewidth=self.linewidth,
45
+ linestyle=self.linestyle,
46
+ color=self.color,
47
+ )
48
+ ax.set_xlim(-1, 1)
49
+ ax.set_ylim(-1, 1)
50
+
51
+ ax.set_aspect("equal")
52
+ fig.add_axes(ax)
53
+ fig.savefig(filename, dpi=30)
54
+
55
+ return filename
56
+
57
+
58
+ def example_image(
59
+ run_cfg: bch.BenchRunCfg = bch.BenchRunCfg(), report: bch.BenchReport = bch.BenchReport()
60
+ ) -> bch.Bench:
61
+ run_cfg.use_cache = False
62
+ bench = bch.Bench("polygons", BenchPolygons(), run_cfg=run_cfg, report=report)
63
+
64
+ bench.result_vars = ["polygon", "area"]
65
+
66
+ bench.add_plot_callback(bch.BenchResult.to_sweep_summary)
67
+ # bench.add_plot_callback(bch.BenchResult.to_auto, level=2)
68
+ bench.add_plot_callback(bch.BenchResult.to_panes, level=3)
69
+ # bench.add_plot_callback(bch.BenchResult.to_panes)
70
+
71
+ sweep_vars = ["sides", "radius", "linewidth", "color"]
72
+
73
+ # sweep_vars = ["sides", "radius" ]
74
+
75
+ for i in range(1, len(sweep_vars)):
76
+ s = sweep_vars[:i]
77
+ bench.plot_sweep(
78
+ f"Polygons Sweeping {len(s)} Parameters",
79
+ input_vars=s,
80
+ )
81
+ bench.report.append(bench.get_result().to_panes())
82
+
83
+ return bench
84
+
85
+
86
+ def example_image_vid(run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None) -> bch.Bench:
87
+ bench = BenchPolygons().to_bench(run_cfg, report)
88
+ bench.add_plot_callback(bch.BenchResult.to_sweep_summary)
89
+ bench.add_plot_callback(
90
+ bch.BenchResult.to_video_grid,
91
+ target_duration=0.06,
92
+ compose_method_list=[
93
+ bch.ComposeType.right,
94
+ bch.ComposeType.right,
95
+ bch.ComposeType.sequence,
96
+ ],
97
+ )
98
+ # from functools import partial
99
+ # bench.add_plot_callback(bch.BenchResult.to_video_summary)
100
+ # bench.add_plot_callback(bch.BenchResult.to_video_grid, time_sequence_dimension=0)
101
+ # bench.add_plot_callback(bch.BenchResult.to_video_grid)
102
+ # bench.add_plot_callback(bch.BenchResult.to_video_grid, time_sequence_dimension=2)
103
+ # bench.add_plot_callback(bch.BenchResult.to_video_grid, time_sequence_dimension=3)
104
+
105
+ bench.plot_sweep(input_vars=["radius"])
106
+ # res = bench.plot_sweep(input_vars=["radius"], plot=False)
107
+ # bench.report.append(res.to_video_grid(target_duration=0.06))
108
+ bench.plot_sweep(input_vars=["radius", "sides"])
109
+ # bench.plot_sweep(input_vars=["radius", "sides", "linewidth"])
110
+ # bench.plot_sweep(input_vars=["radius", "sides", "linewidth", "color"])
111
+
112
+ return bench
113
+
114
+
115
+ if __name__ == "__main__":
116
+
117
+ def simple():
118
+ bench = BenchPolygons().to_bench(bch.BenchRunCfg(level=4))
119
+
120
+ # bench.plot_sweep(input_vars=["sides","color","radius"])
121
+
122
+ # res = bench.sweep(input_vars=["sides", "radius"])
123
+
124
+ # bench.report.append(res.to_heatmap(target_dimension=3))
125
+
126
+ bench.plot_sweep(input_vars=["sides"])
127
+ bench.plot_sweep(input_vars=["sides", "color"])
128
+
129
+ bench.plot_sweep(input_vars=["sides", "radius"])
130
+
131
+ # bench.report.append(res.to_line(target_dimension=1))
132
+
133
+ return bench
134
+
135
+ def example_image_vid_sequential(
136
+ run_cfg: bch.BenchRunCfg = bch.BenchRunCfg(), report: bch.BenchReport = bch.BenchReport()
137
+ ) -> bch.Bench:
138
+ bench = BenchPolygons().to_bench(run_cfg, report)
139
+ bench.add_plot_callback(bch.BenchResult.to_title)
140
+ bench.add_plot_callback(bch.BenchResult.to_video_grid)
141
+ bench.sweep_sequential(input_vars=["radius", "sides", "linewidth", "color"], group_size=4)
142
+ return bench
143
+
144
+ # def example_image_pairs()
145
+
146
+ ex_run_cfg = bch.BenchRunCfg()
147
+ ex_run_cfg.use_sample_cache = True
148
+ # ex_run_cfg.debug = True
149
+ # ex_run_cfg.repeats = 2
150
+ ex_run_cfg.level = 4
151
+ example_image_vid(ex_run_cfg).report.show()
152
+ # simple().report.show()
153
+
154
+ # example_image_vid_sequential(ex_run_cfg).report.show()
155
+ # example_image(ex_run_cfg).report.show()
@@ -0,0 +1,181 @@
1
+ import bencher as bch
2
+ from bencher.utils import int_to_col
3
+
4
+ import math
5
+ import holoviews as hv
6
+ from typing import Any, List
7
+ import panel as pn
8
+ from holoviews import opts
9
+
10
+
11
+ class LevelsExample(bch.ParametrizedSweep):
12
+ xval = bch.FloatSweep(bounds=[0, 3.14])
13
+ yval = bch.FloatSweep(bounds=[0, 3.14])
14
+ level = bch.IntSweep(default=1, bounds=[1, 6])
15
+
16
+ output = bch.ResultVar(units="v")
17
+ hmap = bch.ResultHmap()
18
+
19
+ def __call__(self, **kwargs: Any) -> Any:
20
+ self.update_params_from_kwargs(**kwargs)
21
+ self.output = math.sin(self.xval) + math.cos(self.yval)
22
+ self.hmap = hv.Points((self.xval, self.yval)).opts(
23
+ marker="o", size=110 - self.level * 20, color=int_to_col(self.level - 1)
24
+ )
25
+
26
+ return self.get_results_values_as_dict()
27
+
28
+
29
+ class RunWithLevel(bch.ParametrizedSweep):
30
+ level = bch.IntSweep(default=1, bounds=[1, 8])
31
+ dimensions = bch.IntSweep(default=1, bounds=[1, 2])
32
+
33
+ level_samples = bch.ResultVar()
34
+
35
+ def __call__(self, **kwargs) -> dict():
36
+ self.update_params_from_kwargs(**kwargs)
37
+
38
+ self.level_samples = int(
39
+ pow(
40
+ len(bch.FloatSweep(bounds=[0, 1]).with_level(self.level).values()),
41
+ self.dimensions,
42
+ )
43
+ )
44
+ return self.get_results_values_as_dict()
45
+
46
+
47
+ def run_with_dim(bench: bch.Bench, dims: List[bch.SweepBase]) -> List[bch.BenchResult]:
48
+ results = []
49
+ for level in range(1, 6):
50
+ print(level)
51
+ res = bench.plot_sweep(
52
+ f"Level:{level}",
53
+ input_vars=dims,
54
+ const_vars=LevelsExample.get_input_defaults(
55
+ [LevelsExample.param.level.with_const(level)]
56
+ ),
57
+ result_vars=[LevelsExample.param.output, LevelsExample.param.hmap],
58
+ run_cfg=bch.BenchRunCfg(level=level, auto_plot=False),
59
+ )
60
+
61
+ results.append(res)
62
+ return results
63
+
64
+
65
+ def run_levels_1D(bench: bch.Bench) -> bch.Bench:
66
+ results = run_with_dim(bench, [LevelsExample.param.xval])
67
+ bench.report.append_title("Using Levels to define sample density")
68
+
69
+ bench1 = bch.Bench("levels", RunWithLevel(), run_cfg=bch.BenchRunCfg(auto_plot=False))
70
+ res1 = bench1.plot_sweep("Levels", input_vars=[RunWithLevel.param.level])
71
+
72
+ bench.report.append_markdown(
73
+ "Sample levels let you perform parameter sweeps without having to decide how many samples to take when defining the class. If you perform a sweep at level 2, then all the points are reused when sampling at level 3. The higher levels reuse the points from lower levels to avoid having to recompute potentially expensive samples. The other advantage is that it enables a workflow where you can quickly see the results of the sweep at a low resolution to sense check the code, and then run it at a high level to get the fidelity you want. When calling a sweep at a high level, you can publish the intermediate lower level results as the computiation continues so that you can track the progress of the computation and end the sweep early when you have sufficient resolution",
74
+ width=600,
75
+ )
76
+ row = pn.Row()
77
+ row.append(res1.to_table())
78
+ # row.append(res1.to_curve().opts(shared_axes=False))
79
+ row.append(res1.to_curve())
80
+
81
+ bench.report.append(row)
82
+
83
+ bench.report.append_markdown(
84
+ "Level 1 returns a single point at the lower bound of the parameter. Level 2 uses the uppper and lower bounds of the parameter. All subsequent levels are created by adding a sample between each previously calculated sample to ensure that all previous values can be reused while retaining an equal sample spacing. The following plots show the sample points as circles and the corresponding plot of a sin function sampled at that level.",
85
+ width=600,
86
+ )
87
+
88
+ combined_pts = hv.Overlay()
89
+ combined_curve = hv.Overlay()
90
+ for it, r in enumerate(results):
91
+ lvl = it + 1
92
+ row = pn.Row()
93
+ pts = r.to_holomap().overlay().opts(title=f"Sample Points for level: {lvl}", height=300)
94
+ ds = r.to_hv_dataset()
95
+ crv = r.to_curve_ds(ds.data).opts(shared_axes=False, height=300) * r.to_hv_dataset(
96
+ bch.ReduceType.NONE
97
+ ).to(hv.Scatter).opts(
98
+ title=f"Function Values for level: {lvl}", size=5, height=300, shared_axes=False
99
+ )
100
+
101
+ combined_pts *= pts
102
+ combined_curve *= crv
103
+ row.append(pts)
104
+ row.append(crv)
105
+ bench.report.append_markdown(f"## {r.bench_cfg.title}")
106
+ bench.report.append(row)
107
+
108
+ bench.report.append_markdown(
109
+ "This plot overlays the previous plots into a single image. It shows how each level overlaps the previous level"
110
+ )
111
+
112
+ bench.report.append(pn.Row(combined_pts, combined_curve))
113
+ return bench
114
+
115
+
116
+ def run_levels_2D(bench: bch.Bench) -> bch.Bench:
117
+ results = run_with_dim(bench, [LevelsExample.param.xval, LevelsExample.param.yval])
118
+ bench.report.append_markdown("# Using Levels to define 2D sample density", "Levels 2D")
119
+
120
+ bench1 = bch.Bench("lol", RunWithLevel(), run_cfg=bch.BenchRunCfg(auto_plot=False))
121
+ res1 = bench1.plot_sweep(
122
+ "Levels",
123
+ input_vars=[RunWithLevel.param.level],
124
+ const_vars=[RunWithLevel.param.dimensions.with_const(2)],
125
+ )
126
+ row = pn.Row()
127
+ row.append(res1.to_table())
128
+ # row.append(res1.to_curve().opts(shared_axes=False))
129
+ row.append(res1.to_curve())
130
+
131
+ bench.report.append(row)
132
+
133
+ for it, r in enumerate(results):
134
+ lvl = it + 1
135
+ row = pn.Row()
136
+ bench.report.append_markdown(f"## {r.bench_cfg.title}")
137
+ row.append(
138
+ r.to_holomap()
139
+ .overlay()
140
+ .opts(title=f"Sample Points for level: {lvl}", shared_axes=False)
141
+ )
142
+ row.append(
143
+ r.to_heatmap_single(r.bench_cfg.result_vars[0], bch.ReduceType.NONE).opts(
144
+ title=f"Function Value Heatmap for level: {lvl}", shared_axes=False
145
+ )
146
+ )
147
+ bench.report.append(row)
148
+
149
+ bench.report.append_markdown(
150
+ "This plot overlays the previous plots into a single image. It shows how each level overlaps the previous level"
151
+ )
152
+ overlay = hv.Overlay()
153
+ for lvl, r in enumerate(results):
154
+ overlay *= (
155
+ r.to_holomap()
156
+ .overlay()
157
+ .opts(width=1000, height=1000, show_legend=False, shared_axes=False)
158
+ )
159
+
160
+ bench.report.append(overlay)
161
+ return bench
162
+
163
+
164
+ def run_levels(
165
+ run_cfg: bch.BenchRunCfg = bch.BenchRunCfg(), report: bch.BenchReport = bch.BenchReport()
166
+ ) -> bch.Bench:
167
+ hv.extension("bokeh")
168
+ opts.defaults(
169
+ opts.Curve(show_legend=False),
170
+ opts.Points(show_legend=False),
171
+ )
172
+
173
+ bench = bch.Bench("Levels", LevelsExample(), run_cfg=run_cfg, report=report)
174
+ bench = run_levels_1D(bench)
175
+ bench = run_levels_2D(bench)
176
+
177
+ return bench
178
+
179
+
180
+ if __name__ == "__main__":
181
+ run_levels().report.show()
@@ -0,0 +1,37 @@
1
+ import bencher as bch
2
+
3
+
4
+ class Square(bch.ParametrizedSweep):
5
+ """An example of a datatype with an integer and float parameter"""
6
+
7
+ x = bch.FloatSweep(default=0, bounds=[0, 6])
8
+
9
+ result = bch.ResultVar("ul", doc="Square of x")
10
+
11
+ def __call__(self, **kwargs) -> dict:
12
+ self.update_params_from_kwargs(**kwargs)
13
+ self.result = self.x * self.x
14
+ return self.get_results_values_as_dict()
15
+
16
+
17
+ def example_levels2(run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None) -> bch.Bench:
18
+ """This example shows how to define a custom set of value to sample from intead of a uniform sweep
19
+
20
+ Args:
21
+ run_cfg (BenchRunCfg): configuration of how to perform the param sweep
22
+
23
+ Returns:
24
+ Bench: results of the parameter sweep
25
+ """
26
+
27
+ bench = Square().to_bench(run_cfg=run_cfg, report=report)
28
+
29
+ # These are all equivalent
30
+ bench.plot_sweep(input_vars=[Square.param.x.with_level(run_cfg.level, 3)])
31
+ bench.plot_sweep(input_vars=[bch.p("x", max_level=3)])
32
+
33
+ return bench
34
+
35
+
36
+ if __name__ == "__main__":
37
+ example_levels2(bch.BenchRunCfg(level=4)).report.show()
@@ -0,0 +1,53 @@
1
+ # pylint: disable=duplicate-code
2
+
3
+ import bencher as bch
4
+
5
+ # All the examples will be using the data structures and benchmark function defined in this file
6
+ from bencher.example.benchmark_data import ExampleBenchCfgIn, ExampleBenchCfgOut, bench_function
7
+
8
+
9
+ def example_pareto(
10
+ run_cfg: bch.BenchRunCfg = bch.BenchRunCfg(), report: bch.BenchReport = bch.BenchReport()
11
+ ) -> bch.Bench:
12
+ """Example of how to calculate the pareto front of a parameter sweep
13
+
14
+ Args:
15
+ run_cfg (BenchRunCfg): configuration of how to perform the param sweep
16
+
17
+ Returns:
18
+ Bench: results of the parameter sweep
19
+ """
20
+ run_cfg.use_optuna = True
21
+
22
+ bench = bch.Bench(
23
+ "Multi-objective optimisation",
24
+ bench_function,
25
+ ExampleBenchCfgIn,
26
+ run_cfg=run_cfg,
27
+ report=report,
28
+ )
29
+
30
+ res = bench.plot_sweep(
31
+ title="Pareto Optimisation with Optuna",
32
+ description="This example shows how to plot the pareto front of the tradeoff between multiple criteria. When multiple result variable are defined, and use_optuna=True a pareto plot and the relative importance of each input variable on the output criteria is plotted. A summary of the points on the pareto front is printed as well. You can use the pareto plot to decide the how to trade off one objective for another. Pareto plots are suppored for 2D and 3D. If you have more than 3 result variables the first 3 are selected for the pareto plot. Plotting 4D surfaces is left as an exercise to the reader",
33
+ input_vars=[
34
+ ExampleBenchCfgIn.param.theta,
35
+ ExampleBenchCfgIn.param.offset,
36
+ ],
37
+ result_vars=[ExampleBenchCfgOut.param.out_sin, ExampleBenchCfgOut.param.out_cos],
38
+ const_vars=ExampleBenchCfgIn.get_input_defaults(
39
+ [ExampleBenchCfgIn.param.noisy.with_const(True)]
40
+ ),
41
+ post_description="""# Post Description
42
+ This is a slightly unusual way of doing pareto optimisation as we are not using a typical multi-objective optimisation algorithm [TODO, add example]. Instead we are performing a grid search and looking at the resulting pareto plot. The reason for doing a grid search instead of standard pareto optimisation is that we can produce more isolated plots of how an input affects an output which can help understanding of the parameter space. Future examples will show how to use grid search to bootstrap further optimisation with a multi objective optimiser""",
43
+ )
44
+
45
+ bench.report.append(res.to_optuna_plots())
46
+ return bench
47
+
48
+
49
+ if __name__ == "__main__":
50
+ run_cfg_ex = bch.BenchRunCfg()
51
+ run_cfg_ex.repeats = 2
52
+ run_cfg_ex.level = 2
53
+ example_pareto(run_cfg_ex).report.show()