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

时间:2015-03-20 22:23:29

标签: ios objective-c matrix 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但不是真的得到它。如果有人有任何帮助,建议或一些教程可以遵循我会很感激,已经在这三天了,现在在网上看东西!!

2 个答案:

答案 0 :(得分:23)

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

首先,让我们做你或多或少尝试做的事情:我们将使用Accelerate框架中的LAPACK和BLAS进行计算。请注意,我使用BLAS函数cblas_sgemv代替vDSP_mmul来执行矩阵向量乘法。我之所以这样做有三个原因。首先,它在使用LAPACK的代码中更具惯用性。其次,LAPACK确实希望以 column-major 顺序存储矩阵,而BLAS支持这些矩阵,但vDSP不支持。最后,vDSP_mmul实际上只是BLAS矩阵乘法例程的包装器,因此我们可以删除中间人。这将在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进行计算,这是iOS 8.0和OS X 10.10中Accelerate Framework的一项新功能。它抽象了几乎所有这些计算所需的簿记,但它只提供了LAPACK和BLAS中可用的全部功能的一小部分。请注意,虽然有一些样板需要将原始数组转换为la_objects和从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 :(得分:6)

在矩阵结束之后,您正在将指针传递给内存

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

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

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