Skip to content

r8vnhill/keen

Repository files navigation

Keen | Kotlin Evolutionary Computation Framework

Maven Central License

Keen logo

Keen is a framework for Kotlin that leverages evolutionary algorithms to solve optimization problems. It harnesses the power and expressiveness of Kotlin to provide an intuitive and efficient interface for researchers, scientists, and developers alike. With Keen, you can build and fine-tune complex evolutionary algorithms with just a few lines of code.

Whether you are delving into genetic algorithms for the first time or are an experienced researcher looking for a Kotlin-native tool, Keen offers a streamlined experience without sacrificing depth or flexibility. Its modular architecture means you can easily extend or modify components, from selection and crossover methods to mutation and fitness evaluation.

Table of Contents

Installation

This section guides you through the process of setting up the Keen framework and the EvolutionPlotter in your Kotlin project using Gradle Kotlin DSL.

Gradle Kotlin DSL Setup

Step 1: Specify Versions in gradle.properties

First, define the versions of Keen and Compose in your gradle.properties file. Make sure to replace these with the latest versions available.

# gradle.properties
# Keen framework version. Replace with the latest version.
keen.version=1.1.0
# Compose version for the EvolutionPlotter. Replace at your discretion.
compose.version=1.5.11

Step 2: Configure Plugin Management in settings.gradle.kts

This step is essential only if you plan to use the EvolutionPlotter. Here, you configure the plugin management for the Compose plugin.

// settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()  // Standard Gradle plugin repository.
        maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")  // Repository for JetBrains Compose.
        google()  // Google's Maven repository, sometimes needed for dependencies.
    }

    plugins {
        id("org.jetbrains.compose") version extra["compose.version"] as String  // Apply the Compose plugin with the specified version.
    }
}

Step 3: Configure Project Plugins, Repositories, and Dependencies

In your build script, configure the necessary plugins, repositories, and dependencies.

val keenVersion: String = extra["keen.version"] as String  // Retrieve the Keen version defined earlier.

plugins {
    /* ... */
    id("org.jetbrains.compose") // Include this only if using the EvolutionPlotter.
}

repositories {
    mavenCentral()  // Maven Central repository for most dependencies.
    /* ... */
}

dependencies {
    implementation("cl.ravenhill:keen-core:$keenVersion")  // Keen core library dependency.
    implementation(compose.desktop.currentOs)   // Compose dependency, required for the EvolutionPlotter.
    /* ... */
}

Additional Notes:

  • Ensure that the versions specified in gradle.properties are compatible with your project setup.
  • The pluginManagement block in settings.gradle.kts is crucial for resolving the Compose plugin, especially if you're using features like the EvolutionPlotter.
  • Remember to sync your Gradle project after making changes to these files to apply the configurations.

Examples

The following examples only have the explanation of what's the problem to solve. You can find the explanations of the code in the manual (WIP).

One Max (Ones Counting) Problem

The One Max problem is a well known problem widely used to exemplify the genetic algorithms' capabilities. The problem consists of maximizing the number of ones in a bit string. The following example shows how to solve the One Max problem with Keen.

Implementation

import cl.ravenhill.keen.ExperimentalKeen
import cl.ravenhill.keen.dsl.booleans
import cl.ravenhill.keen.dsl.chromosomeOf
import cl.ravenhill.keen.dsl.evolutionEngine
import cl.ravenhill.keen.dsl.genotypeOf
import cl.ravenhill.keen.genetic.Genotype
import cl.ravenhill.keen.genetic.genes.BooleanGene
import cl.ravenhill.keen.limits.MaxGenerations
import cl.ravenhill.keen.limits.TargetFitness
import cl.ravenhill.keen.listeners.plotter.EvolutionPlotter
import cl.ravenhill.keen.listeners.summary.EvolutionSummary
import cl.ravenhill.keen.operators.alteration.crossover.SinglePointCrossover
import cl.ravenhill.keen.operators.alteration.crossover.UniformCrossover
import cl.ravenhill.keen.operators.alteration.mutation.BitFlipMutator
import cl.ravenhill.keen.operators.selection.RouletteWheelSelector
import cl.ravenhill.keen.operators.selection.TournamentSelector

private fun count(genotype: Genotype<Boolean, BooleanGene>) = genotype.flatten().count { it }.toDouble()

fun main() {
  val engine = evolutionEngine(::count, genotypeOf {
    chromosomeOf {
      booleans {
        size = 50
        trueRate = 0.15
      }
    }
  }) {
    populationSize = 500
    parentSelector = RouletteWheelSelector()
    survivorSelector = TournamentSelector()
    alterers += listOf(BitFlipMutator(individualRate = 0.5), UniformCrossover(chromosomeRate = 0.6))
    limits += listOf(MaxGenerations(500), TargetFitness(TARGET_FITNESS))
    listeners += listOf(EvolutionSummary(), EvolutionPlotter())
  }
  engine.evolve()
  engine.listeners.forEach { it.display() }
}

Output

------------ Evolution Summary ---------------
|--> Initialization time: 16 ms
------------- Evaluation Times ----------------
|--> Average: 0.23076923076923078 ms
|--> Max: 1 ms
|--> Min: 0 ms
-------------- Selection Times ----------------
|   |--> Offspring Selection
|   |   |--> Average: 0.6153846153846154 ms
|   |   |--> Max: 8 ms
|   |   |--> Min: 0 ms
|   |--> Survivor Selection
|   |   |--> Average: 0.0 ms
|   |   |--> Max: 0 ms
|   |   |--> Min: 0 ms
--------------- Alteration Times --------------
|--> Average: 3.3846153846153846 ms
|--> Max: 19 ms
|--> Min: 1 ms
-------------- Evolution Results --------------
|--> Total time: 113 ms
|--> Average generation time: 7.3076923076923075 ms
|--> Max generation time: 57 ms
|--> Min generation time: 2 ms
|--> Generation: 13
|--> Steady generations: 0
|--> Fittest: [1111 1111 1111 1111 1111]
|--> Best fitness: 20.0

One Max Fitness Plot

Acknowledgements

This project has benefited from the support provided by JetBrains. We're immensely grateful for their backing.

JetBrains Logo (Main) logo.

A big thank you to JetBrains for their open-source support.