Evolving Dynamical Systems

 

Simulation of Biology Final Project

Ken Camann

 

 

Idea

 

The idea for this project comes from the paper "Interactive Evolution of Dynamical Systems" by Karl Sims.  In it, he suggests that visually interesting dynamical systems can be evolved more effectively than they can be designed.  He defines his systems using four scalar functions of two variables, two giving the initial conditions of quantities A and B, and two giving their time derivatives.  These equations represent the genotype of a dynamical system, with the phenotype being the system generated from time integrating the differential equations.  A user picks a phenotype that he or she thinks is visually interesting and the system copies its equations.  During copying, several different types of mutations can change the functions slightly, creating a different genotype.  A typical session is similar to a Blind Watchmaker simulation: the user is presented with multiple systems, and after time integrating them the user picks the most visually interesting.  This is copied 4 times, possibly with mutations.  The user again picks the favorite as it evolves.

 

Programming Challenges

 

In Karl Sims’ implementation of this program, Lisp expressions were used to encode the functions in prefix form.  A typical function might be (+ A x), which would correspond to f(x, y) = A(x, y) + x, where A is the name of the first variable in the system.  The original version was also implemented on a Connection Machine, a massively parallel super computer from the 1980s, in order to run at interactive speed.  To achieve an acceptable balance between ease of development and execution time, my implementation was written using Microsoft Visual C# Express Edition.  This is because I needed the rapid application development properties of modern language (like the ability to build a quick user interface) but also needed direct memory access (software bit-block transfers are used to actually draw the image, for speed reasons).  Despite this, the program is still very slow, although it is much faster than my Processing reaction-diffusion code.  Because I did not use Lisp, I represented functions as expression trees.  My program is able to parse and output the Lisp expressions corresponding to the internal expression tree representation, however.

 

Function Set


The following functions are supported:

 

Unary Functions

abs, round, negate, sqrt, square, exp (e^x), sin, cos, sign

 

Binary Functions

+, -, *, /, ^ (power), log, mod, atan2, min, max

 

Ternary Functions

ifpos, ifneg, noise

 

Neighborhood Functions (functions which operate on neighborhoods in one image)

xgrad, ygrad, gradmag, graddir, gradmagsquared, gradindir, laplacian, nhmin, nhmax, nhave, conv5, conv9

 

The function noise is special: it generates Perlin noise and has three parameters: frequency, persistence, and number of octaves.  For each octave, the frequency doubles and the amplitude weighted by the product of the existing weight and the persistence factor as in fBm noise.

 

Randomly Generated Expressions

 

Certain types of mutations, as well as the initial 4 systems, require a mechanism for generating random expressions.  A random expression can be of 3 different types: constant, variable, or function.  A constant is just a constant scalar value.  A variable is either A, B, x, or y.  A function is one of the above functions types, which requires additional random expressions for its arguments.  Many special rules apply to avoid generating mathematically uninteresting arguments for certain functions, for example the Lisp expression (laplacian 3).  Because random function expressions are generated recursively, a “maximum height” is specified in the random generation call.  The procedure will be forced to generate a variable or constant expression after the tree reaches this depth.  Noise functions cannot be generated in differential equations, only in initial conditions.

 

Mutations

 

The probability of a mutation while recursively copying an expression tree is inversely proportional to the number of nodes in the tree, multiplied with some constant.  This ensures that longer expressions do not change too drastically when they are copied.  There are several different types of mutations:

 

Random Expression – A node is replaced with a random expression tree

 

Type Preserving – A node is mutated to another expression of the same type.  I.e., constants become different constants, variables become different variables, and functions become different functions.  The old function arguments are used in the new function.  If the number of arguments changes, existing arguments are either omitted or new ones are generated.

 

Function Argument – An existing node becomes an argument to a new random function

 

Function Deletion – If the type of this node is a function, the function is deleted and its argument becomes the new expression.

 

The frequency with which these occur must be tweaked to ensure that the differential equations usually do something interesting.  For example, inappropriate mutation settings might create too many constant scalar values, resulting in uninteresting behavior because the function does not have enough dependency on variables.  This often results in a harmful mutation; completely visually uninteresting systems are never selected by the user, and some genetic mutations will cause the screen to be all white (or all black) if the system is unstable or always evaluates to a constant value.

 

Differential Equations & Ensuring Interesting Behavior

 

Because of technological limitations at the time, the original program used Euler time integration.   I had wanted to implement the classic 4th order Runge-Kutta method instead to ensure greater stability, but I did not have time to do so.  Because of the sensitivity of differential equations I had expected not to get much interesting behavior without starting from equations known to be interesting, but I was able to get many interesting results just using the random expression generator.  The first two of these pictures were taken at a time when the program output was not working so the corresponding functions do not appear.  All 16 functions appear for the second screenshot.

 

Some interesting initial conditions

 

 

Some interesting systems after 500 Euler steps

 

 

Top Left

 

A0 = (gradmagsquared (noise 0.0932686469744745 0.00121141318288232 4))

B0 = (neg (noise 0.00638372166193264 0.583485082529245 1))

dA/dt = (square (sin B))

dB/dt = (sqrt (atan2 A (round B)))

 

Top Right

A0 = (gradmagsquared (noise 0.0932686469744745 0.00121141318288232 4))

B0 = (neg (noise 0.00638372166193264 0.583485082529245 1))

dA/dt = (nhmin (sin B))

dB/dt = (nhave A)

 

Bottom Left

A0 = (gradmagsquared (noise 0.0932686469744745 0.00121141318288232 4))

B0 = (neg (noise 0.00638372166193264 0.583485082529245 1))

dA/dt = (square (sin B))

dB/dt = (nhave A)

 

Bottom Right

A0 = (gradmagsquared (min (noise 0.0932686469744745 0.00121141318288232 4) (nhave (noise 0.0783055754617348 0.0433988650531503 1))))

B0 = (neg (noise 0.00638372166193264 0.583485082529245 1))

dA/dt = (square (sin B))

dB/dt = (nhave (conv9 A (nhmin x)))

 

Using the Program

 

Initially the screen will be black.  Right click anywhere on the black screen and a dialog will pop up with a proposed set of 4 functions for a random system.  Either accept this by clicking Update, click “CopyM” to copy a given function until it mutates, or enter in your own function in Lisp form.  Clicking Update will generate 4 random genotypes and display their initial condition.  Pressing the “TI Start” button starts the time integration.  Pressing the “TI Stop” button stops this integration.  Left clicking a system shows the other variable (B if A is currently shown, A if B is currently shown), and middle clicking at any time selects that system for reproduction.  All 4 system windows will automatically be populated with copied (and possibly mutated) children from the selected system.  The program requires the Microsoft .Net framework 2.0 to run.  There is a known bug in the software: the dialog that pops up when right clicking a system cannot be closed by hitting the “X” button.  You must either click Update or Cancel.  Closing it with “X” will cause a runtime exception.

 

 

 

The source code and binaries are located here

 

References


1. K.Sims, Towards a Practice of Autonomous Systems: Proceedings of the First European Conference on Artificial Life , MIT Press, 1992, pp.171-178.