Improvments for year 2020-2021
This commit is contained in:
parent
27a970026f
commit
1d4883388b
22 changed files with 362 additions and 147 deletions
|
@ -7,7 +7,7 @@ plugins {
|
|||
group 'jobshop'
|
||||
//version '0.1'
|
||||
|
||||
sourceCompatibility = 8
|
||||
sourceCompatibility = 11
|
||||
|
||||
|
||||
application {
|
||||
|
|
11
instances/aaa2
Normal file
11
instances/aaa2
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Example 2
|
||||
# This is the example used as a support for exercises during classes (year 2020-2021)
|
||||
4 3 # num-jobs num-tasks
|
||||
0 1 1 3 2 2
|
||||
1 8 0 5 2 10
|
||||
0 5 2 4 1 8
|
||||
2 4 0 10 1 6
|
||||
|
||||
|
||||
|
||||
|
20
instances/aaa3
Normal file
20
instances/aaa3
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Example 3
|
||||
# This problem has deterministic results for all greedy solver variants.
|
||||
# Makespan for each variant:
|
||||
# - SPT: 53
|
||||
# - LPT: 92
|
||||
# - SRPT: 78
|
||||
# - LRPT: 54
|
||||
# - EST_SPT: 48
|
||||
# - EST_LPT: 56
|
||||
# - EST_SRPT: 53
|
||||
# - EST_LRPT: 56
|
||||
4 3 # num-jobs num-tasks
|
||||
0 1 1 2 2 15
|
||||
1 4 0 5 2 11
|
||||
0 7 2 8 1 13
|
||||
2 3 0 18 1 6
|
||||
|
||||
|
||||
|
||||
|
|
@ -55,6 +55,8 @@ public class BestKnownResults {
|
|||
static {
|
||||
bests = new HashMap<>();
|
||||
bests.put("aaa1", 11);
|
||||
bests.put("aaa2", 29);
|
||||
bests.put("aaa3", 41);
|
||||
bests.put("abz5", 1234);
|
||||
bests.put("abz6", 943);
|
||||
bests.put("abz7", 656);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package jobshop;
|
||||
|
||||
import jobshop.encodings.JobNumbers;
|
||||
import jobshop.encodings.Schedule;
|
||||
import jobshop.solvers.GreedySolver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -12,25 +14,22 @@ public class DebuggingMain {
|
|||
// load the aaa1 instance
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// construit une solution dans la représentation par
|
||||
// numéro de jobs : [0 1 1 0 0 1]
|
||||
// Note : cette solution a aussi été vue dans les exercices (section 3.3)
|
||||
// mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2]
|
||||
// builds a solution in the job-numbers encoding [0 1 1 0 0 1]
|
||||
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++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.addTask(0);
|
||||
enc.addTask(1);
|
||||
enc.addTask(1);
|
||||
enc.addTask(0);
|
||||
enc.addTask(0);
|
||||
enc.addTask(1);
|
||||
|
||||
System.out.println("\nENCODING: " + enc);
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
// 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());
|
||||
Schedule schedule = enc.toSchedule();
|
||||
System.out.println("VALID: " + schedule.isValid());
|
||||
System.out.println("MAKESPAN: " + schedule.makespan());
|
||||
System.out.println("SCHEDULE: " + schedule.toString());
|
||||
System.out.println("GANTT: " + schedule.asciiGantt());
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -7,8 +7,10 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jobshop.solvers.*;
|
||||
import jobshop.solvers.neighborhood.Nowicki;
|
||||
import net.sourceforge.argparse4j.ArgumentParsers;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParser;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParserException;
|
||||
|
@ -20,14 +22,7 @@ import net.sourceforge.argparse4j.inf.Namespace;
|
|||
*/
|
||||
public class Main {
|
||||
|
||||
/** All solvers available in this program */
|
||||
private static final HashMap<String, Solver> solvers;
|
||||
static {
|
||||
solvers = new HashMap<>();
|
||||
solvers.put("basic", new BasicSolver());
|
||||
solvers.put("random", new RandomSolver());
|
||||
// TODO: add new solvers here
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -72,14 +67,7 @@ public class Main {
|
|||
// 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(0);
|
||||
}
|
||||
}
|
||||
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<>();
|
||||
|
@ -126,11 +114,10 @@ public class Main {
|
|||
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++) {
|
||||
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.
|
||||
String solverName = solversToTest.get(solverId);
|
||||
Solver solver = solvers.get(solverName);
|
||||
Solver solver = solvers.get(solverId);
|
||||
|
||||
// start chronometer and compute deadline for the solver to provide a result.
|
||||
long start = System.currentTimeMillis();
|
||||
|
@ -168,7 +155,7 @@ public class Main {
|
|||
|
||||
|
||||
} catch (Exception e) {
|
||||
// there was uncought exception, print the stack trace and exit with error.
|
||||
// there was uncaught exception, print the stack trace and exit with error.
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package jobshop;
|
||||
|
||||
import java.util.Optional;
|
||||
import jobshop.encodings.Schedule;
|
||||
|
||||
public class Result {
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package jobshop;
|
||||
|
||||
public interface Solver {
|
||||
|
||||
Result solve(Instance instance, long deadline);
|
||||
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
package jobshop;
|
||||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Instance;
|
||||
|
||||
public abstract class Encoding {
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Encoding;
|
||||
import jobshop.Instance;
|
||||
import jobshop.Schedule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
@ -45,11 +43,15 @@ public class JobNumbers extends Encoding {
|
|||
.min(Comparator.comparing(t -> schedule.startTime(t.job, t.task)))
|
||||
.get();
|
||||
|
||||
this.jobs[nextToSet++] = next.job;
|
||||
this.addTask(next.job);
|
||||
nextOnJob[next.job] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void addTask(int jobNumber) {
|
||||
this.jobs[nextToSet++] = jobNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
// time at which each machine is going to be freed
|
||||
|
@ -59,22 +61,22 @@ public class JobNumbers extends Encoding {
|
|||
int[] nextTask = new int[instance.numJobs];
|
||||
|
||||
// for each task, its start time
|
||||
int[][] startTimes = new int[instance.numJobs][instance.numTasks];
|
||||
Schedule schedule = new Schedule(instance);
|
||||
|
||||
// compute the earliest start time for every task of every job
|
||||
for(int job : jobs) {
|
||||
int task = nextTask[job];
|
||||
int machine = instance.machine(job, task);
|
||||
// earliest start time for this task
|
||||
int est = task == 0 ? 0 : startTimes[job][task-1] + instance.duration(job, task-1);
|
||||
int est = task == 0 ? 0 : schedule.endTime(job, task-1);
|
||||
est = Math.max(est, nextFreeTimeResource[machine]);
|
||||
|
||||
startTimes[job][task] = est;
|
||||
schedule.setStartTime(job, task, est);
|
||||
nextFreeTimeResource[machine] = est + instance.duration(job, task);
|
||||
nextTask[job] = task + 1;
|
||||
}
|
||||
|
||||
return new Schedule(instance, startTimes);
|
||||
return schedule;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Encoding;
|
||||
import jobshop.Instance;
|
||||
import jobshop.Schedule;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
|
@ -53,10 +51,21 @@ public class ResourceOrder extends Encoding {
|
|||
}
|
||||
}
|
||||
|
||||
public void addTaskToMachine(int machine, Task task) {
|
||||
tasksByMachine[machine][nextFreeSlot[machine]] = task;
|
||||
nextFreeSlot[machine] += 1;
|
||||
}
|
||||
|
||||
public void swapTasks(int machine, int indexFirstTask, int indexSecondTask) {
|
||||
Task tmp = tasksByMachine[machine][indexFirstTask];
|
||||
tasksByMachine[machine][indexFirstTask] = tasksByMachine[machine][indexSecondTask];
|
||||
tasksByMachine[machine][indexSecondTask] = tmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
// indicate for each task that have been scheduled, its start time
|
||||
int [][] startTimes = new int [instance.numJobs][instance.numTasks];
|
||||
Schedule schedule = new Schedule(instance);
|
||||
|
||||
// for each job, how many tasks have been scheduled (0 initially)
|
||||
int[] nextToScheduleByJob = new int[instance.numJobs];
|
||||
|
@ -88,9 +97,9 @@ public class ResourceOrder extends Encoding {
|
|||
int machine = instance.machine(t.job, t.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 : schedule.endTime(t.job, t.task-1);
|
||||
est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]);
|
||||
startTimes[t.job][t.task] = est;
|
||||
schedule.setStartTime(t.job, t.task, est);
|
||||
|
||||
// mark the task as scheduled
|
||||
nextToScheduleByJob[t.job]++;
|
||||
|
@ -103,7 +112,7 @@ public class ResourceOrder extends Encoding {
|
|||
}
|
||||
}
|
||||
// we exited the loop : all tasks have been scheduled successfully
|
||||
return new Schedule(instance, startTimes);
|
||||
return schedule;
|
||||
}
|
||||
|
||||
/** Creates an exact copy of this resource order. */
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package jobshop;
|
||||
package jobshop.encodings;
|
||||
|
||||
|
||||
import jobshop.encodings.Task;
|
||||
import jobshop.Instance;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.IntStream;
|
||||
|
@ -12,16 +11,18 @@ public class Schedule {
|
|||
// times[j][i] is the start time of task (j,i) : i^th task of the j^th job
|
||||
final int[][] times;
|
||||
|
||||
public Schedule(Instance pb, int[][] times) {
|
||||
/** Creates a new schedule for the given instance where all start times are uninitialized. */
|
||||
public Schedule(Instance pb) {
|
||||
this.pb = pb;
|
||||
this.times = new int[pb.numJobs][];
|
||||
for(int j = 0 ; j < pb.numJobs ; j++) {
|
||||
this.times[j] = Arrays.copyOf(times[j], pb.numTasks);
|
||||
this.times[j] = new int[pb.numTasks];
|
||||
}
|
||||
}
|
||||
|
||||
public int startTime(int job, int task) {
|
||||
return times[job][task];
|
||||
/** Sets the start time of the given task. */
|
||||
public void setStartTime(int job, int task, int startTime) {
|
||||
times[job][task] = startTime;
|
||||
}
|
||||
|
||||
/** Returns true if this schedule is valid (no constraint is violated) */
|
||||
|
@ -55,22 +56,38 @@ public class Schedule {
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Makespan of the solution.
|
||||
* The makespan is the end time of the latest finishing task.
|
||||
*/
|
||||
public int makespan() {
|
||||
int max = -1;
|
||||
for(int j = 0 ; j<pb.numJobs ; j++) {
|
||||
max = Math.max(max, startTime(j, pb.numTasks-1) + pb.duration(j, pb.numTasks -1));
|
||||
max = Math.max(max, endTime(j, pb.numTasks-1));
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/** Start time of the given task. */
|
||||
public int startTime(int job, int task) {
|
||||
return times[job][task];
|
||||
}
|
||||
|
||||
/** Start time of the given task. */
|
||||
public int startTime(Task task) {
|
||||
return startTime(task.job, task.task);
|
||||
}
|
||||
|
||||
public int endTime(Task task) {
|
||||
return startTime(task) + pb.duration(task.job, task.task);
|
||||
/** End time of the given task. */
|
||||
public int endTime(int job, int task) {
|
||||
return startTime(job, task) + pb.duration(job, task);
|
||||
}
|
||||
|
||||
/** End time of the given task. */
|
||||
public int endTime(Task task) {
|
||||
return endTime(task.job, task.task);
|
||||
}
|
||||
|
||||
/** Returns true if the given sequence of task is a critical path of the schedule. */
|
||||
public boolean isCriticalPath(List<Task> path) {
|
||||
if(startTime(path.get(0)) != 0) {
|
||||
return false;
|
||||
|
@ -85,6 +102,10 @@ public class Schedule {
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Computes a critical path of the schedule.
|
||||
*
|
||||
* @return A sequence of task along a critical path.
|
||||
*/
|
||||
public List<Task> criticalPath() {
|
||||
// select task with greatest end time
|
||||
Task ldd = IntStream.range(0, pb.numJobs)
|
||||
|
@ -117,7 +138,7 @@ public class Schedule {
|
|||
if(endTime(predOnJob) == startTime(cur))
|
||||
latestPredecessor = Optional.of(predOnJob);
|
||||
}
|
||||
if(!latestPredecessor.isPresent()) {
|
||||
if(latestPredecessor.isEmpty()) {
|
||||
// no latest predecessor found yet, look among tasks executing on the same machine
|
||||
latestPredecessor = IntStream.range(0, pb.numJobs)
|
||||
.mapToObj(j -> new Task(j, pb.task_with_machine(j, machine)))
|
||||
|
@ -132,4 +153,81 @@ public class Schedule {
|
|||
assert isCriticalPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\nStart times of all tasks:\n");
|
||||
for(int job=0; job<pb.numJobs; job++) {
|
||||
sb.append("Job ");
|
||||
sb.append(job);
|
||||
sb.append(": ");
|
||||
for(int task=0; task<pb.numTasks; task++) {
|
||||
sb.append(String.format("%5d", startTime(job, task)));
|
||||
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string containing the Gantt chart of the given schedule, in ASCII art.
|
||||
*
|
||||
* Each line of the Gantt chart, contains the tasks of a particular job. Each character in the output represents a
|
||||
* fixed number of time units
|
||||
* For each task, we indicate :
|
||||
* - the machine on which the task must be executed
|
||||
* - whether this task is on the critical path (task on the critical path are filled in with stars).
|
||||
*/
|
||||
public String asciiGantt() {
|
||||
var criticalPath = this.criticalPath();
|
||||
int minTaskDur = IntStream.range(0, pb.numJobs).flatMap(job -> IntStream.range(0, pb.numTasks).map(task -> pb.duration(job, task))).min().getAsInt();
|
||||
// time units by character
|
||||
int charsPerTimeUnit = minTaskDur >= 5 ? 1 : (5 / minTaskDur) +1;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\nGantt Chart\n");
|
||||
for(int job=0; job<pb.numJobs; job++) {
|
||||
sb.append(String.format("Job %2d: ", job));
|
||||
int cursor = 0;
|
||||
for(int task=0; task<pb.numTasks; task++) {
|
||||
Task t = new Task(job, task);
|
||||
var st = startTime(job, task);
|
||||
// add spaces until the start of our task
|
||||
sb.append(" ".repeat(charsPerTimeUnit * (st - cursor )));
|
||||
sb.append(formatTask(t, charsPerTimeUnit, criticalPath.contains(t)));
|
||||
|
||||
cursor = endTime(job, task);
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** Utility function to display a set of characters representing a task in a gantt chart.
|
||||
*
|
||||
* @param t Task to display
|
||||
* @param charPerTimeUnit How many characters to represent a time unit.
|
||||
* @param isCritical Is the task on the critical path.
|
||||
* @return Ascii representation of the task. Length is the duration * charPerTimeUnit.
|
||||
*/
|
||||
String formatTask(Task t, int charPerTimeUnit, boolean isCritical) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String fill = isCritical ? "*" : "-";
|
||||
int dur = pb.duration(t);
|
||||
int machine = pb.machine(t);
|
||||
int stringLength = dur * charPerTimeUnit;
|
||||
int charsForMachine = machine < 10 ? 1 : 2;
|
||||
int numSpaces = stringLength - 2 - charsForMachine; // we use 2 chars for '[' and '[' + 1 or 2 for the machine number
|
||||
int startSpaces = numSpaces / 2;
|
||||
int endSpaces = numSpaces - startSpaces;
|
||||
sb.append("[");
|
||||
sb.append(fill.repeat(startSpaces - 1));
|
||||
sb.append(" ");
|
||||
sb.append(machine);
|
||||
sb.append(" ");
|
||||
sb.append(fill.repeat(endSpaces - 1));
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -36,6 +36,6 @@ public final class Task {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + job +", " + task + '}';
|
||||
return "(" + job +", " + task + ')';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package jobshop.solvers;
|
|||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Solver;
|
||||
import jobshop.encodings.JobNumbers;
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,89 +2,22 @@ package jobshop.solvers;
|
|||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Solver;
|
||||
import jobshop.encodings.ResourceOrder;
|
||||
|
||||
import java.util.List;
|
||||
import jobshop.solvers.neighborhood.Neighborhood;
|
||||
|
||||
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;
|
||||
final Neighborhood<ResourceOrder> neighborhood;
|
||||
final Solver baseSolver;
|
||||
|
||||
Block(int machine, int firstTask, int lastTask) {
|
||||
this.machine = machine;
|
||||
this.firstTask = firstTask;
|
||||
this.lastTask = lastTask;
|
||||
}
|
||||
public DescentSolver(Neighborhood<ResourceOrder> neighborhood, Solver baseSolver) {
|
||||
this.neighborhood = neighborhood;
|
||||
this.baseSolver = baseSolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 swap 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
21
src/main/java/jobshop/solvers/GreedySolver.java
Normal file
21
src/main/java/jobshop/solvers/GreedySolver.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
package jobshop.solvers;
|
||||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
|
||||
public class GreedySolver implements Solver {
|
||||
|
||||
public enum Priority {
|
||||
SPT, LPT, SRPT, LRPT, EST_SPT, EST_LPT, EST_SRPT, EST_LRPT
|
||||
}
|
||||
|
||||
final Priority priority;
|
||||
public GreedySolver(Priority p) {
|
||||
this.priority = p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@ package jobshop.solvers;
|
|||
|
||||
import jobshop.*;
|
||||
import jobshop.encodings.JobNumbers;
|
||||
import jobshop.encodings.Schedule;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomSolver implements Solver {
|
||||
|
|
21
src/main/java/jobshop/solvers/Solver.java
Normal file
21
src/main/java/jobshop/solvers/Solver.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
package jobshop.solvers;
|
||||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
|
||||
public interface Solver {
|
||||
|
||||
Result solve(Instance instance, long deadline);
|
||||
|
||||
/** Static factory method to create a new solver based on its name. */
|
||||
static Solver getSolver(String name) {
|
||||
switch (name) {
|
||||
case "basic": return new BasicSolver();
|
||||
case "random": return new RandomSolver();
|
||||
case "spt": return new GreedySolver(GreedySolver.Priority.SPT);
|
||||
// TODO: add new solvers
|
||||
default: throw new RuntimeException("Unknown solver: "+ name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
8
src/main/java/jobshop/solvers/neighborhood/Neighbor.java
Normal file
8
src/main/java/jobshop/solvers/neighborhood/Neighbor.java
Normal file
|
@ -0,0 +1,8 @@
|
|||
package jobshop.solvers.neighborhood;
|
||||
|
||||
public abstract class Neighbor<Encoding> {
|
||||
|
||||
public abstract void applyOn(Encoding current);
|
||||
public abstract void undoApplyOn(Encoding current);
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package jobshop.solvers.neighborhood;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Neighborhood<Encoding> {
|
||||
|
||||
public abstract List<Neighbor<Encoding>> generateNeighbors(Encoding current);
|
||||
|
||||
}
|
102
src/main/java/jobshop/solvers/neighborhood/Nowicki.java
Normal file
102
src/main/java/jobshop/solvers/neighborhood/Nowicki.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
package jobshop.solvers.neighborhood;
|
||||
|
||||
import jobshop.encodings.ResourceOrder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Nowicki extends Neighborhood<ResourceOrder> {
|
||||
|
||||
/** 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 swap 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 extends Neighbor<ResourceOrder> {
|
||||
// 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. */
|
||||
@Override
|
||||
public void applyOn(ResourceOrder current) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoApplyOn(ResourceOrder current) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Neighbor<ResourceOrder>> generateNeighbors(ResourceOrder current) {
|
||||
List<Neighbor<ResourceOrder>> neighbors = new ArrayList<>();
|
||||
// iterate over all blocks of the critical
|
||||
for(var block : blocksOfCriticalPath(current)) {
|
||||
// for this block, compute all neighbors and add them to the list of neighbors
|
||||
neighbors.addAll(neighbors(block));
|
||||
}
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
/** Returns a list of all the 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -2,8 +2,7 @@ package jobshop.encodings;
|
|||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Schedule;
|
||||
import jobshop.Solver;
|
||||
import jobshop.solvers.Solver;
|
||||
import jobshop.solvers.BasicSolver;
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
Loading…
Reference in a new issue