IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ticket #383: psMinimize.c

File psMinimize.c, 75.1 KB (added by eugene, 21 years ago)

psMinimize with my version of psMinimizeLMChi2

Line 
1/** @file psMinimize.c
2 * \brief basic minimization functions
3 * @ingroup Math
4 *
5 * This file will contain functions to minimize an arbitrary function at
6 * a data point, fit an arbitrary function to a set of data points, and
7 * fit a 1-D polynomial to a set of data points.
8 *
9 * @author GLG, MHPCC
10 *
11 * @version $Revision: 1.110 $ $Name: $
12 * @date $Date: 2005/03/31 01:02:15 $
13 *
14 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
15 *
16 * XXX: must follow coding name standards on local functions.
17 *
18 */
19/*****************************************************************************/
20/* INCLUDE FILES */
21/*****************************************************************************/
22#include <stdio.h>
23#include <float.h>
24#include <math.h>
25
26#include "psMinimize.h"
27#include "psStats.h"
28/*****************************************************************************/
29/* DEFINE STATEMENTS */
30/*****************************************************************************/
31#define PS_SEG psLib
32#define PS_PWD dataManip
33#define PS_FILE psMinimize
34/*****************************************************************************/
35/* TYPE DEFINITIONS */
36/*****************************************************************************/
37
38/*****************************************************************************/
39/* GLOBAL VARIABLES */
40/*****************************************************************************/
41static psMinimizeChi2PowellFunc Chi2PowellFunc = NULL;
42static psVector *myValue;
43static psVector *myError;
44
45/*****************************************************************************/
46/* FILE STATIC VARIABLES */
47/*****************************************************************************/
48
49// None
50
51/*****************************************************************************/
52/* FUNCTION IMPLEMENTATION - LOCAL */
53/*****************************************************************************/
54
55/******************************************************************************
56p_psBuildSums1D(x, polyOrder, sums): this routine calculates the powers of
57input parameter "x" between 0 and input parameter polyOrder. The result is
58returned as a psVector sums.
59
60XXX: Use a static vector.
61 *****************************************************************************/
62void psBuildSums1D(psF64 x,
63 psS32 polyOrder,
64 psVector* sums)
65{
66 psS32 i = 0;
67 psF64 xSum = 0.0;
68
69 if (sums == NULL) {
70 sums = psVectorAlloc(polyOrder, PS_TYPE_F64);
71 }
72 if (polyOrder > sums->n) {
73 sums = psVectorRealloc(sums, polyOrder);
74 }
75
76 xSum = 1.0;
77 for (i = 0; i <= polyOrder; i++) {
78 sums->data.F64[i] = xSum;
79 xSum *= x;
80 }
81}
82
83/*****************************************************************************
84CalculateSecondDerivs(): Given a set of x/y vectors corresponding to a
85tabulated function at n points, this routine calculates the second
86derivatives of the interpolating cubic splines at those n points.
87
88The first and second derivatives at the endpoints, undefined in the SDR, are
89here defined to be 0.0. They can be modified via ypo and yp1.
90
91This routine assumes that vectors x and y are of the appropriate types/sizes
92(F32).
93
94XXX: This algorithm is derived from the Numerical Recipes.
95XXX: use recycled vectors for internal data.
96XXX: do an F64 version?
97 *****************************************************************************/
98psF32 *CalculateSecondDerivs(const psVector* x, ///< Ordinates (or NULL to just use the indices)
99 const psVector* y) ///< Coordinates
100{
101 psTrace(".psLib.dataManip.CalculateSecondDerivs", 4,
102 "---- CalculateSecondDerivs() begin ----\n");
103
104 psS32 i;
105 psS32 k;
106 psF32 sig;
107 psF32 p;
108 psS32 n = y->n;
109 psF32 *u = (psF32 *) psAlloc(n * sizeof(psF32));
110 psF32 *derivs2 = (psF32 *) psAlloc(n * sizeof(psF32));
111 psF32 *X = (psF32 *) & (x->data.F32[0]);
112 psF32 *Y = (psF32 *) & (y->data.F32[0]);
113 psF32 qn;
114
115 // XXX: The second derivatives at the endpoints, undefined in the SDR,
116 // are set in psConstants.h: PS_LEFT_SPLINE_DERIV, PS_RIGHT_SPLINE_DERIV.
117 derivs2[0] = -0.5;
118 u[0]= (3.0/(X[1]-X[0])) * ((Y[1]-Y[0])/(X[1]-X[0]) - PS_LEFT_SPLINE_DERIV);
119
120 for (i=1;i<=(n-2);i++) {
121 sig = (X[i] - X[i-1]) / (X[i+1] - X[i-1]);
122 p = sig * derivs2[i-1] + 2.0;
123 derivs2[i] = (sig - 1.0) / p;
124 u[i] = ((Y[i+1] - Y[i])/(X[i+1]-X[i])) - ((Y[i]-Y[i-1])/(X[i]-X[i-1]));
125 u[i] = ((6.0 * u[i] / (X[i+1] - X[i-1])) - (sig * u[i-1])) / p;
126
127 psTrace(".psLib.dataManip.CalculateSecondDerivs", 6,
128 "X[%d] is %f\n", i, X[i]);
129 psTrace(".psLib.dataManip.CalculateSecondDerivs", 6,
130 "Y[%d] is %f\n", i, Y[i]);
131 psTrace(".psLib.dataManip.CalculateSecondDerivs", 6,
132 "u[%d] is %f\n", i, u[i]);
133 }
134
135 qn = 0.5;
136 u[n-1] = (3.0/(X[n-1]-X[n-2])) * (PS_RIGHT_SPLINE_DERIV - (Y[n-1]-Y[n-2])/(X[n-1]-X[n-2]));
137 derivs2[n-1] = (u[n-1] - (qn * u[n-2])) / ((qn * derivs2[n-2]) + 1.0);
138
139 for (k=(n-2);k>=0;k--) {
140 derivs2[k] = derivs2[k] * derivs2[k+1] + u[k];
141
142 psTrace(".psLib.dataManip.CalculateSecondDerivs", 6,
143 "derivs2[%d] is %f\n", k, derivs2[k]);
144 }
145
146 psFree(u);
147 psTrace(".psLib.dataManip.CalculateSecondDerivs", 4,
148 "---- CalculateSecondDerivs() end ----\n");
149 return(derivs2);
150}
151
152/******************************************************************************
153p_psNRSpline1DEval(): This routine does NR-style evaluation of cubic splines.
154It takes advantage of the 2nd derivatives of the cubic splines, which are
155stored in the psSPline1D data structure, and computes the interpolated value
156directly, without computing (or using) the interpolating cubic spline
157polynomial.
158
159This routine is here mostly for a sanity check on the psLib function
160evalSpline() which computes the interpolated value based on the cubic spline
161polynomials which are stored in psSpline1D.
162
163XXX: This is F32 only
164
165XXX: spline->knots must be psF32
166 *****************************************************************************/
167/*
168psF32 p_psNRSpline1DEval(psSpline1D *spline,
169 const psVector* x,
170 const psVector* y,
171 psF32 X)
172{
173 PS_PTR_CHECK_NULL(spline, NAN);
174 PS_INT_CHECK_NON_NEGATIVE(spline->n, NAN);
175 PS_VECTOR_CHECK_NULL(spline->domains, NAN);
176 PS_PTR_CHECK_NULL(spline->p_psDeriv2, NAN);
177 PS_VECTOR_CHECK_NULL(x, NAN);
178 PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NAN);
179 PS_VECTOR_CHECK_NULL(y, NAN);
180 PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NAN);
181 PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NULL);
182
183 psS32 n;
184 psS32 klo;
185 psS32 khi;
186 psF32 H;
187 psF32 A;
188 psF32 B;
189 psF32 C;
190 psF32 D;
191 psF32 Y;
192
193 n = spline->n;
194 klo = p_psVectorBinDisect32(spline->knots->data.F32, (spline->n)+1, X);
195 if (klo < 0) {
196 psLogMsg(__func__, PS_LOG_WARN,
197 "WARNING: psMinimize.c: p_psNRSpline1DEval(): p_psVectorBinDisect32 returned an error (%d).\n", klo);
198 return(NAN);
199 }
200 khi = klo + 1;
201 H = spline->knots->data.F32[khi] - spline->knots->data.F32[klo];
202 A = (spline->knots->data.F32[khi] - X) / H;
203 B = (X - spline->knots->data.F32[klo]) / H;
204 C = ((A*A*A)-A) * (H*H/6.0);
205 D = ((B*B*B)-B) * (H*H/6.0);
206
207 Y = (A * y->data.F32[klo]) +
208 (B * y->data.F32[khi]) +
209 (C * (spline->p_psDeriv2)[klo]) +
210 (D * (spline->p_psDeriv2)[khi]);
211
212 return(Y);
213}
214*/
215/*****************************************************************************/
216/* FUNCTION IMPLEMENTATION - PUBLIC */
217/*****************************************************************************/
218
219/*****************************************************************************
220psVectorFitSpline1D(): given a psSpline1D data structure and a set of x/y
221vectors, this routine generates the linear or cublic splines which satisfy
222those data points.
223
224The formula for calculating the spline polynomials is derived from Numerical
225Recipes in C. The basic idea is that the polynomial is
226 (1) y = (A * y[0]) +
227 (2) (B * y[1]) +
228 (3) ((((A*A*A)-A) * mySpline->p_psDeriv2[0]) * H^2)/6.0 +
229 (4) ((((B*B*B)-B) * mySpline->p_psDeriv2[1]) * H^2)/6.0
230Where:
231 H = x[1]-x[0]
232 A = (x[1]-x)/H
233 B = (x-x[0])/H
234The bulk of the code in this routine is the expansion of the above equation
235into a polynomial in terms of x, and then saving the coefficients of the
236powers of x in the spline polynomials. This gets pretty complicated.
237
238XXX: usage of yErr is not specified in IfA documentation.
239
240XXX: Is the x argument redundant? What do we do if the x argument is
241supplied, but does not equal the knots specified in mySpline?
242
243XXX: can psSpline be NULL?
244
245XXX: reimplement this assuming that mySpline is NULL?
246
247XXX: What happens if X is NULL, then an index vector is generated for X, but
248that index vector lies outside the range vectors in mySpline?
249
250XXX: Assumes mySpline->knots is psF32. Must add psU32 and psF64.
251 *****************************************************************************/
252psSpline1D *psVectorFitSpline1D(psSpline1D *mySpline, ///< The spline which will be generated.
253 const psVector* x, ///< Ordinates (or NULL to just use the indices)
254 const psVector* y, ///< Coordinates
255 const psVector* yErr) ///< Errors in coordinates, or NULL
256{
257 PS_VECTOR_CHECK_NULL(y, NULL);
258 PS_VECTOR_CHECK_TYPE_F32_OR_F64(y, NULL);
259 if (mySpline != NULL) {
260 PS_VECTOR_CHECK_TYPE(mySpline->knots, PS_TYPE_F32, NULL);
261 }
262
263 psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
264 "---- psVectorFitSpline1D() begin ----\n");
265 psS32 numSplines = (y->n)-1;
266 psF32 tmp;
267 psF32 H;
268 psS32 i;
269 psF32 slope;
270 psVector *x32 = NULL;
271 psVector *y32 = NULL;
272 psVector *yErr32 = NULL;
273 static psVector *x32Static = NULL;
274 static psVector *y32Static = NULL;
275 static psVector *yErr32Static = NULL;
276
277 PS_VECTOR_CONVERT_F64_TO_F32_STATIC(y, y32, y32Static);
278
279 // If yErr==NULL, set all errors equal.
280 if (yErr == NULL) {
281 PS_VECTOR_GEN_YERR_STATIC_F32(yErr32Static, y->n);
282 yErr32 = yErr32Static;
283 } else {
284 PS_VECTOR_CHECK_TYPE_F32_OR_F64(yErr, NULL);
285 PS_VECTOR_CONVERT_F64_TO_F32_STATIC(yErr, yErr32, yErr32Static);
286 }
287
288 // If x==NULL, create an x32 vector with x values set to (0:n).
289 if (x == NULL) {
290 PS_VECTOR_GEN_X_INDEX_STATIC_F32(x32Static, y->n);
291 x32 = x32Static;
292 } else {
293 PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
294 PS_VECTOR_CONVERT_F64_TO_F32_STATIC(x, x32, x32Static);
295 }
296 PS_VECTOR_CHECK_SIZE_EQUAL(x32, y32, NULL);
297 PS_VECTOR_CHECK_SIZE_EQUAL(yErr32, y32, NULL);
298
299 /*
300 XXX:
301 This can not be implemented until SDR states what order spline should be
302 created.
303 Should we error if mySpline is not NULL?
304 Should we error if mySPline is not NULL?
305 */
306 if (mySpline == NULL) {
307 mySpline = psSpline1DAllocGeneric(x32, 3);
308 }
309 PS_PTR_CHECK_NULL(mySpline, NULL);
310 PS_INT_CHECK_NON_NEGATIVE(mySpline->n, NULL);
311
312 if (y32->n != (1 + mySpline->n)) {
313 psError(PS_ERR_BAD_PARAMETER_SIZE, true,
314 "data size / spline size mismatch (%d %d)\n",
315 y32->n, mySpline->n);
316 return(NULL);
317 }
318
319 // If these are linear splines, which means their polynomials will have
320 // two coefficients, then we do the simple calculation.
321 if (2 == (mySpline->spline[0])->n) {
322 for (i=0;i<mySpline->n;i++) {
323 slope = (y32->data.F32[i+1] - y32->data.F32[i]) /
324 (mySpline->knots->data.F32[i+1] - mySpline->knots->data.F32[i]);
325 (mySpline->spline[i])->coeff[0] = y32->data.F32[i] -
326 (slope * mySpline->knots->data.F32[i]);
327
328 (mySpline->spline[i])->coeff[1] = slope;
329 psTrace(".psLib.dataManip.psMinimize.psVectorFitSpline1D", 4,
330 "---- mySpline %d coeffs are (%f, %f)\n", i,
331 (mySpline->spline[i])->coeff[0],
332 (mySpline->spline[i])->coeff[1]);
333 }
334 psTrace(".psLib.dataManip.psMinimize.psVectorFitSpline1D", 4,
335 "---- Exiting psVectorFitSpline1D()()\n");
336 return((psSpline1D *) mySpline);
337 }
338
339 // Check if these are cubic splines (n==4). If not, psError.
340 if (4 != (mySpline->spline[0])->n) {
341 psError(PS_ERR_BAD_PARAMETER_SIZE, true,
342 "Don't know how to generate %d-order splines.",
343 (mySpline->spline[0])->n-1);
344 return(NULL);
345 }
346
347 // If we get here, then we know these are cubic splines. We first
348 // generate the second derivatives at each data point.
349 mySpline->p_psDeriv2 = CalculateSecondDerivs(x32, y32);
350 for (i=0;i<y32->n;i++)
351 psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
352 "Second deriv[%d] is %f\n", i, mySpline->p_psDeriv2[i]);
353
354 // We generate the coefficients of the spline polynomials. I can't
355 // concisely explain how this code works. See above function comments
356 // and Numerical Recipes in C.
357 for (i=0;i<numSplines;i++) {
358 H = x32->data.F32[i+1] - x32->data.F32[i];
359 psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
360 "x data (%f - %f) (%f)\n",
361 x32->data.F32[i],
362 x32->data.F32[i+1], H);
363 //
364 // ******** Calculate 0-order term ********
365 //
366 // From (1)
367 (mySpline->spline[i])->coeff[0] = (y32->data.F32[i] * x32->data.F32[i+1]/H);
368 // From (2)
369 ((mySpline->spline[i])->coeff[0])-= ((y32->data.F32[i+1] * x32->data.F32[i])/H);
370 // From (3)
371 tmp = (x32->data.F32[i+1] * x32->data.F32[i+1] * x32->data.F32[i+1]) / (H * H * H);
372 tmp-= (x32->data.F32[i+1] / H);
373 tmp*= (mySpline->p_psDeriv2)[i] * H * H / 6.0;
374 ((mySpline->spline[i])->coeff[0])+= tmp;
375 // From (4)
376 tmp = -(x32->data.F32[i] * x32->data.F32[i] * x32->data.F32[i]) / (H * H * H);
377 tmp+= (x32->data.F32[i] / H);
378 tmp*= (mySpline->p_psDeriv2)[i+1] * H * H / 6.0;
379 ((mySpline->spline[i])->coeff[0])+= tmp;
380
381 //
382 // ******** Calculate 1-order term ********
383 //
384 // From (1)
385 (mySpline->spline[i])->coeff[1] = -(y32->data.F32[i]) / H;
386 // From (2)
387 ((mySpline->spline[i])->coeff[1])+= (y32->data.F32[i+1] / H);
388 // From (3)
389 tmp = -3.0 * (x32->data.F32[i+1] * x32->data.F32[i+1]) / (H * H * H);
390 tmp+= (1.0 / H);
391 tmp*= ((mySpline->p_psDeriv2)[i]) * H * H / 6.0;
392 ((mySpline->spline[i])->coeff[1])+= tmp;
393 // From (4)
394 tmp = 3.0 * (x32->data.F32[i] * x32->data.F32[i]) / (H * H * H);
395 tmp-= (1.0 / H);
396 tmp*= ((mySpline->p_psDeriv2)[i+1]) * H * H / 6.0;
397 ((mySpline->spline[i])->coeff[1])+= tmp;
398
399 //
400 // ******** Calculate 2-order term ********
401 //
402 // From (3)
403 (mySpline->spline[i])->coeff[2] = ((mySpline->p_psDeriv2)[i]) * 3.0 * x32->data.F32[i+1] / (6.0 * H);
404 // From (4)
405 ((mySpline->spline[i])->coeff[2])-= (((mySpline->p_psDeriv2)[i+1]) * 3.0 * x32->data.F32[i] / (6.0 * H));
406
407 //
408 // ******** Calculate 3-order term ********
409 //
410 // From (3)
411 (mySpline->spline[i])->coeff[3] = -((mySpline->p_psDeriv2)[i]) / (6.0 * H);
412 // From (4)
413 ((mySpline->spline[i])->coeff[3])+= ((mySpline->p_psDeriv2)[i+1]) / (6.0 * H);
414
415 psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
416 "(mySpline->spline[%d])->coeff[0] is %f\n", i, (mySpline->spline[i])->coeff[0]);
417 psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
418 "(mySpline->spline[%d])->coeff[1] is %f\n", i, (mySpline->spline[i])->coeff[1]);
419 psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
420 "(mySpline->spline[%d])->coeff[2] is %f\n", i, (mySpline->spline[i])->coeff[2]);
421 psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
422 "(mySpline->spline[%d])->coeff[3] is %f\n", i, (mySpline->spline[i])->coeff[3]);
423
424 }
425
426 psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
427 "---- psVectorFitSpline1D() end ----\n");
428 return(mySpline);
429}
430
431
432/******************************************************************************
433XXX: We assume unnormalized gaussians.
434 *****************************************************************************/
435psVector *psMinimizeLMChi2Gauss1D(psImage *deriv,
436 const psVector *params,
437 const psArray *coords)
438{
439 PS_PTR_CHECK_NULL(coords, NULL);
440 PS_PTR_CHECK_NULL(params, NULL);
441
442 psTrace(".psLib.dataManip.psMinimize", 4,
443 "---- psMinimizeLMChi2Gauss1D() begin ----\n");
444 psF32 x;
445 psS32 i;
446 psF32 mean = params->data.F32[0];
447 psF32 stdev = params->data.F32[1];
448 psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
449
450 psTrace(".psLib.dataManip.psMinimize", 6,
451 "(mean, stdev) is (%f, %f)\n", mean, stdev);
452
453 if (deriv == NULL) {
454 deriv = psImageAlloc(params->n, coords->n, PS_TYPE_F32);
455 } else {
456 PS_IMAGE_CHECK_SIZE(deriv, params->n, coords->n, NULL);
457 PS_IMAGE_CHECK_TYPE(deriv, PS_TYPE_F32, NULL);
458 }
459
460 for (i=0;i<coords->n;i++) {
461 x = ((psVector *) (coords->data[i]))->data.F32[0];
462 out->data.F32[i] = psGaussian(x, mean, stdev, false);
463 }
464
465 for (i=0;i<coords->n;i++) {
466 x = ((psVector *) (coords->data[i]))->data.F32[0];
467 psF32 tmp = (x - mean) * psGaussian(x, mean, stdev, false);
468 deriv->data.F32[i][0] = tmp / (stdev * stdev);
469 tmp = (x - mean) * (x - mean) *
470 psGaussian(x, mean, stdev, 0);
471 deriv->data.F32[i][1] = tmp / (stdev * stdev * stdev);
472 }
473
474 psTrace(".psLib.dataManip.psMinimize", 4,
475 "---- psMinimizeLMChi2Gauss1D() end ----\n");
476 return(out);
477}
478
479/*
480XXX: from bug 230:
481
482We first perform a rotation:
483u = - (x-x0)*cos(theta) + (y-y0)*sin(theta)
484v = (x-x0)*cos(theta) + (y-y0)*sin(theta)
485
486Here u is the major axis, and v is the minor axis, x0,y0 is the centre, and
487theta is the position angle.
488
489Then the flux is
490
491flux = norm * exp(-( u*u/2.0/sigmau/sigmau + v*v/2.0/sigmav/sigmav)
492)/2.0/pi/sigmau/sigmav
493
494Here sigmau and sigmav are the widths of the major and minor axes.
495
496The "norm" parameter in the equation above corresponds to the normalisation.
497
498Suggest order:
499
500norm
501x0
502y0
503sigma_u
504sigma_v
505theta
506*/
507
508psVector *psMinimizeLMChi2Gauss2D(psImage *deriv,
509 const psVector *params,
510 const psArray *coords)
511{
512 PS_PTR_CHECK_NULL(coords, NULL);
513 PS_PTR_CHECK_NULL(params, NULL);
514
515 psF64 normalization = params->data.F32[0];
516 psF64 x0 = params->data.F32[1];
517 psF64 y0 = params->data.F32[2];
518 psF64 sigmaX = params->data.F32[3];
519 psF64 sigmaY = params->data.F32[4];
520 psF64 theta = params->data.F32[5];
521 psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
522
523 if (deriv == NULL) {
524 deriv = psImageAlloc(params->n, coords->n, PS_TYPE_F32);
525 } else {
526 PS_IMAGE_CHECK_SIZE(deriv, 6, coords->n, NULL);
527 PS_IMAGE_CHECK_TYPE(deriv, PS_TYPE_F32, NULL);
528 }
529
530 psTrace(".psLib.dataManip.psMinimize", 4,
531 "---- psMinimizeLMChi2Gauss2D() begin ----\n");
532
533 for (psS32 i=0;i<coords->n;i++) {
534 psF64 x = ((psVector *) coords->data[i])->data.F32[0];
535 psF64 y = ((psVector *) coords->data[i])->data.F32[0];
536
537 psF64 u = - (x-x0)*cos(theta) + (y-y0)*sin(theta);
538 psF64 v = (x-x0)*cos(theta) + (y-y0)*sin(theta);
539
540 psF64 flux = normalization * exp(-( u*u/(2.0 * sigmaX * sigmaX) +
541 v*v/(2.0 * sigmaY * sigmaY)))/
542 (2.0 * PS_PI * sigmaX * sigmaY);
543 out->data.F32[i] = flux;
544
545 // XXX: Calculate these correctly.
546 deriv->data.F32[i][0] = 0.0;
547 deriv->data.F32[i][1] = 0.0;
548 deriv->data.F32[i][2] = 0.0;
549 deriv->data.F32[i][3] = 0.0;
550 deriv->data.F32[i][4] = 0.0;
551 deriv->data.F32[i][5] = 0.0;
552 }
553
554 psTrace(".psLib.dataManip.psMinimize", 4,
555 "---- psMinimizeLMChi2Gauss2D() end ----\n");
556 return(out);
557}
558
559psF64 p_psImageGetElementF64(psImage *a, int i, int j);
560
561// XXX EAM these two functions are useful for testing
562// XXX EAM this should move to psImage.c
563bool p_psImagePrint (FILE *f, psImage *a, char *name) {
564
565 fprintf (f, "matrix: %s\n", name);
566
567 for (int j = 0; j < a[0].numRows; j++) {
568 for (int i = 0; i < a[0].numCols; i++) {
569 fprintf (f, "%f ", p_psImageGetElementF64(a, i, j));
570 }
571 fprintf (f, "\n");
572 }
573 fprintf (f, "\n");
574 return (true);
575}
576
577// XXX EAM this should move to psVector.c
578bool p_psVectorPrint (FILE *f, psVector *a, char *name) {
579
580 fprintf (f, "vector: %s\n", name);
581
582 for (int i = 0; i < a[0].n; i++) {
583 fprintf (f, "%f\n", p_psVectorGetElementF64(a, i));
584 }
585 fprintf (f, "\n");
586 return (true);
587}
588
589// XXX EAM this is my re-implementation of MinLM
590psBool psMinimizeLMChi2(psMinimization *min,
591 psImage *covar,
592 psVector *params,
593 const psVector *paramMask,
594 const psArray *x,
595 const psVector *y,
596 const psVector *yErr,
597 psMinimizeLMChi2Func func)
598{
599 PS_PTR_CHECK_NULL(min, NULL);
600 PS_VECTOR_CHECK_NULL(params, NULL);
601 PS_VECTOR_CHECK_EMPTY(params, NULL);
602 PS_PTR_CHECK_NULL(x, NULL);
603 PS_VECTOR_CHECK_NULL(y, NULL);
604 PS_VECTOR_CHECK_EMPTY(y, NULL);
605 PS_VECTOR_CHECK_SIZE_EQUAL(x, y, NULL);
606 PS_PTR_CHECK_NULL(func, NULL);
607
608 // this function has test and current values for several things
609 // the current best value is in lower case
610 // the next guess value is in upper case
611
612 // allocate internal arrays (current vs Guess)
613 psImage *alpha = psImageAlloc (params->n, params->n, PS_TYPE_F64);
614 psImage *Alpha = psImageAlloc (params->n, params->n, PS_TYPE_F64);
615 psVector *beta = psVectorAlloc (params->n, PS_TYPE_F64);
616 psVector *Beta = psVectorAlloc (params->n, PS_TYPE_F64);
617 psVector *Params = psVectorAlloc (params->n, PS_TYPE_F64);
618 psVector *dy = NULL;
619 psF64 chisq = 0.0;
620 psF64 Chisq = 0.0;
621 psF64 lambda = 0.001;
622
623 // the initial guess on params is provided by the user
624 Params = psVectorCopy (Params, params, PS_TYPE_F32);
625
626 // the user provides the error or NULL. we need to convert
627 // to appropriate weights
628 dy = psVectorAlloc (y->n, PS_TYPE_F32);
629 if (yErr != NULL) {
630 for (int i = 0; i < dy->n; i++) {
631 dy->data.F32[i] = 1.0 / PS_SQR (yErr->data.F32[i]);
632 }
633 } else {
634 for (int i = 0; i < dy->n; i++) {
635 dy->data.F32[i] = 1.0;
636 }
637 }
638
639 // calculate initial alpha and beta, set chisq (min->value)
640 min->value = p_psMinLM_SetABX (alpha, beta, params, x, y, dy, func);
641# ifndef PS_NO_TRACE
642 // dump some useful info if trace is defined
643 if (psTraceGetLevel (".psLib.dataManip.psMinimizeLMChi2") > 4) {
644 p_psImagePrint (psTraceGetDestination(), alpha, "alpha guess");
645 p_psVectorPrint (psTraceGetDestination(), beta, "beta guess");
646 p_psVectorPrint (psTraceGetDestination(), params, "params guess");
647 }
648# endif /* PS_NO_TRACE */
649
650
651 // iterate until the tolerance is reached, or give up
652 while ((min->lastDelta > min->tol) && (min->iter < min->maxIter)) {
653
654 // set a new guess for Alpha, Beta, Params
655 p_psMinLM_GuessABP (Alpha, Beta, Params, alpha, beta, params, lambda);
656
657# ifndef PS_NO_TRACE
658 // dump some useful info if trace is defined
659 if (psTraceGetLevel (".psLib.dataManip.psMinimizeLMChi2") > 4) {
660 p_psImagePrint (psTraceGetDestination(), Alpha, "alpha guess");
661 p_psVectorPrint (psTraceGetDestination(), Beta, "beta guess");
662 p_psVectorPrint (psTraceGetDestination(), Params, "params guess");
663 }
664# endif /* PS_NO_TRACE */
665
666 // calculate Chisq for new guess, update Alpha & Beta
667 Chisq = p_psMinLM_SetABX (Alpha, Beta, Params, x, y, dy, func);
668 psTrace (".psLib.dataManip.psMinimizeLMChi2", 3, "chisq: %f, Chisq %f, delta: %f\n", chisq, Chisq, min->lastDelta);
669
670 // accept new guess (if improvement), or increase lambda
671 if (Chisq < min->value) {
672 min->lastDelta = (min->value - Chisq) / (dy->n - params->n);
673 min->value = Chisq;
674 alpha = psImageCopy (alpha, Alpha, PS_TYPE_F64);
675 beta = psVectorCopy (beta, Beta, PS_TYPE_F64);
676 params = psVectorCopy (params, Params, PS_TYPE_F32);
677 lambda *= 0.1;
678 } else {
679 lambda *= 10.0;
680 }
681 min->iter ++;
682 }
683 psTrace (".psLib.dataManip.psMinimizeLMChi2", 3, "chisq: %f, Chisq %f, delta: %f\n", chisq, Chisq, min->lastDelta);
684
685 // free the internal temporary data
686 psFree (alpha);
687 psFree (Alpha);
688 psFree (beta);
689 psFree (Beta);
690 psFree (Params);
691 psFree (dy);
692 return (true);
693}
694
695// XXX EAM: this needs to respect the mask on params
696// XXX EAM: check not NULL on alpha, beta, params
697// alpha, beta, params are already allocated
698psF64 p_psMinLM_SetABX (psImage *alpha,
699 psVector *beta,
700 psVector *params,
701 const psArray *x,
702 const psVector *y,
703 const psVector *dy,
704 psMinimizeLMChi2Func func)
705{
706
707 psF64 chisq;
708 psF64 delta;
709 psF64 weight;
710 psF64 ymodel;
711 psVector *deriv = psVectorAlloc (params->n, PS_TYPE_F32);
712
713 // zero alpha and beta for summing below
714 for (int j = 0; j < params->n; j++) {
715 for (int k = 0; k < params->n; k++) {
716 alpha->data.F64[j][k] = 0;
717 }
718 beta->data.F64[j] = 0;
719 }
720 chisq = 0.0;
721
722 // calculate chisq, alpha, beta
723 for (int i = 0; i < y->n; i++) {
724 ymodel = func (deriv, params, (psVector *) x->data[i]);
725
726 delta = ymodel - y->data.F32[i];
727 chisq += PS_SQR (delta) * dy->data.F32[i];
728
729 for (int j = 0; j < params->n; j++) {
730 weight = deriv->data.F32[j] * dy->data.F32[i];
731 for (int k = 0; k <= j; k++) {
732 alpha->data.F64[j][k] += weight * deriv->data.F32[k];
733 }
734 beta->data.F64[j] += weight * delta;
735 }
736 }
737
738 // calculate lower-left half of alpha
739 for (int j = 1; j < params->n; j++) {
740 for (int k = 0; k < j; k++) {
741 alpha->data.F64[k][j] = alpha->data.F64[j][k];
742 }
743 }
744 psFree (deriv);
745 return (chisq);
746}
747
748// XXX EAM : can we use static copies of LUv, LUm, A?
749psBool p_psMinLM_GuessABP (psImage *Alpha,
750 psVector *Beta,
751 psVector *Params,
752 psImage *alpha,
753 psVector *beta,
754 psVector *params,
755 psF64 lambda)
756{
757
758# define USE_LU_DECOMP 1
759# if (USE_LU_DECOMP)
760 psVector *LUv = NULL;
761 psImage *LUm = NULL;
762 psImage *A = NULL;
763 psF32 det;
764
765 // LU decomposition version
766 psTrace (".pslib.dataManip.psMinLM_GuessABP", 3, "using LUD version");
767
768 // set new guess values (creates matrix A)
769 A = psImageCopy (NULL, alpha, PS_TYPE_F64);
770 for (int j = 0; j < params->n; j++) {
771 A->data.F64[j][j] = alpha->data.F64[j][j] * (1.0 + lambda);
772 }
773
774 // solve A*beta = Beta (Alpha = 1/A)
775 // these operations do not modify the input values (creates LUm, LUv)
776 LUm = psMatrixLUD (NULL, &LUv, A);
777 Beta = psMatrixLUSolve (Beta, LUm, beta, LUv);
778 Alpha = psMatrixInvert (Alpha, A, &det);
779
780# else
781 // gauss-jordan version
782 psTrace (".pslib.dataManip.psMinLM_GuessABP", 3, "using Gauss-J version");
783
784 // set new guess values (creates matrix A)
785 Beta = psVectorCopy (Beta, beta, PS_TYPE_F64);
786 Alpha = psImageCopy (Alpha, alpha, PS_TYPE_F64);
787 for (int j = 0; j < params->n; j++) {
788 Alpha->data.F64[j][j] = alpha->data.F64[j][j] * (1.0 + lambda);
789 }
790
791 psGaussJordan (Alpha, Beta);
792# endif
793
794 // apply beta to get new params values
795 for (int j = 0; j < params->n; j++) {
796 Params->data.F32[j] = params->data.F32[j] - Beta->data.F64[j];
797 }
798
799# if (USE_LU_DECOMP)
800 psFree (A);
801 psFree (LUm);
802 psFree (LUv);
803# endif
804
805 return true;
806}
807
808# define SWAP(X,Y) {double tmp=(X); (X) = (Y); (Y) = tmp;}
809
810// XXX EAM : temporary gauss-jordan solver based on gene's
811// version based on the Numerical Recipes version
812bool psGaussJordan (psImage *a, psVector *b) {
813
814 int *indxc,*indxr,*ipiv;
815 int Nx, icol, irow;
816 int i, j, k, l, ll;
817 float big, dum, pivinv;
818 psF64 *vector;
819 psF64 **matrix;
820
821 Nx = a->numCols;
822 matrix = a->data.F64;
823 vector = b->data.F64;
824
825 indxc = psAlloc (Nx*sizeof(int));
826 indxr = psAlloc (Nx*sizeof(int));
827 ipiv = psAlloc (Nx*sizeof(int));
828 for (j = 0; j < Nx; j++) ipiv[j] = 0;
829
830 irow = icol = 0;
831 big = fabs(matrix[0][0]);
832
833 for (i = 0; i < Nx; i++) {
834 big = 0.0;
835 for (j = 0; j < Nx; j++) {
836 if (!finite(matrix[i][j])) {
837 // XXX EAM: this should use the psError stack
838 fprintf (stderr, "GAUSSJ: NaN\n");
839 goto fescape;
840 }
841 if (ipiv[j] != 1) {
842 for (k = 0; k < Nx; k++) {
843 if (ipiv[k] == 0) {
844 if (fabs (matrix[j][k]) >= big) {
845 big = fabs (matrix[j][k]);
846 irow = j;
847 icol = k;
848 }
849 } else {
850 if (ipiv[k] > 1) {
851 // XXX EAM: this should use the psError stack
852 fprintf (stderr, "GAUSSJ: Singular Matrix! (1)\n");
853 goto fescape;
854 }
855 }
856 }
857 }
858 }
859 ipiv[icol]++;
860 if (irow != icol) {
861 for (l = 0; l < Nx; l++) {
862 SWAP (matrix[irow][l], matrix[icol][l]);
863 }
864 SWAP (vector[irow], vector[icol]);
865 }
866 indxr[i] = irow;
867 indxc[i] = icol;
868 if (matrix[icol][icol] == 0.0) {
869 // XXX EAM: this should use the psError stack
870 fprintf (stderr, "GAUSSJ: Singular Matrix! (2)\n");
871 goto fescape;
872 }
873 pivinv = 1.0 / matrix[icol][icol];
874 matrix[icol][icol] = 1.0;
875 for (l = 0; l < Nx; l++) {
876 matrix[icol][l] *= pivinv;
877 }
878 vector[icol] *= pivinv;
879
880 for (ll = 0; ll < Nx; ll++) {
881 if (ll != icol) {
882 dum = matrix[ll][icol];
883 matrix[ll][icol] = 0.0;
884 for (l = 0; l < Nx; l++)
885 matrix[ll][l] -= matrix[icol][l]*dum;
886 vector[ll] -= vector[icol]*dum;
887 }
888 }
889 }
890
891 for (l = Nx - 1; l >= 0; l--) {
892 if (indxr[l] != indxc[l])
893 for (k = 0; k < Nx; k++)
894 SWAP (matrix[k][indxr[l]], matrix[k][indxc[l]]);
895 }
896 psFree (ipiv);
897 psFree (indxr);
898 psFree (indxc);
899 return (true);
900
901fescape:
902 psFree (ipiv);
903 psFree (indxr);
904 psFree (indxc);
905 return (false);
906}
907
908/******************************************************************************
909psMinimizeLMChi2(): This routine will take an procedure which calculates
910an arbitrary function and it's derivative and minimize the chi-squared match
911between that function at the specified coords and the specified value at
912those coords.
913
914XXX: Do this:
915 After checking that all entries in the paramMask are 1 or 0, when
916 forming the A matrix from alpha, try this:
917
918 A[i][i] = (1 + lambda*paramask[i]) * alpha[i][i];
919
920XXX: This is very different from what is specified in the SDR. Must
921coordinate with IfA on new SDR.
922
923XXX: Do vector/image recycles.
924
925XXX: probably yErr will be part of the SDR.
926
927XXX: This must work for both F32 and F64. F32 is currently implemented.
928 Note: since the LUD routines are only implemented in F64, then we
929 will have to convert all F32 input vectors to F64 regardless. So,
930 the F64 port might be.
931
932XXX: Must update the covar matrix.
933 *****************************************************************************/
934psBool psMinimizeLMChi2Old(psMinimization *min,
935 psImage *covar,
936 psVector *params,
937 const psVector *paramMask,
938 const psArray *x,
939 const psVector *y,
940 const psVector *yErr,
941 psMinimizeLMChi2Func func)
942{
943 PS_PTR_CHECK_NULL(min, NULL);
944 PS_VECTOR_CHECK_NULL(params, NULL);
945 PS_VECTOR_CHECK_EMPTY(params, NULL);
946 PS_PTR_CHECK_NULL(x, NULL);
947 PS_VECTOR_CHECK_NULL(y, NULL);
948 PS_VECTOR_CHECK_EMPTY(y, NULL);
949 PS_VECTOR_CHECK_SIZE_EQUAL(x, y, NULL);
950 PS_PTR_CHECK_NULL(func, NULL);
951
952 if (paramMask != NULL) {
953 PS_VECTOR_CHECK_SIZE_EQUAL(params, paramMask, NULL);
954 }
955 if (yErr != NULL) {
956 PS_VECTOR_CHECK_SIZE_EQUAL(y, yErr, NULL);
957 }
958 if (covar != NULL) {
959 PS_IMAGE_CHECK_SIZE(covar, params->n, params->n, NULL);
960 }
961
962 psTrace(".psLib.dataManip.psMinimize", 4,
963 "---- psMinimizeLMChi2() begin ----\n");
964 psS32 numData = y->n;
965 psS32 numParams = params->n;
966 psS32 i;
967 psS32 j;
968 psS32 k;
969 psS32 l;
970 psS32 n;
971 psS32 p;
972 psVector *beta = psVectorAlloc(numParams, PS_TYPE_F64);
973 psVector *perm = NULL;
974
975 psVector *paramDeltasF64 = psVectorAlloc(numParams, PS_TYPE_F64);
976 psVector *origParams = psVectorAlloc(numParams, PS_TYPE_F32);
977 psVector *newParams = psVectorAlloc(numParams, PS_TYPE_F32);
978
979 psImage *alpha = psImageAlloc(numParams, numParams, PS_TYPE_F32);
980 psImage *A = psImageAlloc(numParams, numParams, PS_TYPE_F64);
981 psImage *aOut = psImageAlloc(numParams, numParams, PS_TYPE_F64);
982 psImage *deriv = psImageAlloc(numParams, numData, PS_TYPE_F32);
983 psVector *currValueVec = NULL;
984 psVector *newValueVec = NULL;
985 psF32 currChi2 = 0.0;
986 psF32 newChi2 = 0.0;
987 psF32 lamda = 0.00005; // XXX EAM : this starting value is VERY small (lamda is mis-spelt)
988 lamda = 0.05; // XXX EAM : this starting value is quite large (lamda is mis-spelt)
989
990 psTrace(".psLib.dataManip.psMinimize", 6,
991 "min->maxIter is %d\n", min->maxIter);
992 psTrace(".psLib.dataManip.psMinimize", 6,
993 "min->tol is %f\n", min->tol);
994
995 for (p=0;p<numParams;p++) {
996 origParams->data.F32[p] = params->data.F32[p];
997 }
998
999 min->lastDelta = PS_MAX_F32;
1000 min->iter = 0;
1001
1002 while ((min->lastDelta > min->tol) && (min->iter < min->maxIter)) {
1003 psTrace(".psLib.dataManip.psMinimize", 4,
1004 "------------------------------------------------------\n");
1005 psTrace(".psLib.dataManip.psMinimize", 4,
1006 "Iteration %d. Delta is %f\n", min->iter, min->lastDelta);
1007
1008 //
1009 // Calculate the current values and chi-squared of the function.
1010 //
1011 currChi2 = 0.0;
1012 // currValueVec = func(deriv, params, x);
1013
1014 // XXX EAM: use BinaryOp ?
1015 // t1 = BinaryOp (NULL, currValueVec, "-", y);
1016 // t1 = BinaryOp (t1, t1, "*", t1);
1017
1018 // XXX EAM: this ignores yErr
1019 for (n=0;n<numData;n++) {
1020 currChi2+= (currValueVec->data.F32[n] - y->data.F32[n]) *
1021 (currValueVec->data.F32[n] - y->data.F32[n]);
1022 psTrace(".psLib.dataManip.psMinimize", 6,
1023 "data[%d], chi2 calculation+= (%f - %f)^2\n", n,
1024 currValueVec->data.F32[n], y->data.F32[n]);
1025 }
1026
1027 // XXX EAM: this is just for tracing
1028 for (p=0;p<numParams;p++) {
1029 psTrace(".psLib.dataManip.psMinimize", 6,
1030 "params->data.F32[%d] is %f.\n", p, params->data.F32[p]);
1031 }
1032 psTrace(".psLib.dataManip.psMinimize", 6,
1033 "Current chi-squared is (%f)\n", currChi2);
1034
1035 //
1036 // Mask elements of the derivative for each data point.
1037 // XXX EAM : is this necessary? probably not...
1038 for (p=0;p<numParams;p++) {
1039 if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) {
1040 for (n=0;n<numData;n++) {
1041 deriv->data.F32[n][p] = 0.0;
1042 }
1043 }
1044 }
1045
1046 //
1047 // Calculate the BETA vector.
1048 // XXX EAM: I think this is wrong
1049 for (p=0;p<numParams;p++) {
1050 if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) {
1051 continue;
1052 }
1053 beta->data.F64[p] = 0.0;
1054 for (n=0;n<numData;n++) {
1055 (beta->data.F64[p])+=
1056 (y->data.F32[n] - currValueVec->data.F32[n]) *
1057 deriv->data.F32[n][p];
1058 }
1059 // XXX: multiply by -1 here?
1060 (beta->data.F64[p])*= -1.0;
1061 psTrace(".psLib.dataManip.psMinimize", 6,
1062 "beta->data.F64[%d] is %f.\n", p, beta->data.F64[p]);
1063 }
1064 psFree(currValueVec);
1065
1066 //
1067 // Calculate the ALPHA matrix.
1068 // XXX EAM: also wrong? (missing yErr)
1069 for (k=0;k<numParams;k++) {
1070 for (l=0;l<numParams;l++) {
1071 alpha->data.F32[k][l] = 0.0;
1072 for (n=0;n<numData;n++) {
1073 alpha->data.F32[k][l]+= deriv->data.F32[n][k] *
1074 deriv->data.F32[n][l];
1075 }
1076 }
1077 }
1078
1079 //
1080 // Calculate the matrix A.
1081 //
1082 for (j=0;j<numParams;j++) {
1083 for (k=0;k<numParams;k++) {
1084 if (j == k) {
1085 A->data.F64[j][k] =
1086 (psF64) ((1.0 + lamda) * alpha->data.F32[j][k]);
1087 } else {
1088 A->data.F64[j][k] = (psF64) alpha->data.F32[j][k];
1089 }
1090 }
1091 }
1092 for (j=0;j<numParams;j++) {
1093 psTrace(".psLib.dataManip.psMinimize", 6, "Matrix A[][]:\n");
1094 for (k=0;k<numParams;k++) {
1095 psTrace(".psLib.dataManip.psMinimize", 6, "%f ", A->data.F64[j][k]);
1096 }
1097 psTrace(".psLib.dataManip.psMinimize", 6, "Matrix A[][]:\n");
1098 }
1099
1100 //
1101 // Solve A * alpha = Beta
1102 //
1103 // XXX: How do we know if these functions were successful?
1104 //
1105 aOut = psMatrixLUD(aOut, &perm, A);
1106 paramDeltasF64 = psMatrixLUSolve(paramDeltasF64, aOut, beta, perm);
1107
1108 //
1109 // Mask any masked parameters.
1110 //
1111 for (i=0;i<numParams;i++) {
1112 psTrace(".psLib.dataManip.psMinimize", 6,
1113 "paramDeltasF64->data.F64[%d] is %f.\n", i, paramDeltasF64->data.F64[i]);
1114 if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) {
1115 newParams->data.F32[i] = origParams->data.F32[i];
1116 } else {
1117 newParams->data.F32[i] = params->data.F32[i] -
1118 (psF32) paramDeltasF64->data.F64[i];
1119 }
1120 }
1121
1122 psTrace(".psLib.dataManip.psMinimize", 6,
1123 "Calling func() with new parameters:\n");
1124 for (i=0;i<numParams;i++) {
1125 psTrace(".psLib.dataManip.psMinimize", 6,
1126 "newParams->data.F32[%d] is %f.\n", i, newParams->data.F32[i]);
1127 }
1128
1129
1130 //
1131 // Calculate new function values.
1132 //
1133 newChi2 = 0.0;
1134 // newValueVec = func(deriv, newParams, x);
1135 for (n=0;n<numData;n++) {
1136 newChi2+= (newValueVec->data.F32[n] - y->data.F32[n]) *
1137 (newValueVec->data.F32[n] - y->data.F32[n]);
1138
1139 }
1140 psFree(newValueVec);
1141
1142 psTrace(".psLib.dataManip.psMinimize", 4,
1143 "old/new chi-squareds are (%f, %f)\n", currChi2, newChi2);
1144
1145 //
1146 // If the new chi-squared is lower, then keep it.
1147 //
1148 if (currChi2 > newChi2) {
1149 min->lastDelta = (currChi2 - newChi2)/currChi2;
1150 min->value = newChi2;
1151
1152 // We already masked params.
1153 for (i=0;i<numParams;i++) {
1154 params->data.F32[i] = (psF32) newParams->data.F32[i];
1155 }
1156 lamda*= 0.1;
1157 psTrace(".psLib.dataManip.psMinimize", 4, "*** Reducing lamda by factor of 10\n");
1158 } else {
1159 lamda*= 10.0;
1160 psTrace(".psLib.dataManip.psMinimize", 4, "*** Increasing lamda by factor of 10\n");
1161 }
1162 psTrace(".psLib.dataManip.psMinimize", 4,
1163 "lamda is %f\n", lamda);
1164 min->iter++;
1165 }
1166 psFree(beta);
1167 psFree(perm);
1168 psFree(paramDeltasF64);
1169 psFree(origParams);
1170 psFree(newParams);
1171 psFree(alpha);
1172 psFree(A);
1173 psFree(aOut);
1174 psFree(deriv);
1175
1176 if ((min->iter < min->maxIter) ||
1177 (min->lastDelta <= min->tol)) {
1178 return(true);
1179 }
1180
1181 psTrace(".psLib.dataManip.psMinimize", 4,
1182 "---- psMinimizeLMChi2() end (false) ----\n");
1183 return(false);
1184}
1185
1186/******************************************************************************
1187VectorFitPolynomial1DCheb(): This routine will fit a Chebyshev polynomial of
1188degree myPoly to the data points (x, y) and return the coefficients of that
1189polynomial.
1190
1191XXX: yErr is currently ignored.
1192
1193XXX: Use private name?
1194*****************************************************************************/
1195psPolynomial1D *VectorFitPolynomial1DCheby(psPolynomial1D* myPoly,
1196 const psVector* x,
1197 const psVector* y,
1198 const psVector* yErr)
1199{
1200 psS32 j;
1201 psS32 k;
1202 psS32 n = x->n;
1203 psF64 fac;
1204 psF64 sum;
1205 PS_VECTOR_GEN_STATIC_RECYCLED(f, n, PS_TYPE_F64);
1206 psScalar *fScalar;
1207 psScalar tmpScalar;
1208 tmpScalar.type.type = PS_TYPE_F64;
1209
1210 // XXX: These assignments appear too simple to warrant code and
1211 // variable declarations. I retain them here to maintain coherence
1212 // with the NR code.
1213 psF64 min = -1.0;
1214 psF64 max = 1.0;
1215 psF64 bma = 0.5 * (max-min); // 1
1216 psF64 bpa = 0.5 * (max+min); // 0
1217
1218 // In this loop, we first calculate the values of X for which the
1219 // Chebyshev polynomials are zero (see NR, section 5.4). Then we
1220 // calculate the value of the function we are fitting the Chebyshev
1221 // polynomials to at those values of X. This is a bit tricky since
1222 // we don't know that function. So, we instead do 3-order LaGrange
1223 // interpolation at the point X for the psVectors x,y for which we
1224 // are fitting this ChebyShev polynomial to.
1225
1226 for (psS32 i=0;i<n;i++) {
1227 // NR 5.8.4
1228 psF64 Y = cos(PS_PI * (0.5 + ((psF32) i)) / ((psF32) n));
1229 psF64 X = (Y + bma + bpa) - 1.0;
1230 tmpScalar.data.F64 = X;
1231
1232 // We interpolate against are tabluated x,y vectors to determine the
1233 // function value at X.
1234 fScalar = p_psVectorInterpolate((psVector *) x,
1235 (psVector *) y,
1236 3,
1237 &tmpScalar);
1238
1239 f->data.F64[i] = fScalar->data.F64;
1240 psFree(fScalar);
1241
1242 psTrace(".psLib.dataManip.VectorFitPolynomial1DCheby", 6,
1243 "(x, X, y, f(X)) is (%f, %f, %f, %f)\n",
1244 x->data.F64[i], X, y->data.F64[i], f->data.F64[i]);
1245 }
1246
1247 // We have the values for f() at the zero points, we now calculate the
1248 // coefficients of the Chebyshev polynomial: NR 5.8.7.
1249
1250 fac = 2.0/((psF32) n);
1251 // XXX: is this loop bound correct?
1252 for (j=0;j<myPoly->n;j++) {
1253 sum = 0.0;
1254 for (k=0;k<n;k++) {
1255 sum+= f->data.F64[k] *
1256 cos(PS_PI * ((psF32) j) * (0.5 + ((psF32) k)) / ((psF32) n));
1257 }
1258
1259 myPoly->coeff[j] = fac * sum;
1260 }
1261
1262 return(myPoly);
1263}
1264
1265/******************************************************************************
1266VectorFitPolynomial1DOrd(): This routine will fit an ordinary polynomial of
1267degree myPoly to the data points (x, y) and return the coefficients of that
1268polynomial.
1269
1270XXX: Use private name?
1271XXX: Use recycled vectors.
1272 *****************************************************************************/
1273psPolynomial1D* VectorFitPolynomial1DOrd(psPolynomial1D* myPoly,
1274 const psVector* x,
1275 const psVector* y,
1276 const psVector* yErr)
1277{
1278 psS32 polyOrder = myPoly->n;
1279 psImage* A = NULL;
1280 psImage* ALUD = NULL;
1281 psVector* B = NULL;
1282 psVector* outPerm = NULL;
1283 psVector* X = NULL; // NOTE: do we need this?
1284 psVector* coeffs = NULL;
1285 psS32 i = 0;
1286 psS32 j = 0;
1287 psS32 k = 0;
1288 psVector* xSums = NULL;
1289
1290 psTrace(".psLib.dataManip.VectorFitPolynomial1DOrd", 4,
1291 "---- VectorFitPolynomial1DOrd() begin ----\n");
1292 // printf("VectorFitPolynomial1D()\n");
1293 // for (i=0;i<x->n;i++) {
1294 // printf("(x, y, yErr) is (%f, %f, %f)\n", x->data.F64[i], y->data.F64[i], yErr->data.F64[i]);
1295 // }
1296
1297 A = psImageAlloc(polyOrder, polyOrder, PS_TYPE_F64);
1298 ALUD = psImageAlloc(polyOrder, polyOrder, PS_TYPE_F64);
1299
1300 B = psVectorAlloc(polyOrder, PS_TYPE_F64);
1301 coeffs = psVectorAlloc(polyOrder, PS_TYPE_F64);
1302 X = psVectorAlloc(x->n, PS_TYPE_F64);
1303 xSums = psVectorAlloc(1 + 2 * polyOrder, PS_TYPE_F64);
1304
1305 // Initialize data structures.
1306 for (i = 0; i < polyOrder; i++) {
1307 B->data.F64[i] = 0.0;
1308 coeffs->data.F64[i] = 0.0;
1309 for (j = 0; j < polyOrder; j++) {
1310 A->data.F64[i][j] = 0.0;
1311 ALUD->data.F64[i][j] = 0.0;
1312 }
1313 }
1314 for (i = 0; i < X->n; i++) {
1315 X->data.F64[i] = x->data.F64[i];
1316 }
1317
1318 // Build the B and A data structs.
1319 if (yErr == NULL) {
1320 for (i = 0; i < X->n; i++) {
1321 psBuildSums1D(X->data.F64[i], 2 * polyOrder, xSums);
1322
1323 for (k = 0; k < polyOrder; k++) {
1324 B->data.F64[k] += y->data.F64[i] * xSums->data.F64[k];
1325 }
1326
1327 for (k = 0; k < polyOrder; k++) {
1328 for (j = 0; j < polyOrder; j++) {
1329 A->data.F64[k][j] += xSums->data.F64[k + j];
1330 }
1331 }
1332 }
1333 } else {
1334 for (i = 0; i < X->n; i++) {
1335 psBuildSums1D(X->data.F64[i], 2 * polyOrder, xSums);
1336
1337 for (k = 0; k < polyOrder; k++) {
1338 B->data.F64[k] += y->data.F64[i] * xSums->data.F64[k] /
1339 yErr->data.F64[i];
1340 }
1341
1342 for (k = 0; k < polyOrder; k++) {
1343 for (j = 0; j < polyOrder; j++) {
1344 A->data.F64[k][j] += xSums->data.F64[k + j] /
1345 yErr->data.F64[i];
1346 }
1347 }
1348 }
1349 }
1350
1351 // XXX: How do we know if these routines were successful?
1352 ALUD = psMatrixLUD(ALUD, &outPerm, A);
1353 coeffs = psMatrixLUSolve(coeffs, ALUD, B, outPerm);
1354
1355 for (k = 0; k < polyOrder; k++) {
1356 myPoly->coeff[k] = coeffs->data.F64[k];
1357 // printf("myPoly->coeff[%d] is %f\n", k, myPoly->coeff[k]);
1358 }
1359
1360 psFree(A);
1361 psFree(ALUD);
1362 psFree(B);
1363 psFree(coeffs);
1364 psFree(X);
1365 psFree(outPerm);
1366 psFree(xSums);
1367
1368 psTrace(".psLib.dataManip.VectorFitPolynomial1DOrd", 4,
1369 "---- VectorFitPolynomial1DOrd() begin ----\n");
1370 return (myPoly);
1371}
1372
1373/******************************************************************************
1374psVectorFitPolynomial1D(): This routine must fit a polynomial of degree
1375myPoly to the data points (x, y) and return the coefficients of that
1376polynomial.
1377
1378XXX: type F32 is done via vector conversion only.
1379 *****************************************************************************/
1380psPolynomial1D* psVectorFitPolynomial1D(psPolynomial1D* myPoly,
1381 const psVector* x,
1382 const psVector* y,
1383 const psVector* yErr)
1384{
1385 PS_POLY_CHECK_NULL(myPoly, NULL);
1386 PS_INT_CHECK_NON_NEGATIVE(myPoly->n, NULL);
1387 PS_VECTOR_CHECK_NULL(y, NULL);
1388 PS_VECTOR_CHECK_EMPTY(y, NULL);
1389 PS_VECTOR_CHECK_TYPE_F32_OR_F64(y, NULL);
1390
1391 psS32 i;
1392 psVector *x64 = NULL;
1393 psVector *y64 = NULL;
1394 psVector *yErr64 = NULL;
1395 static psVector *x64Static = NULL;
1396 static psVector *y64Static = NULL;
1397 static psVector *yErr64Static = NULL;
1398
1399 PS_VECTOR_CONVERT_F32_TO_F64_STATIC(y, y64, y64Static);
1400 // If yErr==NULL, set all errors equal.
1401 if (yErr == NULL) {
1402 PS_VECTOR_GEN_YERR_STATIC_F64(yErr64Static, y->n);
1403 yErr64 = yErr64Static;
1404 } else {
1405 PS_VECTOR_CHECK_TYPE_F32_OR_F64(yErr, NULL);
1406 PS_VECTOR_CONVERT_F32_TO_F64_STATIC(yErr, yErr64, yErr64Static);
1407 }
1408
1409 // If x==NULL, create an x64 vector with x values set to (0:n).
1410 if (x == NULL) {
1411 PS_VECTOR_GEN_X_INDEX_STATIC_F64(x64Static, y->n);
1412 if (myPoly->type == PS_POLYNOMIAL_CHEB) {
1413 p_psNormalizeVectorRangeF64(x64Static, -1.0, 1.0);
1414 }
1415 x64 = x64Static;
1416 } else {
1417 PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
1418 PS_VECTOR_CONVERT_F32_TO_F64_STATIC(x, x64, x64Static);
1419 if (myPoly->type == PS_POLYNOMIAL_CHEB) {
1420 p_psNormalizeVectorRangeF64(x64, -1.0, 1.0);
1421 }
1422 }
1423 PS_VECTOR_CHECK_SIZE_EQUAL(x64, y64, NULL);
1424 PS_VECTOR_CHECK_SIZE_EQUAL(yErr64, y64, NULL);
1425
1426 // Call the appropriate vector fitting routine.
1427 psPolynomial1D *rc = NULL;
1428 if (myPoly->type == PS_POLYNOMIAL_CHEB) {
1429 rc = VectorFitPolynomial1DCheby(myPoly, x64, y64, yErr64);
1430 } else if (myPoly->type == PS_POLYNOMIAL_ORD) {
1431 rc = VectorFitPolynomial1DOrd(myPoly, x64, y64, yErr64);
1432 } else {
1433 psError(PS_ERR_BAD_PARAMETER_VALUE, true,
1434 "unknown polynomial type.\n");
1435 return(NULL);
1436 }
1437 if (rc == NULL) {
1438 psError(PS_ERR_UNKNOWN, true, "Could not fit a polynomial to the data. Returning NULL.\n");
1439 return(NULL);
1440 }
1441
1442 return(myPoly);
1443}
1444
1445
1446
1447/******************************************************************************
1448 *****************************************************************************/
1449psMinimization *psMinimizationAlloc(psS32 maxIter,
1450 psF32 tol)
1451{
1452 PS_INT_CHECK_NON_NEGATIVE(maxIter, NULL);
1453
1454 psMinimization *min = psAlloc(sizeof(psMinimization));
1455 min->maxIter = maxIter;
1456 min->tol = tol;
1457 min->value = 0.0;
1458 min->iter = 0;
1459 min->lastDelta = tol + 1;
1460
1461 return(min);
1462}
1463
1464// This macro takes as input the vector BASE and adds a multiple of the vector
1465// LINE to it. We assume BASEMASK is non-null.
1466#define PS_VECTOR_ADD_MULTIPLE(BASE, BASEMASK, LINE, OUT, MUL) \
1467for (psS32 i=0;i<BASE->n;i++) { \
1468 if (BASEMASK->data.U8[i] == 0) { \
1469 OUT->data.F32[i] = BASE->data.F32[i] + (MUL * LINE->data.F32[i]); \
1470 } else { \
1471 OUT->data.F32[i] = BASE->data.F32[i]; \
1472 } \
1473} \
1474
1475#define PS_VECTOR_F32_CHECK_ZERO_VECTOR(IN, BOOL_VAR) \
1476BOOL_VAR = true; \
1477for (psS32 i=0;i<IN->n;i++) { \
1478 if (fabs(IN->data.F32[i]) >= FLT_EPSILON) { \
1479 BOOL_VAR = false; \
1480 break; \
1481 } \
1482} \
1483
1484#define PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(IN, INMASK, BOOL_VAR) \
1485BOOL_VAR = true; \
1486for (psS32 i=0;i<IN->n;i++) { \
1487 if ((INMASK->data.U8[i] == 0) && (fabs(IN->data.F32[i]) >= FLT_EPSILON)) { \
1488 BOOL_VAR = false; \
1489 break; \
1490 } \
1491} \
1492
1493
1494/******************************************************************************
1495p_psDetermineBracket(): This routine takes as input an arbitrary function,
1496and the parameter to vary, and the line along which it must vary. This
1497function produces as output a bracket [a, b, c] such that
1498f(param + b * line) < f(param + a * line)
1499f(param + b * line) < f(param + c * line)
1500a < b < c
1501
1502Algorithm:
1503
1504XXX completely ad hoc:
1505start with the user-supplied starting parameter and
1506call that b. Calculate a/c as a fractional amount smaller/larger than b.
1507Repeat this process until a local minimum is found.
1508
1509XXX:
1510new algorithm:
1511start at x=0, expand in one direction until the function
1512decreases. Then you have two points in the bracket. Keep going until it
1513increases, or x is too large. If thst does not work, expand in the other
1514direction.
1515
1516XXX:
1517This is F32 only.
1518
1519XXX:
1520output bracket vector should be an input as well.
1521*****************************************************************************/
1522psVector *p_psDetermineBracket(psVector *params,
1523 psVector *line,
1524 const psVector *paramMask,
1525 const psArray *coords,
1526 psMinimizePowellFunc func)
1527{
1528 psF32 a = 0.0;
1529 psF32 b = 0.0;
1530 psF32 c = 0.0;
1531 psF32 fa = 0.0;
1532 psF32 fb = 0.0;
1533 psF32 fc = 0.0;
1534 psS32 iter = 100;
1535 psF32 aDir = 0.0;
1536 psF32 cDir = 0.0;
1537 psF32 new_aDir = 0.0;
1538 psF32 new_cDir = 0.0;
1539 psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);
1540 psF32 stepSize = PS_DETERMINE_BRACKET_STEP_SIZE;
1541 psVector *tmp = NULL;
1542 psBool boolLineIsNull = true;
1543
1544 psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
1545 "---- p_psDetermineBracket() begin ----\n");
1546
1547 // If the line vector is zero, then return NULL.
1548 PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(params, paramMask, boolLineIsNull);
1549 if (boolLineIsNull == true) {
1550 psTrace(".psLib.dataManip.p_psDetermineBracket", 2,
1551 "p_psDetermineBracket() called with zero line vector.\n");
1552 psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
1553 "---- p_psDetermineBracket() end (NULL) ----\n");
1554 psFree(bracket);
1555 return(NULL);
1556 }
1557
1558 tmp = psVectorAlloc(params->n, PS_TYPE_F32);
1559
1560 b = 0;
1561 a = -stepSize;
1562 c = stepSize;
1563
1564 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, a);
1565 fa = func(tmp, coords);
1566
1567 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, b);
1568 fb = func(tmp, coords);
1569
1570 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
1571 fc = func(tmp, coords);
1572
1573 if (fa < fb) {
1574 aDir = -1;
1575 } else {
1576 aDir = 1;
1577 }
1578
1579 if (fc < fb) {
1580 cDir = -1;
1581 } else {
1582 cDir = 1;
1583 }
1584
1585 psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
1586 "(a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", a, b, c, fa, fb, fc);
1587
1588 while (iter > 0) {
1589 psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
1590 "psDetermineBracket(): iteration %d\n", iter);
1591 if ((fb < fa) && (fb < fc)) {
1592 bracket->data.F32[0] = a;
1593 bracket->data.F32[1] = b;
1594 bracket->data.F32[2] = c;
1595 psFree(tmp);
1596 psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
1597 "---- p_psDetermineBracket() end ----\n");
1598 return(bracket);
1599 }
1600 stepSize*= (1.0 + stepSize);
1601 a =- stepSize;
1602 c =+ stepSize;
1603
1604 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, a);
1605 fa = func(tmp, coords);
1606
1607 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
1608 fc = func(tmp, coords);
1609
1610 psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
1611 "Iter(%d): (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", iter, a, b, c, fa, fb, fc);
1612
1613 if (fa < fb) {
1614 new_aDir = -1;
1615 } else {
1616 new_aDir = 1;
1617 }
1618
1619 if (fc < fb) {
1620 new_cDir = -1;
1621 } else {
1622 new_cDir = 1;
1623 }
1624 if ((new_aDir == 1) && (aDir == -1)) {
1625 bracket->data.F32[0] = a;
1626 bracket->data.F32[1] = b;
1627 bracket->data.F32[2] = c;
1628 psFree(tmp);
1629 psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
1630 "---- p_psDetermineBracket() end ----\n");
1631 return(bracket);
1632 }
1633
1634 if ((new_cDir == 1) && (cDir == -1)) {
1635 bracket->data.F32[0] = a;
1636 bracket->data.F32[1] = b;
1637 bracket->data.F32[2] = c;
1638 psFree(tmp);
1639 psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
1640 "---- p_psDetermineBracket() end ----\n");
1641 return(bracket);
1642 }
1643 aDir = new_aDir;
1644 cDir = new_cDir;
1645 iter--;
1646 }
1647 psFree(tmp);
1648 psFree(bracket);
1649 psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
1650 "---- p_psDetermineBracket() end (NULL) ----\n");
1651 return(NULL);
1652}
1653
1654
1655#define RETURN_FINAL_BRACKET(d) \
1656if (a < c) { \
1657 bracket->data.F32[0] = a; \
1658 bracket->data.F32[1] = b; \
1659 bracket->data.F32[2] = c; \
1660} else { \
1661 bracket->data.F32[0] = c; \
1662 bracket->data.F32[1] = b; \
1663 bracket->data.F32[2] = a; \
1664} \
1665psTrace(".psLib.dataManip.p_psDetermineBracket", 4, \
1666 "---- p_psDetermineBracket() end ----\n"); \
1667psTrace(".psLib.dataManip.p_psDetermineBracket", 4, "Final bracket (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", a, b, c, fa, fb, fc); \
1668return(bracket); \
1669
1670#define PS_DETERMINE_BRACKET_MAX_ITERATIONS 100
1671psVector *p_psDetermineBracket2(psVector *params,
1672 psVector *line,
1673 const psVector *paramMask,
1674 const psArray *coords,
1675 psMinimizePowellFunc func)
1676{
1677 psF32 a = 0.0;
1678 psF32 b = 0.0;
1679 psF32 c = 0.0;
1680 psF32 fa = 0.0;
1681 psF32 fb = 0.0;
1682 psF32 fc = 0.0;
1683 psS32 iter = 0;
1684 PS_VECTOR_GEN_STATIC_RECYCLED(tmp, params->n, PS_TYPE_F32);
1685 psBool boolLineIsNull = true;
1686 psF32 prevMin = 0.0;
1687 psS32 countMin = 0;
1688
1689 psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
1690 "---- p_psDetermineBracket() begin ----\n");
1691
1692 // If the line vector is zero, then return NULL.
1693 PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(params, paramMask, boolLineIsNull);
1694 if (boolLineIsNull == true) {
1695 psTrace(".psLib.dataManip.p_psDetermineBracket", 2,
1696 "p_psDetermineBracket() called with zero line vector.\n");
1697 psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
1698 "---- p_psDetermineBracket() end (NULL) ----\n");
1699 return(NULL);
1700 }
1701
1702 // We determine in what x-direction does the function decrease.
1703 a = 0.0;
1704 fa = func(params, coords);
1705 b = 0.5;
1706 iter = 0;
1707 do {
1708 b*= (1.0 + PS_DETERMINE_BRACKET_STEP_SIZE);
1709 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, b);
1710 fb = func(tmp, coords);
1711 } while ((fabs(fb - fa) < FLT_EPSILON) && (iter++ < 100));
1712
1713 if (fb > fa) {
1714 a = b;
1715 fa = fb;
1716 b = 0.0;
1717 fb = func(params, coords);
1718 }
1719 c = b;
1720
1721 // At this point we have (a, b) and we know that (fa >= fb). Initially, c=b;
1722 // We keep stretching b out further from "a" until (fc > previous fc). If
1723 // that happens, then we have our bracket.
1724 psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);
1725 iter = 0;
1726 while (iter < PS_DETERMINE_BRACKET_MAX_ITERATIONS) {
1727 psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
1728 "psDetermineBracket(): iterationA %d\n", iter);
1729 c+= (1.0 + PS_DETERMINE_BRACKET_STEP_SIZE) * (c - a);
1730
1731 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
1732 fc = func(tmp, coords);
1733
1734 psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
1735 "Iteration(%d) (bracket): (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", iter, a, b, c, fa, fb, fc);
1736
1737 if ((fb < fa) && (fb < fc)) {
1738 RETURN_FINAL_BRACKET();
1739 } else {
1740 b = c;
1741 fb = fc;
1742 }
1743
1744 // This code maintains a count of how many times the minimum fc has
1745 // stayed the same. If it gets too high, we exit this loop.
1746 if (fc == prevMin) {
1747 countMin++;
1748 } else {
1749 countMin = 0;
1750 }
1751 prevMin = fc;
1752 if (countMin == 10) {
1753 RETURN_FINAL_BRACKET();
1754 }
1755
1756 iter++;
1757 }
1758
1759 psFree(bracket);
1760 psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
1761 "---- p_psDetermineBracket() end (NULL) (BAD) ----\n");
1762 return(NULL);
1763}
1764
1765/******************************************************************************
1766This routine takes as input a possibly multi-dimensional function, along
1767with an initial guess at the parameters of that function and vector "line"
1768of the same size as the parameter vector. It will minimize the function
1769along that vector and returns the offset along that vector at which the
1770minimum is determined.
1771
1772XXX: This routine is not very efficient in terms of total evaluations of the
1773function.
1774XXX: This is F32 only
1775XXX: Since this is an internal function, many of the parameter checks are
1776 redundant.
1777XXX: Don't modify the psMinimization argument.
1778 *****************************************************************************/
1779#define PS_LINEMIN_MAX_ITERATIONS 30
1780psF32 p_psLineMin(psMinimization *min,
1781 psVector *params,
1782 psVector *line,
1783 const psVector *paramMask,
1784 const psArray *coords,
1785 psMinimizePowellFunc func)
1786{
1787 PS_PTR_CHECK_NULL(min, NAN);
1788 PS_VECTOR_CHECK_NULL(params, NAN);
1789 PS_VECTOR_CHECK_EMPTY(params, NAN);
1790 PS_VECTOR_CHECK_TYPE(params, PS_TYPE_F32, NAN);
1791 PS_VECTOR_CHECK_NULL(line, NAN);
1792 PS_VECTOR_CHECK_EMPTY(line, NAN);
1793 PS_VECTOR_CHECK_TYPE(line, PS_TYPE_F32, NAN);
1794 PS_VECTOR_CHECK_NULL(paramMask, NAN);
1795 PS_VECTOR_CHECK_EMPTY(paramMask, NAN);
1796 PS_VECTOR_CHECK_TYPE(paramMask, PS_TYPE_U8, NAN);
1797 PS_PTR_CHECK_NULL(coords, NAN);
1798 PS_PTR_CHECK_NULL(func, NAN);
1799 psVector *bracket;
1800 psF32 a = 0.0;
1801 psF32 b = 0.0;
1802 psF32 c = 0.0;
1803 psF32 n = 0.0;
1804 psF32 fa = 0.0;
1805 psF32 fb = 0.0;
1806 psF32 fc = 0.0;
1807 psF32 fn = 0.0;
1808 psF32 mul = 0.0;
1809 PS_VECTOR_GEN_STATIC_RECYCLED(tmpa, params->n, PS_TYPE_F32);
1810 PS_VECTOR_GEN_STATIC_RECYCLED(tmpb, params->n, PS_TYPE_F32);
1811 PS_VECTOR_GEN_STATIC_RECYCLED(tmpc, params->n, PS_TYPE_F32);
1812 PS_VECTOR_GEN_STATIC_RECYCLED(tmpn, params->n, PS_TYPE_F32);
1813 psS32 i = 0;
1814 psS32 boolLineIsNull = true;
1815 psS32 numIterations = 0;
1816
1817 psTrace(".psLib.dataManip.p_psLineMin", 4, "---- p_psLineMin() begin ----\n");
1818 PS_VECTOR_F32_CHECK_ZERO_VECTOR(line, boolLineIsNull);
1819
1820 if (boolLineIsNull == true) {
1821 min->value = func(params, coords);
1822 psTrace(".psLib.dataManip.p_psLineMin", 2,
1823 "p_psLineMin() called with zero line vector. Return 0.0. Function value is %f\n", min->value);
1824 return(0.0);
1825 }
1826
1827 for (i=0;i<params->n;i++) {
1828 psTrace(".psLib.dataManip.p_psLineMin", 6,
1829 "(params, paramMask, line)[%d] is (%f %d %f)\n", i,
1830 params->data.F32[i],
1831 paramMask->data.U8[i],
1832 line->data.F32[i]);
1833 }
1834
1835 bracket = p_psDetermineBracket2(params, line, paramMask, coords, func);
1836 if (bracket == NULL) {
1837 psError(PS_ERR_UNKNOWN, false,
1838 "Could not bracket minimum. Returning NAN.\n");
1839 return(NAN);
1840 }
1841 numIterations = 0;
1842 while (numIterations < PS_LINEMIN_MAX_ITERATIONS) {
1843 numIterations++;
1844 psTrace(".psLib.dataManip.p_psLineMin", 6,
1845 "p_psLineMin(): iteration %d\n", numIterations);
1846
1847 a = bracket->data.F32[0];
1848 b = bracket->data.F32[1];
1849 c = bracket->data.F32[2];
1850 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpa, a);
1851 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpb, b);
1852 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpc, c);
1853 fa = func(tmpa, coords);
1854 fb = func(tmpb, coords);
1855 fc = func(tmpc, coords);
1856 psTrace(".psLib.dataManip.p_psLineMin", 6,
1857 "LineMin: f(%f %f %f) is (%f %f %f)\n", a, b, c, fa, fb, fc);
1858
1859 // We determine which is the biggest segment in [a,b,c] then split
1860 // that with the point n.
1861 if ((b-a) > (c-b)) {
1862 // This is the golden section formula
1863 n = a + (0.69 * (b-a));
1864 for (i=0;i<params->n;i++) {
1865 tmpn->data.F32[i] = params->data.F32[i] + (n * line->data.F32[i]);
1866 }
1867 fn = func(tmpn, coords);
1868
1869 if (fn > fb) {
1870 // a = n, b = b, c = c
1871 bracket->data.F32[0] = n;
1872 } else {
1873 // a = a, b = n, c = b
1874 bracket->data.F32[1] = n;
1875 bracket->data.F32[2] = b;
1876 }
1877 } else {
1878 n = b + (0.69 * (c-b));
1879 for (i=0;i<params->n;i++) {
1880 tmpn->data.F32[i] = params->data.F32[i] + (n * line->data.F32[i]);
1881 }
1882 fn = func(tmpn, coords);
1883
1884 if (fn > fb) {
1885 // a = a, b = b, c = n
1886 bracket->data.F32[2] = n;
1887 } else {
1888 // a = b, b = n, c = c
1889 bracket->data.F32[0] = b;
1890 bracket->data.F32[1] = n;
1891 }
1892 }
1893 psTrace(".psLib.dataManip.p_psLineMin", 6,
1894 "LineMin: new bracket is (%f %f %f)\n", bracket->data.F32[0], bracket->data.F32[1], bracket->data.F32[2]);
1895
1896 mul = bracket->data.F32[1];
1897 if ((fabs(a-b) < min->tol) && (fabs(b-c) < min->tol)) {
1898 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
1899 min->value = func(params, coords);
1900 psFree(bracket);
1901 psTrace(".psLib.dataManip.p_psLineMin", 4,
1902 "---- p_psLineMin() end.a (%f) (%f) ----\n", mul, min->value);
1903 return(mul);
1904 }
1905 }
1906
1907 mul = bracket->data.F32[1];
1908 PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
1909 min->value = func(params, coords);
1910 psTrace(".psLib.dataManip.p_psLineMin", 4,
1911 "---- p_psLineMin() end.b (%f) %f ----\n", mul, min->value);
1912
1913 psFree(bracket);
1914 return(mul);
1915}
1916
1917
1918/******************************************************************************
1919This routine must minimize a possibly multi-dimensional function. The
1920function to be minimized "func" is:
1921 psF32 func(psVector *params, psArray *coords)
1922The "params" are the parameters of the function which are varied. The data
1923points at which the function is varied are in the argument "coords" which is
1924a psArray of psVectors: each vector represents a different coordinate.
1925
1926XXX: We do not use Brent's method.
1927
1928XXX: The SDR is silent about data types. F32 is implemented here.
1929
1930XXX: Check for F32 types?
1931 *****************************************************************************/
1932#define PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS 20
1933#define PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE 0.01
1934
1935psBool psMinimizePowell(psMinimization *min,
1936 psVector *params,
1937 const psVector *paramMask,
1938 const psArray *coords,
1939 psMinimizePowellFunc func)
1940{
1941 PS_PTR_CHECK_NULL(min, NULL);
1942 PS_VECTOR_CHECK_NULL(params, NULL);
1943 PS_VECTOR_CHECK_EMPTY(params, NULL);
1944 PS_VECTOR_CHECK_TYPE(params, PS_TYPE_F32, NULL);
1945 PS_PTR_CHECK_NULL(coords, NULL);
1946 PS_PTR_CHECK_NULL(func, NULL);
1947 psS32 numDims = params->n;
1948 PS_VECTOR_GEN_STATIC_RECYCLED(pQP, numDims, PS_TYPE_F32);
1949 PS_VECTOR_GEN_STATIC_RECYCLED(u, numDims, PS_TYPE_F32);
1950 PS_VECTOR_GEN_STATIC_RECYCLED(Q, numDims, PS_TYPE_F32);
1951 psS32 i = 0;
1952 psS32 j = 0;
1953 psVector *myParamMask = NULL;
1954 psMinimization dummyMin;
1955 psF32 mul = 0.0;
1956 psF32 baseFuncVal = 0.0;
1957 psF32 currFuncVal = 0.0;
1958 psS32 biggestIter = 0;
1959 psF32 biggestDiff = 0.0;
1960 psS32 iterationNumber = 0;
1961
1962 psTrace(".psLib.dataManip.psMinimizePowell", 4,
1963 "---- psMinimizePowell() begin ----\n");
1964 psTrace(".psLib.dataManip.psMinimizePowell", 6,
1965 "min->maxIter is %d\n", min->maxIter);
1966 psTrace(".psLib.dataManip.psMinimizePowell", 6,
1967 "min->tol is %f\n", min->tol);
1968
1969 if (paramMask == NULL) {
1970 myParamMask = psVectorRecycle(myParamMask, params->n, PS_TYPE_U8);
1971 p_psMemSetPersistent(myParamMask, true);
1972 p_psMemSetPersistent(myParamMask->data.U8, true);
1973 for (i=0;i<myParamMask->n;i++) {
1974 myParamMask->data.U8[i] = 0;
1975 }
1976 } else {
1977 myParamMask = (psVector *) paramMask;
1978 }
1979 PS_VECTOR_CHECK_SIZE_EQUAL(params, myParamMask, NULL);
1980
1981 // 1: Set v[i] to be the unit vectors for each dimension in params
1982 psArray *v = psArrayAlloc(numDims);
1983 for (i=0;i<numDims;i++) {
1984 (v->data[i]) = (psVector *) psVectorAlloc(numDims, PS_TYPE_F32);
1985 for (j=0;j<numDims;j++) {
1986 if (i == j) {
1987 ((psVector *) (v->data[i]))->data.F32[j] = 1.0;
1988 } else {
1989 ((psVector *) (v->data[i]))->data.F32[j] = 0.0;
1990 }
1991 }
1992 }
1993
1994 // 2: Set Q to be the initial params (P in the ADD)
1995 for (i=0;i<numDims;i++) {
1996 Q->data.F32[i] = params->data.F32[i];
1997 }
1998
1999 while (iterationNumber < min->maxIter) {
2000 iterationNumber++;
2001 psTrace(".psLib.dataManip.psMinimizePowell", 6,
2002 "psMinimizePowell() iteration %d\n", iterationNumber);
2003
2004 // 3: For each dimension in params, move Q only in the vector v[i] to
2005 // minimize the function.
2006
2007 baseFuncVal = func(Q, coords);
2008 currFuncVal = baseFuncVal;
2009 psTrace(".psLib.dataManip.psMinimizePowell", 6,
2010 "Current function value is %f\n", currFuncVal);
2011
2012 biggestDiff = 0;
2013 biggestIter = 0;
2014 for (i=0;i<numDims;i++) {
2015 if (myParamMask->data.U8[i] == 0) {
2016 dummyMin.maxIter = PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS;
2017 dummyMin.tol = PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE;
2018 mul = p_psLineMin(&dummyMin,
2019 Q,
2020 ((psVector *) v->data[i]),
2021 myParamMask,
2022 coords,
2023 func);
2024 if (isnan(mul)) {
2025 psError(PS_ERR_UNKNOWN, false,
2026 "Could not perform line minimization. Returning FALSE.\n");
2027 psFree(v);
2028 return(false);
2029 }
2030 psTrace(".psLib.dataManip.psMinimizePowell", 6,
2031 "LineMin along dimension %d has multiple %f\n", i, mul);
2032
2033 if (fabs(dummyMin.value - currFuncVal) > biggestDiff) {
2034 biggestDiff = fabs(dummyMin.value - currFuncVal);
2035 biggestIter = i;
2036 }
2037 currFuncVal = dummyMin.value;
2038 }
2039 }
2040 psTrace(".psLib.dataManip.psMinimizePowell", 6,
2041 "New function value is %f\n", currFuncVal);
2042
2043 // 4: Set the vector u = Q - P
2044 for (i=0;i<numDims;i++) {
2045 if (myParamMask->data.U8[i] == 0) {
2046 u->data.F32[i] = Q->data.F32[i] - params->data.F32[i];
2047
2048 psTrace(".psLib.dataManip.psMinimizePowell", 6,
2049 "u[i]=Q[i]-P[i] (%f = %f - %f)\n", u->data.F32[i],
2050 Q->data.F32[i],
2051 params->data.F32[i]);
2052
2053 } else {
2054 u->data.F32[i] = 0.0;
2055 }
2056 }
2057
2058 // 5: Move Q only in the direction u, and minimize the function.
2059 for (i=0;i<numDims;i++) {
2060 psTrace(".psLib.dataManip.psMinimizePowell", 6,
2061 "u[i] is %f\n", u->data.F32[i]);
2062 }
2063
2064 mul = p_psLineMin(&dummyMin, params, u, myParamMask, coords, func);
2065 if (isnan(mul)) {
2066 psError(PS_ERR_UNKNOWN, false,
2067 "Could not perform line minimization. Returning FALSE.\n");
2068 psFree(v);
2069 return(false);
2070 }
2071
2072 // 6:
2073 if (dummyMin.value > currFuncVal) {
2074 psFree(v);
2075 min->iter = iterationNumber;
2076 // XXX: Ensure that currFuncVal is the correct value to use here.
2077 min->value = currFuncVal;
2078 psTrace(".psLib.dataManip.psMinimizePowell", 4,
2079 "---- psMinimizePowell() end (1)(true) ----\n");
2080 return(true);
2081 }
2082
2083 for (i=0;i<numDims;i++) {
2084 if (myParamMask->data.U8[i] == 0) {
2085 pQP->data.F32[i] = (2 * Q->data.F32[i]) - params->data.F32[i];
2086 } else {
2087 pQP->data.F32[i] = params->data.F32[i];
2088 }
2089 }
2090 psF32 fqp = func(pQP, coords);
2091 psF32 term1 = (baseFuncVal - currFuncVal) - biggestDiff;
2092 term1*= term1;
2093 term1*= 2.0 * (baseFuncVal - (2.0 * currFuncVal) + fqp);
2094 psF32 term2 = baseFuncVal - fqp;
2095 term2*= term2 * biggestDiff;
2096 if (term1 < term2) {
2097 for (i=0;i<numDims;i++) {
2098 if (myParamMask->data.U8[i] == 0) {
2099 ((psVector *) v->data[biggestIter])->data.F32[i] = u->data.F32[i];
2100 }
2101 }
2102 }
2103
2104 // 7: Set P to Q
2105 for (i=0;i<numDims;i++) {
2106 if (myParamMask->data.U8[i] == 0) {
2107 params->data.F32[i] = Q->data.F32[i];
2108 }
2109 }
2110
2111 // 8: Go to step 3 until the change is less than some tolerance.
2112 if (fabs(baseFuncVal - currFuncVal) <= min->tol) {
2113 psFree(v);
2114 // XXX: Ensure that currFuncVal is the correct value to use here.
2115 min->value = currFuncVal;
2116 min->iter = iterationNumber;
2117 psTrace(".psLib.dataManip.psMinimizePowell", 4,
2118 "---- psMinimizePowell() end (2) (true) ----\n");
2119 return(true);
2120 }
2121 }
2122
2123 psFree(v);
2124 min->iter = iterationNumber;
2125 psTrace(".psLib.dataManip.psMinimizePowell", 4,
2126 "---- psMinimizePowell() end (0) (false) ----\n");
2127 return(false);
2128}
2129
2130
2131/******************************************************************************
2132XXX: We assume unnormalized gaussians.
2133XXX: Currently, yErr is ignored.
2134 *****************************************************************************/
2135psVector *psMinimizePowellChi2Gauss1D(const psVector *params,
2136 const psArray *coords)
2137{
2138 PS_PTR_CHECK_NULL(coords, NULL);
2139 PS_PTR_CHECK_NULL(params, NULL);
2140
2141 psF32 x;
2142 psS32 i;
2143 psF32 mean = params->data.F32[0];
2144 psF32 stdev = params->data.F32[1];
2145 psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
2146
2147 for (i=0;i<coords->n;i++) {
2148 x = ((psVector *) (coords->data[i]))->data.F32[0];
2149 out->data.F32[i] = psGaussian(x, mean, stdev, false);
2150 }
2151
2152 return(out);
2153}
2154
2155/******************************************************************************
2156This routine is to be used with the psMinimizeChi2Powell() function below.
2157and the psMinimizePowell() function above.
2158
2159The basic idea is calculate chi-squared for a set of params/coords/errors.
2160This functions uses global variables to receive the function pointer, the
2161data values, and the data errors.
2162XXX: This is F32 only
2163 *****************************************************************************/
2164psF32 myPowellChi2Func(const psVector *params,
2165 const psArray *coords)
2166{
2167 psTrace(".psLib.dataManip.myPowellChi2Func", 4,
2168 "---- myPowellChi2Func() begin ----\n");
2169 PS_VECTOR_CHECK_NULL(params, NAN);
2170 PS_VECTOR_CHECK_EMPTY(params, NAN);
2171 PS_VECTOR_CHECK_NULL(myValue, NAN);
2172 PS_VECTOR_CHECK_EMPTY(myValue, NAN);
2173 PS_PTR_CHECK_NULL(coords, NAN);
2174
2175 psF32 chi2 = 0.0;
2176 psF32 d;
2177 psS32 i;
2178 psVector *tmp;
2179
2180 tmp = Chi2PowellFunc(params, coords);
2181 if (myError == NULL) {
2182 for (i=0;i<coords->n;i++) {
2183 d = (tmp->data.F32[i] - myValue->data.F32[i]);
2184 chi2+= d * d;
2185 }
2186 } else {
2187 for (i=0;i<coords->n;i++) {
2188 d = (tmp->data.F32[i] - myValue->data.F32[i]) / myError->data.F32[i];
2189 chi2+= d * d;
2190 }
2191 }
2192 psFree(tmp);
2193 psTrace(".psLib.dataManip.myPowellChi2Func", 4,
2194 "---- myPowellChi2Func() end (chi2 is %f) ----\n", chi2);
2195 return(chi2);
2196}
2197
2198
2199/******************************************************************************
2200This routine must minimize the chi-squared match of a set of data points and
2201values for a possibly multi-dimensional function.
2202
2203The basic idea is to use the psMinimizePowell() function defined above. In
2204order to do so, we defined above a function myPowellChi2Func() which takes
2205the "func" function and returns chi-squared over the params/coords/values.
2206We then use that function myPowellChi2Func() in the call to
2207psMinimizePowell().
2208 *****************************************************************************/
2209psBool psMinimizeChi2Powell(psMinimization *min,
2210 psVector *params,
2211 const psVector *paramMask,
2212 const psArray *coords,
2213 const psVector *value,
2214 const psVector *error,
2215 psMinimizeChi2PowellFunc func)
2216{
2217 myValue = (psVector *) value;
2218 myError = (psVector *) error;
2219
2220 Chi2PowellFunc = func;
2221
2222 return(psMinimizePowell(min, params, paramMask, coords, myPowellChi2Func));
2223}
2224
2225