Sensitivity.java#

// Copyright 2024, Gurobi Optimization, LLC

// A simple sensitivity analysis example which reads a MIP model from a
// file and solves it. Then uses the scenario feature to analyze the impact
// w.r.t. the objective function of each binary variable if it is set to
// 1-X, where X is its value in the optimal solution.
//
// Usage:
//     java Sensitivity <model filename>

import com.gurobi.gurobi.*;

public class Sensitivity {

    // Maximum number of scenarios to be considered
    private static final int MAXSCENARIOS = 100;

    public static void main(String[] args) {

    if (args.length < 1) {
      System.out.println("Usage: java Sensitivity filename");
      System.exit(1);
    }

    try {

      // Create environment
      GRBEnv env = new GRBEnv();

      // Read model
      GRBModel model = new GRBModel(env, args[0]);

      int scenarios;

      if (model.get(GRB.IntAttr.IsMIP) == 0) {
        System.out.println("Model is not a MIP");
        System.exit(1);
      }

      // Solve model
      model.optimize();

      if (model.get(GRB.IntAttr.Status) != GRB.OPTIMAL) {
        System.out.println("Optimization ended with status "
                           + model.get(GRB.IntAttr.Status));
        System.exit(1);
      }

      // Store the optimal solution
      double   origObjVal = model.get(GRB.DoubleAttr.ObjVal);
      GRBVar[] vars       = model.getVars();
      double[] origX      = model.get(GRB.DoubleAttr.X, vars);

      scenarios = 0;

      // Count number of unfixed, binary variables in model. For each we
      // create a scenario.
      for (int i = 0; i < vars.length; i++) {
        GRBVar v     = vars[i];
        char   vType = v.get(GRB.CharAttr.VType);

        if (v.get(GRB.DoubleAttr.LB) == 0                 &&
            v.get(GRB.DoubleAttr.UB) == 1                 &&
            (vType == GRB.BINARY || vType == GRB.INTEGER)   ) {

          scenarios++;

          if (scenarios >= MAXSCENARIOS)
            break;
        }
      }

      System.out.println("###  construct multi-scenario model with "
                         + scenarios + " scenarios");

      // Set the number of scenarios in the model */
      model.set(GRB.IntAttr.NumScenarios, scenarios);

      scenarios = 0;

      // Create a (single) scenario model by iterating through unfixed
      // binary variables in the model and create for each of these
      // variables a scenario by fixing the variable to 1-X, where X is its
      // value in the computed optimal solution
      for (int i = 0; i < vars.length; i++) {
        GRBVar v     = vars[i];
        char   vType = v.get(GRB.CharAttr.VType);

        if (v.get(GRB.DoubleAttr.LB) == 0                 &&
            v.get(GRB.DoubleAttr.UB) == 1                 &&
            (vType == GRB.BINARY || vType == GRB.INTEGER) &&
            scenarios < MAXSCENARIOS                        ) {

          // Set ScenarioNumber parameter to select the corresponding
          // scenario for adjustments
          model.set(GRB.IntParam.ScenarioNumber, scenarios);

          // Set variable to 1-X, where X is its value in the optimal solution */
          if (origX[i] < 0.5)
            v.set(GRB.DoubleAttr.ScenNLB, 1.0);
          else
            v.set(GRB.DoubleAttr.ScenNUB, 0.0);

          scenarios++;
        } else {
          // Add MIP start for all other variables using the optimal
          // solution of the base model
          v.set(GRB.DoubleAttr.Start, origX[i]);
        }
      }

      // Solve multi-scenario model
      model.optimize();

      // In case we solved the scenario model to optimality capture the
      // sensitivity information
      if (model.get(GRB.IntAttr.Status) == GRB.OPTIMAL) {

        // get the model sense (minimization or maximization)
        int modelSense = model.get(GRB.IntAttr.ModelSense);

        scenarios = 0;

        for (int i = 0; i < vars.length; i++) {
          GRBVar v     = vars[i];
          char   vType = v.get(GRB.CharAttr.VType);

          if (v.get(GRB.DoubleAttr.LB) == 0                 &&
              v.get(GRB.DoubleAttr.UB) == 1                 &&
              (vType == GRB.BINARY || vType == GRB.INTEGER)   ) {

            // Set scenario parameter to collect the objective value of the
            // corresponding scenario
            model.set(GRB.IntParam.ScenarioNumber, scenarios);

            // Collect objective value and bound for the scenario
            double scenarioObjVal = model.get(GRB.DoubleAttr.ScenNObjVal);
            double scenarioObjBound = model.get(GRB.DoubleAttr.ScenNObjBound);

            System.out.print("Objective sensitivity for variable "
                             + v.get(GRB.StringAttr.VarName) + " is ");

            // Check if we found a feasible solution for this scenario
            if (modelSense * scenarioObjVal >= GRB.INFINITY) {
              // Check if the scenario is infeasible
              if (modelSense * scenarioObjBound >= GRB.INFINITY)
                System.out.println("infeasible");
              else
                System.out.println("unknown (no solution available)");
            } else {
              // Scenario is feasible and a solution is available
              System.out.println("" + modelSense * (scenarioObjVal - origObjVal));
            }

            scenarios++;

            if (scenarios >= MAXSCENARIOS)
              break;
          }
        }
      }

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

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