LAPACKE_dgesvd 的英特尔 MKL 不匹配结果
Intel MKL Mismatch results of LAPACKE_dgesvd
我的代码中调用了 LAPACKE_dgesvd 函数。此代码包含在自动测试中。在编译器迁移后,我们决定将 MKL 也从 11.3.4 升级到 2019.0.5。
并且测试变成了红色。经过深入调查,我发现这个函数不再返回相同的 U 和 V 矩阵。
我提取代码并在单独的 env/project 和相同的观察中将其设为 运行。观察是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.629
0.575268 -0.579935 0.576838 -0.575268 -0.579935 0.576838
0.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.803572
0.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)
感谢您的帮助和最诚挚的问候
莫达
奇异值分解不是唯一的。例如,如果我们有一个 SVD 分解(例如一组矩阵 U、S、V)使得 A=U* S* V^T 那么这组矩阵(-U、S、-V)也是一个 SVD分解,因为 (-U) S (-V^T) = USV^T = A。此外,如果 D 是对角矩阵,其对角元素等于-1 或 1 那么矩阵集 UD, S, VD 也是 SVD 分解因为 (UD)SDV^T = US*V^T =A.
因为通过比较两组矩阵来验证 SVD 分解不是一个好主意。 LAPACK 用户指南和许多其他出版物一样建议检查计算的 SVD 分解的以下条件:
1. || A V – US || / ||一个||应该足够小
2. || U^T *U – 我 ||接近于零
3. || V^T *V – 我 ||接近于零
4.对角线S的所有对角线项必须为正,并按降序排列。上面给出的所有表达式的误差范围可以在 https://www.netlib.org/lapack/lug/node97.html
上找到
因此 post-return 中提到的两个 MKL 版本满足所有 4 个误差范围的奇异值和奇异向量。因为那并且因为 SVD 不是唯一的,所以两个结果都是正确的。第一个奇异向量的符号发生变化是因为对于非常小的矩阵,开始使用另一种更快的方法来简化为双对角形式。
我的代码中调用了 LAPACKE_dgesvd 函数。此代码包含在自动测试中。在编译器迁移后,我们决定将 MKL 也从 11.3.4 升级到 2019.0.5。
并且测试变成了红色。经过深入调查,我发现这个函数不再返回相同的 U 和 V 矩阵。
我提取代码并在单独的 env/project 和相同的观察中将其设为 运行。观察是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.629 0.575268 -0.579935 0.576838 -0.575268 -0.579935 0.576838 0.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.803572 0.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)
感谢您的帮助和最诚挚的问候
莫达
奇异值分解不是唯一的。例如,如果我们有一个 SVD 分解(例如一组矩阵 U、S、V)使得 A=U* S* V^T 那么这组矩阵(-U、S、-V)也是一个 SVD分解,因为 (-U) S (-V^T) = USV^T = A。此外,如果 D 是对角矩阵,其对角元素等于-1 或 1 那么矩阵集 UD, S, VD 也是 SVD 分解因为 (UD)SDV^T = US*V^T =A.
因为通过比较两组矩阵来验证 SVD 分解不是一个好主意。 LAPACK 用户指南和许多其他出版物一样建议检查计算的 SVD 分解的以下条件: 1. || A V – US || / ||一个||应该足够小 2. || U^T *U – 我 ||接近于零 3. || V^T *V – 我 ||接近于零 4.对角线S的所有对角线项必须为正,并按降序排列。上面给出的所有表达式的误差范围可以在 https://www.netlib.org/lapack/lug/node97.html
上找到因此 post-return 中提到的两个 MKL 版本满足所有 4 个误差范围的奇异值和奇异向量。因为那并且因为 SVD 不是唯一的,所以两个结果都是正确的。第一个奇异向量的符号发生变化是因为对于非常小的矩阵,开始使用另一种更快的方法来简化为双对角形式。