holobench 1.40.1__py3-none-any.whl → 1.42.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. CHANGELOG.md +10 -0
  2. bencher/__init__.py +20 -2
  3. bencher/bench_cfg.py +265 -61
  4. bencher/bench_report.py +2 -2
  5. bencher/bench_runner.py +96 -10
  6. bencher/bencher.py +421 -89
  7. bencher/caching.py +1 -4
  8. bencher/class_enum.py +70 -7
  9. bencher/example/example_composable_container_image.py +60 -0
  10. bencher/example/example_composable_container_video.py +49 -0
  11. bencher/example/example_dataframe.py +2 -2
  12. bencher/example/example_image.py +17 -21
  13. bencher/example/example_image1.py +16 -20
  14. bencher/example/example_levels.py +17 -173
  15. bencher/example/example_pareto.py +107 -31
  16. bencher/example/example_rerun2.py +1 -1
  17. bencher/example/example_simple_bool.py +2 -2
  18. bencher/example/example_simple_float2d.py +6 -1
  19. bencher/example/example_video.py +35 -17
  20. bencher/example/experimental/example_hvplot_explorer.py +3 -4
  21. bencher/example/inputs_0D/example_0_in_1_out.py +25 -15
  22. bencher/example/inputs_0D/example_0_in_2_out.py +12 -3
  23. bencher/example/inputs_0_float/example_0_cat_in_2_out.py +88 -0
  24. bencher/example/inputs_0_float/example_1_cat_in_2_out.py +98 -0
  25. bencher/example/inputs_0_float/example_2_cat_in_2_out.py +107 -0
  26. bencher/example/inputs_0_float/example_3_cat_in_2_out.py +111 -0
  27. bencher/example/inputs_1D/example1d_common.py +48 -12
  28. bencher/example/inputs_1D/example_0_float_1_cat.py +33 -0
  29. bencher/example/inputs_1D/example_1_cat_in_2_out_repeats.py +68 -0
  30. bencher/example/inputs_1D/example_1_float_2_cat_repeats.py +15 -0
  31. bencher/example/inputs_1D/example_1_int_in_1_out.py +98 -0
  32. bencher/example/inputs_1D/example_1_int_in_2_out.py +101 -0
  33. bencher/example/inputs_1D/example_1_int_in_2_out_repeats.py +99 -0
  34. bencher/example/inputs_1_float/example_1_float_0_cat_in_2_out.py +117 -0
  35. bencher/example/inputs_1_float/example_1_float_1_cat_in_2_out.py +124 -0
  36. bencher/example/inputs_1_float/example_1_float_2_cat_in_2_out.py +132 -0
  37. bencher/example/inputs_1_float/example_1_float_3_cat_in_2_out.py +140 -0
  38. bencher/example/inputs_2D/example_2_cat_in_4_out_repeats.py +104 -0
  39. bencher/example/inputs_2_float/example_2_float_0_cat_in_2_out.py +98 -0
  40. bencher/example/inputs_2_float/example_2_float_1_cat_in_2_out.py +112 -0
  41. bencher/example/inputs_2_float/example_2_float_2_cat_in_2_out.py +122 -0
  42. bencher/example/inputs_2_float/example_2_float_3_cat_in_2_out.py +138 -0
  43. bencher/example/inputs_3_float/example_3_float_0_cat_in_2_out.py +111 -0
  44. bencher/example/inputs_3_float/example_3_float_1_cat_in_2_out.py +117 -0
  45. bencher/example/inputs_3_float/example_3_float_2_cat_in_2_out.py +124 -0
  46. bencher/example/inputs_3_float/example_3_float_3_cat_in_2_out.py +129 -0
  47. bencher/example/meta/generate_examples.py +124 -7
  48. bencher/example/meta/generate_meta.py +88 -40
  49. bencher/job.py +175 -12
  50. bencher/plotting/plot_filter.py +52 -17
  51. bencher/results/bench_result.py +119 -26
  52. bencher/results/bench_result_base.py +119 -10
  53. bencher/results/composable_container/composable_container_video.py +39 -12
  54. bencher/results/dataset_result.py +6 -200
  55. bencher/results/explorer_result.py +23 -0
  56. bencher/results/{hvplot_result.py → histogram_result.py} +3 -18
  57. bencher/results/holoview_results/__init__.py +0 -0
  58. bencher/results/holoview_results/bar_result.py +79 -0
  59. bencher/results/holoview_results/curve_result.py +110 -0
  60. bencher/results/holoview_results/distribution_result/__init__.py +0 -0
  61. bencher/results/holoview_results/distribution_result/box_whisker_result.py +73 -0
  62. bencher/results/holoview_results/distribution_result/distribution_result.py +109 -0
  63. bencher/results/holoview_results/distribution_result/scatter_jitter_result.py +92 -0
  64. bencher/results/holoview_results/distribution_result/violin_result.py +70 -0
  65. bencher/results/holoview_results/heatmap_result.py +319 -0
  66. bencher/results/holoview_results/holoview_result.py +346 -0
  67. bencher/results/holoview_results/line_result.py +240 -0
  68. bencher/results/holoview_results/scatter_result.py +107 -0
  69. bencher/results/holoview_results/surface_result.py +158 -0
  70. bencher/results/holoview_results/table_result.py +14 -0
  71. bencher/results/holoview_results/tabulator_result.py +20 -0
  72. bencher/results/laxtex_result.py +42 -35
  73. bencher/results/optuna_result.py +30 -115
  74. bencher/results/video_controls.py +38 -0
  75. bencher/results/video_result.py +39 -36
  76. bencher/results/video_summary.py +2 -2
  77. bencher/results/{plotly_result.py → volume_result.py} +29 -8
  78. bencher/utils.py +176 -30
  79. bencher/variables/inputs.py +122 -15
  80. bencher/video_writer.py +38 -2
  81. bencher/worker_job.py +34 -7
  82. {holobench-1.40.1.dist-info → holobench-1.42.0.dist-info}/METADATA +21 -25
  83. holobench-1.42.0.dist-info/RECORD +147 -0
  84. bencher/example/example_composable_container.py +0 -106
  85. bencher/example/example_levels2.py +0 -37
  86. bencher/example/inputs_1D/example_1_in_1_out.py +0 -62
  87. bencher/example/inputs_1D/example_1_in_2_out.py +0 -63
  88. bencher/example/inputs_1D/example_1_in_2_out_repeats.py +0 -61
  89. bencher/results/holoview_result.py +0 -787
  90. bencher/results/panel_result.py +0 -41
  91. holobench-1.40.1.dist-info/RECORD +0 -111
  92. {holobench-1.40.1.dist-info → holobench-1.42.0.dist-info}/WHEEL +0 -0
  93. {holobench-1.40.1.dist-info → holobench-1.42.0.dist-info}/licenses/LICENSE +0 -0
bencher/caching.py CHANGED
@@ -3,9 +3,6 @@ from diskcache import Cache
3
3
  from bencher.variables.parametrised_sweep import ParametrizedSweep
4
4
  from bencher.utils import hash_sha1
5
5
  import logging
6
- from sortedcontainers import SortedDict
7
-
8
- # from job import job,JobCache,JobFunctionCache
9
6
 
10
7
 
11
8
  class CachedParams(ParametrizedSweep):
@@ -20,7 +17,7 @@ class CachedParams(ParametrizedSweep):
20
17
  self.cache.clear()
21
18
 
22
19
  def kwargs_to_hash_key(self, **kwargs):
23
- return tuple(SortedDict(kwargs).items())
20
+ return tuple(sorted(kwargs.items(), key=lambda item: str(item[0])))
24
21
 
25
22
  def in_cache(self, **kwargs):
26
23
  self.update_params_from_kwargs(**kwargs)
bencher/class_enum.py CHANGED
@@ -1,52 +1,115 @@
1
1
  from __future__ import annotations
2
2
  from strenum import StrEnum
3
- from typing import Any
3
+ from typing import Any, TypeVar
4
4
  import importlib
5
5
  from abc import abstractmethod
6
6
  from dataclasses import dataclass
7
7
  from enum import auto
8
8
 
9
+ T = TypeVar("T") # Generic type for return value of to_class
10
+
9
11
 
10
12
  class ClassEnum(StrEnum):
11
- """A ClassEnum is a pattern to make it easier to create factory a factory method that converts from an enum to a corresponding class. Subclasses should implement to_class(enum_instance:EnumType) which takes an enum returns the corresponding instance of that class."""
13
+ """A string-based enum class that maps enum values to corresponding class instances.
14
+
15
+ ClassEnum is a pattern to make it easier to create a factory method that converts
16
+ from an enum value to a corresponding class instance. Subclasses should implement
17
+ the to_class() method which takes an enum value and returns an instance of the
18
+ corresponding class.
19
+
20
+ This pattern is useful for configuration-driven class instantiation, allowing
21
+ classes to be selected via string configuration values that match enum names.
22
+ """
12
23
 
13
24
  @classmethod
14
25
  def to_class_generic(cls, module_import: str, class_name: str) -> Any:
15
- """Create an instance of the class referred to by this enum
26
+ """Create an instance of a class from its module path and class name.
27
+
28
+ This utility method dynamically imports a module and instantiates a class from it.
29
+
30
+ Args:
31
+ module_import (str): The module path to import (e.g., "bencher.class_enum")
32
+ class_name (str): The name of the class to instantiate
16
33
 
17
34
  Returns:
18
- Any: instance of the class
35
+ Any: A new instance of the specified class
19
36
  """
20
-
21
37
  class_def = getattr(importlib.import_module(module_import), class_name)
22
38
  return class_def()
23
39
 
24
40
  @classmethod
25
41
  @abstractmethod
26
42
  def to_class(cls, enum_val: ClassEnum) -> Any:
27
- """Subclasses should overrides this method to take an enum returns the corresponding instance of that class."""
28
- raise NotImplementedError()
43
+ """Convert an enum value to its corresponding class instance.
44
+
45
+ Subclasses must override this method to implement the mapping from
46
+ enum values to class instances.
47
+
48
+ Args:
49
+ enum_val (ClassEnum): The enum value to convert to a class instance
50
+
51
+ Returns:
52
+ Any: An instance of the class corresponding to the enum value
53
+
54
+ Raises:
55
+ NotImplementedError: If this method is not overridden by a subclass
56
+ """
57
+ raise NotImplementedError("Subclasses must implement to_class()")
29
58
 
30
59
 
31
60
  @dataclass
32
61
  class BaseClass:
62
+ """Base class for the ClassEnum example.
63
+
64
+ A simple dataclass that serves as the base class for the ClassEnum example classes.
65
+
66
+ Attributes:
67
+ baseclassname (str): A name for the base class
68
+ """
69
+
33
70
  baseclassname: str = "class0"
34
71
 
35
72
 
36
73
  @dataclass
37
74
  class Class1(BaseClass):
75
+ """Example subclass 1 for the ClassEnum demonstration.
76
+
77
+ Attributes:
78
+ classname (str): A name for this class
79
+ """
80
+
38
81
  classname: str = "class1"
39
82
 
40
83
 
41
84
  @dataclass
42
85
  class Class2(BaseClass):
86
+ """Example subclass 2 for the ClassEnum demonstration.
87
+
88
+ Attributes:
89
+ classname (str): A name for this class
90
+ """
91
+
43
92
  classname: str = "class2"
44
93
 
45
94
 
46
95
  class ExampleEnum(ClassEnum):
96
+ """An example implementation of ClassEnum.
97
+
98
+ This enum demonstrates how to use ClassEnum to map enum values to class instances.
99
+ Each enum value corresponds to a class name that can be instantiated.
100
+ """
101
+
47
102
  Class1 = auto()
48
103
  Class2 = auto()
49
104
 
50
105
  @classmethod
51
106
  def to_class(cls, enum_val: ExampleEnum) -> BaseClass:
107
+ """Convert an ExampleEnum value to its corresponding class instance.
108
+
109
+ Args:
110
+ enum_val (ExampleEnum): The enum value to convert
111
+
112
+ Returns:
113
+ BaseClass: An instance of either Class1 or Class2, depending on the enum value
114
+ """
52
115
  return cls.to_class_generic("bencher.class_enum", enum_val)
@@ -0,0 +1,60 @@
1
+ import bencher as bch
2
+
3
+ from bencher.example.example_image import BenchPolygons
4
+
5
+
6
+ class BenchComposableContainerImage(BenchPolygons):
7
+ compose_method = bch.EnumSweep(bch.ComposeType)
8
+ labels = bch.BoolSweep()
9
+ num_frames = bch.IntSweep(default=5, bounds=[1, 100])
10
+
11
+ polygon_vid = bch.ResultVideo()
12
+
13
+ def __call__(self, **kwargs):
14
+ self.update_params_from_kwargs(**kwargs)
15
+ var_name = None
16
+ var_value = None
17
+
18
+ if self.labels:
19
+ var_name = "sides"
20
+ var_value = self.sides
21
+ vr = bch.ComposableContainerVideo()
22
+ for i in range(self.num_frames):
23
+ res = super().__call__(start_angle=i)
24
+ print(res)
25
+ vr.append(res["polygon"])
26
+ self.polygon_vid = vr.to_video(
27
+ bch.RenderCfg(
28
+ compose_method=self.compose_method,
29
+ var_name=var_name,
30
+ var_value=var_value,
31
+ max_frame_duration=1.0 / 20.0,
32
+ )
33
+ )
34
+ return self.get_results_values_as_dict()
35
+
36
+
37
+ def example_composable_container_image(
38
+ run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None
39
+ ) -> bch.Bench:
40
+ bench = BenchComposableContainerImage().to_bench(run_cfg, report)
41
+ # bench.add_plot_callback(bch.BenchResult.to_panes)
42
+ # bench.add_plot_callback(bch.BenchResult.to_video_grid, result_types=(bch.ResultVideo))
43
+ # bench.add_plot_callback(bch.BenchResult.to_video_summary, result_types=(bch.ResultVideo))
44
+ # bench.plot_sweep(input_vars=["compose_method", "labels"])
45
+
46
+ bench.plot_sweep(input_vars=["compose_method"])
47
+
48
+ # bench.compose_
49
+ # bench.plot_sweep(
50
+ # input_vars=[bch.p("num_frames", [2, 8, 20])],
51
+ # const_vars=dict(compose_method=bch.ComposeType.sequence),
52
+ # )
53
+
54
+ return bench
55
+
56
+
57
+ if __name__ == "__main__":
58
+ ex_run_cfg = bch.BenchRunCfg()
59
+ ex_composable_image = example_composable_container_image(ex_run_cfg)
60
+ ex_composable_image.report.show()
@@ -0,0 +1,49 @@
1
+ import bencher as bch
2
+ from bencher.example.example_composable_container_image import BenchComposableContainerImage
3
+
4
+
5
+ class BenchComposableContainerVideo(bch.ParametrizedSweep):
6
+ unequal_length = bch.BoolSweep()
7
+ compose_method = bch.EnumSweep(bch.ComposeType)
8
+ labels = bch.BoolSweep()
9
+ polygon_vid = bch.ResultVideo()
10
+
11
+ def __call__(self, **kwargs):
12
+ self.update_params_from_kwargs(**kwargs)
13
+ vr = bch.ComposableContainerVideo()
14
+ for i in range(3, 5):
15
+ num_frames = i * 10 if self.unequal_length else 5
16
+ res = BenchComposableContainerImage().__call__(
17
+ compose_method=bch.ComposeType.sequence, sides=i, num_frames=num_frames
18
+ )
19
+ vr.append(res["polygon_vid"])
20
+
21
+ self.polygon_vid = vr.to_video(bch.RenderCfg(compose_method=kwargs.get("compose_method")))
22
+ return self.get_results_values_as_dict()
23
+
24
+
25
+ def example_composable_container_video(
26
+ run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None
27
+ ) -> bch.Bench:
28
+ bench = BenchComposableContainerVideo().to_bench(run_cfg, report)
29
+
30
+ bench.result_vars = ["polygon_vid"]
31
+ bench.add_plot_callback(bch.BenchResult.to_panes)
32
+ bench.add_plot_callback(bch.BenchResult.to_video_grid, result_types=(bch.ResultVideo))
33
+ bench.add_plot_callback(bch.BenchResult.to_video_summary, result_types=(bch.ResultVideo))
34
+ bench.plot_sweep(input_vars=["compose_method", "labels"], const_vars=dict(unequal_length=True))
35
+
36
+ res = bench.plot_sweep(
37
+ input_vars=[],
38
+ const_vars=dict(unequal_length=False, compose_method=bch.ComposeType.sequence),
39
+ plot_callbacks=False,
40
+ )
41
+
42
+ bench.report.append(res.to_video_grid())
43
+
44
+ return bench
45
+
46
+
47
+ if __name__ == "__main__":
48
+ ex_run_cfg = bch.BenchRunCfg()
49
+ example_composable_container_video(ex_run_cfg).report.show()
@@ -25,7 +25,7 @@ class ExampleMergeDataset(bch.ParametrizedSweep):
25
25
 
26
26
  def example_dataset(run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None):
27
27
  bench = ExampleMergeDataset().to_bench(run_cfg, report)
28
- res = bench.plot_sweep(input_vars=["value"], const_vars=dict(repeats_x=4))
28
+ bench.plot_sweep(input_vars=["value"], const_vars=dict(repeats_x=4))
29
29
  # bench.report.append(res.to_panes(target_dimension=1))
30
30
  # bench.report.append(res.to_panes(target_dimension=2))
31
31
  # bench.reprt.append(res.to_video_grid
@@ -39,7 +39,7 @@ def example_dataset(run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = N
39
39
  # )
40
40
  # bench.report.append(res.to_panes(container=hv.Bars,target_dimension=1))
41
41
  # bench.report.append(res.to_panes(container=hv.Curve))
42
- bench.report.append(res.to_dataset1(container=hv.Curve))
42
+ bench.add(bch.DataSetResult, container=hv.Curve)
43
43
  return bench
44
44
 
45
45
 
@@ -1,7 +1,7 @@
1
1
  import bencher as bch
2
2
  import numpy as np
3
3
  import math
4
- import matplotlib.pyplot as plt
4
+ from PIL import Image, ImageDraw
5
5
 
6
6
 
7
7
  def polygon_points(radius: float, sides: int, start_angle: float):
@@ -16,7 +16,6 @@ class BenchPolygons(bch.ParametrizedSweep):
16
16
  sides = bch.IntSweep(default=3, bounds=(3, 7))
17
17
  radius = bch.FloatSweep(default=1, bounds=(0.2, 1))
18
18
  linewidth = bch.FloatSweep(default=1, bounds=(1, 10))
19
- linestyle = bch.StringSweep(["solid", "dashed", "dotted"])
20
19
  color = bch.StringSweep(["red", "green", "blue"])
21
20
  start_angle = bch.FloatSweep(default=0, bounds=[0, 360])
22
21
  polygon = bch.ResultImage()
@@ -26,33 +25,30 @@ class BenchPolygons(bch.ParametrizedSweep):
26
25
  def __call__(self, **kwargs):
27
26
  self.update_params_from_kwargs(**kwargs)
28
27
  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"))
28
+ filepath = bch.gen_image_path("polygon")
29
+ self.polygon = self.points_to_polygon_png(points, filepath)
30
+ # Verify filepath is being returned
31
+ assert isinstance(self.polygon, str), f"Expected string filepath, got {type(self.polygon)}"
31
32
 
32
33
  self.side_length = 2 * self.radius * math.sin(math.pi / self.sides)
33
34
  self.area = (self.sides * self.side_length**2) / (4 * math.tan(math.pi / self.sides))
34
35
  return super().__call__()
35
36
 
36
37
  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)
38
+ """Draw a closed polygon and save to png using PIL"""
39
+ size = 300
40
+ img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
41
+ draw = ImageDraw.Draw(img)
42
+
43
+ # Scale points to image size (from [-1,1] to [0,size])
44
+ scaled_points = [(((p[0] + 1) * size / 2), ((1 - p[1]) * size / 2)) for p in points]
50
45
 
51
- ax.set_aspect("equal")
52
- fig.add_axes(ax)
53
- fig.savefig(filename, dpi=30)
46
+ # Draw polygon outline
47
+ draw.line(scaled_points, fill=self.color, width=int(self.linewidth))
54
48
 
55
- return filename
49
+ img.save(filename, "PNG")
50
+ # Explicitly return the filename string
51
+ return str(filename)
56
52
 
57
53
 
58
54
  def example_image(run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None) -> bch.Bench:
@@ -1,7 +1,7 @@
1
1
  import bencher as bch
2
2
  import numpy as np
3
3
  import math
4
- import matplotlib.pyplot as plt
4
+ from PIL import Image, ImageDraw
5
5
 
6
6
 
7
7
  def polygon_points(radius: float, sides: int, start_angle: float):
@@ -28,36 +28,32 @@ class BenchPolygons(bch.ParametrizedSweep):
28
28
  def __call__(self, **kwargs):
29
29
  self.update_params_from_kwargs(**kwargs)
30
30
  points = polygon_points(self.radius, self.sides, self.start_angle)
31
- # self.hmap = hv.Curve(points)
32
- self.polygon = self.points_to_polygon_png(points, bch.gen_image_path("polygon"), dpi=30)
31
+ filepath = bch.gen_image_path("polygon")
32
+ self.polygon = self.points_to_polygon_png(points, filepath, dpi=30)
33
33
  self.polygon_small = self.points_to_polygon_png(
34
34
  points, bch.gen_image_path("polygon"), dpi=10
35
35
  )
36
+ # Verify filepaths are being returned
37
+ assert isinstance(self.polygon, str), f"Expected string filepath, got {type(self.polygon)}"
38
+ assert isinstance(self.polygon_small, str), (
39
+ f"Expected string filepath, got {type(self.polygon_small)}"
40
+ )
36
41
 
37
42
  self.side_length = 2 * self.radius * math.sin(math.pi / self.sides)
38
43
  self.area = (self.sides * self.side_length**2) / (4 * math.tan(math.pi / self.sides))
39
44
  return super().__call__()
40
45
 
41
46
  def points_to_polygon_png(self, points: list[float], filename: str, dpi):
42
- """Draw a closed polygon and save to png"""
43
- fig = plt.figure(frameon=False)
44
- ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0], frameon=False)
45
- ax.set_axis_off()
46
- ax.plot(
47
- [p[0] for p in points],
48
- [p[1] for p in points],
49
- linewidth=self.linewidth,
50
- linestyle=self.linestyle,
51
- color=self.color,
52
- )
53
- ax.set_xlim(-1, 1)
54
- ax.set_ylim(-1, 1)
47
+ """Draw a closed polygon and save to png using PIL"""
48
+ size = int(100 * (dpi / 30))
49
+ img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
50
+ draw = ImageDraw.Draw(img)
55
51
 
56
- ax.set_aspect("equal")
57
- fig.add_axes(ax)
58
- fig.savefig(filename, dpi=dpi)
52
+ scaled_points = [(((p[0] + 1) * size / 2), ((1 - p[1]) * size / 2)) for p in points]
53
+ draw.line(scaled_points, fill=self.color, width=int(self.linewidth))
59
54
 
60
- return filename
55
+ img.save(filename, "PNG")
56
+ return str(filename)
61
57
 
62
58
 
63
59
  def example_image_vid_sequential1(
@@ -1,179 +1,23 @@
1
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 upper 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,
2
+ from bencher.example.meta.example_meta import BenchMeta
3
+
4
+
5
+ def example_levels(run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None) -> bch.Bench:
6
+ bench = BenchMeta().to_bench(run_cfg, report)
7
+
8
+ bench.plot_sweep(
9
+ title="Using Levels to define sample density",
10
+ description="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",
11
+ input_vars=[
12
+ bch.p("float_vars", [1, 2]),
13
+ bch.p("level", [2, 3, 4, 5]),
14
+ ],
15
+ const_vars=[
16
+ BenchMeta.param.categorical_vars.with_const(0),
17
+ ],
86
18
  )
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(run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None) -> bch.Bench:
165
- hv.extension("bokeh")
166
- opts.defaults(
167
- opts.Curve(show_legend=False),
168
- opts.Points(show_legend=False),
169
- )
170
-
171
- bench = bch.Bench("Levels", LevelsExample(), run_cfg=run_cfg, report=report)
172
- bench = run_levels_1D(bench)
173
- bench = run_levels_2D(bench)
174
-
175
19
  return bench
176
20
 
177
21
 
178
22
  if __name__ == "__main__":
179
- run_levels().report.show()
23
+ example_levels().report.show()