コンテンツにスキップ

SQP(逐次二次計画法)

制約付き非線形最適化問題をSQPソルバを用いて解く例

制約として最適化変数のノルムが1以下になるように設定している。

橙:最適化変数のパス
青:制約の境界
緑:目的関数が極小値となる座標

#define CR_USE_MATPLOTLIB
#include <iostream>
#include <cpp_robotics/optimize/sqp.hpp>
#include "cpp_robotics/third_party/matplotlib-cpp/matplotlibcpp.h"

using namespace cpp_robotics;

int main()
{
    SQP solver;
    SQP::Problem prob;

    //////////////////// 問題設定 ////////////////////
    // 極小値の時x = (3, -1)
    prob.func = [](Eigen::VectorXd x) -> double
    {
        Eigen::MatrixXd Q(2,2);
        Eigen::VectorXd c(2);
        Q << 1, 0, 
                0, 1;
        c << -3, 1;
        return 0.5*(x.transpose()*Q).dot(x) + c.dot(x);
    };

    prob.con.push_back({
        Constraint::Ineq,
        [](Eigen::VectorXd x)
        {
            const double radius = 1.0;
            return (x.squaredNorm() - std::pow(radius, 2.0));
        },
    });

    Eigen::VectorXd x0(2);
    x0 << 0, 0.5;

    std::vector<double> x_, y_;

    //////////////////// 解く ////////////////////
    prob.use_slsqp = false; // Todo
    auto result = solver.solve(prob, x0, [&](auto x){ x_.push_back(x(0)); y_.push_back(x(1)); });

    std::cout << "min (x(0)-3)^2 + (x(1)+1)^2" << std::endl;
    std::cout << "s.t. x(0)^2 + x(1)^2 <= 1" << std::endl;

    if(not result.is_solved)
    {
        std::cout << "solve failed" << std::endl;
    }

    std::cout << "iter: " << result.iter_cnt << std::endl;
    std::cout << "constraint satisfy: " << ((prob.con.all_satisfy(result.x, prob.tol_con)) ? "true" : "false") << std::endl;
    std::cout << "optimal x =" << std::endl;
    std::cout << result.x << std::endl;
    std::cout << "optimal x norm =" << std::endl;
    std::cout << result.x.norm() << std::endl;

    std::vector<double> radius_x, radius_y;
    for(size_t i = 0; i < 100; i++)
    {
        double theta = 2*M_PI*i/100;
        radius_x.push_back(std::cos(theta));
        radius_y.push_back(std::sin(theta));
    }

    namespace plt = matplotlibcpp;
    plt::plot(radius_x, radius_y, "--"); // 制約
    plt::plot(x_, y_, "o--");
    plt::plot(std::vector{3}, std::vector{-1}, "*");
    plt::xlim(-3.5, 3.5);
    plt::ylim(-2.0, 2.0);
    plt::set_aspect_equal();

    plt::show();
}