Creamas - Creative Multi-Agent Systems¶

Creamas is Python (3.5+) library for (creative) multi-agent systems. It was created as a tool to research and implement multi-agent systems that exhibit emergent and/or creative behavior in some ways. However, its main implementations are general enough to be used for multi-agent systems with other purposes.
Features, etc.¶
- Built on top of aiomas
- Agents are designed to produce creative artifacts
- Each agent lives in an environment
- Environment acts also as a communication route between the agents
- Support for multiple cores
- Support for distributed systems running on multiple nodes
- Easy made iterative simulations for agents
- Social decision making using voting
- NetworkX integration to generate agent connections from NetworkX structures and vice versa
See Overview for a more detailed introduction to the library’s main components. Project’s main repository is in github.
Overview¶
Creamas is developed as a tool for people to effortlessly build, and do research
on, multi-agent systems in the field of computational creativity. Creamas is built
on top of aiomas, which provides
a communication route (RPC) between agents and the basic agent and container
(dubbed as Environment
in Creamas) implementations. If you
want to know more about how the low level communication works, see aiomas
documentation.
Agents And Environments¶
Agents (CreativeAgent
) in Creamas focus on building artifacts
and evaluating them. Each agent belongs to Environment
, which
also serves as a communication route between the agents.
Environment can also hold other information shared by
all agents, or, e.g. provide means for the agents to communicate with nearby
agents in a 2D-grid. Agents are created by giving the environment as an
initialization parameter.
from creamas.core import CreativeAgent, Environment
# Create environment to tcp://localhost:5555/
env = Environment.create(('localhost', 5555))
# Create agent with address tcp://localhost:5555/0
a1 = CreativeAgent(env)
# Create agent with address tcp://localhost:5555/1
a2 = CreativeAgent(env)
The fundamental design principle guiding the development of Creamas is that
agents create artifacts (Artifact
) in some domain(s) and
evaluate them. A lot of the current functionality is geared towards this goal.
Note
Creamas does not take a stand on the design of the multi-agent systems or individual agents and is quite agnostic of the agent implementations. Therefore, Creamas can be used to develop arbitrary agent societies, but you might want to take a look at aiomas if you do not need any of the additional functionality provided by Creamas.
Communication Between the Agents¶
Communication between the agents in one of the key interests in multi-agent systems.
In Creamas, agents can expose their own functions as services to other agents by using
expose()
decorator from aiomas library. An agent wishing to communicate
with another agent connects to the remote agent and calls the exposed function
remotely. To this end they have to know the other agent’s (tcp) address.
Note
Agents derived from CreativeAgent
are
supposed to store addresses of known other agents in their
connections
.
Consider a following hypothetical service()
-method an agent A has:
@aiomas.expose
def service(self, param):
# do something with param
# ...
return value
Another agent, agent B, knowing that the agent A’s address is addr
can then use A’s service
method by connecting to agent A through
its environment.
async def client(self, my_param):
remote_agent_A = await self.env.connect(addr)
value = await remote_agent_A.service(my_param)
# do something with the value
Importantly, the agents do not have to reside in the same environment or even in the same machine, i.e. you can connect to any agent or environment as long as you know the address of the specific agent in the environment. However, the remote agent and its environment have to be implemented using classes derived from aiomas library, like Creamas agent classes and environments do.
Note
Connecting to an agent and calling an exposed function are done
asynchronously using await
keyword before the function call. Any method
using await
has to have async
keyword at the start of its function
definition.
Creating and Analyzing Agent Connections¶
Studying varying social network structures in creative agent systems is one of
the main purposes Creamas exists. To generate agent connections and to analyze
them, Creamas has some built-in support for integration with
NetworkX graph library.
These functionalities are found from creamas.nx
module. The main
functions are connections_from_graph()
and
graph_from_connections()
. They allow priming agent-to-agent
connections with varying properties and analyzing them easily using NetworkX
graph structures.
Evaluating Artifacts¶
Exchanging artifacts, and information about them, is an eminent functionality for agents in Creamas. An agent can ask other agent’s opinions about its own artifacts or other artifacts it has seen. This allows the agent to accumulate knowledge about the artifact preferences of other agents, which may alter the agent’s own behavior.
Method ask_opinion()
offers
a shortcut to ask an opinion about an artifact from a remote agent.
# This is a toy example. Code won't work off the shelf as the agents don't
# have any evaluation methods, which we will see in the next section.
from creamas.core import Artifact
# Create some artifact.
ar = Artifact()
# first evaluate it yourself
ev = a1.evaluate(ar)
# ask other agent's opinion (evaluation) of it
remote_addr = a1.connections[0]
ret = a1.ask_opinion(remote_addr, ar)
# get a1's current attitude towards a2
att = a1.get_attitude(remote_addr)
# get difference between evaluations
diff = abs(ev - ret)
# if evaluations are very similar, become friendlier with the agent
if diff < 0.2:
a1.set_attitude(a2.addr, att + 0.1)
# if evaluations are very different, dislike the agent
if diff > 0.8
a1.set_attitude(a2.addr, att - 0.1)
Iterative Simulation¶
Creamas provides an easy to use Simulation
which can be used to run iterative simulations using agents’ act()
.
Simulations can be created directly or they can be given an environment at
initialization time. See Using Simulation for details.
Voting and Other Social Choice Functions¶
Creamas offers some basic social choice/social decision making behavior for agents in the form of voting. Agents can publish their own artifacts as candidates. These candidates can then be collected and voted upon. Optionally, agents can validate each others candidates, i.e. exercise veto power on them, before voting takes place. See Voting for Agents for details.
Support for Multiple Cores and Distributed Systems¶
Creamas has inherent support for using multiple cores on a single machine and
distributing your environments on multiple nodes, e.g., on a computing cluster.
However, these functionalities are not yet fully tested, but have been used in
several systems and platforms effectively. Multiprocessing functionality is in
mp
-module (see Multiprocessing functionality), and distributing the environments on several
nodes is in ds
-module (see Distributed Systems).
If you want to learn more about multiprocessing and distributed system support in Creamas, read an overview of them: Multiprocessing and Distributed Systems.
Installation¶
Creamas is developed with Python 3.7, the easiest way to install the latest distribution of Creamas is using pip:
pip install creamas
We encourage the use of virtual environments to avoid conflicting package requirements in your projects.
Installing the Development Version¶
If you want to use the latest development version, you can clone the repository from git:
$>git clone https://github.com/assamite/creamas.git creamas
$>cd creamas
$>python3.7 -m venv env # create venv for python 3.7
$>source env/bin/activate # start virtualenv
$>pip install -r requirements.txt
$>pip install -r c_reqs.txt # packages using C are in different file for readthedocs
Implementing Agent Classes¶
All agents used in Creamas must inherit from
CreativeAgent
. They should also accept
the agent’s Environment
as the first
parameter in their __init__()
. The environment should then be passed on
to super().__init__()
.
Each agent class should call super().__init__()
as one of the first
things in __init__()
, for example:
from creamas import CreativeAgent, Environment
class MyAgent(CreativeAgent):
def __init__(self, environment, *args, **kwargs):
self.my_arg = kwargs.pop('my_arg', None)
super().__init__(environment, *args, **kwargs)
# Your own initialization code
env = Environment.create(('localhost', 5555))
agent = MyAgent(env, my_arg=5)
# do stuff
env.close()
Using Simulation¶
Creamas contains easy to use iterative simulation to perform experiments on
a single computer set up. Each agent wishing to use simulation has to implement
act()
:
-
CreativeAgent.
act
(*args, **kwargs)[source] Trigger agent to act.
This is a dummy method which should be overridden in a subclass.
This function serves as the main function for the simulations, and is called for each agent on each iteration step of the simulation.
Raises: NotImplementedError – If not overridden in subclass. See also
Simulation calls act()
for each agent in each simulation step. What agents
do in their turn is up to you!
Simple Simulations¶
Good news! Creating a simple simulation is made easy with create
.
Observe.
from mymodule import MyAgent
from creamas.core.simulation import Simulation
env_kwargs={'addr': ('localhost', 5555)}
agent_kwargs = {'foo': 'bar', 'Cthulhu': 'rises'}
# Create initial simulation with default parameters using MyAgents-class
# and passing agent_kwargs to each agent at initialization time.
sim = Simulation.create(env_kwargs=env_kwargs,
agent_cls=MyAgent,
agent_kwargs=agent_kwargs,
n_agents=20)
# Advance simulation by single step
sim.step()
# Close the simulation and it's environment.
sim.close()
create()
offers few arguments to modify simulation initialization:
- You can create simulation with multiple agent classes each with its own keyword arguments and number of agents.
from mymodule import MyAgent, CthulhuAgent
from.creamas.core.simulation import Simulation
env_kwargs={'addr': ('localhost', 5555)}
myagent_kwargs = {'foo': 'bar', 'Cthulhu': 'rises'}
cthulhu_kwargs = {"R'lyeh": 'sunken'}
agent_kwargs=[myagent_kwargs,cthulhu_kwargs]
agent_cls = [MyAgent, CthulhuAgent]
n_agents = [10, 1]
sim = Simulation.create(env_kwargs=env_kwargs,
agent_cls=agent_cls,
n_agents=n_agents,
agent_kwargs=agent_kwargs)
- You can create a simulation with your own environment, which is automatically passed down to the agents at their initialization time.
from mymodule import StarSpawnAgent
from myenv import InnsmouthEnvironment
from creamas.core.simulation import Simulation
env_kwargs = {'addr': ('localhost', 5555),
'weather': 'drizzle, slight wind',
'atmosphere': 'gloomy'}
sim = Simulation.create(agent_cls=StarSpawnAgent
env_cls=InnsmouthEnvironment,
env_kwargs=env_kwargs)
Complex Simulation Setups¶
If you need more control on creating the environment and agents, you can
create your environment directly and then create your agents. After you have
fully initialized the environment, you can then pass it to the
Simulation
at initialization time.
from mymodule import StarSpawnAgent
from creamas.core.enviroment import Environment
from creamas.core.simulation import Simulation
env = Environment.create(('localhost', 5555))
for i in range(10):
# do some complex calculation
# ...
StarSpawnAgent(env, cause_havoc=True, non_euclidian_angle=mystery)
sim = Simulation(env=env)
Advancing Simulation¶
Simulation holds a few different ways to advance it.
# Advance simulation by a single step (executing all agents once)
# or advance simulation to the end of the current step.
sim.step()
# Advance simulation by executing a single agent.
sim.next()
# Advance simulation by 10 steps
sim.steps(10)
Logging Simulation¶
TODO: Log the logging of logger.
Multiprocessing and Distributed Systems¶
Creamas has builtin basic support for agent environments running on multiple cores (see Multiprocessing functionality) and agent environments set up on distributed systems (see Distributed Systems), e.g. computing clusters. In this section, we explain the basic architectural concepts required to understand how to build your own multiprocessing and distributed environments. You should be familiar with basic overview of the library before venturing forth (see Overview).
Next, we first go over multiprocessing implementation of the basic environment,
MultiEnvironment
, and then the distributed system
implementation, DistributedEnvironment
.
Support for Multiple Cores¶
Multiprocessing support in Creamas is built around
MultiEnvironment
. This class spawns a set of
Environment
slaves in their own subprocesses
and acts as
a master for them. The master environment has also its own instance of
Environment
which is used to communicate
with the slaves, but because it does not contain any agents (other than
a possible manager agent, as we will see when dealing with distributed systems),
we will not distinguish it from the MultiEnvironment
for
the time being.
Slave Environments and Managers¶
Each of the slave environments in the MultiEnvironment
is executed in its own subprocess (see Figure 1.).
As the slave environments are outside the master environment’s
process, their functions cannot be directly called by the master and thus the
slaves require other functionality to accept orders from the master.
To this end, each slave environment is initialized
with a manager agent, EnvManager
or its subclass,
which acts as a bridge between external sources and the environment instance itself;
the external source being in most cases the master environment.
Figure 1. Basic architecture for MultiEnvironment
.
The environment in the main process is used to connect to each slave
environment’s manager and sends commands to them. The managers then forward
the commands to the slave environments which execute them.
Note
If an environment is a slave environment in some
MultiEnvironment
, then its first agent (the agent in path
tcp://environment-address:port/0
) is always expected to be an instance of
EnvManager
, or a subclass of it.
Managing Functions¶
The basic manager implementation contains several exposed
managing functions for the environment’s functions, i.e. functions that call
the underlying environment’s
functions with the same name. These managing functions allow the master to
execute tasks on each of the slave environments, e.g., to collect the addresses
of all the agents in all the environments or trigger act()
of each of
these agents.
Communication Between Master and Slaves¶
The communication between the master and the slave environment happens through tcp connection. In principle, the functionality works as follows:
- Master environment connects to the slave’s manager.
- Master environment calls slave manager’s exposed method.
- The slave’s manager calls the method with the same name in its environment with the given arguments.
- The slave environment executes the method and returns possible return value.
- The slave manager passes the return value back to the master environment.
- Master environment closes the connection.
Warning
Managers do not check who gives the execution orders by default. When deploying in open environments, e.g. environments exposed to internet, it is important that you do not expose any unwanted functionality through them without adding some safe guards to the exposed functions.
Creamas is mainly developed to be a research tool to be used in closed
environments, and therefore is not particularly designed to offer protection
for any kinds of attacks. However, aiomas
has some built-in encryption support for, e.g., TSL. As Creamas’
Environment
is just a subclass of aiomas’
Container
, the TSL support from aiomas can be utilised in Creamas.
Developing for Multiple Cores¶
To utilize multiprocessing support in your own implementations, you can give
following initialization parameters to MultiEnvironment
:
- Address: Address for the manager/master environment.
- Environment class: Class for the manager/master environment which is used to connect to each of the slave managers.
- Manager class: Class for the master environment’s manager. This should not be needed if you are not using
MultiEnvironment
as a part ofDistributedEnvironment
After the master environment has been created, the slave environments can be
spawned using spawn_slaves()
. It accepts
at least the following arguments.
- Slave addresses: Addresses for the slave environments, the size of this list will define how many subprocesses are spawned.
- Slave environment class: Class for each slave environment inside the multiprocessing environment.
- Slave environment parameters: Initialization parameters for each slave environment.
- Slave manager class: This is the manager agent class that is used for each slave environment.
You can, of course, also subclass MultiEnvironment
itself
(see GridMultiEnvironment
for an example).
Support for Distributed Systems¶
Support for distributed systems in Creamas is built around
DistributedEnvironment
. Distributed environment is
designed to be used with multiple (quite homogeneous) nodes which operate in
a closed system where each node can make tcp connections to ports in
other nodes. Further on, it requires that it is located in a machine that is
able to make SSH connections to the nodes.
The basic architecture of DistributedEnvironment
can
be seen in the Figure 2. In short, DistributedEnvironment
acts
as a master for the whole environment, i.e. it does not hold “actual” simulation
agents, but serves only as a manager for the simulation. Other nodes
in the environment then each contain an instance of
MultiEnvironment
with its own manager, which accepts orders
from DistributedEnvironment
. The slave environments inside
each MultiEnvironment
then hold the actual agents for the
simulation (and the manager for the slave environment).
Figure 2. Basic architecture for DistributedEnvironment
.
It manages a set of nodes each containing a MultiEnvironment
.
The main difference from the single node implementation is, that the main
process environment on each node also holds a manager which accepts commands
for that node.
Next, we look at how to set up and use DistributedEnvironment
.
In the following, node and MultiEnvironment
are used
interchangeably.
Using a Distributed Environment¶
Initialization of a distributed environment is done roughly in the following steps:
- Initialize
DistributedEnvironment
with a list of node locations- Create node spawning terminal commands for each node, i.e. commands which start
MultiEnvironment
on each node.- Spawn nodes using
spawn_nodes()
- Wait until all nodes are ready (see, e.g.
is_ready()
) usingwait_nodes()
. A node is ready when it has finished its own initialization and is ready to execute orders.- Make any additional preparation for the nodes using
prepare_nodes()
.
After this sequence, the DistributedEnvironment
should be
ready to be used. The main usage for iterative simulations is to call
trigger_all()
, which triggers all
agents in all the nodes (in all the slave environments) to act.
Spawning Nodes¶
When spawn_nodes()
is called,
DistributedEnvironment
spawns a new process for each node
in the list of node locations given at initialization time. For each process
it does the following:
- it opens a SSH connection to one of the nodes, and
- executes a command line script on the node.
The command line script executed is assumed to spawn an instance of
MultiEnvironment
with a manager attached to it. This
manager is then used to communicate any commands from
DistributedEnvironment
to the slave environments on that
node. The command line script can also do other preparation for the node, e.g.
populate its slave environments with agents.
The command line script executed is assumed to wait until the
MultiEnvironment
is stopped, i.e. it does not exit
after the initialization (as in the naive case this would delete the
environment). To achieve this, you can for example add a following kind of
function to your node spawning script and call it last in the script:
async def run_node(menv, log_folder):
try:
await menv.manager.stop_received
except KeyboardInterrupt:
logger.info('Execution interrupted by user.')
finally:
ret = await menv.close(log_folder, as_coro=True)
return ret
When run_node()
is called, the script will block its execution until the
manager of MultiEnvironment
receives a stop sign. The stop
sign is sent to each node’s manager when stop_nodes()
is called.
See creamas/examples/grid/
for an example implementation of a distributed
agent environment.
API Documentation¶
Core¶
Agent¶
Agent module holds CreativeAgent
implementation, a subclass of
aiomas.Agent
, which holds basic functionality thought to be shared by
creative agents.
-
class
creamas.core.agent.
CreativeAgent
(environment, resources=0, name=None, log_folder=None, log_level=10)[source]¶ Base class for all creative agents.
All agents share certain common attributes:
Variables: - env – The environment where the agent lives.
- max_res (int) – Agent’s resources per step, 0 if agent has unlimited resources.
- cur_res (int) – Agent’s current resources.
- A (list) – Artifacts the agent has created so far
- D (dict) – Domain knowledge, other agents’ artifacts seen by this agent
- connections (list) – Dictionary of other agents this agent knows
- name (str) – Name of the agent. Defaults to the address of the agent.
-
coroutine
act
(*args, **kwargs)[source]¶ Trigger agent to act.
This is a dummy method which should be overridden in a subclass.
This function serves as the main function for the simulations, and is called for each agent on each iteration step of the simulation.
Raises: NotImplementedError – If not overridden in subclass. See also
-
add_artifact
(artifact)[source]¶ Add artifact to
A
.Raises: TypeError – If the artifact is not derived from Artifact
.
-
add_connection
(addr, **kwargs)[source]¶ Add an agent with given address to current
connections
with given information.Does nothing if address is already in
connections
. Given**kwargs
are stored as key-value pairs toconnections[addr]
dictionary.Parameters: addr (str) – Address of the agent to be added Returns: True
if the agent was successfully added,False
otherwise.
-
add_connections
(conns)[source]¶ Add agents from
conns
toconnections
.Parameters: conns (list) – A list of (addr, kwargs)
-tuplesReturns: A boolean list, as returned by add_connections()
.
-
coroutine
ask_opinion
(addr, artifact)[source]¶ Ask an agent’s opinion about an artifact.
Parameters: - addr (
CreativeAgent
) – Address of the agent which opinion is asked - artifact (object) – artifact to be evaluated
Returns: agent’s evaluation of the artifact
Return type: This is a shortcut to:
remote_agent = await self.env.connect(addr) opinion = await remote_agent.evaluate(artifact)
Note
The artifact object should be serializable by the environment.
- addr (
-
close
(folder=None)[source]¶ Perform any bookkeeping needed before closing the agent.
This is a dummy method which should be overridden in a subclass.
Parameters: folder (str) – Folder where the agent should save its data.
-
coroutine
connect
(addr)[source]¶ Connect to agent in given address using the agent’s environment.
This is a shortcut to
connect()
.Returns: aiomas.Proxy
object for the connected agent.
-
evaluate
(artifact)[source]¶ Evaluate an artifact.
** This is a dummy method which should be overridden in a subclass. **
-
get_connections
(data=False)[source]¶ Get agent’s current connections.
Parameters: data (bool) – Also return the data dictionary for each connection. Returns: A list of agent addresses or a dictionary
-
publish
(artifact)[source]¶ Publish artifact to agent’s environment.
Parameters: artifact ( Artifact
) – artifact to be published
-
coroutine
random_connection
()[source]¶ Connect to random agent from current
connections
.Returns: aiomas.Proxy
object for the connected agent.
-
A
¶ Artifacts created so far by the agent.
-
D
¶ Domain knowledge accumulated by this agent.
Dictionary of agents and their artifacts.
-
connections
¶ Known other agents
The connections has a dict-in-a-dict data type able to hold arbitrary information about known other agents. The keys in the dictionary are agent addresses and values are dictionaries holding information relating to the key-agent.
-
cur_res
¶ Agent’s current resources. Capped to maximum resources.
-
env
¶ The environment where the agent lives. Must be a subclass of
Environment
.
-
logger
¶ A logger for the agent.
The logger should be derived from
ObjectLogger
.
-
max_res
¶ Maximum resources for the agent per simulation iteration act.
If
max_res == 0
, agent has unlimited resources. If maximum resources are set below current resources, current resources are capped to new maximum resources.
-
name
¶ The name of the agent.
The agent should not change its name during its lifetime.
Artifact¶
Artifact module holds Artifact, a base class for artifacts created by creative agents.
-
class
creamas.core.artifact.
Artifact
(creator, obj, domain=<class 'int'>)[source]¶ Base class for artifacts.
A wrapper around the actual artifact object (
obj
) which holds information about the creator, framings and evaluations of the artifact.-
add_eval
(agent, e, fr=None)[source]¶ Add or change agent’s evaluation of the artifact with given framing information.
Parameters:
-
creator
¶ The name of the agent which created the artifact.
-
domain
¶ Domain of the artifact. Domain must match feature’s possible domains at evaluation time, or None is returned.
-
evals
¶ Dictionary of evaluations for the artifact.
Keys are the names of the evaluating agents and values are their actual evaluations.
-
framings
¶ Dictionary of framings for the artifacts.
Keys are the names of the framing agents and values are their actual framings.
-
obj
¶ Artifact object itself.
-
Environment¶
This module holds Enviroment
-class, an universe where the agents live.
Environment holds methods for inter-agent communication and some utilities that
are usually needed when implementing creative multi-agent systems.
All implementations should subclass Environment
in order to provide basic
functionality for the system to operate.
Environments are used by defining their address at the instantation time, and adding agents to their container.
-
class
creamas.core.environment.
Environment
(base_url, loop, clock, connect_kwargs)[source]¶ Base environment class inherited from
aiomas.Container
.-
add_artifact
(artifact)[source]¶ Add artifact with given framing to the environment.
Parameters: artifact (object) – Artifact to be added.
-
add_artifacts
(artifacts)[source]¶ Add artifacts to
artifacts
.Parameters: artifacts – list of Artifact
objects
-
close
(folder=None, as_coro=False)[source]¶ Close the environment.
Does the following:
- calls
save_info()
- for each agent: calls
close()
- Shuts down its RPC-service.
- calls
-
create_connections
(connection_map)[source]¶ Create agent connections from a given connection map.
Parameters: connection_map (dict) – A map of connections to be created. Dictionary where keys are agent addresses and values are lists of (addr, attitude)-tuples suitable for add_connections()
.Only connections for agents in this environment are made.
-
create_random_connections
(n=5)[source]¶ Create random connections for all agents in the environment.
Parameters: n (int) – the number of connections for each agent Existing agent connections that would be created by chance are not doubled in the agent’s
connections
, but count towards connections created.
-
destroy
(folder=None, as_coro=False)[source]¶ Close the environment.
Deprecated since version 0.4.0: Use
close()
instead
-
get_agents
(addr=True, agent_cls=None, include_manager=False)[source]¶ Get agents in the environment.
Parameters: - addr (bool) – If
True
, returns only addresses of the agents. - agent_cls – Optional, if specified returns only agents belonging to that particular class.
- include_manager (bool) – If
True
includes the environment’s manager, i.e. the agent in the addresstcp://environment-host:port/0
, to the returned list if the environment has attributemanager
. If environment does not havemanager
, then the parameter does nothing.
Returns: A list of agents in the environment.
Return type: Note
Manager agents are excluded from the returned lists of agents by default.
- addr (bool) – If
-
coroutine
get_artifacts
(agent=None)[source]¶ Return artifacts published to the environment.
Parameters: agent – If not None
, then returns only artifacts created by the agent.Returns: All artifacts published (by the agent). Return type: list If environment has a
manager
agent, e.g. it is a slave environment inMultiEnvironment
, then the manager’sget_artifacts()
is called.
-
get_connections
(data=True)[source]¶ Return connections from all the agents in the environment.
Parameters: data (bool) – If True
return also the dictionary associated with each connectionReturns: A list of (addr, connections)
-tuples, whereconnections
is a list of addresses agent inaddr
is connected to. Ifdata
parameter isTrue
, then theconnections
list contains tuples of(nb_addr, data)
-pairs , wheredata
is a dictionary.Return type: dict Note
Environment’s manager agent is excluded from the returned list.
-
get_random_agent
(agent)[source]¶ Return random agent that is not the same as the agent given as a parameter.
Parameters: agent ( CreativeAgent
) – Agent that is not wanted to returnReturns: random, non-connected, agent from the environment Return type: CreativeAgent
-
is_ready
()[source]¶ Check if the environment is fully initialized.
The function is mainly used by the multiprocessing environment managers and distributed environments to ensure that the environment has been correctly initialized before any other preparations are done for the environments or the simulation is started.
Override the function in the subclasses which need more time consuming initialization routines. The function should return True when the environment is ready be used in a simulation, or when any cross-environment initialization routines can be run. That is, the environment is inherently in a coherent state, and can execute orders from managers or simulations.
Return type: bool Returns: This basic implementation returns always True.
-
save_info
(folder, *args, **kwargs)[source]¶ Save information accumulated during the environments lifetime.
Called from
destroy()
. Override in subclass.Parameters: folder (str) – root folder to save information
-
coroutine
trigger_act
(*args, addr=None, agent=None, **kwargs)[source]¶ Trigger agent to act.
If agent is None, then looks the agent by the address.
Raises: ValueError – if both agent and addr are None.
-
coroutine
trigger_all
(*args, **kwargs)[source]¶ Trigger all agents in the environment to act asynchronously.
Returns: A list of agents’ act()
return values.Given arguments and keyword arguments are passed down to each agent’s
creamas.core.agent.CreativeAgent.act()
.Note
The environment’s manager agent, i.e. if the environment has
manager
, is excluded from acting.
-
age
¶ Age of the environment.
-
artifacts
¶ Published artifacts for all agents.
-
log_folder
¶ Logging folder for the environment. If set, will create py:class:creamas.logging.ObjectLogger for that folder.
-
logger
¶ Logger for the environment.
-
name
¶ Name of the environment.
-
Simulation¶
Basic simulation implementation where agents in the same environment can be run in an iterative manner.
-
class
creamas.core.simulation.
Simulation
(env, callback=None, log_folder=None)[source]¶ A base class for iterative simulations.
In each step the simulation calls
act()
for each agent in the simulation.Create simulation for previously set up environment.
Parameters: - env (
Environment
,MultiEnvironment
orDistributedEnvironment
) – fully initialized environment with agents already set - callback (callable) – function to call after each simulation step
- log_folder (str) – folder to log simulation information
-
classmethod
create
(agent_cls=None, n_agents=10, agent_kwargs={}, env_cls=<class 'creamas.core.environment.Environment'>, env_kwargs={}, callback=None, conns=0, log_folder=None)[source]¶ A convenience function to create simple simulations.
Method first creates environment, then instantiates agents into it with give arguments, and finally creates simulation for the environment.
Parameters: - agent_cls – class for agents, or list of classes. If list, then n_agents and agent_kwargs are expected to be lists also.
- n_agents – amount of agents for simulation, or list of amounts
- agent_kwargs – keyword arguments passed to agents at creation time, or list of keyword arguments.
- env_cls (
Environment
) – environment class for simulation - env_kwargs (dict) – keyword arguments passed to environment at creation time
- callback (callable) – optional callable to call after each simulation step
- conns – Create conns amount of initial (random) connections for agents in the simulation environment.
- log_folder (str) – folder for possible logging. This overwrites log_folder keyword argument from agent_kwargs and env_kwargs.
-
end
(folder=None)[source]¶ Close the simulation and the current simulation environment.
Deprecated since version 0.4.0: Use func:close instead.
-
finish_step
()[source]¶ Progress simulation to the end of the current step.
Deprecated since version 0.4.0: Use
step()
instead.
-
steps
(n)[source]¶ Progress simulation with given amount of steps.
Can not be called when some of the agents have not acted for the current step.
Parameters: n (int) – amount of steps to run
-
age
¶ Age of the simulation.
-
callback
¶ Callable to be called after each simulation step for any extra bookkeeping, etc.. Should accept one parameter: age that is current simulation age.
-
env
¶ Environment for the simulation.
-
name
¶ Name of the simulation.
-
order
¶ Order in which agents are run. Order is not enforced for asynchronous executions.
Possible values:
- alphabetical: agents are sorted by name
- random: agents are shuffled
Changing the order while iteration is unfinished will take place in the next iteration.
- env (
Core package holds base implementations for agents, environments, simulations and artifacts.
- core.agent - base agent implementation
- core.environment - base environment, each agent lives in an environment
- core.simulation - base simulation to run environment’s agents in iterations
- core.artifact - base artifact implementation
Multiprocessing functionality¶
This module contains multiprocessing implementation for Environment
,
MultiEnvironment
.
A MultiEnvironment
holds several Environment
slaves, which are
spawned on their own processes, and uses managers to obtain much of the same functionality as the single processor
environment. See EnvManager
and MultiEnvManager
for details.
Warning
This functionality is currently largely untested. However, it seems to work as intended and may be used in
Simulation
.
-
class
creamas.mp.
EnvManager
(environment)[source]¶ A manager for
Environment
, which is a subclass ofaiomas.subproc.Manager
.Managers are used in environments which need to be able to execute commands originating from outside sources, e.g. in slave environments inside a multiprocessing environment.
A manager can spawn other agents into its environment, and can execute other tasks relevant to the environment. The manager should always be the first agent created to the environment.
Note
You should not need to create managers directly, instead pass the desired manager class to an instance of
MultiEnvironment
at its initialization time.-
create_connections
(connection_map)[source]¶ Create connections for agents in the environment.
This is a managing function for
create_connections()
.
-
get_agents
(addr=True, agent_cls=None, as_coro=False)[source]¶ Get agents from the managed environment.
This is a managing function for the
get_agents()
. The returned agent list excludes the environment’s manager agent (i.e. this agent) by design.
-
coroutine
get_artifacts
()[source]¶ Get all artifacts from the host environment.
Returns: All the artifacts in the environment.
-
get_connections
(data=False)[source]¶ Get connections from the agents in the environment.
This is a managing function for
get_connections()
.
-
coroutine
is_ready
()[source]¶ Check if the managed environment is ready.
This is a managing function for
is_ready()
.
-
set_host_manager
(addr)[source]¶ Set host (or master) manager for this manager.
Parameters: addr – Address for the host manager.
-
coroutine
spawn_n
(agent_cls, n, *args, **kwargs)[source]¶ Spawn
n
agents to the managed environment.This is a convenience function so that one does not have to repeatedly make connections to the environment to spawn multiple agents with the same parameters.
See
aiomas.subproc.Manager.spawn()
for details.
-
coroutine
trigger_all
(*args, **kwargs)[source]¶ Trigger all agents in the managed environment to act once.
This is a managing function for
trigger_all()
.
-
-
class
creamas.mp.
MultiEnvManager
(environment)[source]¶ A manager for
MultiEnvironment
, which is a subclass ofaiomas.subproc.Manager
.A Manager can spawn other agents into its slave environments, and can execute other tasks relevant to the whole environment. The manager should always be the first (and usually only) agent created for the multi-environment’s managing environment. The actual simulation agents should be created to the slave environments, typically using multi-environment’s or its manager’s functionality.
Note
You should not need to create managers directly, instead pass the desired manager class to an instance of
MultiEnvironment
at its initialization time.-
coroutine
create_connections
(connection_map)[source]¶ Create connections for agents in the multi-environment.
This is a managing function for
create_connections()
.
-
coroutine
get_agents
(addr=True, agent_cls=None)[source]¶ Get addresses of all agents in all the slave environments.
This is a managing function for
creamas.mp.MultiEnvironment.get_agents()
.Note
Since
aiomas.rpc.Proxy
objects do not seem to handle (re)serialization,addr
andagent_cls
parameters are omitted from the call to underlying multi-environment’sget_agents()
.If
aiomas.rpc.Proxy
objects from all the agents are needed, call each slave environment manager’sget_agents()
directly.
-
coroutine
get_connections
(data=True)[source]¶ Return connections for all the agents in the slave environments.
This is a managing function for
get_connections()
.
-
coroutine
get_slave_managers
()[source]¶ Get addresses of the slave environment managers in this multi-environment.
-
handle
(msg)[source]¶ Handle message from a slave manager.
This is a dummy method which should be overridden in a subclass.
-
coroutine
is_ready
()[source]¶ A managing function for
is_ready()
.
-
coroutine
set_as_host_manager
(addr, timeout=5)[source]¶ Set the this manager as a host manager for the manager in addr.
This is a managing function for
set_host_manager()
.
-
coroutine
spawn
(agent_cls, *args, addr=None, **kwargs)[source]¶ Spawn an agent to the environment.
This is a managing function for
spawn()
.Note
Since
aiomas.rpc.Proxy
objects do not seem to handle (re)serialization, only the address of the spawned agent is returned.
-
coroutine
spawn_n
(agent_cls, n, *args, addr=None, **kwargs)[source]¶ Same as
spawn()
, but spawnn
agents with same initialization parameters.This is a managing function for
spawn_n()
.Note
Since
aiomas.rpc.Proxy
objects do not seem to handle (re)serialization, only the addresses of the spawned agents are returned.
-
coroutine
trigger_all
(*args, **kwargs)[source]¶ Trigger all agents in the managed multi-environment to act.
This is a managing function for
trigger_all()
.
-
coroutine
-
class
creamas.mp.
MultiEnvironment
(addr, env_cls, mgr_cls=None, name=None, logger=None, **env_kwargs)[source]¶ Environment for utilizing multiple processes (and cores) on a single machine.
MultiEnvironment
is not a subclass ofcreamas.core.environment.Environment
.MultiEnvironment
has a master environment, typically containing only a single manager, and a set of slave environments each having their own manager and (once spawned) the actual agents.Order of usage is:
import aiomas from creamas Environment from creamas.mp import EnvManager, MultiEnvironment from creamas.util import run # Create the multi-environment and the environment used to connect to # slave environments addr = ('localhost', 5555) env_cls = Environment env_kwargs = {'codec': aiomas.MsgPack} menv = MultiEnvironment(addr, env_cls, **env_kwargs) # Define slave environments and their arguments slave_addrs = [('localhost', 5556), ('localhost', 5557)] slave_env_cls = Environment slave_mgr_cls = EnvManager n_slaves = 2 slave_kwargs = [{'codec': aiomas.MsgPack} for _ in range(n_slaves)] # Spawn the actual slave environments menv.spawn_slaves(slave_addrs, slave_env_cls, slave_mgr_cls, slave_kwargs) # Wait that all the slaves are ready, if you need to do some other # preparation before environments' return True for is_ready, then # change check_ready=False run(menv.wait_slaves(10, check_ready=True)) # Do any additional preparations here, like spawning the agents to # slave environments. # Trigger all agents to act run(menv.trigger_all()) # Destroy the environment to free the resources menv.destroy(as_coro=False)
Parameters: - addr –
(HOST, PORT)
address for the master environment. - env_cls – Class for the master environment, used to make connections to the
slave environments. Must be a subclass of
Environment
. - mgr_cls – Class for the multi-environment’s manager.
- name (str) – Name of the environment. Will be shown in logs.
- logger – Optional. Logger for the master environment.
-
add_artifact
(artifact)[source]¶ Add an artifact to the environment.
Parameters: artifact (object) – Artifact to be added.
-
add_artifacts
(artifacts)[source]¶ Add artifacts to
artifacts
.Parameters: artifacts – list of Artifact
objects
-
check_ready
()[source]¶ Check if this multi-environment itself is ready.
Override in subclass if it needs any additional (asynchronous) initialization other than spawning its slave environments.
Return type: bool Returns: This basic implementation returns always True.
-
close
(folder=None, as_coro=False)[source]¶ Close the multiprocessing environment and its slave environments.
-
create_connections
(connection_map, as_coro=False)[source]¶ Create agent connections from the given connection map.
Parameters: - connection_map (dict) – A map of connections to be created. Dictionary where keys are
agent addresses and values are lists of (addr, data)-tuples
suitable for
add_connections()
. - as_coro (bool) – If
True
returns a coroutine, otherwise runs the asynchronous calls to the slave environment managers in the event loop.
Only the connections for the agents that are in the slave environments are created.
- connection_map (dict) – A map of connections to be created. Dictionary where keys are
agent addresses and values are lists of (addr, data)-tuples
suitable for
-
destroy
(folder=None, as_coro=False)[source]¶ Close the multiprocessing environment and its slave environments.
Deprecated since version 0.4.0: Use
close()
instead
-
get_agents
(addr=True, agent_cls=None, as_coro=False)[source]¶ Get agents from the slave environments.
Parameters: Returns: A coroutine or list of
Proxy
objects or addresses as specified by the input parameters.Slave environment managers are excluded from the returned list by default. Essentially, this method calls each slave environment manager’s
creamas.mp.EnvManager.get_agents()
asynchronously.Note
Calling each slave environment’s manager might be costly in some situations. Therefore, it is advisable to store the returned agent list if the agent sets in the slave environments are not bound to change.
-
get_artifacts
(agent_name=None)[source]¶ Get all artifacts or all artifacts published by a specific agent.
Parameters: agent_name (str) – Optional. Name of the agent which artifacts are returned. Returns: All artifacts or all artifacts published by the agent. Return type: list
-
get_connections
(data=True, as_coro=False)[source]¶ Return connections from all the agents in the slave environments.
Parameters:
-
coroutine
is_ready
()[source]¶ Check if the multi-environment has been fully initialized.
This calls each slave environment managers’
is_ready()
and checks if the multi-environment itself is ready by callingcheck_ready()
.
-
save_info
(folder, *args, **kwargs)[source]¶ Save information accumulated during the environment’s lifetime.
Called from
destroy()
. Override in subclass.Parameters: folder (str) – root folder to save information
-
coroutine
set_host_manager
(addr, timeout=5)[source]¶ Set this multi-environment’s manager as the host manager for a manager agent in addr
-
coroutine
set_host_managers
(timeout=5)[source]¶ Set the master environment’s manager as host manager for the slave environment managers.
Parameters: timeout (int) – Timeout for connecting to the slave managers. This enables the slave environment managers to communicate back to the master environment. The master environment manager,
manager
, must be an instance ofMultiEnvManager
or its subclass if this method is called.
-
coroutine
spawn
(agent_cls, *args, addr=None, **kwargs)[source]¶ Spawn a new agent in a slave environment.
Parameters: - agent_cls (str) – qualname` of the agent class.
That is, the name should be in the form
pkg.mod:cls
, e.g.creamas.core.agent:CreativeAgent
. - addr (str) – Optional. Address for the slave enviroment’s manager.
If
addr
is None, spawns the agent in the slave environment with currently smallest number of agents.
Returns: aiomas.rpc.Proxy
and address for the created agent.The
*args
and**kwargs
are passed down to the agent’s__init__()
.Note
Use
spawn_n()
to spawn large number of agents with identical initialization parameters.- agent_cls (str) – qualname` of the agent class.
That is, the name should be in the form
-
coroutine
spawn_n
(agent_cls, n, *args, addr=None, **kwargs)[source]¶ Same as
spawn()
, but allows spawning multiple agents with the same initialization parameters simultaneously into one slave environment.Parameters: - agent_cls (str) –
qualname
of the agent class. That is, the name should be in the form ofpkg.mod:cls
, e.g.creamas.core.agent:CreativeAgent
. - n (int) – Number of agents to spawn
- addr (str) – Optional. Address for the slave enviroment’s manager.
If
addr
is None, spawns the agents in the slave environment with currently smallest number of agents.
Returns: A list of (
aiomas.rpc.Proxy
, address)-tuples for the spawned agents.The
*args
and**kwargs
are passed down to each agent’s__init__()
.- agent_cls (str) –
-
coroutine
spawn_slaves
(slave_addrs, slave_env_cls, slave_mgr_cls, slave_kwargs=None)[source]¶ Spawn slave environments.
Parameters: - slave_addrs – List of (HOST, PORT) addresses for the slave-environments.
- slave_env_cls – Class for the slave environments.
- slave_kwargs – If not None, must be a list of the same size as addrs. Each item in the list containing parameter values for one slave environment.
- slave_mgr_cls – Class of the slave environment managers.
-
coroutine
stop_slaves
(timeout=1)[source]¶ Stop all the slaves by sending a stop-message to their managers.
Parameters: timeout (int) – Timeout for connecting to each manager. If a connection can not be made before the timeout expires, the resulting error for that particular manager is logged, but the stopping of other managers is not halted.
-
coroutine
trigger_act
(addr)[source]¶ Trigger agent in
addr
to act.This method is quite inefficient if used repeatedly for a large number of agents.
-
coroutine
trigger_all
(*args, **kwargs)[source]¶ Trigger all agents in all the slave environments to
act()
asynchronously.Given arguments and keyword arguments are passed down to each agent’s
creamas.core.agent.CreativeAgent.act()
.Note
By design, the manager agents in each slave environment, i.e.
manager
, are excluded from acting.
-
coroutine
wait_slaves
(timeout, check_ready=False)[source]¶ Wait until all slaves are online (their managers accept connections) or timeout expires.
Parameters: - timeout (int) – Timeout (in seconds) after which the method will return even though all the slaves are not online yet.
- check_ready (bool) – If
True
also checks if all slave environment’s are ready. A slave environment is assumed to be ready when its manager’sis_ready()
-method returnsTrue
.
-
addrs
¶ Addresses of the slave environment managers.
-
artifacts
¶ Published artifacts for all agents.
-
env
¶ The environment hosting the manager of this multi-environment.
This environment is also used without the manager to connect to the slave environment managers and communicate with them.
-
logger
¶ Logger for the environment.
Logger should have at least
log()
method which takes two arguments: a log level and a message.
-
manager
¶ This multi-environment’s master manager.
-
name
¶ The name of the environment.
- addr –
-
creamas.mp.
set_base_timeout
(timeout)[source]¶ Set base timeout (in seconds) for the rpc calls originating from the instances in this module.
-
creamas.mp.
spawn_container
(addr, env_cls=<class 'creamas.core.environment.Environment'>, mgr_cls=<class 'creamas.mp.EnvManager'>, set_seed=True, *args, **kwargs)[source]¶ Spawn a new environment in a given address as a coroutine.
Arguments and keyword arguments are passed down to the created environment at initialization time.
If setproctitle is installed, this function renames the title of the process to start with ‘creamas’ so that the process is easily identifiable, e.g. with
ps -x | grep creamas
.
-
creamas.mp.
spawn_containers
(addrs, env_cls=<class 'creamas.core.environment.Environment'>, env_params=None, mgr_cls=<class 'creamas.mp.EnvManager'>, *args, **kwargs)[source]¶ Spawn environments in a multiprocessing
multiprocessing.Pool
.Arguments and keyword arguments are passed down to the created environments at initialization time if env_params is None. If env_params is not None, then it is assumed to contain individual initialization parameters for each environment in addrs.
Parameters: - addrs – List of (HOST, PORT) addresses for the environments.
- env_cls – Callable for the environments. Must be a subclass of
Environment
. - env_params (Iterable of same length as addrs or None.) – Initialization parameters for the environments.
- mgr_cls – Callable for the managers. Must be a subclass of
EnvManager
.s
Returns: The created process pool and the ApplyAsync results for the spawned environments.
-
coroutine
creamas.mp.
start
(addr, env_cls, mgr_cls, *env_args, **env_kwargs)[source]¶ Coroutine that starts an environment with
mgr_cls
manager agent.The agent will connect to addr
('host', port)
and wait for commands to spawn new agents within its environment.The env_args and env_kwargs will be passed to
env_cls.create()
factory function.This coroutine finishes after manager’s
stop()
was called or when aKeyboardInterrupt
is raised and callsenv_cls.destroy()
before it finishes.Parameters: - addr – (HOST, PORT) for the new environment
- env_cls – Class of the environment, subclass of
Environment
. - mgr_cls – Class of the manager agent, subclass of
EnvManager
.
Distributed Systems¶
The module holds a base implementation, DistributedEnvironment
,
for simulations and environments where the resources span over multiple nodes
on computing clusters or other distributed systems.
Note
The module needs asyncssh to function. Asyncssh is not installed as a dependency by default.
-
class
creamas.ds.
DistributedEnvironment
(addr, env_cls, nodes, mgr_cls=None, name=None, logger=None, **env_kwargs)[source]¶ Distributed environment which manages several nodes containing multi-environments, a subclass of
MultiEnvironment
.This environment spawns its slave multi-environments on the different servers (nodes) using SSH-connections. The spawning process assumes that the user can make a SSH-connection without login credentials to each node. The environment can then be used to wait until all the nodes are ready (have done their individual initialization) and do optional additional preparing of the nodes (e.g. adding inter-node connections between agents).
After all the nodes are ready and prepared, the environment can be used to run an iterative (asynchronous) simulation using
DistributedEnvironment.trigger_all()
which callstrigger_all()
for each node’s manager.Warning
To free the resources on each node, it is crucial to call
creamas.ds.DistributedEnvironment.close()
after the simulation is finished. Otherwise, some rogue processes are likely to be left unattended on the external nodes.The intended order of usage is as follows:
ds = DistributedEnvironment(*args, **kwargs) run(ds.spawn_nodes(spawn_cmd)) timeout = 30 loop = asyncio.get_event_loop() task = ds.wait_nodes(timeout, check_ready=True) nodes_ready = loop.run_until_complete(task) if nodes_ready: # All nodes are ready so we can do additional preparation loop.run_until_complete(ds.prepare_nodes()) # Run the simulation for i in range(10): loop.run_until_complete(ds.trigger_all()) # Destroy the simulation afterwards to free the resources on each node. ds.close()
Parameters: - addr –
(HOST, PORT)
address for the env property (this node). Thehost
should not be present in nodes. - env_cls – Class for the master environment, used to make connections to the
slave environments. Must be a subclass of
Environment
. - mgr_cls – Class for the master environment’s manager.
- name (str) – Name of the environment. Will be shown in logs.
- nodes – List of (server, port)-tuples which are used to make host the slave
multi-environments. The port is the port for the SSH connection,
default SSH-port is 22. See also
spawn_slaves()
. - logger – Optional logger for this simulation.
-
add_artifact
(artifact)¶ Add an artifact to the environment.
Parameters: artifact (object) – Artifact to be added.
-
add_artifacts
(artifacts)¶ Add artifacts to
artifacts
.Parameters: artifacts – list of Artifact
objects
-
check_ready
()¶ Check if this multi-environment itself is ready.
Override in subclass if it needs any additional (asynchronous) initialization other than spawning its slave environments.
Return type: bool Returns: This basic implementation returns always True.
-
close
(folder=None, as_coro=False)¶ Close the multiprocessing environment and its slave environments.
-
coroutine
connect
(*args, **kwargs)¶ A shortcut to
env.connect()
.
-
create_connections
(connection_map, as_coro=False)¶ Create agent connections from the given connection map.
Parameters: - connection_map (dict) – A map of connections to be created. Dictionary where keys are
agent addresses and values are lists of (addr, data)-tuples
suitable for
add_connections()
. - as_coro (bool) – If
True
returns a coroutine, otherwise runs the asynchronous calls to the slave environment managers in the event loop.
Only the connections for the agents that are in the slave environments are created.
- connection_map (dict) – A map of connections to be created. Dictionary where keys are
agent addresses and values are lists of (addr, data)-tuples
suitable for
-
destroy
(folder=None, as_coro=False)¶ Close the multiprocessing environment and its slave environments.
Deprecated since version 0.4.0: Use
close()
instead
-
get_agents
(addr=True, agent_cls=None, as_coro=False)¶ Get agents from the slave environments.
Parameters: Returns: A coroutine or list of
Proxy
objects or addresses as specified by the input parameters.Slave environment managers are excluded from the returned list by default. Essentially, this method calls each slave environment manager’s
creamas.mp.EnvManager.get_agents()
asynchronously.Note
Calling each slave environment’s manager might be costly in some situations. Therefore, it is advisable to store the returned agent list if the agent sets in the slave environments are not bound to change.
-
get_artifacts
(agent_name=None)¶ Get all artifacts or all artifacts published by a specific agent.
Parameters: agent_name (str) – Optional. Name of the agent which artifacts are returned. Returns: All artifacts or all artifacts published by the agent. Return type: list
-
get_connections
(data=True, as_coro=False)¶ Return connections from all the agents in the slave environments.
Parameters:
-
get_slave_managers
(as_coro=False)[source]¶ Return all slave environment manager addresses.
Parameters: as_coro (bool) – If True
returns awaitable coroutine, otherwise runs the calls to the slave managers asynchronously in the event loop.This method returns the addresses of the true slave environment managers, i.e. managers derived from
EnvManager
, not multi-environment managers. For example, if this node environment has two nodes with four slave environments in each, then this method returns 8 addresses.
-
coroutine
is_ready
()¶ Check if the multi-environment has been fully initialized.
This calls each slave environment managers’
is_ready()
and checks if the multi-environment itself is ready by callingcheck_ready()
.
-
coroutine
prepare_nodes
(*args, **kwargs)[source]¶ Prepare nodes (and slave environments and agents) so that they are ready. Should be called after
wait_nodes()
.Note
Override in the subclass for the intended functionality.
-
save_info
(folder, *args, **kwargs)¶ Save information accumulated during the environment’s lifetime.
Called from
destroy()
. Override in subclass.Parameters: folder (str) – root folder to save information
-
coroutine
set_host_manager
(addr, timeout=5)¶ Set this multi-environment’s manager as the host manager for a manager agent in addr
-
coroutine
set_host_managers
(timeout=5)¶ Set the master environment’s manager as host manager for the slave environment managers.
Parameters: timeout (int) – Timeout for connecting to the slave managers. This enables the slave environment managers to communicate back to the master environment. The master environment manager,
manager
, must be an instance ofMultiEnvManager
or its subclass if this method is called.
-
coroutine
spawn
(agent_cls, *args, addr=None, **kwargs)¶ Spawn a new agent in a slave environment.
Parameters: - agent_cls (str) – qualname` of the agent class.
That is, the name should be in the form
pkg.mod:cls
, e.g.creamas.core.agent:CreativeAgent
. - addr (str) – Optional. Address for the slave enviroment’s manager.
If
addr
is None, spawns the agent in the slave environment with currently smallest number of agents.
Returns: aiomas.rpc.Proxy
and address for the created agent.The
*args
and**kwargs
are passed down to the agent’s__init__()
.Note
Use
spawn_n()
to spawn large number of agents with identical initialization parameters.- agent_cls (str) – qualname` of the agent class.
That is, the name should be in the form
-
coroutine
spawn_n
(agent_cls, n, *args, addr=None, **kwargs)¶ Same as
spawn()
, but allows spawning multiple agents with the same initialization parameters simultaneously into one slave environment.Parameters: - agent_cls (str) –
qualname
of the agent class. That is, the name should be in the form ofpkg.mod:cls
, e.g.creamas.core.agent:CreativeAgent
. - n (int) – Number of agents to spawn
- addr (str) – Optional. Address for the slave enviroment’s manager.
If
addr
is None, spawns the agents in the slave environment with currently smallest number of agents.
Returns: A list of (
aiomas.rpc.Proxy
, address)-tuples for the spawned agents.The
*args
and**kwargs
are passed down to each agent’s__init__()
.- agent_cls (str) –
-
coroutine
spawn_nodes
(spawn_cmd, ports=None, **ssh_kwargs)[source]¶ An alias for
creamas.ds.DistributedEnvironment.spawn_slaves()
.
-
coroutine
spawn_slaves
(spawn_cmd, ports=None, **ssh_kwargs)[source]¶ Spawn multi-environments on the nodes through SSH-connections.
Parameters: - spawn_cmd – str or list, command(s) used to spawn the environment on each node.
If list, it must contain one command for each node in
nodes
. If str, the same command is used for each node. - ports – Optional. If not
None
, must be a mapping from nodes ((server, port)
-tuples) to ports which are used for the spawned multi-environments’ master manager environments. IfNone
, then the same port is used to derive the master manager addresses as was used to initialize this distributed environment’s managing environment (port inaddr
). - ssh_kwargs – Any additional SSH-connection arguments, as specified by
asyncssh.connect()
. See asyncssh documentation for details.
Nodes are spawned by creating a multiprocessing pool where each node has its own subprocess. These subprocesses then use SSH-connections to spawn the multi-environments on the nodes. The SSH-connections in the pool are kept alive until the nodes are stopped, i.e. this distributed environment is destroyed.
- spawn_cmd – str or list, command(s) used to spawn the environment on each node.
If list, it must contain one command for each node in
-
coroutine
stop_slaves
(timeout=1)¶ Stop all the slaves by sending a stop-message to their managers.
Parameters: timeout (int) – Timeout for connecting to each manager. If a connection can not be made before the timeout expires, the resulting error for that particular manager is logged, but the stopping of other managers is not halted.
-
coroutine
trigger_act
(addr)¶ Trigger agent in
addr
to act.This method is quite inefficient if used repeatedly for a large number of agents.
-
coroutine
trigger_all
(*args, **kwargs)¶ Trigger all agents in all the slave environments to
act()
asynchronously.Given arguments and keyword arguments are passed down to each agent’s
creamas.core.agent.CreativeAgent.act()
.Note
By design, the manager agents in each slave environment, i.e.
manager
, are excluded from acting.
-
coroutine
wait_nodes
(timeout, check_ready=True)[source]¶ Wait until all nodes are online (their managers accept connections) or timeout expires. Should be called after
spawn_nodes()
.This is an alias for
wait_slaves()
.
-
coroutine
wait_slaves
(timeout, check_ready=False)¶ Wait until all slaves are online (their managers accept connections) or timeout expires.
Parameters: - timeout (int) – Timeout (in seconds) after which the method will return even though all the slaves are not online yet.
- check_ready (bool) – If
True
also checks if all slave environment’s are ready. A slave environment is assumed to be ready when its manager’sis_ready()
-method returnsTrue
.
-
addrs
¶ Addresses of the node managers.
These addresses are derived from nodes and ports parameters given in
spawn_slaves()
, and are used to communicate tasks (trigger agents) to the nodes. Each manager is assumed to be the first agent in its own managed environment.
-
artifacts
¶ Published artifacts for all agents.
-
env
¶ The environment hosting the manager of this multi-environment.
This environment is also used without the manager to connect to the slave environment managers and communicate with them.
-
logger
¶ Logger for the environment.
Logger should have at least
log()
method which takes two arguments: a log level and a message.
-
manager
¶ This multi-environment’s master manager.
-
name
¶ The name of the environment.
-
nodes
¶ Environment nodes (excluding the current host).
Altering the nodes after the initialization most probably causes unexpected behavior.
- addr –
-
coroutine
creamas.ds.
run_node
(menv, log_folder)[source]¶ Run
MultiEnvironment
until its manager’sstop()
is called.Parameters: - menv –
MultiEnvironment
to wait for. - log_folder (str) – Logging folder to be passed down to
destroy()
afterstop()
is called.
This method will block the current thread until the manager’s
stop()
is called. After the stop-message is received, multi-environment is destroyed.The method is intended to be used in
DistributedEnvironment
scripts which spawn multi-environments on different nodes. That is, using this function in the script will block the script’s further execution until the simulation has run its course and the nodes need to be closed. Callingclose()
will automatically call each node manager’sstop()
and therefore release the script.- menv –
-
coroutine
creamas.ds.
ssh_exec
(server, cmd, timeout=10, **ssh_kwargs)[source]¶ Execute a command on a given server using asynchronous SSH-connection.
The connection to the server is wrapped in
asyncio.wait_for()
and giventimeout
is applied to it. If the server is not reachable before timeout expires,asyncio.TimeoutError
is raised.Parameters: - server (str) – Address of the server
- cmd (str) – Command to be executed
- timeout (int) – Timeout to connect to server.
- ssh_kwargs –
Any additional SSH-connection arguments, as specified by
asyncssh.connect()
. See asyncssh documentation for details.
Returns: closed SSH-connection
-
creamas.ds.
ssh_exec_in_new_loop
(server, cmd, timeout=10, **ssh_kwargs)[source]¶ Same as
ssh_exec()
but creates a new event loop and executesssh_exec()
in that event loop.
2D Grid Environment¶
The module holds implementations for 2D-grid environments where the agents know their neighbors in each of the four cardinal directions. Currently implemented are:
GridAgent
: The base grid agent implementation, should be used insideGridEnvironment
. Agent places itself in the environment’sgrid
and knows its neighbors in the cardinal directions: N, E, S, W. Subclass the agent for your specific needs.GridEnvironment
: A single process grid environment.GridEnvManager
: A manager for a single process grid environment.GridMultiEnvironment
: Multi-processing environment holding severalGridEnvironment
slaves with managers.GridMultiEnvManager
: A manager for a multi-processing environment. Used especially if the environment needs to be able to execute commands from external sources, e.g. when used as a part ofcreamas.ds.DistributedEnvironment
.
-
class
creamas.grid.
GridAgent
(*args, **kwargs)[source]¶ An agent living in a 2D-grid with four neighbors in cardinal directions.
The agent assumes that its environment is derived from
GridEnvironment
, and places itself into the grid when it is initialized.-
coroutine
rcv
(msg)[source]¶ Receive and handle message coming from another agent.
This method is called from
send()
.The base implementation does nothing, override in a subclass.
-
coroutine
send
(card, msg)[source]¶ Send message msg to the neighboring agent in the card cardinal direction.
Parameters: - card (str) – ‘N’, ‘E’, ‘S’, or ‘W’.
- msg – Message to the agent.
Returns: Response from the agent
The method calls the neighboring agent’s
rcv()
with the message and returns any value returned by that agent.This method will fail silently if there is no neighbor agent in the given cardinal direction.
-
neighbors
¶ Map of neighboring agent addresses in cardinal directions: N, E, S, W.
-
xy
¶ Agent’s place (coordinate) in the grid, (x, y)-tuple.
-
coroutine
-
class
creamas.grid.
GridEnvManager
(environment)[source]¶ Manager for
GridEnvironment
.-
get_xy_address
(xy)[source]¶ Get address of the agent in xy coordinate, or None if no such agent exists.
-
coroutine
set_agent_neighbors
()[source]¶ Set neighboring agents for all the agents in the grid.
If the managed grid contains neighboring grids, uses those to correctly set also neighboring agents for agents on the edge of the grid.
This function assumes that:
- Grid is full, i.e. it has maximum number of agents.
- All the (possible) neighboring grids have been initialized and
have the maximum number of agents. That is, if managed grid’s
neighbor map still points to
None
, this grid is assumed to be in the edge of the super-grid containing multipleGridEnvironment
instances.
-
-
class
creamas.grid.
GridEnvironment
(base_url, loop, clock, connect_kwargs)[source]¶ Environment where agents reside in a 2D-grid.
Each agent is connected to neighbors in cardinal directions: N, E, S, W. Grid environments can be horizontally stacked with
GridMultiEnvironment
.-
add_to_grid
(agent)[source]¶ Add agent to the next available spot in the grid.
Returns: (x,y) of the agent in the grid. This is the agent’s overall coordinate in the grand grid (i.e. the actual coordinate of the agent w.t.r origin). Raises: ValueError if the grid is full.
-
get_xy
(xy, addr=True)[source]¶ Get the agent with xy-coordinate in the grid. If addr is True, returns only the agent’s address.
If no such agent in the grid, returns None.
Raises: ValueError
if xy-coordinate is outside the environment’s grid.
-
is_full
()[source]¶ GridEnvironment
is full when its grid is fully populated with agents.Returns: True if the grid is full, False otherwise. Will also return False for uninitialized grids with (0,0) grid size.
-
is_ready
()[source]¶ Grid environment is ready when its grid is full.
See also
GridEnvironment.is_full()
,Environment.is_ready()
-
coroutine
set_agent_neighbors
()[source]¶ Set neighbors for each agent in each cardinal direction.
This method assumes that the neighboring
GridEnvironment
of this grid environment have already been set.
-
grid
¶ The agents in the grid. 2D-list with the same size as gs.
-
gs
¶ Size of the grid as a 2-tuple. Changing the size of the grid after spawning any agents in the environment will clear the grid, but does not remove the agents from the environment.
-
neighbors
¶ Map of neighboring grid environments in cardinal directions.
Acceptable keys: N, E, S, W.
The values are the addresses of the managers in the neighboring grid environments.
-
origin
¶ Upper left corner of the grid, [0,0] by default.
You should define the origin before spawning any agents into the environment.
-
-
class
creamas.grid.
GridMultiEnvManager
(environment)[source]¶ Manager agent for
GridMultiEnvironment
.-
coroutine
get_xy_address
(xy)[source]¶ Get address of the agent in the environment with given coordinates.
This is a managing function for
get_xy_address()
.
-
get_xy_environment
(xy)[source]¶ Get environment (address of the manager of that environment) which has agent with given coordinates.
This is a managing function for
get_xy_environment()
.
-
coroutine
set_agent_neighbors
()[source]¶ Set neighbor agents for all the agents in the slave environments.
This is a managing function for
creamas.grid.GridMultiEnvironment.set_agent_neighbors()
.
-
set_grid_neighbor
(card, addr)[source]¶ Set the neighbor multi-grid for this multi-grid in card cardinal direction. The addr should point to the manager of the neighboring multi-grid.
-
coroutine
set_gs
(mgr_addr, gs)[source]¶ Set grid size for
GridEnvironment
which manager is in given address.Parameters: - mgr_addr (str) – Address of the manager agent
- gs – New grid size of the grid environment, iterable with length 2.
-
coroutine
set_neighbors
()[source]¶ Set neighbors for all the agents in all the slave environments.
This is a managing function for
creamas.grid.GridMultiEnvironment.set_neighbors()
.
-
coroutine
set_origin
(mgr_addr, origin)[source]¶ Set originating coordinates for
GridEnvironment
which manager is in given address.Parameters: - mgr_addr (str) – Address of the manager agent
- origin – New origin of the grid environment, iterable with length 2.
-
coroutine
set_slave_neighbors
()[source]¶ Set neighbor environments for all the slave environments.
This is a managing function for
creamas.grid.GridMultiEnvironment.set_slave_neighbors()
.
-
coroutine
-
class
creamas.grid.
GridMultiEnvironment
(*args, **kwargs)[source]¶ Multi-environment which stacks its slave
GridEnvironment
instances horizontally.Call
creamas.grid.GridMultiEnvironment.set_slave_params()
immediately after initializingGridMultiEnvironment
!Note
The manager agents for the slave environments will not be part of
grid
in the slave environments.-
coroutine
get_xy_address
(xy)[source]¶ Get address of the agent residing in xy coordinate, or
None
if no such agent is in this multi-environment.
-
get_xy_environment
(xy)[source]¶ Get manager address for the environment which should have the agent with given xy coordinate, or None if no such environment is in this multi-environment.
-
coroutine
populate
(agent_cls, *args, **kwargs)[source]¶ Populate all the slave grid environments with agents. Assumes that no agents have been spawned yet to the slave environment grids. This excludes the slave environment managers as they are not in the grids.)
-
coroutine
set_agent_neighbors
()[source]¶ Set neighbors for all the agents in all the slave environments. Assumes that all the slave environments have their neighbors set.
-
coroutine
set_neighbors
()[source]¶ Set neighbors for all slave environments and agents in them.
This is a convenience function for calling
set_slave_neighbors()
andset_agent_neighbors()
.
-
coroutine
set_slave_neighbors
()[source]¶ Set neighbor environments for all the slave environments. Assumes that
neighbors
are set for this multi-environment.
-
coroutine
set_slave_params
()[source]¶ Set origin and grid size for each slave environment.
This method needs to be called before slave environments are populated and agents’ and slave environments’ neighbors are set.
-
coroutine
spawn_slaves
(*args, **kwargs)[source]¶ Spawn slave environments.
Parameters: - slave_addrs – List of (HOST, PORT) addresses for the slave-environments.
- slave_env_cls – Class for the slave environments.
- slave_kwargs – If not None, must be a list of the same size as addrs. Each item in the list containing parameter values for one slave environment.
- slave_mgr_cls – Class of the slave environment managers.
-
gs
¶ Grid size for each slave environment.
-
neighbors
¶ Map of neighboring multi-environments for this multi-environment. The map’s values are manager addresses for the neighbors.
-
origin
¶ Origin of this multi-environment (the xy coordinate of the first agent in the first slave environment).
-
coroutine
NetworkX Integration¶
Functions to create agent connections from NetworkX graph structures and NetworkX graphs from agent connections.
Note
NetworkX has to be installed in order for the functions in this module to work. It is not installed as a default dependency.
Use, e.g. pip install networkx
-
creamas.nx.
connections_from_graph
(env, G, edge_data=False)[source]¶ Create connections for agents in the given environment from the given NetworkX graph structure.
Parameters: - env – Environment where the agents live. The environment should be derived
from
Environment
,MultiEnvironment
orDistributedEnvironment
. - G – NetworkX graph structure, either
networkx.graph.Graph
ornetworkx.digraph.DiGraph
. The graph needs to have the same number of nodes as the environment has agents (excluding the managers). - edge_data (bool) – If
True
, edge data from the given graph is copied to the agents’connections
.
Note
By design, manager agents are excluded from the connections and should not be counted towards environment’s agent count.
The created connections are stored in each agent’s
connections
and the possible edge data is stored as key-value pairs in the connection dictionary.The agents are sorted by their environments’ hosts and ports before each agent is mapped to a node in G. This should cause some network generation methods in NetworkX, e.g.
connected_watts_strogatz_graph()
, to create more connections between agents in the same environment and/or node when usingMultiEnvironment
orDistributedEnvironment
.- env – Environment where the agents live. The environment should be derived
from
-
creamas.nx.
graph_from_connections
(env, directed=False)[source]¶ Create NetworkX graph from agent connections in a given environment.
Parameters: - env – Environment where the agents live. The environment must be derived from
Environment
,MultiEnvironment
orDistributedEnvironment
. - directed (bool) – If
True
, creates an instance ofDiGraph
, otherwise creates an instance ofGraph
.
Returns: The created NetworkX graph.
Return type: DiGraph
orGraph
Note
If the created graph is undirected and two connected agents have different data stored for each other, then the data for the given edge is chosen randomly between the two agents.
- env – Environment where the agents live. The environment must be derived from
Voting for Agents¶
Implementations for voting and other social choice behaviors.
Holds basic implementations for:
VoteAgent
: An agent implementing functions needed for votingVoteEnvironment
: An environment holding basic voting functionality.VoteManager
: A manager agent for instances ofVoteEnvironment
when they are slave environments in multi- or distributed environments.VoteOrganizer
: A class which can initiate voting and compute its results.
It should be noted that only the “true” slave environments, i.e. environments
derived from Environment
need to have voting behavior implemented
(and appropriate voting managers). VoteOrganizer
communicates with the “true” slave environments directly without the need of
the middle layer environments (multi-environments) or managers in the case of
distributed systems.
-
class
creamas.vote.
VoteAgent
(environment, resources=0, name=None, log_folder=None, log_level=10)[source]¶ An agent with voting behavior.
Implements three functions needed for voting:
validate()
: Validates a set of candidates returning only the validated candidates.vote()
: Votes from a set of candidates, i.e. orders them by preference. Returns the ordered list and preferences. Basic implementation orders the candidates using the agent’sevaluate()
function.add_candidate()
: Add candidate artifact to the agent’s environment’s list of current candidates.
-
validate
(candidates)[source]¶ Validate a list of candidate artifacts.
Candidate validation should prune unwanted artifacts from the overall candidate set. Agent can use its own reasoning to validate the given candidates. The method should return a subset of the given candidates list containing the validated artifacts (i.e. the artifacts that are not pruned).
Note
This basic implementation returns the given candidate list as is. Override this function in the subclass for the appropriate validation procedure.
Parameters: candidates – A list of candidate artifacts Returns: The validated artifacts, a subset of given candidates
-
vote
(candidates)[source]¶ Rank artifact candidates.
The voting is needed for the agents living in societies using social decision making. The function should return a sorted list of (candidate, evaluation)-tuples. Depending on the social choice function used, the evaluation might be omitted from the actual decision making, or only a number of (the highest ranking) candidates may be used.
This basic implementation ranks candidates based on
evaluate()
.Parameters: candidates – list of Artifact
objects to be rankedReturns: Ordered list of (candidate, evaluation)-tuples
-
class
creamas.vote.
VoteEnvironment
(*args, **kwargs)[source]¶ An environment implementing functionality needed for voting.
Voting depends largely on a list of
candidate
artifacts, which are passed down to agents at the time of voting. Candidate artifacts can also be validated by the agents (only returning artifacts that are deemed appropriate, or good enough, by all agents in the environment).-
gather_votes
(candidates)[source]¶ Gather votes for the given candidates from the agents in the environment.
Returned votes are anonymous, i.e. they cannot be tracked to any individual agent afterwards.
Returns: A list of votes. Each vote is a list of (artifact, preference)
-tuples sorted in a preference order of a single agent.
-
validate_candidates
(candidates)[source]¶ Validate the candidate artifacts with the agents in the environment.
In larger societies this method might be costly, as it calls each agents’
validate()
.Returns: A list of candidates that are validated by all agents in the environment.
-
candidates
¶ Current artifact candidates, subject to e.g. agents voting to determine which candidate(s) are added to
artifacts
.
-
-
class
creamas.vote.
VoteManager
(environment)[source]¶ Manager agent for voting environments.
The class is designed be used in conjunction with
VoteEnvironment
in multiprocessing and distributed settings.-
clear_candidates
()[source]¶ Clear candidates in the managed environment.
This is a managing function for
clear_candidates()
.
-
coroutine
gather_votes
(candidates)[source]¶ Gather votes for the given candidates from the agents in the managed environment.
This is a managing function for
gather_votes()
.
-
coroutine
validate_candidates
(candidates)[source]¶ Validate the candidates with the agents in the managed environment.
This is a managing function for
validate_candidates()
.
-
-
class
creamas.vote.
VoteOrganizer
(environment, logger=None)[source]¶ A class which organizes voting behavior in an environment.
The organizer can
gather_candidates()
from the environment, and thengather_votes()
for the candidates. Optionally the organizer mayvalidate_candidates()
before gathering the votes. After the votes have been collected the organizer maycompute_results()
of the votes with a given voting method.The organizer also has
gather_and_vote()
to do all of the above in one go.-
clear_candidates
(clear_env=True)[source]¶ Clear the current candidates.
Parameters: clear_env (bool) – If True
, clears also environment’s (or its underlying slave environments’) candidates.
-
compute_results
(voting_method, votes=None, winners=1, **kwargs)[source]¶ Compute voting results to decide the winner(s) from the
votes
.The votes should have been made for the current
candidates
.Parameters: - voting_method – A function which computes the results from the votes. Should
accept at least three parameters: candidates, votes and number of
vote winners. The function should return at least a list of vote
winners. See, e.g.
vote_mean()
orvote_best()
. Additional**kwargs
are passed down to the voting method. - votes (list) – A list of votes by which the voting is performed. Each vote should
have the same set of artifacts in them. If
None
the results are computed for the current list ofvotes
. - winners (int) – The number of vote winners
Returns: list of
Artifact
objects, the winning artifacts. Some voting methods may also return a score associated with each winning artifact.Return type: - voting_method – A function which computes the results from the votes. Should
accept at least three parameters: candidates, votes and number of
vote winners. The function should return at least a list of vote
winners. See, e.g.
-
gather_and_vote
(voting_method, validate=False, winners=1, **kwargs)[source]¶ Convenience function to gathering candidates and votes and performing voting using them.
Additional
**kwargs
are passed down to voting method.Parameters: - voting_method – The voting method to use, see
compute_results()
for details. - validate (bool) – Validate gathered candidates before voting.
- winners (int) – The number of vote winners
Returns: Winner(s) of the vote.
- voting_method – The voting method to use, see
-
gather_candidates
()[source]¶ Gather candidates from the slave environments.
The candidates are stored in
candidates
, overriding any previous candidates.
-
gather_votes
()[source]¶ Gather votes from all the underlying slave environments for the current list of candidates.
The votes are stored in
votes
, overriding any previous votes.
-
validate_candidates
()[source]¶ Validate current candidates.
This method validates the current candidate list in all the agents in the environment (or underlying slave environments) and replaces the current
candidates
with the list of validated candidates.The artifact candidates must be hashable and have a
__eq__()
implemented for validation to work on multi-environments and distributed environments.
-
candidates
¶ Current list of candidates gathered from the environment.
-
env
¶ The environment associated with this voting organizer.
-
votes
¶ Current list of votes gathered from the environment.
-
-
creamas.vote.
vote_IRV
(candidates, votes, n_winners)[source]¶ Perform IRV voting based on votes.
Ties are resolved randomly.
Parameters: - candidates – All candidates in the vote
- votes – Votes from the agents
- n_winners (int) – The number of vote winners
-
creamas.vote.
vote_best
(candidates, votes, n_winners)[source]¶ Select the artifact with the single best evaluation as the winner of the vote.
Ties are resolved randomly.
Parameters: - candidates – All candidates in the vote
- votes – Votes from the agents
- n_winners (int) – The number of vote winners
-
creamas.vote.
vote_least_worst
(candidates, votes, n_winners)[source]¶ Select “least worst” artifact as the winner of the vote.
Least worst artifact is the artifact with the best worst evaluation, i.e. its worst evaluation is the best among all of the artifacts.
Ties are resolved randomly.
Parameters: - candidates – All candidates in the vote
- votes – Votes from the agents
- n_winners (int) – The number of vote winners
-
creamas.vote.
vote_mean
(candidates, votes, n_winners)[source]¶ Perform mean voting based on votes.
Mean voting computes the mean preference for each of the artifact candidates from the votes and sorts the candidates in the mean preference order.
Ties are resolved randomly.
Parameters: - candidates – All candidates in the vote
- votes – Votes from the agents
- n_winners (int) – The number of vote winners
Assorted Utility Functions¶
Miscellaneous utility functions.
-
creamas.util.
create_tasks
(task_coro, addrs, *args, flatten=True, **kwargs)[source]¶ Create and schedule a set of asynchronous tasks.
The function creates the tasks using a given list of agent addresses and wraps each of them in
asyncio.ensure_future()
. The*args
and**kwargs
are passed down totask_coro()
when creating tasks for each address inaddrs
.Usage example for a method in a class derived from
MultiEnvironment
:async def my_method(self, *args, **kwargs): async def task(addr, *args, **kwargs): r_manager = await self.env.connect(addr) return await r_manager.my_method(*args, **kwargs) return await util.create_tasks(task, self.addrs, *args, **kwargs)
Parameters: - task_coro – Coroutine which is used for each address in
addrs
. The coroutine should accept an agent address as the first parameter. - addrs (list) – A list of agent addresses used as the first parameters of
task_coro()
. - flatten (bool) – If
True
the returned results are flattened into one list if the tasks return iterable objects. The parameter does nothing if all the results are not iterable.
Returns: An awaitable coroutine which returns the results of tasks as a list or as a flattened list
- task_coro – Coroutine which is used for each address in
-
creamas.util.
get_manager
(addr)[source]¶ Get assumed environment manager’s address for a given agent address.
-
creamas.util.
run
(task=None, loop=None)[source]¶ Run the event loop forever or until the task/future task is finished.
Parameters: - task – Optional. Task or Future which is run until complete. If parameter is
None
runs the event loop forever. - loop – Optional. Event loop to use. If the parameter is
None
uses asyncio’s base event loop.
Note
This method has the same intent as
aiomas.util.run()
.- task – Optional. Task or Future which is run until complete. If parameter is
-
creamas.util.
run_or_coro
(task, as_coro, loop=None)[source]¶ A shorthand to run the task/future or return it as is.
Parameters: - task – Optional. Task or Future which is run until complete. If parameter is
None
runs the event loop forever. - as_coro (bool) – If
True
returns the given task as is, otherwise runs it in the event loop. - loop – Optional. Event loop to use. If the parameter is
None
uses asyncio’s base event loop.
- task – Optional. Task or Future which is run until complete. If parameter is
-
creamas.util.
sort_addrs
(addrs)[source]¶ Return agent addresses in a sorted order.
- Agent addresses are sorted with following hierarchical criteria:
- by the host of an agent’s environment
- by the port (interpreted as an integer) of an agent’s environment
- by the order in which the agents were created in their environment
For example, the following list of addresses:
['tcp://bnode:5555/0', 'tcp://anode:5555/0', 'tcp://anode:50/1', 'tcp://anode:5555/2', 'tcp://anode:50/2', 'tcp://anode:18000/0', 'tcp://bnode:50/0', 'tcp://bnode:18000/0', 'tcp://anode:18000/1', 'tcp://anode:18000/2', 'tcp://bnode:50/1', 'tcp://bnode:5555/2', 'tcp://bnode:5555/1', 'tcp://bnode:50/2', 'tcp://bnode:18000/2', 'tcp://anode:50/0', 'tcp://bnode:18000/1', 'tcp://anode:5555/1']
would be sorted into the following order:
['tcp://anode:50/0', 'tcp://anode:50/1', 'tcp://anode:50/2', 'tcp://anode:5555/0', 'tcp://anode:5555/1', 'tcp://anode:5555/2', 'tcp://anode:18000/0', 'tcp://anode:18000/1', 'tcp://anode:18000/2', 'tcp://bnode:50/0', 'tcp://bnode:50/1', 'tcp://bnode:50/2', 'tcp://bnode:5555/0', 'tcp://bnode:5555/1', 'tcp://bnode:5555/2', 'tcp://bnode:18000/0', 'tcp://bnode:18000/1', 'tcp://bnode:18000/2']
Parameters: addrs (list) – List of addresses to be sorted. Returns: List of addresses in a sorted order.
-
creamas.util.
split_addrs
(addrs)[source]¶ Split addresses into dictionaries by hosts and ports.
Parameters: addrs (list) – A list of addresses. Returns: A dictionary of dictionaries, where dict[HOST][PORT]
holds a list of all agent addresses in that environment.
-
coroutine
creamas.util.
wait_tasks
(tasks, flatten=True)[source]¶ Gather a list of asynchronous tasks and wait their completion.
Parameters: - tasks (list) – A list of asyncio tasks wrapped in
asyncio.ensure_future()
. - flatten (bool) – If
True
the returned results are flattened into one list if the tasks return iterable objects. The parameter does nothing if all the results are not iterable.
Returns: The results of tasks as a list or as a flattened list
- tasks (list) – A list of asyncio tasks wrapped in
Rules¶
Rule Agent¶
The module holding RuleAgent
, an agent which evaluates artifacts using
its rules.
-
class
creamas.rules.agent.
RuleAgent
(*args, **kwargs)[source]¶ Base class for agents using rules to evaluate artifacts.
In addition to common attributes inherited from
CreativeAgent
, rule agents have following attributes:Variables: -
add_rule
(rule, weight)[source]¶ Add rule to
R
with initial weight.Parameters: - rule (~creamas.core.rule.Rule) – rule to be added
- weight (float) – initial weight for the rule
Raises: TypeError – if rule is not subclass of
Rule
Returns: True
if rule was successfully added, otherwiseFalse
.Rtype bool:
-
evaluate
(artifact)[source]¶ Evaluate artifact with agent’s current rules and weights.
Parameters: artifact ( Artifact
) –Artifact
to be evaluatedReturns: Agent’s evaluation of the artifact, in [-1,1], and framing. In this basic implementation framing is always None
.Return type: tuple Actual evaluation formula in this basic implementation is:
\[e(A) = \frac{\sum_{i=1}^{n} r_{i}(A)w_i} {\sum_{i=1}^{n} \lvert w_i \rvert},\]where \(r_{i}(A)\) is the \(i\) th rule’s evaluation on artifact \(A\), and \(w_i\) is the weight for rule \(r_i\).
-
remove_rule
(rule)[source]¶ Remove rule from
R
and its corresponding weight fromW
.Parameters: rule ( Rule
orRuleLeaf
) – rule to removeRaises: TypeError – If rule is not derived from Rule
orRuleLeaf
.Returns: True
if the rule was successfully removed, otherwiseFalse
.Rtype bool:
-
R
¶ Rules agent uses to evaluate artifacts. Each rule in R is expected to be a callable with a single parameter, the artifact to be evaluated. Callable should return a float in [-1,1]; where 1 means that rule is very prominent in the artifact; 0 means that there is none of that rule in the artifact; -1 means that the artifact shows traits opposite to the rule.
-
W
¶ Weights for the rules.
Each weight should be in [-1,1].
-
Feature¶
Feature module holds base implementation for features,
Feature
, that creative agents have in their
rulesets.
-
class
creamas.rules.feature.
Feature
(name, domains, rtype)[source]¶ Base feature class that is callable after initialization.
Each feature value takes as an input an artifact, and returns feature’s value for that artifact. If artifact type is not supported, feature’s evaluation should return
None
.Returned feature values can be of any type, but rules (
Rule
) should have appropriate mappers to map possible feature values to the interval [-1, 1].Usage example:
from myfeat import MyFeature from myartifact import MyArtifact myart = MyArtifact(*myparams) myart.domain = mytype f = MyFeature() mytype in f.domains == True # True ret = f(myart) type(ret) == f.rtype # True
Parameters: -
extract
(artifact)[source]¶ Extract feature’s value from an artifact.
If artifact with a domain not in
domains
is used as a parameter, the function should returnNone
.Note
Dummy implementation, override in a subclass.
Returns: Value extracted from the artifact. Return type: rtype
or NoneRaises: NotImplementedError – if not overridden in subclass
-
domains
¶ Set of acceptable artifact domains for this feature.
When artifacts with other domains are used as parameters for
extract()
, the function should returnNone
.
-
name
¶ Human readable name of the feature.
-
rtype
¶ Value type returned by this feature.
-
Rule¶
Rule module holds the base implementation of a rule,
Rule
. Rules combine features and mappers to
a functional body, where each feature also has a weight attached to it.
-
class
creamas.rules.rule.
Rule
(rules, weights, evaluation='ave')[source]¶ A
Rule
is a treelike data structure consisting of otherRule
andRuleLeaf
instances. Rules can be used by agents to evaluate artifacts.Like features, rules offer a simple interface where artifact can be evaluated by calling a rule instance with artifact as the only argument. Rules should return a float in [-1, 1] when evaluated.
from creamas.core.rule import Rule rule = Rule([myleaf, myleaf2, myrule], [1.0, 1.0, 1.0]) res = rule(myartifact)
Parameters: - rules (list) – Subrules for this rule. Subrule can be either an iterable of length 2, the (Feature, Mapper)-pair, or another Rule instance.
- weights (list) – Weights for the subrules.
- evaluation –
How rule’s internal evaluation is done. Either one of the predefined functions, or user defined callable which takes two arguments: rule and artifact (in that order). Predefined functions and their keywords are:
- ’ave’:
weighted_average()
- ’min’:
minimum()
- ’max’:
maximum()
- ’ave’:
-
R
¶ A list of subrules in this rule.
-
W
¶ A list of weights for subrules in this rule.
-
domains
¶ Rule’s acceptable artifact domains is the union of all its subrules acceptable domains. Each artifact is evaluated only with subrules that do not return
None
when the feature is evaluated with it.
-
class
creamas.rules.rule.
RuleLeaf
(feat, mapper)[source]¶ Leaf implementation for rules.
A
RuleLeaf
combines a feature and a mapper into one functional unit. Adding twoRuleLeaf
instances together will result in an instance ofRule
. Two instances ofRuleLeaf
are equal if their features are equal, mappers are not considered.Parameters: - feat (py:class:~creamas.core.feature.Feature) – Feature for this leaf rule.
- mapper (py:class:~creamas.core.mapper.Mapper) – Mapper for this leaf rule
-
domains
¶ Domains for this rule leaf.
-
feat
¶ The feature for this rule leaf.
-
mapper
¶ The mapper used in this rule leaf.
Mapper¶
Mapper module hold base implementation for mappers. Mappers are functions that map feature’s possible values to the interval [-1, 1]. While features are thought to belong to artifacts of certain types, mappers usually belong to a single agent making it possible for each agent to have their own appreciation standards for the feature.
-
class
creamas.rules.mapper.
Mapper
[source]¶ Base implementation of mapper, serves as identity function for
int
andfloat
types.Mappers, as rules and features, are callable after initialization.
-
map
(value)[source]¶ Map given value to the interval [-1, 1].
This base implementation maps each value to itself, capping to [-1, 1].
Parameters: value – Value to map Returns: Value mapped to the interval [-1, 1] Rtype float:
-
value_set
¶ Acceptable input types for the mapper.
-
Rules package holds base implementations for features, mappers, rules and agents using them.
- rules.agent -
RuleAgent
implementation - rules.feature - base feature implementation
- rules.rule - base feature implementation
- rules.mapper - base mapper implementation
Mappers¶
Various mapper implementations. Mappers are functions that map possible feature value’s to the interval [-1, 1]. In Creamas, they are used by individual agent’s to represent agent’s preferences over features values.
-
class
creamas.mappers.
BooleanMapper
(mode='10')[source]¶ Boolean mapper that has four different modes.
Depending on the mode, True and False are mapped either to 1, 0, or -1.
mode True False ‘10’ 1.0 0.0 ‘01’ 0.0 1.0 ‘1-1’ 1.0 -1.0 ‘-11’ -1.0 1.0 -
map
(value)[source]¶ Map given value to the interval [-1, 1].
This base implementation maps each value to itself, capping to [-1, 1].
Parameters: value – Value to map Returns: Value mapped to the interval [-1, 1] Rtype float:
-
mode
¶ Mode of the mapper.
-
value_set
¶ Acceptable input types for the mapper.
-
-
class
creamas.mappers.
LinearMapper
(lo, hi, mode='01')[source]¶ Mapper that maps values in given interval linearly.
Can be used for features that return either ‘int’ or ‘float’ values.
Based on its mode, maps lo and hi to different end points and values between them to a straight line. Depending on the mode, lo and hi have following end points:
mode lo hi ‘10’ 1.0 0.0 ‘01’ 0.0 1.0 ‘1-1’ 1.0 -1.0 ‘-11’ -1.0 1.0 -
map
(value)[source]¶ Map given value to the interval [-1, 1].
This base implementation maps each value to itself, capping to [-1, 1].
Parameters: value – Value to map Returns: Value mapped to the interval [-1, 1] Rtype float:
-
mode
¶ Mode of the mapper.
-
value_set
¶ Accepted value types, i.e. this mapper can be used for the features that return these types of values.
-
-
class
creamas.mappers.
DoubleLinearMapper
(lo, mid, hi, mode='01')[source]¶ Mapper that concatenates two linear mappers.
Can be used for features that return either ‘int’ or ‘float’ values.
First line is created from lo to mid and second line from mid to hi. Depending on the mode, lo, mid and hi are mapped to following end points.
mode lo mid hi ‘10’ 1.0 0.0 1.0 ‘01’ 0.0 1.0 0.0 ‘1-1’ 1.0 -1.0 1.0 ‘-11’ -1.0 1.0 -1.0 -
map
(value)[source]¶ Map given value to the interval [-1, 1].
This base implementation maps each value to itself, capping to [-1, 1].
Parameters: value – Value to map Returns: Value mapped to the interval [-1, 1] Rtype float:
-
mode
¶ Mode of the mapper.
-
value_set
¶ Accepted value types, i.e. this mapper can be used for the features that return these types of values.
-
-
class
creamas.mappers.
GaussianMapper
(mean, std, mode='01')[source]¶ Gaussian distribution mapper.
The mapped value is relative to given Gaussian distribution’s maximum point (pmax, evaluated at point loc) and the probability density function’s value at given evaluation point (pval).
The actual value calculation changes with the mode of the mapper:
mode mapped value ‘10’ \(1.0 - (pval / pmax)\) ‘01’ \(pval / pmax\) ‘1-1’ \(1.0 - 2(pval / pmax)\) ‘-11’ \(-1.0 + 2(pval / pmax)\) Parameters: -
map
(value)[source]¶ Map given value to the interval [-1, 1].
This base implementation maps each value to itself, capping to [-1, 1].
Parameters: value – Value to map Returns: Value mapped to the interval [-1, 1] Rtype float:
-
mode
¶ Mode of the mapper.
-
value_set
¶ Acceptable input types for the mapper.
-
-
class
creamas.mappers.
LogisticMapper
(x0, k, mode='01')[source]¶ Logistic function mapper.
The mapped value is relative to the logistic function’s value in the mapping point. Depending on the mode, some transformations (mirroring, shifting), might be applied to the mapped value.
Parameters: -
map
(value)[source]¶ Map given value to the interval [-1, 1].
This base implementation maps each value to itself, capping to [-1, 1].
Parameters: value – Value to map Returns: Value mapped to the interval [-1, 1] Rtype float:
-
mode
¶ Mode of the mapper.
-
value_set
¶ Acceptable input types for the mapper.
-
Domains¶
Creamas includes some utility functions for different artifact domains, i.e. images or language.
TODO: This functionality is currently being developed.
Images¶
Information¶
Various image information functions.
-
creamas.domains.image.image.
channel_portion
(image, channel)[source]¶ Estimates the amount of a color relative to other colors.
Parameters: - image – numpy.ndarray
- channel – int
Returns: portion of a channel in an image
Return type:
-
creamas.domains.image.image.
fractal_dimension
(image)[source]¶ Estimates the fractal dimension of an image with box counting. Counts pixels with value 0 as empty and everything else as non-empty. Input image has to be grayscale.
See, e.g Wikipedia.
Parameters: image – numpy.ndarray Returns: estimation of fractal dimension Return type: float
Features¶
Various feature implementations. Each feature value takes as an input an artifact, and returns feature’s value for that artifact.
Note
OpenCV has to be installed in order for the ImageComplexityFeature and ImageIntensityFeature classes in this module to work. It is not installed as a default dependency.
Use, e.g. pip install opencv-python
-
class
creamas.domains.image.features.
ImageComplexityFeature
[source]¶ Feature that estimates the fractal dimension of an image. The color values must be in range [0, 255] and type
int
. Returns afloat
.-
extract
(artifact)[source]¶ Extract feature’s value from an artifact.
If artifact with a domain not in
domains
is used as a parameter, the function should returnNone
.Note
Dummy implementation, override in a subclass.
Returns: Value extracted from the artifact. Return type: rtype
or NoneRaises: NotImplementedError – if not overridden in subclass
-
-
class
creamas.domains.image.features.
ImageRednessFeature
[source]¶ Feature that measures the redness of an image. Returns a
float
in range [0, 1].-
extract
(artifact)[source]¶ Extract feature’s value from an artifact.
If artifact with a domain not in
domains
is used as a parameter, the function should returnNone
.Note
Dummy implementation, override in a subclass.
Returns: Value extracted from the artifact. Return type: rtype
or NoneRaises: NotImplementedError – if not overridden in subclass
-
-
class
creamas.domains.image.features.
ImageGreennessFeature
[source]¶ Feature that measures the greenness of an image. Returns a
float
in range [0, 1].-
extract
(artifact)[source]¶ Extract feature’s value from an artifact.
If artifact with a domain not in
domains
is used as a parameter, the function should returnNone
.Note
Dummy implementation, override in a subclass.
Returns: Value extracted from the artifact. Return type: rtype
or NoneRaises: NotImplementedError – if not overridden in subclass
-
-
class
creamas.domains.image.features.
ImageBluenessFeature
[source]¶ Feature that measures the blueness of an image. Returns a
float
in range [0, 1].-
extract
(artifact)[source]¶ Extract feature’s value from an artifact.
If artifact with a domain not in
domains
is used as a parameter, the function should returnNone
.Note
Dummy implementation, override in a subclass.
Returns: Value extracted from the artifact. Return type: rtype
or NoneRaises: NotImplementedError – if not overridden in subclass
-
-
class
creamas.domains.image.features.
ImageIntensityFeature
[source]¶ Feature that measures the intensity of an image. Returns a
float
in range [0, 1].-
extract
(artifact)[source]¶ Extract feature’s value from an artifact.
If artifact with a domain not in
domains
is used as a parameter, the function should returnNone
.Note
Dummy implementation, override in a subclass.
Returns: Value extracted from the artifact. Return type: rtype
or NoneRaises: NotImplementedError – if not overridden in subclass
-
Other domains¶
Other domains are currently being developed
Math¶
Various mathematical utility functions.
-
creamas.math.
gaus_pdf
(x, mean, std)[source]¶ Gaussian distribution’s probability density function.
See, e.g. Wikipedia.
Parameters: Returns: pdf(s) in point x
Return type: float or numpy.ndarray
Logging¶
Logging module holds utilities that help with logging and analyzing the creative system’s behavior.
-
class
creamas.logging.
ObjectLogger
(obj, folder, add_name=False, init=True, log_level=10)[source]¶ Base logger for Creamas objects. Not a subclass of
logging.Logger
.Generates one file for each attribute to be logged.
Create new logger instance for obj in folder. If add_name is
True
, creates subfolder carryingobj.name
to folder. If init is true, sets logger’s level to log_level and adds basic StreamHandler to the logger.-
log_attr
(level, attr_name)[source]¶ Log attribute to file and pass the message to underlying logger.
Parameters:
-
write
(attr_name, prefix=None)[source]¶ Write attribute’s value to a file.
Parameters: Returns: message written to file
Return type:
-
folder
¶ Root logging folder for this logger.
-
obj
¶ Object this logger belongs to. Object has to have name attribute.
-
-
creamas.logging.
log_before
(attr, level=10)[source]¶ Decorator to log attribute’s value(s) before function call.
Implementation allows usage only for methods belonging to class. The class instance needs to have a
logger
that is derived fromObjectLogger
.Parameters:
-
creamas.logging.
log_after
(attr, level=10)[source]¶ Decorator to log attribute’s value(s) after function call.
Implementation allows usage only for methods belonging to class. The class instance needs to have a
logger
that is derived fromObjectLogger
.Parameters: