Greedy Solver completed
This commit is contained in:
parent
3b67200387
commit
6f78309d10
16 changed files with 1372 additions and 1126 deletions
|
@ -2,8 +2,6 @@ package jobshop;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BestKnownResult {
|
||||
|
||||
|
@ -11,13 +9,6 @@ public class BestKnownResult {
|
|||
return bests.containsKey(instanceName);
|
||||
}
|
||||
|
||||
public static List<String> instancesMatching(String namePrefix) {
|
||||
return Arrays.stream(instances)
|
||||
.filter(i -> i.startsWith(namePrefix))
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static int of(String instanceName) {
|
||||
if(!bests.containsKey(instanceName)) {
|
||||
throw new RuntimeException("Unknown best result for "+instanceName);
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package jobshop;
|
||||
|
||||
import jobshop.encodings.JobNumbers;
|
||||
import jobshop.encodings.ResourceOrder;
|
||||
import jobshop.encodings.Task;
|
||||
import jobshop.solvers.GreedySolver;
|
||||
import jobshop.solvers.GreedySolver.PriorityESTRule;
|
||||
import jobshop.solvers.GreedySolver.PriorityRule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -18,18 +23,85 @@ public class DebuggingMain {
|
|||
// mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2]
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
System.out.println("\nENCODING: " + enc);
|
||||
System.out.println("\nJOB NUMBER ENCODING: " + enc + "\n");
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
// TODO: make it print something meaningful
|
||||
// by implementing the toString() method
|
||||
System.out.println("SCHEDULE: " + sched);
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid() + "\n");
|
||||
System.out.println("MAKESPAN: " + sched.makespan() + "\n");
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
|
||||
ResourceOrder ro = new ResourceOrder(instance);
|
||||
ro.tasksByMachine[0][0] = new Task(0,0);
|
||||
ro.tasksByMachine[0][1] = new Task(1,1);
|
||||
ro.tasksByMachine[1][0] = new Task(1,0);
|
||||
ro.tasksByMachine[1][1] = new Task(0,1);
|
||||
ro.tasksByMachine[2][0] = new Task(0,2);
|
||||
ro.tasksByMachine[2][1] = new Task(1,2);
|
||||
|
||||
System.out.println("RESOURCE ORDER ENCODING:\n" + ro + "\n");
|
||||
|
||||
System.out.println("Default Solver\n");
|
||||
sched = ro.toSchedule();
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
|
||||
JobNumbers jo = JobNumbers.fromSchedule(sched);
|
||||
System.out.println("JOB NUMBER ENCODING (FROM_SCHEDULE): " + jo + "\n");
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
System.out.println("Greedy Solver: STP");
|
||||
PriorityRule SPT = PriorityRule.SPT;
|
||||
Solver solverSPT = new GreedySolver(SPT);
|
||||
Result resultSPT = solverSPT.solve(instance, System.currentTimeMillis() + 10);
|
||||
sched = resultSPT.schedule;
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
System.out.println("Greedy Solver: LRPT\n");
|
||||
PriorityRule LRPT = PriorityRule.LRPT;
|
||||
Solver solverLRPT = new GreedySolver(LRPT);
|
||||
Result resultLRPT = solverLRPT.solve(instance, System.currentTimeMillis() + 10);
|
||||
sched = resultLRPT.schedule;
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
System.out.println("Greedy Solver: EST_SPT\n");
|
||||
PriorityESTRule EST_SPT = PriorityESTRule.EST_SPT;
|
||||
Solver solverEST_SPT = new GreedySolver(EST_SPT);
|
||||
Result resultEST_SPT = solverEST_SPT.solve(instance, System.currentTimeMillis() + 10);
|
||||
sched = resultEST_SPT.schedule;
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
System.out.println("Greedy Solver: EST_SPT\n");
|
||||
PriorityESTRule EST_LRPT = PriorityESTRule.EST_LRPT;
|
||||
Solver solverEST_LRPT = new GreedySolver(EST_SPT);
|
||||
Result resultEST_LRPT = solverEST_LRPT.solve(instance, System.currentTimeMillis() + 10);
|
||||
sched = resultEST_LRPT.schedule;
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package jobshop;
|
||||
|
||||
import jobshop.encodings.Task;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -26,15 +24,9 @@ public class Instance {
|
|||
public int duration(int job, int task) {
|
||||
return durations[job][task];
|
||||
}
|
||||
public int duration(Task t) {
|
||||
return duration(t.job, t.task);
|
||||
}
|
||||
public int machine(int job, int task) {
|
||||
return machines[job][task];
|
||||
}
|
||||
public int machine(Task t) {
|
||||
return this.machine(t.job, t.task);
|
||||
}
|
||||
|
||||
/** among the tasks of the given job, returns the task index that uses the given machine. */
|
||||
public int task_with_machine(int job, int wanted_machine) {
|
||||
|
@ -54,7 +46,6 @@ public class Instance {
|
|||
machines = new int[numJobs][numTasks];
|
||||
}
|
||||
|
||||
/** Parses a instance from a file. */
|
||||
public static Instance fromFile(Path path) throws IOException {
|
||||
Iterator<String> lines = Files.readAllLines(path).stream()
|
||||
.filter(l -> !l.startsWith("#"))
|
||||
|
@ -72,8 +63,11 @@ public class Instance {
|
|||
pb.machines[job][task] = line.nextInt();
|
||||
pb.durations[job][task] = line.nextInt();
|
||||
}
|
||||
line.close();
|
||||
}
|
||||
|
||||
header.close();
|
||||
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,16 @@ 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.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import jobshop.solvers.*;
|
||||
import jobshop.solvers.BasicSolver;
|
||||
import jobshop.solvers.RandomSolver;
|
||||
import jobshop.solvers.GreedySolver.PriorityESTRule;
|
||||
import jobshop.solvers.GreedySolver.PriorityRule;
|
||||
import jobshop.solvers.GreedySolver;
|
||||
import net.sourceforge.argparse4j.ArgumentParsers;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParser;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParserException;
|
||||
|
@ -25,10 +28,18 @@ public class Main {
|
|||
solvers.put("basic", new BasicSolver());
|
||||
solvers.put("random", new RandomSolver());
|
||||
// add new solvers here
|
||||
PriorityRule SPT = PriorityRule.SPT;
|
||||
solvers.put("greedySPT", new GreedySolver(SPT));
|
||||
PriorityRule LRPT = PriorityRule.LRPT;
|
||||
solvers.put("greedyLRPT", new GreedySolver(LRPT));
|
||||
PriorityESTRule EST_SPT = PriorityESTRule.EST_SPT;
|
||||
solvers.put("greedyEST_SPT", new GreedySolver(EST_SPT));
|
||||
PriorityESTRule EST_LRPT = PriorityESTRule.EST_LRPT;
|
||||
solvers.put("greedyEST_LRPT", new GreedySolver(EST_LRPT));
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String[] args) {
|
||||
ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()
|
||||
.defaultHelp(true)
|
||||
.description("Solves jobshop problems.");
|
||||
|
@ -68,23 +79,20 @@ public class Main {
|
|||
System.exit(1);
|
||||
}
|
||||
}
|
||||
List<String> instancePrefixes = ns.getList("instance");
|
||||
List<String> instances = new ArrayList<>();
|
||||
for(String instancePrefix : instancePrefixes) {
|
||||
List<String> matches = BestKnownResult.instancesMatching(instancePrefix);
|
||||
if(matches.isEmpty()) {
|
||||
System.err.println("ERROR: instance prefix \"" + instancePrefix + "\" does not match any instance.");
|
||||
List<String> instances = ns.<String>getList("instance");
|
||||
for(String instanceName : instances) {
|
||||
if(!BestKnownResult.isKnown(instanceName)) {
|
||||
System.err.println("ERROR: instance \"" + instanceName + "\" is not avalaible.");
|
||||
System.err.println(" available instances: " + Arrays.toString(BestKnownResult.instances));
|
||||
System.exit(1);
|
||||
}
|
||||
instances.addAll(matches);
|
||||
}
|
||||
|
||||
float[] runtimes = new float[solversToTest.size()];
|
||||
float[] distances = new float[solversToTest.size()];
|
||||
|
||||
try {
|
||||
output.print( " ");
|
||||
output.print( " ");;
|
||||
for(String s : solversToTest)
|
||||
output.printf("%-30s", s);
|
||||
output.println();
|
||||
|
@ -95,46 +103,46 @@ public class Main {
|
|||
output.println();
|
||||
|
||||
|
||||
for(String instanceName : instances) {
|
||||
int bestKnown = BestKnownResult.of(instanceName);
|
||||
for(String instanceName : instances) {
|
||||
int bestKnown = BestKnownResult.of(instanceName);
|
||||
|
||||
|
||||
Path path = Paths.get("instances/", instanceName);
|
||||
Instance instance = Instance.fromFile(path);
|
||||
Path path = Paths.get("instances/", instanceName);
|
||||
Instance instance = Instance.fromFile(path);
|
||||
|
||||
output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
|
||||
output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
|
||||
|
||||
for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
|
||||
String solverName = solversToTest.get(solverId);
|
||||
Solver solver = solvers.get(solverName);
|
||||
long start = System.currentTimeMillis();
|
||||
long deadline = System.currentTimeMillis() + solveTimeMs;
|
||||
Result result = solver.solve(instance, deadline);
|
||||
long runtime = System.currentTimeMillis() - start;
|
||||
|
||||
if(!result.schedule.isValid()) {
|
||||
System.err.println("ERROR: solver returned an invalid schedule");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
assert result.schedule.isValid();
|
||||
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();
|
||||
|
||||
output.printf("%7d %8s %5.1f ", runtime, makespan, dist);
|
||||
output.flush();
|
||||
}
|
||||
output.println();
|
||||
|
||||
}
|
||||
|
||||
|
||||
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]);
|
||||
String solverName = solversToTest.get(solverId);
|
||||
Solver solver = solvers.get(solverName);
|
||||
long start = System.currentTimeMillis();
|
||||
long deadline = System.currentTimeMillis() + solveTimeMs;
|
||||
Result result = solver.solve(instance, deadline);
|
||||
long runtime = System.currentTimeMillis() - start;
|
||||
|
||||
if(!result.schedule.isValid()) {
|
||||
System.err.println("ERROR: solver returned an invalid schedule");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
assert result.schedule.isValid();
|
||||
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();
|
||||
|
||||
output.printf("%7d %8s %5.1f ", runtime, makespan, dist);
|
||||
output.flush();
|
||||
}
|
||||
output.println();
|
||||
|
||||
}
|
||||
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package jobshop;
|
|||
|
||||
import java.util.Optional;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Result {
|
||||
|
||||
public Result(Instance instance, Schedule schedule, ExitCause cause) {
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package jobshop;
|
||||
|
||||
|
||||
import jobshop.encodings.Task;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import jobshop.encodings.Task;
|
||||
|
||||
public class Schedule {
|
||||
public final Instance pb;
|
||||
// start times of each job and task
|
||||
|
@ -132,4 +136,22 @@ public class Schedule {
|
|||
assert isCriticalPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
public Schedule copy() {
|
||||
return new Schedule(this.pb, this.times);
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
/* Implémentation de la méthode toString() de la classe Schedule*/
|
||||
/****************************************************************/
|
||||
public String toString() {
|
||||
String res = "";
|
||||
for (int i = 0; i < this.times.length; i++) {
|
||||
res += "Job " + Integer.toString(i + 1) + " starting times : \n";
|
||||
for (int j = 0; j < this.times[i].length; j++) {
|
||||
res += "\tTask " + Integer.toString(j + 1) + " starts at time : " + Integer.toString(this.times[i][j]) + "\n";
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ public class JobNumbers extends Encoding {
|
|||
/** A numJobs * numTasks array containing the representation by job numbers. */
|
||||
public final int[] jobs;
|
||||
|
||||
/** In case the encoding is only partially filled, indicates the index of the first
|
||||
/** In case the encoding is only partially filled, indicates the index of first
|
||||
* element of `jobs` that has not been set yet. */
|
||||
public int nextToSet = 0;
|
||||
|
||||
|
@ -77,6 +77,38 @@ public class JobNumbers extends Encoding {
|
|||
return new Schedule(instance, startTimes);
|
||||
}
|
||||
|
||||
public static JobNumbers fromSchedule(Schedule sched) {
|
||||
JobNumbers jo = new JobNumbers(sched.pb);
|
||||
|
||||
int current_time = 0;
|
||||
Task current_task = new Task(-1,-1);
|
||||
Task [] done_tasks = new Task[sched.pb.numJobs*sched.pb.numTasks];
|
||||
Arrays.fill(done_tasks, current_task);
|
||||
|
||||
int min;
|
||||
|
||||
for (int i = 0; i < sched.pb.numJobs*sched.pb.numTasks; i++) {
|
||||
// Il faut faire le code ci-dessous autant de fois que l'on a de taches
|
||||
// On trouve le minimum parmis les restants
|
||||
min = Integer.MAX_VALUE;
|
||||
for (int job = 0; job < sched.pb.numJobs; job++) {
|
||||
for (int task = 0; task < sched.pb.numTasks; task++) {
|
||||
int task_start_time = sched.startTime(job, task);
|
||||
Task this_task = new Task(job, task);
|
||||
if (task_start_time < min && task_start_time >= current_time && !(Arrays.asList(done_tasks).contains(this_task))) {
|
||||
min = task_start_time;
|
||||
current_task = this_task;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Une fois on a trouvé la suivante tache a realiser on introduit le numero du job dans jobs
|
||||
jo.jobs[jo.nextToSet++] = current_task.job;
|
||||
done_tasks[i] = current_task;
|
||||
}
|
||||
|
||||
return jo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Arrays.toString(Arrays.copyOfRange(jobs,0, nextToSet));
|
||||
|
|
|
@ -1,37 +1,35 @@
|
|||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Encoding;
|
||||
import jobshop.Instance;
|
||||
import jobshop.Schedule;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import jobshop.Encoding;
|
||||
import jobshop.Instance;
|
||||
import jobshop.Schedule;
|
||||
|
||||
|
||||
public class ResourceOrder extends Encoding {
|
||||
|
||||
// for each machine m, taskByMachine[m] is an array of tasks to be
|
||||
// executed on this machine in the same order
|
||||
public final Task[][] tasksByMachine;
|
||||
public final Task[][] tasksByMachine;
|
||||
|
||||
// for each machine, indicate on many tasks have been initialized
|
||||
public final int[] nextFreeSlot;
|
||||
public final int[] nextFreeSlot;
|
||||
|
||||
/** Creates a new empty resource order. */
|
||||
public ResourceOrder(Instance instance)
|
||||
{
|
||||
super(instance);
|
||||
public ResourceOrder(Instance instance) {
|
||||
super(instance);
|
||||
|
||||
// matrix of null elements (null is the default value of objects)
|
||||
tasksByMachine = new Task[instance.numMachines][instance.numJobs];
|
||||
this.tasksByMachine = new Task[instance.numMachines][instance.numJobs];
|
||||
for (int i = 0; i < instance.numMachines; i++) {
|
||||
for (int j = 0; j < instance.numJobs; j++) {
|
||||
this.tasksByMachine[i][j] = new Task(-1,-1);
|
||||
}
|
||||
}
|
||||
|
||||
// no task scheduled on any machine (0 is the default value)
|
||||
// no task scheduled on any machine (0 is the default value)
|
||||
nextFreeSlot = new int[instance.numMachines];
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a resource order from a schedule. */
|
||||
public ResourceOrder(Schedule schedule)
|
||||
{
|
||||
public ResourceOrder(Schedule schedule) {
|
||||
super(schedule.pb);
|
||||
Instance pb = schedule.pb;
|
||||
|
||||
|
@ -51,9 +49,10 @@ public class ResourceOrder extends Encoding {
|
|||
// indicate that all tasks have been initialized for machine m
|
||||
nextFreeSlot[m] = instance.numJobs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
// indicate for each task that have been scheduled, its start time
|
||||
int [][] startTimes = new int [instance.numJobs][instance.numTasks];
|
||||
|
@ -89,7 +88,7 @@ public class ResourceOrder extends Encoding {
|
|||
|
||||
// compute the earliest start time (est) of the task
|
||||
int est = t.task == 0 ? 0 : startTimes[t.job][t.task-1] + instance.duration(t.job, t.task-1);
|
||||
est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]);
|
||||
est = Math.max(est, releaseTimeOfMachine[instance.machine(t.job, t.task)]);
|
||||
startTimes[t.job][t.task] = est;
|
||||
|
||||
// mark the task as scheduled
|
||||
|
@ -106,26 +105,16 @@ public class ResourceOrder extends Encoding {
|
|||
return new Schedule(instance, startTimes);
|
||||
}
|
||||
|
||||
/** Creates an exact copy of this resource order. */
|
||||
public ResourceOrder copy() {
|
||||
return new ResourceOrder(this.toSchedule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder s = new StringBuilder();
|
||||
for(int m=0; m < instance.numMachines; m++)
|
||||
{
|
||||
s.append("Machine ").append(m).append(" : ");
|
||||
for(int j=0; j<instance.numJobs; j++)
|
||||
{
|
||||
s.append(tasksByMachine[m][j]).append(" ; ");
|
||||
}
|
||||
s.append("\n");
|
||||
}
|
||||
|
||||
return s.toString();
|
||||
@Override
|
||||
public String toString() {
|
||||
String res = "";
|
||||
for (int i = 0; i < this.tasksByMachine.length; i++) {
|
||||
res += "Machine number : " + Integer.toString(i+1) + "\n";
|
||||
for (int j = 0; j < this.tasksByMachine[i].length; j++) {
|
||||
res += "\tUse number " + Integer.toString(j+1) + " : " + this.tasksByMachine[i][j].add_one() + "\n";
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
|
@ -36,6 +36,10 @@ public final class Task {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + job +", " + task + '}';
|
||||
return "Job " + this.job + " Task " + this.task;
|
||||
}
|
||||
|
||||
public Task add_one() {
|
||||
return new Task(this.job + 1, this.task + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
package jobshop.solvers;
|
||||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Solver;
|
||||
import jobshop.encodings.ResourceOrder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DescentSolver implements Solver {
|
||||
|
||||
/** A block represents a subsequence of the critical path such that all tasks in it execute on the same machine.
|
||||
* This class identifies a block in a ResourceOrder representation.
|
||||
*
|
||||
* Consider the solution in ResourceOrder representation
|
||||
* machine 0 : (0,1) (1,2) (2,2)
|
||||
* machine 1 : (0,2) (2,1) (1,1)
|
||||
* machine 2 : ...
|
||||
*
|
||||
* The block with : machine = 1, firstTask= 0 and lastTask = 1
|
||||
* Represent the task sequence : [(0,2) (2,1)]
|
||||
*
|
||||
* */
|
||||
static class Block {
|
||||
/** machine on which the block is identified */
|
||||
final int machine;
|
||||
/** index of the first task of the block */
|
||||
final int firstTask;
|
||||
/** index of the last task of the block */
|
||||
final int lastTask;
|
||||
|
||||
Block(int machine, int firstTask, int lastTask) {
|
||||
this.machine = machine;
|
||||
this.firstTask = firstTask;
|
||||
this.lastTask = lastTask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a swap of two tasks on the same machine in a ResourceOrder encoding.
|
||||
*
|
||||
* Consider the solution in ResourceOrder representation
|
||||
* machine 0 : (0,1) (1,2) (2,2)
|
||||
* machine 1 : (0,2) (2,1) (1,1)
|
||||
* machine 2 : ...
|
||||
*
|
||||
* The swam with : machine = 1, t1= 0 and t2 = 1
|
||||
* Represent inversion of the two tasks : (0,2) and (2,1)
|
||||
* Applying this swap on the above resource order should result in the following one :
|
||||
* machine 0 : (0,1) (1,2) (2,2)
|
||||
* machine 1 : (2,1) (0,2) (1,1)
|
||||
* machine 2 : ...
|
||||
*/
|
||||
static class Swap {
|
||||
// machine on which to perform the swap
|
||||
final int machine;
|
||||
// index of one task to be swapped
|
||||
final int t1;
|
||||
// index of the other task to be swapped
|
||||
final int t2;
|
||||
|
||||
Swap(int machine, int t1, int t2) {
|
||||
this.machine = machine;
|
||||
this.t1 = t1;
|
||||
this.t2 = t2;
|
||||
}
|
||||
|
||||
/** Apply this swap on the given resource order, transforming it into a new solution. */
|
||||
public void applyOn(ResourceOrder order) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** Returns a list of all blocks of the critical path. */
|
||||
List<Block> blocksOfCriticalPath(ResourceOrder order) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */
|
||||
List<Swap> neighbors(Block block) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
194
src/main/java/jobshop/solvers/GreedySolver.java
Normal file
194
src/main/java/jobshop/solvers/GreedySolver.java
Normal file
|
@ -0,0 +1,194 @@
|
|||
package jobshop.solvers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import jobshop.*;
|
||||
import jobshop.encodings.ResourceOrder;
|
||||
import jobshop.encodings.Task;
|
||||
import jobshop.solvers.GreedySolver.PriorityESTRule;
|
||||
|
||||
public class GreedySolver implements Solver {
|
||||
|
||||
/*********************************************************************************************/
|
||||
/******************* Priority Rules and EST Priority Rules enumerations **********************/
|
||||
/*********************************************************************************************/
|
||||
public enum PriorityRule{
|
||||
SPT {
|
||||
@Override
|
||||
public Task getTaskByRule(ArrayList<Task> achievableTasks, Instance instance) {
|
||||
// Return the task with the MINIMAL duration
|
||||
Task TaskSPT = null;
|
||||
int minDuration = Integer.MAX_VALUE;
|
||||
|
||||
for(int i = 0; i < achievableTasks.size(); i++) {
|
||||
Task current = achievableTasks.get(i);
|
||||
int currentDuration = instance.duration(current.job, current.task);
|
||||
|
||||
if(currentDuration < minDuration) {
|
||||
minDuration = currentDuration;
|
||||
TaskSPT = current;
|
||||
}
|
||||
}
|
||||
return TaskSPT;
|
||||
}
|
||||
},
|
||||
LRPT {
|
||||
@Override
|
||||
public Task getTaskByRule(ArrayList<Task> achievableTasks, Instance instance) {
|
||||
Task TaskLRPT = null;
|
||||
Task current;
|
||||
int maxDuration = -1;
|
||||
|
||||
for(int i = 0; i < achievableTasks.size(); i++) {
|
||||
current = achievableTasks.get(i);
|
||||
int remainingTime = 0;
|
||||
for(int j = current.task; j < instance.numTasks; j++) {
|
||||
remainingTime += instance.duration(current.job, j);
|
||||
}
|
||||
if(remainingTime > maxDuration) {
|
||||
maxDuration = remainingTime;
|
||||
TaskLRPT = current;
|
||||
}
|
||||
}
|
||||
return TaskLRPT;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns the Task to work with depending on the rule chosen
|
||||
* @param achievableTasks, instance
|
||||
* @return currentTask
|
||||
*/
|
||||
public abstract Task getTaskByRule(ArrayList<Task> achievableTasks, Instance instance);
|
||||
}
|
||||
|
||||
public enum PriorityESTRule{
|
||||
EST_SPT {
|
||||
@Override
|
||||
public Task getTaskByESTRule(ArrayList<Task> achievableTasks, Instance instance, int[] nextStartDateJobs, int[] nextStartDateMachines) {
|
||||
|
||||
ArrayList<Task> priorityTasks = this.getESTPriorityTasks(achievableTasks, instance, nextStartDateJobs, nextStartDateMachines);
|
||||
// STP function: Return the task with the MINIMAL duration
|
||||
PriorityRule SPT = PriorityRule.SPT;
|
||||
Task TaskSPT = SPT.getTaskByRule(priorityTasks, instance);
|
||||
|
||||
return TaskSPT;
|
||||
}
|
||||
},
|
||||
EST_LRPT {
|
||||
@Override
|
||||
public Task getTaskByESTRule(ArrayList<Task> achievableTasks, Instance instance, int[] nextStartDateJobs, int[] nextStartDateMachines) {
|
||||
ArrayList<Task> priorityTasks = this.getESTPriorityTasks(achievableTasks, instance, nextStartDateJobs, nextStartDateMachines);
|
||||
// STP function: Return the task with the MINIMAL duration
|
||||
PriorityRule LRPT = PriorityRule.LRPT;
|
||||
Task TaskLRPT = LRPT.getTaskByRule(priorityTasks, instance);
|
||||
|
||||
return TaskLRPT;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns the Task to work with depending on the rule chosen
|
||||
* @param achievableTasks, instance
|
||||
* @return currentTask
|
||||
*/
|
||||
public abstract Task getTaskByESTRule(ArrayList<Task> achievableTasks, Instance instance, int[] nextStartDateJobs, int[] nextStartDateMachines);
|
||||
/**
|
||||
* Returns the array of tasks with the shortest start date
|
||||
* @param achievableTasks, instance
|
||||
* @return priorityTask
|
||||
*/
|
||||
protected ArrayList<Task> getESTPriorityTasks(ArrayList<Task> achievableTasks, Instance instance, int[] nextStartDateJobs, int[] nextStartDateMachines){
|
||||
// Search for the date or dates which start sooner
|
||||
ArrayList<Task> priorityTasks = new ArrayList<>();
|
||||
int minStartDate = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
for(int i = 0; i < achievableTasks.size(); i++) {
|
||||
Task currentTask = achievableTasks.get(i);
|
||||
int currentMachine = instance.machine(currentTask.job, currentTask.task);
|
||||
int currentStartDate = Integer.max(nextStartDateJobs[currentTask.job], nextStartDateMachines[currentMachine]);
|
||||
|
||||
if(currentStartDate < minStartDate) {
|
||||
minStartDate = currentStartDate;
|
||||
// When we find a smaller start date we "restart" the array
|
||||
priorityTasks.clear();
|
||||
priorityTasks.add(currentTask);
|
||||
} else if (currentStartDate == minStartDate) {
|
||||
priorityTasks.add(currentTask);
|
||||
}
|
||||
}
|
||||
return priorityTasks;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
/********************** Greedy Solver: Constructors + Solve function *************************/
|
||||
/*********************************************************************************************/
|
||||
|
||||
public PriorityRule priorityRule;
|
||||
public PriorityESTRule priorityESTRule;
|
||||
|
||||
// 2 constructors: the default and one with the EST restriction
|
||||
public GreedySolver(PriorityRule rule) {
|
||||
this.priorityRule = rule;
|
||||
this.priorityESTRule = null;
|
||||
}
|
||||
|
||||
public GreedySolver(PriorityESTRule ruleEST) {
|
||||
this.priorityESTRule = ruleEST;
|
||||
this.priorityRule = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
|
||||
int currentMachine, currentDuration;
|
||||
// We declare 2 arrays containing the updated moment the next task will start in a job and a machine respectively
|
||||
int[] nextStartDateJobs = new int[instance.numJobs];
|
||||
int[] nextStartDateMachines = new int[instance.numMachines];
|
||||
|
||||
// We create a new ResourceOrder for putting all tasks in the schedule
|
||||
ResourceOrder solution = new ResourceOrder(instance);
|
||||
// Array list with all the achievable current tasks
|
||||
ArrayList<Task> achievableTasks = new ArrayList<>();
|
||||
|
||||
// Initialize the array list with all the first task achievable
|
||||
for(int i = 0 ; i < instance.numJobs ; i++) {
|
||||
Task currentTask = new Task(i, 0);
|
||||
achievableTasks.add(currentTask);
|
||||
}
|
||||
|
||||
while(!achievableTasks.isEmpty()) {
|
||||
// We take the task we should do now in function of the priority rule used
|
||||
Task currentTask = null;
|
||||
if(priorityESTRule == null) {
|
||||
currentTask = priorityRule.getTaskByRule(achievableTasks, instance);
|
||||
} else if(priorityRule == null) {
|
||||
currentTask = priorityESTRule.getTaskByESTRule(achievableTasks, instance, nextStartDateJobs, nextStartDateMachines);
|
||||
} else {
|
||||
System.out.printf("Error priorityRule and priorityRuleEST are null. You must give a value to one of them.");
|
||||
}
|
||||
|
||||
// Updating starting dates
|
||||
currentMachine = instance.machine(currentTask.job, currentTask.task);
|
||||
currentDuration = instance.duration(currentTask.job, currentTask.task);
|
||||
nextStartDateJobs[currentTask.job] += currentDuration;
|
||||
nextStartDateMachines[currentMachine] += currentDuration;
|
||||
|
||||
// We remove the current task from the achievable tasks list
|
||||
achievableTasks.remove(currentTask);
|
||||
|
||||
// If it's not the last task of the job, we update the array list with the new task
|
||||
if (currentTask.task < (instance.numTasks - 1)) {
|
||||
achievableTasks.add(new Task(currentTask.job, currentTask.task + 1));
|
||||
}
|
||||
|
||||
// We add the current task to the solution
|
||||
int nextFreeSlot = solution.nextFreeSlot[currentMachine]++;
|
||||
solution.tasksByMachine[currentMachine][nextFreeSlot] = currentTask;
|
||||
}
|
||||
|
||||
return new Result(instance, solution.toSchedule(), Result.ExitCause.Blocked);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import jobshop.encodings.JobNumbers;
|
|||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class RandomSolver implements Solver {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,6 +5,9 @@ import jobshop.Result;
|
|||
import jobshop.Schedule;
|
||||
import jobshop.Solver;
|
||||
import jobshop.solvers.BasicSolver;
|
||||
import jobshop.solvers.GreedySolver;
|
||||
import jobshop.solvers.GreedySolver.PriorityRule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -72,4 +75,29 @@ public class EncodingTests {
|
|||
assert result.schedule.makespan() == sched.makespan(); // should have the same makespan
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreedySolver() throws IOException {
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// build a solution that should be equal to the result of BasicSolver
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 12;
|
||||
|
||||
PriorityRule priorityRule = PriorityRule.SPT;
|
||||
Solver solver = new GreedySolver(priorityRule);
|
||||
Result result = solver.solve(instance, System.currentTimeMillis() + 10);
|
||||
|
||||
assert result.schedule.isValid();
|
||||
assert result.schedule.makespan() == sched.makespan(); // should have the same makespan
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue