Improve documentation
This commit is contained in:
parent
9802bd39fd
commit
dce9b786e9
7 changed files with 140 additions and 54 deletions
61
README.md
61
README.md
|
@ -2,21 +2,48 @@
|
|||
|
||||
This repository contains the starter code for the assignment.
|
||||
|
||||
## Working in IntelliJ
|
||||
|
||||
## Compile
|
||||
For working on this project, we recommend using the IntelliJ-IDEA development environment. It is available in INSA's
|
||||
classrooms as well as on `montp.insa-toulouse.fr`.
|
||||
|
||||
Compilation instructions are given for Linux. On windows you can use the `gradlew.bat` script.
|
||||
To import the project in IntelliJ (once IntelliJ is running):
|
||||
|
||||
- Open a new project : `Open project` or `File > Open`
|
||||
- Select the `gradle.build` file in the cloned repository.
|
||||
- Select `Open as project`.
|
||||
|
||||
To run the program in IntelliJ, you can
|
||||
|
||||
- Right click on the `src/main/java/Jobshop/Main` class in the project view.
|
||||
- Select `Run Main.main()`. It should complain that some arguments are missing.
|
||||
- Give it the expected command line arguments : `Run > Edit Configuration`, then fill in the `Program arguments` text box.
|
||||
|
||||
|
||||
## Working on the command line (Gradle)
|
||||
|
||||
Compilation instructions are given for Linux. On Windows you can use the `gradlew.bat` script (but you are on your own).
|
||||
|
||||
```
|
||||
❯ ./gradlew build # Compiles the project
|
||||
❯ ./gradlew jar # Creates a fat-jar in build/libs/JSP.jar
|
||||
❯ ./gradlew build # Compiles the project
|
||||
```
|
||||
|
||||
The compiled jar is now `build/libs/JSP.jar` can be executed like so :
|
||||
The project can be executed directly with `gradle` by specifying the arguments like so :
|
||||
|
||||
```
|
||||
❯ ./gradlew run --args="--solver basic random --instance aaa1 ft"
|
||||
```
|
||||
|
||||
You can also build an executable jar file, and run it with the java command.
|
||||
This is especially useful if you want to run it on another machine.
|
||||
|
||||
```
|
||||
# Create a jar file with all dependencies in build/libs/JSP.jar
|
||||
❯ ./gradlew jar
|
||||
# Run the jar file. Only requires a Java Runtime Environment (JRE)
|
||||
❯ java -jar build/libs/JSP.jar --solver basic --instance ft06
|
||||
```
|
||||
|
||||
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 :
|
||||
```
|
||||
basic
|
||||
|
@ -48,36 +75,26 @@ AVG - - 0.3 - 31.5 999.0 - 20.4
|
|||
Here the last line give the average `runtime` and `ecart` for each solver.
|
||||
|
||||
```
|
||||
usage: jsp-solver [-h] [-t TIMEOUT] --solver SOLVER [SOLVER ...]
|
||||
sage: jsp-solver [-h] [-t TIMEOUT] --solver SOLVER [SOLVER ...]
|
||||
--instance INSTANCE [INSTANCE ...]
|
||||
|
||||
Solves jobshop problems.
|
||||
|
||||
named arguments:
|
||||
-h, --help show this help message and exit
|
||||
-t TIMEOUT, --timeout TIMEOUT
|
||||
Solver timeout in seconds for each instance.
|
||||
(default: 1)
|
||||
--solver SOLVER [SOLVER ...]
|
||||
Solver(s) to use (space separated if more than
|
||||
one)
|
||||
-t TIMEOUT, --timeout TIMEOUT
|
||||
Solver timeout in seconds for each instance
|
||||
(default: 1)
|
||||
--instance INSTANCE [INSTANCE ...]
|
||||
Instance(s) to solve (space separated if more
|
||||
than one)
|
||||
|
||||
|
||||
than one). All instances starting with the given
|
||||
String will be selected. (e.g. "ft" will select
|
||||
the instances ft06, ft10 and ft20.
|
||||
```
|
||||
|
||||
### Running directly from Gradle
|
||||
|
||||
The project can be executed directly with `gradle` by specifying the arguments like so :
|
||||
|
||||
```
|
||||
❯ ./gradlew run --args="--solver basic random --instance aaa1 ft06 ft10 ft20"
|
||||
```
|
||||
|
||||
This notably ensures that sources have been recompiled whenever necessary.
|
||||
|
||||
|
||||
## IDE Support
|
||||
|
||||
|
|
|
@ -5,12 +5,28 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BestKnownResult {
|
||||
/**
|
||||
* This class contains the best known results for common jobshop instances.
|
||||
* Note that the best known result might not have been proven to be the optimal solution
|
||||
* for the instance.
|
||||
*/
|
||||
public class BestKnownResults {
|
||||
|
||||
/**
|
||||
* Checks whether we have data available for the provided instance.
|
||||
* @param instanceName Name of the instance.
|
||||
* @return True if the isntance is known, false otherwise.
|
||||
*/
|
||||
public static boolean isKnown(String instanceName) {
|
||||
return bests.containsKey(instanceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all instances that start with the given prefix.
|
||||
* For example, the prefix "ft" should match the instances "ft06", "ft10" and "ft20"
|
||||
* @param namePrefix Prefix that should be looked-up.
|
||||
* @return All instances that start with the given prefix, in alphabetical order.
|
||||
*/
|
||||
public static List<String> instancesMatching(String namePrefix) {
|
||||
return Arrays.stream(instances)
|
||||
.filter(i -> i.startsWith(namePrefix))
|
||||
|
@ -18,6 +34,11 @@ public class BestKnownResult {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best known result for the given instance name.
|
||||
* @param instanceName Instance of which we want to retrieve the best result.
|
||||
* @return Best makespan that has ever been found for this instance.
|
||||
*/
|
||||
public static int of(String instanceName) {
|
||||
if(!bests.containsKey(instanceName)) {
|
||||
throw new RuntimeException("Unknown best result for "+instanceName);
|
||||
|
@ -25,8 +46,12 @@ public class BestKnownResult {
|
|||
return bests.get(instanceName);
|
||||
}
|
||||
|
||||
static private HashMap<String, Integer> bests;
|
||||
// all best results.
|
||||
static final private HashMap<String, Integer> bests;
|
||||
// a sorted array of instance names
|
||||
static String[] instances;
|
||||
|
||||
// initialize the internal data structures.
|
||||
static {
|
||||
bests = new HashMap<>();
|
||||
bests.put("aaa1", 11);
|
|
@ -27,8 +27,7 @@ public class DebuggingMain {
|
|||
System.out.println("\nENCODING: " + enc);
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
// TODO: make it print something meaningful
|
||||
// by implementing the toString() method
|
||||
// TODO: make it print something meaningful by implementing the Schedule.toString() method
|
||||
System.out.println("SCHEDULE: " + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
|
|
@ -20,18 +20,28 @@ public class Instance {
|
|||
/** Number of machines, assumed to be same as number of tasks. */
|
||||
public final int numMachines;
|
||||
|
||||
/** Matrix containing the duration of all tasks. */
|
||||
final int[][] durations;
|
||||
|
||||
/** Matrix containing the machine on which each task must be scheduled. */
|
||||
final int[][] machines;
|
||||
|
||||
/** Duration of the given task. */
|
||||
public int duration(int job, int task) {
|
||||
return durations[job][task];
|
||||
}
|
||||
|
||||
/** Duration of the given task. */
|
||||
public int duration(Task t) {
|
||||
return duration(t.job, t.task);
|
||||
}
|
||||
|
||||
/** Machine on which the given task must be scheduled. */
|
||||
public int machine(int job, int task) {
|
||||
return machines[job][task];
|
||||
}
|
||||
|
||||
/** Machine on which the given task must be scheduled. */
|
||||
public int machine(Task t) {
|
||||
return this.machine(t.job, t.task);
|
||||
}
|
||||
|
@ -45,6 +55,11 @@ public class Instance {
|
|||
throw new RuntimeException("No task targeting machine "+wanted_machine+" on job "+job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance, with uninitialized durations and machines.
|
||||
* This should no be called directly. Instead, Instance objects should be created with the
|
||||
* <code>Instance.fromFile()</code> static method.
|
||||
*/
|
||||
Instance(int numJobs, int numTasks) {
|
||||
this.numJobs = numJobs;
|
||||
this.numTasks = numTasks;
|
||||
|
|
|
@ -8,82 +8,101 @@ import java.util.Arrays;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
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 testing solver on instances.
|
||||
* It provides
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
/** All solvers available in this program */
|
||||
private static HashMap<String, Solver> solvers;
|
||||
private static final HashMap<String, Solver> solvers;
|
||||
static {
|
||||
solvers = new HashMap<>();
|
||||
solvers.put("basic", new BasicSolver());
|
||||
solvers.put("random", new RandomSolver());
|
||||
// add new solvers here
|
||||
// TODO: add new solvers here
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
.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)");
|
||||
.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(1);
|
||||
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");
|
||||
for(String solverName : solversToTest) {
|
||||
if(!solvers.containsKey(solverName)) {
|
||||
System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible.");
|
||||
System.err.println(" Available solvers: " + solvers.keySet().toString());
|
||||
System.err.println(" You can provide your own solvers by adding them to the `Main.solvers` HashMap.");
|
||||
System.exit(1);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
List<String> instancePrefixes = ns.getList("instance");
|
||||
|
||||
// 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 = BestKnownResult.instancesMatching(instancePrefix);
|
||||
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(BestKnownResult.instances));
|
||||
System.err.println(" available instances: " + Arrays.toString(BestKnownResults.instances));
|
||||
System.exit(1);
|
||||
}
|
||||
instances.addAll(matches);
|
||||
}
|
||||
|
||||
float[] runtimes = new float[solversToTest.size()];
|
||||
float[] distances = new float[solversToTest.size()];
|
||||
// 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);
|
||||
|
@ -94,51 +113,62 @@ public class Main {
|
|||
}
|
||||
output.println();
|
||||
|
||||
|
||||
// for all instances, load it from f
|
||||
for(String instanceName : instances) {
|
||||
int bestKnown = BestKnownResult.of(instanceName);
|
||||
|
||||
// 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 < solversToTest.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.
|
||||
String solverName = solversToTest.get(solverId);
|
||||
Solver solver = solvers.get(solverName);
|
||||
|
||||
// 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.isValid()) {
|
||||
System.err.println("ERROR: solver returned an invalid schedule");
|
||||
System.exit(1);
|
||||
System.exit(1); // bug in implementation, bail out
|
||||
}
|
||||
|
||||
assert result.schedule.isValid();
|
||||
// compute some statistics on the solution and print them.
|
||||
int makespan = result.schedule.makespan();
|
||||
float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
|
||||
runtimes[solverId] += (float) runtime / (float) instances.size();
|
||||
distances[solverId] += dist / (float) instances.size();
|
||||
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 ", runtimes[solverId], "-", distances[solverId]);
|
||||
output.printf("%7.1f %8s %5.1f ", avg_runtimes[solverId], "-", avg_distances[solverId]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
// there was uncought exception, print the stack trace and exit with error.
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public class ResourceOrder extends Encoding {
|
|||
for(int m = 0 ; m<schedule.pb.numMachines ; m++) {
|
||||
final int machine = m;
|
||||
|
||||
// for thi machine, find all tasks that are executed on it and sort them by their start time
|
||||
// for this machine, find all tasks that are executed on it and sort them by their start time
|
||||
tasksByMachine[m] =
|
||||
IntStream.range(0, pb.numJobs) // all job numbers
|
||||
.mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) // all tasks on this machine (one per job)
|
||||
|
|
|
@ -2,10 +2,10 @@ package jobshop.encodings;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
/** Represents a task (job,task) of an jobshop problem.
|
||||
/** Represents a task (job,task) of a jobshop problem.
|
||||
*
|
||||
* Example : (2, 3) repesents the fourth task of the third job. (remeber that we tart counting at 0)
|
||||
* */
|
||||
* Example : (2, 3) represents the fourth task of the third job. (remember that we start counting at 0)
|
||||
**/
|
||||
public final class Task {
|
||||
|
||||
/** Identifier of the job */
|
||||
|
|
Loading…
Reference in a new issue