Create a Vector structure from Range and ClosedRange

I have a Vector structure as defined below:

import Accelerate

struct Vector<T> {

    private let data: DataBuffer<T>

    var buffer: UnsafeMutableBufferPointer<T> {
        get { self.data.buffer }
        set { self.data.buffer = newValue }
    }

    var length: Int {
        self.buffer.count
    }

    init(_ values: [T]) {
        self.data = DataBuffer(array: values)
    }

    subscript(item: Int) -> T {
        get { return self.buffer[item] }
        set { self.buffer[item] = newValue }
    }

    static func range(_ r: Range<Int>) -> Vector where T == Float {
        let c = r.upperBound - r.lowerBound
        let v = vDSP.ramp(withInitialValue: Float(r.lowerBound), increment: 1.0, count: c)
        let vec = Vector(v)
        return vec
    }

    static func range(_ r: Range<Int>) -> Vector where T == Double {
        let c = r.upperBound - r.lowerBound
        let v = vDSP.ramp(withInitialValue: Double(r.lowerBound), increment: 1.0, count: c)
        let vec = Vector(v)
        return vec
    }
}

The underlying data for a Vector is handled with a mutable buffer:

class DataBuffer<T> {
    var buffer: UnsafeMutableBufferPointer<T>
    
    init(count: Int) {
        let start = UnsafeMutablePointer<T>.allocate(capacity: count)
        self.buffer = UnsafeMutableBufferPointer(start: start, count: count)
    }

    init(array: [T]) {
        let count = array.count
        let start = UnsafeMutablePointer<T>.allocate(capacity: count)
        self.buffer = UnsafeMutableBufferPointer(start: start, count: count)
        _ = self.buffer.initialize(fromContentsOf: array)
    }

    deinit {
        self.buffer.deinitialize()
        self.buffer.deallocate()
    }
}

This allows me to create a Vector from an open-ended range as shown here:

let vec = Vector<Float>.range(0..<10)

for i in 0..<vec.length {
    print(vec[i], terminator: "  ")
}
print()

// This prints the following:
// 0.0  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0

However, I would like to initialize a Vector from a range as shown below. I want to support Int, Float, and Double for the Vector values. Any ideas on how I could accomplish this?

// I would like to initialize a vector from a Range
let vec = Vector<Float>(0..<10)

// I also want to initialize a vector from a ClosedRange
let vec = Vector<Float>(2...11)

Something like this might work, adapting your existing array-based initializer:

extension Vector {
  init(_ range: some RangeExpression<T> & Collection<T>) {
    self.data = DataBuffer(array: Array(range))
  }
}

But we can improve this further and allow arbitrary collections of the correct type in DataBuffer and in Vector:

extension Vector {
  init(fromContentsOf collection: some Collection<T>) {
    self.data = DataBuffer(fromContentsOf: collection)
  }
}

extension DataBuffer {
  init(fromContentsOf collection: some Collection<T>) {
    let count = collection.count
    let start = UnsafeMutablePointer<T>.allocate(capacity: count)
    self.buffer = UnsafeMutableBufferPointer(start: start, count: count)
    _ = self.buffer.initialize(fromContentsOf: collection)
  }
}

And now your Vector type can be initialized from any type that conforms to Collection and has the appropriate Element type.

1 Like

Small improvement: UnsafeMutableBufferPointer has allocate(capacity:) so you can skip a step and not accidentally use the unbounded pointer.

(I assume the reason to use a custom type and not Array is so you can have a stable buffer address between statements? If you don’t need that, you might as well just use Array.)

I was trying to preserve the structure as much as possible to make the critical bits clear. :slight_smile:

1 Like

What about this i like to limit my types by some protocol if you also want to implement the vDSP functions like that you can just extend it.

You can ofc. clean this code and probably make this range functions cleaner but i like to be verbose about all possibilities in my code.

import Accelerate

protocol VectorType {
    static func range(_ r: Range<Int>) -> [Self]
    static func range(_ r: ClosedRange<Int>) -> [Self]
}

extension Float: VectorType {
    static func range(_ r: Range<Int>) -> [Float] {
        let count = r.upperBound - r.lowerBound
        var result = [Float](repeating: 0, count: count)
        var (low, high) = (Float(r.lowerBound), Float(1.0))
        vDSP_vramp(&low, &high, &result, 1, vDSP_Length(count))
        return result
    }

    static func range(_ r: ClosedRange<Int>) -> [Float] {
        let count = r.upperBound - r.lowerBound + 1
        var result = [Float](repeating: 0, count: count)
        var (low, high) = (Float(r.lowerBound), Float(1.0))
        vDSP_vramp(&low, &high, &result, 1, vDSP_Length(count))
        return result
    }
}

extension Double: VectorType {
    static func range(_ r: Range<Int>) -> [Double] {
        let count = r.upperBound - r.lowerBound
        var (low, high) = (Double(r.lowerBound), Double(1.0))
        var result = [Double](repeating: 0, count: count)
        vDSP_vrampD(&low, &high, &result, 1, vDSP_Length(count))
        return result
    }

    static func range(_ r: ClosedRange<Int>) -> [Double] {
        let count = r.upperBound - r.lowerBound + 1
        var (low, high) = (Double(r.lowerBound), Double(1.0))
        var result = [Double](repeating: 0, count: count)
        vDSP_vrampD(&low, &high, &result, 1, vDSP_Length(count))
        return result
    }
}

extension Int: VectorType {
    static func range(_ r: Range<Int>) -> [Int] {
        return Array(r)
    }

    static func range(_ r: ClosedRange<Int>) -> [Int] {
        return Array(r)
    }
}

struct Vector<T: VectorType> {
    private let data: DataBuffer<T>

    var buffer: UnsafeMutableBufferPointer<T> {
        get { self.data.buffer }
        set { self.data.buffer = newValue }
    }

    var length: Int {
        self.buffer.count
    }

    init(_ values: [T]) {
        self.data = DataBuffer(array: values)
    }

    subscript(item: Int) -> T {
        get { return self.buffer[item] }
        set { self.buffer[item] = newValue }
    }

    static func range(_ r: Range<Int>) -> Vector<T> {
        let values = T.range(r)
        return Vector(values)
    }
    
    static func range(_ r: ClosedRange<Int>) -> Vector<T> {
        let values = T.range(r)
        return Vector(values)
    }
}

class DataBuffer<T: VectorType> {
    var buffer: UnsafeMutableBufferPointer<T>
    
    init(array: [T]) {
        let count = array.count
        let start = UnsafeMutablePointer<T>.allocate(capacity: count)
        self.buffer = UnsafeMutableBufferPointer(start: start, count: count)
        _ = self.buffer.initialize(fromContentsOf: array)
    }

    deinit {
        self.buffer.deinitialize()
        self.buffer.deallocate()
    }
}

//let vec = Vector<Float>.range(0..<10)
//let vec = Vector<Double>.range(0..<10)
//let vec = Vector<Int>.range(0..<10)
let vec = Vector<Int>.range(5...10)

for i in 0..<vec.length {
    print(vec[i], terminator: "  ")
}

Your suggestion works for Int values.

let vec = Vector<Int>(fromContentsOf: 0..<10)

let vec = Vector(fromContentsOf: 0..<10)

// vec is
// 0  1  2  3  4  5  6  7  8  9  

But Float and Double do not conform to SignedInteger so the build fails.

let vec = Vector<Float>(fromContentsOf: 0..<10)

let vec = Vector<Double>(fromContentsOf: 0..<10)

You can use vDSP as you do in your initial example to add support for floating-point ranges in an additional init() overload that takes a some RangeExpression<some FloatingPoint>. :upside_down_face:

I couldn't get RangeExpression and FloatingPoint to work. But I can use stride to create Int, Float, and Double vectors from a range.

init(_ r: Range<Int>) where T == Int {
    self.data = DataBuffer(array: Array(r))
}

init(_ r: Range<Int>) where T == Double {
    let s = Double(r.lowerBound)
    let e = Double(r.upperBound)
    let arr = Array(stride(from: s, to: e, by: 1.0))
    self.data = DataBuffer(array: arr)
}

init(_ r: Range<Int>) where T == Float {
    let s = Float(r.lowerBound)
    let e = Float(r.upperBound)
    let arr = Array(stride(from: s, to: e, by: 1.0))
    self.data = DataBuffer(array: arr)
}

This allows the following:

let vec1 = Vector<Int>(0..<10)
let vec2 = Vector<Float>(0..<10)
let vec3 = Vector<Double>(0..<10)

Thank you for the suggestion. Using allocate(capacity:), I can do the following for the data buffer class:

class DataBuffer<T> {
    var buffer: UnsafeMutableBufferPointer<T>
    
    init(count: Int) {
        self.buffer = UnsafeMutableBufferPointer<T>.allocate(capacity: count)
    }

    init(array: [T]) {
        self.buffer = UnsafeMutableBufferPointer<T>.allocate(capacity: array.count)
        _ = self.buffer.initialize(fromContentsOf: array)
    }

    deinit {
        self.buffer.deinitialize()
        self.buffer.deallocate()
    }
}