31 #define lapack_complex_float float _Complex 32 #define lapack_complex_double double _Complex 39 #if __has_include(<lapacke.h>) 41 #elif __has_include(<lapacke/lapacke.h>) 42 #include <lapacke/lapacke.h> 44 #pragma error no lapacke found 67 namespace blasWrapper {
76 double one_norm(
const double*
const _x,
const size_t _n) {
77 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
81 const double result = cblas_dasum(static_cast<int>(_n), _x, 1);
88 double two_norm(
const double*
const _x,
const size_t _n) {
89 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
93 const double result = cblas_dnrm2(static_cast<int>(_n), _x, 1);
100 double dot_product(
const double*
const _x,
const size_t _n,
const double*
const _y) {
101 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
105 const double result = cblas_ddot(static_cast<int>(_n), _x, 1, _y, 1);
115 void matrix_vector_product(
double*
const _x,
const size_t _m,
const double _alpha,
const double*
const _A,
const size_t _n,
const bool _transposed,
const double*
const _y) {
117 if(_m == 1) { *_x = _alpha*
dot_product(_A, _n, _y);
return;}
119 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
120 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
124 cblas_dgemv(CblasRowMajor, CblasNoTrans, static_cast<int>(_m), static_cast<int>(_n), _alpha, _A, static_cast<int>(_n), _y, 1, 0.0, _x, 1);
126 cblas_dgemv(CblasRowMajor, CblasTrans, static_cast<int>(_n), static_cast<int>(_m), _alpha, _A, static_cast<int>(_m) , _y, 1, 0.0, _x, 1);
132 void dyadic_vector_product(
double* _A,
const size_t _m,
const size_t _n,
const double _alpha,
const double*
const _x,
const double*
const _y) {
133 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
134 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
141 cblas_dger(CblasRowMajor, static_cast<int>(_m), static_cast<int>(_n), _alpha, _x, 1, _y, 1, _A, static_cast<int>(_n));
150 const size_t _leftDim,
151 const size_t _rightDim,
153 const double*
const _A,
155 const bool _transposeA,
156 const size_t _middleDim,
157 const double*
const _B,
159 const bool _transposeB) {
163 }
else if(_rightDim == 1) {
165 }
else if(_middleDim == 1) {
169 REQUIRE(_leftDim <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
170 REQUIRE(_middleDim <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
171 REQUIRE(_rightDim <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
172 REQUIRE(_lda <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
173 REQUIRE(_ldb <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
177 cblas_dgemm( CblasRowMajor,
178 _transposeA ? CblasTrans : CblasNoTrans,
179 _transposeB ? CblasTrans : CblasNoTrans,
180 static_cast<int>(_leftDim),
181 static_cast<int>(_rightDim),
182 static_cast<int>(_middleDim),
185 static_cast<int>(_lda),
187 static_cast<int>(_ldb),
190 static_cast<int>(_rightDim)
201 void svd(
double*
const _U,
double*
const _S,
double*
const _Vt,
const double*
const _A,
const size_t _m,
const size_t _n) {
203 const std::unique_ptr<double[]> tmpA(
new double[_m*_n]);
210 void svd_destructive(
double*
const _U,
double*
const _S,
double*
const _Vt,
double*
const _A,
const size_t _m,
const size_t _n) {
211 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
212 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
215 std::unique_ptr<double[]> tmpA(
new double[_m*_n]);
218 int lapackAnswer = LAPACKE_dgesdd(LAPACK_ROW_MAJOR,
'S', static_cast<int>(_m), static_cast<int>(_n), _A, static_cast<int>(_n), _S, _U, static_cast<int>(std::min(_m, _n)), _Vt, static_cast<int>(_n));
219 CHECK(lapackAnswer == 0, warning,
"Lapack failed to compute SVD. Answer is: " << lapackAnswer);
220 CHECK(lapackAnswer == 0, warning,
"Call was: LAPACKE_dgesdd(LAPACK_ROW_MAJOR, 'S', " << static_cast<int>(_m) <<
", " << static_cast<int>(_n) <<
", " << _A <<
", " << static_cast<int>(_n) <<
", " 221 << _S <<
", " << _U <<
", " << static_cast<int>(std::min(_m, _n)) <<
", " << _Vt <<
", " << static_cast<int>(_n) <<
");");
222 if(lapackAnswer != 0) {
223 LOG(warning,
"SVD failed ");
235 std::tuple<std::unique_ptr<double[]>, std::unique_ptr<double[]>,
size_t>
qc(
const double*
const _A,
const size_t _m,
const size_t _n) {
236 const std::unique_ptr<double[]> tmpA(
new double[_m*_n]);
243 std::tuple<std::unique_ptr<double[]>, std::unique_ptr<double[]>,
size_t>
qc_destructive(
double*
const _A,
const size_t _m,
const size_t _n) {
244 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
245 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
247 REQUIRE(_n > 0,
"Dimension n must be larger than zero");
248 REQUIRE(_m > 0,
"Dimension m must be larger than zero");
253 const size_t maxRank = std::min(_m, _n);
256 const std::unique_ptr<double[]> tau(
new double[maxRank]);
258 const std::unique_ptr<int[]> permutation(
new int[_n]());
262 IF_CHECK(
int lapackAnswer = ) LAPACKE_dgeqp3(LAPACK_ROW_MAJOR, static_cast<int>(_m), static_cast<int>(_n), _A, static_cast<int>(_n), permutation.get(), tau.get());
263 REQUIRE(lapackAnswer == 0,
"Unable to perform QC factorisaton (dgeqp3). Lapacke says: " << lapackAnswer );
268 for (rank = 1; rank <= maxRank; ++rank) {
269 if (rank == maxRank || std::abs(_A[rank+rank*_n]) < 16*std::numeric_limits<double>::epsilon()*_A[0]) {
276 std::unique_ptr<double[]> C(
new double[rank*_n]);
280 for (
size_t col = 0; col < _n; ++col) {
281 const size_t targetCol =
static_cast<size_t>(permutation[col]-1);
282 for(
size_t row = 0; row < rank && row < col+1; ++row) {
283 C[row*_n + targetCol] = _A[row*_n + col];
289 IF_CHECK(lapackAnswer = ) LAPACKE_dorgqr(LAPACK_ROW_MAJOR, static_cast<int>(_m), static_cast<int>(maxRank), static_cast<int>(maxRank), _A, static_cast<int>(_n), tau.get());
290 CHECK(lapackAnswer == 0, error,
"Unable to reconstruct Q from the QC factorisation. Lapacke says: " << lapackAnswer);
293 std::unique_ptr<double[]> Q(
new double[_m*rank]);
297 for(
size_t row = 0; row < _m; ++row) {
298 misc::copy(Q.get()+row*rank, _A+row*_n, rank);
304 return std::make_tuple(std::move(Q), std::move(C), rank);
308 std::tuple<std::unique_ptr<double[]>, std::unique_ptr<double[]>,
size_t>
cq(
const double*
const _A,
const size_t _m,
const size_t _n) {
309 const std::unique_ptr<double[]> tmpA(
new double[_m*_n]);
317 std::tuple<std::unique_ptr<double[]>, std::unique_ptr<double[]>,
size_t>
cq_destructive(
double*
const _A,
const size_t _m,
const size_t _n) {
318 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
319 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
321 REQUIRE(_m > 0,
"Dimension m must be larger than zero");
322 REQUIRE(_n > 0,
"Dimension n must be larger than zero");
327 const size_t maxRank = std::min(_n, _m);
330 const std::unique_ptr<double[]> tau(
new double[maxRank]);
332 const std::unique_ptr<int[]> permutation(
new int[_m]());
336 IF_CHECK(
int lapackAnswer = ) LAPACKE_dgeqp3(LAPACK_COL_MAJOR, static_cast<int>(_n), static_cast<int>(_m), _A, static_cast<int>(_n), permutation.get(), tau.get());
337 REQUIRE(lapackAnswer == 0,
"Unable to perform QC factorisaton (dgeqp3). Lapacke says: " << lapackAnswer );
342 for (rank = 1; rank <= maxRank; ++rank) {
343 if (rank == maxRank || std::abs(_A[rank+rank*_n]) < 16*std::numeric_limits<double>::epsilon()*_A[0]) {
350 std::unique_ptr<double[]> C(
new double[rank*_m]);
354 for (
size_t col = 0; col < _m; ++col) {
355 const size_t targetCol =
static_cast<size_t>(permutation[col]-1);
356 misc::copy(C.get()+targetCol*rank, _A+col*_n, std::min(rank, col+1));
361 IF_CHECK(lapackAnswer = ) LAPACKE_dorgqr(LAPACK_COL_MAJOR, static_cast<int>(_n), static_cast<int>(maxRank), static_cast<int>(maxRank), _A, static_cast<int>(_n), tau.get());
362 CHECK(lapackAnswer == 0, error,
"Unable to reconstruct Q from the QC factorisation. Lapacke says: " << lapackAnswer);
365 std::unique_ptr<double[]> Q(
new double[_n*rank]);
370 return std::make_tuple(std::move(C), std::move(Q), rank);
374 void qr(
double*
const _Q,
double*
const _R,
const double*
const _A,
const size_t _m,
const size_t _n) {
376 const std::unique_ptr<double[]> tmpA(
new double[_m*_n]);
383 void inplace_qr(
double*
const _AtoQ,
double*
const _R,
const size_t _m,
const size_t _n) {
388 void qr_destructive(
double*
const _Q,
double*
const _R,
double*
const _A,
const size_t _m,
const size_t _n) {
389 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
390 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
392 REQUIRE(_n > 0,
"Dimension n must be larger than zero");
393 REQUIRE(_m > 0,
"Dimension m must be larger than zero");
395 REQUIRE(_Q && _R && _A,
"QR decomposition must not be called with null pointers: Q:" << _Q <<
" R: " << _R <<
" A: " << _A);
396 REQUIRE(_A != _R,
"_A and _R must be different, otherwise qr call will fail.");
401 const size_t rank = std::min(_m, _n);
404 const std::unique_ptr<double[]> tau(
new double[rank]);
408 IF_CHECK(
int lapackAnswer = ) LAPACKE_dgeqrf(LAPACK_ROW_MAJOR, static_cast<int>(_m), static_cast<int>(_n), _A, static_cast<int>(_n), tau.get());
409 CHECK(lapackAnswer == 0, error,
"Unable to perform QR factorisaton. Lapacke says: " << lapackAnswer );
412 for(
size_t row =0; row < rank; ++row) {
414 misc::copy(_R+row*_n+row, _A+row*_n+row, _n-row);
419 IF_CHECK( lapackAnswer = ) LAPACKE_dorgqr(LAPACK_ROW_MAJOR, static_cast<int>(_m), static_cast<int>(rank), static_cast<int>(rank), _A, static_cast<int>(_n), tau.get());
420 CHECK(lapackAnswer == 0, error,
"Unable to reconstruct Q from the QR factorisation. Lapacke says: " << lapackAnswer);
427 for(
size_t row = 0; row < _m; ++row) {
431 }
else if(_n != rank) {
432 for(
size_t row = 1; row < _m; ++row) {
441 void rq(
double*
const _R,
double*
const _Q,
const double*
const _A,
const size_t _m,
const size_t _n) {
443 const std::unique_ptr<double[]> tmpA(
new double[_m*_n]);
450 void inplace_rq(
double*
const _R,
double*
const _AtoQ,
const size_t _m,
const size_t _n) {
455 void rq_destructive(
double*
const _R,
double*
const _Q,
double*
const _A,
const size_t _m,
const size_t _n) {
456 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
457 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
459 REQUIRE(_n > 0,
"Dimension n must be larger than zero");
460 REQUIRE(_m > 0,
"Dimension m must be larger than zero");
462 REQUIRE(_Q && _R && _A,
"QR decomposition must not be called with null pointers: R " << _R <<
" Q: " << _Q <<
" A: " << _A);
463 REQUIRE(_A != _R,
"_A and _R must be different, otherwise qr call will fail.");
468 const size_t rank = std::min(_m, _n);
471 const std::unique_ptr<double[]> tau(
new double[rank]);
473 IF_CHECK(
int lapackAnswer = ) LAPACKE_dgerqf(LAPACK_ROW_MAJOR, static_cast<int>(_m), static_cast<int>(_n), _A, static_cast<int>(_n), tau.get());
474 CHECK(lapackAnswer == 0, error,
"Unable to perform QR factorisaton. Lapacke says: " << lapackAnswer <<
". Call was: LAPACKE_dgerqf(LAPACK_ROW_MAJOR, "<<static_cast<int>(_m)<<
", "<<static_cast<int>(_n)<<
", "<<_A<<
", "<<static_cast<int>(_n)<<
", "<<tau.get()<<
");" );
479 for( ; row < _m - rank; ++row) {
480 misc::copy(_R+row*rank, _A+row*_n+_n-rank, rank);
482 for(
size_t skip = 0; row < _m; ++row, ++skip) {
484 misc::copy(_R+row*rank+skip, _A+row*_n+_n-rank+skip, rank-skip);
488 IF_CHECK( lapackAnswer = ) LAPACKE_dorgrq(LAPACK_ROW_MAJOR, static_cast<int>(rank), static_cast<int>(_n), static_cast<int>(rank), _A+(_m-rank)*_n, static_cast<int>(_n), tau.get());
489 CHECK(lapackAnswer == 0, error,
"Unable to reconstruct Q from the RQ factorisation. Lapacke says: " << lapackAnswer <<
". Call was: LAPACKE_dorgrq(LAPACK_ROW_MAJOR, "<<static_cast<int>(rank)<<
", "<<static_cast<int>(_n)<<
", "<<static_cast<int>(rank)<<
", "<<_A+(_m-rank)*_n<<
", "<<static_cast<int>(_n)<<
", "<<tau.get()<<
");");
503 for (
size_t i=0; i<_n*_n; ++i) {
504 max = std::max(max, _A[i]);
507 for (
size_t i=0; i<_n; ++i) {
508 for (
size_t j=i+1; j<_n; ++j) {
509 if (std::abs(_A[i*_n + j] - _A[i + j*_n]) >= 4 * max * std::numeric_limits<double>::epsilon()) {
520 bool positive = (_A[0] > 0);
521 const size_t steps=_n+1;
523 for (
size_t i=1; i<_n; ++i) {
524 if (_A[i*steps] < std::numeric_limits<double>::epsilon()) {
530 for (
size_t i=1; i<_n; ++i) {
531 if (_A[i*steps] > -std::numeric_limits<double>::epsilon()) {
542 void solve(
double*
const _x,
const double*
const _A,
const size_t _m,
const size_t _n,
const double*
const _b,
const size_t _nrhs) {
543 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
544 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
545 REQUIRE(_nrhs <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
547 const std::unique_ptr<double[]> tmpA(
new double[_m*_n]);
550 LOG(debug,
"solving with...");
555 const std::unique_ptr<double[]> tmpB(
new double[_m*_nrhs]);
566 std::unique_ptr<int[]> pivot(
new int[_n]);
570 IF_CHECK(
int lapackAnswer = ) LAPACKE_dgesv(
572 static_cast<int>(_n),
573 static_cast<int>(_nrhs),
575 static_cast<int>(_n),
578 static_cast<int>(_nrhs)
580 CHECK(lapackAnswer == 0, error,
"Unable to solve Ax = b (PLU solver). Lapacke says: " << lapackAnswer);
588 LOG(debug,
"trying cholesky");
589 int lapackAnswer = 0;
593 lapackAnswer = LAPACKE_dpotrf2(
596 static_cast<int>(_n),
604 if (lapackAnswer == 0) {
605 LOG(debug,
"cholesky");
610 lapackAnswer = LAPACKE_dpotrs(
613 static_cast<int>(_n),
614 static_cast<int>(_nrhs),
616 static_cast<int>(_n),
618 static_cast<int>(_nrhs)
620 CHECK(lapackAnswer == 0, error,
"Unable to solve Ax = b (cholesky solver). Lapacke says: " << lapackAnswer);
636 std::unique_ptr<int[]> pivot(
new int[_n]);
641 static_cast<int>(_n),
642 static_cast<int>(_nrhs),
644 static_cast<int>(_n),
647 static_cast<int>(_nrhs)
654 void solve_least_squares(
double*
const _x,
const double*
const _A,
const size_t _m,
const size_t _n,
const double*
const _b,
const size_t _p){
655 const std::unique_ptr<double[]> tmpA(
new double[_m*_n]);
658 const std::unique_ptr<double[]> tmpB(
new double[_m*_p]);
666 REQUIRE(_m <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
667 REQUIRE(_n <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
668 REQUIRE(_p <= static_cast<size_t>(std::numeric_limits<int>::max()),
"Dimension to large for BLAS/Lapack");
672 std::unique_ptr<int[]> pivot(
new int[_n]);
675 std::unique_ptr<double[]> signulars(
new double[std::min(_n, _m)]);
701 IF_CHECK(
int lapackAnswer = ) LAPACKE_dgelsd(
703 static_cast<int>(_m),
704 static_cast<int>(_n),
705 static_cast<int>(_p),
707 static_cast<int>(_n),
709 static_cast<int>(_p),
714 CHECK(lapackAnswer == 0, error,
"Unable to solves min ||Ax - b||_2 for x. Lapacke says: " << lapackAnswer <<
" sizes are " << _m <<
" x " << _n <<
" * " << _p);
Header file for some additional math functions.
std::tuple< std::unique_ptr< double[]>, std::unique_ptr< double[]>, size_t > cq(const double *const _A, const size_t _m, const size_t _n)
: splits A = C*Q, with _C an rxm matrix (where r is the rank of _A) and _Q orthogonal.
Header file for CHECK and REQUIRE macros.
std::tuple< std::unique_ptr< double[]>, std::unique_ptr< double[]>, size_t > qc(const double *const _A, const size_t _m, const size_t _n)
: splits A = Q*C, with _C an rxn matrix (where r is the rank of _A) and _Q orthogonal.
Header file for the blas and lapack wrapper functions.
void qr(double *const _Q, double *const _R, const double *const _A, const size_t _m, const size_t _n)
: Performs (Q,R) = QR(A)
static bool pos_neg_definite_diagonal(const double *const _A, const size_t _n)
checks whether the diagonal of _A is all positive or all negative. returns false otherwise ...
void copy_inplace(T *const _x, const T *const _y, const size_t _n) noexcept
Copys _n entries from _y to _x, allowing the accessed memry regions of _y and _x to overlap...
void matrix_matrix_product(double *const _C, const size_t _leftDim, const size_t _rightDim, const double _alpha, const double *const _A, const size_t _lda, const bool _transposeA, const size_t _middleDim, const double *const _B, const size_t _ldb, const bool _transposeB)
: Performs the Matrix-Matrix product C = alpha*OP(A) * OP(B)
void inplace_rq(double *const _R, double *const _AtoQ, const size_t _m, const size_t _n)
: Performs (R,AtoQ) = RQ(AtoQ)
The main namespace of xerus.
void inplace_qr(double *const _AtoQ, double *const _R, const size_t _m, const size_t _n)
: Performs (AtoQ,R) = QR(AtoQ)
void svd_destructive(double *const _U, double *const _S, double *const _Vt, double *const _A, const size_t _m, const size_t _n)
: Performs (U,S,V) = SVD(A). Destroys A.
void rq(double *const _R, double *const _Q, const double *const _A, const size_t _m, const size_t _n)
: Performs (R,Q) = RQ(A)
double dot_product(const double *const _x, const size_t _n, const double *const _y)
: Computes the dot product = x^T*y
double one_norm(const double *const _x, const size_t _n)
: Computes the one norm =||x||_1
void dyadic_vector_product(double *_A, const size_t _m, const size_t _n, const double _alpha, const double *const _x, const double *const _y)
: Performs A = alpha*x*y^T
std::tuple< std::unique_ptr< double[]>, std::unique_ptr< double[]>, size_t > qc_destructive(double *const _A, const size_t _m, const size_t _n)
: splits A = Q*C, with _C an rxn matrix (where r is the rank of _A) and _Q orthogonal. Destroys A.
void solve(double *_x, const double *_A, size_t _m, size_t _n, const double *_b, size_t _p)
: Solves Ax = b for x
static bool is_symmetric(const double *const _A, const size_t _n)
Header file for the low level array support function.
Header file for comfort functions and macros that should not be exported in the library.
constexpr const value_t EPSILON
The default epsilon value in xerus.
Header file for some elementary string manipulation routines.
double two_norm(const double *const _x, const size_t _n)
: Computes the two norm =||x||_2
void qr_destructive(double *const _Q, double *const _R, double *const _A, const size_t _m, const size_t _n)
: Performs (Q,R) = QR(A), destroys A in the process
Header file for shorthand notations that are xerus specific but used throughout the library...
void svd(double *const _U, double *const _S, double *const _Vt, const double *const _A, const size_t _m, const size_t _n)
: Performs (U,S,V) = SVD(A)
void copy(T *const __restrict _dest, const T *const __restrict _src, const size_t _n) noexcept
Copys _n entries from _y to _x, where _y and _x must be disjunkt memory regions.
static XERUS_force_inline std::string to_string(const bool obj)
void set_zero(T *const __restrict _x, const size_t _n) noexcept
Sets all entries equal to zero.
void matrix_vector_product(double *const _x, const size_t _m, const double _alpha, const double *const _A, const size_t _n, const bool _transposed, const double *const _y)
: Perfroms x = alpha*OP(A)*y
void rq_destructive(double *const _R, double *const _Q, double *const _A, const size_t _m, const size_t _n)
: Performs (R,Q) = RQ(A), destroys A in the process
void solve_least_squares_destructive(double *const _x, double *const _A, const size_t _m, const size_t _n, double *const _b, const size_t _p)
: Solves min ||Ax - b||_2 for x
std::tuple< std::unique_ptr< double[]>, std::unique_ptr< double[]>, size_t > cq_destructive(double *const _A, const size_t _m, const size_t _n)
: splits A = C*Q, with _C an rxm matrix (where r is the rank of _A) and _Q orthogonal. Destroys A.
Header file for global shorthand notations of elementary integer types and attribute lists...
void solve_least_squares(double *const _x, const double *const _A, const size_t _m, const size_t _n, const double *const _b, const size_t _p)
: Solves min ||Ax - b||_2 for x