Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1cc9b16
Enable coverage reports and add ci step for the wasm build. The build…
pedrovgs Jan 16, 2026
75cc280
Update workflow toolchain version
pedrovgs Jan 16, 2026
8d1fa5d
Add test step
pedrovgs Jan 16, 2026
7f855de
Merge pull request #1 from GoodNotes/wasm-compatibility-ci
pedrovgs Jan 16, 2026
0c40e94
Fix iOS CI, update WASM CI for testing we will need later and provide…
pedrovgs Jan 16, 2026
4ff93ea
Merge pull request #2 from GoodNotes/wasm-more-ci-improvements
pedrovgs Jan 19, 2026
d25725a
Update CI and swift version
pedrovgs Jan 19, 2026
930c68a
Update CI
pedrovgs Jan 19, 2026
0ddb743
Enable shared memory
pedrovgs Jan 19, 2026
512f0f4
Do not use number formatter for wasm
pedrovgs Jan 19, 2026
7af4032
Make mfarray compatible with wasm by disabling coreml features
pedrovgs Jan 19, 2026
696ece5
Make mfadata compatible with wasm
pedrovgs Jan 19, 2026
0685139
Get mftype compatible with wasm
pedrovgs Jan 19, 2026
bc7ab32
Provide now a mftype fallback types we when accelerate is not available
pedrovgs Jan 19, 2026
a755923
Get order now compatible with XP
pedrovgs Jan 19, 2026
654b9ff
Get a fallback implementaiton if accelerate is not availbe in types.s…
pedrovgs Jan 19, 2026
3ff8285
Get some extra wrappers for libraries we don't support
pedrovgs Jan 19, 2026
86b934d
Get now method folder compatible with wasm
pedrovgs Jan 19, 2026
f9112a2
Get fallback implementation for static folder
pedrovgs Jan 19, 2026
b6a021d
Get library and wrapper compatible with WASM
pedrovgs Jan 19, 2026
fd1101b
Add support for complex testing scenarios
pedrovgs Jan 19, 2026
d2d85dd
Disable complex tests
pedrovgs Jan 19, 2026
27f3c4b
Improve import usage
pedrovgs Jan 19, 2026
bda0fe7
Use the right param names
pedrovgs Jan 19, 2026
c1787d7
Add fallback implementations with fatal errors until we are able to i…
pedrovgs Jan 19, 2026
2c510f8
Disable performance tests temporally
pedrovgs Jan 19, 2026
3817487
Update random tests to reduce memory pressure
pedrovgs Jan 19, 2026
7327253
Fix import
pedrovgs Jan 19, 2026
48bda80
Update wrong import
pedrovgs Jan 19, 2026
acd15f4
Disable some other performance tests
pedrovgs Jan 19, 2026
8e6d4f1
Enable back wasm fallback tests and revert a change made by mistake
pedrovgs Jan 19, 2026
31c679f
Update clapack pacakge and add some guards to our code to prevent ten…
pedrovgs Jan 19, 2026
b0fbfa9
Use https for dependency instead of ssh
pedrovgs Jan 19, 2026
89cb093
Disable some perf tests temporally for WASM
pedrovgs Jan 20, 2026
9fec130
Fix CI by forcing the toolchain version we have to use
pedrovgs Jan 20, 2026
bab1f10
Merge pull request #3 from GoodNotes/code-wasm-compatibility
pedrovgs Jan 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ jobs:

steps:
- uses: actions/checkout@v4
- uses: swift-actions/setup-swift@v1
- uses: swift-actions/setup-swift@v2
with:
swift-version: "5.9.2"
- name: Build
run: swift build -v
- name: Run tests
run: swift test -v
swift-version: "6.1"
- name: Build and Test
run: ./scripts/build-and-test-ios.sh
55 changes: 55 additions & 0 deletions .github/workflows/wasm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: wasm

on:
push:

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: ubuntu-24.04
steps:
- uses: actions/checkout@v4

# 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.
# See: https://book.swiftwasm.org/getting-started/testing.html
- name: Install Wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1
with:
version: "40.0.2"
github_token: ${{ github.token }}

- name: Build and Test
run: ./scripts/build-and-test-wasm.sh
3 changes: 2 additions & 1 deletion .swiftpm/xcode/xcshareddata/xcschemes/Matft.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand Down
33 changes: 20 additions & 13 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 7 additions & 10 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,38 +1,35 @@
// swift-tools-version:5.1
// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "Matft",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "Matft",
targets: ["Matft"]),
//.library(name: "ReleaseTest", targets: ["Matft "])
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/apple/swift-collections", from: "1.0.0"),

.package(url: "https://github.com/goodnotes/CLAPACK", branch: "eigen-support"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "pocketFFT"
),
.target(
name: "Matft",
dependencies: ["Collections", "pocketFFT"]),
dependencies: [
.product(name: "Collections", package: "swift-collections"),
"pocketFFT",
.product(name: "CLAPACK", package: "CLAPACK", condition: .when(platforms: [.wasi])),
]),
.testTarget(
name: "MatftTests",
dependencies: ["Matft"]),
.testTarget(
name: "PerformanceTests",
dependencies: ["Matft"]),
]
//cxxLanguageStandard: .gnucxx1z
)
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ INFO: Support Complex!!
+ [SwiftPM](#swiftpm)
+ [Carthage](#carthage)
+ [CocoaPods](#cocoapods)
* [Build Scripts](#-build-scripts)
+ [iOS/macOS Build & Test](#-iosmacos-build--test)
+ [WebAssembly Build & Test](#-webassembly-build--test)
* [Contact](#contact)

<strike>
Expand Down Expand Up @@ -858,6 +861,43 @@ So, a pull request is very welcome!!
pod install
```

## 🛠️ Build Scripts

Matft provides convenient bash scripts for building and testing the project locally.

### 🍎 iOS/macOS Build & Test

To build and test Matft for iOS/macOS platforms:

```bash
./scripts/build-and-test-ios.sh
```

This script will:
- Build the project using `swift build`
- Run all tests using `swift test`

### 🌐 WebAssembly Build & Test

To build and test Matft for WebAssembly:

```bash
./scripts/build-and-test-wasm.sh
```

This script will:
- 📦 Check and install the Swift WASM SDK if needed
- 🔧 Check and install wasmtime runtime if needed
- 🔨 Build the project for WebAssembly
- 🧪 Build and run tests using wasmtime

**Note:** The WASM script automatically handles SDK and runtime installation, so you can run it on a fresh machine without any prior setup!

### Requirements

- **iOS/macOS:** Swift 6.1 or later
- **WebAssembly:** Swift 6.1 or later (SDK will be automatically installed)

## Contact

Feel free to ask this project or anything via <junnosuke.kado.git@gmail.com>
17 changes: 15 additions & 2 deletions Sources/Matft/core/general/print.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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{
Expand Down Expand Up @@ -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{
Expand Down
5 changes: 4 additions & 1 deletion Sources/Matft/core/object/mfarray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
//

import Foundation
import Accelerate
#if canImport(CoreML)
import CoreML
#endif

open class MfArray: MfArrayProtocol{
public typealias MFDATA = MfData
Expand Down Expand Up @@ -111,6 +112,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
Expand All @@ -126,6 +128,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
Expand Down
9 changes: 4 additions & 5 deletions Sources/Matft/core/object/mfdata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
//

import Foundation
import Accelerate

internal enum MfDataSource{
case mfdata
Expand Down Expand Up @@ -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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!
This was fatal bug...

}
self.storedSize = flatten_realArray.count
self.mftype = mftype
Expand Down Expand Up @@ -106,18 +105,18 @@ 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
}
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
Expand Down
6 changes: 5 additions & 1 deletion Sources/Matft/core/object/mfstructure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Int>(repeating: 0, count: shape.count)

switch mforder {
case .Row://, .None:
var prevAxisNum = shape2size(&shape)
Expand Down
5 changes: 4 additions & 1 deletion Sources/Matft/core/object/mftype.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
//

import Foundation
import Accelerate
#if canImport(CoreML)
import CoreML
#endif

public enum MfType: Int{
case None
Expand Down Expand Up @@ -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{
Expand All @@ -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{
Expand Down
4 changes: 4 additions & 0 deletions Sources/Matft/core/protocol/mfdataProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading