Bonmin icon indicating copy to clipboard operation
Bonmin copied to clipboard

segfault with bound_relax_factor=0.0 on macos

Open jschueller opened this issue 1 year ago • 0 comments

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>

jschueller avatar Oct 24 '24 13:10 jschueller