1#ifndef STAN_MATH_OPENCL_MRRR_HPP
2#define STAN_MATH_OPENCL_MRRR_HPP
34 return almost_one + std::rand() * rand_norm;
46 const Eigen::Ref<const Eigen::VectorXd> subdiagonal,
47 double& min_eigval,
double& max_eigval) {
52 for (
int i = 1; i < n - 1; i++) {
53 min_eigval = std::min(min_eigval,
diagonal[i] -
fabs(subdiagonal[i])
54 -
fabs(subdiagonal[i - 1]));
55 max_eigval = std::max(max_eigval,
diagonal[i] +
fabs(subdiagonal[i])
56 +
fabs(subdiagonal[i - 1]));
58 min_eigval = std::min(min_eigval,
diagonal[n - 1] -
fabs(subdiagonal[n - 2]));
59 max_eigval = std::max(max_eigval,
diagonal[n - 1] +
fabs(subdiagonal[n - 2]));
69inline double max_nan(
double a,
double b) {
return isnan(a) || a > b ? a : b; }
84 const Eigen::Ref<const Eigen::VectorXd> subdiagonal,
88 double element_growth =
fabs(d_plus[0].high);
89 for (
int i = 0; i < subdiagonal.size(); i++) {
90 l[i] = subdiagonal[i] / d_plus[i];
91 d_plus[i + 1] =
diagonal[i + 1] - shift - l[i] * subdiagonal[i];
92 element_growth =
max_nan(element_growth,
fabs(d_plus[i + 1].high));
94 return element_growth;
110 const Eigen::Ref<const Eigen::VectorXd>
diagonal,
111 const Eigen::Ref<const Eigen::VectorXd> subdiagonal,
VectorXdd& l0,
112 VectorXdd& d0,
const double min_eigval,
const double max_eigval,
113 const double max_ele_growth) {
114 double shift = (max_eigval + min_eigval) * 0.5;
116 if (element_growth < max_ele_growth) {
119 double plus = (max_eigval - min_eigval) * 1
e-15;
120 while (!(element_growth < max_ele_growth)) {
142 const int n = l.size();
146 for (
int i = 0; i < n; i++) {
147 d_plus = s + d.coeff(i);
148 count += d_plus >= 0;
151 s = l.coeff(i) * l.coeff(i) * d.coeff(i) - shift;
153 s = l.coeff(i) * l.coeff(i) * s * (d.coeff(i) / d_plus) - shift;
156 d_plus = s + d.coeff(n);
157 count += d_plus >= 0;
174 while ((high - low) > eps *
fabs(high + low)
176 > std::numeric_limits<double>::min()) {
207 const int n = l.size();
209 double element_growth = 0;
210 for (
int i = 0; i < n; i++) {
211 d_plus[i] = s + d[i];
212 element_growth =
max_nan(element_growth,
fabs(d_plus[i].high));
213 l_plus[i] = l[i] * (d[i] / d_plus[i]);
217 s = l[i] * l[i] * d[i] - shift;
219 s = l_plus[i] * l[i] * s - shift;
222 d_plus[n] = s + d[n];
223 element_growth =
max_nan(element_growth,
fabs(d_plus[n].high));
224 return element_growth;
244 const double max_ele_growth,
const double_d max_shift,
246 double& min_element_growth) {
248 const std::vector<double_d> shifts = {
250 high - max_shift * 0.1,
251 low + max_shift * 0.1,
252 high - max_shift * 0.25,
253 low + max_shift * 0.25,
254 high - max_shift * 0.5,
255 low + max_shift * 0.5,
256 high - max_shift * 0.75,
257 low + max_shift * 0.75,
261 min_element_growth = std::numeric_limits<double>::infinity();
264 if (element_growth < min_element_growth) {
268 min_element_growth = element_growth;
269 if (element_growth <= max_ele_growth) {
297template <
bool need_eigenvectors = true>
299 const Eigen::Ref<const Eigen::VectorXd> subdiagonal,
302 const double min_rel_sep = 1
e-4,
303 const double maximum_ele_growth = 15) {
306 const double shift_error = 1
e-19;
310 const Eigen::VectorXd subdiagonal_squared
311 = subdiagonal.array() * subdiagonal.array();
313 if (!need_eigenvectors) {
319 subdiagonal_squared_cl, l_cl, d_cl,
320 eigenvalues_cl, low_cl, high_cl, min_eigval,
321 max_eigval, 0, need_eigenvectors);
326 const double max_ele_growth = maximum_ele_growth * (max_eigval - min_eigval);
328 diagonal, subdiagonal, l, d, min_eigval, max_eigval, max_ele_growth);
329 for (
int i = 0; i < n; i++) {
345 l_cl, d_cl, eigenvalues_cl, low_cl, high_cl,
346 min_eigval, max_eigval, shift0, need_eigenvectors);
353 std::queue<mrrr_task> block_queue;
354 block_queue.push(
mrrr_task{0, n, {shift0, 0}, std::move(l), std::move(d), 0});
357 while (!block_queue.empty()) {
360 double_d shift = std::numeric_limits<double>::infinity();
361 double min_element_growth = std::numeric_limits<double>::infinity();
362 VectorXdd l2(n - 1), d2(n), l_plus(n - 1), u_minus(n - 1);
363 for (
int i =
block.start; i <
block.end; i++) {
365 for (cluster_end = i + 1; cluster_end <
block.end; cluster_end++) {
366 const int prev = cluster_end - 1;
368 = low[prev] * (1 -
copysign(min_rel_sep, low[prev]));
369 if (high[cluster_end] < end_threshold) {
375 if (cluster_end > i) {
377 = (high[cluster_end - 1] - low[cluster_end]) / min_rel_sep;
379 double min_ele_growth;
381 max_shift, l, d, next_shift, min_ele_growth);
382 for (
int j = i; j <= cluster_end; j++) {
384 - next_shift - 1
e-200;
387 - next_shift + 1
e-200;
390 block_queue.push(
mrrr_task{i, cluster_end + 1,
block.shift + next_shift,
391 std::move(l), std::move(d),
400 ?
double_d(std::numeric_limits<double>::infinity())
401 : low[i - 1] - high[i];
404 ?
double_d(std::numeric_limits<double>::infinity())
405 : low[i] - high[i + 1];
406 const double_d min_gap = std::min(low_gap, high_gap);
408 if (!(
fabs(min_gap / ((high[i] + low[i]) * 0.5)) > min_rel_sep)) {
409 if (!(
fabs(min_gap / ((high[i] + low[i]) * 0.5 - shift)) > min_rel_sep
410 && min_element_growth < max_ele_growth)) {
411 const double_d max_shift = min_gap / min_rel_sep;
413 max_shift, l2, d2, shift, min_element_growth);
415 low[i] = low[i] * (1 -
copysign(shift_error, low[i])) - shift;
416 high[i] = high[i] * (1 +
copysign(shift_error, high[i])) - shift;
424 l_big.col(i) = *l_ptr;
425 d_big.col(i) = *d_ptr;
436 subdiag_cl, shifted_eigvals_cl, l_plus_cl,
437 u_minus_cl, temp_cl, eigenvectors_cl);
451template <
bool need_eigenvectors = true>
456 const double split_threshold = 1
e-15) {
459 const int n = diagonal_cl.
rows();
461 if (need_eigenvectors) {
465 Eigen::VectorXd
diagonal = from_matrix_cl<Eigen::VectorXd>(diagonal_cl);
466 Eigen::VectorXd subdiagonal = from_matrix_cl<Eigen::VectorXd>(subdiagonal_cl);
468 for (
int i = 0; i < subdiagonal.size(); i++) {
471 if (need_eigenvectors) {
472 eigenvectors.block(last, i + 1, i + 1 - last, n - i - 1)
473 = Eigen::MatrixXd::Constant(i + 1 - last, n - i - 1, 0);
474 eigenvectors.block(i + 1, last, n - i - 1, i + 1 - last)
475 = Eigen::MatrixXd::Constant(n - i - 1, i + 1 - last, 0);
480 mrrr_cl<need_eigenvectors>(
481 diagonal.segment(last, i + 1 - last),
482 subdiagonal.segment(last, i - last),
484 eigenvectors.block(last, last, i + 1 - last, i + 1 - last));
490 mrrr_cl<need_eigenvectors>(
diagonal.segment(last, i + 1 - last),
491 subdiagonal.segment(last, i - last),
500 if (need_eigenvectors) {
505 if (need_eigenvectors) {
506 mrrr_cl<need_eigenvectors>(
508 subdiagonal.segment(last, subdiagonal.size() - last),
512 mrrr_cl<need_eigenvectors>(
514 subdiagonal.segment(last, subdiagonal.size() - last),
519 if (need_eigenvectors) {
Represents an arithmetic matrix on the OpenCL device.
auto transpose(Arg &&a)
Transposes a kernel generator expression.
auto diagonal(T &&a)
Diagonal of a kernel generator expression.
matrix_cl< scalar_type_t< T > > to_matrix_cl(T &&src)
Copies the source Eigen matrix, std::vector or scalar to the destination matrix that is stored on the...
auto from_matrix_cl(const T &src)
Copies the source matrix that is stored on the OpenCL device to the destination Eigen matrix.
double_d get_random_perturbation_multiplier()
Generates a random number for perturbing a relatively robust representation.
double get_ldl(const Eigen::Ref< const Eigen::VectorXd > diagonal, const Eigen::Ref< const Eigen::VectorXd > subdiagonal, const double shift, VectorXdd &l, VectorXdd &d_plus)
Calculates LDL decomposition of a shifted triagonal matrix T.
void find_shift(const VectorXdd &l, const VectorXdd &d, const double_d low, const double_d high, const double max_ele_growth, const double_d max_shift, VectorXdd &l2, VectorXdd &d2, double_d &shift, double &min_element_growth)
Finds good shift and shifts a LDL decomposition so as to keep element growth low.
double get_shifted_ldl(const VectorXdd &l, const VectorXdd &d, const double_d shift, VectorXdd &l_plus, VectorXdd &d_plus)
Shifts a LDL decomposition.
double find_initial_shift(const Eigen::Ref< const Eigen::VectorXd > diagonal, const Eigen::Ref< const Eigen::VectorXd > subdiagonal, VectorXdd &l0, VectorXdd &d0, const double min_eigval, const double max_eigval, const double max_ele_growth)
Finds a good value for shift of the initial LDL factorization T - shift * I = L * D * L^T.
double copysign(double a, double_d b)
int get_sturm_count_ldl(const VectorXdd &l, const VectorXdd &d, const double_d shift)
Calculates Sturm count of a LDL decomposition of a tridiagonal matrix - number of eigenvalues larger ...
void get_gresgorin(const Eigen::Ref< const Eigen::VectorXd > diagonal, const Eigen::Ref< const Eigen::VectorXd > subdiagonal, double &min_eigval, double &max_eigval)
Calculates bounds on eigenvalues of a symmetric tridiagonal matrix T using Gresgorin discs.
Eigen::Matrix< double_d, -1, 1 > VectorXdd
void eigenval_bisect_refine(const VectorXdd &l, const VectorXdd &d, double_d &low, double_d &high, const int i)
Refines bounds on the i-th largest eigenvalue of LDL decomposition using bisection.
Eigen::Matrix< double_d, -1, -1 > MatrixXdd
static const double_d perturbation_range
void mrrr_cl(const Eigen::Ref< const Eigen::VectorXd > diagonal, const Eigen::Ref< const Eigen::VectorXd > subdiagonal, Eigen::Ref< Eigen::VectorXd > eigenvalues, Eigen::Ref< Eigen::MatrixXd > eigenvectors, const double min_rel_sep=1e-4, const double maximum_ele_growth=15)
Calculates eigenvalues and eigenvectors of a irreducible tridiagonal matrix T using multiple relative...
double max_nan(double a, double b)
NaN-favouring max.
void tridiagonal_eigensolver_cl(const matrix_cl< double > &diagonal_cl, const matrix_cl< double > &subdiagonal_cl, matrix_cl< double > &eigenvalues_cl, matrix_cl< double > &eigenvectors_cl, const double split_threshold=1e-15)
Calculates eigenvalues and eigenvectors of a tridiagonal matrix T using MRRR algorithm.
const kernel_cl< in_buffer, in_buffer, in_buffer, in_buffer, in_out_buffer, in_out_buffer, in_out_buffer, out_buffer > get_eigenvectors("get_eigenvectors", {stan::math::internal::double_d_src, get_eigenvectors_kernel_code})
const kernel_cl< in_buffer, in_buffer, in_buffer, in_buffer, out_buffer, out_buffer, out_buffer, double, double, double, char > eigenvals("eigenvals", {stan::math::internal::double_d_src, eigenvals_bisect_kernel_code})
static constexpr double e()
Return the base of the natural logarithm.
auto block(T_x &&x, size_t i, size_t j, size_t nrows, size_t ncols)
Return a nrows x ncols submatrix starting at (i-1, j-1).
fvar< T > sqrt(const fvar< T > &x)
Eigen::Matrix< complex_return_t< value_type_t< EigMat > >, -1, -1 > eigenvectors(const EigMat &m)
Return the eigenvectors of a (real-valued) matrix.
Eigen::Matrix< complex_return_t< value_type_t< EigMat > >, -1, 1 > eigenvalues(const EigMat &m)
Return the eigenvalues of a (real-valued) matrix.
T plus(T &&x)
Returns the unary plus of the input.
fvar< T > fabs(const fvar< T > &x)
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
bool isinf(const stan::math::var &a)
Return 1 if the specified argument is positive infinity or negative infinity and 0 otherwise.