Search code examples
iosswiftaccelerate-frameworkaccelerate

How to use linear interpolation from Accelerate


I am trying to linearly interpolate new values from a data set using Apple's Accelerate framework. I've used this piece of documentation for implementing my code, but I am facing issues when I try to interpolate a set of values that are under 1, fractions.

let values: [CGFloat] = [0.0, 0.019124083, 0.035419375, 0.05232375, 0.06873629, 0.08550575, 0.10209821, 0.11870141, 0.1355003, 0.15223834, 0.16881292, 0.18565933, 0.20213126, 0.21881929, 0.2355565, 0.2522735, 0.26899675, 0.28572345, 0.30233976, 0.3187645, 0.33557975, 0.35221455]
let indices: [CGFloat] = [0.0, 0.00438118, 0.017873764, 0.04094696, 0.07394123, 0.11698151, 0.16987896, 0.23201275, 0.30223083, 0.37879562, 0.45942688, 0.54145336, 0.6220541, 0.6985626, 0.7687006, 0.8307409, 0.88353443, 0.9264679, 0.95935345, 0.98232174, 0.9957142, 1.0]

let numberOfElements = vDSP_Length(100)

var result = [Float](repeating: 0,
                count: Int(numberOfElements))

let stride = vDSP_Stride(1)

var base: Float = 0
var end = Float(values.count)
var control = [Float](repeating: 0,
                      count: Int(numberOfElements))

vDSP_vgenp(values, stride,
           indices, stride,
           &result, stride,
           numberOfElements,
           vDSP_Length(values.count))

print(result)

// [0.0, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455, 0.35221455]



Solution

  • As explained in the referenced document, the fractional parts of the indices define the interpolation between the pair of values in the values array starting at the index defined by the integer part.

    The precise algorithm is described as pseudo-code in vDSP_vgenp(_:_:_:_:_:_:_:_:):

    for (n = 0; n < N; ++n)
        If n <= B[0],  then C[n] = A[0].
        If B[M-1] < n, then C[n] = A[M-1].
        Otherwise:
            Let m be such that B[m] < n <= B[m+1].
            C[n] = A[m] + (A[m+1]-A[m]) * (n-B[m]) / (B[m+1]-B[m]).
    

    Here A is the values array, B the indices array, and C the result array.

    In your case all indices are <= 1, and that is why result[n] = values[21] = 0.35221455 for all n >= 1.

    If the intention is to interpolate the values over the interval [0, 100] then the indices should be in that range. So what you probably want is to multiply the indices (in the range 0..1) with the length of the result array:

    let numberOfElements = vDSP_Length(100)
    
    let indices: [Float] = [0.0, 0.00438118, ..., 0.9957142, 1.0]
        .map { $0 * Float(numberOfElements) }
    

    The result then is (Screenshot from Quicklook of result.map{ $0 } in a Playground):

    enter image description here