See the wikipedia page on Genetic Algorithms

Example

Consider a Rabbit class

case class Rabbit(a: Int, b: Double, c: Double, d: Double, e: Double, f: Double, g: Double, h: Double)

Define the Species for a Genetic Algorithm, which requires a random generator and a fitness function.

import shapeless._

val gen = Generic[Rabbit]
// gen: Generic[Rabbit]{type Repr = Int :: Double :: Double :: Double :: Double :: Double :: Double :: Double :: shapeless.HNil} = shapeless.Generic$$anon$1@172f9322

import axle.ml._

import scala.util.Random.nextDouble
import scala.util.Random.nextInt

implicit val rabbitSpecies = new Species[gen.Repr] {

  def random(rg: spire.random.Generator): gen.Repr = {

    val rabbit = Rabbit(
      1 + nextInt(2),
      5 + 20 * nextDouble(),
      1 + 4 * nextDouble(),
      3 + 10 * nextDouble(),
      10 + 5 * nextDouble(),
      2 + 2 * nextDouble(),
      3 + 5 * nextDouble(),
      2 + 10 * nextDouble())
    gen.to(rabbit)
  }

  def fitness(rg: gen.Repr): Double = {
    val rabbit = gen.from(rg)
    import rabbit._
    a * 100 + 100.0 * b + 2.2 * (1.1 * c + 0.3 * d) + 1.3 * (1.4 * e - 3.1 * f + 1.3 * g) - 1.4 * h
  }

}
// rabbitSpecies: AnyRef with Species[gen.Repr] = repl.MdocSession$App$$anon$1@7e95f5

Run the genetic algorithm

import cats.implicits._

val ga = GeneticAlgorithm(populationSize = 100, numGenerations = 100)
// ga: GeneticAlgorithm[gen.Repr, ops.hlist.Mapper.<refinement>.this.type.Out] = GeneticAlgorithm(
//   populationSize = 100,
//   numGenerations = 100
// )

val log = ga.run(spire.random.Generator.rng)
// log: GeneticAlgorithmLog[gen.Repr] = GeneticAlgorithmLog(
//   winners = Vector(
//     2 :: 20.385950887947182 :: 4.910445709809158 :: 12.12435887675269 :: 14.651432690293163 :: 2.168451656811953 :: 3.916827807968075 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.12435887675269 :: 13.010357296919523 :: 2.168451656811953 :: 7.9024554192576995 :: 2.085801574125962 :: HNil,
//     1 :: 21.899194313153792 :: 4.910445709809158 :: 12.12435887675269 :: 14.651432690293163 :: 2.0646753968534672 :: 7.972108590202686 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.58669084198368 :: 14.651432690293163 :: 2.168451656811953 :: 7.809450200958542 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 4.374838179208588 :: 14.651432690293163 :: 2.168451656811953 :: 7.972108590202686 :: 6.654040836410464 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.12435887675269 :: 14.651432690293163 :: 2.65526205457222 :: 7.9024554192576995 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.76974023564594 :: 12.58669084198368 :: 14.363331586910709 :: 2.0646753968534672 :: 7.809450200958542 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.12435887675269 :: 14.651432690293163 :: 2.168451656811953 :: 7.9024554192576995 :: 6.551081255904378 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.58669084198368 :: 14.651432690293163 :: 3.0427644953241417 :: 7.9024554192576995 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.12435887675269 :: 14.651432690293163 :: 2.168451656811953 :: 6.710663971974638 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.12435887675269 :: 14.651432690293163 :: 2.168451656811953 :: 5.539143365145807 :: 6.8955369948822085 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.58669084198368 :: 14.651432690293163 :: 2.168451656811953 :: 7.972108590202686 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.58669084198368 :: 14.651432690293163 :: 2.168451656811953 :: 4.065425957777272 :: 3.59208157122551 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 11.873914810978517 :: 14.651432690293163 :: 2.168451656811953 :: 7.972108590202686 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 12.12435887675269 :: 13.644740142467557 :: 2.168451656811953 :: 7.972108590202686 :: 2.085801574125962 :: HNil,
//     2 :: 24.71394131419272 :: 4.910445709809158 :: 11.873914810978517 :: 14.651432690293163 :: 2.168451656811953 :: 7.181637294738287 :: 7.64743918318245 :: HNil...

val winner = log.winners.last
// winner: gen.Repr = 2 :: 24.71394131419272 :: 4.910445709809158 :: 12.58669084198368 :: 14.651432690293163 :: 2.168451656811953 :: 7.9024554192576995 :: 2.085801574125962 :: HNil

Plot the min, average, and max fitness function by generation

import scala.collection.immutable.TreeMap
import axle.visualize._

val plot = Plot[String, Int, Double, TreeMap[Int,Double]](
  () => List("min" -> log.mins, "ave" -> log.aves, "max" -> log.maxs),
  connect = true,
  colorOf = (label: String) => label match {
    case "min" => Color.black
    case "ave" => Color.blue
    case "max" => Color.green },
  title = Some("GA Demo"),
  xAxis = Some(0d),
  xAxisLabel = Some("generation"),
  yAxis = Some(0),
  yAxisLabel = Some("fitness"))
// plot: Plot[String, Int, Double, TreeMap[Int, Double]] = Plot(
//   dataFn = <function0>,
//   connect = true,
//   drawKey = true,
//   width = 700,
//   height = 600,
//   border = 50,
//   pointDiameter = 4,
//   keyLeftPadding = 20,
//   keyTopPadding = 50,
//   keyWidth = 80,
//   fontName = "Courier New",
//   fontSize = 12,
//   bold = false,
//   titleFontName = "Palatino",
//   titleFontSize = 20,
//   colorOf = <function1>,
//   title = Some(value = "GA Demo"),
//   keyTitle = None,
//   xAxis = Some(value = 0.0),
//   xAxisLabel = Some(value = "generation"),
//   yAxis = Some(value = 0),
//   yAxisLabel = Some(value = "fitness")
// )

Render to an SVG file

import axle.web._
import cats.effect._

plot.svg[IO]("ga.svg").unsafeRunSync()

ga