从矩阵动态编写scipy.optimize.minimize的目标函数和约束

时间:2014-05-05 15:23:51

标签: python matrix scipy constraints mathematical-optimization

在我的代码中,我试图编写一个while循环,使用scipy.optimize.minimize使用SQSLP选项更新x的值,因为我有不等式和等式约束。除了更新x之外,这还会导致更新目标函数和约束中的值。

下面是调用在给定x

处更新约束和目标函数的所有函数的部分
import numpy as np
from scipy.optimize import minimize

num_vars = 2
num_eq_cons = 1
num_ineq_cons = 1


# return the desired initial value for x
def x_init():
    x = np.zeros([num_vars,1], dtype = float)
    x[0] = 2
    x[1] = 1
    return x

# Objective
# needs the current value of x (x_k)
def eval_objective(x):
    f = x[0]**2 + x[1]**2
    return float(f)

# Inequality Constraints
# need the current value of x (x_k)
def eval_ineq_cons(x):
    g = np.zeros([num_ineq_cons,1], dtype = float)
    g[0] = x[0]**2 - x[1]
    return g

# Equality Constraints
# need the current value of x (x_k)
def eval_eq_cons(x):
    h = np.zeros([num_eq_cons, 1], dtype = float)
    h[0] = 2 - x[0] - x[1]**2
    return h

# Taylor series Approximations
# needs the current value of x
# For objective
def eval_part_obj(x):
    dfdx = np.zeros([num_vars,1], dtype = float)
    dfdx[0] = 2*x[0]
    dfdx[1] = 2*x[1]
    return dfdx

# For inequalities
def eval_part_ineq_cons(x):
    dgdx = np.zeros([num_ineq_cons, num_vars], dtype = float)
    dgdx[0,0] = 2*x[0]
    dgdx[0,1] = -1.0
    return dgdx

# For equalities
def eval_part_eq_cons(x):
    dhdx = np.zeros([num_eq_cons, num_vars], dtype = float)
    dhdx[0,0] = -1
    dhdx[0,1] = -2*x[1]
    return dhdx

x_k = x_init() # Initialization point
f_k = eval_objective(x_k)
dfdx = eval_part_obj(x_k)
g_k = eval_ineq_cons(x_k)
dgdx = eval_part_ineq_cons(x_k)
h_k = eval_eq_cons(x_k)
dhdx = eval_part_eq_cons(x_k)

以下代码是我收到错误的代码:

fun = lambda x_k1: f_k + dfdx[0]*(x_k1[0] - x_k[0]) + dfdx[1]*(x_k1[1] - x_k[1])

cons = ({'type': 'ineq',
         'fun': lambda x_k1: g_k + np.dot(dgdx, (x_k1 - x_k)).squeeze(),
         'jac': dgdx},
        {'type': 'eq',
         'fun': lambda x_k1: h_k + np.dot(dhdx, (x_k1 - x_k)).squeeze(),
         'jac': dhdx})

bnds = [(.5, 2.5), (0,3)]

res = minimize( fun, x_k, method = 'SLSQP', bounds=bnds, constraints = cons)
print(res)

我获得的错误是:

Traceback (most recent call last):
  File "SLPmain.py", line 104, in <module>
    res = minimize( fun, x_k, method = 'SLSQP', bounds=bnds, constraints = cons)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/optimize/_minimize.py", line 358, in minimize
    constraints, **options)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/optimize/slsqp.py", line 376, in _minimize_slsqp
    for con in cons['eq']])
TypeError: 'numpy.ndarray' object is not callable

如果有更好的方式以更好的方式编写约束和目标函数,请告诉我!

1 个答案:

答案 0 :(得分:0)

有两个明显的问题:

1,Jacobians对于不等式和等式约束都是array而不是callables(函数),因为它们被定义为:

dgdx = eval_part_ineq_cons(x_k)
dhdx = eval_part_eq_cons(x_k)

它们是函数返回(因此array s)不是函数。

2,约束函数返回不是标量,它们是array s。 e.g:

g_k + np.dot(dgdx, (x_k1 - x_k)).squeeze()

纠正这种情况的方法取决于你的实际工作问题,还有其他选择:

(g_k + np.dot(dgdx, (x_k1 - x_k)).squeeze()).sum()

((g_k + np.dot(dgdx, (x_k1 - x_k)).squeeze())**2).sum() #sum of squares.

不平等约束的同样问题。 我怀疑这是因为funfmin_slsqp传递给eqcons而不是f_eqcons。如果您希望等于约束函数中的所有元素返回== 0,则可能应该直接使用fmin_slsqp并传递约束函数f_eqcons

因此,它可以如下:

cons = ({'type': 'ineq',
         'fun': lambda x_k1: (g_k + np.dot(dgdx, (x_k1 - x_k)).squeeze()).sum(),
         'jac':  eval_part_ineq_cons},
        {'type': 'eq',
         'fun': lambda x_k1: (h_k + np.dot(dhdx, (x_k1 - x_k)).squeeze()).sum(),
         'jac': eval_part_eq_cons})

结果是:

In [31]:

res
Out[31]:
  status: 9
 success: False
    njev: 101
    nfev: 1385
     fun: array([ 2.69798824])
       x: array([ 1.89963627,  0.04972158])
 message: 'Iteration limit exceeded'
     jac: array([ 4.,  2.,  0.])
     nit: 101

实际上,请注意优化失败。我怀疑当你有Jacobian约束时它不能保证工作。删除Jac,它会起作用(虽然可能不是你想要的方式)。

In [29]:

res
Out[29]:
  status: 0
 success: True
    njev: 11
    nfev: 122
     fun: array([ 1.33333333])
       x: array([ 0.5       ,  2.16666667])
 message: 'Optimization terminated successfully.'
     jac: array([ 4.,  2.,  0.])
     nit: 15