80std::pair<bool, int>
compute(
const Matrix_& matrix, Eigen::Index number, EigenMatrix_& outU, EigenMatrix_& outV, EigenVector_& outD,
const Options& options) {
81 Eigen::Index smaller = std::min(matrix.rows(), matrix.cols());
82 Eigen::Index requested_number = number;
83 if (requested_number > smaller) {
85 requested_number = smaller;
87 throw std::runtime_error(
"requested number of singular values cannot be greater than the smaller matrix dimension");
90 throw std::runtime_error(
"requested number of singular values must be less than the smaller matrix dimension for IRLBA iterations");
96 internal::exact(matrix, requested_number, outU, outV, outD);
97 return std::make_pair(
true, 0);
100 Eigen::Index work = std::min(requested_number + options.
extra_work, smaller);
102 EigenMatrix_ V(matrix.cols(), work);
103 std::mt19937_64 eng(options.
seed);
105 auto init =
reinterpret_cast<EigenVector_*
>(options.
initial);
106 if (init->size() != V.rows()) {
107 throw std::runtime_error(
"initialization vector does not have expected number of rows");
111 internal::fill_with_random_normals(V, 0, eng);
113 V.col(0) /= V.col(0).norm();
115 bool converged =
false;
118 Eigen::JacobiSVD<EigenMatrix_> svd(work, work, Eigen::ComputeThinU | Eigen::ComputeThinV);
120 internal::LanczosWorkspace<EigenVector_, Matrix_> lptmp(matrix);
122 EigenMatrix_ W(matrix.rows(), work);
123 EigenMatrix_ Wtmp(matrix.rows(), work);
124 EigenMatrix_ Vtmp(matrix.cols(), work);
126 EigenMatrix_ B(work, work);
127 B.setZero(work, work);
128 EigenVector_ res(work);
129 EigenVector_ F(matrix.cols());
131 EigenVector_ prevS(work);
134 typename EigenMatrix_::Scalar svtol_actual = (svtol >= 0 ? svtol : tol);
140 internal::run_lanczos_bidiagonalization(matrix, W, V, B, eng, lptmp, k, options);
149 const auto& BS = svd.singularValues();
150 const auto& BU = svd.matrixU();
151 const auto& BV = svd.matrixV();
154 if (B(work - 1, work - 1) == 0) {
159 const auto& F = lptmp.F;
167 res = R_F * BU.row(work - 1);
169 Eigen::Index n_converged = 0;
171 auto Smax = *std::max_element(BS.begin(), BS.end());
172 auto threshold = Smax * tol;
174 for (Eigen::Index j = 0; j < work; ++j) {
175 if (std::abs(res[j]) <= threshold) {
176 auto ratio = std::abs(BS[j] - prevS[j]) / BS[j];
177 if (ratio <= svtol_actual) {
183 if (n_converged >= requested_number) {
197 if (n_converged + requested_number > k) {
198 k = n_converged + requested_number;
207 Vtmp.leftCols(k).noalias() = V * BV.leftCols(k);
208 V.leftCols(k) = Vtmp.leftCols(k);
218 Wtmp.leftCols(k).noalias() = W * BU.leftCols(k);
219 W.leftCols(k) = Wtmp.leftCols(k);
221 B.setZero(work, work);
222 for (Eigen::Index l = 0; l < k; ++l) {
237 outD.resize(requested_number);
238 outD = svd.singularValues().head(requested_number);
240 outU.resize(matrix.rows(), requested_number);
241 outU.noalias() = W * svd.matrixU().leftCols(requested_number);
243 outV.resize(matrix.cols(), requested_number);
244 outV.noalias() = V * svd.matrixV().leftCols(requested_number);
246 return std::make_pair(converged, iter + 1);
339std::pair<bool, int>
compute(
const Matrix_& matrix,
bool center,
bool scale, Eigen::Index number, EigenMatrix_& outU, EigenMatrix_& outV, EigenVector_& outD,
const Options& options) {
340 if (!scale && !center) {
341 return compute(matrix, number, outU, outV, outD, options);
344 auto nr = matrix.rows();
345 auto nc = matrix.cols();
347 Eigen::VectorXd center0;
350 throw std::runtime_error(
"cannot center with no observations");
355 Eigen::VectorXd scale0;
358 throw std::runtime_error(
"cannot scale with fewer than two observations");
363 for (Eigen::Index i = 0; i < nc; ++i) {
366 mean = matrix.col(i).sum() / nr;
370 EigenVector_ current = matrix.col(i);
371 typename EigenMatrix_::Scalar var = 0;
372 for (
auto x : current) {
373 var += (x - mean)*(x - mean);
377 scale0[i] = std::sqrt(var/(nr - 1));
388 return compute(centered_scaled, number, outU, outV, outD, options);
390 return compute(centered, number, outU, outV, outD, options);
394 return compute(scaled, number, outU, outV, outD, options);