Compare commits
No commits in common. "master" and "devel" have entirely different histories.
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Tooling
|
## Tooling
|
||||||
|
|
||||||
- Python 3.6
|
- Python 3.5
|
||||||
- [MyPy](http://www.mypy-lang.org/ ) für statische Typchecks
|
- [MyPy](http://www.mypy-lang.org/ ) für statische Typchecks
|
||||||
- [Pandoc](https://pandoc.org/ ) für die Dokumentation
|
- [Pandoc](https://pandoc.org/ ) für die Dokumentation
|
||||||
- Python Module: siehe [requirements.txt](https://pip.pypa.io/en/latest/user_guide/#requirements-files )
|
- Python Module: siehe [requirements.txt](https://pip.pypa.io/en/latest/user_guide/#requirements-files )
|
||||||
|
|
23
Readme.txt
23
Readme.txt
|
@ -1,23 +0,0 @@
|
||||||
README
|
|
||||||
-----
|
|
||||||
|
|
||||||
Für die Ausführung des Algorithmus wird Python 3 (empfohlene Version: 3.6.1) benötigt.
|
|
||||||
Die Packages, die zusätzlich gebraucht werden, können der requirements.txt entnommen werden.
|
|
||||||
(Installation kann hier einzeln oder über den Befehl: python -m pip install -r requirements.txt)
|
|
||||||
|
|
||||||
Zur Ausführung bitte im Terminal in den Ordner src gehen und dort das Skript main.py starten.
|
|
||||||
Parameter, die hierbei möglich sind:
|
|
||||||
-h zeigt alle Optionen an
|
|
||||||
-p aktiviert die Ausgabe über den Plotter als Diagramm
|
|
||||||
-l wird benötigt falls die Eingabe eine Liste von Problemen ist (d.h. für jobshop1.txt)
|
|
||||||
-i Index des Problems in der Liste (nur relevant bei -l)
|
|
||||||
-t setzt die Starttemperatur des Simulated Annealings
|
|
||||||
-s setzt die maximalen Umformungsschritte pro Generierung einer neuen Lösung
|
|
||||||
-a setzt die Wahrscheinlichkeit, pro Umformungsschritt auch eine Lösung zu akzeptieren, obwohl
|
|
||||||
noch nicht die maximalen Umformungsschritte erreicht sind
|
|
||||||
|
|
||||||
-t -s und -a müssen nicht alle gesetzt sein, dann wird der jeweilige Defaultwert verwendet
|
|
||||||
Defaultwerte: max_temp = 300, max_steps = 250, accept_prob = 0.01
|
|
||||||
|
|
||||||
Beispielaufruf:
|
|
||||||
python .\main.py -p -l -i 2 -t 50 ..\inputdata\jobshop1.txt
|
|
36
doc.md
36
doc.md
|
@ -1,36 +0,0 @@
|
||||||
## scheduling problem defined by:
|
|
||||||
1. $m$ specialized machines
|
|
||||||
2. tasks $\tau$ of the form $(e, i)$ with $t \in \mathbb{N}$ the execution time and $i \in \{1,2,\dots,m\}$ the machine the task has to run on
|
|
||||||
3. $n$ jobs $T_k$ with $\forall T_k:$ linear order of tasks, with $k \in \{1,2,\dots,n\}$
|
|
||||||
4. Additionally, a multiset $\Omega$ of arbitrary but fixed size that contains wait states $\omega := (1, i)$ with $i \in \{1,2,\dots,m\}$ the blocked machine.
|
|
||||||
|
|
||||||
The goal is to find the fastest feasible schedule $\sigma_{min}$.
|
|
||||||
|
|
||||||
## evaluative function
|
|
||||||
- minimize the execution time of $\sigma$
|
|
||||||
- upper bound: largest processing time first
|
|
||||||
- lower bound: max sum of execution times on one machine
|
|
||||||
|
|
||||||
## solutions
|
|
||||||
- list of tuples $(t, \tau)$ with $t \in \mathbb{N}$ the scheduled begin of $\tau$
|
|
||||||
|
|
||||||
## operations
|
|
||||||
- $\operatorname{ins}(\omega, t)$: block a machine at time $t$ for $w$ time steps.
|
|
||||||
- $\operatorname{xchg}(\tau_1,\tau_2)$: exchange the position of two tasks.
|
|
||||||
|
|
||||||
Both operations require that the start times are recomputed.
|
|
||||||
|
|
||||||
## neighbourhood of solution
|
|
||||||
|
|
||||||
- $\operatorname{neighbours}(\sigma) = \{x \in \Sigma | \delta(\sigma, x) \leq n\}$ with $\Sigma$ the set of all feasible schedules.
|
|
||||||
- $\delta$: $\delta ( \sigma )=0$, $\delta ( \operatorname{op}(x)) = \delta (x) + 1$ (ass. ins has the same penalty xchg has), $x$ either op($y$) or $\sigma$
|
|
||||||
|
|
||||||
## constraints
|
|
||||||
- only schedule new $\tau$ if another $\tau$ is finished
|
|
||||||
- only schedule $\tau \in T_k$ that has no unscheduled predecessor in $T_k$
|
|
||||||
- only one task on a machine any given time
|
|
||||||
|
|
||||||
## implementation in Python
|
|
||||||
- translate problem into list of jobs, jobs into lists of tasks, ie problem = [$T_0, T_1,\dots,T_{k-1}$], $T_i$ = [$\tau_1,\tau_2,\dots$]
|
|
||||||
- address tasks based on their indices, ie [0][1] is the second task of the first job.
|
|
||||||
- compute only one possible next solution, rate, drop/accept. $\delta$ is computed iteratively during generation
|
|
2
notes.md
2
notes.md
|
@ -26,7 +26,7 @@
|
||||||
- $S = \left\{(o_j,t) | o_j \in O \cup \left\{w_n | n \in \mathbb{N} \wedge w_n \text{ v.d.F. } (1, m) \right\} \wedge o_j \text{ v.d.F. } (d, m, j) \wedge t \in T \forall o \in O : \exists (o,t) \in S\right\}$
|
- $S = \left\{(o_j,t) | o_j \in O \cup \left\{w_n | n \in \mathbb{N} \wedge w_n \text{ v.d.F. } (1, m) \right\} \wedge o_j \text{ v.d.F. } (d, m, j) \wedge t \in T \forall o \in O : \exists (o,t) \in S\right\}$
|
||||||
- indirekt lässt sich durch laufende Operation und Zeitpunkt auch Belegung einer Maschine zu einem Zeitpunkt ermitteln
|
- indirekt lässt sich durch laufende Operation und Zeitpunkt auch Belegung einer Maschine zu einem Zeitpunkt ermitteln
|
||||||
- Optimierung: sparse speichern
|
- Optimierung: sparse speichern
|
||||||
1. Liste von (T, $o_j$) mit $T \in \mathbb{N}$ (Time), $o_j \in O$ (Tasks), j bezeichnet den Job
|
1. Liste von (T, $o_j$) mit $T \in \mathbb{N}$ (Time), $o_j \in O$ (Tasks)
|
||||||
- Operationen:
|
- Operationen:
|
||||||
- Vertauschen von 2 Jobs auf einer Maschine, selbstinvers
|
- Vertauschen von 2 Jobs auf einer Maschine, selbstinvers
|
||||||
- Verzögern von Operationen (keine expliziten Wartezustände nötig)
|
- Verzögern von Operationen (keine expliziten Wartezustände nötig)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
mypy
|
mypy
|
||||||
arpeggio
|
Arpeggio
|
||||||
matplotlib
|
matplotlib
|
||||||
numpy
|
|
||||||
tkinter
|
|
|
@ -1,3 +0,0 @@
|
||||||
with import <nixpkgs> {};
|
|
||||||
|
|
||||||
(python3.withPackages (ps: [ps.numpy (ps.matplotlib.override {enableQt=true;}) ps.mypy ps.arpeggio])).env
|
|
|
@ -38,8 +38,8 @@ def accept(solution):
|
||||||
Maybe skip this during the first step to generate a more
|
Maybe skip this during the first step to generate a more
|
||||||
random solution.
|
random solution.
|
||||||
"""
|
"""
|
||||||
return tighten(solution)
|
#return tighten(solution)
|
||||||
#return solution
|
return solution
|
||||||
|
|
||||||
|
|
||||||
def tighten(solution):
|
def tighten(solution):
|
||||||
|
|
|
@ -11,9 +11,7 @@ def create_plot(problem, solution):
|
||||||
|
|
||||||
with plt.xkcd():
|
with plt.xkcd():
|
||||||
fig,ax = plt.subplots()
|
fig,ax = plt.subplots()
|
||||||
col = colors.XKCD_COLORS
|
colorlist = list(colors.XKCD_COLORS.values())
|
||||||
del col['xkcd:white']
|
|
||||||
colorlist = list(col.values())
|
|
||||||
random.shuffle(colorlist)
|
random.shuffle(colorlist)
|
||||||
for m in range(0, problem.machines):
|
for m in range(0, problem.machines):
|
||||||
mach_ops = [ x for x in solution if problem.problem_data[x[1][0]][x[1][1]][1] == m ]
|
mach_ops = [ x for x in solution if problem.problem_data[x[1][0]][x[1][1]][1] == m ]
|
||||||
|
|
|
@ -6,33 +6,33 @@ from collections.abc import Mapping
|
||||||
__all__ = ["js1_style", "js2_style"]
|
__all__ = ["js1_style", "js2_style"]
|
||||||
|
|
||||||
grammar = """
|
grammar = """
|
||||||
// starting point for jobshop1 input file
|
# starting point for jobshop1 input file
|
||||||
job_shop1 = skip_preface
|
job_shop1 = skip_preface
|
||||||
// eat away lines of preface, until first problem_instance is
|
# eat away lines of preface, until first problem_instance is
|
||||||
// encountered; then the list of instances start
|
# encountered; then the list of instances start
|
||||||
skip_preface = (!problem_instance r"[^\n]+" skip_preface) / (eol skip_preface) / instance_list
|
skip_preface = (!problem_instance r"[^\n]+" skip_preface) / (eol skip_preface) / instance_list
|
||||||
instance_list = problem_instance (sep_line trim_ws eol problem_instance eol?)* eof_sep
|
instance_list = problem_instance (sep_line trim_ws eol problem_instance eol?)* eof_sep
|
||||||
problem_instance = trim_ws "instance" ' ' instance_name trim_ws eol trim_ws eol sep_line description eol problem_data
|
problem_instance = trim_ws "instance" ' ' instance_name trim_ws eol trim_ws eol sep_line description eol problem_data
|
||||||
description = r"[^\n]*"
|
description = r"[^\n]*"
|
||||||
instance_name = r"\w+"
|
instance_name = r"\w+"
|
||||||
sep_line = trim_ws plus_line trim_ws eol
|
sep_line = trim_ws plus_line trim_ws eol
|
||||||
// lines out of multiple + signs
|
# lines out of multiple + signs
|
||||||
plus_line = r"\+\+\++"
|
plus_line = r"\+\+\++"
|
||||||
// EOF is a builtin rule matching end of file
|
# EOF is a builtin rule matching end of file
|
||||||
eof_sep = trim_ws plus_line " EOF " plus_line trim_ws eol* EOF
|
eof_sep = trim_ws plus_line " EOF " plus_line trim_ws eol* EOF
|
||||||
// entry point for jobshop2 input files
|
# entry point for jobshop2 input files
|
||||||
job_shop2 = problem_data EOF
|
job_shop2 = problem_data EOF
|
||||||
problem_data = trim_ws num_jobs ' ' num_machines eol job_data+
|
problem_data = trim_ws num_jobs ' ' num_machines eol job_data+
|
||||||
// used for skipping arbitrary number of non-breaking whitespace
|
# used for skipping arbitrary number of non-breaking whitespace
|
||||||
trim_ws = r'[ \t]*'
|
trim_ws = r'[ \t]*'
|
||||||
// git may change line-endings on windows, so we have to match on both
|
# git may change line-endings on windows, so we have to match on both
|
||||||
eol = "\n" / "\r\n"
|
eol = "\n" / "\r\n"
|
||||||
nonneg_num = r'\d+'
|
nonneg_num = r'\d+'
|
||||||
num_jobs = nonneg_num
|
num_jobs = nonneg_num
|
||||||
num_machines = nonneg_num
|
num_machines = nonneg_num
|
||||||
machine = nonneg_num
|
machine = nonneg_num
|
||||||
duration = nonneg_num
|
duration = nonneg_num
|
||||||
// task data for 1 job
|
# task data for 1 job
|
||||||
job_data = ' '* machine ' '+ duration (' '+ machine ' '+ duration)* trim_ws eol
|
job_data = ' '* machine ' '+ duration (' '+ machine ' '+ duration)* trim_ws eol
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import Parser.js1_style as p
|
#import Parser.js1_style as p
|
||||||
#import Parser.js2_style as p
|
import Parser.js2_style as p
|
||||||
from SchedulingAlgorithms import simanneal as sim
|
from SchedulingAlgorithms import simanneal as sim
|
||||||
from Output import output as o
|
from Output import output as o
|
||||||
|
|
||||||
problem = p.parse_file("../inputdata/jobshop1.txt")[0]
|
#problem = p.parse_file("../inputdata/jobshop1.txt")[0]
|
||||||
#problem = p.parse_file("../inputdata/sample")
|
problem = p.parse_file("../inputdata/sample")
|
||||||
sim.init(problem)
|
sim.init(problem)
|
||||||
solution = sim.anneal()
|
solution = sim.anneal()
|
||||||
o.create_plot(problem, solution)
|
o.create_plot(problem, solution)
|
36
src/main.py
36
src/main.py
|
@ -13,9 +13,6 @@ Command line options:
|
||||||
-p activate pretty output (requires tkinter)
|
-p activate pretty output (requires tkinter)
|
||||||
-l assume that a file contains multiple problems, default is only 1
|
-l assume that a file contains multiple problems, default is only 1
|
||||||
-i index of the problem you want solved. has no effect without l
|
-i index of the problem you want solved. has no effect without l
|
||||||
-t set parameter max_temp of simulated annealing
|
|
||||||
-s set parameter max_steps of simulated annealing
|
|
||||||
-a set parameter accept_prob of simulated annealing
|
|
||||||
|
|
||||||
Invocation:
|
Invocation:
|
||||||
python [-hlp] file
|
python [-hlp] file
|
||||||
|
@ -27,8 +24,8 @@ def main():
|
||||||
js1 = False
|
js1 = False
|
||||||
plot = False
|
plot = False
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(sys.argv[1:], 'hpli:t:s:a:')
|
opts, args = getopt.getopt(sys.argv[1:], 'hpli:')
|
||||||
except getopt.GetoptError as err:
|
except getoptGetoptError as err:
|
||||||
print(err)
|
print(err)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
if ('-h', '') in opts:
|
if ('-h', '') in opts:
|
||||||
|
@ -41,12 +38,6 @@ def main():
|
||||||
js1 = True
|
js1 = True
|
||||||
idx = [int(x[1]) for x in opts if x[0]=='-i']
|
idx = [int(x[1]) for x in opts if x[0]=='-i']
|
||||||
idx = idx[0] if idx else -1
|
idx = idx[0] if idx else -1
|
||||||
max_temp = [int(x[1]) for x in opts if x[0]=='-t']
|
|
||||||
max_temp = max_temp[0] if max_temp else -1
|
|
||||||
max_steps = [int(x[1]) for x in opts if x[0]=='-s']
|
|
||||||
max_steps = max_steps[0] if max_steps else -1
|
|
||||||
accept_prob = [int(x[1]) for x in opts if x[0]=='-a']
|
|
||||||
accept_prob = accept_prob[0] if accept_prob else -1
|
|
||||||
if not args:
|
if not args:
|
||||||
print("No file given.")
|
print("No file given.")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
@ -65,28 +56,7 @@ def main():
|
||||||
problem = problem[idx]
|
problem = problem[idx]
|
||||||
print(problem)
|
print(problem)
|
||||||
sim.init(problem)
|
sim.init(problem)
|
||||||
if not max_temp == -1:
|
solution = sim.anneal()
|
||||||
if not max_steps == -1:
|
|
||||||
if not accept_prob == -1:
|
|
||||||
solution = sim.anneal(max_temp, max_steps, accept_prob)
|
|
||||||
else:
|
|
||||||
solution = sim.anneal(max_temp = max_temp, max_steps = max_steps)
|
|
||||||
else:
|
|
||||||
if not accept_prob == -1:
|
|
||||||
solution = sim.anneal(max_temp = max_temp, accept_prob = accept_prob)
|
|
||||||
else:
|
|
||||||
solution = sim.anneal(max_temp = max_temp)
|
|
||||||
else:
|
|
||||||
if not max_steps == -1:
|
|
||||||
if not accept_prob == -1:
|
|
||||||
solution = sim.anneal(max_steps = max_steps, accept_prob = accept_prob)
|
|
||||||
else:
|
|
||||||
solution = sim.anneal(max_steps = max_steps)
|
|
||||||
else:
|
|
||||||
if not accept_prob == -1:
|
|
||||||
solution = sim.anneal(accept_prob = accept_prob)
|
|
||||||
else:
|
|
||||||
solution = sim.anneal()
|
|
||||||
print(solution)
|
print(solution)
|
||||||
print(sim.rate(solution))
|
print(sim.rate(solution))
|
||||||
if plot:
|
if plot:
|
||||||
|
|
Loading…
Reference in a new issue