home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
vis-ftp.cs.umass.edu
/
vis-ftp.cs.umass.edu.tar
/
vis-ftp.cs.umass.edu
/
pub
/
Software
/
ASCENDER
/
ascender.tar.Z
/
ascender.tar
/
Triangulate
/
marqu.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-04-13
|
14KB
|
536 lines
/* @(#)marqu.c 1.17 12/20/94 */
/* Routines for carrying out the Marquardt algorithm */
#include <math.h>
#include "rh_util.h"
#include "marquardt.h"
#include "array_utils.s"
#include "error_message.s"
#include "newgaussj.s"
#include "newgaussj2.s"
#include "svalue.s"
/* #include "utilities_c.h" */
/* Some local variables */
static double **weight;
static double *goal;
static double *mparam;
static double **covar;
static int nrows, ncols;
static int Num_Loops = 100;
static double Min_Incr = 0.05;
static BOOLEAN diag;
/* Some forward declarations */
FUNCTION_DECL (static void (*gen_jacobian), (
double *mparam,
double **dyda,
int nrows,
int ncols
));
FUNCTION_DECL (static void (*function), (
double *mparams,
double *newvalue,
int nrows,
int ncols));
FUNCTION_DECL (static void (*new_position_action), (
double *mparam,
int ncols));
FUNCTION_DECL (static void mrqcof, (
double **weight,
double **alpha,
double *beta,
double **dyda,
double *dy,
int nrows,
int ncols,
int diag));
FUNCTION_DECL (static double calculate_error, (double *del));
/* Routines for handling an interrupt to tell us to stop after next iteration */
#include <signal.h>
static BOOLEAN interrupt_pending = FALSE;
FUNCTION_DEF ( static void keybd_interrupt , (int errno))
{
if (errno == SIGTSTP) interrupt_pending = TRUE;
}
FUNCTION_DEF ( static void calculate_new_position, (
double *dp,
double *mparam)) /* Parameter values to be updated */
{
/* Calculate the new positions from the new values of the parameters */
int i;
/* Update the solution */
for (i=1; i<=ncols; i++)
mparam[i] += dp[i];
}
FUNCTION_DEF (static void compute_error_vector, (
double *mparams, /* The parameters used for the computation */
double *dy, /* The error vector */
int nrows, /* Number of elements in mparams */
int ncols)) /* Number of elements in dy */
{
/* Determines what the error would be if atry were the new position */
double *newvalue = VECTOR (1, nrows, double);
int i;
/* Call the function to compute the function */
(*function) (mparams, newvalue, nrows, ncols);
/* Now compute the error vector */
for (i=1; i<=nrows; i++)
dy[i] = goal[i] - newvalue[i];
/* Free the temporary */
FREE (newvalue, 1);
}
FUNCTION_DEF (static double calculate_error, (double *del))
{
/* Computes the weighted value of an error vector del */
double error = 0.0;
int i;
/* Now compute the weighted error */
if (diag)
for (i=1; i<=nrows; i++)
error += del[i] * del[i] * weight[1][i];
else
for (i=1; i<=nrows; i++)
{
int j;
/* Add twice the off-diagonal error components */
for (j=1; j<i; j++)
error += 2.0 * del[i] * del[j] * weight[i][j];
/* Add once the diagonal contribution */
error += del[i] * del[i] * weight[i][i];
}
/* The error computation is now complete */
return (error);
}
FUNCTION_DEF (static int compute_covariance, (
double **dyda, /* Jacobian */
double *dy, /* Error vector */
double **weight, /* Weight matrix or vector */
BOOLEAN diag, /* Tells whether weight matrix is diagonal */
int nrows, int ncols, /* Dimensions of the Jacobian matrix */
double **covar /* Returned covariance matrix. Must already be
* allocated */
))
{
/* This is the Marquardt Minimization algorithm. Please read the comment
* in the book Numerical recipes, page 545.
*/
static double oldchisq;
double *beta;
int rval;
/* Return if covar is not allocated */
if (covar == (double **)NULL) return 0;
/* Allocate an array */
beta = VECTOR (1, ncols, double);
/* Calculate the matrices */
mrqcof (weight, covar, beta, dyda, dy, nrows, ncols, diag);
/* Use gaussj to invert the covar matrix */
rval = gaussj_check(covar, ncols, beta);
/* Free the temporary */
FREE (beta, 1);
/* Return the return value */
return (rval);
}
FUNCTION_DEF (static void mrqcof, (
double **weight,
double **alpha,
double *beta,
double **dyda,
double *dy,
int nrows,
int ncols,
int diag))
{
/* Routine used by mrqmin to evaluate the linearized fitting matrix
* alpha[1..ncols][1..ncols] and vector beta[1..ncols]
* as in Cookbook, (14.4.8). */
/* Initialize (symmetric) alpha, beta */
{
int j, k;
for (j=1; j<=ncols; j++)
for (k=1; k<=j; k++)
alpha[j][k] = beta[j] = 0.0;
}
/* Compute the alpha and beta matrices */
if (diag)
{
/* Calculate J^T*J and J^T*dy */
register int i;
for (i=1; i<=nrows; i++)
{
/* summation loop over all data */
register int j;
for (j=1; j<=ncols; j++)
{
register int k;
double wt = dyda[i][j]*weight[1][i];
for (k=1; k<=j; k++)
alpha[j][k] += wt*dyda[i][k];
beta[j] += dy[i]*wt;
}
}
}
else
{
/* Compute alpha = J^T * C * J and beta = J^T * C * dy */
register int i;
for (i=1; i<=ncols; i++)
{
int k;
for (k=1; k<=nrows; k++)
{
int j;
double JTC_i_k = 0.0;
/* Compute the i,k-th entry of J^T * C */
for (j=1; j<=nrows; j++)
JTC_i_k += dyda[j][i] * weight[j][k];
/* Now multiply by J */
for (j=1; j<=i; j++)
alpha[i][j] += JTC_i_k * dyda[k][j];
/* Compute J^T*C*dy */
beta[i] += JTC_i_k * dy[k];
}
}
}
/* Fill in the symmetric side */
{
register int j, k;
for (j=2; j<=ncols; j++)
for (k=1; k<=j-1; k++) alpha[k][j] = alpha[j][k];
}
}
#define FAC 1.0e-6
#define DELTA 1.0e-6
FUNCTION_DEF (static void compute_jacobian, (
double *mparam,
double **dyda,
int nrows, int ncols))
{
/* Computes the jacobian by approximation */
int i, j;
double *oldval = VECTOR (1, nrows, double);
double *newval = VECTOR (1, nrows, double);
/* Evaluate the function at the current point */
(*function) (mparam, oldval, nrows, ncols);
/* Now change the parameters one after another */
for (j=1; j<=ncols; j++)
{
/* Remember the value of the j-th parameter */
double oldparam = mparam[j];
/* Increment the j-th parameter */
double delta = FAC * mparam[j];
if (fabs (delta) < DELTA) delta = DELTA;
mparam[j] += delta;
/* Now recompute the function */
(*function) (mparam, newval, nrows, ncols);
/* Now fill out the jacobian values */
for (i=1; i<=nrows; i++)
dyda[i][j] = (newval[i] - oldval[i]) / delta;
/* Restore the value of mparam[j] */
mparam[j] = oldparam;
}
/* Free the temporaries */
FREE (oldval, 1);
FREE (newval, 1);
}
FUNCTION_DEF (static double marquardt_go, ())
{
/* Declare variables */
int icount;
double alamda = 1.0;
double chisq;
BOOLEAN improvement = TRUE;
/* Allocate a bunch of storage */
double *da = VECTOR (1, ncols, double);
double **dyda = MATRIX (1, nrows, 0, ncols, double);
double *dy = VECTOR (1, nrows, double);
double *newparams = VECTOR (1, ncols, double);
double *newdy = VECTOR (1, nrows, double);
double **aprime = MATRIX (1, ncols, 1, ncols, double);
double *beta = VECTOR (1, ncols, double);
double **alpha = MATRIX (1, ncols, 1, ncols, double);
/* Now iterate */
for (icount=0; icount<Num_Loops && improvement; icount++)
{
int j;
double increment;
/* Compute the error vector */
compute_error_vector (mparam, dy, nrows, ncols);
/* Compute an initial value of the error */
chisq = calculate_error (dy);
/* Output some debugging info */
if (icount == 0)
informative_message ("\tInitial error : chisq = %g, rms = %g",
chisq, sqrt(chisq/nrows));
/* Compute the Jacobian */
if (gen_jacobian)
(*gen_jacobian) (mparam, dyda, nrows, ncols);
else
compute_jacobian (mparam, dyda, nrows, ncols);
#ifdef WRITE_JACOBIAN
fprintf (stderr, "\nJacobian (%d x %d) : \n", nrows, ncols);
fprint_matrix ("%11.3f ", stderr, dyda, 1, nrows, 1, ncols);
#endif
/* Calculate the matrices */
mrqcof (weight, alpha, beta, dyda, dy, nrows, ncols, diag);
/* Now estimate new position with different alambda until improvement */
do {
double newchisq;
int i;
/* Copy the values of alpha to aprime, the augmented alpha */
for (i=1; i<=ncols; i++)
{
/* Copy beta */
da[i] = beta[i];
/* Copy alpha */
for (j=1; j<=ncols; j++)
aprime[i][j] = alpha[i][j];
}
/* Augment diagonal elements of aprime */
for (j=1; j<=ncols; j++)
aprime[j][j] *= 1.0 + (alamda);
/* Matrix solution. Solution overwrites beta, now called da */
if (! (lin_solve(aprime, da, da, ncols)
|| solve_simple (aprime, da, da, ncols, ncols)))
{
error_message ("Can not solve normal equations -- exiting");
bail_out (1);
}
/* Compute the length of the increment */
increment = 0.0;
for (j=1; j<=ncols; j++)
increment += da[j] * da[j];
/* Calculate new error at this position */
{
int i;
/* Add the delta to get new provisional parameter set */
for (i=1; i<=ncols; i++)
newparams[i] = mparam[i] + da[i];
/* Compute the error vector at this value */
compute_error_vector (newparams, newdy, nrows, ncols);
/* Get the value of chisq resulting from this error vector */
newchisq = calculate_error(newdy);
}
/* Accept or reject it */
if (newchisq < chisq)
{
/* Success, accept this new solution */
alamda *= 0.1;
chisq = newchisq;
/* Accept the new parameter values */
for (i=1; i<=ncols; i++) mparam[i] = newparams[i];
/* Call any user function */
if (new_position_action)
(*new_position_action) (mparam, ncols);
/* Signal improvement */
improvement = TRUE;
}
else
{
/* Failure, increas alamda and return. */
alamda *= 10.0;
improvement = FALSE;
}
} while ( ! improvement && increment >= Min_Incr && (alamda < 1e10));
/* Output some debugging info */
informative_message ("\talamda = %5.0e, chisq = %g, rms = %g",
alamda, chisq, sqrt(chisq/nrows));
/* Test for an interrupt */
if (interrupt_pending) break;
/* Test to see if the increment has been too small */
if (increment < Min_Incr) break;
}
/* Compute the covariance matrix */
if (covar)
if (! compute_covariance(dyda, dy, weight, diag, nrows, ncols, covar))
error_message ("Can not compute covariance matrix");
/* Free the storage */
FREE(da,1);
FREE(dyda,1);
FREE(dy,1);
FREE(newparams,1);
FREE(newdy,1);
FREE(aprime,1);
FREE(beta,1);
FREE(alpha,1);
/* Return the value of the error */
return chisq;
}
FUNCTION_DEF (double marquardt, (Marquardt_info *mar))
{
/* Do marquardt iteration */
double return_val; /* The error of fit */
BOOLEAN free_weights = FALSE; /* Free if we allocated here */
/* Pick the local variables off the marquardt descriptor structure */
if (mar->Num_Loops > 0) Num_Loops = mar->Num_Loops;
if (mar->Min_Incr >= 0.0) Min_Incr = mar->Min_Incr;
nrows = mar->nrows;
ncols = mar->ncols;
weight = mar->weight;
goal = mar->goal;
mparam = mar->param;
diag = mar->diag;
covar = mar->covar;
gen_jacobian = mar->gen_jacobian;
function = mar->function;
new_position_action = mar->new_position_action;
/* Set up the interrupt_pending value */
interrupt_pending = FALSE;
signal (SIGTSTP, /*(SIG_PF)*/ keybd_interrupt);
/* If the weight matrix is not known, then make it the identity matrix */
if (! weight)
{
int i;
weight = MATRIX (1, 1, 1, nrows, double);
free_weights = TRUE;
for (i=1; i<=nrows; i++) weight[1][i] = 1.0;
diag = TRUE;
}
/* Now that house-keeping is done, start the routine */
return_val = marquardt_go();
/* Free the weights */
if (free_weights) FREE (weight, 1);
/* Return success of failure */
return return_val;
}
FUNCTION_DEF (void marquardt_debug, (
double **alpha,
double *beta))
{
/* Outputs debug info to test the new algorithm */
int i, j;
int d1 = 12;
int d2 = 24;
double **A = MATRIX (1, d1, 1, d1, double);
double **BT = alpha+d1;
double **C = MATRIX (1, d2, 1, d2, double);
double **B = MATRIX (1, d1, 1, d2, double);
double *K1 = VECTOR (1, d1, double);
double *K2 = beta + d1;
/* Transfer matrixes C and B */
for (i=1; i<=d2; i++)
for (j=1; j<=d2; j++)
C[i][j] = alpha[i+d1][j+d1];
for (i=1; i<=d1; i++)
for (j=1; j<=d2; j++)
B[i][j] = alpha [i][j+d1];
/* Invert C in place */
if (! gaussj2_check (C, d2, (double **) 0, 0))
{
error_message ("Can not invert matrix");
bail_out (1);
}
/* Multiply it on the right by B */
matrix_prod (B, C, d1, d2, d2, B);
/* Now multiply it into A */
matrix_prod (B, BT, d1, d2, d1, A);
/* Change the sign and add the elements of alpha */
for (i=1; i<=d1; i++)
for (j=1; j<=d1; j++)
A[i][j] = alpha[i][j] - A[i][j];
/* Now do the same thing for the goal vector */
matrix_vector_prod (B, K2, d1, d2, K1);
for (i=1; i<=d1; i++)
K1[i] = beta[i] - K1[i];
/* Now output the two matrices */
fprintf (stderr, "\nGOAL VECTOR\n");
fprint_vector ("%10.3e ", stderr, K1, 1, d1);
fprintf (stderr, "\nNORMAL MATRIX\n");
fprint_matrix ("%10.3e ", stderr, A, 1, d1, 1, d1);
/* Free the temporaries */
FREE (A, 1);
FREE (C, 1);
FREE (B, 1);
FREE (K1, 1);
}