_differentialevolution.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. """
  2. differential_evolution: The differential evolution global optimization algorithm
  3. Added by Andrew Nelson 2014
  4. """
  5. from __future__ import division, print_function, absolute_import
  6. import warnings
  7. import numpy as np
  8. from scipy.optimize import OptimizeResult, minimize
  9. from scipy.optimize.optimize import _status_message
  10. from scipy._lib._util import check_random_state, MapWrapper
  11. from scipy._lib.six import xrange, string_types
  12. __all__ = ['differential_evolution']
  13. _MACHEPS = np.finfo(np.float64).eps
  14. def differential_evolution(func, bounds, args=(), strategy='best1bin',
  15. maxiter=1000, popsize=15, tol=0.01,
  16. mutation=(0.5, 1), recombination=0.7, seed=None,
  17. callback=None, disp=False, polish=True,
  18. init='latinhypercube', atol=0, updating='immediate',
  19. workers=1):
  20. """Finds the global minimum of a multivariate function.
  21. Differential Evolution is stochastic in nature (does not use gradient
  22. methods) to find the minimium, and can search large areas of candidate
  23. space, but often requires larger numbers of function evaluations than
  24. conventional gradient based techniques.
  25. The algorithm is due to Storn and Price [1]_.
  26. Parameters
  27. ----------
  28. func : callable
  29. The objective function to be minimized. Must be in the form
  30. ``f(x, *args)``, where ``x`` is the argument in the form of a 1-D array
  31. and ``args`` is a tuple of any additional fixed parameters needed to
  32. completely specify the function.
  33. bounds : sequence
  34. Bounds for variables. ``(min, max)`` pairs for each element in ``x``,
  35. defining the lower and upper bounds for the optimizing argument of
  36. `func`. It is required to have ``len(bounds) == len(x)``.
  37. ``len(bounds)`` is used to determine the number of parameters in ``x``.
  38. args : tuple, optional
  39. Any additional fixed parameters needed to
  40. completely specify the objective function.
  41. strategy : str, optional
  42. The differential evolution strategy to use. Should be one of:
  43. - 'best1bin'
  44. - 'best1exp'
  45. - 'rand1exp'
  46. - 'randtobest1exp'
  47. - 'currenttobest1exp'
  48. - 'best2exp'
  49. - 'rand2exp'
  50. - 'randtobest1bin'
  51. - 'currenttobest1bin'
  52. - 'best2bin'
  53. - 'rand2bin'
  54. - 'rand1bin'
  55. The default is 'best1bin'.
  56. maxiter : int, optional
  57. The maximum number of generations over which the entire population is
  58. evolved. The maximum number of function evaluations (with no polishing)
  59. is: ``(maxiter + 1) * popsize * len(x)``
  60. popsize : int, optional
  61. A multiplier for setting the total population size. The population has
  62. ``popsize * len(x)`` individuals (unless the initial population is
  63. supplied via the `init` keyword).
  64. tol : float, optional
  65. Relative tolerance for convergence, the solving stops when
  66. ``np.std(pop) <= atol + tol * np.abs(np.mean(population_energies))``,
  67. where and `atol` and `tol` are the absolute and relative tolerance
  68. respectively.
  69. mutation : float or tuple(float, float), optional
  70. The mutation constant. In the literature this is also known as
  71. differential weight, being denoted by F.
  72. If specified as a float it should be in the range [0, 2].
  73. If specified as a tuple ``(min, max)`` dithering is employed. Dithering
  74. randomly changes the mutation constant on a generation by generation
  75. basis. The mutation constant for that generation is taken from
  76. ``U[min, max)``. Dithering can help speed convergence significantly.
  77. Increasing the mutation constant increases the search radius, but will
  78. slow down convergence.
  79. recombination : float, optional
  80. The recombination constant, should be in the range [0, 1]. In the
  81. literature this is also known as the crossover probability, being
  82. denoted by CR. Increasing this value allows a larger number of mutants
  83. to progress into the next generation, but at the risk of population
  84. stability.
  85. seed : int or `np.random.RandomState`, optional
  86. If `seed` is not specified the `np.RandomState` singleton is used.
  87. If `seed` is an int, a new `np.random.RandomState` instance is used,
  88. seeded with seed.
  89. If `seed` is already a `np.random.RandomState instance`, then that
  90. `np.random.RandomState` instance is used.
  91. Specify `seed` for repeatable minimizations.
  92. disp : bool, optional
  93. Display status messages
  94. callback : callable, `callback(xk, convergence=val)`, optional
  95. A function to follow the progress of the minimization. ``xk`` is
  96. the current value of ``x0``. ``val`` represents the fractional
  97. value of the population convergence. When ``val`` is greater than one
  98. the function halts. If callback returns `True`, then the minimization
  99. is halted (any polishing is still carried out).
  100. polish : bool, optional
  101. If True (default), then `scipy.optimize.minimize` with the `L-BFGS-B`
  102. method is used to polish the best population member at the end, which
  103. can improve the minimization slightly.
  104. init : str or array-like, optional
  105. Specify which type of population initialization is performed. Should be
  106. one of:
  107. - 'latinhypercube'
  108. - 'random'
  109. - array specifying the initial population. The array should have
  110. shape ``(M, len(x))``, where len(x) is the number of parameters.
  111. `init` is clipped to `bounds` before use.
  112. The default is 'latinhypercube'. Latin Hypercube sampling tries to
  113. maximize coverage of the available parameter space. 'random'
  114. initializes the population randomly - this has the drawback that
  115. clustering can occur, preventing the whole of parameter space being
  116. covered. Use of an array to specify a population subset could be used,
  117. for example, to create a tight bunch of initial guesses in an location
  118. where the solution is known to exist, thereby reducing time for
  119. convergence.
  120. atol : float, optional
  121. Absolute tolerance for convergence, the solving stops when
  122. ``np.std(pop) <= atol + tol * np.abs(np.mean(population_energies))``,
  123. where and `atol` and `tol` are the absolute and relative tolerance
  124. respectively.
  125. updating : {'immediate', 'deferred'}, optional
  126. If ``'immediate'``, the best solution vector is continuously updated
  127. within a single generation [4]_. This can lead to faster convergence as
  128. trial vectors can take advantage of continuous improvements in the best
  129. solution.
  130. With ``'deferred'``, the best solution vector is updated once per
  131. generation. Only ``'deferred'`` is compatible with parallelization, and
  132. the `workers` keyword can over-ride this option.
  133. .. versionadded:: 1.2.0
  134. workers : int or map-like callable, optional
  135. If `workers` is an int the population is subdivided into `workers`
  136. sections and evaluated in parallel (uses `multiprocessing.Pool`).
  137. Supply -1 to use all available CPU cores.
  138. Alternatively supply a map-like callable, such as
  139. `multiprocessing.Pool.map` for evaluating the population in parallel.
  140. This evaluation is carried out as ``workers(func, iterable)``.
  141. This option will override the `updating` keyword to
  142. ``updating='deferred'`` if ``workers != 1``.
  143. Requires that `func` be pickleable.
  144. .. versionadded:: 1.2.0
  145. Returns
  146. -------
  147. res : OptimizeResult
  148. The optimization result represented as a `OptimizeResult` object.
  149. Important attributes are: ``x`` the solution array, ``success`` a
  150. Boolean flag indicating if the optimizer exited successfully and
  151. ``message`` which describes the cause of the termination. See
  152. `OptimizeResult` for a description of other attributes. If `polish`
  153. was employed, and a lower minimum was obtained by the polishing, then
  154. OptimizeResult also contains the ``jac`` attribute.
  155. Notes
  156. -----
  157. Differential evolution is a stochastic population based method that is
  158. useful for global optimization problems. At each pass through the population
  159. the algorithm mutates each candidate solution by mixing with other candidate
  160. solutions to create a trial candidate. There are several strategies [2]_ for
  161. creating trial candidates, which suit some problems more than others. The
  162. 'best1bin' strategy is a good starting point for many systems. In this
  163. strategy two members of the population are randomly chosen. Their difference
  164. is used to mutate the best member (the `best` in `best1bin`), :math:`b_0`,
  165. so far:
  166. .. math::
  167. b' = b_0 + mutation * (population[rand0] - population[rand1])
  168. A trial vector is then constructed. Starting with a randomly chosen 'i'th
  169. parameter the trial is sequentially filled (in modulo) with parameters from
  170. ``b'`` or the original candidate. The choice of whether to use ``b'`` or the
  171. original candidate is made with a binomial distribution (the 'bin' in
  172. 'best1bin') - a random number in [0, 1) is generated. If this number is
  173. less than the `recombination` constant then the parameter is loaded from
  174. ``b'``, otherwise it is loaded from the original candidate. The final
  175. parameter is always loaded from ``b'``. Once the trial candidate is built
  176. its fitness is assessed. If the trial is better than the original candidate
  177. then it takes its place. If it is also better than the best overall
  178. candidate it also replaces that.
  179. To improve your chances of finding a global minimum use higher `popsize`
  180. values, with higher `mutation` and (dithering), but lower `recombination`
  181. values. This has the effect of widening the search radius, but slowing
  182. convergence.
  183. By default the best solution vector is updated continuously within a single
  184. iteration (``updating='immediate'``). This is a modification [4]_ of the
  185. original differential evolution algorithm which can lead to faster
  186. convergence as trial vectors can immediately benefit from improved
  187. solutions. To use the original Storn and Price behaviour, updating the best
  188. solution once per iteration, set ``updating='deferred'``.
  189. .. versionadded:: 0.15.0
  190. Examples
  191. --------
  192. Let us consider the problem of minimizing the Rosenbrock function. This
  193. function is implemented in `rosen` in `scipy.optimize`.
  194. >>> from scipy.optimize import rosen, differential_evolution
  195. >>> bounds = [(0,2), (0, 2), (0, 2), (0, 2), (0, 2)]
  196. >>> result = differential_evolution(rosen, bounds)
  197. >>> result.x, result.fun
  198. (array([1., 1., 1., 1., 1.]), 1.9216496320061384e-19)
  199. Now repeat, but with parallelization.
  200. >>> bounds = [(0,2), (0, 2), (0, 2), (0, 2), (0, 2)]
  201. >>> result = differential_evolution(rosen, bounds, updating='deferred',
  202. ... workers=2)
  203. >>> result.x, result.fun
  204. (array([1., 1., 1., 1., 1.]), 1.9216496320061384e-19)
  205. Next find the minimum of the Ackley function
  206. (https://en.wikipedia.org/wiki/Test_functions_for_optimization).
  207. >>> from scipy.optimize import differential_evolution
  208. >>> import numpy as np
  209. >>> def ackley(x):
  210. ... arg1 = -0.2 * np.sqrt(0.5 * (x[0] ** 2 + x[1] ** 2))
  211. ... arg2 = 0.5 * (np.cos(2. * np.pi * x[0]) + np.cos(2. * np.pi * x[1]))
  212. ... return -20. * np.exp(arg1) - np.exp(arg2) + 20. + np.e
  213. >>> bounds = [(-5, 5), (-5, 5)]
  214. >>> result = differential_evolution(ackley, bounds)
  215. >>> result.x, result.fun
  216. (array([ 0., 0.]), 4.4408920985006262e-16)
  217. References
  218. ----------
  219. .. [1] Storn, R and Price, K, Differential Evolution - a Simple and
  220. Efficient Heuristic for Global Optimization over Continuous Spaces,
  221. Journal of Global Optimization, 1997, 11, 341 - 359.
  222. .. [2] http://www1.icsi.berkeley.edu/~storn/code.html
  223. .. [3] http://en.wikipedia.org/wiki/Differential_evolution
  224. .. [4] Wormington, M., Panaccione, C., Matney, K. M., Bowen, D. K., -
  225. Characterization of structures from X-ray scattering data using
  226. genetic algorithms, Phil. Trans. R. Soc. Lond. A, 1999, 357,
  227. 2827-2848
  228. """
  229. # using a context manager means that any created Pool objects are
  230. # cleared up.
  231. with DifferentialEvolutionSolver(func, bounds, args=args,
  232. strategy=strategy,
  233. maxiter=maxiter,
  234. popsize=popsize, tol=tol,
  235. mutation=mutation,
  236. recombination=recombination,
  237. seed=seed, polish=polish,
  238. callback=callback,
  239. disp=disp, init=init, atol=atol,
  240. updating=updating,
  241. workers=workers) as solver:
  242. ret = solver.solve()
  243. return ret
  244. class DifferentialEvolutionSolver(object):
  245. """This class implements the differential evolution solver
  246. Parameters
  247. ----------
  248. func : callable
  249. The objective function to be minimized. Must be in the form
  250. ``f(x, *args)``, where ``x`` is the argument in the form of a 1-D array
  251. and ``args`` is a tuple of any additional fixed parameters needed to
  252. completely specify the function.
  253. bounds : sequence
  254. Bounds for variables. ``(min, max)`` pairs for each element in ``x``,
  255. defining the lower and upper bounds for the optimizing argument of
  256. `func`. It is required to have ``len(bounds) == len(x)``.
  257. ``len(bounds)`` is used to determine the number of parameters in ``x``.
  258. args : tuple, optional
  259. Any additional fixed parameters needed to
  260. completely specify the objective function.
  261. strategy : str, optional
  262. The differential evolution strategy to use. Should be one of:
  263. - 'best1bin'
  264. - 'best1exp'
  265. - 'rand1exp'
  266. - 'randtobest1exp'
  267. - 'currenttobest1exp'
  268. - 'best2exp'
  269. - 'rand2exp'
  270. - 'randtobest1bin'
  271. - 'currenttobest1bin'
  272. - 'best2bin'
  273. - 'rand2bin'
  274. - 'rand1bin'
  275. The default is 'best1bin'
  276. maxiter : int, optional
  277. The maximum number of generations over which the entire population is
  278. evolved. The maximum number of function evaluations (with no polishing)
  279. is: ``(maxiter + 1) * popsize * len(x)``
  280. popsize : int, optional
  281. A multiplier for setting the total population size. The population has
  282. ``popsize * len(x)`` individuals (unless the initial population is
  283. supplied via the `init` keyword).
  284. tol : float, optional
  285. Relative tolerance for convergence, the solving stops when
  286. ``np.std(pop) <= atol + tol * np.abs(np.mean(population_energies))``,
  287. where and `atol` and `tol` are the absolute and relative tolerance
  288. respectively.
  289. mutation : float or tuple(float, float), optional
  290. The mutation constant. In the literature this is also known as
  291. differential weight, being denoted by F.
  292. If specified as a float it should be in the range [0, 2].
  293. If specified as a tuple ``(min, max)`` dithering is employed. Dithering
  294. randomly changes the mutation constant on a generation by generation
  295. basis. The mutation constant for that generation is taken from
  296. U[min, max). Dithering can help speed convergence significantly.
  297. Increasing the mutation constant increases the search radius, but will
  298. slow down convergence.
  299. recombination : float, optional
  300. The recombination constant, should be in the range [0, 1]. In the
  301. literature this is also known as the crossover probability, being
  302. denoted by CR. Increasing this value allows a larger number of mutants
  303. to progress into the next generation, but at the risk of population
  304. stability.
  305. seed : int or `np.random.RandomState`, optional
  306. If `seed` is not specified the `np.random.RandomState` singleton is
  307. used.
  308. If `seed` is an int, a new `np.random.RandomState` instance is used,
  309. seeded with `seed`.
  310. If `seed` is already a `np.random.RandomState` instance, then that
  311. `np.random.RandomState` instance is used.
  312. Specify `seed` for repeatable minimizations.
  313. disp : bool, optional
  314. Display status messages
  315. callback : callable, `callback(xk, convergence=val)`, optional
  316. A function to follow the progress of the minimization. ``xk`` is
  317. the current value of ``x0``. ``val`` represents the fractional
  318. value of the population convergence. When ``val`` is greater than one
  319. the function halts. If callback returns `True`, then the minimization
  320. is halted (any polishing is still carried out).
  321. polish : bool, optional
  322. If True, then `scipy.optimize.minimize` with the `L-BFGS-B` method
  323. is used to polish the best population member at the end. This requires
  324. a few more function evaluations.
  325. maxfun : int, optional
  326. Set the maximum number of function evaluations. However, it probably
  327. makes more sense to set `maxiter` instead.
  328. init : str or array-like, optional
  329. Specify which type of population initialization is performed. Should be
  330. one of:
  331. - 'latinhypercube'
  332. - 'random'
  333. - array specifying the initial population. The array should have
  334. shape ``(M, len(x))``, where len(x) is the number of parameters.
  335. `init` is clipped to `bounds` before use.
  336. The default is 'latinhypercube'. Latin Hypercube sampling tries to
  337. maximize coverage of the available parameter space. 'random'
  338. initializes the population randomly - this has the drawback that
  339. clustering can occur, preventing the whole of parameter space being
  340. covered. Use of an array to specify a population could be used, for
  341. example, to create a tight bunch of initial guesses in an location
  342. where the solution is known to exist, thereby reducing time for
  343. convergence.
  344. atol : float, optional
  345. Absolute tolerance for convergence, the solving stops when
  346. ``np.std(pop) <= atol + tol * np.abs(np.mean(population_energies))``,
  347. where and `atol` and `tol` are the absolute and relative tolerance
  348. respectively.
  349. updating : {'immediate', 'deferred'}, optional
  350. If `immediate` the best solution vector is continuously updated within
  351. a single generation. This can lead to faster convergence as trial
  352. vectors can take advantage of continuous improvements in the best
  353. solution.
  354. With `deferred` the best solution vector is updated once per
  355. generation. Only `deferred` is compatible with parallelization, and the
  356. `workers` keyword can over-ride this option.
  357. workers : int or map-like callable, optional
  358. If `workers` is an int the population is subdivided into `workers`
  359. sections and evaluated in parallel (uses `multiprocessing.Pool`).
  360. Supply `-1` to use all cores available to the Process.
  361. Alternatively supply a map-like callable, such as
  362. `multiprocessing.Pool.map` for evaluating the population in parallel.
  363. This evaluation is carried out as ``workers(func, iterable)``.
  364. This option will override the `updating` keyword to
  365. `updating='deferred'` if `workers != 1`.
  366. Requires that `func` be pickleable.
  367. """
  368. # Dispatch of mutation strategy method (binomial or exponential).
  369. _binomial = {'best1bin': '_best1',
  370. 'randtobest1bin': '_randtobest1',
  371. 'currenttobest1bin': '_currenttobest1',
  372. 'best2bin': '_best2',
  373. 'rand2bin': '_rand2',
  374. 'rand1bin': '_rand1'}
  375. _exponential = {'best1exp': '_best1',
  376. 'rand1exp': '_rand1',
  377. 'randtobest1exp': '_randtobest1',
  378. 'currenttobest1exp': '_currenttobest1',
  379. 'best2exp': '_best2',
  380. 'rand2exp': '_rand2'}
  381. __init_error_msg = ("The population initialization method must be one of "
  382. "'latinhypercube' or 'random', or an array of shape "
  383. "(M, N) where N is the number of parameters and M>5")
  384. def __init__(self, func, bounds, args=(),
  385. strategy='best1bin', maxiter=1000, popsize=15,
  386. tol=0.01, mutation=(0.5, 1), recombination=0.7, seed=None,
  387. maxfun=np.inf, callback=None, disp=False, polish=True,
  388. init='latinhypercube', atol=0, updating='immediate',
  389. workers=1):
  390. if strategy in self._binomial:
  391. self.mutation_func = getattr(self, self._binomial[strategy])
  392. elif strategy in self._exponential:
  393. self.mutation_func = getattr(self, self._exponential[strategy])
  394. else:
  395. raise ValueError("Please select a valid mutation strategy")
  396. self.strategy = strategy
  397. self.callback = callback
  398. self.polish = polish
  399. # set the updating / parallelisation options
  400. if updating in ['immediate', 'deferred']:
  401. self._updating = updating
  402. # want to use parallelisation, but updating is immediate
  403. if workers != 1 and updating == 'immediate':
  404. warnings.warn("differential_evolution: the 'workers' keyword has"
  405. " overridden updating='immediate' to"
  406. " updating='deferred'", UserWarning)
  407. self._updating = 'deferred'
  408. # an object with a map method.
  409. self._mapwrapper = MapWrapper(workers)
  410. # relative and absolute tolerances for convergence
  411. self.tol, self.atol = tol, atol
  412. # Mutation constant should be in [0, 2). If specified as a sequence
  413. # then dithering is performed.
  414. self.scale = mutation
  415. if (not np.all(np.isfinite(mutation)) or
  416. np.any(np.array(mutation) >= 2) or
  417. np.any(np.array(mutation) < 0)):
  418. raise ValueError('The mutation constant must be a float in '
  419. 'U[0, 2), or specified as a tuple(min, max)'
  420. ' where min < max and min, max are in U[0, 2).')
  421. self.dither = None
  422. if hasattr(mutation, '__iter__') and len(mutation) > 1:
  423. self.dither = [mutation[0], mutation[1]]
  424. self.dither.sort()
  425. self.cross_over_probability = recombination
  426. # we create a wrapped function to allow the use of map (and Pool.map
  427. # in the future)
  428. self.func = _FunctionWrapper(func, args)
  429. self.args = args
  430. # convert tuple of lower and upper bounds to limits
  431. # [(low_0, high_0), ..., (low_n, high_n]
  432. # -> [[low_0, ..., low_n], [high_0, ..., high_n]]
  433. self.limits = np.array(bounds, dtype='float').T
  434. if (np.size(self.limits, 0) != 2 or not
  435. np.all(np.isfinite(self.limits))):
  436. raise ValueError('bounds should be a sequence containing '
  437. 'real valued (min, max) pairs for each value'
  438. ' in x')
  439. if maxiter is None: # the default used to be None
  440. maxiter = 1000
  441. self.maxiter = maxiter
  442. if maxfun is None: # the default used to be None
  443. maxfun = np.inf
  444. self.maxfun = maxfun
  445. # population is scaled to between [0, 1].
  446. # We have to scale between parameter <-> population
  447. # save these arguments for _scale_parameter and
  448. # _unscale_parameter. This is an optimization
  449. self.__scale_arg1 = 0.5 * (self.limits[0] + self.limits[1])
  450. self.__scale_arg2 = np.fabs(self.limits[0] - self.limits[1])
  451. self.parameter_count = np.size(self.limits, 1)
  452. self.random_number_generator = check_random_state(seed)
  453. # default population initialization is a latin hypercube design, but
  454. # there are other population initializations possible.
  455. # the minimum is 5 because 'best2bin' requires a population that's at
  456. # least 5 long
  457. self.num_population_members = max(5, popsize * self.parameter_count)
  458. self.population_shape = (self.num_population_members,
  459. self.parameter_count)
  460. self._nfev = 0
  461. if isinstance(init, string_types):
  462. if init == 'latinhypercube':
  463. self.init_population_lhs()
  464. elif init == 'random':
  465. self.init_population_random()
  466. else:
  467. raise ValueError(self.__init_error_msg)
  468. else:
  469. self.init_population_array(init)
  470. self.disp = disp
  471. def init_population_lhs(self):
  472. """
  473. Initializes the population with Latin Hypercube Sampling.
  474. Latin Hypercube Sampling ensures that each parameter is uniformly
  475. sampled over its range.
  476. """
  477. rng = self.random_number_generator
  478. # Each parameter range needs to be sampled uniformly. The scaled
  479. # parameter range ([0, 1)) needs to be split into
  480. # `self.num_population_members` segments, each of which has the following
  481. # size:
  482. segsize = 1.0 / self.num_population_members
  483. # Within each segment we sample from a uniform random distribution.
  484. # We need to do this sampling for each parameter.
  485. samples = (segsize * rng.random_sample(self.population_shape)
  486. # Offset each segment to cover the entire parameter range [0, 1)
  487. + np.linspace(0., 1., self.num_population_members,
  488. endpoint=False)[:, np.newaxis])
  489. # Create an array for population of candidate solutions.
  490. self.population = np.zeros_like(samples)
  491. # Initialize population of candidate solutions by permutation of the
  492. # random samples.
  493. for j in range(self.parameter_count):
  494. order = rng.permutation(range(self.num_population_members))
  495. self.population[:, j] = samples[order, j]
  496. # reset population energies
  497. self.population_energies = np.full(self.num_population_members,
  498. np.inf)
  499. # reset number of function evaluations counter
  500. self._nfev = 0
  501. def init_population_random(self):
  502. """
  503. Initialises the population at random. This type of initialization
  504. can possess clustering, Latin Hypercube sampling is generally better.
  505. """
  506. rng = self.random_number_generator
  507. self.population = rng.random_sample(self.population_shape)
  508. # reset population energies
  509. self.population_energies = np.full(self.num_population_members,
  510. np.inf)
  511. # reset number of function evaluations counter
  512. self._nfev = 0
  513. def init_population_array(self, init):
  514. """
  515. Initialises the population with a user specified population.
  516. Parameters
  517. ----------
  518. init : np.ndarray
  519. Array specifying subset of the initial population. The array should
  520. have shape (M, len(x)), where len(x) is the number of parameters.
  521. The population is clipped to the lower and upper bounds.
  522. """
  523. # make sure you're using a float array
  524. popn = np.asfarray(init)
  525. if (np.size(popn, 0) < 5 or
  526. popn.shape[1] != self.parameter_count or
  527. len(popn.shape) != 2):
  528. raise ValueError("The population supplied needs to have shape"
  529. " (M, len(x)), where M > 4.")
  530. # scale values and clip to bounds, assigning to population
  531. self.population = np.clip(self._unscale_parameters(popn), 0, 1)
  532. self.num_population_members = np.size(self.population, 0)
  533. self.population_shape = (self.num_population_members,
  534. self.parameter_count)
  535. # reset population energies
  536. self.population_energies = (np.ones(self.num_population_members) *
  537. np.inf)
  538. # reset number of function evaluations counter
  539. self._nfev = 0
  540. @property
  541. def x(self):
  542. """
  543. The best solution from the solver
  544. """
  545. return self._scale_parameters(self.population[0])
  546. @property
  547. def convergence(self):
  548. """
  549. The standard deviation of the population energies divided by their
  550. mean.
  551. """
  552. if np.any(np.isinf(self.population_energies)):
  553. return np.inf
  554. return (np.std(self.population_energies) /
  555. np.abs(np.mean(self.population_energies) + _MACHEPS))
  556. def converged(self):
  557. """
  558. Return True if the solver has converged.
  559. """
  560. return (np.std(self.population_energies) <=
  561. self.atol +
  562. self.tol * np.abs(np.mean(self.population_energies)))
  563. def solve(self):
  564. """
  565. Runs the DifferentialEvolutionSolver.
  566. Returns
  567. -------
  568. res : OptimizeResult
  569. The optimization result represented as a ``OptimizeResult`` object.
  570. Important attributes are: ``x`` the solution array, ``success`` a
  571. Boolean flag indicating if the optimizer exited successfully and
  572. ``message`` which describes the cause of the termination. See
  573. `OptimizeResult` for a description of other attributes. If `polish`
  574. was employed, and a lower minimum was obtained by the polishing,
  575. then OptimizeResult also contains the ``jac`` attribute.
  576. """
  577. nit, warning_flag = 0, False
  578. status_message = _status_message['success']
  579. # The population may have just been initialized (all entries are
  580. # np.inf). If it has you have to calculate the initial energies.
  581. # Although this is also done in the evolve generator it's possible
  582. # that someone can set maxiter=0, at which point we still want the
  583. # initial energies to be calculated (the following loop isn't run).
  584. if np.all(np.isinf(self.population_energies)):
  585. self.population_energies[:] = self._calculate_population_energies(
  586. self.population)
  587. self._promote_lowest_energy()
  588. # do the optimisation.
  589. for nit in xrange(1, self.maxiter + 1):
  590. # evolve the population by a generation
  591. try:
  592. next(self)
  593. except StopIteration:
  594. warning_flag = True
  595. if self._nfev > self.maxfun:
  596. status_message = _status_message['maxfev']
  597. elif self._nfev == self.maxfun:
  598. status_message = ('Maximum number of function evaluations'
  599. ' has been reached.')
  600. break
  601. if self.disp:
  602. print("differential_evolution step %d: f(x)= %g"
  603. % (nit,
  604. self.population_energies[0]))
  605. # should the solver terminate?
  606. convergence = self.convergence
  607. if (self.callback and
  608. self.callback(self._scale_parameters(self.population[0]),
  609. convergence=self.tol / convergence) is True):
  610. warning_flag = True
  611. status_message = ('callback function requested stop early '
  612. 'by returning True')
  613. break
  614. if np.any(np.isinf(self.population_energies)):
  615. intol = False
  616. else:
  617. intol = (np.std(self.population_energies) <=
  618. self.atol +
  619. self.tol * np.abs(np.mean(self.population_energies)))
  620. if warning_flag or intol:
  621. break
  622. else:
  623. status_message = _status_message['maxiter']
  624. warning_flag = True
  625. DE_result = OptimizeResult(
  626. x=self.x,
  627. fun=self.population_energies[0],
  628. nfev=self._nfev,
  629. nit=nit,
  630. message=status_message,
  631. success=(warning_flag is not True))
  632. if self.polish:
  633. result = minimize(self.func,
  634. np.copy(DE_result.x),
  635. method='L-BFGS-B',
  636. bounds=self.limits.T)
  637. self._nfev += result.nfev
  638. DE_result.nfev = self._nfev
  639. if result.fun < DE_result.fun:
  640. DE_result.fun = result.fun
  641. DE_result.x = result.x
  642. DE_result.jac = result.jac
  643. # to keep internal state consistent
  644. self.population_energies[0] = result.fun
  645. self.population[0] = self._unscale_parameters(result.x)
  646. return DE_result
  647. def _calculate_population_energies(self, population):
  648. """
  649. Calculate the energies of all the population members at the same time.
  650. Parameters
  651. ----------
  652. population : ndarray
  653. An array of parameter vectors normalised to [0, 1] using lower
  654. and upper limits. Has shape ``(np.size(population, 0), len(x))``.
  655. Returns
  656. -------
  657. energies : ndarray
  658. An array of energies corresponding to each population member. If
  659. maxfun will be exceeded during this call, then the number of
  660. function evaluations will be reduced and energies will be
  661. right-padded with np.inf. Has shape ``(np.size(population, 0),)``
  662. """
  663. num_members = np.size(population, 0)
  664. nfevs = min(num_members,
  665. self.maxfun - num_members)
  666. energies = np.full(num_members, np.inf)
  667. parameters_pop = self._scale_parameters(population)
  668. try:
  669. calc_energies = list(self._mapwrapper(self.func,
  670. parameters_pop[0:nfevs]))
  671. energies[0:nfevs] = calc_energies
  672. except (TypeError, ValueError):
  673. # wrong number of arguments for _mapwrapper
  674. # or wrong length returned from the mapper
  675. raise RuntimeError("The map-like callable must be of the"
  676. " form f(func, iterable), returning a sequence"
  677. " of numbers the same length as 'iterable'")
  678. self._nfev += nfevs
  679. return energies
  680. def _promote_lowest_energy(self):
  681. # promotes the lowest energy to the first entry in the population
  682. l = np.argmin(self.population_energies)
  683. # put the lowest energy into the best solution position.
  684. self.population_energies[[0, l]] = self.population_energies[[l, 0]]
  685. self.population[[0, l], :] = self.population[[l, 0], :]
  686. def __iter__(self):
  687. return self
  688. def __enter__(self):
  689. return self
  690. def __exit__(self, *args):
  691. # to make sure resources are closed down
  692. self._mapwrapper.close()
  693. self._mapwrapper.terminate()
  694. def __del__(self):
  695. # to make sure resources are closed down
  696. self._mapwrapper.close()
  697. self._mapwrapper.terminate()
  698. def __next__(self):
  699. """
  700. Evolve the population by a single generation
  701. Returns
  702. -------
  703. x : ndarray
  704. The best solution from the solver.
  705. fun : float
  706. Value of objective function obtained from the best solution.
  707. """
  708. # the population may have just been initialized (all entries are
  709. # np.inf). If it has you have to calculate the initial energies
  710. if np.all(np.isinf(self.population_energies)):
  711. self.population_energies[:] = self._calculate_population_energies(
  712. self.population)
  713. self._promote_lowest_energy()
  714. if self.dither is not None:
  715. self.scale = (self.random_number_generator.rand()
  716. * (self.dither[1] - self.dither[0]) + self.dither[0])
  717. if self._updating == 'immediate':
  718. # update best solution immediately
  719. for candidate in range(self.num_population_members):
  720. if self._nfev > self.maxfun:
  721. raise StopIteration
  722. # create a trial solution
  723. trial = self._mutate(candidate)
  724. # ensuring that it's in the range [0, 1)
  725. self._ensure_constraint(trial)
  726. # scale from [0, 1) to the actual parameter value
  727. parameters = self._scale_parameters(trial)
  728. # determine the energy of the objective function
  729. energy = self.func(parameters)
  730. self._nfev += 1
  731. # if the energy of the trial candidate is lower than the
  732. # original population member then replace it
  733. if energy < self.population_energies[candidate]:
  734. self.population[candidate] = trial
  735. self.population_energies[candidate] = energy
  736. # if the trial candidate also has a lower energy than the
  737. # best solution then promote it to the best solution.
  738. if energy < self.population_energies[0]:
  739. self._promote_lowest_energy()
  740. elif self._updating == 'deferred':
  741. # update best solution once per generation
  742. if self._nfev >= self.maxfun:
  743. raise StopIteration
  744. # 'deferred' approach, vectorised form.
  745. # create trial solutions
  746. trial_pop = np.array(
  747. [self._mutate(i) for i in range(self.num_population_members)])
  748. # enforce bounds
  749. self._ensure_constraint(trial_pop)
  750. # determine the energies of the objective function
  751. trial_energies = self._calculate_population_energies(trial_pop)
  752. # which solutions are improved?
  753. loc = trial_energies < self.population_energies
  754. self.population = np.where(loc[:, np.newaxis],
  755. trial_pop,
  756. self.population)
  757. self.population_energies = np.where(loc,
  758. trial_energies,
  759. self.population_energies)
  760. # make sure the best solution is updated if updating='deferred'.
  761. # put the lowest energy into the best solution position.
  762. self._promote_lowest_energy()
  763. return self.x, self.population_energies[0]
  764. next = __next__
  765. def _scale_parameters(self, trial):
  766. """Scale from a number between 0 and 1 to parameters."""
  767. return self.__scale_arg1 + (trial - 0.5) * self.__scale_arg2
  768. def _unscale_parameters(self, parameters):
  769. """Scale from parameters to a number between 0 and 1."""
  770. return (parameters - self.__scale_arg1) / self.__scale_arg2 + 0.5
  771. def _ensure_constraint(self, trial):
  772. """Make sure the parameters lie between the limits."""
  773. mask = np.where((trial > 1) | (trial < 0))
  774. trial[mask] = self.random_number_generator.rand(mask[0].size)
  775. def _mutate(self, candidate):
  776. """Create a trial vector based on a mutation strategy."""
  777. trial = np.copy(self.population[candidate])
  778. rng = self.random_number_generator
  779. fill_point = rng.randint(0, self.parameter_count)
  780. if self.strategy in ['currenttobest1exp', 'currenttobest1bin']:
  781. bprime = self.mutation_func(candidate,
  782. self._select_samples(candidate, 5))
  783. else:
  784. bprime = self.mutation_func(self._select_samples(candidate, 5))
  785. if self.strategy in self._binomial:
  786. crossovers = rng.rand(self.parameter_count)
  787. crossovers = crossovers < self.cross_over_probability
  788. # the last one is always from the bprime vector for binomial
  789. # If you fill in modulo with a loop you have to set the last one to
  790. # true. If you don't use a loop then you can have any random entry
  791. # be True.
  792. crossovers[fill_point] = True
  793. trial = np.where(crossovers, bprime, trial)
  794. return trial
  795. elif self.strategy in self._exponential:
  796. i = 0
  797. while (i < self.parameter_count and
  798. rng.rand() < self.cross_over_probability):
  799. trial[fill_point] = bprime[fill_point]
  800. fill_point = (fill_point + 1) % self.parameter_count
  801. i += 1
  802. return trial
  803. def _best1(self, samples):
  804. """best1bin, best1exp"""
  805. r0, r1 = samples[:2]
  806. return (self.population[0] + self.scale *
  807. (self.population[r0] - self.population[r1]))
  808. def _rand1(self, samples):
  809. """rand1bin, rand1exp"""
  810. r0, r1, r2 = samples[:3]
  811. return (self.population[r0] + self.scale *
  812. (self.population[r1] - self.population[r2]))
  813. def _randtobest1(self, samples):
  814. """randtobest1bin, randtobest1exp"""
  815. r0, r1, r2 = samples[:3]
  816. bprime = np.copy(self.population[r0])
  817. bprime += self.scale * (self.population[0] - bprime)
  818. bprime += self.scale * (self.population[r1] -
  819. self.population[r2])
  820. return bprime
  821. def _currenttobest1(self, candidate, samples):
  822. """currenttobest1bin, currenttobest1exp"""
  823. r0, r1 = samples[:2]
  824. bprime = (self.population[candidate] + self.scale *
  825. (self.population[0] - self.population[candidate] +
  826. self.population[r0] - self.population[r1]))
  827. return bprime
  828. def _best2(self, samples):
  829. """best2bin, best2exp"""
  830. r0, r1, r2, r3 = samples[:4]
  831. bprime = (self.population[0] + self.scale *
  832. (self.population[r0] + self.population[r1] -
  833. self.population[r2] - self.population[r3]))
  834. return bprime
  835. def _rand2(self, samples):
  836. """rand2bin, rand2exp"""
  837. r0, r1, r2, r3, r4 = samples
  838. bprime = (self.population[r0] + self.scale *
  839. (self.population[r1] + self.population[r2] -
  840. self.population[r3] - self.population[r4]))
  841. return bprime
  842. def _select_samples(self, candidate, number_samples):
  843. """
  844. obtain random integers from range(self.num_population_members),
  845. without replacement. You can't have the original candidate either.
  846. """
  847. idxs = list(range(self.num_population_members))
  848. idxs.remove(candidate)
  849. self.random_number_generator.shuffle(idxs)
  850. idxs = idxs[:number_samples]
  851. return idxs
  852. class _FunctionWrapper(object):
  853. """
  854. Object to wrap user cost function, allowing picklability
  855. """
  856. def __init__(self, f, args):
  857. self.f = f
  858. self.args = [] if args is None else args
  859. def __call__(self, x):
  860. return self.f(x, *self.args)