GithubHelp home page GithubHelp logo

genetic's Introduction

Package genetic

Package genetic implements genetic algorithms using Golang's Generics support.

What are Genetic Algorithms?

Genetic algorithms are a form of machine learning software inspired by the process of natural selection. GA's can quickly approximate solutions for problems with very large search spaces. They are often used to tackle NP problems whose solutions can vary in levels of quality. In these kinds of problems, measuring a solution's validity and quality are fast, but yet there is no easy way to determine the optimal solution without guessing and checking. Genetic Algorithms provide a means to get a pretty good solution to any such problem. Examples include:

  • Transportation routing & logistics
  • Storage Optimization (Knapsack Problem)
  • 'Travelling Salesman' problems
  • Machine Learning meta-parameter tuning
  • Hardware and Engineering Design optimization
  • Scheduling

GAs work by iteratively evolving a finite pool of possible solutions to your problem. With each iteration, the best solutions are mixed and matched with one another to produce novel solutions, and then mutated to introduce variety. This process repeats until some termination condition is reached, at which point the algorithm terminates and returns the best found solution.

Word Thingies

Terminology that's important to know before using Genetic Algorithms:

  • Genome: One possible evolutionary candidate. AKA "Solution".
  • Population: A pool of genomes.
  • Fitness: A scoring metric which objectively ranks the quality of any genome.
  • Crossover: The process of combining information from parent genomes to produce novel child genomes.
  • Mutations: Random variations in genome structure or content occurring during crossover.
  • Selection: The process of picking which genomes will crossover to produce the next generation of genomes.
  • Elitism: Preferential treatment to the highest-scoring genomes, allowing them to survive into the next generation. This prevents regression and loss of quality solutions.
  • Allele: A candidate gene for a specific location on the Genome. If two genes are alleles of each other, they are evolutionary competitors.

Usage

Find a full example in example_test.go.

Let's say we want to guess a 33-character string, "how did you ever guess my secret!". Our goal is to evolve a population of random strings into the target string.

Thanks to Generics, genetic can be unopinionated about the data type you use to represent your genomes. Instead, you define several functions acting on those genomes, which in concert will define the evolutionary behavior of a population. In our example, we will store our genomes as byte slices.

We need a GenesisFunc[T] which initializes and returns random genomes of type T, where in our case T is []byte.

func genesis() []byte {
  genome := make([]byte, 33)
  rand.Read(genome)
  return genome
}

We need a FitnessFunc[T] which scores every genome on how many of its bytes match the target string. We'll use a minimum fitness of 1, which means our maximum fitness is 34.

func fitnessFn(guesses [][]byte, fitnesses []int) {
  for i, guess := range guesses {
    fitnesses[i] = 1
    for j, b := range guess {
      if b == bytesToGuess[j] {
        fitnesses[i]++
      }
    }
  }
}

Since this is a static fitness function (its output is not dependent on the other genomes in the population), we can optimize this slightly with the StaticFitnessFunc utility.

fitnessFn := genetic.StaticFitnessFunc(func(guess []byte) int {
  fitness := 1
  for j, b := range guess {
    if b == bytesToGuess[j] {
      fitness++
    }
  }
  return fitness
})

We need a CrossoverFunc[T] which combines two genomes together to produce offspring. genetic provides several simple crossover functions which work on any generic slice type genome. Let's use UniformCrossover, which produces offspring whose genomes are a random mix of their parents' genomes.

crossover := genetic.UniformCrossover[[]byte]

We need a SelectionFunc[T] which will pair genomes off into mating pairs, usually based on their fitnesses. A good selection function should give higher-fitness genomes more opportunities to mate than lower-fitness genomes, but should also ensure that the elite (highest-fitness) genomes do not completely dominate all mating pairs, as genetic homogeneity can lead to evolutionary stagnation.

genetic also provides a couple of classic selection functions which work with any genome type. Let's use TournamentSelection, with a tournament size of 3 - randomly selected genomes will be repeatedly subjected to 'tournaments' where the highest-fitness entrants will become mating candidates.

selection := genetic.TournamentSelection[[]byte](3)

Finally, we need a MutationFunc[T] which should randomly (usually with some small probability) mutate the genomes of each new generation. This injects some diversity, helping to explore more optimal solutions.

func mutate(guess []byte) {
  r := make([]byte, len(guess))
  rand.Read(r)
  for i := range guess {
    if rand.Float64() < 0.05 { // 5% mutation rate
      guess[i] ^= r[i]
    }
  }
}

Now we can construct a Population[[]byte] instance:

population := genetic.NewPopulation(
  100, // population size
  genesis,
  crossover,
  fitness,
  selection,
  mutation,
)

This population now contains 100 randomized 33-byte strings. Let's try to evolve them into our target string based solely on how many bytes they guessed correctly.

population.Evolve(
  34,    // minimum fitness. Evolution terminates upon finding a solution with this fitness or higher.
  2000,  // max generations. Evolution terminates after iterating through this many generations.
  2,     // elitism. Carries over this many of the highest-fitness genomes from each previous generation into the next.
)

We can now pull out our best candidate to see how we did.

bestSolution, bestFitness := population.Best()
fmt.Printf("evolved string: %q\n", bestSolution)
fmt.Println("best fitness:", bestFitness)

If we did everything right, we should see our population evolved into our target string and reached maximum fitness 34:

evolved string: "how did you ever guess my secret!"
best fitness: 34

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.