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

Matrix multiplication and inverse problems with accelerate framework

我正在尝试将 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,但并不真正了解它。如果有人有任何帮助、建议或一些教程可以遵循,我将不胜感激,我已经做了三天,现在正在互联网上查找东西!!

您在矩阵结束 之后传递指向内存的指针。

修复看起来像这样(未经测试的代码):

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

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

让我们通过三种不同的方式在 OS X 或 iOS.

上进行此计算(包括逆运算)

首先,让我们开始您more-or-less 尝试做的事情:我们将使用 Accelerate 框架中的 LAPACK 和 BLAS 进行计算。请注意,我使用 BLAS 函数 cblas_sgemv 而不是 vDSP_mmul 来执行 matrix-vector 乘法。我这样做是出于三个原因。首先,它在使用 LAPACK 的代码中更加地道。其次,LAPACK 确实希望矩阵以 column-major 顺序存储,BLAS 支持,但 vDSP 不支持。最后,vDSP_mmul 实际上只是 BLAS matrix-multiply 例程的包装器,所以我们可以去掉中间人。这将在 OS X 回到 10.2 和 iOS 回到 4.0 时起作用——即在您今天可以合理预期遇到的任何目标上。

#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 进行计算,这是 Accelerate Framework 在 iOS 8.0 和 OS X 10.10 中的新功能。它抽象出几乎所有这些计算所需的簿记,但它只提供了 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 中还有另一个新的 header,专门针对小矩阵和矢量数学,这使得 真正 简单高效:

#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]);
}

最后,让我们 double-check 这些都给出相同的结果(除了小的舍入差异):

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 ]