[Music] [Music] [Music] Hi, I'm Dave Miller and this is a video about natural selection and evolution. I wrote a little program where there's a little two-dimensional world and little creatures live in it. They have genomes that they pass on from one generation to the next, and they have little neural network brains that govern their behavior. What I wanted to try out was setting up the conditions necessary for evolution to happen, and then I wanted to see what would happen. Would they evolve their little brains in response to their environment? I made this video for several different audiences. I made it for my programming friends who have an interest in neural net programming and genetic programming. And I made it for my friends who have an interest in biology, because you might be interested in seeing how one programmer simulates a creature's nervous system and genetic inheritance. And I made this for all of my friends who have any interest at all in how evolution and natural selection work in nature, because I hope that by experimenting with it in a simulator, we might get some insights into how it works in nature. So here's a spoiler alert: I'll tell you right now up front exactly what we will learn in this video. We will learn that evolution can happen automatically, without any effort, without any planning, without any oversight, if certain conditions are met. There are five conditions that we have to meet in our simulator for evolution to occur. The first condition is there has to be something that self-replicates. Now the thing is, evolution does not explain where the first self-replicating thing came from. Right now no one knows where the first self-replicating organism came from on this earth or how it came about. Evolution can't explain that, but once you have something that can replicate, then evolution and natural selection does explain very very clearly how that organism can increase in complexity and organization over a period of time. In our simulator we have a really easy job of simulating creatures and how they replicate. Each creature is just a little data structure. The data structure contains a few little bits of information like where it lives in the world, its genome - we'll talk more about that later - and its little neural network brain. And so to make a replication of one of these organisms, we just copied the data structure in memory; there you have new organisms, so it's easy to to simulate replication of organisms in our simulator. The second condition necessary for evolution to occur is that every creature that is born has to be constructed according to some sort of a blueprint. In nature that's what a genome is all about, stored as DNA or RNA. "Genome" is a good word to use when you're referring just to the information that's in that genetic information that gets passed on from generation to generation. And DNA and RNA - those are specific chemical storage formats for storing that information. In nature, DNA is just a long strand of these four different kinds of molecules. We just abbreviate them as A, C, G, and T, or when the same information is encoded in RNA - RNA uses a U molecule instead of a T molecule, but we can represent the genome of any naturally occurring organism just by just by showing its long string of letters that make up its genome. In you, in every one of your cells, your genome is stored in DNA and your genome is about, oh, three billion or so of these letters long. In our software simulator we also use strings of letters or characters to store each individual's genome. But our simulated genomes are a lot shorter. We only use just a few dozen letters or maybe a few hundred letters at most. For example, in our simulator a genome in our simulator might look like this. This is one that's 16 genes long where each gene is a clump of those hexadecimal letters. We'll talk more about the the genome later in the video. But for now we'll just say that we can simulate a genome by a series of letters which is kind of analogous to the way that naturally occurring genomes are represented. The third condition for evolution to occur is that this blueprint that's used to construct each individual has to be inherited from the replicator to the replicant, or from parent to offspring in other words. You, you contain DNA that partly comes from one parent and partly comes from the other parent. And that's what we do in the simulator too as well. In the simulator, when we create a new child, we just copy the data structure, but we copy some of the genome from one parent and some of the genome from the other parent and that makes up a new genome for the child data structure. The fourth condition in order for evolution to happen is that the inherited blueprint that gets passed to the offspring has to undergo occasional mutations. Usually in nature when you inherit the genes from your parents those genes are copied a hundred percent accurately most of the time. But once in a while you'll have a gene that you get that has one letter that's different, or a couple of letters are different, or maybe a few letters that get changed to a different place in your genome. A few little mutations like that occasionally happen so that every replicant in nature is not an exact 100% copy of its parent in all cases. We simulate that in our simulator really simply. We have a parameter that we can set in the simulator that tells how often a mutation happens and I usually keep that set so that that there's a one chance in 1000 that any gene that gets inherited will have a single bit error in its copy. That means for example if this is a genome that the child inherits from its parents, we might by random chance cause this one number here to change from a two to a three, and that satisfies the condition for occasional mutations to happen in our simulator. Later in this video we'll do some experiments to see what happens if we don't allow these chance mutations to happen. We'll see the role of mutations and why it's an important part of evolution. And finally, the fifth and last condition that we have to fulfill in order for evolution to occur is that there must be some sort of a selection method for selecting who gets to reproduce and who doesn't. In nature, we call this "natural selection." In nature, there's so many factors that all work together that select who gets to reproduce and who doesn't. An organism has to escape being eaten by predators; it has to find food; it has to find shelter from the environment; it has to avoid sickness and injury; and it also also has to be sufficiently socially adept to find a mate. All of these things work together in nature to form natural selection. In our software simulator we don't have to go to all that simulation, we don't have to simulate food and sickness and injury and weather and all that sort of stuff. We can do a much simpler selection criterion and fulfill this condition. We'll talk later about the different selection criteria that we use in in the simulator, but it'll be really simple. So let's just get on with our first simulation run and so you can see how it works. After that we'll talk about some of the details of the simulator. There are several parameters that we can set in each simulation run. We can choose the size of our two-dimensional world. In this run we'll make a world that is 128 grid points east to west and 128 grid points north to south. Each individual can occupy one grid point, and in each simulator step, an individual gets to make a move of one grid step in any of the eight directions, just like the way that a king moves on a chessboard. We can also set a parameter that sets the population. We'll set the population for this run to be one thousand, so that at the beginning of each generation, we start with one thousand creatures randomly positioned throughout their world. Let's put them in the world; there they are, they're they're all different colors. The colors approximately indicate the genetic diversity in the colony. Because there's so many different colors here - that's a hint to us that there's a lot of different genomes. We'll call this very first starting position generation zero. These are the founders of our colony, and we give them random genomes that are set by a random number generator. One of the parameters is how big their genomes are. We will set this run to be a tiny short little genome of just four genes long. For example, this one little creature right here has a genome that looks like this which specifies a brain wiring that looks like this. Each genome is a bunch of hexadecimal letters that contain the instructions for how to make the connections in their little neural network brain, and later we'll talk a little bit more about how that encoding works and how the neural network works. But just as another example, this little creature has a genome that looks like this, and its little brain is wired up like this. Those are two different brains from two different genomes and so that gives them two different behaviors. Every one of these 1000 creatures has a different genome and therefore a different brain that makes them work differently. We can also set how many simulator steps occur in each generation. That determines how long each generation lives. For this run, we will say that each generation runs for 300 steps. Now when you watch the little videos I'll show you of these creatures running around in their world, the videos will be running at the same rate as the video frame rate is that you're watching. Since this video was created at 30 frames a second, you'll also see the simulations run at 30 simulator steps per second. That means for 300 simulator steps, their lifetime in one generation will just be 10 seconds. Okay, we call this one "generation zero" and so let's start generation zero and watch them. Well, they don't seem to be doing anything very interesting. Some of them don't don't move at all because they don't have any brain wiring for movement. Others have brains that are wired to move a little bit. Collectively it's kind of random behavior. They don't have any goals. They just do whatever their little random brains are wired to do. And that's the end of generation zero. Their lifetime is over and this is the ending position that everyone has at the end of this generation. Now we're going to apply the selection criterion and and decide who gets to reproduce. For this first run we will make just the simplest selection criterion that I could think of. We'll just say everyone on the eastern half of their world gets to reproduce; everyone on the western half just dies. They will all die, but the ones on the eastern half will get to reproduce first then die. The ones on the western half will just die without reproducing. That's a pretty simple selection criterion, but it's enough. Let's go on to the next generation. The next generation we call generation one. These are the children. The children get distributed at random locations throughout the world and the world gets repopulated back to its 1000 population. That means that in generation zero, approximately half of them happened to be in the correct spawning half of the world and they reproduced. So on average, each of the parents in generation zero had two offspring. So now we've repopulated the world. Here are the children in random positions, and let's watch them live out their lives. They don't look much different than their parents. They still look pretty random. They don't have any coordinated behavior, and I guess that's not too surprising because the ones in the previous generation in the eastern half of the world - most of them were there just by luck of birth. They were born there, they didn't move away from there, and they just they happened to be lucky and be in the right spot and they reproduced. So it's not too surprising that their children are just about as random. Let's move forward to generation number 20. These are the great great great great great great great great great great great great great great great great great great grandkids of the original founding population. 20 generations have passed and now let's watch them. Definitely we see a tendency for some of the creatures to migrate east. That's kind of interesting. Let's move forward to generation 50. Those are the great great - oh never mind - it's generation 50. Now generation 50 something interesting happens. These definitely have an instinct to migrate east as soon as they're born. And notice the colors are all more similar. There's not a as wide of variation in color. That shows that the gene pool is sort of becoming more homogeneous, more of them are sharing similar genes. The vast majority of them, the moment they're born - they have the inborn instinct to just head east and keep going until they can't go any further. Let's go way in the future to generation 1200. There's no question about it - our creatures are getting born with genes that make them go east, and you can see they're all very similar color. That's a rough indication that the gene pool is very narrow. Virtually all the creatures in this generation after 1200 generations have very similar genes. Here's a graph that records this particular run. The X-axis shows the generation number, starting at zero, goes all the way to generation 1200 when we stop the simulation. The green line uses a scale from zero to a thousand. It shows the number of reproducers. Those are the ones who survived the selection criterion and became parents of the next generation. At first only about half were reproducers by random chance, but you can see that that rapidly increased to where nearly everyone became reproducers. The purple line is a measure of genetic diversity or an approximate measure of diversity. At first it's pegged at the top because everyone has different genes and it's maxed out then, but around generation 800 or so the gene pool started to narrow down and this purple line got smaller and smaller as more and more creatures were born with similar genes. At the end of generation 1200, the majority of the population shared very nearly identical genes. My programmer friends will be curious about what these creatures' neural network brains looked like after they evolved for 1200 generations. So here's what here's what it looks like. This is the most common genome in generation 1200. It specifies a brain configuration like this. It's a very small and simple brain. There are four neurons involved. The blue neuron is an input sensory neuron from one of the creature's sensory organs that senses the population density in their forward direction of movement. The two pink neurons are output action neurons like motor neurons. When this one gets driven by a strong enough signal, it causes the creature to have an urge to move in a random direction. And this one, when it gets activated strongly enough, causes the creature to move east. Somehow, all taken together, this wiring causes these creatures then just to head east. The one gray neuron is an internal neuron, and we'll talk more about that a little later. One interesting thing about this brain that evolved is the way that it uses these two neurons - the one for move east and the one for move random. And this internal neuron somehow computes when to drive one and when to drive the other. What happens is that when there's a long distance in front of the organism where there's no one blocking the path, this is the one that's driven the most strongly - the urge to move east. But once it gets to the east where all the other creatures have accumulated against the east wall and there's there's someone blocking the path, then this one here becomes the most strongly driven neuron and it's the one that says move in a random direction. Now this is kind of an interesting development. This is an adaptation that evolved over a course of time, and what it does - let's go back to generation 1200 and let's take a look again and notice how that at the beginning at the first part of the creature's life, they just head east without any deviation. But once they get to the east, they jump around a little bit. Let's watch them go. At first they just go east. Now when they get to the east, where there's a bunch of other people blocking the way, they jitter around a little bit. Now that's kind of an interesting adaptation, because what that does is as they're moving east, if they encounter someone walking the way, their little brains will cause them to jitter around a little bit and perhaps get around that blockage so that they can continue going east. Whenever there's a clear path, this part of the brain is predominant moving them east, but if there's a blockage, this part of their brain becomes predominant and causes them to jitter around in a random direction. That's kind of an interesting little solution to the problem of how to get east if someone is blocking your path. Their little brains evolved after several hundred generations to where virtually a hundred percent of them lived to reproduce. Nothing directed this evolution. There was no explicit programming anywhere that told them what the selection criterion would be. There's no explicit programming anywhere that gave them the solution of how to survive. Yet their little brains evolved to survive, and they did it pretty well. Now, how did that evolution occur when there was nothing in the programming to cause it to occur? To get the answer to that, let's go back to the founding population, generation zero, and let's take a closer look at what happened at the very beginning of this simulation run. Here's generation zero again. Most of them survived because they just happened to be born on the eastern side and that's where they ended up at the end of the generation and they got to reproduce. But not all of them were on the east side just by random chance. On generation zero, in a population of a thousand, they had random brains and a few of them had brains that were by accident wired to move north; some of them were wired to move west; some of them by accidental luck they were wired to move south; and a few were wired to move east. I've highlighted with these crosshairs one particular individual, and just watch it now during generation zero. During its lifetime, it moved from the western half, across the center line, into the eastern half. It moved during its lifetime from the unsafe half into the half where it got to reproduce. It had children. Now there's two or three or maybe four of them, I'm not sure, but a very small number of them that just accidentally had the urge to move east because of the random genome that they had, and they survived and reproduced. There were two or three or four that had the opposite random brains that caused them to move west and they ended up going from the safe side into the unsafe side. They did not reproduce. Since everyone that reproduced had on average two children, it meant that in the next generation - the generation of children - there were slightly more in the population that had the urge to move east and slightly fewer with the urge to move west. It's just a numbers game. After a certain number of generations, more and more and more of them will be from those who survived because they had the urge to go east. Fewer and fewer will be in the population from those who went west because they didn't have children and after a while it's just a matter of time before virtually the entire generation is descended from those with an urge to move east. So if we were to sum up how evolution works, here is the summary. Evolution through natural selection works because whatever reproduces, reproduces. And whatever doesn't, doesn't. That's how it works. That's how it worked in the simulator and that's fundamentally how it works in nature. That was fun. Let's do another simulation with some experiments to show what the role of mutation is in natural selection and evolution. But first let me give you some brain anatomy details. I know that some of my programmer friends are interested in the neural networks that are in the simulator and so let me just give you a quick tour of their their brain anatomy. If you want to just jump to the next simulation you can skip forward in the video about five minutes. [Music] Every one of our creatures is born with a certain number of input sensory neurons already installed, and a bunch of output action neurons already installed. These red ones here or the pink ones are their action output neurons, and these blue ones on top are their sensory input neurons. Every creature also has one or more internal neurons that they can use if they want and those are the gray ones here in the middle. Now at first when a creature is born, nothing is wired up. It's their genome that determines how their wiring is wired up. Each gene in their genome specifies one neural connection. Connections can go directly from one of the sensory neurons right directly to one of the action output neurons. Or it can go from a sensory neuron to one of the internal internal neurons like this. Or connections can go from an inner neuron to an action output neuron. Or from an inner neuron to another internal neuron. Connections can even go from an internal neuron right back to its own self. The combination of all these connections together then form the brain wiring and therefore forms the behavior of one of the organisms. Notice not all of these - the the neurons - need to be wired up. Often with a short genome, only a few connections are made and only a few of these sensory and action neurons are actually used. One more thing I should point out to my fellow programmers - sometimes the genome specifies connections that go to an internal neuron but the internal neuron does not feed anything else. In that case, the internal neuron and all the connections that go to it are completely useless, so we just remove those from the creatures neural network and we don't even compute those useless connections. What's left then is their brain. These connections are either positive or negative. The green ones are positive connections, the red ones are negative connections, and this corresponds roughly in nature to how that some neurons are connected to each other through connections called synapses that are either excitatory - which means that when one neuron is activated it excites or increases the activation of the neuron it's connected to - and other connections are inhibitory, so that when one neuron fires it inhibits the firing of the neuron it's connected to. Some of my fellow programmers will will want just a little bit more detail and here it is. The input sensory neurons produce a floating point number between zero and one. They get multiplied by the weight of the connection, which is a small floating point number in a small range, say between minus four and positive four. Each of the internal neurons will sum up all of the weighted input connections and it will take that sum, positive or negative, and run it through a hyperbolic tangent function to produce a number between -1 and positive 1. Each of the output neurons likewise will sum up each of its inputs and come up with a sum that's in some arbitrary floating point range. It will pass that sum through a hyperbolic tangent function to get a number between -1 and positive 1, and then that determines whether or not that output neuron will fire or not. Take this neuron here for example. This is the output neuron that will cause a pheromone to be emitted in the creature's vicinity if it's driven strongly enough. If the sum of its inputs, after passing through a hyperbolic tangent function, is positive, then it's a number between 0 and 1 that is interpreted as a probability during that simulator step of it actually being activated. Here are some of the different input sensory neurons and what they do. This one here for example is sensitive to the creature's position along the X or Y axis. This one here is sensitive to how closely it is related genetically to the other creature who is in the adjacent grid location right directly in front of it. It's a measure of genetic compatibility. There are a couple of input sensory neurons that are sensitive to how close the creature is to either the X border or the Y border. There are a couple of sensitive neurons to how much pheromone there is in the immediate vicinity. There is a neuron that is sensitive to the population density in the immediate neighborhood. There's a separate input sensor neuron that is sensitive not just to the population density but to the population gradient along the forward direction of movement. There's one that's sensitive to the population gradient along a left-right or a perpendicular axis to the forward direction of movement. There's a sensory neuron that's sensitive to the creature's age. It outputs the lowest value at the beginning of the lifetime and at the end of the life span it outputs the maximum value. There's one input neuron here that is an oscillator neuron. Now in nature, many times nervous systems inside creatures will evolve a number of different oscillators. Inside your own brain, for example, you have oscillators that operate at alpha frequencies, theta wave frequencies, things like that. You also have biological oscillators inside your own body that operate more like a daily or a monthly cycle or yearly cycles. Our creatures could evolve oscillators inside the neural net, but just to give them a little head start, we have one sensory input neuron that is itself an oscillator, and this period of oscillation is by default about whole 20 or 30 simulator cycles, but there is an output neuron here that can be driven low or high or anywhere in between that changes the period of that oscillator. The output neurons - there is a couple of them cause movement along the X and Y axes. There's one that will encourage movement in the forward direction, whatever that happens to be, where the forward direction is defined as whatever the last direction of movement was. There is one output neuron that if driven strongly enough will cause the creature to emit a pheromone in the immediate vicinity. There's another output neuron here that I call the responsiveness neuron. When it's not driven, it means that the creature has just an average level of reactivity to its input stimuli, but if this neuron is connected and if it's driven, it can be driven to a higher level which causes the creature to be more nervous, more reactive, like it's on caffeine. It reacts more quickly and more vigorously to changes in its input sensory neurons, and if it's driven to a lower level, it makes the creature like a little bit more sleepy, less reactive. There's also one output neuron here which we will talk about later. I call it the "kill neuron." Normally I keep that disabled so if there's a connection to it, it doesn't have any effect. Because what that is, is if it's driven strongly enough and if that feature is enabled in the simulator, the creature will attempt to kill the other creature that is in the adjacent grid location right directly in front of it. That's kind of an ominous little thing to play with, and we'll play with it later, you'll see what happens if we enable that, but for right now, I won't say anything more about that. So let's do our next simulation run, and this is the one that shows how mutations work. First of all, let's set up a run where our parameters are similar to the last time. We have 128 by 128 grid points in the world - that's the size, but the population now is bigger this time. We'll have a population of 3000. Again we'll have 300 simulator steps in each generation - they'll last 10 seconds in each generation. This time we'll give them bigger brains. Their genomes will be 24 genes long which means 24 brain connections, which means that they'll be a little smarter. They'll be allowed to use up to four internal neurons so they'll be capable of slightly smarter behavior. In this particular run, our survival criterion will be similar to the last time except this time there's a narrow margin on the right and narrow margin on the left where they can reproduce. Those are the spawning areas. If they end up in those areas, they will reproduce. There's enough space in both of these spawning areas put together to allow the entire population of 3000 to fit inside and to reproduce, if they can just evolve a way to do that. So let's see what happens. Here's our starting population, randomly distributed. This is generation zero. We let them run and they, as usual - generation zero just looks random. At the end, by random chance, 27 percent of the population happened to be in the spawning areas and they reproduced. Let's skip forward to generation 25. In this generation, some of them have learned to move east, and now the survival rate has gone up from 27 to 56 percent. Let's go forward to generation 100. In generation 100, they're getting a little smarter. Now 67 - up from 56 - now 67 percent have found their way to the spawning areas and are reproducing. Let's go forward 600 more generations to generation 700. In generation 700, a mutation happened that caused some of them to start going west instead of east, and now 82 percent of them are surviving in each generation and reproducing. That's a good increase. Let's go forward 300 more generations to generation 1000. Now they're more evenly divided going east and west, and now we have a survival rate that has gone up from 82 percent to 98 and a half percent. Now let's go forward several thousand generations to generation 8300 and take a look. 99.3 percent of the individuals in generation 8300 make it to the spawning areas. The only ones who don't are those who just were unlucky with mutations and they don't have the right brain wiring to know how to make it to the spawning area. There's very low genetic diversity as you can see by the uniform color, most have a very similar set of genes. They've done a really good job of evolving a solution to their environment. They know how to survive. They know how to reproduce. But now let's upset their nice peaceful little world. We will introduce these barriers, these five barriers that block their movement east and west, and let's see what happens. The very next generation after putting in these barriers, a lot of them get blocked on the barriers. The survival rate plummets to just 69% - from almost everyone surviving, now almost a third of them are getting hung up on these barriers and not making it to the spawning area. So what's going to happen now? This is the miracle of mutations. Because of occasional mutations, even though everyone has nearly the same genes, once in a while a child will be born that escapes being blocked by some method and they will have children. Those that get blocked don't reproduce. But if one child makes it around the blockage and reproduces, the next generation will have more of those genes. So here's what happens after just 700 more generations. By generation 9000, many of them have started to move north or south in addition to just moving east or west. They take a different route. They get around the barriers, and the survival rate now has gone up from 69% back up to 73 percent. They are starting to learn how to adapt to their new environment. I let this particular simulation run for 177,000 generations. They never quite got back to 100% survival rate like they had without the barriers, but they finally stabilized at around an 83 percent survival rate. You can see how they go different directions, not just east or west. They've learned how to get around the barriers pretty well. Now this adaptation worked because of mutations and natural selection. There's nothing in their programming that enabled them explicitly to figure this out. It's just that when a mutation happened that allowed a child to survive, it reproduced. Now let's do the same run again but this time let's set the mutation rate to be zero. No mutations will happen, and let's see what the difference is. The first generation of course looks the same. they appear all random. There's no difference there. By generation 100, 97% of them are finding the spawning areas and they're reproducing. By generation 8000, the genetic diversity is very small, just like the last run, and 98% are reproducing. This is very similar to the last run. Now let's introduce the barriers. We introduce the barriers, and the survival rate immediately plummets to 66%. This is similar what to what happened in the last run, but here's the difference. There's no mutations going on now and everyone in the population has just about the same genes. The gene pool is very homogeneous. There's not a lot of diversity there to draw on. The children inherit the same genes over and over and over, generation after generation. There's no change in those genes, no improvement in their behavior. We can let this colony run for millions of generations and they will never ever get any better than this. That's the value of mutation. Mutations need to occur often enough in a population to let the population adapt to changes in the environment, but not too much of mutations so that the colony doesn't have to spend all of its time taking care of members with mutations, so there's some optimal rate of mutations that allows a species to continually, over time, very slowly, step by step, adapt to its changing environment. After seeing the effect of mutation in this simulation, I personally have just a little bit more respect for the role of mutation in the survival of a species. Without mutations in our species, we would not be here having this discussion. Without mutations, our species would not live long enough in the future to survive and eventually reach the stars. [Music] Before we do our next simulation, let's talk a little bit about brain sizes. Our simulator has little bitty tiny brains in it. How does that compare with nature? Well, your brain and my brain, homo sapiens brain, has about 80 billion neurons in it and no one is quite sure how many different connections there are between those neurons, but there has to be at least 100 trillion connections between them, maybe as many as a quadrillion connections. Your cat's brain is not quite as big - it has about 700 million neurons and 10 trillion connections. A fruit fly, even a tiny little fruit fly, has 250,000 neurons in its brain and about 10 million connections between them. One of the simplest brains found in nature and one that is studied a whole lot by biologists is the brain of a nematode worm. This little species called a c. elegans species is one of the most well studied organisms in nature by biologists. Each one born in the laboratory has about 300 neurons and somewhere around 7000 connections between them depending on how you count them. In contrast to all of these brains, the neural network brains in the simulator usually have fewer than oh maybe 20 or so neurons and fewer than 200 or so connections. The program I wrote allows up to 128 neurons and 65000 connections, but with that many neurons, there's not enough CPU power in my possession to actually make use of those, so I usually run the simulator with just a few neurons and a few dozen connections. For fun, let's do a few simulations where we see the effect of different brain sizes in the simulator. For this next run, let's set up the parameters like this. The size of the world is the same as before. We have 3000 individuals in the population. We'll just limit it to 250 simulator steps per generation. The genome size will be really tiny, just two genes in the genome. That only allows two connections inside their brain. That's not very much - they won't be able to have any kind of sophisticated behavior, but we'll just see what happens. For this run, the selection criterion will be that anyone who ends up in a corner will will be allowed to reproduce. There's enough room in all four corners combined to allow all 3000 of the population to reproduce, if they can figure out how to get there. And it turns out that no matter how long we let them evolve, they never get very good at this. After 10000 generations, here's all they could come up with. They have evolved to migrate a little bit to the north and a little bit to the south, and now about 45% of them are reproducing, but this is as good as they ever get. With limited brain power, they never figure out any better strategy than this. Here's one of the most common strains in this population. This strain has brain wiring that urges it to move north most of the time but occasionally it has an urge to move in a random direction. Here's another strain in this population. It uses its two connections in a different way. This one has signals that connected to the move west neuron and that's usually the dominant one, but as it gets closer to the west wall, this connection starts getting a signal that urges it to move south. There's not a whole lot it can do with just two neural connections, but these are two solutions that are found in this colony. There were a few other strains in this population. They all lived together in harmony. None of the brain configurations ever became predominant. None of them outperformed the others enough to become prevalent in the population. The best survival in this colony is that everyone has a different strategy but they all coexist in peace and harmony. That was using parameters of two neural connections and a single internal neuron. Let's increase their brain size to eight connections and two internal neurons. At generation ten thousand, about 72 percent of the colony make it to the spawning corners, up from 45 percent when they only had two brain connections, so their bigger brains give them a little bit better ability to figure out a solution. In this colony there are two strains that are about equally present in the gene pool. One of them has a brain like this. In this brain configuration, the creature has neural connections to their action neurons for moving north, west, and for moving randomly. It uses sensory input neurons - the blue ones - that detect its distance from the north-south boundaries, and senses which boundary is closest. It uses both internal neurons in some way that's a little too complicated for me to easily intuit. The other main strain in this colony simply replaces this connection to the north output neuron with a connection to the south output neuron. Both strains live in harmony, and it's a good strategy for the overall good of the colony. Let's try bigger brains. Let's give them 32 genes which allow them 32 neural connections, and let's let them use up to five internal neurons. So let's skip right to generation 10,000. after ten thousand generations of evolution, this colony survives better. We're now up to eighty percent of the population making it to the spawning areas. The last run only 72 percent of them made it. These bigger brains let them evolve quicker. They have better mental capacity to allow them to find a solution that works. And if you're a programmer and you're used to neural networks, you know this phenomenon where a neural network solves some problems but it's hard for us humans to intuit just exactly how it works. If you look at one of these brains - if if you took a pencil and paper in a calculator and started with the values of the input sensory neurons, you could figure out all the numbers that go on and you can figure out exactly why it behaves as it does. But why should we? With many neural networks, when they work, why not just accept it? Let's go to more extremes and run this again with a genome of a thousand genes allowing a thousand neural connections and up to 127 internal neurons. Now this one took a while to compute on the computer I'm using, but after letting it run all day and getting to generation ten thousand, here's what we have. We're up to 93% survivors or reproducers. That's the best yet. Their bigger brains give them a distinct advantage. In this colony there are many different strains as you can tell by the different colors. The majority of them have brains that are wired up more or less like this, and this is definitely too complex for a human being to understand how it works. And so here's what we found out. With only two brain connections and one internal neuron, sixteen percent of them managed to survive the selection criterion and reproduce. With eight connections and two neurons, 72%. 32 connections five neurons, 86%, and with the thousand connections and 127 internal neurons, 93% survived and reproduced. The takeaway message here I guess is that your neurons are valuable. They serve you well. Take care of your neurons. Now let's move on to talk about the kill neuron. But before we run an experiment where these creatures kill each other, let's first talk just a minute about the encoding scheme we use for the genomes in the simulator. Some of my programming friends are curious about that. If you want to skip right to the next simulation, you can skip forward about two minutes. [Music] Our simulated genomes consist of genes where each gene is represented by eight hexadecimal digits. Let's just take this one gene for example. This one gene - eight hexadecimal digits represents 32 binary bits of data, and each 32-bit gene is divided into five different fields. There's one field of a single bit that determines whether the source of the connection is from an input sensory neuron or from an internal neuron. And then there is a seven bit field which is an identifier of which input neuron or which internal neuron. We take that seven bit field as an unsigned value and take it modulo the number of neurons involved to tell exactly which one it refers to. Likewise we have one bit that determines the sink of the connection, whether or not it goes to an internal neuron or to an output action neuron, and a 7-bit value that identifies exactly which internal neuron or exactly which output action neuron. The rest of the gene is a 16-bit value which we regard as a signed 16-bit integer, and that's the weight of the connection. Now a 16-bit integer goes from minus 32k to plus 32k, and that's way too wide a range for connections in a neural net, so we divide that number by a constant. I usually divide it by around eight thousand or nine thousand or ten thousand something like that to make a smaller floating point range. So that's enough about the gene encoding. Let's go on and play with the kill neuron. This is kind of macabre, but let's see what happens. All creatures are born with this kill neuron, but normally I keep it disabled so that if they have a connection to it and if they activate that output neuron, it doesn't have any effect. And generally what happens is in generation zero, where we start with completely random genomes, there will be a few individuals who have that kill neuron connected and they try to exercise it, but it has no effect, and so after a certain number of generations, those lineages will die out, because when mutations happen that repurpose that connection and redirect that connection from the kill neuron onto a different action neuron - if that gives the child an advantage for survival and reproduction, then that lineage will reproduce. Eventually the lineages that connect to the kill neuron - eventually they just all die out. So after a while, virtually nobody in a population will have connections to their kill neuron. But what happens if we enable it so that it does have an effect? Now what this means is that if it's driven to a high enough signal level, the creature will attempt to kill another creature who's right in front of it in the forward direction of movement, and if there's no one there in the adjacent cell, then no one gets hurt. In this simulation we'll set the population to a thousand. Each generation will last 250 simulator steps. We'll give them genomes that are 16 genes long - 16 neural connections - and a limit of four internal neurons. The selection criterion for this run will be really easy. Any creature that ends up inside this circular area will be allowed to reproduce. They can all easily fit without any kind of competition. It's a very simple selection criterion. They all should be able to easily find a solution that gets them in there after a short bit of evolution. So let's see what happens. The first generation looks random just like usual. There were a few of the members in this initial population that had their kill neurons wired up just by random chance, and those individuals ended up murdering 151 of the creatures, about 15 percent of the population died because of those few who had the kill neuron. So let's go right to generation 200. After 200 generations of evolution, we find that the kill neuron is largely gone from the population. Hardly anyone has a connection to their kill neuron anymore. They learned how to survive without killing each other. In this generation 200, there were only four murders. This is a tiny fraction of homicide compared to what it was with their random starting brains. They don't need to kill and so they don't. But then a mutation happened somewhere around generation 800. A mutation happened where a child was born who made its way to the center spawning area by killing others in its path, and it passed on its genes, and those children passed on their genes, and those children passed on their genes. Here's generation 900. In generation 900 they're trying to get to the center spawning area but a third of the population dies violently by those who are exercising their kill genes. And it gets worse. Let's go up several thousand generations. Let's show generation 3400. This is about as bad as it gets. In generation 3400, they stabilized. They ran for several thousand generations after this very stably where about 70% of the creatures in every generation died violently. Only about 30 percent were making it to the spawning area and reproducing. Generation after generation after generation, children were inheriting the killing instinct of their parents who survived and reproduced. Kind of depressing. Here is a chart showing this particular run. The X-axis shows generations from zero to a hundred thousand - that's as long as I could allow this colony to survive. The Y-axis shows the survivors or the reproducers from zero to a thousand. The green shows those who reproduced. Purple is the genetic diversity which quickly dropped and stayed very low. The yellow-orange line is the number who died by homicide. This colony was bi-stable. They could exist in a stable condition for thousands of generations where most of them were murdered and only a few survived, or they could exist for thousands of generations with virtually no murders all living in peace, but the in-between times were not stable. I tried running this kind of simulation a number of times and I often got similar results - a bi-stable situation where they were either very peaceful or very murderous, but seldom in between. It does get a little better if the population is very small and their world is very sparsely populated. For example, let's run the same kind of simulation but let's do it with only 100 creatures instead of a thousand. Now they don't have a chance to run into each other as often. They don't have an opportunity to kill each other as often. They have a much easier time making it toward the safe area without having to kill anyone to get there. So now after generation 80,000, here's what they look like. And the chart for this run looks like this. They kind of reached a stable spot where some fraction of them were dying in each generation and a larger number were surviving but they never did find a way to peacefully co-exist. So what can we learn from this? Well, in our simulated world we see that it's possible to live together in harmony where nearly everyone survives and it's also possible to live in an ugly world where most people die violently and only a few survive. I don't know what this means if we try to find an analogy in, say, human society. I'll let the sociologists figure that one out. But I do know for a fact that we as a species, we homo sapiens, have something more than just animal instincts. We are all born each of us having a frontal cortex that is capable of thinking through the consequences of our animal actions. We each have the capacity to think about what we do and we can choose to override or at least influence our base animal instincts. To all of us, it might seem once in a while that some violent forceful solution will get us what we want, but we all have the ability to see in the end that violent solutions only lead to suffering for everyone, including the perpetrators. If we exercise our frontal cortices and encourage each other to do so, maybe we can all see that the best optimal way for our society to live is to find ways to coexist despite our inherited genes. Real mother nature is a lot more complex and nuanced than we see in a little simulator like this, and if you've made it this far in this video, you might be interested in learning more about real biological and neurological systems and how they work in the real world. Mother nature is endlessly fascinating. But the fundamental process of evolution through natural selection is pretty clear. It can be summed up in a simple statement that whatever reproduces, reproduces, whatever doesn't, doesn't. So I'll say so long at this point but if you want to hang around for a little bit longer, I'll add a little bit of bonus material at the end. I'll briefly list for my programmer friends some of the software technologies used in this project and then we'll run another simulation. We'll see if our little creatures can learn how to evade a radioactive source in their world. [Music] This project was done using only open source tools. I wrote the simulator code in straight up standard C++ on a linux system. It's not dependent on any libraries other than CImg.h, and that's only for generating the little movies of the creatures darting about in their world. CImg.h is a package you can install on linux. It's a nice convenient wrapper around mainly OpenCV and a few other graphics libraries. I probably could have used OpenCV directly, but I like the CImg.h interface. As the simulator is running, at the end of each simulator step, we create a an image, we fill it with little colored circles to show the state of the world, and then we push that image onto a stack of images that we store in RAM. At the end of a generation, we just call a single function to convert that stack of images into a movie for that generation. I didn't try to code this simulator to run on a GPU because I was constantly modifying the program, and debugging a constantly changing program on a GPU is a lot slower for me than debugging on a CPU. But I did make one of the inner loops of the simulator thread-safe and then spread it over a bunch of multiple CPU cores using OpenMP. With OpenMP, all I had to do was add a single #pragma line before that one loop and poof - it's multi-threaded. I found increasing performance when using up to about six or eight cores on the simulator but I seldom ran it on more than 10 cores because of diminishing returns at that point. The diagrams that show the neural networks were made with iGraph. As the simulator runs, it now and then it prints out a sampling of the neural network brains in the population in a format that looks like this, which can be then fed into a little python program I wrote that assigns colors and line thicknesses and things like that and then uses iGraph to create that network diagram. The charts that show the summary of a simulation run were made on gnuplot. As the simulator is running, it appends a single line to a log file after each generation that records the number of survivors or reproducers, the genetic diversity of that generation, and the number of murders if the kill neuron is enabled, and then this little gnuplot script converts that log file into a chart. And that's about it for the technology list. So now let's do another simulation. This is our radioactive challenge. [Music] In this simulation we'll make radioactive walls. In the first half of each generation, the west wall is radioactive. The radioactivity extends out to the halfway point in their world, and there's an exponential falloff just like radioactivity works in the real world. So the closer that a creature is to that wall and the longer it stays there, the more likely it will die sometime during its lifetime. And just to make it more interesting, at the midpoint during each generation, the radioactive source will switch from the west wall to the east wall. So the last half of each creature's lifetime, the radioactivity will be coming from the opposite side. During each generation, the best survival strategy will be to avoid the west wall when they're young, and then when they're midlife, start avoiding the east wall. At the end of each generation, everyone left standing will reproduce. In this simulation, we'll set up the parameters like this. We'll set up a population of two thousand. We'll allow 250 simulator steps for each generation - that'll just be a few seconds for each generation. The creatures will be allowed to have a genome that's 24 genes long. That allows 24 connections in their little neural network brains. We'll allow them a maximum of four internal neurons. And so here's generation zero. We start off with 2000 randomly placed, and just like all the other simulations before, they do random stuff. Here they go, and it doesn't look very pretty. In the first half of their life, the radioactivity gets a bunch of them on the west side, and during the last half of their life, the radioactivity kills a bunch on the right side. Out of 2000 population, at the end of this generation, only 90 survived. They will be the parents, and they'll repopulate the world for the next generation. Let's just move forward all the way to generation 25. And here they go. At this point, you see they've evolved a little bit of a strategy. If they're born on the eastern side, they make their way west at a time when it allows them to survive when the radioactivity starts, but they yet haven't learned what to do if they're born on the west side - they just kind of stand there and take it. So at this point, out of the 2000 population, 412 survived. Let's move forward to generation 100. After this many generations of evolution, there's not a whole lot of difference. Now this time there's 508 survivors, which is not a whole lot better. So let's let them evolve a little bit and let's check in at generation 2000 and see how they're doing. Now at generation 2000, things are a little bit different. Now they all have evolved a tendency to assemble together just east of the midline and then step over the midline at the midpoint in their life. This is a much better strategy. If we were going to explicitly program these creatures to survive, that's pretty much the way that we would program them to survive. Now there are 1009 survivors - about half of the population. They don't get too much better than that. They lose about half of the population in each generation, and they always will lose some. There's no way to completely avoid being killed by the radioactivity, especially on the west side in the in the early part of their life, they can't escape quickly enough. I let this simulation run go all the way up to generation 34,000, and here's what that looks like. Their genetic diversity is very low - they all share very similar genes, and they seem to have stabilized on this strategy which they assemble together just right at the center line and then step over onto the left hand safe side halfway through the generation. And that's it for this video. Thank you for watching. Take care. Bye. [Music]
This was great! I didnβt expect to watch the whole thing but heβs got a real gentle voice and it was fascinating.
They started communicating by spelling out text and social engineered him into giving them an internet connection. They took over society and silenced him to hide their existence.
I mean his git had commits yesterday so I'm pretty sure he's still doing stuff..
Bro he put that out in 2020, probably took him like a year to make it...don't worry, he'll be back:)
So cool. Thanks for sharing!
Wow that was really cool!
Watched the whole thing. Great post. 3blue1brown makes very similar style math videos for anyone interested