static void Main(string[] args) { try { int n = 10; if (args.Length > 0) { n = Int32.Parse(args[0]); } if ((n % 2) == 1) { n++; } Console.WriteLine("Finding schedule for {0} teams", n); int nbWeeks = 2 * (n - 1); int nbGamesPerWeek = n / 2; int nbGames = n * (n - 1); CP cp = new CP(); IIntVar[][] games = new IIntVar[nbWeeks][]; IIntVar[][] home = new IIntVar[nbWeeks][]; IIntVar[][] away = new IIntVar[nbWeeks][]; for (int i = 0; i < nbWeeks; i++) { home[i] = cp.IntVarArray(nbGamesPerWeek, 0, n - 1); away[i] = cp.IntVarArray(nbGamesPerWeek, 0, n - 1); games[i] = cp.IntVarArray(nbGamesPerWeek, 0, nbGames - 1); } // // For each play slot, set up correspondance between game id, // home team, and away team // IIntTupleSet gha = cp.IntTable(3); int[] tuple = new int[3]; for (int i = 0; i < n; i++) { tuple[0] = i; for (int j = 0; j < n; j++) { if (i != j) { tuple[1] = j; tuple[2] = Game(i, j, n); cp.AddTuple(gha, tuple); } } } for (int i = 0; i < nbWeeks; i++) { for (int j = 0; j < nbGamesPerWeek; j++) { IIntVar[] vars = cp.IntVarArray(3); vars[0] = home[i][j]; vars[1] = away[i][j]; vars[2] = games[i][j]; cp.Add(cp.AllowedAssignments(vars, gha)); } } // // All teams play each week // for (int i = 0; i < nbWeeks; i++) { IIntVar[] teamsThisWeek = cp.IntVarArray(n); for (int j = 0; j < nbGamesPerWeek; j++) { teamsThisWeek[j] = home[i][j]; teamsThisWeek[nbGamesPerWeek + j] = away[i][j]; } cp.Add(cp.AllDiff(teamsThisWeek)); } // // Dual representation: for each game id, the play slot is maintained // IIntVar[] weekOfGame = cp.IntVarArray(nbGames, 0, nbWeeks - 1); IIntVar[] allGames = cp.IntVarArray(nbGames); IIntVar[] allSlots = cp.IntVarArray(nbGames, 0, nbGames - 1); for (int i = 0; i < nbWeeks; i++) { for (int j = 0; j < nbGamesPerWeek; j++) { allGames[i * nbGamesPerWeek + j] = games[i][j]; } } cp.Add(cp.Inverse(allGames, allSlots)); for (int i = 0; i < nbGames; i++) { cp.Add(cp.Eq(weekOfGame[i], cp.Div(allSlots[i], nbGamesPerWeek))); } // // Two half schedules. Cannot play the same pair twice in the same half. // Plus, impose a minimum number of weeks between two games involving // the same teams (up to six weeks) // int mid = nbWeeks / 2; int overlap = 0; if (n >= 6) { overlap = min(n / 2, 6); } for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { int g1 = Game(i, j, n); int g2 = Game(j, i, n); cp.Add(cp.Equiv(cp.Ge(weekOfGame[g1], mid), cp.Lt(weekOfGame[g2], mid))); // Six week difference... if (overlap != 0) { cp.Add(cp.Ge(cp.Abs(cp.Diff(weekOfGame[g1], weekOfGame[g2])), overlap)); } } } // // Can't have three homes or three aways in a row. // IIntVar[][] playHome = new IIntVar[n][]; for (int i = 0; i < n; i++) { playHome[i] = cp.IntVarArray(nbWeeks, 0, 1); for (int j = 0; j < nbWeeks; j++) { cp.Add(cp.Eq(playHome[i][j], cp.Count(home[j], i))); } for (int j = 0; j < nbWeeks - 3; j++) { IIntVar[] window = cp.IntVarArray(3); for (int k = j; k < j + 3; k++) { window[k - j] = playHome[i][k]; } IIntExpr windowSum = cp.Sum(window); cp.Add(cp.Le(1, windowSum)); cp.Add(cp.Le(windowSum, 2)); } } // // If we start the season home, we finish away and vice versa. // for (int i = 0; i < n; i++) { cp.Add(cp.Neq(playHome[i][0], playHome[i][nbWeeks - 1])); } // // Objective: minimize the number of `breaks'. A break is // two consecutive home or away matches for a // particular team IIntVar[] teamBreaks = cp.IntVarArray(n, 0, nbWeeks / 2); for (int i = 0; i < n; i++) { IIntExpr nbreaks = cp.Constant(0); for (int j = 1; j < nbWeeks; j++) { nbreaks = cp.Sum(nbreaks, cp.IntExpr(cp.Eq(playHome[i][j - 1], playHome[i][j]))); } cp.Add(cp.Eq(teamBreaks[i], nbreaks)); } IIntVar breaks = cp.IntVar(n - 2, n * (nbWeeks / 2)); cp.Add(cp.Eq(breaks, cp.Sum(teamBreaks))); cp.Add(cp.Minimize(breaks)); // // Catalyzing constraints // // Each team plays home the same number of times as away for (int i = 0; i < n; i++) { cp.Add(cp.Eq(cp.Sum(playHome[i]), nbWeeks / 2)); } // Breaks must be even for each team for (int i = 0; i < n; i++) { cp.Add(cp.Eq(cp.Modulo(teamBreaks[i], 2), 0)); } // // Symmetry breaking constraints // // Teams are interchangeable. Fix first week. // Also breaks reflection symmetry of the whole schedule. for (int i = 0; i < nbGamesPerWeek; i++) { cp.Add(cp.Eq(home[0][i], i * 2)); cp.Add(cp.Eq(away[0][i], i * 2 + 1)); } // Order of games in each week is arbitrary. // Break symmetry by forcing an order. for (int i = 0; i < nbWeeks; i++) { for (int j = 1; j < nbGamesPerWeek; j++) { cp.Add(cp.Gt(games[i][j], games[i][j - 1])); } } cp.SetParameter(CP.DoubleParam.TimeLimit, 20); cp.SetParameter(CP.IntParam.LogPeriod, 10000); IVarSelector varSel = cp.SelectSmallest(cp.VarIndex(allGames));; IValueSelector valSel = cp.SelectRandomValue(); ISearchPhase phase = cp.SearchPhase(allGames, cp.IntVarChooser(varSel), cp.IntValueChooser(valSel)); cp.SetSearchPhases(phase); cp.StartNewSearch(); while (cp.Next()) { Console.WriteLine("Solution at {0}", cp.GetValue(breaks)); } cp.EndSearch(); } catch (ILOG.Concert.Exception e) { Console.WriteLine("Error {0}", e); } }
static void Main(string[] args) { CP cp = new CP(); coaching = new int[nbPersons]; int i; for (i = 0; i < nbPersons; i++) { coaching[i] = -1; } for (i = 0; i < 12; i = i + 2) // the 12 first of Service A are couples of coached/coach { coaching[i] = i + 1; coaching[i + 1] = i; } for (i = 20; i < 32; i = i + 2)// the 12 first of Service B are couples of coached/coach { coaching[i] = i + 1; coaching[i + 1] = i; } for (i = 40; i < nbPersons; i += 5) // the 4 first of Services C,D,E,F are couples of coached/coach { coaching[i] = i + 1; coaching[i + 1] = i; coaching[i + 2] = i + 3; coaching[i + 3] = i + 2; } //compute the possible solutions of a team IIntTupleSet tupleSet = MakeTeamTuples(cp); // groups[i] represents the ordered set of people in the team i IIntVar[][] groups = new IIntVar[nbTeams][]; for (i = 0; i < nbTeams; i++) { groups[i] = cp.IntVarArray(teamSize, 0, nbPersons - 1); cp.Add(cp.AllowedAssignments(groups[i], tupleSet)); } IIntVar[] allVars = cp.IntVarArray(nbPersons); int s = 0; int w; int p; for (w = 0; w < nbTeams; ++w) { for (p = 0; p < teamSize; ++p) { allVars[s] = groups[w][p]; ++s; } } cp.Add(cp.AllDiff(allVars)); // team[i] represents the number of the team of people number i IIntVar[] team = cp.IntVarArray(nbPersons, 0, nbTeams); for (w = 0; w < nbTeams; ++w) { for (p = 0; p < teamSize; ++p) { cp.Add(cp.Eq(cp.Element(team, groups[w][p]), w)); } } // Additional constraints // to improve efficiency we could force the following // first three constraints directly in MakeTeamTuples but the fourth // constraint cannot be expressed as a restriction of // the tuple set, since it is not local to a tuple cp.Add(cp.Or(cp.Eq(team[5], team[41]), cp.Eq(team[5], team[51]))); cp.Add(cp.Or(cp.Eq(team[15], team[40]), cp.Eq(team[15], team[51]))); cp.Add(cp.Or(cp.Eq(team[25], team[40]), cp.Eq(team[25], team[50]))); cp.Add(cp.Or(cp.Eq(team[20], team[24]), cp.Eq(team[22], team[50]))); //break symmetry: the teams are ordered according to the smallest in each team for (i = 0; i < nbTeams - 1; i++) { cp.Add(cp.Lt(groups[i][0], groups[i + 1][0])); } cp.SetParameter(CP.IntParam.AllDiffInferenceLevel, CP.ParameterValues.Extended); if (cp.Solve()) { Console.WriteLine(); Console.WriteLine("SOLUTION"); for (p = 0; p < nbTeams; ++p) { Console.Write("team " + p + " : "); for (w = 0; w < teamSize; ++w) { Console.Write((int)cp.GetValue(groups[p][w]) + " "); } Console.WriteLine(); } } else { Console.WriteLine("**** NO SOLUTION ****"); } }
/* * MakeTeamTuples return a IIntTupleSet containing all the possible configurations of a team. * The team members in a tuple are ordered to break symmetry. */ public static IIntTupleSet MakeTeamTuples(CP mainCP) { CP cp = new CP(); int i; int[] newEmployee = new int[nbPersons]; int[] service = new int[nbPersons]; for (i = 0; i < nbPersons; i++) { if ((i % 2) == 0) { newEmployee[i] = 1; } else { newEmployee[i] = 0; } if (i < 20) { service[i] = 0; } else if (i < 40) { service[i] = 1; } else if (i < 45) { service[i] = 2; } else if (i < 50) { service[i] = 3; } else if (i < 55) { service[i] = 4; } else { service[i] = 5; } } IIntTupleSet ts = mainCP.IntTable(teamSize); IIntVar[] teamMembers = cp.IntVarArray(teamSize, 0, nbPersons - 1); //number of new employees among the teamMembers = number of teamMembers / 2 IIntExpr nbNewEmployees = cp.Constant(0); for (i = 0; i < teamSize; i++) { nbNewEmployees = cp.Sum(nbNewEmployees, cp.Element(newEmployee, teamMembers[i])); } cp.Add(cp.Eq(nbNewEmployees, teamSize / 2)); //a new employee and his coach must be in the same team for (i = 0; i < 60; i += 2) { if (coaching[i] >= 0) { cp.Add(cp.Eq(cp.Count(teamMembers, i), cp.Count(teamMembers, coaching[i]))); } } IIntVar[] serviceVar = cp.IntVarArray(teamSize, 0, numServices - 1); for (i = 0; i < teamSize; i++) { cp.Add(cp.Eq(serviceVar[i], cp.Element(service, teamMembers[i]))); } // at most 4 people of the same service for (i = 0; i < numServices; i++) { cp.Add(cp.Le(cp.Count(serviceVar, i), 4)); } //Persons of Services A and B cannot be in the same team //Persons of Services E and F cannot be in the same team cp.Add(cp.Or(cp.Eq(cp.Count(serviceVar, 0), 0), cp.Eq(cp.Count(serviceVar, 1), 0))); cp.Add(cp.Or(cp.Eq(cp.Count(serviceVar, 4), 0), cp.Eq(cp.Count(serviceVar, 5), 0))); // order the teamMembers to break symmetry for (i = 0; i < teamSize - 1; i++) { cp.Add(cp.Lt(teamMembers[i], teamMembers[i + 1])); } int[] tuple = new int[teamSize]; cp.SetParameter(CP.IntParam.LogVerbosity, CP.ParameterValues.Quiet); cp.SetParameter(CP.IntParam.SearchType, CP.ParameterValues.DepthFirst); cp.StartNewSearch(); while (cp.Next()) { for (i = 0; i < teamSize; i++) { tuple[i] = (int)cp.GetValue(teamMembers[i]); } cp.AddTuple(ts, tuple); } return(ts); }
static public void Main(string[] args) { CP cp = new CP(); int nbTruckConfigs = 7; // number of possible configurations for the truck int nbOrders = 21; int nbCustomers = 3; int nbTrucks = 15; //max number of travels of the truck int[] maxTruckConfigLoad = //Capacity of the truck depends on its config { 11, 11, 11, 11, 10, 10, 10 }; int maxLoad = Max(maxTruckConfigLoad); int[] customerOfOrder = { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2 }; int[] volumes = { 3, 4, 3, 2, 5, 4, 11, 4, 5, 2, 4, 7, 3, 5, 2, 5, 6, 11, 1, 6, 3 }; int[] colors = { 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 0, 2, 1, 0, 2, 0, 0, 0 }; int[] truckCost = //cost for loading a truck of a given config { 2, 2, 2, 3, 3, 3, 4 }; //Decision variables IIntVar[] truckConfigs = cp.IntVarArray(nbTrucks, 0, nbTruckConfigs - 1); //configuration of the truck IIntVar[] where = cp.IntVarArray(nbOrders, 0, nbTrucks - 1); //In which truck is an order IIntVar[] load = cp.IntVarArray(nbTrucks, 0, maxLoad); //load of a truck IIntVar numUsed = cp.IntVar(0, nbTrucks); // number of trucks used IIntVar[] customerOfTruck = cp.IntVarArray(nbTrucks, 0, nbCustomers); // transition costs between trucks IIntTupleSet costTuples = cp.IntTable(3); cp.AddTuple(costTuples, new int[] { 0, 0, 0 }); cp.AddTuple(costTuples, new int[] { 0, 1, 0 }); cp.AddTuple(costTuples, new int[] { 0, 2, 0 }); cp.AddTuple(costTuples, new int[] { 0, 3, 10 }); cp.AddTuple(costTuples, new int[] { 0, 4, 10 }); cp.AddTuple(costTuples, new int[] { 0, 5, 10 }); cp.AddTuple(costTuples, new int[] { 0, 6, 15 }); cp.AddTuple(costTuples, new int[] { 1, 0, 0 }); cp.AddTuple(costTuples, new int[] { 1, 1, 0 }); cp.AddTuple(costTuples, new int[] { 1, 2, 0 }); cp.AddTuple(costTuples, new int[] { 1, 3, 10 }); cp.AddTuple(costTuples, new int[] { 1, 4, 10 }); cp.AddTuple(costTuples, new int[] { 1, 5, 10 }); cp.AddTuple(costTuples, new int[] { 1, 6, 15 }); cp.AddTuple(costTuples, new int[] { 2, 0, 0 }); cp.AddTuple(costTuples, new int[] { 2, 1, 0 }); cp.AddTuple(costTuples, new int[] { 2, 2, 0 }); cp.AddTuple(costTuples, new int[] { 2, 3, 10 }); cp.AddTuple(costTuples, new int[] { 2, 4, 10 }); cp.AddTuple(costTuples, new int[] { 2, 5, 10 }); cp.AddTuple(costTuples, new int[] { 2, 6, 15 }); cp.AddTuple(costTuples, new int[] { 3, 0, 3 }); cp.AddTuple(costTuples, new int[] { 3, 1, 3 }); cp.AddTuple(costTuples, new int[] { 3, 2, 3 }); cp.AddTuple(costTuples, new int[] { 3, 3, 0 }); cp.AddTuple(costTuples, new int[] { 3, 4, 10 }); cp.AddTuple(costTuples, new int[] { 3, 5, 10 }); cp.AddTuple(costTuples, new int[] { 3, 6, 15 }); cp.AddTuple(costTuples, new int[] { 4, 0, 3 }); cp.AddTuple(costTuples, new int[] { 4, 1, 3 }); cp.AddTuple(costTuples, new int[] { 4, 2, 3 }); cp.AddTuple(costTuples, new int[] { 4, 3, 10 }); cp.AddTuple(costTuples, new int[] { 4, 4, 0 }); cp.AddTuple(costTuples, new int[] { 4, 5, 10 }); cp.AddTuple(costTuples, new int[] { 4, 6, 15 }); cp.AddTuple(costTuples, new int[] { 5, 0, 3 }); cp.AddTuple(costTuples, new int[] { 5, 1, 3 }); cp.AddTuple(costTuples, new int[] { 5, 2, 3 }); cp.AddTuple(costTuples, new int[] { 5, 3, 10 }); cp.AddTuple(costTuples, new int[] { 5, 4, 10 }); cp.AddTuple(costTuples, new int[] { 5, 5, 0 }); cp.AddTuple(costTuples, new int[] { 5, 6, 15 }); cp.AddTuple(costTuples, new int[] { 6, 0, 3 }); cp.AddTuple(costTuples, new int[] { 6, 1, 3 }); cp.AddTuple(costTuples, new int[] { 6, 2, 3 }); cp.AddTuple(costTuples, new int[] { 6, 3, 10 }); cp.AddTuple(costTuples, new int[] { 6, 4, 10 }); cp.AddTuple(costTuples, new int[] { 6, 5, 10 }); cp.AddTuple(costTuples, new int[] { 6, 6, 0 }); IIntVar[] transitionCost = cp.IntVarArray(nbTrucks - 1, 0, 1000); for (int i = 1; i < nbTrucks; i++) { IIntVar[] auxVars = new IIntVar[3]; auxVars[0] = truckConfigs[i - 1]; auxVars[1] = truckConfigs[i]; auxVars[2] = transitionCost[i - 1]; cp.Add(cp.AllowedAssignments(auxVars, costTuples)); } // constrain the volume of the orders in each truck cp.Add(cp.Pack(load, where, volumes, numUsed)); for (int i = 0; i < nbTrucks; i++) { cp.Add(cp.Le(load[i], cp.Element(maxTruckConfigLoad, truckConfigs[i]))); } // compatibility between the colors of an order and the configuration of its truck int[][] allowedContainerConfigs = new int[3][]; allowedContainerConfigs[0] = new int[] { 0, 3, 4, 6 }; allowedContainerConfigs[1] = new int[] { 1, 3, 5, 6 }; allowedContainerConfigs[2] = new int[] { 2, 4, 5, 6 }; for (int j = 0; j < nbOrders; j++) { IIntVar configOfContainer = cp.IntVar(allowedContainerConfigs[colors[j]]); cp.Add(cp.Eq(configOfContainer, cp.Element(truckConfigs, where[j]))); } // only one customer per truck for (int j = 0; j < nbOrders; j++) { cp.Add(cp.Eq(cp.Element(customerOfTruck, where[j]), customerOfOrder[j])); } // non used trucks are at the end for (int j = 1; j < nbTrucks; j++) { cp.Add(cp.Or(cp.Gt(load[j - 1], 0), cp.Eq(load[j], 0))); } // Dominance: the non used trucks keep the last used configuration cp.Add(cp.Gt(load[0], 0)); for (int i = 1; i < nbTrucks; i++) { cp.Add(cp.Or(cp.Gt(load[i], 0), cp.Eq(truckConfigs[i], truckConfigs[i - 1]))); } //Dominance: regroup deliveries with same configuration for (int i = nbTrucks - 2; i > 0; i--) { IConstraint Ct = cp.TrueConstraint(); for (int p = i + 1; p < nbTrucks; p++) { Ct = cp.And(cp.Neq(truckConfigs[p], truckConfigs[i - 1]), Ct); } cp.Add(cp.Or(cp.Eq(truckConfigs[i], truckConfigs[i - 1]), Ct)); } // Objective: first criterion for minimizing the cost for configuring and loading trucks // second criterion for minimizing the number of trucks IIntExpr obj1 = cp.Constant(0); for (int i = 0; i < nbTrucks; i++) { obj1 = cp.Sum(obj1, cp.Prod(cp.Element(truckCost, truckConfigs[i]), cp.Neq(load[i], 0))); } obj1 = cp.Sum(obj1, cp.Sum(transitionCost)); IIntExpr obj2 = numUsed; // Multicriteria lexicographic optimization cp.Add(cp.Minimize(cp.StaticLex(obj1, obj2))); cp.SetParameter(CP.DoubleParam.TimeLimit, 20); cp.SetParameter(CP.IntParam.LogPeriod, 50000); cp.Solve(); double[] obj = cp.GetObjValues(); Console.WriteLine("Configuration cost: " + (int)obj[0] + " Number of Trucks: " + (int)obj[1]); for (int i = 0; i < nbTrucks; i++) { if (cp.GetValue(load[i]) > 0) { Console.Write("Truck " + i + ": Config=" + cp.GetIntValue(truckConfigs[i]) + " Items= "); for (int j = 0; j < nbOrders; j++) { if (cp.GetValue(where[j]) == i) { Console.Write("<" + j + "," + colors[j] + "," + volumes[j] + "> "); } } Console.WriteLine(); } } }