slope 0.31.0
Loading...
Searching...
No Matches
slope.h
Go to the documentation of this file.
1
6#pragma once
7
8#include "logger.h"
9#include "screening.h"
10#include "slope_fit.h"
11#include "slope_path.h"
12#include "solvers/hybrid_cd.h"
13#include "timer.h"
14#include <Eigen/Core>
15#include <Eigen/SparseCore>
16#include <cassert>
17#include <optional>
18
22namespace slope {
23
32class Slope
33{
34public:
40 Slope() = default;
41
50 void setSolver(const std::string& solver);
51
57 void setIntercept(bool intercept);
58
66 void setNormalization(const std::string& type);
67
74 void setUpdateClusters(bool update_clusters);
75
82 void setReturnClusters(const bool return_clusters);
83
91 void setAlphaMinRatio(double alpha_min_ratio);
92
107 void setAlphaType(const std::string& alpha_type);
108
115 void setLearningRateDecr(double learning_rate_decr);
116
123 void setQ(double q);
124
131 void setOscarParameters(const double theta1, const double theta2);
132
138 void setTol(double tol);
139
145 void setRelaxTol(double tol);
146
154 void setRelaxMaxOuterIterations(int max_it);
155
162 void setRelaxMaxInnerIterations(int max_it);
163
171 void setMaxIterations(int max_it);
172
178 void setPathLength(int path_length);
179
187 void setHybridCdIterations(int cd_iterations);
188
196 void setLambdaType(const std::string& lambda_type);
197
208 void setLoss(const std::string& loss_type);
209
219 void setScreening(const std::string& screening_type);
220
228 void setModifyX(const bool modify_x);
229
234 void setDevChangeTol(const double dev_change_tol);
235
241 void setDevRatioTol(const double dev_ratio_tol);
242
250 void setMaxClusters(const int max_clusters);
251
256 void setCentering(const std::string& type);
257
263 void setCentering(const Eigen::VectorXd& x_centers);
264
269 void setScaling(const std::string& type);
270
277 void setDiagnostics(const bool collect_diagnostics);
278
284 void setScaling(const Eigen::VectorXd& x_scales);
285
296 void setAlphaEstimationMaxIterations(const int alpha_est_maxit);
297
303
307 bool getFitIntercept() const;
308
313 const std::string& getLossType();
314
331 template<typename T>
332 SlopePath path(T& x,
333 const Eigen::MatrixXd& y_in,
334 Eigen::ArrayXd alpha = Eigen::ArrayXd::Zero(0),
335 Eigen::ArrayXd lambda = Eigen::ArrayXd::Zero(0));
336
352 template<typename T>
353 SlopeFit fit(T& x,
354 const Eigen::MatrixXd& y_in,
355 const double alpha = 1.0,
356 Eigen::ArrayXd lambda = Eigen::ArrayXd::Zero(0));
357
372 template<typename T>
374 T& x,
375 const Eigen::VectorXd& y_in,
376 const double gamma = 0.0,
377 Eigen::VectorXd beta0 = Eigen::VectorXd(0),
378 Eigen::VectorXd beta = Eigen::VectorXd(0))
379 {
380 using Eigen::MatrixXd;
381 using Eigen::VectorXd;
382
383 int n = x.rows();
384 int p = x.cols();
385
386 if (beta0.size() == 0) {
387 beta0 = fit.getIntercepts(false);
388 }
389
390 if (beta.size() == 0) {
391 beta = fit.getCoefs(false);
392 }
393
394 double alpha = 0;
395
396 Timer timer;
397
398 std::vector<double> primals, duals, time;
399 timer.start();
400
401 auto jit_normalization =
402 normalize(x, x_centers, x_scales, centering_type, scaling_type, modify_x);
403
404 bool update_clusters = false;
405
406 std::unique_ptr<Loss> loss = setupLoss(this->loss_type);
407
408 MatrixXd y = loss->preprocessResponse(y_in);
409
410 int m = y.cols();
411
412 Eigen::ArrayXd lambda_relax = Eigen::ArrayXd::Zero(p * m);
413
414 auto working_set = activeSet(beta);
415
416 Eigen::MatrixXd eta = linearPredictor(x,
417 working_set,
418 beta0,
419 beta,
420 x_centers,
421 x_scales,
422 jit_normalization,
423 intercept);
424 VectorXd gradient = VectorXd::Zero(p * m);
425 VectorXd residual(n);
426 VectorXd working_residual(n);
427
428 VectorXd w = VectorXd::Ones(n);
429 VectorXd w_ones = VectorXd::Ones(n);
430 VectorXd z = y;
431
432 Clusters clusters = fit.getClusters();
433
434 int passes = 0;
435
436 for (int irls_it = 0; irls_it < max_it_outer_relax; irls_it++) {
437 residual = loss->residual(eta, y);
438
439 if (collect_diagnostics) {
440 primals.push_back(loss->loss(eta, y));
441 duals.push_back(0.0);
442 time.push_back(timer.elapsed());
443 }
444
445 Eigen::VectorXd cluster_gradient = clusterGradient(beta,
446 residual,
447 clusters,
448 x,
449 w_ones,
450 x_centers,
451 x_scales,
452 jit_normalization);
453
454 double norm_grad = cluster_gradient.lpNorm<Eigen::Infinity>();
455
456 if (norm_grad < tol_relax) {
457 break;
458 }
459
460 loss->updateWeightsAndWorkingResponse(w, z, eta, y);
461 working_residual = eta - z;
462
463 for (int inner_it = 0; inner_it < max_it_inner_relax; ++inner_it) {
464 passes++;
465
466 double max_abs_gradient = coordinateDescent(beta0,
467 beta,
468 working_residual,
469 clusters,
470 lambda_relax,
471 x,
472 w,
473 x_centers,
474 x_scales,
475 intercept,
476 jit_normalization,
477 update_clusters);
478
479 if (max_abs_gradient < tol_relax) {
480 break;
481 }
482 }
483
484 eta = working_residual + z;
485
486 if (irls_it == max_it_outer_relax) {
488 "Maximum number of IRLS iterations reached.");
489 }
490 }
491
492 double dev = loss->deviance(eta, y);
493
494 if (gamma > 0) {
495 Eigen::VectorXd old_coefs = fit.getCoefs(false);
496 Eigen::VectorXd old_intercept = fit.getIntercepts(false);
497 beta = (1 - gamma) * beta + gamma * old_coefs;
498 }
499
500 SlopeFit fit_out{ beta0,
501 beta.reshaped(p, m).sparseView(),
502 clusters,
503 alpha,
504 lambda_relax,
505 dev,
507 primals,
508 duals,
509 time,
510 passes,
511 centering_type,
512 scaling_type,
513 intercept,
514 x_centers,
515 x_scales };
516
517 return fit_out;
518 }
519
532 template<typename T>
534 T& x,
535 const Eigen::VectorXd& y,
536 const double gamma = 0.0)
537 {
538 std::vector<SlopeFit> fits;
539
540 Eigen::VectorXd beta0 = path(0).getIntercepts(false);
541 Eigen::VectorXd beta = path(0).getCoefs(false);
542
543 for (size_t i = 0; i < path.size(); i++) {
544 auto relaxed_fit = relax(path(i), x, y, gamma, beta0, beta);
545
546 fits.emplace_back(relaxed_fit);
547
548 // Update warm starts
549 // TODO: Maybe be more clever about whether to use the
550 // previous values or the regularized estimates and warm starts.
551 // Maybe just pick the solution with larger coefficients?
552 beta0 = relaxed_fit.getIntercepts(false);
553 beta = relaxed_fit.getCoefs(false);
554 }
555
556 return fits;
557 }
558
559private:
560 // Parameters
561 bool collect_diagnostics = false;
562 bool intercept = true;
563 bool modify_x = false;
564 bool return_clusters = true;
565 bool update_clusters = false;
566 double alpha_min_ratio = -1;
567 double dev_change_tol = 1e-5;
568 double dev_ratio_tol = 0.999;
569 double learning_rate_decr = 0.5;
570 double q = 0.1;
571 double theta1 = 1.0;
572 double theta2 = 0.5;
573 double tol = 1e-4;
574 double tol_relax = 1e-4;
575 int alpha_est_maxit = 1000;
576 int cd_iterations = 10;
577 int max_it = 1e4;
578 int max_it_inner_relax = 1e5;
579 int max_it_outer_relax = 50;
580 int path_length = 100;
581 std::optional<int> max_clusters = std::nullopt;
582 std::string alpha_type = "path";
583 std::string centering_type = "mean";
584 std::string lambda_type = "bh";
585 std::string loss_type = "quadratic";
586 std::string scaling_type = "sd";
587 std::string screening_type = "strong";
588 std::string solver_type = "auto";
589
590 // Data
591 Eigen::VectorXd x_centers;
592 Eigen::VectorXd x_scales;
593};
594
595} // namespace slope
Representation of the clusters in SLOPE.
Definition clusters.h:18
A class representing the results of SLOPE (Sorted L1 Penalized Estimation) fitting.
Definition slope_fit.h:27
const Clusters & getClusters() const
Gets the clusters.
Definition slope_fit.h:151
Eigen::VectorXd getIntercepts(const bool original_scale=true) const
Gets the intercept terms for this SLOPE fit.
Definition slope_fit.h:113
Eigen::SparseMatrix< double > getCoefs(const bool original_scale=true) const
Gets the sparse coefficient matrix for this fit.
Definition slope_fit.h:133
double getNullDeviance() const
Gets the null model deviance.
Definition slope_fit.h:176
Container class for SLOPE regression solution paths.
Definition slope_path.h:31
std::vector< Eigen::VectorXd > getIntercepts(const bool original_scale=true) const
Returns the vector of intercept terms for each solution in the path.
Definition slope_path.h:74
std::size_t size() const
Gets the number of solutions in the path.
Definition slope_path.h:255
std::vector< Eigen::SparseMatrix< double > > getCoefs(const bool original_scale=true) const
Returns the vector of coefficient matrices for each solution in the path.
Definition slope_path.h:96
The SLOPE model.
Definition slope.h:33
void setSolver(const std::string &solver)
Sets the numerical solver used to fit the model.
Definition slope.cpp:341
void setAlphaMinRatio(double alpha_min_ratio)
Sets the alpha min ratio.
Definition slope.cpp:423
void setMaxIterations(int max_it)
Sets the maximum number of iterations.
Definition slope.cpp:502
void setAlphaEstimationMaxIterations(const int alpha_est_maxit)
Sets the maximum number of iterations for the alpha estimation procedure.
Definition slope.cpp:596
void setRelaxMaxInnerIterations(int max_it)
Sets the maximum number of inner iterations for the relaxed solver.
Definition slope.cpp:493
SlopePath path(T &x, const Eigen::MatrixXd &y_in, Eigen::ArrayXd alpha=Eigen::ArrayXd::Zero(0), Eigen::ArrayXd lambda=Eigen::ArrayXd::Zero(0))
Computes SLOPE regression solution path for multiple alpha and lambda values.
Definition slope.cpp:27
void setDevRatioTol(const double dev_ratio_tol)
Sets tolerance in deviance change for early stopping.
Definition slope.cpp:570
void setScaling(const std::string &type)
Sets the scaling type.
Definition slope.cpp:389
SlopeFit fit(T &x, const Eigen::MatrixXd &y_in, const double alpha=1.0, Eigen::ArrayXd lambda=Eigen::ArrayXd::Zero(0))
Fits a single SLOPE regression model for given alpha and lambda values.
Definition slope.cpp:328
void setDevChangeTol(const double dev_change_tol)
Sets tolerance in deviance change for early stopping.
Definition slope.cpp:560
const std::string & getLossType()
Get currently defined loss type.
Definition slope.cpp:614
void setReturnClusters(const bool return_clusters)
Sets the return clusters flag.
Definition slope.cpp:410
void setRelaxMaxOuterIterations(int max_it)
Sets the maximum number of outer (IRLS) iterations for the relaxed solver.
Definition slope.cpp:484
void setMaxClusters(const int max_clusters)
Sets the maximum number of clusters.
Definition slope.cpp:580
void setIntercept(bool intercept)
Sets the intercept flag.
Definition slope.cpp:348
bool getFitIntercept() const
Returns the intercept flag.
Definition slope.cpp:608
void setDiagnostics(const bool collect_diagnostics)
Toggles collection of diagnostics.
Definition slope.cpp:590
void setNormalization(const std::string &type)
Sets normalization type for the design matrix.
Definition slope.cpp:354
SlopeFit relax(const SlopeFit &fit, T &x, const Eigen::VectorXd &y_in, const double gamma=0.0, Eigen::VectorXd beta0=Eigen::VectorXd(0), Eigen::VectorXd beta=Eigen::VectorXd(0))
Relaxes a fitted SLOPE model.
Definition slope.h:373
int getAlphaEstimationMaxIterations() const
Gets the maximum number of iterations allowed for the alpha estimation procedure.
Definition slope.cpp:602
void setScreening(const std::string &screening_type)
Sets the type of feature screening used, which discards predictors that are unlikely to be active.
Definition slope.cpp:547
void setOscarParameters(const double theta1, const double theta2)
Sets OSCAR parameters.
Definition slope.cpp:450
void setLambdaType(const std::string &lambda_type)
Sets the lambda type for regularization weights.
Definition slope.cpp:529
void setAlphaType(const std::string &alpha_type)
Sets the alpha type.
Definition slope.cpp:416
void setModifyX(const bool modify_x)
Controls if x should be modified-in-place.
Definition slope.cpp:554
void setCentering(const std::string &type)
Sets the center points for feature normalization.
Definition slope.cpp:375
Slope()=default
void setLearningRateDecr(double learning_rate_decr)
Sets the learning rate decrement.
Definition slope.cpp:432
void setLoss(const std::string &loss_type)
Sets the loss function type.
Definition slope.cpp:538
SlopePath relax(const SlopePath &path, T &x, const Eigen::VectorXd &y, const double gamma=0.0)
Relaxes a fitted SLOPE path.
Definition slope.h:533
void setRelaxTol(double tol)
Sets the tolerance value for the relaxed SLOPE solver.
Definition slope.cpp:474
void setPathLength(int path_length)
Sets the path length.
Definition slope.cpp:511
void setHybridCdIterations(int cd_iterations)
Sets the frequence of proximal gradient descent steps.
Definition slope.cpp:520
void setTol(double tol)
Sets the tolerance value.
Definition slope.cpp:465
void setQ(double q)
Sets the q value.
Definition slope.cpp:441
void setUpdateClusters(bool update_clusters)
Sets the update clusters flag.
Definition slope.cpp:404
Timer class for measuring elapsed time with high resolution.
Definition timer.h:19
void start()
Starts the timer by recording the current time point.
Definition timer.cpp:6
double elapsed() const
Returns the elapsed time in seconds since start() was called.
Definition timer.cpp:34
static void addWarning(WarningCode code, const std::string &message)
Log a new warning.
Definition logger.cpp:31
An implementation of the coordinate descent step in the hybrid algorithm for solving SLOPE.
Thread-safe warning logging facility for the slope library.
Namespace containing SLOPE regression implementation.
Definition clusters.cpp:5
double coordinateDescent(Eigen::VectorXd &beta0, Eigen::VectorXd &beta, Eigen::VectorXd &residual, Clusters &clusters, const Eigen::ArrayXd &lambda, const T &x, const Eigen::VectorXd &w, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const bool intercept, const JitNormalization jit_normalization, const bool update_clusters)
Definition hybrid_cd.h:188
std::unique_ptr< Loss > setupLoss(const std::string &loss)
Factory function to create the appropriate loss function based on the distribution family.
Eigen::MatrixXd linearPredictor(const T &x, const std::vector< int > &active_set, const Eigen::VectorXd &beta0, const Eigen::VectorXd &beta, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const JitNormalization jit_normalization, const bool intercept)
Definition math.h:146
JitNormalization normalize(Eigen::MatrixXd &x, Eigen::VectorXd &x_centers, Eigen::VectorXd &x_scales, const std::string &centering_type, const std::string &scaling_type, const bool modify_x)
Definition normalize.cpp:6
@ MAXIT_REACHED
Maximum iterations reached without convergence.
std::vector< int > activeSet(const Eigen::VectorXd &beta)
Identifies previously active variables.
Definition screening.cpp:19
Screening rules for SLOPE regression optimization.
SLOPE (Sorted L-One Penalized Estimation) fitting results.
Defines the SlopePath class for storing and accessing SLOPE regression solution paths.
Simple high-resolution timer class for performance measurements.