package jobshop.encodings; import jobshop.Instance; import java.util.Arrays; import java.util.Comparator; import java.util.Optional; import java.util.stream.IntStream; /** Encoding of a solution by the ordering of tasks on each machine. */ public final class ResourceOrder extends Encoding { // for each machine m, taskByMachine[m] is an array of tasks to be // executed on this machine in the same order final Task[][] tasksByMachine; // for each machine, indicate how many tasks have been initialized final int[] nextFreeSlot; /** Creates a new empty resource order. */ public ResourceOrder(Instance instance) { super(instance); // matrix of null elements (null is the default value of objects) tasksByMachine = new Task[instance.numMachines][instance.numJobs]; // no task scheduled on any machine (0 is the default value) nextFreeSlot = new int[instance.numMachines]; } /** Creates a resource order from a schedule. */ public ResourceOrder(Schedule schedule) { super(schedule.instance); Instance pb = schedule.instance; this.tasksByMachine = new Task[pb.numMachines][]; this.nextFreeSlot = new int[instance.numMachines]; for(int m = 0; m new Task(j, pb.task_with_machine(j, machine))) // all tasks on this machine (one per job) .sorted(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) // sorted by start time .toArray(Task[]::new); // as new array and store in tasksByMachine // indicate that all tasks have been initialized for machine m nextFreeSlot[m] = instance.numJobs; } } /** Adds the given task to the queue of the given machine. */ public void addTaskToMachine(int machine, Task task) { if(instance.machine(task) != machine) { throw new RuntimeException("Task " + task + " cannot be scheduled on machine "+machine); } tasksByMachine[machine][nextFreeSlot[machine]] = task; nextFreeSlot[machine] += 1; } /** Returns the i-th task scheduled on a particular machine. * * @param machine Machine on which the task to retrieve is scheduled. * @param taskIndex Index of the task in the queue for this machine. * @return The i-th task scheduled on a machine. */ public Task getTaskOfMachine(int machine, int taskIndex) { return tasksByMachine[machine][taskIndex]; } /** Exchange the order of two tasks that are scheduled on a given machine. * * @param machine Machine on which the two tasks appear (line on which to perform the exchange) * @param indexTask1 Position of the first task in the machine's queue * @param indexTask2 Position of the second task in the machine's queue */ public void swapTasks(int machine, int indexTask1, int indexTask2) { Task tmp = tasksByMachine[machine][indexTask1]; tasksByMachine[machine][indexTask1] = tasksByMachine[machine][indexTask2]; tasksByMachine[machine][indexTask2] = tmp; } @Override public Optional toSchedule() { // indicate for each task that have been scheduled, its start time Schedule schedule = new Schedule(instance); // for each job, how many tasks have been scheduled (0 initially) int[] nextToScheduleByJob = new int[instance.numJobs]; // for each machine, how many tasks have been scheduled (0 initially) int[] nextToScheduleByMachine = new int[instance.numMachines]; // for each machine, earliest time at which the machine can be used int[] releaseTimeOfMachine = new int[instance.numMachines]; // loop while there remains a job that has unscheduled tasks while(IntStream.range(0, instance.numJobs).anyMatch(m -> nextToScheduleByJob[m] < instance.numTasks)) { // selects a task that has no unscheduled predecessor on its job and machine : // - it is the next to be schedule on a machine // - it is the next to be scheduled on its job // If there is no such task, we have cyclic dependency and the solution is invalid. Optional schedulable = IntStream.range(0, instance.numMachines) // all machines ... .filter(m -> nextToScheduleByMachine[m] < instance.numJobs) // ... with unscheduled jobs .mapToObj(m -> this.tasksByMachine[m][nextToScheduleByMachine[m]]) // tasks that are next to schedule on a machine ... .filter(task -> task.task == nextToScheduleByJob[task.job]) // ... and on their job .findFirst(); // select the first one if any if(schedulable.isPresent()) { // we have a schedulable task, lets call it t Task t = schedulable.get(); int machine = instance.machine(t.job, t.task); // compute the earliest start time (est) of the task int est = t.task == 0 ? 0 : schedule.endTime(t.job, t.task-1); est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]); schedule.setStartTime(t.job, t.task, est); // mark the task as scheduled nextToScheduleByJob[t.job]++; nextToScheduleByMachine[machine]++; // increase the release time of the machine releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task); } else { // no tasks are schedulable, there is no solution for this resource ordering return Optional.empty(); } } // we exited the loop : all tasks have been scheduled successfully return Optional.of(schedule); } /** Creates an exact copy of this resource order. * * May fail if the resource order does not represent a valid solution. */ public ResourceOrder copy() { var schedule = this.toSchedule(); if (schedule.isEmpty()) { throw new RuntimeException("Cannot copy an invalid ResourceOrder"); } else { return new ResourceOrder(schedule.get()); } } @Override public String toString() { StringBuilder s = new StringBuilder(); for(int m=0; m < instance.numMachines; m++) { s.append("Machine ").append(m).append(" : "); for(int j=0; j