Blame multilarge/normal.c

Packit 67cb25
/* normal.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2015, 2016 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_math.h>
Packit 67cb25
#include <gsl/gsl_vector.h>
Packit 67cb25
#include <gsl/gsl_matrix.h>
Packit 67cb25
#include <gsl/gsl_linalg.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_blas.h>
Packit 67cb25
#include <gsl/gsl_eigen.h>
Packit 67cb25
#include <gsl/gsl_multifit.h>
Packit 67cb25
#include <gsl/gsl_multilarge.h>
Packit 67cb25
#include <gsl/gsl_permutation.h>
Packit 67cb25
Packit 67cb25
typedef struct
Packit 67cb25
{
Packit 67cb25
  size_t p;              /* number of columns of LS matrix */
Packit 67cb25
  gsl_matrix *ATA;       /* A^T A, p-by-p */
Packit 67cb25
  gsl_vector *ATb;       /* A^T b, p-by-1 */
Packit 67cb25
  double normb;          /* || b || */
Packit 67cb25
  gsl_matrix *work_ATA;  /* workspace for chol(ATA), p-by-p */
Packit 67cb25
  gsl_permutation *perm; /* permutation vector */
Packit 67cb25
  gsl_vector *workp;     /* workspace size p */
Packit 67cb25
  gsl_vector *work3p;    /* workspace size 3*p */
Packit 67cb25
  gsl_vector *D;         /* scale factors for ATA, size p */
Packit 67cb25
  gsl_vector *c;         /* solution vector for L-curve */
Packit 67cb25
  int eigen;             /* 1 if eigenvalues computed */
Packit 67cb25
  double eval_min;       /* minimum eigenvalue */
Packit 67cb25
  double eval_max;       /* maximum eigenvalue */
Packit 67cb25
  gsl_eigen_symm_workspace *eigen_p;
Packit 67cb25
} normal_state_t;
Packit 67cb25
Packit 67cb25
static void *normal_alloc(const size_t p);
Packit 67cb25
static void normal_free(void *vstate);
Packit 67cb25
static int normal_reset(void *vstate);
Packit 67cb25
static int normal_accumulate(gsl_matrix * A, gsl_vector * b,
Packit 67cb25
                             void * vstate);
Packit 67cb25
static int normal_solve(const double lambda, gsl_vector * x,
Packit 67cb25
                        double * rnorm, double * snorm,
Packit 67cb25
                        void * vstate);
Packit 67cb25
static int normal_rcond(double * rcond, void * vstate);
Packit 67cb25
static int normal_lcurve(gsl_vector * reg_param, gsl_vector * rho,
Packit 67cb25
                         gsl_vector * eta, void * vstate);
Packit 67cb25
static int normal_solve_system(const double lambda, gsl_vector * x,
Packit 67cb25
                               normal_state_t *state);
Packit 67cb25
static int normal_solve_cholesky(gsl_matrix * ATA, const gsl_vector * ATb,
Packit 67cb25
                                 gsl_vector * x, normal_state_t *state);
Packit 67cb25
static int normal_calc_norms(const gsl_vector *x, double *rnorm,
Packit 67cb25
                             double *snorm, normal_state_t *state);
Packit 67cb25
static int normal_eigen(normal_state_t *state);
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
normal_alloc()
Packit 67cb25
  Allocate workspace for solving large linear least squares
Packit 67cb25
problems using the normal equations approach
Packit 67cb25
Packit 67cb25
Inputs: p    - number of columns of LS matrix
Packit 67cb25
Packit 67cb25
Return: pointer to workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static void *
Packit 67cb25
normal_alloc(const size_t p)
Packit 67cb25
{
Packit 67cb25
  normal_state_t *state;
Packit 67cb25
Packit 67cb25
  if (p == 0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_NULL("p must be a positive integer",
Packit 67cb25
                     GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state = calloc(1, sizeof(normal_state_t));
Packit 67cb25
  if (!state)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate normal state", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->p = p;
Packit 67cb25
Packit 67cb25
  state->ATA = gsl_matrix_alloc(p, p);
Packit 67cb25
  if (state->ATA == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate ATA matrix", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->work_ATA = gsl_matrix_alloc(p, p);
Packit 67cb25
  if (state->work_ATA == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate temporary ATA matrix", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->ATb = gsl_vector_alloc(p);
Packit 67cb25
  if (state->ATb == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate ATb vector", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->perm = gsl_permutation_alloc(p);
Packit 67cb25
  if (state->perm == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate perm", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->D = gsl_vector_alloc(p);
Packit 67cb25
  if (state->D == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate D vector", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->workp = gsl_vector_alloc(p);
Packit 67cb25
  if (state->workp == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate temporary ATb vector", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->work3p = gsl_vector_alloc(3 * p);
Packit 67cb25
  if (state->work3p == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate work3p", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->c = gsl_vector_alloc(p);
Packit 67cb25
  if (state->c == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate c vector", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->eigen_p = gsl_eigen_symm_alloc(p);
Packit 67cb25
  if (state->eigen_p == NULL)
Packit 67cb25
    {
Packit 67cb25
      normal_free(state);
Packit 67cb25
      GSL_ERROR_NULL("failed to allocate eigen workspace", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  normal_reset(state);
Packit 67cb25
Packit 67cb25
  return state;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
normal_free(void *vstate)
Packit 67cb25
{
Packit 67cb25
  normal_state_t *state = (normal_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  if (state->ATA)
Packit 67cb25
    gsl_matrix_free(state->ATA);
Packit 67cb25
Packit 67cb25
  if (state->work_ATA)
Packit 67cb25
    gsl_matrix_free(state->work_ATA);
Packit 67cb25
Packit 67cb25
  if (state->ATb)
Packit 67cb25
    gsl_vector_free(state->ATb);
Packit 67cb25
Packit 67cb25
  if (state->perm)
Packit 67cb25
    gsl_permutation_free(state->perm);
Packit 67cb25
Packit 67cb25
  if (state->D)
Packit 67cb25
    gsl_vector_free(state->D);
Packit 67cb25
Packit 67cb25
  if (state->workp)
Packit 67cb25
    gsl_vector_free(state->workp);
Packit 67cb25
Packit 67cb25
  if (state->work3p)
Packit 67cb25
    gsl_vector_free(state->work3p);
Packit 67cb25
Packit 67cb25
  if (state->c)
Packit 67cb25
    gsl_vector_free(state->c);
Packit 67cb25
Packit 67cb25
  if (state->eigen_p)
Packit 67cb25
    gsl_eigen_symm_free(state->eigen_p);
Packit 67cb25
Packit 67cb25
  free(state);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_reset(void *vstate)
Packit 67cb25
{
Packit 67cb25
  normal_state_t *state = (normal_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  gsl_matrix_set_zero(state->ATA);
Packit 67cb25
  gsl_vector_set_zero(state->ATb);
Packit 67cb25
  state->normb = 0.0;
Packit 67cb25
  state->eigen = 0;
Packit 67cb25
  state->eval_min = 0.0;
Packit 67cb25
  state->eval_max = 0.0;
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
normal_accumulate()
Packit 67cb25
  Add a new block of rows to the normal equations system
Packit 67cb25
Packit 67cb25
Inputs: A      - new block of rows, n-by-p
Packit 67cb25
        b      - new rhs vector n-by-1
Packit 67cb25
        vstate - workspace
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_accumulate(gsl_matrix * A, gsl_vector * b, void * vstate)
Packit 67cb25
{
Packit 67cb25
  normal_state_t *state = (normal_state_t *) vstate;
Packit 67cb25
  const size_t n = A->size1;
Packit 67cb25
Packit 67cb25
  if (A->size2 != state->p)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("columns of A do not match workspace", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (n != b->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("A and b have different numbers of rows", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int s;
Packit 67cb25
Packit 67cb25
      /* ATA += A^T A, using only the lower half of the matrix */
Packit 67cb25
      s = gsl_blas_dsyrk(CblasLower, CblasTrans, 1.0, A, 1.0, state->ATA);
Packit 67cb25
      if (s)
Packit 67cb25
        return s;
Packit 67cb25
Packit 67cb25
      /* ATb += A^T b */
Packit 67cb25
      s = gsl_blas_dgemv(CblasTrans, 1.0, A, b, 1.0, state->ATb);
Packit 67cb25
      if (s)
Packit 67cb25
        return s;
Packit 67cb25
Packit 67cb25
      /* update || b || */
Packit 67cb25
      state->normb = gsl_hypot(state->normb, gsl_blas_dnrm2(b));
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
normal_solve()
Packit 67cb25
  Solve normal equations system:
Packit 67cb25
Packit 67cb25
(A^T A + \lambda^2 I) x = A^T b
Packit 67cb25
Packit 67cb25
using Cholesky decomposition
Packit 67cb25
Packit 67cb25
Inputs: lambda - regularization parameter
Packit 67cb25
        x      - (output) solution vector p-by-1
Packit 67cb25
        rnorm  - (output) residual norm ||b - A x||
Packit 67cb25
        snorm  - (output) solution norm ||x||
Packit 67cb25
        vstate - workspace
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_solve(const double lambda, gsl_vector * x,
Packit 67cb25
             double * rnorm, double * snorm,
Packit 67cb25
             void * vstate)
Packit 67cb25
{
Packit 67cb25
  normal_state_t *state = (normal_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  if (x->size != state->p)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("solution vector does not match workspace", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status;
Packit 67cb25
Packit 67cb25
      /* solve system (A^T A) x = A^T b */
Packit 67cb25
      status = normal_solve_system(lambda, x, state);
Packit 67cb25
      if (status)
Packit 67cb25
        {
Packit 67cb25
          GSL_ERROR("failed to solve normal equations", status);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* compute residual norm ||y - X c|| and solution norm ||x|| */
Packit 67cb25
      normal_calc_norms(x, rnorm, snorm, state);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_rcond(double * rcond, void * vstate)
Packit 67cb25
{
Packit 67cb25
  normal_state_t *state = (normal_state_t *) vstate;
Packit 67cb25
  int status = GSL_SUCCESS;
Packit 67cb25
  double rcond_ATA;
Packit 67cb25
Packit 67cb25
  status = gsl_linalg_pcholesky_rcond(state->work_ATA, state->perm, &rcond_ATA, state->work3p);
Packit 67cb25
  if (status == GSL_SUCCESS)
Packit 67cb25
    *rcond = sqrt(rcond_ATA);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
normal_lcurve()
Packit 67cb25
  Compute L-curve of least squares system
Packit 67cb25
Packit 67cb25
Inputs: reg_param - (output) vector of regularization parameters
Packit 67cb25
        rho       - (output) vector of residual norms
Packit 67cb25
        eta       - (output) vector of solution norms
Packit 67cb25
        vstate    - workspace
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_lcurve(gsl_vector * reg_param, gsl_vector * rho,
Packit 67cb25
              gsl_vector * eta, void * vstate)
Packit 67cb25
{
Packit 67cb25
  normal_state_t *state = (normal_state_t *) vstate;
Packit 67cb25
  int status;
Packit 67cb25
  double smin, smax; /* minimum/maximum singular values */
Packit 67cb25
  size_t i;
Packit 67cb25
Packit 67cb25
  if (state->eigen == 0)
Packit 67cb25
    {
Packit 67cb25
      status = normal_eigen(state);
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (state->eval_max < 0.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("matrix is not positive definite", GSL_EDOM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* compute singular values which are sqrts of eigenvalues */
Packit 67cb25
  smax = sqrt(state->eval_max);
Packit 67cb25
  if (state->eval_min > 0.0)
Packit 67cb25
    smin = sqrt(state->eval_min);
Packit 67cb25
  else
Packit 67cb25
    smin = 0.0;
Packit 67cb25
Packit 67cb25
  /* compute vector of regularization parameters */
Packit 67cb25
  gsl_multifit_linear_lreg(smin, smax, reg_param);
Packit 67cb25
Packit 67cb25
  /* solve normal equations for each regularization parameter */
Packit 67cb25
  for (i = 0; i < reg_param->size; ++i)
Packit 67cb25
    {
Packit 67cb25
      double lambda = gsl_vector_get(reg_param, i);
Packit 67cb25
      double rnorm, snorm;
Packit 67cb25
Packit 67cb25
      status = normal_solve_system(lambda, state->c, state);
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
Packit 67cb25
      /* compute ||y - X c|| and ||c|| */
Packit 67cb25
      normal_calc_norms(state->c, &rnorm, &snorm, state);
Packit 67cb25
Packit 67cb25
      gsl_vector_set(rho, i, rnorm);
Packit 67cb25
      gsl_vector_set(eta, i, snorm);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
normal_solve_system()
Packit 67cb25
  Compute solution to normal equations:
Packit 67cb25
Packit 67cb25
(A^T A + lambda^2*I) x = A^T b
Packit 67cb25
Packit 67cb25
using LDL decomposition.
Packit 67cb25
Packit 67cb25
Inputs: x     - (output) solution vector
Packit 67cb25
        state - workspace
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_solve_system(const double lambda, gsl_vector * x, normal_state_t *state)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
  const double lambda_sq = lambda * lambda;
Packit 67cb25
  gsl_vector_view d = gsl_matrix_diagonal(state->work_ATA);
Packit 67cb25
Packit 67cb25
  /* copy ATA matrix to temporary workspace and regularize */
Packit 67cb25
  gsl_matrix_tricpy('L', 1, state->work_ATA, state->ATA);
Packit 67cb25
  gsl_vector_add_constant(&d.vector, lambda_sq);
Packit 67cb25
Packit 67cb25
  /* solve with LDL decomposition */
Packit 67cb25
  status = normal_solve_cholesky(state->work_ATA, state->ATb, x, state);
Packit 67cb25
  if (status)
Packit 67cb25
    return status;
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_solve_cholesky(gsl_matrix * ATA, const gsl_vector * ATb,
Packit 67cb25
                      gsl_vector * x, normal_state_t *state)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  status = gsl_linalg_pcholesky_decomp2(ATA, state->perm, state->D);
Packit 67cb25
  if (status)
Packit 67cb25
    return status;
Packit 67cb25
Packit 67cb25
  status = gsl_linalg_pcholesky_solve2(ATA, state->perm, state->D, ATb, x);
Packit 67cb25
  if (status)
Packit 67cb25
    return status;
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
normal_calc_norms()
Packit 67cb25
  Compute residual norm ||y - X c|| and solution
Packit 67cb25
norm ||c||
Packit 67cb25
Packit 67cb25
Inputs: x     - solution vector
Packit 67cb25
        rnorm - (output) residual norm ||y - X c||
Packit 67cb25
        snorm - (output) solution norm ||c||
Packit 67cb25
        state - workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_calc_norms(const gsl_vector *x, double *rnorm,
Packit 67cb25
                  double *snorm, normal_state_t *state)
Packit 67cb25
{
Packit 67cb25
  double r2;
Packit 67cb25
Packit 67cb25
  /* compute solution norm ||x|| */
Packit 67cb25
  *snorm = gsl_blas_dnrm2(x);
Packit 67cb25
Packit 67cb25
  /* compute residual norm ||b - Ax|| */
Packit 67cb25
Packit 67cb25
  /* compute: A^T A x - 2 A^T b */
Packit 67cb25
  gsl_vector_memcpy(state->workp, state->ATb);
Packit 67cb25
  gsl_blas_dsymv(CblasLower, 1.0, state->ATA, x, -2.0, state->workp);
Packit 67cb25
Packit 67cb25
  /* compute: x^T A^T A x - 2 x^T A^T b */
Packit 67cb25
  gsl_blas_ddot(x, state->workp, &r2;;
Packit 67cb25
Packit 67cb25
  /* add b^T b */
Packit 67cb25
  r2 += state->normb * state->normb;
Packit 67cb25
Packit 67cb25
  *rnorm = sqrt(r2);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
normal_eigen()
Packit 67cb25
  Compute eigenvalues of A^T A matrix, which
Packit 67cb25
are stored in state->workp on output. Also,
Packit 67cb25
state->eval_min and state->eval_max are set
Packit 67cb25
to the minimum/maximum eigenvalues
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
normal_eigen(normal_state_t *state)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  /* copy lower triangle of ATA to temporary workspace */
Packit 67cb25
  gsl_matrix_tricpy('L', 1, state->work_ATA, state->ATA);
Packit 67cb25
Packit 67cb25
  /* compute eigenvalues of ATA */
Packit 67cb25
  status = gsl_eigen_symm(state->work_ATA, state->workp, state->eigen_p);
Packit 67cb25
  if (status)
Packit 67cb25
    return status;
Packit 67cb25
Packit 67cb25
  gsl_vector_minmax(state->workp, &state->eval_min, &state->eval_max);
Packit 67cb25
Packit 67cb25
  state->eigen = 1;
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static const gsl_multilarge_linear_type normal_type =
Packit 67cb25
{
Packit 67cb25
  "normal",
Packit 67cb25
  normal_alloc,
Packit 67cb25
  normal_reset,
Packit 67cb25
  normal_accumulate,
Packit 67cb25
  normal_solve,
Packit 67cb25
  normal_rcond,
Packit 67cb25
  normal_lcurve,
Packit 67cb25
  normal_free
Packit 67cb25
};
Packit 67cb25
Packit 67cb25
const gsl_multilarge_linear_type * gsl_multilarge_linear_normal =
Packit 67cb25
  &normal_type;