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 }