Evolution Of Silence

Bachelorarbeit von Matthias Lohscheidt
Fakultät für Gestaltung
Interaktive Medien
Hochschule Augsburg, 2013

betreut durch
Prof. Michael Kipp und Prof. Robert Rose

In »Evolution of Silence« habe ich mich mit den Prinzipien der Evolution und deren Übertragung auf eine emergente Erzeugung von Klanglandschaften beschäftigt. Die Hintergründe und Ergebnisse möchte ich hier dokumentieren.
Anreiz und Inspiration für dieses Thema waren einerseits meine persönliche Faszination für das Phänomen Evolution – sowohl in der Natur als auch in Form von Algorithmen – und andererseits John Cages Gedanken zum Begriff Stille1. Auf den ersten Blick sind das zwei ziemlich unzusammenhängende Ausgangspunkte für eine Arbeit, die später irgendwie zusammenhängen soll. Aber als ich zu Beginn in verschiedene Richtungen des Themas evolutionäre Algorithmen recherchierte, stieß ich auf Arbeiten, die sich im weitesten Sinne mit evolutionärer Musik befassten23. Diese Arbeiten versuchten mit Hilfe evolutionärer Prozesse neue Werkzeuge zu schaffen, die den kreativen Prozess eines Musikers unterstützen sollten. So faszinierend und interessant ich diese Arbeiten auch fand, konnte ich dem Gedanken, in meiner Bachelorarbeit neue Werkzeuge zu schaffen nichts abgewinnen; ich wollte einen Weg finden, auf dem ich mich der Thematik experimenteller und offener annähern konnte. Glücklicherweise stieß ich in dieser Zeit auf einen kurzen und sehr prägnant formulierten Monolog von John Cage über seine Definition von Klang und Stille.

Diese Idee von acting sounds, die einfach nur da sind, weil sie eben da sind, und ohne irgendeine Intention aus verschiedenen Richtungen kommend eine Skulptur bilden, reizte mich sehr. Und irgendwie passte das auch zu dem, was mich an evolutionären Algorithmen interessierte. Ich denke, man kann sich diesem Gebiet aus sehr unterschiedlichen Richtungen nähern. Vielleicht kann man diese Richtungen grob einteilen in ergebnisorientiert und prozessorientiert. In den meisten Fällen dienen solche Algorithmen nämlich zur Lösung eines Problems, indem sie zum Beispiel aus einer unendlichen Zahl von Möglichkeiten möglichst effizient ein optimales Ergebnis finden. Dabei ist der Weg zu diesem Ergebnis nicht von Bedeutung, außer dass er natürlich möglichst kurz sein soll. Auch wenn es bei einer solchen Ergebnisorientierung interessant ist, dass man einen an sich völlig ziellosen und ungerichteten Prozess, wie den der Evolution für so etwas verwenden kann, reizte mich eher der Vorgang an sich.
Diese Orientierung am Prozess vertrug sich gut mit Cages Idee von acting sounds und so begann ich in diese Richtung zu experimentieren. In eine Richtung, in der Klänge in gewisser Weise autonome Wesen sind, die sich innerhalb eines evolutionären Prozesses entwickeln.

Das Ergebnis dieser Experimente ist eine audiovisuelle Installation. Diese Dokumentation beschreibt die verschiedenen Zusammenhänge und Eigenschaften dieser Installation. Am Ende befindet sich noch eine Auswahl verschiedener Experimente, die einzelne Aspekte der Installation genauer erklären.

Installation

Beschreibung

Der Besucher betritt einen rechteckigen Raum, der komplett mit nebelartigem Dunst gefüllt ist. In der Mitte dieses Raums ist eine holographische Projektion zu sehen – vom Boden ausgehend zur Decke strahlend. Die Projektion besteht aus einzelnen verschieden dicken Lichtstrahlen, die scheinbar unabhängig voneinander manchmal vibrieren und manchmal still stehen. In jeder Ecke des Raumes steht ein Lautsprecher. Aus diesen Lautsprechern erklingen in mal gleichmäßigem, mal ungleichmäßigem Rhythmus harmonierende sowie nicht harmonierende Sinustöne. Allerdings ertönen nicht aus allen Lautsprechern die gleichen Töne in der gleichen Lautstärke, sondern jeder Ton scheint aus einer eigenen Richtung zu kommen. Wenn man genau hinsieht und -hört, fällt auf, dass sich das Erklingen und Verstummen einzelner Töne immer synchron zum Vibrieren und Stillstehen einzelner Lichtstrahlen ereignet. Wenn der Betrachter sich nun dem Projektionskegel nähert und einzelne Lichtstrahlen darin berührt, werden diese berührten Lichtstrahlen sofort dicker und heller. Vibrieren sie währenddessen, werden synchron mit dem Dickerwerden der jeweiligen Strahlen auch einzelne Töne lauter erklingen
In einer Ecke des Raumes steht ein schwach leuchtender Monitor. Auf ihm sind verschiedene Graphen und Diagramme zu sehen, die sich laufend verändern. Die Graphen zeigen unter anderem den Verlauf einer Populationsgröße, die Zahl der bereits vergangenen Generationen, die Aufteilung von Lebewesen in verschiedene Gruppen sowie ein Frequenzband, auf dem die verschiedenen Lebewesen verteilt sind. Vor diesem Monitor steht ein Trackpad, das dem Besucher eine Interaktion mit den Graphen erlaubt. Wenn er den Mauszeiger über einzelnen Objekten der Graphik positioniert, erscheinen Zusatzinformationen, die die genauere Bedeutung des jeweiligen Objekts erklären.

Demonstration

Erklärung

Grundlage der Installation ist eine virtuelle Welt, in der virtuelle Kreaturen leben. Sichtbar wird diese Welt durch die holografische Projektion, in der jede Kreatur in Form eines Lichtstrahls dargestellt wird. Darüber hinaus hat jede dieser Kreaturen hat einen genetisch festgelegten Ton, den sie von sich gibt, sobald sie paarungsbereit ist. Je nach Position des Lichtstrahls innerhalb der Projektion ertönt der Ton aus dieser Richtung im Raum. Die Umwelt gibt einen Akkord vor, der für den Betrachter allerdings nicht hörbar ist. Je besser der Ton (unabhängig von dessen Oktavraum) einer Kreatur in das Schema dieses Akkordes passt, desto mehr Energie kann diese Kreatur aufnehmen, desto kräftiger und lauter ist sie, desto länger lebt sie und desto höher sind ihre Chancen sich fortzupflanzen. Da ein Akkord aus mehreren Tönen besteht, gibt es auch mehrere Richtungen, in die sich die Kreaturen entwickeln können. Eine Richtung ist immer ein bestimmter Ton des vorgegebenen Akkords. Wenn eine Kreatur einen dieser vorgegebenen Töne von sich gibt, ist sie optimal angepasst und hat mit großer Wahrscheinlichkeit viele Nachkommen. Damit die Entwicklung in eine Richtung möglichst schnell stattfindet, bevorzugen Kreaturen andere Kreaturen, die einen Ton von sich geben, der zu dem selben Ton strebt, wie sie selbst. Unter diesen bevorzugen sie wiederum die, die dem »Zielton« am nächsten liegen.
Nach ein paar Generationen sind viele Kreaturen gut angepasst und die Klanglandschaft wird harmonischer. Von Zeit zu Zeit ändert sich aber der Akkord, den die Umwelt vorgibt, in einen neuen zufälligen Akkord. Der Grad der Disharmonie steigt und die Entwicklung der Kreaturen schlägt eine neue Richtung ein.
Die Energie, die den Kreaturen zur Verfügung steht, ist begrenzt. Die absolute Menge ändert sich stetig. So steht manchmal viel, manchmal wenig Energie zur Verfügung. Wenn nur wenige Kreaturen existieren, ist der Konkurrenzdruck im Kampf um die vorhandene Energie nicht so hoch und auch weniger gut angepasste Wesen haben eine Chance zu überleben und sich fortzupflanzen. Mit wachsender Population steigt allerdings auch der Konkurrenzdruck und nur ein relativ gut angepasstes Wesen hat eine Chance, lang genug zu überleben, um sich fortzupflanzen.
Wenn ein Benutzer nun in diese Welt hineingreift, führt er den Kreaturen, die er berührt, künstlich Energie zu. Dadurch werden diese Kreaturen lauter, überleben länger und haben eventuell trotz schlechter Anpassung eine Chance, sich zu reproduzieren.

Aufbau und Funktionsweise

Technisch ist die Installation in drei Teile aufgeteilt: Die virtuelle Welt mit den in ihr lebenden und erklingenden »Lebewesen«, sog. Toids (weshalb sie den Namen Toidworld trägt); das Tracking des Benutzers, um eine Interaktion mit der holographischen Projektion zu ermöglichen; der Monitor, auf dem man »Lebensfunktionen« der Toidworld überblicken und verfolgen kann.
Jeder dieser Teile ist ein eigenes Programm und im Folgenden werde ich deren Funktionsweisen und Zusammenspiel genau erklären.

Toidworld

Toidworld ist das Herzstück der gesamten Arbeit. In ihr läuft der evolutionäre Prozess ab, in dem sich Toids den immer wieder im Wandel befindlichen Umweltbedingungen anpassen. Diese Umweltbedingungen sind einerseits ein vorgegebener Grundton und ein davon ausgehendes Akkordpattern und andererseits die Menge an Energie, die den Toids zur Verfügung steht. Für den Benutzer wird Toidworld erfahrbar durch die holographische Projektion und die in Surround-Sound erklingende Klanglandschaft. Der Klang wird allerdings nicht direkt in diesem Programm generiert, sondern in einem SuperCollider-Server4. Dieser Server wird von Toidworld aus über eine OSC-Verbindung angesteuert.
Die kleinste Zeiteinheit in Toidworld ist ein Frame. Die Anwendung läuft bei ca. 60 fps.

Ein Toid

Der Name Toid entstand in Anlehnung an Craig Reynolds' Boids. Boids ist ein Programm, das das Schwarmverhalten von Vögeln simuliert. Der Name Boid bezieht sich auf ein »bird-like object«5, ähnlich dem Wort »humanoid«, das etwas menschenähnliches beschreibt. Ein Toid ist also ein künstliches Wesen, das einem Ton ähnelt.
Jedes Toid hat ein Genom, in dem sein Ton kodiert ist (mehr dazu unter »Genetik«). Bei seiner Geburt wird ein Toid mit einer zufälligen Menge an Lebensenergie ausgestattet. Diese Lebensenergie ist nicht zu verwechseln mit der vorher erwähnten Energie, die die Umwelt zur Verfügung stellt. Sie ist eher wie ein Akku zu verstehen, der nicht leer werden darf und den ein Toid mit der Umweltenergie in einem gewissen Maße auffüllen kann. Die Menge dieser Lebensenergie und der Grad der Anpassung (Fitness) eines Toids sind maßgeblich für dessen Lebensdauer (mehr dazu unter »Energie und Lebenszeit«). Seinen Ton gibt ein Toid von sich, wenn es bereit zur Paarung ist. Diese Bereitschaft wiederum hängt von seiner Libido ab. Die Libido wächst je nach Energiezufuhr schneller oder langsamer. Genauer wird das unter »Triebe und Fortpflanzung« beschrieben.
Ein Toid wird visuell in Form eines weiß gefüllten Quadrats repräsentiert. Dieses Quadrat erscheint in der Projektion durch den Nebel als Lichtstrahl. Je höher die Energiezufuhr eines Toids ist, desto größer ist sein Quadrat und desto dicker damit der Lichtstrahl. Lässt ein Toid seinen Ton erklingen, vibriert sein Lichtstrahl.
Alle wichtigen Funktionen und Eigenschaften eines Toids sind in der Klasse Toid implementiert. Die audiovisuelle Repräsentation ist dann in der Unterklasse SquareToid implementiert. Diese Spezialisierung der Klasse Toid ermöglichte ihre Wiederverwendbarkeit in unterschiedlichen Experimenten.

Genetik

Das Genom eines Toids besteht aus sechs Genen. Jedes Gen ist eine Zahl mit einem Wert von 0 bis 1. Bei der Paarung zweier Toids werden die Genome beider Partner zu einem neuen kombiniert. Dabei wird bei jedem Gen zufällig entschieden, von welchem Elternteil es übernommen wird. Im Mittel besteht das Genom dann jeweils zur Hälfte aus den Genen beider Elternteile. Beim Entstehen jedes Toids können dessen Gene mit einer bestimmten Wahrscheinlichkeit mutieren. Wie hoch diese Mutationsrate ist und wie groß ein Mutationssprung (Deviation) eines Gens sein kann, kann über Parameter zur Laufzeit festgelegt werden. Eine Mutationsrate von 2% und eine Deviation von +/-20% haben sich als ein gutes Maß für eine stabile Entwicklung einer Population erwiesen.
Alle typischen Methoden und Eigenschaften einer digitalen DNA sind dankenswerterweise bereits in der frei verfügbaren Klasse DNA von André Sier implementiert6. So musste ich nur noch für die Expression der DNA vom Geno- zum Phänotypen sorgen. Dafür schrieb ich die Klasse GeneExpressor mit verschiedenen statischen Methoden zur Expression von Genen. Die meiner Meinung nach geeignetste Methode ist diese hier:
Die ersten vier Zahlen des Genoms kodieren einen Ton von 0 bis 11, die restlichen beiden kodieren den Oktavraum, in dem sich der Ton befindet. Kodieren bedeutet in beiden Fällen, dass die jeweiligen Zahlen des Genoms addiert und dann auf einen gewissen Wertebereich gemappt werden (0 bis 11 für den Ton, 2 bis 8 für den Oktavraum). In diesem Stück Pseudocode ist der Vorgang genauer erklärt:

float pitch = dna[0] + dna[1] + dna[2] + dna[3];
pitch = map pitch from 0 ... 4 to 0 ... 11; 

float octave = dna[4] + dna[5];
octave = map octave from 0 ... 2 to 2 ... 8;

pitch += octave * 12;

Diese Methode ist das Ergebnis des Experiments DNA-Variability, das im zweiten Teil der Dokumentation noch genauer beschrieben wird. Es gibt natürlich unzählige Möglichkeiten ein solches Genom in einen Ton zu dekodieren. Mir waren folgende Eigenschaften wichtig:

Die Effekte dieser Methode hängen natürlich stark von der Zahl der vorhandenen Gene ab. Genaueres dazu ist im eben erwähnten Experiment beschrieben.

Energie und Lebenszeit

Abhängig vom Ton, den ein Toid über seine Gene in die Wiege gelegt bekommt, ist seine Fitness gegenüber den Umweltbedingungen, denen es ausgesetzt ist. Je höher diese Fitness ist, desto mehr Energie kann ein Toid aufnehmen. Die Menge der vorhandenen Energie ist allerdings begrenzt und befindet sich in ständiger Schwankung (mehr dazu unter »Umwelt«). Ebenso ist die von der Umwelt vorgegebene Tonart nicht stabil, sondern befindet sich von Zeit zu Zeit im Wandel. Daher wird die Fitness eines Toids in jedem Frame neu bestimmt. Ein perfekt angepasstes Toid hat einen Fitnesswert von 1. Je schlechter die Anpassung, desto mehr geht dieser Wert gegen 0. Mit welcher Methode genau die Fitness eines Toids bestimmt wird, ist in der Beschreibung des Experiments »Frequency-Fitness« erklärt.
Wie bereits erwähnt wird jedes Toid bei seiner Geburt mit einer gewissen zufällig bestimmten Menge an Lebensenergie ausgestattet. Der Schwankungsbereich dieses Zufalls liegt zwischen minimal 300 und maximal 400. In jedem Iterationsschritt (Frame), wird die Lebensenergie eines Toids um den Wert 1 verringert. Gleichzeitig wird aber auch die aufgenommene Energie addiert. Standardmäßig kann ein optimal angepasstes Toid bei genügend vorhandener Energie seine Lebensenergie in jedem Iterationsschritt wieder um 1 erhöhen und seinen Alterungsprozess damit komplett ausgleichen. Das hieße, dass dieses Toid, so lange die Umwelt sich nicht verändern würde, unsterblich wäre. Um das zu verhindern, kann man die maximale Menge der Energie, die ein Toid in einem Iterationsschritt aufnehmen kann, zur Laufzeit anpassen. Es empfiehlt sich dabei ein Wert von ca. 0.8. Ein überhaupt nicht angepasstest Toid mit einer Fitness von 0 und einer Lebensenergie von 350 würde demnach genau 350 Frames überleben, was bei ca. 60 fps dann umgerechnet knapp 6 Sekunden wären. Ein ideal angepasstes Toid mit einem Fitnesswert von 1 und gleicher Lebensenergie hingegen würde bei einer begrenzten Energiezufuhr von maximal 0.8 ca. 1750 Frames, also knapp 29 Sekunden überleben.

Lebenszeit = Lebensenergie / (Alterung - Energiezufuhr)

Die Klasse Toid enthält eine Methode void live(float energy), die genau diesen Prozess ausführt. Sie wird in jedem Iterationsschritt aufgerufen. Über den Parameter energy wird die Menge der Energie übergeben, die einem Toid im jeweiligen Iterationsschritt zugeführt wird. Hier ist der Code dieser Methode (reduziert auf die für diesen Punkt relevanten Zeilen):

/**
 * Do an iteration step in the life of a Toid.
 * @param energy
 */

public void live(float energy) {
    lifeTime -= iterationTime;
    lifeTime += energy * antiAgingFactor;
}

antiAgingFactor ist dabei die zur Laufzeit veränderbare Variable zur Maximierung der Energiezufuhr. iterationTime hat den Wert 1.

Aufteilung in Pools

Ein weiterer wichtiger Aspekt bei der Energiezufuhr und – wie später noch beschrieben wird – auch bei der Fortpflanzung eines Toids ist dessen Zugehörigkeit zu einem bestimmen Pool. Die Zahl der vorhandenen Pools hängt von der Zahl der von der Umwelt vorgegebenen Töne ab. Jeder dieser Töne bildet einen eigenen Pool und jedes Toid landet in dem Pool, dessen Ton seinem am nächsten ist.
Diese Pools entsprechen letztendlich ökologischen Nischen. Jeder Ton bildet eine solche Nische, in der sich Toids unabhängig von den Toids anderer Nischen entwickeln können. In anfänglichen Experimenten habe ich festgestellt, dass sich ohne solche Nischen früher oder später alle Toids zu einem Ton hin entwickeln. Wenn man darüber nachdenkt, ist auch klar warum: Die Toids entwickeln sich zum dem Ton, in dessen Nähe sich am Anfang des Evolutionsprozesses die meisten Toids befinden. Je mehr Toids sich nämlich um denselben Ton tummeln, desto mehr genetische Information ist zur Kodierung eben dieses Tons vorhanden und desto höher ist bei der Fortpflanzung die Wahrscheinlichkeit, dass diese Informationen weitergegeben werden, wenn sie keinen Nachteil bedeuten. Der Anteil genetischer Informationen für die anderen Töne vermengt sich mehr und mehr mit denen des »Haupttons« und geht früher oder später verloren.
Aber nicht nur für die genetische Vielfalt, auch für die Verteilung der vorhandenen Energie sind diese Pools eine wichtige Grundlage. Verteilt man diese Energie ohne Beachtung solcher Pools einfach auf alle Toids abhängig von ihrer Fitness, so würden gut angepasste Toids einer Nische in Konkurrenz treten mit evtl. weniger gut angepassten Toids einer anderen Nische. Das wiederum würde einerseits dafür sorgen, dass die Entwicklung innerhalb der einen Nische wieder gestört wäre, und andererseits evolutionsbiologisch keinen Sinn machen. Eine ökologische Nische zeichnet sich ja dadurch aus, dass Lebewesen in sie hineindrängen, die in irgendeiner Form dort hineinpassen7. Wenn nun die gesamte vorhandene Energie einer Toidworld gleichmäßig auf die vorhandenen Nischen (Pools) verteilt wird, dann haben innerhalb eines dünn besiedelten Pools aufgrund des geringeren Konkurrenzdrucks auch weniger gut angepasste Toids eine Chance, sich fortzupflanzen und sich so Generation für Generation besser an diese Nische anzupassen. Wenn man das ein wenig abstrahiert auf die Natur überträgt, wird das sehr anschaulich:

Angenommen es gibt drei kleine nah beieinander liegende Inseln. Auf der einen Insel wachsen Äpfel, auf der anderen leben Erdwürmer und auf der dritten gibt es nur Moskitos. Plötzlich kommt eine Schar von Vögeln, die zufälligerweise ganz gut darin sind, Moskitos zu jagen, auf die Moskitoinsel. Sie breiten sich auf der Insel aus und über kurz oder lang wächst die Konkurrenz unter den Vögeln um das begrenzte Nahrungsangebot. Nur die schnellsten und am besten sehenden haben eine Chance, genug Moskitos zu fangen und zu überleben.
Zufälligerweise verschlägt es nun ein paar Vögel auf die Insel mit den Würmern. Sie sind zwar nicht besonders gut darin, Würmer aus der Erde zu ziehen, da sie aber nur wenige sind und es genügend Würmer gibt, haben sie keine Probleme, zu überleben und sich fortzupflanzen. Nach vielen Generationen und einer dichten Besiedelung der Wurminsel wird sich das aber ändern und wieder haben nur die am besten angepassten eine Chance zu überleben. Zufälligerweise landen dann ein paar dieser Wurmfänger auf der Apfelinsel und das Ganze geht von vorne los.

Dieser bildliche Vergleich soll den Sinn der Pools in einer Toidworld verdeutlichen. Eine genaue Erklärung zu der Methode, wie die vorhandene Energie innerhalb eines Pools auf die in ihm befindlichen Toids verteilt wird, findet sich in der Beschreibung des Experiments »Energy-Distribution«.
Die Aufteilung der Toids in verschiedene Pools und die Verteilung der Energie, sowie die Fitness-Funktion sind in der Klasse Environment implementiert. In jedem Frame wird deren Methode void update() aufgerufen (hier reduziert auf die relevanten Zeilen):

public void update() {
    updateEnvironment();
    createMatingAndTargetPools();
    feedToids();
    matingTime();
}

In jedem Iterationsschritt werden zunächst die Umweltbedingungen aktualisiert (updateEnvironment() – mehr dazu unter »Umwelt«). Dann werden die Toids abhängig von den Umweltbedingungen und ihren Genen in verschiedene MatingPools und TargetPools eingeteilt. TargetPools sind die eben angesprochenen Nischen, MatingPools sind dann letztlich nur noch eine Spezialisierung der TargetPools. In jedem MatingPool werden nämlich alle Toids eines TargetPools gesammelt, die paarungsbereit sind.
Nach dieser Einteilung wird in der Methode feedToids() die in der Umwelt vorhandene Energie auf alle Toids verteilt (abhängig von dem TargetPool, in dem sie sich befinden, und von ihrer Fitness). Was dann in der Methode matingTime() passiert, verrät schon der Titel, wird aber im nächsten Punkt genauer erklärt.

Triebe und Fortpflanzung

Um die Fortpflanzung eines Toids zu steuern habe ich einen simplen Triebmechanismus implementiert. Dieser Mechanismus basiert auf einer Libido, die mit jedem Iterationsschritt wächst. Die Geschwindigkeit des Wachstums hängt von der Energiezufuhr des Toids ab. Je mehr Energie es bekommt, desto schneller wächst seine Libido. Nun gibt es zwei Grenzen, die diese Libido überschreiten muss, damit es zu einer Paarung kommt. Wenn die erste Grenze libidoReadyLimes erreicht ist, ist ein Toid bereit zur Paarung. Ab diesem Zeitpunkt gibt ein Toid - nennen wir es der Übersicht halber Toid A - seinen Ton von sich und wird in einen der eben erwähnten Mating-Pools aufgenommen. Welcher Mating-Pool das ist hängt von dem Zielton ab, dem das Toid A am nächsten steht. Innerhalb dieses Mating-Pools sucht jedes Toid dann ein möglichst attraktives anderes Toid zur Paarung. Die Attraktivität eines Toids wird an seiner Energiezufuhr gemessen. Je mehr Energie es aufnimmt, desto attraktiver ist es. Wichtig ist, dass das andere Toid - hier Toid B genannt - so attraktiv ist, dass durch diese Attraktivität Toid A die nächste Libido-Grenze überschreitet (libidoMatingLimes). In der Folge kommt es zur Paarung. Falls das nicht passiert, wächst A's Libido weiter, sodass sie irgendwann so hoch ist, dass auch ein weniger attraktives Toid für eine Paarung angemessen erscheint. Dieser Mechanismus liegt in folgender Methode der Klasse Toid:

/**
 * Checks if Toid wants to mate with a given partner
 * @param partner
 * @return boolean
 */

public boolean wannaMate(Toid partner) {
    return (libido + (partner.getAttractiveness() * 100) > libidoMatingLimes);
}

Ein Partner mit der höchst möglichen Attraktivität von 1 erwirkt das Erreichen des libidoMatingLimes damit wesentlich schneller, als ein Partner mit einer Attraktivität von 0,3. Allerdings wäre es schlecht, wenn ein Toid sich überhaupt nicht paaren würde, solange kein sehr attraktives Toid verfügbar ist. Falls zu einem gewissen Zeitpunkt nämlich einfach kein sehr attraktives Toid existierte, würde eine Population wegen fehlender Nachkommen sehr schnell aussterben. Mit diesem Mechanismus ist einerseits sichergestellt, dass besser angepasste Toids bevorzugt werden, zur gleichen Zeit aber das Überleben einer Population gesichert ist.
Wie bereits angedeutet, passiert dieser Paarungsprozess in der Methode void matingTime() der Klasse Environment. Der für den gerade beschriebenen Prozess relevante Teil der Methode sieht so aus:

for (SquareToid a  : squareToids) {
    for (SquareToid b : squareToids) {
        if(a != b && a.wannaMate(b) && b.wannaMate(a)) {
            SquareToid baby = new SquareToid(a.mate(b), p);
            babies.add(baby);
        }
    }
}

Natürlich müssen beide Toids (a und b) gegenseitiges Interesse bekunden, bevor es zu einer Paarung kommt. Und ein Toid sollte sich nicht mit sich selbst paaren, daher die Bedingung a != b. Nach der Paarung wird die Libido beider Toids wieder auf 0 gesetzt und das Spiel beginnt von vorne. Da bei ungefähr gleichen Umweltbedingungen das Wachstum der Libido und die Bereitschaft zur Paarung auch wieder ungefähr so lange dauern wie davor, und jedes paarungsbereite Toid wieder anfängt seinen Ton zu spielen, entsteht in der Klanglandschaft eine relativ konstante Polyrhythmik. Allerdings könnte es passieren, dass ein Toid paarungsbereit ist, deshalb seinen Ton erklingen lässt, aber einfach kein anderes Toid zur Paarung findet. Dadurch würde es seinen Ton ohne Pause von sich geben, bis es stirbt. Bei ca. 20 Sekunden Lebenszeit und einem unter Umständen sehr hohen Ton könnte das unangenehm für den Zuhörer sein. Deshalb habe ich in der Klasse SquareToid noch einen kleinen Mechanismus hinzugefügt, der diesen Fall eleganter löst:

if ((wannaMate() || isMating()) && soundTime < 30 ) {
    amp = 0.005f + (energy * 0.1f);
    synth.setAmp(amp);
    soundTime += 0.2;
} else {
    if(amp > 0) {
        amp -= 0.01;
        soundTime = 60;
    } else {
        soundTime -= 0.5;
        if(soundTime < 31) {
            soundTime = 0;
        }
    }
    synth.setAmp(amp);
}

Nach einer bestimmten Zeit blendet ein Toid seinen Ton aus und macht eine kurze Pause, dann ertönt der Ton wieder von neuem. Dadurch fügt sich auch ein Toid, das kein anderes Toid zur Paarung findet, in den polyrhythmischen Gesamtklang ein.

So sinnvoll und praktisch eine Verteilung aller paarungswilligen Toids auf passende Mating-Pools auch ist, gibt es trotzdem Situationen in denen dieser Umstand zu Problemen führen kann. Wenn zum Beispiel alle Toids zufälligerweise in einem Pool sind und alle anderen Nischen nicht besiedelt sind, könnte es durch eine Mutation durchaus passieren, dass ein Toid plötzlich in einem anderen Pool landet. Wäre dieses Toid dort bis an sein Lebensende alleine, könnte es keine Nachkommen produzieren und diese Nische nicht weiter besiedeln. Deshalb werden Mating-Pools unter einer gewissen Größe (unter 3 Toids) einfach mit einem größeren Pool zusammengeführt. So kann der kleinere Pool für einen kurzen Übergangszeitraum von der genetischen Variabilität des größeren Pools profitieren.

Umwelt

Die Umweltbedingungen, die im Laufe der Erklärung des Toids hin und wieder erwähnt wurden, bestehen aus einer gewissen Menge vorhandener Energie und einem Akkord, der auf einem Grundton aufbaut.

Alle Eigenschaften und Methoden der Energiemenge sind in der Klasse Energy implementiert. Eine wichtige Eigenschaft dieser Energie ist, dass sich ihre verfügbare Menge in jedem Iterationsschritt pseudozufällig ändert. Allerdings tut sie das nicht sprunghaft, sondern in organisch anmutender Weise. Grund dafür ist die zugrundeliegende Perlin-Noise-Funktion8, die in jedem Iterationsschritt einen neuen Wert definiert.
Eine weitere wichtige Aufgabe dieser Klasse ist die Fitness- und Pool-abhängige Verteilung der Energie auf alle Toids. Diese Aufgabe wird von der Methode calculateEnergyUnitByPool erfüllt. Zunächst wird die Energie gleichmäßig auf alle Pools verteilt. Dann wird abhängig von den Fitnesswerten aller in einem Pool befindlichen Toids bestimmt, wie hoch die Energieeinheit ist, die jedem Toid dieses Pools mit seiner Fitness multipliziert zugeführt wird. Das ist so erklärt vielleicht ein bisschen schwierig nachzuvollziehen, wird aber genauer und anschaulicher in der Beschreibung des Experiments »Energy-Distribution« erklärt.

Die andere maßgebende Umweltbedingung ist der vorgegebene Akkord und dessen Grundton, an denen die Fitness aller Toids gemessen wird. Sie wird in der Klasse Key gesteuert. Im Vergleich zur vorhandenen Energie ändert sich der vorgegebene Akkord nicht ständig, sondern nur von Zeit zu Zeit. Genauer gesagt bleiben ein Akkord und sein Grundton 70 - 160 Sekunden stabil und ändern sich dann zufällig in einen neuen Akkord und Grundton. Der Übergang zum neuen Akkord verläuft nicht sprunghaft, sondern wird ebenso möglichst flüssig über eine Dauer von 40 Sekunden interpoliert. Das entspricht einerseits eher einer Umwelt, wie wir sie kennen, die sich normalerweise – abgesehen von Katastrophen – nicht von einem auf den anderen Moment sprunghaft verändert, sondern langsam, zum Teil über viele Jahrmillionen hinweg. Und andererseits hilft es auch den Toids in Toidworld, sich neuen Bedingungen anpassen zu können.

Die folgende kurze Demonstration soll in stark beschleunigter Form Tonartwechsel in Toidworld veranschaulichen (allerdings mit dem Unterschied, dass man in Toidworld den Akkord der Umwelt nicht hört oder sieht).

SuperCollider

Sämtliche Audiosynthese der Installation passiert in einer SuperCollider-Serverinstanz. SuperCollider ist eine Entwicklungsumgebung und Programmiersprache für Echtzeit-Audiosynthese und algorithmische Komposition, entwickelt von James McCartney und seit 2002 unter der GNU General Public License als freie Software lizenziert. SuperCollider ist sehr effizient, sodass es kein Problem darstellt, mehrere hundert Töne gleichzeitig zu generieren und zu modifizieren.

Da ich bereits angefangen hatte, mit evolutionären Algorithmen in Processing zu experimentieren, wollte ich gerne weiterhin auf die vielfältigen Möglichkeiten dieser Bibliothek zugreifen können. Glücklicherweise hat Daniel Jones die Bibliothek processing-sc entwickelt, die eine bequeme OSC-Brücke zwischen Processing und SuperCollider schlägt.

In SuperCollider habe ich diesen sehr simplen Synthesizer geschrieben:

(
    SynthDef(\fadingXY, {
        |freq = 60, attack = 1, decay = 1, gate = 1, amp = 0.2, panX = 0, panY = 0|

        var env = EnvGen.kr(Env.asr(attack, 1, decay, -2), gate: gate, doneAction: 2);
        var data = SinOsc.ar(freq.midicps, 0, amp)* env;
        data = Pan4.ar(data, panX, panY);
        Out.ar(0, data);
    }).add;
)

Diesen Synthesizer kann nun jedes Toid ansteuern und so seinen Ton generieren. Dabei kann es Frequenz, Lautstärke, und Position im Raum festlegen und auch weiterhin laufend verändern. Um die Steuerung dieses speziellen Synthesizers noch weiter abzukapseln, habe ich eine Klasse FadeSynth geschrieben, die Daniel Jones Klasse Synth erweitert. Jedes Toid enthält einen solchen FadeSynth. Bei der Erzeugung eines neuen Toids wird seine Instanz von FadeSynth initialisiert. In jedem Iterationsschritt passt ein Toid dann abhängig von seiner Energiezufuhr die Lautstärke seines Tons an. Stirbt ein Toid, wird sein Ton ausgeblendet und dessen Serverinstanz aufgelöst.

Tracking

Wie bereits beschrieben ist die visuelle Repräsentation eines Toids ein Lichtstrahl innerhalb eines Projektionskegels. Ein Benutzer soll nun die Möglichkeit bekommen, mit einzelnen oder mehreren Strahlen innerhalb der Projektion zu interagieren. Das heißt, das System soll registrieren, wenn ein Benutzer einen Strahl berührt und dazu auch wissen, welchen Strahl er berührt. Das soll für jede Stelle des Strahls zutreffen: von ganz unten, an der Stelle, an der die Strahlen gerade den Projektor verlassen, bis zum höchsten Punkt, den der Benutzer mit seiner Hand erreichen kann. Es gibt mehrere Möglichkeiten, ein solches Tracking zu bewerkstelligen. Eine Möglichkeit wäre, das Bild, das den Projektor verlässt mit dem Bild zu vergleichen, das oben an der Projektionsfläche ankommt: Jeder Punkt, der oben fehlt wurde höchstwahrscheinlich auf dem Weg dorthin unterbrochen, also berührt. Obwohl ich diese Methode nicht getestet habe bin ich überzeugt, dass sie funktionieren würde; ich fand sie jedoch für diesen Zweck nicht praktikabel: Die Kamera, die das projizierte Bild aufnehmen sollte, müsste theoretisch hinter der Projektionsfläche positioniert sein. Man bräuchte also eine Rückprojektionsleinwand und einen gewissen Abstand dieser Leinwand zur Decke, um eine Kamera anbringen zu können, die die komplette Leinwand einfängt. Würde man die Kamera davor positionieren, wäre das eingefangene Bild eventuell gestört von den Lichtstrahlen beziehungsweise von Benutzern, die im Bild stünden.

Eine andere und meiner Meinung nach wesentlich unkompliziertere Methode ist es, eine Kinect an der Decke am Rand der Projektion zu befestigen, auf den Projektionskegel zu richten und die rohen Tiefendaten dieser 3D-Kamera zu analysieren.

Kinect und Nebel

Als ein mögliches Problem zeichnete sich bei dieser Methode im Vorfeld ab, dass die Kinect durch den relativ dichten Nebel gestört würde und so keine zuverlässigen Daten liefern könnte. Leider konnte ich keine wirklich aussagekräftigen Experimente dazu finden. In einem kurzen Versuch konnte ich aber glücklicherweise feststellen, dass die Reichweite der Kinect bei der gewünschten Nebeldichte zwar eingeschränkt ist, allerdings nicht so weit, dass es für diese Installation ein großes Problem darstellen würde. Erst ab ca. 3,5 Metern wurden die Daten unzuverlässig.

Transformation und Projektion

Das Tracking wird vom Programm Tracking übernommen. Tracking ist ebenfalls ein Java-Programm auf Basis der Processing-Bibliothek. Für die Verwendung der Kinect in Processing benutzte ich die Bibliothek openkinect von Daniel Shiffman9. Über diese Bibliothek kann man sehr einfach auf die Rohdaten der Kinect zugreifen. Diese Rohdaten sind eine Wolke aus Punkten im Raum, die ein Tiefenbild der von der Kinect gescannten Szenerie bilden. Um nun zu wissen, ob die Hand oder irgendein anderes Körperteil eines Benutzers in den Projektionskegel hineinragt, positioniert man eine virtuelle Kamera in der Kinect-Szenerie genau an der Stelle, an der in der physischen Welt der Projektor steht. Nun passt man die Brennweite der virtuellen Kamera an die Brennweite des Projektors an, sodass Projektionskegel und Kamerakegel übereinanderliegen. Alles, was diese virtuelle Kamera jetzt sieht, ragt in der physischen Welt in den Projektionskegel hinein. Um dieses Mapping möglichst unkompliziert zur Laufzeit anpassen zu können, habe ich ein kleines Interface dafür gebaut.

Tracking-UI mit virtuellem Kamerafrustum

Das projizierte Bild der Installation ist zweidimensional und wird lediglich durch die Art der Projektion für den Benutzer dreidimensional erfahrbar. Jeder Lichtstrahl ist aber letztendlich durch zweidimensionale Koordinaten auf einer Fläche definiert. Um nun zu wissen, welchen Lichtstrahl ein Benutzer in dem dreidimensionalen Kegel berührt, muss man lediglich alle von der virtuellen Kamera gesehenen Punkte wieder auf eine Ebene projizieren.

Projektion

Diese Skizze veranschaulicht eine solche Projektion. Der Punkt P wird als P' auf eine Ebene projiziert.
Diese Projektion aller von der Kinect getrackten Punkte findet in der Klasse ProjectionSpace statt. Dabei werden auch direkt alle Punkte herausgefiltert, die sich nicht innerhalb des Projektionskegels befinden.

Cluster und Convexhull

Im nächsten Schritt muss sichergestellt werden, dass jedes in die Projektion ragende Objekt als einzeln erkannt wird. Dazu ist eine Clusteranalyse nötig. Zwar bildet jedes Objekt im Raum eine eigene Punktewolke, diese Wolken werden von der Kinect jedoch nicht als einzelne Objekte erkannt, sondern als ein großes Objekt zusammengefasst. Wir müssen also die große unzusammenhängende Wolke in kleine zusammenhängende Wolken (Cluster) zerlegen. Glücklicherweise existieren bereits Algorithmen für dieses Cluster-Problem. In der Methode getClusters der Klasse Projektionspace ist ein solcher Clustering-Algorithmus10 implementiert.

Nun muss noch der zweidimensionale Umriss jedes Clusters errechnet werden, also eine konvexe Hülle, die alle Punkte eines Clusters einschließt. Dafür gibt es sogenannte Convex-Hull-Algorithmen, von denen einer bereits in Java implementiert11 ist, den ich lediglich an meine Code-Struktur anpassen musste. Die nötigen Methoden dafür sind in der Klasse ConvexHull implementiert.

Cluster- & ConvexHull-Analyse der Kinect-Daten

Übertragung an Toidworld

Nach diesem Prozess sind die relativ umfangreichen Daten der dreidimensionalen Punkte-Wolke auf wenige zweidimensionale Punkte reduziert. Diese Punkte stellen grobe Polygonumrisse der Körperteile dar, die in die Projektion hineinragen. Diese geringe Datenmenge kann man nun sehr schnell über das OSC-Protokoll12 an das Programm Toidworld übertragen. Die Anwendung Tracking läuft bei ungefähr 30 fps. In jedem Frame werden die Kameradaten analysiert und übertragen. Jeder Umriss wird dabei in eine einzelne OSC-Message geschrieben. Am Ende werden dann alle OSC-Messages gebündelt und übertragen. Implementiert ist dieser Prozess in der Klasse Communicator.

Projektionsbild von Toidworld – Debug-Ansicht mit Polygonumrissen der interagierenden Arme

Monitor

Würde man die Installation ohne den Monitor betrachten, wären die zugrundeliegenden Prozesse wohl nur schwer nachvollziehbar. Der Monitor soll anhand grafisch aufbereiteter Echtzeitdaten einen abstrakten Einblick in die Vorgänge von Toidworld geben, um so ein Bewusstsein für den evolutionären Prozess dahinter zu schaffen. Er muss allerdings nicht bis ins letzte Detail erklären, was in dieser Installation passiert.

Über eine OSC-Verbindung werden permanent aktuelle Daten aus Toidworld an das Programm Monitor übergeben.

Screenshot Monitor

Energie und Population

Wie bereits erklärt spielt die vorhandene Menge an Energie in Toidworld eine wichtige Rolle. Ist viel Energie vorhanden, kann die Population wachsen, bei wenig Energie hingegen wird sie schrumpfen. Genau dieser Zusammenhang kann sehr deutlich über einen klassischen Kurvenverlauf der beiden absoluten Größen Energiemenge und Populationsgröße dargestellt werden. Die beiden Kurven stellen jeweils den Verlauf der letzten 20 Sekunden dar.
Analog dazu wird in einem Kuchendiagramm das Verhältnis konsumierter und noch freier Energie visualisiert. Der schwarze Teil des Kuchens zeigt, wie viel Energie im Moment von den Toids verbraucht wird. Der graue Teil hingegen steht für die Energie, die noch verfügbar ist. Theoretisch lässt sich an diesem Diagramm ein kurzfristiger Trend der Population ablesen: Wenn noch viel Energie verfügbar ist, kann man davon ausgehen, dass die Population wachsen wird. Bei wenig oder gar keiner freien Energie wird die Population wohl schrumpfen.

Energie- und Populationskurve / Kuchendiagramm mit Verhältnis verbrauchter (schwarz) und freier Energie

Pools und Toids

Ein weiterer wichtiger Aspekt sind die vorhandenen Pools (Nischen) innerhalb der Umwelt. Jeder Pool wird in der Grafik repräsentiert von einem Kreis. Über diesem Kreis steht die Zahl des Tons, der diesen Pool bildet. Da die vorhandene Energie der Umwelt gleichmäßig auf die verschiedenen Pools verteilt wird, ist die Fläche jedes Kreises wieder ein Kuchendiagramm, das anzeigt, wie viel Energie innerhalb des Pools verbraucht wird und wie viel noch frei ist.

Im Inneren eines Kreises wird für jedes Toid, das in diesem Pool existiert, wiederum ein kleiner Kreis gezeichnet. Je opaker dieser Kreis gefüllt ist, desto mehr Energie konsumiert das jeweilige Toid. Insgesamt werden diese Toids mit Hilfe eines Sonnenblumenkern-Algorithmus innerhalb des Pools angeordnet. Das ist einerseits sehr platzsparend und hat andererseits den Vorteil, dass gleichmäßig neue Toids hinzgefügt werden können, ohne die Position der anderen anpassen zu müssen. Dadurch zieht dieser relativ chaotische Prozess permanenter Zu- und Abnahme der Toids nicht zu viel visuelle Aufmerksamkeit auf sich. Gleichzeitig kann man aber dennoch gut nachvollziehen, ob die Zahl der Toids gerade zu- oder abnimmt.

Die Farbe der Toids ist in jedem Pool anders. Da es maximal fünf Pools gibt (das ist die maximale Größe der vorhandenen Akkordpattern) habe ich eine Farbreihe von fünf gut unterscheidbaren Farben zusammengestellt.

Ein Pool und alle darin enthaltenen Toids

Toids auf dem Frequenzband

Diese farbliche Kodierung der Toids ist wichtig für einen weiteren Zusammenhang, den die Grafik auf dem Monitor verdeutlichen soll. Wie bereits erklärt, wird ein Pool gebildet durch einen Ton, der von der Umwelt zusammen mit anderen Tönen eines Akkordes als Umweltbedingung vorgegeben wird. So sammeln sich in diesem Pool alle Toids, die diesem Ton am nächsten sind, und zwar unabhängig vom Oktavraum, in dem sie sich befinden. Das heißt im selben Pool kann theoretisch ein Toid mit einem sehr tiefen C und eines mit einem sehr hohen C existieren. Diesen Umstand kann man am deutlichsten mit einer Art Frequenzband veranschaulichen. Auf diesem Frequenzband verteilt man alle existierenden Toids in Form von aufrecht stehenden weißen Balken. Je tiefer der Ton eines Toids ist, desto weiter links auf dem Band steht sein Balken. Je höher der Ton eines Toids ist, desto weiter rechts wird es stehen.
Insgesamt umfasst das Frequenzband alle sechs Oktavräume, die von Toids bevölkert werden können. Das heißt der Zielton eines jeden Pools ist sechsmal auf diesem Frequenzband vorhanden und wird daher auch sechsmal auf diesem Frequenzband an den jeweiligen Stellen als Balken eingezeichnet. Damit diese Balken von denen der Toids zu unterscheiden sind, werden sie in der ihrem Pool entsprechenden Farbe gefüllt. Es gibt also zwei Ansichten eines Pools: die Ansicht des Kreises, in dem alle ihm angehörigen Toids gesammelt sind und die Ansicht des Frequenzbandes, auf dem der Zielton eines Pools in allen Oktavräumen markiert ist. Die Farbe der Toids in der Kreisansicht ist dieselbe wie die der Zieltonbalken in der Frequenzbandansicht.

Auf diesem Frequenzband kann man nun sehr gut ablesen, welcher Toid welchem Zielton wie nahe steht. Je mehr Energie ein Toid bekommt, desto lauter ist er. Diese Lautstärke wiederum lässt sich an der Größe des weißen Balkens ablesen, der ein Toid auf diesem Frequenzband repräsentiert. Je größer dieser Balken ist, desto lauter ist das dahinter stehende Toid.

Ausschnitt des Frequenzbandes

Ausschnitt des Frequenzbandes, wenn der Benutzer die Maus darüber positioniert

Visueller Aufbau

Visuell ist der Monitor bewusst in sehr dunklen und kontrastarmen Grautönen schattiert. Der Hintergrund ist dunkelgrau, sodass sich die Grafiken in Schwarz und etwas hellerem Grau deutlich genug davon abzeichnen. Der Grund dafür ist folgender: Die Installation befindet sich in einem komplett dunklen Raum, sodass die projizierten Lichtstrahlen möglichst grell und kräftig erscheinen. Da sich dieser Monitor in der Nähe der Installation befindet und man im besten Fall seinen Blick direkt vom Monitor auf die Projektion schweifen lassen kann, muss die allgemeine Helligkeit des Monitors so gering sein, dass er nicht in Konkurrenz mit den Lichtstrahlen der Projektion tritt.

Bei der visuellen Konzeption des Monitors waren mir drei Dinge wichtig:

Das Gefühl, dass es sich um dynamische Echtzeitwerte handelt, lässt sich wohl am einfachsten durch permanente, flüssig animierte Änderungen aller Elemente verstärken. Ergänzt werden die Diagramme durch Textelemente, die die Aktualität des Geschehens verdeutlichen, z.B. "Entwicklung der Population innerhalb der letzten 30 Sekunden", oder "Aktueller Energiekonsum". Auch die Position und Ausrichtung des Monitors im Verhältnis zur Projektion kann dieses Bewusstsein verstärken. Wenn man beinahe gleichzeitig auf den Monitor und die Projektion blicken kann, wird man automatisch versuchen, einen Zusammenhang zwischen beiden zu erkennen.

Um dem Ganzen einen möglichst organischen Charakter zu verleihen, habe ich mich verschiedener Symbole bedient. Zum einen ist der Monitor sehr symmetrisch aufgebaut – eine Eigenschaft, die beinahe alle Lebewesen in der Natur gemeinsam haben. Zum anderen ist der Energiefluss in einer gewissen kaskadenartigen Struktur dargestellt, um den Eindruck eines »Flusses« zu verstärken. Dieser Aufbau weckt aber auch Assoziationen zu Baumstrukturen. Auch die Anordnung der Toids innerhalb der Pools mit Hilfe des Sonnenblumenkern-Algorithmus ist eine starke Analogie zu organischen Formen. Darüber hinaus weckt der Anblick von Punkten innerhalb eines Kreises in diesem Zusammenhang möglicherweise auch das Bild einer Bakterienkultur innerhalb einer Petrischale.

Ein sehr typisches und klassisches Element von Klangdarstellung ist ein Frequenzband, auf dem in den verschiedenen Bereichen des Bandes Balken im Rhythmus der Musik ihre Größe ändern. Solche Visualisierungen findet man beinahe auf jedem Mischpult und im Display fast jeder Stereoanlage. Diese allgemeine Darstellung von Tönen habe ich bei der analogen Verteilung aller Toids auf einem Frequenzband in dieser Grafik übernommen, um den Betrachter diesen Bezug möglichst leicht herstellen zu lassen.

Interaktion

Der Benutzer kann über ein Trackpad einen Mauszeiger über alle Elemente bewegen. Bei allen Elementen erscheint dabei ein kleines Popup, das den genaueren Kontext kurz erläutert.

Popup Pool

Popup Toid

Popup Frequenzband

Experimente

Im Folgenden finden sich die Dokumentationen einer Auswahl von kleineren Experimenten, die sich im Laufe der Arbeit an Evolution Of Silence ergeben haben. Ziel der Experimente war es, mein Verständnis für verschiedene relevante Bereiche der Thematik zu vertiefen. Sie haben keinen wissenschaftlichen Anspruch.

Meet And Mate

Motivation

Im Rahmen dieses Experiments wollte ich Erfahrungen darüber sammeln, mit welchen Methoden man ein "natürliches" Paarungsverhalten unter virtuellen Agenten erreichen und visualisieren kann. Zu diesem Paarungsverhalten gehören neben der Wahl eines Geschlechtspartners auch die Fragen, wie sich Agenten räumlich so nahe kommen, dass sie eine Verbindung miteinander eingehen können und wie das "Bedürfnis" entsteht, überhaupt eine Verbindung einzugehen. Um diese Dinge nicht unnötig zu verkomplizieren, findet dieses Experiment in einem zweidimensionalen, rechteckig begrenzten Raum statt.

Aspekte des Experiments

Triebe

Es liegt sehr nahe, sich die Grundzüge des Paarungsverhaltens von der Natur abzuschauen. Natürlich nur sehr vereinfacht und abstrakt, da das sehr komplexe Zusammenspiel von Hormonflüssen, Trieben und Begattungsritualen bei echten mehrzelligen Lebewesen den Rahmen dieses Experiments und dieser Arbeit sprengen würde und wahrscheinlich noch gar nicht so erschöpfend untersucht wurde, dass man ihn so ohne weiteres simulieren könnte.
Hier werden Fortpflanzungstriebe auf einfache numerische Größen in Variablen reduziert, die je nach Verhältnis zueinander das Verhalten des Agenten beeinflussen.

Eigentlich reicht für die Steuerung des Paarungsverhaltens ein einziger Trieb: die Libido. In jedem Iterationsschritt (die Zeiteinheit der virtuellen Welt, in der die Agenten "leben"), wächst die Libido um einen gewissen Wert. Wenn der Wert eine gewisse Grenze überschreitet (Libidolimes), ist der Agent bereit zur Paarung. Trifft er auf einen anderen ebenfalls paarungswilligen Agenten, paaren sie sich. Nach der Paarung ist die Libido wieder auf 0 und das Spiel beginnt von vorne.
Solange nur die Libido als Trieb existiert, dauert die Paarung allerdings nur einen Iterationsschritt und ist für den Betrachter somit nicht wirklich nachvollziehbar. Dies kann man ändern, indem man eine zweite Variable einführt, die Befriedigung. Während der Paarung steigt der Wert der Befriedigung in jedem Iterationsschritt an. Erreicht die Befriedigung einen gewissen Grenzwert, wird die Libido auf 0 gesetzt und die Paarung beendet. Wenn dann die Libido wieder wächst, verringert sich die Befriedigung zur selben Zeit. Überschreitet die Libido wieder den Grenzwert Libidolimes und ist die Befriedigung unter einen gewissen Wert gefallen, ist der Agent wieder bereit zur Paarung.

Finden und verbinden

Um sich irgendwie in einem Raum finden und annähern zu können, braucht man die Möglichkeit, sich zu bewegen. Im Laufe des Experiments habe ich verschiedene Fortbewegungsmechanismen ausprobiert: Z.B. bekam jeder Agent bei Programmstart eine zufällige Geschwindigkeit in eine zufällige Richtung. Traf er auf eine begrenzende Seitenwand, prallte er einfach mit derselben Geschwindigkeit wieder ab. Trafen sich zufälligerweise zwei paarungsbereite Agenten, d.h. näherten sie sich bis auf einen gewissen Abstand aneinander an, bildete sich plötzlich eine starke Anziehungskraft zwischen beiden, riss sie aus ihrer normalen Laufbahn und verband sie für eine gewisse Zeit miteinander. Wenn die Paarung vollzogen war, löste sich die Verbindung wieder und beide Agenten bekamen jeweils eine neue Geschwindigkeit und Richtung. Die komplette Physik dahinter basiert auf der Verletphysics-Bibliothek von Karsten Schmidt13. Diese Bibliothek enthält unter anderem sogenannte Particles und Springs. Ein Particle hat Partikel-typische Eigenschaften wie eine Position im Raum, eine Geschwindigkeit, ein Gewicht etc. Jeder Agent ist eine Erweiterung eines solchen Particle um die eben beschriebenen Triebe. Springs (deutsch: Sprungfedern) fungieren als eine Art Verbindungsband und können zwei Particles auf vielfältige Weise physisch miteinander verbinden. Man kann bei einem Spring festlegen, wie lang er sein soll und welche Spannkraft er hat. Wenn man nun plötzlich zwischen zwei Agenten eine solche Spring-Verbindung herstellt, können durch die Spannkraft des Springs unter Umständen große Kräfte freiwerden und die beiden Agenten heftig aufeinander zu ziehen. Wenn die Verbindung dann wieder gelöst wird, ist jeder Agent meist ein bisschen schneller als zuvor – bis zu einem gewissen Maximum natürlich, das wohl mit der Spannkraft des Springs zusammenhängt.

Im Experiment führte das dazu, dass beinahe jede Paarung die Agenten beschleunigte und die Bewegung im Raum insgesamt chaotischer und schneller wurde. Die Paarung und das Eingehen von Verbindungen wirkten also wie ein treibender Motor innerhalb der Agenten. Um diesen Effekt noch zu verstärken, ließ ich die Agenten am Anfang komplett ohne Geschwindigkeit starten. Durch die Zufällige Positionierung jedes Agenten waren immer ein paar nah genug zusammen, um sich zu paaren. Da sie durch diesen Vorgang heftig aneinander gerissen wurden, bekamen sie eine Geschwindigkeit in eine zufällige Richtung, was dazu führte, dass sie wieder auf andere eventuell noch still stehende Agenten trafen und diese durch eine Paarung ebenfalls in Bewegung versetzten. Im weiteren Verlauf experimentierte ich auch mit der Anzahl der Verbindungen, die ein Agent gleichzeitig eingehen kann. Dadurch bildeten sich zum Teil interessante geometrische Drahtfiguren aus der Verbindung mehrerer Agenten. Die Demonstration am Ende dieses Kapitels zeigt eine Version, in der jeder Agent bis zu drei Verbindungen gleichzeitig eingehen konnte.

Audiovisueller Aufbau

Natürlich sollten die Agenten auch schon irgendeinen Ton von sich geben. So hatte jeder Agent einen zufällig bestimmten Ton, der allerdings nicht sofort erklang. Um die Dynamik des Paarungsprozesses auch in der Klanglandschaft abzubilden, erklang der Ton eines Agenten erst, wenn er sich paarte und verstummte wieder, wenn er damit fertig war.

Visuell wurden die Agenten durch kleine weiße Kreise auf schwarzem Untergrund dargestellt. Die Größe des Radius spiegelte den Grad der Befriedigung wieder, die Deckkraft des Kreises repräsentierte die Libido. Gingen zwei Agenten eine Verbindung ein, wurden sie durch eine weiße Linie verbunden und fingen an zu vibrieren und in ihrem Ton zu erklingen. Die Länge der Linie wurde durch den mittleren Tonwert der beiden verbundenen Agenten bestimmt. Je tiefer dieser Ton, desto länger war die Linie.

Demonstration

Frequency-Fitness

Motivation

In vorangegangenen Experimenten hatte ich immer einen vorgegebenen Ton, an den sich eine Population anpassen musste. Wenn eine gewisse Anpassung erreicht war, wurde der Ton geändert und die Population entwickelte sich dahingehend. Interessant für eine Soundskultpur ist aber auch die Möglichkeit, eine ganze Skala von Tönen hervorbringen zu können. In diesem Experiment habe ich nach einer Methode gesucht, mit der ich die Fitness eines Tons gegenüber beliebig vielen vorgegebenen Tönen feststellen kann.

Die Fitness-Funktion

Die Fitness eines Tons hängt von seinem Abstand von einem Zielton ab. Je kleiner dieser Abstand ist, desto ähnlicher klingt der Ton dem Zielton und desto höher ist seine Fitness. Die Fitness ist also umgekehrt proportional zur Distanz d zwischen Ton und Zielton. Mit der Funktion fitness = 1/d bekommt man genau dieses gewünschte Verhältnis. Allerdings geht fitness mit kleiner werdendem d immer weiter gegen unendlich und wenn d = 0 sein sollte, ist fitness nicht mehr definiert. Um das zu verhindern, habe ich den Wertebereich von d nach unten begrenzt, sodass er niemals 0 sein kann. Das heißt, dass ab einer gewissen Mindestdistanz zum Zielton der Fitness-Wert nicht mehr ansteigt.

Bei mehreren Zieltönen muss nun vor der Berechnung der Fitness nur festgestellt werden, welchem Zielton ein Ton am nächsten ist. Der Abstand zu diesem Ton wird dann das d in der Fitness-Funktion.

Die Zieltöne setzen sich zusammen aus einem Grundton und einem Akkordpattern, das auf dem Grundton aufbaut. Bei einem Grundton von 7 und einem Dur-Akkordpattern von {0, 4, 7} könnte man nun alle Zieltöne mit 7 + (n * 12), 11 + (n * 12), 14 + (n * 12) abbilden, bei einem n im Wertebereich der natürlichen Zahlen inklusive 0. Dieses n würde das Pattern dann in allen Oktavräumen darstellen. Der Ton 43 wäre also ideal angepasst, da er genau auf 7 + (3 * 12) liegt. So könnte man nun den Zielton eines Tons herausfinden, indem man einfach jeden möglichen Zielton in allen relevanten Oktavräumen durchgeht und die Distanz zum Ton misst.
Das wäre allerdings ziemlich aufwendig. Einfacher ist es, den Ton auf den Oktavraum zwischen 0 und 11 zu »normalisieren« und dann nur einmal die Distanz zu jedem Zielton zu messen. Hier ist die Funktion in Java implementiert:

private float fitness(float note) {
    // Note normalisieren (Grundton soll auf 0 liegen)
    note -= keymod;
    float mod = note % 12;

    float nearestTone = 12;
    for (int i = 0; i < key.length; i++) {
        nearestTone = (Math.abs(key[i] - mod) < nearestTone) ? Math.abs(key[i] - mod) : nearestTone;
    }

    float fitness = map(mouseY, 0, height, 0.2f, 3f) / max(0.02f, nearestTone);
    return fitness;
}

Zunächst wird nun in einer for-Schleife die Distanz zum nächsten Zielton gemessen. Am Ende wird dann die fitness errechnet mit der zuvor erwähnten Funktion 1/d. Allerdings im Rahmen dieses Experiments noch mit einem dynamischen Dividenden. Diesen Dividenden kann der Benutzer mit der y-Position der Maus in einem Bereich zwischen 0.2 und 3 bestimmen. So konnte man interaktiv die Auswirkungen des Dividenden erfahren. Am Ende habe ich mich dann doch für den Wert 1, also die Funktion 1/d entschieden.

Demonstration

DNA-Variabilität

In diesem Experiment habe ich verschiedene Methoden ausprobiert, einen Ton genetisch zu kodieren. Um diese Methoden zu testen, habe ich das Programm DNAVariability geschrieben, in dem sich eine variable Population von virtuellen Kreaturen über einen genetischen Algorithmus entwickelt. Dabei wird jeweils eine der in diesem Kapitel vorgestellten Methoden zur Genexpression verwendet. Um die Populationsentwicklung nachvollziehbar zu machen, wird der Prozess visualisiert und sonifiziert. Im Folgenden werden auch zwei Bildschirmaufnahmen dieses Programms zu sehen sein. Damit man diese besser verstehen kann, erkläre ich kurz, wie die Programmoberfläche aufgebaut ist:
Über ein Interface am oberen Rand des Fensters kann man das Programm steuern. Hier kann man folgende Parameter festlegen:

Visuell werden die Phänotypen aller Lebewesen durch vertikale Linien dargestellt. Je tiefer der Ton eines Wesens, desto weiter links steht seine Linie, je höher, desto weiter rechts. Durch horizontale Linien wird die Verteilung der Gene dargestellt. Jedes Gen hat einen Wert zwischen 0 und 1. Dieser Wert bestimmt die y-Position einer weißen Linie, die dieses Gen darstellt. 0 ist dabei ganz oben und 1 ganz unten.
So kann man gleichzeitig sehen, in welchem phänotypischen Bereich sich die Population befindet und wie die zugrundeliegenden Gene verteilt sind, d.h. welche Gene sich durchsetzen.

Addition

Methode

Addition der Gene und Mapping auf einen Tonbereich:

  1. Der Phänotyp (z.B. eine Tonhöhe zwischen 0 und 11) wird durch n Gene (jeweils eine Fließkommazahl zwischen 0 und 1) kodiert.

  2. Jedes Gen wird gemappt auf einen Wertebereich zwischen Phänotyp-Minimum (0) und dem durch n geteilten Phänotyp-Maximum (11 / n). Diese Werte werden dann addiert und ergeben den Phänotyp.

Wenn alle n Gene zufällig den Wert 1 haben, kodieren sie den höchsten Ton (11). Wenn alle Gene den Wert 0 haben, kodieren sie den niedrigsten Ton (0). Aus allen anderen Kombinationen ergeben sich alle Zwischenwerte.

pheno = 0, n = 3;
pheno += map(gene1, 0, 1, 0, 11 / n);
pheno += map(gene2, 0, 1, 0, 11 / n);
pheno += map(gene3, 0, 1, 0, 11 / n);

Ergebnisse

Je höher n ist, desto höher bleibt auch die genetische Variabilität, da mit der Zahl der Gene auch die Kombinationsmöglichkeiten zwischen zwei Genomen steigt.
Je höher n ist, desto schmaler und mittelwertiger wird bei einer zufälligen Festlegung von Genen das phänotypische Spektrum. Das liegt wahrscheinlich daran, dass bei gleichmäßiger Zufallsverteilung mit jedem Gen die Wahrscheinlichkeit sinkt, dass n mal in Folge ein sehr hoher (~1) bzw. ein sehr niedriger (~0) Wert festgelegt wird.

Demonstration

Suche nach dem passendsten Gen

Methode

Jedes Gen kodiert einen Ton, das passendste Gen wird aktiv. So hat jede DNA mehrere mögliche Phänotypen:

  1. Jedes Gen kodiert einen möglichen Phänotyp (z.B. eine Tonhöhe zwischen 0 und 11).

  2. Anhand der Vorgabe eines oder mehrerer möglicher phänotypischer Ziele wird das passendste Gen ermittelt und auf einen Wertebereich zwischen Phänotyp-Minimum (0) und Phänotyp-Maximum (11) gemappt.

Ergebnisse

Dadurch, dass jedes Genom theoretisch mehrere Phänotypen hervorbringen kann, besteht die Möglichkeit, aus einer Population mehrere phänotypische Ziele herauszubilden (z.B. eine Tonskala).
Allerdings ist diese Methode aus evolutionsbiologischer Sicht wahrscheinlich eher sinnlos, da jede Neukombination keine neuen Phänotypen hervorbringen kann.

Demonstration

Generelle Beobachtungen

Insgesamt habe ich im Laufe des Experiments beobachtet, dass wenn die allgemeine Fitness einer Population nicht hoch ist, bei geringer Mutationsrate und geringem Mutationsspektrum (Deviation) eine Anpassung relativ lange dauern kann (da die nötige Variabilität noch fehlt). Dieser Prozess wird enorm beschleunigt, wenn sich in einer solchen Lage Mutationsrate und Deviation stark erhöhen, da dadurch die genetische Variabilität stark ansteigt und es wahrscheinlicher ist, dass passende Individuen entstehen. Daraus könnte man folgenden Gedanken ableiten: Je schlechter die Fitness eines Individuums, desto höher der Stress, dem es ausgesetzt ist, desto höher die Wahrscheinlichkeit einer Mutation und damit wiederum einer besseren Fitness.

Energy-Distribution

Gedanken und Motivation

In verschiedenen Modellen, in denen ich bisher experimentiert habe, traten immer wieder folgende Probleme auf:

Beide Fälle könnten evtl. behoben werden, indem man die Energie, die im virtuellen Ökosystem zur Verfügung steht, begrenzt. Je nach Anpassungsgrad wird diese Energie dann auf alle Individuen verteilt; je höher die Anpassung, desto höher die Energiezufuhr des einzelnen Individuums. Bei einer großen Population ist der Konkurrenzdruck höher und besser angepasste Individuen haben einen klaren Vorteil. Wenn aber die Bevölkerung und damit der Konkurrenzdruck sehr klein ist, können auch schlecht angepasste Kreaturen lange genug überleben, um sich zu reproduzieren und so die Population den neuen Gegebenheiten anzupassen.

Versuchsablauf

In diesem Experiment ging es nicht um den Evolutionsprozess an sich, sondern primär darum, wie man vorhandene Energie sinnvoll auf eine Population mit unterschiedlich angepassten Individuen verteilen kann. Evolutionsalgorithmen mit Vererbung und Fortpflanzung wurden deshalb nicht implementiert. Es wurde ein Modell entwickelt, in dem man folgende Variablen zu jeder Zeit anpassen kann:

Jedes Individuum wird repräsentiert durch ein Quadrat mit zufälliger Position auf dem Bildschirm, einer Linie, die vom Zentrum des Bildschirms in den Mittelpunkt des Quadrats führt und einem bei der Geburt zufällig festgelegten Ton. Die Größe des Quadrats, die Dicke und Deckkraft der Linie sowie die Lautstärke des Tons hängen von der jeweiligen Energiezufuhr ab. Je größer diese Zufuhr, desto kräftiger und lauter das Individuum (vgl. folgenden Screenshot).

Screenshot EnergyDistribution

Die wichtigste Frage bei diesem Experiment war, mit welcher Methode man die Energie an die einzelnen Individuen ihrer Anpassung entsprechend verteilt. Daraus hat sich Folgendes ergeben:

Es gibt eine vorgegebene Menge Energie e. Jedes Individuum hat je nach Anlage einen bestimmten Fitness-Wert (f_1, f_2, ... f_n) zwischen 0 (nicht angepasst) und 1 (perfekt angepasst). Die Summe dieser Fitnesswerte ist generalFitness. Um herauszufinden, welche Energie einem bestimmten Individuum nzugeteilt werden muss, braucht man nun diese Formel:

energy_n = e / generalFitness * f_n

Je größer die Population ist, desto wichtiger ist eine hohe Fitness, um noch einen ausreichenden Anteil der Energie zu bekommen. Bei sehr geringen Populationen ist genug Energie für alle da und jeder kann sich munter reproduzieren. Mit jeder Vermehrung steigt natürlich wieder der Konkurrenzdruck. Sehr entscheidend für die obige Formel ist die Methode, mit der man die Fitness eines Individuums bemisst. Im Experiment FrequencyFitness habe ich bereits einen sehr effektiven Weg dafür gefunden.

Daneben habe ich noch eine lineare und eine exponentielle Fitness-Funktion implementiert, die beide die Energie wesentlich gleichmäßiger verteilen und gut angepassten Individuen keinen so deutlichen Vorteil verschaffen wie in der zuvor erklärten Methode. Im Verlauf des Videos am Ende des Kapitels wechsele ich bei 0:27 kurz auf eine lineare Funktion um diese Eigenschaft zu demonstrieren.

Ergebnisse

Tatsächlich ist mit der Begrenzung vorhandener Energie eine sehr natürliche Populationsregulation möglich. Wenn nur wenige Individuen existieren, haben diese durch die fehlende Konkurrenz dennoch gute Chancen zu überleben und sich zu vermehren. Interessant ist auch, dass bei einer sehr großen Population sich auch die Lebenszeit sehr gut angepasster Individuen verkürzt, wenn nur genügend ähnlich gut angepasste existieren. Dadurch wird evtl. auch eine natürliche Grenze für eine Maximalpopulation gesetzt.

Demonstration

Credits

Literatur

Wenn auch zum Teil nicht explizit in dieser Dokumentation referenzierbar war die Lektüre folgender Werke dennoch wichtig für diese Arbeit:

Code

Liste aller in diesem Projekt verwendeten Software-Bibliotheken, ohne die diese Arbeit nicht möglich gewesen wäre:

Codefragmente / Algorithmen, auf die ich im Laufe der Arbeit zurückgegriffen habe:

Dank an

Kerstin Öchsner
Judith, Karsten, Sebastian, Sophie und Pascal Lohscheidt
Simon Karlstetter, Leon Kirchlechner, Max Prüfer
Bunti Lohscheidt
Christiane und Alex Fleischer
Prof. Kowarschick
Günther Ricks
Jürgen Branz
Georg Krauss
Christian Unterberg
Marlena Riechert


  1. John Cage: Silence; Wesleyan University Press, Middletown, Connecticut, 2011. 

  2. James McDermott, Niall J. L. Griffith, Michael O'Neill: Evolutionary Computation Applied to Sound Synthesis (2008), in The Art of Artificial Evolution. Springer. Berlin, Heidelberg, New York. 

  3. J. J. Ventrella: Evolving Structure in Liquid Music (2008), in The Art of Artificial Evolution. Springer. Berlin, Heidelberg, New York. 

  4. SuperCollider ist eine Entwicklungsumgebung und Programmiersprache für Echtzeit-Audiosynthese und algorithmische Komposition. 

  5. Craig W. Reynolds: Flocks, Herds, and Schools: A Distributed Behavioral Model, SIGGRAPH '87, Anaheim, 27. - 31. Juli, 1987. Fußnote auf Seite 1. 

  6. Processing-Library DNA von André Sier 

  7. Eine wissenschaftlich korrekte Definition des Begriffs »ökologische Nische« ist natürlich wesentlich komplexer. Hier ist ein guter Einstieg in diese Thematik: http://de.wikipedia.org/wiki/%C3%96kologische_Nische 

  8. Perlin-Noise ist eine fraktale Rauschfunktion auf der Basis von pseudozufälligen Gradientwerten: http://de.wikipedia.org/wiki/Perlin-Noise 

  9. Openkinect von Daniel Shiffman 

  10. Radu Bogdan Rusu: Semantic 3D Object Maps for Everyday Manipulation in Human Living Environments (Dissertation), S. 89, 2009, Technische Universität München, Institut für Informatik 

  11. Quelle der ConvexHull-Implementierung: http://www.ahristov.com/tutorial/geometry-games/convex-hull.html (Stand 19.06.2013), Alexander Hristov 

  12. OpenSound Control 

  13. Verletphysics ist Teil der umfangreichen Bibliothek toxiclibs von Karsten Schmidt