holobench 1.40.1__tar.gz → 1.41.0__tar.gz

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 (117) hide show
  1. holobench-1.41.0/CHANGELOG.md +10 -0
  2. {holobench-1.40.1 → holobench-1.41.0}/PKG-INFO +9 -13
  3. {holobench-1.40.1 → holobench-1.41.0}/bencher/bench_cfg.py +3 -7
  4. {holobench-1.40.1 → holobench-1.41.0}/bencher/caching.py +1 -4
  5. holobench-1.41.0/bencher/example/example_composable_container_image.py +60 -0
  6. holobench-1.41.0/bencher/example/example_composable_container_video.py +49 -0
  7. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_image.py +17 -21
  8. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_image1.py +16 -20
  9. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_video.py +33 -17
  10. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_hvplot_explorer.py +2 -3
  11. holobench-1.41.0/bencher/example/inputs_1D/example_1_float_2_cat_repeats.py +12 -0
  12. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/meta/generate_examples.py +6 -0
  13. {holobench-1.40.1 → holobench-1.41.0}/bencher/job.py +1 -3
  14. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/bench_result.py +2 -1
  15. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/bench_result_base.py +2 -2
  16. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/composable_container/composable_container_video.py +39 -12
  17. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/holoview_result.py +21 -12
  18. holobench-1.41.0/bencher/results/laxtex_result.py +67 -0
  19. {holobench-1.40.1 → holobench-1.41.0}/bencher/utils.py +1 -4
  20. {holobench-1.40.1 → holobench-1.41.0}/bencher/video_writer.py +37 -2
  21. {holobench-1.40.1 → holobench-1.41.0}/bencher/worker_job.py +3 -4
  22. {holobench-1.40.1 → holobench-1.41.0}/pyproject.toml +20 -39
  23. holobench-1.40.1/bencher/example/example_composable_container.py +0 -106
  24. holobench-1.40.1/bencher/results/laxtex_result.py +0 -60
  25. {holobench-1.40.1 → holobench-1.41.0}/.gitignore +0 -0
  26. {holobench-1.40.1 → holobench-1.41.0}/LICENSE +0 -0
  27. {holobench-1.40.1 → holobench-1.41.0}/README.md +0 -0
  28. {holobench-1.40.1 → holobench-1.41.0}/bencher/__init__.py +0 -0
  29. {holobench-1.40.1 → holobench-1.41.0}/bencher/bench_plot_server.py +0 -0
  30. {holobench-1.40.1 → holobench-1.41.0}/bencher/bench_report.py +0 -0
  31. {holobench-1.40.1 → holobench-1.41.0}/bencher/bench_runner.py +0 -0
  32. {holobench-1.40.1 → holobench-1.41.0}/bencher/bencher.py +0 -0
  33. {holobench-1.40.1 → holobench-1.41.0}/bencher/class_enum.py +0 -0
  34. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/__init__.py +0 -0
  35. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/benchmark_data.py +0 -0
  36. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_all.py +0 -0
  37. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_categorical.py +0 -0
  38. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_composable_container2.py +0 -0
  39. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_consts.py +0 -0
  40. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_custom_sweep.py +0 -0
  41. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_custom_sweep2.py +0 -0
  42. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_dataframe.py +0 -0
  43. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_docs.py +0 -0
  44. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_filepath.py +0 -0
  45. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_float3D.py +0 -0
  46. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_float_cat.py +0 -0
  47. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_floats.py +0 -0
  48. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_floats2D.py +0 -0
  49. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_holosweep.py +0 -0
  50. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_holosweep_objects.py +0 -0
  51. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_holosweep_tap.py +0 -0
  52. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_levels.py +0 -0
  53. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_levels2.py +0 -0
  54. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_pareto.py +0 -0
  55. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_publish.py +0 -0
  56. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_rerun.py +0 -0
  57. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_rerun2.py +0 -0
  58. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_sample_cache.py +0 -0
  59. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_sample_cache_context.py +0 -0
  60. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_simple.py +0 -0
  61. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_simple_bool.py +0 -0
  62. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_simple_cat.py +0 -0
  63. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_simple_float.py +0 -0
  64. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_simple_float2d.py +0 -0
  65. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_strings.py +0 -0
  66. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_time_event.py +0 -0
  67. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/example_workflow.py +0 -0
  68. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_bokeh_plotly.py +0 -0
  69. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_hover_ex.py +0 -0
  70. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_interactive.py +0 -0
  71. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_streamnd.py +0 -0
  72. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_streams.py +0 -0
  73. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_template.py +0 -0
  74. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_updates.py +0 -0
  75. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/experimental/example_vector.py +0 -0
  76. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/inputs_0D/example_0_in_1_out.py +0 -0
  77. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/inputs_0D/example_0_in_2_out.py +0 -0
  78. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/inputs_1D/example1d_common.py +0 -0
  79. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/inputs_1D/example_1_in_1_out.py +0 -0
  80. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/inputs_1D/example_1_in_2_out.py +0 -0
  81. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/inputs_1D/example_1_in_2_out_repeats.py +0 -0
  82. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/meta/__init__.py +0 -0
  83. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/meta/example_meta.py +0 -0
  84. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/meta/example_meta_cat.py +0 -0
  85. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/meta/example_meta_float.py +0 -0
  86. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/meta/example_meta_levels.py +0 -0
  87. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/meta/generate_meta.py +0 -0
  88. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/optuna/example_optuna.py +0 -0
  89. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/shelved/example_float2D_scatter.py +0 -0
  90. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/shelved/example_float3D_cone.py +0 -0
  91. {holobench-1.40.1 → holobench-1.41.0}/bencher/example/shelved/example_kwargs.py +0 -0
  92. {holobench-1.40.1 → holobench-1.41.0}/bencher/flask_server.py +0 -0
  93. {holobench-1.40.1 → holobench-1.41.0}/bencher/optuna_conversions.py +0 -0
  94. {holobench-1.40.1 → holobench-1.41.0}/bencher/plotting/__init__.py +0 -0
  95. {holobench-1.40.1 → holobench-1.41.0}/bencher/plotting/plot_filter.py +0 -0
  96. {holobench-1.40.1 → holobench-1.41.0}/bencher/plotting/plt_cnt_cfg.py +0 -0
  97. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/__init__.py +0 -0
  98. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/composable_container/__init__.py +0 -0
  99. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/composable_container/composable_container_base.py +0 -0
  100. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/composable_container/composable_container_dataframe.py +0 -0
  101. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/composable_container/composable_container_panel.py +0 -0
  102. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/dataset_result.py +0 -0
  103. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/float_formatter.py +0 -0
  104. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/hvplot_result.py +0 -0
  105. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/optuna_result.py +0 -0
  106. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/panel_result.py +0 -0
  107. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/plotly_result.py +0 -0
  108. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/video_result.py +0 -0
  109. {holobench-1.40.1 → holobench-1.41.0}/bencher/results/video_summary.py +0 -0
  110. {holobench-1.40.1 → holobench-1.41.0}/bencher/utils_rerun.py +0 -0
  111. {holobench-1.40.1 → holobench-1.41.0}/bencher/variables/__init__.py +0 -0
  112. {holobench-1.40.1 → holobench-1.41.0}/bencher/variables/inputs.py +0 -0
  113. {holobench-1.40.1 → holobench-1.41.0}/bencher/variables/parametrised_sweep.py +0 -0
  114. {holobench-1.40.1 → holobench-1.41.0}/bencher/variables/results.py +0 -0
  115. {holobench-1.40.1 → holobench-1.41.0}/bencher/variables/sweep_base.py +0 -0
  116. {holobench-1.40.1 → holobench-1.41.0}/bencher/variables/time.py +0 -0
  117. {holobench-1.40.1 → holobench-1.41.0}/resource/bencher +0 -0
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.10]
9
+
10
+ Before changelogs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: holobench
3
- Version: 1.40.1
3
+ Version: 1.41.0
4
4
  Summary: A package for benchmarking the performance of arbitrary functions
5
5
  Project-URL: Repository, https://github.com/dyson-ai/bencher
6
6
  Project-URL: Home, https://github.com/dyson-ai/bencher
@@ -12,28 +12,24 @@ Requires-Python: <3.13,>=3.10
12
12
  Requires-Dist: diskcache<=5.6.3,>=5.6
13
13
  Requires-Dist: holoviews<=1.20.0,>=1.15
14
14
  Requires-Dist: hvplot<=0.10.0,>=0.8
15
- Requires-Dist: matplotlib<=3.10.0,>=3.6.3
16
- Requires-Dist: moviepy-fix-codec
17
- Requires-Dist: numpy<=2.2.2,>=1.0
18
- Requires-Dist: optuna<=4.2.0,>=3.2
15
+ Requires-Dist: moviepy<=2.1.2,>=2.1.2
16
+ Requires-Dist: numpy<=2.2.3,>=1.0
17
+ Requires-Dist: optuna<=4.2.1,>=3.2
19
18
  Requires-Dist: pandas<=2.2.3,>=2.0
20
- Requires-Dist: panel<=1.6.0,>=1.3.6
19
+ Requires-Dist: panel<=1.6.1,>=1.3.6
21
20
  Requires-Dist: param<=2.2.0,>=1.13.0
22
21
  Requires-Dist: plotly<=6.0.0,>=5.15
23
22
  Requires-Dist: scikit-learn<=1.6.1,>=1.2
24
- Requires-Dist: scoop<=0.7.2.0,>=0.7.0
25
- Requires-Dist: sortedcontainers<=2.4,>=2.4
26
- Requires-Dist: str2bool<=1.1,>=1.1
27
23
  Requires-Dist: strenum<=0.4.15,>=0.4.0
28
24
  Requires-Dist: xarray<=2025.1.2,>=2023.7
29
25
  Provides-Extra: rerun
30
26
  Requires-Dist: flask; extra == 'rerun'
31
27
  Requires-Dist: flask-cors; extra == 'rerun'
32
28
  Requires-Dist: rerun-notebook; extra == 'rerun'
33
- Requires-Dist: rerun-sdk==0.21.0; extra == 'rerun'
29
+ Requires-Dist: rerun-sdk==0.22.0; extra == 'rerun'
34
30
  Provides-Extra: test
35
- Requires-Dist: coverage<=7.6.10,>=7.5.4; extra == 'test'
36
- Requires-Dist: hypothesis<=6.124.9,>=6.104.2; extra == 'test'
31
+ Requires-Dist: coverage<=7.6.12,>=7.5.4; extra == 'test'
32
+ Requires-Dist: hypothesis<=6.125.3,>=6.104.2; extra == 'test'
37
33
  Requires-Dist: ipykernel; extra == 'test'
38
34
  Requires-Dist: jupyter-bokeh; extra == 'test'
39
35
  Requires-Dist: nbformat; extra == 'test'
@@ -42,7 +38,7 @@ Requires-Dist: pre-commit<=4.1.0; extra == 'test'
42
38
  Requires-Dist: pylint<=3.3.4,>=3.2.5; extra == 'test'
43
39
  Requires-Dist: pytest-cov<=6.0.0,>=4.1; extra == 'test'
44
40
  Requires-Dist: pytest<=8.3.4,>=7.4; extra == 'test'
45
- Requires-Dist: ruff<=0.9.4,>=0.5.0; extra == 'test'
41
+ Requires-Dist: ruff<=0.9.6,>=0.5.0; extra == 'test'
46
42
  Description-Content-Type: text/markdown
47
43
 
48
44
  # Bencher
@@ -6,7 +6,6 @@ import logging
6
6
  from typing import List
7
7
 
8
8
  import param
9
- from str2bool import str2bool
10
9
  import panel as pn
11
10
  from datetime import datetime
12
11
 
@@ -197,11 +196,8 @@ class BenchRunCfg(BenchPlotSrvCfg):
197
196
 
198
197
  parser.add_argument(
199
198
  "--nightly",
200
- type=lambda b: bool(str2bool(b)),
201
- nargs="?",
202
- const=False,
203
- default=False,
204
- help="turn on nightly benchmarking",
199
+ action="store_true",
200
+ help="Turn on nightly benchmarking",
205
201
  )
206
202
 
207
203
  parser.add_argument(
@@ -351,7 +347,7 @@ class BenchCfg(BenchRunCfg):
351
347
  latex = self.to_latex()
352
348
  desc = pn.pane.Markdown(self.describe_benchmark(), width=width)
353
349
  if accordion:
354
- desc = pn.Accordion(("Data Collection Parameters", desc))
350
+ desc = pn.Accordion(("Expand Full Data Collection Parameters", desc))
355
351
 
356
352
  sentence = self.sweep_sentence()
357
353
  if latex is not None:
@@ -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)
@@ -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()
@@ -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,9 +1,24 @@
1
1
  import bencher as bch
2
2
  import numpy as np
3
- import matplotlib.pyplot as plt
3
+ from PIL import Image
4
+ import colorcet as cc
5
+ import numpy.typing as npt
6
+
7
+
8
+ def apply_colormap(data: npt.NDArray) -> npt.NDArray:
9
+ """Apply a perceptually uniform colormap to the data"""
10
+ # Normalize data to [0, 1]
11
+ normalized = (data - data.min()) / (data.max() - data.min())
12
+ # Convert hex colors to RGB values using numpy's frombuffer
13
+ colors = np.array(
14
+ [np.frombuffer(bytes.fromhex(c.lstrip("#")), dtype=np.uint8) for c in cc.rainbow]
15
+ )
16
+ # Map normalized values to colormap indices
17
+ indices = (normalized * (len(colors) - 1)).astype(int)
18
+ # Create RGB array from the colormap
19
+ return colors[indices]
4
20
 
5
21
 
6
- # code from https://ipython-books.github.io/124-simulating-a-partial-differential-equation-reaction-diffusion-systems-and-turing-patterns/
7
22
  class TuringPattern(bch.ParametrizedSweep):
8
23
  alpha = bch.FloatSweep(default=2.8e-4, bounds=(2e-4, 5e-3))
9
24
  beta = bch.FloatSweep(default=5e-3, bounds=(1e-3, 9e-3))
@@ -17,6 +32,7 @@ class TuringPattern(bch.ParametrizedSweep):
17
32
  video = bch.ResultVideo()
18
33
  score = bch.ResultVar()
19
34
  img = bch.ResultImage()
35
+ img_extracted = bch.ResultImage()
20
36
 
21
37
  def laplacian(self, Z, dx):
22
38
  Ztop = Z[0:-2, 1:-1]
@@ -49,28 +65,28 @@ class TuringPattern(bch.ParametrizedSweep):
49
65
  def __call__(self, **kwargs):
50
66
  self.update_params_from_kwargs(**kwargs)
51
67
 
52
- n = int(self.time / self.dt) # number of iterations
53
- dx = 2.0 / self.size # space step
68
+ n = int(self.time / self.dt)
69
+ dx = 2.0 / self.size
54
70
 
55
71
  U = np.random.rand(self.size, self.size)
56
72
  V = np.random.rand(self.size, self.size)
57
73
 
58
- fig, ax = plt.subplots(frameon=False, figsize=(2, 2))
59
- fig.set_tight_layout(True)
60
- ax.set_axis_off()
61
74
  vid_writer = bch.VideoWriter()
62
75
  for i in range(n):
63
76
  self.update(U, V, dx)
64
77
  if i % 500 == 0:
65
- ax.imshow(U)
66
- fig.canvas.draw()
67
- rgb = np.array(fig.canvas.renderer.buffer_rgba())
68
- vid_writer.append(rgb)
69
-
70
- self.img = bch.add_image(rgb)
71
-
78
+ # Apply colormap to create RGB image
79
+ rgb = apply_colormap(U)
80
+ # Create PIL image with alpha channel
81
+ img = Image.fromarray(rgb, "RGB").convert("RGBA")
82
+ img = img.resize((200, 200), Image.Resampling.LANCZOS)
83
+ rgb_alpha = np.array(img)
84
+ vid_writer.append(rgb_alpha)
85
+
86
+ self.img = bch.add_image(rgb_alpha)
72
87
  self.video = vid_writer.write()
73
-
88
+ self.img_extracted = bch.video_writer.VideoWriter.extract_frame(self.video)
89
+ print("img path", self.img_extracted)
74
90
  self.score = self.alpha + self.beta
75
91
  return super().__call__()
76
92
 
@@ -109,8 +125,8 @@ def example_video_tap(
109
125
  if __name__ == "__main__":
110
126
  run_cfg_ex = bch.BenchRunCfg()
111
127
  run_cfg_ex.level = 2
112
- run_cfg_ex.cache_samples = True
113
- run_cfg_ex.only_hash_tag = True
128
+ # run_cfg_ex.cache_samples = True
129
+ # run_cfg_ex.only_hash_tag = True
114
130
 
115
131
  # example_video(run_cfg_ex).report.show()
116
132
  example_video_tap(run_cfg_ex).report.show()
@@ -1,6 +1,5 @@
1
1
  # THIS IS NOT A WORKING EXAMPLE YET
2
2
  # pylint: disable=duplicate-code
3
- import hvplot
4
3
  import bencher as bch
5
4
  from bencher import ExampleBenchCfgIn, ExampleBenchCfgOut, bench_function
6
5
 
@@ -35,5 +34,5 @@ if __name__ == "__main__":
35
34
  ),
36
35
  )
37
36
 
38
- hvexplorer = hvplot.explorer(bench_out.get_dataframe())
39
- hvexplorer.show()
37
+ bench.report.append(bench_out.to_explorer())
38
+ bench.report.show()
@@ -0,0 +1,12 @@
1
+ import bencher as bch
2
+ from bencher.example.meta.example_meta import BenchableObject
3
+
4
+ run_cfg = bch.BenchRunCfg()
5
+ run_cfg.repeats = 20
6
+ run_cfg.level = 4
7
+ bench = BenchableObject().to_bench(run_cfg)
8
+ res = bench.plot_sweep(
9
+ input_vars=["float1", "noisy", "noise_distribution"], result_vars=["distance", "sample_noise"]
10
+ )
11
+
12
+ bench.report.show()
@@ -48,3 +48,9 @@ if __name__ == "__main__":
48
48
  convert_example_to_jupyter_notebook(
49
49
  "/workspaces/bencher/bencher/example/inputs_1D/example_1_in_2_out_repeats.py", "1D"
50
50
  )
51
+
52
+ # todo, enable
53
+ # convert_example_to_jupyter_notebook(
54
+ # "/workspaces/bencher/bencher/example/example_composable_container_video.py",
55
+ # "Media",
56
+ # )
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
  from typing import Callable
3
- from sortedcontainers import SortedDict
4
3
  import logging
5
4
  from diskcache import Cache
6
5
  from concurrent.futures import Future, ProcessPoolExecutor
@@ -11,7 +10,6 @@ from enum import auto
11
10
  try:
12
11
  from scoop import futures as scoop_future_executor
13
12
  except ImportError as e:
14
- logging.warning(e.msg)
15
13
  scoop_future_executor = None
16
14
 
17
15
 
@@ -23,7 +21,7 @@ class Job:
23
21
  self.function = function
24
22
  self.job_args = job_args
25
23
  if job_key is None:
26
- self.job_key = hash_sha1(tuple(SortedDict(self.job_args).items()))
24
+ self.job_key = hash_sha1(tuple(sorted(self.job_args.items())))
27
25
  else:
28
26
  self.job_key = job_key
29
27
  self.tag = tag
@@ -53,6 +53,7 @@ class BenchResult(PlotlyResult, HoloviewResult, HvplotResult, VideoSummaryResult
53
53
  self,
54
54
  plot_list: List[callable] = None,
55
55
  remove_plots: List[callable] = None,
56
+ default_container=pn.Column,
56
57
  **kwargs,
57
58
  ) -> List[pn.panel]:
58
59
  self.plt_cnt_cfg.print_debug = False
@@ -67,7 +68,7 @@ class BenchResult(PlotlyResult, HoloviewResult, HvplotResult, VideoSummaryResult
67
68
 
68
69
  kwargs = self.set_plot_size(**kwargs)
69
70
 
70
- row = EmptyContainer(pn.Row())
71
+ row = EmptyContainer(default_container())
71
72
  for plot_callback in plot_list:
72
73
  if self.plt_cnt_cfg.print_debug:
73
74
  print(f"checking: {plot_callback.__name__}")
@@ -321,7 +321,7 @@ class BenchResultBase(OptunaResult):
321
321
  **kwargs,
322
322
  ) -> Optional[pn.Row]:
323
323
  if hv_dataset is None:
324
- hv_dataset = self.to_hv_dataset()
324
+ hv_dataset = self.to_hv_dataset(reduce=reduce)
325
325
 
326
326
  if pane_collection is None:
327
327
  pane_collection = pn.Row()
@@ -333,7 +333,7 @@ class BenchResultBase(OptunaResult):
333
333
  if result_types is None or isinstance(rv, result_types):
334
334
  row.append(
335
335
  self.to_panes_multi_panel(
336
- self.to_hv_dataset(reduce=reduce, result_var=rv),
336
+ hv_dataset,
337
337
  rv,
338
338
  plot_callback=partial(plot_callback, **kwargs),
339
339
  target_dimension=target_dimension,
@@ -3,7 +3,7 @@ import numpy as np
3
3
  from copy import deepcopy
4
4
  from pathlib import Path
5
5
  from dataclasses import dataclass
6
- from moviepy.editor import (
6
+ from moviepy import (
7
7
  ImageClip,
8
8
  CompositeVideoClip,
9
9
  clips_array,
@@ -11,22 +11,43 @@ from moviepy.editor import (
11
11
  VideoClip,
12
12
  VideoFileClip,
13
13
  )
14
- from moviepy.video.fx.margin import margin
15
14
 
16
15
  from bencher.results.composable_container.composable_container_base import (
17
16
  ComposableContainerBase,
18
17
  ComposeType,
19
18
  )
20
19
  from bencher.video_writer import VideoWriter
20
+ from moviepy import vfx
21
21
 
22
22
 
23
23
  @dataclass()
24
24
  class RenderCfg:
25
+ """Configuration class for video rendering options.
26
+
27
+ This class controls how videos and images are composed and rendered together.
28
+ It provides options for timing, layout, appearance, and labeling of the output.
29
+
30
+ Attributes:
31
+ compose_method (ComposeType): Method to compose multiple clips (sequence, right, down, overlay).
32
+ Defaults to ComposeType.sequence.
33
+ var_name (str, optional): Variable name for labeling. Defaults to None.
34
+ var_value (str, optional): Variable value for labeling. Defaults to None.
35
+ background_col (tuple[int, int, int]): RGB color for background. Defaults to white (255, 255, 255).
36
+ duration (float): Target duration for the composed video in seconds. Defaults to 10.0.
37
+ default_duration (float): Fallback duration when duration is None. Defaults to 10.0.
38
+ duration_target (bool): If True, tries to match target duration while respecting frame
39
+ duration constraints. If False, uses exact duration. Defaults to True.
40
+ min_frame_duration (float): Minimum duration for each frame in seconds. Defaults to 1/30.
41
+ max_frame_duration (float): Maximum duration for each frame in seconds. Defaults to 2.0.
42
+ margin (int): Margin size in pixels to add around clips. Defaults to 0.
43
+ """
44
+
25
45
  compose_method: ComposeType = ComposeType.sequence
26
46
  var_name: str = None
27
47
  var_value: str = None
28
48
  background_col: tuple[int, int, int] = (255, 255, 255)
29
49
  duration: float = 10.0
50
+ default_duration: float = 10.0
30
51
  duration_target: bool = True
31
52
  min_frame_duration: float = 1.0 / 30
32
53
  max_frame_duration: float = 2.0
@@ -69,7 +90,9 @@ class ComposableContainerVideo(ComposableContainerBase):
69
90
  def calculate_duration(self, frames, render_cfg: RenderCfg):
70
91
  if render_cfg.duration_target:
71
92
  # calculate duration based on fps constraints
72
- duration = 10.0 if render_cfg.duration is None else render_cfg.duration
93
+ duration = (
94
+ render_cfg.default_duration if render_cfg.duration is None else render_cfg.duration
95
+ )
73
96
  frame_duration = duration / frames
74
97
  if render_cfg.min_frame_duration is not None:
75
98
  frame_duration = max(frame_duration, render_cfg.min_frame_duration)
@@ -77,7 +100,10 @@ class ComposableContainerVideo(ComposableContainerBase):
77
100
  frame_duration = min(frame_duration, render_cfg.max_frame_duration)
78
101
  duration = frame_duration * frames
79
102
  else:
80
- duration = render_cfg.duration
103
+ if render_cfg.duration is None:
104
+ duration = render_cfg.default_duration
105
+ else:
106
+ duration = render_cfg.duration
81
107
  frame_duration = duration / float(frames)
82
108
 
83
109
  print("max_frame_duration", render_cfg.max_frame_duration)
@@ -111,8 +137,8 @@ class ComposableContainerVideo(ComposableContainerBase):
111
137
  case ComposeType.right | ComposeType.down:
112
138
  for i in range(len(self.container)):
113
139
  self.container[i] = self.extend_clip(self.container[i], max_duration)
114
- self.container[i] = margin(
115
- self.container[i], top=render_cfg.margin, color=render_cfg.background_col
140
+ self.container[i] = self.container[i].with_effects(
141
+ [vfx.Margin(top=render_cfg.margin, color=render_cfg.background_col)]
116
142
  )
117
143
 
118
144
  if render_cfg.compose_method == ComposeType.right:
@@ -126,13 +152,14 @@ class ComposableContainerVideo(ComposableContainerBase):
126
152
  out = concatenate_videoclips(
127
153
  self.container, bg_color=render_cfg.background_col, method="compose"
128
154
  )
129
- # case ComposeType.overlay:
130
- # for i in range(len(self.container)):
131
- # self.container[i].alpha = 1./len(self.container)
132
- # out = CompositeVideoClip(self.container, bg_color=render_cfg.background_col)
133
- # # out.duration = fps
155
+ case ComposeType.overlay:
156
+ for i in range(len(self.container)):
157
+ self.container[i] = self.container[i].with_opacity(1.0 / len(self.container))
158
+ out = CompositeVideoClip(self.container, bg_color=render_cfg.background_col)
134
159
  case _:
135
- raise RuntimeError("This compose type is not supported")
160
+ raise RuntimeError(
161
+ f"This compose type is not supported: {render_cfg.compose_method}"
162
+ )
136
163
 
137
164
  label = self.label_formatter(render_cfg.var_name, render_cfg.var_value)
138
165
  if label is not None: