GithubHelp home page GithubHelp logo

openmole / mgo Goto Github PK

View Code? Open in Web Editor NEW
71.0 14.0 3.0 3.25 MB

Purely functional genetic algorithms for multi-objective optimisation

Scala 100.00%
genetic-algorithm scala optimisation functional-programming parameter-tuning hyperparameter-optimization hyperparameters hyperparameter-tuning

mgo's Introduction

MGO

MGO is a purely functionnal scala library based for evolutionary / genetic algorithms:

  • enforcing immutability,
  • exposes a modular and extensible architecture,
  • implements state of the art algorithms,
  • handles noisy (stochastic) fitness functions,
  • implements auto-adaptatative algortihms,
  • implements algorithms with distributed computing in mind for integration with OpenMOLE.

MGO implements NGSAII, NSGA3, CP (Calibration Profile), PSE (Pattern Search Experiment), OSE (Antecedant research), Niched Evolution, ABC (Bayesian Calibration).

Licence

MGO is licenced under the GNU GPLv3 software licence. 

Example

Define a problem, for instance the multi-modal multi-objective ZDT4 benchmark:

  object zdt4 {

    def continuous(size: Int) = Vector.fill(size)(C(0.0, 5.0))
    
    def compute(genome: Vector[Double], d: Vector[Int]): Vector[Double] = {
      val genomeSize = genome.size

      def g(x: Seq[Double]) = 1 + 10 * (genomeSize - 1) + x.map { i => pow(i, 2) - 10 * cos(4 * Pi * i) }.sum

      def f(x: Seq[Double]) = {
        val gx = g(x)
        gx * (1 - sqrt(genome(0) / gx))
      }

      Vector(genome(0), f(genome.tail))
    }

 }

Define the optimisation algorithm, for instance NSGAII:

  import mgo.evolution._
  import mgo.evolution.algorithm._
  
  // For zdt4
  import mgo.test._

  val nsga2 =
    NSGA2(
      mu = 100,
      lambda = 100,
      fitness = zdt4.compute,
      continuous = zdt4.continuous(10))

Run the optimisation:

  def evolution =
    nsga2.
      until(afterGeneration(1000)).
      trace((s, is) => println(s.generation))

  val (finalState, finalPopulation) = evolution.eval(new util.Random(42))

  println(NSGA2.result(nsga2, finalPopulation).mkString("\n"))
  

Noisy fitness functions

All algorithm in MGO have version to compute on noisy fitness function. MGO handle noisy fitness functions by resampling only the most promising individuals. It uses an aggregation function to aggregate the multiple sample when needed.

For instance a version of NSGA2 for noisy fitness functions may be used has follow:

  import mgo._
  import algorithm.noisynsga2._
  import context.implicits._

  object sphere {
    def scale(s: Vector[Double]): Vector[Double] = s.map(_.scale(-2, 2))
    def compute(i: Vector[Double]): Double = i.map(x => x * x).sum
  }

  object noisySphere {
    def scale(s: Vector[Double]): Vector[Double] = sphere.scale(s)
    def compute(rng: util.Random, v: Vector[Double]) =
      sphere.compute(v) + rng.nextGaussian() * 0.5 * math.sqrt(sphere.compute(v))
  }

  def aggregation(history: Vector[Vector[Double]]) = history.transpose.map { o => o.sum / o.size }

  val nsga2 =
    NoisyNSGA2(
      mu = 100,
      lambda = 100,
      fitness = (rng, v) => Vector(noisySphere.compute(rng, v)),
      aggregation = aggregation,
      genomeSize = 2)

  val (finalState, finalPopulation) =
    run(nsga2).
      until(afterGeneration(1000)).
      trace((s, is) => println(s.generation)).
      eval(new util.Random(42))

  println(result(finalPopulation, aggregation, noisySphere.scale).mkString("\n"))

Diversity only

MGO proposes the PSE alorithm that aim a creating diverse solution instead of optimsing a function. The paper about this algorithm can be found here.

  import mgo._
  import algorithm.pse._
  import context.implicits._

  val pse = PSE(
    lambda = 10,
    phenotype = zdt4.compute,
    pattern =
      boundedGrid(
        lowBound = Vector(0.0, 0.0),
        highBound = Vector(1.0, 200.0),
        definition = Vector(10, 10)),
    genomeSize = 10)

  val (finalState, finalPopulation) =
    run(pse).
      until(afterGeneration(1000)).
      trace((s, is) => println(s.generation)).
      eval(new util.Random(42))

  println(result(finalPopulation, zdt4.scale).mkString("\n"))

This program explores all the different combination of values that can be produced by the multi-objective function of ZDT4.

For more examples, have a look at the main/scala/fr/iscpif/mgo/test directory in the repository.

Mixed optimisation and diversity

The calibration profile algorthim compute the best fitness function for a set of niches. This algorithm is explained here.

In MGO you can compute profiles of a 10 dimensional hyper-sphere function using the following:

  import algorithm.profile._
  import context.implicits._

  //Profile the first dimension of the genome
  val algo = Profile(
    lambda = 100,
    fitness = sphere.compute,
    niche = genomeProfile(x = 0, nX = 10),
    genomeSize = 10)

  val (finalState, finalPopulation) =
    run(algo).
      until(afterGeneration(1000)).
      trace((s, is) => println(s.generation)).
      eval(new util.Random(42))

  println(result(finalPopulation, sphere.scale).mkString("\n"))

Noisy profiles

All algorithms in MGO have a pendant for noisy fitness function. Here is an example of a profile computation for a sphere function with noise.

  import algorithm.noisyprofile._
  import context.implicits._

  def aggregation(history: Vector[Double]) = history.sum / history.size
  def niche = genomeProfile(x = 0, nX = 10)

  val algo = NoisyProfile(
    muByNiche = 20,
    lambda = 100,
    fitness = noisySphere.compute,
    aggregation = aggregation,
    niche = niche,
    genomeSize = 5)

  val (finalState, finalPopulation) =
    run(algo).
      until(afterGeneration(1000)).
      trace((s, is) => println(s.generation)).
      eval(new util.Random(42))

  println(result(finalPopulation, aggregation, noisySphere.scale, niche).mkString("\n"))

Distributed computing

Algorithms implemented in MGO are also avialiable in the workflow plateform for distributed computing OpenMOLE.

SBT dependency

  libraryDependencies += "fr.iscpif" %% "mgo" % "2.45"  

mgo's People

Contributors

gabriel-cardoso avatar guillaumecherel avatar jopasserat avatar justeraimbault avatar romainreuillon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mgo's Issues

[Open Ended Evolution] Metrics

We have internal metrics in mgo to measure crowding, diversity (the hitmap) in pse ; but we do not have generic measures of open-endedness, which could be useful to test benchmark pse or new algorithms.
I cam across this nice paper : https://www.mitpressjournals.org/doi/full/10.1162/artl_a_00280 where they provide a consistent set of metrics with an open source implementation (in cpp though) ; we may want to implement these in mgo

[Noisy NSGA2] Embedding method gives solutions with too few replications

I came across this several time: when running a noisy nsga2, some solutions have too few replications ; but most of the time a filtering of the final population on number of replications does the trick.
It sometimes however becomes problematic (as we were discussing with @mathieuleclaire this morning), when very few solutions have more than, let say 10 replications, and these are relatively bad regarding the optimization objective.
This is due to the way we handle stochasticity, by embedding the problem into a higher dimension by adding an objective as a decreasing function of the number of replications. Work is still in progress (here : https://github.com/openmole/mgo-benchmark ) to test this method against other ways to handle stochasticity, such as fixed number of replications, adaptive sampling, Kalman-based uncertainty computation, RTEA algorithm, etc.
So for the moment I would advice

  • either to go with the fixed replications method : wrap your stochastic model into a Replication task and go with a deterministic NSGA2; this should be fine if the computation budget allows it.
  • add a parameter for a minimal number of replication per points (we have a max only for now) ; this would be a "patch" but ensure no outlier with 1 replication ends in the final population (10 replications e.g. remains cheap but ensure a bit more robustness). This is however against the philosophy of the embedding method (find compromises between optimization objective and confidence in the solution).
    Would be interested to hear your thoughts on that !

Add new dependances for cake layer Evolution

Actually Evolution component is defined like this

trait Evolution extends Termination
    with Lambda
    with G
    with F
    with P
    with Archive
    with Breeding
    with Elitism { self =>

It seems an evolution metaheuristic also need :

  • a mapping function between Genotype and Phenotype, it can be used to define different sort of problem resolution : genome -> simulation, genome -> mathematical function
  • objective functions, the actual evaluation() function defined in Problem Class

A proposition ?

trait Evolution extends Termination
    with Lambda
    with G
    with F
    with P
    with Archive
    with Breeding
    with Elitism 
    with GtoPmapping
    with ObjectiveFunctions { self =>

False definition of Non Dominated Sorting brick

It seems the non dominated elitism used by NSGA2 implementation in MGO is not correct.

Actually the different front are builded using information based on the number of domination, this is another type of existing sorting, but do not correspond to the NDS or Non Dominated Sorting used by NSGA2

In NDS, at each step of the loop we take only the solution with dominated = 0 (cf the non dominated solution) to make a new front, then we remove this population selected, and then recompute a new non dominated front based on this new cutted population. etc.

Actually population are not removed, and not recomputed.

Only the last front is tested using diversity metric, this code seems correct in mgo.

Another point perhaps, it seems the NDS is realized on population + offspring on line 67

NonDominatedElitism(this)(filter(oldGeneration ++ offspring), mu)

But it's more a properties of NSGA2 than a generic properties from NDS.In the case of NSGA1, the non dominated sorting operate only on offspring population (so the NDS can be without elitism). Perhaps it can be a good idea to move this properties in another brick.

Fitness function can not restrain

HI, that's my first experience to use multi objectives algorithm with your code. My fitness function as below:

object test_food {

    var p:Vector[Vector[Double]] = Vector.empty[Vector[Double]]
    var size = 0
    def param(vector: Vector[Vector[Double]],size:Int): Unit ={
      this.p = vector
      this.size = size
    }
    def continuous(size: Int) = Vector.fill(size)(C(2, 5.0))
    def discrete = Vector.empty

    def compute(genome: Vector[Double], d: Vector[Int]): Vector[Double] = {
      val genomeSize = genome.size
      var population :Vector[Double] = Vector.empty
      println(genome,d)
      for (i <- 0 to this.size){
        var p_tmp = (genome.map(t => t*this.p(i)(genome.indexOf(t))).sum + this.p(i)(genome.size+1))
        println(p_tmp,genomeSize, this.p(i)(genome.size+1))
        population = population:+ p_tmp
      }
      println("-----------------")
      population
    }
  }

and call function as below:

object FOODNSGAII extends App {

  import algorithm._

  test_food.p = Vector(Vector(5.913,0.455,1.437,7.169,-65 ),Vector( 2.429 ,2.467,4.931,11.969,-25 ),Vector(12.612,4.281,6.485,33.13,-120 ))
  test_food.size=2
  val nsga2 =
    NSGA2(
      mu = 100,
      lambda = 100,
      fitness = test_food.compute,
      continuous = test_food.continuous(3))

  def evolution[M[_]: Generation: Random: cats.Monad: StartTime: IO] =
    nsga2.
      until(afterGeneration(1000)).
      trace((s, is) => println(s.generation)).
      evolution

  val (finalState, finalPopulation) = NSGA2.run(new util.Random(42)) { impl =>
    import impl._
    evolution[DSL].eval
  }

  println(NSGA2.result(nsga2, finalPopulation).mkString("\n"))

}

But the result can't be restrained. Something wrong?

here is the result:

Result(Vector(2.0, 2.0, 2.0),Vector(),Vector(100.47800000000001, 39.574, 195.672))
Result(Vector(2.0000000000000004, 2.0, 2.0),Vector(),Vector(78.646, 39.726, 162.348))
Result(Vector(2.0, 2.000000000000001, 2.0),Vector(),Vector(89.562, 39.650000000000006, 179.01))
Result(Vector(2.0, 2.0000000000000004, 2.0),Vector(),Vector(89.562, 39.650000000000006, 179.01))
Result(Vector(2.000000000000001, 2.0, 2.0),Vector(),Vector(78.646, 39.726, 162.348))
Result(Vector(2.0, 2.000000000000002, 2.0),Vector(),Vector(89.562, 39.650000000000006, 179.01))
Result(Vector(2.0, 2.0000000000000018, 2.0),Vector(),Vector(89.562, 39.650000000000006, 179.01))
Result(Vector(2.0, 2.0000000000000013, 2.0),Vector(),Vector(89.562, 39.650000000000006, 179.01))

About Fitness computation and storage

It's not really clear in MGO, there is no declared step for fitness computation.
The step is embeded in elitism() function call during evolution, perhaps there is a better way to create a real function assignFitness() somewhere in the program.

The fitness step need to create a real value attached to each individual, because in many algorithms there are multiple real value fitness computed for each individual. For example, in MOGA or NSGA1 there is no elitism, and selection mechanism operate on full population.

Some examples :

  • For nsga 2 the real fitness assigned after NDS with elitism for each individual equal the front number.
  • For Nsga 1 one of the real fitness assigned after NDS without elitism for each individual equal the front number, then after a total fitness is computed using this fitness + niche count (another fitness)
  • For MOGA, one of real fitness assigned after paretoRanking for each individuals in population = number of dominated + 1

It seems we can cumulate multiple fitness for one individual, so we need a clear way to operate and cumulate this fitness.

[Diversity search] PSE and stochasticity

Exploring a highly stochastic model with PSE lead some of us to a questioning on assumptions for a "reasonable" use of PSE regarding stochastic variability of indicators: from what I understood and what seems to be the consensus, probability distributions for a given parameter value should be relatively "narrow" (i.e. with a relatively low standard deviation and no fat tail) and the algorithm works with aggregates (generally medians), so solutions with few replications should be removed in the end.
What about a possible application to highly stochastic models, e.g. with power law distributions, or when noise is significantly higher than variations due to parameters ?
Should we investigate other types of algorithms specific to that purpose ? I have in mind a cloning algorithm for large deviation functions ( arxiv:0811.1041 ) for example, may be worth to look into that and to see to what extent similar stuff could be used in such a highly noisy context.

Implement methods of type MCMC or other Bayesian approaches

With the integration of Approximate Bayesian Computation in mgo, we have a foot in the door of bayesian approaches ; it would be interesting to discuss if we could integrate other methods such as Monte Carlo Markov Chains, Particle filters, etc.
I know @julienperret wanted to integrate such a method (do not remember the name though)

SMS-EMOA not using non dominated sorting

It seems the construction of SMS-EMOA is not correct in MGO.

Simple description of the algorithm say that :

The indicator-based EMOA use a quality indicator for the selection at least as a second stage measure. The SMS-EMOA follows this principle by first performing non-dominated sorting on the population and one offspring, and then for the worst subset, the hypervolume is determined and the individual with the least contribution to the hypervolume is denoted as the worst one and is discarded.

More detail can be found on the p59 to p65 of Nicolas Beume dissertation of 2011 named "Hypervolume-based Metaheuristics for Multiobjective Optimization"

SMS-EMOA use Non Dominated Sorting, then for only for the last front, the hypervolume ranking.

Actually, the non dominated sorting implemented in MGO is not used in the definition of SMS-EMOA .

trait SMSEMOEA <: Evolution
  with GAGenomeWithSigma
  with MG
  with BinaryTournamentSelection
  with TournamentOnRank
  with HypervolumeRanking
  with RankElitism
  with DynamicGACrossover
  with DynamicGAMutation
  with FitnessHypervolumeDiversity
  with NoArchive
  with GeneticBreeding
  with ClampedGenome

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.