160 lines
7.3 KiB
Java
160 lines
7.3 KiB
Java
package jobshop;
|
|
|
|
import java.io.PrintStream;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.stream.Collectors;
|
|
|
|
import jobshop.encodings.Schedule;
|
|
import jobshop.solvers.*;
|
|
import net.sourceforge.argparse4j.ArgumentParsers;
|
|
import net.sourceforge.argparse4j.inf.ArgumentParser;
|
|
import net.sourceforge.argparse4j.inf.ArgumentParserException;
|
|
import net.sourceforge.argparse4j.inf.Namespace;
|
|
|
|
/**
|
|
* This class is the main entry point for doing comparative performance tests of solvers.
|
|
*/
|
|
public class Main {
|
|
|
|
public static void main(String[] args) {
|
|
// configure the argument parser
|
|
ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()
|
|
.defaultHelp(true)
|
|
.description("Solves jobshop problems.");
|
|
parser.addArgument("-t", "--timeout")
|
|
.setDefault(1L)
|
|
.type(Long.class)
|
|
.help("Solver timeout in seconds for each instance. Default is 1 second.");
|
|
parser.addArgument("--solver")
|
|
.nargs("+")
|
|
.required(true)
|
|
.help("Solver(s) to use (space separated if more than one)");
|
|
parser.addArgument("--instance")
|
|
.nargs("+")
|
|
.required(true)
|
|
.help("Instance(s) to solve (space separated if more than one). All instances starting with the given " +
|
|
"string will be selected. (e.g. \"ft\" will select the instances ft06, ft10 and ft20.");
|
|
|
|
// parse command line arguments
|
|
Namespace ns = null;
|
|
try {
|
|
ns = parser.parseArgs(args);
|
|
} catch (ArgumentParserException e) {
|
|
// error while parsing arguments, provide helpful error message and exit.
|
|
System.err.println("Invalid arguments provided to the program.\n");
|
|
System.err.println("In IntelliJ, you can provide arguments to the program by opening the dialog,");
|
|
System.err.println("\"Run > Edit Configurations\" and filling in the \"program arguments\" box.");
|
|
System.err.println("See the README for a documentation of the expected arguments.");
|
|
System.err.println();
|
|
parser.handleError(e);
|
|
System.exit(0);
|
|
}
|
|
|
|
PrintStream output = System.out;
|
|
|
|
// convert the timeout from seconds to milliseconds.
|
|
long solveTimeMs = ns.getLong("timeout") * 1000;
|
|
|
|
// Get the list of solvers that we should benchmark.
|
|
// We also check that we have a solver available for the given name and print an error message otherwise.
|
|
List<String> solversToTest = ns.getList("solver");
|
|
List<Solver> solvers = solversToTest.stream().map(Solver::getSolver).collect(Collectors.toList());
|
|
|
|
// retrieve all instances on which we should run the solvers.
|
|
List<String> instances = new ArrayList<>();
|
|
List<String> instancePrefixes = ns.getList("instance");
|
|
for(String instancePrefix : instancePrefixes) {
|
|
List<String> matches = BestKnownResults.instancesMatching(instancePrefix);
|
|
if(matches.isEmpty()) {
|
|
System.err.println("ERROR: instance prefix \"" + instancePrefix + "\" does not match any instance.");
|
|
System.err.println(" available instances: " + Arrays.toString(BestKnownResults.instances));
|
|
System.exit(1);
|
|
}
|
|
instances.addAll(matches);
|
|
}
|
|
|
|
// average runtime of each solver
|
|
float[] avg_runtimes = new float[solversToTest.size()];
|
|
// average distance to best known result for each solver
|
|
float[] avg_distances = new float[solversToTest.size()];
|
|
|
|
try {
|
|
// header of the result table :
|
|
// - solver names (first line)
|
|
// - name of each column (second line)
|
|
output.print( " ");
|
|
for(String s : solversToTest)
|
|
output.printf("%-30s", s);
|
|
output.println();
|
|
output.print("instance size best ");
|
|
for(String s : solversToTest) {
|
|
output.print("runtime makespan ecart ");
|
|
}
|
|
output.println();
|
|
|
|
// for all instances, load it from f
|
|
for(String instanceName : instances) {
|
|
// get the best known result for this instance
|
|
int bestKnown = BestKnownResults.of(instanceName);
|
|
|
|
// load instance from file.
|
|
Path path = Paths.get("instances/", instanceName);
|
|
Instance instance = Instance.fromFile(path);
|
|
|
|
// print some general statistics on the instance
|
|
output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
|
|
|
|
// run all selected solvers on the instance and print the results
|
|
for(int solverId = 0 ; solverId < solvers.size() ; solverId++) {
|
|
// Select the next solver to run. Given the solver name passed on the command line,
|
|
// we lookup the `Main.solvers` hash map to get the solver object with the given name.
|
|
Solver solver = solvers.get(solverId);
|
|
|
|
// start chronometer and compute deadline for the solver to provide a result.
|
|
long start = System.currentTimeMillis();
|
|
long deadline = System.currentTimeMillis() + solveTimeMs;
|
|
// run the solver on the current instance
|
|
Result result = solver.solve(instance, deadline);
|
|
// measure elapsed time (in milliseconds)
|
|
long runtime = System.currentTimeMillis() - start;
|
|
|
|
// check that the solver returned a valid solution
|
|
if(result.schedule.isEmpty() || !result.schedule.get().isValid()) {
|
|
System.err.println("ERROR: solver returned an invalid schedule");
|
|
System.exit(1); // bug in implementation, bail out
|
|
}
|
|
// we have a valid schedule
|
|
Schedule schedule = result.schedule.get();
|
|
|
|
// compute some statistics on the solution and print them.
|
|
int makespan = schedule.makespan();
|
|
float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
|
|
avg_runtimes[solverId] += (float) runtime / (float) instances.size();
|
|
avg_distances[solverId] += dist / (float) instances.size();
|
|
|
|
output.printf("%7d %8s %5.1f ", runtime, makespan, dist);
|
|
output.flush();
|
|
}
|
|
output.println();
|
|
}
|
|
|
|
|
|
// we have finished all benchmarks, compute the average solve time and distance of each solver.
|
|
output.printf("%-8s %-5s %4s ", "AVG", "-", "-");
|
|
for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
|
|
output.printf("%7.1f %8s %5.1f ", avg_runtimes[solverId], "-", avg_distances[solverId]);
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
// there was uncaught exception, print the stack trace and exit with error.
|
|
e.printStackTrace();
|
|
System.exit(1);
|
|
}
|
|
}
|
|
}
|