class BoxConstraintsLinQuadTransformation(BoxConstraintsTransformationBase):
implement a bijective, monotonous transformation between [lb - al, ub + au] and [lb, ub].
Generally speaking, this transformation aims to resemble sin to be a continuous differentiable (ie. C^1) transformation over R into a bounded interval; then, it also aims to improve over sin in the following two ways: (i) resemble the identity over an interval as large possible while keeping the second derivative in reasonable limits, and (ii) numerical stability in "pathological" corner cases of the boundary limit values.
The transformation is the identity (and therefore linear) in [lb + al, ub - au] (typically about 90% of the interval) and quadratic in [lb - 3*al, lb + al] and in [ub - au, ub + 3*au]. The transformation is periodically expanded beyond the limits (somewhat resembling the shape sin(x-pi/2)) with a period of 2 * (ub - lb + al + au).
Details
Partly due to numerical considerations depend the values al and au on abs(lb) and abs(ub) which makes the transformation non-translation invariant. In particular, the linear proportion decreases to zero when ub-lb becomes small. In contrast to sin(.), the transformation is also robust to "arbitrary" large values for boundaries, e.g. a lower bound of -1e99 or upper bound of np.Inf or bound None.
Examples
Example to use with cma:
>>> import warnings >>> import cma >>> from cma.transformations import BoxConstraintsLinQuadTransformation >>> # only the first variable has an upper bound >>> tf = BoxConstraintsLinQuadTransformation([[1,2], [1,None]]) # second==last pair is re-cycled >>> with warnings.catch_warnings(record=True) as warns: ... x, es = cma.fmin2(cma.ff.elli, 9 * [2], 1, ... {'transformation': [tf.transform, tf.inverse], ... 'verb_disp':0, 'tolflatfitness': 1e9, 'verbose': -2}) >>> not warns or str(warns[0].message).startswith(('in class GenoPheno: user defi', ... 'flat fitness')) True
or:
>>> es = cma.CMAEvolutionStrategy(4 * [2], 1, {'verbose':0, 'verb_log':0}) # doctest: +ELLIPSIS (4_w,8)-aCMA-ES (mu_w=... >>> with warnings.catch_warnings(record=True) as warns: # flat fitness warning, not necessary anymore ... while not es.stop(): ... X = es.ask() ... f = [cma.ff.elli(tf(x)) for x in X] # tf(x)==tf.transform(x) ... es.tell(X, f) >>> assert 'tolflatfitness' in es.stop(), str(es.stop())
Example of the internal workings:
>>> from cma.transformations import BoxConstraintsLinQuadTransformation >>> tf = BoxConstraintsLinQuadTransformation([[1,2], [1,11], [1,11]]) >>> tf.bounds [[1, 2], [1, 11], [1, 11]] >>> tf([1.5, 1.5, 1.5]) [1.5, 1.5, 1.5] >>> list(np.round(tf([1.52, -2.2, -0.2, 2, 4, 10.4]), 9)) [1.52, 4.0, 2.0, 2.0, 4.0, 10.4] >>> res = np.round(tf._au, 2) >>> assert list(res[:4]) == [ 0.15, 0.6, 0.6, 0.6] >>> res = [round(x, 2) for x in tf.shift_or_mirror_into_invertible_domain([1.52, -12.2, -0.2, 2, 4, 10.4])] >>> assert res == [1.52, 9.2, 2.0, 2.0, 4.0, 10.4] >>> tmp = tf([1]) # call with lower dimension
Method | __call__ |
Undocumented |
Method | __init__ |
x is defined in [lb - 3*al, ub + au + r - 2*al] with r = ub - lb + al + au, and x == transformation(x) in [lb + al, ub - au]. |
Method | idx |
return indices of "infeasible" variables, that is, variables that do not directly map into the feasible domain such that tf.inverse(tf(x)) == x. |
Method | initialize |
see __init__ |
Method | is |
return True if value x is in the invertible domain of variable i |
Method | is |
never used |
Method | shift |
parameter solution_genotype is changed. |
Method | _inverse |
return inverse of y in component i |
Method | _shift |
shift into the invertible domain [lb - ab, ub + au], mirror close to boundaries in order to get a smooth transformation everywhere |
Method | _transform |
return transform of x in component i |
Instance Variable | _al |
Undocumented |
Instance Variable | _au |
Undocumented |
Instance Variable | _lb |
Undocumented |
Instance Variable | _ub |
Undocumented |
Inherited from BoxConstraintsTransformationBase
:
Method | bounds |
return [ith_lower_bound, ith_upper_bound] |
Method | inverse |
Undocumented |
Instance Variable | bounds |
Undocumented |
Method | _index |
Undocumented |
Method | _lowerupperval |
Undocumented |
x is defined in [lb - 3*al, ub + au + r - 2*al] with r = ub - lb + al + au, and x == transformation(x) in [lb + al, ub - au].
beta*x - alphal = beta*x - alphau is then defined in [lb, ub].
alphal and alphau represent the same value, but respectively numerically better suited for values close to lb and ub.
todo: revise this to be more comprehensible.
return indices of "infeasible" variables, that is, variables that do not directly map into the feasible domain such that tf.inverse(tf(x)) == x.
cma.transformations.BoxConstraintsTransformationBase.shift_or_mirror_into_invertible_domain
parameter solution_genotype is changed.
The domain is [lb - al, ub + au] and in [lb - 2*al - (ub - lb) / 2, lb - al] mirroring is applied.
shift into the invertible domain [lb - ab, ub + au], mirror close to boundaries in order to get a smooth transformation everywhere