Skip to content

Instantly share code, notes, and snippets.

@justinian336
Last active June 1, 2020 05:18
Show Gist options
  • Save justinian336/2677bce43ec4434e7c4da48a4adffda3 to your computer and use it in GitHub Desktop.
Save justinian336/2677bce43ec4434e7c4da48a4adffda3 to your computer and use it in GitHub Desktop.
Structural Estimation Series III
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "Structural Estimation Series III のコピー",
"provenance": [],
"collapsed_sections": [],
"authorship_tag": "ABX9TyOfkDDULvSlCjLiLbau8jV/",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/justinian336/2677bce43ec4434e7c4da48a4adffda3/structural-estimation-series-iii.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"metadata": {
"id": "kyCg_ghoyeT5",
"colab_type": "code",
"outputId": "d302b999-9ec0-46e1-b1e0-7468cccb7823",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 51
}
},
"source": [
"import numpy as np\n",
"from scipy.special import softmax\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"sns.set()"
],
"execution_count": 1,
"outputs": [
{
"output_type": "stream",
"text": [
"/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.\n",
" import pandas.util.testing as tm\n"
],
"name": "stderr"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "kM8Qw4838gIL",
"colab_type": "code",
"colab": {}
},
"source": [
"# Define the DDC Problem:\n",
"\n",
"# Number of choices\n",
"n_choices = 2\n",
"\n",
"# Number of states\n",
"n_states = 5\n",
"\n",
"# The discount factor\n",
"discount_factor = 0.99\n",
"\n",
"# The transition matrix:\n",
"transition_matrix = np.array(\n",
" [\n",
" [\n",
" [1., 0., 0., 0., 0.],\n",
" [0.1, 0.9, 0., 0., 0.],\n",
" [0., 0.1, 0.9, 0., 0.],\n",
" [0., 0., 0.1, 0.9, 0.],\n",
" [0., 0., 0., 0.1, 0.9]\n",
" ],\n",
" [\n",
" [1., 0., 0., 0., 0.],\n",
" [1., 0., 0., 0., 0.],\n",
" [1., 0., 0., 0., 0.],\n",
" [1., 0., 0., 0., 0.],\n",
" [1., 0., 0., 0., 0.]\n",
" ]\n",
" ]\n",
")\n",
"# The first element of this array (transition_matrix[0]) represents the transition function for the case when *not replacement* (choice 0) is chosen.\n",
"# The second element is the stochastic matrix for the case when choice 1 (replacing) is chosen."
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "HpvxcjeK84sF",
"colab_type": "code",
"outputId": "c22c2ae1-e7aa-4ddd-d4fe-c100ce2ca0f2",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"# Make sure that the shape of the transition matrix is (2, 5, 5):\n",
"transition_matrix.shape"
],
"execution_count": 3,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"(2, 5, 5)"
]
},
"metadata": {
"tags": []
},
"execution_count": 3
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "qgYv-yyc9rmZ",
"colab_type": "code",
"outputId": "91719c06-fd4f-43ad-b8cc-d6f4ba77b6d4",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 51
}
},
"source": [
"# Make sure that the sum of values across the columns of each matrix (axis 2) equals to 1:\n",
"transition_matrix.sum(axis=2)"
],
"execution_count": 4,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"array([[1., 1., 1., 1., 1.],\n",
" [1., 1., 1., 1., 1.]])"
]
},
"metadata": {
"tags": []
},
"execution_count": 4
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "UHbicMJ7yhdB",
"colab_type": "code",
"colab": {}
},
"source": [
"# Define the utility function: it returns the utility level for each combination of choices and states\n",
"# Its output must be a (2, 5, 1) array.\n",
"def utility_function(theta, n_choices, n_states):\n",
" # theta contains the utility function parameters. R is the replacement cost of the \n",
" # engine, and mile_cost is the additional mantainance cost\n",
" # per mile run. \n",
" [mile_cost, replacement_cost] = theta\n",
"\n",
" # This line produces vectors containing all the possible combinations of states and choices\n",
" miles = np.arange(n_states)\n",
"\n",
" # Here we calculate the utility for each possible combination of action and state:\n",
" # If action is *not replacing*, the utility is the cost per mile times the number of \n",
" # miles since the last replacement\n",
" u_no_replacement = mile_cost*miles\n",
"\n",
" # If the replacement is performed, the utility is just the replacement cost, no matter \n",
" # the state. We use np.repeat to create an array containing the replacement cost for\n",
" # each possible state.\n",
" u_replacement = np.repeat(replacement_cost, n_states)\n",
"\n",
" # Finally, the utility is an array \n",
" u = np.array([u_no_replacement, u_replacement]).reshape((n_choices, n_states, 1))\n",
"\n",
" # Finally, we reshape the utility function and return. Remember that U is a (n_choices, n_states, 1) tensor.\n",
" return u"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "5neXmvfD-TIu",
"colab_type": "code",
"outputId": "4606d0b8-585e-45a3-c5e9-40db796e136b",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"# Let's see if the shape is correct...\n",
"cost_per_mile = -1\n",
"replacement_cost = -2.5\n",
"some_theta = [cost_per_mile, replacement_cost]\n",
"utility = utility_function(some_theta, n_choices, n_states)\n",
"utility.shape"
],
"execution_count": 6,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"(2, 5, 1)"
]
},
"metadata": {
"tags": []
},
"execution_count": 6
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "EBC0cft1-f4p",
"colab_type": "code",
"outputId": "e534934d-caa2-4a62-f263-01288183c3bd",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 204
}
},
"source": [
"# Now let's see its output:\n",
"utility"
],
"execution_count": 7,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"array([[[ 0. ],\n",
" [-1. ],\n",
" [-2. ],\n",
" [-3. ],\n",
" [-4. ]],\n",
"\n",
" [[-2.5],\n",
" [-2.5],\n",
" [-2.5],\n",
" [-2.5],\n",
" [-2.5]]])"
]
},
"metadata": {
"tags": []
},
"execution_count": 7
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "lhlTdQQJykpa",
"colab_type": "code",
"colab": {}
},
"source": [
"# Let's define a function that obtains the discounted value for each combination of choice\n",
"# and state. It calculates V = U + \\beta*Fv, where v is the **expected discounted value**.\n",
"# Note that v has dimensions (n_states, 1).\n",
"# This function is necessary to implement Phi Map and returns a (2, 5, 1) array.\n",
"def get_discounted_value(utility, discount_factor, transition_matrix, v):\n",
" n_choices, n_states, _ = transition_matrix.shape\n",
" discounted_value = utility + discount_factor*np.array([transition_matrix[i].dot(v) for i in range(n_choices)])\n",
" return discounted_value"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "5c_teiIwyoIv",
"colab_type": "code",
"colab": {}
},
"source": [
"# Phi Map maps the conditional choice probabilities (ccp) into the Value space.\n",
"# Its output is a (5, 1) array.\n",
"def phi_map(p, transition_matrix, theta, utility_function, discount_factor, n_choices, n_states):\n",
" n_choices, n_states, _ = p.shape\n",
" utility = utility_function(theta, n_choices, n_states)\n",
" denominator = np.identity(n_states) - discount_factor*((p*transition_matrix).sum(axis=0))\n",
" denominator = np.linalg.inv(denominator)\n",
" # In the following line I'm using np.nan_to_num to replace any possible ln(0) with zeros.\n",
" # Don't worry, it doesn't affect convergence\n",
" numerator = (p*(utility + np.euler_gamma - np.nan_to_num(np.log(p), 0))).sum(axis=0)\n",
" v = denominator.dot(numerator)\n",
" return v"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "Q9uYVxH4yqlp",
"colab_type": "code",
"colab": {}
},
"source": [
"# Finally, Lambda Map is a function that maps from the Value space into the Probability Space.\n",
"def lambda_map(v, theta, discount_factor, transition_matrix):\n",
" utility = utility_function(theta, n_choices, n_states)\n",
" V = get_discounted_value(utility, discount_factor, transition_matrix, v)\n",
" return softmax(V, axis=0)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "dOyIhrj3zGHU",
"colab_type": "code",
"colab": {}
},
"source": [
"# Let's see the fixed point into action!\n",
"# Define some random values for V. I'm not fixing the seed to convince you that the fixed\n",
"# point is reached regardless of the initial values.\n",
"# You'll see different values, but that's the point (^▽^)\n",
"initial_v = np.random.uniform(size=(n_choices, n_states, 1))\n",
"\n",
"# I also initialize the vector of conditional choice probabilities with random values\n",
"initial_p = np.random.uniform(size=(n_choices, n_states, 1))\n",
"# Make sure that the probabilities for every choice add up to one in each state.\n",
"initial_p = (initial_p / initial_p.sum(axis=0))\n",
"\n",
"# We will iterate many times using Phi Map and Lambda Map to see how Values and\n",
"# CCPs converge.\n",
"repetitions = 5\n",
"\n",
"# Let's store the values of the squared deviations of each iteration to visualize the fixed\n",
"# point magic\n",
"root_squared_deviations = np.empty(shape=(2, repetitions))\n",
"\n",
"p = initial_p\n",
"v = (p*initial_v).sum(axis=0)\n",
"\n",
"some_other_theta = [1,0]\n",
"for i in range(repetitions):\n",
" # P -> V\n",
" new_v = phi_map(p, transition_matrix, some_other_theta, utility_function, discount_factor, n_choices, n_states)\n",
"\n",
" # V -> P\n",
" new_p = lambda_map(new_v, some_other_theta, discount_factor, transition_matrix)\n",
" root_squared_deviation_v = np.sqrt(((new_v - v)**2).sum())\n",
" root_squared_deviation_p = np.sqrt(((new_p - p)**2).sum())\n",
" \n",
" root_squared_deviations[0, i] = root_squared_deviation_v\n",
" root_squared_deviations[1, i] = root_squared_deviation_p\n",
"\n",
" p = new_p\n",
" v = new_v"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "pB4_UNzyNI7T",
"colab_type": "code",
"outputId": "913444a5-727f-4a90-b85b-f31fee75ee4b",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 284
}
},
"source": [
"# Let's visualize now the squared deviations to see if the values of V and P converged:\n",
"_, ax = plt.subplots(1, 2, sharex=True)\n",
"\n",
"for i, title in enumerate(['Squared Deviations of V', 'Squared Deviations of P']):\n",
" ax[i].plot(np.arange(repetitions), root_squared_deviations[i])\n",
" ax[i].set_title(title)"
],
"execution_count": 12,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 2 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "y_cQZbT3zPoq",
"colab_type": "code",
"outputId": "df134657-c4cb-40b2-a8fd-86eb0345bf3e",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 102
}
},
"source": [
"# It looks like it converged quite fast!\n",
"# Now let's see if the expected discounted value has converged to a fixed point.\n",
"# This is v:\n",
"v"
],
"execution_count": 13,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"array([[127.03628455],\n",
" [130.07404485],\n",
" [141.78482845],\n",
" [161.59553837],\n",
" [188.76306388]])"
]
},
"metadata": {
"tags": []
},
"execution_count": 13
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "wAhtUnyI2LvX",
"colab_type": "code",
"colab": {}
},
"source": [
"# And this is the expected discounted value calculated from Equation (9)\n",
"fv = np.array([transition_matrix[i].dot(v) for i in range(n_choices)])\n",
"expected_v_calculated = (p*(utility_function(some_other_theta, n_choices, n_states) + np.euler_gamma - np.log(p) + discount_factor*fv)).sum(axis=0)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "Z6n4513kK-DD",
"colab_type": "code",
"outputId": "f0405c39-f419-48b4-86d0-7a7a85c0af9f",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 102
}
},
"source": [
"# Let's see if it is the same as v:\n",
"expected_v_calculated"
],
"execution_count": 15,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"array([[127.03628455],\n",
" [130.07404485],\n",
" [141.78482845],\n",
" [161.59553837],\n",
" [188.76306388]])"
]
},
"metadata": {
"tags": []
},
"execution_count": 15
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "JQGGhxOtLV_i",
"colab_type": "code",
"colab": {}
},
"source": [
"# It looks like it converged!"
],
"execution_count": 0,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment