Added descent solver and multi descend solver

This commit is contained in:
alejeune 2023-03-21 10:41:54 +01:00
parent 51c87b72e6
commit 1fb6428694
7 changed files with 148 additions and 5 deletions

View file

@ -118,7 +118,11 @@ public class Main {
Instance instance = Instance.fromFile(path);
// print some general statistics on the instance
output.println();
output.println();
output.println("\u001b[36m" + "------------------------------------------------------------------------" + "\u001b[0m");
output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
output.println();
// run all selected solvers on the instance and print the results
for(int solverId = 0 ; solverId < solvers.size() ; solverId++) {

View file

@ -17,6 +17,10 @@ public final class ResourceOrder extends Encoding {
// for each machine, indicate how many tasks have been initialized
final int[] nextFreeSlot;
public Task[][] getTasksByMachine() {
return tasksByMachine;
}
/** Creates a new empty resource order. */
public ResourceOrder(Instance instance)
{

View file

@ -1,9 +1,15 @@
package jobshop.solvers;
import jobshop.Instance;
import jobshop.encodings.ResourceOrder;
import jobshop.encodings.Schedule;
import jobshop.encodings.Task;
import jobshop.solvers.neighborhood.Neighborhood;
import jobshop.solvers.neighborhood.Nowicki;
import java.time.format.ResolverStyle;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
/** An empty shell to implement a descent solver. */
@ -24,7 +30,35 @@ public class DescentSolver implements Solver {
@Override
public Optional<Schedule> solve(Instance instance, long deadline, int randomness, int randomRunNumber) {
throw new UnsupportedOperationException();
Schedule schedule = baseSolver.solve(instance, deadline,randomness,randomRunNumber).get();
ResourceOrder order = new ResourceOrder(schedule);
ResourceOrder result = order;
Boolean bestFound = false;
Integer best = Integer.MAX_VALUE;
while(!bestFound) {
List<ResourceOrder> neighbours = neighborhood.generateNeighbors(order);
try {
Integer finalBest = 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())) // takes the best
.toArray(ResourceOrder[]::new)[0];
result = order;
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
bestFound = true;
}
}
return result.toSchedule();
}
// used to compute the number of neighbours seen
private int getNumberOfNeighbours(List<ResourceOrder> neighbours) {
return neighbours.stream()
.filter(e -> e.toSchedule().get().isValid()) // removes invalid solutions
.toArray(ResourceOrder[]::new).length;
}
}

View file

@ -0,0 +1,41 @@
package jobshop.solvers;
import jobshop.Instance;
import jobshop.encodings.Schedule;
import jobshop.solvers.neighborhood.Neighborhood;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class DescentSolverMultiStart implements Solver {
final Neighborhood neighborhood;
final List<Solver> solvers = List.of(new GreedySolver(GreedySolver.Priority.SPT),
new GreedySolver(GreedySolver.Priority.LRPT),
new GreedySolver(GreedySolver.Priority.EST_SPT),
new GreedySolver(GreedySolver.Priority.EST_LRPT));
public DescentSolverMultiStart(Neighborhood neighborhood) {
this.neighborhood = neighborhood;
}
@Override
public Optional<Schedule> solve(Instance instance, long deadline, int randomness, int randomRunNumber){
Integer best = Integer.MAX_VALUE;
Schedule bestSchedule = null;
// we try all greedy solvers for the starting solution
for (Solver s : solvers){
System.out.println();
System.out.println("\u001b[36m" + "Trying with solver : " + s.toString() + "\u001b[0m");
DescentSolver descentSolver = new DescentSolver(neighborhood, s);
Schedule solution = descentSolver.solve(instance,deadline,randomness,randomRunNumber).get();
if (solution.makespan() < best){
best = solution.makespan();
bestSchedule = solution;
}
}
return Optional.of(bestSchedule);
}
}

View file

@ -24,6 +24,13 @@ public class GreedySolver implements Solver {
}
@Override
public String toString() {
return "GreedySolver{" +
"priority=" + priority +
'}';
}
private Integer heuristiqueSPT(Task t, Instance instance){
return instance.duration(t);
}

View file

@ -2,6 +2,7 @@ package jobshop.solvers;
import jobshop.Instance;
import jobshop.encodings.Schedule;
import jobshop.solvers.neighborhood.Nowicki;
import java.util.Optional;
@ -25,7 +26,9 @@ public interface Solver {
case "lrpt": return new GreedySolver(GreedySolver.Priority.LRPT);
case "est_spt": return new GreedySolver(GreedySolver.Priority.EST_SPT);
case "est_lrpt": return new GreedySolver(GreedySolver.Priority.EST_LRPT);
// TODO: add new solvers
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());
default: throw new RuntimeException("Unknown solver: "+ name);
}
}

View file

@ -1,8 +1,12 @@
package jobshop.solvers.neighborhood;
import jobshop.Instance;
import jobshop.encodings.ResourceOrder;
import jobshop.encodings.Schedule;
import jobshop.encodings.Task;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@ -87,7 +91,9 @@ public class Nowicki extends Neighborhood {
* The original ResourceOrder MUST NOT be modified by this operation.
*/
public ResourceOrder generateFrom(ResourceOrder original) {
throw new UnsupportedOperationException();
ResourceOrder result = original.copy();
result.swapTasks(machine,t1,t2);
return result;
}
@Override
@ -126,12 +132,56 @@ public class Nowicki extends Neighborhood {
/** Returns a list of all the blocks of the critical path. */
List<Block> blocksOfCriticalPath(ResourceOrder order) {
throw new UnsupportedOperationException();
ArrayList<Block> blocks = new ArrayList<>();
Schedule schedule = order.toSchedule().get();
List<Task> lTask = schedule.criticalPath();
Instance instance = order.instance;
// order in which a machine does the tasks
Task[][] tasksByMachine = order.getTasksByMachine();
int currentMachine = -1;
Task firstTask = lTask.get(0);
Task lastTask = lTask.get(0);
for (Task task: lTask){
// while the machine is the same we update the last task
if (currentMachine == instance.machine(task)){
lastTask = task;
} else {
// then we add a new block
if (!firstTask.equals(lastTask)){
blocks.add(new Block(currentMachine,
Arrays.asList(tasksByMachine[currentMachine]).indexOf(firstTask),
Arrays.asList(tasksByMachine[currentMachine]).indexOf(lastTask)
));
}
// we update the pointers to the first and last tasks
firstTask = task;
lastTask = task;
currentMachine = instance.machine(task);
}
}
return blocks;
}
/** 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<>();
// case 2 tasks : 1 neighbour
if (block.firstTask - block.lastTask == 1){
swaps.add(new Swap(block.machine, block.firstTask, block.lastTask));
} else {
// case >2 tasks : 2 neighbours
if (block.firstTask != block.lastTask) {
swaps.add(new Swap(block.machine, block.firstTask, block.firstTask + 1));
swaps.add(new Swap(block.machine, block.lastTask -1, block.lastTask));
} else {
throw new RuntimeException("block with only one task");
}
}
return swaps;
}
}