Skip to content

Commit

Permalink
more detail kbar
Browse files Browse the repository at this point in the history
  • Loading branch information
jhconning committed Jan 27, 2024
1 parent 4f19f51 commit 8c00fdc
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 43 deletions.
143 changes: 116 additions & 27 deletions notebooks/Contract.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
"""
Solves for and analyzes renegotiation-proof commitment
contracts. See https://github.com/jhconning/commitment
contracts. See https://jhconning.github.io/commitments
This version allows 2 period or 3 period version.
__author__ = "Jonathan Conning"
__author__ = "@author: "Karna Basu"
Expand All @@ -26,7 +28,7 @@ def __init__(self, beta=0.8, y=None): # constructor default params.
self.y = y

def __repr__(self):
return 'Contract(beta=%s, y=%s)' % (self.beta, self.y)
return 'Contract(beta=%s, periods=%s, y=%s)' % (self.beta, self.periods, self.y)

def print_params(self):
""" print out parameters """
Expand All @@ -39,14 +41,14 @@ def params(self):
btr = self.beta**(1/self.rho)
return self.beta, self.rho, btr, self.kappa, self.y

def u(self,ct):
def u(self, ct):
""" utility function """
if self.rho==1:
return np.log(ct)
else:
return (ct**(1-self.rho))/(1-self.rho)

def PV(self,c):
def PV(self, c):
"""discounted present value of any stream c"""
return c[0] + np.sum(c[1:])

Expand All @@ -66,7 +68,7 @@ def negPVU(self, c):
return - self.PVU(c, self.beta)

def indif(self, ubar, beta):
""" returns idc(c1) function from u(c1, c2) =ubar for graphing
""" returns idc(c1) function from u(c1, c2)=ubar for graphing
indifference curves in c1-c2 space. If beta = 1, will describe
self 0's preferences """
if self.rho==1:
Expand All @@ -81,14 +83,14 @@ def idc(c1):
return idc

def isoprofit(self, prfbar, y):
""" returns profit(c1, c2) for graphing isoprofit lines in c1-c2 space.
""" returns isoprf(c1) implied by profit(c1, c2)=y for graphing isoprofit lines in c1-c2 space.
"""
def isoprf(c1):
"""isoprofit function isoprf(c1) """
return np.array(y[1] + y[2] - prfbar) - c1
return np.sum(y[1:]) - prfbar - c1
return isoprf

def reneg(self,c):
def reneg(self, c):
""" Renegotiated monopoly contract offered to period-1-self if
c_0 is past but (c_1,c_2) now replaced by (cr_1, cr_2)"""

Expand All @@ -103,20 +105,23 @@ def reneg(self,c):
b = (1 + btr)**(1/(rho-1))
cr1 = a*b
cr2 = cr1*btr
return np.array([c[0],cr1,cr2])
return np.array([c[0], cr1, cr2])

def noreneg(self, c):
""" no-renegotiation constraint; same for comp or monop
used in optimization
"""
btr = self.beta**(1/self.rho)
return (self.u(c[1]) + self.beta * self.u(c[2])
- (1+btr) * self.u( (c[1]+c[2]-self.kappa)/(1+btr)) )
beta, _, btr, kappa, _ = self.params()
return (self.u(c[1]) + beta * self.u(c[2])
- (1+btr) * self.u( (c[1]+c[2]-kappa)/(1+btr)) )

# for drawing no-renegotiation constraint
def rpc(self, s):
'''return points along the renegotiation-proof constraint
corresponding to resources s sent into period 1'''
corresponding to resources s sent into period 1
$$\u(c_1) + \beta \cdot u(s-c_1) = \frac{u(s-\kappa)}{1+ \beta^\frac{1}{\rho}} \cdot (1+ \beta^\frac{1}{\rho})$$
'''
beta, _, btr, kap, _ = self.params()
c1P = (s-kap)/(1+btr)
ub = (1+btr)*self.u(c1P)
Expand All @@ -143,10 +148,10 @@ def f(c1):


class Competitive(Contract): # build on contract class
""" Class for solving competitive equilibrium contracts """
""" Initialize class for solving competitive equilibrium contracts """
def __init__(self, beta):
super(Competitive,self).__init__(beta) # inherits Contract properties
self.kappa = 0 # cost of renegotiation
super().__init__(beta) # inherits Contract properties
self.kappa = 0 # cost of renegotiation

def __repr__(self):
return 'Competitive(beta=%s, y=%s)' % (self.beta, self.y)
Expand Down Expand Up @@ -202,19 +207,19 @@ def reneg_proof(self):
elif self.kappa ==0.0:
return self.ownsmooth()
else:
xg = self.ownsmooth()
res = minimize(self.negPVU, xg,
initial_guess = self.ownsmooth()
res = minimize(self.negPVU, initial_guess,
method='COBYLA',
constraints=(
{'type': 'ineq', 'fun':self.bankPC},
{'type': 'ineq', 'fun':self.noreneg} ))
return res.x
return res.x if res.success else None


class Monopoly(Contract): # build on contract class
""" Class for solving Monopoly equilibrium contracts """
def __init__(self,beta):
super(Monopoly,self).__init__(beta) # inherit parent class properties
super().__init__(beta) # inherit parent class properties
self.kappa = 0 # cost of renegotiation

def __repr__(self):
Expand Down Expand Up @@ -276,13 +281,13 @@ def reneg_proof(self):
elif self.kappa ==0:
return self.ownsmooth()
else:
xg = self.ownsmooth()
res = minimize(self.PV, xg,
initial_guess = self.ownsmooth()
res = minimize(self.PV, initial_guess,
method='COBYLA',
constraints=(
{'type': 'ineq', 'fun':self.participation_cons},
{'type': 'ineq', 'fun':self.noreneg} ))
return res.x
return res.x if res.success else None


def reneg_proof2(self):
Expand All @@ -302,6 +307,90 @@ def f(c0):
c2rp = Y - c0rp - c1rp

return np.array([c0rp, c1rp, c2rp])


"""
TWO-PERIOD VERSION
To do: fcommit forward
"""


class Competitive2(Competitive): # build on contract class
""" Class for solving competitive equilibrium contracts
Most functionality inherited from Competitive class, here only making adjustments
needed for 2 period version
"""
def __init__(self, beta):
super(Competitive2, self).__init__(beta) # inherits properties from Competitive(Contract)
self.kappa = 0 # cost of renegotiation

def __repr__(self):
return 'Competitive2(beta=%s, y=%s)' % (self.beta, self.y)

def fcommit(self):
"""competitive optimal full commitment contractwith period0 self
from closed form solution for CRRA"""
btr = self.beta**(1/self.rho)
return np.array([1,btr])*self.PV(self.y)/(1 + btr)

def kbar(self):
'''Renegotiation cost necessary to sustain full commitment
competitive contract'''
rho = self.rho
if (rho == 1):
rho = 0.999 # cheap trick to deal with special case
btr = self.beta ** (1 / rho)
c1F = np.sum(self.y) * btr / (1 + 2 * btr)
A = (2 - (1 + btr) * ((1 + self.beta) / (1 + btr))
** (1 / (1 - rho)))
return A * c1F

def ownsmooth(self):
"""contract if have to smooth by oneself without contract
from closed form solution for CRRA with kappa=0"""
beta, rho = self.beta, self.rho
btr = beta**(1/rho)
Y = self.PV(self.y)
lam = (beta+btr)/(1+btr)
ltr = lam**(1/rho)
c0 = Y/( 1+(1+btr)*ltr )
c1 = (Y-c0)/(1+btr)
return np.array( [c0, c1, btr*c1])

def creneg(self, c):
""" Renegotiated competitive contract offered to period-1-self if
c_0 is past but (c_1,c_2) now replaced by (cr_1, cr_2)"""
beta, rho = self.beta, self.rho
btr = beta**(1/rho)
cr1 = (c[1]+c[2])/(1+btr)
cr2 = cr1*btr
return np.array([c[0],cr1, cr2])

def bankPC(self,c):
return (self.PV(self.y) - self.PV(c))


def reneg_proof(self):
"""Optimum renegotiation-proof contract for any kappa
Solved for analytically if kappa=0, numerically otherwise"""
if self.kappa >= self.kbar():
return self.fcommit()
elif self.kappa ==0.0:
return self.ownsmooth()
else:
xg = self.ownsmooth()
res = minimize(self.negPVU, xg,
method='COBYLA',
constraints=(
{'type': 'ineq', 'fun':self.bankPC},
{'type': 'ineq', 'fun':self.noreneg} ))
return res.x




if __name__ == "__main__":

Expand All @@ -321,9 +410,9 @@ def f(c0):
print("reneg from cCF: ",cCr)

cC.kappa = 0
print('kappa=0', cC.opt())
print('kappa=0', cC.reneg_proof())
cC.kappa = 1
print('kappa=1', cC.opt())
print('kappa=1', cC.reneg_proof())

print()
print("Monopoly contracts:")
Expand All @@ -340,9 +429,9 @@ def f(c0):
print(f'kbar = {cM.kbar():.3f}')
print("reneg from cMF: ",cMr)
cM.kappa = 0
print('kappa=0', cM.opt())
print('kappa=0', cM.reneg_proof())

cM.kappa = 1
print('kappa=5', cM.opt())
print('kappa=5', cM.reneg_proof())
print()

Loading

0 comments on commit 8c00fdc

Please sign in to comment.