Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
nelder_mead_simplex.h
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016 Tobias Ellinghaus.
4 Copyright (C) 2016 Ulrich Pegelow.
5 Copyright (C) 2017 luzpaz.
6 Copyright (C) 2020 Hubert Kowalski.
7 Copyright (C) 2020 Pascal Obry.
8 Copyright (C) 2022 Martin Bařinka.
9 Copyright (C) 2023, 2025 Aurélien PIERRE.
10
11 darktable is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
15
16 darktable is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with darktable. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25/* For parameter optimization we are using the Nelder-Mead simplex method
26 * implemented by Michael F. Hutt.
27 * Changes versus the original code:
28 * do not include "nmsimplex.h" (not needed)
29 * renamed configuration variables to NMS_*
30 * add additional argument to objfun for arbitrary parameters
31 * simplex() returns number of used iterations instead of min value
32 * maximum number of iterations as function parameter
33 * make interface function simplex() static * initialize i and j to avoid compiler warnings
34 * comment out printing of status inormation
35 * reformat according to darktable's clang standards
36 */
37
38/*==================================================================================
39 * begin nmsimplex code downloaded from http://www.mikehutt.com/neldermead.html
40 * on February 6, 2016
41 *==================================================================================*/
42/*
43 * Program: nmsimplex.c
44 * Author : Michael F. Hutt
45 * http://www.mikehutt.com
46 * 11/3/97
47 *
48 * An implementation of the Nelder-Mead simplex method.
49 *
50 * Copyright (c) 1997-2011 <Michael F. Hutt>
51 *
52 * Permission is hereby granted, free of charge, to any person obtaining
53 * a copy of this software and associated documentation files (the
54 * "Software"), to deal in the Software without restriction, including
55 * without limitation the rights to use, copy, modify, merge, publish,
56 * distribute, sublicense, and/or sell copies of the Software, and to
57 * permit persons to whom the Software is furnished to do so, subject to
58 * the following conditions:
59 *
60 * The above copyright notice and this permission notice shall be
61 * included in all copies or substantial portions of the Software.
62 *
63 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
64 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
65 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
66 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
67 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
68 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
69 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
70 *
71 *
72 * Jan. 6, 1999
73 * Modified to conform to the algorithm presented
74 * in Margaret H. Wright's paper on Direct Search Methods.
75 *
76 * Jul. 23, 2007
77 * Fixed memory leak.
78 *
79 * Mar. 1, 2011
80 * Added constraints.
81 */
82
83#ifndef DT_MATH_NELDER_MEAD_SIMPLEX_H
84#define DT_MATH_NELDER_MEAD_SIMPLEX_H
85
86// Nelder-Mead reflection / contraction / expansion coefficients (the algorithm's standard
87// constants). Guarded so a caller may override them before including this header.
88#ifndef NMS_ALPHA
89#define NMS_ALPHA 1.0
90#endif
91#ifndef NMS_BETA
92#define NMS_BETA 0.5
93#endif
94#ifndef NMS_GAMMA
95#define NMS_GAMMA 2.0
96#endif
97
98// Nelder-Mead simplex minimizer of objfunc over n parameters, starting from start[] (overwritten
99// with the best point). EPSILON = convergence threshold, scale = initial simplex size, maxiter =
100// iteration cap, constrain = optional projection of a trial point onto the feasible set (may be
101// NULL), params = opaque pointer forwarded to objfunc/constrain. Returns the iteration count.
102static int simplex(double (*objfunc)(double[], void *params), double start[], int n, double EPSILON, double scale,
103 int maxiter, void (*constrain)(double[], int n), void *params)
104{
105
106 int vs; /* vertex with smallest value */
107 int vh; /* vertex with next smallest value */
108 int vg; /* vertex with largest value */
109
110 int i = 0, j = 0, m, row;
111 int itr; /* track the number of iterations */
112
113 double **v; /* holds vertices of simplex */
114 double pn, qn; /* values used to create initial simplex */
115 double *f; /* value of function at each vertex */
116 double fr; /* value of function at reflection point */
117 double fe; /* value of function at expansion point */
118 double fc; /* value of function at contraction point */
119 double *vr; /* reflection - coordinates */
120 double *ve; /* expansion - coordinates */
121 double *vc; /* contraction - coordinates */
122 double *vm; /* centroid - coordinates */
123 //double min;
124
125 double fsum, favg, s, cent;
126
127 /* dynamically allocate arrays */
128
129 /* allocate the rows of the arrays */
130 v = (double **)malloc(sizeof(double *) * (n + 1));
131 f = (double *)malloc(sizeof(double) * (n + 1));
132 vr = (double *)malloc(sizeof(double) * n);
133 ve = (double *)malloc(sizeof(double) * n);
134 vc = (double *)malloc(sizeof(double) * n);
135 vm = (double *)malloc(sizeof(double) * n);
136
137 /* allocate the columns of the arrays */
138 for(i = 0; i <= n; i++)
139 {
140 v[i] = (double *)malloc(sizeof(double) * n);
141 }
142
143 /* create the initial simplex */
144 /* assume one of the vertices is 0,0 */
145
146 pn = scale * (sqrt(n + 1) - 1 + n) / (n * sqrt(2));
147 qn = scale * (sqrt(n + 1) - 1) / (n * sqrt(2));
148
149 for(i = 0; i < n; i++)
150 {
151 v[0][i] = start[i];
152 }
153
154 for(i = 1; i <= n; i++)
155 {
156 for(j = 0; j < n; j++)
157 {
158 if(i - 1 == j)
159 {
160 v[i][j] = pn + start[j];
161 }
162 else
163 {
164 v[i][j] = qn + start[j];
165 }
166 }
167 }
168
169 if(!IS_NULL_PTR(constrain))
170 {
171 constrain(v[j], n);
172 }
173 /* find the initial function values */
174 for(j = 0; j <= n; j++)
175 {
176 f[j] = objfunc(v[j], params);
177 }
178
179#if 0
180 /* print out the initial values */
181 printf("Initial Values\n");
182 for(j = 0; j <= n; j++)
183 {
184 for(i = 0; i < n; i++)
185 {
186 printf("%f %f\n", v[j][i], f[j]);
187 }
188 }
189#endif
190
191 /* begin the main loop of the minimization */
192 for(itr = 1; itr <= maxiter; itr++)
193 {
194 /* find the index of the largest value */
195 vg = 0;
196 for(j = 0; j <= n; j++)
197 {
198 if(f[j] > f[vg])
199 {
200 vg = j;
201 }
202 }
203
204 /* find the index of the smallest value */
205 vs = 0;
206 for(j = 0; j <= n; j++)
207 {
208 if(f[j] < f[vs])
209 {
210 vs = j;
211 }
212 }
213
214 /* find the index of the second largest value */
215 vh = vs;
216 for(j = 0; j <= n; j++)
217 {
218 if(f[j] > f[vh] && f[j] < f[vg])
219 {
220 vh = j;
221 }
222 }
223
224 /* calculate the centroid */
225 for(j = 0; j <= n - 1; j++)
226 {
227 cent = 0.0;
228 for(m = 0; m <= n; m++)
229 {
230 if(m != vg)
231 {
232 cent += v[m][j];
233 }
234 }
235 vm[j] = cent / n;
236 }
237
238 /* reflect vg to new vertex vr */
239 for(j = 0; j <= n - 1; j++)
240 {
241 /*vr[j] = (1+NMS_ALPHA)*vm[j] - NMS_ALPHA*v[vg][j];*/
242 vr[j] = vm[j] + NMS_ALPHA * (vm[j] - v[vg][j]);
243 }
244 if(!IS_NULL_PTR(constrain))
245 {
246 constrain(vr, n);
247 }
248 fr = objfunc(vr, params);
249
250 if(fr < f[vh] && fr >= f[vs])
251 {
252 for(j = 0; j <= n - 1; j++)
253 {
254 v[vg][j] = vr[j];
255 }
256 f[vg] = fr;
257 }
258
259 /* investigate a step further in this direction */
260 if(fr < f[vs])
261 {
262 for(j = 0; j <= n - 1; j++)
263 {
264 /*ve[j] = NMS_GAMMA*vr[j] + (1-NMS_GAMMA)*vm[j];*/
265 ve[j] = vm[j] + NMS_GAMMA * (vr[j] - vm[j]);
266 }
267 if(!IS_NULL_PTR(constrain))
268 {
269 constrain(ve, n);
270 }
271 fe = objfunc(ve, params);
272
273 /* by making fe < fr as opposed to fe < f[vs],
274 Rosenbrocks function takes 63 iterations as opposed
275 to 64 when using double variables. */
276
277 if(fe < fr)
278 {
279 for(j = 0; j <= n - 1; j++)
280 {
281 v[vg][j] = ve[j];
282 }
283 f[vg] = fe;
284 }
285 else
286 {
287 for(j = 0; j <= n - 1; j++)
288 {
289 v[vg][j] = vr[j];
290 }
291 f[vg] = fr;
292 }
293 }
294
295 /* check to see if a contraction is necessary */
296 if(fr >= f[vh])
297 {
298 if(fr < f[vg] && fr >= f[vh])
299 {
300 /* perform outside contraction */
301 for(j = 0; j <= n - 1; j++)
302 {
303 /*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/
304 vc[j] = vm[j] + NMS_BETA * (vr[j] - vm[j]);
305 }
306 if(!IS_NULL_PTR(constrain))
307 {
308 constrain(vc, n);
309 }
310 fc = objfunc(vc, params);
311 }
312 else
313 {
314 /* perform inside contraction */
315 for(j = 0; j <= n - 1; j++)
316 {
317 /*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/
318 vc[j] = vm[j] - NMS_BETA * (vm[j] - v[vg][j]);
319 }
320 if(!IS_NULL_PTR(constrain))
321 {
322 constrain(vc, n);
323 }
324 fc = objfunc(vc, params);
325 }
326
327
328 if(fc < f[vg])
329 {
330 for(j = 0; j <= n - 1; j++)
331 {
332 v[vg][j] = vc[j];
333 }
334 f[vg] = fc;
335 }
336 /* at this point the contraction is not successful,
337 we must halve the distance from vs to all the
338 vertices of the simplex and then continue.
339 10/31/97 - modified to account for ALL vertices.
340 */
341 else
342 {
343 for(row = 0; row <= n; row++)
344 {
345 if(row != vs)
346 {
347 for(j = 0; j <= n - 1; j++)
348 {
349 v[row][j] = v[vs][j] + (v[row][j] - v[vs][j]) / 2.0;
350 }
351 }
352 }
353 if(!IS_NULL_PTR(constrain))
354 {
355 constrain(v[vg], n);
356 }
357 f[vg] = objfunc(v[vg], params);
358 if(!IS_NULL_PTR(constrain))
359 {
360 constrain(v[vh], n);
361 }
362 f[vh] = objfunc(v[vh], params);
363 }
364 }
365#if 0
366 /* print out the value at each iteration */
367 printf("Iteration %d\n", itr);
368 for(j = 0; j <= n; j++)
369 {
370 for(i = 0; i < n; i++)
371 {
372 printf("%f %f\n", v[j][i], f[j]);
373 }
374 }
375#endif
376 /* test for convergence */
377 fsum = 0.0;
378 for(j = 0; j <= n; j++)
379 {
380 fsum += f[j];
381 }
382 favg = fsum / (n + 1);
383 s = 0.0;
384 for(j = 0; j <= n; j++)
385 {
386 s += pow((f[j] - favg), 2.0) / (n);
387 }
388 s = sqrt(s);
389 if(s < EPSILON) break;
390 }
391 /* end main loop of the minimization */
392
393 /* find the index of the smallest value */
394 vs = 0;
395 for(j = 0; j <= n; j++)
396 {
397 if(f[j] < f[vs])
398 {
399 vs = j;
400 }
401 }
402#if 0
403 printf("The minimum was found at\n");
404 for(j = 0; j < n; j++)
405 {
406 printf("%e\n", v[vs][j]);
407 start[j] = v[vs][j];
408 }
409 double min = objfunc(v[vs], params);
410 printf("The minimum value is %f\n", min);
411 printf("%d Iterations through program\n", itr);
412#else
413 for(j = 0; j < n; j++)
414 {
415 start[j] = v[vs][j];
416 }
417#endif
418 dt_free(f);
419 dt_free(vr);
420 dt_free(ve);
421 dt_free(vc);
422 dt_free(vm);
423 for(i = 0; i <= n; i++)
424 {
425 dt_free(v[i]);
426 }
427 dt_free(v);
428 return itr;
429}
430
431/*==================================================================================
432 * end of nmsimplex code
433 *==================================================================================*/
434
435#endif // DT_MATH_NELDER_MEAD_SIMPLEX_H
436
437// clang-format off
438// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
439// vim: shiftwidth=2 expandtab tabstop=2 cindent
440// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
441// clang-format on
#define m
Definition basecurve.c:283
const float f
const float v
static const float const float const float min
static const int row
#define EPSILON
Definition curve_tools.c:42
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
#define NMS_GAMMA
#define NMS_BETA
#define NMS_ALPHA
static int simplex(double(*objfunc)(double[], void *params), double start[], int n, double EPSILON, double scale, int maxiter, void(*constrain)(double[], int n), void *params)