147 lines
3.8 KiB
Elixir
147 lines
3.8 KiB
Elixir
defmodule Scopes.CSys.Program do
|
|
require Logger
|
|
alias Scopes.CSys
|
|
alias Scopes.Shape
|
|
|
|
defmodule State do
|
|
defstruct [:value, :count, :bias, :threshold, :stage, :prog]
|
|
end
|
|
|
|
def prepare(prog, args) do
|
|
state = %State{
|
|
value: args[:bias],
|
|
count: 0,
|
|
bias: args[:bias],
|
|
threshold: args[:threshold],
|
|
stage: :initial,
|
|
prog: prog
|
|
}
|
|
{state, get_proc(state)}
|
|
end
|
|
|
|
def get_proc(state) do
|
|
get_proc(elem(state.prog, 0), state.stage)
|
|
end
|
|
|
|
defp get_proc(stages, stage) do
|
|
stages[stage] || stages[:default]
|
|
end
|
|
|
|
# basic program
|
|
|
|
def basic_prog() do
|
|
default = &basic_active/2
|
|
stages = %{
|
|
default: default,
|
|
initial: &basic_initial/2,
|
|
active: default,
|
|
retired: default
|
|
}
|
|
transitions = %{
|
|
restart: :initial,
|
|
retire: :retired,
|
|
next: [initial: :active, active: :retired]
|
|
}
|
|
{stages, transitions}
|
|
end
|
|
|
|
def prepare_basic(args \\ [threshold: 0, bias: 0]) do
|
|
prepare(basic_prog(), args)
|
|
end
|
|
|
|
# basic processors
|
|
|
|
def basic_initial(msg, scope) do
|
|
basic(msg, scope)
|
|
end
|
|
|
|
def basic_active(msg, scope) do
|
|
basic(msg, scope)
|
|
end
|
|
|
|
def basic(msg, scope) do
|
|
case Shape.head(msg) do
|
|
[:csys, :set | _rest] -> set(msg, scope)
|
|
[:csys, :data | _rest] -> process_data(msg, scope)
|
|
[:csys, :connect | _rest] -> CSys.connect(msg, scope)
|
|
[:csys, :create, :succ | _rest] -> create_succ(msg, scope)
|
|
[:csys, :create, :pred | _rest] -> create_pred(msg, scope)
|
|
#[:csys, :next | _rest] -> next(scope, meta)
|
|
#[:csys, action | rest] -> transition(action, scope)
|
|
_ -> CSys.forward(msg, scope) || CSys.notify(msg, scope)
|
|
end
|
|
end
|
|
|
|
# message handlers // proc steps
|
|
|
|
def process_data(msg, scope) do
|
|
data = Shape.data(msg)
|
|
state = CSys.state(scope)
|
|
value_n = state.value + data.value
|
|
Logger.info([data: data.value, state: state.value, threshold: state.threshold])
|
|
if value_n >= state.threshold do
|
|
msg = Shape.create(Shape.head(msg), data: %{data | value: value_n})
|
|
CSys.forward(msg, scope) || CSys.notify(msg, scope)
|
|
state_n = %{state | value: state.bias}
|
|
CSys.update(put_elem(scope, 0, state_n))
|
|
else
|
|
state_n = %{state | value: value_n}
|
|
CSys.update(put_elem(scope, 0, state_n))
|
|
end
|
|
end
|
|
|
|
def set(msg, scope) do
|
|
data = Shape.data(msg)
|
|
state = CSys.state(scope)
|
|
value = data[:value] || state.value
|
|
threshold = data[:threshold] || state.threshold
|
|
bias = data[:bias] || state.bias
|
|
state_n = %{state | value: value, threshold: threshold, bias: bias}
|
|
CSys.update(put_elem(scope, 0, state_n))
|
|
end
|
|
|
|
def create_succ(msg, scope) do
|
|
new = CSys.create(msg, restart(scope))
|
|
data = Shape.data(msg)
|
|
CSys.send_message(self(), ~w(csys connect)a, Map.put(data, :target, new))
|
|
end
|
|
|
|
def create_pred(msg, scope) do
|
|
new = CSys.create(msg, restart(scope))
|
|
data = Shape.data(msg)
|
|
CSys.send_message(new, ~w(csys connect)a, Map.put(data, :target, self()))
|
|
end
|
|
|
|
# synapse operations
|
|
|
|
def multiply(n) do
|
|
fn msg ->
|
|
value = Shape.data(msg)[:value] || 0
|
|
Shape.create(Shape.head(msg), data: %{Shape.data(msg) | value: n * value})
|
|
end
|
|
end
|
|
|
|
def negate(), do: multiply(-1)
|
|
|
|
# helper functions
|
|
|
|
def restart({state, _proc, syns, env}) do
|
|
state_n = %{state | stage: :initial}
|
|
{state_n, get_proc(state_n), syns, env}
|
|
end
|
|
|
|
# demo init sequences
|
|
|
|
def init_seq_b1() do [
|
|
fn [zero | _ns] -> CSys.send_message(zero, ~w(csys create pred)a,
|
|
%{op: [CSys.data_only(), negate()]}) end,
|
|
fn [one | _ns] -> CSys.send_message(one, ~w(csys create succ)a,
|
|
%{op: CSys.data_only()}) end,
|
|
fn [two | _ns] -> CSys.send_message(two, ~w(csys create pred)a) end,
|
|
fn [three | others] ->
|
|
zero = List.last(others)
|
|
CSys.send_message(three, ~w(csys connect)a, %{target: zero})
|
|
end
|
|
]
|
|
end
|
|
end
|