5

I'm new to python so this might be a stupid question, however I couldn't find an answer to this anywhere.

I'm trying to find the optimal reaction for a player given the action of another player. The situation is your typical Bertrand price competition for those familiar economics. The code is as follows:

import numpy as np
from scipy.optimize import minimize

class Player:
    def __init__(self):
        self.action = np.random.choice(np.linspace(0, 1, 11))

    def payoff(self, other):
        if self.action < other.action:
            return (1 - self.action) * self.action
        elif self.action == other.action:
            return 0.5 * (1 - self.action) * self.action
        else:
            return 0

    def best_reply(self, other):
        br = minimize(-self.payoff, 0.5, other)
        return br['x']

A = Player()
B = Player()

print(A.best_reply(B))

When I run the above code I get an error of:

TypeError: bad operand type for unary -: 'method'

Can someone explain to me why this is? I was able to circumvent the problem by multiplying the payoffs by -1 and removing the '-' from the best_reply function. However, when I then run the code I get:

TypeError: payoff() takes 2 positional arguments but 3 were given

How come? The only arguments I've given are self (A) and the other player (B). If someone would be able to help me out by explaining what exactly I'm doing wrong and what the correct way of running such code is, I would be extremely grateful. Thank you in advance!

Edit: Added imports to the code

merimursu
  • 53
  • 5
  • 1
    The first argument to `minimize` is supposed to be a function. `-self.payoff` is not a valid operation. You can't negate a function. You can negate the array or number that it returns, but you can't apply at `-` abstractly to the function (or method). – hpaulj Aug 28 '20 at 19:22
  • Off the bat, I see you are calling `-self.payoff` in the `minimize` function without the parameter `other`. Also. to use the scipy minimize function, you need to add `from scipy.optimize import minimize`. – Jacob K Aug 28 '20 at 19:22

2 Answers2

3

Here's how I would do it. Separate the function to be optimized from the class method and have a private static method for the payoff calculation that both methods can utilize.

import numpy as np
from scipy.optimize import minimize

class Player:
    def __init__(self):
        self.action = np.random.choice(np.linspace(0, 1, 11))

    @staticmethod
    def _calc_payoff(a, b):
        if a < b:
            return (1 - a) * a
        elif a == b:
            return 0.5 * (1 - a) * a
        else:
            return 0

    def payoff(self, other):
        return self._calc_payoff(self.action, other.action)

    def best_reply(self, other):
        f = lambda x: 1 - self._calc_payoff(x, other.action)
        br = minimize(f, 0.5)
        return br.x.item()

A = Player()
B = Player()

print(A.best_reply(B))

Is 0.5 the correct result?

Bill
  • 10,323
  • 10
  • 62
  • 85
  • Yes, thanks a bunch! This method works perfectly. 0.5 is the correct answer if the competing player chooses an action above 0.5, otherwise the correct answer is marginally below the action of the opposing player. Your method is able to solve the best rely in any case, although I did have to multiply the payoffs by -1 in order to find the maximum. – merimursu Aug 30 '20 at 17:45
  • Ah yes, good point. That is a mistake. I will update the code accordingly. – Bill Aug 30 '20 at 20:33
0

payoff is a method and you cannot do math operations with methods, only with the return values of them. Just like Python can't calculate what -print is, it can't calculate what -self.payoff is.

For the second error refer to the documentation of the minimize method. I think the problem is that you need to change how you call minimize. Try this and please post the result:

br = minimize(self.payoff, 0.5, (other,))
Filip
  • 898
  • 1
  • 8
  • 24