Initializing help system before first use

D. Loading and cutting problems


Description:

Problem name and type, features Difficulty
D‑1 Wagon load balancing: Nonpreemptive scheduling on parallel machines ****
heuristic solution requiring sorting algorithm, formulation of maximin objective; nested subroutines: function returning heuristic solution value and sorting procedure, ceil, getsize, if-then, break, exit, all loop types (forall-do, repeat-until, while-do), setparam, cutoff value, loading a MIP start solution
D‑2 Barge loading: Knapsack problem **
incremental problem definition with 3 different objectives, procedure for solution printing
D‑3 Tank loading: Loading problem ***
2 objectives; data preprocessing, as, dynamic creation of variables, procedure for solution printing, if-then-else
D‑4 Backing up files: Bin-packing problem **
2 versions of mathematical model, symmetry breaking; data preprocessing, ceil, range
D‑5 Cutting sheet metal: Covering problem *
D‑6 Cutting steel bars for desk legs: Cutting-stock problem **
set operation(s) on range sets, set of integer (data as set contents)


File(s): d1wagon.mos (Mar. 2002), d1wagon2.mos (Sep. 2007), d2ship.mos (Mar. 2002), d3tanks.mos (Mar. 2002), d4backup.mos (Mar. 2002), d5cutsh.mos (Mar. 2002), d6cutbar.mos (Mar. 2002)
Data file(s): d1wagon.dat, d2ship.dat, d3tanks.dat, d4backup.dat, d5cutsh.dat, d6cutbar.dat

d1wagon.mos
(!******************************************************
   Mosel Example Problems
   ======================

   file d1wagon.mos
   ````````````````
   Load balancing of train wagons
   
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Mar. 2002
*******************************************************!)

model "D-1 Wagon load balancing"
 uses "mmxprs"

 forward function solve_heur:real
 forward procedure shell_sort(A:array(range) of integer, 
                              I:array(range) of integer)
 
 declarations   
  BOXES = 1..16                      ! Set of boxes
  WAGONS = 1..3                      ! Set of wagons

  WEIGHT: array(BOXES) of integer    ! Box weights
  WMAX: integer                      ! Weight limit per wagon

  load: array(BOXES,WAGONS) of mpvar ! 1 if box loaded on wagon, 0 otherwise
  maxweight: mpvar                   ! Weight of the heaviest wagon load
 end-declarations

 initializations from 'd1wagon.dat'
  WEIGHT WMAX
 end-initializations

! Solve the problem heuristically and terminate the program if the
! heuristic solution is good enough
 if solve_heur<=WMAX then
  writeln("Heuristic solution fits capacity limits")
  exit(0)
 end-if

! Every box into one wagon
 forall(b in BOXES) sum(w in WAGONS) load(b,w) = 1
 
! Limit the weight loaded into every wagon 
 forall(w in WAGONS) sum(b in BOXES) WEIGHT(b)*load(b,w) <= maxweight
 
! Bounds on maximum weight
 maxweight <= WMAX
 maxweight >= ceil((sum(b in BOXES) WEIGHT(b))/3)

 forall(b in BOXES,w in WAGONS) load(b,w) is_binary

! Alternative to lower bound on maxweight: adapt the optimizer cutoff value 
! setparam("XPRS_MIPADDCUTOFF",-0.99999)

! Uncomment the following line to see the optimizer log
 setparam("XPRS_VERBOSE",true) 

! Minimize the heaviest load
 minimize(maxweight)                    ! Start optimization
 
! Solution printing
 writeln("Optimal solution:\n Max weight: ", getobjval)
 forall(w in WAGONS) do
  write(" ", w, ":")
  forall(b in BOXES) write(if(getsol(load(b,w))=1, " "+b, ""))
  writeln(" (total weight: ", getsol(sum(b in BOXES) WEIGHT(b)*load(b,w)), ")")
 end-do

!-----------------------------------------------------------------

(! LPT (Longest processing time) heuristic: 
   One at a time place the heaviest unassigned box onto the wagon with 
   the least load
!)
   
 function solve_heur:real
  declarations
   ORDERW: array(BOXES) of integer      ! Box indices in decreasing weight order
   Load: array(WAGONS,range) of integer ! Boxes loaded onto the wagons 
   CurWeight: array(WAGONS) of integer  ! Current weight of wagon loads 
   CurNum: array(WAGONS) of integer     ! Current number of boxes per wagon
  end-declarations

 ! Copy the box indices into array ORDERW and sort them in decreasing 
 ! order of box weights (the sorted indices are returned in array ORDERW) 
  forall(b in BOXES) ORDERW(b):=b
  shell_sort(WEIGHT,ORDERW)

 ! Distribute the loads to the wagons using the LPT heuristic 
  forall(b in BOXES) do
   v:=1                                 ! Find wagon with the smallest load
   forall(w in WAGONS) v:=if(CurWeight(v)N)  
 
  repeat                                ! Loop over the partial sorts
   inc:=inc div 3
   forall(i in inc+1..N) do             ! Outer loop of straight insertion
    v:=I(i)
    j:=i
    while (A(I(j-inc))

d1wagon2.mos
(!******************************************************
   Mosel Example Problems
   ======================

   file d1wagon2.mos
   `````````````````
   Load balancing of train wagons
   (second version, using heuristic solution as 
    start solution for MIP via 'addmipsol')
   
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Sep. 2007, rev. Feb. 2013
*******************************************************!)

model "D-1 Wagon load balancing (2)"
 uses "mmxprs", "mmsystem"

 forward function solve_heur:real
 
 declarations   
  BOXES = 1..16                      ! Set of boxes
  WAGONS = 1..3                      ! Set of wagons

  WEIGHT: array(BOXES) of integer    ! Box weights
  WMAX: integer                      ! Weight limit per wagon

  load: array(BOXES,WAGONS) of mpvar ! 1 if box loaded on wagon, 0 otherwise
  maxweight: mpvar                   ! Weight of the heaviest wagon load
  MaxWeight: linctr                  ! Objective function

  HeurSol: array(BOXES) of integer   ! Heuristic solution
  AllSol: array(mpvar) of real       ! Start solution for MIP
 end-declarations

 initializations from 'd1wagon.dat'
  WEIGHT WMAX
 end-initializations

! Solve the problem heuristically and terminate the program if the
! heuristic solution is good enough, otherwise, load heuristic solution
! as start solution into the Optimizer
 if solve_heur<=WMAX then
  writeln("Heuristic solution fits capacity limits")
  exit(0)
 end-if

! Every box into one wagon
 forall(b in BOXES) sum(w in WAGONS) load(b,w) = 1
 
! Limit the weight loaded into every wagon 
 forall(w in WAGONS) sum(b in BOXES) WEIGHT(b)*load(b,w) <= maxweight
 
! Lower bound on maximum weight
 maxweight >= ceil((sum(b in BOXES) WEIGHT(b))/3)

 forall(b in BOXES,w in WAGONS) load(b,w) is_binary

! Alternative to lower bound on maxweight: adapt the optimizer cutoff value 
! setparam("XPRS_MIPADDCUTOFF",-0.99999)

! Uncomment the following line to see the optimizer log
! setparam("XPRS_VERBOSE",true) 

! Set the solution values for all discrete variables that are non-zero
 forall(b in BOXES) AllSol(load(b,HeurSol(b))):= 1

! Minimize the heaviest load
 MaxWeight:= maxweight                  ! Objective must be a constraint
                                        ! otherwise 'minimize' reloads problem
 loadprob(MaxWeight)                    ! Load problem into the Optimizer
 addmipsol("HeurSol", AllSol)           ! Load the heuristic solution

 setcallback(XPRS_CB_SOLNOTIFY,"solnotify")  ! Reporting use of user solution

! Re-inforce use of user solution in local search heuristics 
 setparam("XPRS_USERSOLHEURISTIC",3)

 minimize(MaxWeight)                    ! Start optimization
 
! Solution printing
 writeln("Optimal solution:\n Max weight: ", getobjval)
 forall(w in WAGONS) do
  write(" ", w, ":")
  forall(b in BOXES) write(if(getsol(load(b,w))=1, " "+b, ""))
  writeln(" (total weight: ", getsol(sum(b in BOXES) WEIGHT(b)*load(b,w)), ")")
 end-do

!-----------------------------------------------------------------

(! LPT (Longest processing time) heuristic: 
   One at a time place the heaviest unassigned box onto the wagon with 
   the least load
!)
   
 function solve_heur:real
  declarations
   ORDERW: array(BOXES) of integer      ! Box indices in decreasing weight order
   Load: array(WAGONS,range) of integer ! Boxes loaded onto the wagons 
   CurWeight: array(WAGONS) of integer  ! Current weight of wagon loads 
   CurNum: array(WAGONS) of integer     ! Current number of boxes per wagon
  end-declarations

 ! Copy the box indices into array ORDERW and sort them in decreasing 
 ! order of box weights (the sorted indices are returned in array ORDERW) 
  forall(b in BOXES) ORDERW(b):=b
  qsort(SYS_DOWN, WEIGHT,ORDERW)

 ! Distribute the loads to the wagons using the LPT heuristic 
  forall(b in BOXES) do
   v:=1                                 ! Find wagon with the smallest load
   forall(w in WAGONS) v:=if(CurWeight(v)
d2ship.mos
(!******************************************************
   Mosel Example Problems
   ======================

   file d2ship.mos
   ```````````````
   Choice of wheat load for a ship
   
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Mar. 2002
*******************************************************!)

model "D-2 Ship loading"
 uses "mmxprs"

 forward procedure print_sol(num:integer)

 declarations   
  CLIENTS = 1..7                    ! Set of clients

  AVAIL: array(CLIENTS) of integer  ! Number of lots per client
  SIZE: array(CLIENTS) of integer   ! Lot sizes
  PRICE: array(CLIENTS) of integer  ! Prices charged to clients
  COST: array(CLIENTS) of integer   ! Cost per client
  PROF: array(CLIENTS) of integer   ! Profit per client
  CAP: integer                      ! Capacity of the ship
 
  load: array(CLIENTS) of mpvar     ! Lots taken from clients
 end-declarations

 initializations from 'd2ship.dat'
  AVAIL SIZE PRICE COST CAP
 end-initializations

 forall(c in CLIENTS) PROF(c):= PRICE(c) - COST(c)*SIZE(c)

 Profit:= sum(c in CLIENTS) PROF(c)*load(c)

! Limit on the capacity of the ship
 sum(c in CLIENTS) SIZE(c)*load(c) <= CAP

! Problem 1: unlimited availability of lots at clients
 maximize(Profit)
 print_sol(1)

! Problem 2: limits on availability of lots at clients
 forall(c in CLIENTS) load(c) <= AVAIL(c)

 maximize(Profit)
 print_sol(2)

! Problem 3: lots must be integer
 forall(c in CLIENTS) load(c) is_integer

 maximize(Profit)
 print_sol(3)
 
!-----------------------------------------------------------------

! Solution printing
 procedure print_sol(num:integer)
  writeln("Problem ", num, ": profit: ", getobjval)
  forall(c in CLIENTS) 
   write( if(getsol(load(c))>0 , " " + c + ":" + getsol(load(c)), ""))
  writeln
 end-procedure
 
end-model

d3tanks.mos
(!******************************************************
   Mosel Example Problems
   ======================

   file d3tanks.mos
   ````````````````
   Loading of liquid chemicals into tanks
   
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Mar. 2002
*******************************************************!)

model "D-3 Tank loading"
 uses "mmxprs"

 forward procedure print_sol

 declarations   
  TANKS: range                       ! Set of tanks
  LIQ: set of string                 ! Set of liquids

  CAP: array(TANKS) of integer       ! Tank capacities
  TINIT: array(TANKS) of string      ! Initial tank contents type
  QINIT: array(TANKS) of integer     ! Quantity of initial contents
  ARR: array(LIQ) of integer         ! Arriving quantities of chemicals
  REST: array(LIQ) of integer        ! Rest after filling part. filled tanks
 
  load: dynamic array(LIQ,TANKS) of mpvar  ! 1 if liquid loaded into tank, 
                                     ! 0 otherwise
 end-declarations

 initializations from 'd3tanks.dat'
  CAP ARR
  [TINIT, QINIT] as 'FILLED'
 end-initializations

 finalize(LIQ)

 forall(t in TANKS | QINIT(t)=0, l in LIQ) do
  create(load(l,t))
  load(l,t) is_binary
 end-do 

! Complete the initially partially filled tanks and calculate the remaining
! quantities of liquids
 forall(l in LIQ) 
  REST(l):= ARR(l) - sum(t in TANKS | TINIT(t)=l) (CAP(t)-QINIT(t))
 
! Objective 1: total tank capacity used
 TankUse:= sum(l in LIQ, t in TANKS) CAP(t)*load(l,t)

! Objective 2: number of tanks used
 TankNum:= sum(l in LIQ, t in TANKS) load(l,t)

! Do not mix different liquids
 forall(t in TANKS) sum(l in LIQ) load(l,t) <= 1

! Load the empty tanks within their capacity limits
 forall(l in LIQ) sum(t in TANKS) CAP(t)*load(l,t) >= REST(l)

! Solve the problem with objective 1
 minimize(TankUse)
 
! Solution printing
 print_sol

! Solve the problem with objective 2
 minimize(TankNum)
 
! Solution printing
 print_sol
  
!-----------------------------------------------------------------

! Solution printing
 procedure print_sol
  writeln("Used capacity: ", getsol(TankUse) + 
                             sum(t in TANKS | QINIT(t)>0) CAP(t),
         " Capacity of empty tanks: ", sum(t in TANKS) CAP(t) - 
                                       getsol(TankUse) - 
                                       sum(t in TANKS | QINIT(t)>0) CAP(t))
  writeln("Number of tanks used: ", getsol(TankNum) + 
                                    sum(t in TANKS | QINIT(t)>0) 1) 
  forall(t in TANKS)
   if(QINIT(t)=0) then
    write(t, ": ")
    forall(l in LIQ) write( if(getsol(load(l,t))>0 , l, ""))
    writeln
   else
    writeln(t, ": ", TINIT(t))
   end-if
 end-procedure
    
end-model

d4backup.mos
(!******************************************************
   Mosel Example Problems
   ======================

   file d4backup.mos
   `````````````````
   Bin packing: backup of files onto floppy disks
   
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Mar. 2002
*******************************************************!)

model "D-4 Bin packing"
 uses "mmxprs"

 declarations
  ND: integer                        ! Number of floppy disks
  FILES = 1..16                      ! Set of files
  DISKS: range                       ! Set of disks

  CAP:  integer                      ! Floppy disk size
  SIZE: array(FILES) of integer      ! Size of files to be saved
 end-declarations
 
 initializations from 'd4backup.dat'
  CAP SIZE
 end-initializations

! Provide a sufficiently large number of disks
 ND:= ceil((sum(f in FILES) SIZE(f))/CAP)
 DISKS:= 1..ND

 declarations  
  save: array(FILES,DISKS) of mpvar  ! 1 if file saved on disk, 0 otherwise
  diskuse: mpvar                     ! Number of disks used
 end-declarations

! Limit the number of disks used
 forall(f in FILES) diskuse >= sum(d in DISKS) d*save(f,d)

! Every file onto a single disk
 forall(f in FILES) sum(d in DISKS) save(f,d) = 1

! Capacity limit of disks
 forall(d in DISKS) sum(f in FILES) SIZE(f)*save(f,d) <= CAP

 forall(d in DISKS,f in FILES) save(f,d) is_binary

! Minimize the total number of disks used
 minimize(diskuse)
 
! Solution printing
  writeln("Number of disks used: ", getobjval)
  forall(d in 1..integer(getobjval)) do
   write(d, ":")
   forall(f in FILES) write( if(getsol(save(f,d))>0 , " "+SIZE(f), ""))
   writeln("  space used: ", getsol(sum(f in FILES) SIZE(f)*save(f,d)))
  end-do
      
end-model

d5cutsh.mos
(!******************************************************
   Mosel Example Problems
   ======================

   file d5cutsh.mos
   ````````````````
   Cutting of sheet metal 
   (2-dimensional cutting patterns)
   
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Mar. 2002
*******************************************************!)

model "D-5 Sheet metal cutting"
 uses "mmxprs"

 declarations
  PATTERNS = 1..16                       ! Set of cutting patterns
  SIZES = 1..4                           ! Set of sheet sizes

  DEM: array(SIZES) of integer           ! Demands for the different sizes
  CUT: array(SIZES,PATTERNS) of integer  ! Cutting patterns
  
  use: array(PATTERNS) of mpvar          ! Use of cutting patterns
 end-declarations
 
 initializations from 'd5cutsh.dat'
  DEM CUT
 end-initializations

! Objective: total number of sheets used
 Sheets:= sum(p in PATTERNS) use(p)

! Satisfy demands
 forall(s in SIZES) sum(p in PATTERNS) CUT(s,p)*use(p) >= DEM(s)

 forall(p in PATTERNS) use(p) is_integer

! Solve the problem
 minimize(Sheets)
 
! Solution printing
  writeln("Total number of large sheets: ", getobjval)
  write("Cutting patterns used: ")
  forall(p in PATTERNS)
   write( if(getsol(use(p)) > 0 , " " + p + ":" + getsol(use(p)), "") )
  write("\nExemplaries of sheets sizes cut: ")
  forall(s in SIZES)
   write(s, ":", getsol(sum(p in PATTERNS) CUT(s,p)*use(p)), " ")
  writeln
      
end-model

d6cutbar.mos
(!******************************************************
   Mosel Example Problems
   ======================

   file d6cutbar.mos
   `````````````````
   Cutting of steel bars into school desk legs 
   (1-dimensional cutting patterns)
   
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, Mar. 2002
*******************************************************!)

model "D-6 Cutting steel bars"
 uses "mmxprs"

 declarations
  PAT1 = 1..6; PAT2 = 7..12              ! Sets of cutting patterns
  PATTERNS = PAT1 + PAT2                 ! Set of all cutting patterns
  SIZES: set of integer                  ! Desk heights

  DEM: array(SIZES) of integer           ! Demands for the different heights
  CUT: array(PATTERNS,SIZES) of integer  ! Cutting patterns
  LEN: array(range) of integer           ! Lengths of original steel bars
  
  use: array(PATTERNS) of mpvar          ! Use of cutting patterns
 end-declarations
 
 initializations from 'd6cutbar.dat'
  DEM CUT LEN
 end-initializations

! Objective: total loss
 Loss:= sum(p in PAT1) LEN(1)*use(p) + sum(p in PAT2) LEN(2)*use(p) - 
         sum(s in SIZES) 4*DEM(s)*s

! Satisfy demands
 forall(s in SIZES) sum(p in PATTERNS) CUT(p,s)*use(p) >= 4*DEM(s)

 forall(p in PATTERNS) use(p) is_integer

! Solve the problem
 minimize(Loss)
 
! Solution printing
  writeln("Loss: ", getobjval, "cm")
  write("Short bars: ", getsol(sum(p in PAT1) use(p)))
  writeln(", long bars: ", getsol(sum(p in PAT2) use(p)))
  write("Cutting patterns used:")
  forall(p in PATTERNS)
   write( if(getsol(use(p)) > 0 , " " + p + ":" + getsol(use(p)), "") )
  write("\nNumbers of legs cut: ")
  forall(s in SIZES)
   write(s, ":", getsol(sum(p in PATTERNS) CUT(p,s)*use(p)), " ")
  writeln
      
end-model