public void RandomSampleTest() { var sampler = new WeightedSampler<int>(new FixedRandom()); for (var i = 0; i < Weights.Length; i++) { sampler.AddEntry(i, Weights[i]); } foreach (var t in Sequence) { Assert.AreEqual(WeightsAdded.First(n => n >= t), WeightsAdded[sampler.RandomSample()]); } }
public void RandomSampleTest() { var sampler = new WeightedSampler <int>(new FixedRandom()); for (var i = 0; i < Weights.Length; i++) { sampler.AddEntry(i, Weights[i]); } foreach (var t in Sequence) { Assert.AreEqual(WeightsAdded.First(n => n >= t), WeightsAdded[sampler.RandomSample()]); } }
/// <summary> /// Progresses the optimization by evolving the current population. /// </summary> /// <returns>The number of generations thus far.</returns> public void NewGeneration() { if (_population == null) throw new InvalidOperationException("Cannot generate a next" + " generation without prior call to InitializeEvolution!"); GenerationCount++; if (_populationSize == 0) { // Not returning would lead to an infertile generation. return; } Individual[] newPopulation = new Individual[_populationSize]; int newPopIndex = 0; WeightedSampler<Individual> sampler = new WeightedSampler<Individual>(_random); #if VERBOSE Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); double totalHealth = 0; double totalBitsSet = 0; double totalAge = 0; int acceptedTotal = 0; int acceptedWorse = 0; #endif // Sort the population by fitness. _population = _population.OrderBy(ind => ind.Fitness).ToArray(); int index = 0; foreach (Individual individual in _population) { index++; #if VERBOSE stopwatch.Stop(); totalHealth += individual.Fitness; totalBitsSet += SetBits(individual.DNA); stopwatch.Start(); #endif // Survival of the fittest (population was ordered by fitness above) if (index < 0.5 * _populationSize) { continue; } // This seems to have a good effect on convergence speed. // By only allowing solutions that survived a round of culling // to procreate, the solution quality is kept high. if (individual.Age >= 1) sampler.AddEntry(individual, index); #if VERBOSE totalAge += individual.Age; #endif individual.Rank = index; individual.Age++; newPopulation[newPopIndex] = individual; newPopIndex++; } //for (int i = 0; i < newPopIndex; i++) Parallel.For(0, newPopIndex, i => { Individual temp = newPopulation[i]; Individual mutation = SpawnIndividual(MutateDNA(temp.DNA)); // Lowering the age here would lead to faster convergence but would // make the population go extinct several times. mutation.Age = temp.Age; // Mutations have a chance to be rejected based on the fitness loss // relative to the non-mutated individual. See explanation above. if (AcceptNewState(temp, mutation)) { #if VERBOSE // If you want to measure these for debugging purposes, remove the // parallelization of this loop. //acceptedTotal++; //if (mutation.Fitness < temp.Fitness) // acceptedWorse++; #endif temp = mutation; } newPopulation[i] = temp; }); if (!sampler.CanSample) { // This is actually a pretty serious problem. _population = CreatePopulation(); Debug.WriteLine("Entire population was infertile (Generation " + GenerationCount + ")."); return; } // Replace purged individuals //for (int i = newPopIndex; i < populationSize; i++) Parallel.For(newPopIndex, _populationSize, i => { BitArray parent1 = sampler.RandomSample().DNA; BitArray parent2 = sampler.RandomSample().DNA; BitArray newDNA = CombineIndividualsDNA(parent1, parent2); newPopulation[i] = SpawnIndividual(newDNA); }); _population = newPopulation; // Doing this at the end so the last generation has a use. UpdateBestSolution(); _temperature *= _annealingFactor; #if VERBOSE stopwatch.Stop(); Debug.Write("Evaluation time for " + GenerationCount + " : "); Debug.WriteLine(stopwatch.ElapsedMilliseconds + " ms"); Debug.WriteLine("Average health: " + totalHealth / _populationSize); Debug.WriteLine("Average bits set: " + totalBitsSet / _populationSize); Debug.WriteLine("Average age: " + totalAge / _populationSize); Debug.WriteLine("Accepted new states (all/worse): " + acceptedTotal + "/" + acceptedWorse); Debug.WriteLine("Sampler entries: " + sampler.EntryCount); Debug.WriteLine("Best value so far: " + _bestSolution.Fitness); Debug.WriteLine("------------------"); Debug.Flush(); #endif }
/// <summary> /// Progresses the optimization by evolving the current population. /// </summary> /// <returns>The number of generations thus far.</returns> public int NewGeneration() { if (population == null) throw new InvalidOperationException("Cannot generate a next" + " generation without prior call to StartEvolution!"); List<Individual> newPopulation = new List<Individual>(); generationCount++; WeightedSampler<Individual> sampler = new WeightedSampler<Individual>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); // Check the fitness values of the current generation. foreach (Individual individual in population) { if (individual.Fitness < 0) throw new ArgumentOutOfRangeException("solutionFitness function", "Negative fitness values are not allowed! Use 0 fitness " + "for solutions that should not reproduce."); if (individual.Fitness > bestSolution.Fitness) bestSolution = new Individual(individual.DNA, individual.Fitness); maxFitness = Math.Max(individual.Fitness, maxFitness); minFitness = Math.Min(individual.Fitness, minFitness); } double averageHealth = 0; double averageBitsSet = 0; double averageAge = 0; int acceptedTotal = 0; int acceptedWorse = 0; int purgedIndividuals = 0; // Sort the population by fitness. population = population.OrderBy(ind => ind.Fitness).ToArray(); int index = 0; foreach (Individual individual in population) { index++; /// Max and min fitness might have changed, so we need to /// normalize the fitnesses again. individual.normalizedFitness = normalizeFitness(individual.Fitness); averageHealth += individual.normalizedFitness; averageBitsSet += SetBits(individual.DNA); // Survival of the fittest (population was ordered by fitness above) if (index < 0.5 * populationSize) { // Could be slightly more concise, I know. purgedIndividuals++; continue; } /// This seems to have a good effect on convergence speed. /// By only allowing solutions that survived a round of culling /// to procreate, the solution quality is kept high. if (individual.Age >= 1) sampler.AddEntry(individual, individual.normalizedFitness); averageAge += individual.Age; individual.Age++; // Simulated annealing Individual temp = individual; Individual mutation = spawnIndividual(mutateDNA(individual.DNA)); mutation.Age = individual.Age - 1; // TODO: Investigate. if (acceptNewState(individual, mutation)) { acceptedTotal++; if (mutation.Fitness < individual.Fitness) acceptedWorse++; temp = mutation; } newPopulation.Add(temp); } /*if (purgedIndividuals == 0) minFitness = minCurrentFitness;*/ stopwatch.Stop(); Console.Write("Evaluation time for " + generationCount + " : "); Console.WriteLine(stopwatch.ElapsedMilliseconds + " ms"); Console.WriteLine("Temperature: " + temperature); Console.WriteLine("Average health: " + averageHealth / populationSize); Console.WriteLine("Average bits set: " + averageBitsSet / populationSize); Console.WriteLine("Average age: " + averageAge / populationSize); Console.WriteLine("Accepted new states (all/worse): " + acceptedTotal + "/" + acceptedWorse); //Console.WriteLine("Purged individuals: " + purgedIndividuals + "/" + populationSize); Console.WriteLine("Sampler entries: " + sampler.EntryCount); stopwatch.Restart(); if (!sampler.CanSample) { // This is actually a pretty serious problem. population = createPopulation(); Console.WriteLine("Entire population was infertile (Generation " + generationCount + ")."); //Debug.Fail("Population went extinct, not good..."); return generationCount; } // Breed population and apply random mutations. int dnaResets = 0; // Replace purged individuals for (int i = 0; i < purgedIndividuals; i++) { BitArray parent1 = sampler.RandomSample().DNA; BitArray parent2 = sampler.RandomSample().DNA; BitArray newDNA = combineIndividualsDNA(parent1, parent2); newPopulation.Add(spawnIndividual(newDNA)); } population = newPopulation.ToArray(); // Yeah, I know, out of thin air. temperature *= annealingFactor; stopwatch.Stop(); Console.WriteLine("Best value so far: " + (1500 - bestSolution.Fitness)); Console.WriteLine("------------------"); Console.Out.Flush(); return generationCount; }