1

I am trying to solve a minimization problem following this structure:

res=minimize(objective_fnc, x0, args=param, method='Nelder-Mead)

I need to pass a series of arguments that are not par of the optimization variables but parameters to change the conditions/boundaries of the problem in a more general way. For instance, the variable param is a tuple with the next parameters: param = (p1,p2,p3)

Whenever I call the objective_fnc I need to pass the initial conditions x0 and the tuples with the parameters to use these parameters inside the objective function. For instance, objective_fnc(x0,param)

The problem is that I get this error: objective_fnc() takes 2 positional arguments but 4 were given

I know that if I pass the initial conditions and the parameters (without being part of a tuple, this way: objective_fnc(x0,p1,p2,p3)) it works, but I want to write it in a more simplified way in case I need to pass additional parameters later on.

Here you have minimal reproducible example that works, where all the arguments were passed one by one. But if I change p1,p2,p3 for `param' I get the error.

import numpy as np
from scipy.optimize import minimize

def objective_fnc(x,p1,p2,p3):
    """The Rosenbrock function"""
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)


class CallbackFunctor:
    def __init__(self, obj_fun):
        self.num_calls = 0
        if self.num_calls == 0:
            print('Optimization problem started')
        self.obj_fun = obj_fun
    
    def __call__(self, x):
        fun_val = self.obj_fun(x,p1,p2,p3)
        self.num_calls += 1
        if self.num_calls % 5 == 0:
            print('Work in progress...')
            print(p1)
            print(p2)
            print(p3)
            
cb = CallbackFunctor(objective_fnc)

# Parameters
p1=10
p2=20
p3=30
param=(p1,p2,p3)
x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])
res = minimize(objective_fnc, x0, args=(p1,p2,p3), method='nelder-mead', callback=cb, options={'xatol': 1e-8, 'disp': True})

print(res.x)
nekovolta
  • 496
  • 2
  • 14

2 Answers2

1

If your function has the signature objective_func(x, bound_limits), where bounds_limits is a tuple, you need to ensure that unpacking args yields a tuple. Hence, you need to pass a Iterable of a tuple:

bound_limits = (param1,param2,param3,param4,param5,param6,param7,param8)
res = minimize(objective_fnc, x0, args=(bound_limits,), method='Nelder-Mead)

Note that there's no need for the args parameter. You can achieve the same by using a lambda function:

res = minimize(lambda x: objective_fnc(x, bound_limits), x0, method='Nelder-Mead')
joni
  • 6,840
  • 2
  • 13
  • 20
  • I doesn't work as you suggested. The thing is that when I call again the objective function it doesn't pass the argument in `bound_limits` – nekovolta Jul 27 '21 at 09:44
  • @nekovolta Please provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – joni Jul 27 '21 at 09:58
  • I put a minimal reproducible example. It works as I explained, but on the other hand, if I change `p1,p2,p3` for `param` I get the error I mentioned – nekovolta Jul 27 '21 at 19:51
  • @nekovolta Both of my proposed solutions work for `def objective_func(x, params)`. Did you overlook the extra comma in `args=(bound_limits,)`? And PS: This isn't a minimal example. The callback function is not relevant to your question. – joni Jul 27 '21 at 20:49
  • My apologies. I tried the way you suggest in the real problem, and it didn't work, but it was because of an additional mistake I realise when reviewing again. Thank you. Now I would like to understand why putting the `,` changes everything. Finally, I put the callback function because my real problem is relevant, although this simplified version is not. – nekovolta Jul 27 '21 at 21:46
0

try to use unpatch with :

objective_fnc(Xo, *bound_limits)

https://stackabuse.com/unpacking-in-python-beyond-parallel-assignment

jmnguye
  • 347
  • 2
  • 9