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 不是唯一的,所以两个结果都是正确的。第一个奇异向量的符号发生变化是因为对于非常小的矩阵,开始使用另一种更快的方法来简化为双对角形式。