Current section

Files

Jump to
viva_tensor src viva_tensor@nn@cv.erl
Raw

src/viva_tensor@nn@cv.erl

-module(viva_tensor@nn@cv).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_tensor/nn/cv.gleam").
-export([max_pool_2d_with_indices/4, max_unpool_2d_forward/4, nms/3, roi_align/3, batched_matmul/2, batch_norm_2d_init/1, batch_norm_2d_forward/3]).
-export_type([max_unpool2d_config/0, max_acc/0, roi_align_config/0, batch_norm2d/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 max_unpool2d_config() :: {max_unpool2d_config,
integer(),
integer(),
integer()}.
-type max_acc() :: none2 | {some2, float(), integer()}.
-type roi_align_config() :: {roi_align_config,
integer(),
integer(),
float(),
integer()}.
-type batch_norm2d() :: {batch_norm2d,
viva_tensor@tensor:tensor(),
viva_tensor@tensor:tensor(),
viva_tensor@tensor:tensor(),
viva_tensor@tensor:tensor(),
float(),
float()}.
-file("src/viva_tensor/nn/cv.gleam", 1073).
?DOC(false).
-spec join_with(list(binary()), binary()) -> binary().
join_with(Parts, Sep) ->
case Parts of
[] ->
<<""/utf8>>;
[S] ->
S;
[S@1 | Rest] ->
<<<<S@1/binary, Sep/binary>>/binary, (join_with(Rest, Sep))/binary>>
end.
-file("src/viva_tensor/nn/cv.gleam", 1069).
?DOC(false).
-spec shape_to_string(list(integer())) -> binary().
shape_to_string(Shape) ->
<<<<"["/utf8,
(join_with(
gleam@list:map(Shape, fun erlang:integer_to_binary/1),
<<", "/utf8>>
))/binary>>/binary,
"]"/utf8>>.
-file("src/viva_tensor/nn/cv.gleam", 234).
?DOC(false).
-spec scan_window_cols(
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
max_acc()
) -> max_acc().
scan_window_cols(
Arr,
Base,
H_in,
W_in,
H_start,
W_start,
Kernel_size,
Ky,
Kx,
Acc
) ->
case Kx >= Kernel_size of
true ->
Acc;
false ->
Y = H_start + Ky,
X = W_start + Kx,
Acc2 = case (((Y >= 0) andalso (Y < H_in)) andalso (X >= 0)) andalso (X
< W_in) of
false ->
Acc;
true ->
Flat = (Y * W_in) + X,
V = viva_tensor@core@ffi:array_get(Arr, Base + Flat),
case Acc of
none2 ->
{some2, V, Flat};
{some2, Mv, _} ->
case V > Mv of
true ->
{some2, V, Flat};
false ->
Acc
end
end
end,
scan_window_cols(
Arr,
Base,
H_in,
W_in,
H_start,
W_start,
Kernel_size,
Ky,
Kx + 1,
Acc2
)
end.
-file("src/viva_tensor/nn/cv.gleam", 188).
?DOC(false).
-spec scan_window_rows(
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
max_acc()
) -> {float(), integer()}.
scan_window_rows(Arr, Base, H_in, W_in, H_start, W_start, Kernel_size, Ky, Acc) ->
case Ky >= Kernel_size of
true ->
case Acc of
none2 ->
{+0.0, -1};
{some2, V, I} ->
{V, I}
end;
false ->
Acc2 = scan_window_cols(
Arr,
Base,
H_in,
W_in,
H_start,
W_start,
Kernel_size,
Ky,
0,
Acc
),
scan_window_rows(
Arr,
Base,
H_in,
W_in,
H_start,
W_start,
Kernel_size,
Ky + 1,
Acc2
)
end.
-file("src/viva_tensor/nn/cv.gleam", 161).
?DOC(false).
-spec max_in_window(
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> {float(), integer()}.
max_in_window(Arr, Base, H_in, W_in, H_start, W_start, Kernel_size) ->
scan_window_rows(
Arr,
Base,
H_in,
W_in,
H_start,
W_start,
Kernel_size,
0,
none2
).
-file("src/viva_tensor/nn/cv.gleam", 1085).
?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/nn/cv.gleam", 1081).
?DOC(false).
-spec range_int(integer(), integer()) -> list(integer()).
range_int(From, To) ->
range_loop(From, To, []).
-file("src/viva_tensor/nn/cv.gleam", 124).
?DOC(false).
-spec pool_with_indices_compute(
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> {list(float()), list(float())}.
pool_with_indices_compute(
Arr,
N,
C,
H_in,
W_in,
Out_h,
Out_w,
Kernel_size,
Stride,
Padding
) ->
Cells = begin
_pipe = range_int(0, N - 1),
gleam@list:flat_map(_pipe, fun(B) -> _pipe@1 = range_int(0, C - 1),
gleam@list:flat_map(
_pipe@1,
fun(Ch) ->
Base = (((B * C) * H_in) * W_in) + ((Ch * H_in) * W_in),
_pipe@2 = range_int(0, Out_h - 1),
gleam@list:flat_map(
_pipe@2,
fun(Oh) ->
H_start = (Oh * Stride) - Padding,
_pipe@3 = range_int(0, Out_w - 1),
gleam@list:map(
_pipe@3,
fun(Ow) ->
W_start = (Ow * Stride) - Padding,
max_in_window(
Arr,
Base,
H_in,
W_in,
H_start,
W_start,
Kernel_size
)
end
)
end
)
end
) end)
end,
Values = gleam@list:map(Cells, fun(P) -> erlang:element(1, P) end),
Indices = gleam@list:map(
Cells,
fun(P@1) -> erlang:float(erlang:element(2, P@1)) end
),
{Values, Indices}.
-file("src/viva_tensor/nn/cv.gleam", 62).
?DOC(false).
-spec max_pool_2d_with_indices(
viva_tensor@tensor:tensor(),
integer(),
integer(),
integer()
) -> {ok, {viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor()}} |
{error, viva_tensor@core@error:tensor_error()}.
max_pool_2d_with_indices(Input, Kernel_size, Stride, Padding) ->
case viva_tensor@tensor:shape(Input) of
[N, C, H_in, W_in] ->
case (Kernel_size =< 0) orelse (Stride =< 0) of
true ->
{error,
{invalid_shape,
<<<<<<"max_pool_2d_with_indices: kernel_size and stride must be positive, got kernel="/utf8,
(erlang:integer_to_binary(Kernel_size))/binary>>/binary,
" stride="/utf8>>/binary,
(erlang:integer_to_binary(Stride))/binary>>}};
false ->
Padded_h = H_in + (2 * Padding),
Padded_w = W_in + (2 * Padding),
Out_h = (case Stride of
0 -> 0;
Gleam@denominator -> (Padded_h - Kernel_size) div Gleam@denominator
end) + 1,
Out_w = (case Stride of
0 -> 0;
Gleam@denominator@1 -> (Padded_w - Kernel_size) div Gleam@denominator@1
end) + 1,
case (Out_h =< 0) orelse (Out_w =< 0) of
true ->
{error,
{invalid_shape,
<<<<<<<<"max_pool_2d_with_indices: invalid output dims ("/utf8,
(erlang:integer_to_binary(
Out_h
))/binary>>/binary,
", "/utf8>>/binary,
(erlang:integer_to_binary(Out_w))/binary>>/binary,
")"/utf8>>}};
false ->
Arr = viva_tensor@core@ffi:list_to_array(
viva_tensor@tensor:to_list(Input)
),
{Values, Indices} = pool_with_indices_compute(
Arr,
N,
C,
H_in,
W_in,
Out_h,
Out_w,
Kernel_size,
Stride,
Padding
),
Val_tensor = {tensor, Values, [N, C, Out_h, Out_w]},
Idx_tensor = {tensor, Indices, [N, C, Out_h, Out_w]},
{ok, {Val_tensor, Idx_tensor}}
end
end;
Other ->
{error,
{invalid_shape,
<<"max_pool_2d_with_indices: input must have shape [N, C, H, W], got "/utf8,
(shape_to_string(Other))/binary>>}}
end.
-file("src/viva_tensor/nn/cv.gleam", 343).
?DOC(false).
-spec unpool_fill(
integer(),
integer(),
integer(),
integer(),
integer(),
list(float()),
list(float())
) -> list(float()).
unpool_fill(N, C, H_out, W_out, Plane, Values, Idxs) ->
Per_chan = H_out * W_out,
_pipe = range_int(0, N - 1),
gleam@list:flat_map(_pipe, fun(B) -> _pipe@1 = range_int(0, C - 1),
gleam@list:flat_map(
_pipe@1,
fun(Ch) ->
Start = ((B * C) + Ch) * Per_chan,
Chan_vals = gleam@list:take(
gleam@list:drop(Values, Start),
Per_chan
),
Chan_idxs = gleam@list:take(
gleam@list:drop(Idxs, Start),
Per_chan
),
Scatter = gleam@list:fold(
gleam@list:zip(Chan_idxs, Chan_vals),
maps:new(),
fun(Acc, Pair) ->
{Idx_f, V} = Pair,
Idx = erlang:trunc(Idx_f),
case (Idx >= 0) andalso (Idx < Plane) of
true ->
gleam@dict:insert(Acc, Idx, V);
false ->
Acc
end
end
),
_pipe@2 = range_int(0, Plane - 1),
gleam@list:map(
_pipe@2,
fun(I) -> case gleam_stdlib:map_get(Scatter, I) of
{ok, V@1} ->
V@1;
{error, _} ->
+0.0
end end
)
end
) end).
-file("src/viva_tensor/nn/cv.gleam", 296).
?DOC(false).
-spec max_unpool_2d_forward(
max_unpool2d_config(),
viva_tensor@tensor:tensor(),
viva_tensor@tensor:tensor(),
{integer(), integer()}
) -> {ok, viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
max_unpool_2d_forward(_, Input, Indices, Output_size) ->
{H_in, W_in} = Output_size,
case {viva_tensor@tensor:shape(Input), viva_tensor@tensor:shape(Indices)} of
{[N, C, H_out, W_out], [Ni, Ci, Hi, Wi]} ->
case (((N =:= Ni) andalso (C =:= Ci)) andalso (H_out =:= Hi))
andalso (W_out =:= Wi) of
false ->
{error,
{shape_mismatch, [N, C, H_out, W_out], [Ni, Ci, Hi, Wi]}};
true ->
case (H_in =< 0) orelse (W_in =< 0) of
true ->
{error,
{invalid_shape,
<<<<<<<<"max_unpool_2d: output_size must be positive, got ("/utf8,
(erlang:integer_to_binary(
H_in
))/binary>>/binary,
", "/utf8>>/binary,
(erlang:integer_to_binary(W_in))/binary>>/binary,
")"/utf8>>}};
false ->
Values = viva_tensor@tensor:to_list(Input),
Idxs = viva_tensor@tensor:to_list(Indices),
Plane = H_in * W_in,
Out = unpool_fill(
N,
C,
H_out,
W_out,
Plane,
Values,
Idxs
),
{ok, {tensor, Out, [N, C, H_in, W_in]}}
end
end;
{Other_in, _} ->
{error,
{invalid_shape,
<<"max_unpool_2d: input must have shape [N, C, H_out, W_out], got "/utf8,
(shape_to_string(Other_in))/binary>>}}
end.
-file("src/viva_tensor/nn/cv.gleam", 446).
?DOC(false).
-spec iou(list(float()), list(float())) -> float().
iou(A, B) ->
case {A, B} of
{[Ax1, Ay1, Ax2, Ay2], [Bx1, By1, Bx2, By2]} ->
Inter_x1 = gleam@float:max(Ax1, Bx1),
Inter_y1 = gleam@float:max(Ay1, By1),
Inter_x2 = gleam@float:min(Ax2, Bx2),
Inter_y2 = gleam@float:min(Ay2, By2),
Iw = gleam@float:max(+0.0, Inter_x2 - Inter_x1),
Ih = gleam@float:max(+0.0, Inter_y2 - Inter_y1),
Inter = Iw * Ih,
Area_a = gleam@float:max(+0.0, Ax2 - Ax1) * gleam@float:max(
+0.0,
Ay2 - Ay1
),
Area_b = gleam@float:max(+0.0, Bx2 - Bx1) * gleam@float:max(
+0.0,
By2 - By1
),
Union = (Area_a + Area_b) - Inter,
case Union =< +0.0 of
true ->
+0.0;
false ->
case Union of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Inter / Gleam@denominator
end
end;
{_, _} ->
+0.0
end.
-file("src/viva_tensor/nn/cv.gleam", 468).
?DOC(false).
-spec list_at_list(list(list(float())), integer()) -> list(float()).
list_at_list(Xs, Idx) ->
case {Xs, Idx} of
{[], _} ->
[];
{[X | _], 0} ->
X;
{[_ | Rest], I} ->
list_at_list(Rest, I - 1)
end.
-file("src/viva_tensor/nn/cv.gleam", 426).
?DOC(false).
-spec nms_loop(list(integer()), list(list(float())), float(), list(integer())) -> list(integer()).
nms_loop(Order, Box_rows, Iou_threshold, Kept) ->
case Order of
[] ->
Kept;
[Top | Rest] ->
Top_box = list_at_list(Box_rows, Top),
Remaining = gleam@list:filter(
Rest,
fun(J) ->
Other = list_at_list(Box_rows, J),
iou(Top_box, Other) =< Iou_threshold
end
),
nms_loop(Remaining, Box_rows, Iou_threshold, [Top | Kept])
end.
-file("src/viva_tensor/nn/cv.gleam", 1032).
?DOC(false).
-spec chunk_by_loop(list(float()), integer(), list(list(float()))) -> list(list(float())).
chunk_by_loop(Data, N, Acc) ->
case Data of
[] ->
lists:reverse(Acc);
_ ->
Chunk = gleam@list:take(Data, N),
Rest = gleam@list:drop(Data, N),
chunk_by_loop(Rest, N, [Chunk | Acc])
end.
-file("src/viva_tensor/nn/cv.gleam", 1025).
?DOC(false).
-spec chunk_by(list(float()), integer()) -> list(list(float())).
chunk_by(Data, N) ->
case N =< 0 of
true ->
[];
false ->
chunk_by_loop(Data, N, [])
end.
-file("src/viva_tensor/nn/cv.gleam", 398).
?DOC(false).
-spec nms(viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor(), float()) -> {ok,
list(integer())} |
{error, viva_tensor@core@error:tensor_error()}.
nms(Boxes, Scores, Iou_threshold) ->
case viva_tensor@tensor:shape(Boxes) of
[N, 4] ->
case viva_tensor@tensor:shape(Scores) of
[Ns] when Ns =:= N ->
Box_rows = chunk_by(viva_tensor@tensor:to_list(Boxes), 4),
Score_list = viva_tensor@tensor:to_list(Scores),
Indexed = begin
_pipe = gleam@list:index_map(
Score_list,
fun(S, I) -> {I, S} end
),
gleam@list:sort(
_pipe,
fun(A, B) ->
gleam@float:compare(
erlang:element(2, B),
erlang:element(2, A)
)
end
)
end,
Order = gleam@list:map(
Indexed,
fun(P) -> erlang:element(1, P) end
),
Kept = nms_loop(Order, Box_rows, Iou_threshold, []),
{ok, lists:reverse(Kept)};
Other ->
{error, {shape_mismatch, [N], Other}}
end;
Other@1 ->
{error,
{invalid_shape,
<<"nms: boxes must have shape [N, 4], got "/utf8,
(shape_to_string(Other@1))/binary>>}}
end.
-file("src/viva_tensor/nn/cv.gleam", 697).
?DOC(false).
-spec clamp_float(float(), float(), float()) -> float().
clamp_float(V, Lo, Hi) ->
case V < Lo of
true ->
Lo;
false ->
case V > Hi of
true ->
Hi;
false ->
V
end
end.
-file("src/viva_tensor/nn/cv.gleam", 661).
?DOC(false).
-spec bilinear_sample(
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer(),
float(),
float()
) -> float().
bilinear_sample(Feat, Base, H, W, Y, X) ->
case (((Y < -1.0) orelse (Y > erlang:float(H))) orelse (X < -1.0)) orelse (X
> erlang:float(W)) of
true ->
+0.0;
false ->
Yc = clamp_float(Y, +0.0, erlang:float(H - 1)),
Xc = clamp_float(X, +0.0, erlang:float(W - 1)),
Y0 = erlang:trunc(Yc),
X0 = erlang:trunc(Xc),
Y1 = case (Y0 + 1) >= H of
true ->
H - 1;
false ->
Y0 + 1
end,
X1 = case (X0 + 1) >= W of
true ->
W - 1;
false ->
X0 + 1
end,
Dy = Yc - erlang:float(Y0),
Dx = Xc - erlang:float(X0),
V00 = viva_tensor@core@ffi:array_get(Feat, (Base + (Y0 * W)) + X0),
V01 = viva_tensor@core@ffi:array_get(Feat, (Base + (Y0 * W)) + X1),
V10 = viva_tensor@core@ffi:array_get(Feat, (Base + (Y1 * W)) + X0),
V11 = viva_tensor@core@ffi:array_get(Feat, (Base + (Y1 * W)) + X1),
Top = (V00 * (1.0 - Dx)) + (V01 * Dx),
Bot = (V10 * (1.0 - Dx)) + (V11 * Dx),
(Top * (1.0 - Dy)) + (Bot * Dy)
end.
-file("src/viva_tensor/nn/cv.gleam", 1013).
?DOC(false).
-spec sum_range_acc(integer(), integer(), fun((integer()) -> float()), float()) -> float().
sum_range_acc(Start, End, F, Acc) ->
case Start > End of
true ->
Acc;
false ->
sum_range_acc(Start + 1, End, F, Acc + F(Start))
end.
-file("src/viva_tensor/nn/cv.gleam", 1009).
?DOC(false).
-spec sum_range(integer(), integer(), fun((integer()) -> float())) -> float().
sum_range(Start, End, F) ->
sum_range_acc(Start, End, F, +0.0).
-file("src/viva_tensor/nn/cv.gleam", 631).
?DOC(false).
-spec roi_bin_value(
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer(),
float(),
float(),
float(),
float(),
integer(),
integer(),
integer(),
float(),
float()
) -> float().
roi_bin_value(
Feat,
Base,
H,
W,
Sx1,
Sy1,
Bin_w,
Bin_h,
Oh,
Ow,
Sampling_ratio,
Ratio_f,
Inv_samples
) ->
Bin_x_start = Sx1 + (erlang:float(Ow) * Bin_w),
Bin_y_start = Sy1 + (erlang:float(Oh) * Bin_h),
Sub_w = case Ratio_f of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Bin_w / Gleam@denominator
end,
Sub_h = case Ratio_f of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Bin_h / Gleam@denominator@1
end,
Sum = sum_range(
0,
Sampling_ratio - 1,
fun(Iy) ->
Y = Bin_y_start + ((erlang:float(Iy) + 0.5) * Sub_h),
sum_range(
0,
Sampling_ratio - 1,
fun(Ix) ->
X = Bin_x_start + ((erlang:float(Ix) + 0.5) * Sub_w),
bilinear_sample(Feat, Base, H, W, Y, X)
end
)
end
),
Sum * Inv_samples.
-file("src/viva_tensor/nn/cv.gleam", 569).
?DOC(false).
-spec roi_align_compute(
viva_tensor@core@ffi:erlang_array(),
list(list(float())),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
float(),
integer()
) -> list(float()).
roi_align_compute(
Feat,
Roi_rows,
N,
C,
H,
W,
Out_h,
Out_w,
Spatial_scale,
Sampling_ratio
) ->
Ratio_f = erlang:float(Sampling_ratio),
Inv_samples = case (Ratio_f * Ratio_f) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> 1.0 / Gleam@denominator
end,
gleam@list:flat_map(Roi_rows, fun(Row) -> case Row of
[Bi_f, X1, Y1, X2, Y2] ->
Bi_raw = erlang:trunc(Bi_f),
Bi = case Bi_raw < 0 of
true ->
0;
false ->
case Bi_raw >= N of
true ->
N - 1;
false ->
Bi_raw
end
end,
Sx1 = X1 * Spatial_scale,
Sy1 = Y1 * Spatial_scale,
Sx2 = X2 * Spatial_scale,
Sy2 = Y2 * Spatial_scale,
Roi_w = gleam@float:max(Sx2 - Sx1, 1.0),
Roi_h = gleam@float:max(Sy2 - Sy1, 1.0),
Bin_w = case erlang:float(Out_w) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Roi_w / Gleam@denominator@1
end,
Bin_h = case erlang:float(Out_h) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@2 -> Roi_h / Gleam@denominator@2
end,
gleam@list:flat_map(
range_int(0, C - 1),
fun(Ch) ->
Base = (((Bi * C) * H) * W) + ((Ch * H) * W),
gleam@list:flat_map(
range_int(0, Out_h - 1),
fun(Oh) ->
gleam@list:map(
range_int(0, Out_w - 1),
fun(Ow) ->
roi_bin_value(
Feat,
Base,
H,
W,
Sx1,
Sy1,
Bin_w,
Bin_h,
Oh,
Ow,
Sampling_ratio,
Ratio_f,
Inv_samples
)
end
)
end
)
end
);
_ ->
[]
end end).
-file("src/viva_tensor/nn/cv.gleam", 509).
?DOC(false).
-spec roi_align(
roi_align_config(),
viva_tensor@tensor:tensor(),
viva_tensor@tensor:tensor()
) -> {ok, viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
roi_align(Config, Features, Rois) ->
case viva_tensor@tensor:shape(Features) of
[N, C, H, W] ->
case viva_tensor@tensor:shape(Rois) of
[K, 5] ->
case ((erlang:element(2, Config) =< 0) orelse (erlang:element(
3,
Config
)
=< 0))
orelse (erlang:element(5, Config) =< 0) of
true ->
{error,
{invalid_shape,
<<"roi_align: output_h, output_w and sampling_ratio must be positive"/utf8>>}};
false ->
Feat = viva_tensor@core@ffi:list_to_array(
viva_tensor@tensor:to_list(Features)
),
Roi_rows = chunk_by(
viva_tensor@tensor:to_list(Rois),
5
),
Out = roi_align_compute(
Feat,
Roi_rows,
N,
C,
H,
W,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config)
),
{ok,
{tensor,
Out,
[K,
C,
erlang:element(2, Config),
erlang:element(3, Config)]}}
end;
Other ->
{error,
{invalid_shape,
<<"roi_align: rois must have shape [K, 5], got "/utf8,
(shape_to_string(Other))/binary>>}}
end;
Other@1 ->
{error,
{invalid_shape,
<<"roi_align: features must have shape [N, C, H, W], got "/utf8,
(shape_to_string(Other@1))/binary>>}}
end.
-file("src/viva_tensor/nn/cv.gleam", 760).
?DOC(false).
-spec bmm_compute(
viva_tensor@core@ffi:erlang_array(),
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> list(float()).
bmm_compute(A_arr, B_arr, Out_b, M, K, N, Ba, Bb) ->
_pipe = range_int(0, Out_b - 1),
gleam@list:flat_map(
_pipe,
fun(Batch) ->
Ai = case Ba =:= 1 of
true ->
0;
false ->
Batch
end,
Bi = case Bb =:= 1 of
true ->
0;
false ->
Batch
end,
A_base = (Ai * M) * K,
B_base = (Bi * K) * N,
_pipe@1 = range_int(0, M - 1),
gleam@list:flat_map(
_pipe@1,
fun(I) -> _pipe@2 = range_int(0, N - 1),
gleam@list:map(
_pipe@2,
fun(J) ->
sum_range(
0,
K - 1,
fun(P) ->
Av = viva_tensor@core@ffi:array_get(
A_arr,
(A_base + (I * K)) + P
),
Bv = viva_tensor@core@ffi:array_get(
B_arr,
(B_base + (P * N)) + J
),
Av * Bv
end
)
end
) end
)
end
).
-file("src/viva_tensor/nn/cv.gleam", 745).
?DOC(false).
-spec batch_dim(integer(), integer()) -> {ok, integer()} |
{error, viva_tensor@core@error:tensor_error()}.
batch_dim(Ba, Bb) ->
case Ba =:= Bb of
true ->
{ok, Ba};
false ->
case Ba =:= 1 of
true ->
{ok, Bb};
false ->
case Bb =:= 1 of
true ->
{ok, Ba};
false ->
{error, {shape_mismatch, [Ba], [Bb]}}
end
end
end.
-file("src/viva_tensor/nn/cv.gleam", 718).
?DOC(false).
-spec batched_matmul(viva_tensor@tensor:tensor(), viva_tensor@tensor:tensor()) -> {ok,
viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
batched_matmul(A, B) ->
case {viva_tensor@tensor:shape(A), viva_tensor@tensor:shape(B)} of
{[Ba, M, Ka], [Bb, Kb, N]} ->
case Ka =:= Kb of
false ->
{error, {shape_mismatch, [Bb, Ka, N], [Bb, Kb, N]}};
true ->
case batch_dim(Ba, Bb) of
{error, E} ->
{error, E};
{ok, Out_b} ->
A_arr = viva_tensor@core@ffi:list_to_array(
viva_tensor@tensor:to_list(A)
),
B_arr = viva_tensor@core@ffi:list_to_array(
viva_tensor@tensor:to_list(B)
),
Out = bmm_compute(
A_arr,
B_arr,
Out_b,
M,
Ka,
N,
Ba,
Bb
),
{ok, {tensor, Out, [Out_b, M, N]}}
end
end;
{Sa, Sb} ->
{error,
{invalid_shape,
<<<<<<"batched_matmul: inputs must be 3D [B, M, K] and [B, K, N], got "/utf8,
(shape_to_string(Sa))/binary>>/binary,
" and "/utf8>>/binary,
(shape_to_string(Sb))/binary>>}}
end.
-file("src/viva_tensor/nn/cv.gleam", 831).
?DOC(false).
-spec batch_norm_2d_init(integer()) -> batch_norm2d().
batch_norm_2d_init(Num_features) ->
{batch_norm2d,
viva_tensor@tensor:ones([Num_features]),
viva_tensor@tensor:zeros([Num_features]),
viva_tensor@tensor:zeros([Num_features]),
viva_tensor@tensor:ones([Num_features]),
0.1,
1.0e-5}.
-file("src/viva_tensor/nn/cv.gleam", 993).
?DOC(false).
-spec ema_update(list(float()), list(float()), float()) -> list(float()).
ema_update(Running, Batch, Momentum) ->
One_minus = 1.0 - Momentum,
gleam@list:map(
gleam@list:zip(Running, Batch),
fun(P) ->
{R, B} = P,
(One_minus * R) + (Momentum * B)
end
).
-file("src/viva_tensor/nn/cv.gleam", 1054).
?DOC(false).
-spec safe_sqrt(float()) -> float().
safe_sqrt(X) ->
case gleam@float:square_root(X) of
{ok, V} ->
V;
{error, _} ->
+0.0
end.
-file("src/viva_tensor/nn/cv.gleam", 1061).
?DOC(false).
-spec list_at(list(float()), integer()) -> float().
list_at(Xs, Idx) ->
case {Xs, Idx} of
{[], _} ->
+0.0;
{[X | _], 0} ->
X;
{[_ | Rest], I} ->
list_at(Rest, I - 1)
end.
-file("src/viva_tensor/nn/cv.gleam", 972).
?DOC(false).
-spec channel_variances(
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer(),
list(float())
) -> list(float()).
channel_variances(Arr, B, C, Plane, Means) ->
N = erlang:float(B * Plane),
gleam@list:index_map(
Means,
fun(Mu, Ci) ->
Sum = sum_range(
0,
B - 1,
fun(Bi) ->
Base = ((Bi * C) + Ci) * Plane,
sum_range(
0,
Plane - 1,
fun(Off) ->
D = viva_tensor@core@ffi:array_get(Arr, Base + Off)
- Mu,
D * D
end
)
end
),
case N of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Sum / Gleam@denominator
end
end
).
-file("src/viva_tensor/nn/cv.gleam", 959).
?DOC(false).
-spec channel_means(
viva_tensor@core@ffi:erlang_array(),
integer(),
integer(),
integer()
) -> list(float()).
channel_means(Arr, B, C, Plane) ->
N = erlang:float(B * Plane),
_pipe = range_int(0, C - 1),
gleam@list:map(
_pipe,
fun(Ci) ->
Sum = sum_range(
0,
B - 1,
fun(Bi) ->
Base = ((Bi * C) + Ci) * Plane,
sum_range(
0,
Plane - 1,
fun(Off) ->
viva_tensor@core@ffi:array_get(Arr, Base + Off)
end
)
end
),
case N of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Sum / Gleam@denominator
end
end
).
-file("src/viva_tensor/nn/cv.gleam", 892).
?DOC(false).
-spec batch_norm_2d_apply(
batch_norm2d(),
viva_tensor@tensor:tensor(),
integer(),
integer(),
integer(),
integer(),
boolean()
) -> {ok, {batch_norm2d(), viva_tensor@tensor:tensor()}} |
{error, viva_tensor@core@error:tensor_error()}.
batch_norm_2d_apply(Layer, Input, B, C, H, W, Training) ->
gleam@result:'try'(
viva_tensor@tensor:try_to_list(Input),
fun(Data) ->
gleam@result:'try'(
viva_tensor@tensor:try_to_list(erlang:element(2, Layer)),
fun(Scale_data) ->
gleam@result:'try'(
viva_tensor@tensor:try_to_list(erlang:element(3, Layer)),
fun(Bias_data) ->
gleam@result:'try'(
viva_tensor@tensor:try_to_list(
erlang:element(4, Layer)
),
fun(Running_mean_data) ->
gleam@result:'try'(
viva_tensor@tensor:try_to_list(
erlang:element(5, Layer)
),
fun(Running_var_data) ->
Plane = H * W,
Arr = viva_tensor@core@ffi:list_to_array(
Data
),
Batch_mean = channel_means(
Arr,
B,
C,
Plane
),
Batch_var = channel_variances(
Arr,
B,
C,
Plane,
Batch_mean
),
{Use_mean, Use_var} = case Training of
true ->
{Batch_mean, Batch_var};
false ->
{Running_mean_data,
Running_var_data}
end,
Out = begin
_pipe = range_int(0, B - 1),
gleam@list:flat_map(
_pipe,
fun(Bi) ->
_pipe@1 = range_int(
0,
C - 1
),
gleam@list:flat_map(
_pipe@1,
fun(Ci) ->
Mu = list_at(
Use_mean,
Ci
),
Var = list_at(
Use_var,
Ci
),
S = list_at(
Scale_data,
Ci
),
Bias_v = list_at(
Bias_data,
Ci
),
Denom = safe_sqrt(
Var + erlang:element(
7,
Layer
)
),
Base = ((Bi * C)
+ Ci)
* Plane,
_pipe@2 = range_int(
0,
Plane - 1
),
gleam@list:map(
_pipe@2,
fun(Off) ->
X = viva_tensor@core@ffi:array_get(
Arr,
Base
+ Off
),
((case Denom of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> (X
- Mu)
/ Gleam@denominator
end)
* S)
+ Bias_v
end
)
end
)
end
)
end,
Out_tensor = {tensor,
Out,
[B, C, H, W]},
Updated_layer = case Training of
false ->
Layer;
true ->
New_mean = ema_update(
Running_mean_data,
Batch_mean,
erlang:element(6, Layer)
),
New_var = ema_update(
Running_var_data,
Batch_var,
erlang:element(6, Layer)
),
{batch_norm2d,
erlang:element(2, Layer),
erlang:element(3, Layer),
{tensor, New_mean, [C]},
{tensor, New_var, [C]},
erlang:element(6, Layer),
erlang:element(7, Layer)}
end,
{ok, {Updated_layer, Out_tensor}}
end
)
end
)
end
)
end
)
end
).
-file("src/viva_tensor/nn/cv.gleam", 1047).
?DOC(false).
-spec last_dim(list(integer())) -> {ok, integer()} |
{error, viva_tensor@core@error:tensor_error()}.
last_dim(Shape) ->
case gleam@list:last(Shape) of
{ok, D} ->
{ok, D};
{error, _} ->
{error,
{invalid_shape, <<"expected non-empty shape, got []"/utf8>>}}
end.
-file("src/viva_tensor/nn/cv.gleam", 857).
?DOC(false).
-spec batch_norm_2d_forward(
batch_norm2d(),
viva_tensor@tensor:tensor(),
boolean()
) -> {ok, {batch_norm2d(), viva_tensor@tensor:tensor()}} |
{error, viva_tensor@core@error:tensor_error()}.
batch_norm_2d_forward(Layer, Input, Training) ->
Scale_shape = viva_tensor@tensor:shape(erlang:element(2, Layer)),
gleam@result:'try'(
last_dim(Scale_shape),
fun(Num_features) -> case viva_tensor@tensor:shape(Input) of
[B, C, H, W] ->
case C =:= Num_features of
false ->
{error,
{shape_mismatch,
[B, Num_features, H, W],
[B, C, H, W]}};
true ->
case ((B =< 0) orelse (H =< 0)) orelse (W =< 0) of
true ->
{error,
{invalid_shape,
<<"batch_norm_2d: B, H, W must be positive"/utf8>>}};
false ->
batch_norm_2d_apply(
Layer,
Input,
B,
C,
H,
W,
Training
)
end
end;
Other ->
{error,
{invalid_shape,
<<"batch_norm_2d: input must have shape [B, C, H, W], got "/utf8,
(shape_to_string(Other))/binary>>}}
end end
).