slope 6.0.1
Loading...
Searching...
No Matches
math.h
Go to the documentation of this file.
1
6#pragma once
7
8#include "clusters.h"
9#include "jit_normalization.h"
10#include <Eigen/Core>
11#include <Eigen/SparseCore>
12#include <numeric>
13#include <vector>
14
15#ifdef _OPENMP
16#include "threads.h"
17#endif
18
19namespace slope {
20
33template<typename T>
34int
35sign(T val)
36{
37 return (T(0) < val) - (val < T(0));
38}
39
49template<typename T>
50Eigen::ArrayXd
51cumSum(const T& x, const bool leading_zero = false)
52{
53 const size_t start = leading_zero ? 1 : 0;
54 Eigen::ArrayXd out(x.size() + start);
55
56 if (leading_zero) {
57 out(0) = 0.0;
58 }
59
60 std::partial_sum(x.begin(), x.end(), out.begin() + start);
61
62 return out;
63}
64
74template<typename T>
75T
76sigmoid(const T& x)
77{
78 return 1.0 / (1.0 + std::exp(-x));
79}
80
90template<typename T>
91T
92logit(const T& x)
93{
94 assert(x > 0 && x < 1 && "Input must be in (0, 1)");
95
96 return std::log(x) - std::log1p(-x);
97}
98
108template<typename T>
109T
110clamp(const T& x, const T& lo, const T& hi)
111{
112 return x < lo ? lo : x > hi ? hi : x;
113}
114
121Eigen::VectorXd
122logSumExp(const Eigen::MatrixXd& a);
123
132Eigen::MatrixXd
133softmax(const Eigen::MatrixXd& x);
134
149template<typename T>
150Eigen::MatrixXd
152 const std::vector<int>& active_set,
153 const Eigen::VectorXd& beta0,
154 const Eigen::VectorXd& beta,
155 const Eigen::VectorXd& x_centers,
156 const Eigen::VectorXd& x_scales,
157 const JitNormalization jit_normalization,
158 const bool intercept)
159{
160 int n = x.rows();
161 int p = x.cols();
162 int m = beta0.size();
163
164 Eigen::MatrixXd eta = Eigen::MatrixXd::Zero(n, m);
165
166#ifdef _OPENMP
167 bool large_problem = active_set.size() > 100 && n * active_set.size() > 1e7;
168#pragma omp parallel num_threads(Threads::get()) if (large_problem)
169#endif
170 {
171 Eigen::MatrixXd eta_local = Eigen::MatrixXd::Zero(n, m);
172
173#ifdef _OPENMP
174#pragma omp for nowait
175#endif
176 for (int i = 0; i < static_cast<int>(active_set.size()); ++i) {
177 int ind = active_set[i];
178 auto [k, j] = std::div(ind, p);
179
180 switch (jit_normalization) {
182 eta_local.col(k) += x.col(j) * beta(ind) / x_scales(j);
183 eta_local.col(k).array() -= beta(ind) * x_centers(j) / x_scales(j);
184 break;
185
187 eta_local.col(k) += x.col(j) * beta(ind);
188 eta_local.col(k).array() -= beta(ind) * x_centers(j);
189 break;
190
192 eta_local.col(k) += x.col(j) * beta(ind) / x_scales(j);
193 break;
194
196 eta_local.col(k) += x.col(j) * beta(ind);
197 break;
198 }
199 }
200
201#ifdef _OPENMP
202#pragma omp critical
203#endif
204 {
205 eta += eta_local;
206 }
207 }
208
209 if (intercept) {
210 eta.rowwise() += beta0.transpose();
211 }
212
213 return eta;
214}
215
230template<typename T>
231void
232updateGradient(Eigen::VectorXd& gradient,
233 const T& x,
234 const Eigen::MatrixXd& residual,
235 const std::vector<int>& active_set,
236 const Eigen::VectorXd& x_centers,
237 const Eigen::VectorXd& x_scales,
238 const Eigen::VectorXd& w,
239 const JitNormalization jit_normalization)
240{
241 const int n = x.rows();
242 const int p = x.cols();
243 const int m = residual.cols();
244
245 assert(gradient.size() == p * m &&
246 "Gradient matrix has incorrect dimensions");
247
248 Eigen::MatrixXd weighted_residual(n, m);
249 Eigen::ArrayXd wr_sums(m);
250
251#ifdef _OPENMP
252 bool large_problem = active_set.size() > 100 && n * active_set.size() > 1e5;
253#pragma omp parallel for num_threads(Threads::get()) if (large_problem)
254#endif
255 for (int k = 0; k < m; ++k) {
256 weighted_residual.col(k) = residual.col(k).cwiseProduct(w);
257 wr_sums(k) = weighted_residual.col(k).sum();
258 }
259
260#ifdef _OPENMP
261#pragma omp parallel for num_threads(Threads::get()) if (large_problem)
262#endif
263 for (int i = 0; i < static_cast<int>(active_set.size()); ++i) {
264 int ind = active_set[i];
265 auto [k, j] = std::div(ind, p);
266
267 switch (jit_normalization) {
269 gradient(ind) =
270 (x.col(j).dot(weighted_residual.col(k)) - x_centers(j) * wr_sums(k)) /
271 (x_scales(j) * n);
272 break;
274 gradient(ind) =
275 (x.col(j).dot(weighted_residual.col(k)) - x_centers(j) * wr_sums(k)) /
276 n;
277 break;
279 gradient(ind) =
280 x.col(j).dot(weighted_residual.col(k)) / (x_scales(j) * n);
281 break;
283 gradient(ind) = x.col(j).dot(weighted_residual.col(k)) / n;
284 break;
285 }
286 }
287}
288
302template<typename T>
303void
304offsetGradient(Eigen::VectorXd& gradient,
305 const T& x,
306 const Eigen::VectorXd& offset,
307 const std::vector<int>& active_set,
308 const Eigen::VectorXd& x_centers,
309 const Eigen::VectorXd& x_scales,
310 const JitNormalization jit_normalization)
311{
312 const int n = x.rows();
313 const int p = x.cols();
314
315 for (size_t i = 0; i < active_set.size(); ++i) {
316 int ind = active_set[i];
317 auto [k, j] = std::div(ind, p);
318
319 switch (jit_normalization) {
321 gradient(ind) -=
322 offset(k) * (x.col(j).sum() / n - x_centers(j)) / x_scales(j);
323 break;
325 gradient(ind) -= offset(k) * (x.col(j).sum() / n - x_centers(j));
326 break;
328 gradient(ind) -= offset(k) * x.col(j).sum() / (n * x_scales(j));
329 break;
331 gradient(ind) -= offset(k) * x.col(j).sum() / n;
332 break;
333 }
334 }
335}
336
345std::vector<int>
346setUnion(const std::vector<int>& a, const std::vector<int>& b);
347
358std::vector<int>
359setDiff(const std::vector<int>& a, const std::vector<int>& b);
360
372template<typename T>
373int
374whichMax(const T& x)
375{
376 return std::distance(x.begin(), std::max_element(x.begin(), x.end()));
377}
378
390template<typename T>
391int
392whichMin(const T& x)
393{
394 return std::distance(x.begin(), std::min_element(x.begin(), x.end()));
395}
396
410template<typename T, typename Comparator>
411int
412whichBest(const T& x, const Comparator& comp)
413{
414 if (x.size() == 0)
415 return -1;
416
417 return std::distance(x.begin(), std::max_element(x.begin(), x.end(), comp));
418}
419
434Eigen::ArrayXd
435geomSpace(const double start, const double end, const int n);
436
449template<typename T>
450Eigen::VectorXd
451l1Norms(const T& x)
452{
453 const int p = x.cols();
454
455 Eigen::VectorXd out(p);
456
457 for (int j = 0; j < p; ++j) {
458 out(j) = x.col(j).cwiseAbs().sum();
459 }
460
461 return out;
462}
463
474template<typename T>
475Eigen::VectorXd
476l2Norms(const Eigen::SparseMatrixBase<T>& x)
477{
478 const int p = x.cols();
479
480 Eigen::VectorXd out(p);
481
482 for (int j = 0; j < p; ++j) {
483 out(j) = x.col(j).norm();
484 }
485
486 return out;
487}
488
499template<typename T>
500Eigen::VectorXd
501l2Norms(const Eigen::MatrixBase<T>& x)
502{
503 return x.colwise().norm();
504}
505
517template<typename T>
518Eigen::VectorXd
519maxAbs(const Eigen::SparseMatrixBase<T>& x)
520{
521 const int p = x.cols();
522
523 Eigen::VectorXd out(p);
524
525 for (int j = 0; j < p; ++j) {
526 double x_j_maxabs = 0.0;
527
528 for (typename T::InnerIterator it(x.derived(), j); it; ++it) {
529 x_j_maxabs = std::max(x_j_maxabs, std::abs(it.value()));
530 }
531
532 out(j) = x_j_maxabs;
533 }
534
535 return out;
536}
537
550template<typename T>
551Eigen::VectorXd
552maxAbs(const Eigen::MatrixBase<T>& x)
553{
554 return x.cwiseAbs().colwise().maxCoeff();
555}
556
568template<typename T>
569Eigen::VectorXd
570means(const Eigen::SparseMatrixBase<T>& x)
571{
572 const int n = x.rows();
573 const int p = x.cols();
574
575 Eigen::VectorXd out(p);
576
577 for (int j = 0; j < p; ++j) {
578 out(j) = x.col(j).sum() / n;
579 }
580
581 return out;
582}
583
595template<typename T>
596Eigen::VectorXd
597means(const Eigen::MatrixBase<T>& x)
598{
599 return x.colwise().mean();
600}
601
614template<typename T>
615Eigen::VectorXd
616stdDevs(const Eigen::SparseMatrixBase<T>& x)
617{
618 const int n = x.rows();
619 const int p = x.cols();
620
621 Eigen::VectorXd x_means = means(x);
622 Eigen::VectorXd out(p);
623
624 for (int j = 0; j < p; ++j) {
625 double sum_sq_diff = 0.0;
626 const double mean = x_means(j);
627
628 // Process non-zero elements
629 for (typename T::InnerIterator it(x.derived(), j); it; ++it) {
630 double diff = it.value() - mean;
631 sum_sq_diff += diff * diff;
632 }
633
634 // Account for zeros
635 int nz_count = x.col(j).nonZeros();
636 if (nz_count < n) {
637 sum_sq_diff += (n - nz_count) * mean * mean;
638 }
639
640 // Standard deviation is sqrt of the average of squared differences
641 out(j) = std::sqrt(sum_sq_diff / n);
642 }
643
644 return out;
645}
646
659template<typename T>
660Eigen::VectorXd
661stdDevs(const Eigen::MatrixBase<T>& x)
662{
663 int n = x.rows();
664 int p = x.cols();
665
666 Eigen::VectorXd x_means = means(x);
667 Eigen::VectorXd out(p);
668
669 for (int j = 0; j < p; ++j) {
670 out(j) = (x.col(j).array() - x_means(j)).matrix().norm();
671 }
672
673 out.array() /= std::sqrt(n);
674
675 return out;
676}
677
688template<typename T>
689Eigen::VectorXd
690ranges(const Eigen::SparseMatrixBase<T>& x)
691{
692 const int p = x.cols();
693
694 Eigen::VectorXd out(p);
695
696 for (int j = 0; j < p; ++j) {
697 double x_j_max = 0.0;
698 double x_j_min = 0.0;
699
700 for (typename T::InnerIterator it(x.derived(), j); it; ++it) {
701 x_j_max = std::max(x_j_max, it.value());
702 x_j_min = std::min(x_j_min, it.value());
703 }
704
705 out(j) = x_j_max - x_j_min;
706 }
707
708 return out;
709}
710
724template<typename T>
725Eigen::VectorXd
726ranges(const Eigen::MatrixBase<T>& x)
727{
728 return x.colwise().maxCoeff() - x.colwise().minCoeff();
729}
730
741template<typename T>
742Eigen::VectorXd
743mins(const Eigen::SparseMatrixBase<T>& x)
744{
745 const int p = x.cols();
746
747 Eigen::VectorXd out(p);
748
749 for (int j = 0; j < p; ++j) {
750 double x_j_min = 0.0;
751
752 for (typename T::InnerIterator it(x.derived(), j); it; ++it) {
753 x_j_min = std::min(x_j_min, it.value());
754 }
755
756 out(j) = x_j_min;
757 }
758
759 return out;
760}
761
775template<typename T>
776Eigen::VectorXd
777mins(const Eigen::MatrixBase<T>& x)
778{
779 return x.colwise().minCoeff();
780}
781
800template<typename T>
801Eigen::VectorXd
802clusterGradient(Eigen::VectorXd& beta,
803 Eigen::MatrixXd& residual,
804 Clusters& clusters,
805 const T& x,
806 const Eigen::MatrixXd& w,
807 const Eigen::VectorXd& x_centers,
808 const Eigen::VectorXd& x_scales,
809 const JitNormalization jit_normalization)
810{
811 using namespace Eigen;
812
813 const int n = x.rows();
814 const int p = x.cols();
815 const int n_clusters = clusters.size();
816
817 Eigen::VectorXd gradient = Eigen::VectorXd::Zero(n_clusters);
818
819 for (int j = 0; j < n_clusters; ++j) {
820 double c_old = clusters.coeff(j);
821
822 if (c_old == 0) {
823 gradient(j) = 0;
824 continue;
825 }
826
827 int cluster_size = clusters.cluster_size(j);
828 std::vector<int> s;
829 s.reserve(cluster_size);
830
831 for (auto c_it = clusters.cbegin(j); c_it != clusters.cend(j); ++c_it) {
832 int ind = *c_it;
833 double s_k = sign(beta(ind));
834 s.emplace_back(s_k);
835 }
836
837 double hess = 1;
838 double grad = 0;
839
840 if (cluster_size == 1) {
841 int k = *clusters.cbegin(j);
842 std::tie(grad, hess) = computeGradientAndHessian(
843 x, k, w, residual, x_centers, x_scales, s[0], jit_normalization, n);
844 } else {
845 std::tie(hess, grad) = computeClusterGradientAndHessian(
846 x, j, s, clusters, w, residual, x_centers, x_scales, jit_normalization);
847 }
848
849 gradient(j) = grad;
850 }
851
852 return gradient;
853}
854
855} // 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.
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:570
T clamp(const T &x, const T &lo, const T &hi)
Definition math.h:110
Eigen::VectorXd l1Norms(const T &x)
Computes the L1 (Manhattan) norms for each column of a matrix.
Definition math.h:451
int sign(T val)
Returns the sign of a given value.
Definition math.h:35
int whichMin(const T &x)
Returns the index of the minimum element in a container.
Definition math.h:392
T sigmoid(const T &x)
Definition math.h:76
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:42
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:151
Eigen::VectorXd ranges(const Eigen::SparseMatrixBase< T > &x)
Computes the range (max - min) for each column of a matrix.
Definition math.h:690
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:743
Eigen::ArrayXd cumSum(const T &x, const bool leading_zero=false)
Definition math.h:51
int whichBest(const T &x, const Comparator &comp)
Returns the index of the minimum element in a container.
Definition math.h:412
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:232
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:304
T logit(const T &x)
Definition math.h:92
Eigen::VectorXd stdDevs(const Eigen::SparseMatrixBase< T > &x)
Computes the standard deviation for each column of a matrix.
Definition math.h:616
Eigen::VectorXd l2Norms(const Eigen::SparseMatrixBase< T > &x)
Computes the L2 (Euclidean) norms for each column of a sparse matrix.
Definition math.h:476
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:802
Eigen::VectorXd maxAbs(const Eigen::SparseMatrixBase< T > &x)
Computes the maximum absolute value for each column of a matrix.
Definition math.h:519
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:126
int whichMax(const T &x)
Returns the index of the maximum element in a container.
Definition math.h:374
Thread management for parallel computations.