Improvments for year 2020-2021

This commit is contained in:
Arthur Bit-Monnot 2021-04-08 17:14:12 +02:00
parent 27a970026f
commit 1d4883388b
22 changed files with 362 additions and 147 deletions

View file

@ -7,7 +7,7 @@ plugins {
group 'jobshop' group 'jobshop'
//version '0.1' //version '0.1'
sourceCompatibility = 8 sourceCompatibility = 11
application { application {

11
instances/aaa2 Normal file
View 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
View 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

View file

@ -55,6 +55,8 @@ public class BestKnownResults {
static { static {
bests = new HashMap<>(); bests = new HashMap<>();
bests.put("aaa1", 11); bests.put("aaa1", 11);
bests.put("aaa2", 29);
bests.put("aaa3", 41);
bests.put("abz5", 1234); bests.put("abz5", 1234);
bests.put("abz6", 943); bests.put("abz6", 943);
bests.put("abz7", 656); bests.put("abz7", 656);

View file

@ -1,6 +1,8 @@
package jobshop; package jobshop;
import jobshop.encodings.JobNumbers; import jobshop.encodings.JobNumbers;
import jobshop.encodings.Schedule;
import jobshop.solvers.GreedySolver;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -12,25 +14,22 @@ public class DebuggingMain {
// load the aaa1 instance // load the aaa1 instance
Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
// construit une solution dans la représentation par // builds a solution in the job-numbers encoding [0 1 1 0 0 1]
// 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]
JobNumbers enc = new JobNumbers(instance); JobNumbers enc = new JobNumbers(instance);
enc.jobs[enc.nextToSet++] = 0; enc.addTask(0);
enc.jobs[enc.nextToSet++] = 1; enc.addTask(1);
enc.jobs[enc.nextToSet++] = 1; enc.addTask(1);
enc.jobs[enc.nextToSet++] = 0; enc.addTask(0);
enc.jobs[enc.nextToSet++] = 0; enc.addTask(0);
enc.jobs[enc.nextToSet++] = 1; enc.addTask(1);
System.out.println("\nENCODING: " + enc); System.out.println("\nENCODING: " + enc);
Schedule sched = enc.toSchedule(); Schedule schedule = enc.toSchedule();
// TODO: make it print something meaningful by implementing the Schedule.toString() method System.out.println("VALID: " + schedule.isValid());
System.out.println("SCHEDULE: " + sched); System.out.println("MAKESPAN: " + schedule.makespan());
System.out.println("VALID: " + sched.isValid()); System.out.println("SCHEDULE: " + schedule.toString());
System.out.println("MAKESPAN: " + sched.makespan()); System.out.println("GANTT: " + schedule.asciiGantt());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -7,8 +7,10 @@ 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 java.util.stream.Collectors;
import jobshop.solvers.*; import jobshop.solvers.*;
import jobshop.solvers.neighborhood.Nowicki;
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;
@ -20,14 +22,7 @@ import net.sourceforge.argparse4j.inf.Namespace;
*/ */
public class Main { 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) { public static void main(String[] args) {
@ -72,14 +67,7 @@ public class Main {
// Get the list of solvers that we should benchmark. // 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. // We also check that we have a solver available for the given name and print an error message otherwise.
List<String> solversToTest = ns.getList("solver"); List<String> solversToTest = ns.getList("solver");
for(String solverName : solversToTest) { List<Solver> solvers = solversToTest.stream().map(Solver::getSolver).collect(Collectors.toList());
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);
}
}
// retrieve all instances on which we should run the solvers. // retrieve all instances on which we should run the solvers.
List<String> instances = new ArrayList<>(); List<String> instances = new ArrayList<>();
@ -126,11 +114,10 @@ public class Main {
output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown); output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
// run all selected solvers on the instance and print the results // 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, // 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. // 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(solverId);
Solver solver = solvers.get(solverName);
// start chronometer and compute deadline for the solver to provide a result. // start chronometer and compute deadline for the solver to provide a result.
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
@ -168,7 +155,7 @@ public class Main {
} catch (Exception e) { } 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(); e.printStackTrace();
System.exit(1); System.exit(1);
} }

View file

@ -1,6 +1,6 @@
package jobshop; package jobshop;
import java.util.Optional; import jobshop.encodings.Schedule;
public class Result { public class Result {

View file

@ -1,7 +0,0 @@
package jobshop;
public interface Solver {
Result solve(Instance instance, long deadline);
}

View file

@ -1,4 +1,6 @@
package jobshop; package jobshop.encodings;
import jobshop.Instance;
public abstract class Encoding { public abstract class Encoding {

View file

@ -1,8 +1,6 @@
package jobshop.encodings; package jobshop.encodings;
import jobshop.Encoding;
import jobshop.Instance; import jobshop.Instance;
import jobshop.Schedule;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@ -45,11 +43,15 @@ public class JobNumbers extends Encoding {
.min(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) .min(Comparator.comparing(t -> schedule.startTime(t.job, t.task)))
.get(); .get();
this.jobs[nextToSet++] = next.job; this.addTask(next.job);
nextOnJob[next.job] += 1; nextOnJob[next.job] += 1;
} }
} }
public void addTask(int jobNumber) {
this.jobs[nextToSet++] = jobNumber;
}
@Override @Override
public Schedule toSchedule() { public Schedule toSchedule() {
// time at which each machine is going to be freed // 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]; int[] nextTask = new int[instance.numJobs];
// for each task, its start time // 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 // compute the earliest start time for every task of every job
for(int job : jobs) { for(int job : jobs) {
int task = nextTask[job]; int task = nextTask[job];
int machine = instance.machine(job, task); int machine = instance.machine(job, task);
// earliest start time for this 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]); est = Math.max(est, nextFreeTimeResource[machine]);
startTimes[job][task] = est; schedule.setStartTime(job, task, est);
nextFreeTimeResource[machine] = est + instance.duration(job, task); nextFreeTimeResource[machine] = est + instance.duration(job, task);
nextTask[job] = task + 1; nextTask[job] = task + 1;
} }
return new Schedule(instance, startTimes); return schedule;
} }
@Override @Override

View file

@ -1,8 +1,6 @@
package jobshop.encodings; package jobshop.encodings;
import jobshop.Encoding;
import jobshop.Instance; import jobshop.Instance;
import jobshop.Schedule;
import java.util.Comparator; import java.util.Comparator;
import java.util.Optional; 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 @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
int [][] startTimes = new int [instance.numJobs][instance.numTasks]; Schedule schedule = new Schedule(instance);
// for each job, how many tasks have been scheduled (0 initially) // for each job, how many tasks have been scheduled (0 initially)
int[] nextToScheduleByJob = new int[instance.numJobs]; int[] nextToScheduleByJob = new int[instance.numJobs];
@ -88,9 +97,9 @@ public class ResourceOrder extends Encoding {
int machine = instance.machine(t.job, t.task); int machine = instance.machine(t.job, t.task);
// 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 : schedule.endTime(t.job, t.task-1);
est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]); 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 // mark the task as scheduled
nextToScheduleByJob[t.job]++; nextToScheduleByJob[t.job]++;
@ -103,7 +112,7 @@ public class ResourceOrder extends Encoding {
} }
} }
// we exited the loop : all tasks have been scheduled successfully // 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. */ /** Creates an exact copy of this resource order. */

View file

@ -1,7 +1,6 @@
package jobshop; package jobshop.encodings;
import jobshop.Instance;
import jobshop.encodings.Task;
import java.util.*; import java.util.*;
import java.util.stream.IntStream; 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 // times[j][i] is the start time of task (j,i) : i^th task of the j^th job
final int[][] times; 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.pb = pb;
this.times = new int[pb.numJobs][]; this.times = new int[pb.numJobs][];
for(int j = 0 ; j < pb.numJobs ; j++) { 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) { /** Sets the start time of the given task. */
return times[job][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) */ /** Returns true if this schedule is valid (no constraint is violated) */
@ -55,22 +56,38 @@ public class Schedule {
return true; return true;
} }
/** Makespan of the solution.
* The makespan is the end time of the latest finishing task.
*/
public int makespan() { public int makespan() {
int max = -1; int max = -1;
for(int j = 0 ; j<pb.numJobs ; j++) { 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; 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) { public int startTime(Task task) {
return startTime(task.job, task.task); return startTime(task.job, task.task);
} }
public int endTime(Task task) { /** End time of the given task. */
return startTime(task) + pb.duration(task.job, task.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) { public boolean isCriticalPath(List<Task> path) {
if(startTime(path.get(0)) != 0) { if(startTime(path.get(0)) != 0) {
return false; return false;
@ -85,6 +102,10 @@ public class Schedule {
return true; return true;
} }
/** Computes a critical path of the schedule.
*
* @return A sequence of task along a critical path.
*/
public List<Task> criticalPath() { public List<Task> criticalPath() {
// select task with greatest end time // select task with greatest end time
Task ldd = IntStream.range(0, pb.numJobs) Task ldd = IntStream.range(0, pb.numJobs)
@ -117,7 +138,7 @@ public class Schedule {
if(endTime(predOnJob) == startTime(cur)) if(endTime(predOnJob) == startTime(cur))
latestPredecessor = Optional.of(predOnJob); latestPredecessor = Optional.of(predOnJob);
} }
if(!latestPredecessor.isPresent()) { if(latestPredecessor.isEmpty()) {
// no latest predecessor found yet, look among tasks executing on the same machine // no latest predecessor found yet, look among tasks executing on the same machine
latestPredecessor = IntStream.range(0, pb.numJobs) latestPredecessor = IntStream.range(0, pb.numJobs)
.mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine)))
@ -132,4 +153,81 @@ public class Schedule {
assert isCriticalPath(path); assert isCriticalPath(path);
return 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();
}
} }

View file

@ -36,6 +36,6 @@ public final class Task {
@Override @Override
public String toString() { public String toString() {
return "(" + job +", " + task + '}'; return "(" + job +", " + task + ')';
} }
} }

View file

@ -2,7 +2,6 @@ package jobshop.solvers;
import jobshop.Instance; import jobshop.Instance;
import jobshop.Result; import jobshop.Result;
import jobshop.Solver;
import jobshop.encodings.JobNumbers; import jobshop.encodings.JobNumbers;
/** /**

View file

@ -2,89 +2,22 @@ package jobshop.solvers;
import jobshop.Instance; import jobshop.Instance;
import jobshop.Result; import jobshop.Result;
import jobshop.Solver;
import jobshop.encodings.ResourceOrder; import jobshop.encodings.ResourceOrder;
import jobshop.solvers.neighborhood.Neighborhood;
import java.util.List;
public class DescentSolver implements Solver { 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. final Neighborhood<ResourceOrder> neighborhood;
* This class identifies a block in a ResourceOrder representation. final Solver baseSolver;
*
* 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) { public DescentSolver(Neighborhood<ResourceOrder> neighborhood, Solver baseSolver) {
this.machine = machine; this.neighborhood = neighborhood;
this.firstTask = firstTask; this.baseSolver = baseSolver;
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 {
// 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 @Override
public Result solve(Instance instance, long deadline) { public Result solve(Instance instance, long deadline) {
throw new UnsupportedOperationException(); 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,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();
}
}

View file

@ -2,8 +2,8 @@ package jobshop.solvers;
import jobshop.*; import jobshop.*;
import jobshop.encodings.JobNumbers; import jobshop.encodings.JobNumbers;
import jobshop.encodings.Schedule;
import java.util.Optional;
import java.util.Random; import java.util.Random;
public class RandomSolver implements Solver { public class RandomSolver implements Solver {

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

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

View file

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

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

View file

@ -2,8 +2,7 @@ package jobshop.encodings;
import jobshop.Instance; import jobshop.Instance;
import jobshop.Result; import jobshop.Result;
import jobshop.Schedule; import jobshop.solvers.Solver;
import jobshop.Solver;
import jobshop.solvers.BasicSolver; import jobshop.solvers.BasicSolver;
import org.junit.Test; import org.junit.Test;