Written during my sophomore year, this library was an exploration into Evolutionary Algorithms applied to Reinforcement Learning tasks (specifically Atari games).
Overview
Standard Cartesian Genetic Programming (CGP) relies heavily on mutation. This project hybridized CGP with NEAT (NeuroEvolution of Augmenting Topologies) concepts to protect topological innovation through speciation.
The goal was to evolve graph-based programs that could learn control policies using gradient-free optimization.
Features
- Graph-based Crossover: Implemented custom crossover operators like
subgraph_crossoverandaligned_node_crossoverto handle the destructive nature of mating graph structures. - Speciation: Adapted NEAT’s compatibility distance metric to CGP graphs to maintain population diversity and prevent premature convergence.
- Active Gene Tracking: Differentiated between “active” nodes (those contributing to output) and “junk DNA,” optimizing the mutation logic to focus on phenotypic changes.
Usage/Gameplay
The library provides a Julia API for defining CGP graphs, configuring evolutionary parameters, and running the evolutionary loop against custom environments.
Results
Looking back, this codebase captures a transitional moment where I was moving from scripting to library design.
- The Ambition: Attempting to merge NEAT’s speciation dynamics with CGP’s grid-based topology was theoretically distinct and an ambitious undertaking for my software engineering skills at the time.
- The “Legacy” Code: The project relies on the now-deprecated Julia v0.6 and uses
eval(parse(...))patterns for configuration (a significant performance anti-pattern in modern Julia). - The Lesson: It taught me the difficulty of designing genetic operators that respect topological constraints, a lesson that informs my current understanding of optimization in structured spaces.
