-module(viva_tensor@core@format). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/viva_tensor/core/format.gleam"). -export([default_print_options/0, to_string_with/2, to_string/1, inspect/1, accelerated_to_string_with/2, accelerated_to_string/1]). -export_type([print_options/0, sci_mode/0, sign_mode/0, index_entry/0, formatter/0, classification/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 print_options() :: {print_options, integer(), integer(), integer(), integer(), boolean(), sci_mode(), binary(), binary(), sign_mode()}. -type sci_mode() :: sci_auto | sci_always | sci_never. -type sign_mode() :: sign_negative | sign_always_positive. -type index_entry() :: {idx, integer()} | elision. -type formatter() :: {formatter, integer(), fun((float()) -> binary())}. -type classification() :: finite | nan | pos_inf | neg_inf. -file("src/viva_tensor/core/format.gleam", 65). ?DOC(false). -spec default_print_options() -> print_options(). default_print_options() -> {print_options, 4, 1000, 3, 80, false, sci_auto, <<"nan"/utf8>>, <<"inf"/utf8>>, sign_negative}. -file("src/viva_tensor/core/format.gleam", 251). ?DOC(false). -spec render_shape(list(integer())) -> binary(). render_shape(Shape) -> Parts = gleam@list:map(Shape, fun erlang:integer_to_binary/1), case Parts of [Single] -> <<<<"("/utf8, Single/binary>>/binary, ",)"/utf8>>; _ -> <<<<"("/utf8, (gleam@string:join(Parts, <<", "/utf8>>))/binary>>/binary, ")"/utf8>> end. -file("src/viva_tensor/core/format.gleam", 247). ?DOC(false). -spec shape_suffix(list(integer())) -> binary(). shape_suffix(Shape) -> <<"shape="/utf8, (render_shape(Shape))/binary>>. -file("src/viva_tensor/core/format.gleam", 259). ?DOC(false). -spec suffix_block(list(binary())) -> binary(). suffix_block(Suffixes) -> case Suffixes of [] -> <<""/utf8>>; _ -> <<", "/utf8, (gleam@string:join(Suffixes, <<", "/utf8>>))/binary>> end. -file("src/viva_tensor/core/format.gleam", 238). ?DOC(false). -spec unreadable(binary(), list(integer()), binary()) -> binary(). unreadable(Prefix, Shape, Reason) -> <<<<<<<<<>/binary, Reason/binary>>/binary, ">"/utf8>>/binary, (suffix_block([shape_suffix(Shape)]))/binary>>/binary, ")"/utf8>>. -file("src/viva_tensor/core/format.gleam", 703). ?DOC(false). -spec wrap_loop( list(binary()), binary(), integer(), binary(), binary(), binary() ) -> binary(). wrap_loop(Remaining, Separator, Budget, Indent, Current_line, Acc) -> case Remaining of [] -> Acc; [Next | Rest] -> Candidate = <<<>/binary, Next/binary>>, case string:length(Candidate) > Budget of true -> New_line = <>, New_acc = <<<<<>/binary, "\n"/utf8>>/binary, New_line/binary>>, wrap_loop( Rest, Separator, Budget, Indent, New_line, New_acc ); false -> New_acc@1 = <<<>/binary, Next/binary>>, wrap_loop( Rest, Separator, Budget, Indent, Candidate, New_acc@1 ) end end. -file("src/viva_tensor/core/format.gleam", 691). ?DOC(false). -spec wrap_line(list(binary()), binary(), integer(), binary()) -> binary(). wrap_line(Parts, Separator, Budget, Indent) -> case Parts of [] -> <<""/utf8>>; [First | Rest] -> wrap_loop(Rest, Separator, Budget, Indent, First, First) end. -file("src/viva_tensor/core/format.gleam", 755). ?DOC(false). -spec list_at_float_loop(list(float()), integer()) -> {ok, float()} | {error, nil}. list_at_float_loop(Xs, Idx) -> case {Xs, Idx} of {[], _} -> {error, nil}; {[X | _], 0} -> {ok, X}; {[_ | Rest], N} -> list_at_float_loop(Rest, N - 1) end. -file("src/viva_tensor/core/format.gleam", 751). ?DOC(false). -spec list_at_float(list(float()), integer()) -> {ok, float()} | {error, nil}. list_at_float(Xs, Idx) -> list_at_float_loop(Xs, Idx). -file("src/viva_tensor/core/format.gleam", 524). ?DOC(false). -spec flat_index(list(integer()), list(integer())) -> integer(). flat_index(Path, Strides) -> _pipe = gleam@list:zip(Path, Strides), gleam@list:fold( _pipe, 0, fun(Acc, Pair) -> {Idx, Stride} = Pair, Acc + (Idx * Stride) end ). -file("src/viva_tensor/core/format.gleam", 512). ?DOC(false). -spec read_value(list(float()), list(integer()), list(integer())) -> float(). read_value(Values, Strides, Path) -> Flat = flat_index(Path, Strides), case list_at_float(Values, Flat) of {ok, V} -> V; {error, _} -> +0.0 end. -file("src/viva_tensor/core/format.gleam", 407). ?DOC(false). -spec render_innermost( list(float()), list(integer()), formatter(), print_options(), list(integer()), list(index_entry()), integer() ) -> binary(). render_innermost(Values, Strides, Formatter, Opts, Path, Indices, Indent) -> Prefix = <<"["/utf8>>, Suffix = <<"]"/utf8>>, Budget = (erlang:element(5, Opts) - Indent) - 1, Parts = gleam@list:map(Indices, fun(Entry) -> case Entry of elision -> <<"..."/utf8>>; {idx, I} -> Full_path = lists:append(Path, [I]), Value = read_value(Values, Strides, Full_path), (erlang:element(3, Formatter))(Value) end end), Separator = <<", "/utf8>>, Wrapped = wrap_line( Parts, Separator, Budget, gleam@string:repeat(<<" "/utf8>>, Indent) ), <<<>/binary, Suffix/binary>>. -file("src/viva_tensor/core/format.gleam", 487). ?DOC(false). -spec expand_indices(integer(), boolean(), integer()) -> list(index_entry()). expand_indices(Dim_size, Summarize, Edgeitems) -> case Summarize andalso (Dim_size > (2 * Edgeitems)) of true -> Head = begin _pipe = gleam@list:range(0, Edgeitems - 1), gleam@list:map(_pipe, fun(Field@0) -> {idx, Field@0} end) end, Tail = begin _pipe@1 = gleam@list:range(Dim_size - Edgeitems, Dim_size - 1), gleam@list:map(_pipe@1, fun(Field@0) -> {idx, Field@0} end) end, lists:append([Head, [elision], Tail]); false -> _pipe@2 = gleam@list:range(0, Dim_size - 1), gleam@list:map(_pipe@2, fun(Field@0) -> {idx, Field@0} end) end. -file("src/viva_tensor/core/format.gleam", 743). ?DOC(false). -spec list_at_loop(list(integer()), integer()) -> {ok, integer()} | {error, nil}. list_at_loop(Xs, Idx) -> case {Xs, Idx} of {[], _} -> {error, nil}; {[X | _], 0} -> {ok, X}; {[_ | Rest], N} -> list_at_loop(Rest, N - 1) end. -file("src/viva_tensor/core/format.gleam", 739). ?DOC(false). -spec list_at(list(integer()), integer()) -> {ok, integer()} | {error, nil}. list_at(Xs, Idx) -> list_at_loop(Xs, Idx). -file("src/viva_tensor/core/format.gleam", 438). ?DOC(false). -spec render_outer( list(float()), list(integer()), list(integer()), formatter(), print_options(), boolean(), list(integer()), integer(), list(index_entry()), integer(), integer() ) -> binary(). render_outer( Values, Shape, Strides, Formatter, Opts, Summarize, Path, Axes_left, Indices, Indent, Prefix_len ) -> Separator_newlines = gleam@string:repeat(<<"\n"/utf8>>, Axes_left - 1), Separator = <<<<","/utf8, Separator_newlines/binary>>/binary, (gleam@string:repeat(<<" "/utf8>>, Indent))/binary>>, Parts = gleam@list:map(Indices, fun(Entry) -> case Entry of elision -> <<"..."/utf8>>; {idx, I} -> New_path = lists:append(Path, [I]), recurse( Values, Shape, Strides, Formatter, Opts, Summarize, New_path, Axes_left - 1, Prefix_len ) end end), <<<<"["/utf8, (gleam@string:join(Parts, Separator))/binary>>/binary, "]"/utf8>>. -file("src/viva_tensor/core/format.gleam", 361). ?DOC(false). -spec recurse( list(float()), list(integer()), list(integer()), formatter(), print_options(), boolean(), list(integer()), integer(), integer() ) -> binary(). recurse( Values, Shape, Strides, Formatter, Opts, Summarize, Path, Axes_left, Prefix_len ) -> Depth = erlang:length(Path), Indent = (Prefix_len + Depth) + 1, Dim_index = Depth, Dim_size = case list_at(Shape, Dim_index) of {ok, D} -> D; {error, _} -> 0 end, Indices = expand_indices(Dim_size, Summarize, erlang:element(4, Opts)), case Axes_left of 1 -> render_innermost( Values, Strides, Formatter, Opts, Path, Indices, Indent ); _ -> render_outer( Values, Shape, Strides, Formatter, Opts, Summarize, Path, Axes_left, Indices, Indent, Prefix_len ) end. -file("src/viva_tensor/core/format.gleam", 532). ?DOC(false). -spec contiguous_strides(list(integer())) -> list(integer()). contiguous_strides(Shape) -> Reversed = lists:reverse(Shape), {_, Strides} = gleam@list:fold( Reversed, {1, []}, fun(Acc, Dim) -> {Running, Out} = Acc, {Running * Dim, [Running | Out]} end ), Strides. -file("src/viva_tensor/core/format.gleam", 338). ?DOC(false). -spec render_body( list(float()), list(integer()), formatter(), print_options(), boolean(), integer() ) -> binary(). render_body(Values, Shape, Formatter, Opts, Summarize, Prefix_len) -> Strides = contiguous_strides(Shape), Rank = erlang:length(Shape), recurse( Values, Shape, Strides, Formatter, Opts, Summarize, [], Rank, Prefix_len ). -file("src/viva_tensor/core/format.gleam", 633). ?DOC(false). -spec render_float(float(), boolean(), integer()) -> binary(). render_float(V, Sci_mode, Precision) -> case Sci_mode of true -> viva_tensor_format_ffi:fmt_sci(V, Precision); false -> viva_tensor_format_ffi:fmt_fixed(V, Precision) end. -file("src/viva_tensor/core/format.gleam", 668). ?DOC(false). -spec classify(float()) -> classification(). classify(V) -> case viva_tensor_format_ffi:is_finite(V) of true -> finite; false -> case viva_tensor_format_ffi:is_nan(V) of true -> nan; false -> case V > +0.0 of true -> pos_inf; false -> neg_inf end end end. -file("src/viva_tensor/core/format.gleam", 683). ?DOC(false). -spec max_width(list(binary())) -> integer(). max_width(Strs) -> gleam@list:fold( Strs, 1, fun(Acc, S) -> gleam@int:max(Acc, string:length(S)) end ). -file("src/viva_tensor/core/format.gleam", 640). ?DOC(false). -spec decide_sci_mode(list(float()), print_options()) -> boolean(). decide_sci_mode(Finite_nonzero, Opts) -> case erlang:element(7, Opts) of sci_always -> true; sci_never -> false; sci_auto -> case Finite_nonzero of [] -> false; _ -> Abs_vals = gleam@list:map( Finite_nonzero, fun gleam@float:absolute_value/1 ), Fmin = gleam@list:fold( Abs_vals, 1.0e308, fun gleam@float:min/2 ), Fmax = gleam@list:fold( Abs_vals, +0.0, fun gleam@float:max/2 ), Ratio_trigger = case Fmin > +0.0 of true -> (case Fmin of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> Fmax / Gleam@denominator end) > 1000.0; false -> false end, ((Fmax >= 1.0e8) orelse (Fmin < 1.0e-4)) orelse Ratio_trigger end end. -file("src/viva_tensor/core/format.gleam", 601). ?DOC(false). -spec build_float_formatter(list(float()), list(float()), print_options()) -> formatter(). build_float_formatter(Values, Finite_nonzero, Opts) -> Sci_mode = decide_sci_mode(Finite_nonzero, Opts), Precision = erlang:element(2, Opts), Strs = gleam@list:map(Values, fun(V) -> case classify(V) of finite -> render_float(V, Sci_mode, Precision); nan -> erlang:element(8, Opts); pos_inf -> erlang:element(9, Opts); neg_inf -> <<"-"/utf8, (erlang:element(9, Opts))/binary>> end end), Max_w = max_width(Strs), Opts_local = Opts, {formatter, Max_w, fun(V@1) -> Raw = case classify(V@1) of finite -> render_float(V@1, Sci_mode, Precision); nan -> erlang:element(8, Opts_local); pos_inf -> erlang:element(9, Opts_local); neg_inf -> <<"-"/utf8, (erlang:element(9, Opts_local))/binary>> end, gleam@string:pad_start(Raw, Max_w, <<" "/utf8>>) end}. -file("src/viva_tensor/core/format.gleam", 574). ?DOC(false). -spec build_int_formatter(list(float()), print_options()) -> formatter(). build_int_formatter(Values, Opts) -> Strs = gleam@list:map(Values, fun(V) -> case classify(V) of finite -> N = erlang:trunc(V), <<(erlang:integer_to_binary(N))/binary, "."/utf8>>; nan -> erlang:element(8, Opts); pos_inf -> erlang:element(9, Opts); neg_inf -> <<"-"/utf8, (erlang:element(9, Opts))/binary>> end end), Max_w = max_width(Strs), Opts_local = Opts, {formatter, Max_w, fun(V@1) -> Raw = case classify(V@1) of finite -> <<(erlang:integer_to_binary(erlang:trunc(V@1)))/binary, "."/utf8>>; nan -> erlang:element(8, Opts_local); pos_inf -> erlang:element(9, Opts_local); neg_inf -> <<"-"/utf8, (erlang:element(9, Opts_local))/binary>> end, gleam@string:pad_start(Raw, Max_w, <<" "/utf8>>) end}. -file("src/viva_tensor/core/format.gleam", 570). ?DOC(false). -spec float_truncate(float()) -> float(). float_truncate(V) -> erlang:float(erlang:trunc(V)). -file("src/viva_tensor/core/format.gleam", 561). ?DOC(false). -spec looks_integral(list(float())) -> boolean(). looks_integral(Values) -> gleam@list:all(Values, fun(V) -> case viva_tensor_format_ffi:is_finite(V) of false -> true; true -> V =:= float_truncate(V) end end). -file("src/viva_tensor/core/format.gleam", 550). ?DOC(false). -spec build_formatter(list(float()), print_options()) -> formatter(). build_formatter(Values, Opts) -> Finite_nonzero = begin _pipe = Values, gleam@list:filter( _pipe, fun(V) -> viva_tensor_format_ffi:is_finite(V) andalso (V /= +0.0) end ) end, case looks_integral(Values) of true -> build_int_formatter(Values, Opts); false -> build_float_formatter(Values, Finite_nonzero, Opts) end. -file("src/viva_tensor/core/format.gleam", 322). ?DOC(false). -spec materialize(viva_tensor@tensor:tensor()) -> {ok, list(float())} | {error, binary()}. materialize(T) -> case T of {tensor, Data, _} -> {ok, Data}; {strided_tensor, _, _, _, _} -> {ok, viva_tensor@tensor:to_list(T)}; {native_tensor, Ref, _} -> viva_tensor@core@ffi:nt_to_list(Ref) end. -file("src/viva_tensor/core/format.gleam", 735). ?DOC(false). -spec total_size(list(integer())) -> integer(). total_size(Shape) -> gleam@list:fold(Shape, 1, fun(Acc, D) -> Acc * D end). -file("src/viva_tensor/core/format.gleam", 270). ?DOC(false). -spec render_tensor( viva_tensor@tensor:tensor(), print_options(), binary(), list(binary()) ) -> binary(). render_tensor(T, Opts, Prefix, Suffixes) -> Shape = viva_tensor@tensor:shape(T), case Shape of [] -> <<<<<"/utf8>>/binary, (suffix_block(Suffixes))/binary>>/binary, ")"/utf8>>; _ -> Total = total_size(Shape), case Total =:= 0 of true -> S = [shape_suffix(Shape) | Suffixes], <<<<<>/binary, (suffix_block(S))/binary>>/binary, ")"/utf8>>; false -> case materialize(T) of {ok, Values} -> Summarize = Total > erlang:element(3, Opts), Summary_suffixes = case Summarize of true -> [shape_suffix(Shape) | Suffixes]; false -> Suffixes end, Formatter = build_formatter(Values, Opts), Body = render_body( Values, Shape, Formatter, Opts, Summarize, string:length(Prefix) ), <<<<<>/binary, (suffix_block(Summary_suffixes))/binary>>/binary, ")"/utf8>>; {error, Reason} -> unreadable(Prefix, Shape, Reason) end end end. -file("src/viva_tensor/core/format.gleam", 158). ?DOC(false). -spec tensor_suffixes(viva_tensor@tensor:tensor()) -> list(binary()). tensor_suffixes(T) -> case T of {tensor, _, _} -> []; {strided_tensor, _, _, _, _} -> [<<"storage=strided"/utf8>>]; {native_tensor, _, _} -> [<<"storage=native"/utf8>>] end. -file("src/viva_tensor/core/format.gleam", 89). ?DOC(false). -spec to_string_with(viva_tensor@tensor:tensor(), print_options()) -> binary(). to_string_with(T, Opts) -> Prefix = <<"tensor("/utf8>>, Suffixes = tensor_suffixes(T), render_tensor(T, Opts, Prefix, Suffixes). -file("src/viva_tensor/core/format.gleam", 84). ?DOC(false). -spec to_string(viva_tensor@tensor:tensor()) -> binary(). to_string(T) -> to_string_with(T, default_print_options()). -file("src/viva_tensor/core/format.gleam", 97). ?DOC(false). -spec inspect(viva_tensor@tensor:tensor()) -> binary(). inspect(T) -> to_string(T). -file("src/viva_tensor/core/format.gleam", 166). ?DOC(false). -spec accel_backend_suffix(viva_tensor@native@cuda:acceleration_backend()) -> binary(). accel_backend_suffix(Backend) -> case Backend of rtx4090_fp16 -> <<""/utf8>>; rtx4090_fp32 -> <<""/utf8>>; mkl_native -> <<"backend=mkl"/utf8>>; cpu_fallback -> <<"backend=cpu"/utf8>> end. -file("src/viva_tensor/core/format.gleam", 227). ?DOC(false). -spec header_only(binary(), list(integer()), list(binary())) -> binary(). header_only(Prefix, Shape, Raw_suffixes) -> Suffixes = begin _pipe = [shape_suffix(Shape) | Raw_suffixes], gleam@list:filter(_pipe, fun(S) -> S /= <<""/utf8>> end) end, <<<<<"/utf8>>/binary, (suffix_block(Suffixes))/binary>>/binary, ")"/utf8>>. -file("src/viva_tensor/core/format.gleam", 201). ?DOC(false). -spec render_cuda_fp32( viva_tensor@core@ffi:cuda_tensor_ref(), list(integer()), viva_tensor@native@cuda:acceleration_backend(), print_options() ) -> binary(). render_cuda_fp32(Ref, Shape, Backend, Opts) -> case viva_tensor_zig:ct_to_list(Ref) of {ok, Data} -> Inner = {tensor, Data, Shape}, Suffixes = begin _pipe = [<<"device='cuda'"/utf8>>, <<"dtype=fp32"/utf8>>, accel_backend_suffix(Backend)], gleam@list:filter(_pipe, fun(S) -> S /= <<""/utf8>> end) end, render_tensor(Inner, Opts, <<"accelerated_tensor("/utf8>>, Suffixes); {error, Reason} -> unreadable(<<"accelerated_tensor("/utf8>>, Shape, Reason) end. -file("src/viva_tensor/core/format.gleam", 179). ?DOC(false). -spec render_cuda_fp16( viva_tensor@core@ffi:cuda_tensor16_ref(), list(integer()), viva_tensor@native@cuda:acceleration_backend(), print_options() ) -> binary(). render_cuda_fp16(Ref, Shape, Backend, Opts) -> case viva_tensor_zig:ct16_to_list(Ref) of {ok, Data} -> Inner = {tensor, Data, Shape}, Suffixes = begin _pipe = [<<"device='cuda'"/utf8>>, <<"dtype=fp16"/utf8>>, accel_backend_suffix(Backend)], gleam@list:filter(_pipe, fun(S) -> S /= <<""/utf8>> end) end, render_tensor(Inner, Opts, <<"accelerated_tensor("/utf8>>, Suffixes); {error, Reason} -> unreadable(<<"accelerated_tensor("/utf8>>, Shape, Reason) end. -file("src/viva_tensor/core/format.gleam", 114). ?DOC(false). -spec accelerated_to_string_with( viva_tensor@native@cuda:accelerated_tensor(), print_options() ) -> binary(). accelerated_to_string_with(T, Opts) -> case T of {cpu, Inner, Backend} -> Suffixes = begin _pipe = lists:append( tensor_suffixes(Inner), [accel_backend_suffix(Backend)] ), gleam@list:filter(_pipe, fun(S) -> S /= <<""/utf8>> end) end, render_tensor(Inner, Opts, <<"tensor("/utf8>>, Suffixes); {cuda_fp16, Ref, Shape, Backend@1} -> Total = total_size(Shape), case Total =< erlang:element(3, Opts) of true -> render_cuda_fp16(Ref, Shape, Backend@1, Opts); false -> header_only( <<"accelerated_tensor("/utf8>>, Shape, [<<"device='cuda'"/utf8>>, <<"dtype=fp16"/utf8>>, accel_backend_suffix(Backend@1)] ) end; {cuda_fp32, Ref@1, Shape@1, Backend@2} -> Total@1 = total_size(Shape@1), case Total@1 =< erlang:element(3, Opts) of true -> render_cuda_fp32(Ref@1, Shape@1, Backend@2, Opts); false -> header_only( <<"accelerated_tensor("/utf8>>, Shape@1, [<<"device='cuda'"/utf8>>, <<"dtype=fp32"/utf8>>, accel_backend_suffix(Backend@2)] ) end end. -file("src/viva_tensor/core/format.gleam", 103). ?DOC(false). -spec accelerated_to_string(viva_tensor@native@cuda:accelerated_tensor()) -> binary(). accelerated_to_string(T) -> accelerated_to_string_with(T, default_print_options()).