Improvments for year 2020-2021
This commit is contained in:
		
							parent
							
								
									27a970026f
								
							
						
					
					
						commit
						1d4883388b
					
				
					 22 changed files with 362 additions and 147 deletions
				
			
		|  | @ -7,7 +7,7 @@ plugins { | |||
| group 'jobshop' | ||||
| //version '0.1' | ||||
| 
 | ||||
| sourceCompatibility = 8 | ||||
| sourceCompatibility = 11 | ||||
| 
 | ||||
| 
 | ||||
| application { | ||||
|  |  | |||
							
								
								
									
										11
									
								
								instances/aaa2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								instances/aaa2
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| # Example 2 | ||||
| # This is the example used as a support for exercises during classes (year 2020-2021) | ||||
| 4 3 # num-jobs num-tasks | ||||
| 0 1 1 3 2 2 | ||||
| 1 8 0 5 2 10 | ||||
| 0 5 2 4 1 8 | ||||
| 2 4 0 10 1 6 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										20
									
								
								instances/aaa3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								instances/aaa3
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| # Example 3 | ||||
| # This problem has deterministic results for all greedy solver variants. | ||||
| # Makespan for each variant: | ||||
| # - SPT: 53 | ||||
| # - LPT: 92 | ||||
| # - SRPT: 78 | ||||
| # - LRPT: 54 | ||||
| # - EST_SPT: 48 | ||||
| # - EST_LPT: 56 | ||||
| # - EST_SRPT: 53 | ||||
| # - EST_LRPT: 56 | ||||
| 4 3 # num-jobs num-tasks | ||||
| 0 1 1 2 2 15 | ||||
| 1 4 0 5 2 11 | ||||
| 0 7 2 8 1 13 | ||||
| 2 3 0 18 1 6 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -55,6 +55,8 @@ public class BestKnownResults { | |||
|     static { | ||||
|         bests = new HashMap<>(); | ||||
|         bests.put("aaa1", 11); | ||||
|         bests.put("aaa2", 29); | ||||
|         bests.put("aaa3", 41); | ||||
|         bests.put("abz5", 1234); | ||||
|         bests.put("abz6", 943); | ||||
|         bests.put("abz7", 656); | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| package jobshop; | ||||
| 
 | ||||
| import jobshop.encodings.JobNumbers; | ||||
| import jobshop.encodings.Schedule; | ||||
| import jobshop.solvers.GreedySolver; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Paths; | ||||
|  | @ -12,25 +14,22 @@ public class DebuggingMain { | |||
|             // load the aaa1 instance | ||||
|             Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); | ||||
| 
 | ||||
|             // construit une solution dans la représentation par | ||||
|             // numéro de jobs : [0 1 1 0 0 1] | ||||
|             // Note : cette solution a aussi été vue dans les exercices (section 3.3) | ||||
|             //        mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2] | ||||
|             // builds a solution in the job-numbers encoding [0 1 1 0 0 1] | ||||
|             JobNumbers enc = new JobNumbers(instance); | ||||
|             enc.jobs[enc.nextToSet++] = 0; | ||||
|             enc.jobs[enc.nextToSet++] = 1; | ||||
|             enc.jobs[enc.nextToSet++] = 1; | ||||
|             enc.jobs[enc.nextToSet++] = 0; | ||||
|             enc.jobs[enc.nextToSet++] = 0; | ||||
|             enc.jobs[enc.nextToSet++] = 1; | ||||
|             enc.addTask(0); | ||||
|             enc.addTask(1); | ||||
|             enc.addTask(1); | ||||
|             enc.addTask(0); | ||||
|             enc.addTask(0); | ||||
|             enc.addTask(1); | ||||
| 
 | ||||
|             System.out.println("\nENCODING: " + enc); | ||||
| 
 | ||||
|             Schedule sched = enc.toSchedule(); | ||||
|             // TODO: make it print something meaningful by implementing the Schedule.toString() method | ||||
|             System.out.println("SCHEDULE: " + sched); | ||||
|             System.out.println("VALID: " + sched.isValid()); | ||||
|             System.out.println("MAKESPAN: " + sched.makespan()); | ||||
|             Schedule schedule = enc.toSchedule(); | ||||
|             System.out.println("VALID: " + schedule.isValid()); | ||||
|             System.out.println("MAKESPAN: " + schedule.makespan()); | ||||
|             System.out.println("SCHEDULE: " + schedule.toString()); | ||||
|             System.out.println("GANTT: " + schedule.asciiGantt()); | ||||
| 
 | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|  |  | |||
|  | @ -7,8 +7,10 @@ import java.util.ArrayList; | |||
| import java.util.Arrays; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import jobshop.solvers.*; | ||||
| import jobshop.solvers.neighborhood.Nowicki; | ||||
| import net.sourceforge.argparse4j.ArgumentParsers; | ||||
| import net.sourceforge.argparse4j.inf.ArgumentParser; | ||||
| import net.sourceforge.argparse4j.inf.ArgumentParserException; | ||||
|  | @ -20,14 +22,7 @@ import net.sourceforge.argparse4j.inf.Namespace; | |||
|  */ | ||||
| public class Main { | ||||
| 
 | ||||
|     /** All solvers available in this program */ | ||||
|     private static final HashMap<String, Solver> solvers; | ||||
|     static { | ||||
|         solvers = new HashMap<>(); | ||||
|         solvers.put("basic", new BasicSolver()); | ||||
|         solvers.put("random", new RandomSolver()); | ||||
|         // TODO: add new solvers here | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     public static void main(String[] args) { | ||||
|  | @ -72,14 +67,7 @@ public class Main { | |||
|         // Get the list of solvers that we should benchmark. | ||||
|         // We also check that we have a solver available for the given name and print an error message otherwise. | ||||
|         List<String> solversToTest = ns.getList("solver"); | ||||
|         for(String solverName : solversToTest) { | ||||
|             if(!solvers.containsKey(solverName)) { | ||||
|                 System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible."); | ||||
|                 System.err.println("       Available solvers: " + solvers.keySet().toString()); | ||||
|                 System.err.println("       You can provide your own solvers by adding them to the `Main.solvers` HashMap."); | ||||
|                 System.exit(0); | ||||
|             } | ||||
|         } | ||||
|         List<Solver> solvers = solversToTest.stream().map(Solver::getSolver).collect(Collectors.toList()); | ||||
| 
 | ||||
|         // retrieve all instances on which we should run the solvers. | ||||
|         List<String> instances = new ArrayList<>(); | ||||
|  | @ -126,11 +114,10 @@ public class Main { | |||
|                 output.printf("%-8s %-5s %4d      ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown); | ||||
| 
 | ||||
|                 // run all selected solvers on the instance and print the results | ||||
|                 for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) { | ||||
|                 for(int solverId = 0 ; solverId < solvers.size() ; solverId++) { | ||||
|                     // Select the next solver to run. Given the solver name passed on the command line, | ||||
|                     // we lookup the `Main.solvers` hash map to get the solver object with the given name. | ||||
|                     String solverName = solversToTest.get(solverId); | ||||
|                     Solver solver = solvers.get(solverName); | ||||
|                     Solver solver = solvers.get(solverId); | ||||
| 
 | ||||
|                     // start chronometer and compute deadline for the solver to provide a result. | ||||
|                     long start = System.currentTimeMillis(); | ||||
|  | @ -168,7 +155,7 @@ public class Main { | |||
| 
 | ||||
| 
 | ||||
|         } catch (Exception e) { | ||||
|             // there was uncought exception, print the stack trace and exit with error. | ||||
|             // there was uncaught exception, print the stack trace and exit with error. | ||||
|             e.printStackTrace(); | ||||
|             System.exit(1); | ||||
|         } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| package jobshop; | ||||
| 
 | ||||
| import java.util.Optional; | ||||
| import jobshop.encodings.Schedule; | ||||
| 
 | ||||
| public class Result { | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +0,0 @@ | |||
| package jobshop; | ||||
| 
 | ||||
| public interface Solver { | ||||
| 
 | ||||
|     Result solve(Instance instance, long deadline); | ||||
| 
 | ||||
| } | ||||
|  | @ -1,4 +1,6 @@ | |||
| package jobshop; | ||||
| package jobshop.encodings; | ||||
| 
 | ||||
| import jobshop.Instance; | ||||
| 
 | ||||
| public abstract class Encoding { | ||||
| 
 | ||||
|  | @ -1,8 +1,6 @@ | |||
| package jobshop.encodings; | ||||
| 
 | ||||
| import jobshop.Encoding; | ||||
| import jobshop.Instance; | ||||
| import jobshop.Schedule; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.Comparator; | ||||
|  | @ -45,11 +43,15 @@ public class JobNumbers extends Encoding { | |||
|                     .min(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) | ||||
|                     .get(); | ||||
| 
 | ||||
|             this.jobs[nextToSet++] = next.job; | ||||
|             this.addTask(next.job); | ||||
|             nextOnJob[next.job] += 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void addTask(int jobNumber) { | ||||
|         this.jobs[nextToSet++] = jobNumber; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Schedule toSchedule() { | ||||
|         // time at which each machine is going to be freed | ||||
|  | @ -59,22 +61,22 @@ public class JobNumbers extends Encoding { | |||
|         int[] nextTask = new int[instance.numJobs]; | ||||
| 
 | ||||
|         // for each task, its start time | ||||
|         int[][] startTimes = new int[instance.numJobs][instance.numTasks]; | ||||
|         Schedule schedule = new Schedule(instance); | ||||
| 
 | ||||
|         // compute the earliest start time for every task of every job | ||||
|         for(int job : jobs) { | ||||
|             int task = nextTask[job]; | ||||
|             int machine = instance.machine(job, task); | ||||
|             // earliest start time for this task | ||||
|             int est = task == 0 ? 0 : startTimes[job][task-1] + instance.duration(job, task-1); | ||||
|             int est = task == 0 ? 0 : schedule.endTime(job, task-1); | ||||
|             est = Math.max(est, nextFreeTimeResource[machine]); | ||||
| 
 | ||||
|             startTimes[job][task] = est; | ||||
|             schedule.setStartTime(job, task, est); | ||||
|             nextFreeTimeResource[machine] = est + instance.duration(job, task); | ||||
|             nextTask[job] = task + 1; | ||||
|         } | ||||
| 
 | ||||
|         return new Schedule(instance, startTimes); | ||||
|         return schedule; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  |  | |||
|  | @ -1,8 +1,6 @@ | |||
| package jobshop.encodings; | ||||
| 
 | ||||
| import jobshop.Encoding; | ||||
| import jobshop.Instance; | ||||
| import jobshop.Schedule; | ||||
| 
 | ||||
| import java.util.Comparator; | ||||
| import java.util.Optional; | ||||
|  | @ -53,10 +51,21 @@ public class ResourceOrder extends Encoding { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void addTaskToMachine(int machine, Task task) { | ||||
|         tasksByMachine[machine][nextFreeSlot[machine]] = task; | ||||
|         nextFreeSlot[machine] += 1; | ||||
|     } | ||||
| 
 | ||||
|     public void swapTasks(int machine, int indexFirstTask, int indexSecondTask) { | ||||
|         Task tmp = tasksByMachine[machine][indexFirstTask]; | ||||
|         tasksByMachine[machine][indexFirstTask] = tasksByMachine[machine][indexSecondTask]; | ||||
|         tasksByMachine[machine][indexSecondTask] = tmp; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Schedule toSchedule() { | ||||
|         // indicate for each task that have been scheduled, its start time | ||||
|         int [][] startTimes = new int [instance.numJobs][instance.numTasks]; | ||||
|         Schedule schedule = new Schedule(instance); | ||||
| 
 | ||||
|         // for each job, how many tasks have been scheduled (0 initially) | ||||
|         int[] nextToScheduleByJob = new int[instance.numJobs]; | ||||
|  | @ -88,9 +97,9 @@ public class ResourceOrder extends Encoding { | |||
|                 int machine = instance.machine(t.job, t.task); | ||||
| 
 | ||||
|                 // compute the earliest start time (est) of the task | ||||
|                 int est = t.task == 0 ? 0 : startTimes[t.job][t.task-1] + instance.duration(t.job, t.task-1); | ||||
|                 int est = t.task == 0 ? 0 : schedule.endTime(t.job, t.task-1); | ||||
|                 est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]); | ||||
|                 startTimes[t.job][t.task] = est; | ||||
|                 schedule.setStartTime(t.job, t.task, est); | ||||
| 
 | ||||
|                 // mark the task as scheduled | ||||
|                 nextToScheduleByJob[t.job]++; | ||||
|  | @ -103,7 +112,7 @@ public class ResourceOrder extends Encoding { | |||
|             } | ||||
|         } | ||||
|         // we exited the loop : all tasks have been scheduled successfully | ||||
|         return new Schedule(instance, startTimes); | ||||
|         return schedule; | ||||
|     } | ||||
| 
 | ||||
|     /** Creates an exact copy of this resource order. */ | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| package jobshop; | ||||
| package jobshop.encodings; | ||||
| 
 | ||||
| 
 | ||||
| import jobshop.encodings.Task; | ||||
| import jobshop.Instance; | ||||
| 
 | ||||
| import java.util.*; | ||||
| import java.util.stream.IntStream; | ||||
|  | @ -12,16 +11,18 @@ public class Schedule { | |||
|     // times[j][i] is the start time of task (j,i) : i^th task of the j^th job | ||||
|     final int[][] times; | ||||
| 
 | ||||
|     public Schedule(Instance pb, int[][] times) { | ||||
|     /** Creates a new schedule for the given instance where all start times are uninitialized. */ | ||||
|     public Schedule(Instance pb) { | ||||
|         this.pb = pb; | ||||
|         this.times = new int[pb.numJobs][]; | ||||
|         for(int j = 0 ; j < pb.numJobs ; j++) { | ||||
|             this.times[j] = Arrays.copyOf(times[j], pb.numTasks); | ||||
|             this.times[j] = new int[pb.numTasks]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public int startTime(int job, int task) { | ||||
|         return times[job][task]; | ||||
|     /** Sets the start time of the given task. */ | ||||
|     public void setStartTime(int job, int task, int startTime) { | ||||
|         times[job][task] = startTime; | ||||
|     } | ||||
| 
 | ||||
|     /** Returns true if this schedule is valid (no constraint is violated) */ | ||||
|  | @ -55,22 +56,38 @@ public class Schedule { | |||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** Makespan of the solution. | ||||
|      * The makespan is the end time of the latest finishing task. | ||||
|      */ | ||||
|     public int makespan() { | ||||
|         int max = -1; | ||||
|         for(int j = 0 ; j<pb.numJobs ; j++) { | ||||
|             max = Math.max(max, startTime(j, pb.numTasks-1) + pb.duration(j, pb.numTasks -1)); | ||||
|             max = Math.max(max, endTime(j, pb.numTasks-1)); | ||||
|         } | ||||
|         return max; | ||||
|     } | ||||
| 
 | ||||
|     /** Start time of the given task. */ | ||||
|     public int startTime(int job, int task) { | ||||
|         return times[job][task]; | ||||
|     } | ||||
| 
 | ||||
|     /** Start time of the given task. */ | ||||
|     public int startTime(Task task) { | ||||
|         return startTime(task.job, task.task); | ||||
|     } | ||||
| 
 | ||||
|     public int endTime(Task task) { | ||||
|         return startTime(task) + pb.duration(task.job, task.task); | ||||
|     /** End time of the given task. */ | ||||
|     public int endTime(int job, int task) { | ||||
|         return startTime(job, task) + pb.duration(job, task); | ||||
|     } | ||||
| 
 | ||||
|     /** End time of the given task. */ | ||||
|     public int endTime(Task task) { | ||||
|         return endTime(task.job, task.task); | ||||
|     } | ||||
| 
 | ||||
|     /** Returns true if the given sequence of task is a critical path of the schedule. */ | ||||
|     public boolean isCriticalPath(List<Task> path) { | ||||
|         if(startTime(path.get(0)) != 0) { | ||||
|             return false; | ||||
|  | @ -85,6 +102,10 @@ public class Schedule { | |||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** Computes a critical path of the schedule. | ||||
|      * | ||||
|      * @return A sequence of task along a critical path. | ||||
|      */ | ||||
|     public List<Task> criticalPath() { | ||||
|         // select task with greatest end time | ||||
|         Task ldd = IntStream.range(0, pb.numJobs) | ||||
|  | @ -117,7 +138,7 @@ public class Schedule { | |||
|                 if(endTime(predOnJob) == startTime(cur)) | ||||
|                     latestPredecessor = Optional.of(predOnJob); | ||||
|             } | ||||
|             if(!latestPredecessor.isPresent()) { | ||||
|             if(latestPredecessor.isEmpty()) { | ||||
|                 // no latest predecessor found yet, look among tasks executing on the same machine | ||||
|                 latestPredecessor = IntStream.range(0, pb.numJobs) | ||||
|                         .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) | ||||
|  | @ -132,4 +153,81 @@ public class Schedule { | |||
|         assert isCriticalPath(path); | ||||
|         return path; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         sb.append("\nStart times of all tasks:\n"); | ||||
|         for(int job=0; job<pb.numJobs; job++) { | ||||
|             sb.append("Job "); | ||||
|             sb.append(job); | ||||
|             sb.append(": "); | ||||
|             for(int task=0; task<pb.numTasks; task++) { | ||||
|                 sb.append(String.format("%5d",  startTime(job, task))); | ||||
| 
 | ||||
|             } | ||||
|             sb.append("\n"); | ||||
|         } | ||||
|         return sb.toString(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a string containing the Gantt chart of the given schedule, in ASCII art. | ||||
|      * | ||||
|      * Each line of the Gantt chart, contains the tasks of a particular job. Each character in the output represents a | ||||
|      * fixed number of time units | ||||
|      * For each task, we indicate : | ||||
|      *  - the machine on which the task must be executed | ||||
|      *  - whether this task is on the critical path (task on the critical path are filled in with stars). | ||||
|      */ | ||||
|     public String asciiGantt() { | ||||
|         var criticalPath = this.criticalPath(); | ||||
|         int minTaskDur = IntStream.range(0, pb.numJobs).flatMap(job -> IntStream.range(0, pb.numTasks).map(task -> pb.duration(job, task))).min().getAsInt(); | ||||
|         // time units by character | ||||
|         int charsPerTimeUnit = minTaskDur >= 5 ? 1 : (5 / minTaskDur) +1; | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         sb.append("\nGantt Chart\n"); | ||||
|         for(int job=0; job<pb.numJobs; job++) { | ||||
|             sb.append(String.format("Job %2d: ", job)); | ||||
|             int cursor = 0; | ||||
|             for(int task=0; task<pb.numTasks; task++) { | ||||
|                 Task t = new Task(job, task); | ||||
|                 var st = startTime(job, task); | ||||
|                 // add spaces until the start of our task | ||||
|                 sb.append(" ".repeat(charsPerTimeUnit * (st - cursor ))); | ||||
|                 sb.append(formatTask(t, charsPerTimeUnit, criticalPath.contains(t))); | ||||
| 
 | ||||
|                 cursor = endTime(job, task); | ||||
|             } | ||||
|             sb.append("\n"); | ||||
|         } | ||||
|         return sb.toString(); | ||||
|     } | ||||
| 
 | ||||
|     /** Utility function to display a set of characters representing a task in a gantt chart. | ||||
|      * | ||||
|      * @param t Task to display | ||||
|      * @param charPerTimeUnit How many characters to represent a time unit. | ||||
|      * @param isCritical Is the task on the critical path. | ||||
|      * @return Ascii representation of the task. Length is the duration * charPerTimeUnit. | ||||
|      */ | ||||
|     String formatTask(Task t, int charPerTimeUnit, boolean isCritical) { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         String fill = isCritical ? "*" : "-"; | ||||
|         int dur = pb.duration(t); | ||||
|         int machine = pb.machine(t); | ||||
|         int stringLength = dur * charPerTimeUnit; | ||||
|         int charsForMachine = machine < 10 ? 1 : 2; | ||||
|         int numSpaces = stringLength - 2 - charsForMachine; // we use 2 chars for '[' and '[' + 1 or 2 for the machine number | ||||
|         int startSpaces = numSpaces / 2; | ||||
|         int endSpaces = numSpaces - startSpaces; | ||||
|         sb.append("["); | ||||
|         sb.append(fill.repeat(startSpaces - 1)); | ||||
|         sb.append(" "); | ||||
|         sb.append(machine); | ||||
|         sb.append(" "); | ||||
|         sb.append(fill.repeat(endSpaces - 1)); | ||||
|         sb.append("]"); | ||||
|         return sb.toString(); | ||||
|     } | ||||
| } | ||||
|  | @ -36,6 +36,6 @@ public final class Task { | |||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return "(" + job +", " + task + '}'; | ||||
|         return "(" + job +", " + task + ')'; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ package jobshop.solvers; | |||
| 
 | ||||
| import jobshop.Instance; | ||||
| import jobshop.Result; | ||||
| import jobshop.Solver; | ||||
| import jobshop.encodings.JobNumbers; | ||||
| 
 | ||||
| /** | ||||
|  |  | |||
|  | @ -2,89 +2,22 @@ package jobshop.solvers; | |||
| 
 | ||||
| import jobshop.Instance; | ||||
| import jobshop.Result; | ||||
| import jobshop.Solver; | ||||
| import jobshop.encodings.ResourceOrder; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import jobshop.solvers.neighborhood.Neighborhood; | ||||
| 
 | ||||
| public class DescentSolver implements Solver { | ||||
| 
 | ||||
|     /** A block represents a subsequence of the critical path such that all tasks in it execute on the same machine. | ||||
|      * This class identifies a block in a ResourceOrder representation. | ||||
|      * | ||||
|      * Consider the solution in ResourceOrder representation | ||||
|      * machine 0 : (0,1) (1,2) (2,2) | ||||
|      * machine 1 : (0,2) (2,1) (1,1) | ||||
|      * machine 2 : ... | ||||
|      * | ||||
|      * The block with : machine = 1, firstTask= 0 and lastTask = 1 | ||||
|      * Represent the task sequence : [(0,2) (2,1)] | ||||
|      * | ||||
|      * */ | ||||
|     static class Block { | ||||
|         /** machine on which the block is identified */ | ||||
|         final int machine; | ||||
|         /** index of the first task of the block */ | ||||
|         final int firstTask; | ||||
|         /** index of the last task of the block */ | ||||
|         final int lastTask; | ||||
|     final Neighborhood<ResourceOrder> neighborhood; | ||||
|     final Solver baseSolver; | ||||
| 
 | ||||
|         Block(int machine, int firstTask, int lastTask) { | ||||
|             this.machine = machine; | ||||
|             this.firstTask = firstTask; | ||||
|             this.lastTask = lastTask; | ||||
|         } | ||||
|     public DescentSolver(Neighborhood<ResourceOrder> neighborhood, Solver baseSolver) { | ||||
|         this.neighborhood = neighborhood; | ||||
|         this.baseSolver = baseSolver; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Represents a swap of two tasks on the same machine in a ResourceOrder encoding. | ||||
|      * | ||||
|      * Consider the solution in ResourceOrder representation | ||||
|      * machine 0 : (0,1) (1,2) (2,2) | ||||
|      * machine 1 : (0,2) (2,1) (1,1) | ||||
|      * machine 2 : ... | ||||
|      * | ||||
|      * The swap with : machine = 1, t1= 0 and t2 = 1 | ||||
|      * Represent inversion of the two tasks : (0,2) and (2,1) | ||||
|      * Applying this swap on the above resource order should result in the following one : | ||||
|      * machine 0 : (0,1) (1,2) (2,2) | ||||
|      * machine 1 : (2,1) (0,2) (1,1) | ||||
|      * machine 2 : ... | ||||
|      */ | ||||
|     static class Swap { | ||||
|         // machine on which to perform the swap | ||||
|         final int machine; | ||||
|         // index of one task to be swapped | ||||
|         final int t1; | ||||
|         // index of the other task to be swapped | ||||
|         final int t2; | ||||
| 
 | ||||
|         Swap(int machine, int t1, int t2) { | ||||
|             this.machine = machine; | ||||
|             this.t1 = t1; | ||||
|             this.t2 = t2; | ||||
|         } | ||||
| 
 | ||||
|         /** Apply this swap on the given resource order, transforming it into a new solution. */ | ||||
|         public void applyOn(ResourceOrder order) { | ||||
|             throw new UnsupportedOperationException(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Override | ||||
|     public Result solve(Instance instance, long deadline) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|     /** Returns a list of all blocks of the critical path. */ | ||||
|     List<Block> blocksOfCriticalPath(ResourceOrder order) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|     /** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */ | ||||
|     List<Swap> neighbors(Block block) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										21
									
								
								src/main/java/jobshop/solvers/GreedySolver.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/main/java/jobshop/solvers/GreedySolver.java
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| package jobshop.solvers; | ||||
| 
 | ||||
| import jobshop.Instance; | ||||
| import jobshop.Result; | ||||
| 
 | ||||
| public class GreedySolver implements Solver { | ||||
| 
 | ||||
|     public enum Priority { | ||||
|         SPT, LPT, SRPT, LRPT, EST_SPT, EST_LPT, EST_SRPT, EST_LRPT | ||||
|     } | ||||
| 
 | ||||
|     final Priority priority; | ||||
|     public GreedySolver(Priority p) { | ||||
|         this.priority = p; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Result solve(Instance instance, long deadline) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| } | ||||
|  | @ -2,8 +2,8 @@ package jobshop.solvers; | |||
| 
 | ||||
| import jobshop.*; | ||||
| import jobshop.encodings.JobNumbers; | ||||
| import jobshop.encodings.Schedule; | ||||
| 
 | ||||
| import java.util.Optional; | ||||
| import java.util.Random; | ||||
| 
 | ||||
| public class RandomSolver implements Solver { | ||||
|  |  | |||
							
								
								
									
										21
									
								
								src/main/java/jobshop/solvers/Solver.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/main/java/jobshop/solvers/Solver.java
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| package jobshop.solvers; | ||||
| 
 | ||||
| import jobshop.Instance; | ||||
| import jobshop.Result; | ||||
| 
 | ||||
| public interface Solver { | ||||
| 
 | ||||
|     Result solve(Instance instance, long deadline); | ||||
| 
 | ||||
|     /** Static factory method to create a new solver based on its name. */ | ||||
|     static Solver getSolver(String name) { | ||||
|         switch (name) { | ||||
|             case "basic": return new BasicSolver(); | ||||
|             case "random": return new RandomSolver(); | ||||
|             case "spt": return new GreedySolver(GreedySolver.Priority.SPT); | ||||
|             // TODO: add new solvers | ||||
|             default: throw new RuntimeException("Unknown solver: "+ name); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/main/java/jobshop/solvers/neighborhood/Neighbor.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/main/java/jobshop/solvers/neighborhood/Neighbor.java
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| package jobshop.solvers.neighborhood; | ||||
| 
 | ||||
| public abstract class Neighbor<Encoding> { | ||||
| 
 | ||||
|     public abstract void applyOn(Encoding current); | ||||
|     public abstract void undoApplyOn(Encoding current); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| package jobshop.solvers.neighborhood; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| public abstract class Neighborhood<Encoding> { | ||||
| 
 | ||||
|     public abstract List<Neighbor<Encoding>> generateNeighbors(Encoding current); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										102
									
								
								src/main/java/jobshop/solvers/neighborhood/Nowicki.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/main/java/jobshop/solvers/neighborhood/Nowicki.java
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | |||
| package jobshop.solvers.neighborhood; | ||||
| 
 | ||||
| import jobshop.encodings.ResourceOrder; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| public class Nowicki extends Neighborhood<ResourceOrder> { | ||||
| 
 | ||||
|     /** A block represents a subsequence of the critical path such that all tasks in it execute on the same machine. | ||||
|      * This class identifies a block in a ResourceOrder representation. | ||||
|      * | ||||
|      * Consider the solution in ResourceOrder representation | ||||
|      * machine 0 : (0,1) (1,2) (2,2) | ||||
|      * machine 1 : (0,2) (2,1) (1,1) | ||||
|      * machine 2 : ... | ||||
|      * | ||||
|      * The block with : machine = 1, firstTask= 0 and lastTask = 1 | ||||
|      * Represent the task sequence : [(0,2) (2,1)] | ||||
|      * | ||||
|      * */ | ||||
|     static class Block { | ||||
|         /** machine on which the block is identified */ | ||||
|         final int machine; | ||||
|         /** index of the first task of the block */ | ||||
|         final int firstTask; | ||||
|         /** index of the last task of the block */ | ||||
|         final int lastTask; | ||||
| 
 | ||||
|         Block(int machine, int firstTask, int lastTask) { | ||||
|             this.machine = machine; | ||||
|             this.firstTask = firstTask; | ||||
|             this.lastTask = lastTask; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Represents a swap of two tasks on the same machine in a ResourceOrder encoding. | ||||
|      * | ||||
|      * Consider the solution in ResourceOrder representation | ||||
|      * machine 0 : (0,1) (1,2) (2,2) | ||||
|      * machine 1 : (0,2) (2,1) (1,1) | ||||
|      * machine 2 : ... | ||||
|      * | ||||
|      * The swap with : machine = 1, t1= 0 and t2 = 1 | ||||
|      * Represent inversion of the two tasks : (0,2) and (2,1) | ||||
|      * Applying this swap on the above resource order should result in the following one : | ||||
|      * machine 0 : (0,1) (1,2) (2,2) | ||||
|      * machine 1 : (2,1) (0,2) (1,1) | ||||
|      * machine 2 : ... | ||||
|      */ | ||||
|     static class Swap extends Neighbor<ResourceOrder> { | ||||
|         // machine on which to perform the swap | ||||
|         final int machine; | ||||
|         // index of one task to be swapped | ||||
|         final int t1; | ||||
|         // index of the other task to be swapped | ||||
|         final int t2; | ||||
| 
 | ||||
|         Swap(int machine, int t1, int t2) { | ||||
|             this.machine = machine; | ||||
|             this.t1 = t1; | ||||
|             this.t2 = t2; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         /** Apply this swap on the given resource order, transforming it into a new solution. */ | ||||
|         @Override | ||||
|         public void applyOn(ResourceOrder current) { | ||||
|             throw new UnsupportedOperationException(); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void undoApplyOn(ResourceOrder current) { | ||||
|             throw new UnsupportedOperationException(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Override | ||||
|     public List<Neighbor<ResourceOrder>> generateNeighbors(ResourceOrder current) { | ||||
|         List<Neighbor<ResourceOrder>> neighbors = new ArrayList<>(); | ||||
|         // iterate over all blocks of the critical | ||||
|         for(var block : blocksOfCriticalPath(current)) { | ||||
|             // for this block, compute all neighbors and add them to the list of neighbors | ||||
|             neighbors.addAll(neighbors(block)); | ||||
|         } | ||||
|         return neighbors; | ||||
|     } | ||||
| 
 | ||||
|     /** Returns a list of all the blocks of the critical path. */ | ||||
|     List<Block> blocksOfCriticalPath(ResourceOrder order) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|     /** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */ | ||||
|     List<Swap> neighbors(Block block) { | ||||
|         throw new UnsupportedOperationException(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -2,8 +2,7 @@ package jobshop.encodings; | |||
| 
 | ||||
| import jobshop.Instance; | ||||
| import jobshop.Result; | ||||
| import jobshop.Schedule; | ||||
| import jobshop.Solver; | ||||
| import jobshop.solvers.Solver; | ||||
| import jobshop.solvers.BasicSolver; | ||||
| import org.junit.Test; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue