2

Using fmin_slsqp, I have to provide

ieqcons : list, optional

A list of functions of length n such that ieqcons[j](x,*args) >= 0.0 in a successfully optimized problem.

Since my problem is quite large, the simple option of writing them explicitly does not work.

def f1(x,arg1,arg2):
     ...
def f2(x,arg1,arg2):
     ...
...

list_func = [f1,f2,...]

There is this question which addresses dynamic and anonymous functions in Python and most of the answer go around parsing the code as a string, which seems totally unnecessary and complicates passing of arguments to the function.

The nature of the constrains list is that a single parameter (the index e.g.) can uniquely identify the function, but all the functions in the list receive the same arguments and it is impossible to make a distinction based on passed arguments.

A construction of this form would be sufficient and painless

def f(x,arg1,arg2,i):
   # return value based on i
   # e.g. # for constrain of form x[i]>=arg1[i] the funct is:
   #        return x[i]-arg1[i]

i = 1
f1 = lambda x,arg1,arg2 : f(x,arg1,arg2,i)
i = 2
f2 = lambda x,arg1,arg2 : f(x,arg1,arg2,i)

Calling f1 and f2 returns the same functions for i=2

However I can not find a solution to make i final in the function definition or restrict its scope, visibility to a particular lambda function.

As a remark, i has to remain a variable since the final solution would look like

list_f =[]
for i in range(0,num_cons):
    list_f.append(lambda x,arg1,arg2 : f(x,arg1,arg2,i))

How can I solve this issue or is there a more simpler and clever way of achieving my goal?

Community
  • 1
  • 1
Radu Ionescu
  • 3,462
  • 5
  • 24
  • 43

3 Answers3

2

From what I understand, you can solve it using functools.partial:

from functools import partial

list_f = [partial(f, i=i) for i in range(0, num_cons)]

You can find more information on lambda vs partial here:

Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
1

The reason its not working as expected is when you write a lambda expression, it does not call the function; it is called when you evaluate the expression.

So, when you evaluate it, the value of i is 2, hence the same results.

I believe what you need is functools.partial, like this:

from functools import partial

func_call_map = {i: partial(f, i=i) for i in [some range]}

# Get result for i = 4
ret_value = func_call_map[4](other, args, here)
Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
1

Following @alecxe link on Python: Why is functools.partial necessary? and the explanation provided in the comments of one of the answers, to avoid the late binding (lazy evaluation) explained by @BurhanKhalid, the code would need to be changed as follows

i = 1
f1 = lambda x,arg1,arg2,n_i=i : f(x,arg1,arg2,n_i)
i = 2
f2 = lambda x,arg1,arg2,n_i=i : f(x,arg1,arg2,n_i)

But using functions.partial provides a more elegant solution.

Community
  • 1
  • 1
Radu Ionescu
  • 3,462
  • 5
  • 24
  • 43