HDIM  1.0.0
Packages for High Dimensional Linear Regression
x_fos.hpp
1 #ifndef X_FOS_H
2 #define X_FOS_H
3 
4 // C System-Headers
5 //
6 // C++ System headers
7 #include <limits>
8 #include <iomanip>
9 #include <iostream>
10 #include <type_traits>
11 #include <algorithm>
12 #include <memory>
13 // Eigen Headers
14 #include <eigen3/Eigen/Dense>
15 // Boost Headers
16 //
17 // FISTA Headers
18 //
19 // Project Specific Headers
20 #include "../Generic/generics.hpp"
21 #include "../Solvers/abstractsolver.hpp"
22 #include "../Solvers/solver.hpp"
23 #include "../Solvers/screeningsolver.hpp"
24 #include "../Solvers/SubGradientDescent/ISTA/ista.hpp"
25 #include "../Solvers/SubGradientDescent/FISTA/fista.hpp"
26 #include "../Solvers/CoordinateDescent/coordinate_descent.hpp"
27 
28 namespace hdim {
29 
30 enum class SolverType { ista, screen_ista, fista, screen_fista, cd, screen_cd };
31 
32 template < typename T >
36 class X_FOS {
37 
38  public:
39 
40  X_FOS();
41  ~X_FOS();
42 
50  void operator()( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& x,
51  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& y,
52  SolverType s_type = SolverType::ista );
53 
54  T ReturnLambda();
55  T ReturnIntercept();
56  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > ReturnBetas();
57  unsigned int ReturnOptimIndex();
58  Eigen::Matrix< T, Eigen::Dynamic, 1 > ReturnCoefficients();
59  Eigen::Matrix< int, Eigen::Dynamic, 1 > ReturnSupport();
60 
61  protected:
62 
63  Eigen::Matrix< T, Eigen::Dynamic, 1 > fos_fit;
64  T lambda;
65  unsigned int optim_index;
66  T intercept = 0;
67 
68  private:
69 
70  Eigen::Matrix< T, Eigen::Dynamic, 1 > X_weights( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X );
71 
72  T Y_weight( const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y );
73 
74  Eigen::Matrix< T, Eigen::Dynamic, 1 > RescaleCoefficients( const Eigen::Matrix< T, Eigen::Dynamic, 1 >& raw_coefs,
75  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& x_weights,
76  T y_weight);
77 
78  T compute_intercept(const Eigen::Matrix< T, Eigen::Dynamic, 1 >& x,
79  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& y,
80  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& fit );
81 
82  std::vector< T > GenerateLambdaGrid (
83  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
84  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
85  unsigned int M );
86 
87  bool ComputeStatsCond(T C,
88  unsigned int stats_it,
89  T r_stats_it,
90  const std::vector<T> &lambdas,
91  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& Betas );
92 
93  T duality_gap_target( T gamma, T C, T r_stats_it, unsigned int n );
94 
95  T primal_objective( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
96  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
97  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta,
98  T r_stats_it );
99 
100  T dual_objective( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
101  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
102  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta,
103  T r_stats_it );
104 
105  Eigen::Matrix< int, Eigen::Dynamic, 1 > ApplyScreening( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& x,
106  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& nu_tilde,
107  const T duality_gap,
108  const T lambda );
109 
110  void choose_solver( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& x,
111  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& y,
112  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& beta,
113  SolverType s_type );
114 
115  std::unique_ptr< internal::AbstractSolver<T> > solver;
116 
117  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > Betas;
118  Eigen::Matrix< T, Eigen::Dynamic, 1 > x_std_devs;
119 
120  T y_std_dev = 0;
121 
122  const T C = 0.75;
123  const unsigned int M = 100;
124  const T gamma = 1;
125 
126  unsigned int loop_index = 0;
127 
128  unsigned int statsIt = 1;
129 
130  const T L_0 = 0.1;
131  int n = 1, p = 1;
132 
133 };
134 
135 template< typename T >
146  static_assert(std::is_floating_point< T >::value, "X_FOS can only be used with floating point types.");
147 }
148 
149 template < typename T >
150 X_FOS<T>::~X_FOS() {}
151 
152 template < typename T >
154  return lambda;
155 }
156 
157 template < typename T >
158 Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > X_FOS< T >::ReturnBetas() {
159  return Betas;
160 }
161 
162 template < typename T >
163 unsigned int X_FOS< T >::ReturnOptimIndex() {
164  return optim_index;
165 }
166 
167 template < typename T >
168 Eigen::Matrix< T, Eigen::Dynamic, 1 > X_FOS< T >::ReturnCoefficients() {
169  return RescaleCoefficients(fos_fit, x_std_devs, y_std_dev );
170 }
171 
172 template < typename T >
173 Eigen::Matrix<int, Eigen::Dynamic, 1> X_FOS<T>::ReturnSupport() {
174 
175  T n_t = static_cast<T>( n );
176  T cut_off = static_cast<T>( 6 )*C*lambda/n_t;
177  DEBUG_PRINT( "Cut-off for Support Computation: " << cut_off );
178 
179  return GenerateSupport( fos_fit, cut_off );
180 // return fos_fit.unaryExpr( SupportSift<T>( C_t, lambda, n_t ) );
181 
182 }
183 
184 template < typename T >
185 T X_FOS<T>::compute_intercept( const Eigen::Matrix< T, Eigen::Dynamic, 1 >& x,
186  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& y,
187  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& fit ) {
188 
189  Eigen::Matrix< T, Eigen::Dynamic, 1 > scaled_beta = RescaleCoefficients( fit, x_std_devs, y_std_dev );
190 
191  T intercept_part = 0.0;
192 
193  for( unsigned int i = 0; i < x.cols() ; i++ ) {
194 
195  T X_i_bar = x.col( i ).mean();
196  intercept_part += scaled_beta( i )*X_i_bar;
197 
198  }
199 
200  return static_cast<T>( y.mean() ) - static_cast<T>( intercept_part );
201 }
202 
203 template < typename T >
205  return intercept;
206 }
207 
208 //Member functions
209 
210 template < typename T >
220  unsigned int stats_it,
221  T r_stats_it,
222  const std::vector <T>& lambdas,
223  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& Betas ) {
224 
225  bool stats_cond = true;
226 
227  for ( unsigned int i = 1; i <= stats_it; i++ ) {
228 
229  Eigen::Matrix< T, Eigen::Dynamic, 1 > beta_k = Betas.col( i - 1 );
230  T rk = lambdas.at( i - 1 );
231 
232  Eigen::Matrix< T, Eigen::Dynamic, 1 > beta_diff = Betas.col( stats_it - 1 ) - beta_k;
233  T abs_max_betas = beta_diff.template lpNorm< Eigen::Infinity >();
234 
235  T n_t = static_cast<T>( n );
236  T check_parameter = n_t*abs_max_betas / ( r_stats_it + rk );
237 
238  stats_cond &= ( check_parameter <= C );
239  }
240 
241  return stats_cond;
242 
243 }
244 
245 template < typename T >
250 std::vector< T > X_FOS<T>::GenerateLambdaGrid (
251  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
252  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
253  unsigned int M ) {
254 
255  T rMax = 2.0*( X.transpose() * Y ).template lpNorm< Eigen::Infinity >();
256  T rMin = 0.001*rMax;
257 
258  return LogScaleVector( rMax, rMin, M );
259 
260 }
261 
262 template < typename T >
263 T X_FOS< T >::primal_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  Eigen::Matrix< T, Eigen::Dynamic, 1 > error = Y - X*Beta;
269  T f_beta = error.squaredNorm() + r_stats_it*Beta.template lpNorm < 1 >();
270 
271  return f_beta;
272 
273 }
274 
275 template < typename T >
276 T X_FOS< T >::dual_objective ( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X, \
277  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y, \
278  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta, \
279  T r_stats_it ) {
280 
281  //Computation of s
282  T s_chunk = r_stats_it / ( 2.0*X.transpose()*( X*Beta - Y ) ).template lpNorm< Eigen::Infinity >();
283  T s_chunk_prime = ( - static_cast<T>( Y.transpose()*( X*Beta - Y ) ) )/( Y - X*Beta ).squaredNorm();
284  T s = std::min( std::max( - s_chunk, s_chunk_prime ), s_chunk );
285 
286  //Computation of nu tilde
287  Eigen::Matrix< T, Eigen::Dynamic, 1 > nu_tilde = 2.0*s/r_stats_it*( X*Beta - Y );
288 
289  T d_nu = square( r_stats_it )/4.0*( nu_tilde + 2.0/r_stats_it*Y ).squaredNorm() - Y.squaredNorm();
290 
291  return d_nu;
292 }
293 
294 template < typename T >
295 T X_FOS<T>::duality_gap_target( T gamma, T C, T r_stats_it, unsigned int n ) {
296 
297  T n_f = static_cast<T>( n );
298  return gamma*square( C )*square( r_stats_it )/n_f;
299 
300 }
301 
302 template < typename T >
303 Eigen::Matrix< T, Eigen::Dynamic, 1 > X_FOS< T >::X_weights( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X ) {
304 
305  Eigen::Matrix< T, Eigen::Dynamic, 1 > weights( X.cols() );
306 
307  for( unsigned int i = 0; i < X.cols() ; i ++ ) {
308  Eigen::Matrix< T, Eigen::Dynamic, 1 > X_i = X.col( i );
309  weights( i ) = StdDev( X_i );
310  }
311 
312  return weights;
313 }
314 
315 template < typename T >
316 T X_FOS< T >::Y_weight( const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y ) {
317  return StdDev( Y );
318 }
319 
320 template < typename T >
321 Eigen::Matrix< T, Eigen::Dynamic, 1 > X_FOS< T >::RescaleCoefficients(
322  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& raw_coefs,
323  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& x_weights,
324  T y_weight ) {
325 
326  Eigen::Matrix< T, Eigen::Dynamic, 1 > scaled_coefs( raw_coefs.size() );
327 
328  for( unsigned int i = 0; i < raw_coefs.size() ; i++ ) {
329 
330  T weight = ( x_weights(i) == 0.0 )?( 0.0 ):( y_weight / x_weights(i) );
331  scaled_coefs( i ) = weight*raw_coefs( i );
332 
333  }
334 
335  return scaled_coefs;
336 
337 }
338 
339 template < typename T >
340 Eigen::Matrix< int, Eigen::Dynamic, 1 > X_FOS< T >::ApplyScreening( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& x,
341  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& nu_tilde,
342  const T duality_gap,
343  const T lambda ) {
344 
345  Eigen::Matrix< int, Eigen::Dynamic, 1 > A( x.cols() );
346  unsigned int counter = 0;
347 
348  for( unsigned int j = 0; j < x.cols(); j ++ ) {
349 
350  Eigen::Matrix< T, Eigen::Dynamic, 1 > X_j = x.col( j );
351 
352  T radius = std::abs( static_cast<T>( X_j.transpose() * nu_tilde ) ) + std::sqrt( 2.0 / square( lambda ) * duality_gap )*X_j.norm();
353  A[j] = ( radius > 1.0 );
354 
355  if ( radius > 1.0 ) {
356  counter ++;
357  }
358 
359  }
360 
361  DEBUG_PRINT( "Number of active variables: " << counter );
362 
363  return A;
364 
365 }
366 
367 template < typename T >
368 void X_FOS< T >::choose_solver( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& x,
369  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& y,
370  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& beta,
371  SolverType s_type ) {
372 
373  solver.reset();
374 
375  switch( s_type ) {
376  case SolverType::ista:
377  solver = std::unique_ptr< ISTA<T,internal::Solver<T>> >( new ISTA<T,internal::Solver<T>>() );
378  break;
379  case SolverType::screen_ista:
380  solver = std::unique_ptr< ISTA<T,internal::ScreeningSolver<T>> >( new ISTA<T,internal::ScreeningSolver<T>>() );
381  break;
382  case SolverType::fista:
383  solver = std::unique_ptr< FISTA<T,internal::Solver<T>> >( new FISTA<T,internal::Solver<T>>(beta) );
384  break;
385  case SolverType::screen_fista:
386  solver = std::unique_ptr< FISTA<T,internal::ScreeningSolver<T>> >( new FISTA<T,internal::ScreeningSolver<T>>(beta) );
387  break;
388  case SolverType::cd:
389  solver = std::unique_ptr< LazyCoordinateDescent<T,internal::Solver<T>> >( new LazyCoordinateDescent<T,internal::Solver<T>>( x, y, Betas.col( 0 ) ) );
390  break;
391  case SolverType::screen_cd:
392  solver = std::unique_ptr< LazyCoordinateDescent<T,internal::ScreeningSolver<T>> >( new LazyCoordinateDescent<T,internal::ScreeningSolver<T>>( x, y, Betas.col( 0 ) ) );
393  break;
394  }
395 
396 }
397 
398 template < typename T >
399 void X_FOS< T >::operator()( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& x,
400  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& y,
401  SolverType s_type ) {
402 
403  Eigen::Matrix< T, Eigen::Dynamic, 1 > old_Betas;
404 
405  bool statsCont = true;
406 
407  x_std_devs = X_weights( x );
408  y_std_dev = Y_weight( y );
409 
410  Eigen::Matrix< T , Eigen::Dynamic, Eigen::Dynamic > X = Normalize( x );
411  Eigen::Matrix< T , Eigen::Dynamic, 1 > Y = Normalize( y );
412 
413  n = X.rows();
414  p = X.cols();
415 
416  std::vector<T> lambda_grid = GenerateLambdaGrid( X, Y, M );
417 
418  Betas = Eigen::Matrix< T , Eigen::Dynamic, Eigen::Dynamic >::Zero( X.cols(), M );
419 
420  choose_solver( X, Y, Betas.col( 0 ), s_type );
421 
422  //Outer Loop
423  while( statsCont && ( statsIt < M ) ) {
424 
425  statsIt ++;
426 
427  DEBUG_PRINT( "Outer loop #: " << statsIt );
428 
429  old_Betas = Betas.col( statsIt - 2 );
430  T rStatsIt = lambda_grid.at( statsIt - 1 );
431 
432  T gap_target = duality_gap_target( gamma, C, rStatsIt, n );
433 
434  DEBUG_PRINT( "Current Lambda: " << rStatsIt );
435  Betas.col( statsIt - 1 ) = solver->operator()( X, Y, old_Betas, rStatsIt, gap_target );
436 
437  statsCont = ComputeStatsCond( C, statsIt, rStatsIt, lambda_grid, Betas );
438  }
439 
440  fos_fit = Betas.col( statsIt - 2 );
441  lambda = lambda_grid.at( statsIt - 2 );
442  optim_index = statsIt;
443 // intercept = compute_intercept( x, y, fos_fit );
444 
445  // Computation of Intercept -- function call causes exception when compilied with -DDEBUG flag
446  // No idea why this is happening -- does not seem to be an issue when compilied using release parameters
447 
448  Eigen::Matrix< T, Eigen::Dynamic, 1 > scaled_beta = RescaleCoefficients( fos_fit, x_std_devs, y_std_dev );
449 
450  T intercept_part = 0.0;
451  for( unsigned int i = 0; i < x.cols() ; i++ ) {
452  T X_i_bar = x.col( i ).mean();
453  intercept_part += scaled_beta( i )*X_i_bar;
454  }
455 
456  intercept = y.mean() - intercept_part;
457 
458 }
459 
460 }
461 
462 #endif // X_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
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
#define DEBUG_PRINT(x,...)
Definition: debug.hpp:73
T square(T &val)
Compute the square of a value.
Definition: generics.hpp:169
Run the Fast Iterative Shrinking and Thresholding Algorthim.
Definition: fista.hpp:26
Run the Iterative Shrinking and Thresholding Algorthim.
Definition: ista.hpp:33
void operator()(const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &x, const Eigen::Matrix< T, Eigen::Dynamic, 1 > &y, SolverType s_type=SolverType::ista)
Run the main X_FOS algorithm.
Definition: x_fos.hpp:399
The FOS algorithim.
Definition: x_fos.hpp:36
X_FOS()
Initialize a new algorithm, and instantiate member attributes X and Y.
Definition: x_fos.hpp:145