Skip to content

Commit 45b405f

Browse files
authored
Merge pull request #1521 from QingyuanFang/master
Add an alternative way of implementing Tauchen's method to make_tauchen_ar1()
2 parents ea4931f + f5f9fe7 commit 45b405f

File tree

3 files changed

+65
-12
lines changed

3 files changed

+65
-12
lines changed

HARK/distributions/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"Uniform",
2323
"MarkovProcess",
2424
"add_discrete_outcome_constant_mean",
25+
"make_tauchen_ar1",
2526
]
2627

2728
from HARK.distributions.base import (
@@ -53,4 +54,5 @@
5354
combine_indep_dstns,
5455
distr_of_function,
5556
expected,
57+
make_tauchen_ar1,
5658
)

HARK/distributions/utils.py

+27-12
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,10 @@ def make_markov_approx_to_normal_by_monte_carlo(x_grid, mu, sigma, N_draws=10000
180180
return p_vec
181181

182182

183-
def make_tauchen_ar1(N, sigma=1.0, ar_1=0.9, bound=3.0):
183+
def make_tauchen_ar1(N, sigma=1.0, ar_1=0.9, bound=3.0, inflendpoint=True):
184184
"""
185185
Function to return a discretized version of an AR1 process.
186-
See https://www.fperri.net/TEACHING/macrotheory08/numerical.pdf for details
186+
See http://www.fperri.net/TEACHING/macrotheory08/numerical.pdf for details
187187
188188
Parameters
189189
----------
@@ -196,6 +196,12 @@ def make_tauchen_ar1(N, sigma=1.0, ar_1=0.9, bound=3.0):
196196
bound: float
197197
The highest (lowest) grid point will be bound (-bound) multiplied by the unconditional
198198
standard deviation of the process
199+
inflendpoint: Bool
200+
If True: implement the standard method as in Tauchen (1986):
201+
assign the probability of jumping to a point outside the grid to the closest endpoint
202+
If False: implement an alternative method:
203+
discard the probability of jumping to a point outside the grid, effectively
204+
reassigning it to the remaining points in proportion to their probability of being reached
199205
200206
Returns
201207
-------
@@ -208,16 +214,25 @@ def make_tauchen_ar1(N, sigma=1.0, ar_1=0.9, bound=3.0):
208214
y = np.linspace(-yN, yN, N)
209215
d = y[1] - y[0]
210216
trans_matrix = np.ones((N, N))
211-
for j in range(N):
212-
for k_1 in range(N - 2):
213-
k = k_1 + 1
214-
trans_matrix[j, k] = stats.norm.cdf(
215-
(y[k] + d / 2.0 - ar_1 * y[j]) / sigma
216-
) - stats.norm.cdf((y[k] - d / 2.0 - ar_1 * y[j]) / sigma)
217-
trans_matrix[j, 0] = stats.norm.cdf((y[0] + d / 2.0 - ar_1 * y[j]) / sigma)
218-
trans_matrix[j, N - 1] = 1.0 - stats.norm.cdf(
219-
(y[N - 1] - d / 2.0 - ar_1 * y[j]) / sigma
220-
)
217+
if inflendpoint:
218+
for j in range(N):
219+
for k_1 in range(N - 2):
220+
k = k_1 + 1
221+
trans_matrix[j, k] = stats.norm.cdf(
222+
(y[k] + d / 2.0 - ar_1 * y[j]) / sigma
223+
) - stats.norm.cdf((y[k] - d / 2.0 - ar_1 * y[j]) / sigma)
224+
trans_matrix[j, 0] = stats.norm.cdf((y[0] + d / 2.0 - ar_1 * y[j]) / sigma)
225+
trans_matrix[j, N - 1] = 1.0 - stats.norm.cdf(
226+
(y[N - 1] - d / 2.0 - ar_1 * y[j]) / sigma
227+
)
228+
else:
229+
for j in range(N):
230+
for k in range(N):
231+
trans_matrix[j, k] = stats.norm.cdf(
232+
(y[k] + d / 2.0 - ar_1 * y[j]) / sigma
233+
) - stats.norm.cdf((y[k] - d / 2.0 - ar_1 * y[j]) / sigma)
234+
## normalize: each row sums to 1
235+
trans_matrix = trans_matrix / trans_matrix.sum(axis=1)[:, np.newaxis]
221236

222237
return y, trans_matrix
223238

HARK/tests/test_distribution.py

+36
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
combine_indep_dstns,
2323
distr_of_function,
2424
expected,
25+
make_tauchen_ar1,
2526
)
2627
from HARK.tests import HARK_PRECISION
2728

@@ -657,3 +658,38 @@ def transition(shocks, state):
657658

658659
assert np.all(exp1["m"] == exp2["m"]).item()
659660
assert np.all(exp1["n"] == exp2["n"]).item()
661+
662+
663+
class TestTauchenAR1(unittest.TestCase):
664+
def test_tauchen(self):
665+
# Test with a simple AR(1) process
666+
N = 5
667+
sigma = 1.0
668+
ar_1 = 0.9
669+
bound = 3.0
670+
671+
# By default, inflendpoint = True
672+
standard = make_tauchen_ar1(N, sigma, ar_1, bound)
673+
alternative = make_tauchen_ar1(N, sigma, ar_1, bound, inflendpoint=False)
674+
675+
# Check that the grid points of the two methods are identical
676+
self.assertTrue(np.all(np.equal(standard[0], alternative[0])))
677+
678+
# Check the shape of the transition matrix
679+
self.assertEqual(standard[1].shape, (N, N))
680+
self.assertEqual(alternative[1].shape, (N, N))
681+
682+
# Check that the sum of each row in the transition matrix is 1
683+
self.assertTrue(np.allclose(np.sum(standard[1], axis=1), np.ones(N)))
684+
self.assertTrue(np.allclose(np.sum(alternative[1], axis=1), np.ones(N)))
685+
686+
# Check that [k]-th column ./ [k-1]-th column are identical (k = 3, ..., N-1)
687+
# Note: the first and the last column of the 'standard' transition matrix are inflated
688+
if N > 3:
689+
for i in range(2, N - 1):
690+
self.assertTrue(
691+
np.allclose(
692+
standard[1][:, i] * alternative[1][:, i - 1],
693+
alternative[1][:, i] * standard[1][:, i - 1],
694+
)
695+
)

0 commit comments

Comments
 (0)