Swift · Tensors and Views

Tensors and Views

import NumKong 8.0.0-rc.1 · b1f866ab

Tensor<T> is the owning two-dimensional type. It allocates its own buffer, handles deallocation on deinit, and produces non-owning views and spans without nesting withUnsafeBufferPointer closures.

import NumKong

// From an existing array:
let t = try Tensor<Float32>.fromArray([1, 2, 3, 4, 5, 6], rows: 2, columns: 3)

// Zero-initialized:
let z = try Tensor<Float32>.zeros(rows: 4, columns: 768)

// Constant fill:
let c = try Tensor<Float32>.full(rows: 4, columns: 768, value: 1.0)

// Subscript access:
let v = t[0, 2] // row 0, col 2

// Row buffer access:
let row1 = t.row(1) // UnsafeBufferPointer<Float32>

// Non-owning views:
let view: MatrixView<Float32>  = t.view()  // immutable
let span: MatrixSpan<Float32>  = t.span()  // mutable

Tensor<T> is fixed-capacity resizable. capacity reports the allocated element ceiling. tryResize(rows:columns:) reshapes within capacity without moving storage, so an existing view(), span(), or row() stays valid. reserve(_:) may reallocate to grow, which invalidates any view, span, or row pointer obtained earlier. clear() empties the tensor while keeping capacity.

The view/span split is the same aliasing discipline used throughout the binding. MatrixView is non-owning and immutable. MatrixSpan is non-owning and mutable. Neither allocates.

The ownership model is explicit:

  • MatrixView<Element> is a non-owning immutable view.
  • MatrixSpan<Element> is a non-owning mutable view.
  • PackedMatrix<Element> owns one internal packed buffer and deallocates it on deinit.
  • Tensor<Element> owns its element storage and deallocates it on deinit.

PackedMatrix allocates its internal payload with UnsafeMutableRawPointer.allocate(byteCount:alignment:). The alignment is 64 bytes for the owned packed buffer. That does not mean your source matrix must be aligned. Packing accepts ordinary Swift-managed buffers and handles the internal layout itself.

The Tensor API eliminates the nested closure structure required when working directly with Swift’s withUnsafeBufferPointer. The difference in call-site verbosity is significant for anything more than a single kernel call:

// Without Tensor — three nested closures just to call one kernel
try a.withUnsafeBufferPointer { aPtr in
    try b.withUnsafeBufferPointer { bPtr in
        try out.withUnsafeMutableBufferPointer { outPtr in
            let aView = MatrixView(baseAddress: aPtr.baseAddress!, rows: 2, columns: 3)
            let bView = MatrixView(baseAddress: bPtr.baseAddress!, rows: 2, columns: 3)
            var cSpan = MatrixSpan(baseAddress: outPtr.baseAddress!, rows: 2, columns: 2)
            let packed = try PackedMatrix<Float32>(packing: bView)
            try dotsPacked(aView, packed, &cSpan)
        }
    }
}

// With Tensor — no closures at the call site
let a = try Tensor<Float32>.fromArray([1, 2, 3, 4, 5, 6], rows: 2, columns: 3)
let b = try Tensor<Float32>.fromArray([7, 8, 9, 1, 0, 1], rows: 2, columns: 3)
let packed = try b.packForDots()
let c = try a.dotsPacked(packed) // returns Tensor<Float64>

API

StructMatrixViewNon-owning, immutable view over a row-major matrix stored in contiguous memory.5
Matrix.swift:11 ↗
struct MatrixView<Element>
baseAddress
Matrix.swift:12 ↗
let baseAddress: UnsafePointer<Element>
rows
Matrix.swift:13 ↗
let rows: Int
columns
Matrix.swift:14 ↗
let columns: Int
rowStrideBytes
Matrix.swift:15 ↗
let rowStrideBytes: Int
init
Matrix.swift:18 ↗
init(baseAddress: UnsafePointer<Element>, rows: Int, columns: Int, rowStrideBytes: Int? = nil)
StructMatrixSpanNon-owning, mutable view over a row-major matrix stored in contiguous memory.5
Matrix.swift:32 ↗
struct MatrixSpan<Element>
baseAddress
Matrix.swift:33 ↗
let baseAddress: UnsafeMutablePointer<Element>
rows
Matrix.swift:34 ↗
let rows: Int
columns
Matrix.swift:35 ↗
let columns: Int
rowStrideBytes
Matrix.swift:36 ↗
let rowStrideBytes: Int
init
Matrix.swift:39 ↗
init(baseAddress: UnsafeMutablePointer<Element>, rows: Int, columns: Int, rowStrideBytes: Int? = nil)
ClassTensorOwning, row-major dense matrix that deallocates its memory on deinit.24
Tensor.swift:13 ↗
final class Tensor<Element>

Conforms to

Sendable
rows
Tensor.swift:16 ↗
var rows: Int { get }
columns
Tensor.swift:17 ↗
var columns: Int { get }
capacityAllocated element capacity (>= count) — the ceiling tryResize honors.
Tensor.swift:19 ↗
var capacity: Int { get }
count
Tensor.swift:20 ↗
var count: Int { get }
shape
Tensor.swift:21 ↗
var shape: (rows: Int, columns: Int) { get }
fromArrayCreates a tensor by copying elements from a flat array into owned memory.
Tensor.swift:36 ↗
static func fromArray(_ data: [Element], rows: Int, columns: Int) throws -> Tensor
fullCreates a tensor filled with a repeating value.
Tensor.swift:49 ↗
static func full(rows: Int, columns: Int, value: Element) throws -> Tensor
*× 4
Tensor.swift:58 ↗
func view() -> MatrixView<Element>
Symbol
view
span
subscript
row
tryResizeResizes within the allocated capacity without moving storage, so existing view(), span(), and row() results stay valid.
Tensor.swift:86 ↗
@discardableResult func tryResize(rows newRows: Int, columns newColumns: Int) -> Bool

Returns

false, leaving the tensor unchanged, if either extent is negative or rows * columns exceeds capacity; call reserve(_:) first to grow.
reserveGrows capacity to at least minimumCapacity, reallocating and copying the live elements.
Tensor.swift:98 ↗
func reserve(_ minimumCapacity: Int)

A no-op when already large enough.

Warning

This may move storage, invalidating any outstanding view(), span(), or row() pointers obtained before the call.

clearResets to an empty 0×0 tensor while keeping the allocated capacity.
Tensor.swift:108 ↗
func clear()
zerosCreates a zero-filled tensor.
Tensor.swift:130 ↗
static func zeros(
    rows: Int,
    columns: Int
) throws -> Tensor where Element : ExpressibleByIntegerLiteral
packForDotsPacks this tensor into a kernel-optimized layout for batch dot products.
Tensor.swift:139 ↗
func packForDots() throws -> PackedMatrix<Element> where Element : NumKongDotsMatrixElement
dotsPackedComputes dot products between this tensor's rows and a packed matrix, owning the result.
Tensor.swift:144 ↗
func dotsPacked(
    _ packed: PackedMatrix<Element>
) throws -> Tensor<Element.DotsOutput> where Element : NumKongDotsMatrixElement
dotsSymmetricComputes the symmetric dot-product matrix for all row pairs, returning an owned result.
Tensor.swift:152 ↗
func dotsSymmetric(
    rowsBegin: Int = 0,
    rowsEnd: Int? = nil
) throws -> Tensor<Element.DotsOutput> where Element : NumKongDotsMatrixElement
angularsPackedComputes angular distances between this tensor's rows and a packed matrix.
Tensor.swift:164 ↗
func angularsPacked(
    _ packed: PackedMatrix<Element>
) throws -> Tensor<Element.SpatialOutput> where Element : NumKongSpatialsMatrixElement
euclideansPackedComputes Euclidean distances between this tensor's rows and a packed matrix.
Tensor.swift:172 ↗
func euclideansPacked(
    _ packed: PackedMatrix<Element>
) throws -> Tensor<Element.SpatialOutput> where Element : NumKongSpatialsMatrixElement
angularsSymmetricComputes the symmetric angular-distance matrix for all row pairs.
Tensor.swift:180 ↗
func angularsSymmetric(
    rowsBegin: Int = 0,
    rowsEnd: Int? = nil
) throws -> Tensor<Element.SpatialOutput> where Element : NumKongSpatialsMatrixElement
euclideansSymmetricComputes the symmetric Euclidean-distance matrix for all row pairs.
Tensor.swift:188 ↗
func euclideansSymmetric(
    rowsBegin: Int = 0,
    rowsEnd: Int? = nil
) throws -> Tensor<Element.SpatialOutput> where Element : NumKongSpatialsMatrixElement
hammingsPackedComputes Hamming distances between this tensor's rows and a packed matrix.
Tensor.swift:200 ↗
func hammingsPacked(
    _ packed: PackedMatrix<Element>
) throws -> Tensor<Element.HammingOutput> where Element : NumKongSetsMatrixElement
hammingsSymmetricComputes the symmetric Hamming-distance matrix for all row pairs.
Tensor.swift:208 ↗
func hammingsSymmetric(
    rowsBegin: Int = 0,
    rowsEnd: Int? = nil
) throws -> Tensor<Element.HammingOutput> where Element : NumKongSetsMatrixElement
jaccardsPackedComputes Jaccard distances between this tensor's rows and a packed matrix.
Tensor.swift:216 ↗
func jaccardsPacked(
    _ packed: PackedMatrix<Element>
) throws -> Tensor<Element.JaccardOutput> where Element : NumKongSetsMatrixElement
jaccardsSymmetricComputes the symmetric Jaccard-distance matrix for all row pairs.
Tensor.swift:224 ↗
func jaccardsSymmetric(
    rowsBegin: Int = 0,
    rowsEnd: Int? = nil
) throws -> Tensor<Element.JaccardOutput> where Element : NumKongSetsMatrixElement
maxSimPackPacks this tensor into a MaxSim-optimized layout for late-interaction scoring.
Tensor.swift:236 ↗
func maxSimPack() throws -> MaxSimPackedMatrix<Element> where Element : NumKongMaxSimElement
ProtocolRandomAccessCollection6
extension RandomAccessCollection
dotComputes the SIMD-accelerated dot product with another sequence.
Scalar.swift:876 ↗
func dot<B>(
    _ b: B
) -> Element.DotOutput? where B : Sequence, Self.Element == B.Element, Self.Element : NumKongDot
angularComputes the SIMD-accelerated angular, or cosine, distance to another sequence.
Scalar.swift:885 ↗
func angular<B>(
    _ b: B
) -> Element.AngularOutput? where B : Sequence, Self.Element == B.Element, Self.Element : NumKongAngular
euclideanComputes the SIMD-accelerated Euclidean distance to another sequence.
Scalar.swift:894 ↗
func euclidean<B>(
    _ b: B
) -> Element.EuclideanOutput? where B : Sequence, Self.Element == B.Element, Self.Element : NumKongEuclidean
sqeuclideanComputes the SIMD-accelerated squared Euclidean distance to another sequence.
Scalar.swift:903 ↗
func sqeuclidean<B>(
    _ b: B
) -> Element.SqEuclideanOutput? where B : Sequence, Self.Element == B.Element, Self.Element : NumKongSqEuclidean
hammingComputes the SIMD-accelerated Hamming distance to another sequence.
Scalar.swift:912 ↗
func hamming<B>(
    _ b: B
) -> Element.HammingOutput? where B : Sequence, Self.Element == B.Element, Self.Element : NumKongHamming
jaccardComputes the SIMD-accelerated Jaccard distance to another sequence.
Scalar.swift:921 ↗
func jaccard<B>(
    _ b: B
) -> Element.JaccardOutput? where B : Sequence, Self.Element == B.Element, Self.Element : NumKongJaccard