Blame multifit/multilinear.c

Packit 67cb25
/* multifit/multilinear.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2000, 2007, 2010 Brian Gough
Packit 67cb25
 * Copyright (C) 2013, 2015 Patrick Alken
Packit 67cb25
 * 
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 * 
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 * 
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software
Packit 67cb25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_multifit.h>
Packit 67cb25
#include <gsl/gsl_blas.h>
Packit 67cb25
#include <gsl/gsl_vector.h>
Packit 67cb25
#include <gsl/gsl_matrix.h>
Packit 67cb25
#include <gsl/gsl_linalg.h>
Packit 67cb25
Packit 67cb25
#include "linear_common.c"
Packit 67cb25
Packit 67cb25
static int multifit_linear_svd (const gsl_matrix * X,
Packit 67cb25
                                const int balance,
Packit 67cb25
                                gsl_multifit_linear_workspace * work);
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear (const gsl_matrix * X,
Packit 67cb25
                     const gsl_vector * y,
Packit 67cb25
                     gsl_vector * c,
Packit 67cb25
                     gsl_matrix * cov,
Packit 67cb25
                     double *chisq, gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  size_t rank;
Packit 67cb25
  int status = gsl_multifit_linear_tsvd(X, y, GSL_DBL_EPSILON, c, cov, chisq, &rank, work);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_tsvd()
Packit 67cb25
  Solve linear least squares system with truncated SVD
Packit 67cb25
Packit 67cb25
Inputs: X     - least squares matrix, n-by-p
Packit 67cb25
        y     - right hand side vector, n-by-1
Packit 67cb25
        tol   - tolerance for singular value truncation; if
Packit 67cb25
                s_j <= tol * s_0
Packit 67cb25
                then it is discarded from series expansion
Packit 67cb25
        c     - (output) solution vector, p-by-1
Packit 67cb25
        cov   - (output) covariance matrix, p-by-p
Packit 67cb25
        chisq - (output) cost function chi^2
Packit 67cb25
        rank  - (output) effective rank (number of singular values
Packit 67cb25
                used in solution)
Packit 67cb25
        work  - workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_tsvd (const gsl_matrix * X,
Packit 67cb25
                          const gsl_vector * y,
Packit 67cb25
                          const double tol,
Packit 67cb25
                          gsl_vector * c,
Packit 67cb25
                          gsl_matrix * cov,
Packit 67cb25
                          double * chisq,
Packit 67cb25
                          size_t * rank,
Packit 67cb25
                          gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  const size_t n = X->size1;
Packit 67cb25
  const size_t p = X->size2;
Packit 67cb25
Packit 67cb25
  if (y->size != n)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("number of observations in y does not match matrix",
Packit 67cb25
                GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p != c->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("number of parameters c does not match matrix",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (tol <= 0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("tolerance must be positive", GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status;
Packit 67cb25
      double rnorm = 0.0, snorm;
Packit 67cb25
Packit 67cb25
      /* compute balanced SVD */
Packit 67cb25
      status = gsl_multifit_linear_bsvd (X, work);
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
Packit 67cb25
      status = multifit_linear_solve (X, y, tol, -1.0, rank,
Packit 67cb25
                                      c, &rnorm, &snorm, work);
Packit 67cb25
Packit 67cb25
      *chisq = rnorm * rnorm;
Packit 67cb25
Packit 67cb25
      /* variance-covariance matrix cov = s2 * (Q S^-1) (Q S^-1)^T */
Packit 67cb25
      {
Packit 67cb25
        double r2 = rnorm * rnorm;
Packit 67cb25
        double s2 = r2 / (double)(n - *rank);
Packit 67cb25
        size_t i, j;
Packit 67cb25
        gsl_matrix_view QSI = gsl_matrix_submatrix(work->QSI, 0, 0, p, p);
Packit 67cb25
        gsl_vector_view D = gsl_vector_subvector(work->D, 0, p);
Packit 67cb25
Packit 67cb25
        for (i = 0; i < p; i++)
Packit 67cb25
          {
Packit 67cb25
            gsl_vector_view row_i = gsl_matrix_row (&QSI.matrix, i);
Packit 67cb25
            double d_i = gsl_vector_get (&D.vector, i);
Packit 67cb25
Packit 67cb25
            for (j = i; j < p; j++)
Packit 67cb25
              {
Packit 67cb25
                gsl_vector_view row_j = gsl_matrix_row (&QSI.matrix, j);
Packit 67cb25
                double d_j = gsl_vector_get (&D.vector, j);
Packit 67cb25
                double s;
Packit 67cb25
Packit 67cb25
                gsl_blas_ddot (&row_i.vector, &row_j.vector, &s);
Packit 67cb25
Packit 67cb25
                gsl_matrix_set (cov, i, j, s * s2 / (d_i * d_j));
Packit 67cb25
                gsl_matrix_set (cov, j, i, s * s2 / (d_i * d_j));
Packit 67cb25
              }
Packit 67cb25
          }
Packit 67cb25
      }
Packit 67cb25
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_svd()
Packit 67cb25
  Perform SVD decomposition of the matrix X and store in work without
Packit 67cb25
balancing
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_svd (const gsl_matrix * X,
Packit 67cb25
                         gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  /* do not balance by default */
Packit 67cb25
  int status = multifit_linear_svd(X, 0, work);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_bsvd()
Packit 67cb25
  Perform SVD decomposition of the matrix X and store in work with
Packit 67cb25
balancing
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_bsvd (const gsl_matrix * X,
Packit 67cb25
                          gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  int status = multifit_linear_svd(X, 1, work);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
size_t
Packit 67cb25
gsl_multifit_linear_rank(const double tol, const gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  double s0 = gsl_vector_get (work->S, 0);
Packit 67cb25
  size_t rank = 0;
Packit 67cb25
  size_t j;
Packit 67cb25
Packit 67cb25
  for (j = 0; j < work->p; j++)
Packit 67cb25
    {
Packit 67cb25
      double sj = gsl_vector_get (work->S, j);
Packit 67cb25
Packit 67cb25
      if (sj > tol * s0)
Packit 67cb25
        ++rank;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return rank;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Estimation of values for given x */
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_est (const gsl_vector * x,
Packit 67cb25
                         const gsl_vector * c,
Packit 67cb25
                         const gsl_matrix * cov, double *y, double *y_err)
Packit 67cb25
{
Packit 67cb25
Packit 67cb25
  if (x->size != c->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("number of parameters c does not match number of observations x",
Packit 67cb25
         GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (cov->size1 != cov->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("covariance matrix is not square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (c->size != cov->size1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("number of parameters c does not match size of covariance matrix cov",
Packit 67cb25
         GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i, j;
Packit 67cb25
      double var = 0;
Packit 67cb25
      
Packit 67cb25
      gsl_blas_ddot(x, c, y);       /* y = x.c */
Packit 67cb25
Packit 67cb25
      /* var = x' cov x */
Packit 67cb25
Packit 67cb25
      for (i = 0; i < x->size; i++)
Packit 67cb25
        {
Packit 67cb25
          const double xi = gsl_vector_get (x, i);
Packit 67cb25
          var += xi * xi * gsl_matrix_get (cov, i, i);
Packit 67cb25
Packit 67cb25
          for (j = 0; j < i; j++)
Packit 67cb25
            {
Packit 67cb25
              const double xj = gsl_vector_get (x, j);
Packit 67cb25
              var += 2 * xi * xj * gsl_matrix_get (cov, i, j);
Packit 67cb25
            }
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      *y_err = sqrt (var);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_rcond()
Packit 67cb25
  Return reciprocal condition number of LS matrix;
Packit 67cb25
gsl_multifit_linear_svd() must first be called to
Packit 67cb25
compute the SVD of X and its reciprocal condition number
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
double
Packit 67cb25
gsl_multifit_linear_rcond (const gsl_multifit_linear_workspace * w)
Packit 67cb25
{
Packit 67cb25
  return w->rcond;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_residuals()
Packit 67cb25
  Compute vector of residuals from fit
Packit 67cb25
Packit 67cb25
Inputs: X - design matrix
Packit 67cb25
        y - rhs vector
Packit 67cb25
        c - fit coefficients
Packit 67cb25
        r - (output) where to store residuals
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_residuals (const gsl_matrix *X, const gsl_vector *y,
Packit 67cb25
                               const gsl_vector *c, gsl_vector *r)
Packit 67cb25
{
Packit 67cb25
  if (X->size1 != y->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR
Packit 67cb25
        ("number of observations in y does not match rows of matrix X",
Packit 67cb25
         GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (X->size2 != c->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("number of parameters c does not match columns of matrix X",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (y->size != r->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("number of observations in y does not match number of residuals",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      /* r = y - X c */
Packit 67cb25
      gsl_vector_memcpy(r, y);
Packit 67cb25
      gsl_blas_dgemv(CblasNoTrans, -1.0, X, c, 1.0, r);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_multifit_linear_residuals() */
Packit 67cb25
Packit 67cb25
/* Perform a SVD decomposition on the least squares matrix X = U S Q^T
Packit 67cb25
 *
Packit 67cb25
 * Inputs: X       - least squares matrix
Packit 67cb25
 *         balance - 1 to perform column balancing
Packit 67cb25
 *         work    - workspace
Packit 67cb25
 *
Packit 67cb25
 * Notes:
Packit 67cb25
 * 1) On output,
Packit 67cb25
 *    work->A contains the matrix U
Packit 67cb25
 *    work->Q contains the matrix Q
Packit 67cb25
 *    work->S contains the vector of singular values
Packit 67cb25
 * 2) The matrix X may have smaller dimensions than the workspace
Packit 67cb25
 *    in the case of stdform2() - but the dimensions cannot be larger
Packit 67cb25
 * 3) On output, work->n and work->p are set to the dimensions of X
Packit 67cb25
 * 4) On output, work->rcond is set to the reciprocal condition number of X
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
multifit_linear_svd (const gsl_matrix * X,
Packit 67cb25
                     const int balance,
Packit 67cb25
                     gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  const size_t n = X->size1;
Packit 67cb25
  const size_t p = X->size2;
Packit 67cb25
Packit 67cb25
  if (n > work->nmax || p > work->pmax)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("observation matrix larger than workspace", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      gsl_matrix_view A = gsl_matrix_submatrix(work->A, 0, 0, n, p);
Packit 67cb25
      gsl_matrix_view Q = gsl_matrix_submatrix(work->Q, 0, 0, p, p);
Packit 67cb25
      gsl_matrix_view QSI = gsl_matrix_submatrix(work->QSI, 0, 0, p, p);
Packit 67cb25
      gsl_vector_view S = gsl_vector_subvector(work->S, 0, p);
Packit 67cb25
      gsl_vector_view xt = gsl_vector_subvector(work->xt, 0, p);
Packit 67cb25
      gsl_vector_view D = gsl_vector_subvector(work->D, 0, p);
Packit 67cb25
Packit 67cb25
      /* Copy X to workspace,  A <= X */
Packit 67cb25
Packit 67cb25
      gsl_matrix_memcpy (&A.matrix, X);
Packit 67cb25
Packit 67cb25
      /* Balance the columns of the matrix A if requested */
Packit 67cb25
Packit 67cb25
      if (balance) 
Packit 67cb25
        {
Packit 67cb25
          gsl_linalg_balance_columns (&A.matrix, &D.vector);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          gsl_vector_set_all (&D.vector, 1.0);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* decompose A into U S Q^T */
Packit 67cb25
      gsl_linalg_SV_decomp_mod (&A.matrix, &QSI.matrix, &Q.matrix,
Packit 67cb25
                                &S.vector, &xt.vector);
Packit 67cb25
Packit 67cb25
      /* compute reciprocal condition number rcond = smin / smax */
Packit 67cb25
      {
Packit 67cb25
        double smin, smax;
Packit 67cb25
        gsl_vector_minmax(&S.vector, &smin, &smax;;
Packit 67cb25
        work->rcond = smin / smax;
Packit 67cb25
      }
Packit 67cb25
Packit 67cb25
      work->n = n;
Packit 67cb25
      work->p = p;
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}