Added descent solver and multi descend solver
This commit is contained in:
parent
51c87b72e6
commit
1fb6428694
7 changed files with 148 additions and 5 deletions
|
@ -118,7 +118,11 @@ public class Main {
|
||||||
Instance instance = Instance.fromFile(path);
|
Instance instance = Instance.fromFile(path);
|
||||||
|
|
||||||
// print some general statistics on the instance
|
// 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.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
|
||||||
|
output.println();
|
||||||
|
|
||||||
// run all selected solvers on the instance and print the results
|
// run all selected solvers on the instance and print the results
|
||||||
for(int solverId = 0 ; solverId < solvers.size() ; solverId++) {
|
for(int solverId = 0 ; solverId < solvers.size() ; solverId++) {
|
||||||
|
|
|
@ -17,6 +17,10 @@ public final class ResourceOrder extends Encoding {
|
||||||
// for each machine, indicate how many tasks have been initialized
|
// for each machine, indicate how many tasks have been initialized
|
||||||
final int[] nextFreeSlot;
|
final int[] nextFreeSlot;
|
||||||
|
|
||||||
|
public Task[][] getTasksByMachine() {
|
||||||
|
return tasksByMachine;
|
||||||
|
}
|
||||||
|
|
||||||
/** Creates a new empty resource order. */
|
/** Creates a new empty resource order. */
|
||||||
public ResourceOrder(Instance instance)
|
public ResourceOrder(Instance instance)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
package jobshop.solvers;
|
package jobshop.solvers;
|
||||||
|
|
||||||
import jobshop.Instance;
|
import jobshop.Instance;
|
||||||
|
import jobshop.encodings.ResourceOrder;
|
||||||
import jobshop.encodings.Schedule;
|
import jobshop.encodings.Schedule;
|
||||||
|
import jobshop.encodings.Task;
|
||||||
import jobshop.solvers.neighborhood.Neighborhood;
|
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;
|
import java.util.Optional;
|
||||||
|
|
||||||
/** An empty shell to implement a descent solver. */
|
/** An empty shell to implement a descent solver. */
|
||||||
|
@ -24,7 +30,35 @@ public class DescentSolver implements Solver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Schedule> solve(Instance instance, long deadline, int randomness, int randomRunNumber) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
41
src/main/java/jobshop/solvers/DescentSolverMultiStart.java
Normal file
41
src/main/java/jobshop/solvers/DescentSolverMultiStart.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,13 @@ public class GreedySolver implements Solver {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "GreedySolver{" +
|
||||||
|
"priority=" + priority +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
private Integer heuristiqueSPT(Task t, Instance instance){
|
private Integer heuristiqueSPT(Task t, Instance instance){
|
||||||
return instance.duration(t);
|
return instance.duration(t);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package jobshop.solvers;
|
||||||
|
|
||||||
import jobshop.Instance;
|
import jobshop.Instance;
|
||||||
import jobshop.encodings.Schedule;
|
import jobshop.encodings.Schedule;
|
||||||
|
import jobshop.solvers.neighborhood.Nowicki;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -25,7 +26,9 @@ public interface Solver {
|
||||||
case "lrpt": return new GreedySolver(GreedySolver.Priority.LRPT);
|
case "lrpt": return new GreedySolver(GreedySolver.Priority.LRPT);
|
||||||
case "est_spt": return new GreedySolver(GreedySolver.Priority.EST_SPT);
|
case "est_spt": return new GreedySolver(GreedySolver.Priority.EST_SPT);
|
||||||
case "est_lrpt": return new GreedySolver(GreedySolver.Priority.EST_LRPT);
|
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);
|
default: throw new RuntimeException("Unknown solver: "+ name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package jobshop.solvers.neighborhood;
|
package jobshop.solvers.neighborhood;
|
||||||
|
|
||||||
|
import jobshop.Instance;
|
||||||
import jobshop.encodings.ResourceOrder;
|
import jobshop.encodings.ResourceOrder;
|
||||||
|
import jobshop.encodings.Schedule;
|
||||||
|
import jobshop.encodings.Task;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -87,7 +91,9 @@ public class Nowicki extends Neighborhood {
|
||||||
* The original ResourceOrder MUST NOT be modified by this operation.
|
* The original ResourceOrder MUST NOT be modified by this operation.
|
||||||
*/
|
*/
|
||||||
public ResourceOrder generateFrom(ResourceOrder original) {
|
public ResourceOrder generateFrom(ResourceOrder original) {
|
||||||
throw new UnsupportedOperationException();
|
ResourceOrder result = original.copy();
|
||||||
|
result.swapTasks(machine,t1,t2);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -126,12 +132,56 @@ public class Nowicki extends Neighborhood {
|
||||||
|
|
||||||
/** 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> 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 */
|
/** 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<>();
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue