scijit.optimize.differential_evolution¶
- scijit.optimize.differential_evolution(func, bounds, args=(), strategy='best1bin', maxiter=1000, popsize=15, tol=0.01, mutation=(0.5, 1.0), recombination=0.7, rng=None, callback=None, disp=False, polish=True, init='latinhypercube', atol=0.0, updating='immediate', workers=1, constraints=(), x0=None, integrality=None, vectorized=False, seed=None)¶
Differential evolution, a population-based stochastic optimizer.
Callback style A –
funcis a plain@njitfunc(x, *args) -> float64.- Parameters:
- funccallable
A plain
@njitf(x, *args) -> float.- bounds(n, 2) array_like
One
(min, max)pair per variable. Must be finite.- argstuple, optional
Extra parameters, unpacked into the argument list of func after x, so
args=(a, b)callsfunc(x, a, b). Its elements may be of different types and shapes. Default(), which callsfunc(x);Nonemeans the same. An ndarray or a list arrives as ONE argument instead,func(x, args). See Notes.- strategystr, optional
Name of the mutation strategy.
'best1bin'is the default. Full list in the module docstring. A callable strategy is not accepted.- maxiterint or None, optional
Maximum number of generations. Default 1000.
Noneresolves to 1000.- popsizeint, optional
Population-size multiplier. Default 15. Must be
>= 1.- tolfloat, optional
Relative convergence tolerance on the population energies. Default 0.01. Must be
>= 0.- mutationfloat or 2-tuple, optional
A constant mutation factor, or a
(lo, hi)dithering range. Default(0.5, 1). Every entry must lie in[0, 2).- recombinationfloat, optional
Crossover probability. Default 0.7. Must lie in
[0, 1].- rngint or numpy.random.Generator, optional
Source of every draw the population and the generation loop make.
None(default) uses the internal xorshift64* generator; an integer builds anumpy.random.default_rngfor the call; aGeneratoris drawn from directly and its state advances. See Notes.- callbackcallable, optional
Called once per generation, after the convergence quantity is known and before the test that would end the run. A plain Python
callback(xk)orcallback(intermediate_result), or an@njitcallback(xk). Returning a truthy value or raisingStopIterationhalts the run; the result then reportssuccess=Falseandmessage='callback function requested stop early', and the polish still runs.- dispbool, optional
Print the per-iteration line, and the line before the polish. Default
False.- polishbool, optional
Polish the best member with L-BFGS-B over the same box.
Trueby default. The polished point replaces the best member only when it improves the objective, the polish converged, and it lies inside the bounds. A callable polish is not accepted; see Notes.- initstr, optional
'latinhypercube'(default),'random','sobol'or'halton'. An ndarray of starting points is not accepted.- atolfloat, optional
Absolute convergence tolerance on the population energies. Default 0.0. Must be
>= 0.- updating, workers, constraints, x0, integrality, vectorizedoptional
Accepted only at their defaults. See Notes.
- seedint, optional
BEYOND SCIPY. Seeds the internal xorshift64* generator, which is the rng at
Nonepath.None(default) draws fresh entropy per call, so two identical calls give different answers. See Notes.
- Returns:
- resDEResult
A namedtuple carrying
x,fun,nit,nfev,population,population_energies,success,messageandjac.nfevis a count of the evaluations of func this routine makes, the polish’s included.jacis the L-BFGS-B gradient at the polished point, and zeros where no polish was accepted.
- Raises:
- ValueError
Empty bounds, a non-finite bound, bounds that is not
(n, 2),popsize < 1,maxiter < 0, a negative tol or atol, a recombination outside[0, 1], a mutation entry outside[0, 2), an unknown strategy or init, a rng that is notNone, an integer or anumpy.random.Generator, or any of callback, x0, integrality, constraints, vectorized, workers and updating away from its default.
See also
scipy.optimize.differential_evolutionThe scipy routine this mirrors.
scijit.optimize.bruteExhaustive grid, deterministic.
scijit.optimize.basinhoppingRandom restarts around a local minimizer.
Notes
UNAVAILABLE: an
OptimizeResult. res is a namedtuple, so its field set is fixed.jacis always present where scipy adds the key only after a polish it accepted.constr,constr_violation,maxcvandconstr_penalty, which scipy carries under constraints, are absent.NOT IMPLEMENTED: a callable strategy, a callable polish, and an ndarray in init.
init='sobol'andinit='halton'draw the initial population from the same quasi-random sequences scipy uses: unscrambled, the points from scijit.stats.qmc andscipy.stats.qmcagree to the bit. scipy scrambles them with anumpy.random.Generator, which compiled code cannot hold, so the scramble seed comes off this solver’s own stream and the population, the search path and the evaluation count are not scipy’s at the same seed.args is unpacked into the objective’s argument list,
func(x, *args).DELIBERATELY DIFFERENT: an ndarray, a list or a bare scalar args reaches func as ONE argument,
func(x, args). scipy unpacks the first two element by element and refuses the third. The arity of a compiled call is fixed when it compiles, so a sequence whose length is known only at run time cannot be unpacked:func(x, *args)on an ndarray inside@njitis aTypingError. An objective written to the unpacked shape refuses the ndarray rather than reading it wrongly.NOT IMPLEMENTED: x0, integrality, constraints, vectorized, workers and
updating='deferred'. Each raisesValueErroraway from its default rather than being ignored.DELIBERATELY DIFFERENT: scipy’s legacy
convergencevalue passed tocallback(xk, convergence=val)is not carried here. Its value istol / (std(energies) / |mean(energies)|), which this entry point does not compute.DELIBERATELY DIFFERENT: an
intermediate_resulthere carriesxandfun. scipy’s also carriesconvergence,message,nfev,nit,population,population_energiesandsuccess.An
@njitcallback halts by raising; its return value is not read. A plain Python callback also halts on a truthy return.With rng at
Nonethe draws come from an internal xorshift64* generator carried in a per-call state array. seed selects its stream andseed=Nonedraws a fresh one per call.DELIBERATELY DIFFERENT: an integer rng builds a
numpy.random.default_rng, which is PCG64, where scipy’scheck_random_statebuilds the legacy MT19937RandomStatethat compiled code cannot hold. Under any rng the population initialisation, the dither and the crossover consume draws in scipy’s order and shapes, and the trial index is taken from oneuniform()where scipy callschoice, which numba does not implement. So the same integer explores a different population.A rng at
Noneis safe to call from anumba.prangeloop, because the generator state is per-call. OneGeneratorSHARED across aprangeis not: 70,499 of 100,000 draws were distinct across 32 threads. An integer rng builds its generator inside the call, so it is per-call as well.https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.differential_evolution.html
Examples
>>> import numpy as np >>> from numba import njit >>> from scijit.optimize import differential_evolution >>> @njit ... def q(x, target): ... return (x[0] - target[0]) ** 2 + (x[1] - target[1]) ** 2 >>> bounds = np.array([[-3.0, 3.0], [-3.0, 3.0]]) >>> @njit ... def run(): ... return differential_evolution(q, bounds, ... (np.array([1.5, -0.5]),), ... 'best1bin', 200) >>> res = run() >>> np.round(res.x, 4) array([ 1.5, -0.5]) >>> res.success True