35 #if defined(QL_EXTRA_SAFETY_CHECKS)
36 void checkSymmetry(
const Matrix& matrix) {
37 Size size = matrix.rows();
39 "non square matrix: " << size <<
" rows, " <<
40 matrix.columns() <<
" columns");
41 for (
Size i=0; i<size; ++i)
42 for (
Size j=0; j<i; ++j)
44 "non symmetric matrix: " <<
45 "[" << i <<
"][" << j <<
"]=" << matrix[i][j] <<
46 ", [" << j <<
"][" << i <<
"]=" << matrix[j][i]);
50 void normalizePseudoRoot(
const Matrix& matrix,
52 Size size = matrix.rows();
54 "matrix/pseudo mismatch: matrix rows are " << size <<
55 " while pseudo rows are " << pseudo.columns());
56 Size pseudoCols = pseudo.columns();
59 for (
Size i=0; i<size; ++i) {
61 for (
Size j=0; j<pseudoCols; ++j)
62 norm += pseudo[i][j]*pseudo[i][j];
64 Real normAdj = std::sqrt(matrix[i][i]/norm);
65 for (
Size j=0; j<pseudoCols; ++j)
66 pseudo[i][j] *= normAdj;
74 class HypersphereCostFunction :
public CostFunction {
82 HypersphereCostFunction(
const Matrix& targetMatrix,
88 Array values(
const Array&)
const override {
89 QL_FAIL(
"values method not implemented");
91 Real value(
const Array& x)
const override {
95 for (i=0; i<
size_; i++) {
96 for (k=0; k<
size_; k++) {
100 for (j=0; j<=k; j++) {
103 std::cos(x[i*(i-1)/2+j]);
106 std::sin(x[i*(i-1)/2+j]);
112 for (i=0; i<
size_; i++) {
113 for (k=0; k<
size_; k++) {
114 for (j=0; j<=k; j++) {
115 if (j == k && k!=size_-1)
117 std::cos(x[j*size_+i]);
120 std::sin(x[j*size_+i]);
128 for (i=0;i<
size_;i++) {
129 for (j=0;j<
size_;j++) {
140 Matrix hypersphereOptimize(
const Matrix& targetMatrix,
141 const Matrix& currentRoot,
142 const bool lowerDiagonal) {
143 Size i,j,k,size = targetMatrix.rows();
144 Matrix result(currentRoot);
146 for (i=0; i<size; i++){
147 variance[i]=std::sqrt(targetMatrix[i][i]);
150 Matrix approxMatrix(result*
transpose(result));
152 for (i=0; i<size; i++) {
153 for (j=0; j<size; j++) {
154 result[i][j]/=std::sqrt(approxMatrix[i][i]);
158 for (i=0; i<size; i++) {
159 for (j=0; j<size; j++) {
165 ConjugateGradient optimize;
166 EndCriteria endCriteria(100, 10, 1e-8, 1e-8, 1e-8);
167 HypersphereCostFunction costFunction(targetMatrix,
variance,
169 NoConstraint constraint;
174 Array
theta(size * (size-1)/2);
175 const Real eps=1e-16;
176 for (i=1; i<size; i++) {
177 for (j=0; j<i; j++) {
178 theta[i*(i-1)/2+j]=result[i][j];
179 if (
theta[i*(i-1)/2+j]>1-eps)
180 theta[i*(i-1)/2+j]=1-eps;
181 if (
theta[i*(i-1)/2+j]<-1+eps)
182 theta[i*(i-1)/2+j]=-1+eps;
183 for (k=0; k<j; k++) {
184 theta[i*(i-1)/2+j] /= std::sin(
theta[i*(i-1)/2+k]);
185 if (
theta[i*(i-1)/2+j]>1-eps)
186 theta[i*(i-1)/2+j]=1-eps;
187 if (
theta[i*(i-1)/2+j]<-1+eps)
188 theta[i*(i-1)/2+j]=-1+eps;
190 theta[i*(i-1)/2+j] = std::acos(
theta[i*(i-1)/2+j]);
197 Problem p(costFunction, constraint,
theta);
198 optimize.minimize(p, endCriteria);
199 theta = p.currentValue();
200 std::fill(result.begin(),result.end(),1.0);
201 for (i=0; i<size; i++) {
202 for (k=0; k<size; k++) {
206 for (j=0; j<=k; j++) {
209 std::cos(
theta[i*(i-1)/2+j]);
212 std::sin(
theta[i*(i-1)/2+j]);
218 Array
theta(size * (size-1));
219 const Real eps=1e-16;
220 for (i=0; i<size; i++) {
221 for (j=0; j<size-1; j++) {
222 theta[j*size+i]=result[i][j];
223 if (
theta[j*size+i]>1-eps)
224 theta[j*size+i]=1-eps;
225 if (
theta[j*size+i]<-1+eps)
226 theta[j*size+i]=-1+eps;
229 if (
theta[j*size+i]>1-eps)
230 theta[j*size+i]=1-eps;
231 if (
theta[j*size+i]<-1+eps)
232 theta[j*size+i]=-1+eps;
236 if (result[i][j+1]<0)
241 Problem p(costFunction, constraint,
theta);
242 optimize.minimize(p, endCriteria);
243 theta=p.currentValue();
244 std::fill(result.begin(),result.end(),1.0);
245 for (i=0; i<size; i++) {
246 for (k=0; k<size; k++) {
247 for (j=0; j<=k; j++) {
248 if (j == k && k!=size-1)
249 result[i][k] *= std::cos(
theta[j*size+i]);
251 result[i][k] *= std::sin(
theta[j*size+i]);
257 for (i=0; i<size; i++) {
258 for (j=0; j<size; j++) {
267 Real normInf(
const Matrix& M) {
268 Size rows = M.rows();
269 Size cols = M.columns();
271 for (
Size i=0; i<rows; ++i) {
273 for (
Size j=0; j<cols; ++j)
274 colSum += std::fabs(M[i][j]);
275 norm = std::max(norm, colSum);
281 Matrix projectToUnitDiagonalMatrix(
const Matrix& M) {
282 Size size = M.rows();
284 "matrix not square");
287 for (
Size i=0; i<size; ++i)
294 Matrix projectToPositiveSemidefiniteMatrix(Matrix& M) {
295 Size size = M.rows();
297 "matrix not square");
299 Matrix diagonal(size, size, 0.0);
300 SymmetricSchurDecomposition jd(M);
301 for (
Size i=0; i<size; ++i)
302 diagonal[i][i] = std::max<Real>(jd.eigenvalues()[i], 0.0);
305 jd.eigenvectors()*diagonal*
transpose(jd.eigenvectors());
311 Matrix highamImplementation(
const Matrix& A,
const Size maxIterations,
const Real& tolerance) {
313 Size size = A.rows();
314 Matrix R, Y(A), X(A), deltaS(size, size, 0.0);
319 for (
Size i=0; i<maxIterations; ++i) {
321 X = projectToPositiveSemidefiniteMatrix(R);
323 Y = projectToUnitDiagonalMatrix(X);
326 if (std::max(normInf(X-lastX)/normInf(X),
327 std::max(normInf(Y-lastY)/normInf(Y),
328 normInf(Y-X)/normInf(Y)))
338 for (
Size i=0; i<size; ++i)
339 for (
Size j=0; j<i; ++j)
350 #if defined(QL_EXTRA_SAFETY_CHECKS)
351 checkSymmetry(matrix);
354 "non square matrix: " << size <<
" rows, " <<
355 matrix.
columns() <<
" columns");
360 Matrix diagonal(size, size, 0.0);
363 Matrix result(size, size);
369 "negative eigenvalue(s) ("
376 for (
Size i=0; i<size; i++)
378 std::sqrt(std::max<Real>(jd.
eigenvalues()[i], 0.0));
381 normalizePseudoRoot(matrix, result);
386 for (
Size i=0; i<size; ++i){
388 std::sqrt(std::max<Real>(jd.
eigenvalues()[i], 0.0));
392 normalizePseudoRoot(matrix, result);
395 result = hypersphereOptimize(matrix, result,
false);
400 for (
Size i=0; i<size; ++i){
402 std::sqrt(std::max<Real>(jd.
eigenvalues()[i], 0.0));
407 normalizePseudoRoot(matrix, result);
410 result = hypersphereOptimize(matrix, result,
true);
413 int maxIterations = 40;
415 result = highamImplementation(matrix, maxIterations, tol);
420 QL_FAIL(
"unknown salvaging algorithm");
429 Real componentRetainedPercentage,
433 #if defined(QL_EXTRA_SAFETY_CHECKS)
434 checkSymmetry(matrix);
437 "non square matrix: " << size <<
" rows, " <<
438 matrix.
columns() <<
" columns");
442 "no eigenvalues retained");
445 "percentage to be retained > 100%");
448 "max rank required < 1");
459 "negative eigenvalue(s) ("
460 << std::scientific << eigenValues[size-1]
465 for (
Size i=0; i<size; ++i)
466 eigenValues[i] = std::max<Real>(eigenValues[i], 0.0);
470 int maxIterations = 40;
471 Real tolerance = 1e-6;
472 Matrix adjustedMatrix = highamImplementation(matrix, maxIterations, tolerance);
478 QL_FAIL(
"unknown or invalid salvaging algorithm");
482 Real enough = componentRetainedPercentage *
483 std::accumulate(eigenValues.
begin(),
485 if (componentRetainedPercentage == 1.0) {
490 Real components = eigenValues[0];
491 Size retainedFactors = 1;
492 for (
Size i=1; components<enough && i<size; ++i) {
493 components += eigenValues[i];
497 retainedFactors=std::min(retainedFactors, maxRank);
499 Matrix diagonal(size, retainedFactors, 0.0);
500 for (
Size i=0; i<retainedFactors; ++i)
501 diagonal[i][i] = std::sqrt(eigenValues[i]);
504 normalizePseudoRoot(matrix, result);
1-D array used in linear algebra.
const_iterator end() const
const_iterator begin() const
Matrix used in linear algebra.
symmetric threshold Jacobi algorithm.
const Matrix & eigenvectors() const
const Array & eigenvalues() const
floating-point comparisons
Conjugate gradient optimization method.
Abstract constraint class.
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
#define QL_FAIL(message)
throw an error (possibly with file and line information)
LinearInterpolation variance
std::size_t Size
size of a container
Matrix pseudoSqrt(const Matrix &matrix, SalvagingAlgorithm::Type sa)
Matrix rankReducedSqrt(const Matrix &matrix, Size maxRank, Real componentRetainedPercentage, SalvagingAlgorithm::Type sa)
Matrix CholeskyDecomposition(const Matrix &S, bool flexible)
bool close(const Quantity &m1, const Quantity &m2, Size n)
Matrix transpose(const Matrix &m)
Abstract optimization problem class.
pseudo square root of a real symmetric matrix
Eigenvalues/eigenvectors of a real symmetric matrix.