11#ifndef EIGEN_TRIDIAGONAL_INVERSE_ITERATION_H
12#define EIGEN_TRIDIAGONAL_INVERSE_ITERATION_H
14#include "./SelfAdjointEigenSolver.h"
16#include "./TridiagonalBisection.h"
19#include "./InternalHeaderCheck.h"
34struct inverse_iteration_rng {
35 numext::uint64_t state;
37 explicit inverse_iteration_rng(numext::uint64_t seed) : state(seed) {}
39 template <
typename RealScalar>
44 state += 0x9E3779B97F4A7C15ULL;
45 const numext::uint64_t z = splitmix64_mix(state);
50 const numext::uint32_t hi = numext::uint32_t(z >> 32);
51 const RealScalar u = RealScalar(hi) * (RealScalar(1) / RealScalar(4294967296.0));
52 return RealScalar(2) * u - RealScalar(1);
83template <
typename RealScalar>
84void tridiagonal_lagtf(RealScalar* d, RealScalar* du, RealScalar* dl, RealScalar* du2, Index* piv, RealScalar lambda,
90 RealScalar scale1 = numext::abs(d[0]) + numext::abs(du[0]);
91 for (Index k = 0; k < n - 1; ++k) {
93 RealScalar scale2 = numext::abs(dl[k]) + numext::abs(d[k + 1]);
94 if (k < n - 2) scale2 += numext::abs(du[k + 1]);
96 const RealScalar piv1 = numext::is_exactly_zero(d[k]) ? RealScalar(0) : numext::abs(d[k]) / scale1;
97 if (numext::is_exactly_zero(dl[k])) {
100 if (k < n - 2) du2[k] = RealScalar(0);
102 const RealScalar piv2 = numext::abs(dl[k]) / scale2;
107 dl[k] = dl[k] / d[k];
108 d[k + 1] -= dl[k] * du[k];
109 if (k < n - 2) du2[k] = RealScalar(0);
113 const RealScalar mult = d[k] / dl[k];
115 const RealScalar temp = d[k + 1];
116 d[k + 1] = du[k] - mult * temp;
119 du[k + 1] = -mult * du2[k];
149template <
typename RealScalar>
150void tridiagonal_lagts(
const RealScalar* d,
const RealScalar* rcp,
const RealScalar* du,
const RealScalar* dl,
151 const RealScalar* du2,
const Index* piv, RealScalar* b, Index n) {
152 const RealScalar eps = NumTraits<RealScalar>::epsilon();
153 const RealScalar sfmin = (std::numeric_limits<RealScalar>::min)();
154 const RealScalar bignum = RealScalar(1) / sfmin;
157 using ArrayBuf = Map<const Array<RealScalar, Dynamic, 1>>;
158 RealScalar tol = ArrayBuf(d, n).abs().maxCoeff();
159 if (n > 1) tol = numext::maxi(tol, ArrayBuf(du, n - 1).abs().maxCoeff());
160 if (n > 2) tol = numext::maxi(tol, ArrayBuf(du2, n - 2).abs().maxCoeff());
162 if (numext::is_exactly_zero(tol)) tol = eps;
167 for (Index k = 1; k < n; ++k) {
168 const RealScalar dlk = dl[k - 1];
169 const RealScalar bkm1 = b[k - 1];
170 const RealScalar bk = b[k];
171 const bool interchange = piv[k - 1] != 0;
172 b[k - 1] = interchange ? bk : bkm1;
173 b[k] = interchange ? (bkm1 - dlk * bk) : (bk - dlk * bkm1);
182 for (Index k = n - 1; k >= 0; --k) {
183 RealScalar temp = b[k];
184 if (k <= n - 2) temp -= du[k] * b[k + 1];
185 if (k <= n - 3) temp -= du2[k] * b[k + 2];
186 const RealScalar ak = d[k];
187 const RealScalar absak = numext::abs(ak);
188 if (EIGEN_PREDICT_TRUE(absak >= RealScalar(1) || (absak >= sfmin && numext::abs(temp) <= absak * bignum))) {
189 b[k] = temp * rcp[k];
194 RealScalar pert = (a >= RealScalar(0)) ? tol : -tol;
196 const RealScalar aa = numext::abs(a);
197 if (aa < RealScalar(1)) {
199 if (numext::is_exactly_zero(aa) || numext::abs(t) * sfmin > aa) {
201 pert *= RealScalar(2);
207 }
else if (numext::abs(t) > aa * bignum) {
209 pert *= RealScalar(2);
246template <
typename RealScalar,
typename EivecType>
247Index tridiagonal_inverse_iteration_block(
const RealScalar* sdiag,
const RealScalar* ssub,
const RealScalar* xj_scaled,
248 const Index* clstart, Index n, RealScalar onenrm, RealScalar dtpcrt,
249 int maxits,
int extra, EivecType& eivecs, Index j_lo, Index j_hi) {
250 using RealVectorType = Matrix<RealScalar, Dynamic, 1>;
251 const RealScalar eps = NumTraits<RealScalar>::epsilon();
256 RealVectorType lu_d(n), lu_dl(n), lu_du(n), lu_du2(n), lu_rcp(n), b(n);
257 Matrix<Index, Dynamic, 1> piv(n);
260 for (Index j = j_lo; j < j_hi; ++j) {
261 const RealScalar xj = xj_scaled[j];
262 const Index gpind = clstart[j];
265 lu_d = Map<const RealVectorType>(sdiag, n);
266 lu_du.head(n - 1) = Map<const RealVectorType>(ssub, n - 1);
267 lu_dl.head(n - 1) = Map<const RealVectorType>(ssub, n - 1);
268 tridiagonal_lagtf<RealScalar>(lu_d.data(), lu_du.data(), lu_dl.data(), lu_du2.data(), piv.data(), xj, n);
271 lu_rcp.array() = RealScalar(1) / lu_d.array();
274 inverse_iteration_rng rng(numext::uint64_t(j) + 1);
275 for (Index i = 0; i < n; ++i) b[i] = rng.template next<RealScalar>();
278 bool converged =
false;
279 for (
int its = 0; its < maxits; ++its) {
281 const RealScalar bmax = b.cwiseAbs().maxCoeff();
282 if (numext::is_exactly_zero(bmax))
break;
283 const RealScalar target = RealScalar(n) * onenrm * numext::maxi(eps, numext::abs(lu_d[n - 1]));
284 const RealScalar scl = target / bmax;
285 if (EIGEN_PREDICT_TRUE(scl >= (std::numeric_limits<RealScalar>::min)())) {
294 tridiagonal_lagts<RealScalar>(lu_d.data(), lu_rcp.data(), lu_du.data(), lu_dl.data(), lu_du2.data(), piv.data(),
298 for (Index i = gpind; i < j; ++i) b -= b.dot(eivecs.col(i)) * eivecs.col(i);
300 const RealScalar nrm = b.cwiseAbs().maxCoeff();
301 if (nrm < dtpcrt)
continue;
302 if (++nrmchk < extra + 1)
continue;
308 if (!converged) ++nonconv;
314 const RealScalar binf = b.cwiseAbs().maxCoeff(&jmax);
315 safe_scaling<RealScalar>::scale_to(b, b, binf);
316 const RealScalar nrm2 = b.norm();
317 RealScalar scl = numext::is_exactly_zero(nrm2) ? RealScalar(1) : RealScalar(1) / nrm2;
318 if (b[jmax] < RealScalar(0)) scl = -scl;
319 eivecs.col(j) = b * scl;
345template <
typename DiagType,
typename SubdiagType,
typename EivalType,
typename EivecType>
346Index tridiagonal_inverse_iteration_connected(
const DiagType& diag,
const SubdiagType& subdiag,
const EivalType& eivals,
348 using RealScalar =
typename DiagType::Scalar;
349 EIGEN_STATIC_ASSERT(NumTraits<RealScalar>::IsInteger == 0 && NumTraits<RealScalar>::IsComplex == 0,
350 THIS_FUNCTION_IS_NOT_FOR_INTEGER_OR_COMPLEX_TYPES)
352 const Index n = diag.size();
353 const Index m = eivals.size();
354 if (n == 0 || m == 0)
return 0;
360 const RealScalar eps = NumTraits<RealScalar>::epsilon();
365 const RealScalar maxCoeff = max_preserving_subnormals(
366 safe_scaling<RealScalar>::recover_flushed_max_coeff(diag, diag.cwiseAbs().maxCoeff()),
367 safe_scaling<RealScalar>::recover_flushed_max_coeff(subdiag, subdiag.cwiseAbs().maxCoeff()));
368 Matrix<RealScalar, Dynamic, 1> sdiag(n), ssub(n - 1);
369 const auto factors = safe_scaling<RealScalar>::scale_to(sdiag, diag, maxCoeff);
370 safe_scaling<RealScalar>::scale_to(ssub, subdiag, maxCoeff, factors);
371 Matrix<RealScalar, Dynamic, 1> xj_scaled(m);
372 safe_scaling<RealScalar>::scale_to(xj_scaled, eivals, maxCoeff, factors);
373 if (maxCoeff > RealScalar(0) && maxCoeff < (std::numeric_limits<RealScalar>::min)()) {
376 const RealScalar scaledMax = numext::maxi(sdiag.cwiseAbs().maxCoeff(), ssub.cwiseAbs().maxCoeff());
377 const auto remaining = safe_scaling<RealScalar>::scale_in_place(sdiag, scaledMax);
378 safe_scaling<RealScalar>::scale_in_place(ssub, scaledMax, remaining);
379 safe_scaling<RealScalar>::scale_in_place(xj_scaled, scaledMax, remaining);
383 RealScalar onenrm = numext::abs(sdiag[0]) + numext::abs(ssub[0]);
384 onenrm = numext::maxi(onenrm, numext::abs(sdiag[n - 1]) + numext::abs(ssub[n - 2]));
386 onenrm = numext::maxi(onenrm, (ssub.head(n - 2).array().abs() + sdiag.segment(1, n - 2).array().abs() +
387 ssub.segment(1, n - 2).array().abs())
389 if (numext::is_exactly_zero(onenrm)) onenrm = RealScalar(1);
392 const RealScalar ortol = RealScalar(1e-3) * onenrm;
393 const RealScalar dtpcrt = numext::sqrt(RealScalar(0.1) / RealScalar(n));
394 const int maxits = 5;
403 Matrix<Index, Dynamic, 1> clstart(m);
406 RealScalar xjm = RealScalar(0);
407 for (Index j = 0; j < m; ++j) {
408 RealScalar xj = xj_scaled[j];
415 const RealScalar pertol = numext::mini(RealScalar(10) * numext::abs(eps * xj), RealScalar(0.25) * ortol);
416 if (xj - xjm < pertol) xj = xjm + pertol;
418 if (j == 0 || numext::abs(xj - xjm) > ortol) gpind = j;
425 const RealScalar* sdiag_p = sdiag.data();
426 const RealScalar* ssub_p = ssub.data();
427 const RealScalar* xj_p = xj_scaled.data();
428 const Index* cl_p = clstart.data();
436#if defined(EIGEN_HAS_OPENMP)
440 if (omp_get_num_threads() == 1) {
445 const double work = double(m) * double(n);
446 const double kMinTaskSize = 2048.0;
447 const Index work_threads = Index(work / kMinTaskSize);
448 nthreads = int(numext::maxi(Index(1), numext::mini(work_threads, Index(Eigen::nbThreads()))));
451#pragma omp parallel num_threads(nthreads) reduction(+ : nonconv)
453 const Index nt = omp_get_num_threads();
454 const Index tid = omp_get_thread_num();
458 Index lo = tid * m / nt;
459 Index hi = (tid + 1) * m / nt;
460 while (lo < m && cl_p[lo] != lo) ++lo;
461 while (hi < m && cl_p[hi] != hi) ++hi;
462 nonconv += tridiagonal_inverse_iteration_block<RealScalar>(sdiag_p, ssub_p, xj_p, cl_p, n, onenrm, dtpcrt, maxits,
463 extra, eivecs, lo, hi);
468 nonconv = tridiagonal_inverse_iteration_block<RealScalar>(sdiag_p, ssub_p, xj_p, cl_p, n, onenrm, dtpcrt, maxits,
469 extra, eivecs, 0, m);
513template <
typename DiagType,
typename SubdiagType,
typename EivalType,
typename EivecType>
514Index tridiagonal_inverse_iteration(
const DiagType& diag,
const SubdiagType& subdiag,
const EivalType& eivals,
516 using RealScalar =
typename DiagType::Scalar;
517 using VectorType = Matrix<RealScalar, Dynamic, 1>;
518 const Index n = diag.size();
519 const Index m = eivals.size();
520 if (n == 0 || m == 0)
return 0;
529 RealScalar maxCoeff = safe_scaling<RealScalar>::recover_flushed_max_coeff(diag, diag.cwiseAbs().maxCoeff());
531 maxCoeff = max_preserving_subnormals(
532 maxCoeff, safe_scaling<RealScalar>::recover_flushed_max_coeff(subdiag, subdiag.cwiseAbs().maxCoeff()));
536 const int e = frexp_exponent_preserving_subnormals(maxCoeff);
537 if (e != 0 && e <= safe_scaling<RealScalar>::subnormal_recovery_exponent()) {
538 VectorType sdiag(n), ssub(n - 1), seivals(m);
539 const auto factors = safe_scaling<RealScalar>::scale_to(sdiag, diag, maxCoeff);
540 safe_scaling<RealScalar>::scale_to(ssub, subdiag, maxCoeff, factors);
541 safe_scaling<RealScalar>::scale_to(seivals, eivals, maxCoeff, factors);
542 return tridiagonal_inverse_iteration(sdiag, ssub, seivals, eivecs);
551 const RealScalar eps = NumTraits<RealScalar>::epsilon();
552 Matrix<Index, Dynamic, 1> bstart(n + 1);
556 const Array<bool, Dynamic, 1> split = (subdiag.array().abs() <= eps * (diag.head(n - 1).array().abs().sqrt() *
557 diag.tail(n - 1).array().abs().sqrt()));
558 for (Index k = 0; k + 1 < n; ++k)
559 if (split(k)) bstart(nblocks++) = k + 1;
563 if (nblocks == 1)
return tridiagonal_inverse_iteration_connected(diag, subdiag, eivals, eivecs);
571 const RealScalar safemin = numext::maxi(RealScalar(1) / NumTraits<RealScalar>::highest(),
572 (RealScalar(1) + eps) * (std::numeric_limits<RealScalar>::min)());
573 VectorType alpha_all(n), beta_sq_all(n), bscale(nblocks), bpivmin(nblocks), btol(nblocks);
574 RealScalar gscale = RealScalar(0);
575 for (Index b = 0; b < nblocks; ++b) {
576 const Index b0 = bstart(b), nb = bstart(b + 1) - b0;
577 RealScalar s = diag.segment(b0, nb).cwiseAbs().maxCoeff();
578 if (nb > 1) s = numext::maxi(s, subdiag.segment(b0, nb - 1).cwiseAbs().maxCoeff());
579 if (numext::is_exactly_zero(s)) s = RealScalar(1);
580 gscale = numext::maxi(gscale, s);
581 auto alpha = alpha_all.segment(b0, nb);
582 const auto factors = safe_scaling<RealScalar>::scale_to(alpha, diag.segment(b0, nb), s);
583 bscale(b) = factors.scale;
584 RealScalar max_bsq = RealScalar(0);
586 auto beta = beta_sq_all.segment(b0, nb - 1);
587 safe_scaling<RealScalar>::scale_to(beta, subdiag.segment(b0, nb - 1), s, factors);
588 beta = beta.array().square();
589 max_bsq = beta.maxCoeff();
591 bpivmin(b) = safemin * numext::maxi(max_bsq, RealScalar(1));
593 btol(b) = RealScalar(2.1) * (RealScalar(3) * RealScalar(nb) * eps + RealScalar(4) * safemin) * s;
608 const RealScalar gtol = RealScalar(2.1) * (RealScalar(3) * RealScalar(n) * eps + RealScalar(4) * safemin) * gscale;
609 Matrix<Index, Dynamic, 1> blockof(m), assigned(nblocks), caps(nblocks), below_prev(nblocks), below_cur(nblocks),
610 below_edge(nblocks), carry(m), carry_next(m);
612 Matrix<Index, Dynamic, 1> local_index(m);
613 Array<bool, Dynamic, 1> claimed = Array<bool, Dynamic, 1>::Constant(n,
false);
616 auto count_below = [&](Index b, RealScalar x) -> Index {
617 const Index b0 = bstart(b), nb = bstart(b + 1) - b0;
618 return tridiagonal_sturm_count_below<RealScalar>(alpha_all.data() + b0, beta_sq_all.data() + b0, nb, bpivmin(b),
621 for (Index b = 0; b < nblocks; ++b) below_prev(b) = count_below(b, eivals[0] - btol(b));
622 below_edge = below_prev;
625 while (g_begin < m) {
626 Index g_end = g_begin + 1;
627 while (g_end < m && !(eivals[g_begin] < eivals[g_end])) ++g_end;
629 const bool first = (g_begin == 0);
630 const bool last = (g_end >= m);
631 const RealScalar mid_bound =
632 last ? RealScalar(0) : RealScalar(0.5) * eivals[g_end - 1] + RealScalar(0.5) * eivals[g_end];
633 bool widened =
false;
636 for (Index b = 0; b < nblocks; ++b) {
637 const RealScalar bound = last ? eivals[m - 1] + (widened ? gtol : btol(b)) : mid_bound;
638 below_cur(b) = count_below(b, bound);
639 caps(b) = numext::maxi(Index(0), below_cur(b) - below_prev(b));
644 if (widened || !(first || last) || total >= g_end - g_begin + (last ? ncarry : 0))
break;
647 for (Index b = 0; b < nblocks; ++b) below_prev(b) = count_below(b, eivals[0] - gtol);
648 below_edge = below_prev;
652 Index ncarry_next = 0;
653 for (Index pass = 0; pass < 2; ++pass) {
654 const Index count = (pass == 0) ? g_end - g_begin : ncarry;
655 for (Index c = 0; c < count; ++c) {
656 const Index j = (pass == 0) ? g_begin + c : carry(c);
658 for (Index b = 0; b < nblocks && chosen < 0; ++b)
659 if (caps(b) > 0) chosen = b;
661 carry_next(ncarry_next++) = j;
664 const Index index = below_cur(chosen) - caps(chosen);
665 local_index(j) = index;
666 claimed(bstart(chosen) + index) =
true;
672 carry.swap(carry_next);
673 ncarry = ncarry_next;
674 below_prev = below_cur;
678 for (Index c = 0; c < ncarry; ++c) {
679 const Index j = carry(c);
681 for (Index b = 0; b < nblocks && chosen < 0; ++b)
682 if (below_cur(b) - below_edge(b) - assigned(b) > 0) chosen = b;
684 for (Index b = 0; b < nblocks && chosen < 0; ++b)
685 if (bstart(b + 1) - bstart(b) - assigned(b) > 0) chosen = b;
686 if (chosen < 0) chosen = 0;
688 const Index b0 = bstart(chosen), nb = bstart(chosen + 1) - b0;
689 Index index = numext::mini(below_edge(chosen), nb - 1);
690 while (index < nb && claimed(b0 + index)) ++index;
693 while (index < nb && claimed(b0 + index)) ++index;
699 local_index(j) = index;
700 claimed(b0 + index) =
true;
709 VectorType wloc, refined, endpoints;
710 Matrix<numext::int64_t, Dynamic, 1> counts;
711 Matrix<Index, Dynamic, 1> indices;
712 Matrix<RealScalar, Dynamic, Dynamic> vloc;
713 Matrix<Index, Dynamic, 1> colmap(m);
714 for (Index b = 0; b < nblocks; ++b) {
715 const Index b0 = bstart(b), nb = bstart(b + 1) - b0;
717 for (Index j = 0; j < m; ++j)
718 if (blockof(j) == b) colmap(mb++) = j;
719 if (mb == 0)
continue;
721 for (Index k = 0; k < mb; ++k) wloc(k) = eivals[colmap(k)];
723 const VectorType bdiag = diag.segment(b0, nb);
724 const VectorType bsub = subdiag.segment(b0, nb > 1 ? nb - 1 : 0);
729 for (Index k = 0; k < mb; ++k) indices(k) = local_index(colmap(k));
730 std::sort(indices.data(), indices.data() + mb);
731 endpoints.resize(2 * mb);
732 counts.resize(2 * mb);
733 endpoints.head(mb) = (wloc.array() - btol(b)) / bscale(b);
734 endpoints.tail(mb) = (wloc.array() + btol(b)) / bscale(b);
735 tridiagonal_sturm_counts(alpha_all.data() + b0, beta_sq_all.data() + b0, nb, bpivmin(b), endpoints.data(),
736 counts.data(), 2 * mb);
737 bool needs_refinement =
false;
740 for (Index k = 0; k < mb && !needs_refinement; ++k) {
741 needs_refinement = counts(k) > indices(k) || counts(mb + k) <= indices(k);
743 if (needs_refinement) {
744 for (Index first = 0; first < mb;) {
745 Index last = first + 1;
746 while (last < mb && indices(last) == indices(last - 1) + 1) ++last;
748 RealScalar(0), refined);
749 wloc.segment(first, last - first) = refined;
754 nonconv += tridiagonal_inverse_iteration_connected(bdiag, bsub, wloc, vloc);
755 for (Index k = 0; k < mb; ++k) eivecs.col(colmap(k)).segment(b0, nb) = vloc.col(k);
794template <
typename DiagType,
typename SubdiagType,
typename EivalType,
typename EivecType>
795void tridiagonal_rayleigh_ritz_refine(
const DiagType& diag,
const SubdiagType& subdiag,
const EivalType& eivals,
797 using RealScalar =
typename DiagType::Scalar;
798 using DenseType = Matrix<RealScalar, Dynamic, Dynamic>;
799 const Index n = diag.size();
800 const Index m = eivals.size();
801 if (n < 2 || m < 2)
return;
804 RealScalar onenrm = numext::abs(diag[0]) + numext::abs(subdiag[0]);
805 onenrm = numext::maxi(onenrm, numext::abs(diag[n - 1]) + numext::abs(subdiag[n - 2]));
806 for (Index i = 1; i < n - 1; ++i)
807 onenrm = numext::maxi(onenrm, numext::abs(subdiag[i - 1]) + numext::abs(diag[i]) + numext::abs(subdiag[i]));
808 const RealScalar ortol = RealScalar(1e-3) * onenrm;
809 if (!(ortol > RealScalar(0)))
return;
810 const RealScalar inv_onenrm = RealScalar(1) / onenrm;
811 const RealScalar refine_threshold = RealScalar(16) * NumTraits<RealScalar>::epsilon();
813 SelfAdjointEigenSolver<DenseType> block_solver;
818 DenseType Vc, TVc, B, Bsym;
822 while (t + 1 < m && (eivals[t + 1] - eivals[t]) < ortol) ++t;
823 const Index c = t - s + 1;
827 Vc = eivecs.middleCols(s, c);
828 TVc = diag.asDiagonal() * Vc;
829 TVc.topRows(n - 1) += subdiag.asDiagonal() * Vc.bottomRows(n - 1);
830 TVc.bottomRows(n - 1) += subdiag.asDiagonal() * Vc.topRows(n - 1);
834 const RealScalar cluster_resid =
835 ((TVc - Vc * eivals.segment(s, c).asDiagonal()) * inv_onenrm).colwise().norm().maxCoeff();
836 if (cluster_resid > refine_threshold) {
837 B.noalias() = Vc.transpose() * TVc;
838 Bsym = B + B.transpose();
839 Bsym *= RealScalar(0.5);
841 eivecs.middleCols(s, c).noalias() = Vc * block_solver.eigenvectors();
@ ComputeEigenvectors
Definition Constants.h:406
static EigenvalueRange indices(Index il, Index iu)
Definition TridiagonalBisection.h:53