LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
svd.h
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2012-2026, QORE Lab, Imperial College London
3
* All rights reserved.
4
*/
5
#ifndef LINE_UTIL_SVD_H
6
#define LINE_UTIL_SVD_H
7
8
/**
9
* @file
10
* @ingroup line_util
11
* Singular value decomposition WITH the singular vectors, and the
12
* Moore-Penrose pseudo-inverse built from it.
13
*
14
* util/eig.h already exposes the singular VALUES (dgesvd with jobu = jobvt =
15
* 'N'), which is all the rank and conditioning tests need. This header adds
16
* the factors themselves, for the one place in the port that needs them: the
17
* pinv fallback of ldqbd_R, taken when a level's local block is singular.
18
*
19
* DOUBLE ONLY, for the same reason eig.h is: singular values of a rational
20
* matrix are algebraic, not rational, so there is no exact instantiation to
21
* offer, and LAPACK has no multiprecision path. Callers templated on T must
22
* convert and state the precision loss at the call site. Configured without
23
* LAPACK the entry points throw UnsupportedError naming the dependency rather
24
* than substituting anything.
25
*
26
* The Fortran symbol dgesvd_ is declared by eig.h under the same LAPACK guard
27
* and is reused from there, so there is exactly one declaration of it in the
28
* tree.
29
*/
30
31
#include <cstddef>
32
#include <vector>
33
34
#include "
line/util/eig.h
"
35
#include "
line/util/error.h
"
36
#include "
line/util/matrix.h
"
37
38
namespace
line
{
39
40
/** A = U diag(s) Vt, with U (m x m), s of length min(m,n) and Vt (n x n). */
41
struct
SvdFactors
{
42
Matrix<double>
U
;
43
std::vector<double>
s
;
44
Matrix<double>
Vt
;
45
};
46
47
/** Full SVD of a real matrix, singular values in descending order. */
48
inline
SvdFactors
svd_full
(
const
Matrix<double>
& A) {
49
#ifndef LINE_MP_HAVE_LAPACK
50
(void)A;
51
throw
UnsupportedError
(
52
"svd_full requires LAPACK: reconfigure with -DLINE_MP_USE_LAPACK=ON and liblapack "
53
"available"
);
54
#else
55
const
std::size_t m = A.
rows
(), n = A.
cols
();
56
if
(m == 0 || n == 0)
throw
InputError
(
"svd_full: empty matrix"
);
57
58
// LAPACK is column-major, the port is row-major, so transpose on the way in.
59
std::vector<double> a(m * n);
60
for
(std::size_t i = 0; i < m; ++i)
61
for
(std::size_t j = 0; j < n; ++j) a[j * m + i] = A(i, j);
62
63
const
int
mi =
static_cast<
int
>
(m), nj =
static_cast<
int
>
(n);
64
const
std::size_t k = m < n ? m : n;
65
std::vector<double> s(k), u(m * m), vt(n * n);
66
int
info = 0, lwork = -1;
67
double
wopt = 0.0;
68
dgesvd_(
"A"
,
"A"
, &mi, &nj, a.data(), &mi, s.data(), u.data(), &mi, vt.data(), &nj, &wopt,
69
&lwork, &info);
70
if
(info != 0)
throw
NumericError
(
"svd_full: LAPACK workspace query failed"
);
71
lwork =
static_cast<
int
>
(wopt);
72
std::vector<double> work(
static_cast<
std::size_t
>
(lwork));
73
dgesvd_(
"A"
,
"A"
, &mi, &nj, a.data(), &mi, s.data(), u.data(), &mi, vt.data(), &nj, work.data(),
74
&lwork, &info);
75
if
(info != 0)
throw
NumericError
(
"svd_full: LAPACK dgesvd failed to converge"
);
76
77
SvdFactors
out;
78
out.
s
= s;
79
out.
U
=
Matrix<double>
(m, m);
80
for
(std::size_t i = 0; i < m; ++i)
81
for
(std::size_t j = 0; j < m; ++j) out.
U
(i, j) = u[j * m + i];
82
out.
Vt
=
Matrix<double>
(n, n);
83
for
(std::size_t i = 0; i < n; ++i)
84
for
(std::size_t j = 0; j < n; ++j) out.
Vt
(i, j) = vt[j * n + i];
85
return
out;
86
#endif
87
}
88
89
/**
90
* Moore-Penrose pseudo-inverse, A^+ = V diag(1/s_i) U^T over the singular
91
* values above max(m,n) eps sigma_1, which is MATLAB's default pinv tolerance.
92
*/
93
inline
Matrix<double>
pinv
(
const
Matrix<double>
& A) {
94
const
SvdFactors
f =
svd_full
(A);
95
const
std::size_t m = A.
rows
(), n = A.
cols
();
96
const
std::size_t k = f.
s
.size();
97
const
std::size_t dim = m > n ? m : n;
98
const
double
tol =
static_cast<
double
>
(dim) * 2.220446049250313e-16 * (k ? f.
s
[0] : 0.0);
99
Matrix<double>
X(n, m, 0.0);
100
for
(std::size_t r = 0; r < k; ++r) {
101
if
(!(f.
s
[r] > tol))
continue
;
102
const
double
inv = 1.0 / f.
s
[r];
103
// X += inv * V(:,r) * U(:,r)^T, with V(:,r) the r-th ROW of Vt.
104
for
(std::size_t i = 0; i < n; ++i) {
105
const
double
vi = f.
Vt
(r, i);
106
if
(vi == 0.0)
continue
;
107
for
(std::size_t j = 0; j < m; ++j) X(i, j) += inv * vi * f.
U
(j, r);
108
}
109
}
110
return
X;
111
}
112
113
}
// namespace line
114
115
#endif
// LINE_UTIL_SVD_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::Matrix
Definition
matrix.h:56
line::Matrix::cols
std::size_t cols() const
Definition
matrix.h:90
line::Matrix::rows
std::size_t rows() const
Definition
matrix.h:89
line::NumericError::NumericError
NumericError(const std::string &what)
Definition
error.h:45
line::UnsupportedError::UnsupportedError
UnsupportedError(const std::string &what)
Definition
error.h:51
eig.h
Eigenvalues and singular values, backed by LAPACK.
error.h
The exception types the port throws.
matrix.h
Dense matrix and non-owning view.
line
Definition
aoi_dist2ph.h:52
line::pinv
Matrix< double > pinv(const Matrix< double > &A)
Moore-Penrose pseudo-inverse, A^+ = V diag(1/s_i) U^T over the singular values above max(m,...
Definition
svd.h:93
line::svd_full
SvdFactors svd_full(const Matrix< double > &A)
Full SVD of a real matrix, singular values in descending order.
Definition
svd.h:48
line::SvdFactors
A = U diag(s) Vt, with U (m x m), s of length min(m,n) and Vt (n x n).
Definition
svd.h:41
line::SvdFactors::Vt
Matrix< double > Vt
Definition
svd.h:44
line::SvdFactors::U
Matrix< double > U
Definition
svd.h:42
line::SvdFactors::s
std::vector< double > s
Definition
svd.h:43
include
line
util
svd.h
Generated by
1.18.0