slope 6.5.4
Loading...
Searching...
No Matches
hybrid.h
Go to the documentation of this file.
1
7#pragma once
8
9#include "../clusters.h"
10#include "../losses/loss.h"
11#include "../sorted_l1_norm.h"
12#include "hybrid_cd.h"
13#include "pgd.h"
14#include "solver.h"
15#include <memory>
16#include <optional>
17
18namespace slope {
19
36class Hybrid : public SolverBase
37{
38public:
51 bool intercept,
52 bool update_clusters,
53 int cd_iterations,
54 const std::string& cd_type,
55 std::optional<int> random_seed = std::nullopt)
57 , update_clusters(update_clusters)
58 , cd_iterations(cd_iterations)
59 , cd_type(cd_type)
60 , rng(random_seed.has_value() ? std::mt19937(*random_seed)
61 : std::mt19937(std::random_device{}()))
62 {
63 }
64
66 void run(Eigen::VectorXd& beta0,
67 Eigen::VectorXd& beta,
68 Eigen::MatrixXd& eta,
69 const Eigen::ArrayXd& lambda,
70 const std::unique_ptr<Loss>& loss,
71 const SortedL1Norm& penalty,
72 const Eigen::VectorXd& gradient,
73 const std::vector<int>& working_set,
74 const Eigen::MatrixXd& x,
75 const Eigen::VectorXd& x_centers,
76 const Eigen::VectorXd& x_scales,
77 const Eigen::MatrixXd& y) override;
78
80 void run(Eigen::VectorXd& beta0,
81 Eigen::VectorXd& beta,
82 Eigen::MatrixXd& eta,
83 const Eigen::ArrayXd& lambda,
84 const std::unique_ptr<Loss>& loss,
85 const SortedL1Norm& penalty,
86 const Eigen::VectorXd& gradient,
87 const std::vector<int>& working_set,
88 const Eigen::SparseMatrix<double>& x,
89 const Eigen::VectorXd& x_centers,
90 const Eigen::VectorXd& x_scales,
91 const Eigen::MatrixXd& y) override;
92
94 void run(Eigen::VectorXd& beta0,
95 Eigen::VectorXd& beta,
96 Eigen::MatrixXd& eta,
97 const Eigen::ArrayXd& lambda,
98 const std::unique_ptr<Loss>& loss,
99 const SortedL1Norm& penalty,
100 const Eigen::VectorXd& gradient,
101 const std::vector<int>& working_set,
102 const Eigen::Map<Eigen::MatrixXd>& x,
103 const Eigen::VectorXd& x_centers,
104 const Eigen::VectorXd& x_scales,
105 const Eigen::MatrixXd& y) override;
106
108 void run(Eigen::VectorXd& beta0,
109 Eigen::VectorXd& beta,
110 Eigen::MatrixXd& eta,
111 const Eigen::ArrayXd& lambda,
112 const std::unique_ptr<Loss>& loss,
113 const SortedL1Norm& penalty,
114 const Eigen::VectorXd& gradient,
115 const std::vector<int>& working_set,
116 const Eigen::Map<Eigen::SparseMatrix<double>>& x,
117 const Eigen::VectorXd& x_centers,
118 const Eigen::VectorXd& x_scales,
119 const Eigen::MatrixXd& y) override;
120
121private:
136 template<typename MatrixType>
137 void runImpl(Eigen::VectorXd& beta0,
138 Eigen::VectorXd& beta,
139 Eigen::MatrixXd& eta,
140 const Eigen::ArrayXd& lambda,
141 const std::unique_ptr<Loss>& loss,
142 const SortedL1Norm& penalty,
143 const Eigen::VectorXd& gradient_in,
144 const std::vector<int>& working_set,
145 const MatrixType& x,
146 const Eigen::VectorXd& x_centers,
147 const Eigen::VectorXd& x_scales,
148 const Eigen::MatrixXd& y)
149 {
150 using Eigen::MatrixXd;
151 using Eigen::VectorXd;
152
153 const int n = x.rows();
154 const int m = eta.cols();
155
156 PGD pgd_solver(jit_normalization, intercept, "pgd");
157
158 // Run proximal gradient descent
159 pgd_solver.run(beta0,
160 beta,
161 eta,
162 lambda,
163 loss,
164 penalty,
165 gradient_in,
166 working_set,
167 x,
168 x_centers,
169 x_scales,
170 y);
171
172 Clusters clusters(beta);
173
174 // TODO: Make these parameters and initialize once
175 MatrixXd w = MatrixXd::Ones(n, m);
176 MatrixXd z = y;
177
178 loss->updateWeightsAndWorkingResponse(w, z, eta, y);
179
180 MatrixXd residual = eta - z;
181 const VectorXd weight_sums = w.colwise().sum().transpose();
182
183 Eigen::ArrayXd lambda_cumsum(lambda.size() + 1);
184 lambda_cumsum(0) = 0.0;
185 std::partial_sum(lambda.begin(), lambda.end(), lambda_cumsum.begin() + 1);
186
187 for (int it = 0; it < this->cd_iterations; ++it) {
188 double old_obj =
189 computeObjective(penalty, beta, residual, w, lambda, working_set);
190
191 // Store old values to revert if no progress is made
192 Clusters old_clusters = clusters;
193 Eigen::MatrixXd old_residual = residual;
194 Eigen::VectorXd old_beta = beta;
195 Eigen::VectorXd old_beta0 = beta0;
196
197 coordinateDescent(beta0,
198 beta,
199 residual,
200 clusters,
201 lambda_cumsum,
202 x,
203 w,
204 weight_sums,
205 x_centers,
206 x_scales,
207 this->intercept,
208 this->jit_normalization,
209 this->update_clusters,
210 rng,
211 this->cd_type);
212
213 double new_obj =
214 computeObjective(penalty, beta, residual, w, lambda, working_set);
215
216 if (!std::isfinite(new_obj) || new_obj > old_obj) {
217 // No progress, revert to previous state
218 clusters = old_clusters;
219 residual = old_residual;
220 beta = old_beta;
221 beta0 = old_beta0;
222
223 break;
224 }
225 }
226
227 // The residual is kept up to date, but not eta. So we need to compute
228 // it here.
229 eta = residual + z;
230 // TODO: register convergence status
231 }
232
233 double computeObjective(const SortedL1Norm& penalty,
234 const Eigen::VectorXd& beta,
235 const Eigen::MatrixXd& residual,
236 const Eigen::MatrixXd& w,
237 const Eigen::ArrayXd& lambda,
238 const std::vector<int>& working_set)
239 {
240 double val =
241 0.5 * (residual.array().square() * w.array()).sum() / residual.rows() +
242 penalty.eval(beta(working_set), lambda.head(working_set.size()));
243
244 return val;
245 }
246
247 // TODO: These should be used in the PGD solver and taken as arguments to the
248 // Hybrid solver and not just set and ignored here.
249 double pgd_learning_rate =
250 1.0;
251 double pgd_learning_rate_decr =
252 0.5;
253
254 bool update_clusters = false;
255 int cd_iterations = 10;
256 std::string cd_type =
257 "cyclical";
258 std::mt19937 rng{
259 std::random_device{}()
260 };
261};
262
263} // namespace slope
Representation of the nonzero clusters in SLOPE.
Definition clusters.h:23
Hybrid CD-PGD solver for SLOPE.
Definition hybrid.h:37
void run(Eigen::VectorXd &beta0, Eigen::VectorXd &beta, Eigen::MatrixXd &eta, const Eigen::ArrayXd &lambda, const std::unique_ptr< Loss > &loss, const SortedL1Norm &penalty, const Eigen::VectorXd &gradient, const std::vector< int > &working_set, const Eigen::SparseMatrix< double > &x, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const Eigen::MatrixXd &y) override
Pure virtual function defining the solver's optimization routine.
void run(Eigen::VectorXd &beta0, Eigen::VectorXd &beta, Eigen::MatrixXd &eta, const Eigen::ArrayXd &lambda, const std::unique_ptr< Loss > &loss, const SortedL1Norm &penalty, const Eigen::VectorXd &gradient, const std::vector< int > &working_set, const Eigen::MatrixXd &x, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const Eigen::MatrixXd &y) override
Pure virtual function defining the solver's optimization routine.
void run(Eigen::VectorXd &beta0, Eigen::VectorXd &beta, Eigen::MatrixXd &eta, const Eigen::ArrayXd &lambda, const std::unique_ptr< Loss > &loss, const SortedL1Norm &penalty, const Eigen::VectorXd &gradient, const std::vector< int > &working_set, const Eigen::Map< Eigen::SparseMatrix< double > > &x, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const Eigen::MatrixXd &y) override
Pure virtual function defining the solver's optimization routine.
Hybrid(JitNormalization jit_normalization, bool intercept, bool update_clusters, int cd_iterations, const std::string &cd_type, std::optional< int > random_seed=std::nullopt)
Constructs Hybrid solver for SLOPE optimization.
Definition hybrid.h:50
void run(Eigen::VectorXd &beta0, Eigen::VectorXd &beta, Eigen::MatrixXd &eta, const Eigen::ArrayXd &lambda, const std::unique_ptr< Loss > &loss, const SortedL1Norm &penalty, const Eigen::VectorXd &gradient, const std::vector< int > &working_set, const Eigen::Map< Eigen::MatrixXd > &x, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const Eigen::MatrixXd &y) override
Pure virtual function defining the solver's optimization routine.
Proximal Gradient Descent solver for SLOPE optimization.
Definition pgd.h:29
Abstract base class for SLOPE optimization solvers.
Definition solver.h:30
JitNormalization jit_normalization
JIT feature normalization strategy.
Definition solver.h:165
bool intercept
If true, fits intercept term.
Definition solver.h:166
Class representing the Sorted L1 Norm.
An implementation of the coordinate descent step in the hybrid algorithm for solving SLOPE.
Namespace containing SLOPE regression implementation.
Definition clusters.h:11
double coordinateDescent(Eigen::VectorXd &beta0, Eigen::VectorXd &beta, Eigen::MatrixXd &residual, Clusters &clusters, const Eigen::ArrayXd &lambda_cumsum, const T &x, const Eigen::MatrixXd &w, const Eigen::VectorXd &weight_sums, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const bool intercept, const JitNormalization jit_normalization, const bool update_clusters, std::mt19937 &rng, const std::string &cd_type="cyclical")
Definition hybrid_cd.h:546
JitNormalization
Enums to control predictor standardization behavior.
Proximal Gradient Descent solver implementation for SLOPE.
Numerical solver class for SLOPE (Sorted L-One Penalized Estimation)