// Copyright 2026, Gurobi Optimization, LLC
// This example formulates and solves the following simple nonlinear model
// using the nonlinear 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 example is split into two functions that show two ways to build the
// nonlinear constraints with the new NL API:
//
// - nlconstrNLAPI(): uses the nonlinear expression API. The left-hand sides
// of the constraints are built as GRBNLExpr objects with the overloaded
// operators, the nonlinear functions hosted on GRBNLExpr, and the Pow()
// helper, and passed directly to AddNLConstr().
//
// - nlconstrNLAPIAdv(): uses the advanced expression-tree variant of
// AddNLConstr(), where the left-hand side of each constraint is described
// with arrays of opcodes, data, and parent pointers.
//
// In both cases we retrieve a nonlinear constraint with GetNLConstrLHS(),
// delete it with Remove(), replace it with a simple linear constraint, and
// re-optimize.
using System;
using Gurobi;
using static Gurobi.GRBNLExpr;
class nlconstr_cs
{
/// <summary>
/// Run both ways of building nonlinear constraints: the nonlinear
/// expression API and the advanced expression-tree API.
/// </summary>
static void Main()
{
try {
// First, the nonlinear expression API
nlconstrNLAPI();
// Then, the advanced expression-tree API
nlconstrNLAPIAdv();
} catch (GRBException e) {
Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
}
}
/// <summary>
/// Formulate the model with the nonlinear expression API, passing
/// GRBNLExpr left-hand sides to AddNLConstr().
/// </summary>
static void nlconstrNLAPI()
{
// Create environment
GRBEnv env = new GRBEnv("nlconstr.log");
// Create empty model
GRBModel model = new GRBModel(env);
// Create variables
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");
// Add nonlinear constraint: sin(x)*x + x^2*y <= 1.
// The left-hand side is built as a GRBNLExpr:
// GRBNLExpr.Sin(x) -> sin(x)
// GRBNLExpr.Pow(x, 2.0) -> x^2 (a SQUARE node)
GRBNLConstr nlc1 = model.AddNLConstr(
Sin(x) * x + Pow(x, 2.0) * y <= 1.0,
"NonlinearConstraint1");
// Add nonlinear constraint: cos(x) - y = 0
GRBNLConstr nlc2 = model.AddNLConstr(Cos(x) - y == 0.0,
"NonlinearConstraint2");
// Optimize model
model.Optimize();
Console.WriteLine("\nFirst solve (nonlinear constraints)");
if (model.Status == GRB.Status.OPTIMAL) {
Console.WriteLine("Optimal objective: {0:E4}", model.ObjVal);
Console.WriteLine(" x={0:F6}, y={1:F6}", x.X, y.X);
} else if (model.Status == GRB.Status.INF_OR_UNBD) {
Console.WriteLine("Model is infeasible or unbounded");
} else {
Console.WriteLine("Optimization was stopped early");
}
// Retrieve the first nonlinear constraint as a GRBNLExpr and print it
GRBNLExpr lhs = model.GetNLConstrLHS(nlc1);
Console.WriteLine("\nRetrieved NL constraint: \"" + lhs + "\"");
// Delete the nonlinear constraints
model.Remove(nlc1);
model.Remove(nlc2);
model.Update();
Console.WriteLine("Deleted nonlinear constraints");
// Add a simple linear constraint instead: x + y >= 1
model.AddConstr(x + y, GRB.GREATER_EQUAL, 1.0, "LinearConstraint");
// Re-optimize
model.Optimize();
Console.WriteLine("\nSecond solve (linear constraint)");
if (model.Status == GRB.Status.OPTIMAL) {
Console.WriteLine("Optimal objective: {0:E4}", model.ObjVal);
Console.WriteLine(" x={0:F6}, y={1:F6}", x.X, y.X);
} else if (model.Status == GRB.Status.INF_OR_UNBD) {
Console.WriteLine("Model is infeasible or unbounded");
} else {
Console.WriteLine("Optimization was stopped early");
}
// Dispose of model and env
model.Dispose();
env.Dispose();
}
/// <summary>
/// Formulate the model with the advanced expression-tree variant of
/// AddNLConstr(), where the left-hand side of each constraint is
/// described with arrays of opcodes, data, and parent pointers.
/// </summary>
static void nlconstrNLAPIAdv()
{
// Create environment
GRBEnv env = new GRBEnv("nlconstr.log");
// Create empty model
GRBModel model = new GRBModel(env);
// Create variables
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[] opcode1 = new int[] {
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 = new double[] {
-1.0, -1.0, -1.0,
x.Index,
x.Index,
-1.0, -1.0,
x.Index,
2.0,
y.Index
};
int[] parent1 = new int[] {-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[] opcode2 = new int[] {
GRB.OPCODE_MINUS,
GRB.OPCODE_COS,
GRB.OPCODE_VARIABLE,
GRB.OPCODE_VARIABLE
};
double[] data2 = new double[] {
-1.0, -1.0,
x.Index,
y.Index
};
int[] parent2 = new int[] {-1, 0, 1, 0};
// Add nonlinear constraint: sin(x)*x + x^2*y <= 1
GRBNLConstr nlc1 = model.AddNLConstr(opcode1, data1, parent1,
GRB.LESS_EQUAL, 1.0,
"NonlinearConstraint1");
// Add nonlinear constraint: cos(x) - y = 0
GRBNLConstr nlc2 = model.AddNLConstr(opcode2, data2, parent2,
GRB.EQUAL, 0.0,
"NonlinearConstraint2");
// Optimize model
model.Optimize();
Console.WriteLine("\nFirst solve (nonlinear constraints)");
if (model.Status == GRB.Status.OPTIMAL) {
Console.WriteLine("Optimal objective: {0:E4}", model.ObjVal);
Console.WriteLine(" x={0:F6}, y={1:F6}", x.X, y.X);
} else if (model.Status == GRB.Status.INF_OR_UNBD) {
Console.WriteLine("Model is infeasible or unbounded");
} else {
Console.WriteLine("Optimization was stopped early");
}
// Retrieve the first nonlinear constraint and print the number of nodes
int nnodes;
model.GetNLConstrLHS(nlc1, out nnodes, null, null, null);
Console.WriteLine("\nRetrieved NL constraint with " + nnodes + " nodes");
// Delete the nonlinear constraints
model.Remove(nlc1);
model.Remove(nlc2);
model.Update();
Console.WriteLine("Deleted nonlinear constraints");
// Add a simple linear constraint instead: x + y >= 1
model.AddConstr(x + y, GRB.GREATER_EQUAL, 1.0, "LinearConstraint");
// Re-optimize
model.Optimize();
Console.WriteLine("\nSecond solve (linear constraint)");
if (model.Status == GRB.Status.OPTIMAL) {
Console.WriteLine("Optimal objective: {0:E4}", model.ObjVal);
Console.WriteLine(" x={0:F6}, y={1:F6}", x.X, y.X);
} else if (model.Status == GRB.Status.INF_OR_UNBD) {
Console.WriteLine("Model is infeasible or unbounded");
} else {
Console.WriteLine("Optimization was stopped early");
}
// Dispose of model and env
model.Dispose();
env.Dispose();
}
}