Applications: Economics and International Affairs

Module 3 > Econ/IA


Objectives

 

We will look at two very different applications:

 

3.70 Video:

 


Part A: the gravity model of trade

 

The goal of this section is to examine volume-of-trade data between the U.S. and other countries and ask:

 

Let's start with the data:

 

Before we work with the full dataset, we'll use a smaller dataset with 30 countries to explore a few ideas.

Consider this program:

from datatool import datatool
import numpy as np

dt = datatool()

dt.load_csv('US_trade_2014_small.csv')

# Print the column names
print(dt.get_col_list())

# Extract the data into a numpy array (which won't
# have the column names)
D = dt.get_data_as_array()

# The number of rows, and number of columns: (60, 14)
print(D.shape)

# Print the first 3 rows
for i in range(3):
    print(D[i])

# Print the country name and trade volume of the first 3 rows:
for i in range(3):
    print(D[i,1], D[i,4])
  
 

3.71 Exercise: Download US_trade_2014_small.csv and write up the above in my_trade_analysis.py. You will also need datatool.py Confirm that you get this output.
 

Next, looking over the data, we see that some rows have 'import' data while others have 'export'.

Let's sort so that the 'export' rows are grouped together. The reason for doing so is that we want to separately plot choropleths for import and export volumes.

Pandas (via datatool) provides a convenient way to sort by the values in any subset of columns (ascending or descending):

from datatool import datatool
import numpy as np

def find_first_import_row(D):
    # Assume: D is sorted so that export rows appear first.
    # Find the first import row and return the row number.
    # WRITE YOUR CODE HERE:

dt = datatool()

dt.load_csv('US_trade_2014_small.csv')

# Sorting: The second list describes ascending (or not) for 
# each of the first list items.
dt.sort_by_multiple_columns(['trade_direction','trade_volume'], [True, False])

D = dt.get_data_as_array()

first_import_row = find_first_import_row(D)

print('First import row =', first_import_row)
# Should print 30

# Print all the export rows with trade direction and volume
for i in range(first_import_row):
    print(D[i,1], D[i,3], D[i,4])
  
Note:
 

3.72 Exercise: Fill in the code needed above in my_trade_analysis2.py. Confirm that you get this output.
 

We're now ready to plot a choropleth, with one additional detail to address:

 

Let's plot only the export data:

from datatool import datatool
import numpy as np

def find_first_import_row(D):
    # COPY over your code from the previous exercise:

dt = datatool()

dt.load_csv('US_trade_2014_small.csv')

dt.sort_by_multiple_columns(['trade_direction','trade_volume'], [True, False])

D = dt.get_data_as_array()
first_import_row = find_first_import_row(D)

# Tell datatool to trim the rows from first_import_row onwards.
dt.use_n_rows(first_import_row)

# Color scale. More choices: https://plotly.com/python/builtin-colorscales/
dt.set_color_scale('reds')

# The choropleth_iso3() function uses a standard code for countries.
# The second parameter describes which column to use for heat-map
# like coloring. The third is what to show when the mouse hovers.
dt.choropleth_iso3('iso3', 'trade_volume', 'country')

dt.display()
  
 

3.73 Exercise: Type up the above in my_trade_analysis3.py, filling in the needed code. You should see something like:

 

3.74 Exercise: In my_trade_analysis4.py, write code to display imports (and only imports). Change the color scale to 'blues'. You should get something like:

 

3.75 Exercise: Next, in my_trade_analysis5.py, modify the program in the previous exercise to print (using only the 'import' rows) plot a choropleth of sorted GDP, high to low.

 

3.76 Audio:
 

Now that we've seen plots of imports, exports and gdp ... what do these tell us?

 

The gravity model of trade borrows an idea from physics:

 

3.77 Exercise: Download US_trade_2014.csv and add code to my_gravity_analysis.py to complete a few supporting functions. Study the code to see how the ratio above is calculated from the data. The output expected is described in comments.
 

3.78 Audio:
 

3.79 Exercise: In the previous exercise, you applied the trade model to identify countries with high GDP with less than expected trade, resulting in a list of 7 countries. In your module pdf, explain why U.S. trade with these countries is lower than expected from the gravity model. For example, distance might explain why Australia is in the list. What about the others in the list?
 


Part B: the prisoner's dilemma

 

Start by reading this short article on the topic.

We'll begin with a few observations:

  • The prisoner's dilemma is a type of "game" that formalizes interactions between two players.

  • The formal study of such games is called game theory, an area of study that spreads across mathematics, economics and international negotiation.

  • The most fundamental kind of game is a two-player game in which each player simultaneously makes their move (as opposed to taking turns, as in chess).

  • In a one-shot game like the prisoner's dilemma, each player makes their move and that's the end of the game.

  • In an iterated version, there are multiple games in succession.

  • We will be interested in the iterated prisoner's dilemma

  • As you read in the article, the prisoner's dilemma game has many applications. It might not result in actual strategy, but often clarifies intentions and possibilities in a real-life scenario.
 

First, the rules of a one-shot prisoner's dilemma:

  • In our version (the most common one), a bank has been robbed and two robbery suspects (prisoners) are being separately interrogated.

  • If both stay silent, they can both get out and split the haul.

  • If both implicate the other, neither gets out.

  • If one implicates the other, and the other is silent, the implicator gets out and gets the whole haul.

  • In this formulation each prisoner must choose between two possible moves:
    • Cooperate: "Cooperate" means to keep silent and not rat out the other prisoner.
    • Defect: "Defect" means to rat out or implicate the other prisoner.

  • We'll use Prisoner-0 and Prisoner-1 to denote the two.

  • Thus, there are four possibilities, to which are associated four numeric outcomes (as points):
    • Both cooperate, in which case both get 2 points (both split the haul of 4).
    • Both defect, in which case both get 0 points.
    • Prisoner-0 cooperates, Prisoner-1 defects:
      • Prisoner-0 gets -1 points (a penalty)
      • Prisoner-1 gets 3 points (the haul)
    • Prisoner-0 defects, Prisoner-1 cooperates:
      • Prisoner-0 gets 3 points
      • Prisoner-1 gets -1 points

  • This is often expressed in a table:

  • Because we're going to simulate the playing of the game, we can play repeatedly with different strategies.

  • Our main goal: to evalute the average outcome for each strategy when played against other strategies.
 

The strategies we will evaluate

  • Strategy A: random. Simply pick randomly between "cooperate" and "defect".

  • Strategy B: tit-for-tat. Since this is a repeated game, each player can see what the other player did in the previous round. In tit-for-tat, a player uses the move made by the other player in the previous round.

  • Strategy C: randomized tit-for-tat. A variation of tit-for-tat that applies a little randomness (we'll see why).

  • Strategy D: gain-learning. This approach tries to learn from past games by slowly adjusting a parameter.

  • Strategy E: . You devise one.
 

Let's start with Strategy A.
 

3.80 Exercise: Download prisoner0.py, prisoner1.py, prisoner_game.py, and eval_strategyA.py. Examine the code in prisoner0.py to confirm that the implementation of strategy A is indeed to randomly choose between 'cooperate' and 'defect'. Then execute eval_strategyA.py to confirm this output. Increase the number of iterations to 10000 to see what the average turns out. (Change it back to 10 for submission.)
 

Clearly, one should be able to do better than random. Let's examine a strategy called tit-for-tat:

  If it's your first move, pick 'cooperate'
  Otherwise, play the same move your opponent picked in the last round.
  
  • The idea here is to extend the hand of cooperation (at first). As long as your opponent cooperates, you do too (in the next round). If your opponent defects, you do too in the next round.

  • In terms of coding, notice the parameters passed into a strategy:
    def play_strategyA(my_moves, opponent_moves):
      
    A player gets their own history of moves so far as a list, and their opponent's moves as a list.

  • Thus, the last element in the second list is the most recent move made by the opponent.

  • Although neither random nor tit-for-tat adjust their strategy with each round, the code is structured to allow that:
    def update_strategyA(my_moves, opponent_moves, my_score):
      
    Here, the two lists contain the moves played in the current round, and the score obtained for this player.

  • The idea with "update" is that one can adjust based on the latest score received (as we'll see below).
 

3.81 Exercise: Implement tit-for-tat in your prisoner0.py AND prisoner1.py. That is, write code to apply tit-for-tat in play_strategyB() in each of prisoner0.py and prisoner1.py files. Then, download and execute eval_strategyB.py. Then execute to confirm this output.
 

3.82 Exercise: Change the first part of Strategy-B in prisoner0.py to make the first move 'defect'. What do you observe as a result? Report in your module pdf.
 

We'll make two modifications to tit-for-tat to improve its robustness:

  • One problem is that it is too sensitive to the initial starting move of the opponent.

  • Another is that because it's so deterministic and rigid, tit-for-tat might get stuck in an endless pattern that might drive the result towards perpetually playing 'defect'.

  • Thus, the first modification is to make the first move random.

  • The second is occasionally make a random move, the frequency of which is governed by a parameter \(\beta\) as follows:
    • At each turn, with probability \(\beta\), select a random move.
    • Thus, if \(\beta=0.1\) then for approximately 10% of the time, a random move will be selected.
    • Think of \(\beta\) as an "exploration" or "getting unstuck" move.
    • At each turn, generate a random number between 0 and 1. If that number is less than \(\beta\), pick randomly between 'cooperate' and 'defect'.
    • The idea is, over many such trials, the fraction of such exploration moves will be \(\beta\).

  • We'll call this randomized tit-for-tat and implement it as Strategy C.
 

3.83 Exercise: Implement randomized tit-for-tat by writing code in the play_strategyC() function in each of prisoner0.py and prisoner1.py files. Then, download and execute eval_strategyC.py. Then execute to confirm this output.
 

None of the strategies so far have adjusted "along the way" by learning from prior moves.

Let's now examine the possibility of learning from past moves (and outcomes of those moves):

  • Every program that plays games, such as chess-playing programs, learns by adjusting internal "strategy parameters".

  • Typically, such programs "train" by playing thousands of games and using outcomes to incrementally adjust the parameters.

  • Let's examine a highly simplified single-parameter version for Prisoner's Dilemma.

  • The parameter will be called \(\alpha\) and the way \(\alpha\) will be used to decide a particular move is:
    • Choose 'cooperate' with probability \(\alpha\).
    • That is, generate a random number between 0 and 1. If the number lies between 0 and \(\alpha\), pick 'cooperate'.

  • This part is easy. How should \(\alpha\) be updated?

  • We will use the most recent outcome (the score) to update \(\alpha\) as follows:
    • If your last move was 'cooperate' and you received a positive score, then increment \(\alpha\) slightly.
    • If your last move was 'defect' and you received a negative score, then increment \(\alpha\) slightly.
    • In all other cases, decrement \(\alpha\).

  • Remember that \(\alpha\) is the probability of choosing 'cooperate' so if 'defect' did badly (negative) then we need to increase \(\alpha\).

  • We'll call this strategy gain-learning and implement it in Strategy-D.
 

3.84 Exercise: Implement gain-learning by writing code in the update_strategyD() function in each of prisoner0.py and prisoner1.py files. Note that to increment or decrement alpha, just call incr_alpha() or decr_alpha() respectively. Download and execute eval_strategyD.py. Then execute to confirm this output. In your module pdf, explain the reason for the if-statement in each of the increment/decrement functions.
 

3.85 Audio:
 

Now, let's play the strategies against each other.
 

3.86 Exercise: Download and execute eval_ABCD.py. Then execute to confirm this output. Clearly, we see a small benefit to adjusting the strategy as we go.
 

Now it's your turn.
 

3.87 Exercise: In your prisoner0.py and prisoner1.py, implement your own strategy as Strategy-E. Then download and execute eval_all.py to evaluate. Report your results in your module pdf, explaining your strategy and providing an intuition for its performance.
 

About the Prisoner's Dilemma and game theory:

  • There is an interesting history to Prisoner's Dilemma:
    • A few international competitions were held to allow various competitors to submit strategies in code for the iterated game.
    • Some were quite complex, with hundreds of lines of code.
    • Surprisingly, tit-for-tat turned out to be one of the best performers.
    • One can create an evolutionary version of the iterated game where successive rounds result in knocking out losing strategies.
    • Tit-for-tat wins often enough that this has sometimes been interpreted to describe how cooperation evolved in human and animal social groups.

  • Game theory is an interesting subfield of economics and international affairs (including military strategy).
    • Variations include N-player games, games with random outcomes, games with coalitions, and other such rules.
    • It is generally a highly mathematical field, with analyses of all kinds of games and strategies.
    • At the same time, it's generally easy to write up games and strategies in code to simulate and evaluate.
    • The type of game theory we've examined is quite different from the algorithmic approach used in devising chess-playing programs.
 


Optional further exploration

 

If you'd like to explore further:

  • Try this this TED talk about how the rules of international trade have changed.

  • A book (movie) worth reading (watching) about one of the key contributors to game theory, John Nash: A Beautiful Mind.

  • Try this TED talk on an interesting take on the Prisoner's Dilemma.


Back to Module 3



© 2020, Rahul Simha