From 512f0f44578d0d983986a7718e42e0a376da2b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:32:54 +0100 Subject: [PATCH 01/26] Do not use number formatter for wasm --- Sources/Matft/core/general/print.swift | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Sources/Matft/core/general/print.swift b/Sources/Matft/core/general/print.swift index 1380bb9..adfc5fe 100644 --- a/Sources/Matft/core/general/print.swift +++ b/Sources/Matft/core/general/print.swift @@ -24,9 +24,22 @@ extension MfArray: CustomStringConvertible{ var shape = self.shape var strides = self.strides + #if os(WASI) + let formatter: NumberFormatter? = nil + #else let formatter = NumberFormatter() formatter.positivePrefix = formatter.plusSign formatter.maximumFractionDigits = self.storedType == .Float ? 7 : 14 + #endif + + func imagString(_ value: Any) -> String{ + #if os(WASI) + // Avoid NumberFormatter on WASI (Foundation formatter crashes in wasm) + return "\(value)" + #else + return formatter.string(for: value) ?? "\(value)" + #endif + } if self.size > 1000{//if size > 1000, some elements left out will be viewed let flattenLOIndSeq = FlattenLOIndSequence(storedSize: self.storedSize, shape: &shape, strides: &strides) @@ -39,7 +52,7 @@ extension MfArray: CustomStringConvertible{ desc += "\t\(flattenData[flattenIndex + self.offsetIndex]),\t" } else{ - desc += "\t\(flattenData[flattenIndex + self.offsetIndex]) \(formatter.string(for: flattenImagData![flattenIndex + self.offsetIndex]) ?? "")j,\t" + desc += "\t\(flattenData[flattenIndex + self.offsetIndex]) \(imagString(flattenImagData![flattenIndex + self.offsetIndex]))j,\t" } if indices.last! == shape.last! - 1{ @@ -81,7 +94,7 @@ extension MfArray: CustomStringConvertible{ desc += "\t\(flattenData[ret.flattenIndex + self.offsetIndex]),\t" } else{ - desc += "\t\(flattenData[ret.flattenIndex + self.offsetIndex]) \(formatter.string(for: flattenImagData![ret.flattenIndex + self.offsetIndex]) ?? "")j,\t" + desc += "\t\(flattenData[ret.flattenIndex + self.offsetIndex]) \(imagString(flattenImagData![ret.flattenIndex + self.offsetIndex]))j,\t" } if ret.indices.last! == shape.last! - 1{ From 7af4032d994227bfee8755ec9a55796c5afe2661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:33:52 +0100 Subject: [PATCH 02/26] Make mfarray compatible with wasm by disabling coreml features --- Sources/Matft/core/object/mfarray.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/Matft/core/object/mfarray.swift b/Sources/Matft/core/object/mfarray.swift index 30b92ba..11bfcc0 100644 --- a/Sources/Matft/core/object/mfarray.swift +++ b/Sources/Matft/core/object/mfarray.swift @@ -7,8 +7,12 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif +#if canImport(CoreML) import CoreML +#endif open class MfArray: MfArrayProtocol{ public typealias MFDATA = MfData @@ -111,6 +115,7 @@ open class MfArray: MfArrayProtocol{ self.mfstructure = mfstructure//mfstructure will be copied because mfstructure is struct } + #if canImport(CoreML) /// Create a VIEW or Copy mfarray from MLShapedArray /// - Parameters: /// - base: A base MLShapedArray @@ -126,6 +131,7 @@ open class MfArray: MfArrayProtocol{ self.mfdata = mfdata self.mfstructure = MfStructure(shape: base.shape.map{ Int(truncating: $0) }, strides: base.strides.map{ Int(truncating: $0) }) } + #endif deinit { self.base = nil From 696ece5bf35dd95883c344c3d3d5107a918335e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:35:23 +0100 Subject: [PATCH 03/26] Make mfadata compatible with wasm --- Sources/Matft/core/object/mfarray.swift | 3 --- Sources/Matft/core/object/mfdata.swift | 9 ++++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Sources/Matft/core/object/mfarray.swift b/Sources/Matft/core/object/mfarray.swift index 11bfcc0..f6999ad 100644 --- a/Sources/Matft/core/object/mfarray.swift +++ b/Sources/Matft/core/object/mfarray.swift @@ -7,9 +7,6 @@ // import Foundation -#if canImport(Accelerate) -import Accelerate -#endif #if canImport(CoreML) import CoreML #endif diff --git a/Sources/Matft/core/object/mfdata.swift b/Sources/Matft/core/object/mfdata.swift index aa96ccf..41a6796 100644 --- a/Sources/Matft/core/object/mfdata.swift +++ b/Sources/Matft/core/object/mfdata.swift @@ -7,7 +7,6 @@ // import Foundation -import Accelerate internal enum MfDataSource{ case mfdata @@ -72,7 +71,7 @@ public class MfData: MfDataProtocol{ case .Double: // dynamic allocation self.data_real = allocate_doubledata_from_flattenArray(&flatten_realArray, toBool: mftype == .Bool) - self.data_imag = allocate_floatdata_from_flattenArray(&flatten_imagArray, toBool: mftype == .Bool) + self.data_imag = allocate_doubledata_from_flattenArray(&flatten_imagArray, toBool: mftype == .Bool) } self.storedSize = flatten_realArray.count self.mftype = mftype @@ -106,7 +105,7 @@ public class MfData: MfDataProtocol{ if let data_imag_ptr = data_imag_ptr{ self.data_imag = allocate_unsafeMRPtr(type: Float.self, count: storedSize) - memcpy(self.data_imag, data_imag_ptr, self.storedByteSize) + memcpy(self.data_imag!, data_imag_ptr, self.storedByteSize) } else{ self.data_imag = nil @@ -114,10 +113,10 @@ public class MfData: MfDataProtocol{ case .Double: self.data_real = allocate_unsafeMRPtr(type: Double.self, count: storedSize) memcpy(self.data_real, data_real_ptr, self.storedByteSize) - + if let data_imag_ptr = data_imag_ptr{ self.data_imag = allocate_unsafeMRPtr(type: Double.self, count: storedSize) - memcpy(self.data_imag, data_imag_ptr, self.storedByteSize) + memcpy(self.data_imag!, data_imag_ptr, self.storedByteSize) } else{ self.data_imag = nil From 06851394332fe3c45a6ac2f8c0909380a5be3444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:35:47 +0100 Subject: [PATCH 04/26] Get mftype compatible with wasm --- Sources/Matft/core/object/mftype.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/Matft/core/object/mftype.swift b/Sources/Matft/core/object/mftype.swift index c41c4c5..662ff0e 100644 --- a/Sources/Matft/core/object/mftype.swift +++ b/Sources/Matft/core/object/mftype.swift @@ -7,8 +7,9 @@ // import Foundation -import Accelerate +#if canImport(CoreML) import CoreML +#endif public enum MfType: Int{ case None @@ -65,6 +66,7 @@ public enum MfType: Int{ return MfType.mftype(value: value as Any) } + #if canImport(CoreML) @available(macOS 10.13, *) @available(iOS 14.0, *) static internal func mftype(value: MLMultiArrayDataType) -> MfType{ @@ -77,6 +79,7 @@ public enum MfType: Int{ return .Object // Not supported } } + #endif static public func priority(_ a: MfType, _ b: MfType) -> MfType{ if a.rawValue < b.rawValue{ From bc7ab321346d53c90e38cac2ad225184bc5479ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:37:27 +0100 Subject: [PATCH 05/26] Provide now a mftype fallback types we when accelerate is not available --- .../Matft/core/protocol/mfdataProtocol.swift | 4 ++ .../Matft/core/protocol/mftypeProtocol.swift | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Sources/Matft/core/protocol/mfdataProtocol.swift b/Sources/Matft/core/protocol/mfdataProtocol.swift index 4b555f7..f649bb1 100644 --- a/Sources/Matft/core/protocol/mfdataProtocol.swift +++ b/Sources/Matft/core/protocol/mfdataProtocol.swift @@ -6,12 +6,16 @@ // import Foundation +#if canImport(CoreML) import CoreML +#endif public protocol MfDataBasable {} extension MfData: MfDataBasable{} +#if canImport(CoreML) @available(macOS 10.13, *) @available(iOS 14.0, *) extension MLMultiArray: MfDataBasable{} +#endif diff --git a/Sources/Matft/core/protocol/mftypeProtocol.swift b/Sources/Matft/core/protocol/mftypeProtocol.swift index 29107c6..ae72b71 100644 --- a/Sources/Matft/core/protocol/mftypeProtocol.swift +++ b/Sources/Matft/core/protocol/mftypeProtocol.swift @@ -7,7 +7,53 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#else +/// Fallback complex type for non-Apple platforms (split complex format for Float) +public struct DSPSplitComplex { + public var realp: UnsafeMutablePointer + public var imagp: UnsafeMutablePointer + + public init(realp: UnsafeMutablePointer, imagp: UnsafeMutablePointer) { + self.realp = realp + self.imagp = imagp + } +} + +/// Fallback complex type for non-Apple platforms (split complex format for Double) +public struct DSPDoubleSplitComplex { + public var realp: UnsafeMutablePointer + public var imagp: UnsafeMutablePointer + + public init(realp: UnsafeMutablePointer, imagp: UnsafeMutablePointer) { + self.realp = realp + self.imagp = imagp + } +} + +/// Fallback complex type for non-Apple platforms (interleaved complex format for Float) +public struct DSPComplex { + public var real: Float + public var imag: Float + + public init(real: Float, imag: Float) { + self.real = real + self.imag = imag + } +} + +/// Fallback complex type for non-Apple platforms (interleaved complex format for Double) +public struct DSPDoubleComplex { + public var real: Double + public var imag: Double + + public init(real: Double, imag: Double) { + self.real = real + self.imag = imag + } +} +#endif /* public protocol MfTypable: Numeric{} From a755923cfc144a8ed613212b7f01476e14af4d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:37:59 +0100 Subject: [PATCH 06/26] Get order now compatible with XP --- Sources/Matft/core/util/common/order.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/Matft/core/util/common/order.swift b/Sources/Matft/core/util/common/order.swift index a4f3298..93f30e2 100644 --- a/Sources/Matft/core/util/common/order.swift +++ b/Sources/Matft/core/util/common/order.swift @@ -7,8 +7,6 @@ // import Foundation -import Accelerate - /// Copy mfarray including structure /// - Parameter src_mfarray: The source mfarray @@ -36,7 +34,7 @@ internal func copy_all_mfarray(_ src_mfarray: MfArray) -> MfArray{ srcptr in dst_mfarray.withUnsafeMutableStartImagPointer(datatype: Float.self){ dstptr in - memcpy(dstptr, srcptr, MemoryLayout.size*newsize) + memcpy(dstptr!, srcptr, MemoryLayout.size*newsize) } } } @@ -53,7 +51,7 @@ internal func copy_all_mfarray(_ src_mfarray: MfArray) -> MfArray{ srcptr in dst_mfarray.withUnsafeMutableStartImagPointer(datatype: Double.self){ dstptr in - memcpy(dstptr, srcptr, MemoryLayout.size*newsize) + memcpy(dstptr!, srcptr, MemoryLayout.size*newsize) } } } From 654b9ff67adb5ffea90882fce894255f93609a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:38:30 +0100 Subject: [PATCH 07/26] Get a fallback implementaiton if accelerate is not availbe in types.swift --- Sources/Matft/core/util/common/type.swift | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Sources/Matft/core/util/common/type.swift b/Sources/Matft/core/util/common/type.swift index 8982ddc..4614029 100644 --- a/Sources/Matft/core/util/common/type.swift +++ b/Sources/Matft/core/util/common/type.swift @@ -6,7 +6,47 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif + +#if !canImport(Accelerate) +/// Pure Swift fallback for toBool_by_vDSP +internal func toBool_by_vDSP(_ mfarray: MfArray) -> MfArray { + let mfarray = check_contiguous(mfarray) + let size = mfarray.storedSize + let newdata = MfData(size: size, mftype: .Bool) + + newdata.withUnsafeMutableStartPointer(datatype: Float.self) { dstptr in + mfarray.withUnsafeMutableStartPointer(datatype: Float.self) { srcptr in + for i in 0.. MfArray { + let mfarray = check_contiguous(mfarray) + let size = mfarray.storedSize + let newdata = MfData(size: size, mftype: .Bool) + + newdata.withUnsafeMutableStartPointer(datatype: Float.self) { dstptr in + mfarray.withUnsafeMutableStartPointer(datatype: Float.self) { srcptr in + for i in 0.. MfArray{ //convert float and contiguous From 3ff8285006c9638002d83523e4865a1fda7882d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:39:30 +0100 Subject: [PATCH 08/26] Get some extra wrappers for libraries we don't support --- Sources/Matft/core/util/pointer/ptr2array.swift | 2 ++ Sources/Matft/core/util/pointer/withptr.swift | 2 ++ Sources/Matft/core/util/typeconversion.swift | 3 +++ 3 files changed, 7 insertions(+) diff --git a/Sources/Matft/core/util/pointer/ptr2array.swift b/Sources/Matft/core/util/pointer/ptr2array.swift index 96b3de7..6a88891 100644 --- a/Sources/Matft/core/util/pointer/ptr2array.swift +++ b/Sources/Matft/core/util/pointer/ptr2array.swift @@ -7,7 +7,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif //convert rawpointer to flattenarray via float or Double array //All kinds of int and uint has been handled as float diff --git a/Sources/Matft/core/util/pointer/withptr.swift b/Sources/Matft/core/util/pointer/withptr.swift index f8be5f6..61992a2 100644 --- a/Sources/Matft/core/util/pointer/withptr.swift +++ b/Sources/Matft/core/util/pointer/withptr.swift @@ -6,7 +6,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif extension MfArray{ diff --git a/Sources/Matft/core/util/typeconversion.swift b/Sources/Matft/core/util/typeconversion.swift index dfed6b8..7dbb499 100644 --- a/Sources/Matft/core/util/typeconversion.swift +++ b/Sources/Matft/core/util/typeconversion.swift @@ -7,7 +7,10 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif +// Note: WASI fallback implementations for vDSP functions are defined in vDSP.swift /// Get mftype from a flatten array /// - Parameter flattenArray: Flatten array. From 86b934d01dd108fee2fec97c4cb68a647e064930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:40:28 +0100 Subject: [PATCH 09/26] Get now method folder compatible with wasm --- Sources/Matft/function/method/conversion+method.swift | 6 ++++++ Sources/Matft/function/method/creation+method.swift | 1 - Sources/Matft/function/method/subscript.swift | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Sources/Matft/function/method/conversion+method.swift b/Sources/Matft/function/method/conversion+method.swift index 67e2f5b..0d3d8e4 100644 --- a/Sources/Matft/function/method/conversion+method.swift +++ b/Sources/Matft/function/method/conversion+method.swift @@ -7,7 +7,9 @@ // import Foundation +#if canImport(CoreML) import CoreML +#endif extension MfArray{ /** @@ -112,6 +114,7 @@ extension MfArray{ } + #if canImport(CoreML) /// Convert MfArray to MLMultiArray /// - Returns: Converted MLMultiArray @available(macOS 12.0, *) @@ -128,6 +131,7 @@ extension MfArray{ return try MLMultiArray(dataPointer: ptrD, shape: self.shape.map{ NSNumber(value: $0) } , dataType: MLMultiArrayDataType.double, strides: self.strides.map{ NSNumber(value: $0) }, deallocator: _deallocator_MLMultiArray_pointer) } } + #endif /** Create broadcasted mfarray. @@ -276,6 +280,8 @@ extension MfArray{ } } +#if canImport(CoreML) fileprivate func _deallocator_MLMultiArray_pointer(_ ptr: UnsafeMutableRawPointer) -> Void { ptr.deallocate() } +#endif diff --git a/Sources/Matft/function/method/creation+method.swift b/Sources/Matft/function/method/creation+method.swift index bc06d5e..7b657db 100644 --- a/Sources/Matft/function/method/creation+method.swift +++ b/Sources/Matft/function/method/creation+method.swift @@ -7,7 +7,6 @@ // import Foundation -import Accelerate extension MfArray{ /** diff --git a/Sources/Matft/function/method/subscript.swift b/Sources/Matft/function/method/subscript.swift index 61c317f..c4a413e 100644 --- a/Sources/Matft/function/method/subscript.swift +++ b/Sources/Matft/function/method/subscript.swift @@ -7,7 +7,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif extension MfArray: MfSubscriptable{ public subscript(indices: Int...) -> Any{ From f9112a2dc691e0812bf4cc6195dc2c15dc789895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:45:48 +0100 Subject: [PATCH 10/26] Get fallback implementation for static folder --- .../function/static/complex+static.swift | 2 + .../function/static/creation+static.swift | 2 + .../Matft/function/static/fft+static.swift | 14 +- .../Matft/function/static/image+static.swift | 2 + .../Matft/function/static/linalg+static.swift | 2 + .../Matft/function/static/math+static.swift | 458 ++++++++++++++++++ .../Matft/function/static/preop+static.swift | 71 +++ .../Matft/function/static/random+static.swift | 1 - .../Matft/function/static/reduce+static.swift | 1 - .../Matft/function/static/stats+static.swift | 2 + 10 files changed, 552 insertions(+), 3 deletions(-) diff --git a/Sources/Matft/function/static/complex+static.swift b/Sources/Matft/function/static/complex+static.swift index f194134..16698fb 100644 --- a/Sources/Matft/function/static/complex+static.swift +++ b/Sources/Matft/function/static/complex+static.swift @@ -6,6 +6,7 @@ // import Foundation +#if canImport(Accelerate) import Accelerate extension Matft.complex{ @@ -94,6 +95,7 @@ extension Matft.complex{ return (Matft.complex.abs(mfarray), Matft.complex.angle(mfarray)) } } +#endif /// Check it is real or not. if the mfarray is complex, raise precondition failure. /// - Parameters: diff --git a/Sources/Matft/function/static/creation+static.swift b/Sources/Matft/function/static/creation+static.swift index 628b78e..bd8e48e 100644 --- a/Sources/Matft/function/static/creation+static.swift +++ b/Sources/Matft/function/static/creation+static.swift @@ -7,7 +7,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif extension Matft{ /** diff --git a/Sources/Matft/function/static/fft+static.swift b/Sources/Matft/function/static/fft+static.swift index b747db4..298703f 100644 --- a/Sources/Matft/function/static/fft+static.swift +++ b/Sources/Matft/function/static/fft+static.swift @@ -6,7 +6,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif // ortho について // https://helve-blog.com/posts/python/numpy-fast-fourier-transform/ @@ -27,6 +29,7 @@ extension Matft.fft{ static public func rfft(_ signal: MfArray, number: Int? = nil, axis: Int = -1, norm: FFTNorm = .backward, vDSP: Bool = false) -> MfArray { let number = number ?? signal.shape[get_positive_axis(axis, ndim: signal.ndim)] + #if canImport(Accelerate) if vDSP{ switch signal.storedType { case .Float: @@ -38,6 +41,10 @@ extension Matft.fft{ else{ return fft_by_pocketFFT(signal, number: number, axis: axis, isReal: true, isForward: true, norm: norm) } + #else + // vDSP not available on this platform, use pocketFFT + return fft_by_pocketFFT(signal, number: number, axis: axis, isReal: true, isForward: true, norm: norm) + #endif } /// Real backward FFT @@ -52,8 +59,9 @@ extension Matft.fft{ /// - vDSP: Whether to use vDSP /// - Returns: FFT complex mfarray static public func irfft(_ signal: MfArray, number: Int? = nil, axis: Int = -1, norm: FFTNorm = .backward, vDSP: Bool = false) -> MfArray { - + let number = number ?? (signal.shape[get_positive_axis(axis, ndim: signal.ndim)] - 1)*2 + #if canImport(Accelerate) if vDSP{ preconditionFailure("unsupported now") /* @@ -67,6 +75,10 @@ extension Matft.fft{ else{ return fft_by_pocketFFT(signal, number: number, axis: axis, isReal: true, isForward: false, norm: norm) } + #else + // vDSP not available on this platform, use pocketFFT + return fft_by_pocketFFT(signal, number: number, axis: axis, isReal: true, isForward: false, norm: norm) + #endif } } diff --git a/Sources/Matft/function/static/image+static.swift b/Sources/Matft/function/static/image+static.swift index 0093821..46ea587 100644 --- a/Sources/Matft/function/static/image+static.swift +++ b/Sources/Matft/function/static/image+static.swift @@ -5,6 +5,7 @@ // Created by Junnosuke Kado on 2022/07/02. // +#if canImport(Accelerate) import Foundation import Accelerate import CoreGraphics @@ -115,3 +116,4 @@ extension Matft.image{ } +#endif diff --git a/Sources/Matft/function/static/linalg+static.swift b/Sources/Matft/function/static/linalg+static.swift index c5892ad..32b01b7 100644 --- a/Sources/Matft/function/static/linalg+static.swift +++ b/Sources/Matft/function/static/linalg+static.swift @@ -7,7 +7,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif extension Matft.linalg{ /** diff --git a/Sources/Matft/function/static/math+static.swift b/Sources/Matft/function/static/math+static.swift index af33439..e447970 100644 --- a/Sources/Matft/function/static/math+static.swift +++ b/Sources/Matft/function/static/math+static.swift @@ -7,10 +7,13 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif //ref https://developer.apple.com/documentation/accelerate/veclib/vforce +#if canImport(Accelerate) extension Matft.math{//use math_vv_by_vecLib // // trigonometric @@ -667,3 +670,458 @@ extension Matft.math{//use vDSP } } } +#else +// WASI fallback: Math operations using pure Swift implementations +extension Matft.math { + public static func sin(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvsinf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvsin) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func asin(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvasinf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvasin) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func sinh(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvsinhf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvsinh) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func asinh(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvasinhf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvasinh) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func cos(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvcosf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvcos) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func acos(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvacosf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvacos) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func cosh(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvcoshf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvcosh) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func acosh(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvacoshf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvacosh) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func tan(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvtanf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvtan) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func atan(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvatanf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvatan) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func tanh(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvtanhf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvtanh) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func atanh(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvatanhf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvatanh) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func sqrt(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvsqrtf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvsqrt) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func rsqrt(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvrsqrtf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvrsqrt) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func exp(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvexpf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvexp) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func exp2(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvexp2f) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvexp2) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func expm1(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvexpm1f) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvexpm1) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func log(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvlogf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvlog) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func log2(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvlog2f) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvlog2) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func log10(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvlog10f) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvlog10) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func log1p(_ mfarray: MfArray) -> MfArray { + // log1p is not in vForce, implement using log(1+x) + return Matft.math.log(mfarray + 1) + } + + public static func abs(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvfabsf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvfabs) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func reciprocal(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + return biopsv_by_vDSP(Float(1), mfarray, vDSP_svdiv) + case .Double: + return biopsv_by_vDSP(Double(1), mfarray, vDSP_svdivD) + } + } + + public static func power(bases: Float, exponents: MfArray) -> MfArray { + return Matft.math.power(bases: Matft.nums(bases, shape: [1]), exponents: exponents) + } + + public static func power(bases: MfArray, exponents: Float) -> MfArray { + return Matft.math.power(bases: bases, exponents: Matft.nums(exponents, shape: [1])) + } + + public static func power(bases: MfArray, exponents: MfArray) -> MfArray { + unsupport_complex(bases) + unsupport_complex(exponents) + let (baseBc, exponentsBc, _, _) = biop_broadcast_to(bases, exponents) + switch baseBc.storedType { + case .Float: + return math_biop_by_vForce(baseBc, exponentsBc, vvpowf) + case .Double: + return math_biop_by_vForce(baseBc, exponentsBc, vvpow) + } + } + + public static func arctan2(_ mfarrayY: MfArray, _ mfarrayX: MfArray) -> MfArray { + // arctan2 not in our vForce fallback, use element-wise atan2 + unsupport_complex(mfarrayY) + unsupport_complex(mfarrayX) + let (y, x, _, _) = biop_broadcast_to(mfarrayY, mfarrayX) + let yData = y.to_contiguous(mforder: MfOrder.Row) + let xData = x.to_contiguous(mforder: MfOrder.Row) + + switch y.storedType { + case .Float: + let newdata = MfData(size: y.size, mftype: .Float) + newdata.withUnsafeMutableStartPointer(datatype: Float.self) { dstptr in + yData.withUnsafeMutableStartPointer(datatype: Float.self) { yptr in + xData.withUnsafeMutableStartPointer(datatype: Float.self) { xptr in + for i in 0.. MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvfloorf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvfloor) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func ceil(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvceilf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvceil) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func round(_ mfarray: MfArray, decimals: Int = 0) -> MfArray { + unsupport_complex(mfarray) + if decimals == 0 { + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvnintf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvnint) + ret.mfdata.mftype = .Double + return ret + } + } else { + let factor = pow(10.0, Double(decimals)) + switch mfarray.storedType { + case .Float: + let scaled = mfarray * Float(factor) + let rounded = mathf_by_vForce(scaled, vvnintf) + rounded.mfdata.mftype = .Float + return rounded / Float(factor) + case .Double: + let scaled = mfarray * factor + let rounded = mathf_by_vForce(scaled, vvnint) + rounded.mfdata.mftype = .Double + return rounded / factor + } + } + } + + public static func trunc(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + let ret = mathf_by_vForce(mfarray, vvintf) + ret.mfdata.mftype = .Float + return ret + case .Double: + let ret = mathf_by_vForce(mfarray, vvint) + ret.mfdata.mftype = .Double + return ret + } + } + + public static func nearest(_ mfarray: MfArray) -> MfArray { + return Matft.math.round(mfarray) + } + + public static func sign(_ mfarray: MfArray) -> MfArray { + unsupport_complex(mfarray) + switch mfarray.storedType { + case .Float: + return sign_by_vDSP(mfarray, vDSP_vminmg_func: vDSP_vminmg, vDSP_viclip_func: vDSP_viclip, vForce_copysign_func: vvcopysignf) + case .Double: + return sign_by_vDSP(mfarray, vDSP_vminmg_func: vDSP_vminmgD, vDSP_viclip_func: vDSP_viclipD, vForce_copysign_func: vvcopysign) + } + } +} +#endif diff --git a/Sources/Matft/function/static/preop+static.swift b/Sources/Matft/function/static/preop+static.swift index 21efb93..aa32d6c 100644 --- a/Sources/Matft/function/static/preop+static.swift +++ b/Sources/Matft/function/static/preop+static.swift @@ -6,7 +6,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif extension Matft{ /** @@ -25,7 +27,21 @@ extension Matft{ */ public static func logical_not(_ mfarray: MfArray) -> MfArray{ var ret = to_Bool(mfarray)// copy and convert to bool + #if canImport(Accelerate) ret = Matft.math.abs(ret - 1) // force cast to Float + #else + // Pure Swift fallback for abs(ret - 1) + let size = ret.storedSize + let newdata = MfData(size: size, mftype: .Bool) + newdata.withUnsafeMutableStartPointer(datatype: Float.self) { dstptr in + ret.withUnsafeMutableStartPointer(datatype: Float.self) { srcptr in + for i in 0.. MfArray{ switch preop { case .neg: @@ -56,3 +73,57 @@ fileprivate func _prefix_operation(_ mfarray: MfArray, _ preop: PreOp) -> MfArra } } } +#else +fileprivate func _prefix_operation(_ mfarray: MfArray, _ preop: PreOp) -> MfArray{ + let mfarray = check_contiguous(mfarray) + let size = mfarray.storedSize + let newdata = MfData(size: size, mftype: mfarray.mftype, complex: mfarray.isComplex) + + switch preop { + case .neg: + switch mfarray.storedType { + case .Float: + newdata.withUnsafeMutableStartPointer(datatype: Float.self) { dstptr in + mfarray.withUnsafeMutableStartPointer(datatype: Float.self) { srcptr in + for i in 0.. MfArray diff --git a/Sources/Matft/function/static/stats+static.swift b/Sources/Matft/function/static/stats+static.swift index e4a39e7..836f032 100644 --- a/Sources/Matft/function/static/stats+static.swift +++ b/Sources/Matft/function/static/stats+static.swift @@ -7,7 +7,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif extension Matft.stats{ /** From b6a021d78168361026ca87bfcf4ddeaa9726d309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 13:48:07 +0100 Subject: [PATCH 11/26] Get library and wrapper compatible with WASM --- Sources/Matft/library/cblas.swift | 548 ++++++- Sources/Matft/library/extvDSP.swift | 2 + Sources/Matft/library/vDSP.swift | 1369 ++++++++++++++++- Sources/Matft/library/vForce.swift | 505 ++++++ Sources/Matft/library/vImage.swift | 2 + Sources/Matft/wrapper/pocketFFT+wrapper.swift | 2 + 6 files changed, 2426 insertions(+), 2 deletions(-) diff --git a/Sources/Matft/library/cblas.swift b/Sources/Matft/library/cblas.swift index a470805..8d4770c 100644 --- a/Sources/Matft/library/cblas.swift +++ b/Sources/Matft/library/cblas.swift @@ -7,6 +7,7 @@ // import Foundation +#if canImport(Accelerate) import Accelerate @@ -686,5 +687,550 @@ internal func fancysetall_by_cblas(_ mfarray: MfArray, _ indices: } } - + +} +#else +// MARK: - WASI Fallback Implementations for CBLAS + +// MARK: - Type Definitions + +public typealias cblas_copy_func = (Int32, UnsafePointer, Int32, UnsafeMutablePointer, Int32) -> Void + +public typealias cblas_matmul_func = (CBLAS_ORDER, CBLAS_TRANSPOSE, CBLAS_TRANSPOSE, Int32, Int32, Int32, T, UnsafePointer, Int32, UnsafePointer, Int32, T, UnsafeMutablePointer, Int32) -> Void + +// CBLAS enums for WASI +public typealias CBLAS_ORDER = Int32 +public let CblasRowMajor: CBLAS_ORDER = 101 +public let CblasColMajor: CBLAS_ORDER = 102 + +public typealias CBLAS_TRANSPOSE = Int32 +public let CblasNoTrans: CBLAS_TRANSPOSE = 111 +public let CblasTrans: CBLAS_TRANSPOSE = 112 +public let CblasConjTrans: CBLAS_TRANSPOSE = 113 + +// MARK: - CBLAS Copy Functions + +@inline(__always) +internal func cblas_scopy(_ n: Int32, _ x: UnsafePointer, _ incx: Int32, _ y: UnsafeMutablePointer, _ incy: Int32) { + let count = Int(n) + let srcStride = Int(incx) + let dstStride = Int(incy) + for i in 0.., _ incx: Int32, _ y: UnsafeMutablePointer, _ incy: Int32) { + let count = Int(n) + let srcStride = Int(incx) + let dstStride = Int(incy) + for i in 0.., _ lda: Int32, + _ B: UnsafePointer, _ ldb: Int32, + _ beta: Float, _ C: UnsafeMutablePointer, _ ldc: Int32) { + let m = Int(M) + let n = Int(N) + let k = Int(K) + let ldA = Int(lda) + let ldB = Int(ldb) + let ldC = Int(ldc) + + if order == CblasRowMajor { + for i in 0.., _ lda: Int32, + _ B: UnsafePointer, _ ldb: Int32, + _ beta: Double, _ C: UnsafeMutablePointer, _ ldc: Int32) { + let m = Int(M) + let n = Int(N) + let k = Int(K) + let ldA = Int(lda) + let ldB = Int(ldb) + let ldC = Int(ldc) + + if order == CblasRowMajor { + for i in 0..(_ size: Int, _ srcptr: UnsafePointer, _ srcStride: Int, _ dstptr: UnsafeMutablePointer, _ dstStride: Int, _ cblas_func: cblas_copy_func){ + cblas_func(Int32(size), srcptr, Int32(srcStride), dstptr, Int32(dstStride)) +} + +@inline(__always) +internal func wrap_cblas_matmul(_ size: Int, _ mforder: MfOrder, _ lptr: UnsafePointer, _ lrow: Int, _ lcol: Int, _ rptr: UnsafePointer, _ rrow: Int, _ rcol: Int, _ dstptr: UnsafeMutablePointer, _ cblas_func: cblas_matmul_func){ + let M = Int32(lrow) + let N = Int32(rcol) + let K = Int32(lcol) + + switch mforder { + case .Column: + let order = CblasColMajor + let lda = Int32(lrow) + let ldb = Int32(rrow) + let ldc = Int32(lrow) + cblas_func(order, CblasNoTrans, CblasNoTrans, M, N, K, T.from(1), lptr, lda, rptr, ldb, T.zero, dstptr, ldc) + case .Row: + let order = CblasRowMajor + let lda = Int32(lcol) + let ldb = Int32(rcol) + let ldc = Int32(rcol) + cblas_func(order, CblasNoTrans, CblasNoTrans, M, N, K, T.from(1), lptr, lda, rptr, ldb, T.zero, dstptr, ldc) + } +} + +// MARK: - CBLAS High-Level Functions + +internal func copy_mfarray(_ mfarray: MfArray, dsttmpMfarray: MfArray, cblas_func: cblas_copy_func) -> MfArray{ + + dsttmpMfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned dsttmpMfarray] (dstptr) in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray] (srcptr) in + for cblasPrams in OptOffsetParamsSequence(shape: dsttmpMfarray.shape, bigger_strides: dsttmpMfarray.strides, smaller_strides: mfarray.strides){ + wrap_cblas_copy(cblasPrams.blocksize, srcptr + cblasPrams.s_offset, cblasPrams.s_stride, dstptr + cblasPrams.b_offset, cblasPrams.b_stride, cblas_func) + } + } + } + + return dsttmpMfarray +} + +internal func copy_by_cblas(_ src_mfarray: MfArray, _ dst_mfarray: MfArray, cblas_func: cblas_copy_func) -> Void{ + + let shape = dst_mfarray.shape + let bigger_strides = dst_mfarray.strides + let smaller_strides = src_mfarray.strides + + dst_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + dstptr in + src_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + srcptr in + for cblasPrams in OptOffsetParamsSequence(shape: shape, bigger_strides: bigger_strides, smaller_strides: smaller_strides){ + wrap_cblas_copy(cblasPrams.blocksize, srcptr + cblasPrams.s_offset, cblasPrams.s_stride, dstptr + cblasPrams.b_offset, cblasPrams.b_stride, cblas_func) + } + } + } + + return +} + +internal func contiguous_by_cblas(_ src_mfarray: MfArray, cblas_func: cblas_copy_func, mforder: MfOrder) -> MfArray{ + + switch mforder { + case .Row: + if src_mfarray.mfstructure.row_contiguous{ + return copy_all_mfarray(src_mfarray) + } + case .Column: + if src_mfarray.mfstructure.column_contiguous{ + return copy_all_mfarray(src_mfarray) + } + } + + return samesize_by_cblas(src_mfarray, cblas_func: cblas_func, mforder: mforder) +} + +internal func samesize_by_cblas(_ src_mfarray: MfArray, cblas_func: cblas_copy_func, mforder: MfOrder) -> MfArray{ + let newsize = src_mfarray.size + let newdata: MfData = MfData(size: newsize, mftype: src_mfarray.mftype) + let newstructure = MfStructure(shape: src_mfarray.shape, mforder: mforder) + let dst_mfarray = MfArray(mfdata: newdata, mfstructure: newstructure) + + copy_by_cblas(src_mfarray, dst_mfarray, cblas_func: cblas_func) + + return dst_mfarray +} + +internal func shift_by_cblas(_ mfarray: MfArray, shift: Int, axis: Int? = nil, _ cblas_func: cblas_copy_func) -> MfArray{ + + var offset: Int + var size: Int + if let axis = axis, mfarray.ndim > 1 { + let pos_axis = get_positive_axis(axis, ndim: mfarray.ndim) + + offset = shift + size = mfarray.shape[pos_axis] + } + else{ + offset = shift + size = mfarray.size + } + + let sign = offset >= 0 ? 1 : -1 + offset = sign*(abs(offset) % size) + offset = offset >= 0 ? offset : offset + size + + assert(0 <= offset && offset < size) + offset = size - offset + + let src_mfarray: MfArray + if let axis = axis, mfarray.ndim > 1 { + src_mfarray = check_contiguous(mfarray.swapaxes(axis1: axis, axis2: 0), .Row) + var restShape = Array(mfarray.shape.suffix(mfarray.ndim-1)) + let restSize = shape2size(&restShape) + + size = src_mfarray.size + offset *= restSize + } + else{ + src_mfarray = check_contiguous(mfarray, .Row) + } + + let newdata = MfData(size: src_mfarray.storedSize, mftype: src_mfarray.mftype) + + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + src_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + srcptrT in + if size-offset > 0{ + wrap_cblas_copy(size-offset, srcptrT + offset, 1, dstptrT, 1, cblas_func) + } + if offset > 0{ + wrap_cblas_copy(offset, srcptrT, 1, dstptrT + size - offset, 1, cblas_func) + } + } + } + + let newstructure = MfStructure(shape: src_mfarray.shape, strides: src_mfarray.strides) + + if let axis = axis, mfarray.ndim > 1 { + return MfArray(mfdata: newdata, mfstructure: newstructure).swapaxes(axis1: 0, axis2: axis) + } + else{ + return MfArray(mfdata: newdata, mfstructure: newstructure) + } + +} + +internal func stack_by_cblas(_ mfarrays: [MfArray], ret_shape: [Int], ret_mftype: MfType, mforder: MfOrder, _ cblas_func: cblas_copy_func) -> MfArray{ + assert(mfarrays.count > 0) + + var ret_shape = ret_shape + let majorArrays = mfarrays.map{ $0.astype(ret_mftype, mforder: mforder) } + let ret_size = shape2size(&ret_shape) + + let newdata: MfData = MfData(size: ret_size, mftype: ret_mftype) + + var offset = 0 + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + for mfarray in majorArrays { + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + wrap_cblas_copy(mfarray.storedSize, $0, 1, dstptrT + offset, 1, cblas_func) + } + + offset += mfarray.storedSize + } + } + + let newstructure = MfStructure(shape: ret_shape, mforder: mforder) + + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func concat_by_cblas(_ mfarrays: [MfArray], ret_shape: [Int], ret_mftype: MfType, axis: Int, _ cblas_func: cblas_copy_func) -> MfArray{ + var ret_shape = ret_shape + var column_shape = ret_shape + column_shape.removeSubrange(axis..= column_size ? MfOrder.Row : MfOrder.Column + let fasterBlockSize = row_size >= column_size ? row_size : column_size + let slowerBlockSize = row_size >= column_size ? column_size : row_size + + let majorArrays = mfarrays.map{ Matft.astype($0, mftype: ret_mftype, mforder: faster_order) } + let ret_size = shape2size(&ret_shape) + + let newdata = MfData(size: ret_size, mftype: ret_mftype) + var offset = 0 + + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + for sb in 0..(_ lmfarray: inout MfArray, _ rmfarray: inout MfArray, cblas_func: cblas_matmul_func) -> MfArray{ + let lshape = lmfarray.shape + let rshape = rmfarray.shape + var retshape = lmfarray.shape + let retndim = retshape.count + retshape[retndim - 1] = rshape[retndim - 1] + + let retorder = check_matmul_contiguous(&lmfarray, &rmfarray) + + let newstructure = MfStructure(shape: retshape, mforder: retorder) + let newsize = shape2size(&retshape) + + let matNum = lshape[retndim - 2] * rshape[retndim - 1] + let l_matNum = lshape[retndim - 2] * lshape[retndim - 1] + let r_matNum = rshape[retndim - 2] * rshape[retndim - 1] + let iterNum = newsize / matNum + + let newdata = MfData(size: newsize, mftype: lmfarray.mftype) + + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + var dstptrT = dstptrT + lmfarray.withUnsafeMutableStartPointer(datatype: T.self){ + lptr in + var lptr = lptr + rmfarray.withUnsafeMutableStartPointer(datatype: T.self){ + rptr in + var rptr = rptr + + for _ in 0..(_ mfarray: MfArray, _ indices: MfArray, _ cblas_func: cblas_copy_func) -> MfArray{ + assert(indices.mftype == .Int, "must be int") + assert(mfarray.ndim > 1, "must be more than 2d") + + var workShape = Array(mfarray.shape.suffix(from: 1)) + var retShape = indices.shape + workShape + let retSize = shape2size(&retShape) + + let indices = check_contiguous(indices, .Row) + let mfarray = check_contiguous(mfarray, .Row) + + let workSize = shape2size(&workShape) + + if mfarray.isReal{ + let newdata = MfData(size: retSize, mftype: mfarray.mftype) + + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + var dstptrT = dstptrT + let _ = mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray](srcptr) in + + let offsets = (indices.data as! [Int]).map{ get_positive_index($0, axissize: mfarray.shape[0], axis: 0) * mfarray.strides[0] } + for offset in offsets{ + wrap_cblas_copy(workSize, srcptr + offset, 1, dstptrT, 1, cblas_func) + dstptrT += workSize + } + } + } + + let newstructure = MfStructure(shape: retShape, mforder: .Row) + + return MfArray(mfdata: newdata, mfstructure: newstructure) + } + else{ + fatalError("Complex fancy indexing not supported on WASI") + } +} + +internal func fancygetall_by_cblas(_ mfarray: MfArray, _ indices: inout [MfArray], _ cblas_func: cblas_copy_func) -> MfArray{ + assert(indices.count >= 2) + precondition(indices.count <= mfarray.ndim, "too many indices for array: array is \(mfarray.ndim)-dimensional, but \(indices.count) were indexed") + + let mfarray = check_contiguous(mfarray, .Row) + + let (offsets, indShape, _) = get_offsets_from_indices(mfarray, &indices) + + var workShape = Array(mfarray.shape.suffix(from: indices.count)) + var retShape = indShape + workShape + let retSize = shape2size(&retShape) + let workSize = workShape.count > 0 ? shape2size(&workShape) : 1 + + if mfarray.isReal{ + let newdata = MfData(size: retSize, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + var dstptrT = dstptrT + let _ = mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + srcptr in + + for offset in offsets{ + wrap_cblas_copy(workSize, srcptr + offset, 1, dstptrT, 1, cblas_func) + dstptrT += workSize + } + } + } + + let newstructure = MfStructure(shape: retShape, mforder: .Row) + + return MfArray(mfdata: newdata, mfstructure: newstructure) + } + else{ + fatalError("Complex fancy indexing not supported on WASI") + } +} + +internal func fancyset_by_cblas(_ mfarray: MfArray, _ indices: MfArray, _ assignedMfarray: MfArray, _ cblas_func: cblas_copy_func) -> Void{ + assert(indices.mftype == .Int, "must be int") + + var workShape = Array(mfarray.shape.suffix(from: 1)) + let workSize = shape2size(&workShape) + let retShape = indices.shape + workShape + + let indices = check_contiguous(indices, .Row) + let assignedMfarray = check_contiguous(assignedMfarray.broadcast_to(shape: retShape), .Row).astype(mfarray.mftype) + + let offsets = (indices.data as! [Int]).map{ get_positive_index($0, axissize: mfarray.shape[0], axis: 0) * mfarray.strides[0] } + + if mfarray.ndim == 1{ + + let _ = mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + dstptr in + let _ = assignedMfarray.withUnsafeMutableStartPointer(datatype: T.self){ + srcptr in + for (i, offset) in offsets.enumerated(){ + (dstptr + offset).update(from: srcptr + i, count: 1) + } + } + } + } + else{ + let _ = mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray](dstptr) in + let _ = assignedMfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned assignedMfarray](srcptr) in + + let workMfarrayStrides = Array(mfarray.strides.suffix(from: 1)) + let workAssignedMfarrayStrides = Array(assignedMfarray.strides.suffix(from: indices.ndim)) + for cblasParams in OptOffsetParamsSequence(shape: workShape, bigger_strides: workAssignedMfarrayStrides, smaller_strides: workMfarrayStrides){ + for (i, offset) in offsets.enumerated(){ + wrap_cblas_copy(cblasParams.blocksize, srcptr + i*workSize + cblasParams.b_offset, cblasParams.b_stride, dstptr + offset + cblasParams.s_offset, cblasParams.s_stride, cblas_func) + } + } + } + + } + } + +} + +internal func fancysetall_by_cblas(_ mfarray: MfArray, _ indices: inout [MfArray], _ assignedMfarray: MfArray, _ cblas_func: cblas_copy_func) -> Void{ + assert(indices.count >= 2) + precondition(indices.count <= mfarray.ndim, "too many indices for array: array is \(mfarray.ndim)-dimensional, but \(indices.count) were indexed") + + let (offsets, indShape, _) = get_offsets_from_indices(mfarray, &indices) + + var workShape = Array(mfarray.shape.suffix(from: indices.count)) + let retShape = indShape + workShape + let workSize = workShape.count > 0 ? shape2size(&workShape) : 1 + + let assignedMfarray = check_contiguous(assignedMfarray.broadcast_to(shape: retShape), .Row).astype(mfarray.mftype) + + let _ = mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray](dstptr) in + let _ = assignedMfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned assignedMfarray](srcptr) in + if workShape.count == 0{ + for (i, offset) in offsets.enumerated(){ + (dstptr + offset).update(from: srcptr + i, count: 1) + } + } + else{ + let workMfarrayStrides = Array(mfarray.strides.suffix(from: indices.count)) + let workAssignedMfarrayStrides = Array(assignedMfarray.strides.suffix(workShape.count)) + for cblasParams in OptOffsetParamsSequence(shape: workShape, bigger_strides: workAssignedMfarrayStrides, smaller_strides: workMfarrayStrides){ + for (i, offset) in offsets.enumerated(){ + wrap_cblas_copy(cblasParams.blocksize, srcptr + i*workSize + cblasParams.b_offset, cblasParams.b_stride, dstptr + offset + cblasParams.s_offset, cblasParams.s_stride, cblas_func) + } + } + } + + } + } + +} + +#endif // canImport(Accelerate) diff --git a/Sources/Matft/library/extvDSP.swift b/Sources/Matft/library/extvDSP.swift index 44a2519..67b012a 100644 --- a/Sources/Matft/library/extvDSP.swift +++ b/Sources/Matft/library/extvDSP.swift @@ -1,3 +1,4 @@ +#if canImport(Accelerate) import Accelerate import Foundation import simd @@ -127,3 +128,4 @@ internal func sign_by_evDSP(_ mfarray: MfArray, lower: T, upper: return MfArray(mfdata: newdata, mfstructure: newstructure) } +#endif diff --git a/Sources/Matft/library/vDSP.swift b/Sources/Matft/library/vDSP.swift index 39cb1db..82dd999 100644 --- a/Sources/Matft/library/vDSP.swift +++ b/Sources/Matft/library/vDSP.swift @@ -7,8 +7,14 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif +#if canImport(CoreGraphics) import CoreGraphics +#endif + +#if canImport(Accelerate) internal typealias vDSP_convert_func = (UnsafePointer, vDSP_Stride, UnsafeMutablePointer, vDSP_Stride, vDSP_Length) -> Void @@ -1584,7 +1590,1368 @@ fileprivate func _rawptr2cgimage(_ srcrawptr: UnsafeMutablePointer, bitma /// //@inline(__always) fileprivate func _cgimage2rawptr(_ dstrawptr: UnsafeMutablePointer, _ cgimage: CGImage, bitmapInfo: CGBitmapInfo, colorSpace: CGColorSpace, byteNumber: Int, width: Int, height: Int, channel: Int){ - + let contextRef = CGContext(data: dstrawptr, width: width, height: height, bitsPerComponent: 8*byteNumber, bytesPerRow: width*channel*byteNumber, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) contextRef?.draw(cgimage, in: CGRect(x: 0, y: 0, width: width, height: height)) } +#else +// MARK: - WASI Fallback Implementations + +internal typealias vDSP_biopvv_func = (UnsafePointer, Int, UnsafePointer, Int, UnsafeMutablePointer, Int, Int) -> Void +internal typealias vDSP_biopvs_func = (UnsafePointer, Int, UnsafePointer, UnsafeMutablePointer, Int, Int) -> Void +internal typealias vDSP_biopsv_func = (UnsafePointer, UnsafePointer, Int, UnsafeMutablePointer, Int, Int) -> Void +internal typealias vDSP_stats_func = (UnsafePointer, Int, UnsafeMutablePointer, Int) -> Void +internal typealias vDSP_stats_index_func = (UnsafePointer, Int, UnsafeMutablePointer, UnsafeMutablePointer, Int) -> Void +internal typealias vDSP_convert_func = (UnsafePointer, Int, UnsafeMutablePointer, Int, Int) -> Void +internal typealias vDSP_sort_func = (UnsafeMutablePointer, Int, Int32) -> Void +internal typealias vDSP_argsort_func = (UnsafePointer, UnsafeMutablePointer, UnsafeMutablePointer, Int, Int32) -> Void +internal typealias vDSP_clip_func = (UnsafePointer, Int, UnsafePointer, UnsafePointer, UnsafeMutablePointer, Int, Int, UnsafeMutablePointer, UnsafeMutablePointer) -> Void +internal typealias vDSP_vminmg_func = (UnsafePointer, Int, UnsafePointer, Int, UnsafeMutablePointer, Int, Int) -> Void +internal typealias vDSP_viclip_func = (UnsafePointer, Int, UnsafePointer, UnsafePointer, UnsafeMutablePointer, Int, Int) -> Void +internal typealias vDSP_vcmprs_func = (UnsafePointer, Int, UnsafePointer, Int, UnsafeMutablePointer, Int, Int) -> Void +internal typealias vDSP_vgathr_func = (UnsafePointer, UnsafePointer, Int, UnsafeMutablePointer, Int, Int) -> Void +internal typealias vDSP_dotpr_func = (UnsafePointer, Int, UnsafePointer, Int, UnsafeMutablePointer, Int) -> Void + +// MARK: - vDSP Binary Operations (Vector-Vector) + +@inline(__always) +internal func vDSP_vadd(_ srcA: UnsafePointer, _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ scalar: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let s = scalar.pointee + for i in 0.., _ srcStride: Int, _ scalar: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let s = scalar.pointee + for i in 0.., _ srcStride: Int, _ scalar: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let s = scalar.pointee + for i in 0.., _ srcStride: Int, _ scalar: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let s = scalar.pointee + for i in 0.., _ srcStride: Int, _ scalar: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let s = scalar.pointee + for i in 0.., _ srcStride: Int, _ scalar: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let s = scalar.pointee + for i in 0.., _ src: UnsafePointer, _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let s = scalar.pointee + for i in 0.., _ src: UnsafePointer, _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let s = scalar.pointee + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + var sum: Float = 0 + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + var sum: Double = 0 + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + guard count > 0 else { dst.pointee = 0; return } + var maxVal = src[0] + for i in 1.. maxVal { maxVal = val } + } + dst.pointee = maxVal +} + +@inline(__always) +internal func vDSP_maxvD(_ src: UnsafePointer, _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + guard count > 0 else { dst.pointee = 0; return } + var maxVal = src[0] + for i in 1.. maxVal { maxVal = val } + } + dst.pointee = maxVal +} + +@inline(__always) +internal func vDSP_minv(_ src: UnsafePointer, _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + guard count > 0 else { dst.pointee = 0; return } + var minVal = src[0] + for i in 1.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + guard count > 0 else { dst.pointee = 0; return } + var minVal = src[0] + for i in 1.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ idx: UnsafeMutablePointer, _ count: Int) { + guard count > 0 else { dst.pointee = 0; idx.pointee = 0; return } + var maxVal = src[0] + var maxIdx: UInt = 0 + for i in 1.. maxVal { + maxVal = val + maxIdx = UInt(i * srcStride) + } + } + dst.pointee = maxVal + idx.pointee = maxIdx +} + +@inline(__always) +internal func vDSP_maxviD(_ src: UnsafePointer, _ srcStride: Int, _ dst: UnsafeMutablePointer, _ idx: UnsafeMutablePointer, _ count: Int) { + guard count > 0 else { dst.pointee = 0; idx.pointee = 0; return } + var maxVal = src[0] + var maxIdx: UInt = 0 + for i in 1.. maxVal { + maxVal = val + maxIdx = UInt(i * srcStride) + } + } + dst.pointee = maxVal + idx.pointee = maxIdx +} + +@inline(__always) +internal func vDSP_minvi(_ src: UnsafePointer, _ srcStride: Int, _ dst: UnsafeMutablePointer, _ idx: UnsafeMutablePointer, _ count: Int) { + guard count > 0 else { dst.pointee = 0; idx.pointee = 0; return } + var minVal = src[0] + var minIdx: UInt = 0 + for i in 1.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ idx: UnsafeMutablePointer, _ count: Int) { + guard count > 0 else { dst.pointee = 0; idx.pointee = 0; return } + var minVal = src[0] + var minIdx: UInt = 0 + for i in 1.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + var sum: Float = 0 + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + var sum: Double = 0 + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + var sum: Float = 0 + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + var sum: Double = 0 + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ count: Int, _ order: Int32) { + var arr = Array(UnsafeBufferPointer(start: src, count: count)) + if order == 1 { + arr.sort(by: <) + } else { + arr.sort(by: >) + } + for i in 0.., _ count: Int, _ order: Int32) { + var arr = Array(UnsafeBufferPointer(start: src, count: count)) + if order == 1 { + arr.sort(by: <) + } else { + arr.sort(by: >) + } + for i in 0.., _ dst: UnsafeMutablePointer, _ tmp: UnsafeMutablePointer, _ count: Int, _ order: Int32) { + var indices = Array(0.. src[Int($1)] } + } + for i in 0.., _ dst: UnsafeMutablePointer, _ tmp: UnsafeMutablePointer, _ count: Int, _ order: Int32) { + var indices = Array(0.. src[Int($1)] } + } + for i in 0.., _ srcStride: Int, _ low: UnsafePointer, _ high: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int, _ lowCount: UnsafeMutablePointer, _ highCount: UnsafeMutablePointer) { + let lo = low.pointee + let hi = high.pointee + var lc: UInt = 0 + var hc: UInt = 0 + for i in 0.. hi { + dst[i * dstStride] = hi + hc += 1 + } else { + dst[i * dstStride] = val + } + } + lowCount.pointee = lc + highCount.pointee = hc +} + +@inline(__always) +internal func vDSP_vclipD(_ src: UnsafePointer, _ srcStride: Int, _ low: UnsafePointer, _ high: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int, _ lowCount: UnsafeMutablePointer, _ highCount: UnsafeMutablePointer) { + let lo = low.pointee + let hi = high.pointee + var lc: UInt = 0 + var hc: UInt = 0 + for i in 0.. hi { + dst[i * dstStride] = hi + hc += 1 + } else { + dst[i * dstStride] = val + } + } + lowCount.pointee = lc + highCount.pointee = hc +} + +// MARK: - vDSP Misc Functions + +@inline(__always) +internal func vDSP_vminmg(_ srcA: UnsafePointer, _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ strideDst: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ low: UnsafePointer, _ high: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let lo = low.pointee + let hi = high.pointee + for i in 0.. lo && val < hi { + dst[i * dstStride] = hi + } else { + dst[i * dstStride] = val + } + } +} + +@inline(__always) +internal func vDSP_viclipD(_ src: UnsafePointer, _ srcStride: Int, _ low: UnsafePointer, _ high: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + let lo = low.pointee + let hi = high.pointee + for i in 0.. lo && val < hi { + dst[i * dstStride] = hi + } else { + dst[i * dstStride] = val + } + } +} + +@inline(__always) +internal func vDSP_vcmprs(_ src: UnsafePointer, _ srcStride: Int, _ gate: UnsafePointer, _ gateStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + var dstIdx = 0 + for i in 0.., _ srcStride: Int, _ gate: UnsafePointer, _ gateStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + var dstIdx = 0 + for i in 0.., _ indices: UnsafePointer, _ indStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ indices: UnsafePointer, _ indStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + var sum: Float = 0 + for i in 0.., _ strideA: Int, _ srcB: UnsafePointer, _ strideB: Int, _ dst: UnsafeMutablePointer, _ count: Int) { + var sum: Double = 0 + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0.., _ srcStride: Int, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int) { + for i in 0..(_ size: Int, _ srcptr: UnsafePointer, _ srcStride: Int, _ dstptr: UnsafeMutablePointer, _ dstStride: Int, _ vDSP_func: (UnsafePointer, Int, UnsafeMutablePointer, Int, Int) -> Void){ + vDSP_func(srcptr, srcStride, dstptr, dstStride, size) +} + +@inline(__always) +internal func wrap_vDSP_biopvv(_ size: Int, _ lsrcptr: UnsafePointer, _ lsrcStride: Int, _ rsrcptr: UnsafePointer, _ rsrcStride: Int, _ dstptr: UnsafeMutablePointer, _ dstStride: Int, _ vDSP_func: vDSP_biopvv_func){ + vDSP_func(rsrcptr, rsrcStride, lsrcptr, lsrcStride, dstptr, dstStride, size) +} + +@inline(__always) +internal func wrap_vDSP_biopvs(_ size: Int, _ srcptr: UnsafePointer, _ srcStride: Int, _ scalar: UnsafePointer, _ dstptr: UnsafeMutablePointer, _ dstStride: Int, _ vDSP_func: vDSP_biopvs_func){ + vDSP_func(srcptr, srcStride, scalar, dstptr, dstStride, size) +} + +@inline(__always) +internal func wrap_vDSP_biopsv(_ size: Int, _ scalar: UnsafePointer, _ srcptr: UnsafePointer, _ srcStride: Int, _ dstptr: UnsafeMutablePointer, _ dstStride: Int, _ vDSP_func: vDSP_biopsv_func){ + vDSP_func(scalar, srcptr, srcStride, dstptr, dstStride, size) +} + +@inline(__always) +internal func wrap_vDSP_stats(_ size: Int, _ srcptr: UnsafePointer, _ stride: Int, _ dstptr: UnsafeMutablePointer, _ vDSP_func: vDSP_stats_func){ + vDSP_func(srcptr, stride, dstptr, size) +} + +@inline(__always) +internal func wrap_vDSP_stats_index(_ size: Int, _ srcptr: UnsafePointer, _ stride: Int, _ dstptr: UnsafeMutablePointer, _ vDSP_func: vDSP_stats_index_func){ + var tmp = Array(repeating: T.zero, count: size) + vDSP_func(srcptr, stride, &tmp, dstptr, size) +} + +@inline(__always) +internal func wrap_vDSP_sort(_ size: Int, _ srcdstptr: UnsafeMutablePointer, _ order: MfSortOrder, _ vDSP_func: vDSP_sort_func){ + vDSP_func(srcdstptr, size, order.rawValue) +} + +@inline(__always) +internal func wrap_vDSP_argsort(_ size: Int, _ srcptr: UnsafePointer, _ dstptr: UnsafeMutablePointer, _ order: MfSortOrder, _ vDSP_func: vDSP_argsort_func){ + var tmp = Array(repeating: 0, count: size) + vDSP_func(srcptr, dstptr, &tmp, size, order.rawValue) +} + +@inline(__always) +internal func wrap_vDSP_clip(_ size: Int, _ srcptr: UnsafePointer, _ minptr: UnsafePointer, _ maxptr: UnsafePointer, _ dstptr: UnsafeMutablePointer, _ vDSP_clip_func: vDSP_clip_func){ + var mincount = UInt(0) + var maxcount = UInt(0) + vDSP_clip_func(srcptr, 1, minptr, maxptr, dstptr, 1, size, &mincount, &maxcount) +} + +@inline(__always) +internal func wrap_vDSP_cmprs(_ size: Int, _ srcptr: UnsafePointer, _ srcStride: Int, _ indptr: UnsafePointer, _ indStride: Int, _ dstptr: UnsafeMutablePointer, _ dstStride: Int, _ vDSP_func: vDSP_vcmprs_func){ + vDSP_func(srcptr, srcStride, indptr, indStride, dstptr, dstStride, size) +} + +@inline(__always) +internal func wrap_vDSP_gathr(_ size: Int, _ srcptr: UnsafePointer, _ indptr: UnsafePointer, _ indStride: Int, _ dstptr: UnsafeMutablePointer, _ dstStride: Int, _ vDSP_func: vDSP_vgathr_func){ + vDSP_func(srcptr, indptr, indStride, dstptr, dstStride, size) +} + +@inline(__always) +internal func wrap_vDSP_dotpr(_ size: Int, _ lsrcptr: UnsafePointer, _ lsrcStride: Int, _ rsrcptr: UnsafePointer, _ rsrcStride: Int, _ dstptr: UnsafeMutablePointer, _ vDSP_func: vDSP_dotpr_func){ + vDSP_func(lsrcptr, lsrcStride, rsrcptr, rsrcStride, dstptr, size) +} + +// MARK: - vDSP High-Level Functions for WASI + +internal func contiguous_and_astype_by_vDSP(_ src_mfarray: MfArray, mftype: MfType, mforder: MfOrder, vDSP_func: vDSP_convert_func) -> MfArray{ + var ret_shape = src_mfarray.shape + let ret_strides = shape2strides(&ret_shape, mforder: mforder) + + let newdata = MfData(size: src_mfarray.size, mftype: mftype) + + newdata.withUnsafeMutableStartPointer(datatype: U.self){ + dstptrU in + src_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned src_mfarray] srcptrT in + + for vDSPPrams in OptOffsetParamsSequence(shape: ret_shape, bigger_strides: ret_strides, smaller_strides: src_mfarray.strides){ + + wrap_vDSP_convert(vDSPPrams.blocksize, srcptrT + vDSPPrams.s_offset, vDSPPrams.s_stride, dstptrU + vDSPPrams.b_offset, vDSPPrams.b_stride, vDSP_func) + } + + } + } + + let newstructure = MfStructure(shape: ret_shape, strides: ret_strides) + + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func preop_by_vDSP(_ mfarray: MfArray, _ vDSP_func: vDSP_convert_func) -> MfArray{ + var mfarray = mfarray + mfarray = check_contiguous(mfarray) + + let newdata = MfData(size: mfarray.storedSize, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray] in + wrap_vDSP_convert(mfarray.storedSize, $0, 1, dstptrT, 1, vDSP_func) + } + } + + let newstructure = MfStructure(shape: mfarray.shape, strides: mfarray.strides) + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func math_by_vDSP(_ mfarray: MfArray, _ vDSP_func: vDSP_convert_func) -> MfArray{ + return preop_by_vDSP(mfarray, vDSP_func) +} + +internal func biopvs_by_vDSP(_ l_mfarray: MfArray, _ r_scalar: T, _ vDSP_func: vDSP_biopvs_func) -> MfArray{ + var mfarray = l_mfarray + var r_scalar = r_scalar + + mfarray = check_contiguous(mfarray) + + let newdata = MfData(size: mfarray.storedSize, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray] in + wrap_vDSP_biopvs(mfarray.storedSize, $0, 1, &r_scalar, dstptrT, 1, vDSP_func) + } + } + + let newstructure = MfStructure(shape: mfarray.shape, strides: mfarray.strides) + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func biopsv_by_vDSP(_ l_scalar: T, _ r_mfarray: MfArray, _ vDSP_func: vDSP_biopsv_func) -> MfArray{ + var mfarray = r_mfarray + var l_scalar = l_scalar + + mfarray = check_contiguous(mfarray) + + let newdata = MfData(size: mfarray.storedSize, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray] in + wrap_vDSP_biopsv(mfarray.storedSize, &l_scalar, $0, 1, dstptrT, 1, vDSP_func) + } + } + + let newstructure = MfStructure(shape: mfarray.shape, strides: mfarray.strides) + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func biopvv_by_vDSP(_ l_mfarray: MfArray, _ r_mfarray: MfArray, vDSP_func: vDSP_biopvv_func) -> MfArray{ + let (l_mfarray, r_mfarray, biggerL, retsize) = check_biop_contiguous(l_mfarray, r_mfarray, .Row, convertL: true) + + let newdata = MfData(size: retsize, mftype: l_mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + l_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned l_mfarray] (lptr) in + r_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned r_mfarray] (rptr) in + if biggerL{ + for vDSPPrams in OptOffsetParamsSequence(shape: l_mfarray.shape, bigger_strides: l_mfarray.strides, smaller_strides: r_mfarray.strides){ + wrap_vDSP_biopvv(vDSPPrams.blocksize, lptr + vDSPPrams.b_offset, vDSPPrams.b_stride, rptr + vDSPPrams.s_offset, vDSPPrams.s_stride, dstptrT + vDSPPrams.b_offset, vDSPPrams.b_stride, vDSP_func) + } + } + else{ + for vDSPPrams in OptOffsetParamsSequence(shape: r_mfarray.shape, bigger_strides: r_mfarray.strides, smaller_strides: l_mfarray.strides){ + wrap_vDSP_biopvv(vDSPPrams.blocksize, lptr + vDSPPrams.s_offset, vDSPPrams.s_stride, rptr + vDSPPrams.b_offset, vDSPPrams.b_stride, dstptrT + vDSPPrams.b_offset, vDSPPrams.b_stride, vDSP_func) + } + } + } + } + } + + let newstructure: MfStructure + if biggerL{ + newstructure = MfStructure(shape: l_mfarray.shape, strides: l_mfarray.strides) + } + else{ + newstructure = MfStructure(shape: r_mfarray.shape, strides: r_mfarray.strides) + } + + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func stats_by_vDSP(_ typedMfarray: MfArray, axis: Int?, keepDims: Bool, vDSP_func: vDSP_stats_func) -> MfArray{ + + let mfarray = check_contiguous(typedMfarray, .Row) + + if let axis = axis, mfarray.ndim > 1{ + let axis = get_positive_axis(axis, ndim: mfarray.ndim) + var ret_shape = mfarray.shape + let count = ret_shape.remove(at: axis) + var ret_strides = mfarray.strides + let stride = ret_strides.remove(at: axis) + + let ret_size = shape2size(&ret_shape) + + let newdata = MfData(size: ret_size, mftype: mfarray.mftype) + var dst_offset = 0 + + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + for flat in FlattenIndSequence(shape: &ret_shape, strides: &ret_strides){ + wrap_vDSP_stats(count, $0 + flat.flattenIndex, stride, dstptrT + dst_offset, vDSP_func) + dst_offset += 1 + } + } + } + + let newstructure = MfStructure(shape: ret_shape, mforder: .Row) + + let ret = MfArray(mfdata: newdata, mfstructure: newstructure) + return keepDims ? Matft.expand_dims(ret, axis: axis) : ret + } + else{ + let newdata = MfData(size: 1, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + wrap_vDSP_stats(mfarray.size, $0, 1, dstptrT, vDSP_func) + } + } + + let ret_shape = keepDims ? Array(repeating: 1, count: mfarray.ndim) : [1] + let newstructure = MfStructure(shape: ret_shape, mforder: .Row) + return MfArray(mfdata: newdata, mfstructure: newstructure) + } +} + +internal func stats_index_by_vDSP(_ mfarray: MfArray, axis: Int?, keepDims: Bool, vDSP_func: vDSP_stats_index_func) -> MfArray{ + + let mfarray = check_contiguous(mfarray, .Row) + + if let axis = axis, mfarray.ndim > 1{ + let axis = get_positive_axis(axis, ndim: mfarray.ndim) + var ret_shape = mfarray.shape + let count = ret_shape.remove(at: axis) + var ret_strides = mfarray.strides + let stride = ret_strides.remove(at: axis) + let ui_stride = UInt(stride) + + let ret_size = shape2size(&ret_shape) + + let newdata = MfData(size: ret_size, mftype: mfarray.mftype) + var dst_offset = 0 + + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + for flat in FlattenIndSequence(shape: &ret_shape, strides: &ret_strides){ + var uival = UInt.zero + wrap_vDSP_stats_index(count, $0 + flat.flattenIndex, stride, &uival, vDSP_func) + (dstptrT + dst_offset).pointee = T.from(uival / ui_stride) + + dst_offset += 1 + } + } + } + + let newstructure = MfStructure(shape: ret_shape, mforder: .Row) + + let ret = MfArray(mfdata: newdata, mfstructure: newstructure) + return keepDims ? Matft.expand_dims(ret, axis: axis) : ret + } + else{ + let newdata = MfData(size: 1, mftype: mfarray.mftype) + var uival = UInt.zero + + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + wrap_vDSP_stats_index(mfarray.size, $0, 1, &uival, vDSP_func) + } + dstptrT.pointee = T.from(uival) + } + + let ret_shape = keepDims ? Array(repeating: 1, count: mfarray.ndim) : [1] + let newstructure = MfStructure(shape: ret_shape, mforder: .Row) + return MfArray(mfdata: newdata, mfstructure: newstructure) + } +} + +internal func sort_by_vDSP(_ mfarray: MfArray, _ axis: Int, _ order: MfSortOrder, _ vDSP_func: vDSP_sort_func) -> MfArray{ + let retndim = mfarray.ndim + let count = mfarray.shape[axis] + + let lastaxis = retndim - 1 + let srcdst_mfarray = mfarray.moveaxis(src: axis, dst: lastaxis).to_contiguous(mforder: .Row) + + var offset = 0 + + srcdst_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + srcdstptr in + for _ in 0..(_ mfarray: MfArray, _ axis: Int, _ order: MfSortOrder, _ vDSP_func: vDSP_argsort_func) -> MfArray{ + + let count = mfarray.shape[axis] + + let lastaxis = mfarray.ndim - 1 + let srcmfarray = mfarray.moveaxis(src: axis, dst: lastaxis).to_contiguous(mforder: .Row) + var retShape = srcmfarray.shape + + var offset = 0 + + let retSize = shape2size(&retShape) + let newdata = MfData(size: retSize, mftype: .Int) + newdata.withUnsafeMutableStartPointer(datatype: Float.self){ + dstptrF in + srcmfarray.withUnsafeMutableStartPointer(datatype: T.self){ + srcptr in + + for _ in 0..(stride(from: 0, to: UInt(count), by: 1)) + wrap_vDSP_argsort(count, srcptr + offset, &uiarray, order, vDSP_func) + + var flarray = uiarray.map{ Float($0) } + flarray.withUnsafeMutableBufferPointer{ + (dstptrF + offset).moveUpdate(from: $0.baseAddress!, count: count) + } + + offset += count + } + + } + } + + let newstructure = MfStructure(shape: retShape, mforder: .Row) + + let ret = MfArray(mfdata: newdata, mfstructure: newstructure) + + return ret.moveaxis(src: lastaxis, dst: axis) + +} + +internal func clip_by_vDSP(_ mfarray: MfArray, _ minval: T, _ maxval: T, _ vDSP_func: vDSP_clip_func) -> MfArray{ + var mfarray = mfarray + var minval = minval + var maxval = maxval + + mfarray = check_contiguous(mfarray) + + let newdata = MfData(size: mfarray.storedSize, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray] in + wrap_vDSP_clip(mfarray.storedSize, $0, &minval, &maxval, dstptrT, vDSP_func) + } + } + + let newstructure = MfStructure(shape: mfarray.shape, strides: mfarray.strides) + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func boolget_by_vDSP(_ mfarray: MfArray, _ indices: MfArray, _ vDSP_func: vDSP_vcmprs_func) -> MfArray{ + assert(indices.mftype == .Bool, "must be bool") + + let true_num = Float.toInt(indices.sum().scalar(Float.self)!) + let orig_ind_dim = indices.ndim + + let indices = bool_broadcast_to(indices, shape: mfarray.shape) + + let indicesT: MfArray + switch mfarray.storedType { + case .Float: + indicesT = indices + case .Double: + indicesT = indices.astype(.Double) + } + + let lastShape = Array(mfarray.shape.suffix(mfarray.ndim - orig_ind_dim)) + var retShape = [true_num] + lastShape + let retSize = shape2size(&retShape) + + if mfarray.isReal{ + let newdata = MfData(size: retSize, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + indicesT.withUnsafeMutableStartPointer(datatype: T.self){ + indptr in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + srcptr in + + for vDSPPrams in OptOffsetParamsSequence(shape: indicesT.shape, bigger_strides: indicesT.strides, smaller_strides: mfarray.strides){ + wrap_vDSP_cmprs(vDSPPrams.blocksize, srcptr + vDSPPrams.s_offset, vDSPPrams.s_stride, indptr + vDSPPrams.b_offset, vDSPPrams.b_stride, dstptrT + vDSPPrams.b_offset, vDSPPrams.b_stride, vDSP_func) + } + } + } + } + + + let newstructure = MfStructure(shape: retShape, mforder: .Row) + + return MfArray(mfdata: newdata, mfstructure: newstructure) + } + else{ + fatalError("Complex boolean indexing not supported on WASI") + } +} + +internal func fancy1dgetcol_by_vDSP(_ mfarray: MfArray, _ indices: MfArray, _ vDSP_func: vDSP_vgathr_func) -> MfArray{ + assert(indices.mftype == .Int, "must be int") + assert(mfarray.ndim == 1, "must be 1d") + + if mfarray.isReal{ + let newdata = MfData(size: indices.size, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + let _ = mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + srcptr in + var offsets = (indices.data as! [Int]).map{ UInt(get_positive_index($0, axissize: mfarray.size, axis: 0) * mfarray.strides[0] + 1) } + wrap_vDSP_gathr(indices.size, srcptr, &offsets, 1, dstptrT, 1, vDSP_func) + } + } + + let newstructure = MfStructure(shape: indices.shape, strides: indices.strides) + return MfArray(mfdata: newdata, mfstructure: newstructure) + } + else{ + fatalError("Complex fancy indexing not supported on WASI") + } +} + +internal func dotpr_by_vDSP(_ l_mfarray: MfArray, _ r_mfarray: MfArray, vDSP_func: vDSP_dotpr_func) -> MfArray{ + let l_shape = l_mfarray.shape + let r_shape = r_mfarray.shape + assert(l_shape[0] == r_shape[1]) + + let size = l_shape[0] + + let l_mfarray = check_contiguous(l_mfarray, .Row) + let r_mfarray = r_mfarray.swapaxes(axis1: -1, axis2: -2).to_contiguous(mforder: .Row) + + var l_rest_shape = Array(l_shape.prefix(l_shape.count - 1)) + var r_rest_shape = Array(r_shape.prefix(r_shape.count - 2) + r_shape.suffix(1)) + var ret_shape = l_rest_shape + r_rest_shape + + let l_rest_size = l_rest_shape.count > 0 ? shape2size(&l_rest_shape) : 1 + let r_rest_size = shape2size(&r_rest_shape) + let ret_size = shape2size(&ret_shape) + + let newdata = MfData(size: ret_size, mftype: l_mfarray.mftype) + + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptr in + l_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + lptr in + r_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + rptr in + for l_ind in 0..(_ size: Int, _ srcptr: UnsafePointer, _ dstptr: UnsafeMutablePointer, _ vDSP_vminmg_func: vDSP_vminmg_func, _ vDSP_viclip_func: vDSP_viclip_func, _ vForce_copysign_func: vForce_copysign_func){ + var i32size = Int32(size) + + var one = T.from(1) + vDSP_vminmg_func(srcptr, 1, &one, 0, dstptr, 1, size) + + var zero = T.zero + one = T.from(1) + vDSP_viclip_func(dstptr, 1, &zero, &one, dstptr, 1, size) + vForce_copysign_func(dstptr, dstptr, srcptr, &i32size) +} + +internal func sign_by_vDSP(_ mfarray: MfArray, vDSP_vminmg_func: vDSP_vminmg_func, vDSP_viclip_func: vDSP_viclip_func, vForce_copysign_func: vForce_copysign_func) -> MfArray{ + let mfarray = check_contiguous(mfarray) + + let size = mfarray.storedSize + let newdata = MfData(size: mfarray.storedSize, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + wrap_vDSP_sign(size, $0, dstptrT, vDSP_vminmg_func, vDSP_viclip_func, + vForce_copysign_func) + } + } + + let newstructure = MfStructure(shape: mfarray.shape, strides: mfarray.strides) + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +// MARK: - Clip Functions with C-style signatures for WASI + +@inline(__always) +internal func vDSP_vclipc(_ src: UnsafePointer, _ srcStride: Int, _ low: UnsafePointer, _ high: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int, _ lowCount: UnsafeMutablePointer, _ highCount: UnsafeMutablePointer) { + vDSP_vclip(src, srcStride, low, high, dst, dstStride, count, lowCount, highCount) +} + +@inline(__always) +internal func vDSP_vclipcD(_ src: UnsafePointer, _ srcStride: Int, _ low: UnsafePointer, _ high: UnsafePointer, _ dst: UnsafeMutablePointer, _ dstStride: Int, _ count: Int, _ lowCount: UnsafeMutablePointer, _ highCount: UnsafeMutablePointer) { + vDSP_vclipD(src, srcStride, low, high, dst, dstStride, count, lowCount, highCount) +} + +#endif // canImport(Accelerate) diff --git a/Sources/Matft/library/vForce.swift b/Sources/Matft/library/vForce.swift index d496c2e..7ec9f96 100644 --- a/Sources/Matft/library/vForce.swift +++ b/Sources/Matft/library/vForce.swift @@ -7,6 +7,7 @@ // import Foundation +#if canImport(Accelerate) import Accelerate public typealias vForce_copysign_func = (UnsafeMutablePointer, UnsafePointer, UnsafePointer, UnsafePointer) -> Void @@ -86,3 +87,507 @@ internal func math_biop_by_vForce(_ l_mfarray: MfArray, _ r_mfarr let newstructure = MfStructure(shape: l_mfarray.shape, strides: l_mfarray.strides) return MfArray(mfdata: newdata, mfstructure: newstructure) } +#else +// MARK: - WASI Fallback Implementations for vForce + +public typealias vForce_copysign_func = (UnsafeMutablePointer, UnsafePointer, UnsafePointer, UnsafePointer) -> Void + +public typealias vForce_math_func = (UnsafeMutablePointer, UnsafePointer, UnsafePointer) -> Void + +public typealias vForce_math_biop_func = (UnsafeMutablePointer, UnsafePointer, UnsafePointer, UnsafePointer) -> Void + +// MARK: - vForce Math Functions (Float) + +@inline(__always) +internal func vvsqrtf(_ dst: UnsafeMutablePointer, _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ base: UnsafePointer, _ exp: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ mag: UnsafePointer, _ sign: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ base: UnsafePointer, _ exp: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ src: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0.., _ mag: UnsafePointer, _ sign: UnsafePointer, _ count: UnsafePointer) { + let n = Int(count.pointee) + for i in 0..(_ mfarray: MfArray, _ vForce_func: vForce_math_func) -> MfArray{ + let mfarray = check_contiguous(mfarray) + var ret_size = Int32(mfarray.size) + + let newdata = MfData(size: mfarray.size, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + vForce_func(dstptrT, $0, &ret_size) + } + } + + let newstructure = MfStructure(shape: mfarray.shape, strides: mfarray.strides) + + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func mathf_by_vForce(_ mfarray: MfArray, _ vForce_func: vForce_math_func) -> MfArray{ + var mfarray = mfarray + mfarray = check_contiguous(mfarray) + + let newdata = MfData(size: mfarray.storedSize, mftype: mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + [unowned mfarray] in + var storedSize = Int32(mfarray.storedSize) + vForce_func(dstptrT, $0, &storedSize) + } + } + + let newstructure = MfStructure(shape: mfarray.shape, strides: mfarray.strides) + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func math_biop_by_vForce(_ l_mfarray: MfArray, _ r_mfarray: MfArray, _ vForce_func: vForce_math_biop_func) -> MfArray{ + let l_mfarray = l_mfarray.to_contiguous(mforder: .Row) + let r_mfarray = r_mfarray.to_contiguous(mforder: .Row) + + var storedSize = Int32(l_mfarray.storedSize) + let newdata = MfData(size: l_mfarray.storedSize, mftype: l_mfarray.mftype) + newdata.withUnsafeMutableStartPointer(datatype: T.self){ + dstptrT in + l_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + lptr in + r_mfarray.withUnsafeMutableStartPointer(datatype: T.self){ + rptr in + vForce_func(dstptrT, lptr, rptr, &storedSize) + } + } + } + + let newstructure = MfStructure(shape: l_mfarray.shape, strides: l_mfarray.strides) + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +#endif // canImport(Accelerate) diff --git a/Sources/Matft/library/vImage.swift b/Sources/Matft/library/vImage.swift index e8ce5b8..86c01a3 100644 --- a/Sources/Matft/library/vImage.swift +++ b/Sources/Matft/library/vImage.swift @@ -6,6 +6,7 @@ // import Foundation +#if canImport(Accelerate) import Accelerate internal typealias vImage_resize_func = (UnsafePointer, UnsafePointer, UnsafeMutableRawPointer?, vImage_Flags) -> vImage_Error @@ -270,3 +271,4 @@ internal func affine_by_vImage(_ image: MfArray, dstHeight: Int, dstWidth: Int, return MfArray(mfdata: newdata, mfstructure: newstructure) } +#endif diff --git a/Sources/Matft/wrapper/pocketFFT+wrapper.swift b/Sources/Matft/wrapper/pocketFFT+wrapper.swift index c1d74d4..47209d4 100644 --- a/Sources/Matft/wrapper/pocketFFT+wrapper.swift +++ b/Sources/Matft/wrapper/pocketFFT+wrapper.swift @@ -5,7 +5,9 @@ // Created by AM19A0 on 2023/02/10. // import pocketFFT +#if canImport(Accelerate) import Accelerate +#endif internal typealias rfft_func = (rfft_plan, UnsafeMutablePointer, Double) -> Int32 From fd1101b12b4fe1ecab4c3335470ba959aa3dc6c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:10:24 +0100 Subject: [PATCH 12/26] Add support for complex testing scenarios --- Tests/LinuxMain.swift | 7 ------- Tests/MatftTests/ConversionTest.swift | 9 +++++++-- Tests/MatftTests/CreationTest.swift | 4 ++++ Tests/MatftTests/FFTTest.swift | 8 +++++--- Tests/MatftTests/FileTest.swift | 5 +++-- Tests/MatftTests/InterpolationTest.swift | 8 +++++--- Tests/MatftTests/LinAlgTest.swift | 7 +++++-- Tests/MatftTests/MatftTests.swift | 9 ++++++++- Tests/MatftTests/MathTest.swift | 2 +- Tests/MatftTests/SubscriptTest.swift | 25 ++++++++++++++++-------- 10 files changed, 55 insertions(+), 29 deletions(-) delete mode 100644 Tests/LinuxMain.swift diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index b39d7a4..0000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,7 +0,0 @@ -import XCTest - -import MatftTests - -var tests = [XCTestCaseEntry]() -tests += MatftTests.allTests() -XCTMain(tests) diff --git a/Tests/MatftTests/ConversionTest.swift b/Tests/MatftTests/ConversionTest.swift index 8c9d1e6..226efdc 100644 --- a/Tests/MatftTests/ConversionTest.swift +++ b/Tests/MatftTests/ConversionTest.swift @@ -1,7 +1,10 @@ import XCTest //@testable import Matft import Matft + +#if canImport(CoreML) import CoreML +#endif final class ConversionTests: XCTestCase { @@ -578,7 +581,8 @@ final class ConversionTests: XCTestCase { [18, 19, 20]]])) } } - + + #if canImport(CoreML) @available(macOS 12.0, *) @available(iOS 14.0, *) func testToMlMultiArray() throws{ @@ -591,7 +595,7 @@ final class ConversionTests: XCTestCase { let a = MfArray(arr, shape: [2, 3]) XCTAssertEqual(try a.toMLMultiArray(), mlmularr) } - + do { let arr = [1.0, 2, 3, 4.0, 5, 6] let arrT = [1.0, 4, 2, 5, 3, 6] @@ -603,4 +607,5 @@ final class ConversionTests: XCTestCase { XCTAssertEqual(try a.T.toMLMultiArray(), mlmularr) } } + #endif } diff --git a/Tests/MatftTests/CreationTest.swift b/Tests/MatftTests/CreationTest.swift index 7803aba..24c1ba8 100644 --- a/Tests/MatftTests/CreationTest.swift +++ b/Tests/MatftTests/CreationTest.swift @@ -1,10 +1,13 @@ import XCTest //@testable import Matft import Matft +#if canImport(CoreML) import CoreML +#endif final class CreationTests: XCTestCase { + #if canImport(CoreML) @available(macOS 12.0, *) @available(iOS 14.0, *) func testFromMLMultiArray() { @@ -52,6 +55,7 @@ final class CreationTests: XCTestCase { } } + #endif func testAppend() { do { let x = MfArray([1,2,3]) diff --git a/Tests/MatftTests/FFTTest.swift b/Tests/MatftTests/FFTTest.swift index 1f7fe82..55313c2 100644 --- a/Tests/MatftTests/FFTTest.swift +++ b/Tests/MatftTests/FFTTest.swift @@ -1,7 +1,8 @@ +// Execution disabled for WASI until we support complex operations +#if !os(WASI) import XCTest -//@testable import Matft -import Matft -import Accelerate + +@testable import Matft final class FFTTests: XCTestCase { func testrfft() { @@ -92,3 +93,4 @@ final class FFTTests: XCTestCase { } } } +#endif diff --git a/Tests/MatftTests/FileTest.swift b/Tests/MatftTests/FileTest.swift index 12a29b2..7be2ed1 100644 --- a/Tests/MatftTests/FileTest.swift +++ b/Tests/MatftTests/FileTest.swift @@ -1,7 +1,7 @@ +#if !os(WASI) import XCTest -//@testable import Matft -import Matft +@testable import Matft final class FileTests: XCTestCase { var fileurl: URL! @@ -159,3 +159,4 @@ final class FileTests: XCTestCase { } } } +#endif diff --git a/Tests/MatftTests/InterpolationTest.swift b/Tests/MatftTests/InterpolationTest.swift index e417bf5..90b3e3d 100644 --- a/Tests/MatftTests/InterpolationTest.swift +++ b/Tests/MatftTests/InterpolationTest.swift @@ -1,6 +1,8 @@ +// Temporally disabled until we are able to backport this functionality to WASM +#if !os(WASI) import XCTest -//@testable import Matft -import Matft + +@testable import Matft final class InterpolationTests: XCTestCase { @@ -28,6 +30,6 @@ final class InterpolationTests: XCTestCase { } - } +#endif diff --git a/Tests/MatftTests/LinAlgTest.swift b/Tests/MatftTests/LinAlgTest.swift index 6a4a161..962c523 100644 --- a/Tests/MatftTests/LinAlgTest.swift +++ b/Tests/MatftTests/LinAlgTest.swift @@ -1,6 +1,8 @@ +// Disabled temporally until we provide better WASM support +#if !os(WASI) import XCTest -//@testable import Matft -import Matft + +@testable import Matft final class LinAlgTests: XCTestCase { @@ -348,3 +350,4 @@ final class LinAlgTests: XCTestCase { } } } +#endif diff --git a/Tests/MatftTests/MatftTests.swift b/Tests/MatftTests/MatftTests.swift index 62f8a03..77b2056 100644 --- a/Tests/MatftTests/MatftTests.swift +++ b/Tests/MatftTests/MatftTests.swift @@ -1,5 +1,6 @@ import XCTest -import Matft + +@testable import Matft final class MatftTests: XCTestCase { func testExample() { @@ -18,7 +19,9 @@ final class MatftTests: XCTestCase { //view9() //view10() //type() + #if canImport(Accelerate) image() + #endif /* let a = MfArray([[2, 1, -3, 0], [3, 1, 4, -5]], mftype: .Double, mforder: .Column) @@ -141,6 +144,7 @@ func view7(){ } +#if canImport(Accelerate) func view8(){ let coef = MfArray([[3,2],[1,2]]) let b = MfArray([7,1]) @@ -153,6 +157,7 @@ func view8(){ print(ainv) print(a*&ainv) } +#endif func view9(){ @@ -186,8 +191,10 @@ func type(){ print(b) } +#if canImport(Accelerate) func image(){ let a = Matft.nums(Float(0), shape: [150, 150, 4]) Matft.image.warpAffine(a, matrix: MfArray([[1, 0, 0], [0, 1, 0]] as [[Float]]), width: 150, height: 150) } +#endif diff --git a/Tests/MatftTests/MathTest.swift b/Tests/MatftTests/MathTest.swift index 1567f39..ce12928 100644 --- a/Tests/MatftTests/MathTest.swift +++ b/Tests/MatftTests/MathTest.swift @@ -267,7 +267,7 @@ final class MathTests: XCTestCase { [196, 225]]]], mftype: .Float)) } } - + func testArctan2(){ do { let x = MfArray([-1, +1, +1, -1]) diff --git a/Tests/MatftTests/SubscriptTest.swift b/Tests/MatftTests/SubscriptTest.swift index b07df84..7f6517b 100644 --- a/Tests/MatftTests/SubscriptTest.swift +++ b/Tests/MatftTests/SubscriptTest.swift @@ -1,6 +1,6 @@ import XCTest -//@testable import Matft -import Matft + +@testable import Matft final class SubscriptTests: XCTestCase { @@ -538,6 +538,8 @@ final class SubscriptTests: XCTestCase { [ 3, 2], [14, 3]]])) } + // Disabled temporally until we support complex operations in WASM + #if canImport(Accelerate) do{ let a = Matft.arange(start: 0, to: 16, by: 1).reshape([2,4,2]) a[Matft.all, 2, Matft.all] = MfArray(real: MfArray([3]), imag: MfArray([-1])) @@ -564,6 +566,7 @@ final class SubscriptTests: XCTestCase { XCTAssertEqual(a, MfArray(real: ans_real, imag: ans_imag)) } + #endif } func testBooleanIndexingGet(){ @@ -649,8 +652,14 @@ final class SubscriptTests: XCTestCase { } do{ - let a = Matft.arange(start: 0, to: 160000, by: 1, shape: [400, 400]) - let c = MfArray([true]).broadcast_to(shape: [400, 400]) + // Use smaller array size on WASM to avoid memory pressure + #if os(WASI) + let size = 10 + #else + let size = 400 + #endif + let a = Matft.arange(start: 0, to: size * size, by: 1, shape: [size, size]) + let c = MfArray([true]).broadcast_to(shape: [size, size]) /* self.measure { a[c] = MfArray([555]) @@ -658,7 +667,7 @@ final class SubscriptTests: XCTestCase { // average: 0.005, relative standard deviation: 8.334%, values: [0.006032, 0.004685, 0.004630, 0.005293, 0.004738, 0.004789, 0.004905, 0.005056, 0.004624, 0.004729], }*/ a[c] = MfArray([555]) - XCTAssertEqual(a, MfArray([555]).broadcast_to(shape: [400, 400])) + XCTAssertEqual(a, MfArray([555]).broadcast_to(shape: [size, size])) } @@ -816,9 +825,9 @@ final class SubscriptTests: XCTestCase { } } - + func testFancyIndexGet2(){ - + do{ let a = Matft.arange(start: 0, to: 16, by: 1).reshape([2,2,2,2]) @@ -887,7 +896,7 @@ final class SubscriptTests: XCTestCase { [ 6, 7]]]])) } } - + func testFancyIndexingSet(){ do{ From d2d85dd22771cf9ea8160c3e5dbaf4744c401290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:10:45 +0100 Subject: [PATCH 13/26] Disable complex tests --- Tests/MatftTests/ComplexTest.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/MatftTests/ComplexTest.swift b/Tests/MatftTests/ComplexTest.swift index 897b5cf..a84fe5d 100644 --- a/Tests/MatftTests/ComplexTest.swift +++ b/Tests/MatftTests/ComplexTest.swift @@ -1,3 +1,5 @@ +// Test execution disabled for WASM until we support complex operations +#if canImport(Accelerate) // // ComplexTest.swift // @@ -311,3 +313,4 @@ final class ComplexTests: XCTestCase { } } } +#endif From 27f3c4b7e9262541d2949048705c939c38aa67e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:10:58 +0100 Subject: [PATCH 14/26] Improve import usage --- Tests/MatftTests/RandomTest.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/MatftTests/RandomTest.swift b/Tests/MatftTests/RandomTest.swift index d916f0d..7bc8f1b 100644 --- a/Tests/MatftTests/RandomTest.swift +++ b/Tests/MatftTests/RandomTest.swift @@ -6,8 +6,8 @@ // import XCTest -//@testable import Matft -import Matft + +@testable import Matft final class RandomTests: XCTestCase { func testRand() { From bda0fe7813abae9ad3e4296ffcecf42c3d28eb1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:12:30 +0100 Subject: [PATCH 15/26] Use the right param names --- Sources/Matft/function/static/math+static.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Matft/function/static/math+static.swift b/Sources/Matft/function/static/math+static.swift index e447970..663c0da 100644 --- a/Sources/Matft/function/static/math+static.swift +++ b/Sources/Matft/function/static/math+static.swift @@ -1002,7 +1002,7 @@ extension Matft.math { } } - public static func arctan2(_ mfarrayY: MfArray, _ mfarrayX: MfArray) -> MfArray { + public static func arctan2(x1 mfarrayY: MfArray, x2 mfarrayX: MfArray) -> MfArray { // arctan2 not in our vForce fallback, use element-wise atan2 unsupport_complex(mfarrayY) unsupport_complex(mfarrayX) From c1787d70824822dee6bf473bbf8d19a45febfa28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:12:57 +0100 Subject: [PATCH 16/26] Add fallback implementations with fatal errors until we are able to implement this part --- .../Matft/function/static/biop+static.swift | 52 +- .../function/static/conversion+static.swift | 27 + Sources/Matft/library/lapack.swift | 490 +++++++++++++++++- 3 files changed, 567 insertions(+), 2 deletions(-) diff --git a/Sources/Matft/function/static/biop+static.swift b/Sources/Matft/function/static/biop+static.swift index 9355281..93630a1 100644 --- a/Sources/Matft/function/static/biop+static.swift +++ b/Sources/Matft/function/static/biop+static.swift @@ -7,7 +7,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif extension Matft{ //infix @@ -29,15 +31,19 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(rettype){ case .Float: return biopzvv_by_vDSP(l_mfarray, r_mfarray, vDSP_func: vDSP_zvadd) case .Double: return biopzvv_by_vDSP(l_mfarray, r_mfarray, vDSP_func: vDSP_zvaddD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } - + /** Element-wise addition of mfarray and scalar - parameters: @@ -62,12 +68,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(retmftype) { case .Float: return biopzvs_by_vDSP(l_mfarray, Float.from(r_scalar), vDSP_zrvadd) case .Double: return biopzvs_by_vDSP(l_mfarray, Double.from(r_scalar), vDSP_zrvaddD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -94,12 +104,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(retmftype) { case .Float: return biopzvs_by_vDSP(r_mfarray, Float.from(l_scalar), vDSP_zrvadd) case .Double: return biopzvs_by_vDSP(r_mfarray, Double.from(l_scalar), vDSP_zrvaddD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -120,12 +134,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(rettype){ case .Float: return biopzvv_by_vDSP(l_mfarray, r_mfarray, vDSP_func: vDSP_zvsub) case .Double: return biopzvv_by_vDSP(l_mfarray, r_mfarray, vDSP_func: vDSP_zvsubD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -152,12 +170,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(retmftype) { case .Float: return biopzvs_by_vDSP(l_mfarray, Float.from(r_scalar), vDSP_zrvsub) case .Double: return biopzvs_by_vDSP(l_mfarray, Double.from(r_scalar), vDSP_zrvsubD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -184,12 +206,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(retmftype) { case .Float: return biopzvs_by_vDSP(-r_mfarray, Float.from(l_scalar), vDSP_zrvadd) case .Double: return biopzvs_by_vDSP(-r_mfarray, Double.from(l_scalar), vDSP_zrvaddD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -210,12 +236,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(rettype){ case .Float: return biopzvv_by_vDSP(l_mfarray, r_mfarray, vDSP_func: vDSP_zvmul_) case .Double: return biopzvv_by_vDSP(l_mfarray, r_mfarray, vDSP_func: vDSP_zvmulD_) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -242,12 +272,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(retmftype) { case .Float: return biopzvs_by_vDSP(l_mfarray, Float.from(r_scalar), vDSP_zrvmul) case .Double: return biopzvs_by_vDSP(l_mfarray, Double.from(r_scalar), vDSP_zrvmulD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -274,12 +308,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(retmftype) { case .Float: return biopzvs_by_vDSP(r_mfarray, Float.from(l_scalar), vDSP_zrvmul) case .Double: return biopzvs_by_vDSP(r_mfarray, Double.from(l_scalar), vDSP_zrvmulD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -302,12 +340,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(rettype){ case .Float: return biopzvv_by_vDSP(l_mfarray, r_mfarray, vDSP_func: vDSP_zvdiv) case .Double: return biopzvv_by_vDSP(l_mfarray, r_mfarray, vDSP_func: vDSP_zvdivD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -334,12 +376,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(retmftype) { case .Float: return biopzvs_by_vDSP(l_mfarray, Float.from(r_scalar), vDSP_zrvdiv) case .Double: return biopzvs_by_vDSP(l_mfarray, Double.from(r_scalar), vDSP_zrvdivD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } /** @@ -366,12 +412,16 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch MfType.storedType(retmftype) { case .Float: return biopzsv_by_vDSP(Float.from(l_scalar), r_mfarray, vDSP_ztrans) case .Double: return biopzsv_by_vDSP(Double.from(l_scalar), r_mfarray, vDSP_ztransD) } + #else + fatalError("Complex array operations are not supported on this platform") + #endif } } diff --git a/Sources/Matft/function/static/conversion+static.swift b/Sources/Matft/function/static/conversion+static.swift index c53a2a8..4f97a2c 100644 --- a/Sources/Matft/function/static/conversion+static.swift +++ b/Sources/Matft/function/static/conversion+static.swift @@ -7,7 +7,9 @@ // import Foundation +#if canImport(Accelerate) import Accelerate +#endif import Collections extension Matft{ @@ -45,6 +47,7 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch newStoredType{ case .Float://double to float return zcontiguous_and_astype_by_vDSP(mfarray, mftype: mftype, mforder: mforder, src_type: DSPDoubleSplitComplex.self, dst_type: DSPSplitComplex.self, vDSP_func: vDSP_vdpsp) @@ -52,6 +55,10 @@ extension Matft{ case .Double://float to double return zcontiguous_and_astype_by_vDSP(mfarray, mftype: mftype, mforder: mforder, src_type: DSPSplitComplex.self, dst_type: DSPDoubleSplitComplex.self, vDSP_func: vDSP_vspdp) } + #else + // Fallback for WASI: convert complex arrays element by element + return _zcontiguous_and_astype_fallback(mfarray, mftype: mftype, mforder: mforder) + #endif } } /** @@ -298,12 +305,17 @@ extension Matft{ } } else{ + #if canImport(Accelerate) switch mfarray.storedType{ case .Float: return zcontiguous_by_vDSP(mfarray, vDSP_zvmov, mforder: mforder) case .Double: return zcontiguous_by_vDSP(mfarray, vDSP_zvmovD, mforder: mforder) } + #else + // Fallback for WASI: copy complex arrays element by element + return _zcontiguous_fallback(mfarray, mforder: mforder) + #endif } } /** @@ -604,7 +616,22 @@ fileprivate func _unique(_ flattendata: inout [T], restShape: ino } +// MARK: - WASI Fallbacks for complex array operations +#if !canImport(Accelerate) +/// Fallback for zcontiguous_and_astype_by_vDSP on WASI +/// Complex array type conversion is not supported on WASI +internal func _zcontiguous_and_astype_fallback(_ mfarray: MfArray, mftype: MfType, mforder: MfOrder) -> MfArray { + fatalError("Complex array type conversion is not supported on this platform (requires Accelerate framework)") +} + +/// Fallback for zcontiguous_by_vDSP on WASI +/// Complex array contiguous conversion is not supported on WASI +internal func _zcontiguous_fallback(_ mfarray: MfArray, mforder: MfOrder) -> MfArray { + fatalError("Complex array operations are not supported on this platform (requires Accelerate framework)") +} + +#endif /* diff --git a/Sources/Matft/library/lapack.swift b/Sources/Matft/library/lapack.swift index 226c312..7bd6278 100644 --- a/Sources/Matft/library/lapack.swift +++ b/Sources/Matft/library/lapack.swift @@ -7,6 +7,7 @@ // import Foundation +#if canImport(Accelerate) import Accelerate public typealias lapack_solve_func = (UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>) -> Int32 @@ -707,6 +708,493 @@ internal func svd_by_lapack(_ mfarray: MfArray, _ full_matrices: let v = MfArray(mfdata: v_data, mfstructure: MfStructure(shape: v_shape, mforder: .Row)) let s = MfArray(mfdata: s_data, mfstructure: MfStructure(shape: s_shape, mforder: .Row)) let rt = MfArray(mfdata: rt_data, mfstructure: MfStructure(shape: rt_shape, mforder: .Row)) - + + return (v.swapaxes(axis1: -1, axis2: -2), s, rt.swapaxes(axis1: -1, axis2: -2)) +} +#else +// MARK: - WASI Implementation using CLAPACK +// Note: The CLAPACK eigen-support branch only provides dgetrf and dgetri (double-precision). +// Other LAPACK operations will throw fatalError on WASI. +import CLAPACK + +public typealias __CLPK_integer = Int32 + +public typealias lapack_solve_func = (UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>) -> Int32 + +public typealias lapack_LU_func = (UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>) -> Int32 + +public typealias lapack_inv_func = (UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>) -> Int32 + +public typealias lapack_eigen_func = (UnsafeMutablePointer, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>) -> Int32 + +public typealias lapack_svd_func = (UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>,UnsafeMutablePointer<__CLPK_integer>) -> Int32 + +internal typealias lapack_LU = (UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>) -> Int32 + +internal typealias lapack_inv = (UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer, UnsafeMutablePointer<__CLPK_integer>, UnsafeMutablePointer<__CLPK_integer>) -> Int32 + +// MARK: - CLAPACK Wrapper Functions for WASI +// Only dgetrf_ and dgetri_ are available in the CLAPACK eigen-support branch + +@inline(__always) +internal func sgesv_(_ n: UnsafeMutablePointer<__CLPK_integer>, _ nrhs: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ ipiv: UnsafeMutablePointer<__CLPK_integer>, _ b: UnsafeMutablePointer, _ ldb: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + fatalError("LAPACK sgesv_ is not available on WASI (single-precision linear solve not supported)") +} + +@inline(__always) +internal func dgesv_(_ n: UnsafeMutablePointer<__CLPK_integer>, _ nrhs: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ ipiv: UnsafeMutablePointer<__CLPK_integer>, _ b: UnsafeMutablePointer, _ ldb: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + fatalError("LAPACK dgesv_ is not available on WASI (double-precision linear solve not supported)") +} + +@inline(__always) +internal func sgetrf_(_ m: UnsafeMutablePointer<__CLPK_integer>, _ n: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ ipiv: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + fatalError("LAPACK sgetrf_ is not available on WASI (single-precision LU decomposition not supported)") +} + +@inline(__always) +internal func dgetrf_(_ m: UnsafeMutablePointer<__CLPK_integer>, _ n: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ ipiv: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + var mLong = CLong(m.pointee) + var nLong = CLong(n.pointee) + var ldaLong = CLong(lda.pointee) + var infoLong: CLong = 0 + let minMN = min(Int(m.pointee), Int(n.pointee)) + var ipivLong = Array(repeating: 0, count: minMN) + + CLAPACK.dgetrf_(&mLong, &nLong, a, &ldaLong, &ipivLong, &infoLong) + + for i in 0.., _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ ipiv: UnsafeMutablePointer<__CLPK_integer>, _ work: UnsafeMutablePointer, _ lwork: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + fatalError("LAPACK sgetri_ is not available on WASI (single-precision matrix inverse not supported)") +} + +@inline(__always) +internal func dgetri_(_ n: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ ipiv: UnsafeMutablePointer<__CLPK_integer>, _ work: UnsafeMutablePointer, _ lwork: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + var nLong = CLong(n.pointee) + var ldaLong = CLong(lda.pointee) + var lworkLong = CLong(lwork.pointee) + var infoLong: CLong = 0 + var ipivLong = Array(repeating: 0, count: Int(n.pointee)) + for i in 0.., _ jobvr: UnsafeMutablePointer, _ n: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ wr: UnsafeMutablePointer, _ wi: UnsafeMutablePointer, _ vl: UnsafeMutablePointer, _ ldvl: UnsafeMutablePointer<__CLPK_integer>, _ vr: UnsafeMutablePointer, _ ldvr: UnsafeMutablePointer<__CLPK_integer>, _ work: UnsafeMutablePointer, _ lwork: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + fatalError("LAPACK sgeev_ is not available on WASI (single-precision eigenvalue decomposition not supported)") +} + +@inline(__always) +internal func dgeev_(_ jobvl: UnsafeMutablePointer, _ jobvr: UnsafeMutablePointer, _ n: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ wr: UnsafeMutablePointer, _ wi: UnsafeMutablePointer, _ vl: UnsafeMutablePointer, _ ldvl: UnsafeMutablePointer<__CLPK_integer>, _ vr: UnsafeMutablePointer, _ ldvr: UnsafeMutablePointer<__CLPK_integer>, _ work: UnsafeMutablePointer, _ lwork: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + fatalError("LAPACK dgeev_ is not available on WASI (double-precision eigenvalue decomposition not supported)") +} + +@inline(__always) +internal func sgesdd_(_ jobz: UnsafeMutablePointer, _ m: UnsafeMutablePointer<__CLPK_integer>, _ n: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ s: UnsafeMutablePointer, _ u: UnsafeMutablePointer, _ ldu: UnsafeMutablePointer<__CLPK_integer>, _ vt: UnsafeMutablePointer, _ ldvt: UnsafeMutablePointer<__CLPK_integer>, _ work: UnsafeMutablePointer, _ lwork: UnsafeMutablePointer<__CLPK_integer>, _ iwork: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + fatalError("LAPACK sgesdd_ is not available on WASI (single-precision SVD not supported)") +} + +@inline(__always) +internal func dgesdd_(_ jobz: UnsafeMutablePointer, _ m: UnsafeMutablePointer<__CLPK_integer>, _ n: UnsafeMutablePointer<__CLPK_integer>, _ a: UnsafeMutablePointer, _ lda: UnsafeMutablePointer<__CLPK_integer>, _ s: UnsafeMutablePointer, _ u: UnsafeMutablePointer, _ ldu: UnsafeMutablePointer<__CLPK_integer>, _ vt: UnsafeMutablePointer, _ ldvt: UnsafeMutablePointer<__CLPK_integer>, _ work: UnsafeMutablePointer, _ lwork: UnsafeMutablePointer<__CLPK_integer>, _ iwork: UnsafeMutablePointer<__CLPK_integer>, _ info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 { + fatalError("LAPACK dgesdd_ is not available on WASI (double-precision SVD not supported)") +} + +// MARK: - LAPACK Wrapper Functions for WASI + +@inline(__always) +internal func wrap_lapack_solve(_ rownum: Int, _ colnum: Int, _ coef_ptr: UnsafeMutablePointer, _ dst_b_ptr: UnsafeMutablePointer, lapack_func: lapack_solve_func) throws { + var N = __CLPK_integer(rownum) + var LDA = __CLPK_integer(rownum) + var NRHS = __CLPK_integer(colnum) + var LDB = __CLPK_integer(rownum) + var IPIV = Array<__CLPK_integer>(repeating: 0, count: rownum) + var INFO: __CLPK_integer = 0 + + let _ = lapack_func(&N, &NRHS, coef_ptr, &LDA, &IPIV, dst_b_ptr, &LDB, &INFO) + + if INFO < 0 { + throw MfError.LinAlgError.factorizationError("Illegal value found: \(-INFO)th argument") + } + else if INFO > 0 { + throw MfError.LinAlgError.singularMatrix("The factorization has been completed, but the factor U(of A=PLU) is exactly singular, so the solution could not be computed.") + } +} + +@inline(__always) +internal func wrap_lapack_LU(_ rownum: Int, _ colnum: Int, _ srcdstptr: UnsafeMutablePointer, lapack_func: lapack_LU_func) throws -> [__CLPK_integer] { + var M = __CLPK_integer(rownum) + var N = __CLPK_integer(colnum) + var LDA = __CLPK_integer(rownum) + var IPIV = Array<__CLPK_integer>(repeating: 0, count: min(rownum, colnum)) + var INFO: __CLPK_integer = 0 + + let _ = lapack_func(&M, &N, srcdstptr, &LDA, &IPIV, &INFO) + + if INFO < 0 { + throw MfError.LinAlgError.factorizationError("Illegal value found: \(-INFO)th argument") + } + else if INFO > 0 { + throw MfError.LinAlgError.singularMatrix("The factorization has been completed, but the factor U(of A=PLU) is exactly singular, so the solution could not be computed.") + } + + return IPIV +} + +@inline(__always) +internal func wrap_lapack_inv(_ rowcolnum: Int, _ srcdstptr: UnsafeMutablePointer, _ IPIV: UnsafeMutablePointer<__CLPK_integer>, lapack_func: lapack_inv_func) throws { + var N = __CLPK_integer(rowcolnum) + var LDA = __CLPK_integer(rowcolnum) + var INFO: __CLPK_integer = 0 + var WORK = Array(repeating: T.zero, count: rowcolnum) + var LWORK = __CLPK_integer(rowcolnum) + + let _ = lapack_func(&N, srcdstptr, &LDA, IPIV, &WORK, &LWORK, &INFO) + + if INFO < 0 { + throw MfError.LinAlgError.factorizationError("Illegal value found: \(-INFO)th argument") + } + else if INFO > 0 { + throw MfError.LinAlgError.singularMatrix("The factorization has been completed, but the factor U(of A=PLU) is exactly singular, so the solution could not be computed.") + } +} + +@inline(__always) +internal func wrap_lapack_eigen(_ rowcolnum: Int, _ srcptr: UnsafeMutablePointer, _ dstLVecRePtr: UnsafeMutablePointer, _ dstLVecImPtr: UnsafeMutablePointer, _ dstRVecRePtr: UnsafeMutablePointer, _ dstRVecImPtr: UnsafeMutablePointer, _ dstValRePtr: UnsafeMutablePointer, _ dstValImPtr: UnsafeMutablePointer, lapack_func: lapack_eigen_func) throws { + let JOBVL = UnsafeMutablePointer(mutating: ("V" as NSString).utf8String)! + let JOBVR = UnsafeMutablePointer(mutating: ("V" as NSString).utf8String)! + + var N = __CLPK_integer(rowcolnum) + var LDA = __CLPK_integer(rowcolnum) + var WR = Array(repeating: T.zero, count: rowcolnum) + var WI = Array(repeating: T.zero, count: rowcolnum) + var VL = Array(repeating: T.zero, count: rowcolnum*rowcolnum) + var LDVL = __CLPK_integer(rowcolnum) + var VR = Array(repeating: T.zero, count: rowcolnum*rowcolnum) + var LDVR = __CLPK_integer(rowcolnum) + var WORKQ = T.zero + var LWORK = __CLPK_integer(-1) + var INFO: __CLPK_integer = 0 + + let _ = lapack_func(JOBVL, JOBVR, &N, srcptr, &LDA, &WR, &WI, &VL, &LDVL, &VR, &LDVR, &WORKQ, &LWORK, &INFO) + + var WORK = Array(repeating: T.zero, count: T.toInt(WORKQ)) + LWORK = __CLPK_integer(T.toInt(WORKQ)) + let _ = lapack_func(JOBVL, JOBVR, &N, srcptr, &LDA, &WR, &WI, &VL, &LDVL, &VR, &LDVR, &WORK, &LWORK, &INFO) + + if INFO < 0 { + throw MfError.LinAlgError.factorizationError("Illegal value found: \(-INFO)th argument") + } + else if INFO > 0 { + throw MfError.LinAlgError.notConverge("the QR algorithm failed to compute all the eigenvalues, and no eigenvectors have been computed; elements \(INFO)+1:N of WR and WI contain eigenvalues which have converged.") + } + else { + var VLRe = Array(repeating: T.zero, count: rowcolnum*rowcolnum) + var VLIm = Array(repeating: T.zero, count: rowcolnum*rowcolnum) + var VRRe = Array(repeating: T.zero, count: rowcolnum*rowcolnum) + var VRIm = Array(repeating: T.zero, count: rowcolnum*rowcolnum) + for k in 0..(_ rownum: Int, _ colnum: Int, _ srcptr: UnsafeMutablePointer, _ vptr: UnsafeMutablePointer, _ sptr: UnsafeMutablePointer, _ rtptr: UnsafeMutablePointer, _ full_matrices: Bool, lapack_func: lapack_svd_func) throws { + let JOBZ: UnsafeMutablePointer + var M = __CLPK_integer(rownum) + var N = __CLPK_integer(colnum) + let ucol: Int, vtrow: Int + var LDA = __CLPK_integer(rownum) + let snum = min(rownum, colnum) + var S = Array(repeating: T.zero, count: snum) + var U: Array + var LDU = __CLPK_integer(rownum) + var VT: Array + var LDVT: __CLPK_integer + + if full_matrices { + JOBZ = UnsafeMutablePointer(mutating: ("A" as NSString).utf8String)! + LDVT = __CLPK_integer(colnum) + ucol = rownum + vtrow = colnum + } + else { + JOBZ = UnsafeMutablePointer(mutating: ("S" as NSString).utf8String)! + LDVT = __CLPK_integer(snum) + ucol = snum + vtrow = snum + } + U = Array(repeating: T.zero, count: rownum*ucol) + VT = Array(repeating: T.zero, count: colnum*vtrow) + + var WORKQ = T.zero + var LWORK = __CLPK_integer(-1) + var IWORK = Array<__CLPK_integer>(repeating: 0, count: 8*snum) + var INFO: __CLPK_integer = 0 + + let _ = lapack_func(JOBZ, &M, &N, srcptr, &LDA, &S, &U, &LDU, &VT, &LDVT, &WORKQ, &LWORK, &IWORK, &INFO) + + var WORK = Array(repeating: T.zero, count: T.toInt(WORKQ)) + LWORK = __CLPK_integer(T.toInt(WORKQ)) + let _ = lapack_func(JOBZ, &M, &N, srcptr, &LDA, &S, &U, &LDU, &VT, &LDVT, &WORK, &LWORK, &IWORK, &INFO) + + if INFO < 0 { + throw MfError.LinAlgError.factorizationError("Illegal value found: \(-INFO)th argument") + } + else if INFO > 0 { + throw MfError.LinAlgError.notConverge("the QR algorithm failed to compute all the eigenvalues, and no eigenvectors have been computed; elements \(INFO)+1:N of WR and WI contain eigenvalues which have converged.") + } + else { + U.withUnsafeMutableBufferPointer { + vptr.moveUpdate(from: $0.baseAddress!, count: rownum*ucol) + } + S.withUnsafeMutableBufferPointer { + sptr.moveUpdate(from: $0.baseAddress!, count: snum) + } + VT.withUnsafeMutableBufferPointer { + rtptr.moveUpdate(from: $0.baseAddress!, count: colnum*vtrow) + } + } +} + +// MARK: - LAPACK High-Level Functions for WASI + +internal func solve_by_lapack(_ coef: MfArray, _ b: MfArray, ret_mftype: MfType, _ lapack_func: lapack_solve_func) throws -> MfArray { + precondition((coef.ndim == 2), "cannot solve non linear simultaneous equations") + precondition(b.ndim <= 2, "Invalid b. Dimension must be 1 or 2") + + let coef_shape = coef.shape + let b_shape = b.shape + var dst_colnum = 0 + let dst_rownum = coef_shape[0] + + if b.ndim == 1 { + precondition((coef_shape[0] == coef_shape[1] && b_shape[0] == coef_shape[0]), "cannot solve (\(coef_shape[0]),\(coef_shape[1]))(\(b_shape[0]))=(\(b_shape[0])) problem") + dst_colnum = 1 + } + else { + precondition((coef_shape[0] == coef_shape[1] && b_shape[0] == coef_shape[0]), "cannot solve (\(coef_shape[0]),\(coef_shape[1]))(\(b_shape[0]),\(b_shape[1]))=(\(b_shape[0]),\(b_shape[1])) problem") + dst_colnum = b_shape[1] + } + + let coef_column_major = coef.astype(ret_mftype, mforder: .Column) + let ret = b.astype(ret_mftype, mforder: .Column) + + try coef_column_major.withUnsafeMutableStartPointer(datatype: T.self) { + coef_ptr in + try ret.withUnsafeMutableStartPointer(datatype: T.self) { + dst_ptr in + try wrap_lapack_solve(dst_rownum, dst_colnum, coef_ptr, dst_ptr, lapack_func: lapack_func) + } + } + + return ret +} + +internal func inv_by_lapack(_ mfarray: MfArray, _ lapack_func_lu: lapack_LU, _ lapack_func_inv: lapack_inv, _ retMfType: MfType) throws -> MfArray { + let shape = mfarray.shape + + precondition(mfarray.ndim > 1, "cannot get an inverse matrix from 1-d mfarray") + precondition(shape[mfarray.ndim - 1] == shape[mfarray.ndim - 2], "Last 2 dimensions of the mfarray must be square") + + let newdata = MfData(size: mfarray.size, mftype: mfarray.storedType.to_mftype()) + try newdata.withUnsafeMutableStartPointer(datatype: T.self) { + dstptrT in + try mfarray.withMNStackedMajorPointer(datatype: T.self, mforder: .Row) { + srcptr, row, col, offset in + var IPIV = try wrap_lapack_LU(row, col, srcptr, lapack_func: lapack_func_lu) + try wrap_lapack_inv(row, srcptr, &IPIV, lapack_func: lapack_func_inv) + (dstptrT + offset).moveUpdate(from: srcptr, count: row*col) + } + } + + let newstructure = MfStructure(shape: shape, mforder: .Row) + return MfArray(mfdata: newdata, mfstructure: newstructure) +} + +internal func det_by_lapack(_ mfarray: MfArray, _ lapack_func: lapack_LU_func) throws -> MfArray { + let shape = mfarray.shape + + precondition(mfarray.ndim > 1, "cannot get a determinant from 1-d mfarray") + precondition(shape[mfarray.ndim - 1] == shape[mfarray.ndim - 2], "Last 2 dimensions of the mfarray must be square") + + let ret_size = mfarray.size / (shape[mfarray.ndim - 1] * shape[mfarray.ndim - 1]) + + let newdata = MfData(size: ret_size, mftype: mfarray.mftype) + var dst_offset = 0 + + try newdata.withUnsafeMutableStartPointer(datatype: T.self) { + dstptrT in + try mfarray.withMNStackedMajorPointer(datatype: T.self, mforder: .Row) { + srcptr, row, col, offset in + let square_num = row + let IPIV = try wrap_lapack_LU(row, col, srcptr, lapack_func: lapack_func) + var det = T.from(1) + for i in 0..(_ mfarray: MfArray, _ lapack_func: lapack_eigen_func) throws -> (valRe: MfArray, valIm: MfArray, lvecRe: MfArray, lvecIm: MfArray, rvecRe: MfArray, rvecIm: MfArray) { + var shape = mfarray.shape + precondition(mfarray.ndim > 1, "cannot get an inverse matrix from 1-d mfarray") + precondition(shape[mfarray.ndim - 1] == shape[mfarray.ndim - 2], "Last 2 dimensions of the mfarray must be square") + let retMfType = mfarray.storedType.to_mftype() + let eigenvec_size = shape2size(&shape) + + let lvecRe_data = MfData(size: eigenvec_size, mftype: retMfType) + let lvecIm_data = MfData(size: eigenvec_size, mftype: retMfType) + let rvecRe_data = MfData(size: eigenvec_size, mftype: retMfType) + let rvecIm_data = MfData(size: eigenvec_size, mftype: retMfType) + + let eigenval_shape = Array(shape.prefix(mfarray.ndim - 1)) + let valRe_data = MfData(size: eigenvec_size, mftype: retMfType) + let valIm_data = MfData(size: eigenvec_size, mftype: retMfType) + var vec_offset = 0 + var val_offset = 0 + + let lvecRe_ptr = lvecRe_data.data_real.bindMemory(to: T.self, capacity: eigenvec_size) + let lvecIm_ptr = lvecIm_data.data_real.bindMemory(to: T.self, capacity: eigenvec_size) + let rvecRe_ptr = rvecRe_data.data_real.bindMemory(to: T.self, capacity: eigenvec_size) + let rvecIm_ptr = rvecIm_data.data_real.bindMemory(to: T.self, capacity: eigenvec_size) + let valRe_ptr = valRe_data.data_real.bindMemory(to: T.self, capacity: eigenvec_size) + let valIm_ptr = valIm_data.data_real.bindMemory(to: T.self, capacity: eigenvec_size) + + try mfarray.withMNStackedMajorPointer(datatype: T.self, mforder: .Column) { + srcptr, row, col, offset in + let square_num = row + try wrap_lapack_eigen(square_num, srcptr, lvecRe_ptr + vec_offset, lvecIm_ptr + vec_offset, rvecRe_ptr + vec_offset, rvecIm_ptr + vec_offset, valRe_ptr + val_offset, valIm_ptr + val_offset, lapack_func: lapack_func) + val_offset += square_num + vec_offset += offset + } + + return (MfArray(mfdata: valRe_data, mfstructure: MfStructure(shape: eigenval_shape, mforder: .Row)), + MfArray(mfdata: valIm_data, mfstructure: MfStructure(shape: eigenval_shape, mforder: .Row)), + MfArray(mfdata: lvecRe_data, mfstructure: MfStructure(shape: shape, mforder: .Row)), + MfArray(mfdata: lvecIm_data, mfstructure: MfStructure(shape: shape, mforder: .Row)), + MfArray(mfdata: rvecRe_data, mfstructure: MfStructure(shape: shape, mforder: .Row)), + MfArray(mfdata: rvecIm_data, mfstructure: MfStructure(shape: shape, mforder: .Row))) +} + +internal func svd_by_lapack(_ mfarray: MfArray, _ full_matrices: Bool, _ lapack_func: lapack_svd_func) throws -> (v: MfArray, s: MfArray, rt: MfArray) { + let shape = mfarray.shape + let ret_mftype = mfarray.storedType.to_mftype() + let M = shape[mfarray.ndim - 2] + let N = shape[mfarray.ndim - 1] + let ssize = min(M, N) + let stacked_shape = Array(shape.prefix(mfarray.ndim - 2)) + + let v_data: MfData, s_data: MfData, rt_data: MfData + var v_shape: [Int], s_shape: [Int], rt_shape: [Int] + let vcol: Int, rtrow: Int + if full_matrices { + v_shape = stacked_shape + [M, M] + s_shape = stacked_shape + [ssize] + rt_shape = stacked_shape + [N, N] + v_data = MfData(size: shape2size(&v_shape), mftype: ret_mftype) + s_data = MfData(size: shape2size(&s_shape), mftype: ret_mftype) + rt_data = MfData(size: shape2size(&rt_shape), mftype: ret_mftype) + vcol = M + rtrow = N + } + else { + v_shape = stacked_shape + [ssize, M] + s_shape = stacked_shape + [ssize] + rt_shape = stacked_shape + [N, ssize] + v_data = MfData(size: shape2size(&v_shape), mftype: ret_mftype) + s_data = MfData(size: shape2size(&s_shape), mftype: ret_mftype) + rt_data = MfData(size: shape2size(&rt_shape), mftype: ret_mftype) + vcol = ssize + rtrow = ssize + } + + var v_offset = 0 + var s_offset = 0 + var rt_offset = 0 + + let vptr = v_data.data_real.bindMemory(to: T.self, capacity: shape2size(&v_shape)) + let sptr = s_data.data_real.bindMemory(to: T.self, capacity: shape2size(&s_shape)) + let rtptr = rt_data.data_real.bindMemory(to: T.self, capacity: shape2size(&rt_shape)) + + try mfarray.withMNStackedMajorPointer(datatype: T.self, mforder: .Column) { + srcptr, _, _, _ in + try wrap_lapack_svd(M, N, srcptr, vptr + v_offset, sptr + s_offset, rtptr + rt_offset, full_matrices, lapack_func: lapack_func) + v_offset += M*vcol + s_offset += ssize + rt_offset += N*rtrow + } + + let v = MfArray(mfdata: v_data, mfstructure: MfStructure(shape: v_shape, mforder: .Row)) + let s = MfArray(mfdata: s_data, mfstructure: MfStructure(shape: s_shape, mforder: .Row)) + let rt = MfArray(mfdata: rt_data, mfstructure: MfStructure(shape: rt_shape, mforder: .Row)) + return (v.swapaxes(axis1: -1, axis2: -2), s, rt.swapaxes(axis1: -1, axis2: -2)) } + +#endif // canImport(Accelerate) From 2c510f8f854e474f94db7d58b85a9a356a4289be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:17:00 +0100 Subject: [PATCH 17/26] Disable performance tests temporally --- Tests/MatftTests/WASIFallbackTests.swift | 4 ++-- Tests/PerformanceTests/ArithmeticPefTests.swift | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Tests/MatftTests/WASIFallbackTests.swift b/Tests/MatftTests/WASIFallbackTests.swift index 31275dd..143a81b 100644 --- a/Tests/MatftTests/WASIFallbackTests.swift +++ b/Tests/MatftTests/WASIFallbackTests.swift @@ -4,7 +4,7 @@ import Matft /// Tests for WASI fallback implementations /// These tests validate the pure Swift implementations that are used when Accelerate is not available (e.g., on WASI) /// The tests run on macOS to ensure the fallback logic is correct before deploying to WASI -/* +/* These tests were failing on CI only so we disabled them temporally final class WASIFallbackTests: XCTestCase { // MARK: - Type Conversion Tests @@ -466,4 +466,4 @@ final class WASIFallbackTests: XCTestCase { } } -*/ \ No newline at end of file +*/ diff --git a/Tests/PerformanceTests/ArithmeticPefTests.swift b/Tests/PerformanceTests/ArithmeticPefTests.swift index 750c8b1..17d97b0 100644 --- a/Tests/PerformanceTests/ArithmeticPefTests.swift +++ b/Tests/PerformanceTests/ArithmeticPefTests.swift @@ -1,3 +1,4 @@ +#if !OS(wasi) import XCTest //@testable import Matft import Matft @@ -51,4 +52,4 @@ final class ArithmeticPefTests: XCTestCase { } } } - +#endif From 381748752e13f817b8d329095b7682ded35d0ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:19:05 +0100 Subject: [PATCH 18/26] Update random tests to reduce memory pressure --- Tests/MatftTests/RandomTest.swift | 51 ++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/Tests/MatftTests/RandomTest.swift b/Tests/MatftTests/RandomTest.swift index 7bc8f1b..dacbec3 100644 --- a/Tests/MatftTests/RandomTest.swift +++ b/Tests/MatftTests/RandomTest.swift @@ -11,32 +11,53 @@ import XCTest final class RandomTests: XCTestCase { func testRand() { - let a = Matft.random.rand(shape: [100, 100, 100]) + // Use smaller array size on WASM to avoid memory pressure + // 100x100x100 = 1M elements = 4MB per array, too much for WASM + // Smaller sample size requires wider tolerance for mean (higher variance) + #if os(WASI) + let shape = [10, 10, 10] // 1K elements + let tolerance: Float = 0.10 // Wider bounds for smaller sample + #else + let shape = [100, 100, 100] // 1M elements + let tolerance: Float = 0.02 // Tight bounds for large sample + #endif + + let a = Matft.random.rand(shape: shape) let mean_a = a.mean().scalar as! Float - let cond_a = (Float(0.48) < mean_a) && (mean_a < Float(0.52)) - - let b = Matft.random.rand(shape: [100, 100, 100]) + let cond_a = (Float(0.5 - tolerance) < mean_a) && (mean_a < Float(0.5 + tolerance)) + + let b = Matft.random.rand(shape: shape) let mean_b = b.mean().scalar as! Float - let cond_b = (Float(0.48) < mean_b) && (mean_b < Float(0.52)) - + let cond_b = (Float(0.5 - tolerance) < mean_b) && (mean_b < Float(0.5 + tolerance)) + XCTAssertTrue(cond_a, "The result may be failure due to random generation") XCTAssertTrue(cond_b, "The result may be failure due to random generation") - + XCTAssertNotEqual(a, b,"The result may be failure due to random generation") } - + func testRandint() { - let a = Matft.random.randint(low: 0, high: 10, shape: [100, 100, 100]) + // Use smaller array size on WASM to avoid memory pressure + // Smaller sample size requires wider tolerance for mean (higher variance) + #if os(WASI) + let shape = [10, 10, 10] // 1K elements + let tolerance: Float = 0.50 // Wider bounds for smaller sample + #else + let shape = [100, 100, 100] // 1M elements + let tolerance: Float = 0.02 // Tight bounds for large sample + #endif + + let a = Matft.random.randint(low: 0, high: 10, shape: shape) let mean_a = a.mean().scalar as! Float - let cond_a = (Float(4.48) < mean_a) && (mean_a < Float(4.52)) - - let b = Matft.random.randint(low: 0, high: 10, shape: [100, 100, 100]) + let cond_a = (Float(4.5 - tolerance) < mean_a) && (mean_a < Float(4.5 + tolerance)) + + let b = Matft.random.randint(low: 0, high: 10, shape: shape) let mean_b = b.mean().scalar as! Float - let cond_b = (Float(4.48) < mean_b) && (mean_b < Float(4.52)) - + let cond_b = (Float(4.5 - tolerance) < mean_b) && (mean_b < Float(4.5 + tolerance)) + XCTAssertTrue(cond_a, "The result may be failure due to random generation") XCTAssertTrue(cond_b, "The result may be failure due to random generation") - + XCTAssertNotEqual(a, b,"The result may be failure due to random generation") } } From 732725332bddd6697e3f5e94b4025d1636d47b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:19:49 +0100 Subject: [PATCH 19/26] Fix import --- Tests/PerformanceTests/ArithmeticPefTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/PerformanceTests/ArithmeticPefTests.swift b/Tests/PerformanceTests/ArithmeticPefTests.swift index 17d97b0..df81538 100644 --- a/Tests/PerformanceTests/ArithmeticPefTests.swift +++ b/Tests/PerformanceTests/ArithmeticPefTests.swift @@ -1,4 +1,4 @@ -#if !OS(wasi) +#if !OS(WASI) import XCTest //@testable import Matft import Matft From 48bda80dadc9591685729938d533aec29df1355a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:23:40 +0100 Subject: [PATCH 20/26] Update wrong import --- Tests/PerformanceTests/ArithmeticPefTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/PerformanceTests/ArithmeticPefTests.swift b/Tests/PerformanceTests/ArithmeticPefTests.swift index df81538..cb66b00 100644 --- a/Tests/PerformanceTests/ArithmeticPefTests.swift +++ b/Tests/PerformanceTests/ArithmeticPefTests.swift @@ -1,4 +1,4 @@ -#if !OS(WASI) +#if !os(WASI) import XCTest //@testable import Matft import Matft From acd15f4881abba6447b09441e481849e9d9ca3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 14:29:14 +0100 Subject: [PATCH 21/26] Disable some other performance tests --- Tests/PerformanceTests/BoolPefTests.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tests/PerformanceTests/BoolPefTests.swift b/Tests/PerformanceTests/BoolPefTests.swift index 0e0da38..d00a624 100644 --- a/Tests/PerformanceTests/BoolPefTests.swift +++ b/Tests/PerformanceTests/BoolPefTests.swift @@ -5,9 +5,11 @@ // Created by Junnosuke Kado on 2021/05/05. // +// Performance tests for boolean operations disabled for WASM temporally +#if !os(WASI) import XCTest -//@testable import Matft -import Matft + +@testable import Matft final class BoolPefTests: XCTestCase { @@ -68,3 +70,4 @@ final class BoolPefTests: XCTestCase { } } +#endif \ No newline at end of file From 8e6d4f14d8f70468ba1ce3904e7f0804886ca9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 15:00:17 +0100 Subject: [PATCH 22/26] Enable back wasm fallback tests and revert a change made by mistake --- Sources/Matft/core/object/mfdata.swift | 2 +- Tests/MatftTests/WASIFallbackTests.swift | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/Matft/core/object/mfdata.swift b/Sources/Matft/core/object/mfdata.swift index 41a6796..de46163 100644 --- a/Sources/Matft/core/object/mfdata.swift +++ b/Sources/Matft/core/object/mfdata.swift @@ -71,7 +71,7 @@ public class MfData: MfDataProtocol{ case .Double: // dynamic allocation self.data_real = allocate_doubledata_from_flattenArray(&flatten_realArray, toBool: mftype == .Bool) - self.data_imag = allocate_doubledata_from_flattenArray(&flatten_imagArray, toBool: mftype == .Bool) + self.data_imag = allocate_floatdata_from_flattenArray(&flatten_imagArray, toBool: mftype == .Bool) } self.storedSize = flatten_realArray.count self.mftype = mftype diff --git a/Tests/MatftTests/WASIFallbackTests.swift b/Tests/MatftTests/WASIFallbackTests.swift index 143a81b..4afa154 100644 --- a/Tests/MatftTests/WASIFallbackTests.swift +++ b/Tests/MatftTests/WASIFallbackTests.swift @@ -4,7 +4,7 @@ import Matft /// Tests for WASI fallback implementations /// These tests validate the pure Swift implementations that are used when Accelerate is not available (e.g., on WASI) /// The tests run on macOS to ensure the fallback logic is correct before deploying to WASI -/* These tests were failing on CI only so we disabled them temporally + final class WASIFallbackTests: XCTestCase { // MARK: - Type Conversion Tests @@ -465,5 +465,3 @@ final class WASIFallbackTests: XCTestCase { XCTAssertEqual(scalar, 42) } } - -*/ From 31c679fbb8b890a5b35007e4db251199e5660934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 15:33:22 +0100 Subject: [PATCH 23/26] Update clapack pacakge and add some guards to our code to prevent tentative errors --- Package.resolved | 2 +- Package.swift | 2 +- Sources/Matft/core/object/mfdata.swift | 2 +- Sources/Matft/core/object/mfstructure.swift | 6 +++++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Package.resolved b/Package.resolved index becf2b5..737c379 100644 --- a/Package.resolved +++ b/Package.resolved @@ -3,7 +3,7 @@ { "identity" : "clapack", "kind" : "remoteSourceControl", - "location" : "https://github.com/GoodNotes/CLAPACK", + "location" : "git@github.com:GoodNotes/CLAPACK.git", "state" : { "branch" : "eigen-support", "revision" : "a9c8a67890a31af54ed55536b5dd606d6de609a9" diff --git a/Package.swift b/Package.swift index 4d260b5..0cd463b 100644 --- a/Package.swift +++ b/Package.swift @@ -12,7 +12,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), - .package(url: "https://github.com/GoodNotes/CLAPACK", branch: "eigen-support"), + .package(url: "git@github.com:GoodNotes/CLAPACK.git", branch: "eigen-support"), ], targets: [ .target( diff --git a/Sources/Matft/core/object/mfdata.swift b/Sources/Matft/core/object/mfdata.swift index de46163..41a6796 100644 --- a/Sources/Matft/core/object/mfdata.swift +++ b/Sources/Matft/core/object/mfdata.swift @@ -71,7 +71,7 @@ public class MfData: MfDataProtocol{ case .Double: // dynamic allocation self.data_real = allocate_doubledata_from_flattenArray(&flatten_realArray, toBool: mftype == .Bool) - self.data_imag = allocate_floatdata_from_flattenArray(&flatten_imagArray, toBool: mftype == .Bool) + self.data_imag = allocate_doubledata_from_flattenArray(&flatten_imagArray, toBool: mftype == .Bool) } self.storedSize = flatten_realArray.count self.mftype = mftype diff --git a/Sources/Matft/core/object/mfstructure.swift b/Sources/Matft/core/object/mfstructure.swift index b3e4dfe..94db827 100644 --- a/Sources/Matft/core/object/mfstructure.swift +++ b/Sources/Matft/core/object/mfstructure.swift @@ -73,8 +73,12 @@ internal func shape2size(_ shape: inout [Int]) -> Int{ /// - mforder: Order /// - Returns: A strides array internal func shape2strides(_ shape: inout [Int], mforder: MfOrder) -> [Int]{ + guard !shape.isEmpty else { + return [] + } + var ret = Array(repeating: 0, count: shape.count) - + switch mforder { case .Row://, .None: var prevAxisNum = shape2size(&shape) From b0fbfa99cbc905e8f6e8e7ce2b1a0a34f9791035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Mon, 19 Jan 2026 16:28:50 +0100 Subject: [PATCH 24/26] Use https for dependency instead of ssh --- Package.resolved | 2 +- Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.resolved b/Package.resolved index 737c379..f19db85 100644 --- a/Package.resolved +++ b/Package.resolved @@ -3,7 +3,7 @@ { "identity" : "clapack", "kind" : "remoteSourceControl", - "location" : "git@github.com:GoodNotes/CLAPACK.git", + "location" : "https://github.com/goodnotes/CLAPACK", "state" : { "branch" : "eigen-support", "revision" : "a9c8a67890a31af54ed55536b5dd606d6de609a9" diff --git a/Package.swift b/Package.swift index 0cd463b..38c2376 100644 --- a/Package.swift +++ b/Package.swift @@ -12,7 +12,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), - .package(url: "git@github.com:GoodNotes/CLAPACK.git", branch: "eigen-support"), + .package(url: "https://github.com/goodnotes/CLAPACK", branch: "eigen-support"), ], targets: [ .target( From 89cb093158e671fa91d97151c38fb93213efc4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Tue, 20 Jan 2026 08:18:24 +0100 Subject: [PATCH 25/26] Disable some perf tests temporally for WASM --- Tests/PerformanceTests/IndexingPefTests.swift | 8 +++++--- Tests/PerformanceTests/MathPefTests.swift | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Tests/PerformanceTests/IndexingPefTests.swift b/Tests/PerformanceTests/IndexingPefTests.swift index 66db616..b872051 100644 --- a/Tests/PerformanceTests/IndexingPefTests.swift +++ b/Tests/PerformanceTests/IndexingPefTests.swift @@ -1,6 +1,8 @@ +// Performance tests for boolean operations disabled for WASM temporally +#if !os(WASI) import XCTest -//@testable import Matft -import Matft + +@testable import Matft final class IndexingPefTests: XCTestCase { @@ -18,4 +20,4 @@ final class IndexingPefTests: XCTestCase { } } } - +#endif diff --git a/Tests/PerformanceTests/MathPefTests.swift b/Tests/PerformanceTests/MathPefTests.swift index 6086c29..231afcc 100644 --- a/Tests/PerformanceTests/MathPefTests.swift +++ b/Tests/PerformanceTests/MathPefTests.swift @@ -1,3 +1,5 @@ +// Performance tests for boolean operations disabled for WASM temporally +#if !os(WASI) // // MathPefTests.swift // @@ -6,8 +8,8 @@ // import XCTest -//@testable import Matft -import Matft + +@testable import Matft final class MathPefTests: XCTestCase { @@ -67,5 +69,5 @@ final class MathPefTests: XCTestCase { } } } - +#endif From 9fec130840e50495df5094350191e609aa2fecdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Go=CC=81mez?= Date: Tue, 20 Jan 2026 08:59:55 +0100 Subject: [PATCH 26/26] Fix CI by forcing the toolchain version we have to use --- .github/workflows/wasm.yml | 42 +++++++--- .../PerformanceTests/ArithmeticPefTests.swift | 6 +- Tests/PerformanceTests/BoolPefTests.swift | 3 - Tests/PerformanceTests/IndexingPefTests.swift | 2 - Tests/PerformanceTests/MathPefTests.swift | 4 - scripts/build-and-test-wasm.sh | 79 ++++++++++++------- 6 files changed, 85 insertions(+), 51 deletions(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 74fb06f..34babb0 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -2,21 +2,45 @@ name: wasm on: push: - pull_request: + +env: + # Required Swift toolchain version for WASM builds + # Must match REQUIRED_TOOLCHAIN_VERSION in scripts/build-and-test-wasm.sh + SWIFT_TOOLCHAIN_VERSION: "DEVELOPMENT-SNAPSHOT-2025-11-03-a" + # Checksum for the WASM SDK (from SwiftWasm release page) + SWIFT_WASM_SDK_CHECKSUM: "879c08f24c36e20e0b3d1fadc37f4c34c089c72caa018aec726d9e0bf84ea6ff" jobs: build: - runs-on: macos-latest - + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - # Use Swift 6.0.3 to match the pinned SDK version in build-and-test-wasm.sh - # The script will automatically install the 6.0.3-RELEASE WASM SDK - - name: Install Swift - uses: swift-actions/setup-swift@v2 - with: - swift-version: "6.1" + # Install the specific Swift development snapshot required for WASM builds + # Toolchain comes from swift.org, SDK comes from SwiftWasm + - name: Install Swift Toolchain + run: | + SWIFT_URL="https://download.swift.org/development/ubuntu2404/swift-${SWIFT_TOOLCHAIN_VERSION}/swift-${SWIFT_TOOLCHAIN_VERSION}-ubuntu24.04.tar.gz" + echo "Downloading Swift toolchain from: $SWIFT_URL" + mkdir -p /opt/swift + curl -sL "$SWIFT_URL" | tar xz --strip-components=1 -C /opt/swift + echo "/opt/swift/usr/bin" >> $GITHUB_PATH + + - name: Verify Swift Installation + run: swift --version + + # Install the matching WASM SDK from SwiftWasm + - name: Install WASM SDK + run: | + SDK_URL="https://github.com/swiftwasm/swift/releases/download/swift-wasm-${SWIFT_TOOLCHAIN_VERSION}/swift-wasm-${SWIFT_TOOLCHAIN_VERSION}-wasm32-unknown-wasip1-threads.artifactbundle.zip" + echo "Installing WASM SDK from: $SDK_URL" + swift sdk install "$SDK_URL" --checksum "$SWIFT_WASM_SDK_CHECKSUM" + echo "Installed SDKs:" + swift sdk list + + # Set environment variable to signal the correct toolchain is installed + - name: Set Toolchain Environment + run: echo "SWIFT_WASM_TOOLCHAIN_VERIFIED=1" >> $GITHUB_ENV # Wasmtime is required because `swift test` doesn't work for WebAssembly targets. # For WASM, we must build tests separately and run them with a WASM runtime. diff --git a/Tests/PerformanceTests/ArithmeticPefTests.swift b/Tests/PerformanceTests/ArithmeticPefTests.swift index cb66b00..759f7ec 100644 --- a/Tests/PerformanceTests/ArithmeticPefTests.swift +++ b/Tests/PerformanceTests/ArithmeticPefTests.swift @@ -1,7 +1,6 @@ -#if !os(WASI) import XCTest -//@testable import Matft -import Matft + +@testable import Matft final class ArithmeticPefTests: XCTestCase { @@ -52,4 +51,3 @@ final class ArithmeticPefTests: XCTestCase { } } } -#endif diff --git a/Tests/PerformanceTests/BoolPefTests.swift b/Tests/PerformanceTests/BoolPefTests.swift index d00a624..68f2a8c 100644 --- a/Tests/PerformanceTests/BoolPefTests.swift +++ b/Tests/PerformanceTests/BoolPefTests.swift @@ -6,7 +6,6 @@ // // Performance tests for boolean operations disabled for WASM temporally -#if !os(WASI) import XCTest @testable import Matft @@ -69,5 +68,3 @@ final class BoolPefTests: XCTestCase { } } } - -#endif \ No newline at end of file diff --git a/Tests/PerformanceTests/IndexingPefTests.swift b/Tests/PerformanceTests/IndexingPefTests.swift index b872051..d455b51 100644 --- a/Tests/PerformanceTests/IndexingPefTests.swift +++ b/Tests/PerformanceTests/IndexingPefTests.swift @@ -1,5 +1,4 @@ // Performance tests for boolean operations disabled for WASM temporally -#if !os(WASI) import XCTest @testable import Matft @@ -20,4 +19,3 @@ final class IndexingPefTests: XCTestCase { } } } -#endif diff --git a/Tests/PerformanceTests/MathPefTests.swift b/Tests/PerformanceTests/MathPefTests.swift index 231afcc..2de8623 100644 --- a/Tests/PerformanceTests/MathPefTests.swift +++ b/Tests/PerformanceTests/MathPefTests.swift @@ -1,5 +1,3 @@ -// Performance tests for boolean operations disabled for WASM temporally -#if !os(WASI) // // MathPefTests.swift // @@ -69,5 +67,3 @@ final class MathPefTests: XCTestCase { } } } -#endif - diff --git a/scripts/build-and-test-wasm.sh b/scripts/build-and-test-wasm.sh index 2666276..9f22149 100755 --- a/scripts/build-and-test-wasm.sh +++ b/scripts/build-and-test-wasm.sh @@ -6,6 +6,10 @@ echo "🌐 Building and Testing Matft for WebAssembly" echo "🌐 ==========================================" echo "" +# Required toolchain version for WASM builds +# This ensures consistent builds across all environments +REQUIRED_TOOLCHAIN_VERSION="DEVELOPMENT-SNAPSHOT-2025-11-03-a" + # SDK info for different Swift versions (SDK must match Swift compiler version) get_sdk_info() { local SWIFT_VER="$1" @@ -35,41 +39,53 @@ SWIFT_VERSION="" # Setup Swift command and detect version setup_swift() { echo "📦 Setting up Swift..." + echo " Required toolchain: $REQUIRED_TOOLCHAIN_VERSION" + + # In CI, the workflow installs the exact toolchain and sets this env var + if [ "${SWIFT_WASM_TOOLCHAIN_VERIFIED:-}" = "1" ] && command -v swift &> /dev/null; then + SWIFT_CMD="swift" + SWIFT_VERSION="dev" + local VERSION_OUTPUT=$(swift --version 2>/dev/null | head -1) + echo "✅ Using CI-installed toolchain" + echo " Version: $VERSION_OUTPUT" + echo "" + return 0 + fi - # Check for local SwiftWasm toolchain first (macOS development with newer SDK) + # Check for local SwiftWasm toolchain (macOS development with newer SDK) TOOLCHAIN_DIR="$HOME/Library/Developer/Toolchains" if [ -d "$TOOLCHAIN_DIR" ]; then - # Look for development snapshot toolchain (needed for newer macOS SDKs) - local WASM_TOOLCHAIN=$(ls -1 "$TOOLCHAIN_DIR" 2>/dev/null | grep -E "swift-DEVELOPMENT-SNAPSHOT.*\.xctoolchain" | sort -r | head -1) + # Look for the specific required toolchain version + local WASM_TOOLCHAIN="swift-${REQUIRED_TOOLCHAIN_VERSION}.xctoolchain" - if [ -n "$WASM_TOOLCHAIN" ]; then + if [ -d "$TOOLCHAIN_DIR/$WASM_TOOLCHAIN" ]; then local TOOLCHAIN_SWIFT="$TOOLCHAIN_DIR/$WASM_TOOLCHAIN/usr/bin/swift" if [ -x "$TOOLCHAIN_SWIFT" ]; then SWIFT_CMD="$TOOLCHAIN_SWIFT" - echo "✅ Using local development toolchain: $WASM_TOOLCHAIN" + echo "✅ Using required toolchain: $WASM_TOOLCHAIN" local VERSION_OUTPUT=$($SWIFT_CMD --version 2>/dev/null | head -1) echo " Version: $VERSION_OUTPUT" # Dev toolchain - will use dev SDK SWIFT_VERSION="dev" echo "" return 0 + else + echo "❌ Toolchain found but swift binary not executable: $TOOLCHAIN_SWIFT" + exit 1 fi fi fi - # Use system Swift (CI environment) + # No matching toolchain found + echo "❌ Required Swift toolchain not found: $REQUIRED_TOOLCHAIN_VERSION" + echo "" + echo " For macOS: Install the toolchain to ~/Library/Developer/Toolchains/" + echo " For Linux/CI: Install from https://download.swift.org/development/" + echo "" if command -v swift &> /dev/null; then - SWIFT_CMD="swift" - local VERSION_OUTPUT=$($SWIFT_CMD --version 2>/dev/null) - # Extract version number (e.g., "6.1.3" from "Swift version 6.1.3") - SWIFT_VERSION=$(echo "$VERSION_OUTPUT" | grep -oE "Swift version [0-9]+\.[0-9]+(\.[0-9]+)?" | head -1 | sed 's/Swift version //') - echo "✅ Using system Swift" - echo " Version: $SWIFT_VERSION" - echo "" - return 0 + echo " Current Swift in PATH:" + swift --version 2>/dev/null | head -1 | sed 's/^/ /' fi - - echo "❌ No Swift installation found" exit 1 } @@ -79,27 +95,32 @@ setup_wasm_sdk() { local INSTALLED_SDKS=$($SWIFT_CMD sdk list 2>/dev/null || echo "") - # For development toolchain, try to find matching development SDK first + # For development toolchain, find SDK matching the required toolchain version if [ "$SWIFT_VERSION" = "dev" ]; then - local TOOLCHAIN_DATE=$(echo "$SWIFT_CMD" | grep -oE "[0-9]{4}-[0-9]{2}-[0-9]{2}") - if [ -n "$TOOLCHAIN_DATE" ]; then - SWIFT_SDK_NAME=$(echo "$INSTALLED_SDKS" | grep "DEVELOPMENT-SNAPSHOT-${TOOLCHAIN_DATE}" | grep -E "wasm32-unknown-wasi" | grep -v "embedded" | head -1) - if [ -n "$SWIFT_SDK_NAME" ]; then - echo "✅ Found matching development SDK: $SWIFT_SDK_NAME" - echo "" - return 0 - fi + echo " Required SDK version: $REQUIRED_TOOLCHAIN_VERSION" + + # Look for SDK matching the exact required toolchain version (prefer threads variant) + SWIFT_SDK_NAME=$(echo "$INSTALLED_SDKS" | grep "$REQUIRED_TOOLCHAIN_VERSION" | grep "wasm32-unknown-wasip1-threads$" | grep -v "embedded" | head -1) + if [ -n "$SWIFT_SDK_NAME" ]; then + echo "✅ Found matching SDK (threads): $SWIFT_SDK_NAME" + echo "" + return 0 fi - # Fallback to any development snapshot SDK - SWIFT_SDK_NAME=$(echo "$INSTALLED_SDKS" | grep "DEVELOPMENT-SNAPSHOT" | grep "wasm32-unknown-wasip1-threads$" | grep -v "embedded" | sort -r | head -1) + # Try non-threads variant + SWIFT_SDK_NAME=$(echo "$INSTALLED_SDKS" | grep "$REQUIRED_TOOLCHAIN_VERSION" | grep -E "wasm32-unknown-wasi$" | grep -v "embedded" | head -1) if [ -n "$SWIFT_SDK_NAME" ]; then - echo "✅ Found development SDK: $SWIFT_SDK_NAME" + echo "✅ Found matching SDK: $SWIFT_SDK_NAME" echo "" return 0 fi - echo "❌ No development SDK found for development toolchain" + echo "❌ No SDK found matching required version: $REQUIRED_TOOLCHAIN_VERSION" + echo " Available SDKs:" + echo "$INSTALLED_SDKS" | sed 's/^/ - /' + echo "" + echo " Please install the required SDK:" + echo " swift sdk install " exit 1 fi