Skip to content
master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
src
 
 
 
 
 
 
 
 
 
 

Helisa

Latest 2.12 version

What is it?

Helisa (HEL-EE-SAH) is a Scala frontend for solving problems with genetic algorithms and genetic programming.

It’s pretty much a Scala wrapper around the excellent jenetics library [1].

How to use it

Obtaining

helisa is available on Maven central through:

"com.softwaremill" %% "helisa" % "0.8.0"

Currently only Scala 2.12 is supported.

Basic usage

The two things that are absolutely required are:

  1. A genotype, i.e. a representation of possible solutions to the problem,

  2. a fitness function, that scores the solutions generated from the genotypes.

Using an example for guessing a number between 0 and a 100, you would have:

import com.softwaremill.helisa._

case class Guess(num: Int) (1)

val genotype =
  () => genotypes.uniform(chromosomes.int(0, 100)) (2)
def fitness(toGuess: Int) =
  (guess: Guess) => 1.0 / (guess.num - toGuess).abs (3)
  1. The representation of a solution to the problem (the phenotype)

  2. A producer of genotypes.

  3. The fitness function - the closer to the target number, the higher the fitness score.

We use the code above to set up the Evolver, which encapsulates all configuration and generates fresh population streams:

val evolver =
  Evolver(fitness(Number), genotype) (1)
    .populationSize(100) (2)
    .build()

val stream = evolver.streamScalaStdLib() (3)

val best = stream.drop(1000).head.bestPhenotype (4)

println(best)
// Some(Guess(42))
  1. Initialize the Evolver with our genotype and fitness function.

  2. Set the population size.

  3. Obtain a Stream population stream (see Integrating for more information)

  4. Advance the stream and obtain the highest-scored phenotype.

Validation

You can additionally restrict the solution space by adding a phenotype validator:

val evolver =
  Evolver(fitness(Number), genotype)
    .populationSize(100)
    .phenotypeValidator(_.num % 2 == 0) (1)
    .build()
  1. We know the number is even, so we’re restricting possible solutions to only those numbers.

Operators

As a reminder, the three main elements of evolution in genetic algorithms are:

  • the selection of fittest individuals (phenotypes),

  • the recombination of selected individuals to form new individuals in the next generation of the population,

  • the mutation of the new/remaining individuals.

Selection

Standard selectors are available from helisa.selectors, you use them like this:

import com.softwaremill.helisa._

val evolver =
  Evolver(fitnessFunction, genotype)
   .offspringSelector(selectors.x) (1)
   .survivorsSelector(selectors.y) (2)
    .build()
  1. Affect just the survivors.

  2. Affects both the survivors and offspring.

You can also add a custom selector by passing the appropriate function to the survivorSelectorAsFunction or offspringSelectorAsFunction method.

Recombination and mutation

Recombination and mutation is handled are both generalized to operators, available in helisa.operators . You use them as follows:

import com.softwaremill.helisa._

val evolver =
  Evolver(fitnessFunction, genotype)
   .geneticOperators(operators.crossover.x, (1)
   operators.mutation.y) (2)
    .build()
  1. Recombination operators.

  2. Mutation operators.

You can also add a custom operator by passing the appropriate function to the geneticOperatorsAsFunctions method.

Other configuration

See the doc of EvolverBuilder for all Evolver configuration options.

Integrating

Integrations for:

  • scala.collection.Iterator

  • scala.collection.Stream

  • Akka Stream Source

  • FS2 Stream for any Async

  • Reactive Streams Publisher

In addition:

  • Monix is not supported directly, but can be taken advantage with using the other integrations,

  • Spark integration is coming up.

Genetic programming

TBD