Browse Source

Make Encoding.toSchedule() return an optional to handle the case where the solution is not valid.

Arthur Bit-Monnot 5 months ago
parent
commit
1957a255ba

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

@@ -49,7 +49,7 @@ public class Instance {
49 49
         return this.machine(t.job, t.task);
50 50
     }
51 51
 
52
-    /** among the tasks of the given job, returns the task index that uses the given machine. */
52
+    /** Among the tasks of the given job, returns the task number of the one that uses the given machine. */
53 53
     public int task_with_machine(int job, int wanted_machine) {
54 54
         for(int task = 0 ; task < numTasks ; task++) {
55 55
             if(machine(job, task) == wanted_machine)

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

@@ -8,6 +8,7 @@ import java.util.Arrays;
8 8
 import java.util.List;
9 9
 import java.util.stream.Collectors;
10 10
 
11
+import jobshop.encodings.Schedule;
11 12
 import jobshop.solvers.*;
12 13
 import net.sourceforge.argparse4j.ArgumentParsers;
13 14
 import net.sourceforge.argparse4j.inf.ArgumentParser;
@@ -122,13 +123,15 @@ public class Main {
122 123
                     long runtime = System.currentTimeMillis() - start;
123 124
 
124 125
                     // check that the solver returned a valid solution
125
-                    if(!result.schedule.isValid()) {
126
+                    if(result.schedule.isEmpty() || !result.schedule.get().isValid()) {
126 127
                         System.err.println("ERROR: solver returned an invalid schedule");
127 128
                         System.exit(1); // bug in implementation, bail out
128 129
                     }
130
+                    // we have a valid schedule
131
+                    Schedule schedule = result.schedule.get();
129 132
 
130 133
                     // compute some statistics on the solution and print them.
131
-                    int makespan = result.schedule.makespan();
134
+                    int makespan = schedule.makespan();
132 135
                     float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
133 136
                     avg_runtimes[solverId] += (float) runtime / (float) instances.size();
134 137
                     avg_distances[solverId] += dist / (float) instances.size();

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

@@ -28,7 +28,7 @@ public class MainTest {
28 28
             System.out.println("\nENCODING: " + enc);
29 29
 
30 30
             // convert to a schedule and display
31
-            Schedule schedule = enc.toSchedule();
31
+            Schedule schedule = enc.toSchedule().get();
32 32
             System.out.println("VALID: " + schedule.isValid());
33 33
             System.out.println("MAKESPAN: " + schedule.makespan());
34 34
             System.out.println("SCHEDULE: " + schedule.toString());

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

@@ -2,10 +2,22 @@ package jobshop;
2 2
 
3 3
 import jobshop.encodings.Schedule;
4 4
 
5
+import java.util.Optional;
6
+
5 7
 /** Class representing the result of a solver. */
6 8
 public class Result {
7 9
 
8
-    public Result(Instance instance, Schedule schedule, ExitCause cause) {
10
+    /** Instance that was solved. */
11
+    public final Instance instance;
12
+
13
+    /** A schedule of the solution or Optional.empty() if no solution was found. */
14
+    public final Optional<Schedule> schedule;
15
+
16
+    /** Reason why the solver exited with this solution. */
17
+    public final ExitCause cause;
18
+
19
+    /** Creates a new Result object with the corresponding fields. */
20
+    public Result(Instance instance, Optional<Schedule> schedule, ExitCause cause) {
9 21
         this.instance = instance;
10 22
         this.schedule = schedule;
11 23
         this.cause = cause;
@@ -20,15 +32,4 @@ public class Result {
20 32
         /** The solver was not able to further improve the solution (e.g. blocked in a local minima. */
21 33
         Blocked
22 34
     }
23
-
24
-    /** Instance that was solved. */
25
-    public final Instance instance;
26
-
27
-    /** A schedule of the solution or null if no solution was found. */
28
-    public final Schedule schedule;
29
-
30
-    /** Reason why the solver exited with this solution. */
31
-    public final ExitCause cause;
32
-
33
-
34 35
 }

+ 9
- 2
src/main/java/jobshop/encodings/Encoding.java View File

@@ -2,6 +2,8 @@ package jobshop.encodings;
2 2
 
3 3
 import jobshop.Instance;
4 4
 
5
+import java.util.Optional;
6
+
5 7
 /** Common class for all encodings.
6 8
  *
7 9
  * The only requirement for this class is to provide a conversion from the encoding into a Schedule.
@@ -11,10 +13,15 @@ public abstract class Encoding {
11 13
     /** Problem instance of which this is the solution. */
12 14
     public final Instance instance;
13 15
 
16
+    /** Constructor, that initializes the instance field. */
14 17
     public Encoding(Instance instance) {
15 18
         this.instance = instance;
16 19
     }
17 20
 
18
-    /** Convert into a schedule. */
19
-    public abstract Schedule toSchedule();
21
+    /** Attempts to convert this solution into a schedule.
22
+     *
23
+     * @return A empty optional if the solution is not valid. Otherwise the optional will contain a valid schedule of
24
+     *         the solution.
25
+     */
26
+    public abstract Optional<Schedule> toSchedule();
20 27
 }

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

@@ -4,6 +4,7 @@ import jobshop.Instance;
4 4
 
5 5
 import java.util.Arrays;
6 6
 import java.util.Comparator;
7
+import java.util.Optional;
7 8
 import java.util.stream.IntStream;
8 9
 
9 10
 /** Encoding of the solution of a jobshop problem by job numbers. */
@@ -56,7 +57,7 @@ public class JobNumbers extends Encoding {
56 57
     }
57 58
 
58 59
     @Override
59
-    public Schedule toSchedule() {
60
+    public Optional<Schedule> toSchedule() {
60 61
         // time at which each machine is going to be freed
61 62
         int[] nextFreeTimeResource = new int[instance.numMachines];
62 63
 
@@ -79,7 +80,7 @@ public class JobNumbers extends Encoding {
79 80
             nextTask[job] = task + 1;
80 81
         }
81 82
 
82
-        return schedule;
83
+        return Optional.of(schedule);
83 84
     }
84 85
 
85 86
     @Override

+ 10
- 4
src/main/java/jobshop/encodings/ResourceOrder.java View File

@@ -2,6 +2,7 @@ package jobshop.encodings;
2 2
 
3 3
 import jobshop.Instance;
4 4
 
5
+import java.util.Arrays;
5 6
 import java.util.Comparator;
6 7
 import java.util.Optional;
7 8
 import java.util.stream.IntStream;
@@ -85,7 +86,7 @@ public class ResourceOrder extends Encoding {
85 86
     }
86 87
 
87 88
     @Override
88
-    public Schedule toSchedule() {
89
+    public Optional<Schedule> toSchedule() {
89 90
         // indicate for each task that have been scheduled, its start time
90 91
         Schedule schedule = new Schedule(instance);
91 92
 
@@ -130,16 +131,21 @@ public class ResourceOrder extends Encoding {
130 131
                 releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task);
131 132
             } else {
132 133
                 // no tasks are schedulable, there is no solution for this resource ordering
133
-                return null;
134
+                return Optional.empty();
134 135
             }
135 136
         }
136 137
         // we exited the loop : all tasks have been scheduled successfully
137
-        return schedule;
138
+        return Optional.of(schedule);
138 139
     }
139 140
 
140 141
     /** Creates an exact copy of this resource order. */
141 142
     public ResourceOrder copy() {
142
-        return new ResourceOrder(this.toSchedule());
143
+        var schedule = this.toSchedule();
144
+        if (schedule.isEmpty()) {
145
+            throw new RuntimeException("Cannot copy an invalid ResourceOrder");
146
+        } else {
147
+            return new ResourceOrder(schedule.get());
148
+        }
143 149
     }
144 150
 
145 151
     @Override

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

@@ -238,7 +238,7 @@ public class Schedule extends Encoding {
238 238
 
239 239
 
240 240
     @Override
241
-    public Schedule toSchedule() {
242
-        return this;
241
+    public Optional<Schedule> toSchedule() {
242
+        return Optional.of(this);
243 243
     }
244 244
 }

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

@@ -4,6 +4,7 @@ import jobshop.*;
4 4
 import jobshop.encodings.JobNumbers;
5 5
 import jobshop.encodings.Schedule;
6 6
 
7
+import java.util.Optional;
7 8
 import java.util.Random;
8 9
 
9 10
 /** A solver that generates random solutions until a deadline is met.
@@ -22,12 +23,14 @@ public class RandomSolver implements Solver {
22 23
                 sol.jobs[sol.nextToSet++] = j;
23 24
             }
24 25
         }
25
-        Schedule best = sol.toSchedule();
26
+        Optional<Schedule> best = sol.toSchedule();
26 27
         while(deadline - System.currentTimeMillis() > 1) {
27 28
             shuffleArray(sol.jobs, generator);
28
-            Schedule s = sol.toSchedule();
29
-            if(s.makespan() < best.makespan()) {
30
-                best = s;
29
+            Optional<Schedule> candidate = sol.toSchedule();
30
+            if(candidate.isPresent()) {
31
+                if (best.isEmpty() || candidate.get().makespan() < best.get().makespan()) {
32
+                    best = candidate;
33
+                }
31 34
             }
32 35
         }
33 36
 

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

@@ -24,7 +24,7 @@ public class EncodingTests {
24 24
         enc.jobs[enc.nextToSet++] = 0;
25 25
         enc.jobs[enc.nextToSet++] = 1;
26 26
 
27
-        Schedule sched = enc.toSchedule();
27
+        Schedule sched = enc.toSchedule().get();
28 28
         // TODO: make it print something meaningful
29 29
         // by implementing the toString() method
30 30
         System.out.println(sched);
@@ -42,7 +42,7 @@ public class EncodingTests {
42 42
         enc.jobs[enc.nextToSet++] = 0;
43 43
         enc.jobs[enc.nextToSet++] = 1;
44 44
 
45
-        sched = enc.toSchedule();
45
+        sched = enc.toSchedule().get();
46 46
         assert sched.isValid();
47 47
         assert sched.makespan() == 14;
48 48
     }
@@ -60,15 +60,15 @@ public class EncodingTests {
60 60
         enc.jobs[enc.nextToSet++] = 0;
61 61
         enc.jobs[enc.nextToSet++] = 1;
62 62
 
63
-        Schedule sched = enc.toSchedule();
63
+        Schedule sched = enc.toSchedule().get();
64 64
         assert sched.isValid();
65 65
         assert sched.makespan() == 12;
66 66
 
67 67
         Solver solver = new BasicSolver();
68 68
         Result result = solver.solve(instance, System.currentTimeMillis() + 10);
69 69
 
70
-        assert result.schedule.isValid();
71
-        assert result.schedule.makespan() == sched.makespan(); // should have the same makespan
70
+        assert result.schedule.get().isValid();
71
+        assert result.schedule.get().makespan() == sched.makespan(); // should have the same makespan
72 72
     }
73 73
 
74 74
 }

Loading…
Cancel
Save