Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Evolutionary Algorithms: How to Go Darwin

Evolutionary Algorithms: How to Go Darwin

Evolutionary Algorithms explained using the Traveling Salesman Problem as a use case, and implemented in Go, with an AngularJs frontend...

Bas W. Knopper

October 08, 2016
Tweet

More Decks by Bas W. Knopper

Other Decks in Technology

Transcript

  1. [email protected] @BWKnopper github.com/bknopper Let me introduce myself… • Bas W.

    Knopper • Dutch • JavaOne, J-Fall, GeeCon, JFokus Speaker • AI enthousiast • Soft spot for EvolutionaryAlgorithms • (Java) Developer • Managing Partner @ JCore
  2. [email protected] @BWKnopper github.com/bknopper By Developers. For Developers. De missie van

    JCore is om ambitieuze Java Developers een traject te bieden waarmee ze sneller en beter Senior Java Developers kunnen worden. Wat (Java Consultancy) -> We helpen klanten met het realiseren van complexe IT projecten. Hoe Met ambitieuze en enthousiaste Java Consultants die een bijdrage leveren bij de klant. Waarom Vanuit een passie voor IT en het oplossen van complexe problemen.
  3. [email protected] @BWKnopper github.com/bknopper What I would like to accomplish… •

    Interest • Understanding • How & when • Add to toolbox • Attention @DevFestNL #EvolutionaryAlgorithms #golang @BWKnopper
  4. [email protected] @BWKnopper github.com/bknopper Agenda • Introduction • NASA • Evolution

    Concepts • Puzzle Solving Time: Traveling Salesman Problem • Evolutionary Algorithm Design • Go Code • Demo! • Frameworks • Checklist
  5. [email protected] @BWKnopper github.com/bknopper NASA • Space Technology 5 mission •

    launched March 22, 2006, and completed June 20, 2006 • Three full service 25-kilogram-class spacecraft
  6. [email protected] @BWKnopper github.com/bknopper NASA Continued • Needs even smaller antenna

    • That still functions according to spec • Need forsolution that’s not easy to engineer • So they used an EA that made these:
  7. [email protected] @BWKnopper github.com/bknopper Evolution - “Survival of the fittest” Finite

    Resources Lifeforms with a basic instinct towards Reproduction Natural Selection
  8. [email protected] @BWKnopper github.com/bknopper Travelling Salesman Problem • Given n cities

    • n = number of cities to visit • Find (optimal) route for visiting all cities • Visit every city only once • Return to origin city • Search space is huge • For 30 cities there are 30! ≈ 10^32 possible routes That’s 100.000.000.000.000.000.000.000.000.000.000 possible routes! Brute force might not be the best solution…
  9. [email protected] @BWKnopper github.com/bknopper Puzzle solving time! • More down to

    earth example • TravellingSalesman Problem • Use case to show you • EvolutionaryAlgorithm Design • Plain Go Code • Demo
  10. [email protected] @BWKnopper github.com/bknopper Candidate Solution - Representation • n =

    6 • Label cities 1,2,3,4,5,6 • And base city 1 • Candidate Solution • Signifyingthe route • for n = 10 • Enables • Calculatingdistance (fitness function) • Mutation • Recombination 1 5 6 3 4 2 1 1 10 9 4 7 3 5 6 8 2 1
  11. [email protected] @BWKnopper github.com/bknopper Evaluation Function (Fitness Function) • Summed distance:

    • d1,2 + d2,4 + … + d5,1 • Used golang-geofor calculations • Minimize! 1 5 6 3 4 2 1
  12. [email protected] @BWKnopper github.com/bknopper /** * Calculates the total distance of

    the whole route * and stores it as this candidate solution's fitness */ func (candidateSolution *CandidateSolution) calculateFitness() { }
  13. [email protected] @BWKnopper github.com/bknopper /** * Calculates the total distance of

    the whole route * and stores it as this candidate solution's fitness */ func (candidateSolution *CandidateSolution) calculateFitness() { totalDistance := float64(0) for i := 0; i < (len(candidateSolution.Route) - 1); i++ { } candidateSolution.Fitness = totalDistance }
  14. [email protected] @BWKnopper github.com/bknopper /** * Calculates the total distance of

    the whole route * and stores it as this candidate solution's fitness */ func (candidateSolution *CandidateSolution) calculateFitness() { totalDistance := float64(0) for i := 0; i < (len(candidateSolution.Route) - 1); i++ { city := candidateSolution.Route[i] nextCity := candidateSolution.Route[i + 1] totalDistance += city.calculateDistance(nextCity) } candidateSolution.Fitness = totalDistance }
  15. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { }
  16. [email protected] @BWKnopper github.com/bknopper Figures from “Introduction toEvolutionary Computing” byA.E. Eiben

    & J.E. Smith (Springer) Termination Condition • EA’s are stochastic • May never find optimum
  17. [email protected] @BWKnopper github.com/bknopper Termination Condition • Combination • Sure to

    terminate • Time • Max number of runs (generations) • Goal • Fitness threshold • Fitness improvementstagnation Figure from “Introduction to Evolutionary Computing” by A.E. Eiben & J.E. Smith (Springer)
  18. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { }
  19. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; }
  20. [email protected] @BWKnopper github.com/bknopper Parent Selection • Where x is the

    number of parents: • Pick x best • Random x • Best xout of random y • In our example: • Best x out of random y
  21. [email protected] @BWKnopper github.com/bknopper func (algorithm *Algorithm) parentSelection() CandidateSolutions { tempPopulation

    := make(CandidateSolutions, algorithm.populationSize) copy(tempPopulation, algorithm.population) randomCandidates := make(CandidateSolutions, algorithm.parentPoolSize)
  22. [email protected] @BWKnopper github.com/bknopper func (algorithm *Algorithm) parentSelection() CandidateSolutions { tempPopulation

    := make(CandidateSolutions, algorithm.populationSize) copy(tempPopulation, algorithm.population) randomCandidates := make(CandidateSolutions, algorithm.parentPoolSize) for i := 0; i < algorithm.parentPoolSize; i++ { /* pick random candidates */
  23. [email protected] @BWKnopper github.com/bknopper func (algorithm *Algorithm) parentSelection() CandidateSolutions { tempPopulation

    := make(CandidateSolutions, algorithm.populationSize) copy(tempPopulation, algorithm.population) randomCandidates := make(CandidateSolutions, algorithm.parentPoolSize) for i := 0; i < algorithm.parentPoolSize; i++ { randomIndex := rand.Intn(len(tempPopulation)) randomCandidateSolution := tempPopulation[randomIndex]
  24. [email protected] @BWKnopper github.com/bknopper func (algorithm *Algorithm) parentSelection() CandidateSolutions { tempPopulation

    := make(CandidateSolutions, algorithm.populationSize) copy(tempPopulation, algorithm.population) randomCandidates := make(CandidateSolutions, algorithm.parentPoolSize) for i := 0; i < algorithm.parentPoolSize; i++ { randomIndex := rand.Intn(len(tempPopulation)) randomCandidateSolution := tempPopulation[randomIndex] randomCandidates[i] = randomCandidateSolution
  25. [email protected] @BWKnopper github.com/bknopper func (algorithm *Algorithm) parentSelection() CandidateSolutions { tempPopulation

    := make(CandidateSolutions, algorithm.populationSize) copy(tempPopulation, algorithm.population) randomCandidates := make(CandidateSolutions, algorithm.parentPoolSize) for i := 0; i < algorithm.parentPoolSize; i++ { randomIndex := rand.Intn(len(tempPopulation)) randomCandidateSolution := tempPopulation[randomIndex] randomCandidates[i] = randomCandidateSolution /* delete the candidate from the temp population, so we can't pick it again */ tempPopulation[randomIndex] = tempPopulation[len(tempPopulation)-1] tempPopulation = tempPopulation[:len(tempPopulation)-1] }
  26. [email protected] @BWKnopper github.com/bknopper func (algorithm *Algorithm) parentSelection() CandidateSolutions { tempPopulation

    := make(CandidateSolutions, algorithm.populationSize) copy(tempPopulation, algorithm.population) randomCandidates := make(CandidateSolutions, algorithm.parentPoolSize) for i := 0; i < algorithm.parentPoolSize; i++ { randomIndex := rand.Intn(len(tempPopulation)) randomCandidateSolution := tempPopulation[randomIndex] randomCandidates[i] = randomCandidateSolution /* delete the candidate from the temp population, so we can't pick it again */ tempPopulation[randomIndex] = tempPopulation[len(tempPopulation)-1] tempPopulation = tempPopulation[:len(tempPopulation)-1] } /* Sort the population so that the best candidates are up front */ sort.Sort(randomCandidates)
  27. [email protected] @BWKnopper github.com/bknopper func (algorithm *Algorithm) parentSelection() CandidateSolutions { tempPopulation

    := make(CandidateSolutions, algorithm.populationSize) copy(tempPopulation, algorithm.population) randomCandidates := make(CandidateSolutions, algorithm.parentPoolSize) for i := 0; i < algorithm.parentPoolSize; i++ { randomIndex := rand.Intn(len(tempPopulation)) randomCandidateSolution := tempPopulation[randomIndex] randomCandidates[i] = randomCandidateSolution /* delete the candidate from the temp population, so we can't pick it again */ tempPopulation[randomIndex] = tempPopulation[len(tempPopulation)-1] tempPopulation = tempPopulation[:len(tempPopulation)-1] } /* Sort the population so that the best candidates are up front */ sort.Sort(randomCandidates) /* return a list with size parentSelectionSize with the best CandidateSolutions */ return randomCandidates[0:algorithm.parentSelectionSize] }
  28. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; }
  29. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; 2 RECOMBINE pairs of parents; }
  30. [email protected] @BWKnopper github.com/bknopper Recombination • In our example: • half-half

    does not work • “Cut-and-crossfill” 1 6 5 3 4 2 1 1 4 2 1 1 2 4 5 6 3 1 1 5 4 2 1 1 3 5 4 2 1 1 6 3 5 4 2 1 1 4 2 5 6 3 1 1 2 4 5 6 3 1 1 2 4 5 6 3 1 1 2 4 5 6 3 1
  31. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) recombine(otherParent CandidateSolution) CandidateSolutions {

    /* get routes of both parents */ parentRoute1 := candidateSolution.VisitingCities parentRoute2 := otherParent.VisitingCities
  32. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) recombine(otherParent CandidateSolution) CandidateSolutions {

    /* get routes of both parents */ parentRoute1 := candidateSolution.VisitingCities parentRoute2 := otherParent.VisitingCities /* randomize cutIndex for "cross-and-fill point" */ cutIndex := int32(rand.Intn(len(parentRoute1)))
  33. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) recombine(otherParent CandidateSolution) CandidateSolutions {

    /* get routes of both parents */ parentRoute1 := candidateSolution.VisitingCities parentRoute2 := otherParent.VisitingCities /* randomize cutIndex for "cross-and-fill point" */ cutIndex := int32(rand.Intn(len(parentRoute1))) /* initialize the routes for the children */ childRoute1 := make(Cities, len(parentRoute1)) childRoute2 := make(Cities, len(parentRoute1))
  34. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) recombine(otherParent CandidateSolution) CandidateSolutions {

    /* get routes of both parents */ parentRoute1 := candidateSolution.VisitingCities parentRoute2 := otherParent.VisitingCities /* randomize cutIndex for "cross-and-fill point" */ cutIndex := int32(rand.Intn(len(parentRoute1))) /* initialize the routes for the children */ childRoute1 := make(Cities, len(parentRoute1)) childRoute2 := make(Cities, len(parentRoute1)) /* get the first part of both parent routes using the cut index */ partRoute1 := parentRoute1[0:cutIndex] partRoute2 := parentRoute2[0:cutIndex]
  35. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) recombine(otherParent CandidateSolution) CandidateSolutions {

    /* get routes of both parents */ parentRoute1 := candidateSolution.VisitingCities parentRoute2 := otherParent.VisitingCities /* randomize cutIndex for "cross-and-fill point" */ cutIndex := int32(rand.Intn(len(parentRoute1))) /* initialize the routes for the children */ childRoute1 := make(Cities, len(parentRoute1)) childRoute2 := make(Cities, len(parentRoute1)) /* get the first part of both parent routes using the cut index */ partRoute1 := parentRoute1[0:cutIndex] partRoute2 := parentRoute2[0:cutIndex] /* copy the first part of the parents cut into the children */ copy(childRoute1, partRoute1) copy(childRoute2, partRoute2)
  36. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) recombine(otherParent CandidateSolution) CandidateSolutions {

    /* get routes of both parents */ parentRoute1 := candidateSolution.VisitingCities parentRoute2 := otherParent.VisitingCities /* randomize cutIndex for "cross-and-fill point" */ cutIndex := int32(rand.Intn(len(parentRoute1))) /* initialize the routes for the children */ childRoute1 := make(Cities, len(parentRoute1)) childRoute2 := make(Cities, len(parentRoute1)) /* get the first part of both parent routes using the cut index */ partRoute1 := parentRoute1[0:cutIndex] partRoute2 := parentRoute2[0:cutIndex] /* copy the first part of the parents cut into the children */ copy(childRoute1, partRoute1) copy(childRoute2, partRoute2) candidateSolution.crossFill(childRoute1, parentRoute2, cutIndex) candidateSolution.crossFill(childRoute2, parentRoute1, cutIndex)
  37. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) recombine(otherParent CandidateSolution) CandidateSolutions {

    /* get routes of both parents */ parentRoute1 := candidateSolution.VisitingCities parentRoute2 := otherParent.VisitingCities /* randomize cutIndex for "cross-and-fill point" */ cutIndex := int32(rand.Intn(len(parentRoute1))) /* initialize the routes for the children */ childRoute1 := make(Cities, len(parentRoute1)) childRoute2 := make(Cities, len(parentRoute1)) /* get the first part of both parent routes using the cut index */ partRoute1 := parentRoute1[0:cutIndex] partRoute2 := parentRoute2[0:cutIndex] /* copy the first part of the parents cut into the children */ copy(childRoute1, partRoute1) copy(childRoute2, partRoute2) candidateSolution.crossFill(childRoute1, parentRoute2, cutIndex) candidateSolution.crossFill(childRoute2, parentRoute1, cutIndex) /* create new children using the new children routes */ child1 := NewCandidateSolution(getBaseCity(), childRoute1); child2 := NewCandidateSolution(getBaseCity(), childRoute2); /* put the children in a list and return it */ return CandidateSolutions{child1, child2} }
  38. [email protected] @BWKnopper github.com/bknopper /** * Check the rest of the

    route in the crossing parent and add the cities that are not yet in the child * (in the order of the route of the crossing parent) */ func (candidateSolution *CandidateSolution) crossFill(childRoute Cities, parentRoute []City, cutIndex int32) {
  39. [email protected] @BWKnopper github.com/bknopper /** * Check the rest of the

    route in the crossing parent and add the cities that are not yet in the child * (in the order of the route of the crossing parent) */ func (candidateSolution *CandidateSolution) crossFill(childRoute Cities, parentRoute []City, cutIndex int32) { /* * traverse the parent route from the cut index on and add every city * not yet in the child to the child */ childRouteIndex := cutIndex for i := cutIndex; i < int32(len(parentRoute)); i++ { nextCityOnRoute := parentRoute[i] if (!childRoute.contains(nextCityOnRoute)) { childRoute[childRouteIndex] = nextCityOnRoute childRouteIndex++ } } 1 4 2 5 6 3 1
  40. [email protected] @BWKnopper github.com/bknopper /** * Check the rest of the

    route in the crossing parent and add the cities that are not yet in the child * (in the order of the route of the crossing parent) */ func (candidateSolution *CandidateSolution) crossFill(childRoute Cities, parentRoute []City, cutIndex int32) { /* * traverse the parent route from the cut index on and add every city * not yet in the child to the child */ childRouteIndex := cutIndex for i := cutIndex; i < int32(len(parentRoute)); i++ { nextCityOnRoute := parentRoute[i] if (!childRoute.contains(nextCityOnRoute)) { childRoute[childRouteIndex] = nextCityOnRoute childRouteIndex++ } } /* * traverse the parent route from the start of the route and add every * city not yet in the child to the child */ for i := 0; i < int(cutIndex); i++ { nextCityOnRoute := parentRoute[i] if (!childRoute.contains(nextCityOnRoute)) { childRoute[childRouteIndex] = nextCityOnRoute childRouteIndex++ } } } 1 4 2 5 6 3 1 1 4 2 5 6 3 1
  41. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; 2 RECOMBINE pairs of parents; }
  42. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; 2 RECOMBINE pairs of parents; 3 MUTATE the resulting offspring; }
  43. [email protected] @BWKnopper github.com/bknopper 1 6 5 3 4 2 1

    Mutation • Change of nr won’t work • Infeasiblecandidate solution • Swap to the rescue! 1 6 5 3 4 2 1 1 2 5 3 4 6 1
  44. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) mutate() { /* randomly

    select two indices in the route */ indexFirstCity := int32(rand.Intn(len(candidateSolution.VisitingCities))) indexSecondCity := int32(rand.Intn(len(candidateSolution.VisitingCities))) 1 6 5 3 4 2 1 1 6 5 3 4 2 1
  45. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) mutate() { /* randomly

    select two indices in the route */ indexFirstCity := int32(rand.Intn(len(candidateSolution.VisitingCities))) indexSecondCity := int32(rand.Intn(len(candidateSolution.VisitingCities))) /* Make sure they are different */ for (indexFirstCity == indexSecondCity) { indexSecondCity = int32(rand.Intn(len(candidateSolution.VisitingCities))) } 1 6 5 3 4 2 1 1 6 5 3 4 2 1
  46. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) mutate() { /* randomly

    select two indices in the route */ indexFirstCity := int32(rand.Intn(len(candidateSolution.VisitingCities))) indexSecondCity := int32(rand.Intn(len(candidateSolution.VisitingCities))) /* Make sure they are different */ for (indexFirstCity == indexSecondCity) { indexSecondCity = int32(rand.Intn(len(candidateSolution.VisitingCities))) } /* Retrieve the cities */ firstCity := candidateSolution.VisitingCities[indexFirstCity] secondCity := candidateSolution.VisitingCities[indexSecondCity] 1 6 5 3 4 2 1 1 6 5 3 4 2 1
  47. [email protected] @BWKnopper github.com/bknopper func (candidateSolution *CandidateSolution) mutate() { /* randomly

    select two indices in the route */ indexFirstCity := int32(rand.Intn(len(candidateSolution.VisitingCities))) indexSecondCity := int32(rand.Intn(len(candidateSolution.VisitingCities))) /* Make sure they are different */ for (indexFirstCity == indexSecondCity) { indexSecondCity = int32(rand.Intn(len(candidateSolution.VisitingCities))) } /* Retrieve the cities */ firstCity := candidateSolution.VisitingCities[indexFirstCity] secondCity := candidateSolution.VisitingCities[indexSecondCity] /* Changer! */ candidateSolution.VisitingCities[indexFirstCity] = secondCity candidateSolution.VisitingCities[indexFirstCity] = firstCity } 1 2 5 3 4 6 1 1 6 5 3 4 2 1 1 6 5 3 4 2 1
  48. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; 2 RECOMBINE pairs of parents; 3 MUTATE the resulting offspring; }
  49. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; 2 RECOMBINE pairs of parents; 3 MUTATE the resulting offspring; 4 EVALUATE new candidates; }
  50. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; 2 RECOMBINE pairs of parents; 3 MUTATE the resulting offspring; 4 EVALUATE new candidates; 5 SELECT individuals for the next generation; }
  51. [email protected] @BWKnopper github.com/bknopper /** * Selects the survivors by removing

    the worst candidate * solutions from the list, so we have the original * population size again */ func (algorithm *Algorithm) selectSurvivors() { }
  52. [email protected] @BWKnopper github.com/bknopper /** * Selects the survivors by removing

    the worst candidate * solutions from the list, so we have the original * population size again */ func (algorithm *Algorithm) selectSurvivors() { sort.Sort(algorithm.population) }
  53. [email protected] @BWKnopper github.com/bknopper /** * Selects the survivors by removing

    the worst candidate * solutions from the list, so we have the original * population size again */ func (algorithm *Algorithm) selectSurvivors() { sort.Sort(algorithm.population) algorithm.population = algorithm.population[:algorithm.populationSize] }
  54. [email protected] @BWKnopper github.com/bknopper EA Algorithm (Pseudocode) INITIALISE population with random

    candidate solutions; EVALUATE each candidate; WHILE ( TERMINATION CONDITION is not satisfied ) { 1 SELECT parents; 2 RECOMBINE pairs of parents; 3 MUTATE the resulting offspring; 4 EVALUATE new candidates; 5 SELECT individuals for the next generation; }
  55. [email protected] @BWKnopper github.com/bknopper Tuning… • Mutation probability • Population size

    • Nr of offspring • Termination condition (# runs or fitness) • Parent selection • Survival selection • Initialisation • Random
  56. [email protected] @BWKnopper github.com/bknopper With great power comes great responsibility •

    I’m sure I cannot find a solution using a brute-force approach? • (within a reasonable amountof time) • Am I facing an optimization or search problem? • Can I encode a candidate solution to the problem? • Representationpossible? • Can I determine the fitness of a candidate solution?
  57. [email protected] @BWKnopper github.com/bknopper Additional questions? • Contact me on @BWKnopper

    • Google it! • There’slotstofind… • Papers • Demo’s