Make Encoding.toSchedule() return an optional to handle the case where the solution is not valid.
This commit is contained in:
förälder
fd9fa99913
incheckning
1957a255ba
10 ändrade filer med 56 tillägg och 35 borttagningar
|
@ -49,7 +49,7 @@ public class Instance {
|
|||
return this.machine(t.job, t.task);
|
||||
}
|
||||
|
||||
/** among the tasks of the given job, returns the task index that uses the given machine. */
|
||||
/** Among the tasks of the given job, returns the task number of the one 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)
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jobshop.encodings.Schedule;
|
||||
import jobshop.solvers.*;
|
||||
import net.sourceforge.argparse4j.ArgumentParsers;
|
||||
import net.sourceforge.argparse4j.inf.ArgumentParser;
|
||||
|
@ -122,13 +123,15 @@ public class Main {
|
|||
long runtime = System.currentTimeMillis() - start;
|
||||
|
||||
// check that the solver returned a valid solution
|
||||
if(!result.schedule.isValid()) {
|
||||
if(result.schedule.isEmpty() || !result.schedule.get().isValid()) {
|
||||
System.err.println("ERROR: solver returned an invalid schedule");
|
||||
System.exit(1); // bug in implementation, bail out
|
||||
}
|
||||
// we have a valid schedule
|
||||
Schedule schedule = result.schedule.get();
|
||||
|
||||
// compute some statistics on the solution and print them.
|
||||
int makespan = result.schedule.makespan();
|
||||
int makespan = schedule.makespan();
|
||||
float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
|
||||
avg_runtimes[solverId] += (float) runtime / (float) instances.size();
|
||||
avg_distances[solverId] += dist / (float) instances.size();
|
||||
|
|
|
@ -28,7 +28,7 @@ public class MainTest {
|
|||
System.out.println("\nENCODING: " + enc);
|
||||
|
||||
// convert to a schedule and display
|
||||
Schedule schedule = enc.toSchedule();
|
||||
Schedule schedule = enc.toSchedule().get();
|
||||
System.out.println("VALID: " + schedule.isValid());
|
||||
System.out.println("MAKESPAN: " + schedule.makespan());
|
||||
System.out.println("SCHEDULE: " + schedule.toString());
|
||||
|
|
|
@ -2,10 +2,22 @@ package jobshop;
|
|||
|
||||
import jobshop.encodings.Schedule;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/** Class representing the result of a solver. */
|
||||
public class Result {
|
||||
|
||||
public Result(Instance instance, Schedule schedule, ExitCause cause) {
|
||||
/** Instance that was solved. */
|
||||
public final Instance instance;
|
||||
|
||||
/** A schedule of the solution or Optional.empty() if no solution was found. */
|
||||
public final Optional<Schedule> schedule;
|
||||
|
||||
/** Reason why the solver exited with this solution. */
|
||||
public final ExitCause cause;
|
||||
|
||||
/** Creates a new Result object with the corresponding fields. */
|
||||
public Result(Instance instance, Optional<Schedule> schedule, ExitCause cause) {
|
||||
this.instance = instance;
|
||||
this.schedule = schedule;
|
||||
this.cause = cause;
|
||||
|
@ -20,15 +32,4 @@ public class Result {
|
|||
/** The solver was not able to further improve the solution (e.g. blocked in a local minima. */
|
||||
Blocked
|
||||
}
|
||||
|
||||
/** Instance that was solved. */
|
||||
public final Instance instance;
|
||||
|
||||
/** A schedule of the solution or null if no solution was found. */
|
||||
public final Schedule schedule;
|
||||
|
||||
/** Reason why the solver exited with this solution. */
|
||||
public final ExitCause cause;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package jobshop.encodings;
|
|||
|
||||
import jobshop.Instance;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/** Common class for all encodings.
|
||||
*
|
||||
* The only requirement for this class is to provide a conversion from the encoding into a Schedule.
|
||||
|
@ -11,10 +13,15 @@ public abstract class Encoding {
|
|||
/** Problem instance of which this is the solution. */
|
||||
public final Instance instance;
|
||||
|
||||
/** Constructor, that initializes the instance field. */
|
||||
public Encoding(Instance instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
/** Convert into a schedule. */
|
||||
public abstract Schedule toSchedule();
|
||||
/** Attempts to convert this solution into a schedule.
|
||||
*
|
||||
* @return A empty optional if the solution is not valid. Otherwise the optional will contain a valid schedule of
|
||||
* the solution.
|
||||
*/
|
||||
public abstract Optional<Schedule> toSchedule();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import jobshop.Instance;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/** Encoding of the solution of a jobshop problem by job numbers. */
|
||||
|
@ -56,7 +57,7 @@ public class JobNumbers extends Encoding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
public Optional<Schedule> toSchedule() {
|
||||
// time at which each machine is going to be freed
|
||||
int[] nextFreeTimeResource = new int[instance.numMachines];
|
||||
|
||||
|
@ -79,7 +80,7 @@ public class JobNumbers extends Encoding {
|
|||
nextTask[job] = task + 1;
|
||||
}
|
||||
|
||||
return schedule;
|
||||
return Optional.of(schedule);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,6 +2,7 @@ package jobshop.encodings;
|
|||
|
||||
import jobshop.Instance;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
@ -85,7 +86,7 @@ public class ResourceOrder extends Encoding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
public Optional<Schedule> toSchedule() {
|
||||
// indicate for each task that have been scheduled, its start time
|
||||
Schedule schedule = new Schedule(instance);
|
||||
|
||||
|
@ -130,16 +131,21 @@ public class ResourceOrder extends Encoding {
|
|||
releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task);
|
||||
} else {
|
||||
// no tasks are schedulable, there is no solution for this resource ordering
|
||||
return null;
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
// we exited the loop : all tasks have been scheduled successfully
|
||||
return schedule;
|
||||
return Optional.of(schedule);
|
||||
}
|
||||
|
||||
/** Creates an exact copy of this resource order. */
|
||||
public ResourceOrder copy() {
|
||||
return new ResourceOrder(this.toSchedule());
|
||||
var schedule = this.toSchedule();
|
||||
if (schedule.isEmpty()) {
|
||||
throw new RuntimeException("Cannot copy an invalid ResourceOrder");
|
||||
} else {
|
||||
return new ResourceOrder(schedule.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -238,7 +238,7 @@ public class Schedule extends Encoding {
|
|||
|
||||
|
||||
@Override
|
||||
public Schedule toSchedule() {
|
||||
return this;
|
||||
public Optional<Schedule> toSchedule() {
|
||||
return Optional.of(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import jobshop.*;
|
|||
import jobshop.encodings.JobNumbers;
|
||||
import jobshop.encodings.Schedule;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
/** A solver that generates random solutions until a deadline is met.
|
||||
|
@ -22,12 +23,14 @@ public class RandomSolver implements Solver {
|
|||
sol.jobs[sol.nextToSet++] = j;
|
||||
}
|
||||
}
|
||||
Schedule best = sol.toSchedule();
|
||||
Optional<Schedule> best = sol.toSchedule();
|
||||
while(deadline - System.currentTimeMillis() > 1) {
|
||||
shuffleArray(sol.jobs, generator);
|
||||
Schedule s = sol.toSchedule();
|
||||
if(s.makespan() < best.makespan()) {
|
||||
best = s;
|
||||
Optional<Schedule> candidate = sol.toSchedule();
|
||||
if(candidate.isPresent()) {
|
||||
if (best.isEmpty() || candidate.get().makespan() < best.get().makespan()) {
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ public class EncodingTests {
|
|||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
Schedule sched = enc.toSchedule().get();
|
||||
// TODO: make it print something meaningful
|
||||
// by implementing the toString() method
|
||||
System.out.println(sched);
|
||||
|
@ -42,7 +42,7 @@ public class EncodingTests {
|
|||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
sched = enc.toSchedule();
|
||||
sched = enc.toSchedule().get();
|
||||
assert sched.isValid();
|
||||
assert sched.makespan() == 14;
|
||||
}
|
||||
|
@ -60,15 +60,15 @@ public class EncodingTests {
|
|||
enc.jobs[enc.nextToSet++] = 0;
|
||||
enc.jobs[enc.nextToSet++] = 1;
|
||||
|
||||
Schedule sched = enc.toSchedule();
|
||||
Schedule sched = enc.toSchedule().get();
|
||||
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
|
||||
assert result.schedule.get().isValid();
|
||||
assert result.schedule.get().makespan() == sched.makespan(); // should have the same makespan
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Laddar…
Referens i nytt ärende