Many-Affine BBOB
bbobax.many_affine
Many-Affine BBOB: a problem built by combining all 24.
MA-BBOB (Vermetten, Ye, Back, Doerr) draws a sparse weight vector over the 24
functions and combines their values in log space, giving a continuous space of
problems rather than a set of 24. That is what makes it worth having here:
meta-learning over bbob_suite() samples 24 atoms, while meta-learning over
ManyAffine samples a space.
Transcribed from the reference implementation, IOHexperimenter's
ManyAffine (include/ioh/problem/bbob/many_affine.hpp), whose combination is
for each of the 24:
x0 = x + x_opt_i - x_opt # each component sees its own optimum
f0 = clip(f_i(x0) - f_opt_i, 1e-12, 1e20)
result += w_i * (log10(f0) + 8) / scale_i
value = 10 ** (10 * result - 8)
Two things follow from the shift. Every component is evaluated at its own
optimum when x is the combined problem's optimum, so the combination is
anchored there; and x0 can leave the box even when x does not, so each
component's boundary penalty is live and is folded into the log-space sum.
f(x_opt) is therefore not 0 -- the reference evaluates it in its constructor
and stores it as the optimum's value. bbobax's convention is that _value
returns 0 at the optimum and evaluate adds f_opt, so that constant is
subtracted here. It is available in closed form: at the optimum every
component contributes exactly clip(0, 1e-12, ...) -> 1e-12, hence
(log10(1e-12) + 8) / scale_i = -4 / scale_i, needing no extra evaluation.
Add it back to compare against a reference value.
ManyAffine
Bases: BBOBProblem
A weighted affine combination of all 24 BBOB functions, in log space.
The instance is the weight vector and the 24 component instances, all drawn
from params.key rather than stored -- the same choice the Gallagher
functions make for their peak layouts, and for the same reason: it is
instance structure that is cheaper to derive than to carry, and it keeps
BBOBParams the one parameter type every problem here uses, with no
subclass narrowing _value's signature.
Deriving it costs nothing at steady state, which was worth measuring rather
than assuming: the derivation depends on params and not on x, so XLA
hoists it out of a vmap over solutions. A batch through ManyAffine
takes 0.92x the time of evaluating the 24 separately at D = 10, and 0.78x
at D = 40 -- at or below the floor of "24 function evaluations", because
the components fuse into one program.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_dims
|
int
|
The problem dimension. |
10
|
**kwargs
|
Any
|
Passed to |
{}
|
Source code in src/bbobax/many_affine.py
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 | |
__init__(num_dims=10, **kwargs)
Initialize the problem, and the 24 it is built from.