Skip to content

Commit 3ba36e4

Browse files
committed
Merge branch 'development' into v2.4.0
2 parents 27bc064 + e9d71a5 commit 3ba36e4

23 files changed

Lines changed: 813 additions & 85 deletions

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ We hope you enjoy this new user experience as much as we do. 🚀
3838

3939
This instruction is for the installation on a Linux system, for Windows and Mac and further information see the [documentation](https://automl.github.io/SMAC3/latest/1_installation/).
4040

41-
Create a new environment with python 3.10 and make sure swig is installed either on your system or
42-
inside the environment. We demonstrate the installation via anaconda in the following:
41+
Create a new environment with python 3.10. We demonstrate the installation via anaconda in the following:
4342

4443
Create and activate environment:
4544
```
@@ -62,7 +61,7 @@ make install-dev
6261
starting from 2.4.0, SMAC uses random forest from [sklearn](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestRegressor.html)
6362
instead of random forest from [pyrfr](https://pypi.org/project/pyrfr/) as the default surrogate model for HPO tasks.
6463
However, you could still use the old pyrfr surrogate model by calling `smac.facade.old.HyperparameterOptimizationRFRFacade`
65-
and `smac.facade.old.MultiFidelityRFRFacade`
64+
and `smac.facade.old.MultiFidelityRFRFacade`.
6665

6766
To work with pyrfr, you need to first install gcc, gxx, and swig:
6867
```

docs/1_installation.md

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
# Installation
22
## Requirements
33

4-
SMAC is written in python3 and therefore requires an environment with python>=3.8.
5-
Furthermore, the Random Forest used in SMAC requires SWIG as a build dependency.
4+
SMAC is written in python3 and therefore requires an environment with python>=3.9.
65

76
!!! info
87

9-
SMAC is tested on Linux and Mac machines with python >=3.8.
8+
SMAC is tested on Linux and Mac machines with python >=3.9.
109

1110

1211
## SetUp
@@ -18,17 +17,6 @@ conda create -n SMAC python=3.10
1817
conda activate SMAC
1918
```
2019

21-
Now install swig either on the system level e.g. using the following command for Linux:
22-
```bash
23-
apt-get install swig
24-
```
25-
26-
Or install swig inside of an already created conda environment using:
27-
28-
```bash
29-
conda install gxx_linux-64 gcc_linux-64 swig
30-
```
31-
3220
## Install SMAC
3321
You can install SMAC either using PyPI or Conda-forge.
3422

@@ -63,7 +51,31 @@ conda install smac
6351

6452
Read [SMAC feedstock](https://github.com/conda-forge/smac-feedstock) for more details.
6553

66-
## Windows (native or via WSL, experimental)
54+
## Running SMAC with pyrfr
55+
starting from 2.4.0, SMAC uses random forest from [sklearn](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestRegressor.html)
56+
instead of random forest from [pyrfr](https://pypi.org/project/pyrfr/) as the default surrogate model for HPO tasks.
57+
However, you could still use the old pyrfr surrogate model by calling `smac.facade.old.HyperparameterOptimizationRFRFacade`
58+
and `smac.facade.old.MultiFidelityRFRFacade`.
59+
60+
The Random Forest used in SMAC requires SWIG as a build dependency.
61+
You could install swig either on the system level e.g. using the following command for Linux:
62+
```bash
63+
apt-get install swig
64+
```
65+
66+
Or install swig inside of an already created conda environment using:
67+
68+
```bash
69+
conda install gxx_linux-64 gcc_linux-64 swig
70+
```
71+
72+
And then install smac with the pyrfr option:
73+
```
74+
pip install smac[pyrfr]
75+
```
76+
77+
78+
### pyrfr on Windows (native or via WSL, experimental)
6779

6880
SMAC can be installed under Windows in a WSL (Windows Subsystem for Linux).
6981
You can find an instruction on how to do this here: [Experimental](./10_experimental.md).

docs/3_getting_started.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ scenario = Scenario(
8484
)
8585
```
8686

87+
!!! info "Deterministic Behavior"
88+
The **`deterministic`** parameter controls whether the objective function is assumed to be noise-free.
89+
* **For Deterministic Objectives (`deterministic=True`):** Use this when repeated evaluations of the same configuration produce identical results. In this mode, each configuration is evaluated only once, and the surrogate model does not include a noise term.
90+
* **For Stochastic Objectives (`deterministic=False`):** Use this when the objective is noisy. Intensification parameters like `max_config_calls` and `n_seeds` must be set to enable repeated evaluations per configuration.
91+
8792
!!! note
8893
If no `name` is given, a hash of the experiment is used. Running the same experiment again at a later time will result in exactly the same hash. This is important, because the optimization will warmstart on the preexisting evaluations, if not otherwise specified in the [Facade][smac.facade.abstract_facade].
8994

@@ -150,4 +155,4 @@ it supports access to e.g. the runhistory with
150155
````
151156
runhistory = smbo.runhistory()
152157
````
153-
For more information check out the [API][smac.main.smbo].
158+
For more information check out the [API][smac.main.smbo].
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Adaptive Capping
2+
3+
Adaptive capping is a feature that can be used to speedup the evaluation of candidate configurations when the objective
4+
is to minimize runtime of an algorithm across a set of instances. The basic idea is to terminate unpromising candidates
5+
early and adapting the timeout for solving a single instance dynamically based on the incumbent's runtime and the.
6+
runtime already used by the challenging configuration.
7+
8+
## Theoretical Background
9+
10+
When comparing a challenger configuration with the current incumbent for a (sub-)set of instances, we already know how
11+
much cost (in terms of runtime) was incurred by the incumbent to solve the set of instances. As soon as the challenger
12+
configuration exceeds the cost of the incumbent, it is evident that the challenger will not become the new incumbent
13+
since the costs accumulate over time and are strictly positive, i.e., solving an instance cannot have negative runtime.
14+
15+
Example:
16+
*Let the incumbent be evaluated for two instances with observed runtimes 3s and 4s. When a challenger configuration is
17+
evaluated and compared against the incumbent, it is first evaluated on a first instance. For example, we observe a
18+
runtime of 2s. As the challenger appears to be a promising configuration, its evaluation is intensified and the budget
19+
is doubled, i.e., the budget is increased to 2. For solving the second instance, adaptive capping will allow a timeout
20+
of 5s since the sum of runtimes for the incumbent is 7s and the challenger used up 2s for solving the first instance so
21+
far so that 5s remain until the costs of the incumbent are exceeded. Even if the challenger configuration would need 10s
22+
to solve the second instance, its execution would be aborted. In this example, by adaptive capping we thus save 5s of
23+
evaluation costs for the challenger to notice that it will not replace the current incumbent.*
24+
25+
In combination with random online aggressive racing, we can further speedup the evaluation of challenger configurations
26+
as we increase the horizon for adaptive capping step by step with every step of intensification. Note that
27+
intensification will double the number of instances to which the challenger configuration (and eventually also the
28+
incumbent configuration) are applied to. Furthermore, to increase the trust into the current incumbent, the incumbent is
29+
regularly subject to intensification.
30+
31+
32+
## Setting up Adaptive Capping
33+
34+
To achieve this, the user must take active care in the termination of their target function.
35+
The capped problem.train will receive a budget keyword argument, detailing the seconds allocated to the configuration.
36+
Below is an example of a capped problem that will return the used budget if the computation exceeds the budget.
37+
38+
39+
```python
40+
41+
class TimeoutException(Exception):
42+
pass
43+
44+
45+
@contextmanager
46+
def timeout(seconds):
47+
def handler(signum, frame):
48+
raise TimeoutException(f"Function call exceeded timeout of {seconds} seconds")
49+
50+
# Set the signal handler for the alarm signal
51+
signal.signal(signal.SIGALRM, handler)
52+
signal.alarm(seconds) # Schedule an alarm after the given number of seconds
53+
54+
try:
55+
yield
56+
finally:
57+
# Cancel the alarm if the block finishes before timeout
58+
signal.alarm(0)
59+
60+
61+
class CappedProblem:
62+
@property
63+
def configspace(self) -> ConfigurationSpace:
64+
...
65+
66+
def train(self, config: Configuration, instance:str, budget, seed: int = 0) -> float:
67+
68+
try:
69+
with timeout(int(math.ceil(budget))):
70+
start_time = time.time()
71+
... # heavy computation
72+
runtime = time.time() - start_time
73+
return runtime
74+
except TimeoutException as e:
75+
print(f"Timeout for configuration {config} with runtime budget {budget}")
76+
return budget # here the runtime is capped and we return the used budget.
77+
```
78+
79+
In order to enable adaptive capping in smac, we need to create [problem instances](4_instances.md) to optimize over and specify a
80+
global runtime cutoff in the intensifier. Then we optimize as usual.
81+
82+
83+
```python
84+
from smac.intensifier import Intensifier
85+
from smac.scenario.scenario import Scenario
86+
87+
scenario = Scenario(
88+
capped_problem.configspace,
89+
...
90+
instances=['1', '2', '3'], # add problem instances we want to solve
91+
instance_features={'1': [1], '2': [2], '3': [3]} # in the absence of actual features add dummy features for identification
92+
)
93+
94+
intensifier = Intensifier(
95+
scenario,
96+
runtime_cutoff=10 # specify an absolute runtime cutoff (sum over instances) never to be exceeded
97+
)
98+
99+
smac = HyperparameterOptimizationFacade(
100+
scenario,
101+
capped_problem.train,
102+
intensifier=intensifier,
103+
...
104+
)
105+
106+
incumbent = smac.optimize()
107+
```
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""Adaptive Capping
2+
# Flags: doc-Runnable
3+
4+
Adaptive capping is often used in optimization algorithms, particularly in
5+
scenarios where the time taken to evaluate solutions can vary significantly.
6+
For more details on adaptive capping, consult the [info page adaptive capping](../../../advanced_usage/13_adaptive_capping.html).
7+
8+
"""
9+
import math
10+
import time
11+
12+
import signal
13+
from contextlib import contextmanager
14+
15+
from smac.runhistory import InstanceSeedBudgetKey, TrialInfo
16+
17+
import warnings
18+
19+
from ConfigSpace import Categorical, Configuration, ConfigurationSpace, Float
20+
21+
22+
class TimeoutException(Exception):
23+
pass
24+
25+
@contextmanager
26+
def timeout(seconds):
27+
def handler(signum, frame):
28+
raise TimeoutException(f"Function call exceeded timeout of {seconds} seconds")
29+
30+
# Set the signal handler for the alarm signal
31+
signal.signal(signal.SIGALRM, handler)
32+
signal.alarm(seconds) # Schedule an alarm after the given number of seconds
33+
34+
try:
35+
yield
36+
finally:
37+
# Cancel the alarm if the block finishes before timeout
38+
signal.alarm(0)
39+
40+
41+
class CappedProblem:
42+
@property
43+
def configspace(self) -> ConfigurationSpace:
44+
cs = ConfigurationSpace(seed=0)
45+
x0 = Float("x0", (0, 5), default=5, log=False)
46+
x1 = Float("x1", (0, 7), default=7, log=False)
47+
cs.add_hyperparameters([x0, x1])
48+
return cs
49+
50+
def train(self, config: Configuration, instance:str, cutoff, seed: int = 0) -> float:
51+
x0 = config["x0"]
52+
x1 = config["x1"]
53+
54+
try:
55+
with timeout(int(math.ceil(cutoff))):
56+
runtime = 0.5 * x1 + 0.5 * x0 * int(instance)
57+
time.sleep(runtime)
58+
return runtime
59+
except TimeoutException as e:
60+
print(f"Timeout for configuration {config} with runtime cutoff {cutoff}")
61+
return cutoff + 1
62+
63+
64+
if __name__ == '__main__':
65+
from smac import AlgorithmConfigurationFacade
66+
from smac import Scenario
67+
68+
capped_problem = CappedProblem()
69+
70+
scenario = Scenario(
71+
capped_problem.configspace,
72+
walltime_limit=3600, # After 3600 seconds, we stop the hyperparameter optimization
73+
n_trials=500, # Evaluate max 500 different trials
74+
instances=['1', '2', '3'],
75+
instance_features={'1': [1], '2': [2], '3': [3]},
76+
adaptive_capping=True,
77+
runtime_cutoff=200 # We allow an algorithm at maximum 200s to solve all instances
78+
)
79+
80+
# We want to run five random configurations before starting the optimization.
81+
initial_design = AlgorithmConfigurationFacade.get_initial_design(scenario, n_configs=5)
82+
83+
# Create our SMAC object and pass the scenario and the train method
84+
smac = AlgorithmConfigurationFacade(
85+
scenario,
86+
capped_problem.train,
87+
initial_design=initial_design,
88+
overwrite=True,
89+
)
90+
91+
# Let's optimize
92+
incumbent = smac.optimize()
93+
94+
# Get cost of default configuration
95+
default_cost = smac.validate(capped_problem.configspace.get_default_configuration())
96+
97+
# Let's calculate the cost of the incumbent
98+
incumbent_cost = smac.validate(incumbent)

examples/1_basics/8_warmstart.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
With the ask and tell interface, we can support warmstarting SMAC. We can communicate rich
55
information about the previous trials to SMAC using `TrialInfo` and `TrialValue` instances.
6-
For more details on ask and tell consult the [info page ask-and-tell](../../../advanced_usage/5_ask_and_tell).
6+
For more details on ask and tell consult the [info page ask-and-tell](../../../advanced_usage/5_ask_and_tell.html).
77
"""
88
from __future__ import annotations
99

mkdocs.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ nav:
207207
- "advanced_usage/10_continue.md"
208208
- "advanced_usage/11_reproducibility.md"
209209
- "advanced_usage/12_optimizations.md"
210+
- "advanced_usage/13_adaptive_capping.md"
210211
# Auto generated with docs/examples_runner.py
211212
- Examples: "examples/"
212213
# Auto generated with docs/api_generator.py

0 commit comments

Comments
 (0)