The World Vol 6 · Evolution
ch 60 / 105
Chapter 60

Keeping the Winners

Selection from scored cards

The arena hands back sixty-four cards in population order and stops. Genome 43 is at the top with 179.4530 grams, half the generation is at 4.8405 grams or below, and twenty-six of the sixty-four ate nothing while the plate ran. A parent is chosen by drawing a fixed count of genomes at random and keeping the best of the ones drawn.

That count is the whole of the selection pressure, and it is one whole number anybody can turn. Exactly one genome in the population is exempt from the procedure: the best card of the generation crosses as itself, once, so the run cannot lose its current answer by bad luck.

The cards alone decide nothing. If every scored genome has the same chance to breed, the arena measured without selecting. If only the top card breeds, one lucky accident can erase the rest of the population before another useful accident appears.

A tournament of four sits between those failures. It can miss the champion, which keeps variety alive, but a high card beats a low card whenever the two appear in the same draw.

The page runs two hundred generations, then turns mutation all the way up as a failure case. The good run keeps better cards without pretending the best one is the only living animal; the bad run proves selection cannot rescue a stream of ruined children.

Four-genome tournament

Here is the whole procedure for one place in the next generation. Draw four genomes out of the sixty-four at random, with no regard for their scores. Look at the four cards. Keep the genome with the highest number on it and copy that one. Throw the other three away and do it again for the next place.

Be clear about how little that reads. It does not sort the board. It does not add the scores up, divide by the total, or care in the slightest what units the score is in. All it ever asks is whether one number is bigger than another, which means the selection pressure it applies at generation 200, where the best of the board is 1143.0000 grams and the average 1052.9949, is exactly the pressure it applied at generation 0, where the best was 179.4530, the average 24.5807, and twenty-six of the sixty-four were tied at nothing. A rule that divided by the total score would have been savage in the first generation and nearly blind in the last one, without anybody changing a line.

The other property is the one the page is named for. Four is arbitrary. Two is a gentler rule, sixteen is a harder one, and one is no rule at all: a tournament of one never looks at a score, so it hands back whichever genome the stream named. Nothing else in the procedure changes when that number does.

▣ Build · stage 1 — the stream, the rule, and one tournament
// internal/gene/pick.go
// StreamPick is the stream every choice of a parent draws from, and the
// third and last stream this volume's arena spends. The opening
// generation comes off 15, the trials off 21, the mutations off 16, and
// who gets to make a copy comes off here. It is its own stream for the
// reason all the others are: a run that changes how hard it selects
// must not change which genomes it started from or which four trials it
// measured them on.
const StreamPick = 18

// Rule is how a scored generation becomes the next one: how many
// genomes enter a tournament, how many of the best are carried through
// without being copied at all, and how wrong the copies are allowed to
// be.
type Rule struct {
	Size  int
	Elite int
	Rates Rates
}

// Tournament draws Size contestants and hands back the one of them with
// the highest score.
//
// One number a contestant, always, and the number is mapped onto the
// population by index: a draw evenly over 0 to 1 multiplied by the
// count of genomes and cut down to a whole number. There is no
// rejection anywhere in here and there is no second draw.
//
// A tie goes to the contestant drawn first.
func (r Rule) Tournament(on []float64, s *rand.Rand, into []int) int {
	won := -1
	for i := 0; i < r.Size; i++ {
		who := int(s.Float64() * float64(len(on)))
		if into != nil && i < len(into) {
			into[i] = who
		}
		if won < 0 || on[who] > on[won] {
			won = who
		}
	}
	return won
}

Two lines of that are the previous page's discipline arriving at the other end of a birth. The draw is s.Float64() scaled and floored, and not s.IntN(len(on)), which is the call anybody would reach for first. Mapping a whole sixty-four-bit number evenly onto a range that may not divide it cannot be done without throwing something away, so IntN rejects the numbers that would land unevenly and asks the generator for another one. The chance of that happening is small for these bounds, but it is not zero. This bench fixes draw counts so that changing a tournament setting does not also shift later draws. Seeded IntN with deterministic call order still reproduces; variable draw counts do not make it nondeterministic. A multiply and a floor keep one draw here, accepting the finite-sampling bias instead of rejection's variable consumption.

The tie rule is not a corner case here either. Twenty-six genomes of the opening generation score exactly 0.0000, so a tournament of four has about one chance in thirty-seven of drawing four of them, and something has to say who wins a contest between four animals that all did nothing. Saying it in the code is the difference between a run that replays and a run that depends on how a comparison happened to be written.

Sixty-four tournaments over the board the last page left behind, printed with the first eight of them opened up.

$ go run ./cmd/pick -mode round
pick: one generation of 64 scored on grams eaten, tournaments of 4

  the board the tournaments read

  the best of them                           179.4530
  the middle one                               4.8405
  the average                                 24.5807
  genomes that scored nothing whatever             26
  how far apart the genomes are                0.2858

  the first eight tournaments of the sixty-four

    slot   the genomes drawn, and what each of them scored                won
       0     38    0.0000   46  140.0000   30    0.0000   51    0.0000     46
       1     20    0.0000   11    0.0000    1    0.0000   47    0.4357     47
       2     50    0.0000   23    0.0000    7    0.0000   10    1.2740     10
       3     34    4.8405   45   40.0000   26    0.0000   61  100.0000     61
       4     61  100.0000   13   20.0000   38    0.0000   28    0.2160     61
       5     58    0.0000   61  100.0000   40   20.0000   35    2.7889     61
       6     31   14.8106   14    2.3110   14    2.3110    6    0.0000     31
       7     14    2.3110    9    0.0000   42   40.0000   42   40.0000     42

  what the sixty-four tournaments spent

  tournaments held                                       64
  contestants drawn, one number each                    256   off stream 18
  children copied, 790 numbers each                   50560   off stream 16
  places the champion is carried into                     1   and drew both anyway

  winners from the better half of the board          58 of 64
  what a tournament of 4 says it should be           93.75% of 64
  the average score of the winners                  72.3800
  the average score of the whole board              24.5807

Slots 6 and 7 are the ones to look at first. Slot 6 drew genome 14 twice and slot 7 drew genome 42 twice, out of four draws each. Contestants are drawn with replacement, so a tournament of four is four independent draws and not four different genomes, and a genome can beat itself. Nothing is wrong there. Removing the duplicate would mean drawing again, and drawing again is the branch the whole stream discipline exists to avoid; the cost of keeping it is that a tournament of four occasionally has only three real contestants in it, which weakens the pressure by an amount nobody will ever notice.

Slot 1 is the other thing to sit with. Three of its four contestants scored nothing and the fourth scored 0.4357 grams, so 0.4357 grams won, and genome 47 will be a parent sixty-three times over as good as nothing. That is not a defect in the rule. Half the board really is at nothing, and a rule that refused to breed from a bad generation would have to invent something to breed from instead.

The counts at the bottom are the accounting. Sixty-four tournaments of four cost 256 numbers off stream 18, and sixty-four children at 790 numbers apiece cost 50,560 off stream 16. Both totals are settled before the first card is read: they are the size of the population times the size of the tournament, and the size of the population times twice the length of a genome. Nothing about what the scores said can move either one. Fifty-eight of the sixty-four winners came from the better half of the board against an expected sixty, which is close enough that the gap is the ordinary roughness of sixty-four coin flips, and the winners average 72.3800 grams against the board's 24.5807.

∑ Math Interlude — what a tournament of k actually promises

One tournament, one question: what is the chance the winner comes from the better half of the board? The winner is the best of the k drawn, so the winner comes from the worse half only when every one of the k came from the worse half. Each draw lands in the worse half half the time, and the draws are independent, so

P(worse half) = (1 ÷ 2)k

and the chance of a winner from the better half is one minus that. For a tournament of two it is 0.75. For four it is 0.9375, which is the 93.75 percent the run prints and the 60 of 64 it expects. For eight it is 0.9961 and for sixteen it is 0.99998. Past about eight, the top half of the board has almost all of the children, and turning the dial further is buying refinements inside the top few genomes.

The more useful question is how fast one genome's descendants take over the whole population. Suppose selection were the only thing happening, with no mutation at all, and suppose one lineage holds a share p of the population and always wins when it is drawn. A tournament fails to contain it only when all k draws miss it, so the lineage takes a share

p′ = 1 − (1 − p)k

of the next generation, and while p is small that is very close to k times p. The share multiplies by about k every generation. Starting from one genome in sixty-four, the number of generations to reach the whole population is however many times k must be multiplied by itself to reach 64:

g = logk 64

which is 6 generations at a tournament of two, 3 at four, 2 at eight and 1 at sixty-four. The sweep further down this page measures the same thing on real runs and gets 63, 10, 7 and 2. Every one of those is slower than the arithmetic says, and the reason is the assumption in the middle: a lineage's members are not the champion, they are mutated copies of it, and most of them score worse than it does. The arithmetic gives the fastest possible takeover, and it is right about the ordering and about the fact that even the gentlest tournament finishes the job in a few dozen generations.

At a tournament of one there is no selection in the arithmetic at all, k is 1 and the share never grows. Lineages still disappear, because a lineage that happens to be picked no times in a generation is gone for good, and a lineage that is gone cannot come back. That is the same drift a small population always has, and it takes about as many generations as there are genomes: about 64 here, and the run measures 59.

kcontestants in one tournament; the selection pressure
ngenomes in a generation; 64 here
pthe share of the population one lineage holds, from 0 to 1
p′that share one generation later
ggenerations for one lineage to fill the population
xkx multiplied by itself k times
logk nhow many times k must be multiplied by itself to reach n

The loop that runs a generation is the tournament sixty-four times with the two streams spent in a fixed order. Stream 18 first, for who the parent is, then stream 16, for the copy. That order is not an implementation detail: it is the order every birth in this world takes its numbers in, and a run that ever reverses it is reading two different sequences.

▣ Build · stage 2 — one generation, scored and bred
// internal/gene/pick.go, continued
// Loop is one run of the arena from a drawn generation to whatever it
// becomes: the population, the two streams a generation spends, the
// rule that decides who makes the next one, and the ancestor every
// genome in it descends from.
type Loop struct {
	Arena *Arena
	Rule  Rule
	Pop   []*Genome
	Root  []int // the opening genome each member descends from
	Gen   int

	pick *rand.Rand
	mut  *rand.Rand
	drew []int
	kept Card
	held bool
}

// Step scores the population, works out what it came to, and breeds the
// next one out of it.
func (l *Loop) Step() Round {
	cards := l.Arena.Generation(l.Pop)
	on := make([]float64, len(cards))
	for i, c := range cards {
		on[i] = l.Arena.Score(c)
	}
	// ... the numbers this generation came to, into a Round ...

	next := make([]*Genome, len(l.Pop))
	root := make([]int, len(l.Pop))
	for i := range next {
		// ... one tournament and one copy a place, and the places the
		// champion is carried into: the box below is all of it ...
	}
	l.Pop, l.Root = next, root
	l.Gen++
	return r
}

Root is the only bookkeeping in there that is not part of the algorithm. It carries, for every genome alive, which of the sixty-four the run opened with it descends from: a founder starts as its own root and a child inherits its parent's. Nothing reads it, nothing selects on it, and it costs one integer a genome. It is there because counting how many roots are left is the cheapest honest measurement of how much variety a population has, and a page that talks about a population losing its variety without printing a number for it is a page asking to be believed.

One place in the next generation, and the numbers it costs Four bands stacked down the figure, joined by arrows. The first band is the scored board: sixty-four cards in population order, of which genome 38 scored nothing, genome 46 scored 140 grams and genome 43 scored 179.45. The second band is the draw: four numbers off stream 18, each multiplied by 64 and cut down to a whole number, giving genomes 38, 46, 30 and 51, with a note that there is no rejection and no second draw, so the count of numbers is the size of the tournament and nothing else. The third band is the result: the best of the four wins, ties going to the one drawn first, so genome 46 at 140 grams is copied and put through 790 numbers off stream 16. The fourth band is the first place of all, which is not copied at all: the champion crosses as itself, the same 395 numbers and the same card, and its own tournament and its own 790 numbers are drawn and thrown away so that the two streams sit where they would have sat anyway. ONE PLACE IN THE NEXT GENERATION THE BOARD, 64 CARDS IN POPULATION ORDER genome 38 0.0000 genome 46 140.0000 genome 43 179.4530 FOUR NUMBERS OFF STREAM 18, ONE A CONTESTANT 0.605128 x 64 = 38 0.730672 x 64 = 46 0.477536 x 64 = 30 0.802679 x 64 = 51 no rejection and no second draw: the count is the size of the tournament THE BEST OF THE FOUR, TIES TO THE ONE DRAWN FIRST genome 46 copied, then 790 numbers off stream 16 AND THE FIRST PLACE OF ALL, WHICH IS NOT COPIED the champion crosses as itself: same 395 numbers, same card
Figure 60.1 — one place, from board to child. The middle band is the whole of the selection pressure: change the four to a sixteen and nothing else on the page moves. The bottom band is the one place that skips all of it, and it still draws its four numbers and its 790, so that turning it off leaves both streams where they were.

Elitism for the top genome

A tournament has no memory. It reads the board it is given, and the board it is given is whatever the last round of copying produced. Suppose the best genome in the world is at 179.4530 grams and every one of its children is worse, which is the usual case: about thirty-five of a genome's three hundred and ninety-five numbers move in a copy, and moving a number in a controller that is already working is more likely to break it than to improve it. If the parent is not drawn again, or is drawn and loses, the best thing this run has ever found is gone. Nothing anywhere kept a copy of it. The population is the only memory the search has.

Elitism is the repair, and it is one line: the best card of the generation is carried into the next generation as itself. Not re-copied at low rates, not copied and then checked, not re-scored to see whether it still deserves the place. The same three hundred and ninety-five numbers, in the same order, in the first slot.

Elite retention repeats the champion’s score on this fixed trial set under the recorded runtime. It avoids noisy rescoring, but the trial set can still favor a genome that performs poorly on new trials.

▣ Build · stage 3 — the place that draws everything and uses none of it
// internal/gene/pick.go — inside Loop.Step, the loop over places
		won := l.Rule.Tournament(on, l.pick, l.drew)
		r.Picks += l.Rule.Size
		parent := won
		if l.Rule.Greedy {
			parent = r.Champ
		}
		kid, m := l.Rule.Rates.Child(l.Pop[parent], l.mut)
		r.Draws += m.Draws
		if i < l.Rule.Elite {
			next[i], root[i] = l.Pop[r.Champ].Copy(), l.Root[r.Champ]
			continue
		}
		next[i], root[i] = kid, l.Root[parent]

The two lines naming parent are the worked failure at the foot of this page arriving early, and they can be read as though they were not there: Rule.Greedy is false in every run on this page except that one, so parent is won throughout and the flag is explained where it is used.

The elite place holds its tournament, copies the winner, puts the copy through a whole mutation pass, and then drops all of it on the floor and writes the champion in instead. Four numbers off stream 18 and 790 off stream 16, spent on a child that is never born. That is the rule the copying page was built on, applied to a different question: every draw a birth would have made is made whether the birth happens or not. What it buys is that Elite: 1 and Elite: 0 are two runs off one seed that read the same numbers in the same order, and differ in exactly one thing, which is the genome sitting in the first slot. Without it the two runs would be reading different parts of two sequences from the first generation on, and nothing you measured about the difference between them would mean anything.

Untouched is a claim with two halves and both of them are checkable. The genome has to come across gene for gene, and the card the arena hands back for it next generation has to be the card it was carried in with, to the last bit of every one of its numbers. The second half is the one that catches a subtle mistake: a champion that was carried correctly but scored inside a world that had drifted would come back with a different number and would quietly stop being the champion.

$ go test ./internal/gene/ -run 'TheChampionCrosses|ElitismDoesNot|ATournamentSpends|HarderTournaments' -v
=== RUN   TestTheChampionCrossesUntouched
--- PASS: TestTheChampionCrossesUntouched (0.09s)
=== RUN   TestElitismDoesNotMoveTheStreams
--- PASS: TestElitismDoesNotMoveTheStreams (0.11s)
=== RUN   TestATournamentSpendsOneDrawAContestant
--- PASS: TestATournamentSpendsOneDrawAContestant (0.00s)
=== RUN   TestHarderTournamentsPickBetterParents
--- PASS: TestHarderTournamentsPickBetterParents (0.00s)
PASS
ok  	theworld/internal/gene	0.203s

The first walks eight generations and demands that the carried genome match the previous champion at every one of the 395 genes, that the card come back identical, and that the best score never fall. The second runs one seed twice, keeping a champion and dropping one, and then asks both runs' generators for one more number: if the elite place had skipped its draws, those two numbers would differ. The third holds two hundred tournaments over a board where everything is tied and two hundred over a board where nothing is, and demands that both leave the stream in the same place. The fourth is the dial itself: over one board, a bigger tournament has to hand back a better average parent, and a tournament of one has to hand back the average of the whole board, because it never looks at a score.

Now the same forty generations twice, once with a champion kept and once without.

$ go run ./cmd/pick -mode elite -gens 40
pick: 40 generations of 64, tournaments of 4, run twice off one seed

     gen    champion kept   champion dropped
       0         179.4530           179.4530
       5         298.9953           267.7949
      10         557.2623           621.8351
      15         557.2623           607.6523
      20         670.6833           697.9127
      25        1059.0000           963.0000
      30        1103.0000          1062.0000
      35        1109.0000          1116.0000
      40        1113.0000          1126.0000

  times the best score fell, champion kept                0
  times it fell with the champion dropped                12
  the worst of those falls, in grams               149.4598
  generations the carried card came back identical     40 of 40

  numbers each run took off stream 18                 10496
  numbers each run took off stream 16               2072960
  the two runs drew the same numbers in the same order

Read the two columns before reading the summary, because the summary is not what most people expect. The run without a champion finishes ahead: 1126.0000 grams against 1113.0000. It was ahead at generation 10, ahead at 20, behind at 25 and 30, and ahead again at the end. Elitism did not win this race and does not promise to. What it promises is the first line of the summary: in forty generations the best score in the world went down zero times with a champion kept and twelve times without one, and the worst of those twelve falls threw away 149.4598 grams of progress that something had already found and nothing had written down.

That is the trade, stated plainly. A run with a champion is a ratchet: whatever it has found, it still has. A run without one wanders, and wandering is not always worse, because a genome that is merely very good can be in the way of a genome that would have been better. What makes the ratchet the right default here is that it costs one place in sixty-four and it makes the best number the run prints mean something. Without it, the best score at generation 40 is a report on the last generation and not on the run.

The 200-generation selection run

Everything above happens in one generation. The interesting behaviour is in hundreds of them, and it costs nothing but time: two hundred generations of sixty-four genomes on four trials of six hundred ticks is fifty-one thousand two hundred trials, and the whole of it runs in about twenty seconds of wall clock on the machine this was written on, an eight-core Ryzen 7 3700X on Linux, with the generation split across eight goroutines. Yours will differ; the numbers below will not.

$ go run ./cmd/pick -mode climb
pick: 200 generations of 64, tournaments of 4, 1 champion kept

     gen         best      average   at nothing     spread   lineages
       0     179.4530      24.5807           26     0.2858         64
       1     201.6845      64.8548            5     0.2762         24
       2     221.6845     110.0989            0     0.2553         11
       5     298.9953     180.3688            0     0.1514          3
      10     557.2623     351.8400            0     0.0616          1
      20     670.6833     536.3701            0     0.0749          1
      50    1116.0000    1039.9645            0     0.0661          1
     100    1121.0000    1051.9895            0     0.0546          1
     150    1143.0000    1060.6508            0     0.1002          1
     200    1143.0000    1052.9949            0     0.1188          1

  the champion of the last generation, and the one the run opened with

                   generation 0 generation 200
  score, grams         179.4530      1143.0000
  Bulk                  65.7271        46.1673   factors 1.6432 and 1.1542
  Bite                   0.2393         1.0000   factors 0.9573 and 4.0000
  Reach                  3.1559         3.1616   factors 3.1559 and 3.1616
  Sight                 20.9548         8.0066   factors 1.7462 and 0.6672
  Top                    0.1823         1.7595   factors 0.4052 and 3.9099
  Convert               11.4661        11.2572   factors 2.8665 and 2.8143
  Full                1160.6651      1061.1300   factors 2.9017 and 2.6528

  the opening genome the last champion is from           43
  what that genome scored in generation 0          179.4530
  where it stood on that board                        1 of 64
  the generation it was the last lineage standing         10

  a bite of 1.0000 grams, 300 bites a trial, 4 trials: 1200.0000 grams is all there is
  it scored 1143.0000, which is 95.2 percent of that
  the fodder standing in the four trials comes to 1880.0000 grams

The at nothing column empties in two generations. Twenty-six genomes that could not find food at all became five and then none, and they did not learn anything: they were replaced by the children of genomes that could. The average follows the best up the page and then sits about eighty grams behind it for the last hundred and fifty generations, which is the standing cost of mutation. Every child is a copy with about thirty-five numbers moved, most of those moves make a slightly worse animal, and the gap between the best and the average is the size of the damage the population is carrying at any moment.

The last block is the reason to run two hundred generations rather than twenty. A bite is two ticks, so six hundred ticks hold at most three hundred bites, and the largest bite this world's ranges allow is a factor of four on the founding row's quarter gram: exactly 1.0000 grams. So the most any animal can possibly take off four trials is 1,200.0000 grams, and the champion of generation 200 took 1,143.0000, which is 95.2 percent of a life spent with its mouth on something. Nothing told it that number. Nothing in the code knows the number exists. The score is grams eaten, and grams eaten is what it went and got.

The body it did it with is readable factor by factor. Bite sits at exactly 4.0000, hard against the top of its range, which is the clamp inside every write to a genome doing its job: the copies kept pushing and the range kept refusing, and a gene pinned at an end is a gene the search is still leaning on. Top is at 3.9099, so the animal that won this arena sprints at 1.7595 cells a tick, while genome 43, the animal that won generation 0, crawled at 0.1823 and won by standing still with a wide mouth. Two hundred generations turned a sitter into a sprinter, because the sitter had already eaten everything it could reach by the end of trial one and the only grams left on the plate were somewhere else. Bulk came down by thirty percent, which is thirty percent off the bill a standing body pays every tick. Reach barely moved at all: 3.1559 to 3.1616, because the whole population descends from genome 43 and genome 43's reach was already most of what a mouth can usefully have.

Sight is the odd one. It fell from twenty cells to eight. Nothing in this arena charges an animal energy for its eyes, so nothing was saved by shortening them, and the only other thing Sight does is set the span every eye reading is divided by. A creature with a twenty-cell view and one with an eight-cell view hand their controllers two different rescalings of the same world. The gene and the weights that read it are inherited together and are moved together, and this lineage settled on the pair it settled on. That is as much as the run supports, and more than the run supports would be storytelling.

One line in the middle of that block is the whole of the next section in advance. The lineage still standing at generation 200 is genome 43's, and it was the only one left from generation 10. Sixty-three of the sixty-four opening genomes have no descendants at all, and the arena spent one hundred and ninety generations refining what one of them stumbled into.

Six settings of one number

Now turn the dial. Same seed, same arena, same four trials, same opening sixty-four genomes, same mutation rates, same one champion. The only thing that differs between these six runs is how many contestants a tournament has.

$ go run ./cmd/pick -mode sweep
pick: 200 generations at 6 settings of the tournament, 1 champion kept

  the best score in the generation, as the run goes on

    size      gen 1      gen 5     gen 25     gen 50    gen 100    gen 200
       1   179.4530   181.2308   398.4640   642.6098   782.8505   951.8972
       2   220.0000   260.0000  1010.7753  1052.7441  1121.0000  1134.0000
       4   201.6845   298.9953  1059.0000  1116.0000  1121.0000  1143.0000
       8   201.6845   460.0000  1073.0000  1106.0000  1132.0000  1146.0000
      16   277.4477   591.9911  1091.0000  1111.0000  1113.0000  1119.0000
      64   224.3077   667.7905  1094.0000  1114.0000  1122.0000  1124.0000

  and what the population looked like at generation 200

    size         best      average     spread   lineages down to one at
       1     951.8972     175.5683     0.2117          1  generation 59
       2    1134.0000     991.0108     0.1269          1  generation 63
       4    1143.0000    1052.9949     0.1188          1  generation 10
       8    1146.0000    1041.6535     0.0498          1   generation 7
      16    1119.0000     998.1320     0.0390          1   generation 6
      64    1124.0000     943.4744     0.0331          1   generation 2

Read the first table down the columns and the ordering reverses as you go across. At generation 5 the board is in order of pressure: the hardest tournament is winning by a street, 667.7905 against 298.9953 for four and 181.2308 for one. At generation 200 it is nearly the opposite: of the five settings that select at all the two hardest are the two worst finishers, 1119.0000 and 1124.0000, and the best result on the page belongs to a tournament of eight with a tournament of four just behind it. Only the tournament of one, which never reads a score, finishes below them. Sixteen and sixty-four spent the first twenty-five generations winning and the next hundred and seventy-five being overtaken.

The second table says what they spent to win early. A tournament of sixty-four had lost sixty-three of its sixty-four lineages by generation 2, and its population at generation 200 sits at a spread of 0.0331, against 0.1188 for a tournament of four and 0.2858 for the generation everything started from. Nearly nine tenths of the variety the run was handed was gone in two generations, and it never came back, because the only thing in this world that makes variety is mutation, and mutation makes it about thirty-five genes at a time.

The bottom of the dial fails the other way and fails harder. A tournament of one is not selection at all, and its run still climbs, from 179.4530 to 951.8972, which is worth understanding before it is dismissed: the climb is the champion place doing all of the work by itself. One genome in sixty-four is protected, the protected genome is occasionally drawn as a parent, and its better children occasionally become the new champion. That is a ratchet with sixty-three places of pure drift attached to it, and the population it leaves behind is a wreck. The average at generation 200 is 175.5683 against 1052.9949 for a tournament of four, and running that setting on its own with go run ./cmd/pick -mode climb -size 1 shows twenty-eight of its sixty-four genomes back to scoring nothing whatever at the end of it.

All of that is one seed and one arena. The ordering among the middle settings, four against eight against two, is inside the wobble of a single run and nobody can read it as a ranking. The two ends are not: no seed is going to make a tournament of one competitive on the average, and no seed is going to give a tournament of sixty-four its variety back.

Mutation at full scale

There is one more setting past sixty-four, and it is the one that is tempting to write in the first place. The board is already scored. The best genome is already known. Why hold sixty-four tournaments to find out something a single pass over the scores would tell you? Copy the best one sixty-four times and get on with it.

⚠ Worked failure — a population of one animal, sixty-four times over
// internal/gene/pick.go — inside type Rule struct
	// Greedy is the selection rule as it is tempting to write it: the
	// board is already sorted, so hand every place in the next
	// generation to the genome at the top of it and skip the
	// tournaments. It is kept behind a flag so what the top of the
	// pressure dial costs a population can be run instead of described,
	// and it is false in every run this book ships. The tournaments are
	// drawn and thrown away under it, so a greedy run and a tournament
	// run read the same numbers in the same order.
	Greedy bool
$ go run ./cmd/pick -mode greedy
pick: 200 generations of 64, run twice off one seed

         every child off the best genome   tournaments of 4
     gen        best     average    spread        best     average    spread
       0    179.4530     24.5807    0.2858    179.4530     24.5807    0.2858
      20    794.7282    626.2426    0.0303    670.6833    536.3701    0.0749
      40    850.8741    656.0192    0.0258   1113.0000   1052.7217    0.0574
      60    972.7943    812.6964    0.0275   1121.0000   1048.7328    0.0630
      80   1024.8977    859.1105    0.0279   1121.0000   1062.3461    0.0687
     100   1113.0000    763.3327    0.0259   1121.0000   1051.9895    0.0546
     120   1126.0000    773.2897    0.0285   1143.0000   1087.1920    0.0731
     140   1133.0000    853.2018    0.0276   1143.0000   1071.8725    0.0901
     160   1134.0000    816.3410    0.0285   1143.0000   1077.2450    0.1081
     180   1136.0000    833.5280    0.0272   1143.0000   1081.7466    0.1046
     200   1136.0000    816.8780    0.0282   1143.0000   1052.9949    0.1188

                                                     greedy    tourney
  the best genome's score                         1136.0000  1143.0000
  the average of the generation                    816.8780  1052.9949
  how far apart the genomes are                      0.0282     0.1188
  how far apart they were at generation 20           0.0303     0.0749
  the generation it came down to one lineage              1         10
  of the 64, how many share one ancestor                 64         64

The first surprise is that the greedy run is ahead at generation 20, by a hundred and twenty-four grams. That is the whole seduction of the thing: it works, visibly, immediately, in the part of a run somebody is most likely to watch. By generation 40 it is two hundred and sixty grams behind and it stays behind for the next hundred and sixty generations, finishing seven grams short after two hundred.

The seven grams are not the failure. The failure is the two columns beside them. At generation 20 the greedy population's spread is 0.0303, against 0.2858 for the generation it started from: nearly nine tenths of the variety in the world was gone in twenty generations, and the genomes left were all cousins, every one of them a mutated copy of one animal that had been alive the generation before. It came down to a single lineage at generation 1, which is as fast as that can possibly happen. And unlike the tournament run, which climbs back to a spread of 0.1188 as its lineage explores, the greedy run never recovers: every reading after the first one is between 0.0258 and 0.0303, because every generation of it is rebuilt from scratch out of one genome.

The average is where that shows up as damage. The greedy population averages 816.8780 grams against the tournament's 1052.9949, and the reason is precise: in the greedy run, every one of the sixty-three children is one fresh mutation pass away from the champion, so the population is one generation of unfiltered mistakes. In the tournament run, a parent had to win a tournament to become a parent, so it had already survived being measured, and its children are copies of something that was already checked. The tournament's population is filtered twice and the greedy population is filtered once.

The reasoning from symptom to cause has no bug in it, again. The code did what it was told. It was told that the best genome is the best genome, which is true, and asked to act on that once per place, which turned sixty-four searches into one search run sixty-four times over. A population is not a list of the answers so far; it is the list of places the next generation is allowed to look, and a population of one animal can only look in one place. Everything the greedy run does after generation 1 is hill climbing with sixty-four attempts a step, and hill climbing gets stuck where the hill stops.

Why tournaments tune pressure

Strip out the creatures and the grams and there are two mechanisms on this page, and they answer two different questions.

The tournament answers "how strongly". Its virtue is that it turns a vague preference into a whole number with an arithmetic meaning: a tournament of k gives the better half of the board 1 minus one over two to the k of the children, and lets one lineage take over in about log to the base k of n generations. It reads only comparisons, so it is immune to what the score's units are, immune to a score that grows tenfold over a run, and immune to a board where half the entries are tied at zero. All three of those would have broken a rule that divided by the total.

Elitism answers "what happens to the best one", and it is a different kind of decision that people run together with the first one because both of them favour good genomes. The difference is that pressure is a rate applied to everybody and elitism is a guarantee applied to one. Keeping a champion converts the best score from a report on the current generation into a report on the whole run, at the cost of one place in sixty-four. It also has a real cost, and the second exercise below runs it: eight champions instead of one is eight places not searching, and the population's variety pays for it.

The dial itself is the thing to carry away, and it is not particular to creatures. Every search that keeps a set of candidates has the same trade in it: pressure buys progress now and spends the variety that progress later has to come from. The measurements on this page say the same thing three ways. Turn it up and the early generations are the best on the page and the late ones are the worst. Turn it down to nothing and the population keeps its variety and does nothing with it. Turn it all the way up, past the end of the dial, and there is no population left to have variety at all. Nothing in the arena has an opinion about which of those you want; the right setting depends on how long the run gets, and nobody can read it off the first twenty generations, which is exactly when it looks easiest to decide.

✓ Checkpoint — the tournament, the champion, and the price of pressure
  • Given a tournament size, You can work out the chance its winner comes from the better half of the board, and say why that number does not change when the scores do.
  • You can say how many numbers a generation takes off stream 18 and off stream 16, and why neither total depends on what any card said.
  • You can explain why the contestants are drawn with Float64 and a floor instead of with IntN, and what a single extra draw would do to a run.
  • You can state both halves of what "carried through untouched" claims, and describe the check that would catch a champion that was carried but quietly re-scored.
  • Handed a run's count of surviving lineages by generation, You can say roughly what tournament size produced it, and say what the spread number adds that the lineage count cannot.
  • You can say what the greedy run gains in its first twenty generations, what it gives up to get it, and why its population averages two hundred and thirty grams below the tournament's.
⚡ Exercises — try first, then reveal
Exercise 1 — a run with no selection in it. A tournament of one never reads a score. Predict what its best score does over fifty generations, then run go run ./cmd/pick -mode climb -size 1 -gens 50 and explain what is actually doing the climbing.

It climbs, from 179.4530 to 642.6098 in fifty generations, which looks impossible for a rule that never compares two genomes. The champion place is doing all of it. One genome in sixty-four is carried through untouched, so the best score can never fall; the other sixty-three are copies of parents picked at random, and once in a while one of them lands above the champion and takes the place. That is a ratchet bolted to a random walk, and a ratchet alone is a real if very slow search.

What it cannot do is drag the population along. The average at generation 50 is 154.5773 against 1039.9645 for a tournament of four, and seven genomes are still scoring nothing whatever. Take the ratchet off as well, with -size 1 -elite 0, and the last thing holding the run up goes: the best score falls from 179.4530 to 140.0000 in the first generation and is still at 140.0000 at generation 5.

Exercise 2 — eight champions instead of one. If keeping one genome untouched is good, keeping eight might be better. Run go run ./cmd/pick -mode climb -elite 8 -gens 50 against the same fifty generations at -elite 1 and say what the extra seven bought and what they cost.

They buy a faster start and nothing at the end. With eight kept, generation 10 is at 778.4839 grams against 557.2623 for one kept, and generation 50 lands at 1119.0000 against 1116.0000, which is a difference of nothing after fifty generations of arithmetic.

The cost is in the two right-hand columns. Eight champions is eight of sixty-four places that are not searching anything, and the run is down to a single lineage by generation 10 with a spread of 0.0456, against 0.0616 for one champion. By generation 50 it is at 0.0392 against 0.0661. Elitism is selection pressure wearing a different name, and eight sixty-fourths of it is a lot of pressure to apply without meaning to.

Exercise 3 — the same dial, a different world. The sweep is one seed. Run go run ./cmd/pick -mode climb -seed 7 -gens 50 at -size 1, -size 4 and -size 64, and say which of this page's claims survive a change of world and which were facts about one run.

Seed 7 is a different arena and a different opening sixty-four: its best genome starts at 459.5906 grams instead of 179.4530, and twenty-two of its genomes score nothing instead of twenty-six. The ends of the dial behave exactly as before. A tournament of sixty-four is down to one lineage at generation 2 with a spread of 0.0309, and at generation 50 it sits at 1104.0000 with a population averaging 1020.9145. A tournament of one is still at three lineages at generation 50, still has thirteen genomes scoring nothing, and averages 221.8210.

The middle is where the single run was never evidence. On seed 5 a tournament of four reached 1116.0000 by generation 50; on seed 7 it reaches 1119.0000, and the gap between it and a tournament of sixty-four is fifteen grams rather than two. Claims like "eight beats four" need many seeds and this page has one. Claims like "the top of the dial destroys the population's variety in two generations" survive, because the mechanism behind them is arithmetic and not luck.

Every child on this page had one parent. Three lineages were still standing at generation 5 and one at generation 10, so two of them went out inside five generations with everything their lines had found, and whatever that was, genome 43's descendants had to find it again from scratch or do without it. A population that copies one parent at a time can only ever hold what one chain of copying stumbled into, and the two hundred generations above are the record of one chain doing exactly that, very well, alone.