mercredi 23 décembre 2020

NLopt optimization function isn't optimizing parameters

I am using NLopt to solve an optimization problem. I am trying to find 3 optimal parameters to minimize my function (detailed in my_func).

With ub and lb I am defining the upper and lower constraints for each parameter.

With x I am defining the initial guess for each parameter value.

What I am confused about is that there doesn't appear to be an searching for the optimal values happening. Depending on whatever my initial parameter guess is, this guess is returned as the optimal value.

For example, if I set x[0], x[1], x[2] to 0.1, 0.1, 0.1, I get this print out in the terminal:

Found minimum at : f(0.1, 0.1, 0.1) = 1928.76

However, if I set the initial guess to the upper limit of accepted values within each constrain (like in the code below), I get this:

Found minimum at : f(6.27, 809, 539) = 10186.6

What's going on here? Shouldn't this code be looping through parameter combinations to minimize the function value?

My code is below:

#include <nlopt.hpp>
#include <iostream>
using namespace std;

struct ExtraData
{
    double xM0;
    double yM0;
    double xF0;
    double yF0;

    double xM1;
    double yM1;
    double xF1;
    double yF1;

    double xM2;
    double yM2;
    double xF2;
    double yF2;

    double xM3;
    double yM3;
    double xF3;
    double yF3;

    double xM4;
    double yM4;
    double xF4;
    double yF4;

    double xM5;
    double yM5;
    double xF5;
    double yF5;

    double xM6;
    double yM6;
    double xF6;
    double yF6;

    double xM7;
    double yM7;
    double xF7;
    double yF7;

    double xM8;
    double yM8;
    double xF8;
    double yF8;
};
    
double myfunc(const std::vector<double> &x, std::vector<double> &grad, void *my_func_data);
// void multi_constraint(unsigned m, double *result, unsigned n, const double* x, double* grad, void* f_data);

int main()
{
    nlopt::opt opt(nlopt::LD_SLSQP, 3);
    std::vector<double> lb(3);
    lb[0] = .01;   
    lb[1] = .01;
    lb[2] = .01;
    opt.set_lower_bounds(lb);

    std::vector<double> ub(3);
    ub[0] = 6.28;   
    ub[1] = 810.;
    ub[2] = 540.;
    opt.set_upper_bounds(ub);

    ExtraData extraData{
        .xM0 = 550.234, .yM0 = 124.645, .xF0 = 511.778, .yF0 = 129.153,
        .xM1 = 551.234, .yM1 = 125.645, .xF1 = 521.778, .yF1 = 139.153,
        .xM2 = 553.234, .yM2 = 126.645, .xF2 = 531.778, .yF2 = 109.153,
        .xM3 = 547.234, .yM3 = 127.645, .xF3 = 541.778, .yF3 = 199.153,
        .xM4 = 540.234, .yM4 = 128.645, .xF4 = 502.778, .yF4 = 189.153,
        .xM5 = 540.234, .yM5 = 129.645, .xF5 = 503.778, .yF5 = 149.153,
        .xM6 = 546.234, .yM6 = 121.645, .xF6 = 504.778, .yF6 = 117.153,
        .xM7 = 501.234, .yM7 = 122.645, .xF7 = 504.778, .yF7 = 114.153,
        .xM8 = 500.234, .yM8 = 120.645, .xF8 = 508.778, .yF8 = 113.153
    };

    opt.set_min_objective(myfunc, &extraData);
    opt.set_xtol_rel(1e-4);

    // set initial parameter guesses
    // x[0] = theta, x[1] = tx, x[2] = ty
    std::vector<double> x(3);
    x[0] = 6.27;
    x[1] = 809.;
    x[2] = 539.;

    double minf;
    nlopt::result result = opt.optimize(x, minf);
    cout << "Found minimum at : f(" << x[0] << ", " << x[1] << ", " << x[2] << ") " << "= " << minf << endl;
}

double myfunc(const std::vector<double> &x, std::vector<double> &grad, void *my_func_data)
{
    // // need to compute gradients for parameters
    // if (!grad.empty()) {
    //     grad[0] = 0.0;
    //     grad[1] = 0.5 / sqrt(x[1]);
    //     grad[2] = 0.0;
    // }

    ExtraData *extraData = static_cast<ExtraData*>(my_func_data);
    double xM0 = extraData -> xM0;
    double yM0 = extraData -> yM0;
    double xF0 = extraData -> xF0;
    double yF0 = extraData -> yF0;

    double xM1 = extraData -> xM1;
    double yM1 = extraData -> yM1;
    double xF1 = extraData -> xF1;
    double yF1 = extraData -> yF1;

    double xM2 = extraData -> xM2;
    double yM2 = extraData -> yM2;
    double xF2 = extraData -> xF2;
    double yF2 = extraData -> yF2;

    double xM3 = extraData -> xM3;
    double yM3 = extraData -> yM3;
    double xF3 = extraData -> xF3;
    double yF3 = extraData -> yF3;

    double xM4 = extraData -> xM4;
    double yM4 = extraData -> yM4;
    double xF4 = extraData -> xF4;
    double yF4 = extraData -> yF4;

    double xM5 = extraData -> xM5;
    double yM5 = extraData -> yM5;
    double xF5 = extraData -> xF5;
    double yF5 = extraData -> yF5;

    double xM6 = extraData -> xM6;
    double yM6 = extraData -> yM6;
    double xF6 = extraData -> xF6;
    double yF6 = extraData -> yF6;

    double xM7 = extraData -> xM7;
    double yM7 = extraData -> yM7;
    double xF7 = extraData -> xF7;
    double yF7 = extraData -> yF7;

    double xM8 = extraData -> xM8;
    double yM8 = extraData -> yM8;
    double xF8 = extraData -> xF8;
    double yF8 = extraData -> yF8;
    cout << x[0] << x[1] << x[2] << endl;
    return sqrt(
                pow(abs(xF0 - (cos(x[0])*xM0) - (sin(x[0])*yM0) + x[1]), 2) + \
                pow(abs(yF0 - (sin(x[0])*xM0) + (cos(x[0])*yM0) + x[2]), 2)
            ) +
            sqrt(
                pow(abs(xF1 - (cos(x[0])*xM1) - (sin(x[0])*yM1) + x[1]), 2) + \
                pow(abs(yF1 - (sin(x[0])*xM1) + (cos(x[0])*yM1) + x[2]), 2)
            ) +
            sqrt(
                pow(abs(xF2 - (cos(x[0])*xM2) - (sin(x[0])*yM2) + x[1]), 2) + \
                pow(abs(yF2 - (sin(x[0])*xM2) + (cos(x[0])*yM2) + x[2]), 2)
            ) +
            sqrt(
                pow(abs(xF3 - (cos(x[0])*xM3) - (sin(x[0])*yM3) + x[1]), 2) + \
                pow(abs(yF3 - (sin(x[0])*xM3) + (cos(x[0])*yM3) + x[2]), 2)
            ) +
            sqrt(
                pow(abs(xF4 - (cos(x[0])*xM4) - (sin(x[0])*yM4) + x[1]), 2) + \
                pow(abs(yF4 - (sin(x[0])*xM4) + (cos(x[0])*yM4) + x[2]), 2)
            ) +
            sqrt(
                pow(abs(xF5 - (cos(x[0])*xM5) - (sin(x[0])*yM5) + x[1]), 2) + \
                pow(abs(yF5 - (sin(x[0])*xM5) + (cos(x[0])*yM5) + x[2]), 2)
            ) +
            sqrt(
                pow(abs(xF6 - (cos(x[0])*xM6) - (sin(x[0])*yM6) + x[1]), 2) + \
                pow(abs(yF6 - (sin(x[0])*xM6) + (cos(x[0])*yM6) + x[2]), 2)
            ) +
            sqrt(
                pow(abs(xF7 - (cos(x[0])*xM7) - (sin(x[0])*yM7) + x[1]), 2) + \
                pow(abs(yF7 - (sin(x[0])*xM7) + (cos(x[0])*yM7) + x[2]), 2)
            ) +
            sqrt(
                pow(abs(xF8 - (cos(x[0])*xM8) - (sin(x[0])*yM8) + x[1]), 2) + \
                pow(abs(yF8 - (sin(x[0])*xM8) + (cos(x[0])*yM8) + x[2]), 2)
            )
            ;
}

If useful, my code is compiled with: clang++ -std=c++11 test.cpp -lnlopt -lm -o test on my Mac catalina.

1 commentaire:

  1. Possibly because you used a solver that needs a gradient. Did you try to choose a solver that doesn't need gradients (LN_.. or GN_..)?

    RépondreSupprimer