KScalar stats_inbreeding_depression (KConfig K) // Compute inbreeding depression (1 - self/outcross) from // the load class data for selfed and outcrossed progeny. { const char * thisfunction = "stats_inbreeding_depression"; KInt i, j, g; KArray a, via_fgam, via_mgam; KVector1 thismgam, thisfgam, allmgam, allfgam; KScalar w_self, w_out, ibd, thisibd; if (K->genotypes != 1) { not_implemented(thisfunction, "genotypes != 1"); } ibd = 0.0; apply_gametes_full(K, allmgam, allfgam, K->x); for (i=0; i <= K->MI; i++) { for (j=0; j <= K->MJ; j++) { for (g=0; g < K->genotypes; g++) { /* compute fitness of selfed and outcrossed ** progeny of this load class by creating dummy ** arrays representing the progeny resulting ** from matings involving just these load classes. ** ** Selfing is straightforward. ** ** For outcrossing, matings involving both female ** and male gametes must be considered. This is ** done by mating female gametes produced by this ** load class with the population male gametes, ** and the male gametes produced by this load ** class with the population female gametes. */ if (K->x[i][j][g] == 0.0) continue; /* fill_KArray(K, a1, 0.0); ** a1[i][j][g] = K->x[i][j][g]; ** apply_self_progeny(K, a2, a1); */ apply_self_progeny_stats(K, a, K->x[i][j][g], i, j, g); w_self = cumulative_fitness(K, a); apply_gametes_stats(K, thismgam, thisfgam, K->x[i][j][g], i, j, g); /* apply_gametes(K, allmgam, allfgam, K->x); */ apply_zygotes(K, via_fgam, allmgam, thisfgam); apply_zygotes(K, via_mgam, thismgam, allfgam); w_out = cumulative_fitness(K, via_fgam) + cumulative_fitness(K, via_mgam); w_out *= 0.5; thisibd = 1.0 - (w_self / w_out); ibd += thisibd * K->x[i][j][g]; } } } return ibd; }
void GeneticAlgorithm<Model>::Run( const GeneticFitnessFunction<Model>& fitness_function, GeneticSearchStrategy<Model>& searcher, size_t population_size, size_t breeds_per_generation) { // A lambda for sorting the population based on fitness via std::sort. auto fitness_sort = [](const member_t& a, const member_t& b) { return a.fitness() > b.fitness(); }; // Randomly selects an index based off of a given probability distribution. // The CDF does not need to be normalized, just monotonically increasing. auto select_index = [](const std::vector<double>& cdf) { double p = (double)rand() / RAND_MAX * cdf.back(); int i = 0; while (p > cdf[i]) i++; return i; }; // Given the population, construct a cumulative distribution function based // on the fitness of the members. Despite the name, the CDF is not // normalized. auto compute_fitness_cdf = [&fitness_sort](const population_t& population, std::vector<double>& fitness_cdf) { fitness_cdf.resize(population.size()); auto minmax_members = std::minmax_element( std::begin(population), std::end(population), fitness_sort); double min_fitness = minmax_members.second->fitness(); double max_fitness = minmax_members.first->fitness(); double range = max_fitness - min_fitness; double offset = (range == 0) ? 1 - min_fitness : -min_fitness; int index = 0; for (const auto& tmp : population) { fitness_cdf[index] = (index == 0) ? tmp.fitness() + offset : fitness_cdf[index-1] + tmp.fitness() + offset; index++; } }; // Initialize the population. { std::lock_guard<std::mutex> lock(generation_lock_); population_.clear(); population_.reserve(population_size + breeds_per_generation); for (size_t i = 0; i < population_size; i++) { member_t member(std::move(searcher.Introduce(fitness_function)), 0); fitness_function(member); population_.push_back(std::move(member)); } std::sort(std::begin(population_), std::end(population_), fitness_sort); } std::vector<double> cumulative_fitness(population_size, 0); std::vector<double> new_cumulative_fitness( population_size + breeds_per_generation, 0); std::vector<size_t> selection_indices(population_size, 0); generation_num_ = 0; // Main loop of the genetic algorithm. do { std::lock_guard<std::mutex> lock(generation_lock_); running_ = true; generation_num_++; compute_fitness_cdf(population_, cumulative_fitness); // Construct the new population members from the existing ones. for (size_t i = 0; i < breeds_per_generation; i++) { int index1 = select_index(cumulative_fitness); int index2 = index1; while (index1 == index2) { index2 = select_index(cumulative_fitness); } do { member_t member(std::move(searcher.Crossover(population_[index1], population_[index2]))); searcher.Mutate(member); // Only add if the model is valid. if (fitness_function(member)) { population_.push_back(std::move(member)); break; } } while (true); } // If we extra members hanging around from the previous generation, select // the best population member and fill out the population using the fitness // to weight selection. if (population_.size() > population_size) { compute_fitness_cdf(population_, new_cumulative_fitness); selection_indices[0] = 0; for (size_t i = 1; i < population_size; i++) { bool unique = false; size_t index = 0; while (!unique) { index = select_index(new_cumulative_fitness); unique = true; for (int j = i - 1; j >= 0; j--) { if (selection_indices[j] == index) { unique = false; break; } } } selection_indices[i] = index; } // Sort the selection indices in ascending order, so we can select them in // the population in one pass. std::sort(std::begin(selection_indices), std::end(selection_indices)); // Construct the new population based on the selections. for (size_t i = 0; i < population_size; i++) { if (i == selection_indices[i]) continue; std::swap(population_[i], population_[selection_indices[i]]); } population_.erase(std::begin(population_) + population_size, std::end(population_)); } std::sort(std::begin(population_), std::end(population_), fitness_sort); } while (searcher.ShouldContinue(population_, generation_num_)); running_ = false; }