Skip to content

Commit

Permalink
- EXNN.NodeServer protocol now uses calls instead of casts
Browse files Browse the repository at this point in the history
- EXNN.NodeServer.forward/3 to hide internal genserver api
  • Loading branch information
zampino committed Mar 4, 2015
1 parent 3def9f0 commit 05e06d1
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 39 deletions.
4 changes: 2 additions & 2 deletions lib/exnn/actuator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ defmodule EXNN.Actuator do
defstruct unquote(Keyword.merge state_keyword, [id: nil, ins: []])

defimpl EXNN.Connection, for: CurrentActuatorBase do
def signal(actuator, message, _metadata) do
def signal(actuator, message, metadata) do
# notify_dispatcher_with(message, metadata)
CurrentActuatorBase.act(actuator, message)
CurrentActuatorBase.act(actuator, message, metadata)
end
end

Expand Down
11 changes: 7 additions & 4 deletions lib/exnn/neuron.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ defmodule EXNN.Neuron do
@doc "broadcast input to registered outs and resets its trigger"
def fire(%__MODULE__{trigger: []} = neuron) do
{neuron, value} = impulse(neuron)
neuron.outs |>

:ok = neuron.outs |>
Enum.each(&forward(&1, neuron, value))

%__MODULE__{neuron | trigger: Dict.keys(neuron.ins)}
end

def forward(out_id, neuron, value) do
GenServer.cast(out_id, {:signal, [{neuron.id, value}], neuron.metadata})
EXNN.NodeServer.forward(out_id, [{neuron.id, value}], neuron.metadata)
end

def fire(neuron) do
Expand All @@ -42,19 +44,20 @@ defmodule EXNN.Neuron do
{neuron, _impulse}
end

def signal(neuron, message, metadata\\nil) do
def signal(neuron, message, metadata\\[]) do
acc = neuron.acc ++ message
metadata = Dict.merge neuron.metadata, metadata
trigger = Keyword.keys(message)
|> List.foldl(neuron.trigger, fn(origin, trigger)->
List.delete(trigger, origin)
end)

%__MODULE__{neuron | trigger: trigger, acc: acc, metadata: metadata}
|> fire
end

defimpl EXNN.Connection, for: __MODULE__ do
def signal(neuron, message, metadata) do
IO.puts "---- signaling neuron: #{neuron.id} -- #{inspect(neuron)} ------"
EXNN.Neuron.signal(neuron, message, metadata)
end
end
Expand Down
15 changes: 11 additions & 4 deletions lib/exnn/node_server.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
defmodule EXNN.NodeServer do
# public api

# TODO: maybe import some API module in the quote below
def forward(id, message, metadata) do
GenServer.call(id, {:forward, message, metadata})
end

defmacro __using__(options) do
quote do
use GenServer
Expand All @@ -13,14 +20,14 @@ defmodule EXNN.NodeServer do

def initialize(genome), do: genome

# server callbacks
@doc "NodeServer basic protocol action is to react to
a :signal event.
a :forward event.
message is a keyword [origin: value]
"
def handle_cast({:signal, message, metadata}, connectable) do
connectable = EXNN.Connection.signal(connectable, message, metadata)
{:noreply, connectable}
def handle_call({:forward, message, metadata}, _from, connectable) do
{:reply, :ok, EXNN.Connection.signal(connectable, message, metadata)}
end

defoverridable [initialize: 1]
Expand Down
2 changes: 1 addition & 1 deletion lib/exnn/sensor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ defmodule EXNN.Sensor do
def forward(sensor, value) do
spread_value = format_impulse(sensor, value)
cast_out = fn(out_id) ->
GenServer.cast out_id, {:signal, spread_value, spread_value}
EXNN.NodeServer.forward(out_id, spread_value, [{sensor.id, value}])
end
sensor.outs |> Enum.each(cast_out)
sensor
Expand Down
10 changes: 5 additions & 5 deletions lib/exnn/trainer.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule EXNN.Trainer do
use GenServer

@train_interval 200
@train_interval 100

def start_link do
GenServer.start_link(__MODULE__,
Expand All @@ -15,12 +15,12 @@ defmodule EXNN.Trainer do
end

def train do
# IO.puts "^^^^^^^^^ TRAINING ^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
EXNN.Config.sensors |> Enum.each(&train/1)
# :timer.sleep @train_interval
end

def train(sensor_id) do
GenServer.cast sensor_id, {:signal, :sync, self}
EXNN.NodeServer.forward(sensor_id, :sync, self)
end

# public api
Expand All @@ -30,7 +30,7 @@ defmodule EXNN.Trainer do

# server callbacks
def handle_call({:iterate, num}, _from, state) do
report = Stream.take(state.stream, num) |> Stream.run()
{:reply, report, state}
num = Enum.take(state.stream, num) |> length()
{:reply, :ok, state}
end
end
6 changes: 3 additions & 3 deletions test/exnn/actuator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule EXNN.ActuatorTest do
defmodule TestAct do
use EXNN.Actuator, with_state: [store: []]

def act(actuator, message) do
def act(actuator, message, _) do
%__MODULE__{actuator | store: actuator.store ++ message}
end
end
Expand All @@ -16,7 +16,7 @@ defmodule EXNN.ActuatorTest do
Dict.merge genome, %{store: [init: 'state']}
end

def act(actuator, message) do
def act(actuator, message, _) do
%__MODULE__{actuator | store: message ++ actuator.store}
end

Expand Down Expand Up @@ -48,7 +48,7 @@ defmodule EXNN.ActuatorTest do
end

test 'it should implement the nodeserver behaviour' do
GenServer.cast :my_name, {:signal, [self: 101], nil}
EXNN.NodeServer.forward(:my_name, [self: 101], [])
:timer.sleep 100
store = TestActTwo.store
assert store[:self] == 101
Expand Down
18 changes: 9 additions & 9 deletions test/exnn/neuron_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ defmodule EXNN.NeuronTest do
defmodule TestServer do
use GenServer

def handle_cast({:signal, message, _}, state) do
{:noreply, state ++ message}
def handle_call({:forward, message, _}, _from, state) do
{:reply, :ok, state ++ message}
end

def handle_call(:state, _from, state) do
Expand Down Expand Up @@ -53,23 +53,23 @@ defmodule EXNN.NeuronTest do
assert state_1 == []
assert state_2 == []

neuron = EXNN.Neuron.signal(neuron, [b: 2])
neuron = EXNN.Neuron.signal(neuron, [b: 2], [])
state_1 = GenServer.call(:test_server_1, :state)
state_2 = GenServer.call(:test_server_2, :state)
assert state_1 == []
assert state_2 == state_1
assert neuron.trigger == [:a]
assert neuron.acc == [c: 7, a: 6, c: 1, b: 2]

neuron = EXNN.Neuron.signal(neuron, [a: 2])
neuron = EXNN.Neuron.signal(neuron, [a: 2], [])
state_1 = GenServer.call(:test_server_1, :state)
state_2 = GenServer.call(:test_server_2, :state)
assert state_1 == [me: 15] # 6 + 2 + 7
assert state_2 == state_1
assert neuron.trigger == [:a, :b, :c]
assert neuron.acc == [c: 1, a: 2]

neuron = EXNN.Neuron.signal(neuron, [c: 1])
neuron = EXNN.Neuron.signal(neuron, [c: 1], [])
state_1 = GenServer.call(:test_server_1, :state)
state_2 = GenServer.call(:test_server_2, :state)
assert state_1 == [me: 15]
Expand All @@ -87,11 +87,11 @@ defmodule EXNN.NeuronTest do
bias: 0,
activation: &EXNN.Math.id/1
}
{:ok, pid} = EXNN.Neuron.start_link(genome)
{:ok, _pid} = EXNN.Neuron.start_link(genome)
# :timer.sleep 200
GenServer.cast :my_name, {:signal, [c: 3], nil}
GenServer.cast :my_name, {:signal, [a: 1], nil}
GenServer.cast :my_name, {:signal, [b: 2], nil}
EXNN.NodeServer.forward(:my_name, [c: 3], [])
EXNN.NodeServer.forward(:my_name, [a: 1], [])
EXNN.NodeServer.forward(:my_name, [b: 2], [])
:timer.sleep 100
state_1 = GenServer.call(:test_server_1, :state)
state_2 = GenServer.call(:test_server_2, :state)
Expand Down
6 changes: 3 additions & 3 deletions test/exnn/sensor_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ defmodule EXNN.SensorTest do
{:reply, state, state}
end

def handle_cast({:signal, message, _}, state) do
{:noreply, state ++ message}
def handle_call({:forward, message, _}, _from, state) do
{:reply, :ok, state ++ message}
end

end
Expand Down Expand Up @@ -45,7 +45,7 @@ defmodule EXNN.SensorTest do
end

test "it should catch a signal and forward it to it's outs" do
GenServer.cast :my_name, {:signal, :sync, self}
EXNN.NodeServer.forward(:my_name, :sync, self)
:timer.sleep 5
assert TestOut.state == [my_name_1: 3]
end
Expand Down
30 changes: 22 additions & 8 deletions test/exnn_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,19 @@ defmodule EXNNTest do
end

test "It should launch a first Training task" do
report = EXNN.Trainer.iterate(5)
:timer.sleep 500
iterations = 1000
report = EXNN.Trainer.iterate(iterations)

recorded = GenServer.call(:a_1, :store)
IO.puts "==== #{inspect(recorded)} =========="
meta = GenServer.call(:a_1, :meta)

IO.puts "==== #{inspect(recorded)} ========== #{length(recorded)} ========="
IO.puts "**** #{inspect(meta)} ************** #{Dict.size(meta)} **********"

assert report == :ok
assert length(recorded) == 2*iterations
assert Dict.size(meta) == 2*iterations

refute Enum.empty?(recorded)
end
end
Expand Down Expand Up @@ -105,29 +113,35 @@ end


defmodule HostApp.Recorder do
use EXNN.Actuator, with_state: [store: []]
use EXNN.Actuator, with_state: [store: [], meta: []]

def act(state, message) do
%__MODULE__{state | store: state.store ++ message }
def act(state, message, meta) do
%__MODULE__{state |
store: state.store ++ message,
meta: [meta | state.meta]}
end

def handle_call(:store, _from, state) do
{:reply, state.store, state}
end

def handle_call(:meta, _from, state) do
{:reply, state.meta, state}
end
end

defmodule HostApp.SensOne do
use EXNN.Sensor

def sense(_sensor, _meta) do
def sense(sensor, _meta) do
{0.1}
end
end

defmodule HostApp.SensTwo do
use EXNN.Sensor

def sense(_sensor, _meta) do
def sense(sensor, _meta) do
{0.1, 0.9}
end
end

0 comments on commit 05e06d1

Please sign in to comment.