diff --git a/doc/src/TP_Meta-20-21-Version2.pdf b/doc/src/TP_Meta-20-21-Version2.pdf new file mode 100644 index 0000000..f456c4e Binary files /dev/null and b/doc/src/TP_Meta-20-21-Version2.pdf differ diff --git a/src/main/java/jobshop/Main.java b/src/main/java/jobshop/Main.java index 3ae8ccc..d0b491e 100644 --- a/src/main/java/jobshop/Main.java +++ b/src/main/java/jobshop/Main.java @@ -81,6 +81,7 @@ public class Main { float[] avg_runtimes = new float[solversToTest.size()]; // average distance to best known result for each solver float[] avg_distances = new float[solversToTest.size()]; + float[] avg_voisins = new float[solversToTest.size()]; try { // header of the result table : @@ -88,11 +89,11 @@ public class Main { // - name of each column (second line) output.print( " "); for(String s : solversToTest) - output.printf("%-30s", s); + output.printf("%-38s", s); output.println(); output.print("instance size best "); for(String s : solversToTest) { - output.print("runtime makespan ecart "); + output.print("runtime makespan ecart voisins "); } output.println(); @@ -135,8 +136,8 @@ public class Main { float dist = 100f * (makespan - bestKnown) / (float) bestKnown; avg_runtimes[solverId] += (float) runtime / (float) instances.size(); avg_distances[solverId] += dist / (float) instances.size(); - - output.printf("%7d %8s %5.1f ", runtime, makespan, dist); + avg_voisins[solverId] += (float) result.getVoisinsVisites() / (float) instances.size(); + output.printf("%7d %8s %5.1f %7d ", runtime, makespan, dist, result.getVoisinsVisites()); output.flush(); } output.println(); @@ -146,7 +147,7 @@ public class Main { // we have finished all benchmarks, compute the average solve time and distance of each solver. output.printf("%-8s %-5s %4s ", "AVG", "-", "-"); for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) { - output.printf("%7.1f %8s %5.1f ", avg_runtimes[solverId], "-", avg_distances[solverId]); + output.printf("%7.1f %8s %5.1f %7.1f ", avg_runtimes[solverId], "-", avg_distances[solverId], avg_voisins[solverId]); } diff --git a/src/main/java/jobshop/Result.java b/src/main/java/jobshop/Result.java index ec3c26d..fda0365 100644 --- a/src/main/java/jobshop/Result.java +++ b/src/main/java/jobshop/Result.java @@ -16,11 +16,22 @@ public class Result { /** Reason why the solver exited with this solution. */ public final ExitCause cause; + private int voisinsVisites; + /** Creates a new Result object with the corresponding fields. */ public Result(Instance instance, Optional schedule, ExitCause cause) { this.instance = instance; this.schedule = schedule; this.cause = cause; + this.voisinsVisites = 0; + } + + public int getVoisinsVisites() { + return voisinsVisites; + } + + public void setVoisinsVisites(int voisinsVisites) { + this.voisinsVisites = voisinsVisites; } /** Documents the reason why a solver returned the solution. */ diff --git a/src/main/java/jobshop/solvers/DescentSolver.java b/src/main/java/jobshop/solvers/DescentSolver.java index a6ca4d1..f6ed664 100644 --- a/src/main/java/jobshop/solvers/DescentSolver.java +++ b/src/main/java/jobshop/solvers/DescentSolver.java @@ -38,14 +38,16 @@ public class DescentSolver implements Solver { boolean ended = false; int bestMakespan; Neighbor bestNeighbor; + int voisinsVisites = 0; - while (!ended) { + while (!ended && deadline - System.currentTimeMillis() > 0) { schedule = resourceOrder.toSchedule().get(); bestMakespan = schedule.makespan(); bestNeighbor = null; List> generatedNeighbors = neighborhood.generateNeighbors(resourceOrder); Iterator> iter = generatedNeighbors.iterator(); while (iter.hasNext()) { + voisinsVisites++; Neighbor neighbor = iter.next(); neighbor.applyOn(resourceOrder); try { @@ -65,7 +67,15 @@ public class DescentSolver implements Solver { bestNeighbor.applyOn(resourceOrder); } } - return new Result(instance, resourceOrder.toSchedule(), Result.ExitCause.ProvedOptimal); + + Result result; + if (ended) { + result = new Result(instance, resourceOrder.toSchedule(), Result.ExitCause.Blocked); + } else { + result = new Result(instance, resourceOrder.toSchedule(), Result.ExitCause.Timeout); + } + result.setVoisinsVisites(voisinsVisites); + return result; } } diff --git a/src/main/java/jobshop/solvers/GreedySolver.java b/src/main/java/jobshop/solvers/GreedySolver.java index aea875c..c7b243e 100644 --- a/src/main/java/jobshop/solvers/GreedySolver.java +++ b/src/main/java/jobshop/solvers/GreedySolver.java @@ -179,7 +179,7 @@ public class GreedySolver implements Solver { Task nextTask; //Itérations - while (!this.tachesRestantes.isEmpty()) { + while (!this.tachesRestantes.isEmpty() && deadline - System.currentTimeMillis() > 0) { task = this.getTask(instance); int startTime = getEarliestDate(instance, task); resourceOrder.addTaskToMachine(instance.machine(task), task); @@ -190,6 +190,13 @@ public class GreedySolver implements Solver { this.addTask(instance, new Task(task.job, task.task + 1)); } } - return new Result (instance, resourceOrder.toSchedule(), ProvedOptimal); + + Result result; + if (this.tachesRestantes.isEmpty()) { + result = new Result(instance, resourceOrder.toSchedule(), Result.ExitCause.Blocked); + } else { + result = new Result(instance, resourceOrder.toSchedule(), Result.ExitCause.Timeout); + } + return result; } } diff --git a/src/main/java/jobshop/solvers/Solver.java b/src/main/java/jobshop/solvers/Solver.java index 0653031..36cbebd 100644 --- a/src/main/java/jobshop/solvers/Solver.java +++ b/src/main/java/jobshop/solvers/Solver.java @@ -38,6 +38,14 @@ public interface Solver { case "descent_est_lpt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_LPT)); case "descent_est_srpt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_SRPT)); case "descent_est_lrpt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_LRPT)); + case "taboo_spt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.SPT)); + case "taboo_lpt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.LPT)); + case "taboo_srpt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.SRPT)); + case "taboo_lrpt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.LRPT)); + case "taboo_est_spt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_SPT)); + case "taboo_est_lpt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_LPT)); + case "taboo_est_srpt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_SRPT)); + case "taboo_est_lrpt": return new TabooSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_LRPT)); // TODO: add new solvers 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..01b9b4f --- /dev/null +++ b/src/main/java/jobshop/solvers/TabooSolver.java @@ -0,0 +1,101 @@ +package jobshop.solvers; + +import jobshop.Instance; +import jobshop.Result; +import jobshop.encodings.ResourceOrder; +import jobshop.encodings.Schedule; +import jobshop.solvers.neighborhood.Neighbor; +import jobshop.solvers.neighborhood.Neighborhood; +import jobshop.solvers.neighborhood.Nowicki; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +/** An empty shell to implement a descent solver. */ +public class TabooSolver implements Solver { + + final Neighborhood neighborhood; + final Solver baseSolver; + int maxIter = 100; + int dureeTaboo = 4; + + /** Creates a new taboo 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 Result solve(Instance instance, long deadline) { + Schedule schedule = baseSolver.solve(instance, deadline).schedule.get(); + ResourceOrder resourceOrder = new ResourceOrder(schedule); + int nbKey = Nowicki.Swap.getNbKey(instance.numMachines, instance.numJobs); + int[] taboo = new int[nbKey]; + for (int i = 0; i bestNeighbor; + int voisinsVisites = 0; + int k = 0; + while (deadline - System.currentTimeMillis() > 0 && k < maxIter) { + k++; + bestMakespan = Integer.MAX_VALUE; + bestNeighbor = null; + List> generatedNeighbors = neighborhood.generateNeighbors(resourceOrder); + Iterator> iter = generatedNeighbors.iterator(); + while (iter.hasNext()) { + voisinsVisites++; + Neighbor neighbor = iter.next(); + Nowicki.Swap currentSwap = (Nowicki.Swap) neighbor.getChange(); + int currentMakespan = Integer.MAX_VALUE; + try { + neighbor.applyOn(resourceOrder); + Schedule currentSchedule = resourceOrder.toSchedule().get(); + currentMakespan = currentSchedule.makespan(); + neighbor.undoApplyOn(resourceOrder); + } catch (Exception e) { + } + if (taboo[currentSwap.generateKey(instance.numJobs)] < k || currentMakespan < starMakespan) { + neighbor.applyOn(resourceOrder); + try { + Schedule currentSchedule = resourceOrder.toSchedule().get(); + currentMakespan = currentSchedule.makespan(); + if (currentMakespan < bestMakespan) { + bestMakespan = currentMakespan; + bestNeighbor = neighbor; + } + } catch (Exception e) { + } + neighbor.undoApplyOn(resourceOrder); + } + } + + if (bestNeighbor != null) { + Nowicki.Swap swap = (Nowicki.Swap) bestNeighbor.getChange(); + taboo[swap.generateKey(instance.numJobs)] = k + dureeTaboo; + bestNeighbor.applyOn(resourceOrder); + if (bestMakespan < starMakespan) { + starMakespan = bestMakespan; + starResourceOrder = resourceOrder.copy(); + } + } + } + + Result result; + result = new Result(instance, starResourceOrder.toSchedule(), Result.ExitCause.Timeout); + result.setVoisinsVisites(voisinsVisites); + return result; + } + +} diff --git a/src/main/java/jobshop/solvers/neighborhood/Neighbor.java b/src/main/java/jobshop/solvers/neighborhood/Neighbor.java index 4effc28..8948a0f 100644 --- a/src/main/java/jobshop/solvers/neighborhood/Neighbor.java +++ b/src/main/java/jobshop/solvers/neighborhood/Neighbor.java @@ -15,4 +15,7 @@ public abstract class Neighbor { /** Transform the neighbor back into the original solution. */ public abstract void undoApplyOn(Enc current); + /** Renvoi l'objet caracterisant le changement **/ + public abstract Object getChange(); + } diff --git a/src/main/java/jobshop/solvers/neighborhood/Nowicki.java b/src/main/java/jobshop/solvers/neighborhood/Nowicki.java index f0d4636..528583e 100644 --- a/src/main/java/jobshop/solvers/neighborhood/Nowicki.java +++ b/src/main/java/jobshop/solvers/neighborhood/Nowicki.java @@ -70,7 +70,7 @@ public class Nowicki extends Neighborhood { public final int t2; /** Creates a new swap of two tasks. */ - Swap(int machine, int t1, int t2) { + public Swap(int machine, int t1, int t2) { this.machine = machine; this.t1 = t1; this.t2 = t2; @@ -88,6 +88,38 @@ public class Nowicki extends Neighborhood { public void undoApplyOn(ResourceOrder current) { current.swapTasks(machine, t1, t2); } + + public int generateKey(int nbJobs) { + int myT1; + int myT2; + if (t2 < t1) { + myT1 = t2; + myT2 = t1; + } else { + myT1 = t1; + myT2 = t2; + } + int a = (((nbJobs - 1)*nbJobs*(machine + 1))/2); + int b = (((nbJobs-1-myT1)*(nbJobs-myT1))/2); + int c = (myT2 - myT1 - 1); + //System.out.println(a + " " + b + " " + c); + return a - b + c; + } + + public static int getNbKey(int nbMachines, int nbJobs) { + return ((nbJobs - 1)*nbJobs*nbMachines)/2; + } + + @Override + public boolean equals (Object o) { + Swap obj = (Swap) o; + return (machine == obj.machine && ((t1 == obj.t1 && t2 == obj.t2) || (t1 == obj.t2 && t2 == obj.t1 ))); + } + + @Override + public Object getChange() { + return this; + } } diff --git a/src/test/java/jobshop/swap/SwapTest.java b/src/test/java/jobshop/swap/SwapTest.java new file mode 100644 index 0000000..f4d09a7 --- /dev/null +++ b/src/test/java/jobshop/swap/SwapTest.java @@ -0,0 +1,26 @@ +package jobshop.swap; + +import jobshop.solvers.neighborhood.Nowicki; +import org.junit.Test; + + +public class SwapTest { + @Test + public void test() { + int nbJobs = 5; + int nbMachine = 8; + int k = -1; + for (int machine = 0; machine