Browse Source

Improvments for year 2020-2021

Arthur Bit-Monnot 6 months ago
parent
commit
1d4883388b

+ 1
- 1
build.gradle View File

@@ -7,7 +7,7 @@ plugins {
7 7
 group 'jobshop'
8 8
 //version '0.1'
9 9
 
10
-sourceCompatibility = 8
10
+sourceCompatibility = 11
11 11
 
12 12
 
13 13
 application {

+ 11
- 0
instances/aaa2 View File

@@ -0,0 +1,11 @@
1
+# Example 2
2
+# This is the example used as a support for exercises during classes (year 2020-2021)
3
+4 3 # num-jobs num-tasks
4
+0 1 1 3 2 2
5
+1 8 0 5 2 10
6
+0 5 2 4 1 8
7
+2 4 0 10 1 6
8
+
9
+
10
+
11
+

+ 20
- 0
instances/aaa3 View File

@@ -0,0 +1,20 @@
1
+# Example 3
2
+# This problem has deterministic results for all greedy solver variants.
3
+# Makespan for each variant:
4
+# - SPT: 53
5
+# - LPT: 92
6
+# - SRPT: 78
7
+# - LRPT: 54
8
+# - EST_SPT: 48
9
+# - EST_LPT: 56
10
+# - EST_SRPT: 53
11
+# - EST_LRPT: 56
12
+4 3 # num-jobs num-tasks
13
+0 1 1 2 2 15
14
+1 4 0 5 2 11
15
+0 7 2 8 1 13
16
+2 3 0 18 1 6
17
+
18
+
19
+
20
+

+ 2
- 0
src/main/java/jobshop/BestKnownResults.java View File

@@ -55,6 +55,8 @@ public class BestKnownResults {
55 55
     static {
56 56
         bests = new HashMap<>();
57 57
         bests.put("aaa1", 11);
58
+        bests.put("aaa2", 29);
59
+        bests.put("aaa3", 41);
58 60
         bests.put("abz5", 1234);
59 61
         bests.put("abz6", 943);
60 62
         bests.put("abz7", 656);

+ 14
- 15
src/main/java/jobshop/DebuggingMain.java View File

@@ -1,6 +1,8 @@
1 1
 package jobshop;
2 2
 
3 3
 import jobshop.encodings.JobNumbers;
4
+import jobshop.encodings.Schedule;
5
+import jobshop.solvers.GreedySolver;
4 6
 
5 7
 import java.io.IOException;
6 8
 import java.nio.file.Paths;
@@ -12,25 +14,22 @@ public class DebuggingMain {
12 14
             // load the aaa1 instance
13 15
             Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
14 16
 
15
-            // construit une solution dans la représentation par
16
-            // numéro de jobs : [0 1 1 0 0 1]
17
-            // Note : cette solution a aussi été vue dans les exercices (section 3.3)
18
-            //        mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2]
17
+            // builds a solution in the job-numbers encoding [0 1 1 0 0 1]
19 18
             JobNumbers enc = new JobNumbers(instance);
20
-            enc.jobs[enc.nextToSet++] = 0;
21
-            enc.jobs[enc.nextToSet++] = 1;
22
-            enc.jobs[enc.nextToSet++] = 1;
23
-            enc.jobs[enc.nextToSet++] = 0;
24
-            enc.jobs[enc.nextToSet++] = 0;
25
-            enc.jobs[enc.nextToSet++] = 1;
19
+            enc.addTask(0);
20
+            enc.addTask(1);
21
+            enc.addTask(1);
22
+            enc.addTask(0);
23
+            enc.addTask(0);
24
+            enc.addTask(1);
26 25
 
27 26
             System.out.println("\nENCODING: " + enc);
28 27
 
29
-            Schedule sched = enc.toSchedule();
30
-            // TODO: make it print something meaningful by implementing the Schedule.toString() method
31
-            System.out.println("SCHEDULE: " + sched);
32
-            System.out.println("VALID: " + sched.isValid());
33
-            System.out.println("MAKESPAN: " + sched.makespan());
28
+            Schedule schedule = enc.toSchedule();
29
+            System.out.println("VALID: " + schedule.isValid());
30
+            System.out.println("MAKESPAN: " + schedule.makespan());
31
+            System.out.println("SCHEDULE: " + schedule.toString());
32
+            System.out.println("GANTT: " + schedule.asciiGantt());
34 33
 
35 34
         } catch (IOException e) {
36 35
             e.printStackTrace();

+ 7
- 20
src/main/java/jobshop/Main.java View File

@@ -7,8 +7,10 @@ import java.util.ArrayList;
7 7
 import java.util.Arrays;
8 8
 import java.util.HashMap;
9 9
 import java.util.List;
10
+import java.util.stream.Collectors;
10 11
 
11 12
 import jobshop.solvers.*;
13
+import jobshop.solvers.neighborhood.Nowicki;
12 14
 import net.sourceforge.argparse4j.ArgumentParsers;
13 15
 import net.sourceforge.argparse4j.inf.ArgumentParser;
14 16
 import net.sourceforge.argparse4j.inf.ArgumentParserException;
@@ -20,14 +22,7 @@ import net.sourceforge.argparse4j.inf.Namespace;
20 22
  */
21 23
 public class Main {
22 24
 
23
-    /** All solvers available in this program */
24
-    private static final HashMap<String, Solver> solvers;
25
-    static {
26
-        solvers = new HashMap<>();
27
-        solvers.put("basic", new BasicSolver());
28
-        solvers.put("random", new RandomSolver());
29
-        // TODO: add new solvers here
30
-    }
25
+
31 26
 
32 27
 
33 28
     public static void main(String[] args) {
@@ -72,14 +67,7 @@ public class Main {
72 67
         // Get the list of solvers that we should benchmark.
73 68
         // We also check that we have a solver available for the given name and print an error message otherwise.
74 69
         List<String> solversToTest = ns.getList("solver");
75
-        for(String solverName : solversToTest) {
76
-            if(!solvers.containsKey(solverName)) {
77
-                System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible.");
78
-                System.err.println("       Available solvers: " + solvers.keySet().toString());
79
-                System.err.println("       You can provide your own solvers by adding them to the `Main.solvers` HashMap.");
80
-                System.exit(0);
81
-            }
82
-        }
70
+        List<Solver> solvers = solversToTest.stream().map(Solver::getSolver).collect(Collectors.toList());
83 71
 
84 72
         // retrieve all instances on which we should run the solvers.
85 73
         List<String> instances = new ArrayList<>();
@@ -126,11 +114,10 @@ public class Main {
126 114
                 output.printf("%-8s %-5s %4d      ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
127 115
 
128 116
                 // run all selected solvers on the instance and print the results
129
-                for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
117
+                for(int solverId = 0 ; solverId < solvers.size() ; solverId++) {
130 118
                     // Select the next solver to run. Given the solver name passed on the command line,
131 119
                     // we lookup the `Main.solvers` hash map to get the solver object with the given name.
132
-                    String solverName = solversToTest.get(solverId);
133
-                    Solver solver = solvers.get(solverName);
120
+                    Solver solver = solvers.get(solverId);
134 121
 
135 122
                     // start chronometer and compute deadline for the solver to provide a result.
136 123
                     long start = System.currentTimeMillis();
@@ -168,7 +155,7 @@ public class Main {
168 155
 
169 156
 
170 157
         } catch (Exception e) {
171
-            // there was uncought exception, print the stack trace and exit with error.
158
+            // there was uncaught exception, print the stack trace and exit with error.
172 159
             e.printStackTrace();
173 160
             System.exit(1);
174 161
         }

+ 1
- 1
src/main/java/jobshop/Result.java View File

@@ -1,6 +1,6 @@
1 1
 package jobshop;
2 2
 
3
-import java.util.Optional;
3
+import jobshop.encodings.Schedule;
4 4
 
5 5
 public class Result {
6 6
 

+ 0
- 7
src/main/java/jobshop/Solver.java View File

@@ -1,7 +0,0 @@
1
-package jobshop;
2
-
3
-public interface Solver {
4
-
5
-    Result solve(Instance instance, long deadline);
6
-
7
-}

src/main/java/jobshop/Encoding.java → src/main/java/jobshop/encodings/Encoding.java View File

@@ -1,4 +1,6 @@
1
-package jobshop;
1
+package jobshop.encodings;
2
+
3
+import jobshop.Instance;
2 4
 
3 5
 public abstract class Encoding {
4 6
 

+ 9
- 7
src/main/java/jobshop/encodings/JobNumbers.java View File

@@ -1,8 +1,6 @@
1 1
 package jobshop.encodings;
2 2
 
3
-import jobshop.Encoding;
4 3
 import jobshop.Instance;
5
-import jobshop.Schedule;
6 4
 
7 5
 import java.util.Arrays;
8 6
 import java.util.Comparator;
@@ -45,11 +43,15 @@ public class JobNumbers extends Encoding {
45 43
                     .min(Comparator.comparing(t -> schedule.startTime(t.job, t.task)))
46 44
                     .get();
47 45
 
48
-            this.jobs[nextToSet++] = next.job;
46
+            this.addTask(next.job);
49 47
             nextOnJob[next.job] += 1;
50 48
         }
51 49
     }
52 50
 
51
+    public void addTask(int jobNumber) {
52
+        this.jobs[nextToSet++] = jobNumber;
53
+    }
54
+
53 55
     @Override
54 56
     public Schedule toSchedule() {
55 57
         // time at which each machine is going to be freed
@@ -59,22 +61,22 @@ public class JobNumbers extends Encoding {
59 61
         int[] nextTask = new int[instance.numJobs];
60 62
 
61 63
         // for each task, its start time
62
-        int[][] startTimes = new int[instance.numJobs][instance.numTasks];
64
+        Schedule schedule = new Schedule(instance);
63 65
 
64 66
         // compute the earliest start time for every task of every job
65 67
         for(int job : jobs) {
66 68
             int task = nextTask[job];
67 69
             int machine = instance.machine(job, task);
68 70
             // earliest start time for this task
69
-            int est = task == 0 ? 0 : startTimes[job][task-1] + instance.duration(job, task-1);
71
+            int est = task == 0 ? 0 : schedule.endTime(job, task-1);
70 72
             est = Math.max(est, nextFreeTimeResource[machine]);
71 73
 
72
-            startTimes[job][task] = est;
74
+            schedule.setStartTime(job, task, est);
73 75
             nextFreeTimeResource[machine] = est + instance.duration(job, task);
74 76
             nextTask[job] = task + 1;
75 77
         }
76 78
 
77
-        return new Schedule(instance, startTimes);
79
+        return schedule;
78 80
     }
79 81
 
80 82
     @Override

+ 15
- 6
src/main/java/jobshop/encodings/ResourceOrder.java View File

@@ -1,8 +1,6 @@
1 1
 package jobshop.encodings;
2 2
 
3
-import jobshop.Encoding;
4 3
 import jobshop.Instance;
5
-import jobshop.Schedule;
6 4
 
7 5
 import java.util.Comparator;
8 6
 import java.util.Optional;
@@ -53,10 +51,21 @@ public class ResourceOrder extends Encoding {
53 51
         }
54 52
     }
55 53
 
54
+    public void addTaskToMachine(int machine, Task task) {
55
+        tasksByMachine[machine][nextFreeSlot[machine]] = task;
56
+        nextFreeSlot[machine] += 1;
57
+    }
58
+
59
+    public void swapTasks(int machine, int indexFirstTask, int indexSecondTask) {
60
+        Task tmp = tasksByMachine[machine][indexFirstTask];
61
+        tasksByMachine[machine][indexFirstTask] = tasksByMachine[machine][indexSecondTask];
62
+        tasksByMachine[machine][indexSecondTask] = tmp;
63
+    }
64
+
56 65
     @Override
57 66
     public Schedule toSchedule() {
58 67
         // indicate for each task that have been scheduled, its start time
59
-        int [][] startTimes = new int [instance.numJobs][instance.numTasks];
68
+        Schedule schedule = new Schedule(instance);
60 69
 
61 70
         // for each job, how many tasks have been scheduled (0 initially)
62 71
         int[] nextToScheduleByJob = new int[instance.numJobs];
@@ -88,9 +97,9 @@ public class ResourceOrder extends Encoding {
88 97
                 int machine = instance.machine(t.job, t.task);
89 98
 
90 99
                 // compute the earliest start time (est) of the task
91
-                int est = t.task == 0 ? 0 : startTimes[t.job][t.task-1] + instance.duration(t.job, t.task-1);
100
+                int est = t.task == 0 ? 0 : schedule.endTime(t.job, t.task-1);
92 101
                 est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]);
93
-                startTimes[t.job][t.task] = est;
102
+                schedule.setStartTime(t.job, t.task, est);
94 103
 
95 104
                 // mark the task as scheduled
96 105
                 nextToScheduleByJob[t.job]++;
@@ -103,7 +112,7 @@ public class ResourceOrder extends Encoding {
103 112
             }
104 113
         }
105 114
         // we exited the loop : all tasks have been scheduled successfully
106
-        return new Schedule(instance, startTimes);
115
+        return schedule;
107 116
     }
108 117
 
109 118
     /** Creates an exact copy of this resource order. */

src/main/java/jobshop/Schedule.java → src/main/java/jobshop/encodings/Schedule.java View File

@@ -1,7 +1,6 @@
1
-package jobshop;
1
+package jobshop.encodings;
2 2
 
3
-
4
-import jobshop.encodings.Task;
3
+import jobshop.Instance;
5 4
 
6 5
 import java.util.*;
7 6
 import java.util.stream.IntStream;
@@ -12,16 +11,18 @@ public class Schedule {
12 11
     // times[j][i] is the start time of task (j,i) : i^th task of the j^th job
13 12
     final int[][] times;
14 13
 
15
-    public Schedule(Instance pb, int[][] times) {
14
+    /** Creates a new schedule for the given instance where all start times are uninitialized. */
15
+    public Schedule(Instance pb) {
16 16
         this.pb = pb;
17 17
         this.times = new int[pb.numJobs][];
18 18
         for(int j = 0 ; j < pb.numJobs ; j++) {
19
-            this.times[j] = Arrays.copyOf(times[j], pb.numTasks);
19
+            this.times[j] = new int[pb.numTasks];
20 20
         }
21 21
     }
22 22
 
23
-    public int startTime(int job, int task) {
24
-        return times[job][task];
23
+    /** Sets the start time of the given task. */
24
+    public void setStartTime(int job, int task, int startTime) {
25
+        times[job][task] = startTime;
25 26
     }
26 27
 
27 28
     /** Returns true if this schedule is valid (no constraint is violated) */
@@ -55,22 +56,38 @@ public class Schedule {
55 56
         return true;
56 57
     }
57 58
 
59
+    /** Makespan of the solution.
60
+     * The makespan is the end time of the latest finishing task.
61
+     */
58 62
     public int makespan() {
59 63
         int max = -1;
60 64
         for(int j = 0 ; j<pb.numJobs ; j++) {
61
-            max = Math.max(max, startTime(j, pb.numTasks-1) + pb.duration(j, pb.numTasks -1));
65
+            max = Math.max(max, endTime(j, pb.numTasks-1));
62 66
         }
63 67
         return max;
64 68
     }
65 69
 
70
+    /** Start time of the given task. */
71
+    public int startTime(int job, int task) {
72
+        return times[job][task];
73
+    }
74
+
75
+    /** Start time of the given task. */
66 76
     public int startTime(Task task) {
67 77
         return startTime(task.job, task.task);
68 78
     }
69 79
 
80
+    /** End time of the given task. */
81
+    public int endTime(int job, int task) {
82
+        return startTime(job, task) + pb.duration(job, task);
83
+    }
84
+
85
+    /** End time of the given task. */
70 86
     public int endTime(Task task) {
71
-        return startTime(task) + pb.duration(task.job, task.task);
87
+        return endTime(task.job, task.task);
72 88
     }
73 89
 
90
+    /** Returns true if the given sequence of task is a critical path of the schedule. */
74 91
     public boolean isCriticalPath(List<Task> path) {
75 92
         if(startTime(path.get(0)) != 0) {
76 93
             return false;
@@ -85,6 +102,10 @@ public class Schedule {
85 102
         return true;
86 103
     }
87 104
 
105
+    /** Computes a critical path of the schedule.
106
+     *
107
+     * @return A sequence of task along a critical path.
108
+     */
88 109
     public List<Task> criticalPath() {
89 110
         // select task with greatest end time
90 111
         Task ldd = IntStream.range(0, pb.numJobs)
@@ -117,7 +138,7 @@ public class Schedule {
117 138
                 if(endTime(predOnJob) == startTime(cur))
118 139
                     latestPredecessor = Optional.of(predOnJob);
119 140
             }
120
-            if(!latestPredecessor.isPresent()) {
141
+            if(latestPredecessor.isEmpty()) {
121 142
                 // no latest predecessor found yet, look among tasks executing on the same machine
122 143
                 latestPredecessor = IntStream.range(0, pb.numJobs)
123 144
                         .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine)))
@@ -132,4 +153,81 @@ public class Schedule {
132 153
         assert isCriticalPath(path);
133 154
         return path;
134 155
     }
156
+
157
+    @Override
158
+    public String toString() {
159
+        StringBuilder sb = new StringBuilder();
160
+        sb.append("\nStart times of all tasks:\n");
161
+        for(int job=0; job<pb.numJobs; job++) {
162
+            sb.append("Job ");
163
+            sb.append(job);
164
+            sb.append(": ");
165
+            for(int task=0; task<pb.numTasks; task++) {
166
+                sb.append(String.format("%5d",  startTime(job, task)));
167
+
168
+            }
169
+            sb.append("\n");
170
+        }
171
+        return sb.toString();
172
+    }
173
+
174
+    /**
175
+     * Returns a string containing the Gantt chart of the given schedule, in ASCII art.
176
+     *
177
+     * Each line of the Gantt chart, contains the tasks of a particular job. Each character in the output represents a
178
+     * fixed number of time units
179
+     * For each task, we indicate :
180
+     *  - the machine on which the task must be executed
181
+     *  - whether this task is on the critical path (task on the critical path are filled in with stars).
182
+     */
183
+    public String asciiGantt() {
184
+        var criticalPath = this.criticalPath();
185
+        int minTaskDur = IntStream.range(0, pb.numJobs).flatMap(job -> IntStream.range(0, pb.numTasks).map(task -> pb.duration(job, task))).min().getAsInt();
186
+        // time units by character
187
+        int charsPerTimeUnit = minTaskDur >= 5 ? 1 : (5 / minTaskDur) +1;
188
+        StringBuilder sb = new StringBuilder();
189
+        sb.append("\nGantt Chart\n");
190
+        for(int job=0; job<pb.numJobs; job++) {
191
+            sb.append(String.format("Job %2d: ", job));
192
+            int cursor = 0;
193
+            for(int task=0; task<pb.numTasks; task++) {
194
+                Task t = new Task(job, task);
195
+                var st = startTime(job, task);
196
+                // add spaces until the start of our task
197
+                sb.append(" ".repeat(charsPerTimeUnit * (st - cursor )));
198
+                sb.append(formatTask(t, charsPerTimeUnit, criticalPath.contains(t)));
199
+
200
+                cursor = endTime(job, task);
201
+            }
202
+            sb.append("\n");
203
+        }
204
+        return sb.toString();
205
+    }
206
+
207
+    /** Utility function to display a set of characters representing a task in a gantt chart.
208
+     *
209
+     * @param t Task to display
210
+     * @param charPerTimeUnit How many characters to represent a time unit.
211
+     * @param isCritical Is the task on the critical path.
212
+     * @return Ascii representation of the task. Length is the duration * charPerTimeUnit.
213
+     */
214
+    String formatTask(Task t, int charPerTimeUnit, boolean isCritical) {
215
+        StringBuilder sb = new StringBuilder();
216
+        String fill = isCritical ? "*" : "-";
217
+        int dur = pb.duration(t);
218
+        int machine = pb.machine(t);
219
+        int stringLength = dur * charPerTimeUnit;
220
+        int charsForMachine = machine < 10 ? 1 : 2;
221
+        int numSpaces = stringLength - 2 - charsForMachine; // we use 2 chars for '[' and '[' + 1 or 2 for the machine number
222
+        int startSpaces = numSpaces / 2;
223
+        int endSpaces = numSpaces - startSpaces;
224
+        sb.append("[");
225
+        sb.append(fill.repeat(startSpaces - 1));
226
+        sb.append(" ");
227
+        sb.append(machine);
228
+        sb.append(" ");
229
+        sb.append(fill.repeat(endSpaces - 1));
230
+        sb.append("]");
231
+        return sb.toString();
232
+    }
135 233
 }

+ 1
- 1
src/main/java/jobshop/encodings/Task.java View File

@@ -36,6 +36,6 @@ public final class Task {
36 36
 
37 37
     @Override
38 38
     public String toString() {
39
-        return "(" + job +", " + task + '}';
39
+        return "(" + job +", " + task + ')';
40 40
     }
41 41
 }

+ 0
- 1
src/main/java/jobshop/solvers/BasicSolver.java View File

@@ -2,7 +2,6 @@ package jobshop.solvers;
2 2
 
3 3
 import jobshop.Instance;
4 4
 import jobshop.Result;
5
-import jobshop.Solver;
6 5
 import jobshop.encodings.JobNumbers;
7 6
 
8 7
 /**

+ 6
- 73
src/main/java/jobshop/solvers/DescentSolver.java View File

@@ -2,89 +2,22 @@ package jobshop.solvers;
2 2
 
3 3
 import jobshop.Instance;
4 4
 import jobshop.Result;
5
-import jobshop.Solver;
6 5
 import jobshop.encodings.ResourceOrder;
7
-
8
-import java.util.List;
6
+import jobshop.solvers.neighborhood.Neighborhood;
9 7
 
10 8
 public class DescentSolver implements Solver {
11 9
 
12
-    /** A block represents a subsequence of the critical path such that all tasks in it execute on the same machine.
13
-     * This class identifies a block in a ResourceOrder representation.
14
-     *
15
-     * Consider the solution in ResourceOrder representation
16
-     * machine 0 : (0,1) (1,2) (2,2)
17
-     * machine 1 : (0,2) (2,1) (1,1)
18
-     * machine 2 : ...
19
-     *
20
-     * The block with : machine = 1, firstTask= 0 and lastTask = 1
21
-     * Represent the task sequence : [(0,2) (2,1)]
22
-     *
23
-     * */
24
-    static class Block {
25
-        /** machine on which the block is identified */
26
-        final int machine;
27
-        /** index of the first task of the block */
28
-        final int firstTask;
29
-        /** index of the last task of the block */
30
-        final int lastTask;
31
-
32
-        Block(int machine, int firstTask, int lastTask) {
33
-            this.machine = machine;
34
-            this.firstTask = firstTask;
35
-            this.lastTask = lastTask;
36
-        }
37
-    }
38
-
39
-    /**
40
-     * Represents a swap of two tasks on the same machine in a ResourceOrder encoding.
41
-     *
42
-     * Consider the solution in ResourceOrder representation
43
-     * machine 0 : (0,1) (1,2) (2,2)
44
-     * machine 1 : (0,2) (2,1) (1,1)
45
-     * machine 2 : ...
46
-     *
47
-     * The swap with : machine = 1, t1= 0 and t2 = 1
48
-     * Represent inversion of the two tasks : (0,2) and (2,1)
49
-     * Applying this swap on the above resource order should result in the following one :
50
-     * machine 0 : (0,1) (1,2) (2,2)
51
-     * machine 1 : (2,1) (0,2) (1,1)
52
-     * machine 2 : ...
53
-     */
54
-    static class Swap {
55
-        // machine on which to perform the swap
56
-        final int machine;
57
-        // index of one task to be swapped
58
-        final int t1;
59
-        // index of the other task to be swapped
60
-        final int t2;
61
-
62
-        Swap(int machine, int t1, int t2) {
63
-            this.machine = machine;
64
-            this.t1 = t1;
65
-            this.t2 = t2;
66
-        }
10
+    final Neighborhood<ResourceOrder> neighborhood;
11
+    final Solver baseSolver;
67 12
 
68
-        /** Apply this swap on the given resource order, transforming it into a new solution. */
69
-        public void applyOn(ResourceOrder order) {
70
-            throw new UnsupportedOperationException();
71
-        }
13
+    public DescentSolver(Neighborhood<ResourceOrder> neighborhood, Solver baseSolver) {
14
+        this.neighborhood = neighborhood;
15
+        this.baseSolver = baseSolver;
72 16
     }
73 17
 
74
-
75 18
     @Override
76 19
     public Result solve(Instance instance, long deadline) {
77 20
         throw new UnsupportedOperationException();
78 21
     }
79 22
 
80
-    /** Returns a list of all blocks of the critical path. */
81
-    List<Block> blocksOfCriticalPath(ResourceOrder order) {
82
-        throw new UnsupportedOperationException();
83
-    }
84
-
85
-    /** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */
86
-    List<Swap> neighbors(Block block) {
87
-        throw new UnsupportedOperationException();
88
-    }
89
-
90 23
 }

+ 21
- 0
src/main/java/jobshop/solvers/GreedySolver.java View File

@@ -0,0 +1,21 @@
1
+package jobshop.solvers;
2
+
3
+import jobshop.Instance;
4
+import jobshop.Result;
5
+
6
+public class GreedySolver implements Solver {
7
+
8
+    public enum Priority {
9
+        SPT, LPT, SRPT, LRPT, EST_SPT, EST_LPT, EST_SRPT, EST_LRPT
10
+    }
11
+
12
+    final Priority priority;
13
+    public GreedySolver(Priority p) {
14
+        this.priority = p;
15
+    }
16
+
17
+    @Override
18
+    public Result solve(Instance instance, long deadline) {
19
+        throw new UnsupportedOperationException();
20
+    }
21
+}

+ 1
- 1
src/main/java/jobshop/solvers/RandomSolver.java View File

@@ -2,8 +2,8 @@ package jobshop.solvers;
2 2
 
3 3
 import jobshop.*;
4 4
 import jobshop.encodings.JobNumbers;
5
+import jobshop.encodings.Schedule;
5 6
 
6
-import java.util.Optional;
7 7
 import java.util.Random;
8 8
 
9 9
 public class RandomSolver implements Solver {

+ 21
- 0
src/main/java/jobshop/solvers/Solver.java View File

@@ -0,0 +1,21 @@
1
+package jobshop.solvers;
2
+
3
+import jobshop.Instance;
4
+import jobshop.Result;
5
+
6
+public interface Solver {
7
+
8
+    Result solve(Instance instance, long deadline);
9
+
10
+    /** Static factory method to create a new solver based on its name. */
11
+    static Solver getSolver(String name) {
12
+        switch (name) {
13
+            case "basic": return new BasicSolver();
14
+            case "random": return new RandomSolver();
15
+            case "spt": return new GreedySolver(GreedySolver.Priority.SPT);
16
+            // TODO: add new solvers
17
+            default: throw new RuntimeException("Unknown solver: "+ name);
18
+        }
19
+    }
20
+
21
+}

+ 8
- 0
src/main/java/jobshop/solvers/neighborhood/Neighbor.java View File

@@ -0,0 +1,8 @@
1
+package jobshop.solvers.neighborhood;
2
+
3
+public abstract class Neighbor<Encoding> {
4
+
5
+    public abstract void applyOn(Encoding current);
6
+    public abstract void undoApplyOn(Encoding current);
7
+
8
+}

+ 9
- 0
src/main/java/jobshop/solvers/neighborhood/Neighborhood.java View File

@@ -0,0 +1,9 @@
1
+package jobshop.solvers.neighborhood;
2
+
3
+import java.util.List;
4
+
5
+public abstract class Neighborhood<Encoding> {
6
+
7
+    public abstract List<Neighbor<Encoding>> generateNeighbors(Encoding current);
8
+
9
+}

+ 102
- 0
src/main/java/jobshop/solvers/neighborhood/Nowicki.java View File

@@ -0,0 +1,102 @@
1
+package jobshop.solvers.neighborhood;
2
+
3
+import jobshop.encodings.ResourceOrder;
4
+
5
+import java.util.ArrayList;
6
+import java.util.List;
7
+
8
+public class Nowicki extends Neighborhood<ResourceOrder> {
9
+
10
+    /** A block represents a subsequence of the critical path such that all tasks in it execute on the same machine.
11
+     * This class identifies a block in a ResourceOrder representation.
12
+     *
13
+     * Consider the solution in ResourceOrder representation
14
+     * machine 0 : (0,1) (1,2) (2,2)
15
+     * machine 1 : (0,2) (2,1) (1,1)
16
+     * machine 2 : ...
17
+     *
18
+     * The block with : machine = 1, firstTask= 0 and lastTask = 1
19
+     * Represent the task sequence : [(0,2) (2,1)]
20
+     *
21
+     * */
22
+    static class Block {
23
+        /** machine on which the block is identified */
24
+        final int machine;
25
+        /** index of the first task of the block */
26
+        final int firstTask;
27
+        /** index of the last task of the block */
28
+        final int lastTask;
29
+
30
+        Block(int machine, int firstTask, int lastTask) {
31
+            this.machine = machine;
32
+            this.firstTask = firstTask;
33
+            this.lastTask = lastTask;
34
+        }
35
+    }
36
+
37
+    /**
38
+     * Represents a swap of two tasks on the same machine in a ResourceOrder encoding.
39
+     *
40
+     * Consider the solution in ResourceOrder representation
41
+     * machine 0 : (0,1) (1,2) (2,2)
42
+     * machine 1 : (0,2) (2,1) (1,1)
43
+     * machine 2 : ...
44
+     *
45
+     * The swap with : machine = 1, t1= 0 and t2 = 1
46
+     * Represent inversion of the two tasks : (0,2) and (2,1)
47
+     * Applying this swap on the above resource order should result in the following one :
48
+     * machine 0 : (0,1) (1,2) (2,2)
49
+     * machine 1 : (2,1) (0,2) (1,1)
50
+     * machine 2 : ...
51
+     */
52
+    static class Swap extends Neighbor<ResourceOrder> {
53
+        // machine on which to perform the swap
54
+        final int machine;
55
+        // index of one task to be swapped
56
+        final int t1;
57
+        // index of the other task to be swapped
58
+        final int t2;
59
+
60
+        Swap(int machine, int t1, int t2) {
61
+            this.machine = machine;
62
+            this.t1 = t1;
63
+            this.t2 = t2;
64
+        }
65
+
66
+
67
+        /** Apply this swap on the given resource order, transforming it into a new solution. */
68
+        @Override
69
+        public void applyOn(ResourceOrder current) {
70
+            throw new UnsupportedOperationException();
71
+        }
72
+
73
+        @Override
74
+        public void undoApplyOn(ResourceOrder current) {
75
+            throw new UnsupportedOperationException();
76
+        }
77
+    }
78
+
79
+
80
+    @Override
81
+    public List<Neighbor<ResourceOrder>> generateNeighbors(ResourceOrder current) {
82
+        List<Neighbor<ResourceOrder>> neighbors = new ArrayList<>();
83
+        // iterate over all blocks of the critical
84
+        for(var block : blocksOfCriticalPath(current)) {
85
+            // for this block, compute all neighbors and add them to the list of neighbors
86
+            neighbors.addAll(neighbors(block));
87
+        }
88
+        return neighbors;
89
+    }
90
+
91
+    /** Returns a list of all the blocks of the critical path. */
92
+    List<Block> blocksOfCriticalPath(ResourceOrder order) {
93
+        throw new UnsupportedOperationException();
94
+    }
95
+
96
+    /** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */
97
+    List<Swap> neighbors(Block block) {
98
+        throw new UnsupportedOperationException();
99
+
100
+    }
101
+
102
+}

+ 1
- 2
src/test/java/jobshop/encodings/EncodingTests.java View File

@@ -2,8 +2,7 @@ package jobshop.encodings;
2 2
 
3 3
 import jobshop.Instance;
4 4
 import jobshop.Result;
5
-import jobshop.Schedule;
6
-import jobshop.Solver;
5
+import jobshop.solvers.Solver;
7 6
 import jobshop.solvers.BasicSolver;
8 7
 import org.junit.Test;
9 8
 

Loading…
Cancel
Save