Merge branch 'simanneal' of PSSAI_Team/JobShopScheduling into devel

merge
lukasstracke 2017-07-12 17:51:33 +02:00 committed by Gitea
commit 22196bdfa6
4 changed files with 247 additions and 0 deletions

4
inputdata/sample Normal file
View File

@ -0,0 +1,4 @@
3 3
0 4 1 6 2 1
1 3 0 13 2 4
1 2 2 5 0 3

197
src/Generator/generator.py Normal file
View File

@ -0,0 +1,197 @@
from Parser import JobShopProblem as Problem
import random
def pull_fwd(solution):
"""
Pull a task from a pseudo-random position to the position of
a random task forward. If the task directly in front is part
of the same job, pull that instead. The first task can never
be pulled forward. Will not rectify solutions.
Returns the modified solution and the tasks index.
"""
old_idx = random.randint(1, len(solution)-1)
#print("old_idx" + str(old_idx))
while(solution[old_idx][1][0] == solution[old_idx-1][1][0]):
old_idx -= 1
#Catch case of the op to be pulled being 0
#print("old_idx: " + str(old_idx))
if(old_idx == 0):
return pull_fwd(solution)
new_idx = random.randint(0, old_idx-1)
for task in solution[new_idx:old_idx]:
if(task[1][0] == solution[old_idx][1][0]):
#break
return pull_fwd(solution)
#else:
task = solution[old_idx]
solution.remove(task)
solution.insert(new_idx, task)
return rectify(solution, new_idx)
def accept(solution):
"""
Accept the current generated solution and evaluate it.
Maybe skip this during the first step to generate a more
random solution.
"""
return tighten(solution)
def tighten(solution):
"""
Try to remove any holes in the schedule.
"""
global problem
bound = len(solution)
for i in range(0,bound):
jobs = ( task for task in solution[i-1:bound:-1] if task[1][0] == solution[i][1][0] )
machs = ( task for task in solution[i-1:bound:-1] if problem[task[1]][1] == problem[solution[i][1]][1] )
job = next(jobs,None)
mach = next(machs,None)
times = []
if job:
times += [job[0] + problem[job[1]][0]]
if mach:
times += [mach[0] + problem[job[1]][0]]
if times:
solution[i] = (max(times), solution[i][1])
solution.sort()
return solution
def rectify(solution, idx):
"""
Transform solution by adapting the begin times and delaying
tasks on the same machine if affected.
"""
solution[idx] = (solution[idx+1][0],) + solution[idx][1:]
update_begin(solution, idx)
correct_indices(solution, idx)
for i in range(idx, len(solution)-1):
#print("i: " + str(i))
correct_machine(solution, i)
#print(solution)
correct_precedence(solution, i)
#print(solution)
return solution
def update_begin(solution, idx):
"""
Update the start time of the given task wrt machine and job.
"""
global problem
task = solution[idx]
if(idx == 0):
solution[idx] = (0,) + solution[idx][1:]
return
#find the next task with condition=true, if exists
machine = ( x for x in solution[idx-1::-1] if problem[x[1]][1] == problem[task[1]][1] )
prev_mach = next(machine, None) #returns the task or None
job = ( x for x in solution[idx-1::-1] if task[1][0] == x[1][0] )
prev_job = next(job, None)
end_mach = 0
end_job = 0
if prev_mach:
end_mach = problem[prev_mach[1]][0] + prev_mach[0]
if prev_job:
end_job = problem[prev_job[1]][0] + prev_job[0]
solution[idx] = (max(end_mach, end_job, task[0]),) + solution[idx][1:]
def correct_indices(solution, idx):
"""
Adapt solution to reestablish ascending order of execution times.
"""
task = solution[idx]
tasks = [ x for x in solution[idx:] if x[0] < task[0]]
if tasks:
solution.remove(task)
solution.insert(idx + len(tasks), task)
#[1,3,2] -> idx = 1, len([2])=1
def correct_machine(solution, idx):
"""
Check conflicts on machines and correct if needed.
"""
task = solution[idx]
end = problem[task[1]][0] + task[0]
possible_conf = ( x for x in solution[idx+1:] if problem[x[1]][1] == problem[task[1]][1])
conflict = next(( x for x in possible_conf if x[0] < end ), None)
if(conflict):
idx = solution.index(conflict)
solution[idx] = (end,) + solution[idx][1:]
correct_indices(solution,idx)
def correct_precedence(solution, idx):
"""
Check precedence relation and correct if needed.
"""
task = solution[idx]
end = problem[task[1]][0] + task[0]
possible_conf = ( x for x in solution[idx+1:] if x[1][0] == task[1][0] )
conflict = next(( x for x in possible_conf if x[0] < end or x[1][1] < task[1][1]), None)
if(conflict):
idx = solution.index(conflict)
#print("idx->" + str(idx))
if(conflict[0] < end):
solution[idx] = (end,) + solution[idx][1:]
correct_indices(solution,idx)
if(conflict[1][1] < task[1][1]):
new_start = solution[idx][0] + solution[idx][1][1]
#print("new_start: " + str(new_start))
solution.remove(task)
task = (new_start,) + task[1:]
solution.insert(idx, task)
def generate(old_solution, steps, p=0.01):
"""
Generate a new solution from an existing solution with a
specified number of max steps.
"""
import sys
print("Max steps: " + str(steps))
print("Accept probability: " + str(p))
sys.stdout.write("Start generation... ")
solution = old_solution[:]
option = pull_fwd #do at least one pull
for i in range(0, steps):
solution = option(solution)
if(option == accept):
break
option = pull_fwd if p < random.random() else accept
if ((i * 100) % steps == 0):
sys.stdout.write(str(i*100/steps) + "%... ")
sys.stdout.flush()
sys.stdout.write("Done\n")
if option != accept:
accept(solution)
return solution
def mock():
"""
Reads a mock problem and creates the corresponding enumerated
solution. Should clean up the namespace afterwards.
"""
global problem
from Parser.js2_style import parse_file as mockload
from SchedulingAlgorithms.enumerate import enumerate as mockenum
problem = mockload('../inputdata/sample')
solution = mockenum(problem)
del mockload
del mockenum
return solution
def init(in_problem):
global problem
problem = in_problem

View File

@ -0,0 +1,10 @@
from Parser import JobShopProblem as Problem
def enumerate(problem):
schedule = ( (job, task) for job in range(0, problem.jobs) for task in range(0, len(problem.get_tasks_by_job(job))) )
begin = 0
solution = []
for task in schedule:
solution.append((begin, task))
begin += problem[task][0]
return solution

View File

@ -0,0 +1,36 @@
from Generator.generator import generate
from Generator.generator import init as gen_init
from SchedulingAlgorithms.enumerate import enumerate as enum
from math import e
from random import random
def anneal(max_temp = 300, max_steps = 250, accept_prob=0.01):
global problem
gen_init(problem)
temp = max_temp
initial = enum(problem)
current = generate(initial, problem.machines * problem.jobs * 10, 0) #Complete the iteration once fully.
del initial
for step in range(0, max_steps):
new = generate(current, problem.machines * problem.jobs, accept_prob)
new_end = rate(new)
curr_end = rate(current)
p = 1 / ( 1 + (e ** ((curr_end - new_end)/temp)))
if (new_end < curr_end) or (p < random()):
current = new
print("Old: " + str(curr_end) + " New: " + str(new_end))
temp = ((max_temp-1)/(max_steps**2))*(step-max_steps)**2+1
print("Iteration: "+ str(step) + " Temperature: " + str(temp))
return current
def rate(solution):
global problem
last_tasks = []
for i in range(0,problem.jobs):
last_tasks += [next(( x for x in solution[::-1] if x[1][0] == i), [])]
end_times = [ problem[x[1]][0] + x[0] for x in last_tasks]
return max(end_times)
def init(in_problem):
global problem
problem = in_problem