1 // This code has been mechanically translated from the original FORTRAN 2 // code at http://netlib.org/minpack. 3 4 /** Authors: Lars Tandle Kyllingstad 5 Copyright: Copyright (c) 2009, Lars T. Kyllingstad. All rights reserved. 6 License: Boost Licence 1.0 7 */ 8 module scid.ports.minpack.hybrd; 9 10 11 private import std.algorithm: min, max; 12 private import std.math: abs; 13 14 private import scid.ports.minpack.dogleg; 15 private import scid.ports.minpack.enorm; 16 private import scid.ports.minpack.fdjac1; 17 private import scid.ports.minpack.qform; 18 private import scid.ports.minpack.qrfac; 19 private import scid.ports.minpack.r1mpyq; 20 private import scid.ports.minpack.r1updt; 21 22 23 24 25 /** The purpose of hybrd is to find a zero of a system of 26 n nonlinear functions in n variables by a modification 27 of the Powell Hybrid Method. The user must provide a 28 function which calculates the functions. The Jacobian is 29 then calculated by a forward-difference approximation. 30 31 Params: 32 fcn = the user-supplied function or delegate which 33 calculates the functions. The function referenced 34 by fcn must be declared 35 in an external statement in the user calling 36 program, and should be written as follows. 37 --- 38 void foo (size_t n,real* x, real* fvec, int iflag) 39 { 40 // calculate the functions at x and 41 // return this vector in fvec. 42 } 43 --- 44 The value of iflag should not be changed by the function unless 45 the user wants to terminate execution of hybrd. 46 In this case set iflag to a negative integer. 47 48 n = a positive integer input variable set to the number 49 of functions and variables. 50 51 x = an array of length n. On input x must contain 52 an initial estimate of the solution vector. On output x 53 contains the final estimate of the solution vector. 54 55 fvec = an output array of length n which contains 56 the functions evaluated at the output x. 57 58 xtol = a nonnegative input variable. Termination 59 occurs when the relative error between two consecutive 60 iterates is at most xtol. 61 62 maxfev = a positive integer input variable. Termination 63 occurs when the number of calls to fcn is at least maxfev 64 by the end of an iteration. 65 66 ml = a nonnegative integer input variable which specifies 67 the number of subdiagonals within the band of the 68 jacobian matrix. If the Jacobian is not banded, set 69 ml to at least n - 1. 70 71 mu = a nonnegative integer input variable which specifies 72 the number of superdiagonals within the band of the 73 jacobian matrix. If the Jacobian is not banded, set 74 mu to at least n - 1. 75 76 epsfcn = an input variable used in determining a suitable 77 step length for the forward-difference approximation. This 78 approximation assumes that the relative errors in the 79 functions are of the order of epsfcn. If epsfcn is less 80 than the machine precision, it is assumed that the relative 81 errors in the functions are of the order of the machine 82 precision. 83 84 diag = an array of length n. If mode = 1 (see 85 below), diag is internally set. If mode = 2, diag 86 must contain positive entries that serve as 87 multiplicative scale factors for the variables. 88 89 mode = an integer input variable. If mode = 1, the 90 variables will be scaled internally. If mode = 2, 91 the scaling is specified by the input diag. Other 92 values of mode are equivalent to mode = 1. 93 94 factor = a positive input variable used in determining the 95 initial step bound. This bound is set to the product of 96 factor and the Euclidean norm of diag*x if nonzero, or else 97 to factor itself. In most cases factor should lie in the 98 interval (.1,100.). 100. is a generally recommended value. 99 100 nprint = an integer input variable that enables controlled 101 printing of iterates if it is positive. In this case, 102 fcn is called with iflag = 0 at the beginning of the first 103 iteration and every nprint iterations thereafter and 104 immediately prior to return, with x and fvec available 105 for printing. If nprint is not positive, no special calls 106 of fcn with iflag = 0 are made. 107 108 info = an integer output variable. If the user has 109 terminated execution, info is set to the (negative) 110 value of iflag. See description of fcn. Otherwise, 111 info is set as follows. 112 --- 113 0: improper input parameters. 114 115 1: relative error between two consecutive iterates 116 is at most xtol. 117 118 2: number of calls to fcn has reached or exceeded 119 maxfev. 120 121 3: xtol is too small. no further improvement in 122 the approximate solution x is possible. 123 124 4: iteration is not making good progress, as 125 measured by the improvement from the last 126 five Jacobian evaluations. 127 128 5: iteration is not making good progress, as 129 measured by the improvement from the last 130 ten iterations. 131 --- 132 133 nfev = an integer output variable set to the number of 134 calls to fcn. 135 136 fjac = an output n by n array which contains the 137 orthogonal matrix Q produced by the QR factorization 138 of the final approximate Jacobian. 139 140 ldfjac = a positive integer input variable not less than n 141 which specifies the leading dimension of the array fjac. 142 143 r = an output array of length lr which contains the 144 upper triangular matrix produced by the QR factorization 145 of the final approximate Jacobian, stored rowwise. 146 147 lr = a positive integer input variable not less than 148 (n*(n+1))/2. 149 150 qtf = an output array of length n which contains 151 the vector (Q transpose)*fvec. 152 153 wa1 = work array of length n. 154 wa2 = work array of length n. 155 wa3 = work array of length n. 156 wa4 = work array of length n. 157 */ 158 void hybrd(Real, Func)(Func fcn, size_t n, Real* x, Real* fvec, 159 Real xtol, uint maxfev, size_t ml, size_t mu, Real epsfcn, 160 Real* diag, int mode, Real factor, int nprint, out int info, 161 out uint nfev, Real* fjac, size_t ldfjac, Real* r, size_t lr, 162 Real* qtf, Real* wa1, Real* wa2, Real* wa3, Real* wa4) 163 { 164 size_t i, j, l; 165 ptrdiff_t jm1; 166 long msum; 167 int iflag, iter, ncsuc, ncfail, nslow1, nslow2; 168 size_t[1] iwa; 169 bool jeval, sing; 170 Real actred, delta, fnorm, fnorm1, pnorm, prered, ratio, sum, temp, xnorm; 171 172 enum : Real 173 { 174 one = 1.0, 175 p1 = 1e-1, 176 p5 = 5e-1, 177 p001 = 1e-3, 178 p0001 = 1e-4, 179 zero = 0.0, 180 181 // epsmch is the machine precision. 182 epsmch = Real.epsilon 183 } 184 185 info = 0; 186 iflag = 0; 187 nfev = 0; 188 189 190 // Check the input parameters for errors. 191 if (xtol < zero || factor <= zero || ldfjac < n || lr < (n*(n+1)/2)) 192 goto end; 193 194 if (mode == 2) 195 { 196 for (j=0; j<n; j++) 197 if (diag[j] <= zero) goto end; 198 } 199 200 201 // Evaluate the function at the starting point 202 // and calculate its norm. 203 iflag = 1; 204 fcn(n, x, fvec, iflag); 205 nfev = 1; 206 if (iflag < 0) goto end; 207 fnorm = enorm(n, fvec); 208 209 210 // Determine the number of calls to fcn needed to compute 211 // the Jacobian matrix. 212 msum = min(ml+mu+1, n); 213 214 215 // Initialize iteration counter and monitors. 216 iter = 1; 217 ncsuc = 0; 218 ncfail = 0; 219 nslow1 = 0; 220 nslow2 = 0; 221 222 223 // Beginning of the outer loop. 224 outer: while(true) 225 { 226 jeval = true; 227 228 // Calculate the Jacobian matrix. 229 fdjac1!(Real, Func) 230 (fcn, n, x, fvec, fjac, ldfjac, iflag, ml, epsfcn, mu, wa1, wa2); 231 nfev += msum; 232 if (iflag < 0) break outer; 233 234 235 // Compute the QR factorization of the Jacobian. 236 qrfac(n, n, fjac, ldfjac, false, iwa.ptr, 1, wa1, wa2, wa3); 237 238 239 // On the first iteration and if mode is 1, scale according 240 // to the norms of the columns of the initial Jacobian. 241 if (iter == 1) 242 { 243 if (mode != 2) 244 { 245 for (j=0; j<n; j++) 246 { 247 diag[j] = wa2[j]; 248 if (wa2[j] == zero) diag[j] = one; 249 } 250 } 251 252 253 // On the first iteration, calculate the norm of the scaled x 254 // and initialize the step bound delta. 255 for (j=0; j<n; j++) 256 wa3[j] = diag[j]*x[j]; 257 xnorm = enorm(n, wa3); 258 delta = factor*xnorm; 259 if (delta == zero) delta = factor; 260 } 261 262 263 // Form (Q transpose)*fvec and store in qtf. 264 for (i=0; i<n; i++) 265 qtf[i] = fvec[i]; 266 for (j=0; j<n; j++) 267 { 268 if (fjac[j+j*ldfjac] != zero) 269 { 270 sum = zero; 271 for (i=j; i<n; i++) 272 sum += fjac[i+j*ldfjac]*qtf[i]; 273 temp = -sum/fjac[j+j*ldfjac]; 274 for (i=j; i<n; i++) 275 qtf[i] = qtf[i] + fjac[i+j*ldfjac]*temp; 276 } 277 } 278 279 280 // Copy the triangular factor of the QR factorization into r. 281 sing = false; 282 for (j=0; j<n; j++) 283 { 284 l = j; 285 jm1 = j - 1; 286 if (jm1 >= 0) 287 { 288 for (i=0; i<=jm1; i++) 289 { 290 r[l] = fjac[i+j*ldfjac]; 291 l += n - i - 1; 292 } 293 } 294 295 r[l] = wa1[j]; 296 if (wa1[j] == zero) sing = true; 297 } 298 299 300 // Accumulate the orthogonal factor in fjac. 301 qform!(Real)(n, n, fjac, ldfjac, wa1); 302 303 304 // Rescale if necessary. 305 if (mode != 2) 306 { 307 for (j=0; j<n; j++) 308 diag[j] = max(diag[j], wa2[j]); 309 } 310 311 312 // Beginning of inner loop. 313 inner: while(true) 314 { 315 // If requested, call fcn to enable printing of iterates. 316 if (nprint > 0) 317 { 318 iflag = 0; 319 if ((iter-1 % nprint) == 0) fcn(n, x, fvec, iflag); 320 if (iflag < 0) break outer; 321 } 322 323 324 // Determine the direction p. 325 dogleg(n, r, lr, diag, qtf, delta, wa1, wa2, wa3); 326 327 328 // Store the direction p and x + p. Calculate the norm of p. 329 for (j=0; j<n; j++) 330 { 331 wa1[j] = -wa1[j]; 332 wa2[j] = x[j] + wa1[j]; 333 wa3[j] = diag[j]*wa1[j]; 334 } 335 pnorm = enorm(n, wa3); 336 337 338 // On the first iteration, adjust the initial step bound. 339 if (iter == 1) delta = min(delta, pnorm); 340 341 342 // Evaluate the function at x + p and calculate its norm. 343 iflag = 1; 344 fcn(n, wa2, wa4, iflag); 345 nfev++; 346 if (iflag < 0) break outer; 347 fnorm1 = enorm(n, wa4); 348 349 350 // Compute the scaled actual reduction. 351 actred = -one; 352 if (fnorm1 < fnorm) actred = one - (fnorm1/fnorm)^^2; 353 354 355 // Compute the scaled predicted reduction. 356 l = 0; 357 for (i=0; i<n; i++) 358 { 359 sum = zero; 360 for (j=i; j<n; j++) 361 { 362 sum += r[l]*wa1[j]; 363 l++; 364 } 365 wa3[i] = qtf[i] + sum; 366 } 367 368 temp = enorm(n, wa3); 369 prered = zero; 370 if (temp < fnorm) prered = one - (temp/fnorm)^^2; 371 372 373 // Compute the ratio of the actual to the predicted 374 // reduction. 375 ratio = zero; 376 if (prered > zero) ratio = actred/prered; 377 378 379 // Update the step bound. 380 if (ratio < p1) 381 { 382 ncsuc = 0; 383 ncfail++; 384 delta *= p5; 385 } 386 else 387 { 388 ncfail = 0; 389 ncsuc++; 390 if (ratio >= p5 || ncsuc > 1) 391 delta = max(delta, pnorm/p5); 392 if (abs(ratio-one) <= p1) delta = pnorm/p5; 393 } 394 395 396 // Test for successful iteration. 397 if (ratio >= p0001) 398 { 399 // Successful iteration. Update x, fvec, and their norms. 400 for (j=0; j<n; j++) 401 { 402 x[j] = wa2[j]; 403 wa2[j] = diag[j]*x[j]; 404 fvec[j] = wa4[j]; 405 } 406 407 xnorm = enorm(n, wa2); 408 fnorm = fnorm1; 409 iter++; 410 } 411 412 413 // Determine the progress of the iteration. 414 nslow1++; 415 if (actred >= p001) nslow1 = 0; 416 if (jeval) nslow2++; 417 if (actred >= p1) nslow2 = 0; 418 419 420 // Test for convergence. 421 if (delta <= xtol*xnorm || fnorm == zero) 422 { 423 info = 1; 424 break outer; 425 } 426 427 428 // Tests for termination and stringent tolerances. 429 if (nfev >= maxfev) info = 2; 430 if (p1*max(p1*delta, pnorm) <= epsmch*xnorm) info = 3; 431 if (nslow2 == 5) info = 4; 432 if (nslow1 == 10) info = 5; 433 if (info != 0) break outer; 434 435 436 // Criterion for recalculating Jacobian approximation 437 // by forward differences. 438 if (ncfail == 2) break inner; 439 440 // Calculate the rank one modification to the Jacobian 441 // and update qtf if necessary. 442 for (j=0; j<n; j++) 443 { 444 sum = zero; 445 for (i=0; i<n; i++) 446 sum += fjac[i+j*ldfjac]*wa4[i]; 447 wa2[j] = (sum - wa3[j])/pnorm; 448 wa1[j] = diag[j]*((diag[j]*wa1[j])/pnorm); 449 if (ratio >= p0001) qtf[j] = sum; 450 } 451 452 453 // Compute the QR factorization of the updated Jacobian. 454 r1updt(n, n, r, lr, wa1, wa2, wa3, sing); 455 r1mpyq(n, n, fjac, ldfjac, wa2, wa3); 456 r1mpyq(1, n, qtf, 1, wa2, wa3); 457 458 459 // End of the inner loop. 460 jeval = false; 461 } 462 463 464 // End of the outer loop. 465 } 466 467 end: 468 // Termination, either normal or user imposed. 469 if (iflag < 0) info = iflag; 470 471 iflag = 0; 472 if (nprint > 0) fcn(n, x, fvec, iflag); 473 } 474 475 476 version (unittest) { import std.stdio; } 477 478 unittest 479 { 480 real a = 1.0; 481 real b = 10.0; 482 483 void rosenbrock(size_t n, real* x, real* fx, ref int iflag) 484 { 485 assert (n == 2); 486 fx[0] = a * (1 - x[0]); 487 fx[1] = b * (x[1] - x[0]*x[0]); 488 //writefln("f(%s, %s) = (%s, %s)", x[0], x[1], fx[0], fx[1]); 489 } 490 491 alias hybrd!(real, typeof(&rosenbrock)) rhybrd; 492 493 enum size_t n = 2; 494 enum size_t lr = (n*(n+1))/2; 495 496 real[n] x = [ -10.0, -5.0 ]; 497 real[n] fvec, diag, qtf, wa1, wa2, wa3, wa4; 498 real[lr] r; 499 real[n*n] fjac; 500 501 real xtol = 0.0; 502 uint maxfev = 1000; 503 real epsfcn = 1e-10; 504 505 uint mode = 1; 506 real factor = 100.0; 507 int nprint = 0; 508 int info; 509 uint nfev; 510 511 rhybrd(&rosenbrock, n, x.ptr, fvec.ptr, xtol, maxfev, n, n, epsfcn, 512 diag.ptr, mode, factor, nprint, info, nfev, fjac.ptr, n, r.ptr, 513 lr, qtf.ptr, wa1.ptr, wa2.ptr, wa3.ptr, wa4.ptr); 514 //writefln("Status: %s Result: (%s , %s) nfev: %s", info, x[0], x[1], nfev); 515 assert (info == 1); 516 assert (abs(1.0-x[0]) <= xtol && abs(1.0-x[1]) <= xtol); 517 518 } 519