HDIM  1.0.0
Packages for High Dimensional Linear Regression
coordinatedescentwithscreen.hpp
1 #ifndef COORDINATEDESCENTWITHSCREEN_HPP
2 #define COORDINATEDESCENTWITHSCREEN_HPP
3 
4 
5 // C System-Headers
6 //
7 // C++ System headers
8 #include <vector>
9 #include <limits>
10 #include <iomanip>
11 #include <iostream>
12 #include <type_traits>
13 #include <algorithm>
14 // Eigen Headers
15 #include <eigen3/Eigen/Dense>
16 // Boost Headers
17 //
18 // FISTA Headers
19 //
20 // Project Specific Headers
21 #include "../../Generic/debug.hpp"
22 #include "../../Generic/generics.hpp"
23 #include "../../Screening/screening_rules.hpp"
24 #include "../solver.hpp"
25 
26 namespace hdim {
27 
28 template < typename T >
30 
31  public:
32  CoordinateDescentWithScreen( const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
33  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
34  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_0 );
36 
37  Eigen::Matrix< T, Eigen::Dynamic, 1 > operator()(const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
38  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
39  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_0,
40  const T lambda,
41  const T duality_gap_target );
42 
43  Eigen::Matrix< T, Eigen::Dynamic, 1 > operator()(
44  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
45  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
46  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_0,
47  const T lambda,
48  const unsigned int num_iterations );
49 
50  private:
51  std::vector<T> inverse_norms;
52 
53 };
54 
55 template < typename T >
57  const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> &X,
58  const Eigen::Matrix<T, Eigen::Dynamic, 1> &Y,
59  const Eigen::Matrix<T, Eigen::Dynamic, 1> &Beta_0 ) {
60 
61  (void)Y;
62 
63  inverse_norms.reserve( Beta_0.size() );
64 
65  for( int i = 0; i < Beta_0.size() ; i++ ) {
66 
67  T X_i_norm = X.col( i ).squaredNorm();
68 
69  T inverse_norm = ( X_i_norm == 0 )?( 0.0 ):( static_cast<T>(1)/X_i_norm );
70  inverse_norms.push_back( inverse_norm );
71 
72  }
73 }
74 
75 template < typename T >
77 
78 template < typename T >
79 Eigen::Matrix< T, Eigen::Dynamic, 1 > CoordinateDescentWithScreen<T>::operator () (
80  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
81  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
82  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_0,
83  const T lambda,
84  const unsigned int num_iterations ) {
85 
86  const T lambda_half = lambda / 2.0;
87 
88  Eigen::Matrix< T, Eigen::Dynamic, 1 > Beta = Beta_0;
89  Eigen::Matrix< T, Eigen::Dynamic, 1 > Residual = Y - X*Beta_0;
90 
91  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > X_A = X;
92  Eigen::Matrix< T, Eigen::Dynamic, 1 > Beta_A = Beta;
93 
94 
95  std::vector< unsigned int > active_set, inactive_set;
96 
97  // Initialize vector of values [ 0, 1, ..., p - 1, p ]
98  std::vector< unsigned int > universe ( X.cols() );
99  std::iota ( std::begin(universe), std::end(universe) , 0 );
100 
101  T duality_gap_2 = static_cast<T>( 0 );
102 
103  for( unsigned int j = 0; j < num_iterations ; j ++ ) {
104 
105  Eigen::Matrix< T, Eigen::Dynamic, 1 > nu = DualPoint( X_A, Y, Beta_A, lambda_half );
106  duality_gap_2 = DualityGap2( X_A, Y, Beta_A, nu, lambda_half );
107 
108  if( j % 10 == 0 ) {
109 
110  T radius = std::sqrt( 2.0 * duality_gap_2 / square( lambda ) );
111  active_set = SafeActiveSet( X, nu, radius );
112 
113  X_A = slice( X, active_set );
114  Beta_A = slice( Beta, active_set );
115 
116  std::set_difference( universe.begin(),
117  universe.end(),
118  active_set.begin(),
119  active_set.end(),
120  std::inserter(inactive_set, inactive_set.begin()) );
121  }
122 
123  for( const auto& active_index : active_set ) {
124 
125  Eigen::Matrix< T, Eigen::Dynamic, 1 > X_j = X.col( active_index );
126 
127  if( Beta( active_index ) != static_cast<T>(0) ) {
128  Residual = Residual + X_j*Beta( active_index );
129  }
130 
131  T inverse_norm_j = inverse_norms[ active_index ];
132 
133  T threshold = lambda_half * inverse_norm_j;
134  T elem = inverse_norm_j*X_j.transpose()*Residual;
135 
136  Beta( active_index ) = soft_threshold<T>( elem, threshold );
137 
138  if( Beta( active_index ) != static_cast<T>(0) ) {
139  Residual = Residual - X_j*Beta( active_index );
140  }
141 
142  }
143 
144  for( const auto& inactive_index : inactive_set ) {
145  Beta( inactive_index ) = 0.0;
146  }
147 
148  DEBUG_PRINT( "Norm Squared of updated Beta: " << Beta.squaredNorm() );
149 
150  }
151 
152  return Beta;
153 }
154 
155 template < typename T >
156 Eigen::Matrix< T, Eigen::Dynamic, 1 > CoordinateDescentWithScreen<T>::operator () (
157  const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic >& X,
158  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Y,
159  const Eigen::Matrix< T, Eigen::Dynamic, 1 >& Beta_0,
160  const T lambda,
161  const T duality_gap_target ) {
162 
163  const T lambda_half = lambda / 2.0;
164  const T dgt_half = duality_gap_target / 2.0;
165 
166  Eigen::Matrix< T, Eigen::Dynamic, 1 > Beta = Beta_0;
167  Eigen::Matrix< T, Eigen::Dynamic, 1 > Residual = Y - X*Beta_0;
168 
169  Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > X_A = X;
170  Eigen::Matrix< T, Eigen::Dynamic, 1 > Beta_A = Beta;
171 
172 
173  std::vector< unsigned int > active_set, inactive_set;
174 
175  // Initialize vector of values [ 0, 1, ..., p - 1, p ]
176  std::vector< unsigned int > universe ( X.cols() );
177  std::iota ( std::begin(universe), std::end(universe) , 0 );
178 
179  T duality_gap_2 = static_cast<T>( 0 );
180 
181  unsigned int counter = 0;
182 
183  do {
184 
185  Eigen::Matrix< T, Eigen::Dynamic, 1 > nu = DualPoint( X_A, Y, Beta_A, lambda_half );
186  duality_gap_2 = DualityGap2( X_A, Y, Beta_A, nu, lambda_half );
187 
188  if( counter % 10 == 0 ) {
189 
190  T radius = std::sqrt( 2.0 * duality_gap_2 / square( lambda ) );
191  active_set = SafeActiveSet( X, nu, radius );
192 
193  X_A = slice( X, active_set );
194  Beta_A = slice( Beta, active_set );
195 
196  std::set_difference( universe.begin(),
197  universe.end(),
198  active_set.begin(),
199  active_set.end(),
200  std::inserter(inactive_set, inactive_set.begin()) );
201  }
202 
203  for( const auto& active_index : active_set ) {
204 
205  Eigen::Matrix< T, Eigen::Dynamic, 1 > X_j = X.col( active_index );
206 
207  if( Beta( active_index ) != static_cast<T>(0) ) {
208  Residual = Residual + X_j*Beta( active_index );
209  }
210 
211  T inverse_norm_j = inverse_norms[ active_index ];
212 
213  T threshold = lambda_half * inverse_norm_j;
214  T elem = inverse_norm_j*X_j.transpose()*Residual;
215 
216  Beta( active_index ) = soft_threshold<T>( elem, threshold );
217 
218  if( Beta( active_index ) != static_cast<T>(0) ) {
219  Residual = Residual - X_j*Beta( active_index );
220  }
221 
222  }
223 
224  for( const auto& inactive_index : inactive_set ) {
225  Beta( inactive_index ) = 0.0;
226  }
227 
228  DEBUG_PRINT( "Current Duality Gap: " << duality_gap_2 << " Current Target: " << dgt_half );
229  DEBUG_PRINT( "Norm Squared of updated Beta: " << Beta.squaredNorm() );
230 
231  counter ++;
232 
233  } while ( duality_gap_2 > dgt_half );
234 
235  return Beta;
236 }
237 
238 }
239 
240 #endif // COORDINATEDESCENTWITHSCREEN_HPP
Eigen::Matrix< T, Eigen::Dynamic, 1 > operator()(const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &X, const Eigen::Matrix< T, Eigen::Dynamic, 1 > &Y, const Eigen::Matrix< T, Eigen::Dynamic, 1 > &Beta_0, const T lambda, const T duality_gap_target)
Run the Sub-Gradient Descent algorithm until the duality gap is below the threshold specified by dual...
Definition: fos.hpp:18
Abstract base class for solvers that do not make use of GAP SAFE screening rules. ...
Definition: solver.hpp:31
#define DEBUG_PRINT(x,...)
Definition: debug.hpp:73
T square(T &val)
Compute the square of a value.
Definition: generics.hpp:169