In [1]:
## Import various packages that enable this notebook to work

import sys
!{sys.executable} -m pip install scipy

import numpy
import pylab
from scipy import optimize

Requirement already satisfied: scipy in /opt/conda/lib/python3.6/site-packages (1.1.0)

In [2]:
## Define the Benefit and Cost Functions

def Benefit(x):
return x*(4-x)

def Cost(x):
return x*x

In [3]:
## Define the Payoff functions for both players

def APayoff(a,b): return Benefit(a+b)-Cost(a)
def BPayoff(a,b): return Benefit(a+b)-Cost(b)

In [4]:
## Suppose A invests a=0.6.  How should B respond?
## Let's try all the values from 0 to 1, in increments of 0.1

for b in range(0,11):
print("An investment of b =", b/10, "yields a payoff of", round(BPayoff(0.6,b/10),4))

An investment of b = 0.0 yields a payoff of 2.04
An investment of b = 0.1 yields a payoff of 2.3
An investment of b = 0.2 yields a payoff of 2.52
An investment of b = 0.3 yields a payoff of 2.7
An investment of b = 0.4 yields a payoff of 2.84
An investment of b = 0.5 yields a payoff of 2.94
An investment of b = 0.6 yields a payoff of 3.0
An investment of b = 0.7 yields a payoff of 3.02
An investment of b = 0.8 yields a payoff of 3.0
An investment of b = 0.9 yields a payoff of 2.94
An investment of b = 1.0 yields a payoff of 2.84

In [5]:
## Given an input value for the variable a, this function determines the optimal value of b.
## Thus, after A invests some amount a, this function calculates B's best response move b that maximizes his payoff,
## i.e., minimizes the value of -BPayoff.  We do this to take advantage of Python's Optimize.Minimize function.

def GetBestB(a):
def func(x): return -BPayoff(a,x)
BestB = optimize.minimize_scalar(func,bounds=(0,1), method='bounded')
b = BestB.x
return b

In [6]:
GetBestB(0.6)

Out[6]:
0.6999999999999995
In [7]:
## Thus, if A selects a=0.6, B will respond with b=0.7, which is the move that maximizes B's payoff.
## Below we see the payoffs for both players.

APayoff(0.6,0.7),BPayoff(0.6,0.7)

Out[7]:
(3.15, 3.02)
In [8]:
## Now that we can caculate GetBestB(a) for all values of a, A wants to select the value of a that maximizes her payoff.
## The function below determines this value of a.

def GetBestA():
def f(x): return -APayoff(x,GetBestB(x))
BestA = optimize.minimize_scalar(f,bounds=(0,1), method='bounded')
a = BestA.x
return a

In [9]:
## Now we have Python determine the optimal values of a and b.

a = GetBestA()
b = GetBestB(a)
print("A's optimal strategy is a =", a)
print("B's optimal strategy is b =", b)
print("The payoff to A is", APayoff(a,b), "and the payoff to B is", BPayoff(a,b))

A's optimal strategy is a = 0.40000000000000124
B's optimal strategy is b = 0.7999999999999986
The payoff to A is 3.1999999999999984 and the payoff to B is 2.7200000000000015

In [10]:
## Let's generalize this to other Benefit and Cost functions.
## We'll assume that the Benefit and Cost functions are quadratic, i.e, Benefit(x)=px^2+qx+r and Cost(x)=sx^2+tx+u
## The function below is simply a cut+paste of what we showed above.

def CalculatePayoffs(p,q,r,s,t,u):

def Benefit(x): return p*x*x + q*x + r
def Cost(x): return s*x*x + t*x + u

def APayoff(a,b): return Benefit(a+b)-Cost(a)
def BPayoff(a,b): return Benefit(a+b)-Cost(b)

def GetBestB(a):
def func(x): return -BPayoff(a,x)
BestB = optimize.minimize_scalar(func,bounds=(0,1), method='bounded')
b = BestB.x
return b

def GetBestA():
def f(x): return -APayoff(x,GetBestB(x))
BestA = optimize.minimize_scalar(f,bounds=(0,1), method='bounded')
a = BestA.x
return a

a = GetBestA()
b = GetBestB(a)
print("A's optimal strategy is a =", a)
print("B's optimal strategy is b =", b)
print("The payoff to A is", APayoff(a,b), "and the payoff to B is", BPayoff(a,b))
print()
return

In [11]:
## Let's double-check our answers for our original question, where Benefit(x)=x(4-x)=-x^2+4x+0 and Cost(x)=x^2.
## Thus, we have p=-1, q=4, r=0, s=1, t=0, u=0 in this example.

CalculatePayoffs(-1,4,0,1,0,0)

A's optimal strategy is a = 0.39999999999999575
B's optimal strategy is b = 0.8000000000000033
The payoff to A is 3.200000000000002 and the payoff to B is 2.7199999999999935


In [12]:
## Now use the SLIDERS below to adjust the values of p,q,r,s,t,u.  Instantly we see the optimal values and payoffs.
## Do you notice anything interesting?

from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets

interact(CalculatePayoffs,
p=widgets.IntSlider(min=-10, max=10, step=1, value=-1),
q=widgets.IntSlider(min=-10, max=10, step=1, value=4),
r=widgets.IntSlider(min=-10, max=10, step=1, value=0),
s=widgets.IntSlider(min=-10, max=10, step=1, value=1),
t=widgets.IntSlider(min=-10, max=10, step=1, value=0),
u=widgets.IntSlider(min=-10, max=10, step=1, value=0))

Out[12]:
<function __main__.CalculatePayoffs(p, q, r, s, t, u)>