added not on day and only on day constraint
This commit is contained in:
parent
c646051058
commit
3b33fcaad7
|
@ -1,13 +1,12 @@
|
|||
import json
|
||||
from math import floor
|
||||
import subprocess
|
||||
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
# Maximum time for AK: 2 Timeslots
|
||||
SLOT_LEN = 120
|
||||
|
||||
DAYS = 1
|
||||
DAYS = 3
|
||||
START = 480
|
||||
END = 1200
|
||||
|
||||
|
@ -19,25 +18,37 @@ class WorkGroup:
|
|||
self.track = track if track is not None else 'none' # haben alle aks einen track?
|
||||
self.projector = 'true' if projector else 'false'
|
||||
self.reso = 'true' if reso else 'false'
|
||||
self.length = floor(length/SLOT_LEN) + 1
|
||||
self.length = floor(length / SLOT_LEN) + 1
|
||||
if self.length > 2:
|
||||
print(f"Warning, length of workgroup {id} greater than 2.")
|
||||
self.length = 2
|
||||
self.leaders = leaders
|
||||
self.constrainted_time = []
|
||||
self.not_on_day = []
|
||||
self.constrainted_time = [{'day': 1, 'time': 840, 'type': 'before'}] if reso else []
|
||||
self.not_on_day = [0, 1, 2] if len(list(filter(lambda c: c['type'] == 'OnlyOnDay', constraints))) else []
|
||||
if reso and not self.not_on_day:
|
||||
self.not_on_day.append(2)
|
||||
self.only_on_day = []
|
||||
|
||||
for constraint in constraints:
|
||||
if constraint["type"] == 'OnlyAfterTime':
|
||||
if constraint['workGroup'] is None:
|
||||
self.constrainted_time.append({'day': constraint['day'], 'time': constraint['time'], 'type':'after'})
|
||||
self.constrainted_time.append(
|
||||
{'day': constraint['day'], 'time': constraint['time'], 'type': 'after'})
|
||||
elif constraint["type"] == 'OnlyBeforeTime':
|
||||
self.constrainted_time.append({'day': constraint['day'], 'time': constraint['time'], 'type':'before'})
|
||||
|
||||
print(self.constrainted_time)
|
||||
self.constrainted_time.append({'day': constraint['day'], 'time': constraint['time'], 'type': 'before'})
|
||||
elif constraint['type'] == 'NotOnDay':
|
||||
self.not_on_day.append(constraint['day'])
|
||||
elif constraint['type'] == 'OnlyOnDay':
|
||||
if constraint['day'] == 2 and reso:
|
||||
print(f'WorkGroup {id}: only on day two, but has reso. Warning, is UNSATISFIABLE!')
|
||||
self.not_on_day.remove(constraint['day'])
|
||||
# TODO: more unsat warning when reso == True
|
||||
|
||||
def __str__(self):
|
||||
leader_rules = ''
|
||||
for leader in self.leaders:
|
||||
leader_rules += f'leader({self.id}, "{leader}").\n'
|
||||
if leader.strip() != '':
|
||||
leader_rules += f'leader({self.id}, "{leader}").\n'
|
||||
return f'ak({self.id}, {self.interest}, "{self.track}", {self.projector}, {self.reso}, {self.length}).\n' + leader_rules
|
||||
|
||||
|
||||
|
@ -63,6 +74,7 @@ class Timeslot:
|
|||
def __str__(self):
|
||||
return f'timeslot("{self.id}", {self.order}, {self.begin}, {self.end}, {self.day}).'
|
||||
|
||||
|
||||
class Schedule:
|
||||
def __init__(self, id, workgroup, day, time, room):
|
||||
self.id = id
|
||||
|
@ -93,6 +105,7 @@ def parse_workgroups(data):
|
|||
id_to_workgroup[workgroup['id']] = i
|
||||
return workgroups, id_to_workgroup
|
||||
|
||||
|
||||
def parse_rooms(data):
|
||||
rooms = []
|
||||
id_to_room = {}
|
||||
|
@ -112,8 +125,6 @@ data = json.load(open('backup.json'))
|
|||
workgroups, id_to_workgroup = parse_workgroups(data)
|
||||
rooms, id_to_room = parse_rooms(data)
|
||||
|
||||
|
||||
|
||||
rules = """
|
||||
%LENGTH = { schedule(AK,TIMESLOT,ROOM): timeslot(TIMESLOT,_,_,_,_), room(ROOM,_,_) } :- ak(AK,_,_,_,_,LENGTH).
|
||||
LENGTH = { schedule(AK,TIMESLOT,ROOM): timeslot(TIMESLOT,_,_,_,_), room(ROOM,_,_) } :- ak(AK,_,_,_,_,LENGTH).
|
||||
|
@ -141,52 +152,65 @@ for workgroup in workgroups:
|
|||
for room in rooms:
|
||||
rules += str(room) + '\n'
|
||||
|
||||
|
||||
timeslots = [(start, start+SLOT_LEN, day) for day in range(DAYS) for start in range(START, END, SLOT_LEN)]
|
||||
timeslots = [(start, start + SLOT_LEN, day) for day in range(DAYS) for start in range(START, END, SLOT_LEN)]
|
||||
|
||||
del timeslots[0] # remove slot thursday morning
|
||||
if DAYS >= 3: del timeslots[-1] #remove last slot for Abschlussplenum
|
||||
print(len(timeslots))
|
||||
if DAYS >= 3: del timeslots[-1] # remove last slot for Abschlussplenum
|
||||
print(f'Number of Workgroups: {len(workgroups)}, Number of Rooms: {len(rooms)}, Number of Timeslots: {len(timeslots)}')
|
||||
for order, timeslot in enumerate(timeslots):
|
||||
if order == 8:
|
||||
continue # remove slot for Resovorstellung, keep order to avoid two-slot-workgroups to be scheduled before and after reso.
|
||||
continue # remove slot for Resovorstellung, keep order to avoid two-slot-workgroups to be scheduled before and after Resovorstellung.
|
||||
rules += str(Timeslot(order, timeslot)) + '\n'
|
||||
|
||||
|
||||
############################
|
||||
# ADD MORE CONSTRAINTS #
|
||||
############################
|
||||
|
||||
def time_to_id(timeslots, day, time):
|
||||
timeslots = list(filter(lambda ts: time>= ts[1][0] and time < ts[1][1] and day == ts[1][2], enumerate(timeslots)))
|
||||
timeslots = list(filter(lambda ts: time >= ts[1][0] and time < ts[1][1] and day == ts[1][2], enumerate(timeslots)))
|
||||
return timeslots[0][0]
|
||||
|
||||
|
||||
def get_last_on_day(day):
|
||||
if day == 0:
|
||||
return 4
|
||||
else:
|
||||
return (day+1)*5
|
||||
return (day + 1) * 5
|
||||
|
||||
|
||||
def get_first_on_day(day):
|
||||
if day == 3:
|
||||
return 15
|
||||
else:
|
||||
return (day)*5
|
||||
return (day) * 5
|
||||
|
||||
for workgroup in filter(lambda wg: wg.constrainted_time, workgroups):
|
||||
for constrainted_time in workgroup.constrainted_time:
|
||||
# TODO constraint for 2 slot workgroups missing!
|
||||
timeslot_id = time_to_id(timeslots, constrainted_time['day'], constrainted_time['time'])
|
||||
timeslot = timeslots[timeslot_id] # after this slot: ok
|
||||
operator1 = '<' if constrainted_time['type'] == 'after' else '>'
|
||||
operator2 = '>' if constrainted_time['type'] == 'after' else '<'
|
||||
day_restriction = get_last_on_day(constrainted_time['day']) + 1 if constrainted_time['type'] == 'before' else get_first_on_day(constrainted_time['day']) -1 #first or last slot on this day
|
||||
timeslot2 = timeslot_id if constrainted_time['type'] == 'before' else timeslot_id
|
||||
print(f':- schedule({workgroup.id}, TIMESLOT, _), timeslot(TIMESLOT, ORDER, _, _, _), ORDER {operator1} {timeslot2}, ORDER {operator2} {day_restriction}.')
|
||||
rules += f':- schedule({workgroup.id}, TIMESLOT, _), timeslot(TIMESLOT, ORDER, _, _, _), ORDER {operator1} {timeslot2}, ORDER {operator2} {day_restriction}.\n'
|
||||
|
||||
for workgroup in workgroups:
|
||||
if workgroup.constrainted_time:
|
||||
for constrainted_time in workgroup.constrainted_time:
|
||||
# TODO constraint for 2 slot workgroups missing!
|
||||
timeslot_id = time_to_id(timeslots, constrainted_time['day'], constrainted_time['time'])
|
||||
timeslot = timeslots[timeslot_id] # after this slot: ok
|
||||
operator1 = '<' if constrainted_time['type'] == 'after' else '>'
|
||||
operator2 = '>' if constrainted_time['type'] == 'after' else '<'
|
||||
day_restriction = get_last_on_day(constrainted_time['day']) + 1 if constrainted_time[
|
||||
'type'] == 'before' else get_first_on_day(
|
||||
constrainted_time['day']) - 1 # first or last slot on this day
|
||||
timeslot2 = timeslot_id if constrainted_time['type'] == 'before' else timeslot_id
|
||||
print(
|
||||
f':- schedule({workgroup.id}, TIMESLOT, _), timeslot(TIMESLOT, ORDER, _, _, _), ORDER {operator1} {timeslot2}, ORDER {operator2} {day_restriction}.')
|
||||
rules += f':- schedule({workgroup.id}, TIMESLOT, _), timeslot(TIMESLOT, ORDER, _, _, _), ORDER {operator1} {timeslot2}, ORDER {operator2} {day_restriction}.\n'
|
||||
if workgroup.not_on_day:
|
||||
for day in workgroup.not_on_day:
|
||||
first_timeslot = get_first_on_day(day)
|
||||
last_timeslot = get_last_on_day(day)
|
||||
print(
|
||||
f':- schedule({workgroup.id}, TIMESLOT, _), timeslot(TIMESLOT, ORDER, _, _, _), ORDER >= {first_timeslot}, ORDER <= {last_timeslot}.')
|
||||
rules += f':- schedule({workgroup.id}, TIMESLOT, _), timeslot(TIMESLOT, ORDER, _, _, _), ORDER >= {first_timeslot}, ORDER <= {last_timeslot}.\n'
|
||||
|
||||
rules += "#show schedule/3."
|
||||
|
||||
|
||||
#######################
|
||||
# GENERATE SCHEDULE #
|
||||
#######################
|
||||
|
@ -199,7 +223,7 @@ open(output_file_name, 'w').write(rules)
|
|||
process = ['clingo', output_file_name]
|
||||
|
||||
# run subprocess, not sure, if busy-waiting..
|
||||
completed_process = subprocess.run(process, universal_newlines = True, stdout = subprocess.PIPE, stderr = subprocess.DEVNULL)
|
||||
completed_process = subprocess.run(process, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
||||
output = completed_process.stdout
|
||||
|
||||
#######################
|
||||
|
@ -212,6 +236,7 @@ if 'UNSATISFIABLE' in output:
|
|||
else:
|
||||
print('SATISFIABLE, outputting schedule...')
|
||||
import re
|
||||
|
||||
try:
|
||||
match = re.match('(.|\n)*Answer: (.+)\n(.*)\nSATISFIABLE', output)
|
||||
schedules_string = match.group(3)
|
||||
|
@ -219,23 +244,23 @@ else:
|
|||
print(e)
|
||||
print(output)
|
||||
schedules = []
|
||||
from collections import defaultdict
|
||||
workgroup_to_schedule = defaultdict(list)
|
||||
for i, schedule in enumerate(schedules_string.split('schedule')[1:]):
|
||||
match = re.match('\((.+),"(.+)#(.+)#.+",(.+)\)', schedule)
|
||||
try:
|
||||
workgroup_id = id_to_workgroup[int(match.group(1))]
|
||||
room_id = id_to_room[int(match.group(4))]
|
||||
schedules.append(Schedule(i, data['workGroups'][workgroup_id], match.group(2), match.group(3), data['rooms'][room_id]))
|
||||
# {
|
||||
# 'id': i,
|
||||
# 'workGroup': data['workGroups'][workgroup_id],
|
||||
# 'room': data['rooms'][room_id],
|
||||
# 'time': match.group(3),
|
||||
# 'day': match.group(2),
|
||||
# 'lockRoom': False,
|
||||
# 'lockTime': False,
|
||||
# })
|
||||
schedules.append(
|
||||
Schedule(i, data['workGroups'][workgroup_id], match.group(2), match.group(3), data['rooms'][room_id]))
|
||||
# {
|
||||
# 'id': i,
|
||||
# 'workGroup': data['workGroups'][workgroup_id],
|
||||
# 'room': data['rooms'][room_id],
|
||||
# 'time': match.group(3),
|
||||
# 'day': match.group(2),
|
||||
# 'lockRoom': False,
|
||||
# 'lockTime': False,
|
||||
# })
|
||||
workgroup_to_schedule[workgroup_id].append(i)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
@ -256,7 +281,6 @@ else:
|
|||
else:
|
||||
schedules_filtered.append(schedules_for_workgroup_1)
|
||||
|
||||
|
||||
data = {}
|
||||
data['schedules'] = [s.to_dict() for s in schedules_filtered]
|
||||
json.dump(data, open('output.json', 'w'))
|
||||
|
|
Loading…
Reference in a new issue