# Carries out mutation operations on FreeNetworks # FreeNetGa.tz (c) 2010 Jacob Schrum. # See Simulation.tz for more copyright information @include "Abstract.tz" @include "FreeNetwork.tz" #Means that input nodes can't have inward connections @define PURE_INPUTS 1. @define NEW_CONNECTION_CHANCES 15. @define SPLICE_CHANCES 15. @define MERGE_CHANCES 15. # Max Values @define MAX_PERTURBATION 5.0. Abstract : FreeNetGA { + variables: id-counter (int). innovation-counter (int). output-width (int). adaptive-mutation-rates (int). multi-modal-random-source (int). multi-modal-input-source (int). inverse-usage-proportionate-mode-deletion (int). mode-age-before-new-add (int). mode-age-before-delete (int). + to new-starting-brain: inputs (int). outputs (int). brain (object). tempList (list). i (int). inputs = (controller get-num-network-inputs). outputs = (controller get-num-network-outputs). if ((controller get-commandline) bit-param named "separate-networks-per-task") : { brain = new MultiNetwork. tempList = {}. for i = 0, i < (controller num-tasks), i++ : { push (self new-free-network in inputs out outputs) onto tempList. } brain set-networks to tempList. brain set-mode to 0. #brain print-network. #print "--------------------------". } else { brain = (self new-free-network in inputs out outputs). } brain set-ids unique (self get-id-and-advance) parents {}. return brain. + to new-free-network in inputs (int) out outputs (int): brain (object). brain = new FreeNetwork. if ((controller get-commandline) bit-param named "fs-net") : brain initialize-feature-selective-structure in inputs out outputs. else brain random-initialize with-inputs inputs and-outputs outputs. return brain. # Trivial archiving, since state is simple + to archive: return 1. + to dearchive: return 1. + to get-mutation-rate type name (string) network net (object): if adaptive-mutation-rates : return (net get-mutation-rate type name). else return ((controller get-commandline) num-param named name). # The same distance measure used for speciation in NEAT # + to measure-genetic-difference between net1 (object) and-net net2 (object) : # bigE (int). # bigD (int). # aveW (double). # total-links (int). # i (int). # j (int). # p (int). # q (int). # tempList1 (list). # tempList2 (list). # subTempList1 (list). # subTempList2 (list). # max1 (int). # max2 (int). # totalDiff (double). # numCommon (int). # tempInt (int). # # # Calculate max number of genes # # total-links = max( (net1 get-number-of-links), (net2 get-number-of-links) ). # # # Count excess and disjoint genes # # bigE = 0. # bigD = 0. # # numCommon = 0. # totalDiff = 0.0. # # tempList1 = (net1 get-sorted-innovation-nums). # tempList2 = (net2 get-sorted-innovation-nums). # # max1 = tempList1{ (|tempList1| - 1) }. # max2 = tempList2{ (|tempList2| - 1) }. # # j = 0. # for i = 0, i < |tempList1|, i++ : # { # while (j < |tempList2| && tempList2{j} < tempList1{i}) : # { # tempInt = ((net2 get-node-with-innovation-number node ( tempList2{j} )) get-number-of-output-targets). # bigD += tempInt. # j++. # } # # if (j < |tempList2| && tempList2{j} == tempList1{i}) : # { # subTempList1 = (net1 get-sorted-innovation-nums-of-targets node-with ( tempList1{i} )). # subTempList2 = (net2 get-sorted-innovation-nums-of-targets node-with ( tempList2{j} )). # # q = 0. # for p = 0, p < |subTempList1|, p++ : # { # while (q < |subTempList2| && subTempList2{q} < subTempList1{p}) : # { # bigD++. # q++. # } # # if (q < |subTempList2| && subTempList2{q} == subTempList1{p}) : # { # numCommon++. # totalDiff += |( (net1 weight-of-connection-between-innovation-numbers of ( tempList1{i} ) and ( subTempList1{p} )) - (net2 weight-of-connection-between-innovation-numbers of ( tempList2{j} ) and ( subTempList2{q} )) )|. # # q++. # } # else if (q < |subTempList2| && subTempList2{q} > subTempList1{p}) : # { # bigD++. # } # else if (q == |subTempList2|) : # { # if (subTempList1{p} <= max2) : # { # bigD++. # } # else # { # bigE++. # } # } # } # # while (q < |subTempList2|) : # { # if (subTempList2{q} <= max1) : # { # bigD++. # } # else # { # bigE++. # } # q++. # } # # j++. # } # else if (j < |tempList2| && tempList2{j} > tempList1{i}) : # { # tempInt = ((net1 get-node-with-innovation-number node ( tempList1{i} )) get-number-of-output-targets). # bigD += tempInt. # } # else if (j == |tempList2|) : # { # tempInt = ((net1 get-node-with-innovation-number node ( tempList1{i} )) get-number-of-output-targets). # bigE += tempInt. # } # } # # while (j < |tempList2|) : # { # tempInt = ((net2 get-node-with-innovation-number node ( tempList2{j} )) get-number-of-output-targets). # bigE += tempInt. # j++. # } # # if (numCommon > 0) : aveW = totalDiff / numCommon. # else aveW = 0. # # return (((bigE * 1.0) / total-links), ((bigD * 1.0) / total-links), aveW). + to get-innovation-counter: return innovation-counter. + to get-id-counter: return id-counter. + to get-id-and-advance: temp (int). temp = id-counter. id-counter++. return temp. # num: new starting innovation number # - should only be called at start of simulation + to set-innovation-counter to num (int): innovation-counter = num. + to set-id-counter to id (int): id-counter = id. + to set-output-width to num (int): output-width = num. + to init: if ((controller get-commandline) bit-param named "action-selector-net") : output-width = OUTPUTS_ACTION_SELECTOR. else output-width = OUTPUTS_STANDARD. adaptive-mutation-rates = ((controller get-commandline) bit-param named "adaptive-mutation-rates"). multi-modal-random-source = ((controller get-commandline) bit-param named "new-modes-random-source"). multi-modal-input-source = ((controller get-commandline) bit-param named "connect-inputs-on-mode-mutation"). inverse-usage-proportionate-mode-deletion = ((controller get-commandline) bit-param named "inverse-usage-proportionate-mode-deletion"). mode-age-before-new-add = ((controller get-commandline) num-param named "mode-age-before-new-add"). mode-age-before-delete = ((controller get-commandline) num-param named "mode-age-before-delete"). + to set-multi-modal-random-source to val (int): multi-modal-random-source = val. # NOTES: Only use in conjunction with RAND ModeMutation! # Otherwise, new modes might be disconnected when modes to # the left are deleted. However, if the original inputs were # random internal nodes, then all modes will remain connected # despite deletion. + to delete-output-mode of net (object) mode x (int): outputs (int). first-output-pos (int). first-output-of-mode (int). i (int). innovation-numbers (list). if (!multi-modal-random-source && !multi-modal-input-source) : { print "The delete mode method should never be called when the default mode mutation is being used.". print "Make the source inputs come from the hidden and/or input layers only". controller end-simulation. return. } else if !( mode-age-before-delete > (net get-mode-age of x) ) : { #print "---------------------------------------". #print "Delete mode $x". #net print-network. outputs = (net get-number-outputs). # Only procede if more than one mode exists if (outputs > (output-width + 1)) : { first-output-pos = ((net get-number-of-nodes) - (net get-number-outputs)). first-output-of-mode = first-output-pos + (x * (output-width + 1)). # Delete output nodes of mode, from right to left. # First get the innovation numbers, since positions may change # during removals. innovation-numbers = {}. for i = output-width, i >= 0, i-- : { push (net get-innovation-number at-position (first-output-of-mode + i)) onto innovation-numbers. } for i = 0, i < |innovation-numbers|, i++ : { #print "Remove node w/ inno:", (innovation-numbers{i}). net remove-output-node-by-innovation with-innovation (innovation-numbers{i}). } } net remove-mode-age mode x. } + to delete-random-output-mode of net (object): outputs (int). modes (int). mode-to-delete (int). outputs = (net get-number-outputs). # If more than one mode if (outputs > (output-width + 1)) : { modes = outputs / (output-width + 1). mode-to-delete = random[ (modes - 1) ]. self delete-output-mode of net mode mode-to-delete. } + to merge-output-modes of net (object): tempInt (int). outputs (int). output-modes (int). mode1 (int). mode2 (int). first-output-pos (int). first-output-of-mode1 (int). first-output-of-mode2 (int). i (int). outputs = (net get-number-outputs). # Only procede if more than one mode exists if (outputs > (output-width + 1)) : { output-modes = outputs / (output-width + 1). mode1 = random[ (output-modes - 1) ]. mode2 = random[ (output-modes - 2) ]. if (mode2 >= mode1) : mode2++. else { # Not entirely necessary, but helps tempInt = mode1. mode1 = mode2. mode2 = tempInt. } first-output-pos = ((net get-number-of-nodes) - (net get-number-outputs)). first-output-of-mode1 = first-output-pos + (mode1 * (output-width + 1)). first-output-of-mode2 = first-output-pos + (mode2 * (output-width + 1)). # Merge each output node of mode 2 with a node of mode 1, from right to left for i = output-width, i >= 0, i-- : { net output-node-merge at-position (first-output-of-mode1 + i) with-position (first-output-of-mode2 + i). } } net remove-mode-age mode mode2. outputs = (net get-number-outputs). # Networks with only one mode should not have a preference node if (outputs == (output-width + 1)) : { net remove-output-node at-position (first-output-pos + output-width). } + to demote-output-mode of net (object): outputs (int). output-modes (int). mode-to-remove (int). mode-to-link (int). first-output-pos (int). first-output-of-mode (int). first-output-of-link (int). i (int). weight (double). outputs = (net get-number-outputs). # Only procede if more than one mode exists if (outputs > (output-width + 1)) : { output-modes = outputs / (output-width + 1). mode-to-remove = random[ (output-modes - 1) ]. mode-to-link = random[ (output-modes - 2) ]. if (mode-to-link >= mode-to-remove) : mode-to-link++. first-output-pos = ((net get-number-of-nodes) - (net get-number-outputs)). first-output-of-mode = first-output-pos + (mode-to-remove * (output-width + 1)). first-output-of-link = first-output-pos + (mode-to-link * (output-width + 1)). # Demote each output node of one output mode for i = 0, i < (output-width + 1), i++ : { weight = (net random-initial-weight). net demote-output-node at-pos (first-output-of-mode + i) link-to (first-output-of-link + i) with weight. if (first-output-of-link < first-output-of-mode) : first-output-of-link++. } net remove-mode-age mode mode-to-remove. } + to add-output-mode onto net (object) comes-from random-pos = 0 (int) inputs-only from-inputs = 0 (int): i (int). pos (int). nodes (int). if !( mode-age-before-new-add > (net get-youngest-mode-age) ) : { # The one and only mode doesn't yet have a preference indicator if ((net get-number-outputs) == output-width) : { nodes = (((net get-number-of-nodes) - (net get-number-outputs)) - 1). pos = random[ nodes ]. net add-output-node with-innovation-number innovation-counter from-position pos with-weight (net random-initial-weight). innovation-counter++. } if ((controller get-commandline) bit-param named "freeze-all-on-mode-add") : { self freeze-all genome net. } pos = ((net get-number-of-nodes) - (output-width + 1)). # Enough outputs to fill the mode and have a preference indicator for it for i = 0, i < (output-width + 1), i++ : { if random-pos : { nodes = (((net get-number-of-nodes) - (net get-number-outputs)) - 1). pos = random[ nodes ]. net add-output-node with-innovation-number innovation-counter from-position pos with-weight (self random-weight). } else if from-inputs : { nodes = ((net get-number-inputs) - 1). pos = random[ nodes ]. net add-output-node with-innovation-number innovation-counter from-position pos with-weight (self random-weight). } else { net add-output-node with-innovation-number innovation-counter from-position (pos + i) with-weight 1. } innovation-counter++. } net add-mode-to-ages. } #net print-network. # net: a FreeNetwork instance to be mutated + to freeze-node of net (object): pos (int). nodes (int). nodes = ((net get-number-of-nodes) - 1). pos = random[ nodes ]. net freeze node pos. # net: a FreeNetwork instance to be mutated + to switch-activation of net (object): pos (int). nodes (int). # Don't switch the type of the output nodes nodes = (((net get-number-of-nodes) - (net get-number-outputs)) - 1). pos = random[ nodes ]. net switch-activation-function of pos. # net: a FreeNetwork instance to be mutated + to perturb genome net (object): pos1 (int). pos2 (int). targets (int). origins (int). delta (double). targets = 0. origins = ((net get-number-of-nodes) - 1). while targets == 0 : { pos1 = random[ origins ]. targets = (net get-number-of-targets-of-node at-position pos1). } targets--. pos2 = random[ targets ]. delta = random[2 * MAX_PERTURBATION] - MAX_PERTURBATION. net perturb-weight-between-nodes at-position pos1 and-position pos2 by delta. # net: a FreeNetwork instance to be mutated + to splice genome net (object): pos1 (int). pos2 (int). targets (int). origins (int). chances (int). #print "splice genome net". chances = 0. targets = 0. # No nodes can come after an output node origins = (((net get-number-of-nodes) - (net get-number-outputs)) - 1). while ((targets == 0) && (chances < SPLICE_CHANCES)) : { chances++. pos1 = random[ origins ]. targets = (net get-number-of-non-frozen-targets-of-node at-position pos1). #print "splice genome net: loop $chances $targets". } # Non-frozen target found if (targets > 0) : { targets--. pos2 = random[ targets ]. net splice-node-between-nodes at-position pos1 and-position pos2 with-innovation-number innovation-counter. innovation-counter++. } + to add-cascade-node genome net (object): net add-cascade-node with-innovation-number innovation-counter. innovation-counter++. + to random-weight: return (random[2 * INITIAL_MAX_WEIGHT] - INITIAL_MAX_WEIGHT). # net: a FreeNetwork instance to be mutated + to new-connection genome net (object): targets (int). targets = net get-number-of-nodes. self add-connection genome net start-targets targets. # Mutation specifically to connect a network input. # Keeps the rate at which inputs are connected constant, # even though number of nodes is increasing. Only makes # sense when starting with Feature Selective nets. + to new-input-connection genome net (object): targets (int). targets = net get-number-inputs. self add-connection genome net start-targets targets. + to add-connection genome net (object) start-targets targets (int): pos1 (int). pos2 (int). weight (double). chance (int). #print "add-connection genome net start-targets targets". chance = 0. targets--. pos1 = random[ targets ]. pos2 = (self new-connection-target-pos given targets and (net get-number-inputs)). pos2 = (net adjust-to-non-frozen index pos2). if (pos2 > -1) : { # Then non-frozen targets are available weight = (self random-weight). while (chance < NEW_CONNECTION_CHANCES) && (( ((net get-number-of-targets-of-node at-position pos2) == 0) && (pos2 < (net get-number-of-nodes) - (net get-number-outputs)) ) || ((net weight-of-connection-between-positions of pos1 and pos2) != NO_CONNECTION) ) : { #print "add-connection genome net start-targets targets: loop $chance". chance++. pos2 = (self new-connection-target-pos given targets and (net get-number-inputs)). pos2 = (net adjust-to-non-frozen index pos2). } if ((net weight-of-connection-between-positions of pos1 and pos2) == NO_CONNECTION) && !(net node-is-frozen node pos2) : { net add-connection-between-nodes at-position pos1 and pos2 with-synaptic-weight weight. } } + to freeze-all genome net (object): i (int). for i = 0, i < (net get-number-of-nodes), i++ : { net freeze node i. } #net print-network. + to new-connection-target-pos given targets (int) and inputs (int): if PURE_INPUTS : { return ( inputs + (random[ (targets - inputs) ]) ). } else { return ( random[ targets ] ). } # net: a FreeNetwork instance to be mutated + to merge-nodes genome net (object): pos1 (int). pos2 (int). targets1 (int). targets2 (int). chance (int). i (int). tempNode (object). chance = 0. targets1 = (net get-number-of-nodes) - (net get-number-inputs). # If nothing but outputs are left, then no merge is possible if (targets1 == (net get-number-outputs)) : { return. } targets1--. targets2 = 0. while (targets2 == 0) && (chance < MERGE_CHANCES) : { # choose non-input node pos1 = random[ targets1 ] + (net get-number-inputs). targets2 = (net get-number-of-targets-of-node at-position pos1). # Output nodes, input nodes and self-recurrent links are not valid targets for i = 0, i < (net get-number-of-targets-of-node at-position pos1), i++ : { tempNode = (net get-target-node of-parent pos1 target-num i). if ((tempNode get-type) != NODE_HIDDEN) || ((tempNode get-innovation-number) == (net get-innovation-number at-position pos1)) : { targets2--. } } chance++. } # Found a node to merge with if targets2 > 0 : { targets2--. pos2 = random[ targets2 ]. # adjust pos2 to skip input and output nodes, and self-recurrent links targets2 = (net get-number-of-targets-of-node at-position pos1). for i = 0, i < targets2, i++ : { tempNode = (net get-target-node of-parent pos1 target-num i). if ((tempNode get-type) == NODE_HIDDEN) && ((tempNode get-innovation-number) != (net get-innovation-number at-position pos1)) : { # i was the index of the link chosen if pos2 == 0: { pos2 = i. # break i = targets2. } else pos2--. } } net merge-node at-position pos1 with-target pos2. } + to get-binary-tournament-champion from population (list) with-scores fitnesses (list) using selection (object): choice1 (int). choice2 (int). best (object). choice1 = random[ ( |population| - 1 ) ]. choice2 = random[ ( |population| - 1 ) ]. best = ( selection get-best between (population{choice1}) and (population{choice2}) given (fitnesses{choice1}) and (fitnesses{choice2}) ). return best. # Can't be used with the ZScores method + to get-mutated-binary-tournament-champion from population (list) with-scores fitnesses (list) using selection (object): return (self get-mutated-clone of (self get-binary-tournament-champion from population with-scores fitnesses using selection)). # Delete up to one network mode, based on disuse + to delete-mode-according-to-disuse network net (object) usage mode-usage (list): i (int). # Check each mode starting from oldest, so that older unused # modes are more likely to be removed for i = 0, i < |mode-usage|, i++ : { # Liklihood to remove is the opposite of usage, namely the disuse # SMEGSMEG: This rate is too harsh. Even with 100% mode addition, modes are deleted too fast to be used. if random[1.0] < (1.0 - mode-usage{i}) : { #print "Mode $i chosen to be deleted for under use: usage =", mode-usage{i}. self delete-output-mode of net mode i. # Break loop so at max one mode is deleted i = |mode-usage|. } } + to delete-all-unused-modes network net (object) usage mode-usage (list): i (int). removed (int). removed = 0. # Go through in reverse order so deletions don't mess up target for i = (|mode-usage| - 1), i >= 0, i-- : { if (mode-usage{i} == 0) : { self delete-output-mode of net mode i. remove mode-usage{i}. removed++. } } return removed. # Delete the least-used mode, where left most mode wins ties + to delete-least-used-mode network net (object) usage mode-usage (list): i (int). least-used (int). lowest-usage (double). #print "mode-usage: $mode-usage". # Don't delete last mode if (|mode-usage| <= 1) : return. else { least-used = 0. lowest-usage = mode-usage{0}. # Check each mode starting from oldest, so that older unused # modes are more likely to be removed for i = 0, i < |mode-usage|, i++ : { if (mode-usage{i} < lowest-usage) : { lowest-usage = mode-usage{i}. least-used = i. } } #print "Least-used mode $least-used chosen to be deleted: usage =", mode-usage{least-used}. self delete-output-mode of net mode least-used. } # net: a neural network or multi network + to get-mutated-clone of net (object): newNet (object). mode-to-delete (int). mode-usage (list). i (int). mode-to-delete = (net get-and-reset-pending-mode-deletion). mode-usage = (net get-most-recent-mode-usage-percentages). newNet = net copy. # These options should not be combined with MultiNetworks, so assume FreeNetwork is used if inverse-usage-proportionate-mode-deletion : { self delete-mode-according-to-disuse network newNet usage mode-usage. } else if (mode-to-delete > -1) : { # SMEG: Should mutation be avoided in this case? self delete-output-mode of newNet mode mode-to-delete. } if (newNet is a "MultiNetwork") : { for i = 0, i < (newNet num-networks), i++ : { self mutate genome (newNet get-network index i) usage ({ (mode-usage{i}) }). } } else { self mutate genome newNet usage mode-usage. } newNet set-ids unique id-counter parents { (net get-id) }. id-counter++. return newNet. # net: a FreeNetwork instance to be mutated + to mutate genome net (object) usage mode-usage = 0 (list): total-modes (int). last-mode-uniqueness (double). i (int). # New net has new age net reset. if ((controller get-commandline) bit-param named "cascade-mutate") : { if random[1.0] < (self get-mutation-rate type "perturbation-rate" network net) : { self perturb genome net. } if random[1.0] < (self get-mutation-rate type "cascade-node-rate" network net) : { self add-cascade-node genome net. } if random[1.0] < (self get-mutation-rate type "merge-nodes-rate" network net) : { self merge-nodes genome net. } if random[1.0] < (self get-mutation-rate type "freeze-rate" network net) : { self freeze-node of net. } } else { if !((controller get-commandline) bit-param named "predefined-network-modes") : { if random[1.0] < (self get-mutation-rate type "demote-mode-rate" network net) : { self demote-output-mode of net. } if random[1.0] < (self get-mutation-rate type "delete-mode-rate" network net) : { # Delete all the unused modes first if ((controller get-commandline) bit-param named "delete-mode-del-all-unused") : { #print "Before: $mode-usage". self delete-all-unused-modes network net usage mode-usage. #print "After: $mode-usage". } if ((controller get-commandline) bit-param named "delete-mode-mut-removes-least-used") : { self delete-least-used-mode network net usage mode-usage. } else { self delete-random-output-mode of net. } } if random[1.0] < (self get-mutation-rate type "merge-output-modes-rate" network net) : { self merge-output-modes of net. } if random[1.0] < (self get-mutation-rate type "new-nn-mode-rate" network net) : { # Default values assure proper behavior if "restrict-similar-modes" is not being used total-modes = 1. last-mode-uniqueness = INT_INFINITY. if ((controller get-commandline) bit-param named "restrict-similar-modes") : { total-modes = ((controller get-mode-arbitrator) get-num-output-modes-from-net network net). if (total-modes > 1) : { # Most recently added mode must be distinct from all previous for i = 0, i < (total-modes - 1), i++: { last-mode-uniqueness = min( last-mode-uniqueness, ((controller get-behavior-state) compare-network-modes network net mode (total-modes - 1) and i) ). } } } # Only add new mode if previously added mode has sufficiently differentiated if (last-mode-uniqueness > total-modes) : { self add-output-mode onto net comes-from multi-modal-random-source inputs-only multi-modal-input-source. } } } if random[1.0] < (self get-mutation-rate type "perturbation-rate" network net) : { self perturb genome net. } if random[1.0] < (self get-mutation-rate type "new-connection-rate" network net) : { self new-connection genome net. } if random[1.0] < (self get-mutation-rate type "new-input-connection-rate" network net) : { self new-input-connection genome net. } if random[1.0] < (self get-mutation-rate type "splice-rate" network net) : { self splice genome net. } #if random[1.0] < (self get-mutation-rate type "switch-activation-rate" network net) : #{ # self switch-activation of net. #} if random[1.0] < (self get-mutation-rate type "merge-nodes-rate" network net) : { self merge-nodes genome net. } if random[1.0] < (self get-mutation-rate type "freeze-rate" network net) : { self freeze-node of net. } } + to burst-mutations population brains (list): i (int). j (int). k (int). bursts (int). bursts = ((controller get-commandline) num-param named "num-burst-mutations"). for i = 0, i < |brains|, i++ : { for j = 0, j < |brains{i}|, j++ : { for k = 0, k < bursts, k++ : self mutate genome (brains{i}{j}). } } + to new-offspring of population (list) binary-tournament bt (int) selection sel (object) scores fitnesses (list): choice1 (int). choice2 (int). parent1 (object). parent2 (object). newNet (object). upper-limit (int). if bt : { # Binary tournament parent1 = self get-binary-tournament-champion from population with-scores fitnesses using sel. parent2 = self get-binary-tournament-champion from population with-scores fitnesses using sel. } else { upper-limit = ( |population| - 1 ). choice1 = (random[(upper-limit)]). choice2 = choice1. while choice1 == choice2 : { choice2 = (random[(upper-limit)]). } parent1 = population{choice1}. parent2 = population{choice2}. } newNet = (self crossover this-genome parent1 with-this-genome parent2). # Modifies existing net self mutate genome newNet. return newNet. # net1: a FreeNetwork instance parent # net2: another FreeNetwork instance parent + to crossover this-genome net1 (object) with-this-genome net2 (object): newNet (object). i (int). j (int). tempNode (object). innovationListNet1 (list). innovationListNet2 (list). lastInputInnovation (int). innovation1 (int). innovation2 (int). weight (double). combinedOutputs (int). if random[1.0] < ((controller get-commandline) num-param named "crossover-rate"): { net1 update-absolute-positions. net2 update-absolute-positions. newNet = new FreeNetwork. newNet set-ids unique id-counter parents { (net1 get-id), (net2 get-id) }. id-counter++. innovationListNet1 = {}. innovationListNet2 = {}. # Add input nodes for i=0, i < (net1 get-number-inputs), i++ : { tempNode = new FreeNode. tempNode set-type to NODE_INPUT. innovation1 = ((net1 get-node-list){i} get-innovation-number). tempNode set-innovation-number to innovation1. push innovation1 onto innovationListNet1. push innovation1 onto innovationListNet2. newNet push-onto-node-list node tempNode. } newNet set-number-inputs to (net1 get-number-inputs). lastInputInnovation = innovation1. # Add hidden nodes for i = (net1 get-number-inputs), i < (net1 get-number-of-nodes) - (net1 get-number-outputs), i++ : { tempNode = new FreeNode. tempNode set-type to NODE_HIDDEN. innovation1 = (net1 get-node-list){i} get-innovation-number. tempNode set-innovation-number to innovation1. push innovation1 onto innovationListNet1. newNet push-onto-node-list node tempNode. } innovation1 = lastInputInnovation. for i = (net2 get-number-inputs), i < (net2 get-number-of-nodes) - (net2 get-number-outputs), i++ : { innovation2 = innovation1. innovation1 = ((net2 get-node-list){i} get-innovation-number). push innovation1 onto innovationListNet2. # see if innovation is already in list/net if (!((controller get-list-ops) contains element innovation1 in-list innovationListNet1)) : { tempNode = new FreeNode. tempNode set-type to NODE_HIDDEN. tempNode set-innovation-number to innovation1. newNet insert-into-node-list node tempNode after-innovation innovation2. } } combinedOutputs = 0. # Add output nodes for i = (net1 get-number-of-nodes) - (net1 get-number-outputs), i < (net1 get-number-of-nodes), i++ : { tempNode = new FreeNode. tempNode set-type to NODE_OUTPUT. innovation1 = ((net1 get-node-list){i} get-innovation-number). tempNode set-innovation-number to innovation1. push innovation1 onto innovationListNet1. newNet push-onto-node-list node tempNode. combinedOutputs++. } # Checking net2 separately is required if mode mutation has resulted in different numbers of output nodes for i = (net2 get-number-of-nodes) - (net2 get-number-outputs), i < (net2 get-number-of-nodes), i++ : { innovation2 = innovation1. innovation1 = ((net2 get-node-list){i} get-innovation-number). push innovation1 onto innovationListNet2. # see if innovation is already in list/net if (!((controller get-list-ops) contains element innovation1 in-list innovationListNet1)) : { tempNode = new FreeNode. tempNode set-type to NODE_OUTPUT. tempNode set-innovation-number to innovation1. newNet insert-into-node-list node tempNode after-innovation innovation2. combinedOutputs++. } } newNet set-number-outputs to combinedOutputs. # Now create the connections for i=0, i < (net1 get-number-of-nodes), i++ : { innovation1 = (net1 get-node-list){i} get-innovation-number. for j=0, j < ((net1 get-node-list){i} get-number-of-output-targets), j++ : { innovation2 = (((net1 get-node-list){i} get-output-targets){j} get-innovation-number). weight = (net2 weight-of-connection-between-innovation-numbers of innovation1 and innovation2). if (weight == NO_CONNECTION) || (random[1.0] < 0.5) : weight = ((net1 get-node-list){i} get-synapse-weights){j}. newNet add-connection-between-nodes-with-innovation-numbers of innovation1 and innovation2 with-synaptic-weight weight. } } for i=0, i < (net2 get-number-of-nodes), i++ : { innovation1 = (net2 get-node-list){i} get-innovation-number. for j=0, j < ((net2 get-node-list){i} get-number-of-output-targets), j++ : { innovation2 = (((net2 get-node-list){i} get-output-targets){j} get-innovation-number). weight = (newNet weight-of-connection-between-innovation-numbers of innovation1 and innovation2). if (weight == NO_CONNECTION) : { weight = ((net2 get-node-list){i} get-synapse-weights){j}. newNet add-connection-between-nodes-with-innovation-numbers of innovation1 and innovation2 with-synaptic-weight weight. } } } } else { newNet = net1 copy. newNet set-ids unique id-counter parents { (net1 get-id) }. id-counter++. } return newNet. }