Descente OK

This commit is contained in:
Paul Faure 2021-05-11 12:08:46 +02:00
parent d97d8141a1
commit 2824abf3a5
6 changed files with 327 additions and 42 deletions

View file

@ -72,6 +72,22 @@ public final class ResourceOrder extends Encoding {
return tasksByMachine[machine][taskIndex]; return tasksByMachine[machine][taskIndex];
} }
/** Returns the index of a particular task scheduled on a particular machine.
*
* @param machine Machine on which the task to retrieve is scheduled.
* @param task Task in the queue for this machine.
* @return The index of the task scheduled on a machine.
*/
public int getIndexOfTaskOnMachine(int machine, Task task) {
int i = 0;
boolean found = false;
while (i<instance.numJobs && !found) {
found = getTaskOfMachine(machine, i).equals(task);
i++;
}
return i - 1;
}
/** Exchange the order of two tasks that are scheduled on a given machine. /** Exchange the order of two tasks that are scheduled on a given machine.
* *
* @param machine Machine on which the two tasks appear (line on which to perform the exchange) * @param machine Machine on which the two tasks appear (line on which to perform the exchange)

View file

@ -3,7 +3,17 @@ package jobshop.solvers;
import jobshop.Instance; import jobshop.Instance;
import jobshop.Result; import jobshop.Result;
import jobshop.encodings.ResourceOrder; import jobshop.encodings.ResourceOrder;
import jobshop.encodings.Schedule;
import jobshop.solvers.neighborhood.Neighbor;
import jobshop.solvers.neighborhood.Neighborhood; 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. */ /** An empty shell to implement a descent solver. */
public class DescentSolver implements Solver { public class DescentSolver implements Solver {
@ -23,7 +33,39 @@ public class DescentSolver implements Solver {
@Override @Override
public Result solve(Instance instance, long deadline) { public Result solve(Instance instance, long deadline) {
throw new UnsupportedOperationException(); Schedule schedule = baseSolver.solve(instance, deadline).schedule.get();
ResourceOrder resourceOrder = new ResourceOrder(schedule);
boolean ended = false;
int bestMakespan;
Neighbor<ResourceOrder> bestNeighbor;
while (!ended) {
schedule = resourceOrder.toSchedule().get();
bestMakespan = schedule.makespan();
bestNeighbor = null;
List<Neighbor<ResourceOrder>> generatedNeighbors = neighborhood.generateNeighbors(resourceOrder);
Iterator<Neighbor<ResourceOrder>> iter = generatedNeighbors.iterator();
while (iter.hasNext()) {
Neighbor<ResourceOrder> neighbor = iter.next();
neighbor.applyOn(resourceOrder);
try {
Schedule currentSchedule = resourceOrder.toSchedule().get();
int currentMakespan = currentSchedule.makespan();
if (currentMakespan < bestMakespan) {
bestMakespan = currentMakespan;
bestNeighbor = neighbor;
}
} catch (Exception e) {
}
neighbor.undoApplyOn(resourceOrder);
}
if (bestNeighbor == null) {
ended = true;
} else {
bestNeighbor.applyOn(resourceOrder);
}
}
return new Result(instance, resourceOrder.toSchedule(), Result.ExitCause.ProvedOptimal);
} }
} }

View file

@ -23,6 +23,9 @@ public class GreedySolver implements Solver {
final Priority priority; final Priority priority;
final ArrayList<Task> tachesRestantes; final ArrayList<Task> tachesRestantes;
int[] machineFreeDate;
int[] jobFreeDate;
/** Creates a new greedy solver that will use the given priority. */ /** Creates a new greedy solver that will use the given priority. */
@ -31,8 +34,12 @@ public class GreedySolver implements Solver {
this.tachesRestantes = new ArrayList<>(); this.tachesRestantes = new ArrayList<>();
} }
private int getEarliestDate(Instance instance, Task task) {
return Math.max(machineFreeDate[instance.machine(task)], jobFreeDate[task.job]);
}
/** return true if t1 est plus prioritaire que t1 **/ /** return true if t1 est plus prioritaire que t1 **/
private boolean prioritaire(Instance instance, Task t1, Task t2) throws Exception { private boolean prioritaire(Instance instance, Task t1, Task t2) {
boolean rt = false; boolean rt = false;
switch(priority) { switch(priority) {
case SPT: case SPT:
@ -65,51 +72,107 @@ public class GreedySolver implements Solver {
rt = sigmaT1 >= sigmaT2; rt = sigmaT1 >= sigmaT2;
break; break;
case EST_SPT: case EST_SPT:
throw new Exception("UNKNOWN PRIORITY"); if (getEarliestDate(instance, t1) < getEarliestDate(instance, t2)) {
//break; rt = true;
} else if (getEarliestDate(instance, t1) > getEarliestDate(instance, t2)) {
rt = false;
} else {
rt = instance.duration(t1) <= instance.duration(t2);
}
break;
case EST_LPT: case EST_LPT:
throw new Exception("UNKNOWN PRIORITY"); if (getEarliestDate(instance, t1) < getEarliestDate(instance, t2)) {
//break; rt = true;
} else if (getEarliestDate(instance, t1) > getEarliestDate(instance, t2)) {
rt = false;
} else {
rt = instance.duration(t1) >= instance.duration(t2);
}
break;
case EST_SRPT: case EST_SRPT:
throw new Exception("UNKNOWN PRIORITY"); if (getEarliestDate(instance, t1) < getEarliestDate(instance, t2)) {
//break; rt = true;
} else if (getEarliestDate(instance, t1) > getEarliestDate(instance, t2)) {
rt = false;
} else {
sigmaT1 = 0;
sigmaT2 = 0;
for (i=t1.task; i<instance.numTasks; i++) {
sigmaT1 += instance.duration(t1.job, i);
}
for (i=t2.task; i<instance.numTasks; i++) {
sigmaT2 += instance.duration(t2.job, i);
}
rt = sigmaT1 <= sigmaT2;
}
break;
case EST_LRPT: case EST_LRPT:
throw new Exception("UNKNOWN PRIORITY"); if (getEarliestDate(instance, t1) < getEarliestDate(instance, t2)) {
//break; rt = true;
} else if (getEarliestDate(instance, t1) > getEarliestDate(instance, t2)) {
rt = false;
} else {
sigmaT1 = 0;
sigmaT2 = 0;
for (i=t1.task; i<instance.numTasks; i++) {
sigmaT1 += instance.duration(t1.job, i);
}
for (i=t2.task; i<instance.numTasks; i++) {
sigmaT2 += instance.duration(t2.job, i);
}
rt = sigmaT1 >= sigmaT2;
}
break;
} }
return rt; return rt;
} }
private void addTask(Instance instance, Task task) throws Exception { private void addTask(Instance instance, Task task) {
Iterator<Task> iter = this.tachesRestantes.iterator(); if (priority == Priority.SPT || priority == Priority.LPT || priority == Priority.SRPT || priority == Priority.LRPT) {
int index = 0; Iterator<Task> iter = this.tachesRestantes.iterator();
boolean trouve = false; int index = 0;
while (iter.hasNext() && !trouve) { boolean trouve = false;
Task current = iter.next(); while (iter.hasNext() && !trouve) {
if (this.prioritaire(instance, task, current)) { Task current = iter.next();
trouve = true; if (this.prioritaire(instance, task, current)) {
} else { trouve = true;
index++; } else {
index++;
}
} }
this.tachesRestantes.add(index, task);
} else {
this.tachesRestantes.add(task);
} }
this.tachesRestantes.add(index, task);
} }
private Task getTask() { private Task getTask(Instance instance) {
return this.tachesRestantes.remove(0); if (priority == Priority.SPT || priority == Priority.LPT || priority == Priority.SRPT || priority == Priority.LRPT) {
return this.tachesRestantes.remove(0);
} else {
int indexBest = 0;
int indexChallenger;
for (indexChallenger = 1; indexChallenger < tachesRestantes.size(); indexChallenger++) {
if (prioritaire(instance, tachesRestantes.get(indexChallenger), tachesRestantes.get(indexBest))) {
indexBest = indexChallenger;
}
}
return this.tachesRestantes.remove(indexBest);
}
} }
@Override @Override
public Result solve(Instance instance, long deadline) { public Result solve(Instance instance, long deadline) {
//Initialisation //Initialisation
jobFreeDate = new int[instance.numJobs];
machineFreeDate = new int[instance.numMachines];
int i; int i;
for (i=0; i<instance.numJobs; i++) { for (i=0; i<instance.numJobs; i++) {
try { this.addTask(instance, new Task(i,0));
this.addTask(instance, new Task(i,0)); this.jobFreeDate[i] = 0;
} catch (Exception e) { }
System.out.println("ERREUR POLITIQUE DE PRIORITE INCONNUE"); for (i=0; i<instance.numMachines; i++) {
System.exit(2); this.machineFreeDate[i] = 0;
}
} }
ResourceOrder resourceOrder = new ResourceOrder(instance); ResourceOrder resourceOrder = new ResourceOrder(instance);
Task task; Task task;
@ -117,16 +180,14 @@ public class GreedySolver implements Solver {
//Itérations //Itérations
while (!this.tachesRestantes.isEmpty()) { while (!this.tachesRestantes.isEmpty()) {
task = this.getTask(); task = this.getTask(instance);
int startTime = getEarliestDate(instance, task);
resourceOrder.addTaskToMachine(instance.machine(task), task); resourceOrder.addTaskToMachine(instance.machine(task), task);
this.machineFreeDate[instance.machine(task)] = startTime + instance.duration(task);
this.jobFreeDate[task.job] = startTime + instance.duration(task);
nextTask = instance.nextTask(task); nextTask = instance.nextTask(task);
if (nextTask != null) { if (nextTask != null) {
try { this.addTask(instance, new Task(task.job, task.task + 1));
this.addTask(instance, new Task(task.job, task.task + 1));
} catch (Exception e) {
System.out.println("ERREUR POLITIQUE DE PRIORITE INCONNUE");
System.exit(2);
}
} }
} }
return new Result (instance, resourceOrder.toSchedule(), ProvedOptimal); return new Result (instance, resourceOrder.toSchedule(), ProvedOptimal);

View file

@ -2,6 +2,8 @@ package jobshop.solvers;
import jobshop.Instance; import jobshop.Instance;
import jobshop.Result; import jobshop.Result;
import jobshop.encodings.ResourceOrder;
import jobshop.solvers.neighborhood.Nowicki;
/** Common interface that must implemented by all solvers. */ /** Common interface that must implemented by all solvers. */
public interface Solver { public interface Solver {
@ -28,6 +30,14 @@ public interface Solver {
case "est_lpt": return new GreedySolver(GreedySolver.Priority.EST_LPT); case "est_lpt": return new GreedySolver(GreedySolver.Priority.EST_LPT);
case "est_srpt": return new GreedySolver(GreedySolver.Priority.EST_SRPT); case "est_srpt": return new GreedySolver(GreedySolver.Priority.EST_SRPT);
case "est_lrpt": return new GreedySolver(GreedySolver.Priority.EST_LRPT); case "est_lrpt": return new GreedySolver(GreedySolver.Priority.EST_LRPT);
case "descent_spt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.SPT));
case "descent_lpt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.LPT));
case "descent_srpt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.SRPT));
case "descent_lrpt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.LRPT));
case "descent_est_spt": return new DescentSolver(new Nowicki(), new GreedySolver(GreedySolver.Priority.EST_SPT));
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));
// TODO: add new solvers // TODO: add new solvers
default: throw new RuntimeException("Unknown solver: "+ name); default: throw new RuntimeException("Unknown solver: "+ name);
} }

View file

@ -1,8 +1,10 @@
package jobshop.solvers.neighborhood; package jobshop.solvers.neighborhood;
import jobshop.encodings.ResourceOrder; import jobshop.encodings.ResourceOrder;
import jobshop.encodings.Task;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
/** Implementation of the Nowicki and Smutnicki neighborhood. /** Implementation of the Nowicki and Smutnicki neighborhood.
@ -78,13 +80,13 @@ public class Nowicki extends Neighborhood<ResourceOrder> {
/** Apply this swap on the given ResourceOrder, transforming it into a new solution. */ /** Apply this swap on the given ResourceOrder, transforming it into a new solution. */
@Override @Override
public void applyOn(ResourceOrder current) { public void applyOn(ResourceOrder current) {
throw new UnsupportedOperationException(); current.swapTasks(machine, t1, t2);
} }
/** Unapply this swap on the neighbor, transforming it back into the original solution. */ /** Unapply this swap on the neighbor, transforming it back into the original solution. */
@Override @Override
public void undoApplyOn(ResourceOrder current) { public void undoApplyOn(ResourceOrder current) {
throw new UnsupportedOperationException(); current.swapTasks(machine, t1, t2);
} }
} }
@ -109,13 +111,50 @@ public class Nowicki extends Neighborhood<ResourceOrder> {
/** Returns a list of all the blocks of the critical path. */ /** Returns a list of all the blocks of the critical path. */
List<Block> blocksOfCriticalPath(ResourceOrder order) { List<Block> blocksOfCriticalPath(ResourceOrder order) {
throw new UnsupportedOperationException(); ArrayList<Block> results = new ArrayList<>();
List<Task> criticalPath = order.toSchedule().get().criticalPath();
int currentMachine = -1;
int currentFirstIndex = -1;
int currentLastIndex = -1;
Iterator<Task> iter = criticalPath.iterator();
Task currentTask;
if (iter.hasNext()) {
currentTask = iter.next();
currentMachine = order.instance.machine(currentTask);
currentFirstIndex = order.getIndexOfTaskOnMachine(currentMachine, currentTask);
currentLastIndex = currentFirstIndex;
}
while (iter.hasNext()) {
currentTask = iter.next();
if (currentMachine == order.instance.machine(currentTask)) {
currentLastIndex = order.getIndexOfTaskOnMachine(currentMachine, currentTask);
} else {
if (currentLastIndex > currentFirstIndex) {
results.add(new Block(currentMachine, currentFirstIndex, currentLastIndex));
}
currentMachine = order.instance.machine(currentTask);
currentFirstIndex = order.getIndexOfTaskOnMachine(currentMachine, currentTask);
currentLastIndex = currentFirstIndex;
}
}
if (currentLastIndex > currentFirstIndex) {
results.add(new Block(currentMachine, currentFirstIndex, currentLastIndex));
}
return results;
} }
/** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */ /** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */
List<Swap> neighbors(Block block) { List<Swap> neighbors(Block block) {
throw new UnsupportedOperationException(); ArrayList<Swap> swaps = new ArrayList<>();
if (block.lastTask - block.firstTask == 1) {
swaps.add(new Swap(block.machine, block.firstTask, block.lastTask));
} else {
swaps.add(new Swap(block.machine, block.firstTask, block.firstTask + 1));
swaps.add(new Swap(block.machine, block.lastTask - 1, block.lastTask));
}
return swaps;
} }
} }

View file

@ -116,4 +116,121 @@ public class GreedySolverTest {
} }
} }
} }
@Test
public void testEST_SPT() {
List<String> instancesNames = BestKnownResults.instancesMatching("la");
instancesNames.addAll(BestKnownResults.instancesMatching("ft"));
List<Instance> instances = instancesNames.stream().map(name -> {
Instance instance = null;
try {
instance = Instance.fromFile(Paths.get("instances/", name));
} catch (IOException e) {
e.printStackTrace();
}
return instance;
}).collect(Collectors.toList());
GreedySolver solver = new GreedySolver(EST_SPT);
Result result;
for (int i = 0; i < instances.size(); i++) {
result = solver.solve(instances.get(i), 100000);
assert result.schedule.get().isValid();
if (BestKnownResults.isKnown(instancesNames.get(i))) {
assert result.schedule.get().makespan() >= BestKnownResults.of(instancesNames.get(i));
}
}
}
@Test
public void testEST_LPT() throws IOException {
List<String> instancesNames = BestKnownResults.instancesMatching("la");
instancesNames.addAll(BestKnownResults.instancesMatching("ft"));
List<Instance> instances = instancesNames.stream().map(name -> {
Instance instance = null;
try {
instance = Instance.fromFile(Paths.get("instances/", name));
} catch (IOException e) {
e.printStackTrace();
}
return instance;
}).collect(Collectors.toList());
GreedySolver solver = new GreedySolver(EST_LPT);
Result result;
for (int i = 0; i < instances.size(); i++) {
result = solver.solve(instances.get(i), 100000);
assert result.schedule.get().isValid();
if (BestKnownResults.isKnown(instancesNames.get(i))) {
assert result.schedule.get().makespan() >= BestKnownResults.of(instancesNames.get(i));
}
}
}
@Test
public void testEST_SRPT() throws IOException {
List<String> instancesNames = BestKnownResults.instancesMatching("la");
instancesNames.addAll(BestKnownResults.instancesMatching("ft"));
List<Instance> instances = instancesNames.stream().map(name -> {
Instance instance = null;
try {
instance = Instance.fromFile(Paths.get("instances/", name));
} catch (IOException e) {
e.printStackTrace();
}
return instance;
}).collect(Collectors.toList());
GreedySolver solver = new GreedySolver(EST_SRPT);
Result result;
for (int i = 0; i < instances.size(); i++) {
result = solver.solve(instances.get(i), 100000);
assert result.schedule.get().isValid();
if (BestKnownResults.isKnown(instancesNames.get(i))) {
assert result.schedule.get().makespan() >= BestKnownResults.of(instancesNames.get(i));
}
}
}
@Test
public void testEST_LRPT() throws IOException {
List<String> instancesNames = BestKnownResults.instancesMatching("la");
instancesNames.addAll(BestKnownResults.instancesMatching("ft"));
List<Instance> instances = instancesNames.stream().map(name -> {
Instance instance = null;
try {
instance = Instance.fromFile(Paths.get("instances/", name));
} catch (IOException e) {
e.printStackTrace();
}
return instance;
}).collect(Collectors.toList());
GreedySolver solver = new GreedySolver(EST_LRPT);
Result result;
for (int i = 0; i < instances.size(); i++) {
result = solver.solve(instances.get(i), 100000);
assert result.schedule.get().isValid();
if (BestKnownResults.isKnown(instancesNames.get(i))) {
assert result.schedule.get().makespan() >= BestKnownResults.of(instancesNames.get(i));
}
}
}
@Test
public void testSusucre() throws IOException {
Instance instance = Instance.fromFile(Paths.get("instances/aaa3"));
GreedySolver solver = new GreedySolver(EST_LRPT);
Result result = solver.solve(instance, 100000);
System.out.println("SCHEDULE EST_LRPT: " + result.schedule.get().toString());
solver = new GreedySolver(EST_SPT);
result = solver.solve(instance, 100000);
System.out.println("SCHEDULE EST_SPT: " + result.schedule.get().toString());
}
} }