algodesigner-genetic is a straightforward and easy-to-use Genetic Programming (GP) library designed for Java developers. With a focus on simplicity and clarity, it provides a minimalist framework for implementing GP algorithms without any external dependencies.
This library is ideal for educational purposes, quick prototyping, or as a foundation for more advanced genetic algorithm applications. Its intuitive design makes it accessible for both beginners and experienced developers.
- Features
- Installation
- Quick Start
- Core Concepts
- Customising Evolution
- API Overview
- Building from Source
- Running Tests
- Contributing
- License
- Zero dependencies - Pure Java implementation
- Simple API - Easy to learn and use
- Flexible configuration - Customisable crossover, mutation, and selection strategies
- Extensible design - Implement your own fitness functions and strategies
- Educational focus - Clear, readable code with comprehensive test examples
- Dual evolution engines - Both simple and composite engine implementations
Since this is a lightweight library with no dependencies, you can simply:
- Download the JAR: Build the project using Ant (see below) to generate
algodesigner-genetic-0.0.2.jar - Add to classpath: Include the JAR in your project's classpath
- Use the source: Copy the source files directly into your project
The recommended way to use this library is to build it from source using Ant:
# Clone the repository
git clone https://github.com/algodesigner/genetic.git
cd genetic
# Build the library (compiles, runs tests, creates JAR)
ant all
# The JAR will be created in the lib/ directory
# Use: lib/algodesigner-genetic-0.0.2.jarHere's a minimal example to get started:
import com.algodesigner.genetic.*;
// Create initial generation
GenerationBuilder builder = new GenerationBuilder();
for (int i = 0; i < 100; i++) {
builder.addChromosome("010101010101010101010101010101010101010101010101");
}
Generation initialGeneration = builder.build();
// Define fitness function
IFitnessFunction fitnessFunction = chromosome -> {
Gene[] genes = chromosome.getGenes();
int zeroCount = 0;
for (Gene gene : genes) {
if (((Character)gene.getValue()).charValue() == '0') {
zeroCount++;
}
}
return (double)zeroCount / genes.length;
};
// Create evolution engine
EvolutionEngine engine = new EvolutionEngine(
initialGeneration,
0.7, // crossover rate
0.5, // mutation rate
fitnessFunction,
true // elitism enabled
);
// Run evolution
TerminationCriteria criteria = new TerminationCriteria(3000, 10000);
engine.findSolution(1.0, criteria);
// Get results
System.out.println("Best fitness: " + engine.getBestFitnessScore());
System.out.println("Best chromosome: " + engine.getGeneration().getChromosome(engine.getBestIndex()));This example demonstrates a simple genetic algorithm where the fitness function promotes dominant genes (0 or 1) while ignoring "useless" genes (.):
// Fitness function that favours dominant genes
IFitnessFunction fitnessFunction = chromosome -> {
Gene[] genes = chromosome.getGenes();
int zeroCount = 0;
int oneCount = 0;
for (int i = 0; i < genes.length; i++) {
char c = ((Character)genes[i].getValue()).charValue();
if (c == '0')
zeroCount++;
else if (c == '1')
oneCount++;
}
return (double)Math.max(zeroCount, oneCount) / (double)genes.length;
};
// Create engine with initial population
EvolutionEngine engine = new EvolutionEngine(
createInitialGeneration(),
0.7, // crossover rate
0.5, // mutation rate
fitnessFunction,
true // elitism
);
// Run until perfect solution found or timeout
TerminationCriteria criteria = new TerminationCriteria(1000 * 60 * 3, 10000);
engine.findSolution(1.0, criteria);Sample Output:
Initial generation (mixed 0s, 1s, and .s) evolves to:
000000000000000000000000000000000000000000000000 1.0 <- Winner!
This example solves an optimisation problem to maximise area given perimeter constraints:
// Fitness function for maximising area with perimeter constraint
private static class FitnessFunction implements IFitnessFunction {
private final int maxPerimeter;
public FitnessFunction(int maxPerimeter) {
this.maxPerimeter = maxPerimeter;
}
@Override
public double evaluate(Chromosome chromosome) {
Gene[] genes = chromosome.getGenes();
int a = Integer.parseInt("" + genes[0].getValue() + genes[1].getValue());
int b = Integer.parseInt("" + genes[2].getValue() + genes[3].getValue());
int perimeter = 2 * (a + b);
if (perimeter > maxPerimeter)
return 0;
return a * b;
}
}
// Create engine with composite strategy (multiple populations)
CompositeEvolutionEngine engine = new CompositeEvolutionEngine(
createInitialGeneration(),
0.7, // crossover rate
0.5, // mutation rate
new FitnessFunction(200),
true, // elitism
3 // number of subpopulations
);
engine.findSolution(Double.MAX_VALUE, new TerminationCriteria(3000, 3000));This example solves a mathematical puzzle: find three distinct digits A, B, C such that CBA - ABC produces a valid result:
// Fitness function for subtraction problem
private static class FitnessFunction implements IFitnessFunction {
private static final int CHROMOSOME_LENGTH = 9;
@Override
public double evaluate(Chromosome chromosome) {
if (chromosome.length() != CHROMOSOME_LENGTH)
throw new IllegalArgumentException("Invalid chromosome length");
final String cs = chromosome.toString();
// Decode the first three digits (A, B, C)
int a = cs.charAt(0) - '0';
int b = cs.charAt(1) - '0';
int c = cs.charAt(2) - '0';
// Calculate CBA - ABC
int result = (c * 100 + b * 10 + a) - (a * 100 + b * 10 + c);
String resultStr = String.valueOf(result);
// Evaluate fitness based on multiple criteria
double c1 = resultStr.indexOf(cs.charAt(0)) != -1 ? 1 : 0; // A in result
double c2 = resultStr.indexOf(cs.charAt(1)) != -1 ? 1 : 0; // B in result
double c3 = resultStr.indexOf(cs.charAt(2)) != -1 ? 1 : 0; // C in result
double c4 = (a != b && b != c) ? 1 : 0; // All digits distinct
double c5 = result > 0 ? 1 : 0; // Positive result
return (c1 + c2 + c3 + c4 + c5) / 5; // Average of criteria
}
}
// Create evolution engine
EvolutionEngine engine = new EvolutionEngine(
createInitGeneration(),
0.7, // crossover rate
0.5, // mutation rate
new FitnessFunction(),
true // elitism
);
// Create initial generation
private static Generation createInitGeneration() {
GenerationBuilder builder = new GenerationBuilder();
// Create population of chromosomes with digits 1-9
builder.addChromosomes(256, "123456789");
return builder.build();
}
// Run evolution
engine.findSolution(1.0, new TerminationCriteria(3000, 3000));
// Display solution
Chromosome solution = engine.getGeneration().getChromosome(engine.getBestIndex());
System.out.println("Solution found in " + engine.getGenerationCount() + " generations:");
System.out.println("Chromosome: " + solution);Sample Solution:
CBA
- ABC
---
495
Where A=4, B=9, C=5 gives 594 - 459 = 495
The library provides several customisation points:
// Create engine with custom parameters
EvolutionEngine engine = new EvolutionEngine(
initialGeneration,
0.7, // crossover rate (0.0 to 1.0)
0.05, // mutation rate (0.0 to 1.0)
fitnessFunction,
true, // elitism (preserve best individuals)
new Random(12345) // optional random seed for reproducibility
);
// Configure termination criteria
TerminationCriteria criteria = new TerminationCriteria(
5000, // maximum time in milliseconds
10000 // maximum number of generations
);
// Find solution with target fitness
int solutionIndex = engine.findSolution(0.95, criteria);public class MyFitnessFunction implements IFitnessFunction {
@Override
public double evaluate(Chromosome chromosome) {
// Decode chromosome into solution representation
Gene[] genes = chromosome.getGenes();
// Calculate fitness (higher is better)
double fitness = 0.0;
// ... your fitness calculation logic
return fitness;
}
}The CompositeEvolutionEngine maintains multiple subpopulations for better diversity:
CompositeEvolutionEngine engine = new CompositeEvolutionEngine(
initialGeneration,
0.7, // crossover rate
0.5, // mutation rate
fitnessFunction,
true, // elitism
3 // number of subpopulations
);| Class | Purpose |
|---|---|
EvolutionEngine |
Main evolution engine with standard genetic algorithm |
CompositeEvolutionEngine |
Engine with multiple subpopulations for better diversity |
Chromosome |
Represents an individual solution with genes |
Gene |
Basic building block of a chromosome |
Generation |
Collection of chromosomes in a population |
GenerationBuilder |
Helper for creating initial generations |
TerminationCriteria |
Defines when evolution should stop (time/generations) |
DefaultSelector |
Default selection strategy (roulette wheel) |
| Interface | Purpose |
|---|---|
IEvolutionEngine |
Evolution engine contract |
IFitnessFunction |
Fitness evaluation contract |
ISelector |
Selection strategy contract |
ICrossoverStrategy |
Crossover strategy contract |
IMutationStrategy |
Mutation strategy contract |
- Java 8 or higher
- Apache Ant 1.9+
The project includes an Ant build file (build.xml) that handles everything:
# Build everything (compile, test, create JAR)
ant all
# Run tests only
ant test
# Clean all build artifacts
ant clean
# Generate Javadoc documentation
ant javadoc
# Just compile the source
ant compile
# Create JAR file (after compilation)
ant jar
# Run the example (requires jar target first)
ant runAfter running ant all, you'll get:
lib/algodesigner-genetic-0.0.2.jar- Main library JARlib/algodesigner-genetic-0.0.2-sources.jar- Source code JARlib/algodesigner-genetic-0.0.2-javadoc.jar- Javadoc JAR- Compiled classes in
obj/directory
The library includes comprehensive unit tests that serve as practical examples. When you run the tests, you'll see actual evolution in action:
# Run all tests with Ant (shows evolution progress)
ant test
# Example output from EvolutionEngineTest:
# Shows initial generation with mixed 0s, 1s, and .s
# Then shows evolved generation with dominant genes winning
# Final output shows perfect chromosome with fitness 1.0Key test files to explore:
EvolutionEngineTest.java- Basic evolution example (dominant gene problem)MaxAreaTest.java- Optimisation problem example (maximise area with perimeter constraint)SubtractionProblemTest.java- Mathematical problem example (CBA - ABC puzzle)ExtendedEvolutionEngineTest.java- Advanced usage examples (currently ignored)
Test Output Examples:
- EvolutionEngineTest: Shows chromosomes evolving from mixed patterns (
010101...,101010...) to uniform solutions (000000...or111111...) - SubtractionProblemTest: Finds digits A=4, B=9, C=5 that solve 954 - 459 = 495
- MaxAreaTest: Optimises rectangle dimensions to maximise area within perimeter constraints
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.
Copyright (c) 2008-2023, Vlad Shurupov