Equilibrium in the Besley-Case (1995) Model
By Bas Machielsen
May 19, 2022
Introduction
Besley and Case (1995) have a paper about the influence of term limits for politicians on policy behavior. They use a dynamic game. Politicians can serve either 1, or 2, periods, depending on whether they are retained. In these periods, politicians choose to put in effort. Putting in more or less effort is costly to different extents, according to their type. The chosen effort probabilistically affects a policy outcome. An infinitely-lived voter makes decisions to retain or dismiss the politician after observing the outcome. Their solution, based on Banks and Sundaram (1998) involves a cut-off equilibrium, with voters reelecting an incumbent policy if the probabilistically-influenced policy variable realization is higher than some threshold \(r_{min}\)
.
One of their predictions is that the equilibrium amount of effort put in by the politicians who are in their first period is higher than in their last period, irrespective of the politician’s type. Also, politicians who are in their second period are more likely to be high-type politicians, so that cross-sectionally, it seems that politicians in the second period have put in more effort than politicians in the first period, even though, if one looks at a “panel” of politicians over two periods, the first period effort is higher than the second period. The interpretation is that the policy outcome is better when there is politicians who do not face a term limit, irrespective of the type (ability) of the politicians. As they put it:
If two terms are allowed, then incumbents who give higher first-term payoffs to voters are more likely to be retained to serve a second term. Those in their last term put in less effort and give lower payoffs to voters, on average, compared with their first term in office.
Their model is fairly general, so in this blog post, I wanted to give an easier to understand version for a specific case using Python to solve for some expressions that are not analytically soluble. I also want to demonstrate how to use standard computational techniques in macro for this specific case.
Set-up
In the Besley and Case (1995) model, there can be \(k\)
types of politicians, ordered by ability \(\omega_1 < \dots < \omega_k\)
. They occur with probabilities \(\pi_1, \dots, \pi_k\)
. In my setup, there are only two types, \(\omega_1 < \omega_2\)
with \(\omega_1 = 3\)
and \(\omega_2 = 9\)
. \(\omega_1\)
occurs with probability \(\pi_1 = 0.2\)
and \(\omega_2\)
with probability \(\pi_2 = 0.8\)
. The politicians per-period utility is:
$$ v(\alpha, \omega_i) = \alpha - \frac{\alpha^2}{\omega_i} $$ If the politician is in power, otherwise, utility is zero. This reflects an expected reward proportional to effort, and the utility is negative in effort, meaning that effort is costly, but less negative for higher-quality types.
Effort is mapped into policy outcomes, \(r\)
, probabilistically via \(F(\alpha, r)\)
. In the Besley and Coate (1995) setting, this can be any distribution satisfying the monotone likelihood ratio property. In my setting, I take \(F(\alpha, r)\)
to be \(\Phi(\alpha)\)
, meaning \(R \sim \mathcal{N}(\alpha, 1)\)
. The voter’s one-period utility is simply the realized outcome \(r\)
.
Optimization of the politician
In the second period, when retained, the politician will simply optimize their one-period utility:
$$ a^s_i = \frac{w_i}{4} $$
In the first period, the politician will optimize:
$$ a^l_i={\arg\max} \{ v(\alpha,\omega_i) +\delta \cdot [ \text{Pr} (r \in R(\sigma) | \alpha) \cdot v(a_s(\omega_i), \omega_i)] \} $$
In words, this means the politician will take into account their second-period optimal strategy, and the likelihood that their realized \(r\)
is greater than the voter’s retention \(r\)
. Given a retention rule, that probability is further specified as Pr(\( r > r_{\min} \)).
Optimization of the voter
After observing the realized \(r\)
after one period, the voter computes the belief that the incumbent is of type 1 (the low type) as follows:
$$ \beta_1 (r) = \frac{\pi_1 \cdot f(r; a_l (\omega_1))}{\pi_1 \cdot f(r; a_l (\omega_1)) + \pi_2 \cdot f(r; a_l(\omega_2))} $$ The expected utility to the voter of retention is then:
$$
v_2 (r, \alpha^s, \alpha^l) = \sum_{i=1}^2 \int \beta_i (r) v(\hat{r})f(\hat{r}|\alpha^s_i) d\hat{r}
$$
which corresponds to the probability that the player is of type \(i\)
, and then integrating over the entire range of potential \(r\)
s, weighted by the density, given that they put in the optimal second period effort corresponding to their type.
Banks and Sundaram (1993) then show that the Bellman equation for the voter’s problem is:
$$
W(1, r, p) = \max \left\{v_2 (r, \alpha^s, \alpha^l) + \alpha W(0, r, \pi), W(0, r, \pi) \right\}
$$
where the state vector consists of \(\{ 0, 1\}\)
, meaning how many periods the politician can still be retained, the realized \(r\)
in the period, and the beliefs of the voter regarding the incumbent type \(p\)
. In general, the last element of the tripe \((\{0,1\}, r, p)\)
is the \(n\)
-dimensional simplex, but because there are only 2 types, it is now summarized by \(\beta_1 (r)\)
.
The Bellman equation for the state \(W(0,r,p)\)
is equal to the expected immediate reward plus the probability-weighted discounted future reward for each \(W(1,r,p)\)
:
$$
W(0, r, p) = \sum_{i=1}^2 \pi_i \cdot \int \hat{r} f(\hat{r}|a^s_i) d\hat{r} + \delta \int W(1, \hat{r}, \beta_1 (\hat{r})) \cdot \sum_{k=1}^2 \pi_k \cdot f(\hat{r} | \alpha^s_k) d \hat{r}
$$
Now that we have the Bellman, we can use value-function iteration to derive the optimal \(r\)
. We also need to take into account equilibrium conditions, so that (i) the voters respond to the optimal retention \(r\)
according to the value function, and (ii) the politicians always set the best retention \(r\)
given a particular estimate of the value function. Sundaram and Banks (1993) also prove that the dynamic programming theorems for this to be a contraction mapping hold.
Implementation
Below, I load the libraries, and I store a Python class called RetentionModel
.
#%matplotlib inline
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (11, 5) #set default figure size
import numpy as np
from interpolation import interp
from scipy.optimize import minimize_scalar, bisect
from scipy.stats import norm
import scipy.integrate as integrate
import scipy.interpolate as interpolate
from numba import jit, njit, prange, float64, int32
from numba.experimental import jitclass
from tqdm import tqdm
class RetentionModel:
def __init__(self,delta=0.95,alpha=0.2,types=(3, 9),types_freq=(0.2, 0.8),up=6,low=2):
self.delta, self.alpha, self.types, self.types_freq, self.up, self.low = delta, alpha, types, types_freq, up, low
#'create a 3-dimensional grid'
#'optimal r probably somewhere in between the means' of the efforts
self.x_grid = np.mgrid[0:2, 2.5:3.5:0.1, 0:1.05:0.1].reshape(3, -1).T
def evaluated_int(self, draws, value_f, typ, retention_r):
"""Evaluate an integral"""
beta_1 = self.beta_1
types_freq = self.types_freq
n = len(draws)
realization = [value_f([1, i, beta_1(i, retention_r)])[0] for i in draws]
samples = np.dot(draws, realization)
return types_freq[typ]*np.mean(realization)
def vot_util(self, x):
"""voter utility function"""
return x
def f(self, r, effort):
"""normal pdf of r according to exerted effort"""
return norm.pdf(r, loc = effort, scale = 1)
def F(self, r, effort):
"""normal cdf of r according to exerted effort"""
return norm.cdf(r, loc = effort, scale = 1)
def expected_utility(self,effort, typ, retention_r, delta):
F = self.F
delta = self.delta
"""
Return the total expected utility a priori from
the second period and then from the first period
as a function of the retention r, discount rate and exterted effort
"""
out = effort - (effort**2)/typ + delta*(1-F(retention_r, effort))*typ/4
return out
def effort_first_period(self, typ, retention_r):
"""optimal effort in the first period given type"""
#changed upper limit to typ, from 5
return maximize(self.expected_utility, 1e-15, typ, (typ, retention_r, self.delta))[0]
def effort_second_period(self, typ):
"""optimal effort in the second period given type and realized r"""
return typ/2
def beta_1(self, r, retention_r):
"""
probability that type = type_1 after observing a specific r
"""
f = self.f
types = self.types
types_freq = self.types_freq
num = types_freq[0]*f(r, self.effort_first_period(types[0], retention_r))
denom = num + types_freq[1]*f(r, self.effort_first_period(types[1], retention_r))
return num/denom
def v_2(self, r, retention_r, eff_sp_t1, eff_sp_t2):
"""
Calculate the second period expected utility given a certain realized r
and the optimal efforts by the players
"""
types, types_freq, f, vot_util = self.types, self.types_freq, self.f, self.vot_util
beta_1 = self.beta_1
def integrand(x, e):
return vot_util(x)*f(x, e)
#'under linear utility, this just simplifies to the expected value'
out1 = beta_1(r, retention_r)*integrate.quad(integrand, a=-np.inf, b=np.inf, args=eff_sp_t1)[0]
out2 = (1-beta_1(r, retention_r))*integrate.quad(integrand, a=-np.inf, b=np.inf, args=eff_sp_t2)[0]
return out1 + out2
def state_action_value(self, action, state, v_array, retention_r):
"""
Given state x_grid=[{0,1}, r, beliefs], and choice actions (k,d) and a guess v,
what is the maximum (RHS bellman equation)
"""
v_2, types, types_freq, beta_1, delta, alpha, f = self.v_2, self.types, self.types_freq, self.beta_1, self.delta, self.alpha, self.f
vot_util, effort_first_period = self.vot_util, self.effort_first_period
evaluated_int = self.evaluated_int
#a multidimensional linear interpolator - make v a function
v = interpolate.LinearNDInterpolator(self.x_grid, v_array, fill_value=0)
if state[0] == 0:
# return is eq. to instantaneous reward + expectation over all future 1 states
out1 = types_freq[0]*effort_first_period(types[0], retention_r)
out2 = types_freq[1]*effort_first_period(types[1], retention_r)
def integrand(x, *args): #v, beta_1, types, types_freq, effort_first_period, retention_r):
f = self.f
term1 = types_freq[0]*f(x, effort_first_period(types[0], retention_r))
term2 = types_freq[1]*f(x, effort_first_period(types[1], retention_r))
out = v([1, x, beta_1(x, retention_r)])[0]*(term1 + term2)
return out
def calc_integral(func, a, b, n, *args):
delta = (b-a)/n
points = np.linspace(a, b, n)
values = [func(i, *args) for i in points]
#values = func(points, *args)
return delta*np.sum(values)
out3 = calc_integral(integrand, 0, 13, 30,
v,
beta_1,
types,
types_freq,
effort_first_period,
retention_r)
return out1 + out2 + alpha*(out3)
elif state[0] == 1:
#both dismiss and keep are possible
if action == "k":
one = alpha*v([0, state[1], types_freq[0]])[0]
two = v_2(state[1],
retention_r,
self.effort_second_period(types[0]),
self.effort_second_period(types[1]))
return one + two
elif action == "d":
return v([0, state[1], types_freq[0]])[0]
else:
raise ValueError("No action chosen")
I also define a custom maximizer, to take into account that the voter maximizes over a dichotomous \( \{ \text{dismiss, retain} \} \) decision. The politician uses a normal (continuous) maximizer, which I also implement. Finally, I use a Monte Carlo approximation for most complicated integrals I calculate:
def maximize(g, a, b, args):
"""
Maximize the function g over the interval [a, b].
We use the fact that the maximizer of g on any interval is
also the minimizer of -g. The tuple args collects any extra
arguments to g.
Returns the maximal value and the maximizer.
"""
objective = lambda x: -g(x, *args)
result = minimize_scalar(objective, bounds=(a, b), method='bounded')
maximizer, maximum = result.x, -result.fun
return maximizer, maximum
# Write a maximizer like the concept maximizer above to find the optimal value for each point on the grid
def maximize_sav(function, args):
"""
a function that maximizes the sav for a given state, v_array, and retention_r
and on the basis of choices k,v
returns the maximizer (the best action) and the maximum
"""
state = args[0]
v_array = args[1]
retention_r = args[2]
if state[0] == 0:
action = "d"
maximum = function("d", state, v_array, retention_r)
elif state[0] == 1:
dismiss = function("d", state, v_array, retention_r)
keep = function("k", state, v_array, retention_r)
if dismiss > keep:
action = "d"
maximum = dismiss
else:
action = "k"
maximum = keep
return action, maximum
def monte_carlo_uniform(func,
a=-10,
b=10,
n=150):
subsets = np.arange(0, n+1, n/10)
steps = n/10
u = np.zeros(n)
for i in range(10):
start = int(subsets[i])
end = int(subsets[i+1])
u[start:end] = np.random.uniform(low=i/10, high=(i+1)/10, size=end-start)
np.random.shuffle(u)
u_func = func(a+(b-a)*u)
s = ((b-a)/n)*u_func.sum()
return s
Value Function Iteration
Then, I instantiate an instance of the model and apply value function iteration:
model = RetentionModel(delta=0.99, alpha = 0.2)
def T(v, rm, retention_r):
"""
The Bellman operator. Updates the guess of the value function.
* ce is an instance of CakeEating
* v is an array representing a guess of the value function
"""
v_new = np.empty_like(v)
p_new = [None]*len(v_new)
for i, x in enumerate(rm.x_grid):
# Maximize RHS of Bellman equation at state x
#replace by my custom maximize function
sol = maximize_sav(rm.state_action_value, args=(x, v, retention_r))
p_new[i] = sol[0]
v_new[i] = sol[1]
new_retention_r = rm.x_grid[p_new.index('k')][1]
return p_new, v_new, new_retention_r
def compute_value_function(rm,
tol=1e-3,
max_iter=200,
verbose=True,
print_skip=1):
# Set up loop
v = np.zeros(len(rm.x_grid)) # Initial guess
rr = 2 # Initial guess
i = 0
error = tol + 1
while i < max_iter and error > tol:
update = T(v, rm, rr)
p_new = update[0]
v_new = update[1]
rr_new = update[2]
error = np.max(np.abs(v - v_new))
i += 1
if verbose and i % print_skip == 0:
print(f"Error at iteration {i} is {error}.")
v = v_new
rr = rr_new
if i == max_iter:
print("Failed to converge!")
if verbose and i < max_iter:
print(f"\nConverged in {i} iterations.")
return p_new, v_new, rr_new
Solution
The procedure converges and the model is saved under out
:
out = compute_value_function(model)
## Error at iteration 1 is 4.09024743100576.
## Error at iteration 2 is 1.608588398221574.
## Error at iteration 3 is 0.17448573956574087.
## Error at iteration 4 is 0.14479551187642237.
## Error at iteration 5 is 0.06623950959972902.
## Error at iteration 6 is 0.03690126342482625.
## Error at iteration 7 is 0.03690126342482625.
## Error at iteration 8 is 0.0003930360580230996.
##
## Converged in 8 iterations.
and the optimal retention \(r\)
for this specific case is:
out[2]
## 3.3
.. which is an interior solution to the problem. It is also easily verifiable that this solution satisfies the characterization by Banks and Sundaram (1993), in that there is a cut-off \( r \), with no lower \( r \) being used for retention.
I hope this has been useful! If you have any tips or remarks, let me know, and thanks for reading.
- Posted on:
- May 19, 2022
- Length:
- 11 minute read, 2174 words
- See Also: