1

Situation: I am using the scipy for an optimization problem. For many constraints are needed, the coefficients are in a pandas.dataframe object. I am trying to input all these constraints using cycles. the problem is in the next cycle, the parameters have been changed, so there is only on constraints remained actually.

to express the problem more clearly, I give an example(in this example, just a few parameters is given. But in the actual situation, maybe there are more than 50 paramters). Step1: the code I used is something as follows: the constraints is w1 + w2 * 2 >= 0; w1 * 3 + w2 * 5 >= 0

cons = []
d = {
        'type': 'ineq', 
        'fun': ''}
a = np.array([1,2])
d['fun'] = lambda w: a.dot(w)
cons.append(d.copy())
a = np.array([3,5])
d['fun'] = lambda w: a.dot(w)
cons.append(d.copy())
cons

Step2: test the cons using the code shown as follows: just using w as [1, 1], I expect the fun stored in cons will output 3 and 8, but actually 8 and 8.

w = np.array([1,1])
for each in cons:
    fun = each['fun']
    print(fun(w))

Can anyone give some help on how to solve this problem? Or it's just something wrong.

loki
  • 976
  • 1
  • 10
  • 22
Andy
  • 58
  • 7
  • Related: https://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture, https://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda – wwii Jan 02 '20 at 05:25
  • `d['fun'] = lambda w: np.array([1,2]).dot(w)` would work. – wwii Jan 02 '20 at 05:28
  • One more - https://stackoverflow.com/a/13355291/2823755 -> `closures (lambdas or otherwise) close over names, not values.` – wwii Jan 02 '20 at 05:31
  • 1
    More relevant Q&As: https://stackoverflow.com/questions/37791680/scipy-optimize-minimize-slsqp-with-linear-constraints-fails/37792650#37792650, https://stackoverflow.com/questions/45491376/scipy-optimization-not-running-when-i-try-to-set-constraints-using-a-for-loop/45493887#45493887 – Warren Weckesser Jan 02 '20 at 06:14
  • thanks for you help, I have got the reason for this kind of problem. – Andy Jan 02 '20 at 06:26

1 Answers1

1

The problem is you redefine a.

This code renames your second definition of a to b and gives the result you expect:

import numpy as np

cons = []
d = {
        'type': 'ineq', 
        'fun': ''}
a = np.array([1, 2])
d['fun'] = lambda w: a.dot(w)
cons.append(d.copy())
b = np.array([3, 5])
d['fun'] = lambda w: b.dot(w)
cons.append(d.copy())
print(cons)

c = np.array([1, 1])
for each in cons:
    fun = each['fun']
    print(fun(c))

Note that this solves your immediate problem, but it doesn't mean this is an optimal or very robust solution to the problem you're trying to solve.

I think what you're looking for here is functools.partial. Here's your code, but rewritten to use partial:

import numpy as np
from functools import partial


def get_dot(a, w):
    return a.dot(w)


cons = []
d = {
    'type': 'ineq',
    'fun': None
}

d['fun'] = partial(get_dot, np.array([1, 2]))
cons.append(d.copy())

d['fun'] = partial(get_dot, np.array([3, 5]))
cons.append(d.copy())

print(cons)

g = np.array([1, 1])
for each in cons:
    fun = each['fun']
    print(fun(g))
Grismar
  • 27,561
  • 4
  • 31
  • 54
  • 1
    Or just `d['fun'] = partial(np.dot, np.array([1,2]))` - givem `import numpy as np` – wwii Jan 02 '20 at 05:17
  • ...and - `for a in (np.array([1,2]),np.array([3,5])): d['fun'] = partial(np.dot, a); cons.append(d.copy())` – wwii Jan 02 '20 at 05:23
  • 1
    there's many further optimisations or rewrites possible, but I tried to stay as close as possible to the example given, to ensure the OP gets why `partial` is the key element here. `dot()` was being used as a method on an array, but can also be used as a function, which would be better in this case (but another change) – Grismar Jan 02 '20 at 05:24