我在我的代码中有一个对LAPACKE_dgesvd函数的调用。该代码由自动测试覆盖。在编译器迁移后,我们决定将MKL也从11.3.4升级到2019.0.5。
测试变成红色。经过深入研究,我发现此函数不再返回相同的U&V矩阵。
我提取了代码,并使其在单独的环境/项目中运行并具有相同的观察结果。观察结果是U的第一列和V的第一行具有相反的符号
您能告诉我我在做什么错吗?还是应该使用新版本来获得旧结果?
我做了一个简单的项目,可以轻松地重现该问题。这是代码:
// MKL.cpp : This file contains the 'main' function. Program execution begins and ends there
#include <iostream>
#include <algorithm>
#include <mkl.h>
int main()
{
const int rows(3), cols(3);
double covarMatrix[rows*cols] = { 0.9992441421012894, -0.6088405718211041, -0.4935146797825398,
-0.6088405718211041, 0.9992441421012869, -0.3357678733652218,
-0.4935146797825398, -0.3357678733652218, 0.9992441421012761};
double U[rows*rows] = { -1,-1,-1,
-1,-1,-1,
-1,-1,-1 };
double V[cols*cols] = { -1,-1,-1,
-1,-1,-1,
-1,-1,-1 };
double superb[std::min(rows, cols) - 1];
double eigenValues[std::max(rows, cols)];
MKL_INT info = LAPACKE_dgesvd(LAPACK_ROW_MAJOR, 'A', 'A',
rows, cols, covarMatrix, cols, eigenValues, U, rows, V, cols, superb);
if (info > 0)
std::cout << "not converged!\n";
std::cout << "U\n";
for (int row(0); row < rows; ++row)
{
for (int col(0); col < rows; ++col)
std::cout << U[row * rows + col] << " ";
std::cout << std::endl;
}
std::cout << "V\n";
for (int row(0); row < cols; ++row)
{
for (int col(0); col < cols; ++col)
std::cout << V[row * rows + col] << " ";
std::cout << std::endl;
}
std::cout << "Converged!\n";
}
这里有更多的数字解释:
A = 0.9992441421012894,-0.6088405718211041,-0.4935146797825398,-0.6088405718211041、0.9992441421012869,-0.3357678733652218,-0.4935146797825398,-0.3357678733652218,0.9992441421012761
结果:
11.3.4 2019.0.5&2020.1.216
U
-0.765774 -0.13397 0.629 0.765774 -0.13397 0.6290.575268 -0.579935 0.576838 -0.575268 -0.579935 0.5768380.2875 0.803572 0.521168 -0.2875 0.803572 0.521168
V
-0.765774 0.575268 0.2875 0.765774 -0.575268 -0.2875-0.13397 -0.579935 0.803572 -0.13397 -0.579935 0.8035720.629 0.576838 0.521168 0.629 0.576838 0.521168
我使用scipy测试,结果与11.3.4版本相同。
from scipy import linalg
from numpy import array
A = array([[0.9992441421012894, -0.6088405718211041, -0.4935146797825398], [-0.6088405718211041, 0.9992441421012869, -0.3357678733652218], [-0.4935146797825398, -0.3357678733652218, 0.9992441421012761]])
print(A)
u,s,vt,info = linalg.lapack.dgesvd(A)
print(u)
print(s)
print(vt)
print(info)
感谢您的帮助和最诚挚的问候
Mokhtar
奇异值分解不是唯一的。例如,如果我们进行了SVD分解(例如,一组矩阵U,S,V),从而A = U * S * V ^ T,则该组矩阵(-U,S,-V)也是SVD因为(-U)S (-V ^ T)= U S V ^ T =A。 U D,S,V D也是SVD分解,因为(U D)S D V ^ T = U S * V ^ T = A。
因为通过比较两组矩阵来验证SVD分解不是一个好主意。与许多其他出版物一样,《 LAPACK用户指南》建议检查计算的SVD分解的以下条件:1. || A V – U S || / || A ||应该足够小2. || U ^ T * U –我||接近零3. || V ^ T * V – I ||接近零4.对角线S的所有对角线项必须为正,并以降序排列。上面给出的所有表达式的错误范围可以在https://www.netlib.org/lapack/lug/node97.html
中找到因此,后文中提到的两个MKL版本都返回满足所有4个误差范围的奇异值和奇异矢量。既然如此,并且由于SVD不是唯一的,因此两种结果都是正确的。第一个奇异矢量的符号发生变化是因为对于非常小的矩阵,开始使用另一种更快的方法来简化为对角线形式。