001 package numal.lowprecision; 002 003 import java.util.*; 004 import static java.lang.Math.*; 005 import static numal.lowprecision.Basic.*; 006 import static numal.lowprecision.LinearAlgebra.qrisngvaldec; 007 008 public class AnalyticProblems extends Object { 009 010 /** 011 * The standard deviation of each parameter in the fit is sqrt(v[j][j] / m) 012 * and the correlation between parameter i and parameter j is 013 * v[i][j]/ sqrt(v[i][i]*v[j][j]) where i=1,...,n and j=i+1,...,n. 014 * // http://www.crcpress.com/shopping_cart/products/product_detail.asp?sku=C4304 015 * <ul> 016 * <lh>Output values</lh> 017 * <li>out[1] information about the termination of the process 018 * <li>0 : normal termination</li> 019 * <li>1 : number of invocations of {@link Function#computeResidualVector} exceeded {@link Function#getInvocations()}</li> 020 * <li>2 : {@link Function#computeResidualVector} returned false</li> 021 * <li>3 : {@link Function#computeResidualVector} returned false when called with the initial estimates of parameter[1:n]; 022 * the iteration process was not started and so v[1:n][1:n] can't be used</li> 023 * <li>4 : precision asked for can't be attained; this precision is possibly chosen too high, relative to the 024 * precision in which the residual vector is calculated (see {@link Function#getRelativeTolerance()}</li> 025 * </li> 026 * <li>out[2] the Euclidean norm of the residual vector calculated with values of the unknowns delivered</li> 027 * <li>out[3] the Euclidean norm of the residual vector calculated with the initial values of the unknown variables</li> 028 * <li>out[4] the number of calls of {@link Function#computeResidualVector} necessary to attain the calculated results 029 * </li> 030 * <li>out[5] the total number of iterations performed; note that in each iteration one call to {@link Function#computeJacobian} had to be made</li> 031 * <li>out[6] the improvement vector in the last iteration</li> 032 * <li>out[7] the condition number of J^T J, i.e., the ratio of its largest to smallest eigenvalues</li> 033 * </ul> 034 * 035 * Dependencies: {@link Basic#mulcol}, 036 * {@link Basic#dupvec}, 037 * {@link Basic#vecvec}, 038 * {@link Basic#matvec}, 039 * {@link Basic#tamvec}, 040 * {@link Basic#mattam}, 041 * {@link LinearAlgebra#qrisngvaldec}. 042 * 043 * @param m sample points used in the fitting 044 * @param n number of parameters, must be less than or equal to m 045 * @param parameter <b>input</b>: initial approximation to the set of parameters <b>output</b>: parameters producing a least square fit 046 * @param rv 1xm vector <b>output</b> : residual vector obtained with current value of unknowns 047 * @param v nxn matrix <b>output</b> : inverse of matrix J^T J where J denotes the transpose of the matrix of partial derivatives dg[1..m]/dp[1..n] 048 * @param function residual vector of a given function, jacobian and options such as precision and maximum number of iterations 049 * @param out an array of 7 output values (see description above) 050 */ 051 public static void marquardt(int m, int n, float parameter[], float rv[], float v[][], Function function, float out[]) { 052 int invocation = 1; 053 int errorCode = 0; 054 int iteration = 1; 055 if (!function.computeResidualVector(m, n, parameter, rv)) { 056 errorCode = 3; 057 out[4] = invocation; 058 out[5] = iteration - 1; 059 out[1] = errorCode; 060 return; 061 } 062 final float em[] = { function.getMachinePrecision(), 0f, function.getMachinePrecision(), 0f, 10 * n, 0f, function.getMachinePrecision(), 0f}; // 8 entries 063 final float mu = 0.01f; 064 final float w = 0.5f; 065 final float vv = 10f; 066 final float ww = (function.getStartingValue() < 1.0e-7) ? 1e-8f : 1e-1f * function.getStartingValue(); 067 final float reltolres = function.getRelativeTolerance(); 068 final float abstolres = function.getAbsoluteTolerance() * function.getAbsoluteTolerance(); 069 final float pw = (float) (-log(ww * function.getMachinePrecision()) / 2.3d); 070 float fparpres; 071 float val[] = new float[n + 1]; 072 float b[] = new float[n + 1]; 073 float bb[] = new float[n + 1]; 074 float parpres[] = new float[n + 1]; 075 float jac[][] = new float[m + 1][n + 1]; 076 float p = 0f; 077 float res = 0f; 078 boolean emergency = false; 079 float lambda = 0f; 080 float lambdamin; 081 float fpar = vecvec(1, m, 0, rv, rv); 082 out[3] = (float) sqrt(fpar); 083 084 do { 085 function.computeJacobian(m, n, parameter, rv, jac); 086 qrisngvaldec(jac, m, n, val, v, em); 087 if (iteration == 1) 088 lambda = function.getStartingValue() * vecvec(1, n, 0, val, val); 089 else 090 if (p == 0f) lambda *= w; 091 for (int i = 1; i <= n; i++) b[i] = val[i] * tamvec(1, m, i, jac, rv); 092 while (true) { 093 for (int i = 1; i <= n; i++) bb[i] = b[i] / (val[i] * val[i] + lambda); 094 for (int i = 1; i <= n; i++) parpres[i] = parameter[i] - matvec(1, n, i, v, bb); 095 invocation++; 096 if (invocation >= function.getInvocations()) 097 errorCode = 1; 098 else 099 if (!function.computeResidualVector(m, n, parpres, rv)) errorCode = 2; 100 if (errorCode != 0) { 101 emergency = true; 102 break; 103 } 104 fparpres = vecvec(1, m, 0, rv, rv); 105 res = fpar - fparpres; 106 if (res < mu * vecvec(1, n, 0, b, bb)) { 107 p += 1f; 108 lambda *= vv; 109 if (p == 1f) { 110 lambdamin = ww * vecvec(1, n, 0, val, val); 111 if (lambda < lambdamin) lambda = lambdamin; 112 } 113 if (p >= pw) { 114 errorCode = 4; 115 emergency = true; 116 break; 117 } 118 } else { 119 dupvec(1, n, 0, parameter, parpres); 120 fpar = fparpres; 121 break; 122 } 123 System.out.println(iteration + " " + parameter[1] + " " + parameter[2] + " " + parameter[3]); 124 } 125 if (emergency) break; 126 iteration++; 127 128 } while (fpar > abstolres && res > reltolres * fpar + abstolres); 129 130 for (int i = 1; i <= n; i++) 131 mulcol(1, n, i, i, jac, v, 1f / (val[i] + function.getMachinePrecision())); 132 for (int i = 1; i <= n; i++) 133 for (int j = 1; j <= i; j++) 134 v[i][j] = v[j][i] = mattam(1, n, i, j, jac, jac); 135 136 lambda = lambdamin = val[1]; 137 for (int i = 2; i <= n; i++) 138 if (val[i] > lambda) lambda = val[i]; 139 else if (val[i] < lambdamin) lambdamin = val[i]; 140 141 float temp = lambda / (lambdamin + function.getMachinePrecision()); 142 out[7] = temp * temp; 143 out[2] = (float) sqrt(fpar); 144 out[6] = (float) sqrt(res + fpar) - out[2]; 145 out[4] = invocation; 146 out[5] = iteration - 1; 147 out[1] = errorCode; 148 } 149 150 151 }