使用加速框架的矩阵乘法和逆问题

Posted

技术标签:

【中文标题】使用加速框架的矩阵乘法和逆问题【英文标题】:Matrix multiplication and inverse problems with accelerate framework 【发布时间】:2015-03-20 22:23:29 【问题描述】:

我试图在 Objective-C 中将两个矩阵相乘。我已经在 Xcode 中将加速框架导入到我的项目中,一切都编译得很好。我在计算器上进行了矩阵乘法并得到了正确的值,但是,在运行代码时,我没有。这就是我定义矩阵的方式..

float matrixA [3][3] = -.045, -.141, -.079, -.012, -.079, .0578, .112, -.011, -.0830;

float matrixB [3][1] = 40, -61, 98;

然后我在加速框架中使用了mmul函数..

vDSP_mmul(&matrixA[3][3], 1, &matrixB[3][1], 1, &results[3][1], 1, 3, 1, 3);

数组结果是通过执行以下操作创建的..

float results[3][1];

我只是把它放在一个空项目的 viewDidLoad 方法中,这样我就可以 NSLog 结果。因此,当我将 matrixA 乘以 matrixB 时,我应该得到以下结果.. (-1, 10, -3)。但是,NSLog 中的结果显示 (-0.045, 0.000, 0.000)。这是不正确的,我不明白为什么。我的理解是这个函数会将两个矩阵相乘。但我不确定它在做什么。我可能输入错误,希望有人能帮助我。

旁注:matrixA 实际上是另一个矩阵的逆矩阵。但是,我在加速框架中找不到任何相反的东西。我确实找到了一个名为

的函数
sgetrf_

使用 LAPACK 但并不真正了解它。如果有人有任何帮助、建议或要遵循的教程,我将不胜感激,现在已经在网上找了三天了!!

【问题讨论】:

【参考方案1】:

让我们通过三种不同的方式在 OS X 或 ios 上进行这种计算(包括逆向计算)。

首先,让我们开始您或多或少尝试做的事情:我们将使用 Accelerate 框架中的 LAPACK 和 BLAS 进行计算。请注意,我使用 BLAS 函数 cblas_sgemv 而不是 vDSP_mmul 来进行矩阵向量乘法。我这样做有三个原因。首先,它在使用 LAPACK 的代码中更为惯用。其次,LAPACK 确实想要以 column-major 顺序存储的矩阵,BLAS 支持,但 vDSP 不支持。最后,vDSP_mmul 实际上只是 BLAS 矩阵乘法例程的包装,所以我们可以去掉中间人。这将适用于回到 10.2 的 OS X 和回到 4.0 的 iOS——即在你今天可以合理预期遇到的任何目标上。

#include <Accelerate/Accelerate.h>
#include <stdio.h>
#include <string.h>

void using_blas_and_lapack(void) 
    printf("Using BLAS and LAPACK:\n");
    //  Note: you'll want to store A in *column-major* order to use it with
    //  LAPACK (even though it's not strictly necessary for this simple example,
    //  if you try to do something more complex you'll need it).
    float A[3][3] = -4,-3,-5, 6,-7, 9, 8,-2,-1;
    float x[3] =  -1, 10, -3;
    //  Compute b = Ax using cblas_sgemv.
    float b[3];
    cblas_sgemv(CblasColMajor, CblasNoTrans, 3, 3, 1.f, &A[0][0], 3, x, 1, 0.f, b, 1);
    printf("b := A x = [ %g, %g, %g ]\n", b[0], b[1], b[2]);
    //  You probably don't actually want to compute A^-1; instead you simply
    //  want to solve the equation Ay = b for y (like y = A\b in matlab).  To
    //  do this with LAPACK, you typically use the routines sgetrf and sgetrs.
    //  sgetrf will overwrite its input matrix with the LU factorization, so
    //  we'll make a copy first in case you need to use A again.
    float factored[3][3];
    memcpy(factored, A, sizeof factored);
    //  Now that we have our copy, go ahead and factor it using sgetrf.
    __CLPK_integer n = 3;
    __CLPK_integer info = 0;
    __CLPK_integer ipiv[3];
    sgetrf_(&n, &n, &factored[0][0], &n, ipiv, &info);
    if (info != 0)  printf("Something went wrong factoring A\n"); return; 
    //  Finally, use the factored matrix to solve y = A\b via sgetrs.  Just
    //  like sgetrf overwrites the matrix with its factored form, sgetrs
    //  overwrites the input vector with the solution, so we'll make a copy
    //  before calling the function.
    float y[3];
    memcpy(y, b, sizeof y);
    __CLPK_integer nrhs = 1;
    sgetrs_("No transpose", &n, &nrhs, &factored[0][0], &n, ipiv, y, &n, &info);
    printf("y := A\\b = [ %g, %g, %g ]\n\n", y[0], y[1], y[2]);

接下来,让我们使用 LinearAlgebra.h 进行计算,这是 iOS 8.0 和 OS X 10.10 中 Accelerate Framework 的新功能。它抽象出几乎所有这些计算所需的簿记,但它只提供了 LAPACK 和 BLAS 中可用的全部功能的一小部分。请注意,虽然将原始数组与 la_objects 相互转换需要一些样板文件,但一旦我们拥有它们,实际计算就非常简单。

#include <Accelerate/Accelerate.h>
#include <stdio.h>

void using_la(void) 
    printf("Using LA:\n");
    //  LA accepts row-major as well as column-major data, but requires a short
    //  two-step dance to get our data in.
    float Adata[3][3] = -4, 6, 8, -3,-7,-2, -5, 9,-1;
    float xdata[3] =  -1, 10, -3;
    la_object_t A = la_matrix_from_float_buffer(&Adata[0][0], 3, 3, 3, LA_NO_HINT, LA_DEFAULT_ATTRIBUTES);
    la_object_t x = la_vector_from_float_buffer(xdata, 3, 1, LA_DEFAULT_ATTRIBUTES);
    //  Once our data is stored as LA objects, it's easy to do the computation:
    la_object_t b = la_matrix_product(A, x);
    la_object_t y = la_solve(A, b);
    //  And finally we need to get our data back out:
    float bdata[3];
    if (la_vector_to_float_buffer(bdata, 1, b) != LA_SUCCESS) 
        printf("Something went wrong computing b.\n");
        return;
     else printf("b := A x = [ %g, %g, %g ]\n", bdata[0], bdata[1], bdata[2]);
    float ydata[3];
    if (la_vector_to_float_buffer(ydata, 1, y) != LA_SUCCESS) 
        printf("Something went wrong computing y.\n");
        return;
     else printf("y := A\\b = [ %g, %g, %g ]\n\n", ydata[0], ydata[1], ydata[2]);

最后,还有一种方法。如果你的矩阵真的只是 3x3,这就是我会使用的方法,因为前面任何一种方法所涉及的开销都会淹没实际的计算。但是,这只适用于大小为 4x4 或更小的矩阵。 iOS 8.0 和 OS X 10.10 中还有另一个新标头,专门针对小矩阵和矢量数学,这使得这真的简单而高效:

#include <simd/simd.h>
#include <stdio.h>

void using_simd(void) 
    printf("Using <simd/simd.h>:\n");
    matrix_float3x3 A = matrix_from_rows((vector_float3)-4,  6,  8,
                                         (vector_float3)-3, -7, -2,
                                         (vector_float3)-5,  9, -1);
    vector_float3 x =  -1, 10, -3 ;
    vector_float3 b = matrix_multiply(A, x);
    printf("b := A x = [ %g, %g, %g ]\n", b[0], b[1], b[2]);
    vector_float3 y = matrix_multiply(matrix_invert(A),b);
    printf("y := A^-1 b = [ %g, %g, %g ]\n\n", y[0], y[1], y[2]);

最后,让我们仔细检查一下,这些结果是否都相同(直到四舍五入的小差异):

scanon$ xcrun -sdk macosx clang matrix.m -framework Accelerate  && ./a.out
Using BLAS and LAPACK:
b := A x = [ 40, -61, 98 ]
y := A\b = [ -0.999999, 10, -3 ]

Using LA:
b := A x = [ 40, -61, 98 ]
y := A\b = [ -1, 10, -3 ]

Using <simd/simd.h>:
b := A x = [ 40, -61, 98 ]
y := A^-1 b = [ -1, 10, -3 ]

【讨论】:

哇!!非常感谢。顺便问一下,你是老师吗?这个解释是迄今为止我在研究过程中读到的最好的解释。我希望我能接受这两个答案。来自我的 +1! 我过去曾教过书,但在这种情况下,我只是非常非常了解相关库。【参考方案2】:

您在矩阵的末尾之后传递指向内存的指针。

修复如下所示(未经测试的代码):

vDSP_mmul(&matrixA[0][0], 1, &matrixB[0][0], 1, &results[0][0], 1, 3, 1, 3);

将数组传递给 C 中的函数实际上会将指针传递给数组的第一个元素,在这种情况下,这似乎是您需要做的。您在矩阵中的最后一个数组元素之后直接传递了一个指向一块内存的指针,这意味着您将得到无意义的结果或崩溃。

【讨论】:

感谢您的快速回复。但是,当我取出矩阵的 & 和大小,使我的行看起来像你的行时,我收到一个警告,指出不兼容的指针类型将浮点 [3][3](或 [3][1])传递给参数输入常量浮点数。这就是我这样写它们的原因,以摆脱那个警告。 要避开该警告,您需要传递&amp;matrix[0][0],而不是&amp;matrix[3][3]&amp;matrix[3][1]。这会给你一个指向矩阵开始的指针。 好吧,做到了!非常感谢您的时间。关于第二部分的任何想法,反转矩阵?谢谢。 无论如何我都不是 vDSP 专家,只是擅长发现指针数学错误。 再次感谢,我想我在 inverses 上发现了一些新东西,我会尝试一下。

以上是关于使用加速框架的矩阵乘法和逆问题的主要内容,如果未能解决你的问题,请参考以下文章

矩阵乘法和逆

P3 矩阵乘法和逆线性代数

矩阵乘法加速器的设计框架

矩阵乘法加速图上问题专题总结

在矩阵乘法中使用 C++2011 线程而不是 OpenMP 时出现异常加速

C++使用cuBLAS加速矩阵乘法运算