Private Non-Renewable Resource Model

Randall Romero Aguilar, PhD

This demo is based on the original Matlab demo accompanying the Computational Economics and Finance 2001 textbook by Mario Miranda and Paul Fackler.

Original (Matlab) CompEcon file: demdp09.m

Running this file requires the Python version of CompEcon. This can be installed with pip by running

!pip install compecon --upgrade

Last updated: 2022-Oct-23


About

Profit maximizing mine owner must decide how much ore to extract

  • States

    • s: ore stock

  • Actions

    • q: ore extracted and sold

  • Parameters

    • a0,a1: demand function parameters

    • b0,b1: supply function parameters

    • \(\delta\): discount factor

import numpy as np
import matplotlib.pyplot as plt
from compecon import BasisSpline, DPmodel, DPoptions, qnwnorm

Model parameters

a0, a1, b0, b1, δ = 5, 0.8, 7, 1.0, 0.9

State space

The state variable is s=”Stock”, which is restricted to \(s\in[0, 10]\).

Here, we represent it with a cubic spline basis, with \(n=101\) nodes.

n, smin, smax = 101, 0, 10
basis = BasisSpline(n, smin, smax, labels=['Ore Stock'])

Action space

The choice variable q=”Ore extracted” must be nonnegative.

def bounds(s, i=None, j=None):
    return np.zeros_like(s), s[:]

Reward function

The reward function is the utility of harvesting \(q\) units.

def reward(s, q, i=None, j=None):
    u = (a0-b0+b1*s)*q - (a1+b1/2)*q**2
    ux = (a0-b0+b1*s) - (2*a1+b1)*q
    uxx = -2*(a1+b1/2)*np.ones_like(s)    
    return u, ux, uxx

State transition function

Next period, the stock will be equal that is \(s' = \alpha (s-q) - 0.5\beta(s-q)^2\)

def transition(s, q, i=None, j=None, in_=None, e=None):
    g = s-q
    gx = -np.ones_like(s)
    gxx = np.zeros_like(s)
    return g, gx, gxx

Model structure

The value of wealth \(s\) satisfies the Bellman equation

\[\begin{equation*} V(s) = \max_q\left\{(a_0-b_0+b_1 s)q - (a_1+b_1/2)q^2 + \delta V(s') \right\} \end{equation*}\]

To solve and simulate this model,use the CompEcon class DPmodel

model = DPmodel(basis, reward, transition, bounds,
                       x=['Ore Extracted'],
                       discount=δ)

Solving the model

Solving the growth model by collocation

S = model.solve()
S.head()
Solving infinite-horizon model collocation equation by Newton's method
iter change       time    
------------------------------
   0       1.9e+01    0.0156
   1       3.5e+00    0.0334
   2       2.2e-01    0.0516
   3       1.0e-03    0.0673
   4       2.2e-08    0.0673
   5       1.9e-14    0.0829
Elapsed Time =    0.08 Seconds
Ore Stock value resid Ore Extracted
Ore Stock
0.000000 0.000000 0.000000e+00 0.000000e+00 0.000000e+00
0.009911 0.009911 -3.725252e-16 -3.725252e-17 3.016346e-15
0.019822 0.019822 -3.915019e-16 -3.915019e-17 3.170001e-15
0.029732 0.029732 -1.557981e-16 -1.557981e-17 1.261501e-15
0.039643 0.039643 2.357181e-16 2.357181e-17 -1.908615e-15

DPmodel.solve returns a pandas DataFrame with the following data:

Compute and print abandonment point

sstar = (b0-a0)/b1
print(f'Abandonment Point = {sstar:5.2f}')
Abandonment Point =  2.00

We are also interested in the shadow price of ore (the first derivative of the value function).

S['shadow price'] = model.Value(S['Ore Stock'],1)

Plotting the results

Optimal Policy

fig1, ax = plt.subplots()
ax.set(title='Optimal Extraction', xlabel='Ore Stock', ylabel='Ore Extracted')
ax.plot(S['Ore Extracted'])

ax.plot(sstar, 0, 'wo')
ax.annotate(f"$s^*$ = {sstar:.2f}",[sstar, 0], va='top');
../../_images/09 Private Non-Renewable Resource Model_24_0.png

Value Function

fig2, ax = plt.subplots()

ax.set(title='Value Function', xlabel='Ore Stock', ylabel='Value')
ax.plot(S.value);
../../_images/09 Private Non-Renewable Resource Model_26_0.png

Shadow Price Function

fig3,ax = plt.subplots()
ax.set(title='Shadow Price Function', xlabel='Ore Stock', ylabel='Shadow Price')
ax.plot(S['shadow price']);
../../_images/09 Private Non-Renewable Resource Model_28_0.png

Residual

fig4, ax = plt.subplots()
ax.set(title='Bellman Equation Residual', xlabel='Ore Stock', ylabel='Residual')
ax.axhline(0, color='white')
ax.plot(S['resid'])
ax.ticklabel_format(style='sci', axis='y', scilimits=(-1,1))
../../_images/09 Private Non-Renewable Resource Model_30_0.png

Simulating the model

We simulate 21 periods of the model starting from \(s=s_{\max}\)

T = 21
data = model.simulate(T, smax)
data.head()
time Ore Stock Ore Extracted
0 0 10.000000 1.506241
1 1 8.493759 1.222646
2 2 7.271113 0.992446
3 3 6.278667 0.805588
4 4 5.473079 0.653912

Simulated State and Policy Paths

fig5, ax = plt.subplots()
ax.set(title='State and Policy Paths',
       xlabel='Period', 
       ylabel='Stock/Extraction',
       xlim=[0, T + 0.5])

ax.plot(data[['Ore Stock', 'Ore Extracted']])
ax.axhline(sstar, linestyle='--')


ax.annotate(f'$s^* = {sstar:.2f}$', [T, sstar], ha='right', va='top', color='C0');
../../_images/09 Private Non-Renewable Resource Model_35_0.png