78inline Eigen::Index update_k(Eigen::Index k,
const Eigen::Index requested_number,
const Eigen::Index n_converged,
const Eigen::Index work) {
143 const Matrix_& matrix,
144 const Eigen::Index number,
150 const Eigen::Index smaller = std::min(matrix.rows(), matrix.cols());
151 Eigen::Index requested_number = sanisizer::cast<Eigen::Index>(number);
152 if (requested_number > smaller) {
154 requested_number = smaller;
156 throw std::runtime_error(
"requested number of singular values cannot be greater than the smaller matrix dimension");
159 throw std::runtime_error(
"requested number of singular values must be less than the smaller matrix dimension for IRLBA iterations");
166 (options.
exact_for_large_number && requested_greater_than_or_equal_to_half_smaller(requested_number, smaller))
168 exact(matrix, requested_number, outU, outV, outD);
169 return std::make_pair(
true, 0);
172 Eigen::Index work = choose_requested_plus_extra_work_or_smaller(requested_number, options.
extra_work, smaller);
174 throw std::runtime_error(
"number of requested dimensions must be positive");
179 EigenMatrix_ V(matrix.cols(), work);
180 std::mt19937_64 eng(options.
seed);
181 if (options.
initial.has_value()) {
182 const auto& init = *(options.
initial);
183 if (init.size() != V.rows()) {
184 throw std::runtime_error(
"initialization vector does not have expected number of rows");
188 fill_with_random_normals(V, 0, eng);
190 V.col(0) /= V.col(0).norm();
192 bool converged =
false;
195 JacobiSVD<EigenMatrix_> svd(work, work);
197 LanczosWorkspace<EigenVector_, Matrix_> lpwork(matrix);
199 EigenMatrix_ W(matrix.rows(), work);
200 EigenMatrix_ Wtmp(matrix.rows(), work);
201 EigenMatrix_ Vtmp(matrix.cols(), work);
203 EigenMatrix_ B(work, work);
204 B.setZero(work, work);
205 EigenVector_ res(work);
206 EigenVector_ F(matrix.cols());
208 EigenVector_ prevS(work);
211 typename EigenMatrix_::Scalar svtol_actual = (svtol >= 0 ? svtol : tol);
217 run_lanczos_bidiagonalization(lpwork, W, V, B, eng, k, options);
226 const auto& BS = svd.singularValues();
227 const auto& BU = svd.matrixU();
228 const auto& BV = svd.matrixV();
231 if (B(work - 1, work - 1) == 0) {
236 const auto& F = lpwork.F;
244 res = R_F * BU.row(work - 1);
246 Eigen::Index n_converged = 0;
248 const auto Smax = *std::max_element(BS.begin(), BS.end());
249 const auto threshold = Smax * tol;
251 for (Eigen::Index j = 0; j < work; ++j) {
252 if (std::abs(res[j]) <= threshold) {
253 const auto ratio = std::abs(BS[j] - prevS[j]) / BS[j];
254 if (ratio <= svtol_actual) {
260 if (n_converged >= requested_number) {
267 k = update_k(k, requested_number, n_converged, work);
268 Vtmp.leftCols(k).noalias() = V * BV.leftCols(k);
269 V.leftCols(k) = Vtmp.leftCols(k);
279 Wtmp.leftCols(k).noalias() = W * BU.leftCols(k);
280 W.leftCols(k) = Wtmp.leftCols(k);
282 B.setZero(work, work);
283 for (I<
decltype(k)> l = 0; l < k; ++l) {
298 outD.resize(requested_number);
299 outD = svd.singularValues().head(requested_number);
301 outU.resize(matrix.rows(), requested_number);
302 outU.noalias() = W * svd.matrixU().leftCols(requested_number);
304 outV.resize(matrix.cols(), requested_number);
305 outV.noalias() = V * svd.matrixV().leftCols(requested_number);
307 return std::make_pair(converged, (converged ? iter + 1 : iter));