Greedy Solver completed

This commit is contained in:
AlexJavor 2020-04-27 16:12:01 +02:00
parent 3b67200387
commit 6f78309d10
16 changed files with 1372 additions and 1126 deletions

View file

@ -2,8 +2,6 @@ package jobshop;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
public class BestKnownResult { public class BestKnownResult {
@ -11,13 +9,6 @@ public class BestKnownResult {
return bests.containsKey(instanceName); 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) { public static int of(String instanceName) {
if(!bests.containsKey(instanceName)) { if(!bests.containsKey(instanceName)) {
throw new RuntimeException("Unknown best result for "+instanceName); throw new RuntimeException("Unknown best result for "+instanceName);

View file

@ -1,6 +1,11 @@
package jobshop; package jobshop;
import jobshop.encodings.JobNumbers; 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.io.IOException;
import java.nio.file.Paths; 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] // mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2]
JobNumbers enc = new JobNumbers(instance); JobNumbers enc = new JobNumbers(instance);
enc.jobs[enc.nextToSet++] = 0; enc.jobs[enc.nextToSet++] = 0;
enc.jobs[enc.nextToSet++] = 1;
enc.jobs[enc.nextToSet++] = 1;
enc.jobs[enc.nextToSet++] = 0; enc.jobs[enc.nextToSet++] = 0;
enc.jobs[enc.nextToSet++] = 1;
enc.jobs[enc.nextToSet++] = 1;
enc.jobs[enc.nextToSet++] = 0; enc.jobs[enc.nextToSet++] = 0;
enc.jobs[enc.nextToSet++] = 1; enc.jobs[enc.nextToSet++] = 1;
System.out.println("\nENCODING: " + enc); System.out.println("\nJOB NUMBER ENCODING: " + enc + "\n");
Schedule sched = enc.toSchedule(); Schedule sched = enc.toSchedule();
// TODO: make it print something meaningful
// by implementing the toString() method System.out.println("SCHEDULE:\n" + sched);
System.out.println("SCHEDULE: " + 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("VALID: " + sched.isValid());
System.out.println("MAKESPAN: " + sched.makespan()); System.out.println("MAKESPAN: " + sched.makespan());

View file

@ -1,7 +1,5 @@
package jobshop; package jobshop;
import jobshop.encodings.Task;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -26,15 +24,9 @@ public class Instance {
public int duration(int job, int task) { public int duration(int job, int task) {
return durations[job][task]; return durations[job][task];
} }
public int duration(Task t) {
return duration(t.job, t.task);
}
public int machine(int job, int task) { public int machine(int job, int task) {
return machines[job][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. */ /** 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) { public int task_with_machine(int job, int wanted_machine) {
@ -54,7 +46,6 @@ public class Instance {
machines = new int[numJobs][numTasks]; machines = new int[numJobs][numTasks];
} }
/** Parses a instance from a file. */
public static Instance fromFile(Path path) throws IOException { public static Instance fromFile(Path path) throws IOException {
Iterator<String> lines = Files.readAllLines(path).stream() Iterator<String> lines = Files.readAllLines(path).stream()
.filter(l -> !l.startsWith("#")) .filter(l -> !l.startsWith("#"))
@ -72,8 +63,11 @@ public class Instance {
pb.machines[job][task] = line.nextInt(); pb.machines[job][task] = line.nextInt();
pb.durations[job][task] = line.nextInt(); pb.durations[job][task] = line.nextInt();
} }
line.close();
} }
header.close();
return pb; return pb;
} }
} }

View file

@ -3,13 +3,16 @@ package jobshop;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; 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.ArgumentParsers;
import net.sourceforge.argparse4j.inf.ArgumentParser; import net.sourceforge.argparse4j.inf.ArgumentParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.ArgumentParserException;
@ -25,9 +28,17 @@ public class Main {
solvers.put("basic", new BasicSolver()); solvers.put("basic", new BasicSolver());
solvers.put("random", new RandomSolver()); solvers.put("random", new RandomSolver());
// add new solvers here // 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));
} }
@SuppressWarnings("unused")
public static void main(String[] args) { public static void main(String[] args) {
ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build() ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()
.defaultHelp(true) .defaultHelp(true)
@ -68,23 +79,20 @@ public class Main {
System.exit(1); System.exit(1);
} }
} }
List<String> instancePrefixes = ns.getList("instance"); List<String> instances = ns.<String>getList("instance");
List<String> instances = new ArrayList<>(); for(String instanceName : instances) {
for(String instancePrefix : instancePrefixes) { if(!BestKnownResult.isKnown(instanceName)) {
List<String> matches = BestKnownResult.instancesMatching(instancePrefix); System.err.println("ERROR: instance \"" + instanceName + "\" is not avalaible.");
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(BestKnownResult.instances));
System.exit(1); System.exit(1);
} }
instances.addAll(matches);
} }
float[] runtimes = new float[solversToTest.size()]; float[] runtimes = new float[solversToTest.size()];
float[] distances = new float[solversToTest.size()]; float[] distances = new float[solversToTest.size()];
try { try {
output.print( " "); output.print( " ");;
for(String s : solversToTest) for(String s : solversToTest)
output.printf("%-30s", s); output.printf("%-30s", s);
output.println(); output.println();

View file

@ -2,6 +2,7 @@ package jobshop;
import java.util.Optional; import java.util.Optional;
@SuppressWarnings("unused")
public class Result { public class Result {
public Result(Instance instance, Schedule schedule, ExitCause cause) { public Result(Instance instance, Schedule schedule, ExitCause cause) {

View file

@ -1,11 +1,15 @@
package jobshop; package jobshop;
import jobshop.encodings.Task; import java.util.Arrays;
import java.util.Comparator;
import java.util.*; import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import jobshop.encodings.Task;
public class Schedule { public class Schedule {
public final Instance pb; public final Instance pb;
// start times of each job and task // start times of each job and task
@ -132,4 +136,22 @@ public class Schedule {
assert isCriticalPath(path); assert isCriticalPath(path);
return 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;
}
} }

View file

@ -14,7 +14,7 @@ public class JobNumbers extends Encoding {
/** A numJobs * numTasks array containing the representation by job numbers. */ /** A numJobs * numTasks array containing the representation by job numbers. */
public final int[] jobs; 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. */ * element of `jobs` that has not been set yet. */
public int nextToSet = 0; public int nextToSet = 0;
@ -77,6 +77,38 @@ public class JobNumbers extends Encoding {
return new Schedule(instance, startTimes); 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 @Override
public String toString() { public String toString() {
return Arrays.toString(Arrays.copyOfRange(jobs,0, nextToSet)); return Arrays.toString(Arrays.copyOfRange(jobs,0, nextToSet));

View file

@ -1,37 +1,35 @@
package jobshop.encodings; package jobshop.encodings;
import jobshop.Encoding;
import jobshop.Instance;
import jobshop.Schedule;
import java.util.Comparator; import java.util.Comparator;
import java.util.Optional; import java.util.Optional;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import jobshop.Encoding;
import jobshop.Instance;
import jobshop.Schedule;
public class ResourceOrder extends Encoding { 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) {
public ResourceOrder(Instance instance)
{
super(instance); super(instance);
// matrix of null elements (null is the default value of objects) this.tasksByMachine = new Task[instance.numMachines][instance.numJobs];
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]; nextFreeSlot = new int[instance.numMachines];
} }
/** Creates a resource order from a schedule. */ public ResourceOrder(Schedule schedule) {
public ResourceOrder(Schedule schedule)
{
super(schedule.pb); super(schedule.pb);
Instance pb = schedule.pb; Instance pb = schedule.pb;
@ -53,6 +51,7 @@ public class ResourceOrder extends Encoding {
} }
} }
@Override @Override
public Schedule toSchedule() { public Schedule toSchedule() {
// indicate for each task that have been scheduled, its start time // indicate for each task that have been scheduled, its start time
@ -89,7 +88,7 @@ public class ResourceOrder extends Encoding {
// compute the earliest start time (est) of the task // 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); 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; startTimes[t.job][t.task] = est;
// mark the task as scheduled // mark the task as scheduled
@ -106,26 +105,16 @@ public class ResourceOrder extends Encoding {
return new Schedule(instance, startTimes); return new Schedule(instance, startTimes);
} }
/** Creates an exact copy of this resource order. */
public ResourceOrder copy() {
return new ResourceOrder(this.toSchedule());
}
@Override @Override
public String toString() public String toString() {
{ String res = "";
StringBuilder s = new StringBuilder(); for (int i = 0; i < this.tasksByMachine.length; i++) {
for(int m=0; m < instance.numMachines; m++) res += "Machine number : " + Integer.toString(i+1) + "\n";
{ for (int j = 0; j < this.tasksByMachine[i].length; j++) {
s.append("Machine ").append(m).append(" : "); res += "\tUse number " + Integer.toString(j+1) + " : " + this.tasksByMachine[i][j].add_one() + "\n";
for(int j=0; j<instance.numJobs; j++)
{
s.append(tasksByMachine[m][j]).append(" ; ");
} }
s.append("\n");
} }
return res;
return s.toString();
} }
} }

View file

@ -36,6 +36,10 @@ public final class Task {
@Override @Override
public String toString() { 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);
} }
} }

View file

@ -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();
}
}

View 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);
}
}

View file

@ -6,6 +6,7 @@ import jobshop.encodings.JobNumbers;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
@SuppressWarnings("unused")
public class RandomSolver implements Solver { public class RandomSolver implements Solver {
@Override @Override

View file

@ -5,6 +5,9 @@ import jobshop.Result;
import jobshop.Schedule; import jobshop.Schedule;
import jobshop.Solver; import jobshop.Solver;
import jobshop.solvers.BasicSolver; import jobshop.solvers.BasicSolver;
import jobshop.solvers.GreedySolver;
import jobshop.solvers.GreedySolver.PriorityRule;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import java.io.IOException;
@ -72,4 +75,29 @@ public class EncodingTests {
assert result.schedule.makespan() == sched.makespan(); // should have the same makespan 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
}
} }