summaryrefslogtreecommitdiff
path: root/gsl-1.9/multifit/multilinear.c
diff options
context:
space:
mode:
Diffstat (limited to 'gsl-1.9/multifit/multilinear.c')
-rw-r--r--gsl-1.9/multifit/multilinear.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/gsl-1.9/multifit/multilinear.c b/gsl-1.9/multifit/multilinear.c
new file mode 100644
index 0000000..6616809
--- /dev/null
+++ b/gsl-1.9/multifit/multilinear.c
@@ -0,0 +1,432 @@
+/* multifit/multilinear.c
+ *
+ * Copyright (C) 2000 Brian Gough
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_multifit.h>
+#include <gsl/gsl_blas.h>
+#include <gsl/gsl_linalg.h>
+
+/* Fit
+ *
+ * y = X c
+ *
+ * where X is an M x N matrix of M observations for N variables.
+ *
+ */
+
+int
+gsl_multifit_linear (const gsl_matrix * X,
+ const gsl_vector * y,
+ gsl_vector * c,
+ gsl_matrix * cov,
+ double *chisq, gsl_multifit_linear_workspace * work)
+{
+ size_t rank;
+ int status = gsl_multifit_linear_svd (X, y, GSL_DBL_EPSILON, &rank, c,
+ cov, chisq, work);
+ return status;
+}
+
+/* Handle the general case of the SVD with tolerance and rank */
+
+int
+gsl_multifit_linear_svd (const gsl_matrix * X,
+ const gsl_vector * y,
+ double tol,
+ size_t * rank,
+ gsl_vector * c,
+ gsl_matrix * cov,
+ double *chisq, gsl_multifit_linear_workspace * work)
+{
+ if (X->size1 != y->size)
+ {
+ GSL_ERROR
+ ("number of observations in y does not match rows of matrix X",
+ GSL_EBADLEN);
+ }
+ else if (X->size2 != c->size)
+ {
+ GSL_ERROR ("number of parameters c does not match columns of matrix X",
+ GSL_EBADLEN);
+ }
+ else if (cov->size1 != cov->size2)
+ {
+ GSL_ERROR ("covariance matrix is not square", GSL_ENOTSQR);
+ }
+ else if (c->size != cov->size1)
+ {
+ GSL_ERROR
+ ("number of parameters does not match size of covariance matrix",
+ GSL_EBADLEN);
+ }
+ else if (X->size1 != work->n || X->size2 != work->p)
+ {
+ GSL_ERROR
+ ("size of workspace does not match size of observation matrix",
+ GSL_EBADLEN);
+ }
+ else if (tol <= 0)
+ {
+ GSL_ERROR ("tolerance must be positive", GSL_EINVAL);
+ }
+ else
+ {
+ const size_t n = X->size1;
+ const size_t p = X->size2;
+
+ size_t i, j, p_eff;
+
+ gsl_matrix *A = work->A;
+ gsl_matrix *Q = work->Q;
+ gsl_matrix *QSI = work->QSI;
+ gsl_vector *S = work->S;
+ gsl_vector *xt = work->xt;
+ gsl_vector *D = work->D;
+
+ /* Copy X to workspace, A <= X */
+
+ gsl_matrix_memcpy (A, X);
+
+ /* Balance the columns of the matrix A */
+
+ gsl_linalg_balance_columns (A, D);
+
+ /* Decompose A into U S Q^T */
+
+ gsl_linalg_SV_decomp_mod (A, QSI, Q, S, xt);
+
+ /* Solve y = A c for c */
+
+ gsl_blas_dgemv (CblasTrans, 1.0, A, y, 0.0, xt);
+
+ /* Scale the matrix Q, Q' = Q S^-1 */
+
+ gsl_matrix_memcpy (QSI, Q);
+
+ {
+ double alpha0 = gsl_vector_get (S, 0);
+ p_eff = 0;
+
+ for (j = 0; j < p; j++)
+ {
+ gsl_vector_view column = gsl_matrix_column (QSI, j);
+ double alpha = gsl_vector_get (S, j);
+
+ if (alpha <= tol * alpha0) {
+ alpha = 0.0;
+ } else {
+ alpha = 1.0 / alpha;
+ p_eff++;
+ }
+
+ gsl_vector_scale (&column.vector, alpha);
+ }
+
+ *rank = p_eff;
+ }
+
+ gsl_vector_set_zero (c);
+
+ gsl_blas_dgemv (CblasNoTrans, 1.0, QSI, xt, 0.0, c);
+
+ /* Unscale the balancing factors */
+
+ gsl_vector_div (c, D);
+
+ /* Compute chisq, from residual r = y - X c */
+
+ {
+ double s2 = 0, r2 = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ double yi = gsl_vector_get (y, i);
+ gsl_vector_const_view row = gsl_matrix_const_row (X, i);
+ double y_est, ri;
+ gsl_blas_ddot (&row.vector, c, &y_est);
+ ri = yi - y_est;
+ r2 += ri * ri;
+ }
+
+ s2 = r2 / (n - p_eff); /* p_eff == rank */
+
+ *chisq = r2;
+
+ /* Form variance-covariance matrix cov = s2 * (Q S^-1) (Q S^-1)^T */
+
+ for (i = 0; i < p; i++)
+ {
+ gsl_vector_view row_i = gsl_matrix_row (QSI, i);
+ double d_i = gsl_vector_get (D, i);
+
+ for (j = i; j < p; j++)
+ {
+ gsl_vector_view row_j = gsl_matrix_row (QSI, j);
+ double d_j = gsl_vector_get (D, j);
+ double s;
+
+ gsl_blas_ddot (&row_i.vector, &row_j.vector, &s);
+
+ gsl_matrix_set (cov, i, j, s * s2 / (d_i * d_j));
+ gsl_matrix_set (cov, j, i, s * s2 / (d_i * d_j));
+ }
+ }
+ }
+
+ return GSL_SUCCESS;
+ }
+}
+
+int
+gsl_multifit_wlinear (const gsl_matrix * X,
+ const gsl_vector * w,
+ const gsl_vector * y,
+ gsl_vector * c,
+ gsl_matrix * cov,
+ double *chisq, gsl_multifit_linear_workspace * work)
+{
+ size_t rank;
+ int status = gsl_multifit_wlinear_svd (X, w, y, GSL_DBL_EPSILON, &rank, c,
+ cov, chisq, work);
+ return status;
+}
+
+
+int
+gsl_multifit_wlinear_svd (const gsl_matrix * X,
+ const gsl_vector * w,
+ const gsl_vector * y,
+ double tol,
+ size_t * rank,
+ gsl_vector * c,
+ gsl_matrix * cov,
+ double *chisq, gsl_multifit_linear_workspace * work)
+{
+ if (X->size1 != y->size)
+ {
+ GSL_ERROR
+ ("number of observations in y does not match rows of matrix X",
+ GSL_EBADLEN);
+ }
+ else if (X->size2 != c->size)
+ {
+ GSL_ERROR ("number of parameters c does not match columns of matrix X",
+ GSL_EBADLEN);
+ }
+ else if (w->size != y->size)
+ {
+ GSL_ERROR ("number of weights does not match number of observations",
+ GSL_EBADLEN);
+ }
+ else if (cov->size1 != cov->size2)
+ {
+ GSL_ERROR ("covariance matrix is not square", GSL_ENOTSQR);
+ }
+ else if (c->size != cov->size1)
+ {
+ GSL_ERROR
+ ("number of parameters does not match size of covariance matrix",
+ GSL_EBADLEN);
+ }
+ else if (X->size1 != work->n || X->size2 != work->p)
+ {
+ GSL_ERROR
+ ("size of workspace does not match size of observation matrix",
+ GSL_EBADLEN);
+ }
+ else
+ {
+ const size_t n = X->size1;
+ const size_t p = X->size2;
+
+ size_t i, j, p_eff;
+
+ gsl_matrix *A = work->A;
+ gsl_matrix *Q = work->Q;
+ gsl_matrix *QSI = work->QSI;
+ gsl_vector *S = work->S;
+ gsl_vector *t = work->t;
+ gsl_vector *xt = work->xt;
+ gsl_vector *D = work->D;
+
+ /* Scale X, A = sqrt(w) X */
+
+ gsl_matrix_memcpy (A, X);
+
+ for (i = 0; i < n; i++)
+ {
+ double wi = gsl_vector_get (w, i);
+
+ if (wi < 0)
+ wi = 0;
+
+ {
+ gsl_vector_view row = gsl_matrix_row (A, i);
+ gsl_vector_scale (&row.vector, sqrt (wi));
+ }
+ }
+
+ /* Balance the columns of the matrix A */
+
+ gsl_linalg_balance_columns (A, D);
+
+ /* Decompose A into U S Q^T */
+
+ gsl_linalg_SV_decomp_mod (A, QSI, Q, S, xt);
+
+ /* Solve sqrt(w) y = A c for c, by first computing t = sqrt(w) y */
+
+ for (i = 0; i < n; i++)
+ {
+ double wi = gsl_vector_get (w, i);
+ double yi = gsl_vector_get (y, i);
+ if (wi < 0)
+ wi = 0;
+ gsl_vector_set (t, i, sqrt (wi) * yi);
+ }
+
+ gsl_blas_dgemv (CblasTrans, 1.0, A, t, 0.0, xt);
+
+ /* Scale the matrix Q, Q' = Q S^-1 */
+
+ gsl_matrix_memcpy (QSI, Q);
+
+ {
+ double alpha0 = gsl_vector_get (S, 0);
+ p_eff = 0;
+
+ for (j = 0; j < p; j++)
+ {
+ gsl_vector_view column = gsl_matrix_column (QSI, j);
+ double alpha = gsl_vector_get (S, j);
+
+ if (alpha <= tol * alpha0) {
+ alpha = 0.0;
+ } else {
+ alpha = 1.0 / alpha;
+ p_eff++;
+ }
+
+ gsl_vector_scale (&column.vector, alpha);
+ }
+
+ *rank = p_eff;
+ }
+
+ gsl_vector_set_zero (c);
+
+ /* Solution */
+
+ gsl_blas_dgemv (CblasNoTrans, 1.0, QSI, xt, 0.0, c);
+
+ /* Unscale the balancing factors */
+
+ gsl_vector_div (c, D);
+
+ /* Form covariance matrix cov = (Q S^-1) (Q S^-1)^T */
+
+ for (i = 0; i < p; i++)
+ {
+ gsl_vector_view row_i = gsl_matrix_row (QSI, i);
+ double d_i = gsl_vector_get (D, i);
+
+ for (j = i; j < p; j++)
+ {
+ gsl_vector_view row_j = gsl_matrix_row (QSI, j);
+ double d_j = gsl_vector_get (D, j);
+ double s;
+
+ gsl_blas_ddot (&row_i.vector, &row_j.vector, &s);
+
+ gsl_matrix_set (cov, i, j, s / (d_i * d_j));
+ gsl_matrix_set (cov, j, i, s / (d_i * d_j));
+ }
+ }
+
+ /* Compute chisq, from residual r = y - X c */
+
+ {
+ double r2 = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ double yi = gsl_vector_get (y, i);
+ double wi = gsl_vector_get (w, i);
+ gsl_vector_const_view row = gsl_matrix_const_row (X, i);
+ double y_est, ri;
+ gsl_blas_ddot (&row.vector, c, &y_est);
+ ri = yi - y_est;
+ r2 += wi * ri * ri;
+ }
+
+ *chisq = r2;
+ }
+
+ return GSL_SUCCESS;
+ }
+}
+
+
+int
+gsl_multifit_linear_est (const gsl_vector * x,
+ const gsl_vector * c,
+ const gsl_matrix * cov, double *y, double *y_err)
+{
+
+ if (x->size != c->size)
+ {
+ GSL_ERROR ("number of parameters c does not match number of observations x",
+ GSL_EBADLEN);
+ }
+ else if (cov->size1 != cov->size2)
+ {
+ GSL_ERROR ("covariance matrix is not square", GSL_ENOTSQR);
+ }
+ else if (c->size != cov->size1)
+ {
+ GSL_ERROR ("number of parameters c does not match size of covariance matrix cov",
+ GSL_EBADLEN);
+ }
+ else
+ {
+ size_t i, j;
+ double var = 0;
+
+ gsl_blas_ddot(x, c, y); /* y = x.c */
+
+ /* var = x' cov x */
+
+ for (i = 0; i < x->size; i++)
+ {
+ const double xi = gsl_vector_get (x, i);
+ var += xi * xi * gsl_matrix_get (cov, i, i);
+
+ for (j = 0; j < i; j++)
+ {
+ const double xj = gsl_vector_get (x, j);
+ var += 2 * xi * xj * gsl_matrix_get (cov, i, j);
+ }
+ }
+
+ *y_err = sqrt (var);
+
+ return GSL_SUCCESS;
+ }
+}