Skip to content
This repository has been archived by the owner on Apr 27, 2023. It is now read-only.

Commit

Permalink
First version of the API, incl/ demo.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikolak-net committed May 22, 2018
1 parent df29b95 commit 2455d44
Show file tree
Hide file tree
Showing 8 changed files with 458 additions and 0 deletions.
65 changes: 65 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Created by .ignore support plugin (hsz.mobi)
### SBT template
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control

dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
.history
.cache
### Java template
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.war
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff:
.idea

## File-based project format:
*.iws

## Plugin-specific files:

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Scala template
30 changes: 30 additions & 0 deletions src/main/scala/com/softwaremill/helisa/EvolutionResult.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.softwaremill.helisa

import com.softwaremill.helisa.api.convert.Decoder
import io.{jenetics => j}

class EvolutionResult[A: Decoder[?, G], G <: Gene[_, G], FRC <: Comparable[FRC]](val jResult: j.engine.EvolutionResult[G, FRC]) {

def optimize = jResult.getOptimize

def population = jResult.getPopulation.asScala.flatMap(_.decode[A].toSeq)

def bestPhenotype: Option[A] = jResult.getBestPhenotype.decode

def worstPhenotype: Option[A] = jResult.getWorstPhenotype.decode

def generation: Long = jResult.getGeneration

def totalGenerations: Long = jResult.getTotalGenerations

def killCount = jResult.getKillCount

def altererCount = jResult.getAlterCount

def invalidCount = jResult.getInvalidCount

def bestFitness = jResult.getBestFitness

def worstFitness = jResult.getWorstFitness

}
90 changes: 90 additions & 0 deletions src/main/scala/com/softwaremill/helisa/Evolver.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.softwaremill.helisa

import com.softwaremill.helisa.api.convert.{CodecBuilder, Decoder}
import io.{jenetics => j}

import scala.collection.JavaConverters._
import scala.concurrent.ExecutionContext

object Evolver {

def apply[A, G <: Gene[_, G], @specialized(Int, Double, Float, Long) FitnessResult](fitnessFunction: A => FitnessResult,
genotype: () => Genotype[G])(
implicit decoder: Decoder[A, G],
evComp: FitnessResultComparable[FitnessResult]): EvolverBuilder[A, G, evComp.FitnessResultC] = {

import java.util.function._
import scala.compat.java8.FunctionConverters._

val codec: j.engine.Codec[A, G] = CodecBuilder.codecFor[A](genotype)
new EvolverBuilder(j.engine.Engine.builder(fitnessFunction.andThen(evComp(_)).asJava, codec))
}

}

class Evolver[A: Decoder[?, G], G <: Gene[_, G], FRC <: Comparable[FRC]](val jEngine: j.engine.Engine[G, FRC]) {

def stream(): Stream[EvolutionResult[A, G, FRC]] = jEngine.stream().iterator().asScala.map(new EvolutionResult(_)).toStream

}

class EvolverBuilder[A: Decoder[?, G], G <: Gene[_, G], FRC <: Comparable[FRC]] private[helisa] (
private val jBuilder: j.engine.Engine.Builder[G, FRC]) {
import scala.compat.java8.FunctionConverters._

private def modifyBuilder(mod: j.engine.Engine.Builder[G, FRC] => Unit): EvolverBuilder[A, G, FRC] = {
mod(jBuilder)
this
}

def fitnessScaler(scaler: FRC => FRC) = modifyBuilder(_.fitnessScaler(scaler.asJava))

def offspringSelector(selector: Selector[G, FRC]) = modifyBuilder(_.offspringSelector(selector.asJava))

def offspringSelector(selector: j.Selector[G, FRC]) = modifyBuilder(_.offspringSelector(selector))

def survivorsSelector(selector: Selector[G, FRC]) = modifyBuilder(_.survivorsSelector(selector.asJava))

def survivorsSelector(selector: j.Selector[G, FRC]) = modifyBuilder(_.survivorsSelector(selector))

def sSelector(selector: Selector[G, FRC]) = modifyBuilder(_.selector(selector.asJava))

def selector(selector: j.Selector[G, FRC]) = modifyBuilder(_.selector(selector))

def alterers(alterer1: Alterer[G, FRC], rest: Alterer[G, FRC]*) =
modifyBuilder(_.alterers(alterer1.asJava, rest.map(_.asJava): _*))

def alterers(alterer1: j.Alterer[G, FRC], rest: j.Alterer[G, FRC]*) = modifyBuilder(_.alterers(alterer1, rest: _*))

def phenotypeValidator(validator: A => Boolean) =
modifyBuilder(_.phenotypeValidator(_.getGenotype.decode[A].exists(validator)))

def genotypeValidator(validator: Genotype[G] => Boolean) = modifyBuilder(_.genotypeValidator(validator.asJava))

def optimize(optimize: Optimize) = modifyBuilder(_.optimize(optimize))

def maximizing() = modifyBuilder(_.maximizing())

def minimizing() = modifyBuilder(_.minimizing())

def offspringFraction(fraction: Double) = modifyBuilder(_.offspringFraction(fraction))

def survivorsFraction(fraction: Double) = modifyBuilder(_.survivorsFraction(fraction))

def survivorsSize(size: Int) = modifyBuilder(_.survivorsSize(size))

def offspringSize(size: Int) = modifyBuilder(_.offspringSize(size))

def populationSize(size: Int) = modifyBuilder(_.populationSize(size))

def maximalPhenotypeAge(age: Long) = modifyBuilder(_.maximalPhenotypeAge(age))

def executor(ec: ExecutionContext) = modifyBuilder(_.executor(ec.execute(_)))

def clock(clock: java.time.Clock) = modifyBuilder(_.clock(clock))

def individualCreationRetries(retries: Int) = modifyBuilder(_.individualCreationRetries(retries))

def build(): Evolver[A, G, FRC] = new Evolver(jBuilder.build())

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.softwaremill.helisa

import scala.annotation.implicitNotFound

@implicitNotFound("""Comparable to {FitnessResult} not found. The result of your fitness function should either:
1. Be a primitive (Int/Long/Double/Float).
2. Implement java.lang.Comparable.""")
trait FitnessResultComparable[FitnessResult] {
type FitnessResultC <: Comparable[FitnessResultC]
def apply(c: FitnessResult): FitnessResultC
}

object FitnessResultComparable {
implicit def fitnessResultComparable[_FitnessResultC <: Comparable[_FitnessResultC]]: FitnessResultComparable[_FitnessResultC] =
new FitnessResultComparable[_FitnessResultC] {
type FitnessResultC = _FitnessResultC
def apply(c: _FitnessResultC): _FitnessResultC = c
}

implicit object DoubleFitnessResultComparable extends FitnessResultComparable[Double] {
type FitnessResultC = java.lang.Double
def apply(c: Double): java.lang.Double = c
}

implicit object FloatFitnessResultComparable extends FitnessResultComparable[Float] {
type FitnessResultC = java.lang.Float
def apply(c: Float): java.lang.Float = c
}

implicit object IntegerFitnessResultComparable extends FitnessResultComparable[Int] {
type FitnessResultC = java.lang.Integer
def apply(c: Int): java.lang.Integer = c
}

implicit object LongFitnessResultComparable extends FitnessResultComparable[Long] {
type FitnessResultC = java.lang.Long

def apply(c: Long): java.lang.Long = c
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.softwaremill.helisa.api.convert

import java.util.function
import io.{jenetics => j}
import com.softwaremill.helisa.Gene

abstract class CodecBuilder[A] {
def apply[G <: Gene[_, G]](genotype: () => j.Genotype[G])(implicit d: Decoder[A, G]): j.engine.Codec[A, G]
}

object CodecBuilder {
def codecFor[A] = new CodecBuilder[A] {
def apply[G <: Gene[_, G]](genotype: () => j.Genotype[G])(implicit d: Decoder[A, G]) = new j.engine.Codec[A, G] {
def encoding(): j.util.Factory[j.Genotype[G]] = () => genotype()

def decoder(): function.Function[j.Genotype[G], A] =
(jGenotype: j.Genotype[G]) =>
d.decode(jGenotype).getOrElse(throw new IllegalStateException(s"Can't convert this genotype: $jGenotype"))
}
}
}
35 changes: 35 additions & 0 deletions src/main/scala/com/softwaremill/helisa/api/convert/Decoder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.softwaremill.helisa.api.convert

import com.softwaremill.helisa.Gene
import io.{jenetics => j}
import shapeless.ops.hlist.ToTraversable
import shapeless.ops.traversable.FromTraversable
import shapeless.{Generic, HList}

import scala.annotation.implicitNotFound
import scala.collection.JavaConverters._

@implicitNotFound(msg = """Decoder for ${A} , ${G} not found.
For automatic generation, you need these things:
1. *All* fields of the case class you use *must* be compatible with possible Gene values.
2. import com.softwaremill.helisa._
""")
trait Decoder[A, G <: Gene[_, G]] {

def decode(genotype: j.Genotype[G]): Option[A]

}

object Decoder {

implicit def caseClassDecoder[A, G <: Gene[_, G], Repr <: HList](implicit g: Generic.Aux[A, Repr],
tT: ToTraversable.Aux[Repr, Vector, _],
fT: FromTraversable[Repr]): Decoder[A, G] =
(genotype: j.Genotype[G]) => {
import shapeless.syntax.std.traversable._
val repr = genotype.iterator().asScala.map(_.getGene.getAllele).toTraversable.toHList[Repr]
repr.map(g.from)
}

}
Loading

0 comments on commit 2455d44

Please sign in to comment.