//// High-performance tensor operations for Gleam on the BEAM. //// //// This module is the stable entry point for the package. It re-exports the //// tensor type, common constructors, shape operations, linear algebra, //// element-wise math, reductions, neural-network helpers, and TFLOPS //// measurement utilities. //// //// Lower-level implementation, backend, neural-network, quantization, sparse, //// telemetry, and benchmark modules are intentionally excluded from the public //// documentation until their contracts are stable. Prefer this module as the //// public API surface. //// //// ```gleam //// import gleam/result //// import viva_tensor as t //// //// let a = t.zeros([2, 3]) //// let b = t.ones([2, 3]) //// use c <- result.try(t.add(a, b)) //// c //// ``` import viva_tensor/core/ffi import viva_tensor/cuda import viva_tensor/tensor import viva_tensor/tflops as tflops_mod // --- Types ------------------------------------------------------------------ /// A tensor value backed by dense, strided, or native storage. pub type Tensor = tensor.Tensor /// Error returned by fallible tensor constructors and operations. pub type TensorError = tensor.TensorError /// Result storage selected by the RTX-first planner. pub type AcceleratedTensor = cuda.AcceleratedTensor /// Backend selected by the RTX-first planner. pub type AccelerationBackend = cuda.AccelerationBackend /// Workspace for persistent GPU buffers. pub type GpuWorkspace = cuda.GpuWorkspace /// Persisted linear layer parameters. pub type LinearLayer = cuda.LinearLayer /// Opaque reference to a tensor stored in native NIF memory. pub type NativeTensorRef = ffi.NativeTensorRef /// Configuration for two-dimensional convolution operations. pub type Conv2dConfig = tensor.Conv2dConfig // --- Constructors ----------------------------------------------------------- /// All zeros. The tensor equivalent of a blank canvas. pub fn zeros(shape: List(Int)) -> Tensor { tensor.zeros(shape) } /// Create tensor of ones pub fn ones(shape: List(Int)) -> Tensor { tensor.ones(shape) } /// Create tensor filled with value pub fn fill(shape: List(Int), value: Float) -> Tensor { tensor.fill(shape, value) } /// Create tensor from list (1D) pub fn from_list(data: List(Float)) -> Tensor { tensor.from_list(data) } /// Create 2D tensor from list of lists pub fn from_list2d(rows: List(List(Float))) -> Result(Tensor, TensorError) { tensor.from_list2d(rows) } /// Create vector (1D tensor) pub fn vector(data: List(Float)) -> Tensor { tensor.vector(data) } /// Create matrix (2D tensor) pub fn matrix( rows: Int, cols: Int, data: List(Float), ) -> Result(Tensor, TensorError) { tensor.matrix(rows, cols, data) } /// Wrap an existing native NIF tensor resource. pub fn from_native_ref(ref: NativeTensorRef, shape: List(Int)) -> Tensor { tensor.from_native_ref(ref, shape) } /// Extract the native NIF tensor resource when present. pub fn native_ref(t: Tensor) -> Result(NativeTensorRef, Nil) { tensor.native_ref(t) } /// Check whether a tensor is backed by native NIF memory. pub fn is_native(t: Tensor) -> Bool { tensor.is_native(t) } /// Create a native-backed tensor of zeros. pub fn native_zeros(shape: List(Int)) -> Result(Tensor, TensorError) { tensor.native_zeros(shape) } /// Create a native-backed tensor of ones. pub fn native_ones(shape: List(Int)) -> Result(Tensor, TensorError) { tensor.native_ones(shape) } /// Create a native-backed tensor filled with a value. pub fn native_fill( shape: List(Int), value: Float, ) -> Result(Tensor, TensorError) { tensor.native_fill(shape, value) } /// Create a native-backed tensor from row-major list data. pub fn native_from_list( data: List(Float), shape: List(Int), ) -> Result(Tensor, TensorError) { tensor.native_from_list(data, shape) } /// Move a tensor to the best persistent backend: RTX 4090 first, then MKL/CPU. pub fn to_accelerated(t: Tensor) -> Result(AcceleratedTensor, TensorError) { cuda.to_accelerated(t) } /// Upload a tensor to persistent RTX 4090 FP16 memory. pub fn to_rtx4090_fp16(t: Tensor) -> Result(AcceleratedTensor, TensorError) { cuda.to_rtx4090_fp16(t) } /// Upload a tensor to persistent RTX 4090 FP32 memory. pub fn to_rtx4090_fp32(t: Tensor) -> Result(AcceleratedTensor, TensorError) { cuda.to_rtx4090_fp32(t) } /// Create an RTX 4090 FP16 workspace. pub fn gpu_workspace() -> Result(GpuWorkspace, TensorError) { cuda.gpu_workspace() } /// Allocate a reusable zero-filled output buffer in workspace memory. pub fn workspace_zeros( workspace: GpuWorkspace, shape: List(Int), ) -> Result(AcceleratedTensor, TensorError) { cuda.workspace_zeros(workspace, shape) } /// Move a tensor into workspace memory. pub fn workspace_from_tensor( workspace: GpuWorkspace, tensor: Tensor, ) -> Result(AcceleratedTensor, TensorError) { cuda.workspace_from_tensor(workspace, tensor) } /// Create a persisted FP16 linear layer on the RTX. pub fn linear_layer_fp16( weight: Tensor, bias: Tensor, ) -> Result(LinearLayer, TensorError) { cuda.linear_layer_fp16(weight, bias) } /// Create a persisted linear layer in workspace memory. pub fn linear_layer( workspace: GpuWorkspace, weight: Tensor, bias: Tensor, ) -> Result(LinearLayer, TensorError) { cuda.linear_layer(workspace, weight, bias) } // --- Random ----------------------------------------------------------------- /// Random uniform [0, 1) pub fn random_uniform(shape: List(Int)) -> Tensor { tensor.random_uniform(shape) } /// Tensor with normal random values pub fn random_normal(shape: List(Int), mean: Float, std: Float) -> Tensor { tensor.random_normal(shape, mean, std) } /// Xavier initialization for neural network weights pub fn xavier_init(fan_in: Int, fan_out: Int) -> Tensor { tensor.xavier_init(fan_in, fan_out) } /// He initialization (for ReLU networks) pub fn he_init(fan_in: Int, fan_out: Int) -> Tensor { tensor.he_init(fan_in, fan_out) } // --- Math ------------------------------------------------------------------- /// Add element-wise pub fn add(a: Tensor, b: Tensor) -> Result(Tensor, TensorError) { tensor.add(a, b) } /// Element-wise subtraction pub fn sub(a: Tensor, b: Tensor) -> Result(Tensor, TensorError) { tensor.sub(a, b) } /// Element-wise multiplication pub fn mul(a: Tensor, b: Tensor) -> Result(Tensor, TensorError) { tensor.mul(a, b) } /// Element-wise division pub fn div(a: Tensor, b: Tensor) -> Result(Tensor, TensorError) { tensor.div(a, b) } /// Write out = a + b into a preallocated native tensor. pub fn add_into(out: Tensor, a: Tensor, b: Tensor) -> Result(Nil, TensorError) { tensor.add_into(out, a, b) } /// Write out = a - b into a preallocated native tensor. pub fn sub_into(out: Tensor, a: Tensor, b: Tensor) -> Result(Nil, TensorError) { tensor.sub_into(out, a, b) } /// Write out = a * b into a preallocated native tensor. pub fn mul_into(out: Tensor, a: Tensor, b: Tensor) -> Result(Nil, TensorError) { tensor.mul_into(out, a, b) } /// Scale by constant pub fn scale(t: Tensor, s: Float) -> Tensor { tensor.scale(t, s) } /// Write out = a * scalar into a preallocated native tensor. pub fn scale_into( out: Tensor, a: Tensor, scalar: Float, ) -> Result(Nil, TensorError) { tensor.scale_into(out, a, scalar) } /// Apply function to each element pub fn map(t: Tensor, f: fn(Float) -> Float) -> Tensor { tensor.map(t, f) } /// Apply a binary function element-wise over tensors with the same shape. pub fn map2( a: Tensor, b: Tensor, f: fn(Float, Float) -> Float, ) -> Result(Tensor, TensorError) { tensor.map2(a, b, f) } // --- Reductions ------------------------------------------------------------- /// Sum everything pub fn sum(t: Tensor) -> Float { tensor.sum(t) } /// Mean of all elements pub fn mean(t: Tensor) -> Float { tensor.mean(t) } /// Maximum value pub fn max(t: Tensor) -> Float { tensor.max(t) } /// Minimum value pub fn min(t: Tensor) -> Float { tensor.min(t) } /// Index of maximum value pub fn argmax(t: Tensor) -> Int { tensor.argmax(t) } /// Index of minimum value pub fn argmin(t: Tensor) -> Int { tensor.argmin(t) } /// Variance pub fn variance(t: Tensor) -> Float { tensor.variance(t) } /// Standard deviation pub fn std(t: Tensor) -> Float { tensor.std(t) } // --- Linear Algebra --------------------------------------------------------- /// Dot product (vectors only) pub fn dot(a: Tensor, b: Tensor) -> Result(Float, TensorError) { tensor.dot(a, b) } /// Matrix-matrix multiplication pub fn matmul(a: Tensor, b: Tensor) -> Result(Tensor, TensorError) { tensor.matmul(a, b) } /// Matrix multiplication with priority: RTX 4090 first, then MKL/native CPU. pub fn matmul_auto( a: Tensor, b: Tensor, ) -> Result(AcceleratedTensor, TensorError) { cuda.matmul_auto(a, b) } /// Matrix multiplication between persistent accelerated tensors. pub fn matmul_accelerated( a: AcceleratedTensor, b: AcceleratedTensor, ) -> Result(AcceleratedTensor, TensorError) { cuda.matmul_accelerated(a, b) } /// Write `out = a @ b` into a persistent accelerated output buffer. pub fn matmul_accelerated_into( out: AcceleratedTensor, a: AcceleratedTensor, b: AcceleratedTensor, ) -> Result(Nil, TensorError) { cuda.matmul_accelerated_into(out, a, b) } /// Write `out = relu(a @ b)` using the FP16 Tensor Core fused epilogue. pub fn matmul_relu_accelerated_into( out: AcceleratedTensor, a: AcceleratedTensor, b: AcceleratedTensor, ) -> Result(Nil, TensorError) { cuda.matmul_relu_accelerated_into(out, a, b) } /// Write `out = gelu(a @ b)` using the FP16 Tensor Core fused epilogue. pub fn matmul_gelu_accelerated_into( out: AcceleratedTensor, a: AcceleratedTensor, b: AcceleratedTensor, ) -> Result(Nil, TensorError) { cuda.matmul_gelu_accelerated_into(out, a, b) } /// Write `out = relu(a @ b + bias)` using the FP16 Tensor Core fused epilogue. pub fn linear_relu_accelerated_into( out: AcceleratedTensor, a: AcceleratedTensor, b: AcceleratedTensor, bias: AcceleratedTensor, ) -> Result(Nil, TensorError) { cuda.linear_relu_accelerated_into(out, a, b, bias) } /// Write `out = gelu(a @ b + bias)` using the FP16 Tensor Core fused epilogue. pub fn linear_gelu_accelerated_into( out: AcceleratedTensor, a: AcceleratedTensor, b: AcceleratedTensor, bias: AcceleratedTensor, ) -> Result(Nil, TensorError) { cuda.linear_gelu_accelerated_into(out, a, b, bias) } /// Allocate a reusable output buffer for a persisted linear layer. pub fn linear_output( workspace: GpuWorkspace, layer: LinearLayer, batch_size: Int, ) -> Result(AcceleratedTensor, TensorError) { cuda.linear_output(workspace, layer, batch_size) } /// Run `out = relu(input @ layer.weight + layer.bias)`. pub fn linear_relu_forward_into( out: AcceleratedTensor, input: AcceleratedTensor, layer: LinearLayer, ) -> Result(Nil, TensorError) { cuda.linear_relu_forward_into(out, input, layer) } /// Run `out = gelu(input @ layer.weight + layer.bias)`. pub fn linear_gelu_forward_into( out: AcceleratedTensor, input: AcceleratedTensor, layer: LinearLayer, ) -> Result(Nil, TensorError) { cuda.linear_gelu_forward_into(out, input, layer) } /// Download an accelerated tensor back to a regular CPU tensor. pub fn accelerated_to_tensor( t: AcceleratedTensor, ) -> Result(Tensor, TensorError) { cuda.to_cpu_tensor(t) } /// Inspect which backend was selected by `matmul_auto`. pub fn accelerated_backend(t: AcceleratedTensor) -> AccelerationBackend { cuda.backend(t) } /// Shape of an accelerated tensor without forcing a download. pub fn accelerated_shape(t: AcceleratedTensor) -> List(Int) { cuda.accelerated_shape(t) } /// Wait for queued CUDA work to complete. pub fn accelerated_sync() -> Result(Nil, TensorError) { cuda.sync() } /// Workspace backend. pub fn workspace_backend(workspace: GpuWorkspace) -> AccelerationBackend { cuda.workspace_backend(workspace) } /// Linear layer backend. pub fn linear_layer_backend(layer: LinearLayer) -> AccelerationBackend { cuda.linear_layer_backend(layer) } /// Linear layer input feature count. pub fn linear_layer_input_features(layer: LinearLayer) -> Int { cuda.linear_layer_input_features(layer) } /// Linear layer output feature count. pub fn linear_layer_output_features(layer: LinearLayer) -> Int { cuda.linear_layer_output_features(layer) } /// Write out = a @ b into a preallocated native tensor. pub fn matmul_into( out: Tensor, a: Tensor, b: Tensor, ) -> Result(Nil, TensorError) { tensor.matmul_into(out, a, b) } /// Fused linear layer with ReLU: max(0, a @ b + bias). pub fn linear_relu( a: Tensor, b: Tensor, bias: Tensor, ) -> Result(Tensor, TensorError) { tensor.linear_relu(a, b, bias) } /// Write out = max(0, a @ b + bias) into a preallocated native tensor. pub fn linear_relu_into( out: Tensor, a: Tensor, b: Tensor, bias: Tensor, ) -> Result(Nil, TensorError) { tensor.linear_relu_into(out, a, b, bias) } /// Matrix-vector multiplication pub fn matmul_vec(mat: Tensor, vec: Tensor) -> Result(Tensor, TensorError) { tensor.matmul_vec(mat, vec) } /// Matrix transpose pub fn transpose(t: Tensor) -> Result(Tensor, TensorError) { tensor.transpose(t) } /// Outer product pub fn outer(a: Tensor, b: Tensor) -> Result(Tensor, TensorError) { tensor.outer(a, b) } // --- Shape Ops -------------------------------------------------------------- /// Reshape (total size must match) pub fn reshape(t: Tensor, new_shape: List(Int)) -> Result(Tensor, TensorError) { tensor.reshape(t, new_shape) } /// Flatten to 1D pub fn flatten(t: Tensor) -> Tensor { tensor.flatten(t) } /// Remove dimensions of size 1 pub fn squeeze(t: Tensor) -> Tensor { tensor.squeeze(t) } /// Add dimension of size 1 pub fn unsqueeze(t: Tensor, axis: Int) -> Tensor { tensor.unsqueeze(t, axis) } // --- Accessors -------------------------------------------------------------- /// Shape as list of dimensions pub fn shape(t: Tensor) -> List(Int) { tensor.shape(t) } /// Get total size pub fn size(t: Tensor) -> Int { tensor.size(t) } /// Get rank (number of dimensions) pub fn rank(t: Tensor) -> Int { tensor.rank(t) } /// Convert to list pub fn to_list(t: Tensor) -> List(Float) { tensor.to_list(t) } // --- Utils ------------------------------------------------------------------ /// L2 norm (Euclidean length) pub fn norm(t: Tensor) -> Float { tensor.norm(t) } /// Normalize to unit length pub fn normalize(t: Tensor) -> Tensor { tensor.normalize(t) } /// Clamp values pub fn clamp(t: Tensor, min_val: Float, max_val: Float) -> Tensor { tensor.clamp(t, min_val, max_val) } // --- Broadcasting ----------------------------------------------------------- /// Can these shapes broadcast together? pub fn can_broadcast(a: List(Int), b: List(Int)) -> Bool { tensor.can_broadcast(a, b) } /// Broadcast tensor to a target shape. pub fn broadcast_to( t: Tensor, target_shape: List(Int), ) -> Result(Tensor, TensorError) { tensor.broadcast_to(t, target_shape) } /// Add with broadcasting pub fn add_broadcast(a: Tensor, b: Tensor) -> Result(Tensor, TensorError) { tensor.add_broadcast(a, b) } /// Multiply with broadcasting pub fn mul_broadcast(a: Tensor, b: Tensor) -> Result(Tensor, TensorError) { tensor.mul_broadcast(a, b) } // --- Strided (Zero-copy) ---------------------------------------------------- /// Convert to strided representation for O(1) element access pub fn to_strided(t: Tensor) -> Tensor { tensor.to_strided(t) } /// Convert to contiguous tensor pub fn to_contiguous(t: Tensor) -> Tensor { tensor.to_contiguous(t) } /// Zero-copy transpose pub fn transpose_strided(t: Tensor) -> Result(Tensor, TensorError) { tensor.transpose_strided(t) } /// Check if contiguous pub fn is_contiguous(t: Tensor) -> Bool { tensor.is_contiguous(t) } // --- CNN Ops ---------------------------------------------------------------- // Convolution: the operation that made deep learning work for images. // Yann LeCun et al. (1989) showed CNNs could recognize handwritten digits. // 35 years later, we're using the same basic operation to generate cat pics. /// Default conv2d config (3x3 kernel, stride 1, no padding) pub fn conv2d_config() -> Conv2dConfig { tensor.conv2d_config() } /// Conv2d config with "same" padding pub fn conv2d_same(kernel_h: Int, kernel_w: Int) -> Conv2dConfig { tensor.conv2d_same(kernel_h, kernel_w) } /// 2D Convolution pub fn conv2d( input: Tensor, kernel: Tensor, config: Conv2dConfig, ) -> Result(Tensor, TensorError) { tensor.conv2d(input, kernel, config) } /// Pad 2D tensor with zeros pub fn pad2d(t: Tensor, pad_h: Int, pad_w: Int) -> Result(Tensor, TensorError) { tensor.pad2d(t, pad_h, pad_w) } /// Pad 4D tensor with zeros pub fn pad4d(t: Tensor, pad_h: Int, pad_w: Int) -> Result(Tensor, TensorError) { tensor.pad4d(t, pad_h, pad_w) } /// Max pooling 2D pub fn max_pool2d( input: Tensor, pool_h: Int, pool_w: Int, stride_h: Int, stride_w: Int, ) -> Result(Tensor, TensorError) { tensor.max_pool2d(input, pool_h, pool_w, stride_h, stride_w) } /// Average pooling 2D pub fn avg_pool2d( input: Tensor, pool_h: Int, pool_w: Int, stride_h: Int, stride_w: Int, ) -> Result(Tensor, TensorError) { tensor.avg_pool2d(input, pool_h, pool_w, stride_h, stride_w) } /// Global average pooling pub fn global_avg_pool2d(input: Tensor) -> Result(Tensor, TensorError) { tensor.global_avg_pool2d(input) } // --- TFLOPS Benchmarking ---------------------------------------------------- /// Backend used when measuring matrix-multiplication throughput. pub type TflopsBackend = tflops_mod.Backend /// Result returned by TFLOPS measurement helpers. pub type TflopsResult = tflops_mod.TflopsResult /// Measure TFLOPS for a single matmul operation pub fn measure_tflops( backend: TflopsBackend, m: Int, n: Int, k: Int, ) -> TflopsResult { tflops_mod.measure_matmul(backend, m, n, k) } /// Measure averaged TFLOPS (warmup + iterations) pub fn measure_tflops_averaged( backend: TflopsBackend, m: Int, n: Int, k: Int, iterations: Int, ) -> TflopsResult { tflops_mod.measure_matmul_averaged(backend, m, n, k, iterations) } /// Detect available compute backends pub fn detect_backends() -> List(TflopsBackend) { tflops_mod.detect_backends() }