HDIM  1.0.0
Packages for High Dimensional Linear Regression
fos.hpp
1 #ifndef FOS_H
2 #define FOS_H
3 
4 // C System-Headers
5 //
6 // C++ System headers
7 #include <vector>
8 // Eigen Headers
9 #include <eigen3/Eigen/Dense>
10 // Boost Headers
11 //
12 // FISTA Headers
13 //
14 // Project Specific Headers
15 #include "../Generic/debug.hpp"
16 #include "../Generic/generics.hpp"
17 
18 namespace hdim {
19 
20 template < typename T >
24 class FOS {
25 
26 // using Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > = Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >;
27 // using Eigen::Matrix< T, Eigen::Dynamic, 1 > = Eigen::Matrix< T, Eigen::Dynamic, 1 >;
28 
29  public:
30  FOS( Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > x, Eigen::Matrix< T, Eigen::Dynamic, 1 > y );
31  void Algorithm();
32 
33  T ReturnLambda();
34  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > ReturnBetas();
35  unsigned int ReturnOptimIndex();
36  Eigen::Matrix< T, Eigen::Dynamic, 1 > ReturnCoefficients();
37  Eigen::Matrix< T, Eigen::Dynamic, 1 > ReturnSupport();
38 
39  protected:
40  Eigen::Matrix< T, Eigen::Dynamic, 1 > avfos_fit;
41  T lambda;
42  unsigned int optim_index;
43 
44  private:
45  std::vector< T > GenerateLambdaGrid(const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> &X,
46  const Eigen::Matrix<T, Eigen::Dynamic, 1 > &Y,
47  unsigned int M);
48 
49 
50  bool ComputeStatsCond(
51  T C,
52  unsigned int stats_it,
53  T r_stats_it,
54  const std::vector<T>& lambdas,
55  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
56  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& Betas );
57 
58  T duality_gap_target( T gamma, T C, T r_stats_it, unsigned int n );
59  T dual_objective ( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X, \
60  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y, \
61  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta, \
62  T r_stats_it );
63  T primal_objective( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X, \
64  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y, \
65  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta, \
66  T r_stats_it );
67 
68  T f_beta (
69  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
70  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
71  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta );
72 
73  T f_beta_tilda (
74  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
75  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
76  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta,
77  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_prime,
78  T L );
79 
80  Eigen::Matrix< T, Eigen::Dynamic, 1 > update_beta_ista (
81  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
82  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
83  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta,
84  T L,
85  T thres );
86 
87  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > ISTA (
88  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X, \
89  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y, \
90  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_0, \
91  unsigned int num_iterations, \
92  T L_0, \
93  T lambda );
94 
95  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > Betas;
96  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > old_Betas;
97 
98  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > X;
99  Eigen::Matrix< T, Eigen::Dynamic, 1 > Y;
100 
101  const T C = 0.75;
102  const unsigned int M = 100;
103  const T gamma = 1;
104 
105  T rMax;
106  T rMin;
107 
108  T L_k_less_1 = 0.1;
109  unsigned int statsIt = 1;
110 
111  unsigned int n = 0, p = 0;
112 
113  std::vector< T > lambda_grid;
114 
115  unsigned int loop_index = 0;
116 
117 };
118 
119 template< typename T >
129 FOS< T >::FOS(Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> x, Eigen::Matrix<T, Eigen::Dynamic, 1 > y ) : X( x ), Y( y ) {
130  static_assert(std::is_floating_point< T >::value, "FOS can only be used with floating point types.");
131 }
132 
133 template < typename T >
135  return lambda;
136 }
137 
138 template < typename T >
139 Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > FOS< T >::ReturnBetas() {
140  return Betas;
141 }
142 
143 template < typename T >
144 unsigned int FOS< T >::ReturnOptimIndex() {
145  return optim_index;
146 }
147 
148 template < typename T >
149 Eigen::Matrix< T, Eigen::Dynamic, 1 > FOS< T >::ReturnCoefficients() {
150  return avfos_fit;
151 }
152 
153 template < typename T >
154 Eigen::Matrix<T, Eigen::Dynamic, 1> FOS<T>::ReturnSupport() {
155 
156  T C_t = static_cast<T>( C );
157  T n_t = static_cast<T>( X.rows() );
158 
159  return avfos_fit.unaryExpr( SupportSift<T>( C_t, lambda, n_t ) );
160 
161 }
162 
163 //Free functions
164 
165 template < typename T >
166 T compute_lp_norm( T& matrix, int norm_type ) {
167  return matrix.template lpNorm< norm_type >();
168 }
169 
170 template < typename T >
171 T compute_sqr_norm( T& matrix ) {
172  return matrix.squaredNorm();
173 }
174 
175 //Member functions
176 
177 template < typename T >
186 bool FOS<T>::ComputeStatsCond(T C,
187  unsigned int stats_it,
188  T r_stats_it,
189  const std::vector<T> &lambdas,
190  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
191  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& Betas ) {
192 
193  bool stats_cond = true;
194 
195  for ( unsigned int i = 1; i <= stats_it; i++ ) {
196 
197  Eigen::Matrix< T, Eigen::Dynamic, 1 > beta_k = Betas.col( i - 1 );
198  T rk = lambdas.at( i - 1 );
199 
200  Eigen::Matrix< T, Eigen::Dynamic, 1 > beta_diff = Betas.col( stats_it - 1 );
201  beta_diff -= beta_k;
202  T abs_max_betas = beta_diff.cwiseAbs().maxCoeff();
203 
204  T n = static_cast<T>( X.rows() );
205 
206  T check_parameter = n*abs_max_betas / ( r_stats_it + rk );
207 
208  stats_cond &= ( check_parameter <= C );
209  }
210 
211  return stats_cond;
212 
213 }
214 
215 template < typename T >
220 std::vector< T > FOS<T>::GenerateLambdaGrid( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
221  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
222  unsigned int M ) {
223 
224  T rMax = 2.0*( X.transpose() * Y ).template lpNorm< Eigen::Infinity >();
225  T rMin = 0.001*rMax;
226 
227  return LogScaleVector( rMax, rMin, M );
228 
229 }
230 
231 template < typename T >
242 T FOS< T >::duality_gap_target( T gamma, T C, T r_stats_it, unsigned int n ) {
243 
244  T n_f = static_cast<T>( n );
245  return gamma*square( C )*square( r_stats_it )/n_f;
246 
247 }
248 
249 template < typename T >
250 T FOS< T >::primal_objective( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X, \
251  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y, \
252  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta, \
253  T r_stats_it ) {
254 
255  Eigen::Matrix< T, Eigen::Dynamic, 1 > error = Y - X*Beta;
256  T f_beta = error.squaredNorm() + r_stats_it*Beta.template lpNorm < 1 >();
257 
258  return f_beta;
259 
260 }
261 
262 template < typename T >
263 T FOS< T >::dual_objective ( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X, \
264  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y, \
265  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta, \
266  T r_stats_it ) {
267 
268  //Computation of s
269  T s_chunk = r_stats_it / ( 2.0*X.transpose()*( X*Beta - Y ) ).template lpNorm< Eigen::Infinity >();
270  T s_chunk_prime = ( - static_cast<T>( Y.transpose()*( X*Beta - Y ) ) )/( Y - X*Beta ).squaredNorm();
271  T s = std::min( std::max( - s_chunk, s_chunk_prime ), s_chunk );
272 
273  //Computation of nu tilde
274  Eigen::Matrix< T, Eigen::Dynamic, 1 > nu_tilde = 2.0*s/r_stats_it*( X*Beta - Y );
275 
276  T d_nu = square( r_stats_it )/4.0*( nu_tilde + 2.0/r_stats_it*Y ).squaredNorm() - Y.squaredNorm();
277 
278  return d_nu;
279 }
280 
281 template < typename T >
282 T FOS<T>::f_beta (
283  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
284  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
285  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta ) {
286 
287  return (X*Beta - Y).squaredNorm();
288 
289 }
290 
291 template < typename T >
293  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
294  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
295  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta,
296  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_prime,
297  T L ) {
298 
299  Eigen::Matrix< T, Eigen::Dynamic, 1 > f_beta = X*Beta_prime - Y;
300  T taylor_term_0 = f_beta.squaredNorm();
301 
302  Eigen::Matrix< T, Eigen::Dynamic, 1 > f_grad = 2.0*X.transpose()*( f_beta );
303  Eigen::Matrix< T, Eigen::Dynamic, 1 > beta_diff = ( Beta - Beta_prime );
304 
305  T taylor_term_1 = f_grad.transpose()*beta_diff;
306 
307  T taylor_term_2 = L/2.0*beta_diff.squaredNorm();
308 
309  return taylor_term_0 + taylor_term_1 + taylor_term_2;
310 }
311 
312 template < typename T >
313 Eigen::Matrix< T, Eigen::Dynamic, 1 > FOS<T>::update_beta_ista (
314  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
315  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
316  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta,
317  T L,
318  T thres ) {
319 
320  Eigen::Matrix< T, Eigen::Dynamic, 1 > f_grad = 2.0*( X.transpose()*( X*Beta - Y ) );
321  Eigen::Matrix< T, Eigen::Dynamic, 1 > beta_to_modify = Beta - (1.0/L)*f_grad;
322 
323  return beta_to_modify.unaryExpr( SoftThres<T>( thres/L ) );
324 
325 }
326 
327 template < typename T >
328 Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > FOS<T>::ISTA (
329  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X, \
330  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y, \
331  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_0, \
332  unsigned int num_iterations, \
333  T L_0, \
334  T lambda ) {
335 
336  T eta = 1.5;
337  T L = L_0;
338 
339  Eigen::Matrix< T, Eigen::Dynamic, 1 > Beta = Beta_0;
340 
341  for( unsigned int i = 0; i < num_iterations; i++ ) {
342 
343  unsigned int counter = 0;
344 
345  Eigen::Matrix< T, Eigen::Dynamic, 1 > Beta_temp = update_beta_ista( X, Y, Beta, L, lambda );
346 
347  counter++;
348  DEBUG_PRINT( "Backtrace iteration: " << counter );
349 
350  while( ( f_beta( X, Y, Beta_temp ) > f_beta_tilda( X, Y, Beta_temp, Beta, L ) ) ) {
351 
352  counter++;
353  DEBUG_PRINT( "Backtrace iteration: " << counter );
354  L*= eta;
355  Beta_temp = update_beta_ista( X, Y, Beta, L, lambda );
356 
357  }
358 
359  Beta = update_beta_ista( X, Y, Beta, L, lambda );
360 
361  }
362 
363  L_k_less_1 = L;
364 
365  return Beta;
366 
367 }
368 
369 template< typename T >
378 
379  X = Normalize( X );
380  Y = Normalize( Y );
381 
382  lambda_grid = GenerateLambdaGrid( X, Y, M );
383 
384  bool statsCont = true;
385  unsigned int statsIt = 1;
386 
387  Betas = Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >::Zero( X.cols(), M );
388 
389 
390  //Outer Loop
391  while( statsCont && ( statsIt < M ) ) {
392 
393  statsIt ++;
394 
395  DEBUG_PRINT( "Outer loop #: " << statsIt );
396 
397  old_Betas = Betas.col( statsIt - 2 );
398  T rStatsIt = lambda_grid.at( statsIt - 1 );
399 
400  //Inner Loop
401  while( true ) {
402 
403  loop_index ++;
404  DEBUG_PRINT( "Inner loop #: " << loop_index );
405 
406  T duality_gap = primal_objective( X, Y, old_Betas, rStatsIt ) + dual_objective( X, Y, old_Betas, rStatsIt );
407 
408 
409  unsigned int n = static_cast< unsigned int >( X.rows() );
410  T gap_target = duality_gap_target( gamma, C, rStatsIt, n );
411 
412  DEBUG_PRINT( "Duality gap is " << duality_gap << " gap target is " << gap_target );
413 
414  //Criteria meet, exit loop
415  if( duality_gap <= gap_target ) {
416 
417  DEBUG_PRINT( "Duality gap is below specified threshold, exiting inner loop." );
418  Betas.col( statsIt - 1 ) = old_Betas;
419  loop_index = 0;
420 
421  L_k_less_1 = 0.1;
422  break;
423 
424  } else {
425 
426  Betas.col( statsIt - 1 ) = ISTA( X, Y, old_Betas, 1, L_k_less_1, rStatsIt );
427  old_Betas = Betas.col( statsIt - 1 );
428 
429  }
430 
431  }
432 
433  statsCont = ComputeStatsCond( C, statsIt, rStatsIt, lambda_grid, X, Betas );
434  }
435 
436  avfos_fit = Betas.col( statsIt - 2 );
437  lambda = lambda_grid.at( statsIt - 2 );
438  optim_index = statsIt;
439 
440  std::cout << "Stopping Index: " << optim_index << std::endl;
441 
442 }
443 
444 }
445 
446 #endif // FOS_H
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > Normalize(const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &mat)
Set the mean of a matrix to 0 and the standard deviation to 1.
Definition: generics.hpp:67
void Algorithm()
Run the main FOS algorithm.
Definition: fos.hpp:377
Definition: fos.hpp:18
std::vector< T > LogScaleVector(T lower_bound, T upper_bound, unsigned int num_elements)
Generate a vector of logarithmically equally spaced points.
Definition: generics.hpp:198
Soft Threshold functor used to apply hdim::soft_threshold to each element in a matrix or vector...
Definition: generics.hpp:288
#define DEBUG_PRINT(x,...)
Definition: debug.hpp:73
T square(T &val)
Compute the square of a value.
Definition: generics.hpp:169
Run the Iterative Shrinking and Thresholding Algorthim.
Definition: ista.hpp:33
FOS(Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > x, Eigen::Matrix< T, Eigen::Dynamic, 1 > y)
Initialize a new algorithm, and instantiate member attributes X and Y.
Definition: fos.hpp:129
The main FOS algorithim.
Definition: fos.hpp:24