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);
|
||||
|
||||
// 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++) {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
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){
|
||||
return instance.duration(t);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue