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.
- bencher/__init__.py +20 -2
- bencher/bench_cfg.py +262 -54
- bencher/bench_report.py +2 -2
- bencher/bench_runner.py +96 -10
- bencher/bencher.py +421 -89
- bencher/class_enum.py +70 -7
- bencher/example/example_dataframe.py +2 -2
- bencher/example/example_levels.py +17 -173
- bencher/example/example_pareto.py +107 -31
- bencher/example/example_rerun2.py +1 -1
- bencher/example/example_simple_bool.py +2 -2
- bencher/example/example_simple_float2d.py +6 -1
- bencher/example/example_video.py +2 -0
- bencher/example/experimental/example_hvplot_explorer.py +2 -2
- bencher/example/inputs_0D/example_0_in_1_out.py +25 -15
- bencher/example/inputs_0D/example_0_in_2_out.py +12 -3
- bencher/example/inputs_0_float/example_0_cat_in_2_out.py +88 -0
- bencher/example/inputs_0_float/example_1_cat_in_2_out.py +98 -0
- bencher/example/inputs_0_float/example_2_cat_in_2_out.py +107 -0
- bencher/example/inputs_0_float/example_3_cat_in_2_out.py +111 -0
- bencher/example/inputs_1D/example1d_common.py +48 -12
- bencher/example/inputs_1D/example_0_float_1_cat.py +33 -0
- bencher/example/inputs_1D/example_1_cat_in_2_out_repeats.py +68 -0
- bencher/example/inputs_1D/example_1_float_2_cat_repeats.py +3 -0
- bencher/example/inputs_1D/example_1_int_in_1_out.py +98 -0
- bencher/example/inputs_1D/example_1_int_in_2_out.py +101 -0
- bencher/example/inputs_1D/example_1_int_in_2_out_repeats.py +99 -0
- bencher/example/inputs_1_float/example_1_float_0_cat_in_2_out.py +117 -0
- bencher/example/inputs_1_float/example_1_float_1_cat_in_2_out.py +124 -0
- bencher/example/inputs_1_float/example_1_float_2_cat_in_2_out.py +132 -0
- bencher/example/inputs_1_float/example_1_float_3_cat_in_2_out.py +140 -0
- bencher/example/inputs_2D/example_2_cat_in_4_out_repeats.py +104 -0
- bencher/example/inputs_2_float/example_2_float_0_cat_in_2_out.py +98 -0
- bencher/example/inputs_2_float/example_2_float_1_cat_in_2_out.py +112 -0
- bencher/example/inputs_2_float/example_2_float_2_cat_in_2_out.py +122 -0
- bencher/example/inputs_2_float/example_2_float_3_cat_in_2_out.py +138 -0
- bencher/example/inputs_3_float/example_3_float_0_cat_in_2_out.py +111 -0
- bencher/example/inputs_3_float/example_3_float_1_cat_in_2_out.py +117 -0
- bencher/example/inputs_3_float/example_3_float_2_cat_in_2_out.py +124 -0
- bencher/example/inputs_3_float/example_3_float_3_cat_in_2_out.py +129 -0
- bencher/example/meta/generate_examples.py +118 -7
- bencher/example/meta/generate_meta.py +88 -40
- bencher/job.py +174 -9
- bencher/plotting/plot_filter.py +52 -17
- bencher/results/bench_result.py +117 -25
- bencher/results/bench_result_base.py +117 -8
- bencher/results/dataset_result.py +6 -200
- bencher/results/explorer_result.py +23 -0
- bencher/results/{hvplot_result.py → histogram_result.py} +3 -18
- bencher/results/holoview_results/__init__.py +0 -0
- bencher/results/holoview_results/bar_result.py +79 -0
- bencher/results/holoview_results/curve_result.py +110 -0
- bencher/results/holoview_results/distribution_result/__init__.py +0 -0
- bencher/results/holoview_results/distribution_result/box_whisker_result.py +73 -0
- bencher/results/holoview_results/distribution_result/distribution_result.py +109 -0
- bencher/results/holoview_results/distribution_result/scatter_jitter_result.py +92 -0
- bencher/results/holoview_results/distribution_result/violin_result.py +70 -0
- bencher/results/holoview_results/heatmap_result.py +319 -0
- bencher/results/holoview_results/holoview_result.py +346 -0
- bencher/results/holoview_results/line_result.py +240 -0
- bencher/results/holoview_results/scatter_result.py +107 -0
- bencher/results/holoview_results/surface_result.py +158 -0
- bencher/results/holoview_results/table_result.py +14 -0
- bencher/results/holoview_results/tabulator_result.py +20 -0
- bencher/results/optuna_result.py +30 -115
- bencher/results/video_controls.py +38 -0
- bencher/results/video_result.py +39 -36
- bencher/results/video_summary.py +2 -2
- bencher/results/{plotly_result.py → volume_result.py} +29 -8
- bencher/utils.py +175 -26
- bencher/variables/inputs.py +122 -15
- bencher/video_writer.py +2 -1
- bencher/worker_job.py +31 -3
- {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/METADATA +24 -24
- holobench-1.43.0.dist-info/RECORD +147 -0
- bencher/example/example_levels2.py +0 -37
- bencher/example/inputs_1D/example_1_in_1_out.py +0 -62
- bencher/example/inputs_1D/example_1_in_2_out.py +0 -63
- bencher/example/inputs_1D/example_1_in_2_out_repeats.py +0 -61
- bencher/results/holoview_result.py +0 -796
- bencher/results/panel_result.py +0 -41
- holobench-1.41.0.dist-info/RECORD +0 -114
- {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/WHEEL +0 -0
- {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()
|