SDDSlib
Loading...
Searching...
No Matches
rcds_powell.c
Go to the documentation of this file.
1/**
2 * @file rcds_powell.c
3 * @brief Implementation of the RCDS (Robust Conjugate Direction Search) algorithm.
4 *
5 * This code is translated from XiaoBiao Huang's MATLAB code for the RCDS algorithm.
6 * The RCDS algorithm is used for automated tuning via minimization.
7 *
8 * Reference: X. Huang, et al. Nucl. Instr. Methods, A, 726 (2013) 77-83.
9 */
10
11#include "mdb.h"
12#include <time.h>
13
14#define DEFAULT_MAXEVALS 100
15#define DEFAULT_MAXPASSES 5
16
17#define DEBUG 0
18
19#define RCDS_ABORT 0x0001UL
20static unsigned long rcdsFlags = 0;
21
22/**
23 * @brief Sets or queries the abort flag for the RCDS minimization.
24 *
25 * @param abort If non-zero, sets the abort flag. If zero, queries the abort flag status.
26 * @return Returns 1 if the abort flag is set, 0 otherwise.
27 */
28long rcdsMinAbort(long abort) {
29 if (abort) {
30 /* if zero, then operation is a query */
31 rcdsFlags |= RCDS_ABORT;
32#ifdef DEBUG
33 fprintf(stderr, "rcdsMin abort requested\n");
34#endif
35 }
36 return rcdsFlags & RCDS_ABORT ? 1 : 0;
37}
38
39/* translated from powellmain.m by X. Huang
40 %Powell's method for minimization
41 %use line scan
42 %Input:
43 % func, function handle
44 % xGuess, initial solution
45 % dxGuess, step size
46 % dmat0, initial direction set, default to unit vectors
47 % tol, a small number to define the termination condition, set to 0 to
48 % disable the feature.
49 % target, the target value
50 % maxPasses, maximum number of iteration, default to 100
51 % maxEvaluations, maximum number of function evaluation, default to 1500
52 % dimensions -- number of variables
53 % noise -- function value noise
54 %Output:
55 % xBset, best solution
56 % yReturn, best func(x1)
57 % nevals, number of evaluations
58 %
59 %Created by X. Huang, 2/22/2013
60 %[x1,f1,nf]=powellmain(@func_obj,x0,step,dmat)
61 %[x1,f1,nf]=powellmain(@func_obj,x0,step,dmat,[],100,'noplot')
62 %[x1,f1,nf]=powellmain(@func_obj,x0,step,dmat,0,100,'noplot',2000)
63 %
64 %Reference: X. Huang, et al. Nucl. Instr. Methods, A, 726 (2013) 77-83.
65 %
66 %Disclaimer: The RCDS algorithm or the Matlab RCDS code come with absolutely
67 %NO warranty. The author of the RCDS method and the Matlab RCDS code does not
68 %take any responsibility for any damage to equipments or personnel injury
69 %that may result from the use of the algorithm or the code.
70 %
71*/
72
73void sort_two_arrays(double *x, double *y, long n);
74/*normalize variable values to [0,1] */
75void normalize_variables(double *x0, double *relative_x, double *lowerLimit, double *upperLimit, long dimensions);
76/*compute the variable values from its normalized values */
77void scale_variables(double *x0, double *relative_x, double *lowerLimit, double *upperLimit, long dimensions);
78
79/*static double (*ipower)(double x, long n); */
80static long DIMENSIONS;
81
82long bracketmin(double (*func)(double *x, long *invalid),
83 double *x0, double f0, double *dv, double *lowerLimit, double *upperLimit, long dimensions, double noise, double step, double *a10, double *a20, double **stepList, double **flist, long *nflist, double *xm, double *fm, double *xmin, double *fmin);
84
85long linescan(double (*func)(double *x, long *invalid), double *x0, double f0, double *dv, double *lowerLimit, double *upperLimit, long dimensions, double alo, double ahi, long Np, double *step_list, double *f_list, long n_list, double *xm, double *fm, double *xmin, double *fmin);
86
87long outlier_1d(double *x, long n, double mul_tol, double perlim, long *removed_index);
88
89/**
90 * @brief Performs minimization using the RCDS (Robust Conjugate Direction Search) algorithm.
91 *
92 * This function minimizes the given objective function using the RCDS algorithm, which is based on Powell's method with line scans.
93 *
94 * @param yReturn Pointer to store the best function value found.
95 * @param xBest Pointer to an array to store the best solution found.
96 * @param xGuess Initial guess for the solution (array of size 'dimensions').
97 * @param dxGuess Initial step sizes for each variable (array of size 'dimensions').
98 * @param xLowerLimit Lower bounds for the variables (array of size 'dimensions'). Can be NULL.
99 * @param xUpperLimit Upper bounds for the variables (array of size 'dimensions'). Can be NULL.
100 * @param dmat0 Initial direction set (array of pointers to arrays of size 'dimensions x dimensions'). If NULL, unit vectors are used.
101 * @param dimensions Number of variables (dimensions of the problem).
102 * @param target Target function value to reach. The minimization will stop if this value is reached.
103 * @param tolerance Tolerance for termination condition. If negative, interpreted as fractional tolerance; if positive, interpreted as absolute tolerance.
104 * @param func Objective function to minimize. Should take an array of variables and a pointer to an invalid flag, and return the function value.
105 * @param report Optional reporting function to call after each iteration. Can be NULL.
106 * @param maxEvaluations Maximum number of function evaluations allowed.
107 * @param maxPasses Maximum number of iterations (passes) allowed.
108 * @param noise Estimated noise level in the function value.
109 * @param rcdsStep Initial step size for the line searches.
110 * @param flags Control flags for the algorithm behavior.
111 * @return Number of function evaluations performed, or negative value on error.
112 */
113long rcdsMin(double *yReturn, double *xBest, double *xGuess, double *dxGuess, double *xLowerLimit, double *xUpperLimit, double **dmat0, long dimensions, double target, /* will return if any value is <= this */
114 double tolerance, /* <0 means fractional, >0 means absolute */
115 double (*func)(double *x, long *invalid), void (*report)(double ymin, double *xmin, long pass, long evals, long dims), long maxEvaluations, /*maimum number of funcation evaluation */
116 long maxPasses, /*maximum number of iterations */
117 double noise, double rcdsStep, unsigned long flags) {
118 long i, j, totalEvaluations = 0, inValid = 0, k, pass, Npmin = 6, direction;
119 double *x0 = NULL; /*normalized xGuess */
120 double *dv = NULL, del = 0, f0, step = 0.01, f1, fm, ft, a1, a2, tmp, norm, maxp = 0, tmpf, *tmpx = NULL;
121 double *xm = NULL, *x1 = NULL, *xt = NULL, *ndv = NULL, *dotp = NULL, *x_value = NULL, *xmin = NULL, fmin;
122 double *step_list = NULL, *f_list = NULL, step_init;
123 long n_list;
124
125 rcdsFlags = 0;
126 if (rcdsStep > 0 && rcdsStep < 1)
127 step = rcdsStep;
128
129 if (dimensions <= 0)
130 return (-3);
131 DIMENSIONS = dimensions;
132
133 if (flags & SIMPLEX_VERBOSE_LEVEL1)
134 fprintf(stdout, "rcdsMin dimensions: %ld\n", dimensions);
135
136 x0 = malloc(sizeof(*x0) * dimensions);
137 tmpx = malloc(sizeof(*tmpx) * dimensions);
138 /*normalize xGuess to between 0 and 1; for step unification purpose */
139 normalize_variables(xGuess, x0, xLowerLimit, xUpperLimit, dimensions);
140
141 f0 = (*func)(xGuess, &inValid); /*note that the function evaluation still uses non-normalized */
142 if (inValid) {
143 f0 = DBL_MAX;
144 fprintf(stderr, "error: initial guess is invalid in rcdsMin()\n");
145 free(x0);
146 free(tmpx);
147 return (-3);
148 }
149 totalEvaluations++;
150 if (!dmat0) {
151 dmat0 = malloc(sizeof(*dmat0) * dimensions);
152 for (i = 0; i < dimensions; i++) {
153 dmat0[i] = calloc(dimensions, sizeof(**dmat0));
154 for (j = 0; j < dimensions; j++)
155 if (j == i)
156 dmat0[i][j] = 1;
157 }
158 }
159 if (dxGuess) {
160 step = 0;
161 for (i = 0; i < dimensions; i++) {
162 if (xLowerLimit && xUpperLimit) {
163 step += dxGuess[i] / (xUpperLimit[i] - xLowerLimit[i]);
164 } else
165 step += dxGuess[i];
166 }
167 step /= dimensions;
168 }
169 /* step = 0.01; */
170 if (rcdsStep > 0 && rcdsStep < 1)
171 step = rcdsStep;
172 /*best solution so far */
173 xm = malloc(sizeof(*xm) * dimensions);
174 xmin = malloc(sizeof(*xmin) * dimensions);
175 memcpy(xm, x0, sizeof(*xm) * dimensions);
176 memcpy(xmin, x0, sizeof(*xm) * dimensions);
177 fmin = fm = f0;
178 memcpy(xBest, xGuess, sizeof(*xBest) * dimensions);
179 *yReturn = f0;
180 if (f0 <= target) {
181 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
182 fprintf(stdout, "rcdsMin: target value achieved in initial setup.\n");
183 }
184 if (report)
185 (*report)(f0, xGuess, 0, 1, dimensions);
186 free(tmpx);
187 free(x0);
188 free(xm);
189 free(xmin);
190 free_zarray_2d((void **)dmat0, dimensions, dimensions);
191 return (totalEvaluations);
192 }
193
194 if (maxPasses <= 0)
195 maxPasses = DEFAULT_MAXPASSES;
196
197 x1 = tmalloc(sizeof(*x1) * dimensions);
198 xt = tmalloc(sizeof(*xt) * dimensions);
199 ndv = tmalloc(sizeof(*ndv) * dimensions);
200 dotp = tmalloc(sizeof(*dotp) * dimensions);
201 for (i = 0; i < dimensions; i++)
202 dotp[i] = 0;
203
204 if (!x_value)
205 x_value = tmalloc(sizeof(*x_value) * dimensions);
206
207 if (flags & SIMPLEX_VERBOSE_LEVEL1) {
208 fprintf(stdout, "rcdsMin: starting conditions:\n");
209 for (direction = 0; direction < dimensions; direction++)
210 fprintf(stdout, "direction %ld: guess=%le \n", direction, xGuess[direction]);
211 fprintf(stdout, "starting funcation value %le \n", f0);
212 }
213 pass = 0;
214 while (pass < maxPasses && !(rcdsFlags & RCDS_ABORT)) {
215 step = step / 1.2;
216 step_init = step;
217 k = 0;
218 del = 0;
219 for (i = 0; !(rcdsFlags & RCDS_ABORT) && i < dimensions; i++) {
220 dv = dmat0[i];
221 if (flags & SIMPLEX_VERBOSE_LEVEL1)
222 fprintf(stdout, "begin iteration %ld, var %ld, nf=%ld\n", pass + 1, i + 1, totalEvaluations);
223 if (step_list) {
224 free(step_list);
225 step_list = NULL;
226 }
227 if (f_list) {
228 free(f_list);
229 f_list = NULL;
230 }
231 totalEvaluations += bracketmin(func, xm, fm, dv, xLowerLimit, xUpperLimit, dimensions, noise, step_init, &a1, &a2, &step_list, &f_list, &n_list, x1, &f1, xmin, &fmin);
232 memcpy(tmpx, x1, sizeof(*tmpx) * dimensions);
233 tmpf = f1;
234 if (flags & SIMPLEX_VERBOSE_LEVEL1)
235 fprintf(stdout, "\niter %ld, dir (var) %ld: begin linescan %ld\n", pass + 1, i + 1, totalEvaluations);
236 if (rcdsFlags & RCDS_ABORT)
237 break;
238 totalEvaluations += linescan(func, tmpx, tmpf, dv, xLowerLimit, xUpperLimit, dimensions, a1, a2, Npmin, step_list, f_list, n_list, x1, &f1, xmin, &fmin);
239 /*direction with largest decrease */
240 if ((fm - f1) > del) {
241 del = fm - f1;
242 k = i;
243 if (flags & SIMPLEX_VERBOSE_LEVEL1)
244 fprintf(stdout, "iteration %ld, var %ld: del= %f updated", pass + 1, i + 1, del);
245 }
246 if (flags & SIMPLEX_VERBOSE_LEVEL1)
247 fprintf(stdout, "iteration %ld, director %ld done, fm=%f, f1=%f\n", pass + 1, i + 1, fm, f1);
248
249 if (flags & RCDS_USE_MIN_FOR_BRACKET) {
250 fm = fmin;
251 memcpy(xm, xmin, sizeof(*xm) * dimensions);
252 } else {
253 fm = f1;
254 memcpy(xm, x1, sizeof(*xm) * dimensions);
255 }
256 }
257 if (flags & SIMPLEX_VERBOSE_LEVEL1)
258 fprintf(stderr, "\niteration %ld, fm=%f fmin=%f\n", pass + 1, fm, fmin);
259 if (rcdsFlags & RCDS_ABORT)
260 break;
261 inValid = 0;
262 for (i = 0; i < dimensions; i++) {
263 xt[i] = 2 * xm[i] - x0[i];
264 if (fabs(xt[i]) > 1) {
265 inValid = 1;
266 break;
267 }
268 }
269 if (!inValid) {
270 scale_variables(x_value, xt, xLowerLimit, xUpperLimit, dimensions);
271 ft = (*func)(x_value, &inValid);
272 totalEvaluations++;
273 }
274 if (inValid)
275 ft = DBL_MAX;
276 tmp = 2 * (f0 - 2 * fm + ft) * pow((f0 - fm - del) / (ft - f0), 2);
277 if ((f0 <= ft) || tmp >= del) {
278 if (flags & SIMPLEX_VERBOSE_LEVEL1)
279 fprintf(stdout, "dir %ld not replaced, %d, %d\n", k, f0 <= ft, tmp >= del);
280 } else {
281 /*compute norm of xm - x0 */
282 if (flags & SIMPLEX_VERBOSE_LEVEL1)
283 fprintf(stdout, "compute dotp\n");
284 norm = 0;
285 for (i = 0; i < dimensions; i++) {
286 norm += (xm[i] - x0[i]) * (xm[i] - x0[i]);
287 }
288 norm = pow(norm, 0.5);
289 for (i = 0; i < dimensions; i++)
290 ndv[i] = (xm[i] - x0[i]) / norm;
291 maxp = 0;
292 for (i = 0; i < dimensions; i++) {
293 dv = dmat0[i];
294 dotp[i] = 0;
295 for (j = 0; j < dimensions; j++)
296 dotp[i] += ndv[j] * dv[j];
297 dotp[i] = fabs(dotp[i]);
298 if (dotp[i] > maxp)
299 maxp = dotp[i];
300 }
301 if (maxp < 0.9) {
302 if (flags & SIMPLEX_VERBOSE_LEVEL1)
303 fprintf(stdout, "max dot product <0.9, do bracketmin and linescan...\n");
304 if (k < dimensions - 1) {
305 for (i = k; i < dimensions - 1; i++) {
306 for (j = 0; j < dimensions; j++)
307 dmat0[i][j] = dmat0[i + 1][j];
308 }
309 }
310 for (j = 0; j < dimensions; j++)
311 dmat0[dimensions - 1][j] = ndv[j];
312 dv = dmat0[dimensions - 1];
313 totalEvaluations += bracketmin(func, xm, fm, dv, xLowerLimit, xUpperLimit, dimensions, noise, step, &a1, &a2, &step_list, &f_list, &n_list, x1, &f1, xmin, &fmin);
314
315 memcpy(tmpx, x1, sizeof(*tmpx) * dimensions);
316 tmpf = f1;
317 totalEvaluations += linescan(func, tmpx, tmpf, dv, xLowerLimit, xUpperLimit, dimensions, a1, a2, Npmin, step_list, f_list, n_list, x1, &f1, xmin, &fmin);
318 memcpy(xm, x1, sizeof(*xm) * dimensions);
319 fm = f1;
320 if (flags & SIMPLEX_VERBOSE_LEVEL1)
321 fprintf(stderr, "fm=%le \n", fm);
322 } else {
323 if (flags & SIMPLEX_VERBOSE_LEVEL1)
324 fprintf(stdout, " , skipped new direction %ld, max dot product %f\n", k, maxp);
325 }
326 }
327 /*termination */
328 if (totalEvaluations > maxEvaluations) {
329 fprintf(stderr, "Terminated, reaching function evaluation limit %ld > %ld\n", totalEvaluations, maxEvaluations);
330 break;
331 }
332 if (2.0 * fabs(f0 - fmin) < tolerance * (fabs(f0) + fabs(fmin)) && tolerance > 0) {
333 if (flags & SIMPLEX_VERBOSE_LEVEL1)
334 fprintf(stdout, "Reach tolerance, terminated, f0=%le, fmin=%le, f0-fmin=%le\n", f0, fmin, f0 - fmin);
335 break;
336 }
337 if (fmin <= target) {
338 if (flags & SIMPLEX_VERBOSE_LEVEL1)
339 fprintf(stdout, "Reach target, terminated, fm=%le, target=%le\n", fm, target);
340 break;
341 }
342 f0 = fm;
343 memcpy(x0, xm, sizeof(*x0) * dimensions);
344 pass++;
345 }
346
347 /*x1, f1 best solution */
348 scale_variables(xBest, xmin, xLowerLimit, xUpperLimit, dimensions);
349 *yReturn = fmin;
350
351 free(x0);
352 free(xm);
353 free_zarray_2d((void **)dmat0, dimensions, dimensions);
354 free(x1);
355 free(xt);
356 free(ndv);
357 free(dotp);
358 free(f_list);
359 f_list = NULL;
360 free(step_list);
361 step_list = NULL;
362 free(tmpx);
363 free(x_value);
364 return (totalEvaluations);
365}
366
367/* translated from bracket.m by X. Huang
368 %bracket the minimum along the line with unit direction dv
369 %Input:
370 % func, function handle, f=func(x)
371 % x0, Npx1 vec, initial point, func(x0+alpha*dv)
372 % f0, f0=func(x0), provided so that no need to re-evaluate, can be NaN
373 % or [], to be evaluated.
374 % dv, Npx1 vec, the unit vector for a direction in the parameter space
375 % step, initial stepsize of alpha,
376 %Output:
377 % xm, fm, the best solution and its value
378 % a1,a2, values of alpha that satisfy f(xm+a1*dv)>fmin and
379 % f(xm+a2*dv)>fmin
380 % xflist, Nx2, all tried solutions
381 % nf, number of function evluations
382 %created by X. Huang, 1/25/2013
383 %
384
385*/
386
387long bracketmin(double (*func)(double *x, long *invalid),
388 double *x0, double f0, double *dv, double *lowerLimit, double *upperLimit, long dimensions,
389 double noise, double step, double *a10, double *a20, double **stepList, double **fList,
390 long *nflist, double *xm, double *fm, double *xmin, double *fmin) {
391 long nf = 0, inValid, i, n_list, count;
392 static double *x1 = NULL, *x2 = NULL, gold_r = 1.618034;
393 double *step_list = NULL, *f_list = NULL;
394 double f1, step_init, am, a1, a2, f2, tmp, step0;
395 static double *x_value = NULL;
396
397 *fm = f0;
398 memcpy(xm, x0, sizeof(*xm) * dimensions);
399 am = 0;
400
401 if (!x1)
402 x1 = malloc(sizeof(*x1) * dimensions);
403 if (!f_list)
404 f_list = malloc(sizeof(*f_list) * 100);
405 if (!step_list)
406 step_list = malloc(sizeof(*step_list) * 100);
407 n_list = 0;
408 step_list[0] = 0;
409 f_list[0] = f0;
410 n_list++;
411 step_init = step;
412 inValid = 0;
413 for (i = 0; i < dimensions; i++) {
414 x1[i] = x0[i] + dv[i] * step;
415 if (fabs(x1[i]) > 1) {
416 inValid = 1;
417 break;
418 }
419 }
420 if (!x_value)
421 x_value = malloc(sizeof(*x_value) * dimensions);
422
423 if (inValid)
424 f1 = DBL_MAX;
425 else {
426 scale_variables(x_value, x1, lowerLimit, upperLimit, dimensions);
427 /*need scale variable values before calling the function */
428 f1 = (*func)(x_value, &inValid);
429 nf++;
430 if (inValid)
431 f1 = DBL_MAX;
432 }
433
434 f_list[n_list] = f1;
435 step_list[n_list] = step;
436 n_list++;
437
438 if (f1 < *fm) {
439 *fm = f1;
440 memcpy(xm, x1, sizeof(*xm) * dimensions);
441 am = step;
442 }
443 if (f1 < *fmin) {
444 *fmin = f1;
445 memcpy(xmin, x1, sizeof(*xmin) * dimensions);
446 }
447 count = 0;
448 while (f1 < *fm + noise * 3 && !(rcdsFlags & RCDS_ABORT)) {
449 step0 = step;
450 /*maximum step 0.1 */
451 if (fabs(step) < 0.1)
452 step = step * (1.0 + gold_r);
453 else
454 step = step + 0.01;
455
456 inValid = 0;
457 for (i = 0; i < dimensions; i++) {
458 x1[i] = x0[i] + dv[i] * step;
459 if (fabs(x1[i]) > 1) {
460 inValid = 1;
461 break;
462 }
463 }
464 if (inValid) {
465 f1 = DBL_MAX;
466 } else {
467 scale_variables(x_value, x1, lowerLimit, upperLimit, dimensions);
468 f1 = (*func)(x_value, &inValid);
469 if (inValid)
470 f1 = DBL_MAX;
471 nf++;
472 }
473 if (n_list > 100) {
474 f_list = trealloc(f_list, sizeof(*f_list) * (n_list + 1));
475 step_list = trealloc(step_list, sizeof(*step_list) * (n_list + 1));
476 }
477 if (inValid) {
478 step = step0; /*get the last vaild solution */
479 break;
480 }
481 f_list[n_list] = f1;
482 step_list[n_list] = step;
483 n_list++;
484 if (f1 < *fm) {
485 *fm = f1;
486 am = step;
487 memcpy(xm, x1, sizeof(*xm) * dimensions);
488 }
489 if (f1 < *fmin) {
490 *fmin = f1;
491 memcpy(xmin, x1, sizeof(*xmin) * dimensions);
492 }
493 count++;
494 }
495 a2 = step;
496 if (f0 > *fm + noise * 3) {
497 a1 = 0;
498 a1 = a1 - am;
499 a2 = a2 - am;
500 for (i = 0; i < n_list; i++)
501 step_list[i] -= am;
502 free(x1);
503 x1 = NULL;
504 free(x2);
505 x2 = NULL;
506 *a10 = a1;
507 *a20 = a2;
508
509 *fList = f_list;
510 *stepList = step_list;
511 *nflist = n_list;
512 *a10 = a1;
513 *a20 = a2;
514 free(x1);
515 x1 = NULL;
516 free(x2);
517 x2 = NULL;
518 return nf;
519 }
520
521 if (!x2)
522 x2 = malloc(sizeof(*x2) * dimensions);
523 /*go to negative direction */
524 step = -1 * step_init;
525 inValid = 0;
526 for (i = 0; i < dimensions; i++) {
527 x2[i] = x0[i] + dv[i] * step;
528 if (fabs(x2[i]) > 1) {
529 inValid = 1;
530 break;
531 }
532 }
533 if (inValid)
534 f2 = DBL_MAX;
535 else {
536 scale_variables(x_value, x2, lowerLimit, upperLimit, dimensions);
537 f2 = (*func)(x_value, &inValid);
538 if (inValid)
539 f2 = DBL_MAX;
540 nf++;
541 }
542 if (n_list > 100) {
543 f_list = trealloc(f_list, sizeof(*f_list) * (n_list + 1));
544 step_list = trealloc(step_list, sizeof(*step_list) * (n_list + 1));
545 }
546 f_list[n_list] = f2;
547 step_list[n_list] = step;
548 n_list++;
549
550 if (f2 < *fm) {
551 *fm = f2;
552 am = step;
553 memcpy(xm, x2, sizeof(*xm) * dimensions);
554 }
555 if (f2 < *fmin) {
556 *fmin = f2;
557 memcpy(xmin, x2, sizeof(*xmin) * dimensions);
558 }
559 count = 0;
560 while (f2 < *fm + noise * 3 && !(rcdsFlags & RCDS_ABORT)) {
561 step0 = step;
562 if (fabs(step) < 0.1)
563 step = step * (1.0 + gold_r);
564 else
565 step = step - 0.01;
566 inValid = 0;
567 for (i = 0; i < dimensions; i++) {
568 x2[i] = x0[i] + dv[i] * step;
569 if (fabs(x2[i]) > 1) {
570 inValid = 1;
571 break;
572 }
573 }
574 if (inValid) {
575 f2 = DBL_MAX;
576 } else {
577 scale_variables(x_value, x2, lowerLimit, upperLimit, dimensions);
578 f2 = (*func)(x_value, &inValid);
579 if (inValid)
580 f2 = DBL_MAX;
581 nf++;
582 }
583 if (inValid) {
584 /* get the last valid solution */
585 step = step0;
586 break;
587 }
588
589 if (n_list > 100) {
590 f_list = trealloc(f_list, sizeof(*f_list) * (n_list + 1));
591 step_list = trealloc(step_list, sizeof(*step_list) * (n_list + 1));
592 }
593 f_list[n_list] = f2;
594 step_list[n_list] = step;
595 n_list++;
596 count++;
597 if (f2 < *fm) {
598 *fm = f2;
599 am = step;
600 memcpy(xm, x2, sizeof(*xm) * dimensions);
601 }
602 if (f2 < *fmin) {
603 *fmin = f2;
604 memcpy(xmin, x2, sizeof(*xmin) * dimensions);
605 }
606 }
607
608 a1 = step;
609 if (a1 > a2) {
610 tmp = a1;
611 a1 = a2;
612 a2 = tmp;
613 }
614 a1 = a1 - am;
615 a2 = a2 - am;
616 for (i = 0; i < n_list; i++)
617 step_list[i] -= am;
618
619 sort_two_arrays(step_list, f_list, n_list);
620 /******/
621 /*move linescan here instead of powellMain so that no need to return step_list and f_list */
622
623 *stepList = step_list;
624 *fList = f_list;
625 *nflist = n_list;
626
627 free(x1);
628 x1 = NULL;
629 free(x2);
630 x2 = NULL;
631 *a10 = a1;
632 *a20 = a2;
633 return nf;
634}
635
636void scale_variables(double *x0, double *relative_x, double *lowerLimit, double *upperLimit, long dimensions) {
637 long i;
638 if (lowerLimit && upperLimit) {
639 for (i = 0; i < dimensions; i++) {
640 x0[i] = relative_x[i] * (upperLimit[i] - lowerLimit[i]) + lowerLimit[i];
641 }
642 } else {
643 memcpy(x0, relative_x, sizeof(*x0) * dimensions);
644 }
645}
646
647void normalize_variables(double *x0, double *relative_x, double *lowerLimit, double *upperLimit, long dimensions) {
648 long i;
649 if (lowerLimit && upperLimit) {
650 for (i = 0; i < dimensions; i++) {
651 relative_x[i] = (x0[i] - lowerLimit[i]) / (upperLimit[i] - lowerLimit[i]);
652 }
653 } else
654 memcpy(relative_x, x0, sizeof(*relative_x) * dimensions);
655}
656
657/* transfered from X. Huang's matlab code linescan.m
658 %line scan in the parameter space along a direction dv
659 %Input:
660 % func, function handle, f=func(x)
661 % x0, Npx1 vec, initial point
662 % f0, f0=func(x0), provided so that no need to re-evaluate, can be NaN
663 % or [], to be evaluated.
664 % dv, Npx1 vec, the unit vector for a direction in the parameter space
665 % alo, ahi, scalar, the low and high bound of alpha, for f=func(x0+a dv)
666 % Np, minimum number of points for fitting.
667 % xflist, Nx2, known solutions
668 %Output:
669 % xm, the new solution
670 % fm, the value at the new solution, f1=func(x1)
671 % nf, the number of function evaluations
672 %Created by X. Huang, 1/25/2013
673 %
674
675*/
676
677long linescan(double (*func)(double *x, long *invalid), double *x0, double f0, double *dv, double *lowerLimit, double *upperLimit, long dimensions, double alo, double ahi, long Np, double *step_list, double *f_list, long n_list, double *xm, double *fm, double *xmin, double *fmin) {
678 long nf = 0, i, j, k, MP, n_new, terms, *order;
679 long inValid;
680 int64_t imin, imax;
681 long *is_outlier, outliers;
682 double a1, f1, tmp_min, tmp_max, tmp, delta, delta2, mina;
683 static double *x1 = NULL, *aNew = NULL, *fNew = NULL, *av = NULL, *x_value = NULL;
684 double *coef, *coefSigma, chi, *diff, *tmpa = NULL, *tmpf = NULL, *fv = NULL;
685
686 if (alo >= ahi) {
687 fprintf(stderr, "high bound should be larger than the low bound\n");
688 return 0;
689 }
690 if (Np < 6)
691 Np = 6;
692 delta = (ahi - alo) / (Np - 1);
693 delta2 = delta / 2.0;
694 if (!x1)
695 x1 = malloc(sizeof(*x1) * dimensions);
696 /*add interpolation points to eveanly space */
697 n_new = 0;
698 if (!aNew)
699 aNew = malloc(sizeof(*aNew) * Np);
700 if (!fNew)
701 fNew = malloc(sizeof(*fNew) * Np);
702 if (!x_value)
703 x_value = malloc(sizeof(*x_value) * dimensions);
704
705 for (i = 0; i < Np && !(rcdsFlags & RCDS_ABORT); i++) {
706 a1 = alo + delta * i;
707 mina = fabs(a1 - step_list[0]);
708 for (j = 1; j < n_list; j++) {
709 tmp = fabs(a1 - step_list[j]);
710 if (tmp < mina) {
711 mina = fabs(a1 - step_list[j]);
712 }
713 }
714 /*remove points which mian<=delta/2.0, keep the insert points if mina>delta/2.0 */
715 /*added a small value 1.0e-16, to keep the point that close to delta/2.0 */
716 if (mina + 1.0e-16 > delta2) {
717 for (k = 0; k < dimensions; k++) {
718 x1[k] = x0[k] + dv[k] * a1;
719 }
720 scale_variables(x_value, x1, lowerLimit, upperLimit, dimensions);
721 f1 = (*func)(x_value, &inValid);
722 nf++;
723 if (f1 < *fmin) {
724 *fmin = f1;
725 memcpy(xmin, x1, sizeof(*xmin) * dimensions);
726 }
727 if (inValid) {
728 f1 = DBL_MAX;
729 } else {
730 aNew[n_new] = a1;
731 fNew[n_new] = f1;
732 n_new++;
733 }
734 }
735 }
736 if (rcdsFlags & RCDS_ABORT) {
737 if (x1)
738 free(x1);
739 x1 = NULL;
740 return nf;
741 }
742
743 if (n_new) {
744 /*merge insertion points */
745 if (n_new + n_list > 100) {
746 f_list = trealloc(f_list, sizeof(*f_list) * (n_list + n_new + 1));
747 step_list = trealloc(step_list, sizeof(*step_list) * (n_list + n_new + 1));
748 }
749 for (i = 0; i < n_new; i++) {
750 step_list[n_list + i] = aNew[i];
751 f_list[n_list + i] = fNew[i];
752 }
753 n_list += n_new;
754 }
755
756 if (aNew)
757 free(aNew);
758 aNew = NULL;
759 if (fNew)
760 free(fNew);
761 fNew = NULL;
762
763 /*sort new list after adding inserting points */
764 sort_two_arrays(step_list, f_list, n_list);
765
766 index_min_max(&imin, &imax, f_list, n_list);
767 for (i = 0; i < dimensions; i++)
768 xm[i] = x0[i] + step_list[imin] * dv[i];
769 *fm = f_list[imin];
770 if (*fm < *fmin) {
771 *fmin = *fm;
772 memcpy(xmin, xm, sizeof(*xmin) * dimensions);
773 }
774 if (n_list <= 5)
775 return nf;
776 /*else, do outlier */
777 tmp_min = MAX(step_list[0], step_list[imin] - 6 * delta);
778 tmp_max = MIN(step_list[n_list - 1], step_list[imin] + 6 * delta);
779
780 MP = 101;
781 if (!av)
782 av = tmalloc(sizeof(*av) * MP);
783 for (i = 0; i < MP; i++)
784 av[i] = tmp_min + (tmp_max - tmp_min) * i * 1.0 / MP;
785 fv = tmalloc(sizeof(*fv) * MP);
786
787 /* polynormial fit */
788 terms = 3;
789 coef = tmalloc(sizeof(*coef) * terms);
790 coefSigma = tmalloc(sizeof(*coefSigma) * terms);
791 order = tmalloc(sizeof(*order) * terms);
792 for (i = 0; i < terms; i++)
793 order[i] = i;
794 diff = tmalloc(sizeof(*diff) * n_list);
795
796 lsfp(step_list, f_list, NULL, n_list, terms, order, coef, coefSigma, &chi, diff);
797
798 /*do outlier based on diff */
799 is_outlier = tmalloc(sizeof(*is_outlier) * n_list);
800 for (i = 0; i < n_list; i++)
801 diff[i] = -1.0 * diff[i];
802
803 outliers = outlier_1d(diff, n_list, 3.0, 0.25, is_outlier);
804
805 for (i = 0; i < n_list; i++) {
806 diff[i] = -1 * diff[i];
807 }
808 if (outliers <= 1) {
809 if (outliers == 1) {
810 tmpa = tmalloc(sizeof(*tmpa) * n_list);
811 tmpf = tmalloc(sizeof(*tmpf) * n_list);
812 n_new = 0;
813 for (j = 0; j < n_list; j++)
814 if (!is_outlier[j]) {
815 tmpa[n_new] = step_list[j];
816 tmpf[n_new] = f_list[j];
817 n_new++;
818 }
819
820 lsfp(tmpa, tmpf, NULL, n_new, terms, order, coef, coefSigma, &chi, diff);
821
822 free(tmpf);
823 free(tmpa);
824 }
825 for (i = 0; i < MP; i++) {
826 fv[i] = coef[0] + av[i] * coef[1] + coef[2] * av[i] * av[i];
827 }
828 index_min_max(&imin, &imax, fv, MP);
829 for (i = 0; i < dimensions; i++) {
830 x1[i] = xm[i] = x0[i] + av[imin] * dv[i];
831 }
832 scale_variables(x_value, x1, lowerLimit, upperLimit, dimensions);
833 memcpy(xm, x1, sizeof(*xm) * dimensions);
834
835 f1 = (*func)(x_value, &inValid);
836 nf++;
837 if (inValid)
838 f1 = DBL_MAX;
839 *fm = f1;
840 if (f1 < *fmin) {
841 *fmin = f1;
842 memcpy(xmin, x1, sizeof(*xmin) * dimensions);
843 }
844 } else {
845 /* do nothing, use the minimum result */
846 }
847 if (x1)
848 free(x1);
849 x1 = NULL;
850 free(av);
851 av = NULL;
852 free(coef);
853 coef = NULL;
854 free(coefSigma);
855 coefSigma = NULL;
856 free(order);
857 order = NULL;
858 free(diff);
859 diff = NULL;
860 free(is_outlier);
861 is_outlier = NULL;
862 free(fv);
863 fv = NULL;
864 return nf;
865}
866
867void sort_two_arrays(double *x, double *y, long n) {
868 double *tmpx, *tmpy;
869 long i, j;
870
871 tmpx = malloc(sizeof(*tmpx) * n);
872 memcpy(tmpx, x, sizeof(*tmpx) * n);
873 tmpy = malloc(sizeof(*tmpy) * n);
874
875 qsort(tmpx, n, sizeof(*tmpx), double_cmpasc);
876 for (i = 0; i < n; i++) {
877 for (j = 0; j < n; j++) {
878 if (tmpx[i] == x[j])
879 break;
880 }
881 tmpy[i] = y[j];
882 }
883 for (i = 0; i < n; i++) {
884 y[i] = tmpy[i];
885 x[i] = tmpx[i];
886 }
887 free(tmpx);
888 free(tmpy);
889 return;
890}
891
892/* function [x,indxin,indxout]=outlier1d(x,varargin)
893 %[x,indxin]=outlier1d(x,varargin)
894 %remove the outliers of x from it
895 %x is 1d vector with dim>=3
896 %mul_tol = varargin{1}, default 3.0,
897 %perlim = varargin{2}, default 0.25
898 %The algorithm is: 1. sort x to ascending order. 2. calculate the difference series of the
899 %sorted x. 3. calculate the average difference of the central (1- 2*perlim) part.
900 %4. examine the difference series of the upper and lower perlim (25% by default) part, if
901 %there is a jump that is larger than mul_tol (3 by default) times of the std, remove
902 %the upper or lower part from that point on
903 %
904 %Created by X. Huang in matlab, Nov. 2004
905 %
906*/
907
908long outlier_1d(double *x, long n, double mul_tol, double perlim, long *is_outlier) {
909 long i, j, outlier = 0, *index = NULL, upl, dnl, upcut, dncut;
910 double *tmpx = NULL, *diff = NULL, ave1 = 0, ave2 = 0;
911
912 if (n < 3)
913 return 0;
914 index = tmalloc(sizeof(*index) * n);
915 tmpx = tmalloc(sizeof(*tmpx) * n);
916 diff = tmalloc(sizeof(*diff) * n);
917
918 /*sort data x */
919 memcpy(tmpx, x, sizeof(*tmpx) * n);
920 qsort((void *)tmpx, n, sizeof(*tmpx), double_cmpasc);
921
922 for (i = 0; i < n; i++) {
923 is_outlier[i] = 0;
924 for (j = 0; j < n; j++) {
925 if (tmpx[i] == x[j])
926 break;
927 }
928 index[i] = j;
929 }
930
931 /*length of diff is n-1 */
932 for (i = 1; i < n; i++) {
933 diff[i - 1] = tmpx[i] - tmpx[i - 1];
934 }
935 if (n <= 4) {
936 /*n=3 or n=4; remove lower or upper diff; diff dimension is n-1 */
937 /*average from 0 to n-3 */
938 for (i = 0; i < n - 2; i++)
939 ave1 += diff[i];
940 for (i = 1; i < n - 1; i++)
941 ave2 += diff[i];
942 ave1 /= n - 2;
943 ave2 /= n - 2;
944 if (diff[n - 2] > mul_tol * ave1) {
945 is_outlier[index[n - 2]] = 1;
946 outlier++;
947 }
948 if (diff[0] > mul_tol * ave2) {
949 is_outlier[index[0]] = 1;
950 outlier++;
951 }
952 return outlier;
953 }
954 upl = MAX((int)(n * (1 - perlim)), 3) - 1;
955 dnl = MAX((int)(n * perlim), 2) - 1;
956 ave1 = 0;
957 for (i = dnl; i <= upl; i++)
958 ave1 += diff[i];
959 ave1 /= upl - dnl + 1;
960 upcut = n;
961 dncut = -1;
962
963 outlier = 0;
964 for (i = upl; i < n - 1; i++) {
965 if (diff[i] > mul_tol * ave1) {
966 upcut = i + 1;
967 }
968 }
969
970 for (i = dnl; i >= 0; i--) {
971 if (diff[i] > mul_tol * ave1) {
972 dncut = i;
973 }
974 }
975 for (i = upcut; i < n; i++) {
976 is_outlier[index[i]] = 1;
977 outlier++;
978 }
979 for (i = dncut; i >= 0; i--) {
980 is_outlier[index[i]] = 1;
981 outlier++;
982 }
983 free(tmpx);
984 free(diff);
985 free(index);
986 return outlier;
987}
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
Definition array.c:181
int free_zarray_2d(void **array, uint64_t n1, uint64_t n2)
Frees a 2D array and its associated memory.
Definition array.c:155
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
Definition array.c:59
int index_min_max(int64_t *imin, int64_t *imax, double *list, int64_t n)
Finds the indices of the minimum and maximum values in a list of doubles.
Definition findMinMax.c:116
long rcdsMin(double *yReturn, double *xBest, double *xGuess, double *dxGuess, double *xLowerLimit, double *xUpperLimit, double **dmat0, long dimensions, double target, double tolerance, double(*func)(double *x, long *invalid), void(*report)(double ymin, double *xmin, long pass, long evals, long dims), long maxEvaluations, long maxPasses, double noise, double rcdsStep, unsigned long flags)
Performs minimization using the RCDS (Robust Conjugate Direction Search) algorithm.
long rcdsMinAbort(long abort)
Sets or queries the abort flag for the RCDS minimization.
Definition rcds_powell.c:28
int double_cmpasc(const void *a, const void *b)
Compare two doubles in ascending order.