Creamas - Creative Multi-Agent Systems

_images/python-powered-w-70x28.png

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

trigger_all()

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:

  1. 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)
  1. 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.

_images/multiprocessing_architecture.svg

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:

  1. Master environment connects to the slave’s manager.
  2. Master environment calls slave manager’s exposed method.
  3. The slave’s manager calls the method with the same name in its environment with the given arguments.
  4. The slave environment executes the method and returns possible return value.
  5. The slave manager passes the return value back to the master environment.
  6. 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 of DistributedEnvironment

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).

_images/distributed_architecture.svg

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:

  1. Initialize DistributedEnvironment with a list of node locations
  2. Create node spawning terminal commands for each node, i.e. commands which start MultiEnvironment on each node.
  3. Spawn nodes using spawn_nodes()
  4. Wait until all nodes are ready (see, e.g. is_ready()) using wait_nodes(). A node is ready when it has finished its own initialization and is ready to execute orders.
  5. 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:

  1. it opens a SSH connection to one of the nodes, and
  2. 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

trigger_all()

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 to connections[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 to connections.

Parameters:conns (list) – A list of (addr, kwargs)-tuples
Returns: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:

float

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.

clear_connections()[source]

Clear all connections from the agent.

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
qualname()[source]

Get qualified name of this class.

coroutine random_connection()[source]

Connect to random agent from current connections.

Returns:aiomas.Proxy object for the connected agent.
refill()[source]

Refill agent’s resources to maximum.

remove_connection(addr)[source]

Remove agent with given address from current connections.

sanitized_name()[source]

Sanitized name of the agent, used for file and directory creation.

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:
  • agent – Name of the agent which did the evaluation.
  • e (float) – Evaluation for the artifact.
  • fr (object) – Framing information for the evaluation.
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
clear_connections()[source]

Clear all connections from the agents in the environment.

close(folder=None, as_coro=False)[source]

Close the environment.

Does the following:

  1. calls save_info()
  2. for each agent: calls close()
  3. Shuts down its RPC-service.
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 address tcp://environment-host:port/0, to the returned list if the environment has attribute manager. If environment does not have manager, then the parameter does nothing.
Returns:

A list of agents in the environment.

Return type:

list

Note

Manager agents are excluded from the returned lists of agents by default.

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 in MultiEnvironment, then the manager’s get_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 connection
Returns:A list of (addr, connections)-tuples, where connections is a list of addresses agent in addr is connected to. If data parameter is True, then the connections list contains tuples of (nb_addr, data)-pairs , where data 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 return
Returns: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:
async_step()[source]

Progress simulation by running all agents asynchronously once.

async_steps(n)[source]

Progress simulation by running all agents n times asynchronously.

close(folder=None)[source]

Close the simulation and the current simulation environment.

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.

step()[source]

Progress simulation by a single step.

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.

Core package holds base implementations for agents, environments, simulations and artifacts.

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 of aiomas.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.

artifacts()[source]

Return artifacts from the managed environment.

close(folder=None)[source]

Implemented for consistency. This basic implementation does nothing.

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().

handle(msg)[source]

Handle message, override in subclass if needed.

host_manager()[source]

Get address of the host manager.

coroutine is_ready()[source]

Check if the managed environment is ready.

This is a managing function for is_ready().

coroutine report(msg, timeout=5)[source]

Report message to the host manager.

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 of aiomas.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.

close(folder=None)[source]

Implemented for consistency. This basic implementation does nothing.

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 and agent_cls parameters are omitted from the call to underlying multi-environment’s get_agents().

If aiomas.rpc.Proxy objects from all the agents are needed, call each slave environment manager’s get_agents() directly.

coroutine get_artifacts()[source]

Get all the artifacts from the multi-environment.

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 spawn n 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().

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 of creamas.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.

coroutine connect(*args, **kwargs)[source]

A shortcut to env.connect().

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.

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:
  • addr (bool) – If True, returns only addresses of the agents, otherwise returns a Proxy object for each agent.
  • agent_cls – If specified, returns only agents that are members of that particular class.
  • as_coro (bool) – If True, returns a coroutine, otherwise runs the method in an event loop.
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:
  • data (bool) – If True, returns also the data stored for each connection.
  • as_coro (bool) – If True returns a coroutine, otherwise runs the asynchronous calls to the slave environment managers in the event loop.
get_slave_managers()[source]

Get addresses of all slave environment managers.

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 calling check_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 of MultiEnvManager 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.

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 of pkg.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__().

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’s is_ready()-method returns True.
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.

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 a KeyboardInterrupt is raised and calls env_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 calls trigger_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). The host 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.

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:
  • addr (bool) – If True, returns only addresses of the agents, otherwise returns a Proxy object for each agent.
  • agent_cls – If specified, returns only agents that are members of that particular class.
  • as_coro (bool) – If True, returns a coroutine, otherwise runs the method in an event loop.
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:
  • data (bool) – If True, returns also the data stored for each connection.
  • as_coro (bool) – If True returns a coroutine, otherwise runs the asynchronous calls to the slave environment managers in the event loop.
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 calling check_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 of MultiEnvManager 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.

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 of pkg.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__().

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. If None, then the same port is used to derive the master manager addresses as was used to initialize this distributed environment’s managing environment (port in addr).
  • 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.

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’s is_ready()-method returns True.
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.

coroutine creamas.ds.run_node(menv, log_folder)[source]

Run MultiEnvironment until its manager’s stop() is called.

Parameters:

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. Calling close() will automatically call each node manager’s stop() and therefore release the script.

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 given timeout 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 executes ssh_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:

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 act(*args, **kwargs)[source]

See creamas.core.agent.CreativeAgent.act.

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.

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 multiple GridEnvironment instances.
coroutine set_grid_neighbor(card, addr)[source]

Set the neighbor grid for the grid in card cardinal direction. The addr should point to thanager* of the neighboring grid.

set_gs(gs)[source]

Set grid size for the managed environment.

set_origin(origin)[source]

Set originating coordinates for the managed environment.

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 spawn() for details.

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().

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 initializing GridMultiEnvironment!

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() and set_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).

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 or DistributedEnvironment.
  • G – NetworkX graph structure, either networkx.graph.Graph or networkx.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 using MultiEnvironment or DistributedEnvironment.

creamas.nx.graph_from_connections(env, directed=False)[source]

Create NetworkX graph from agent connections in a given environment.

Parameters:
Returns:

The created NetworkX graph.

Return type:

DiGraph or Graph

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.

Voting for Agents

Implementations for voting and other social choice behaviors.

Holds basic implementations for:

  • VoteAgent: An agent implementing functions needed for voting
  • VoteEnvironment: An environment holding basic voting functionality.
  • VoteManager: A manager agent for instances of VoteEnvironment 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’s evaluate() function.
  • add_candidate(): Add candidate artifact to the agent’s environment’s list of current candidates.
add_candidate(artifact)[source]

Add artifact to the environment’s current list of 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 ranked
Returns: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).

add_candidate(artifact)[source]

Add candidate artifact to the list of current candidates.

clear_candidates()[source]

Remove current candidate artifacts from 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 get_candidates()[source]

Get current candidates from the managed environment.

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 then gather_votes() for the candidates. Optionally the organizer may validate_candidates() before gathering the votes. After the votes have been collected the organizer may compute_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() or vote_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 of votes.
  • 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:

list

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.

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.

get_managers()[source]

Get managers for the slave environments.

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
creamas.vote.vote_random(candidates, votes, n_winners)[source]

Select random winners from the candidates.

This voting method bypasses the given votes completely.

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.addrs2managers(addrs)[source]

Map agent addresses to their assumed managers.

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 to task_coro() when creating tasks for each address in addrs.

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

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().

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.
creamas.util.sort_addrs(addrs)[source]

Return agent addresses in a sorted order.

Agent addresses are sorted with following hierarchical criteria:
  1. by the host of an agent’s environment
  2. by the port (interpreted as an integer) of an agent’s environment
  3. 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

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:
  • R (list) – rules agent uses to evaluate artifacts
  • W (list) – Weight for each rule in R, in [-1,1].
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, otherwise False.

Rtype bool:
evaluate(artifact)[source]

Evaluate artifact with agent’s current rules and weights.

Parameters:artifact (Artifact) – Artifact to be evaluated
Returns: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\).

get_weight(rule)[source]

Get weight for rule.

If rule is not in R, returns None.

remove_rule(rule)[source]

Remove rule from R and its corresponding weight from W.

Parameters:rule (Rule or RuleLeaf) – rule to remove
Raises:TypeError – If rule is not derived from Rule or RuleLeaf.
Returns:True if the rule was successfully removed, otherwise False.
Rtype bool:
set_weight(rule, weight)[source]

Set weight for rule in R.

Adds the rule if it is not in R.

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:
  • name (str) – Feature’s name
  • domains (list) – A list of all artifact domains (type) that can be evaluated with the feature.
  • rtype – Value type returned by this feature. This can be combined with mappers accepting this type as the input parameter.
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 return None.

Note

Dummy implementation, override in a subclass.

Returns:Value extracted from the artifact.
Return type:rtype or None
Raises: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 return None.

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 other Rule and RuleLeaf 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()
add_subrule(subrule, weight)[source]

Add subrule to the rule.

Parameters:
  • subrule – Subrule to add to this rule, an instance of Rule or RuleLeaf.
  • weight (float) – Weight of the subrule
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 two RuleLeaf instances together will result in an instance of Rule. Two instances of RuleLeaf 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 and float 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.

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:
  • mean (float) – mean of the mapping distribution
  • std (float) – standard deviation of the mapping distribution
  • mode – mode of the mapper: ‘10’, ‘01’, ‘1-1’ or ‘-11’.
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:
  • x0 (float) – sigmoid’s midpoint
  • k (float) – steepness of the curve
  • mode – mode of the mapper: ‘10’, ‘01’, ‘1-1’ or ‘-11’.
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:

float

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
creamas.domains.image.image.intensity(image)[source]

Calculates the average intensity of the pixels in an image. Accepts both RGB and grayscale images.

Parameters:image – numpy.ndarray
Returns:image intensity
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 a float.

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 return None.

Note

Dummy implementation, override in a subclass.

Returns:Value extracted from the artifact.
Return type:rtype or None
Raises: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 return None.

Note

Dummy implementation, override in a subclass.

Returns:Value extracted from the artifact.
Return type:rtype or None
Raises: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 return None.

Note

Dummy implementation, override in a subclass.

Returns:Value extracted from the artifact.
Return type:rtype or None
Raises: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 return None.

Note

Dummy implementation, override in a subclass.

Returns:Value extracted from the artifact.
Return type:rtype or None
Raises: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 return None.

Note

Dummy implementation, override in a subclass.

Returns:Value extracted from the artifact.
Return type:rtype or None
Raises: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:
  • x (float or numpy.ndarray) – point in x-axis
  • mean (float) – mean or expectation
  • str (float) – standard deviation
Returns:

pdf(s) in point x

Return type:

float or numpy.ndarray

creamas.math.logistic(x, x0, k, L)[source]

Logistic function.

See, e.g Wikipedia.

Parameters:
  • x (float or numpy.ndarray) – point in x-axis
  • x0 (float) – sigmoid’s midpoint
  • k (float) – steepness of the curve
  • L (float) – maximum value of the curve
Returns:

function’s value(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 carrying obj.name to folder. If init is true, sets logger’s level to log_level and adds basic StreamHandler to the logger.

add_handler(handler)[source]

Add handler to the logger.

get_file(attr_name)[source]

Return absolute path to logging file for obj’s attribute.

log_attr(level, attr_name)[source]

Log attribute to file and pass the message to underlying logger.

Parameters:
  • level (int) – logging level
  • attr_name (str) – attribute’s name to be logged
write(attr_name, prefix=None)[source]

Write attribute’s value to a file.

Parameters:
  • attr_name (str) – Attribute’s name to be logged
  • prefix (str) – Optional. Attribute’s name that is prefixed to logging message, defaults to None.
Returns:

message written to file

Return type:

str

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 from ObjectLogger.

Parameters:
  • level (int) – logging level
  • attr (str) – name of the class instance’s parameter to be logged
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 from ObjectLogger.

Parameters:
  • level (int) – logging level
  • attr (str) – name of the class instance’s parameter to be logged