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#pragma once
84
85// Nelder-Mead reflection / contraction / expansion coefficients (the algorithm's standard
86// constants). Guarded so a caller may override them before including this header.
87#ifndef NMS_ALPHA
88#define NMS_ALPHA 1.0
89#endif
90#ifndef NMS_BETA
91#define NMS_BETA 0.5
92#endif
93#ifndef NMS_GAMMA
94#define NMS_GAMMA 2.0
95#endif
96
97// Nelder-Mead simplex minimizer of objfunc over n parameters, starting from start[] (overwritten
98// with the best point). EPSILON = convergence threshold, scale = initial simplex size, maxiter =
99// iteration cap, constrain = optional projection of a trial point onto the feasible set (may be
100// NULL), params = opaque pointer forwarded to objfunc/constrain. Returns the iteration count.
101static int simplex(double (*objfunc)(double[], void *params), double start[], int n, double EPSILON, double scale,
102 int maxiter, void (*constrain)(double[], int n), void *params)
103{
104
105 int vs; /* vertex with smallest value */
106 int vh; /* vertex with next smallest value */
107 int vg; /* vertex with largest value */
108
109 int i = 0, j = 0, m, row;
110 int itr; /* track the number of iterations */
111
112 double **v; /* holds vertices of simplex */
113 double pn, qn; /* values used to create initial simplex */
114 double *f; /* value of function at each vertex */
115 double fr; /* value of function at reflection point */
116 double fe; /* value of function at expansion point */
117 double fc; /* value of function at contraction point */
118 double *vr; /* reflection - coordinates */
119 double *ve; /* expansion - coordinates */
120 double *vc; /* contraction - coordinates */
121 double *vm; /* centroid - coordinates */
122 //double min;
123
124 double fsum, favg, s, cent;
125
126 /* dynamically allocate arrays */
127
128 /* allocate the rows of the arrays */
129 v = (double **)malloc(sizeof(double *) * (n + 1));
130 f = (double *)malloc(sizeof(double) * (n + 1));
131 vr = (double *)malloc(sizeof(double) * n);
132 ve = (double *)malloc(sizeof(double) * n);
133 vc = (double *)malloc(sizeof(double) * n);
134 vm = (double *)malloc(sizeof(double) * n);
135
136 /* allocate the columns of the arrays */
137 for(i = 0; i <= n; i++)
138 {
139 v[i] = (double *)malloc(sizeof(double) * n);
140 }
141
142 /* create the initial simplex */
143 /* assume one of the vertices is 0,0 */
144
145 pn = scale * (sqrt(n + 1) - 1 + n) / (n * sqrt(2));
146 qn = scale * (sqrt(n + 1) - 1) / (n * sqrt(2));
147
148 for(i = 0; i < n; i++)
149 {
150 v[0][i] = start[i];
151 }
152
153 for(i = 1; i <= n; i++)
154 {
155 for(j = 0; j < n; j++)
156 {
157 if(i - 1 == j)
158 {
159 v[i][j] = pn + start[j];
160 }
161 else
162 {
163 v[i][j] = qn + start[j];
164 }
165 }
166 }
167
168 if(!IS_NULL_PTR(constrain))
169 {
170 constrain(v[j], n);
171 }
172 /* find the initial function values */
173 for(j = 0; j <= n; j++)
174 {
175 f[j] = objfunc(v[j], params);
176 }
177
178#if 0
179 /* print out the initial values */
180 printf("Initial Values\n");
181 for(j = 0; j <= n; j++)
182 {
183 for(i = 0; i < n; i++)
184 {
185 printf("%f %f\n", v[j][i], f[j]);
186 }
187 }
188#endif
189
190 /* begin the main loop of the minimization */
191 for(itr = 1; itr <= maxiter; itr++)
192 {
193 /* find the index of the largest value */
194 vg = 0;
195 for(j = 0; j <= n; j++)
196 {
197 if(f[j] > f[vg])
198 {
199 vg = j;
200 }
201 }
202
203 /* find the index of the smallest value */
204 vs = 0;
205 for(j = 0; j <= n; j++)
206 {
207 if(f[j] < f[vs])
208 {
209 vs = j;
210 }
211 }
212
213 /* find the index of the second largest value */
214 vh = vs;
215 for(j = 0; j <= n; j++)
216 {
217 if(f[j] > f[vh] && f[j] < f[vg])
218 {
219 vh = j;
220 }
221 }
222
223 /* calculate the centroid */
224 for(j = 0; j <= n - 1; j++)
225 {
226 cent = 0.0;
227 for(m = 0; m <= n; m++)
228 {
229 if(m != vg)
230 {
231 cent += v[m][j];
232 }
233 }
234 vm[j] = cent / n;
235 }
236
237 /* reflect vg to new vertex vr */
238 for(j = 0; j <= n - 1; j++)
239 {
240 /*vr[j] = (1+NMS_ALPHA)*vm[j] - NMS_ALPHA*v[vg][j];*/
241 vr[j] = vm[j] + NMS_ALPHA * (vm[j] - v[vg][j]);
242 }
243 if(!IS_NULL_PTR(constrain))
244 {
245 constrain(vr, n);
246 }
247 fr = objfunc(vr, params);
248
249 if(fr < f[vh] && fr >= f[vs])
250 {
251 for(j = 0; j <= n - 1; j++)
252 {
253 v[vg][j] = vr[j];
254 }
255 f[vg] = fr;
256 }
257
258 /* investigate a step further in this direction */
259 if(fr < f[vs])
260 {
261 for(j = 0; j <= n - 1; j++)
262 {
263 /*ve[j] = NMS_GAMMA*vr[j] + (1-NMS_GAMMA)*vm[j];*/
264 ve[j] = vm[j] + NMS_GAMMA * (vr[j] - vm[j]);
265 }
266 if(!IS_NULL_PTR(constrain))
267 {
268 constrain(ve, n);
269 }
270 fe = objfunc(ve, params);
271
272 /* by making fe < fr as opposed to fe < f[vs],
273 Rosenbrocks function takes 63 iterations as opposed
274 to 64 when using double variables. */
275
276 if(fe < fr)
277 {
278 for(j = 0; j <= n - 1; j++)
279 {
280 v[vg][j] = ve[j];
281 }
282 f[vg] = fe;
283 }
284 else
285 {
286 for(j = 0; j <= n - 1; j++)
287 {
288 v[vg][j] = vr[j];
289 }
290 f[vg] = fr;
291 }
292 }
293
294 /* check to see if a contraction is necessary */
295 if(fr >= f[vh])
296 {
297 if(fr < f[vg] && fr >= f[vh])
298 {
299 /* perform outside contraction */
300 for(j = 0; j <= n - 1; j++)
301 {
302 /*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/
303 vc[j] = vm[j] + NMS_BETA * (vr[j] - vm[j]);
304 }
305 if(!IS_NULL_PTR(constrain))
306 {
307 constrain(vc, n);
308 }
309 fc = objfunc(vc, params);
310 }
311 else
312 {
313 /* perform inside contraction */
314 for(j = 0; j <= n - 1; j++)
315 {
316 /*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/
317 vc[j] = vm[j] - NMS_BETA * (vm[j] - v[vg][j]);
318 }
319 if(!IS_NULL_PTR(constrain))
320 {
321 constrain(vc, n);
322 }
323 fc = objfunc(vc, params);
324 }
325
326
327 if(fc < f[vg])
328 {
329 for(j = 0; j <= n - 1; j++)
330 {
331 v[vg][j] = vc[j];
332 }
333 f[vg] = fc;
334 }
335 /* at this point the contraction is not successful,
336 we must halve the distance from vs to all the
337 vertices of the simplex and then continue.
338 10/31/97 - modified to account for ALL vertices.
339 */
340 else
341 {
342 for(row = 0; row <= n; row++)
343 {
344 if(row != vs)
345 {
346 for(j = 0; j <= n - 1; j++)
347 {
348 v[row][j] = v[vs][j] + (v[row][j] - v[vs][j]) / 2.0;
349 }
350 }
351 }
352 if(!IS_NULL_PTR(constrain))
353 {
354 constrain(v[vg], n);
355 }
356 f[vg] = objfunc(v[vg], params);
357 if(!IS_NULL_PTR(constrain))
358 {
359 constrain(v[vh], n);
360 }
361 f[vh] = objfunc(v[vh], params);
362 }
363 }
364#if 0
365 /* print out the value at each iteration */
366 printf("Iteration %d\n", itr);
367 for(j = 0; j <= n; j++)
368 {
369 for(i = 0; i < n; i++)
370 {
371 printf("%f %f\n", v[j][i], f[j]);
372 }
373 }
374#endif
375 /* test for convergence */
376 fsum = 0.0;
377 for(j = 0; j <= n; j++)
378 {
379 fsum += f[j];
380 }
381 favg = fsum / (n + 1);
382 s = 0.0;
383 for(j = 0; j <= n; j++)
384 {
385 s += pow((f[j] - favg), 2.0) / (n);
386 }
387 s = sqrt(s);
388 if(s < EPSILON) break;
389 }
390 /* end main loop of the minimization */
391
392 /* find the index of the smallest value */
393 vs = 0;
394 for(j = 0; j <= n; j++)
395 {
396 if(f[j] < f[vs])
397 {
398 vs = j;
399 }
400 }
401#if 0
402 printf("The minimum was found at\n");
403 for(j = 0; j < n; j++)
404 {
405 printf("%e\n", v[vs][j]);
406 start[j] = v[vs][j];
407 }
408 double min = objfunc(v[vs], params);
409 printf("The minimum value is %f\n", min);
410 printf("%d Iterations through program\n", itr);
411#else
412 for(j = 0; j < n; j++)
413 {
414 start[j] = v[vs][j];
415 }
416#endif
417 dt_free(f);
418 dt_free(vr);
419 dt_free(ve);
420 dt_free(vc);
421 dt_free(vm);
422 for(i = 0; i <= n; i++)
423 {
424 dt_free(v[i]);
425 }
426 dt_free(v);
427 return itr;
428}
429
430/*==================================================================================
431 * end of nmsimplex code
432 *==================================================================================*/
433
434// clang-format off
435// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
436// vim: shiftwidth=2 expandtab tabstop=2 cindent
437// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
438// clang-format on
#define m
Definition basecurve.c:278
const dt_aligned_pixel_t f
static const float const float const float min
static const int row
#define EPSILON
Definition curve_tools.c:41
#define dt_free(ptr)
Definition darktable.h:478
#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 darktable.h:293
const float v
#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)