Commit cf8913a1 authored by Dipo Olaitan's avatar Dipo Olaitan

ACO Updates and Default Shift Pattern in Exceptions Sheet

parent 8c8ef0db
......@@ -23,7 +23,12 @@ class ACO(plugin.ExecutionPlugin):
"""Calculate the score of this ant. Implemented in the Subclass, raises NotImplementedError
"""
raise NotImplementedError("ACO subclass must define '_calculateAntScore' method")
def _calculateAntActualDelay(self, ant):
"""Calculate the score of this ant. Implemented in the Subclass, raises NotImplementedError
"""
raise NotImplementedError("ACO subclass must define '_calculateAntActualDelay' method")
def createCollatedScenarios(self,data):
"""creates the collated scenarios, i.e. the list of options collated into a dictionary for ease of referencing in ManPy
to be implemented in the subclass
......@@ -51,14 +56,16 @@ class ACO(plugin.ExecutionPlugin):
tested_ants = set()
start = time.time() # start counting execution time
collated=self.createCollatedScenarios(data)
collated=self.createCollatedScenarios(data)
resetCollated = deepcopy(collated)#starting pheromone level, use for reset after every generation
assert collated
max_results = int(data['general'].get('numberOfSolutions',0))
assert max_results >= 1
ants = [] #list of ants for keeping track of their performance
solutionConvergence = [] #for monitoring solution convergence
# Number of times new ants are to be created, i.e. number of generations (a
# generation can have more than 1 ant)
seedPlus = 0
......@@ -133,6 +140,7 @@ class ACO(plugin.ExecutionPlugin):
for ant in scenario_list:
ant['score'] = self._calculateAntScore(ant)
ant['actualDelays'] = self._calculateAntActualDelay(ant)
ants.extend(scenario_list)
......@@ -147,9 +155,14 @@ class ACO(plugin.ExecutionPlugin):
# The ants in this generation are ranked based on their scores and the
# best (max_results) are selected
ants = sorted(ants_without_duplicates.values(),
if max([ant['score'] for ant in ants]) == 0:#if all ants achieved zero delays, then use their earliness values
ants = sorted(ants_without_duplicates.values(),
key=operator.itemgetter('actualDelays'))[:max_results]
else:
ants = sorted(ants_without_duplicates.values(),
key=operator.itemgetter('score'))[:max_results]
collated = deepcopy(resetCollated)#reset previous pheromone updates
for l in ants:
# update the options list to ensure that good performing queue-rule
# combinations have increased representation and good chance of
......@@ -159,6 +172,13 @@ class ACO(plugin.ExecutionPlugin):
# 'EDD' is added to Q1 so there is a higher chance that it is
# selected by the next ants.
collated[m].append(l[m])
#termination criterion: after four generations check if the solution has been improving
solutionConvergence.append(max([ant['actualDelays'] for ant in ants])) #add the worst solution after the last generation
solutionConvergence.sort()
del(solutionConvergence[int(data['general'].get('numberOfSolutions',0)):]) #extract the last 4 worst solutions among the best, if there has been no improvement, terminate the optimisation process
if len(solutionConvergence) == int(data['general'].get('numberOfSolutions',0)) and max(solutionConvergence) == min(solutionConvergence):
break
data['result']['result_list'] = result_list = []
for ant in ants:
......
......@@ -24,6 +24,19 @@ class JobShopACO(ACO):
# should not be considered better than being on time.
totalDelay += max(delay, 0)
return totalDelay
def _calculateAntActualDelay(self, ant):
antActualDelay = 0 #used to further compare ants with the earliness if they all had zero delay
result, = ant['result']['result_list'] #read the result as JSON
#loop through the elements
for element in result['elementList']:
element_family = element.get('family', None)
#id the class is Job
if element_family == 'Job':
results=element['results']
delay = float(results.get('delay', "0"))
antActualDelay += delay
return antActualDelay
# creates the collated scenarios, i.e. the list
# of options collated into a dictionary for ease of referencing in ManPy
......
......@@ -52,7 +52,12 @@ class ReadJSShifts(plugin.InputPreparationPlugin, TimeSupportMixin):
exceptionShiftPattern = {} # exceptions for shift pattern dictionary as defined in the spreadsheet
if shiftData:
shiftData.pop(0)
shiftData.pop(0)#remove headers from the shiftData
standardStartTime = shiftData[0][2]
standardEndTime = shiftData[0][3]
shiftData.pop(0) #remove standard times declared on the first line from the shiftData
#iteration through the raw data to structure it into ManPy config
lastrec=None
#iteration through the raw data to structure it into ManPy config
for line in shiftData:
# if all the records of that line are none then continue
......@@ -70,12 +75,12 @@ class ReadJSShifts(plugin.InputPreparationPlugin, TimeSupportMixin):
#if no shift start was given, assume standard 8:00
startTime = line[2]
if startTime == '' or startTime == None:
startTime = "08:00"
startTime = standardStartTime
shiftStart = self.convertToSimulationTime(strptime("%s %s" % (line[1], startTime), '%Y/%m/%d %H:%M'))
#if no shift end was given, assume standard 18:00
endTime = line[3]
if endTime == '' or endTime == None:
endTime = "18:00"
endTime = standardEndTime
shiftEnd = self.convertToSimulationTime(strptime("%s %s" % (line[1], endTime), '%Y/%m/%d %H:%M'))
timePair = self.correctTimePair(shiftStart, shiftEnd)
if not timePair:
......@@ -146,8 +151,8 @@ class ReadJSShifts(plugin.InputPreparationPlugin, TimeSupportMixin):
timeStartList = []
timeEndList = []
for dayNumber in range(0,20):
startTime = "08:00"
endTime = "18:00"
startTime = standardStartTime
endTime = standardEndTime
upDate = now.date()+datetime.timedelta(days=dayNumber)
shiftStart = self.convertToSimulationTime(strptime("%s %s" % (upDate, startTime), '%Y-%m-%d %H:%M'))
shiftEnd = self.convertToSimulationTime(strptime("%s %s" % (upDate, endTime), '%Y-%m-%d %H:%M'))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment