13#include <Eigen/SparseCore>
42 return (T(0) < val) - (val < T(0));
56cumSum(
const T& x,
const bool leading_zero =
false)
58 const size_t start = leading_zero ? 1 : 0;
59 Eigen::ArrayXd out(x.size() + start);
65 std::partial_sum(x.begin(), x.end(), out.begin() + start);
83 return 1.0 / (1.0 + std::exp(-x));
99 assert(x > 0 && x < 1 &&
"Input must be in (0, 1)");
101 return std::log(x) - std::log1p(-x);
115clamp(
const T& x,
const T& lo,
const T& hi)
117 return x < lo ? lo : x > hi ? hi : x;
156linearPredictor(
const T& x,
157 const std::vector<int>& active_set,
158 const Eigen::VectorXd& beta0,
159 const Eigen::VectorXd& beta,
160 const Eigen::VectorXd& x_centers,
161 const Eigen::VectorXd& x_scales,
163 const bool intercept)
167 int m = beta0.size();
169 Eigen::MatrixXd eta = Eigen::MatrixXd::Zero(n, m);
172 bool large_problem = active_set.size() > 100 && n * active_set.size() > 1e7;
173#pragma omp parallel num_threads(Threads::get()) if (large_problem)
176 Eigen::MatrixXd eta_local = Eigen::MatrixXd::Zero(n, m);
179#pragma omp for nowait
181 for (
int i = 0; i < static_cast<int>(active_set.size()); ++i) {
182 int ind = active_set[i];
183 auto [k, j] = std::div(ind, p);
185 switch (jit_normalization) {
187 eta_local.col(k) += x.col(j) * beta(ind) / x_scales(j);
188 eta_local.col(k).array() -= beta(ind) * x_centers(j) / x_scales(j);
192 eta_local.col(k) += x.col(j) * beta(ind);
193 eta_local.col(k).array() -= beta(ind) * x_centers(j);
197 eta_local.col(k) += x.col(j) * beta(ind) / x_scales(j);
201 eta_local.col(k) += x.col(j) * beta(ind);
215 eta.rowwise() += beta0.transpose();
237template<
typename T,
typename IndexAt>
239updateGradientImpl(Eigen::VectorXd& gradient,
241 const Eigen::MatrixXd& residual,
242 const int active_size,
243 const IndexAt& index_at,
244 const Eigen::VectorXd& x_centers,
245 const Eigen::VectorXd& x_scales,
246 const Eigen::VectorXd& w,
249 const int n = x.rows();
250 const int p = x.cols();
251 const int m = residual.cols();
253 assert(gradient.size() == p * m &&
254 "Gradient matrix has incorrect dimensions");
256 Eigen::MatrixXd weighted_residual(n, m);
257 Eigen::ArrayXd wr_sums(m);
261 active_size > 100 &&
static_cast<long long>(n) * active_size > 1e5;
262#pragma omp parallel for num_threads(Threads::get()) if (large_problem)
264 for (
int k = 0; k < m; ++k) {
265 weighted_residual.col(k) = residual.col(k).cwiseProduct(w);
266 for (
int i = 0; i < n; ++i) {
268 weighted_residual(i, k) = 0.0;
271 wr_sums(k) = weighted_residual.col(k).sum();
275#pragma omp parallel for num_threads(Threads::get()) if (large_problem)
277 for (
int i = 0; i < active_size; ++i) {
278 int ind = index_at(i);
279 auto [k, j] = std::div(ind, p);
281 switch (jit_normalization) {
284 (x.col(j).dot(weighted_residual.col(k)) - x_centers(j) * wr_sums(k)) /
289 (x.col(j).dot(weighted_residual.col(k)) - x_centers(j) * wr_sums(k)) /
294 x.col(j).dot(weighted_residual.col(k)) / (x_scales(j) * n);
297 gradient(ind) = x.col(j).dot(weighted_residual.col(k)) / n;
320 const Eigen::MatrixXd& residual,
321 const std::vector<int>& active_set,
322 const Eigen::VectorXd& x_centers,
323 const Eigen::VectorXd& x_scales,
324 const Eigen::VectorXd& w,
327 detail::updateGradientImpl(
331 static_cast<int>(active_set.size()),
332 [&active_set](
int i) { return active_set[i]; },
348 const Eigen::MatrixXd& residual,
349 const Eigen::VectorXd& x_centers,
350 const Eigen::VectorXd& x_scales,
351 const Eigen::VectorXd& w,
354 detail::updateGradientImpl(
358 static_cast<int>(gradient.size()),
359 [](
int i) { return i; },
381template<
typename T,
typename IndexAt>
383offsetGradientImpl(Eigen::VectorXd& gradient,
385 const Eigen::VectorXd& offset,
386 const int active_size,
387 const IndexAt& index_at,
388 const Eigen::VectorXd& x_centers,
389 const Eigen::VectorXd& x_scales,
392 const int n = x.rows();
393 const int p = x.cols();
395 for (
int i = 0; i < active_size; ++i) {
396 int ind = index_at(i);
397 auto [k, j] = std::div(ind, p);
399 switch (jit_normalization) {
402 offset(k) * (x.col(j).sum() / n - x_centers(j)) / x_scales(j);
405 gradient(ind) -= offset(k) * (x.col(j).sum() / n - x_centers(j));
408 gradient(ind) -= offset(k) * x.col(j).sum() / (n * x_scales(j));
411 gradient(ind) -= offset(k) * x.col(j).sum() / n;
433 const Eigen::VectorXd& offset,
434 const std::vector<int>& active_set,
435 const Eigen::VectorXd& x_centers,
436 const Eigen::VectorXd& x_scales,
439 detail::offsetGradientImpl(
443 static_cast<int>(active_set.size()),
444 [&active_set](
int i) { return active_set[i]; },
459 const Eigen::VectorXd& offset,
460 const Eigen::VectorXd& x_centers,
461 const Eigen::VectorXd& x_scales,
464 detail::offsetGradientImpl(
468 static_cast<int>(gradient.size()),
469 [](
int i) { return i; },
484setUnion(
const std::vector<int>& a,
const std::vector<int>& b);
497setDiff(
const std::vector<int>& a,
const std::vector<int>& b);
514 return std::distance(x.begin(), std::max_element(x.begin(), x.end()));
532 return std::distance(x.begin(), std::min_element(x.begin(), x.end()));
548template<
typename T,
typename Comparator>
555 return std::distance(x.begin(), std::max_element(x.begin(), x.end(), comp));
573geomSpace(
const double start,
const double end,
const int n);
591 const int p = x.cols();
593 Eigen::VectorXd out(p);
595 for (
int j = 0; j < p; ++j) {
596 out(j) = x.col(j).cwiseAbs().sum();
616 const int p = x.cols();
618 Eigen::VectorXd out(p);
620 for (
int j = 0; j < p; ++j) {
621 out(j) = x.col(j).norm();
641 return x.colwise().norm();
657maxAbs(
const Eigen::SparseMatrixBase<T>& x)
659 const int p = x.cols();
661 Eigen::VectorXd out(p);
663 for (
int j = 0; j < p; ++j) {
664 double x_j_maxabs = 0.0;
666 for (
typename T::InnerIterator it(x.derived(), j); it; ++it) {
667 x_j_maxabs = std::max(x_j_maxabs, std::abs(it.value()));
692 return x.cwiseAbs().colwise().maxCoeff();
708means(
const Eigen::SparseMatrixBase<T>& x)
710 const int n = x.rows();
711 const int p = x.cols();
713 Eigen::VectorXd out(p);
715 for (
int j = 0; j < p; ++j) {
716 out(j) = x.col(j).sum() / n;
737 return x.colwise().mean();
756 const int n = x.rows();
757 const int p = x.cols();
759 Eigen::VectorXd x_means =
means(x);
760 Eigen::VectorXd out(p);
762 for (
int j = 0; j < p; ++j) {
763 double sum_sq_diff = 0.0;
764 const double mean = x_means(j);
765 double minimum = std::numeric_limits<double>::infinity();
766 double maximum = -std::numeric_limits<double>::infinity();
769 for (
typename T::InnerIterator it(x.derived(), j); it; ++it) {
770 double diff = it.value() - mean;
771 sum_sq_diff += diff * diff;
772 minimum = std::min(minimum, it.value());
773 maximum = std::max(maximum, it.value());
779 sum_sq_diff += (n - nz_count) * mean * mean;
780 minimum = std::min(minimum, 0.0);
781 maximum = std::max(maximum, 0.0);
785 if (minimum == maximum) {
791 out(j) = std::sqrt(sum_sq_diff / n);
816 Eigen::VectorXd x_means =
means(x);
817 Eigen::VectorXd out(p);
819 for (
int j = 0; j < p; ++j) {
821 if (n > 0 && x.col(j).minCoeff() == x.col(j).maxCoeff()) {
824 out(j) = (x.col(j).array() - x_means(j)).matrix().norm();
828 out.array() /= std::sqrt(n);
845ranges(
const Eigen::SparseMatrixBase<T>& x)
847 const int p = x.cols();
849 Eigen::VectorXd out(p);
851 for (
int j = 0; j < p; ++j) {
852 double x_j_max = 0.0;
853 double x_j_min = 0.0;
855 for (
typename T::InnerIterator it(x.derived(), j); it; ++it) {
856 x_j_max = std::max(x_j_max, it.value());
857 x_j_min = std::min(x_j_min, it.value());
860 out(j) = x_j_max - x_j_min;
883 return x.colwise().maxCoeff() - x.colwise().minCoeff();
898mins(
const Eigen::SparseMatrixBase<T>& x)
900 const int p = x.cols();
902 Eigen::VectorXd out(p);
904 for (
int j = 0; j < p; ++j) {
905 double x_j_min = 0.0;
907 for (
typename T::InnerIterator it(x.derived(), j); it; ++it) {
908 x_j_min = std::min(x_j_min, it.value());
932mins(
const Eigen::MatrixBase<T>& x)
934 return x.colwise().minCoeff();
959 Eigen::MatrixXd& residual,
962 const Eigen::MatrixXd& w,
963 const Eigen::VectorXd& x_centers,
964 const Eigen::VectorXd& x_scales,
967 using namespace Eigen;
969 const int n = x.rows();
970 const int p = x.cols();
971 const int n_clusters = clusters.
size();
973 Eigen::VectorXd gradient = Eigen::VectorXd::Zero(n_clusters);
975 for (
int j = 0; j < n_clusters; ++j) {
976 double c_old = clusters.
coeff(j);
985 s.reserve(cluster_size);
987 for (
auto c_it = clusters.
cbegin(j); c_it != clusters.
cend(j); ++c_it) {
989 double s_k =
sign(beta(ind));
996 if (cluster_size == 1) {
997 int k = *clusters.
cbegin(j);
999 x, k, w, residual, x_centers, x_scales, s[0], jit_normalization, n);
1002 x, j, s, clusters, w, residual, x_centers, x_scales, jit_normalization);
Representation of the nonzero clusters in SLOPE.
double coeff(const int i) const
Returns the coefficient of the cluster with the given index.
std::size_t size()
Returns the number of stored nonzero clusters.
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.
Eigen::VectorXd means(const Eigen::SparseMatrixBase< T > &x)
Computes the arithmetic mean for each column of a sparse matrix.
std::pair< double, double > computeGradientAndHessian(const Eigen::MatrixBase< 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, const Eigen::VectorXd &weight_sums)
T clamp(const T &x, const T &lo, const T &hi)
Eigen::VectorXd l1Norms(const T &x)
Computes the L1 (Manhattan) norms for each column of a matrix.
int sign(T val)
Returns the sign of a given value.
int whichMin(const T &x)
Returns the index of the minimum element in a container.
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::VectorXd ranges(const Eigen::SparseMatrixBase< T > &x)
Computes the range (max - min) for each column of a matrix.
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.
Eigen::ArrayXd cumSum(const T &x, const bool leading_zero=false)
int whichBest(const T &x, const Comparator &comp)
Returns the index of the minimum element in a container.
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)
Computes the gradient for selected coefficients.
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)
Offsets the gradient for selected coefficients.
Eigen::VectorXd stdDevs(const Eigen::SparseMatrixBase< T > &x)
Computes the standard deviation for each column of a matrix.
Eigen::VectorXd l2Norms(const Eigen::SparseMatrixBase< T > &x)
Computes the L2 (Euclidean) norms for each column of a sparse matrix.
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)
Eigen::VectorXd maxAbs(const Eigen::SparseMatrixBase< T > &x)
Computes the maximum absolute value for each column of a matrix.
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)
int whichMax(const T &x)
Returns the index of the maximum element in a container.
Eigen::Index nonZeros(const Eigen::SparseMatrixBase< Derived > &x)
Thread management for parallel computations.
Various utility functions.