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.fdjac1;
9 
10 
11 private import std.algorithm: max;
12 private import std.math: abs, sqrt;
13 
14 
15 
16 /** This subroutine computes a forward-difference approximation
17     to the n by n Jacobian matrix associated with a specified
18     problem of n functions in n variables. If the Jacobian has
19     a banded form, then function evaluations are saved by only
20     approximating the nonzero terms.
21 
22 
23     Params:
24         fcn = 
25             the name of the user-supplied function or delegate which
26             calculates the functions. fcn should be written as follows.
27             ---
28             void f(size_t n, real* x, real* fvec, int iflag)
29             {
30                 // calculate the functions at x and
31                 // return this vector in fvec.
32             }
33             ---
34             The value of iflag should not be changed by fcn unless
35             the user wants to terminate execution of fdjac1.
36             In this case set iflag to a negative integer.
37 
38         n = 
39             a positive integer input variable set to the number
40             of functions and variables.
41 
42         x = 
43             an input array of length n.
44 
45         fvec = 
46             an input array of length n which must contain the
47             functions evaluated at x.
48 
49         fjac =
50             an output n by n array which contains the
51             approximation to the Jacobian matrix evaluated at x.
52 
53         ldfjac =
54             a positive integer input variable not less than n
55             which specifies the leading dimension of the array fjac.
56 
57         iflag =
58             an integer variable which can be used to terminate
59             the execution of fdjac1. See description of fcn.
60 
61         ml = 
62             a nonnegative integer input variable which specifies
63             the number of subdiagonals within the band of the
64             Jacobian matrix. If the Jacobian is not banded, set
65             ml to at least n - 1.
66 
67         epsfcn =
68             an input variable used in determining a suitable
69             step length for the forward-difference approximation. This
70             approximation assumes that the relative errors in the
71             functions are of the order of epsfcn. If epsfcn is less
72             than the machine precision, it is assumed that the relative
73             errors in the functions are of the order of the machine
74             precision.
75 
76         mu =
77             a nonnegative integer input variable which specifies
78             the number of superdiagonals within the band of the
79             Jacobian matrix. If the jacobian is not banded, set
80             mu to at least n - 1.
81 
82         wa1 =
83             work array of length n.
84 
85         wa2 =
86             work array of length n. If ml + mu + 1 is at
87             least n, then the Jacobian is considered dense, and wa2 is
88             not referenced.
89 */
90 void fdjac1(Real, Func)(Func fcn, size_t n, Real* x, Real* fvec,
91     Real* fjac, size_t ldfjac, int iflag, size_t ml, Real epsfcn,
92     size_t mu, Real* wa1, Real* wa2)
93 {
94     size_t i, j, k;
95     Real temp, h;
96 
97 
98     enum Real zero = 0.0;
99 
100     // epsmch is the machine precision.
101     enum Real epsmch = Real.epsilon;
102 
103 
104     immutable Real eps = sqrt(max(epsfcn, epsmch));
105     immutable size_t msum = ml + mu + 1;
106 
107     if (msum >= n)
108     {
109         // Computation of dense approximate Jacobian.
110         for (j=0; j<n; j++)
111         {
112             temp = x[j];
113             h = eps*abs(temp);
114             if (h == zero) h = eps;
115             x[j] = temp + h;
116 
117             fcn(n, x, wa1, iflag);
118             if (iflag < 0)  return;
119 
120             x[j] = temp;
121             for (i=0; i<n; i++)
122                 fjac[i+j*ldfjac] = (wa1[i] - fvec[i])/h;
123         }
124         return;
125     }
126 
127 
128     // Computation of banded approximate Jacobian.
129     size_t ij;
130     for (k=0; k<msum; k++)
131     {
132         for (j=k; j<n; j+= msum)
133         {
134             wa2[j] = x[j];
135             h = eps*abs(wa2[j]);
136             if (h == zero) h = eps;
137             x[j] = wa2[j] + h;
138         }
139 
140         fcn(n, x, wa1, iflag);
141         if (iflag < 0)  return;
142 
143         for (j=k; j<n; j+=msum)
144         {
145             x[j] = wa2[j];
146             h = eps*abs(wa2[j]);
147             if (h == zero) h = eps;
148             
149             for (i=0; i<n; i++)
150             {
151                 ij = i+j*ldfjac;
152                 fjac[ij] = zero;
153                 if (i >= j-mu  &&  i <= j+ml)
154                     fjac[ij] = (wa1[i] - fvec[i])/h;
155             }
156         }
157     }
158 }
159 
160 
161 unittest
162 {
163     void f(size_t n, double* a, double* fvec, ref int iflag)
164     {
165         auto x = a[0], y = a[1];
166 
167         fvec[0] = 2.0*x*y;
168         fvec[1] = x-y;
169     }
170 
171     bool close(double x, double y)
172     {
173         return (abs(x-y) < 1e-6);
174     }
175 
176     double[] x = [ 1.0, 2.0 ];
177     double[] fx = new double[2];
178     double[] j = new double[4];
179     double[] w1 = new double[2];
180     double[] w2 = new double[2];
181     int iflag = 1;
182 
183     f(2, x.ptr, fx.ptr, iflag);
184     fdjac1(&f, 2, x.ptr, fx.ptr, j.ptr, 2, iflag, 2, 1e-8, 2, w1.ptr, w2.ptr);
185 
186     assert (close(j[0], 4.0) && close(j[2], 2.0)
187         &&  close(j[1], 1.0) && close(j[3], -1.0));
188 }