Benchmarkers
hgp_lib.benchmarkers.gp_benchmarker.GPBenchmarker
Benchmarker for Boolean GP: runs multiple independent experiments with stratified train/test split and k-fold CV per run, then aggregates results.
Data flow per run:
- Stratified train/test split (using
test_size). - For each of the
n_foldsfolds:
a. A fresh copy of the binarizer is fitted on the training fold.
b. The validation fold is transformed using the same fitted binarizer.
c. Binarized data is converted to boolean numpy arrays and used for GP
training.
- The best fold (highest validation score) is selected. Its binarizer is used to transform the held-out test set for final evaluation.
Raw (non-binarized) data should be passed as a
pandas.DataFrame in BenchmarkerConfig.data. Binarization happens
internally per fold to prevent data leakage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
BenchmarkerConfig
|
Configuration with data,
labels, binarizer, trainer_config template, and benchmarker-specific
options. See |
required |
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig, BenchmarkerConfig
>>> from hgp_lib.benchmarkers import GPBenchmarker
>>> from hgp_lib.rules import Rule
>>> data = pd.DataFrame({
... "f1": [True, False, True, False, True, False, True, False],
... "f2": [False, True, True, False, False, True, True, False],
... })
>>> labels = np.array([1, 0, 1, 0, 1, 0, 1, 0])
>>> gp_config = BooleanGPConfig(optimize_scorer=False)
>>> trainer_config = TrainerConfig(gp_config=gp_config, num_epochs=3, progress_bar=False)
>>> config = BenchmarkerConfig(
... data=data, labels=labels, trainer_config=trainer_config,
... num_runs=2, n_folds=2, n_jobs=1,
... )
>>> benchmarker = GPBenchmarker(config)
>>> result = benchmarker.fit()
>>> len(result.runs)
2
>>> isinstance(result.best_rule, Rule)
True
Source code in src/hgp_lib/benchmarkers/gp_benchmarker.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | |
fit()
Run all benchmark runs (parallel or sequential) and aggregate results.
Each run performs a stratified train/test split, k-fold CV with per-fold binarization, and test-set evaluation. Results across runs are aggregated into an ExperimentResult.
Returns:
| Name | Type | Description |
|---|---|---|
ExperimentResult |
ExperimentResult
|
Contains all run results with methods to get best_run, best_rule, test scores statistics, etc. |
Source code in src/hgp_lib/benchmarkers/gp_benchmarker.py
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | |
predict(data)
Predict labels for raw data using the best rule found across all runs.
This mirrors the scikit-learn predict API so a fitted GPBenchmarker
can be used where an estimator is expected. It must be called after fit.
The raw DataFrame is binarized with the best run's fitted binarizer (the
one from its best fold), then the best rule is evaluated on it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
Raw (non-binarized) features with the same columns, in the same order and dtypes, as the data used to fit the benchmarker. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: 1-D boolean array with one prediction per input row. |
Raises:
| Type | Description |
|---|---|
NotFittedError
|
If called before |
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> from hgp_lib.configs import BooleanGPConfig, TrainerConfig, BenchmarkerConfig
>>> from hgp_lib.benchmarkers import GPBenchmarker
>>> data = pd.DataFrame({
... "f1": [True, False, True, False, True, False, True, False],
... "f2": [False, True, True, False, False, True, True, False],
... })
>>> labels = np.array([1, 0, 1, 0, 1, 0, 1, 0])
>>> gp_config = BooleanGPConfig(optimize_scorer=False)
>>> trainer_config = TrainerConfig(gp_config=gp_config, num_epochs=3, progress_bar=False)
>>> config = BenchmarkerConfig(
... data=data, labels=labels, trainer_config=trainer_config,
... num_runs=2, n_folds=2, n_jobs=1,
... )
>>> benchmarker = GPBenchmarker(config)
>>> _ = benchmarker.fit()
>>> predictions = benchmarker.predict(data)
>>> predictions.shape
(8,)
Source code in src/hgp_lib/benchmarkers/gp_benchmarker.py
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | |
hgp_lib.benchmarkers.runner.execute_single_run(run_id, seed, config, reporter=None)
Execute one benchmark run: stratified train/test split, per-fold binarization, k-fold CV training, best-fold selection, and test-set evaluation.
This is a module-level function so it can be pickled for multiprocessing.
Per-fold binarization: For each fold a fresh clone of the configured
binarizer is fitted on the training fold (with labels, enabling supervised
binning for numerical features) and used to transform the validation fold.
After selecting the best fold, its binarizer transforms the held-out test
data. This prevents data leakage across folds and between train/test sets.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_id
|
int
|
Index of the run (0-based). |
required |
seed
|
int
|
Random seed for stratified split and k-fold. |
required |
config
|
BenchmarkerConfig
|
Configuration for the benchmark run. |
required |
reporter
|
ProgressReporter | None
|
Where to report epoch/fold/run progress. Default: an inert reporter that discards updates. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
RunResult |
RunResult
|
Contains run_id, seed, folds, test_score, best_rule, feature_names. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If no best rule is available after training a fold. |
Source code in src/hgp_lib/benchmarkers/runner.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
hgp_lib.benchmarkers.progress.ProgressListener
Listener thread for aggregating progress updates from worker processes.
Runs in the main process and listens to a multiprocessing queue for progress updates from worker processes. Updates three tqdm progress bars: - Experiments (runs) - Folds - Epochs
The listener uses timeouts on queue.get() to periodically check for stop signals, preventing indefinite hangs if workers crash.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
progress_queue
|
Queue
|
Multiprocessing queue for receiving progress updates. |
required |
config
|
ProgressConfig
|
Progress configuration with totals and display settings. |
required |
Examples:
>>> from multiprocessing import Queue
>>> from hgp_lib.benchmarkers.progress import ProgressConfig, ProgressListener
>>> q = Queue()
>>> cfg = ProgressConfig(
... total_runs=1, total_folds=1, total_epochs=1,
... show_run_progress=False, show_fold_progress=False,
... show_epoch_progress=False,
... )
>>> listener = ProgressListener(q, cfg)
>>> listener.start()
>>> q.put(("epoch", 1))
>>> q.put(("fold", 1))
>>> q.put(("run", 1))
>>> listener.join()
Source code in src/hgp_lib/benchmarkers/progress.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | |
start()
Start the listener thread.
Source code in src/hgp_lib/benchmarkers/progress.py
95 96 97 98 99 | |
stop()
Signal the listener to stop and wait for it to finish.
Source code in src/hgp_lib/benchmarkers/progress.py
101 102 103 104 105 106 107 108 109 110 | |
join()
Wait for the listener thread to finish naturally (all runs completed).
Source code in src/hgp_lib/benchmarkers/progress.py
112 113 114 115 | |
hgp_lib.benchmarkers.progress.ProgressReporter
Producer-side handle for reporting progress, one method per channel.
Wraps the queue that :class:ProgressListener consumes. A reporter built
without a queue is inert and discards every update.
Every method has the shape Callable[[int], None], so a bound method such
as reporter.epoch can be passed as TrainerConfig.progress_callback.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
progress_queue
|
Queue | None
|
Queue to publish updates on, or |
None
|
Examples:
>>> from multiprocessing import Queue
>>> from hgp_lib.benchmarkers.progress import ProgressReporter
>>> reporter = ProgressReporter(Queue())
>>> reporter.enabled
True
>>> reporter.fold()
>>> reporter.queue.get()
('fold', 1)
>>> reporter.epoch(10)
>>> reporter.queue.get()
('epoch', 10)
An inert reporter accepts the same calls and discards them:
>>> inert = ProgressReporter()
>>> inert.enabled
False
>>> inert.run(5)
Source code in src/hgp_lib/benchmarkers/progress.py
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
enabled
property
bool: Whether updates are published rather than discarded.
epoch(count=1)
Report count completed epochs. Default: 1.
Source code in src/hgp_lib/benchmarkers/progress.py
231 232 233 | |
fold(count=1)
Report count completed folds. Default: 1.
Source code in src/hgp_lib/benchmarkers/progress.py
235 236 237 | |
run(count=1)
Report count completed runs. Default: 1.
Source code in src/hgp_lib/benchmarkers/progress.py
239 240 241 | |
hgp_lib.benchmarkers.progress.ProgressChannel
Bases: str, Enum
The nesting levels a worker can report progress on.
Each member's value is the literal that travels over the queue.
Examples:
>>> from hgp_lib.benchmarkers.progress import ProgressChannel
>>> ProgressChannel.FOLD.value
'fold'
Source code in src/hgp_lib/benchmarkers/progress.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |