|
@@ -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
|
}
|