#72 Differential Evolution improvement

closed-accepted
None
5
2012-11-21
2012-11-01
No

Hi All,
I have come across the new implementation of Differential Evolution that appeared in QuantLib HEAD source code recently. I decided to improve it slightly. I took performance very seriously as the DE implementation should be light in order to avoid unnecessary time overhead.

The most significant change is that I use DiffEvolConfiguration object that defines the behavior of the algorithm. The reason is that it makes the implementation more flexible. There are dozens of DE variants... I tried to be in line with current QuantLib optimization interface.

Important changes:
1) DE Optimizer consumes configuration object that defines its behavior - as I mentioned, the number of variations of the DE algorithm is huge. This approach makes it possible to extend the implementations in the future.
2) Constraints that define search space are not the part of the algorithm itself - this is why additional upperBound and lowerBound functions were added to Constraint object. NonhomogeneousBoundaryConstraint to define box constraints was added too. The other aspect is if the bounds are going to be valid for emerging populations - practical advise is that it should as for certain parameters, new populations seem to diverge. On the other hand, it adds some additional overhead. General observation is that it is always possible to improve performance for a given objective function. One needs to find appropriate DE configuration only.
3) I added three different crossover types: normal, binomial and exponential (the name for the first crossover type comes from the fact that assuming binomial crossover, the number of mutants taken into account in a given population converges in distribution to normal variable for growing problem size)
4) There are 6 methods available in this implementation. However, it is possible to go further and implement various base element types, differences of a given size, various weights distributions for the differences etc. Implemented approaches are the most common used in practice as the more complicated recombination procedure, the higher computation cost is.
5) For ModFourthDeJong objective function as I was able to find a point for which the objective function value is 8.86549 which is significantly lower than 12.3724219287 :
DE type: bestMemberWithJitter
Crossover type: normal
Apply Bounds: true
CrossoverProb: 0.25
NumOfPopulationMembers: 500
PrintFullInfo: false
StepsizeWeight: 0.2
MaxIterations
Point: [ 0.299015; 0.352861; -0.0306954; -0.349516; 0.202407; 0.111475; 0.0783742; -0.0640798; 0.284987; -0.00386122; 0.134481; -0.174184; -0.0188605; 0.217705; 0.0557937; 0.176954; -0.0757169; 0.219995; -0.079788; -0.142831; -0.129607; 0.135615; -0.152286; 0.0420379; -0.193061; 0.0582583; -0.0617313; -0.238126; -0.11224; -0.069721 ]
Objective function value: 8.86549
However, this is the best result only. Usually, the algorithm was stuck in several local minimas - most of them in [10;15] range. Further investigation is needed.
6) I have problem with Griewangk function too. I am not able to find fully successful method although the objective function value oscillated around 0.1 which is not bad. It needs to be verified.

I find the bestMemberWithJitter method the most successful and this is the only reason I set it as default method to be used. Hope it helps. I am happy to answer your questions. In case my patch does not work properly, please use changed files directly.

Kind regards,
Mateusz Kapturski

Discussion

  • Luigi Ballabio

    Luigi Ballabio - 2012-11-06

    Mateusz,
    thanks for the contribution. However, the patch is made practically unreadable by the fact that you re-indented the files (for instance, the Constraint class shows as completely changed, whereas you only actually added the lowerBound and upperBound methods).

    May you reformat the files so that they match the original indentation, and therefore so that the patch only contains the actual differences? This might seem picky on my part, and I know I'm asking some work; but on the one hand, as I said, the patch (and the diffs from version-control, once your changes get in) would be made more clearly readable, and on the other hand, having a consistent format in the library sources makes it easier to see the context.

    Thanks,
    Luigi

     
  • Mateusz Kapturski

    Hi Luigi,

    I have a habit of constant auto forrmatting when coding in Eclipse... Unfortunately, the autoformat profile cannot be adjusted to QL style easily. I removed unnecessary changes in order to make the patch more readable.

    Mateusz

    BTW. Is it possible to use other external tool (e.g. vim or emacs) to format source code file in line with QL style? First line in each QL file specifies autoformat options, doesn't it?

     
  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous - 2012-11-07

    Hi Mateusz,

    I have some remarks/questions concerning your DE implementation:

    1. You are using time(0) for the seed in the generation of the initial population (differentialevolution.cpp, line 27). Wouldn't a fix seed be better for reproducability, in the sense that the same data yield the same result?
    2. As a part of QuantLib, shouldn't one choose QuantLib's random number generator instead of boost's?
    3. For the ModFourthDeJong you mention that you find a lower minimum than the previous implementation. But that's not the point. If you take a look at the function, you see the uniform random part, i.e. the functional minimum is f(0) <= 30*Expectation(uniform) = 15. Therefore you'll get different realizations for different random numbers, which is ok as long as the minimum found is below 15.
    4. For the Griewangk function, the adaptive method in the current DE implementation succeeds to find the minimum f(0) = 0. Is the adaptive method implemented (or implementable) in your code as well?

    Thanks,
    Ralph

     
  • Mateusz Kapturski

    Hi Ralph,

    thank you for your remarks.

    1) Fixing the seed for reproducibility is a great idea. I simply forgot to do it. I tested my implementation against current time as seed to be perfectly sure that results are reliable. I added an optional config param. One may want "increase randomness" using the useFixedSeed_=false parameter. Therefore, both options are available now.
    2) This is a philosophical aspect. I used Boost as it was just easier for me and I find it a reliable tool. It does not add external dependency as QL already uses Boost libraries. The question is if there is value added if we change it. In my view - not significant. If you would like to change it, just go for it.
    3) I see your point now with respect to ModFourthDeJong. My random results were connected with your first observation - after fixing the seed, I obtained stable minimum equal to 10.409792. I have already amended the test case.
    4) The adaptive method is not implemented yet but it can be easily added. Could you provide more details on the method as I could not find it in the literature. The most important aspects are: how many differences are taken into account and how are weights applied to it? Was crossover important aspect for this method? And last practical question: Was the adaptive strategy successful for every chosen seed in your implementation? Griewangk objective function is probably the last major issue to resolve now.

    Thanks
    Mateusz

     
  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous - 2012-11-08

    Hi Mateusz,

    thank you for your reply. The adaptive method can be found here (section V):

    Brest, J. et al., 2006,
    "Self-Adapting Control Parameters in Differential Evolution: A Comparative Study on Numerical Benchmark Problems."
    ( A link which worked for me is http://150.214.190.154/docencia/sf1/Brest06.pdf\)

    And yes, the adaptive method always found the minimum of the Griewanck function when I played around with it.

    Ralph

     
  • Mateusz Kapturski

    Hi Ralph, Luigi

    you were right - self adapting method is quite successful. I have already added it to my implementation. All tests are passed now. I would be grateful if you double check my results. I think this is a stable version that can go to QL trunk. QL users have much more options now.

    Recent changes/remarks:
    1) Crossover self-adaptation is a separate option - it was very easy to implement it that way.
    2) User can decide if DE should use fixed seed or not (as discussed earlier)
    3) Adaptation is performed on the population member basis as stated in the paper.
    4) Additional rotation was added to the self-adaptive method to improve its performance.
    5) Extracted some logic into separate private methods.
    6) Original Griewangk function is usually defined on [-600, 600]^n box and my DE implementation is successful on this search domain.

     
  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous - 2012-11-12

    Hi Mateusz,

    good that the adaptive method seems to add some value to your implemetation ;-)

    When I run the test cases, 4 out of 5 pass now. However, for the ModFourthDeJong objective function, I get 12.0945 instead of your 10.964. Maybe this is due to the fact that I'm using a different boost version (1.47.0) and that boost's MT impementation has changed, but I don't know if it's worth investgating on this issue or rather exclude the ModFourthDeJong test case for now (because both values are valid).

    Ralph

     
  • Luigi Ballabio

    Luigi Ballabio - 2012-11-21
    • assigned_to: nobody --> lballabio
    • status: open --> closed-accepted
     
  • Luigi Ballabio

    Luigi Ballabio - 2012-11-21

    Mateusz,
    I've added your code to the repository. I've made a few refactorings to make it more in line with the style we've been using in the library, so you might want to check that everything still works. In particular:

    - I've replaced the use of boost::random with our mersenne-twister generator;
    - I've turned the configuration class into an inner class;
    - I've removed setters, which don't play well with observability;
    - I've removed pointer data members;
    - plus some minor cleanup.

    Thanks,
    Luigi

     
  • Mateusz Kapturski

    Hi Luigi,

    thank you for your amendments. The logic has not changed and it works for me. All tests passed.

    However, I had to change Makefile.am file in ql/math/optimisation directory and put -std=c++0x flag as you used Initializer list (feature from c++11). I compile QL using gcc. I wonder, if other users may experience similar problems. Is there a simple way to use this flag in the whole project? I tried to compile it using VS2010 and VS2012 but both failed (initializer lists are still missing in VS).

    Regards,
    Mateusz

     
  • Luigi Ballabio

    Luigi Ballabio - 2012-11-27

    That's strange. It's not an initializer list---well, now it is, but it's been allowed for a long time for struct initialization. I compiled it correctly with gcc 4.7 without the c++0x flag.
    Oh well. I'll try writing it differently.

     

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks