437#include <gsl/gsl_errno.h>
441 for (
size_t i = 0; i < A.
rows(); i++) {
442 os << (i == 0 ?
"[ " :
" [ ");
443 for (
size_t j = 0; j < A.
cols(); j++) {
444 os << A.
at(i, j) << (j == A.
cols() - 1 ?
" " :
", ");
446 if (i != A.
rows() - 1)
447 os <<
"]" << std::endl;
449 os <<
"]]" << std::endl;
455 std::vector<double> new_data(n * n, 0.0);
457 for (
size_t i = 0; i < n; ++i) {
458 new_data[i * (n + 1)] = 1.0;
465 const std::size_t n = X->size;
466 std::vector<double> new_data(n * n, 0.0);
468 for (
size_t i = 0; i < n; ++i) {
469 new_data[i * (n + 1)] = gsl_vector_get(X, i);
476 if (R.rows() != R.cols())
477 throw std::invalid_argument(
"Matrix should be square");
482 R = 0.5 * (R + R.transpose());
486 for (
size_t i = 0; i < n; i++) {
487 e.
D.
at(i, i) = std::max(e.
D.
at(i, i), thr);
492 std::vector<double> inv_sqrt_diag(n);
493 for (
size_t i = 0; i < n; ++i)
494 inv_sqrt_diag[i] = 1.0 / std::sqrt(R_psd.
at(i, i));
496 for (
size_t i = 0; i < n; ++i)
497 for (
size_t j = 0; j < n; ++j)
498 R_psd.
at(i, j) *= inv_sqrt_diag[i] * inv_sqrt_diag[j];
500 R_psd = 0.5 * (R_psd + R_psd.
transpose());
502 for (
size_t i = 0; i < n; i++)
503 R_psd.
at(i, i) = 1.0;
509 if (R.rows() != R.cols())
510 throw std::invalid_argument(
"Matrix should be square");
514 if (gsl_linalg_cholesky_decomp1(R_gsl.get()) != GSL_SUCCESS)
515 throw std::runtime_error(
"Cholesky decomposition failed (matrix not PSD)");
517 const size_t n = R.rows();
520 for (
size_t i = 0; i < n; ++i) {
521 for (
size_t j = 0; j <= i; ++j) {
522 L.at(i, j) = gsl_matrix_get(R_gsl.get(), i, j);
533 std::size_t total_dim = 0;
535 for (
const auto&
B : blocks) {
536 if (
B.rows() !=
B.cols())
537 throw std::invalid_argument(
"block_diag: matrices must be square");
538 total_dim +=
B.rows();
543 std::size_t offset = 0;
544 for (
const auto&
B : blocks) {
545 const std::size_t n =
B.rows();
546 for (std::size_t i = 0; i < n; ++i)
547 for (std::size_t j = 0; j < n; ++j)
548 M.
unchecked_at(offset + i, offset + j) =
B.unchecked_at(i, j);
555RealMatrix::RealMatrix(std::vector<double> data_, std::size_t rows, std::size_t cols) : data(data_), rows_(rows), cols_(cols) {
556 if (data.size() != rows * cols)
557 throw std::invalid_argument(
"Data size does not match matrix shape.");
561 if (data.size() == 0)
562 throw std::invalid_argument(
"Matrix should not be sizeless.");
564 this->rows_ = data.size();
566 std::size_t size_2 = data[0].size();
567 for (
auto& row : data) {
568 if (row.size() != size_2)
569 throw std::invalid_argument(
"All rows should have the same number of columns.");
572 this->cols_ = size_2;
574 std::vector<double> new_data (rows_ * cols_, 0.0);
575 for (
size_t i = 0; i < rows_; i++) {
576 for (
size_t j = 0; j < cols_; j++) {
577 new_data[i * cols_ + j] = data[i][j];
581 this->data = std::move(new_data);
588 if (i >= rows_ || j >= cols_)
589 throw std::out_of_range(
"Indices outside matrix shape");
591 return this->data[i * cols_ + j];
595 if (i >= rows_ || j >= cols_)
596 throw std::out_of_range(
"Indices outside matrix shape");
598 return this->data[i * cols_ + j];
610 std::vector<double> new_data = std::vector<double>((this->rows_ - 1) * this->cols_, 0.0);
612 for (
size_t i = 0; i < this->rows_; i++) {
613 if (i == row_idx)
continue;
614 std::size_t new_i = i < row_idx ? i : i - 1;
615 for (
size_t j = 0; j < this->cols_; j++) {
616 new_data[new_i * this->cols_ + j] = this->
at(i, j);
620 this->data = std::move(new_data);
625 std::vector<double> new_data = std::vector<double>(this->rows_ * (this->cols_ - 1), 0.0);
627 for (
size_t j = 0; j < this->cols_; j++) {
628 if (j == col_idx)
continue;
629 std::size_t new_j = j < col_idx ? j : j - 1;
630 for (
size_t i = 0; i < this->rows_; i++) {
631 new_data[i * (this->cols_ - 1) + new_j] = this->
at(i, j);
635 this->data = std::move(new_data);
640 std::vector<double> new_data((this->rows_ - 1) * (this->cols_ - 1), 0.0);
642 for (
size_t i = 0; i < this->rows_; i++) {
643 if (i == dim_idx)
continue;
644 std::size_t new_i = i < dim_idx ? i : i - 1;
646 for (
size_t j = 0; j < this->cols_; j++) {
647 if (j == dim_idx)
continue;
648 std::size_t new_j = j < dim_idx ? j : j - 1;
649 new_data[new_i * (this->cols_ - 1) + new_j] = this->
at(i, j);
653 this->data = std::move(new_data);
659 const size_t n = A->size1;
660 const size_t m = A->size2;
661 std::vector<double> data(n * m, 0.0);
663 for (
size_t i = 0; i < n; ++i) {
664 for (
size_t j = 0; j < m; ++j) {
665 data[i * m + j] = gsl_matrix_get(A, i, j);
675 for (
size_t i = 0; i < rows_; ++i) {
676 for (
size_t j = 0; j < cols_; ++j) {
677 gsl_matrix_set(A.get(), i, j, this->data[i * cols_ + j]);
688 for (std::size_t i = 0; i < rows_; i++) {
689 for (std::size_t j = i + 1; j < cols_; j++) {
690 if (!
fpeq(data[i * cols_ + j], data[j * cols_ + i]))
699 auto new_data = std::vector(this->cols_ * this->rows_, 0.0);
701 for (
size_t i = 0; i < this->rows_; ++i) {
702 for (
size_t j = 0; j < this->cols_; ++j) {
707 return RealMatrix(std::move(new_data), cols_, rows_);
712 throw std::invalid_argument(
"Eigen decomposition requires a square matrix.");
715 throw std::runtime_error(
"Matrix is not symmetric.");
721 gsl_eigen_symmv (M.get(), eval.get(), evec.get(), w.get());
722 gsl_eigen_symmv_sort(eval.get(), evec.get(), GSL_EIGEN_SORT_VAL_DESC);
726 e.
D =
diag(eval.get());
733 throw std::invalid_argument(
"LU decomposition requires a square matrix.");
739 if (gsl_linalg_LU_decomp(M.get(), p.get(), &p_sign) != GSL_SUCCESS) {
744 sld.
logdet = gsl_linalg_LU_lndet(M.get());
745 sld.
sign = gsl_linalg_LU_sgndet(M.get(), p_sign);
752 throw std::invalid_argument(
"inversion requires a square matrix.");
754 gsl_error_handler_t* old_handler = gsl_set_error_handler_off();
761 const int decomp_status = gsl_linalg_LU_decomp(M.get(), p.get(), &p_sign);
762 if (decomp_status != GSL_SUCCESS) {
763 gsl_set_error_handler(old_handler);
764 throw std::runtime_error(
"Matrix LU decomposition failed");
767 const int invert_status = gsl_linalg_LU_invert(M.get(), p.get(), M_inv.get());
768 gsl_set_error_handler(old_handler);
770 if (invert_status != GSL_SUCCESS) {
771 throw std::runtime_error(
"Matrix is singular");
778 auto new_data = std::vector(this->rows_ * this->cols_, 0.0);
780 for (
size_t i = 0; i < this->rows_; ++i) {
781 for (
size_t j = 0; j < this->cols_; ++j) {
786 return RealMatrix(std::move(new_data), rows_, cols_);
790 if (this->rows_ != rhs.
rows() || this->cols_ != rhs.
cols())
791 throw std::invalid_argument(
"Matrices should have the same shape.");
793 for (
size_t i = 0; i < this->rows_; ++i) {
794 for (
size_t j = 0; j < this->cols_; ++j) {
803 if (this->rows_ != rhs.
rows() || this->cols_ != rhs.
cols())
804 throw std::invalid_argument(
"Matrices should have the same shape.");
806 for (
size_t i = 0; i < this->rows_; ++i) {
807 for (
size_t j = 0; j < this->cols_; ++j) {
816 if (this->cols_ != rhs.
rows())
817 throw std::invalid_argument(
"Matrices don't have the right shape to be multiplied.");
819 const size_t M = rows_;
820 const size_t N = rhs.cols_;
821 const size_t K = cols_;
823 std::vector<double> result(M * N, 0.0);
825 constexpr size_t BLOCK = 32;
827 for (
size_t ii = 0; ii < M; ii +=
BLOCK) {
828 for (
size_t kk = 0; kk <
K; kk +=
BLOCK) {
829 for (
size_t jj = 0; jj < N; jj +=
BLOCK) {
831 size_t i_max = std::min(ii +
BLOCK, M);
832 size_t k_max = std::min(kk +
BLOCK,
K);
833 size_t j_max = std::min(jj +
BLOCK, N);
835 for (
size_t i = ii; i < i_max; ++i) {
836 for (
size_t k = kk; k < k_max; ++k) {
838 const double* rhs_row = &rhs.data[k * rhs.cols_];
840 double* res_row = &result[i * N];
842 for (
size_t j = jj; j < j_max; ++j) {
843 res_row[j] += aik * rhs_row[j];
851 data = std::move(result);
857 for (
double& x: data)
864 if (
fpeq(scalar, 0.0))
865 throw std::invalid_argument(
"Division by zero.");
867 for (
double& x: data)
874 return data[i * cols_ + j];
878 return data[i * cols_ + j];
RealMatrix nearest_psd(RealMatrix R, double thr)
Projects a matrix to the nearest positive semi-definite correlation-like matrix.
RealMatrix eye(std::size_t n)
Returns the identity matrix of size n.
RealMatrix diag(const gsl_vector *X)
Builds a diagonal matrix from a GSL vector.
RealMatrix cholesky_L(RealMatrix R)
Returns the lower-triangular Cholesky factor of a PSD matrix.
RealMatrix block_diag(const std::vector< RealMatrix > &blocks)
Builds a block-diagonal matrix from several square blocks.
std::ostream & operator<<(std::ostream &os, RealMatrix A)
Lightweight dense real-matrix utilities built on top of STL storage and GSL backends.
gsl_vector_sptr make_gsl_vector(size_t size)
Allocates a GSL vector with automatic RAII destruction.
gsl_permutation_sptr make_gsl_permutation(size_t size)
Allocates a GSL permutation with automatic RAII destruction.
std::unique_ptr< gsl_matrix, decltype(&gsl_matrix_free)> gsl_matrix_sptr
Owning smart pointer for a GSL matrix.
std::unique_ptr< gsl_vector, decltype(&gsl_vector_free)> gsl_vector_sptr
Owning smart pointer for a GSL vector.
gsl_eigen_workspace_sptr make_eigen_workspace(size_t n)
Allocates a GSL symmetric eigensolver workspace with RAII destruction.
gsl_matrix_sptr make_gsl_matrix(size_t rows, size_t cols)
Allocates a GSL matrix with automatic RAII destruction.
std::unique_ptr< gsl_eigen_symmv_workspace, decltype(&gsl_eigen_symmv_free)> gsl_eigen_workspace_sptr
Owning smart pointer for a GSL symmetric-eigensystem workspace.
std::unique_ptr< gsl_permutation, decltype(&gsl_permutation_free)> gsl_permutation_sptr
Owning smart pointer for a GSL permutation.
void remove_row_and_column(std::size_t dim_idx)
Removes one row and one column with the same index.
void remove_column(std::size_t col_idx)
Removes one column from the matrix.
RealMatrix & operator/=(double scalar)
In-place scalar division.
std::size_t rows() const
Returns the number of rows.
EigenSystem eig() const
Computes the eigensystem of a symmetric matrix.
double & at(size_t i, size_t j)
Returns a mutable reference to element (i,j) with bounds checking.
RealMatrix & operator-=(const RealMatrix &rhs)
In-place matrix subtraction.
RealMatrix & operator*=(const RealMatrix &rhs)
In-place matrix multiplication.
RealMatrix()
Constructs an empty 0×0 matrix.
static RealMatrix from_gsl_copy(const gsl_matrix *A)
Creates a RealMatrix by copying data from a GSL matrix.
void remove_row(std::size_t row_idx)
Removes one row from the matrix.
SignedLogDet slogdet() const
Computes the signed logarithmic determinant via LU decomposition.
gsl_matrix_sptr to_gsl_matrix() const
Converts this matrix to a newly allocated GSL matrix.
RealMatrix & operator+=(const RealMatrix &rhs)
In-place matrix addition.
double & unchecked_at(size_t i, size_t j)
Returns a mutable reference to element (i,j) without bounds checking.
bool is_symmetric() const
Checks whether the matrix is symmetric.
RealMatrix operator-() const
Unary minus.
std::size_t cols() const
Returns the number of columns.
RealMatrix transpose() const
Returns the transpose of the matrix.
RealMatrix inv() const
Computes the inverse of the matrix via LU decomposition.
std::enable_if_t< not std::numeric_limits< T >::is_integer, bool > fpeq(T, T, std::size_t n=10)
Compares two floating point numbers with a given precision.
Container for an eigendecomposition.
RealMatrix P
Diagonal matrix of eigenvalues.
Signed logarithmic determinant representation.
int sign
Natural logarithm of the absolute determinant.