Compare commits
	
		
			No commits in common. "master" and "simanneal" have entirely different histories.
		
	
	
		
	
		
					 14 changed files with 21 additions and 213 deletions
				
			
		|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| ## Tooling | ||||
| 
 | ||||
| - Python 3.6 | ||||
| - Python 3.5 | ||||
| - [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 ) | ||||
|  |  | |||
							
								
								
									
										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\}$ | ||||
|     - 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), j bezeichnet den Job | ||||
| 1. Liste von (T, $o_j$) mit $T \in \mathbb{N}$ (Time), $o_j \in O$ (Tasks) | ||||
|     - Operationen: | ||||
|         - Vertauschen von 2 Jobs auf einer Maschine, selbstinvers | ||||
|         - Verzögern von Operationen (keine expliziten Wartezustände nötig) | ||||
|  |  | |||
|  | @ -1,5 +1,2 @@ | |||
| mypy | ||||
| arpeggio | ||||
| matplotlib | ||||
| numpy | ||||
| tkinter | ||||
| Arpeggio | ||||
|  |  | |||
|  | @ -1,3 +0,0 @@ | |||
| with import <nixpkgs> {}; | ||||
| 
 | ||||
|   (python3.withPackages (ps: [ps.numpy (ps.matplotlib.override {enableQt=true;}) ps.mypy ps.arpeggio])).env | ||||
|  | @ -39,7 +39,6 @@ def accept(solution): | |||
|     random solution. | ||||
|     """ | ||||
|     return tighten(solution) | ||||
|     #return solution | ||||
| 
 | ||||
| 
 | ||||
| def tighten(solution): | ||||
|  |  | |||
|  | @ -1,32 +0,0 @@ | |||
| from matplotlib import pyplot as plt | ||||
| from matplotlib import colors | ||||
| from matplotlib import patches | ||||
| import numpy as np | ||||
| from SchedulingAlgorithms.simanneal import rate | ||||
| import random | ||||
| 
 | ||||
| def create_plot(problem, solution): | ||||
|      | ||||
|     end = rate(solution) | ||||
| 
 | ||||
|     with plt.xkcd(): | ||||
|         fig,ax = plt.subplots() | ||||
|         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 ] | ||||
|             xranges = [ (x[0], problem.problem_data[x[1][0]][x[1][1]][0]) for x in mach_ops ] | ||||
|             ax.broken_barh(xranges, ((problem.machines - m)*10, 9),  | ||||
|                     facecolors=[colorlist[x[1][0]] for x in mach_ops]) | ||||
|         ax.set_ylim(5, 5 + (problem.machines+1)*10) | ||||
|         ax.set_xlim(0, 1.25 * end) | ||||
|         ax.set_yticks([15 + m * 10 for m in range(0, problem.machines)]) | ||||
|         ax.set_yticklabels([ problem.machines - 1 - m for m in range(0, problem.machines)] ) | ||||
|         handlecolors = colorlist[0:problem.jobs] | ||||
|         handles = [ patches.Patch(color = handlecolors[j], label = "Job "+str(j)) for j in range(0,problem.jobs) ] | ||||
|         labels = ["Job "+str(j) for j in range(0,problem.jobs)] | ||||
|         ax.legend(handles, labels) | ||||
| 
 | ||||
|     plt.show() | ||||
|  | @ -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 | ||||
|         """ | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,10 +1,7 @@ | |||
| import Parser.js1_style as p | ||||
| #import Parser.js2_style as p | ||||
| from SchedulingAlgorithms import simanneal as sim | ||||
| from Output import output as o | ||||
| INSTANCES = [(5, 5)] | ||||
| TASKS = [[(1, 21), (0, 53), (4, 95), (3, 55), (2, 35)], | ||||
|          [(0, 21), (3, 52), (4, 16), (2, 26), (1, 71)], | ||||
|          [(3, 39), (4, 98), (1, 42), (2, 31), (0, 12)], | ||||
|          [(1, 77), (0, 55), (4, 79), (2, 66), (3, 77)], | ||||
|          [(0, 83), (3, 34), (2, 64), (1, 19), (4, 37)]] | ||||
| 
 | ||||
| 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) | ||||
							
								
								
									
										97
									
								
								src/main.py
									
										
									
									
									
								
							
							
						
						
									
										97
									
								
								src/main.py
									
										
									
									
									
								
							|  | @ -1,97 +1,6 @@ | |||
| #! /usr/bin/env python | ||||
| def main() -> None: | ||||
|     pass | ||||
| 
 | ||||
| import sys | ||||
| import getopt | ||||
| from SchedulingAlgorithms import simanneal as sim | ||||
| from Output import output as o | ||||
| 
 | ||||
| 
 | ||||
| def usage(): | ||||
|     s=  """ | ||||
| Command line options: | ||||
|     -h      show this help | ||||
|     -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 | ||||
| """ | ||||
|     return s | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|     js1 = False | ||||
|     plot = False | ||||
|     try: | ||||
|         opts, args = getopt.getopt(sys.argv[1:], 'hpli:t:s:a:') | ||||
|     except getopt.GetoptError as err: | ||||
|         print(err) | ||||
|         sys.exit() | ||||
|     if ('-h', '') in opts: | ||||
|         print(usage()) | ||||
|     if ('-p', '') in opts: | ||||
|         print("Plotting enabled.") | ||||
|         from Output import output as o | ||||
|         plot = True | ||||
|     if('-l', '') in opts: | ||||
|         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()             | ||||
|     else: | ||||
|         infile = args[0] | ||||
|     if js1: | ||||
|         from Parser import js1_style as parser | ||||
|     else: | ||||
|         from Parser import js2_style as parser | ||||
|     print("Parsing file: " + infile) | ||||
|     problem = parser.parse_file(infile) | ||||
|     if js1: | ||||
|         print("File contains " + str(len(problem)) + " problems.") | ||||
|         if idx == -1: | ||||
|             idx = int(input("Which problem do you want so solve? [0-" + str(len(problem)-1) + "] ")) | ||||
|         problem = problem[idx] | ||||
|     print(problem) | ||||
|     sim.init(problem) | ||||
|     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: | ||||
|         o.create_plot(problem, solution) | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
| if "__name__" == "__main__": | ||||
|     main() | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue