-module(gleam_synapses@model@draw). -compile(no_auto_import). -export([network_svg/1]). -spec circle_vertical_distance() -> float(). circle_vertical_distance() -> 400.0 * 0.02. -spec circle_horizontal_distance() -> float(). circle_horizontal_distance() -> 400.0 * 0.15. -spec circle_radius() -> float(). circle_radius() -> 400.0 * 0.03. -spec circle_stroke_width() -> float(). circle_stroke_width() -> 400.0 / 150.0. -spec line_stroke_width() -> float(). line_stroke_width() -> 400.0 / 300.0. -spec activation_name_to_stroke(binary()) -> binary(). activation_name_to_stroke(Activ_name) -> case Activ_name of <<"sigmoid"/utf8>> -> <<"blue"/utf8>>; <<"identity"/utf8>> -> <<"orange"/utf8>>; <<"tanh"/utf8>> -> <<"yellow"/utf8>>; <<"leakyReLU"/utf8>> -> <<"pink"/utf8>> end. -spec layer_width(integer()) -> float(). layer_width(Num_of_circles) -> Num_of_circles_float = gleam@int:to_float(Num_of_circles), circle_vertical_distance() + (Num_of_circles_float * ((2.0 * circle_radius()) + circle_vertical_distance( ))). -spec circle_cx(integer()) -> float(). circle_cx(Chain_order) -> Chain_order_float = gleam@int:to_float(Chain_order), circle_horizontal_distance() + (Chain_order_float * circle_horizontal_distance( )). -spec circle_cy(integer(), integer(), integer()) -> float(). circle_cy(Max_chain_circles, Num_of_chain_circles, Circle_order) -> Current_layer_width = layer_width(Num_of_chain_circles), Max_layer_width = layer_width(Max_chain_circles), Layer_y = 0.5 * (Max_layer_width - Current_layer_width), Circle_order_float = gleam@int:to_float(Circle_order), Layer_y + ((Circle_order_float + 1.0) * ((2.0 * circle_radius()) + circle_vertical_distance( ))). -spec circle_svg(float(), float(), binary()) -> binary(). circle_svg(X, Y, Stroke_val) -> gleam@string:concat( [<<">, gleam@float:to_string(X), <<"\" cy=\""/utf8>>, gleam@float:to_string(Y), <<"\" r=\""/utf8>>, gleam@float:to_string(circle_radius()), <<"\" stroke=\""/utf8>>, Stroke_val, <<"\" stroke-width=\""/utf8>>, gleam@float:to_string(circle_stroke_width()), <<"\" fill=\""/utf8>>, <<"white"/utf8>>, <<"\">"/utf8>>] ). -spec input_circles_svgs(integer(), integer()) -> gleam_zlists@interop:z_list(binary()). input_circles_svgs(Max_chain_circles, Input_circles) -> gleam_zlists:map( gleam_zlists:take(gleam_zlists:indices(), Input_circles), fun(I) -> Stroke_val = case I =:= 0 of true -> <<"black"/utf8>>; false -> <<"brown"/utf8>> end, circle_svg( circle_cx(0), circle_cy(Max_chain_circles, Input_circles, I), Stroke_val ) end ). -spec output_circles_svgs( integer(), integer(), gleam_zlists@interop:z_list(binary()) ) -> gleam_zlists@interop:z_list(binary()). output_circles_svgs(Max_chain_circles, Output_chain_order, Output_activations) -> Num_of_activations = gleam_zlists:count(Output_activations), gleam_zlists:map( gleam_zlists:with_index(Output_activations), fun(T) -> {Activ, I} = T, circle_svg( circle_cx(Output_chain_order), circle_cy(Max_chain_circles, Num_of_activations, I), activation_name_to_stroke(Activ) ) end ). -spec hidden_circles_svgs( integer(), integer(), gleam_zlists@interop:z_list(binary()) ) -> gleam_zlists@interop:z_list(binary()). hidden_circles_svgs(Max_chain_circles, Hidden_chain_order, Hidden_activations) -> Num_of_activations = gleam_zlists:count(Hidden_activations), gleam_zlists:map( gleam_zlists:with_index( gleam_zlists:cons( gleam_zlists:map(Hidden_activations, fun(A) -> {some, A} end), none ) ), fun(T) -> {Maybe_activ, I} = T, Stroke_val = case Maybe_activ of {some, Activ} -> activation_name_to_stroke(Activ); none -> <<"black"/utf8>> end, circle_svg( circle_cx(Hidden_chain_order), circle_cy(Max_chain_circles, Num_of_activations + 1, I), Stroke_val ) end ). -spec layer_circles_svgs( integer(), integer(), integer(), gleam_zlists@interop:z_list(gleam_synapses@model@net_elems@neuron:neuron()) ) -> gleam_zlists@interop:z_list(binary()). layer_circles_svgs(Max_chain_circles, Layer_order, Num_of_layers, Layer_val) -> Is_last_layer = Layer_order =:= (Num_of_layers - 1), {ok, Prev_layer_size} = gleam@result:map( gleam_zlists:head(Layer_val), fun(Neuron_val) -> gleam_zlists:count(erlang:element(3, Neuron_val)) end ), Activations = gleam_zlists:map( Layer_val, fun(Neuron_val@1) -> erlang:element(2, erlang:element(2, Neuron_val@1)) end ), Input_circles = case Layer_order =:= 0 of true -> input_circles_svgs(Max_chain_circles, Prev_layer_size); false -> gleam_zlists:new() end, Hidden_circles = case Is_last_layer of true -> gleam_zlists:new(); false -> hidden_circles_svgs(Max_chain_circles, Layer_order + 1, Activations) end, Output_circles = case Is_last_layer of true -> output_circles_svgs(Max_chain_circles, Layer_order + 1, Activations); false -> gleam_zlists:new() end, gleam_zlists:append( gleam_zlists:append(Input_circles, Hidden_circles), Output_circles ). -spec line_svg( integer(), integer(), integer(), integer(), integer(), integer(), float(), float() ) -> binary(). line_svg( Max_chain_circles, Base_chain_order, Num_of_circles_in_base_chain, Num_of_circles_in_target_chain, Base_circle_order, Target_circle_order, Weight, Max_abs_weight ) -> Alpha = case Max_abs_weight of 0.0 -> 0.0; Gleam@denominator -> gleam@float:absolute_value(Weight) / Gleam@denominator end, X1_val = circle_cx(Base_chain_order), Y1_val = circle_cy( Max_chain_circles, Num_of_circles_in_base_chain, Base_circle_order ), X2_val = circle_cx(Base_chain_order + 1), Y2_val = circle_cy( Max_chain_circles, Num_of_circles_in_target_chain, Target_circle_order ), Stroke_val = case Weight > 0.0 of true -> <<"lawngreen"/utf8>>; false -> <<"palevioletred"/utf8>> end, gleam@string:concat( [<<">, gleam@float:to_string(Alpha), <<"\" x1=\""/utf8>>, gleam@float:to_string(X1_val), <<"\" y1=\""/utf8>>, gleam@float:to_string(Y1_val), <<"\" x2=\""/utf8>>, gleam@float:to_string(X2_val), <<"\" y2=\""/utf8>>, gleam@float:to_string(Y2_val), <<"\" stroke=\""/utf8>>, Stroke_val, <<"\" stroke-width=\""/utf8>>, gleam@float:to_string(line_stroke_width()), <<"\">"/utf8>>] ). -spec neuron_lines_svgs( integer(), integer(), integer(), integer(), integer(), float(), gleam_zlists@interop:z_list(float()) ) -> gleam_zlists@interop:z_list(binary()). neuron_lines_svgs( Max_chain_circles, Layer_size, Layer_order, Num_of_layers, Neuron_order_in_layer, Max_abs_weight, Weights ) -> Is_output_layer = Layer_order =:= (Num_of_layers - 1), Num_of_circles_in_base_chain = gleam_zlists:count(Weights), Num_of_circles_in_target_chain = case Is_output_layer of true -> Layer_size; false -> Layer_size + 1 end, Target_circle_order = case Is_output_layer of true -> Neuron_order_in_layer; false -> Neuron_order_in_layer + 1 end, gleam_zlists:map(gleam_zlists:with_index(Weights), fun(T) -> {W, I} = T, line_svg( Max_chain_circles, Layer_order, Num_of_circles_in_base_chain, Num_of_circles_in_target_chain, I, Target_circle_order, W, Max_abs_weight ) end). -spec layer_lines_svgs( integer(), integer(), integer(), float(), gleam_zlists@interop:z_list(gleam_synapses@model@net_elems@neuron:neuron()) ) -> gleam_zlists@interop:z_list(binary()). layer_lines_svgs( Max_chain_circles, Layer_order, Num_of_layers, Max_abs_weight, Layer_val ) -> Num_of_neurons = gleam_zlists:count(Layer_val), gleam_zlists:flat_map( gleam_zlists:with_index(Layer_val), fun(T) -> {Neuron, I} = T, neuron_lines_svgs( Max_chain_circles, Num_of_neurons, Layer_order, Num_of_layers, I, Max_abs_weight, erlang:element(3, Neuron) ) end ). -spec network_svg( gleam_zlists@interop:z_list(gleam_zlists@interop:z_list(gleam_synapses@model@net_elems@neuron:neuron())) ) -> binary(). network_svg(Network_val) -> Num_of_layers = gleam_zlists:count(Network_val), {ok, Max_chain_circles_float} = gleam_zlists:max( gleam_zlists:map( gleam_zlists:map( gleam_zlists:with_index(Network_val), fun(T) -> {Layer_val, I} = T, case I =:= (Num_of_layers - 1) of true -> gleam_zlists:count(Layer_val) + 1; false -> gleam_zlists:count(Layer_val) end end ), fun gleam@int:to_float/1 ) ), Max_chain_circles = gleam@float:round(Max_chain_circles_float), {ok, Max_abs_weight} = gleam_zlists:max( gleam_zlists:flat_map( Network_val, fun(Layer_val@1) -> gleam_zlists:flat_map( Layer_val@1, fun(Neuron_val) -> gleam_zlists:map( erlang:element(3, Neuron_val), fun(W) -> gleam@float:absolute_value(W) end ) end ) end ) ), Circles_svgs = gleam_zlists:flat_map( gleam_zlists:with_index(Network_val), fun(T@1) -> {Layer_val@2, I@1} = T@1, layer_circles_svgs( Max_chain_circles, I@1, Num_of_layers, Layer_val@2 ) end ), Lines_svgs = gleam_zlists:flat_map( gleam_zlists:with_index(Network_val), fun(T@2) -> {Layer_val@3, I@2} = T@2, layer_lines_svgs( Max_chain_circles, I@2, Num_of_layers, Max_abs_weight, Layer_val@3 ) end ), W@1 = circle_cx(Num_of_layers + 1), H = circle_cy(Max_chain_circles, Max_chain_circles, Max_chain_circles), Net_svgs = gleam_zlists:append(Lines_svgs, Circles_svgs), gleam@string:concat( [<<">, gleam@float:to_string(W@1), <<"\" height=\""/utf8>>, gleam@float:to_string(H), <<"\">"/utf8>>, gleam_zlists:reduce( Net_svgs, <<""/utf8>>, fun(X, Acc) -> gleam@string:append(Acc, X) end ), <<""/utf8>>] ).