Vector multiplication on a 2D array using Accelerate in Swift

197 Views Asked by At

I have the following NumPy example which does element-wise vector multiplication on a 2D array:

import numpy as np

a = np.array([[3, 5, 8, 7, 2],
              [1, 4, 9, 1, 3],
              [2, 7, 3, 1, 2],
              [5, 4, 9, 1, 6],
              [4, 2, 9, 6, 7]])

b = np.array([[2],
              [0.5],
              [9],
              [1],
              [18]])

c = a * b

print(c)

This prints the following:

[[  6.   10.   16.   14.    4. ]
 [  0.5   2.    4.5   0.5   1.5]
 [ 18.   63.   27.    9.   18. ]
 [  5.    4.    9.    1.    6. ]
 [ 72.   36.  162.  108.  126. ]]

I'm trying to do this calculation in Swift using Accelerate as shown below:

import Foundation
import Accelerate

let a: [Float] = [3, 5, 8, 7, 2,
                  1, 4, 9, 1, 3,
                  2, 7, 3, 1, 2,
                  5, 4, 9, 1, 6,
                  4, 2, 9, 6, 7]

let b: [Float] = [2,
                  0.5,
                  9,
                  1,
                  18]

var c: [Float] = Array(repeating: 0, count: 25)
vDSP_vmul(a, 1, b, 1, &c, 1, 25)

print(c)

The Swift code prints the following which is not correct:

[6.0, 2.5, 72.0, 7.0, 36.0, 0.0, 0.0, 0.0, 1.104904e-39, 4.7331654e-30, 3.7740115e-24, 6.737052e-37, 1.806498e-35, 1.19422e-39, 5.1722454e-26, 5.9787867e-25, 2.7286e-41, 0.0, 0.0, 0.0, 1.6543612e-24, 0.0, 0.0, 5.533857e-39, 3.73e-43]

Accelerate works with flatten arrays whereas NumPy supports 2D arrays. So I'm not sure how to multiply the 2D array with a vector in Accelerate. Is there a different Accelerate function that I should use for this type of multiplication?

1

There are 1 best solutions below

4
Rob Napier On

To multiply this as a column, you need to use a stride. If you're taking the trouble to use Accelerate, then you probably also want to avoid extra allocations. You can use Array(unsafeUninitializedCapacity:) to fill in all the data like this:

let c = Array(unsafeUninitializedCapacity: a.count) { output, initializedCount in
    a.withUnsafeBufferPointer { abuffer in
        for i in b.indices {
            vDSP_vmul(abuffer.baseAddress! + i, b.count, // stride through a by the column size
                      b, 1,                              // stride one-by-one through b
                      output.baseAddress! + i, b.count,  // stride through the output by the column size
                      vDSP_Length(b.count))
        }
        initializedCount = abuffer.count
    }
}

// [6.0, 10.0, 16.0, 14.0, 4.0, 0.5, 2.0, 4.5, 0.5, 1.5, 18.0, 63.0, 27.0, 9.0, 18.0, 5.0, 4.0, 9.0, 1.0, 6.0, 72.0, 36.0, 162.0, 108.0, 126.0]