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

View file

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

View file

@ -1,8 +1,10 @@
package jobshop.solvers.neighborhood;
import jobshop.encodings.ResourceOrder;
import jobshop.encodings.Task;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/** 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. */
@Override
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. */
@Override
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. */
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 */
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());
}
}