3 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Brian Gough
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or (at
8 * your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <gsl/gsl_permute_vector_double.h>
25 count_nsing (const gsl_matrix * r)
27 /* Count the number of nonsingular entries. Returns the index of the
28 first entry which is singular. */
33 for (i = 0; i < n; i++)
35 double rii = gsl_matrix_get (r, i, i);
48 compute_newton_direction (const gsl_matrix * r, const gsl_permutation * perm,
49 const gsl_vector * qtf, gsl_vector * x)
52 /* Compute and store in x the Gauss-Newton direction. If the
53 Jacobian is rank-deficient then obtain a least squares
56 const size_t n = r->size2;
59 for (i = 0 ; i < n ; i++)
61 double qtfi = gsl_vector_get (qtf, i);
62 gsl_vector_set (x, i, qtfi);
65 nsing = count_nsing (r);
68 printf("nsing = %d\n", nsing);
69 printf("r = "); gsl_matrix_fprintf(stdout, r, "%g"); printf("\n");
70 printf("qtf = "); gsl_vector_fprintf(stdout, x, "%g"); printf("\n");
73 for (i = nsing; i < n; i++)
75 gsl_vector_set (x, i, 0.0);
80 for (j = nsing; j > 0 && j--;)
82 double rjj = gsl_matrix_get (r, j, j);
83 double temp = gsl_vector_get (x, j) / rjj;
85 gsl_vector_set (x, j, temp);
87 for (i = 0; i < j; i++)
89 double rij = gsl_matrix_get (r, i, j);
90 double xi = gsl_vector_get (x, i);
91 gsl_vector_set (x, i, xi - rij * temp);
96 gsl_permute_vector_inverse (perm, x);
100 compute_newton_correction (const gsl_matrix * r, const gsl_vector * sdiag,
101 const gsl_permutation * p, gsl_vector * x,
103 const gsl_vector * diag, gsl_vector * w)
108 for (i = 0; i < n; i++)
110 size_t pi = gsl_permutation_get (p, i);
112 double dpi = gsl_vector_get (diag, pi);
113 double xpi = gsl_vector_get (x, pi);
115 gsl_vector_set (w, i, dpi * (dpi * xpi) / dxnorm);
118 for (j = 0; j < n; j++)
120 double sj = gsl_vector_get (sdiag, j);
121 double wj = gsl_vector_get (w, j);
125 gsl_vector_set (w, j, tj);
127 for (i = j + 1; i < n; i++)
129 double rij = gsl_matrix_get (r, i, j);
130 double wi = gsl_vector_get (w, i);
132 gsl_vector_set (w, i, wi - rij * tj);
138 compute_newton_bound (const gsl_matrix * r, const gsl_vector * x,
139 double dxnorm, const gsl_permutation * perm,
140 const gsl_vector * diag, gsl_vector * w)
142 /* If the jacobian is not rank-deficient then the Newton step
143 provides a lower bound for the zero of the function. Otherwise
144 set this bound to zero. */
150 size_t nsing = count_nsing (r);
154 gsl_vector_set_zero (w);
158 for (i = 0; i < n; i++)
160 size_t pi = gsl_permutation_get (perm, i);
162 double dpi = gsl_vector_get (diag, pi);
163 double xpi = gsl_vector_get (x, pi);
165 gsl_vector_set (w, i, dpi * (dpi * xpi / dxnorm));
168 for (j = 0; j < n; j++)
172 for (i = 0; i < j; i++)
174 sum += gsl_matrix_get (r, i, j) * gsl_vector_get (w, i);
178 double rjj = gsl_matrix_get (r, j, j);
179 double wj = gsl_vector_get (w, j);
181 gsl_vector_set (w, j, (wj - sum) / rjj);
187 compute_gradient_direction (const gsl_matrix * r, const gsl_permutation * p,
188 const gsl_vector * qtf, const gsl_vector * diag,
191 const size_t n = r->size2;
195 for (j = 0; j < n; j++)
199 for (i = 0; i <= j; i++)
201 sum += gsl_matrix_get (r, i, j) * gsl_vector_get (qtf, i);
205 size_t pj = gsl_permutation_get (p, j);
206 double dpj = gsl_vector_get (diag, pj);
208 gsl_vector_set (g, j, sum / dpj);
214 lmpar (gsl_matrix * r, const gsl_permutation * perm, const gsl_vector * qtf,
215 const gsl_vector * diag, double delta, double * par_inout,
216 gsl_vector * newton, gsl_vector * gradient, gsl_vector * sdiag,
217 gsl_vector * x, gsl_vector * w)
219 double dxnorm, gnorm, fp, fp_old, par_lower, par_upper, par_c;
221 double par = *par_inout;
226 printf("ENTERING lmpar\n");
230 compute_newton_direction (r, perm, qtf, newton);
233 printf ("newton = ");
234 gsl_vector_fprintf (stdout, newton, "%g");
238 gsl_vector_fprintf (stdout, diag, "%g");
242 /* Evaluate the function at the origin and test for acceptance of
243 the Gauss-Newton direction. */
245 dxnorm = scaled_enorm (diag, newton);
250 printf ("dxnorm = %g, delta = %g, fp = %g\n", dxnorm, delta, fp);
253 if (fp <= 0.1 * delta)
255 gsl_vector_memcpy (x, newton);
257 printf ("took newton (fp = %g, delta = %g)\n", fp, delta);
267 gsl_matrix_fprintf (stdout, r, "%g");
270 printf ("newton = ");
271 gsl_vector_fprintf (stdout, newton, "%g");
274 printf ("dxnorm = %g\n", dxnorm);
278 compute_newton_bound (r, newton, dxnorm, perm, diag, w);
281 printf("perm = "); gsl_permutation_fprintf(stdout, perm, "%d");
284 gsl_vector_fprintf (stdout, diag, "%g");
288 gsl_vector_fprintf (stdout, w, "%g");
294 double wnorm = enorm (w);
295 double phider = wnorm * wnorm;
297 /* w == zero if r rank-deficient,
298 then set lower bound to zero form MINPACK, lmder.f
299 Hans E. Plesser 2002-02-25 (hans.plesser@itf.nlh.no) */
301 par_lower = fp / (delta * phider);
307 printf("par = %g\n", par );
308 printf("par_lower = %g\n", par_lower);
311 compute_gradient_direction (r, perm, qtf, diag, gradient);
313 gnorm = enorm (gradient);
316 printf("gradient = "); gsl_vector_fprintf(stdout, gradient, "%g"); printf("\n");
317 printf("gnorm = %g\n", gnorm);
320 par_upper = gnorm / delta;
324 par_upper = GSL_DBL_MIN / GSL_MIN_DBL(delta, 0.1);
328 printf("par_upper = %g\n", par_upper);
334 printf("set par to par_upper\n");
339 else if (par < par_lower)
342 printf("set par to par_lower\n");
350 par = gnorm / dxnorm;
352 printf("set par to gnorm/dxnorm = %g\n", par);
357 /* Beginning of iteration */
364 printf("lmpar iteration = %d\n", iter);
368 /* Seems like this is described in the paper but not in the MINPACK code */
370 if (par < par_lower || par > par_upper)
372 par = GSL_MAX_DBL (0.001 * par_upper, sqrt(par_lower * par_upper));
376 /* Evaluate the function at the current value of par */
380 par = GSL_MAX_DBL (0.001 * par_upper, GSL_DBL_MIN);
382 printf("par = 0, set par to = %g\n", par);
387 /* Compute the least squares solution of [ R P x - Q^T f, sqrt(par) D x]
391 printf ("calling qrsolv with par = %g\n", par);
395 double sqrt_par = sqrt(par);
397 qrsolv (r, perm, sqrt_par, diag, qtf, x, sdiag, w);
400 dxnorm = scaled_enorm (diag, x);
407 printf ("After qrsolv dxnorm = %g, delta = %g, fp = %g\n", dxnorm, delta, fp);
408 printf ("sdiag = ") ; gsl_vector_fprintf(stdout, sdiag, "%g"); printf("\n");
409 printf ("x = ") ; gsl_vector_fprintf(stdout, x, "%g"); printf("\n");
410 printf ("r = ") ; gsl_matrix_fprintf(stdout, r, "%g"); printf("\nXXX\n");
413 /* If the function is small enough, accept the current value of par */
415 if (fabs (fp) <= 0.1 * delta)
418 if (par_lower == 0 && fp <= fp_old && fp_old < 0)
421 /* Check for maximum number of iterations */
426 /* Compute the Newton correction */
428 compute_newton_correction (r, sdiag, perm, x, dxnorm, diag, w);
431 printf ("newton_correction = ");
432 gsl_vector_fprintf(stdout, w, "%g"); printf("\n");
436 double wnorm = enorm (w);
437 par_c = fp / (delta * wnorm * wnorm);
441 printf("fp = %g\n", fp);
442 printf("par_lower = %g\n", par_lower);
443 printf("par_upper = %g\n", par_upper);
444 printf("par_c = %g\n", par_c);
448 /* Depending on the sign of the function, update par_lower or par_upper */
456 printf("fp > 0: set par_lower = par = %g\n", par);
466 printf("fp < 0: set par_upper = par = %g\n", par);
472 /* Compute an improved estimate for par */
475 printf("improved estimate par = MAX(%g, %g) \n", par_lower, par+par_c);
478 par = GSL_MAX_DBL (par_lower, par + par_c);
481 printf("improved estimate par = %g \n", par);
490 printf("LEAVING lmpar, par = %g\n", par);