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 rangebuffer[0..<initializedCount]
must be initialized at the end of the closure’s execution, and the memory in the rangebuffer[initializedCount...]
must be uninitialized. This postcondition must hold even if theinitializer
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
})