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' | group 'jobshop' | ||||||
| //version '0.1' | //version '0.1' | ||||||
| 
 | 
 | ||||||
| sourceCompatibility = 8 | sourceCompatibility = 11 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| application { | 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 { |     static { | ||||||
|         bests = new HashMap<>(); |         bests = new HashMap<>(); | ||||||
|         bests.put("aaa1", 11); |         bests.put("aaa1", 11); | ||||||
|  |         bests.put("aaa2", 29); | ||||||
|  |         bests.put("aaa3", 41); | ||||||
|         bests.put("abz5", 1234); |         bests.put("abz5", 1234); | ||||||
|         bests.put("abz6", 943); |         bests.put("abz6", 943); | ||||||
|         bests.put("abz7", 656); |         bests.put("abz7", 656); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,8 @@ | ||||||
| package jobshop; | package jobshop; | ||||||
| 
 | 
 | ||||||
| import jobshop.encodings.JobNumbers; | import jobshop.encodings.JobNumbers; | ||||||
|  | import jobshop.encodings.Schedule; | ||||||
|  | import jobshop.solvers.GreedySolver; | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.nio.file.Paths; | import java.nio.file.Paths; | ||||||
|  | @ -12,25 +14,22 @@ public class DebuggingMain { | ||||||
|             // load the aaa1 instance |             // load the aaa1 instance | ||||||
|             Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); |             Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); | ||||||
| 
 | 
 | ||||||
|             // construit une solution dans la représentation par |             // builds a solution in the job-numbers encoding [0 1 1 0 0 1] | ||||||
|             // 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] |  | ||||||
|             JobNumbers enc = new JobNumbers(instance); |             JobNumbers enc = new JobNumbers(instance); | ||||||
|             enc.jobs[enc.nextToSet++] = 0; |             enc.addTask(0); | ||||||
|             enc.jobs[enc.nextToSet++] = 1; |             enc.addTask(1); | ||||||
|             enc.jobs[enc.nextToSet++] = 1; |             enc.addTask(1); | ||||||
|             enc.jobs[enc.nextToSet++] = 0; |             enc.addTask(0); | ||||||
|             enc.jobs[enc.nextToSet++] = 0; |             enc.addTask(0); | ||||||
|             enc.jobs[enc.nextToSet++] = 1; |             enc.addTask(1); | ||||||
| 
 | 
 | ||||||
|             System.out.println("\nENCODING: " + enc); |             System.out.println("\nENCODING: " + enc); | ||||||
| 
 | 
 | ||||||
|             Schedule sched = enc.toSchedule(); |             Schedule schedule = enc.toSchedule(); | ||||||
|             // TODO: make it print something meaningful by implementing the Schedule.toString() method |             System.out.println("VALID: " + schedule.isValid()); | ||||||
|             System.out.println("SCHEDULE: " + sched); |             System.out.println("MAKESPAN: " + schedule.makespan()); | ||||||
|             System.out.println("VALID: " + sched.isValid()); |             System.out.println("SCHEDULE: " + schedule.toString()); | ||||||
|             System.out.println("MAKESPAN: " + sched.makespan()); |             System.out.println("GANTT: " + schedule.asciiGantt()); | ||||||
| 
 | 
 | ||||||
|         } catch (IOException e) { |         } catch (IOException e) { | ||||||
|             e.printStackTrace(); |             e.printStackTrace(); | ||||||
|  |  | ||||||
|  | @ -7,8 +7,10 @@ import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| import jobshop.solvers.*; | import jobshop.solvers.*; | ||||||
|  | import jobshop.solvers.neighborhood.Nowicki; | ||||||
| import net.sourceforge.argparse4j.ArgumentParsers; | import net.sourceforge.argparse4j.ArgumentParsers; | ||||||
| import net.sourceforge.argparse4j.inf.ArgumentParser; | import net.sourceforge.argparse4j.inf.ArgumentParser; | ||||||
| import net.sourceforge.argparse4j.inf.ArgumentParserException; | import net.sourceforge.argparse4j.inf.ArgumentParserException; | ||||||
|  | @ -20,14 +22,7 @@ import net.sourceforge.argparse4j.inf.Namespace; | ||||||
|  */ |  */ | ||||||
| public class Main { | 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) { |     public static void main(String[] args) { | ||||||
|  | @ -72,14 +67,7 @@ public class Main { | ||||||
|         // Get the list of solvers that we should benchmark. |         // 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. |         // 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"); |         List<String> solversToTest = ns.getList("solver"); | ||||||
|         for(String solverName : solversToTest) { |         List<Solver> solvers = solversToTest.stream().map(Solver::getSolver).collect(Collectors.toList()); | ||||||
|             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); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         // retrieve all instances on which we should run the solvers. |         // retrieve all instances on which we should run the solvers. | ||||||
|         List<String> instances = new ArrayList<>(); |         List<String> instances = new ArrayList<>(); | ||||||
|  | @ -126,11 +114,10 @@ public class Main { | ||||||
|                 output.printf("%-8s %-5s %4d      ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown); |                 output.printf("%-8s %-5s %4d      ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown); | ||||||
| 
 | 
 | ||||||
|                 // run all selected solvers on the instance and print the results |                 // 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, |                     // 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. |                     // 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(solverId); | ||||||
|                     Solver solver = solvers.get(solverName); |  | ||||||
| 
 | 
 | ||||||
|                     // start chronometer and compute deadline for the solver to provide a result. |                     // start chronometer and compute deadline for the solver to provide a result. | ||||||
|                     long start = System.currentTimeMillis(); |                     long start = System.currentTimeMillis(); | ||||||
|  | @ -168,7 +155,7 @@ public class Main { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         } catch (Exception e) { |         } 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(); |             e.printStackTrace(); | ||||||
|             System.exit(1); |             System.exit(1); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| package jobshop; | package jobshop; | ||||||
| 
 | 
 | ||||||
| import java.util.Optional; | import jobshop.encodings.Schedule; | ||||||
| 
 | 
 | ||||||
| public class Result { | 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 { | public abstract class Encoding { | ||||||
| 
 | 
 | ||||||
|  | @ -1,8 +1,6 @@ | ||||||
| package jobshop.encodings; | package jobshop.encodings; | ||||||
| 
 | 
 | ||||||
| import jobshop.Encoding; |  | ||||||
| import jobshop.Instance; | import jobshop.Instance; | ||||||
| import jobshop.Schedule; |  | ||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Comparator; | import java.util.Comparator; | ||||||
|  | @ -45,11 +43,15 @@ public class JobNumbers extends Encoding { | ||||||
|                     .min(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) |                     .min(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) | ||||||
|                     .get(); |                     .get(); | ||||||
| 
 | 
 | ||||||
|             this.jobs[nextToSet++] = next.job; |             this.addTask(next.job); | ||||||
|             nextOnJob[next.job] += 1; |             nextOnJob[next.job] += 1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public void addTask(int jobNumber) { | ||||||
|  |         this.jobs[nextToSet++] = jobNumber; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public Schedule toSchedule() { |     public Schedule toSchedule() { | ||||||
|         // time at which each machine is going to be freed |         // 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]; |         int[] nextTask = new int[instance.numJobs]; | ||||||
| 
 | 
 | ||||||
|         // for each task, its start time |         // 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 |         // compute the earliest start time for every task of every job | ||||||
|         for(int job : jobs) { |         for(int job : jobs) { | ||||||
|             int task = nextTask[job]; |             int task = nextTask[job]; | ||||||
|             int machine = instance.machine(job, task); |             int machine = instance.machine(job, task); | ||||||
|             // earliest start time for this 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]); |             est = Math.max(est, nextFreeTimeResource[machine]); | ||||||
| 
 | 
 | ||||||
|             startTimes[job][task] = est; |             schedule.setStartTime(job, task, est); | ||||||
|             nextFreeTimeResource[machine] = est + instance.duration(job, task); |             nextFreeTimeResource[machine] = est + instance.duration(job, task); | ||||||
|             nextTask[job] = task + 1; |             nextTask[job] = task + 1; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return new Schedule(instance, startTimes); |         return schedule; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  |  | ||||||
|  | @ -1,8 +1,6 @@ | ||||||
| package jobshop.encodings; | package jobshop.encodings; | ||||||
| 
 | 
 | ||||||
| import jobshop.Encoding; |  | ||||||
| import jobshop.Instance; | import jobshop.Instance; | ||||||
| import jobshop.Schedule; |  | ||||||
| 
 | 
 | ||||||
| import java.util.Comparator; | import java.util.Comparator; | ||||||
| import java.util.Optional; | 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 |     @Override | ||||||
|     public Schedule toSchedule() { |     public Schedule toSchedule() { | ||||||
|         // indicate for each task that have been scheduled, its start time |         // 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) |         // for each job, how many tasks have been scheduled (0 initially) | ||||||
|         int[] nextToScheduleByJob = new int[instance.numJobs]; |         int[] nextToScheduleByJob = new int[instance.numJobs]; | ||||||
|  | @ -88,9 +97,9 @@ public class ResourceOrder extends Encoding { | ||||||
|                 int machine = instance.machine(t.job, t.task); |                 int machine = instance.machine(t.job, t.task); | ||||||
| 
 | 
 | ||||||
|                 // compute the earliest start time (est) of the 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)]); |                 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 |                 // mark the task as scheduled | ||||||
|                 nextToScheduleByJob[t.job]++; |                 nextToScheduleByJob[t.job]++; | ||||||
|  | @ -103,7 +112,7 @@ public class ResourceOrder extends Encoding { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         // we exited the loop : all tasks have been scheduled successfully |         // 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. */ |     /** Creates an exact copy of this resource order. */ | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| package jobshop; | package jobshop.encodings; | ||||||
| 
 | 
 | ||||||
| 
 | import jobshop.Instance; | ||||||
| import jobshop.encodings.Task; |  | ||||||
| 
 | 
 | ||||||
| import java.util.*; | import java.util.*; | ||||||
| import java.util.stream.IntStream; | 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 |     // times[j][i] is the start time of task (j,i) : i^th task of the j^th job | ||||||
|     final int[][] times; |     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.pb = pb; | ||||||
|         this.times = new int[pb.numJobs][]; |         this.times = new int[pb.numJobs][]; | ||||||
|         for(int j = 0 ; j < pb.numJobs ; j++) { |         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) { |     /** Sets the start time of the given task. */ | ||||||
|         return times[job][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) */ |     /** Returns true if this schedule is valid (no constraint is violated) */ | ||||||
|  | @ -55,22 +56,38 @@ public class Schedule { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** Makespan of the solution. | ||||||
|  |      * The makespan is the end time of the latest finishing task. | ||||||
|  |      */ | ||||||
|     public int makespan() { |     public int makespan() { | ||||||
|         int max = -1; |         int max = -1; | ||||||
|         for(int j = 0 ; j<pb.numJobs ; j++) { |         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; |         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) { |     public int startTime(Task task) { | ||||||
|         return startTime(task.job, task.task); |         return startTime(task.job, task.task); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public int endTime(Task task) { |     /** End time of the given task. */ | ||||||
|         return startTime(task) + pb.duration(task.job, task.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) { |     public boolean isCriticalPath(List<Task> path) { | ||||||
|         if(startTime(path.get(0)) != 0) { |         if(startTime(path.get(0)) != 0) { | ||||||
|             return false; |             return false; | ||||||
|  | @ -85,6 +102,10 @@ public class Schedule { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** Computes a critical path of the schedule. | ||||||
|  |      * | ||||||
|  |      * @return A sequence of task along a critical path. | ||||||
|  |      */ | ||||||
|     public List<Task> criticalPath() { |     public List<Task> criticalPath() { | ||||||
|         // select task with greatest end time |         // select task with greatest end time | ||||||
|         Task ldd = IntStream.range(0, pb.numJobs) |         Task ldd = IntStream.range(0, pb.numJobs) | ||||||
|  | @ -117,7 +138,7 @@ public class Schedule { | ||||||
|                 if(endTime(predOnJob) == startTime(cur)) |                 if(endTime(predOnJob) == startTime(cur)) | ||||||
|                     latestPredecessor = Optional.of(predOnJob); |                     latestPredecessor = Optional.of(predOnJob); | ||||||
|             } |             } | ||||||
|             if(!latestPredecessor.isPresent()) { |             if(latestPredecessor.isEmpty()) { | ||||||
|                 // no latest predecessor found yet, look among tasks executing on the same machine |                 // no latest predecessor found yet, look among tasks executing on the same machine | ||||||
|                 latestPredecessor = IntStream.range(0, pb.numJobs) |                 latestPredecessor = IntStream.range(0, pb.numJobs) | ||||||
|                         .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) |                         .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) | ||||||
|  | @ -132,4 +153,81 @@ public class Schedule { | ||||||
|         assert isCriticalPath(path); |         assert isCriticalPath(path); | ||||||
|         return 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 |     @Override | ||||||
|     public String toString() { |     public String toString() { | ||||||
|         return "(" + job +", " + task + '}'; |         return "(" + job +", " + task + ')'; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ package jobshop.solvers; | ||||||
| 
 | 
 | ||||||
| import jobshop.Instance; | import jobshop.Instance; | ||||||
| import jobshop.Result; | import jobshop.Result; | ||||||
| import jobshop.Solver; |  | ||||||
| import jobshop.encodings.JobNumbers; | import jobshop.encodings.JobNumbers; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  |  | ||||||
|  | @ -2,89 +2,22 @@ package jobshop.solvers; | ||||||
| 
 | 
 | ||||||
| import jobshop.Instance; | import jobshop.Instance; | ||||||
| import jobshop.Result; | import jobshop.Result; | ||||||
| import jobshop.Solver; |  | ||||||
| import jobshop.encodings.ResourceOrder; | import jobshop.encodings.ResourceOrder; | ||||||
| 
 | import jobshop.solvers.neighborhood.Neighborhood; | ||||||
| import java.util.List; |  | ||||||
| 
 | 
 | ||||||
| public class DescentSolver implements Solver { | 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. |     final Neighborhood<ResourceOrder> neighborhood; | ||||||
|      * This class identifies a block in a ResourceOrder representation. |     final Solver baseSolver; | ||||||
|      * |  | ||||||
|      * 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) { |     public DescentSolver(Neighborhood<ResourceOrder> neighborhood, Solver baseSolver) { | ||||||
|             this.machine = machine; |         this.neighborhood = neighborhood; | ||||||
|             this.firstTask = firstTask; |         this.baseSolver = baseSolver; | ||||||
|             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 { |  | ||||||
|         // 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 |     @Override | ||||||
|     public Result solve(Instance instance, long deadline) { |     public Result solve(Instance instance, long deadline) { | ||||||
|         throw new UnsupportedOperationException(); |         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.*; | ||||||
| import jobshop.encodings.JobNumbers; | import jobshop.encodings.JobNumbers; | ||||||
|  | import jobshop.encodings.Schedule; | ||||||
| 
 | 
 | ||||||
| import java.util.Optional; |  | ||||||
| import java.util.Random; | import java.util.Random; | ||||||
| 
 | 
 | ||||||
| public class RandomSolver implements Solver { | 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.Instance; | ||||||
| import jobshop.Result; | import jobshop.Result; | ||||||
| import jobshop.Schedule; | import jobshop.solvers.Solver; | ||||||
| import jobshop.Solver; |  | ||||||
| import jobshop.solvers.BasicSolver; | import jobshop.solvers.BasicSolver; | ||||||
| import org.junit.Test; | import org.junit.Test; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue