/* Copyright (C) 1998 Berwin A Turlach */ /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ /* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. */ /* You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include "lasso.h" //#include "fortify.h" #define PRECISION FLT_EPSILON static void lasso_alloc(long n, long m); static void lasso_free(void); static void qr_init(int n); static void qr_incr(void); static void qr_free(void); static void qr_del(int l, int aug); static void qr_add(double *x, int swap); #if defined (S_Plus) static void errmsg(char* string); #else static void errmsg(char* where, char* string); #endif static int QR_CHUNK = 10; static double *xtr=NULL, *btmp=NULL, *qtr=NULL, *rinvt_theta=NULL, *step=NULL, ytyd2=0.0; static int *theta=NULL, *nz_x=NULL, num_nz_x=0; static double *qmat=NULL, *rmat=NULL; static int qr_max_size=0, r_ncol=0, q_nrow=0, q_use_row=0; static char *no_dyn_mem_message="Cannot allocate dynamic memory"; void lasso(double *x, /* input: data */ long *pn, /* input: rows */ long *pm, /* input: columns */ double *pt, /* input: threshold */ double *beta, /* input/ouput: intial/final beta */ double *y, /* input: labels */ double *yhat1, /* output */ double *r, /* output */ double *lagrangian, /* output */ long *psuc, /* output: succeed: 0/-1 */ long *pverb, /* input: verbose: yes/no */ long *pas_sub) /* input: no!? */ { double t = *pt, prec; long n = *pn, m = *pm, verb = *pverb, as_sub = *pas_sub; int not_solved; double *x_elem=NULL, tmp, max_val, b_1norm; int i, j, max_ind; double p_obj, d_obj; int num_iter=0, max_num; double b_1norm_old, mu; double rho_up, rho_low, rho; int to_del; double *q_elem, wtc, wtw; double *r_elem; int k; int add; if( !as_sub) lasso_alloc(n,m); prec = sqrt(PRECISION); if( as_sub ){ b_1norm = 0.0; for(j=0;j prec){ b_1norm += fabs(beta[j]); nz_x[num_nz_x] = j; num_nz_x++; }else beta[j] = 0.0; } } if( b_1norm > t){ if(verb){ mexPrintf("******************************\n"); mexPrintf("Rescaling beta from L1-norm %f to %f\n",b_1norm,t); mexEvalString("drawnow;"); } for(j=0; j max_val ){ max_val = fabs(xtr[j]); max_ind = j; } if( !as_sub ){ qr_add(y,TRUE); ytyd2 = *rmat * *rmat/2.0; for(j=0;j\tAdding variable: %d\n",max_ind+1); mexEvalString("drawnow;"); } qr_add(x+max_ind*n, TRUE); theta[0] = xtr[max_ind] < 0 ? -1 : 1; } *psuc=0; if(verb){ /* Find out how many times [[max_val]] is attained */ tmp = (1.0-prec)*max_val; if( tmp < prec ) tmp = 0.0; max_num = 1; for(j=0; j=0;j--){ tmp = step[j]; for(k=num_nz_x-1;k>j;k--) tmp -= RMAT(j,k) * step[k]; step[j] = tmp / RMAT(j,j); } for(j=0;j (1+prec)*t){ not_solved=TRUE; if(b_1norm_old < (1.0-prec)*t){ if(verb) { mexPrintf(" -->\tStepping onto the border of the L1 ball.\n"); mexEvalString("drawnow;"); } rho_up = rho = 1.0; rho_low = 0.0; while( fabs(t-b_1norm) > prec*t ){ if( b_1norm > t){ rho_up = rho; rho = (rho+rho_low)/2.0; } if( b_1norm < t){ rho_low = rho; rho = (rho+rho_up)/2.0; } if(rho < prec) break; for(j=0; j 0 ? -1 : 1; else theta[j] = beta[nz_x[j]] < 0 ? -1 : 1; } }else{ rho = 1.0; to_del = -1; for(j=0; j prec){ tmp = -btmp[j]/(step[j]); if( 0.0 < tmp && tmp < rho ){ rho = tmp; to_del = j; } } } if(to_del < 0 ){ *psuc= -1; goto EXIT_HERE; } for(j=0; j\tRemoving variable: %d",nz_x[to_del]+1); mexEvalString("drawnow;"); } beta[nz_x[to_del]] = 0.0; qr_del(to_del,TRUE); for(j=to_del+1; j< num_nz_x; j++){ nz_x[j-1] = nz_x[j]; theta[j-1] = theta[j]; } num_nz_x--; b_1norm = 0.0; for(j=0;j max_val ){ max_val = fabs(xtr[j]); max_ind = j; } if(verb){ /* Find out how many times [[max_val]] is attained */ tmp = (1.0-prec)*max_val; if( tmp < prec ) tmp = 0.0; max_num = 1; for(j=0; j\tAdding variable: %d\n",max_ind+1); mexEvalString("drawnow;"); } } EXIT_HERE: if( !as_sub) lasso_free(); } void mult_lasso(double *x, long *pn, long *pm, double *pt, long *pl, double *beta, double *y, double *yhat1, double *r, double *lagrangian, long *psuc, long *pverb) { double prec; long n = *pn, m = *pm, l = *pl, verb = *pverb, as_sub = TRUE, i, j; lasso_alloc(n,m); qr_add(y,TRUE); ytyd2 = *rmat * *rmat/2.0; prec = sqrt(PRECISION); num_nz_x = 0; for(j=0; j prec){ qr_add(x+j*n, TRUE); nz_x[num_nz_x] = j; num_nz_x++; }else beta[j] = 0.0; } *psuc = 0; for(i=0; i0) Memcpy(beta,beta-m,m); lasso(x, pn, pm, pt+i, beta, y, yhat1, r, lagrangian, psuc, pverb, &as_sub); if( *psuc < 0 ){ goto EXIT_HERE; } beta += m; yhat1 += n; r += n; lagrangian++; } EXIT_HERE: lasso_free(); } static void lasso_alloc(long n, long m){ #if defined(S_Plus) if( nz_x != NULL || theta != NULL || xtr != NULL || btmp != NULL || qtr != NULL || rinvt_theta != NULL || step != NULL || num_nz_x != 0 || ytyd2 != 0.0){ MESSAGE "Possible memory corruption or memory leak.\n We" "advise to restart your S+ session" WARNING(NULL_ENTRY); lasso_free(); } #endif QR_CHUNK = m; // Added: CJV nz_x = Calloc(m,int); if( nz_x == NULL ) ERRMSG("lasso_alloc", no_dyn_mem_message); theta = Calloc(m,int); if( theta==NULL ) ERRMSG("lasso_alloc", no_dyn_mem_message); xtr = Calloc(m,double); if( xtr==NULL ) ERRMSG("lasso_alloc", no_dyn_mem_message); btmp = Calloc(m,double); if( btmp==NULL ) ERRMSG("lasso_alloc", no_dyn_mem_message); qtr = Calloc(m,double); if( qtr==NULL ) ERRMSG("lasso_alloc", no_dyn_mem_message); rinvt_theta = Calloc(m,double); if( rinvt_theta==NULL ) ERRMSG("lasso_alloc", no_dyn_mem_message); step = Calloc(m,double); if( step==NULL ) ERRMSG("lasso_alloc", no_dyn_mem_message); qr_init(n); } static void lasso_free(void){ num_nz_x=0; ytyd2 = 0.0; Free(nz_x); Free(theta); Free(xtr); Free(btmp); Free(qtr); Free(rinvt_theta); Free(step); qr_free(); } static void qr_init(int n) { #if defined (S_Plus) if(qr_max_size!=0 || r_ncol!=0 || q_nrow!=0 || q_use_row!=0 || qmat!=NULL || rmat!=NULL){ MESSAGE "Possible memory corruption or memory leak.\n We" "advise to restart your S+ session" WARNING(NULL_ENTRY); qr_free(); } #endif qr_max_size = QR_CHUNK; r_ncol = 0; q_nrow = n; qmat = Calloc(n*qr_max_size,double); if(qmat==NULL) ERRMSG("qr_init", no_dyn_mem_message); rmat = Calloc(qr_max_size*(qr_max_size+1)/2,double); if(rmat==NULL) ERRMSG("qr_init", no_dyn_mem_message); } static void qr_incr(void) { qr_max_size += QR_CHUNK; /* reallocate R always */ rmat = Realloc(rmat,qr_max_size*(qr_max_size+1)/2,double); if(rmat==NULL) ERRMSG("qr_incr", no_dyn_mem_message); /* reallocate Q only if necessary and only to maximal necessary size */ if( qr_max_size >= q_nrow){ if( qr_max_size-QR_CHUNK < q_nrow){ qmat = Realloc(qmat,q_nrow*q_nrow,double); if(qmat==NULL) ERRMSG("qr_incr", no_dyn_mem_message); } }else{ qmat = Realloc(qmat,q_nrow*qr_max_size,double); if(qmat==NULL) ERRMSG("qr_incr", no_dyn_mem_message); } } static void qr_free(void) { qr_max_size = 0; r_ncol = 0; q_nrow = 0; q_use_row = 0; Free(qmat); Free(rmat); } static void qr_del(int l, int aug) { double c, s, tau, nu, *col_k, *col_kp1, *a, *b, tmp; int i, j, k, l0; if( l<0 || l>=r_ncol ) ERRMSG("qr_del", "Invalid column number"); r_ncol--; if( l==r_ncol) if( aug ) ERRMSG("qr_del", "Trying to delete last column of augmented matrix"); else return; /* this is TRUE if $m\le n$ ($m-1\le n$ if the matrix is augmented) */ if (r_ncol < q_nrow ){ for(k=l; k= q_nrow ){ /* Multiply new column with Q-transpose in [[qr_add]] */ q_elem = qmat; for(i=0;i= norm_last/2.0) break; if( norm_new > 0.1*norm_init*PRECISION ) norm_last = norm_new; else{ norm_init = norm_last = 0.1*norm_last*PRECISION; for(i=0;i 0.0 ){ /*Calculate Givens rotation*/ nu = tau*sqrt((*a/tau)*(*a/tau)+(*b/tau)*(*b/tau)); c = *a/nu; s = *b/nu; *a = nu; /* Fix column before last in R */ *b = -s* *(b-1); /* Fix last element of last column in R */ *(b-1) = c * *(b-1); /* Fix second last element of last column in R */ if( *b < 0){ *b = -*b; col_lm1 = QCOL(r_ncol-1); col_l = col_lm1+q_nrow; for(j=0;j 3) { mexErrMsgTxt("Syntax error: too many arguments\n"); return; } /* if */ if (nrhs < 3) { mexErrMsgTxt("Syntax error: arguments missing\n"); return; } /* if */ if (nlhs > 1) { mexErrMsgTxt("Syntax error: too many return arguments\n"); return; } /* if */ arg = 2; array_ptr = (mxArray *) prhs[arg]; if (mxGetClassID(array_ptr) != mxDOUBLE_CLASS) { mexErrMsgTxt("Threshold argument is not a number\n"); return; } /* if */ threshold = mxGetScalar(array_ptr); arg = 1; array_ptr = (mxArray *) prhs[arg]; if (mxGetClassID(array_ptr) != mxDOUBLE_CLASS) { mexErrMsgTxt("Label argument is not a number\n"); return; } /* if */ y = mxGetPr(array_ptr); arg = 0; array_ptr = (mxArray *) prhs[arg]; if (mxGetClassID(array_ptr) != mxDOUBLE_CLASS) { mexErrMsgTxt("Data set argument is not a double matrix\n"); return; } /* if */ dataSize = mxGetM(array_ptr); dimensions = mxGetN(array_ptr); mx = mxGetPr(array_ptr); arg = 0; plhs[arg] = mxCreateDoubleMatrix(dimensions, 1, mxREAL); yhat1 = new double[dataSize]; r = new double[dataSize]; beta = mxGetPr(plhs[arg]); for (i=0; i 0) { lasso(mx, &dataSize, &dimensions, &threshold, beta, y, yhat1, r, &lagrangian, &succeed, &verbose, &as_sub); } else { mexPrintf("Dataset with 0 dimensions\n"); } /* if */ delete [] yhat1; delete [] r; #ifdef FORTIFY Fortify_LeaveScope(); #endif }