Steel Mill Slab Design¶
Principles learned¶
Define the actual set of decision variables
Preprocess the input to create useful data for the model
Access an array with an “at” operator at an index that is an expression
Problem¶
Steel is produced by casting molten iron into slabs. A steel mill can produce a finite number of slab sizes. An order has two properties, a color corresponding to the route required through the steel mill and a weight.
Given input orders, the problem is to assign the orders to slabs, the number and size of which are also to be determined, such that the total weight of steel produced is minimised. In other words we want to minimize the waste of steel (steel produced in addition to the actual sizes of orders). This assignment is subject to two further constraints:
Capacity constraints: The total weight of orders assigned to a slab cannot exceed the slab capacity.
Color constraints: Each slab can contain at most p of k total colors (p is usually 2). The color constraints arise because it is expensive to cut up slabs in order to send them to different parts of the mill.
For more details, see CSPLib and beCool.
Download the exampleData¶
The format of the data is as follows:
1st line: number of slab sizes, then the sequence of sizes
2nd line: number of colors
3rd line: number of orders
Then for each order: size and color of the order
Program¶
The model is based on the assignment of orders to slabs. An important point is
that we associate no decision variable to the selected size for a slab. Instead,
the wasted steel in each slab is automatically determined by its content. The
waste is zero when the content fits exactly in one of the possible slab sizes
and increases linearly otherwise. We define an array of values
wasteForContent
representing this saw-tooth profile, which allows a very
simple writing of this non-linear relationship: wastedSteel[j] <-
wasteForContent[slabContent[j]]
. It is pre-computed after the instance data
has been read. The slab size being determined by the content, we must still
ensure by a constraint that the maximum slab size is not exceeded.
It is a typical example of the importance of defining the right set of decision variables as explained in our quick start guide.
- Execution:
- hexaly steel_mill_slab_design.hxm inFileName=instances/12orderproblem.in [hxTimeLimit=] [solFileName=]
use io;
/* Read instance data */
function input() {
local usage = "Usage: hexaly steel_mill_slab_design.hxm "
+ "inFileName=inputFile [solFileName=outputFile] [hxTimeLimit=timeLimit]";
if (inFileName == nil) throw usage;
local inFile = io.openRead(inFileName);
nbColorsMaxSlab = 2;
nbSlabSizes = inFile.readInt();
slabSizes[1..nbSlabSizes] = inFile.readInt();
maxSize = slabSizes[nbSlabSizes];
nbColors = inFile.readInt();
nbOrders = inFile.readInt();
nbSlabs = nbOrders;
sumSizeOrders = 0;
// List of quantities and colors for each order
for [i in 0...nbOrders] {
quantities[i] = inFile.readInt();
colors[i] = inFile.readInt();
sumSizeOrders += quantities[i];
}
preComputeWasteForContent();
}
// Compute the vector wasteForContent
function preComputeWasteForContent() {
// No waste when a slab is empty
wasteForContent[0] = 0;
// The waste for each content is the difference between the minimum slab size
// able to contain this content and the content
prevSize = 0;
for [size in slabSizes] {
if (size < prevSize) throw "Slab sizes should be sorted in ascending order";
wasteForContent[content in prevSize + 1..size] = size - content;
prevSize = size;
}
wasteForContent[prevSize+1..sumSizeOrders] = 0;
}
/* Declare the optimization model */
function model() {
// Set decisions: slab[k] represents the orders in slab k
slabs[0...nbSlabs] <- set(nbOrders);
// Each order must be in one slab and one slab only
constraint partition[s in 0...nbSlabs](slabs[s]);
for [s in 0...nbSlabs] {
local orders <- slabs[s];
// The number of colors per slab must not exceed a specified value
constraint count(distinct(orders, o => colors[o])) <= nbColorsMaxSlab;
// The content of each slab must not exceed the maximum size of the slab
slabContent[s] <- sum(orders, o => quantities[o]);
constraint slabContent[s] <= maxSize;
}
// Wasted steel is computed according to the content of the slab
wastedSteel[s in 0...nbSlabs] <- wasteForContent[slabContent[s]];
// Minimize the total wasted steel
totalWastedSteel <- sum[s in 0...nbSlabs](wastedSteel[s]);
minimize totalWastedSteel;
}
/* Parametrize the solver */
function param() {
if (hxTimeLimit == nil) hxTimeLimit = 60;
}
/* Write the solution in a file with the following format:
* - total wasted steel
* - number of slabs used
* - for each slab used, the number of orders in the slab and the list of orders */
function output() {
if (solFileName == nil) return;
local solFile = io.openWrite(solFileName);
solFile.println(totalWastedSteel.value);
actualNbSlabs = 0;
for [s in 0...nbSlabs] {
if (slabs[s].value.count() > 0) actualNbSlabs += 1;
}
solFile.println(actualNbSlabs);
for [s in 0...nbOrders] {
nbOrdersInSlab = slabs[s].value.count();
if (nbOrdersInSlab == 0) continue;
solFile.print(nbOrdersInSlab);
for [o in slabs[s].value] solFile.print(" ", o + 1);
solFile.println();
}
}
- Execution (Windows)
- set PYTHONPATH=%HX_HOME%\bin\pythonpython steel_mill_slab_design.py instances\12orderproblem.in
- Execution (Linux)
- export PYTHONPATH=/opt/hexaly_13_5/bin/pythonpython steel_mill_slab_design.py instances/12orderproblem.in
import hexaly.optimizer
import sys
if len(sys.argv) < 2:
print("Usage: python steel_mill_slab_design.py inputFile [outputFile] [timeLimit]")
sys.exit(1)
def read_integers(filename):
with open(filename) as f:
return [int(elem) for elem in f.read().split()]
# Compute the vector waste_for_content
def pre_compute_waste_for_content(slab_sizes, sum_size_orders):
# No waste when a slab is empty
waste_for_content = [0] * sum_size_orders
prev_size = 0
for size in slab_sizes:
if size < prev_size:
print("Slab sizes should be sorted in ascending order")
sys.exit(1)
for content in range(prev_size + 1, size):
waste_for_content[content] = size - content
prev_size = size
return waste_for_content
with hexaly.optimizer.HexalyOptimizer() as optimizer:
#
# Read instance data
#
nb_colors_max_slab = 2
file_it = iter(read_integers(sys.argv[1]))
nb_slab_sizes = next(file_it)
slab_sizes = [next(file_it) for i in range(nb_slab_sizes)]
max_size = slab_sizes[nb_slab_sizes - 1]
nb_colors = next(file_it)
nb_orders = next(file_it)
nb_slabs = nb_orders
sum_size_orders = 0
# List of quantities and colors for each order
quantities_data = []
colors_data = []
for o in range(nb_orders):
quantities_data.append(next(file_it))
colors_data.append(next(file_it))
sum_size_orders += quantities_data[o]
waste_for_content = pre_compute_waste_for_content(slab_sizes, sum_size_orders)
#
# Declare the optimization model
#
model = optimizer.model
# Create array and function to retrieve the orders's colors and quantities
colors = model.array(colors_data)
color_lambda = model.lambda_function(lambda l: colors[l])
quantities = model.array(quantities_data)
quantity_lambda = model.lambda_function(lambda o: quantities[o])
# Set decisions: slab[k] represents the orders in slab k
slabs = [model.set(nb_orders) for s in range(nb_slabs)]
# Each order must be in one slab and one slab only
model.constraint(model.partition(slabs))
slabContent = []
for s in range(nb_slabs):
# The number of colors per slab must not exceed a specified value
model.constraint(model.count(model.distinct(slabs[s], color_lambda)) <= nb_colors_max_slab)
# The content of each slab must not exceed the maximum size of the slab
slabContent.append(model.sum(slabs[s], quantity_lambda))
model.constraint(slabContent[s] <= max_size)
waste_for_content_array = model.array(waste_for_content)
# Wasted steel is computed according to the content of the slab
wasted_steel = [waste_for_content_array[slabContent[s]] for s in range(nb_slabs)]
# Minimize the total wasted steel
total_wasted_steel = model.sum(wasted_steel)
model.minimize(total_wasted_steel)
model.close()
# Parameterize the optimizer
if len(sys.argv) >= 4:
optimizer.param.time_limit = int(sys.argv[3])
else:
optimizer.param.time_limit = 60
optimizer.solve()
#
# Write the solution in a file with the following format:
# - total wasted steel
# - number of slabs used
# - for each slab used, the number of orders in the slab and the list of orders
#
if len(sys.argv) >= 3:
with open(sys.argv[2], 'w') as f:
f.write("%d\n" % total_wasted_steel.value)
actual_nb_slabs = 0
for s in range(nb_slabs):
if slabs[s].value.count() > 0:
actual_nb_slabs += 1
f.write("%d\n" % actual_nb_slabs)
for s in range(nb_slabs):
nb_orders_in_slab = slabs[s].value.count()
if nb_orders_in_slab == 0:
continue
f.write("%d" % nb_orders_in_slab)
for o in slabs[s].value:
f.write(" %d" % (o + 1))
f.write("\n")
- Compilation / Execution (Windows)
- cl /EHsc steel_mill_slab_design.cpp -I%HX_HOME%\include /link %HX_HOME%\bin\hexaly135.libsteel_mill_slab_design instances\12orderproblem.in
- Compilation / Execution (Linux)
- g++ steel_mill_slab_design.cpp -I/opt/hexaly_13_5/include -lhexaly135 -lpthread -o steel_mill_slab_design./steel_mill_slab_design instances/12orderproblem.in
#include "optimizer/hexalyoptimizer.h"
#include <fstream>
#include <iostream>
#include <vector>
using namespace hexaly;
using namespace std;
class SteelMillSlabDesign {
public:
// Number of available slabs
int nbSlabs;
// Number of orders
int nbOrders;
// Number of colors
int nbColors;
// Maximum number of colors per slab
int nbColorsMaxSlab;
// Maximum size of a slab
int maxSize;
// List of colors for each order
vector<int> colors;
// List of quantities for each order
vector<int> quantities;
// Steel waste computed for each content value
vector<int> wasteForContent;
// Hexaly Optimizer
HexalyOptimizer optimizer;
// Hexaly Program variables
vector<HxExpression> slabs;
// Objective
HxExpression totalWastedSteel;
/* Read instance data */
void readInstance(const string& fileName) {
ifstream infile;
infile.exceptions(ifstream::failbit | ifstream::badbit);
infile.open(fileName.c_str());
nbColorsMaxSlab = 2;
int nbSlabSizes;
infile >> nbSlabSizes;
vector<int> slabSizes(nbSlabSizes);
for (int i = 0; i < nbSlabSizes; ++i) {
infile >> slabSizes[i];
}
maxSize = slabSizes[nbSlabSizes - 1];
infile >> nbColors;
infile >> nbOrders;
nbSlabs = nbOrders;
quantities.resize(nbOrders);
colors.resize(nbOrders);
int sumSizeOrders = 0;
for (int o = 0; o < nbOrders; ++o) {
infile >> quantities[o];
// Note: colors are in 1..nbColors
infile >> colors[o];
sumSizeOrders += quantities[o];
}
preComputeWasteForContent(slabSizes, sumSizeOrders);
}
private:
// Compute the vector wasteForContent
void preComputeWasteForContent(const vector<int>& slabSizes, int sumSizeOrders) {
// No waste when a slab is empty
wasteForContent.resize(sumSizeOrders, (int)0);
int prevSize = 0;
for (size_t i = 0; i < slabSizes.size(); ++i) {
int size = slabSizes[i];
if (size < prevSize) {
cerr << "Slab sizes should be sorted in ascending order" << endl;
exit(1);
}
for (int content = prevSize + 1; content < size; ++content) {
wasteForContent[content] = (int)(size - content);
}
prevSize = size;
}
}
public:
void solve(int limit) {
// Declare the optimization model
HxModel model = optimizer.getModel();
// Create a HexalyOptimizer array and a function to retrieve the orders's colors and quantities
HxExpression colorsArray = model.array(colors.begin(), colors.end());
HxExpression colorLambda = model.createLambdaFunction([&](HxExpression i) { return colorsArray[i]; });
HxExpression quantitiesArray = model.array(quantities.begin(), quantities.end());
HxExpression quantitiesLambda = model.createLambdaFunction([&](HxExpression i) { return quantitiesArray[i]; });
vector<HxExpression> slabContent(nbSlabs);
vector<HxExpression> wastedSteel(nbSlabs);
// Create a HexalyOptimizer array to be able to access it with "at" operators
HxExpression wasteForContentArray = model.array(wasteForContent.begin(), wasteForContent.end());
// Set decisions: slab[k] represents the orders in slab k
slabs.resize(nbSlabs);
for (int s = 0; s < nbSlabs; ++s) {
slabs[s] = model.setVar(nbOrders);
}
// Each order must be in one slab and one slab only
model.constraint(model.partition(slabs.begin(), slabs.end()));
for (int s = 0; s < nbSlabs; ++s) {
HxExpression orders = slabs[s];
// The number of colors per slab must not exceed a specified value
model.constraint(model.count(model.distinct(orders, colorLambda)) <= nbColorsMaxSlab);
// The content of each slab must not exceed the maximum size of the slab
slabContent[s] = model.sum(orders, quantitiesLambda);
model.constraint(slabContent[s] <= maxSize);
// Wasted steel is computed according to the content of the slab
wastedSteel[s] = wasteForContentArray[slabContent[s]];
}
// Minimize the total wasted steel
totalWastedSteel = model.sum(wastedSteel.begin(), wastedSteel.end());
model.minimize(totalWastedSteel);
model.close();
// Parametrize the optimizer
optimizer.getParam().setTimeLimit(limit);
optimizer.solve();
}
/* Write the solution in a file with the following format:
* - total wasted steel
* - number of slabs used
* - for each slab used, the number of orders in the slab and the list of orders */
void writeSolution(const string& fileName) {
ofstream outfile;
outfile.exceptions(ofstream::failbit | ofstream::badbit);
outfile.open(fileName.c_str());
outfile << totalWastedSteel.getValue() << endl;
int actualNbSlabs = 0;
for(int s = 0; s < nbSlabs; ++s) {
if (slabs[s].getCollectionValue().count() > 0) actualNbSlabs++;
}
outfile << actualNbSlabs << endl;
for (int s = 0; s < nbSlabs; ++s) {
HxCollection slabCollection = slabs[s].getCollectionValue();
int nbOrdersInSlab = slabCollection.count();
if (nbOrdersInSlab == 0) continue;
outfile << nbOrdersInSlab;
for(int o = 0; o < nbOrdersInSlab; ++o) {
outfile << " " << slabCollection.get(o) + 1;
}
outfile << endl;
}
}
};
int main(int argc, char** argv) {
if (argc < 2) {
cerr << "Usage: steel_mill_slab_design inputFile [outputFile] [timeLimit]" << endl;
return 1;
}
const char* instanceFile = argv[1];
const char* outputFile = argc >= 3 ? argv[2] : NULL;
const char* strTimeLimit = argc >= 4 ? argv[3] : "60";
try {
SteelMillSlabDesign model;
model.readInstance(instanceFile);
model.solve(atoi(strTimeLimit));
if (outputFile != NULL)
model.writeSolution(outputFile);
return 0;
} catch (const exception& e) {
cerr << "An error occurred: " << e.what() << endl;
return 1;
}
}
- Compilation / Execution (Windows)
- copy %HX_HOME%\bin\Hexaly.NET.dll .csc SteelMillSlabDesign.cs /reference:Hexaly.NET.dllSteelMillSlabDesign instances\12orderproblem.in
using System;
using System.IO;
using System.Collections.Generic;
using Hexaly.Optimizer;
public class SteelMillSlabDesign : IDisposable
{
// Number of available slabs
int nbSlabs;
// Number of orders
int nbOrders;
// Number of colors
int nbColors;
// Maximum number of colors per slab
int nbColorsMaxSlab;
// Maximum size of a slab
int maxSize;
// List of colors for each order
int[] colorsData;
// List of quantities for each order
int[] quantitiesData;
// Steel waste computed for each content value
long[] wasteForContent;
// Hexaly Optimizer
HexalyOptimizer optimizer;
// Hexaly Program variables
HxExpression[] slabs;
// Objective
HxExpression totalWastedSteel;
public SteelMillSlabDesign()
{
optimizer = new HexalyOptimizer();
}
public void Dispose()
{
if (optimizer != null)
optimizer.Dispose();
}
/* Read instance data */
void ReadInstance(string fileName)
{
using (StreamReader input = new StreamReader(fileName))
{
nbColorsMaxSlab = 2;
string[] splitted = input.ReadLine().Split();
int nbSlabSizes = int.Parse(splitted[0]);
int[] slabSizes = new int[nbSlabSizes];
for (int i = 0; i < nbSlabSizes; ++i)
slabSizes[i] = int.Parse(splitted[i + 1]);
maxSize = slabSizes[nbSlabSizes - 1];
nbColors = int.Parse(input.ReadLine());
nbOrders = int.Parse(input.ReadLine());
nbSlabs = nbOrders;
int sumSizeOrders = 0;
quantitiesData = new int[nbOrders];
colorsData = new int[nbOrders];
for (int o = 0; o < nbOrders; ++o)
{
splitted = input.ReadLine().Split();
quantitiesData[o] = int.Parse(splitted[0]);
int c = int.Parse(splitted[1]);
// Note: colors are in 1..nbColors
colorsData[o] = c;
sumSizeOrders += quantitiesData[o];
}
PreComputeWasteForContent(slabSizes, sumSizeOrders);
}
}
// Compute the vector wasteForContent
private void PreComputeWasteForContent(int[] slabSizes, int sumSizeOrders)
{
// No waste when a slab is empty
wasteForContent = new long[sumSizeOrders];
int prevSize = 0;
for (int i = 0; i < slabSizes.Length; ++i)
{
int size = slabSizes[i];
if (size < prevSize)
throw new Exception("Slab sizes should be sorted in ascending order");
for (int content = prevSize + 1; content < size; ++content)
wasteForContent[content] = size - content;
prevSize = size;
}
}
void Solve(int limit)
{
// Declare the optimization model
HxModel model = optimizer.GetModel();
// Create a HexalyOptimizer array and a function to retrieve the orders's colors and quantities
HxExpression colors = model.Array(colorsData);
HxExpression colorsLambda = model.LambdaFunction(i => colors[i]);
HxExpression quantities = model.Array(quantitiesData);
HxExpression quantitiesLambda = model.LambdaFunction(i => quantities[i]);
HxExpression[] slabContent = new HxExpression[nbSlabs];
HxExpression[] wastedSteel = new HxExpression[nbSlabs];
// Create a HexalyOptimizer array to be able to access it with "at" operators
HxExpression wasteForContentArray = model.Array(wasteForContent);
// Set decisions: slabs[k] represents the orders in slab k
slabs = new HxExpression[nbSlabs];
for (int s = 0; s < nbSlabs; ++s)
{
slabs[s] = model.Set(nbOrders);
}
// Each order must be in one slab and one slab only
model.Constraint(model.Partition(slabs));
for (int s = 0; s < nbSlabs; ++s)
{
// The number of colors per slab must not exceed a specified value
model.Constraint(model.Count(model.Distinct(slabs[s], colorsLambda)) <=nbColorsMaxSlab);
// The content of each slab must not exceed the maximum size of the slab
slabContent[s] = model.Sum(slabs[s], quantitiesLambda);
model.Constraint(slabContent[s] <= maxSize);
// Wasted steel is computed according to the content of the slab
wastedSteel[s] = wasteForContentArray[slabContent[s]];
}
// Minimize the total wasted steel
totalWastedSteel = model.Sum(wastedSteel);
model.Minimize(totalWastedSteel);
model.Close();
// Parametrize the optimizer
optimizer.GetParam().SetTimeLimit(limit);
optimizer.Solve();
}
/* Write the solution in a file with the following format:
* - total wasted steel
* - number of slabs used
* - for each slab used, the number of orders in the slab and the list of orders */
void WriteSolution(string fileName)
{
using (StreamWriter output = new StreamWriter(fileName))
{
output.WriteLine(totalWastedSteel.GetValue());
int actualNbSlabs = 0;
for (int s = 0; s < nbSlabs; ++s)
{
if (slabs[s].GetCollectionValue().Count() > 0)
{
actualNbSlabs++;
}
}
output.WriteLine(actualNbSlabs);
for (int s = 0; s < nbSlabs; ++s)
{
HxCollection slabCollection = slabs[s].GetCollectionValue();
int nbOrdersInSlab = slabCollection.Count();
if (nbOrdersInSlab == 0) continue;
output.Write(nbOrdersInSlab);
for (int o = 0; o < nbOrdersInSlab; ++o)
{
output.Write(" " + (slabCollection.Get(o) + 1));
}
output.WriteLine();
}
}
}
public static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage: SteelMillSlabDesign inputFile [outputFile] [timeLimit]");
Environment.Exit(1);
}
string instanceFile = args[0];
string outputFile = args.Length > 1 ? args[1] : null;
string strTimeLimit = args.Length > 2 ? args[2] : "60";
using (SteelMillSlabDesign model = new SteelMillSlabDesign())
{
model.ReadInstance(instanceFile);
model.Solve(int.Parse(strTimeLimit));
if (outputFile != null)
model.WriteSolution(outputFile);
}
}
}
- Compilation / Execution (Windows)
- javac SteelMillSlabDesign.java -cp %HX_HOME%\bin\hexaly.jarjava -cp %HX_HOME%\bin\hexaly.jar;. SteelMillSlabDesign instances\12orderproblem.in
- Compilation / Execution (Linux)
- javac SteelMillSlabDesign.java -cp /opt/hexaly_13_5/bin/hexaly.jarjava -cp /opt/hexaly_13_5/bin/hexaly.jar:. SteelMillSlabDesign instances/12orderproblem.in
import java.util.*;
import java.io.*;
import com.hexaly.optimizer.*;
public class SteelMillSlabDesign {
// Number of available slabs
private int nbSlabs;
// Number of orders
private int nbOrders;
// Number of colors
private int nbColors;
// Maximum number of colors per slab
private int nbColorsMaxSlab;
// Maximum size of a slab
private int maxSize;
// List of colors for each order
private int[] colorsData;
// List of quantities for each order
private int[] quantitiesData;
// Steel waste computed for each content value
private long[] wasteForContentData;
// Hexaly Optimizer
private final HexalyOptimizer optimizer;
// Objective
private HxExpression totalWastedSteel;
// Hexaly Program variables
private HxExpression[] slabs;
private SteelMillSlabDesign(HexalyOptimizer optimizer) {
this.optimizer = optimizer;
}
/* Read instance data */
private void readInstance(String fileName) throws IOException {
try (Scanner input = new Scanner(new File(fileName))) {
nbColorsMaxSlab = 2;
int nbSlabSizes = input.nextInt();
int[] slabSizes = new int[nbSlabSizes];
for (int i = 0; i < nbSlabSizes; ++i) {
slabSizes[i] = input.nextInt();
}
maxSize = slabSizes[nbSlabSizes - 1];
nbColors = input.nextInt();
nbOrders = input.nextInt();
nbSlabs = nbOrders;
int sumSizeOrders = 0;
colorsData = new int[nbOrders];
quantitiesData = new int[nbOrders];
for (int o = 0; o < nbOrders; ++o) {
quantitiesData[o] = input.nextInt();
int c = input.nextInt();
// Note: colors are in 1..nbColors
colorsData[o] = c;
sumSizeOrders += quantitiesData[o];
}
preComputeWasteForContent(slabSizes, sumSizeOrders);
}
}
// Compute the vector wasteForContent
private void preComputeWasteForContent(int[] slabSizes, int sumSizeOrders) {
// No waste when a slab is empty
wasteForContentData = new long[sumSizeOrders];
int prevSize = 0;
for (int i = 0; i < slabSizes.length; ++i) {
int size = slabSizes[i];
if (size < prevSize)
throw new RuntimeException("Slab sizes should be sorted in ascending order");
for (int content = prevSize + 1; content < size; ++content) {
wasteForContentData[content] = size - content;
}
prevSize = size;
}
}
private void solve(int limit) {
// Declare the optimization model
HxModel model = optimizer.getModel();
// Create a HexalyOptimizer array and a function to retrieve the orders's colors and quantities
HxExpression colors = model.array(colorsData);
HxExpression colorLambda = model.lambdaFunction(i -> model.at(colors, i));
HxExpression quantities = model.array(quantitiesData);
HxExpression quantitiesLambda = model.lambdaFunction(i -> model.at(quantities, i));
HxExpression[] slabContent = new HxExpression[nbSlabs];
HxExpression[] wastedSteel = new HxExpression[nbSlabs];
// Create a HexalyOptimizer array to be able to access it with "at" operators
HxExpression wasteForContent = model.array(wasteForContentData);
// Set decisions: slabs[k] represents the orders in slab k
slabs = new HxExpression[nbSlabs];
for (int s = 0; s < nbSlabs; ++s) {
slabs[s] = model.setVar(nbOrders);
}
// Each order must be in one slab and one slab only
model.constraint(model.partition(slabs));
for (int s = 0; s < nbSlabs; ++s) {
HxExpression orders = slabs[s];
// The number of colors per slab must not exceed a specified value
model.constraint(model.leq(model.count(model.distinct(orders, colorLambda)), nbColorsMaxSlab));
// The content of each slab must not exceed the maximum size of the slab
slabContent[s] = model.sum(orders, quantitiesLambda);
model.constraint(model.leq(slabContent[s], maxSize));
// Wasted steel is computed according to the content of the slab
wastedSteel[s] = model.at(wasteForContent, slabContent[s]);
}
// Minimize the total wasted steel
totalWastedSteel = model.sum(wastedSteel);
model.minimize(totalWastedSteel);
model.close();
// Parametrize the optimizer
optimizer.getParam().setTimeLimit(limit);
optimizer.solve();
}
/*
* Write the solution in a file with the following format:
* - total wasted steel
* - number of slabs used
* - for each slab used, the number of orders in the slab and the list of orders
*/
private void writeSolution(String fileName) throws IOException {
try (PrintWriter output = new PrintWriter(new FileWriter(fileName))) {
output.println(totalWastedSteel.getValue());
int actualNbSlabs = 0;
for(int s = 0; s < nbSlabs; ++s) {
if (slabs[s].getCollectionValue().count() > 0) {
actualNbSlabs++;
}
}
output.println(actualNbSlabs);
for (int s = 0; s < nbSlabs; ++s) {
int nbOrdersInSlab = slabs[s].getCollectionValue().count();
if (nbOrdersInSlab == 0) continue;
output.print(nbOrdersInSlab);
for(int o = 0; o < nbOrdersInSlab; ++o) {
output.print(" " + (slabs[s].getCollectionValue().get(o) + 1));
}
output.println();
}
}
}
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Usage: java SteelMillSlabDesign inputFile [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()) {
SteelMillSlabDesign model = new SteelMillSlabDesign(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);
}
}
}