butchc 0.6.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.
butchc-0.6.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BUTChC contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
butchc-0.6.0/PKG-INFO ADDED
@@ -0,0 +1,424 @@
1
+ Metadata-Version: 2.4
2
+ Name: butchc
3
+ Version: 0.6.0
4
+ Summary: Bayesian Update Tree Chained Conditionally — a dependency-free probabilistic black-box hyperparameter optimizer
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/AdventuresInDataScience/BUTChC
7
+ Project-URL: Repository, https://github.com/AdventuresInDataScience/BUTChC
8
+ Project-URL: Documentation, https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/api.md
9
+ Project-URL: Changelog, https://github.com/AdventuresInDataScience/BUTChC/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/AdventuresInDataScience/BUTChC/issues
11
+ Keywords: hyperparameter optimization,bayesian optimization,black-box optimization,tree search,machine learning
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Provides-Extra: test
28
+ Requires-Dist: pytest>=7; extra == "test"
29
+ Provides-Extra: configspace
30
+ Requires-Dist: ConfigSpace>=0.6; extra == "configspace"
31
+ Dynamic: license-file
32
+
33
+ # BUTChC
34
+
35
+ **B**ayesian **U**pdate **T**ree **Ch**ained **C**onditionally — a dependency-free, probabilistic black-box hyperparameter optimizer.
36
+
37
+ BUTChC maintains a probability distribution over a **hierarchical, conditional search space** and refines it from observed objective values. Parameters can depend on choices made higher up the tree: `momentum` exists only when `optimizer=sgd`, `kernel_size` only when `model=cnn`. Invalid combinations are unreachable by construction rather than discovered by trial, and each branch learns its own parameters from only the trials that used it.
38
+
39
+ No gradients, no differentiability, no assumptions about the objective's internals.
40
+
41
+ - **Zero dependencies** — Python ≥ 3.8 standard library only
42
+ - **Conditional search spaces** — nested arbitrarily deep via `next_level`
43
+ - **Non-parametric continuous model** — a weighted KDE elite archive, no distributional assumptions
44
+ - **Scale-invariant** — updates use rank, not raw objective, so an objective in the millions behaves like one in `[0, 1]`
45
+ - **Parallel evaluation** — bring your own executor; threads, processes, joblib, dask
46
+ - **Reproducible** — `seed` gives a private RNG, and the answer does not depend on which worker finishes first
47
+ - **Fails fast** — search spaces and hyperparameters are validated before the first objective call
48
+ - **Prunable** — `prune` turns a finished run into a smaller space, to search again or hand to another optimiser
49
+
50
+ Against TPE across 18 benchmark problems at 30 paired seeds: **13 significant wins, zero significant losses**, at **73–145× lower per-trial cost**.
51
+
52
+ 📖 **[API reference](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/api.md)** · **[Examples](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/examples.md)** · **[Design notes](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/design.md)** · **[Limitations](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/limitations.md)** · **[Changelog](https://github.com/AdventuresInDataScience/BUTChC/blob/main/CHANGELOG.md)**
53
+
54
+ ---
55
+
56
+ ## Install
57
+
58
+ ```bash
59
+ pip install butchc
60
+ pip install "butchc[configspace]" # optional ConfigSpace interop
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Quick start
66
+
67
+ ```python
68
+ from butchc import BUTChC_optimize
69
+
70
+ searchspace = {
71
+ # Top-level choice of model family. Each choice unlocks its own
72
+ # sub-parameters via 'next_level'.
73
+ 'model_type': {
74
+ 'values': ['svm', 'random_forest', 'neural_net'],
75
+ 'next_level': {
76
+ 'svm': {
77
+ 'kernel': {'values': ['rbf', 'linear', 'poly']},
78
+ 'C': {'min': 0.01, 'max': 100.0, 'log': True},
79
+ 'gamma': {'min': 1e-4, 'max': 10.0, 'log': True},
80
+ },
81
+ 'random_forest': {
82
+ 'n_estimators': {'values': [50, 100, 200, 500]},
83
+ 'max_depth': {'min': 2, 'max': 30, 'int': True},
84
+ 'max_features': {'values': ['sqrt', 'log2']},
85
+ },
86
+ 'neural_net': {
87
+ 'learning_rate': {'min': 1e-4, 'max': 1e-1, 'log': True},
88
+ 'hidden_units': {'values': [64, 128, 256, 512]},
89
+ 'dropout': {'min': 0.0, 'max': 0.5},
90
+ },
91
+ },
92
+ },
93
+ # Always present, whatever model_type is chosen
94
+ 'preprocessing': {'values': ['standard_scaler', 'min_max', 'none']},
95
+ }
96
+
97
+ def objective(config):
98
+ # config carries 'model_type' and 'preprocessing', plus only the params
99
+ # of the chosen branch, e.g.
100
+ # {'model_type': 'svm', 'kernel': 'rbf', 'C': 4.2, 'gamma': 0.01, ...}
101
+ # 'dropout' never appears in an svm config; 'C' never in a neural_net one.
102
+ return cross_val_score(build_model(config), X, y).mean() # higher is better
103
+
104
+ results = BUTChC_optimize(
105
+ searchspace = searchspace,
106
+ objective = objective,
107
+ budget = 150,
108
+ seed = 0,
109
+ )
110
+
111
+ print(results['best_params'])
112
+ print(f"Best score: {results['best_value']:.4f}")
113
+ ```
114
+
115
+ BUTChC always **maximizes**. Negate to minimize.
116
+
117
+ ### Slow objective? Use an executor
118
+
119
+ ```python
120
+ from concurrent.futures import ThreadPoolExecutor
121
+
122
+ with ThreadPoolExecutor(max_workers=8) as pool:
123
+ results = BUTChC_optimize(searchspace, objective, budget=200,
124
+ batch=8, executor=pool, seed=0)
125
+ ```
126
+
127
+ ### Already have a ConfigSpace?
128
+
129
+ ```python
130
+ results = BUTChC_optimize(configuration_space, objective, budget=200, seed=0)
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Defining a search space
136
+
137
+ A search space is a plain dict — JSON-serializable, and no imports needed to write one.
138
+
139
+ ```python
140
+ 'activation': {'values': ['relu', 'tanh', 'elu']} # categorical
141
+ 'dropout': {'min': 0.0, 'max': 0.5} # linear
142
+ 'learning_rate': {'min': 1e-5, 'max': 1e-1, 'log': True} # log10-uniform
143
+ 'n_layers': {'min': 1, 'max': 6, 'int': True} # integer-valued
144
+ ```
145
+
146
+ Use `log: True` whenever the range spans more than about one order of magnitude. Sampled linearly, `[1e-5, 1e-1]` places 99.99% of its mass above `1e-3`, leaving the bottom three decades effectively unreachable.
147
+
148
+ A categorical node can map each of its choices to a sub-searchspace via `next_level`. Those parameters are sampled and updated only when their parent value is chosen:
149
+
150
+ ```python
151
+ 'optimizer': {
152
+ 'values': ['adam', 'sgd', 'lbfgs'],
153
+ 'next_level': {
154
+ 'adam': {'lr': {'min': 1e-4, 'max': 1e-2, 'log': True}},
155
+ 'sgd': {'lr': {'min': 1e-3, 'max': 1e-1, 'log': True},
156
+ 'momentum': {'min': 0.0, 'max': 0.99}},
157
+ # 'lbfgs' takes no sub-params — omitting it is fine
158
+ },
159
+ }
160
+ ```
161
+
162
+ Each branch keeps its own model, so learning the best `lr` for adam does not interfere with learning the best `lr` for sgd. `next_level` is the only nesting mechanism and it composes, so nesting is arbitrarily deep — [a worked four-level space](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/examples.md#nesting-more-than-one-level) shows how.
163
+
164
+ This is also how you express an invalid combination. "`penalty=elasticnet` only works with `solver=saga`" becomes a `solver` node whose branches carry different `penalty` values — the invalid pairing is then unreachable, and no budget is spent finding that out. Constraints that cross the tree rather than nest are the exception; see [limitations](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/limitations.md#search-spaces-butchc-cannot-express).
165
+
166
+ Any node can carry a `prior` and a `prior_strength` measured in pseudo-trials. See the [API reference](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/api.md#search-space-format) for the full format, priors, and the uniqueness rule for names.
167
+
168
+ ---
169
+
170
+ ## How it works
171
+
172
+ 1. **Initialize** a probability tree from the search space. Categorical nodes start uniform; continuous nodes start with an evenly spaced reservoir covering the range.
173
+
174
+ 2. **Sample** by traversing the tree. Categorical nodes draw from a temperature-scaled softmax. Continuous nodes pick a reservoir point weighted by its rank, add Silverman-bandwidth Gaussian jitter, and *reflect* back into range.
175
+
176
+ 3. **Evaluate** the objective.
177
+
178
+ 4. **Rank** the result against every finite objective seen so far, counting ties as half. Continuous nodes apply the `gamma` gate and convert what survives into a `quality` in `[0, 1]`; categorical nodes use the raw rank, ungated.
179
+
180
+ 5. **Update**. Categorical nodes score each choice by its recency-weighted mean rank, smoothed by `alpha` pseudo-visits. Continuous nodes append to an elite archive, evict the worst-scoring entry, and reweight geometrically by rank.
181
+
182
+ 6. **Repeat**, with categorical commitment ramping up over the budget.
183
+
184
+ Two properties are load-bearing. **Reflection rather than clipping**: jitter clipped to `[min, max]` deposits probability mass on each bound and biases every search toward interval edges. **Rank rather than raw objective**: raw-value weighting makes behaviour depend on units, and one catastrophic outlier could dominate the archive permanently.
185
+
186
+ Full reasoning in [design notes](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/design.md).
187
+
188
+ ---
189
+
190
+ ## Tuning
191
+
192
+ | Parameter | Start | Increase if… | Decrease if… |
193
+ |---|---|---|---|
194
+ | `lambda_` | `2.0` | tree adapts too slowly | converging too fast to a suboptimal region |
195
+ | `alpha` | `3.0` | many categorical options, small budget | want faster commitment to early evidence |
196
+ | `temp` | `1.0` | categorical exploration too greedy | budget spent on clearly bad choices |
197
+ | `gamma` | `0.85` | objective is noisy; want only strong trials to count | want more trials contributing signal |
198
+ | `explore` | `0.05` | search collapses to a local optimum early | objective is expensive and smooth |
199
+ | `batch` | `1` | you have idle workers | you have no executor |
200
+ | `budget` | 10× param count | rolling loss has not plateaued | rolling loss flat after 20% of the run |
201
+
202
+ `lambda_` and `alpha` act on different node types and do not interact: `lambda_` controls how sharply continuous archives concentrate, `alpha` how slowly categorical nodes commit. Note `lambda_` saturates — it acts only through `min(RANK_SHARPNESS * lambda_, MAX_SHARPNESS)`, so any value at or above 3.33 is clipped and does nothing. See the [API reference](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/api.md#butchc_optimize).
203
+
204
+ The two knobs most worth reaching for are not in this table. `KDE_RESERVOIR_SIZE` (default 25) and `MIN_BANDWIDTH_FRACTION` (default 0.0003) between them decide how hard the continuous model concentrates, and they carry more of the measured gain than anything else. They interact, so retune them together: a **smooth, high-dimensional** space wants the gentler pair (`50` and `0.001`), while conditional and multimodal spaces want the sharp defaults.
205
+
206
+ ### Tuning for your problem's shape
207
+
208
+ The defaults are an average over problem shapes. `benchmarks/tune.py --regime` (in the repository, not the installed package) sweeps against problems sharing one property and prints what that shape wants: `branched` spaces want faster commitment, `noisy` ones want `explore 0.1`, `multimodal` ones want a short warm-up. The table is in the [API reference](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/api.md#tuning-by-problem-shape).
209
+
210
+ ---
211
+
212
+ ## Results
213
+
214
+ Median best objective over 30 paired seeds — same budget, same seeds, every
215
+ method. Every problem is a maximization with optimum 0, so nearer zero is
216
+ better (Styblinski's offset is rounded, so it can read fractionally above 0).
217
+ Re-running the commands below from a clone of the repository reproduces every
218
+ table in this section.
219
+
220
+ ```bash
221
+ python benchmarks/evaluate.py 30
222
+ python benchmarks/evaluate.py 10 --suite heldout --methods tpe,butchc
223
+ ```
224
+
225
+ Random search is a low bar, so the comparator carried throughout is TPE — same
226
+ niche, and what a user choosing against BUTChC would actually reach for.
227
+ `benchmarks/baselines.py` carries a dependency-free TPE and an Optuna
228
+ `TPESampler` wrapper. `evaluate.py` also reports paired win-loss records and
229
+ two-sided exact sign-test p-values; those records, not the medians, are what
230
+ the claims here rest on.
231
+
232
+ The suite is split in half. Defaults were selected by coordinate descent
233
+ against the **tuned-on** problems only; the **held-out** problems were never
234
+ consulted during that sweep, so they are the fairer test of whether the
235
+ defaults generalise.
236
+
237
+ ### Tuned on
238
+
239
+ | Problem | Budget | Random | TPE | BUTChC | vs TPE |
240
+ |---|---|---|---|---|---|
241
+ | 2D quadratic | 200 | -0.0425 | -0.0004 | **-0.0000** | 30-0, p=0.000 |
242
+ | 5D sphere | 500 | -3.7393 | -0.1312 | **-0.0000** | 29-1, p=0.000 |
243
+ | Rosenbrock | 500 | -0.1036 | **-0.0202** | -0.0368 | 13-17, p=0.585 |
244
+ | Rastrigin 4D | 600 | -17.5005 | -8.6510 | **-5.1169** | 26-4, p=0.000 |
245
+ | 10D sphere | 1000 | -19.0750 | -2.2176 | **-0.0036** | 30-0, p=0.000 |
246
+ | Ackley 5D | 600 | -14.1027 | -4.3008 | **-0.0323** | 30-0, p=0.000 |
247
+ | Log-scale target | 200 | -0.0085 | -0.0006 | **-0.0000** | 29-1, p=0.000 |
248
+ | Branch trap | 300 | -0.6219 | -1.0004 | **-0.0189** | 25-5, p=0.000 |
249
+ | Categorical mix | 300 | -0.3226 | -0.0680 | **-0.0004** | 28-2, p=0.000 |
250
+ | Integer mix | 300 | -0.2727 | -0.0016 | **-0.0000** | 26-4, p=0.000 |
251
+ | Plateau (ties) | 300 | -0.5000 | -0.5000 | -0.5000 | 0-0, p=1.000 |
252
+
253
+ ### Held out
254
+
255
+ | Problem | Budget | Random | TPE | BUTChC | vs TPE |
256
+ |---|---|---|---|---|---|
257
+ | Griewank 6D | 800 | -1.0183 | -0.4915 | **-0.4383** | 18-12, p=0.362 |
258
+ | Styblinski 4D | 600 | -20.0007 | -4.0751 | **+0.0007** | 30-0, p=0.000 |
259
+ | Nested pipeline | 400 | -0.2970 | -0.0096 | **-0.0089** | 15-15, p=1.000 |
260
+ | Optimiser choice | 300 | -0.0277 | -0.0007 | **-0.0001** | 25-5, p=0.000 |
261
+ | Rastrigin 8D | 1000 | -62.4076 | -39.0921 | **-21.5439** | 28-2, p=0.000 |
262
+ | 20D sphere | 1500 | -69.0434 | -18.8961 | **-0.1255** | 30-0, p=0.000 |
263
+
264
+ Four of the six held-out problems are significant wins on defaults that never
265
+ saw them.
266
+
267
+ ### Under observation noise
268
+
269
+ Scoring the *reported* best on the noise-free function (5D sphere, N(0,1)
270
+ noise, budget 400): random -4.8604, TPE -0.8099, BUTChC **-0.4490** — 19-11
271
+ against TPE at p=0.200, better on the median but not separable at 30 seeds.
272
+
273
+ ### What the records show
274
+
275
+ **13 significant wins, zero significant losses, 5 ties**, across all 18
276
+ problems. The margins are large where they are large: `Styblinski 4D` reaches
277
+ the optimum outright (+0.0007 against -4.0751, 30-0), `20D sphere` lands 150×
278
+ nearer it, `Ackley 5D` 133× nearer. `Branch trap` is 25-5 on a problem where
279
+ TPE does *worse than random*, because the trap is precisely the conditional
280
+ structure a flat model cannot see.
281
+
282
+ The five ties are genuine non-results rather than hidden losses, and each has a
283
+ reason; they are set out in full in
284
+ [limitations](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/limitations.md#the-non-results-in-full).
285
+
286
+ ### How long a result takes to arrive
287
+
288
+ Final quality says where a method ends up, not when. `evaluate.py --anytime`
289
+ reports the other axis. The clearest framing is **how much budget BUTChC needs
290
+ to match TPE's final answer**:
291
+
292
+ | Problem | Budget | Trials BUTChC needed | Fraction of budget |
293
+ |---|---|---|---|
294
+ | 20D sphere | 1500 | 148 | **1/10.1** |
295
+ | 10D sphere | 1000 | 138 | **1/7.3** |
296
+ | Styblinski 4D | 600 | 146 | 1/4.1 |
297
+ | Ackley 5D | 600 | 155 | 1/3.9 |
298
+ | 5D sphere | 500 | 128 | 1/3.9 |
299
+ | Rastrigin 8D | 1000 | 325 | 1/3.1 |
300
+ | Integer mix | 300 | 100 | 1/3.0 |
301
+ | Categorical mix | 300 | 107 | 1/2.8 |
302
+ | Branch trap | 300 | 111 | 1/2.7 |
303
+ | Nested pipeline | 400 | 324 | 1/1.2 |
304
+
305
+ On every problem it reaches, BUTChC matches TPE's *final* result partway
306
+ through its own budget — median around a third of it.
307
+
308
+ Against TPE's own arrival time the picture is split. On high-dimensional
309
+ problems BUTChC is far quicker: `20D sphere` in 148 trials against TPE's 1133,
310
+ on 30 of 30 seeds against TPE's 15. On conditional problems it arrives later
311
+ even while matching or beating TPE's final quality — `Nested pipeline` 324
312
+ against 188, `Optimiser choice` 206 against 115.
313
+
314
+ Reliability is consistently BUTChC's. At 50% of the achievable range it reaches
315
+ the target on 27–30 of 30 seeds where TPE manages 10–29; at 99% on `20D sphere`
316
+ it arrives on 29 of 30 seeds while TPE never arrives at all. Full tables in
317
+ [`dev/tools/results/`](https://github.com/AdventuresInDataScience/BUTChC/tree/main/dev/tools/results).
318
+
319
+ ### What the optimiser itself costs
320
+
321
+ Sample efficiency is one axis; the wall clock the optimiser spends choosing is
322
+ another. With the objective stubbed to a constant, so the number is all
323
+ optimiser:
324
+
325
+ | Optimiser | 1D | 5D | 20D |
326
+ |---|---|---|---|
327
+ | **BUTChC** (pure Python, 0 deps) | **14 µs** | **52 µs** | **193 µs** |
328
+ | TPE (this repo, pure Python) | 1037 µs | 5219 µs | 21270 µs |
329
+ | Optuna `TPESampler` (numpy-backed) | 1433 µs | 6789 µs | 27864 µs |
330
+
331
+ BUTChC is **73–145× cheaper per trial** than either TPE. Zero dependencies is
332
+ not costing speed here: the numpy-backed implementation pays ~145× more per
333
+ suggestion, because TPE refits Parzen estimators over the whole history and
334
+ scores candidates, while BUTChC draws a reservoir point and jitters it —
335
+ `O(d·K)` with `K = 25`. The gap widens with width, since BUTChC only touches
336
+ the parameters on the sampled path while a flat model touches all of them.
337
+
338
+ Reproduce with `python benchmarks/overhead.py`.
339
+
340
+ ### What batching costs
341
+
342
+ A batch of `k` leaves the model stale for `k-1` evaluations. Median extra regret against sequential, 18 problems × 20 seeds at matched budgets:
343
+
344
+ | `batch` | 2 | 4 | 8 | 16 | 32 |
345
+ |---|---|---|---|---|---|
346
+ | Extra regret | −3.3% | −0.1% | +2.6% | +21.3% | +43.1% |
347
+
348
+ ```bash
349
+ python benchmarks/batch_cost.py 20 --sizes 1,2,4,8,16,32
350
+ ```
351
+
352
+ Up to `k=8` the cost sits inside seed noise, which makes an 8× wall-clock speedup close to free. Past `k=16` it is real. Batch sizes above your worker count pay the cost for nothing.
353
+
354
+ ### The spaces this is built for
355
+
356
+ The 18 problems above are synthetic and were written in this repository. To
357
+ measure the *shape* of real conditional spaces independently,
358
+ [`dev/eval/pcs_stats.py`](https://github.com/AdventuresInDataScience/BUTChC/blob/main/dev/eval/pcs_stats.py)
359
+ parses eleven configuration spaces published by other people, for other
360
+ purposes, years before this library existed, and reports how much of each is
361
+ inactive in a typical configuration. The five most conditional:
362
+
363
+ | Space | Params | Median active | Inactive | Depth |
364
+ |---|---|---|---|---|
365
+ | AutoWEKA | 786 | 14 | **98.2%** | 4 |
366
+ | auto-sklearn (2017) | 138 | 16 | **88.4%** | 2 |
367
+ | SparrowToRiss | 222 | 67 | 69.8% | 4 |
368
+ | SATenstein | 54 | 26 | 51.9% | 4 |
369
+ | clasp 3.1.4 | 98 | 59 | 39.8% | 3 |
370
+
371
+ ```bash
372
+ python dev/eval/pcs_stats.py --download
373
+ ```
374
+
375
+ In AutoWEKA, 98.2% of the declared parameters are inactive in any given
376
+ configuration — the share a flat optimiser searches and a conditional one
377
+ skips. All 174 of its multi-parent conditions are chain-shaped, so the space is
378
+ representable as a tree exactly rather than approximately.
379
+
380
+ This measures the premise the library is built on, in spaces nobody here
381
+ designed. It is not a head-to-head: no BUTChC-versus-TPE run has been executed
382
+ on these spaces yet. Both halves of that are set out in
383
+ [limitations](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/limitations.md#what-the-benchmarks-establish-and-what-they-do-not).
384
+
385
+ ---
386
+
387
+ ## Where it fits
388
+
389
+ Reach for BUTChC when the search space is conditional, when it is wide, when
390
+ the objective is noisy or on an awkward scale, or when adding a dependency is
391
+ not an option. Those are the axes it is measured strongest on.
392
+
393
+ Reach for something else when the budget is under ~50 trials on a smooth
394
+ low-dimensional objective, where a Gaussian process will do better, or when
395
+ your parameters interact strongly *within* a branch — sibling nodes are
396
+ modelled independently, and `Rosenbrock` measures what that costs.
397
+
398
+ The complete list of what the library does not model, does not express, and has
399
+ not yet measured is in **[limitations](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/limitations.md)**. The one
400
+ line worth carrying from it: the 18 benchmark problems are synthetic and were
401
+ written alongside the optimiser, so benchmark your own space before committing
402
+ to it.
403
+
404
+ ---
405
+
406
+ ## Versioning
407
+
408
+ Defaults and internals change between minor versions, so **seeded runs do not reproduce across them**. See the [changelog](https://github.com/AdventuresInDataScience/BUTChC/blob/main/CHANGELOG.md).
409
+
410
+ ## Development
411
+
412
+ ```bash
413
+ git clone https://github.com/AdventuresInDataScience/BUTChC
414
+ cd BUTChC
415
+ pip install -e ".[test]"
416
+ pytest
417
+ ```
418
+
419
+ The benchmark suite lives in `benchmarks/`, and a map of the source for
420
+ contributors is in [docs/codemap.md](https://github.com/AdventuresInDataScience/BUTChC/blob/main/docs/codemap.md).
421
+
422
+ ## License
423
+
424
+ MIT — see [LICENSE](https://github.com/AdventuresInDataScience/BUTChC/blob/main/LICENSE).