Home > Software design >  How to integrate in Swift using vDSP
How to integrate in Swift using vDSP

Time:11-15

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))

CodePudding user response:

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
})
  • Related