On Wed, Jan 11, 2017 at 12:38 PM, Denis Laxalde <denis@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