Yes, I want solver to choose the optimal solution, but to get optimal solution in my case I would like the solver to choose between 3 (or any number of) alternative tasks.
Why not use "Disjunctive Graph" to solve it?
using System;using System.Collections.Generic;using Google.OrTools.ConstraintSolver;
class Machine{ public long Id { get; private set; } public string Name { get; private set; }
public Machine(long id) { Id = id; Name = $"Machine {Id}"; }}
public class Task{ public long Id { get; private set; } public long MachineId { get; private set; } public long Duration { get; set; } public string Name { get; private set; }
public Task(long jobId, long id, long machineId, long duration) { Id = id; MachineId = machineId; Duration = duration; Name = $"Job {jobId}; Task {id}; Machine {machineId}; Duration {duration}"; }}
public class Job{ public long Id { get; private set; } public List<Task> TaskList { get; private set; }
public Job(long id) { Id = id; TaskList = new List<Task>(); }
internal void AddTask(long machineId, long duration) { Task newTask = new Task(Id, GetNewTaskId(), machineId, duration); TaskList.Add(newTask); }
private long GetNewTaskId() { return TaskList.Count + 1; }}
public class JobShop{ /* Solves the Job Shop problem */
public static void Main(String[] args) { // Create example problem instance List<Machine> MachineList = new List<Machine> { new Machine(1), new Machine(2), new Machine(3) }; Job j1 = new Job(1); Job j2 = new Job(2); List<Job> JobList = new List<Job> { j1, j2 }; j1.AddTask(1, 2); j1.AddTask(2, 4); j2.AddTask(1, 3); j2.AddTask(2, 3); j2.AddTask(3, 3);
// ----- Create all intervals and vars -----
// Create solver Solver solver = new Solver("JobShop"); long horizon = 0; foreach (Job job in JobList) { foreach (Task task in job.TaskList) { horizon += task.Duration; } }
// Initialize dictionaries to hold references to task intervals Dictionary<long, IntervalVarVector> tasksByMachineId = new Dictionary<long, IntervalVarVector>(); foreach (var machine in MachineList) { tasksByMachineId[machine.Id] = new IntervalVarVector(); } Dictionary<long, IntervalVarVector> tasksByJobId = new Dictionary<long, IntervalVarVector>(); foreach (var job in JobList) { tasksByJobId[job.Id] = new IntervalVarVector(job.TaskList.Count); }
// Creates all individual interval variables and collect in dictionaries foreach (var job in JobList) { foreach (Task task in job.TaskList) { IntervalVar taskInterval = solver.MakeFixedDurationIntervalVar(0, horizon, task.Duration, false, task.Name); tasksByJobId[job.Id].Add(taskInterval); tasksByMachineId[task.MachineId].Add(taskInterval); } }
// ----- Create model -----
// Create precedences inside jobs foreach (var taskVec in tasksByJobId.Values) { for (int i = 0; i < taskVec.Count - 1; i++) { IntervalVar t1 = taskVec[i]; IntervalVar t2 = taskVec[i + 1]; Constraint precCt = solver.MakeIntervalVarRelation(t1, Solver.STARTS_AFTER_END, t2); solver.Add(precCt); } }
// 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 sequenceVec = new SequenceVarVector(); foreach (var machine in MachineList) { DisjunctiveConstraint disjCt = solver.MakeDisjunctiveConstraint(tasksByMachineId[machine.Id], machine.Name); solver.Add(disjCt); sequenceVec.Add(disjCt.SequenceVar()); }
// Create array of end_times of jobs. IntVarVector endsVec = new IntVarVector(); foreach (var taskVec in tasksByJobId.Values) { IntervalVar lastTask = taskVec[taskVec.Count - 1]; endsVec.Add(lastTask.EndExpr().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 rank all tasks on all machines. DecisionBuilder sequencePhase = solver.MakePhase( sequenceVec, 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 // 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(sequencePhase, objectivePhase);
// Search log const int kLogFrequency = 1000000; SearchMonitor searchLog = solver.MakeSearchLog(kLogFrequency, objectiveMonitor);
const long FLAGS_time_limit_in_ms = 10000; SearchLimit limit = null; if (FLAGS_time_limit_in_ms > 0) { limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); }
SolutionCollector collector = solver.MakeLastSolutionCollector(); collector.Add(sequenceVec);
// Search if (solver.Solve(mainPhase, searchLog, objectiveMonitor, limit, collector)) { Console.WriteLine(); foreach (var sequence in sequenceVec) { string msg = sequence.Name() + ": "; msg += string.Join(", ", collector.ForwardSequence(0, sequence)); Console.WriteLine(msg); } }
// Done Console.ReadLine(); }}
foreach (var taskVec in tasksByMachineId.Values) { foreach (var task in taskVec) { collector.Add(task.StartExpr().Var()); } }
Console.Write("Starting times:"); foreach (var s in collector.ForwardSequence(0, sequence)) { Console.Write(" " + collector.Value(0, tasksByMachineId[i][s].StartExpr().Var()).ToString()); } Console.WriteLine();
using System;
using System.Collections.Generic;using Google.OrTools.ConstraintSolver;
public class Machinepublic class CsJobShop{ /** * Solves the job shop problem. * We have 2 machines, 2 jobs with 2 tasks each. */ private static void Solve() { // Create example problem instance List<Machine> MachineList = new List<Machine> { new Machine(1), new Machine(2) }; List<Job> JobList = new List<Job> { new Job(1), new Job(2), new Job(3), new Job(4) }; long horizon = 0; foreach (Job job in JobList) { job.AddTask(1, 5); horizon += 5; job.AddTask(2, 4); horizon += 4; }
// ----- Create all intervals and vars -----
// Create solver Solver solver = new Solver("JobShop");
// Initialize dictionaries to hold references to task intervals Dictionary<long, IntervalVarVector> tasksByJobId = new Dictionary<long, IntervalVarVector>(); foreach (var job in JobList) { tasksByJobId[job.Id] = new IntervalVarVector(job.TaskList.Count); } Dictionary<long, IntervalVarVector> tasksByMachineId = new Dictionary<long, IntervalVarVector>(); foreach (var machine in MachineList) { tasksByMachineId[machine.Id] = new IntervalVarVector(); }
// Creates all individual interval variables and collect in dictionaries foreach (var job in JobList) { foreach (var task in job.TaskList) { IntervalVar interval = solver.MakeFixedDurationIntervalVar( 0, horizon, task.Duration, false, task.Name); tasksByJobId[job.Id].Add(interval); tasksByMachineId[task.MachineId].Add(interval); } }
// ----- Create model -----
// Create precedences inside jobs foreach (var taskVec in tasksByJobId.Values) { for (int i = 0; i < taskVec.Count - 1; i++) { IntervalVar currentTask = taskVec[i]; IntervalVar nextTask = taskVec[i + 1]; Constraint precCt = solver.MakeIntervalVarRelation( nextTask, Solver.STARTS_AFTER_END, currentTask); SearchLimit limit = null; long FLAGS_time_limit_in_ms = 1000; if (FLAGS_time_limit_in_ms > 0) { limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); }
SolutionCollector collector = solver.MakeLastSolutionCollector(); collector.Add(sequenceVec); foreach (var taskVec in tasksByMachineId.Values) { foreach (var task in taskVec) { collector.Add(task.StartExpr().Var()); } }
// Search if (solver.Solve(mainPhase, searchLog, objectiveMonitor, limit, collector)) { Console.WriteLine(); int i = 0; foreach (var sequence in sequenceVec) {
i++; Console.WriteLine($"{sequence.Name()}: {collector.ForwardSequence(0, sequence)}"); Console.Write(" Starting times:"); foreach (var s in seq) { Console.Write(" " + collector.Value(0, tasksByMachineId[i][s].StartExpr().Var()).ToString()); } Console.WriteLine(); } } }
public static void Main(String[] args) { Solve(); Console.ReadLine(); }}