anchor.diff:      create_neural_network/4,
       supported_activations/0,
       initialize/3,
  +    initialize/4,
       add_layer/2,
       add_activation/2,
       set_cost/2,
  
positive.code: 
  -spec initialize(binary(), pos_integer(), pos_integer()) -> ok | {error, binary()}.
  initialize(NetworkId, InpSize, OutSize) ->
      gen_server:call(?NETWORK_GID(NetworkId), {initialize, InpSize, OutSize}).
  
  -spec add_layer(binary(), pos_integer()) -> ok | {error, binary()}.
  add_layer(NetworkId, Size) ->
      gen_server:call(?NETWORK_GID(NetworkId), {add_layer, Size}).
  
  -spec add_activation(binary(), atom()) -> ok | {error, binary()}.
  add_activation(NetworkId, Activation) ->
      gen_server:call(?NETWORK_GID(NetworkId), {add_activation, Activation}).
  
  -spec set_cost(binary(), atom()) -> ok | {error, binary()}.
  set_cost(NetworkId, CostFunc) ->
      gen_server:call(?NETWORK_GID(NetworkId), {set_cost, CostFunc}).
  
  -spec add_data_chunk(binary(), list(), list()) -> ok | {error, binary()}.
  add_data_chunk(NetworkId, Inputs, Labels) ->
      gen_server:call(?NETWORK_GID(NetworkId), {add_data_chunk, [Inputs, Labels]}).
  
  -spec add_data_chunk(binary(), list(), list(), list()) -> ok | {error, binary()}.
  add_data_chunk(NetworkId, Inputs, Labels, Scale) ->
      gen_server:call(?NETWORK_GID(NetworkId), {add_data_chunk, [Inputs, Labels, Scale]}).
  
  -spec set_learning_rate(binary(), float()) -> ok | {error, binary()}.
  set_learning_rate(NetworkId, LearningRate) ->
      gen_server:call(?NETWORK_GID(NetworkId), {set_learning_rate, LearningRate}).
  
  -spec train(binary()) -> ok | {error, binary()}.
  train(NetworkId) -> train(NetworkId, []).
  
  -spec train(binary(), pos_integer() | [pos_integer()]) -> ok | {error, binary()}.
  train(NetworkId, Epochs) when is_integer(Epochs) -> train(NetworkId, [Epochs]);
  train(NetworkId, Args) when is_list(Args) ->
      gen_server:call(?NETWORK_GID(NetworkId), {train, Args}).
  
  -spec train(binary(), pos_integer(), pos_integer()) -> ok | {error, binary()}.
  train(NetworkId, Epochs, BatchSize) -> train(NetworkId, [Epochs, BatchSize]).
  
  -spec get_weights(binary()) -> ok | {error, binary()}.
  get_weights(NetworkId) ->
      gen_server:call(?NETWORK_GID(NetworkId), get_weights).
  
  -spec set_weights(binary(), [float()]) -> ok | {error, binary()}.
  set_weights(NetworkId, Weights) ->
      gen_server:call(?NETWORK_GID(NetworkId), {set_weights, Weights}).
  
  -spec predict(binary(), [number()]) -> list() | {error, binary()} .
  predict(NetworkId, [N|_]=Input) when is_number(N) ->
      %% TODO: Check for errors...
      [Result] = gen_server:call(?NETWORK_GID(NetworkId), {predict, [Input]}),
      Result;
  predict(NetworkId, Set) ->
      gen_server:call(?NETWORK_GID(NetworkId), {predict, Set}).
  
  %% Gen server related functions
  
  
negative.code:     {ok, Conn} = annlink_conn:connect(Address, Port),
      {ok, #state{networkId = NetworkId, conn = Conn}}.
  
  handle_call({Operation, Args}, _From, #state{conn = Conn} = State) when is_atom(Operation) ->
      Result = annlink_conn:call(Conn, atom_to_binary(Operation, utf8), Args),
      {reply, Result, State};
  handle_call(Req, _From, #state{networkId = NetworkId} = State) ->
      ?Error("Invalid call request ~p received by player ~p process ~p", [NetworkId, Req, self()]),
      {reply, {error, <<"invalid request">>}, State}.
  