1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.

256 lines
8.5 KiB
MiniZinc

%-----------------------------------------------------------------------------%
% Resource Availability Cost Problem (also known as Resource Investment Problem)
%
% The problem consists of a set of tasks having some precedence relations
% between them and requiring some resource from scarce renewable resource
% having a different availability cost per unit. The goal is to complete all
% tasks in the given planning horizon, so that the resource availability
% cost is minimised. Note that cost per unit does not depend on its
% utilisation!
%
% This simple model was derived from the model presented in this publication:
%
% Stefan Kreter, Andreas Schutt, Peter J. Stuckey, Jürgen Zimmermann (2018):
% Mixed-integer Linear Programming and Constraint Programming Formulations
% for Solving Resource Availability Cost Problems. European Journal of
% Operational Research, Volume 266, Issue 2, pages 472-486. 2018.
% https://doi.org/10.1016/j.ejor.2017.10.014
%
% Submitter: Andreas Schutt
%
%-----------------------------------------------------------------------------%
% MiniZinc Version Requirement
% Note that at least version 2.1.7 is required due to a bug in the
% evaluation of recursive functions in previous versions.
mzn_min_version_required = 21007;
%-----------------------------------------------------------------------------%
% Includes
include "cumulative.mzn";
%-----------------------------------------------------------------------------%
% Parameters
% Resources
%
int: n_res; % The number of resources
set of int: Res = 1..n_res; % The set of resources
array [Res] of int: cost; % The unit cost of resources
array [Res] of int: lb_usage = % Trivial lower bound on the resource demand
[ max(i in Tasks)(rr[r, i]) | r in Res ];
array [Res] of int: ub_usage = % Trivial upper bound on the resource demand
[ sum(i in Tasks)(rr[r, i]) | r in Res ];
array [Res] of int: lb_cost = % Trivial lower bound on the resource cost
[ cost[r] * lb_usage[r] | r in Res ];
array [Res] of int: ub_cost = % Trivial upper bound on the resource cost
[ cost[r] * ub_usage[r] | r in Res ];
% Tasks
%
int: n_tasks; % The number of tasks
set of int: Tasks = 1..n_tasks; % The set of tasks
array [Tasks] of int: dur; % The duration of tasks
array [Res, Tasks] of int: rr; % The resource requirement of tasks
array [Tasks] of set of Tasks: succ; % The (immediate) successors of tasks
% Planning Horizon
%
int: t_max; % End of the planning horizon
set of int: Times = 0..t_max; % The planning horizon
%-----------------------------------------------------------------------------%
% Derived parameters and schedule
% Tasks
%
array [Tasks] of set of Tasks: prec = % The (immediate) predecessors of tasks
[ {j | j in Tasks diff all_succ[i] where i in succ[j]} | i in Tasks ];
array [Tasks] of set of Tasks: all_succ = % All successors of tasks
[ all_succs(i) | i in Tasks ];
array [Tasks] of set of Tasks: all_prec = % All predecessors of tasks
[ {j | j in Tasks diff all_succ[i] where i in all_succ[j]} | i in Tasks ];
array [Tasks] of set of Tasks: unrelated = % All unrelated tasks of tasks (no successor and no predecessor)
[ Tasks diff all_succ[i] diff all_prec[i] diff {i} | i in Tasks ];
% The earliest start time schedule
%
array [Tasks] of Times: es = [ est(i) | i in Tasks ];
% The maximal resource consumption for the earliest start time schedule
%
array [Res] of int: rusage_es =
[ max(i in Tasks)(
rr[r, i] + sum(j in unrelated[i] where overlap(es[i], dur[i], es[j], dur[j]))(rr[r, j])
)
| r in Res ];
% The latest completion time schedule
%
array [Tasks] of Times: ls = [ lct(i) | i in Tasks ];
% The maximal resource consumption for the latest completion time schedule
%
array [Res] of int: rusage_ls =
[ max(i in Tasks)(
rr[r, i] + sum(j in unrelated[i] where overlap(ls[i] - dur[i], dur[i], ls[j] - dur[j], dur[j]))(rr[r, j])
)
| r in Res ];
%-----------------------------------------------------------------------------%
% Variables.
% The start times of tasks
%
array [Tasks] of var Times: s;
% The resource capacity/usage
%
array [Res] of var min(lb_usage)..max(ub_usage): rcap;
% The objective
%
var sum(lb_cost)..sum(ub_cost): objective;
%-----------------------------------------------------------------------------%
% Auxiliary functions and tests
% Computing all successors of a task
% (Note that this is not an efficient computation of all successors for a
% task and may take a long time for a large number of tasks.)
%
function set of Tasks: all_succs(Tasks: i) = (
let {
array[int] of set of Tasks: all_succs_js =
[ all_succs(j) | j in succ[i] ];
} in array_union(all_succs_js) union succ[i]
);
% Test whether the executions of two tasks are overlapping
%
test overlap(int: si, int: di, int: sj, int: dj) = (
si < sj + dj /\ sj < si + di
);
% Computation of the earliest start time of a task in the earliest
% start time schedule
% (Note that this is not an efficient computation of the earliest
% start time and may take a long time for a large number of tasks.)
%
function Times: est(Tasks: i) = (
let {
array[int] of Times: all_prec_ect = [0] ++ [ est(j) + dur[j] | j in prec[i] ];
} in
max(all_prec_ect)
);
% Computation of the latest completion time of a task in the latest
% completion time schedule
% (Note that this is not an efficient computation of the latest
% completion time and may take a long time for a large number of
% tasks.)
%
function Times: lct(Tasks: i) = (
let {
array[int] of Times: all_succ_lst = [t_max] ++ [ lct(j) - dur[j] | j in succ[i] ];
} in
min(all_succ_lst)
);
%-----------------------------------------------------------------------------%
% Constraints.
% Restricting the bounds on the resource capacity
%
constraint forall(r in Res)(
lb_usage[r] <= rcap[r] /\ rcap[r] <= ub_usage[r]
);
constraint forall(i in Tasks)(
s[i] + dur[i] <= t_max
);
% Precedence constraints
%
constraint forall(i in Tasks, j in succ[i])(
s[i] + dur[i] <= s[j]
);
% Cumulative resource constraints
% Note that this constraint will not bind the resource capacity variable!
constraint forall(r in Res)(
cumulative(s, dur, [ rr[r, i] | i in Tasks ], rcap[r])
);
% Redundant non-overlapping constraints
% Seems to be not worhtwhile at least for Chuffed
constraint redundant_constraint(
forall(i in Tasks, j in unrelated[i])(
forall(r in Res where rr[r, i] + rr[r, j] > lb_usage[r])(
(rr[r, i] + rr[r, j] > rcap[r]) -> (
(s[i] + dur[i] <= s[j]) \/ (s[j] + dur[j] <= s[i])
)
)
)
);
% Redundant constraint on the lower bound of the resource capacity
% modelling the task-decomposition of a cumulative constraint
%constraint redundant_constraint(
%/\ forall(i in Tasks, r in Res where rr[r, i] > 0)(
% rcap[r] >= rr[r, i] + sum(j in unrelated[i] where rr[r, j] > 0)(
% rr[r, j] * not(
% (s[j] + dur[j] <= s[i]) \/ (s[j] > s[i])
% )
% )
% )
%);
% Resource availability constraint
%
constraint objective = sum(r in Res)( cost[r] * rcap[r] );
% Upper bound on the resource availability cost wrt. to the
% earliest start time schedule
constraint objective <= sum(r in Res)( cost[r] * rusage_es[r] );
% Upper bound on the resource availability cost wrt. to the
% latest completion time schedule
constraint objective <= sum(r in Res)( cost[r] * rusage_ls[r] );
%-----------------------------------------------------------------------------%
% Search.
ann: search1 = seq_search([
int_search(rcap, first_fail, indomain_min, complete),
int_search(s, smallest, indomain_min, complete)
]);
array [Res] of Res: sort_idx_cost_desc = reverse(arg_sort(cost));
ann: search2 = seq_search([
int_search([rcap[sort_idx_cost_desc[r]] | r in Res], input_order, indomain_min, complete),
int_search(s, smallest, indomain_min, complete)
]);
solve
:: search1
minimize objective;
%-----------------------------------------------------------------------------%
% Output.
output [
"s = \(s);\n",
"rcap = \(rcap);\n",
"objective = \(objective);\n"
];
%-----------------------------------------------------------------------------%