Social Golfer Problem
Problem
In the Social Golfer Problem, we consider a golf club with 32 members. Each of them plays golf once a week, always in groups of 4. The goal of the problem is to build a 10-week schedule for the golfers with maximum socialization, that is, as few repeated meetings as possible. More generally, the problem consists in scheduling m groups of n golfers over p weeks with maximum socialization. For more details, see CSPLib or MathPuzzle.
Principles learned
- Add Boolean decision variables to model the schedule
- Use non-linear operators to compute the number of meetings between each pair of golfers
- Learn about Hexaly Optimizer’s modeling style: distinguish decision variables from intermediate expressions
Data
The instance files we provide comprise three numbers:
- the number of groups
- the size of the groups
- the number of weeks
The instance we use in the example has a known solution with no repeated meetings.
Program
The Hexaly model for the Social Golfer Problem uses Boolean decision variables. For each week w
, each golfer gf
, and each group gr
, x[w][gr][gf]
is equal to 1 if golfer gf
is in group gr
on week w
, else 0. Using the ‘sum’ operator, we constrain each golfer to be in exactly one group each week. We also ensure that each group has the right size.
Using the ‘and’ operator, we compute intermediate expressions to determine, for each week and each pair of golfers, whether these golfers meet on that week. We can then use these expressions to compute the total number of meetings between each pair of golfers. Finally, thanks to the ‘max’ operator, we can compute the objective function to minimize, which is the maximum number of times any pair of golfers meets during the season.
- Execution
-
hexaly social_golfer.hxm inFileName=instances/c_4_3_3.in [hxTimeLimit=] [solFileName=]
use io;
/* Read instance data */
function input() {
local usage = "Usage: hexaly social_golfer.hxm "
+ "inFileName=inputFile [solFileName=outputFile] [hxTimeLimit=timeLimit]";
if (inFileName == nil) throw usage;
local inFile = io.openRead(inFileName);
nbGroups = inFile.readInt();
groupSize = inFile.readInt();
nbWeeks = inFile.readInt();
nbGolfers = nbGroups * groupSize;
}
/* Declare the optimization model */
function model() {
// Decision variables
// 0-1 decisions variables: x[w][gr][gf]=1 if golfer gf is in group gr on week w
x[w in 0...nbWeeks][gr in 0...nbGroups][gf in 0...nbGolfers] <- bool();
// Each week, each golfer is assigned to exactly one group
for [w in 0...nbWeeks][gf in 0...nbGolfers]
constraint sum[gr in 0...nbGroups](x[w][gr][gf]) == 1;
// Each week, each group contains exactly groupSize golfers
for [w in 0...nbWeeks][gr in 0...nbGroups]
constraint sum[gf in 0...nbGolfers](x[w][gr][gf]) == groupSize;
// Golfers gf0 and gf1 meet in group gr on week w if both are
// assigned to this group for week w
meetings[w in 0...nbWeeks][gr in 0...nbGroups][gf0 in 0...nbGolfers][gf1 in gf0+1...nbGolfers]
<- and(x[w][gr][gf0], x[w][gr][gf1]);
// The number of meetings of golfers gf0 and gf1 is the sum
// of their meeting variables over all weeks and groups
for [gf0 in 0...nbGolfers][gf1 in gf0+1...nbGolfers] {
nb_meetings[gf0][gf1] <- sum[w in 0...nbWeeks][gr in 0...nbGroups](meetings[w][gr][gf0][gf1]);
redundant_meetings[gf0][gf1] <- max(nb_meetings[gf0][gf1] -1, 0);
}
// The goal is to minimize the number of redundant meetings
obj <- sum[gf0 in 0...nbGolfers][gf1 in gf0+1...nbGolfers](redundant_meetings[gf0][gf1]);
minimize obj;
}
/* Parametrize the solver */
function param() {
if (hxTimeLimit == nil) hxTimeLimit = 10;
}
/* Write the solution in a file with the following format:
* - the objective value
* - for each week and each group, write the golfers of the group
* (nbWeeks x nbGroupes lines of groupSize numbers). */
function output() {
if (solFileName == nil) return;
local solution = io.openWrite(solFileName);
solution.println(obj.value);
for [w in 0...nbWeeks] {
for [gr in 0...nbGroups] {
for [gf in 0...nbGolfers] {
if (x[w][gr][gf].value==true) {
solution.print(gf, " ");
}
}
solution.println();
}
solution.println();
}
}
- Execution (Windows)
-
set PYTHONPATH=%HX_HOME%\bin\pythonpython social_golfer.py instances\c_4_3_3.in
- Execution (Linux)
-
export PYTHONPATH=/opt/hexaly_13_0/bin/pythonpython social_golfer.py instances/c_4_3_3.in
import hexaly.optimizer
import sys
if len(sys.argv) < 2:
print("Usage: python social_golfer.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()]
with hexaly.optimizer.HexalyOptimizer() as optimizer:
#
# Read instance data
#
file_it = iter(read_integers(sys.argv[1]))
nb_groups = next(file_it)
group_size = next(file_it)
nb_weeks = next(file_it)
nb_golfers = nb_groups * group_size
#
# Declare the optimization model
#
model = optimizer.model
# Decision variables
# 0-1 decisions variables: x[w][gr][gf]=1 if golfer gf is in group gr on week w
x = [[[model.bool() for gf in range(nb_golfers)]
for gr in range(nb_groups)] for w in range(nb_weeks)]
# Each week, each golfer is assigned to exactly one group
for w in range(nb_weeks):
for gf in range(nb_golfers):
model.constraint(
model.eq(model.sum(x[w][gr][gf] for gr in range(nb_groups)), 1))
# Each week, each group contains exactly group_size golfers
for w in range(nb_weeks):
for gr in range(nb_groups):
model.constraint(
model.eq(model.sum(x[w][gr][gf] for gf in range(nb_golfers)), group_size))
# Golfers gf0 and gf1 meet in group gr on week w if both are
# assigned to this group for week w
meetings = [None] * nb_weeks
for w in range(nb_weeks):
meetings[w] = [None] * nb_groups
for gr in range(nb_groups):
meetings[w][gr] = [None] * nb_golfers
for gf0 in range(nb_golfers):
meetings[w][gr][gf0] = [None] * nb_golfers
for gf1 in range(gf0 + 1, nb_golfers):
meetings[w][gr][gf0][gf1] = model.and_(x[w][gr][gf0], x[w][gr][gf1])
# The number of meetings of golfers gf0 and gf1 is the sum
# of their meeting variables over all weeks and groups
redundant_meetings = [None] * nb_golfers
for gf0 in range(nb_golfers):
redundant_meetings[gf0] = [None] * nb_golfers
for gf1 in range(gf0 + 1, nb_golfers):
nb_meetings = model.sum(meetings[w][gr][gf0][gf1] for w in range(nb_weeks)
for gr in range(nb_groups))
redundant_meetings[gf0][gf1] = model.max(nb_meetings - 1, 0)
# the goal is to minimize the number of redundant meetings
obj = model.sum(redundant_meetings[gf0][gf1] for gf0 in range(nb_golfers)
for gf1 in range(gf0 + 1, nb_golfers))
model.minimize(obj)
model.close()
# Parameterize the optimizer
optimizer.param.nb_threads = 1
if len(sys.argv) >= 4:
optimizer.param.time_limit = int(sys.argv[3])
else:
optimizer.param.time_limit = 10
optimizer.solve()
#
# Write the solution in a file with the following format:
# - the objective value
# - for each week and each group, write the golfers of the group
# (nb_weeks x nbGroupes lines of group_size numbers).
#
if len(sys.argv) >= 3:
with open(sys.argv[2], 'w') as f:
f.write("%d\n" % obj.value)
for w in range(nb_weeks):
for gr in range(nb_groups):
for gf in range(nb_golfers):
if x[w][gr][gf].value:
f.write("%d " % (gf))
f.write("\n")
f.write("\n")
- Compilation / Execution (Windows)
-
cl /EHsc social_golfer.cpp -I%HX_HOME%\include /link %HX_HOME%\bin\hexaly130.libsocial_golfer instances\c_4_3_3.in
- Compilation / Execution (Linux)
-
g++ social_golfer.cpp -I/opt/hexaly_13_0/include -lhexaly130 -lpthread -o social_golfer
./social_golfer instances/c_4_3_3.in
#include "optimizer/hexalyoptimizer.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
using namespace hexaly;
using namespace std;
class SocialGolfer {
public:
// Number of groups
int nbGroups;
// Size of each group
int groupSize;
// Number of week
int nbWeeks;
// Number of golfers
int nbGolfers;
// Objective
HxExpression obj;
// Hexaly Optimizer
HexalyOptimizer optimizer;
// Decisions variables
vector<vector<vector<HxExpression>>> x;
// Read instance data
void readInstance(const string& fileName) {
ifstream infile;
infile.exceptions(ifstream::failbit | ifstream::badbit);
infile.open(fileName.c_str());
infile >> nbGroups;
infile >> groupSize;
infile >> nbWeeks;
infile.close();
nbGolfers = nbGroups * groupSize;
}
// Declare the optimization model
void solve(int limit) {
HxModel model = optimizer.getModel();
// Decision variables
// 0-1 decisions variables: x[w][gr][gf]=1 if golfer gf is in group gr on week w
x.resize(nbWeeks);
for (int w = 0; w < nbWeeks; ++w) {
x[w].resize(nbGroups);
for (int gr = 0; gr < nbGroups; ++gr) {
x[w][gr].resize(nbGolfers);
for (int gf = 0; gf < nbGolfers; ++gf) {
x[w][gr][gf] = model.boolVar();
}
}
}
// Each week, each golfer is assigned to exactly one group
for (int w = 0; w < nbWeeks; ++w) {
for (int gf = 0; gf < nbGolfers; ++gf) {
HxExpression nbGroupsAssigned = model.sum();
for (int gr = 0; gr < nbGroups; ++gr) {
nbGroupsAssigned.addOperand(x[w][gr][gf]);
}
model.constraint(nbGroupsAssigned == 1);
}
}
// Each week, each group contains exactly groupSize golfers
for (int w = 0; w < nbWeeks; ++w) {
for (int gr = 0; gr < nbGroups; ++gr) {
HxExpression nbGolfersInGroup = model.sum();
for (int gf = 0; gf < nbGolfers; ++gf) {
nbGolfersInGroup.addOperand(x[w][gr][gf]);
}
model.constraint(nbGolfersInGroup == groupSize);
}
}
// Golfers gf0 and gf1 meet in group gr on week w if both are
// assigned to this group for week w
vector<vector<vector<vector<HxExpression>>>> meetings;
meetings.resize(nbWeeks);
for (int w = 0; w < nbWeeks; ++w) {
meetings[w].resize(nbGroups);
for (int gr = 0; gr < nbGroups; ++gr) {
meetings[w][gr].resize(nbGolfers);
for (int gf0 = 0; gf0 < nbGolfers; ++gf0) {
meetings[w][gr][gf0].resize(nbGolfers);
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1) {
meetings[w][gr][gf0][gf1] = model.and_(x[w][gr][gf0], x[w][gr][gf1]);
}
}
}
}
// The number of meetings of golfers gf0 and gf1 is the sum of
// their meeting variables over all weeks and groups
vector<vector<HxExpression>> redundantMeetings;
redundantMeetings.resize(nbGolfers);
for (int gf0 = 0; gf0 < nbGolfers; ++gf0) {
redundantMeetings[gf0].resize(nbGolfers);
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1) {
HxExpression nbMeetings = model.sum();
for (int w = 0; w < nbWeeks; ++w) {
for (int gr = 0; gr < nbGroups; ++gr) {
nbMeetings.addOperand(meetings[w][gr][gf0][gf1]);
}
}
redundantMeetings[gf0][gf1] = model.max(nbMeetings - 1, 0);
}
}
// The goal is to minimize the number of redundant meetings
obj = model.sum();
for (int gf0 = 0; gf0 < nbGolfers; ++gf0) {
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1) {
obj.addOperand(redundantMeetings[gf0][gf1]);
}
}
model.minimize(obj);
model.close();
// Parametrize the optimizer
optimizer.getParam().setTimeLimit(limit);
optimizer.solve();
}
/* Write the solution in a file with the following format:
* - the objective value
* - for each week and each group, write the golfers of the group
* (nbWeeks x nbGroupes lines of groupSize numbers). */
void writeSolution(const string& fileName) {
ofstream outfile;
outfile.exceptions(ofstream::failbit | ofstream::badbit);
outfile.open(fileName.c_str());
outfile << obj.getValue() << endl;
for (int w = 0; w < nbWeeks; ++w) {
for (int gr = 0; gr < nbGroups; ++gr) {
for (int gf = 0; gf < nbGolfers; ++gf) {
if (x[w][gr][gf].getValue()) {
outfile << gf << " ";
}
}
outfile << endl;
}
outfile << endl;
}
}
};
int main(int argc, char** argv) {
if (argc < 2) {
cerr << "Usage: social_golfer inputFile [outputFile] [timeLimit]" << endl;
return 1;
}
const char* instanceFile = argv[1];
const char* solFile = argc > 2 ? argv[2] : NULL;
const char* strTimeLimit = argc > 3 ? argv[3] : "10";
try {
SocialGolfer model;
model.readInstance(instanceFile);
model.solve(atoi(strTimeLimit));
if (solFile != NULL)
model.writeSolution(solFile);
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 SocialGolfer.cs /reference:Hexaly.NET.dllSocialGolfer instances\c_4_3_3.in
using System;
using System.IO;
using Hexaly.Optimizer;
public class SocialGolfer : IDisposable
{
// Number of groups
int nbGroups;
// Size of each group
int groupSize;
// Number of week
int nbWeeks;
// Number of golfers
int nbGolfers;
// Hexaly Optimizer
HexalyOptimizer optimizer;
// Objective
HxExpression obj;
// Decisions variables
HxExpression[,,] x;
public SocialGolfer()
{
optimizer = new HexalyOptimizer();
}
/* Read instance data */
public void ReadInstance(string fileName)
{
using (StreamReader input = new StreamReader(fileName))
{
var tokens = input.ReadLine().Split(' ');
nbGroups = int.Parse(tokens[0]);
groupSize = int.Parse(tokens[1]);
nbWeeks = int.Parse(tokens[2]);
}
nbGolfers = nbGroups * groupSize;
}
public void Dispose()
{
if (optimizer != null)
optimizer.Dispose();
}
// Declare the optimization model
public void Solve(int limit)
{
HxModel model = optimizer.GetModel();
// Decision variables
// 0-1 decisions variables: x[w,gr,gf]=1 if golfer gf is in group gr on week w
x = new HxExpression[nbWeeks, nbGroups, nbGolfers];
for (int w = 0; w < nbWeeks; ++w)
{
for (int gr = 0; gr < nbGroups; ++gr)
{
for (int gf = 0; gf < nbGolfers; ++gf)
x[w, gr, gf] = model.Bool();
}
}
// Each week, each golfer is assigned to exactly one group
for (int w = 0; w < nbWeeks; ++w)
{
for (int gf = 0; gf < nbGolfers; ++gf)
{
HxExpression nbGroupsAssigned = model.Sum();
for (int gr = 0; gr < nbGroups; ++gr)
nbGroupsAssigned.AddOperand(x[w, gr, gf]);
model.Constraint(nbGroupsAssigned == 1);
}
}
// Each week, each group contains exactly groupSize golfers
for (int w = 0; w < nbWeeks; ++w)
{
for (int gr = 0; gr < nbGroups; ++gr)
{
HxExpression nbGolfersInGroup = model.Sum();
for (int gf = 0; gf < nbGolfers; ++gf)
nbGolfersInGroup.AddOperand(x[w, gr, gf]);
model.Constraint(nbGolfersInGroup == groupSize);
}
}
// Golfers gf0 and gf1 meet in group gr on week w if both are
// assigned to this group for week w
HxExpression[,,,] meetings = new HxExpression[nbWeeks, nbGroups, nbGolfers, nbGolfers];
for (int w = 0; w < nbWeeks; ++w)
{
for (int gr = 0; gr < nbGroups; ++gr)
{
for (int gf0 = 0; gf0 < nbGolfers; ++gf0)
{
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1)
meetings[w, gr, gf0, gf1] = model.And(x[w, gr, gf0], x[w, gr, gf1]);
}
}
}
// The number of meetings of golfers gf0 and gf1 is the sum
// of their meeting variables over all weeks and groups
HxExpression[,] redundantMeetings = new HxExpression[nbGolfers, nbGolfers];
for (int gf0 = 0; gf0 < nbGolfers; ++gf0)
{
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1)
{
HxExpression nbMeetings = model.Sum();
for (int w = 0; w < nbWeeks; ++w)
{
for (int gr = 0; gr < nbGroups; ++gr)
nbMeetings.AddOperand(meetings[w, gr, gf0, gf1]);
}
redundantMeetings[gf0, gf1] = model.Max(nbMeetings - 1, 0);
}
}
// The goal is to minimize the number of redundant meetings
obj = model.Sum();
for (int gf0 = 0; gf0 < nbGolfers; ++gf0)
{
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1)
obj.AddOperand(redundantMeetings[gf0, gf1]);
}
model.Minimize(obj);
model.Close();
// Parametrize the optimizer
optimizer.GetParam().SetTimeLimit(limit);
optimizer.Solve();
}
/* Write the solution in a file with the following format:
* - the objective value
* - for each week and each group, write the golfers of the group
* (nbWeeks x nbGroupes lines of groupSize numbers). */
public void WriteSolution(string fileName)
{
using (StreamWriter output = new StreamWriter(fileName))
{
output.WriteLine(obj.GetValue());
for (int w = 0; w < nbWeeks; ++w)
{
for (int gr = 0; gr < nbGroups; ++gr)
{
for (int gf = 0; gf < nbGolfers; ++gf)
{
if (x[w, gr, gf].GetValue() == 1)
output.Write(gf + " ");
}
output.WriteLine();
}
output.WriteLine();
}
}
}
public static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage: SocialGolfer inputFile [solFile] [timeLimit]");
Environment.Exit(1);
}
string instanceFile = args[0];
string outputFile = args.Length > 1 ? args[1] : null;
string strTimeLimit = args.Length > 2 ? args[2] : "10";
using (SocialGolfer model = new SocialGolfer())
{
model.ReadInstance(instanceFile);
model.Solve(int.Parse(strTimeLimit));
if (outputFile != null)
model.WriteSolution(outputFile);
}
}
}
- Compilation / Execution (Windows)
-
javac SocialGolfer.java -cp %HX_HOME%\bin\hexaly.jarjava -cp %HX_HOME%\bin\hexaly.jar;. SocialGolfer instances\c_4_3_3.in
- Compilation / Execution (Linux)
-
javac SocialGolfer.java -cp /opt/hexaly_13_0/bin/hexaly.jarjava -cp /opt/hexaly_13_0/bin/hexaly.jar:. SocialGolfer instances/c_4_3_3.in
import java.util.*;
import java.io.*;
import com.hexaly.optimizer.*;
public class SocialGolfer {
// Number of groups
private int nbGroups;
// Size of each group
private int groupSize;
// Number of week
private int nbWeeks;
// Number of golfers
private int nbGolfers;
// Objective
private HxExpression obj;
// Hexaly Optimizer.
private final HexalyOptimizer optimizer;
// Decisions variables
private HxExpression[][][] x;
private SocialGolfer(HexalyOptimizer optimizer) {
this.optimizer = optimizer;
}
// Read instance data
private void readInstance(String fileName) throws IOException {
try (Scanner input = new Scanner(new File(fileName))) {
nbGroups = input.nextInt();
groupSize = input.nextInt();
nbWeeks = input.nextInt();
}
nbGolfers = nbGroups * groupSize;
}
private void solve(int limit) {
// Declare the optimization model
HxModel model = optimizer.getModel();
x = new HxExpression[nbWeeks][nbGroups][nbGolfers];
// Decision variables
// 0-1 decisions variables: x[w][gr][gf]=1 if golfer gf is in group gr on week w
for (int w = 0; w < nbWeeks; ++w) {
for (int gr = 0; gr < nbGroups; ++gr) {
for (int gf = 0; gf < nbGolfers; ++gf) {
x[w][gr][gf] = model.boolVar();
}
}
}
// Each week, each golfer is assigned to exactly one group
for (int w = 0; w < nbWeeks; ++w) {
for (int gf = 0; gf < nbGolfers; ++gf) {
HxExpression nbGroupsAssigned = model.sum();
for (int gr = 0; gr < nbGroups; ++gr) {
nbGroupsAssigned.addOperand(x[w][gr][gf]);
}
model.constraint(model.eq(nbGroupsAssigned, 1));
}
}
// Each week, each group contains exactly groupSize golfers
for (int w = 0; w < nbWeeks; ++w) {
for (int gr = 0; gr < nbGroups; ++gr) {
HxExpression nbGolfersInGroup = model.sum();
for (int gf = 0; gf < nbGolfers; ++gf) {
nbGolfersInGroup.addOperand(x[w][gr][gf]);
}
model.constraint(model.eq(nbGolfersInGroup, groupSize));
}
}
// Golfers gf0 and gf1 meet in group gr on week w if both are
// assigned to this group for week w
HxExpression[][][][] meetings = new HxExpression[nbWeeks][nbGroups][nbGolfers][nbGolfers];
for (int w = 0; w < nbWeeks; ++w) {
for (int gr = 0; gr < nbGroups; ++gr) {
for (int gf0 = 0; gf0 < nbGolfers; ++gf0) {
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1) {
meetings[w][gr][gf0][gf1] = model.and(x[w][gr][gf0], x[w][gr][gf1]);
}
}
}
}
// The number of meetings of golfers gf0 and gf1 is the sum
// of their meeting variables over all weeks and groups
HxExpression[][] redundantMeetings;
redundantMeetings = new HxExpression[nbGolfers][nbGolfers];
for (int gf0 = 0; gf0 < nbGolfers; ++gf0) {
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1) {
HxExpression nbMeetings = model.sum();
for (int w = 0; w < nbWeeks; ++w) {
for (int gr = 0; gr < nbGroups; ++gr) {
nbMeetings.addOperand(meetings[w][gr][gf0][gf1]);
}
}
redundantMeetings[gf0][gf1] = model.max(model.sub(nbMeetings, 1), 0);
}
}
// the goal is to minimize the number of redundant meetings
obj = model.sum();
for (int gf0 = 0; gf0 < nbGolfers; ++gf0) {
for (int gf1 = gf0 + 1; gf1 < nbGolfers; ++gf1) {
obj.addOperand(redundantMeetings[gf0][gf1]);
}
}
model.minimize(obj);
model.close();
// Parametrize the optimizer
optimizer.getParam().setTimeLimit(limit);
optimizer.solve();
}
/* Write the solution in a file with the following format:
*- the objective value
* - for each week and each group, write the golfers of the group
* (nbWeeks x nbGroupes lines of groupSize numbers). */
private void writeSolution(String fileName) throws IOException {
try (PrintWriter output = new PrintWriter(fileName)) {
output.println(obj.getValue());
for (int w = 0; w < nbWeeks; ++w) {
for (int gr = 0; gr < nbGroups; ++gr) {
for (int gf = 0; gf < nbGolfers; ++gf) {
if (x[w][gr][gf].getValue() == 1) {
output.print(gf + " ");
}
}
output.println();
}
output.println();
}
}
}
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Usage: java SocialGolfer 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] : "10";
try (HexalyOptimizer optimizer = new HexalyOptimizer()) {
SocialGolfer model = new SocialGolfer(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);
}
}
}