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