我用一个简单的 10D 二次函数对 SLSQP 算法进行基准测试:
c++函数是:
double myfunc(const std::vector<double> &x, std::vector<double> &grad, void *)
{
++counter;
assert(x.size() == 10);
double y = pow(x[0], 2);
double factor = 1e6;
for (size_t i = 1; i < 10; ++i)
y += factor * pow(x[i], 2);
if (!grad.empty())
{
assert(grad.size() == 10);
grad[0] = 2 * x[0];
for (size_t i = 1; i < 10; ++i)
grad[i] = 2 * factor * x[i];
}
return y;
}
令我惊讶的是,SLSQP 对于这个简单的函数失败了,但是如果我将算法切换到nlopt::LD_LBFGS
,nlopt 仍然有效地优化了函数。
谁能解释一下为什么 SLSQP 在这个演示功能上失败了?
NLOPT 的版本是 2.4.2(由 给出nlopt::version
),下面是完整代码:
#include <cassert>
#include <cmath>
#include <iostream>
#include <nlopt.hpp>
#include <random>
#include <vector>
using namespace std;
mt19937_64 engine(random_device{}());
static int counter = 0;
double myfunc(const std::vector<double> &x, std::vector<double> &grad, void *)
{
++counter;
assert(x.size() == 10);
double y = pow(x[0], 2);
double factor = 1e6;
for (size_t i = 1; i < 10; ++i)
y += factor * pow(x[i], 2);
if (!grad.empty())
{
assert(grad.size() == 10);
grad[0] = 2 * x[0];
for (size_t i = 1; i < 10; ++i)
grad[i] = 2 * factor * x[i];
}
return y;
}
int main()
{
nlopt::opt opt(nlopt::LD_LBFGS, 10);
std::vector<double> lb(10, -100);
std::vector<double> ub(10, 100);
opt.set_lower_bounds(lb);
opt.set_upper_bounds(ub);
opt.set_min_objective(myfunc, NULL);
opt.set_maxeval(1000);
// opt.add_inequality_constraint(myfunc_constr, nullptr, 1);
vector<double> x(10);
uniform_real_distribution<double> distr(-100, 100);
for (auto &vx : x)
vx = distr(engine);
double minf = 1e20;
vector<double> fake_grad;
cout << "f(x0) = " << myfunc(x, fake_grad, nullptr) << endl;
try
{
counter = 0;
nlopt::result result = opt.optimize(x, minf);
cout << "exit val: " << result << endl;
}
catch (runtime_error &err)
{
cerr << err.what() << endl;
}
cout << endl;
cout << "optimized: " << minf << endl;
cout << "counter: " << counter << endl;
cout << opt.get_algorithm_name() << endl;
int maj, min, bugf;
nlopt::version(maj, min, bugf);
cout << "version: " << maj << "." << min << "." << bugf << endl;
return 0;
}