alt text

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)>

Here are some questions to ponder.

When is A's optimal strategy equal to a=0? And when is it equal to a=1?

When is B's optimal strategy equal to b=0? And when is it equal to b=1?

When is A's payoff higher than B's payoff? And how does this relate to the first and second derivatives of the Benefit and Cost functions?

alt text