Current section
Files
Jump to
Current section
Files
src/tastoids@taste.erl
-module(tastoids@taste).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/tastoids/taste.gleam").
-export([from_one/1, from_tuples/1, from_sparse_embedding/2, from_dense_embedding/1, scale/2, add/2, negate/1, multiply/2, length/1, condense/1]).
-export_type([taste/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(
" Tastes, broadly are any indexable sentiment (or lack thereof),\n"
" able to participate in the Tastoid algebra \n"
"\n"
" Apart from the 0-ideal/`Nil` taste (`tasteless`), Tastes must share a\n"
" given index(-space) i.e. strings, Ints, or sets/combinations thereof\n"
"\n"
" More algebraically:\n"
" \n"
" Consider that the set of all Vectors are a field ℝⁿ (aka 𝕍),\n"
" AND i is an enumerable (countable) set of indices (ⅈ) with\n"
" `imbed(a ∈ 𝔸) -> aᵢ` (a bijective imbedding of 'a' subject into 𝕍/ⅈ)\n"
"\n"
" Then, 𝛂aᵢ ∈ 𝔸 are the set of all 'unit' `tastes` (scaled by 𝛂)\n"
).
-opaque taste() :: nil |
{taste, binary(), float()} |
{tastes, gleam@dict:dict(binary(), float())} |
{sparse_embedding, list(float()), list(integer())} |
{dense_embedding, list(float())}.
-file("src/tastoids/taste.gleam", 39).
?DOC(" Return a singular of 'index', a la `Taste(index, 1.0)`\n").
-spec from_one(binary()) -> taste().
from_one(Index) ->
{taste, Index, 1.0}.
-file("src/tastoids/taste.gleam", 44).
?DOC(" Return a `Tastes(index)` consisting of the provided #(index, value: Float) tuples.\n").
-spec from_tuples(list({binary(), float()})) -> taste().
from_tuples(Tastes) ->
_pipe = maps:from_list(Tastes),
{tastes, _pipe}.
-file("src/tastoids/taste.gleam", 48).
-spec from_sparse_embedding(list(float()), list(integer())) -> taste().
from_sparse_embedding(Values, Indices) ->
{sparse_embedding, Values, Indices}.
-file("src/tastoids/taste.gleam", 52).
-spec from_dense_embedding(list(float())) -> taste().
from_dense_embedding(Values) ->
{dense_embedding, Values}.
-file("src/tastoids/taste.gleam", 57).
?DOC(" Internal: Short-hand for use in `scalar_multiply`; curries weight w/ float.multiply\n").
-spec scaling(float()) -> fun((float()) -> float()).
scaling(Weight) ->
fun(Taste) -> gleam@float:multiply(Weight, Taste) end.
-file("src/tastoids/taste.gleam", 62).
?DOC(" Internal: curry weight `scaling` further for `dict.map_values` to ignore the index\n").
-spec scaling_values(float()) -> fun((any(), float()) -> float()).
scaling_values(Weight) ->
Scaled = scaling(Weight),
fun(_, Sentiment) -> _pipe = Sentiment,
Scaled(_pipe) end.
-file("src/tastoids/taste.gleam", 73).
?DOC(
" Scale the given tastes by a portion (_weight_)–relative to its\n"
"'natural' values–a la scalar multiplication.\n"
"\n"
" _Note from Avery: While this could be accomplished via a Tastoid's\n"
" cardinality, I wanted keep the notion of a signal's 'worth' (the\n"
"_scale_ of its impression) distinct from its quantity.\n"
).
-spec scale(taste(), float()) -> taste().
scale(Taste, Weight) ->
Scale_by_weight = fun(Sentiment) ->
gleam@float:multiply(Sentiment, Weight)
end,
case Taste of
{dense_embedding, Ts} ->
_pipe = Ts,
_pipe@1 = gleam@list:map(_pipe, Scale_by_weight),
{dense_embedding, _pipe@1};
{sparse_embedding, Ts@1, Indices} ->
_pipe@2 = Ts@1,
_pipe@3 = gleam@list:map(_pipe@2, Scale_by_weight),
{sparse_embedding, _pipe@3, Indices};
{tastes, Ts@2} ->
_pipe@4 = gleam@dict:map_values(Ts@2, scaling_values(Weight)),
{tastes, _pipe@4};
{taste, I, Sentiment@1} ->
{taste, I, Scale_by_weight(Sentiment@1)};
nil ->
Taste
end.
-file("src/tastoids/taste.gleam", 91).
?DOC(" Internal: Short-hand for `dict.filter` keeping only non-zero tastes\n").
-spec non_zeroes(any(), float()) -> boolean().
non_zeroes(_, T) ->
T /= +0.0.
-file("src/tastoids/taste.gleam", 97).
?DOC(
" Internal: Simplify a taste(s) by recursively excising zeroed tastes, collapsing\n"
" the smallest possible representation (ensuring direct equality when possible)\n"
).
-spec deflate(taste()) -> taste().
deflate(T) ->
case T of
{dense_embedding, _} ->
T;
{sparse_embedding, _, _} ->
T;
{tastes, Ts} ->
Zeroless_ts = begin
_pipe = Ts,
gleam@dict:filter(_pipe, fun non_zeroes/2)
end,
case maps:size(Zeroless_ts) of
0 ->
nil;
N when N > 1 ->
_pipe@1 = Zeroless_ts,
{tastes, _pipe@1};
_ ->
case maps:to_list(Ts) of
[{Index, Sentiment}] ->
_pipe@2 = {taste, Index, Sentiment},
deflate(_pipe@2);
_ ->
T
end
end;
{taste, _, Sentiment@1} when Sentiment@1 =:= +0.0 ->
nil;
{taste, _, _} ->
T;
nil ->
nil
end.
-file("src/tastoids/taste.gleam", 124).
?DOC(
" Combine two tastes, linearly adding their sentiments (per index)\n"
" a la 'scalar addition'\n"
).
-spec add(taste(), taste()) -> taste().
add(T, U) ->
Sum = case {T, U} of
{{dense_embedding, Ts}, {dense_embedding, Us}} ->
_pipe = gleam@list:map2(Ts, Us, fun gleam@float:add/2),
{dense_embedding, _pipe};
{{sparse_embedding, Ts@1, T_indices},
{sparse_embedding, Us@1, U_indices}} ->
Ts_dict = begin
_pipe@1 = gleam@list:zip(T_indices, Ts@1),
maps:from_list(_pipe@1)
end,
Us_dict = begin
_pipe@2 = gleam@list:zip(U_indices, Us@1),
maps:from_list(_pipe@2)
end,
{Indices, Tus} = begin
_pipe@3 = gleam@dict:combine(
Ts_dict,
Us_dict,
fun gleam@float:add/2
),
_pipe@4 = gleam@dict:filter(_pipe@3, fun non_zeroes/2),
_pipe@5 = maps:to_list(_pipe@4),
gleam@list:unzip(_pipe@5)
end,
{sparse_embedding, Tus, Indices};
{{taste, T_i, Sentiment_t}, {taste, Index_u, Sentiment_u}} when T_i =:= Index_u ->
_pipe@6 = {taste, T_i, gleam@float:add(Sentiment_t, Sentiment_u)},
deflate(_pipe@6);
{{taste, T_i@1, Sentiment_t@1}, {taste, Index_u@1, Sentiment_u@1}} ->
_pipe@7 = maps:from_list(
[{T_i@1, Sentiment_t@1}, {Index_u@1, Sentiment_u@1}]
),
_pipe@8 = gleam@dict:filter(_pipe@7, fun non_zeroes/2),
{tastes, _pipe@8};
{{taste, _, _}, {tastes, _}} ->
add(U, T);
{{tastes, Ts@2}, {taste, Index_u@2, Sentiment_u@2}} ->
_pipe@9 = gleam@dict:upsert(
Ts@2,
Index_u@2,
fun(Sentiment_t@2) -> case Sentiment_t@2 of
{some, Sentiment_t@3} ->
gleam@float:add(Sentiment_t@3, Sentiment_u@2);
none ->
Sentiment_u@2
end end
),
_pipe@10 = gleam@dict:filter(_pipe@9, fun non_zeroes/2),
{tastes, _pipe@10};
{{tastes, T@1}, {tastes, U@1}} ->
_pipe@11 = gleam@dict:combine(
T@1,
U@1,
fun(Sentiment_t@4, Sentiment_u@3) ->
gleam@float:add(Sentiment_t@4, Sentiment_u@3)
end
),
_pipe@12 = gleam@dict:filter(_pipe@11, fun non_zeroes/2),
{tastes, _pipe@12};
{nil, _} ->
U;
{_, nil} ->
T;
{_, _} ->
nil
end,
_pipe@13 = Sum,
deflate(_pipe@13).
-file("src/tastoids/taste.gleam", 179).
-spec map_negate(list(float())) -> list(float()).
map_negate(Ts) ->
_pipe = Ts,
gleam@list:map(_pipe, fun gleam@float:negate/1).
-file("src/tastoids/taste.gleam", 184).
?DOC(" Returns the 'negative' of a taste (like <-> dislike)\n").
-spec negate(taste()) -> taste().
negate(T) ->
case T of
{dense_embedding, Ts} ->
_pipe = Ts,
_pipe@1 = map_negate(_pipe),
{dense_embedding, _pipe@1};
{sparse_embedding, Ts@1, Indices} ->
_pipe@2 = Ts@1,
_pipe@3 = map_negate(_pipe@2),
{sparse_embedding, _pipe@3, Indices};
{tastes, Ts@2} ->
_pipe@4 = gleam@dict:map_values(
Ts@2,
fun(_, Sentiment) -> gleam@float:negate(Sentiment) end
),
{tastes, _pipe@4};
{taste, I, Sentiment@1} ->
{taste, I, gleam@float:negate(Sentiment@1)};
nil ->
T
end.
-file("src/tastoids/taste.gleam", 199).
?DOC(
" Return the _and_-ish product of two tastes. Multiplying the\n"
" indices between—possibly sparse—amplifying shared indices and\n"
" nullifying ones they don't (i.e. they were multiplied by zero).\n"
).
-spec multiply(taste(), taste()) -> taste().
multiply(T, U) ->
case {T, U} of
{nil, _} ->
nil;
{_, nil} ->
nil;
{{dense_embedding, Ts}, {dense_embedding, Us}} ->
_pipe = gleam@list:map2(Ts, Us, fun gleam@float:multiply/2),
{dense_embedding, _pipe};
{{sparse_embedding, Ts@1, T_indices},
{sparse_embedding, Us@1, U_indices}} ->
T_set = gleam@set:from_list(T_indices),
U_set = gleam@set:from_list(U_indices),
case gleam@set:is_disjoint(T_set, U_set) of
true ->
nil;
false ->
Excluded_indices = begin
_pipe@1 = gleam@set:symmetric_difference(T_set, U_set),
gleam@set:to_list(_pipe@1)
end,
Ts_dict = begin
_pipe@2 = gleam@list:zip(T_indices, Ts@1),
_pipe@3 = maps:from_list(_pipe@2),
gleam@dict:drop(_pipe@3, Excluded_indices)
end,
Us_dict = begin
_pipe@4 = gleam@list:zip(U_indices, Us@1),
_pipe@5 = maps:from_list(_pipe@4),
gleam@dict:drop(_pipe@5, Excluded_indices)
end,
Product = begin
_pipe@6 = gleam@dict:combine(
Ts_dict,
Us_dict,
fun gleam@float:multiply/2
),
gleam@dict:filter(_pipe@6, fun(_, V) -> V /= +0.0 end)
end,
{Indices, Values} = begin
_pipe@7 = maps:to_list(Product),
gleam@list:unzip(_pipe@7)
end,
{sparse_embedding, Values, Indices}
end;
{{taste, I1, _}, {taste, I2, _}} when I1 =/= I2 ->
nil;
{{taste, I, T1}, {taste, _, T2}} ->
{taste, I, gleam@float:multiply(T1, T2)};
{{taste, _, _}, {tastes, _}} ->
multiply(U, T);
{{tastes, Ts@2}, {taste, I@1, T1@1}} ->
case gleam_stdlib:map_get(Ts@2, I@1) of
{ok, T2@1} ->
case T2@1 of
+0.0 ->
nil;
_ ->
{taste, I@1, gleam@float:multiply(T1@1, T2@1)}
end;
{error, _} ->
nil
end;
{{tastes, Ts1}, {tastes, Ts2}} ->
_pipe@8 = gleam@dict:combine(Ts1, Ts2, fun gleam@float:multiply/2),
_pipe@9 = gleam@dict:filter(_pipe@8, fun non_zeroes/2),
{tastes, _pipe@9};
{_, _} ->
nil
end.
-file("src/tastoids/taste.gleam", 258).
-spec absolute_length(list(float())) -> float().
absolute_length(Tastes) ->
_pipe = Tastes,
_pipe@1 = gleam@list:map(_pipe, fun gleam@float:absolute_value/1),
gleam@list:fold(_pipe@1, +0.0, fun gleam@float:add/2).
-file("src/tastoids/taste.gleam", 265).
?DOC(" Returns the combined sentiments of the entire Taste.\n").
-spec length(taste()) -> float().
length(Taste) ->
case Taste of
{dense_embedding, Ts} ->
absolute_length(Ts);
{sparse_embedding, Ts@1, _} ->
absolute_length(Ts@1);
{tastes, Ts@2} ->
gleam@dict:fold(
Ts@2,
+0.0,
fun(Sum, _, Sentiment) -> _pipe = Sentiment,
_pipe@1 = gleam@float:absolute_value(_pipe),
gleam@float:add(_pipe@1, Sum) end
);
{taste, _, Sentiment@1} ->
Sentiment@1;
nil ->
+0.0
end.
-file("src/tastoids/taste.gleam", 282).
?DOC(
" Returns a combined taste, where n taste indices become one set of its\n"
" indices, with its value representing the sum of their sentiments.\n"
).
-spec condense(taste()) -> taste().
condense(Taste) ->
Sum = length(Taste),
case Taste of
{dense_embedding, _} ->
Taste;
{sparse_embedding, Ts, Indices} ->
{Condensed_indices, Condensed_ts} = begin
_pipe = gleam@list:zip(Indices, Ts),
_pipe@1 = gleam@list:filter(
_pipe,
fun(Key_value) -> erlang:element(2, Key_value) /= +0.0 end
),
gleam@list:unzip(_pipe@1)
end,
{sparse_embedding, Condensed_ts, Condensed_indices};
{tastes, Ts@1} ->
_pipe@2 = Ts@1,
_pipe@3 = maps:keys(_pipe@2),
_pipe@4 = gleam@list:sort(_pipe@3, fun gleam@string:compare/2),
_pipe@5 = gleam@list:intersperse(_pipe@4, <<"⩐"/utf8>>),
_pipe@6 = gleam@list:fold(
_pipe@5,
<<""/utf8>>,
fun gleam@string:append/2
),
{taste, _pipe@6, Sum};
{taste, _, _} ->
Taste;
nil ->
nil
end.