Blame multifit/test_shaw.c

Packit 67cb25
/*
Packit 67cb25
 * test_shaw.c
Packit 67cb25
 *
Packit 67cb25
 * Test L-curve (Tikhonov) regression routines using Shaw
Packit 67cb25
 * problem. See example 1.10 of
Packit 67cb25
 *
Packit 67cb25
 * [1] R.C. Aster, B. Borchers and C. H. Thurber,
Packit 67cb25
 *     Parameter Estimation and Inverse Problems (2nd ed), 2012.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include <gsl/gsl_sf_trig.h>
Packit 67cb25
Packit 67cb25
/* alternate (and inefficient) method of computing G(lambda) */
Packit 67cb25
static double
Packit 67cb25
shaw_gcv_G(const double lambda, const gsl_matrix * X, const gsl_vector * y,
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
  gsl_matrix * XTX = gsl_matrix_alloc(p, p);
Packit 67cb25
  gsl_matrix * XI = gsl_matrix_alloc(p, n);
Packit 67cb25
  gsl_matrix * XXI = gsl_matrix_alloc(n, n);
Packit 67cb25
  gsl_vector * c = gsl_vector_alloc(p);
Packit 67cb25
  gsl_vector_view d;
Packit 67cb25
  double rnorm, snorm;
Packit 67cb25
  double term1, term2, G;
Packit 67cb25
  size_t i;
Packit 67cb25
Packit 67cb25
  /* compute regularized solution with this lambda */
Packit 67cb25
  gsl_multifit_linear_solve(lambda, X, y, c, &rnorm, &snorm, work);
Packit 67cb25
Packit 67cb25
  /* compute X^T X */
Packit 67cb25
  gsl_blas_dsyrk(CblasLower, CblasTrans, 1.0, X, 0.0, XTX);
Packit 67cb25
Packit 67cb25
  /* add lambda*I */
Packit 67cb25
  d = gsl_matrix_diagonal(XTX);
Packit 67cb25
  gsl_vector_add_constant(&d.vector, lambda * lambda);
Packit 67cb25
Packit 67cb25
  /* invert (X^T X + lambda*I) */
Packit 67cb25
  gsl_linalg_cholesky_decomp1(XTX);
Packit 67cb25
  gsl_linalg_cholesky_invert(XTX);
Packit 67cb25
  gsl_matrix_transpose_tricpy('L', 0, XTX, XTX);
Packit 67cb25
Packit 67cb25
  /* XI = (X^T X + lambda*I)^{-1} X^T */
Packit 67cb25
  gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0, XTX, X, 0.0, XI);
Packit 67cb25
Packit 67cb25
  /* XXI = X (X^T X + lambda*I)^{-1} X^T */
Packit 67cb25
  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, X, XI, 0.0, XXI);
Packit 67cb25
Packit 67cb25
  /* compute: term1 = Tr(I - X XI) */
Packit 67cb25
  term1 = 0.0;
Packit 67cb25
  for (i = 0; i < n; ++i)
Packit 67cb25
    {
Packit 67cb25
      double *Ai = gsl_matrix_ptr(XXI, i, i);
Packit 67cb25
      term1 += 1.0 - (*Ai);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  gsl_matrix_free(XTX);
Packit 67cb25
  gsl_matrix_free(XI);
Packit 67cb25
  gsl_matrix_free(XXI);
Packit 67cb25
  gsl_vector_free(c);
Packit 67cb25
Packit 67cb25
  term2 = rnorm / term1;
Packit 67cb25
Packit 67cb25
  return term2 * term2;;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* construct design matrix and rhs vector for Shaw problem */
Packit 67cb25
static int
Packit 67cb25
shaw_system(gsl_matrix * X, gsl_vector * y)
Packit 67cb25
{
Packit 67cb25
  int s = GSL_SUCCESS;
Packit 67cb25
  const size_t n = X->size1;
Packit 67cb25
  const size_t p = X->size2;
Packit 67cb25
  const double dtheta = M_PI / (double) p;
Packit 67cb25
  size_t i, j;
Packit 67cb25
  gsl_vector *m = gsl_vector_alloc(p);
Packit 67cb25
Packit 67cb25
  /* build the design matrix */
Packit 67cb25
  for (i = 0; i < n; ++i)
Packit 67cb25
    {
Packit 67cb25
      double si = (i + 0.5) * M_PI / n - M_PI / 2.0;
Packit 67cb25
      double csi = cos(si);
Packit 67cb25
      double sni = sin(si);
Packit 67cb25
Packit 67cb25
      for (j = 0; j < p; ++j)
Packit 67cb25
        {
Packit 67cb25
          double thetaj = (j + 0.5) * M_PI / p - M_PI / 2.0;
Packit 67cb25
          double term1 = csi + cos(thetaj);
Packit 67cb25
          double term2 = gsl_sf_sinc(sni + sin(thetaj));
Packit 67cb25
          double Xij = term1 * term1 * term2 * term2 * dtheta;
Packit 67cb25
Packit 67cb25
          gsl_matrix_set(X, i, j, Xij);
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* construct coefficient vector */
Packit 67cb25
  {
Packit 67cb25
    const double a1 = 2.0;
Packit 67cb25
    const double a2 = 1.0;
Packit 67cb25
    const double c1 = 6.0;
Packit 67cb25
    const double c2 = 2.0;
Packit 67cb25
    const double t1 = 0.8;
Packit 67cb25
    const double t2 = -0.5;
Packit 67cb25
Packit 67cb25
    for (j = 0; j < p; ++j)
Packit 67cb25
      {
Packit 67cb25
        double tj = -M_PI / 2.0 + (j + 0.5) * dtheta;
Packit 67cb25
        double mj = a1 * exp(-c1 * (tj - t1) * (tj - t1)) +
Packit 67cb25
                    a2 * exp(-c2 * (tj - t2) * (tj - t2));
Packit 67cb25
        gsl_vector_set(m, j, mj);
Packit 67cb25
      }
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  /* construct rhs vector */
Packit 67cb25
  gsl_blas_dgemv(CblasNoTrans, 1.0, X, m, 0.0, y);
Packit 67cb25
Packit 67cb25
  gsl_vector_free(m);
Packit 67cb25
Packit 67cb25
  return s;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
test_shaw_system_l(gsl_rng *rng_p, const size_t n, const size_t p,
Packit 67cb25
                   const double lambda_expected,
Packit 67cb25
                   gsl_vector *rhs)
Packit 67cb25
{
Packit 67cb25
  const size_t npoints = 1000; /* number of points on L-curve */
Packit 67cb25
  const double tol1 = 1.0e-12;
Packit 67cb25
  const double tol2 = 1.0e-9;
Packit 67cb25
  const double tol3 = 1.0e-5;
Packit 67cb25
  gsl_vector * reg_param = gsl_vector_alloc(npoints);
Packit 67cb25
  gsl_vector * rho = gsl_vector_alloc(npoints);
Packit 67cb25
  gsl_vector * eta = gsl_vector_alloc(npoints);
Packit 67cb25
Packit 67cb25
  gsl_matrix * X = gsl_matrix_alloc(n, p);
Packit 67cb25
  gsl_matrix * cov = gsl_matrix_alloc(p, p);
Packit 67cb25
  gsl_vector * c = gsl_vector_alloc(p);
Packit 67cb25
  gsl_vector * ytmp = gsl_vector_alloc(n);
Packit 67cb25
  gsl_vector * y;
Packit 67cb25
  gsl_vector * r = gsl_vector_alloc(n);
Packit 67cb25
  gsl_multifit_linear_workspace * work = 
Packit 67cb25
    gsl_multifit_linear_alloc (n, p);
Packit 67cb25
Packit 67cb25
  size_t reg_idx, i;
Packit 67cb25
  double lambda, rnorm, snorm;
Packit 67cb25
Packit 67cb25
  /* build design matrix */
Packit 67cb25
  shaw_system(X, ytmp);
Packit 67cb25
Packit 67cb25
  if (rhs)
Packit 67cb25
    y = rhs;
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      y = ytmp;
Packit 67cb25
Packit 67cb25
      /* add random noise to exact rhs vector */
Packit 67cb25
      test_random_vector_noise(rng_p, y);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* SVD decomposition */
Packit 67cb25
  gsl_multifit_linear_svd(X, work);
Packit 67cb25
Packit 67cb25
  /* calculate L-curve */
Packit 67cb25
  gsl_multifit_linear_lcurve(y, reg_param, rho, eta, work);
Packit 67cb25
Packit 67cb25
  /* test rho and eta vectors */
Packit 67cb25
  for (i = 0; i < npoints; ++i)
Packit 67cb25
    {
Packit 67cb25
      double rhoi = gsl_vector_get(rho, i);
Packit 67cb25
      double etai = gsl_vector_get(eta, i);
Packit 67cb25
      double lami = gsl_vector_get(reg_param, i);
Packit 67cb25
Packit 67cb25
      /* solve regularized system and check for consistent rho/eta values */
Packit 67cb25
      gsl_multifit_linear_solve(lami, X, y, c, &rnorm, &snorm, work);
Packit 67cb25
      gsl_test_rel(rhoi, rnorm, tol3, "shaw rho n=%zu p=%zu lambda=%e",
Packit 67cb25
                   n, p, lami);
Packit 67cb25
      gsl_test_rel(etai, snorm, tol1, "shaw eta n=%zu p=%zu lambda=%e",
Packit 67cb25
                   n, p, lami);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* calculate corner of L-curve */
Packit 67cb25
  gsl_multifit_linear_lcorner(rho, eta, &reg_idx);
Packit 67cb25
Packit 67cb25
  lambda = gsl_vector_get(reg_param, reg_idx);
Packit 67cb25
Packit 67cb25
  /* test against known lambda value if given */
Packit 67cb25
  if (lambda_expected > 0.0)
Packit 67cb25
    {
Packit 67cb25
      gsl_test_rel(lambda, lambda_expected, tol1,
Packit 67cb25
                   "shaw: n=%zu p=%zu L-curve corner lambda",
Packit 67cb25
                   n, p);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* compute regularized solution with optimal lambda */
Packit 67cb25
  gsl_multifit_linear_solve(lambda, X, y, c, &rnorm, &snorm, work);
Packit 67cb25
Packit 67cb25
  /* compute residual norm ||y - X c|| */
Packit 67cb25
  gsl_vector_memcpy(r, y);
Packit 67cb25
  gsl_blas_dgemv(CblasNoTrans, 1.0, X, c, -1.0, r);
Packit 67cb25
Packit 67cb25
  /* test rnorm value */
Packit 67cb25
  gsl_test_rel(rnorm, gsl_blas_dnrm2(r), tol2,
Packit 67cb25
               "shaw: n=%zu p=%zu rnorm", n, p);
Packit 67cb25
Packit 67cb25
  /* test snorm value */
Packit 67cb25
  gsl_test_rel(snorm, gsl_blas_dnrm2(c), tol2,
Packit 67cb25
               "shaw: n=%zu p=%zu snorm", n, p);
Packit 67cb25
Packit 67cb25
  gsl_matrix_free(X);
Packit 67cb25
  gsl_matrix_free(cov);
Packit 67cb25
  gsl_vector_free(reg_param);
Packit 67cb25
  gsl_vector_free(rho);
Packit 67cb25
  gsl_vector_free(eta);
Packit 67cb25
  gsl_vector_free(r);
Packit 67cb25
  gsl_vector_free(c);
Packit 67cb25
  gsl_vector_free(ytmp);
Packit 67cb25
  gsl_multifit_linear_free(work);
Packit 67cb25
Packit 67cb25
  return 0;
Packit 67cb25
} /* test_shaw_system_l() */
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
test_shaw_system_gcv(gsl_rng *rng_p, const size_t n, const size_t p,
Packit 67cb25
                     const double lambda_expected,
Packit 67cb25
                     gsl_vector *rhs)
Packit 67cb25
{
Packit 67cb25
  const size_t npoints = 200; /* number of points on L-curve */
Packit 67cb25
  const double tol1 = 1.0e-12;
Packit 67cb25
  const double tol2 = 1.4e-10;
Packit 67cb25
  const double tol3 = 1.0e-5;
Packit 67cb25
  gsl_vector * reg_param = gsl_vector_alloc(npoints);
Packit 67cb25
  gsl_vector * G = gsl_vector_alloc(npoints);
Packit 67cb25
Packit 67cb25
  gsl_matrix * X = gsl_matrix_alloc(n, p);
Packit 67cb25
  gsl_matrix * cov = gsl_matrix_alloc(p, p);
Packit 67cb25
  gsl_vector * c = gsl_vector_alloc(p);
Packit 67cb25
  gsl_vector * ytmp = gsl_vector_alloc(n);
Packit 67cb25
  gsl_vector * y;
Packit 67cb25
  gsl_vector * r = gsl_vector_alloc(n);
Packit 67cb25
  gsl_multifit_linear_workspace * work = 
Packit 67cb25
    gsl_multifit_linear_alloc (n, p);
Packit 67cb25
Packit 67cb25
  size_t reg_idx, i;
Packit 67cb25
  double lambda, rnorm, snorm, G_lambda;
Packit 67cb25
Packit 67cb25
  /* build design matrix */
Packit 67cb25
  shaw_system(X, ytmp);
Packit 67cb25
Packit 67cb25
  if (rhs)
Packit 67cb25
    y = rhs;
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      y = ytmp;
Packit 67cb25
Packit 67cb25
      /* add random noise to exact rhs vector */
Packit 67cb25
      test_random_vector_noise(rng_p, y);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* SVD decomposition */
Packit 67cb25
  gsl_multifit_linear_svd(X, work);
Packit 67cb25
Packit 67cb25
  /* calculate GCV curve */
Packit 67cb25
  gsl_multifit_linear_gcv(y, reg_param, G, &lambda, &G_lambda, work);
Packit 67cb25
Packit 67cb25
  /* test G vector */
Packit 67cb25
  for (i = 0; i < npoints; ++i)
Packit 67cb25
    {
Packit 67cb25
      double lami = gsl_vector_get(reg_param, i);
Packit 67cb25
Packit 67cb25
      if (lami > 1.0e-5)
Packit 67cb25
        {
Packit 67cb25
          /* test unreliable for small lambda */
Packit 67cb25
          double Gi = gsl_vector_get(G, i);
Packit 67cb25
          double Gi_expected = shaw_gcv_G(lami, X, y, work);
Packit 67cb25
Packit 67cb25
          gsl_test_rel(Gi, Gi_expected, tol3, "shaw[%zu,%zu] gcv G i=%zu lambda=%e",
Packit 67cb25
                       n, p, i, lami);
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* test against known lambda value if given */
Packit 67cb25
  if (lambda_expected > 0.0)
Packit 67cb25
    {
Packit 67cb25
      gsl_test_rel(lambda, lambda_expected, tol2,
Packit 67cb25
                   "shaw gcv: n=%zu p=%zu lambda",
Packit 67cb25
                   n, p);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* compute regularized solution with optimal lambda */
Packit 67cb25
  gsl_multifit_linear_solve(lambda, X, y, c, &rnorm, &snorm, work);
Packit 67cb25
Packit 67cb25
  /* compute residual norm ||y - X c|| */
Packit 67cb25
  gsl_vector_memcpy(r, y);
Packit 67cb25
  gsl_blas_dgemv(CblasNoTrans, 1.0, X, c, -1.0, r);
Packit 67cb25
Packit 67cb25
  /* test rnorm value */
Packit 67cb25
  gsl_test_rel(rnorm, gsl_blas_dnrm2(r), tol2,
Packit 67cb25
               "shaw gcv: n=%zu p=%zu rnorm", n, p);
Packit 67cb25
Packit 67cb25
  /* test snorm value */
Packit 67cb25
  gsl_test_rel(snorm, gsl_blas_dnrm2(c), tol2,
Packit 67cb25
               "shaw gcv: n=%zu p=%zu snorm", n, p);
Packit 67cb25
Packit 67cb25
  gsl_matrix_free(X);
Packit 67cb25
  gsl_matrix_free(cov);
Packit 67cb25
  gsl_vector_free(reg_param);
Packit 67cb25
  gsl_vector_free(G);
Packit 67cb25
  gsl_vector_free(r);
Packit 67cb25
  gsl_vector_free(c);
Packit 67cb25
  gsl_vector_free(ytmp);
Packit 67cb25
  gsl_multifit_linear_free(work);
Packit 67cb25
Packit 67cb25
  return 0;
Packit 67cb25
} /* test_shaw_system_gcv() */
Packit 67cb25
Packit 67cb25
void
Packit 67cb25
test_shaw(void)
Packit 67cb25
{
Packit 67cb25
  gsl_rng * r = gsl_rng_alloc(gsl_rng_default);
Packit 67cb25
Packit 67cb25
  {
Packit 67cb25
    double shaw20_y[] = {
Packit 67cb25
      8.7547455124379323e-04, 5.4996835885761936e-04,
Packit 67cb25
      1.7527999407005367e-06, 1.9552372913117047e-03,
Packit 67cb25
      1.4411645433785081e-02, 5.2800013336393704e-02,
Packit 67cb25
      1.3609152023257112e-01, 2.7203484587635818e-01,
Packit 67cb25
      4.3752225136193390e-01, 5.7547667319875240e-01,
Packit 67cb25
      6.2445052213539942e-01, 5.6252658286441348e-01,
Packit 67cb25
      4.2322239923561566e-01, 2.6768469219560631e-01,
Packit 67cb25
      1.4337901162734543e-01, 6.5614569346074361e-02,
Packit 67cb25
      2.6013851831752945e-02, 9.2336933089481269e-03,
Packit 67cb25
      3.2269066658993694e-03, 1.3999201459261811e-03 };
Packit 67cb25
    gsl_vector_view rhs = gsl_vector_view_array(shaw20_y, 20);
Packit 67cb25
Packit 67cb25
    /* lambda and rhs values from [1] */
Packit 67cb25
    test_shaw_system_l(r, 20, 20, 5.793190958069266e-06, &rhs.vector);
Packit 67cb25
Packit 67cb25
    test_shaw_system_gcv(r, 20, 20, 1.24921780949051038e-05, &rhs.vector);
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  {
Packit 67cb25
    size_t n, p;
Packit 67cb25
Packit 67cb25
    for (n = 10; n <= 50; n += 2)
Packit 67cb25
      {
Packit 67cb25
        for (p = n - 6; p <= n; p += 2)
Packit 67cb25
          test_shaw_system_l(r, n, p, -1.0, NULL);
Packit 67cb25
      }
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  gsl_rng_free(r);
Packit 67cb25
} /* test_shaw() */