update_duration_with_idle_times
Warning: when adding several idle time windows for a resource and a task, they must be added in increasing time order and all time windows must be disjoint. If not done this way, durations might be inconsistent. Also, propagation should not be called between successive calls.
When developing the model, it is recommended to check the start-based duration constraint by printing the constraint: cp_show_var_constraints(getduration(task))
procedure update_duration_with_idle_times(resource: cpresource, task:cptask, t1:integer, t2:integer, allow_start_in_idle:boolean)
|
resource
|
a resource
|
|
task
|
a task
|
|
t1
|
The idle time window start
|
|
t2
|
The idle time window end
|
|
allow_start_in_idle
|
If true, the
task will be able to start in the idle time window.
|
model "Scheduling with alternative resources"
uses "kalis", "mmsystem"
setparam("KALIS_DEFAULT_LB", 0)
setparam("KALIS_VERBOSE_LEVEL", 1)
declarations
JOBS: set of string ! Index set of jobs
TEAMS: set of string ! Index set of teams (resources)
DURATIONS: array(JOBS, TEAMS) of integer ! Durations of JOBS for each team
POSSIBLE_TEAMS: array(JOBS) of set of string ! Possible team for each task
PRECEDENCES: list of list of string ! Pairs of precedence constraints
SOFT_BREAKS: array(TEAMS) of list of list of integer ! Start and end times
! of soft breaks for each team
ALLOW_START_IN_IDLE: boolean
job: array(JOBS) of cptask ! Jobs
team: array(TEAMS) of cpresource ! Teams
ASSIGN_VARS_INDEXES: set of integer ! Mapping model entities to indices
ASSIGNMENT_VARS_JOB: array(ASSIGN_VARS_INDEXES) of string
ASSIGNMENT_VARS_TEAM: array(ASSIGN_VARS_INDEXES) of string
procedure print_and_check_solution
function get_actual_duration(j: string, t: string, start: integer): integer
end-declarations
! **************** Data ****************
! -1 duration indicates the team cannot process the task
DURATIONS::(["J0", "J1", "J2", "J3", "J4", "J5", "J6", "J7", "J8", "J9",
"J10", "J11", "J12", "J13", "J14", "J15", "J16", "J17"],
["T1", "T2", "T3"])[
2, -1, 2,
16, 14, 15,
9, -1, 8,
8, 5, 8,
10, 11, 8,
6, 5, 7,
-1, 3, 4,
2, 1, 2,
9, 6, 9,
5, 7, -1,
3, -1, 1,
2, 3, 3,
1, -1, 1,
7, 4, 7,
4, 6, 4,
3, 2, 1,
9, 9, -1,
1, 2, 3]
forall(j in JOBS)
POSSIBLE_TEAMS(j) := union(t in TEAMS | DURATIONS(j, t) <> -1) {t}
PRECEDENCES:= [ ["J1", "J0"], ["J2", "J1"], ["J3", "J1"], ["J13", "J1"],
["J4", "J2"], ["J5", "J3"], ["J6", "J3"], ["J8", "J3"],
["J9", "J3"], ["J14", "J3"], ["J5", "J4"], ["J7", "J5"],
["J8", "J5"], ["J10", "J5"], ["J12", "J6"], ["J15", "J7"],
["J11", "J8"], ["J15", "J10"], ["J16", "J11"],
["J14", "J13"], ["J15", "J13"], ["J17", "J16"]]
SOFT_BREAKS::(["T1", "T2", "T3"]) [[[10, 25], [42, 52]], [[15, 37]],
[[25, 35], [45, 58]]]
ALLOW_START_IN_IDLE := true
! **************** Problem formulation ****************
! Define discrete resources
forall(t in TEAMS) do
set_resource_attributes(team(t), KALIS_DISCRETE_RESOURCE, 1)
team(t).name := t
end-do
! Define possible teams for each task
forall(j in JOBS) do
job(j).name := j
requires(job(j), union(t in POSSIBLE_TEAMS(j)) {resusage(team(t), 1)})
end-do
! Define associated duration for each task
forall(j in JOBS) do
! Initialize duration bounds
setdomain(getduration(job(j)),
min(t in POSSIBLE_TEAMS(j)) DURATIONS(j, t),
max(t in POSSIBLE_TEAMS(j)) DURATIONS(j, t))
forall(t in POSSIBLE_TEAMS(j)) do
! Set nominal duration
setduration(team(t), job(j), DURATIONS(j, t))
! Update the actual duration with idle times
forall(b in SOFT_BREAKS(t))
update_duration_with_idle_times(team(t), job(j), b.first, b.last,
ALLOW_START_IN_IDLE)
end-do
end-do
! Display all constraints involving the duration of task 'J0'
cp_show_var_constraints(getduration(job("J0")))
! Define precedences
forall(j in JOBS)
setpredecessors(job(j), union(p in PRECEDENCES | p(1) = j) {job(p.last)})
cp_close_schedule
! **************** Solving ****************
! Perform constraint propagation
if not cp_propagate then
writeln("Problem is infeasible")
exit(1)
end-if
setparam("KALIS_MAX_COMPUTATION_TIME", 10)
! Solve the problem
if cp_schedule(getmakespan)=0 then
writeln("No solution")
exit(0)
end-if
! Solution printing
print_and_check_solution
! **************** Subroutine definitions ****************
procedure print_and_check_solution
declarations
starts: array(JOBS) of integer
ends: array(JOBS) of integer
operating_teams: array(JOBS) of string
end-declarations
! Display the solution
writeln("makespan=", getsol(getmakespan))
forall(j in JOBS) do
starts(j) := getsol(getstart(job(j)))
ends(j) := getsol(getend(job(j)))
forall(t in POSSIBLE_TEAMS(j) | getsol(getassignment(job(j),team(t)))>0) do
operating_teams(j) := t
break
end-do
writeln(formattext("%3s: %2d - %2d team=%s", j, starts(j), ends(j),
operating_teams(j)))
end-do
! Check solution
forall(j in JOBS | operating_teams(j) not in TEAMS)
writeln("Error: ", j, " doesn't have an operating team.")
forall(p in PRECEDENCES | starts(getfirst(p)) < ends(getlast(p)))
writeln("Error: Precedence constraint ", p, " is violated.")
forall(j in JOBS | starts(j) + get_actual_duration(j, operating_teams(j),
starts(j)) <> ends(j))
writeln("Error: Job ", j, " has a wrong duration.")
if max(j in JOBS) ends(j) <> getsol(getmakespan) then
writeln("Error: Objective value is different from computed makespan value.")
end-if
end-procedure
! **** Return the actual duration of a job given its team and start
function get_actual_duration(j: string, t: string, start: integer): integer
expected_end := start + DURATIONS(j, t)
forall(b in SOFT_BREAKS(t)) do
if start >= b.last then
next
end-if
! Adding soft break duration
if expected_end > b.first then
expected_end += b.last - maxlist(start, b.first)
end-if
end-do
returned := expected_end - start
end-function
end-model
© 2001-2023 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.
