Edge Preserving Cyclic Crossover

Upfront apology.

This kind of stuff is very hard to describe in words, but if you plow on through them anyway, the examples are probably a lot clearer (they come right from the print-outs I used to debug the code).

What is it?

Cyclic Crossover is a well known heuristic used with "permutation genomes", genomes which must preserve the property that they are a reordering of the elements of some alphabet, usually either a real alphabet, or else some analogue to "the numbers from 1 to N".

With some complexity, a cyclic crossover takes a subset of codons from one parent, and the complementary set (with respect to the total alphabet) from the other parent, and intercalates them in the same order as they had in their respective parent genomes, and in the same matching "slots" each subset occupied in both parent genomes.

The crossover is "cyclic" because it continues to swap codons until it runs into the one with which it began, again, thus guaranteeing that it has preserved the permutation genome property.

The cycle swapped may be a subcycle of the permutation, allowing some chance for progress to occur, or may merely have swapped the two involved parent genomes completely if the permutation contains no subcycles, a complete waste of time.

Edge Preserving Cyclic Crossover blends this well known Cyclic Crossover heuristic for permutation genomes with a new capability to cross matched binsfull of codons that represent edges or clusters of edges that are the same in the two parent genomes being crossed, instead of merely focusing on crossing individual codons.

Why do it?

By swapping binsfull of codons intact (along with individual codons where edges do not match between the parents), the common, shared edges represented by the codons in those bins are preserved into the next generation of genomes.

When two genomes are selected from a population for crossover on the basis of being among the more fit parents for the next generation, there is an easy presumption that things they have in common may be part of the reason they are in the fitter subset of genomes. If this is true, then preserving what they have in common during crossover while trying to produce new fitness enhancing changes from the "stuff" they do not have in common is a worthwhile goal.

What happens when you do it?

Population diversity drops like a rock. Consider the thought problem of a population with just three members, A, B, and C. When A and B are crossed, not only are their common edges preserved in the offspring, but also some fractions of each parent's edges that are not shared in common are also preserved in the children, say D and E. Similarly if A and C are crossed (yielding F and G) and if B and C are crossed (yielding H and I). Back-crosses like E with F and I with D now find not only the edges deliberately preserved still matching, but also some fraction of the edges accidentally preserved also still matching, so that the number of edges preserved at each generations' crossings tend to increase inexorably with time, which when all edges are preserved means that all genomes are identical.

Since this less diverse population also preserves common features of its fitter members, the usual case is that population fitness increases to some limiting case.

What issues arise?

Although Edge Preserving Cyclic Crossover is very good at bringing the population to convergence, it is not so good at making that convergence occur at the global optimum. A genetic algorithm whose only heuristic is Edge Preserving Cyclic Crossover removes population diversity too fast, and most usually converges to a local optimum instead. Thus, while Edge Preserving Cyclic Crossover is highly suitable for a mixed heuristic genetic algorithm (GA) engine, it is highly unsatisfactory as the one and only heuristic being run in a GA engine.

When edges, or strings of edges, occur in both parent genomes, they don't necessarily "face the same direction" in those genomes, so that crossing them may vastly worsten fitness where the ends of substrings meet other codons. If this issue is ignored, Edge Preserving Cyclic Crossover is too infrequently successful at reducing entropy in the population to be worthwhile.

The alternative is to try the bins/substrings of matched edges in both orders, and retain the more fit versions. Now the heuristic is too strong, but "plays well with others", as mentioned above.

Where the crossover sets bins next to one another in the offspring, this end-for-end swapping to find the more fit candidate offspring quickly turns into a "two to the M" problem, which can become computationally intractible for large genomes in a maturing population where parents probably have lots of edges in common. This means that some compromise that seeks to return an improved child genome without trying all possible cases must be accepted. In the "Traveller with KPD mods" implementation, long sets of adjacent multi-codon bins in the progeny are tested for best fitness in sets of at most eight at a time to limit processing requirements.

How does it work?

Glad you asked! For the example here, we'll consider a population of permutation genomes of length 12, codon values running from 0 to 11.

Consider a couple of parent genomes selected for mating by some fitness criterion.

[0,5,10,4,11,8,7,9,1,3,2,6] father
[0,4,6,9,1,3,2,10,8,11,5,7] mother

We copy them to sibling offspring genomes without change.

[0,5,10,4,11,8,7,9,1,3,2,6] son
[0,4,6,9,1,3,2,10,8,11,5,7] daughter

We create an edgelist for each, where an edge runs from the previous codon to the current codon, with wrapping around the end of the genome, and the canonical edge representation puts the lower valued codon first (to simplify matching edges).

[(0,6),(0,5),(5,10),(4,10),(4,11),(8,11),(7,8),(7,9),(1,9),(1,3),(2,3),(2,6)] son edgelist
[(0,7),(0,4),(4,6),(6,9),(1,9),(1,3),(2,3),(2,10),(8,10),(8,11),(5,11),(5,7)] daughter edgelist

We create sorted versions of each edgelist, to enable the matching of their common edges.

[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist

We march a pointer from left to right on each list, always advancing the pointer currently at the lower (sort order valued) edge, or advancing both at a match, marking matches as we find them:

   v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
   ^

   v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
         ^

         v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
         ^

               v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
         ^

               v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
               ^

               v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
               ^

             match match   v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match   ^

             match match match   v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match   ^

             match match match         v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match   ^

             match match match         v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match          ^

             match match match         v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                ^

             match match match                v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                ^

             match match match                       v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                ^

             match match match                       v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                      ^

             match match match                              v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                      ^

             match match match                              v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                             ^

             match match match                              v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                                   ^

             match match match                                    v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                                   ^

             match match match                                          v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                                   ^

             match match match                                          v
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                                          ^

             match match match                                        match
[(0,5),(0,6),(1,3),(1,9),(2,3),(2,6),(4,10),(4,11),(5,10),(7,8),(7,9),(8,11)] sorted son edgelist
[(0,4),(0,7),(1,3),(1,9),(2,3),(2,10),(4,6),(5,7),(5,11),(6,9),(8,10),(8,11)] sorted daughter edgelist
             match match match                                        match

We keep a list of matched edges (mostly for debugging use).

[(1,3),(1,9),(2,3),(8,11)] matched edges

We copy annotations of matchedness back to the unsorted edge lists, marking edges that were matched, and also to the genome lists, marking codons that were part of some matched edge, with the previous codon, the next codon, or both; we will need both annotations to detect strings of multiple matched edges correctly.

                                  match              match match match
[(0,6),(0,5),(5,10),(4,10),(4,11),(8,11),(7,8),(7,9),(1,9),(1,3),(2,3),(2,6)] son edgelist
[(0,7),(0,4),(4,6),(6,9),(1,9),(1,3),(2,3),(2,10),(8,10),(8,11),(5,11),(5,7)] daughter edgelist
                         match match match               match

           m m   m m m m
[0,5,10,4,11,8,7,9,1,3,2,6] son
[0,4,6,9,1,3,2,10,8,11,5,7] daughter
       m m m m    m  m

Using the preceding annotation, reclassify the genome codons into bins of one (unmatched), two (single matched edge), or several (chain of matched edges) codons, retaining the order in which they first appeared in each genome.

[(0),(5),(10),(4),(11,8),(7),(9,1,3,2),(6)] son bins
[(0),(4),(6),(9,1,3,2),(10),(8,11),(5),(7)] daughter bins
There are issues that don't happen to occur in this example. One must be sure not to start building bins in the middle of a bin that wraps around the end of a genome, lest a bin become two bins, and one must not artificially consolidate edges that do not abutt in the same order (or entirely opposite orders) in both genomes.

Notice in that above, that the same bins occur in both bin lists, though not in the same places in the bin lists, and not necessarily with the codons both in "direct" order. One bin can be in the reverse order of the other, but otherwise identical; here son edge (11,8) matches daughter edge (8,11), creating those bins. Notice also that since the genome is a permutation genome, and thus all codons are unique in each genome, to match bins we need merely check whether one end of one bin is a codon with matches the codon at either end of the other bin; internal codons need not be checked, we've "already done that".

Next, to avoid artifacts of treating a reorientable reversable ring as a fixed-origin array, we put the second genome in general position (bin-wise) with respect to the first genome, in this example by picking a son bins pivot point at son offset 5, the "(7)" bin, then offsetting the daughter by five bins to the right and then reversing the daughter genome about that pivot point. Here, daughter bin "(0)" rotates under son bin "(7)", and then the daughter genome is reversed, so that

[(0),(5),(10),(4),      (11,8),(7),   (9,1,3,2),(6)] son bins
[(0),(4),(6), (9,1,3,2),(10),  (8,11),(5),      (7)] daughter bins

when the daughter genome bins are offset becomes

[(0),      (5), (10),  (4),(11,8),(7),(9,1,3,2),(6)] son bins
[(9,1,3,2),(10),(8,11),(5),(7),   (0),(4),      (6)] daughter bins

then when the daughter genome is reversed (bin-wise but not the bin internals, other efforts will compensate for that) while holding daughter bin "(0)" under son bin "(7)" becomes

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] son bins
[(8,11),(10),(9,1,3,2),(6),(4),   (0),(7),      (5)] daughter bins

Because we are depicting the daughter bins list in its general orientation here, but are not swapping bin internals in reality, we must depict the son multi-codon bins as reversing when they swap down to the daughter bin, to keep the bin ends next to the actually adjacent (in this case) daughter bins single codons. This is called smoke and mirrors, pay no attention.

Now we are ready to do our binwise crossover; however, while doing it, we will put the genomes temporarily in a state inconsistent with the "permutation genome" requirement, and partially as a consequence of that, we are going to need a duplicate of one bin list that is unmutilated, in which we can find original locations of bins. Since the son genome is still in canonical genome order, it is easier to use that genome, so we create an artifical "father" bin list as a tool we will later discard.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
                                   ^
                                   v
[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] son bins
[(8,11),(10),(9,1,3,2),(6),(4),   (0),(7),      (5)] daughter bins

The cursors indicate the starting point for our crossover cycle, with son bin "(7)" about to swap down and daughter bin "(0)" about to swap up. Our cycle will be complete when daughter bin "(7)" is swapped up, which will restore the "permutation genome" requirement for son and daughter genomes.

Our cycle runs like this:

Here we go! Son slot five bin "(7)" moves down, daughter slot five bin "(0)" moves up, find newly duplicate son bin "(0)" in father at slot zero, move father and son cursors to slot zero.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
                                   ^
                                   v
[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] son bins
[(8,11),(10),(9,1,3,2),(6),(4),   (0),(7),      (5)] daughter bins

Son slot zero bin "(0)" moves down, daughter slot zero bin "(8,11)" moves up, find newly duplicate son bin "(8,11)" (really "(11,8)") in son using father bins at father bins slot four, move father and son cursors to slot four.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
  ^
  v
[(0),   (5), (10),     (4),(11,8),(0),(9,1,3,2),(6)] son bins
[(8,11),(10),(9,1,3,2),(6),(4),   (7),(7),      (5)] daughter bins

Son slot four bin "(11,8)" moves down, daughter slot four bin "(4)" moves up, find newly duplicate son bin "(4)" at father bins slot three, move father and son cursors to slot three.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
                            ^
                            v
[(8,11) (5), (10),     (4),(11,8),(0),(9,1,3,2),(6)] son bins
[(0),   (10),(9,1,3,2),(6),(4),   (7),(7),      (5)] daughter bins

Son slot three bin "(4)" moves down, daughter slot three bin "(6)" moves up, find newly duplicate son bin "(6)" using father bins at slot seven, move father and son cursors to slot seven.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
                        ^
                        v
[(8,11) (5), (10),     (4),(4),   (0),(9,1,3,2),(6)] son bins
[(0),   (10),(9,1,3,2),(6),(8,11),(7),(7),      (5)] daughter bins

Son slot seven bin "(6)" moves down, daughter slot seven bin "(5)" moves up, find newly duplicate son bin "(5)" using father bins at slot one, move father and son cursors to slot one.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
                                                 ^
                                                 v
[(8,11) (5), (10),     (6),(4),   (0),(9,1,3,2),(6)] son bins
[(0),   (10),(9,1,3,2),(4),(8,11),(7),(7),      (5)] daughter bins

Son slot one bin "(5)" moves down, daughter slot one bin "(10)" moves up, find newly duplicate son bin "(10)" using father bins at slot two, move father and son cursors to slot two.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
         ^
         v
[(8,11) (5), (10),     (6),(4),   (0),(9,1,3,2),(5)] son bins
[(0),   (10),(9,1,3,2),(4),(8,11),(7),(7),      (6)] daughter bins

Son slot two bin "(10)" moves down, daughter slot two bin "(9,1,3,2)" moves up, find newly duplicate son bin "(9,1,3,2)" using father bins at slot six, move father and son cursors to slot six.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
              ^
              v
[(8,11) (10),(10),     (6),(4),   (0),(9,1,3,2),(5)] son bins
[(0),   (5), (9,1,3,2),(4),(8,11),(7),(7),      (6)] daughter bins

Son slot six bin "(9,1,3,2)" moved down, daughter slot six bin "(7)" moves up, we have moved up the daughter duplicate of the original son bin we moved down, our termination condition, and so the cycle is complete.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] father bins
                                       ^
                                       v
[(8,11) (10),(9,1,3,2),(6),(4),   (0),(9,1,3,2),(5)] son bins
[(0),   (5), (10),     (4),(8,11),(7),(7),      (6)] daughter bins

Unfortunately, this case had no subcycles, and in fact, we have simply swapped the entire bin lists for the sibling genomes. Since we didn't take any trouble to swap bin internals for the bins, knowing we were later going to try each orientation of each multi-codon bin, they may be reversed when the bins lists are collapsed to genomes and those genomes canonicalized, with codon "0" in genome slot zero, and reversed as needed to put the lower valued of codon "0"'s neighbors, here "4" for the son genome and "5" for the daughter genome, in slot one.

[(8,11) (10),(9,1,3,2),(6),(4),   (0),(7),      (5)] son bins
[(0),   (5), (10),     (4),(8,11),(7),(2,3,1,9),(6)] daughter bins
[0,5,10,4,11,8,7,9,1,3,2,6] original son genome
[0,4,6,9,1,3,2,10,8,11,5,7] original daughter genome

That completes the Edge Preserving Cyclic Crossover per se, and it was one of the "complete waste of time" cases. However, since we still have to cater to the need for checking which orientations of the multi-codon bins should be in reversed order for better fitness, we have another arrow in our quiver, and in the original test case, things continued like this.

Flip first (isolated) multicodon bin list, flipped case has better fitness, freeze that choice.

[(8,11) (10),(9,1,3,2),(6),(4),   (0),(7),      (5)] son bins
[(11,8) (10),(9,1,3,2),(6),(4),   (0),(7),      (5)] son bins  <= better fitness

Flip second (isolated) multi-codon bin list, unflipped case has better fitness, freeze that choice, all son bins needing flip testing have been flipped, choosing best son genome.

[(11,8) (10),(2,3,1,9),(6),(4),   (0),(7),      (5)] son bins  <= better fitness
[(11,8) (10),(9,1,3,2),(6),(4),   (0),(7),      (5)] son bins

Flip first (isolated) multicodon bin list, unflipped case has better fitness, freeze that choice.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] daughter bins <= better fitness
[(0),   (5), (10),     (4),(8,11),(7),(9,1,3,2),(6)] daughter bins

Flip second (isolated) multi-codon bin list, unflipped case has better fitness, freeze that choice, all daughter bins needing flip testing have been flipped, choosing best daughter genome.

[(0),   (5), (10),     (4),(11,8),(7),(9,1,3,2),(6)] daughter bins <= better fitness
[(0),   (5), (10),     (4),(11,8),(7),(2,3,1,9),(6)] daughter bins

That might seem a little grim for our chances of improving anything, but actually, in our mechanations, with one genome reversed, completely swapping genomes actually reversed two bins as well, and so when everything is put back in canonical form, three out of four possible bins have flipped, and the daughter genome turned out to have fitness intermediate between her parents, while the son genome turned out to be more fit (where for this application lower values are more fit values) than either parent.

So, for this case, the son genome is selected, and Edge Preserving Cyclic Crossover is a win.

[0,4,6,9,1,3,2,10,8,11,5,7] / 1753.6090491614661 original mother / her fitness
[0,4,6,9,1,3,2,10,11,8,5,7] / 1751.049918380168  canonical son after build from bins / his fitness
[0,5,10,4,8,11,7,2,3,1,9,6] / 1813.3762562612453 canonical daughter after build from bins / her fitness
[0,5,10,4,11,8,7,9,1,3,2,6] / 1995.9306102964679 original father / his fitness