/* Copyright 2026, Gurobi Optimization, LLC */
/* This example formulates and solves the following simple nonlinear model
using the nonlinear constraint API:
minimize x - y
subject to sin(x) * x + x^2 * y <= 1
y = cos(x) (<=> cos(x) - y = 0)
-1 <= x <= 2
0 <= y <= 2
The nonlinear constraint is set via addNLConstr() using an expression tree.
Then we retrieve the tree with getNLConstrLHS() and verify it, delete it
with remove(), replace it with a simple linear constraint, and re-optimize.
*/
#include "gurobi_c++.h"
#include <cmath>
using namespace std;
int
main(int argc,
char *argv[])
{
try {
/* Create environment and model */
GRBEnv env = GRBEnv("nlconstr.log");
GRBModel model = GRBModel(env);
/* Create variables: x in [-1, 2], y in [0, 2] */
GRBVar x = model.addVar(-1.0, 2.0, 1.0, GRB_CONTINUOUS, "x");
GRBVar y = model.addVar(0.0, 2.0, -1.0, GRB_CONTINUOUS, "y");
/* Expression tree for sin(x) * x + x^2 * y (10 nodes)
*
* node 0: PLUS (root)
* node 1: MULTIPLY (child of 0) -> sin(x) * x
* node 2: SIN (child of 1)
* node 3: VARIABLE x (child of 2)
* node 4: VARIABLE x (child of 1)
* node 5: MULTIPLY (child of 0) -> x^2 * y
* node 6: POW (child of 5)
* node 7: VARIABLE x (child of 6)
* node 8: CONSTANT 2 (child of 6)
* node 9: VARIABLE y (child of 5)
*/
int nnodes_in1 = 10;
int opcode1[10] = {GRB_OPCODE_PLUS,
GRB_OPCODE_MULTIPLY,
GRB_OPCODE_SIN,
GRB_OPCODE_VARIABLE,
GRB_OPCODE_VARIABLE,
GRB_OPCODE_MULTIPLY,
GRB_OPCODE_POW,
GRB_OPCODE_VARIABLE,
GRB_OPCODE_CONSTANT,
GRB_OPCODE_VARIABLE};
double data1[10] = {-1.0, -1.0, -1.0,
(double) x.index(),
(double) x.index(),
-1.0, -1.0,
(double) x.index(),
2.0,
(double) y.index()};
int parent1[10] = {-1, 0, 1, 2, 1, 0, 5, 6, 6, 5};
/* Expression tree for cos(x) - y = 0 (4 nodes)
*
* node 0: MINUS (root)
* node 1: COS (child of 0)
* node 2: VARIABLE x (child of 1)
* node 3: VARIABLE y (child of 0)
*/
int nnodes_in2 = 4;
int opcode2[4] = {GRB_OPCODE_MINUS, /* 0: root */
GRB_OPCODE_COS, /* 1: cos(...) */
GRB_OPCODE_VARIABLE, /* 2: x */
GRB_OPCODE_VARIABLE}; /* 3: y */
double data2[4] = {-1.0, -1.0,
(double) x.index(),
(double) y.index()};
int parent2[4] = {-1, 0, 1, 0};
/* Add nonlinear constraint: sin(x)*x + x^2*y <= 1 */
GRBNLConstr nlc1 = model.addNLConstr(nnodes_in1, opcode1, data1, parent1,
GRB_LESS_EQUAL, 1.0,
"NonlinearConstraint1");
/* Add nonlinear constraint: cos(x) - y = 0 */
GRBNLConstr nlc2 = model.addNLConstr(nnodes_in2, opcode2, data2, parent2,
GRB_EQUAL, 0.0,
"NonlinearConstraint2");
/* Optimize model */
model.optimize();
int status = model.get(GRB_IntAttr_Status);
cout << "\nFirst solve (nonlinear constraints)" << endl;
if (status == GRB_OPTIMAL) {
cout << "Optimal objective: " << model.get(GRB_DoubleAttr_ObjVal) << endl;
cout << " x = " << x.get(GRB_DoubleAttr_X)
<< ", y = " << y.get(GRB_DoubleAttr_X) << endl;
} else if (status == GRB_INF_OR_UNBD) {
cout << "Model is infeasible or unbounded" << endl;
} else {
cout << "Optimization was stopped early" << endl;
}
/* Retrieve number of nodes (first pass with NULLs) */
int nnodes_out = -1;
model.getNLConstrLHS(nlc1, &nnodes_out, NULL, NULL, NULL);
cout << "\nRetrieved NL constraint with " << nnodes_out << " nodes" << endl;
/* Retrieve full tree (second pass) */
int* opcode_out = new int[nnodes_out];
double* data_out = new double[nnodes_out];
int* parent_out = new int[nnodes_out];
model.getNLConstrLHS(nlc1, &nnodes_out, opcode_out, data_out, parent_out);
/* Verify tree matches what we added */
for (int i = 0; i < nnodes_out; i++) {
if (opcode_out[i] != opcode1[i] || parent_out[i] != parent1[i] || data_out[i] != data1[i]) {
cout << "ERROR: tree mismatch at node " << i << endl;
}
}
delete[] opcode_out;
delete[] data_out;
delete[] parent_out;
/* Delete nonlinear constraints */
model.remove(nlc1);
model.remove(nlc2);
model.update();
cout << "Deleted nonlinear constraints" << endl;
/* Add a simple linear constraint instead: x + y >= 1 */
model.addConstr(x + y >= 1.0, "LinearConstraint");
/* Re-optimize */
model.optimize();
status = model.get(GRB_IntAttr_Status);
cout << "\nSecond solve (linear constraint)" << endl;
if (status == GRB_OPTIMAL) {
cout << "Optimal objective: " << model.get(GRB_DoubleAttr_ObjVal) << endl;
cout << " x = " << x.get(GRB_DoubleAttr_X)
<< ", y = " << y.get(GRB_DoubleAttr_X) << endl;
} else if (status == GRB_INF_OR_UNBD) {
cout << "Model is infeasible or unbounded" << endl;
} else {
cout << "Optimization was stopped early" << endl;
}
} catch(GRBException e) {
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
} catch(...) {
cout << "Exception during optimization" << endl;
}
return 0;
}