fix(math): preserve certified sums and complete MKL algebra

This commit is contained in:
KOKO\Mimi
2026-10-14 15:40:52 +09:00
parent 59c8e93b07
commit 590a93793f
9 changed files with 236 additions and 95 deletions
+62
View File
@@ -1,10 +1,72 @@
#include "math/dense_blas_internal.h"
#include <cmath>
#include <limits>
#include <stdexcept>
#include <utility>
#include <vector>
namespace fesa::dense_blas_internal {
bool CholeskyLowerAndInverse(const Matrix& matrix, Matrix& lower,
Matrix& inverse_lower) {
if (matrix.Rows() != matrix.Columns())
throw std::invalid_argument{"Cholesky requires a square matrix."};
const auto size = ToMklSize(matrix.Rows());
if (!size.HasValue())
throw std::length_error{"Cholesky size exceeds the MKL integer range."};
Matrix candidate_lower{matrix.Rows(), matrix.Columns()};
Matrix candidate_inverse{matrix.Rows(), matrix.Columns()};
if (matrix.Rows() == 0U) {
lower = std::move(candidate_lower);
inverse_lower = std::move(candidate_inverse);
return true;
}
std::vector<double> values(matrix.Rows() * matrix.Columns());
for (std::size_t row = 0; row < matrix.Rows(); ++row)
for (std::size_t column = 0; column < matrix.Columns(); ++column) {
if (!std::isfinite(matrix(row, column)))
throw std::invalid_argument{"Cholesky inputs must be finite."};
values[row * matrix.Columns() + column] = matrix(row, column);
}
if (LAPACKE_dpotrf(LAPACK_ROW_MAJOR, 'L', size.Value(), values.data(),
size.Value()) != 0) return false;
for (std::size_t row = 0; row < matrix.Rows(); ++row)
for (std::size_t column = 0; column <= row; ++column)
candidate_lower(row, column) = values[row * matrix.Columns() + column];
if (LAPACKE_dtrtri(LAPACK_ROW_MAJOR, 'L', 'N', size.Value(), values.data(),
size.Value()) != 0) return false;
for (std::size_t row = 0; row < matrix.Rows(); ++row)
for (std::size_t column = 0; column <= row; ++column) {
const auto value = values[row * matrix.Columns() + column];
if (!std::isfinite(candidate_lower(row, column)) || !std::isfinite(value))
return false;
candidate_inverse(row, column) = value;
}
lower = std::move(candidate_lower);
inverse_lower = std::move(candidate_inverse);
return true;
}
Vector MultiplyDiagonal(const Vector& diagonal, const Vector& rhs) {
if (diagonal.Size() != rhs.Size())
throw std::invalid_argument{"Diagonal product dimensions differ."};
const auto size = ToMklSize(diagonal.Size());
if (!size.HasValue())
throw std::length_error{"Diagonal size exceeds the MKL integer range."};
for (std::size_t i = 0; i < rhs.Size(); ++i) {
if (!std::isfinite(diagonal[i]) || !std::isfinite(rhs[i]))
throw std::invalid_argument{"Diagonal product inputs must be finite."};
}
Vector candidate{rhs.Size()};
if (rhs.Size() == 0U) return candidate;
// k=0 stores exactly one coefficient per independent row. MKL performs
// each product without regrouping a caller's subsequent certified sums.
cblas_dsbmv(CblasColMajor, CblasUpper, size.Value(), 0, 1.0, diagonal.Data(),
1, rhs.Data(), 1, 0.0, candidate.Data(), 1);
return candidate;
}
Matrix MultiplyTransposeLeft(const Matrix& left, const Matrix& right) {
if (left.Rows() != right.Rows()) {
throw std::invalid_argument{"Transpose product dimensions differ."};
+14
View File
@@ -7,6 +7,7 @@
#include "fesa/core/status.h"
#include "fesa/math/matrix.h"
#include "fesa/math/vector.h"
namespace fesa::dense_blas_internal {
@@ -18,6 +19,19 @@ Result<MKL_INT> ToMklSize(std::size_t size);
/// @throws std::invalid_argument if the row counts differ.
Matrix MultiplyTransposeLeft(const Matrix& left, const Matrix& right);
/// @brief Multiplies a compact diagonal matrix by a vector through MKL.
/// @throws std::invalid_argument if shapes differ or inputs are nonfinite.
/// @throws std::length_error if the size exceeds the backend integer range.
Vector MultiplyDiagonal(const Vector& diagonal, const Vector& rhs);
/// @brief Computes a lower Cholesky factor and its inverse with MKL LAPACK.
/// @return False for a nonpositive pivot or nonfinite factor/inverse.
/// @note Outputs are replaced only after both finite candidates succeed.
/// @throws std::invalid_argument for a nonsquare or nonfinite input.
/// @throws std::length_error for a backend size overflow.
bool CholeskyLowerAndInverse(const Matrix& matrix, Matrix& lower,
Matrix& inverse_lower);
/// @brief Copies contiguous double values through the private BLAS backend.
/// @param source Readable storage for size values when size is nonzero.
/// @param size Number of contiguous values to copy.
+4
View File
@@ -211,6 +211,10 @@ Vector SparseMatrix::Multiply(const Vector& rhs) const {
if (!Validate().IsOk()) {
throw std::invalid_argument{"Sparse multiplication requires valid CSR."};
}
for (std::size_t i = 0; i < rhs.Size(); ++i) {
if (!std::isfinite(rhs[i]))
throw std::invalid_argument{"Sparse multiplication RHS must be finite."};
}
const MKL_INT rows = SparseSize(rows_);
const MKL_INT columns = SparseSize(columns_);
static_cast<void>(SparseSize(values_.size()));