11#include "observation_weights.h"
15#include <Eigen/SparseCore>
24std::pair<double, Eigen::VectorXd>
25fitWeightedOls(
const Eigen::MatrixBase<T>& x,
26 const Eigen::VectorXd& y,
28 const Eigen::VectorXd& weights)
30 const Eigen::VectorXd root_weights = weights.array().sqrt();
31 Eigen::MatrixXd design =
32 Eigen::MatrixXd::Zero(x.rows(), x.cols() +
static_cast<int>(intercept));
33 Eigen::VectorXd response = Eigen::VectorXd::Zero(y.size());
34 for (Eigen::Index i = 0; i < x.rows(); ++i) {
35 if (weights(i) > 0.0) {
36 design.row(i).head(x.cols()) = x.row(i) * root_weights(i);
37 response(i) = y(i) * root_weights(i);
41 design.col(x.cols()) = root_weights;
43 auto fit = fitOls(design, response,
false);
47 const double beta0 = fit.second(x.cols());
48 return { beta0, fit.second.head(x.cols()) };
52std::pair<double, Eigen::VectorXd>
53fitWeightedOls(
const Eigen::SparseMatrixBase<T>& x,
54 const Eigen::VectorXd& y,
56 const Eigen::VectorXd& weights)
58 const Eigen::VectorXd root_weights = weights.array().sqrt();
59 Eigen::SparseMatrix<double> weighted_x = x;
60 for (
int k = 0; k < weighted_x.outerSize(); ++k) {
61 for (Eigen::SparseMatrix<double>::InnerIterator it(weighted_x, k); it;
64 weights(it.row()) > 0.0 ? it.value() * root_weights(it.row()) : 0.0;
67 Eigen::VectorXd response = Eigen::VectorXd::Zero(y.size());
68 for (Eigen::Index i = 0; i < y.size(); ++i) {
69 if (weights(i) > 0.0) {
70 response(i) = y(i) * root_weights(i);
73 Eigen::SparseMatrix<double> design(x.rows(),
74 x.cols() +
static_cast<int>(intercept));
75 design.leftCols(x.cols()) = weighted_x;
77 Eigen::SparseMatrix<double> intercept_column = root_weights.sparseView();
78 design.rightCols(1) = intercept_column;
80 auto fit = fitOls(design, response,
false);
84 const double beta0 = fit.second(x.cols());
85 return { beta0, fit.second.head(x.cols()) };
90quadraticOlsDualPointFromDesign(
92 const Eigen::MatrixXd& y,
94 const Eigen::VectorXd& observation_weights = Eigen::VectorXd())
96 const Eigen::VectorXd weights =
97 normalizeObservationWeights(observation_weights, y.rows());
98 Eigen::MatrixXd theta(y.rows(), y.cols());
100 for (Eigen::Index k = 0; k < y.cols(); ++k) {
102 uniformObservationWeights(weights)
103 ? fitOls(x, Eigen::VectorXd(y.col(k)), intercept)
104 : fitWeightedOls(x, Eigen::VectorXd(y.col(k)), intercept, weights);
105 const auto& [beta0, beta] = fit;
106 theta.col(k) = x * beta - y.col(k);
108 theta.col(k).array() += beta0;
109 const double center = weightedMean(theta.col(k), weights)(0);
110 theta.col(k).array() -= center;
114 for (Eigen::Index i = 0; i < y.rows(); ++i) {
115 if (weights(i) == 0.0) {
116 theta.row(i).setZero();
125quadraticOlsDualPoint(
126 const Eigen::MatrixBase<T>& x,
127 const Eigen::MatrixXd& y,
128 const Eigen::VectorXd& x_centers,
129 const Eigen::VectorXd& x_scales,
131 const bool intercept,
132 const Eigen::VectorXd& observation_weights = Eigen::VectorXd())
134 const Eigen::VectorXd weights =
135 normalizeObservationWeights(observation_weights, y.rows());
136 Eigen::MatrixXd x_effective = x;
142 for (Eigen::Index i = 0; i < weights.size(); ++i) {
143 if (weights(i) == 0.0) {
144 x_effective.row(i).setZero();
147 if (center && !intercept) {
148 x_effective.rowwise() -= x_centers.transpose();
151 for (Eigen::Index i = 0; i < weights.size(); ++i) {
152 if (weights(i) == 0.0) {
153 x_effective.row(i).setZero();
157 x_effective.array().rowwise() /= x_scales.transpose().array();
160 return quadraticOlsDualPointFromDesign(
161 x_effective, y, intercept, observation_weights);
166quadraticOlsDualPoint(
167 const Eigen::SparseMatrixBase<T>& x,
168 const Eigen::MatrixXd& y,
169 const Eigen::VectorXd& x_centers,
170 const Eigen::VectorXd& x_scales,
172 const bool intercept,
173 const Eigen::VectorXd& observation_weights = Eigen::VectorXd())
175 const Eigen::VectorXd weights =
176 normalizeObservationWeights(observation_weights, y.rows());
182 Eigen::SparseMatrix<double> x_effective = x;
183 for (
int k = 0; k < x_effective.outerSize(); ++k) {
184 for (Eigen::SparseMatrix<double>::InnerIterator it(x_effective, k); it;
186 if (weights(it.row()) == 0.0) {
189 it.valueRef() /= x_scales(it.col());
194 if (!center || intercept) {
195 return quadraticOlsDualPointFromDesign(
196 x_effective, y, intercept, observation_weights);
199 Eigen::VectorXd effective_centers = x_centers;
201 effective_centers.array() /= x_scales.array();
204 if (!uniformObservationWeights(observation_weights)) {
208 Eigen::MatrixXd centered =
209 Eigen::MatrixXd(x_effective).rowwise() - effective_centers.transpose();
210 for (Eigen::Index i = 0; i < weights.size(); ++i) {
211 if (weights(i) == 0.0) {
212 centered.row(i).setZero();
215 return quadraticOlsDualPointFromDesign(
216 centered, y,
false, observation_weights);
219 Eigen::MatrixXd theta(y.rows(), y.cols());
220 for (Eigen::Index k = 0; k < y.cols(); ++k) {
221 auto fit = fitOls(x_effective, Eigen::VectorXd(y.col(k)),
true);
222 const Eigen::VectorXd& beta = fit.second;
223 theta.col(k) = x_effective * beta - y.col(k);
224 theta.col(k).array() -= effective_centers.dot(beta);
249template<
typename MatrixType>
252 const Eigen::VectorXd& beta,
253 Eigen::MatrixXd theta,
254 const std::unique_ptr<Loss>& loss,
256 const Eigen::ArrayXd& lambda,
258 const Eigen::MatrixXd& y,
259 const Eigen::VectorXd& x_centers,
260 const Eigen::VectorXd& x_scales,
262 const Eigen::VectorXd& observation_weights = Eigen::VectorXd())
264 const int n = x.rows();
265 const Eigen::VectorXd weights =
266 detail::normalizeObservationWeights(observation_weights, n);
267 Eigen::VectorXd gradient(beta.size());
270 gradient, x, theta, x_centers, x_scales, weights, jit_normalization);
272 const double dual_norm = sl1_norm.
dualNorm(gradient, lambda);
273 theta.array() /= std::max(1.0, dual_norm);
275 return loss->dual(theta, y, weights);
300template<
typename MatrixType>
303 const Eigen::MatrixXd& eta,
304 const std::unique_ptr<Loss>& loss,
306 const Eigen::ArrayXd& lambda,
308 const Eigen::MatrixXd& y,
309 const Eigen::VectorXd& x_centers,
310 const Eigen::VectorXd& x_scales,
312 const bool intercept,
313 const Eigen::VectorXd& observation_weights = Eigen::VectorXd())
315 Eigen::MatrixXd theta =
316 loss->dualPoint(eta, y, intercept, observation_weights);
327 observation_weights);
Class representing the Sorted L1 Norm.
double dualNorm(const Eigen::VectorXd &a, const Eigen::ArrayXd &lambda) const
Computes the dual norm of a vector.
Enums to control predictor standardization behavior.
The declartion of the Objctive class and its subclasses, which represent the data-fitting part of the...
Mathematical support functions for the slope package.
Namespace containing SLOPE regression implementation.
double computeDualFromPoint(const Eigen::VectorXd &beta, Eigen::MatrixXd theta, const std::unique_ptr< Loss > &loss, const SortedL1Norm &sl1_norm, const Eigen::ArrayXd &lambda, const MatrixType &x, const Eigen::MatrixXd &y, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const JitNormalization &jit_normalization, const Eigen::VectorXd &observation_weights=Eigen::VectorXd())
Scales a candidate into the SLOPE dual constraint and evaluates it.
JitNormalization
Enums to control predictor standardization behavior.
double computeDual(const Eigen::VectorXd &beta, const Eigen::MatrixXd &eta, const std::unique_ptr< Loss > &loss, const SortedL1Norm &sl1_norm, const Eigen::ArrayXd &lambda, const MatrixType &x, const Eigen::MatrixXd &y, const Eigen::VectorXd &x_centers, const Eigen::VectorXd &x_scales, const JitNormalization &jit_normalization, const bool intercept, const Eigen::VectorXd &observation_weights=Eigen::VectorXd())
Computes the dual objective function value for SLOPE optimization.
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.
Ordinary Least Squares (OLS) regression functionality.
The declaration of the SortedL1Norm class.