Blame multifit/multireg.c

Packit 67cb25
/* multifit/multireg.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 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
/*
Packit 67cb25
 * References:
Packit 67cb25
 *
Packit 67cb25
 * [1] P. C. Hansen & D. P. O'Leary, "The use of the L-curve in
Packit 67cb25
 * the regularization of discrete ill-posed problems",  SIAM J. Sci.
Packit 67cb25
 * Comput. 14 (1993), pp. 1487-1503.
Packit 67cb25
 *
Packit 67cb25
 * [2] P. C. Hansen, "Discrete Inverse Problems: Insight and Algorithms,"
Packit 67cb25
 * SIAM Press, 2010.
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
int
Packit 67cb25
gsl_multifit_linear_solve (const double lambda,
Packit 67cb25
                           const gsl_matrix * X,
Packit 67cb25
                           const gsl_vector * y,
Packit 67cb25
                           gsl_vector * c,
Packit 67cb25
                           double *rnorm,
Packit 67cb25
                           double *snorm,
Packit 67cb25
                           gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  size_t rank;
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  status = multifit_linear_solve(X, y, GSL_DBL_EPSILON, lambda, &rank, c,
Packit 67cb25
                                 rnorm, snorm, work);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
} /* gsl_multifit_linear_solve() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_applyW()
Packit 67cb25
  Apply weight matrix to (X,y) LS system
Packit 67cb25
Packit 67cb25
Inputs: X    - least squares matrix n-by-p
Packit 67cb25
        w    - weight vector n-by-1 or NULL for W = I
Packit 67cb25
        y    - right hand side n-by-1
Packit 67cb25
        WX   - (output) sqrt(W) X, n-by-p
Packit 67cb25
        Wy   - (output) sqrt(W) y, n-by-1
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) If w = NULL, on output WX = X and Wy = y
Packit 67cb25
2) It is allowed for WX = X and Wy = y for in-place transform
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_applyW(const gsl_matrix * X,
Packit 67cb25
                           const gsl_vector * w,
Packit 67cb25
                           const gsl_vector * y,
Packit 67cb25
                           gsl_matrix * WX,
Packit 67cb25
                           gsl_vector * Wy)
Packit 67cb25
{
Packit 67cb25
  const size_t n = X->size1;
Packit 67cb25
  const size_t p = X->size2;
Packit 67cb25
Packit 67cb25
  if (n != y->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("y vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (w != NULL && n != w->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("weight vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != WX->size1 || p != WX->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("WX matrix dimensions do not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != Wy->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("Wy vector must be length n", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i;
Packit 67cb25
Packit 67cb25
      /* copy WX = X; Wy = y if distinct pointers */
Packit 67cb25
      if (WX != X)
Packit 67cb25
        gsl_matrix_memcpy(WX, X);
Packit 67cb25
      if (Wy != y)
Packit 67cb25
        gsl_vector_memcpy(Wy, y);
Packit 67cb25
Packit 67cb25
      if (w != NULL)
Packit 67cb25
        {
Packit 67cb25
          /* construct WX = sqrt(W) X and Wy = sqrt(W) y */
Packit 67cb25
          for (i = 0; i < n; ++i)
Packit 67cb25
            {
Packit 67cb25
              double wi = gsl_vector_get(w, i);
Packit 67cb25
              double swi;
Packit 67cb25
              gsl_vector_view row = gsl_matrix_row(WX, i);
Packit 67cb25
              double *yi = gsl_vector_ptr(Wy, i);
Packit 67cb25
Packit 67cb25
              if (wi < 0.0)
Packit 67cb25
                wi = 0.0;
Packit 67cb25
Packit 67cb25
              swi = sqrt(wi);
Packit 67cb25
              gsl_vector_scale(&row.vector, swi);
Packit 67cb25
              *yi *= swi;
Packit 67cb25
            }
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_wstdform1()
Packit 67cb25
  Using regularization matrix
Packit 67cb25
L = diag(l_1,l_2,...,l_p), transform to Tikhonov standard form:
Packit 67cb25
Packit 67cb25
X~ = sqrt(W) X L^{-1}
Packit 67cb25
y~ = sqrt(W) y
Packit 67cb25
c~ = L c
Packit 67cb25
Packit 67cb25
Inputs: L    - Tikhonov matrix as a vector of diagonal elements p-by-1;
Packit 67cb25
               or NULL for L = I
Packit 67cb25
        X    - least squares matrix n-by-p
Packit 67cb25
        y    - right hand side vector n-by-1
Packit 67cb25
        w    - weight vector n-by-1; or NULL for W = I
Packit 67cb25
        Xs   - least squares matrix in standard form X~ n-by-p
Packit 67cb25
        ys   - right hand side vector in standard form y~ n-by-1
Packit 67cb25
        work - workspace
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) It is allowed for X = Xs and y = ys
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_wstdform1 (const gsl_vector * L,
Packit 67cb25
                               const gsl_matrix * X,
Packit 67cb25
                               const gsl_vector * w,
Packit 67cb25
                               const gsl_vector * y,
Packit 67cb25
                               gsl_matrix * Xs,
Packit 67cb25
                               gsl_vector * ys,
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 if (L != NULL && p != L->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("L vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != y->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("y vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (w != NULL && n != w->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("weight vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != Xs->size1 || p != Xs->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("Xs matrix dimensions do not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != ys->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("ys vector must be length n", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status = GSL_SUCCESS;
Packit 67cb25
Packit 67cb25
      /* compute Xs = sqrt(W) X and ys = sqrt(W) y */
Packit 67cb25
      status = gsl_multifit_linear_applyW(X, w, y, Xs, ys);
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
Packit 67cb25
      if (L != NULL)
Packit 67cb25
        {
Packit 67cb25
          size_t j;
Packit 67cb25
Packit 67cb25
          /* construct X~ = sqrt(W) X * L^{-1} matrix */
Packit 67cb25
          for (j = 0; j < p; ++j)
Packit 67cb25
            {
Packit 67cb25
              gsl_vector_view Xj = gsl_matrix_column(Xs, j);
Packit 67cb25
              double lj = gsl_vector_get(L, j);
Packit 67cb25
Packit 67cb25
              if (lj == 0.0)
Packit 67cb25
                {
Packit 67cb25
                  GSL_ERROR("L matrix is singular", GSL_EDOM);
Packit 67cb25
                }
Packit 67cb25
Packit 67cb25
              gsl_vector_scale(&Xj.vector, 1.0 / lj);
Packit 67cb25
            }
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_stdform1()
Packit 67cb25
  Using regularization matrix L = diag(l_1,l_2,...,l_p),
Packit 67cb25
and W = I, transform to Tikhonov standard form:
Packit 67cb25
Packit 67cb25
X~ = X L^{-1}
Packit 67cb25
y~ = y
Packit 67cb25
c~ = L c
Packit 67cb25
Packit 67cb25
Inputs: L    - Tikhonov matrix as a vector of diagonal elements p-by-1
Packit 67cb25
        X    - least squares matrix n-by-p
Packit 67cb25
        y    - right hand side vector n-by-1
Packit 67cb25
        Xs   - least squares matrix in standard form X~ n-by-p
Packit 67cb25
        ys   - right hand side vector in standard form y~ n-by-1
Packit 67cb25
        work - workspace
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) It is allowed for X = Xs
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_stdform1 (const gsl_vector * L,
Packit 67cb25
                              const gsl_matrix * X,
Packit 67cb25
                              const gsl_vector * y,
Packit 67cb25
                              gsl_matrix * Xs,
Packit 67cb25
                              gsl_vector * ys,
Packit 67cb25
                              gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  status = gsl_multifit_linear_wstdform1(L, X, NULL, y, Xs, ys, work);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_L_decomp (gsl_matrix * L, gsl_vector * tau)
Packit 67cb25
{
Packit 67cb25
  const size_t m = L->size1;
Packit 67cb25
  const size_t p = L->size2;
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  if (tau->size != GSL_MIN(m, p))
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("tau vector must be min(m,p)", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (m >= p)
Packit 67cb25
    {
Packit 67cb25
      /* square or tall L matrix */
Packit 67cb25
      status = gsl_linalg_QR_decomp(L, tau);
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      /* more columns than rows, compute qr(L^T) */
Packit 67cb25
      gsl_matrix_view LTQR = gsl_matrix_view_array(L->data, p, m);
Packit 67cb25
      gsl_matrix *LT = gsl_matrix_alloc(p, m);
Packit 67cb25
Packit 67cb25
      /* XXX: use temporary storage due to difficulties in transforming
Packit 67cb25
       * a rectangular matrix in-place */
Packit 67cb25
      gsl_matrix_transpose_memcpy(LT, L);
Packit 67cb25
      gsl_matrix_memcpy(&LTQR.matrix, LT);
Packit 67cb25
      gsl_matrix_free(LT);
Packit 67cb25
Packit 67cb25
      status = gsl_linalg_QR_decomp(&LTQR.matrix, tau);
Packit 67cb25
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_wstdform2()
Packit 67cb25
  Using regularization matrix L which is m-by-p, transform to Tikhonov
Packit 67cb25
standard form. This routine is separated into two cases:
Packit 67cb25
Packit 67cb25
Case 1: m >= p, here we can use the QR decomposition of L = QR, and note
Packit 67cb25
that ||L c|| = ||R c|| where R is p-by-p. Therefore,
Packit 67cb25
Packit 67cb25
X~ = X R^{-1} is n-by-p
Packit 67cb25
y~ = y is n-by-1
Packit 67cb25
c~ is p-by-1
Packit 67cb25
M is not used
Packit 67cb25
Packit 67cb25
Case 2: m < p
Packit 67cb25
Packit 67cb25
X~ is (n - p + m)-by-m
Packit 67cb25
y~ is (n - p + m)-by-1
Packit 67cb25
c~ is m-by-1
Packit 67cb25
M is n-by-p (workspace)
Packit 67cb25
Packit 67cb25
Inputs: LQR  - output from gsl_multifit_linear_L_decomp()
Packit 67cb25
        Ltau - output from gsl_multifit_linear_L_decomp()
Packit 67cb25
        X    - least squares matrix n-by-p
Packit 67cb25
        w    - weight vector n-by-1; or NULL for W = I
Packit 67cb25
        y    - right hand side vector n-by-1
Packit 67cb25
        Xs   - (output) least squares matrix in standard form
Packit 67cb25
               case 1: n-by-p
Packit 67cb25
               case 2: (n - p + m)-by-m
Packit 67cb25
        ys   - (output) right hand side vector in standard form
Packit 67cb25
               case 1: n-by-1
Packit 67cb25
               case 2: (n - p + m)-by-1
Packit 67cb25
        M    - (output) workspace matrix needed to reconstruct solution vector
Packit 67cb25
               case 1: not used
Packit 67cb25
               case 2: n-by-p
Packit 67cb25
        work - workspace
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) If m >= p, on output:
Packit 67cb25
   Xs = X R^{-1}
Packit 67cb25
   ys = y
Packit 67cb25
Packit 67cb25
2) If m < p, on output:
Packit 67cb25
   M(:,1:pm) contains QR decomposition of A * K_o, needed to reconstruct
Packit 67cb25
   solution vector, where pm = p - m; M(:,p) contains Householder scalars
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_wstdform2 (const gsl_matrix * LQR,
Packit 67cb25
                               const gsl_vector * Ltau,
Packit 67cb25
                               const gsl_matrix * X,
Packit 67cb25
                               const gsl_vector * w,
Packit 67cb25
                               const gsl_vector * y,
Packit 67cb25
                               gsl_matrix * Xs,
Packit 67cb25
                               gsl_vector * ys,
Packit 67cb25
                               gsl_matrix * M,
Packit 67cb25
                               gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  const size_t m = LQR->size1;
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 if (p != LQR->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("LQR and X matrices have different numbers of columns", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != y->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("y vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (w != NULL && n != w->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("weights vector must be length n", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (m >= p) /* square or tall L matrix */
Packit 67cb25
    {
Packit 67cb25
      /* the sizes of Xs and ys depend on whether m >= p or m < p */
Packit 67cb25
      if (n != Xs->size1 || p != Xs->size2)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("Xs matrix must be n-by-p", GSL_EBADLEN);
Packit 67cb25
        }
Packit 67cb25
      else if (n != ys->size)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("ys vector must have length n", GSL_EBADLEN);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          int status;
Packit 67cb25
          size_t i;
Packit 67cb25
          gsl_matrix_const_view R = gsl_matrix_const_submatrix(LQR, 0, 0, p, p);
Packit 67cb25
Packit 67cb25
          /* compute Xs = sqrt(W) X and ys = sqrt(W) y */
Packit 67cb25
          status = gsl_multifit_linear_applyW(X, w, y, Xs, ys);
Packit 67cb25
          if (status)
Packit 67cb25
            return status;
Packit 67cb25
Packit 67cb25
          /* compute X~ = X R^{-1} using QR decomposition of L */
Packit 67cb25
          for (i = 0; i < n; ++i)
Packit 67cb25
            {
Packit 67cb25
              gsl_vector_view v = gsl_matrix_row(Xs, i);
Packit 67cb25
Packit 67cb25
              /* solve: R^T y = X_i */
Packit 67cb25
              gsl_blas_dtrsv(CblasUpper, CblasTrans, CblasNonUnit, &R.matrix, &v.vector);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          return GSL_SUCCESS;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
  else /* L matrix with m < p */
Packit 67cb25
    {
Packit 67cb25
      const size_t pm = p - m;
Packit 67cb25
      const size_t npm = n - pm;
Packit 67cb25
Packit 67cb25
      /*
Packit 67cb25
       * This code closely follows section 2.6.1 of Hansen's
Packit 67cb25
       * "Regularization Tools" manual
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      if (npm != Xs->size1 || m != Xs->size2)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("Xs matrix must be (n-p+m)-by-m", GSL_EBADLEN);
Packit 67cb25
        }
Packit 67cb25
      else if (npm != ys->size)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("ys vector must be of length (n-p+m)", GSL_EBADLEN);
Packit 67cb25
        }
Packit 67cb25
      else if (n != M->size1 || p != M->size2)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("M matrix must be n-by-p", GSL_EBADLEN);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          int status;
Packit 67cb25
          gsl_matrix_view A = gsl_matrix_submatrix(work->A, 0, 0, n, p);
Packit 67cb25
          gsl_vector_view b = gsl_vector_subvector(work->t, 0, n);
Packit 67cb25
Packit 67cb25
          gsl_matrix_view LTQR = gsl_matrix_view_array(LQR->data, p, m);           /* qr(L^T) */
Packit 67cb25
          gsl_matrix_view Rp = gsl_matrix_view_array(LQR->data, m, m);             /* R factor of L^T */
Packit 67cb25
          gsl_vector_const_view LTtau = gsl_vector_const_subvector(Ltau, 0, m);
Packit 67cb25
Packit 67cb25
          /*
Packit 67cb25
           * M(:,1:p-m) will hold QR decomposition of A K_o; M(:,p) will hold
Packit 67cb25
           * Householder scalars
Packit 67cb25
           */
Packit 67cb25
          gsl_matrix_view MQR = gsl_matrix_submatrix(M, 0, 0, n, pm);
Packit 67cb25
          gsl_vector_view Mtau = gsl_matrix_subcolumn(M, p - 1, 0, GSL_MIN(n, pm));
Packit 67cb25
Packit 67cb25
          gsl_matrix_view AKo, AKp, HqTAKp;
Packit 67cb25
          gsl_vector_view v;
Packit 67cb25
          size_t i;
Packit 67cb25
Packit 67cb25
          /* compute A = sqrt(W) X and b = sqrt(W) y */
Packit 67cb25
          status = gsl_multifit_linear_applyW(X, w, y, &A.matrix, &b.vector);
Packit 67cb25
          if (status)
Packit 67cb25
            return status;
Packit 67cb25
Packit 67cb25
          /* compute: A <- A K = [ A K_p ; A K_o ] */
Packit 67cb25
          gsl_linalg_QR_matQ(&LTQR.matrix, &LTtau.vector, &A.matrix);
Packit 67cb25
          AKp = gsl_matrix_submatrix(&A.matrix, 0, 0, n, m); 
Packit 67cb25
          AKo = gsl_matrix_submatrix(&A.matrix, 0, m, n, pm); 
Packit 67cb25
Packit 67cb25
          /* compute QR decomposition [H,T] = qr(A * K_o) and store in M */
Packit 67cb25
          gsl_matrix_memcpy(&MQR.matrix, &AKo.matrix);
Packit 67cb25
          gsl_linalg_QR_decomp(&MQR.matrix, &Mtau.vector);
Packit 67cb25
Packit 67cb25
          /* AKp currently contains A K_p; apply H^T from the left to get H^T A K_p */
Packit 67cb25
          gsl_linalg_QR_QTmat(&MQR.matrix, &Mtau.vector, &AKp.matrix);
Packit 67cb25
Packit 67cb25
          /* the last npm rows correspond to H_q^T A K_p */
Packit 67cb25
          HqTAKp = gsl_matrix_submatrix(&AKp.matrix, pm, 0, npm, m);
Packit 67cb25
Packit 67cb25
          /* solve: Xs R_p^T = H_q^T A K_p for Xs */
Packit 67cb25
          gsl_matrix_memcpy(Xs, &HqTAKp.matrix);
Packit 67cb25
          for (i = 0; i < npm; ++i)
Packit 67cb25
            {
Packit 67cb25
              gsl_vector_view x = gsl_matrix_row(Xs, i);
Packit 67cb25
              gsl_blas_dtrsv(CblasUpper, CblasNoTrans, CblasNonUnit, &Rp.matrix, &x.vector);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          /*
Packit 67cb25
           * compute: ys = H_q^T b; this is equivalent to computing
Packit 67cb25
           * the last q elements of H^T b (q = npm)
Packit 67cb25
           */
Packit 67cb25
          v = gsl_vector_subvector(&b.vector, pm, npm);
Packit 67cb25
          gsl_linalg_QR_QTvec(&MQR.matrix, &Mtau.vector, &b.vector);
Packit 67cb25
          gsl_vector_memcpy(ys, &v.vector);
Packit 67cb25
Packit 67cb25
          return GSL_SUCCESS;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_stdform2 (const gsl_matrix * LQR,
Packit 67cb25
                              const gsl_vector * Ltau,
Packit 67cb25
                              const gsl_matrix * X,
Packit 67cb25
                              const gsl_vector * y,
Packit 67cb25
                              gsl_matrix * Xs,
Packit 67cb25
                              gsl_vector * ys,
Packit 67cb25
                              gsl_matrix * M,
Packit 67cb25
                              gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  status = gsl_multifit_linear_wstdform2(LQR, Ltau, X, NULL, y, Xs, ys, M, work);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_genform1()
Packit 67cb25
  Backtransform regularized solution vector using matrix
Packit 67cb25
L = diag(L)
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_genform1 (const gsl_vector * L,
Packit 67cb25
                              const gsl_vector * cs,
Packit 67cb25
                              gsl_vector * c,
Packit 67cb25
                              gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  if (L->size > work->pmax)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("L vector does not match workspace", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (L->size != cs->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("cs vector does not match L", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (L->size != c->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("c vector does not match L", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      /* compute true solution vector c = L^{-1} c~ */
Packit 67cb25
      gsl_vector_memcpy(c, cs);
Packit 67cb25
      gsl_vector_div(c, L);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_wgenform2()
Packit 67cb25
  Backtransform regularized solution vector in standard form to recover
Packit 67cb25
original vector
Packit 67cb25
Packit 67cb25
Inputs: LQR  - output from gsl_multifit_linear_L_decomp()
Packit 67cb25
        Ltau - output from gsl_multifit_linear_L_decomp()
Packit 67cb25
        X    - original least squares matrix n-by-p
Packit 67cb25
        w    - original weight vector n-by-1 or NULL for W = I
Packit 67cb25
        y    - original rhs vector n-by-1
Packit 67cb25
        cs   - standard form solution vector
Packit 67cb25
        c    - (output) original solution vector p-by-1
Packit 67cb25
        M    - matrix computed by gsl_multifit_linear_wstdform2()
Packit 67cb25
        work - workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_wgenform2 (const gsl_matrix * LQR,
Packit 67cb25
                               const gsl_vector * Ltau,
Packit 67cb25
                               const gsl_matrix * X,
Packit 67cb25
                               const gsl_vector * w,
Packit 67cb25
                               const gsl_vector * y,
Packit 67cb25
                               const gsl_vector * cs,
Packit 67cb25
                               const gsl_matrix * M,
Packit 67cb25
                               gsl_vector * c,
Packit 67cb25
                               gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  const size_t m = LQR->size1;
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("X matrix does not match workspace", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p != LQR->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("LQR matrix does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p != c->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("c vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (w != NULL && n != w->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("w vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != y->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("y vector does not match X", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (m >= p)                    /* square or tall L matrix */
Packit 67cb25
    {
Packit 67cb25
      if (p != cs->size)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("cs vector must be length p", GSL_EBADLEN);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          int s;
Packit 67cb25
          gsl_matrix_const_view R = gsl_matrix_const_submatrix(LQR, 0, 0, p, p); /* R factor of L */
Packit 67cb25
Packit 67cb25
          /* solve R c = cs for true solution c, using QR decomposition of L */
Packit 67cb25
          gsl_vector_memcpy(c, cs);
Packit 67cb25
          s = gsl_blas_dtrsv(CblasUpper, CblasNoTrans, CblasNonUnit, &R.matrix, c);
Packit 67cb25
Packit 67cb25
          return s;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
  else                                /* rectangular L matrix with m < p */
Packit 67cb25
    {
Packit 67cb25
      if (m != cs->size)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("cs vector must be length m", GSL_EBADLEN);
Packit 67cb25
        }
Packit 67cb25
      else if (n != M->size1 || p != M->size2)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("M matrix must be size n-by-p", GSL_EBADLEN);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          int status;
Packit 67cb25
          const size_t pm = p - m;
Packit 67cb25
          gsl_matrix_view A = gsl_matrix_submatrix(work->A, 0, 0, n, p);
Packit 67cb25
          gsl_vector_view b = gsl_vector_subvector(work->t, 0, n);
Packit 67cb25
          gsl_matrix_view Rp = gsl_matrix_view_array(LQR->data, m, m); /* R_p */
Packit 67cb25
          gsl_matrix_view LTQR = gsl_matrix_view_array(LQR->data, p, m);
Packit 67cb25
          gsl_vector_const_view LTtau = gsl_vector_const_subvector(Ltau, 0, m);
Packit 67cb25
          gsl_matrix_const_view MQR = gsl_matrix_const_submatrix(M, 0, 0, n, pm);
Packit 67cb25
          gsl_vector_const_view Mtau = gsl_matrix_const_subcolumn(M, p - 1, 0, GSL_MIN(n, pm));
Packit 67cb25
          gsl_matrix_const_view To = gsl_matrix_const_submatrix(&MQR.matrix, 0, 0, pm, pm);
Packit 67cb25
          gsl_vector_view workp = gsl_vector_subvector(work->xt, 0, p);
Packit 67cb25
          gsl_vector_view v1, v2;
Packit 67cb25
Packit 67cb25
          /* compute A = sqrt(W) X and b = sqrt(W) y */
Packit 67cb25
          status = gsl_multifit_linear_applyW(X, w, y, &A.matrix, &b.vector);
Packit 67cb25
          if (status)
Packit 67cb25
            return status;
Packit 67cb25
Packit 67cb25
          /* initialize c to zero */
Packit 67cb25
          gsl_vector_set_zero(c);
Packit 67cb25
Packit 67cb25
          /* compute c = L_inv cs = K_p R_p^{-T} cs */
Packit 67cb25
Packit 67cb25
          /* set c(1:m) = R_p^{-T} cs */
Packit 67cb25
          v1 = gsl_vector_subvector(c, 0, m);
Packit 67cb25
          gsl_vector_memcpy(&v1.vector, cs);
Packit 67cb25
          gsl_blas_dtrsv(CblasUpper, CblasTrans, CblasNonUnit, &Rp.matrix, &v1.vector);
Packit 67cb25
Packit 67cb25
          /* c <- K R_p^{-T} cs = [ K_p R_p^{_T} cs ; 0 ] */
Packit 67cb25
          gsl_linalg_QR_Qvec(&LTQR.matrix, &LTtau.vector, c);
Packit 67cb25
Packit 67cb25
          /* compute: b1 = b - A L_inv cs */
Packit 67cb25
          gsl_blas_dgemv(CblasNoTrans, -1.0, &A.matrix, c, 1.0, &b.vector);
Packit 67cb25
Packit 67cb25
          /* compute: b2 = H^T b1 */
Packit 67cb25
          gsl_linalg_QR_QTvec(&MQR.matrix, &Mtau.vector, &b.vector);
Packit 67cb25
Packit 67cb25
          /* compute: b3 = T_o^{-1} b2 */
Packit 67cb25
          v1 = gsl_vector_subvector(&b.vector, 0, pm);
Packit 67cb25
          gsl_blas_dtrsv(CblasUpper, CblasNoTrans, CblasNonUnit, &To.matrix, &v1.vector);
Packit 67cb25
Packit 67cb25
          /* compute: b4 = K_o b3 */
Packit 67cb25
          gsl_vector_set_zero(&workp.vector);
Packit 67cb25
          v2 = gsl_vector_subvector(&workp.vector, m, pm);
Packit 67cb25
          gsl_vector_memcpy(&v2.vector, &v1.vector);
Packit 67cb25
          gsl_linalg_QR_Qvec(&LTQR.matrix, &LTtau.vector, &workp.vector);
Packit 67cb25
Packit 67cb25
          /* final solution vector */
Packit 67cb25
          gsl_vector_add(c, &workp.vector);
Packit 67cb25
Packit 67cb25
          return GSL_SUCCESS;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_genform2 (const gsl_matrix * LQR,
Packit 67cb25
                              const gsl_vector * Ltau,
Packit 67cb25
                              const gsl_matrix * X,
Packit 67cb25
                              const gsl_vector * y,
Packit 67cb25
                              const gsl_vector * cs,
Packit 67cb25
                              const gsl_matrix * M,
Packit 67cb25
                              gsl_vector * c,
Packit 67cb25
                              gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  status = gsl_multifit_linear_wgenform2(LQR, Ltau, X, NULL, y, cs, M, c, work);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_lreg()
Packit 67cb25
  Calculate regularization parameters to use in L-curve
Packit 67cb25
analysis
Packit 67cb25
Packit 67cb25
Inputs: smin      - smallest singular value of LS system
Packit 67cb25
        smax      - largest singular value of LS system > 0
Packit 67cb25
        reg_param - (output) vector of regularization parameters
Packit 67cb25
                    derived from singular values
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_lreg (const double smin, const double smax,
Packit 67cb25
                          gsl_vector * reg_param)
Packit 67cb25
{
Packit 67cb25
  if (smax <= 0.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("smax must be positive", GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      const size_t N = reg_param->size;
Packit 67cb25
Packit 67cb25
      /* smallest regularization parameter */
Packit 67cb25
      const double smin_ratio = 16.0 * GSL_DBL_EPSILON;
Packit 67cb25
      const double new_smin = GSL_MAX(smin, smax*smin_ratio);
Packit 67cb25
      double ratio;
Packit 67cb25
      size_t i;
Packit 67cb25
Packit 67cb25
      gsl_vector_set(reg_param, N - 1, new_smin);
Packit 67cb25
Packit 67cb25
      /* ratio so that reg_param(1) = s(1) */
Packit 67cb25
      ratio = pow(smax / new_smin, 1.0 / ((double)N - 1.0));
Packit 67cb25
Packit 67cb25
      /* calculate the regularization parameters */
Packit 67cb25
      for (i = N - 1; i > 0 && i--; )
Packit 67cb25
        {
Packit 67cb25
          double rp1 = gsl_vector_get(reg_param, i + 1);
Packit 67cb25
          gsl_vector_set(reg_param, i, ratio * rp1);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_lcurve()
Packit 67cb25
  Calculate L-curve using regularization parameters estimated
Packit 67cb25
from singular values of least squares matrix
Packit 67cb25
Packit 67cb25
Inputs: y         - right hand side vector
Packit 67cb25
        reg_param - (output) vector of regularization parameters
Packit 67cb25
                    derived from singular values
Packit 67cb25
        rho       - (output) vector of residual norms ||y - X c||
Packit 67cb25
        eta       - (output) vector of solution norms ||lambda c||
Packit 67cb25
        work      - workspace
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) SVD of X must be computed first by calling multifit_linear_svd();
Packit 67cb25
   work->n and work->p are initialized by this function
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_lcurve (const gsl_vector * y,
Packit 67cb25
                            gsl_vector * reg_param,
Packit 67cb25
                            gsl_vector * rho, gsl_vector * eta,
Packit 67cb25
                            gsl_multifit_linear_workspace * work)
Packit 67cb25
{
Packit 67cb25
  const size_t n = y->size;
Packit 67cb25
  const size_t N = rho->size; /* number of points on L-curve */
Packit 67cb25
Packit 67cb25
  if (n != work->n)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("y vector does not match workspace", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (N < 3)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("at least 3 points are needed for L-curve analysis",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (N != eta->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("size of rho and eta vectors do not match",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (reg_param->size != eta->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("size of reg_param and eta vectors do not match",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status = GSL_SUCCESS;
Packit 67cb25
      const size_t p = work->p;
Packit 67cb25
Packit 67cb25
      size_t i, j;
Packit 67cb25
Packit 67cb25
      gsl_matrix_view A = gsl_matrix_submatrix(work->A, 0, 0, n, 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 workp = gsl_matrix_subcolumn(work->QSI, 0, 0, p);
Packit 67cb25
      gsl_vector_view workp2 = gsl_vector_subvector(work->D, 0, p); /* D isn't used for regularized problems */
Packit 67cb25
Packit 67cb25
      const double smax = gsl_vector_get(&S.vector, 0);
Packit 67cb25
      const double smin = gsl_vector_get(&S.vector, p - 1);
Packit 67cb25
Packit 67cb25
      double dr; /* residual error from projection */
Packit 67cb25
      double normy = gsl_blas_dnrm2(y);
Packit 67cb25
      double normUTy;
Packit 67cb25
Packit 67cb25
      /* compute projection xt = U^T y */
Packit 67cb25
      gsl_blas_dgemv (CblasTrans, 1.0, &A.matrix, y, 0.0, &xt.vector);
Packit 67cb25
Packit 67cb25
      normUTy = gsl_blas_dnrm2(&xt.vector);
Packit 67cb25
      dr = normy*normy - normUTy*normUTy;
Packit 67cb25
Packit 67cb25
      /* calculate regularization parameters */
Packit 67cb25
      gsl_multifit_linear_lreg(smin, smax, reg_param);
Packit 67cb25
Packit 67cb25
      for (i = 0; i < N; ++i)
Packit 67cb25
        {
Packit 67cb25
          double lambda = gsl_vector_get(reg_param, i);
Packit 67cb25
          double lambda_sq = lambda * lambda;
Packit 67cb25
Packit 67cb25
          for (j = 0; j < p; ++j)
Packit 67cb25
            {
Packit 67cb25
              double sj = gsl_vector_get(&S.vector, j);
Packit 67cb25
              double xtj = gsl_vector_get(&xt.vector, j);
Packit 67cb25
              double f = sj / (sj*sj + lambda_sq);
Packit 67cb25
Packit 67cb25
              gsl_vector_set(&workp.vector, j, f * xtj);
Packit 67cb25
              gsl_vector_set(&workp2.vector, j, (1.0 - sj*f) * xtj);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          gsl_vector_set(eta, i, gsl_blas_dnrm2(&workp.vector));
Packit 67cb25
          gsl_vector_set(rho, i, gsl_blas_dnrm2(&workp2.vector));
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (n > p && dr > 0.0)
Packit 67cb25
        {
Packit 67cb25
          /* add correction to residual norm (see eqs 6-7 of [1]) */
Packit 67cb25
          for (i = 0; i < N; ++i)
Packit 67cb25
            {
Packit 67cb25
              double rhoi = gsl_vector_get(rho, i);
Packit 67cb25
              double *ptr = gsl_vector_ptr(rho, i);
Packit 67cb25
Packit 67cb25
              *ptr = sqrt(rhoi*rhoi + dr);
Packit 67cb25
            }
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* restore D to identity matrix */
Packit 67cb25
      gsl_vector_set_all(work->D, 1.0);
Packit 67cb25
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_multifit_linear_lcurve() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_lcorner()
Packit 67cb25
  Determine point on L-curve of maximum curvature. For each
Packit 67cb25
set of 3 points on the L-curve, the circle which passes through
Packit 67cb25
the 3 points is computed. The radius of the circle is then used
Packit 67cb25
as an estimate of the curvature at the middle point. The point
Packit 67cb25
with maximum curvature is then selected.
Packit 67cb25
Packit 67cb25
Inputs: rho - vector of residual norms ||A x - b||
Packit 67cb25
        eta - vector of solution norms ||L x||
Packit 67cb25
        idx - (output) index i such that
Packit 67cb25
              (log(rho(i)),log(eta(i))) is the point of
Packit 67cb25
              maximum curvature
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_lcorner(const gsl_vector *rho,
Packit 67cb25
                            const gsl_vector *eta,
Packit 67cb25
                            size_t *idx)
Packit 67cb25
{
Packit 67cb25
  const size_t n = rho->size;
Packit 67cb25
Packit 67cb25
  if (n < 3)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("at least 3 points are needed for L-curve analysis",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != eta->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("size of rho and eta vectors do not match",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int s = GSL_SUCCESS;
Packit 67cb25
      size_t i;
Packit 67cb25
      double x1, y1;      /* first point of triangle on L-curve */
Packit 67cb25
      double x2, y2;      /* second point of triangle on L-curve */
Packit 67cb25
      double rmin = -1.0; /* minimum radius of curvature */
Packit 67cb25
Packit 67cb25
      /* initial values */
Packit 67cb25
      x1 = log(gsl_vector_get(rho, 0));
Packit 67cb25
      y1 = log(gsl_vector_get(eta, 0));
Packit 67cb25
Packit 67cb25
      x2 = log(gsl_vector_get(rho, 1));
Packit 67cb25
      y2 = log(gsl_vector_get(eta, 1));
Packit 67cb25
Packit 67cb25
      for (i = 1; i < n - 1; ++i)
Packit 67cb25
        {
Packit 67cb25
          /*
Packit 67cb25
           * The points (x1,y1), (x2,y2), (x3,y3) are the previous,
Packit 67cb25
           * current, and next point on the L-curve. We will find
Packit 67cb25
           * the circle which fits these 3 points and take its radius
Packit 67cb25
           * as an estimate of the curvature at this point.
Packit 67cb25
           */
Packit 67cb25
          double x3 = log(gsl_vector_get(rho, i + 1));
Packit 67cb25
          double y3 = log(gsl_vector_get(eta, i + 1));
Packit 67cb25
Packit 67cb25
          double x21 = x2 - x1;
Packit 67cb25
          double y21 = y2 - y1;
Packit 67cb25
          double x31 = x3 - x1;
Packit 67cb25
          double y31 = y3 - y1;
Packit 67cb25
          double h21 = x21*x21 + y21*y21;
Packit 67cb25
          double h31 = x31*x31 + y31*y31;
Packit 67cb25
          double d = fabs(2.0 * (x21*y31 - x31*y21));
Packit 67cb25
          double r = sqrt(h21*h31*((x3-x2)*(x3-x2)+(y3-y2)*(y3-y2))) / d;
Packit 67cb25
Packit 67cb25
          /* if d =~ 0 then there are nearly colinear points */
Packit 67cb25
          if (gsl_finite(r))
Packit 67cb25
            {
Packit 67cb25
              /* check for smallest radius of curvature */
Packit 67cb25
              if (r < rmin || rmin < 0.0)
Packit 67cb25
                {
Packit 67cb25
                  rmin = r;
Packit 67cb25
                  *idx = i;
Packit 67cb25
                }
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          /* update previous/current L-curve values */
Packit 67cb25
          x1 = x2;
Packit 67cb25
          y1 = y2;
Packit 67cb25
          x2 = x3;
Packit 67cb25
          y2 = y3;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* check if a minimum radius was found */
Packit 67cb25
      if (rmin < 0.0)
Packit 67cb25
        {
Packit 67cb25
          /* possibly co-linear points */
Packit 67cb25
          GSL_ERROR("failed to find minimum radius", GSL_EINVAL);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return s;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_multifit_linear_lcorner() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_lcorner2()
Packit 67cb25
  Determine point on L-curve (lambda^2, ||c||^2) of maximum curvature.
Packit 67cb25
For each set of 3 points on the L-curve, the circle which passes through
Packit 67cb25
the 3 points is computed. The radius of the circle is then used
Packit 67cb25
as an estimate of the curvature at the middle point. The point
Packit 67cb25
with maximum curvature is then selected.
Packit 67cb25
Packit 67cb25
This routine is based on the paper
Packit 67cb25
Packit 67cb25
M. Rezghi and S. M. Hosseini, "A new variant of L-curve for Tikhonov
Packit 67cb25
regularization", J. Comp. App. Math., 231 (2009).
Packit 67cb25
Packit 67cb25
Inputs: reg_param - vector of regularization parameters
Packit 67cb25
        eta       - vector of solution norms ||L x||
Packit 67cb25
        idx       - (output) index i such that
Packit 67cb25
                    (lambda(i)^2,eta(i)^2) is the point of
Packit 67cb25
                    maximum curvature
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_lcorner2(const gsl_vector *reg_param,
Packit 67cb25
                             const gsl_vector *eta,
Packit 67cb25
                             size_t *idx)
Packit 67cb25
{
Packit 67cb25
  const size_t n = reg_param->size;
Packit 67cb25
Packit 67cb25
  if (n < 3)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("at least 3 points are needed for L-curve analysis",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != eta->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("size of reg_param and eta vectors do not match",
Packit 67cb25
                 GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int s = GSL_SUCCESS;
Packit 67cb25
      size_t i;
Packit 67cb25
      double x1, y1;      /* first point of triangle on L-curve */
Packit 67cb25
      double x2, y2;      /* second point of triangle on L-curve */
Packit 67cb25
      double rmin = -1.0; /* minimum radius of curvature */
Packit 67cb25
Packit 67cb25
      /* initial values */
Packit 67cb25
      x1 = gsl_vector_get(reg_param, 0);
Packit 67cb25
      x1 *= x1;
Packit 67cb25
      y1 = gsl_vector_get(eta, 0);
Packit 67cb25
      y1 *= y1;
Packit 67cb25
Packit 67cb25
      x2 = gsl_vector_get(reg_param, 1);
Packit 67cb25
      x2 *= x2;
Packit 67cb25
      y2 = gsl_vector_get(eta, 1);
Packit 67cb25
      y2 *= y2;
Packit 67cb25
Packit 67cb25
      for (i = 1; i < n - 1; ++i)
Packit 67cb25
        {
Packit 67cb25
          /*
Packit 67cb25
           * The points (x1,y1), (x2,y2), (x3,y3) are the previous,
Packit 67cb25
           * current, and next point on the L-curve. We will find
Packit 67cb25
           * the circle which fits these 3 points and take its radius
Packit 67cb25
           * as an estimate of the curvature at this point.
Packit 67cb25
           */
Packit 67cb25
          double lamip1 = gsl_vector_get(reg_param, i + 1);
Packit 67cb25
          double etaip1 = gsl_vector_get(eta, i + 1);
Packit 67cb25
          double x3 = lamip1 * lamip1;
Packit 67cb25
          double y3 = etaip1 * etaip1;
Packit 67cb25
Packit 67cb25
          double x21 = x2 - x1;
Packit 67cb25
          double y21 = y2 - y1;
Packit 67cb25
          double x31 = x3 - x1;
Packit 67cb25
          double y31 = y3 - y1;
Packit 67cb25
          double h21 = x21*x21 + y21*y21;
Packit 67cb25
          double h31 = x31*x31 + y31*y31;
Packit 67cb25
          double d = fabs(2.0 * (x21*y31 - x31*y21));
Packit 67cb25
          double r = sqrt(h21*h31*((x3-x2)*(x3-x2)+(y3-y2)*(y3-y2))) / d;
Packit 67cb25
Packit 67cb25
          /* if d =~ 0 then there are nearly colinear points */
Packit 67cb25
          if (gsl_finite(r))
Packit 67cb25
            {
Packit 67cb25
              /* check for smallest radius of curvature */
Packit 67cb25
              if (r < rmin || rmin < 0.0)
Packit 67cb25
                {
Packit 67cb25
                  rmin = r;
Packit 67cb25
                  *idx = i;
Packit 67cb25
                }
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          /* update previous/current L-curve values */
Packit 67cb25
          x1 = x2;
Packit 67cb25
          y1 = y2;
Packit 67cb25
          x2 = x3;
Packit 67cb25
          y2 = y3;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* check if a minimum radius was found */
Packit 67cb25
      if (rmin < 0.0)
Packit 67cb25
        {
Packit 67cb25
          /* possibly co-linear points */
Packit 67cb25
          GSL_ERROR("failed to find minimum radius", GSL_EINVAL);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return s;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_multifit_linear_lcorner2() */
Packit 67cb25
Packit 67cb25
#define GSL_MULTIFIT_MAXK      100
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_L()
Packit 67cb25
  Compute discrete approximation to derivative operator of order
Packit 67cb25
k on a regular grid of p points, ie: L is (p-k)-by-p
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_Lk(const size_t p, const size_t k, gsl_matrix *L)
Packit 67cb25
{
Packit 67cb25
  if (p <= k)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("p must be larger than derivative order", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (k >= GSL_MULTIFIT_MAXK - 1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("derivative order k too large", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p - k != L->size1 || p != L->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("L matrix must be (p-k)-by-p", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      double c_data[GSL_MULTIFIT_MAXK];
Packit 67cb25
      gsl_vector_view cv = gsl_vector_view_array(c_data, k + 1);
Packit 67cb25
      size_t i, j;
Packit 67cb25
Packit 67cb25
      /* zeroth derivative */
Packit 67cb25
      if (k == 0)
Packit 67cb25
        {
Packit 67cb25
          gsl_matrix_set_identity(L);
Packit 67cb25
          return GSL_SUCCESS;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_matrix_set_zero(L);
Packit 67cb25
  
Packit 67cb25
      gsl_vector_set_zero(&cv.vector);
Packit 67cb25
      gsl_vector_set(&cv.vector, 0, -1.0);
Packit 67cb25
      gsl_vector_set(&cv.vector, 1, 1.0);
Packit 67cb25
Packit 67cb25
      for (i = 1; i < k; ++i)
Packit 67cb25
        {
Packit 67cb25
          double cjm1 = 0.0;
Packit 67cb25
Packit 67cb25
          for (j = 0; j < k + 1; ++j)
Packit 67cb25
            {
Packit 67cb25
              double cj = gsl_vector_get(&cv.vector, j);
Packit 67cb25
Packit 67cb25
              gsl_vector_set(&cv.vector, j, cjm1 - cj);
Packit 67cb25
              cjm1 = cj;
Packit 67cb25
            }
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* build L, the c_i are the entries on the diagonals */
Packit 67cb25
      for (i = 0; i < k + 1; ++i)
Packit 67cb25
        {
Packit 67cb25
          gsl_vector_view v = gsl_matrix_superdiagonal(L, i);
Packit 67cb25
          double ci = gsl_vector_get(&cv.vector, i);
Packit 67cb25
Packit 67cb25
          gsl_vector_set_all(&v.vector, ci);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_multifit_linear_Lk() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_multifit_linear_Lsobolev()
Packit 67cb25
  Construct Sobolev smoothing norm operator
Packit 67cb25
Packit 67cb25
L = [ a_0 I; a_1 L_1; a_2 L_2; ...; a_k L_k ]
Packit 67cb25
Packit 67cb25
by computing the Cholesky factor of L^T L
Packit 67cb25
Packit 67cb25
Inputs: p     - number of columns of L
Packit 67cb25
        kmax  - maximum derivative order (< p)
Packit 67cb25
        alpha - vector of weights; alpha_k multiplies L_k, size kmax + 1
Packit 67cb25
        L     - (output) upper triangular Sobolev matrix p-by-p,
Packit 67cb25
                stored in upper triangle
Packit 67cb25
        work  - workspace
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) work->Q is used to store intermediate L_k matrices
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_linear_Lsobolev(const size_t p, const size_t kmax,
Packit 67cb25
                             const gsl_vector *alpha, gsl_matrix *L,
Packit 67cb25
                             gsl_multifit_linear_workspace *work)
Packit 67cb25
{
Packit 67cb25
  if (p > work->pmax)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("p is larger than workspace", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p <= kmax)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("p must be larger than derivative order", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (kmax + 1 != alpha->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("alpha must be size kmax + 1", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p != L->size1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("L matrix is wrong size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (L->size1 != L->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("L matrix is not square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int s;
Packit 67cb25
      size_t j, k;
Packit 67cb25
      gsl_vector_view d = gsl_matrix_diagonal(L);
Packit 67cb25
      const double alpha0 = gsl_vector_get(alpha, 0);
Packit 67cb25
Packit 67cb25
      /* initialize L to alpha0^2 I */
Packit 67cb25
      gsl_matrix_set_zero(L);
Packit 67cb25
      gsl_vector_add_constant(&d.vector, alpha0 * alpha0);
Packit 67cb25
Packit 67cb25
      for (k = 1; k <= kmax; ++k)
Packit 67cb25
        {
Packit 67cb25
          gsl_matrix_view Lk = gsl_matrix_submatrix(work->Q, 0, 0, p - k, p);
Packit 67cb25
          double ak = gsl_vector_get(alpha, k);
Packit 67cb25
Packit 67cb25
          /* compute a_k L_k */
Packit 67cb25
          s = gsl_multifit_linear_Lk(p, k, &Lk.matrix);
Packit 67cb25
          if (s)
Packit 67cb25
            return s;
Packit 67cb25
Packit 67cb25
          gsl_matrix_scale(&Lk.matrix, ak);
Packit 67cb25
Packit 67cb25
          /* LTL += L_k^T L_k */
Packit 67cb25
          gsl_blas_dsyrk(CblasLower, CblasTrans, 1.0, &Lk.matrix, 1.0, L);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      s = gsl_linalg_cholesky_decomp(L);
Packit 67cb25
      if (s)
Packit 67cb25
        return s;
Packit 67cb25
Packit 67cb25
      /* copy Cholesky factor to upper triangle and zero out bottom */
Packit 67cb25
      gsl_matrix_transpose_tricpy('L', 1, L, L);
Packit 67cb25
Packit 67cb25
      for (j = 0; j < p; ++j)
Packit 67cb25
        {
Packit 67cb25
          for (k = 0; k < j; ++k)
Packit 67cb25
            gsl_matrix_set(L, j, k, 0.0);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}