c - 使用 NLOPT SLSQP(基于梯度的算法)时 C 中的 NLopt nullptr 异常

标签 c optimization nlopt

Servus 伙计们 我正在用 Nlopt(SLSQP) 做一个关于参数识别的项目,我写了一个测试代码,但是在 3 次迭代之后,编译器总是抛出关于 Nullptr 在 'myfunc' 中的 'grad'(objectfunction):

'抛出异常:写访问冲突。 grad 是 nullptr。'

,这里我使用有限差分来计算梯度,因为有限差分可以在我的项目中计算复杂模型的梯度。

我曾尝试将不同的步长从 1e-8 更改为 1e-6 以获得有限差分,之后代码无一异常(exception)地正常工作,我不知道原因,有人可以告诉我?

double myfunc(unsigned n, const double *x,double *grad,void *data){ 

    double h =1e-8;

    grad[0] = (log(x[0] + h) - log(x[0])) / h; //hier compiler throws exception
    grad[1] = (log(x[1] + h) - log(x[1])) / h;

    printf("\ngrad[0] is %10f grad[1] is %10f\n", grad[0], grad[1]);
    printf("\nx[0] is %10f x[1] is %10f\n",x[0],x[1]);

    return log(x[0]) + log(x[1]);
}

double myconstraint(unsigned n, const double *x, double *grad, void*data) {

    double *a = (double *)data; 
    grad[0] = a[0];
    grad[1] = a[1];
    return x[0] * a[0] + x[1] * a[1] - 5;
}

double myinconstraint(unsigned n, const double *x, double *grad, void *data) {

    grad[0] = 1;
    grad[1] = -1;
    return x[0] - x[1];
}

void main(){
    //test-code

    double f_max = -10000;
    double tol = 1e-16;
    double p[2] = { 1,2 };
    double x[2] = { 1,1 };
    double lb[2] = { 0,0 };
    double ub[2] = { 10000,10000 }; 

    nlopt_opt opter = nlopt_create(NLOPT_LD_SLSQP, 2);      
    nlopt_set_max_objective(opter, myfunc, NULL);

    nlopt_set_lower_bounds(opter, lb);
    nlopt_set_upper_bounds(opter, ub);
    nlopt_add_equality_constraint(opter, myconstraint, p, tol);
    nlopt_add_inequality_constraint(opter, myinconstraint, NULL, tol); 
    nlopt_set_xtol_rel(opter, tol);
    nlopt_set_ftol_abs(opter, tol);

    nlopt_result result = nlopt_optimize(opter, x, &f_max);//?

    printf("Maximum utility=%f, x=(%f,%f)\n", f_max, x[0], x[1]);

    system("pause");
}

hier 是命令窗口中的结果,步长为 1e-8

grad[0] 是 1.000000 grad[1] 是 1.000000

x[0] 是 1.000000 x[1] 是 1.000000

grad[0] 是 0.600000 grad[1] 是 0.600000

x[0] 是 1.666667 x[1] 是 1.666667

grad[0] 是 0.600000 grad[1] 是 0.600000

x[0] 是 1.666667 x[1] 是 1.666667

然后抛出编译器异常

最佳答案

你必须检查 grad 是否为 NULL,并且只有在它不为 NULL 时才返回梯度。来自文档:

Also, if the parameter grad is not NULL, then we set grad[0] and grad[1] to the partial derivatives of our objective with respect to x[0] and x[1]. The gradient is only needed for gradient-based algorithms; if you use a derivative-free optimization algorithm, grad will always be NULL and you need never compute any derivatives.

因此您的代码应该如下所示:

double myfunc(unsigned n, const double *x,double *grad,void *data)
{ 
    double h = 1e-8;

    if (grad) {
        grad[0] = (log(x[0] + h) - log(x[0])) / h;
        grad[1] = (log(x[1] + h) - log(x[1])) / h;
    }

    return log(x[0]) + log(x[1]);
}

关于c - 使用 NLOPT SLSQP(基于梯度的算法)时 C 中的 NLopt nullptr 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57717739/

相关文章:

c - 常量的默认类型

c - 如何在C中将RGB转换为HSL?

c - 串行端口 read() 一次仅获取较少的字节

performance - 在 x86 程序集 : xor, mov 或 and 中将寄存器设置为零的最佳方法是什么?

performance - 计算数组中“小于x”的元素

c++ - 如何直接在 Rcpp 包中调用 nloptr 包中的 C 函数?

c# - 将代码片段从 C 转换为 C#

r - 使用 R nloptr 包进行最小化 - 多重等式约束

python - scipy.optimize 非线性约束