Source code for abcEconomics.group

# Copyright 2012 Davoud Taghawi-Nejad
#
#  Module Author: Davoud Taghawi-Nejad
#
#  abcEconomics is open-source software. If you are using abcEconomics for your research you are
#  requested the quote the use of this software.
#
#  Licensed under the Apache License, Version 2.0 (the "License"); you may not
#  use this file except in compliance with the License and quotation of the
#  author. You may obtain a copy of the License at
#        http://www.apache.org/licenses/LICENSE-2.0
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#  License for the specific language governing permissions and limitations under
# the License.


[docs]class Chain: def __init__(self, iterables): self.iterables = iterables def __iter__(self): for it in self.iterables: for element in it: yield element def __repr__(self): return repr(list(self.iterables)) def __str__(self): return str(list(self.iterables)) def __getitem__(self, item): try: return self.iterables[item] except IndexError: self.iterables = [i for i in iter(self)] return self.iterables[item]
[docs]class Action: # This allows actions of Group to be combined. For example:: # # (firms.sell & households.buy)() # # It works by returning a callable and combinable action from # the groups __getattr__ method. def __init__(self, scheduler, actions): self._scheduler = scheduler self.actions = actions def __add__(self, other): return Action(self._scheduler, self.actions + other.actions) def __call__(self, *args, **kwargs): for names, command, _, __ in self.actions: self._scheduler.do(names, command, args, kwargs) return Chain([self._scheduler.post_messages(action[0]) for action in self.actions])
# itertools.chain, does not work here
[docs]class Group: """ A group of agents. Groups of agents inherit the actions of the agents class they are created by. When a group is called with an agent action all agents execute this actions simultaneously. e.G. :code:`banks.buy_stocks()`, then all banks buy stocks simultaneously. agents groups are created like this:: sim = Simulation() Agents = sim.build_agents(AgentClass, 'group_name', number=100, param1=param1, param2=param2) Agents = sim.build_agents(AgentClass, 'group_name', param1=param1, param2=param2, agent_parameters=[dict(ap=ap1_agentA, ap=ap2_agentA), dict(ap=ap1_agentB, ap=ap2_agentB), dict(ap=ap1_agentC, ap=ap2_agentC)]) Agent groups can be combined using the + sign:: financial_institutions = banks + hedgefunds ... financial_institutions.buy_stocks() or:: (banks + hedgefunds).buy_stocks() Simultaneous execution means that all agents act on the same information set and influence each other only after this action. individual agents in a group are addressable, you can also get subgroups (only from non combined groups): banks[5].buy_stocks() (banks[6,4] + hedgefunds[7,9]).buy_stocks() agents actions can also be combined:: buying_stuff = banks.buy_stocks & hedgefunds.buy_feraries buy_stocks() or:: (banks.buy_stocks & hedgefunds.buy_feraries)() """ def __init__(self, sim, scheduler, names, agent_arguments=None): self.sim = sim self.num_managers = sim.processes self._scheduler = scheduler if names is None: self.names = set() else: self.names = names self._agent_arguments = agent_arguments self.panel_serial = 0 self.last_action = "Begin_of_Simulation" self.num_agents = 0 if agent_arguments is not None: self.agent_name_prefix = agent_arguments['group'] def __add__(self, other): return Group(self.sim, self._scheduler, self.names.union(other.names)) def __radd__(self, g): if isinstance(g, Group): return self.__add__(g) else: return self
[docs] def panel_log(self, variables=[], goods=[], func={}, len=[]): """ panel_log(.) writes a panel of variables and goods of a group of agents into the database, so that it is displayed in the gui. Args: goods (list, optional): a list of all goods you want to track as 'strings' variables (list, optional): a list of all variables you want to track as 'strings' func (dict, optional): accepts lambda functions that execute functions. e.G. :code:`func = lambda self: self.old_money - self.new_money` len (list, optional): records the length of the list or dictionary with that name. Example in start.py:: for round in simulation.next_round(): firms.produce_and_sell() firms.panel_log(goods=['money', 'input'], variables=['production_target', 'gross_revenue']) households.buying() """ self._do('_panel_log', variables, goods, func, len, self.last_action)
[docs] def agg_log(self, variables=[], goods=[], func={}, len=[]): """ agg_log(.) writes a aggregate data of variables and goods of a group of agents into the database, so that it is displayed in the gui. Args: goods (list, optional): a list of all goods you want to track as 'strings' variables (list, optional): a list of all variables you want to track as 'strings' func (dict, optional): accepts lambda functions that execute functions. e.G. :code:`func = lambda self: self.old_money - self.new_money` len (list, optional): records the length of the list or dictionary with that name. Example in start.py:: for round in simulation.next_round(): firms.produce_and_sell() firms.agg_log(goods=['money', 'input'], variables=['production_target', 'gross_revenue']) households.buying() """ self._do('_agg_log', variables, goods, func, len)
[docs] def create_agents(self, Agent, number=1, agent_parameters=None, **common_parameters): """ Create new agents to this group. Works only for non-combined groups Args: Agent: The class used to initialize the agents agent_parameters: List of dictionaries of agent_parameters number: number of agents to create if agent_parameters is not set any keyword parameter: parameters directly passed to :code:`agent.init` methood Returns: The id of the new agent """ if agent_parameters is None: agent_parameters = number new_names = self._scheduler.add_agents(Agent, common_parameters, agent_parameters, self._agent_arguments, self.num_agents) self.num_agents += len(new_names) self.names.update(new_names) return new_names
def _do(self, command, *args, **kwargs): """ agent actions can be executed by :code:`group.action(args=args)`. """ self.last_action = command return self._scheduler.do(self.names, command, args, kwargs) def __getattr__(self, command, *args, **kwargs): self.last_action = command return Action(self._scheduler, [(self.names, command, args, kwargs)])
[docs] def delete_agents(self, names): """ Remove an agents from a group, by specifying their id. Args: ids: list of ids of the agent Example:: students.delete_agents([1, 5, 15]) """ for name in names: self.names.remove(name) self._scheduler.delete_agents(names)
def __getitem__(self, ids): try: names = {(self.agent_name_prefix, id) for id in ids} except TypeError: names = {(self.agent_name_prefix, ids)} return Group(self.sim, self._scheduler, names, self._agent_arguments)
[docs] def by_names(self, names): """ Return a callable group of agents from a list of names.group Example:: banks.by_names(['UBS', 'RBS', "DKB"]).give_loans() """ names = set(names) return Group(self.sim, self._scheduler, names, self._agent_arguments)
[docs] def by_name(self, name): """ Return a group of a single agents by its name """ names = {name} return Group(self.sim, self._scheduler, names, self._agent_arguments)
def __len__(self): """ Returns the length of a group """ return len(self.names) def __repr__(self): return repr(self.names)