slope 6.5.0
Loading...
Searching...
No Matches
math.h
Go to the documentation of this file.
1
6#pragma once
7
8#include "eigen_compat.h"
9#include "clusters.h"
10#include "jit_normalization.h"
11#include "utils.h"
12#include <Eigen/Core>
13#include <Eigen/SparseCore>
14#include <cassert>
15#include <numeric>
16#include <vector>
17
18#ifdef _OPENMP
19#include "threads.h"
20#endif
21
22namespace slope {
23
36template<typename T>
37int
38sign(T val)
39{
40 return (T(0) < val) - (val < T(0));
41}
42
52template<typename T>
53Eigen::ArrayXd
54cumSum(const T& x, const bool leading_zero = false)
55{
56 const size_t start = leading_zero ? 1 : 0;
57 Eigen::ArrayXd out(x.size() + start);
58
59 if (leading_zero) {
60 out(0) = 0.0;
61 }
62
63 std::partial_sum(x.begin(), x.end(), out.begin() + start);
64
65 return out;
66}
67
77template<typename T>
78T
79sigmoid(const T& x)
80{
81 return 1.0 / (1.0 + std::exp(-x));
82}
83
93template<typename T>
94T
95logit(const T& x)
96{
97 assert(x > 0 && x < 1 && "Input must be in (0, 1)");
98
99 return std::log(x) - std::log1p(-x);
100}
101
111template<typename T>
112T
113clamp(const T& x, const T& lo, const T& hi)
114{
115 return x < lo ? lo : x > hi ? hi : x;
116}
117
124Eigen::VectorXd
125logSumExp(const Eigen::MatrixXd& a);
126
135Eigen::MatrixXd
136softmax(const Eigen::MatrixXd& x);
137
152template<typename T>
153Eigen::MatrixXd
155 const std::vector<int>& active_set,
156 const Eigen::VectorXd& beta0,
157 const Eigen::VectorXd& beta,
158 const Eigen::VectorXd& x_centers,
159 const Eigen::VectorXd& x_scales,
160 const JitNormalization jit_normalization,
161 const bool intercept)
162{
163 int n = x.rows();
164 int p = x.cols();
165 int m = beta0.size();
166
167 Eigen::MatrixXd eta = Eigen::MatrixXd::Zero(n, m);
168
169#ifdef _OPENMP
170 bool large_problem = active_set.size() > 100 && n * active_set.size() > 1e7;
171#pragma omp parallel num_threads(Threads::get()) if (large_problem)
172#endif
173 {
174 Eigen::MatrixXd eta_local = Eigen::MatrixXd::Zero(n, m);
175
176#ifdef _OPENMP
177#pragma omp for nowait
178#endif
179 for (int i = 0; i < static_cast<int>(active_set.size()); ++i) {
180 int ind = active_set[i];
181 auto [k, j] = std::div(ind, p);
182
183 switch (jit_normalization) {
185 eta_local.col(k) += x.col(j) * beta(ind) / x_scales(j);
186 eta_local.col(k).array() -= beta(ind) * x_centers(j) / x_scales(j);
187 break;
188
190 eta_local.col(k) += x.col(j) * beta(ind);
191 eta_local.col(k).array() -= beta(ind) * x_centers(j);
192 break;
193
195 eta_local.col(k) += x.col(j) * beta(ind) / x_scales(j);
196 break;
197
199 eta_local.col(k) += x.col(j) * beta(ind);
200 break;
201 }
202 }
203
204#ifdef _OPENMP
205#pragma omp critical
206#endif
207 {
208 eta += eta_local;
209 }
210 }
211
212 if (intercept) {
213 eta.rowwise() += beta0.transpose();
214 }
215
216 return eta;
217}
218
233template<typename T>
234void
235updateGradient(Eigen::VectorXd& gradient,
236 const T& x,
237 const Eigen::MatrixXd& residual,
238 const std::vector<int>& active_set,
239 const Eigen::VectorXd& x_centers,
240 const Eigen::VectorXd& x_scales,
241 const Eigen::VectorXd& w,
242 const JitNormalization jit_normalization)
243{
244 const int n = x.rows();
245 const int p = x.cols();
246 const int m = residual.cols();
247
248 assert(gradient.size() == p * m &&
249 "Gradient matrix has incorrect dimensions");
250
251 Eigen::MatrixXd weighted_residual(n, m);
252 Eigen::ArrayXd wr_sums(m);
253
254#ifdef _OPENMP
255 bool large_problem = active_set.size() > 100 && n * active_set.size() > 1e5;
256#pragma omp parallel for num_threads(Threads::get()) if (large_problem)
257#endif
258 for (int k = 0; k < m; ++k) {
259 weighted_residual.col(k) = residual.col(k).cwiseProduct(w);
260 wr_sums(k) = weighted_residual.col(k).sum();
261 }
262
263#ifdef _OPENMP
264#pragma omp parallel for num_threads(Threads::get()) if (large_problem)
265#endif
266 for (int i = 0; i < static_cast<int>(active_set.size()); ++i) {
267 int ind = active_set[i];
268 auto [k, j] = std::div(ind, p);
269
270 switch (jit_normalization) {
272 gradient(ind) =
273 (x.col(j).dot(weighted_residual.col(k)) - x_centers(j) * wr_sums(k)) /
274 (x_scales(j) * n);
275 break;
277 gradient(ind) =
278 (x.col(j).dot(weighted_residual.col(k)) - x_centers(j) * wr_sums(k)) /
279 n;
280 break;
282 gradient(ind) =
283 x.col(j).dot(weighted_residual.col(k)) / (x_scales(j) * n);
284 break;
286 gradient(ind) = x.col(j).dot(weighted_residual.col(k)) / n;
287 break;
288 }
289 }
290}
291
305template<typename T>
306void
307offsetGradient(Eigen::VectorXd& gradient,
308 const T& x,
309 const Eigen::VectorXd& offset,
310 const std::vector<int>& active_set,
311 const Eigen::VectorXd& x_centers,
312 const Eigen::VectorXd& x_scales,
313 const JitNormalization jit_normalization)
314{
315 const int n = x.rows();
316 const int p = x.cols();
317
318 for (size_t i = 0; i < active_set.size(); ++i) {
319 int ind = active_set[i];
320 auto [k, j] = std::div(ind, p);
321
322 switch (jit_normalization) {
324 gradient(ind) -=
325 offset(k) * (x.col(j).sum() / n - x_centers(j)) / x_scales(j);
326 break;
328 gradient(ind) -= offset(k) * (x.col(j).sum() / n - x_centers(j));
329 break;
331 gradient(ind) -= offset(k) * x.col(j).sum() / (n * x_scales(j));
332 break;
334 gradient(ind) -= offset(k) * x.col(j).sum() / n;
335 break;
336 }
337 }
338}
339
348std::vector<int>
349setUnion(const std::vector<int>& a, const std::vector<int>& b);
350
361std::vector<int>
362setDiff(const std::vector<int>& a, const std::vector<int>& b);
363
375template<typename T>
376int
377whichMax(const T& x)
378{
379 return std::distance(x.begin(), std::max_element(x.begin(), x.end()));
380}
381
393template<typename T>
394int
395whichMin(const T& x)
396{
397 return std::distance(x.begin(), std::min_element(x.begin(), x.end()));
398}
399
413template<typename T, typename Comparator>
414int
415whichBest(const T& x, const Comparator& comp)
416{
417 if (x.size() == 0)
418 return -1;
419
420 return std::distance(x.begin(), std::max_element(x.begin(), x.end(), comp));
421}
422
437Eigen::ArrayXd
438geomSpace(const double start, const double end, const int n);
439
452template<typename T>
453Eigen::VectorXd
454l1Norms(const T& x)
455{
456 const int p = x.cols();
457
458 Eigen::VectorXd out(p);
459
460 for (int j = 0; j < p; ++j) {
461 out(j) = x.col(j).cwiseAbs().sum();
462 }
463
464 return out;
465}
466
477template<typename T>
478Eigen::VectorXd
479l2Norms(const Eigen::SparseMatrixBase<T>& x)
480{
481 const int p = x.cols();
482
483 Eigen::VectorXd out(p);
484
485 for (int j = 0; j < p; ++j) {
486 out(j) = x.col(j).norm();
487 }
488
489 return out;
490}
491
502template<typename T>
503Eigen::VectorXd
504l2Norms(const Eigen::MatrixBase<T>& x)
505{
506 return x.colwise().norm();
507}
508
520template<typename T>
521Eigen::VectorXd
522maxAbs(const Eigen::SparseMatrixBase<T>& x)
523{
524 const int p = x.cols();
525
526 Eigen::VectorXd out(p);
527
528 for (int j = 0; j < p; ++j) {
529 double x_j_maxabs = 0.0;
530
531 for (typename T::InnerIterator it(x.derived(), j); it; ++it) {
532 x_j_maxabs = std::max(x_j_maxabs, std::abs(it.value()));
533 }
534
535 out(j) = x_j_maxabs;
536 }
537
538 return out;
539}
540
553template<typename T>
554Eigen::VectorXd
555maxAbs(const Eigen::MatrixBase<T>& x)
556{
557 return x.cwiseAbs().colwise().maxCoeff();
558}
559
571template<typename T>
572Eigen::VectorXd
573means(const Eigen::SparseMatrixBase<T>& x)
574{
575 const int n = x.rows();
576 const int p = x.cols();
577
578 Eigen::VectorXd out(p);
579
580 for (int j = 0; j < p; ++j) {
581 out(j) = x.col(j).sum() / n;
582 }
583
584 return out;
585}
586
598template<typename T>
599Eigen::VectorXd
600means(const Eigen::MatrixBase<T>& x)
601{
602 return x.colwise().mean();
603}
604
617template<typename T>
618Eigen::VectorXd
619stdDevs(const Eigen::SparseMatrixBase<T>& x)
620{
621 const int n = x.rows();
622 const int p = x.cols();
623
624 Eigen::VectorXd x_means = means(x);
625 Eigen::VectorXd out(p);
626
627 for (int j = 0; j < p; ++j) {
628 double sum_sq_diff = 0.0;
629 const double mean = x_means(j);
630
631 // Process non-zero elements
632 for (typename T::InnerIterator it(x.derived(), j); it; ++it) {
633 double diff = it.value() - mean;
634 sum_sq_diff += diff * diff;
635 }
636
637 // Account for zeros
638 int nz_count = slope::nonZeros(x.col(j));
639 if (nz_count < n) {
640 sum_sq_diff += (n - nz_count) * mean * mean;
641 }
642
643 // Standard deviation is sqrt of the average of squared differences
644 out(j) = std::sqrt(sum_sq_diff / n);
645 }
646
647 return out;
648}
649
662template<typename T>
663Eigen::VectorXd
664stdDevs(const Eigen::MatrixBase<T>& x)
665{
666 int n = x.rows();
667 int p = x.cols();
668
669 Eigen::VectorXd x_means = means(x);
670 Eigen::VectorXd out(p);
671
672 for (int j = 0; j < p; ++j) {
673 out(j) = (x.col(j).array() - x_means(j)).matrix().norm();
674 }
675
676 out.array() /= std::sqrt(n);
677
678 return out;
679}
680
691template<typename T>
692Eigen::VectorXd
693ranges(const Eigen::SparseMatrixBase<T>& x)
694{
695 const int p = x.cols();
696
697 Eigen::VectorXd out(p);
698
699 for (int j = 0; j < p; ++j) {
700 double x_j_max = 0.0;
701 double x_j_min = 0.0;
702
703 for (typename T::InnerIterator it(x.derived(), j); it; ++it) {
704 x_j_max = std::max(x_j_max, it.value());
705 x_j_min = std::min(x_j_min, it.value());
706 }
707
708 out(j) = x_j_max - x_j_min;
709 }
710
711 return out;
712}
713
727template<typename T>
728Eigen::VectorXd
729ranges(const Eigen::MatrixBase<T>& x)
730{
731 return x.colwise().maxCoeff() - x.colwise().minCoeff();
732}
733
744template<typename T>
745Eigen::VectorXd
746mins(const Eigen::SparseMatrixBase<T>& x)
747{
748 const int p = x.cols();
749
750 Eigen::VectorXd out(p);
751
752 for (int j = 0; j < p; ++j) {
753 double x_j_min = 0.0;
754
755 for (typename T::InnerIterator it(x.derived(), j); it; ++it) {
756 x_j_min = std::min(x_j_min, it.value());
757 }
758
759 out(j) = x_j_min;
760 }
761
762 return out;
763}
764
778template<typename T>
779Eigen::VectorXd
780mins(const Eigen::MatrixBase<T>& x)
781{
782 return x.colwise().minCoeff();
783}
784
803template<typename T>
804Eigen::VectorXd
805clusterGradient(Eigen::VectorXd& beta,
806 Eigen::MatrixXd& residual,
807 Clusters& clusters,
808 const T& x,
809 const Eigen::MatrixXd& w,
810 const Eigen::VectorXd& x_centers,
811 const Eigen::VectorXd& x_scales,
812 const JitNormalization jit_normalization)
813{
814 using namespace Eigen;
815
816 const int n = x.rows();
817 const int p = x.cols();
818 const int n_clusters = clusters.size();
819
820 Eigen::VectorXd gradient = Eigen::VectorXd::Zero(n_clusters);
821
822 for (int j = 0; j < n_clusters; ++j) {
823 double c_old = clusters.coeff(j);
824
825 if (c_old == 0) {
826 gradient(j) = 0;
827 continue;
828 }
829
830 int cluster_size = clusters.cluster_size(j);
831 std::vector<int> s;
832 s.reserve(cluster_size);
833
834 for (auto c_it = clusters.cbegin(j); c_it != clusters.cend(j); ++c_it) {
835 int ind = *c_it;
836 double s_k = sign(beta(ind));
837 s.emplace_back(s_k);
838 }
839
840 double hess = 1;
841 double grad = 0;
842
843 if (cluster_size == 1) {
844 int k = *clusters.cbegin(j);
845 std::tie(grad, hess) = computeGradientAndHessian(
846 x, k, w, residual, x_centers, x_scales, s[0], jit_normalization, n);
847 } else {
848 std::tie(hess, grad) = computeClusterGradientAndHessian(
849 x, j, s, clusters, w, residual, x_centers, x_scales, jit_normalization);
850 }
851
852 gradient(j) = grad;
853 }
854
855 return gradient;
856}
857
858} // namespace slope
Representation of the clusters in SLOPE.
Definition clusters.h:18
double coeff(const int i) const
Returns the coefficient of the cluster with the given index.
std::size_t size()
Returns the number of clusters.
Definition clusters.h:35
int cluster_size(const int i) const
Returns the size of the cluster with the given index.
std::vector< int >::const_iterator cend(const int i) const
Returns a constant iterator pointing to the end of the cluster with the given index.
std::vector< int >::const_iterator cbegin(const int i) const
Returns a constant iterator pointing to the beginning of the cluster with the given index.
The declaration of the Clusters class.
Eigen compatibility layer for version differences.
Enums to control predictor standardization behavior.
Namespace containing SLOPE regression implementation.
Definition clusters.h:11
Eigen::VectorXd means(const Eigen::SparseMatrixBase< T > &x)
Computes the arithmetic mean for each column of a sparse matrix.
Definition math.h:573
T clamp(const T &x, const T &lo, const T &hi)
Definition math.h:113
Eigen::VectorXd l1Norms(const T &x)
Computes the L1 (Manhattan) norms for each column of a matrix.
Definition math.h:454
int sign(T val)
Returns the sign of a given value.
Definition math.h:38
int whichMin(const T &x)
Returns the index of the minimum element in a container.
Definition math.h:395
T sigmoid(const T &x)
Definition math.h:79
std::pair< double, double > computeGradientAndHessian(const T &x, const int ind, const Eigen::MatrixXd &w, const Eigen::MatrixXd &residual, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const double s, const JitNormalization jit_normalization, const int n)
Definition hybrid_cd.h:44
JitNormalization
Enums to control predictor standardization behavior.
@ None
No JIT normalization.
Eigen::VectorXd logSumExp(const Eigen::MatrixXd &a)
std::vector< int > setDiff(const std::vector< int > &a, const std::vector< int > &b)
Computes the set difference of two sorted integer vectors.
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:154
Eigen::VectorXd ranges(const Eigen::SparseMatrixBase< T > &x)
Computes the range (max - min) for each column of a matrix.
Definition math.h:693
Eigen::MatrixXd softmax(const Eigen::MatrixXd &x)
std::vector< int > setUnion(const std::vector< int > &a, const std::vector< int > &b)
Computes the union of two sorted integer vectors.
Eigen::VectorXd mins(const Eigen::SparseMatrixBase< T > &x)
Computes the minimum value for each column of a sparse matrix.
Definition math.h:746
Eigen::ArrayXd cumSum(const T &x, const bool leading_zero=false)
Definition math.h:54
int whichBest(const T &x, const Comparator &comp)
Returns the index of the minimum element in a container.
Definition math.h:415
void updateGradient(Eigen::VectorXd &gradient, const T &x, const Eigen::MatrixXd &residual, const std::vector< int > &active_set, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const Eigen::VectorXd &w, const JitNormalization jit_normalization)
Definition math.h:235
void offsetGradient(Eigen::VectorXd &gradient, const T &x, const Eigen::VectorXd &offset, const std::vector< int > &active_set, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const JitNormalization jit_normalization)
Definition math.h:307
T logit(const T &x)
Definition math.h:95
Eigen::VectorXd stdDevs(const Eigen::SparseMatrixBase< T > &x)
Computes the standard deviation for each column of a matrix.
Definition math.h:619
Eigen::VectorXd l2Norms(const Eigen::SparseMatrixBase< T > &x)
Computes the L2 (Euclidean) norms for each column of a sparse matrix.
Definition math.h:479
Eigen::VectorXd clusterGradient(Eigen::VectorXd &beta, Eigen::MatrixXd &residual, Clusters &clusters, const T &x, const Eigen::MatrixXd &w, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const JitNormalization jit_normalization)
Definition math.h:805
Eigen::VectorXd maxAbs(const Eigen::SparseMatrixBase< T > &x)
Computes the maximum absolute value for each column of a matrix.
Definition math.h:522
Eigen::ArrayXd geomSpace(const double start, const double end, const int n)
Creates an array of n numbers in geometric progression from start to end.
std::pair< double, double > computeClusterGradientAndHessian(const Eigen::MatrixBase< T > &x, const int c_ind, const std::vector< int > &s, const Clusters &clusters, const Eigen::MatrixXd &w, const Eigen::MatrixXd &residual, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const JitNormalization jit_normalization)
Definition hybrid_cd.h:128
int whichMax(const T &x)
Returns the index of the maximum element in a container.
Definition math.h:377
Eigen::Index nonZeros(const Eigen::SparseMatrixBase< Derived > &x)
Definition utils.h:34
Thread management for parallel computations.
Various utility functions.