scipy.optimize.minimize 具有通用数组索引

scipy.optimize.minimize with general array indexing

我想用scipy.optimize.minimize中的方法'COBYLA'解决一个优化问题如下:

test = spopt.minimize(testobj, x_init, method='COBYLA', constraints=cons1)
y = test.x
print 'solution x =', y

但是,由于程序非常大,编写 objective 函数(和约束)的一种可伸缩方法是对参数使用通用索引。例如,如果我可以使用 x['parameter1']x.param1 而不是 x[0],那么程序将更易于阅读和调试。我尝试将 x 写成一个对象或 pandas Series 并使用像 x['parameter1'] 这样的一般索引,如下所示:

def testobj(x):
    return x['a']**2 + x['b'] + 1

def testcon1(x):
    return x['a']

def testcon2(x):
    return x['b']

def testcon3(x):
    return 1 - x['a'] - x['b']


x_init = pd.Series([0.1, 0.1])
x_init.index = ['a','b']

cons1 = ({'type': 'ineq', 'fun': testcon1}, \
    {'type': 'ineq', 'fun': testcon2}, \
    {'type': 'ineq', 'fun': testcon3})

但是每当我将它传递给 minimize 例程时,它就会抛出一个错误:

return x['a']**2 + x['b'] + 1
ValueError: field named a not found

如果我使用普通的 numpy 数组,它工作得很好。也许我做的不对,但这是我必须使用 numpy 数组而不是任何其他数据结构的最小化函数的限制吗? this topic 上的 scipy 文档提到初始猜测必须是 ndarray,但我很好奇例程如何调用参数,因为对于 pandas Series 调用变量 x[0]x['a'] 是等价的。

如您所见,scipy 优化使用 numpy 数组作为输入,而不是 pandas 系列。当您使用 pandas 系列进行初始化时,它会有效地将其转换为数组,因此您无法再按名称访问字段。

可能最简单的方法就是创建一个函数,在您每次调用它们时重新包装参数;例如:

def make_series(params):
    return pd.Series(params, index=['a', 'b'])

def testobj(x):
    x = make_series(x)
    return x['a']**2 + x['b'] + 1

def testcon1(x):
    x = make_series(x)
    return x['a']

def testcon2(x):
    x = make_series(x)
    return x['b']

def testcon3(x):
    x = make_series(x)
    return 1 - x['a'] - x['b']

x_init = make_series([1, 1])
test = spopt.minimize(testobj, x_init, method='COBYLA', constraints=cons1)
print('solution x =', test.x)
# solution x = [  1.38777878e-17   0.00000000e+00]