Packages
Tensor library for Gleam/BEAM with a pure Gleam API, zero-copy views, and optional native acceleration
Current section
Files
Jump to
Current section
Files
src/viva_tensor@core@linalg.erl
-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(QZR), integer(), QZR) -> list(QZR).
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(QYX), integer()) -> {ok, QYX} | {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(QYV), integer()) -> QYV.
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 => <<?FILEPATH/utf8>>,
module => <<"viva_tensor/core/linalg"/utf8>>,
function => <<"unsafe_at"/utf8>>,
line => 810,
value => _assert_fail,
start => 22689,
'end' => 22729,
pattern_start => 22700,
pattern_end => 22709})
end,
Value@1.
-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 ->
gleam@list:range(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 = gleam@list:range(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 = gleam@list:range(0, N - 1),
gleam@list:flat_map(
_pipe,
fun(I) -> _pipe@1 = gleam@list:range(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 => <<?FILEPATH/utf8>>,
module => <<"viva_tensor/core/linalg"/utf8>>,
function => <<"unsafe_at_int"/utf8>>,
line => 863,
value => _assert_fail,
start => 23837,
'end' => 23873,
pattern_start => 23848,
pattern_end => 23853})
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 = gleam@list:range(0, N - 1),
L_init = begin
_pipe = gleam@list:range(0, N - 1),
gleam@list:map(_pipe, fun(I) -> _pipe@1 = gleam@list:range(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 = gleam@list:range(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 = gleam@list:range(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>>}}.