[SciPy-Dev] API to control iteration in differential evolution #6923

Evgeni Burovski evgeny.burovskiy at gmail.com
Sat Jan 14 08:49:07 EST 2017


On Wed, Jan 11, 2017 at 12:38 PM, Denis Laxalde <denis at laxalde.org> wrote:
> Hi,
>
> Recently there have been discussions about possible extensions of
> scipy.optimize.differential_evolution() :
>
> * https://github.com/scipy/scipy/issues/6878 is about having the
> callback function receive the function value
>
> * https://github.com/scipy/scipy/issues/6879 is about customizing of
> population initialization
>
> The differential_evolution function being already quite complex, we
> thought it would be better to expose a "lower level" API to control the
> iteration in the algorithm. This would be done by making the
> DifferentialEvolutionSolver class (in
> scipy/optimize/_differentialevolution.py) public and cleaning it a bit.
>
> I submitted a pull request for this :
> https://github.com/scipy/scipy/pull/6923
> One noticeable thing is that it also makes the class a context manager
> to encapsulate the population initialization (in enter step) and final
> polishing (in exit step). Ultimately, usage of this class (renamed as
> DifferentialEvolution) would be:
>
>   with DifferentialEvolution(func, bounds) as solver:
>     # iterate until maxiter/maxfev is reached or the algorithm converged
>     for step in solver:
>       if mycallback(step.x, step.fun):
>         break
>   result = solver.result
>
> I might look a bit fancy, but I think it makes sense to regard a
> computation as a kind of context. Feedback welcome!


FWIW, +1 from me. (I'm not a heavy user however)

Exposing the iteration API seems reasonable and better than relying on
callbacks.

Small details, feel free to take or leave:

1. What is `step` in `for step in solver`? If it's a current "state of
the minimizer", then can its contents be retrieved from the solver
object itself (step vs `solver.result` above)? Or is it just a step
number and the solver holds all the information at each step via
`solver.result?` This needs to be specified and documented.

2. Since the solver class is iterable, just iterating it without the
context manager should be equivalent. Something like this (pseudocode)

solver = DifferentialEvolutionSolver(...)
while not_converged:
    result = next(solver)

should be equivalent to your example above (modulo callback).

3. My mental model would be that the solver being iterable and the
context manager are both a syntax sugar for a pair of methods, roughly
`Solver.make_a_single_step_then_pause()` and
`Solver.do_full_minimization_until_convergence()` which repeatedly
calls the first one.
(This might be influenced by a DIY MC code I had a while ago,
https://github.com/ev-br/mcba/blob/master/mcba/abstract_walker.py#L80,
not sure how well it maps onto this case, so feel free to disregard)

4. Having the solver class iterable, with or without the context
manager, forces the iteration to happen at the python level.
This is likely not a problem with the current code, where the whole
class is in python anyway. However if some future refactoring moves
parts of the implementation to a compiled code, the overhead can get
significant. This seems to argue for a black-box
`do_a_full_simulation` method.
 (Again, this might be my MCMC background talking, where a single step
is cheap and the python generator overhead is non-trivial.)


Cheers,

Evgeni



More information about the SciPy-Dev mailing list