Skip to content

Commit

Permalink
add ios sdk and example with CoreML (#1289)
Browse files Browse the repository at this point in the history
Co-authored-by: nugrahad <daniel.nugraha@pub.tech>
Co-authored-by: ge57sug <ge57sug@mytum.de>
  • Loading branch information
3 people committed Aug 6, 2022
1 parent 4840b71 commit a1ea6c4
Show file tree
Hide file tree
Showing 124 changed files with 54,709 additions and 0 deletions.
9 changes: 9 additions & 0 deletions examples/ios/Flower/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
35 changes: 35 additions & 0 deletions examples/ios/Flower/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// swift-tools-version: 5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "Flower",
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "Flower",
targets: ["Flower"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/pvieito/PythonKit.git", branch: "master"),
.package(url: "https://github.com/kewlbear/NumPy-iOS.git", branch: "main"),
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.0.0"),
],
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 this package depends on.
.target(
name: "Flower",
dependencies: [
.product(name: "GRPC", package: "grpc-swift"),
.product(name: "NumPy-iOS", package: "NumPy-iOS"),
.product(name: "PythonKit", package: "PythonKit")],
path: "Sources/Flower"),
.testTarget(
name: "FlowerTests",
dependencies: ["Flower"]),
]
)
3 changes: 3 additions & 0 deletions examples/ios/Flower/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Flower

A description of this package.
21 changes: 21 additions & 0 deletions examples/ios/Flower/Sources/Flower/Client/Client.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// Client.swift
// FlowerSDK
//
// Created by Daniel Nugraha on 09.01.22.
//

import Foundation

public protocol Client {
func getParameters() -> ParametersRes
func getProperties(ins: PropertiesIns) -> PropertiesRes
func fit(ins: FitIns) -> FitRes
func evaluate(ins: EvaluateIns) -> EvaluateRes
}

public extension Client {
func getProperties(ins: PropertiesIns) -> PropertiesRes {
return PropertiesRes(properties: [:])
}
}
13 changes: 13 additions & 0 deletions examples/ios/Flower/Sources/Flower/Common/Exception.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Exception.swift
// FlowerSDK
//
// Created by Daniel Nugraha on 09.01.22.
//

import Foundation

public enum FlowerException: Error {
case TypeException(String)
case UnknownServerMessage
}
120 changes: 120 additions & 0 deletions examples/ios/Flower/Sources/Flower/Common/Parameter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// Parameter.swift
// FlowerSDK
//
// Created by Daniel Nugraha on 09.01.22.
//

import Foundation
import NumPySupport
import PythonSupport
import PythonKit
import CoreML

@available(iOS 14.0, *)
public class ParameterConverter {
var np: PythonObject
var io: PythonObject
var typing: PythonObject

private static let appDirectory = FileManager.default.urls(for: .applicationSupportDirectory,
in: .userDomainMask).first!
/// The permanent location of the numpyArray.
private var numpyArrayUrl = appDirectory.appendingPathComponent("numpyArray.npy")

public init() {
PythonSupport.initialize()
NumPySupport.sitePackagesURL.insertPythonPath()
np = Python.import("numpy")
io = Python.import("io")
typing = Python.import("typing")
}

private func multiArrayToNumpy(multiArray: MLMultiArray) -> PythonObject? {
let pointer = try! UnsafeBufferPointer<Float>(multiArray)
let array = pointer.compactMap{$0}
let shape = multiArray.shape.map { Int16(truncating: $0) }
let filteredShape = shape.filter { $0 > 1 }
return array.makeNumpyArray().reshape(filteredShape)
}

private func numpyToData(numpy: PythonObject) -> Data? {
guard Python.isinstance(numpy, np.ndarray) == true else {
return nil
}

let fileManager = FileManager.default
if fileManager.fileExists(atPath: numpyArrayUrl.path) {
try? fileManager.removeItem(at: numpyArrayUrl)
}

np.save(numpyArrayUrl.path, numpy)
return try? Data(contentsOf: numpyArrayUrl)
}

private func dataToNumpy(data: Data) -> PythonObject? {
let fileManager = FileManager.default
if fileManager.fileExists(atPath: numpyArrayUrl.path) {
try? fileManager.removeItem(at: numpyArrayUrl)
}
try? data.write(to: numpyArrayUrl)

return np.load(numpyArrayUrl.path)
}

private func numpyToArray(numpy: PythonObject) -> [Float]? {
guard Python.isinstance(numpy, np.ndarray) == true else {
return nil
}
let flattened = numpy.flatten()
return Array<Float>(numpy: flattened)
}

private func numpyToMultiArray(numpy: PythonObject) -> MLMultiArray? {
guard Python.isinstance(numpy, np.ndarray) == true else {
return nil
}

let pyShape = numpy.__array_interface__["shape"]
guard let shape = Array<Int>(pyShape) else { return nil }
let shapeNSNumber = shape.map { NSNumber(value: $0) }

if let swiftArray = numpyToArray(numpy: numpy),
let multiArray = try? MLMultiArray(shape: shapeNSNumber, dataType: .float) {
for (index, element) in swiftArray.enumerated() {
multiArray[index] = NSNumber(value: element)
}
return multiArray
}
return nil
}

public func dataToMultiArray(data: Data) -> MLMultiArray? {
if let numpy = dataToNumpy(data: data) {
return numpyToMultiArray(numpy: numpy)
}
return nil
}

public func multiArrayToData(multiArray: MLMultiArray) -> Data? {
if let numpy = multiArrayToNumpy(multiArray: multiArray) {
return numpyToData(numpy: numpy)
}
return nil
}

public func dataToArray(data: Data) -> [Float]? {
if let numpy = dataToNumpy(data: data) {
return numpyToArray(numpy: numpy)
}
return nil
}

public func arrayToData(array: [Float], shape: [Int16]) -> Data? {
let numpy = array.makeNumpyArray().reshape(shape)
//print("before shape: \(shape)")
//print("after shape: \(numpy.shape)")
return numpyToData(numpy: numpy)
}
}

Loading

0 comments on commit a1ea6c4

Please sign in to comment.