4#ifndef LBFGSPP_BK_LDLT_H
5#define LBFGSPP_BK_LDLT_H
30template <
typename Scalar =
double>
34 using Index = Eigen::Index;
35 using Matrix = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
36 using Vector = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
37 using MapVec = Eigen::Map<Vector>;
38 using MapConstVec = Eigen::Map<const Vector>;
40 using IntVector = Eigen::Matrix<Index, Eigen::Dynamic, 1>;
41 using GenericVector = Eigen::Ref<Vector>;
42 using GenericMatrix = Eigen::Ref<Matrix>;
43 using ConstGenericMatrix =
const Eigen::Ref<const Matrix>;
44 using ConstGenericVector =
const Eigen::Ref<const Vector>;
48 std::vector<Scalar*> m_colptr;
50 std::vector<std::pair<Index, Index> > m_permc;
57 Scalar* col_pointer(Index k) {
return m_colptr[k]; }
59 Scalar& coeff(Index i, Index j) {
return m_colptr[j][i - j]; }
60 const Scalar& coeff(Index i, Index j)
const {
return m_colptr[j][i - j]; }
62 Scalar& diag_coeff(Index i) {
return m_colptr[i][0]; }
63 const Scalar& diag_coeff(Index i)
const {
return m_colptr[i][0]; }
66 void compute_pointer()
69 m_colptr.reserve(m_n);
70 Scalar* head = m_data.data();
72 for (Index i = 0; i < m_n; i++)
74 m_colptr.push_back(head);
80 void copy_data(ConstGenericMatrix& mat,
int uplo,
const Scalar& shift)
82 if (uplo == Eigen::Lower)
84 for (Index j = 0; j < m_n; j++)
86 const Scalar* begin = &mat.coeffRef(j, j);
87 const Index len = m_n - j;
88 std::copy(begin, begin + len, col_pointer(j));
89 diag_coeff(j) -= shift;
94 Scalar* dest = m_data.data();
95 for (Index i = 0; i < m_n; i++)
97 for (Index j = i; j < m_n; j++, dest++)
99 *dest = mat.coeff(i, j);
101 diag_coeff(i) -= shift;
107 void compress_permutation()
109 for (Index i = 0; i < m_n; i++)
112 const Index perm = (m_perm[i] >= 0) ? (m_perm[i]) : (-m_perm[i] - 1);
114 m_permc.push_back(std::make_pair(i, perm));
121 void pivoting_1x1(Index k, Index r)
131 std::swap(diag_coeff(k), diag_coeff(r));
134 std::swap_ranges(&coeff(r + 1, k), col_pointer(k + 1), &coeff(r + 1, r));
137 Scalar* src = &coeff(k + 1, k);
138 for (Index j = k + 1; j < r; j++, src++)
140 std::swap(*src, coeff(r, j));
149 void pivoting_2x2(Index k, Index r, Index p)
152 pivoting_1x1(k + 1, r);
155 std::swap(coeff(k + 1, k), coeff(r, k));
159 m_perm[k] = -m_perm[k] - 1;
160 m_perm[k + 1] = -m_perm[k + 1] - 1;
165 void interchange_rows(Index r1, Index r2, Index c1, Index c2)
170 for (Index j = c1; j <= c2; j++)
172 std::swap(coeff(r1, j), coeff(r2, j));
180 Scalar find_lambda(Index k, Index& r)
184 const Scalar* head = col_pointer(k);
185 const Scalar* end = col_pointer(k + 1);
188 Scalar lambda = abs(head[1]);
190 for (
const Scalar* ptr = head + 2; ptr < end; ptr++)
192 const Scalar abs_elem = abs(*ptr);
193 if (lambda < abs_elem)
196 r = k + (ptr - head);
207 Scalar find_sigma(Index k, Index r, Index& p)
213 Scalar sigma = Scalar(-1);
215 sigma = find_lambda(r, p);
218 for (Index j = k; j < r; j++)
220 const Scalar abs_elem = abs(coeff(r, j));
221 if (sigma < abs_elem)
233 bool permutate_mat(Index k,
const Scalar& alpha)
238 const Scalar lambda = find_lambda(k, r);
241 if (lambda > Scalar(0))
243 const Scalar abs_akk = abs(diag_coeff(k));
245 if (abs_akk < alpha * lambda)
247 const Scalar sigma = find_sigma(k, r, p);
250 if (sigma * abs_akk < alpha * lambda * lambda)
252 if (abs_akk >= alpha * sigma)
258 interchange_rows(k, r, 0, k - 1);
289 pivoting_2x2(k, r, p);
291 interchange_rows(k, p, 0, k - 1);
292 interchange_rows(k + 1, r, 0, k - 1);
305 void inverse_inplace_2x2(Scalar& e11, Scalar& e21, Scalar& e22)
const
309 const Scalar delta = e11 * e22 - e21 * e21;
317 int gaussian_elimination_1x1(Index k)
320 const Scalar akk = diag_coeff(k);
322 if (akk == Scalar(0))
323 return NUMERICAL_ISSUE;
325 diag_coeff(k) = Scalar(1) / akk;
328 Scalar* lptr = col_pointer(k) + 1;
329 const Index ldim = m_n - k - 1;
330 MapVec l(lptr, ldim);
331 for (Index j = 0; j < ldim; j++)
333 MapVec(col_pointer(j + k + 1), ldim - j).noalias() -= (lptr[j] / akk) * l.tail(ldim - j);
343 int gaussian_elimination_2x2(Index k)
346 Scalar& e11 = diag_coeff(k);
347 Scalar& e21 = coeff(k + 1, k);
348 Scalar& e22 = diag_coeff(k + 1);
350 if (e11 * e22 - e21 * e21 == Scalar(0))
351 return NUMERICAL_ISSUE;
353 inverse_inplace_2x2(e11, e21, e22);
356 Scalar* l1ptr = &coeff(k + 2, k);
357 Scalar* l2ptr = &coeff(k + 2, k + 1);
358 const Index ldim = m_n - k - 2;
359 MapVec l1(l1ptr, ldim), l2(l2ptr, ldim);
361 Eigen::Matrix<Scalar, Eigen::Dynamic, 2> X(ldim, 2);
362 X.col(0).noalias() = l1 * e11 + l2 * e21;
363 X.col(1).noalias() = l1 * e21 + l2 * e22;
366 for (Index j = 0; j < ldim; j++)
368 MapVec(col_pointer(j + k + 2), ldim - j).noalias() -= (X.col(0).tail(ldim - j) * l1ptr[j] + X.col(1).tail(ldim - j) * l2ptr[j]);
372 l1.noalias() = X.col(0);
373 l2.noalias() = X.col(1);
380 m_n(0), m_computed(false), m_info(NOT_COMPUTED)
384 BKLDLT(ConstGenericMatrix& mat,
int uplo = Eigen::Lower,
const Scalar& shift = Scalar(0)) :
385 m_n(mat.rows()), m_computed(false), m_info(NOT_COMPUTED)
387 compute(mat, uplo, shift);
390 void compute(ConstGenericMatrix& mat,
int uplo = Eigen::Lower,
const Scalar& shift = Scalar(0))
395 if (m_n != mat.cols())
396 throw std::invalid_argument(
"BKLDLT: matrix must be square");
398 m_perm.setLinSpaced(m_n, 0, m_n - 1);
402 m_data.resize((m_n * (m_n + 1)) / 2);
404 copy_data(mat, uplo, shift);
406 const Scalar alpha = (1.0 + std::sqrt(17.0)) / 8.0;
408 for (k = 0; k < m_n - 1; k++)
411 bool is_1x1 = permutate_mat(k, alpha);
416 m_info = gaussian_elimination_1x1(k);
420 m_info = gaussian_elimination_2x2(k);
425 if (m_info != SUCCESSFUL)
431 const Scalar akk = diag_coeff(k);
432 if (akk == Scalar(0))
433 m_info = NUMERICAL_ISSUE;
435 diag_coeff(k) = Scalar(1) / diag_coeff(k);
438 compress_permutation();
444 void solve_inplace(GenericVector b)
const
447 throw std::logic_error(
"BKLDLT: need to call compute() first");
451 Scalar* x = b.data();
453 Index npermc = m_permc.size();
454 for (Index i = 0; i < npermc; i++)
456 std::swap(x[m_permc[i].first], x[m_permc[i].second]);
461 const Index end = (m_perm[m_n - 1] < 0) ? (m_n - 3) : (m_n - 2);
462 for (Index i = 0; i <= end; i++)
464 const Index b1size = m_n - i - 1;
465 const Index b2size = b1size - 1;
468 MapConstVec l(&coeff(i + 1, i), b1size);
469 res.segment(i + 1, b1size).noalias() -= l * x[i];
473 MapConstVec l1(&coeff(i + 2, i), b2size);
474 MapConstVec l2(&coeff(i + 2, i + 1), b2size);
475 res.segment(i + 2, b2size).noalias() -= (l1 * x[i] + l2 * x[i + 1]);
481 for (Index i = 0; i < m_n; i++)
483 const Scalar e11 = diag_coeff(i);
490 const Scalar e21 = coeff(i + 1, i), e22 = diag_coeff(i + 1);
491 const Scalar wi = x[i] * e11 + x[i + 1] * e21;
492 x[i + 1] = x[i] * e21 + x[i + 1] * e22;
500 Index i = (m_perm[m_n - 1] < 0) ? (m_n - 3) : (m_n - 2);
503 const Index ldim = m_n - i - 1;
504 MapConstVec l(&coeff(i + 1, i), ldim);
505 x[i] -= res.segment(i + 1, ldim).dot(l);
509 MapConstVec l2(&coeff(i + 1, i - 1), ldim);
510 x[i - 1] -= res.segment(i + 1, ldim).dot(l2);
516 for (i = npermc - 1; i >= 0; i--)
518 std::swap(x[m_permc[i].first], x[m_permc[i].second]);
522 Vector solve(ConstGenericVector& b)
const
529 int info()
const {
return m_info; }