| 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 | /*****************************************************************************/
|
|---|
| 41 | static psMinimizeChi2PowellFunc Chi2PowellFunc = NULL;
|
|---|
| 42 | static psVector *myValue;
|
|---|
| 43 | static psVector *myError;
|
|---|
| 44 |
|
|---|
| 45 | /*****************************************************************************/
|
|---|
| 46 | /* FILE STATIC VARIABLES */
|
|---|
| 47 | /*****************************************************************************/
|
|---|
| 48 |
|
|---|
| 49 | // None
|
|---|
| 50 |
|
|---|
| 51 | /*****************************************************************************/
|
|---|
| 52 | /* FUNCTION IMPLEMENTATION - LOCAL */
|
|---|
| 53 | /*****************************************************************************/
|
|---|
| 54 |
|
|---|
| 55 | /******************************************************************************
|
|---|
| 56 | p_psBuildSums1D(x, polyOrder, sums): this routine calculates the powers of
|
|---|
| 57 | input parameter "x" between 0 and input parameter polyOrder. The result is
|
|---|
| 58 | returned as a psVector sums.
|
|---|
| 59 |
|
|---|
| 60 | XXX: Use a static vector.
|
|---|
| 61 | *****************************************************************************/
|
|---|
| 62 | void 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 | /*****************************************************************************
|
|---|
| 84 | CalculateSecondDerivs(): Given a set of x/y vectors corresponding to a
|
|---|
| 85 | tabulated function at n points, this routine calculates the second
|
|---|
| 86 | derivatives of the interpolating cubic splines at those n points.
|
|---|
| 87 |
|
|---|
| 88 | The first and second derivatives at the endpoints, undefined in the SDR, are
|
|---|
| 89 | here defined to be 0.0. They can be modified via ypo and yp1.
|
|---|
| 90 |
|
|---|
| 91 | This routine assumes that vectors x and y are of the appropriate types/sizes
|
|---|
| 92 | (F32).
|
|---|
| 93 |
|
|---|
| 94 | XXX: This algorithm is derived from the Numerical Recipes.
|
|---|
| 95 | XXX: use recycled vectors for internal data.
|
|---|
| 96 | XXX: do an F64 version?
|
|---|
| 97 | *****************************************************************************/
|
|---|
| 98 | psF32 *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 | /******************************************************************************
|
|---|
| 153 | p_psNRSpline1DEval(): This routine does NR-style evaluation of cubic splines.
|
|---|
| 154 | It takes advantage of the 2nd derivatives of the cubic splines, which are
|
|---|
| 155 | stored in the psSPline1D data structure, and computes the interpolated value
|
|---|
| 156 | directly, without computing (or using) the interpolating cubic spline
|
|---|
| 157 | polynomial.
|
|---|
| 158 |
|
|---|
| 159 | This routine is here mostly for a sanity check on the psLib function
|
|---|
| 160 | evalSpline() which computes the interpolated value based on the cubic spline
|
|---|
| 161 | polynomials which are stored in psSpline1D.
|
|---|
| 162 |
|
|---|
| 163 | XXX: This is F32 only
|
|---|
| 164 |
|
|---|
| 165 | XXX: spline->knots must be psF32
|
|---|
| 166 | *****************************************************************************/
|
|---|
| 167 | /*
|
|---|
| 168 | psF32 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 | /*****************************************************************************
|
|---|
| 220 | psVectorFitSpline1D(): given a psSpline1D data structure and a set of x/y
|
|---|
| 221 | vectors, this routine generates the linear or cublic splines which satisfy
|
|---|
| 222 | those data points.
|
|---|
| 223 |
|
|---|
| 224 | The formula for calculating the spline polynomials is derived from Numerical
|
|---|
| 225 | Recipes 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
|
|---|
| 230 | Where:
|
|---|
| 231 | H = x[1]-x[0]
|
|---|
| 232 | A = (x[1]-x)/H
|
|---|
| 233 | B = (x-x[0])/H
|
|---|
| 234 | The bulk of the code in this routine is the expansion of the above equation
|
|---|
| 235 | into a polynomial in terms of x, and then saving the coefficients of the
|
|---|
| 236 | powers of x in the spline polynomials. This gets pretty complicated.
|
|---|
| 237 |
|
|---|
| 238 | XXX: usage of yErr is not specified in IfA documentation.
|
|---|
| 239 |
|
|---|
| 240 | XXX: Is the x argument redundant? What do we do if the x argument is
|
|---|
| 241 | supplied, but does not equal the knots specified in mySpline?
|
|---|
| 242 |
|
|---|
| 243 | XXX: can psSpline be NULL?
|
|---|
| 244 |
|
|---|
| 245 | XXX: reimplement this assuming that mySpline is NULL?
|
|---|
| 246 |
|
|---|
| 247 | XXX: What happens if X is NULL, then an index vector is generated for X, but
|
|---|
| 248 | that index vector lies outside the range vectors in mySpline?
|
|---|
| 249 |
|
|---|
| 250 | XXX: Assumes mySpline->knots is psF32. Must add psU32 and psF64.
|
|---|
| 251 | *****************************************************************************/
|
|---|
| 252 | psSpline1D *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 | /******************************************************************************
|
|---|
| 433 | XXX: We assume unnormalized gaussians.
|
|---|
| 434 | *****************************************************************************/
|
|---|
| 435 | psVector *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 | /*
|
|---|
| 480 | XXX: from bug 230:
|
|---|
| 481 |
|
|---|
| 482 | We first perform a rotation:
|
|---|
| 483 | u = - (x-x0)*cos(theta) + (y-y0)*sin(theta)
|
|---|
| 484 | v = (x-x0)*cos(theta) + (y-y0)*sin(theta)
|
|---|
| 485 |
|
|---|
| 486 | Here u is the major axis, and v is the minor axis, x0,y0 is the centre, and
|
|---|
| 487 | theta is the position angle.
|
|---|
| 488 |
|
|---|
| 489 | Then the flux is
|
|---|
| 490 |
|
|---|
| 491 | flux = norm * exp(-( u*u/2.0/sigmau/sigmau + v*v/2.0/sigmav/sigmav)
|
|---|
| 492 | )/2.0/pi/sigmau/sigmav
|
|---|
| 493 |
|
|---|
| 494 | Here sigmau and sigmav are the widths of the major and minor axes.
|
|---|
| 495 |
|
|---|
| 496 | The "norm" parameter in the equation above corresponds to the normalisation.
|
|---|
| 497 |
|
|---|
| 498 | Suggest order:
|
|---|
| 499 |
|
|---|
| 500 | norm
|
|---|
| 501 | x0
|
|---|
| 502 | y0
|
|---|
| 503 | sigma_u
|
|---|
| 504 | sigma_v
|
|---|
| 505 | theta
|
|---|
| 506 | */
|
|---|
| 507 |
|
|---|
| 508 | psVector *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 |
|
|---|
| 559 | psF64 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
|
|---|
| 563 | bool 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
|
|---|
| 578 | bool 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
|
|---|
| 590 | psBool 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
|
|---|
| 698 | psF64 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?
|
|---|
| 749 | psBool 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
|
|---|
| 812 | bool 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 |
|
|---|
| 901 | fescape:
|
|---|
| 902 | psFree (ipiv);
|
|---|
| 903 | psFree (indxr);
|
|---|
| 904 | psFree (indxc);
|
|---|
| 905 | return (false);
|
|---|
| 906 | }
|
|---|
| 907 |
|
|---|
| 908 | /******************************************************************************
|
|---|
| 909 | psMinimizeLMChi2(): This routine will take an procedure which calculates
|
|---|
| 910 | an arbitrary function and it's derivative and minimize the chi-squared match
|
|---|
| 911 | between that function at the specified coords and the specified value at
|
|---|
| 912 | those coords.
|
|---|
| 913 |
|
|---|
| 914 | XXX: 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 |
|
|---|
| 920 | XXX: This is very different from what is specified in the SDR. Must
|
|---|
| 921 | coordinate with IfA on new SDR.
|
|---|
| 922 |
|
|---|
| 923 | XXX: Do vector/image recycles.
|
|---|
| 924 |
|
|---|
| 925 | XXX: probably yErr will be part of the SDR.
|
|---|
| 926 |
|
|---|
| 927 | XXX: 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 |
|
|---|
| 932 | XXX: Must update the covar matrix.
|
|---|
| 933 | *****************************************************************************/
|
|---|
| 934 | psBool 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 | /******************************************************************************
|
|---|
| 1187 | VectorFitPolynomial1DCheb(): This routine will fit a Chebyshev polynomial of
|
|---|
| 1188 | degree myPoly to the data points (x, y) and return the coefficients of that
|
|---|
| 1189 | polynomial.
|
|---|
| 1190 |
|
|---|
| 1191 | XXX: yErr is currently ignored.
|
|---|
| 1192 |
|
|---|
| 1193 | XXX: Use private name?
|
|---|
| 1194 | *****************************************************************************/
|
|---|
| 1195 | psPolynomial1D *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 | /******************************************************************************
|
|---|
| 1266 | VectorFitPolynomial1DOrd(): This routine will fit an ordinary polynomial of
|
|---|
| 1267 | degree myPoly to the data points (x, y) and return the coefficients of that
|
|---|
| 1268 | polynomial.
|
|---|
| 1269 |
|
|---|
| 1270 | XXX: Use private name?
|
|---|
| 1271 | XXX: Use recycled vectors.
|
|---|
| 1272 | *****************************************************************************/
|
|---|
| 1273 | psPolynomial1D* 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 | /******************************************************************************
|
|---|
| 1374 | psVectorFitPolynomial1D(): This routine must fit a polynomial of degree
|
|---|
| 1375 | myPoly to the data points (x, y) and return the coefficients of that
|
|---|
| 1376 | polynomial.
|
|---|
| 1377 |
|
|---|
| 1378 | XXX: type F32 is done via vector conversion only.
|
|---|
| 1379 | *****************************************************************************/
|
|---|
| 1380 | psPolynomial1D* 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 | *****************************************************************************/
|
|---|
| 1449 | psMinimization *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) \
|
|---|
| 1467 | for (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) \
|
|---|
| 1476 | BOOL_VAR = true; \
|
|---|
| 1477 | for (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) \
|
|---|
| 1485 | BOOL_VAR = true; \
|
|---|
| 1486 | for (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 | /******************************************************************************
|
|---|
| 1495 | p_psDetermineBracket(): This routine takes as input an arbitrary function,
|
|---|
| 1496 | and the parameter to vary, and the line along which it must vary. This
|
|---|
| 1497 | function produces as output a bracket [a, b, c] such that
|
|---|
| 1498 | f(param + b * line) < f(param + a * line)
|
|---|
| 1499 | f(param + b * line) < f(param + c * line)
|
|---|
| 1500 | a < b < c
|
|---|
| 1501 |
|
|---|
| 1502 | Algorithm:
|
|---|
| 1503 |
|
|---|
| 1504 | XXX completely ad hoc:
|
|---|
| 1505 | start with the user-supplied starting parameter and
|
|---|
| 1506 | call that b. Calculate a/c as a fractional amount smaller/larger than b.
|
|---|
| 1507 | Repeat this process until a local minimum is found.
|
|---|
| 1508 |
|
|---|
| 1509 | XXX:
|
|---|
| 1510 | new algorithm:
|
|---|
| 1511 | start at x=0, expand in one direction until the function
|
|---|
| 1512 | decreases. Then you have two points in the bracket. Keep going until it
|
|---|
| 1513 | increases, or x is too large. If thst does not work, expand in the other
|
|---|
| 1514 | direction.
|
|---|
| 1515 |
|
|---|
| 1516 | XXX:
|
|---|
| 1517 | This is F32 only.
|
|---|
| 1518 |
|
|---|
| 1519 | XXX:
|
|---|
| 1520 | output bracket vector should be an input as well.
|
|---|
| 1521 | *****************************************************************************/
|
|---|
| 1522 | psVector *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) \
|
|---|
| 1656 | if (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 | } \
|
|---|
| 1665 | psTrace(".psLib.dataManip.p_psDetermineBracket", 4, \
|
|---|
| 1666 | "---- p_psDetermineBracket() end ----\n"); \
|
|---|
| 1667 | psTrace(".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); \
|
|---|
| 1668 | return(bracket); \
|
|---|
| 1669 |
|
|---|
| 1670 | #define PS_DETERMINE_BRACKET_MAX_ITERATIONS 100
|
|---|
| 1671 | psVector *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 | /******************************************************************************
|
|---|
| 1766 | This routine takes as input a possibly multi-dimensional function, along
|
|---|
| 1767 | with an initial guess at the parameters of that function and vector "line"
|
|---|
| 1768 | of the same size as the parameter vector. It will minimize the function
|
|---|
| 1769 | along that vector and returns the offset along that vector at which the
|
|---|
| 1770 | minimum is determined.
|
|---|
| 1771 |
|
|---|
| 1772 | XXX: This routine is not very efficient in terms of total evaluations of the
|
|---|
| 1773 | function.
|
|---|
| 1774 | XXX: This is F32 only
|
|---|
| 1775 | XXX: Since this is an internal function, many of the parameter checks are
|
|---|
| 1776 | redundant.
|
|---|
| 1777 | XXX: Don't modify the psMinimization argument.
|
|---|
| 1778 | *****************************************************************************/
|
|---|
| 1779 | #define PS_LINEMIN_MAX_ITERATIONS 30
|
|---|
| 1780 | psF32 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 | /******************************************************************************
|
|---|
| 1919 | This routine must minimize a possibly multi-dimensional function. The
|
|---|
| 1920 | function to be minimized "func" is:
|
|---|
| 1921 | psF32 func(psVector *params, psArray *coords)
|
|---|
| 1922 | The "params" are the parameters of the function which are varied. The data
|
|---|
| 1923 | points at which the function is varied are in the argument "coords" which is
|
|---|
| 1924 | a psArray of psVectors: each vector represents a different coordinate.
|
|---|
| 1925 |
|
|---|
| 1926 | XXX: We do not use Brent's method.
|
|---|
| 1927 |
|
|---|
| 1928 | XXX: The SDR is silent about data types. F32 is implemented here.
|
|---|
| 1929 |
|
|---|
| 1930 | XXX: 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 |
|
|---|
| 1935 | psBool 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 | /******************************************************************************
|
|---|
| 2132 | XXX: We assume unnormalized gaussians.
|
|---|
| 2133 | XXX: Currently, yErr is ignored.
|
|---|
| 2134 | *****************************************************************************/
|
|---|
| 2135 | psVector *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 | /******************************************************************************
|
|---|
| 2156 | This routine is to be used with the psMinimizeChi2Powell() function below.
|
|---|
| 2157 | and the psMinimizePowell() function above.
|
|---|
| 2158 |
|
|---|
| 2159 | The basic idea is calculate chi-squared for a set of params/coords/errors.
|
|---|
| 2160 | This functions uses global variables to receive the function pointer, the
|
|---|
| 2161 | data values, and the data errors.
|
|---|
| 2162 | XXX: This is F32 only
|
|---|
| 2163 | *****************************************************************************/
|
|---|
| 2164 | psF32 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 | /******************************************************************************
|
|---|
| 2200 | This routine must minimize the chi-squared match of a set of data points and
|
|---|
| 2201 | values for a possibly multi-dimensional function.
|
|---|
| 2202 |
|
|---|
| 2203 | The basic idea is to use the psMinimizePowell() function defined above. In
|
|---|
| 2204 | order to do so, we defined above a function myPowellChi2Func() which takes
|
|---|
| 2205 | the "func" function and returns chi-squared over the params/coords/values.
|
|---|
| 2206 | We then use that function myPowellChi2Func() in the call to
|
|---|
| 2207 | psMinimizePowell().
|
|---|
| 2208 | *****************************************************************************/
|
|---|
| 2209 | psBool 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 |
|
|---|