...
 
Commits (4)
......@@ -36,11 +36,11 @@ defmodule Marlon.Actor do
# Attaches an agent for the given goal to this actor
def attach_agent(actor, goal) do
GenServer.call(actor, {:attach_agent, goal, goal}, 30000) # 30 seconds
GenServer.call(actor, {:attach_agent, goal, goal}, 30000) # The goal module itself acts as a group here!
end
def attach_agent(actor, goal, group) do
GenServer.call(actor, {:attach_agent, goal, group}, 30000) # 30 seconds
GenServer.call(actor, {:attach_agent, goal, group}, 30000)
end
# Removes an agent from this actor
......@@ -94,6 +94,10 @@ defmodule Marlon.Actor do
{:reply, state.marlon_agents}
end
def handle_call(:get_state, _from, state) do
{:reply, state, state}
end
def handle_cast(:do_action, state) do
Enum.map(state.marlon_agents,
fn(agent) -> Marlon.Agent.do_action(agent) end)
......
......@@ -30,29 +30,22 @@ defmodule Marlon.Application do
## Application initialisation
def start(_reason, _args) do
# Check whether this is the node initiated the Marlon system
Logger.info "hello there application!"
Logger.info "cookie: " <> (Kernel.inspect(:erlang.get_cookie()))
Logger.info "hello there!"
# Get libcluster topologies
topologies = Application.get_env(:libcluster, :topologies)
# Check whether this is the node initiated the Marlon system
node_name = Atom.to_string(Node.self())
topologies = Application.get_env(:libcluster, :topologies)
children = if ((String.contains?(node_name, "aux")) || (String.contains?(node_name, "slave"))) do
Logger.info "Starting Marlon on auxiliary node"
[{__MODULE__, []},
{Cluster.Supervisor, [topologies, [name: MyApp.ClusterSupervisor]]},
{Cluster.Supervisor, [topologies, [name: Marlon.ClusterSupervisor]]},
]
else
Logger.info "Starting Marlon on primary node"
[{__MODULE__, []},
%{id: :agentgroups, start: {Marlon.AgentGroupManager, :start_link, [[], [name: :agentgroups]]}},
{Cluster.Supervisor, [topologies, [name: MyApp.ClusterSupervisor]]},
{Cluster.Supervisor, [topologies, [name: Marlon.ClusterSupervisor]]},
]
end
......
# Marlon - Multi-Agent Reinforcement Learning On Networks
# Copyright (C) 2018 Smile-IT project
#
# This file is part of Marlon.
#
# Marlon is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Marlon is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Marlon. If not, see http://www.gnu.org/licenses
defmodule Marlon_0 do
@moduledoc """
Deprecated version of Marlon's macros
(which uses a keyword list syntax)
"""
# Ensure you can `use` the macros defined in this module
defmacro __using__(_opts) do
quote do
import Marlon_0
end
end
################################
## defactor
################################
@doc """
Defines an actor. (An actor is a wrapper around a GenServer that automatically
generates the client-side API.)
defactor has the following keyword parameters:
:init An anonymous function that returns an actor's initial state
(equivalent to a GenServer's init function)
:x Handler for message x. The value of this parameter is a 2-element List
The first element is either :call, :cast or :info, to specify the handler type.
The second element is an anonymous function.
Its first parameter always is the actor's state.
Any other parameters are the message's parameters.
"""
defmacro defactor(name, properties) do
{raw_contract, properties} = Keyword.pop(properties, :contract)
[fn_names, genserver_names] = categorize_members(properties)
contract = Marlon.Contract.build_contract(raw_contract, genserver_names)
# Generate client API fns
client_fns = for fn_name <- genserver_names do
[fn_type, fn_ast] = properties[fn_name]
genclient_fn(fn_name, fn_type, fn_ast)
end
# Generate corresponding server handlers
server_fns = for fn_name <- genserver_names do
[fn_type, fn_ast] = properties[fn_name]
genserver_fn(fn_name, fn_type, fn_ast)
end
# Generate plain functions
fns = for fn_name <- fn_names do
fn_args = get_fn_params(properties[fn_name])
fn_body = get_fn_body(properties[fn_name])
quote do
def unquote(fn_name)(unquote_splicing(fn_args)) do
unquote(fn_body)
end
end
end
# Put it all together
quote do
defmodule unquote(name) do
use GenServer
def start_link(state, opts) do
GenServer.start_link(__MODULE__, state, opts)
end
defp check_contract(msg) do
Marlon.Contract.check_contract(msg, unquote(contract))
end
unquote_splicing(fns)
unquote_splicing(Enum.filter(client_fns, & !is_nil(&1)))
use Marlon.Actor # Add agent-related functions right here
unquote_splicing(server_fns)
end
end
end
# Find out which of the members are plain functions, and which are GenServer handlers
# Returns [fn_names, genserver_names],
# where fn_names is a list of member names of plain functions
# and genserver_names is a list of member names of GenServer handlers
defp categorize_members(properties) do
member_names = Keyword.keys(properties)
Enum.reduce(member_names, [[], []],
fn(name, [fn_names, genserver_names]) ->
if (is_list(properties[name])) do
[fn_names, genserver_names ++ [name]]
else
[fn_names ++ [name] , genserver_names]
end
end)
end
# Generate a Genserver server-side function
# fn_type is :call, :cast or :info
defp genserver_fn(fn_name, fn_type, fn_ast) do
args = get_fn_params(fn_ast)
{fixed_args, other_args} = if (fn_type == :call) do
Enum.split(args, 2) # Call handlers have two fixed params; the state and sender
else
Enum.split(args, 1) # Cast and info handlers only have state
end
body = get_fn_body(fn_ast)
handle_fn_name = String.to_atom("handle"<>"_"<>Atom.to_string(fn_type))
quote do
def unquote(handle_fn_name)({unquote(fn_name),unquote_splicing(other_args)},unquote_splicing(Enum.reverse(fixed_args))) do
check_contract(unquote(fn_name))
unquote(body)
end
end
end
# Generate a Genserver client-side function
defp genclient_fn(fn_name, fn_type, fn_ast) do
args = get_fn_params(fn_ast)
{_fixed_args, other_args} = if (fn_type == :call) do
Enum.split(args, 2) # Call handlers have two fixed params; the state and sender
else
Enum.split(args, 1) # Cast and info handlers only have state
end
if fn_type == :info do
nil
else
quote do
def unquote(fn_name)(pid, unquote_splicing(other_args)) do
GenServer.unquote(fn_type)(pid,{unquote(fn_name), unquote_splicing(other_args)})
end
end
end
end
# Given an AST node of an anonymous function, get its list of formal parameters
defp get_fn_params(fn_ast) do
List.first(elem(List.first(elem(fn_ast,2)),2))
end
# Given an AST node of an anonymous function, get its function body
defp get_fn_body(fn_ast) do
List.last(elem(List.first(elem(fn_ast,2)),2))
end
################################
## defgoal
################################
defmacro defgoal(name, properties) do
# module = gen_module(properties[:type])
module = properties[:type]
quote do
defmodule unquote(name) do
import unquote(module)
def instantiate() do
Marlon.LearningAlgorithm.init_algorithm(%unquote(module){}, unquote(properties))
end
end
end
end
# Generates a module name: Marlon.XGoal
# def gen_module(name) do
# atom = String.to_atom(name <> "Goal")
# {:__aliases__, [alias: false], [:Marlon, atom]}
# end
end
......@@ -42,7 +42,7 @@ defmodule Marlon.Mixfile do
defp deps do
[
# {:erlport, "~> 0.10.0"},
{:libcluster, "~> 3.0.2"},
{:libcluster, "~> 3.0.3"},
# {:smile_it_demo_ui, git: "https://github.com/rhumbertgz/smile_it_demo_ui.git"},
# {:exprof, "~> 0.2.0"},
# {:csv, "~> 2.0.0"},
......