The first shape has been grown using an evolved program! This is part of the plant grower project I am working on. It doesn’t really look like much, but that’s because it is evolved to grow as many nodes as possible in a set amount of time. So it is not surprising that it looks like a dense hairball. I suspect that results will get more visually interesting when the fitness criteria encourages compromise. Here is the shape:
The shape is really a representation of the ‘phenotype.’ A phenotype is ‘the set of observable characteristics of an individual resulting from the interaction of its genotype with the environment.’ (according to google) The genotype is below:
Rather simple, and it is plausible that a human could create such a program. All this program does is take the mean vector between x and x’s unit vector. X is the vector describing the position of a collided sphere-‘nutrient’ relative to the node that just ‘fed.’ What is interesting is that this program was evolved rather than written by a human (me). This evolution ran for five generations, where the population consisted of ten individuals. The evolution is being run using deap. As far as genetic programming goes this is a small population and not many generations to get an interesting result (usually defined in terms of ‘fitness’). But I am just working out the errors first with a simple prototype. For reference this point in the project has been git tagged as
looks like I was too much in a rush to type ‘first’! Not sure what fist-evolving is :)
Here are some more plant-shapes that are ‘grown’ in the same simulation environment, but with different randomly generated growth rules. Note the variety in shapes! See the previous post for a detailed explanation. I am posting the most interesting structures I have come across, but many of the randomly generated rules result in ‘boring’ shapes, like a vertical line. Beware my aesthetic bias!
Since the last post I realized why many of the structures grew preferentially in a certain direction. This was because of the primitive functions ‘add,’ ‘multiply,’ and ‘subtract’ that operated element-wise on two vectors. Such element-wise operations are likely two take two vectors and make all there elements more positive, or more negative, or some combination. Because this affects the y-vector of the next node, this has a positive-feedback effect, resulting in structures that grew in one direction. I removed those functions for now.
gnarly-spindly-tree Below is a structure tagged gnarly-spindly-tree (I am using git tags to keep track of interesting structures along the way.) Kinda reminds me of a blue-oak (Quercus douglasii)
This node-edge shape could be thought of as the ‘phenotype’ in the context of the evolutionary algorithm.
Below is the full processor tree for the above structure. This can be thought of as the ‘genotype.’
Here is a close up of the processor tree ‘genotype.’
I have added some more primitive functions since the last post, like cross product for vectors, extracting the x, y, z component, and finding the angle between vectors.
Here are some more fun shapes!
shagy-wild-bush. For this one the processor tree is so huge its not very informative to look at.
straight-segment-bush I like how simple this processor tree is, but still leads to an interesting growth shape. I probably would not have come up with this myself. Since the processor tree is simple enough to evaluate yourself, I’ll explain its parts: x and y are vectors (see prev. post for description of what they are). Norm( vec ) outputs the length of the input vector. Rotate_vec_np( vec1, vec2, angle ) rotates the first vector about the second vector, by some angle in radians (c1=.5 in this case). If_greater( arg1, arg2, arg3, arg4 ) outputs arg3 if arg1 is greater than arg 2, otherwise it returns arg4. Thats it!
No-Name Alas, the below structure was generated before I created a system for saving and ‘resurrecting’ processor trees.
A note on how I save the processor trees: I mentioned above that I am using a git tag to save out interesting shapes. In the past when working on generative projects like this coral one, I tried to save out lists of parameters with a given shape. The problem with this is that as soon as I changed the code and added new parameters or completely different behavior, those parameter lists were useless! This time around I realized that that the entire code base of the project at the time the form is generated is the ‘DNA’ of a given shape. So really the best way to save that information is using git, a version control system for keeping track of code. I started the practice of committing all relevant code after an interesting shape was found, and then immediately using ‘ git tag some-descriptive-name ‘ to associate that tag with a certain shape. That worked well until I started messing around with the randomly generated programs I have been referring to as ‘processor trees.’ To save these I have the script save a .txt file which contains a text representation of the processor tree. For example straight-segment-bush uses this processor tree:
if_greater(norm(x), norm(y), rotate_vec_np(x, y, c1), y)
If the structure is one I want to save, I copy the file and rename it, and I create a git tag of the same name. That way if I change the primitive set of functions that the processor tree uses, I can still go back to the version of the code where the primitive set was correctly configured for that processor tree. In this case I use
git checkout refs/tags/tag-name
To re-enstate the code. Finally, there is a function in the deap.gp library that can recreate the working processor tree from the text-based representation. That’s how I can resurrect an extinct virtual organism!
Last post I was figuring out how to get a genetic programming package inside of a 3d modeling program called blender. Today I got the simplest use of the genetic programming package woking: generating random trees of functions, to make a sort of ‘processor.’ I took the randomly generated processor and plugged it into the plant/coral simulation I have been developing. In this simulation a bunch of spherical particles jitter downwards, roughly simulating the diffusion of nutrients in water or light in a forest. A point, or collection of points, acts as a ‘seed.’ When a sphere collides with the seed, it creates a new point and an edge. I will refer to the points as ‘nodes.’ Each node decides where to put the new node. For the moment this decision is based on the position of the collided sphere, and it’s relative position to its parent node.
The decision of where to put the new node is where the randomly generated processor comes in. Normally I would write the program or sub-routine that decides where to put the next node. Indeed I have been doing that for a bit. Shapes that are generated from my growth rules are in the renders portion of this website. But I am interested in making generative shapes that evolve! Presumably many of the plant, algae and coral shapes that we observe in the real world have growth rules that have evolved and been subjected to natural selection. Seeing all of that variation makes me want to experiment with virtual growth systems and simulated evolution. So anyway, this is the first step in that direction.
How it works
There is a set of ‘primitive functions’ that are sampled randomly to create a nested series of function calls. There is also a set of ‘terminals’ which are constants and variables that act as inputs to the innermost functions. Here is an example of a randomly generated processor:
mean_vec(mean_vec(rotate_vec_np(y, y, c1), rotate_vec_np(y, y, c1)), scale(rotate_vec_np(y, y, c1), dot(x, y)))
The primitive set included mean_vec, rotate_vec_np, scale and dot. The terminal set consists of the 3d vectors x,y and the constant scalar c1.
This is most easily visualized as a tree (I did not make the code to generate processor trees or visualize them. It is all from the python package deap):
This processor tree takes in two 3d vectors and outputs a new vector. This is intentionally designed to act as the ‘decision maker’ of the node! the vector x is the relative position of the sphere to the collided node. The vector y is the vector from the parent node to the collided node.
Clearly the functions available in the primitive set, and any constants in the terminal set, are going to have an important role in constraining the possible growth behaviors. So I have been playing around with different primitive sets.
Below: some pleasing shapes that resulted, with their associated processing tree on the right.
You might notice some terminals are numbers with alot of decimals. Those are ‘ephemeral constants,’ constants that are randomly picked, in some range, when the processor was generated. Also, some of the structures are a bit lopsided. I accidentally had a setting where particles were being spawned at a side plane of the box as well as the top. In all of these cases the ‘seed’ is a line segment. This way the top most node has a non-zero vector from the parent node. If there was only one node, the y vector would be zero, and more processor trees would result in no visible structure.