Ejemplo n.º 1
0
        /// <summary>
        /// Adds a new connection between 2 randomly chosen nodes.
        /// The first node can not be an output.
        /// The second node cannot be an input node if the first node is an input node, and the layer cannot be the same as the first node.
        ///
        /// If the connection that is going to be created already exists, there will be a maximum of 5 attempts to create a new one.
        /// </summary>
        /// <param name="trainingRoomSettings">The training room settings.</param>
        /// <param name="innovationFunction">The innovation function.</param>
        protected void AddConnectionMutation(TrainingRoomSettings trainingRoomSettings, Func <uint, uint, uint> innovationFunction)
        {
            // Set initial attempt counter to 0.
            int attemptsDone = 0;

            // Rebuild structure so we get layer information to make sure we don't get circular dependencies.
            RebuildStructure();

            while (true)
            {
                // Prepare start and end node.
                Node startNode;
                Node endNode;

                // Set start node to a random node while start note is of type OutputNode.
                do
                {
                    startNode = TempNodes[trainingRoomSettings.Random.Next(TempNodes.Count)];
                } while (IsNodeAnOutputNode(startNode));


                // Set end node to a random node while the layer of the start and end node are the same or
                // start node is of type InputNode and end node is of type InputNode.
                do
                {
                    endNode = TempNodes[trainingRoomSettings.Random.Next(TempNodes.Count)];
                } while (endNode.Layer == startNode.Layer || (IsNodeAnInputNode(startNode) && IsNodeAnInputNode(endNode)));

                // If end node layer is higher than start node layer swap them.
                if (endNode.Layer > startNode.Layer)
                {
                    Node temp = endNode;
                    endNode   = startNode;
                    startNode = temp;
                }

                // If the connection between the selected start and end node already exists continue the loop, until 5 attempts are done,
                // then return early.
                if (ConnectionExists(startNode, endNode))
                {
                    if (attemptsDone >= 5)
                    {
                        return;
                    }
                    attemptsDone += 1;
                    continue;
                }
                // Create a new connection gene from the start and end nodes.
                ConnectionGene connection = CreateConnectionGene(innovationFunction(startNode.NodeIdentifier, endNode.NodeIdentifier), startNode.NodeIdentifier, endNode.NodeIdentifier, trainingRoomSettings.Random.NextDouble() * 2 - 1);

                // Add the connection gene.
                ConnectionGenes.Add(connection);

                // Update the max innovation.
                _maxInnovation = Math.Max(connection.InnovationNumber, _maxInnovation);

                // Break the loop.
                break;
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Adds a node by selecting a random connection, disabling it and replacing it by 2 genes.
        /// The first gene goes from the original input node, to the new node with a weight of 1.
        /// The second gene goes from the the new node to the old output node with the same weight as the original connection.
        /// </summary>
        /// <param name="trainingRoomSettings">The training room settings.</param>
        /// <param name="getAndIncreaseNodeIdFunction">The get and increase node id function.</param>
        /// <param name="innovationFunction">The innovation function.</param>
        private void AddNodeMutation(TrainingRoomSettings trainingRoomSettings, Func <uint> getAndIncreaseNodeIdFunction, Func <uint, uint, uint> innovationFunction)
        {
            // If the connection genes count is 0, then there are not connection genes to mutate.
            if (ConnectionGenes.Count == 0)
            {
                return;
            }

            // Find a random connection.
            ConnectionGene theChosenOne = ConnectionGenes.ElementAt(trainingRoomSettings.Random.Next(ConnectionGenes.Count));

            // Disable the connection gene.
            theChosenOne.Enabled = false;

            // Generate the new node's id.
            uint newNodeId = getAndIncreaseNodeIdFunction();

            // Get the old in and out node identifiers.
            uint oldInId  = theChosenOne.InNodeIdentifier;
            uint oldOutId = theChosenOne.OutNodeIdentifier;

            // Create a connectionGene from oldIn (a) to new (c) and from new (c) to oldOut (b)
            ConnectionGene aToC = new ConnectionGene(Id, innovationFunction(oldInId, newNodeId), oldInId, newNodeId, 1);
            ConnectionGene cToB = new ConnectionGene(Id, innovationFunction(newNodeId, oldOutId), newNodeId, oldOutId, theChosenOne.Weight);

            // Add new connection genes.
            ConnectionGenes.Add(aToC);
            ConnectionGenes.Add(cToB);

            // Update the max innovation.
            _maxInnovation = Math.Max(Math.Max(cToB.InnovationNumber, aToC.InnovationNumber), _maxInnovation);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Mutates the genome by creating a new connection.
        /// </summary>
        public void Mutate_Link()
        {
            //Get first node to connect. It is random.
            NodeGene nodeGene_a = NodeGenes.RandomValue(Random).Take(1).ElementAt(0);


            IEnumerable <NodeGene> temp_subset = NodeGenes.Values.Where(a => a.NodeGenePattern.X > nodeGene_a.NodeGenePattern.X);

            if (temp_subset.Count() == 0)
            {
                return; //TODO handle this too
            }


            NodeGene nodeGene_b = temp_subset.ElementAt(Random.Next(temp_subset.Count()));  //Get a random gene with a higher X value.


            ConnectionGene connectionGene = Pedigree.Create_ConnectionGene(nodeGene_a, nodeGene_b, Pedigree.Mutation_WeightRandom * (Random.NextDouble() * 2 - 1),
                                                                           true);

            if (ConnectionGenes.ContainsKey(connectionGene.ConnectionGenePattern.InnovationNumber)) //Can only happen if it already existed in the tracker.
            {
                return;                                                                             //TODO think of how to handle this, maybe have a retry somewhere?
            }


            ConnectionGenes.Add(connectionGene.ConnectionGenePattern.InnovationNumber, connectionGene);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Adds a node by selecting a random connection, disabling it and replacing it by 2 genes.
        /// The first gene goes from the original input node, to the new node with a weight of 1.
        /// The second gene goes from the the new node to the old output node with the same weight as the original connection.
        /// </summary>
        private void AddNodeMutation()
        {
            if (ConnectionGenes.Count == 0)
            {
                return;
            }

            // Choose a random connection to add a node into
            ConnectionGene theChosenOne = ConnectionGenes.ElementAt(TrainingRoom.Random.Next(ConnectionGenes.Count));

            // Disable it
            theChosenOne.Enabled = false;

            // Generate the new node's id
            uint newNodeId = TrainingRoom.GetAndIncreaseNodeId();

            uint oldInId  = theChosenOne.InId;
            uint oldOutId = theChosenOne.OutId;

            // Create a connectionGene from oldIn (a) to new (c) and from new (c) to oldOut (b)
            ConnectionGene aToC = new ConnectionGene(Id, oldInId, newNodeId, 1, TrainingRoom.GetInnovationNumber(oldInId, newNodeId));
            ConnectionGene cToB = new ConnectionGene(Id, newNodeId, oldOutId, theChosenOne.Weight, TrainingRoom.GetInnovationNumber(newNodeId, oldOutId));

            ConnectionGenes.Add(aToC);
            ConnectionGenes.Add(cToB);
            _maxInnovation = Math.Max(Math.Max(cToB.InnovationNumber, aToC.InnovationNumber), _maxInnovation);
        }
Ejemplo n.º 5
0
        public override void CreateBrain(BrainComponent brainComp, IMutatorConfig config)
        {
            var neatConfig = (NeatMutationConfig)config;

            // Add nodes for inputs and outputs
            foreach ((string namedInput, string _) in brainComp.InputMap)
            {
                // Note activation function on input nodes is not used.
                this.AddNamedNode(namedInput, NodeType.INPUT, ActivationFunctionType.SOFTSIGN);
            }
            foreach ((string namedOutput, string _) in brainComp.OutputMap)
            {
                this.AddNamedNode(namedOutput, NodeType.OUTPUT, (ActivationFunctionType)neatConfig.OutputActivationFunction);
            }

            // Increase connection innovation id as we loop
            int    connInnovationId = 0;
            Random r = new Random();

            double chanceToMakeConnection = 1d;

            foreach (var input in NodeGenes.FindAll((g) => g.NodeType == NodeType.INPUT || g.NodeType == NodeType.BIAS))
            {
                foreach (var output in NodeGenes.FindAll((g) => g.NodeType == NodeType.OUTPUT))
                {
                    if (r.NextDouble() < chanceToMakeConnection)
                    {
                        // Add new connection gene
                        ConnectionGenes.Add(new ConnectionGene(connInnovationId, input.InnovationId, output.InnovationId, r.Normal(0, 1.5)));
                        connInnovationId++;
                    }
                }
            }
        }
Ejemplo n.º 6
0
 /// <summary>
 /// Initializes an instance of the <see cref="Brain"/> class with a set amount of input and output nodes and provided genes.
 /// </summary>
 /// <param name="id">The id for the brain.</param>
 /// <param name="trainingRoom">The training room this brain is a part of.</param>
 /// <param name="genes">The genes to create the brain out of.</param>
 public Brain(Guid id, TrainingRoom trainingRoom, IEnumerable <ConnectionGene> genes) : this(trainingRoom)
 {
     Id = id;
     foreach (ConnectionGene gene in genes)
     {
         ConnectionGenes.Add(gene.Clone());
         _maxInnovation = Math.Max(gene.InnovationNumber, _maxInnovation);
     }
 }
Ejemplo n.º 7
0
        /// <summary>
        /// Initializes an instance of the <see cref="Organism"/> class.
        /// </summary>
        /// <param name="trainingRoomSettings">The training room settings.</param>
        /// <param name="innovationFunction">The innovation function.</param>
        /// <param name="generation">The generation.</param>
        public Organism(TrainingRoomSettings trainingRoomSettings, Func <uint, uint, uint> innovationFunction, uint generation) : this(generation, trainingRoomSettings)
        {
            foreach (OrganismInputNode organismInputNode in Inputs)
            {
                InputNode inputNode = organismInputNode.InputNode;

                foreach (OrganismOutputNode organismOutputNode in Outputs)
                {
                    OutputNode outputNode = organismOutputNode.OutputNode;

                    // Create a new connection gene from the start and end nodes.
                    ConnectionGene connection = CreateConnectionGene(innovationFunction(inputNode.NodeIdentifier, outputNode.NodeIdentifier), inputNode.NodeIdentifier, outputNode.NodeIdentifier, trainingRoomSettings.Random.NextDouble() * 2 - 1);

                    // Add the connection gene.
                    ConnectionGenes.Add(connection);
                }
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Adds a new connection between 2 randomly chosen nodes.
        /// The first node can not be an output.
        /// The second node cannot be an input node if the first node is an input node, and the layer cannot be the same as the first node.
        ///
        /// If the connection that is going to be created already exists, there will be a maximum of 5 attempts to create a new one.
        /// </summary>
        private void AddConnectionMutation()
        {
            int attemptsDone = 0;

            while (true)
            {
                // NOTE: Rebuild structure so we get layer information to make sure we don't get circle dependencies
                RebuildStructure();

                Node startNode;
                Node endNode;
                do
                {
                    startNode = _nodes.ElementAt(TrainingRoom.Random.Next(_nodes.Count)).Value;
                } while (startNode.NodeType == NodeType.Output);

                do
                {
                    endNode = _nodes.ElementAt(TrainingRoom.Random.Next(_nodes.Count)).Value;
                } while (endNode.Layer == startNode.Layer || (startNode.NodeType == NodeType.Input && endNode.NodeType == NodeType.Input));

                if (endNode.Layer > startNode.Layer)
                {
                    Node temp = endNode;
                    endNode   = startNode;
                    startNode = temp;
                }

                if (ConnectionExists(startNode, endNode))
                {
                    if (attemptsDone >= 5)
                    {
                        return;
                    }
                    attemptsDone += 1;
                    continue;
                }

                ConnectionGene connection = new ConnectionGene(Id, startNode.Id, endNode.Id, TrainingRoom.Random.NextDouble() * 2 - 1, TrainingRoom.GetInnovationNumber(startNode.Id, endNode.Id));
                ConnectionGenes.Add(connection);
                _maxInnovation = Math.Max(connection.InnovationNumber, _maxInnovation);
                break;
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Mutates a random connection splitting it with a node.
        /// </summary>
        public void Mutate_Node()
        {
            if (NodeGenes.Count >= Pedigree.MaxNodes)
            {
                return; //Do nothing if we have max nodes.
            }
            else if (ConnectionGenes.Count == 0)
            {
                return; //Literally cannot make a node.
            }


            ConnectionGene connectionGene = ConnectionGenes.RandomValue(Random).Take(1).ElementAt(0);

            if (connectionGene == null)
            {
                return;
            }


            NodeGene from = NodeGenes[connectionGene.ConnectionGenePattern.From.InnovationNumber];
            NodeGene to   = NodeGenes[connectionGene.ConnectionGenePattern.To.InnovationNumber];

            NodeGene created = Pedigree.Create_NodeGene(connectionGene);

            if (NodeGenes.ContainsKey(created.NodeGenePattern.InnovationNumber))
            {
                return; //TODO maybe retry here as well?
            }

            NodeGenes.Add(created.NodeGenePattern.InnovationNumber, created);


            ConnectionGene created_connectionGene_1 = Pedigree.Create_ConnectionGene(from, created, 1, true); //Default weight of 1.
            ConnectionGene created_connectionGene_2 = Pedigree.Create_ConnectionGene(created, to, connectionGene.Weight, connectionGene.Enabled);

            ConnectionGenes.Remove(connectionGene.ConnectionGenePattern.InnovationNumber);

            ConnectionGenes.Add(created_connectionGene_1.ConnectionGenePattern.InnovationNumber, created_connectionGene_1);
            ConnectionGenes.Add(created_connectionGene_2.ConnectionGenePattern.InnovationNumber, created_connectionGene_2);
        }
Ejemplo n.º 10
0
 public void AddConnectionGene(ConnectionGene toAdd)
 {
     ConnectionGenes.Add(IdGenerator.NextConnectionId(), toAdd);
 }
Ejemplo n.º 11
0
 public void AddConnectionGene(ConnectionGene toAdd, int index)
 {
     ConnectionGenes.Add(index, toAdd);
 }
Ejemplo n.º 12
0
        /// <summary>
        /// Adds a new connection between 2 randomly chosen nodes.
        /// The first node can not be an output.
        /// The second node cannot be an input node if the first node is an input node, and the layer cannot be the same as the first node.
        ///
        /// If the connection that is going to be created already exists, there will be a maximum of 5 attempts to create a new one.
        /// </summary>
        /// <param name="trainingRoomSettings">The training room settings.</param>
        /// <param name="innovationFunction">The innovation function.</param>
        private void AddConnectionMutation(TrainingRoomSettings trainingRoomSettings, Func <uint, uint, uint> innovationFunction)
        {
            // Set initial attempt counter to 0.
            int attemptsDone = 0;

            // Rebuild structure so we get layer information to make sure we don't get circular dependencies.
            RebuildStructure();

            while (true)
            {
                // Prepare start and end node.
                Node startNode;
                Node endNode;

                // Set start node to a random node while start note is of type OutputNode.
                do
                {
                    startNode = GetRandomNode();
                } while (startNode is OutputNode);


                // Set end node to a random node while the layer of the start and end node are the same or
                // start node is of type InputNode and end node is of type InputNode.
                do
                {
                    endNode = GetRandomNode();
                } while (endNode.Layer == startNode.Layer || (startNode is InputNode && endNode is InputNode));

                // If end node layer is higher than start node layer swap them.
                if (endNode.Layer > startNode.Layer)
                {
                    Node temp = endNode;
                    endNode   = startNode;
                    startNode = temp;
                }

                // If the connection between the selected start and end node already exists continue the loop, until 5 attempts are done,
                // then return early.
                if (ConnectionExists(startNode, endNode))
                {
                    if (attemptsDone >= 5)
                    {
                        return;
                    }
                    attemptsDone += 1;
                    continue;
                }

                // Create a new connection gene from the start and end nodes.
                ConnectionGene connection = new ConnectionGene(Id, innovationFunction(startNode.NodeIdentifier, endNode.NodeIdentifier), startNode.NodeIdentifier, endNode.NodeIdentifier, trainingRoomSettings.Random.NextDouble() * 2 - 1);

                // Add the connection gene.
                ConnectionGenes.Add(connection);

                // Update the max innovation.
                _maxInnovation = Math.Max(connection.InnovationNumber, _maxInnovation);

                // Break the loop.
                break;
            }

            Node GetRandomNode()
            {
                //TODO: Why not use the hidden nodes list instead of the connection genes?

                List <Node> nodes = ConnectionGenes.SelectMany(p => new[] { p.InNode, p.OutNode }).Distinct().ToList();

                nodes.AddRange(Inputs.Select(p => p.InputNode));
                nodes.AddRange(Outputs.Select(p => p.OutputNode));

                return(nodes[trainingRoomSettings.Random.Next(nodes.Count)]);
            }
        }