//copy constructor public NMMatrix(NMMatrix A) { _matrix = new double[A.N, A.M]; _n = A.N; _m = A.M; for (int i = 0; i < _n; i++) for (int j = 0; j < _m; j++) _matrix[i, j] = A[i, j]; }
public NVector Calculate(NVector par_initial, NVector t, NVector y_dat) { _result = 0; m = t.N; if (par_initial.N != n) throw new Exception("LevenbergMarquardt.Calculate: size mismatch parms"); this.p = par_initial; this.t = t; if (y_dat.N != m) throw new Exception("LevenbergMarquardt.Calculate: size mismatch t-y"); this.y_dat = y_dat; // weight_sq = (m - n + 1) / y_dat.Dot(y_dat); DOF = (double)(m - n + 1); //initalize Jacobian and related matrices y_hat = func(t, p); y_old = y_hat; if (Jfunc == null) J = Jacobian(p, y_hat); else J = Jfunc(t, p); NVector delta_y = y_dat - y_hat; X2 = delta_y.Dot(delta_y); JtJ = J.Transpose() * J; Jtdy = J.Transpose() * delta_y; iteration = 0; if (Jtdy.Abs().Max() < eps[0]) { _result = 1; return p; //Good guess!!! } if (updateType == UpdateType.Marquardt) lambda = 0.01D; else lambda = 0.01D * JtJ.Diag().Max(); bool stop = false; /************************** Begin Main loop ***********************/ // y_hat = vector of y estimates for current value of parameters // y_try = vector of y estimates for current trial value of parameters // y_dat = given dependent values (fixed) // y_old = vector of y estimates for previous value of parameters (used in Broyden estimate of J) // t = given independent values (fixed) // p = current accepted estimate of parameters // h = last calculated (trial) increment for the parameters // p_try = current trial value for the parameters // p_old = previous accepted value of parameters (used in Broyden estimate of J) // X2 = chi^2 of last accepted estimate // X2_try = chi^2 of current trial estimate // J = current estimate of Jacobian at p while (!stop) { iteration++; NVector h; if (updateType == UpdateType.Marquardt) h = Jtdy / (JtJ + lambda * JtJ.Diag().Diag()); else h = Jtdy / (JtJ + lambda * NMMatrix.I(n)); NVector p_try = (p + h).Max(p_min).Min(p_max); NVector y_try = func(t, p_try); delta_y = y_dat - y_try; double X2_try = delta_y.Dot(delta_y); if (updateType == UpdateType.Quadratic) { alpha = Jtdy.Dot(h) / ((X2_try - X2) / 2D + 2D * Jtdy.Dot(h)); h = h * alpha; p_try = (p_try + h).Max(p_min).Min(p_max); delta_y = y_dat - func(t, p_try); X2_try = delta_y .Dot(delta_y); } dX2 = X2_try - X2; double rho = -dX2 / (2D * (lambda * h + Jtdy).Dot(h)); if (dX2 < 0D) //found a better estimate { X2 = X2_try; p_old = p; p = p_try; y_old = y_hat; y_hat = y_try; if (iteration % (2 * n) == 0) //|| dX2 > 0 or is it rho > ep[3] ? if (Jfunc == null) J = Jacobian(p, y_hat); else J = Jfunc(t, p); else J = J + (y_hat - y_old - J * h).Cross(h) / h.Dot(h); //Broyden rank-1 update of J JtJ = J.Transpose() * J; Jtdy = J.Transpose() * delta_y; switch (updateType) { case UpdateType.Marquardt: lambda = Math.Max(lambda / lambda_DN_fac, 1E-7); break; case UpdateType.Quadratic: lambda = Math.Max(lambda / (1 + alpha), 1E-7); break; case UpdateType.Nielsen: lambda = lambda * Math.Max(1D / 3D, 1D - Math.Pow(2D * rho - 1D, 3)); nu = 2D; break; } if (Jtdy.Abs().Max() < eps[0] && iteration > 2) { _result = 1; stop = true; } else if ((h / p).Abs().Max() < eps[1] && iteration > 2) { _result = 2; stop = true; } else if (X2 / (m - n + 1) < eps[2] && iteration > 2) { _result = 3; stop = true; } } else //Not a better estimate { if (iteration % (2 * n) == 0) //update J every 2n th no matter what { if (Jfunc == null) J = Jacobian(p, y_hat); else J = Jfunc(t, p); JtJ = J.Transpose() * J; Jtdy = J.Transpose() * (y_dat - y_hat); } switch (updateType) { case UpdateType.Marquardt: lambda = Math.Min(lambda * lambda_UP_fac, 1E7); break; case UpdateType.Quadratic: lambda = lambda + Math.Abs(dX2 / (2D * alpha)); break; case UpdateType.Nielsen: lambda = lambda * nu; nu *= 2D; break; } } if (iteration > MaxIter && !stop) { _result = -1; return p; } } /************************** End Main loop ************************/ return p; }
public NMMatrix Diag() { NMMatrix A = new NMMatrix(_n, _n); for (int i = 0; i < _n; i++) A[i, i] = _vector[i]; return A; }
public NMMatrix Cross(NVector A) { NMMatrix C = new NMMatrix(_n, A._n); for (int i = 0; i < _n; i++) for (int j = 0; j < A._n; j++) C[i,j] += _vector[i] * A._vector[j]; return C; }
public NMMatrix Transpose() { //make shallow copy, so both transposed and untransposed matrix may be used in same calculation NMMatrix A = new NMMatrix(); A._matrix = _matrix; A._m = _m; A._n = _n; A._transpose = !_transpose; return A; }
public static NMMatrix operator *(NMMatrix A, NMMatrix B) { int l1 = A.N; int l2 = B.M; int l3 = A.M; if (l3 != B.N ) throw new Exception("NMMatrix.Mul: incompatable sizes"); NMMatrix C = new NMMatrix(l1, l2); for (int i = 0; i < l1; i++) for (int j = 0; j < l2; j++) { double c = 0D; for (int k = 0; k < l3; k++) c += A[i, k] * B[k, j]; C._matrix[i, j] = c; } return C; }
public NMMatrix ExtractMatrix(int col, int coln) { NMMatrix A = new NMMatrix(N, coln); for (int i = 0; i < N; i++) for (int j = 0; j < coln; j++) A[i, j] = this[i, col + j]; return A; }
public NMMatrix Apply(F func) { NMMatrix A = new NMMatrix(this); for (int i = 0; i < _n; i++) for (int j = 0; j < _m; j++) A[i, j] = func(A[i, j]); return A; }
public NMMatrix Augment(NVector V) { if (N != V.N) throw new Exception("NMMatrix.Augment(Vector): incompatable sizes"); NMMatrix B = new NMMatrix(N, M + 1); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) B[i, j] = this[i, j]; B[i, M] = V[i]; } return B; }
public static NMMatrix operator /(NMMatrix A, double b) { int l1 = A.N; int l2 = A.M; NMMatrix C = new NMMatrix(l1,l2); for (int i = 0; i < l1; i++) for (int j = 0; j < l2; j++) C._matrix[i, j] = A[i, j] / b; return C; }
public static NMMatrix I(int n) { NMMatrix A = new NMMatrix(n, n); for (int i = 0; i < n; i++) A._matrix[i, i] = 1D; return A; }
public static NMMatrix operator -(NMMatrix A, NMMatrix B) { int l1 = A.N; int l2 = A.M; if (l1 != B.N || l2 != B.M) throw new Exception("NMMatrix.Subtract: incompatable sizes"); NMMatrix C = new NMMatrix(A); for (int i = 0; i < l1; i++) for (int j = 0; j < l2; j++) C._matrix[i, j] -= B[i, j]; return C; }
public static NMMatrix operator +(NMMatrix A, NMMatrix B) { if (A.N != B.N || A.M != B.M) throw new Exception("NMMatrix.Add: incompatable sizes"); NMMatrix C = new NMMatrix(A); for (int i = 0; i < A.N; i++) for (int j = 0; j < A.M; j++) C._matrix[i, j] += B[i, j]; return C; }
public static NMMatrix operator *(double a, NMMatrix B) { int l1 = B.N; int l2 = B.M; NMMatrix C = new NMMatrix(B); for (int i = 0; i < l1; i++) for (int j = 0; j < l2; j++) C._matrix[i, j] *= a; return C; }
private NMMatrix Broyden(NVector p_old, NVector y_old, NMMatrix J, NVector p, NVector y) { NVector h = p - p_old; J = J + (y - y_old - J * h).Cross(h) / h.Dot(h); return J; }
public NMMatrix Augment(NMMatrix A) { if (N != A.N) throw new Exception("NMMatrix.Augment(Matrix): incompatable sizes"); NMMatrix B = new NMMatrix(N, M + A.M); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) B[i, j] = this[i, j]; for (int j = 0; j < A.M; j++) B[i, M + j] = A[i, j]; } return B; }
private NMMatrix Jacobian(NVector p, NVector y) { NVector ps = new NVector(p); //save a copy NMMatrix J = new NMMatrix(m, n); //creating a new J from scratch double del_p; for (int j = 0; j < n; j++) { del_p = Math.Max(dp[j] * Math.Abs(p[j]), dp[j]); p[j] = ps[j] + del_p; NVector y1 = func(t, p); if (dp[j] != 0D) //forward or backward difference J.ReplaceColumn(j, (y1 - y) / del_p); else //central difference { p[j] = ps[j] - del_p; J.ReplaceColumn(j, (y1 - func(t, p)) / (2D * del_p)); } p[j] = ps[j]; //restore this value } return J; }
static NMMatrix Jfunc(NVector t, NVector p) { double eat; double ebt; NMMatrix J = new NMMatrix(t.N, p.N); for (int i = 0; i < t.N; i++) { double t0 = t[i] - p[5]; eat = Math.Exp(-p[3] * t0); ebt = Math.Exp(-p[4] * t0); J[i, 1] = 1D - ebt; //B J[i, 2] = ebt; //C J[i, 4] = p[1] - p[2]; //beta J[i, 5] = (p[2] - p[1]) * p[4]; //t0 if (t0 > 0D) //UnitStep portion { J[i, 0] = ebt * (1D - eat); //A J[i, 3] = p[0] * t0 * eat * ebt; //alpha J[i, 4] -= p[0] * (1D - eat); //beta J[i, 5] += p[0] * (p[4] * (1D - eat) - p[3] * eat); //t0 } J[i, 4] *= t0 * ebt; //beta J[i, 5] *= ebt; //t0 } return J; }