# juzna/PA184

first commit

0 parents commit ecf5959a0a78cb5551b3825993116636bc897b89 committed Jun 4, 2010
 @@ -0,0 +1,6 @@ +Project for PA184 - Heuristic Search Methods + +In this directory: + c/ - constructive heuristics written in C + python/ - great deluge written in Python + sets/ - data sets (problem definitions)
1 c/.gitignore
 @@ -0,0 +1 @@ +/main
 @@ -0,0 +1,40 @@ +1/ COMPILE + for release: g++ -O2 -pthread -o main main.c + for debugging: g++ -ggdb3 -pthread -o main main.c + + + +2/ USAGE +Usage: ./main jobs workers job-randomizer worker-randomizer dataset [set-index] + Job order funcions: constant order order-r price-diff + Worker order funcions: constant order order-r free-capacity price + Randomizers: rand linear add + +Sample: ./main order order - - sets/gap1.txt 0 # priorize jobs and workers by it's order, no randomize, dataset gap1.txt, first problem +Sample: ./main price-diff free-capacity linear linear sets/gap1.txt 0 # heuristics for jobs and workers, peckish +Sample: ./main - - rand rand sets/gap1.txt 0 # no heuristics for jobs and workers, totally random + +Environment variables: + DEBUG=1 - print debug informations + SIMPLE=1 - print just problem number, price, used space and time in miliseconds + Sample: SIMPLE=1 ./main order order - - sets/gap1.txt 0 + + + +3/ ALGORITHM +There is universal greedy solver with several hooks, which has to be implemented to solve problems. +This hooks are: + - getJobsPriority - computes priority for jobs + - getWorkersPriority - computes priority for workers + - randomizeJobs, randomizeWorkers - add some random component to priorities +The higher priority, the sooner is job (resp. worker) assigned. + +Algorithm then works as follows: + call getJobsPriority and order jobs + solveGreedy(problem of size n): + findNextJob + order workers + foreach worker: + assign worker to job + recursion of problem os size (n-1) + (if recursion failed, continue by trying another worker)
25 c/all.sh
 @@ -0,0 +1,25 @@ +#!/bin/bash +clear +g++ -O2 -pthread -o main main.c || exit + +for ds in 1 2 3 4 5 6 7 8 9 10 11 12; do + for job in constant order order-r price-diff; do + for worker in constant order order-r free-capacity price; do + for jr in - linear; do + for wr in - linear; do + echo "$ds$job $worker$jr $wr" + ./main$job $worker$jr $wr sets/gap$ds.txt + echo "------------" + done + done + done + done + + # random + #for i in seq 10; do + echo "$ds - - rand rand" + ./main - - rand rand sets/gap$ds.txt + echo "------------" + # sleep 2 + #done +done
199 c/main.c
 @@ -0,0 +1,199 @@ +/* +* Very simple Greedy GAP solver +* - findNextJob returns first not assigned job +* - findWorkersForJob orders workers by free capacity (max free capacity goes first) +*/ +#include "main.h" + + +/** +* Worker order functions +*/ + +// Give all workers same priority +char _gw_constant(Problem *p, int j, int workers[]) { + for(int w = 0; w < p->wc; w++) workers[w] = 1; +} + +// Worker priority as it's number +char _gw_order(Problem *p, int j, int workers[]) { + for(int w = 0; w < p->wc; w++) workers[w] = p->wc - w; +} + +// Worker priority as reverse order number +char _gw_orderr(Problem *p, int j, int workers[]) { + for(int w = 0; w < p->wc; w++) workers[w] = w; +} + +// Workers have priority same as free capacity +char _gw_freeCapacity(Problem *p, int j, int workers[]) { + for(int w = 0; w < p->wc; w++) workers[w] = p->capacityFree[w]; +} + +// Workers priority by price of assigning job +char _gw_price(Problem *p, int j, int workers[]) { + int priceMax = 0; + + for(int w = 0; w < p->wc; w++) { + int space = getSpace(p, w, j); // Space required + if(p->capacityFree[w] < space) { + workers[w] = -1; // Worker is full + } + else { + int price = getPrice(p, w, j); + if(price > priceMax) priceMax = price; // Store maximal price + workers[w] = price; + } + } + + // Revert high prices to small priorities + for(int w = 0; w < p->wc; w++) { + if(workers[w] > 0) workers[w] = priceMax - workers[w]; + } +} + + + + +/** +* Job order functions +*/ + +// Job priority is constant +char _gj_constant(struct Problem *p, int jobs[]) { + for(int j = 0; j < p->jc; j++) jobs[j] = 1; +} + +// Job priority by order +char _gj_order(struct Problem *p, int jobs[]) { + for(int j = 0; j < p->jc; j++) jobs[j] = p->jc - j; +} + +// Job priority reverse order +char _gj_orderr(struct Problem *p, int jobs[]) { + for(int j = 0; j < p->jc; j++) jobs[j] = j; +} + +// Job priority highest with biggest difference between max- and min-price +char _gj_pricediff(struct Problem *p, int jobs[]) { + int diff, min = -1, max = -1; + + for(int j = 0; j < p->jc; j++) { + // Find min and max price + for(int w = 0; w < p->wc; w++) { + int c = getPrice(p, w, j); + if(c > max) max = c; + if(c < min || min == -1) min = c; + } + + // Difference + diff = max - min; + jobs[j] = diff; + } +} + + + + + + + +// Callback list: Get worker priority +const GetWorkersPriorityEntry getWorkersPriorityCallbacks[] = { + { "constant", _gw_constant }, + { "order", _gw_order }, + { "order-r", _gw_orderr }, + { "free-capacity", _gw_freeCapacity }, + { "price", _gw_price }, + { NULL, NULL }, +}; + +// Callback list: Get jobs priority +const GetJobsPriorityEntry getJobPriorityCallbacks[] = { + { "constant", _gj_constant }, + { "order", _gj_order }, + { "order-r", _gj_orderr }, + { "price-diff", _gj_pricediff }, + { NULL, NULL }, +}; + +// Callback list: Randomizers +const RandomizerEntry randomizerCallbacks[] = { + { "rand", randomizeTotal}, + { "linear", randomizeLinear }, + { "add", randomizeAdd}, + { NULL, NULL }, +}; + +GetJobsPriority getJobsPriorityCallback(char *name) { + // Find callbacks + const GetJobsPriorityEntry *cb = getJobPriorityCallbacks; + + for(; cb && cb->name; ++cb) if(!strcmp(cb->name, name)) return cb->cb; + return NULL; +} + +GetWorkersPriority getWorkersPriorityCallback(char *name) { + // Find callbacks + const GetWorkersPriorityEntry *cb = getWorkersPriorityCallbacks; + + for(; cb && cb->name; ++cb) if(!strcmp(cb->name, name)) return cb->cb; + return NULL; +} + +Randomizer getRandomizerCallback(char *name) { + // Find callbacks + const RandomizerEntry *cb = randomizerCallbacks; + + for(; cb && cb->name; ++cb) if(!strcmp(cb->name, name)) return cb->cb; + return NULL; +} + +#define DUMP_LIST(TEntry, List) {\ + const TEntry *cb = List;\ + for(; cb && cb->name; ++cb) printf("%s ", cb->name);\ + printf("\n");\ +} + +void usage(char *file) { + printf("Usage: %s jobs workers job-randomizer worker-randomizer dataset [set-index]\n", file); + + // Dump Job order cb's + printf(" Job order funcions: "); DUMP_LIST(GetJobsPriorityEntry, getJobPriorityCallbacks); + printf(" Worker order funcions: "); DUMP_LIST(GetWorkersPriorityEntry, getWorkersPriorityCallbacks); + printf(" Randomizers: "); DUMP_LIST(RandomizerEntry, randomizerCallbacks); + printf("\n"); + + printf("Sample: %s order order - - sets/gap1.txt 0 # priorize jobs and workers by it's order, no randomize, dataset gap1.txt, first problem\n", file); + printf("Sample: %s price-diff free-capacity linear linear sets/gap1.txt 0 # heuristics for jobs and workers, peckish\n", file); + printf("Sample: %s - - rand rand sets/gap1.txt 0 # no heuristics for jobs and workers, totally random\n", file); + printf("\n"); + + printf("Environment variables:\n DEBUG=1 - print debug informations\n SIMPLE=1 - print just problem number, price, used space and time in miliseconds\n"); + printf(" Sample: SIMPLE=1 %s order order - - sets/gap1.txt 0\n", file); + + printf("\n"); + exit(1); +} + + +// Main function +int main(int argc, char**argv) { + // Find callbacks + const GetJobsPriorityEntry *cb = getJobPriorityCallbacks; + + if(argc < 5) usage(argv[0]); + + // Create solver + Solver solver; + solver.findJobMethod = Priority; + solver.getJobsPriority = getJobsPriorityCallback(argv[1]); + solver.getWorkersPriority = getWorkersPriorityCallback(argv[2]); + solver.randomizeJobs = getRandomizerCallback(argv[3]); + solver.randomizeWorkers = getRandomizerCallback(argv[4]); + + //printf("%u %u %u %u\n", solver.getJobsPriority, solver.getWorkersPriority, solver.randomizeJobs, solver.randomizeWorkers); + + // Run program + return main_(&solver, argc - 4, argv + 4); +}
537 c/main.h
2 c/run.sh
 @@ -0,0 +1,2 @@ +#!/bin/bash +clear;g++ -ggdb3 -o $1$1.c && ./$1 sets/gap1.txt$2
1 c/sets
2 c/test.sh
 @@ -0,0 +1,2 @@ +#!/bin/bash +clear;g++ -pthread -ggdb3 -o $1$1.c && valgrind --leak-check=full ./$1 constant constant - - sets/gap8.txt 188 python/constructive_heuristics.py  @@ -0,0 +1,188 @@ +#!/usr/bin/env python + +import random +from heapq import heappush, heappop + +# Select random job, select first fitting worker +# Instead of backtracking, release random job +def constructive_random_heuristic( w_price, w_space, w_capacity ): + available_jobs = set( range(len(w_price[0]))) + wc = len(w_capacity) + available_capacity = w_capacity[:] # copy list + solution = {} + + safe_count = 0 + # until all jobs are assigned + while len(available_jobs)>0: + safe_count += 1 + if safe_count>10000: + print "no solution avaible in 10000 steps." + break + """ select job """ + jc = len(available_jobs) + # random selection + select_job = list(available_jobs)[ random.randint(0, jc-1) ] + # job isnt avaible anymore + available_jobs.remove( select_job ) + """ select worker """ + # feasable workers + candidates = [] + for worker in range(wc): + if available_capacity[ worker ] >= w_space[ worker ][ select_job ]: + candidates.append(worker) + # select random worker if available + if len(candidates)>0: + select_worker = candidates[random.randint(0, len(candidates)-1 )] + # udpate available capacity + available_capacity[ select_worker ] -= w_space[ select_worker ][ select_job ] + # save solution + solution[ select_job ] = select_worker + else: + # otherwise clear selection + available_jobs.add( select_job ) + # because the job cant be assigned, we release another job from the worker + # select random assigned job + remove = solution.keys()[ random.randint( + 0, len( solution.keys() )-1 + )] + # free worker capacity + worker_inc = solution[remove] + available_capacity[worker_inc] += w_space[ worker_inc ][ remove ] + del solution[remove] + # make job available + available_jobs.add( remove ) + + + + return solution + + +" NOT IMPLEMENTED " +def constructive_peckish_heuristic_r( w_price, w_space, w_capacity, criteria ): + available_jobs = set( range(len(w_price[0]))) + w_id = range( len(w_capacity) ) + available_capacity = w_capacity[:] # copy list + solution = {} + return count_greedy( + w_price, w_space, available_jobs, available_capacity, solution, w_id, + criteria, True + ) + +""" +while we have unassigned jobs: + Select job by criteria + (for example biggest difference between min and max available assertion). + Assign job to the cheapest available worker. + If there is no available worker nor job, backtrack to the last assigment and + select next optimal assignment. + +style: + if criteria == "maxdif": + criteria_val = -(max-min) + if criteria == "mindif": + criteria_val = -(min-max) + if criteria == "maxmax": + criteria_val = -(max) + if criteria == "minmax": + criteria_val = (max) + if criteria == "minmin": + criteria_val = (min) + if criteria == "maxmin": + criteria_val = -(min) + if criteria == "minavg": + criteria_val = -(max+min)/2 +""" +def constructive_greedy_heuristic_r( w_price, w_space, w_capacity, criteria ): + available_jobs = set( range(len(w_price[0]))) + w_id = range( len(w_capacity) ) + available_capacity = w_capacity[:] # copy list + solution = {} + return count_greedy( + w_price, w_space, available_jobs, available_capacity, solution, w_id, criteria + ) + +# recursive function +# select best job +# assign worker +# call recursively +# backtrack if call return None +# ++ peckish: give some randomness +def count_greedy( + w_price, w_space, available_jobs, available_capacity, solution, w_id, + criteria, peckish=False + ): + + if len(available_jobs) == 0: + return solution + + # job to be processed + selected_job = None + if peckish and len(available_jobs)>0: + # peckish select random job + selected_job = list(available_jobs)[ random.randint(0, len(available_jobs)-1) ] + else: + # greedy, select best job + selected_job = select_best_job( + w_price, w_space, available_jobs, available_capacity, w_id, criteria + ) + + if selected_job == None: + return None + + w_order = [] + + for worker_id in w_id: + if available_capacity[worker_id] >= w_space[worker_id][selected_job]: + heappush( w_order, (w_price[worker_id][selected_job], worker_id)) + while len(w_order) > 0: + worker_id = heappop( w_order )[1] + + solution[selected_job] = worker_id + available_capacity[worker_id] -= w_space[worker_id][selected_job] + available_jobs.remove(selected_job) + new_solution = count_greedy( + w_price, w_space, available_jobs, available_capacity, + solution, w_id, criteria, peckish + ) + + if new_solution != None: + return new_solution + else: + del solution[selected_job] + available_jobs.add(selected_job) + available_capacity[worker_id] += w_space[worker_id][selected_job] + return None + +def select_best_job( w_price, w_space, available_jobs, available_capacity, w_id, criteria ): + jobs = [] + for job_id in available_jobs: + min = 32000 + max = -1 + for worker_id in w_id: + if available_capacity[worker_id] > w_space[worker_id][job_id]: + price = w_price[worker_id][job_id] + if price < min: + min = price + if price > max: + max = price + + if max > -1: + if criteria == "maxdif": + criteria_val = -(max-min) + if criteria == "mindif": + criteria_val = -(min-max) + if criteria == "maxmax": + criteria_val = -(max) + if criteria == "minmax": + criteria_val = (max) + if criteria == "minmin": + criteria_val = (min) + if criteria == "maxmin": + criteria_val = -(min) + if criteria == "minavg": + criteria_val = -(max+min)/2 + heappush( jobs, (criteria_val, job_id) ) + if len( jobs ) == 0: + return None + # maximum difference + return heappop( jobs )[1] 8 python/doc/doc.aux  @@ -0,0 +1,8 @@ +\relax +\select@language{english} +\@writefile{toc}{\select@language{english}} +\@writefile{lof}{\select@language{english}} +\@writefile{lot}{\select@language{english}} +\@writefile{toc}{\contentsline {section}{\numberline {1}Requirements}{1}} +\@writefile{toc}{\contentsline {section}{\numberline {2}Usage}{1}} +\@writefile{toc}{\contentsline {section}{\numberline {3}Structure}{3}} 104 python/doc/doc.log  @@ -0,0 +1,104 @@ +This is pdfTeXk, Version 3.1415926-1.40.9 (Web2C 7.5.7) (format=pdflatex 2009.8.1) 4 JUN 2010 14:18 +entering extended mode + %&-line parsing enabled. +**doc +(./doc.tex +LaTeX2e <2005/12/01> +Babel and hyphenation patterns for english, usenglishmax, dumylang, noh +yphenation, german-x-2008-06-18, ngerman-x-2008-06-18, ancientgreek, ibycus, ar +abic, basque, bulgarian, catalan, pinyin, coptic, croatian, czech, danish, dutc +h, esperanto, estonian, farsi, finnish, french, galician, german, ngerman, mono +greek, greek, hungarian, icelandic, indonesian, interlingua, irish, italian, la +tin, lithuanian, mongolian, mongolian2a, bokmal, nynorsk, polish, portuguese, r +omanian, russian, sanskrit, serbian, slovak, slovenian, spanish, swedish, turki +sh, ukenglish, ukrainian, uppersorbian, welsh, loaded. +(c:/Programy/texlive/2008/texmf-dist/tex/latex/base/article.cls +Document Class: article 2005/09/16 v1.4f Standard LaTeX document class +(c:/Programy/texlive/2008/texmf-dist/tex/latex/base/size10.clo +File: size10.clo 2005/09/16 v1.4f Standard LaTeX file (size option) +) +\c@part=\count79 +\c@section=\count80 +\c@subsection=\count81 +\c@subsubsection=\count82 +\c@paragraph=\count83 +\c@subparagraph=\count84 +\c@figure=\count85 +\c@table=\count86 +\abovecaptionskip=\skip41 +\belowcaptionskip=\skip42 +\bibindent=\dimen102 +) +(c:/Programy/texlive/2008/texmf-dist/tex/generic/babel/babel.sty +Package: babel 2008/07/06 v3.8l The Babel package + +(c:/Programy/texlive/2008/texmf-dist/tex/generic/babel/english.ldf +Language: english 2005/03/30 v3.3o English support from the babel system + +(c:/Programy/texlive/2008/texmf-dist/tex/generic/babel/babel.def +File: babel.def 2008/07/06 v3.8l Babel common definitions +\babel@savecnt=\count87 +\U@D=\dimen103 +) +\l@canadian = a dialect from \language\l@american +\l@australian = a dialect from \language\l@british +\l@newzealand = a dialect from \language\l@british +)) (./doc.aux) +\openout1 = doc.aux'. + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 5. +LaTeX Font Info: ... okay on input line 5. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 5. +LaTeX Font Info: ... okay on input line 5. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 5. +LaTeX Font Info: ... okay on input line 5. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 5. +LaTeX Font Info: ... okay on input line 5. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 5. +LaTeX Font Info: ... okay on input line 5. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 5. +LaTeX Font Info: ... okay on input line 5. +LaTeX Font Info: External font cmex10' loaded for size +(Font) <12> on input line 6. +LaTeX Font Info: External font cmex10' loaded for size +(Font) <8> on input line 6. +LaTeX Font Info: External font cmex10' loaded for size +(Font) <6> on input line 6. + +LaTeX Warning: No \author given. + +LaTeX Font Info: Try loading font information for OMS+cmr on input line 20. +(c:/Programy/texlive/2008/texmf-dist/tex/latex/base/omscmr.fd +File: omscmr.fd 1999/05/25 v2.5h Standard LaTeX font definitions +) +LaTeX Font Info: Font shape OMS/cmr/m/n' in size <10> not available +(Font) Font shape OMS/cmsy/m/n' tried instead on input line 20. + [1 + +{c:/Programy/texlive/2008/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] +LaTeX Font Info: External font cmex10' loaded for size +(Font) <7> on input line 76. +LaTeX Font Info: External font cmex10' loaded for size +(Font) <5> on input line 76. + [2] [3] (./doc.aux) ) +Here is how much of TeX's memory you used: + 558 strings out of 493876 + 6476 string characters out of 1150232 + 61071 words of memory out of 3000000 + 3892 multiletter control sequences out of 10000+50000 + 7756 words of font info for 28 fonts, out of 3000000 for 5000 + 714 hyphenation exceptions out of 8191 + 24i,6n,19p,181b,211s stack positions out of 5000i,500n,10000p,200000b,50000s + +Output written on doc.pdf (3 pages, 41519 bytes). +PDF statistics: + 40 PDF objects out of 1000 (max. 8388607) + 0 named destinations out of 1000 (max. 131072) + 1 words of extra memory for PDF output out of 10000 (max. 10000000) + BIN python/doc/doc.pdf Binary file not shown. 82 python/doc/doc.tex  @@ -0,0 +1,82 @@ +\documentclass{article} +\usepackage[english]{babel} +\title{Great deluge algorithm} + +\begin{document} +\maketitle +\section{Requirements} +The program was written and tested in Python2.5.\\ +To visualize solution you need to install {\tt{}matplotlib} and {\tt{}numpy} module. + +\section{Usage} + +To call a program type: +\begin{verbatim}python main.py [options]\end{verbatim} + +Available options: + +\begin{itemize} + +\item{--action=} +\begin{verbatim}python main.py -a=action\end{verbatim} +Select the goal of run: + \begin{itemize} + \item{\tt{}c} All constructive heuristics. + \item{\tt{}cgreedy} Greedy constructive heuristic. + \item{\tt{}cpeckish} Peckish constructive heuristic. + \item{\tt{}crandom} Random constructive heuristic. + \item{\tt{}iterative} Iterative local search with random initialisation. + \item{\tt{}deluge} Great deluge with default initialisation. + \item{\tt{}delugeall} Run great deluge with peckish, random and greedy initialisation. + \item{\tt{}delugerandom} Run great deluge with random initialisation. + \item{\tt{}delugegreedy} Run great deluge with greedy initialisation. + \item{\tt{}delugepeckish} Run great deluge with peckish initialisation. + \end{itemize} + +\item{--dataset=} +\begin{verbatim}python main.py -d=number\end{verbatim} +Select the set from input file to be used. If you type {\tt{}-d=*}, all sets are used instead. + +\item{--help} +\begin{verbatim}python main.py -h\end{verbatim} +Print list of commands. + +\item{--input=} +\begin{verbatim}python main.py -i=path\end{verbatim} +Path to input file. Default path is 'sets/gap1.txt'. + +\item{--solution} +\begin{verbatim}python main.py -s\end{verbatim} +Print solution as table job:worker. + +\item{--visualize} +\begin{verbatim}python main.py -v\end{verbatim} +Visualize solution.\\ +Required modules: numpy, matplotlib + +\item{--time} +\begin{verbatim}python main.py -t\end{verbatim} +Print time marks (start and end of run). + +\item{--beautyoff} +\begin{verbatim}python main.py -b\end{verbatim} +Strip some decorative texts. + +\end{itemize} + +Examples: +\begin{verbatim} +python main.py -i="sets/gap9.txt" -s -t -d=* -a=delugerandom +python main.py -i="sets/gap9.txt" -t -v -a=delugerandom +\end{verbatim} + +\section{Structure} +\begin{itemize} + \item{main.py} Call other programmes and mark time. + \item{local$\_{}$search.py} Implement great deluge and iterative search. + \item{constructive$\_{}$heuristics.py} Implement constructive heuristics. + \item{visualisation.py} Implement generating of 3d image using matplotlib and numpy. + \item{process$\_{}\$data.py} Load and process input. + \item{img} Examples of visualization. Green color means good solution, more red in RGB means worse solution. +\end{itemize} +\end{document}
BIN python/img/c-set1a.png
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN python/img/c-set1b.png
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN python/img/c-set1c.png
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN python/img/c-set1d.png
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN python/img/deluge-set12.png
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN python/img/deluge-set1a.png
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN python/img/deluge-set1b.png
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
383 python/local_search.py
 @@ -0,0 +1,383 @@ +#!/usr/bin/env python +import random +from copy import deepcopy, copy +from math import pow as power +from math import exp + +from heapq import heappush, heappop, nlargest + +class Frame: + + PENALTY_EXP = 0 + PENALTY_LIN = 20 + DELTA = 0.0005 + START_LEVEL_MULTIPLY_BEST = 2 + AVERAGE_ERRORS = 4 + + # + raw solution: dictionary job:worker + # + w_price, w_space, w_capacity: output of load function + def __init__( self, raw_solution, w_price, w_space, w_capacity ): + self.init_solution = self.solution_format( raw_solution ) + self.w_price = w_price + self.w_space = w_space + self.w_capacity = w_capacity + self.wc = len(w_capacity) + + + + # + raw solution: dictionary job:worker + # @return dictionary worker:list(jobs) + def solution_format( self, raw_solution ): + solution = {} + for job_key in raw_solution: + worker_id = raw_solution[job_key] + if solution.has_key(worker_id): + solution[worker_id].append(job_key) + else: + solution[worker_id] = [job_key] + return solution + + # + counted solution: dictionary worker:list of jobs + # @return dictionary job:worker + # Inverse to solution format function + def output_format( self, counted_solution ): + output = {} + for worker_id in counted_solution.keys(): + for job_id in counted_solution[worker_id]: + output[job_id] = worker_id + return output + + # level of decay + # + current_level float + # @return new level + # The function is based on the article + """ + Non-Linear Great Deluge with Learning Mechanism for Solving + the Course Timetabling Problem + Joe H. Obit Dario Landa-Silva Djamilah Ouelhadj Marc Sevauxy + """ + def _decay( self, current_level ): + level = current_level + level = level * ( + exp(-self.DELTA * (2 + random.random())) + ) + return level + + # ITERATIVE SEARCH + # ++ max. number of iterations + # ++ debug True to view intern information about progress + def great_deluge( self, number=1600, debug = False ): + # init solution generated by some constructive feasable h. + solution = self.init_solution + # best_solution = deepcopy( solution ) + # best feasable solution + best_solution_w_e = deepcopy( solution ) + # best feasable/unfeasable price + best_price = price = self.count( solution ) + # best only feasable price + best_price_w_e = price + # debug statistics + stats1 = { + "switch two jobs":0, + "switch max and random jobs":0, + "switch two workers":0, + "rearange 2-5 workers":0, + "rearange greedy 2-5 workers":0 + } + if debug: + print "Begin parameters:" + print self.count(solution, True) + # level of decay + level = best_price *\ + self.START_LEVEL_MULTIPLY_BEST +\ + self.AVERAGE_ERRORS * self.PENALTY_LIN +\ + power(self.AVERAGE_ERRORS, self.PENALTY_EXP) + # for number of interations or too low decay + for i in range(number): + level = self._decay( level ) + new_price, (new_solution, source) = self.select_step( + self.available_steps( solution ) + ) + # if step is acceptable + if new_price < price + level: + price = new_price + solution = new_solution + # if step is optimal + if new_price < best_price: + # if new price doesnt have any error + if self.count(new_solution, True)[2] == 0: + best_price_w_e = min(new_price, best_price_w_e) + best_solution_w_e = deepcopy( solution ) + if debug: + print self.count(new_solution, True), " l:", round(level), source + # best infeasable or feasable solution + stats1[source] += 1 + best_price = min(new_price, best_price) + # best_solution = deepcopy( solution ) + # finish condition + if level <= max(self.PENALTY_LIN, 1): + if debug: + print "level", level + break + return self.output_format( best_solution_w_e ) + + # ITERATIVE SEARCH + # ++ max. number of iterations + # ++ debug True to view intern information about progress + # ++ stop_condition_max = 10. if 10 iterations in row are worse than + # actual solution, break + def run_iteration( self, number=2000, debug = False, stop_condition_max = 10 ): + solution = self.init_solution + price = new_price = self.count( solution ) + + stop_condition = stop_condition_max + + stats1 = { + "switch two jobs":0, + "switch max and random jobs":0, + "switch two workers":0, + "rearange 2-5 workers":0, + "rearange greedy 2-5 workers":0 + } + if debug: + print self.count(solution, True), "begin" + min_p = price + for i in range(number): + if stop_condition < 0: + break + + old_price = new_price + new_price, (new_solution, source) = self.select_step( + self.available_steps( solution ) + ) + if old_price < new_price: + stop_condition -= 1 + else: + stop_condition = stop_condition_max + + if new_price < min_p: + price = new_price + solution = new_solution + if new_price < min_p: + if debug: + print self.count(new_solution, True) + stats1[source] += 1 + min_p = min(price, min_p) + store = min_p + + min_p = self.count(solution) + if debug: + for key in stats1.keys(): + print key + print "1:",stats1[key] + print "2:",stats2[key] + return self.output_format( new_solution ) + + """ + Generate local + """ + # + current solution + # @return list of prices and local steps [price,solution] + # local steps are generated randomly, because there are too many possibilities + def available_steps( self, solution ): + priced_solutions = [] + + # switch two workers + """ + for i in range(5): + w1, w2 = self._select_n_workers( 2 ) + temp = w1 + w1 = w2 + w2 = w1 + new_solution = self._switch_all_jobs(w1, w2, solution) + price = self.count( new_solution ) + heappush( priced_solutions, (price, (new_solution, "switch two workers") ) ) + """ + + # switch two jobs + for i in range(5): + w1, w2 = self._select_n_workers( 2 ) + j1_v = self._select_n_jobs( solution[w1], 1) + j2_v = self._select_n_jobs( solution[w2], 1) + # selected worker doesnt have any job + if len(j1_v)==0 or len(j2_v)==0: + continue + j1 = j1_v[0] + j2 = j2_v[0] + new_solution = self._switch_2_jobs(w1, j1, w2, j2, solution) + price = self.count( new_solution ) + heappush( priced_solutions, (price, (new_solution, "switch two jobs") ) ) + + + # rearange randomly x worker's jobs, x in <2,5> + for i in range(2): + k = random.randint(2, 5) + w_vector = self._select_n_workers( k ) + new_solution = deepcopy( solution ) + + released_jobs = [] + # release + for worker_id in w_vector: + released_jobs += new_solution[worker_id] + new_solution[worker_id] = [] + + for job_id in released_jobs: + new_solution[w_vector[random.randint(0, k-1)]].append(job_id) + + price = self.count( new_solution ) + heappush( priced_solutions, (price, (new_solution, "rearange 2-5 workers") ) ) + + # rearange x jobs -- greedy, x in <2,5> + for i in range(2): + k = random.randint(2, 5) + w_vector = self._select_n_workers( k ) + new_solution = deepcopy( solution ) + + released_jobs = [] + # release + for worker_id in w_vector: + released_jobs += new_solution[worker_id] + new_solution[worker_id] = [] + + for job_id in released_jobs: + # find cheapest job for available workers + min_price = 32000 + min_sel = None + for worker_id in w_vector: + if self.w_price[worker_id][job_id] < min_price: + min_price = self.w_price[worker_id][job_id] + min_sel = worker_id + new_solution[min_sel].append(job_id) + + price = self.count( new_solution ) + heappush( priced_solutions, (price, (new_solution, "rearange greedy 2-5 workers") ) ) + + # move the most expensive + most_exp_w = None + most_exp_id = None + most_exp_price = -1 + + for worker_id in solution.keys(): + for job_id in solution[worker_id]: + if self.w_price[worker_id][job_id]>most_exp_price: + most_exp_price = self.w_price[worker_id][job_id] + most_exp_id = job_id + most_exp_w = worker_id + for i in range(5): + w = self._select_n_workers( 1 )[0] + while (w == most_exp_w): + w = self._select_n_workers( 1 )[0] + + j_v = self._select_n_jobs( solution[w], 1 ) + if len(j_v)==0: + # worker doesnt have any job + continue + j = j_v[0] + + new_solution = self._switch_2_jobs(most_exp_w, most_exp_id, w, j, solution) + price = self.count( new_solution ) + heappush( priced_solutions, (price, (new_solution, "switch max and random jobs") ) ) + + return priced_solutions + + # + list of (price, step) + # @return some solution + def select_step( self, priced_solutions ): + return heappop( priced_solutions ) + + # + w1 worker 1 id + # + w2 worker 2 id + # + solution_old... old solution, remains intact + # @return switched copy of solution + def _switch_all_jobs( self, w1, w2, solution_old ): + solution = deepcopy(solution_old) + temp = solution[w1] + solution[w1] = solution[w2] + solution[w2] = temp + return solution + + # + w1 worker 1 id, + # + j1 switched job of worker 1 + # + w2 worker 2 id + # + j2 switched job of worker 2 + # + solution_old... old solution, remains intact + # if selected worker doesnt have any job, this fucntion is identity + # @return switched copy of solution + def _switch_2_jobs( self, w1, j1, w2, j2, solution_old ): + if len(solution_old[w1])==0 or len(solution_old[w2])==0: + return solution_old + solution = deepcopy(solution_old) + solution[w1].remove(j1) + solution[w1].append(j2) + solution[w2].remove(j2) + solution[w2].append(j1) + return solution + + # + w_list list of jobs available to worker + # + number n less or equal to number of jobs + # @return random distinct ids of jobs + def _select_n_jobs( self, w_list, n ): + ws = copy(w_list) + w_len = len(ws) + if n > w_len: + n = w_len + jobs = [] + for demand in range(n): + get = ws[random.randint(0, w_len-1)] + while get in jobs: + get = ws[random.randint(0, w_len-1)] + jobs.append(get) + return jobs + + # + number n less or equal to number of workers + # @return random distinct ids of workers + def _select_n_workers( self, n ): + if n > self.wc: + n = self.wc + workers = [] + for demand in range(n): + get = random.randint(0, self.wc-1) + while get in workers: + get = random.randint(0, self.wc-1) + workers.append(get) + return workers + + # + solution + # ++ parts True/False, return total price or vector of cost and penalty + # @return price of solution, tak in consideration the penalty + def count( self, solution, parts=False ): + cost = 0 + weight_penalty = copy(self.w_capacity) + penalty_cost = 0 + for worker_id in solution.keys(): + for job_id in solution[worker_id]: + cost += self.w_price[worker_id][job_id] + weight_penalty[worker_id] -= self.w_space[worker_id][job_id] + for item in weight_penalty: + if item<0: + penalty_cost += self.PENALTY_LIN + power( -1 * item, self.PENALTY_EXP) + if parts==True: + return round(cost + penalty_cost), cost, round(penalty_cost) + else: + return float(cost + penalty_cost) + + +if __name__ == u"__main__": + from main import * + load = load_data() + #from visualisation import visualize + w_price, w_space, w_capacity = select_problem( load, 0) + if trivial_conditions( w_price, w_space, w_capacity ): + #solution = constructive_greedy_heuristic_r( w_price, w_space, w_capacity, "maxmax" ) + solution = constructive_random_heuristic( w_price, w_space, w_capacity ) + print calculate_price(solution, w_price) + f = Frame(solution, w_price, w_space, w_capacity) + + """ + for i in range(1,100): + print i*10, f._decay(i*10) + """ + result = f.great_deluge() + print result + #visualize( w_price, w_space, w_capacity, f.output_format( result ) ) + #f.run_iteration()
280 python/main.py
109 python/process_data.py