-
Notifications
You must be signed in to change notification settings - Fork 3
Using an Environment
Let's see how we can use an environment to our search.
For this tutorial, we won't be solving a problem. Instead, we'll see how we can use the search engine to simulate a habitat.
Our habitat will have two types of chromosomes in it. Oc2Producers - who consume oxygen (O), and produce carbon dioxide (CO2), and OProducers - who consume carbon dioxide (CO2), and produce oxygen (O). So that our habitat will work, all chromosomes will create more then they consume (that is, if a OProducer consumes 1 CO2, it will produce 2 Os).
Let's start with an enum that will define the types of chromosomes:
enum ChromosomeType
{
OProducer,
Oc2Producer
}Our habitat will contain 2 type of chromosomes: OProducer, Oc2Producer. The chromosome will get its type in its constructor.
class MyChromosome : IChromosome
{
private readonly Random random = new Random();
public ChromosomeType Type { get; private set; }
public MyChromosome(ChromosomeType type)
{
Type = type;
}
public double Evaluate()
{
// We don't need to implement this. Evaluations will be handled by our custom ChromosomeEvaluator.
throw new NotImplementedException();
}
/// <summary>
/// A mutation will change the type of a chromosome
/// </summary>
public void Mutate()
{
Type = Type == ChromosomeType.OProducer ? ChromosomeType.Oc2Producer : ChromosomeType.OProducer;
}
public override string ToString() => Type.ToString();
}Our crossover manager will operate as following:
- If both chromosomes are OProducers: return a chromosome of type OProducer.
- If both chromosomes are Oc2Producer : return a chromosome of type Oc2Producer.
- In any other case: return an OProducerswith a probability of 0.5, and an Oc2Producer with a probability of 0.5.
class CrossoverManager : ICrossoverManager
{
private readonly Random random = new Random();
public IChromosome Crossover(IChromosome chromosome1, IChromosome chromosome2)
{
var type1 = ((MyChromosome) chromosome1).Type;
var type2 = ((MyChromosome) chromosome2).Type;
if (type1 == ChromosomeType.OProducer && type2 == ChromosomeType.OProducer)
return new MyChromosome(ChromosomeType.OProducer);
if (type1 == ChromosomeType.Oc2Producer && type2 == ChromosomeType.Oc2Producer)
return new MyChromosome(ChromosomeType.Oc2Producer);
return new MyChromosome(random.NextDouble() < 0.5 ? ChromosomeType.OProducer : ChromosomeType.Oc2Producer);
}
}Our PopulationGenerator will generate chromosomes of type OProducers with a probability of 0.5, and one of type Oc2Producers with a probability of 0.5
class PopulationGenerator : IPopulationGenerator
{
private readonly Random random = new Random();
public IEnumerable<IChromosome> GeneratePopulation(int size)
{
var chromosomes = new IChromosome[size];
for (int i = 0; i < size; i++)
chromosomes[i] = new MyChromosome(random.NextDouble() < 0.5
? ChromosomeType.OProducer
: ChromosomeType.Oc2Producer);
return chromosomes;
}
}Our environment will hold the amounts of O and OC2 available. To calculate this, it will go through the chromosomes in the population, and for every OProducer add 2 Os and reduce one OC2. For every Oc2Producer it will add 2 OC2s and reduce one O.
class MyEnvironment : IEnvironment
{
public void UpdateEnvierment(IChromosome[] chromosomes, int generation)
{
O = 0;
OC2 = 0;
foreach (var chromosome in chromosomes)
{
var myChromosome = (MyChromosome) chromosome;
if (myChromosome.Type == ChromosomeType.OProducer)
{
O += 2;
OC2 -= 1;
}
else
{
OC2 += 2;
O -= 1;
}
}
O = Math.Max(0, O);
OC2 = Math.Max(0, OC2);
}
public int O { get; private set; }
public int OC2 { get; private set; }
public override string ToString() => $"O: {O}; OC2 {OC2}";Let's create the ChromosomeEvaluator. The ChromosomeEvaluator will evaluate the chromosomes based on the O and OC2 in the environment.
class ChromosomeEvaluator : IChromosomeEvaluator
{
private MyEnvironment environment;
public void SetEnvierment(IEnvironment environment)
{
this.environment = (MyEnvironment)environment;
}
public double Evaluate(IChromosome chromosome)
{
var type = ((MyChromosome) chromosome).Type;
double baseEvaluation = type == ChromosomeType.OProducer ? environment.OC2 : environment.O;
return (baseEvaluation + 1) / EnvironmentForm.POPULATION_SIZE;
}Finally, lets put everything together and run the search:
var engine = new GeneticSearchEngineBuilder(POPULATION_SIZE, GENERATIONS, new CrossoverManager(),
new PopulationGenerator()).SetCustomChromosomeEvaluator(new ChromosomeEvaluator())
.SetEnvironment(new MyEnvironment()).SetMutationProbability(0.01).Build();
while (<SomeStopCondition>){
var result = engine.Next();
// Do something with the result here.
}You can find the complete code (together with a poorly designed GUI), here.