Flexible Resource-Constrained Project Scheduling Problem with States (FRCPSPS)
SchedulingProblem
In the Flexible Resource-Constrained Project Scheduling Problem with States (FRCPSPS), a project consists of a set of tasks to schedule. Several resources are available, and each resource can process multiple tasks simultaneously. However, this must respect the capacity of the resource, and additionally, the resource and the tasks must be in the same state. If the resource changes state in order to perform tasks, a waiting time is applied before the state change.
The goal is to find a schedule that minimizes the makespan: the time when all tasks have been processed.
Principles learned
- Add set decision variables to model the allocation of tasks to resources
- Add interval decision variables to model the tasks
- Define lambda functions to model the resource capacity constraints and the state incompatibility constraints
Data
To illustrate this example, we generated random instances. The format of the Flexible Resource-Constrained Project Scheduling Problem with States (FRCPSPS) instances is as follows:
- First line:
- Number of tasks
- Number of resources
- Number of states
- Second line: maximum capacity for each resource
- From the third line,
- for each pair of states (i, j), the corresponding waiting time before changing from state i to state j .
- From the next line, for each task:
- Duration of the task
- State of the task
Model
The Hexaly model for the Flexible Resource-Constrained Project Scheduling Problem with States (FRCPSPS) uses interval decision variables representing the tasks, and set decision variables for the task set scheduled on each resource.
Using the partition operator, we ensure that each task is assigned to exactly one resource.
The resource constraints can be formulated as follows: for each resource and each time slot t, the amount of resource consumed by the tasks being processed must not exceed the resource’s capacity. To model these constraints, we sum up, for each resource and each time slot, the number of active tasks. We use a variadic ‘and’ formulation with a lambda function to ensure that resource capacity is respected at any time. Thanks to this variadic ‘and’, the constraint formulation remains compact and efficient even if the time horizon is very large.
Finally, we formulate the state constraint by ensuring that, for each pair of tasks assigned to a resource, either the tasks are of the same type or their execution intervals are disjoint, thereby preventing two tasks of different types from being executed simultaneously on the same resource. In addition, for tasks of different types, when the condition of disjoint intervals holds, we introduce the corresponding waiting time for each transition between states. This ensures that if a task of type i finishes, a task of type j cannot start until the waiting time from state i to state j has passed.
The makespan to minimize is the time when all tasks have finished.
- Execution
-
hexaly frcpsps.hxm inFileName=instances/instance1.txt [hxTimeLimit=] [solFileName=]
use io;
function input() {
local usage = "Usage: hexaly project_scheduling.hxm inFileName=instanceFile ";
if (inFileName == nil) println(usage);
inFile = io.openRead(inFileName);
// Number of tasks
nbTasks = inFile.readInt();
// Number of resources
nbResources = inFile.readInt();
// Number of states
nbStates = inFile.readInt();
// Maximum capacity of each resource
for [r in 0...nbResources] {
capacity[r] = inFile.readInt();
}
// Delay after change of state
for[i in 0...nbStates]{
for[j in 0...nbStates]{
delayState[i][j] = inFile.readInt();
}
}
for [i in 0...nbTasks] {
// Duration of each task
duration[i] = inFile.readInt();
// state of each task
state[i] = inFile.readInt();
}
}
function model() {
// Trivial upper bound for the end times of the tasks
timeHorizon = sum[i in 0...nbTasks](duration[i]);
// Set of tasks done by each resource
resourceTasks[k in 0..nbResources - 1] <- set(nbTasks);
// All tasks must be sheduled on a resource
constraint partition[k in 0..nbResources - 1](resourceTasks[k]);
// Interval decisions: time range of each task
tasks[i in 0...nbTasks] <- interval(0, timeHorizon);
// Task duration constraints
for [i in 0...nbTasks]{
constraint length(tasks[i]) == duration[i];
machineOfTask[i] <- find(resourceTasks,i);
}
for[k in 0..nbResources-1] {
// Resource constraints
constraint and(0...timeHorizon, t => sum(resourceTasks[k], i => contains(tasks[i], t)) <= capacity[k]);
// State incompatibility constraints
constraint and(resourceTasks[k], i =>
and(resourceTasks[k], j => state[i]==state[j] ||
end(tasks[i]) + delayState[state[i]][state[j]] <=start(tasks[j]) ||
end(tasks[j]) + delayState[state[j]][state[i]] <=start(tasks[i]))) ;
}
// Makespan: end of the last task
makespan <- max[i in 0...nbTasks](end(tasks[i]));
// Minimize the makespan
minimize makespan;
}
/* Parameterize the solver */
function param() {
if (hxTimeLimit == nil) hxTimeLimit = 60;
}
/* Write the solution in a file with the following format:
* - total makespan, number of Tasks
* - for each task, the task id, the start, the end times and the ressource of the task*/
function output() {
if (outFileName != nil) {
outFile = io.openWrite(outFileName);
println("Solution written in file ", outFileName);
outFile.println(makespan.value, " ", nbTasks);
for [m in 0...nbResources]{
for [task in resourceTasks[m].value]{
task_machine.value[task]=m;
}
}
for [i in 0...nbTasks] {
outFile.println(i + 1, " ", tasks[i].value.start, " ", tasks[i].value.end, " ", task_machine.value[i]);
}
}
}
- Execution (Windows)
-
set PYTHONPATH=%HX_HOME%\bin\pythonpython frcpsps.py instances\instance1.txt
- Execution (Linux)
-
export PYTHONPATH=/opt/hexaly_14_0/bin/pythonpython frcpsps.py instances/instance1.txt
import hexaly.optimizer
import sys
def read_instance(filename):
with open(filename) as f:
lines = f.readlines()
first_line = lines[0].split()
# Number of tasks
nb_tasks = int(first_line[0])
# Number of resources
nb_resources = int(first_line[1])
# Number of states
nb_states = int(first_line[2])
# Maximum capacity of each resource
capacity = [int(lines[1].split()[r]) for r in range(nb_resources)]
#Delay after change of state
delay_state = [[] for i in range(nb_states)]
for i in range(nb_states):
delay_state[i] = [int(lines[2+i].split()[j]) for j in range(nb_states)]
# Duration of each task
duration = [0 for i in range(nb_tasks)]
# State of each task
state = [0 for i in range(nb_tasks)]
for i in range(nb_tasks):
task_line = lines[i + 2 + nb_states].split()
duration[i] = int(task_line[0])
state[i] = int(task_line[1])
# Trivial upper bound for the end times of the tasks
horizon = sum(duration[i] for i in range(nb_tasks))
return (nb_tasks, nb_resources, nb_states, capacity, delay_state, duration, state , horizon)
def main(instance_file, output_file, time_limit):
nb_tasks, nb_resources, nb_states, capacity, delay_state, duration, state , horizon = read_instance(instance_file)
with hexaly.optimizer.HexalyOptimizer() as optimizer:
# Declare the optimization model
model = optimizer.model
# Interval decisions: time range of each task
tasks = [model.interval(0, horizon) for i in range(nb_tasks)]
# Task duration constraints
for i in range(nb_tasks):
model.constraint(model.length(tasks[i]) == duration[i])
# Set of tasks done by each resource
resources_tasks = [model.set(nb_tasks) for r in range(nb_resources)]
resources = model.array(resources_tasks)
# All tasks must be sheduled on a resource
model.constraint(model.partition(resources))
# Creates Hexaly arrays to allow access through "at" operator
tasks_array = model.array(tasks)
state_array = model.array(state)
delay_array = model.array(delay_state)
# Makespan: end of the last task
makespan = model.max([model.end(tasks[i]) for i in range(nb_tasks)])
# Resource constraints
for r in range(nb_resources):
capacity_respected = model.lambda_function(
lambda t: model.sum(resources[r], model.lambda_function(
lambda i: model.contains(tasks_array[i], t)))
<= capacity[r])
model.constraint(model.and_(model.range(makespan), capacity_respected))
# State incompatibility constraints
for r in range(nb_resources):
state_respected = model.lambda_function(
lambda i: model.and_(resources_tasks[r], model.lambda_function(
lambda j: model.or_(state_array[i]==state_array[j],
(model.end(tasks_array[i]) + delay_array[state_array[i]][state_array[j]]) <=model.start(tasks_array[j]),
(model.end(tasks_array[j]) + delay_array[state_array[j]][state_array[i]]) <=model.start(tasks_array[i])))))
model.constraint(model.and_(resources_tasks[r], state_respected))
# Minimize the makespan
model.minimize(makespan)
model.close()
# Parameterize the optimizer
optimizer.param.time_limit = time_limit
optimizer.solve()
# Write the solution in a file with the following format:
# - total makespan, number of Tasks
# - for each task, the task id, the start, the end times and the ressource of the task*/
#
if output_file != None:
task_resources = [0 for i in range(nb_tasks)]
for r in range(nb_resources):
for task in resources_tasks[r].value:
task_resources[task]= r
with open(output_file, "w") as f:
print("Solution written in file", output_file)
f.write(str(makespan.value) + " " + str(nb_tasks) + "\n")
for i in range(nb_tasks):
f.write(str(i + 1) + " " + str(tasks[i].value.start()) + " " + str(tasks[i].value.end()) + " " + str(task_resources[i]))
f.write("\n")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python rcpsp.py instance_file [output_file] [time_limit]")
sys.exit(1)
instance_file = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) >= 3 else None
time_limit = int(sys.argv[3]) if len(sys.argv) >= 4 else 60
main(instance_file, output_file, time_limit)
- Compilation / Execution (Windows)
-
cl /EHsc frcpsps.cpp -I%HX_HOME%\include /link %HX_HOME%\bin\hexaly140.libfrcpsps instances\instance1.txt
- Compilation / Execution (Linux)
-
g++ frcpsps.cpp -I/opt/hexaly_14_0/include -lhexaly140 -lpthread -o frcpsps./frcpsps instances/instance1.txt
#include "optimizer/hexalyoptimizer.h"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <limits>
#include <numeric>
#include <vector>
using namespace hexaly;
class Frcpsps {
private:
// Number of tasks
int nbTasks;
// Number of resources
int nbResources;
// Number of states
int nbStates;
// Maximum capacity of each resource
std::vector<int> capacity;
// Delay after change of state
std::vector<std::vector<int>> delayState;
// Duration of each task
std::vector<int> duration;
// state of each task
std::vector<int> state;
// Trivial upper bound for the end times of the tasks
int horizon = 0;
// Hexaly Optimizer
HexalyOptimizer optimizer;
// Decision variables: time range of each task
std::vector<HxExpression> tasks;
// Decision variables: set of tasks done by each resource
std::vector<HxExpression> resourceTasks;
// For each task, the selected resource
std::vector<HxExpression> taskResource;
// Objective = minimize the makespan: end of the last task
HxExpression makespan;
public:
Frcpsps(const std::string& fileName) : optimizer() {}
void readInstance(const std::string& fileName) {
std::ifstream infile;
infile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
infile.open(fileName.c_str());
infile >> nbTasks >> nbResources >> nbStates;
capacity.resize(nbResources);
for (int r = 0; r < nbResources; ++r) {
infile >> capacity[r];
}
delayState.resize(nbStates);
for (int s = 0; s < nbStates; ++s) {
delayState[s].resize(nbStates);
}
for (int i = 0; i < nbStates; ++i) {
for (int j = 0; j < nbStates; ++j) {
infile >> delayState[i][j];
}
}
duration.resize(nbTasks);
state.resize(nbTasks);
for (int i = 0; i < nbTasks; ++i) {
infile >> duration[i] >> state[i];
}
// Trivial upper bound for the end times of the tasks
horizon = std::accumulate(duration.begin(), duration.end(), 0);
infile.close();
}
void solve(int TimeLimit) {
// Declare the optimization model
HxModel model = optimizer.getModel();
// Interval decisions: time range of each task
tasks.resize(nbTasks);
for (int i = 0; i < nbTasks; ++i) {
tasks[i] = model.intervalVar(0, horizon);
// Task duration constraints
model.constraint(model.length(tasks[i]) == duration[i]);
}
// Set of tasks done by each resource
resourceTasks.resize(nbResources);
HxExpression resources = model.array();
for (int r = 0; r < nbResources; ++r) {
resourceTasks[r] = model.setVar(nbTasks);
resources.addOperand(resourceTasks[r]);
}
// All tasks must be sheduled on a resource
model.constraint(model.partition(resources));
// Makespan: end of the last task
makespan = model.max();
for (int i = 0; i < nbTasks; ++i) {
makespan.addOperand(model.end(tasks[i]));
}
// For each task, the selected resource
taskResource.resize(nbTasks);
for (int i = 0; i < nbTasks; ++i) {
taskResource[i] = model.find(resources, i);
}
// Creates Hexaly arrays to allow access through "at" operator
HxExpression tasksArray = model.array();
for (int i = 0; i < nbTasks; ++i) {
tasksArray.addOperand(tasks[i]);
}
HxExpression stateArray = model.array();
for (int i = 0; i < nbTasks; ++i) {
stateArray.addOperand(state[i]);
}
HxExpression delayArray = model.array();
for (int s = 0; s < nbStates; ++s) {
delayArray.addOperand(model.array(delayState[s].begin(), delayState[s].end()));
}
// Resource constraints
for (int r = 0; r < nbResources; ++r) {
HxExpression capacityRespected = model.createLambdaFunction([&](HxExpression t) {
HxExpression total = model.createLambdaFunction([&](HxExpression i) {
return model.contains(tasksArray[i], t);
});
HxExpression totalTasks = model.sum(resourceTasks[r], total);
return model.leq(totalTasks, capacity[r]);
});
model.constraint(model.and_(model.range(0, makespan), capacityRespected));
}
// State incompatibility constraints
for (int r = 0; r < nbResources; ++r) {
HxExpression stateRespected = model.createLambdaFunction([&](HxExpression j) {
HxExpression total = model.createLambdaFunction([&](HxExpression i) {
return model.or_(stateArray[i]==stateArray[j],
model.end(tasksArray[i]) + delayArray[stateArray[i]][stateArray[j]] <= model.start(tasksArray[j]),
model.end(tasksArray[j]) + delayArray[stateArray[j]][stateArray[i]] <= model.start(tasksArray[i]));
});
HxExpression totalstates = model.and_(resourceTasks[r], total);
return totalstates;
});
model.constraint(model.and_(resourceTasks[r], stateRespected));
}
// Minimize the makespan
model.minimize(makespan);
model.close();
// Parameterize the optimizer
optimizer.getParam().setTimeLimit(TimeLimit);
optimizer.solve();
}
/* Write the solution in a file with the following format:
* - total makespan, number of Tasks
* - for each task, the task id, the start, the end times and the ressource of the task*/
void writeSolution(const std::string& fileName) {
std::ofstream outfile(fileName.c_str());
if (!outfile.is_open()) {
std::cerr << "File " << fileName << " cannot be opened." << std::endl;
exit(1);
}
std::cout << "Solution written in file " << fileName << std::endl;
outfile << makespan.getValue() << " " << nbTasks << std::endl;
for (int i = 0; i < nbTasks; ++i) {
outfile << i + 1 << " " << tasks[i].getIntervalValue().getStart() << " "
<< tasks[i].getIntervalValue().getEnd() << " "
<< taskResource[i].getValue()
<< std::endl;
}
outfile.close();
}
};
int main(int argc, char** argv) {
if (argc < 2) {
std::cout << "Usage: Frcpsps instanceFile [outputFile] [timeLimit]" << std::endl;
exit(1);
}
const char* instanceFile = argv[1];
const char* outputFile = argc > 2 ? argv[2] : NULL;
const char* strTimeLimit = argc > 3 ? argv[3] : "60";
Frcpsps model(instanceFile);
try {
model.readInstance(instanceFile);
const int timeLimit = atoi(strTimeLimit);
model.solve(timeLimit);
if (outputFile != NULL)
model.writeSolution(outputFile);
return 0;
} catch (const std::exception& e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
return 1;
}
}
- Compilation / Execution (Windows)
-
copy %HX_HOME%\bin\Hexaly.NET.dll .csc Frcpsps.cs /reference:Hexaly.NET.dllFrcpsps instances\instance1.txt
using System;
using System.IO;
using Hexaly.Optimizer;
public class Frcpsps : IDisposable
{
// Number of tasks
private int nbTasks;
// Number of resources
private int nbResources;
// Number of states
private int nbStates;
// Maximum capacity of each resource
private int[] capacity;
// Delay after change of state
private int[][] delayState;
// Duration of each task
private int[] duration;
// state of each task
private int[] state;
// Trivial upper bound for the end times of the tasks
private int horizon = 0;
// Hexaly Optimizer
private HexalyOptimizer optimizer;
// Decision variables: time range of each task
private HxExpression[] tasks;
// Decision variables: set of tasks done by each resource
private HxExpression[] resourceTasks;
// For each task, the selected resource
private HxExpression[] taskResource;
// Objective = minimize the makespan: end of the last task of the last job
private HxExpression makespan;
public Frcpsps(string fileName)
{
optimizer = new HexalyOptimizer();
}
private static string[] ReadNextLine(StreamReader input) {
return input.ReadLine().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
}
private void ReadInstance(string fileName)
{
using (StreamReader input = new StreamReader(fileName))
{
string[] splitted = ReadNextLine(input);
if (splitted.Length == 0)
splitted = ReadNextLine(input);
nbTasks = int.Parse(splitted[0]);
nbResources = int.Parse(splitted[1]);
nbStates = int.Parse(splitted[2]);
capacity = new int[nbResources];
splitted = ReadNextLine(input);
for (int r = 0; r < nbResources; ++r)
capacity[r] = int.Parse(splitted[r]);
delayState = new int[nbStates][];
for (int i = 0; i < nbStates; ++i)
{
splitted = ReadNextLine(input);
delayState[i] = new int[nbStates];
for (int j = 0; j < nbStates; ++j)
delayState[i][j] = int.Parse(splitted[j]);
}
duration = new int[nbTasks];
state = new int[nbTasks];
for (int i = 0; i < nbTasks; ++i)
{
splitted = ReadNextLine(input);
if (splitted.Length == 0)
splitted = ReadNextLine(input);
duration[i] = int.Parse(splitted[0]);
state[i] = int.Parse(splitted[1]);
horizon += duration[i];
}
}
}
public void Dispose()
{
optimizer.Dispose();
}
public void Solve(int timeLimit)
{
// Declare the optimization model
HxModel model = optimizer.GetModel();
// Interval decisions: time range of each task
tasks = new HxExpression[nbTasks];
for (int i = 0; i < nbTasks; ++i)
{
tasks[i] = model.Interval(0, horizon);
// Task duration constraints
model.Constraint(model.Length(tasks[i]) == duration[i]);
}
// Set of tasks done by each resource
resourceTasks = new HxExpression[nbResources];
for (int r = 0; r < nbResources; ++r)
resourceTasks[r] = model.Set(nbTasks);
// Creates Hexaly arrays to allow access through "at" operator
HxExpression resources = model.Array(resourceTasks);
HxExpression delayArray = model.Array(delayState);
HxExpression tasksArray = model.Array(tasks);
HxExpression stateArray = model.Array(state);
// All tasks must be sheduled on a resource
model.Constraint(model.Partition(resources));
// For each task, the selected resource
taskResource = new HxExpression[nbTasks];
for (int i = 0; i < nbTasks; ++i)
{
taskResource[i] = model.Find(resources, i);
}
// Makespan: end of the last task
makespan = model.Max();
for (int i = 0; i < nbTasks; ++i)
makespan.AddOperand(model.End(tasks[i]));
// Resource constraints
for (int r = 0; r < nbResources; ++r)
{
HxExpression capacityRespected = model.LambdaFunction(t =>
{
HxExpression total = model.LambdaFunction(i =>
{
HxExpression rExpr = model.CreateConstant(r);
return model.Contains(tasksArray[i], t);
});
HxExpression totalTasks = model.Sum(resourceTasks[r], total);
return totalTasks <= capacity[r];
});
model.Constraint(model.And(model.Range(0, makespan), capacityRespected));
}
// State incompatibility constraints
for (int r = 0; r < nbResources; ++r)
{
HxExpression stateRespected = model.LambdaFunction(j =>
{
HxExpression total = model.LambdaFunction(i =>
{
HxExpression rExpr = model.CreateConstant(r);
return model.Or(stateArray[i]==stateArray[j],
(model.End(tasksArray[i]) + delayArray[stateArray[i]][stateArray[j]]) <= model.Start(tasksArray[j]),
(model.End(tasksArray[j]) + delayArray[stateArray[j]][stateArray[i]]) <= model.Start(tasksArray[i]));
});
HxExpression TotalExpr = model.And(resourceTasks[r], total);
return TotalExpr;
});
model.Constraint(model.And(resourceTasks[r], stateRespected));
}
// Minimize the makespan
model.Minimize(makespan);
model.Close();
// Parameterize the optimizer
optimizer.GetParam().SetTimeLimit(timeLimit);
optimizer.Solve();
}
/* Write the solution in a file with the following format:
* - total makespan, number of Tasks
* - for each task, the task id, the start, the end times and the ressource of the task*/
public void WriteSolution(string fileName)
{
using (StreamWriter output = new StreamWriter(fileName))
{
Console.WriteLine("Solution written in file " + fileName);
output.WriteLine(makespan.GetValue()+ " " + nbTasks);
for (int i = 0; i < nbTasks; ++i)
{
output.Write((i + 1) + " " + tasks[i].GetIntervalValue().Start() + " " + tasks[i].GetIntervalValue().End() + " " + taskResource[i].GetValue());
output.WriteLine();
}
}
}
public static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage: Frcpsps instanceFile [outputFile] [timeLimit]");
System.Environment.Exit(1);
}
string instanceFile = args[0];
string outputFile = args.Length > 1 ? args[1] : null;
string strTimeLimit = args.Length > 2 ? args[2] : "60";
using (Frcpsps model = new Frcpsps(instanceFile))
{
model.ReadInstance(instanceFile);
model.Solve(int.Parse(strTimeLimit));
if (outputFile != null)
model.WriteSolution(outputFile);
}
}
}
- Compilation / Execution (Windows)
-
javac Frcpsps.java -cp %HX_HOME%\bin\hexaly.jarjava -cp %HX_HOME%\bin\hexaly.jar;. Frcpsps instances\instance1.txt
- Compilation / Execution (Linux)
-
javac Frcpsps.java -cp /opt/hexaly_14_0/bin/hexaly.jarjava -cp /opt/hexaly_14_0/bin/hexaly.jar:. Frcpsps instances/instance1.txt
import java.util.*;
import java.io.*;
import com.hexaly.optimizer.*;
public class Frcpsps {
// Number of tasks
private int nbTasks;
// Number of resources
private int nbResources;
// Number of states
private int nbStates;
// Maximum capacity of each resource
private int[] capacity;
// Delay after change of state
private int[][] delayState;
// Duration of each task
private int[] duration;
// state of each task
private int[] state;
// Trivial upper bound for the end times of the tasks
private int horizon = 0;
// Hexaly Optimizer
final HexalyOptimizer optimizer;
// Decision variables: time range of each task
private HxExpression[] tasks;
// Decision variables: set of tasks done by each resource
private HxExpression[] resourceTasks;
// For each task, the selected resource
private HxExpression[] taskResource;
// Objective = minimize the makespan: end of the last task of the last job
private HxExpression makespan;
public Frcpsps(HexalyOptimizer optimizer, String fileName) throws IOException {
this.optimizer = optimizer;
}
private void readInstance(String fileName) throws IOException {
try (Scanner input = new Scanner(new File(fileName))) {
input.useLocale(Locale.ROOT);
nbTasks = input.nextInt();
nbResources = input.nextInt();
nbStates = input.nextInt();
capacity = new int[nbResources];
for (int r = 0; r < nbResources; ++r) {
capacity[r] = input.nextInt();
}
delayState = new int[nbStates][nbStates];
for (int i = 0; i < nbStates; ++i) {
for (int j = 0; j < nbStates; ++j) {
delayState[i][j] = input.nextInt();
}
}
duration = new int[nbTasks];
state = new int[nbTasks];
for (int i = 0; i < nbTasks; ++i) {
duration[i] = input.nextInt();
state[i] = input.nextInt();
horizon += duration[i];
}
}
}
public void solve(int timeLimit) {
// Declare the optimization model
HxModel model = optimizer.getModel();
// Interval decisions: time range of each task
tasks = new HxExpression[nbTasks];
for (int i = 0; i < nbTasks; ++i) {
tasks[i] = model.intervalVar(0, horizon);
// Task duration constraints
model.constraint(model.eq(model.length(tasks[i]), duration[i]));
}
// Set of tasks done by each resource
resourceTasks = new HxExpression[nbResources];
for (int r = 0; r < nbResources; ++r) {
resourceTasks[r] = model.setVar(nbTasks);
}
// Creates Hexaly arrays to allow access through "at" operator
HxExpression resources = model.array(resourceTasks);
HxExpression delayArray = model.array(delayState);
HxExpression tasksArray = model.array(tasks);
HxExpression stateArray = model.array(state);
// For each task, the selected resource
taskResource = new HxExpression[nbTasks];
for (int i = 0; i < nbTasks; ++i) {
taskResource[i] = model.find(resources, i);
}
// All tasks must be sheduled on a resource
model.constraint(model.partition(resources));
// Makespan: end of the last task
makespan = model.max();
for (int i = 0; i < nbTasks; ++i) {
makespan.addOperand(model.end(tasks[i]));
}
// Resource constraints
for (int r = 0; r < nbResources; ++r) {
final int rL = r;
HxExpression capacityRespected = model.lambdaFunction(t -> {
HxExpression total = model.lambdaFunction(i -> {
HxExpression rExpr = model.createConstant(rL);
return model.contains(model.at(tasksArray, i), t);
});
HxExpression totalTasks = model.sum(resourceTasks[rL], total);
return model.leq(totalTasks, capacity[rL]);
});
model.constraint(model.and(model.range(0, makespan), capacityRespected));
}
// State incompatibility constraints
for (int r = 0; r < nbResources; ++r) {
final int rL = r;
HxExpression stateRespected = model.lambdaFunction(j -> {
HxExpression total = model.lambdaFunction(i ->
{
HxExpression rExpr = model.createConstant(rL);
return model.or(
model.eq(model.at(stateArray, i), model.at(stateArray, j)),
model.leq(
model.sum(model.end(model.at(tasksArray, j)), model.at(delayArray, model.at(stateArray, j), model.at(stateArray, i))),
model.start(model.at(tasksArray, i))),
model.leq(
model.sum(model.end(model.at(tasksArray, i)), model.at(delayArray, model.at(stateArray, i), model.at(stateArray, j))),
model.start(model.at(tasksArray, j)))
);
});
HxExpression TotalExpr = model.and(resourceTasks[rL], total);
return TotalExpr;
});
model.constraint(model.and(resourceTasks[rL], stateRespected));
}
// Minimize the makespan
model.minimize(makespan);
model.close();
// Parameterize the optimizer
optimizer.getParam().setTimeLimit(timeLimit);
optimizer.solve();
}
/* Write the solution in a file with the following format:
* - total makespan, number of Tasks
* - for each task, the task id, the start, the end times and the ressource of the task*/
public void writeSolution(String fileName) throws IOException {
try (PrintWriter output = new PrintWriter(fileName)) {
System.out.println("Solution written in file " + fileName);
output.println(makespan.getValue()+ " " + nbTasks);
for (int i = 0; i < nbTasks; ++i) {
output.println((i + 1) + " " + tasks[i].getIntervalValue().getStart() + " "
+ tasks[i].getIntervalValue().getEnd() + " "
+ taskResource[i].getValue()
);
}
}
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: java project_schedulingg instanceFile [outputFile] [timeLimit]");
System.exit(1);
}
String instanceFile = args[0];
String outputFile = args.length > 1 ? args[1] : null;
String strTimeLimit = args.length > 2 ? args[2] : "60";
try (HexalyOptimizer optimizer = new HexalyOptimizer()) {
Frcpsps model = new Frcpsps(optimizer, instanceFile);
model.readInstance(instanceFile);
model.solve(Integer.parseInt(strTimeLimit));
if (outputFile != null) {
model.writeSolution(outputFile);
}
} catch (Exception ex) {
System.err.println(ex);
ex.printStackTrace();
System.exit(1);
}
}
}