Initializing help system before first use

Project scheduling problem with disjunctive resource constraints


Type: Project scheduling
Rating: 3 (intermediate)
Description: Project scheduling problem with disjunctive resource constraints and minimum and maximum delays between tasks, formulated
  • with 'disjunctive' constraints (bridgescheduling.mos), or
  • using task and resources objects (bridgescheduling_alt.mos)
File(s): bridgescheduling.mos, bridgesched_graph.mos, bridgescheduling_alt.mos
Data file(s): bridgescheduling.dat, bridgescheduling_alt.dat


bridgescheduling.mos
(!****************************************************************

   CP example problems
   ===================
   
   file bridgescheduling.mos
   `````````````````````````
   Project scheduling problem with disjunctive resource constraints.         
 
   We wish to schedule a set of tasks for the construction of a     
   bridge. Each task uses a particular resource (excavator,        
   pileDriver, ...) and can only start after the completion of its 
   predecessor(s). A resource can only be used by one task at a    
   time (disjunctive resources).                                   
   The aim is to minimize the total duration of the schedule (PE).    

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

   Copyright(C) 2008 Artelys S.A. and Fair Isaac Corporation
                     Creation: 2005, rev. Mar. 2013
*****************************************************************!)

model "Bridge Scheduling (CP)"
 uses "kalis"
 
 declarations       
  RESOURCES: set of string            ! Set of resources
  TASKS: set of string                ! Set of tasks   
  DUR  : array(TASKS) of integer      ! Durations of tasks      
  USE  : array(TASKS) of string       ! Resource used by a task
  NPREC: array(TASKS) of integer      ! Number of predecessors of a task
  PRECS: array(TASKS,range) of string ! Predecessors of each task

  SetEE, SetES, SetSS, SetSE: range   ! Index sets of distance data
  DISTEE: array(SetEE,1..2) of string ! End-to-end max. distance
  DISTES: array(SetES,1..2) of string ! End-to-start max. distance
  DISTSS: array(SetSS,1..2) of string ! Start-to-start min. distance
  DISTSE: array(SetSE,1..2) of string ! Start-to-end min. distance
 end-declarations

 initializations from "Data/bridgescheduling.dat"
  [DUR, NPREC, USE] as "TASKDATA"
  PRECS
  DISTEE DISTES DISTSS DISTSE
 end-initializations

 finalize(TASKS)
 forall(t in TASKS | USE(t)<>"") RESOURCES += {USE(t)}

 setparam("KALIS_DEFAULT_LB", 0)
 setparam("KALIS_DEFAULT_UB", sum(t in TASKS) DUR(t))
  
 declarations       
  start: array(TASKS) of cpvar              ! Start times of tasks 
  Disj,Disj_crane,Disj_brick: set of cpctr  ! Disjunction constraints   
  Strategy: array(range) of cpbranching     ! Branching strategy
 end-declarations

!******************************************************************
! **** Create disjunctions between tasks using the same resource ****
 procedure post_disjunctive_constraints(r:string)
  declarations
   usedby: set of cpvar
   durby : array(cpvar) of integer
  end-declarations

  nbr := 0     
  forall (t in TASKS | USE(t) = r) do
   usedby += {start(t)}
   durby(start(t)) := DUR(t)
   nbr += 1
  end-do  
  if (r="crane") then
   disjunctive(usedby,durby,Disj_crane,0)
  elif (r="bricklaying") then 
   disjunctive(usedby,durby,Disj_brick,0)    
  else   
   disjunctive(usedby,durby,Disj,0)
  end-if      
 end-procedure


! **** Distance constraints between starts or ends of certain task pairs ****
 procedure post_distance_constraints
  forall(i in SetEE)                    ! End-to-end max. distance
   start(DISTEE(i,1)) + DUR(DISTEE(i,1)) + 4 >= start(DISTEE(i,2)) +
                                                DUR(DISTEE(i,2))
  forall(i in SetES)                    ! End-to-start max. distance
   start(DISTES(i,1)) + DUR(DISTES(i,1)) + 3 >= start(DISTES(i,2)) 
  forall(i in SetSS)                    ! Start-to-start min. distance
   start(DISTSS(i,1)) + 6 <= start(DISTSS(i,2))
  forall(i in SetSE)                    ! Start-to-end min. distance
   start(DISTSE(i,1)) + DUR(DISTSE(i,1)) - 2 <= start(DISTSE(i,2))
 end-procedure 

!******************************************************************
! **** Model definition *****
 setparam("kalis_auto_propagate", false) ! Do not post constraints at definition

 forall(t in TASKS) setname(start(t),t) ! Set variable names        

! Precedences between a task and its predecessors 
 forall(t in TASKS, i in 1..NPREC(t))
  start(PRECS(t,i)) + DUR(PRECS(t,i)) <= start(t)

! Disjunctions between tasks using the same resource
 forall(r in RESOURCES) post_disjunctive_constraints(r)
  
! Define min/max distance between certain starts and ends of task pairs
 post_distance_constraints

! Post all constraints
 if (not cp_propagate) then
  writeln("Problem is infeasible")
  exit(1)
 end-if 

! Define the branching strategy: branch first on bottleneck resources  
 Strategy(1) := settle_disjunction(Disj_crane)
 Strategy(2) := settle_disjunction(Disj_brick)
 Strategy(3) := split_domain(KALIS_LARGEST_MAX, KALIS_MIN_TO_MAX) 
 cp_set_branching(Strategy) 
 
! Uncomment to limit computation time 
 setparam("KALIS_MAX_COMPUTATION_TIME", 10)

 setparam("KALIS_VERBOSE_LEVEL", 1)

 if not(cp_minimize(start("PE"))) then
  writeln("Problem is infeasible")
  exit(1)
 end-if 
 
 ct:= 0 
 forall(t in TASKS) do
  write(t, "=", getsol(start(t)), if(ct mod 10=9, ",\n", ", "))
  ct+=1
 end-do
 writeln("\nMakespan: ", getsol(start("PE")))
 
 writeln("Proof of optimality in:")
 cp_show_stats
   
end-model 


  

bridgesched_graph.mos
(!****************************************************************

   CP example problems
   ===================
   
   file bridgescheduling.mos
   `````````````````````````
   Project scheduling problem with disjunctive resource constraints. 
   - Graphical solution representation -        
 
   We wish to schedule a set of tasks for the construction of a     
   bridge. Each task uses a particular resource (excavator,        
   pileDriver, ...) and can only start after the completion of its 
   predecessor(s). A resource can only be used by one task at a    
   time (disjunctive resources).                                   
   The aim is to minimize the total duration of the schedule (PE).    

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

   Copyright(C) 2008 Artelys S.A. and Fair Isaac Corporation
                     Creation: 2005, rev. Sep. 2017
*****************************************************************!)

model "Bridge Scheduling (CP)"
 uses "kalis", "mmsvg"
 
 forward procedure show_solution

 declarations       
  RESOURCES: set of string            ! Set of resources
  TASKS: set of string                ! Set of tasks   
  DUR  : array(TASKS) of integer      ! Durations of tasks      
  USE  : array(TASKS) of string       ! Resource used by a task
  NPREC: array(TASKS) of integer      ! Number of predecessors of a task
  PRECS: array(TASKS,range) of string ! Predecessors of each task

  SetEE, SetES, SetSS, SetSE: range   ! Index sets of distance data
  DISTEE: array(SetEE,1..2) of string ! End-to-end max. distance
  DISTES: array(SetES,1..2) of string ! End-to-start max. distance
  DISTSS: array(SetSS,1..2) of string ! Start-to-start min. distance
  DISTSE: array(SetSE,1..2) of string ! Start-to-end min. distance
 end-declarations

 initializations from "Data/bridgescheduling.dat"
  [DUR, NPREC, USE] as "TASKDATA"
  PRECS
  DISTEE DISTES DISTSS DISTSE
 end-initializations

 finalize(TASKS)
 forall(t in TASKS | USE(t)<>"") RESOURCES += {USE(t)}

 setparam("KALIS_DEFAULT_LB", 0)
 setparam("KALIS_DEFAULT_UB", sum(t in TASKS) DUR(t))
  
 declarations       
  start: array(TASKS) of cpvar              ! Start times of tasks 
  Disj,Disj_crane,Disj_brick: set of cpctr  ! Disjunction constraints   
  Strategy: array(range) of cpbranching     ! Branching strategy
 end-declarations

!******************************************************************
! **** Create disjunctions between tasks using the same resource ****
 procedure post_disjunctive_constraints(r:string)
  declarations
   usedby: set of cpvar
   durby : array(cpvar) of integer
  end-declarations

  nbr := 0     
  forall (t in TASKS | USE(t) = r) do
   usedby += {start(t)}
   durby(start(t)) := DUR(t)
   nbr += 1
  end-do  
  if (r="crane") then
   disjunctive(usedby,durby,Disj_crane,0)
  elif (r="bricklaying") then 
   disjunctive(usedby,durby,Disj_brick,0)    
  else   
   disjunctive(usedby,durby,Disj,0)
  end-if      
 end-procedure


! **** Distance constraints between starts or ends of certain task pairs ****
 procedure post_distance_constraints
  forall(i in SetEE)                    ! End-to-end max. distance
   start(DISTEE(i,1)) + DUR(DISTEE(i,1)) + 4 >= start(DISTEE(i,2)) +
                                                DUR(DISTEE(i,2))
  forall(i in SetES)                    ! End-to-start max. distance
   start(DISTES(i,1)) + DUR(DISTES(i,1)) + 3 >= start(DISTES(i,2)) 
  forall(i in SetSS)                    ! Start-to-start min. distance
   start(DISTSS(i,1)) + 6 <= start(DISTSS(i,2))
  forall(i in SetSE)                    ! Start-to-end min. distance
   start(DISTSE(i,1)) + DUR(DISTSE(i,1)) - 2 <= start(DISTSE(i,2))
 end-procedure 

!******************************************************************
! **** Model definition *****
 setparam("kalis_auto_propagate", false)  ! Do not post constraints at definition

 forall(t in TASKS) setname(start(t),t) ! Set variable names        

! Precedences between a task and its predecessors 
 forall(t in TASKS, i in 1..NPREC(t))
  start(PRECS(t,i)) + DUR(PRECS(t,i)) <= start(t)

! Disjunctions between tasks using the same resource
 forall(r in RESOURCES) post_disjunctive_constraints(r)
  
! Define min/max distance between certain starts and ends of task pairs
 post_distance_constraints

! Post all constraints
 if (not cp_propagate) then
  writeln("Problem is infeasible")
  exit(1)
 end-if 

! Define the branching strategy: branch first on bottleneck resources  
 Strategy(1) := settle_disjunction(Disj_crane)
 Strategy(2) := settle_disjunction(Disj_brick)
 Strategy(3) := split_domain(KALIS_LARGEST_MAX, KALIS_MIN_TO_MAX) 
 cp_set_branching(Strategy) 

 setparam("KALIS_MAX_COMPUTATION_TIME", 5)

 if not(cp_minimize(start("PE"))) then
  writeln("Problem is infeasible")
  exit(1)
 elif getparam("KALIS_SEARCH_LIMIT")=KALIS_SLIM_BY_TIME then
  writeln("Search time limit reached")
 else 
  writeln("Optimal solution found")
 end-if
 
 ct:= 0 
 forall(t in TASKS) do
  write(t, "=", getsol(start(t)), if(ct mod 10=9, ",\n", ", "))
  ct+=1
 end-do
 writeln("\nMakespan: ", getsol(start("PE")))
 
 cp_show_stats
  
 show_solution

!******************************************************************
! **** Graphical representation of solutions ****
 procedure show_solution    
  ind:= 1
  forall(r in RESOURCES) do
   svgaddgroup(r, r)
   svgsetstyle(SVG_FILL,SVG_CURRENT)
   svgsetstyle(SVG_STROKE,SVG_GRAY)
   forall(ind as counter, t in TASKS | USE(t) = r) do      
    svgaddtext((2*getsol(start(t))+DUR(t))/2 -0.5 , ind+1.5, t) 
    svgaddrectangle(getsol(start(t)), ind, if(DUR(t)=0,0.1,DUR(t)), 0.9)
   end-do  
  end-do

  svgsetgraphscale(5)
  svgsetgraphviewbox(0,0,getsol(start("PE"))+10,ind+5)
  svgsetgraphlabels("Time", "Resources")

  svgsave("bridge.svg")
  svgrefresh
  svgwaitclose("Close browser window to terminate model execution.", 1)
 end-procedure
 
end-model 


  

bridgescheduling_alt.mos
(!****************************************************************

   CP example problems
   ===================
   
   file bridgescheduling_alt.mos
   `````````````````````````````
   Project scheduling problem with disjunctive resource constraints. 
   - Alternative formulation using resource and task objects -        
 
   We wish to schedule a set of tasks for the construction of a     
   bridge. Each task uses a particular resource (excavator,        
   pileDriver, ...) and can only start after the completion of its 
   predecessor(s). A resource can only be used by one task at a    
   time (disjunctive resources).                                   
   The aim is to minimize the total duration of the schedule (PE).    

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

   Copyright(C) 2008 Artelys S.A. and Fair Isaac Corporation
                     Creation: 2005
*****************************************************************!)

model "Bridge Construction Schedule"  
 uses "kalis"

 declarations       
  RESOURCES: set of string             ! Set of resources
  TASKS: set of string                 ! Set of tasks   
  DUR  : array(TASKS) of integer       ! Durations of tasks      
  USE  : array(TASKS) of string        ! Resource used by a task
  PRECS: dynamic array(TASKS) of set of string ! Predecessors of each task

  SetEE, SetES, SetSS, SetSE: range    ! Index sets of distance data
  DISTEE: array(SetEE,1..2) of string  ! End-to-end max. distance
  DISTES: array(SetES,1..2) of string  ! End-to-start max. distance
  DISTSS: array(SetSS,1..2) of string  ! Start-to-start min. distance
  DISTSE: array(SetSE,1..2) of string  ! Start-to-end min. distance
 end-declarations

 initializations from "Data/bridgescheduling_alt.dat"
  [DUR, USE] as "TASKDATA"
  PRECS
  DISTEE DISTES DISTSS DISTSE
 end-initializations

 finalize(TASKS)
 forall(t in TASKS | USE(t)<>"") RESOURCES += {USE(t)}
 finalize(RESOURCES)
 
 declarations  
  res: array(RESOURCES) of cpresource ! Declaration of resources
  task: array(TASKS) of cptask        ! Declaration of tasks
 end-declarations

! Setting up disjunctive resources (only one task at a time can be processed)
 forall(r in RESOURCES)
  set_resource_attributes(res(r), KALIS_UNARY_RESOURCE, 1)

! Setting the task attributes (duration, resource use and predecessors)
 forall(t in TASKS) do
  setduration(task(t), DUR(t))
  if USE(t)<>"" then
   requires(task(t), 1, res(USE(t)))
  end-if
  if exists(PRECS(t)) then
   setpredecessors(task(t), union(j in PRECS(t)) {task(j)})
  end-if
 end-do

! Additional distance constraints between tasks
  forall(i in SetEE)                    ! End-to-end max. distance
   getend(task(DISTEE(i,1))) + 4 >= getend(task(DISTEE(i,2)))
  forall(i in SetES)                    ! End-to-start max. distance
   getend(task(DISTES(i,1))) + 3 >= getstart(task(DISTES(i,2))) 
  forall(i in SetSS)                    ! Start-to-start min. distance
   getstart(task(DISTSS(i,1))) + 6 <= getstart(task(DISTSS(i,2)))
  forall(i in SetSE)                    ! Start-to-end min. distance
   getend(task(DISTSE(i,1))) - 2 <= getstart(task(DISTSE(i,2)))

! Find the optimal schedule (minimizing the makespan)
 if cp_schedule(getmakespan) = 0 then
  writeln("Inconsistent schedule")
 else
! Print the solution
  ct:= 0 
  forall(t in TASKS) do
   write(t, "=", getsol(getstart(task(t))), if(ct mod 10=9, ",\n", ", "))
   ct+=1
  end-do
  writeln("\nMakespan: ", getsol(getmakespan))
  !cp_show_sol 
  cp_show_stats
 end-if

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.