holobench 1.41.0__py3-none-any.whl → 1.43.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. bencher/__init__.py +20 -2
  2. bencher/bench_cfg.py +262 -54
  3. bencher/bench_report.py +2 -2
  4. bencher/bench_runner.py +96 -10
  5. bencher/bencher.py +421 -89
  6. bencher/class_enum.py +70 -7
  7. bencher/example/example_dataframe.py +2 -2
  8. bencher/example/example_levels.py +17 -173
  9. bencher/example/example_pareto.py +107 -31
  10. bencher/example/example_rerun2.py +1 -1
  11. bencher/example/example_simple_bool.py +2 -2
  12. bencher/example/example_simple_float2d.py +6 -1
  13. bencher/example/example_video.py +2 -0
  14. bencher/example/experimental/example_hvplot_explorer.py +2 -2
  15. bencher/example/inputs_0D/example_0_in_1_out.py +25 -15
  16. bencher/example/inputs_0D/example_0_in_2_out.py +12 -3
  17. bencher/example/inputs_0_float/example_0_cat_in_2_out.py +88 -0
  18. bencher/example/inputs_0_float/example_1_cat_in_2_out.py +98 -0
  19. bencher/example/inputs_0_float/example_2_cat_in_2_out.py +107 -0
  20. bencher/example/inputs_0_float/example_3_cat_in_2_out.py +111 -0
  21. bencher/example/inputs_1D/example1d_common.py +48 -12
  22. bencher/example/inputs_1D/example_0_float_1_cat.py +33 -0
  23. bencher/example/inputs_1D/example_1_cat_in_2_out_repeats.py +68 -0
  24. bencher/example/inputs_1D/example_1_float_2_cat_repeats.py +3 -0
  25. bencher/example/inputs_1D/example_1_int_in_1_out.py +98 -0
  26. bencher/example/inputs_1D/example_1_int_in_2_out.py +101 -0
  27. bencher/example/inputs_1D/example_1_int_in_2_out_repeats.py +99 -0
  28. bencher/example/inputs_1_float/example_1_float_0_cat_in_2_out.py +117 -0
  29. bencher/example/inputs_1_float/example_1_float_1_cat_in_2_out.py +124 -0
  30. bencher/example/inputs_1_float/example_1_float_2_cat_in_2_out.py +132 -0
  31. bencher/example/inputs_1_float/example_1_float_3_cat_in_2_out.py +140 -0
  32. bencher/example/inputs_2D/example_2_cat_in_4_out_repeats.py +104 -0
  33. bencher/example/inputs_2_float/example_2_float_0_cat_in_2_out.py +98 -0
  34. bencher/example/inputs_2_float/example_2_float_1_cat_in_2_out.py +112 -0
  35. bencher/example/inputs_2_float/example_2_float_2_cat_in_2_out.py +122 -0
  36. bencher/example/inputs_2_float/example_2_float_3_cat_in_2_out.py +138 -0
  37. bencher/example/inputs_3_float/example_3_float_0_cat_in_2_out.py +111 -0
  38. bencher/example/inputs_3_float/example_3_float_1_cat_in_2_out.py +117 -0
  39. bencher/example/inputs_3_float/example_3_float_2_cat_in_2_out.py +124 -0
  40. bencher/example/inputs_3_float/example_3_float_3_cat_in_2_out.py +129 -0
  41. bencher/example/meta/generate_examples.py +118 -7
  42. bencher/example/meta/generate_meta.py +88 -40
  43. bencher/job.py +174 -9
  44. bencher/plotting/plot_filter.py +52 -17
  45. bencher/results/bench_result.py +117 -25
  46. bencher/results/bench_result_base.py +117 -8
  47. bencher/results/dataset_result.py +6 -200
  48. bencher/results/explorer_result.py +23 -0
  49. bencher/results/{hvplot_result.py → histogram_result.py} +3 -18
  50. bencher/results/holoview_results/__init__.py +0 -0
  51. bencher/results/holoview_results/bar_result.py +79 -0
  52. bencher/results/holoview_results/curve_result.py +110 -0
  53. bencher/results/holoview_results/distribution_result/__init__.py +0 -0
  54. bencher/results/holoview_results/distribution_result/box_whisker_result.py +73 -0
  55. bencher/results/holoview_results/distribution_result/distribution_result.py +109 -0
  56. bencher/results/holoview_results/distribution_result/scatter_jitter_result.py +92 -0
  57. bencher/results/holoview_results/distribution_result/violin_result.py +70 -0
  58. bencher/results/holoview_results/heatmap_result.py +319 -0
  59. bencher/results/holoview_results/holoview_result.py +346 -0
  60. bencher/results/holoview_results/line_result.py +240 -0
  61. bencher/results/holoview_results/scatter_result.py +107 -0
  62. bencher/results/holoview_results/surface_result.py +158 -0
  63. bencher/results/holoview_results/table_result.py +14 -0
  64. bencher/results/holoview_results/tabulator_result.py +20 -0
  65. bencher/results/optuna_result.py +30 -115
  66. bencher/results/video_controls.py +38 -0
  67. bencher/results/video_result.py +39 -36
  68. bencher/results/video_summary.py +2 -2
  69. bencher/results/{plotly_result.py → volume_result.py} +29 -8
  70. bencher/utils.py +175 -26
  71. bencher/variables/inputs.py +122 -15
  72. bencher/video_writer.py +2 -1
  73. bencher/worker_job.py +31 -3
  74. {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/METADATA +24 -24
  75. holobench-1.43.0.dist-info/RECORD +147 -0
  76. bencher/example/example_levels2.py +0 -37
  77. bencher/example/inputs_1D/example_1_in_1_out.py +0 -62
  78. bencher/example/inputs_1D/example_1_in_2_out.py +0 -63
  79. bencher/example/inputs_1D/example_1_in_2_out_repeats.py +0 -61
  80. bencher/results/holoview_result.py +0 -796
  81. bencher/results/panel_result.py +0 -41
  82. holobench-1.41.0.dist-info/RECORD +0 -114
  83. {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/WHEEL +0 -0
  84. {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,140 @@
1
+ """This file demonstrates benchmarking with 1 float and 3 categorical inputs with 2 output variables.
2
+
3
+ It benchmarks different algorithmic configurations to compare their performance characteristics
4
+ using simulated performance data to illustrate how benchmarking works.
5
+ """
6
+
7
+ import random
8
+ import math
9
+ import bencher as bch
10
+
11
+ random.seed(0)
12
+
13
+
14
+ class AlgorithmBenchmark(bch.ParametrizedSweep):
15
+ """Example class for benchmarking algorithm performance with various parameters.
16
+
17
+ This class demonstrates how to structure a benchmark with one float parameter and
18
+ three categorical parameters, producing multiple output metrics. It uses simulated
19
+ performance data that follows realistic patterns while being deterministic.
20
+ """
21
+
22
+ # Float input parameter
23
+ problem_size = bch.FloatSweep(default=100, bounds=[1, 100], doc="Size of the problem to solve")
24
+
25
+ # Categorical input parameters
26
+ algorithm_type = bch.StringSweep(
27
+ ["recursive", "iterative"], doc="Type of algorithm implementation"
28
+ )
29
+ data_structure = bch.StringSweep(
30
+ ["array", "linked_list"], doc="Underlying data structure to use"
31
+ )
32
+ optimization_level = bch.StringSweep(
33
+ ["none", "basic", "advanced"], doc="Level of optimization applied"
34
+ )
35
+
36
+ # Output metrics
37
+ execution_time = bch.ResultVar(units="ms", doc="Execution time in milliseconds")
38
+ memory_usage = bch.ResultVar(units="MB", doc="Memory usage in megabytes")
39
+
40
+ def __call__(self, **kwargs) -> dict:
41
+ """Execute the benchmark for the given set of parameters.
42
+
43
+ Args:
44
+ **kwargs: Parameters to update before executing
45
+
46
+ Returns:
47
+ dict: Dictionary containing the benchmark results
48
+ """
49
+ self.update_params_from_kwargs(**kwargs)
50
+
51
+ # Base values for calculation
52
+ base_time = 1.0 # ms
53
+ base_memory = 0.1 # MB
54
+
55
+ # Size factor (non-linear relationship with problem size)
56
+ size_factor_time = math.log10(self.problem_size) ** 1.5
57
+ size_factor_memory = math.sqrt(self.problem_size) / 10
58
+
59
+ # Algorithm type factor
60
+ if self.algorithm_type == "recursive":
61
+ algo_time_factor = 1.2 # Recursive is slower
62
+ algo_memory_factor = 1.5 # Recursive uses more memory (stack)
63
+ else: # iterative
64
+ algo_time_factor = 0.8 # Iterative is faster
65
+ algo_memory_factor = 0.7 # Iterative uses less memory
66
+
67
+ # Data structure factor
68
+ if self.data_structure == "array":
69
+ ds_time_factor = 0.9 # Arrays have faster access
70
+ ds_memory_factor = 1.1 # Arrays use slightly more contiguous memory
71
+ else: # linked_list
72
+ ds_time_factor = 1.3 # Linked lists have slower access
73
+ ds_memory_factor = 0.9 # Linked lists might use less memory
74
+
75
+ # Optimization level factor
76
+ if self.optimization_level == "none":
77
+ opt_time_factor = 1.5
78
+ opt_memory_factor = 0.8
79
+ elif self.optimization_level == "basic":
80
+ opt_time_factor = 1.0
81
+ opt_memory_factor = 1.0
82
+ else: # advanced
83
+ opt_time_factor = 0.6
84
+ opt_memory_factor = 1.2 # Better time but more memory usage
85
+
86
+ # Calculate final metrics with some random variation
87
+ time_multiplier = (
88
+ algo_time_factor * ds_time_factor * opt_time_factor * random.uniform(0.9, 1.1)
89
+ )
90
+ memory_multiplier = (
91
+ algo_memory_factor * ds_memory_factor * opt_memory_factor * random.uniform(0.95, 1.05)
92
+ )
93
+
94
+ self.execution_time = base_time * size_factor_time * time_multiplier
95
+ self.memory_usage = base_memory * size_factor_memory * memory_multiplier
96
+
97
+ return super().__call__(**kwargs)
98
+
99
+
100
+ def example_1_float_3_cat_in_2_out(
101
+ run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None
102
+ ) -> bch.Bench:
103
+ """This example demonstrates benchmarking with 1 float and 3 categorical inputs.
104
+
105
+ It creates a synthetic benchmark that simulates performance characteristics of different
106
+ algorithm configurations, varying problem size (float), algorithm type, data structure,
107
+ and optimization level. The benchmark produces realistic patterns of execution time
108
+ and memory usage without actually executing real algorithms.
109
+
110
+ Args:
111
+ run_cfg: Configuration for the benchmark run
112
+ report: Report to append the results to
113
+
114
+ Returns:
115
+ bch.Bench: The benchmark object
116
+ """
117
+
118
+ if run_cfg is None:
119
+ run_cfg = bch.BenchRunCfg()
120
+ run_cfg.repeats = 3 # Fewer repeats for a quicker benchmark
121
+ bench = AlgorithmBenchmark().to_bench(run_cfg, report)
122
+ bench.plot_sweep(
123
+ title="Algorithm Performance Benchmark (1 Float, 3 Categorical Variables)",
124
+ description="Comparing execution time and memory usage across problem sizes and algorithm configurations",
125
+ post_description="""
126
+ This benchmark illustrates how different algorithm configurations affect performance across problem sizes.
127
+
128
+ Key observations:
129
+ - Execution time generally increases non-linearly with problem size
130
+ - Iterative algorithms typically outperform recursive ones in both time and memory
131
+ - Arrays provide faster access than linked lists but may use more memory
132
+ - Advanced optimization improves speed but can increase memory usage
133
+ - Performance characteristics vary significantly based on the combination of all parameters
134
+ """,
135
+ )
136
+ return bench
137
+
138
+
139
+ if __name__ == "__main__":
140
+ example_1_float_3_cat_in_2_out().report.show()
@@ -0,0 +1,104 @@
1
+ """This file demonstrates benchmarking with categorical inputs and multiple outputs with repeats.
2
+
3
+ It simulates comparing programming languages and development environments, measuring
4
+ performance and developer productivity metrics.
5
+ """
6
+
7
+ import random
8
+ import bencher as bch
9
+
10
+ random.seed(42) # Fixed seed for reproducibility
11
+
12
+
13
+ class ProgrammingBenchmark(bch.ParametrizedSweep):
14
+ """Benchmark class comparing programming languages and development environments."""
15
+
16
+ language = bch.StringSweep(
17
+ ["Python", "JavaScript", "Rust", "Go"], doc="Programming language being benchmarked"
18
+ )
19
+ environment = bch.StringSweep(
20
+ ["Development", "Testing", "Production"], doc="Environment configuration"
21
+ )
22
+
23
+ execution_time = bch.ResultVar(units="ms", doc="Execution time in milliseconds")
24
+ memory_usage = bch.ResultVar(units="MB", doc="Memory usage in megabytes")
25
+
26
+ def __call__(self, **kwargs) -> dict:
27
+ """Execute the parameter sweep for the given inputs.
28
+
29
+ Args:
30
+ **kwargs: Additional parameters to update before executing
31
+
32
+ Returns:
33
+ dict: Dictionary containing the outputs of the parameter sweep
34
+ """
35
+ self.update_params_from_kwargs(**kwargs)
36
+
37
+ # Base values that will be modified by language and environment
38
+ base_execution = 0
39
+ base_memory = 0
40
+
41
+ # Different languages have different performance characteristics
42
+ if self.language == "Python":
43
+ base_execution = 150
44
+ base_memory = 80
45
+ elif self.language == "JavaScript":
46
+ base_execution = 100
47
+ base_memory = 60
48
+ elif self.language == "Rust":
49
+ base_execution = 20
50
+ base_memory = 15
51
+ elif self.language == "Go":
52
+ base_execution = 40
53
+ base_memory = 30
54
+
55
+ # Environment affects performance
56
+ if self.environment == "Development":
57
+ # Dev environments have debugging overhead
58
+ env_exec_modifier = 1.5
59
+ env_mem_modifier = 1.3
60
+ elif self.environment == "Testing":
61
+ # Testing has moderate overhead
62
+ env_exec_modifier = 1.2
63
+ env_mem_modifier = 1.1
64
+ else: # Production
65
+ # Production is optimized
66
+ env_exec_modifier = 1.0
67
+ env_mem_modifier = 1.0
68
+
69
+ # Calculate final values with some randomness
70
+ self.execution_time = base_execution * env_exec_modifier * random.uniform(0.9, 1.1)
71
+ self.memory_usage = base_memory * env_mem_modifier * random.uniform(0.95, 1.05)
72
+
73
+ return super().__call__(**kwargs)
74
+
75
+
76
+ def example_2_cat_in_4_out_repeats(
77
+ run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None
78
+ ) -> bch.Bench:
79
+ """This example compares performance metrics across programming languages and environments.
80
+
81
+ It demonstrates how to sample categorical variables with multiple repeats
82
+ and plot the results of two output variables.
83
+
84
+ Args:
85
+ run_cfg: Configuration for the benchmark run
86
+ report: Report to append the results to
87
+
88
+ Returns:
89
+ bch.Bench: The benchmark object
90
+ """
91
+
92
+ if run_cfg is None:
93
+ run_cfg = bch.BenchRunCfg()
94
+ run_cfg.repeats = 15 # Run multiple times to get statistical significance
95
+ bench = ProgrammingBenchmark().to_bench(run_cfg, report)
96
+ bench.plot_sweep(
97
+ title="Programming Language and Environment Performance Metrics",
98
+ description="Comparing execution time and memory usage across different programming languages and environments",
99
+ )
100
+ return bench
101
+
102
+
103
+ if __name__ == "__main__":
104
+ example_2_cat_in_4_out_repeats().report.show()
@@ -0,0 +1,98 @@
1
+ """Demonstration of benchmarking with 2 float inputs and 0 categorical inputs producing a distinct pattern.
2
+
3
+ The two float inputs create a visually distinctive surface shape.
4
+ """
5
+
6
+ import random
7
+ import math
8
+ import bencher as bch
9
+
10
+ random.seed(0)
11
+
12
+
13
+ class Pattern0CatBenchmark(bch.ParametrizedSweep):
14
+ """Benchmark demonstrating patterns with two float inputs and no categorical variables."""
15
+
16
+ # Float input parameters
17
+ x_value = bch.FloatSweep(default=100, bounds=[1, 100], doc="X value parameter")
18
+ y_value = bch.FloatSweep(default=10, bounds=[1, 100], doc="Y value parameter")
19
+
20
+ # No categorical input parameters
21
+
22
+ # Output metrics
23
+ response_a = bch.ResultVar(units="units", doc="Response variable A")
24
+ response_b = bch.ResultVar(units="units", doc="Response variable B")
25
+
26
+ def __call__(self, **kwargs) -> dict:
27
+ """Generate responses with a distinctive pattern based on x and y values."""
28
+ self.update_params_from_kwargs(**kwargs)
29
+
30
+ # Normalize inputs to [0,1]
31
+ x = self.x_value / 100
32
+ y = self.y_value / 100
33
+
34
+ # Using fixed "linear" pattern type
35
+ base_a = 2 * x + 3 * y
36
+ base_b = 3 * x - y
37
+
38
+ # Using fixed "symmetric" symmetry type
39
+ sym_a = (x + y) ** 2
40
+ sym_b = (x + y) * abs(x - y)
41
+
42
+ # Using fixed "smooth" feature type
43
+ feat_a = math.sin(3 * math.pi * x) * math.sin(3 * math.pi * y)
44
+ feat_b = math.cos(3 * math.pi * x) * math.cos(3 * math.pi * y)
45
+
46
+ # Fixed weights for valley pattern
47
+ w_a = [1, 2, 0.5]
48
+ w_b = [1, 1.5, 0.3]
49
+
50
+ # Calculate final responses with weights
51
+ self.response_a = w_a[0] * base_a + w_a[1] * sym_a + w_a[2] * feat_a
52
+ self.response_b = w_b[0] * base_b + w_b[1] * sym_b + w_b[2] * feat_b
53
+
54
+ # Add minimal randomness (to maintain pattern visibility)
55
+ random_factor = random.uniform(0.98, 1.02)
56
+ self.response_a *= random_factor
57
+ self.response_b *= random_factor
58
+
59
+ return super().__call__(**kwargs)
60
+
61
+
62
+ def example_2_float_0_cat_in_2_out(
63
+ run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None
64
+ ) -> bch.Bench:
65
+ """Benchmark demonstrating a surface pattern based solely on float parameters.
66
+
67
+ This example is simplified from the 1-category version by fixing all categorical
68
+ settings to create a valley pattern.
69
+
70
+ Args:
71
+ run_cfg: Configuration for the benchmark run
72
+ report: Report to append the results to
73
+
74
+ Returns:
75
+ bch.Bench: The benchmark object
76
+ """
77
+ if run_cfg is None:
78
+ run_cfg = bch.BenchRunCfg()
79
+ run_cfg.repeats = 3 # Fewer repeats for a quicker benchmark
80
+
81
+ bench = Pattern0CatBenchmark().to_bench(run_cfg, report)
82
+ bench.plot_sweep(
83
+ title="Pattern Visualization (2 Float, 0 Categorical Variables)",
84
+ description="Response pattern with a valley shape based on x and y values",
85
+ post_description="""
86
+ This example demonstrates a surface pattern created using only two float inputs
87
+ (x_value and y_value) with no categorical variables.
88
+
89
+ The pattern showcases a 'valley' shape that exhibits different characteristics
90
+ across the range of x and y values. This simplified example uses fixed settings
91
+ equivalent to a linear pattern with symmetric properties and smooth features.
92
+ """,
93
+ )
94
+ return bench
95
+
96
+
97
+ if __name__ == "__main__":
98
+ example_2_float_0_cat_in_2_out().report.show()
@@ -0,0 +1,112 @@
1
+ """Demonstration of benchmarking with 2 float and 1 categorical input producing visually distinct patterns.
2
+
3
+ The categorical input has 2 conditions that create distinctly different surface shapes.
4
+ """
5
+
6
+ import random
7
+ import math
8
+ import bencher as bch
9
+
10
+ random.seed(0)
11
+
12
+
13
+ class Pattern1CatBenchmark(bch.ParametrizedSweep):
14
+ """Benchmark demonstrating patterns with distinctive shapes based on pattern type."""
15
+
16
+ # Float input parameters
17
+ x_value = bch.FloatSweep(default=100, bounds=[1, 100], doc="X value parameter")
18
+ y_value = bch.FloatSweep(default=10, bounds=[1, 100], doc="Y value parameter")
19
+
20
+ # Categorical input parameter - with 2 conditions
21
+ pattern_type = bch.StringSweep(["linear", "exponential"], doc="Pattern relationship")
22
+ # symmetry_type and feature_type removed
23
+
24
+ # Output metrics
25
+ response_a = bch.ResultVar(units="units", doc="Response variable A")
26
+ response_b = bch.ResultVar(units="units", doc="Response variable B")
27
+
28
+ def __call__(self, **kwargs) -> dict:
29
+ """Generate responses with distinctly different patterns based on pattern type."""
30
+ self.update_params_from_kwargs(**kwargs)
31
+
32
+ # Normalize inputs to [0,1]
33
+ x = self.x_value / 100
34
+ y = self.y_value / 100
35
+
36
+ # Set base patterns based on pattern_type
37
+ if self.pattern_type == "linear":
38
+ base_a = 2 * x + 3 * y
39
+ base_b = 3 * x - y
40
+ else: # exponential
41
+ base_a = math.exp(2 * x) * math.exp(y) / math.exp(3)
42
+ base_b = math.exp(x) * math.exp(2 * y) / math.exp(3)
43
+
44
+ # Using fixed "symmetric" symmetry type
45
+ sym_a = (x + y) ** 2
46
+ sym_b = (x + y) * abs(x - y)
47
+
48
+ # Using fixed "smooth" feature type
49
+ feat_a = math.sin(3 * math.pi * x) * math.sin(3 * math.pi * y)
50
+ feat_b = math.cos(3 * math.pi * x) * math.cos(3 * math.pi * y)
51
+
52
+ # Simplified weights dictionary for 1 categorical variable
53
+ weights = {
54
+ "linear": ([1, 2, 0.5], [1, 1.5, 0.3]), # Valley pattern
55
+ "exponential": ([1, 1, 0.3], [1, 0.8, 0.4]), # Corner peak with ripples
56
+ }
57
+
58
+ # Get the weights for the current pattern type
59
+ w_a, w_b = weights[self.pattern_type]
60
+
61
+ # Calculate final responses with weights
62
+ if self.pattern_type == "linear":
63
+ self.response_a = w_a[0] * base_a + w_a[1] * sym_a + w_a[2] * feat_a
64
+ self.response_b = w_b[0] * base_b + w_b[1] * sym_b + w_b[2] * feat_b
65
+ else: # exponential - multiplicative relationship
66
+ self.response_a = base_a * (1 + w_a[1] * sym_a) * (1 + w_a[2] * feat_a)
67
+ self.response_b = base_b * (1 + w_b[1] * sym_b) * (1 + w_b[2] * feat_b)
68
+
69
+ # Add minimal randomness (to maintain pattern visibility)
70
+ random_factor = random.uniform(0.98, 1.02)
71
+ self.response_a *= random_factor
72
+ self.response_b *= random_factor
73
+
74
+ return super().__call__(**kwargs)
75
+
76
+
77
+ def example_2_float_1_cat_in_2_out(
78
+ run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None
79
+ ) -> bch.Bench:
80
+ """Benchmark demonstrating visually distinct patterns based on pattern type.
81
+
82
+ This example is simplified from the 2-category version by fixing the symmetry_type
83
+ to "symmetric" and feature_type to "smooth".
84
+
85
+ Args:
86
+ run_cfg: Configuration for the benchmark run
87
+ report: Report to append the results to
88
+
89
+ Returns:
90
+ bch.Bench: The benchmark object
91
+ """
92
+ if run_cfg is None:
93
+ run_cfg = bch.BenchRunCfg()
94
+ run_cfg.repeats = 3 # Fewer repeats for a quicker benchmark
95
+
96
+ bench = Pattern1CatBenchmark().to_bench(run_cfg, report)
97
+ bench.plot_sweep(
98
+ title="Pattern Visualization (2 Float, 1 Categorical Variable)",
99
+ description="Response patterns with distinctive shapes based on pattern type",
100
+ post_description="""
101
+ Pattern Type: Linear (diagonal gradients) vs Exponential (corner-concentrated)
102
+
103
+ Each pattern type produces a unique surface shape that remains visually distinctive
104
+ even with auto-scaling of the color maps. This example uses fixed "symmetric" symmetry
105
+ and "smooth" feature settings.
106
+ """,
107
+ )
108
+ return bench
109
+
110
+
111
+ if __name__ == "__main__":
112
+ example_2_float_1_cat_in_2_out().report.show()
@@ -0,0 +1,122 @@
1
+ """Demonstration of benchmarking with 2 float and 2 categorical inputs producing visually distinct patterns.
2
+
3
+ Each categorical input has 2 conditions that create distinctly different surface shapes.
4
+ """
5
+
6
+ import random
7
+ import math
8
+ import bencher as bch
9
+
10
+ random.seed(0)
11
+
12
+
13
+ class Pattern2CatBenchmark(bch.ParametrizedSweep):
14
+ """Benchmark demonstrating patterns with distinctive shapes based on categorical settings."""
15
+
16
+ # Float input parameters
17
+ x_value = bch.FloatSweep(default=100, bounds=[1, 100], doc="X value parameter")
18
+ y_value = bch.FloatSweep(default=10, bounds=[1, 100], doc="Y value parameter")
19
+
20
+ # Categorical input parameters - each with 2 conditions
21
+ pattern_type = bch.StringSweep(["linear", "exponential"], doc="Pattern relationship")
22
+ symmetry_type = bch.StringSweep(["symmetric", "asymmetric"], doc="Pattern symmetry")
23
+ # feature_type removed
24
+
25
+ # Output metrics
26
+ response_a = bch.ResultVar(units="units", doc="Response variable A")
27
+ response_b = bch.ResultVar(units="units", doc="Response variable B")
28
+
29
+ def __call__(self, **kwargs) -> dict:
30
+ """Generate responses with distinctly different patterns based on categorical inputs."""
31
+ self.update_params_from_kwargs(**kwargs)
32
+
33
+ # Normalize inputs to [0,1]
34
+ x = self.x_value / 100
35
+ y = self.y_value / 100
36
+
37
+ # Set base patterns based on pattern_type
38
+ if self.pattern_type == "linear":
39
+ base_a = 2 * x + 3 * y
40
+ base_b = 3 * x - y
41
+ else: # exponential
42
+ base_a = math.exp(2 * x) * math.exp(y) / math.exp(3)
43
+ base_b = math.exp(x) * math.exp(2 * y) / math.exp(3)
44
+
45
+ # Apply symmetry effect
46
+ if self.symmetry_type == "symmetric":
47
+ sym_a = (x + y) ** 2
48
+ sym_b = (x + y) * abs(x - y)
49
+ else: # asymmetric
50
+ sym_a = x**2 * y
51
+ sym_b = x * y**2
52
+
53
+ # Using fixed "smooth" feature type
54
+ feat_a = math.sin(3 * math.pi * x) * math.sin(3 * math.pi * y)
55
+ feat_b = math.cos(3 * math.pi * x) * math.cos(3 * math.pi * y)
56
+
57
+ # Simplified weights dictionary for 2 categorical variables
58
+ weights = {
59
+ "linear": {
60
+ "symmetric": ([1, 2, 0.5], [1, 1.5, 0.3]), # Valley pattern
61
+ "asymmetric": ([1, 1.8, 0.7], [1, 1.2, 0.9]), # Tilted plane with waves
62
+ },
63
+ "exponential": {
64
+ "symmetric": ([1, 1, 0.3], [1, 0.8, 0.4]), # Corner peak with ripples
65
+ "asymmetric": ([1, 1.5, 0.4], [1, 1.8, 0.2]), # Curved gradient
66
+ },
67
+ }
68
+
69
+ # Get the weights for the current combination
70
+ w_a, w_b = weights[self.pattern_type][self.symmetry_type]
71
+
72
+ # Calculate final responses with weights
73
+ if self.pattern_type == "linear":
74
+ self.response_a = w_a[0] * base_a + w_a[1] * sym_a + w_a[2] * feat_a
75
+ self.response_b = w_b[0] * base_b + w_b[1] * sym_b + w_b[2] * feat_b
76
+ else: # exponential - multiplicative relationship
77
+ self.response_a = base_a * (1 + w_a[1] * sym_a) * (1 + w_a[2] * feat_a)
78
+ self.response_b = base_b * (1 + w_b[1] * sym_b) * (1 + w_b[2] * feat_b)
79
+
80
+ # Add minimal randomness (to maintain pattern visibility)
81
+ random_factor = random.uniform(0.98, 1.02)
82
+ self.response_a *= random_factor
83
+ self.response_b *= random_factor
84
+
85
+ return super().__call__(**kwargs)
86
+
87
+
88
+ def example_2_float_2_cat_in_2_out(
89
+ run_cfg: bch.BenchRunCfg = None, report: bch.BenchReport = None
90
+ ) -> bch.Bench:
91
+ """Benchmark demonstrating visually distinct patterns based on 2 categorical settings.
92
+
93
+ This example is simplified from the 3-category version by fixing the feature_type to "smooth".
94
+
95
+ Args:
96
+ run_cfg: Configuration for the benchmark run
97
+ report: Report to append the results to
98
+
99
+ Returns:
100
+ bch.Bench: The benchmark object
101
+ """
102
+ if run_cfg is None:
103
+ run_cfg = bch.BenchRunCfg()
104
+ run_cfg.repeats = 3 # Fewer repeats for a quicker benchmark
105
+
106
+ bench = Pattern2CatBenchmark().to_bench(run_cfg, report)
107
+ bench.plot_sweep(
108
+ title="Pattern Visualization (2 Float, 2 Categorical Variables)",
109
+ description="Response patterns with distinctive shapes based on pattern and symmetry types",
110
+ post_description="""
111
+ Pattern Type: Linear (diagonal gradients) vs Exponential (corner-concentrated)
112
+ Symmetry Type: Symmetric (x/y similarity) vs Asymmetric (x/y difference)
113
+
114
+ Each combination produces a unique pattern shape that remains visually distinctive
115
+ even with auto-scaling of the color maps. Feature type is fixed to "smooth" in this example.
116
+ """,
117
+ )
118
+ return bench
119
+
120
+
121
+ if __name__ == "__main__":
122
+ example_2_float_2_cat_in_2_out().report.show()