GAMS [ Home | Documentation | Solvers | APIs and Tools | Model Libraries | Sales | Support | Contact Us | Search ]

GAMS/CPLEX Indicator Constraints

An indicator constraint is a new way of expressing relationships among variables by specifying a binary variable to control whether or not a constraint takes effect. For example, indicator constraints are useful in problems where there are fixed charges to express only if a given variable comes into play.

So-called Big M formulations often exhibit trickle flow, and at times, they behave unstably. The main purpose of indicator constraints is to avoid the unwanted side-effects of Big M formulations. Generally, the use of indicator constraints is not warranted when the unwanted side-effects of Big M formulations are not present.

For example, instead of the following Big M formulation, which relies on the x values summing to less than one billion (a formulation that can cause numeric instability or undesirable solutions in some situations):

constr01.. x1 + x2 + x3 =l= 1e+9*y; // may cause problems 
a natural way of entering an indicator constraint, where y is a binary variable, would look like this:
constr01$(y=0).. x1 + x2 + x3 =l= 0; // alternative 
Since indicators are currently supported by CPLEX only, we have delayed the necessary chance of the GAMS syntax to allow for endogenous varibles in the $-condition. We still want to give GAMS/CPLEX user's access to this new feature, therefore the mapping between constraints and binary variables is described in a Cplex option file cplex.opt. The example from above would be implemented in the following way:
constr01.. x1 + x2 + x3 =l= 0;
plus additional information in the Cplex option file cplex.opt:
indic constr01$y 0
This has the following effect: Equation constr01 will become an indicator constraint and becomes effective in a solution where the binary variable takes the value 0. If the value of y in a solution is 1, the constraint is not active.

Note, that this way of entering an indicator constraint is dangerous since the option files changes the model (usually an option file has some effect on the performance of the algorithm). Therefore, GAMS/CPLEX will abort if there is a problem processing the indicator options in the Cplex option file. Moreover, if the model is given to a different solver or to CPLEX without the option file containt the indicator mapping, a very different model is solved. The current experimental implementation of indicator constraints requires a significant amount of caution from the user.

There are two ways of entering the equation/binary variable mapping in a Cplex option file: (1) using an indexed format, (2) using labels.

The indexed format is a convenient short hand notation which borrows its syntax from the GAMS syntax. It requires that the indices for the binary variable are already present in the index set of the equation. For example, the following invalid GAMS syntax with an endogenous variable in the $-condition

equ1(i,j,k)$(ord(i)<ord(j) and bin1(i,k)=1).. lhs =l= rhs;
would be specified like this in the GAMS file
equ1(i,j,k)$(ord(i)<ord(j)).. lhs =l= rhs;
plus the Cplex option file
indic equ1(i,j,k)$bin1(i,k) 1
In cases where the binary variable indices are not present in the equation indices (or is adjusted using lags or leads), one needs to specify the mapping of all individual equations and variables of the indicator constraints. For example,
set i /i1*i3/, j /j1*j2/;
binary variable bin1(j); equation equ1(i,j);
equ1(i,j)$(bin1(j++1)=0) lhs =e= 0;
This would be specified using the label format of indicator constraint as follows. The GAMS file
equ1(i,j).. lhs =e= 0;
plus the Cplex option file
indic equ1('i1','j1')$bin1('j2') 0
indic equ1('i1','j2')$bin1('j1') 0
indic equ1('i2','j1')$bin1('j2') 0
indic equ1('i2','j2')$bin1('j1') 0
indic equ1('i3','j1')$bin1('j2') 0
indic equ1('i3','j2')$bin1('j1') 0
Finally, we want to present a fixed-charge network example based on the TRNSPORT model from the GAMS Model library that used indicator constraints, big M forumulations and a forumulation that makes it easy to switch between these two.
$Title  Fixed Charge Transportation Problem with Indicator Constraints


  Sets
       i   canning plants   / seattle, san-diego /
       j   markets          / new-york, chicago, topeka / ;

  Parameters

       a(i)  capacity of plant i in cases
         /    seattle     350
              san-diego   600  /

       b(j)  demand at market j in cases
         /    new-york    325
              chicago     300
              topeka      275  / ;

  Table d(i,j)  distance in thousands of miles
                    new-york       chicago      topeka
      seattle          2.5           1.7          1.8
      san-diego        2.5           1.8          1.4  ;

  Scalar f  freight in dollars per case per thousand miles  /90/ ;

  Parameter c(i,j)  transport cost in thousands of dollars per case ;

            c(i,j) = f * d(i,j) / 1000 ;


  Parameter fixcost(i,j)  fixed cost in thousands of dollars ;

            fixcost(i,j) = 10*d(i,j) / 1000 ;

  Scalar minshipping minimum shipping of cases /100/;

  Scalar bigM sufficiently large number; bigM = smax(i, a(i));

  Variables
       x(i,j)   shipment quantities in cases
       use(i,j) is 1 if arc is used in solution
       z        total transportation costs in thousands of dollars ;

  Positive Variable x ; Binary Variable use;

  Equations
       cost         define objective function
       supply(i)    observe supply limit at plant i
       demand(j)    satisfy demand at market j 
       minship(i,j) ensure minimum shipping
       maxship(i,j) ensure zero shipping if use variable is 0;

  cost ..        z  =e=  sum((i,j), c(i,j)*x(i,j) + fixcost(i,j)*use(i,j)) ;

  supply(i) ..   sum(j, x(i,j))  =l=  a(i) ;

  demand(j) ..   sum(i, x(i,j))  =g=  b(j) ;

  minship(i,j).. x(i,j) =g= minshipping*use(i,j);

  maxship(i,j).. x(i,j) =l= bigM*use(i,j);

  Option limrow=0, limcol=0, optcr=0, mip=cplex;

  Model bigMModel /all/ ;

  Solve bigMModel using mip minimizing z ;

* Now let's build a model for the same problem using indicator constraints
  Equations
       iminship(i,j) ensure minimum shipping using indicator constraints
       imaxship(i,j) ensure zero shipping if use variable is 0 using indicator constraints;
  
  iminship(i,j).. x(i,j) =g= minshipping;

  imaxship(i,j).. x(i,j) =e= 0;

  Model indicatorModel /cost, supply, demand, iminship, imaxship/ ;

  file fcpx Cplex Option file / cplex.opt /;

  putclose fcpx 'indic iminship(i,j)$use(i,j) 1' / 'indic imaxship(i,j)$use(i,j) 0';

  indicatorModel.optfile = 1; Solve indicatorModel using mip minimizing z ;

* Let's do the same using indicator option with labels
  loop((i,j),
     put fcpx 'indic ' iminship.tn(i,j) '$' use.tn(i,j) yes
            / 'indic ' imaxship.tn(i,j) '$' use.tn(i,j) no / );
  putclose fcpx;

  Solve indicatorModel using mip minimizing z ;

* Now let's build a model for the same problem that can be used with
* and without indicator constraints. This can become handy when
* debugging a model with indicator constraints

  Positive Variable minslack(i,j), maxslack(i,j);

  Equations
       xminship(i,j) ensure minimum shipping using indicator constraints and bigM
       xmaxship(i,j) ensure zero shipping ig use variable is 0 using indicator constraints and bigM
       bndminslack(i,j) ensure minslack is zero if use variable is 1
       bndmaxslack(i,j) ensure maxslack is zero if use variable is 0;
  
  xminship(i,j).. x(i,j) =g= minshipping - minslack(i,j);

  xmaxship(i,j).. x(i,j) =e= 0 + maxslack(i,j);

  bndminslack(i,j).. minslack(i,j) =l= bigM*(1-use(i,j));
  bndmaxslack(i,j).. maxslack(i,j) =l= bigM*use(i,j);

  Model indicatorbigMModel /cost, supply, demand, xminship, xmaxship, bndminslack, bndmaxslack/ ;

* Let's first solve this without use of indicators

  indicatorbigMModel.optfile = 0; Solve indicatorbigMModel using mip minimizing z ;

* Now we will use indicators and therefore we don't need the slacks

  putclose fcpx 'indic xminship(i,j)$use(i,j) 1' / 'indic xmaxship(i,j)$use(i,j) 0';

  minslack.fx(i,j) = 0;   maxslack.fx(i,j) = 0;
  indicatorbigMModel.optfile = 1; Solve indicatorbigMModel using mip minimizing z ;

* We can also mix and match bigM with indicator constraints

  putclose fcpx 'indic xminship(i,j)$use(i,j) 1';

  minslack.fx(i,j) = 0;   maxslack.up(i,j) = inf;
  indicatorbigMModel.optfile = 1; Solve indicatorbigMModel using mip minimizing z ;