Search code examples
pythonscipyscipy-optimizescipy-optimize-minimize

How to use scipy's minimize within a class?


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


Solution

  • 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?