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