Blame splinalg/test.c

Packit 67cb25
/* test.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2012-2014 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 <stdlib.h>
Packit 67cb25
#include <math.h>
Packit 67cb25
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_randist.h>
Packit 67cb25
#include <gsl/gsl_rng.h>
Packit 67cb25
#include <gsl/gsl_matrix.h>
Packit 67cb25
#include <gsl/gsl_vector.h>
Packit 67cb25
#include <gsl/gsl_test.h>
Packit 67cb25
#include <gsl/gsl_blas.h>
Packit 67cb25
#include <gsl/gsl_spmatrix.h>
Packit 67cb25
#include <gsl/gsl_spblas.h>
Packit 67cb25
#include <gsl/gsl_splinalg.h>
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
create_random_sparse()
Packit 67cb25
  Create a random sparse matrix with approximately
Packit 67cb25
M*N*density non-zero entries
Packit 67cb25
Packit 67cb25
Inputs: M       - number of rows
Packit 67cb25
        N       - number of columns
Packit 67cb25
        density - sparse density \in [0,1]
Packit 67cb25
                  0 = no non-zero entries
Packit 67cb25
                  1 = all m*n entries are filled
Packit 67cb25
        r       - random number generator
Packit 67cb25
Packit 67cb25
Return: pointer to sparse matrix in triplet format (must be freed by caller)
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) non-zero matrix entries are uniformly distributed in [0,1]
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static gsl_spmatrix *
Packit 67cb25
create_random_sparse(const size_t M, const size_t N, const double density,
Packit 67cb25
                     const gsl_rng *r)
Packit 67cb25
{
Packit 67cb25
  size_t nnzwanted = (size_t) floor(M * N * GSL_MIN(density, 1.0));
Packit 67cb25
  gsl_spmatrix *m = gsl_spmatrix_alloc_nzmax(M, N,
Packit 67cb25
                                             nnzwanted,
Packit 67cb25
                                             GSL_SPMATRIX_TRIPLET);
Packit 67cb25
  size_t i;
Packit 67cb25
Packit 67cb25
  /* set diagonal entries to try to ensure non-singularity */
Packit 67cb25
  for (i = 0; i < GSL_MIN(M, N); ++i)
Packit 67cb25
    {
Packit 67cb25
      double x = gsl_rng_uniform(r);
Packit 67cb25
      gsl_spmatrix_set(m, i, i, x);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  while (gsl_spmatrix_nnz(m) < nnzwanted)
Packit 67cb25
    {
Packit 67cb25
      /* generate a random row and column */
Packit 67cb25
      size_t i = gsl_rng_uniform(r) * M;
Packit 67cb25
      size_t j = gsl_rng_uniform(r) * N;
Packit 67cb25
Packit 67cb25
      /* generate random m_{ij} and add it */
Packit 67cb25
      double x = gsl_rng_uniform(r);
Packit 67cb25
      gsl_spmatrix_set(m, i, j, x);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return m;
Packit 67cb25
} /* create_random_sparse() */
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
create_random_vector(gsl_vector *v, const gsl_rng *r)
Packit 67cb25
{
Packit 67cb25
  size_t i;
Packit 67cb25
Packit 67cb25
  for (i = 0; i < v->size; ++i)
Packit 67cb25
    {
Packit 67cb25
      double x = gsl_rng_uniform(r);
Packit 67cb25
      gsl_vector_set(v, i, x);
Packit 67cb25
    }
Packit 67cb25
} /* create_random_vector() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
test_poisson()
Packit 67cb25
  Solve u''(x) = -pi^2 sin(pi*x), u(x) = sin(pi*x)
Packit 67cb25
  epsrel is the relative error threshold with the exact solution
Packit 67cb25
*/
Packit 67cb25
static void
Packit 67cb25
test_poisson(const size_t N, const double epsrel, const int compress)
Packit 67cb25
{
Packit 67cb25
  const gsl_splinalg_itersolve_type *T = gsl_splinalg_itersolve_gmres;
Packit 67cb25
  const size_t n = N - 2;                     /* subtract 2 to exclude boundaries */
Packit 67cb25
  const double h = 1.0 / (N - 1.0);           /* grid spacing */
Packit 67cb25
  const double tol = 1.0e-9;
Packit 67cb25
  const size_t max_iter = 10;
Packit 67cb25
  size_t iter = 0;
Packit 67cb25
  gsl_spmatrix *A = gsl_spmatrix_alloc(n ,n); /* triplet format */
Packit 67cb25
  gsl_spmatrix *B;
Packit 67cb25
  gsl_vector *b = gsl_vector_alloc(n);        /* right hand side vector */
Packit 67cb25
  gsl_vector *u = gsl_vector_calloc(n);       /* solution vector, u0 = 0 */
Packit 67cb25
  gsl_splinalg_itersolve *w = gsl_splinalg_itersolve_alloc(T, n, 0);
Packit 67cb25
  const char *desc = gsl_splinalg_itersolve_name(w);
Packit 67cb25
  size_t i;
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  /* construct the sparse matrix for the finite difference equation */
Packit 67cb25
Packit 67cb25
  /* first row of matrix */
Packit 67cb25
  gsl_spmatrix_set(A, 0, 0, -2.0);
Packit 67cb25
  gsl_spmatrix_set(A, 0, 1, 1.0);
Packit 67cb25
Packit 67cb25
  /* loop over interior grid points */
Packit 67cb25
  for (i = 1; i < n - 1; ++i)
Packit 67cb25
    {
Packit 67cb25
      gsl_spmatrix_set(A, i, i + 1, 1.0);
Packit 67cb25
      gsl_spmatrix_set(A, i, i, -2.0);
Packit 67cb25
      gsl_spmatrix_set(A, i, i - 1, 1.0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* last row of matrix */
Packit 67cb25
  gsl_spmatrix_set(A, n - 1, n - 1, -2.0);
Packit 67cb25
  gsl_spmatrix_set(A, n - 1, n - 2, 1.0);
Packit 67cb25
Packit 67cb25
  /* scale by h^2 */
Packit 67cb25
  gsl_spmatrix_scale(A, 1.0 / (h * h));
Packit 67cb25
Packit 67cb25
  /* construct right hand side vector */
Packit 67cb25
  for (i = 0; i < n; ++i)
Packit 67cb25
    {
Packit 67cb25
      double xi = (i + 1) * h;
Packit 67cb25
      double bi = -M_PI * M_PI * sin(M_PI * xi);
Packit 67cb25
      gsl_vector_set(b, i, bi);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (compress)
Packit 67cb25
    B = gsl_spmatrix_compcol(A);
Packit 67cb25
  else
Packit 67cb25
    B = A;
Packit 67cb25
Packit 67cb25
  /* solve the system */
Packit 67cb25
  do
Packit 67cb25
    {
Packit 67cb25
      status = gsl_splinalg_itersolve_iterate(B, b, tol, u, w);
Packit 67cb25
    }
Packit 67cb25
  while (status == GSL_CONTINUE && ++iter < max_iter);
Packit 67cb25
Packit 67cb25
  gsl_test(status, "%s poisson status s=%d N=%zu", desc, status, N);
Packit 67cb25
Packit 67cb25
  /* check solution against analytic */
Packit 67cb25
  for (i = 0; i < n; ++i)
Packit 67cb25
    {
Packit 67cb25
      double xi = (i + 1) * h;
Packit 67cb25
      double u_gsl = gsl_vector_get(u, i);
Packit 67cb25
      double u_exact = sin(M_PI * xi);
Packit 67cb25
Packit 67cb25
      gsl_test_rel(u_gsl, u_exact, epsrel, "%s poisson N=%zu i=%zu",
Packit 67cb25
                   desc, N, i);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* check that the residual satisfies ||r|| <= tol*||b|| */
Packit 67cb25
  {
Packit 67cb25
    gsl_vector *r = gsl_vector_alloc(n);
Packit 67cb25
    double normr, normb;
Packit 67cb25
Packit 67cb25
    gsl_vector_memcpy(r, b);
Packit 67cb25
    gsl_spblas_dgemv(CblasNoTrans, -1.0, A, u, 1.0, r);
Packit 67cb25
Packit 67cb25
    normr = gsl_blas_dnrm2(r);
Packit 67cb25
    normb = gsl_blas_dnrm2(b);
Packit 67cb25
Packit 67cb25
    status = (normr <= tol*normb) != 1;
Packit 67cb25
    gsl_test(status, "%s poisson residual N=%zu normr=%.12e normb=%.12e",
Packit 67cb25
             desc, N, normr, normb);
Packit 67cb25
Packit 67cb25
    gsl_vector_free(r);
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  gsl_splinalg_itersolve_free(w);
Packit 67cb25
  gsl_spmatrix_free(A);
Packit 67cb25
  gsl_vector_free(b);
Packit 67cb25
  gsl_vector_free(u);
Packit 67cb25
Packit 67cb25
  if (compress)
Packit 67cb25
    gsl_spmatrix_free(B);
Packit 67cb25
} /* test_poisson() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
test_toeplitz()
Packit 67cb25
  Test Toeplitz matrix T = A + B + C where:
Packit 67cb25
A = diag(a,-1)
Packit 67cb25
B = diag(b)
Packit 67cb25
C = diag(c,1)
Packit 67cb25
Packit 67cb25
rhs = ones(n,1)
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
test_toeplitz(const size_t N, const double a, const double b,
Packit 67cb25
              const double c)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
  const double tol = 1.0e-10;
Packit 67cb25
  const size_t max_iter = 10;
Packit 67cb25
  const gsl_splinalg_itersolve_type *T = gsl_splinalg_itersolve_gmres;
Packit 67cb25
  const char *desc;
Packit 67cb25
  gsl_spmatrix *A;
Packit 67cb25
  gsl_vector *rhs, *x;
Packit 67cb25
  gsl_splinalg_itersolve *w;
Packit 67cb25
  size_t i, iter = 0;
Packit 67cb25
Packit 67cb25
  if (N <= 1)
Packit 67cb25
    return;
Packit 67cb25
Packit 67cb25
  A = gsl_spmatrix_alloc(N ,N);
Packit 67cb25
  rhs = gsl_vector_alloc(N);
Packit 67cb25
  x = gsl_vector_calloc(N);
Packit 67cb25
  w = gsl_splinalg_itersolve_alloc(T, N, 0);
Packit 67cb25
  desc = gsl_splinalg_itersolve_name(w);
Packit 67cb25
Packit 67cb25
  /* first row */
Packit 67cb25
  gsl_spmatrix_set(A, 0, 0, b);
Packit 67cb25
  gsl_spmatrix_set(A, 0, 1, c);
Packit 67cb25
Packit 67cb25
  /* interior rows */
Packit 67cb25
  for (i = 1; i < N - 1; ++i)
Packit 67cb25
    {
Packit 67cb25
      gsl_spmatrix_set(A, i, i - 1, a);
Packit 67cb25
      gsl_spmatrix_set(A, i, i, b);
Packit 67cb25
      gsl_spmatrix_set(A, i, i + 1, c);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* last row */
Packit 67cb25
  gsl_spmatrix_set(A, N - 1, N - 2, a);
Packit 67cb25
  gsl_spmatrix_set(A, N - 1, N - 1, b);
Packit 67cb25
Packit 67cb25
  /* set rhs vector */
Packit 67cb25
  gsl_vector_set_all(rhs, 1.0);
Packit 67cb25
Packit 67cb25
  /* solve the system */
Packit 67cb25
  do
Packit 67cb25
    {
Packit 67cb25
      status = gsl_splinalg_itersolve_iterate(A, rhs, tol, x, w);
Packit 67cb25
    }
Packit 67cb25
  while (status == GSL_CONTINUE && ++iter < max_iter);
Packit 67cb25
Packit 67cb25
  gsl_test(status, "%s toeplitz status s=%d N=%zu a=%f b=%f c=%f",
Packit 67cb25
           desc, status, N, a, b, c);
Packit 67cb25
Packit 67cb25
  /* check that the residual satisfies ||r|| <= tol*||b|| */
Packit 67cb25
  {
Packit 67cb25
    gsl_vector *r = gsl_vector_alloc(N);
Packit 67cb25
    double normr, normb;
Packit 67cb25
Packit 67cb25
    gsl_vector_memcpy(r, rhs);
Packit 67cb25
    gsl_spblas_dgemv(CblasNoTrans, -1.0, A, x, 1.0, r);
Packit 67cb25
Packit 67cb25
    normr = gsl_blas_dnrm2(r);
Packit 67cb25
    normb = gsl_blas_dnrm2(rhs);
Packit 67cb25
Packit 67cb25
    status = (normr <= tol*normb) != 1;
Packit 67cb25
    gsl_test(status, "%s toeplitz residual N=%zu a=%f b=%f c=%f normr=%.12e normb=%.12e",
Packit 67cb25
             desc, N, a, b, c, normr, normb);
Packit 67cb25
Packit 67cb25
    gsl_vector_free(r);
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  gsl_vector_free(x);
Packit 67cb25
  gsl_vector_free(rhs);
Packit 67cb25
  gsl_spmatrix_free(A);
Packit 67cb25
  gsl_splinalg_itersolve_free(w);
Packit 67cb25
} /* test_toeplitz() */
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
test_random(const size_t N, const gsl_rng *r, const int compress)
Packit 67cb25
{
Packit 67cb25
  const gsl_splinalg_itersolve_type *T = gsl_splinalg_itersolve_gmres;
Packit 67cb25
  const double tol = 1.0e-8;
Packit 67cb25
  int status;
Packit 67cb25
  gsl_spmatrix *A = create_random_sparse(N, N, 0.3, r);
Packit 67cb25
  gsl_spmatrix *B;
Packit 67cb25
  gsl_vector *b = gsl_vector_alloc(N);
Packit 67cb25
  gsl_vector *x = gsl_vector_calloc(N);
Packit 67cb25
Packit 67cb25
  /* these random matrices require all N iterations to converge */
Packit 67cb25
  gsl_splinalg_itersolve *w = gsl_splinalg_itersolve_alloc(T, N, N);
Packit 67cb25
Packit 67cb25
  const char *desc = gsl_splinalg_itersolve_name(w);
Packit 67cb25
Packit 67cb25
  create_random_vector(b, r);
Packit 67cb25
Packit 67cb25
  if (compress)
Packit 67cb25
    B = gsl_spmatrix_compcol(A);
Packit 67cb25
  else
Packit 67cb25
    B = A;
Packit 67cb25
Packit 67cb25
  status = gsl_splinalg_itersolve_iterate(B, b, tol, x, w);
Packit 67cb25
  gsl_test(status, "%s random status s=%d N=%zu", desc, status, N);
Packit 67cb25
Packit 67cb25
  /* check that the residual satisfies ||r|| <= tol*||b|| */
Packit 67cb25
  {
Packit 67cb25
    gsl_vector *res = gsl_vector_alloc(N);
Packit 67cb25
    double normr, normb;
Packit 67cb25
Packit 67cb25
    gsl_vector_memcpy(res, b);
Packit 67cb25
    gsl_spblas_dgemv(CblasNoTrans, -1.0, A, x, 1.0, res);
Packit 67cb25
Packit 67cb25
    normr = gsl_blas_dnrm2(res);
Packit 67cb25
    normb = gsl_blas_dnrm2(b);
Packit 67cb25
Packit 67cb25
    status = (normr <= tol*normb) != 1;
Packit 67cb25
    gsl_test(status, "%s random residual N=%zu normr=%.12e normb=%.12e",
Packit 67cb25
             desc, N, normr, normb);
Packit 67cb25
Packit 67cb25
    gsl_vector_free(res);
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  gsl_spmatrix_free(A);
Packit 67cb25
  gsl_vector_free(b);
Packit 67cb25
  gsl_vector_free(x);
Packit 67cb25
  gsl_splinalg_itersolve_free(w);
Packit 67cb25
Packit 67cb25
  if (compress)
Packit 67cb25
    gsl_spmatrix_free(B);
Packit 67cb25
} /* test_random() */
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
main()
Packit 67cb25
{
Packit 67cb25
  gsl_rng *r = gsl_rng_alloc(gsl_rng_default);
Packit 67cb25
  size_t n;
Packit 67cb25
Packit 67cb25
  test_poisson(7, 1.0e-1, 0);
Packit 67cb25
  test_poisson(7, 1.0e-1, 1);
Packit 67cb25
Packit 67cb25
  test_poisson(543, 1.0e-5, 0);
Packit 67cb25
  test_poisson(543, 1.0e-5, 1);
Packit 67cb25
Packit 67cb25
  test_poisson(1000, 1.0e-6, 0);
Packit 67cb25
  test_poisson(1000, 1.0e-6, 1);
Packit 67cb25
Packit 67cb25
  test_poisson(5000, 1.0e-7, 0);
Packit 67cb25
  test_poisson(5000, 1.0e-7, 1);
Packit 67cb25
Packit 67cb25
  test_toeplitz(15, 0.01, 1.0, 0.01);
Packit 67cb25
  test_toeplitz(15, 1.0, 1.0, 0.01);
Packit 67cb25
  test_toeplitz(50, 1.0, 2.0, 0.01);
Packit 67cb25
  test_toeplitz(1000, 0.5, 1.0, 0.01);
Packit 67cb25
Packit 67cb25
  for (n = 1; n <= 100; ++n)
Packit 67cb25
    {
Packit 67cb25
      test_random(n, r, 0);
Packit 67cb25
      test_random(n, r, 1);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  gsl_rng_free(r);
Packit 67cb25
Packit 67cb25
  exit (gsl_test_summary());
Packit 67cb25
} /* main() */