Replace Objective-C bridge with Swift ONNX runtime backend
CodeQL Advanced / Analyze (swift) (push) Has been cancelled
CodeQL Advanced / Analyze (swift) (push) Has been cancelled
This commit is contained in:
+25
-104
@@ -1,114 +1,35 @@
|
||||
// swift-tools-version: 5.9
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
// A user of the Swift Package Manager (SPM) package will consume this file directly from the ORT SPM github repository.
|
||||
// For example, the end user's config will look something like:
|
||||
//
|
||||
// dependencies: [
|
||||
// .package(url: "https://github.com/microsoft/onnxruntime-swift-package-manager", from: "1.16.0"),
|
||||
// ...
|
||||
// ],
|
||||
//
|
||||
// NOTE: For valid version numbers, please refer to this page:
|
||||
// https://github.com/microsoft/onnxruntime-swift-package-manager/releases
|
||||
// swift-tools-version: 6.1
|
||||
|
||||
import PackageDescription
|
||||
import class Foundation.ProcessInfo
|
||||
|
||||
let package = Package(
|
||||
name: "onnxruntime",
|
||||
platforms: [.iOS(.v15),
|
||||
.macOS(.v14)],
|
||||
products: [
|
||||
.library(name: "onnxruntime",
|
||||
type: .static,
|
||||
targets: ["OnnxRuntimeBindings"]),
|
||||
.library(name: "onnxruntime_extensions",
|
||||
type: .static,
|
||||
targets: ["OnnxRuntimeExtensions"]),
|
||||
.library(
|
||||
name: "onnxruntime",
|
||||
targets: ["OnnxRuntimeBindings"]
|
||||
),
|
||||
],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(name: "OnnxRuntimeBindings",
|
||||
dependencies: ["onnxruntime"],
|
||||
path: "objectivec",
|
||||
exclude: ["ReadMe.md", "format_objc.sh", "test", "docs",
|
||||
"ort_checkpoint.mm",
|
||||
"ort_checkpoint_internal.h",
|
||||
"ort_training_session_internal.h",
|
||||
"ort_training_session.mm",
|
||||
"include/ort_checkpoint.h",
|
||||
"include/ort_training_session.h",
|
||||
"include/onnxruntime_training.h"],
|
||||
cxxSettings: [
|
||||
.define("SPM_BUILD"),
|
||||
]),
|
||||
.testTarget(name: "OnnxRuntimeBindingsTests",
|
||||
dependencies: ["OnnxRuntimeBindings"],
|
||||
path: "swift/OnnxRuntimeBindingsTests",
|
||||
resources: [
|
||||
.copy("Resources/single_add.basic.ort")
|
||||
]),
|
||||
.target(name: "OnnxRuntimeExtensions",
|
||||
dependencies: ["onnxruntime_extensions", "onnxruntime"],
|
||||
path: "extensions",
|
||||
cxxSettings: [
|
||||
.define("ORT_SWIFT_PACKAGE_MANAGER_BUILD"),
|
||||
]),
|
||||
.testTarget(name: "OnnxRuntimeExtensionsTests",
|
||||
dependencies: ["OnnxRuntimeExtensions", "OnnxRuntimeBindings"],
|
||||
path: "swift/OnnxRuntimeExtensionsTests",
|
||||
resources: [
|
||||
.copy("Resources/decode_image.onnx")
|
||||
]),
|
||||
.systemLibrary(
|
||||
name: "COnnxRuntime",
|
||||
path: "swift/COnnxRuntime",
|
||||
pkgConfig: "libonnxruntime"
|
||||
),
|
||||
.target(
|
||||
name: "OnnxRuntimeBindings",
|
||||
dependencies: ["COnnxRuntime"],
|
||||
path: "swift/OnnxRuntimeBindings"
|
||||
),
|
||||
.testTarget(
|
||||
name: "OnnxRuntimeBindingsTests",
|
||||
dependencies: ["OnnxRuntimeBindings", "COnnxRuntime"],
|
||||
path: "swift/OnnxRuntimeBindingsTests",
|
||||
exclude: ["SwiftOnnxRuntimeBindingsTests.swift"],
|
||||
resources: [
|
||||
.copy("Resources/single_add.basic.ort")
|
||||
]
|
||||
),
|
||||
],
|
||||
cxxLanguageStandard: .cxx17
|
||||
cxxLanguageStandard: .cxx20
|
||||
)
|
||||
|
||||
// Add the ORT CocoaPods C/C++ pod archive as a binary target.
|
||||
//
|
||||
// There are 2 scenarios:
|
||||
// - Target will be set to a released pod archive and its checksum.
|
||||
//
|
||||
// - Target will be set to a local pod archive.
|
||||
// This can be used to test with the latest (not yet released) ORT Objective-C source code.
|
||||
|
||||
// CI or local testing where you have built/obtained the pod archive matching the current source code.
|
||||
// Requires the ORT_POD_LOCAL_PATH environment variable to be set to specify the location of the pod.
|
||||
if let pod_archive_path = ProcessInfo.processInfo.environment["ORT_POD_LOCAL_PATH"] {
|
||||
// ORT_POD_LOCAL_PATH MUST be a path that is relative to Package.swift.
|
||||
//
|
||||
// To build locally, tools/ci_build/github/apple/build_and_assemble_apple_pods.py can be used
|
||||
// See https://onnxruntime.ai/docs/build/custom.html#ios
|
||||
// Example command:
|
||||
// python3 tools/ci_build/github/apple/build_and_assemble_apple_pods.py \
|
||||
// --build-settings-file tools/ci_build/github/apple/default_full_apple_framework_build_settings.json
|
||||
//
|
||||
// This should produce the pod archive in build/apple_pod_staging, and ORT_POD_LOCAL_PATH can be set to
|
||||
// "build/apple_pod_staging/pod-archive-onnxruntime-c-???.zip" where '???' is replaced by the version info in the
|
||||
// actual filename.
|
||||
package.targets.append(Target.binaryTarget(name: "onnxruntime", path: pod_archive_path))
|
||||
|
||||
} else {
|
||||
// ORT release
|
||||
package.targets.append(
|
||||
Target.binaryTarget(name: "onnxruntime",
|
||||
url: "https://download.onnxruntime.ai/pod-archive-onnxruntime-c-1.24.2.zip",
|
||||
// SHA256 checksum
|
||||
checksum: "f7100a992d2a8135168c8afd831e6a58b465349101982aa58b3e11d36e600b54")
|
||||
)
|
||||
}
|
||||
|
||||
if let ext_pod_archive_path = ProcessInfo.processInfo.environment["ORT_EXTENSIONS_POD_LOCAL_PATH"] {
|
||||
package.targets.append(Target.binaryTarget(name: "onnxruntime_extensions", path: ext_pod_archive_path))
|
||||
} else {
|
||||
// ORT Extensions release
|
||||
package.targets.append(
|
||||
Target.binaryTarget(name: "onnxruntime_extensions",
|
||||
url: "https://download.onnxruntime.ai/pod-archive-onnxruntime-extensions-c-0.13.0.zip",
|
||||
// SHA256 checksum
|
||||
checksum: "346522d1171d4c99cb0908fa8e4e9330a4a6aad39cd83ce36eb654437b33e6b5")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,32 +1,48 @@
|
||||
# Swift Package Manager for ONNX Runtime
|
||||
|
||||
A light-weight repository for providing [Swift Package Manager (SPM)](https://www.swift.org/package-manager/) support for [ONNXRuntime](https://github.com/microsoft/onnxruntime). The ONNX Runtime native package is included as a binary dependency of the SPM package.
|
||||
Swift-native ONNX Runtime bindings derived from the public Objective-C wrapper surface, but implemented without the Objective-C bridge so the package can build on Linux.
|
||||
|
||||
## Status
|
||||
|
||||
SPM is the alternative to CocoaPods when desired platform to consume is mobile iOS.
|
||||
- Public API covers inference, session/run options, tensor values, checkpoint state, and training session entry points.
|
||||
- Backend is wired to the ONNX Runtime C API and training C API through a Swift system library target.
|
||||
- The package expects `libonnxruntime` and headers to be available on the host, typically via `pkg-config`.
|
||||
|
||||
## Note
|
||||
## Requirements
|
||||
|
||||
The `objectivec/` directory is copied from the [ORT repo](https://github.com/microsoft/onnxruntime/tree/main/objectivec) and it's expected to match. It will be updated periodically/before release to merge new changes.
|
||||
- Swift 6.1+
|
||||
- ONNX Runtime installed on the system with:
|
||||
- `libonnxruntime`
|
||||
- `onnxruntime_c_api.h`
|
||||
- `onnxruntime_training_c_api.h`
|
||||
- `pkg-config` metadata for `libonnxruntime`
|
||||
|
||||
## Contributing
|
||||
## Usage
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||
```swift
|
||||
import OnnxRuntimeBindings
|
||||
|
||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
let environment = try ORTEnv(loggingLevel: .warning)
|
||||
let sessionOptions = try ORTSessionOptions()
|
||||
let session = try ORTSession(
|
||||
env: environment,
|
||||
modelPath: "/path/to/model.onnx",
|
||||
sessionOptions: sessionOptions
|
||||
)
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
let inputTensor = try ORTValue(
|
||||
tensorData: NSMutableData(data: inputData),
|
||||
elementType: .float,
|
||||
shape: [1, 224, 224, 3]
|
||||
)
|
||||
|
||||
## Trademarks
|
||||
let outputs = try session.run(
|
||||
inputs: ["input": inputTensor],
|
||||
outputNames: ["output"]
|
||||
)
|
||||
```
|
||||
|
||||
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
|
||||
trademarks or logos is subject to and must follow
|
||||
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
|
||||
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
|
||||
Any use of third-party trademarks or logos are subject to those third-party's policies.
|
||||
## Notes
|
||||
|
||||
- The `objectivec/` directory remains in the repo as the reference contract surface from upstream ONNX Runtime.
|
||||
- The shipped Swift package target is the pure Swift `OnnxRuntimeBindings` implementation under `swift/OnnxRuntimeBindings`.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module COnnxRuntime [system] {
|
||||
header "shim.h"
|
||||
link "onnxruntime"
|
||||
export *
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#include <onnxruntime/onnxruntime_c_api.h>
|
||||
#include <onnxruntime/onnxruntime_training_c_api.h>
|
||||
@@ -0,0 +1,21 @@
|
||||
import Foundation
|
||||
|
||||
public enum ORTError: Error, LocalizedError {
|
||||
case notImplemented(String)
|
||||
case runtimeFailure(action: String, message: String)
|
||||
case invalidArgument(String)
|
||||
case unsupported(String)
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .notImplemented(let symbol):
|
||||
return "ONNX Runtime Swift interface symbol is not implemented yet: \(symbol)"
|
||||
case .runtimeFailure(let action, let message):
|
||||
return "\(action) failed: \(message)"
|
||||
case .invalidArgument(let message):
|
||||
return message
|
||||
case .unsupported(let message):
|
||||
return message
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
@preconcurrency import COnnxRuntime
|
||||
import Foundation
|
||||
|
||||
internal enum ORTNative {
|
||||
nonisolated(unsafe) static let apiBase = OrtGetApiBase()
|
||||
nonisolated(unsafe) static let api = apiBase!.pointee.GetApi(UInt32(ORT_API_VERSION))!
|
||||
nonisolated(unsafe) static let trainingApi = api.pointee.GetTrainingApi?(UInt32(ORT_API_VERSION))
|
||||
|
||||
static func check(_ status: OpaquePointer?, action: String) throws {
|
||||
guard let status else {
|
||||
return
|
||||
}
|
||||
|
||||
let message = api.pointee.GetErrorMessage?(status).map { String(cString: $0) } ?? "Unknown ONNX Runtime error"
|
||||
api.pointee.ReleaseStatus?(status)
|
||||
throw ORTError.runtimeFailure(action: action, message: message)
|
||||
}
|
||||
|
||||
static func withAllocator<Result>(_ body: (UnsafeMutablePointer<OrtAllocator>) throws -> Result) throws -> Result {
|
||||
var allocator: UnsafeMutablePointer<OrtAllocator>?
|
||||
try check(api.pointee.GetAllocatorWithDefaultOptions?(&allocator), action: "GetAllocatorWithDefaultOptions")
|
||||
guard let allocator else {
|
||||
throw ORTError.runtimeFailure(action: "GetAllocatorWithDefaultOptions", message: "Allocator was not returned")
|
||||
}
|
||||
return try body(allocator)
|
||||
}
|
||||
|
||||
static func copyAllocatedString(
|
||||
_ stringPointer: UnsafeMutablePointer<CChar>?,
|
||||
allocator: UnsafeMutablePointer<OrtAllocator>
|
||||
) throws -> String {
|
||||
guard let stringPointer else {
|
||||
throw ORTError.runtimeFailure(action: "copyAllocatedString", message: "Expected allocated string")
|
||||
}
|
||||
|
||||
defer {
|
||||
_ = api.pointee.AllocatorFree?(allocator, stringPointer)
|
||||
}
|
||||
|
||||
return String(cString: stringPointer)
|
||||
}
|
||||
|
||||
static func tensorElementCount(shape: [Int]) throws -> Int {
|
||||
guard shape.allSatisfy({ $0 >= 0 }) else {
|
||||
throw ORTError.invalidArgument("Tensor shape contains a negative dimension: \(shape)")
|
||||
}
|
||||
|
||||
return shape.reduce(1, *)
|
||||
}
|
||||
|
||||
static func elementByteCount(for type: ORTTensorElementDataType) throws -> Int {
|
||||
switch type {
|
||||
case .float:
|
||||
return MemoryLayout<Float>.stride
|
||||
case .int8:
|
||||
return MemoryLayout<Int8>.stride
|
||||
case .uInt8:
|
||||
return MemoryLayout<UInt8>.stride
|
||||
case .int32:
|
||||
return MemoryLayout<Int32>.stride
|
||||
case .uInt32:
|
||||
return MemoryLayout<UInt32>.stride
|
||||
case .int64:
|
||||
return MemoryLayout<Int64>.stride
|
||||
case .uInt64:
|
||||
return MemoryLayout<UInt64>.stride
|
||||
case .string:
|
||||
throw ORTError.invalidArgument("String tensors do not have a fixed byte width")
|
||||
case .undefined:
|
||||
throw ORTError.invalidArgument("Undefined tensor element type is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
static func makeShapeBuffer(_ shape: [Int]) -> [Int64] {
|
||||
shape.map(Int64.init)
|
||||
}
|
||||
|
||||
static func withCStringArray<Result>(
|
||||
_ strings: [String],
|
||||
_ body: ([UnsafePointer<CChar>?]) throws -> Result
|
||||
) rethrows -> Result {
|
||||
let storage = strings.map { Array($0.utf8CString) }
|
||||
let pointers = storage.map { $0.withUnsafeBufferPointer { $0.baseAddress } }
|
||||
return try body(pointers)
|
||||
}
|
||||
|
||||
static func fetchTensorInfo(from value: OpaquePointer) throws -> ORTTensorTypeAndShapeInfo {
|
||||
var info: OpaquePointer?
|
||||
try check(api.pointee.GetTensorTypeAndShape?(value, &info), action: "GetTensorTypeAndShape")
|
||||
guard let info else {
|
||||
throw ORTError.runtimeFailure(action: "GetTensorTypeAndShape", message: "Tensor info was not returned")
|
||||
}
|
||||
|
||||
defer {
|
||||
api.pointee.ReleaseTensorTypeAndShapeInfo?(info)
|
||||
}
|
||||
|
||||
var elementType = ONNXTensorElementDataType(rawValue: 0)
|
||||
try check(api.pointee.GetTensorElementType?(info, &elementType), action: "GetTensorElementType")
|
||||
|
||||
var dimensionsCount: Int = 0
|
||||
try check(api.pointee.GetDimensionsCount?(info, &dimensionsCount), action: "GetDimensionsCount")
|
||||
|
||||
var dimensions = Array(repeating: Int64(0), count: dimensionsCount)
|
||||
if dimensionsCount > 0 {
|
||||
try check(
|
||||
api.pointee.GetDimensions?(info, &dimensions, dimensionsCount),
|
||||
action: "GetDimensions"
|
||||
)
|
||||
}
|
||||
|
||||
return ORTTensorTypeAndShapeInfo(
|
||||
elementType: try ORTTensorElementDataType(native: elementType),
|
||||
shape: dimensions.map(Int.init)
|
||||
)
|
||||
}
|
||||
|
||||
static func fetchValueTypeInfo(from value: OpaquePointer) throws -> ORTValueTypeInfo {
|
||||
var typeInfo: OpaquePointer?
|
||||
try check(api.pointee.GetTypeInfo?(value, &typeInfo), action: "GetTypeInfo")
|
||||
guard let typeInfo else {
|
||||
throw ORTError.runtimeFailure(action: "GetTypeInfo", message: "Type info was not returned")
|
||||
}
|
||||
|
||||
defer {
|
||||
api.pointee.ReleaseTypeInfo?(typeInfo)
|
||||
}
|
||||
|
||||
var onnxType = ONNXType(rawValue: 0)
|
||||
try check(api.pointee.GetOnnxTypeFromTypeInfo?(typeInfo, &onnxType), action: "GetOnnxTypeFromTypeInfo")
|
||||
|
||||
var tensorInfoPointer: OpaquePointer?
|
||||
try check(api.pointee.CastTypeInfoToTensorInfo?(typeInfo, &tensorInfoPointer), action: "CastTypeInfoToTensorInfo")
|
||||
|
||||
let tensorInfo = try tensorInfoPointer.map { pointer in
|
||||
try fetchTensorInfoView(from: pointer)
|
||||
}
|
||||
|
||||
return ORTValueTypeInfo(
|
||||
type: try ORTValueType(native: onnxType),
|
||||
tensorTypeAndShapeInfo: tensorInfo
|
||||
)
|
||||
}
|
||||
|
||||
static func fetchTensorInfoView(from info: OpaquePointer) throws -> ORTTensorTypeAndShapeInfo {
|
||||
var elementType = ONNXTensorElementDataType(rawValue: 0)
|
||||
try check(api.pointee.GetTensorElementType?(info, &elementType), action: "GetTensorElementType")
|
||||
|
||||
var dimensionsCount: Int = 0
|
||||
try check(api.pointee.GetDimensionsCount?(info, &dimensionsCount), action: "GetDimensionsCount")
|
||||
|
||||
var dimensions = Array(repeating: Int64(0), count: dimensionsCount)
|
||||
if dimensionsCount > 0 {
|
||||
try check(api.pointee.GetDimensions?(info, &dimensions, dimensionsCount), action: "GetDimensions")
|
||||
}
|
||||
|
||||
return ORTTensorTypeAndShapeInfo(
|
||||
elementType: try ORTTensorElementDataType(native: elementType),
|
||||
shape: dimensions.map(Int.init)
|
||||
)
|
||||
}
|
||||
|
||||
static func boolString(_ value: Bool) -> String {
|
||||
value ? "1" : "0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@preconcurrency import COnnxRuntime
|
||||
@testable import OnnxRuntimeBindings
|
||||
|
||||
@_cdecl("SwiftTestRegisterCustomOps")
|
||||
func SwiftTestRegisterCustomOps(_ options: OpaquePointer?, _ api: UnsafePointer<OrtApiBase>?) -> OpaquePointer? {
|
||||
_ = options
|
||||
_ = api
|
||||
return nil
|
||||
}
|
||||
|
||||
struct SwiftAPITests {
|
||||
@Test
|
||||
func sessionOptionsRetainConfiguredState() throws {
|
||||
let options = try ORTSessionOptions()
|
||||
|
||||
try options.setIntraOpNumThreads(4)
|
||||
try options.setGraphOptimizationLevel(.extended)
|
||||
try options.setOptimizedModelFilePath("/tmp/model.optimized.onnx")
|
||||
try options.setLogID("session-log")
|
||||
try options.setLogSeverityLevel(.warning)
|
||||
try options.addConfigEntry(key: "session.disable_prepacking", value: "1")
|
||||
|
||||
#expect(options.intraOpNumThreads == 4)
|
||||
#expect(options.graphOptimizationLevel == .extended)
|
||||
#expect(options.optimizedModelFilePath == "/tmp/model.optimized.onnx")
|
||||
#expect(options.logID == "session-log")
|
||||
#expect(options.logSeverityLevel == .warning)
|
||||
#expect(options.configEntries["session.disable_prepacking"] == "1")
|
||||
#expect(options.executionProviders.isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
func unsupportedExecutionProviderSurfacesRuntimeError() throws {
|
||||
let options = try ORTSessionOptions()
|
||||
|
||||
#expect(throws: ORTError.self) {
|
||||
try options.appendExecutionProvider("XNNPACK", providerOptions: ["threads": "2"])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func customOpsFunctionPointerRegistrationIsInvoked() throws {
|
||||
let options = try ORTSessionOptions()
|
||||
let functionPointer = unsafeBitCast(
|
||||
SwiftTestRegisterCustomOps as RegisterCustomOpsFn,
|
||||
to: UnsafeMutableRawPointer.self
|
||||
)
|
||||
|
||||
try options.registerCustomOps(usingFunctionPointer: functionPointer)
|
||||
|
||||
#expect(options.customOpRegistrationFunctionPointer == functionPointer)
|
||||
}
|
||||
|
||||
@Test
|
||||
func runOptionsRetainConfiguredState() throws {
|
||||
let options = try ORTRunOptions()
|
||||
|
||||
try options.setLogTag("inference")
|
||||
try options.setLogSeverityLevel(.error)
|
||||
try options.addConfigEntry(key: "terminate", value: "0")
|
||||
|
||||
#expect(options.logTag == "inference")
|
||||
#expect(options.logSeverityLevel == .error)
|
||||
#expect(options.configEntries["terminate"] == "0")
|
||||
}
|
||||
|
||||
@Test
|
||||
func numericTensorValueExposesTensorInfo() throws {
|
||||
let bytes = NSMutableData(data: Data([0, 0, 128, 63]))
|
||||
let value = try ORTValue(tensorData: bytes, elementType: .float, shape: [1])
|
||||
|
||||
let typeInfo = try value.typeInfo()
|
||||
let tensorInfo = try value.tensorTypeAndShapeInfo()
|
||||
let data = try value.tensorData()
|
||||
|
||||
#expect(typeInfo.type == .tensor)
|
||||
#expect(typeInfo.tensorTypeAndShapeInfo?.elementType == .float)
|
||||
#expect(tensorInfo.elementType == .float)
|
||||
#expect(tensorInfo.shape == [1])
|
||||
#expect(data.length == bytes.length)
|
||||
#expect(Data(referencing: data) == Data(referencing: bytes))
|
||||
}
|
||||
|
||||
@Test
|
||||
func stringTensorValueExposesTensorInfo() throws {
|
||||
let value = try ORTValue(tensorStringData: ["hello", "world"], shape: [2])
|
||||
|
||||
let typeInfo = try value.typeInfo()
|
||||
let tensorInfo = try value.tensorTypeAndShapeInfo()
|
||||
let strings = try value.tensorStringData()
|
||||
|
||||
#expect(typeInfo.type == .tensor)
|
||||
#expect(typeInfo.tensorTypeAndShapeInfo?.elementType == .string)
|
||||
#expect(tensorInfo.elementType == .string)
|
||||
#expect(tensorInfo.shape == [2])
|
||||
#expect(strings == ["hello", "world"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func wrongTensorAccessorThrows() throws {
|
||||
let numeric = try ORTValue(tensorData: NSMutableData(data: Data([0, 0, 0, 0])), elementType: .float, shape: [])
|
||||
let string = try ORTValue(tensorStringData: [], shape: [0])
|
||||
|
||||
#expect(throws: ORTError.self) {
|
||||
_ = try numeric.tensorStringData()
|
||||
}
|
||||
#expect(throws: ORTError.self) {
|
||||
_ = try string.tensorData()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user