Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Numpy 2.0 compatibility #251

Merged
merged 4 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cma/bbobbenchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
<BLANKLINE>

>>> f3 = bn.F3(13) # instantiate instance 13 of function f3
>>> f3([0, 1, 2]) # short-cut for f3.evaluate([0, 1, 2]) # doctest:+ELLIPSIS
>>> f3([0, 1, 2]).item() # short-cut for f3.evaluate([0, 1, 2]) # doctest:+ELLIPSIS
59.8733529...
>>> print(bn.instantiate(5)[1]) # returns function instance and optimal f-value
51.53
Expand Down Expand Up @@ -395,7 +395,7 @@ def __call__(self, x): # makes the instances callable

>>> from cma import bbobbenchmarks as bn
>>> f3 = bn.F3(13) # instantiate function 3 on instance 13
>>> 59.8733529 < f3([0, 1, 2]) < 59.87335292 # call f3, same as f3.evaluate([0, 1, 2])
>>> 59.8733529 < f3([0, 1, 2]).item() < 59.87335292 # call f3, same as f3.evaluate([0, 1, 2])
True

"""
Expand Down
15 changes: 7 additions & 8 deletions cma/constraints_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ def _get_bounds(self, ib, dimension):
sign_ = 2 * ib - 1
assert sign_**2 == 1
if self.bounds is None or self.bounds[ib] is None:
return np.array(dimension * [sign_ * np.Inf])
return np.array(dimension * [sign_ * np.inf])
res = []
for i in range(dimension):
res.append(self.bounds[ib][min([i, len(self.bounds[ib]) - 1])])
if res[-1] is None:
res[-1] = sign_ * np.Inf
res[-1] = sign_ * np.inf
return np.array(res)

def has_bounds(self):
Expand Down Expand Up @@ -328,7 +328,7 @@ class BoundPenalty(BoundaryHandlerBase):
>>> if res[1] >= 13.76: print(res) # should never happen

Reference: Hansen et al 2009, A Method for Handling Uncertainty...
IEEE TEC, with addendum, see
IEEE TEC, with addendum, see
https://ieeexplore.ieee.org/abstract/document/4634579
https://hal.inria.fr/inria-00276216/file/TEC2008.pdf

Expand Down Expand Up @@ -411,7 +411,7 @@ def __call__(self, x, archive, gp):
gamma = list(self.gamma) # fails if self.gamma is a scalar
for i in sorted(gp.fixed_values): # fails if fixed_values is None
gamma.insert(i, 0.0)
gamma = np.array(gamma, copy=False)
gamma = np.asarray(gamma)
except TypeError:
gamma = self.gamma
pen = []
Expand Down Expand Up @@ -737,7 +737,7 @@ def init(self, dimension, force=False):
if force or self.chi_exponent_threshold is None: # threshold for number of consecutive changes
# self.chi_exponent_threshold = 5 + self.dimension**0.5 # previous value, probably too small in >- 20-D
# self.chi_exponent_threshold = 5 + self.dimension**0.75 # sweeps in aug-lag4-mu-update.ipynv
self.chi_exponent_threshold = 2 + self.dimension
self.chi_exponent_threshold = 2 + self.dimension
if force or self.chi_exponent_factor is None: # factor used in shape_exponent
# self.chi_exponent_factor = 3 / dimension**0.5 # previous value
# self.chi_exponent_factor = 1 * dimension**0.25 / self.chi_exponent_threshold # steepness of ramp up
Expand Down Expand Up @@ -835,7 +835,7 @@ class AugmentedLagrangian(object):
More testing based on the simpler `ConstrainedFitnessAL` interface:

>>> import cma
>>> for algorithm, evals in zip((0, 1, 2, 3), (2000, 2200, 1500, 1800)):
>>> for algorithm, evals in zip((0, 1, 2, 3), (2000, 2200, 1500, 1800)):
... alf = cma.ConstrainedFitnessAL(cma.ff.sphere, lambda x: [x[0] + 1], 3,
... find_feasible_first=True)
... _ = alf.al.set_algorithm(algorithm)
Expand Down Expand Up @@ -1302,7 +1302,7 @@ class ConstrainedFitnessAL:
(g_i > 0) x g_i^2`` and omits ``f + sum_i lam_i g_i`` altogether.
Whenever a feasible solution is found, the `finding_feasible` flag is
reset to `False`.

`find_feasible(es)` sets ``finding_feasible = True`` and uses
`es.optimize` to optimize `self.__call__`. This works well with
`CMAEvolutionStrategy` but may easily fail with solvers that do not
Expand Down Expand Up @@ -1576,4 +1576,3 @@ def log_in_es(self, es, f, g):
]
except:
pass

64 changes: 32 additions & 32 deletions cma/evolution_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
# TODO: keep best ten solutions
# TODO: implement constraints handling
# TODO: eigh(): thorough testing would not hurt
# TODO: (partly done) apply style guide
# WON'T FIX ANYTIME SOON (done within fmin): implement bipop in a separate
# algorithm as meta portfolio algorithm of IPOP and a local restart
# TODO: (partly done) apply style guide
# WON'T FIX ANYTIME SOON (done within fmin): implement bipop in a separate
# algorithm as meta portfolio algorithm of IPOP and a local restart
# option to be implemented
# in fmin (e.g. option restart_mode in [IPOP, local])
# DONE: extend function unitdoctest, or use unittest?
Expand Down Expand Up @@ -264,7 +264,7 @@ def __init__(self):
self.cc_multiplier = 1.0 ## [~0.01, ~20] l
self.cs_multiplier = 1.0 ## [~0.01, ~10] l # learning rate for cs
self.CSA_dampfac = 1.0 ## [~0.01, ~10]
self.CMA_dampsvec_fac = None ## [~0.01, ~100] # def=np.Inf or 0.5, not clear whether this is a log parameter
self.CMA_dampsvec_fac = None ## [~0.01, ~100] # def=np.inf or 0.5, not clear whether this is a log parameter
self.CMA_dampsvec_fade = 0.1 ## [0, ~2]

# exponents for learning rates
Expand Down Expand Up @@ -438,7 +438,7 @@ def cma_default_options_( # to get keyword completion back
CMA_rankmu='1.0 # multiplier for rank-mu update learning rate of covariance matrix',
CMA_rankone='1.0 # multiplier for rank-one update learning rate of covariance matrix',
CMA_recombination_weights='None # a list, see class RecombinationWeights, overwrites CMA_mu and popsize options',
CMA_dampsvec_fac='np.Inf # tentative and subject to changes, 0.5 would be a "default" damping for sigma vector update',
CMA_dampsvec_fac='np.inf # tentative and subject to changes, 0.5 would be a "default" damping for sigma vector update',
CMA_dampsvec_fade='0.1 # tentative fading out parameter for sigma vector update',
CMA_teststds='None # factors for non-isotropic initial distr. of C, mainly for test purpose, see CMA_stds for production',
CMA_stds='None # multipliers for sigma0 in each coordinate (not represented in C), or use `cma.ScaleCoordinates` instead',
Expand All @@ -461,7 +461,7 @@ def cma_default_options_( # to get keyword completion back
fixed_variables='None # dictionary with index-value pairs like {0:1.1, 2:0.1} that are not optimized',
ftarget='-inf #v target function value, minimization',
integer_variables='[] # index list, invokes basic integer handling: prevent std dev to become too small in the given variables',
is_feasible='is_feasible #v a function that computes feasibility, by default lambda x, f: f not in (None, np.NaN)',
is_feasible='is_feasible #v a function that computes feasibility, by default lambda x, f: f not in (None, np.nan)',
maxfevals='inf #v maximum number of function evaluations',
maxiter='100 + 150 * (N+3)**2 // popsize**0.5 #v maximum number of iterations',
mean_shift_line_samples='False #v sample two new solutions colinear to previous mean shift',
Expand Down Expand Up @@ -543,7 +543,7 @@ def safe_str(s):
return purecma.safe_str(s.split('#')[0],
dict([k, k] for k in
['True', 'False', 'None',
'N', 'dim', 'popsize', 'int', 'np.Inf', 'inf',
'N', 'dim', 'popsize', 'int', 'np.inf', 'inf',
'np.log', 'np.random.randn', 'time',
# 'cma_signals.in', 'outcmaes/',
'BoundTransform', 'is_feasible', 'np.linalg.eigh',
Expand Down Expand Up @@ -629,7 +629,7 @@ def versatile_options():
to date.

The string ' #v ' in the default value indicates a versatile
option that can be changed any time, however a string will not
option that can be changed any time, however a string will not
necessarily be evaluated again.

"""
Expand Down Expand Up @@ -1073,7 +1073,7 @@ def _generate(self):
self.countevals,
self.countiter,
self.gp.pheno(self.mean, into_bounds=self.boundary_handler.repair),
self.stds)) #
self.stds)) #

class CMAEvolutionStrategy(interfaces.OOOptimizer):
"""CMA-ES stochastic optimizer class with ask-and-tell interface.
Expand Down Expand Up @@ -1213,9 +1213,9 @@ class (see also there). An object instance is generated from::
... fit, X = [], []
... while len(X) < es.popsize:
... curr_fit = None
... while curr_fit in (None, np.NaN):
... while curr_fit in (None, np.nan):
... x = es.ask(1)[0]
... curr_fit = cma.ff.somenan(x, cma.ff.elli) # might return np.NaN
... curr_fit = cma.ff.somenan(x, cma.ff.elli) # might return np.nan
... X.append(x)
... fit.append(curr_fit)
... es.tell(X, fit)
Expand Down Expand Up @@ -1567,7 +1567,7 @@ def identity(x):
# self.mean = self.gp.geno(array(self.x0, copy=True), copy_if_changed=False)
self.N = len(self.mean)
assert N == self.N
# self.fmean = np.NaN # TODO name should change? prints nan in output files (OK with matlab&octave)
# self.fmean = np.nan # TODO name should change? prints nan in output files (OK with matlab&octave)
# self.fmean_noise_free = 0. # for output only

self.sp = _CMAParameters(N, opts, verbose=opts['verbose'] > 0)
Expand Down Expand Up @@ -1926,12 +1926,12 @@ def get_i(bnds, i):

def _copy_light(self, sigma=None, inopts=None):
"""tentative copy of self, versatile (interface and functionalities may change).

`sigma` overwrites the original initial `sigma`.
`inopts` allows to overwrite any of the original options.

This copy may not work as expected depending on the used sampler.

Copy mean and sample distribution parameters and input options. Do
not copy evolution paths, termination status or other state variables.

Expand Down Expand Up @@ -1961,8 +1961,8 @@ def _copy_light(self, sigma=None, inopts=None):
except: warnings.warn("self.sm.C.copy failed")
es.sm.update_now(-1) # make B and D consistent with C
es._updateBDfromSM()
return es
return es

# ____________________________________________________________
# ____________________________________________________________
def ask(self, number=None, xmean=None, sigma_fac=1,
Expand Down Expand Up @@ -2446,7 +2446,7 @@ def ask_and_eval(self, func, args=(), gradf=None, number=None, xmean=None, sigma
-------
While ``not self.is_feasible(x, func(x))`` new solutions are
sampled. By default
``self.is_feasible == cma.feasible == lambda x, f: f not in (None, np.NaN)``.
``self.is_feasible == cma.feasible == lambda x, f: f not in (None, np.nan)``.
The argument to `func` can be freely modified within `func`.

Depending on the ``CMA_mirrors`` option, some solutions are not
Expand Down Expand Up @@ -2493,7 +2493,7 @@ def ask_and_eval(self, func, args=(), gradf=None, number=None, xmean=None, sigma
is_feasible = self.opts['is_feasible']

# do the work
fit = [] # or np.NaN * np.empty(number)
fit = [] # or np.nan * np.empty(number)
X_first = self.ask(popsize, xmean=xmean, gradf=gradf, args=args)
if xmean is None:
xmean = self.mean # might have changed in self.ask
Expand Down Expand Up @@ -2595,7 +2595,7 @@ def _prepare_injection_directions(self):
if self.mean_shift_samples:
ary = [self.mean - self.mean_old]
ary.append(self.mean_old - self.mean) # another copy!
if np.alltrue(ary[-1] == 0.0):
if np.all(ary[-1] == 0.0):
utils.print_warning('zero mean shift encountered',
'_prepare_injection_directions',
'CMAEvolutionStrategy', self.countiter)
Expand Down Expand Up @@ -2878,7 +2878,7 @@ def tell(self, solutions, function_values, check_points=None,
x_elit = self.mean0.copy()
# self.clip_or_fit_solutions([x_elit], [0]) # just calls repair_genotype
self.random_rescale_to_mahalanobis(x_elit)
pop = array([x_elit] + list(pop), copy=False)
pop = np.asarray([x_elit] + list(pop))
utils.print_message('initial solution injected %f<%f' %
(self.f0, fit.fit[0]),
'tell', 'CMAEvolutionStrategy',
Expand Down Expand Up @@ -3144,8 +3144,8 @@ def tell(self, solutions, function_values, check_points=None,
copy_if_changed=False), copy=False)
if _new_injections:
self.pop_injection_directions = self._prepare_injection_directions()
if (self.opts['verbose'] > 4 and self.countiter < 3 and
not isinstance(self.adapt_sigma, CMAAdaptSigmaTPA) and
if (self.opts['verbose'] > 4 and self.countiter < 3 and
not isinstance(self.adapt_sigma, CMAAdaptSigmaTPA) and
len(self.pop_injection_directions)):
utils.print_message(' %d directions prepared for injection %s' %
(len(self.pop_injection_directions),
Expand Down Expand Up @@ -3221,7 +3221,7 @@ def inject(self, solutions, force=None):
if len(solution) != self.N:
raise ValueError('method `inject` needs a list or array'
+ (' each el with dimension (`len`) %d' % self.N))
solution = array(solution, copy=False, dtype=float)
solution = np.asarray(solution, dtype=float)
if force:
self.pop_injection_solutions.append(solution)
else:
Expand Down Expand Up @@ -3342,8 +3342,8 @@ def repair_genotype(self, x, copy_if_changed=False):
``N**0.5 + 2 * N / (N + 2)``, but the specific repair
mechanism may change in future.
"""
x = array(x, copy=False)
mold = array(self.mean, copy=False)
x = np.asarray(x)
mold = np.asarray(self.mean)
if 1 < 3: # hard clip at upper_length
upper_length = self.N**0.5 + 2 * self.N / (self.N + 2)
# should become an Option, but how? e.g. [0, 2, 2]
Expand Down Expand Up @@ -3795,7 +3795,7 @@ def __init__(self, x=None):
def set_params(self, param_values, names=(
'delta', 'time_delta_offset', 'time_delta_frac', 'time_delta_expo')):
"""`param_values` is a `list` conforming to ``CMAOptions['tolxstagnation']``.

Do nothing if ``param_values in (None, True)``, set ``delta = -1``
if ``param_values is False``.

Expand Down Expand Up @@ -3830,7 +3830,7 @@ def update(self, x):
return self
@property
def time_threshold(self):
return (self.time_delta_offset +
return (self.time_delta_offset +
self.time_delta_frac * self.count**self.time_delta_expo)
@property
def stop(self):
Expand Down Expand Up @@ -4014,8 +4014,8 @@ def _update(self, es):
es.timer.elapsed > opts['timeout'],
es.timer.elapsed if self._get_value else None)
except AttributeError:
if es.countiter <= 0:
pass
if es.countiter <= 0:
pass
# else: raise

if 11 < 3 and 2 * l < len(es.fit.histbest): # TODO: this might go wrong, because the nb of written columns changes
Expand Down Expand Up @@ -4113,12 +4113,12 @@ class _CMAParameters(object):
{'CMA_on': True,
'N': 20,
'c1': 0.00437235...,
'c1_sep': 0.0343279...,
'c1_sep': ...0.0343279...,
'cc': 0.171767...,
'cc_sep': 0.252594...,
'cmean': array(1...,
'cmu': 0.00921656...,
'cmu_sep': 0.0565385...,
'cmu_sep': ...0.0565385...,
'lam_mirr': 0,
'mu': 6,
'popsize': 12,
Expand Down Expand Up @@ -5268,7 +5268,7 @@ def update(es):
def f_post(x):
return sum(gi ** 2 for gi in g(x) if gi > 0) + sum(
hi ** 2 for hi in h(x) if hi ** 2 > post_optimization ** 2)

kwargs_post = kwargs.copy()
kwargs_post.setdefault('options', {})['ftarget'] = 0

Expand Down
22 changes: 11 additions & 11 deletions cma/fitness_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ def rot(self, x, fun, rot=1, args=()):
else:
return fun(x)
def somenan(self, x, fun, p=0.1):
"""returns sometimes np.NaN, otherwise fun(x)"""
"""returns sometimes np.nan, otherwise fun(x)"""
if np.random.rand(1) < p:
return np.NaN
return np.nan
else:
return fun(x)

Expand Down Expand Up @@ -130,9 +130,9 @@ def subspace_sphere(self, x, visible_ratio=1/2):
def pnorm(self, x, p=0.5):
return sum(np.abs(x)**p)**(1./p)
def grad_sphere(self, x, *args):
return 2*array(x, copy=False)
return 2*np.asarray(x)
def grad_to_one(self, x, *args):
return array(x, copy=False) - 1
return np.asarray(x) - 1
def sphere_pos(self, x):
"""Sphere (squared norm) test objective function"""
# return np.random.rand(1)[0]**0 * sum(x**2) + 1 * np.random.rand(1)[0]
Expand Down Expand Up @@ -179,17 +179,17 @@ def cornersphere(self, x):
"""Sphere (squared norm) test objective function constraint to the corner"""
nconstr = len(x) - 0
if any(x[:nconstr] < 1):
return np.NaN
return np.nan
return sum(x**2) - nconstr
def cornerelli(self, x):
""" """
if any(x < 1):
return np.NaN
return np.nan
return self.elli(x) - self.elli(np.ones(len(x)))
def cornerellirot(self, x):
""" """
if any(x < 1):
return np.NaN
return np.nan
return self.ellirot(x)
def normalSkew(self, f):
N = np.random.randn(1)[0]**2
Expand Down Expand Up @@ -309,7 +309,7 @@ def ellihalfrot(self, x, frac=0.5, cond1=1e6, cond2=1e6):
def grad_elli(self, x, *args):
cond = 1e6
N = len(x)
return 2 * cond**(np.arange(N) / (N - 1.)) * array(x, copy=False)
return 2 * cond**(np.arange(N) / (N - 1.)) * np.asarray(x)
def fun_as_arg(self, x, *args):
"""``fun_as_arg(x, fun, *more_args)`` calls ``fun(x, *more_args)``.

Expand Down Expand Up @@ -471,7 +471,7 @@ def optprob(self, x):
def lincon(self, x, theta=0.01):
"""ridge like linear function with one linear constraint"""
if x[0] < 0:
return np.NaN
return np.nan
return theta * x[1] + x[0]
def rosen_nesterov(self, x, rho=100):
"""needs exponential number of steps in a non-increasing
Expand Down Expand Up @@ -536,7 +536,7 @@ def xinsheyang2(self, x, termination_friendly=True):
to the global optimum >= 1. That is, the global optimum is
surrounded by 3^n - 1 local optima that have the better values the
further they are away from the global optimum.

Conclusion: it is a rather suspicious sign if an algorithm finds the global
optimum of this function in larger dimension.

Expand Down Expand Up @@ -565,7 +565,7 @@ def binval(x, foffset=1e-2 - 1e-5):
"""return sum_i(0 if (1 <= x[i] < 2) else 2**i)**(1/n)"""
s = sum([0 if 1 <= val < 2 else 2**i for i, val in enumerate(x)])
return s**(1/len(x)) + (s == 0) * foffset

def _fetch_bbob_fcts(self):
"""Fetch GECCO BBOB 2009 functions from WWW and set as `self.BBOB`.

Expand Down
Loading