-module(viva_tensor@core@linalg). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/viva_tensor/core/linalg.gleam"). -export([solve/2, eye/1, inv/1, det/1, lu/1, cholesky/1, qr/1, svd/1, eig/1]). -export_type([lu_failure/0, lu_decomp/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(false). -type lu_failure() :: lu_singular | {lu_error, viva_tensor@core@error:tensor_error()}. -type lu_decomp() :: {lu_decomp, list(list(float())), list(list(float())), list(integer()), integer()}. -file("src/viva_tensor/core/linalg.gleam", 901). ?DOC(false). -spec set_at(list(UIS), integer(), UIS) -> list(UIS). set_at(Lst, Idx, Value) -> _pipe = Lst, gleam@list:index_map(_pipe, fun(Item, I) -> case I =:= Idx of true -> Value; false -> Item end end). -file("src/viva_tensor/core/linalg.gleam", 814). ?DOC(false). -spec list_at(list(UHY), integer()) -> {ok, UHY} | {error, nil}. list_at(Lst, Idx) -> case {Lst, Idx} of {[], _} -> {error, nil}; {[Head | _], 0} -> {ok, Head}; {[_ | Rest], _} -> list_at(Rest, Idx - 1) end. -file("src/viva_tensor/core/linalg.gleam", 809). ?DOC(false). -spec unsafe_at(list(UHW), integer()) -> UHW. unsafe_at(Lst, Idx) -> Value@1 = case list_at(Lst, Idx) of {ok, Value} -> Value; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"viva_tensor/core/linalg"/utf8>>, function => <<"unsafe_at"/utf8>>, line => 810, value => _assert_fail, start => 22696, 'end' => 22736, pattern_start => 22707, pattern_end => 22716}) end, Value@1. -file("src/viva_tensor/core/linalg.gleam", 942). ?DOC(false). -spec range_loop(integer(), integer(), list(integer())) -> list(integer()). range_loop(From, To, Acc) -> case From > To of true -> lists:reverse(Acc); false -> range_loop(From + 1, To, [From | Acc]) end. -file("src/viva_tensor/core/linalg.gleam", 938). ?DOC(false). -spec range_int(integer(), integer()) -> list(integer()). range_int(From, To) -> range_loop(From, To, []). -file("src/viva_tensor/core/linalg.gleam", 795). ?DOC(false). -spec int_range(integer(), integer()) -> list(integer()). int_range(Start, Stop) -> case Stop < Start of true -> []; false -> range_int(Start, Stop) end. -file("src/viva_tensor/core/linalg.gleam", 489). ?DOC(false). -spec back_substitute_loop( list(list(float())), list(list(float())), integer(), integer(), integer() ) -> list(list(float())). back_substitute_loop(Rows, Solution, I, N, Nrhs) -> case I < 0 of true -> Solution; false -> Row = unsafe_at(Rows, I), A_ii = unsafe_at(Row, I), B_part = begin _pipe = Row, _pipe@1 = gleam@list:drop(_pipe, N), gleam@list:take(_pipe@1, Nrhs) end, Summed = begin _pipe@2 = int_range(I + 1, N - 1), gleam@list:fold( _pipe@2, B_part, fun(Acc, J) -> A_ij = unsafe_at(Row, J), X_j = unsafe_at(Solution, J), _pipe@3 = gleam@list:zip(Acc, X_j), gleam@list:map( _pipe@3, fun(Pair) -> {A_val, X_val} = Pair, A_val - (A_ij * X_val) end ) end ) end, X_i = gleam@list:map(Summed, fun(V) -> case A_ii of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> V / Gleam@denominator end end), New_solution = set_at(Solution, I, X_i), back_substitute_loop(Rows, New_solution, I - 1, N, Nrhs) end. -file("src/viva_tensor/core/linalg.gleam", 478). ?DOC(false). -spec back_substitute(list(list(float())), integer(), integer()) -> list(list(float())). back_substitute(Rows, N, Nrhs) -> Row_array = Rows, Solution_init = gleam@list:repeat(gleam@list:repeat(+0.0, Nrhs), N), back_substitute_loop(Row_array, Solution_init, N - 1, N, Nrhs). -file("src/viva_tensor/core/linalg.gleam", 466). ?DOC(false). -spec eliminate_row(list(float()), list(float()), float()) -> list(float()). eliminate_row(Row, Pivot_row, Factor) -> _pipe = gleam@list:zip(Row, Pivot_row), gleam@list:map( _pipe, fun(Pair) -> {R, P} = Pair, R - (Factor * P) end ). -file("src/viva_tensor/core/linalg.gleam", 826). ?DOC(false). -spec swap_rows(list(list(float())), integer(), integer()) -> list(list(float())). swap_rows(Rows, I, J) -> case I =:= J of true -> Rows; false -> Row_i = unsafe_at(Rows, I), Row_j = unsafe_at(Rows, J), _pipe = Rows, gleam@list:index_map( _pipe, fun(Row, Idx) -> case {Idx =:= I, Idx =:= J} of {true, _} -> Row_j; {_, true} -> Row_i; {_, _} -> Row end end ) end. -file("src/viva_tensor/core/linalg.gleam", 822). ?DOC(false). -spec value_at(list(list(float())), integer(), integer()) -> float(). value_at(Rows, I, J) -> unsafe_at(unsafe_at(Rows, I), J). -file("src/viva_tensor/core/linalg.gleam", 442). ?DOC(false). -spec find_pivot(list(list(float())), integer(), integer()) -> {ok, integer()} | {error, nil}. find_pivot(Rows, K, N) -> Candidates = begin _pipe = range_int(K, N - 1), gleam@list:map( _pipe, fun(I) -> {I, gleam@float:absolute_value(value_at(Rows, I, K))} end ) end, case Candidates of [] -> {error, nil}; [First | Rest] -> {Best_idx, Best_val} = gleam@list:fold( Rest, First, fun(Acc, Candidate) -> {_, Acc_val} = Acc, {_, Cand_val} = Candidate, case Cand_val > Acc_val of true -> Candidate; false -> Acc end end ), case Best_val < 1.0e-12 of true -> {error, nil}; false -> {ok, Best_idx} end end. -file("src/viva_tensor/core/linalg.gleam", 404). ?DOC(false). -spec eliminate_loop(list(list(float())), integer(), integer()) -> {ok, list(list(float()))} | {error, viva_tensor@core@error:tensor_error()}. eliminate_loop(Rows, K, N) -> case K =:= N of true -> {ok, Rows}; false -> Pivot_idx = find_pivot(Rows, K, N), case Pivot_idx of {error, _} -> {error, {invalid_shape, <<"matrix is singular"/utf8>>}}; {ok, Idx} -> Swapped = swap_rows(Rows, K, Idx), Pivot_row = unsafe_at(Swapped, K), Pivot_value = unsafe_at(Pivot_row, K), case gleam@float:absolute_value(Pivot_value) < 1.0e-12 of true -> {error, {invalid_shape, <<"matrix is singular"/utf8>>}}; false -> Updated = begin _pipe = Swapped, gleam@list:index_map( _pipe, fun(Row, I) -> case I =:= K of true -> Row; false -> Factor = case Pivot_value of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> unsafe_at( Row, K ) / Gleam@denominator end, eliminate_row( Row, Pivot_row, Factor ) end end ) end, eliminate_loop(Updated, K + 1, N) end end end. -file("src/viva_tensor/core/linalg.gleam", 393). ?DOC(false). -spec gauss_eliminate(list(list(float())), integer(), integer()) -> {ok, list(list(float()))} | {error, viva_tensor@core@error:tensor_error()}. gauss_eliminate(Rows, N, Nrhs) -> case eliminate_loop(Rows, 0, N) of {error, E} -> {error, E}; {ok, Triangular} -> {ok, back_substitute(Triangular, N, Nrhs)} end. -file("src/viva_tensor/core/linalg.gleam", 802). ?DOC(false). -spec chunk(list(float()), integer()) -> list(list(float())). chunk(Data, N) -> case Data of [] -> []; _ -> [gleam@list:take(Data, N) | chunk(gleam@list:drop(Data, N), N)] end. -file("src/viva_tensor/core/linalg.gleam", 359). ?DOC(false). -spec rows_with_rhs( list(list(float())), viva_tensor@tensor:tensor(), integer(), integer() ) -> {ok, list(list(float()))} | {error, viva_tensor@core@error:tensor_error()}. rows_with_rhs(A_rows, B, N, Nrhs) -> gleam@result:'try'( viva_tensor@tensor:try_to_list(B), fun(B_data) -> case erlang:length(B_data) =:= (N * Nrhs) of false -> {error, {invalid_shape, <<<<<<"right-hand side data length "/utf8, (erlang:integer_to_binary( erlang:length(B_data) ))/binary>>/binary, " does not match expected "/utf8>>/binary, (erlang:integer_to_binary(N * Nrhs))/binary>>}}; true -> B_rows = chunk(B_data, Nrhs), {ok, begin _pipe = gleam@list:zip(A_rows, B_rows), gleam@list:map( _pipe, fun(Pair) -> {Arow, Brow} = Pair, lists:append(Arow, Brow) end ) end} end end ). -file("src/viva_tensor/core/linalg.gleam", 328). ?DOC(false). -spec rhs_rows(viva_tensor@tensor:tensor(), integer()) -> {ok, {integer(), integer(), boolean()}} | {error, viva_tensor@core@error:tensor_error()}. rhs_rows(B, N) -> case erlang:element(3, B) of [Rows] -> {ok, {Rows, 1, true}}; [Rows@1, Cols] -> {ok, {Rows@1, Cols, false}}; _ -> _ = N, {error, {dimension_error, <<"right-hand side must be 1D or 2D"/utf8>>}} end. -file("src/viva_tensor/core/linalg.gleam", 339). ?DOC(false). -spec to_rows(viva_tensor@tensor:tensor(), integer(), integer()) -> {ok, list(list(float()))} | {error, viva_tensor@core@error:tensor_error()}. to_rows(A, M, N) -> gleam@result:'try'( viva_tensor@tensor:try_to_list(A), fun(Data) -> case erlang:length(Data) =:= (M * N) of true -> {ok, chunk(Data, N)}; false -> {error, {invalid_shape, <<<<<<<<<<"tensor data length "/utf8, (erlang:integer_to_binary( erlang:length(Data) ))/binary>>/binary, " does not match shape "/utf8>>/binary, (erlang:integer_to_binary(M))/binary>>/binary, "x"/utf8>>/binary, (erlang:integer_to_binary(N))/binary>>}} end end ). -file("src/viva_tensor/core/linalg.gleam", 315). ?DOC(false). -spec square_matrix_rows(viva_tensor@tensor:tensor()) -> {ok, {integer(), list(list(float()))}} | {error, viva_tensor@core@error:tensor_error()}. square_matrix_rows(A) -> case erlang:element(3, A) of [N, M] when N =:= M -> gleam@result:'try'( to_rows(A, N, N), fun(Rows) -> {ok, {N, Rows}} end ); [N@1, M@1] -> {error, {shape_mismatch, [N@1, N@1], [N@1, M@1]}}; _ -> {error, {dimension_error, <<"expected a 2D square matrix"/utf8>>}} end. -file("src/viva_tensor/core/linalg.gleam", 53). ?DOC(false). -spec solve(viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor()) -> {ok, viva_tensor@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. solve(A, B) -> gleam@result:'try'( square_matrix_rows(A), fun(_use0) -> {N, A_rows} = _use0, gleam@result:'try'( rhs_rows(B, N), fun(_use0@1) -> {B_rows, Nrhs, B_is_vector} = _use0@1, case B_rows =:= N of false -> {error, {shape_mismatch, [N, Nrhs], [B_rows, Nrhs]}}; true -> gleam@result:'try'( rows_with_rhs(A_rows, B, N, Nrhs), fun(Rows) -> gleam@result:'try'( gauss_eliminate(Rows, N, Nrhs), fun(Solved) -> Flat = lists:append(Solved), case B_is_vector of true -> {ok, {tensor, Flat, [N]}}; false -> {ok, {tensor, Flat, [N, Nrhs]}} end end ) end ) end end ) end ). -file("src/viva_tensor/core/linalg.gleam", 243). ?DOC(false). -spec eye(integer()) -> viva_tensor@tensor:tensor(). eye(N) -> Data = begin _pipe = range_int(0, N - 1), gleam@list:flat_map(_pipe, fun(I) -> _pipe@1 = range_int(0, N - 1), gleam@list:map(_pipe@1, fun(J) -> case I =:= J of true -> 1.0; false -> +0.0 end end) end) end, {tensor, Data, [N, N]}. -file("src/viva_tensor/core/linalg.gleam", 85). ?DOC(false). -spec inv(viva_tensor@tensor:tensor()) -> {ok, viva_tensor@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. inv(A) -> gleam@result:'try'( square_matrix_rows(A), fun(_use0) -> {N, _} = _use0, Identity = eye(N), case solve(A, Identity) of {ok, X} -> {ok, X}; {error, {invalid_shape, <<"matrix is singular"/utf8>>}} -> {error, {invalid_shape, <<"matrix is singular"/utf8>>}}; {error, Other} -> {error, Other} end end ). -file("src/viva_tensor/core/linalg.gleam", 911). ?DOC(false). -spec set_in_row(list(float()), integer(), float()) -> list(float()). set_in_row(Row, Idx, Value) -> set_at(Row, Idx, Value). -file("src/viva_tensor/core/linalg.gleam", 606). ?DOC(false). -spec lu_eliminate_below( list(list(float())), list(list(float())), list(float()), integer(), integer() ) -> {list(list(float())), list(list(float()))}. lu_eliminate_below(Working, L, Pivot_row, K, N) -> Pivot_val = unsafe_at(Pivot_row, K), Updated = begin _pipe = Working, gleam@list:index_map(_pipe, fun(Row, I) -> case I =< K of true -> {Row, +0.0}; false -> Factor = case Pivot_val of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> unsafe_at(Row, K) / Gleam@denominator end, New_row = eliminate_row(Row, Pivot_row, Factor), {New_row, Factor} end end) end, Working_next = begin _pipe@1 = Updated, gleam@list:map(_pipe@1, fun(Pair) -> erlang:element(1, Pair) end) end, _ = N, L_next = begin _pipe@2 = L, gleam@list:index_map(_pipe@2, fun(Row@1, I@1) -> case I@1 =< K of true -> Row@1; false -> Factor@1 = case gleam@list:drop(Updated, I@1) of [{_, F} | _] -> F; _ -> +0.0 end, set_in_row(Row@1, K, Factor@1) end end) end, {Working_next, L_next}. -file("src/viva_tensor/core/linalg.gleam", 885). ?DOC(false). -spec take_below_k_from(list(float()), list(float()), integer()) -> list(float()). take_below_k_from(Source, Target, K) -> _pipe = gleam@list:zip(Source, Target), gleam@list:index_map( _pipe, fun(Pair, Idx) -> {S, T} = Pair, case Idx < K of true -> S; false -> T end end ). -file("src/viva_tensor/core/linalg.gleam", 867). ?DOC(false). -spec swap_l_below_k(list(list(float())), integer(), integer()) -> list(list(float())). swap_l_below_k(L, I, J) -> Row_i = unsafe_at(L, I), Row_j = unsafe_at(L, J), New_i = take_below_k_from(Row_j, Row_i, I), New_j = take_below_k_from(Row_i, Row_j, J), _pipe = L, gleam@list:index_map(_pipe, fun(Row, Idx) -> case {Idx =:= I, Idx =:= J} of {true, _} -> New_i; {_, true} -> New_j; {_, _} -> Row end end). -file("src/viva_tensor/core/linalg.gleam", 862). ?DOC(false). -spec unsafe_at_int(list(integer()), integer()) -> integer(). unsafe_at_int(Lst, Idx) -> V@1 = case list_at(Lst, Idx) of {ok, V} -> V; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"viva_tensor/core/linalg"/utf8>>, function => <<"unsafe_at_int"/utf8>>, line => 863, value => _assert_fail, start => 23844, 'end' => 23880, pattern_start => 23855, pattern_end => 23860}) end, V@1. -file("src/viva_tensor/core/linalg.gleam", 844). ?DOC(false). -spec swap_int_list(list(integer()), integer(), integer()) -> list(integer()). swap_int_list(Lst, I, J) -> case I =:= J of true -> Lst; false -> V_i = unsafe_at_int(Lst, I), V_j = unsafe_at_int(Lst, J), _pipe = Lst, gleam@list:index_map( _pipe, fun(V, Idx) -> case {Idx =:= I, Idx =:= J} of {true, _} -> V_j; {_, true} -> V_i; {_, _} -> V end end ) end. -file("src/viva_tensor/core/linalg.gleam", 602). ?DOC(false). -spec find_pivot_lu(list(list(float())), integer(), integer()) -> {ok, integer()} | {error, nil}. find_pivot_lu(Rows, K, N) -> find_pivot(Rows, K, N). -file("src/viva_tensor/core/linalg.gleam", 549). ?DOC(false). -spec lu_loop( list(list(float())), list(list(float())), list(list(float())), list(integer()), integer(), integer(), integer() ) -> {ok, lu_decomp()} | {error, lu_failure()}. lu_loop(Working, L, U, Perm, K, Swaps, N) -> case K =:= N of true -> {ok, {lu_decomp, L, U, Perm, Swaps}}; false -> Pivot_idx = find_pivot_lu(Working, K, N), case Pivot_idx of {error, _} -> {error, lu_singular}; {ok, Idx} -> {Swapped, Swap_inc} = case Idx =:= K of true -> {Working, 0}; false -> {swap_rows(Working, K, Idx), 1} end, Perm_swapped = case Idx =:= K of true -> Perm; false -> swap_int_list(Perm, K, Idx) end, L_swapped = case Idx =:= K of true -> L; false -> swap_l_below_k(L, K, Idx) end, Pivot_row = unsafe_at(Swapped, K), Pivot_val = unsafe_at(Pivot_row, K), case gleam@float:absolute_value(Pivot_val) < 1.0e-12 of true -> {error, lu_singular}; false -> U_updated = set_at(U, K, Pivot_row), {Working_next, L_next} = lu_eliminate_below( Swapped, L_swapped, Pivot_row, K, N ), lu_loop( Working_next, L_next, U_updated, Perm_swapped, K + 1, Swaps + Swap_inc, N ) end end end. -file("src/viva_tensor/core/linalg.gleam", 529). ?DOC(false). -spec lu_decompose(list(list(float())), integer()) -> {ok, lu_decomp()} | {error, lu_failure()}. lu_decompose(Rows, N) -> Perm = range_int(0, N - 1), L_init = begin _pipe = range_int(0, N - 1), gleam@list:map(_pipe, fun(I) -> _pipe@1 = range_int(0, N - 1), gleam@list:map(_pipe@1, fun(J) -> case I =:= J of true -> 1.0; false -> +0.0 end end) end) end, U_init = gleam@list:repeat(gleam@list:repeat(+0.0, N), N), lu_loop(Rows, L_init, U_init, Perm, 0, 0, N). -file("src/viva_tensor/core/linalg.gleam", 112). ?DOC(false). -spec det(viva_tensor@tensor:tensor()) -> {ok, float()} | {error, viva_tensor@core@error:tensor_error()}. det(A) -> gleam@result:'try'( square_matrix_rows(A), fun(_use0) -> {N, Rows} = _use0, case lu_decompose(Rows, N) of {error, lu_singular} -> {ok, +0.0}; {error, {lu_error, E}} -> {error, E}; {ok, Decomp} -> Diag_product = begin _pipe = range_int(0, N - 1), gleam@list:fold( _pipe, 1.0, fun(Acc, I) -> Row = unsafe_at(erlang:element(3, Decomp), I), Acc * unsafe_at(Row, I) end ) end, Sign = case gleam@int:is_even(erlang:element(5, Decomp)) of true -> 1.0; false -> -1.0 end, {ok, Sign * Diag_product} end end ). -file("src/viva_tensor/core/linalg.gleam", 152). ?DOC(false). -spec lu(viva_tensor@tensor:tensor()) -> {ok, {viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor(), list(integer())}} | {error, viva_tensor@core@error:tensor_error()}. lu(A) -> gleam@result:'try'( square_matrix_rows(A), fun(_use0) -> {N, Rows} = _use0, case lu_decompose(Rows, N) of {error, lu_singular} -> {error, {invalid_shape, <<"matrix is singular"/utf8>>}}; {error, {lu_error, E}} -> {error, E}; {ok, Decomp} -> L_flat = lists:append(erlang:element(2, Decomp)), U_flat = lists:append(erlang:element(3, Decomp)), {ok, {{tensor, L_flat, [N, N]}, {tensor, U_flat, [N, N]}, erlang:element(4, Decomp)}} end end ). -file("src/viva_tensor/core/linalg.gleam", 686). ?DOC(false). -spec cholesky_row_loop( list(float()), list(list(float())), integer(), integer() ) -> {ok, list(list(float()))} | {error, viva_tensor@core@error:tensor_error()}. cholesky_row_loop(A_i, L, I, J) -> case J > I of true -> {ok, L}; false -> A_ij = unsafe_at(A_i, J), L_i = unsafe_at(L, I), L_j = unsafe_at(L, J), Dot_sum = begin _pipe = int_range(0, J - 1), gleam@list:fold( _pipe, +0.0, fun(Acc, K) -> Acc + (unsafe_at(L_i, K) * unsafe_at(L_j, K)) end ) end, Raw = A_ij - Dot_sum, case I =:= J of true -> case Raw =< +0.0 of true -> {error, {invalid_shape, <<"matrix is not positive definite"/utf8>>}}; false -> Value = math:sqrt(Raw), New_l_i = set_in_row(L_i, J, Value), {ok, set_at(L, I, New_l_i)} end; false -> L_jj = unsafe_at(L_j, J), case gleam@float:absolute_value(L_jj) < 1.0e-15 of true -> {error, {invalid_shape, <<"matrix is not positive definite"/utf8>>}}; false -> Value@1 = case L_jj of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> Raw / Gleam@denominator end, New_l_i@1 = set_in_row(L_i, J, Value@1), New_l = set_at(L, I, New_l_i@1), cholesky_row_loop(A_i, New_l, I, J + 1) end end end. -file("src/viva_tensor/core/linalg.gleam", 676). ?DOC(false). -spec cholesky_row( list(list(float())), list(list(float())), integer(), integer() ) -> {ok, list(list(float()))} | {error, viva_tensor@core@error:tensor_error()}. cholesky_row(A, L, I, _) -> A_i = unsafe_at(A, I), cholesky_row_loop(A_i, L, I, 0). -file("src/viva_tensor/core/linalg.gleam", 659). ?DOC(false). -spec cholesky_loop( list(list(float())), list(list(float())), integer(), integer() ) -> {ok, list(list(float()))} | {error, viva_tensor@core@error:tensor_error()}. cholesky_loop(A, L, I, N) -> case I =:= N of true -> {ok, L}; false -> case cholesky_row(A, L, I, N) of {error, E} -> {error, E}; {ok, New_l} -> cholesky_loop(A, New_l, I + 1, N) end end. -file("src/viva_tensor/core/linalg.gleam", 651). ?DOC(false). -spec cholesky_rows(list(list(float())), integer()) -> {ok, list(list(float()))} | {error, viva_tensor@core@error:tensor_error()}. cholesky_rows(Rows, N) -> L_init = gleam@list:repeat(gleam@list:repeat(+0.0, N), N), cholesky_loop(Rows, L_init, 0, N). -file("src/viva_tensor/core/linalg.gleam", 188). ?DOC(false). -spec cholesky(viva_tensor@tensor:tensor()) -> {ok, viva_tensor@tensor:tensor()} | {error, viva_tensor@core@error:tensor_error()}. cholesky(A) -> gleam@result:'try'( square_matrix_rows(A), fun(_use0) -> {N, Rows} = _use0, gleam@result:'try'( cholesky_rows(Rows, N), fun(L_rows) -> {ok, {tensor, lists:append(L_rows), [N, N]}} end ) end ). -file("src/viva_tensor/core/linalg.gleam", 923). ?DOC(false). -spec transpose_rows(list(list(float())), integer(), integer()) -> list(list(float())). transpose_rows(Rows, _, N) -> _pipe = range_int(0, N - 1), gleam@list:map(_pipe, fun(J) -> _pipe@1 = Rows, gleam@list:map(_pipe@1, fun(Row) -> unsafe_at(Row, J) end) end). -file("src/viva_tensor/core/linalg.gleam", 915). ?DOC(false). -spec dot(list(float()), list(float())) -> float(). dot(A, B) -> _pipe = gleam@list:zip(A, B), gleam@list:fold( _pipe, +0.0, fun(Acc, Pair) -> {X, Y} = Pair, Acc + (X * Y) end ). -file("src/viva_tensor/core/linalg.gleam", 745). ?DOC(false). -spec gram_schmidt_loop( list(list(float())), list(list(float())), list(list(float())), integer(), integer() ) -> {ok, {list(list(float())), list(list(float()))}} | {error, viva_tensor@core@error:tensor_error()}. gram_schmidt_loop(Cols, Q, R, K, N) -> case K =:= N of true -> {ok, {Q, R}}; false -> A_k = unsafe_at(Cols, K), {V, R_updated} = begin _pipe = int_range(0, K - 1), gleam@list:fold( _pipe, {A_k, R}, fun(Acc, J) -> {Current_v, Current_r} = Acc, Q_j = unsafe_at(Q, J), R_jk = dot(Q_j, A_k), New_v = begin _pipe@1 = gleam@list:zip(Current_v, Q_j), gleam@list:map( _pipe@1, fun(Pair) -> {Vv, Qq} = Pair, Vv - (R_jk * Qq) end ) end, R_row_j = unsafe_at(Current_r, J), New_r_row_j = set_in_row(R_row_j, K, R_jk), New_r = set_at(Current_r, J, New_r_row_j), {New_v, New_r} end ) end, Norm_v = math:sqrt(dot(V, V)), case Norm_v < 1.0e-15 of true -> {error, {invalid_shape, <<"qr: columns are linearly dependent"/utf8>>}}; false -> Q_k = gleam@list:map(V, fun(X) -> case Norm_v of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> X / Gleam@denominator end end), Q_next = set_at(Q, K, Q_k), R_row_k = unsafe_at(R_updated, K), New_r_row_k = set_in_row(R_row_k, K, Norm_v), R_next = set_at(R_updated, K, New_r_row_k), gram_schmidt_loop(Cols, Q_next, R_next, K + 1, N) end end. -file("src/viva_tensor/core/linalg.gleam", 735). ?DOC(false). -spec gram_schmidt(list(list(float())), integer(), integer()) -> {ok, {list(list(float())), list(list(float()))}} | {error, viva_tensor@core@error:tensor_error()}. gram_schmidt(Cols, M, N) -> R_init = gleam@list:repeat(gleam@list:repeat(+0.0, N), N), Q_init = gleam@list:repeat(gleam@list:repeat(+0.0, M), N), gram_schmidt_loop(Cols, Q_init, R_init, 0, N). -file("src/viva_tensor/core/linalg.gleam", 214). ?DOC(false). -spec qr(viva_tensor@tensor:tensor()) -> {ok, {viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor()}} | {error, viva_tensor@core@error:tensor_error()}. qr(A) -> case erlang:element(3, A) of [M, N] -> gleam@result:'try'( to_rows(A, M, N), fun(Rows) -> Cols = transpose_rows(Rows, M, N), gleam@result:'try'( gram_schmidt(Cols, M, N), fun(_use0) -> {Q_cols, R_rows} = _use0, Q_rows = transpose_rows(Q_cols, N, M), {ok, {{tensor, lists:append(Q_rows), [M, N]}, {tensor, lists:append(R_rows), [N, N]}}} end ) end ); _ -> {error, {dimension_error, <<"qr requires a 2D matrix"/utf8>>}} end. -file("src/viva_tensor/core/linalg.gleam", 271). ?DOC(false). -spec svd(viva_tensor@tensor:tensor()) -> {ok, {viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor()}} | {error, viva_tensor@core@error:tensor_error()}. svd(_) -> {error, {dimension_error, <<"svd: not implemented in v1; use a NIF in v2"/utf8>>}}. -file("src/viva_tensor/core/linalg.gleam", 289). ?DOC(false). -spec eig(viva_tensor@tensor:tensor()) -> {ok, {viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor()}} | {error, viva_tensor@core@error:tensor_error()}. eig(_) -> {error, {dimension_error, <<"eig: not implemented in v1; use a NIF in v2"/utf8>>}}.