-module(viva_tensor@core@ops). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/viva_tensor/core/ops.gleam"). -export([map/2, map_indexed/2, add/2, sub/2, mul/2, 'div'/2, scale/2, add_scalar/2, negate/1, abs/1, square/1, sqrt/1, exp/1, log/1, pow/2, sum/1, product/1, mean/1, max/1, min/1, argmax/1, argmin/1, variance/1, std/1, norm/1, normalize/1, dot/2, matmul_vec/2, matmul/2, dot_fast/2, matmul_fast/2, dot_auto/2, matmul_auto/2, backend_info/0, all_backends_info/0, sum_auto/1, add_auto/2, sub_auto/2, mul_auto/2, scale_auto/2, transpose/1, outer/2, clamp/3, relu/1, sigmoid/1, tanh/1, softmax/1, can_broadcast/2, broadcast_shape/2, broadcast_to/2, add_broadcast/2, mul_broadcast/2]). -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(false). -file("src/viva_tensor/core/ops.gleam", 26). ?DOC(false). -spec map(viva_tensor@core@tensor:tensor(), fun((float()) -> float())) -> viva_tensor@core@tensor:tensor(). map(T, F) -> Data = viva_tensor@core@tensor:to_list(T), New_data = gleam@list:map(Data, F), case viva_tensor@core@tensor:new(New_data, viva_tensor@core@tensor:shape(T)) of {ok, Result} -> Result; {error, _} -> T end. -file("src/viva_tensor/core/ops.gleam", 36). ?DOC(false). -spec map_indexed( viva_tensor@core@tensor:tensor(), fun((float(), integer()) -> float()) ) -> viva_tensor@core@tensor:tensor(). map_indexed(T, F) -> Data = viva_tensor@core@tensor:to_list(T), New_data = gleam@list:index_map(Data, fun(X, I) -> F(X, I) end), case viva_tensor@core@tensor:new(New_data, viva_tensor@core@tensor:shape(T)) of {ok, Result} -> Result; {error, _} -> T end. -file("src/viva_tensor/core/ops.gleam", 46). ?DOC(false). -spec add(viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor()) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. add(A, B) -> case viva_tensor@core@tensor:shape(A) =:= viva_tensor@core@tensor:shape(B) of true -> A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Data = gleam@list:map2(A_data, B_data, fun(X, Y) -> X + Y end), viva_tensor@core@tensor:new(Data, viva_tensor@core@tensor:shape(A)); false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 59). ?DOC(false). -spec sub(viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor()) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. sub(A, B) -> case viva_tensor@core@tensor:shape(A) =:= viva_tensor@core@tensor:shape(B) of true -> A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Data = gleam@list:map2(A_data, B_data, fun(X, Y) -> X - Y end), viva_tensor@core@tensor:new(Data, viva_tensor@core@tensor:shape(A)); false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 72). ?DOC(false). -spec mul(viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor()) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. mul(A, B) -> case viva_tensor@core@tensor:shape(A) =:= viva_tensor@core@tensor:shape(B) of true -> A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Data = gleam@list:map2(A_data, B_data, fun(X, Y) -> X * Y end), viva_tensor@core@tensor:new(Data, viva_tensor@core@tensor:shape(A)); false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 85). ?DOC(false). -spec 'div'(viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor()) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. 'div'(A, B) -> case viva_tensor@core@tensor:shape(A) =:= viva_tensor@core@tensor:shape(B) of true -> A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Data = gleam@list:map2(A_data, B_data, fun(X, Y) -> case Y of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> X / Gleam@denominator end end), viva_tensor@core@tensor:new(Data, viva_tensor@core@tensor:shape(A)); false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 98). ?DOC(false). -spec scale(viva_tensor@core@tensor:tensor(), float()) -> viva_tensor@core@tensor:tensor(). scale(T, S) -> map(T, fun(X) -> X * S end). -file("src/viva_tensor/core/ops.gleam", 103). ?DOC(false). -spec add_scalar(viva_tensor@core@tensor:tensor(), float()) -> viva_tensor@core@tensor:tensor(). add_scalar(T, S) -> map(T, fun(X) -> X + S end). -file("src/viva_tensor/core/ops.gleam", 108). ?DOC(false). -spec negate(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). negate(T) -> scale(T, -1.0). -file("src/viva_tensor/core/ops.gleam", 113). ?DOC(false). -spec abs(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). abs(T) -> map(T, fun(X) -> viva_tensor@core@ffi:abs(X) end). -file("src/viva_tensor/core/ops.gleam", 118). ?DOC(false). -spec square(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). square(T) -> map(T, fun(X) -> X * X end). -file("src/viva_tensor/core/ops.gleam", 123). ?DOC(false). -spec sqrt(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). sqrt(T) -> map(T, fun(X) -> viva_tensor@core@ffi:sqrt(X) end). -file("src/viva_tensor/core/ops.gleam", 128). ?DOC(false). -spec exp(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). exp(T) -> map(T, fun(X) -> viva_tensor@core@ffi:exp(X) end). -file("src/viva_tensor/core/ops.gleam", 133). ?DOC(false). -spec log(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). log(T) -> map(T, fun(X) -> viva_tensor@core@ffi:log(X) end). -file("src/viva_tensor/core/ops.gleam", 138). ?DOC(false). -spec pow(viva_tensor@core@tensor:tensor(), float()) -> viva_tensor@core@tensor:tensor(). pow(T, Exponent) -> map(T, fun(X) -> viva_tensor@core@ffi:pow(X, Exponent) end). -file("src/viva_tensor/core/ops.gleam", 152). ?DOC(false). -spec sum(viva_tensor@core@tensor:tensor()) -> float(). sum(T) -> Data = viva_tensor@core@tensor:to_list(T), gleam@list:fold(Data, +0.0, fun(Acc, X) -> Acc + X end). -file("src/viva_tensor/core/ops.gleam", 158). ?DOC(false). -spec product(viva_tensor@core@tensor:tensor()) -> float(). product(T) -> Data = viva_tensor@core@tensor:to_list(T), gleam@list:fold(Data, 1.0, fun(Acc, X) -> Acc * X end). -file("src/viva_tensor/core/ops.gleam", 164). ?DOC(false). -spec mean(viva_tensor@core@tensor:tensor()) -> float(). mean(T) -> S = sum(T), N = erlang:float(viva_tensor@core@tensor:size(T)), case N > +0.0 of true -> case N of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> S / Gleam@denominator end; false -> +0.0 end. -file("src/viva_tensor/core/ops.gleam", 174). ?DOC(false). -spec max(viva_tensor@core@tensor:tensor()) -> float(). max(T) -> Data = viva_tensor@core@tensor:to_list(T), case Data of [] -> +0.0; [First | Rest] -> gleam@list:fold( Rest, First, fun(Acc, X) -> gleam@float:max(Acc, X) end ) end. -file("src/viva_tensor/core/ops.gleam", 183). ?DOC(false). -spec min(viva_tensor@core@tensor:tensor()) -> float(). min(T) -> Data = viva_tensor@core@tensor:to_list(T), case Data of [] -> +0.0; [First | Rest] -> gleam@list:fold( Rest, First, fun(Acc, X) -> gleam@float:min(Acc, X) end ) end. -file("src/viva_tensor/core/ops.gleam", 192). ?DOC(false). -spec argmax(viva_tensor@core@tensor:tensor()) -> integer(). argmax(T) -> Data = viva_tensor@core@tensor:to_list(T), case Data of [] -> 0; [First | Rest] -> {Idx, _, _} = gleam@list:fold( Rest, {0, First, 1}, fun(Acc, X) -> {Best_idx, Best_val, Curr_idx} = Acc, case X > Best_val of true -> {Curr_idx, X, Curr_idx + 1}; false -> {Best_idx, Best_val, Curr_idx + 1} end end ), Idx end. -file("src/viva_tensor/core/ops.gleam", 212). ?DOC(false). -spec argmin(viva_tensor@core@tensor:tensor()) -> integer(). argmin(T) -> Data = viva_tensor@core@tensor:to_list(T), case Data of [] -> 0; [First | Rest] -> {Idx, _, _} = gleam@list:fold( Rest, {0, First, 1}, fun(Acc, X) -> {Best_idx, Best_val, Curr_idx} = Acc, case X < Best_val of true -> {Curr_idx, X, Curr_idx + 1}; false -> {Best_idx, Best_val, Curr_idx + 1} end end ), Idx end. -file("src/viva_tensor/core/ops.gleam", 231). ?DOC(false). -spec variance(viva_tensor@core@tensor:tensor()) -> float(). variance(T) -> Data = viva_tensor@core@tensor:to_list(T), N = erlang:float(viva_tensor@core@tensor:size(T)), case N > +0.0 of true -> {Total, Total_sq} = gleam@list:fold( Data, {+0.0, +0.0}, fun(Acc, X) -> {S, Sq} = Acc, {S + X, Sq + (X * X)} end ), M = case N of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> Total / Gleam@denominator end, (case N of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@1 -> Total_sq / Gleam@denominator@1 end) - (M * M); false -> +0.0 end. -file("src/viva_tensor/core/ops.gleam", 250). ?DOC(false). -spec std(viva_tensor@core@tensor:tensor()) -> float(). std(T) -> viva_tensor@core@ffi:sqrt(variance(T)). -file("src/viva_tensor/core/ops.gleam", 255). ?DOC(false). -spec norm(viva_tensor@core@tensor:tensor()) -> float(). norm(T) -> Data = viva_tensor@core@tensor:to_list(T), Sum_sq = gleam@list:fold(Data, +0.0, fun(Acc, X) -> Acc + (X * X) end), viva_tensor@core@ffi:sqrt(Sum_sq). -file("src/viva_tensor/core/ops.gleam", 262). ?DOC(false). -spec normalize(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). normalize(T) -> N = norm(T), case N > 0.0001 of true -> scale(T, case N of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> 1.0 / Gleam@denominator end); false -> T end. -file("src/viva_tensor/core/ops.gleam", 278). ?DOC(false). -spec dot(viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor()) -> {ok, float()} | {error, viva_tensor@core@error:tensor_error()}. dot(A, B) -> case ((viva_tensor@core@tensor:rank(A) =:= 1) andalso (viva_tensor@core@tensor:rank( B ) =:= 1)) andalso (viva_tensor@core@tensor:size(A) =:= viva_tensor@core@tensor:size(B)) of true -> A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Products = gleam@list:map2(A_data, B_data, fun(X, Y) -> X * Y end), {ok, gleam@list:fold(Products, +0.0, fun(Acc, X@1) -> Acc + X@1 end)}; false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 297). ?DOC(false). -spec matmul_vec( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. matmul_vec(Mat, Vec) -> case {viva_tensor@core@tensor:shape(Mat), viva_tensor@core@tensor:shape(Vec)} of {[M, N], [Vec_n]} when N =:= Vec_n -> Mat_arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(Mat) ), Vec_arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(Vec) ), Result_data = begin _pipe = gleam@list:range(0, M - 1), gleam@list:map( _pipe, fun(Row_idx) -> Start = Row_idx * N, _pipe@1 = gleam@list:range(0, N - 1), gleam@list:fold( _pipe@1, +0.0, fun(Acc, K) -> Acc + (viva_tensor@core@ffi:array_get( Mat_arr, Start + K ) * viva_tensor@core@ffi:array_get(Vec_arr, K)) end ) end ) end, viva_tensor@core@tensor:new(Result_data, [M]); {[_, N@1], [Vec_n@1]} -> {error, {shape_mismatch, [N@1], [Vec_n@1]}}; {_, _} -> {error, {dimension_error, <<"Expected matrix and vector"/utf8>>}} end. -file("src/viva_tensor/core/ops.gleam", 325). ?DOC(false). -spec matmul(viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor()) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. matmul(A, B) -> case {viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)} of {[M, N], [N2, P]} when N =:= N2 -> A_arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(A) ), B_arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(B) ), Result_data = begin _pipe = gleam@list:range(0, M - 1), gleam@list:flat_map( _pipe, fun(I) -> Row_start = I * N, _pipe@1 = gleam@list:range(0, P - 1), gleam@list:map( _pipe@1, fun(J) -> _pipe@2 = gleam@list:range(0, N - 1), gleam@list:fold( _pipe@2, +0.0, fun(Acc, K) -> Acc + (viva_tensor@core@ffi:array_get( A_arr, Row_start + K ) * viva_tensor@core@ffi:array_get( B_arr, (K * P) + J )) end ) end ) end ) end, viva_tensor@core@tensor:new(Result_data, [M, P]); {[_, N@1], [N2@1, _]} -> {error, {shape_mismatch, [N@1, -1], [N2@1, -1]}}; {_, _} -> {error, {dimension_error, <<"Expected two matrices"/utf8>>}} end. -file("src/viva_tensor/core/ops.gleam", 356). ?DOC(false). -spec dot_fast( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, float()} | {error, viva_tensor@core@error:tensor_error()}. dot_fast(A, B) -> case ((viva_tensor@core@tensor:rank(A) =:= 1) andalso (viva_tensor@core@tensor:rank( B ) =:= 1)) andalso (viva_tensor@core@tensor:size(A) =:= viva_tensor@core@tensor:size(B)) of true -> A_arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(A) ), B_arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(B) ), {ok, viva_tensor@core@ffi:array_dot(A_arr, B_arr)}; false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 372). ?DOC(false). -spec matmul_fast( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. matmul_fast(A, B) -> case {viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)} of {[M, K], [K2, N]} when K =:= K2 -> A_arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(A) ), B_arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(B) ), Result_arr = viva_tensor@core@ffi:array_matmul( A_arr, B_arr, M, N, K ), Result_list = viva_tensor@core@ffi:array_to_list(Result_arr), viva_tensor@core@tensor:new(Result_list, [M, N]); {[_, K@1], [K2@1, _]} -> {error, {shape_mismatch, [K@1, -1], [K2@1, -1]}}; {_, _} -> {error, {dimension_error, <<"Expected two matrices"/utf8>>}} end. -file("src/viva_tensor/core/ops.gleam", 444). ?DOC(false). -spec dot_with_zig_fallback( list(float()), list(float()), viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {{ok, float()} | {error, viva_tensor@core@error:tensor_error()}, binary()}. dot_with_zig_fallback(A_list, B_list, A, B) -> case viva_tensor@core@ffi:zig_is_loaded() of true -> case viva_tensor@core@ffi:zig_dot(A_list, B_list) of {ok, R} -> {{ok, R}, <<"zig"/utf8>>}; {error, _} -> {dot_fast(A, B), <<"erlang"/utf8>>} end; false -> {dot_fast(A, B), <<"erlang"/utf8>>} end. -file("src/viva_tensor/core/ops.gleam", 428). ?DOC(false). -spec dot_list_fallback( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {{ok, float()} | {error, viva_tensor@core@error:tensor_error()}, binary()}. dot_list_fallback(A, B) -> A_list = viva_tensor@core@tensor:to_list(A), B_list = viva_tensor@core@tensor:to_list(B), case viva_tensor@core@ffi:is_nif_loaded() of true -> case viva_tensor@core@ffi:nif_dot(A_list, B_list) of {ok, R} -> {{ok, R}, <<"accelerate"/utf8>>}; {error, _} -> dot_with_zig_fallback(A_list, B_list, A, B) end; false -> dot_with_zig_fallback(A_list, B_list, A, B) end. -file("src/viva_tensor/core/ops.gleam", 404). ?DOC(false). -spec dot_auto( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, float()} | {error, viva_tensor@core@error:tensor_error()}. dot_auto(A, B) -> case ((viva_tensor@core@tensor:rank(A) =:= 1) andalso (viva_tensor@core@tensor:rank( B ) =:= 1)) andalso (viva_tensor@core@tensor:size(A) =:= viva_tensor@core@tensor:size(B)) of true -> T0 = viva_tensor@core@ffi:now_microseconds(), {Result, Backend} = case {viva_tensor@core@tensor:native_ref(A), viva_tensor@core@tensor:native_ref(B)} of {{ok, Ref_a}, {ok, Ref_b}} -> case viva_tensor@core@ffi:nt_dot(Ref_a, Ref_b) of {ok, R} -> {{ok, R}, <<"native"/utf8>>}; {error, _} -> dot_list_fallback(A, B) end; {_, _} -> dot_list_fallback(A, B) end, viva_tensor@telemetry:record_dot( viva_tensor@core@tensor:size(A), viva_tensor@core@ffi:now_microseconds() - T0, Backend ), Result; false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 504). ?DOC(false). -spec matmul_with_zig_fallback( list(float()), list(float()), integer(), integer(), integer(), viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {{ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}, binary()}. matmul_with_zig_fallback(A_list, B_list, M, N, K, A, B) -> case viva_tensor@core@ffi:zig_is_loaded() of true -> case viva_tensor@core@ffi:zig_matmul(A_list, B_list, M, N, K) of {ok, Result_list} -> {viva_tensor@core@tensor:new(Result_list, [M, N]), <<"zig"/utf8>>}; {error, _} -> {matmul_fast(A, B), <<"erlang"/utf8>>} end; false -> {matmul_fast(A, B), <<"erlang"/utf8>>} end. -file("src/viva_tensor/core/ops.gleam", 485). ?DOC(false). -spec matmul_list_fallback( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor(), integer(), integer(), integer() ) -> {{ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}, binary()}. matmul_list_fallback(A, B, M, N, K) -> A_list = viva_tensor@core@tensor:to_list(A), B_list = viva_tensor@core@tensor:to_list(B), case viva_tensor@core@ffi:is_nif_loaded() of true -> case viva_tensor@core@ffi:nif_matmul(A_list, B_list, M, N, K) of {ok, Result_list} -> {viva_tensor@core@tensor:new(Result_list, [M, N]), <<"accelerate"/utf8>>}; {error, _} -> matmul_with_zig_fallback(A_list, B_list, M, N, K, A, B) end; false -> matmul_with_zig_fallback(A_list, B_list, M, N, K, A, B) end. -file("src/viva_tensor/core/ops.gleam", 464). ?DOC(false). -spec matmul_auto( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. matmul_auto(A, B) -> case {viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)} of {[M, K], [K2, N]} when K =:= K2 -> T0 = viva_tensor@core@ffi:now_microseconds(), {Result, Backend} = case {viva_tensor@core@tensor:native_ref(A), viva_tensor@core@tensor:native_ref(B)} of {{ok, Ref_a}, {ok, Ref_b}} -> case viva_tensor@core@ffi:nt_matmul(Ref_a, Ref_b, M, N, K) of {ok, Ref_c} -> {{ok, viva_tensor@core@tensor:from_native_ref( Ref_c, [M, N] )}, <<"native"/utf8>>}; {error, _} -> matmul_list_fallback(A, B, M, N, K) end; {_, _} -> matmul_list_fallback(A, B, M, N, K) end, viva_tensor@telemetry:record_matmul( M, N, K, viva_tensor@core@ffi:now_microseconds() - T0, Backend ), Result; {[_, K@1], [K2@1, _]} -> {error, {shape_mismatch, [K@1, -1], [K2@1, -1]}}; {_, _} -> {error, {dimension_error, <<"Expected two matrices"/utf8>>}} end. -file("src/viva_tensor/core/ops.gleam", 527). ?DOC(false). -spec backend_info() -> binary(). backend_info() -> case viva_tensor@core@ffi:zig_is_loaded() of true -> <<(viva_tensor@core@ffi:zig_backend_info())/binary, " + NIF Resources"/utf8>>; false -> case viva_tensor@core@ffi:is_nif_loaded() of true -> viva_tensor@core@ffi:nif_backend_info(); false -> <<"Pure Erlang (O(1) array access)"/utf8>> end end. -file("src/viva_tensor/core/ops.gleam", 539). ?DOC(false). -spec all_backends_info() -> binary(). all_backends_info() -> Zig_status = case viva_tensor@core@ffi:zig_is_loaded() of true -> <<"✓ "/utf8, (viva_tensor@core@ffi:zig_backend_info())/binary>>; false -> <<"✗ Not available"/utf8>> end, Accel_status = case viva_tensor@core@ffi:is_nif_loaded() of true -> <<"✓ "/utf8, (viva_tensor@core@ffi:nif_backend_info())/binary>>; false -> <<"✗ Not available"/utf8>> end, <<<<<<<<"Zig SIMD: "/utf8, Zig_status/binary>>/binary, "\nApple Accelerate: "/utf8>>/binary, Accel_status/binary>>/binary, "\nPure Erlang: ✓ Always available"/utf8>>. -file("src/viva_tensor/core/ops.gleam", 585). ?DOC(false). -spec sum_with_accel_fallback(list(float())) -> float(). sum_with_accel_fallback(Data) -> case viva_tensor@core@ffi:is_nif_loaded() of true -> case viva_tensor@core@ffi:nif_sum(Data) of {ok, Result} -> Result; {error, _} -> gleam@list:fold(Data, +0.0, fun(Acc, X) -> Acc + X end) end; false -> gleam@list:fold(Data, +0.0, fun(Acc@1, X@1) -> Acc@1 + X@1 end) end. -file("src/viva_tensor/core/ops.gleam", 573). ?DOC(false). -spec sum_list_fallback(viva_tensor@core@tensor:tensor()) -> float(). sum_list_fallback(T) -> Data = viva_tensor@core@tensor:to_list(T), case viva_tensor@core@ffi:zig_is_loaded() of true -> case viva_tensor@core@ffi:zig_sum(Data) of {ok, R} -> R; {error, _} -> sum_with_accel_fallback(Data) end; false -> sum_with_accel_fallback(Data) end. -file("src/viva_tensor/core/ops.gleam", 558). ?DOC(false). -spec sum_auto(viva_tensor@core@tensor:tensor()) -> float(). sum_auto(T) -> T0 = viva_tensor@core@ffi:now_microseconds(), Result = case viva_tensor@core@tensor:native_ref(T) of {ok, Ref} -> case viva_tensor@core@ffi:nt_sum(Ref) of {ok, R} -> R; {error, _} -> sum_list_fallback(T) end; {error, _} -> sum_list_fallback(T) end, viva_tensor@telemetry:record_op( <<"sum"/utf8>>, viva_tensor@core@ffi:now_microseconds() - T0 ), Result. -file("src/viva_tensor/core/ops.gleam", 613). ?DOC(false). -spec add_list_fallback( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. add_list_fallback(A, B) -> A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Result_data = case viva_tensor@core@ffi:zig_is_loaded() of true -> case viva_tensor@core@ffi:zig_add(A_data, B_data) of {ok, R} -> R; {error, _} -> gleam@list:map2(A_data, B_data, fun(X, Y) -> X + Y end) end; false -> gleam@list:map2(A_data, B_data, fun(X@1, Y@1) -> X@1 + Y@1 end) end, viva_tensor@core@tensor:new(Result_data, viva_tensor@core@tensor:shape(A)). -file("src/viva_tensor/core/ops.gleam", 598). ?DOC(false). -spec add_auto( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. add_auto(A, B) -> case viva_tensor@core@tensor:shape(A) =:= viva_tensor@core@tensor:shape(B) of true -> case {viva_tensor@core@tensor:native_ref(A), viva_tensor@core@tensor:native_ref(B)} of {{ok, Ref_a}, {ok, Ref_b}} -> case viva_tensor@core@ffi:nt_add(Ref_a, Ref_b) of {ok, Ref_c} -> {ok, viva_tensor@core@tensor:from_native_ref( Ref_c, viva_tensor@core@tensor:shape(A) )}; {error, _} -> add_list_fallback(A, B) end; {_, _} -> add_list_fallback(A, B) end; false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 643). ?DOC(false). -spec sub_list_fallback( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. sub_list_fallback(A, B) -> A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Result_data = case viva_tensor@core@ffi:zig_is_loaded() of true -> case viva_tensor@core@ffi:zig_scale(B_data, -1.0) of {ok, Neg_b} -> case viva_tensor@core@ffi:zig_add(A_data, Neg_b) of {ok, R} -> R; {error, _} -> gleam@list:map2( A_data, B_data, fun(X, Y) -> X - Y end ) end; {error, _} -> gleam@list:map2( A_data, B_data, fun(X@1, Y@1) -> X@1 - Y@1 end ) end; false -> gleam@list:map2(A_data, B_data, fun(X@2, Y@2) -> X@2 - Y@2 end) end, viva_tensor@core@tensor:new(Result_data, viva_tensor@core@tensor:shape(A)). -file("src/viva_tensor/core/ops.gleam", 628). ?DOC(false). -spec sub_auto( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. sub_auto(A, B) -> case viva_tensor@core@tensor:shape(A) =:= viva_tensor@core@tensor:shape(B) of true -> case {viva_tensor@core@tensor:native_ref(A), viva_tensor@core@tensor:native_ref(B)} of {{ok, Ref_a}, {ok, Ref_b}} -> case viva_tensor@core@ffi:nt_sub(Ref_a, Ref_b) of {ok, Ref_c} -> {ok, viva_tensor@core@tensor:from_native_ref( Ref_c, viva_tensor@core@tensor:shape(A) )}; {error, _} -> sub_list_fallback(A, B) end; {_, _} -> sub_list_fallback(A, B) end; false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 677). ?DOC(false). -spec mul_list_fallback( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. mul_list_fallback(A, B) -> A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Result_data = case viva_tensor@core@ffi:zig_is_loaded() of true -> case viva_tensor@core@ffi:zig_mul(A_data, B_data) of {ok, R} -> R; {error, _} -> gleam@list:map2(A_data, B_data, fun(X, Y) -> X * Y end) end; false -> gleam@list:map2(A_data, B_data, fun(X@1, Y@1) -> X@1 * Y@1 end) end, viva_tensor@core@tensor:new(Result_data, viva_tensor@core@tensor:shape(A)). -file("src/viva_tensor/core/ops.gleam", 662). ?DOC(false). -spec mul_auto( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. mul_auto(A, B) -> case viva_tensor@core@tensor:shape(A) =:= viva_tensor@core@tensor:shape(B) of true -> case {viva_tensor@core@tensor:native_ref(A), viva_tensor@core@tensor:native_ref(B)} of {{ok, Ref_a}, {ok, Ref_b}} -> case viva_tensor@core@ffi:nt_mul(Ref_a, Ref_b) of {ok, Ref_c} -> {ok, viva_tensor@core@tensor:from_native_ref( Ref_c, viva_tensor@core@tensor:shape(A) )}; {error, _} -> mul_list_fallback(A, B) end; {_, _} -> mul_list_fallback(A, B) end; false -> {error, {shape_mismatch, viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B)}} end. -file("src/viva_tensor/core/ops.gleam", 720). ?DOC(false). -spec scale_with_accel_fallback(list(float()), float()) -> list(float()). scale_with_accel_fallback(Data, S) -> case viva_tensor@core@ffi:is_nif_loaded() of true -> case viva_tensor@core@ffi:nif_scale(Data, S) of {ok, Result} -> Result; {error, _} -> gleam@list:map(Data, fun(X) -> X * S end) end; false -> gleam@list:map(Data, fun(X@1) -> X@1 * S end) end. -file("src/viva_tensor/core/ops.gleam", 704). ?DOC(false). -spec scale_list_fallback(viva_tensor@core@tensor:tensor(), float()) -> viva_tensor@core@tensor:tensor(). scale_list_fallback(T, S) -> Data = viva_tensor@core@tensor:to_list(T), Result_data = case viva_tensor@core@ffi:zig_is_loaded() of true -> case viva_tensor@core@ffi:zig_scale(Data, S) of {ok, R} -> R; {error, _} -> scale_with_accel_fallback(Data, S) end; false -> scale_with_accel_fallback(Data, S) end, case viva_tensor@core@tensor:new( Result_data, viva_tensor@core@tensor:shape(T) ) of {ok, Result} -> Result; {error, _} -> T end. -file("src/viva_tensor/core/ops.gleam", 692). ?DOC(false). -spec scale_auto(viva_tensor@core@tensor:tensor(), float()) -> viva_tensor@core@tensor:tensor(). scale_auto(T, S) -> case viva_tensor@core@tensor:native_ref(T) of {ok, Ref} -> case viva_tensor@core@ffi:nt_scale(Ref, S) of {ok, Ref_c} -> viva_tensor@core@tensor:from_native_ref( Ref_c, viva_tensor@core@tensor:shape(T) ); {error, _} -> scale_list_fallback(T, S) end; {error, _} -> scale_list_fallback(T, S) end. -file("src/viva_tensor/core/ops.gleam", 733). ?DOC(false). -spec transpose(viva_tensor@core@tensor:tensor()) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. transpose(T) -> case viva_tensor@core@tensor:shape(T) of [M, N] -> Arr = viva_tensor@core@ffi:list_to_array( viva_tensor@core@tensor:to_list(T) ), Result_data = begin _pipe = gleam@list:range(0, N - 1), gleam@list:flat_map( _pipe, fun(J) -> _pipe@1 = gleam@list:range(0, M - 1), gleam@list:map( _pipe@1, fun(I) -> viva_tensor@core@ffi:array_get(Arr, (I * N) + J) end ) end ) end, viva_tensor@core@tensor:new(Result_data, [N, M]); _ -> {error, {dimension_error, <<"Transpose requires 2D tensor"/utf8>>}} end. -file("src/viva_tensor/core/ops.gleam", 750). ?DOC(false). -spec outer(viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor()) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. outer(A, B) -> case (viva_tensor@core@tensor:rank(A) =:= 1) andalso (viva_tensor@core@tensor:rank( B ) =:= 1) of true -> M = viva_tensor@core@tensor:size(A), N = viva_tensor@core@tensor:size(B), A_data = viva_tensor@core@tensor:to_list(A), B_data = viva_tensor@core@tensor:to_list(B), Result_data = gleam@list:flat_map( A_data, fun(Ai) -> gleam@list:map(B_data, fun(Bj) -> Ai * Bj end) end ), viva_tensor@core@tensor:new(Result_data, [M, N]); false -> {error, {dimension_error, <<"Outer product requires two vectors"/utf8>>}} end. -file("src/viva_tensor/core/ops.gleam", 768). ?DOC(false). -spec clamp(viva_tensor@core@tensor:tensor(), float(), float()) -> viva_tensor@core@tensor:tensor(). clamp(T, Min_val, Max_val) -> map(T, fun(X) -> gleam@float:min(gleam@float:max(X, Min_val), Max_val) end). -file("src/viva_tensor/core/ops.gleam", 773). ?DOC(false). -spec relu(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). relu(T) -> map(T, fun(X) -> gleam@float:max(X, +0.0) end). -file("src/viva_tensor/core/ops.gleam", 778). ?DOC(false). -spec sigmoid(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). sigmoid(T) -> map(T, fun(X) -> case (1.0 + viva_tensor@core@ffi:exp(+0.0 - X)) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> 1.0 / Gleam@denominator end end). -file("src/viva_tensor/core/ops.gleam", 783). ?DOC(false). -spec tanh(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). tanh(T) -> map(T, fun(X) -> viva_tensor@core@ffi:tanh(X) end). -file("src/viva_tensor/core/ops.gleam", 797). ?DOC(false). -spec softmax(viva_tensor@core@tensor:tensor()) -> viva_tensor@core@tensor:tensor(). softmax(T) -> Data = viva_tensor@core@tensor:to_list(T), Max_val = max(T), Shifted = gleam@list:map( Data, fun(X) -> viva_tensor@core@ffi:exp(X - Max_val) end ), Sum_exp = gleam@list:fold(Shifted, +0.0, fun(Acc, X@1) -> Acc + X@1 end), Result = gleam@list:map(Shifted, fun(X@2) -> case Sum_exp of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> X@2 / Gleam@denominator end end), case viva_tensor@core@tensor:new(Result, viva_tensor@core@tensor:shape(T)) of {ok, R} -> R; {error, _} -> T end. -file("src/viva_tensor/core/ops.gleam", 822). ?DOC(false). -spec can_broadcast(list(integer()), list(integer())) -> boolean(). can_broadcast(A, B) -> {Longer, Shorter} = case erlang:length(A) >= erlang:length(B) of true -> {A, B}; false -> {B, A} end, Diff = erlang:length(Longer) - erlang:length(Shorter), Padded = lists:append(gleam@list:repeat(1, Diff), Shorter), _pipe = gleam@list:zip(Longer, Padded), gleam@list:all( _pipe, fun(Pair) -> {Dim_a, Dim_b} = Pair, ((Dim_a =:= Dim_b) orelse (Dim_a =:= 1)) orelse (Dim_b =:= 1) end ). -file("src/viva_tensor/core/ops.gleam", 839). ?DOC(false). -spec broadcast_shape(list(integer()), list(integer())) -> {ok, list(integer())} | {error, viva_tensor@core@error:tensor_error()}. broadcast_shape(A, B) -> case can_broadcast(A, B) of false -> {error, {broadcast_error, A, B}}; true -> Max_rank = gleam@int:max(erlang:length(A), erlang:length(B)), Diff_a = Max_rank - erlang:length(A), Diff_b = Max_rank - erlang:length(B), Padded_a = lists:append(gleam@list:repeat(1, Diff_a), A), Padded_b = lists:append(gleam@list:repeat(1, Diff_b), B), Result_shape = begin _pipe = gleam@list:zip(Padded_a, Padded_b), gleam@list:map( _pipe, fun(Pair) -> {Dim_a, Dim_b} = Pair, gleam@int:max(Dim_a, Dim_b) end ) end, {ok, Result_shape} end. -file("src/viva_tensor/core/ops.gleam", 970). ?DOC(false). -spec list_at_float(list(float()), integer()) -> {ok, float()} | {error, nil}. list_at_float(Lst, Index) -> case Index < 0 of true -> {error, nil}; false -> _pipe = Lst, _pipe@1 = gleam@list:drop(_pipe, Index), gleam@list:first(_pipe@1) end. -file("src/viva_tensor/core/ops.gleam", 960). ?DOC(false). -spec compute_strides(list(integer())) -> list(integer()). compute_strides(Shape) -> Reversed = lists:reverse(Shape), {Strides, _} = gleam@list:fold( Reversed, {[], 1}, fun(Acc, Dim) -> {S, Running} = Acc, {[Running | S], Running * Dim} end ), Strides. -file("src/viva_tensor/core/ops.gleam", 951). ?DOC(false). -spec multi_to_flat(list(integer()), list(integer())) -> integer(). multi_to_flat(Indices, Shape) -> Strides = compute_strides(Shape), _pipe = gleam@list:zip(Indices, Strides), gleam@list:fold( _pipe, 0, fun(Acc, Pair) -> {Idx, Stride} = Pair, Acc + (Idx * Stride) end ). -file("src/viva_tensor/core/ops.gleam", 939). ?DOC(false). -spec flat_to_multi(integer(), list(integer())) -> list(integer()). flat_to_multi(Flat, Shape) -> Reversed = lists:reverse(Shape), {Indices, _} = gleam@list:fold( Reversed, {[], Flat}, fun(Acc, Dim) -> {Idxs, Remaining} = Acc, Idx = case Dim of 0 -> 0; Gleam@denominator -> Remaining rem Gleam@denominator end, Next = case Dim of 0 -> 0; Gleam@denominator@1 -> Remaining div Gleam@denominator@1 end, {[Idx | Idxs], Next} end ), Indices. -file("src/viva_tensor/core/ops.gleam", 906). ?DOC(false). -spec broadcast_data(viva_tensor@core@tensor:tensor(), list(integer())) -> list(float()). broadcast_data(T, Target_shape) -> Target_size = gleam@list:fold( Target_shape, 1, fun(Acc, Dim) -> Acc * Dim end ), Src_shape = viva_tensor@core@tensor:shape(T), Src_rank = erlang:length(Src_shape), Target_rank = erlang:length(Target_shape), Data = viva_tensor@core@tensor:to_list(T), Diff = Target_rank - Src_rank, Padded_shape = lists:append(gleam@list:repeat(1, Diff), Src_shape), _pipe = gleam@list:range(0, Target_size - 1), gleam@list:map( _pipe, fun(Flat_idx) -> Target_indices = flat_to_multi(Flat_idx, Target_shape), Src_indices = begin _pipe@1 = gleam@list:zip(Target_indices, Padded_shape), _pipe@2 = gleam@list:map( _pipe@1, fun(Pair) -> {Idx, Dim@1} = Pair, case Dim@1 =:= 1 of true -> 0; false -> Idx end end ), gleam@list:drop(_pipe@2, Diff) end, Src_flat = multi_to_flat(Src_indices, Src_shape), case list_at_float(Data, Src_flat) of {ok, V} -> V; {error, _} -> +0.0 end end ). -file("src/viva_tensor/core/ops.gleam", 887). ?DOC(false). -spec broadcast_to(viva_tensor@core@tensor:tensor(), list(integer())) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. broadcast_to(T, Target_shape) -> Src_shape = viva_tensor@core@tensor:shape(T), case can_broadcast(Src_shape, Target_shape) of false -> {error, {broadcast_error, Src_shape, Target_shape}}; true -> case Src_shape =:= Target_shape of true -> {ok, T}; false -> Data = broadcast_data(T, Target_shape), viva_tensor@core@tensor:new(Data, Target_shape) end end. -file("src/viva_tensor/core/ops.gleam", 865). ?DOC(false). -spec add_broadcast( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. add_broadcast(A, B) -> gleam@result:'try'( broadcast_shape( viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B) ), fun(Result_shape) -> gleam@result:'try'( broadcast_to(A, Result_shape), fun(A_bc) -> gleam@result:'try'( broadcast_to(B, Result_shape), fun(B_bc) -> add(A_bc, B_bc) end ) end ) end ). -file("src/viva_tensor/core/ops.gleam", 876). ?DOC(false). -spec mul_broadcast( viva_tensor@core@tensor:tensor(), viva_tensor@core@tensor:tensor() ) -> {ok, viva_tensor@core@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. mul_broadcast(A, B) -> gleam@result:'try'( broadcast_shape( viva_tensor@core@tensor:shape(A), viva_tensor@core@tensor:shape(B) ), fun(Result_shape) -> gleam@result:'try'( broadcast_to(A, Result_shape), fun(A_bc) -> gleam@result:'try'( broadcast_to(B, Result_shape), fun(B_bc) -> mul(A_bc, B_bc) end ) end ) end ).