Illustration of the Physis framework: A) There is a fixed set of basic instructions, called opcodes, that cover different types of operations that can be performaned on a tape and registers B) The system lives on a two-dimensional grid where each cell contains a tape. At each step of the simulation a tape executes on a virtual machine. If during its execution it performs the right operations to create a copy of itself, this copy will occupy a random cell in the neighborhood of the current cell C) The tape is divided into three parts: a number indicating how many registers we have (there is always an Instruction Pointer (I) indicating the next instruction to execute while the number of registers can vary), the language part is a set of high-level instructions (separated by the delimiter I) that are combinations of the opcodes and the program part is a set of numbers corresponding to instructions from the language (instructions and program numbers are ordered and indexed so can be accessed based on their addres/location on the tape). At the beginning of the execution the Instruction Pointers always points to the first number in the program. .
TODO: Visualizing the dynamics of the simulation.
To install the project and set up the environment using uv, run:
uv venv
source .venv/bin/activate
uv syncThis will create a virtual environment, activate it, and install all dependencies (including PyTorch with the cu121 setup).
The main experiment is a set of independent seeds,
each a 200k-cycle run on the 128×128 grid (pop_size 16384) seeded with 50
arche.replicator founders. Launch one process per seed (--seed can be any integer,
e.g. 0):
python -m physis --pop_size 16384 --initial_pop 50 --total_cycles 200000 --log_interval 50 \
--seed 62 --wandb --track_lineage --no-caching --max_micro_ops 32 --snapshot_interval 1000
python -m physis --pop_size 16384 --initial_pop 50 --total_cycles 200000 --log_interval 50 \
--seed 63 --wandb --track_lineage --no-caching --max_micro_ops 32 --snapshot_interval 1000The simulation runs on the Numba CUDA VM backend (~3.5 h/seed) and therefore
requires a CUDA GPU. Each run preallocates a nearly-full GPU, so give each seed
its own device (CUDA_VISIBLE_DEVICES=<gpu>). Flag notes:
--no-cachingand--max_micro_ops 32keep self-replicators alive; caching-on and/or the defaultmax_micro_ops 16cause a die-off after ~40k cycles.--snapshot_interval 1000dumps population snapshots to<run>/lineage/snapshot_<cycle>.npz(needed for the figures).
Runs land in output/run_200000_cycles_seed_<seed>_<timestamp>/.
Every run generates its figures automatically when it finishes, writing them into
its own output/run_.../ folder:
evolution_3panel.gif— animated 3-panel spatial view of the grid over the run.unique_over_time.png— stackplot of unique genomes (by hash) over cycles.simulation_metrics.png,gestation_diversity.png,top_genomes_*.png— summary plots.
To (re)generate them from a finished run without re-simulating, point the standalone
renderer at the run folder. It loads that run's simulation_stats.pkl and rewrites all
figures in place:
python -m physis.analysis.visualization --folder run_200000_cycles_seed_62_<timestamp>--folder is a folder name inside the base path (output/ by default, or $BASE_PATH
from a .env file). Omit --folder to auto-select the most recent run_* folder. The
run's simulation_stats.pkl can be tens of GB, so the load takes a minute or two.
