Skip to content

Commit

Permalink
Merge pull request #104 from CharJon/hooker-scheduling-generators
Browse files Browse the repository at this point in the history
Hooker scheduling generators
  • Loading branch information
CharJon authored Feb 23, 2021
2 parents 23d2fb0 + 09eba2d commit eb965b7
Show file tree
Hide file tree
Showing 6 changed files with 677 additions and 87 deletions.
219 changes: 219 additions & 0 deletions geco/mips/scheduling/generic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,225 @@
import itertools

from networkx.utils import py_random_state
from pyscipopt import scip


def late_tasks_formulation(
number_of_facilities,
number_of_tasks,
time_steps,
processing_times,
capacities,
assignment_costs,
release_dates,
deadlines,
name="Hooker Scheduling Late Tasks Formulation",
):
"""Generates late tasks mip formulation described in section 4 in [1].
Parameters
----------
number_of_facilities: int
the number of facilities to schedule on
number_of_tasks: int
the number of tasks to assign to facilities
time_steps:
the number of time steps starting from 0 (corresponds to "N" in the paper)
processing_times: dict[int,int]
time steps to process each task
capacities: list[int]
capacity of each facility
assignment_costs: dict[int,int]
cost of assigning a task to a facility
release_dates: list[int]
time step at which a job is released
deadlines: dict[int, float]
deadline (time step) to finish a job
name: str
assigned name to generated instance
Returns
-------
model: SCIP model of the late tasks instance
References
----------
.. [1] Hooker, John. (2005). Planning and Scheduling to Minimize
Tardiness. 314-327. 10.1007/11564751_25.
"""
model = scip.Model(name)

start_time = min(release_dates)
time_steps = range(start_time, start_time + time_steps)

# add variables and their cost
L = []
for i in range(number_of_tasks):
var = model.addVar(lb=0, ub=1, obj=1, name=f"L_{i}", vtype="B")
L.append(var)

# assignment vars
x = {}
for j, i, t in itertools.product(
range(number_of_tasks), range(number_of_facilities), time_steps
):
var = model.addVar(lb=0, ub=1, obj=0, name=f"x_{j}_{i}_{t}", vtype="B")
x[j, i, t] = var

# add constraints
# constraint (a)
for j, t in itertools.product(range(number_of_tasks), time_steps):
model.addCons(
len(time_steps) * L[j]
>= scip.quicksum(
(
(t + processing_times[j, i]) * x[j, i, t] - deadlines[j]
for i in range(number_of_facilities)
)
)
)

# constraint (b)
for j in range(number_of_tasks):
vars = (
x[j, i, t]
for i, t in itertools.product(range(number_of_facilities), time_steps)
)
model.addCons(scip.quicksum(vars) == 1)

# constraint (c)
for i, t in itertools.product(range(number_of_facilities), time_steps):
vars = []
for j in range(number_of_tasks):
vars += [
assignment_costs[j, i] * x[j, i, t_prime]
for t_prime in range(t - processing_times[j, i] + 1, t + 1)
if (j, i, t_prime) in x
]
model.addCons(scip.quicksum(vars) <= capacities[i])

# constraint (d)
for i, j, t in itertools.product(
range(number_of_facilities), range(number_of_tasks), time_steps
):
if t < release_dates[j] or t > len(time_steps) - processing_times[j, i]:
model.addCons(x[j, i, t] == 0)

model.setMinimize()

return model


def heinz_formulation(
number_of_facilities,
number_of_tasks,
processing_times,
capacities,
assignment_costs,
release_dates,
deadlines,
resource_requirements,
name="Heinz Scheduling Formulation",
):
"""Generates scheduling MIP formulation according to Model 4 in [1].
Parameters
----------
number_of_facilities: int
the number of facilities to schedule on
number_of_tasks: int
the number of tasks to assign to facilities
processing_times: dict[(int,int),int]
time steps to process each task
capacities: list[int]
capacity of each facility
assignment_costs: dict[(int,int),int]
cost of assigning a task to a facility
release_dates: list[int]
time step at which a job is released
deadlines: dict[int, float]
deadline (time step) to finish a job
resource_requirements: dict[(int,int),int]
resources required for each task assigned to a facility
name: str
assigned name to generated instance
Returns
-------
model: SCIP model of generated instance
References
----------
.. [1] Heinz, J. (2013). Recent Improvements Using Constraint Integer Programming for Resource Allocation and Scheduling.
In Integration of AI and OR Techniques in Constraint Programming for Combinatorial Optimization Problems
(pp. 12–27). Springer Berlin Heidelberg.
"""
model = scip.Model(name)

time_steps = range(min(release_dates), int(max(deadlines)))

# objective function
x = {}
for j, k in itertools.product(range(number_of_tasks), range(number_of_facilities)):
var = model.addVar(
lb=0, ub=1, obj=assignment_costs[j, k], name=f"x_{j}_{k}", vtype="B"
)
x[j, k] = var

# y vars
y = {}
for j, k, t in itertools.product(
range(number_of_tasks), range(number_of_facilities), time_steps
):
if release_dates[j] <= t <= deadlines[j] - processing_times[j, k]:
var = model.addVar(lb=0, ub=1, obj=0, name=f"y_{j}_{k}_{t}", vtype="B")
y[j, k, t] = var

# add constraints
# constraint (12)
for j in range(number_of_tasks):
model.addCons(scip.quicksum(x[j, k] for k in range(number_of_facilities)) == 1)

# constraint (13)
for j, k in itertools.product(range(number_of_tasks), range(number_of_facilities)):
model.addCons(
scip.quicksum(
y[j, k, t]
for t in range(
release_dates[j], int(deadlines[j]) - processing_times[j, k]
)
if t < len(time_steps)
)
== x[j, k]
)

# constraint (14)
for k, t in itertools.product(range(number_of_facilities), time_steps):
model.addCons(
scip.quicksum(
resource_requirements[j, k] * y[j, k, t_prime]
for j in range(number_of_tasks)
for t_prime in range(t - processing_times[j, k], t + 1)
if (j, k, t_prime) in y
)
<= capacities[k]
)

# constraint (15)
epsilon = filter(
lambda ts: ts[0] < ts[1], itertools.product(release_dates, deadlines)
)
for k, (t1, t2) in itertools.product(range(number_of_facilities), epsilon):
model.addCons(
scip.quicksum(
processing_times[j, k] * resource_requirements[j, k] * x[j, k]
for j in range(number_of_tasks)
if t1 <= release_dates[j] and t2 >= deadlines[j]
)
<= capacities[k] * (t2 - t1)
)

return model


@py_random_state(-1)
Expand Down
9 changes: 5 additions & 4 deletions geco/mips/scheduling/heinz.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from networkx.utils import py_random_state
import pyscipopt as scip

from geco.mips.scheduling.generic import *
Expand All @@ -18,17 +19,17 @@ def heinz_params(number_of_facilities, number_of_tasks, seed=0):
Returns
-------
processing_times: dict[int,int]
processing_times: dict[(int,int),int]
time steps to process each task
capacities: list[int]
capacity of each facility
assignment_costs: dict[int,int]
assignment_costs: dict[(int,int),int]
cost of assigning a task to a facility
release_times: list[int]
time step at which a job is released
deadlines: dict[int, float]
deadlines: dict[int,int]
deadline (time step) to finish a job
resource_requirements: dict[int,int]
resource_requirements: dict[(int,int),int]
resources required for each task assigned to a facility
References
Expand Down
Loading

0 comments on commit eb965b7

Please sign in to comment.