AndreaCossu/avida-python

Reproducing the Avida ALife world in Python

★ 1Forks 0PythonGitHub ↗Compare

README

Minimal Avida in Python

I used Codex 5.5 heavily throughout the development of the project. Main code reference: https://github.com/devosoft/avida

I am currently validating this implementation against the original Avida experiments.
Bugs might still be around. Any help is appreciated.

avida.py is a compact, hackable Python implementation inspired by the Avida artificial life system. Digital organisms execute circular genomes on a small virtual CPU, copy themselves into neighboring cells, mutate, solve logic tasks, and earn merit that gives them more CPU time.

This is not a port of Avida. It keeps the core digital evolution loop readable: the per-organism CPU is scalar, while population bookkeeping uses NumPy arrays for scheduling, occupancy, neighbors, statistics, and rendering.

Requirements

  • Python 3.9+
  • NumPy
  • ImageIO plus ffmpeg support for MP4/GIF output

Quick Start

Run a basic experiment:

python avida.py --updates 200 --width 20 --height 20 --seed 1

Render solved-function counts instead of genotype IDs:

python avida.py --updates 200 --view functions

Write an animation:

python avida.py --updates 500 --video videos/avida.mp4 --video-every 1 --fps 20

Save and resume a checkpoint:

python avida.py --updates 1000 --checkpoint checkpoints/run.pkl --checkpoint-every 100
python avida.py --resume checkpoints/run.pkl --updates 1000

Checkpoints are pickle files containing the full world state, including organisms, CPU state, genomes, NumPy arrays, counters, task history, and RNG state. Only load checkpoint files you trust.

What It Shows

render() prints an ASCII genotype map. Empty cells are ., and living cells are grouped by genotype with symbols like 0, 1, 2, and so on.

render_functions() prints solved-function counts per organism. Empty cells are ., and living cells show 0 through 9, meaning how many logic tasks that organism has solved.

render_rgb() returns a NumPy RGB frame for videos. Genotype view uses stable colors per genotype; function view colors cells by solved-function count.

Logic Tasks and Merit

When an organism executes IO, it outputs a register value. That output is checked against the two most recent inputs. A newly solved task is recorded and, unless --no-task-rewards is used, multiplies the organism's merit.

Merit controls CPU scheduling: organisms are selected for execution with probability proportional to merit. Higher merit does not directly prevent replacement, but it gives an organism more execution time and therefore more chances to reproduce.

Task Operation Merit
not ~A or ~B 2
nand ~(A & B) 2
and A & B 4
or_n `A ~Bor~A
or `A B`
and_n A & ~B or ~A & B 8
nor ~A & ~B 16
xor A ^ B 16
equ ~(A ^ B) 32

All bitwise outputs are wrapped to unsigned 32-bit values.

Instruction Set

General rule: after each instruction, cpu.ip moves to the next genome position unless the instruction changes the next instruction pointer.

  • nop-A: no operation; also modifies commands to select ax or ip.
  • nop-B: no operation; also modifies commands to select bx or read_head.
  • nop-C: no operation; also modifies commands to select cx or write_head.
  • if-n-equ: skips the next instruction if the selected register equals its complement.
  • if-less: skips unless selected register is signed-less-than its complement.
  • pop: pops the active stack into the selected register, or 0 if empty.
  • push: pushes the selected register onto the active stack.
  • swap-stk: switches between the two stacks.
  • swap: swaps the selected register with its complement.
  • shift-r: right-shifts the selected register by one bit.
  • shift-l: left-shifts the selected register by one bit.
  • inc: increments the selected register.
  • dec: decrements the selected register.
  • add: adds the complement register into the selected register.
  • sub: subtracts the complement register from the selected register.
  • nand: sets selected register to ~(selected & complement).
  • IO: outputs the selected register, records solved tasks, then receives a new input.
  • h-alloc: allocates child memory equal to parent genome length.
  • h-divide: divides if child memory is complete, applying divide mutations.
  • h-copy: copies one instruction from read_head to child memory at write_head.
  • h-search: searches for the complement of the following NOP label.
  • mov-head: moves a selected head to flow_head; can jump ip.
  • jmp-head: moves a selected head by signed offset cx; can jump ip.
  • get-head: stores the selected head position in cx.
  • if-label: checks recently copied instructions against the complement NOP label.
  • set-flow: sets flow_head = cx.

Modifier selection:

Modifier Register Head
nop-A ax ip
nop-B bx read_head
nop-C cx write_head
none bx ip

Useful Options

  • --ancestor {nofunction,nand,original}: choose the injected ancestor genome.
  • --ancestor-copies N: inject multiple ancestor copies at startup.
  • --mutation RATE: per-instruction copy mutation rate.
  • --insert RATE: per-instruction insertion rate at division.
  • --delete RATE: per-instruction deletion rate at division.
  • --report-every N: print stats every N updates.
  • --view {genotype,functions}: choose final render and video view.
  • --checkpoint PATH: save a checkpoint.
  • --checkpoint-every N: save periodically; 0 disables periodic saves.
  • --resume PATH: resume from a saved checkpoint.

Contributors

AndreaCossu

Issues