Giter VIP home page Giter VIP logo

biosim4's Introduction

biosim4

What is this?

This pile of code was used to simulate biological creatures that evolve through natural selection. The results of the experiments are summarized in this YouTube video:

      "I programmed some creatures. They evolved."

      https://www.youtube.com/watch?v=N3tRFayqVtk

This code lacks a friendly interface, so compiling and executing the program may require attention to details. If you ask questions in the Issues, I'll try to help if I can.

Document Contents

Code walkthrough

Main data structures

The code in the src directory compiles to a single console program named biosim4. When it is invoked, it will read parameters from a config file named biosim4.ini by default. A different config file can be specified on the command line.

The simulator will then configure a 2D arena where the creatures live. Class Grid (see grid.h and grid.cpp) contains a 2D array of 16-bit indexes, where each nonzero index refers to a specific individual in class Peeps (see below). Zero values in Grid indicate empty locations. Class Grid does not know anything else about the world; it only stores indexes to represent who lives where.

The population of creatures is stored in class Peeps (see peeps.h and peeps.cpp). Class Peeps contains all the individuals in the simulation, stored as instances of struct Indiv in a std::vector container. The indexes in class Grid are indexes into the vector of individuals in class Peeps. Class Peeps keeps a container of struct Indiv, but otherwise does not know anything about the internal workings of individuals.

Each individual is represented by an instance of struct Indiv (see indiv.h and indiv.cpp). Struct Indiv contains an individual's genome, its corresponding neural net brain, and a redundant copy of the individual's X,Y location in the 2D grid. It also contains a few other parameters for the individual, such as its "responsiveness" level, oscillator period, age, and other personal parameters. Struct Indiv knows how to convert an individual's genome into its neural net brain at the beginning of the simulation. It also knows how to print the genome and neural net brain in text format to stdout during a simulation. It also has a function Indiv::getSensor() that is called to compute the individual's input neurons for each simulator step.

All the simulator code lives in the BS namespace (short for "biosim".)

Config file

The config file, named biosim4.ini by default, contains all the tunable parameters for a simulation run. The biosim4 executable reads the config file at startup, then monitors it for changes during the simulation. Although it's not foolproof, many parameters can be modified during the simulation run. Class ParamManager (see params.h and params.cpp) manages the configuration parameters and makes them available to the simulator through a read-only pointer provided by ParamManager::getParamRef().

See the provided biosim4.ini for documentation for each parameter. Most of the parameters in the config file correspond to members in struct Params (see params.h). A few additional parameters may be stored in struct Params. See the documentation in params.h for how to support new parameters.

Program output

Depending on the parameters in the config file, the following data can be produced:

  • The simulator will append one line to logs/epoch.txt after the completion of each generation. Each line records the generation number, number of individuals who survived the selection criterion, an estimate of the population's genetic diversity, average genome length, and number of deaths due to the "kill" gene. This file can be fed to tools/graphlog.gp to produce a graphic plot.

  • The simulator will display a small number of sample genomes at regular intervals to stdout. Parameters in the config file specify the number and interval. The genomes are displayed in hex format and also in a mnemonic format that can be fed to tools/graph-nnet.py to produce a graphic network diagram.

  • Movies of selected generations will be created in the images/ directory. Parameters in the config file specify the interval at which to make movies. Each movie records a single generation.

  • At intervals, a summary is printed to stdout showing the total number of neural connections throughout the population from each possible sensory input neuron and to each possible action output neuron.

Main program loop

The simulator starts with a call to simulator() in simulator.cpp. After initializing the world, the simulator executes three nested loops: the outer loop for each generation, an inner loop for each simulator step within the generation, and an innermost loop for each individual in the population. The innermost loop is thread-safe so that it can be parallelized by OpenMP.

At the end of each simulator step, a call is made to endOfSimStep() in single-thread mode (see endOfSimStep.cpp) to create a video frame representing the locations of all the individuals at the end of the simulator step. The video frame is pushed on to a stack to be converted to a movie later. Also some housekeeping may be done for certain selection scenarios. See the comments in endOfSimStep.cpp for more information.

At the end of each generation, a call is made to endOfGeneration() in single-thread mode (see endOfGeneration.cpp) to create a video from the saved video frames. Also a new graph might be generated showing the progress of the simulation. See endOfGeneraton.cpp for more information.

Sensory inputs and action outputs

See the YouTube video (link above) for a description of the sensory inputs and action outputs. Each sensory input and each action output is a neuron in the individual's neural net brain.

The header file sensors-actions.h contains enum Sensor which enumerates all the possible sensory inputs and enum Action which enumerates all the possible action outputs. In enum Sensor, all the sensory inputs before the enumerant NUM_SENSES will be compiled into the executable, and all action outputs before NUM_ACTIONS will be compiled. By rearranging the enumerants in those enums, you can select a subset of all possible sensory and action neurons to be compiled into the simulator.

Basic value types

There are a few basic value types:

  • enum Compass represents eight-way directions with enumerants N=0, NE, E, SW, S, SW, W, NW, CENTER.

  • struct Dir is an abstract representation of the values of enum Compass.

  • struct Coord is a signed 16-bit integer X,Y coordinate pair. It is used to represent a location in the 2D world, or can represent the difference between two locations.

  • struct Polar holds a signed 32-bit integer magnitude and a direction of type Dir.

Various conversions and math are possible between these basic types. See unitTestBasicTypes.cpp for examples. Also see basicTypes.h for more information.

Pheromones

A simple system is used to simulate pheromones emitted by the individuals. Pheromones are called "signals" in simulator-speak (see signals.h and signals.cpp). Struct Signals holds a single layer that overlays the 2D world in class Grid. Each location can contain a level of pheromone (there's only a single kind of pheromone supported at present). The pheromone level at any grid location is stored as an unsigned 8-bit integer, where zero means no pheromone, and 255 is the maximum. Each time an individual emits a pheromone, it increases the pheromone values in a small neighborhood around the individual up to the maximum value of 255. Pheromone levels decay over time if they are not replenished by the individuals in the area.

Useful utility functions

The utility function visitNeighborhood() in grid.cpp can be used to execute a user-defined lambda or function over each location within a circular neighborhood defined by a center point and floating point radius. The function calls the user-defined function once for each location, passing it a Coord value. Only locations within the bounds of the grid are visited. The center location is included among the visited locations. For example, a radius of 1.0 includes only the center location plus four neighboring locations. A radius of 1.5 includes the center plus all the eight-way neighbors. The radius can be arbitrarily large but large radii require lots of CPU cycles.

Installing the code


Copy the directory structure to a location of your choice.

Building the executable


System requirements

This code is known to run in the following environment:

  • Ubuntu 21.04, 22.04, or Debian 10 (Buster)
  • cimg-dev 2.4.5 or later
  • libopencv-dev 3.2 or later
  • gcc 8.3, 9.3 or 10.3
  • python-igraph 0.8.3 (used only by tools/graph-nnet.py)
  • gnuplot 5.2.8 (used only by tools/graphlog.gp)

The code also runs in distributions based on Ubuntu 20.04, but only if the default version of cimg-dev is replaced with version 2.8.4 or later.

Compiling

You have several options:

Code::Blocks project file

The file named "biosim4.cbp" is a configuration file for the Code::Blocks IDE version 20.03.

Makefile

A Makefile is provided which was created from biosim4.cbp with cbp2make. Possible make commands include:

  • "make" with no arguments makes release and debug versions in ./bin/Release and ./bin/Debug
  • "make release" makes a release version in ./bin/Release
  • "make debug" makes a debug version in ./bin/Debug
  • "make clean" removes the intermediate build files

Docker

A Dockerfile is provided which leverages the aforementioned Makefile.

To build a Docker environment in which you can compile the program:

docker build -t biosim4 .

You can then compile the program with an ephemeral container:

docker run --rm -ti -v `pwd`:/app --name biosim biosim4 make

When you exit the container, the files compiled in your container files will persist in ./bin.

CMake

A CMakeList.txt file is provided to allow development, build, test, installation and packaging with the CMake tool chain and all IDE's that support CMake.

To build with cmake you need to install cmake. Once installed use the procedure below:

mkdir build
cd build
cmake ../
cmake --build ./

To make a test installation and run the program:

mkdir build
cd build
cmake ../
cmake --build ./
mkdir test_install
cmake --install ./ --prefix ./test_install
cd test_install
./bin/biosim4

To make a release package:

mkdir build
cd build
cmake ../
cmake --build ./
cpack ./

Bugs


If you try to compile the simulator under a distribution based on Ubuntu 20.04, you will encounter this bug in the version of CImg.h (package cimg-dev) provided by the package maintainer:

      https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=951965

In biosim4, CImg.h is used only as a convenient interface to OpenCV to generate movies of the simulated creatures in their 2D world. You have several choices if you want to proceed with Ubuntu 20.04:

  • You can strip out the code that generates the movies and just run the simulator without the movies. Most of that graphics code is in imageWriter.cpp and imageWriter.h.

  • You can upgrade your CImg.h to version 2.8.4 or later by installing the Ubuntu 22.04 cimg-dev package, For example:

cd /tmp && \
wget http://mirrors.kernel.org/ubuntu/pool/universe/c/cimg/cimg-dev_2.9.4+dfsg-3_all.deb -O cimg-dev_2.9.4+dfsg-3_all.deb && \
sudo apt install ./cimg-dev_2.9.4+dfsg-3_all.deb && \
rm cimg-dev_2.9.4+dfsg-3_all.deb;
  • You could convert the CImg.h function calls to use OpenCV directly. Sorry I don't have a guide for how to do that.

Execution


Test everything is working by executing the Debug or Release executable in the bin directory with the default config file ("biosim4.ini"). e.g.:

./bin/Release/biosim4 biosim4.ini

You should have output something like: Gen 1, 2290 survivors

If this works then edit the config file ("biosim4.ini") for the parameters you want for the simulation run and execute the Debug or Release executable. Optionally specify the name of the config file as the first command line argument, e.g.:

./bin/Release/biosim4 [biosim4.ini]

Note: If using docker,

docker run --rm -ti -v `pwd`:/app --name biosim biosim4 bash

will put you into an environment where you can run the above and have all your files persist when you exit (using Ctrl-D).

Tools directory


tools/graphlog.gp takes the generated log file logs/epoch-log.txt and generates a graphic plot of the simulation run in images/log.png. You may need to adjust the directory paths in graphlog.gp for your environment. graphlog.gp can be invoked manually, or if the option "updateGraphLog" is set to true in the simulation config file, the simulator will try to invoke tools/graphlog.gp automatically during the simulation run. Also see the parameter named updateGraphLogStride in the config file.

tools/graph-nnet.py takes a text file (hardcoded name "net.txt") and generates a neural net connection diagram using igraph. The file net.txt contains an encoded form of one genome, and must be the same format as the files generated by displaySampleGenomes() in src/analysis.cpp which is called by simulator() in src/simulator.cpp. The genome output is printed to stdout automatically if the parameter named "displaySampleGenomes" is set to nonzero in the config file. An individual genome can be copied from that output stream and renamed "net.txt" in order to run graph-nnet.py.

Note: If using the docker run ... bash command, the presumed directory structure would necessitate the following syntax:

gnuplot tools/graphlog.gp
cd tools && python3 graph-nnet.py

Build log


In case it helps for debugging the build process, here is a build log from Code::Blocks running under Ubuntu 21.04:

-------------- Clean: Release in biosim4 (compiler: GNU GCC Compiler)---------------

Cleaned "biosim4 - Release"

-------------- Build: Release in biosim4 (compiler: GNU GCC Compiler)---------------

g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/analysis.cpp -o obj/Release/src/analysis.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/basicTypes.cpp -o obj/Release/src/basicTypes.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/createBarrier.cpp -o obj/Release/src/createBarrier.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/endOfGeneration.cpp -o obj/Release/src/endOfGeneration.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/endOfSimStep.cpp -o obj/Release/src/endOfSimStep.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/executeActions.cpp -o obj/Release/src/executeActions.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/feedForward.cpp -o obj/Release/src/feedForward.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/genome-compare.cpp -o obj/Release/src/genome-compare.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/genome.cpp -o obj/Release/src/genome.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/getSensor.cpp -o obj/Release/src/getSensor.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/grid.cpp -o obj/Release/src/grid.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/imageWriter.cpp -o obj/Release/src/imageWriter.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/indiv.cpp -o obj/Release/src/indiv.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/main.cpp -o obj/Release/src/main.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/params.cpp -o obj/Release/src/params.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/peeps.cpp -o obj/Release/src/peeps.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/random.cpp -o obj/Release/src/random.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/signals.cpp -o obj/Release/src/signals.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/simulator.cpp -o obj/Release/src/simulator.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/spawnNewGeneration.cpp -o obj/Release/src/spawnNewGeneration.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/survival-criteria.cpp -o obj/Release/src/survival-criteria.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/unitTestBasicTypes.cpp -o obj/Release/src/unitTestBasicTypes.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/unitTestConnectNeuralNetWiringFromGenome.cpp -o obj/Release/src/unitTestConnectNeuralNetWiringFromGenome.o
g++ -Wall -fexceptions -fopenmp -O3 -I/usr/include/opencv4 -c /home/dm/sw/biosim4-git/src/unitTestGridVisitNeighborhood.cpp -o obj/Release/src/unitTestGridVisitNeighborhood.o
g++  -o bin/Release/biosim4 obj/Release/src/analysis.o obj/Release/src/basicTypes.o obj/Release/src/createBarrier.o obj/Release/src/endOfGeneration.o obj/Release/src/endOfSimStep.o obj/Release/src/executeActions.o obj/Release/src/feedForward.o obj/Release/src/genome-compare.o obj/Release/src/genome.o obj/Release/src/getSensor.o obj/Release/src/grid.o obj/Release/src/imageWriter.o obj/Release/src/indiv.o obj/Release/src/main.o obj/Release/src/params.o obj/Release/src/peeps.o obj/Release/src/random.o obj/Release/src/signals.o obj/Release/src/simulator.o obj/Release/src/spawnNewGeneration.o obj/Release/src/survival-criteria.o obj/Release/src/unitTestBasicTypes.o obj/Release/src/unitTestConnectNeuralNetWiringFromGenome.o obj/Release/src/unitTestGridVisitNeighborhood.o  -lX11 -lgomp -pthread -O3 -s  /usr/lib/x86_64-linux-gnu/libopencv_core.so /usr/lib/x86_64-linux-gnu/libopencv_video.so /usr/lib/x86_64-linux-gnu/libopencv_videoio.so
Output file is bin/Release/biosim4 with size 778.42 KB
Process terminated with status 0 (0 minute(s), 11 second(s))
0 error(s), 0 warning(s) (0 minute(s), 11 second(s))

biosim4's People

Contributors

apatho avatar arhi avatar asa-hopkins avatar cauardsilva avatar davidrmiller avatar divinity76 avatar gregeakin avatar mathematicalmichael avatar mjoergen avatar petterreinholdtsen avatar stepgrif avatar wasimj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

biosim4's Issues

Exception on run.

pi@raspberrypi:~/biosim4 $ ./bin/Debug/biosim4 biosim4.ini
Floating point exception

It doesn't even tell any other info.

License?

I love this project. Not so much C++. I can't find anything about license - would it be possible to add something about the license of this repo so we know what we can do with the code? I'm considering rewriting in golang.

Confusion about signals

I watched the video on YouTube and it was great. I watched it a few times and I'm trying to re-implement the simulator myself.

I had a question about how signals travel in the neural network. My understanding goes like this:

  1. Signals come from input neurons, when it is activated (whatever the reason). The strength of a signal can be 0, 1, or anything in between.
  2. Signals travel through connections and the strength is multiplied by the connection's strength.
  3. The strength of the signal leaving a neuron is tanh(sum(inputs)).
  4. When a signal gets to an output neuron, then this action represented by the neuron has some chance to be executed. The greater the strength, the more likely that action will be chosen.

I currently have the following questions:

  1. How does it work when an internal neuron is connected to itself? In tanh(sum(inputs)), we can't include the self connection's input, because it does not exist yet. But once the output is activated, then a new input is activated. I don't know how to deal with this recursivity.
  2. What does it mean when an output neuron receives a negative signal? Does everything below 0 mean no activation at all?
  3. At around the 15-minute mark in the video, an example neural network is shown. (see the image below) It says that the MvE neuron is more likely to be activated when the path forward is free to go (LPf input). But when it isn't then the other output neuron (Mrn) becomes more likely to be chosen. How can it be active? I don't see anything connected to it. The only input I see goes to MvE, and MvE has no output. The video says that the signal comes from the internal neuron, but where does the signal come from?

(image for question 3)
image

Thank you!

(I'm still reading the code, so if I find an answer I'll post it here. I'm not comfortable with C++ unfortunately.)

How are colors calculated?

I have very little knowledge of C++, but I find the project fascinating. But I can't figure out how a genome is converted into colors. Looking at the code, it seems like the function to convert a genome

uint8_t makeGeneticColor(const Genome &genome)
{
return ((genome.size() & 1)
| ((genome.front().sourceType) << 1)
| ((genome.back().sourceType) << 2)
| ((genome.front().sinkType) << 3)
| ((genome.back().sinkType) << 4)
| ((genome.front().sourceNum & 1) << 5)
| ((genome.front().sinkNum & 1) << 6)
| ((genome.back().sourceNum & 1) << 7));
}

It seems like only the first and last gene is being used to convert to a color. Which seems to be unpacked and plotted here

color[0] = (c); // R: 0..255
color[1] = ((c & 0x1f) << 3); // G: 0..255
color[2] = ((c & 7) << 5); // B: 0..255

I have couple questions

  1. The first and last genes are not in any ways special, then why only use those two? This can give an illusion of two very different genomes looking very similar.
  2. Why unpack a uint8 to three uint8s, only for visualization?

Installation and compilation instructions

It would be fabulous if you could provide some instructions on how to compile and install this awesome simulation.

What would be most useful are:

  • What OS packages are needed?
  • What libraries are needed?
  • How to install them using a typical package manager like apt get etc.
  • Compilation instructions, what tools and flags are needed?

Input neuron values not negative

I was wondering why the input neuron output is 0 to 1 whereas other neurons are -1 to 1 and the output neurons seem to expect -1 to 1 as well.

In particular, input like LAST_MOVE_DIR_X maps -1 to 0 to 1 as 0 to .5 to 1. This seem asymmetrical and seems like the math would artificially bias toward one direction or the other.

Thanks so much for making this video! Before I watched it I had thought neural networks were way beyond my comprehension.

Igraph ncol error

Hi, I have a very silly problem... I have managed to get everything to work, except plotting with graph-nnet.py and igraph. I think the problem is the format of my net.txt file but I can't understand it.

For example, I have this outpot from a simulation:

"Individual ID 1
a17735f8 f672f545 ad21e321 76291949
N0 MvW -24201
Osc MvW -2446
Blr MvY -21215
Bfd MvS 30249
"

I create a net.txt using gedit with "a17735f8 f672f545 ad21e321 76291949" in it but when I run the python script I get this error message:

"Traceback (most recent call last):
File "/home/usr/Desktop/Biosim4/tools/graph-nnet.py", line 7, in
g = igraph.Graph.Read_Ncol('net.txt', names=True, weights=True)
igraph._igraph.InternalError: Error at src/io/ncol.c:150: Parse error in NCOL file, line 1 (syntax error, unexpected ALNUM, expecting NEWLINE). -- Parse error"

What info from the output should I have in the net.txt file for it to work?

Thanks!

GUI Development Request

Is there a push towards creating a GUI for this project? If so I would like to contribute.

[suggestion] Add github topics to repo

After seeing your excellent video I wanted to simulate more types of evolution, but I don't have much C++ experience, and I don't have Ubuntu installed, so instead of trying to see if I could make this work with WSL, I first tried to find if someone else made some other open source evolution simulator. And searching online that I found this github topic: evolution-simulator. And it seems like your repo already has more stars than any other in there (so it seems like good free advertisement).

My suggestion is to add some topics to your repository to increase the chances to get more contributions (and views in your video).
It's really easy, here is github explanation for how it works: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/classifying-your-repository-with-topics

make: *** No targets specified and no makefile found. Stop.

I am excited to take your code for a spin.

I am following your docker instructions.

  1. I cloned the repo
  2. I ran docker build -t biosim4 . it build successfully
  3. I tried to run docker run --rm -ti -v pwd:/app --name biosim biosim4 make but get the error: make: *** No targets specified and no makefile found. Stop.

Is there something im missing here?

Im on windows 10 if that helps.

Pair check is incorrect

In survival-criteria.cpp, in the case CHALLENGE_PAIRS there is a pair of for loops that have a bad check for the end of the loop

        for (int16_t x = indiv.loc.x - 1; x < indiv.loc.x + 1; ++x) {
            for (int16_t y = indiv.loc.y - 1; y < indiv.loc.y + 1; ++y) {

should be

        for (int16_t x = indiv.loc.x - 1; x <= indiv.loc.x + 1; ++x) {
            for (int16_t y = indiv.loc.y - 1; y <= indiv.loc.y + 1; ++y) {

same issue for the inner pair.

                        for (int16_t x1 = tloc.x - 1; x1 < tloc.x + 1; ++x1) {
                            for (int16_t y1 = tloc.y - 1; y1 < tloc.y + 1; ++y1) {

should be

                        for (int16_t x1 = tloc.x - 1; x1 <= tloc.x + 1; ++x1) {
                            for (int16_t y1 = tloc.y - 1; y1 <= tloc.y + 1; ++y1) {

Installing Issue

Hi,

NEVER in my life have I written code, done simulations in programs like yours, etc ... And I don't know how to start the simulation. So, if someone can please help me to know if I have to install a program or do something similar, please tell me.

Thanks.

Peeps::queueForDeath() queues already dead Indiv-s in RadioactiveChallenge

Symptom:
Some individuals are queued for death even after they marked as dead already when RadioActiveWalls mode is on. This results indirectly allowing for individuals to be added to move queue even if they have no movement scheduled.
As a result getPopulationDensityAlongAxis will assert with assert(dir != Compass::CENTER);

Expected:
No assert and dead individuals never should be queued twice.

This is my first ever github community contribution, so I am not familiar with the procedure. I am happy to create a PR with the fix, if you think it needs to be fixed. Please leave a comment and I get the work done.

Makefile improvements

The purpose of this issue is to invite suggestions for restructuring and simplifying the Makefile. The existing Makefile was auto-generated from an IDE. It is not optimally structured and has at least one deficiency that I'm aware of:

In the existing Makefile, source files don't get recompiled when the header files they depend on are modified. The solution proposed by @cavac in #59 would add "src/*.h" as a dependency for every source file, so that if any header file is modified, all source files get recompiled. That would be easy to implement and would need no further maintenance, but would result in unnecessary recompilations when any header file is modified.

I suspect that the Makefile could be considerably simplified while also taking care of the header dependencies.

suse error

First let me start by saying i have very little experience in linux atempted to get running from suse
cimg-devel version 2.9.7
libopencv-devel version 4.5.2
gcc10 10.3.1

attempted make failed with the following.

sychris@localhost:~/Downloads/biosim4-main> make
test -d bin/Debug || mkdir -p bin/Debug
test -d obj/Debug/src || mkdir -p obj/Debug/src
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/signals.cpp -o obj/Debug/src/signals.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/main.cpp -o obj/Debug/src/main.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/params.cpp -o obj/Debug/src/params.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/peeps.cpp -o obj/Debug/src/peeps.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/random.cpp -o obj/Debug/src/random.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/simulator.cpp -o obj/Debug/src/simulator.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/spawnNewGeneration.cpp -o obj/Debug/src/spawnNewGeneration.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/survival-criteria.cpp -o obj/Debug/src/survival-criteria.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/unitTestBasicTypes.cpp -o obj/Debug/src/unitTestBasicTypes.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/unitTestConnectNeuralNetWiringFromGenome.cpp -o obj/Debug/src/unitTestConnectNeuralNetWiringFromGenome.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/unitTestGridVisitNeighborhood.cpp -o obj/Debug/src/unitTestGridVisitNeighborhood.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/genome-compare.cpp -o obj/Debug/src/genome-compare.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/analysis.cpp -o obj/Debug/src/analysis.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/basicTypes.cpp -o obj/Debug/src/basicTypes.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/createBarrier.cpp -o obj/Debug/src/createBarrier.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/endOfGeneration.cpp -o obj/Debug/src/endOfGeneration.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/endOfSimStep.cpp -o obj/Debug/src/endOfSimStep.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/executeActions.cpp -o obj/Debug/src/executeActions.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/feedForward.cpp -o obj/Debug/src/feedForward.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/genome.cpp -o obj/Debug/src/genome.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/getSensor.cpp -o obj/Debug/src/getSensor.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/grid.cpp -o obj/Debug/src/grid.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/imageWriter.cpp -o obj/Debug/src/imageWriter.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/indiv.cpp -o obj/Debug/src/indiv.o
g++ -o bin/Debug/biosim4 obj/Debug/src/signals.o obj/Debug/src/main.o obj/Debug/src/params.o obj/Debug/src/peeps.o obj/Debug/src/random.o obj/Debug/src/simulator.o obj/Debug/src/spawnNewGeneration.o obj/Debug/src/survival-criteria.o obj/Debug/src/unitTestBasicTypes.o obj/Debug/src/unitTestConnectNeuralNetWiringFromGenome.o obj/Debug/src/unitTestGridVisitNeighborhood.o obj/Debug/src/genome-compare.o obj/Debug/src/analysis.o obj/Debug/src/basicTypes.o obj/Debug/src/createBarrier.o obj/Debug/src/endOfGeneration.o obj/Debug/src/endOfSimStep.o obj/Debug/src/executeActions.o obj/Debug/src/feedForward.o obj/Debug/src/genome.o obj/Debug/src/getSensor.o obj/Debug/src/grid.o obj/Debug/src/imageWriter.o obj/Debug/src/indiv.o -lX11 -lpthread -fopenmp /usr/lib/x86_64-linux-gnu/libopencv_core.so /usr/lib/x86_64-linux-gnu/libopencv_video.so /usr/lib/x86_64-linux-gnu/libopencv_videoio.so
/usr/lib64/gcc/x86_64-suse-linux/11/../../../../x86_64-suse-linux/bin/ld: cannot find /usr/lib/x86_64-linux-gnu/libopencv_core.so: No such file or directory
/usr/lib64/gcc/x86_64-suse-linux/11/../../../../x86_64-suse-linux/bin/ld: cannot find /usr/lib/x86_64-linux-gnu/libopencv_video.so: No such file or directory
/usr/lib64/gcc/x86_64-suse-linux/11/../../../../x86_64-suse-linux/bin/ld: cannot find /usr/lib/x86_64-linux-gnu/libopencv_videoio.so: No such file or directory
collect2: error: ld returned 1 exit status
make: *** [Makefile:60: out_debug] Error 1

Trying to gauge how tricky to upgrade CImg.h on Ubuntu

Is this a trivial thing or more tricky?

Hello, so I'm on linux (Zorin OS 16) and I seem to need CImg.h, I tried to upgrade:

You can upgrade your CImg.h to version 2.8.4 or later by getting it from the appropriate Debian repository. Sorry I don't have the instructions at hand to do this.

Hmnmm. So I maybe stuck.

git clone https://github.com/davidrmiller/biosim4
cd biosim4/
make 
test -d bin/Debug || mkdir -p bin/Debug
test -d obj/Debug/src || mkdir -p obj/Debug/src
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/signals.cpp -o obj/Debug/src/signals.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/main.cpp -o obj/Debug/src/main.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/params.cpp -o obj/Debug/src/params.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/peeps.cpp -o obj/Debug/src/peeps.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/random.cpp -o obj/Debug/src/random.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/simulator.cpp -o obj/Debug/src/simulator.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/spawnNewGeneration.cpp -o obj/Debug/src/spawnNewGeneration.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/survival-criteria.cpp -o obj/Debug/src/survival-criteria.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/unitTestBasicTypes.cpp -o obj/Debug/src/unitTestBasicTypes.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/unitTestConnectNeuralNetWiringFromGenome.cpp -o obj/Debug/src/unitTestConnectNeuralNetWiringFromGenome.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/unitTestGridVisitNeighborhood.cpp -o obj/Debug/src/unitTestGridVisitNeighborhood.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/genome-compare.cpp -o obj/Debug/src/genome-compare.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/analysis.cpp -o obj/Debug/src/analysis.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/basicTypes.cpp -o obj/Debug/src/basicTypes.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/createBarrier.cpp -o obj/Debug/src/createBarrier.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/endOfGeneration.cpp -o obj/Debug/src/endOfGeneration.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/endOfSimStep.cpp -o obj/Debug/src/endOfSimStep.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/executeActions.cpp -o obj/Debug/src/executeActions.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/feedForward.cpp -o obj/Debug/src/feedForward.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/genome.cpp -o obj/Debug/src/genome.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/getSensor.cpp -o obj/Debug/src/getSensor.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/grid.cpp -o obj/Debug/src/grid.o
g++ -Wall -fexceptions -g -fopenmp -I/usr/include/opencv4 -c src/imageWriter.cpp -o obj/Debug/src/imageWriter.o
src/imageWriter.cpp:13:10: fatal error: CImg.h: No such file or directory
13 | #include "CImg.h"
|          ^~~~~~~~
compilation terminated.
make: *** [Makefile:129: obj/Debug/src/imageWriter.o] Error 1

make -v
GNU Make 4.2.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Linux putin 5.11.0-40-generic #44~20.04.2-Ubuntu SMP Tue Oct 26 18:07:44 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

I'm not super pro on c so make is about all I know how to do.

Repeated sensorVal message

Great video and good idea to make the code open source .

Successfully compiled from the instructions in the README file using the docker image.

For the initial hundred or so generations of most sims I run, this message repeats verbosely:

sensorVal=-2147483648 for genetic similarity fwd

Do most users experience this or is it perhaps due to a wayward parameter ?

I'm not seeing repeat messages other than this particular value and genetic similarity "fwd".

Methodology Questions

Just saw your video. Great stuff. I'm thinking of dabbling in this using Ruby.

After each filial generation, is it assumed a redistribution in the arena for survivors? It also seems you repopulate back to 1000 in F(n+1).

How are duplicate synapses prevented?

Dear @davidrmiller,

first and foremost I want to thank you for all the time and effort you invested in both your inspiring video and this GitHub repository. A lot of questions are very well answered in the video and even more are answered by the comments in your source code. Well done. Thank you very much.

While implementing a WinForms application in C# with a simulation following your example I came to the conclusion that with a certain probability two genes could encode a connection between the same neurons. Hence there might be two equal connections with most likely different weights. I assume it would be of no issue if source and target of both connections were swapped as it could happen for internal neurons. But how do you prevent duplicate connections between the same source and target neuron or are such duplicates of no issue and just calculated as usual?

Kind regards,
Florian

Edit:
I might have found an answer to this question myself in the abstract of this paper: https://www.pnas.org/content/115/29/E6871

Recent experimental studies suggest that, in cortical microcircuits of the mammalian brain, the majority of neuron-to-neuron connections are realized by multiple synapses.

Balance out "kill" function

I came here after view the video about the simulation. The section about the "kill" function has a terrifying implication. It kind of keeps me awake at night. But I think I finally found the solution.

  • The "kill" function is a social action but the sims have no social evaluation. I will refer to the Sims collective/community/collection as "the Sims", therefore using singular verbs. The "sims" without capitalization refers to multiple of sims. That means the Sims has no "motive" to evolve into a stronger community, they simply evolved to survive.
  • The "kill" function also is a purely negative function in terms of social benefit while it is the ONLY social function. And by "social" I mean the interaction between the sims themselves, not "environmental" factors. And by "negative" I mean having less sims seems worse than having more.
  • After those realizations, it would be kind of surprising to see a long-term equilibrium that does not involve mass killings.

I think if we can introduce a mechanism for the Sims to evaluate its actions (without implicit instruction aka "divine intervention"), it will evolve differently. There is an elegant way to do it. I think the conditions to reproduce should include 1 more dimension. The sims can only reproduce if it has at least 1 sim in close proximity. It's not stated explicitly but repopulation/reproduction is also a social function, a passive/hidden one rather than an active one.

I think that can be achieved in multiple ways. I can see there are multiple sensory inputs coded about population/pheromones gradient/density in the video which were not used. But I am not qualified to say which way represents best whatever the program wants to achieve.

I hope someone can take the suggestion to tweak the project to see if we can find "humanity".

I don't know how much resource is required to do that tweak so I will just put one of them up here. I of course couldn't do it myself as I barely understand the requirements to run the simulation. If anyone wants more ideas, here are a few more "natural social evaluation":

  • the more crowded the proximity, the higher chance of reproduction. That means repopulation is not an equal game for all genes, the "killing loners" are what they are.
  • We can introduce genders where reproduction requires different sexes in close proximity. That probably will also reduce the "kill" gene. I think that tweak could reveal some fascinating understanding about who we are.

does not compile on ARM64 architecture

i thought it would be a good use for a raspberry pi with manjaro Linux currently installed on it to run this for a few days and then i could see the result but /lib/x84_64 or /bin/x84_64 or something like that was not found

maybe you could eventually add support for foreign CPU architectures

Evaluating loops in the network

Hi,

thank you for the inspiring video and the nicely written source code which I am thoroughly studying.

I am having issues understanding how to proper evaluate a network with no layers which therefore allows for circular loops.
The simplest example is in this frame from your video:

Screenshot from 2022-01-11 18-38-01

where to update the value of N1 I would need the value of N0 and to update N0 I need N1. These loops may involve many neurons and be very hard to spot and disentangle.

The only simple way out that I see is by initializing all the neurons to zero and update them using their values in the previous time step. But I am worried that this will make the network very "slow" in the sense that propagating information from sensors to actuators may take a very long number of steps. Is this how it actually works?

Many thanks for your clarifications!

Request Challange Files from Youtube Video

Dear Dave,
i ported your biosim Application to FPC ( https://github.com/PascalCorpsman/biosim4_FPC_translation ) to be able to adjust your code with my own needs in my most favorite programming language (and also to be able to run it on Windows and Linux)
To Validate my port i try to reproduce the results you show on your YouTube video.
I stored the solutions i already found here : https://github.com/PascalCorpsman/biosim4_FPC_translation/tree/main/Challanges

Timestamp 7:57 - 14:38 "Challange_1_Right_Half.ini" was no problem and reproducable

Timestamp 27:16 - 33:40 "Challange_13_Left_Right_eights.ini" i also was able to reproduce this (with only 5000 - 7000 generations)

Timestamp 35:54 - 40:52 Brain sizes

This is where i got into trouble, when having 32 or more inner neurons the individuals start like expected and after a view hundred generations they start to not go to the outside walls anymore but keep a little bit more in the middle and then the surviver rate always drops below 50% and stays there ( even the 8 Inner neuron version does this, but the effekt is not that hard).

So can you please share your .ini files so that i can try to reproduce your results with my simulator ?

Regards
Uwe Schächterle

Intelligence has a cost

One thought about the number of neurons and connections, it could be interesting to randomize the genome length and inner neurons, but introduce a cost associated with each.

The cost could be implemented as requiring a rest between movements (cycles where an individual is simply skipped and remains in place).

To see the effects it might make sense to increase the size of the grid, and gradually reduce the number of steps/generation.

Smaller brains will probably start to win out even before reducing the number of steps as they will be more likely to hit the breeding area first, leaving the smarter ones to possibly arrive in a breeding area that is already full, but moving the breeding targets (perhaps on a wall, but not a corner) could provide a motivation. Barriers, in particular those that form a corner that an individual would need to overcome could be interesting as this would start to require "distance from wall" decisions.

Actually this could be generalized, it would be interesting to add a cost to being able to kill, and/or a cost to kill (perhaps they would be propelled backwards after a kill, possibly jumping over other creatures)?

There is a lot more here than I would be capable of coding, so consider this a feature request if it makes sense?

Windows support

hello, can i ask about OS this apps. can i run this app on windows?

System requirements

Just watched your video, and I'm just looking for system requirements.
What hardware is required for this to run? I see from the makefile we want an x86 64bit. How about RAM and core counts?

What specs are you using?

Thanks for sharing your code.

Confirm 64bit won't work? New territory for me

Just want to confirm 32 bit vs 64 bit support. Encountering this error...

zsh: exec format error: ./bin/Release/biosim4

From looking quickly online it seems this might be that this is built for 32 bit and the latest mac versions won't work. Just wanted to confirm with this community that might be case.

Thanks in advance for any insights!

Confusing behavior. Are move actions correct?

I was trying to run through the individual actions, to understand their effect on the network. While doing so I stumbled upon something confusing.
Let's take the MOVE_FORWARD action. Based on the name and on the fact that there is a MOVE_REVERSE this neurons sole task is to either move the individual forward or not move at all. First question: Is this assumption correct?
If yes, there is a logical issue in calculating the move. If the answer is no, this ticket is obsolete and can be closed.

The explanation for "yes" answer. When the actionLevel is calculated, there is weight that can be negative as well, resulting in a negative action level. In case of MOVE_FORWARD, this would mean a change of direction, so reversing.
I couldn't come up yet with a solution to this, yet. But I guess normalizing with tanh and offsetting to [0.0..1.0] would be the solution, except that, normalization happens once all the movement neurons are processed.

Edit: The reason I was checking move actions, is because I am experiencing that after a while all the individuals get really fast, like slow ones never have a chance for survival. Which, realistically, shouldn't be the case. I am wondering if just over time, the chance of any action resulting in no move is just dramatically reducing because of this logical confusion.
I imagine, if for example MOVE_FORWARD can be only move forward or not move, statistically not move would happen more often, than in the current case. This is just me trying to understand the system and maybe this assumption is incorrect.

Any thoughts? By the way, great simulation tool!

Having issue with makefile

I've been trying to make it using the makefile, but I've been experiencing issues. I'm running on raspberry pi 4 4GB, which is debian-based.

pi@raspberrypi:~/biosim4 $ make
test -d bin/Debug || mkdir -p bin/Debug
test -d obj/Debug/src || mkdir -p obj/Debug/src
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/signals.cpp -o obj/Debug/src/signals.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/main.cpp -o obj/Debug/src/main.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/params.cpp -o obj/Debug/src/params.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/peeps.cpp -o obj/Debug/src/peeps.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/random.cpp -o obj/Debug/src/random.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/simulator.cpp -o obj/Debug/src/simulator.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/spawnNewGeneration.cpp -o obj/Debug/src/spawnNewGeneration.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/survival-criteria.cpp -o obj/Debug/src/survival-criteria.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/unitTestBasicTypes.cpp -o obj/Debug/src/unitTestBasicTypes.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/unitTestConnectNeuralNetWiringFromGenome.cpp -o obj/Debug/src/unitTestConnectNeuralNetWiringFromGenome.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/unitTestGridVisitNeighborhood.cpp -o obj/Debug/src/unitTestGridVisitNeighborhood.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/genome-compare.cpp -o obj/Debug/src/genome-compare.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/analysis.cpp -o obj/Debug/src/analysis.o
g++ -Wall -fexceptions -fopenmp -g -fopenmp -I/usr/include/opencv4 -c src/basicTypes.cpp -o obj/Debug/src/basicTypes.o
src/basicTypes.cpp: In member function ‘BS::Coord BS::Polar::asCoord() const’:
src/basicTypes.cpp:135:15: warning: left shift count >= width of type [-Wshift-count-overflow]
         1L << 32,    // S
               ^~
src/basicTypes.cpp:137:15: warning: left shift count >= width of type [-Wshift-count-overflow]
         1L << 32,    // W
               ^~
src/basicTypes.cpp:139:15: warning: left shift count >= width of type [-Wshift-count-overflow]
         1L << 32,    // E
               ^~
src/basicTypes.cpp:141:15: warning: left shift count >= width of type [-Wshift-count-overflow]
         1L << 32,    // N
               ^~
src/basicTypes.cpp:135:12: error: right operand of shift expression ‘(1 << 32)’ is >= than the precision of the left operand [-fpermissive]
         1L << 32,    // S
         ~~~^~~~~
src/basicTypes.cpp:153:55: warning: integer overflow in expression of type ‘long int’ results in ‘2147483647’ [-Woverflow]
     int64_t temp = ((int64_t)mag >> 32) ^ ((1L << 31) - 1);
                                            ~~~~~~~~~~~^~~
src/basicTypes.cpp:154:33: warning: left shift count >= width of type [-Wshift-count-overflow]
     len = (len + temp) / (1L << 32); // Divide to make sure we get an arithmetic shift

New to everything, how does someone run this?

This might not be a great question or be bad for some other reason, but I really have no idea what I'm doing. How do I get this program to run, and can it even run on windows? Sorry if someone has already asked this but I couldn't find it anywhere.

Thanks in advance!

Java/Processing "fork"

David, this is such a fantastic project/learning experience for me. I am not well versed in C++ so I decided to port this over to Java (within the Processing framework).
https://github.com/MrSlinkyman/NeuronGames

I explained some differences in my implementation vs. the original in the README, however, right now they aren't that different. Just some environment differences between C++ and Java/Processing.

Kill sensory neuron ?

I seen in https://www.youtube.com/watch?v=N3tRFayqVtk that there is a kill action output.

What about if there is a kill sensory neuron ?

Let me explain, if there is kill sensory neuron and a kill action output, evolution could evolve that if an individual A detect a kill from individual B to individual C, that individual A will kill that individual B but because that individual B killed individual C than individual D will not kill indvidual A because it's also detected that individual B did kill individual C.

@davidrmiller I'm not sure it's clear and i understand that this need a lot of mutation and evolution but it's might profoundly change the actual kill you recorded but i suppose that's why you did go up to 200 000 gen...

What you think ?

Defold port

Hi Im working on a Defold port. It looks really great so far:

12-54-50.mp4

I wanted to make sure this is ok to do. I will be releasing the project soon as MIT for anyone to use and wanted to be sure to give correct credits etc. Would you like any specific papers or names in the credits?

Gene Coding

I'm curious about how you allocate the bits in a gene. The first bit is the source type and the next 7 bits are the source ID, right? But if you know the source ID you automatically know its type. Same for the sink.

Please say a bit (ha-ha; sometimes I slay me) about this gene design. Is it to add some "robustness"? Maybe it arose out of a software consideration? Reduce execution time?

Thanks in advance.

getSignalDensityAlongAxis and getPopulationDensityAlongAxis assertions

I saw in a previous issue where these two assertions were added. I'm curious, however, if the program asserts that the direction is not CENTER, how do you prevent creatures from actually using that direction when these sensors are called?

assert(dir != Compass::CENTER); // require a defined axis

assert(dir != Compass::CENTER); // require a defined axis

In the getSensor() call I would imagine that the creatures would have a last move direction as CENTER just by chance.

sensorVal = getPopulationDensityAlongAxis(loc, lastMoveDir);

In my implementation in Java, these assertions are preventing the program from moving forward so I changed them to early returns where the method just returns 0.5 if the direction supplied is CENTER. But I am not sure if that is the right interpretation.

Would love some insight.

Planning a testing methodology

I generally adhere to the Python philosophy that it is better to ask forgiveness than permission. However, in this case it makes sense to gauge the opinions of @davidrmiller and other active contributors. Before I start coding on new or additional functionality, it will be useful to consider implications, best practice and David's vision for this project.

I have a few proposals to facilitate the user experience and put shoulder-to-wheel with some TODO items I noticed in the code:

  1. Hard-coded filenames such as epoch-log.txt (mentioned by David in analysis.cpp) can be specified in the config file. Additional parameters will need to be added.
  2. Optional Automation of tool scripts. As is currently the case with graphlog.gp, it is also possible to automate the genome plotting done by graph-nnet.py. It will entail a similar workflow: Indiv::printGenome() outputs genome plotting instructions to ./logs/net.txt (and console, if desired) and endOfGeneration() (for example) can call Python to draw a sample genome. This will also require a parameter in the config file - to enable or disable this functionality - and to specify a filename and system command. It is, therefore, possible to provide hooks for arbitrary tool scripts the user may choose to run during a simulation.
  3. Parameter records. When running simulations systematically for research it will be valuable to keep a record of the parameters being used for successive or specific simulations. I propose a config file parameter that enables/disables output of parameters to a user specified file. This file should also record any changes to parameters during the course of a simulation - value change, sim generation at time of change, and a date and timestamp.
  4. Convert asserts to useful output. assert() statements are useful during development but are misleading for users. Some people have complained about "segmentation faults" while these are simply code asserts that correctly (and meaningfully) trigger. Unless critical, asserts should be replaced with useful console output that users can report, but that allows sim execution to continue. A stack of errors encountered during execution can be logged (during execution) and dumped to the console at the end of code execution.
  5. Baseline params for barebones simulation. When running biosim without any config file (minus biosim4.ini too) it should execute a short minimal simulation that, ideally, never fails. The current set of hard-coded parameters seems to achieve this. I only had to get rid of an assert() statement in the genome comparator function to achieve a reliable, repeatable minimal simulation. The reason why I mention this is that future code changes should not break this baseline state.

I have a few other ideas, such as coding a Python matplotlib tool and genome editor, but the above list of items will provide the foundation for that, while resolving some of the self-evident current issues.

Let's hear some feedback and opinions.

Comments on two-way reproduction optimality?

Hi,

I saw the code that spawns a child by randomly mixing the genome of two parents and I was wondering if the two-way mixing achieves the fastest improvement of the behavior gen after gen.
Do you have any experience/comments on one-way reproduction (the genome of a single parent is copied and mutated) or multi-way reproduction (the genome from three or more parents is mixed and mutated)?

Thank you for your time and patience.

understanding generateChildGenome

Hi, really nice project. Thanks for sharing it.

I was looking at the function in the title and I'm curious what it does. I thought there would be a crossover approach where a child is born with half the genes from one parent and half the genes from the other parent. But looking at that function, it seems we take only 1 parent and then we randomly insert or delete 1 connection. I don't know c++ so I might be reading the code incorrectly.

Thanks again

Typo in random gene creation?

I'm looking to create my own evolution simulation and this code base seemed like an excellent place to start, so I was perusing the code and I came across the gene declaration and the makeRandomWeight code.

struct Gene { //__attribute__((packed)) Gene {

    // <SNIP>

    static int16_t makeRandomWeight() { return randomUint(0, 0xefff) - 0x8000; }
};

Why 0xefff? Are you deliberately trying to create weights that are biased towards the negative? Or is it a typo for 0xffff?

Windows Port / Similar Sim

Hi,
Thank you for creating this program and the explanation video, it really helped me understand the theory behind this simulation and I decided to create my own version of the simulation using the theory and visualisations you created :D

generation10000.mov

https://github.com/oxi-dev0/NeuroSim

It uses SFML to render and so is Windows compatible; I'm not sure about mac or linux, as I did not setup the premake file to configure for them, but it should work.

Its not a port / a full rewrite, as the only code i used for reference was the grid header; the actual neural simulation is my own code where it back flows from the effectors down it's chain using cached values and recursion when needed, im not sure how biosim4 does it on a code level, but it does produce results similar to biosim4 so it could be considered somewhat of a rewrite :D

It also can produce the neural map visualisations in the program and has a viewer where you can drag the view around, zoom in and hover over the connections to see their weight.

Despite it not being a port / os specific rewrite (it doesnt have pheremones or the population receptors), I hope some people on windows who want to run biosim4 still find it beneficial :)

How do you calculate the action to do

Hi ! I was looking for a example of a complex genetic algorithm w/ explanations so that I could get inspired to dev one by myself. I found the youtube video and loved it !
I'm trying to reproduce a smaller and simpler version (less kind of neuron etc...)
However I'm a bit lost on how to calculate the action to do given a sample of genes, especially beacuse of the loops that can occur.
Can someone explain me the algorithm (maybe in pseudo code or smth like that) ? I tried reading the C++ code but I am a bit lost.
Thanks a lot !

Internal node clarification

You mention in the video during the "Eastern Half Survival" if you will, that the internal node somehow computes whether to drive the MvE neuron or the Mrn neuron, as if there are calculations beside the weighted inputs and activation function occurring in the internal node.

For clarification, I wonder if that was the case. I didn't see anything like that in the code. From what I can tell, the Mrn neuron gets the same inputs every step from the internal node, but when the LPf neuron isn't further driving the MvE neuron then the Mrn neuron is executed. Is that correct? Or is the internal node actually doing something else when the MvE neuron is activated.

Thanks for any clarification.

Publish scientific article on Biosim?

Learning about Biosim4 reminded me of the Tierra system from thirty years ago, and made me hope you will publish your experiences with it as a scientific paper to make sure your ideas are conserved and recognized also when Youtube is long gone.

Are you aware of the Tierray system? You can read more about it in
https://en.wikipedia.org/wiki/Tierra_(computer_simulation) ,
https://faculty.cc.gatech.edu/~turk/bio_sim/articles/tierra_thomas_ray.pdf ,
https://www.nytimes.com/1991/08/27/science/lively-computer-creation-blurs-definition-of-life.html ,
https://www.mediamatic.net/en/page/9047/tierra and
https://yewtu.be/watch?v=Wl5rRGVD0QI .

Getting a segmentation fault

Hey,

Just spent most of my day getting this set up on Windows. I've got it compiling however, but when I try to run the executable, I'm getting a segmentation fault.

The only thing I've changed is the biosim4.ini, so here's how that looks like:

numThreads = 10


sizeX = 128
sizeY = 128

population = 1000

stepsPerGeneration = 300

maxGenerations = 10

genomeInitialLengthMin = 20
genomeInitialLengthMax = 20
genomeMaxLength = 300

maxNumberNeurons = 3

killEnable = false

sexualReproduction = true

chooseParentsByFitness = true

pointMutationRate = 0.001

geneInsertionDeletionRate = 0.0
deletionRatio = 0.5

responsivenessCurveKFactor = 2

populationSensorRadius = 2.5

longProbeDistance = 24

shortProbeBarrierDistance = 4

signalSensorRadius = 2.0

signalLayers = 1

imageDir = images

logDir = logs

displayScale = 8

agentSize = 4

videoSaveFirstFrames = 2

updateGraphLog = true

saveVideo = true

videoStride = 25

updateGraphLogStride = videoStride

genomeAnalysisStride = videoStride

genomeComparisonMethod = 1

displaySampleGenomes = 10

challenge = 17

barrierType = 0

replaceBarrierType = 0
replaceBarrierTypeGenerationNumber = -1

Here's the last part of the output from the terminal as well:

Gen 9, 266 survivors
---------------------------
Individual ID 1
effa52e0 6d003ea5 d8c016e5 be088ac0 1cdeaea8 518250dc 1932306a 44eeee4e
4d5a0762 385d169c da55a7ac b51e412f 11066f79 0a2c4f98 6ad098ab 0bc67bfa
db7d66c9 d90cd171 69968660 2840a9be

N1 N2 -4102
Sfd N1 27904
LPb N2 -10048
N0 N0 -16888
N0 N0 7390
N2 N1 20866
N2 N0 6450
N0 N2 17646
N0 N2 14429
Ly N1 27344
Blr N0 -9347
N0 N1 27030
N1 MRL 19802
N2 MRL -9643
EDx MvX -19170
Sg Res 4358
N1 Res 2604
N2 MvL 3014
Age SG -9972
N2 Mrn 10304
---------------------------
---------------------------
Individual ID 2
f9b6055a ad8f7a13 f17617d5 f2213c97 33fb9cc3 1f1e7dbd f17e3a08 97dece03
65fb4c48 235759ab 0e59ba5b c91cb7eb 42553b97 a782768e fae2f3ad c366166b
a58421d1 2529dc1a 0bac2384 22ec565a

Plr N0 -3551
Osc N0 13307
ED N0 3673
N0 Mfd -1610
Ly MvN -3722
Pop MvR 7966
Ly MvS 9047
Plr MvN -14052
Plr MvL 16981
EDx MvE -1310
Slr MvX -23164
N0 MvY 2988
---------------------------
---------------------------
Individual ID 3
4885dbf4 de441096 2e7b285e b8728c3f 26fe379d 1f1e7dbd f17e3a08 9c945ed3
3bff3ef7 89cf29c9 ca1758a1 85f14b7c 997e8665 54083818 6ad098ab 0bc67bfa
db7d66c9 52dfac37 dd64fe22 af4775a3

N0 N2 -8636
N2 N2 11899
Pop N1 -18318
N1 N2 -3714
Lx N2 -25452
Sg N1 15359
Rnd N2 -13801
LPf N1 -26242
N0 N1 21512
Ly N1 27344
Blr N0 -9347
LMx N2 21215
N2 N1 -8860
N2 MvL 18565
Bfd MvN 9982
Pop MvR 7966
Blr Mrn -30257
N2 OSC -31247
N2 MvL 3014
Sg MvW -20665
---------------------------
Sensors in use:
  862 - loc X
  721 - loc Y
  805 - boundary dist X
  355 - boundary dist
  545 - boundary dist Y
  332 - genetic similarity fwd
  170 - last move dir X
  303 - last move dir Y
  492 - long probe population fwd
  573 - long probe barrier fwd
  651 - population
  388 - population fwd
  709 - population LR
  387 - osc1
  367 - age
  465 - short probe barrier fwd-rev
  521 - short probe barrier left-right
  204 - random
  604 - signal 0
  544 - signal 0 fwd
  418 - signal 0 LR
Actions in use:
  391 - move X
  670 - move Y
  600 - move fwd
  684 - move R-L
  637 - move random
  576 - set osc1
  697 - set longprobe dist
  532 - set inv-responsiveness
  482 - emit signal 0
  739 - move east
  667 - move west
  420 - move north
  966 - move south
  647 - move left
  789 - move right
  520 - move reverse
Simulator waiting...
Simulator exit.
Segmentation fault

Would really appreciate some help with this.

Also, want to thank David for posting this video. Easily one of my favorites on the platform today.

I'm a CS guy, but I found the intersectionality with biology fascinating. The complexity of a brain with only a couple of neurons leaves me in awe of how complicated our brains must be.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.