Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

segfault with bound_relax_factor=0.0 on macos #54

Open
jschueller opened this issue Oct 24, 2024 · 0 comments
Open

segfault with bound_relax_factor=0.0 on macos #54

jschueller opened this issue Oct 24, 2024 · 0 comments

Comments

@jschueller
Copy link
Contributor

jschueller commented Oct 24, 2024

I'm using the bound_relax_factor set to 0 to enforce strict bounds in my program but I got a crash on macos only (clang 17),
here is a minimal program to reproduce it based on the c++ example but rewritten with dense matrices:

#include <BonBonminSetup.hpp>
#include <BonCbc.hpp>
#include <BonminConfig.h>
#include <BonTMINLP.hpp>
#include <IpTNLP.hpp>

typedef ::Bonmin::TMINLP::VariableType * VariableTypeTable;
typedef ::Ipopt::TNLP::LinearityType * LinearityTypeTable;


class MyTMINLP
  : public ::Bonmin::TMINLP
{
public:

  /** Constructor with parameters */
  MyTMINLP() : ::Bonmin::TMINLP() {}

  bool get_nlp_info(int & n,
                    int & m,
                    int & nnz_jac_g, // Number of non-zero components in the Jacobian of g
                    int & nnz_h_lag, // Number of non-zero components in Hessian of Lagrangean
                    ::Ipopt::TNLP::IndexStyleEnum & index_style)
  {
    m = 3;
    n = 4;
    
    // All components of the jacobian and lagrangian's hessian are assumed to be non-zero
    nnz_jac_g = n * m;
    nnz_h_lag = n * n;

    // Index style is C-like
    index_style = ::Ipopt::TNLP::C_STYLE;
    return true;
  }

  bool get_variables_types( int n,
                            VariableTypeTable var_types)
  {
    var_types[0] = ::Bonmin::TMINLP::BINARY;
    var_types[1] = ::Bonmin::TMINLP::CONTINUOUS;
    var_types[2] = ::Bonmin::TMINLP::CONTINUOUS;
    var_types[3] = ::Bonmin::TMINLP::INTEGER;
    return true;
  }

  bool get_variables_linearity( int n,
                                LinearityTypeTable var_types)
  {
    var_types[0] = ::Ipopt::TNLP::LINEAR;
    var_types[1] = ::Ipopt::TNLP::NON_LINEAR;
    var_types[2] = ::Ipopt::TNLP::NON_LINEAR;
    var_types[3] = ::Ipopt::TNLP::LINEAR;
    return true;
  }

  bool get_constraints_linearity( int m, LinearityTypeTable const_types)
  {
    return true;
  }
  bool get_bounds_info( int n,
                        double* x_l,
                        double* x_u,
                        int m,
                        double* g_l,
                        double* g_u)
  {
    for (int i = 0; i < n; ++i)
    {
      x_l[i] = 0.0;
      x_u[i] = std::numeric_limits<double>::max();
    }
    x_u[0] = 1.0;  
    x_u[3] = 5.0;

    for (int i = 0; i < 3; ++i)
    {
      g_l[i] = 0.0;   // OT constraints are expressed as g(x) = 0 and h(x) >= 0
      g_u[i] = std::numeric_limits<double>::max();
    }

    return true;
  }

  bool get_starting_point(int n,
                          bool init_x,
                          double* x,
                          bool init_z,
                          double* z_L,
                          double* z_U,
                          int m,
                          bool init_lambda,
                          double* lambda)
  {
    for (int i=0;i <4; ++i)
      x[i] = 0.0;
    return true;
  }
  bool eval_f(int n,
              const double* x,
              bool new_x,
              double& obj_value)
  {
    obj_value = -x[0] -x[1] -x[2];

    static int ncalls = 0;
    ++ ncalls;
    return ncalls <= 10000;
  }
  bool eval_grad_f( int n,
                    const double* x,
                    bool new_x,
                    double* grad_f)
  {

    grad_f[0] = -1.0;
  grad_f[1] = -1.0;
  grad_f[2] = -1.0;
    return true;
  }
  bool eval_g(int n,
              const double* x,
              bool new_x,
              int m,
              double* g)
  {
    g[0] = -(x[1] - 0.5)*(x[1] - 0.5) - (x[2] - 0.5)*(x[2] - 0.5) + 0.25;
    g[1] = -x[0] +x[1];
    g[2] = -x[0] -x[2]-x[3] + 2.0;
    return true;
  }

  bool eval_jac_g(int n,
                  const double* x,
                  bool new_x,
                  int m,
                  int nele_jac,
                  int* iRow,
                  int *jCol,
                  double* values)
  {
    /* Switch on first call / later calls */
    if (values == NULL)
    {
      // First call: initialization of iRow/jCol
      int k = 0;
      for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
        {
          iRow[k] = i;
          jCol[k] = j;
          ++k;
        }
    }
    else     // Later calls
    {
      for (int i = 0; i < 12; ++i)
        values[i] = 0.0;
      values[1] = -2.0*(x[1]-0.5);
      values[2] = -2.0*(x[2]-0.5);
      values[4] = -1.0;
      values[5] = 1.0;
      values[8] = -1.0;
      values[10] = -1.0;
      values[11] = -1.0;
    }

    return true;
  }
  bool eval_h(int n,
              const double* x,
              bool new_x,
              double obj_factor,
              int m,
              const double* lambda,
              bool new_lambda,
              int nele_hess,
              int* iRow,
              int* jCol,
              double* values)
  {
    /* Switch on first call / later calls */
    if (values == NULL) // First call: initialization of iRow/jCol
    {
      int k = 0;
      for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
        {
          iRow[k] = i;
          jCol[k] = j;
          ++k;
        }
    }
    else // Later calls
    {
      int k = 0;
      for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
        {
          values[k] = 0.0;
          ++k;
        }
      values[5] = -2.0 * lambda[0];
      values[10] = -2.0 * lambda[0];
    }

    return true;
  }
  bool eval_gi(int n,
               const double* x,
               bool new_x,
               int i,
               double& gi)
  {
    return true;
  }

  bool eval_grad_gi(int n,
                    const double* x,
                    bool new_x,
                    int i,
                    int& nele_grad_gi,
                    int* jCol,
                    double* values)
  {
    return true;
  }

  void finalize_solution( ::Bonmin::TMINLP::SolverReturn status,
                          ::Ipopt::Index n,
                          const ::Ipopt::Number* x,
                          ::Ipopt::Number obj_value)
  {
    status_ = status;
  }

  const ::Bonmin::TMINLP::BranchingInfo * branchingInfo() const
  {
    return NULL;
  };

  const ::Bonmin::TMINLP::SosInfo * sosConstraints() const
  {
    return NULL;
  };

  ::Bonmin::TMINLP::SolverReturn getStatus() const
  {
    return status_;
  }



private:
  ::Bonmin::TMINLP::SolverReturn status_;

};

int main()
{
  ::Ipopt::SmartPtr<MyTMINLP> tminlp = new MyTMINLP();
  ::Bonmin::BonminSetup app;
  app.initializeOptionsAndJournalist();
  app.options()->SetStringValue("bonmin.algorithm", "B-BB");
  app.options()->SetNumericValue("bound_relax_factor", 0.0);

  // Update setup with MyTMINLP
  try
  {
    app.initialize(GetRawPtr(tminlp));

    // Solve problem
    ::Bonmin::Bab solver;
    solver(app);
  }
  catch (::Bonmin::TNLPSolver::UnsolvedError *exc)
  {
    return 1;
  }
  catch(const ::Bonmin::OsiTMINLPInterface::SimpleError & exc)
  {
    return 1;
  }
  catch(const CoinError & exc)
  {
    return 1;
  }

  // print used options
  std::string optionsLog;
  app.options()->PrintList(optionsLog);
  std::cout << optionsLog << std::endl;

  return 0;
}

here some some of the trace, I tried to debug it and it seems it boild down to the continousSolver_ attribute being freed several times:

2024-10-24T13:12:29.7453620Z (lldb) process launch
2024-10-24T13:12:30.3710190Z 
2024-10-24T13:12:30.3711250Z ******************************************************************************
2024-10-24T13:12:30.3712160Z This program contains Ipopt, a library for large-scale nonlinear optimization.
2024-10-24T13:12:30.3713140Z  Ipopt is released as open source code under the Eclipse Public License (EPL).
2024-10-24T13:12:30.3713860Z          For more information visit https://github.com/coin-or/Ipopt
2024-10-24T13:12:30.3714440Z ******************************************************************************
2024-10-24T13:12:30.3714820Z 
2024-10-24T13:12:30.5555360Z NLP0012I 
2024-10-24T13:12:30.5556220Z               Num      Status      Obj             It       time                 Location
2024-10-24T13:12:30.5556870Z NLP0014I             1         OPT -2.618034       17 0.28445
2024-10-24T13:12:30.5978020Z NLP0012I 
2024-10-24T13:12:30.5979220Z               Num      Status      Obj             It       time                 Location
2024-10-24T13:12:30.5980570Z NLP0014I             1         OPT -1.7071068        5 0.080399
2024-10-24T13:12:30.6474980Z NLP0012I 
2024-10-24T13:12:30.6476040Z               Num      Status      Obj             It       time                 Location
2024-10-24T13:12:30.6477150Z NLP0014I             1         OPT -1.7071068        4 0.071529
2024-10-24T13:12:30.6478410Z Cbc0012I Integer solution of -1.7071068 found by DiveMIPFractional after 0 iterations and 0 nodes (0.16 seconds)
2024-10-24T13:12:30.6954460Z NLP0014I             2         OPT -2.618034        5 0.080884
2024-10-24T13:12:30.7782900Z NLP0014I             3         OPT -2        9 0.177716
2024-10-24T13:12:30.8344660Z NLP0014I             4         OPT -1.7071068        5 0.10837
2024-10-24T13:12:31.1507680Z NLP0014I             5         OPT -2.5       38 0.621345
2024-10-24T13:12:31.5107530Z NLP0014I             6         OPT -2.5       38 0.542727
2024-10-24T13:12:31.5109460Z Cbc0010I After 0 nodes, 1 on tree, -1.7071068 best solution, best possible -2.5 (1.69 seconds)
2024-10-24T13:12:31.8530360Z NLP0014I             7         OPT -2.5       36 0.402693
2024-10-24T13:12:32.2198180Z Process 67939 launched: '$SRC_DIR/build/lib/test/t_Bonmin_std' (x86_64)
2024-10-24T13:12:32.2199070Z Process 67939 stopped
2024-10-24T13:12:32.2201380Z * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xfffffe4085f60000)
2024-10-24T13:12:32.2202640Z     frame #0: 0xfffffe4085f60000
2024-10-24T13:12:32.2204290Z error: memory read failed for 0xfffffe4085f60000
2024-10-24T13:12:32.2205480Z (lldb) bt
2024-10-24T13:12:32.2608770Z * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xfffffe4085f60000)
2024-10-24T13:12:32.2610580Z   * frame #0: 0xfffffe4085f60000
2024-10-24T13:12:32.2611810Z     frame #1: 0x0000000100b4a5b0 libCbc.3.dylib`CbcModel::resetModel(this=0x00007ff7bfefa8d8) at CbcModel.cpp:7052:3 [opt]
2024-10-24T13:12:32.2612570Z     frame #2: 0x0000000100b4a561 libCbc.3.dylib`CbcModel::gutsOfDestructor2(this=<unavailable>) at CbcModel.cpp:7039:3 [opt] [artificial]
2024-10-24T13:12:32.2613320Z     frame #3: 0x0000000100b4a2f8 libCbc.3.dylib`CbcModel::gutsOfDestructor(this=<unavailable>) at CbcModel.cpp:7013:3 [opt] [artificial]
2024-10-24T13:12:32.2614510Z     frame #4: 0x0000000100b4a379 libCbc.3.dylib`CbcModel::~CbcModel(this=0x00007ff7bfefa8d8) at CbcModel.cpp:6976:3 [opt]
2024-10-24T13:12:32.2615200Z     frame #5: 0x0000000100001d19 t_Bonmin_std`main at t_Bonmin_std.cxx:278:3 [opt]
2024-10-24T13:12:32.2615740Z     frame #6: 0x000000010000c52e dyld`start + 462
2024-10-24T13:12:32.2616190Z (lldb) quit

I'm using the latest stable bonmin 1.8.9, ipopt 3.14.16, cbc 2.10.12 etc
it also works if I set the input variable upper bounds to 1e6 for example instead of numeric_limits::max<double>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant