coin-or / Couenne

Convex Over and Under Envelopes for Nonlinear Estimation
Eclipse Public License 1.0
69 stars 7 forks source link

How to solve a CouenneProblem when using Couenne as a C++ library #59

Closed jdh8 closed 1 year ago

jdh8 commented 2 years ago

I am using Couenne as a C++ library to solve MINLP programmatically. I have constructed a CouenneProblem with the following code:

#include <coin/CouenneExprMul.hpp>
#include <coin/CouenneExprSum.hpp>
#include <coin/CouenneProblem.hpp>

int main()
{
  const Couenne::CouNumber xs[] = { 1, 5, 5, 1 };
  const Couenne::CouNumber lbs[] = { 1, 1, 1, 1 };
  const Couenne::CouNumber ubs[] = { 5, 5, 5, 5 };

  Couenne::Domain domain;
  domain.push(Couenne::DomainPoint(4, xs, lbs, ubs, false), false);

  Couenne::CouenneProblem problem;

  Couenne::expression *x0 = problem.addVariable(/*isint=*/false, &domain);
  Couenne::expression *x1 = problem.addVariable(/*isint=*/false, &domain);
  Couenne::expression *x2 = problem.addVariable(/*isint=*/true, &domain);
  Couenne::expression *x3 = problem.addVariable(/*isint=*/true, &domain);

  problem.addObjective(new Couenne::exprSum(
    new Couenne::exprMul(new Couenne::expression *[3]{
      x0->clone(),
      x3->clone(),
      new Couenne::exprSum(new Couenne::expression *[3]{ x0->clone(), x1->clone(), x2->clone() }, 3)
    }, 3),
    x2->clone()));
}

However, I don't know how to solve the problem. I tried Bonmin::BonminSetup, but it seemed to lack a Bonmin::TMINLP for initialization.

merraksh commented 2 years ago

Hello,

 

unfortunately this is not possible yet for a number of reasons. The only way to run Couenne is through AMPL, due to Ipopt using the AMPL interface to obtain nonlinear information (gradient, Hessian, etc.). Some recent developments sidestep this for specific heuristics, but these are not available yet for the use you showed.

 

Regards,

Pietro

   

Sent: Monday, December 13, 2021 at 12:05 PM From: "Chen-Pang He" @.> To: "coin-or/Couenne" @.> Cc: "Subscribed" @.***> Subject: [coin-or/Couenne] How to solve a CouenneProblem when using Couenne as a C++ library (Issue #59)

 

I am using Couenne as a C++ library to solve MINLP programmatically. I have constructed a CouenneProblem with the following code:

include <coin/CouenneExprMul.hpp>

include <coin/CouenneExprSum.hpp>

include <coin/CouenneProblem.hpp>

int main() { const Couenne::CouNumber xs[] = { 1, 5, 5, 1 }; const Couenne::CouNumber lbs[] = { 1, 1, 1, 1 }; const Couenne::CouNumber ubs[] = { 5, 5, 5, 5 };

Couenne::Domain domain; domain.push(Couenne::DomainPoint(4, xs, lbs, ubs, false), false);

Couenne::CouenneProblem problem;

Couenne::expression x0 = problem.addVariable(/isint=/false, &domain); Couenne::expression x1 = problem.addVariable(/isint=/false, &domain); Couenne::expression x2 = problem.addVariable(/isint=/true, &domain); Couenne::expression x3 = problem.addVariable(/isint=/true, &domain);

problem.addObjective(new Couenne::exprSum( new Couenne::exprMul(new Couenne::expression [3]{ x0->clone(), x3->clone(), new Couenne::exprSum(new Couenne::expression [3]{ x0->clone(), x1->clone(), x2->clone() }, 3) }, 3), x2->clone())); }

However, I don't know how to solve the problem. I tried Bonmin::BonminSetup, but it seemed to lack a Bonmin::TMINLP for initialization.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android.

svigerske commented 2 years ago

The AMPL and various other interfaces to Couenne implement the problem twice, as CouenneProblem and as TMINLP. So this would need to be done here, too.

jdh8 commented 2 years ago

I am working on computing gradients/Jacobians/Hessians with autodiff. I am still unsure about which function whose Hessian Bonmin::TMINLP wants. Does it want the Hessian of the objective function? I think so because we are finding extrema as zeros of the first derivative (gradient) with help from the second derivative (Hessian).

svigerske commented 2 years ago

It wants the Hessian of the Lagrangian function. It may be best to look at the Ipopt and Bonmin docus. A Bonmin::TMINLP is an Ipopt::TNLP plus some extras.

merraksh commented 2 years ago

The files Couenne/src/heuristics/CouenneF* implement a feasibility pump and interface Ipopt with the problem through a native, non-AMPL interface. Maybe you want to take a look at that for your example, but I haven't generalized yet to Couenne as such.

   

Sent: Wednesday, December 15, 2021 at 10:50 AM From: "Stefan Vigerske" @.> To: "coin-or/Couenne" @.> Cc: "Pietro Belotti" @.>, "Comment" @.> Subject: Re: [coin-or/Couenne] How to solve a CouenneProblem when using Couenne as a C++ library (Issue #59)

 

It wants the Hessian of the Lagrangian function. It may be best to look at the Ipopt and Bonmin docus. A Bonmin::TMINLP is an Ipopt::TNLP plus some extras.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android.

tkralphs commented 2 years ago

@jdh8 If I'm not mistaken, Optimization Services provides an interface for doing what you want to do. I couldn't find a very good succinct set of instructions that explain the basics (although there surely is something), but you can get the idea starting around Slide 28 of this deck. @merraksh please correct me if I'm wrong.

merraksh commented 2 years ago

@tkralphs thanks, I forgot about OS. Slide 30 of the deck mentions methods for constructing general nonlinear constraints, so that must be the way to go. OS provides Hessian/Jacobian/gradient interfaces too, so this seems possible indeed.

jdh8 commented 2 years ago

Thanks! I've built an OSInstance of my problem. Now I'm going to find out where its solving API is.

#include <coin/OSInstance.h>

namespace {
struct Expression : std::unique_ptr<OSnLNode>
{
  using std::unique_ptr<OSnLNode>::unique_ptr;
  Expression(OSnLNodeVariable &);
  Expression(double);
};
} // namespace

Expression::Expression(OSnLNodeVariable &var)
  : std::unique_ptr<OSnLNode>(var.cloneExprNode())
{}

Expression::Expression(double value)
  : std::unique_ptr<OSnLNode>([](double v)
    {
      OSnLNodeNumber *raw = new OSnLNodeNumber;
      raw->value = v;
      return raw;
    }(value))
{}

Expression operator+(Expression &&x, Expression &&y)
{
  Expression expr(new OSnLNodePlus);
  expr->m_mChildren[0] = x.release();
  expr->m_mChildren[1] = y.release();
  return expr;
}

Expression operator*(Expression &&x, Expression &&y)
{
  Expression expr(new OSnLNodeTimes);
  expr->m_mChildren[0] = x.release();
  expr->m_mChildren[1] = y.release();
  return expr;
}

Expression square(Expression &&x)
{
  Expression expr(new OSnLNodeSquare);
  expr->m_mChildren[0] = x.release();
  return expr;
}

Nl make_nl(int index, Expression &&expr)
{
  Nl nl;
  ScalarExpressionTree *tree = new ScalarExpressionTree;

  tree->m_treeRoot = expr.release();
  nl.idx = index;
  nl.osExpressionTree = tree;
  return nl;
}

int main()
{
  OSInstance instance;
  instance.setInstanceSource("An example of MINLP");
  instance.setInstanceDescription("A hard nonlinear program from "
    "https://apmonitor.com/online/view_pass.php?f=minlp.apm");

  instance.setVariableNumber(4);
  instance.addVariable(0, "x0", 1, 5, 'C');
  instance.addVariable(1, "x1", 1, 5, 'C');
  instance.addVariable(2, "x2", 1, 5, 'I');
  instance.addVariable(3, "x3", 1, 5, 'I');

  SparseVector objcoeffs;
  instance.setObjectiveNumber(1);

  // Indices of objective functions go -1, -2, -3, ....
  // This convention is how OS tells objectives from constraints.
  instance.addObjective(-1, "", "min", 0, 1, &objcoeffs);

  instance.setConstraintNumber(2); 
  instance.addConstraint(0, "", 25, OSDBL_MAX, 0);
  instance.addConstraint(1, "", 40, 40, 0);

  OSnLNodeVariable x0, x1, x2, x3;

  x0.idx = 0;
  x1.idx = 1;
  x2.idx = 2;
  x3.idx = 3;

  Nl exprs[] = {
    make_nl(-1, x0 * x3 * (x0 + x1 + x2) + x2),
    make_nl(0, x0 * x1 * x2 * x3),
    make_nl(1, square(x0) + square(x1) + square(x2) + square(x3)),
  };

  Nl *root[] = { exprs, exprs + 1, exprs + 2 };
  instance.setNonlinearExpressions(3, root);

  // From this point, `instance` has all data of my problem
}
jdh8 commented 1 year ago

I found the solver API. We can solve the instance with CouenneSolver

OSInstance instance /*= Somehow make an instance */;
CouenneSolver solver;
solver.osinstance = &instance;
solver.solve();