fix(math): preserve certified sums and complete MKL algebra
This commit is contained in:
@@ -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."};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()));
|
||||
|
||||
Reference in New Issue
Block a user