coin-or / Ipopt

COIN-OR Interior Point Optimizer IPOPT
https://coin-or.github.io/Ipopt
Other
1.41k stars 281 forks source link

Ipopt will crash if there is no non-fixed variable. #648

Closed zhzhzoo-autra closed 1 year ago

zhzhzoo-autra commented 1 year ago

I have a simple problem: min f(x0), where f(x0) = 0.0, subject to 0.0 <= x0 <= 0.0. The code to solve this problem using Ipopt is:

#include <iostream>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <coin-or/IpIpoptApplication.hpp>
#include <cmath>

using namespace Ipopt;

class Problem : public TNLP {
public:
    bool get_nlp_info(Index& n, Index& m, Index& nnz_jac_g, Index& nnz_h_lag, IndexStyleEnum& index_style) {
        n = 1;
        m = 0;
        nnz_jac_g = 0;
        nnz_h_lag = 0;
        index_style = TNLP::C_STYLE;
        return true;
    }

    bool get_bounds_info(Index n, Number* x_l, Number* x_u, Index m, Number* g_l, Number* g_u) {

        for (Index i = 0; i < n; i++) {
            x_l[i] = 0;
            x_u[i] = 0;
        }
        assert(m == 0);
        return true;
    }

    bool get_starting_point(Index n, bool init_x, Number* x, bool init_z, Number* z_l, Number* z_u, Index m, bool init_lambda, Number* lambda) {
        for (Index i = 0; i < n; i++) {
            x[i] = 0;
        }
        return true;
    }

    bool eval_f(Index n, const Number* x, bool new_x, Number& obj_value) {
        obj_value = 0;
        return true;
    }

    bool eval_grad_f(Index n, const Number* x, bool new_x, Number* grad_f) {
        assert(n == 1);
        for (int i = 0; i < n; i++)
        {
            grad_f[i] = 0;
        }
        return true;
    }

    bool eval_g(Index n, const Number* x, bool new_x, Index m, Number* g) {
        assert(m == 0);
        return true;
    }

    bool eval_jac_g(Index n, const Number* x, bool new_x, Index m, Index nele_jac, Index* irow, Index* jcol, Number* values) {
        assert(m == 0);
        return true;
    }

    bool eval_h(Index n, const Number* x, bool new_x, Number obj_factor, Index m, const Number* lambda, bool new_lambda, Index nele_hess, Index* irow, Index* jcol, Number* values) {
        assert(n == 1);
        assert(m == 0);
        return true;
    }

    void finalize_solution(SolverReturn status, Index n, const Number* x, const Number* z_L, const Number* z_U, Index m, const Number* g, const Number* lambda, Number obj_value, const IpoptData* ip_data, IpoptCalculatedQuantities* ip_cq) {
        std::cout << "x = [ ";
        for (Index i = 0; i < n; i++) {
            std::cout << x[i] << " ";
        }
        std::cout << "]" << std::endl;
        std::cout << "objective value: " << obj_value << std::endl;
    }
};

int main(int argc, char* argv[]) {
    SmartPtr<IpoptApplication> app = IpoptApplicationFactory();
    app->Options()->SetIntegerValue("max_iter", 100);
    app->Options()->SetStringValue("linear_solver", "pardisomkl");
    app->Initialize();
    SmartPtr<Problem> problem = new Problem();
    app->OptimizeTNLP(problem);
    return 0;
}

It will segfault.

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7f0ee6e in Ipopt::TripletToCSRConverter::InitializeConverter (this=0x5555555a8270, 
    dim=<optimized out>, nonzeros=<optimized out>, airn=<optimized out>, ajcn=<optimized out>)
    at /autra/.cache/bazel/cb92cb09597ef463226f6e13f5e110cf/execroot/autra/external/ipopt/src/Algorithm/LinearSolvers/IpTripletToCSRConverter.hpp:54
54           return i_row_;

Do you want to fix this corner case or it is users' responsibility to guarantee that there's at least one non-fixed variable?

svigerske commented 1 year ago

It would be good to fix this.

Here is a small patch that works for me:

@@ -66,6 +66,14 @@ Index TripletToCSRConverter::InitializeConverter(
    dim_ = dim;
    nonzeros_triplet_ = nonzeros;

+   if( nonzeros == 0 )
+   {
+      initialized_ = true;
+      nonzeros_compressed_ = 0;
+      num_doubles_ = 0;
+      return 0;
+   }
+
    // Create a list with all triplet entries
    std::vector<TripletEntry> entry_list(nonzeros);
    std::vector<TripletEntry>::iterator list_iterator = entry_list.begin();

There are some error messages printed, but it terminates with a good status.

zhzhzoo-autra commented 1 year ago

Thanks for your quick reply!! It works for me too and looks reasonable.