The bbob suite: the 24 standard noiseless functions, one class each.
Every function is verified numerically against the official 2009 BBOB
implementation (bbobbenchmarks.py) on identical instances in float64 -- see
tests/test_alignment.py. Where the paper (Hansen, Finck, Ros, Auger,
INRIA RR-6829) and the official code disagree, the code wins, with a comment at
the site: the code is what every published BBOB result actually ran.
Three conventions to know:
_value receives fully prepared params: params.x_opt is the true argmin
of every function, drawn by _sample_x_opt, mirroring how COCO stores the
optimum its own construction ends up at.
_value returns (value, penalty) with the optimum at value 0; f_opt is
added by BBOBProblem.evaluate, and noise applies to the value only.
- Every constant the paper or COCO gives a name to is a class attribute, so a
function's settings are readable without reading its body. Two names recur
and are spelled the same way throughout, whatever the COCO file happens to
call them locally:
condition is the conditioning, and penalty_factor is
the multiplier on the boundary penalty. Constants that are part of a
function's own formula and unnamed upstream (Rosenbrock's 100, Griewank's
4000) stay inline, where the formula reads.
Where two of the 24 are the same landscape at different settings -- the two
Schaffers conditionings, the two Gallagher peak counts -- that relationship is
a subclass with different class attributes, so the math is written once.
AttractiveSector
Bases: BBOBProblem
Attractive Sector Function (Hansen et al., 2010, p. 30).
Source code in src/bbobax/bbob.py
| class AttractiveSector(BBOBProblem):
"""Attractive Sector Function (Hansen et al., 2010, p. 30)."""
name = "attractive_sector"
# Conditioning.
condition: float = 10.0
# Exponent of the final power transform (COCO: `transform_obj_power`).
power: float = 0.9
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
z = lambda_alpha(self.condition, self.num_dims) * z
z = params.q @ z
s = jnp.where(z * params.x_opt > 0.0, 100.0, 1.0)
value = jnp.power(transform_osz(jnp.sum(jnp.square(s * z))), self.power)
return value, jnp.array(0.0)
|
BentCigar
Bases: BBOBProblem
Bent Cigar Function (Hansen et al., 2010, p. 60).
Source code in src/bbobax/bbob.py
| class BentCigar(BBOBProblem):
"""Bent Cigar Function (Hansen et al., 2010, p. 60)."""
name = "bent_cigar"
# Conditioning of all but the first axis.
condition: float = 1e6
# Asymmetry, the paper's beta.
beta: float = 0.5
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
z = transform_asy(z, self.beta)
z = params.r @ z
z_squared = jnp.square(z)
value = jnp.sum(z_squared.at[1:].multiply(self.condition))
return value, jnp.array(0.0)
|
BuecheRastrigin
Bases: BBOBProblem
Bueche-Rastrigin Function (Hansen et al., 2010, p. 20).
Source code in src/bbobax/bbob.py
| class BuecheRastrigin(BBOBProblem):
"""Bueche-Rastrigin Function (Hansen et al., 2010, p. 20)."""
name = "bueche_rastrigin"
# Conditioning.
condition: float = 10.0
# Multiplier on the boundary penalty.
penalty_factor: float = 100.0
def _sample_x_opt(self, key: jax.Array) -> jax.Array:
"""Draw the optimum with its skewed (0-based even) coordinates non-negative.
Both official implementations do this (`f_bueche_rastrigin.c:81-84`,
whose comment notes it is "in the legacy code but _not_ in the function
description"; `bbobbenchmarks` applies `xopt[::2] = abs(...)`). The
paper omits it; the code defines the instances every published result
ran on, so the code wins.
"""
x_opt = super()._sample_x_opt(key)
is_even = jnp.arange(self.num_dims) % 2 == 0
return jnp.where(is_even, jnp.abs(x_opt), x_opt)
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
# The paper's "for i = 1, 3, 5, ..." is 1-based; the skewed coordinates
# are the 0-based EVEN ones. Both officials agree (transform_vars_brs.c:46
# with a comment flagging exactly this trap; bbobbenchmarks applies it to
# x[::2]). The paper's seemingly circular z/s definition resolves cleanly:
# s_i > 0 always, so conditioning on the post-osz sign (as the officials
# do) is exact.
z = transform_osz(x - params.x_opt)
conditioning = lambda_alpha(self.condition, self.num_dims)
is_skewed = (z > 0.0) & (jnp.arange(self.num_dims) % 2 == 0)
z = jnp.where(is_skewed, 10.0, 1.0) * conditioning * z
oscillation = jnp.sum(jnp.cos(2 * jnp.pi * z))
value = 10 * (self.num_dims - oscillation) + jnp.sum(jnp.square(z))
return value, self.penalty_factor * f_pen(x)
|
DifferentPowers
Bases: BBOBProblem
Different Powers Function (Hansen et al., 2010, p. 70).
Source code in src/bbobax/bbob.py
| class DifferentPowers(BBOBProblem):
"""Different Powers Function (Hansen et al., 2010, p. 70)."""
name = "different_powers"
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
exp = 2.0 + 4.0 * jnp.arange(self.num_dims) / (self.num_dims - 1)
value = jnp.sqrt(jnp.sum(jnp.power(jnp.abs(z), exp)))
return value, jnp.array(0.0)
|
Discus
Bases: BBOBProblem
Discus Function (Hansen et al., 2010, p. 55).
Source code in src/bbobax/bbob.py
| class Discus(BBOBProblem):
"""Discus Function (Hansen et al., 2010, p. 55)."""
name = "discus"
# Conditioning of the single distinguished axis.
condition: float = 1e6
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
z = transform_osz(z)
z_squared = jnp.square(z)
value = jnp.sum(z_squared.at[0].multiply(self.condition))
return value, jnp.array(0.0)
|
Ellipsoidal
Bases: BBOBProblem
Ellipsoidal Function (Hansen et al., 2010, p. 10).
Source code in src/bbobax/bbob.py
| class Ellipsoidal(BBOBProblem):
"""Ellipsoidal Function (Hansen et al., 2010, p. 10)."""
name = "ellipsoidal"
# Conditioning (COCO: the `conditioning` f_ellipsoid is allocated with).
condition: float = 1e6
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = transform_osz(x - params.x_opt)
exp = jnp.arange(self.num_dims) / (self.num_dims - 1)
value = jnp.sum(jnp.power(self.condition, exp) * jnp.square(z))
return value, jnp.array(0.0)
|
EllipsoidalRotated
Bases: BBOBProblem
Ellipsoidal Function, rotated (Hansen et al., 2010, p. 50).
Not a subclass of Ellipsoidal: only the conditioning is shared, the
coordinate pipeline differs, so subclassing would inherit one constant and
override everything else. Subclassing is reserved here for the pairs that
share their whole _value -- the two Schaffers and the two Gallaghers.
Source code in src/bbobax/bbob.py
| class EllipsoidalRotated(BBOBProblem):
"""Ellipsoidal Function, rotated (Hansen et al., 2010, p. 50).
Not a subclass of `Ellipsoidal`: only the conditioning is shared, the
coordinate pipeline differs, so subclassing would inherit one constant and
override everything else. Subclassing is reserved here for the pairs that
share their whole `_value` -- the two Schaffers and the two Gallaghers.
"""
name = "ellipsoidal_rotated"
# Conditioning.
condition: float = 1e6
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = transform_osz(params.r @ (x - params.x_opt))
exp = jnp.arange(self.num_dims) / (self.num_dims - 1)
value = jnp.sum(jnp.power(self.condition, exp) * jnp.square(z))
return value, jnp.array(0.0)
|
Gallagher101Me
Bases: _Gallagher
Gallagher's Gaussian 101-me Peaks Function (Hansen et al., 2010, p. 105).
Source code in src/bbobax/bbob.py
| class Gallagher101Me(_Gallagher):
"""Gallagher's Gaussian 101-me Peaks Function (Hansen et al., 2010, p. 105)."""
name = "gallagher_101_me"
num_optima = 101
first_condition = 1000.0
y_range = 5.0
fold = 21
|
Gallagher21Hi
Bases: _Gallagher
Gallagher's Gaussian 21-hi Peaks Function (Hansen et al., 2010, p. 110).
Source code in src/bbobax/bbob.py
| class Gallagher21Hi(_Gallagher):
"""Gallagher's Gaussian 21-hi Peaks Function (Hansen et al., 2010, p. 110)."""
name = "gallagher_21_hi"
num_optima = 21
first_condition = 1000.0**2
y_range = 4.9
fold = 22
def _sample_x_opt(self, key: jax.Array) -> jax.Array:
"""Draw from [-3.92, 3.92]^D, the 21-hi global peak's range (paper 5.22)."""
return 0.98 * super()._sample_x_opt(key)
|
GriewankRosenbrock
Bases: BBOBProblem
Composite Griewank-Rosenbrock Function F8F2 (Hansen et al., 2010, p. 95).
Source code in src/bbobax/bbob.py
| class GriewankRosenbrock(BBOBProblem):
"""Composite Griewank-Rosenbrock Function F8F2 (Hansen et al., 2010, p. 95)."""
name = "griewank_rosenbrock"
# Overall scale of the composition (bbobbenchmarks: `facftrue`). The noisy
# suite's f125-f127 are this same function at 1 rather than 10.
facftrue: float = 10.0
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
# Same deliberate parameterization as RosenbrockRotated: official BBOB
# has z = s*R*x + 1/2 with an R-derived optimum; this form is verified
# exactly equivalent (x_opt = R^T (0.5/s) 1 reproduces it bit-for-bit)
# and makes params.x_opt the argmin.
scale = jnp.maximum(1.0, jnp.sqrt(self.num_dims) / 8.0)
z = scale * (params.r @ (x - params.x_opt)) + 1.0
z_i = z[:-1]
z_ip1 = jnp.roll(z, -1)[:-1]
s = 100.0 * jnp.square(jnp.square(z_i) - z_ip1) + jnp.square(z_i - 1)
out = jnp.sum(s / 4000.0 - jnp.cos(s))
value = self.facftrue * out / (self.num_dims - 1) + self.facftrue
return value, jnp.array(0.0)
|
Katsuura
Bases: BBOBProblem
Katsuura Function (Hansen et al., 2010, p. 115).
Source code in src/bbobax/bbob.py
| class Katsuura(BBOBProblem):
"""Katsuura Function (Hansen et al., 2010, p. 115)."""
name = "katsuura"
# Conditioning.
condition: float = 100.0
# Terms of the inner series, exactly as official. The powers are computed
# in floating point (2.0**j), so there is no integer overflow; in float32
# the j > 24 terms are below mantissa resolution and contribute harmless
# near-zero noise, while in float64 all 32 are needed for exactness (J=30
# costs ~1e-7 absolute).
num_terms: int = 32
# Multiplier on the boundary penalty.
penalty_factor: float = 1.0
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
z = lambda_alpha(self.condition, self.num_dims) * z
z = params.q @ z
two_pow_j = jnp.power(2.0, jnp.arange(1, self.num_terms + 1))
out = jnp.sum(
jnp.abs(two_pow_j * z[:, None] - jnp.round(two_pow_j * z[:, None]))
/ two_pow_j,
axis=1,
)
prod = jnp.prod(1.0 + jnp.arange(1, self.num_dims + 1) * out)
value = (10.0 / self.num_dims**2) * (
jnp.power(prod, 10.0 / self.num_dims**1.2) - 1.0
)
return value, self.penalty_factor * f_pen(x)
|
LinearSlope
Bases: BBOBProblem
Linear Slope (Hansen et al., 2010, p. 25).
Source code in src/bbobax/bbob.py
| class LinearSlope(BBOBProblem):
"""Linear Slope (Hansen et al., 2010, p. 25)."""
name = "linear_slope"
# Conditioning of the slope (COCO: `alpha` in f_linear_slope.c).
condition: float = 100.0
def _sample_x_opt(self, key: jax.Array) -> jax.Array:
"""Draw a corner of the box: a linear function has no interior minimum.
Only the sign of each coordinate is an instance choice, so that is what
is drawn -- `x_opt_range` does not apply here.
"""
is_positive = jax.random.bernoulli(key, shape=(self.num_dims,))
return jnp.where(is_positive, 5.0, -5.0)
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
# params.x_opt is the true optimum: +-5 per coordinate, drawn by
# _sample_x_opt.
x_opt = params.x_opt
z = jnp.where(x * x_opt < 25.0, x, x_opt)
s = jnp.sign(x_opt) * lambda_alpha(self.condition, self.num_dims)
value = jnp.sum(5.0 * jnp.abs(s) - s * z)
return value, jnp.array(0.0)
|
Lunacek
Bases: BBOBProblem
Lunacek bi-Rastrigin Function (Hansen et al., 2010, p. 120).
Source code in src/bbobax/bbob.py
| class Lunacek(BBOBProblem):
"""Lunacek bi-Rastrigin Function (Hansen et al., 2010, p. 120)."""
name = "lunacek"
# Conditioning.
condition: float = 100.0
# Centre of the first sphere; the optimum sits at +-half of it.
mu_0: float = 2.5
# Depth of the second sphere (COCO: `d`).
depth: float = 1.0
# Multiplier on the boundary penalty.
penalty_factor: float = 1e4
def _sample_x_opt(self, key: jax.Array) -> jax.Array:
"""Draw +-`mu_0`/2 per coordinate: only the sign is an instance choice."""
is_positive = jax.random.bernoulli(key, shape=(self.num_dims,))
return jnp.where(is_positive, self.mu_0 / 2.0, -self.mu_0 / 2.0)
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
# COCO spells s as `1 - 0.5 / (sqrt(D + 20) - 4.1)`, which is the same
# number (f_lunacek_bi_rastrigin.c:41); this is the paper's form.
s = 1.0 - 1.0 / (2.0 * jnp.sqrt(self.num_dims + 20.0) - 8.2)
mu_1 = -jnp.sqrt((self.mu_0**2 - self.depth) / s)
# params.x_opt is the true optimum, drawn by _sample_x_opt.
x_hat = 2.0 * jnp.sign(params.x_opt) * x
z = params.r @ (x_hat - self.mu_0)
z = lambda_alpha(self.condition, self.num_dims) * z
z = params.q @ z
s_1 = jnp.sum(jnp.square(x_hat - self.mu_0))
s_2 = jnp.sum(jnp.square(x_hat - mu_1))
oscillation = jnp.sum(jnp.cos(2 * jnp.pi * z))
value = jnp.minimum(s_1, self.depth * self.num_dims + s * s_2) + 10.0 * (
self.num_dims - oscillation
)
return value, self.penalty_factor * f_pen(x)
|
Rastrigin
Bases: BBOBProblem
Rastrigin Function (Hansen et al., 2010, p. 15).
Source code in src/bbobax/bbob.py
| class Rastrigin(BBOBProblem):
"""Rastrigin Function (Hansen et al., 2010, p. 15)."""
name = "rastrigin"
# Conditioning.
condition: float = 10.0
# Asymmetry, the paper's beta.
beta: float = 0.2
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = transform_asy(transform_osz(x - params.x_opt), self.beta)
z = lambda_alpha(self.condition, self.num_dims) * z
oscillation = jnp.sum(jnp.cos(2 * jnp.pi * z))
value = 10 * (self.num_dims - oscillation) + jnp.sum(jnp.square(z))
return value, jnp.array(0.0)
|
RastriginRotated
Bases: BBOBProblem
Rastrigin Function, rotated (Hansen et al., 2010, p. 75).
Source code in src/bbobax/bbob.py
| class RastriginRotated(BBOBProblem):
"""Rastrigin Function, rotated (Hansen et al., 2010, p. 75)."""
name = "rastrigin_rotated"
# Conditioning.
condition: float = 10.0
# Asymmetry, the paper's beta.
beta: float = 0.2
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
z = transform_asy(transform_osz(z), self.beta)
z = params.q @ z
z = lambda_alpha(self.condition, self.num_dims) * z
z = params.r @ z
oscillation = jnp.sum(jnp.cos(2 * jnp.pi * z))
value = 10 * (self.num_dims - oscillation) + jnp.sum(jnp.square(z))
return value, jnp.array(0.0)
|
Rosenbrock
Bases: BBOBProblem
Rosenbrock Function, original (Hansen et al., 2010, p. 40).
Source code in src/bbobax/bbob.py
| class Rosenbrock(BBOBProblem):
"""Rosenbrock Function, original (Hansen et al., 2010, p. 40)."""
name = "rosenbrock"
def _sample_x_opt(self, key: jax.Array) -> jax.Array:
"""Draw from [-3, 3]^D: Rosenbrock's optimum lives there (paper 2.8)."""
return 0.75 * super()._sample_x_opt(key)
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
# params.x_opt is the true optimum, already scaled at sampling time
# (COCO stores the scaled vector too: f_rosenbrock.c:86).
scale = jnp.maximum(1.0, jnp.sqrt(self.num_dims) / 8.0)
z = scale * (x - params.x_opt) + 1.0
z_i = z[:-1]
z_ip1 = jnp.roll(z, -1)[:-1]
out = 100.0 * jnp.square(jnp.square(z_i) - z_ip1) + jnp.square(z_i - 1)
value = jnp.sum(out)
return value, jnp.array(0.0)
|
RosenbrockRotated
Bases: BBOBProblem
Rosenbrock Function, rotated (Hansen et al., 2010, p. 45).
Source code in src/bbobax/bbob.py
| class RosenbrockRotated(BBOBProblem):
"""Rosenbrock Function, rotated (Hansen et al., 2010, p. 45)."""
name = "rosenbrock_rotated"
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
# Official BBOB uses z = s*R*x + 1/2 with the optimum derived from R
# (norm ~ sqrt(D)/2, always near the origin). This parameterization is
# verified exactly equivalent: with x_opt = R^T (0.5/s) 1 it reproduces
# the official function bit-for-bit, and any other x_opt is a pure
# translation of an official landscape. Kept deliberately: the optimum
# is placeable, and params.x_opt is the argmin like everywhere else.
scale = jnp.maximum(1.0, jnp.sqrt(self.num_dims) / 8.0)
z = scale * (params.r @ (x - params.x_opt)) + 1.0
z_i = z[:-1]
z_ip1 = jnp.roll(z, -1)[:-1]
out = 100.0 * jnp.square(jnp.square(z_i) - z_ip1) + jnp.square(z_i - 1)
value = jnp.sum(out)
return value, jnp.array(0.0)
|
SchaffersF7
Bases: BBOBProblem
Schaffers F7 Function (Hansen et al., 2010, p. 85).
Source code in src/bbobax/bbob.py
| class SchaffersF7(BBOBProblem):
"""Schaffers F7 Function (Hansen et al., 2010, p. 85)."""
name = "schaffers_f7"
# Conditioning; the only thing f18 changes.
condition: float = 10.0
# Asymmetry, the paper's beta.
beta: float = 0.5
# Multiplier on the boundary penalty.
penalty_factor: float = 10.0
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
z = transform_asy(z, self.beta)
z = params.q @ z
z = lambda_alpha(self.condition, self.num_dims) * z
z_i = z[:-1]
z_ip1 = jnp.roll(z, -1)[:-1]
s = jnp.sqrt(jnp.square(z_i) + jnp.square(z_ip1))
out = jnp.sum(jnp.sqrt(s) + jnp.sqrt(s) * jnp.sin(50 * s**0.2) ** 2)
# The sum runs over D - 1 consecutive pairs.
value = (out / (self.num_dims - 1.0)) ** 2
return value, self.penalty_factor * f_pen(x)
|
SchaffersF7IllConditioned
Bases: SchaffersF7
Schaffers F7 Function, ill-conditioned (Hansen et al., 2010, p. 90).
The same landscape as SchaffersF7 with the conditioning raised from 10 to
1000 -- which is exactly how the official suite defines f18 against f17.
Source code in src/bbobax/bbob.py
| class SchaffersF7IllConditioned(SchaffersF7):
"""Schaffers F7 Function, ill-conditioned (Hansen et al., 2010, p. 90).
The same landscape as `SchaffersF7` with the conditioning raised from 10 to
1000 -- which is exactly how the official suite defines f18 against f17.
"""
name = "schaffers_f7_ill_conditioned"
condition: float = 1000.0
|
Schwefel
Bases: BBOBProblem
Schwefel Function (Hansen et al., 2010, p. 100).
Source code in src/bbobax/bbob.py
| class Schwefel(BBOBProblem):
"""Schwefel Function (Hansen et al., 2010, p. 100)."""
name = "schwefel"
# Conditioning.
condition: float = 10.0
# The constant Schwefel is built around; the optimum sits at +-half of it.
# (The C source carries ...4637 in one literal; the paper, bbobbenchmarks
# and C's own best_parameter all say ...4633.)
constant: float = 4.2096874633
# Multiplier on the boundary penalty, which acts on the scaled z.
penalty_factor: float = 100.0
def _sample_x_opt(self, key: jax.Array) -> jax.Array:
"""Draw +-`constant`/2 per coordinate: only the sign is an instance choice."""
is_positive = jax.random.bernoulli(key, shape=(self.num_dims,))
return jnp.where(is_positive, self.constant / 2.0, -self.constant / 2.0)
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
# params.x_opt is the true optimum, drawn by _sample_x_opt.
x_opt = params.x_opt
sign = jnp.where(x_opt > 0.0, 1.0, -1.0)
x_hat = 2.0 * sign * x
x_hat_im1 = jnp.roll(x_hat, 1).at[0].set(0.0)
x_opt_im1 = jnp.roll(x_opt, 1).at[0].set(0.0)
z_hat = x_hat + 0.25 * (x_hat_im1 - 2 * jnp.abs(x_opt_im1))
z = 100 * (
lambda_alpha(self.condition, self.num_dims) * (z_hat - 2 * jnp.abs(x_opt))
+ 2 * jnp.abs(x_opt)
)
out = jnp.sum(z * jnp.sin(jnp.sqrt(jnp.abs(z))))
value = -(out / (100.0 * self.num_dims)) + 4.189828872724339
return value, self.penalty_factor * f_pen(z / 100)
|
SharpRidge
Bases: BBOBProblem
Sharp Ridge Function (Hansen et al., 2010, p. 65).
Source code in src/bbobax/bbob.py
| class SharpRidge(BBOBProblem):
"""Sharp Ridge Function (Hansen et al., 2010, p. 65)."""
name = "sharp_ridge"
# Conditioning.
condition: float = 10.0
# Weight of the ridge (COCO: `alpha` in f_sharp_ridge.c).
alpha: float = 100.0
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
z = lambda_alpha(self.condition, self.num_dims) * z
z = params.q @ z
z_squared = jnp.square(z)
value = z_squared[0] + self.alpha * jnp.sqrt(jnp.sum(z_squared[1:]))
return value, jnp.array(0.0)
|
Sphere
Bases: BBOBProblem
Sphere Function (Hansen et al., 2010, p. 5).
Source code in src/bbobax/bbob.py
| class Sphere(BBOBProblem):
"""Sphere Function (Hansen et al., 2010, p. 5)."""
name = "sphere"
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = x - params.x_opt
value = jnp.sum(jnp.square(z))
return value, jnp.array(0.0)
|
StepEllipsoidal
Bases: BBOBProblem
Step Ellipsoidal Function (Hansen et al., 2010, p. 35).
Source code in src/bbobax/bbob.py
| class StepEllipsoidal(BBOBProblem):
"""Step Ellipsoidal Function (Hansen et al., 2010, p. 35)."""
name = "step_ellipsoidal"
# Conditioning of the ellipsoid.
condition: float = 100.0
# Conditioning applied before the step (COCO: `alpha` in f_step_ellipsoid.c).
alpha: float = 10.0
# Multiplier on the boundary penalty.
penalty_factor: float = 1.0
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z_hat = params.r @ (x - params.x_opt)
z_hat = lambda_alpha(self.alpha, self.num_dims) * z_hat
# floor(0.5 + z) is round-half-up, matching COCO's C
# (`coco_double_round`). The 2009 Python reference uses numpy's
# round-half-to-even instead, so the two officials disagree on exact .5
# ties; ties have measure zero and the C is what the suite ships.
z_tilde = jnp.where(
jnp.abs(z_hat) > 0.5,
jnp.floor(0.5 + z_hat),
jnp.floor(0.5 + 10.0 * z_hat) / 10.0,
)
z = params.q @ z_tilde
exp = jnp.arange(self.num_dims) / (self.num_dims - 1.0)
# No leading coefficient: the conditioning lives entirely in the
# exponent. (An earlier version carried an extra x100 here; paper 2.7,
# f_step_ellipsoid.c and bbobbenchmarks all agree there is none.)
out = jnp.sum(jnp.power(self.condition, exp) * jnp.square(z))
value = 0.1 * jnp.maximum(jnp.abs(z_hat[0]) / 1e4, out)
return value, self.penalty_factor * f_pen(x)
|
Weierstrass
Bases: BBOBProblem
Weierstrass Function (Hansen et al., 2010, p. 80).
Source code in src/bbobax/bbob.py
| class Weierstrass(BBOBProblem):
"""Weierstrass Function (Hansen et al., 2010, p. 80)."""
name = "weierstrass"
# Conditioning. The series flattens the landscape, so the conditioning
# enters inverted: COCO spells it `base = 1 / sqrt(condition)`.
condition: float = 100.0
# Summands of the truncated series (COCO: `F_WEIERSTRASS_SUMMANDS`).
k_order: int = 12
# Multiplier on the boundary penalty, divided by the dimension.
penalty_factor: float = 10.0
# The series coefficients and f_0, built once. With numpy rather than jnp:
# a jnp constant would freeze its dtype when the class is created, silently
# staying float32 under `jax_enable_x64` and costing precision.
_half_pow_k = np.power(0.5, np.arange(k_order))
_three_pow_k = np.power(3.0, np.arange(k_order))
_f_0 = np.sum(_half_pow_k * np.cos(np.pi * _three_pow_k))
def _value(self, x: jax.Array, params: BBOBParams) -> tuple[jax.Array, jax.Array]:
z = params.r @ (x - params.x_opt)
z = transform_osz(z)
z = params.q @ z
z = lambda_alpha(1.0 / self.condition, self.num_dims) * z
z = params.r @ z
out = jnp.sum(
self._half_pow_k
* jnp.cos(2 * jnp.pi * self._three_pow_k * (z[:, None] + 0.5))[:, None]
)
value = 10 * (out / self.num_dims - self._f_0) ** 3
return value, self.penalty_factor * f_pen(x) / self.num_dims
|
bbob_suite(names=None, **kwargs)
Build the standard BBOB functions as individual problems.
Parameters:
| Name |
Type |
Description |
Default |
names
|
list[str] | None
|
Which functions to include; defaults to all 24, in the
canonical f1-f24 order.
|
None
|
**kwargs
|
Any
|
Passed to every problem (num_dims, noise_model, ...).
|
{}
|
Returns:
| Name | Type |
Description |
|
dict[str, BBOBProblem]
|
A mapping from function name to problem. Loop over it to cover the
|
suite |
dict[str, BBOBProblem]
|
each problem compiles separately, so nothing pays for dispatch.
|
Raises:
| Type |
Description |
KeyError
|
If names contains something that is not a BBOB function.
|
Source code in src/bbobax/bbob.py
| def bbob_suite(names: list[str] | None = None, **kwargs: Any) -> dict[str, BBOBProblem]:
"""Build the standard BBOB functions as individual problems.
Args:
names: Which functions to include; defaults to all 24, in the
canonical f1-f24 order.
**kwargs: Passed to every problem (`num_dims`, `noise_model`, ...).
Returns:
A mapping from function name to problem. Loop over it to cover the
suite: each problem compiles separately, so nothing pays for dispatch.
Raises:
KeyError: If `names` contains something that is not a BBOB function.
"""
names = list(BBOB_PROBLEMS) if names is None else names
unknown = [name for name in names if name not in BBOB_PROBLEMS]
if unknown:
raise KeyError(
f"not BBOB functions: {unknown}; available: {sorted(BBOB_PROBLEMS)}"
)
return {name: BBOB_PROBLEMS[name](**kwargs) for name in names}
|