internal OrToolsCache() { this.singletonVariableMap = new Dictionary <string, Tuple <SingletonVariableModel, IntVar> >(); this.aggregateVariableMap = new Dictionary <string, Tuple <AggregateVariableModel, IntVarVector> >(); this.bucketMap = new Dictionary <string, OrBucketVariableMap>(); Variables = new IntVarVector(); }
private static void assignNursesToDifferentShiftsEachDay(IntVar[,] nurses, IntVar[,] shifts, Solver solver) { for (int d = 0; d < numberDays; d++) { IntVarVector x = Enumerable.Range(0, numberNurses).Select(n => shifts[n, d]).ToArray(); solver.Add(solver.MakeAllDifferent(x)); IntVarVector y = Enumerable.Range(0, numberShifts).Select(s => nurses[s, d]).ToArray(); solver.Add(solver.MakeAllDifferent(y)); } }
public override IEnumerable <Constraint> GetConstraints(Solver solver) { var s = solver; foreach (var cell in Cells.Flatten()) { var c = s.MakeBetweenCt(cell, Min, Max); s.Add(c); yield return(c); } for (var i = 0; i < Size; i++) { var perpIdx = Enumerable.Range(0, Size).ToArray(); var rowOrColumnIdx = new[] { i }; { var vect = new IntVarVector(GetGroup(rowOrColumnIdx, perpIdx).ToList()).TrackClrObject(this); var c = s.MakeAllDifferent(vect).TrackClrObject(this); s.Add(c); yield return(c); } { var vect = new IntVarVector(GetGroup(perpIdx, rowOrColumnIdx).ToList()).TrackClrObject(this); var c = s.MakeAllDifferent(vect).TrackClrObject(this); s.Add(c); yield return(c); } } for (var i = 0; i < Div; i++) { var rowIdx = Enumerable.Range(i * Div, Div).ToArray(); for (var j = 0; j < Div; j++) { var columnIdx = Enumerable.Range(j * Div, Div).ToArray(); var vect = new IntVarVector(GetGroup(rowIdx, columnIdx).ToList()).TrackClrObject(this); var c = s.MakeAllDifferent(vect).TrackClrObject(this); s.Add(c); yield return(c); } } }
// TODO: TBD: PrepareSearchMonitors is perhaps closer to a fluent style... /// <summary> /// Override to prepare the <see cref="SearchMonitor"/> or monitors for the /// <paramref name="agent"/> and corresponding <see cref="Solver"/>. If called, the base /// method should be called after preparing any other required Monitors. By default, a /// single <see cref="SolutionCollector"/> is prepared. /// </summary> /// <param name="agent"></param> /// <param name="variables"></param> /// <returns></returns> protected virtual ISearchAgent PrepareSearchMonitors(ISearchAgent agent, params IntVar[] variables) { if (!agent.HasSolutionCollector <SolutionCollector>()) { // Start with a single all-solution-collector. agent.Monitor(a => { var m = a.Solver.MakeAllSolutionCollector(); var vect = new IntVarVector().TrackClrObject(this); foreach (var v in variables) { vect.Add(v); } m.Add(vect); return(m); }); } return(agent); }
private static void CreateConstraints(Solver model, IntVar[] x) { for (int group = 0; group < 9; group++) { // Create row constraints IntVarVector row = new IntVarVector(); for (int i = 0; i < 9; i++) { int index = (group * 9) + i; row.Add(x[index]); } var rowConstraint = model.MakeAllDifferent(row); model.Add(rowConstraint); // Create column constraints IntVarVector col = new IntVarVector(); for (int i = 0; i < 9; i++) { int index = (i * 9) + group; col.Add(x[index]); } var colConstraint = model.MakeAllDifferent(col); model.Add(colConstraint); // Create region constraints int regionStartX = (group % 3) * 3; int regionStartY = (group / 3) * 3; IntVarVector region = new IntVarVector(); for (int i = 0; i < 9; i++) { int deltaX = (i % 3); int deltaY = (i / 3); int xLoc = regionStartX + deltaX; int yLoc = regionStartY + deltaY; int index = (yLoc * 9) + xLoc; region.Add(x[index]); } var regionConstraint = model.MakeAllDifferent(region); model.Add(regionConstraint); } }
public override IEnumerable <Constraint> GetConstraints(Solver source) { for (var i = MinimumValue; i < MaximumValue; i++) { var perpendicularIndexes = Enumerable.Range(0, Size).ToArray(); var rowOrColumnIndexes = new[] { i }; { var vector = new IntVarVector(GetGroup(rowOrColumnIndexes, perpendicularIndexes).ToList()).TrackClrObject(this); var c = source.MakeAllDifferent(vector).TrackClrObject(this); source.Add(c); yield return(c); } { var vector = new IntVarVector(GetGroup(perpendicularIndexes, rowOrColumnIndexes).ToList()).TrackClrObject(this); var c = source.MakeAllDifferent(vector).TrackClrObject(this); source.Add(c); yield return(c); } } for (var i = MinimumValue; i < BlockSize; i++) { var rowIndexes = Enumerable.Range(i * BlockSize, BlockSize).ToArray(); for (var j = MinimumValue; j < BlockSize; j++) { var columnIndexes = Enumerable.Range(j * BlockSize, BlockSize).ToArray(); var vector = new IntVarVector(GetGroup(rowIndexes, columnIndexes).ToList()).TrackClrObject(this); var c = source.MakeAllDifferent(vector).TrackClrObject(this); source.Add(c); yield return(c); } } }
/// <summary> /// Provides a helpful C-Sharp friendly extension method for <see /// cref="Solver.MakePhase(IntVarVector, VariableChooser, int)"/>. /// </summary> /// <param name="solver"></param> /// <param name="variables"></param> /// <param name="varChooser"></param> /// <param name="evalStrategy"></param> /// <returns></returns> public static DecisionBuilder MakePhase(this Solver solver, IntVarVector variables, VariableChooser varChooser, EvaluatorStrategy evalStrategy) => solver.MakePhase(variables, varChooser, evalStrategy.ToInt());
/// <summary> /// Provides a helpful C-Sharp friendly extension method for <see /// cref="Solver.MakePhase(IntVarVector, int, int)"/>. /// </summary> /// <param name="solver"></param> /// <param name="variables"></param> /// <param name="varStrategy"></param> /// <param name="valStrategy"></param> /// <returns></returns> public static DecisionBuilder MakePhase(this Solver solver, IntVarVector variables, IntVarStrategy varStrategy, IntValueStrategy valStrategy) => solver.MakePhase(variables, varStrategy.ToInt(), valStrategy.ToInt());
public static void Solve(FlexibleJobShopData data, bool chatty = false) { // Compute horizon var horizon = data.JobList.Sum( j => j.TaskList.Sum( t => t.Durations.Max())); // ----- Create all intervals and vars ----- /* * // Use some profiling and change the default parameters of the solver * SolverParameters solverParams = new SolverParameters(); * // Change the profile level * solverParams.profile_level = SolverParameters.NORMAL_PROFILING; * // Constraint programming engine * Solver solver = new Solver("JobShop", solverParams); */ // Constraint programming engine Solver solver = new Solver($"FlexibleJobShop: {data.Name}"); // Initialize dictionaries to hold references to task intervals var tasksByJobId = new Dictionary <uint, List <TaskAlternative> >(); foreach (var job in data.JobList) { tasksByJobId[job.Id] = new List <TaskAlternative>(job.TaskList.Count); } var tasksByMachineId = new Dictionary <uint, IntervalVarVector>(); foreach (var machine in data.MachineList) { tasksByMachineId[machine.Id] = new IntervalVarVector(); } // Creates all individual interval variables and collect in dictionaries foreach (var job in data.JobList) { foreach (var task in job.TaskList) { var alternative = new TaskAlternative(job.Id); tasksByJobId[job.Id].Add(alternative); var activeVariables = new IntVarVector(); var hasAlternatives = task.AlternativesCount > 1; for (int alt = 0; alt < task.AlternativesCount; alt++) { var machine = task.Machines[alt]; var duration = task.Durations[alt]; var name = $"{task.Name}; Alternative {alt}: {machine.Name}, Duration {duration}"; IntervalVar interval = solver.MakeFixedDurationIntervalVar(0, horizon, duration, hasAlternatives, name); alternative.Add(interval); tasksByMachineId[machine.Id].Add(interval); if (hasAlternatives) { activeVariables.Add(interval.PerformedExpr().Var()); } } alternative.AlternativeVar = solver.MakeIntVar(0, task.AlternativesCount - 1, task.Name); if (hasAlternatives) { solver.Add(solver.MakeMapDomain(alternative.AlternativeVar, activeVariables)); } } } // ----- Create model ----- // Create precedences inside jobs foreach (var taskAltList in tasksByJobId.Values) { for (int i = 0; i < taskAltList.Count - 1; i++) { TaskAlternative currentTaskAlt = taskAltList[i]; TaskAlternative nextTaskAlt = taskAltList[i + 1]; foreach (var alt1 in currentTaskAlt) { foreach (var alt2 in nextTaskAlt) { solver.Add(solver.MakeIntervalVarRelation( alt2, Solver.STARTS_AFTER_END, alt1)); } } } } // Collect alternative variables. IntVarVector alternativeVariableVec = new IntVarVector(); foreach (var taskAltList in tasksByJobId.Values) { foreach (var taskAlt in taskAltList) { if (!taskAlt.AlternativeVar.Bound()) { alternativeVariableVec.Add(taskAlt.AlternativeVar); } } } // Add disjunctive constraints on unary resources, and create // sequence variables. A sequence variable is a dedicated variable // whose job is to sequence interval variables. SequenceVarVector allSequences = new SequenceVarVector(); foreach (var machine in data.MachineList) { DisjunctiveConstraint disjCt = solver.MakeDisjunctiveConstraint( tasksByMachineId[machine.Id], machine.Name); solver.Add(disjCt); allSequences.Add(disjCt.SequenceVar()); } // Create array of end_times of jobs IntVarVector endsVec = new IntVarVector(); foreach (var taskAltList in tasksByJobId.Values) { TaskAlternative lastTaskAlt = taskAltList.Last(); foreach (var alt in lastTaskAlt) { endsVec.Add(alt.SafeEndExpr(-1).Var()); } } // Objective: minimize the makespan (maximum end times of all tasks) // of the problem. IntVar objectiveVar = solver.MakeMax(endsVec).Var(); OptimizeVar objectiveMonitor = solver.MakeMinimize(objectiveVar, 1); // ----- Search monitors and decision builder ----- // This decision builder will assign all alternative variables. DecisionBuilder alternativePhase = solver.MakePhase(alternativeVariableVec, Solver.CHOOSE_MIN_SIZE, Solver.ASSIGN_MIN_VALUE); // This decision builder will rank all tasks on all machines. DecisionBuilder sequencePhase = solver.MakePhase(allSequences, Solver.SEQUENCE_DEFAULT); // After the ranking of tasks, the schedule is still loose and any // task can be postponed at will. But, because the problem is now a PERT // (http://en.wikipedia.org/wiki/Program_Evaluation_and_Review_Technique), // we can schedule each task at its earliest start time. This is // conveniently done by fixing the objective variable to its // minimum value. DecisionBuilder objectivePhase = solver.MakePhase(objectiveVar, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); // The main decision builder (ranks all tasks, then fixes the // objective_variable). DecisionBuilder mainPhase = solver.Compose(alternativePhase, sequencePhase, objectivePhase); // Search log const int kLogFrequency = 1000000; SearchMonitor searchLog = solver.MakeSearchLog(kLogFrequency, objectiveMonitor); const long FLAGS_time_limit_in_ms = 1000 * 60 * 20; SearchLimit limit = null; if (FLAGS_time_limit_in_ms > 0) { limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); } SolutionCollector collector = solver.MakeLastSolutionCollector(); collector.AddObjective(objectiveVar); collector.Add(alternativeVariableVec); collector.Add(allSequences); foreach (var taskVec in tasksByMachineId.Values) { foreach (var task in taskVec) { collector.Add(task.StartExpr().Var()); } } // ----- Search ----- bool solutionFound = solver.Solve(mainPhase, searchLog, objectiveMonitor, limit, collector); if (solutionFound) { // The index of the solution from the collector const int SOLUTION_INDEX = 0; Assignment solution = collector.Solution(SOLUTION_INDEX); Console.WriteLine(); uint machineIdx = 0; foreach (var seq in allSequences) { machineIdx++; var taskSeq = collector.ForwardSequence(SOLUTION_INDEX, seq); Console.WriteLine($"{seq.Name()}:"); Console.WriteLine(" Tasks: " + string.Join(", ", taskSeq)); //foreach (var taskIndex in storedSequence) //{ // IntervalVar task = sequence.Interval(taskIndex); // long startMin = solution.StartMin(task); // long startMax = solution.StartMax(task); // if (startMin == startMax) // { // Console.WriteLine($"Task {task.Name()} starts at {startMin}."); // } // else // { // Console.WriteLine($"Task {task.Name()} starts between {startMin} and {startMax}."); // } //} Console.WriteLine(); Console.Write(" Starting times:"); foreach (var s in taskSeq) { Console.Write(" " + collector.Value(0, tasksByMachineId[machineIdx][s].StartExpr().Var()).ToString()); } Console.WriteLine(); } //var x = tasksByMachineId[1][0].StartExpr().Var(); //var xValStr = collector.Value(0, x).ToString(); Console.WriteLine("objective function value = " + solution.ObjectiveValue()); //TEMP } // Save profile in file //solver.ExportProfilingOverview("profile.txt"); // Done solver.EndSearch(); }