Edgeworth Boxed!

Through the most difficult part, I think: the agents trade their goods until they reach an equilibrium. Here is the Python code that does this, formatted by Blogger, which seems to always want to reduce two spaces to one:

"""
edgebox_model.py
An Edgeworth Box model where two agents trade goods.
"""
import logging
import entity
import spatial_agent

TRADE = "trade"

WINE = "wine"
CHEESE = "cheese"

GAIN = 1
LOSE = -1

Accept = True
Refuse = False


def util_func(qty):
    """
    Later, we want to be able to pass in arbitrary util funcs for each good-trader combo.
    """
    return 10 - .5 * qty


class EdgeboxAgent(spatial_agent.SpatialAgent):
    """
    Agents who attempt to trade goods to achieve greater utility.
    We are descending this from SpatialAgent, because later on we want
    traders who can detect local prices but may not know about distant ones
    """

    def __init__(self, name, goal=TRADE):
        super().__init__(name, goal, max_detect=1000.0)
        self.goods = {}
        self.utils = 0


    def act(self):
        """
        Act is called in an interactive loop by code in the base framework
        """

        potential_traders = self.survey_env(TRADE)       
        for t in potential_traders:
            logging.info("Potential trader for "
                    + self.name
                    + " (who has " + " ".join(self.list_goods())
                    + " and utils of " + str(self.utils)
                    + "): " + t.name)
            for g in self.goods:
                if self.goods[g]["endow"] > 0:
                    logging.info(self.name + " is offering " + g + " to " + t.name)
                    t.rec_offer(g, 1, self)


    def endow(self, good, endow):
        if good not in self.goods:
            self.__add_good(good)
        self.goods[good]["endow"] = endow
        for i in range(1, endow):
            self.utils += self.goods[good]["util_func"](i)

        self.pretrade_utils = self.utils


# for the moment all offers are of 1 unit!
    def rec_offer(self, offer_good, amt, counterparty):
        """
        Agent has received an offer of a good, and loops over her goods to
        see if there is a profitable trade. If 'yes,' make a counter-offer.
        """

        util_gain = self.__marginal_util(offer_good, amt, GAIN)
        logging.info(self.name
                + " is looking at a util gain of "
                + str(util_gain)
                + " for good "
                + offer_good)
        for g in self.goods:
            if (g != offer_good) and (self.goods[g]["endow"] > 0):
                util_loss = self.__marginal_util(g, 1, LOSE)
                logging.info(self.name
                     + " is looking at a util loss of "
                     + str(util_loss)
                     + " for good "
                     + g)
                if (util_gain + util_loss) > 0:
                    if(counterparty.rec_reply(
                            offer_good, amt, g, 1, self) is Accept):
                        self.trade(g, counterparty, offer_good)
                        return Accept


    def rec_reply(self, my_good, my_amt, his_good, his_amt, counterparty):
        """
        This is a response to a trade offer this agent has initiated
        """

        util_gain = self.__marginal_util(his_good, his_amt, 1)
        util_loss = self.__marginal_util(my_good, my_amt, -1)
        return (util_gain + util_loss) > 0


    def list_goods(self):
        goods_list = []
        for g in self.goods:
            goods_list.append(g)
        return goods_list


    def trade(self, my_good, counterparty, his_good):
        """
        We actually swap goods, and record the trade in the environment
        """

        logging.info(self.name + " going to trade " + my_good + " for " + his_good)

        self.__gain_lose_good(my_good, LOSE)
        self.__gain_lose_good(his_good, GAIN)
        counterparty.__gain_lose_good(his_good, LOSE)
        counterparty.__gain_lose_good(my_good, GAIN)

        self.env.record_trade(1)


    def util_gain(self):
        return self.utils - self.pretrade_utils


    def __gain_lose_good(self, good, gain_or_lose):
        self.utils += self.__marginal_util(good, 1, gain_or_lose)
        self.goods[good]["endow"] += gain_or_lose


    def __marginal_util(self, good, amt, gain_or_lose):
        g = self.goods[good]
        if gain_or_lose == GAIN:
# we are calling our utility function stored in a dictionary here:
            return g["util_func"](g["endow"] + 1)
        else:
            return -(g["util_func"](g["endow"]))


    def __add_good(self, good):
        self.goods[good] = {"endow": 0, "util_func": util_func}


class EdgeboxEnv(spatial_agent.SpatialEnvironment):
    """
    Contains goods and agents who exchange them.
    """

    def __init__(self, length, height, model_nm=None):
        super().__init__("An Edgeworth Box", length, height, model_nm=model_nm)
        self.do_census = False
        self.trades_this_turn = 0


    def step(self, delay=0):
        super().step(delay=delay)
        self.user.tell("Trades this period: " + str(self.trades_this_turn))
        for a in self.agents:
            print(a.name + " has gained " + str(a.util_gain()))

# any return other than "None" from the step function breaks the interactive loop
        if self.trades_this_turn <= 0:
            return("We've reached equilibrium.")

        self.trades_this_turn = 0

   
    def record_trade(self, amt):
        self.trades_this_turn += amt

Comments

  1. If I may ask:
    •Why all the underscores to start your variable's names?
    •Why not functions instead of OOP?

    ReplyDelete
    Replies
    1. Samson:

      1) None of my variable names start with underscores. There are some *Function* names that start with underscores: that is the python convention for naming a function that should not be called from outside of a class.

      2) The second question strikes me kind of like someone asking, "Why are you traveling to India by airplane, when there are canoes available?"

      Delete
    2. 1: That is what I should've said. I didn't know that was convention.

      2: Is that really the case? It's data in, data out with functions. Seems pretty simply to me which is why I usually can't take it when I find things like ciphers, hashes, and so forth constructed as classes instead of functions.

      Delete
    3. 2. Yes, really. Of course, every program expressed with functions could be expressed in pure machine language with no functions involved. Software engineering is mostly about making programs more comprehensible and more extensible by humans. An object oriented programming is the greatest advance in this regard in the last several decades. So, for instance, what goes on in the program I posted is based on the behavior of the many classes the classes you see in the post are descended from. Really, we have decades of programming experience the testify to this fact.

      Delete
  2. In what scenarios would functions be better?

    ReplyDelete

Post a Comment

Popular posts from this blog

Libertarians, My Libertarians!

"Machine Learning"

"Pre-Galilean" Foolishness