mip06.gms : Cuts and solution enumeration

Description

This model is based on the dice.176 model from model library.  To make
it work better as an example for MIP solution enumeration it was
cleaned up and constraints were added to make solutions and solution
representatives unique.  Then cuts were added.  By default, it stops
cutting/enumerating when all optimal solutions have been found.

Contributor: Steve Dirkse, Jan 2012


Small Model of Type : MIP


Category : GAMS Test library


Main file : mip06.gms

$title Cuts and solution enumeration (MIP06,SEQ=553)

$onText

This model is based on the dice.176 model from model library.  To make
it work better as an example for MIP solution enumeration it was
cleaned up and constraints were added to make solutions and solution
representatives unique.  Then cuts were added.  By default, it stops
cutting/enumerating when all optimal solutions have been found.

Contributor: Steve Dirkse, Jan 2012

$offText

* to run without cut generation use --SKIPCUTS=1 on the command line
* this will just solve the problem without cuts and by default will
* only return one solution
$if not set SKIPCUTS $set SKIPCUTS 0

sets
  f  'die faces'        / face1*face6 /
  d  'number of dice'   / die1*die3 /
  ;
$eval NV card(f)*card(d)
set
  v 'face values'       / v1*v%NV% /
  pp     'previous solutions' / p1*p500 /
  p(pp)
  ;
alias(f,fp),(d,dp);

parameters
  vals(v)            'possible face values'
  pMap(pp,d,f,v)     'map from previous solutions'
  winCnt(pp)
  M
  ;
vals(v) = ord(v);
M = card(d)*card(f);

variables
  xWins
  faceVal(d,f)      'value on die face - will be integer'
  ;
binary variables
  win(d,f,fp)       'true if faceVal(d,f) beats faceVal(d++,fp)'
  map(d,f,v)        'maps dice faces to values'
  ;

faceVal.lo(d,f) = 1;
faceVal.up(d,f) = card(d)*card(f);

faceVal.fx('die1','face1') = 1;

equation
  winBnd(d)       'bound the wins for d vs. d++'
  winDefA(d,f,fp) 'win(d,f,fp) implies dominance of (d,f) over (d++,fp)'
  winDefB(d,f,fp) 'not win(d,f,fp) implies dominance of (d++,fp) over (d,f)'
  faceDef(d,f)    'define the face values'
  ordered(d,f)    'order the face values on each die'
  unique(v)       'each value used only once'
  cuts(pp)        'cut off previous solutions'
  ;

winBnd(d)      .. sum{(f,fp), win(d,f,fp)} =g= xWins;

winDefA(d,f,fp).. faceVal(d,f) +  M*(1-win(d,f,fp)) =g= faceVal(d++1,fp) + 1;

winDefB(d,f,fp).. faceVal(d++1,fp) + M*win(d,f,fp)  =g= faceVal(d,f) + 1;

faceDef(d,f)   .. faceVal(d,f) =e= sum{v, vals(v)*map(d,f,v)};

ordered(d,f-1) .. faceVal(d,f-1) + 1 =l= faceVal(d,f);

unique(v)      .. sum{(d,f), map(d,f,v)} =e= 1;

cuts(p)        .. sum{(d,f,v), map(d,f,v) $[not pMap(p,d,f,v)] +
                            (1-map(d,f,v))$[    pMap(p,d,f,v)]}
                  =g= 1;

model dice / all /;

option optcr = 0.0;
p(pp) = no;
pMap(p,d,f,v) = 0;

execute_unload 'diceDef', f, d, v, vals;

solve dice using mip maximizing xWins;

option faceVal:0; display faceVal.l;

parameter rep1 Chance of winning against next;
rep1(d,f) = 100*sum(fp, win.l(d,f,fp)) / card(f);
rep1(d,'chance') = sum(f, rep1(d,f))/card(f);
option rep1:0; display rep1;

$if %SKIPCUTS% == 1 $exit

scalar
  cutoff
  nSols 'number of solutions found'
  ;
cutoff = 20.5;

option limrow=0, limcol=0, solprint=off;
option solveLink = %solveLink.loadLibrary%;
loop{pp$(dice.solvestat=%solveStat.normalCompletion% and
         dice.modelstat=%modelStat.optimal% and
         round(xWins.l)>=cutoff),

   p(pp) = yes;
   winCnt(pp) = round(xWins.l);
   pMap(pp,d,f,v ) = round(map.l(d,f,v));
   solve dice maximizing xWins using mip;
};
nSols = card(p);
display nSols;
execute_unload 'diceSolsCuts', f, d, v, vals, p, pMap, winCnt;
abort$[card(p) eq card(pp)] 'set pp too small';
file log /''/;
putclose log // 'Test finished OK' / '  cutoff = ', cutoff /  '  nSols = ', nSols //;