NLConstr.java#

/* 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.
*/

import com.gurobi.gurobi.*;

public class NLConstr {

  public static void main(String[] args) {
    try {
      // 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,      // 0: root
        GRB.OPCODE_MULTIPLY,  // 1: sin(x)*x
        GRB.OPCODE_SIN,       // 2: sin(...)
        GRB.OPCODE_VARIABLE,  // 3: x
        GRB.OPCODE_VARIABLE,  // 4: x
        GRB.OPCODE_MULTIPLY,  // 5: x^2 * y
        GRB.OPCODE_POW,       // 6: x^2
        GRB.OPCODE_VARIABLE,  // 7: x
        GRB.OPCODE_CONSTANT,  // 8: 2.0
        GRB.OPCODE_VARIABLE   // 9: y
      };
      double[] data1 = new double[] {
        -1.0, -1.0, -1.0,
         x.index(),  // x index
         x.index(),  // x index
        -1.0, -1.0,
         x.index(),  // x index
         2.0,
         y.index()   // 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,     // 0: root
        GRB.OPCODE_COS,       // 1: cos(...)
        GRB.OPCODE_VARIABLE,  // 2: x
        GRB.OPCODE_VARIABLE,  // 3: y
      };
      double[] data2 = new double[] {
        -1.0, -1.0,
         x.index(),  // x index
         y.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();

      System.out.println("\nFirst solve (nonlinear constraints)");
      if (model.get(GRB.IntAttr.Status) == GRB.OPTIMAL) {
        System.out.printf("Optimal objective: %.4e%n",
                          model.get(GRB.DoubleAttr.ObjVal));
        System.out.printf("  x=%.6f, y=%.6f%n",
                          x.get(GRB.DoubleAttr.X),
                          y.get(GRB.DoubleAttr.X));
      } else if (model.get(GRB.IntAttr.Status) == GRB.INF_OR_UNBD) {
        System.out.println("Model is infeasible or unbounded");
      } else {
        System.out.println("Optimization was stopped early");
      }

      // Retrieve the nonlinear constraint and print the number of nodes
      int[] nnodes = new int[1];
      model.getNLConstrLHS(nlc1, nnodes, null, null, null);
      System.out.println("\nRetrieved NL constraint with " + nnodes[0] +
                         " nodes");

      // Delete nonlinear constraints
      model.remove(nlc1);
      model.remove(nlc2);
      model.update();
      System.out.println("Deleted nonlinear constraints");

      // Add a simple linear constraint instead: x + y >= 1
      GRBLinExpr expr = new GRBLinExpr();
      expr.addTerm(1.0, x);
      expr.addTerm(1.0, y);
      model.addConstr(expr, GRB.GREATER_EQUAL, 1.0, "LinearConstraint");

      // Re-optimize
      model.optimize();

      System.out.println("\nSecond solve (linear constraint)");
      if (model.get(GRB.IntAttr.Status) == GRB.OPTIMAL) {
        System.out.printf("Optimal objective: %.4e%n",
                          model.get(GRB.DoubleAttr.ObjVal));
        System.out.printf("  x=%.6f, y=%.6f%n",
                          x.get(GRB.DoubleAttr.X),
                          y.get(GRB.DoubleAttr.X));
      } else if (model.get(GRB.IntAttr.Status) == GRB.INF_OR_UNBD) {
        System.out.println("Model is infeasible or unbounded");
      } else {
        System.out.println("Optimization was stopped early");
      }

      // Dispose of model and environment
      model.dispose();
      env.dispose();

    } catch (GRBException e) {
      System.out.println("Error code: " + e.getErrorCode() + ". " +
                         e.getMessage());
    }
  }
}