Greedy Solver completed
This commit is contained in:
parent
3b67200387
commit
6f78309d10
16 changed files with 1372 additions and 1126 deletions
|
@ -1,189 +1,180 @@
|
|||
package jobshop;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BestKnownResult {
|
||||
|
||||
public static boolean isKnown(String instanceName) {
|
||||
return bests.containsKey(instanceName);
|
||||
}
|
||||
|
||||
public static List<String> instancesMatching(String namePrefix) {
|
||||
return Arrays.stream(instances)
|
||||
.filter(i -> i.startsWith(namePrefix))
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static int of(String instanceName) {
|
||||
if(!bests.containsKey(instanceName)) {
|
||||
throw new RuntimeException("Unknown best result for "+instanceName);
|
||||
}
|
||||
return bests.get(instanceName);
|
||||
}
|
||||
|
||||
static private HashMap<String, Integer> bests;
|
||||
static String[] instances;
|
||||
static {
|
||||
bests = new HashMap<>();
|
||||
bests.put("aaa1", 11);
|
||||
bests.put("abz5", 1234);
|
||||
bests.put("abz6", 943);
|
||||
bests.put("abz7", 656);
|
||||
bests.put("abz8", 665);
|
||||
bests.put("abz9", 679);
|
||||
bests.put("ft06", 55);
|
||||
bests.put("ft10", 930);
|
||||
bests.put("ft20", 1165);
|
||||
bests.put("la01", 666);
|
||||
bests.put("la02", 655);
|
||||
bests.put("la03", 597);
|
||||
bests.put("la04", 590);
|
||||
bests.put("la05", 593);
|
||||
bests.put("la06", 926);
|
||||
bests.put("la07", 890);
|
||||
bests.put("la08", 863);
|
||||
bests.put("la09", 951);
|
||||
bests.put("la10", 958);
|
||||
bests.put("la11", 1222);
|
||||
bests.put("la12", 1039);
|
||||
bests.put("la13", 1150);
|
||||
bests.put("la14", 1292);
|
||||
bests.put("la15", 1207);
|
||||
bests.put("la16", 945);
|
||||
bests.put("la17", 784);
|
||||
bests.put("la18", 848);
|
||||
bests.put("la19", 842);
|
||||
bests.put("la20", 902);
|
||||
bests.put("la21", 1046);
|
||||
bests.put("la22", 927);
|
||||
bests.put("la23", 1032);
|
||||
bests.put("la24", 935);
|
||||
bests.put("la25", 977);
|
||||
bests.put("la26", 1218);
|
||||
bests.put("la27", 1235);
|
||||
bests.put("la28", 1216);
|
||||
bests.put("la29", 1152);
|
||||
bests.put("la30", 1355);
|
||||
bests.put("la31", 1784);
|
||||
bests.put("la32", 1850);
|
||||
bests.put("la33", 1719);
|
||||
bests.put("la34", 1721);
|
||||
bests.put("la35", 1888);
|
||||
bests.put("la36", 1268);
|
||||
bests.put("la37", 1397);
|
||||
bests.put("la38", 1196);
|
||||
bests.put("la39", 1233);
|
||||
bests.put("la40", 1222);
|
||||
bests.put("orb01", 1059);
|
||||
bests.put("orb02", 888);
|
||||
bests.put("orb03", 1005);
|
||||
bests.put("orb04", 1005);
|
||||
bests.put("orb05", 887);
|
||||
bests.put("orb06", 1010);
|
||||
bests.put("orb07", 397);
|
||||
bests.put("orb08", 899);
|
||||
bests.put("orb09", 934);
|
||||
bests.put("orb10", 944);
|
||||
bests.put("swv01", 1407);
|
||||
bests.put("swv02", 1475);
|
||||
bests.put("swv03", 1398);
|
||||
bests.put("swv04", 1474);
|
||||
bests.put("swv05", 1424);
|
||||
bests.put("swv06", 1678);
|
||||
bests.put("swv07", 1600);
|
||||
bests.put("swv08", 1763);
|
||||
bests.put("swv09", 1661);
|
||||
bests.put("swv10", 1767);
|
||||
bests.put("swv11", 2991);
|
||||
bests.put("swv12", 3003);
|
||||
bests.put("swv13", 3104);
|
||||
bests.put("swv14", 2968);
|
||||
bests.put("swv15", 2904);
|
||||
bests.put("swv16", 2924);
|
||||
bests.put("swv17", 2794);
|
||||
bests.put("swv18", 2852);
|
||||
bests.put("swv19", 2843);
|
||||
bests.put("swv20", 2823);
|
||||
bests.put("yn1", 885);
|
||||
bests.put("yn2", 909);
|
||||
bests.put("yn3", 892);
|
||||
bests.put("yn4", 968);
|
||||
bests.put("ta01", 1231);
|
||||
bests.put("ta02", 1244);
|
||||
bests.put("ta03", 1218);
|
||||
bests.put("ta04", 1175);
|
||||
bests.put("ta05", 1224);
|
||||
bests.put("ta06", 1238);
|
||||
bests.put("ta07", 1227);
|
||||
bests.put("ta08", 1217);
|
||||
bests.put("ta09", 1274);
|
||||
bests.put("ta10", 1241);
|
||||
bests.put("ta11", 1361);
|
||||
bests.put("ta12", 1367);
|
||||
bests.put("ta13", 1342);
|
||||
bests.put("ta14", 1345);
|
||||
bests.put("ta15", 1340);
|
||||
bests.put("ta16", 1360);
|
||||
bests.put("ta17", 1462);
|
||||
bests.put("ta18", 1396);
|
||||
bests.put("ta19", 1335);
|
||||
bests.put("ta20", 1351);
|
||||
bests.put("ta21", 1644);
|
||||
bests.put("ta22", 1600);
|
||||
bests.put("ta23", 1557);
|
||||
bests.put("ta24", 1647);
|
||||
bests.put("ta25", 1595);
|
||||
bests.put("ta26", 1645);
|
||||
bests.put("ta27", 1680);
|
||||
bests.put("ta28", 1614);
|
||||
bests.put("ta29", 1635);
|
||||
bests.put("ta30", 1584);
|
||||
bests.put("ta31", 1764);
|
||||
bests.put("ta32", 1796);
|
||||
bests.put("ta33", 1793);
|
||||
bests.put("ta34", 1829);
|
||||
bests.put("ta35", 2007);
|
||||
bests.put("ta36", 1819);
|
||||
bests.put("ta37", 1778);
|
||||
bests.put("ta38", 1673);
|
||||
bests.put("ta39", 1795);
|
||||
bests.put("ta40", 1674);
|
||||
bests.put("ta41", 2018);
|
||||
bests.put("ta42", 1956);
|
||||
bests.put("ta43", 1859);
|
||||
bests.put("ta44", 1984);
|
||||
bests.put("ta45", 2000);
|
||||
bests.put("ta46", 2021);
|
||||
bests.put("ta47", 1903);
|
||||
bests.put("ta48", 1952);
|
||||
bests.put("ta49", 1968);
|
||||
bests.put("ta50", 1926);
|
||||
bests.put("ta51", 2760);
|
||||
bests.put("ta52", 2756);
|
||||
bests.put("ta53", 2717);
|
||||
bests.put("ta54", 2839);
|
||||
bests.put("ta55", 2679);
|
||||
bests.put("ta56", 2781);
|
||||
bests.put("ta57", 2943);
|
||||
bests.put("ta58", 2885);
|
||||
bests.put("ta59", 2655);
|
||||
bests.put("ta60", 2723);
|
||||
bests.put("ta61", 2868);
|
||||
bests.put("ta62", 2869);
|
||||
bests.put("ta63", 2755);
|
||||
bests.put("ta64", 2702);
|
||||
bests.put("ta65", 2725);
|
||||
bests.put("ta66", 2845);
|
||||
bests.put("ta67", 2825);
|
||||
bests.put("ta68", 2784);
|
||||
bests.put("ta69", 3071);
|
||||
bests.put("ta70", 2995);
|
||||
instances = bests.keySet().toArray(new String[0]);
|
||||
Arrays.sort(instances);
|
||||
}
|
||||
|
||||
}
|
||||
package jobshop;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class BestKnownResult {
|
||||
|
||||
public static boolean isKnown(String instanceName) {
|
||||
return bests.containsKey(instanceName);
|
||||
}
|
||||
|
||||
public static int of(String instanceName) {
|
||||
if(!bests.containsKey(instanceName)) {
|
||||
throw new RuntimeException("Unknown best result for "+instanceName);
|
||||
}
|
||||
return bests.get(instanceName);
|
||||
}
|
||||
|
||||
static private HashMap<String, Integer> bests;
|
||||
static String[] instances;
|
||||
static {
|
||||
bests = new HashMap<>();
|
||||
bests.put("aaa1", 11);
|
||||
bests.put("abz5", 1234);
|
||||
bests.put("abz6", 943);
|
||||
bests.put("abz7", 656);
|
||||
bests.put("abz8", 665);
|
||||
bests.put("abz9", 679);
|
||||
bests.put("ft06", 55);
|
||||
bests.put("ft10", 930);
|
||||
bests.put("ft20", 1165);
|
||||
bests.put("la01", 666);
|
||||
bests.put("la02", 655);
|
||||
bests.put("la03", 597);
|
||||
bests.put("la04", 590);
|
||||
bests.put("la05", 593);
|
||||
bests.put("la06", 926);
|
||||
bests.put("la07", 890);
|
||||
bests.put("la08", 863);
|
||||
bests.put("la09", 951);
|
||||
bests.put("la10", 958);
|
||||
bests.put("la11", 1222);
|
||||
bests.put("la12", 1039);
|
||||
bests.put("la13", 1150);
|
||||
bests.put("la14", 1292);
|
||||
bests.put("la15", 1207);
|
||||
bests.put("la16", 945);
|
||||
bests.put("la17", 784);
|
||||
bests.put("la18", 848);
|
||||
bests.put("la19", 842);
|
||||
bests.put("la20", 902);
|
||||
bests.put("la21", 1046);
|
||||
bests.put("la22", 927);
|
||||
bests.put("la23", 1032);
|
||||
bests.put("la24", 935);
|
||||
bests.put("la25", 977);
|
||||
bests.put("la26", 1218);
|
||||
bests.put("la27", 1235);
|
||||
bests.put("la28", 1216);
|
||||
bests.put("la29", 1152);
|
||||
bests.put("la30", 1355);
|
||||
bests.put("la31", 1784);
|
||||
bests.put("la32", 1850);
|
||||
bests.put("la33", 1719);
|
||||
bests.put("la34", 1721);
|
||||
bests.put("la35", 1888);
|
||||
bests.put("la36", 1268);
|
||||
bests.put("la37", 1397);
|
||||
bests.put("la38", 1196);
|
||||
bests.put("la39", 1233);
|
||||
bests.put("la40", 1222);
|
||||
bests.put("orb01", 1059);
|
||||
bests.put("orb02", 888);
|
||||
bests.put("orb03", 1005);
|
||||
bests.put("orb04", 1005);
|
||||
bests.put("orb05", 887);
|
||||
bests.put("orb06", 1010);
|
||||
bests.put("orb07", 397);
|
||||
bests.put("orb08", 899);
|
||||
bests.put("orb09", 934);
|
||||
bests.put("orb10", 944);
|
||||
bests.put("swv01", 1407);
|
||||
bests.put("swv02", 1475);
|
||||
bests.put("swv03", 1398);
|
||||
bests.put("swv04", 1474);
|
||||
bests.put("swv05", 1424);
|
||||
bests.put("swv06", 1678);
|
||||
bests.put("swv07", 1600);
|
||||
bests.put("swv08", 1763);
|
||||
bests.put("swv09", 1661);
|
||||
bests.put("swv10", 1767);
|
||||
bests.put("swv11", 2991);
|
||||
bests.put("swv12", 3003);
|
||||
bests.put("swv13", 3104);
|
||||
bests.put("swv14", 2968);
|
||||
bests.put("swv15", 2904);
|
||||
bests.put("swv16", 2924);
|
||||
bests.put("swv17", 2794);
|
||||
bests.put("swv18", 2852);
|
||||
bests.put("swv19", 2843);
|
||||
bests.put("swv20", 2823);
|
||||
bests.put("yn1", 885);
|
||||
bests.put("yn2", 909);
|
||||
bests.put("yn3", 892);
|
||||
bests.put("yn4", 968);
|
||||
bests.put("ta01", 1231);
|
||||
bests.put("ta02", 1244);
|
||||
bests.put("ta03", 1218);
|
||||
bests.put("ta04", 1175);
|
||||
bests.put("ta05", 1224);
|
||||
bests.put("ta06", 1238);
|
||||
bests.put("ta07", 1227);
|
||||
bests.put("ta08", 1217);
|
||||
bests.put("ta09", 1274);
|
||||
bests.put("ta10", 1241);
|
||||
bests.put("ta11", 1361);
|
||||
bests.put("ta12", 1367);
|
||||
bests.put("ta13", 1342);
|
||||
bests.put("ta14", 1345);
|
||||
bests.put("ta15", 1340);
|
||||
bests.put("ta16", 1360);
|
||||
bests.put("ta17", 1462);
|
||||
bests.put("ta18", 1396);
|
||||
bests.put("ta19", 1335);
|
||||
bests.put("ta20", 1351);
|
||||
bests.put("ta21", 1644);
|
||||
bests.put("ta22", 1600);
|
||||
bests.put("ta23", 1557);
|
||||
bests.put("ta24", 1647);
|
||||
bests.put("ta25", 1595);
|
||||
bests.put("ta26", 1645);
|
||||
bests.put("ta27", 1680);
|
||||
bests.put("ta28", 1614);
|
||||
bests.put("ta29", 1635);
|
||||
bests.put("ta30", 1584);
|
||||
bests.put("ta31", 1764);
|
||||
bests.put("ta32", 1796);
|
||||
bests.put("ta33", 1793);
|
||||
bests.put("ta34", 1829);
|
||||
bests.put("ta35", 2007);
|
||||
bests.put("ta36", 1819);
|
||||
bests.put("ta37", 1778);
|
||||
bests.put("ta38", 1673);
|
||||
bests.put("ta39", 1795);
|
||||
bests.put("ta40", 1674);
|
||||
bests.put("ta41", 2018);
|
||||
bests.put("ta42", 1956);
|
||||
bests.put("ta43", 1859);
|
||||
bests.put("ta44", 1984);
|
||||
bests.put("ta45", 2000);
|
||||
bests.put("ta46", 2021);
|
||||
bests.put("ta47", 1903);
|
||||
bests.put("ta48", 1952);
|
||||
bests.put("ta49", 1968);
|
||||
bests.put("ta50", 1926);
|
||||
bests.put("ta51", 2760);
|
||||
bests.put("ta52", 2756);
|
||||
bests.put("ta53", 2717);
|
||||
bests.put("ta54", 2839);
|
||||
bests.put("ta55", 2679);
|
||||
bests.put("ta56", 2781);
|
||||
bests.put("ta57", 2943);
|
||||
bests.put("ta58", 2885);
|
||||
bests.put("ta59", 2655);
|
||||
bests.put("ta60", 2723);
|
||||
bests.put("ta61", 2868);
|
||||
bests.put("ta62", 2869);
|
||||
bests.put("ta63", 2755);
|
||||
bests.put("ta64", 2702);
|
||||
bests.put("ta65", 2725);
|
||||
bests.put("ta66", 2845);
|
||||
bests.put("ta67", 2825);
|
||||
bests.put("ta68", 2784);
|
||||
bests.put("ta69", 3071);
|
||||
bests.put("ta70", 2995);
|
||||
instances = bests.keySet().toArray(new String[0]);
|
||||
Arrays.sort(instances);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,42 +1,114 @@
|
|||
package jobshop;
|
||||
|
||||
import jobshop.encodings.JobNumbers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class DebuggingMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// load the aaa1 instance
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// construit une solution dans la représentation par
|
||||
// numéro de jobs : [0 1 1 0 0 1]
|
||||
// Note : cette solution a aussi été vue dans les exercices (section 3.3)
|
||||
// mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2]
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
System.out.println("\nENCODING: " + enc);
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
// TODO: make it print something meaningful
|
||||
// by implementing the toString() method
|
||||
System.out.println("SCHEDULE: " + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
package jobshop;
|
||||
|
||||
import jobshop.encodings.JobNumbers;
|
||||
import jobshop.encodings.ResourceOrder;
|
||||
import jobshop.encodings.Task;
|
||||
import jobshop.solvers.GreedySolver;
|
||||
import jobshop.solvers.GreedySolver.PriorityESTRule;
|
||||
import jobshop.solvers.GreedySolver.PriorityRule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class DebuggingMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// load the aaa1 instance
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// construit une solution dans la représentation par
|
||||
// numéro de jobs : [0 1 1 0 0 1]
|
||||
// Note : cette solution a aussi été vue dans les exercices (section 3.3)
|
||||
// mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2]
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
System.out.println("\nJOB NUMBER ENCODING: " + enc + "\n");
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid() + "\n");
|
||||
System.out.println("MAKESPAN: " + sched.makespan() + "\n");
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
|
||||
ResourceOrder ro = new ResourceOrder(instance);
|
||||
ro.tasksByMachine[0][0] = new Task(0,0);
|
||||
ro.tasksByMachine[0][1] = new Task(1,1);
|
||||
ro.tasksByMachine[1][0] = new Task(1,0);
|
||||
ro.tasksByMachine[1][1] = new Task(0,1);
|
||||
ro.tasksByMachine[2][0] = new Task(0,2);
|
||||
ro.tasksByMachine[2][1] = new Task(1,2);
|
||||
|
||||
System.out.println("RESOURCE ORDER ENCODING:\n" + ro + "\n");
|
||||
|
||||
System.out.println("Default Solver\n");
|
||||
sched = ro.toSchedule();
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
|
||||
JobNumbers jo = JobNumbers.fromSchedule(sched);
|
||||
System.out.println("JOB NUMBER ENCODING (FROM_SCHEDULE): " + jo + "\n");
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
System.out.println("Greedy Solver: STP");
|
||||
PriorityRule SPT = PriorityRule.SPT;
|
||||
Solver solverSPT = new GreedySolver(SPT);
|
||||
Result resultSPT = solverSPT.solve(instance, System.currentTimeMillis() + 10);
|
||||
sched = resultSPT.schedule;
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
System.out.println("Greedy Solver: LRPT\n");
|
||||
PriorityRule LRPT = PriorityRule.LRPT;
|
||||
Solver solverLRPT = new GreedySolver(LRPT);
|
||||
Result resultLRPT = solverLRPT.solve(instance, System.currentTimeMillis() + 10);
|
||||
sched = resultLRPT.schedule;
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
System.out.println("Greedy Solver: EST_SPT\n");
|
||||
PriorityESTRule EST_SPT = PriorityESTRule.EST_SPT;
|
||||
Solver solverEST_SPT = new GreedySolver(EST_SPT);
|
||||
Result resultEST_SPT = solverEST_SPT.solve(instance, System.currentTimeMillis() + 10);
|
||||
sched = resultEST_SPT.schedule;
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
System.out.println("---------------------------------------------\n");
|
||||
System.out.println("Greedy Solver: EST_SPT\n");
|
||||
PriorityESTRule EST_LRPT = PriorityESTRule.EST_LRPT;
|
||||
Solver solverEST_LRPT = new GreedySolver(EST_SPT);
|
||||
Result resultEST_LRPT = solverEST_LRPT.solve(instance, System.currentTimeMillis() + 10);
|
||||
sched = resultEST_LRPT.schedule;
|
||||
|
||||
System.out.println("SCHEDULE:\n" + sched);
|
||||
System.out.println("VALID: " + sched.isValid());
|
||||
System.out.println("MAKESPAN: " + sched.makespan());
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package jobshop;
|
||||
|
||||
public abstract class Encoding {
|
||||
|
||||
public final Instance instance;
|
||||
|
||||
public Encoding(Instance instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public abstract Schedule toSchedule();
|
||||
}
|
||||
package jobshop;
|
||||
|
||||
public abstract class Encoding {
|
||||
|
||||
public final Instance instance;
|
||||
|
||||
public Encoding(Instance instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public abstract Schedule toSchedule();
|
||||
}
|
||||
|
|
|
@ -1,79 +1,73 @@
|
|||
package jobshop;
|
||||
|
||||
import jobshop.encodings.Task;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Iterator;
|
||||
import java.util.Scanner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Instance {
|
||||
|
||||
/** Number of jobs in the instance */
|
||||
public final int numJobs;
|
||||
|
||||
/** Number of tasks per job */
|
||||
public final int numTasks;
|
||||
|
||||
/** Number of machines, assumed to be same as number of tasks. */
|
||||
public final int numMachines;
|
||||
|
||||
final int[][] durations;
|
||||
final int[][] machines;
|
||||
|
||||
public int duration(int job, int task) {
|
||||
return durations[job][task];
|
||||
}
|
||||
public int duration(Task t) {
|
||||
return duration(t.job, t.task);
|
||||
}
|
||||
public int machine(int job, int task) {
|
||||
return machines[job][task];
|
||||
}
|
||||
public int machine(Task t) {
|
||||
return this.machine(t.job, t.task);
|
||||
}
|
||||
|
||||
/** among the tasks of the given job, returns the task index that uses the given machine. */
|
||||
public int task_with_machine(int job, int wanted_machine) {
|
||||
for(int task = 0 ; task < numTasks ; task++) {
|
||||
if(machine(job, task) == wanted_machine)
|
||||
return task;
|
||||
}
|
||||
throw new RuntimeException("No task targeting machine "+wanted_machine+" on job "+job);
|
||||
}
|
||||
|
||||
Instance(int numJobs, int numTasks) {
|
||||
this.numJobs = numJobs;
|
||||
this.numTasks = numTasks;
|
||||
this.numMachines = numTasks;
|
||||
|
||||
durations = new int[numJobs][numTasks];
|
||||
machines = new int[numJobs][numTasks];
|
||||
}
|
||||
|
||||
/** Parses a instance from a file. */
|
||||
public static Instance fromFile(Path path) throws IOException {
|
||||
Iterator<String> lines = Files.readAllLines(path).stream()
|
||||
.filter(l -> !l.startsWith("#"))
|
||||
.collect(Collectors.toList())
|
||||
.iterator();
|
||||
|
||||
Scanner header = new Scanner(lines.next());
|
||||
int num_jobs = header.nextInt();
|
||||
int num_tasks = header.nextInt();
|
||||
Instance pb = new Instance(num_jobs, num_tasks);
|
||||
|
||||
for(int job = 0 ; job<num_jobs ; job++) {
|
||||
Scanner line = new Scanner(lines.next());
|
||||
for(int task = 0 ; task < num_tasks ; task++) {
|
||||
pb.machines[job][task] = line.nextInt();
|
||||
pb.durations[job][task] = line.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
package jobshop;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Iterator;
|
||||
import java.util.Scanner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Instance {
|
||||
|
||||
/** Number of jobs in the instance */
|
||||
public final int numJobs;
|
||||
|
||||
/** Number of tasks per job */
|
||||
public final int numTasks;
|
||||
|
||||
/** Number of machines, assumed to be same as number of tasks. */
|
||||
public final int numMachines;
|
||||
|
||||
final int[][] durations;
|
||||
final int[][] machines;
|
||||
|
||||
public int duration(int job, int task) {
|
||||
return durations[job][task];
|
||||
}
|
||||
public int machine(int job, int task) {
|
||||
return machines[job][task];
|
||||
}
|
||||
|
||||
/** among the tasks of the given job, returns the task index that uses the given machine. */
|
||||
public int task_with_machine(int job, int wanted_machine) {
|
||||
for(int task = 0 ; task < numTasks ; task++) {
|
||||
if(machine(job, task) == wanted_machine)
|
||||
return task;
|
||||
}
|
||||
throw new RuntimeException("No task targeting machine "+wanted_machine+" on job "+job);
|
||||
}
|
||||
|
||||
Instance(int numJobs, int numTasks) {
|
||||
this.numJobs = numJobs;
|
||||
this.numTasks = numTasks;
|
||||
this.numMachines = numTasks;
|
||||
|
||||
durations = new int[numJobs][numTasks];
|
||||
machines = new int[numJobs][numTasks];
|
||||
}
|
||||
|
||||
public static Instance fromFile(Path path) throws IOException {
|
||||
Iterator<String> lines = Files.readAllLines(path).stream()
|
||||
.filter(l -> !l.startsWith("#"))
|
||||
.collect(Collectors.toList())
|
||||
.iterator();
|
||||
|
||||
Scanner header = new Scanner(lines.next());
|
||||
int num_jobs = header.nextInt();
|
||||
int num_tasks = header.nextInt();
|
||||
Instance pb = new Instance(num_jobs, num_tasks);
|
||||
|
||||
for(int job = 0 ; job<num_jobs ; job++) {
|
||||
Scanner line = new Scanner(lines.next());
|
||||
for(int task = 0 ; task < num_tasks ; task++) {
|
||||
pb.machines[job][task] = line.nextInt();
|
||||
pb.durations[job][task] = line.nextInt();
|
||||
}
|
||||
line.close();
|
||||
}
|
||||
|
||||
header.close();
|
||||
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,146 +1,154 @@
|
|||
package jobshop;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import jobshop.solvers.*;
|
||||
import net.sourceforge.argparse4j.ArgumentParsers;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParser;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParserException;
|
||||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
|
||||
|
||||
public class Main {
|
||||
|
||||
/** All solvers available in this program */
|
||||
private static HashMap<String, Solver> solvers;
|
||||
static {
|
||||
solvers = new HashMap<>();
|
||||
solvers.put("basic", new BasicSolver());
|
||||
solvers.put("random", new RandomSolver());
|
||||
// add new solvers here
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()
|
||||
.defaultHelp(true)
|
||||
.description("Solves jobshop problems.");
|
||||
|
||||
parser.addArgument("-t", "--timeout")
|
||||
.setDefault(1L)
|
||||
.type(Long.class)
|
||||
.help("Solver timeout in seconds for each instance");
|
||||
parser.addArgument("--solver")
|
||||
.nargs("+")
|
||||
.required(true)
|
||||
.help("Solver(s) to use (space separated if more than one)");
|
||||
|
||||
parser.addArgument("--instance")
|
||||
.nargs("+")
|
||||
.required(true)
|
||||
.help("Instance(s) to solve (space separated if more than one)");
|
||||
|
||||
Namespace ns = null;
|
||||
try {
|
||||
ns = parser.parseArgs(args);
|
||||
} catch (ArgumentParserException e) {
|
||||
parser.handleError(e);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
PrintStream output = System.out;
|
||||
|
||||
long solveTimeMs = ns.getLong("timeout") * 1000;
|
||||
|
||||
List<String> solversToTest = ns.getList("solver");
|
||||
for(String solverName : solversToTest) {
|
||||
if(!solvers.containsKey(solverName)) {
|
||||
System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible.");
|
||||
System.err.println(" Available solvers: " + solvers.keySet().toString());
|
||||
System.err.println(" You can provide your own solvers by adding them to the `Main.solvers` HashMap.");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
List<String> instancePrefixes = ns.getList("instance");
|
||||
List<String> instances = new ArrayList<>();
|
||||
for(String instancePrefix : instancePrefixes) {
|
||||
List<String> matches = BestKnownResult.instancesMatching(instancePrefix);
|
||||
if(matches.isEmpty()) {
|
||||
System.err.println("ERROR: instance prefix \"" + instancePrefix + "\" does not match any instance.");
|
||||
System.err.println(" available instances: " + Arrays.toString(BestKnownResult.instances));
|
||||
System.exit(1);
|
||||
}
|
||||
instances.addAll(matches);
|
||||
}
|
||||
|
||||
float[] runtimes = new float[solversToTest.size()];
|
||||
float[] distances = new float[solversToTest.size()];
|
||||
|
||||
try {
|
||||
output.print( " ");
|
||||
for(String s : solversToTest)
|
||||
output.printf("%-30s", s);
|
||||
output.println();
|
||||
output.print("instance size best ");
|
||||
for(String s : solversToTest) {
|
||||
output.print("runtime makespan ecart ");
|
||||
}
|
||||
output.println();
|
||||
|
||||
|
||||
for(String instanceName : instances) {
|
||||
int bestKnown = BestKnownResult.of(instanceName);
|
||||
|
||||
|
||||
Path path = Paths.get("instances/", instanceName);
|
||||
Instance instance = Instance.fromFile(path);
|
||||
|
||||
output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
|
||||
|
||||
for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
|
||||
String solverName = solversToTest.get(solverId);
|
||||
Solver solver = solvers.get(solverName);
|
||||
long start = System.currentTimeMillis();
|
||||
long deadline = System.currentTimeMillis() + solveTimeMs;
|
||||
Result result = solver.solve(instance, deadline);
|
||||
long runtime = System.currentTimeMillis() - start;
|
||||
|
||||
if(!result.schedule.isValid()) {
|
||||
System.err.println("ERROR: solver returned an invalid schedule");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
assert result.schedule.isValid();
|
||||
int makespan = result.schedule.makespan();
|
||||
float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
|
||||
runtimes[solverId] += (float) runtime / (float) instances.size();
|
||||
distances[solverId] += dist / (float) instances.size();
|
||||
|
||||
output.printf("%7d %8s %5.1f ", runtime, makespan, dist);
|
||||
output.flush();
|
||||
}
|
||||
output.println();
|
||||
|
||||
}
|
||||
|
||||
|
||||
output.printf("%-8s %-5s %4s ", "AVG", "-", "-");
|
||||
for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
|
||||
output.printf("%7.1f %8s %5.1f ", runtimes[solverId], "-", distances[solverId]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
package jobshop;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import jobshop.solvers.BasicSolver;
|
||||
import jobshop.solvers.RandomSolver;
|
||||
import jobshop.solvers.GreedySolver.PriorityESTRule;
|
||||
import jobshop.solvers.GreedySolver.PriorityRule;
|
||||
import jobshop.solvers.GreedySolver;
|
||||
import net.sourceforge.argparse4j.ArgumentParsers;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParser;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParserException;
|
||||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
|
||||
|
||||
public class Main {
|
||||
|
||||
/** All solvers available in this program */
|
||||
private static HashMap<String, Solver> solvers;
|
||||
static {
|
||||
solvers = new HashMap<>();
|
||||
solvers.put("basic", new BasicSolver());
|
||||
solvers.put("random", new RandomSolver());
|
||||
// add new solvers here
|
||||
PriorityRule SPT = PriorityRule.SPT;
|
||||
solvers.put("greedySPT", new GreedySolver(SPT));
|
||||
PriorityRule LRPT = PriorityRule.LRPT;
|
||||
solvers.put("greedyLRPT", new GreedySolver(LRPT));
|
||||
PriorityESTRule EST_SPT = PriorityESTRule.EST_SPT;
|
||||
solvers.put("greedyEST_SPT", new GreedySolver(EST_SPT));
|
||||
PriorityESTRule EST_LRPT = PriorityESTRule.EST_LRPT;
|
||||
solvers.put("greedyEST_LRPT", new GreedySolver(EST_LRPT));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String[] args) {
|
||||
ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build()
|
||||
.defaultHelp(true)
|
||||
.description("Solves jobshop problems.");
|
||||
|
||||
parser.addArgument("-t", "--timeout")
|
||||
.setDefault(1L)
|
||||
.type(Long.class)
|
||||
.help("Solver timeout in seconds for each instance");
|
||||
parser.addArgument("--solver")
|
||||
.nargs("+")
|
||||
.required(true)
|
||||
.help("Solver(s) to use (space separated if more than one)");
|
||||
|
||||
parser.addArgument("--instance")
|
||||
.nargs("+")
|
||||
.required(true)
|
||||
.help("Instance(s) to solve (space separated if more than one)");
|
||||
|
||||
Namespace ns = null;
|
||||
try {
|
||||
ns = parser.parseArgs(args);
|
||||
} catch (ArgumentParserException e) {
|
||||
parser.handleError(e);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
PrintStream output = System.out;
|
||||
|
||||
long solveTimeMs = ns.getLong("timeout") * 1000;
|
||||
|
||||
List<String> solversToTest = ns.getList("solver");
|
||||
for(String solverName : solversToTest) {
|
||||
if(!solvers.containsKey(solverName)) {
|
||||
System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible.");
|
||||
System.err.println(" Available solvers: " + solvers.keySet().toString());
|
||||
System.err.println(" You can provide your own solvers by adding them to the `Main.solvers` HashMap.");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
List<String> instances = ns.<String>getList("instance");
|
||||
for(String instanceName : instances) {
|
||||
if(!BestKnownResult.isKnown(instanceName)) {
|
||||
System.err.println("ERROR: instance \"" + instanceName + "\" is not avalaible.");
|
||||
System.err.println(" available instances: " + Arrays.toString(BestKnownResult.instances));
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
float[] runtimes = new float[solversToTest.size()];
|
||||
float[] distances = new float[solversToTest.size()];
|
||||
|
||||
try {
|
||||
output.print( " ");;
|
||||
for(String s : solversToTest)
|
||||
output.printf("%-30s", s);
|
||||
output.println();
|
||||
output.print("instance size best ");
|
||||
for(String s : solversToTest) {
|
||||
output.print("runtime makespan ecart ");
|
||||
}
|
||||
output.println();
|
||||
|
||||
|
||||
for(String instanceName : instances) {
|
||||
int bestKnown = BestKnownResult.of(instanceName);
|
||||
|
||||
|
||||
Path path = Paths.get("instances/", instanceName);
|
||||
Instance instance = Instance.fromFile(path);
|
||||
|
||||
output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown);
|
||||
|
||||
for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
|
||||
String solverName = solversToTest.get(solverId);
|
||||
Solver solver = solvers.get(solverName);
|
||||
long start = System.currentTimeMillis();
|
||||
long deadline = System.currentTimeMillis() + solveTimeMs;
|
||||
Result result = solver.solve(instance, deadline);
|
||||
long runtime = System.currentTimeMillis() - start;
|
||||
|
||||
if(!result.schedule.isValid()) {
|
||||
System.err.println("ERROR: solver returned an invalid schedule");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
assert result.schedule.isValid();
|
||||
int makespan = result.schedule.makespan();
|
||||
float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
|
||||
runtimes[solverId] += (float) runtime / (float) instances.size();
|
||||
distances[solverId] += dist / (float) instances.size();
|
||||
|
||||
output.printf("%7d %8s %5.1f ", runtime, makespan, dist);
|
||||
output.flush();
|
||||
}
|
||||
output.println();
|
||||
|
||||
}
|
||||
|
||||
|
||||
output.printf("%-8s %-5s %4s ", "AVG", "-", "-");
|
||||
for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) {
|
||||
output.printf("%7.1f %8s %5.1f ", runtimes[solverId], "-", distances[solverId]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
package jobshop;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class Result {
|
||||
|
||||
public Result(Instance instance, Schedule schedule, ExitCause cause) {
|
||||
this.instance = instance;
|
||||
this.schedule = schedule;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public enum ExitCause {
|
||||
Timeout, ProvedOptimal, Blocked
|
||||
}
|
||||
|
||||
public final Instance instance;
|
||||
public final Schedule schedule;
|
||||
public final ExitCause cause;
|
||||
|
||||
|
||||
}
|
||||
package jobshop;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Result {
|
||||
|
||||
public Result(Instance instance, Schedule schedule, ExitCause cause) {
|
||||
this.instance = instance;
|
||||
this.schedule = schedule;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public enum ExitCause {
|
||||
Timeout, ProvedOptimal, Blocked
|
||||
}
|
||||
|
||||
public final Instance instance;
|
||||
public final Schedule schedule;
|
||||
public final ExitCause cause;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,135 +1,157 @@
|
|||
package jobshop;
|
||||
|
||||
|
||||
import jobshop.encodings.Task;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class Schedule {
|
||||
public final Instance pb;
|
||||
// start times of each job and task
|
||||
// times[j][i] is the start time of task (j,i) : i^th task of the j^th job
|
||||
final int[][] times;
|
||||
|
||||
public Schedule(Instance pb, int[][] times) {
|
||||
this.pb = pb;
|
||||
this.times = new int[pb.numJobs][];
|
||||
for(int j = 0 ; j < pb.numJobs ; j++) {
|
||||
this.times[j] = Arrays.copyOf(times[j], pb.numTasks);
|
||||
}
|
||||
}
|
||||
|
||||
public int startTime(int job, int task) {
|
||||
return times[job][task];
|
||||
}
|
||||
|
||||
/** Returns true if this schedule is valid (no constraint is violated) */
|
||||
public boolean isValid() {
|
||||
for(int j = 0 ; j<pb.numJobs ; j++) {
|
||||
for(int t = 1 ; t<pb.numTasks ; t++) {
|
||||
if(startTime(j, t-1) + pb.duration(j, t-1) > startTime(j, t))
|
||||
return false;
|
||||
}
|
||||
for(int t = 0 ; t<pb.numTasks ; t++) {
|
||||
if(startTime(j, t) < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int machine = 0 ; machine < pb.numMachines ; machine++) {
|
||||
for(int j1=0 ; j1<pb.numJobs ; j1++) {
|
||||
int t1 = pb.task_with_machine(j1, machine);
|
||||
for(int j2=j1+1 ; j2<pb.numJobs ; j2++) {
|
||||
int t2 = pb.task_with_machine(j2, machine);
|
||||
|
||||
boolean t1_first = startTime(j1, t1) + pb.duration(j1, t1) <= startTime(j2, t2);
|
||||
boolean t2_first = startTime(j2, t2) + pb.duration(j2, t2) <= startTime(j1, t1);
|
||||
|
||||
if(!t1_first && !t2_first)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int makespan() {
|
||||
int max = -1;
|
||||
for(int j = 0 ; j<pb.numJobs ; j++) {
|
||||
max = Math.max(max, startTime(j, pb.numTasks-1) + pb.duration(j, pb.numTasks -1));
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
public int startTime(Task task) {
|
||||
return startTime(task.job, task.task);
|
||||
}
|
||||
|
||||
public int endTime(Task task) {
|
||||
return startTime(task) + pb.duration(task.job, task.task);
|
||||
}
|
||||
|
||||
public boolean isCriticalPath(List<Task> path) {
|
||||
if(startTime(path.get(0)) != 0) {
|
||||
return false;
|
||||
}
|
||||
if(endTime(path.get(path.size()-1)) != makespan()) {
|
||||
return false;
|
||||
}
|
||||
for(int i=0 ; i<path.size()-1 ; i++) {
|
||||
if(endTime(path.get(i)) != startTime(path.get(i+1)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Task> criticalPath() {
|
||||
// select task with greatest end time
|
||||
Task ldd = IntStream.range(0, pb.numJobs)
|
||||
.mapToObj(j -> new Task(j, pb.numTasks-1))
|
||||
.max(Comparator.comparing(this::endTime))
|
||||
.get();
|
||||
assert endTime(ldd) == makespan();
|
||||
|
||||
// list that will contain the critical path.
|
||||
// we construct it from the end, starting with the
|
||||
// task that finishes last
|
||||
LinkedList<Task> path = new LinkedList<>();
|
||||
path.add(0,ldd);
|
||||
|
||||
// keep adding tasks to the path until the first task in the path
|
||||
// starts a time 0
|
||||
while(startTime(path.getFirst()) != 0) {
|
||||
Task cur = path.getFirst();
|
||||
int machine = pb.machine(cur.job, cur.task);
|
||||
|
||||
// will contain the task that was delaying the start
|
||||
// of our current task
|
||||
Optional<Task> latestPredecessor = Optional.empty();
|
||||
|
||||
if(cur.task > 0) {
|
||||
// our current task has a predecessor on the job
|
||||
Task predOnJob = new Task(cur.job, cur.task -1);
|
||||
|
||||
// if it was the delaying task, save it to predecessor
|
||||
if(endTime(predOnJob) == startTime(cur))
|
||||
latestPredecessor = Optional.of(predOnJob);
|
||||
}
|
||||
if(!latestPredecessor.isPresent()) {
|
||||
// no latest predecessor found yet, look among tasks executing on the same machine
|
||||
latestPredecessor = IntStream.range(0, pb.numJobs)
|
||||
.mapToObj(j -> new Task(j, pb.task_with_machine(j, machine)))
|
||||
.filter(t -> endTime(t) == startTime(cur))
|
||||
.findFirst();
|
||||
}
|
||||
// at this point we should have identified a latest predecessor, either on the job or on the machine
|
||||
assert latestPredecessor.isPresent() && endTime(latestPredecessor.get()) == startTime(cur);
|
||||
// insert predecessor at the beginning of the path
|
||||
path.add(0, latestPredecessor.get());
|
||||
}
|
||||
assert isCriticalPath(path);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
package jobshop;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import jobshop.encodings.Task;
|
||||
|
||||
public class Schedule {
|
||||
public final Instance pb;
|
||||
// start times of each job and task
|
||||
// times[j][i] is the start time of task (j,i) : i^th task of the j^th job
|
||||
final int[][] times;
|
||||
|
||||
public Schedule(Instance pb, int[][] times) {
|
||||
this.pb = pb;
|
||||
this.times = new int[pb.numJobs][];
|
||||
for(int j = 0 ; j < pb.numJobs ; j++) {
|
||||
this.times[j] = Arrays.copyOf(times[j], pb.numTasks);
|
||||
}
|
||||
}
|
||||
|
||||
public int startTime(int job, int task) {
|
||||
return times[job][task];
|
||||
}
|
||||
|
||||
/** Returns true if this schedule is valid (no constraint is violated) */
|
||||
public boolean isValid() {
|
||||
for(int j = 0 ; j<pb.numJobs ; j++) {
|
||||
for(int t = 1 ; t<pb.numTasks ; t++) {
|
||||
if(startTime(j, t-1) + pb.duration(j, t-1) > startTime(j, t))
|
||||
return false;
|
||||
}
|
||||
for(int t = 0 ; t<pb.numTasks ; t++) {
|
||||
if(startTime(j, t) < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int machine = 0 ; machine < pb.numMachines ; machine++) {
|
||||
for(int j1=0 ; j1<pb.numJobs ; j1++) {
|
||||
int t1 = pb.task_with_machine(j1, machine);
|
||||
for(int j2=j1+1 ; j2<pb.numJobs ; j2++) {
|
||||
int t2 = pb.task_with_machine(j2, machine);
|
||||
|
||||
boolean t1_first = startTime(j1, t1) + pb.duration(j1, t1) <= startTime(j2, t2);
|
||||
boolean t2_first = startTime(j2, t2) + pb.duration(j2, t2) <= startTime(j1, t1);
|
||||
|
||||
if(!t1_first && !t2_first)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int makespan() {
|
||||
int max = -1;
|
||||
for(int j = 0 ; j<pb.numJobs ; j++) {
|
||||
max = Math.max(max, startTime(j, pb.numTasks-1) + pb.duration(j, pb.numTasks -1));
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
public int startTime(Task task) {
|
||||
return startTime(task.job, task.task);
|
||||
}
|
||||
|
||||
public int endTime(Task task) {
|
||||
return startTime(task) + pb.duration(task.job, task.task);
|
||||
}
|
||||
|
||||
public boolean isCriticalPath(List<Task> path) {
|
||||
if(startTime(path.get(0)) != 0) {
|
||||
return false;
|
||||
}
|
||||
if(endTime(path.get(path.size()-1)) != makespan()) {
|
||||
return false;
|
||||
}
|
||||
for(int i=0 ; i<path.size()-1 ; i++) {
|
||||
if(endTime(path.get(i)) != startTime(path.get(i+1)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Task> criticalPath() {
|
||||
// select task with greatest end time
|
||||
Task ldd = IntStream.range(0, pb.numJobs)
|
||||
.mapToObj(j -> new Task(j, pb.numTasks-1))
|
||||
.max(Comparator.comparing(this::endTime))
|
||||
.get();
|
||||
assert endTime(ldd) == makespan();
|
||||
|
||||
// list that will contain the critical path.
|
||||
// we construct it from the end, starting with the
|
||||
// task that finishes last
|
||||
LinkedList<Task> path = new LinkedList<>();
|
||||
path.add(0,ldd);
|
||||
|
||||
// keep adding tasks to the path until the first task in the path
|
||||
// starts a time 0
|
||||
while(startTime(path.getFirst()) != 0) {
|
||||
Task cur = path.getFirst();
|
||||
int machine = pb.machine(cur.job, cur.task);
|
||||
|
||||
// will contain the task that was delaying the start
|
||||
// of our current task
|
||||
Optional<Task> latestPredecessor = Optional.empty();
|
||||
|
||||
if(cur.task > 0) {
|
||||
// our current task has a predecessor on the job
|
||||
Task predOnJob = new Task(cur.job, cur.task -1);
|
||||
|
||||
// if it was the delaying task, save it to predecessor
|
||||
if(endTime(predOnJob) == startTime(cur))
|
||||
latestPredecessor = Optional.of(predOnJob);
|
||||
}
|
||||
if(!latestPredecessor.isPresent()) {
|
||||
// no latest predecessor found yet, look among tasks executing on the same machine
|
||||
latestPredecessor = IntStream.range(0, pb.numJobs)
|
||||
.mapToObj(j -> new Task(j, pb.task_with_machine(j, machine)))
|
||||
.filter(t -> endTime(t) == startTime(cur))
|
||||
.findFirst();
|
||||
}
|
||||
// at this point we should have identified a latest predecessor, either on the job or on the machine
|
||||
assert latestPredecessor.isPresent() && endTime(latestPredecessor.get()) == startTime(cur);
|
||||
// insert predecessor at the beginning of the path
|
||||
path.add(0, latestPredecessor.get());
|
||||
}
|
||||
assert isCriticalPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
public Schedule copy() {
|
||||
return new Schedule(this.pb, this.times);
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
/* Implémentation de la méthode toString() de la classe Schedule*/
|
||||
/****************************************************************/
|
||||
public String toString() {
|
||||
String res = "";
|
||||
for (int i = 0; i < this.times.length; i++) {
|
||||
res += "Job " + Integer.toString(i + 1) + " starting times : \n";
|
||||
for (int j = 0; j < this.times[i].length; j++) {
|
||||
res += "\tTask " + Integer.toString(j + 1) + " starts at time : " + Integer.toString(this.times[i][j]) + "\n";
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package jobshop;
|
||||
|
||||
public interface Solver {
|
||||
|
||||
Result solve(Instance instance, long deadline);
|
||||
|
||||
}
|
||||
package jobshop;
|
||||
|
||||
public interface Solver {
|
||||
|
||||
Result solve(Instance instance, long deadline);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,84 +1,116 @@
|
|||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Encoding;
|
||||
import jobshop.Instance;
|
||||
import jobshop.Schedule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/** Représentation par numéro de job. */
|
||||
public class JobNumbers extends Encoding {
|
||||
|
||||
/** A numJobs * numTasks array containing the representation by job numbers. */
|
||||
public final int[] jobs;
|
||||
|
||||
/** In case the encoding is only partially filled, indicates the index of the first
|
||||
* element of `jobs` that has not been set yet. */
|
||||
public int nextToSet = 0;
|
||||
|
||||
public JobNumbers(Instance instance) {
|
||||
super(instance);
|
||||
|
||||
jobs = new int[instance.numJobs * instance.numMachines];
|
||||
Arrays.fill(jobs, -1);
|
||||
}
|
||||
|
||||
public JobNumbers(Schedule schedule) {
|
||||
super(schedule.pb);
|
||||
|
||||
this.jobs = new int[instance.numJobs * instance.numTasks];
|
||||
|
||||
// for each job indicates which is the next task to be scheduled
|
||||
int[] nextOnJob = new int[instance.numJobs];
|
||||
|
||||
while(Arrays.stream(nextOnJob).anyMatch(t -> t < instance.numTasks)) {
|
||||
Task next = IntStream
|
||||
// for all jobs numbers
|
||||
.range(0, instance.numJobs)
|
||||
// build the next task for this job
|
||||
.mapToObj(j -> new Task(j, nextOnJob[j]))
|
||||
// only keep valid tasks (some jobs have no task left to be executed)
|
||||
.filter(t -> t.task < instance.numTasks)
|
||||
// select the task with the earliest execution time
|
||||
.min(Comparator.comparing(t -> schedule.startTime(t.job, t.task)))
|
||||
.get();
|
||||
|
||||
this.jobs[nextToSet++] = next.job;
|
||||
nextOnJob[next.job] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
// time at which each machine is going to be freed
|
||||
int[] nextFreeTimeResource = new int[instance.numMachines];
|
||||
|
||||
// for each job, the first task that has not yet been scheduled
|
||||
int[] nextTask = new int[instance.numJobs];
|
||||
|
||||
// for each task, its start time
|
||||
int[][] startTimes = new int[instance.numJobs][instance.numTasks];
|
||||
|
||||
// compute the earliest start time for every task of every job
|
||||
for(int job : jobs) {
|
||||
int task = nextTask[job];
|
||||
int machine = instance.machine(job, task);
|
||||
// earliest start time for this task
|
||||
int est = task == 0 ? 0 : startTimes[job][task-1] + instance.duration(job, task-1);
|
||||
est = Math.max(est, nextFreeTimeResource[machine]);
|
||||
|
||||
startTimes[job][task] = est;
|
||||
nextFreeTimeResource[machine] = est + instance.duration(job, task);
|
||||
nextTask[job] = task + 1;
|
||||
}
|
||||
|
||||
return new Schedule(instance, startTimes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Arrays.toString(Arrays.copyOfRange(jobs,0, nextToSet));
|
||||
}
|
||||
}
|
||||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Encoding;
|
||||
import jobshop.Instance;
|
||||
import jobshop.Schedule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/** Représentation par numéro de job. */
|
||||
public class JobNumbers extends Encoding {
|
||||
|
||||
/** A numJobs * numTasks array containing the representation by job numbers. */
|
||||
public final int[] jobs;
|
||||
|
||||
/** In case the encoding is only partially filled, indicates the index of first
|
||||
* element of `jobs` that has not been set yet. */
|
||||
public int nextToSet = 0;
|
||||
|
||||
public JobNumbers(Instance instance) {
|
||||
super(instance);
|
||||
|
||||
jobs = new int[instance.numJobs * instance.numMachines];
|
||||
Arrays.fill(jobs, -1);
|
||||
}
|
||||
|
||||
public JobNumbers(Schedule schedule) {
|
||||
super(schedule.pb);
|
||||
|
||||
this.jobs = new int[instance.numJobs * instance.numTasks];
|
||||
|
||||
// for each job indicates which is the next task to be scheduled
|
||||
int[] nextOnJob = new int[instance.numJobs];
|
||||
|
||||
while(Arrays.stream(nextOnJob).anyMatch(t -> t < instance.numTasks)) {
|
||||
Task next = IntStream
|
||||
// for all jobs numbers
|
||||
.range(0, instance.numJobs)
|
||||
// build the next task for this job
|
||||
.mapToObj(j -> new Task(j, nextOnJob[j]))
|
||||
// only keep valid tasks (some jobs have no task left to be executed)
|
||||
.filter(t -> t.task < instance.numTasks)
|
||||
// select the task with the earliest execution time
|
||||
.min(Comparator.comparing(t -> schedule.startTime(t.job, t.task)))
|
||||
.get();
|
||||
|
||||
this.jobs[nextToSet++] = next.job;
|
||||
nextOnJob[next.job] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
// time at which each machine is going to be freed
|
||||
int[] nextFreeTimeResource = new int[instance.numMachines];
|
||||
|
||||
// for each job, the first task that has not yet been scheduled
|
||||
int[] nextTask = new int[instance.numJobs];
|
||||
|
||||
// for each task, its start time
|
||||
int[][] startTimes = new int[instance.numJobs][instance.numTasks];
|
||||
|
||||
// compute the earliest start time for every task of every job
|
||||
for(int job : jobs) {
|
||||
int task = nextTask[job];
|
||||
int machine = instance.machine(job, task);
|
||||
// earliest start time for this task
|
||||
int est = task == 0 ? 0 : startTimes[job][task-1] + instance.duration(job, task-1);
|
||||
est = Math.max(est, nextFreeTimeResource[machine]);
|
||||
|
||||
startTimes[job][task] = est;
|
||||
nextFreeTimeResource[machine] = est + instance.duration(job, task);
|
||||
nextTask[job] = task + 1;
|
||||
}
|
||||
|
||||
return new Schedule(instance, startTimes);
|
||||
}
|
||||
|
||||
public static JobNumbers fromSchedule(Schedule sched) {
|
||||
JobNumbers jo = new JobNumbers(sched.pb);
|
||||
|
||||
int current_time = 0;
|
||||
Task current_task = new Task(-1,-1);
|
||||
Task [] done_tasks = new Task[sched.pb.numJobs*sched.pb.numTasks];
|
||||
Arrays.fill(done_tasks, current_task);
|
||||
|
||||
int min;
|
||||
|
||||
for (int i = 0; i < sched.pb.numJobs*sched.pb.numTasks; i++) {
|
||||
// Il faut faire le code ci-dessous autant de fois que l'on a de taches
|
||||
// On trouve le minimum parmis les restants
|
||||
min = Integer.MAX_VALUE;
|
||||
for (int job = 0; job < sched.pb.numJobs; job++) {
|
||||
for (int task = 0; task < sched.pb.numTasks; task++) {
|
||||
int task_start_time = sched.startTime(job, task);
|
||||
Task this_task = new Task(job, task);
|
||||
if (task_start_time < min && task_start_time >= current_time && !(Arrays.asList(done_tasks).contains(this_task))) {
|
||||
min = task_start_time;
|
||||
current_task = this_task;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Une fois on a trouvé la suivante tache a realiser on introduit le numero du job dans jobs
|
||||
jo.jobs[jo.nextToSet++] = current_task.job;
|
||||
done_tasks[i] = current_task;
|
||||
}
|
||||
|
||||
return jo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Arrays.toString(Arrays.copyOfRange(jobs,0, nextToSet));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,131 +1,120 @@
|
|||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Encoding;
|
||||
import jobshop.Instance;
|
||||
import jobshop.Schedule;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class ResourceOrder extends Encoding {
|
||||
|
||||
// for each machine m, taskByMachine[m] is an array of tasks to be
|
||||
// executed on this machine in the same order
|
||||
public final Task[][] tasksByMachine;
|
||||
|
||||
// for each machine, indicate on many tasks have been initialized
|
||||
public final int[] nextFreeSlot;
|
||||
|
||||
/** Creates a new empty resource order. */
|
||||
public ResourceOrder(Instance instance)
|
||||
{
|
||||
super(instance);
|
||||
|
||||
// matrix of null elements (null is the default value of objects)
|
||||
tasksByMachine = new Task[instance.numMachines][instance.numJobs];
|
||||
|
||||
// no task scheduled on any machine (0 is the default value)
|
||||
nextFreeSlot = new int[instance.numMachines];
|
||||
}
|
||||
|
||||
/** Creates a resource order from a schedule. */
|
||||
public ResourceOrder(Schedule schedule)
|
||||
{
|
||||
super(schedule.pb);
|
||||
Instance pb = schedule.pb;
|
||||
|
||||
this.tasksByMachine = new Task[pb.numMachines][];
|
||||
this.nextFreeSlot = new int[instance.numMachines];
|
||||
|
||||
for(int m = 0 ; m<schedule.pb.numMachines ; m++) {
|
||||
final int machine = m;
|
||||
|
||||
// for thi machine, find all tasks that are executed on it and sort them by their start time
|
||||
tasksByMachine[m] =
|
||||
IntStream.range(0, pb.numJobs) // all job numbers
|
||||
.mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) // all tasks on this machine (one per job)
|
||||
.sorted(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) // sorted by start time
|
||||
.toArray(Task[]::new); // as new array and store in tasksByMachine
|
||||
|
||||
// indicate that all tasks have been initialized for machine m
|
||||
nextFreeSlot[m] = instance.numJobs;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
// indicate for each task that have been scheduled, its start time
|
||||
int [][] startTimes = new int [instance.numJobs][instance.numTasks];
|
||||
|
||||
// for each job, how many tasks have been scheduled (0 initially)
|
||||
int[] nextToScheduleByJob = new int[instance.numJobs];
|
||||
|
||||
// for each machine, how many tasks have been scheduled (0 initially)
|
||||
int[] nextToScheduleByMachine = new int[instance.numMachines];
|
||||
|
||||
// for each machine, earliest time at which the machine can be used
|
||||
int[] releaseTimeOfMachine = new int[instance.numMachines];
|
||||
|
||||
|
||||
// loop while there remains a job that has unscheduled tasks
|
||||
while(IntStream.range(0, instance.numJobs).anyMatch(m -> nextToScheduleByJob[m] < instance.numTasks)) {
|
||||
|
||||
// selects a task that has noun scheduled predecessor on its job and machine :
|
||||
// - it is the next to be schedule on a machine
|
||||
// - it is the next to be scheduled on its job
|
||||
// if there is no such task, we have cyclic dependency and the solution is invalid
|
||||
Optional<Task> schedulable =
|
||||
IntStream.range(0, instance.numMachines) // all machines ...
|
||||
.filter(m -> nextToScheduleByMachine[m] < instance.numJobs) // ... with unscheduled jobs
|
||||
.mapToObj(m -> this.tasksByMachine[m][nextToScheduleByMachine[m]]) // tasks that are next to schedule on a machine ...
|
||||
.filter(task -> task.task == nextToScheduleByJob[task.job]) // ... and on their job
|
||||
.findFirst(); // select the first one if any
|
||||
|
||||
if(schedulable.isPresent()) {
|
||||
// we found a schedulable task, lets call it t
|
||||
Task t = schedulable.get();
|
||||
int machine = instance.machine(t.job, t.task);
|
||||
|
||||
// compute the earliest start time (est) of the task
|
||||
int est = t.task == 0 ? 0 : startTimes[t.job][t.task-1] + instance.duration(t.job, t.task-1);
|
||||
est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]);
|
||||
startTimes[t.job][t.task] = est;
|
||||
|
||||
// mark the task as scheduled
|
||||
nextToScheduleByJob[t.job]++;
|
||||
nextToScheduleByMachine[machine]++;
|
||||
// increase the release time of the machine
|
||||
releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task);
|
||||
} else {
|
||||
// no tasks are schedulable, there is no solution for this resource ordering
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// we exited the loop : all tasks have been scheduled successfully
|
||||
return new Schedule(instance, startTimes);
|
||||
}
|
||||
|
||||
/** Creates an exact copy of this resource order. */
|
||||
public ResourceOrder copy() {
|
||||
return new ResourceOrder(this.toSchedule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder s = new StringBuilder();
|
||||
for(int m=0; m < instance.numMachines; m++)
|
||||
{
|
||||
s.append("Machine ").append(m).append(" : ");
|
||||
for(int j=0; j<instance.numJobs; j++)
|
||||
{
|
||||
s.append(tasksByMachine[m][j]).append(" ; ");
|
||||
}
|
||||
s.append("\n");
|
||||
}
|
||||
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
}
|
||||
package jobshop.encodings;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import jobshop.Encoding;
|
||||
import jobshop.Instance;
|
||||
import jobshop.Schedule;
|
||||
|
||||
|
||||
public class ResourceOrder extends Encoding {
|
||||
|
||||
public final Task[][] tasksByMachine;
|
||||
|
||||
public final int[] nextFreeSlot;
|
||||
|
||||
public ResourceOrder(Instance instance) {
|
||||
super(instance);
|
||||
|
||||
this.tasksByMachine = new Task[instance.numMachines][instance.numJobs];
|
||||
for (int i = 0; i < instance.numMachines; i++) {
|
||||
for (int j = 0; j < instance.numJobs; j++) {
|
||||
this.tasksByMachine[i][j] = new Task(-1,-1);
|
||||
}
|
||||
}
|
||||
|
||||
// no task scheduled on any machine (0 is the default value)
|
||||
nextFreeSlot = new int[instance.numMachines];
|
||||
}
|
||||
|
||||
public ResourceOrder(Schedule schedule) {
|
||||
super(schedule.pb);
|
||||
Instance pb = schedule.pb;
|
||||
|
||||
this.tasksByMachine = new Task[pb.numMachines][];
|
||||
this.nextFreeSlot = new int[instance.numMachines];
|
||||
|
||||
for(int m = 0 ; m<schedule.pb.numMachines ; m++) {
|
||||
final int machine = m;
|
||||
|
||||
// for thi machine, find all tasks that are executed on it and sort them by their start time
|
||||
tasksByMachine[m] =
|
||||
IntStream.range(0, pb.numJobs) // all job numbers
|
||||
.mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) // all tasks on this machine (one per job)
|
||||
.sorted(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) // sorted by start time
|
||||
.toArray(Task[]::new); // as new array and store in tasksByMachine
|
||||
|
||||
// indicate that all tasks have been initialized for machine m
|
||||
nextFreeSlot[m] = instance.numJobs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
// indicate for each task that have been scheduled, its start time
|
||||
int [][] startTimes = new int [instance.numJobs][instance.numTasks];
|
||||
|
||||
// for each job, how many tasks have been scheduled (0 initially)
|
||||
int[] nextToScheduleByJob = new int[instance.numJobs];
|
||||
|
||||
// for each machine, how many tasks have been scheduled (0 initially)
|
||||
int[] nextToScheduleByMachine = new int[instance.numMachines];
|
||||
|
||||
// for each machine, earliest time at which the machine can be used
|
||||
int[] releaseTimeOfMachine = new int[instance.numMachines];
|
||||
|
||||
|
||||
// loop while there remains a job that has unscheduled tasks
|
||||
while(IntStream.range(0, instance.numJobs).anyMatch(m -> nextToScheduleByJob[m] < instance.numTasks)) {
|
||||
|
||||
// selects a task that has noun scheduled predecessor on its job and machine :
|
||||
// - it is the next to be schedule on a machine
|
||||
// - it is the next to be scheduled on its job
|
||||
// if there is no such task, we have cyclic dependency and the solution is invalid
|
||||
Optional<Task> schedulable =
|
||||
IntStream.range(0, instance.numMachines) // all machines ...
|
||||
.filter(m -> nextToScheduleByMachine[m] < instance.numJobs) // ... with unscheduled jobs
|
||||
.mapToObj(m -> this.tasksByMachine[m][nextToScheduleByMachine[m]]) // tasks that are next to schedule on a machine ...
|
||||
.filter(task -> task.task == nextToScheduleByJob[task.job]) // ... and on their job
|
||||
.findFirst(); // select the first one if any
|
||||
|
||||
if(schedulable.isPresent()) {
|
||||
// we found a schedulable task, lets call it t
|
||||
Task t = schedulable.get();
|
||||
int machine = instance.machine(t.job, t.task);
|
||||
|
||||
// compute the earliest start time (est) of the task
|
||||
int est = t.task == 0 ? 0 : startTimes[t.job][t.task-1] + instance.duration(t.job, t.task-1);
|
||||
est = Math.max(est, releaseTimeOfMachine[instance.machine(t.job, t.task)]);
|
||||
startTimes[t.job][t.task] = est;
|
||||
|
||||
// mark the task as scheduled
|
||||
nextToScheduleByJob[t.job]++;
|
||||
nextToScheduleByMachine[machine]++;
|
||||
// increase the release time of the machine
|
||||
releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task);
|
||||
} else {
|
||||
// no tasks are schedulable, there is no solution for this resource ordering
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// we exited the loop : all tasks have been scheduled successfully
|
||||
return new Schedule(instance, startTimes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String res = "";
|
||||
for (int i = 0; i < this.tasksByMachine.length; i++) {
|
||||
res += "Machine number : " + Integer.toString(i+1) + "\n";
|
||||
for (int j = 0; j < this.tasksByMachine[i].length; j++) {
|
||||
res += "\tUse number " + Integer.toString(j+1) + " : " + this.tasksByMachine[i][j].add_one() + "\n";
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,41 +1,45 @@
|
|||
package jobshop.encodings;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/** Represents a task (job,task) of an jobshop problem.
|
||||
*
|
||||
* Example : (2, 3) repesents the fourth task of the third job. (remeber that we tart counting at 0)
|
||||
* */
|
||||
public final class Task {
|
||||
|
||||
/** Identifier of the job */
|
||||
public final int job;
|
||||
|
||||
/** Index of the task inside the job. */
|
||||
public final int task;
|
||||
|
||||
|
||||
public Task(int job, int task) {
|
||||
this.job = job;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Task task1 = (Task) o;
|
||||
return job == task1.job &&
|
||||
task == task1.task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(job, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + job +", " + task + '}';
|
||||
}
|
||||
}
|
||||
package jobshop.encodings;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/** Represents a task (job,task) of an jobshop problem.
|
||||
*
|
||||
* Example : (2, 3) repesents the fourth task of the third job. (remeber that we tart counting at 0)
|
||||
* */
|
||||
public final class Task {
|
||||
|
||||
/** Identifier of the job */
|
||||
public final int job;
|
||||
|
||||
/** Index of the task inside the job. */
|
||||
public final int task;
|
||||
|
||||
|
||||
public Task(int job, int task) {
|
||||
this.job = job;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Task task1 = (Task) o;
|
||||
return job == task1.job &&
|
||||
task == task1.task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(job, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Job " + this.job + " Task " + this.task;
|
||||
}
|
||||
|
||||
public Task add_one() {
|
||||
return new Task(this.job + 1, this.task + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
package jobshop.solvers;
|
||||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Solver;
|
||||
import jobshop.encodings.JobNumbers;
|
||||
|
||||
public class BasicSolver implements Solver {
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
|
||||
JobNumbers sol = new JobNumbers(instance);
|
||||
for(int t = 0 ; t<instance.numTasks ; t++) {
|
||||
for(int j = 0 ; j<instance.numJobs ; j++) {
|
||||
sol.jobs[sol.nextToSet++] = j;
|
||||
}
|
||||
}
|
||||
|
||||
return new Result(instance, sol.toSchedule(), Result.ExitCause.Blocked);
|
||||
}
|
||||
}
|
||||
package jobshop.solvers;
|
||||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Solver;
|
||||
import jobshop.encodings.JobNumbers;
|
||||
|
||||
public class BasicSolver implements Solver {
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
|
||||
JobNumbers sol = new JobNumbers(instance);
|
||||
for(int t = 0 ; t<instance.numTasks ; t++) {
|
||||
for(int j = 0 ; j<instance.numJobs ; j++) {
|
||||
sol.jobs[sol.nextToSet++] = j;
|
||||
}
|
||||
}
|
||||
|
||||
return new Result(instance, sol.toSchedule(), Result.ExitCause.Blocked);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
package jobshop.solvers;
|
||||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Solver;
|
||||
import jobshop.encodings.ResourceOrder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DescentSolver implements Solver {
|
||||
|
||||
/** A block represents a subsequence of the critical path such that all tasks in it execute on the same machine.
|
||||
* This class identifies a block in a ResourceOrder representation.
|
||||
*
|
||||
* Consider the solution in ResourceOrder representation
|
||||
* machine 0 : (0,1) (1,2) (2,2)
|
||||
* machine 1 : (0,2) (2,1) (1,1)
|
||||
* machine 2 : ...
|
||||
*
|
||||
* The block with : machine = 1, firstTask= 0 and lastTask = 1
|
||||
* Represent the task sequence : [(0,2) (2,1)]
|
||||
*
|
||||
* */
|
||||
static class Block {
|
||||
/** machine on which the block is identified */
|
||||
final int machine;
|
||||
/** index of the first task of the block */
|
||||
final int firstTask;
|
||||
/** index of the last task of the block */
|
||||
final int lastTask;
|
||||
|
||||
Block(int machine, int firstTask, int lastTask) {
|
||||
this.machine = machine;
|
||||
this.firstTask = firstTask;
|
||||
this.lastTask = lastTask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a swap of two tasks on the same machine in a ResourceOrder encoding.
|
||||
*
|
||||
* Consider the solution in ResourceOrder representation
|
||||
* machine 0 : (0,1) (1,2) (2,2)
|
||||
* machine 1 : (0,2) (2,1) (1,1)
|
||||
* machine 2 : ...
|
||||
*
|
||||
* The swam with : machine = 1, t1= 0 and t2 = 1
|
||||
* Represent inversion of the two tasks : (0,2) and (2,1)
|
||||
* Applying this swap on the above resource order should result in the following one :
|
||||
* machine 0 : (0,1) (1,2) (2,2)
|
||||
* machine 1 : (2,1) (0,2) (1,1)
|
||||
* machine 2 : ...
|
||||
*/
|
||||
static class Swap {
|
||||
// machine on which to perform the swap
|
||||
final int machine;
|
||||
// index of one task to be swapped
|
||||
final int t1;
|
||||
// index of the other task to be swapped
|
||||
final int t2;
|
||||
|
||||
Swap(int machine, int t1, int t2) {
|
||||
this.machine = machine;
|
||||
this.t1 = t1;
|
||||
this.t2 = t2;
|
||||
}
|
||||
|
||||
/** Apply this swap on the given resource order, transforming it into a new solution. */
|
||||
public void applyOn(ResourceOrder order) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** Returns a list of all blocks of the critical path. */
|
||||
List<Block> blocksOfCriticalPath(ResourceOrder order) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */
|
||||
List<Swap> neighbors(Block block) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
194
src/main/java/jobshop/solvers/GreedySolver.java
Normal file
194
src/main/java/jobshop/solvers/GreedySolver.java
Normal file
|
@ -0,0 +1,194 @@
|
|||
package jobshop.solvers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import jobshop.*;
|
||||
import jobshop.encodings.ResourceOrder;
|
||||
import jobshop.encodings.Task;
|
||||
import jobshop.solvers.GreedySolver.PriorityESTRule;
|
||||
|
||||
public class GreedySolver implements Solver {
|
||||
|
||||
/*********************************************************************************************/
|
||||
/******************* Priority Rules and EST Priority Rules enumerations **********************/
|
||||
/*********************************************************************************************/
|
||||
public enum PriorityRule{
|
||||
SPT {
|
||||
@Override
|
||||
public Task getTaskByRule(ArrayList<Task> achievableTasks, Instance instance) {
|
||||
// Return the task with the MINIMAL duration
|
||||
Task TaskSPT = null;
|
||||
int minDuration = Integer.MAX_VALUE;
|
||||
|
||||
for(int i = 0; i < achievableTasks.size(); i++) {
|
||||
Task current = achievableTasks.get(i);
|
||||
int currentDuration = instance.duration(current.job, current.task);
|
||||
|
||||
if(currentDuration < minDuration) {
|
||||
minDuration = currentDuration;
|
||||
TaskSPT = current;
|
||||
}
|
||||
}
|
||||
return TaskSPT;
|
||||
}
|
||||
},
|
||||
LRPT {
|
||||
@Override
|
||||
public Task getTaskByRule(ArrayList<Task> achievableTasks, Instance instance) {
|
||||
Task TaskLRPT = null;
|
||||
Task current;
|
||||
int maxDuration = -1;
|
||||
|
||||
for(int i = 0; i < achievableTasks.size(); i++) {
|
||||
current = achievableTasks.get(i);
|
||||
int remainingTime = 0;
|
||||
for(int j = current.task; j < instance.numTasks; j++) {
|
||||
remainingTime += instance.duration(current.job, j);
|
||||
}
|
||||
if(remainingTime > maxDuration) {
|
||||
maxDuration = remainingTime;
|
||||
TaskLRPT = current;
|
||||
}
|
||||
}
|
||||
return TaskLRPT;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns the Task to work with depending on the rule chosen
|
||||
* @param achievableTasks, instance
|
||||
* @return currentTask
|
||||
*/
|
||||
public abstract Task getTaskByRule(ArrayList<Task> achievableTasks, Instance instance);
|
||||
}
|
||||
|
||||
public enum PriorityESTRule{
|
||||
EST_SPT {
|
||||
@Override
|
||||
public Task getTaskByESTRule(ArrayList<Task> achievableTasks, Instance instance, int[] nextStartDateJobs, int[] nextStartDateMachines) {
|
||||
|
||||
ArrayList<Task> priorityTasks = this.getESTPriorityTasks(achievableTasks, instance, nextStartDateJobs, nextStartDateMachines);
|
||||
// STP function: Return the task with the MINIMAL duration
|
||||
PriorityRule SPT = PriorityRule.SPT;
|
||||
Task TaskSPT = SPT.getTaskByRule(priorityTasks, instance);
|
||||
|
||||
return TaskSPT;
|
||||
}
|
||||
},
|
||||
EST_LRPT {
|
||||
@Override
|
||||
public Task getTaskByESTRule(ArrayList<Task> achievableTasks, Instance instance, int[] nextStartDateJobs, int[] nextStartDateMachines) {
|
||||
ArrayList<Task> priorityTasks = this.getESTPriorityTasks(achievableTasks, instance, nextStartDateJobs, nextStartDateMachines);
|
||||
// STP function: Return the task with the MINIMAL duration
|
||||
PriorityRule LRPT = PriorityRule.LRPT;
|
||||
Task TaskLRPT = LRPT.getTaskByRule(priorityTasks, instance);
|
||||
|
||||
return TaskLRPT;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns the Task to work with depending on the rule chosen
|
||||
* @param achievableTasks, instance
|
||||
* @return currentTask
|
||||
*/
|
||||
public abstract Task getTaskByESTRule(ArrayList<Task> achievableTasks, Instance instance, int[] nextStartDateJobs, int[] nextStartDateMachines);
|
||||
/**
|
||||
* Returns the array of tasks with the shortest start date
|
||||
* @param achievableTasks, instance
|
||||
* @return priorityTask
|
||||
*/
|
||||
protected ArrayList<Task> getESTPriorityTasks(ArrayList<Task> achievableTasks, Instance instance, int[] nextStartDateJobs, int[] nextStartDateMachines){
|
||||
// Search for the date or dates which start sooner
|
||||
ArrayList<Task> priorityTasks = new ArrayList<>();
|
||||
int minStartDate = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
for(int i = 0; i < achievableTasks.size(); i++) {
|
||||
Task currentTask = achievableTasks.get(i);
|
||||
int currentMachine = instance.machine(currentTask.job, currentTask.task);
|
||||
int currentStartDate = Integer.max(nextStartDateJobs[currentTask.job], nextStartDateMachines[currentMachine]);
|
||||
|
||||
if(currentStartDate < minStartDate) {
|
||||
minStartDate = currentStartDate;
|
||||
// When we find a smaller start date we "restart" the array
|
||||
priorityTasks.clear();
|
||||
priorityTasks.add(currentTask);
|
||||
} else if (currentStartDate == minStartDate) {
|
||||
priorityTasks.add(currentTask);
|
||||
}
|
||||
}
|
||||
return priorityTasks;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
/********************** Greedy Solver: Constructors + Solve function *************************/
|
||||
/*********************************************************************************************/
|
||||
|
||||
public PriorityRule priorityRule;
|
||||
public PriorityESTRule priorityESTRule;
|
||||
|
||||
// 2 constructors: the default and one with the EST restriction
|
||||
public GreedySolver(PriorityRule rule) {
|
||||
this.priorityRule = rule;
|
||||
this.priorityESTRule = null;
|
||||
}
|
||||
|
||||
public GreedySolver(PriorityESTRule ruleEST) {
|
||||
this.priorityESTRule = ruleEST;
|
||||
this.priorityRule = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
|
||||
int currentMachine, currentDuration;
|
||||
// We declare 2 arrays containing the updated moment the next task will start in a job and a machine respectively
|
||||
int[] nextStartDateJobs = new int[instance.numJobs];
|
||||
int[] nextStartDateMachines = new int[instance.numMachines];
|
||||
|
||||
// We create a new ResourceOrder for putting all tasks in the schedule
|
||||
ResourceOrder solution = new ResourceOrder(instance);
|
||||
// Array list with all the achievable current tasks
|
||||
ArrayList<Task> achievableTasks = new ArrayList<>();
|
||||
|
||||
// Initialize the array list with all the first task achievable
|
||||
for(int i = 0 ; i < instance.numJobs ; i++) {
|
||||
Task currentTask = new Task(i, 0);
|
||||
achievableTasks.add(currentTask);
|
||||
}
|
||||
|
||||
while(!achievableTasks.isEmpty()) {
|
||||
// We take the task we should do now in function of the priority rule used
|
||||
Task currentTask = null;
|
||||
if(priorityESTRule == null) {
|
||||
currentTask = priorityRule.getTaskByRule(achievableTasks, instance);
|
||||
} else if(priorityRule == null) {
|
||||
currentTask = priorityESTRule.getTaskByESTRule(achievableTasks, instance, nextStartDateJobs, nextStartDateMachines);
|
||||
} else {
|
||||
System.out.printf("Error priorityRule and priorityRuleEST are null. You must give a value to one of them.");
|
||||
}
|
||||
|
||||
// Updating starting dates
|
||||
currentMachine = instance.machine(currentTask.job, currentTask.task);
|
||||
currentDuration = instance.duration(currentTask.job, currentTask.task);
|
||||
nextStartDateJobs[currentTask.job] += currentDuration;
|
||||
nextStartDateMachines[currentMachine] += currentDuration;
|
||||
|
||||
// We remove the current task from the achievable tasks list
|
||||
achievableTasks.remove(currentTask);
|
||||
|
||||
// If it's not the last task of the job, we update the array list with the new task
|
||||
if (currentTask.task < (instance.numTasks - 1)) {
|
||||
achievableTasks.add(new Task(currentTask.job, currentTask.task + 1));
|
||||
}
|
||||
|
||||
// We add the current task to the solution
|
||||
int nextFreeSlot = solution.nextFreeSlot[currentMachine]++;
|
||||
solution.tasksByMachine[currentMachine][nextFreeSlot] = currentTask;
|
||||
}
|
||||
|
||||
return new Result(instance, solution.toSchedule(), Result.ExitCause.Blocked);
|
||||
}
|
||||
}
|
|
@ -1,52 +1,53 @@
|
|||
package jobshop.solvers;
|
||||
|
||||
import jobshop.*;
|
||||
import jobshop.encodings.JobNumbers;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomSolver implements Solver {
|
||||
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
Random generator = new Random(0);
|
||||
|
||||
JobNumbers sol = new JobNumbers(instance);
|
||||
|
||||
for(int j = 0 ; j<instance.numJobs ; j++) {
|
||||
for(int t = 0 ; t<instance.numTasks ; t++) {
|
||||
sol.jobs[sol.nextToSet++] = j;
|
||||
}
|
||||
}
|
||||
Schedule best = sol.toSchedule();
|
||||
while(deadline - System.currentTimeMillis() > 1) {
|
||||
shuffleArray(sol.jobs, generator);
|
||||
Schedule s = sol.toSchedule();
|
||||
if(s.makespan() < best.makespan()) {
|
||||
best = s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Result(instance, best, Result.ExitCause.Timeout);
|
||||
}
|
||||
|
||||
/** Simple Fisher–Yates array shuffling */
|
||||
private static void shuffleArray(int[] array, Random random)
|
||||
{
|
||||
int index;
|
||||
for (int i = array.length - 1; i > 0; i--)
|
||||
{
|
||||
index = random.nextInt(i + 1);
|
||||
if (index != i)
|
||||
{
|
||||
array[index] ^= array[i];
|
||||
array[i] ^= array[index];
|
||||
array[index] ^= array[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package jobshop.solvers;
|
||||
|
||||
import jobshop.*;
|
||||
import jobshop.encodings.JobNumbers;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class RandomSolver implements Solver {
|
||||
|
||||
@Override
|
||||
public Result solve(Instance instance, long deadline) {
|
||||
Random generator = new Random(0);
|
||||
|
||||
JobNumbers sol = new JobNumbers(instance);
|
||||
|
||||
for(int j = 0 ; j<instance.numJobs ; j++) {
|
||||
for(int t = 0 ; t<instance.numTasks ; t++) {
|
||||
sol.jobs[sol.nextToSet++] = j;
|
||||
}
|
||||
}
|
||||
Schedule best = sol.toSchedule();
|
||||
while(deadline - System.currentTimeMillis() > 1) {
|
||||
shuffleArray(sol.jobs, generator);
|
||||
Schedule s = sol.toSchedule();
|
||||
if(s.makespan() < best.makespan()) {
|
||||
best = s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Result(instance, best, Result.ExitCause.Timeout);
|
||||
}
|
||||
|
||||
/** Simple Fisher–Yates array shuffling */
|
||||
private static void shuffleArray(int[] array, Random random)
|
||||
{
|
||||
int index;
|
||||
for (int i = array.length - 1; i > 0; i--)
|
||||
{
|
||||
index = random.nextInt(i + 1);
|
||||
if (index != i)
|
||||
{
|
||||
array[index] ^= array[i];
|
||||
array[i] ^= array[index];
|
||||
array[index] ^= array[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,75 +1,103 @@
|
|||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Schedule;
|
||||
import jobshop.Solver;
|
||||
import jobshop.solvers.BasicSolver;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class EncodingTests {
|
||||
|
||||
@Test
|
||||
public void testJobNumbers() throws IOException {
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// numéro de jobs : 1 2 2 1 1 2 (cf exercices)
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
// TODO: make it print something meaningful
|
||||
// by implementing the toString() method
|
||||
System.out.println(sched);
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 12;
|
||||
|
||||
|
||||
|
||||
// numéro de jobs : 1 1 2 2 1 2
|
||||
enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
sched = enc.toSchedule();
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 14;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicSolver() throws IOException {
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// build a solution that should be equal to the result of BasicSolver
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 12;
|
||||
|
||||
Solver solver = new BasicSolver();
|
||||
Result result = solver.solve(instance, System.currentTimeMillis() + 10);
|
||||
|
||||
assert result.schedule.isValid();
|
||||
assert result.schedule.makespan() == sched.makespan(); // should have the same makespan
|
||||
}
|
||||
|
||||
}
|
||||
package jobshop.encodings;
|
||||
|
||||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
import jobshop.Schedule;
|
||||
import jobshop.Solver;
|
||||
import jobshop.solvers.BasicSolver;
|
||||
import jobshop.solvers.GreedySolver;
|
||||
import jobshop.solvers.GreedySolver.PriorityRule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class EncodingTests {
|
||||
|
||||
@Test
|
||||
public void testJobNumbers() throws IOException {
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// numéro de jobs : 1 2 2 1 1 2 (cf exercices)
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
// TODO: make it print something meaningful
|
||||
// by implementing the toString() method
|
||||
System.out.println(sched);
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 12;
|
||||
|
||||
|
||||
|
||||
// numéro de jobs : 1 1 2 2 1 2
|
||||
enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
sched = enc.toSchedule();
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 14;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicSolver() throws IOException {
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// build a solution that should be equal to the result of BasicSolver
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 12;
|
||||
|
||||
Solver solver = new BasicSolver();
|
||||
Result result = solver.solve(instance, System.currentTimeMillis() + 10);
|
||||
|
||||
assert result.schedule.isValid();
|
||||
assert result.schedule.makespan() == sched.makespan(); // should have the same makespan
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreedySolver() throws IOException {
|
||||
Instance instance = Instance.fromFile(Paths.get("instances/aaa1"));
|
||||
|
||||
// build a solution that should be equal to the result of BasicSolver
|
||||
JobNumbers enc = new JobNumbers(instance);
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 12;
|
||||
|
||||
PriorityRule priorityRule = PriorityRule.SPT;
|
||||
Solver solver = new GreedySolver(priorityRule);
|
||||
Result result = solver.solve(instance, System.currentTimeMillis() + 10);
|
||||
|
||||
assert result.schedule.isValid();
|
||||
assert result.schedule.makespan() == sched.makespan(); // should have the same makespan
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue