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@quant@hadamard.erl
-module(viva_tensor@quant@hadamard).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_tensor/quant/hadamard.gleam").
-export([hadamard/1, normalize_hadamard/1, randomized_hadamard/2, pad_to/2, next_power_of_two/1, try_preprocess/2, inverse_randomized_hadamard/2, inverse/1, try_walsh_hadamard/1, try_normalized_walsh_hadamard/1]).
-export_type([hadamard_preprocess/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 hadamard_preprocess() :: {hadamard_preprocess,
viva_tensor@tensor:tensor(),
integer(),
integer(),
integer()}.
-file("src/viva_tensor/quant/hadamard.gleam", 175).
?DOC(false).
-spec shape_to_string(list(integer())) -> binary().
shape_to_string(Shape) ->
Body = begin
_pipe = Shape,
_pipe@1 = gleam@list:map(_pipe, fun erlang:integer_to_binary/1),
gleam@list:fold(_pipe@1, <<""/utf8>>, fun(Acc, Dim) -> case Acc of
<<""/utf8>> ->
Dim;
_ ->
<<<<Acc/binary, ", "/utf8>>/binary, Dim/binary>>
end end)
end,
<<<<"["/utf8, Body/binary>>/binary, "]"/utf8>>.
-file("src/viva_tensor/quant/hadamard.gleam", 120).
?DOC(false).
-spec hadamard(list(float())) -> list(float()).
hadamard(Values) ->
case Values of
[] ->
[];
[_] ->
Values;
_ ->
Half = erlang:length(Values) div 2,
Left = gleam@list:take(Values, Half),
Right = gleam@list:drop(Values, Half),
Sum = gleam@list:map2(Left, Right, fun(A, B) -> A + B end),
Diff = gleam@list:map2(Left, Right, fun(A@1, B@1) -> A@1 - B@1 end),
lists:append(hadamard(Sum), hadamard(Diff))
end.
-file("src/viva_tensor/quant/hadamard.gleam", 135).
?DOC(false).
-spec normalize_hadamard(list(float())) -> list(float()).
normalize_hadamard(Values) ->
case Values of
[] ->
[];
_ ->
Scale = math:sqrt(erlang:float(erlang:length(Values))),
gleam@list:map(Values, fun(Value) -> case Scale of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Value / Gleam@denominator
end end)
end.
-file("src/viva_tensor/quant/hadamard.gleam", 168).
?DOC(false).
-spec random_sign(integer(), integer()) -> integer().
random_sign(Seed, Index) ->
case erlang:phash2({Seed, Index}, 2) of
0 ->
-1;
_ ->
1
end.
-file("src/viva_tensor/quant/hadamard.gleam", 99).
?DOC(false).
-spec randomized_hadamard(list(float()), integer()) -> list(float()).
randomized_hadamard(Values, Seed) ->
Signed = begin
_pipe = Values,
gleam@list:index_map(
_pipe,
fun(Value, Index) ->
Value * erlang:float(random_sign(Seed, Index))
end
)
end,
normalize_hadamard(hadamard(Signed)).
-file("src/viva_tensor/quant/hadamard.gleam", 145).
?DOC(false).
-spec pad_to(list(float()), integer()) -> list(float()).
pad_to(Values, Target_size) ->
Missing = Target_size - erlang:length(Values),
case Missing =< 0 of
true ->
Values;
false ->
lists:append(Values, gleam@list:repeat(+0.0, Missing))
end.
-file("src/viva_tensor/quant/hadamard.gleam", 157).
?DOC(false).
-spec next_power_of_two_loop(integer(), integer()) -> integer().
next_power_of_two_loop(Value, Current) ->
case Current >= Value of
true ->
Current;
false ->
next_power_of_two_loop(Value, Current * 2)
end.
-file("src/viva_tensor/quant/hadamard.gleam", 153).
?DOC(false).
-spec next_power_of_two(integer()) -> integer().
next_power_of_two(Value) ->
next_power_of_two_loop(Value, 1).
-file("src/viva_tensor/quant/hadamard.gleam", 27).
?DOC(false).
-spec try_preprocess(viva_tensor@tensor:tensor(), integer()) -> {ok,
hadamard_preprocess()} |
{error, viva_tensor@core@error:tensor_error()}.
try_preprocess(Input, Seed) ->
case erlang:element(3, Input) of
[_] ->
gleam@result:'try'(
viva_tensor@tensor:try_to_list(Input),
fun(Values) -> case Values of
[] ->
{error,
{invalid_shape,
<<"Hadamard preprocessing requires a non-empty vector"/utf8>>}};
_ ->
Original_dim = erlang:length(Values),
Padded_dim = next_power_of_two(Original_dim),
Rotated = begin
_pipe = Values,
_pipe@1 = pad_to(_pipe, Padded_dim),
randomized_hadamard(_pipe@1, Seed)
end,
{ok,
{hadamard_preprocess,
viva_tensor@tensor:from_list(Rotated),
Original_dim,
Padded_dim,
Seed}}
end end
);
Shape ->
{error,
{invalid_shape,
<<"Hadamard preprocessing expects a vector, got "/utf8,
(shape_to_string(Shape))/binary>>}}
end.
-file("src/viva_tensor/quant/hadamard.gleam", 109).
?DOC(false).
-spec inverse_randomized_hadamard(list(float()), integer()) -> list(float()).
inverse_randomized_hadamard(Values, Seed) ->
_pipe = hadamard(Values),
_pipe@1 = normalize_hadamard(_pipe),
gleam@list:index_map(
_pipe@1,
fun(Value, Index) -> Value * erlang:float(random_sign(Seed, Index)) end
).
-file("src/viva_tensor/quant/hadamard.gleam", 64).
?DOC(false).
-spec inverse(hadamard_preprocess()) -> {ok, viva_tensor@tensor:tensor()} |
{error, viva_tensor@core@error:tensor_error()}.
inverse(Preprocessed) ->
gleam@result:'try'(
viva_tensor@tensor:try_to_list(erlang:element(2, Preprocessed)),
fun(Values) -> _pipe = Values,
_pipe@1 = inverse_randomized_hadamard(
_pipe,
erlang:element(5, Preprocessed)
),
_pipe@2 = gleam@list:take(_pipe@1, erlang:element(3, Preprocessed)),
_pipe@3 = viva_tensor@tensor:from_list(_pipe@2),
{ok, _pipe@3} end
).
-file("src/viva_tensor/quant/hadamard.gleam", 164).
?DOC(false).
-spec is_power_of_two(integer()) -> boolean().
is_power_of_two(Value) ->
(Value > 0) andalso (next_power_of_two(Value) =:= Value).
-file("src/viva_tensor/quant/hadamard.gleam", 75).
?DOC(false).
-spec try_walsh_hadamard(list(float())) -> {ok, list(float())} |
{error, viva_tensor@core@error:tensor_error()}.
try_walsh_hadamard(Values) ->
case Values of
[] ->
{error,
{invalid_shape,
<<"Walsh-Hadamard transform requires a non-empty vector"/utf8>>}};
_ ->
case is_power_of_two(erlang:length(Values)) of
true ->
{ok, hadamard(Values)};
false ->
{error,
{invalid_shape,
<<"Walsh-Hadamard transform requires power-of-two length"/utf8>>}}
end
end.
-file("src/viva_tensor/quant/hadamard.gleam", 92).
?DOC(false).
-spec try_normalized_walsh_hadamard(list(float())) -> {ok, list(float())} |
{error, viva_tensor@core@error:tensor_error()}.
try_normalized_walsh_hadamard(Values) ->
gleam@result:'try'(
try_walsh_hadamard(Values),
fun(Transformed) -> {ok, normalize_hadamard(Transformed)} end
).