DifferentialEvolution: custom mutation and recombination functions?
Hi - newbie here, my apologies for any ignorance here! It is my understanding that scipy has no ability for the user to specify custom mutation or recombination functions, or any sort of manual alteration of new population members at all - correct? If so: are there any plans to introduce such a thing? If not: would there be interest in it? It's among the most trivial of all possible feature additions - just adding in ", mutation_fn=<current_scipy_mutation_fn>, recombination_fn=<current_scipy_recombination_fn>" to scipy.optimize.differential_evolution, updating the docs, and writing test functions that just return a string of 1s, then printing the input provided to the minimize function, which should eventually turn into all 1s. Use case: the user knows far more about what their data "means" than scipy will ever know. Including when a change in one place implies changes in another, how various factors ahould impact new values, etc. A particularly useful case is where the data represents a string of "genes", where duplication is not inherently deleterious. One of natural evolution's most powerful features is gene duplication followed by genetic drift of the copies, with their functionality diverging: the original function is retained while a new function is created. But scipy's stock mutation and recombination functions can't duplicate, shift around, or match up genes. A user however could readily provide such a function. I'm currently dealing with such a case (geometry optimization - each cluster of floats represents how a new element attaches to a mesh) If there are no plans to implement it, I might (or might not, depending) be able to find the time to do so. Though I know 98% of the time required would not be coding / testing, but rather figuring out how to setup and develop for the scipy test environment and figuring out how to contribute the changes - so it would be easier if an existing dev could just be bothered to add ", mutation_fn=<current_scipy_mutation_fn>, recombination_fn=<current_scipy_recombination_fn>" :) Anyway, sorry to bother you all! - kv, Karen
Hi Karen, thanks for your question/request. It's great to have you contribute, there's no apologies need to be made for being new.
It is my understanding that scipy has no ability for the user to specify custom mutation or recombination functions, or any sort of manual alteration of new population members at all - correct?
That is correct, there is no current way of doing that.
If so: are there any plans to introduce such a thing?
Not until you suggested it.
If not: would there be interest in it?
If a good case is made for feature additions, then they stand a good chance of getting in if someone writes a PR for it.
It's among the most trivial of all possible feature additions - just adding in ", mutation_fn=<current_scipy_mutation_fn>, recombination_fn=<current_scipy_recombination_fn>" to scipy.optimize.differential_evolution, updating the docs, and writing test functions that just return a string of 1s, then printing the input provided to the minimize function, which should eventually turn into all 1s.
What you're requesting is not necessarily trivial, there have been a lot of other features added that have been significantly easier to pass in. I think if we were to add in such a feature it would be added by supplying a callable to `strategy`. The callable function would be completely responsible for generating trial vectors, i.e. doing the mutation AND recombination. A possible call signature would be `strategy_func(candidate, population, rng=None)`, where `candidate` is an int in the range [0, S], `population` contains all the current population members and has shape (S, N), where N is the number of parameters and S is the total population size. Finally `rng` is the random number generator being used within the solver. rng is provided to obtain fully reproducible optimisations, you can choose to use it or not. The candidate is the index of the population member that you're going to be comparing the trial vector to. The strategy_func would be responsible for mutating (blending) members of the population together, doing the crossover/recombination itself, and returning a trial vector with shape (N,). Note that all entries in population are currently numbers in the range [0, 1], and are scaled to 'actual values' using the lower and upper bounds. If such a feature is added it would be reasonable for the strategy_func to receive population values in their actual range, i.e. scaled from [0, 1] to [lowerlim, upperlim], sent to strategy_func, trial returned from func is then scaled back to [0, 1]. The to/from scaling would add some overhead. It's unclear what you meant by test functions returning a string of 1s, differential_evolution works by modifying values in continuous variables. You can see the tests for the various strategies at https://github.com/scipy/scipy/blob/d4efe3665e69a6a46ab591ac3da359d8d98e7d10.... You can see the blending in action at https://github.com/scipy/scipy/blob/main/scipy/optimize/_differentialevoluti.... There are no strings anywhere. If such a feature was added it would be limited to generation of the trial vector only. Determination of whether a solution is accepted or not is a separate matter, and one which would be harder to change. Use case: the user knows far more about what their data "means" than scipy
will ever know. Including when a change in one place implies changes in another, how various factors ahould impact new values, etc.
A particularly useful case is where the data represents a string of
"genes", where duplication is not inherently deleterious. One of natural evolution's most powerful features is gene duplication followed by genetic drift of the copies, with their functionality diverging: the original function is retained while a new function is created. But scipy's stock mutation and recombination functions can't duplicate, shift around, or match up genes. A user however could readily provide such a function. I'm currently dealing with such a case (geometry optimization - each cluster of floats represents how a new element attaches to a mesh)
It's not quite clear to me what your strategy function looks like, it'd be interesting to see an example.
If there are no plans to implement it, I might (or might not, depending) be able to find the time to do so. Though I know 98% of the time required would not be coding / testing, but rather figuring out how to setup and develop for the scipy test environment and figuring out how to contribute the changes - so it would be easier if an existing dev could just be bothered to add ", mutation_fn=<current_scipy_mutation_fn>, recombination_fn=<current_scipy_recombination_fn>" :)
THe time split would probably be 5% setup, 30 % writing code, 30% writing test cases, 25 % polishing.
participants (2)
-
Andrew Nelson -
Karen Róbertsdóttir