/** * Runs the Inverover algorithm for the number of generations specified in the constructor. * * @param[in,out] pop input/output pagmo::population to be evolved. */ void inverover::evolve(population &pop) const { const problem::base_tsp* prob; //check if problem is of type pagmo::problem::base_tsp try { const problem::base_tsp& tsp_prob = dynamic_cast<const problem::base_tsp &>(pop.problem()); prob = &tsp_prob; } catch (const std::bad_cast& e) { pagmo_throw(value_error,"Problem not of type pagmo::problem::base_tsp"); } // Let's store some useful variables. const population::size_type NP = pop.size(); const problem::base::size_type Nv = prob->get_n_cities(); // Initializing the random number generators boost::uniform_real<double> uniform(0.0, 1.0); boost::variate_generator<boost::lagged_fibonacci607 &, boost::uniform_real<double> > unif_01(m_drng, uniform); boost::uniform_int<int> NPless1(0, NP - 2); boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > unif_NPless1(m_urng, NPless1); boost::uniform_int<int> Nv_(0, Nv - 1); boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > unif_Nv(m_urng, Nv_); boost::uniform_int<int> Nvless1(0, Nv - 2); boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > unif_Nvless1(m_urng, Nvless1); //create own local population std::vector<decision_vector> my_pop(NP, decision_vector(Nv)); //check if some individuals in the population that is passed as a function input are feasible. bool feasible; std::vector<int> not_feasible; for (size_t i = 0; i < NP; i++) { feasible = prob->feasibility_x(pop.get_individual(i).cur_x); if(feasible) { //if feasible store it in my_pop switch(prob->get_encoding()) { case problem::base_tsp::FULL: my_pop[i] = prob->full2cities(pop.get_individual(i).cur_x); break; case problem::base_tsp::RANDOMKEYS: my_pop[i] = prob->randomkeys2cities(pop.get_individual(i).cur_x); break; case problem::base_tsp::CITIES: my_pop[i] = pop.get_individual(i).cur_x; break; } } else { not_feasible.push_back(i); } } //replace the not feasible individuals by feasible ones int i; switch (m_ini_type) { case 0: { //random initialization (produces feasible individuals) for (size_t ii = 0; ii < not_feasible.size(); ii++) { i = not_feasible[ii]; for (size_t j = 0; j < Nv; j++) { my_pop[i][j] = j; } } int tmp; size_t rnd_idx; for (size_t j = 1; j < Nv-1; j++) { boost::uniform_int<int> dist_(j, Nv - 1); boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > dist(m_urng,dist_); for (size_t ii = 0; ii < not_feasible.size(); ii++) { i = not_feasible[ii]; rnd_idx = dist(); tmp = my_pop[i][j]; my_pop[i][j] = my_pop[i][rnd_idx]; my_pop[i][rnd_idx] = tmp; } } break; } case 1: { //initialize with nearest neighbor algorithm std::vector<int> starting_notes(std::max(Nv,not_feasible.size())); for (size_t j = 0; j < starting_notes.size(); j++) { starting_notes[j] = j; } //std::shuffle(starting_notes.begin(), starting_notes.end(), m_urng); for (size_t ii = 0; ii < not_feasible.size(); ii++) { i = not_feasible[ii]; pagmo::population one_ind_pop(pop.problem(), 1); std::cout << starting_notes[i] << ' '; pagmo::algorithm::nn_tsp algo(starting_notes[i] % Nv); algo.evolve(one_ind_pop); switch( prob->get_encoding() ) { case problem::base_tsp::FULL: my_pop[i] = prob->full2cities(one_ind_pop.get_individual(0).cur_x); break; case problem::base_tsp::RANDOMKEYS: my_pop[i] = prob->randomkeys2cities(one_ind_pop.get_individual(0).cur_x); break; case problem::base_tsp::CITIES: my_pop[i] = one_ind_pop.get_individual(0).cur_x; break; } std::cout << i << ' ' << one_ind_pop.get_individual(0).cur_f << std::endl; } break; } default: pagmo_throw(value_error,"Invalid initialization type"); } std::vector<fitness_vector> fitness(NP, fitness_vector(1)); for(size_t i=0; i < NP; i++){ switch( prob->get_encoding() ) { case problem::base_tsp::FULL: fitness[i] = prob->objfun(prob->full2cities(my_pop[i])); break; case problem::base_tsp::RANDOMKEYS: fitness[i] = prob->objfun(prob->cities2randomkeys(my_pop[i], pop.get_individual(i).cur_x)); break; case problem::base_tsp::CITIES: fitness[i] = prob->objfun(my_pop[i]); break; } } decision_vector tmp_tour(Nv); bool stop, changed; size_t rnd_num, i2, pos1_c1, pos1_c2, pos2_c1, pos2_c2; //pos2_c1 denotes the position of city1 in parent2 fitness_vector fitness_tmp; //InverOver main loop for(int iter = 0; iter < m_gen; iter++) { for(size_t i1 = 0; i1 < NP; i1++) { tmp_tour = my_pop[i1]; pos1_c1 = unif_Nv(); stop = false; changed = false; while(!stop){ if(unif_01() < m_ri) { rnd_num = unif_Nvless1(); pos1_c2 = (rnd_num == pos1_c1? Nv-1:rnd_num); } else { i2 = unif_NPless1(); i2 = (i2 == i1? NP-1:i2); pos2_c1 = std::find(my_pop[i2].begin(),my_pop[i2].end(),tmp_tour[pos1_c1])-my_pop[i2].begin(); pos2_c2 = (pos2_c1 == Nv-1? 0:pos2_c1+1); pos1_c2 = std::find(tmp_tour.begin(),tmp_tour.end(),my_pop[i2][pos2_c2])-tmp_tour.begin(); } stop = (abs(pos1_c1-pos1_c2)==1 || static_cast<problem::base::size_type>(abs(pos1_c1-pos1_c2))==Nv-1); if(!stop) { changed = true; if(pos1_c1<pos1_c2) { for(size_t l=0; l < (double (pos1_c2-pos1_c1-1)/2); l++) { std::swap(tmp_tour[pos1_c1+1+l],tmp_tour[pos1_c2-l]); } pos1_c1 = pos1_c2; } else { //inverts the section from c1 to c2 (see documentation Note3) for(size_t l=0; l < (double (pos1_c1-pos1_c2-1)/2); l++) { std::swap(tmp_tour[pos1_c2+l],tmp_tour[pos1_c1-l-1]); } pos1_c1 = (pos1_c2 == 0? Nv-1:pos1_c2-1); } } } //end of while loop (looping over a single indvidual) if(changed) { switch(prob->get_encoding()) { case problem::base_tsp::FULL: fitness_tmp = prob->objfun(prob->full2cities(tmp_tour)); break; case problem::base_tsp::RANDOMKEYS: //using "randomly" index 0 as a temporary template fitness_tmp = prob->objfun(prob->cities2randomkeys(tmp_tour, pop.get_individual(0).cur_x)); break; case problem::base_tsp::CITIES: fitness_tmp = prob->objfun(tmp_tour); break; } if(prob->compare_fitness(fitness_tmp,fitness[i1])) { //replace individual? my_pop[i1] = tmp_tour; fitness[i1][0] = fitness_tmp[0]; } } } // end of loop over population } // end of loop over generations //change representation of tour for (size_t ii = 0; ii < NP; ii++) { switch(prob->get_encoding()) { case problem::base_tsp::FULL: pop.set_x(ii,prob->cities2full(my_pop[ii])); break; case problem::base_tsp::RANDOMKEYS: pop.set_x(ii,prob->cities2randomkeys(my_pop[ii],pop.get_individual(ii).cur_x)); break; case problem::base_tsp::CITIES: pop.set_x(ii,my_pop[ii]); break; } } } // end of evolve
/** * Run the co-evolution algorithm * * @param[in,out] pop input/output pagmo::population to be evolved. */ void cstrs_co_evolution::evolve(population &pop) const { // Let's store some useful variables. const problem::base &prob = pop.problem(); const population::size_type pop_size = pop.size(); const problem::base::size_type prob_dimension = prob.get_dimension(); // get the constraints dimension problem::base::c_size_type prob_c_dimension = prob.get_c_dimension(); //We perform some checks to determine wether the problem/population are suitable for co-evolution if(prob_c_dimension < 1) { pagmo_throw(value_error,"The problem is not constrained and co-evolution is not suitable to solve it"); } if(prob.get_f_dimension() != 1) { pagmo_throw(value_error,"The problem is multiobjective and co-evolution is not suitable to solve it"); } // Get out if there is nothing to do. if(pop_size == 0) { return; } //get the dimension of the chromosome of P2 unsigned int pop_2_dim = 0; switch(m_method) { case algorithm::cstrs_co_evolution::SIMPLE: { pop_2_dim = 2; break; } case algorithm::cstrs_co_evolution::SPLIT_NEQ_EQ: { pop_2_dim = 4; break; } case algorithm::cstrs_co_evolution::SPLIT_CONSTRAINTS: pop_2_dim = 2*prob.get_c_dimension(); break; default: pagmo_throw(value_error,"The constraints co-evolutionary method is not valid."); break; } // split the population into two populations // the population P1 associated to the modified problem with penalized fitness // and population P2 encoding the penalty weights // Populations size population::size_type pop_1_size = pop_size; population::size_type pop_2_size = m_pop_penalties_size; //Creates problem associated to P2 problem::cstrs_co_evolution_penalty prob_2(prob,pop_2_dim,pop_2_size); prob_2.set_bounds(m_pen_lower_bound,m_pen_upper_bound); //random initialization of the P2 chromosome (needed for the fist generation) std::vector<decision_vector> pop_2_x(pop_2_size); std::vector<fitness_vector> pop_2_f(pop_2_size); for(population::size_type j=0; j<pop_2_size; j++) { pop_2_x[j] = decision_vector(pop_2_dim,0.); // choose random coefficients between lower bound and upper bound for(population::size_type i=0; i<pop_2_dim;i++) { pop_2_x[j][i] = boost::uniform_real<double>(m_pen_lower_bound,m_pen_upper_bound)(m_drng); } } //vector of the population P1. Initialized with clones of the original population std::vector<population> pop_1_vector; for(population::size_type i=0; i<pop_2_size; i++){ pop_1_vector.push_back(population(pop)); } // Main Co-Evolution loop for(int k=0; k<m_gen; k++) { // for each individuals of pop 2, evolve the current population, // and store the position of the feasible idx for(population::size_type j=0; j<pop_2_size; j++) { problem::cstrs_co_evolution prob_1(prob, pop_1_vector.at(j), m_method); // modify the problem by setting decision vector encoding penalty // coefficients w1 and w2 in prob 1 prob_1.set_penalty_coeff(pop_2_x.at(j)); // creating the POPULATION 1 instance based on the // updated prob 1 // prob_1 is a BASE_META???? THE CLONE OF prob_1 IN POP_1 IS AT THE LEVEL OF // THE BASE CLASS AND NOT AT THE LEVEL OF THE BASE_META, NO?!? population pop_1(prob_1,0); // initialize P1 chromosomes. The fitnesses related to problem 1 are computed for(population::size_type i=0; i<pop_1_size; i++) { pop_1.push_back(pop_1_vector.at(j).get_individual(i).cur_x); } // evolve the P1 instance m_original_algo->evolve(pop_1); //updating the original problem population (computation of fitness and constraints) pop_1_vector.at(j).clear(); for(population::size_type i=0; i<pop_1_size; i++){ pop_1_vector.at(j).push_back(pop_1.get_individual(i).cur_x); } // set up penalization variables needs for the population 2 // the constraints has not been evaluated yet. prob_2.update_penalty_coeff(j,pop_2_x.at(j),pop_1_vector.at(j)); } // creating the POPULATION 2 instance based on the // updated prob 2 population pop_2(prob_2,0); // compute the fitness values of the second population for(population::size_type i=0; i<pop_2_size; i++) { pop_2.push_back(pop_2_x[i]); } m_original_algo_penalties->evolve(pop_2); // store the new chromosomes for(population::size_type i=0; i<pop_2_size; i++) { pop_2_x[i] = pop_2.get_individual(i).cur_x; pop_2_f[i] = pop_2.get_individual(i).cur_f; } // Check the exit conditions (every 40 generations, just as DE) if(k % 40 == 0) { // finds the best population population::size_type best_idx = 0; for(population::size_type j=1; j<pop_2_size; j++) { if(pop_2_f[j][0] < pop_2_f[best_idx][0]) { best_idx = j; } } const population ¤t_population = pop_1_vector.at(best_idx); decision_vector tmp(prob_dimension); double dx = 0; for(decision_vector::size_type i=0; i<prob_dimension; i++) { tmp[i] = current_population.get_individual(current_population.get_worst_idx()).best_x[i] - current_population.get_individual(current_population.get_best_idx()).best_x[i]; dx += std::fabs(tmp[i]); } if(dx < m_xtol ) { if (m_screen_output) { std::cout << "Exit condition -- xtol < " << m_xtol << std::endl; } break; } double mah = std::fabs(current_population.get_individual(current_population.get_worst_idx()).best_f[0] - current_population.get_individual(current_population.get_best_idx()).best_f[0]); if(mah < m_ftol) { if(m_screen_output) { std::cout << "Exit condition -- ftol < " << m_ftol << std::endl; } break; } // outputs current values if(m_screen_output) { std::cout << "Generation " << k << " ***" << std::endl; std::cout << " Best global fitness: " << current_population.champion().f << std::endl; std::cout << " xtol: " << dx << ", ftol: " << mah << std::endl; std::cout << " xtol: " << dx << ", ftol: " << mah << std::endl; } } } // find the best fitness population in the final pop population::size_type best_idx = 0; for(population::size_type j=1; j<pop_2_size; j++) { if(pop_2_f[j][0] < pop_2_f[best_idx][0]) { best_idx = j; } } // store the final population in the main population // can't avoid to recompute the vectors here, otherwise we // clone the problem stored in the population with the // pop = operator! pop.clear(); for(population::size_type i=0; i<pop_1_size; i++) { pop.push_back(pop_1_vector.at(best_idx).get_individual(i).cur_x); } }
/** * Runs the Inverover algorithm for the number of generations specified in the constructor. * * @param[in,out] pop input/output pagmo::population to be evolved. */ void inverover::evolve(population &pop) const { const problem::tsp* prob; //check if problem is of type pagmo::problem::tsp try { const problem::tsp& tsp_prob = dynamic_cast<const problem::tsp &>(pop.problem()); prob = &tsp_prob; } catch (const std::bad_cast& e) { pagmo_throw(value_error,"Problem not of type pagmo::problem::tsp"); } // Let's store some useful variables. const population::size_type NP = pop.size(); const std::vector<std::vector<double> >& weights = prob->get_weights(); const problem::base::size_type Nv = prob->get_n_cities(); // Get out if there is nothing to do. if (m_gen == 0) { return; } // Initializing the random number generators boost::uniform_real<double> uniform(0.0,1.0); boost::variate_generator<boost::lagged_fibonacci607 &, boost::uniform_real<double> > unif_01(m_drng,uniform); boost::uniform_int<int> NPless1(0, NP - 2); boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > unif_NPless1(m_urng,NPless1); boost::uniform_int<int> Nv_(0, Nv - 1); boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > unif_Nv(m_urng,Nv_); boost::uniform_int<int> Nvless1(0, Nv - 2); boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > unif_Nvless1(m_urng,Nvless1); //check if we have a symmetric problem (symmetric weight matrix) bool is_sym = true; for(size_t i = 0; i < Nv; i++) { for(size_t j = i+1; j < Nv; j++) { if(weights[i][j] != weights[j][i]) { is_sym = false; goto end_loop; } } } end_loop: //create own local population std::vector<decision_vector> my_pop(NP, decision_vector(Nv)); //check if some individuals in the population that is passed as a function input are feasible. bool feasible; std::vector<int> not_feasible; for (size_t i = 0; i < NP; i++) { feasible = prob->feasibility_x(pop.get_individual(i).cur_x); if(feasible){ //if feasible store it in my_pop switch( prob->get_encoding() ) { case problem::tsp::FULL: my_pop[i] = prob->full2cities(pop.get_individual(i).cur_x); break; case problem::tsp::RANDOMKEYS: my_pop[i] = prob->randomkeys2cities(pop.get_individual(i).cur_x); break; case problem::tsp::CITIES: my_pop[i] = pop.get_individual(i).cur_x; break; } } else { not_feasible.push_back(i); } } //replace the not feasible individuals by feasible ones int i; switch (m_ini_type){ case 0: { //random initialization (produces feasible individuals) for (size_t ii = 0; ii < not_feasible.size(); ii++) { i = not_feasible[ii]; for (size_t j = 0; j < Nv; j++) { my_pop[i][j] = j; } } int tmp; size_t rnd_idx; for (size_t j = 1; j < Nv-1; j++) { boost::uniform_int<int> dist_(j, Nv - 1); boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > dist(m_urng,dist_); for (size_t ii = 0; ii < not_feasible.size(); ii++) { i = not_feasible[ii]; rnd_idx = dist(); tmp = my_pop[i][j]; my_pop[i][j] = my_pop[i][rnd_idx]; my_pop[i][rnd_idx] = tmp; } } break; } case 1: { //initialize with nearest neighbor algorithm int nxt_city; size_t min_idx; std::vector<int> not_visited(Nv); for (size_t ii = 0; ii < not_feasible.size(); ii++) { i = not_feasible[ii]; for (size_t j = 0; j < Nv; j++) { not_visited[j] = j; } my_pop[i][0] = unif_Nv(); std::swap(not_visited[my_pop[i][0]],not_visited[Nv-1]); for (size_t j = 1; j < Nv-1; j++) { min_idx = 0; nxt_city = not_visited[0]; for (size_t l = 1; l < Nv-j; l++) { if(weights[my_pop[i][j-1]][not_visited[l]] < weights[my_pop[i][j-1]][nxt_city]){ min_idx = l; nxt_city = not_visited[l];} } my_pop[i][j] = nxt_city; std::swap(not_visited[min_idx],not_visited[Nv-j-1]); } my_pop[i][Nv-1] = not_visited[0]; } break; } default: pagmo_throw(value_error,"Invalid initialization type"); } //compute fitness of individuals (necessary if weight matrix is not symmetric) std::vector<double> fitness(NP, 0); if(!is_sym){ for(size_t i=0; i < NP; i++){ fitness[i] = weights[my_pop[i][Nv-1]][my_pop[i][0]]; for(size_t k=1; k < Nv; k++){ fitness[i] += weights[my_pop[i][k-1]][my_pop[i][k]]; } } } decision_vector tmp_tour(Nv); bool stop; size_t rnd_num, i2, pos1_c1, pos1_c2, pos2_c1, pos2_c2; //pos2_c1 denotes the position of city1 in parent2 double fitness_change, fitness_tmp = 0; //InverOver main loop for(int iter = 0; iter < m_gen; iter++){ for(size_t i1 = 0; i1 < NP; i1++){ fitness_change = 0; tmp_tour = my_pop[i1]; pos1_c1 = unif_Nv(); stop = false; while(!stop){ if(unif_01() < m_ri){ rnd_num = unif_Nvless1(); pos1_c2 = (rnd_num == pos1_c1? Nv-1:rnd_num); } else{ i2 = unif_NPless1(); i2 = (i2 == i1? NP-1:i2); pos2_c1 = std::find(my_pop[i2].begin(),my_pop[i2].end(),tmp_tour[pos1_c1])-my_pop[i2].begin(); pos2_c2 = (pos2_c1 == Nv-1? 0:pos2_c1+1); pos1_c2 = std::find(tmp_tour.begin(),tmp_tour.end(),my_pop[i2][pos2_c2])-tmp_tour.begin(); } stop = (abs(pos1_c1-pos1_c2)==1 || abs(pos1_c1-pos1_c2)==Nv-1); if(!stop){ if(pos1_c1<pos1_c2){ for(size_t l=0; l < (double (pos1_c2-pos1_c1-1)/2); l++){ std::swap(tmp_tour[pos1_c1+1+l],tmp_tour[pos1_c2-l]);} if(is_sym){ fitness_change -= weights[tmp_tour[pos1_c1]][tmp_tour[pos1_c2]] + weights[tmp_tour[pos1_c1+1]][tmp_tour[pos1_c2+1 - (pos1_c2+1 > Nv-1? Nv:0)]]; fitness_change += weights[tmp_tour[pos1_c1]][tmp_tour[pos1_c1+1]] + weights[tmp_tour[pos1_c2]][tmp_tour[pos1_c2+1 - (pos1_c2+1 > Nv-1? Nv:0)]]; } } else{ //inverts the section from c1 to c2 (see documentation Note3) for(size_t l=0; l < (double (Nv-(pos1_c1-pos1_c2)-1)/2); l++){ std::swap(tmp_tour[pos1_c1+1+l - (pos1_c1+1+l>Nv-1? Nv:0)],tmp_tour[pos1_c2-l + (pos1_c2<l? Nv:0)]);} if(is_sym){ fitness_change -= weights[tmp_tour[pos1_c1]][tmp_tour[pos1_c2]] + weights[tmp_tour[pos1_c1+1 - (pos1_c1+1 > Nv-1? Nv:0)]][tmp_tour[pos1_c2+1]]; fitness_change += weights[tmp_tour[pos1_c1]][tmp_tour[pos1_c1+1 - (pos1_c1+1 > Nv-1? Nv:0)]] + weights[tmp_tour[pos1_c2]][tmp_tour[pos1_c2+1]]; } } pos1_c1 = pos1_c2; //better performance than original Inver-Over (shorter tour in less time) } } //end of while loop (looping over a single indvidual) if(!is_sym){ //compute fitness of the temporary tour fitness_tmp = weights[tmp_tour[Nv-1]][tmp_tour[0]]; for(size_t k=1; k < Nv; k++){ fitness_tmp += weights[tmp_tour[k-1]][tmp_tour[k]]; } fitness_change = fitness_tmp - fitness[i1]; } if(fitness_change < 0){ //replace individual? my_pop[i1] = tmp_tour; if(!is_sym){ fitness[i1] = fitness_tmp; } } } //end of loop over population } //end of loop over generations //change representation of tour for (size_t ii = 0; ii < NP; ii++) { switch( prob->get_encoding() ) { case problem::tsp::FULL: pop.set_x(ii,prob->cities2full(my_pop[ii])); break; case problem::tsp::RANDOMKEYS: pop.set_x(ii,prob->cities2randomkeys(my_pop[ii],pop.get_individual(ii).cur_x)); break; case problem::tsp::CITIES: pop.set_x(ii,my_pop[ii]); break; } } } // end of evolve