I have a generic matrix type that conforms to the FloatingPoint
protocol as shown below.
import Accelerate
struct Matrix<T: FloatingPoint> {
let rows: Int
let columns: Int
var values: [T]
init(_ content: [[T]]) {
self.rows = content.count
self.columns = content[0].count
self.values = content.flatMap { $0 }
}
init(rows: Int, columns: Int, values: [T]) {
self.rows = rows
self.columns = columns
self.values = values
}
subscript(row: Int, column: Int) -> T {
get { return values[(row * columns) + column] }
set { values[(row * columns) + column] = newValue }
}
}
The matrix supports various mathematical operations for single and double precision. Below is the matrix multiplication operation.
extension Matrix {
static func * (lhs: Matrix, rhs: Matrix) -> Matrix where T == Double {
precondition(lhs.columns == rhs.rows, "Number of columns in left matrix must equal number of rows in right matrix")
let a = lhs.values
let b = rhs.values
var c = [Double](repeating: 0.0, count: lhs.rows * rhs.columns)
let m = lhs.rows // rows in matrices A and C
let n = rhs.columns // columns in matrices B and C
let k = lhs.columns // columns in matrix A and rows in matrix B
let alpha = 1.0
let beta = 0.0
// matrix multiplication where C ← αAB + βC
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, m, n, k, alpha, &a, k, &b, n, beta, &c, n)
let mat = Matrix(rows: lhs.rows, columns: rhs.columns, values: c)
return mat
}
static func * (lhs: Matrix, rhs: Matrix) -> Matrix where T == Float {
precondition(lhs.columns == rhs.rows, "Number of columns in left matrix must equal number of rows in right matrix")
let a = lhs.values
let b = rhs.values
var c = [Float](repeating: 0.0, count: lhs.rows * rhs.columns)
let m = lhs.rows // rows in matrices A and C
let n = rhs.columns // columns in matrices B and C
let k = lhs.columns // columns in matrix A and rows in matrix B
let alpha = Float(1.0)
let beta = Float(0.0)
// matrix multiplication where C ← αAB + βC
cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, m, n, k, alpha, a, k, b, n, beta, &c, n)
let mat = Matrix(rows: lhs.rows, columns: rhs.columns, values: c)
return mat
}
}
I can create matrices with integer, float, and double values. See below for examples that use integer and double values.
let a = Matrix([[1, 2],
[3, 4]])
let b = Matrix([[1, 2],
[3, 4]])
let c = a * b
let a = Matrix([[1.0, 2.0],
[3.0, 4.0]])
let b = Matrix([[1.0, 2.0],
[3.0, 4.0]])
let c = a * b
I would like to add support for single and double precision complex numbers which I attempted to do below:
struct Complex<T> {
let real: T
let imag: T
}
extension Matrix {
static func * (lhs: Matrix, rhs: Matrix) -> Matrix where T == Complex<Double> {
precondition(lhs.columns == rhs.rows, "Number of columns in left matrix must equal number of rows in right matrix")
let a = lhs.values
let b = rhs.values
var c = [Complex](repeating: Complex(real: 0.0, imag: 0.0), count: lhs.rows * rhs.columns)
let m = lhs.rows
let n = rhs.columns
let k = lhs.columns
let alpha = [Complex(real: 1.0, imag: 0.0)]
let beta = [Complex(real: 1.0, imag: 0.0)]
cblas_zgemm_wrapper(m, n, k, alpha, a, k, b, n, beta, &c, n)
let mat = Matrix(rows: lhs.rows, columns: rhs.columns, values: c)
return mat
}
}
This doesn't work because obviously Complex
doesn't conform to FloatingPoint
. I could just use struct Matrix<T> { ... }
but then I lose all the benefits of conforming to FloatingPoint
. Is there a way to extend the FloatingPoint
protocol with a complex number type? Or is there a different approach I should use for this?