Development Overview¶
You probably want to check when this page was last updated.
🛠️ 👷♀️ If anything is unclear or out of date, please open an issue or a pull request so we can fix it! Also, if you have project-specific questions or any thing else you’d like to chat about you can always feel free to get in touch directly. I’d love to hear from you!
Cells & Cardinals¶
The goal of the DISHTINY project is to power scalable digital evolution simulations of fraternal transitions in individuality (a.k.a., multicellularity and eusociality).
At the most coarse-grained level, a DISHTINY simulation is just an undirected graph (in the discrete mathematics sense of the word). We refer to individual nodes within this graph as “cells.” A single digital organism can occupy each cell or a cell can be unoccupied.
In a graph, each node has a collection of edges that connect it to other nodes. Likewise, in the DISHTINY simulation, each cell is adjacent to several neighbor cells.
Each cell contains an array of “cardinal” hardware units, one per neighbor cell. (Cardinal in the sense of cardinal directions.) Each cardinal manages a cell’s interactions with a single neighbor cell using a single SignalGP virtual CPU. All cardinals within a cell independently execute the same genetic program. Cardinals units within the same cell can interact with each other via message passing.
The following schematic summarizes
how a single SignalGP CPU functions, executing instruction modules in response to stimuli from the environment and from other SignalGP CPUs, and
how SignalGP CPUs are arranged into cells in the DISHTINY model.
By default, cells are arranged as a two-dimensional toroidal grid, basically a chess board with wraparound edges. However, this structure can be easily reconfigured! A DISHTINY population is just a collection of digital organisms occupying available cells. The following visualizations show
cell kin groups grown on the toroidal grid (individual squares correspond to cells), and
a snapshot of open spawn requests on a cardinal-by-cardinal basis (each cardinal corresponds to a small triangle wedge, there are four cardinals per cell).
The dish2::Cardinal
class manages the state of a single cardinal.
Likewise, the dish2::Cell
class (which contains a vector of dish2::Cardinal
) manages the state of a single cell.
The following schematic summarizes the class structure that organizes simulation state under the hood. Click for a larger, zoomable view.
This diagram was generated using Lucidchart.
The source document can be accessed here and here.
Vector-format versions are available in docs/assets
.
Simulation Update Loop¶
DISHTINY performs simulation in discrete steps called updates. During an update, each cell has an opportunity to perform tasks like executing some SignalGP code, collect resource, share resource, reproducing, etc.
These tasks are organized as discrete “services.” Each service specifies,
how often it should run,
if it should run on dead cells, live cells, or both,
what it should do to a cell when it runs.
Not every service runs every update! For example, at current default settings we only process reproduction requests every 16 updates.
So, all we do during an update is loop over the cells and, within each cell, test and potentially execute each service.
We specify which services should run and in what order within the dish2::Spec
object.
Most simulation customizations will probably involve copying and modifying a service or writing an entirely new service.
Repository Layout¶
A whistle-stop tour of the repository! 🚂
binder/
¶
configpacks/
¶
dishpylib/
¶
include/
¶
C++ header source for DISHTINY executable. (Simulation code lives in here.)
include/dish2/algorithm/
¶
Functions that modify dish2::ThreadWorld
, for example seeding genomes into it, or use dish2::ThreadWorld
to perform an assay, for example detecting phenotypic divergence.
include/dish2/cell/
¶
Contains dish2::Cell
class, which represents a slot for individual cell-like digital organism (it may either be in an occupied/alive or an unoccupied/dead state), and dish2::Cardinal
class, which contains a SignalGP-Lite cpu instance that manages interaction with the environment and one of a dish2::Cell
’s neighbors.
Each dish2::Cardinal
contains various conduit
uit::Inlet
/uit::Outlet
s that link it to dish2::Cardinal
’s in other cells as well as uit::Inlet
/uit::Outlet
’s that link it to dish2::Cardinal
’s in the same cell.
include/dish2/config/
¶
Utilities for runtime configuration.
Notably, contains the dish2::ConfigBase
class, which manages configurable parameters, and dish2:cfg
, a thread_local
instantiation of the dish2::ConfigBase
class.
include/dish2/configbyroot/
¶
Allows for configuration adjustments or experimental manipulations that apply only to cells with particular phylogenetic roots IDs.
include/dish2/debug/
¶
Utilities for debugging and logging.
include/dish2/enum/
¶
Enumerated value classes.
include/dish2/events/
¶
Utilities and event specifications event-driven computation with SignalGP-Lite.
These source files implement simulation-managed events that were dispatched on virtual CPUs.
Each event is defined as a class with a static Test
function that examines a dish2::Cardinal
and decides if the event’s trigger should be dispatched on that dish2::Cardinal
.
Several events may be defined using the same class via template instantiations of the Test
function over a range of SeriesIdx
integer values.
In addition to a program, each genome contains an array of tags — one for each event. When an event’s criteria is met in the simulation, the genome’s corresponding tag is used to dispatch a module in the program and launch a core executing that module.
All events are also exposed to the cell as a corresponding input sensor. The state of the event is stored in the sensor register using a truthy encoding and is then available during virtual CPU execution. Events are triggered based on the reading of that sensor register (not by re-reading the underlying simulation state). This means that experimental perturbations that perturb sensor input also disrupted event-handling, allowing the state interface complexity metric to measure both event-driven and sensor-based behaviors.
include/dish2/genome/
¶
Classes representing genetic information for the cell-like organisms.
include/dish2/introspection/
¶
Functions to collect summary data about dish2::ThreadWorld
state.
include/dish2/load/
¶
Utilities for loading populations and genomes from file.
include/dish2/operations/
¶
Custom SignalGP-Lite instructions for the virtual CPUs inside dish2::Cardinal
’s.
include/dish2/peripheral/
¶
State that can be read by SignalGP-Lite virtual CPUs, a subset of which can also be written by those virtual CPUs.
All CPU-environment interaction is mediated through the dish2::Peripheral
class.
include/dish2/prefab/
¶
Web widgets used in the interactive web viewer.
include/dish2/push/
¶
Utilities for environmental signal-action messaging between cell tiles. Can be used to determine position within world. Perhaps could be used for distributed generation of stochastic environmental features.
include/dish2/py/
¶
Embedded python scripts.
include/dish2/quorum/
¶
System used for distributed detection of kin ID group size.
include/dish2/record/
¶
Utilities for data collection. Dump refers to data files that are written to once, presumably at the end of a simulation. Write refers to data files that can be updated over the course of a simulation.
include/dish2/run/
¶
Contains code to run a simulation session with a dish2::ThreadWorld
object.
Mediates data collection, inter-cpu synchronization (if configured), and simulation end conditions (i.e., updates or time elapsed).
include/dish2/runninglog/
¶
Keeps a record of events (birth, death, spawn, etc.) logged over the last x
updates for data collection purposes.
Each dish2::Cell
has its own dish2::RunningLogs
instance.
include/dish2/services/
¶
Actions that are performed on each cell.
This is where the bulk of the simulation logic resides.
Specifically, at each update, each cell iterates through all registered services and, based on the result of their static ShouldRun
method, runs them.
For a jump start on writing and hooking up a custom service, use a project search on an existing service to see where its defined and plugged in to the rest of the source.
include/dish2/spec/
¶
Utilities for compile-time configuration.
Most simulation classes and functions are templated on typename Spec
.
This type contains compile-time configuration settings.
include/dish2/utility/
¶
Miscellaneous non-dishtiny-specific utilities.
include/dish2/viz/
¶
Depicts different aspects of simulation state.
getters/
take a cell or cardinal coordinate and return particular data from that coordinate (e.g., amount of resource owned by a cell, whether a cell is alive, how many cores are active in a cardinal’s cpu, etc.).
fill_colormaps
translate one piece of data to a color.
border_colormaps
translate two pieces of data (i.e., either side of a border) to a color.
renderers/
combine a getter/
and a color map to draw one aspect of a visualization (cell fill, cardinal fill, or borders between cell).
artists/
layer several renderers/
to create a complete visualization.
This code is used both to draw on JavaScript Canvas
es in the web viewer and to generate native png output.
include/dish2/web/
¶
Source for the web interface.
include/dish2/world/
¶
Classes to manage global simulation state.
The dish2::ProcWorld
class is just a factory to pump out dish2::ThreadWorld
s.
Worker threads call Update
on dish2::ThreadWorld
objects to actually perform simulation.
postprocessing/
¶
Shell and Python scripts for post-processing data.
Python package requirements, listed in third-party/requirements.txt
, are pre-installed in the project’s Docker container.
source/
¶
Drivers that define a main
function to instantiate and run the simulation natively or on the web.
script/
¶
Shell and Python scripts for running jobs and generating data, mostly.
Python package requirements, listed in third-party/requirements.txt
, are pre-installed in the project’s Docker container.
slurm/
¶
Shell scripts to submit and run jobs on MSU iCER’s HPCC cluster. Should be mostly compatible with other Slurm-based clusters.
web/
¶
Static assets (i.e., images, html) for the interactive web viewer.
Compiled artifacts generated by Emscripten get put in here when you run make web
.
Dependencies¶
Notably, this project is built on top of the conduit, Empirical, and signalgp-lite C++ libraries.
We use the Emscripten compiler to build the project’s interactive web viewer.
For a more complete understanding of how this software works, click over to those projects and peruse their README.md
for a sense of what they do.