Professor Daniel Shiffman’s Nature of Code is outstanding. Just check out the syllabus. And play with the Nature of Code repo Processing sketches hosted on Github. And be sure to get his book when it comes out! We covered some basic algorithms for simulating inside the Processing environment vectors and forces/repellers, genetical algorithms, Wolfram cellular automata, neural networks, autonomous agents, flocking behavior, particle systems.
Background
For me, the pull towards genetic algorithms, heredity, fitness, evolution, Punnett squares, etc. was great, so my project for my first intro to computational media class turned into my Nature of Code final.
My Genetic Crossings project attempts to create a simulated environment where people exist within a world connected to God, the peoples’ religions, their nationalities, and each other. They produce offspring based on characteristic attraction rules (for my demo I only used “appearance”, “money”, and “religion”, but only to demonstrate what was possible –I would like to create a more fully-formed algorithm for my personal reputation/identity ecosystem Galapag.us to approximate and adjust to the infinite ways that people become attracted to each other and become married or have children or devote themselves to the other), and they can die. Their well-being or happiness (what in Galapag.us will be eudaimonia) is dependent on their quality of living within their religions and nations.
A video from an older version:
Click here to view the embedded video.
See previous documentation on this project:
- http://blog.benturner.com/2012/03/07/nature-of-code-midterm-genetic-crossing-with-verlet-physics/
- http://blog.benturner.com/2012/04/07/nature-of-code-final-proposal/
- http://blog.benturner.com/2011/12/08/genetic-crossings-icm-final-project-presentation/
- http://blog.benturner.com/2011/11/10/icm-final-project-genetic-crossing/
- http://blog.benturner.com/2011/10/16/icm-genetic-crossing-part-2/
- http://blog.benturner.com/2011/10/13/icm-genetic-crossings/
Here are the initial characteristics I created for each person. There are so many more yet to add! Strength, intelligence, wisdom, charisma, stamina, wit, humor, education, creativity, responsibility, discipline, honesty, religiosity, entrepreneurialism, appearance, money, gracefulness, stress, health, luck, talent_math, talent_art, talent_sports.
Initial nationalities/regions: USA, China, EU, Africa, South America. Characteristics of security, innovation, job opportunity, immigration policy, life expectancy, education, sanitation, standard of living, pollution, biodiversity, crime, political freedom, and nutrition. By no means comprehensive.
Initial religions/spiritualities: Christianity, Judaism, Islam, Taoism, Confucianism, Hinduism, Buddhism. With characteristics of commercialism, morality, hierarchy, portability. Obviously these need some tightening up/additions/discarding.
Final Project
So in my midterm I managed to add Verlet 2D physics to the sketch so that people, nations, and religions have connections to each other which make them bounce around like they’re on springs, relative to their attractions and strength of ties to each other.
In my final proposal, I sought to add the following:
- Wolfram cellular automata
- Connect to external database
- A selection algorithm to choose certain parts from parents based on mutation rate and fitness to pass along to new offspring
- Interaction sliders to change variables
- Macro events that affect well-being of all objects in the world, such as earthquakes or war
- Micro events that affect individual well-being, like rites of passage
- Discrete clusters of people, mostly based on familial strength of ties, instead of one big clump of people in the middle like in my midterm
I managed to get most of this done.
I enjoyed adding Wolfram CA. I wrote a node.js app for express which would act as a JSON middleman between my main genetic crossings sketch and a wolfram sketch. Basically, when viewing the chromosomal stainings (genotypes), you can click on the CA button to the left of a person’s staining, and this will pass a JSON object (using Prof. John Schimmel’s Processing-Nodejs code) to the node server, which is detected within 5 seconds by a polling timer within the wolfram sketch. The wolfram sketch then uses the JSON object as its data to apply rules to to construct a pattern unique to the selected person’s genetic characteristic code.
1) I added a third parameter to the rules which would display as either black or aqua, depending on the CA rules. Prof. Shiffman’s code used base-2 groups of 3, which had 2^3, or 8 total possible combinations (using only the digits 0 and 1), but I used base-3 (0 = white, 1 = black, 2 = aqua), so it became 3^3, or 27 possible combinations. For this ruleset, I duplicated Wolfram rule 90 three times, then added a few extra codes. The triangular look seemed the most visually interesting for what I was doing. Anyway, what was cool about Prof. Shiffman’s code was that it keeps streaming the pattern from bottom to top. So when a new person is clicked on, that person’s signature is integrated into the flow seamlessly once the sketch loads the JSON off the node server. I don’t know that any of this actually is useful except that it looks cool and uses principles from class and maybe, just maybe, shows someone’s digital characteristic “signature”.
Click here to view the embedded video.
2) I’m happy I was able to set up a MongoDB to be accessed via node as well as by my Processing sketches. When I build out Galapag.us, I’ll be able to pump out JSON objects of actual users into these sketches for data visualization. I’d been wanting to do this since my first semester and now it’s done. Most credit goes to John Schimmel though for writing the hook into node though!
3) I realized that I already had some mutation within my matingDance.sex() function, once the two parents’ characteristics were passed to a matingDance.punnettSquare() function. Before, the function would just average the two parents’ characteristic values together and then add or subtract a random amount from them for mutation. What I changed was making the function choose randomly from either parent’s base characteristic. So if one parent had 10, and the other had 1, the result would not be 5 (rough average) but either 1 or 10. Then I would offset a random amount (hardcoded as 4) if mutation kicked in (if a random number between 0 and 50 was less than 2, for approximately a 1 in 25 chance of mutation per characteristic). What this ended up doing was increase the diversity within the genepool and more accurately reflect reproduction. I still need to tweak these numbers to get more consistent levels of variety but the algorithm is mostly there.
Here’s a view of the chromosomal stainings before I changed the algorithm — here it averaged the parents’ traits, which, in the case of this sketch’s iteration’s octomom, created many extremely similar offspring:
And here’s the view after picking from either parent and allowing for a slim chance of mutation:
I feel as though the end result has a more diversified population with more variability between generations and individuals. I need to tweak this so that if the value from a parent is at 1 or 10, it may mutate in only one direction, but here’s the matingDance.punnettSquare() function:
int punnettSquare(int comp1, int comp2) { int mutation = (int)random(0, 50); int dominance = floor(random(0, 1.99)); int crossover = 0; if (dominance == 0) { crossover = comp1; } else { crossover = comp2; } if (mutation < 2) { crossover += (int)random(-4, 4); } // don't want it to be out of bounds // TODO: fix so it can mutate only one way if parent is 1 or 10 if (crossover < 1) { crossover = 1; } else if (crossover > 10) { crossover = 10; } return crossover; }
4) I didn’t add sliders to change variables mid-sketch — at this point I can use variables pre-set in the main class but I’d like to make the interface more user-friendly and interactive later.
5 and 6) I didn’t do macro and micro events because I figured they’d just require making a button that, when pressed, would cause particles’ values to change. What would be interesting would be to have random events happen based on their likelihood to occur and then some events would have permanent effects (damage to peoples’ personalities) or temporary effects (nationalities’ well-being that would later recover). This kind of introduces the possibility for individual peoples’ health and whether they have injuries/disabilities/diseases/gifts/talents.
I did add a Ritual class though, which only includes right now a funeral function. When funerals are recognized by a culture (by pressing ‘f’), the dead are removed from view on the map and their attraction springs are removed as well. What this is supposed to represent is that funerals are a way for the living to remember the dead and then put them to rest so that the living can move on and create new ties with the living. I do like the idea that we retain our ties to the past, which can sometimes become weaker in death and sometimes become even stronger. I didn’t model that yet.
public class Rituals { Rituals() { } void funeral(boolean funeralsRecognized) { println(funeralsRecognized); for (int i=0; i<numPeople; i++) { if (person[i].parent1 != -1 && person[i].parent2 != -1 && person[i].alive == false) { if (funeralsRecognized == true) { physics.removeSpring(parentSpringArray.get(person[i].parent1Spring)); physics.removeSpring(parentSpringArray.get(person[i].parent2Spring)); physics.removeSpring(parentMinDistanceSpringArray.get(person[i].parent1MinDistanceSpring)); physics.removeSpring(parentMinDistanceSpringArray.get(person[i].parent2MinDistanceSpring)); person[i].lock(); person[i].display(0); } else { // TODO: re-reference spring after it's recreated? parentSpringArray.add(new VerletConstrainedSpring2D(person[i], person[person[i].parent1], person[i].parent1RL, random(parentGravity1, parentGravity2))); person[i].parent1Spring = parentSpringArray.size()-1; physics.addSpring(parentSpringArray.get(parentSpringArray.size()-1)); parentSpringArray.add(new VerletConstrainedSpring2D(person[i], person[person[i].parent2], person[i].parent2RL, random(parentGravity1, parentGravity2))); person[i].parent2Spring = parentSpringArray.size()-1; physics.addSpring(parentSpringArray.get(parentSpringArray.size()-1)); parentMinDistanceSpringArray.add(new VerletMinDistanceSpring2D(person[i], person[person[i].parent1], random(parentMinDistanceRL1, parentMinDistanceRL2), random(parentGravity1, parentGravity2))); physics.addSpring(parentMinDistanceSpringArray.get(parentMinDistanceSpringArray.size()-1)); parentMinDistanceSpringArray.add(new VerletMinDistanceSpring2D(person[i], person[person[i].parent2], random(parentMinDistanceRL1, parentMinDistanceRL2), random(parentGravity1, parentGravity2))); physics.addSpring(parentMinDistanceSpringArray.get(parentMinDistanceSpringArray.size()-1)); person[i].unlock(); person[i].display(1); } } } } }
7) Discrete clusters. I ended up adding relationships between people and their parents from just constrained springs to a combination of constrained springs and minimum distance springs. What this would change is that a person’s distance from his parents is both constrained to no more than a certain length but also more than a minimum length, so that they can both be more visible instead of overlapping visually, and also be more clustered together. I found that this makes certain groups on the map appear more clustered instead of forming a big ball in the middle. I still need to do more work on this though because as there are more people in the sketch, the big clusterfuck returns (because there are too many connections between everything and I can’t zoom in closer to see the gaps and relative spacing between different networks).
I converted a lot of my arrays of spring connections over to one large ArrayList, which I think was easier to deal with in the end in terms of manipulating them after they were initiated into the environment. I did find, however, that I had to pass a reference to the spring’s number (since it was just an ArrayList entry) to the person’s class instance so it could refer to it later. A problem with this though, as I realize just now, is that if I remove springs (as I do in the funeral ritual), I’ll lose the correct references. So I have to make sure that when the springs are added again, when funerals are disabled, that a pass a new reference to the ArrayList.
I also found that there tends to be super-breeders every time I run the sketch, with certain people tending to produce tons of offspring while others produce none. I’m talking like 1 or 2 people will produce 10 kids, which tends to make the sketch appear too tightly clustered because everyone is closely linked. Perhaps this is a feature, rather than a bug, of reproduction?
Code
You can download the code from Github. You’ll probably want to start up a node instance and then start the genetic crossings sketch, then finally the wolfram sketch. Instructions are in the README.md.
Github: https://github.com/Xeus/Genetic-Crossing
Conclusion
And this leads me to some closing notes. I shied away from adding fitness yet again to my reproduction algorithms because I felt like “fitness” in the short-term was too much like large-scale evolution theory and autonomous agent simulation. In my sketch there wasn’t really an ideal fitness state, with no limitations or rules imposed on the larger scale. What I wanted was to break into modeling some culture into the simulation, so that choices were made between sexual partners based on cultural norms and not as much on randomized reproduction. Obviously modeling culture would work best if it were overlaid on top of basic biological reproductive theory such as choosing the fittest partner and whatnot, but I felt that was too much for the scope of this simulation, which I wanted mainly to focus on social networks.
JavaScript has come a long way. It’s now the same on the backend and the frontend. Processing can be exported to JavaScript in some capacity, and dataviz libraries such as D3 are taking off. Soon we will be able to introduce more fluid, data, physics, and particle system simulations within a browser. It’s too early for my sketch yet (ToxicLibs takes some finagling) but this is a glimpse of the web to come.
As I begin to do more serious work on the internal mechanics of Galapag.us, it’s stuff like this Processing project that makes me appreciate how careful I’ll have to be with positioning different factors against each other so that people can create their own formulae/evolutions to weight different priorities how they deem fit.
Looks like this book out of the Harvard Berkman Center, “Interop: The Promise and Perils of Highly Interconnected Systems”, by John Palfrey and Urs Gasser, is a must-read.
What I do feel is that current online social networks have not really tried to map out the complex interweaving, competing, variable connections and attractions we have between ourselves and others, between the different identities we all have, between the things we care about more or less at different times in our lives, etc. To facilitate something like this, I can’t help but feel there needs to be a massive API that allows people to access all this data (if privacy settings allow it) so that we can take advantage of the multi-dimensional nature of our species.
You can think of someone’s identity as a meshed web that is being pulled apart by the external world and people and ideas and being pulled together by muscle and ligament and cartilage and sense of self and personality and such. You can think of a community as a bunch of these springy people pulling on and apart from each other constantly, but at a stronger tension than from other communities. Communities form religions and nations and cultures, again with that same network of relationships and competing identities. I hope that’s the dynamic I was able to capture in doing this project.
Credit
Special thanks to:
- Prof. Dan Shiffman for all his documentation and code from Nature of Code, particularly his chapters on forces, genetic algorithms, ToxicLibs, and cellular automata
- Prof. John Schimmel for his Processing-Nodejs code