Evolving Digital Life: Designing a Modular Evolution Simulator
Evolutionary Simulation with LLM-Assisted Phenotype Analysis
This project is a modular digital evolution framework that borrows structure and concepts from genomics, evolutionary dynamics, and modern AI systems engineering. It is implemented as a Python package and lives at:
https://github.com/vaddhiparthy/Evolutionary-simulation-using-LLM
At its core, the system models a population of synthetic organisms, each with an explicit genotype (a high-dimensional DNA matrix), a derived phenotype (continuous trait vector), and a dynamic interaction with a stochastic environment. On top of that, it integrates a language model layer that translates abstract trait vectors into human-readable descriptions, acting as an AI-assisted “phenotype interpreter” for emergent clusters of organisms.
1. Design Goals and Perspective
The project is designed like a production-quality AI data platform, not a one-off script. The primary goals are:
-
Explicit genotype–phenotype separation
The framework distinguishes clearly between encoded genetic information (DNA matrix) and emergent traits (phenotypic vectors), mirroring how genomic variants map to organism-level properties. -
Config-driven experimental control
Genome dimensionality, mutation rates, population size, environmental volatility, clustering hyperparameters, and LLM settings are externalised in YAML configurations to support systematic experimentation. -
Observability and reproducibility
Each generation’s summary statistics and cluster-level phenotypes are written to a structured SQLite database for auditability and post-hoc analysis. -
LLM-assisted interpretation layer
High-dimensional trait vectors are translated into natural-language descriptions using a Hugging Face-compatible LLM (or a deterministic heuristic), producing an interpretable “phenotype report” per cluster. -
Modular, maintainable architecture
The codebase is organised as a proper Python package (evo_sim) with well-defined modules: configuration, DNA/traits, environment, organism dynamics, evolution engine, database, and UI.
The result is closer to a digital evolution lab than a tutorial snippet: you can run controlled evolutionary experiments, adjust evolutionary parameters, and analyse population-level outcomes both numerically and through LLM-generated summaries.
2. Architectural Overview
The high-level architecture is composed of the following layers:
- Genotype layer: DNA represented as a 2-D matrix of continuous allelic intensities.
- Phenotype layer: deterministic projection from DNA to a trait vector.
- Environment layer: time-varying ecological state (UV, temperature, nutrients, toxins).
- Organism model: energy balance, damage accumulation, and reproduction dynamics.
- Evolution engine: multi-generation loop implementing mutation, selection, and replacement.
- Analytics and storage: SQLite database capturing generation metrics and cluster means.
- LLM integration: trait-vector-to-text translation using a Hugging Face-style interface.
- UI layer: static HTML dashboard for inspecting metrics and cluster descriptions.
The package layout is:
evo_sim/
__init__.py
main.py # entry point: simulation + dashboard generation
config/
__init__.py
simulation_config.yaml # genome, population, environment, traits, clustering
llm_config.yaml # LLM model + prompt template
models/
dna.py # DNA matrix representation & mutation operators
traits.py # genotype -> phenotype mapping
environment.py # stochastic environment ("bombardment") dynamics
organism.py # organismal state & survival functions
engine.py # evolutionary loop across generations
database.py # SQLite logging of metrics and cluster means
llm.py # trait vectors -> natural language descriptions
ui/
dashboard.py # static HTML dashboard from the SQLite log
Running:
python -m evo_sim.main --output dashboard.html
executes a full evolutionary run with the configured parameters and generates both the SQLite log and a static dashboard HTML file.
3. Genomic Representation: DNA as a Matrix
Each organism’s genome is encoded as a dense matrix:
- Rows ≈ chromosomes
- Columns ≈ gene loci or feature positions along each chromosome
- Entries ≈ continuous “allelic intensities” in the range [0, 1]
Formally, for an organism:
DNA ∈ [0, 1]C × G
where C is the number of synthetic chromosomes (e.g., 48) and G
is the number of loci per chromosome, both specified in the YAML configuration.
While biological genomes use discrete nucleotides and codons, this simulator adopts a continuous representation to support a smooth genotype–phenotype mapping. Small perturbations in the DNA matrix lead to continuous variations in trait space, which is well-suited for synthetic evolution.
3.1 Mutation Operators
Mutations operate directly on the allelic intensities:
-
Point mutations
With probabilityp_mutper gene, Gaussian noise is added to that entry: values are then clamped back into [0, 1]. -
Structural perturbations (optional)
Low-probability duplication or deletion events act along columns (loci), emulating copy-number-like changes. -
Clamping
All mutated values are clipped to [0, 1] to keep the representation numerically bounded.
Each offspring inherits the parent’s DNA matrix subject to these mutation operators. Conceptually, each matrix element stands in for the effective dosage or strength of a hypothetical gene product at that locus.
4. Genotype → Phenotype: Trait Projection
The genotype–phenotype map is implemented as a fixed, randomly initialised, nonlinear projection from DNA space to trait space:
- Flatten the
C × GDNA matrix into a single vector. - Apply a linear transformation
z = Wg + b, whereWandbare fixed. - Apply a logistic activation
t = σ(z)to obtain trait values in (0, 1).
The resulting trait vector t is interpreted along a set of named axes such as:
light_sensitivityuv_resistancetemp_optimumtemp_tolerancenutrient_affinity_a,nutrient_affinity_bmotility_speed,motility_directionalitydna_repair_ratereplication_rate
The trait count and names are defined in simulation_config.yaml,
while the projection matrix W and bias b are initialised from a
seeded RNG and remain fixed throughout the run. This captures a “frozen developmental
program” that evolution must adapt to, in analogy with the biophysical constraints of
real organisms.
5. Environment Model and Bombardment Dynamics
The environment is represented as a compact ecological state vector:
- UV radiation level
- Ambient temperature
- Concentration of nutrient A
- Concentration of nutrient B
- Toxic load
All components are normalised to the interval [0, 1]. At each time step within a generation, the environment undergoes:
- Minor fluctuations: small random perturbations representing background noise.
- Major events: larger jumps modelling events such as UV spikes, nutrient crashes, or toxicity surges, occurring with a configurable low probability.
This produces a bounded stochastic process on a low-dimensional environmental manifold. By adjusting step sizes, probabilities, and the number of steps per generation, one can simulate different selective regimes (stable vs. volatile environments, frequent vs. rare catastrophes, etc.).
6. Organism Dynamics: Energy, Damage, and Reproduction
Each organism experiences the environment through its trait vector. The
Organism model encodes three main state variables:
- Energy: a scalar representing metabolic reserves.
- Damage: a scalar summarising cumulative stress and injury.
- Reproduction count: the number of successful replication events.
6.1 Energy Balance
Energy is updated each time step based on:
-
Intake from nutrient fields, modulated by
nutrient_affinity_aandnutrient_affinity_b. -
Expenditure comprising a baseline metabolic cost plus a
motility-dependent cost (higher
motility_speedincreases energy usage). - Organisms whose energy declines below a configured threshold are considered non-viable.
6.2 Damage Accumulation and DNA Repair
Damage evolves according to:
-
UV-induced damage, attenuated by
uv_resistance. -
Thermal stress, driven by the difference between current environmental temperature
and
temp_optimum, withtemp_toleranceacting as a buffer. - Toxin-related damage, scaled by environmental toxin load and any configured resistance traits.
-
A repair component proportional to
dna_repair_rate.
If accumulated damage exceeds a threshold, the organism is marked as dead. This captures a stylised interaction between genotoxic stress, repair mechanisms, and survival.
6.3 Reproductive Potential
Reproduction is modelled as a stochastic event whose probability depends on:
- Current energy level (reproduction is suppressed at low energy).
- Current damage level (heavily damaged organisms rarely reproduce).
- The trait
replication_rate, analogous to an intrinsic r-strategy parameter.
When conditions are favourable, the organism increments its reproduction counter. At the end of the generation, this count feeds into the fitness calculation.
7. Evolution Engine: Generations, Fitness, and Clustering
The EvolutionEngine coordinates the dynamics across generations. For each
generation, it:
- Simulates a fixed number of time steps, updating the environment via bombardment and stepping each organism.
- Computes a scalar fitness for each organism, combining final energy, reproduction count, and damage penalties.
- Logs summary statistics (mean energy, mean damage, mean reproduction, survivors) to SQLite.
- Performs k-means clustering in trait space to approximate “species” and logs cluster means.
- Samples parents proportional to fitness and generates the next generation via mutation.
The system currently uses asexual reproduction with mutation. The architecture is modular enough to support later extension to recombination, multi-parent reproduction, or explicit phylogenetic tracking.
8. Data, Logging, and Experiment Traceability
All data from a run is written to data/logs/evolution.db using SQLite. Two core tables are maintained:
8.1 Generation-Level Metrics
The generations table holds:
- Generation index
- Mean energy
- Mean damage
- Mean reproduction count
- Number of surviving organisms
- Timestamp and optional metadata
8.2 Cluster-Level Phenotypes
The cluster_means table holds:
- Generation index
- Cluster identifier
- Mean trait vector (JSON-encoded float array)
- Textual description (generated by the LLM or heuristic translator)
This transforms each simulation run into a structured artifact that can be interrogated with notebooks, dashboards, or external tools. The project also includes a static dashboard generator (ui/dashboard.py) that renders an HTML summary of metrics and cluster descriptions over time.
9. LLM Integration: Phenotype Interpretation
High-dimensional trait vectors are difficult to interpret manually. To address this, the project uses a trait-vector-to-text translation layer implemented in llm.py.
For each cluster mean trait vector, the system:
- Reads an LLM configuration from llm_config.yaml, including model name and prompt template.
- Interpolates the trait values into the prompt template.
- If a Hugging Face-compatible model is available, calls the LLM to generate a concise description of the cluster’s ecological niche and survival strategy.
- If no LLM is configured, falls back to a deterministic heuristic that buckets trait values into low/medium/high and constructs a textual summary.
- Stores the generated description in the
cluster_meanstable.
This effectively produces an “AI-generated phenotype report” per cluster, offering a human-readable description of emergent strategies in trait space. It also opens the door to future LLM-based extensions, such as scenario generation or narrative reporting across generations.
10. Why This Architecture Matters
This framework is deliberately engineered as a platform for serious experimentation in digital evolution, rather than a minimal demonstration. It combines:
- A well-structured genomic-style representation.
- A nontrivial genotype–phenotype mapping.
- Explicit, configurable environmental dynamics.
- A clear organismal life-cycle model.
- A persistent analytics layer for traceability.
- LLM-based interpretation of emergent phenotypes.
From a data/AI engineering standpoint, it illustrates how to:
- Design simulations as maintainable packages, not monoliths.
- Treat simulation output as first-class data, suitable for long-term analysis.
- Integrate LLMs as interpreters on top of structured numeric dynamics.
From a computational-evolution standpoint, it provides a sandbox for exploring:
- How mutation rates and bombardment regimes influence stability and diversity.
- Which trait combinations consistently survive given environmental statistics.
- How phenotypic clusters (species-like entities) emerge and shift over time.
The current implementation also serves as a foundation for further extensions: multi-cellular organisation, explicit spatial structure, recombination, vector-database-driven search over phenotypes, and more.
11. Running the Simulation
From the project root, a full simulation and dashboard generation can be invoked with:
python -m evo_sim.main --output my_dashboard.html
This will:
- Load
simulation_config.yamlandllm_config.yaml. - Run the configured number of generations.
- Write metrics and cluster means to
data/logs/evolution.db. - Generate
my_dashboard.htmlas a self-contained static dashboard.
Comments
Post a Comment