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

  1. how a single SignalGP CPU functions, executing instruction modules in response to stimuli from the environment and from other SignalGP CPUs, and

  2. how SignalGP CPUs are arranged into cells in the DISHTINY model.

hardware layout schematics

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

  1. cell kin groups grown on the toroidal grid (individual squares correspond to cells), and

  2. 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).

population visualizations

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.

class diagram

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,

  1. how often it should run,

  2. if it should run on dead cells, live cells, or both,

  3. 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::Outlets 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 Canvases 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::ThreadWorlds. 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.