Problem
bbobax.problem
The BBOB problem: one function at one dimension.
BBOBProblem is the contract every benchmark function in bbobax satisfies:
problem = Sphere(num_dims=10)
params = problem.sample(key) # draw an instance
evaluation = problem.evaluate(key, x, params)
Instance generation is per function, not merely per suite -- where the
optimum may sit, which rotations matter, what extra structure a landscape needs
-- so the function and the generation of its instances live in one object. A
subclass supplies _value, and overrides _sample_x_opt when its definition
constrains where the optimum can be.
There is deliberately no evaluation state. All 24 BBOB functions are
memoryless: the value at x does not depend on when x was asked. A dynamic
benchmark whose landscape moves with the evaluation count is a genuinely
different contract and would get its own, rather than a parameter these 24
carry and ignore.
BBOBEval
BBOBParams
Bases: PyTreeNode
One sampled instance of a problem.
Everything that varies between instances lives here, drawn once by
BBOBProblem.sample and never mutated. The function and the dimension are
the problem's, not the instance's -- COCO enumerates those and draws only
the instance, and so does bbobax.
keyis the instance's own PRNG key, for instance structure that is cheaper to derive than to store -- the Gallagher functions draw their peak layouts from it.x_optis the function's true argmin, drawn by_sample_x_opt.randqare the instance's rotation matrices, the R and Q of the function definitions.noise_modelis whatever the problem's noise model'ssamplereturns.
The last one follows the rule the whole package uses: a field of a params
container is named for the component whose parameters it holds, so
problem.noise_model and params.noise_model line up, exactly as
QDProblem.descriptor lines up with QDParams.descriptor.
Source code in src/bbobax/problem.py
BBOBProblem
One BBOB function at one fixed dimension.
That is COCO's own structure -- a suite enumerates function x dimension x
instance, and only the instance is drawn. A problem here fixes the function
and the dimension; sample draws an instance of it.
To cover many functions or many dimensions, hold many problems and loop
over them (bbobax.bbob_suite builds the standard 24, bbobax.DIMENSIONS
lists the standard dimensions). Under jit that loop unrolls, so each problem
keeps its own compiled code and nothing pays for dispatch -- unlike a
single problem that switches over functions, which under vmap must
evaluate every branch for every solution.
Subclasses supply _value, and override _sample_x_opt when their own
definition constrains where the optimum can be.
Every default here is COCO's, not a neutral choice: the box is [-5, 5]^D, the optimum is drawn from [-4, 4]^D, rotations are on, and the boundary is handled by an in-function penalty rather than by clipping. A problem built with the defaults is the noiseless bbob suite's, and the deviations from COCO are listed in the README rather than hidden in a signature.
A base class, where NoiseModel and Descriptor are protocols, because
the two cases are genuinely different. Those two share only a contract --
Gaussian.sample and Cauchy.sample have nothing in common -- so there
would be nothing for a base class to hold, and a third party should be able
to plug one in by shape alone. This one shares almost everything: instance
generation, noise application and the boundary-penalty convention
are written once here and a subclass fills in one hole. It also narrows
nothing -- _value has the same signature in all 24 -- so inheritance
stays substitutable, which is exactly what a per-model apply could not
manage and why the noise models are not a class hierarchy.
Source code in src/bbobax/problem.py
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 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | |
__init__(num_dims=10, x_range=(-5.0, 5.0), x_opt_range=(-4.0, 4.0), f_opt_range=(0.0, 0.0), sample_rotation=True, noise_model=None)
Initialize the problem.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_dims
|
int
|
The problem dimension, at least 2 as BBOB requires. |
10
|
x_range
|
tuple[float, float]
|
The search box. Solutions are drawn from it and QD descriptors are normalized against it, but nothing is held to it: BBOB is defined on all of R^D and about ten of the 24 functions carry their own penalty for leaving the box. |
(-5.0, 5.0)
|
x_opt_range
|
tuple[float, float]
|
Range the optimum is drawn from. BBOB uses [-4, 4].
This is the default draw: a function whose definition pins its
optimum elsewhere overrides |
(-4.0, 4.0)
|
f_opt_range
|
tuple[float, float]
|
Range of optimal fitness values. The default pins f_opt to 0; official BBOB draws a 2-decimal Cauchy clipped to +-1000 -- configure that explicitly if you want it. |
(0.0, 0.0)
|
sample_rotation
|
bool
|
Whether to sample rotation matrices. BBOB always rotates; with False, R = Q = I and every rotated variant collapses onto its axis-aligned base function (measured: f10 becomes f2 exactly). Only disable this deliberately. |
True
|
noise_model
|
NoiseModel | None
|
How values are disturbed. The default is
|
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbobax/problem.py
evaluate(key, x, params)
Evaluate the fitness of a solution.
x is evaluated as given, wherever it is: BBOB is defined on all of
R^D, and a solution outside the box is scored by the function's own
boundary penalty rather than pulled back to the edge. Clipping here
would compute that penalty at the clipped point, which for the
functions that penalize x directly switches the term off entirely.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Array
|
JAX random key, consumed by the noise model. |
required |
x
|
Array
|
Input solution, shape |
required |
params
|
BBOBParams
|
Instance parameters. |
required |
Returns:
| Type | Description |
|---|---|
BBOBEval
|
The evaluation results. |
Source code in src/bbobax/problem.py
generate_random_rotation(key, num_dims)
staticmethod
Generate a random rotation matrix, Haar-uniform on SO(n).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Array
|
JAX random key. |
required |
num_dims
|
int
|
Size of the matrix. |
required |
Returns:
| Type | Description |
|---|---|
Array
|
An orthogonal |
Source code in src/bbobax/problem.py
sample(key)
Sample an instance of this problem.
params.x_opt is always the true argmin, whatever _sample_x_opt had
to do to draw it -- the invariant COCO keeps by storing the optimum its
own construction ends up at.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Array
|
JAX random key. |
required |
Returns:
| Type | Description |
|---|---|
BBOBParams
|
The instance's parameters. |
Source code in src/bbobax/problem.py
sample_x(key)
Sample a random solution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Array
|
JAX random key. |
required |
Returns:
| Type | Description |
|---|---|
Array
|
Random solution within |