Initializing help system before first use

Solving the office cleaning problem with different algorithms


Type: Office cleaning
Rating: 3 (intermediate)
Description: The program oclean.c runs the office cleaning problem in the model file cleana.mos with different algorithm settings.
File(s): oclean.c, cleana.mos
Data file(s): clparam.dat, cldata.dat


oclean.c
/*******************************************************
   Mosel Example Problems
   ====================== 

   file oclean.c
   `````````````
   Run the Office cleaning problem with different 
   algorithm settings.
   
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, 2002, rev. 2007
********************************************************/

#include <stdio.h>
#include "xprm_mc.h"

int main()
{
 XPRMmodel mod;
 int result;

 if(XPRMinit())                         /* Initialize Mosel */
  return 1;
                                        /* Execute a model file */
 if(XPRMexecmod(NULL, "cleana.mos", NULL, &result, &mod))
  return 2;

 if(XPRMexecmod(NULL, "cleana.mos", "ALG=1", &result, &mod))
  return 3;

 if(XPRMexecmod(NULL, "cleana.mos", "ALG=2", &result, &mod))
  return 4;

 if(XPRMexecmod(NULL, "cleana.mos", "ALG=3", &result, &mod))
  return 5;

 if(XPRMexecmod(NULL, "cleana.mos", "ALG=4", &result, &mod))
  return 6;

 if(XPRMexecmod(NULL, "cleana.mos", "ALG=5", &result, &mod))
  return 7;

 if(XPRMexecmod(NULL, "cleana.mos", "ALG=6", &result, &mod))
  return 8;

 return 0;
}

cleana.mos
(!*******************************************************
   Mosel User Guide Examples
   =========================

   file cleana.mos
   ```````````````
   Solving a MIP by cut-and-branch or branch-and-cut.
   - Configurable algorithm choice -

   *** This model cannot be run with a Community Licence 
       for the provided data instance ***

   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, 2001, rev. June 2010
*******************************************************!)

model "Office cleaning"
 uses "mmxprs","mmsystem"

 parameters
  ALG = 0                               ! Default algorithm: no user cuts
 end-parameters 

 forward procedure top_cut_gen
 forward procedure tree_cut_gen
 forward procedure tree_cut_gen2

 declarations
  PARAM: array(1..3) of integer
 end-declarations
 
 initializations from 'clparam.dat'
  PARAM
 end-initializations

 declarations
  NSITES = PARAM(1)                     ! Number of sites 
  NAREAS = PARAM(2)                     ! Number of areas (subsets of sites) 
  NCONTRACTORS = PARAM(3)               ! Number of contractors 
  AREAS = 1..NAREAS
  CONTR = 1..NCONTRACTORS
  SITES = 1..NSITES
  AREA: array(SITES) of integer         ! Area site is in 
  NUMSITE: array(AREAS) of integer      ! Number of sites in an area 
  LOWCON: array(AREAS) of integer       ! Lower limit on the number of 
                                        ! contractors per area 
  UPPCON: array(AREAS) of integer       ! Upper limit on the number of 
                                        ! contractors per area 
  ADJACENT: array(AREAS,AREAS) of integer    ! 1 if areas adjacent, 0 otherwise
  PRICE: array(SITES,CONTR) of real     ! Price per contractor per site 

  clean: dynamic array(CONTR,SITES) of mpvar ! 1 iff contractor c cleans site s 
  alloc: array(CONTR,AREAS) of mpvar    ! 1 iff contractor allocated to a site 
                                        !  in area a 

  feastol: real                         ! Zero tolerance
  solc: array(CONTR,SITES) of real      ! Sol. values for variables `clean'
  sola: array(CONTR,AREAS) of real      ! Sol. values for variables `alloc'
 end-declarations

 initializations from 'cldata.dat'
  [NUMSITE,LOWCON,UPPCON] as 'AREADATA'
  ADJACENT
  PRICE
 end-initializations 

 ct:=1
 forall(a in AREAS) do
  forall(s in ct..ct+NUMSITE(a)-1)
   AREA(s):=a
  ct+= NUMSITE(a)
 end-do
 
 forall(c in CONTR, s in SITES | PRICE(s,c) > 0.01) create(clean(c,s))
 
! Objective: Minimize total cost of all cleaning contracts 
 Cost:= sum(c in CONTR, s in SITES) PRICE(s,c)*clean(c,s)

! Each site must be cleaned by exactly one contractor 
 forall(s in SITES) sum(c in CONTR) clean(c,s) = 1

! Ban same contractor from serving adjacent areas 
 forall(c in CONTR, a,b in AREAS | a > b and ADJACENT(a,b) = 1)
  alloc(c,a) + alloc(c,b) <= 1   

! Specify lower & upper limits on contracts per area 
 forall(a in AREAS | LOWCON(a)>0)
  sum(c in CONTR) alloc(c,a) >= LOWCON(a)
 forall(a in AREAS | UPPCON(a)<NCONTRACTORS) 
  sum(c in CONTR) alloc(c,a) <= UPPCON(a)

! Define alloc(c,a) to be 1 iff some clean(c,s)=1 for sites s in area a 
 forall(c in CONTR, a in AREAS) do
  alloc(c,a) <= sum(s in SITES| AREA(s)=a) clean(c,s) 
  alloc(c,a) >= 1.0/NUMSITE(a) * sum(s in SITES| AREA(s)=a) clean(c,s)  
 end-do
 
 forall(c in CONTR) do
  forall(s in SITES) clean(c,s) is_binary
  forall(a in AREAS) alloc(c,a) is_binary
 end-do
 
 starttime:= gettime

! Uncomment to get detailed MIP output
 setparam("XPRS_VERBOSE", true)
 setparam("XPRS_LPLOG", 0)
 setparam("XPRS_MIPLOG", 3)

 feastol:= getparam("XPRS_FEASTOL")  ! Get the Optimizer zero tolerance
 setparam("zerotol", feastol * 10)   ! Set Mosel comparison tolerance  
 
 writeln("**************ALG=",ALG,"***************")

 case ALG of
  1: tree_cut_gen                     ! Set up cut generation in B&B tree
  2: top_cut_gen                      ! Constraint generation at top node
  3: do
      tree_cut_gen
      top_cut_gen
     end-do 
  4: setparam("XPRS_CUTSTRATEGY", 0)   
  5: setparam("XPRS_EXTRAROWS", 5000)
  6: do
      setparam("XPRS_CUTSTRATEGY", 0)
      setparam("XPRS_HEURSTRATEGY", 0) 
     end-do
  7: tree_cut_gen2
 end-case

 minimize(Cost)                       ! Solve the MIP problem
 writeln("(", gettime-starttime, " sec) Global status ", 
         getparam("XPRS_MIPSTATUS"), ", best solution: ", getobjval); 


!*************************************************************************
!  Cut generation loop at the top node:                                 
!  * solve the LP and save the basis                                    
!  * get the solution values                                            
!  * identify and set up violated constraints                           
!  * load the modified problem and load the saved basis                 
!*************************************************************************

 procedure top_cut_gen

  declarations
   MAXCUTS = 5000                     ! Max no. of constraints added in total
   MAXPCUTS = 1000                    ! Max no. of constraints added per pass
   MAXPASS  = 50                      ! Max no. passes
   ncut, npass, npcut: integer        ! Counters for cuts and passes
   objval: real
   bas: basis                         ! LP basis
  end-declarations

  setparam("XPRS_CUTSTRATEGY", 0)     ! Disable automatic cuts
  setparam("XPRS_HEURSTRATEGY", 0)    ! Disable MIP heuristics
  setparam("XPRS_PRESOLVE", 0)        ! Switch presolve off
  ncut:=0 
  npass:=0

  while (ncut<MAXCUTS and npass<MAXPASS) do
    npass+=1
    npcut:= 0
    minimize(XPRS_LIN, Cost)          ! Solve the LP
    if (npass>1 and objval=getobjval) then break; end-if
    savebasis(bas)                    ! Save the current basis
    objval:= getobjval                ! Get the objective value

    forall(c in CONTR) do             ! Get the solution values
      forall(a in AREAS) sola(c,a):=getsol(alloc(c,a))
      forall(s in SITES) solc(c,s):=getsol(clean(c,s))
    end-do
  
! Search for violated constraints and add them to the problem:
    forall(s in SITES)
     forall(c in CONTR)
      if (solc(c,s)-sola(c,AREA(s)) > 0) then
       alloc(c,AREA(s)) >= clean(c,s)
       ncut+=1
       npcut+=1
       if (npcut>MAXPCUTS or ncut>MAXCUTS) then break 2; end-if
      end-if
   
    writeln("Pass ", npass, " (", gettime-starttime, " sec), objective value ", 
            objval, ", cuts added: ", npcut, " (total ", ncut,")")

    if npcut=0 then
      break
    else
      loadprob(Cost)                  ! Reload the problem
      loadbasis(bas)                  ! Load the saved basis
    end-if
  end-do

                                      ! Display cut generation status
  write("Cut phase completed: ")
  if ncut >= MAXCUTS then writeln("space for cuts exhausted")
  elif npass >= MAXPASS then writeln("maximum number of passes reached")
  else writeln("no more violations or no improvement to objective")
  end-if
 
 end-procedure

!*************************************************************************
!  Cut generation loop at tree nodes:                                 
!  * get the solution values                                            
!  * identify violated constraints and add them to the cut pool                  !*************************************************************************

 public function cb_node:boolean

  declarations
   ncut: integer                      ! Counters for cuts
   cut: array(range) of linctr        ! Cuts
   cutid: array(range) of integer     ! Cut type identification
   type: array(range) of integer      ! Cut constraint type 
  end-declarations

  depth:=getparam("XPRS_NODEDEPTH")

  if depth<4 then
   ncut:=0 

! Get the solution values
   forall(c in CONTR) do
    forall(a in AREAS) sola(c,a):=getsol(alloc(c,a))
    forall(s in SITES) solc(c,s):=getsol(clean(c,s))
   end-do
  
! Search for violated constraints
   forall(s in SITES)
    forall(c in CONTR)
     if (solc(c,s)-sola(c,AREA(s)) > 0) then
      cut(ncut):= alloc(c,AREA(s)) - clean(c,s)
      cutid(ncut):= 1
      type(ncut):= CT_GEQ
      ncut+=1
     end-if

! Add cuts to the problem
   if ncut>0 then 
    addcuts(cutid, type, cut);  
    writeln("Cuts added : ", ncut, " (depth ", depth, ", node ", 
            getparam("XPRS_NODES"), ", obj. ", getparam("XPRS_LPOBJVAL"), ")")
   end-if
  end-if
  
  returned:=false                     ! Call this function once per node
 end-function

! ****Optimizer settings for using the cut manager****

 procedure tree_cut_gen
 
  setparam("XPRS_CUTSTRATEGY", 0)     ! Disable automatic cuts
  setparam("XPRS_HEURSTRATEGY", 0)    ! Disable MIP heuristics
  setparam("XPRS_PRESOLVE", 0)        ! Switch presolve off
  setparam("XPRS_EXTRAROWS", 5000)    ! Reserve extra rows in matrix
  setparam("XPRS_EXTRAELEMS", 2*5000) ! Reserve extra matrix elements

  setcallback(XPRS_CB_CUTMGR, "cb_node")
 end-procedure

! **** Same as 'cb_node' but several cut generation runs per node ****
 public function cb_node2:boolean

  declarations
   ncut: integer                      ! Counters for cuts
   cut: array(range) of linctr        ! Cuts
   cutid: array(range) of integer     ! Cut type identification
   type: array(range) of integer      ! Cut constraint type 
  end-declarations

  depth:=getparam("XPRS_NODEDEPTH")
  returned:=false                     ! Call this function once per node
  ncut:=0 

  if depth<4 then

! Get the solution values
   forall(c in CONTR) do
    forall(a in AREAS) sola(c,a):=getsol(alloc(c,a))
    forall(s in SITES) solc(c,s):=getsol(clean(c,s))
   end-do
  
! Search for violated constraints
   forall(s in SITES)
    forall(c in CONTR)
     if (solc(c,s)-sola(c,AREA(s)) > 0) then
      cut(ncut):= alloc(c,AREA(s)) - clean(c,s)
      cutid(ncut):= 1
      type(ncut):= CT_GEQ
      ncut+=1
     end-if

! Add cuts to the problem
   if ncut>0 then 
    addcuts(cutid, type, cut);  
    writeln("Cuts added : ", ncut, " (depth ", depth, ", node ", 
            getparam("XPRS_NODES"), ", obj. ", getparam("XPRS_LPOBJVAL"), ")")
!    writeln(getparam("XPRS_CUTS"))
    returned:=true                    ! Call this function repeatedly per node
   end-if
  end-if
  
 end-function

! ****Optimizer settings for using the cut manager****

 procedure tree_cut_gen2
 
  setparam("XPRS_CUTSTRATEGY", 0)     ! Disable automatic cuts
  setparam("XPRS_HEURSTRATEGY", 0)    ! Disable MIP heuristics
  setparam("XPRS_PRESOLVE", 0)        ! Switch presolve off
  setparam("XPRS_EXTRAROWS", 5000)    ! Reserve extra rows in matrix
  setparam("XPRS_EXTRAELEMS", 2*5000) ! Reserve extra matrix elements

  setcallback(XPRS_CB_CUTMGR, "cb_node2")
 end-procedure

end-model

© 2001-2020 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.