Blame integration/fixed.c

Packit 67cb25
/* integration/fixed.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2017 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
/* the code in this module performs fixed-point quadrature calculations for
Packit 67cb25
 * integrands and is based on IQPACK */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <stdlib.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_integration.h>
Packit 67cb25
Packit 67cb25
static int fixed_compute(const double a, const double b, const double alpha, const double beta,
Packit 67cb25
                         gsl_integration_fixed_workspace * w);
Packit 67cb25
static int imtqlx ( const int n, double d[], double e[], double z[] );
Packit 67cb25
Packit 67cb25
gsl_integration_fixed_workspace *
Packit 67cb25
gsl_integration_fixed_alloc(const gsl_integration_fixed_type * type, const size_t n,
Packit 67cb25
                            const double a, const double b, const double alpha, const double beta)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
  gsl_integration_fixed_workspace *w;
Packit 67cb25
Packit 67cb25
  /* check inputs */
Packit 67cb25
  if (n < 1)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_VAL ("workspace size n must be at least 1", GSL_EDOM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w = calloc(1, sizeof(gsl_integration_fixed_workspace));
Packit 67cb25
  if (w == NULL)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_VAL ("unable to allocate workspace", GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->weights = malloc(n * sizeof(double));
Packit 67cb25
  if (w->weights == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_integration_fixed_free(w);
Packit 67cb25
      GSL_ERROR_VAL ("unable to allocate weights", GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->x = malloc(n * sizeof(double));
Packit 67cb25
  if (w->x == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_integration_fixed_free(w);
Packit 67cb25
      GSL_ERROR_VAL ("unable to allocate x", GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->diag = malloc(n * sizeof(double));
Packit 67cb25
  if (w->diag == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_integration_fixed_free(w);
Packit 67cb25
      GSL_ERROR_VAL ("unable to allocate diag", GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->subdiag = malloc(n * sizeof(double));
Packit 67cb25
  if (w->subdiag == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_integration_fixed_free(w);
Packit 67cb25
      GSL_ERROR_VAL ("unable to allocate subdiag", GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->n = n;
Packit 67cb25
  w->type = type;
Packit 67cb25
Packit 67cb25
  /* compute quadrature weights and nodes */
Packit 67cb25
  status = fixed_compute(a, b, alpha, beta, w);
Packit 67cb25
  if (status)
Packit 67cb25
    {
Packit 67cb25
      gsl_integration_fixed_free(w);
Packit 67cb25
      GSL_ERROR_VAL ("error in integration parameters", GSL_EDOM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return w;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
void
Packit 67cb25
gsl_integration_fixed_free(gsl_integration_fixed_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (w->weights)
Packit 67cb25
    free(w->weights);
Packit 67cb25
Packit 67cb25
  if (w->x)
Packit 67cb25
    free(w->x);
Packit 67cb25
Packit 67cb25
  if (w->diag)
Packit 67cb25
    free(w->diag);
Packit 67cb25
Packit 67cb25
  if (w->subdiag)
Packit 67cb25
    free(w->subdiag);
Packit 67cb25
Packit 67cb25
  free(w);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
size_t
Packit 67cb25
gsl_integration_fixed_n(const gsl_integration_fixed_workspace * w)
Packit 67cb25
{
Packit 67cb25
  return w->n;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
double *
Packit 67cb25
gsl_integration_fixed_nodes(const gsl_integration_fixed_workspace * w)
Packit 67cb25
{
Packit 67cb25
  return w->x;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
double *
Packit 67cb25
gsl_integration_fixed_weights(const gsl_integration_fixed_workspace * w)
Packit 67cb25
{
Packit 67cb25
  return w->weights;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_integration_fixed(const gsl_function * func, double * result,
Packit 67cb25
                      const gsl_integration_fixed_workspace * w)
Packit 67cb25
{
Packit 67cb25
  const size_t n = w->n;
Packit 67cb25
  size_t i;
Packit 67cb25
  double sum = 0.0;
Packit 67cb25
Packit 67cb25
  for (i = 0; i < n; ++i)
Packit 67cb25
    {
Packit 67cb25
      double fi = GSL_FN_EVAL(func, w->x[i]);
Packit 67cb25
      sum += w->weights[i] * fi;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  *result = sum;
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
fixed_compute()
Packit 67cb25
  Compute quadrature weights and nodes
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
fixed_compute(const double a, const double b, const double alpha, const double beta,
Packit 67cb25
              gsl_integration_fixed_workspace * w)
Packit 67cb25
{
Packit 67cb25
  int s;
Packit 67cb25
  const size_t n = w->n;
Packit 67cb25
  gsl_integration_fixed_params params;
Packit 67cb25
  size_t i;
Packit 67cb25
Packit 67cb25
  params.a = a;
Packit 67cb25
  params.b = b;
Packit 67cb25
  params.alpha = alpha;
Packit 67cb25
  params.beta = beta;
Packit 67cb25
Packit 67cb25
  /* check input parameters */
Packit 67cb25
  s = (w->type->check)(n, &params);
Packit 67cb25
  if (s)
Packit 67cb25
    return s;
Packit 67cb25
Packit 67cb25
  /* initialize Jacobi matrix */
Packit 67cb25
  s = (w->type->init)(n, w->diag, w->subdiag, &params);
Packit 67cb25
  if (s)
Packit 67cb25
    return s;
Packit 67cb25
Packit 67cb25
  if (params.zemu <= 0.0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("zeroth moment must be positive", GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  for ( i = 0; i < n; i++ )
Packit 67cb25
    {
Packit 67cb25
      w->x[i] = w->diag[i];
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->weights[0] = sqrt (params.zemu);
Packit 67cb25
Packit 67cb25
  for ( i = 1; i < n; i++ )
Packit 67cb25
    {
Packit 67cb25
      w->weights[i] = 0.0;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* diagonalize the Jacobi matrix */
Packit 67cb25
  s = imtqlx (n, w->x, w->subdiag, w->weights);
Packit 67cb25
  if (s)
Packit 67cb25
    return s;
Packit 67cb25
Packit 67cb25
  for (i = 0; i < n; i++)
Packit 67cb25
    {
Packit 67cb25
      w->weights[i] = w->weights[i] * w->weights[i];
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * The current weights and nodes are valid for a = 0, b = 1.
Packit 67cb25
   * Now scale them for arbitrary a,b
Packit 67cb25
   */
Packit 67cb25
  {
Packit 67cb25
    double p = pow ( params.slp, params.al + params.be + 1.0 );
Packit 67cb25
    size_t k;
Packit 67cb25
Packit 67cb25
    for ( k = 0; k < n; k++ )
Packit 67cb25
      {
Packit 67cb25
        w->x[k] = params.shft + params.slp * w->x[k];
Packit 67cb25
        w->weights[k] = w->weights[k] * p;
Packit 67cb25
      }
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/******************************************************************************/
Packit 67cb25
/*
Packit 67cb25
  Purpose:
Packit 67cb25
Packit 67cb25
    IMTQLX diagonalizes a symmetric tridiagonal matrix.
Packit 67cb25
Packit 67cb25
  Discussion:
Packit 67cb25
Packit 67cb25
    This routine is a slightly modified version of the EISPACK routine to 
Packit 67cb25
    perform the implicit QL algorithm on a symmetric tridiagonal matrix. 
Packit 67cb25
Packit 67cb25
    The authors thank the authors of EISPACK for permission to use this
Packit 67cb25
    routine. 
Packit 67cb25
Packit 67cb25
    It has been modified to produce the product Q' * Z, where Z is an input 
Packit 67cb25
    vector and Q is the orthogonal matrix diagonalizing the input matrix.  
Packit 67cb25
    The changes consist (essentially) of applying the orthogonal transformations
Packit 67cb25
    directly to Z as they are generated.
Packit 67cb25
Packit 67cb25
  Licensing:
Packit 67cb25
Packit 67cb25
    This code is distributed under the GNU LGPL license. 
Packit 67cb25
Packit 67cb25
  Modified:
Packit 67cb25
Packit 67cb25
    11 January 2010
Packit 67cb25
Packit 67cb25
  Author:
Packit 67cb25
Packit 67cb25
    Original FORTRAN77 version by Sylvan Elhay, Jaroslav Kautsky.
Packit 67cb25
    C version by John Burkardt.
Packit 67cb25
Packit 67cb25
  Reference:
Packit 67cb25
Packit 67cb25
    Sylvan Elhay, Jaroslav Kautsky,
Packit 67cb25
    Algorithm 655: IQPACK, FORTRAN Subroutines for the Weights of 
Packit 67cb25
    Interpolatory Quadrature,
Packit 67cb25
    ACM Transactions on Mathematical Software,
Packit 67cb25
    Volume 13, Number 4, December 1987, pages 399-415.
Packit 67cb25
Packit 67cb25
    Roger Martin, James Wilkinson,
Packit 67cb25
    The Implicit QL Algorithm,
Packit 67cb25
    Numerische Mathematik,
Packit 67cb25
    Volume 12, Number 5, December 1968, pages 377-383.
Packit 67cb25
Packit 67cb25
  Parameters:
Packit 67cb25
Packit 67cb25
    Input, int N, the order of the matrix.
Packit 67cb25
Packit 67cb25
    Input/output, double D(N), the diagonal entries of the matrix.
Packit 67cb25
    On output, the information in D has been overwritten.
Packit 67cb25
Packit 67cb25
    Input/output, double E(N), the subdiagonal entries of the 
Packit 67cb25
    matrix, in entries E(1) through E(N-1).  On output, the information in
Packit 67cb25
    E has been overwritten.
Packit 67cb25
Packit 67cb25
    Input/output, double Z(N).  On input, a vector.  On output,
Packit 67cb25
    the value of Q' * Z, where Q is the matrix that diagonalizes the
Packit 67cb25
    input symmetric tridiagonal matrix.
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
imtqlx ( const int n, double d[], double e[], double z[] )
Packit 67cb25
{
Packit 67cb25
  double b;
Packit 67cb25
  double c;
Packit 67cb25
  double f;
Packit 67cb25
  double g;
Packit 67cb25
  int i;
Packit 67cb25
  int ii;
Packit 67cb25
  int itn = 30;
Packit 67cb25
  int j;
Packit 67cb25
  int k;
Packit 67cb25
  int l;
Packit 67cb25
  int m;
Packit 67cb25
  int mml;
Packit 67cb25
  double p;
Packit 67cb25
  double r;
Packit 67cb25
  double s;
Packit 67cb25
Packit 67cb25
  if ( n == 1 )
Packit 67cb25
  {
Packit 67cb25
    return GSL_SUCCESS;
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  e[n-1] = 0.0;
Packit 67cb25
Packit 67cb25
  for ( l = 1; l <= n; l++ )
Packit 67cb25
  {
Packit 67cb25
    j = 0;
Packit 67cb25
    for ( ; ; )
Packit 67cb25
    {
Packit 67cb25
      for ( m = l; m <= n; m++ )
Packit 67cb25
      {
Packit 67cb25
        if ( m == n )
Packit 67cb25
        {
Packit 67cb25
          break;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
        if ( fabs ( e[m-1] ) <= GSL_DBL_EPSILON * ( fabs ( d[m-1] ) + fabs ( d[m] ) ) )
Packit 67cb25
        {
Packit 67cb25
          break;
Packit 67cb25
        }
Packit 67cb25
      }
Packit 67cb25
      p = d[l-1];
Packit 67cb25
      if ( m == l )
Packit 67cb25
      {
Packit 67cb25
        break;
Packit 67cb25
      }
Packit 67cb25
      if ( itn <= j )
Packit 67cb25
      {
Packit 67cb25
        return GSL_EMAXITER;
Packit 67cb25
      }
Packit 67cb25
      j = j + 1;
Packit 67cb25
      g = ( d[l] - p ) / ( 2.0 * e[l-1] );
Packit 67cb25
      r =  sqrt ( g * g + 1.0 );
Packit 67cb25
      g = d[m-1] - p + e[l-1] / ( g + fabs ( r ) * GSL_SIGN ( g ) );
Packit 67cb25
      s = 1.0;
Packit 67cb25
      c = 1.0;
Packit 67cb25
      p = 0.0;
Packit 67cb25
      mml = m - l;
Packit 67cb25
Packit 67cb25
      for ( ii = 1; ii <= mml; ii++ )
Packit 67cb25
      {
Packit 67cb25
        i = m - ii;
Packit 67cb25
        f = s * e[i-1];
Packit 67cb25
        b = c * e[i-1];
Packit 67cb25
Packit 67cb25
        if ( fabs ( g ) <= fabs ( f ) )
Packit 67cb25
        {
Packit 67cb25
          c = g / f;
Packit 67cb25
          r =  sqrt ( c * c + 1.0 );
Packit 67cb25
          e[i] = f * r;
Packit 67cb25
          s = 1.0 / r;
Packit 67cb25
          c = c * s;
Packit 67cb25
        }
Packit 67cb25
        else
Packit 67cb25
        {
Packit 67cb25
          s = f / g;
Packit 67cb25
          r =  sqrt ( s * s + 1.0 );
Packit 67cb25
          e[i] = g * r;
Packit 67cb25
          c = 1.0 / r;
Packit 67cb25
          s = s * c;
Packit 67cb25
        }
Packit 67cb25
        g = d[i] - p;
Packit 67cb25
        r = ( d[i-1] - g ) * s + 2.0 * c * b;
Packit 67cb25
        p = s * r;
Packit 67cb25
        d[i] = g + p;
Packit 67cb25
        g = c * r - b;
Packit 67cb25
        f = z[i];
Packit 67cb25
        z[i] = s * z[i-1] + c * f;
Packit 67cb25
        z[i-1] = c * z[i-1] - s * f;
Packit 67cb25
      }
Packit 67cb25
      d[l-1] = d[l-1] - p;
Packit 67cb25
      e[l-1] = g;
Packit 67cb25
      e[m-1] = 0.0;
Packit 67cb25
    }
Packit 67cb25
  }
Packit 67cb25
/*
Packit 67cb25
  Sorting.
Packit 67cb25
*/
Packit 67cb25
  for ( ii = 2; ii <= m; ii++ )
Packit 67cb25
  {
Packit 67cb25
    i = ii - 1;
Packit 67cb25
    k = i;
Packit 67cb25
    p = d[i-1];
Packit 67cb25
Packit 67cb25
    for ( j = ii; j <= n; j++ )
Packit 67cb25
    {
Packit 67cb25
      if ( d[j-1] < p )
Packit 67cb25
      {
Packit 67cb25
         k = j;
Packit 67cb25
         p = d[j-1];
Packit 67cb25
      }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
    if ( k != i )
Packit 67cb25
    {
Packit 67cb25
      d[k-1] = d[i-1];
Packit 67cb25
      d[i-1] = p;
Packit 67cb25
      p = z[i-1];
Packit 67cb25
      z[i-1] = z[k-1];
Packit 67cb25
      z[k-1] = p;
Packit 67cb25
    }
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}