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