In [1]:
from HARK.utilities import plotFuncs
from time import process_time
from copy import deepcopy, copy
import numpy as np
from HARK.ConsumptionSaving.ConsIndShockModel import init_idiosyncratic_shocks
from HARK.ConsumptionSaving.ConsMarkovModel import MarkovConsumerType
from HARK.distribution import DiscreteDistribution
mystr = lambda number: "{:.4f}".format(number)
do_simulation = True

In [2]:
# Define the Markov transition matrix for serially correlated unemployment
unemp_length = 5  # Averange length of unemployment spell
urate_good = 0.05  # Unemployment rate when economy is in good state
urate_bad = 0.12  # Unemployment rate when economy is in bad state
bust_prob = 0.01  # Probability of economy switching from good to bad
recession_length = 20  # Averange length of bad state
p_reemploy = 1.0 / unemp_length
p_unemploy_good = p_reemploy * urate_good / (1 - urate_good)
p_unemploy_bad = p_reemploy * urate_bad / (1 - urate_bad)
boom_prob = 1.0 / recession_length
MrkvArray = np.array(
    [
        [
            (1 - p_unemploy_good) * (1 - bust_prob),
            p_unemploy_good * (1 - bust_prob),
            (1 - p_unemploy_good) * bust_prob,
            p_unemploy_good * bust_prob,
        ],
        [
            p_reemploy * (1 - bust_prob),
            (1 - p_reemploy) * (1 - bust_prob),
            p_reemploy * bust_prob,
            (1 - p_reemploy) * bust_prob,
        ],
        [
            (1 - p_unemploy_bad) * boom_prob,
            p_unemploy_bad * boom_prob,
            (1 - p_unemploy_bad) * (1 - boom_prob),
            p_unemploy_bad * (1 - boom_prob),
        ],
        [
            p_reemploy * boom_prob,
            (1 - p_reemploy) * boom_prob,
            p_reemploy * (1 - boom_prob),
            (1 - p_reemploy) * (1 - boom_prob),
        ],
    ]
)

In [3]:
# Make a consumer with serially correlated unemployment, subject to boom and bust cycles
init_serial_unemployment = copy(init_idiosyncratic_shocks)
init_serial_unemployment["MrkvArray"] = [MrkvArray]
init_serial_unemployment["UnempPrb"] = 0  # to make income distribution when employed
init_serial_unemployment["global_markov"] = False
SerialUnemploymentExample = MarkovConsumerType(**init_serial_unemployment)
SerialUnemploymentExample.cycles = 0
SerialUnemploymentExample.vFuncBool = False  # for easy toggling here

In [4]:
# Replace the default (lognormal) income distribution with a custom one
employed_income_dist = DiscreteDistribution(np.ones(1), [np.ones(1), np.ones(1)])  # Definitely get income
unemployed_income_dist = DiscreteDistribution(np.ones(1), [np.ones(1), np.zeros(1)]) # Definitely don't
SerialUnemploymentExample.IncomeDstn = [
    [
        employed_income_dist,
        unemployed_income_dist,
        employed_income_dist,
        unemployed_income_dist,
    ]
]

In [5]:
# Interest factor, permanent growth rates, and survival probabilities are constant arrays
SerialUnemploymentExample.Rfree = np.array(4 * [SerialUnemploymentExample.Rfree])
SerialUnemploymentExample.PermGroFac = [
    np.array(4 * SerialUnemploymentExample.PermGroFac)
]
SerialUnemploymentExample.LivPrb = [SerialUnemploymentExample.LivPrb * np.ones(4)]

In [6]:
# Solve the serial unemployment consumer's problem and display solution
start_time = process_time()
SerialUnemploymentExample.solve()
end_time = process_time()
print(
    "Solving a Markov consumer with serially correlated unemployment took "
    + mystr(end_time - start_time)
    + " seconds."
)
print("Consumption functions for each discrete state:")
plotFuncs(SerialUnemploymentExample.solution[0].cFunc, 0, 50)
if SerialUnemploymentExample.vFuncBool:
    print("Value functions for each discrete state:")
    plotFuncs(SerialUnemploymentExample.solution[0].vFunc, 5, 50)


Solving a Markov consumer with serially correlated unemployment took 0.2855 seconds.
Consumption functions for each discrete state:

In [7]:
# Simulate some data; results stored in cHist, mNrmNow_hist, cNrmNow_hist, and MrkvNow_hist
if do_simulation:
    SerialUnemploymentExample.T_sim = 120
    SerialUnemploymentExample.MrkvPrbsInit = [0.25, 0.25, 0.25, 0.25]
    SerialUnemploymentExample.track_vars = ["mNrmNow", "cNrmNow"]
    SerialUnemploymentExample.makeShockHistory()  # This is optional
    SerialUnemploymentExample.initializeSim()
    SerialUnemploymentExample.simulate()

In [8]:
# Make a consumer who occasionally gets "unemployment immunity" for a fixed period
UnempPrb = 0.05  # Probability of becoming unemployed each period
ImmunityPrb = 0.01  # Probability of becoming "immune" to unemployment
ImmunityT = 6  # Number of periods of immunity

In [9]:
StateCount = ImmunityT + 1  # Total number of Markov states
IncomeDstnReg = DiscreteDistribution(
    np.array([1 - UnempPrb, UnempPrb]),
    [np.array([1.0, 1.0]),
     np.array([1.0 / (1.0 - UnempPrb), 0.0])]
)  # Ordinary income distribution
IncomeDstnImm = DiscreteDistribution(
    np.array([1.0]),
    [np.array([1.0]),
     np.array([1.0])]
)
IncomeDstn = [IncomeDstnReg] + ImmunityT * [
    IncomeDstnImm
]  # Income distribution for each Markov state, in a list

In [10]:
# Make the Markov transition array.  MrkvArray[i,j] is the probability of transitioning
# to state j in period t+1 from state i in period t.
MrkvArray = np.zeros((StateCount, StateCount))
MrkvArray[0, 0] = (
    1.0 - ImmunityPrb
)  # Probability of not becoming immune in ordinary state: stay in ordinary state
MrkvArray[
    0, ImmunityT
] = (
    ImmunityPrb
)  # Probability of becoming immune in ordinary state: begin immunity periods
for j in range(ImmunityT):
    MrkvArray[
        j + 1, j
    ] = (
        1.0
    )  # When immune, have 100% chance of transition to state with one fewer immunity periods remaining

In [11]:
init_unemployment_immunity = copy(init_idiosyncratic_shocks)
init_unemployment_immunity["MrkvArray"] = [MrkvArray]
ImmunityExample = MarkovConsumerType(**init_unemployment_immunity)
ImmunityExample.assignParameters(
    Rfree=np.array(np.array(StateCount * [1.03])),  # Interest factor same in all states
    PermGroFac=[
        np.array(StateCount * [1.01])
    ],  # Permanent growth factor same in all states
    LivPrb=[np.array(StateCount * [0.98])],  # Same survival probability in all states
    BoroCnstArt=None,  # No artificial borrowing constraint
    cycles=0,
)  # Infinite horizon
ImmunityExample.IncomeDstn = [IncomeDstn]

In [12]:
# Solve the unemployment immunity problem and display the consumption functions
start_time = process_time()
ImmunityExample.solve()
end_time = process_time()
print(
    'Solving an "unemployment immunity" consumer took '
    + mystr(end_time - start_time)
    + " seconds."
)
print("Consumption functions for each discrete state:")
mNrmMin = np.min([ImmunityExample.solution[0].mNrmMin[j] for j in range(StateCount)])
plotFuncs(ImmunityExample.solution[0].cFunc, mNrmMin, 10)


Solving an "unemployment immunity" consumer took 0.3666 seconds.
Consumption functions for each discrete state:
/home/sb/projects/econ-ark/HARK/HARK/interpolation.py:1710: RuntimeWarning: All-NaN slice encountered
  y = np.nanmin(fx,axis=1)

In [13]:
# Make a consumer with serially correlated permanent income growth
UnempPrb = 0.05  # Unemployment probability
StateCount = 5  # Number of permanent income growth rates
Persistence = (
    0.5
)  # Probability of getting the same permanent income growth rate next period

In [14]:
IncomeDstnReg = DiscreteDistribution(
    np.array([1 - UnempPrb, UnempPrb]),
    [np.array([1.0, 1.0]),
     np.array([1.0, 0.0])]
)
IncomeDstn = StateCount * [
    IncomeDstnReg
]  # Same simple income distribution in each state

In [15]:
# Make the state transition array for this type: Persistence probability of remaining in the same state, equiprobable otherwise
MrkvArray = Persistence * np.eye(StateCount) + (1.0 / StateCount) * (
    1.0 - Persistence
) * np.ones((StateCount, StateCount))

In [16]:
init_serial_growth = copy(init_idiosyncratic_shocks)
init_serial_growth["MrkvArray"] = [MrkvArray]
SerialGroExample = MarkovConsumerType(**init_serial_growth)
SerialGroExample.assignParameters(
    Rfree=np.array(
        np.array(StateCount * [1.03])
    ),  # Same interest factor in each Markov state
    PermGroFac=[
        np.array([0.97, 0.99, 1.01, 1.03, 1.05])
    ],  # Different permanent growth factor in each Markov state
    LivPrb=[np.array(StateCount * [0.98])],  # Same survival probability in all states
    cycles=0,
)
SerialGroExample.IncomeDstn = [IncomeDstn]

In [17]:
# Solve the serially correlated permanent growth shock problem and display the consumption functions
start_time = process_time()
SerialGroExample.solve()
end_time = process_time()
print(
    "Solving a serially correlated growth consumer took "
    + mystr(end_time - start_time)
    + " seconds."
)
print("Consumption functions for each discrete state:")
plotFuncs(SerialGroExample.solution[0].cFunc, 0, 10)


Solving a serially correlated growth consumer took 0.3781 seconds.
Consumption functions for each discrete state:

In [18]:
# Make a consumer with serially correlated interest factors
SerialRExample = deepcopy(SerialGroExample)  # Same as the last problem...
SerialRExample.assignParameters(
    PermGroFac=[
        np.array(StateCount * [1.01])
    ],  # ...but now the permanent growth factor is constant...
    Rfree=np.array([1.01, 1.02, 1.03, 1.04, 1.05]),
)  # ...and the interest factor is what varies across states

In [19]:
# Solve the serially correlated interest rate problem and display the consumption functions
start_time = process_time()
SerialRExample.solve()
end_time = process_time()
print(
    "Solving a serially correlated interest consumer took "
    + mystr(end_time - start_time)
    + " seconds."
)
print("Consumption functions for each discrete state:")
plotFuncs(SerialRExample.solution[0].cFunc, 0, 10)


Solving a serially correlated interest consumer took 0.2305 seconds.
Consumption functions for each discrete state: