esa / pagmo_plugins_nonfree

A pagmo affiliated package (https://github.com/esa/pagmo2) adding commercial solvers to the list of solvers/algorithms: SNOPT7, WORHP
GNU General Public License v3.0
10 stars 9 forks source link

SNOPT does not communicate the number of function evaluations to pagmo #25

Open castanhas98 opened 2 years ago

castanhas98 commented 2 years ago

As stated in the title, pagmo does not seem to be able to retrieve the number of function evaluations performed by SNOPT.

Below is an example similar to the one found in https://esa.github.io/pagmo_plugins_nonfree/quickstart.html with some changes.

int main( )
{
    // 1 - Instantiate a pagmo problem constructing it from the Rosenbrock UDP.
    problem prob{rosenbrock(30)};

    // 2 - Instantiate a pagmo_plugins_nonfree algorithm, in this case SNOPT. This assumes a library libsnopt7_c.so is
    ppnf::snopt7 algo(false, "./snopt-interface/lib/libsnopt7_c.so", 6u);
    algo.set_verbosity(1);

    // 3 - Instantiate a population with a single individual and random_seed = 0.
    population pop( prob, 1, 0 );

    // 4 - Print the initial population.
    std::cout << "Initial Population:" << std::endl;
    std::cout << pop << std::endl;

    std::cout.setstate(std::ios_base::failbit); // Preventing output from being printed to cout
    // 5 - Perform one generation of evolution with snopt7.
    pop = algo.evolve( pop );
    std::cout.clear(); // Allowing output to be printed to cout

    // 6 - Print the population after the evolution.
    std::cout << "Final Population:" << std::endl;
    std::cout << pop << std::endl;

    std::cout << "Actual number of fitness evaluations (algo.get_log().size()): " << algo.get_log().size() << std::endl;

    return 0;
}

This produces the following output:

Initial Population:
Problem name: Multidimensional Rosenbrock Function
    C++ class name: pagmo::rosenbrock

    Global dimension:           30
    Integer dimension:          0
    Fitness dimension:          1
    Number of objectives:           1
    Equality constraints dimension:     0
    Inequality constraints dimension:   0
    Lower bounds: [-5, -5, -5, -5, -5, ... ]
    Upper bounds: [10, 10, 10, 10, 10, ... ]
    Has batch fitness evaluation: false

    Has gradient: true
    User implemented gradient sparsity: false
    Expected gradients: 30
    Has hessians: false
    User implemented hessians sparsity: false

    Fitness evaluations: 1
    Gradient evaluations: 0

    Thread safety: constant

Population size: 1

List of individuals: 
#0:
    ID:         4880189657833503230
    Decision vector:    [3.89267, 7.66399, 7.86918, 7.70878, 4.35346, ... ]
    Fitness vector:     [3.34052e+06]

Champion decision vector: [3.89267, 7.66399, 7.86918, 7.70878, 4.35346, ... ]
Champion fitness: [3.34052e+06]

Final Population:
Problem name: Multidimensional Rosenbrock Function
    C++ class name: pagmo::rosenbrock

    Global dimension:           30
    Integer dimension:          0
    Fitness dimension:          1
    Number of objectives:           1
    Equality constraints dimension:     0
    Inequality constraints dimension:   0
    Lower bounds: [-5, -5, -5, -5, -5, ... ]
    Upper bounds: [10, 10, 10, 10, 10, ... ]
    Has batch fitness evaluation: false

    Has gradient: true
    User implemented gradient sparsity: false
    Expected gradients: 30
    Has hessians: false
    User implemented hessians sparsity: false

    Fitness evaluations: 1
    Gradient evaluations: 0

    Thread safety: constant

Population size: 1

List of individuals: 
#0:
    ID:         4880189657833503230
    Decision vector:    [1, 1, 1, 1, 1, ... ]
    Fitness vector:     [1.08934e-16]

Champion decision vector: [1, 1, 1, 1, 1, ... ]
Champion fitness: [1.08934e-16]

Actual number of fitness evaluations (algo.get_log().size()): 228

As it can be seen, the number of fitness evaluations indicated by the population is not the same as the number of lines (=function evaluations) of the log when the verbosity is set to 1.

darioizzo commented 2 years ago

Problem is revealed by the line:

https://github.com/esa/pagmo_plugins_nonfree/blob/master/src/snopt7.cpp#L715

the struct info contains a full instance of problem that is then copied .... a solution to try is to store a pointer rather than an instance as data member of the user_data struct:

https://github.com/esa/pagmo_plugins_nonfree/blob/a784bd3bfdba8e3c253bbeff627af18da92f7ce1/include/pagmo_plugins_nonfree/snopt7.hpp#L94

castanhas98 commented 2 years ago

Problem is revealed by the line:

https://github.com/esa/pagmo_plugins_nonfree/blob/master/src/snopt7.cpp#L715

the struct info contains a full instance of problem that is then copied .... a solution to try is to store a pointer rather than an instance as data member of the user_data struct:

https://github.com/esa/pagmo_plugins_nonfree/blob/a784bd3bfdba8e3c253bbeff627af18da92f7ce1/include/pagmo_plugins_nonfree/snopt7.hpp#L94

@darioizzo, I believe I managed to get your suggestion to work, but there is the need to remove the const qualifier from prob in the line below: https://github.com/esa/pagmo_plugins_nonfree/blob/a784bd3bfdba8e3c253bbeff627af18da92f7ce1/src/snopt7.cpp#L514

Both just removing the const in that line or making a const_cast to pagmo::problem * when assigning the problem in the user_data struct seem to do the trick.

https://github.com/esa/pagmo_plugins_nonfree/blob/a784bd3bfdba8e3c253bbeff627af18da92f7ce1/src/snopt7.cpp#L715

I honestly do not know which would be the preferred option, or if there are others that I am not considering. Any thoughts on this?

EDIT: I believe I was creating a problem where there was none. user_data storing a pointer to a const pagmo::problem seems to be enough.

EDIT2: I took the liberty to submit a pull request in an attempt to solve this issue. Will wait for feedback.