Search code examples
iosswiftaccelerate-framework

How to integrate in Swift using vDSP


I try to find replacement for SciPy's cumtrapz function function in Swift. I found something called vDSP_vtrapzD but I have no idea how to use it. This is what I've done so far:

import Accelerate
var f1: [Double] = [<some data>]
var tdata: [Double] = [<time vector>]
var output = [Double](unsafeUninitializedCapacity:Int(f1.count), initializingWith: {_, _ in})

vDSP_vtrapzD(&f1, 1, &tdata, &output, 1, vDSP_Length(f1.count))

Solution

  • You're close, but you're using Array.init(unsafeUninitializedCapacity:initializingWith:) incorrectly. From its documentation:

    Discussion

    Inside the closure, set the initializedCount parameter to the number of elements that are initialized by the closure. The memory in the range buffer[0..<initializedCount] must be initialized at the end of the closure’s execution, and the memory in the range buffer[initializedCount...] must be uninitialized. This postcondition must hold even if the initializer closure throws an error.

    This API is a more unsafe (but performant counterpart) to Array.init(repeating:count:), which allocates an array of a fixed size, and spends the time to initialize all its contents). This has two potential drawbacks:

    • If the purpose of the array is to provide a buffer to write a result into, then initializing it prior to that is redundant and wasteful

    • If the result you put into that buffer ends up being larger than your array, you need to remember to manually "trim" the excess off by copying it into a new array.

    Array.init(unsafeUninitializedCapacity:initializingWith:) improves upon this by:

    • Asking you for the maximum capacity you might possibly need
    • Giving you a temporary buffer with the capacity
      • Importantly, it's uninitialized. This makes it faster, but also more dangerous (risk of buffer underflow errors) if used incorrectly.
      • You then tell it exactly how much of that temporary buffer you actually used
      • It will automatically copy that much of the buffer into the final array, and return that as the result.

    You're using Array.init(unsafeUninitializedCapacity:initializingWith:) as if it were Array.init(repeating:count:). To use it correctly, you would put your initialization logic inside the initializer parameter, like so:

    let result = Array<Double>(unsafeUninitializedCapacity: f1.count, initializingWith: { resultBuffer, count in
        assert(f1.count == tdata.count)
        
        vDSP_vtrapzD(
            &f1,                       // Double-precision real input vector.
            1,                         // Address stride for A.
            &tdata,                    // Pointer to double-precision real input scalar: step size.
            resultBuffer.baseAddress!, // Double-precision real output vector.
            1,                         // Address stride for C.
            vDSP_Length(f1.count)      // The number of elements to process.,
        )
        
        count = f1.count // This tells Swift how many elements of the buffer to copy into the resultant Array
    })