Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / matrix.c
diff --git a/src/matrix.c b/src/matrix.c
new file mode 100644 (file)
index 0000000..fa84eb4
--- /dev/null
@@ -0,0 +1,361 @@
+#ifndef lint
+static char *RCSid() { return RCSid("$Id: matrix.c,v 1.10 2004/07/01 17:10:06 broeker Exp $"); }
+#endif
+
+/*  NOTICE: Change of Copyright Status
+ *
+ *  The author of this module, Carsten Grammes, has expressed in
+ *  personal email that he has no more interest in this code, and
+ *  doesn't claim any copyright. He has agreed to put this module
+ *  into the public domain.
+ *
+ *  Lars Hecking  15-02-1999
+ */
+
+/*
+ *      Matrix algebra, part of
+ *
+ *      Nonlinear least squares fit according to the
+ *      Marquardt-Levenberg-algorithm
+ *
+ *      added as Patch to Gnuplot (v3.2 and higher)
+ *      by Carsten Grammes
+ *      Experimental Physics, University of Saarbruecken, Germany
+ *
+ *      Previous copyright of this module:   Carsten Grammes, 1993
+ *
+ */
+
+#include "matrix.h"
+
+#include "alloc.h"
+#include "fit.h"
+#include "util.h"
+
+/*****************************************************************/
+
+#define Swap(a,b)   {double temp = (a); (a) = (b); (b) = temp;}
+/* HBB 20010424: unused: */
+/* #define WINZIG            1e-30 */
+
+
+/*****************************************************************
+    internal prototypes
+*****************************************************************/
+
+static GP_INLINE int fsign __PROTO((double x));
+
+/*****************************************************************
+    first straightforward vector and matrix allocation functions
+*****************************************************************/
+
+/* allocates a double vector with n elements */
+double *
+vec(int n)
+{
+    double *dp;
+
+    if (n < 1)
+       return NULL;
+    dp = gp_alloc(n * sizeof(double), "vec");
+    return dp;
+}
+
+
+/* allocates a double matrix */
+double **
+matr(int rows, int cols)
+{
+    int i;
+    double **m;
+
+    if (rows < 1 || cols < 1)
+       return NULL;
+    m = gp_alloc(rows * sizeof(m[0]), "matrix row pointers");
+    m[0] = gp_alloc(rows * cols * sizeof(m[0][0]), "matrix elements");
+    for (i = 1; i < rows; i++)
+       m[i] = m[i - 1] + cols;
+    return m;
+}
+
+
+void
+free_matr(double **m)
+{
+    free(m[0]);
+    free(m);
+}
+
+
+double *
+redim_vec(double **v, int n)
+{
+    if (n < 1)
+       *v = NULL;
+    else
+       *v = gp_realloc(*v, n * sizeof((*v)[0]), "vec");
+    return *v;
+}
+
+/* HBB: TODO: is there a better value for 'epsilon'? how to specify
+ * 'inline'?  is 'fsign' really not available elsewhere? use
+ * row-oriented version (p. 309) instead?
+ */
+
+static GP_INLINE int
+fsign(double x)
+{
+    return (x > 0 ? 1 : (x < 0) ? -1 : 0);
+}
+
+/*****************************************************************
+
+     Solve least squares Problem C*x+d = r, |r| = min!, by Given rotations
+     (QR-decomposition). Direct implementation of the algorithm
+     presented in H.R.Schwarz: Numerische Mathematik, 'equation'
+     number (7.33)
+
+     If 'd == NULL', d is not accesed: the routine just computes the QR
+     decomposition of C and exits.
+
+     If 'want_r == 0', r is not rotated back (\hat{r} is returned
+     instead).
+
+*****************************************************************/
+
+void
+Givens(
+    double **C,
+    double *d,
+    double *x,
+    double *r,
+    int N,
+    int n,
+    int want_r)
+{
+    int i, j, k;
+    double w, gamma, sigma, rho, temp;
+    double epsilon = DBL_EPSILON;      /* FIXME (?) */
+
+/*
+ * First, construct QR decomposition of C, by 'rotating away'
+ * all elements of C below the diagonal. The rotations are
+ * stored in place as Givens coefficients rho.
+ * Vector d is also rotated in this same turn, if it exists
+ */
+    for (j = 0; j < n; j++) {
+       for (i = j + 1; i < N; i++) {
+           if (C[i][j]) {
+               if (fabs(C[j][j]) < epsilon * fabs(C[i][j])) {
+                   /* find the rotation parameters */
+                   w = -C[i][j];
+                   gamma = 0;
+                   sigma = 1;
+                   rho = 1;
+               } else {
+                   w = fsign(C[j][j]) * sqrt(C[j][j] * C[j][j] + C[i][j] * C[i][j]);
+                   if (w == 0)
+                       Eex3("w = 0 in Givens();  Cjj = %g,  Cij = %g", C[j][j], C[i][j]);
+                   gamma = C[j][j] / w;
+                   sigma = -C[i][j] / w;
+                   rho = (fabs(sigma) < gamma) ? sigma : fsign(sigma) / gamma;
+               }
+               C[j][j] = w;
+               C[i][j] = rho;  /* store rho in place, for later use */
+               for (k = j + 1; k < n; k++) {
+                   /* rotation on index pair (i,j) */
+                   temp = gamma * C[j][k] - sigma * C[i][k];
+                   C[i][k] = sigma * C[j][k] + gamma * C[i][k];
+                   C[j][k] = temp;
+
+               }
+               if (d) {        /* if no d vector given, don't use it */
+                   temp = gamma * d[j] - sigma * d[i];         /* rotate d */
+                   d[i] = sigma * d[j] + gamma * d[i];
+                   d[j] = temp;
+               }
+           }
+       }
+    }
+
+    if (!d)                    /* stop here if no d was specified */
+       return;
+
+    /* solve R*x+d = 0, by backsubstitution */
+    for (i = n - 1; i >= 0; i--) {
+       double s = d[i];
+
+       r[i] = 0;               /* ... and also set r[i] = 0 for i<n */
+       for (k = i + 1; k < n; k++)
+           s += C[i][k] * x[k];
+       if (C[i][i] == 0)
+           Eex("Singular matrix in Givens()");
+       x[i] = -s / C[i][i];
+    }
+
+    for (i = n; i < N; i++)
+       r[i] = d[i];            /* set the other r[i] to d[i] */
+
+    if (!want_r)               /* if r isn't needed, stop here */
+       return;
+
+    /* rotate back the r vector */
+    for (j = n - 1; j >= 0; j--) {
+       for (i = N - 1; i >= 0; i--) {
+           if ((rho = C[i][j]) == 1) {         /* reconstruct gamma, sigma from stored rho */
+               gamma = 0;
+               sigma = 1;
+           } else if (fabs(rho) < 1) {
+               sigma = rho;
+               gamma = sqrt(1 - sigma * sigma);
+           } else {
+               gamma = 1 / fabs(rho);
+               sigma = fsign(rho) * sqrt(1 - gamma * gamma);
+           }
+           temp = gamma * r[j] + sigma * r[i];         /* rotate back indices (i,j) */
+           r[i] = -sigma * r[j] + gamma * r[i];
+           r[j] = temp;
+       }
+    }
+}
+
+
+/* Given a triangular Matrix R, compute (R^T * R)^(-1), by forward
+ * then back substitution
+ *
+ * R, I are n x n Matrices, I is for the result. Both must already be
+ * allocated.
+ *
+ * Will only calculate the lower triangle of I, as it is symmetric
+ */
+
+void
+Invert_RtR(double **R, double **I, int n)
+{
+    int i, j, k;
+
+    /* fill in the I matrix, and check R for regularity : */
+
+    for (i = 0; i < n; i++) {
+       for (j = 0; j < i; j++) /* upper triangle isn't needed */
+           I[i][j] = 0;
+       I[i][i] = 1;
+       if (!R[i][i])
+           Eex("Singular matrix in Invert_RtR");
+    }
+
+    /* Forward substitution: Solve R^T * B = I, store B in place of I */
+
+    for (k = 0; k < n; k++) {
+       for (i = k; i < n; i++) {       /* upper half needn't be computed */
+           double s = I[i][k];
+           for (j = k; j < i; j++)     /* for j<k, I[j][k] always stays zero! */
+               s -= R[j][i] * I[j][k];
+           I[i][k] = s / R[i][i];
+       }
+    }
+    /* Backward substitution: Solve R * A = B, store A in place of B */
+
+    for (k = 0; k < n; k++) {
+       for (i = n - 1; i >= k; i--) {  /* don't compute upper triangle of A */
+           double s = I[i][k];
+           for (j = i + 1; j < n; j++)
+               s -= R[i][j] * I[j][k];
+           I[i][k] = s / R[i][i];
+       }
+    }
+}
+
+/* HBB 20010424: Functions that used to be here in matrix.c, but were
+ * replaced by others and deleted, later.  But the
+ * THIN_PLATE_SPLINES_GRID needed them, later, so they appeared in
+ * plot3d.c, where they don't belong --> moved them back here. */
+
+void
+lu_decomp(double **a, int n, int *indx, double *d)
+{
+    int i, imax = -1, j, k;    /* HBB: added initial value, to shut up gcc -Wall */
+    double large, dummy, temp, **ar, **lim, *limc, *ac, *dp, *vscal;
+
+    dp = vscal = vec(n);
+    *d = 1.0;
+    for (ar = a, lim = &(a[n]); ar < lim; ar++) {
+       large = 0.0;
+       for (ac = *ar, limc = &(ac[n]); ac < limc;)
+           if ((temp = fabs(*ac++)) > large)
+               large = temp;
+       if (large == 0.0)
+           int_error(NO_CARET, "Singular matrix in LU-DECOMP");
+       *dp++ = 1 / large;
+    }
+    ar = a;
+    for (j = 0; j < n; j++, ar++) {
+       for (i = 0; i < j; i++) {
+           ac = &(a[i][j]);
+           for (k = 0; k < i; k++)
+               *ac -= a[i][k] * a[k][j];
+       }
+       large = 0.0;
+       dp = &(vscal[j]);
+       for (i = j; i < n; i++) {
+           ac = &(a[i][j]);
+           for (k = 0; k < j; k++)
+               *ac -= a[i][k] * a[k][j];
+           if ((dummy = *dp++ * fabs(*ac)) >= large) {
+               large = dummy;
+               imax = i;
+           }
+       }
+       if (j != imax) {
+           ac = a[imax];
+           dp = *ar;
+           for (k = 0; k < n; k++, ac++, dp++)
+               Swap(*ac, *dp);
+           *d = -(*d);
+           vscal[imax] = vscal[j];
+       }
+       indx[j] = imax;
+       if (*(dp = &(*ar)[j]) == 0)
+           *dp = 1e-30;
+
+       if (j != n - 1) {
+           dummy = 1 / (*ar)[j];
+           for (i = j + 1; i < n; i++)
+               a[i][j] *= dummy;
+       }
+    }
+    free(vscal);
+}
+
+void
+lu_backsubst(double **a, int n, int *indx, double *b)
+{
+    int i, memi = -1, ip, j;
+
+    double sum, *bp, *bip, **ar, *ac;
+
+    ar = a;
+
+    for (i = 0; i < n; i++, ar++) {
+       ip = indx[i];
+       sum = b[ip];
+       b[ip] = b[i];
+       if (memi >= 0) {
+           ac = &((*ar)[memi]);
+           bp = &(b[memi]);
+           for (j = memi; j <= i - 1; j++)
+               sum -= *ac++ * *bp++;
+       } else if (sum)
+           memi = i;
+       b[i] = sum;
+    }
+    ar--;
+    for (i = n - 1; i >= 0; i--) {
+       ac = &(*ar)[i + 1];
+       bp = &(b[i + 1]);
+       bip = &(b[i]);
+       for (j = i + 1; j < n; j++)
+           *bip -= *ac++ * *bp++;
+       *bip /= (*ar--)[i];
+    }
+}