Package: kala-econ-games
Kala, agent-based econ gamesThis package defines a general framework to implement different agent-based models (ABMs). One of the most important things that we aim to support is a networked topology, with agents being represented as nodes that can only be matched against their neighbours. However, we also support games that do not include a network topology.To begin with, we have used the framework to coded the model presented inErnst, Ekkehard. ‘The Evolution of Time Horizons for Economic Development’, 2004.[Ernst04](DOI). We added some extensions, including support for networks.InstallationThe package is written in Python (minimal version: 3.10). We recommend that the installation is made inside a virtual environment and to do this, one can use eitherconda(recommended in order to control the Python version) or the Python builtinvenv(if the system's version of Python is compatible).Create a virtual environment using Python's builtinvenvThe first step is running$python-mvenvkalaThis creates a folder that contains the virtual environmentkala(a different name can be used; change below as appropriate). We activate it using$sourcekala/bin/activateUsing conda (recommended)The toolconda, which comes bundled with Anaconda has the advantage that it lets us specify the version of Python that we want to use. Python>=3.10 is required.A new environment can be created with$condacreate-nkalapython=3.10-yLike before, the environment's name can be anything else instead ofkala(simply change the name below). We activate it using$condaactivatekalaLocal install of the packageOnce we are working inside an active virtual environment, we install (the dependencies and) the package by running[$pipinstall-rrequirements.txt]$pipinstall-e.Coming soonIn the future, we would like to completely package the code and publish it inPyPIso that a local installation isn't needed any more, and the installation can be solved directly by the package manager of choice.User guideThe user-facing interface of the package is located underkala.modelsbut the concrete classes have been added to the top-level package so that they can be imported directly.Before going into more detail, we'll give and comment a full example that creates a working game.We start with 3 importsimportnetworkxasnxfromkalaimportInvestorAgent,CooperationStrategy,DiscreteTwoByTwoGame,SimpleGraphWe've importedNetworkxas a handy tool to create a pre-defined network from which we "steal" the edges, but other ways of creating graph topologies will be supported in the future.The first step is defining the agents.num_nodes=10# A list of InvestorAgents, half of them savers and half non-saversis_saver=savers=[True,False]*5agents=[InvestorAgent(is_saver=is_saver[i])foriinrange(num_nodes)]Next, we create a network (with NetworkX) and we instantiate our ownSimpleGraphwhich can take a NetworkX object as argument. We also pass the agents so that there is a one-to-one mapping between the nodes in the graph and the individual agents.g=nx.barabasi_albert_graph(num_nodes,8,seed=0)G=SimpleGraph(g,nodes=agents)We initialise a strategycoop=CooperationStrategy(stochastic=True,# True to draw random numbersdifferential_efficient=0.5,# eta_hat_hat = 1 + differential_efficientdifferential_inefficient=0.05,# eta_hat = 1 - differential_inefficientrng=0,# this seeds the random number generator (not needed in general))and then we combine the graph (which already encompasses the agents) and the strategy into a game.game=DiscreteTwoByTwoGame(G,coop)All we need to do now is call a method and, if we want, keep track of different properties like so:for_inrange(10):game.play_round()wealth,num_savers=game.get_total_wealth(),game.get_num_savers()print(f"wealth={wealth:.2f},{num_savers=}")More complicated configurations can be added in several places. For example, we can create agents that have memory, an update rule, and some amount of homophily by callingfromkalaimportFractionMemoryRuleagents=[InvestorAgent(is_saver=is_saver[i],updates_from_n_last_games=5,update_rule=FractionMemoryRule(fraction=0.75),homophily=0.8)foriinrange(num_nodes)]AgentsAgents should be the starting point of any model. Agents are created by passing arguments that are specific to the model, but that can be broken down into two categories (traits and properties). Once created, an agent should have all of the necessary information to call theupdate()method which for example adds a payoff to the running total of the agent.In order to implement the models in [Ernst04], we define the classInvestorAgentwhich takes the following arguments:is_saver: Boolean indicating whether the agent is a saver or not. This trait is important because it will influence the payoff that our agent will get once they play an opponent that can have the same trait or not.homophily: Normally, agents are paired using the topology of a graph in which they are the nodes. The homophily is an optional probability (a number between [0, 1]) which controls whether agents should preferentially select opponents with the same trait or avoid them entirely.updates_from_n_last_games: Agents can have a memory ofnprevious games. This goes hand-in hand with the...update_rule: A rule which codifies what the agent should do with the outcome of previousngames. Normally, an agent records whether their payoff has been smaller than that of its opponent, so they might decide to change their saver trait after loosing half of thengames, or allngames.Traits and propertiesTraits and properties belong to agents. Roughly speaking, traits were thought as fixed and properties as changing in time, but the distinction is no longer clear-cut and in the future we might decide to deprecate one of the two classes.Memory rulesRules also belong to agents. They handle all the logic of deciding whether the agent should flip its saving strategy based on the pastngames. The following rules are available (ordered from simplest to most general):AnyPastMemoryRuleAllPastMemoryRuleAverageMemoryRuleFractionMemoryRuleWeightedMemoryRuleA description of the rules will follow shortly.GraphsAgents are initialised and organised inside aSimpleGraphwhich encodes the topology, that is, the connections of agents. For the time being, the connections are fixed but in the future this can easily be extended so that edges can be re-wired as a game progresses.StrategiesA strategy encodes what two agents should do when they "encounter" and they play each other. The main method exposed iscalculate_payoff().Following [Ernst04], we coded the classCooperationStrategywhich draws payoffs from a log-normal distribution with mean 1.0 and a variance (and therefore risk) that depends on the specialization of each agent. The specialization depends the two values:differential_inefficient: When a saver meets a non-saver, their specialization is set to1 - differential_inefficientwhich in the paper's notation is $\hat{\eta}$.differential_efficient:  When two savers play each other, they decide to cooperate and they select a higher specialization set to1 + differential_efficient($\hat{\hat{\eta}}$ in the paper).GamesA game is defined by the graph (which itself already contains initialised agents) and the strategy. It handles the logic of matching two (or more) opponents calculating and distributing the payoffs. The most important method isplay_round(), but there are also convenient methods to get summaries such asget_total_wealth()orget_num_savers().In order to implement the models [Ernst04], we useDiscreteTwoByTwoGame.ContributingThe package has a couple of utilities (linters, formatters, etc.) that automatically run checks and help correct simple problems.To run on new contributions, use$pre-commitrun--all-filesYou might find it useful to create an alias, for examplealias pcr='pre-commit run --all-files.Extending the existing modelsIn all cases, we define a base class which can be extended easily to define new models. For example, one could define a new set of agent "traits" like so:classMyNewTraits(BaseAgentTraits):color:strage:intis_tall:boolWhen possible, we have have strived to make all such sub-classes interoperable meaning that other parts of the code tend not to assume any functionality other than the one specified in the base class (there are exceptions to this but are working on getting around them).