From 626ffdd4e20e82d0361941c482ec222474f792a9 Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Tue, 20 Jun 2017 16:14:04 +0200 Subject: [PATCH 01/10] clarify index j --- notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes.md b/notes.md index a21ba6f..efe7c46 100644 --- a/notes.md +++ b/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\}$ - indirekt lässt sich durch laufende Operation und Zeitpunkt auch Belegung einer Maschine zu einem Zeitpunkt ermitteln - Optimierung: sparse speichern -1. Liste von (T, $o_j$) mit $T \in \mathbb{N}$ (Time), $o_j \in O$ (Tasks) +1. Liste von (T, $o_j$) mit $T \in \mathbb{N}$ (Time), $o_j \in O$ (Tasks), j bezeichnet den Job - Operationen: - Vertauschen von 2 Jobs auf einer Maschine, selbstinvers - Verzögern von Operationen (keine expliziten Wartezustände nötig) From 26c229929d6a86d724b72d39d6d15d6e8c3b8ac9 Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Tue, 20 Jun 2017 16:25:18 +0200 Subject: [PATCH 02/10] require python 3.6 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index b2820bc..52607a2 100644 --- a/Readme.md +++ b/Readme.md @@ -2,7 +2,7 @@ ## Tooling -- Python 3.5 +- Python 3.6 - [MyPy](http://www.mypy-lang.org/ ) für statische Typchecks - [Pandoc](https://pandoc.org/ ) für die Dokumentation - Python Module: siehe [requirements.txt](https://pip.pypa.io/en/latest/user_guide/#requirements-files ) From 21f78a0479a70ff7f9ea6cec913b6531a4e26de2 Mon Sep 17 00:00:00 2001 From: Maximilian Schlosser Date: Fri, 23 Jun 2017 19:00:50 +0200 Subject: [PATCH 03/10] initial documentation --- doc.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 doc.md diff --git a/doc.md b/doc.md new file mode 100644 index 0000000..4eed2da --- /dev/null +++ b/doc.md @@ -0,0 +1,37 @@ +## 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 job on a machine any given time + +## implementation in Python + - translate jobs into lists of tasks, problem into list of jobs, 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 job of the first task. + - compute only one possible next solution, rate, drop/accept. $\delta$ is computed iteratively during generation + - From 58c386175e427545f2c644b9c707f2b3125fc2d4 Mon Sep 17 00:00:00 2001 From: Maximilian Schlosser Date: Fri, 23 Jun 2017 19:05:31 +0200 Subject: [PATCH 04/10] merge --- doc.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc.md b/doc.md index 4eed2da..b9b2076 100644 --- a/doc.md +++ b/doc.md @@ -34,4 +34,3 @@ Both operations require that the start times are recomputed. - translate jobs into lists of tasks, problem into list of jobs, 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 job of the first task. - compute only one possible next solution, rate, drop/accept. $\delta$ is computed iteratively during generation - - From e82512f2b0a6a7e9bf1df775229324262a3582fc Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Mon, 26 Jun 2017 14:16:02 +0200 Subject: [PATCH 05/10] correct possible ambiguation of and --- doc.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc.md b/doc.md index b9b2076..8d63bd9 100644 --- a/doc.md +++ b/doc.md @@ -31,6 +31,6 @@ Both operations require that the start times are recomputed. - only one job on a machine any given time ## implementation in Python - - translate jobs into lists of tasks, problem into list of jobs, 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 job of the first task. + - 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 From 457eaf74be3bd898c6420ada9813140fc92891b3 Mon Sep 17 00:00:00 2001 From: maxschlosser Date: Mon, 26 Jun 2017 21:43:14 +0200 Subject: [PATCH 06/10] Update 'doc.md' --- doc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.md b/doc.md index 8d63bd9..fd1f7b9 100644 --- a/doc.md +++ b/doc.md @@ -28,7 +28,7 @@ Both operations require that the start times are recomputed. ## 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 job on a machine any given time + - 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$] From 03c5fb612dad7a1af498d6a66c55ef59eee28e0f Mon Sep 17 00:00:00 2001 From: lukasstracke Date: Thu, 13 Jul 2017 00:03:27 +0200 Subject: [PATCH 07/10] =?UTF-8?q?finale=20Version:=20Readme=20f=C3=BCr=20D?= =?UTF-8?q?r.=20Gaggl,=20Reqs=20erg=C3=A4nzt=20(kp=20ob=20notwendig),=20we?= =?UTF-8?q?iss=20als=20Farbe=20im=20Plot=20ausschliessen,=20main=20um=20si?= =?UTF-8?q?manneal-params=20ergaenzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Readme.txt | 23 +++++++++++++++++++++++ requirements.txt | 4 +++- src/Generator/generator.py | 4 ++-- src/Output/output.py | 4 +++- src/example.py | 8 ++++---- src/main.py | 36 +++++++++++++++++++++++++++++++++--- 6 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 Readme.txt diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000..2990537 --- /dev/null +++ b/Readme.txt @@ -0,0 +1,23 @@ +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 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5556179..55650c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ mypy -Arpeggio +arpeggio matplotlib +numpy +tkinter \ No newline at end of file diff --git a/src/Generator/generator.py b/src/Generator/generator.py index d6dca45..4cc4ab3 100644 --- a/src/Generator/generator.py +++ b/src/Generator/generator.py @@ -38,8 +38,8 @@ def accept(solution): Maybe skip this during the first step to generate a more random solution. """ - #return tighten(solution) - return solution + return tighten(solution) + #return solution def tighten(solution): diff --git a/src/Output/output.py b/src/Output/output.py index 2eb31ec..a2d2168 100644 --- a/src/Output/output.py +++ b/src/Output/output.py @@ -11,7 +11,9 @@ def create_plot(problem, solution): with plt.xkcd(): fig,ax = plt.subplots() - colorlist = list(colors.XKCD_COLORS.values()) + col = colors.XKCD_COLORS + del col['xkcd:white'] + colorlist = list(col.values()) random.shuffle(colorlist) 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 ] diff --git a/src/example.py b/src/example.py index 14ed58a..50d2f22 100644 --- a/src/example.py +++ b/src/example.py @@ -1,10 +1,10 @@ -#import Parser.js1_style as p -import Parser.js2_style as p +import Parser.js1_style as p +#import Parser.js2_style as p from SchedulingAlgorithms import simanneal as sim from Output import output as o -#problem = p.parse_file("../inputdata/jobshop1.txt")[0] -problem = p.parse_file("../inputdata/sample") +problem = p.parse_file("../inputdata/jobshop1.txt")[0] +#problem = p.parse_file("../inputdata/sample") sim.init(problem) solution = sim.anneal() o.create_plot(problem, solution) \ No newline at end of file diff --git a/src/main.py b/src/main.py index 31c5c07..44f5448 100644 --- a/src/main.py +++ b/src/main.py @@ -13,6 +13,9 @@ Command line options: -p activate pretty output (requires tkinter) -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 + -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: python [-hlp] file @@ -24,8 +27,8 @@ def main(): js1 = False plot = False try: - opts, args = getopt.getopt(sys.argv[1:], 'hpli:') - except getoptGetoptError as err: + opts, args = getopt.getopt(sys.argv[1:], 'hpli:t:s:a:') + except getopt.GetoptError as err: print(err) sys.exit() if ('-h', '') in opts: @@ -38,6 +41,12 @@ def main(): js1 = True idx = [int(x[1]) for x in opts if x[0]=='-i'] 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: print("No file given.") sys.exit() @@ -56,7 +65,28 @@ def main(): problem = problem[idx] print(problem) sim.init(problem) - solution = sim.anneal() + if not max_temp == -1: + 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(sim.rate(solution)) if plot: From 412b572afb8dc3e1cbbc8576122b03d399d537da Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Sat, 19 Mar 2022 03:05:21 +0100 Subject: [PATCH 08/10] update grammar comments syntax to arpeggio-1.9.0 --- src/Parser/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Parser/__init__.py b/src/Parser/__init__.py index d42e09c..59d2edf 100644 --- a/src/Parser/__init__.py +++ b/src/Parser/__init__.py @@ -6,33 +6,33 @@ from collections.abc import Mapping __all__ = ["js1_style", "js2_style"] grammar = """ - # starting point for jobshop1 input file + // starting point for jobshop1 input file job_shop1 = skip_preface - # eat away lines of preface, until first problem_instance is - # encountered; then the list of instances start + // eat away lines of preface, until first problem_instance is + // encountered; then the list of instances start 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 problem_instance = trim_ws "instance" ' ' instance_name trim_ws eol trim_ws eol sep_line description eol problem_data description = r"[^\n]*" instance_name = r"\w+" sep_line = trim_ws plus_line trim_ws eol - # lines out of multiple + signs + // lines out of multiple + signs 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 - # entry point for jobshop2 input files + // entry point for jobshop2 input files job_shop2 = problem_data EOF 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]*' - # 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" nonneg_num = r'\d+' num_jobs = nonneg_num num_machines = nonneg_num machine = nonneg_num duration = nonneg_num - # task data for 1 job + // task data for 1 job job_data = ' '* machine ' '+ duration (' '+ machine ' '+ duration)* trim_ws eol """ From c18dde9cc48edd6878b46923099bf7c4cb916992 Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Sat, 19 Mar 2022 03:04:34 +0100 Subject: [PATCH 09/10] add nix environment --- default.nix | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 default.nix diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..5963f18 --- /dev/null +++ b/default.nix @@ -0,0 +1,3 @@ +with import {}; + + (python3.withPackages (ps: [ps.numpy ps.matplotlib ps.mypy ps.tkinter ps.arpeggio])).env From f46b2cdd9f27d1a2e3c0a19584006a0213d7e852 Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Sat, 19 Mar 2022 03:04:50 +0100 Subject: [PATCH 10/10] move nix environment to fit lorri tooling --- default.nix | 3 --- shell.nix | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 default.nix create mode 100644 shell.nix diff --git a/default.nix b/default.nix deleted file mode 100644 index 5963f18..0000000 --- a/default.nix +++ /dev/null @@ -1,3 +0,0 @@ -with import {}; - - (python3.withPackages (ps: [ps.numpy ps.matplotlib ps.mypy ps.tkinter ps.arpeggio])).env diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..e8d926d --- /dev/null +++ b/shell.nix @@ -0,0 +1,3 @@ +with import {}; + + (python3.withPackages (ps: [ps.numpy (ps.matplotlib.override {enableQt=true;}) ps.mypy ps.arpeggio])).env