nlconstr_cs.cs#

// 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();
  }
}