Using Accelerate to operate on ArraySlice<Complex>

for var stack:[Double]

for i in 1...N {
	stack[sp+i] += stack[sp+b+i]
	}

conveniently becomes

vDSP.add(stack[sp+1 ... sp+N], stack[sp+b+1 ... sp+b+N], &stack[sp+1 ... sp+N])

since ArraySlice<Double> conforms to AccelerateBuffer.

What would be the equivalent Accelerate magic for var stack: [Complex] (where Complex is defined the usual way).

Bonus points if it is simple to use in a generic. i.e.

func evaluate(T:Computable) { // Double and Complex conform to Computable  
      var stack:[T]
      etc...

vDSP_zvaddD looks promising, but [Complex] interleaves real and imaginary parts, and to use vDSP_zvaddD would seem to require getting an UnsafePointer<DSPDoubleSplitComplex> from ArraySlice<Double>.

(I will be needing all the usual arithmetic and transcendental operations on double precision Double and Complex, and mixed vector-scalar operations, as well....)

2 Likes

My initial thought here is to provide a split complex buffer type that conforms to Collection with Element == Complex, but also has contiguous real and imaginary parts that can be used to make nice wrappers for the vDSP interfaces. This is something that I've been meaning to design out and add, but simply haven't gotten around to yet.

5 Likes

Extremely sketchy draft of what I'm talking about here:

Note that the actual implementation here is not at all what I would end up doing eventually, but it at least outlines what such a project would look like and how it could interoperate with Accelerate.

3 Likes

There's enough here that I don't understand that I am not sure where to begin my questions.

I'm trying to implement the equivalent of

func vvMultiply(stack: inout [Complex], sp: Int, n: Int) {
    for i in 1...n { stack[sp+i] *= stack[sp+n+1+i]  }
}

using vDSP_zvmulD

  1. Is vDSP_zvmulD the appropriate API?
  2. How do I approach that?
1 Like

Current attempt (seems to work....)

func vSquare(_ stack: inout [Complex], sp: Int, n: Int) {
    stack.withUnsafeMutablePointerToDouble {
        let p = $0 + 2*sp + 2
        var dsc = DSPDoubleSplitComplex(realp: p, imagp: p + 1)
        vDSP_zvmulD(&dsc, 2, &dsc, 2, &dsc, 2, UInt(n), +1)
    }
}

extension Array where Element == Complex {
    func withUnsafeMutablePointerToDouble(_ body: (UnsafeMutablePointer<Double>) -> ()) {
        self.withUnsafeBufferPointer {
            let complexBufferBaseAddress = $0.baseAddress!
            let numDoubles = $0.count * 2
            complexBufferBaseAddress.withMemoryRebound(to: Double.self, capacity: numDoubles) { doubleBufferBaseAddress in
                let doubleBuffer = UnsafeBufferPointer(start: doubleBufferBaseAddress, count: numDoubles)
                body(UnsafeMutablePointer<Double>(mutating: doubleBuffer.baseAddress!))
            }
        }
    }
}
1 Like

Finally getting around to implementing the vector-scalar ops, and again, [Double] conformance to AccelerateBuffer makes the Double version simple, but my attempt for the Complex version seems overly verbose. Any suggestions for simplifying:

static func vsMul(_ scalar: Complex, _ stack: inout [Complex], sp: Int, spResult: Int, n: Int) {
    stack.withUnsafeMutablePointerToDouble { p in
        var re = scalar.re, im = scalar.im
        withUnsafeMutablePointer(to: &re) { pRe in
            withUnsafeMutablePointer(to: &im) { pIm in
                let p1 = p + 2*sp + 2
                let p2 = p + 2*spResult + 2
                var dsc1 = DSPDoubleSplitComplex(realp: p1, imagp: p1 + 1)
                var dsc2 = DSPDoubleSplitComplex(realp: p2, imagp: p2 + 1)
                var dscScalar = DSPDoubleSplitComplex(realp: pRe, imagp: pIm)
                vDSP_zvzsmlD(&dsc1, 2, &dscScalar, &dsc2, 2, UInt(n))
            }
        }
    }
}

which seems to work to do:

for i in 1...n { stack[spResult + i] = stack[sp + i] * scalar }
2 Likes