Skip to content

Commit 659fa0c

Browse files
committed
LB-IC integer handling
1 parent 77e7159 commit 659fa0c

File tree

4 files changed

+639
-94
lines changed

4 files changed

+639
-94
lines changed

cma/evolution_strategy.py

Lines changed: 42 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@
205205
from . import constraints_handler as _constraints_handler
206206
from cma import fitness_models as _fitness_models
207207
from .constraints_handler import BoundNone, BoundPenalty, BoundTransform, AugmentedLagrangian
208+
from .integer_centering import IntegerCentering
208209
from .logger import CMADataLogger # , disp, plot
209210
from .utilities.utils import BlancClass as _BlancClass
210211
from .utilities.utils import rglen #, global_verbosity
@@ -231,6 +232,10 @@ def _callable_to_list(c):
231232
return [c]
232233
return c
233234

235+
def _pass(*args, **kwargs):
236+
"""a callable that does nothing and return args[0] in case"""
237+
return args[0] if args else None
238+
234239
# use_archives uses collections
235240
use_archives = sys.version_info[0] >= 3 or sys.version_info[1] >= 6
236241
# use_archives = False # on False some unit tests fail
@@ -913,6 +918,7 @@ def identity(x):
913918
# self.fmean = np.nan # TODO name should change? prints nan in output files (OK with matlab&octave)
914919
# self.fmean_noise_free = 0. # for output only
915920

921+
opts.amend_integer_options(N, inopts)
916922
self.sp = options_parameters.CMAParameters(N, opts, verbose=opts['verbose'] > 0)
917923
self.sp0 = self.sp # looks useless, as it is not a copy
918924

@@ -974,7 +980,14 @@ def eval_vector(in_, opts, N, default_value=1.0):
974980
opts['minstd'] = eval_vector(opts['minstd'], opts, N, 0)
975981
opts['maxstd'] = eval_vector(opts['maxstd'], opts, N, np.inf)
976982

977-
if 1 < 3 and len(opts['integer_variables']):
983+
# iiinteger handling currently as LB-IC, see cma.integer_centering:
984+
if len(opts['integer_variables']):
985+
opts.set_integer_min_std(N, self.sp.weights.mueff)
986+
self.integer_centering = IntegerCentering(self) # read opts['integer_variables'] and use boundary handler
987+
else:
988+
self.integer_centering = _pass # do nothing by default
989+
990+
if 11 < 3 and len(opts['integer_variables']):
978991
try:
979992
from . import integer
980993
s = utils.format_message(
@@ -984,46 +997,6 @@ def eval_vector(in_, opts, N, default_value=1.0):
984997
warnings.warn(s, category=DeprecationWarning) # TODO: doesn't show up
985998
except ImportError:
986999
pass
987-
opts['integer_variables'] = list(opts['integer_variables'])
988-
989-
# iiinteger handling, currently very basic:
990-
# CAVEAT: integer indices may give unexpected results if fixed_variables is used
991-
if len(opts['integer_variables']) and opts['fixed_variables']:
992-
# transform integer indices to genotype
993-
popped = [] # just for the record
994-
for i in reversed(range(self.N)):
995-
if i in opts['fixed_variables']:
996-
opts['integer_variables'].remove(i)
997-
if 1 < 3: # just for catching errors
998-
popped.append(i)
999-
if i in opts['integer_variables']:
1000-
raise ValueError("index {0} appeared more than once in `'integer_variables'` option".format(i))
1001-
# reduce integer variable indices > i by one
1002-
for j, idx in enumerate(opts['integer_variables']):
1003-
if idx > i:
1004-
opts['integer_variables'][j] -= 1
1005-
if opts['verbose'] >= 0:
1006-
warnings.warn("Handling integer variables when some variables are fixed."
1007-
"\n This code is poorly tested and fails for negative indices."
1008-
"\n Variables {0} are fixed integer variables and discarded"
1009-
" for integer handling."
1010-
.format(popped))
1011-
# 1) prepare minstd to be a vector
1012-
if (len(opts['integer_variables']) and
1013-
np.isscalar(opts['minstd'])):
1014-
opts['minstd'] = opts['minstd'] * np.ones(N)
1015-
# 2) set minstd to 1 / (2 Nint + 1),
1016-
# the setting 2 / (2 Nint + 1) already prevents convergence
1017-
for i in opts['integer_variables']:
1018-
if -N <= i < N: # when i < 0, the index computes to N + i
1019-
if opts['minstd'][i] == 0: # negative values prevent setting minstd
1020-
# opts['minstd'][i] = 1 / (2 * len(opts['integer_variables']) + 1)
1021-
opts['minstd'][i] = min((0.2, self.sp.weights.mueff / N))
1022-
else:
1023-
utils.print_warning(
1024-
"""dropping integer index %d as it is not in range of dimension %d""" %
1025-
(i, N))
1026-
opts['integer_variables'].pop(opts['integer_variables'].index(i))
10271000

10281001
# initialization of state variables
10291002
self.countiter = 0
@@ -2005,22 +1978,25 @@ def get_selective_mirrors(self, number=None):
20051978

20061979
def limit_integer_relative_deltas(self, dX, threshold=None,
20071980
recombination_weight_condition=None):
2008-
"""limit absolute values of int-coordinates in vector list `dX`
1981+
"""versatile: limit absolute values of int-coordinates in vector list `dX`
20091982
20101983
relative to the current sample standard deviations and by default
20111984
only when the respective recombination weight is negative.
20121985
1986+
This function is currently not in effect (called with threshold=inf)
1987+
and not guarantied to stay as is.
1988+
20131989
``dX == pop_sorted - mold`` where ``pop_sorted`` is a genotype.
20141990
20151991
``threshold=2.3`` by default.
20161992
20171993
A 2.3-sigma threshold affects 2 x 1.1% of the unmodified
20181994
(nonsorted) normal samples.
20191995
"""
2020-
if threshold is None: # TODO: how interpret negative thresholds?
2021-
threshold = 2.3
20221996
if not self.opts['integer_variables'] or not np.isfinite(threshold):
20231997
return dX
1998+
if threshold is None: # TODO: how interpret negative thresholds?
1999+
threshold = 2.3
20242000
if recombination_weight_condition is None:
20252001
def recombination_weight_condition(w):
20262002
return w < 0
@@ -2300,6 +2276,8 @@ def tell(self, solutions, function_values, check_points=None,
23002276
pop = np.asarray([xp[0]] + list(pop))
23012277

23022278
self.pop_sorted = pop
2279+
self.integer_centering(pop[:sp.weights.mu], self.mean)
2280+
23032281
# compute new mean
23042282
self.mean = np.dot(sp.weights.positive_weights, pop[:sp.weights.mu])
23052283
if sp.cmean != 1:
@@ -2390,8 +2368,10 @@ def tell(self, solutions, function_values, check_points=None,
23902368
pass # without CSA we may not need the mean_shift
23912369

23922370
# covariance matrix adaptation/udpate
2393-
pop_zero = self.limit_integer_relative_deltas(
2394-
pop - mold, self.opts['CMA_active_limit_int_std'])
2371+
pop_zero = self.limit_integer_relative_deltas( # does by default nothing
2372+
pop - mold,
2373+
options_parameters.integer_active_limit_std,
2374+
options_parameters.integer_active_limit_recombination_weight_condition)
23952375
if c1a + cmu > 0:
23962376
# TODO: make sure cc is 1 / N**0.5 rather than 1 / N
23972377
# TODO: simplify code: split the c1 and cmu update and call self.sm.update twice
@@ -2429,16 +2409,22 @@ def tell(self, solutions, function_values, check_points=None,
24292409
sampler_weights[i + 1] *= self.opts['CMA_active_injected']
24302410
if sampler_weights_dd[i + 1] < 0:
24312411
sampler_weights_dd[i + 1] *= self.opts['CMA_active_injected']
2432-
for s in list(self._injected_solutions_archive):
2433-
if self._injected_solutions_archive[s]['iteration'] < self.countiter - 2:
2434-
warnings.warn("""orphanated injected solution %s
2435-
This could be a bug in the calling order/logics or due to
2436-
a too small popsize used in `ask()` or when only using
2437-
`ask(1)` repeatedly. Please check carefully.
2438-
In case this is desired, the warning can be surpressed with
2439-
``warnings.simplefilter("ignore", cma.evolution_strategy.InjectionWarning)``
2440-
""" % str(self._injected_solutions_archive.pop(s)),
2441-
InjectionWarning)
2412+
for k, s in list(self._injected_solutions_archive.items()):
2413+
if s['iteration'] < self.countiter - 2:
2414+
# warn unless TPA injections were messed up by integer centering
2415+
if (not isinstance(self.adapt_sigma, CMAAdaptSigmaTPA)
2416+
# self.integer_centering and
2417+
# self.integer_centering is not _pass and
2418+
or not isinstance(self.integer_centering, IntegerCentering)
2419+
or s['index'] > 1):
2420+
warnings.warn("""orphanated injected solution %s
2421+
This could be a bug in the calling order/logics or due to
2422+
a too small popsize used in `ask()` or when only using
2423+
`ask(1)` repeatedly. Please check carefully.
2424+
In case this is desired, the warning can be surpressed with
2425+
``warnings.simplefilter("ignore", cma.evolution_strategy.InjectionWarning)``
2426+
""" % str(s), InjectionWarning)
2427+
self._injected_solutions_archive.pop(k)
24422428
assert len(sampler_weights) == len(pop_zero) + 1
24432429
if flg_diagonal:
24442430
self.sigma_vec.update(

0 commit comments

Comments
 (0)