为 scipy.optimize.最小化生成'sequence of dicts'



我正在使用scipy.optimize.minimale与约束。文档中的示例(在 http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.optimize.minimize.html)用于约束:

cons = ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + 2},
        {'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6},
        {'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2})

我想做类似的事情,但在循环中生成此序列的元素,但我遇到了麻烦。

我尝试将缺点视为元组,这是与上述形式相同的示例:

cons = (,)
for i in range(4):
    cons += ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i})

但我得到TypeError: unsupported operand type(s) for +=: 'dict' and 'dict'.

我还尝试使用streval

cons = (str({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1]}))
for i in range(3):
    cons += (str({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i + 1}))
cons = eval(cons)

但这也没有用,我得到了类似的东西

cons = eval(cons)
  File "<string>", line 1
    {'fun': <function <lambda> at 0x062964F0>, 'type': 'eq'}{'fun': <function <lambda> at 0x062964F0>, 'type': 'eq'}{'fun': <function <lambda> at 0x062964F0>, 'type': 'eq'}{'fun': <function <lambda> at 0x062964F0>, 'type': 'eq'}
            ^
SyntaxError: invalid syntax

元组是不可变的

创建元组后,无法修改其中元素的数量或其顺序。

您可以这样做:

cons = []
for i in range(4):
    cons.append({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i})

这将为您提供字典列表。列表完成后,如果要将其更改为元组,可以执行以下操作:

cons = tuple(cons)

结果:

>>> cons
({'fun': <function <lambda> at 0x106e2cb18>, 'type': 'ineq'},
    {'fun': <function <lambda> at 0x106e2cf50>, 'type': 'ineq'},
    {'fun': <function <lambda> at 0x106e335f0>, 'type': 'ineq'},
    {'fun': <function <lambda> at 0x106e33cf8>, 'type': 'ineq'})

--

请注意,雷蒙德·赫廷格本人不久前说过:

通常,列表用于循环;元组用于结构。列表是 同质;元组异构。可变长度的列表。

因此,在您的情况下,您可能希望保留列表,除非某些第三方模块需要元组。

而不是添加一个字典(括号在这里没有任何作用):

for i in range(4):
    cons += ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i})
添加一个

元组,在字典后添加一个逗号:

for i in range(4):
    cons += ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i},)
>>> cons
({'fun': <function <lambda> at 0xb746de2c>, 'type': 'ineq'}, {'fun': <function <lambda> at 0xb747656c>, 'type': 'ineq'}, {'fun': <function <lambda> at 0xb74765a4>, 'type': 'ineq'}, {'fun': <function <lambda> at 0xb74765dc>, 'type': 'ineq'})

--

请注意,正如@Jivan在其答案中指出的那样,您应该为此类工作使用列表。

这是一个较老的问题,但提出的两种解决方案似乎都是错误的。还是我错过了什么?

def generate_constraints_wrong(n_params):
    cons = []
    for i in range(n_params):
        cons.append({'type': 'ineq', 'fun': lambda x:  x[i]})
    return tuple(cons)
def generate_constraints_wrong2(n_params):
    cons = tuple()
    for i in range(n_params):
        cons += ({'type': 'ineq', 'fun': lambda x:  x[i]},)
    return cons
def generate_constraints_right(n_params):
    # let's create a function generator that uses closure to pass i to the generated function
    def wrapper_fun(x, i):
        def fun(x):
            return x[i]
        return fun
    cons = []
    for i in range(n_params):
        f = wrapper_fun(x, i)
        cons.append({'type': 'ineq', 'fun': f})
    return tuple(cons)
# verify the generated functions
n_params = 3
x = [1,10, 100]
cons1 = generate_constraints_wrong(n_params)
cons2 = generate_constraints_wrong2(n_params)
cons3 = generate_constraints_right(n_params)
print(cons1[0]['fun'](x)) # this should be 1 but instead we end up modifying all of our lambda objects to have the last i
print(cons1[1]['fun'](x))
print(cons2[0]['fun'](x))
print(cons2[1]['fun'](x))
print(cons3[0]['fun'](x))
print(cons3[1]['fun'](x))

指纹:

100
100
100
100
1
10

问题在于,每个 lambda 的闭包指向对象 i,而不是对象 i 在特定循环迭代中的值。有关更多详细信息,请参阅:Python lambda 闭包作用域

最新更新