Пример #1
0
/**
 * @brief Low level fitting routine for a Gaussian trend filtering problem.
 * Function used by tf_admm to fit a Gaussian ADMM trendfilter, or as a
 * subproblem by tf_admm_glm when using logistic or poisson losses. Fits
 * the solution for a single value of lambda. Most users will want to call
 * tf_admm, rather than tf_admm_gauss directly.
 *
 * @param y                    a vector of responses
 * @param x                    a vector of response locations; must be in increasing order
 * @param w                    a vector of sample weights
 * @param n                    the length of y, x, and w
 * @param k                    degree of the trendfilter; i.e., k=1 linear
 * @param max_iter             maximum number of ADMM interations; ignored for k=0
 * @param lam                  the value of lambda
 * @param beta                 allocated space for output coefficents; must pre-fill as it is used in warm start
 * @param alpha                allocated space for ADMM alpha covariates; must pre-fill as it is used in warm start
 * @param u                    allocated space for ADMM u covariates; must pre-fill as it is used in warm start
 * @param obj                  allocated space to store the objective; will fill at most max_iter elements
 * @param iter                 allocated space to store the number of iterations; will fill just one element
 * @param rho                  tuning parameter for the ADMM algorithm; set to 1 for default
 * @param obj_tol              stopping criteria tolerance; set to 1e-10 for default
 * @param DktDk                pointer to the inner product of DktDk
 * @param verbose              0/1 flag for printing progress
 * @return void
 * @see tf_admm
 */
void tf_admm_gauss (double * y, double * x, double * w, int n, int k,
       int max_iter, double lam,
       double * beta, double * alpha, double * u,
       double * obj, int * iter,
       double rho, double obj_tol, cs * DktDk, int verbose)
{
  int i;
  int it;
  double *v;
  double *z;
  double *db;
  double loss;
  double pen;

  cs * kernmat;
  gqr * kernmat_qr;

  /* Special case for k=0: skip the ADMM algorithm */
  if (k==0)
  {
    /* Use Nick's DP algorithm, weighted version */
    tf_dp_weight(n,y,w,lam,beta);

    db = (double *) malloc(n*sizeof(double));

    /* Compute objective */
    loss = 0; pen = 0;
    for (i=0; i<n; i++) loss += w[i]*(y[i]-beta[i])*(y[i]-beta[i]);
    loss = loss/2;
    tf_dx(x,n,k+1,beta,db); /* IMPORTANT: use k+1 here! */
    for (i=0; i<n-k-1; i++) pen += fabs(db[i]);
    obj[0] = loss+lam*pen;

    free(db);
    return;
  }

  /* Otherwise we run our ADMM routine */

  /* Construct the kernel matrix and its QR decomposition */
  kernmat = scalar_plus_diag(DktDk, rho, w);
  kernmat_qr = glmgen_qr(kernmat);

  /* Other variables that will be useful during our iterations */
  v = (double*) malloc(n*sizeof(double));
  z = (double*) malloc(n*sizeof(double));

  if (verbose) printf("\nlambda=%0.3e\n",lam);
  if (verbose) printf("Iteration\tObjective\tLoss\tPenalty\n");

  for(it=0; it < max_iter; it++)
  {
    /* Update beta: banded linear system (kernel matrix) */
    for (i=0; i < n-k; i++) v[i] = alpha[i] + u[i];
    tf_dtxtil(x,n,k,v,z);
    for (i=0; i<n; i++) beta[i] = w[i]*y[i] + rho*z[i];
    /* Solve the least squares problem with sparse QR */
    glmgen_qrsol(kernmat_qr, beta);

    /* Update alpha: 1d fused lasso
     * Build the response vector */
    tf_dxtil(x,n,k,beta,v);
    for (i=0; i<n-k; i++)
    {
      z[i] = v[i]-u[i];
    }
    /* Use Nick's DP algorithm */
    tf_dp(n-k,z,lam/rho,alpha);

    /* Update u: dual update */
    for (i=0; i<n-k; i++)
    {
      u[i] = u[i]+alpha[i]-v[i];
    }

    /* Compute loss */
    loss = 0;
    for (i=0; i<n; i++) loss += w[i]*(y[i]-beta[i])*(y[i]-beta[i]);
    loss = loss/2;
    /* Compute penalty */
    tf_dx(x,n,k+1,beta,z); /* IMPORTANT: use k+1 here! */
    pen = 0;
    for (i=0; i<n-k-1; i++) pen += fabs(z[i]);
    obj[it] = loss+lam*pen;

    if (verbose) printf("%i\t%0.3e\t%0.3e\t%0.3e\n",it+1,obj[it],loss,lam*pen);

    /* Stop if relative difference of objective values <= obj_tol */
    if(it > 0)
    {
      if( fabs(obj[it] - obj[it-1]) < fabs(obj[it]) * obj_tol ) break;
    }
  }

  *iter = it;

  cs_spfree(kernmat);
  glmgen_gqr_free(kernmat_qr);
  free(v);
  free(z);
}
Пример #2
0
/**
 * @brief Low level fitting routine for a Gaussian trend filtering problem.
 * Function used by tf_admm to fit a Gaussian ADMM trendfilter, or as a
 * subproblem by tf_admm_glm when using logistic or poisson losses. Fits
 * the solution for a single value of lambda. Most users will want to call
 * tf_admm, rather than tf_admm_gauss directly.
 *
 * @param x                    a vector of data locations; must be in increasing order
 * @param y                    a vector of responses
 * @param w                    a vector of sample weights
 * @param n                    the length of x, y, and w
 * @param k                    polynomial degree of the fitted trend; i.e., k=1 for linear
 * @param max_iter             maximum number of ADMM interations; ignored for k=0
 * @param lam                  the value of lambda
 * @param df                   allocated space for df value at the solution
 * @param beta                 allocated space for output coefficents; must pre-fill as it is used in warm start
 * @param alpha                allocated space for ADMM alpha variable; must pre-fill as it is used in warm start
 * @param u                    allocated space for ADMM u variable; must pre-fill as it is used in warm start
 * @param obj                  allocated space to store the objective; will fill at most max_iter elements
 * @param iter                 allocated space to store the number of iterations; will fill just one element
 * @param rho                  tuning parameter for the ADMM algorithm; set to 1 for default
 * @param obj_tol              stopping criteria tolerance; set to 1e-10 for default
 * @param DktDk                pointer to the inner product of DktDk
 * @param verbose              0/1 flag for printing progress
 * @return void
 * @see tf_admm
 */
void tf_admm_gauss (double * x, double * y, double * w, int n, int k,
    int max_iter, double lam, int * df,
    double * beta, double * alpha, double * u,
    double * obj, int * iter,
    double rho, double obj_tol, cs * DktDk, int verbose)
{
  int i;
  int d;
  int it, itbest;
  double *v;
  double *z;
  double *betabest;
  double *alphabest;
  double descent;
  double variation;

  cs * kernmat;
  gqr * kernmat_qr;

  /* Special case for k=0: skip the ADMM algorithm */
  if (k==0)
  {
    /* Use Nick's DP algorithm, weighted version */
    tf_dp_weight(n,y,w,lam,beta);

    /* Compute df value */
    d = 1;
    for (i=0; i<n-1; i++) if (beta[i] != beta[i+1]) d += 1;
    *df = d;

    /* Compute objective */
    v = (double *) malloc(n*sizeof(double));
    obj[0] = tf_obj_gauss(x,y,w,n,k,lam,beta,v);
    free(v);
    return;
  }

  /* Otherwise we run our ADMM routine */

  /* Construct the kernel matrix and its QR decomposition */
  kernmat = scalar_plus_diag(DktDk, rho, w);
  kernmat_qr = glmgen_qr(kernmat);

  /* Other variables that will be useful during our iterations */
  v = (double*) malloc(n*sizeof(double));
  z = (double*) malloc(n*sizeof(double));
  betabest = (double*) malloc(n*sizeof(double));
  alphabest = (double*) malloc(n*sizeof(double));
  
  if (verbose) printf("\nlambda=%0.3e\n",lam);
  if (verbose) printf("Iteration\tObjective\n");

  itbest = 0;
  obj[0] = tf_obj_gauss(x,y,w,n,k,lam,beta,v);
  memcpy(betabest, beta, n * sizeof(double));
  memcpy(alphabest, alpha, n * sizeof(double));
  
  for (it=0; it < max_iter; it++)
  {
    /* Update beta: banded linear system (kernel matrix) */
    for (i=0; i < n-k; i++) v[i] = alpha[i] + u[i];
    tf_dtxtil(x,n,k,v,z);
    for (i=0; i<n; i++) beta[i] = w[i]*y[i] + rho*z[i];
    /* Solve the least squares problem with sparse QR */
    glmgen_qrsol(kernmat_qr, beta);

    /* Update alpha: 1d fused lasso
     * Build the response vector */
    tf_dxtil(x,n,k,beta,v);
    for (i=0; i<n-k; i++) z[i] = v[i]-u[i];

    /* Use Nick's DP algorithm */
    tf_dp(n-k,z,lam/rho,alpha);

    /* Update u: dual update */
    for (i=0; i<n-k; i++) u[i] = u[i]+alpha[i]-v[i];

    /* Compute objective */
    obj[it+1] = tf_obj_gauss(x,y,w,n,k,lam,beta,z);
    if (verbose) printf("%i\t%0.3e\n",it+1,obj[it]);

    /* Stop if relative difference of objective values < obj_tol */
    descent = obj[itbest] - obj[it+1];
    
    if ( descent > 0 ) 
    {
      memcpy(betabest, beta, n * sizeof(double));
      memcpy(alphabest, alpha, n * sizeof(double));
      itbest = it+1;
    }
    if (it >= 10)
    {
      variation = 0;
      for (i=0; i < 10; i++ )
        variation += fabs(obj[it+1-i] - obj[it-i]);
      
      //variation = fabs(obj[it+1] - obj[it]) + fabs(obj[it] - obj[it-1]) + fabs(obj[it-1] - obj[it-2]);
      if (variation < fabs(obj[itbest]) * 10 * obj_tol)
        break;
    }
  }
  
  memcpy(beta, betabest, n * sizeof(double));
  memcpy(alpha, alphabest, n * sizeof(double));  

  *iter = it;
  
  if (verbose)
    printf("itbest = %d it = %d obj[0]= %f  obj.best = %f\n", 
           itbest, it, obj[0], obj[itbest]);

  /* Compute final df value, based on alpha */
  d = k+1;
  for (i=0; i<n-k-1; i++) if (alpha[i] != alpha[i+1]) d += 1;
  *df = d;

  cs_spfree(kernmat);
  glmgen_gqr_free(kernmat_qr);
  free(v);
  free(z);
  free(betabest);
  free(alphabest);  
}