Browse Source

Improve documentation

Arthur Bit-Monnot 3 years ago
parent
commit
dce9b786e9

+ 39
- 22
README.md View File

2
 
2
 
3
 This repository contains the starter code for the assignment.
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
 ❯ java -jar build/libs/JSP.jar --solver basic --instance ft06
44
 ❯ java -jar build/libs/JSP.jar --solver basic --instance ft06
19
 ```
45
 ```
46
+
20
 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 :
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
                          basic
49
                          basic
48
 Here the last line give the average `runtime` and `ecart` for each solver.
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
                   --instance INSTANCE [INSTANCE ...]
79
                   --instance INSTANCE [INSTANCE ...]
53
 
80
 
54
 Solves jobshop problems.
81
 Solves jobshop problems.
55
 
82
 
56
 named arguments:
83
 named arguments:
57
   -h, --help             show this help message and exit
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
   --solver SOLVER [SOLVER ...]
88
   --solver SOLVER [SOLVER ...]
59
                          Solver(s) to use  (space  separated  if  more than
89
                          Solver(s) to use  (space  separated  if  more than
60
                          one)
90
                          one)
61
-  -t TIMEOUT, --timeout TIMEOUT
62
-                         Solver  timeout  in  seconds   for  each  instance
63
-                         (default: 1)
64
   --instance INSTANCE [INSTANCE ...]
91
   --instance INSTANCE [INSTANCE ...]
65
                          Instance(s) to  solve  (space  separated  if  more
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
 ## IDE Support
99
 ## IDE Support
83
 
100
 

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

5
 import java.util.List;
5
 import java.util.List;
6
 import java.util.stream.Collectors;
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
     public static boolean isKnown(String instanceName) {
20
     public static boolean isKnown(String instanceName) {
11
         return bests.containsKey(instanceName);
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
     public static List<String> instancesMatching(String namePrefix) {
30
     public static List<String> instancesMatching(String namePrefix) {
15
         return Arrays.stream(instances)
31
         return Arrays.stream(instances)
16
                 .filter(i -> i.startsWith(namePrefix))
32
                 .filter(i -> i.startsWith(namePrefix))
18
                 .collect(Collectors.toList());
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
     public static int of(String instanceName) {
42
     public static int of(String instanceName) {
22
         if(!bests.containsKey(instanceName)) {
43
         if(!bests.containsKey(instanceName)) {
23
             throw new RuntimeException("Unknown best result for "+instanceName);
44
             throw new RuntimeException("Unknown best result for "+instanceName);
25
         return bests.get(instanceName);
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
     static String[] instances;
52
     static String[] instances;
53
+
54
+    // initialize the internal data structures.
30
     static {
55
     static {
31
         bests = new HashMap<>();
56
         bests = new HashMap<>();
32
         bests.put("aaa1", 11);
57
         bests.put("aaa1", 11);

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

27
             System.out.println("\nENCODING: " + enc);
27
             System.out.println("\nENCODING: " + enc);
28
 
28
 
29
             Schedule sched = enc.toSchedule();
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
             System.out.println("SCHEDULE: " + sched);
31
             System.out.println("SCHEDULE: " + sched);
33
             System.out.println("VALID: " + sched.isValid());
32
             System.out.println("VALID: " + sched.isValid());
34
             System.out.println("MAKESPAN: " + sched.makespan());
33
             System.out.println("MAKESPAN: " + sched.makespan());

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

20
     /** Number of machines, assumed to be same as number of tasks. */
20
     /** Number of machines, assumed to be same as number of tasks. */
21
     public final int numMachines;
21
     public final int numMachines;
22
 
22
 
23
+    /** Matrix containing the duration of all tasks. */
23
     final int[][] durations;
24
     final int[][] durations;
25
+
26
+    /** Matrix containing the machine on which each task must be scheduled. */
24
     final int[][] machines;
27
     final int[][] machines;
25
 
28
 
29
+    /** Duration of the given task. */
26
     public int duration(int job, int task) {
30
     public int duration(int job, int task) {
27
         return durations[job][task];
31
         return durations[job][task];
28
     }
32
     }
33
+
34
+    /** Duration of the given task. */
29
     public int duration(Task t) {
35
     public int duration(Task t) {
30
         return duration(t.job, t.task);
36
         return duration(t.job, t.task);
31
     }
37
     }
38
+
39
+    /** Machine on which the given task must be scheduled. */
32
     public int machine(int job, int task) {
40
     public int machine(int job, int task) {
33
         return machines[job][task];
41
         return machines[job][task];
34
     }
42
     }
43
+
44
+    /** Machine on which the given task must be scheduled. */
35
     public int machine(Task t) {
45
     public int machine(Task t) {
36
         return this.machine(t.job, t.task);
46
         return this.machine(t.job, t.task);
37
     }
47
     }
45
         throw new RuntimeException("No task targeting machine "+wanted_machine+" on job "+job);
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
     Instance(int numJobs, int numTasks) {
63
     Instance(int numJobs, int numTasks) {
49
         this.numJobs = numJobs;
64
         this.numJobs = numJobs;
50
         this.numTasks = numTasks;
65
         this.numTasks = numTasks;

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

8
 import java.util.HashMap;
8
 import java.util.HashMap;
9
 import java.util.List;
9
 import java.util.List;
10
 
10
 
11
-
12
 import jobshop.solvers.*;
11
 import jobshop.solvers.*;
13
 import net.sourceforge.argparse4j.ArgumentParsers;
12
 import net.sourceforge.argparse4j.ArgumentParsers;
14
 import net.sourceforge.argparse4j.inf.ArgumentParser;
13
 import net.sourceforge.argparse4j.inf.ArgumentParser;
15
 import net.sourceforge.argparse4j.inf.ArgumentParserException;
14
 import net.sourceforge.argparse4j.inf.ArgumentParserException;
16
 import net.sourceforge.argparse4j.inf.Namespace;
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
 public class Main {
21
 public class Main {
20
 
22
 
21
     /** All solvers available in this program */
23
     /** All solvers available in this program */
22
-    private static HashMap<String, Solver> solvers;
24
+    private static final HashMap<String, Solver> solvers;
23
     static {
25
     static {
24
         solvers = new HashMap<>();
26
         solvers = new HashMap<>();
25
         solvers.put("basic", new BasicSolver());
27
         solvers.put("basic", new BasicSolver());
26
         solvers.put("random", new RandomSolver());
28
         solvers.put("random", new RandomSolver());
27
-        // add new solvers here
29
+        // TODO: add new solvers here
28
     }
30
     }
29
 
31
 
30
 
32
 
31
     public static void main(String[] args) {
33
     public static void main(String[] args) {
34
+        // configure the argument parser
32
         ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()
35
         ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()
33
                 .defaultHelp(true)
36
                 .defaultHelp(true)
34
                 .description("Solves jobshop problems.");
37
                 .description("Solves jobshop problems.");
35
-
36
         parser.addArgument("-t", "--timeout")
38
         parser.addArgument("-t", "--timeout")
37
                 .setDefault(1L)
39
                 .setDefault(1L)
38
                 .type(Long.class)
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
         parser.addArgument("--solver")
42
         parser.addArgument("--solver")
41
                 .nargs("+")
43
                 .nargs("+")
42
                 .required(true)
44
                 .required(true)
43
                 .help("Solver(s) to use (space separated if more than one)");
45
                 .help("Solver(s) to use (space separated if more than one)");
44
-
45
         parser.addArgument("--instance")
46
         parser.addArgument("--instance")
46
                 .nargs("+")
47
                 .nargs("+")
47
                 .required(true)
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
         Namespace ns = null;
53
         Namespace ns = null;
51
         try {
54
         try {
52
             ns = parser.parseArgs(args);
55
             ns = parser.parseArgs(args);
53
         } catch (ArgumentParserException e) {
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
             parser.handleError(e);
63
             parser.handleError(e);
55
-            System.exit(1);
64
+            System.exit(0);
56
         }
65
         }
57
 
66
 
58
         PrintStream output = System.out;
67
         PrintStream output = System.out;
59
 
68
 
69
+        // convert the timeout from seconds to milliseconds.
60
         long solveTimeMs = ns.getLong("timeout") * 1000;
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
         List<String> solversToTest = ns.getList("solver");
74
         List<String> solversToTest = ns.getList("solver");
63
         for(String solverName : solversToTest) {
75
         for(String solverName : solversToTest) {
64
             if(!solvers.containsKey(solverName)) {
76
             if(!solvers.containsKey(solverName)) {
65
                 System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible.");
77
                 System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible.");
66
                 System.err.println("       Available solvers: " + solvers.keySet().toString());
78
                 System.err.println("       Available solvers: " + solvers.keySet().toString());
67
                 System.err.println("       You can provide your own solvers by adding them to the `Main.solvers` HashMap.");
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
         List<String> instances = new ArrayList<>();
85
         List<String> instances = new ArrayList<>();
86
+        List<String> instancePrefixes = ns.getList("instance");
73
         for(String instancePrefix : instancePrefixes) {
87
         for(String instancePrefix : instancePrefixes) {
74
-            List<String> matches = BestKnownResult.instancesMatching(instancePrefix);
88
+            List<String> matches = BestKnownResults.instancesMatching(instancePrefix);
75
             if(matches.isEmpty()) {
89
             if(matches.isEmpty()) {
76
                 System.err.println("ERROR: instance prefix \"" + instancePrefix + "\" does not match any instance.");
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
                 System.exit(1);
92
                 System.exit(1);
79
             }
93
             }
80
             instances.addAll(matches);
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
         try {
102
         try {
103
+            // header of the result table :
104
+            //   - solver names (first line)
105
+            //   - name of each column (second line)
87
             output.print(  "                         ");
106
             output.print(  "                         ");
88
             for(String s : solversToTest)
107
             for(String s : solversToTest)
89
                 output.printf("%-30s", s);
108
                 output.printf("%-30s", s);
94
             }
113
             }
95
             output.println();
114
             output.println();
96
 
115
 
97
-
116
+            // for all instances, load it from f
98
             for(String instanceName : instances) {
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
                 Path path = Paths.get("instances/", instanceName);
122
                 Path path = Paths.get("instances/", instanceName);
103
                 Instance instance = Instance.fromFile(path);
123
                 Instance instance = Instance.fromFile(path);
104
 
124
 
125
+                // print some general statistics on the instance
105
                 output.printf("%-8s %-5s %4d      ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
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
                 for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
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
                     String solverName = solversToTest.get(solverId);
132
                     String solverName = solversToTest.get(solverId);
109
                     Solver solver = solvers.get(solverName);
133
                     Solver solver = solvers.get(solverName);
134
+
135
+                    // start chronometer and compute deadline for the solver to provide a result.
110
                     long start = System.currentTimeMillis();
136
                     long start = System.currentTimeMillis();
111
                     long deadline = System.currentTimeMillis() + solveTimeMs;
137
                     long deadline = System.currentTimeMillis() + solveTimeMs;
138
+                    // run the solver on the current instance
112
                     Result result = solver.solve(instance, deadline);
139
                     Result result = solver.solve(instance, deadline);
140
+                    // measure elapsed time (in milliseconds)
113
                     long runtime = System.currentTimeMillis() - start;
141
                     long runtime = System.currentTimeMillis() - start;
114
 
142
 
143
+                    // check that the solver returned a valid solution
115
                     if(!result.schedule.isValid()) {
144
                     if(!result.schedule.isValid()) {
116
                         System.err.println("ERROR: solver returned an invalid schedule");
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
                     int makespan = result.schedule.makespan();
150
                     int makespan = result.schedule.makespan();
122
                     float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
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
                     output.printf("%7d %8s %5.1f        ", runtime, makespan, dist);
155
                     output.printf("%7d %8s %5.1f        ", runtime, makespan, dist);
127
                     output.flush();
156
                     output.flush();
128
                 }
157
                 }
129
                 output.println();
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
             output.printf("%-8s %-5s %4s      ", "AVG", "-", "-");
163
             output.printf("%-8s %-5s %4s      ", "AVG", "-", "-");
135
             for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
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
         } catch (Exception e) {
170
         } catch (Exception e) {
171
+            // there was uncought exception, print the stack trace and exit with error.
142
             e.printStackTrace();
172
             e.printStackTrace();
143
             System.exit(1);
173
             System.exit(1);
144
         }
174
         }

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

41
         for(int m = 0 ; m<schedule.pb.numMachines ; m++) {
41
         for(int m = 0 ; m<schedule.pb.numMachines ; m++) {
42
             final int machine = m;
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
             tasksByMachine[m] =
45
             tasksByMachine[m] =
46
                     IntStream.range(0, pb.numJobs) // all job numbers
46
                     IntStream.range(0, pb.numJobs) // all job numbers
47
                             .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) // all tasks on this machine (one per job)
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
 
2
 
3
 import java.util.Objects;
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
 public final class Task {
9
 public final class Task {
10
 
10
 
11
     /** Identifier of the job */
11
     /** Identifier of the job */

Loading…
Cancel
Save