This page is for an old version of Hexaly Optimizer. We recommend that you update your version and read the documentation for the latest stable release.

Social golfer¶

Principles learned¶

  • Create a generic model that uses data
  • Use of n-ary operator “and”

Problem¶

../_images/golfer.png

In a golf club, there are 32 social golfers, each of whom play golf once a week, and always in groups of 4. The problem is to build a schedule of play for 10 weeks with maximum socialisation; that is, as few repeated meetings as possible. More generally the problem is to schedule m groups of n golfers over p weeks, with maximum socialisation. The complexity of the problem is unknown. The instance mentioned has a known solution with no repeated meeting. For more details see CSPLib or MathPuzzle

Download the example

Data¶

Each data file is made of only three numbers:

  • the number of groups
  • the size of groups
  • the number of weeks

Program¶

The decisions variables are binaries x[w][gr][gf], equal to 1 if golfer gf is in group gr on week w. Then, the number of meetings between each pair of golfers is computed in nbMeetings[gf0][gf1]. Finally the number of redundant meetings for a pair of golfers is max(0,nbMeetings[gf0][gf1]-1).

Execution:
localsolver social_golfer.lsp inFileName=instances/c_4_3_3.in [lsTimeLimit=] [solFileName=]
/********** social_golfer.lsp **********/

use io;

/* Reads instance data. */
function input() {
    local usage = "\nUsage: localsolver social_golfer.lsp "
    + "inFileName=inputFile [solFileName=outputFile] [lsTimeLimit=timeLimit]\n";

    if (inFileName == nil) throw usage;

    local inFile = io.openRead(inFileName);
    nbGroups = inFile.readInt();
    groupSize = inFile.readInt();
    nbWeeks = inFile.readInt();
}

/* Declares the optimization model. */
function model() {

    // the number of golfers 
    nbGolfers = nbGroups*groupSize;

    // 0-1 decisions variables: x[w][gr][gf]=1 if golfer gf is in group gr on week w.
    x[1..nbWeeks][1..nbGroups][1..nbGolfers] <- bool();

    // each week, each golfer is assigned to exactly one group
    for[w in 1..nbWeeks][gf in 1..nbGolfers]
        constraint sum[gr in 1..nbGroups](x[w][gr][gf]) == 1;

    // each week, each group contains exactly groupSize golfers
    for[w in 1..nbWeeks][gr in 1..nbGroups]
        constraint sum[gf in 1..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 1..nbWeeks][gr in 1..nbGroups][gf0 in 1..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 1..nbGolfers][gf1 in gf0+1..nbGolfers] {
        nb_meetings[gf0][gf1] <- sum[w in 1..nbWeeks][gr in 1..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 1..nbGolfers][gf1 in gf0+1..nbGolfers](redundant_meetings[gf0][gf1]);
    minimize obj;
}

/* Parameterizes the solver. */
function param() {
    if (lsTimeLimit == nil) lsTimeLimit = 10; 
    if (lsNbThreads == nil) lsNbThreads = 1;
}

/* Writes the solution in a file following 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 1..nbWeeks]{
        for [gr in 1..nbGroups]{
            for [gf in 1..nbGolfers] {
                if (x[w][gr][gf].value==true){
                    solution.print(gf-1, " ");
                }
            }
            solution.println();
        }
        solution.println();
    }
}
Execution (Windows)
set PYTHONPATH=%LS_HOME%\bin\python27\
python social_golfer.py instances\c_4_3_3.in
Execution (Linux)
export PYTHONPATH=/opt/localsolver_XXX/bin/python27/
python social_golfer.py instances/c_4_3_3.in
########## social_golfer.py ##########

import localsolver
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 localsolver.LocalSolver() as ls:

    #
    # Reads instance data 
    #
    file_it = iter(read_integers(sys.argv[1]))
    nb_groups = file_it.next()
    group_size = file_it.next()
    nb_weeks = file_it.next()
    nb_golfers = nb_groups*group_size

    #
    # Declares the optimization model
    #
    model = ls.model  

    # 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()

    #
    # Parameterizes the solver
    #
    ls.param.nb_threads = 1
    if len(sys.argv) >= 4: ls.create_phase().time_limit = int(sys.argv[3])
    else: ls.create_phase().time_limit = 10

    ls.solve()

    # Writes the solution in a file following 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%LS_HOME%\include /link %LS_HOME%\bin\localsolver.dll.lib
social_golfer instances\c_4_3_3.in
Compilation / Execution (Linux)
g++ social_golfer.cpp -I/opt/localsolver_XXX/include -llocalsolver -lpthread -o social_golfer
./social_golfer instances/c_4_3_3.in
/********** social_golfer.cpp **********/

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include "localsolver.h"

using namespace localsolver;
using namespace std;

class SocialGolfer{
public:

    /* Number of groups */
    lsint nbGroups;
    /* Size of each group */
    lsint groupSize;
    /* Number of week */
    lsint nbWeeks;
    /* Number of golfers */
    lsint nbGolfers;

    /* Objective */
    LSExpression obj;

    /* LocalSolver. */
    LocalSolver localsolver;

    /* Decisions variables */
    vector< vector< vector< LSExpression > > > x;

    /* Reads instance data */
    void readInstance(const string & fileName){
        ifstream infile(fileName.c_str());
        if(!infile.is_open()){
            cerr << "File " << fileName << " cannot be opened." << endl;
            exit(1);
        }
        infile >> nbGroups;
        infile >> groupSize;
        infile >> nbWeeks;
        infile.close();

        nbGolfers = nbGroups*groupSize;
    }

    /* Declares the optimization model. */
    void solve(int limit){
        try{
            LSModel model = localsolver.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++){
                    LSExpression nbGroupsAssigned = model.sum();
                    for(int gr = 0; gr < nbGroups; gr++){
                        nbGroupsAssigned += 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++){
                    LSExpression nbGolfersInGroup = model.sum();
                    for(int gf = 0; gf < nbGolfers; gf++){
                        nbGolfersInGroup += 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< LSExpression > > > > 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< LSExpression> > redundantMeetings;
            redundantMeetings.resize(nbGolfers);
            for(int gf0 = 0; gf0 < nbGolfers; gf0++){
                redundantMeetings[gf0].resize(nbGolfers);
                for(int gf1 = gf0+1; gf1 < nbGolfers; gf1++){
                    LSExpression nbMeetings = model.sum();
                    for(int w = 0; w < nbWeeks; w++){
                        for(int gr = 0; gr < nbGroups; gr++){
                            nbMeetings += 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 += redundantMeetings[gf0][gf1];
                }
            }
            model.minimize(obj);

            model.close();
            /* Parameterizes the solver. */
            LSPhase phase = localsolver.createPhase();
            phase.setTimeLimit(limit);
            localsolver.solve(); 

        } catch (const LSException& e) {
            cout << "LSException:" << e.getMessage() << endl;
            exit(1);
        }
    }

    // Writes the solution in a file following 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(fileName.c_str());
        if (!outfile.is_open()) {
            cerr << "File " << fileName << " cannot be opened." << endl;
            exit(1);
        }
        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;
        }
        outfile.close();
    }
};
    
int main(int argc, char** argv) {
    if (argc < 2) {
        cout << "Usage: solcial_golfer inputFile [outputFile] [timeLimit]" << endl;
        exit(1);
    }

    const char* instanceFile = argv[1];
    const char* solFile = argc > 2 ? argv[2] : NULL;
    const char* strTimeLimit = argc > 3 ? argv[3] : "10";

    SocialGolfer model;
    model.readInstance(instanceFile);
    model.solve(atoi(strTimeLimit));

    if(solFile != NULL)
        model.writeSolution(solFile);
    return 0;
}
Compilation / Execution (Windows)
javac SocialGolfer.java -cp %LS_HOME%\bin\localsolver.jar
java -cp %LS_HOME%\bin\localsolver.jar;. SocialGolfer instances\c_4_3_3.in
Compilation/Execution (Linux)
javac SocialGolfer.java -cp /opt/localsolver_XXX/bin/localsolver.jar
java -cp /opt/localsolver_XXX/bin/localsolver.jar:. SocialGolfer instances/c_4_3_3.in
/********** SocialGolfer.java **********/

import java.util.*;
import java.io.*;
import localsolver.*;


public class SocialGolfer{
    /* Number of groups */
    int nbGroups;
    /* Size of each group */
    int groupSize;
    /* Number of week */
    int nbWeeks;
    /* Number of golfers */
    int nbGolfers;

    /* Objective */
    LSExpression obj;

    /* LocalSolver. */
    LocalSolver localsolver;

    /* Decisions variables */
    LSExpression[][][] x;

    /* Reads instance data */
    void readInstance(String fileName){
        try {
            Scanner input = new Scanner(new File(fileName));
            nbGroups = input.nextInt();
            groupSize = input.nextInt();
            nbWeeks = input.nextInt();
            input.close();
                        
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        nbGolfers = nbGroups*groupSize;
    }

    /* Declares the optimization model. */
    void solve(int limit){
        try{
            localsolver = new LocalSolver();
            LSModel model = localsolver.getModel();

            x = new LSExpression[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++){
                    LSExpression 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++){
                    LSExpression 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.
            LSExpression[][][][] meetings = new LSExpression[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
            LSExpression[][] redundantMeetings;
            redundantMeetings = new LSExpression[nbGolfers][nbGolfers];
            for(int gf0 = 0; gf0 < nbGolfers; gf0++){
                for(int gf1 = gf0+1; gf1 < nbGolfers; gf1++){
                    LSExpression 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();
            
            /* Parameterizes the solver. */
            LSPhase phase = localsolver.createPhase();
            phase.setTimeLimit(limit);

            localsolver.solve();

        } catch  (LSException e) {
            System.out.println("LSException:" + e.getMessage());
            System.exit(1);
        }

    }

    /* Writes the solution in a file following 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(String fileName) {
        try {
            BufferedWriter output = new BufferedWriter(new FileWriter(fileName));
            output.write(obj.getValue() + "\n");
            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.write("\n");
                }
                output.write("\n");
            }
            output.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.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";

        SocialGolfer model = new SocialGolfer();
        model.readInstance(instanceFile);
        model.solve(Integer.parseInt(strTimeLimit));
        if(outputFile != null) {
            model.writeSolution(outputFile);
        }
    }

}
Compilation/Execution (Windows)
copy %LS_HOME%\bin\*net.dll .
csc SocialGolfer.cs /reference:localsolvernet.dll
SocialGolfer instances\c_4_3_3.in
/********** SocialGolfer.cs **********/

using System;
using System.IO;
using System.Collections.Generic;
using localsolver;

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;

    // Solver
    LocalSolver localsolver;

    // Objective
    LSExpression obj;

    // Decisions variables
    LSExpression[,,] x;

    public SocialGolfer ()
    {
        localsolver = new LocalSolver();
    }


    /* Reads 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 ()
    {
        localsolver.Dispose();
    }

    /* Declares the optimization model. */
    public void Solve(int limit)
    {
        LSModel model = localsolver.GetModel();

        // Decision variables
        // 0-1 decisions variables: x[w,gr,gf]=1 if golfer gf is in group gr on week w
        x = new LSExpression[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++)
            {
                LSExpression 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++)
            {
                LSExpression 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.
        LSExpression[,,,] meetings = new LSExpression[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
        LSExpression[,] redundantMeetings = new LSExpression[nbGolfers,nbGolfers];
        for(int gf0 = 0; gf0 < nbGolfers; gf0++)
        {
            for(int gf1 = gf0 + 1; gf1 < nbGolfers; gf1++)
            {
                LSExpression 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();

        /* Parameterizes the solver. */
        LSPhase phase = localsolver.CreatePhase();
        phase.SetTimeLimit(limit);
        localsolver.Solve();
    }

    /* Writes the solution in a file following 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]");
            System.Environment.Exit(1);
        }
        string instanceFile = args[0];
        string outputFile = args.Length > 1 ? args[1] : null;
        string strTimeLimit = args.Length > 2 ? args[2] : "10";

        SocialGolfer model = new SocialGolfer();
        model.ReadInstance(instanceFile);
        model.Solve(int.Parse(strTimeLimit));
        if(outputFile != null)
            model.WriteSolution(outputFile);
    }
}