Creamas - Creative Multi-Agent Systems

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

Creamas project is created as a tool for researching creative multi-agent systems. 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 (environment) implementations.

Agents And Environments

Agents in Creamas focus on building artifacts and evaluating them. Each agent belongs to an environment, which also serves as a communication route between 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 3D-space. Agents are created by giving the environment as an initialization parameter.

from creamas.core import CreativeAgent, Environment
# Create environment to http://localhost:5555/
env = Environment.create(('localhost', 5555))
# Create agent with address http://localhost:5555/0
a1 = CreativeAgent(env)
# Create agent with address http://localhost:5555/1
a2 = CreativeAgent(env)

The fundamental design principle guiding the development of Creamas is that each agent creates artifacts in some domain(s) and evaluates them. A lot of the current functionality is geared towards this goal. However, Creamas does not take a stand on the design of the multi-agent systems and is therefore quite agnostic of the actual agent implementations.

Exchanging artifacts and information about them is one of the main tasks of the agents. An agent can also ask other agent’s opinions about their own artifacts or other artifacts they have seen. This allows the agent to accumulate knowledge about the preferences of other agents, which may alter the agent’s own activity.

# This is a toy example. Code won't work off the shelf as the agent's 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
ret = a1.ask_opinion(a2.addr, ar)
# get a1's current attitude towards a2
att = a1.get_attitude(a2.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)

Features, Mappers And Rules

Warning

Functionality in this section is not yet fully developed and tested.

Agents can evaluate artifacts by extracting features from them. As features can have all kinds of outputs, they are paired with mappers. A mapper serves as a function from feature’s outputs to the interval \([-1, 1] \in \mathbb{R}\). Where features are though to be artifact domain depended, and shareable between agents, mappers represent individual agent’s preferences over possible feature values.

Rules combine a set of features, and their corresponding mappers, to a functional unit. Rules also have weight for each feature, which may inhibit its effect on the overall rule’s evaluation. In its most basic form rule has one feature and its mapper, but agents are encouraged to merge existing rules together, or add new features to them in order to make them more expressive.

Simulation

Creamas provides an easy to use simulation creator which can be used to execute agents act() to run the environment in an iterative manner. See Using Simulation 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).

Installation

Creamas is developed with Python 3.5, 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
$>pyvenv-3.5 venv # create venv for python 3.5
$>source venv/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 one parameter that is passed down to super().__init__():

Each agent class should call super().__init__() first thing in __init__(), for example:

from creamas.core.agent import CreativeAgent

class MyAgent(CreativeAgent):

        def __init__(self, environment, *args, **kwargs):
                super().__init__(environment)
                # Your own initialization code

Using Simulation

creamas contains easy to use iterative simulation to perform experiments on single computer set up. Each agent wishing to use simulation has to implement act:

CreativeAgent.act(*args, **kwargs)[source]

Trigger agent to act. Dummy method, override in 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.

Simulation calls act for each agent in each simulation step. What agents do in their turn is up to you!

Simple Simulations

Creating simple iterative simulation is made easy with create. Observe.

from mymodule import MyAgent
from creamas.core.simulation import Simulation

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(agent_cls = MyAgent, agent_kwargs=agent_kwargs, n_agents=20)

# Advance simulation by single step
sim.step()

# End simulation and destroy it's environment.
sim.end()

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

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(agent_cls=agent_cls, n_agents=n_agents,agent_kwargs=agent_kwargs)
  1. You can create 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 = {'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()
for i in range(10):

        # do some complex calculation
        # ...

        Starspawn(env, cause_havoc=True, non_euclidian_angle=mystery)

sim = Simulation(env=env)

Advancing Simulation

Simulation holds few different ways to advance it.

# Advance simulation by single step (executing all agents once)
sim.step()

# Advance simulation by executing single agent.
sim.next()

# Advance simulation to the end of the current step.
sim.finish_step()

# Advance simulation by 10 steps
sim.steps(10)

Logging Simulation

TODO: Log the logging of logger.

API Documentation

Core

Agent

Agent module holds CreativeAgent implementation, a subclass of aiomas.Agent, which holds basic functionality thought to be shared by all 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.
  • R (list) – rules agent uses to evaluate artifacts
  • W (list) – Weight for each rule in R, in [-1,1].
  • A (list) – Artifacts the agent has created so far
  • D (dict) – Domain knowledge, other agents’ artifacts seen by this agent
  • connections (list) – Other agents this agent knows
  • attitudes (list) – Attitude towards each agent in connections, in [-1,1]
  • name (str) – Name of the agent. Defaults to address of the agent.
  • age – Age of the agent
coroutine act(*args, **kwargs)[source]

Trigger agent to act. Dummy method, override in 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.
add_artifact(artifact)[source]

Add artifact to A.

Raises:TypeError – If the artifact is not a member of Artifact or its subclass.
add_connection(addr, attitude=0.0)[source]

Added agent with given address to current connections with given initial attitude.

Does nothing if agent is already in connections.

Parameters:
  • addr (str) – Address of the agent to be added
  • attitude (float) – initial attitude towards agent, in [-1, 1]
Returns:

True if the agent was successfully added, False otherwise.

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:
coroutine ask_opinion(addr, artifact)[source]

Ask agent’s opinion about artifact.

The artifact object should be serializable by the environment.

Parameters:
  • addr (str) – Address of the agent which opinion is asked
  • artifact (object) – artifact to be evaluated
Returns:

agent’s evaluation of the artifact

Return type:

float

close(folder=None)[source]

Perform any bookkeeping needed before closing the agent.

Dummy implementation, override in subclass if needed.

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 agent.env.connect(addr).

Returns:Proxy object for the connected agent.
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 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_attitude(addr)[source]

Get attitude towards agent in connections. If agent is not in connections, returns None.

coroutine get_older()[source]

Age agent by one simulation step.

get_weight(rule)[source]

Get weight for rule. If rule is not in R, returns None.

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

remove_rule(rule)[source]

Remove rule from R and its corresponding weight from W.

Parameters:rule (~creamas.core.rule.Rule) – rule to remove
Raises:TypeError – if rule is not subclass of Rule
Returns:true if rule was successfully removed, otherwise false
Rtype bool:
sanitized_name()[source]

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

set_attitude(addr, attitude)[source]

Set attitude towards agent. If agent is not in connections, adds it.

set_weight(rule, weight)[source]

Set weight for rule in R, if rule is not in R, adds it.

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

This basic implementation returns the given candidate list as is.

vote(candidates)[source]

Rank artifact candidates.

The voting is needed for the agents living in societies using social 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
A

Artifacts created so far by the agent.

D

Domain knowledge accumulated by this agent.

Dictionary of agents and their artifacts.

R

Rules agent uses to evaluate artifacts. Each rule in R is expected to be a callable with 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 features. Each weight should be in [-1,1].

age

Age of the agent.

attitudes

Attitudes towards agents in connections.

connections

Addresses of the other agents the agent is aware of.

cur_res

Agent’s current resources. Capped to maximum resources.

env

The environment where the agent lives. Must be a subclass of Environment.

max_res

Maximum resources for the agent per act. If 0, agent has unlimited resources. If maximum resources are set below current resources, current resources are capped to new maximum resources.

name

Human readable 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.

add_eval(agent, e, fr=None)[source]

Add or change agent’s evaluation of the artifact with given framing information.

Parameters:
  • agent – agent which did the evaluation
  • e (float) – evaluation for the artifact
  • fr (object) – framing information for the evaluation
domain()[source]

Domain of the artifact. Domain must match feature’s possible domains at evaluation time, or None is returned.

creator

CreativeAgent who created the artifact.

evals

dict - evaluations of the artifact.

framings

dict - framings given for the evaluations.

obj

Artifact itself.

Feature

Feature module holds base implementation for features (Feature) that creative agents have in their rulesets.

class creamas.core.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’s values to the interval [-1, 1].

Usage example:

from myfeat import MyFeature
from myartifact import MyArtifact
myart = MyArtifact(*myparams)
myart.rtype == mytype # True
f = MyFeature()
ret = f(myart)
type(ret) == mytype # True
Parameters:
  • name (str) – feature’s name
  • domains (list) – all artifact domains (type) that can be evaluated with the feature.
  • rtype – value type returned by this feature
extract(artifact)[source]

Extract feature from artifact. Dummy implementation, override in subclass.

Returns:feature value extracted from the artifact
Return type:rtype
Raises:NotImplementedError – if not overridden in subclass
domains

Set of acceptable artifact domains for this feature. Other types of artifacts will return None when tried to extract with this feature.

name

Name of the feature.

rtype

Value type returned by this feature.

Rule

Rule module holds base implementation of rule (Rule). Rules combine features and mappers to functional body, where each feature also has weight attached to it.

TODO: write rule to accept other rules in F also.

class creamas.core.rule.Rule(rules, weights, evaluation='ave')[source]

Rule is a treelike data structure consisting of other Rules and RuleLeaf instances. Rules are 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. Subrule can be either an iterable of length 2, the (Feature, Mapper)-pair, or another Rule instance.
  • weight (float) – weight of the subrule
Returns:

false if subrule already in rule, true otherwise

Return type:

bool

R

list - subrules in this rule.

W

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

Mapper

Mapper module hold base implementation for mappers. Mappers are functions, that map each feature’s possible values to the interval [-1, 1]. While features are though to belong to artifacts of certain types, mappers usually belong to single agent making it possible for each agent to have their own appreciation standards for the feautre.

class creamas.core.mapper.Mapper[source]

Base implementation of mapper, serves as identity function.

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:

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, 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
add_candidate(artifact)[source]

Add candidate artifact to current candidates.

clear_candidates()[source]

Remove current candidates from the environment.

create_initial_connections(n=5)[source]

Create random initial connections for all agents.

Parameters:n (int) – the number of connections for each agent
destroy(folder=None, as_coro=False)[source]

Destroy the environment.

Does the following:

  1. calls save_info()
  2. for each agent: calls close()
  3. Shuts down its RPC-service.
get_agents(address=True, agent_cls=None)[source]

Get agents in the environment.

Parameters:
  • address (bool) – If true, returns only addresses of the agents.
  • agent_cls – Optional, if specified returns only agents belonging to that particular class.
coroutine get_artifacts(agent=None)[source]

Get artifacts published by certain agent.

Returns:All artifacts published by the agent.
Return type:list
get_random_agent(agent)[source]

Return random agent that is not the same as agent given as 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.
perform_voting(method='IRV', accepted=1)[source]

Perform voting to decide the ordering of the current candidates.

Voting calls each agent’s vote()-method, which might be costly in larger societies.

Parameters:
  • method (str) – Used voting method. One of the following: IRV = instant run-off voting, mean = best mean vote (requires cardinal ordering for votes), best = best singular vote (requires cardinal ordering, returns only one candidate), least_worst = least worst singular vote, random = selects random candidates
  • accepted (int) – the number of returned candidates
Returns:

list of Artifact objects, accepted artifacts. Some voting methods, e.g. mean, also return the associated scores for each accepted artifact.

Rype:

list

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(addr=None, agent=None)[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()[source]

Trigger all agents in the environment to act asynchronously.

validate_candidates()[source]

Validate current candidates in the environment by pruning candidates that are not validated at least by one agent, i.e. they are vetoed.

In larger societies this method might be costly, as it calls each agents’ validate().

age

Age of the environment.

artifacts

Published artifacts for all agents.

candidates

Current artifact candidates, subject to e.g. agents voting to determine which candidate(s) are added to artifacts.

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]

Base class for iterative simulations.

In each step the simulation calls act() for each agent in simulation environment.

Create simulation for previously set up environment.

Parameters:
  • env (Environment or MultiEnvironment) – fully initialized environment with agents already set
  • callback (callable) – function to call after each simulation step
Parat str log_folder:
 

folder to log simulation information

async_step()[source]

Progress simulation by running all agents once asynchronously.

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]

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]

End simulation and destroy the current simulation environment.

finish_step()[source]

Progress simulation to the end of the current step.

next()[source]

Trigger next agent to act() in the current step.

step()[source]

Progress simulation with a single step.

Can not be called when some of the agents have not acted for the current 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. Must be a subclass of Environment.

name

Name of the simulation.

order

Order in which agents are run.

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, features and artifacts.

Agents

Agents module holds various agent implementations inheriting from CreativeAgent.

class creamas.agents.NumberAgent(*args, **kwargs)[source]

Number agent created for early testing purposes of basic functionality.

Searchs “new” integers and evaluates them based on how many features in F (also integers) result in mod == 0.

invent_number(low, high)[source]

Invent new number from given interval.

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

A manager for Environment.

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.

coroutine act()[source]

For consistency. Override in subclass if needed.

coroutine add_candidate(artifact)[source]

Add candidate to the host manager’s list of candidates.

artifacts()[source]

Return artifacts from the managed environment.

candidates()[source]

Return candidates from the managed environment.

clear_candidates()[source]

Clear candidates in the managed environment.

This is a managing function for clear_candidates().

get_agents(address=True, agent_cls=None)[source]

Get agents from the managed environment

This is a managing function for the get_agents(), but returned agent lists exclude this manager by default.

coroutine get_artifacts()[source]

Get all artifacts from the host environment.

Returns:All the artifacts in the environment.
coroutine get_older(addr=None)[source]

Make agent in addr to get older, i.e. advance its internal clock.

handle(msg)[source]

Handle message, override in subclass if needed.

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

Report message to the host manager.

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

stop(folder=None)[source]

Stop the managed environment, close all the agents and set stop_received on this agent to True.

coroutine trigger_all()[source]

Trigger all agents in the managed environment to act once.

This is a managing function for trigger_all().

validate(candidates)[source]

Returns the candidate list unaltered.

Implemented for consistency.

validate_candidates(candidates)[source]

Validate the candidates with the agents in the managed environment.

vote(candidates)[source]

Vote for candidates. Manager votes each candidate similarly.

Implemented for consistency.

class creamas.mp.MultiEnvManager(container)[source]

A manager for MultiEnvironment.

A Manager can spawn other agents into its slave environments, and can execute other tasks relevant to the whole environment. The manager should always be the first (and usually only) agent created for the multi-environment’s managing environment. The actual simulation agents should be created to the slave environments, typically using multi-environment’s or its manager’s functionality.

Note

You should not need to create managers directly, instead pass the desired manager class to an instance of MultiEnvironment at its initialization time.

coroutine act(addr=None)[source]

Trigger agent in given addr to act.

add_candidate(artifact)[source]

Add candidate artifact into the candidates.

coroutine destroy()[source]

A managing function for trigger_all().

coroutine get_agents(address=True, agent_cls=None)[source]

Get all agents in all the slave environments.

This is a managing function for creamas.mp.MultiEnvironment.get_agents().

coroutine get_candidates(addr)[source]

Get candidates from the environment manager in addr manages.

coroutine get_older(addr)[source]

Make agent in addr to get older, i.e. advance its internal clock.

coroutine get_slave_agents(addr, address=True, agent_cls=None)[source]

Get agents in the specified manager’s environment.

Parameters:
  • addr (str) – Address of the environment’s manager
  • address (bool) – Return only the addresses of the agents, not proxies.
  • agent_cls – If specified, return only the agents that are members of the class.

See also

creamas.environment.Environment.get_agents() creamas.mp.EnvManager.get_agents(), creamas.mp.MultiEnvironment.get_agents()

handle(msg)[source]

Handle message. Override in subclass if needed.

coroutine is_ready()[source]

A managing function for is_ready().

coroutine kill(addr, folder=None)[source]

Send stop command to the manager agent in a given address. This will shutdown the manager’s environment.

coroutine set_host_manager(addr)[source]

Set this manager as a host manager to the manager in addr.

coroutine spawn(addr, agent_cls, *agent_args, **agent_kwargs)[source]

Spawn an agent to an environment in a manager in the given address.

agent_args and agent_kwargs are passed to the manager doing to spawning to be used as the agent’s initialization parameters.

Parameters:
  • addr (str) – Environment’s manager’s address
  • agent_cls – Class of the agent as a string, e.g. creamas.grid:GridAgent
Returns:

Proxy and port of the spawned agent

coroutine spawn_n(addr, agent_cls, n, *agent_args, **agent_kwargs)[source]

Same as spawn(), but spawn multiple agents with same initialization parameters.

This should considerably reduce the time needed to spawn a large number of homogeneous agents.

agent_args and agent_kwargs are passed to the manager doing to spawning to be used as the agent’s initialization parameters.

Parameters:
  • addr (str) – Environment’s manager’s address
  • agent_cls – Class of the agent as a string, e.g. creamas.grid:GridAgent
  • n (int) – Number of agents to spawn.
Returns:

List of (Proxy, port) tuples for the spawned agents.

... seealso:

:meth:`creamas.mp.EnvManager.spawn_n`
coroutine trigger_all()[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=None, mgr_cls=None, slave_addrs=[], slave_env_cls=None, slave_params=None, slave_mgr_cls=None, name=None, clock=None, extra_ser=None, log_folder=None, log_level=20)[source]

Environment for utilizing multiple processes (and cores) on a single machine.

MultiEnvironment has a managing environment, typically containing only a single manager, and a set of slave environments each having their own manager and (once spawned) the actual agents.

Currently, the implementation assumes that the slave environments do not use any time consuming internal initialization. If the slaves are not reachable after a few seconds after the initialization, an exception is raised. Thus, any slave environments should do their additional preparations, e.g. agent spawning, outside their __init__(), after MultiEnvironment has been initialized successfully.

Note

MultiEnvironment and the slave environments are internally initialized to have aiomas.MsgPack as the codec for the message serialization. Any communication to these environments and agents in them must use the same codec.

Parameters:
  • addr – (HOST, PORT) address for the manager environment.
  • env_cls – Class for the environment. Must be a subclass of Environment.
  • mgr_cls – Class for the multi-environment’s manager.
  • addrs – List of (HOST, PORT) addresses for the slave-environments.
  • slave_env_cls – Class for the slave environments.
  • slave_params – 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.
  • name (str) – Name of the environment. Will be shown in logs.
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
add_candidate(artifact)[source]

Add candidate artifact to current candidates.

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

Remove current candidates from the environment.

coroutine create_connection(addr, addr2)[source]

Create connection from agent in addr to agent in addr2.

This does not create a connection the other way around.

create_initial_connections(n=5)[source]

Create random initial connections for all agents.

Parameters:n (int) – the number of connections for each agent
destroy(folder=None, as_coro=False)[source]

Destroy the multiprocessing environment and its slave environments.

get_agents(address=True, agent_cls=None)[source]

Get agents from the slave environments. This method excludes each slave environment’s manager agent from the returned list.

Essentially calls get_agents() for each of the slave environment managers.

Parameters:
  • address (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.
Returns:

List of Proxy objects or addresses as specified by the input parameters.

get_artifacts(agent)[source]

Get artifacts published by certain agent.

Returns:All artifacts published by the agent.
Return type:list
get_random_agent(agent)[source]

Return random agent that is not the same as agent given as parameter.

Parameters:agent (CreativeAgent) – Agent that is not wanted to return
Returns:random, non-connected, agent from the environment
Return type:CreativeAgent
coroutine get_votes(addr, candidates)[source]

Get votes for candidates from a manager in addr.

Manager should implement get_votes().

See also

creamas.mp.EnvManager.get_votes()

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

perform_voting(method='IRV', accepted=1)[source]

Perform voting to decide the ordering of the current candidates.

Voting calls each agent’s vote-method, which might be costly in larger societies.

Parameters:
  • method (str) – Used voting method. One of the following: IRV = instant run-off voting, mean = best mean vote (requires cardinal ordering for votes), best = best singular vote (requires cardinal ordering, returns only one candidate), least_worst = least worst singular vote, random = selects random candidates
  • accepted (int) – the number of returned candidates
Returns:

list of :py:class`~creamas.core.artifact.Artifact`s, accepted artifacts

Rype:

list

random_addr()[source]

Get random manager for a slave environment.

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 spawn(agent_cls, *args, addr=None, **kwargs)[source]

Spawn a new agent.

If addr is None, spawns the agent in the slave environment with currently smallest number of agents.

Parameters:
  • agent_cls – Subclass of CreativeAgent
  • addr – Address for the slave enviroment’s manager, if specified.
Returns:

Proxy and address for the created agent.

coroutine trigger_act(addr)[source]

Trigger agent in addr to act.

This method is very inefficient if used repeatedly for a large number of agents.

coroutine trigger_all()[source]

Trigger all agents in all the slave environments to act asynchronously.

validate_candidates()[source]

Validate current candidates in the environment by pruning candidates that are not validated at least by one agent, i.e. they are vetoed.

In larger societies this method might be costly, as it calls each agents’ validate_candidates-method.

addrs

Addresses of the slave environment managers.

age

Age of the environment.

artifacts

Published artifacts for all get_agents.

candidates

Current artifact candidates, subject to e.g. agents voting to determine which candidate(s) are added to artifacts.

env

Environment hosting the manager of this multi-environment. This environment is also used without the manager to connect to the slave environment managers.

manager

This multi-environment’s manager.

name

Name of the environment.

creamas.mp.spawn_container(addr=('localhost', 5555), 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=[('localhost', 5555)], 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=<class 'creamas.core.environment.Environment'>, mgr_cls=<class 'creamas.mp.EnvManager'>, *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.

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 (>=1.6.2, developed with 1.6.2) to function. Asyncssh is not installed by default as a dependency.

class creamas.ds.DistributedEnvironment(host, port, nodes, logger=None)[source]

Distributed environment which manages several nodes containing multi-environments.

This environment is used to spawn the 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 DistributedEnvironment.destroy() after the simulation has been done. 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)
# 'pool' holds the process pool for SSH-connections to nodes,
# 'r' contains the (future) return values of the connections.
pool, r = ds.spawn_nodes(spawn_cmd)
timeout = 30
loop = asyncio.get_event_loop()
nodes_ready = loop.run_until_complete(ds.wait_nodes(timeout))
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.destroy()
Parameters:
  • host – Host of the env property (this node). The host should not be present in nodes.
  • port – Port for the managing environments on each node (incl. this node), e.g. 5555. This port is not checked for availability before the node environments are spawned.
  • nodes – List of nodes (servers) which are used to host the multi-environments. Each node should allow SSH-connections without login credentials.
  • logger – Optional logger for this simulation.
destroy()[source]

Destroy the simulation.

Stop the nodes and free other resources associated with the simulation.

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

Prepare nodes (and slave environments and agents) so that they are ready for the simulation. Should be called after wait_nodes().

Note

Override in the subclass for the intended functionality.

spawn_nodes(spawn_cmd)[source]

Spawn all multi-environments on the nodes using SSH.

Parameters:spawn_cmd (int) – 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.

Warning

The spawning process of the nodes assumes that the manager agent of each multi-environment (on each node) is initialized in the port given by the parameter port at the distributed environment’s initialization time.

coroutine stop_nodes(timeout=1)[source]

Stop all the nodes 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_all()[source]

Trigger all agents in all the nodes to act asynchronously.

This method makes a connection to each manager in manager_addrs and asynchronously executes trigger_all() in all of them.

coroutine wait_nodes(timeout)[source]

Wait until all nodes are ready or timeout expires. Should be called after spawn_nodes().

Node is assumed ready when its managers is_ready()-method returns True.

Parameters:timeout (int) – Timeout (in seconds) after which the method will return even though all the nodes are not ready yet.
env

Environment used to communicate with node managers.

The environment does not hold any agents by default, but it is easy to create a manager for it on your own, if the node managers need to be able to communicate back to this environment.

manager_addrs

Addresses of node managers.

These addresses are derived from nodes and port parameters given at the initialization time, 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.

nodes

Environment nodes (excluding the current host).

Altering the nodes after the initialization most probably causes unexpected behavior.

coroutine creamas.ds.ssh_exec(server, cmd)[source]

Execute a command on a given server using asynchronous SSH-connection.

The method does not propagate exceptions raised during the SSH-connection, instead, the exceptions are caught and returned. The method will exit after the first exception is raised.

Parameters:
  • server (str) – Address of the server
  • cmd (str) – Command to be executed
Returns:

(closed SSH-connection, exception)-tuple, if no exceptions are caught, the second value is None.

creamas.ds.ssh_exec_in_new_loop(server, cmd)[source]

Same as ssh_exec() but creates a new event loop and executes ssh_exec() in that event loop.

Features

Various implemented features

class creamas.features.ModuloFeature(n)[source]

Feature that returns true if artifact’s object’s remainder is zero when divided by n.

Accepts ints and floats as artifact domains.

n

Feature’s divisor.

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
mode

Mode of 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
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
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’.
mode

Mode of 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’.
mode

Mode of the mapper.

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 objects. Not a subclass of base 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='age')[source]

Write attribute’s value to file.

Parameters:
  • attr_name (str) – attribute’s name to be logged
  • prefix (str) – attribute’s name that is prefixed to logging message, defaults to age.
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 logger attribute that is subclass of 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 logger variable set.

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

Indices and tables