我正在尝试在numpy中实现数值梯度计算,以用作cyipopt中渐变的回调函数。我对numpy梯度函数的理解是它应该返回基于finite different approximation的点计算的梯度。
我不明白如何用这个模块实现非线性函数的梯度。给出的样本问题似乎是线性函数。
>>> f = np.array([1, 2, 4, 7, 11, 16], dtype=np.float)
>>> np.gradient(f)
array([ 1. , 1.5, 2.5, 3.5, 4.5, 5. ])
>>> np.gradient(f, 2)
array([ 0.5 , 0.75, 1.25, 1.75, 2.25, 2.5 ])
我的代码段如下:
import numpy as np
# Hock & Schittkowski test problem #40
x = np.mgrid[0.75:0.85:0.01, 0.75:0.8:0.01, 0.75:0.8:0.01, 0.75:0.8:0.01]
# target is evaluation at x = [0.8, 0.8, 0.8, 0.8]
f = -x[0] * x[1] * x[2] * x[3]
g = np.gradient(f)
print g
另一个缺点是我必须在几个点评估x(它会在几个点返回渐变) numpy / scipy中是否有更好的选项可以在单点进行数值计算,因此我可以将其作为回调函数实现?
答案 0 :(得分:1)
x >= y
+ {{{ 1}} 可以工作)您的完整示例问题在Test Examples for Nonlinear Programming Codes中定义:
这里有一些代码,基于数值微分,解决你的测试问题,包括官方设置(函数,渐变,起点,边界......)
x <= y
输出:
import numpy as np
import scipy.sparse as sps
import ipopt
from scipy.optimize import approx_fprime
class Problem40(object):
""" # Hock & Schittkowski test problem #40
Basic structure follows:
- cyipopt example from https://pythonhosted.org/ipopt/tutorial.html#defining-the-problem
- which follows ipopt's docs from: https://www.coin-or.org/Ipopt/documentation/node22.html
Changes:
- numerical-diff using scipy for function & constraints
- removal of hessian-calculation
- we will use limited-memory approximation
- ipopt docs: https://www.coin-or.org/Ipopt/documentation/node31.html
- (because i'm too lazy to reason about the math; lagrange and co.)
"""
def __init__(self):
self.num_diff_eps = 1e-8 # maybe tuning needed!
def objective(self, x):
# callback for objective
return -np.prod(x) # -x1 x2 x3 x4
def constraint_0(self, x):
return np.array([x[0]**3 + x[1]**2 -1])
def constraint_1(self, x):
return np.array([x[0]**2 * x[3] - x[2]])
def constraint_2(self, x):
return np.array([x[3]**2 - x[1]])
def constraints(self, x):
# callback for constraints
return np.concatenate([self.constraint_0(x),
self.constraint_1(x),
self.constraint_2(x)])
def gradient(self, x):
# callback for gradient
return approx_fprime(x, self.objective, self.num_diff_eps)
def jacobian(self, x):
# callback for jacobian
return np.concatenate([
approx_fprime(x, self.constraint_0, self.num_diff_eps),
approx_fprime(x, self.constraint_1, self.num_diff_eps),
approx_fprime(x, self.constraint_2, self.num_diff_eps)])
def hessian(self, x, lagrange, obj_factor):
return False # we will use quasi-newton approaches to use hessian-info
# progress callback
def intermediate(
self,
alg_mod,
iter_count,
obj_value,
inf_pr,
inf_du,
mu,
d_norm,
regularization_size,
alpha_du,
alpha_pr,
ls_trials
):
print("Objective value at iteration #%d is - %g" % (iter_count, obj_value))
# Remaining problem definition; still following official source:
# http://www.ai7.uni-bayreuth.de/test_problem_coll.pdf
# start-point -> infeasible
x0 = [0.8, 0.8, 0.8, 0.8]
# variable-bounds -> empty => np.inf-approach deviates from cyipopt docs!
lb = [-np.inf, -np.inf, -np.inf, -np.inf]
ub = [np.inf, np.inf, np.inf, np.inf]
# constraint bounds -> c == 0 needed -> both bounds = 0
cl = [0, 0, 0]
cu = [0, 0, 0]
nlp = ipopt.problem(
n=len(x0),
m=len(cl),
problem_obj=Problem40(),
lb=lb,
ub=ub,
cl=cl,
cu=cu
)
# IMPORTANT: need to use limited-memory / lbfgs here as we didn't give a valid hessian-callback
nlp.addOption(b'hessian_approximation', b'limited-memory')
x, info = nlp.solve(x0)
print(x)
print(info)
# CORRECT RESULT & SUCCESSFUL STATE
答案 1 :(得分:0)
我认为你对什么是数学函数以及它的数值实现有什么误解。
您应该将您的功能定义为:
def func(x1, x2, x3, x4):
return -x1*x2*x3*x4
现在,您想要评估特定点的功能,您可以使用自己提供的np.mgrid
进行评估。
如果您想计算渐变,请使用copy.misc.derivative
(https://docs.scipy.org/doc/scipy/reference/generated/scipy.misc.derivative.html)(注意dx的默认参数通常不好,请将其更改为1e-5
。两者之间没有区别数值评估的线性和非线性梯度,只有非线性函数的梯度在任何地方都不一样。
您使用np.gradient
所做的实际上是从数组中的点计算渐变,您的函数定义被f
的定义隐藏,因此不允许多个渐变评估在不同的点。同样使用您的方法会使您依赖于您的离散化步骤。