Packages

Tensor library for Gleam/BEAM with a pure Gleam API, zero-copy views, and optional native acceleration

Retired package: Release invalid

Current section

Files

Jump to
viva_tensor src viva_tensor@core@ffi.erl
Raw

src/viva_tensor@core@ffi.erl

-module(viva_tensor@core@ffi).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_tensor/core/ffi.gleam").
-export([abs/1, list_to_array/1, array_get/2, array_size/1, array_to_list/1, array_dot/2, array_matmul/5, array_sum/1, array_scale/2, sqrt/1, log/1, exp/1, cos/1, sin/1, tan/1, tanh/1, pow/2, random_uniform/0, is_nif_loaded/0, nif_backend_info/0, nif_matmul/5, nif_dot/2, nif_sum/1, nif_scale/2, now_microseconds/0, nt_zeros/1, nt_ones/1, nt_fill/2, nt_from_list/2, nt_to_list/1, nt_shape/1, nt_size/1, nt_add/2, nt_sub/2, nt_mul/2, nt_scale/2, nt_negate/1, nt_dot/2, nt_sum/1, nt_max/1, nt_min/1, nt_matmul/5, nt_transpose/1, nt_relu/1, nt_sigmoid/1, nt_exp/1, nt_log/1, nt_add_mut/2, nt_scale_mut/2, nt_negate_mut/1, nt_relu_mut/1, nt_saturn_blend/3, nt_fused_linear_relu/6, nt_resonance_mul/2, nt_resonance_power/2, zig_is_loaded/0, zig_backend_info/0, zig_dot/2, zig_sum/1, zig_scale/2, zig_add/2, zig_mul/2, zig_matmul/5, lns_from_f64/1, lns_to_f64/1, lns_mul/2, lns_mul_corrected/2, lns_div/2, lns_sqrt/1, lns_rsqrt/1, horde_create/2, horde_set_positions/2, horde_set_velocities/2, horde_integrate/2, horde_dampen/2, horde_wrap/2, horde_get_positions/1, horde_get_velocities/1, horde_count/1, horde_kinetic_energy/1, hdc_create/1, hdc_random/2, hdc_bind/2, hdc_similarity/2, hdc_permute/2, hdc_dim/1, nt_matmul_nf4/7, ct_from_list/2, ct_to_list/1, ct_shape/1, ct_matmul/5, ct16_available/0, ct16_from_list/2, ct16_to_list/1, ct16_shape/1, ct16_matmul/5, ct_int8_available/0, ct_int8_from_list/2, ct_int8_to_list/1, ct_int8_shape/1, ct_int8_matmul/5, sparse_available/0, sparse_from_ct16/1, sparse_shape/1, sparse_compression_ratio/1, sparse_matmul/5]).
-export_type([erlang_array/0, native_tensor_ref/0, lns_tensor_ref/0, horde_ref/0, hdc_vector_ref/0, cuda_tensor_ref/0, cuda_tensor16_ref/0, cuda_int8_tensor_ref/0, sparse_tensor_ref/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" FFI - Foreign Function Interface to Erlang\n"
"\n"
" The escape hatch from pure functional bliss into the world of\n"
" mutable arrays and hardware-specific optimizations.\n"
"\n"
" Why we need this:\n"
" 1. Erlang lists are O(n) for random access. That's death for matrix ops.\n"
" 2. Erlang's :array gives us O(1) access (technically O(log32 n), close enough).\n"
" 3. Native NIFs unlock SIMD, BLAS, and GPU backends.\n"
"\n"
" ## Performance Hierarchy (fastest to slowest)\n"
"\n"
" 1. Zig SIMD NIF: Hand-tuned SIMD for the hot paths. 10-100x vs pure Gleam.\n"
" 2. Apple Accelerate NIF: cblas_dgemm on macOS. Ridiculously optimized.\n"
" 3. Erlang :array: O(1) access, pure Erlang. 10-50x vs lists for matmul.\n"
" 4. Pure Gleam lists: Beautiful, correct, slow. Fine for small tensors.\n"
"\n"
" ## Architecture\n"
"\n"
" We have three acceleration backends that we auto-select from:\n"
" - viva_tensor_zig: Portable SIMD via Zig. Works everywhere Zig compiles.\n"
" - viva_tensor_nif: Apple Accelerate on macOS (cblas, vDSP).\n"
" - viva_tensor_ffi: Pure Erlang fallback. Always works, just slower.\n"
"\n"
" The ops module auto-selects the best available backend at runtime.\n"
).
-type erlang_array() :: any().
-type native_tensor_ref() :: any().
-type lns_tensor_ref() :: any().
-type horde_ref() :: any().
-type hdc_vector_ref() :: any().
-type cuda_tensor_ref() :: any().
-type cuda_tensor16_ref() :: any().
-type cuda_int8_tensor_ref() :: any().
-type sparse_tensor_ref() :: any().
-file("src/viva_tensor/core/ffi.gleam", 243).
?DOC(
" Absolute value.\n"
"\n"
" Implemented in pure Gleam because :math.abs/1 doesn't exist\n"
" and erlang:abs/1 is polymorphic (returns same type as input).\n"
).
-spec abs(float()) -> float().
abs(X) ->
case X < +0.0 of
true ->
+0.0 - X;
false ->
X
end.
-file("src/viva_tensor/core/ffi.gleam", 56).
?DOC(
" Convert list to Erlang array for O(1) access.\n"
"\n"
" O(n) to build, but subsequent access is O(1).\n"
" Worth it for any tensor you'll index more than once.\n"
).
-spec list_to_array(list(float())) -> erlang_array().
list_to_array(Lst) ->
viva_tensor_ffi:list_to_array(Lst).
-file("src/viva_tensor/core/ffi.gleam", 65).
?DOC(
" Get element from array at index - O(1).\n"
"\n"
" Contrast with list indexing: O(n).\n"
" For a 1000-element matmul (1000 iterations, each indexing both inputs),\n"
" that's 2M list traversals vs 2K array lookups. Huge difference.\n"
).
-spec array_get(erlang_array(), integer()) -> float().
array_get(Arr, Index) ->
viva_tensor_ffi:array_get(Arr, Index).
-file("src/viva_tensor/core/ffi.gleam", 70).
?DOC(" Get array size - O(1).\n").
-spec array_size(erlang_array()) -> integer().
array_size(Arr) ->
viva_tensor_ffi:array_size(Arr).
-file("src/viva_tensor/core/ffi.gleam", 78).
?DOC(
" Convert array back to list - O(n).\n"
"\n"
" Use this for final output or when you need list operations.\n"
" Try to stay in array-land as long as possible for hot paths.\n"
).
-spec array_to_list(erlang_array()) -> list(float()).
array_to_list(Arr) ->
viva_tensor_ffi:array_to_list(Arr).
-file("src/viva_tensor/core/ffi.gleam", 92).
?DOC(
" Dot product using Erlang arrays.\n"
"\n"
" Performance: ~10-50x faster than list-based for large vectors.\n"
" The speedup comes entirely from O(1) vs O(n) element access.\n"
).
-spec array_dot(erlang_array(), erlang_array()) -> float().
array_dot(A, B) ->
viva_tensor_ffi:array_dot(A, B).
-file("src/viva_tensor/core/ffi.gleam", 105).
?DOC(
" Matrix multiplication using Erlang arrays.\n"
"\n"
" C[m,n] = A[m,k] @ B[k,n]\n"
"\n"
" Naive O(mnk) algorithm but with O(1) element access.\n"
" For 100x100 matrices: ~50x faster than list-based.\n"
"\n"
" For serious work, use the Zig SIMD or Accelerate NIF backends.\n"
" This is the reliable fallback that works everywhere.\n"
).
-spec array_matmul(
erlang_array(),
erlang_array(),
integer(),
integer(),
integer()
) -> erlang_array().
array_matmul(A, B, M, N, K) ->
viva_tensor_ffi:array_matmul(A, B, M, N, K).
-file("src/viva_tensor/core/ffi.gleam", 116).
?DOC(" Sum all elements - O(n).\n").
-spec array_sum(erlang_array()) -> float().
array_sum(Arr) ->
viva_tensor_ffi:array_sum(Arr).
-file("src/viva_tensor/core/ffi.gleam", 121).
?DOC(" Scale all elements by scalar - O(n).\n").
-spec array_scale(erlang_array(), float()) -> erlang_array().
array_scale(Arr, Scalar) ->
viva_tensor_ffi:array_scale(Arr, Scalar).
-file("src/viva_tensor/core/ffi.gleam", 191).
?DOC(" Square root - wraps :math.sqrt/1\n").
-spec sqrt(float()) -> float().
sqrt(X) ->
math:sqrt(X).
-file("src/viva_tensor/core/ffi.gleam", 199).
?DOC(
" Natural logarithm - wraps :math.log/1\n"
"\n"
" Undefined for x <= 0. Erlang will return -inf for 0, NaN for negative.\n"
" Caller's responsibility to check input.\n"
).
-spec log(float()) -> float().
log(X) ->
math:log(X).
-file("src/viva_tensor/core/ffi.gleam", 207).
?DOC(
" Exponential e^x - wraps :math.exp/1\n"
"\n"
" Watch for overflow: exp(710) = inf in Float64.\n"
" For softmax, subtract max first: exp(x - max(x)).\n"
).
-spec exp(float()) -> float().
exp(X) ->
math:exp(X).
-file("src/viva_tensor/core/ffi.gleam", 212).
?DOC(" Cosine - wraps :math.cos/1\n").
-spec cos(float()) -> float().
cos(X) ->
math:cos(X).
-file("src/viva_tensor/core/ffi.gleam", 217).
?DOC(" Sine - wraps :math.sin/1\n").
-spec sin(float()) -> float().
sin(X) ->
math:sin(X).
-file("src/viva_tensor/core/ffi.gleam", 222).
?DOC(" Tangent - wraps :math.tan/1\n").
-spec tan(float()) -> float().
tan(X) ->
math:tan(X).
-file("src/viva_tensor/core/ffi.gleam", 230).
?DOC(
" Hyperbolic tangent - wraps :math.tanh/1\n"
"\n"
" Range: (-1, 1). Saturates for |x| > ~20.\n"
" Used in some activation functions, though ReLU dominates now.\n"
).
-spec tanh(float()) -> float().
tanh(X) ->
math:tanh(X).
-file("src/viva_tensor/core/ffi.gleam", 235).
?DOC(" Power x^y - wraps :math.pow/2\n").
-spec pow(float(), float()) -> float().
pow(X, Y) ->
math:pow(X, Y).
-file("src/viva_tensor/core/ffi.gleam", 261).
?DOC(
" Uniform random float in [0, 1).\n"
"\n"
" Uses Erlang's per-process PRNG (Xoroshiro116+ by default).\n"
" Not suitable for cryptography, but fine for ML initialization.\n"
"\n"
" For reproducible results, seed with :rand.seed(Algorithm, Seed).\n"
).
-spec random_uniform() -> float().
random_uniform() ->
rand:uniform().
-file("src/viva_tensor/core/ffi.gleam", 140).
?DOC(
" Check if the Apple Accelerate NIF is loaded.\n"
"\n"
" Returns True on macOS with the NIF built, False elsewhere.\n"
" Use this to decide whether to use nif_* functions or fall back.\n"
).
-spec is_nif_loaded() -> boolean().
is_nif_loaded() ->
viva_tensor_nif:is_nif_loaded().
-file("src/viva_tensor/core/ffi.gleam", 147).
?DOC(
" Get backend info string for debugging.\n"
"\n"
" Returns something like \"Apple Accelerate (cblas_dgemm, vDSP)\" on macOS.\n"
).
-spec nif_backend_info() -> binary().
nif_backend_info() ->
viva_tensor_nif:backend_info().
-file("src/viva_tensor/core/ffi.gleam", 157).
?DOC(
" NIF-accelerated matrix multiplication via cblas_dgemm.\n"
"\n"
" This is where the magic happens on macOS. Apple has spent years\n"
" optimizing BLAS for their chips. We just call their code.\n"
"\n"
" Falls back to pure Erlang if NIF not available.\n"
).
-spec nif_matmul(list(float()), list(float()), integer(), integer(), integer()) -> {ok,
list(float())} |
{error, binary()}.
nif_matmul(A, B, M, N, K) ->
viva_tensor_nif:matmul(A, B, M, N, K).
-file("src/viva_tensor/core/ffi.gleam", 168).
?DOC(" NIF-accelerated dot product via vDSP.\n").
-spec nif_dot(list(float()), list(float())) -> {ok, float()} | {error, binary()}.
nif_dot(A, B) ->
viva_tensor_nif:dot(A, B).
-file("src/viva_tensor/core/ffi.gleam", 173).
?DOC(" NIF-accelerated sum via vDSP.\n").
-spec nif_sum(list(float())) -> {ok, float()} | {error, binary()}.
nif_sum(Data) ->
viva_tensor_nif:sum(Data).
-file("src/viva_tensor/core/ffi.gleam", 178).
?DOC(" NIF-accelerated scale via vDSP.\n").
-spec nif_scale(list(float()), float()) -> {ok, list(float())} |
{error, binary()}.
nif_scale(Data, Scalar) ->
viva_tensor_nif:scale(Data, Scalar).
-file("src/viva_tensor/core/ffi.gleam", 373).
?DOC(
" Get current time in microseconds.\n"
"\n"
" Use for benchmarking: before/after difference gives wall-clock time.\n"
" For production profiling, use Erlang's :fprof or :eprof instead.\n"
).
-spec now_microseconds() -> integer().
now_microseconds() ->
viva_tensor_ffi:now_microseconds().
-file("src/viva_tensor/core/ffi.gleam", 459).
?DOC(" Create native tensor of zeros\n").
-spec nt_zeros(list(integer())) -> {ok, native_tensor_ref()} | {error, binary()}.
nt_zeros(Shape) ->
viva_tensor_zig:nt_zeros(Shape).
-file("src/viva_tensor/core/ffi.gleam", 464).
?DOC(" Create native tensor of ones\n").
-spec nt_ones(list(integer())) -> {ok, native_tensor_ref()} | {error, binary()}.
nt_ones(Shape) ->
viva_tensor_zig:nt_ones(Shape).
-file("src/viva_tensor/core/ffi.gleam", 469).
?DOC(" Create native tensor filled with value\n").
-spec nt_fill(list(integer()), float()) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_fill(Shape, Value) ->
viva_tensor_zig:nt_fill(Shape, Value).
-file("src/viva_tensor/core/ffi.gleam", 477).
?DOC(" Create native tensor from list data + shape\n").
-spec nt_from_list(list(float()), list(integer())) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_from_list(Data, Shape) ->
viva_tensor_zig:nt_from_list(Data, Shape).
-file("src/viva_tensor/core/ffi.gleam", 485).
?DOC(" Extract data as list (one-time conversion at boundaries)\n").
-spec nt_to_list(native_tensor_ref()) -> {ok, list(float())} | {error, binary()}.
nt_to_list(Ref) ->
viva_tensor_zig:nt_to_list(Ref).
-file("src/viva_tensor/core/ffi.gleam", 490).
?DOC(" Get shape from native tensor\n").
-spec nt_shape(native_tensor_ref()) -> {ok, list(integer())} | {error, binary()}.
nt_shape(Ref) ->
viva_tensor_zig:nt_shape(Ref).
-file("src/viva_tensor/core/ffi.gleam", 495).
?DOC(" Get total element count\n").
-spec nt_size(native_tensor_ref()) -> {ok, integer()} | {error, binary()}.
nt_size(Ref) ->
viva_tensor_zig:nt_size(Ref).
-file("src/viva_tensor/core/ffi.gleam", 500).
?DOC(" Native add: ref + ref → ref (zero copy)\n").
-spec nt_add(native_tensor_ref(), native_tensor_ref()) -> {ok,
native_tensor_ref()} |
{error, binary()}.
nt_add(A, B) ->
viva_tensor_zig:nt_add(A, B).
-file("src/viva_tensor/core/ffi.gleam", 508).
?DOC(" Native sub: ref - ref → ref\n").
-spec nt_sub(native_tensor_ref(), native_tensor_ref()) -> {ok,
native_tensor_ref()} |
{error, binary()}.
nt_sub(A, B) ->
viva_tensor_zig:nt_sub(A, B).
-file("src/viva_tensor/core/ffi.gleam", 516).
?DOC(" Native element-wise mul: ref * ref → ref\n").
-spec nt_mul(native_tensor_ref(), native_tensor_ref()) -> {ok,
native_tensor_ref()} |
{error, binary()}.
nt_mul(A, B) ->
viva_tensor_zig:nt_mul(A, B).
-file("src/viva_tensor/core/ffi.gleam", 524).
?DOC(" Native scale: ref * scalar → ref\n").
-spec nt_scale(native_tensor_ref(), float()) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_scale(A, Scalar) ->
viva_tensor_zig:nt_scale(A, Scalar).
-file("src/viva_tensor/core/ffi.gleam", 532).
?DOC(" Native negate: -ref → ref\n").
-spec nt_negate(native_tensor_ref()) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_negate(A) ->
viva_tensor_zig:nt_negate(A).
-file("src/viva_tensor/core/ffi.gleam", 537).
?DOC(" Native dot product: ref · ref → scalar\n").
-spec nt_dot(native_tensor_ref(), native_tensor_ref()) -> {ok, float()} |
{error, binary()}.
nt_dot(A, B) ->
viva_tensor_zig:nt_dot(A, B).
-file("src/viva_tensor/core/ffi.gleam", 542).
?DOC(" Native sum reduction → scalar\n").
-spec nt_sum(native_tensor_ref()) -> {ok, float()} | {error, binary()}.
nt_sum(A) ->
viva_tensor_zig:nt_sum(A).
-file("src/viva_tensor/core/ffi.gleam", 547).
?DOC(" Native max → scalar\n").
-spec nt_max(native_tensor_ref()) -> {ok, float()} | {error, binary()}.
nt_max(A) ->
viva_tensor_zig:nt_max(A).
-file("src/viva_tensor/core/ffi.gleam", 552).
?DOC(" Native min → scalar\n").
-spec nt_min(native_tensor_ref()) -> {ok, float()} | {error, binary()}.
nt_min(A) ->
viva_tensor_zig:nt_min(A).
-file("src/viva_tensor/core/ffi.gleam", 557).
?DOC(" Native matmul: [m,k] @ [k,n] → [m,n] in native memory\n").
-spec nt_matmul(
native_tensor_ref(),
native_tensor_ref(),
integer(),
integer(),
integer()
) -> {ok, native_tensor_ref()} | {error, binary()}.
nt_matmul(A, B, M, N, K) ->
viva_tensor_zig:nt_matmul(A, B, M, N, K).
-file("src/viva_tensor/core/ffi.gleam", 568).
?DOC(" Native transpose: [m,n] → [n,m] contiguous copy\n").
-spec nt_transpose(native_tensor_ref()) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_transpose(A) ->
viva_tensor_zig:nt_transpose(A).
-file("src/viva_tensor/core/ffi.gleam", 573).
?DOC(" Native ReLU activation\n").
-spec nt_relu(native_tensor_ref()) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_relu(A) ->
viva_tensor_zig:nt_relu(A).
-file("src/viva_tensor/core/ffi.gleam", 578).
?DOC(" Native sigmoid activation\n").
-spec nt_sigmoid(native_tensor_ref()) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_sigmoid(A) ->
viva_tensor_zig:nt_sigmoid(A).
-file("src/viva_tensor/core/ffi.gleam", 583).
?DOC(" Native exp\n").
-spec nt_exp(native_tensor_ref()) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_exp(A) ->
viva_tensor_zig:nt_exp(A).
-file("src/viva_tensor/core/ffi.gleam", 588).
?DOC(" Native log\n").
-spec nt_log(native_tensor_ref()) -> {ok, native_tensor_ref()} |
{error, binary()}.
nt_log(A) ->
viva_tensor_zig:nt_log(A).
-file("src/viva_tensor/core/ffi.gleam", 599).
?DOC(" In-place add: a += b. Returns ok. MUTATES a.\n").
-spec nt_add_mut(native_tensor_ref(), native_tensor_ref()) -> {ok, nil} |
{error, binary()}.
nt_add_mut(A, B) ->
viva_tensor_zig:nt_add_mut(A, B).
-file("src/viva_tensor/core/ffi.gleam", 604).
?DOC(" In-place scale: a *= scalar. Returns ok. MUTATES a.\n").
-spec nt_scale_mut(native_tensor_ref(), float()) -> {ok, nil} |
{error, binary()}.
nt_scale_mut(A, Scalar) ->
viva_tensor_zig:nt_scale_mut(A, Scalar).
-file("src/viva_tensor/core/ffi.gleam", 609).
?DOC(" In-place negate: a = -a. Returns ok. MUTATES a.\n").
-spec nt_negate_mut(native_tensor_ref()) -> {ok, nil} | {error, binary()}.
nt_negate_mut(A) ->
viva_tensor_zig:nt_negate_mut(A).
-file("src/viva_tensor/core/ffi.gleam", 614).
?DOC(" In-place ReLU: a = max(0, a). Returns ok. MUTATES a.\n").
-spec nt_relu_mut(native_tensor_ref()) -> {ok, nil} | {error, binary()}.
nt_relu_mut(A) ->
viva_tensor_zig:nt_relu_mut(A).
-file("src/viva_tensor/core/ffi.gleam", 622).
?DOC(
" Saturn Blend: result = texture + (shade - bias)\n"
" VDP1-inspired lighting with pure SIMD addition.\n"
).
-spec nt_saturn_blend(native_tensor_ref(), native_tensor_ref(), float()) -> {ok,
native_tensor_ref()} |
{error, binary()}.
nt_saturn_blend(Texture, Shade, Bias) ->
viva_tensor_zig:nt_saturn_blend(Texture, Shade, Bias).
-file("src/viva_tensor/core/ffi.gleam", 632).
?DOC(
" Fused MatMul + Bias + ReLU: C = max(0, A@B + bias)\n"
" Single pass, saves 2 full tensor traversals.\n"
).
-spec nt_fused_linear_relu(
native_tensor_ref(),
native_tensor_ref(),
native_tensor_ref(),
integer(),
integer(),
integer()
) -> {ok, native_tensor_ref()} | {error, binary()}.
nt_fused_linear_relu(A, B, Bias, M, N, K) ->
viva_tensor_zig:nt_fused_linear_relu(A, B, Bias, M, N, K).
-file("src/viva_tensor/core/ffi.gleam", 646).
?DOC(
" Resonance Multiply: LNS element-wise multiply.\n"
" result[i] = sign * exp(log|a[i]| + log|b[i]|)\n"
" Multiplication via addition in log domain — better precision for chains.\n"
).
-spec nt_resonance_mul(native_tensor_ref(), native_tensor_ref()) -> {ok,
native_tensor_ref()} |
{error, binary()}.
nt_resonance_mul(A, B) ->
viva_tensor_zig:nt_resonance_mul(A, B).
-file("src/viva_tensor/core/ffi.gleam", 656).
?DOC(
" Resonance Power: LNS element-wise power.\n"
" result[i] = sign(x) * |x|^exponent via exp(exponent * log|x|)\n"
" Power = multiply in log domain. Sign preserved for bipolar states.\n"
).
-spec nt_resonance_power(native_tensor_ref(), float()) -> {ok,
native_tensor_ref()} |
{error, binary()}.
nt_resonance_power(Data, Exponent) ->
viva_tensor_zig:nt_resonance_power(Data, Exponent).
-file("src/viva_tensor/core/ffi.gleam", 395).
?DOC(" Check if Zig SIMD NIF is loaded.\n").
-spec zig_is_loaded() -> boolean().
zig_is_loaded() ->
viva_tensor_zig:is_loaded().
-file("src/viva_tensor/core/ffi.gleam", 402).
?DOC(
" Get Zig backend info for debugging.\n"
"\n"
" Returns SIMD capability info: \"Zig SIMD (AVX2)\" or \"Zig SIMD (NEON)\" etc.\n"
).
-spec zig_backend_info() -> binary().
zig_backend_info() ->
viva_tensor_zig:backend_info().
-file("src/viva_tensor/core/ffi.gleam", 410).
?DOC(
" Zig SIMD dot product.\n"
"\n"
" Uses 4-way or 8-way SIMD depending on platform.\n"
" Unrolled loop with accumulator to maximize throughput.\n"
).
-spec zig_dot(list(float()), list(float())) -> {ok, float()} | {error, binary()}.
zig_dot(A, B) ->
viva_tensor_zig:simd_dot(A, B).
-file("src/viva_tensor/core/ffi.gleam", 415).
?DOC(" Zig SIMD sum reduction.\n").
-spec zig_sum(list(float())) -> {ok, float()} | {error, binary()}.
zig_sum(Data) ->
viva_tensor_zig:simd_sum(Data).
-file("src/viva_tensor/core/ffi.gleam", 420).
?DOC(" Zig SIMD scale (multiply all elements by scalar).\n").
-spec zig_scale(list(float()), float()) -> {ok, list(float())} |
{error, binary()}.
zig_scale(Data, Scalar) ->
viva_tensor_zig:simd_scale(Data, Scalar).
-file("src/viva_tensor/core/ffi.gleam", 428).
?DOC(" Zig SIMD element-wise add.\n").
-spec zig_add(list(float()), list(float())) -> {ok, list(float())} |
{error, binary()}.
zig_add(A, B) ->
viva_tensor_zig:simd_add(A, B).
-file("src/viva_tensor/core/ffi.gleam", 433).
?DOC(" Zig SIMD element-wise multiply.\n").
-spec zig_mul(list(float()), list(float())) -> {ok, list(float())} |
{error, binary()}.
zig_mul(A, B) ->
viva_tensor_zig:simd_mul(A, B).
-file("src/viva_tensor/core/ffi.gleam", 441).
?DOC(
" Zig SIMD matrix multiplication.\n"
"\n"
" Tiled implementation with SIMD inner loops.\n"
" Not quite BLAS-level but respectable: ~10-50 GFLOPS depending on platform.\n"
).
-spec zig_matmul(list(float()), list(float()), integer(), integer(), integer()) -> {ok,
list(float())} |
{error, binary()}.
zig_matmul(A, B, M, N, K) ->
viva_tensor_zig:simd_matmul(A, B, M, N, K).
-file("src/viva_tensor/core/ffi.gleam", 850).
?DOC(" Convert f64 NativeTensor to f32 LNS tensor\n").
-spec lns_from_f64(native_tensor_ref()) -> {ok, lns_tensor_ref()} |
{error, binary()}.
lns_from_f64(Ref) ->
viva_tensor_zig:lns_from_f64(Ref).
-file("src/viva_tensor/core/ffi.gleam", 855).
?DOC(" Convert LNS tensor back to f64 NativeTensor\n").
-spec lns_to_f64(lns_tensor_ref()) -> {ok, native_tensor_ref()} |
{error, binary()}.
lns_to_f64(Ref) ->
viva_tensor_zig:lns_to_f64(Ref).
-file("src/viva_tensor/core/ffi.gleam", 860).
?DOC(" Fast LNS multiply via IADD (~11% max error, 8x throughput)\n").
-spec lns_mul(lns_tensor_ref(), lns_tensor_ref()) -> {ok, lns_tensor_ref()} |
{error, binary()}.
lns_mul(A, B) ->
viva_tensor_zig:lns_mul(A, B).
-file("src/viva_tensor/core/ffi.gleam", 865).
?DOC(" Mitchell's corrected LNS multiply (~2% max error)\n").
-spec lns_mul_corrected(lns_tensor_ref(), lns_tensor_ref()) -> {ok,
lns_tensor_ref()} |
{error, binary()}.
lns_mul_corrected(A, B) ->
viva_tensor_zig:lns_mul_corrected(A, B).
-file("src/viva_tensor/core/ffi.gleam", 873).
?DOC(" LNS division via ISUB\n").
-spec lns_div(lns_tensor_ref(), lns_tensor_ref()) -> {ok, lns_tensor_ref()} |
{error, binary()}.
lns_div(A, B) ->
viva_tensor_zig:lns_div(A, B).
-file("src/viva_tensor/core/ffi.gleam", 878).
?DOC(" LNS sqrt via bit shift\n").
-spec lns_sqrt(lns_tensor_ref()) -> {ok, lns_tensor_ref()} | {error, binary()}.
lns_sqrt(A) ->
viva_tensor_zig:lns_sqrt(A).
-file("src/viva_tensor/core/ffi.gleam", 883).
?DOC(" Fast inverse sqrt (Quake III trick)\n").
-spec lns_rsqrt(lns_tensor_ref()) -> {ok, lns_tensor_ref()} | {error, binary()}.
lns_rsqrt(A) ->
viva_tensor_zig:lns_rsqrt(A).
-file("src/viva_tensor/core/ffi.gleam", 926).
?DOC(" Create new Horde with entity count and dimensionality (1, 2, or 3)\n").
-spec horde_create(integer(), integer()) -> {ok, horde_ref()} |
{error, binary()}.
horde_create(Entity_count, Dims) ->
viva_tensor_zig:horde_create(Entity_count, Dims).
-file("src/viva_tensor/core/ffi.gleam", 931).
?DOC(" Set all positions from flat list [x0, y0, x1, y1, ...] for 2D\n").
-spec horde_set_positions(horde_ref(), list(float())) -> {ok, nil} |
{error, binary()}.
horde_set_positions(Horde, Data) ->
viva_tensor_zig:horde_set_positions(Horde, Data).
-file("src/viva_tensor/core/ffi.gleam", 939).
?DOC(" Set all velocities from flat list\n").
-spec horde_set_velocities(horde_ref(), list(float())) -> {ok, nil} |
{error, binary()}.
horde_set_velocities(Horde, Data) ->
viva_tensor_zig:horde_set_velocities(Horde, Data).
-file("src/viva_tensor/core/ffi.gleam", 947).
?DOC(" Euler integration step: positions += velocities * dt (FMA)\n").
-spec horde_integrate(horde_ref(), float()) -> {ok, nil} | {error, binary()}.
horde_integrate(Horde, Dt) ->
viva_tensor_zig:horde_integrate(Horde, Dt).
-file("src/viva_tensor/core/ffi.gleam", 952).
?DOC(" Apply velocity damping: velocities *= friction\n").
-spec horde_dampen(horde_ref(), float()) -> {ok, nil} | {error, binary()}.
horde_dampen(Horde, Friction) ->
viva_tensor_zig:horde_dampen(Horde, Friction).
-file("src/viva_tensor/core/ffi.gleam", 957).
?DOC(" Toroidal wrap: positions mod max_bound\n").
-spec horde_wrap(horde_ref(), float()) -> {ok, nil} | {error, binary()}.
horde_wrap(Horde, Max_bound) ->
viva_tensor_zig:horde_wrap(Horde, Max_bound).
-file("src/viva_tensor/core/ffi.gleam", 962).
?DOC(" Get current positions as flat list\n").
-spec horde_get_positions(horde_ref()) -> {ok, list(float())} |
{error, binary()}.
horde_get_positions(Horde) ->
viva_tensor_zig:horde_get_positions(Horde).
-file("src/viva_tensor/core/ffi.gleam", 967).
?DOC(" Get current velocities as flat list\n").
-spec horde_get_velocities(horde_ref()) -> {ok, list(float())} |
{error, binary()}.
horde_get_velocities(Horde) ->
viva_tensor_zig:horde_get_velocities(Horde).
-file("src/viva_tensor/core/ffi.gleam", 972).
?DOC(" Get entity count\n").
-spec horde_count(horde_ref()) -> {ok, integer()} | {error, binary()}.
horde_count(Horde) ->
viva_tensor_zig:horde_count(Horde).
-file("src/viva_tensor/core/ffi.gleam", 977).
?DOC(" Compute total kinetic energy: 0.5 * sum(vel^2)\n").
-spec horde_kinetic_energy(horde_ref()) -> {ok, float()} | {error, binary()}.
horde_kinetic_energy(Horde) ->
viva_tensor_zig:horde_kinetic_energy(Horde).
-file("src/viva_tensor/core/ffi.gleam", 1039).
?DOC(" Create empty hypervector (dim must be multiple of 64)\n").
-spec hdc_create(integer()) -> {ok, hdc_vector_ref()} | {error, binary()}.
hdc_create(Dim) ->
viva_tensor_zig:hdc_create(Dim).
-file("src/viva_tensor/core/ffi.gleam", 1044).
?DOC(" Create random hypervector (seed for reproducibility)\n").
-spec hdc_random(integer(), integer()) -> {ok, hdc_vector_ref()} |
{error, binary()}.
hdc_random(Dim, Seed) ->
viva_tensor_zig:hdc_random(Dim, Seed).
-file("src/viva_tensor/core/ffi.gleam", 1049).
?DOC(" XOR binding: associates two concepts (invertible: A XOR B XOR B = A)\n").
-spec hdc_bind(hdc_vector_ref(), hdc_vector_ref()) -> {ok, hdc_vector_ref()} |
{error, binary()}.
hdc_bind(A, B) ->
viva_tensor_zig:hdc_bind(A, B).
-file("src/viva_tensor/core/ffi.gleam", 1058).
?DOC(
" Cosine-like similarity via Hamming distance [0, 1]\n"
" 1 = identical, 0.5 = orthogonal (random), 0 = opposite\n"
).
-spec hdc_similarity(hdc_vector_ref(), hdc_vector_ref()) -> {ok, float()} |
{error, binary()}.
hdc_similarity(A, B) ->
viva_tensor_zig:hdc_similarity(A, B).
-file("src/viva_tensor/core/ffi.gleam", 1064).
?DOC(
" Circular permutation for sequence encoding\n"
" encode(ABC) = A XOR perm(B,1) XOR perm(C,2)\n"
).
-spec hdc_permute(hdc_vector_ref(), integer()) -> {ok, hdc_vector_ref()} |
{error, binary()}.
hdc_permute(Vec, Shift) ->
viva_tensor_zig:hdc_permute(Vec, Shift).
-file("src/viva_tensor/core/ffi.gleam", 1072).
?DOC(" Get dimensionality (total bits)\n").
-spec hdc_dim(hdc_vector_ref()) -> {ok, integer()} | {error, binary()}.
hdc_dim(Vec) ->
viva_tensor_zig:hdc_dim(Vec).
-file("src/viva_tensor/core/ffi.gleam", 1106).
?DOC(" Matrix multiplication with NF4 quantized weights\n").
-spec nt_matmul_nf4(
native_tensor_ref(),
list(integer()),
list(float()),
integer(),
integer(),
integer(),
integer()
) -> {ok, native_tensor_ref()} | {error, binary()}.
nt_matmul_nf4(A, B_indices, B_scales, M, N, K, Block_size) ->
viva_tensor_zig:nt_matmul_nf4(A, B_indices, B_scales, M, N, K, Block_size).
-file("src/viva_tensor/core/ffi.gleam", 1137).
-spec ct_from_list(list(float()), list(integer())) -> {ok, cuda_tensor_ref()} |
{error, binary()}.
ct_from_list(Data, Shape) ->
viva_tensor_zig:ct_from_list(Data, Shape).
-file("src/viva_tensor/core/ffi.gleam", 1143).
-spec ct_to_list(cuda_tensor_ref()) -> {ok, list(float())} | {error, binary()}.
ct_to_list(Ref) ->
viva_tensor_zig:ct_to_list(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1146).
-spec ct_shape(cuda_tensor_ref()) -> {ok, list(integer())} | {error, binary()}.
ct_shape(Ref) ->
viva_tensor_zig:ct_shape(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1149).
-spec ct_matmul(
cuda_tensor_ref(),
cuda_tensor_ref(),
integer(),
integer(),
integer()
) -> {ok, cuda_tensor_ref()} | {error, binary()}.
ct_matmul(A, B, M, N, K) ->
viva_tensor_zig:ct_matmul(A, B, M, N, K).
-file("src/viva_tensor/core/ffi.gleam", 1164).
-spec ct16_available() -> boolean().
ct16_available() ->
viva_tensor_zig:ct16_available().
-file("src/viva_tensor/core/ffi.gleam", 1167).
-spec ct16_from_list(list(float()), list(integer())) -> {ok,
cuda_tensor16_ref()} |
{error, binary()}.
ct16_from_list(Data, Shape) ->
viva_tensor_zig:ct16_from_list(Data, Shape).
-file("src/viva_tensor/core/ffi.gleam", 1173).
-spec ct16_to_list(cuda_tensor16_ref()) -> {ok, list(float())} |
{error, binary()}.
ct16_to_list(Ref) ->
viva_tensor_zig:ct16_to_list(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1176).
-spec ct16_shape(cuda_tensor16_ref()) -> {ok, list(integer())} |
{error, binary()}.
ct16_shape(Ref) ->
viva_tensor_zig:ct16_shape(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1179).
-spec ct16_matmul(
cuda_tensor16_ref(),
cuda_tensor16_ref(),
integer(),
integer(),
integer()
) -> {ok, cuda_tensor16_ref()} | {error, binary()}.
ct16_matmul(A, B, M, N, K) ->
viva_tensor_zig:ct16_matmul(A, B, M, N, K).
-file("src/viva_tensor/core/ffi.gleam", 1194).
-spec ct_int8_available() -> boolean().
ct_int8_available() ->
viva_tensor_zig:ct_int8_available().
-file("src/viva_tensor/core/ffi.gleam", 1197).
-spec ct_int8_from_list(list(float()), list(integer())) -> {ok,
cuda_int8_tensor_ref()} |
{error, binary()}.
ct_int8_from_list(Data, Shape) ->
viva_tensor_zig:ct_int8_from_list(Data, Shape).
-file("src/viva_tensor/core/ffi.gleam", 1203).
-spec ct_int8_to_list(cuda_int8_tensor_ref()) -> {ok, list(float())} |
{error, binary()}.
ct_int8_to_list(Ref) ->
viva_tensor_zig:ct_int8_to_list(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1206).
-spec ct_int8_shape(cuda_int8_tensor_ref()) -> {ok, list(integer())} |
{error, binary()}.
ct_int8_shape(Ref) ->
viva_tensor_zig:ct_int8_shape(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1209).
-spec ct_int8_matmul(
cuda_int8_tensor_ref(),
cuda_int8_tensor_ref(),
integer(),
integer(),
integer()
) -> {ok, cuda_int8_tensor_ref()} | {error, binary()}.
ct_int8_matmul(A, B, M, N, K) ->
viva_tensor_zig:ct_int8_matmul(A, B, M, N, K).
-file("src/viva_tensor/core/ffi.gleam", 1224).
-spec sparse_available() -> boolean().
sparse_available() ->
viva_tensor_zig:sparse_available().
-file("src/viva_tensor/core/ffi.gleam", 1227).
-spec sparse_from_ct16(cuda_tensor16_ref()) -> {ok, sparse_tensor_ref()} |
{error, binary()}.
sparse_from_ct16(Ref) ->
viva_tensor_zig:sparse_from_ct16(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1230).
-spec sparse_shape(sparse_tensor_ref()) -> {ok, list(integer())} |
{error, binary()}.
sparse_shape(Ref) ->
viva_tensor_zig:sparse_shape(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1233).
-spec sparse_compression_ratio(sparse_tensor_ref()) -> {ok, float()} |
{error, binary()}.
sparse_compression_ratio(Ref) ->
viva_tensor_zig:sparse_compression_ratio(Ref).
-file("src/viva_tensor/core/ffi.gleam", 1236).
-spec sparse_matmul(
sparse_tensor_ref(),
cuda_tensor16_ref(),
integer(),
integer(),
integer()
) -> {ok, cuda_tensor16_ref()} | {error, binary()}.
sparse_matmul(A_sparse, B_dense, M, N, K) ->
viva_tensor_zig:sparse_matmul(A_sparse, B_dense, M, N, K).