Aircraft Deicing Problem
SchedulingProblem
The Aircraft Deicing Problem is a common case in operations research. In this context, aircraft arriving at an airport must undergo a deicing operation. One has to schedule these deicing tasks with limited resources: the locations to carry out this operation (gates) and the deicing trucks. The deicing time for an aircraft decreases with the number of trucks assigned to it. The objective is to determine, for each aircraft, an assignment to a deicing gate, a processing interval, and a number of trucks, in such a way as to minimize the completion time of the final aircraft (makespan).
Principles learned
- Add interval decision variables to model the deicing
- Add list decision variables to model the aircraft assigned to each gate
- Add int decision variables to model the number of trucks assigned to each aircraft
- Use the makespan as upper bound for the and operator in the trucks resource constraint
Data
We provide randomly generated instances with the following format:
- The number of gates.
- The number of aircraft.
- The size of each aircraft.
- The arrival date of each aircraft.
- The lower bound of the number of trucks needed by each aircraft.
- The upper bound of the number of trucks needed by each aircraft.
- The base duration for deicing of each aircraft (then normalized by the number of assigned trucks).
- The setup time matrix, which indicates the minimal separation time at a given gate between two consecutive aircrafts i and j.
Program
The Hexaly model for the Aircraft Deicing Problem relies on three types of decision variables:
- An array of list decisions representing the gates: list i corresponds to the sequence of aircraft assigned to gate i;
- An array of interval decisions representing the processing intervals: the interval associated with aircraft j corresponds to its deicing time window;
- An array of integer decisions representing the number of trucks assigned to each aircraft.
We define the constraints of the problem as follows. Each aircraft must be assigned to exactly one gate, ensuring a unique allocation. The processing interval associated with each aircraft must match its required deicing time, which depends on the number of trucks assigned to it. Furthermore, no two aircraft can be processed simultaneously at the same gate: the deicing of one aircraft may only begin after the previous one has finished, with an additional setup time that depends on the pair of aircraft involved. Finally, the total number of trucks in use at any given moment cannot exceed the number of trucks available at the airport.
The objective function is to minimize the makespan, i.e., the time at which all aircraft have been deiced.
- Execution
-
hexaly aircraft-deicing.hxm inFileName=instances/60_aircrafts.txt [hxTimeLimit=] [solFileName=]
/********** aircraft_deicing.hxm **********/
use io;
use hexaly;
function input() {
local usage = "\nUsage: hexaly aircraft_deicing.hxm inFileName=inputFile"
+ "[solFileName=outputFile] [hxTimeLimit=timeLimit]\n";
if (inFileName == nil) throw usage;
local inFile = io.openRead(inFileName);
// Number of gates
nbGates = inFile.readInt();
// Aircrafts data
nbAircrafts = inFile.readInt();
sizeAircrafts[_ in 0...nbAircrafts] = inFile.readInt();
arrivalDates[_ in 0...nbAircrafts] = inFile.readInt();
// Trucks data
nbTrucks = inFile.readInt();
minTrucks[_ in 0...nbAircrafts] = inFile.readInt();
maxTrucks[_ in 0...nbAircrafts] = inFile.readInt();
local maxTotalTrucks = max[t in maxTrucks](t);
// Durations data
durations[m in 0...maxTotalTrucks][i in 0...nbAircrafts] = inFile.readInt();
// Setup Matrix: times needed between two aircrafts
for [line in 0...nbAircrafts][column in 0...nbAircrafts] {
setupMatrix[line][column] = inFile.readInt();
}
inFile.close();
// Horizon: maximum durations
H = max[aircraft in 0...nbAircrafts](arrivalDates[aircraft])
+ sum[i in 0...nbAircrafts](durations[0][i]);
}
function model() {
// Decision variables: to each aircraft, we associate an interval, a gate and a number of trucks
aircraftsIntervals[i in 0...nbAircrafts] <- interval(arrivalDates[i], H);
aircraftsNbTrucks[i in 0...nbAircrafts]
<- int(minTrucks[sizeAircrafts[i]], maxTrucks[sizeAircrafts[i]]);
gates[_ in 0...nbGates] <- list(nbAircrafts);
// Constraint: every aircraft has a unique gate
constraint partition(gates);
// Constraint non-overlap: one aircraft per gate for each time,
// and a setup time between two aircrafts on the same gate
for [gate in gates] {
constraint and(0...count(gate)-1, i => end(aircraftsIntervals[gate[i]])
+ setupMatrix[gate[i]][gate[i+1]] <= start(aircraftsIntervals[gate[i+1]]));
}
// Constraint: durations of the de-icing
for [aircraft in 0...nbAircrafts] {
constraint length(aircraftsIntervals[aircraft])
== durations[aircraftsNbTrucks[aircraft]-1][aircraft];
}
// Constraint: no more than nbTrucks in total
makespan <- max[aircraftInter in aircraftsIntervals](end(aircraftInter));
constraint and(0...makespan, time => sum[aircraft in 0...nbAircrafts](
contains(aircraftsIntervals[aircraft], time)
* aircraftsNbTrucks[aircraft]) <= nbTrucks);
// Minimize makespan
minimize makespan;
}
function param() {
if (hxTimeLimit is nil) hxTimeLimit = 60;
}
function output() {
if (solFileName == nil) return;
local outfile = io.openWrite(solFileName);
/* Write the solution in a file with the following format:
* - the total makespan
* - for each aircraft, the bay, the starting date of the interval,
the ending date of the interval, the number of trucks attributed */
outfile.println(makespan.value);
for [b in 0...nbGates] {
gate = gates[b].value;
for [aircraft in gate] {
outfile.print(
b, " ",
aircraftsIntervals[aircraft].value.start, " ",
aircraftsIntervals[aircraft].value.end, " ",
aircraftsNbTrucks[aircraft].value
);
outfile.println();
}
}
println("Solution written in " + solFileName);
}
- Execution (Windows)
-
set PYTHONPATH=%HX_HOME%\bin\pythonpython aircraft_deicing.py instances\60_aircrafts.txt
- Execution (Linux)
-
export PYTHONPATH=/opt/hexaly_14_0/bin/pythonpython aircraft_deicing.py instances/60_aircrafts.txt
import hexaly.optimizer
import sys
import math
def main(instance_file, output_file, time_limit):
with hexaly.optimizer.HexalyOptimizer() as optimizer:
#
# Declare the optimization model
#
model = optimizer.model
with open(instance_file) as f:
lines = f.readlines()
# Number of gates
nb_gates = int(lines[0])
# Aircrafts data
nb_aircrafts = int(lines[1])
size_aircraft = [int(i) for i in lines[2].split()]
arrival_date = [int(i) for i in lines[3].split()]
# Trucks data
nb_trucks = int(lines[4])
min_trucks = [int(i) for i in lines[5].split()]
max_trucks = [int(i) for i in lines[6].split()]
max_total_trucks = max(max_trucks)
# Duration data
# base_line_duration = [int(i) for i in lines[7].split()]
# ratio = [math.sqrt(k) for k in range(1, max_total_trucks + 1)]
# duration = model.array([[math.ceil(base_line_duration[i]/ratio[m]) \
# for m in range(max_total_trucks)] for i in range(nb_aircrafts)])
duration = []
for line in lines[7:12]:
line_split = [int(i) for i in line.split()]
duration.append(line_split)
# Horizon: maximum duration
H = max(arrival_date) + sum(duration[0])
duration = model.array(duration)
# Setup Matrix: times needed between two aircrafts in a gate
setup_matrix = []
for line in lines[12:]:
line_split = [int(i) for i in line.split()]
setup_matrix.append(line_split)
setup_matrix = model.array(setup_matrix)
# Decision variables: to each aircraft, we associate an interval of de-icing
# and a number of trucks.
# A aircraft cannot be de-iced before its arrival
aircraft_intervals = model.array( \
[model.interval(arrival_date[aircraft], H) for aircraft in range(nb_aircrafts)])
aircraft_nb_trucks = model.array( \
[model.int(min_trucks[size_aircraft[i]], max_trucks[size_aircraft[i]]) \
for i in range(nb_aircrafts)])
# Decision variables: to each gate, we associate the list of
# the aircrafts that will be de-iced there.
gates = model.array([model.list(nb_aircrafts) for _ in range(nb_gates)])
# Constraint: every aircraft has a unique gate
model.constraint(model.partition(gates))
# Constraint non-overlap: one aircraft per gate for each time, and a setup time
# between two aircrafts on the same gate
for i in range(nb_gates):
gate = gates[i]
constraint_overlapping = model.lambda_function(
lambda i : model.end(aircraft_intervals[gate[i]]) + setup_matrix[gate[i]][gate[i+1]] \
<= model.start(aircraft_intervals[gate[i+1]]))
model.constraint(model.and_( \
model.range(0, model.count(gate)-1), constraint_overlapping))
# Constraint: duration of the de-icing
for aircraft in range(nb_aircrafts):
constraint_duration = model.length(aircraft_intervals[aircraft]) \
== duration[aircraft_nb_trucks[aircraft]-1][aircraft]
model.constraint(constraint_duration)
# Makespan: time of the end of all the de-icing tasks
makespan = model.max([model.end(aircraft_intervals[aircraft]) \
for aircraft in range(nb_aircrafts)])
# Constraint: no more than nb_trucks in total
constraint_capacity = model.lambda_function(
lambda time : model.sum(model.contains(aircraft_intervals[aircraft], time) \
* aircraft_nb_trucks[aircraft] for aircraft in range(0, nb_aircrafts)) \
<= nb_trucks
)
model.constraint(model.and_(model.range(0, makespan), constraint_capacity))
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:
# -the total makespan
# - for each aircraft, the bay, the starting date of the interval,
# the ending date of the interval, the number of trucks attributed
if output_file != None:
with open(output_file, "w") as f:
print("Solution written in file ", output_file)
f.write(str(makespan.value))
f.write("\n")
for b in range(nb_gates):
gate = gates.value[b]
for aircraft in gate:
f.write(str(b) + " " \
+ str(aircraft_intervals.value[aircraft].start()) + " " \
+ str(aircraft_intervals.value[aircraft].end()) + " " \
+ str(aircraft_nb_trucks.value[aircraft]))
f.write("\n")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python aircraft_deicing.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 aircraft_deicing.cpp -I%HX_HOME%\include /link %HX_HOME%\bin\hexaly140.libaircraft_deicing instances/60_aircrafts.txt
- Compilation / Execution (Linux)
-
g++ deicing_planes.cpp -I/opt/hexaly_14_0/include -lhexaly140 -lpthread -o deicing_planes./deicing_planes instances/60_aircrafts.txt
#include "optimizer/hexalyoptimizer.h"
#include <fstream>
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <cmath>
using namespace hexaly;
using namespace std;
class AircraftDeicing {
private:
// Number of gates
int nbGates;
// Number of aircrafts
int nbAircrafts;
// Size of the aircrafts
vector<int> aircraftsSize;
// Arrival date of the aircrafts
vector<int> arrivalDates;
// Number of trucks
int nbTrucks;
// Number of trucks possible per aircraft
vector<int> minTrucks;
vector<int> maxTrucks;
int maxGlobalTrucks;
// Durations normalised by the truck number of the aircraft
vector<vector<int>> durationsData;
// Setup matrix: the time needed between two aircrafts to clean the gate
vector<vector<int>> setupMatrixData;
// Horizon: trivial upper bound for the end times of the tasks
int H;
// Hexaly Optimizer
HexalyOptimizer optimizer;
// Decision variables: to each aircraft, we associate a de-icing interval and a number of trucks
vector<HxExpression> aircraftsIntervals;
vector<HxExpression> aircraftsNbTrucks;
// To each gate, we associate the list of the aircrafts that will be de-iced there
vector<HxExpression> gates;
// Objective = minimize the makespan: end of the de-icing of the last aircraft
HxExpression makespan;
public:
void readInstance(const string& inFileName);
void solve(int timeLimit);
void writeSolution(const string& fileName);
};
void AircraftDeicing::readInstance (const string& inFileName){
ifstream infile;
infile.exceptions(ifstream::failbit | ifstream::badbit);
infile.open(inFileName.c_str());
infile >> nbGates;
infile >> nbAircrafts;
int value;
for (int i = 0; i < nbAircrafts; ++i) {
infile >> value;
aircraftsSize.push_back(value);
}
for (int i = 0; i < nbAircrafts; ++i) {
infile >> value;
arrivalDates.push_back(value);
}
infile >> nbTrucks;
for (int i = 0; i < nbAircrafts; ++i) {
infile >> value;
minTrucks.push_back(value);
}
for (int i = 0; i < nbAircrafts; ++i) {
infile >> value;
maxTrucks.push_back(value);
}
maxGlobalTrucks = *max_element(maxTrucks.begin(), maxTrucks.end());
for (unsigned int m=0; m<maxGlobalTrucks; m++) {
durationsData.push_back(vector<int> {});
for (unsigned int i = 0; i < nbAircrafts; i++) {
infile >> value;
durationsData[m].push_back(value);
}
}
H = *max_element(arrivalDates.begin(), arrivalDates.end())
+ accumulate(durationsData[0].begin(), durationsData[0].end(), 0);
for (int i = 0; i < nbAircrafts; ++i) {
setupMatrixData.push_back(vector<int> {});
for (int j = 0; j < nbAircrafts; ++j) {
infile >> value;
setupMatrixData[i].push_back(value);
}
}
infile.close();
}
void AircraftDeicing::solve(int timeLimit) {
// Declare the optimization model
HxModel model = optimizer.getModel();
// Decision Variable: time range for each aircraft
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft) {
aircraftsIntervals.push_back(model.intervalVar(arrivalDates[aircraft], H));
}
HxExpression aircraftsIntervalsArray
= model.array(aircraftsIntervals.begin(), aircraftsIntervals.end());
// Decision Variable: number of trucks used for each aircraft
for (int aircraft = 0; aircraft < nbAircrafts; aircraft++) {
aircraftsNbTrucks.push_back(
model.intVar(minTrucks[aircraftsSize[aircraft]],
maxTrucks[aircraftsSize[aircraft]]));
}
HxExpression aircraftsNbTrucksArray
= model.array(aircraftsNbTrucks.begin(), aircraftsNbTrucks.end());
// Decision Variable: attribution of a gate to each aircraft
for (int i = 0; i < nbGates; ++i) gates.push_back(model.listVar(nbAircrafts));
HxExpression gatesArray = model.array(gates.begin(), gates.end());
// Makespan: end of the de-icing of the last aircraft
makespan = model.max();
for (int i = 0; i < nbAircrafts; ++i) makespan.addOperand(model.end(aircraftsIntervals[i]));
// Conversion of durations from vector<vector<int>> to HxExpression (array)
// to be able to access it with "at" operators
HxExpression durations = model.array();
for (int m = 0; m < maxGlobalTrucks; ++m) {
durations.addOperand(
model.array(durationsData[m].begin(), durationsData[m].end())
);
}
// Conversion of setupMatrix from vector<vector<int>> to HxExpression (array)
// to be able to access it with "at" operators
HxExpression setupMatrix = model.array();
for (int i = 0; i < nbAircrafts; ++i) {
setupMatrix.addOperand(
model.array(setupMatrixData[i].begin(), setupMatrixData[i].end())
);
}
// Constraint: every aircraft has a unique gate
model.constraint(model.partition(gates.begin(), gates.end()));
// Constraint: duration of the de-icing
for (int aircraft = 0; aircraft < nbAircrafts; aircraft++) {
HxExpression constraintDuration =
model.length(aircraftsIntervals[aircraft])
== durations[aircraftsNbTrucks[aircraft]-1][aircraft];
model.constraint(constraintDuration);
}
// Constraint no-overlapping: one aircraft per gate for each time, and a setup time
// between two aircrafts on the same gate
for (int j = 0; j < nbGates; j++) {
HxExpression gate = gates[j];
HxExpression constraintOverlapping = model.lambdaFunction([&](HxExpression i) {
return (model.end(aircraftsIntervalsArray[gate[i]])
+ setupMatrix[gate[i]][gate[i+1]]
<= model.start(aircraftsIntervalsArray[gate[i+1]]));
}
);
model.constraint(model.and_(model.range(0, model.count(gate)-1), constraintOverlapping));
}
// Constraint: trucks capacity
HxExpression respectCapacity = model.lambdaFunction([&](HxExpression time) {
HxExpression currNbTrucks = model.sum();;
for (int i = 0; i < nbAircrafts; i++){
currNbTrucks.addOperand(model.contains(aircraftsIntervalsArray[i], time) * aircraftsNbTrucksArray[i]);
}
return currNbTrucks <= nbTrucks;
});
model.constraint(model.and_(model.range(0, makespan), respectCapacity));
// 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:
* - the total makespan
* - for each aircraft, the gate, the starting date of the interval,
* the ending date of the interval,the number of trucks attributed */
void AircraftDeicing::writeSolution(const string& fileName) {
ofstream outfile;
outfile.exceptions(ofstream::failbit | ofstream::badbit);
outfile.open(fileName.c_str());
outfile << makespan.getValue() << endl;
for (int gate = 0; gate < nbGates; ++gate) {
HxCollection aircraftsCollection = gates[gate].getCollectionValue();
for (int i = 0; i < aircraftsCollection.count(); ++i) {
int aircraft = aircraftsCollection[i];
outfile << gate << "\t"
<< aircraftsIntervals[aircraft].getIntervalValue().getStart() << "\t"
<< aircraftsIntervals[aircraft].getIntervalValue().getEnd() <<"\t"
<< aircraftsNbTrucks[aircraft].getIntValue() << endl;
}
}
}
int main(int argc, char** argv) {
if (argc < 2) {
std::cout << "Usage: aircraft_deicing instanceFile [outputFile] [timeLimit]" << std::endl;
exit(1);
}
const char* instanceFile = argv[1];
string inFileName = string(instanceFile);
const char* outputFile = argc > 2 ? argv[2] : NULL;
const char* strTimeLimit = argc > 3 ? argv[3] : "60";
try {
AircraftDeicing model;
model.readInstance(inFileName);
int TimeLimit = atoi(strTimeLimit);
model.solve(TimeLimit);
if (outputFile != NULL) {
cout << "output file : " << outputFile << endl;
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 aircraft_deicing.cs /reference:Hexaly.NET.dllaircraft_deicing instances/60_aircrafts.txt
using System;
using System.IO;
using System.Linq;
using Hexaly.Optimizer;
using System.Collections.Generic;
public class AircraftDeicing : IDisposable
{
// Number of gates
private int nbGates;
// Number of aircrafts
private int nbAircrafts;
// Size of the aircrafts
private int[] aircraftsSize;
// Arrival date of the aircrafts
private int[] arrivalDates;
// Number of trucks
private int nbTrucks;
// Bounds of the number trucks authorized per aircraft
private int[] minTrucks;
private int[] maxTrucks;
// Durations normalised by the truck number of the aircraft
private int[,] durations;
// Setup Matrix: the time needed between two aircrafts to clean the bay
private int[,] setupMatrix;
// Horizon: trivial upper bound for the end times of the tasks
private int H;
// Hexaly Optimizer
private HexalyOptimizer optimizer;
// Decision variables: to each aircraft, we associate an interval of de-icing
private HxExpression[] aircraftsIntervals;
// Decision variable: to each aircraft, we associate a number of trucks
private HxExpression[] aircraftsNbTrucks;
// Decision variables: to each gate, we associate the
// list of the aircrafts that will be de-iced there
private HxExpression[] gates;
// Objective = minimize the makespan: end of the de-icing of the last aircraft
private HxExpression makespan;
public AircraftDeicing()
{
optimizer = new HexalyOptimizer();
}
public void ReadInstance(string fileName)
{
using (StreamReader input = new StreamReader(fileName))
{
nbGates = int.Parse(input.ReadLine());
nbAircrafts = int.Parse(input.ReadLine());
string[] line_sp = input.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);
aircraftsSize = Array.ConvertAll(line_sp, int.Parse);
line_sp = input.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);
arrivalDates = Array.ConvertAll(line_sp, int.Parse);
nbTrucks = int.Parse(input.ReadLine());
line_sp = input.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);
minTrucks = Array.ConvertAll(line_sp, int.Parse);
line_sp = input.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);
maxTrucks = Array.ConvertAll(line_sp, int.Parse);
int maxGlobalTrucks = maxTrucks.Max();
durations = new int[maxGlobalTrucks, nbAircrafts];
for (int i=0; i<maxGlobalTrucks; ++i)
{
line_sp = input.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);
int[] lineDuration = Array.ConvertAll(line_sp, int.Parse);
for (int j = 0; j < nbAircrafts; ++j) durations[i, j] = lineDuration[j];
}
int baseLineDurationSum = 0;
for (int i = 0; i < nbAircrafts; ++i) baseLineDurationSum += durations[0, i];
H = arrivalDates.Max() + baseLineDurationSum;
List<int[]> setupMatrixTemp = new List<int[]>();
string line;
while ((line = input.ReadLine()) != null)
{
string[] valuesStr = line.Split(new char[] { ' ', '\t' },
StringSplitOptions.RemoveEmptyEntries);
int[] values = Array.ConvertAll(valuesStr, int.Parse);
setupMatrixTemp.Add(values);
}
int nbLines = setupMatrixTemp.Count;
setupMatrix = new int[nbLines, nbLines];
for (int i = 0; i < nbLines; ++i)
{
for (int j = 0; j < nbLines; ++j) setupMatrix[i, j] = setupMatrixTemp[i][j];
}
}
}
public void Dispose()
{
optimizer.Dispose();
}
public void Solve(int timeLimit)
{
// Declare the optimization model
HxModel model = optimizer.GetModel();
// Decision Variable: time range for each aircraft
aircraftsIntervals = new HxExpression[nbAircrafts];
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft)
{
aircraftsIntervals[aircraft] = model.Interval(arrivalDates[aircraft], H);
}
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression aircraftsIntervalsArray = model.Array(aircraftsIntervals);
// Decision Variable: number of trucks used for each aircraft
aircraftsNbTrucks = new HxExpression[nbAircrafts];
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft)
{
aircraftsNbTrucks[aircraft] = model.Int(minTrucks[aircraftsSize[aircraft]],
maxTrucks[aircraftsSize[aircraft]]);
}
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression aircraftsNbTrucksArray = model.Array(aircraftsNbTrucks);
// Decision Variable: number of trucks used for each aircraft
gates = new HxExpression[nbGates];
for (int i = 0; i < nbGates; ++i) gates[i] = model.List(nbAircrafts);
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression gatesArray = model.Array(gates);
// Makespan: end of the de-icing of the last aircraft
makespan = model.Max();
for (int i = 0; i < nbAircrafts; ++i) makespan.AddOperand(model.End(aircraftsIntervals[i]));
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression setupMatrixArray = model.Array(setupMatrix);
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression durationsArray = model.Array(durations);
// Constraint: every aircraft has a unique gate
model.Constraint(model.Partition(gatesArray));
// Constraint: duration of the de-icing
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft)
{
HxExpression constraintDuration = (model.Length(aircraftsIntervals[aircraft])
== durationsArray[aircraftsNbTrucks[aircraft]-1][aircraft]);
model.Constraint(constraintDuration);
}
// Constraint no-overlapping: one aircraft per gate for each time, and a setup time
// between two aircrafts on the same gate
for (int j = 0; j < nbGates; ++j)
{
HxExpression gate = gates[j];
HxExpression constraintOverlapping = model.LambdaFunction(i =>
model.End(aircraftsIntervalsArray[gate[i]])
+ setupMatrixArray[gate[i]][gate[i+1]]
<= model.Start(aircraftsIntervalsArray[gate[i+1]])
);
model.Constraint(model.And(model.Range(0, model.Count(gate)-1), constraintOverlapping));
}
// Constraint: trucks capacity
HxExpression respectCapacity = model.LambdaFunction(time =>
{
HxExpression currNbTrucks = model.Sum();
for (int i = 0; i< nbAircrafts; i++)
{
currNbTrucks.AddOperand(model.Contains(aircraftsIntervalsArray[i], time)
* aircraftsNbTrucksArray[i]
);
}
return currNbTrucks <= nbTrucks;
});
model.Constraint(model.And(model.Range(0, makespan), respectCapacity));
// 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:
* - the total makespan
* - for each aircraft, the bay, the starting date of the interval,
the ending date of the interval, the number of trucks attributed */
public void WriteSolution(string fileName)
{
using (StreamWriter output = new StreamWriter(fileName))
{
Console.WriteLine("Solution written in file " + fileName);
output.Write(makespan.GetValue);
output.WriteLine();
for (int gate = 0; gate < nbGates; ++gate)
{
HxCollection aircraftsCollection = gates[gate].GetCollectionValue();
for (int i = 0; i < aircraftsCollection.Count(); ++i)
{
long aircraft = aircraftsCollection[i];
output.Write(gate + "\t"
+ aircraftsIntervals[aircraft].GetIntervalValue().Start() + "\t"
+ aircraftsIntervals[aircraft].GetIntervalValue().End() + "\t"
+ aircraftsNbTrucks[aircraft].GetIntValue());
output.WriteLine();
}
}
}
}
public static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage: AircraftDeicing 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 (AircraftDeicing model = new AircraftDeicing())
{
model.ReadInstance(instanceFile);
model.Solve(int.Parse(strTimeLimit));
if (outputFile != null) model.WriteSolution(outputFile);
}
}
}
- Compilation / Execution (Windows)
-
javac AircraftDeicing.java -cp %HX_HOME%\bin\hexaly.jarjava -cp %HX_HOME%\bin\hexaly.jar;. AircraftDeicing instances/60_aircrafts.txt
- Compilation / Execution (Linux)
-
javac AircraftDeicing.java -cp /opt/hexaly_14_0/bin/hexaly.jarjava -cp /opt/hexaly_14_0/bin/hexaly.jar:. AircraftDeicing instances/60_aircrafts.txt
import java.util.*;
import java.io.*;
import com.hexaly.optimizer.*;
public class AircraftDeicing {
// Number of gates
private int nbGates;
// Number of aircrafts
private int nbAircrafts;
// Size of the aircrafts
private int[] aircraftsSize;
// Arrival date of the aircrafts
private int[] arrivalDates;
// Number of trucks
private int nbTrucks;
// Number of trucks possible per aircraft
private int[] minTrucks;
private int[] maxTrucks;
// Durations normaised by the truck number of the aircraft
private int[][] durations;
// Setup Matirx: the time needed between two aircrafts to clean the bay
private int[][] setupMatrix;
// Horizon: trivial upper bound for the end times of the tasks
private int H;
// Hexaly Optimizer
final HexalyOptimizer optimizer;
// Decision variables: to each aircraft, we associate an interval of de-icing
private HxExpression[] aircraftsIntervals;
// Decision variable: to each aircraft, we associate a number of trucks
private HxExpression[] aircraftsNbTrucks;
// Decision variables: to each gate, we associate the list of aircrafts to be de-iced there
private HxExpression[] gates;
// Objective = minimize the makespan: end of the de-icing of the last aircraft
private HxExpression makespan;
public AircraftDeicing(HexalyOptimizer optimizer) throws IOException {
this.optimizer = optimizer;
}
public static int maxInt(int[] v) {
return Arrays.stream(v).max().getAsInt();
}
public void readInstance(String fileName) throws IOException {
try (Scanner input = new Scanner(new File(fileName))) {
input.useLocale(Locale.ROOT);
nbGates = input.nextInt();
nbAircrafts = input.nextInt();
aircraftsSize = new int[nbAircrafts];
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft) {
aircraftsSize[aircraft] = input.nextInt();
}
arrivalDates = new int[nbAircrafts];
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft) {
arrivalDates[aircraft] = input.nextInt();
}
nbTrucks = input.nextInt();
minTrucks = new int[nbAircrafts];
for (int i = 0; i < nbAircrafts; ++i) minTrucks[i] = input.nextInt();
maxTrucks = new int[nbAircrafts];
for (int i = 0; i < nbAircrafts; ++i) maxTrucks[i] = input.nextInt();
int maxGlobalTrucks = maxInt(maxTrucks);
durations = new int[maxGlobalTrucks][nbAircrafts];
for (int m = 0; m < maxGlobalTrucks; ++m) {
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft) {
durations[m][aircraft] = input.nextInt();
}
}
H = maxInt(arrivalDates) + Arrays.stream(durations[0]).sum();
setupMatrix = new int[nbAircrafts][nbAircrafts];
for (int i = 0; i < nbAircrafts; ++i) {
int[] col = new int[nbAircrafts];
for (int j = 0; j < nbAircrafts; ++j) col[j] = (input.nextInt());
setupMatrix[i] = col;
}
}
}
public void solve(int timeLimit) {
// Declare the optimization model
HxModel model = optimizer.getModel();
// Decision Variable: time range for each aircraft
aircraftsIntervals = new HxExpression[nbAircrafts];
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft) {
aircraftsIntervals[aircraft] = model.intervalVar(arrivalDates[aircraft], H);
}
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression aircraftsIntervalsArray = model.array(aircraftsIntervals);
// Decision Variable: number of trucks used for each aircraft
aircraftsNbTrucks = new HxExpression[nbAircrafts];
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft) {
aircraftsNbTrucks[aircraft] = model.intVar(
minTrucks[aircraftsSize[aircraft]], maxTrucks[aircraftsSize[aircraft]]);
}
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression aircraftsNbTrucksArray = model.array(aircraftsNbTrucks);
// Decision Variable: number of trucks used for each aircraft
gates = new HxExpression[nbGates];
for (int i = 0; i < nbGates; ++i) gates[i] = model.listVar(nbAircrafts);
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression gatesArray = model.array(gates);
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression setupMatrixArray = model.array(setupMatrix);
// Create a HexalyOptimizer array in order to be able to access it with "at" operators
HxExpression durationsArray = model.array(durations);
// Makespan: end of the de-icing of the last aircraft
makespan = model.max();
for (int i = 0; i < nbAircrafts; ++i) makespan.addOperand(model.end(aircraftsIntervals[i]));
// Constraint: every aircraft has a unique gate
model.constraint(model.partition(gatesArray));
// Constraint: duration of the de-icing
for (int aircraft = 0; aircraft < nbAircrafts; ++aircraft) {
HxExpression constraintDuration = model.eq(model.length(aircraftsIntervals[aircraft]),
model.at(model.at(durationsArray, model.sum(aircraftsNbTrucks[aircraft], -1)), aircraft));
model.constraint(constraintDuration);
}
// Constraint no-overlapping: one aircraft per gate for each time, and a setup time
// between two aircrafts on the same gate
for (int j = 0; j < nbGates; ++j) {
HxExpression gate = gates[j];
HxExpression constraintOverlapping = model.lambdaFunction(i -> {
HxExpression nextAvailability = model.sum();
nextAvailability.addOperand(
model.end(model.at(aircraftsIntervalsArray, model.at(gate,i))));
nextAvailability.addOperand(
model.at(setupMatrixArray, model.at(gate, i),
model.at(gate, model.sum(i, 1))));
return model.leq(nextAvailability, model.start(
model.at(aircraftsIntervalsArray, model.at(gate, model.sum(i, 1)))));
});
model.constraint(model.and(
model.range(0, model.sum(model.count(gate), -1)), constraintOverlapping));
}
// Constraint: trucks capacity
HxExpression respectCapacity = model.lambdaFunction(time -> {
HxExpression currNbTrucks = model.sum();
for (int i = 0; i < nbAircrafts; i++) {
currNbTrucks.addOperand(model.prod(model.contains(model.at(aircraftsIntervalsArray, i), time),
model.at(aircraftsNbTrucksArray, i)));
}
return model.leq(currNbTrucks, nbTrucks);
}
);
model.constraint(model.and(model.range(0, makespan), respectCapacity));
// Minimization of 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:
* - the total makespan
* - for each aircraft, the bay, the starting date of the interval,
* the ending date of the intrval, the number of trucks attributed */
public void writeSolution(String fileName) throws IOException {
try (PrintWriter output = new PrintWriter(fileName)) {
System.out.println("Solution written in file " + fileName);
output.write((int)makespan.getValue());
output.write("\n");
for (int gate = 0; gate < nbGates; ++gate) {
HxCollection aircraftsCollection = gates[gate].getCollectionValue();
for (int i = 0; i < aircraftsCollection.count(); ++i) {
int aircraft = (int)aircraftsCollection.get(i);
output.write(gate + "\t"
+ aircraftsIntervals[aircraft].getIntervalValue().getStart() + "\t"
+ aircraftsIntervals[aircraft].getIntervalValue().getEnd() + "\t"
+ aircraftsNbTrucks[aircraft].getIntValue());
output.write("\n");
}
}
}
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: AircraftDeicing 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()) {
AircraftDeicing model = new AircraftDeicing(optimizer);
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);
}
}
}