synor/sdk/swift/Sources/SynorCompute/Tensor.swift
Gulshan Yadav 3aff77a799 feat(sdk): add consumer SDKs for Java, Kotlin, Swift, C, C++, C#, Ruby, and Rust
Expands SDK support to 8 additional languages/frameworks:
- Java SDK with Maven/OkHttp/Jackson
- Kotlin SDK with Gradle/Ktor/kotlinx.serialization
- Swift SDK with Swift Package Manager/async-await
- C SDK with CMake/libcurl
- C++ SDK with CMake/Modern C++20
- C# SDK with .NET 8.0/HttpClient
- Ruby SDK with Bundler/Faraday
- Rust SDK with Cargo/reqwest/tokio

All SDKs include:
- Tensor operations (matmul, conv2d, attention)
- LLM inference with streaming support
- Model registry, pricing, and usage APIs
- Builder patterns where idiomatic
- Full type safety
2026-01-11 17:46:22 +05:30

204 lines
6.2 KiB
Swift

import Foundation
/// Multi-dimensional tensor for compute operations.
///
/// ```swift
/// // Create a 2D tensor
/// let matrix = Tensor(shape: [2, 3], data: [1, 2, 3, 4, 5, 6])
///
/// // Create random tensor
/// let random = Tensor.rand([512, 512])
///
/// // Operations
/// let mean = random.mean()
/// let transposed = matrix.transpose()
/// ```
public struct Tensor: Codable, Equatable {
public let shape: [Int]
public let data: [Double]
public let dtype: Precision
/// Total number of elements
public var size: Int {
shape.reduce(1, *)
}
/// Number of dimensions
public var ndim: Int {
shape.count
}
public init(shape: [Int], data: [Double], dtype: Precision = .fp32) {
let expectedSize = shape.reduce(1, *)
precondition(data.count == expectedSize,
"Data size \(data.count) does not match shape \(shape)")
self.shape = shape
self.data = data
self.dtype = dtype
}
/// Get element at indices
public subscript(indices: Int...) -> Double {
precondition(indices.count == shape.count, "Index dimensions must match tensor dimensions")
var idx = 0
var stride = 1
for i in (0..<shape.count).reversed() {
idx += indices[i] * stride
stride *= shape[i]
}
return data[idx]
}
/// Reshape tensor to new shape
public func reshape(_ newShape: [Int]) -> Tensor {
let newSize = newShape.reduce(1, *)
precondition(newSize == size, "Cannot reshape tensor of size \(size) to shape \(newShape)")
return Tensor(shape: newShape, data: data, dtype: dtype)
}
/// Transpose 2D tensor
public func transpose() -> Tensor {
precondition(ndim == 2, "Transpose only supported for 2D tensors")
let rows = shape[0]
let cols = shape[1]
var transposed = [Double](repeating: 0, count: data.count)
for i in 0..<rows {
for j in 0..<cols {
transposed[j * rows + i] = data[i * cols + j]
}
}
return Tensor(shape: [cols, rows], data: transposed, dtype: dtype)
}
/// Compute mean of all elements
public func mean() -> Double {
data.reduce(0, +) / Double(data.count)
}
/// Compute sum of all elements
public func sum() -> Double {
data.reduce(0, +)
}
/// Compute standard deviation
public func std() -> Double {
let meanVal = mean()
let variance = data.map { ($0 - meanVal) * ($0 - meanVal) }.reduce(0, +) / Double(data.count)
return sqrt(variance)
}
/// Find maximum value
public func max() -> Double {
data.max() ?? .nan
}
/// Find minimum value
public func min() -> Double {
data.min() ?? .nan
}
/// Apply ReLU activation
public func relu() -> Tensor {
Tensor(shape: shape, data: data.map { Swift.max(0, $0) }, dtype: dtype)
}
/// Apply sigmoid activation
public func sigmoid() -> Tensor {
Tensor(shape: shape, data: data.map { 1 / (1 + exp(-$0)) }, dtype: dtype)
}
/// Apply softmax activation
public func softmax() -> Tensor {
let maxVal = max()
let expValues = data.map { exp($0 - maxVal) }
let sum = expValues.reduce(0, +)
return Tensor(shape: shape, data: expValues.map { $0 / sum }, dtype: dtype)
}
/// Convert to nested array (1D or 2D)
public func toNestedArray() -> Any {
switch ndim {
case 1:
return data
case 2:
let rows = shape[0]
let cols = shape[1]
return (0..<rows).map { i in
(0..<cols).map { j in data[i * cols + j] }
}
default:
fatalError("toNestedArray only supports 1D and 2D tensors")
}
}
// MARK: - Factory Methods
/// Create tensor from 1D array
public static func of(_ data: [Double]) -> Tensor {
Tensor(shape: [data.count], data: data)
}
/// Create tensor from 2D array
public static func of(_ data: [[Double]]) -> Tensor {
let rows = data.count
let cols = data[0].count
let flat = data.flatMap { $0 }
return Tensor(shape: [rows, cols], data: flat)
}
/// Create tensor filled with zeros
public static func zeros(_ shape: [Int]) -> Tensor {
let size = shape.reduce(1, *)
return Tensor(shape: shape, data: [Double](repeating: 0, count: size))
}
/// Create tensor filled with ones
public static func ones(_ shape: [Int]) -> Tensor {
let size = shape.reduce(1, *)
return Tensor(shape: shape, data: [Double](repeating: 1, count: size))
}
/// Create tensor with uniform random values [0, 1)
public static func rand(_ shape: [Int]) -> Tensor {
let size = shape.reduce(1, *)
return Tensor(shape: shape, data: (0..<size).map { _ in Double.random(in: 0..<1) })
}
/// Create tensor with standard normal random values
public static func randn(_ shape: [Int]) -> Tensor {
let size = shape.reduce(1, *)
return Tensor(shape: shape, data: (0..<size).map { _ in
// Box-Muller transform
let u1 = Double.random(in: 0..<1)
let u2 = Double.random(in: 0..<1)
return sqrt(-2 * log(u1)) * cos(2 * .pi * u2)
})
}
/// Create identity matrix
public static func eye(_ n: Int) -> Tensor {
var data = [Double](repeating: 0, count: n * n)
for i in 0..<n {
data[i * n + i] = 1
}
return Tensor(shape: [n, n], data: data)
}
/// Create range tensor
public static func arange(_ start: Double, _ end: Double, _ step: Double = 1) -> Tensor {
let size = Int(ceil((end - start) / step))
return Tensor(shape: [size], data: (0..<size).map { start + Double($0) * step })
}
/// Create linearly spaced tensor
public static func linspace(_ start: Double, _ end: Double, _ num: Int) -> Tensor {
let step = (end - start) / Double(num - 1)
return Tensor(shape: [num], data: (0..<num).map { start + Double($0) * step })
}
}
extension Tensor: CustomStringConvertible {
public var description: String {
"Tensor(shape=\(shape), dtype=\(dtype.rawValue))"
}
}