Browse Source

Improvments for year 2020-2021

Arthur Bit-Monnot 3 years ago
parent
commit
1d4883388b

+ 1
- 1
build.gradle View File

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

+ 11
- 0
instances/aaa2 View File

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

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
     static {
55
     static {
56
         bests = new HashMap<>();
56
         bests = new HashMap<>();
57
         bests.put("aaa1", 11);
57
         bests.put("aaa1", 11);
58
+        bests.put("aaa2", 29);
59
+        bests.put("aaa3", 41);
58
         bests.put("abz5", 1234);
60
         bests.put("abz5", 1234);
59
         bests.put("abz6", 943);
61
         bests.put("abz6", 943);
60
         bests.put("abz7", 656);
62
         bests.put("abz7", 656);

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

1
 package jobshop;
1
 package jobshop;
2
 
2
 
3
 import jobshop.encodings.JobNumbers;
3
 import jobshop.encodings.JobNumbers;
4
+import jobshop.encodings.Schedule;
5
+import jobshop.solvers.GreedySolver;
4
 
6
 
5
 import java.io.IOException;
7
 import java.io.IOException;
6
 import java.nio.file.Paths;
8
 import java.nio.file.Paths;
12
             // load the aaa1 instance
14
             // load the aaa1 instance
13
             Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
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
             JobNumbers enc = new JobNumbers(instance);
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
             System.out.println("\nENCODING: " + enc);
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
         } catch (IOException e) {
34
         } catch (IOException e) {
36
             e.printStackTrace();
35
             e.printStackTrace();

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

7
 import java.util.Arrays;
7
 import java.util.Arrays;
8
 import java.util.HashMap;
8
 import java.util.HashMap;
9
 import java.util.List;
9
 import java.util.List;
10
+import java.util.stream.Collectors;
10
 
11
 
11
 import jobshop.solvers.*;
12
 import jobshop.solvers.*;
13
+import jobshop.solvers.neighborhood.Nowicki;
12
 import net.sourceforge.argparse4j.ArgumentParsers;
14
 import net.sourceforge.argparse4j.ArgumentParsers;
13
 import net.sourceforge.argparse4j.inf.ArgumentParser;
15
 import net.sourceforge.argparse4j.inf.ArgumentParser;
14
 import net.sourceforge.argparse4j.inf.ArgumentParserException;
16
 import net.sourceforge.argparse4j.inf.ArgumentParserException;
20
  */
22
  */
21
 public class Main {
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
     public static void main(String[] args) {
28
     public static void main(String[] args) {
72
         // Get the list of solvers that we should benchmark.
67
         // Get the list of solvers that we should benchmark.
73
         // We also check that we have a solver available for the given name and print an error message otherwise.
68
         // We also check that we have a solver available for the given name and print an error message otherwise.
74
         List<String> solversToTest = ns.getList("solver");
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
         // retrieve all instances on which we should run the solvers.
72
         // retrieve all instances on which we should run the solvers.
85
         List<String> instances = new ArrayList<>();
73
         List<String> instances = new ArrayList<>();
126
                 output.printf("%-8s %-5s %4d      ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
114
                 output.printf("%-8s %-5s %4d      ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
127
 
115
 
128
                 // run all selected solvers on the instance and print the results
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
                     // Select the next solver to run. Given the solver name passed on the command line,
118
                     // Select the next solver to run. Given the solver name passed on the command line,
131
                     // we lookup the `Main.solvers` hash map to get the solver object with the given name.
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
                     // start chronometer and compute deadline for the solver to provide a result.
122
                     // start chronometer and compute deadline for the solver to provide a result.
136
                     long start = System.currentTimeMillis();
123
                     long start = System.currentTimeMillis();
168
 
155
 
169
 
156
 
170
         } catch (Exception e) {
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
             e.printStackTrace();
159
             e.printStackTrace();
173
             System.exit(1);
160
             System.exit(1);
174
         }
161
         }

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

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

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

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
-package jobshop;
1
+package jobshop.encodings;
2
+
3
+import jobshop.Instance;
2
 
4
 
3
 public abstract class Encoding {
5
 public abstract class Encoding {
4
 
6
 

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

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

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

1
 package jobshop.encodings;
1
 package jobshop.encodings;
2
 
2
 
3
-import jobshop.Encoding;
4
 import jobshop.Instance;
3
 import jobshop.Instance;
5
-import jobshop.Schedule;
6
 
4
 
7
 import java.util.Comparator;
5
 import java.util.Comparator;
8
 import java.util.Optional;
6
 import java.util.Optional;
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
     @Override
65
     @Override
57
     public Schedule toSchedule() {
66
     public Schedule toSchedule() {
58
         // indicate for each task that have been scheduled, its start time
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
         // for each job, how many tasks have been scheduled (0 initially)
70
         // for each job, how many tasks have been scheduled (0 initially)
62
         int[] nextToScheduleByJob = new int[instance.numJobs];
71
         int[] nextToScheduleByJob = new int[instance.numJobs];
88
                 int machine = instance.machine(t.job, t.task);
97
                 int machine = instance.machine(t.job, t.task);
89
 
98
 
90
                 // compute the earliest start time (est) of the task
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
                 est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]);
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
                 // mark the task as scheduled
104
                 // mark the task as scheduled
96
                 nextToScheduleByJob[t.job]++;
105
                 nextToScheduleByJob[t.job]++;
103
             }
112
             }
104
         }
113
         }
105
         // we exited the loop : all tasks have been scheduled successfully
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
     /** Creates an exact copy of this resource order. */
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
-package jobshop;
1
+package jobshop.encodings;
2
 
2
 
3
-
4
-import jobshop.encodings.Task;
3
+import jobshop.Instance;
5
 
4
 
6
 import java.util.*;
5
 import java.util.*;
7
 import java.util.stream.IntStream;
6
 import java.util.stream.IntStream;
12
     // times[j][i] is the start time of task (j,i) : i^th task of the j^th job
11
     // times[j][i] is the start time of task (j,i) : i^th task of the j^th job
13
     final int[][] times;
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
         this.pb = pb;
16
         this.pb = pb;
17
         this.times = new int[pb.numJobs][];
17
         this.times = new int[pb.numJobs][];
18
         for(int j = 0 ; j < pb.numJobs ; j++) {
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
     /** Returns true if this schedule is valid (no constraint is violated) */
28
     /** Returns true if this schedule is valid (no constraint is violated) */
55
         return true;
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
     public int makespan() {
62
     public int makespan() {
59
         int max = -1;
63
         int max = -1;
60
         for(int j = 0 ; j<pb.numJobs ; j++) {
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
         return max;
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
     public int startTime(Task task) {
76
     public int startTime(Task task) {
67
         return startTime(task.job, task.task);
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
     public int endTime(Task task) {
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
     public boolean isCriticalPath(List<Task> path) {
91
     public boolean isCriticalPath(List<Task> path) {
75
         if(startTime(path.get(0)) != 0) {
92
         if(startTime(path.get(0)) != 0) {
76
             return false;
93
             return false;
85
         return true;
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
     public List<Task> criticalPath() {
109
     public List<Task> criticalPath() {
89
         // select task with greatest end time
110
         // select task with greatest end time
90
         Task ldd = IntStream.range(0, pb.numJobs)
111
         Task ldd = IntStream.range(0, pb.numJobs)
117
                 if(endTime(predOnJob) == startTime(cur))
138
                 if(endTime(predOnJob) == startTime(cur))
118
                     latestPredecessor = Optional.of(predOnJob);
139
                     latestPredecessor = Optional.of(predOnJob);
119
             }
140
             }
120
-            if(!latestPredecessor.isPresent()) {
141
+            if(latestPredecessor.isEmpty()) {
121
                 // no latest predecessor found yet, look among tasks executing on the same machine
142
                 // no latest predecessor found yet, look among tasks executing on the same machine
122
                 latestPredecessor = IntStream.range(0, pb.numJobs)
143
                 latestPredecessor = IntStream.range(0, pb.numJobs)
123
                         .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine)))
144
                         .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine)))
132
         assert isCriticalPath(path);
153
         assert isCriticalPath(path);
133
         return path;
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
 
36
 
37
     @Override
37
     @Override
38
     public String toString() {
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
 
2
 
3
 import jobshop.Instance;
3
 import jobshop.Instance;
4
 import jobshop.Result;
4
 import jobshop.Result;
5
-import jobshop.Solver;
6
 import jobshop.encodings.JobNumbers;
5
 import jobshop.encodings.JobNumbers;
7
 
6
 
8
 /**
7
 /**

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

2
 
2
 
3
 import jobshop.Instance;
3
 import jobshop.Instance;
4
 import jobshop.Result;
4
 import jobshop.Result;
5
-import jobshop.Solver;
6
 import jobshop.encodings.ResourceOrder;
5
 import jobshop.encodings.ResourceOrder;
7
-
8
-import java.util.List;
6
+import jobshop.solvers.neighborhood.Neighborhood;
9
 
7
 
10
 public class DescentSolver implements Solver {
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
     @Override
18
     @Override
76
     public Result solve(Instance instance, long deadline) {
19
     public Result solve(Instance instance, long deadline) {
77
         throw new UnsupportedOperationException();
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

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
 
2
 
3
 import jobshop.*;
3
 import jobshop.*;
4
 import jobshop.encodings.JobNumbers;
4
 import jobshop.encodings.JobNumbers;
5
+import jobshop.encodings.Schedule;
5
 
6
 
6
-import java.util.Optional;
7
 import java.util.Random;
7
 import java.util.Random;
8
 
8
 
9
 public class RandomSolver implements Solver {
9
 public class RandomSolver implements Solver {

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

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

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

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

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
 
2
 
3
 import jobshop.Instance;
3
 import jobshop.Instance;
4
 import jobshop.Result;
4
 import jobshop.Result;
5
-import jobshop.Schedule;
6
-import jobshop.Solver;
5
+import jobshop.solvers.Solver;
7
 import jobshop.solvers.BasicSolver;
6
 import jobshop.solvers.BasicSolver;
8
 import org.junit.Test;
7
 import org.junit.Test;
9
 
8
 

Loading…
Cancel
Save