Skip to main content

Pure-numpy genetic algorithm framework for single-objective (GA) and multi-objective (NSGA-II) optimization

Project description

ctrl-freak

An extensible genetic algorithm framework for single and multi-objective optimization, built on pure numpy.

Maintenance Status

๐ŸŸข Active Development

This repository is part of an ongoing project and actively maintained.


Installation

uv add ctrl-freak

Quick Start

import numpy as np
from ctrl_freak import nsga2, ga

# === Multi-Objective: NSGA-II ===
def init(rng):
    return rng.uniform(0, 1, size=5)

def evaluate_multi(x):
    f1 = x[0]
    f2 = 1 - np.sqrt(x[0]) + x[1:]@x[1:]
    return np.array([f1, f2])

def crossover(p1, p2):
    return (p1 + p2) / 2

def mutate(x):
    return np.clip(x + np.random.normal(0, 0.1, size=x.shape), 0, 1)

result = nsga2(
    init=init,
    evaluate=evaluate_multi,
    crossover=crossover,
    mutate=mutate,
    pop_size=100,
    n_generations=50,
    seed=42,
)

# Extract Pareto front
pareto_front = result.pareto_front
print(f"Found {len(pareto_front)} Pareto-optimal solutions")

# === Single-Objective: Standard GA ===
def evaluate_single(x):
    return float(np.sum(x ** 2))  # Sphere function

result = ga(
    init=init,
    evaluate=evaluate_single,
    crossover=crossover,
    mutate=mutate,
    pop_size=100,
    n_generations=100,
    seed=42,
)

print(f"Best fitness: {result.best[1]:.6f}")

Documentation


Design Philosophy

  • Pure numpy for performance
  • Functional style with immutable data structures
  • User thinks about individuals, framework handles vectorization via lift()
  • Fail fast with eager validation
  • Domain agnostic โ€” framework handles selection pressure, user handles constraints/bounds
  • Extensible via pluggable selection and survival strategies

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  User Domain Layer                                          โ”‚
โ”‚  init(), evaluate(), crossover(), mutate()                  โ”‚
โ”‚  (per-individual, user-defined)                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ”‚ lift()
                          โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Algorithm Layer                                            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                         โ”‚
โ”‚  โ”‚   nsga2()   โ”‚    โ”‚    ga()     โ”‚                         โ”‚
โ”‚  โ”‚ multi-obj   โ”‚    โ”‚ single-obj  โ”‚                         โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                         โ”‚
โ”‚         โ”‚                  โ”‚                                โ”‚
โ”‚         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                โ”‚
โ”‚                  โ–ผ                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  Pluggable Strategies                                 โ”‚  โ”‚
โ”‚  โ”‚  Selection: crowded, tournament, roulette             โ”‚  โ”‚
โ”‚  โ”‚  Survival: nsga2, truncation, elitist                 โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ”‚
                          โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Primitives (pure functions)                                โ”‚
โ”‚  non_dominated_sort(), crowding_distance(), dominates()     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

API Reference

Algorithms

# Multi-objective optimization
nsga2(init, evaluate, crossover, mutate, pop_size, n_generations,
      seed=None, callback=None, select='crowded', survive='nsga2',
      n_workers=1) -> NSGA2Result

# Single-objective optimization
ga(init, evaluate, crossover, mutate, pop_size, n_generations,
   seed=None, callback=None, select='tournament', survive='elitist',
   n_workers=1) -> GAResult

User Function Contracts

Function Signature Description
init (rng) -> (n_vars,) Initialize one random individual
evaluate (n_vars,) -> (n_obj,) or float Compute objectives (minimization)
crossover (n_vars,), (n_vars,) -> (n_vars,) Combine two parents into one child
mutate (n_vars,) -> (n_vars,) Perturb an individual

Result Types

NSGA2Result โ€” Multi-objective optimization result:

  • population: Population โ€” Final population
  • rank: np.ndarray โ€” Pareto front ranks (n,) where 0 = optimal
  • crowding_distance: np.ndarray โ€” Diversity measure (n,)
  • pareto_front: Population โ€” Property returning rank-0 individuals
  • generations: int โ€” Generations completed
  • evaluations: int โ€” Total evaluations

GAResult โ€” Single-objective optimization result:

  • population: Population โ€” Final population
  • fitness: np.ndarray โ€” Fitness values (n,)
  • best: tuple[np.ndarray, float] โ€” Property returning (best_x, best_fitness)
  • generations: int โ€” Generations completed
  • evaluations: int โ€” Total evaluations

Data Structures

Population โ€” Immutable collection of solutions:

  • x: np.ndarray โ€” Decision variables (n, n_vars)
  • objectives: np.ndarray | None โ€” Objective values (n, n_obj)

Selection Strategies

Name Function Use Case
'crowded' crowded_tournament() NSGA-II (rank + crowding)
'tournament' fitness_tournament() GA (fitness-based)
'roulette' roulette_wheel() GA (fitness-proportionate)

Survival Strategies

Name Function Use Case
'nsga2' nsga2_survival() NSGA-II (fronts + crowding)
'truncation' truncation_survival() Keep best k
'elitist' elitist_survival() Preserve elite parents

Primitives

Function Description
dominates(a, b) Check if a Pareto-dominates b
dominates_matrix(objectives) Pairwise dominance matrix
non_dominated_sort(objectives) Assign Pareto front ranks
crowding_distance(front) Compute crowding distances for one front

Benchmarks

Tested against Pymoo and DEAP on ZDT test problems (100 pop, 250 generations, 10 seeds):

Problem ctrl-freak Pymoo DEAP
ZDT1 0.8653 ยฑ 0.0011 0.8241 ยฑ 0.0255 0.8698 ยฑ 0.0002
ZDT2 0.5320 ยฑ 0.0017 0.4764 ยฑ 0.0182 0.5363 ยฑ 0.0002
ZDT3 1.3224 ยฑ 0.0008 1.2836 ยฑ 0.0123 1.3275 ยฑ 0.0002

ctrl-freak matches DEAP-level hypervolume on ZDT1-3 with low variance. See full benchmark results.


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ctrl_freak-0.1.0.tar.gz (25.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ctrl_freak-0.1.0-py3-none-any.whl (38.6 kB view details)

Uploaded Python 3

File details

Details for the file ctrl_freak-0.1.0.tar.gz.

File metadata

  • Download URL: ctrl_freak-0.1.0.tar.gz
  • Upload date:
  • Size: 25.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.0

File hashes

Hashes for ctrl_freak-0.1.0.tar.gz
Algorithm Hash digest
SHA256 93d81208b6f46c5898dceb4cd8f84d759a02c7f6f3d9fbaed1824ae924dc9708
MD5 740eca747933f86953826c62624e581c
BLAKE2b-256 57931b74e39f2ddd0a67181947cd3cb861aff40b25a0a3ea39eacb7a8ceb894c

See more details on using hashes here.

File details

Details for the file ctrl_freak-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ctrl_freak-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 38.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.0

File hashes

Hashes for ctrl_freak-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 edb053a1b9bc4bba2397feb11d71326d5a0e999ad30dcc911aaa1d006312d842
MD5 bc0f48628a255832fdeae5a2950e6efb
BLAKE2b-256 5f3107736be816a8a999c97fd1a58fbd3b9cfe582cb2cdfcfc29fab45486a70b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page