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