Browse Source

Improve documentation

Arthur Bit-Monnot 5 months ago
parent
commit
dce9b786e9

+ 39
- 22
README.md View File

@@ -2,21 +2,48 @@
2 2
 
3 3
 This repository contains the starter code for the assignment.
4 4
 
5
+## Working in IntelliJ
5 6
 
6
-## Compile
7
+For working on this project, we recommend using the IntelliJ-IDEA development environment. It is available in INSA's 
8
+classrooms as well as on `montp.insa-toulouse.fr`.
7 9
 
8
-Compilation instructions are given for Linux. On windows you can use the `gradlew.bat` script.
10
+To import the project in IntelliJ (once IntelliJ is running):
11
+
12
+ - Open a new project : `Open project` or `File > Open`
13
+ - Select the `gradle.build` file in the cloned repository. 
14
+ - Select `Open as project`.
15
+
16
+To run the program in IntelliJ, you can 
17
+
18
+ - Right click on the `src/main/java/Jobshop/Main` class in the project view.
19
+ - Select `Run Main.main()`. It should complain that some arguments are missing.
20
+ - Give it the expected command line arguments : `Run > Edit Configuration`, then fill in the `Program arguments` text box.
21
+
22
+
23
+## Working on the command line (Gradle)
24
+
25
+Compilation instructions are given for Linux. On Windows you can use the `gradlew.bat` script (but you are on your own).
9 26
 
10 27
 ```
11
-❯ ./gradlew build  # Compiles the project
12
-❯ ./gradlew jar      # Creates a fat-jar in build/libs/JSP.jar
28
+❯ ./gradlew build    # Compiles the project
13 29
 ```
14 30
 
15
-The compiled jar is now `build/libs/JSP.jar` can be executed like so :
31
+The project can be executed directly with `gradle` by specifying the arguments like so :
16 32
 
17 33
 ```
34
+❯ ./gradlew run --args="--solver basic random --instance aaa1 ft"
35
+```
36
+
37
+You can also build an executable jar file, and run it with the java command.
38
+This is especially useful if you want to run it on another machine.
39
+
40
+```
41
+ # Create a jar file with all dependencies in build/libs/JSP.jar
42
+❯ ./gradlew jar     
43
+# Run the jar file. Only requires a Java Runtime Environment (JRE)
18 44
 ❯ java -jar build/libs/JSP.jar --solver basic --instance ft06
19 45
 ```
46
+
20 47
 The command line above indicates that we want to solve the instance named`ft06` with the `basic` solver. It should give an output like the following :
21 48
 ```
22 49
                          basic
@@ -48,36 +75,26 @@ AVG      -        -          0.3        -  31.5          999.0        -  20.4
48 75
 Here the last line give the average `runtime` and `ecart` for each solver.
49 76
 
50 77
 ```
51
-usage: jsp-solver [-h]  [-t TIMEOUT] --solver SOLVER [SOLVER ...]
78
+sage: jsp-solver [-h] [-t TIMEOUT] --solver SOLVER [SOLVER ...]
52 79
                   --instance INSTANCE [INSTANCE ...]
53 80
 
54 81
 Solves jobshop problems.
55 82
 
56 83
 named arguments:
57 84
   -h, --help             show this help message and exit
85
+  -t TIMEOUT, --timeout TIMEOUT
86
+                         Solver  timeout  in  seconds  for  each  instance. 
87
+                         (default: 1)
58 88
   --solver SOLVER [SOLVER ...]
59 89
                          Solver(s) to use  (space  separated  if  more than
60 90
                          one)
61
-  -t TIMEOUT, --timeout TIMEOUT
62
-                         Solver  timeout  in  seconds   for  each  instance
63
-                         (default: 1)
64 91
   --instance INSTANCE [INSTANCE ...]
65 92
                          Instance(s) to  solve  (space  separated  if  more
66
-                         than one)
67
-
68
-
69
-```
70
-
71
-### Running directly from Gradle
72
-
73
-The project can be executed directly with `gradle` by specifying the arguments like so :
74
-
75
-```
76
-❯ ./gradlew run --args="--solver basic random --instance aaa1 ft06 ft10 ft20"
93
+                         than one). All instances  starting  with the given
94
+                         String will be  selected.  (e.g.  "ft" will select
95
+                         the instances ft06, ft10 and ft20.
77 96
 ```
78 97
 
79
-This notably ensures that sources have been recompiled whenever necessary.
80
-
81 98
 
82 99
 ## IDE Support
83 100
 

src/main/java/jobshop/BestKnownResult.java → src/main/java/jobshop/BestKnownResults.java View File

@@ -5,12 +5,28 @@ import java.util.HashMap;
5 5
 import java.util.List;
6 6
 import java.util.stream.Collectors;
7 7
 
8
-public class BestKnownResult {
8
+/**
9
+ * This class contains the best known results for common jobshop instances.
10
+ * Note that the best known result might not have been proven to be the optimal solution
11
+ * for the instance.
12
+ */
13
+public class BestKnownResults {
9 14
 
15
+    /**
16
+     * Checks whether we have data available for the provided instance.
17
+     * @param instanceName Name of the instance.
18
+     * @return True if the isntance is known, false otherwise.
19
+     */
10 20
     public static boolean isKnown(String instanceName) {
11 21
         return bests.containsKey(instanceName);
12 22
     }
13 23
 
24
+    /**
25
+     * Returns all instances that start with the given prefix.
26
+     * For example, the prefix "ft" should match the instances "ft06", "ft10" and "ft20"
27
+     * @param namePrefix Prefix that should be looked-up.
28
+     * @return All instances that start with the given prefix, in alphabetical order.
29
+     */
14 30
     public static List<String> instancesMatching(String namePrefix) {
15 31
         return Arrays.stream(instances)
16 32
                 .filter(i -> i.startsWith(namePrefix))
@@ -18,6 +34,11 @@ public class BestKnownResult {
18 34
                 .collect(Collectors.toList());
19 35
     }
20 36
 
37
+    /**
38
+     * Returns the best known result for the given instance name.
39
+     * @param instanceName Instance of which we want to retrieve the best result.
40
+     * @return Best makespan that has ever been found for this instance.
41
+     */
21 42
     public static int of(String instanceName) {
22 43
         if(!bests.containsKey(instanceName)) {
23 44
             throw new RuntimeException("Unknown best result for "+instanceName);
@@ -25,8 +46,12 @@ public class BestKnownResult {
25 46
         return bests.get(instanceName);
26 47
     }
27 48
 
28
-    static private HashMap<String, Integer> bests;
49
+    // all best results.
50
+    static final private HashMap<String, Integer> bests;
51
+    // a sorted array of instance names
29 52
     static String[] instances;
53
+
54
+    // initialize the internal data structures.
30 55
     static {
31 56
         bests = new HashMap<>();
32 57
         bests.put("aaa1", 11);

+ 1
- 2
src/main/java/jobshop/DebuggingMain.java View File

@@ -27,8 +27,7 @@ public class DebuggingMain {
27 27
             System.out.println("\nENCODING: " + enc);
28 28
 
29 29
             Schedule sched = enc.toSchedule();
30
-            // TODO: make it print something meaningful
31
-            // by implementing the toString() method
30
+            // TODO: make it print something meaningful by implementing the Schedule.toString() method
32 31
             System.out.println("SCHEDULE: " + sched);
33 32
             System.out.println("VALID: " + sched.isValid());
34 33
             System.out.println("MAKESPAN: " + sched.makespan());

+ 15
- 0
src/main/java/jobshop/Instance.java View File

@@ -20,18 +20,28 @@ public class Instance {
20 20
     /** Number of machines, assumed to be same as number of tasks. */
21 21
     public final int numMachines;
22 22
 
23
+    /** Matrix containing the duration of all tasks. */
23 24
     final int[][] durations;
25
+
26
+    /** Matrix containing the machine on which each task must be scheduled. */
24 27
     final int[][] machines;
25 28
 
29
+    /** Duration of the given task. */
26 30
     public int duration(int job, int task) {
27 31
         return durations[job][task];
28 32
     }
33
+
34
+    /** Duration of the given task. */
29 35
     public int duration(Task t) {
30 36
         return duration(t.job, t.task);
31 37
     }
38
+
39
+    /** Machine on which the given task must be scheduled. */
32 40
     public int machine(int job, int task) {
33 41
         return machines[job][task];
34 42
     }
43
+
44
+    /** Machine on which the given task must be scheduled. */
35 45
     public int machine(Task t) {
36 46
         return this.machine(t.job, t.task);
37 47
     }
@@ -45,6 +55,11 @@ public class Instance {
45 55
         throw new RuntimeException("No task targeting machine "+wanted_machine+" on job "+job);
46 56
     }
47 57
 
58
+    /**
59
+     * Creates a new instance, with uninitialized durations and machines.
60
+     * This should no be called directly. Instead, Instance objects should be created with the
61
+     * <code>Instance.fromFile()</code> static method.
62
+     */
48 63
     Instance(int numJobs, int numTasks) {
49 64
         this.numJobs = numJobs;
50 65
         this.numTasks = numTasks;

+ 54
- 24
src/main/java/jobshop/Main.java View File

@@ -8,82 +8,101 @@ import java.util.Arrays;
8 8
 import java.util.HashMap;
9 9
 import java.util.List;
10 10
 
11
-
12 11
 import jobshop.solvers.*;
13 12
 import net.sourceforge.argparse4j.ArgumentParsers;
14 13
 import net.sourceforge.argparse4j.inf.ArgumentParser;
15 14
 import net.sourceforge.argparse4j.inf.ArgumentParserException;
16 15
 import net.sourceforge.argparse4j.inf.Namespace;
17 16
 
18
-
17
+/**
18
+ * This class is the main entry point for testing solver on instances.
19
+ * It provides
20
+ */
19 21
 public class Main {
20 22
 
21 23
     /** All solvers available in this program */
22
-    private static HashMap<String, Solver> solvers;
24
+    private static final HashMap<String, Solver> solvers;
23 25
     static {
24 26
         solvers = new HashMap<>();
25 27
         solvers.put("basic", new BasicSolver());
26 28
         solvers.put("random", new RandomSolver());
27
-        // add new solvers here
29
+        // TODO: add new solvers here
28 30
     }
29 31
 
30 32
 
31 33
     public static void main(String[] args) {
34
+        // configure the argument parser
32 35
         ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()
33 36
                 .defaultHelp(true)
34 37
                 .description("Solves jobshop problems.");
35
-
36 38
         parser.addArgument("-t", "--timeout")
37 39
                 .setDefault(1L)
38 40
                 .type(Long.class)
39
-                .help("Solver timeout in seconds for each instance");
41
+                .help("Solver timeout in seconds for each instance. Default is 1 second.");
40 42
         parser.addArgument("--solver")
41 43
                 .nargs("+")
42 44
                 .required(true)
43 45
                 .help("Solver(s) to use (space separated if more than one)");
44
-
45 46
         parser.addArgument("--instance")
46 47
                 .nargs("+")
47 48
                 .required(true)
48
-                .help("Instance(s) to solve (space separated if more than one)");
49
+                .help("Instance(s) to solve (space separated if more than one). All instances starting with the given " +
50
+                        "string will be selected. (e.g. \"ft\" will select the instances ft06, ft10 and ft20.");
49 51
 
52
+        // parse command line arguments
50 53
         Namespace ns = null;
51 54
         try {
52 55
             ns = parser.parseArgs(args);
53 56
         } catch (ArgumentParserException e) {
57
+            // error while parsing arguments, provide helpful error message and exit.
58
+            System.err.println("Invalid arguments provided to the program.\n");
59
+            System.err.println("In IntelliJ, you can provide arguments to the program by opening the dialog,");
60
+            System.err.println("\"Run > Edit Configurations\" and filling in the \"program arguments\" box.");
61
+            System.err.println("See the README for a documentation of the expected arguments.");
62
+            System.err.println();
54 63
             parser.handleError(e);
55
-            System.exit(1);
64
+            System.exit(0);
56 65
         }
57 66
 
58 67
         PrintStream output = System.out;
59 68
 
69
+        // convert the timeout from seconds to milliseconds.
60 70
         long solveTimeMs = ns.getLong("timeout") * 1000;
61 71
 
72
+        // 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.
62 74
         List<String> solversToTest = ns.getList("solver");
63 75
         for(String solverName : solversToTest) {
64 76
             if(!solvers.containsKey(solverName)) {
65 77
                 System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible.");
66 78
                 System.err.println("       Available solvers: " + solvers.keySet().toString());
67 79
                 System.err.println("       You can provide your own solvers by adding them to the `Main.solvers` HashMap.");
68
-                System.exit(1);
80
+                System.exit(0);
69 81
             }
70 82
         }
71
-        List<String> instancePrefixes = ns.getList("instance");
83
+
84
+        // retrieve all instances on which we should run the solvers.
72 85
         List<String> instances = new ArrayList<>();
86
+        List<String> instancePrefixes = ns.getList("instance");
73 87
         for(String instancePrefix : instancePrefixes) {
74
-            List<String> matches = BestKnownResult.instancesMatching(instancePrefix);
88
+            List<String> matches = BestKnownResults.instancesMatching(instancePrefix);
75 89
             if(matches.isEmpty()) {
76 90
                 System.err.println("ERROR: instance prefix \"" + instancePrefix + "\" does not match any instance.");
77
-                System.err.println("       available instances: " + Arrays.toString(BestKnownResult.instances));
91
+                System.err.println("       available instances: " + Arrays.toString(BestKnownResults.instances));
78 92
                 System.exit(1);
79 93
             }
80 94
             instances.addAll(matches);
81 95
         }
82 96
 
83
-        float[] runtimes = new float[solversToTest.size()];
84
-        float[] distances = new float[solversToTest.size()];
97
+        // average runtime of each solver
98
+        float[] avg_runtimes = new float[solversToTest.size()];
99
+        // average distance to best known result for each solver
100
+        float[] avg_distances = new float[solversToTest.size()];
85 101
 
86 102
         try {
103
+            // header of the result table :
104
+            //   - solver names (first line)
105
+            //   - name of each column (second line)
87 106
             output.print(  "                         ");
88 107
             for(String s : solversToTest)
89 108
                 output.printf("%-30s", s);
@@ -94,51 +113,62 @@ public class Main {
94 113
             }
95 114
             output.println();
96 115
 
97
-
116
+            // for all instances, load it from f
98 117
             for(String instanceName : instances) {
99
-                int bestKnown = BestKnownResult.of(instanceName);
100
-
118
+                // get the best known result for this instance
119
+                int bestKnown = BestKnownResults.of(instanceName);
101 120
 
121
+                // load instance from file.
102 122
                 Path path = Paths.get("instances/", instanceName);
103 123
                 Instance instance = Instance.fromFile(path);
104 124
 
125
+                // print some general statistics on the instance
105 126
                 output.printf("%-8s %-5s %4d      ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
106 127
 
128
+                // run all selected solvers on the instance and print the results
107 129
                 for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
130
+                    // 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.
108 132
                     String solverName = solversToTest.get(solverId);
109 133
                     Solver solver = solvers.get(solverName);
134
+
135
+                    // start chronometer and compute deadline for the solver to provide a result.
110 136
                     long start = System.currentTimeMillis();
111 137
                     long deadline = System.currentTimeMillis() + solveTimeMs;
138
+                    // run the solver on the current instance
112 139
                     Result result = solver.solve(instance, deadline);
140
+                    // measure elapsed time (in milliseconds)
113 141
                     long runtime = System.currentTimeMillis() - start;
114 142
 
143
+                    // check that the solver returned a valid solution
115 144
                     if(!result.schedule.isValid()) {
116 145
                         System.err.println("ERROR: solver returned an invalid schedule");
117
-                        System.exit(1);
146
+                        System.exit(1); // bug in implementation, bail out
118 147
                     }
119 148
 
120
-                    assert result.schedule.isValid();
149
+                    // compute some statistics on the solution and print them.
121 150
                     int makespan = result.schedule.makespan();
122 151
                     float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
123
-                    runtimes[solverId] += (float) runtime / (float) instances.size();
124
-                    distances[solverId] += dist / (float) instances.size();
152
+                    avg_runtimes[solverId] += (float) runtime / (float) instances.size();
153
+                    avg_distances[solverId] += dist / (float) instances.size();
125 154
 
126 155
                     output.printf("%7d %8s %5.1f        ", runtime, makespan, dist);
127 156
                     output.flush();
128 157
                 }
129 158
                 output.println();
130
-
131 159
             }
132 160
 
133 161
 
162
+            // we have finished all benchmarks, compute the average solve time and distance of each solver.
134 163
             output.printf("%-8s %-5s %4s      ", "AVG", "-", "-");
135 164
             for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
136
-                output.printf("%7.1f %8s %5.1f        ", runtimes[solverId], "-", distances[solverId]);
165
+                output.printf("%7.1f %8s %5.1f        ", avg_runtimes[solverId], "-", avg_distances[solverId]);
137 166
             }
138 167
 
139 168
 
140 169
 
141 170
         } catch (Exception e) {
171
+            // there was uncought exception, print the stack trace and exit with error.
142 172
             e.printStackTrace();
143 173
             System.exit(1);
144 174
         }

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

@@ -41,7 +41,7 @@ public class ResourceOrder extends Encoding {
41 41
         for(int m = 0 ; m<schedule.pb.numMachines ; m++) {
42 42
             final int machine = m;
43 43
 
44
-            // for thi machine, find all tasks that are executed on it and sort them by their start time
44
+            // for this machine, find all tasks that are executed on it and sort them by their start time
45 45
             tasksByMachine[m] =
46 46
                     IntStream.range(0, pb.numJobs) // all job numbers
47 47
                             .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) // all tasks on this machine (one per job)

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

@@ -2,10 +2,10 @@ package jobshop.encodings;
2 2
 
3 3
 import java.util.Objects;
4 4
 
5
-/** Represents a task (job,task) of an jobshop problem.
5
+/** Represents a task (job,task) of a jobshop problem.
6 6
  *
7
- * Example : (2, 3) repesents the fourth task of the third job. (remeber that we tart counting at 0)
8
- * */
7
+ * Example : (2, 3) represents the fourth task of the third job. (remember that we start counting at 0)
8
+ **/
9 9
 public final class Task {
10 10
 
11 11
     /** Identifier of the job */

Loading…
Cancel
Save