void nlopt_qsort_r(void *base_, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *)) { #ifdef HAVE_QSORT_R_damn_it_use_my_own /* Even if we could detect glibc vs. BSD by appropriate macrology, there is no way to make the calls compatible without writing a wrapper for the compar function...screw this. */ qsort_r(base_, nmemb, size, thunk, compar); #else char *base = (char *) base_; if (nmemb < 10) { /* use O(nmemb^2) algorithm for small enough nmemb */ size_t i, j; for (i = 0; i+1 < nmemb; ++i) for (j = i+1; j < nmemb; ++j) if (compar(thunk, base+i*size, base+j*size) > 0) swap(base+i*size, base+j*size, size); } else { size_t i, pivot, npart; /* pick median of first/middle/last elements as pivot */ { const char *a = base, *b = base + (nmemb/2)*size, *c = base + (nmemb-1)*size; pivot = compar(thunk,a,b) < 0 ? (compar(thunk,b,c) < 0 ? nmemb/2 : (compar(thunk,a,c) < 0 ? nmemb-1 : 0)) : (compar(thunk,a,c) < 0 ? 0 : (compar(thunk,b,c) < 0 ? nmemb-1 : nmemb/2)); } /* partition array */ swap(base + pivot*size, base + (nmemb-1) * size, size); pivot = (nmemb - 1) * size; for (i = npart = 0; i < nmemb-1; ++i) if (compar(thunk, base+i*size, base+pivot) <= 0) swap(base+i*size, base+(npart++)*size, size); swap(base+npart*size, base+pivot, size); /* recursive sort of two partitions */ nlopt_qsort_r(base, npart, size, thunk, compar); npart++; /* don't need to sort pivot */ nlopt_qsort_r(base+npart*size, nmemb-npart, size, thunk, compar); } #endif /* !HAVE_QSORT_R */ }
nlopt_result chevolutionarystrategy( unsigned nparameters, /* Number of input parameters */ nlopt_func f, /* Recursive Objective Funtion Call */ void * data_f, /* Data to Objective Function */ const double* lb, /* Lower bound values */ const double* ub, /* Upper bound values */ double* x, /*in: initial guess, out: minimizer */ double* minf, nlopt_stopping* stop, /* nlopt stop condition */ unsigned np, /* Number of Parents */ unsigned no) { /* Number of Offsprings */ /* variables from nlopt */ nlopt_result ret = NLOPT_SUCCESS; double vetor[8]; unsigned i, id, item; int parent1, parent2; unsigned crosspoint; /* crossover parameteres */ int contmutation, totalmutation; /* mutation parameters */ int idoffmutation, paramoffmutation; /* mutation parameters */ Individual * esparents; /* Parents population */ Individual * esoffsprings; /* Offsprings population */ Individual * estotal;/* copy containing Parents and Offsprings pops */ /* It is interesting to maintain the parents and offsprings * populations stablished and sorted; when the final iterations * is achieved, they are ranked and updated. */ /********************************* * controling the population size *********************************/ if (!np) np = 40; if (!no) no = 60; if ((np < 1)||(no<1)) { nlopt_stop_msg(stop, "populations %d, %d are too small", np, no); return NLOPT_INVALID_ARGS; } esparents = (Individual*) malloc(sizeof(Individual) * np); esoffsprings = (Individual*) malloc(sizeof(Individual) * no); estotal = (Individual*) malloc(sizeof(Individual) * (np+no)); if ((!esparents)||(!esoffsprings)||(!estotal)) { free(esparents); free(esoffsprings); free(estotal); return NLOPT_OUT_OF_MEMORY; } for (id=0; id < np; id++) esparents[id].parameters = NULL; for (id=0; id < no; id++) esoffsprings[id].parameters = NULL; /* From here the population is initialized */ /* we don't handle unbounded search regions; this check is unnecessary since it is performed in nlopt_optimize. for (j = 0; j < nparameters; ++j) if (nlopt_isinf(low[j]) || nlopt_isinf(up[j])) return NLOPT_INVALID_ARGS; */ /* main vector of parameters to randcauchy */ vetor[0] = 4; /* ignored */ vetor[3] = 0; vetor[4] = 1; vetor[5] = 10; vetor[6] = 1; vetor[7] = 0; /* ignored */ /************************************** * Initializing parents population **************************************/ for (id=0; id < np; id++) { esparents[id].parameters = (double*) malloc(sizeof(double) * nparameters); if (!esparents[id].parameters) { ret = NLOPT_OUT_OF_MEMORY; goto done; } for (item=0; item<nparameters; item++) { vetor[1] = lb[item]; vetor[2] = ub[item]; vetor[7]=vetor[7]+1; esparents[id].parameters[item] = randcauchy(vetor); } } memcpy(esparents[0].parameters, x, nparameters * sizeof(double)); /************************************** * Initializing offsprings population **************************************/ for (id=0; id < no; id++) { esoffsprings[id].parameters = (double*) malloc(sizeof(double) * nparameters); if (!esoffsprings[id].parameters) { ret = NLOPT_OUT_OF_MEMORY; goto done; } for (item=0; item<nparameters; item++) { vetor[1] = lb[item]; vetor[2] = ub[item]; vetor[7]=vetor[7]+1; esoffsprings[id].parameters[item] = randcauchy(vetor); } } /************************************** * Parents fitness evaluation **************************************/ for (id=0; id < np; id++) { esparents[id].fitness = f(nparameters, esparents[id].parameters, NULL, data_f); estotal[id].fitness = esparents[id].fitness; stop->nevals++; if (*minf > esparents[id].fitness) { *minf = esparents[id].fitness; memcpy(x, esparents[id].parameters, nparameters * sizeof(double)); } if (nlopt_stop_forced(stop)) ret = NLOPT_FORCED_STOP; else if (*minf < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED; else if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED; else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED; if (ret != NLOPT_SUCCESS) goto done; } /************************************** * Main Loop - Generations **************************************/ while (1) { /************************************** * Crossover **************************************/ for (id=0; id < no; id++) { parent1 = nlopt_iurand((int) np); parent2 = nlopt_iurand((int) np); crosspoint = (unsigned) nlopt_iurand((int) nparameters); for (item=0; item < crosspoint; item++) esoffsprings[id].parameters[item] = esparents[parent1].parameters[item]; for (item=crosspoint; item < nparameters; item++) esoffsprings[id].parameters[item] = esparents[parent2].parameters[item]; } /************************************** * Gaussian Mutation **************************************/ totalmutation = (int) ((no * nparameters) / 10); if (totalmutation < 1) totalmutation = 1; for (contmutation=0; contmutation < totalmutation; contmutation++) { idoffmutation = nlopt_iurand((int) no); paramoffmutation = nlopt_iurand((int) nparameters); vetor[1] = lb[paramoffmutation]; vetor[2] = ub[paramoffmutation]; vetor[7] = vetor[7]+contmutation; esoffsprings[idoffmutation].parameters[paramoffmutation] = randcauchy(vetor); } /************************************** * Offsprings fitness evaluation **************************************/ for (id=0; id < no; id++){ /*esoffsprings[id].fitness = (double)fitness(esoffsprings[id].parameters, nparameters,fittype);*/ esoffsprings[id].fitness = f(nparameters, esoffsprings[id].parameters, NULL, data_f); estotal[id+np].fitness = esoffsprings[id].fitness; stop->nevals++; if (*minf > esoffsprings[id].fitness) { *minf = esoffsprings[id].fitness; memcpy(x, esoffsprings[id].parameters, nparameters * sizeof(double)); } if (nlopt_stop_forced(stop)) ret = NLOPT_FORCED_STOP; else if (*minf < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED; else if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED; else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED; if (ret != NLOPT_SUCCESS) goto done; } /************************************** * Individual selection **************************************/ /* all the individuals are copied to one vector to easily identify best solutions */ for (i=0; i < np; i++) estotal[i] = esparents[i]; for (i=0; i < no; i++) estotal[np+i] = esoffsprings[i]; /* Sorting */ nlopt_qsort_r(estotal, no+np, sizeof(Individual), NULL, CompareIndividuals); /* copy after sorting: */ for (i=0; i < no+np; i++) { if (i<np) esparents[i] = estotal[i]; else esoffsprings[i-np] = estotal[i]; } } /* generations loop */ done: for (id=0; id < np; id++) free(esparents[id].parameters); for (id=0; id < no; id++) free(esoffsprings[id].parameters); if (esparents) free(esparents); if (esoffsprings) free(esoffsprings); if (estotal) free(estotal); return ret; }
// Unified alpha-beta and quiescence search int abq(board *b, int alpha, int beta, int ply, int centiply_extension, bool allow_extensions, bool side_to_move_in_check) { if (search_terminate_requested) return 0; // Check for search termination int alpha_orig = alpha; // For use in later TT storage // Retrieve the value from the transposition table, if appropriate evaluation stored; tt_get(b, &stored); if (!e_eq(stored, no_eval) && stored.depth >= ply && use_ttable) { if (stored.type == qexact || stored.type == exact) return stored.score; if (stored.type == qlowerbound || stored.type == lowerbound) alpha = max(alpha, stored.score); else if (stored.type == qupperbound || stored.type == upperbound) beta = min(beta, stored.score); if (alpha >= beta) return stored.score; } // Futility pruning: enter quiescence early if the node is futile if (use_futility_pruning && !side_to_move_in_check && ply == 1) { if (relative_evaluation(b) + frontier_futility_margin < alpha) ply = 0; } else if (use_futility_pruning && !side_to_move_in_check && ply == 2) { if (relative_evaluation(b) + prefrontier_futility_margin < alpha) ply = 0; } bool quiescence = (ply <= 0); // Generate all possible moves for the quiscence search or normal search, and compute the // static evaluation if applicable. move *moves = NULL; int num_available_moves = 0; if (quiescence) moves = board_moves(b, &num_available_moves, true); // Generate only captures else moves = board_moves(b, &num_available_moves, false); // Generate all moves if (quiescence && !use_qsearch) { free(moves); return relative_evaluation(b); // If qsearch is turned off } // Abort if the quiescence search is too deep (currently 45 plies) if (ply < -quiesce_ply_cutoff) { sstats.qnode_aborts++; free(moves); return relative_evaluation(b); } int quiescence_stand_pat; // Allow the quiescence search to generate cutoffs if (quiescence) { quiescence_stand_pat = relative_evaluation(b); alpha = max(alpha, quiescence_stand_pat); if (alpha >= beta) { free(moves); return quiescence_stand_pat; } } else if (!e_eq(stored, no_eval) && use_tt_move_hueristic) { assert(is_legal_move(b, stored.best)); // TODO // For non-quiescence search, use the TT entry as a hueristic moves[num_available_moves] = stored.best; num_available_moves++; } // Update search stats if (quiescence) sstats.qnodes_searched++; else sstats.nodes_searched++; // Search hueristic: sort exchanges using MVV-LVA if (quiescence && mvvlva) nlopt_qsort_r(moves, num_available_moves, sizeof(move), b, &capture_move_comparator); // Search extensions bool no_more_extensions = false; // Extend the search if we are in check //coord king_loc = b->black_to_move ? b->black_king : b->white_king; bool currently_in_check = side_to_move_in_check; //in_check(b, king_loc.col, king_loc.row, b->black_to_move); if (check_extend && currently_in_check && ply <= check_extend_threshold && !quiescence && allow_extensions) { // only extend in shallow non-quiescence situations centiply_extension += check_extension_centiply; no_more_extensions = true; } // Process any extensions if (allow_extensions && centiply_extension >= 100) { centiply_extension -= 100; ply += 1; } else if (allow_extensions && centiply_extension <= -100) { centiply_extension += 100; ply -= 1; } if (no_more_extensions) allow_extensions = false; // Only allow one check extension move best_move_yet = no_move; int best_score_yet = NEG_INFINITY; int num_moves_actually_examined = 0; // We might end up in checkmate //for (int iterations = 0; iterations < 2; iterations++) { // ABDADA iterations for (int i = num_available_moves - 1; i >= 0; i--) { // Iterate backwards to match MVV-LVA sort order /*int claimed_node_id = -1; if (i != num_available_moves - 1 && iterations == 1) { // Skip redundant young brothers on the first pass if (!tt_try_to_claim_node(b, &claimed_node_id)) continue; // Skip the node if it is already being searched } else tt_always_claim_node(b, &claimed_node_id);*/ apply(b, moves[i]); bool we_moved_into_check; // Choose the more efficient version if possible // If we were already in check, we need to do the expensive search if (side_to_move_in_check) { coord king_loc = b->black_to_move ? b->white_king : b->black_king; // for side that just moved we_moved_into_check = in_check(b, king_loc.col, king_loc.row, !(b->black_to_move)); } else we_moved_into_check = puts_in_check(b, moves[i], !b->black_to_move); // Never move into check if (we_moved_into_check) { unapply(b, moves[i]); //tt_unclaim_node(claimed_node_id); continue; } bool opponent_in_check = puts_in_check(b, moves[i], b->black_to_move); /*coord opp_king_loc = b->black_to_move ? b->black_king : b->white_king; bool opponent_in_check = in_check(b, opp_king_loc.col, opp_king_loc.row, (b->black_to_move));*/ int score = -abq(b, -beta, -alpha, ply - 1, centiply_extension, allow_extensions, opponent_in_check); num_moves_actually_examined++; unapply(b, moves[i]); if (score > best_score_yet) { best_score_yet = score; best_move_yet = moves[i]; } alpha = max(alpha, best_score_yet); if (alpha >= beta) { //tt_unclaim_node(claimed_node_id); break; } //tt_unclaim_node(claimed_node_id); } //} free(moves); // We are done with the array // We have no available moves (or captures) that don't leave us in check // This means checkmate or stalemate in normal search // It might mean no captures are available in quiescence search if (num_moves_actually_examined == 0) { if (quiescence) return quiescence_stand_pat; // TODO: qsearch doesn't understand stalemate or checkmate // This seems paradoxical, but the +1 is necessary so we pick some move in case of checkmate if (currently_in_check) return NEG_INFINITY + 1; // checkmate else return 0; // stalemate } if (quiescence && best_score_yet < quiescence_stand_pat) return quiescence_stand_pat; // TODO experimental stand pat if (search_terminate_requested) return 0; // Search termination preempts tt_put // Record the selected move in the transposition table evaltype type; if (best_score_yet <= alpha_orig) type = (quiescence) ? qupperbound : upperbound; else if (best_score_yet >= beta) type = (quiescence) ? qlowerbound : lowerbound; else type = (quiescence) ? qexact : exact; evaluation eval = {.best = best_move_yet, .score = best_score_yet, .type = type, .depth = ply}; tt_put(b, eval); return best_score_yet; }
static void sort_fv(int n, double *fv, int *isort) { int i; for (i = 0; i < n; ++i) isort[i] = i; nlopt_qsort_r(isort, (unsigned) n, sizeof(int), fv, sort_fv_compare); }
nlopt_result isres_minimize(int n, nlopt_func f, void *f_data, int m, nlopt_constraint *fc, /* fc <= 0 */ int p, nlopt_constraint *h, /* h == 0 */ const double *lb, const double *ub, /* bounds */ double *x, /* in: initial guess, out: minimizer */ double *minf, nlopt_stopping *stop, int population) /* pop. size (= 0 for default) */ { const double ALPHA = 0.2; /* smoothing factor from paper */ const double GAMMA = 0.85; /* step-reduction factor from paper */ const double PHI = 1.0; /* expected rate of convergence, from paper */ const double PF = 0.45; /* fitness probability, from paper */ const double SURVIVOR = 1.0/7.0; /* survivor fraction, from paper */ int survivors; nlopt_result ret = NLOPT_SUCCESS; double *sigmas = 0, *xs; /* population-by-n arrays (row-major) */ double *fval; /* population array of function vals */ double *penalty; /* population array of penalty vals */ double *x0; int *irank = 0; int k, i, j, c; int mp = m + p; double minf_penalty = HUGE_VAL, minf_gpenalty = HUGE_VAL; double taup, tau; double *results = 0; /* scratch space for mconstraint results */ unsigned ires; *minf = HUGE_VAL; if (!population) population = 20 * (n + 1); if (population < 1) return NLOPT_INVALID_ARGS; survivors = ceil(population * SURVIVOR); taup = PHI / sqrt((double) 2*n); tau = PHI / sqrt((double) 2*sqrt((double) n)); /* we don't handle unbounded search regions */ for (j = 0; j < n; ++j) if (nlopt_isinf(lb[j]) || nlopt_isinf(ub[j])) return NLOPT_INVALID_ARGS; ires = imax2(nlopt_max_constraint_dim(m, fc), nlopt_max_constraint_dim(p, h)); results = (double *) malloc(ires * sizeof(double)); if (ires > 0 && !results) return NLOPT_OUT_OF_MEMORY; sigmas = (double*) malloc(sizeof(double) * (population*n*2 + population + population + n)); if (!sigmas) { free(results); return NLOPT_OUT_OF_MEMORY; } xs = sigmas + population*n; fval = xs + population*n; penalty = fval + population; x0 = penalty + population; irank = (int*) malloc(sizeof(int) * population); if (!irank) { ret = NLOPT_OUT_OF_MEMORY; goto done; } for (k = 0; k < population; ++k) { for (j = 0; j < n; ++j) { sigmas[k*n+j] = (ub[j] - lb[j]) / sqrt((double) n); xs[k*n+j] = nlopt_urand(lb[j], ub[j]); } } memcpy(xs, x, sizeof(double) * n); /* use input x for xs_0 */ while (1) { /* each loop body = one generation */ int all_feasible = 1; /* evaluate f and constraint violations for whole population */ for (k = 0; k < population; ++k) { int feasible = 1; double gpenalty; stop->nevals++; fval[k] = f(n, xs + k*n, NULL, f_data); if (nlopt_stop_forced(stop)) { ret = NLOPT_FORCED_STOP; goto done; } penalty[k] = 0; for (c = 0; c < m; ++c) { /* inequality constraints */ nlopt_eval_constraint(results, NULL, fc + c, n, xs + k*n); if (nlopt_stop_forced(stop)) { ret = NLOPT_FORCED_STOP; goto done; } for (ires = 0; ires < fc[c].m; ++ires) { double gval = results[ires]; if (gval > fc[c].tol[ires]) feasible = 0; if (gval < 0) gval = 0; penalty[k] += gval*gval; } } gpenalty = penalty[k]; for (c = m; c < mp; ++c) { /* equality constraints */ nlopt_eval_constraint(results, NULL, h + (c-m), n, xs + k*n); if (nlopt_stop_forced(stop)) { ret = NLOPT_FORCED_STOP; goto done; } for (ires = 0; ires < h[c-m].m; ++ires) { double hval = results[ires]; if (fabs(hval) > h[c-m].tol[ires]) feasible = 0; penalty[k] += hval*hval; } } if (penalty[k] > 0) all_feasible = 0; /* convergence criteria (FIXME: improve?) */ /* FIXME: with equality constraints, how do we decide which solution is the "best" so far? ... need some total order on the solutions? */ if ((penalty[k] <= minf_penalty || feasible) && (fval[k] <= *minf || minf_gpenalty > 0) && ((feasible ? 0 : penalty[k]) != minf_penalty || fval[k] != *minf)) { if (fval[k] < stop->minf_max && feasible) ret = NLOPT_MINF_MAX_REACHED; else if (!nlopt_isinf(*minf)) { if (nlopt_stop_f(stop, fval[k], *minf) && nlopt_stop_f(stop, feasible ? 0 : penalty[k], minf_penalty)) ret = NLOPT_FTOL_REACHED; else if (nlopt_stop_x(stop, xs+k*n, x)) ret = NLOPT_XTOL_REACHED; } memcpy(x, xs+k*n, sizeof(double)*n); *minf = fval[k]; minf_penalty = feasible ? 0 : penalty[k]; minf_gpenalty = feasible ? 0 : gpenalty; if (ret != NLOPT_SUCCESS) goto done; } if (nlopt_stop_forced(stop)) ret = NLOPT_FORCED_STOP; else if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED; else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED; if (ret != NLOPT_SUCCESS) goto done; } /* "selection" step: rank the population */ for (k = 0; k < population; ++k) irank[k] = k; if (all_feasible) /* special case: rank by objective function */ nlopt_qsort_r(irank, population, sizeof(int), fval,key_compare); else { /* Runarsson & Yao's stochastic ranking of the population */ for (i = 0; i < population; ++i) { int swapped = 0; for (j = 0; j < population-1; ++j) { double u = nlopt_urand(0,1); if (u < PF || (penalty[irank[j]] == 0 && penalty[irank[j+1]] == 0)) { if (fval[irank[j]] > fval[irank[j+1]]) { int irankj = irank[j]; irank[j] = irank[j+1]; irank[j+1] = irankj; swapped = 1; } } else if (penalty[irank[j]] > penalty[irank[j+1]]) { int irankj = irank[j]; irank[j] = irank[j+1]; irank[j+1] = irankj; swapped = 1; } } if (!swapped) break; } } /* evolve the population: differential evolution for the best survivors, and standard mutation of the best survivors for the rest: */ for (k = survivors; k < population; ++k) { /* standard mutation */ double taup_rand = taup * nlopt_nrand(0,1); int rk = irank[k], ri; i = k % survivors; ri = irank[i]; for (j = 0; j < n; ++j) { double sigmamax = (ub[j] - lb[j]) / sqrt((double) n); sigmas[rk*n+j] = sigmas[ri*n+j] * exp(taup_rand + tau*nlopt_nrand(0,1)); if (sigmas[rk*n+j] > sigmamax) sigmas[rk*n+j] = sigmamax; do { xs[rk*n+j] = xs[ri*n+j] + sigmas[rk*n+j] * nlopt_nrand(0,1); } while (xs[rk*n+j] < lb[j] || xs[rk*n+j] > ub[j]); sigmas[rk*n+j] = sigmas[ri*n+j] + ALPHA*(sigmas[rk*n+j] - sigmas[ri*n+j]); } } memcpy(x0, xs, n * sizeof(double)); for (k = 0; k < survivors; ++k) { /* differential variation */ double taup_rand = taup * nlopt_nrand(0,1); int rk = irank[k]; for (j = 0; j < n; ++j) { double xi = xs[rk*n+j]; if (k+1 < survivors) xs[rk*n+j] += GAMMA * (x0[j] - xs[(k+1)*n+j]); if (k+1 == survivors || xs[rk*n+j] < lb[j] || xs[rk*n+j] > ub[j]) { /* standard mutation for last survivor and for any survivor components that are now outside the bounds */ double sigmamax = (ub[j] - lb[j]) / sqrt((double) n); double sigi = sigmas[rk*n+j]; sigmas[rk*n+j] *= exp(taup_rand + tau*nlopt_nrand(0,1)); if (sigmas[rk*n+j] > sigmamax) sigmas[rk*n+j] = sigmamax; do { xs[rk*n+j] = xi + sigmas[rk*n+j] * nlopt_nrand(0,1); } while (xs[rk*n+j] < lb[j] || xs[rk*n+j] > ub[j]); sigmas[rk*n+j] = sigi + ALPHA * (sigmas[rk*n+j] - sigi); } } } } done: if (irank) free(irank); if (sigmas) free(sigmas); if (results) free(results); return ret; }