From 9a8742fe3de50e20af8b432e4a423f01a1793383 Mon Sep 17 00:00:00 2001 From: Gavin Wiggins <6828967+wigging@users.noreply.github.com> Date: Fri, 3 Jan 2025 22:36:47 -0500 Subject: [PATCH] Add mutating scale method to Vector and Matrix structs --- Sources/MatrixModule/Matrix.swift | 11 +++++++++++ Sources/MatrixModule/MatrixAlgebra.swift | 13 +++++++++++++ Sources/VectorModule/Vector.swift | 18 +++++++++++++++++- Sources/VectorModule/VectorAlgebra.swift | 13 +++++++++++++ Tests/MatrixTests.swift | 12 ++++++++++++ Tests/VectorTests.swift | 13 ++++++++++++- 6 files changed, 78 insertions(+), 2 deletions(-) diff --git a/Sources/MatrixModule/Matrix.swift b/Sources/MatrixModule/Matrix.swift index 264b789..afce290 100644 --- a/Sources/MatrixModule/Matrix.swift +++ b/Sources/MatrixModule/Matrix.swift @@ -202,6 +202,17 @@ extension Matrix where Scalar: MatrixAlgebra { Scalar.norm(self) } + /// Multiply each value in the matrix by a constant. + /// + /// For integer matrices, this performs element-wise multiplication. For + /// single and double precision matrices this uses BLAS routines `sscal` + /// and `dscal` respectively. + /// + /// - Parameter k: The scaling factor. + public mutating func scale(by k: Scalar) { + Scalar.scale(&self, by: k) + } + /// Transpose the matrix and return the result. /// - Returns: The transposed matrix. public func transpose() -> Matrix { diff --git a/Sources/MatrixModule/MatrixAlgebra.swift b/Sources/MatrixModule/MatrixAlgebra.swift index 866319e..6d6af2d 100644 --- a/Sources/MatrixModule/MatrixAlgebra.swift +++ b/Sources/MatrixModule/MatrixAlgebra.swift @@ -6,6 +6,7 @@ import Accelerate public protocol MatrixAlgebra { static func norm(_ a: Matrix) -> Self + static func scale(_ a: inout Matrix, by k: Self) static func transpose(_ a: Matrix) -> Matrix } @@ -22,6 +23,10 @@ extension Int: MatrixAlgebra { return Int(result) } + public static func scale(_ a: inout Matrix, by k: Int) { + a = a .* k + } + public static func transpose(_ a: Matrix) -> Matrix { var transposed = Matrix(rows: a.columns, columns: a.rows) for i in 0.., by k: Float) { + cblas_sscal(a.rows * a.columns, k, a.buffer.baseAddress, 1) + } + public static func transpose(_ a: Matrix) -> Matrix { let m = vDSP_Length(a.columns) let n = vDSP_Length(a.rows) @@ -54,6 +63,10 @@ extension Double: MatrixAlgebra { cblas_dnrm2(a.buffer.count, a.buffer.baseAddress, 1) } + public static func scale(_ a: inout Matrix, by k: Double) { + cblas_dscal(a.rows * a.columns, k, a.buffer.baseAddress, 1) + } + public static func transpose(_ a: Matrix) -> Matrix { let m = vDSP_Length(a.columns) let n = vDSP_Length(a.rows) diff --git a/Sources/VectorModule/Vector.swift b/Sources/VectorModule/Vector.swift index 14f74ad..af8f12a 100644 --- a/Sources/VectorModule/Vector.swift +++ b/Sources/VectorModule/Vector.swift @@ -150,6 +150,10 @@ extension Vector where Scalar: VectorArithmetic { public static func * (lhs: Vector, rhs: Vector) -> Vector { Scalar.multiply(lhs, rhs) } + + public static func *= (lhs: inout Vector, rhs: Scalar) { + lhs = lhs * rhs + } } extension Vector where Scalar: VectorAlgebra { @@ -172,12 +176,24 @@ extension Vector where Scalar: VectorAlgebra { return Scalar.dot(self, b) } - /// The Euclidean norm of the vector. Also known as the L² norm, 2-norm, vector magnitude, or Euclidean length. + /// The Euclidean norm of the vector. Also known as the L² norm, 2-norm, + /// vector magnitude, or Euclidean length. /// - Returns: The vector norm. public func norm() -> Scalar { Scalar.norm(self) } + /// Multiply each value in the vector by a constant. + /// + /// For integer vectors, this performs element-wise multiplication. For + /// single and double precision vectors this uses BLAS routines `sscal` + /// and `dscal` respectively. + /// + /// - Parameter k: The scaling factor. + public mutating func scale(by k: Scalar) { + Scalar.scale(&self, by: k) + } + /// Sum of the vector values. /// /// This example calculates the sum of the values in vector `a`. diff --git a/Sources/VectorModule/VectorAlgebra.swift b/Sources/VectorModule/VectorAlgebra.swift index 878a467..1ef4136 100644 --- a/Sources/VectorModule/VectorAlgebra.swift +++ b/Sources/VectorModule/VectorAlgebra.swift @@ -7,6 +7,7 @@ import Accelerate public protocol VectorAlgebra { static func dot(_ a: Vector, _ b: Vector) -> Self static func norm(_ a: Vector) -> Self + static func scale(_ a: inout Vector, by k: Self) static func sum(_ a: Vector) -> Self static func absoluteSum(_ a: Vector) -> Self static func cumulativeSum(_ a: Vector) -> Vector @@ -33,6 +34,10 @@ extension Int: VectorAlgebra { return Int(result) } + public static func scale(_ a: inout Vector, by k: Int) { + a *= k + } + public static func sum(_ a: Vector) -> Int { var res = Int.zero for i in 0.., by k: Float) { + cblas_sscal(a.size, k, a.buffer.baseAddress, 1) + } + public static func sum(_ a: Vector) -> Float { vDSP.sum(a.buffer) } @@ -100,6 +109,10 @@ extension Double: VectorAlgebra { cblas_dnrm2(a.size, a.buffer.baseAddress, 1) } + public static func scale(_ a: inout Vector, by k: Double) { + cblas_dscal(a.size, k, a.buffer.baseAddress, 1) + } + public static func sum(_ a: Vector) -> Double { vDSP.sum(a.buffer) } diff --git a/Tests/MatrixTests.swift b/Tests/MatrixTests.swift index 5fcefad..29b7165 100644 --- a/Tests/MatrixTests.swift +++ b/Tests/MatrixTests.swift @@ -191,6 +191,10 @@ struct MatrixTests { #expect(a.norm() == 16) #expect(a.transpose() == Matrix([[1, 4, 7], [2, 5, 8], [3, 6, 9]])) #expect(b.transpose() == Matrix([[2, 6], [3, 7], [4, 8], [5, 9]])) + + var c = Matrix([[1, 2, 3], [4, 5, 6]]) + c.scale(by: 3) + #expect(c == [[3, 6, 9], [12, 15, 18]]) } @Test func floatAlgebra() { @@ -200,6 +204,10 @@ struct MatrixTests { #expect(a.norm() == 16.881943) #expect(a.transpose() == Matrix([[1, 4, 7], [2, 5, 8], [3, 6, 9]])) #expect(b.transpose() == Matrix([[2, 6], [3, 7], [4, 8], [5, 9]])) + + var c = Matrix([[1, 2, 3], [4, 5, 6]]) + c.scale(by: 3.0) + #expect(c == [[3, 6, 9], [12, 15, 18.0]]) } @Test func doubleAlgebra() { @@ -209,5 +217,9 @@ struct MatrixTests { #expect(a.norm() == 16.881943016134134) #expect(a.transpose() == Matrix([[1, 4, 7], [2, 5, 8], [3, 6, 9.0]])) #expect(b.transpose() == Matrix([[2, 6], [3, 7], [4, 8], [5, 9.0]])) + + var c = Matrix([[1, 2, 3], [4, 5, 6.0]]) + c.scale(by: 3.0) + #expect(c == [[3, 6, 9], [12, 15, 18.0]]) } } diff --git a/Tests/VectorTests.swift b/Tests/VectorTests.swift index 9068aeb..fa9013c 100644 --- a/Tests/VectorTests.swift +++ b/Tests/VectorTests.swift @@ -141,12 +141,15 @@ struct VectorTests { @Test func integerAlgebra() { let a = Vector([1, 2, 3, 4, 5]) let b = Vector([4, 5, 6, 7, 8]) - #expect(a.dot(b) == 100) #expect(a.norm() == 7) #expect(a.sum() == 15) #expect(a.absoluteSum() == 15) #expect(a.cumulativeSum() == [1, 3, 6, 10, 15]) + + var c = Vector([8, 9, 10, 11]) + c.scale(by: 3) + #expect(c == [24, 27, 30, 33]) } @Test func floatAlgebra() { @@ -158,6 +161,10 @@ struct VectorTests { #expect(a.sum() == 15) #expect(a.absoluteSum() == 15) #expect(a.cumulativeSum() == [1, 3, 6, 10, 15]) + + var c = Vector([8, 9, 10, 11]) + c.scale(by: 3) + #expect(c == [24, 27, 30, 33]) } @Test func doubleAlgebra() { @@ -169,5 +176,9 @@ struct VectorTests { #expect(a.sum() == 15) #expect(a.absoluteSum() == 15) #expect(a.cumulativeSum() == [1, 3, 6, 10, 15]) + + var c = Vector([8, 9, 10, 11.0]) + c.scale(by: 3) + #expect(c == [24, 27, 30, 33]) } }