Profit-maximizing entrepreneur must decide when to replace an aging asset.
%matplotlib inline
from warnings import simplefilter
simplefilter('ignore')
from compecon import BasisSpline, DPmodel
from compecon.quad import qnwnorm
from demos.setup import np, plt, demo
The maximum age of the asset is $A=6$, production function coefficients are $\alpha =[50.0, -2.5, -2.5]$, net replacement cost is $\kappa = 40$, long-run mean unit profit is $\bar{p} = 1$, the unit profit autoregression coefficient is $\gamma = 0.5$, the standard deviation of unit profit shock is $\sigma = 0.15$, and the discount factor is $\delta = 0.9$.
A = 6
alpha = np.array([50, -2.5, -2.5])
kappa = 40
pbar = 1.0
gamma = 0.5
sigma = 0.15
delta = 0.9
The state variables are the current unit profit $p$ (continuous) and the age of asset $a\in\{1,2,\dots,A\}$.
Here, we approximate the unit profit with a cubic spline basis of $n=200$ nodes, over the interval $p\in[0,2]$.
n = 200
pmin, pmax = 0.0, 2.0
basis = BasisSpline(n, pmin, pmax, labels=['unit profit'])
There is only one choice variable $j$: whether to keep(0) or replace(1) the asset.
The instant profit depends on the asset age and whether it is replaced. An asset of age $a=A$ must be replaced. If the asset is replaced, the profit is $50p$ minus the cost of replacement $\kappa$; otherwise the profit depends on the age of the asset, $(\alpha_0 + \alpha_1 a + \alpha_2 a^2)p$.
def profit(p, x, i, j):
a = i + 1
if j or a == A:
return p * 50 - kappa
else:
return p * (alpha[0] + alpha[1] * a + alpha[2] * a ** 2 )
The unit profit $p$ follows a Markov Process \begin{equation}p' = \bar{p} + \gamma(p-\bar{p}) + \epsilon \end{equation} where $\epsilon \sim N(0,\sigma^2)$.
def transition(p, x, i, j, in_, e):
return pbar + gamma * (p - pbar) + e
The continuous shock must be discretized. Here we use Gauss-Legendre quadrature to obtain nodes and weights defining a discrete distribution that matches the first 10 moments of the Normal distribution (this is achieved with $m=5$ nodes and weights).
m = 5
e, w = qnwnorm(m,0,sigma ** 2)
On the other hand, the age of the asset is deterministic: if it is replaced now it will be new ($a=0$) next period; otherwise its age will be $a+1$ next period if current age is $a$.
h = np.zeros((2, A))
h[0, :-1] = np.arange(1, A)
model = DPmodel(basis, profit, transition,
i=[a + 1 for a in range(A)],
j=['keep', 'replace'],
discount=delta, e=e, w=w, h=h)
The solve
method returns a pandas DataFrame
.
S = model.solve()
fig = demo.qplot('unit profit', 'value_j', 'i',
data=S,
main='Action-Contingent Value Functions',
xlab='Net Unit Profit',
ylab='Value')
ni, nj = model.dims['ni', 'nj']
vr = S['value_j'].reshape(ni, nj, -1)
pr = S['unit profit'].reshape(ni, nj, -1)[0, 0]
fig.draw()
print('Critical Replacement Profit\n')
for a in range(A-1):
pcrit = np.interp(0.0, vr[a, 1] - vr[a, 0], pr, np.nan, np.nan)
vcrit = np.interp(pcrit, pr, vr[a, 0])
if np.isnan(pcrit):
continue
demo.annotate(pcrit, vcrit, '$p^*_' + str(a+1) + '$', 'wo',
(0, 0), fs=11, ms=18)
print(' Age {:2d} Profit {:5.2f}'.format(a, pcrit))
plt.xlim(pmin,pmax)
plt.show()
S['resid2'] = 100*S.resid / S.value
demo.qplot('unit profit', 'resid2','i',
data=S,
geom='line',
main='Bellman Equation Residual',
xlab='Net Unit Profit',
ylab='Percent Residual')
T = 50
nrep = 10000
sinit = np.full(nrep, pbar)
iinit = 0
data = model.simulate(T,sinit,iinit, seed=945)
frm = '\t{:<10s} = {:5.2f}'
print('\nErgodic Means')
print(frm.format('Price', data['unit profit'].mean()))
print(frm.format('Age', data.i.mean()))
print('\nErgodic Standard Deviations')
print(frm.format('Price', data['unit profit'].std()))
print(frm.format('Age', data.i.std()))
demo.qplot('time', 'unit profit', '_rep',
data=data[data['_rep'] < 3],
geom='line',
main='Simulated and Expected Price',
ylab='Net Unit Profit',
xlab='Period')
data[['time', 'i']].groupby('time').mean().plot(legend=False)
plt.title('Expected Machine Age')
plt.xlabel('Period')
plt.ylabel('Age')