0% found this document useful (0 votes)
72 views6 pages

NEAT in Unity

This document describes a Neuroevolution of Augmenting Topologies (NEAT) algorithm implementation in C# for evolving neural networks. It defines classes for genomes containing neural network configurations, and a NEAT manager that evolves a population of genomes. Key aspects include initializing a population of genomes, evaluating fitness, selecting parents, breeding new genomes with crossover and mutation, and iterating the process each generation to evolve increasingly fit networks.

Uploaded by

Hrittik Das
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
72 views6 pages

NEAT in Unity

This document describes a Neuroevolution of Augmenting Topologies (NEAT) algorithm implementation in C# for evolving neural networks. It defines classes for genomes containing neural network configurations, and a NEAT manager that evolves a population of genomes. Key aspects include initializing a population of genomes, evaluating fitness, selecting parents, breeding new genomes with crossover and mutation, and iterating the process each generation to evolve increasingly fit networks.

Uploaded by

Hrittik Das
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 6

using System.Collections.

Generic;
using UnityEngine;

public class NEAT : MonoBehaviour


{
// Define the number of inputs, outputs, and hidden nodes in the network
private const int NUM_INPUTS = 2;
private const int NUM_OUTPUTS = 1;
private const int NUM_HIDDEN = 2;

// Define the population size


private const int POPULATION_SIZE = 100;

// Define the mutation rate


private const float MUTATION_RATE = 0.05f;

// Define the mutation amount


private const float MUTATION_AMOUNT = 0.1f;

// Define the list of genomes


private List<Genome> genomes = new List<Genome>();

// Initialize the NEAT algorithm


private void Start()
{
InitializePopulation();
}

// Initialize the population of genomes


private void InitializePopulation()
{
// Generate the initial population of genomes
for (int i = 0; i < POPULATION_SIZE; i++)
{
genomes.Add(new Genome(NUM_INPUTS, NUM_OUTPUTS, NUM_HIDDEN));
}
}

// Evaluate the fitness of each genome in the population


private void EvaluateFitness()
{
// Calculate the fitness of each genome
foreach (Genome genome in genomes)
{
genome.CalculateFitness();
}
}

// Select the parents for the next generation


private void Selection()
{
// Sort the genomes based on their fitness
genomes.Sort((a, b) => b.Fitness.CompareTo(a.Fitness));

// Select the top 50% of genomes for the next generation


int half = genomes.Count / 2;
genomes = genomes.GetRange(0, half);
}
// Breed the new generation of genomes
private void Breeding()
{
// Breed the next generation of genomes
while (genomes.Count < POPULATION_SIZE)
{
// Select two parents at random
Genome parent1 = genomes[Random.Range(0, genomes.Count)];
Genome parent2 = genomes[Random.Range(0, genomes.Count)];

// Crossover the parents to generate a child


Genome child = parent1.Crossover(parent2);

// Mutate the child


child.Mutate(MUTATION_RATE, MUTATION_AMOUNT);

// Add the child to the new generation


genomes.Add(child);
}
}

// Update the NEAT algorithm


private void Update()
{
// Evaluate the fitness of each genome
EvaluateFitness();

// Select the parents for the next generation


Selection();

// Breed the new generation of genomes


Breeding();
}
}

// Define the Genome class


public class Genome
{
// Define the list of nodes
private List<Node> nodes = new List<Node>();

// Define the fitness of the genome


private float fitness;

// Constructor for the Genome class


public Genome(int numInputs, int numOutputs, int numHidden)
{
// Create the input nodes
for (int i = 0; i < numInputs; i++)
{
nodes.Add(new Node(NodeType.Input));
}

// Create the output nodes


for (int i = 0; i < numOutputs; i++)
{
nodes.Add(new Node(NodeType.Output));
}
// Create the hidden nodes
for (int i = 0; i < numHidden; i++)
{
nodes.Add(new Node(NodeType.Hidden));
}

// Connect the nodes randomly


for (int i = 0; i < nodes.Count; i++)
{
for (int j = 0; j < nodes.Count; j++)
{
if (Random.Range(0, 2) == 1)
{
connections.Add(new Connection(nodes[i], nodes[j], Random.Range(-
1.0f, 1.0f)));
}
}
}
}

// Crossover function to breed two genomes


public Genome Crossover(Genome other)
{
// Create a new genome
Genome child = new Genome(0, 0, 0);

// Choose the fittest genome as the primary parent


Genome parent1 = fitness > other.fitness ? this : other;
Genome parent2 = fitness > other.fitness ? other : this;

// Add the nodes from the primary parent to the child


foreach (Node node in parent1.nodes)
{
child.nodes.Add(new Node(node.Type));
}

// Add the connections from the primary parent to the child


foreach (Connection connection in parent1.connections)
{
// Find the corresponding nodes in the child
Node fromNode = child.nodes.Find(x => x.ID == connection.FromNode.ID);
Node toNode = child.nodes.Find(x => x.ID == connection.ToNode.ID);

// Add the connection to the child


child.connections.Add(new Connection(fromNode, toNode, connection.Weight));
}

// Loop through the connections in the secondary parent


foreach (Connection connection in parent2.connections)
{
// Check if the connection already exists in the child
if (!child.connections.Exists(x => x.FromNode.ID == connection.FromNode.ID
&& x.ToNode.ID == connection.ToNode.ID))
{
// Find the corresponding nodes in the child
Node fromNode = child.nodes.Find(x => x.ID == connection.FromNode.ID);
Node toNode = child.nodes.Find(x => x.ID == connection.ToNode.ID);

// Add the connection to the child


child.connections.Add(new Connection(fromNode, toNode,
connection.Weight));
}
}

// Return the child genome


return child;
}

// Mutate the genome by adding a node, connection, or changing a weight


public void Mutate()
{
// Choose a random mutation type
switch (Random.Range(0, 3))
{
// Add a node
case 0:
// Choose a random connection
Connection connection = connections[Random.Range(0,
connections.Count)];

// Disable the connection


connection.Enabled = false;

// Create a new hidden node


Node node = new Node(NodeType.Hidden);

// Add the new node to the list of nodes


nodes.Add(node);

// Create two new connections, one from the from node to the new node,
and one from the new node to the to node
connections.Add(new Connection(connection.FromNode, node, 1.0f));
connections.Add(new Connection(node, connection.ToNode,
connection.Weight));
break;

// Add a connection
case 1:
// Choose two random nodes that are not already connected
Node fromNode = nodes[Random.Range(0, nodes.Count)];
Node toNode = nodes[Random.Range(0, nodes.Count)];

// Check if the nodes are not the same and if the connection does not
already exist
if (fromNode.ID != toNode.ID && !connections.Exists(x => x.FromNode.ID
== fromNode.ID && x.ToNode.ID == toNode.ID))
{
// Add the connection
connections.Add(new Connection(fromNode, toNode, Random.Range(-
1.0f, 1.0f)));
}
break;

// Change a weight
case 2:
// Choose a random connection
Connection c = connections[Random.Range(0, connections.Count)];
// Change the weight of the connection
c.Weight += Random.Range(-0.1f, 0.1f);
break;
}
}

// Define the NEATManager class


public class NEATManager : MonoBehaviour
{
// Define the population size
public int populationSize = 100;

// Define the number of inputs, outputs, and hidden nodes


public int numInputs = 2;
public int numOutputs = 1;
public int numHidden = 2;

// Define the mutation rate


public float mutationRate = 0.01f;

// List of genomes
private List<Genome> genomes = new List<Genome>();

// Start is called before the first frame update


void Start()
{
// Create the initial population
for (int i = 0; i < populationSize; i++)
{
genomes.Add(new Genome(numInputs, numOutputs, numHidden));
}
}

// Update is called once per frame


void Update()
{
// Loop through the genomes
foreach (Genome genome in genomes)
{
// Evaluate the fitness of the genome
genome.Fitness = EvaluateFitness(genome);

// Check if the mutation rate is met


if (Random.Range(0, 1.0f) < mutationRate){
// Mutate the genome
genome.Mutate();
}
}

// Sort the genomes by fitness


genomes.Sort((x, y) => x.Fitness.CompareTo(y.Fitness));

// Keep the top x% of the genomes for the next generation


int keep = (int)(populationSize * 0.1f);
genomes = genomes.GetRange(0, keep);

// Create new offspring to fill the rest of the population


while (genomes.Count < populationSize)
{
// Choose two random genomes to breed
Genome parent1 = genomes[Random.Range(0, keep)];
Genome parent2 = genomes[Random.Range(0, keep)];

// Breed the two genomes to create a child


Genome child = parent1.Breed(parent2);

// Add the child to the list of genomes


genomes.Add(child);
}
}

// Evaluate the fitness of a genome


float EvaluateFitness(Genome genome)
{
// Your code to evaluate the fitness of the genome goes here

return Random.Range(0.0f, 1.0f);


}
}

You might also like