diff --git a/src/main/java/jobshop/encodings/ResourceOrder.java b/src/main/java/jobshop/encodings/ResourceOrder.java index 0dfccad..48e7f09 100644 --- a/src/main/java/jobshop/encodings/ResourceOrder.java +++ b/src/main/java/jobshop/encodings/ResourceOrder.java @@ -1,7 +1,9 @@ package jobshop.encodings; import jobshop.Instance; +import jobshop.solvers.neighborhood.Nowicki; +import java.sql.Array; import java.util.Arrays; import java.util.Comparator; import java.util.Optional; @@ -194,4 +196,25 @@ public final class ResourceOrder extends Encoding { result = 31 * result + Arrays.hashCode(nextFreeSlot); return result; } + + + public int[] getSwap(ResourceOrder o){ // returns [machineNb,task1,task2] + int[] ret = {0,0,0}; + if (this.equals(o)){ + ret = new int[]{-1, -1, -1}; + } + Task[][] thisTasks = this.getTasksByMachine(); + Task[][] otherTasks = o.getTasksByMachine(); + for(int i=0; i< instance.numMachines; i++){ + for(int j=0; j< instance.numJobs; j++){ + if (thisTasks[i][j] != otherTasks[i][j]){ + ret[0] = i; + ret[1] = j; + ret[2] = j+1; + break; + } + } + } + return ret; + } } \ No newline at end of file diff --git a/src/main/java/jobshop/solvers/DescentSolver.java b/src/main/java/jobshop/solvers/DescentSolver.java index df18175..b63d087 100644 --- a/src/main/java/jobshop/solvers/DescentSolver.java +++ b/src/main/java/jobshop/solvers/DescentSolver.java @@ -48,7 +48,7 @@ public class DescentSolver implements Solver { best = order.toSchedule().get().makespan(); System.out.println("\u001b[32m" + "Current best makespam : " + best + "\u001b[0m, " + "\u001b[33m" + "Number of neighbours : " + getNumberOfNeighbours(neighbours) + "\u001b[0m"); - } catch (Exception e) { // no solution found ==> stop + } catch (ArrayIndexOutOfBoundsException e) { // no solution found ==> stop bestFound = true; } } diff --git a/src/main/java/jobshop/solvers/GreedySolver.java b/src/main/java/jobshop/solvers/GreedySolver.java index cbe5058..f5bb180 100644 --- a/src/main/java/jobshop/solvers/GreedySolver.java +++ b/src/main/java/jobshop/solvers/GreedySolver.java @@ -13,7 +13,7 @@ public class GreedySolver implements Solver { /** All possible priorities for the greedy solver. */ public enum Priority { - SPT, LPT, SRPT, LRPT, EST_SPT, EST_LPT, EST_SRPT, EST_LRPT + SPT, LRPT, EST_SPT, EST_LRPT } /** Priority that the solver should use. */ @@ -54,20 +54,6 @@ public class GreedySolver implements Solver { .sorted(Comparator.comparing(e -> heuristiqueSPT(e, instance))) .toArray(Task[]::new)[0] ); - case LPT: - // TODO - return( - lTask.stream() - .sorted(Comparator.comparing(e -> heuristiqueSPT(e, instance))) - .toArray(Task[]::new)[0] - ); - case SRPT: - // TODO - return( - lTask.stream() - .sorted(Comparator.comparing(e -> heuristiqueSPT(e, instance))) - .toArray(Task[]::new)[0] - ); case LRPT: return( lTask.stream() @@ -93,21 +79,6 @@ public class GreedySolver implements Solver { .sorted(Comparator.comparing(e -> heuristiqueSPT(e, instance))) .toArray(Task[]::new)[0] ); - - case EST_LPT: - // TODO - return( - lTask.stream() - .sorted(Comparator.comparing(e -> heuristiqueSPT(e, instance))) - .toArray(Task[]::new)[0] - ); - case EST_SRPT: - //TODO - return( - lTask.stream() - .sorted(Comparator.comparing(e -> heuristiqueSPT(e, instance))) - .toArray(Task[]::new)[0] - ); case EST_LRPT: // find the minimal next execution time value diff --git a/src/main/java/jobshop/solvers/Solver.java b/src/main/java/jobshop/solvers/Solver.java index 66f63f1..1724181 100644 --- a/src/main/java/jobshop/solvers/Solver.java +++ b/src/main/java/jobshop/solvers/Solver.java @@ -29,6 +29,8 @@ public interface Solver { case "desc_est_spt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_SPT)); case "desc_est_lrpt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_LRPT)); case "desc_multi": return new DescentSolverMultiStart(new Nowicki()); + case "taboo_est_spt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_SPT)); + case "taboo_est_lrpt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_LRPT)); default: throw new RuntimeException("Unknown solver: "+ name); } } diff --git a/src/main/java/jobshop/solvers/TabooSolver.java b/src/main/java/jobshop/solvers/TabooSolver.java new file mode 100644 index 0000000..6b23ecd --- /dev/null +++ b/src/main/java/jobshop/solvers/TabooSolver.java @@ -0,0 +1,144 @@ +package jobshop.solvers; + +import java.io.*; + +import jobshop.Instance; +import jobshop.encodings.ResourceOrder; +import jobshop.encodings.Schedule; +import jobshop.solvers.neighborhood.Neighborhood; + +import java.time.format.ResolverStyle; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +public class TabooSolver implements Solver { + final Neighborhood neighborhood; + final Solver baseSolver; + + /** Creates a new descent solver with a given neighborhood and a solver for the initial solution. + * + * @param neighborhood Neighborhood object that should be used to generates neighbor solutions to the current candidate. + * @param baseSolver A solver to provide the initial solution. + */ + public TabooSolver(Neighborhood neighborhood, Solver baseSolver) { + this.neighborhood = neighborhood; + this.baseSolver = baseSolver; + } + + @Override + public Optional solve(Instance instance, long deadline, int randomness, int randomRunNumber) { + Schedule schedule = baseSolver.solve(instance, deadline,randomness,randomRunNumber).get(); + ResourceOrder order = new ResourceOrder(schedule); + ResourceOrder result = order; + + /*for output writing*/ + FileWriter fw = null; + try { + fw = new FileWriter("output.csv"); + } catch (IOException e) { + e.printStackTrace(); + } + PrintWriter pw = new PrintWriter(fw); + + + //init forbidden states : Array of forbidden swaps + int[][][] forbiddenNeighbours = new int[instance.numMachines][instance.numJobs][instance.numJobs]; + int[] zeroArray = new int[instance.numJobs]; + Arrays.fill(zeroArray,0); + + for (int i = 0; i < instance.numMachines; i++) { + Arrays.fill(forbiddenNeighbours[i],zeroArray); + } + int iterationCount = 0; + Boolean noMoreSolution = false; + Integer best = Integer.MAX_VALUE; + ResourceOrder bestRO = order; + ResourceOrder previousRO; + while(System.currentTimeMillis() < deadline && !noMoreSolution) { + List neighbours = neighborhood.generateNeighbors(order); + previousRO = order; + try { + Integer finalBest = best; + try { // is there an improving best ? + order = neighbours.stream() + .filter(e -> e.toSchedule().get().isValid()) // removes invalid solutions + .filter(e -> e.toSchedule().get().makespan() < finalBest) // removes solutions that do not improve + .sorted(Comparator.comparing(e -> e.toSchedule().get().makespan())) // sorts with regard to makespan + .toArray(ResourceOrder[]::new)[0]; + result = order; + } catch (ArrayIndexOutOfBoundsException e1) { + + /*added to have final variables*/ + ResourceOrder finalPreviousRO = previousRO; + int[][][] finalForbiddenNeighbours = forbiddenNeighbours; + int finalIterationCount = iterationCount; + + order = neighbours.stream() + .filter(e -> e.toSchedule().get().isValid()) // removes invalid solutions + .filter(e -> !isForbidden(e, finalPreviousRO, finalForbiddenNeighbours, finalIterationCount)) + .sorted(Comparator.comparing(e -> e.toSchedule().get().makespan())) // sorts with regard to makespan + .toArray(ResourceOrder[]::new)[0]; + result = order; + } + } catch (ArrayIndexOutOfBoundsException e2) { // no solution found ==> stop + + } + System.out.println("\u001b[34m" + "Current makespam : " + result.toSchedule().get().makespan() + "\u001b[0m, " + + "\u001b[32m" + "Current best makespam : " + best + "\u001b[0m, " + + "\u001b[33m" + "Number of neighbours : " + getNumberOfNeighbours(neighbours) + "\u001b[0m, " + + "\u001b[37m" + "Number of non-forbidden neighbours : " + getNumberOfNonForbiddenNeighbours(neighbours,previousRO, forbiddenNeighbours, iterationCount) + "\u001b[0m"); + + pw.println(result.toSchedule().get().makespan() + "," + + best + "," + + getNumberOfNeighbours(neighbours) + "," + + getNumberOfNonForbiddenNeighbours(neighbours,previousRO, forbiddenNeighbours, iterationCount)); + + + best = Integer.min(result.toSchedule().get().makespan(),best); + if (bestRO.toSchedule().get().makespan() > result.toSchedule().get().makespan()) { + bestRO = result; + } + forbiddenNeighbours = computeNewForbidden(result,previousRO,forbiddenNeighbours,iterationCount); + iterationCount++; + } + + return bestRO.toSchedule(); + } + // used to compute the number of neighbours seen + private int getNumberOfNeighbours(List neighbours) { + return neighbours.stream() + .filter(e -> e.toSchedule().get().isValid()) // removes invalid solutions + .toArray(ResourceOrder[]::new).length; + } + + private int getNumberOfNonForbiddenNeighbours(List neighbours, ResourceOrder previous, int[][][] forbiddenNeighbours, int iterationCount) { + return neighbours.stream() + .filter(e -> e.toSchedule().get().isValid()) // removes invalid solutions + .filter(e -> !isForbidden(e, previous, forbiddenNeighbours, iterationCount)) + .toArray(ResourceOrder[]::new).length; + } + + private boolean isForbidden(ResourceOrder ro, ResourceOrder previous, int[][][] forbiddenNeighbours, int iterationCount){ + int[] changes = ro.getSwap(previous); + + if (changes[0] == -1) {return true;} + + if ((forbiddenNeighbours[changes[0]][changes[1]][changes[2]] == 0) + || (forbiddenNeighbours[changes[0]][changes[1]][changes[2]] < iterationCount - 10 /*to tune*/)){ + return false; + } else { + return true; + } + } + + private int[][][] computeNewForbidden(ResourceOrder ro, ResourceOrder previous, int[][][] forbiddenNeighbours, int iterationCount){ + int[] changes = ro.getSwap(previous); + if (changes[0] != -1){ + forbiddenNeighbours[changes[0]][changes[1]][changes[2]] = iterationCount; + } + return forbiddenNeighbours; + } +} + diff --git a/src/main/java/jobshop/solvers/neighborhood/Neighborhood.java b/src/main/java/jobshop/solvers/neighborhood/Neighborhood.java index 7dd6190..6914c6d 100644 --- a/src/main/java/jobshop/solvers/neighborhood/Neighborhood.java +++ b/src/main/java/jobshop/solvers/neighborhood/Neighborhood.java @@ -8,8 +8,6 @@ import java.util.List; * a set of closely related solutions. */ public abstract class Neighborhood { - /** Generates all neighbors for the current solution. */ public abstract List generateNeighbors(ResourceOrder current); - }