/// <summary> /// Mutates the weights of a neural net. For each connection in the genotype there is a random chance of different mutations occuring according to the properties defined on this class. /// </summary> /// <param name="genotype">Genotype to mutate.</param> /// <param name="random">Random for number generation.</param> /// <param name="innovationIdManager"></param> public override void Mutate(NeatBrainGenotype genotype, Random random, InnovationIdManager innovationIdManager) { foreach (ConnectionGene gene in genotype.ConnectionGenes) { if (random.NextDouble() < this.ProbPerturbWeight) { // Perturb the weights gene.Weight += (float)random.Normal(0.0d, NewWeightStd) * WeightPerturbScale; } if (random.NextDouble() < this.ProbResetWeight) { // Reset the weight to a new random value gene.Weight = (float)random.Normal(0.0d, NewWeightStd) * NewWeightPower; } if (random.NextDouble() < this.ProbEnableConn) { if (!genotype.CreatesCycle(gene)) { gene.Enabled = true; } } if (random.NextDouble() < this.ProbDisableConn) { gene.Enabled = false; } } }
public void TestAdder() { NeatBrainGenotype test = new NeatBrainGenotype(); test.AddNamedNode("input1", NodeType.INPUT, ActivationFunctionType.RELU); test.AddNamedNode("hidden1", NodeType.HIDDEN, ActivationFunctionType.RELU); test.AddNamedNode("output1", NodeType.OUTPUT, ActivationFunctionType.RELU); test.ConnectionGenes.Add(new ConnectionGene(1, 0, 1, 1)); // Input to hidden test.ConnectionGenes.Add(new ConnectionGene(2, 1, 1, 1, true, true)); // Hidden to hidden (recurrent) test.ConnectionGenes.Add(new ConnectionGene(3, 1, 2, 1)); //# Hidden to output var network = new NeatBrainPhenotype(test); network.SetInput("input1", 1.0f); Assert.AreEqual(1.0f, network.Get("input1")); network.Calculate(); Assert.AreEqual(0, network.Get("output1")); network.SetInput("input1", 2.0f); network.Calculate(); Assert.AreEqual(1, network.Get("output1")); network.SetInput("input1", 10.0f); network.Calculate(); Assert.AreEqual(3, network.Get("output1")); Assert.AreEqual(13, network.Get("hidden1")); Assert.AreEqual(10, network.Get("input1")); }
public void TestCreatesCycleIgnoresRecurrentConnections() { // Test that a gene already in the genome can be tested fro recurrence NeatBrainGenotype test = new NeatBrainGenotype(); test.AddNamedNode("input1", NodeType.INPUT, ActivationFunctionType.RELU); test.AddNamedNode("input2", NodeType.INPUT, ActivationFunctionType.RELU); test.AddNamedNode("hidden1", NodeType.HIDDEN, ActivationFunctionType.RELU); test.AddNamedNode("hidden2", NodeType.HIDDEN, ActivationFunctionType.RELU); test.AddNamedNode("output1", NodeType.OUTPUT, ActivationFunctionType.RELU); test.ConnectionGenes.Add(new ConnectionGene(1, 0, 2, 1)); test.ConnectionGenes.Add(new ConnectionGene(3, 1, 3, 1)); test.ConnectionGenes.Add(new ConnectionGene(4, 2, 4, 1)); test.ConnectionGenes.Add(new ConnectionGene(5, 3, 4, 1)); var nonRecurrentGene = new ConnectionGene(2, 2, 3, 1, true, true); Assert.IsFalse(test.CreatesCycle(nonRecurrentGene)); test.ConnectionGenes.Add(nonRecurrentGene); var recurrentGene = new ConnectionGene(2, 3, 2, 1); Assert.IsFalse(test.CreatesCycle(recurrentGene)); }
/// <summary> /// Mutates the given genotype with the assigned mutators. /// Mutators each have an assigned probability of activating (ProbabiltiyOfMutation). The list of mutators is read from the lowest index up until the highest. /// If a mutator activates, then no more mutators can be activated and the mutattion finishes. /// A mutator with a ProbabiltiyOfMutation will always activate if we reach it in the list. /// </summary> /// <param name="genotype">Genotype to mutate</param> /// <param name="innovationIdManager">Innovation id manager. New innovation ids will be taken from here.</param> public void Mutate(NeatBrainGenotype genotype, InnovationIdManager innovationIdManager) { foreach (AbstractMutator mutator in Mutators) { if (Random.NextDouble() < mutator.ProbabiltiyOfMutation) { mutator.Mutate(genotype, Random, innovationIdManager); } } }
public void TestRecurrentFromOutput() { NeatBrainGenotype test = new NeatBrainGenotype(); test.AddNamedNode("input1", NodeType.INPUT, ActivationFunctionType.RELU); test.AddNamedNode("input2", NodeType.INPUT, ActivationFunctionType.RELU); test.AddNamedNode("input3", NodeType.INPUT, ActivationFunctionType.RELU); // Hidden genes test.NodeGenes.Add(new NodeGene(3, NodeType.HIDDEN, ActivationFunctionType.RELU)); test.NodeGenes.Add(new NodeGene(4, NodeType.HIDDEN, ActivationFunctionType.RELU)); // Output genes test.AddNamedNode("output", NodeType.OUTPUT, ActivationFunctionType.RELU); test.NodeGenes.Add(new NodeGene(5, NodeType.OUTPUT, ActivationFunctionType.RELU)); // Input connections test.ConnectionGenes.Add(new ConnectionGene(1, 0, 3, 1)); test.ConnectionGenes.Add(new ConnectionGene(2, 1, 3, 1)); test.ConnectionGenes.Add(new ConnectionGene(3, 1, 4, 1)); test.ConnectionGenes.Add(new ConnectionGene(4, 2, 4, 1)); // Hidden connection test.ConnectionGenes.Add(new ConnectionGene(5, 3, 4, 1)); // The recurrent connection from 4 to 3 test.ConnectionGenes.Add(new ConnectionGene(6, 4, 3, 1, true, true)); // Hidden to output test.ConnectionGenes.Add(new ConnectionGene(7, 3, 5, 1)); test.ConnectionGenes.Add(new ConnectionGene(8, 4, 5, 1)); // The recurrent connection from 5 to 3 test.ConnectionGenes.Add(new ConnectionGene(9, 5, 3, -1)); var network = new NeatBrainPhenotype(test); network.SetInput("input1", 1.0f); network.SetInput("input2", 2.0f); network.SetInput("input3", 3.0f); network.Calculate(); Assert.AreEqual(0.0d, network.Get(5)); network.SetInput("input1", 1.0f); network.SetInput("input2", 2.0f); network.SetInput("input3", 3.0f); network.Calculate(); Assert.AreEqual(8.0d, network.Get(5)); network.SetInput("input1", 1.0f); network.SetInput("input2", 2.0f); network.SetInput("input3", 3.0f); network.Calculate(); Assert.AreEqual(16.0d, network.Get(5)); }
public void TestNamedNodes() { NeatBrainGenotype test = new NeatBrainGenotype(); test.AddNamedNode("bias", NodeType.BIAS, ActivationFunctionType.RELU); test.AddNamedNode("input1", NodeType.INPUT, ActivationFunctionType.RELU); test.AddNamedNode("input2", NodeType.INPUT, ActivationFunctionType.RELU); Assert.IsTrue(test.NodeGenes.Count == 3); Assert.IsTrue(test.NodeNameMap.ContainsKey("bias")); Assert.IsTrue(test.NodeNameMap.ContainsKey("input1")); Assert.IsTrue(test.NodeNameMap.ContainsKey("input2")); }
public void TestReEnableCreatesCycle() { // Test that a gene already in the genome can be tested fro recurrence NeatBrainGenotype test = new NeatBrainGenotype(); test.AddNamedNode("input1", NodeType.INPUT, ActivationFunctionType.RELU); test.AddNamedNode("hidden1", NodeType.HIDDEN, ActivationFunctionType.RELU); test.AddNamedNode("output1", NodeType.OUTPUT, ActivationFunctionType.RELU); test.ConnectionGenes.Add(new ConnectionGene(1, 0, 1, 1)); var recurrentGene = new ConnectionGene(2, 1, 1, 1, false, true); test.ConnectionGenes.Add(recurrentGene); test.ConnectionGenes.Add(new ConnectionGene(3, 1, 2, 1)); // This new gene should not create a cycle Assert.IsTrue(test.CreatesCycle(recurrentGene)); }
public void Update(uint tick, float _gameSpeed) { Random r = new Random(); for (int i = 0; i < Compatible.Count; i++) { var parentEntity = Compatible[i]; var reproduction = parentEntity.GetComponent <ReproductionComponent>(); if (reproduction.Reproduce > reproduction.ReproductionThreshold) { var energy = parentEntity.GetComponent <EnergyComponent>(); // check if we have the energy if (!energy.CanAfford(reproduction.ReproductionEnergyCost)) { continue; } if (energy.Energy - reproduction.ReproductionEnergyCost < reproduction.RequiredRemainingEnergy) { continue; } // charge teh parent energy. Each critter has an efficency rating, so some energy gets wasted an the rest gets sent to the child. float charged = energy.ChargeEnergy(reproduction.ReproductionEnergyCost); float toChild = reproduction.Efficency * charged; float wasted = charged - toChild; // Let the energy manager know about the wasted energy. _energyManager.DepositEnergy(wasted); // Some set up for the new child, setting positions/giving energy. var babyEntity = Pool.AddEntityFromDefinition(reproduction.ChildDefinitionId, _simulation.JsonSettings, parentEntity.Tag); babyEntity.GetComponent <EnergyComponent>().Energy = toChild; var parentPosition = parentEntity.GetComponent <TransformComponent>().LocalPosition; babyEntity.GetComponent <TransformComponent>().LocalPosition = parentPosition; // The new child will need a mutated brain var originalBrainComponent = parentEntity.GetComponent <BrainComponent>(); var originalBrain = originalBrainComponent.Brain; AbstractBrainGenotype parentGenotype = ((NeatBrainPhenotype)originalBrain).Genotype; NeatBrainGenotype childGenotype = (NeatBrainGenotype)parentGenotype.Clone(); _muationManager.Mutate(childGenotype, _innovationIdManager); var newBrain = babyEntity.GetComponent <BrainComponent>(); newBrain.SetGenotype(childGenotype); // Do not need to pass it type as we have already set the brain, therefore it does not need initializing. newBrain.SetUpLinks(null, _simulation.Settings); newBrain.ParentSpecies = originalBrainComponent.Species; BirthEventInfo e = new BirthEventInfo(parentPosition, tick * _gameSpeed, parentEntity.Id, parentEntity.Tag, babyEntity.Id, babyEntity.Tag, childGenotype, originalBrainComponent.Species.Id); BirthEvent?.Invoke(e); } } }
/// <summary> /// Mutates a given genotype /// </summary> /// <param name="genotype">Genotype to mutate</param> public abstract void Mutate(NeatBrainGenotype genotype, Random random, InnovationIdManager innovationIdManager);