Complete documentation + equals/hashCode for encodings
Tento commit je obsažen v:
rodič
1957a255ba
revize
c98accd114
14 změnil soubory, kde provedl 92 přidání a 31 odebrání
|
@ -2,10 +2,11 @@
|
|||
|
||||
Several encoding and associated utilities are provided in the `jobshop.encodings` package.
|
||||
The abstract class `Encoding` provides a common interface for all encodings.
|
||||
The only requirement for an encoding is to transform it self into a `Schedule`.
|
||||
|
||||
## Schedule
|
||||
|
||||
The `Schedule` has direct encoding of a solution: it associates every task in the jobshop to a start time.
|
||||
The `Schedule` is direct encoding of a solution: it associates every task in the jobshop instance to a start time.
|
||||
|
||||
It plays a particular role as it is the standard way of representing a solution. As a consequence, all other encodings must provide a way to produce a schedule.
|
||||
|
||||
|
@ -19,7 +20,7 @@ Convenience methods:
|
|||
|
||||
## NumJobs
|
||||
|
||||
The `NumJobs` encoding consists of a sequence of job numbers. To produce a schedule, one should iterate on the job numbers and tries to schedule *as early as possible* the next task of the job.
|
||||
The `NumJobs` encoding consists of a sequence of job numbers. To produce a schedule, one should iterate on the job numbers and try to schedule *as early as possible* the next task of the job.
|
||||
|
||||
For instance the encoding `[0 0 1 1 0 1]` will produce a schedule by trying to place as early as possible the following tasks (in this order):
|
||||
|
||||
|
@ -35,7 +36,7 @@ Convenience methods:
|
|||
|
||||
The resource order encoding specifies the order in which each machine will process its tasks.
|
||||
|
||||
Each machine is associated with an array of task that specifies the order on which the task must be scheduled on the machine.
|
||||
Each machine is associated with an array of tasks that specifies the order on which the tasks must be scheduled on the machine.
|
||||
|
||||
For instance, the encoding:
|
||||
|
||||
|
@ -45,4 +46,6 @@ For instance, the encoding:
|
|||
machine 2: [(1, 2), (0, 2)]
|
||||
```
|
||||
|
||||
Specifies that the first machine (machine 0) will first process the second task of the first job `(0, 1)` and only when it is finished can start processing the first task of the second job `(1, 0)`.
|
||||
Specifies that the first machine (machine 0) will first process the second task of the first job `(0, 1)` and only when it is finished can start processing the first task of the second job `(1, 0)`.
|
||||
|
||||
Unlike `JobNumbers`, the `ResourceOrder` encoding might represent invalid solutions. In this case, its `toSchedule()` method will return an empty result.
|
|
@ -4,14 +4,19 @@
|
|||
|
||||
`jobshop.solvers.Solver` provides a common interface for all solvers.
|
||||
|
||||
Implementing the `Solver` interface requires implementing a method `solve(Instance instance, long deadline)` where:
|
||||
|
||||
## Basic solver
|
||||
- `instance` is the jobshop instance that should be solved.
|
||||
- `deadline` is the absolute time by which the solver should have exited. This deadline is in milliseconds and can be compared with the result of `System.currentTimeMillis()`.
|
||||
|
||||
The `solve()` method should return a `Result` object, that provides the found solution as a `Schedule` and the cause for exiting.
|
||||
|
||||
## `BasicSolver`
|
||||
|
||||
A very simple solver that tries to schedule all first tasks, then all second tasks, then all third tasks, ...
|
||||
It does so using the `JobNumbers` encoding.
|
||||
|
||||
It does so using the `JobNumbers` encoding
|
||||
|
||||
## Random solver
|
||||
## `RandomSolver`
|
||||
|
||||
Another very simple solver based on the `JobNumbers` encoding.
|
||||
At each iteration, the solver generates a new random solution keeps it if it the best one found so far.
|
||||
|
@ -19,14 +24,11 @@ At each iteration, the solver generates a new random solution keeps it if it the
|
|||
It repeats this process until the deadline to produce a result is met and finally returns the best solution found.
|
||||
|
||||
|
||||
## Greedy solver
|
||||
## `GreedySolver`
|
||||
|
||||
The greedy solver is not implemented yet.
|
||||
Its constructor accepts a parameter that specifies the priority that should be used to produce solutions.
|
||||
|
||||
## Descent Solver
|
||||
## `DescentSolver`
|
||||
|
||||
|
||||
### Neighborhoods
|
||||
|
||||
TODO
|
||||
Not implemented yet. It should use the *Nowicki and Smutnicki* neighborhood for which some initial code is provided in the `jobshop.solver.neighborhood` package.
|
|
@ -10,7 +10,7 @@ import java.util.stream.Collectors;
|
|||
* Note that the best known result might not have been proven to be the optimal solution
|
||||
* for the instance.
|
||||
*/
|
||||
public class BestKnownResults {
|
||||
public final class BestKnownResults {
|
||||
|
||||
/**
|
||||
* Checks whether we have data available for the provided instance.
|
||||
|
@ -35,7 +35,7 @@ public class BestKnownResults {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the best known result for the given instance name.
|
||||
* Returns the best known result for the given instance.
|
||||
* @param instanceName Instance of which we want to retrieve the best result.
|
||||
* @return Best makespan that has ever been found for this instance.
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,8 @@ import java.util.Iterator;
|
|||
import java.util.Scanner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Instance {
|
||||
/** Represents an instance of a JobShop problem. */
|
||||
public final class Instance {
|
||||
|
||||
/** Name of the instance. Same as the filename from which the instance is loaded. */
|
||||
public final String name;
|
||||
|
|
|
@ -9,6 +9,7 @@ import jobshop.solvers.GreedySolver;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/** A java main classes for testing purposes. */
|
||||
public class MainTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -4,11 +4,12 @@ import jobshop.Instance;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/** Encoding of the solution of a jobshop problem by job numbers. */
|
||||
public class JobNumbers extends Encoding {
|
||||
public final class JobNumbers extends Encoding {
|
||||
|
||||
/** A numJobs * numTasks array containing the representation by job numbers. */
|
||||
public final int[] jobs;
|
||||
|
@ -25,7 +26,7 @@ public class JobNumbers extends Encoding {
|
|||
Arrays.fill(jobs, -1);
|
||||
}
|
||||
|
||||
/** Cerates a new encoding based on the given schedule. */
|
||||
/** Creates a new encoding based on the given schedule. */
|
||||
public JobNumbers(Schedule schedule) {
|
||||
super(schedule.instance);
|
||||
|
||||
|
@ -87,4 +88,19 @@ public class JobNumbers extends Encoding {
|
|||
public String toString() {
|
||||
return Arrays.toString(Arrays.copyOfRange(jobs,0, nextToSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
JobNumbers that = (JobNumbers) o;
|
||||
return nextToSet == that.nextToSet && Arrays.equals(jobs, that.jobs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(nextToSet);
|
||||
result = 31 * result + Arrays.hashCode(jobs);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ import java.util.Comparator;
|
|||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class ResourceOrder extends Encoding {
|
||||
/** Encoding of a solution by the ordering of tasks on each machine. */
|
||||
public final 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
|
||||
|
@ -55,9 +56,11 @@ public class ResourceOrder extends Encoding {
|
|||
/** Enqueues a task for the given job on the machine. We automatically, find the task
|
||||
* that must be executed on this particular machine. */
|
||||
public void addToMachine(int machine, int jobNumber) {
|
||||
addTaskToMachine(machine, new Task(jobNumber, instance.task_with_machine(jobNumber, machine)));
|
||||
Task taskToEnqueue = new Task(jobNumber, instance.task_with_machine(jobNumber, machine));
|
||||
addTaskToMachine(machine, taskToEnqueue);
|
||||
}
|
||||
|
||||
/** Adds the given task to the queue of the given machine. */
|
||||
public void addTaskToMachine(int machine, Task task) {
|
||||
tasksByMachine[machine][nextFreeSlot[machine]] = task;
|
||||
nextFreeSlot[machine] += 1;
|
||||
|
@ -76,8 +79,8 @@ public class ResourceOrder extends Encoding {
|
|||
/** Exchange the order of two tasks that are scheduled on a given machine.
|
||||
*
|
||||
* @param machine Machine on which the two tasks appear (line on which to perform the exchange)
|
||||
* @param indexTask1 Position of the first task on the machine (column of the first element)
|
||||
* @param indexTask2 Position of the second task on the machine (column of the second element)
|
||||
* @param indexTask1 Position of the first task in the machine's queue
|
||||
* @param indexTask2 Position of the second task in the machine's queue
|
||||
*/
|
||||
public void swapTasks(int machine, int indexTask1, int indexTask2) {
|
||||
Task tmp = tasksByMachine[machine][indexTask1];
|
||||
|
@ -138,7 +141,10 @@ public class ResourceOrder extends Encoding {
|
|||
return Optional.of(schedule);
|
||||
}
|
||||
|
||||
/** Creates an exact copy of this resource order. */
|
||||
/** Creates an exact copy of this resource order.
|
||||
*
|
||||
* May fail if the resource order does not represent a valid solution.
|
||||
*/
|
||||
public ResourceOrder copy() {
|
||||
var schedule = this.toSchedule();
|
||||
if (schedule.isEmpty()) {
|
||||
|
@ -165,4 +171,18 @@ public class ResourceOrder extends Encoding {
|
|||
return s.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ResourceOrder that = (ResourceOrder) o;
|
||||
return Arrays.deepEquals(tasksByMachine, that.tasksByMachine) && Arrays.equals(nextFreeSlot, that.nextFreeSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(tasksByMachine);
|
||||
result = 31 * result + Arrays.hashCode(nextFreeSlot);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ public final class Task {
|
|||
/** Index of the task inside the job. */
|
||||
public final int task;
|
||||
|
||||
|
||||
/** Creates a new Task object (job, task). */
|
||||
public Task(int job, int task) {
|
||||
this.job = job;
|
||||
this.task = task;
|
||||
|
|
|
@ -14,7 +14,7 @@ public class BasicSolver implements Solver {
|
|||
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;
|
||||
sol.addTask(j);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,17 @@ import jobshop.Result;
|
|||
import jobshop.encodings.ResourceOrder;
|
||||
import jobshop.solvers.neighborhood.Neighborhood;
|
||||
|
||||
/** An empty shell to implement a descent solver. */
|
||||
public class DescentSolver implements Solver {
|
||||
|
||||
final Neighborhood<ResourceOrder> neighborhood;
|
||||
final Solver baseSolver;
|
||||
|
||||
/** Creates a new descent solver with a given neighborhood and a solver for the initial solution.
|
||||
*
|
||||
* @param neighborhood Neighborhood object that should be used to generates neighbor solutions to the current candidate.
|
||||
* @param baseSolver A solver to provide the initial solution.
|
||||
*/
|
||||
public DescentSolver(Neighborhood<ResourceOrder> neighborhood, Solver baseSolver) {
|
||||
this.neighborhood = neighborhood;
|
||||
this.baseSolver = baseSolver;
|
||||
|
|
|
@ -3,13 +3,18 @@ package jobshop.solvers;
|
|||
import jobshop.Instance;
|
||||
import jobshop.Result;
|
||||
|
||||
/** An empty shell to implement a greedy solver. */
|
||||
public class GreedySolver implements Solver {
|
||||
|
||||
/** All possible priorities for the greedy solver. */
|
||||
public enum Priority {
|
||||
SPT, LPT, SRPT, LRPT, EST_SPT, EST_LPT, EST_SRPT, EST_LRPT
|
||||
}
|
||||
|
||||
/** Priority that the solver should use. */
|
||||
final Priority priority;
|
||||
|
||||
/** Creates a new greedy solver that will use the given priority. */
|
||||
public GreedySolver(Priority p) {
|
||||
this.priority = p;
|
||||
}
|
||||
|
|
|
@ -18,12 +18,16 @@ public class RandomSolver implements Solver {
|
|||
|
||||
JobNumbers sol = new JobNumbers(instance);
|
||||
|
||||
// initialize a first solution to the problem.
|
||||
for(int j = 0 ; j<instance.numJobs ; j++) {
|
||||
for(int t = 0 ; t<instance.numTasks ; t++) {
|
||||
sol.jobs[sol.nextToSet++] = j;
|
||||
sol.addTask(j);
|
||||
}
|
||||
}
|
||||
// best solution is currently the initial one
|
||||
Optional<Schedule> best = sol.toSchedule();
|
||||
|
||||
// while we have some time left, generate new solutions by shuffling the current one
|
||||
while(deadline - System.currentTimeMillis() > 1) {
|
||||
shuffleArray(sol.jobs, generator);
|
||||
Optional<Schedule> candidate = sol.toSchedule();
|
||||
|
@ -39,12 +43,12 @@ public class RandomSolver implements Solver {
|
|||
}
|
||||
|
||||
/** Simple Fisher–Yates array shuffling */
|
||||
private static void shuffleArray(int[] array, Random random)
|
||||
private static void shuffleArray(int[] array, Random randomNumberGenerator)
|
||||
{
|
||||
int index;
|
||||
for (int i = array.length - 1; i > 0; i--)
|
||||
{
|
||||
index = random.nextInt(i + 1);
|
||||
index = randomNumberGenerator.nextInt(i + 1);
|
||||
if (index != i)
|
||||
{
|
||||
array[index] ^= array[i];
|
||||
|
|
|
@ -3,7 +3,10 @@ package jobshop.solvers.neighborhood;
|
|||
import jobshop.encodings.Encoding;
|
||||
|
||||
/** This class provides a representation of neighbor by allowing to transform
|
||||
* a solution in a particular encoding Enc into the neighbor and back.*/
|
||||
* a solution in a particular encoding Enc into the neighbor and back.
|
||||
*
|
||||
* @param <Enc> A subclass of Encoding for that can be transformed into a neighbor and back.
|
||||
* */
|
||||
public abstract class Neighbor<Enc extends Encoding> {
|
||||
|
||||
/** Transform the given solution into the neighbor. */
|
||||
|
|
|
@ -7,7 +7,7 @@ import java.util.List;
|
|||
/** For a particular encoding Enc, a neighborhood allow the generation of the neighbors of
|
||||
* a particular solution.
|
||||
*
|
||||
* @param <Enc> A subcless of Encoding for which this encoding can generate neighbors.
|
||||
* @param <Enc> A subclass of Encoding for which this encoding can generate neighbors.
|
||||
*/
|
||||
public abstract class Neighborhood<Enc extends Encoding> {
|
||||
|
||||
|
|
Načítání…
Odkázat v novém problému