Browse Source

Doc improvements + rename DebuggingMain to MainTest

Arthur Bit-Monnot 5 months ago
parent
commit
0b551e3b4e

+ 1
- 5
src/main/java/jobshop/Main.java View File

@@ -17,14 +17,10 @@ import net.sourceforge.argparse4j.inf.ArgumentParserException;
17 17
 import net.sourceforge.argparse4j.inf.Namespace;
18 18
 
19 19
 /**
20
- * This class is the main entry point for testing solver on instances.
21
- * It provides
20
+ * This class is the main entry point for doing comparative performance tests of solvers.
22 21
  */
23 22
 public class Main {
24 23
 
25
-
26
-
27
-
28 24
     public static void main(String[] args) {
29 25
         // configure the argument parser
30 26
         ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()

src/main/java/jobshop/DebuggingMain.java → src/main/java/jobshop/MainTest.java View File

@@ -1,13 +1,15 @@
1 1
 package jobshop;
2 2
 
3 3
 import jobshop.encodings.JobNumbers;
4
+import jobshop.encodings.ResourceOrder;
4 5
 import jobshop.encodings.Schedule;
6
+import jobshop.encodings.Task;
5 7
 import jobshop.solvers.GreedySolver;
6 8
 
7 9
 import java.io.IOException;
8 10
 import java.nio.file.Paths;
9 11
 
10
-public class DebuggingMain {
12
+public class MainTest {
11 13
 
12 14
     public static void main(String[] args) {
13 15
         try {
@@ -25,12 +27,18 @@ public class DebuggingMain {
25 27
 
26 28
             System.out.println("\nENCODING: " + enc);
27 29
 
30
+            // convert to a schedule and display
28 31
             Schedule schedule = enc.toSchedule();
29 32
             System.out.println("VALID: " + schedule.isValid());
30 33
             System.out.println("MAKESPAN: " + schedule.makespan());
31 34
             System.out.println("SCHEDULE: " + schedule.toString());
32 35
             System.out.println("GANTT: " + schedule.asciiGantt());
33 36
 
37
+            Schedule manualSchedule = new Schedule(instance);
38
+            // TODO: encode the same solution
39
+
40
+            ResourceOrder manualRO = new ResourceOrder(instance);
41
+            // TODO: encode the same solution
34 42
         } catch (IOException e) {
35 43
             e.printStackTrace();
36 44
             System.exit(1);

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

@@ -2,6 +2,7 @@ package jobshop;
2 2
 
3 3
 import jobshop.encodings.Schedule;
4 4
 
5
+/** Class representing the result of a solver. */
5 6
 public class Result {
6 7
 
7 8
     public Result(Instance instance, Schedule schedule, ExitCause cause) {
@@ -10,12 +11,23 @@ public class Result {
10 11
         this.cause = cause;
11 12
     }
12 13
 
14
+    /** Documents the reason why a solver returned the solution. */
13 15
     public enum ExitCause {
14
-        Timeout, ProvedOptimal, Blocked
16
+        /** The solver ran out of time and had to exit. */
17
+        Timeout,
18
+        /** The solution has been proved optimal and thus can no longer be improved. */
19
+        ProvedOptimal,
20
+        /** The solver was not able to further improve the solution (e.g. blocked in a local minima. */
21
+        Blocked
15 22
     }
16 23
 
24
+    /** Instance that was solved. */
17 25
     public final Instance instance;
26
+
27
+    /** A schedule of the solution or null if no solution was found. */
18 28
     public final Schedule schedule;
29
+
30
+    /** Reason why the solver exited with this solution. */
19 31
     public final ExitCause cause;
20 32
 
21 33
 

+ 6
- 0
src/main/java/jobshop/encodings/Encoding.java View File

@@ -2,13 +2,19 @@ package jobshop.encodings;
2 2
 
3 3
 import jobshop.Instance;
4 4
 
5
+/** Common class for all encodings.
6
+ *
7
+ * The only requirement for this class is to provide a conversion from the encoding into a Schedule.
8
+ */
5 9
 public abstract class Encoding {
6 10
 
11
+    /** Problem instance of which this is the solution. */
7 12
     public final Instance instance;
8 13
 
9 14
     public Encoding(Instance instance) {
10 15
         this.instance = instance;
11 16
     }
12 17
 
18
+    /** Convert into a schedule. */
13 19
     public abstract Schedule toSchedule();
14 20
 }

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

@@ -24,7 +24,7 @@ public class JobNumbers extends Encoding {
24 24
     }
25 25
 
26 26
     public JobNumbers(Schedule schedule) {
27
-        super(schedule.pb);
27
+        super(schedule.instance);
28 28
 
29 29
         this.jobs = new int[instance.numJobs * instance.numTasks];
30 30
 

+ 7
- 3
src/main/java/jobshop/encodings/ResourceOrder.java View File

@@ -30,13 +30,13 @@ public class ResourceOrder extends Encoding {
30 30
     /** Creates a resource order from a schedule. */
31 31
     public ResourceOrder(Schedule schedule)
32 32
     {
33
-        super(schedule.pb);
34
-        Instance pb = schedule.pb;
33
+        super(schedule.instance);
34
+        Instance pb = schedule.instance;
35 35
 
36 36
         this.tasksByMachine = new Task[pb.numMachines][];
37 37
         this.nextFreeSlot = new int[instance.numMachines];
38 38
 
39
-        for(int m = 0 ; m<schedule.pb.numMachines ; m++) {
39
+        for(int m = 0; m<schedule.instance.numMachines ; m++) {
40 40
             final int machine = m;
41 41
 
42 42
             // for this machine, find all tasks that are executed on it and sort them by their start time
@@ -51,6 +51,10 @@ public class ResourceOrder extends Encoding {
51 51
         }
52 52
     }
53 53
 
54
+    public void addToMachine(int machine, int jobNumber) {
55
+        addTaskToMachine(machine, new Task(jobNumber, instance.task_with_machine(jobNumber, machine)));
56
+    }
57
+
54 58
     public void addTaskToMachine(int machine, Task task) {
55 59
         tasksByMachine[machine][nextFreeSlot[machine]] = task;
56 60
         nextFreeSlot[machine] += 1;

+ 63
- 52
src/main/java/jobshop/encodings/Schedule.java View File

@@ -5,21 +5,46 @@ import jobshop.Instance;
5 5
 import java.util.*;
6 6
 import java.util.stream.IntStream;
7 7
 
8
-public class Schedule {
9
-    public final Instance pb;
8
+/** Direct encoding of the solution to JobShop problem.
9
+ *
10
+ * Associates every task to its start time.
11
+ */
12
+public class Schedule extends Encoding {
13
+
10 14
     // start times of each job and task
11 15
     // times[j][i] is the start time of task (j,i) : i^th task of the j^th job
12 16
     final int[][] times;
13 17
 
14 18
     /** Creates a new schedule for the given instance where all start times are uninitialized. */
15
-    public Schedule(Instance pb) {
16
-        this.pb = pb;
17
-        this.times = new int[pb.numJobs][];
18
-        for(int j = 0 ; j < pb.numJobs ; j++) {
19
-            this.times[j] = new int[pb.numTasks];
19
+    public Schedule(Instance instance) {
20
+        super(instance);
21
+        this.times = new int[instance.numJobs][];
22
+        for(int j = 0; j < instance.numJobs ; j++) {
23
+            this.times[j] = new int[instance.numTasks];
20 24
         }
21 25
     }
22 26
 
27
+
28
+    /** Start time of the given task. */
29
+    public int startTime(int job, int task) {
30
+        return times[job][task];
31
+    }
32
+
33
+    /** Start time of the given task. */
34
+    public int startTime(Task task) {
35
+        return startTime(task.job, task.task);
36
+    }
37
+
38
+    /** End time of the given task. */
39
+    public int endTime(int job, int task) {
40
+        return startTime(job, task) + instance.duration(job, task);
41
+    }
42
+
43
+    /** End time of the given task. */
44
+    public int endTime(Task task) {
45
+        return endTime(task.job, task.task);
46
+    }
47
+
23 48
     /** Sets the start time of the given task. */
24 49
     public void setStartTime(int job, int task, int startTime) {
25 50
         times[job][task] = startTime;
@@ -27,25 +52,25 @@ public class Schedule {
27 52
 
28 53
     /** Returns true if this schedule is valid (no constraint is violated) */
29 54
     public boolean isValid() {
30
-        for(int j = 0 ; j<pb.numJobs ; j++) {
31
-            for(int t = 1 ; t<pb.numTasks ; t++) {
32
-                if(startTime(j, t-1) + pb.duration(j, t-1) > startTime(j, t))
55
+        for(int j = 0; j<instance.numJobs ; j++) {
56
+            for(int t = 1; t< instance.numTasks ; t++) {
57
+                if(startTime(j, t-1) + instance.duration(j, t-1) > startTime(j, t))
33 58
                     return false;
34 59
             }
35
-            for(int t = 0 ; t<pb.numTasks ; t++) {
60
+            for(int t = 0; t< instance.numTasks ; t++) {
36 61
                 if(startTime(j, t) < 0)
37 62
                     return false;
38 63
             }
39 64
         }
40 65
 
41
-        for (int machine = 0 ; machine < pb.numMachines ; machine++) {
42
-            for(int j1=0 ; j1<pb.numJobs ; j1++) {
43
-                int t1 = pb.task_with_machine(j1, machine);
44
-                for(int j2=j1+1 ; j2<pb.numJobs ; j2++) {
45
-                    int t2 = pb.task_with_machine(j2, machine);
66
+        for (int machine = 0; machine < instance.numMachines ; machine++) {
67
+            for(int j1 = 0; j1< instance.numJobs ; j1++) {
68
+                int t1 = instance.task_with_machine(j1, machine);
69
+                for(int j2 = j1+1; j2< instance.numJobs ; j2++) {
70
+                    int t2 = instance.task_with_machine(j2, machine);
46 71
 
47
-                    boolean t1_first = startTime(j1, t1) + pb.duration(j1, t1) <= startTime(j2, t2);
48
-                    boolean t2_first = startTime(j2, t2) + pb.duration(j2, t2) <= startTime(j1, t1);
72
+                    boolean t1_first = startTime(j1, t1) + instance.duration(j1, t1) <= startTime(j2, t2);
73
+                    boolean t2_first = startTime(j2, t2) + instance.duration(j2, t2) <= startTime(j1, t1);
49 74
 
50 75
                     if(!t1_first && !t2_first)
51 76
                         return false;
@@ -61,32 +86,12 @@ public class Schedule {
61 86
      */
62 87
     public int makespan() {
63 88
         int max = -1;
64
-        for(int j = 0 ; j<pb.numJobs ; j++) {
65
-            max = Math.max(max, endTime(j, pb.numTasks-1));
89
+        for(int j = 0; j< instance.numJobs ; j++) {
90
+            max = Math.max(max, endTime(j, instance.numTasks-1));
66 91
         }
67 92
         return max;
68 93
     }
69 94
 
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. */
76
-    public int startTime(Task task) {
77
-        return startTime(task.job, task.task);
78
-    }
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. */
86
-    public int endTime(Task task) {
87
-        return endTime(task.job, task.task);
88
-    }
89
-
90 95
     /** Returns true if the given sequence of task is a critical path of the schedule. */
91 96
     public boolean isCriticalPath(List<Task> path) {
92 97
         if(startTime(path.get(0)) != 0) {
@@ -108,8 +113,8 @@ public class Schedule {
108 113
      */
109 114
     public List<Task> criticalPath() {
110 115
         // select task with greatest end time
111
-        Task ldd = IntStream.range(0, pb.numJobs)
112
-                .mapToObj(j -> new Task(j, pb.numTasks-1))
116
+        Task ldd = IntStream.range(0, instance.numJobs)
117
+                .mapToObj(j -> new Task(j, instance.numTasks-1))
113 118
                 .max(Comparator.comparing(this::endTime))
114 119
                 .get();
115 120
         assert endTime(ldd) == makespan();
@@ -124,7 +129,7 @@ public class Schedule {
124 129
         // starts a time 0
125 130
         while(startTime(path.getFirst()) != 0) {
126 131
             Task cur = path.getFirst();
127
-            int machine = pb.machine(cur.job, cur.task);
132
+            int machine = instance.machine(cur.job, cur.task);
128 133
 
129 134
             // will contain the task that was delaying the start
130 135
             // of our current task
@@ -140,8 +145,8 @@ public class Schedule {
140 145
             }
141 146
             if(latestPredecessor.isEmpty()) {
142 147
                 // no latest predecessor found yet, look among tasks executing on the same machine
143
-                latestPredecessor = IntStream.range(0, pb.numJobs)
144
-                        .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine)))
148
+                latestPredecessor = IntStream.range(0, instance.numJobs)
149
+                        .mapToObj(j -> new Task(j, instance.task_with_machine(j, machine)))
145 150
                         .filter(t -> endTime(t) == startTime(cur))
146 151
                         .findFirst();
147 152
             }
@@ -158,11 +163,11 @@ public class Schedule {
158 163
     public String toString() {
159 164
         StringBuilder sb = new StringBuilder();
160 165
         sb.append("\nStart times of all tasks:\n");
161
-        for(int job=0; job<pb.numJobs; job++) {
166
+        for(int job = 0; job< instance.numJobs; job++) {
162 167
             sb.append("Job ");
163 168
             sb.append(job);
164 169
             sb.append(": ");
165
-            for(int task=0; task<pb.numTasks; task++) {
170
+            for(int task = 0; task< instance.numTasks; task++) {
166 171
                 sb.append(String.format("%5d",  startTime(job, task)));
167 172
 
168 173
             }
@@ -182,15 +187,15 @@ public class Schedule {
182 187
      */
183 188
     public String asciiGantt() {
184 189
         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();
190
+        int minTaskDur = IntStream.range(0, instance.numJobs).flatMap(job -> IntStream.range(0, instance.numTasks).map(task -> instance.duration(job, task))).min().getAsInt();
186 191
         // time units by character
187 192
         int charsPerTimeUnit = minTaskDur >= 5 ? 1 : (5 / minTaskDur) +1;
188 193
         StringBuilder sb = new StringBuilder();
189 194
         sb.append("\nGantt Chart\n");
190
-        for(int job=0; job<pb.numJobs; job++) {
195
+        for(int job = 0; job< instance.numJobs; job++) {
191 196
             sb.append(String.format("Job %2d: ", job));
192 197
             int cursor = 0;
193
-            for(int task=0; task<pb.numTasks; task++) {
198
+            for(int task = 0; task< instance.numTasks; task++) {
194 199
                 Task t = new Task(job, task);
195 200
                 var st = startTime(job, task);
196 201
                 // add spaces until the start of our task
@@ -214,8 +219,8 @@ public class Schedule {
214 219
     String formatTask(Task t, int charPerTimeUnit, boolean isCritical) {
215 220
         StringBuilder sb = new StringBuilder();
216 221
         String fill = isCritical ? "*" : "-";
217
-        int dur = pb.duration(t);
218
-        int machine = pb.machine(t);
222
+        int dur = instance.duration(t);
223
+        int machine = instance.machine(t);
219 224
         int stringLength = dur * charPerTimeUnit;
220 225
         int charsForMachine = machine < 10 ? 1 : 2;
221 226
         int numSpaces = stringLength - 2 - charsForMachine; // we use 2 chars for '[' and '[' + 1 or 2 for the machine number
@@ -230,4 +235,10 @@ public class Schedule {
230 235
         sb.append("]");
231 236
         return sb.toString();
232 237
     }
238
+
239
+
240
+    @Override
241
+    public Schedule toSchedule() {
242
+        return this;
243
+    }
233 244
 }

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

@@ -3,8 +3,16 @@ package jobshop.solvers;
3 3
 import jobshop.Instance;
4 4
 import jobshop.Result;
5 5
 
6
+/** Common interface that must implemented by all solvers. */
6 7
 public interface Solver {
7 8
 
9
+    /** Look for a solution until blocked or a deadline has been met.
10
+     *
11
+     * @param instance Jobshop instance that should be solved.
12
+     * @param deadline Absolute time at which the solver should have returned a solution.
13
+     *                 This time is in milliseconds and can be compared with System.currentTimeMilliseconds()
14
+     * @return A Result containing the solution found and an explanation of why the solver exited.
15
+     */
8 16
     Result solve(Instance instance, long deadline);
9 17
 
10 18
     /** Static factory method to create a new solver based on its name. */

Loading…
Cancel
Save