Make Encoding.toSchedule() return an optional to handle the case where the solution is not valid.
This commit is contained in:
parent
fd9fa99913
commit
1957a255ba
10 changed files with 56 additions and 35 deletions
|
@ -49,7 +49,7 @@ public class Instance {
|
||||||
return this.machine(t.job, t.task);
|
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) {
|
public int task_with_machine(int job, int wanted_machine) {
|
||||||
for(int task = 0 ; task < numTasks ; task++) {
|
for(int task = 0 ; task < numTasks ; task++) {
|
||||||
if(machine(job, task) == wanted_machine)
|
if(machine(job, task) == wanted_machine)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jobshop.encodings.Schedule;
|
||||||
import jobshop.solvers.*;
|
import jobshop.solvers.*;
|
||||||
import net.sourceforge.argparse4j.ArgumentParsers;
|
import net.sourceforge.argparse4j.ArgumentParsers;
|
||||||
import net.sourceforge.argparse4j.inf.ArgumentParser;
|
import net.sourceforge.argparse4j.inf.ArgumentParser;
|
||||||
|
@ -122,13 +123,15 @@ public class Main {
|
||||||
long runtime = System.currentTimeMillis() - start;
|
long runtime = System.currentTimeMillis() - start;
|
||||||
|
|
||||||
// check that the solver returned a valid solution
|
// 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.err.println("ERROR: solver returned an invalid schedule");
|
||||||
System.exit(1); // bug in implementation, bail out
|
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.
|
// 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;
|
float dist = 100f * (makespan - bestKnown) / (float) bestKnown;
|
||||||
avg_runtimes[solverId] += (float) runtime / (float) instances.size();
|
avg_runtimes[solverId] += (float) runtime / (float) instances.size();
|
||||||
avg_distances[solverId] += dist / (float) instances.size();
|
avg_distances[solverId] += dist / (float) instances.size();
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class MainTest {
|
||||||
System.out.println("\nENCODING: " + enc);
|
System.out.println("\nENCODING: " + enc);
|
||||||
|
|
||||||
// convert to a schedule and display
|
// convert to a schedule and display
|
||||||
Schedule schedule = enc.toSchedule();
|
Schedule schedule = enc.toSchedule().get();
|
||||||
System.out.println("VALID: " + schedule.isValid());
|
System.out.println("VALID: " + schedule.isValid());
|
||||||
System.out.println("MAKESPAN: " + schedule.makespan());
|
System.out.println("MAKESPAN: " + schedule.makespan());
|
||||||
System.out.println("SCHEDULE: " + schedule.toString());
|
System.out.println("SCHEDULE: " + schedule.toString());
|
||||||
|
|
|
@ -2,10 +2,22 @@ package jobshop;
|
||||||
|
|
||||||
import jobshop.encodings.Schedule;
|
import jobshop.encodings.Schedule;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/** Class representing the result of a solver. */
|
/** Class representing the result of a solver. */
|
||||||
public class Result {
|
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.instance = instance;
|
||||||
this.schedule = schedule;
|
this.schedule = schedule;
|
||||||
this.cause = cause;
|
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. */
|
/** The solver was not able to further improve the solution (e.g. blocked in a local minima. */
|
||||||
Blocked
|
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 jobshop.Instance;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/** Common class for all encodings.
|
/** Common class for all encodings.
|
||||||
*
|
*
|
||||||
* The only requirement for this class is to provide a conversion from the encoding into a Schedule.
|
* 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. */
|
/** Problem instance of which this is the solution. */
|
||||||
public final Instance instance;
|
public final Instance instance;
|
||||||
|
|
||||||
|
/** Constructor, that initializes the instance field. */
|
||||||
public Encoding(Instance instance) {
|
public Encoding(Instance instance) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert into a schedule. */
|
/** Attempts to convert this solution into a schedule.
|
||||||
public abstract Schedule toSchedule();
|
*
|
||||||
|
* @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.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
/** Encoding of the solution of a jobshop problem by job numbers. */
|
/** Encoding of the solution of a jobshop problem by job numbers. */
|
||||||
|
@ -56,7 +57,7 @@ public class JobNumbers extends Encoding {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Schedule toSchedule() {
|
public Optional<Schedule> toSchedule() {
|
||||||
// time at which each machine is going to be freed
|
// time at which each machine is going to be freed
|
||||||
int[] nextFreeTimeResource = new int[instance.numMachines];
|
int[] nextFreeTimeResource = new int[instance.numMachines];
|
||||||
|
|
||||||
|
@ -79,7 +80,7 @@ public class JobNumbers extends Encoding {
|
||||||
nextTask[job] = task + 1;
|
nextTask[job] = task + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return schedule;
|
return Optional.of(schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,6 +2,7 @@ package jobshop.encodings;
|
||||||
|
|
||||||
import jobshop.Instance;
|
import jobshop.Instance;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
@ -85,7 +86,7 @@ public class ResourceOrder extends Encoding {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Schedule toSchedule() {
|
public Optional<Schedule> toSchedule() {
|
||||||
// indicate for each task that have been scheduled, its start time
|
// indicate for each task that have been scheduled, its start time
|
||||||
Schedule schedule = new Schedule(instance);
|
Schedule schedule = new Schedule(instance);
|
||||||
|
|
||||||
|
@ -130,16 +131,21 @@ public class ResourceOrder extends Encoding {
|
||||||
releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task);
|
releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task);
|
||||||
} else {
|
} else {
|
||||||
// no tasks are schedulable, there is no solution for this resource ordering
|
// 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
|
// we exited the loop : all tasks have been scheduled successfully
|
||||||
return schedule;
|
return Optional.of(schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an exact copy of this resource order. */
|
/** Creates an exact copy of this resource order. */
|
||||||
public ResourceOrder copy() {
|
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
|
@Override
|
||||||
|
|
|
@ -238,7 +238,7 @@ public class Schedule extends Encoding {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Schedule toSchedule() {
|
public Optional<Schedule> toSchedule() {
|
||||||
return this;
|
return Optional.of(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import jobshop.*;
|
||||||
import jobshop.encodings.JobNumbers;
|
import jobshop.encodings.JobNumbers;
|
||||||
import jobshop.encodings.Schedule;
|
import jobshop.encodings.Schedule;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/** A solver that generates random solutions until a deadline is met.
|
/** 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;
|
sol.jobs[sol.nextToSet++] = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Schedule best = sol.toSchedule();
|
Optional<Schedule> best = sol.toSchedule();
|
||||||
while(deadline - System.currentTimeMillis() > 1) {
|
while(deadline - System.currentTimeMillis() > 1) {
|
||||||
shuffleArray(sol.jobs, generator);
|
shuffleArray(sol.jobs, generator);
|
||||||
Schedule s = sol.toSchedule();
|
Optional<Schedule> candidate = sol.toSchedule();
|
||||||
if(s.makespan() < best.makespan()) {
|
if(candidate.isPresent()) {
|
||||||
best = s;
|
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++] = 0;
|
||||||
enc.jobs[enc.nextToSet++] = 1;
|
enc.jobs[enc.nextToSet++] = 1;
|
||||||
|
|
||||||
Schedule sched = enc.toSchedule();
|
Schedule sched = enc.toSchedule().get();
|
||||||
// TODO: make it print something meaningful
|
// TODO: make it print something meaningful
|
||||||
// by implementing the toString() method
|
// by implementing the toString() method
|
||||||
System.out.println(sched);
|
System.out.println(sched);
|
||||||
|
@ -42,7 +42,7 @@ public class EncodingTests {
|
||||||
enc.jobs[enc.nextToSet++] = 0;
|
enc.jobs[enc.nextToSet++] = 0;
|
||||||
enc.jobs[enc.nextToSet++] = 1;
|
enc.jobs[enc.nextToSet++] = 1;
|
||||||
|
|
||||||
sched = enc.toSchedule();
|
sched = enc.toSchedule().get();
|
||||||
assert sched.isValid();
|
assert sched.isValid();
|
||||||
assert sched.makespan() == 14;
|
assert sched.makespan() == 14;
|
||||||
}
|
}
|
||||||
|
@ -60,15 +60,15 @@ public class EncodingTests {
|
||||||
enc.jobs[enc.nextToSet++] = 0;
|
enc.jobs[enc.nextToSet++] = 0;
|
||||||
enc.jobs[enc.nextToSet++] = 1;
|
enc.jobs[enc.nextToSet++] = 1;
|
||||||
|
|
||||||
Schedule sched = enc.toSchedule();
|
Schedule sched = enc.toSchedule().get();
|
||||||
assert sched.isValid();
|
assert sched.isValid();
|
||||||
assert sched.makespan() == 12;
|
assert sched.makespan() == 12;
|
||||||
|
|
||||||
Solver solver = new BasicSolver();
|
Solver solver = new BasicSolver();
|
||||||
Result result = solver.solve(instance, System.currentTimeMillis() + 10);
|
Result result = solver.solve(instance, System.currentTimeMillis() + 10);
|
||||||
|
|
||||||
assert result.schedule.isValid();
|
assert result.schedule.get().isValid();
|
||||||
assert result.schedule.makespan() == sched.makespan(); // should have the same makespan
|
assert result.schedule.get().makespan() == sched.makespan(); // should have the same makespan
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue