// Copyright (c) 2014–2015 Mattt Thompson (http://mattt.me) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import Accelerate public enum MatrixAxies { case row case column } public struct Matrix where Scalar: FloatingPoint, Scalar: ExpressibleByFloatLiteral { public let rows: Int public let columns: Int var grid: [Scalar] public init(rows: Int, columns: Int, repeatedValue: Scalar) { self.rows = rows self.columns = columns self.grid = [Scalar](repeating: repeatedValue, count: rows * columns) } public init(_ contents: [[Scalar]]) { self.init(rows: contents.count, columns: contents[0].count, repeatedValue: 0.0) for (i, row) in contents.enumerated() { precondition(row.count == columns, "All rows should have the same number of columns") grid.replaceSubrange(i*columns ..< (i + 1)*columns, with: row) } } public subscript(row: Int, column: Int) -> Scalar { get { assert(indexIsValidForRow(row, column: column)) return grid[(row * columns) + column] } set { assert(indexIsValidForRow(row, column: column)) grid[(row * columns) + column] = newValue } } public subscript(row row: Int) -> [Scalar] { get { assert(row < rows) let startIndex = row * columns let endIndex = row * columns + columns return Array(grid[startIndex.. [Scalar] { get { var result = [Scalar](repeating: 0.0, count: rows) for i in 0.. Bool { return row >= 0 && row < rows && column >= 0 && column < columns } } // MARK: - Printable extension Matrix: CustomStringConvertible { public var description: String { var description = "" for i in 0.. AnyIterator> { let endIndex = rows * columns var nextRowStartIndex = 0 return AnyIterator { if nextRowStartIndex == endIndex { return nil } let currentRowStartIndex = nextRowStartIndex nextRowStartIndex += self.columns return self.grid[currentRowStartIndex.. (lhs: Matrix, rhs: Matrix) -> Bool { return lhs.rows == rhs.rows && lhs.columns == rhs.columns && lhs.grid == rhs.grid } // MARK: - public func add(_ x: Matrix, y: Matrix) -> Matrix { precondition(x.rows == y.rows && x.columns == y.columns, "Matrix dimensions not compatible with addition") var results = y cblas_saxpy(Int32(x.grid.count), 1.0, x.grid, 1, &(results.grid), 1) return results } public func add(_ x: Matrix, y: Matrix) -> Matrix { precondition(x.rows == y.rows && x.columns == y.columns, "Matrix dimensions not compatible with addition") var results = y cblas_daxpy(Int32(x.grid.count), 1.0, x.grid, 1, &(results.grid), 1) return results } public func mul(_ alpha: Float, x: Matrix) -> Matrix { var results = x cblas_sscal(Int32(x.grid.count), alpha, &(results.grid), 1) return results } public func mul(_ alpha: Double, x: Matrix) -> Matrix { var results = x cblas_dscal(Int32(x.grid.count), alpha, &(results.grid), 1) return results } public func mul(_ x: Matrix, y: Matrix) -> Matrix { precondition(x.columns == y.rows, "Matrix dimensions not compatible with multiplication") var results = Matrix(rows: x.rows, columns: y.columns, repeatedValue: 0.0) cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, Int32(x.rows), Int32(y.columns), Int32(x.columns), 1.0, x.grid, Int32(x.columns), y.grid, Int32(y.columns), 0.0, &(results.grid), Int32(results.columns)) return results } public func mul(_ x: Matrix, y: Matrix) -> Matrix { precondition(x.columns == y.rows, "Matrix dimensions not compatible with multiplication") var results = Matrix(rows: x.rows, columns: y.columns, repeatedValue: 0.0) cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, Int32(x.rows), Int32(y.columns), Int32(x.columns), 1.0, x.grid, Int32(x.columns), y.grid, Int32(y.columns), 0.0, &(results.grid), Int32(results.columns)) return results } public func elmul(_ x: Matrix, y: Matrix) -> Matrix { precondition(x.rows == y.rows && x.columns == y.columns, "Matrix must have the same dimensions") var result = Matrix(rows: x.rows, columns: x.columns, repeatedValue: 0.0) result.grid = x.grid * y.grid return result } public func elmul(_ x: Matrix, y: Matrix) -> Matrix { precondition(x.rows == y.rows && x.columns == y.columns, "Matrix must have the same dimensions") var result = Matrix(rows: x.rows, columns: x.columns, repeatedValue: 0.0) result.grid = x.grid * y.grid return result } public func div(_ x: Matrix, y: Matrix) -> Matrix { let yInv = inv(y) precondition(x.columns == yInv.rows, "Matrix dimensions not compatible") return mul(x, y: yInv) } public func div(_ x: Matrix, y: Matrix) -> Matrix { let yInv = inv(y) precondition(x.columns == yInv.rows, "Matrix dimensions not compatible") return mul(x, y: yInv) } public func pow(_ x: Matrix, _ y: Double) -> Matrix { var result = Matrix(rows: x.rows, columns: x.columns, repeatedValue: 0.0) result.grid = pow(x.grid, y) return result } public func pow(_ x: Matrix, _ y: Float) -> Matrix { var result = Matrix(rows: x.rows, columns: x.columns, repeatedValue: 0.0) result.grid = pow(x.grid, y) return result } public func exp(_ x: Matrix) -> Matrix { var result = Matrix(rows: x.rows, columns: x.columns, repeatedValue: 0.0) result.grid = exp(x.grid) return result } public func exp(_ x: Matrix) -> Matrix { var result = Matrix(rows: x.rows, columns: x.columns, repeatedValue: 0.0) result.grid = exp(x.grid) return result } public func sum(_ x: Matrix, axies: MatrixAxies = .column) -> Matrix { switch axies { case .column: var result = Matrix(rows: 1, columns: x.columns, repeatedValue: 0.0) for i in 0..(rows: x.rows, columns: 1, repeatedValue: 0.0) for i in 0..) -> Matrix { precondition(x.rows == x.columns, "Matrix must be square") var results = x var ipiv = [__CLPK_integer](repeating: 0, count: x.rows * x.rows) var lwork = __CLPK_integer(x.columns * x.columns) var work = [CFloat](repeating: 0.0, count: Int(lwork)) var error: __CLPK_integer = 0 var mc = __CLPK_integer(x.columns) var nc = __CLPK_integer(x.rows) var lda = __CLPK_integer(x.columns) sgetrf_(&mc, &nc, &(results.grid), &lda, &ipiv, &error) sgetri_(&nc, &(results.grid), &lda, &ipiv, &work, &lwork, &error) assert(error == 0, "Matrix not invertible") return results } public func inv(_ x : Matrix) -> Matrix { precondition(x.rows == x.columns, "Matrix must be square") var results = x var ipiv = [__CLPK_integer](repeating: 0, count: x.rows * x.rows) var lwork = __CLPK_integer(x.columns * x.columns) var work = [CDouble](repeating: 0.0, count: Int(lwork)) var error: __CLPK_integer = 0 var mc = __CLPK_integer(x.columns) var nc = __CLPK_integer(x.rows) var lda = __CLPK_integer(x.columns) dgetrf_(&mc, &nc, &(results.grid), &lda, &ipiv, &error) dgetri_(&nc, &(results.grid), &lda, &ipiv, &work, &lwork, &error) assert(error == 0, "Matrix not invertible") return results } public func transpose(_ x: Matrix) -> Matrix { var results = Matrix(rows: x.columns, columns: x.rows, repeatedValue: 0.0) vDSP_mtrans(x.grid, 1, &(results.grid), 1, vDSP_Length(results.rows), vDSP_Length(results.columns)) return results } public func transpose(_ x: Matrix) -> Matrix { var results = Matrix(rows: x.columns, columns: x.rows, repeatedValue: 0.0) vDSP_mtransD(x.grid, 1, &(results.grid), 1, vDSP_Length(results.rows), vDSP_Length(results.columns)) return results } // MARK: - Operators public func + (lhs: Matrix, rhs: Matrix) -> Matrix { return add(lhs, y: rhs) } public func + (lhs: Matrix, rhs: Matrix) -> Matrix { return add(lhs, y: rhs) } public func * (lhs: Float, rhs: Matrix) -> Matrix { return mul(lhs, x: rhs) } public func * (lhs: Double, rhs: Matrix) -> Matrix { return mul(lhs, x: rhs) } public func * (lhs: Matrix, rhs: Matrix) -> Matrix { return mul(lhs, y: rhs) } public func * (lhs: Matrix, rhs: Matrix) -> Matrix { return mul(lhs, y: rhs) } public func / (lhs: Matrix, rhs: Matrix) -> Matrix { return div(lhs, y: rhs) } public func / (lhs: Matrix, rhs: Matrix) -> Matrix { return div(lhs, y: rhs) } public func / (lhs: Matrix, rhs: Double) -> Matrix { var result = Matrix(rows: lhs.rows, columns: lhs.columns, repeatedValue: 0.0) result.grid = lhs.grid / rhs; return result; } public func / (lhs: Matrix, rhs: Float) -> Matrix { var result = Matrix(rows: lhs.rows, columns: lhs.columns, repeatedValue: 0.0) result.grid = lhs.grid / rhs; return result; } postfix operator ′ public postfix func ′ (value: Matrix) -> Matrix { return transpose(value) } public postfix func ′ (value: Matrix) -> Matrix { return transpose(value) }