private static void addClonedConnection(Connection connection, NeuralGraph child, Dictionary <Neuron, Neuron> addedNeurons, bool disabled) { Neuron[] oldNeurons = new Neuron[] { connection.getSource(), connection.getDest() }; Neuron[] newNeurons = new Neuron[oldNeurons.Length]; for (int i = 0; i < oldNeurons.Length; i++) { if (oldNeurons[i].getType() != NeuronType.Hidden) { // TODO !Important! If we allow recurrent connections on outputs, need to clone the outputs too newNeurons[i] = oldNeurons[i]; continue; } if (!addedNeurons.ContainsKey(oldNeurons[i])) { newNeurons[i] = oldNeurons[i].clone(); addedNeurons.Add(oldNeurons[i], newNeurons[i]); child.addHiddenNeuron(newNeurons[i]); } else { newNeurons[i] = addedNeurons[oldNeurons[i]]; } } Dictionary <Neuron, Dictionary <Neuron, Connection> > childConnections = child.getConnections(); if (childConnections.ContainsKey(newNeurons[1]) && childConnections[newNeurons[1]].ContainsKey(newNeurons[0])) { // If the link already exists, enable it if (!disabled) { childConnections[newNeurons[1]][newNeurons[0]].enable(); } } else { Connection newConnection = new Connection(newNeurons[0], newNeurons[1], connection.getParentInnovation(), connection.getInnovation(), true); newConnection.setDisabled(disabled); child.addConnection(newConnection); } }
public Individual mutate(NEATConfig config) { Individual newIndiv = clone(); NeuralGraph newGraph = newIndiv.getGraph(); if (Utils.rand.NextDouble() < config.newConnectionProb) { // Select two random neurons and create (or enable) a link in-between int hiddenCount = newGraph.getHiddenNeurons().Count(); int inputCount = newGraph.getInputNeurons().Count(); int outputCount = newGraph.getOutputNeurons().Count(); // Select the input and output neurons int sourceIndex = Utils.rand.Next(hiddenCount + inputCount + outputCount); int destIndex = Utils.rand.Next(hiddenCount + outputCount); Neuron source, dest; if (sourceIndex < hiddenCount) { source = newGraph.getHiddenNeurons()[sourceIndex]; } else if (sourceIndex < hiddenCount + inputCount) { source = newGraph.getInputNeurons()[sourceIndex - hiddenCount]; } else { source = newGraph.getOutputNeurons()[sourceIndex - hiddenCount - inputCount]; } if (destIndex < hiddenCount) { dest = newGraph.getHiddenNeurons()[destIndex]; } else { dest = newGraph.getOutputNeurons()[destIndex - hiddenCount]; } Connection connection; if (!newGraph.getConnections()[dest].TryGetValue(source, out connection)) { connection = new Connection(source, dest, -1); // TODO what the crap is the parent innovation supposed to be here newGraph.addConnection(connection); } else { connection.enable(); } } if (Utils.rand.NextDouble() < config.newNodeProb) { // When adding a node, disable a connection and replace it by a neuron and two connections // The new input connection to that neuron has weight of 1, while output has same weight as disabled connection Connection connection = newGraph.getConnectionList()[Utils.rand.Next(newGraph.getConnectionList().Count)]; connection.disable(); Neuron neuron = new Neuron(NeuronType.Hidden); Connection newConn1 = new Connection(connection.getSource(), neuron, connection.getInnovation()); Connection newConn2 = new Connection(neuron, connection.getDest(), connection.getInnovation()); newGraph.addHiddenNeuron(neuron); newGraph.addConnection(newConn1); newGraph.addConnection(newConn2); } // Mutate each connection weight foreach (Connection connection in newGraph.getConnectionList()) { if (Utils.rand.NextDouble() < config.mutationProb) { if (Utils.rand.NextDouble() < config.uniformPerturbationMutationProb) { connection.setWeight(connection.getWeight() + Utils.rand.NextDouble() * 2 * config.perturbationStep - config.perturbationStep); } else { connection.setWeight(Utils.nextGaussian(0, 1)); } } } return(newIndiv); }