11#ifndef EIGEN_TRIDIAGONAL_BISECTION_H
12#define EIGEN_TRIDIAGONAL_BISECTION_H
15#include "./InternalHeaderCheck.h"
39 enum Kind { All, ByIndex, ByValue };
93template <
int kUnroll,
typename RealScalar>
94EIGEN_STRONG_INLINE
void tridiagonal_sturm_block(
const RealScalar* alpha,
const RealScalar* beta_sq, Index n,
95 RealScalar pivmin,
const RealScalar* eval_points,
96 numext::int64_t* count, Index start) {
97 using Packet =
typename packet_traits<RealScalar>::type;
98 constexpr int kPacketSize = unpacket_traits<Packet>::size;
99 constexpr int kFlushShift = NumTraits<RealScalar>::digits() - 2 < 30 ? NumTraits<RealScalar>::digits() - 2 : 30;
100 constexpr Index kFlushPeriod = Index(1) << kFlushShift;
101 const Packet pivmin_p = pset1<Packet>(pivmin);
102 const Packet neg_pivmin_p = pset1<Packet>(-pivmin);
103 const Packet one_p = pset1<Packet>(RealScalar(1));
105 numext::int64_t* cnt = count + start;
106 Packet x[kUnroll], q[kUnroll], c[kUnroll];
107 const Packet alpha_0 = pset1<Packet>(alpha[0]);
109 for (
int k = 0; k < kUnroll; ++k) {
110 x[k] = ploadu<Packet>(eval_points + start + k * kPacketSize);
111 q[k] = psub(alpha_0, x[k]);
112 const Packet mask = pcmp_le(q[k], pivmin_p);
113 c[k] = pand(one_p, mask);
114 q[k] = pselect(mask, pmin(q[k], neg_pivmin_p), q[k]);
119 RealScalar buf[kPacketSize];
120 auto store_counts = [&](
bool accumulate) {
122 for (
int k = 0; k < kUnroll; ++k) {
125 for (
int l = 0; l < kPacketSize; ++l) {
127 cnt[k * kPacketSize + l] += numext::int64_t(buf[l]);
129 cnt[k * kPacketSize + l] = numext::int64_t(buf[l]);
133 bool flushed =
false;
136 const Index i_end = numext::mini(n, i + kFlushPeriod);
137 for (; i < i_end; ++i) {
138 const Packet alpha_i = pset1<Packet>(alpha[i]);
139 const Packet beta_sq_im1 = pset1<Packet>(beta_sq[i - 1]);
141 for (
int k = 0; k < kUnroll; ++k) {
143 q[k] = psub(psub(alpha_i, pdiv(beta_sq_im1, q[k])), x[k]);
144 const Packet mask = pcmp_le(q[k], pivmin_p);
145 c[k] = padd(c[k], pand(one_p, mask));
146 q[k] = pselect(mask, pmin(q[k], neg_pivmin_p), q[k]);
152 store_counts(flushed);
154 for (
int k = 0; k < kUnroll; ++k) c[k] = pzero(c[k]);
158 store_counts(flushed);
161template <
typename RealScalar>
162void tridiagonal_sturm_counts(
const RealScalar* alpha,
const RealScalar* beta_sq, Index n, RealScalar pivmin,
163 const RealScalar* eval_points, numext::int64_t* count, Index num_points) {
164 using Packet =
typename packet_traits<RealScalar>::type;
165 constexpr Index kPacketSize = Index(unpacket_traits<Packet>::size);
169 num_points &= (std::numeric_limits<Index>::max)();
170 const Index full = num_points / kPacketSize;
176 for (; p + 8 <= full; p += 8)
177 tridiagonal_sturm_block<8>(alpha, beta_sq, n, pivmin, eval_points, count, p * kPacketSize);
178 for (; p + 4 <= full; p += 4)
179 tridiagonal_sturm_block<4>(alpha, beta_sq, n, pivmin, eval_points, count, p * kPacketSize);
180 for (; p + 2 <= full; p += 2)
181 tridiagonal_sturm_block<2>(alpha, beta_sq, n, pivmin, eval_points, count, p * kPacketSize);
182 for (; p + 1 <= full; p += 1)
183 tridiagonal_sturm_block<1>(alpha, beta_sq, n, pivmin, eval_points, count, p * kPacketSize);
187 for (Index j = full * kPacketSize; j < num_points; ++j) {
188 const RealScalar xj = eval_points[j];
189 RealScalar qj = alpha[0] - xj;
190 numext::int64_t cj = 0;
193 qj = numext::mini(qj, -pivmin);
195 for (Index i = 1; i < n; ++i) {
196 qj = (alpha[i] - beta_sq[i - 1] / qj) - xj;
199 qj = numext::mini(qj, -pivmin);
228template <
typename RealScalar>
229void tridiagonal_bisection_block(
const RealScalar* alpha,
const RealScalar* beta_sq, Index n, RealScalar pivmin,
230 RealScalar bracket_lo, RealScalar bracket_hi, Index t_lo, Index t_hi,
int max_iters,
231 RealScalar abs_tol, RealScalar* out) {
232 using ArrayType = Array<RealScalar, Dynamic, 1>;
233 using CountArrayType = Array<numext::int64_t, Dynamic, 1>;
234 const Index m = t_hi - t_lo;
239 ArrayType lower = ArrayType::Constant(m, bracket_lo);
240 ArrayType upper = ArrayType::Constant(m, bracket_hi);
241 const CountArrayType targets = CountArrayType::LinSpaced(m, t_lo, t_hi - 1);
242 CountArrayType counts(m);
243 ArrayType mid = RealScalar(0.5) * (lower + upper);
251 ArrayType result = mid;
252 Array<bool, Dynamic, 1> done = Array<bool, Dynamic, 1>::Constant(m,
false);
261 constexpr int kPacketSize = unpacket_traits<typename packet_traits<RealScalar>::type>::size;
262 bool deduplicate = (m >= 64 * Index(kPacketSize));
264 CountArrayType counts_distinct;
267 counts_distinct.resize(m);
269 for (
int iter = 0; iter < max_iters; ++iter) {
271 const RealScalar* midp = mid.data();
272 RealScalar* distp = distinct.data();
274 distp[nd++] = midp[0];
275 for (Index i = 1; i < m; ++i)
276 if (midp[i] != midp[i - 1]) distp[nd++] = midp[i];
277 tridiagonal_sturm_counts<RealScalar>(alpha, beta_sq, n, pivmin, distp, counts_distinct.data(), nd);
278 const numext::int64_t* cdp = counts_distinct.data();
279 numext::int64_t* cp = counts.data();
282 for (Index i = 1; i < m; ++i) {
283 if (midp[i] != midp[i - 1]) ++g;
286 if (nd == m) deduplicate =
false;
288 tridiagonal_sturm_counts<RealScalar>(alpha, beta_sq, n, pivmin, mid.data(), counts.data(), m);
292 const auto raise = (counts <= targets);
293 lower = raise.select(mid, lower);
294 upper = raise.select(upper, mid);
295 const ArrayType new_mid = RealScalar(0.5) * (lower + upper);
298 const auto converged = (new_mid == mid) || ((upper - lower) <= abs_tol);
299 result = (!done && converged).select(new_mid, result);
300 done = done || converged;
302 if (done.all())
break;
306 Eigen::Map<ArrayType>(out, m) = done.select(result, mid);
329template <
typename RealScalar>
330Index tridiagonal_sturm_count_below(
const RealScalar* alpha,
const RealScalar* beta_sq, Index n, RealScalar pivmin,
332 RealScalar q = alpha[0] - x;
333 Index count = (q < RealScalar(0)) ? 1 : 0;
334 if (numext::abs(q) < pivmin) q = numext::copysign(pivmin, q);
335 for (Index i = 1; i < n; ++i) {
336 q = (alpha[i] - beta_sq[i - 1] / q) - x;
337 if (q < RealScalar(0)) ++count;
338 if (numext::abs(q) < pivmin) q = numext::copysign(pivmin, q);
362template <
typename DiagType,
typename SubdiagType,
typename EivalType>
363Index tridiagonal_bisection(
const DiagType& diag,
const SubdiagType& subdiag,
const EigenvalueRange& range,
364 typename DiagType::Scalar abs_tol, EivalType& eivalues) {
365 using RealScalar =
typename DiagType::Scalar;
366 using ArrayType = Array<RealScalar, Dynamic, 1>;
367 EIGEN_STATIC_ASSERT(NumTraits<RealScalar>::IsInteger == 0 && NumTraits<RealScalar>::IsComplex == 0,
368 THIS_FUNCTION_IS_NOT_FOR_INTEGER_OR_COMPLEX_TYPES)
370 const Index n = diag.size();
372 eivalues.derived().resize(0);
381 RealScalar maxCoeff = safe_scaling<RealScalar>::recover_flushed_max_coeff(diag, diag.cwiseAbs().maxCoeff());
383 maxCoeff = max_preserving_subnormals(
384 maxCoeff, safe_scaling<RealScalar>::recover_flushed_max_coeff(subdiag, subdiag.cwiseAbs().maxCoeff()));
388 ArrayType alpha(n), beta_abs(n - 1);
389 const auto factors = safe_scaling<RealScalar>::scale_to(alpha, diag.array(), maxCoeff);
391 safe_scaling<RealScalar>::scale_to(beta_abs, subdiag.array(), maxCoeff, factors);
394 for (Index i = 0; i < n - 1; ++i) beta_abs(i) = abs_preserving_subnormals(beta_abs(i));
396 const RealScalar scale = factors.scale;
397 const ArrayType beta_sq = (n >= 2) ? ArrayType(beta_abs.square()) : ArrayType(0);
401 const RealScalar eps = NumTraits<RealScalar>::epsilon();
402 const RealScalar safemin = numext::maxi(RealScalar(1) / NumTraits<RealScalar>::highest(),
403 (RealScalar(1) + eps) * (std::numeric_limits<RealScalar>::min)());
404 const RealScalar max_beta_sq = (n >= 2) ? beta_sq.maxCoeff() : RealScalar(0);
405 const RealScalar pivmin = safemin * numext::maxi(max_beta_sq, RealScalar(1));
411 radius(0) = RealScalar(0);
413 radius(0) = beta_abs(0);
414 radius(n - 1) = beta_abs(n - 2);
415 if (n > 2) radius.segment(1, n - 2) = beta_abs.head(n - 2) + beta_abs.segment(1, n - 2);
417 RealScalar lambda_min = (alpha - radius).minCoeff();
418 RealScalar lambda_max = (alpha + radius).maxCoeff();
422 const RealScalar tnorm = numext::maxi(numext::abs(lambda_min), numext::abs(lambda_max));
431 abs_tol = numext::maxi(abs_tol / scale, numext::maxi(eps * tnorm, RealScalar(2) * pivmin));
438 const RealScalar expand = RealScalar(2.1) * (RealScalar(n) * eps * tnorm + RealScalar(2) * pivmin);
439 lambda_min -= expand;
440 lambda_max += expand;
443 Index t_lo = 0, t_hi = n;
444 RealScalar bracket_lo = lambda_min, bracket_hi = lambda_max;
445 bool value_filter =
false;
446 RealScalar vl_n = RealScalar(0), vu_n = RealScalar(0);
447 RealScalar end_tol_lo = RealScalar(0), end_tol_hi = RealScalar(0);
448 if (range.kind == EigenvalueRange::ByIndex) {
449 eigen_assert(range.il >= 0 && range.il <= range.iu && range.iu <= n &&
"invalid eigenvalue index range");
452 }
else if (range.kind == EigenvalueRange::ByValue) {
453 eigen_assert(range.vl <= range.vu &&
"invalid eigenvalue value range");
454 vl_n = RealScalar(range.vl) / scale;
455 vu_n = RealScalar(range.vu) / scale;
466 if ((numext::isfinite)(vl_n)) {
468 RealScalar(2.1) * (RealScalar(n) * eps * numext::maxi(tnorm, numext::abs(vl_n)) + RealScalar(2) * pivmin) +
470 t_lo = tridiagonal_sturm_count_below<RealScalar>(alpha.data(), beta_sq.data(), n, pivmin,
471 vl_n - RealScalar(2) * end_tol_lo);
472 bracket_lo = numext::maxi(lambda_min, vl_n - RealScalar(2) * end_tol_lo);
474 t_lo = (vl_n < RealScalar(0)) ? 0 : n;
476 if ((numext::isfinite)(vu_n)) {
478 RealScalar(2.1) * (RealScalar(n) * eps * numext::maxi(tnorm, numext::abs(vu_n)) + RealScalar(2) * pivmin) +
480 t_hi = tridiagonal_sturm_count_below<RealScalar>(alpha.data(), beta_sq.data(), n, pivmin, vu_n + end_tol_hi);
481 bracket_hi = numext::mini(lambda_max, vu_n + RealScalar(2) * end_tol_hi);
483 t_hi = (vu_n > RealScalar(0)) ? n : 0;
488 const Index m = t_hi - t_lo;
489 eivalues.derived().resize(m);
490 if (m <= 0)
return 0;
492 const int max_iters = NumTraits<RealScalar>::digits() + 2;
493 ArrayType mid_all(m);
494 RealScalar* out = mid_all.data();
495 const RealScalar* alpha_p = alpha.data();
496 const RealScalar* beta_sq_p = beta_sq.data();
501#if defined(EIGEN_HAS_OPENMP)
505 if (omp_get_num_threads() == 1) {
506 constexpr Index kPacketSize = Index(unpacket_traits<
typename packet_traits<RealScalar>::type>::size);
509 const double work = double(m) * double(n) * double(max_iters);
510 const double kMinTaskSize = 131072.0;
511 const Index work_threads = Index(work / kMinTaskSize);
512 const Index point_threads = m / (8 * kPacketSize);
513 const Index pb = numext::maxi(Index(1), numext::mini(work_threads, point_threads));
514 nthreads = int(numext::mini(pb, Index(Eigen::nbThreads())));
517#pragma omp parallel num_threads(nthreads)
519 const Index nt = omp_get_num_threads();
520 const Index tid = omp_get_thread_num();
522 const Index lo = tid * m / nt;
523 const Index hi = (tid + 1) * m / nt;
524 tridiagonal_bisection_block<RealScalar>(alpha_p, beta_sq_p, n, pivmin, bracket_lo, bracket_hi, t_lo + lo,
525 t_lo + hi, max_iters, abs_tol, out + lo);
530 tridiagonal_bisection_block<RealScalar>(alpha_p, beta_sq_p, n, pivmin, bracket_lo, bracket_hi, t_lo, t_hi,
531 max_iters, abs_tol, out);
539 for (Index j = 1; j < m; ++j) out[j] = numext::maxi(out[j], out[j - 1]);
550 const RealScalar keep_lo = (numext::isfinite)(vl_n) ? vl_n - end_tol_lo : vl_n;
551 const RealScalar keep_hi = (numext::isfinite)(vu_n) ? vu_n - end_tol_hi : vu_n;
553 for (Index j = 0; j < m; ++j)
554 if (out[j] >= keep_lo && out[j] < keep_hi) out[k++] = out[j];
556 if (m_out != m) eivalues.derived().resize(m_out);
560 eivalues = mid_all.head(m_out).matrix();
561 safe_scaling<RealScalar>::unscale_in_place(eivalues, maxCoeff, factors);
Selects which eigenvalues to compute in a spectral-bisection solve.
Definition TridiagonalBisection.h:38
static EigenvalueRange all()
Definition TridiagonalBisection.h:50
static EigenvalueRange indices(Index il, Index iu)
Definition TridiagonalBisection.h:53
static EigenvalueRange values(long double vl, long double vu)
Definition TridiagonalBisection.h:56