From 85e61f91fb5283768aad16a26f2dadbf93e229bf Mon Sep 17 00:00:00 2001 From: Maximilian Schlosser Date: Sun, 9 Jul 2017 18:57:52 +0200 Subject: [PATCH] Multiple methods for rectify, merge of mapping. TODO: rectify loop --- src/Generator/generator.py | 110 +++++++++++++++++++------- src/SchedulingAlgorithms/enumerate.py | 2 +- 2 files changed, 82 insertions(+), 30 deletions(-) diff --git a/src/Generator/generator.py b/src/Generator/generator.py index 057cf2f..907f00d 100644 --- a/src/Generator/generator.py +++ b/src/Generator/generator.py @@ -4,70 +4,122 @@ import random def pull_fwd(solution): -""" Pull a task from a pseudo-random position to the position of + """ + 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) while(solution[old_idx][1][0] == solution[old_idx-1][1][0]): old_idx -= 1 new_idx = random.randint(0, old_idx-1) - for task in solution[new:old_idx]: + for task in solution[new_idx:old_idx]: if(task[1][0] == solution[old_idx][1][0]): break else: - new_solution = solution[:] task = solution[old_idx] - new_solution.remove(task) - new_solution.insert(new_idx, task) - return (new_solution, new_idx) + solution.remove(task) + solution.insert(new_idx, task) + return (solution, new_idx) def accept(solution): -""" Accept the current generated solution and evaluate it. + """ + Accept the current generated solution and evaluate it. Maybe skip this during the first step to generate a more random solution. -""" + """ return 3 - +#TODO: Loop over successors def rectify(solution, idx): -""" Transform solution by adapting the begin times and delaying + """ + Transform solution by adapting the begin times and delaying tasks on the same machine if affected. -""" + """ + solution[idx][0] = solution[idx+1][0] + + update_begin(solution, idx) + correct_indices(solution, idx) + for task in solution[idx:]: + correct_machine(solution, solution.index(task)) + correct_precedence(solution, solution.index(task)) + +def update_begin(solution, idx): + """ + Update the start time of the given task. + """ + global problem + task = solution[idx] - #move previous task on the same machine back if clash -""" task = solution[idx][1] - prev_task = solution[idx-1][1] - while(problem[task[0]][task[1]][1] == problem[prev_task[0]][prev_task[1]][1]): - solution[idx-1][0] += problem[task[0]][task[1]][0] - solution[idx], solution[idx-1] = solution[idx-1], solution[idx] - idx -= 1 - task = solution[idx][1] - prev_task = solution[idx-1][1] - del prev_task -""" - #make parallel - solution[idx][0] = solution[idx-1][0] + machine = ( x for x in solution[idx-1::-1] if problem[x[1]][1] == problem[task[1]][1] ) + prev_mach = next(machine, 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][0] = max(end_mach, end_job, task[0]) +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) + +def correct_machine(solution, idx): + """ + Push jobs on machines back if conflicts exist. + """ + 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][0] = end + + +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 ), None) + if(conflict): + idx = solution.index(conflict) + solution[idx][0] = end + def generate(solution, steps): -"""Generate a new solution from an existing solution with a + """ + Generate a new solution from an existing solution with a specified number of max steps. -""" + """ + solution = solution[:] options = [pull_fwd, accept] option = random.choice(options) return option(solution) def mock(): -""" Reads a mock problem and creates the corresponding enumerated + """ + Reads a mock problem and creates the corresponding enumerated solution. Should clean up the namespace afterwards. -""" + """ global problem global solution from Parser.js2_style import parse_file as mockload diff --git a/src/SchedulingAlgorithms/enumerate.py b/src/SchedulingAlgorithms/enumerate.py index 558ea12..64f9086 100644 --- a/src/SchedulingAlgorithms/enumerate.py +++ b/src/SchedulingAlgorithms/enumerate.py @@ -1,7 +1,7 @@ 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[job])) ) + schedule = ( (job, task) for job in range(0, problem.jobs) for task in range(0, problem.get_tasks_by_job(job)) ) begin = 0 solution = [] for task in schedule: